Browse Source

dns-cache: support cache when speed check is disabled

Nick Peng 5 years ago
parent
commit
aee19be262
5 changed files with 442 additions and 182 deletions
  1. 100 53
      src/dns_cache.c
  2. 54 13
      src/dns_cache.h
  3. 2 2
      src/dns_client.c
  4. 284 113
      src/dns_server.c
  5. 2 1
      src/dns_server.h

+ 100 - 53
src/dns_cache.c

@@ -82,6 +82,7 @@ static void _dns_cache_delete(struct dns_cache *dns_cache)
 	hash_del(&dns_cache->node);
 	list_del_init(&dns_cache->list);
 	atomic_dec(&dns_cache_head.num);
+	dns_cache_data_free(dns_cache->cache_data);
 	free(dns_cache);
 }
 
@@ -118,10 +119,89 @@ static void _dns_cache_move_inactive(struct dns_cache *dns_cache)
 	list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list);
 }
 
-int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
-					  int addr_len, int speed)
+enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data)
+{
+	return cache_data->head.cache_type;
+}
+
+uint32_t dns_cache_get_cache_flag(struct dns_cache_data *cache_data)
+{
+	return cache_data->head.cache_flag;
+}
+
+void dns_cache_data_free(struct dns_cache_data *data)
+{
+	if (data == NULL) {
+		return;
+	}
+
+	free(data);
+}
+
+struct dns_cache_data *dns_cache_new_data_addr(uint32_t cache_flag, char *cname, int cname_ttl, unsigned char *addr,
+											   int addr_len)
+{
+	struct dns_cache_addr *cache_addr = malloc(sizeof(struct dns_cache_addr));
+	memset(cache_addr, 0, sizeof(struct dns_cache_addr));
+	if (cache_addr == NULL) {
+		return NULL;
+	}
+
+	if (addr_len == DNS_RR_A_LEN) {
+		memcpy(cache_addr->addr_data.addr, addr, DNS_RR_A_LEN);
+	} else if (addr_len != DNS_RR_AAAA_LEN) {
+		memcpy(cache_addr->addr_data.addr, addr, DNS_RR_AAAA_LEN);
+	} else {
+		goto errout;
+	}
+
+	if (cname) {
+		safe_strncpy(cache_addr->addr_data.cname, cname, DNS_MAX_CNAME_LEN);
+		cache_addr->addr_data.cname_ttl = cname_ttl;
+	}
+
+	cache_addr->head.cache_flag = cache_flag;
+	cache_addr->head.cache_type = CACHE_TYPE_ADDR;
+	cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_head);
+
+	return (struct dns_cache_data *)cache_addr;
+
+errout:
+	if (cache_addr) {
+		free(cache_addr);
+		cache_addr = NULL;
+	}
+
+	return NULL;
+}
+
+struct dns_cache_data *dns_cache_new_data_packet(uint32_t cache_flag, void *packet, size_t packet_len)
+{
+	struct dns_cache_packet *cache_packet = NULL;
+	size_t data_size = 0;
+	if (packet == NULL || packet_len <= 0) {
+		return NULL;
+	}
+
+	data_size = sizeof(*cache_packet) + packet_len;
+	cache_packet = malloc(data_size);
+	if (cache_packet == NULL) {
+		return NULL;
+	}
+
+	memcpy(cache_packet->data, packet, packet_len);
+
+	cache_packet->head.cache_flag = cache_flag;
+	cache_packet->head.cache_type = CACHE_TYPE_PACKET;
+	cache_packet->head.size = packet_len;
+
+	return (struct dns_cache_data *)cache_packet;
+}
+
+int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data)
 {
 	struct dns_cache *dns_cache = NULL;
+	struct dns_cache_data *old_cache_data = NULL;
 
 	if (dns_cache_head.size <= 0) {
 		return 0;
@@ -130,7 +210,7 @@ int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_typ
 	/* lookup existing cache */
 	dns_cache = dns_cache_lookup(domain, qtype);
 	if (dns_cache == NULL) {
-		return dns_cache_insert(domain, cname, cname_ttl, ttl, qtype, addr, addr_len, speed);
+		return dns_cache_insert(domain, ttl, qtype, speed, cache_data);
 	}
 
 	if (ttl < DNS_CACHE_TTL_MIN) {
@@ -145,46 +225,26 @@ int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_typ
 	dns_cache->del_pending = 0;
 	dns_cache->speed = speed;
 	time(&dns_cache->insert_time);
-	if (qtype == DNS_T_A) {
-		if (addr_len != DNS_RR_A_LEN) {
-			goto errout_unlock;
-		}
-		memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
-	} else if (qtype == DNS_T_AAAA) {
-		if (addr_len != DNS_RR_AAAA_LEN) {
-			goto errout_unlock;
-		}
-		memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
-	} else {
-		goto errout_unlock;
-	}
-
-	if (cname) {
-		safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
-		dns_cache->cname_ttl = cname_ttl;
-	}
-
+	old_cache_data = dns_cache->cache_data;
+	dns_cache->cache_data = cache_data;
 	list_del_init(&dns_cache->list);
 	list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
 	pthread_mutex_unlock(&dns_cache_head.lock);
 
+	dns_cache_data_free(old_cache_data);
 	dns_cache_release(dns_cache);
 	return 0;
-errout_unlock:
-	pthread_mutex_unlock(&dns_cache_head.lock);
-	// errout:
-	if (dns_cache) {
-		dns_cache_release(dns_cache);
-	}
-	return -1;
 }
 
-int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
-					 int addr_len, int speed)
+int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data)
 {
 	uint32_t key = 0;
 	struct dns_cache *dns_cache = NULL;
 
+	if (cache_data == NULL || domain == NULL) {
+		return -1;
+	}
+
 	if (dns_cache_head.size <= 0) {
 		return 0;
 	}
@@ -206,37 +266,19 @@ int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type
 		ttl = DNS_CACHE_TTL_MIN;
 	}
 
+	memset(dns_cache, 0, sizeof(*dns_cache));
 	key = hash_string(domain);
 	key = jhash(&qtype, sizeof(qtype), key);
 	safe_strncpy(dns_cache->domain, domain, DNS_MAX_CNAME_LEN);
-	dns_cache->cname[0] = 0;
+	atomic_set(&dns_cache->hitnum, 3);
+	atomic_set(&dns_cache->ref, 1);
 	dns_cache->qtype = qtype;
 	dns_cache->ttl = ttl;
-	atomic_set(&dns_cache->hitnum, 3);
 	dns_cache->hitnum_update_add = DNS_CACHE_HITNUM_STEP;
 	dns_cache->del_pending = 0;
 	dns_cache->speed = speed;
-	atomic_set(&dns_cache->ref, 1);
+	dns_cache->cache_data = cache_data;
 	time(&dns_cache->insert_time);
-	if (qtype == DNS_T_A) {
-		if (addr_len != DNS_RR_A_LEN) {
-			goto errout;
-		}
-		memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
-	} else if (qtype == DNS_T_AAAA) {
-		if (addr_len != DNS_RR_AAAA_LEN) {
-			goto errout;
-		}
-		memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
-	} else {
-		goto errout;
-	}
-
-	if (cname) {
-		safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
-		dns_cache->cname_ttl = cname_ttl;
-	}
-
 	pthread_mutex_lock(&dns_cache_head.lock);
 	hash_add(dns_cache_head.cache_hash, &dns_cache->node, key);
 	list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
@@ -321,6 +363,11 @@ int dns_cache_get_ttl(struct dns_cache *dns_cache)
 	return ttl;
 }
 
+struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache)
+{
+	return dns_cache->cache_data;
+}
+
 void dns_cache_delete(struct dns_cache *dns_cache)
 {
 	pthread_mutex_lock(&dns_cache_head.lock);

+ 54 - 13
src/dns_cache.h

@@ -33,35 +33,74 @@ extern "C" {
 
 #define DNS_CACHE_TTL_MIN 30
 
+enum CACHE_TYPE {
+	CACHE_TYPE_NONE,
+	CACHE_TYPE_ADDR,
+	CACHE_TYPE_PACKET,
+};
+struct dns_cache_data_head {
+	uint32_t cache_flag;
+	enum CACHE_TYPE cache_type;
+	size_t size;
+};
+
+struct dns_cache_data {
+	struct dns_cache_data_head head;
+	unsigned char data[0];
+};
+
+struct dns_cache_addr {
+	struct dns_cache_data_head head;
+	struct dns_cache_addr_data {
+		unsigned int cname_ttl;
+		char cname[DNS_MAX_CNAME_LEN];
+		union {
+			unsigned char ipv4_addr[DNS_RR_A_LEN];
+			unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
+			unsigned char addr[0];
+		};
+	} addr_data;
+};
+
+struct dns_cache_packet {
+	struct dns_cache_data_head head;
+	unsigned char data[0];
+};
+
 struct dns_cache {
 	struct hlist_node node;
 	struct list_head list;
 	struct list_head check_list;
+	
 	atomic_t ref;
+	atomic_t hitnum;
+
 	char domain[DNS_MAX_CNAME_LEN];
-	char cname[DNS_MAX_CNAME_LEN];
-	unsigned int cname_ttl;
-	unsigned int ttl;
+	int ttl;
 	int speed;
-	atomic_t hitnum;
 	int hitnum_update_add;
 	int del_pending;
 	time_t insert_time;
+	
 	dns_type_t qtype;
-	union {
-		unsigned char ipv4_addr[DNS_RR_A_LEN];
-		unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
-		unsigned char addr[0];
-	};
+	struct dns_cache_data *cache_data;
 };
 
+enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data);
+
+uint32_t dns_cache_get_cache_flag(struct dns_cache_data *cache_data);
+
+void dns_cache_data_free(struct dns_cache_data *data);
+
+struct dns_cache_data *dns_cache_new_data_addr(uint32_t cache_flag, char *cname, int cname_ttl, unsigned char *addr, int addr_len);
+
+struct dns_cache_data *dns_cache_new_data_packet(uint32_t cache_flag, void *packet, size_t packet_len);
+
 int dns_cache_init(int size, int enable_inactive, int inactive_list_expired);
 
-int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
-					  int addr_len, int speed);
+int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data);
 
-int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr,
-					 int addr_len, int speed);
+int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data);
 
 struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype);
 
@@ -81,6 +120,8 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre);
 
 int dns_cache_get_ttl(struct dns_cache *dns_cache);
 
+struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache);
+
 void dns_cache_destroy(void);
 
 #ifdef __cpluscplus

+ 2 - 2
src/dns_client.c

@@ -2917,7 +2917,7 @@ static void _dns_client_add_pending_servers(void)
 		if (pending->query_v4 == 0) {
 			pending->query_v4 = 1;
 			_dns_client_server_pending_get(pending);
-			if (dns_server_query(pending->host, DNS_T_A, _dns_client_pending_server_resolve, pending) != 0) {
+			if (dns_server_query(pending->host, DNS_T_A, 0, _dns_client_pending_server_resolve, pending) != 0) {
 				_dns_client_server_pending_release_lck(pending);
 			}
 		}
@@ -2925,7 +2925,7 @@ static void _dns_client_add_pending_servers(void)
 		if (pending->query_v6 == 0) {
 			pending->query_v6 = 1;
 			_dns_client_server_pending_get(pending);
-			if (dns_server_query(pending->host, DNS_T_AAAA, _dns_client_pending_server_resolve, pending) != 0) {
+			if (dns_server_query(pending->host, DNS_T_AAAA, 0, _dns_client_pending_server_resolve, pending) != 0) {
 				_dns_client_server_pending_release_lck(pending);
 			}
 		}

+ 284 - 113
src/dns_server.c

@@ -137,6 +137,8 @@ struct dns_request {
 	atomic_t refcnt;
 
 	struct dns_server_conn_head *conn;
+	uint32_t server_flags;
+
 	/* dns request list */
 	struct list_head list;
 
@@ -206,7 +208,7 @@ static struct dns_server server;
 
 static tlog_log *dns_audit;
 
-static int _dns_server_prefetch_request(char *domain, dns_type_t qtype);
+static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags);
 
 static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len)
 {
@@ -216,17 +218,27 @@ static int _dns_server_forward_request(unsigned char *inpacket, int inpacket_len
 
 static int _dns_server_has_bind_flag(struct dns_request *request, uint32_t flag)
 {
-	if (request->conn == NULL) {
-		return -1;
-	}
-
-	if (request->conn->server_flags & flag) {
+	if (request->server_flags & flag) {
 		return 0;
 	}
 
 	return -1;
 }
 
+static int _dns_server_get_conf_ttl(int ttl)
+{
+	if (dns_conf_rr_ttl > 0) {
+		return dns_conf_rr_ttl;
+	}
+
+	if (dns_conf_rr_ttl_max > 0 && ttl > dns_conf_rr_ttl_max) {
+		ttl = dns_conf_rr_ttl_max;
+	} else if (dns_conf_rr_ttl_min > 0 && ttl < dns_conf_rr_ttl_min) {
+		ttl = dns_conf_rr_ttl_min;
+	}
+	return ttl;
+}
+
 static int _dns_server_epoll_ctl(struct dns_server_conn_head *head, int op, uint32_t events)
 {
 	struct epoll_event event;
@@ -692,10 +704,47 @@ 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 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)
 {
 	char *cname = NULL;
 	int cname_ttl = 0;
+	struct dns_cache_data *cache_data = NULL;
 
 	if (request->has_cname) {
 		cname = request->cname;
@@ -718,23 +767,32 @@ static int _dns_server_request_complete_A(struct dns_request *request)
 		return 0;
 	}
 
-	/* if doing prefetch, update cache only */
-	if (request->prefetch) {
-		dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN,
-						  request->ping_ttl_v4);
-	} else {
-		/* insert result to cache */
-		dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN,
-						 request->ping_ttl_v4);
+	cache_data = dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv4_addr, DNS_RR_A_LEN);
+	if (cache_data == NULL) {
+		goto errout;
+	}
+
+	if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
+		goto errout;
 	}
 
 	return 0;
+
+errout:
+	if (cache_data) {
+		dns_cache_data_free(cache_data);
+		cache_data = NULL;
+	}
+
+	return -1;
 }
 
 static int _dns_server_request_complete_AAAA(struct dns_request *request)
 {
+	int ret = -1;
 	char *cname = NULL;
 	int cname_ttl = 0;
+	struct dns_cache_data *cache_data = NULL;
 
 	if (request->has_cname) {
 		cname = request->cname;
@@ -756,13 +814,14 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
 
 		/* if doing prefetch, update cache only */
 		if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
-			if (request->prefetch) {
-				dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr,
-								  DNS_RR_AAAA_LEN, request->ping_ttl_v6);
-			} else {
-				/* insert result to cache */
-				dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr,
-								 DNS_RR_AAAA_LEN, request->ping_ttl_v6);
+			cache_data =
+				dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv6_addr, DNS_T_AAAA);
+			if (cache_data == NULL) {
+				goto errout;
+			}
+
+			if (_dns_server_request_update_cache(request, DNS_T_AAAA, cache_data) != 0) {
+				goto errout;
 			}
 		}
 
@@ -778,21 +837,25 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
 			request->ping_ttl_v6 < 0) {
 			tlog(TLOG_DEBUG, "Force IPV4 perfered.");
 			if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_CACHE) != 0) {
-				if (request->prefetch) {
-					dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr,
-									  DNS_RR_A_LEN, request->ping_ttl_v4);
-				} else {
-					dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr,
-									 DNS_RR_A_LEN, request->ping_ttl_v4);
+				cache_data =
+					dns_cache_new_data_addr(request->server_flags, cname, cname_ttl, request->ipv4_addr, DNS_T_A);
+				if (cache_data == NULL) {
+					goto errout;
+				}
+
+				if (_dns_server_request_update_cache(request, DNS_T_A, cache_data) != 0) {
+					goto errout;
 				}
 			}
 
 			if (request->dualstack_selection) {
 				if (_dns_server_reply_SOA(DNS_RC_NOERROR, request) != 0) {
-					return -1;
+					ret = -1;
+					goto errout;
 				}
 
-				return 1;
+				ret = 1;
+				goto errout;
 			}
 		}
 	}
@@ -800,6 +863,14 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request)
 	request->has_ipv4 = 0;
 
 	return 0;
+
+errout:
+	if (cache_data == NULL) {
+		dns_cache_data_free(cache_data);
+		cache_data = NULL;
+	}
+
+	return ret;
 }
 
 static int _dns_server_request_complete(struct dns_request *request)
@@ -1196,20 +1267,6 @@ static int _dns_ip_address_check_add(struct dns_request *request, unsigned char
 	return 0;
 }
 
-static int _dns_server_get_conf_ttl(int ttl)
-{
-	if (dns_conf_rr_ttl > 0) {
-		return dns_conf_rr_ttl;
-	}
-
-	if (dns_conf_rr_ttl_max > 0 && ttl > dns_conf_rr_ttl_max) {
-		ttl = dns_conf_rr_ttl_max;
-	} else if (dns_conf_rr_ttl_min > 0 && ttl < dns_conf_rr_ttl_min) {
-		ttl = dns_conf_rr_ttl_min;
-	}
-	return ttl;
-}
-
 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)
 {
@@ -1709,8 +1766,7 @@ static int _dns_server_get_answer(struct dns_request *request, struct dns_packet
 	return 0;
 }
 
-static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns_packet *packet,
-										 unsigned char *inpacket, int inpacket_len)
+static int _dns_server_setup_ipset_packet(struct dns_request *request, struct dns_packet *packet)
 {
 	int ttl;
 	char name[DNS_MAX_CNAME_LEN] = {0};
@@ -1720,44 +1776,21 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
 	struct dns_rrs *rrs = NULL;
 	struct dns_ipset_rule *ipset_rule = NULL;
 	struct dns_rule_flags *rule_flags = NULL;
-	int ret = 0;
-
-	if (atomic_inc_return(&request->notified) != 1) {
-		return 0;
-	}
-
-	if (request->result_callback) {
-		_dns_server_get_answer(request, packet);
-		_dns_result_callback(request);
-	}
-
-	if (request->conn == NULL) {
-		return 0;
-	}
-
-	/* When passthrough, modify the id to be the id of the client request. */
-	dns_server_update_reply_packet_id(request, inpacket, inpacket_len);
-	ret = _dns_reply_inpacket(request, inpacket, inpacket_len);
-
-	if (packet->head.rcode != DNS_RC_NOERROR && packet->head.rcode != DNS_RC_NXDOMAIN) {
-		return ret;
-	}
 
 	if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) {
-		return ret;
+		return -1;
 	}
-
 	/* check ipset rule */
 	rule_flags = request->domain_rule.rules[DOMAIN_RULE_FLAGS];
 	if (rule_flags) {
 		if (rule_flags->flags & DOMAIN_FLAG_IPSET_IGNORE) {
-			return ret;
+			return -1;
 		}
 	}
 
 	ipset_rule = request->domain_rule.rules[DOMAIN_RULE_IPSET];
 	if (ipset_rule == NULL) {
-		return ret;
+		return -1;
 	}
 
 	for (j = 1; j < DNS_RRS_END; j++) {
@@ -1778,8 +1811,8 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
 				/* add IPV4 to ipset */
 				ipset_add(ipset_rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2);
 
-				tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d",
-					request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
+				tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain,
+					 ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
 			} break;
 			case DNS_T_AAAA: {
 				unsigned char addr[16];
@@ -1797,9 +1830,11 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
 					ipset_add(ipset_rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ttl_v6 * 2);
 				}
 
-				tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
-					request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8],
-					addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
+				tlog(TLOG_DEBUG,
+					 "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: "
+					 "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
+					 request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
+					 addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
 			} break;
 			default:
 				break;
@@ -1807,7 +1842,48 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns
 		}
 	}
 
-	return ret;
+	return 0;
+}
+
+static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns_packet *packet,
+										 unsigned char *inpacket, int inpacket_len)
+{
+	int ret = 0;
+
+	if (atomic_inc_return(&request->notified) != 1) {
+		return 0;
+	}
+
+	_dns_server_get_answer(request, packet);
+	if (request->result_callback) {
+		_dns_result_callback(request);
+	}
+
+	if (request->conn == NULL) {
+		return 0;
+	}
+
+	/* When passthrough, modify the id to be the id of the client request. */
+	dns_server_update_reply_packet_id(request, inpacket, inpacket_len);
+	ret = _dns_reply_inpacket(request, inpacket, inpacket_len);
+
+	if (packet->head.rcode != DNS_RC_NOERROR && packet->head.rcode != DNS_RC_NXDOMAIN) {
+		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.");
+		}
+	}
+
+	return _dns_server_setup_ipset_packet(request, packet);
 }
 
 static int dns_server_resolve_callback(char *domain, dns_result_type rtype, unsigned int result_flag,
@@ -1949,7 +2025,6 @@ static void _dns_server_log_rule(const char *domain, enum domain_rule rule_type,
 								 int rule_key_len)
 {
 	char rule_name[DNS_MAX_CNAME_LEN];
-
 	if (rule_key_len <= 0) {
 		return;
 	}
@@ -2184,6 +2259,118 @@ static void _dns_server_process_speed_check_rule(struct dns_request *request)
 	request->check_order_list = check_order;
 }
 
+static int _dns_server_process_cache_addr(struct dns_request *request, struct dns_cache *dns_cache)
+{
+	struct dns_cache_addr *cache_addr = (struct dns_cache_addr *)dns_cache_get_data(dns_cache);
+
+	if (cache_addr->head.cache_type != CACHE_TYPE_ADDR) {
+		goto errout;
+	}
+	/* Cache hits, returning results in the cache */
+	switch (request->qtype) {
+	case DNS_T_A:
+		memcpy(request->ipv4_addr, cache_addr->addr_data.ipv4_addr, DNS_RR_A_LEN);
+		request->ttl_v4 = dns_cache_get_ttl(dns_cache);
+		request->has_ipv4 = 1;
+		break;
+	case DNS_T_AAAA:
+		memcpy(request->ipv6_addr, cache_addr->addr_data.ipv6_addr, DNS_RR_AAAA_LEN);
+		request->ttl_v6 = dns_cache_get_ttl(dns_cache);
+		request->has_ipv6 = 1;
+		break;
+	default:
+		goto errout;
+		break;
+	}
+
+	if (cache_addr->addr_data.cname[0] != 0) {
+		safe_strncpy(request->cname, cache_addr->addr_data.cname, DNS_MAX_CNAME_LEN);
+		request->has_cname = 1;
+		request->ttl_cname = cache_addr->addr_data.cname_ttl;
+	}
+
+	request->rcode = DNS_RC_NOERROR;
+
+	_dns_result_callback(request);
+
+	if (request->prefetch == 0) {
+		_dns_reply(request);
+	}
+
+	return 0;
+errout:
+	return -1;
+}
+
+static int _dns_server_process_cache_packet(struct dns_request *request, struct dns_cache *dns_cache)
+{
+	struct dns_cache_packet *cache_packet = (struct dns_cache_packet *)dns_cache_get_data(dns_cache);
+
+	if (cache_packet->head.cache_type != CACHE_TYPE_PACKET) {
+		goto errout;
+	}
+
+	if (dns_cache->qtype != request->qtype) {
+		goto errout;
+	}
+
+	if (atomic_inc_return(&request->notified) != 1) {
+		return 0;
+	}
+
+	if (request->result_callback) {
+		unsigned char packet_buff[DNS_PACKSIZE];
+		struct dns_packet *packet = (struct dns_packet *)packet_buff;
+
+		if (dns_decode(packet, DNS_PACKSIZE, cache_packet->data, cache_packet->head.size) != 0) {
+			goto errout;
+		}
+
+		_dns_server_get_answer(request, packet);
+		_dns_result_callback(request);
+	}
+
+	if (request->conn == NULL) {
+		return 0;
+	}
+
+	/* 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);
+	return _dns_reply_inpacket(request, cache_packet->data, cache_packet->head.size);
+errout:
+	return -1;
+}
+
+static int _dns_server_process_cache_data(struct dns_request *request, struct dns_cache *dns_cache)
+{
+	enum CACHE_TYPE cache_type = CACHE_TYPE_NONE;
+	int ret = -1;
+
+	cache_type = dns_cache_data_type(dns_cache->cache_data);
+	switch (cache_type) {
+	case CACHE_TYPE_ADDR:
+		ret = _dns_server_process_cache_addr(request, dns_cache);
+		if (ret != 0) {
+			goto out;
+		}
+		break;
+	case CACHE_TYPE_PACKET:
+		ret = _dns_server_process_cache_packet(request, dns_cache);
+		if (ret != 0) {
+			goto out;
+		}
+
+		break;
+	default:
+		goto out;
+		break;
+	}
+
+	return 0;
+out:
+	return -1;
+}
+
 static int _dns_server_process_cache(struct dns_request *request)
 {
 	struct dns_cache *dns_cache = NULL;
@@ -2201,7 +2388,11 @@ static int _dns_server_process_cache(struct dns_request *request)
 			if (dns_cache_A) {
 				tlog(TLOG_DEBUG, "No IPV6 Found, Force IPV4 perfered.");
 				if (dns_cache_get_ttl(dns_cache_A) == 0) {
-					_dns_server_prefetch_request(request->domain, request->qtype);
+					uint32_t server_flags = request->server_flags;
+					if (request->conn == NULL) {
+						server_flags = dns_cache_get_cache_flag(dns_cache_A->cache_data);
+					}
+					_dns_server_prefetch_request(request->domain, request->qtype, server_flags);
 				}
 				ret = _dns_server_reply_SOA(DNS_RC_NOERROR, request);
 				goto out;
@@ -2226,42 +2417,18 @@ static int _dns_server_process_cache(struct dns_request *request)
 		}
 	}
 
-	/* Cache hits, returning results in the cache */
-	switch (request->qtype) {
-	case DNS_T_A:
-		memcpy(request->ipv4_addr, dns_cache->ipv4_addr, DNS_RR_A_LEN);
-		request->ttl_v4 = dns_cache_get_ttl(dns_cache);
-		request->has_ipv4 = 1;
-		break;
-	case DNS_T_AAAA:
-		memcpy(request->ipv6_addr, dns_cache->ipv6_addr, DNS_RR_AAAA_LEN);
-		request->ttl_v6 = dns_cache_get_ttl(dns_cache);
-		request->has_ipv6 = 1;
-		break;
-	default:
+	ret = _dns_server_process_cache_data(request, dns_cache);
+	if (ret != 0) {
 		goto out;
-		break;
-	}
-
-	if (dns_cache->cname[0] != 0) {
-		safe_strncpy(request->cname, dns_cache->cname, DNS_MAX_CNAME_LEN);
-		request->has_cname = 1;
-		request->ttl_cname = dns_cache->cname_ttl;
-	}
-
-	request->rcode = DNS_RC_NOERROR;
-
-	_dns_result_callback(request);
-
-	if (request->prefetch == 0) {
-		_dns_reply(request);
 	}
 
-	ret = 0;
-
 out_update_cache:
 	if (dns_cache_get_ttl(dns_cache) == 0) {
-		_dns_server_prefetch_request(request->domain, request->qtype);
+		uint32_t server_flags = request->server_flags;
+		if (request->conn == NULL) {
+			server_flags = dns_cache_get_cache_flag(dns_cache_A->cache_data);
+		}
+		_dns_server_prefetch_request(request->domain, request->qtype, server_flags);
 	} else {
 		dns_cache_update(dns_cache);
 	}
@@ -2282,6 +2449,7 @@ out:
 static void _dns_server_request_set_client(struct dns_request *request, struct dns_server_conn_head *conn)
 {
 	request->conn = conn;
+	request->server_flags = conn->server_flags;
 	_dns_server_conn_get(conn);
 }
 
@@ -2551,7 +2719,7 @@ errout:
 	return ret;
 }
 
-static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
+static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags)
 {
 	int ret = -1;
 	struct dns_request *request = NULL;
@@ -2562,6 +2730,7 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
 		goto errout;
 	}
 
+	request->server_flags = server_flags;
 	_dns_server_request_set_enable_prefetch(request);
 	ret = _dns_server_do_query(request, domain, qtype);
 	if (ret != 0) {
@@ -2579,7 +2748,7 @@ errout:
 	return ret;
 }
 
-int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr)
+int dns_server_query(char *domain, int qtype, uint32_t server_flags, dns_result_callback callback, void *user_ptr)
 {
 	int ret = -1;
 	struct dns_request *request = NULL;
@@ -2590,6 +2759,7 @@ int dns_server_query(char *domain, int qtype, dns_result_callback callback, void
 		goto errout;
 	}
 
+	request->server_flags = server_flags;
 	_dns_server_request_set_callback(request, callback, user_ptr);
 	ret = _dns_server_do_query(request, domain, qtype);
 	if (ret != 0) {
@@ -2986,7 +3156,8 @@ static void _dns_server_prefetch_domain(struct dns_cache *dns_cache)
 	/* start prefetch domain */
 	tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->domain, dns_cache->qtype,
 		 dns_cache->ttl, hitnum);
-	if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype) != 0) {
+	if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype,
+									 dns_cache_get_cache_flag(dns_cache->cache_data)) != 0) {
 		tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->domain, dns_cache->qtype);
 	}
 }

+ 2 - 1
src/dns_server.h

@@ -20,6 +20,7 @@
 #define _SMART_DNS_SERVER_H
 
 #include "dns.h"
+#include <stdint.h>
 
 #ifdef __cpluscplus
 extern "C" {
@@ -40,7 +41,7 @@ typedef int (*dns_result_callback)(char *domain, dns_rtcode_t rtcode, dns_type_t
 								   unsigned int ping_time, void *user_ptr);
 
 /* query domain */
-int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr);
+int dns_server_query(char *domain, int qtype, uint32_t server_flags, dns_result_callback callback, void *user_ptr);
 
 #ifdef __cpluscplus
 }