Browse Source

Feature: select all best ip

Nick Peng 3 years ago
parent
commit
6e1363dca4
1 changed files with 328 additions and 253 deletions
  1. 328 253
      src/dns_server.c

+ 328 - 253
src/dns_server.c

@@ -126,6 +126,7 @@ struct dns_ip_address {
 	struct hlist_node node;
 	int hitnum;
 	unsigned long recv_tick;
+	int ping_ttl;
 	dns_type_t addr_type;
 	union {
 		unsigned char ipv4_addr[DNS_RR_A_LEN];
@@ -397,15 +398,15 @@ static int _dns_add_rrs(struct dns_packet *packet, struct dns_request *request)
 
 	/* add A record */
 	if (request->has_ipv4 && request->qtype == DNS_T_A) {
-		ret |= dns_add_A(packet, DNS_RRS_AN, domain, request->ttl_v4, request->ipv4_addr);
+		ret |= dns_add_A(packet, DNS_RRS_AN, domain, 3, request->ipv4_addr);
 	}
 
 	/* add AAAA record */
 	if (request->has_ipv6 && request->qtype == DNS_T_AAAA) {
 		if (request->has_ipv4) {
-			ret |= dns_add_A(packet, DNS_RRS_AN, domain, request->ttl_v4, request->ipv4_addr);
+			ret |= dns_add_A(packet, DNS_RRS_AN, domain, 3, request->ipv4_addr);
 		}
-		ret |= dns_add_AAAA(packet, DNS_RRS_AN, domain, request->ttl_v6, request->ipv6_addr);
+		ret |= dns_add_AAAA(packet, DNS_RRS_AN, domain, 3, request->ipv6_addr);
 	}
 
 	/* add SOA record */
@@ -536,7 +537,69 @@ static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpac
 	return ret;
 }
 
-static int _dns_reply(struct dns_request *request)
+static int _dns_server_request_update_cache(struct dns_request *request, dns_type_t qtype,
+											struct dns_cache_data *cache_data)
+{
+	int ttl;
+	int speed = 0;
+
+	if (qtype == DNS_T_A) {
+		ttl = _dns_server_get_conf_ttl(request->ttl_v4);
+		speed = request->ping_ttl_v4;
+	} else if (qtype == DNS_T_AAAA) {
+		ttl = _dns_server_get_conf_ttl(request->ttl_v6);
+		speed = request->ping_ttl_v6;
+	} else {
+		goto errout;
+	}
+
+	if (request->has_soa) {
+		ttl = dns_conf_rr_ttl;
+	}
+
+	/* if doing prefetch, update cache only */
+	if (request->prefetch) {
+		if (dns_cache_replace(request->domain, ttl, qtype, speed, cache_data) != 0) {
+			goto errout;
+		}
+	} else {
+		/* insert result to cache */
+		if (dns_cache_insert(request->domain, ttl, qtype, speed, cache_data) != 0) {
+			goto errout;
+		}
+	}
+
+	return 0;
+errout:
+	if (cache_data) {
+		dns_cache_data_free(cache_data);
+	}
+	return -1;
+}
+
+static int _dns_cache_reply_packet(struct dns_request *request, unsigned char *inpacket, int inpacket_len)
+{
+	if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) == 0) {
+		return 0;
+	}
+
+	if (request->qtype != DNS_T_AAAA && request->qtype != DNS_T_A) {
+		return 0;
+	}
+
+	struct dns_cache_data *cache_packet = dns_cache_new_data_packet(request->server_flags, inpacket, inpacket_len);
+	if (cache_packet == NULL) {
+		return -1;
+	}
+
+	if (_dns_server_request_update_cache(request, request->qtype, cache_packet) != 0) {
+		tlog(TLOG_WARN, "update packet cache failed.");
+	}
+
+	return 0;
+}
+
+static int _dns_reply(struct dns_request *request, int docache)
 {
 	unsigned char inpacket[DNS_IN_PACKSIZE];
 	unsigned char packet_buff[DNS_PACKSIZE];
@@ -587,6 +650,14 @@ static int _dns_reply(struct dns_request *request)
 
 	/* send request */
 	atomic_inc_return(&request->notified);
+
+	if (docache) {
+		ret = _dns_cache_reply_packet(request, inpacket, encode_len);
+		if (ret != 0) {
+			tlog(TLOG_WARN, "cache packet for %s failed.", request->domain);
+		}
+	}
+
 	return _dns_reply_inpacket(request, inpacket, encode_len);
 }
 
@@ -674,7 +745,7 @@ static int _dns_server_reply_SOA(int rcode, struct dns_request *request)
 
 	_dns_result_callback(request);
 
-	_dns_reply(request);
+	_dns_reply(request, 1);
 
 	return 0;
 }
@@ -745,63 +816,13 @@ static int _dns_setup_ipset(struct dns_request *request)
 	return ret;
 }
 
-static int _dns_server_request_update_cache(struct dns_request *request, dns_type_t qtype,
-											struct dns_cache_data *cache_data)
-{
-	int ttl;
-	int speed = 0;
-
-	if (qtype == DNS_T_A) {
-		ttl = _dns_server_get_conf_ttl(request->ttl_v4);
-		speed = request->ping_ttl_v4;
-	} else if (qtype == DNS_T_AAAA) {
-		ttl = _dns_server_get_conf_ttl(request->ttl_v6);
-		speed = request->ping_ttl_v6;
-	} else {
-		goto errout;
-	}
-
-	if (request->has_soa) {
-		ttl = dns_conf_rr_ttl;
-	}
-
-	/* if doing prefetch, update cache only */
-	if (request->prefetch) {
-		if (dns_cache_replace(request->domain, ttl, qtype, speed, cache_data) != 0) {
-			goto errout;
-		}
-	} else {
-		/* insert result to cache */
-		if (dns_cache_insert(request->domain, ttl, qtype, speed, cache_data) != 0) {
-			goto errout;
-		}
-	}
-
-	return 0;
-errout:
-	if (cache_data) {
-		dns_cache_data_free(cache_data);
-	}
-	return -1;
-}
-
-static int _dns_server_request_complete_A(struct dns_request *request)
+static int _dns_server_request_complete(struct dns_request *request)
 {
-	char *cname = NULL;
-	int cname_ttl = dns_conf_rr_ttl;
-	struct dns_cache_data *cache_data = NULL;
-
-	if (request->has_cname) {
-		cname = request->cname;
-		cname_ttl = request->ttl_cname;
-	}
-
-	cache_data = dns_cache_new_data();
-	if (cache_data == NULL) {
-		goto errout;
+	if (atomic_inc_return(&request->notified) != 1) {
+		return 0;
 	}
 
-	if (request->has_ipv4 != 0) {
+	if (request->qtype == DNS_T_A && request->has_ipv4 != 0) {
 		tlog(TLOG_INFO, "result: %s, rcode: %d,  %d.%d.%d.%d\n", request->domain, request->rcode, request->ipv4_addr[0],
 			 request->ipv4_addr[1], request->ipv4_addr[2], request->ipv4_addr[3]);
 
@@ -809,165 +830,151 @@ static int _dns_server_request_complete_A(struct dns_request *request)
 		if (request->has_ping_result == 0 && request->ttl_v4 > DNS_SERVER_TMOUT_TTL) {
 			request->ttl_v4 = DNS_SERVER_TMOUT_TTL;
 		}
-		dns_cache_set_data_addr(cache_data, request->server_flags, cname, cname_ttl, request->ipv4_addr, DNS_RR_A_LEN);
-	} else if (request->has_soa) {
-		dns_cache_set_data_soa(cache_data, request->server_flags, cname, cname_ttl);
-	}
-
-	if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) == 0) {
-		dns_cache_data_free(cache_data);
-		return 0;
-	}
+	} else if (request->qtype == DNS_T_AAAA) {
+		if (request->has_ipv6) {
+			tlog(TLOG_INFO,
+				 "result: %s, rcode: %d,  %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
+				 request->domain, request->rcode, request->ipv6_addr[0], request->ipv6_addr[1], request->ipv6_addr[2],
+				 request->ipv6_addr[3], request->ipv6_addr[4], request->ipv6_addr[5], request->ipv6_addr[6],
+				 request->ipv6_addr[7], request->ipv6_addr[8], request->ipv6_addr[9], request->ipv6_addr[10],
+				 request->ipv6_addr[11], request->ipv6_addr[12], request->ipv6_addr[13], request->ipv6_addr[14],
+				 request->ipv6_addr[15]);
 
-	if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
-		goto errout;
-	}
+			if (request->has_ping_result == 0 && request->ttl_v6 > DNS_SERVER_TMOUT_TTL) {
+				request->ttl_v6 = DNS_SERVER_TMOUT_TTL;
+			}
 
-	return 0;
+			request->has_soa = 0;
+		}
 
-errout:
-	if (cache_data) {
-		dns_cache_data_free(cache_data);
-		cache_data = NULL;
-	}
+		if (request->has_ipv4 && (request->ping_ttl_v4 > 0)) {
+			tlog(TLOG_INFO, "result: %s, rcode: %d,  %d.%d.%d.%d\n", request->domain, request->rcode,
+				 request->ipv4_addr[0], request->ipv4_addr[1], request->ipv4_addr[2], request->ipv4_addr[3]);
 
-	return -1;
-}
+			/* if ipv4 is fasting than ipv6, add ipv4 to cache, and return SOA for AAAA request */
+			if ((request->ping_ttl_v4 + (dns_conf_dualstack_ip_selection_threshold * 10)) < request->ping_ttl_v6 ||
+				request->ping_ttl_v6 < 0) {
+				tlog(TLOG_DEBUG, "Force IPV4 perfered.");
+				// TODO cache ipv4 result
 
-static int _dns_server_request_complete_AAAA(struct dns_request *request)
-{
-	int ret = -1;
-	char *cname = NULL;
-	int cname_ttl = dns_conf_rr_ttl;
-	struct dns_cache_data *cache_data = NULL;
+				if (request->dualstack_selection) {
+					if (_dns_server_reply_SOA(DNS_RC_NOERROR, request) == 0) {
+						return 0;
+					}
+				}
+			}
+		}
 
-	if (request->has_cname) {
-		cname = request->cname;
-		cname_ttl = request->ttl_cname;
+		request->has_ipv4 = 0;
 	}
 
-	cache_data = dns_cache_new_data();
-	if (cache_data == NULL) {
-		goto errout;
+	if (request->has_soa) {
+		tlog(TLOG_INFO, "result: %s, qtype: %d, SOA", request->domain, request->qtype);
 	}
 
-	if (request->has_ipv6) {
-		tlog(TLOG_INFO,
-			 "result: %s, rcode: %d,  %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
-			 request->domain, request->rcode, request->ipv6_addr[0], request->ipv6_addr[1], request->ipv6_addr[2],
-			 request->ipv6_addr[3], request->ipv6_addr[4], request->ipv6_addr[5], request->ipv6_addr[6],
-			 request->ipv6_addr[7], request->ipv6_addr[8], request->ipv6_addr[9], request->ipv6_addr[10],
-			 request->ipv6_addr[11], request->ipv6_addr[12], request->ipv6_addr[13], request->ipv6_addr[14],
-			 request->ipv6_addr[15]);
-
-		if (request->has_ping_result == 0 && request->ttl_v6 > DNS_SERVER_TMOUT_TTL) {
-			request->ttl_v6 = DNS_SERVER_TMOUT_TTL;
-		}
-
-		/* if doing prefetch, update cache only */
-		dns_cache_set_data_addr(cache_data, request->server_flags, cname, cname_ttl, request->ipv6_addr, DNS_T_AAAA);
+	/* update ipset */
+	_dns_setup_ipset(request);
 
-		request->has_soa = 0;
-	} else if (request->has_soa) {
-		dns_cache_set_data_soa(cache_data, request->server_flags, cname, cname_ttl);
-	}
+	_dns_result_callback(request);
 
-	if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
-		if (_dns_server_request_update_cache(request, DNS_T_AAAA, cache_data) != 0) {
-			goto errout;
-		}
-		cache_data = NULL;
-	} else {
-		dns_cache_data_free(cache_data);
-		cache_data = NULL;
+	if (request->prefetch) {
+		return 0;
 	}
 
-	if (request->has_ipv4 && (request->ping_ttl_v4 > 0)) {
-		tlog(TLOG_INFO, "result: %s, rcode: %d,  %d.%d.%d.%d\n", request->domain, request->rcode, request->ipv4_addr[0],
-			 request->ipv4_addr[1], request->ipv4_addr[2], request->ipv4_addr[3]);
+	/* return result to client */
+	_dns_reply(request, 1);
 
-		/* if ipv4 is fasting than ipv6, add ipv4 to cache, and return SOA for AAAA request */
-		if ((request->ping_ttl_v4 + (dns_conf_dualstack_ip_selection_threshold * 10)) < request->ping_ttl_v6 ||
-			request->ping_ttl_v6 < 0) {
-			tlog(TLOG_DEBUG, "Force IPV4 perfered.");
-			if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
-				cache_data = dns_cache_new_data();
-				if (cache_data == NULL) {
-					goto errout;
-				}
+	return 0;
+}
 
-				dns_cache_set_data_addr(cache_data, request->server_flags, cname, cname_ttl, request->ipv4_addr,
-										DNS_T_A);
-				if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
-					goto errout;
-				}
-				cache_data = NULL;
-			}
+struct dns_ip_address *_dns_ip_address_get(struct dns_request *request, unsigned char *addr, dns_type_t addr_type)
+{
+	uint32_t key = 0;
+	struct dns_ip_address *addr_map = NULL;
+	struct dns_ip_address *addr_tmp = NULL;
+	int addr_len = 0;
 
-			if (request->dualstack_selection) {
-				if (_dns_server_reply_SOA(DNS_RC_NOERROR, request) != 0) {
-					ret = -1;
-					goto errout;
-				}
+	if (addr_type == DNS_T_A) {
+		addr_len = DNS_RR_A_LEN;
+	} else if (addr_type == DNS_T_AAAA) {
+		addr_len = DNS_RR_AAAA_LEN;
+	} else {
+		return NULL;
+	}
 
-				ret = 1;
-				goto errout;
+	/* store the ip address and the number of hits */
+	key = jhash(addr, addr_len, 0);
+	pthread_mutex_lock(&request->ip_map_lock);
+	hash_for_each_possible(request->ip_map, addr_tmp, node, key)
+	{
+		if (addr_type == DNS_T_A) {
+			if (memcmp(addr_tmp->ipv4_addr, addr, addr_len) == 0) {
+				addr_map = addr_tmp;
+				break;
+			}
+		} else if (addr_type == DNS_T_AAAA) {
+			if (memcmp(addr_tmp->ipv6_addr, addr, addr_len) == 0) {
+				addr_map = addr_tmp;
+				break;
 			}
 		}
 	}
+	pthread_mutex_unlock(&request->ip_map_lock);
 
-	request->has_ipv4 = 0;
-
-	return 0;
-
-errout:
-	if (cache_data != NULL) {
-		dns_cache_data_free(cache_data);
-		cache_data = NULL;
-	}
-
-	return ret;
+	return addr_map;
 }
 
-static int _dns_server_request_complete(struct dns_request *request)
+static int _dns_ip_address_check_add(struct dns_request *request, unsigned char *addr, dns_type_t addr_type)
 {
-	int ret = 0;
+	uint32_t key = 0;
+	struct dns_ip_address *addr_map = NULL;
+	int addr_len = 0;
 
-	if (atomic_inc_return(&request->notified) != 1) {
-		return 0;
+	if (addr_type == DNS_T_A) {
+		addr_len = DNS_RR_A_LEN;
+	} else if (addr_type == DNS_T_AAAA) {
+		addr_len = DNS_RR_AAAA_LEN;
+	} else {
+		return -1;
 	}
 
-	if (request->qtype == DNS_T_A) {
-		if (_dns_server_request_complete_A(request) != 0) {
-			tlog(TLOG_ERROR, "complete DNS A failed.");
-			return -1;
-		}
-	} else if (request->qtype == DNS_T_AAAA) {
-		ret = _dns_server_request_complete_AAAA(request);
-		if (ret != 0) {
-			if (ret == 1) {
-				return 0;
+	/* store the ip address and the number of hits */
+	key = jhash(addr, addr_len, 0);
+	pthread_mutex_lock(&request->ip_map_lock);
+	hash_for_each_possible(request->ip_map, addr_map, node, key)
+	{
+		if (addr_type == DNS_T_A) {
+			if (memcmp(addr_map->ipv4_addr, addr, addr_len) == 0) {
+				addr_map->hitnum++;
+				addr_map->recv_tick = get_tick_count();
+				pthread_mutex_unlock(&request->ip_map_lock);
+				return -1;
+			}
+		} else if (addr_type == DNS_T_AAAA) {
+			if (memcmp(addr_map->ipv6_addr, addr, addr_len) == 0) {
+				addr_map->hitnum++;
+				addr_map->recv_tick = get_tick_count();
+				pthread_mutex_unlock(&request->ip_map_lock);
+				return -1;
 			}
-			tlog(TLOG_ERROR, "complete DNS A failed.");
-			return -1;
 		}
 	}
+	request->ip_map_num++;
 
-	if (request->has_soa) {
-		tlog(TLOG_INFO, "result: %s, qtype: %d, SOA", request->domain, request->qtype);
-	}
-
-	/* update ipset */
-	_dns_setup_ipset(request);
-
-	_dns_result_callback(request);
-
-	if (request->prefetch) {
-		return 0;
+	addr_map = malloc(sizeof(*addr_map));
+	if (addr_map == NULL) {
+		pthread_mutex_unlock(&request->ip_map_lock);
+		tlog(TLOG_ERROR, "malloc failed");
+		return -1;
 	}
 
-	/* return result to client */
-	_dns_reply(request);
+	addr_map->addr_type = addr_type;
+	addr_map->hitnum = 1;
+	addr_map->recv_tick = get_tick_count();
+	addr_map->ping_ttl = -1;
+	memcpy(addr_map->addr, addr, addr_len);
+	hash_add(request->ip_map, &addr_map->node, key);
+	pthread_mutex_unlock(&request->ip_map_lock);
 
 	return 0;
 }
@@ -1068,6 +1075,115 @@ static void _dns_server_delete_request(struct dns_request *request)
 	free(request);
 }
 
+static void _dns_server_setup_multi_ip_cache(struct dns_request *request)
+{
+	struct dns_ip_address *addr_map;
+	struct hlist_node *tmp;
+	int bucket = 0;
+
+	unsigned char inpacket[DNS_IN_PACKSIZE];
+	unsigned char packet_buff[DNS_PACKSIZE];
+	char *domain;
+	struct dns_packet *packet = (struct dns_packet *)packet_buff;
+	struct dns_head head;
+	int ret = 0;
+	int encode_len = 0;
+	int ip_num = 0;
+	int ignore_speed = 0;
+	int maxhit = 0;
+
+	memset(&head, 0, sizeof(head));
+	head.id = request->id;
+	head.qr = DNS_QR_ANSWER;
+	head.opcode = DNS_OP_QUERY;
+	head.rd = 1;
+	head.ra = 1;
+	head.aa = 0;
+	head.tc = 0;
+	head.rcode = request->rcode;
+
+	/* init a new DNS packet */
+	ret = dns_packet_init(packet, DNS_PACKSIZE, &head);
+	if (ret != 0) {
+		return;
+	}
+	domain = request->domain;
+
+	/* add request domain */
+	ret = dns_add_domain(packet, request->domain, request->qtype, DNS_C_IN);
+	if (ret != 0) {
+		return;
+	}
+
+	/* add CNAME record */
+	if (request->has_cname) {
+		ret |= dns_add_CNAME(packet, DNS_RRS_AN, request->domain, request->ttl_cname, request->cname);
+		domain = request->cname;
+	}
+
+	while (true) {
+		pthread_mutex_lock(&request->ip_map_lock);
+		hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
+		{
+			if (request->qtype != addr_map->addr_type) {
+				continue;
+			}
+
+			if (addr_map->hitnum > maxhit) {
+				maxhit = addr_map->hitnum;
+			}
+
+			if (addr_map->ping_ttl < 0 && ignore_speed == 0) {
+				continue;
+			}
+
+			if (addr_map->hitnum < maxhit && ignore_speed == 1) {
+				continue;
+			}
+
+			if (addr_map->addr_type == DNS_T_A) {
+				int ttl_range = request->ping_ttl_v4 + request->ping_ttl_v4 / 10;
+				if ((ttl_range < addr_map->ping_ttl) && addr_map->ping_ttl >= 100 && ignore_speed == 0) {
+					continue;
+				}
+				ret |= dns_add_A(packet, DNS_RRS_AN, domain, request->ttl_v4, addr_map->ipv4_addr);
+				ip_num++;
+			} else if (addr_map->addr_type == DNS_T_AAAA) {
+				int ttl_range = request->ping_ttl_v6 + request->ping_ttl_v6 / 10;
+				if (ttl_range < addr_map->ping_ttl && addr_map->ping_ttl >= 100 && ignore_speed == 0) {
+					continue;
+				}
+				ret |= dns_add_AAAA(packet, DNS_RRS_AN, domain, request->ttl_v6, addr_map->ipv6_addr);
+				ip_num++;
+			}
+		}
+		pthread_mutex_unlock(&request->ip_map_lock);
+
+		if (ip_num <= 0 && ignore_speed == 0) {
+			ignore_speed = 1;
+		} else {
+			break;
+		}
+	}
+
+	if (ret || ip_num <= 0) {
+		return;
+	}
+
+	/* encode to binary data */
+	encode_len = dns_encode(inpacket, DNS_IN_PACKSIZE, packet);
+	if (encode_len <= 0) {
+		return;
+	}
+
+	ret = _dns_cache_reply_packet(request, inpacket, encode_len);
+	if (ret != 0) {
+		tlog(TLOG_WARN, "cache packet for %s failed.", request->domain);
+	}
+
+	return;
+}
+
 static void _dns_server_request_release_complete(struct dns_request *request, int do_complete)
 {
 	struct dns_ip_address *addr_map;
@@ -1093,6 +1209,7 @@ static void _dns_server_request_release_complete(struct dns_request *request, in
 		_dns_server_request_complete(request);
 	}
 
+	_dns_server_setup_multi_ip_cache(request);
 	pthread_mutex_lock(&request->ip_map_lock);
 	hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
 	{
@@ -1158,6 +1275,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
 	struct dns_request *request = userptr;
 	int may_complete = 0;
 	int threshold = 100;
+	struct dns_ip_address *addr_map = NULL;
 
 	if (request == NULL) {
 		return;
@@ -1183,6 +1301,11 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
 			memcpy(request->ipv4_addr, &addr_in->sin_addr.s_addr, 4);
 		}
 
+		addr_map = _dns_ip_address_get(request, (unsigned char *)&addr_in->sin_addr.s_addr, DNS_T_A);
+		if (addr_map) {
+			addr_map->ping_ttl = rtt;
+		}
+
 		if (request->qtype == DNS_T_AAAA && request->dualstack_selection) {
 			if (request->ping_ttl_v6 < 0 && request->has_soa == 0) {
 				return;
@@ -1197,12 +1320,20 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
 				request->ping_ttl_v4 = rtt;
 				request->has_ipv4 = 1;
 				memcpy(request->ipv4_addr, addr_in6->sin6_addr.s6_addr + 12, 4);
+				addr_map = _dns_ip_address_get(request, addr_in6->sin6_addr.s6_addr + 12, DNS_T_A);
+				if (addr_map) {
+					addr_map->ping_ttl = rtt;
+				}
 			}
 		} else {
 			if (request->ping_ttl_v6 > rtt) {
 				request->ping_ttl_v6 = rtt;
 				request->has_ipv6 = 1;
 				memcpy(request->ipv6_addr, addr_in6->sin6_addr.s6_addr, 16);
+				addr_map = _dns_ip_address_get(request, addr_in6->sin6_addr.s6_addr, DNS_T_AAAA);
+				if (addr_map) {
+					addr_map->ping_ttl = rtt;
+				}
 			}
 		}
 	} break;
@@ -1267,60 +1398,6 @@ static int _dns_server_check_speed(struct dns_request *request, char *ip, int mo
 	return -1;
 }
 
-static int _dns_ip_address_check_add(struct dns_request *request, unsigned char *addr, dns_type_t addr_type)
-{
-	uint32_t key = 0;
-	struct dns_ip_address *addr_map = NULL;
-	int addr_len = 0;
-
-	if (addr_type == DNS_T_A) {
-		addr_len = DNS_RR_A_LEN;
-	} else if (addr_type == DNS_T_AAAA) {
-		addr_len = DNS_RR_AAAA_LEN;
-	} else {
-		return -1;
-	}
-
-	/* store the ip address and the number of hits */
-	key = jhash(addr, addr_len, 0);
-	pthread_mutex_lock(&request->ip_map_lock);
-	hash_for_each_possible(request->ip_map, addr_map, node, key)
-	{
-		if (addr_type == DNS_T_A) {
-			if (memcmp(addr_map->ipv4_addr, addr, addr_len) == 0) {
-				addr_map->hitnum++;
-				addr_map->recv_tick = get_tick_count();
-				pthread_mutex_unlock(&request->ip_map_lock);
-				return -1;
-			}
-		} else if (addr_type == DNS_T_AAAA) {
-			if (memcmp(addr_map->ipv6_addr, addr, addr_len) == 0) {
-				addr_map->hitnum++;
-				addr_map->recv_tick = get_tick_count();
-				pthread_mutex_unlock(&request->ip_map_lock);
-				return -1;
-			}
-		}
-	}
-	request->ip_map_num++;
-
-	addr_map = malloc(sizeof(*addr_map));
-	if (addr_map == NULL) {
-		pthread_mutex_unlock(&request->ip_map_lock);
-		tlog(TLOG_ERROR, "malloc failed");
-		return -1;
-	}
-
-	addr_map->addr_type = addr_type;
-	addr_map->hitnum = 1;
-	addr_map->recv_tick = get_tick_count();
-	memcpy(addr_map->addr, addr, addr_len);
-	hash_add(request->ip_map, &addr_map->node, key);
-	pthread_mutex_unlock(&request->ip_map_lock);
-
-	return 0;
-}
-
 static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char *addr, int addr_len,
 									 dns_type_t addr_type, int result_flag)
 {
@@ -1950,17 +2027,7 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
 		return ret;
 	}
 
-	if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0 &&
-		(request->qtype == DNS_T_AAAA || request->qtype == DNS_T_A)) {
-		struct dns_cache_data *cache_packet = dns_cache_new_data_packet(request->server_flags, inpacket, inpacket_len);
-		if (cache_packet == NULL) {
-			return ret;
-		}
-
-		if (_dns_server_request_update_cache(request, request->qtype, cache_packet) != 0) {
-			tlog(TLOG_WARN, "update packet cache failed.");
-		}
-	}
+	_dns_cache_reply_packet(request, inpacket, inpacket_len);
 
 	if (_dns_server_setup_ipset_packet(request, packet) != 0) {
 		tlog(TLOG_DEBUG, "setup ipset failed.");
@@ -2093,7 +2160,7 @@ static int _dns_server_process_ptr(struct dns_request *request)
 
 	request->rcode = DNS_RC_NOERROR;
 	request->has_ptr = 1;
-	_dns_reply(request);
+	_dns_reply(request, 0);
 
 	freeifaddrs(ifaddr);
 	return 0;
@@ -2330,7 +2397,7 @@ static int _dns_server_process_address(struct dns_request *request)
 	}
 
 	request->rcode = DNS_RC_NOERROR;
-	_dns_reply(request);
+	_dns_reply(request, 0);
 
 	return 0;
 errout:
@@ -2424,7 +2491,7 @@ static int _dns_server_process_cache_addr(struct dns_request *request, struct dn
 	_dns_result_callback(request);
 
 	if (request->prefetch == 0) {
-		_dns_reply(request);
+		_dns_reply(request, 1);
 	}
 
 	return 0;
@@ -2467,7 +2534,15 @@ static int _dns_server_process_cache_packet(struct dns_request *request, struct
 	}
 
 	/* When passthrough, modify the id to be the id of the client request. */
-	dns_server_update_reply_packet_id(request, cache_packet->data, cache_packet->head.size);
+	struct dns_update_param param;
+	param.id = request->id;
+	param.cname_ttl = _dns_server_get_expired_ttl_reply(dns_cache);
+	param.ip_ttl = _dns_server_get_expired_ttl_reply(dns_cache);
+	if (dns_packet_update(cache_packet->data, cache_packet->head.size, &param) != 0) {
+		tlog(TLOG_ERROR, "update cache info failed.");
+		goto errout;
+	}
+
 	return _dns_reply_inpacket(request, cache_packet->data, cache_packet->head.size);
 errout:
 	return -1;