فهرست منبع

feature: add cname option

Nick Peng 2 سال پیش
والد
کامیت
a6d6781a2a
7فایلهای تغییر یافته به همراه335 افزوده شده و 10 حذف شده
  1. 1 0
      ReadMe.md
  2. 1 0
      ReadMe_en.md
  3. 3 0
      etc/smartdns/smartdns.conf
  4. 1 1
      src/Makefile
  5. 74 1
      src/dns_conf.c
  6. 8 0
      src/dns_conf.h
  7. 247 8
      src/dns_server.c

+ 1 - 0
ReadMe.md

@@ -602,6 +602,7 @@ entware|ipkg update<br />ipkg install smartdns|软件源路径:<https://bin.en
 | speed-check-mode | 测速模式选择 | 无 | [ping\|tcp:[80]\|none] | speed-check-mode ping,tcp:80,tcp:443 |
 | response-mode | 首次查询响应模式 | first-ping |模式:[first-ping\|fastest-ip\|fastest-response]<br /> [first-ping]: 最快ping响应地址模式,DNS上游最快查询时延+ping时延最短,查询等待与链接体验最佳;<br />[fastest-ip]: 最快IP地址模式,查询到的所有IP地址中ping最短的IP。需等待IP测速; <br />[fastest-response]: 最快响应的DNS结果,DNS查询等待时间最短,返回的IP地址可能不是最快。| response-mode first-ping |
 | address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br />- 表示忽略 <br /># 表示返回 SOA <br />4 表示 IPv4 <br />6 表示 IPv6 | address /www.example.com/1.2.3.4 |
+| cname | 指定域名别名 | 无 | cname /domain/target <br />- 表示忽略 <br />指定对应域名的cname | cname /www.example.com/cdn.example.com |
 | nameserver | 指定域名使用 server 组解析 | 无 | nameserver /domain/[group\|-], group 为组名,- 表示忽略此规则,配套 server 中的 -group 参数使用 | nameserver /www.example.com/office |
 | ipset | 域名 ipset | 无 | ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]],-表示忽略 | ipset /www.example.com/#4:dns4,#6:- <br />ipset /www.example.com/dns |
 | ipset-timeout | 设置 ipset 超时功能启用  | no | [yes\|no] | ipset-timeout yes |

+ 1 - 0
ReadMe_en.md

@@ -565,6 +565,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
 |speed-check-mode|Speed ​​mode|None|[ping\|tcp:[80]\|none]|speed-check-mode ping,tcp:80,tcp:443
 |response-mode|First query response mode|first-ping|Mode: [first-ping\|fastest-ip\|fastest-response]<br /> [first-ping]: The fastest dns + ping response mode, DNS query delay + ping delay is the shortest;<br />[fastest-ip]: The fastest IP address mode, return the fastest ip address, may take some time to test speed. <br />[fastest-response]: The fastest response DNS result mode, the DNS query waiting time is the shortest. | response-mode first-ping |
 |address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4
+|cname|set cname to domain| None | cname /domain/target <br />- for ignore <br />set cname to domain. | cname /www.example.com/cdn.example.com |
 |nameserver|To query domain with specific server group|None|nameserver /domain/[group\|-], `group` is the group name, `-` means ignore this rule, use the `-group` parameter in the related server|nameserver /www.example.com/office
 |ipset|Domain IPSet|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-` for ignore|ipset /www.example.com/#4:dns4,#6:-
 |ipset-timeout|ipset timeout enable|no|[yes\|no]|ipset-timeout yes

+ 3 - 0
etc/smartdns/smartdns.conf

@@ -226,6 +226,9 @@ log-level info
 # address /www.example.com/-, ignore address, query from upstream, suffix 4, for ipv4, 6 for ipv6, none for all
 # address /www.example.com/#, return SOA to client, suffix 4, for ipv4, 6 for ipv6, none for all
 
+# specific cname to domain
+# cname /domain/target
+
 # enable ipset timeout by ttl feature
 # ipset-timeout [yes]
 

+ 1 - 1
src/Makefile

@@ -20,7 +20,7 @@ OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_co
 
 # cflags
 ifndef CFLAGS
-CFLAGS =-O2 -g -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing -funwind-tables -Wmissing-prototypes -Wshadow -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough
+CFLAGS =-g -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing -funwind-tables -Wmissing-prototypes -Wshadow -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough
 endif
 override CFLAGS +=-Iinclude
 override CFLAGS += -DBASE_FILE_NAME='"$(notdir $<)"'

+ 74 - 1
src/dns_conf.c

@@ -191,6 +191,9 @@ static void *_new_dns_rule(enum domain_rule domain_rule)
 	case DOMAIN_RULE_CHECKSPEED:
 		size = sizeof(struct dns_domain_check_orders);
 		break;
+	case DOMAIN_RULE_CNAME:
+		size = sizeof(struct dns_cname_rule);
+		break;
 	default:
 		return NULL;
 	}
@@ -1547,6 +1550,64 @@ errout:
 	return 0;
 }
 
+static int _conf_domain_rule_cname(const char *domain, const char *cname)
+{
+	struct dns_cname_rule *cname_rule = NULL;
+	enum domain_rule type = DOMAIN_RULE_CNAME;
+
+	cname_rule = _new_dns_rule(type);
+	if (cname_rule == NULL) {
+		goto errout;
+	}
+
+	/* ignore this domain */
+	if (*cname == '-') {
+		if (_config_domain_rule_flag_set(domain, DOMAIN_FLAG_CNAME_IGN, 0) != 0) {
+			goto errout;
+		}
+
+		return 0;
+	}
+
+	safe_strncpy(cname_rule->cname, cname, DNS_MAX_CONF_CNAME_LEN);
+
+	if (_config_domain_rule_add(domain, type, cname_rule) != 0) {
+		goto errout;
+	}
+	_dns_rule_put(&cname_rule->head);
+	cname_rule = NULL;
+
+	return 0;
+
+errout:
+	tlog(TLOG_ERROR, "add cname %s:%s failed", domain, cname);
+
+	if (cname_rule) {
+		_dns_rule_put(&cname_rule->head);
+	}
+
+	return 0;
+}
+
+static int _config_cname(void *data, int argc, char *argv[])
+{
+	char *value = argv[1];
+	char domain[DNS_MAX_CONF_CNAME_LEN];
+
+	if (argc <= 1) {
+		goto errout;
+	}
+
+	if (_get_domain(value, domain, DNS_MAX_CONF_CNAME_LEN, &value) != 0) {
+		goto errout;
+	}
+
+	return _conf_domain_rule_cname(domain, value);
+errout:
+	tlog(TLOG_ERROR, "add cname %s:%s failed", domain, value);
+	return 0;
+}
+
 static void _config_speed_check_mode_clear(struct dns_domain_check_orders *check_orders)
 {
 	memset(check_orders->orders, 0, sizeof(check_orders->orders));
@@ -2326,6 +2387,7 @@ static int _conf_domain_rules(void *data, int argc, char *argv[])
 		{"nftset", required_argument, NULL, 't'},
 		{"nameserver", required_argument, NULL, 'n'},
 		{"dualstack-ip-selection", required_argument, NULL, 'd'},
+		{"cname", required_argument, NULL, 'A'},
 		{"no-serve-expired", no_argument, NULL, 254},
 		{"delete", no_argument, NULL, 255},
 		{NULL, no_argument, NULL, 0}
@@ -2344,7 +2406,7 @@ static int _conf_domain_rules(void *data, int argc, char *argv[])
 	/* process extra options */
 	optind = 1;
 	while (1) {
-		opt = getopt_long_only(argc, argv, "c:a:p:t:n:d:", long_options, NULL);
+		opt = getopt_long_only(argc, argv, "c:a:p:t:n:d:A:", long_options, NULL);
 		if (opt == -1) {
 			break;
 		}
@@ -2402,6 +2464,16 @@ static int _conf_domain_rules(void *data, int argc, char *argv[])
 
 			break;
 		}
+		case 'A': {
+			const char *cname = optarg;
+
+			if (_conf_domain_rule_cname(domain, cname) != 0) {
+				tlog(TLOG_ERROR, "add cname rule failed.");
+				goto errout;
+			}
+
+			break;
+		}
 		case 'd': {
 			const char *yesno = optarg;
 			if (_conf_domain_rule_dualstack_selection(domain, yesno) != 0) {
@@ -2866,6 +2938,7 @@ static struct config_item _config_item[] = {
 	CONF_CUSTOM("server-https", _config_server_https, NULL),
 	CONF_CUSTOM("nameserver", _config_nameserver, NULL),
 	CONF_CUSTOM("address", _config_address, NULL),
+	CONF_CUSTOM("cname", _config_cname, NULL),
 	CONF_CUSTOM("proxy-server", _config_proxy_server, NULL),
 	CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable),
 	CONF_CUSTOM("ipset", _config_ipset, NULL),

+ 8 - 0
src/dns_conf.h

@@ -74,6 +74,7 @@ enum domain_rule {
 	DOMAIN_RULE_NFTSET_IP6,
 	DOMAIN_RULE_NAMESERVER,
 	DOMAIN_RULE_CHECKSPEED,
+	DOMAIN_RULE_CNAME,
 	DOMAIN_RULE_MAX,
 };
 
@@ -104,6 +105,7 @@ typedef enum {
 #define DOMAIN_FLAG_NFTSET_IP_IGN (1 << 13)
 #define DOMAIN_FLAG_NFTSET_IP6_IGN (1 << 14)
 #define DOMAIN_FLAG_NO_SERVE_EXPIRED (1 << 15)
+#define DOMAIN_FLAG_CNAME_IGN (1 << 16)
 
 #define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0)
 
@@ -116,6 +118,7 @@ typedef enum {
 #define BIND_FLAG_NO_CACHE (1 << 6)
 #define BIND_FLAG_NO_DUALSTACK_SELECTION (1 << 7)
 #define BIND_FLAG_FORCE_AAAA_SOA (1 << 8)
+#define BIND_FLAG_NO_RULE_CNAME (1 << 9)
 
 struct dns_rule {
 	atomic_t refcnt;
@@ -156,6 +159,11 @@ struct dns_ipset_names {
 };
 extern struct dns_ipset_names dns_conf_ipset_no_speed;
 
+struct dns_cname_rule {
+	struct dns_rule head;
+	char cname[DNS_MAX_CNAME_LEN];
+};
+
 struct dns_nftset_name {
 	struct hlist_node node;
 	char nftfamilyname[DNS_MAX_NFTSET_FAMILYLEN];

+ 247 - 8
src/dns_server.c

@@ -121,6 +121,7 @@ struct dns_server_post_context {
 	int do_force_soa;
 	int skip_notify_count;
 	int select_all_best_ip;
+	int no_release_parent;
 };
 
 struct dns_server_conn_udp {
@@ -165,6 +166,8 @@ struct dns_request_pending_list {
 	struct hlist_node node;
 };
 
+typedef int (*child_request_callback)(struct dns_request *request, struct dns_request *child_request);
+
 struct dns_request {
 	atomic_t refcnt;
 
@@ -242,6 +245,10 @@ struct dns_request {
 
 	pthread_mutex_t ip_map_lock;
 
+	struct dns_request *child_request;
+	struct dns_request *parent_request;
+	child_request_callback child_callback;
+
 	atomic_t ip_map_num;
 	DECLARE_HASHTABLE(ip_map, 4);
 
@@ -281,6 +288,8 @@ static void _dns_server_request_release(struct dns_request *request);
 static void _dns_server_request_release_complete(struct dns_request *request, int do_complete);
 static int _dns_server_reply_passthrough(struct dns_server_post_context *context);
 static int _dns_server_do_query(struct dns_request *request, int skip_notify_event);
+static int _dns_request_post(struct dns_server_post_context *context);
+static int _dns_server_reply_all_pending_list(struct dns_request *request, struct dns_server_post_context *context);
 
 static void _dns_server_wakeup_thread(void)
 {
@@ -1553,6 +1562,47 @@ static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context
 	return 0;
 }
 
+static int _dns_result_child_post(struct dns_server_post_context *context)
+{
+	struct dns_request *request = context->request;
+	struct dns_request *parent_request = request->parent_request;
+	int ret = 0;
+
+	/* not a child request */
+	if (parent_request == NULL) {
+		return 0;
+	}
+
+	if (request->child_callback) {
+		ret = request->child_callback(parent_request, request);
+	}
+
+	if (context->do_reply == 1) {
+		struct dns_server_post_context parent_context;
+		_dns_server_post_context_init(&parent_context, parent_request);
+		parent_context.do_cache = context->do_cache;
+		parent_context.do_ipset = context->do_ipset;
+		parent_context.do_force_soa = context->do_force_soa;
+		parent_context.do_audit = context->do_audit;
+		parent_context.do_reply = context->do_reply;
+		parent_context.reply_ttl = context->reply_ttl;
+		parent_context.skip_notify_count = context->skip_notify_count;
+		parent_context.select_all_best_ip = 1;
+
+		_dns_request_post(&parent_context);
+		ret = _dns_server_reply_all_pending_list(parent_request, &parent_context);
+	}
+
+	if (context->no_release_parent == 0) {
+		tlog(TLOG_INFO, "query %s with cname %s done", parent_request->domain, request->domain);
+		request->parent_request = NULL;
+		parent_request->request_wait--;
+		_dns_server_request_release(parent_request);
+	}
+
+	return ret;
+}
+
 static int _dns_request_post(struct dns_server_post_context *context)
 {
 	struct dns_request *request = context->request;
@@ -1583,6 +1633,9 @@ static int _dns_request_post(struct dns_server_post_context *context)
 	/* setup ipset */
 	_dns_server_setup_ipset_nftset_packet(context);
 
+	/* reply child request */
+	_dns_result_child_post(context);
+
 	if (context->do_reply == 0) {
 		return 0;
 	}
@@ -1811,6 +1864,7 @@ out:
 	context.reply_ttl = reply_ttl;
 	context.skip_notify_count = 1;
 	context.select_all_best_ip = with_all_ips;
+	context.no_release_parent = 1;
 
 	_dns_request_post(&context);
 	return _dns_server_reply_all_pending_list(request, &context);
@@ -1822,12 +1876,16 @@ static int _dns_server_request_complete(struct dns_request *request)
 }
 
 static int _dns_ip_address_check_add(struct dns_request *request, char *cname, unsigned char *addr,
-									 dns_type_t addr_type)
+									 dns_type_t addr_type, int ping_time)
 {
 	uint32_t key = 0;
 	struct dns_ip_address *addr_map = NULL;
 	int addr_len = 0;
 
+	if (ping_time == 0) {
+		ping_time = -1;
+	}
+
 	if (addr_type == DNS_T_A) {
 		addr_len = DNS_RR_A_LEN;
 	} else if (addr_type == DNS_T_AAAA) {
@@ -1868,7 +1926,7 @@ static int _dns_ip_address_check_add(struct dns_request *request, char *cname, u
 	addr_map->addr_type = addr_type;
 	addr_map->hitnum = 1;
 	addr_map->recv_tick = get_tick_count();
-	addr_map->ping_time = -1;
+	addr_map->ping_time = ping_time;
 	memcpy(addr_map->ip_addr, addr, addr_len);
 	if (dns_conf_force_no_cname == 0) {
 		safe_strncpy(addr_map->cname, cname, DNS_MAX_CNAME_LEN);
@@ -2050,6 +2108,11 @@ static void _dns_server_request_release_complete(struct dns_request *request, in
 		_dns_server_complete_with_multi_ipaddress(request);
 	}
 
+	if (request->parent_request != NULL) {
+		_dns_server_request_release(request->parent_request);
+		request->parent_request = NULL;
+	}
+
 	pthread_mutex_lock(&request->ip_map_lock);
 	hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
 	{
@@ -2530,7 +2593,7 @@ static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request
 	}
 
 	/* add this ip to request */
-	if (_dns_ip_address_check_add(request, cname, addr, DNS_T_A) != 0) {
+	if (_dns_ip_address_check_add(request, cname, addr, DNS_T_A, 0) != 0) {
 		_dns_server_request_release(request);
 		return -1;
 	}
@@ -2607,7 +2670,7 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque
 	}
 
 	/* add this ip to request */
-	if (_dns_ip_address_check_add(request, cname, addr, DNS_T_AAAA) != 0) {
+	if (_dns_ip_address_check_add(request, cname, addr, DNS_T_AAAA, 0) != 0) {
 		_dns_server_request_release(request);
 		return -1;
 	}
@@ -2883,7 +2946,8 @@ static int _dns_server_get_answer(struct dns_server_post_context *context)
 					continue;
 				}
 
-				if (context->no_check_add_ip == 0 && _dns_ip_address_check_add(request, name, addr, DNS_T_A) != 0) {
+				if (context->no_check_add_ip == 0 &&
+					_dns_ip_address_check_add(request, name, addr, DNS_T_A, request->ping_time) != 0) {
 					continue;
 				}
 
@@ -2913,7 +2977,8 @@ static int _dns_server_get_answer(struct dns_server_post_context *context)
 					continue;
 				}
 
-				if (context->no_check_add_ip == 0 && _dns_ip_address_check_add(request, name, addr, DNS_T_AAAA) != 0) {
+				if (context->no_check_add_ip == 0 &&
+					_dns_ip_address_check_add(request, name, addr, DNS_T_AAAA, request->ping_time) != 0) {
 					continue;
 				}
 
@@ -2983,16 +3048,19 @@ static int _dns_server_reply_passthrough(struct dns_server_post_context *context
 
 	_dns_server_get_answer(context);
 
-	_dns_result_callback(context);
-
 	_dns_cache_reply_packet(context);
 
 	if (_dns_server_setup_ipset_nftset_packet(context) != 0) {
 		tlog(TLOG_DEBUG, "setup ipset failed.");
 	}
 
+	_dns_result_callback(context);
+
 	_dns_server_audit_log(context);
 
+	/* reply child request */
+	_dns_result_child_post(context);
+
 	if (request->conn && context->do_reply == 1) {
 		/* When passthrough, modify the id to be the id of the client request. */
 		struct dns_update_param param;
@@ -3730,6 +3798,173 @@ errout:
 	return -1;
 }
 
+static struct dns_request *_dns_server_new_child_request(struct dns_request *request,
+														 child_request_callback child_callback)
+{
+	struct dns_request *child_request = NULL;
+
+	child_request = _dns_server_new_request();
+	if (child_request == NULL) {
+		tlog(TLOG_ERROR, "malloc failed.\n");
+		goto errout;
+	}
+
+	child_request->server_flags = request->server_flags;
+	safe_strncpy(child_request->dns_group_name, request->dns_group_name, sizeof(request->dns_group_name));
+	child_request->prefetch = request->prefetch;
+	child_request->prefetch_expired_domain = request->prefetch_expired_domain;
+	child_request->child_callback = child_callback;
+	child_request->parent_request = request;
+	_dns_server_request_get(request);
+	/* reference count is 1 hold by parent request */
+	request->child_request = child_request;
+	return child_request;
+errout:
+	if (child_request) {
+		_dns_server_request_release(child_request);
+	}
+
+	return NULL;
+}
+
+static int _dns_server_request_copy(struct dns_request *request, struct dns_request *from)
+{
+	unsigned long bucket = 0;
+	struct dns_ip_address *addr_map = NULL;
+	struct hlist_node *tmp = NULL;
+	uint32_t key = 0;
+	int addr_len = 0;
+
+	request->rcode = from->rcode;
+
+	if (from->has_ip) {
+		request->has_ip = 1;
+		request->ip_ttl = from->ip_ttl;
+		request->ping_time = from->ping_time;
+		memcpy(request->ip_addr, from->ip_addr, sizeof(request->ip_addr));
+	}
+
+	if (from->has_cname) {
+		request->has_cname = 1;
+		request->ttl_cname = from->ttl_cname;
+		safe_strncpy(request->cname, from->cname, sizeof(request->cname));
+	}
+
+	if (from->has_soa) {
+		request->has_soa = 1;
+		memcpy(&request->soa, &from->soa, sizeof(request->soa));
+	}
+
+	pthread_mutex_lock(&request->ip_map_lock);
+	hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
+	{
+		hash_del(&addr_map->node);
+		free(addr_map);
+	}
+	pthread_mutex_unlock(&request->ip_map_lock);
+
+	pthread_mutex_lock(&from->ip_map_lock);
+	hash_for_each_safe(from->ip_map, bucket, tmp, addr_map, node)
+	{
+		struct dns_ip_address *new_addr_map = NULL;
+
+		if (addr_map->addr_type == DNS_T_A) {
+			addr_len = DNS_RR_A_LEN;
+		} else if (addr_map->addr_type == DNS_T_AAAA) {
+			addr_len = DNS_RR_AAAA_LEN;
+		} else {
+			continue;
+		}
+
+		new_addr_map = malloc(sizeof(struct dns_ip_address));
+		if (new_addr_map == NULL) {
+			tlog(TLOG_ERROR, "malloc failed.\n");
+			pthread_mutex_unlock(&from->ip_map_lock);
+			return -1;
+		}
+
+		memcpy(new_addr_map, addr_map, sizeof(struct dns_ip_address));
+		new_addr_map->ping_time = addr_map->ping_time;
+		key = jhash(new_addr_map->ip_addr, addr_len, 0);
+		key = jhash(&addr_map->addr_type, sizeof(addr_map->addr_type), key);
+		pthread_mutex_lock(&request->ip_map_lock);
+		hash_add(request->ip_map, &new_addr_map->node, key);
+		pthread_mutex_unlock(&request->ip_map_lock);
+	}
+	pthread_mutex_unlock(&from->ip_map_lock);
+
+	return 0;
+}
+
+static int _dns_server_process_cname_callback(struct dns_request *request, struct dns_request *child_request)
+{
+	_dns_server_request_copy(request, child_request);
+	safe_strncpy(request->cname, child_request->domain, sizeof(request->cname));
+
+	return 0;
+}
+
+static int _dns_server_process_cname(struct dns_request *request)
+{
+	struct dns_cname_rule *cname = NULL;
+	int ret = 0;
+	struct dns_rule_flags *rule_flag = NULL;
+
+	if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_CNAME) == 0) {
+		return 0;
+	}
+
+	/* get domain rule flag */
+	rule_flag = _dns_server_get_dns_rule(request, DOMAIN_RULE_FLAGS);
+	if (rule_flag != NULL) {
+		if (rule_flag->flags & DOMAIN_FLAG_CNAME_IGN) {
+			return 0;
+		}
+	}
+
+	/* cname /domain/ rule */
+	if (request->domain_rule.rules[DOMAIN_RULE_CNAME] == NULL) {
+		return 0;
+	}
+
+	cname = _dns_server_get_dns_rule(request, DOMAIN_RULE_CNAME);
+	if (cname == NULL) {
+		return 0;
+	}
+
+	tlog(TLOG_INFO, "query %s with cname %s", request->domain, cname->cname);
+
+	struct dns_request *child_request = _dns_server_new_child_request(request, _dns_server_process_cname_callback);
+	if (child_request == NULL) {
+		tlog(TLOG_ERROR, "malloc failed.\n");
+		return -1;
+	}
+
+	child_request->qtype = request->qtype;
+	child_request->qclass = request->qclass;
+	safe_strncpy(child_request->domain, cname->cname, sizeof(child_request->cname));
+
+	request->request_wait++;
+	ret = _dns_server_do_query(child_request, 0);
+	if (ret != 0) {
+		request->request_wait--;
+		tlog(TLOG_ERROR, "do query %s type %d failed.\n", request->domain, request->qtype);
+		goto errout;
+	}
+
+	_dns_server_request_release_complete(child_request, 0);
+	return 1;
+
+errout:
+
+	if (child_request) {
+		request->child_request = NULL;
+		_dns_server_request_release(child_request);
+	}
+
+	return -1;
+}
+
 static int _dns_server_qtype_soa(struct dns_request *request)
 {
 	struct dns_qtype_soa_list *soa_list = NULL;
@@ -4408,6 +4643,10 @@ static int _dns_server_do_query(struct dns_request *request, int skip_notify_eve
 		goto clean_exit;
 	}
 
+	if (_dns_server_process_cname(request) != 0) {
+		goto clean_exit;
+	}
+
 	// setup options
 	_dns_server_setup_query_option(request, &options);