From aee19be262796db5d5959a12a76090b41aa7ba4f Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Sun, 23 Aug 2020 20:12:50 +0800 Subject: [PATCH] dns-cache: support cache when speed check is disabled --- src/dns_cache.c | 153 +++++++++++------- src/dns_cache.h | 69 ++++++-- src/dns_client.c | 4 +- src/dns_server.c | 397 +++++++++++++++++++++++++++++++++-------------- src/dns_server.h | 3 +- 5 files changed, 443 insertions(+), 183 deletions(-) diff --git a/src/dns_cache.c b/src/dns_cache.c index fc12ea0..9315ada 100644 --- a/src/dns_cache.c +++ b/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); diff --git a/src/dns_cache.h b/src/dns_cache.h index 691b8bf..efcd0d4 100644 --- a/src/dns_cache.h +++ b/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; - char domain[DNS_MAX_CNAME_LEN]; - char cname[DNS_MAX_CNAME_LEN]; - unsigned int cname_ttl; - unsigned int ttl; - int speed; atomic_t hitnum; + + char domain[DNS_MAX_CNAME_LEN]; + int ttl; + int speed; 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 diff --git a/src/dns_client.c b/src/dns_client.c index 5c76873..abe9dd6 100644 --- a/src/dns_client.c +++ b/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); } } diff --git a/src/dns_server.c b/src/dns_server.c index 05ec6ca..3452c1f 100644 --- a/src/dns_server.c +++ b/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); } } diff --git a/src/dns_server.h b/src/dns_server.h index 358bd60..4b05eaf 100644 --- a/src/dns_server.h +++ b/src/dns_server.h @@ -20,6 +20,7 @@ #define _SMART_DNS_SERVER_H #include "dns.h" +#include #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 }