diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 33e3262..39e70bd 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -17,6 +17,10 @@ bind [::]:53 # 0: for no cache cache-size 512 +# prefetch domain +# prefetch-domain [true|false] +# prefetch-domain true + # ttl for all resource record # rr-ttl: ttl for all record # rr-ttl-min: minimum ttl for resource record diff --git a/src/conf.c b/src/conf.c index 13ae4a4..ffbbaf5 100644 --- a/src/conf.c +++ b/src/conf.c @@ -15,6 +15,7 @@ char dns_conf_server_ip[DNS_MAX_IPLEN]; int dns_conf_cachesize = DEFAULT_DNS_CACHE_SIZE; +int dns_conf_prefetch = 0; struct dns_servers dns_conf_servers[DNS_MAX_SERVERS]; struct dns_bogus_nxdomain dns_conf_bogus_nxdomain; char dns_conf_server_name[DNS_MAX_CONF_CNAME_LEN]; @@ -211,6 +212,18 @@ int config_cache_size(char *value) return 0; } +int config_cache_prefetch_domain(char *value) +{ + /* read dns cache size */ + if (strncmp("yes", value, sizeof("yes")) == 0 || strncmp("YES", value, sizeof("YES")) == 0) { + dns_conf_prefetch = 1; + } else if (strncmp("no", value, sizeof("no")) == 0 || strncmp("NO", value, sizeof("NO")) == 0) { + dns_conf_prefetch = 0; + } + + return 0; +} + int config_log_level(char *value) { /* read log level and set */ @@ -446,6 +459,7 @@ struct config_item config_item[] = { {"server-tcp", config_server_tcp}, {"server-http", config_server_http}, {"cache-size", config_cache_size}, + {"prefetch-domain", config_cache_prefetch_domain}, {"log-level", config_log_level}, {"log-file", config_log_file}, {"log-size", config_log_size}, diff --git a/src/conf.h b/src/conf.h index 615bdfa..0f1afdf 100644 --- a/src/conf.h +++ b/src/conf.h @@ -46,6 +46,7 @@ struct dns_bogus_nxdomain { extern char dns_conf_server_ip[DNS_MAX_IPLEN]; extern int dns_conf_cachesize; +extern int dns_conf_prefetch; extern struct dns_servers dns_conf_servers[DNS_MAX_SERVERS]; extern int dns_conf_server_num; diff --git a/src/dns_cache.c b/src/dns_cache.c index b09dcc2..6434548 100644 --- a/src/dns_cache.c +++ b/src/dns_cache.c @@ -1,4 +1,5 @@ #include "dns_cache.h" +#include "tlog.h" #include struct dns_cache_head { @@ -41,6 +42,14 @@ void _dns_cache_delete(struct dns_cache *dns_cache) free(dns_cache); } +void dns_cache_get(struct dns_cache *dns_cache) +{ + if (atomic_inc_return(&dns_cache->ref) == 1) { + tlog(TLOG_ERROR, "BUG: dns_cache is invalid."); + return; + } +} + void dns_cache_release(struct dns_cache *dns_cache) { if (!atomic_dec_and_test(&dns_cache->ref)) { @@ -50,6 +59,52 @@ void dns_cache_release(struct dns_cache *dns_cache) _dns_cache_delete(dns_cache); } +int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len) +{ + struct dns_cache *dns_cache = NULL; + + if (dns_cache_head.size <= 0) { + return 0; + } + + dns_cache = dns_cache_lookup(domain, qtype); + if (dns_cache == NULL) { + return 0; + } + + dns_cache->ttl = ttl; + dns_cache->qtype = qtype; + dns_cache->ttl = ttl; + 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) { + strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN); + dns_cache->cname_ttl = cname_ttl; + } + + dns_cache_release(dns_cache); + return 0; + +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) { uint32_t key = 0; @@ -59,10 +114,10 @@ int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type return 0; } - dns_cache = dns_cache_get(domain, qtype); + dns_cache = dns_cache_lookup(domain, qtype); if (dns_cache) { dns_cache_release(dns_cache); - return 0; + dns_cache = NULL; } dns_cache = malloc(sizeof(*dns_cache)); @@ -100,6 +155,7 @@ int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type 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); + INIT_LIST_HEAD(&dns_cache->check_list); dns_cache_head.num++; if (dns_cache_head.num > dns_cache_head.size) { @@ -118,7 +174,7 @@ errout: return -1; } -struct dns_cache *dns_cache_get(char *domain, dns_type_t qtype) +struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype) { uint32_t key = 0; struct dns_cache *dns_cache = NULL; @@ -195,12 +251,13 @@ void dns_cache_update(struct dns_cache *dns_cache) pthread_mutex_unlock(&dns_cache_head.lock); } -void dns_cache_invalidate(void) +void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre) { struct dns_cache *dns_cache = NULL; struct dns_cache *tmp; time_t now; int ttl = 0; + LIST_HEAD(checklist); if (dns_cache_head.size <= 0) { return; @@ -212,6 +269,17 @@ void dns_cache_invalidate(void) { ttl = dns_cache->insert_time + dns_cache->ttl - now; if (ttl > 0) { + if (ttl < ttl_pre) { + if (callback) { + list_add_tail(&dns_cache->check_list, &checklist); + dns_cache_get(dns_cache); + continue; + } + } + + if (callback) { + continue; + } break; } @@ -220,6 +288,13 @@ void dns_cache_invalidate(void) dns_cache_release(dns_cache); } pthread_mutex_unlock(&dns_cache_head.lock); + + list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list) + { + callback(dns_cache); + list_del_init(&dns_cache->check_list); + dns_cache_release(dns_cache); + } } void dns_cache_destroy(void) diff --git a/src/dns_cache.h b/src/dns_cache.h index 6954d71..7725912 100644 --- a/src/dns_cache.h +++ b/src/dns_cache.h @@ -11,6 +11,7 @@ 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]; @@ -27,17 +28,23 @@ struct dns_cache { int dns_cache_init(int size); +int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len); + int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len); -struct dns_cache *dns_cache_get(char *domain, dns_type_t qtype); +struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype); void dns_cache_delete(struct dns_cache *dns_cache); +void dns_cache_get(struct dns_cache *dns_cache); + void dns_cache_release(struct dns_cache *dns_cache); void dns_cache_update(struct dns_cache *dns_cache); -void dns_cache_invalidate(void); +typedef void dns_cache_preinvalid_callback(struct dns_cache *dns_cache); + +void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre); int dns_cache_get_ttl(struct dns_cache *dns_cache); diff --git a/src/dns_client.c b/src/dns_client.c index db81b2b..c3cfcb3 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -1104,7 +1104,7 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void goto errout_del_list; } - tlog(TLOG_INFO, "send request %s, id %d\n", domain, query->sid); + tlog(TLOG_INFO, "send request %s, qtype %d, id %d\n", domain, qtype, query->sid); _dns_client_query_release(query); return 0; diff --git a/src/dns_server.c b/src/dns_server.c index f86de17..9ec9e2f 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -124,6 +124,8 @@ struct dns_request { /* send original raw packet to server/client like proxy */ int passthrough; + int prefetch; + pthread_mutex_t ip_map_lock; int ip_map_num; DECLARE_HASHTABLE(ip_map, 4); @@ -293,11 +295,16 @@ int _dns_server_request_complete(struct dns_request *request) if (request->has_ping_result == 0 && request->ttl_v4 > DNS_SERVER_TMOUT_TTL) { request->ttl_v4 = DNS_SERVER_TMOUT_TTL; } + + if (request->prefetch) { + dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN); + } else { + dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN); + } } - dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN); } else if (request->qtype == DNS_T_AAAA) { - 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, + 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]); @@ -306,10 +313,19 @@ int _dns_server_request_complete(struct dns_request *request) if (request->has_ping_result == 0 && request->ttl_v6 > DNS_SERVER_TMOUT_TTL) { request->ttl_v6 = DNS_SERVER_TMOUT_TTL; } - dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN); + + if (request->prefetch) { + dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN); + } else { + dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN); + } } } + if (request->prefetch) { + return 0; + } + _dns_reply(request); return ret; @@ -504,7 +520,7 @@ static int _dns_server_bogus_nxdomain_exists(struct dns_request *request, unsign int ret = 0; ret = dns_bogus_nxdomain_exists(ip, addr_type); - if (ret != 0 ) { + if (ret != 0) { return -1; } @@ -581,7 +597,7 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain, _dns_server_request_release(request); break; } - + request->rcode = packet->head.rcode; sprintf(ip, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); @@ -604,12 +620,12 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain, /* bogus ip address, skip */ if (_dns_server_bogus_nxdomain_exists(request, addr, DNS_T_AAAA) == 0) { _dns_server_request_release(request); - tlog(TLOG_DEBUG, "bogus-nxdomain: %s TTL: %d IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", name, ttl, 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, "bogus-nxdomain: %s TTL: %d IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", name, ttl, + 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; } - if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(request->cname, name, DNS_MAX_CNAME_LEN) != 0) { _dns_server_request_release(request); break; @@ -693,7 +709,7 @@ static int dns_server_resolve_callback(char *domain, dns_result_type rtype, stru pthread_mutex_lock(&request->ip_map_lock); ip_num = request->ip_map_num; pthread_mutex_unlock(&request->ip_map_lock); - + /* Not need to wait check result if only has one ip address */ if (ip_num == 1) { _dns_server_request_complete(request); @@ -815,8 +831,8 @@ static struct dns_address *_dns_server_get_address_by_domain(char *domain, int q if (likely(dns_conf_log_level > TLOG_INFO)) { return art_substring(&dns_conf_address, (unsigned char *)domain_key, domain_len, NULL, NULL); - } - + } + address = art_substring(&dns_conf_address, (unsigned char *)domain_key, domain_len, matched_key, &matched_key_len); if (address == NULL) { return NULL; @@ -868,7 +884,7 @@ static int _dns_server_process_cache(struct dns_request *request, struct dns_pac { struct dns_cache *dns_cache = NULL; - dns_cache = dns_cache_get(request->domain, request->qtype); + dns_cache = dns_cache_lookup(request->domain, request->qtype); if (dns_cache == NULL) { goto errout; } @@ -934,16 +950,17 @@ static int _dns_server_recv(unsigned char *inpacket, int inpacket_len, struct so } request = malloc(sizeof(*request)); + if (request == NULL) { + tlog(TLOG_ERROR, "malloc failed.\n"); + goto errout; + } memset(request, 0, sizeof(*request)); pthread_mutex_init(&request->ip_map_lock, 0); atomic_set(&request->adblock, 0); request->ping_ttl_v4 = -1; request->ping_ttl_v6 = -1; + request->prefetch = 0; request->rcode = DNS_RC_SERVFAIL; - if (request == NULL) { - tlog(TLOG_ERROR, "malloc failed.\n"); - goto errout; - } if (_dns_recv_addr(request, from, from_len) != 0) { goto errout; @@ -1017,6 +1034,45 @@ errout: return ret; } +static int _dns_server_prefetch_request(char *domain, dns_type_t qtype) +{ + int ret = -1; + struct dns_request *request = NULL; + + request = malloc(sizeof(*request)); + if (request == NULL) { + tlog(TLOG_ERROR, "malloc failed.\n"); + goto errout; + } + memset(request, 0, sizeof(*request)); + pthread_mutex_init(&request->ip_map_lock, 0); + atomic_set(&request->adblock, 0); + request->ping_ttl_v4 = -1; + request->ping_ttl_v6 = -1; + request->prefetch = 1; + request->qtype = qtype; + request->rcode = DNS_RC_SERVFAIL; + + request->id = 0; + hash_init(request->ip_map); + strncpy(request->domain, domain, DNS_MAX_CNAME_LEN); + + tlog(TLOG_INFO, "prefetch domain %s, qtype = %d\n", request->domain, qtype); + + _dns_server_request_get(request); + pthread_mutex_lock(&server.request_list_lock); + list_add_tail(&request->list, &server.request_list); + pthread_mutex_unlock(&server.request_list_lock); + + _dns_server_request_get(request); + request->send_tick = get_tick_count(); + dns_client_query(request->domain, qtype, dns_server_resolve_callback, request); + + return 0; +errout: + return ret; +} + static int _dns_server_process(unsigned long now) { int len; @@ -1079,13 +1135,25 @@ void _dns_server_tcp_ping_check(struct dns_request *request) request->has_ping_tcp = 1; } +void _dns_server_prefetch_domain(struct dns_cache *dns_cache) +{ + tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d", dns_cache->domain, dns_cache->qtype, dns_cache->ttl); + if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype) != 0) { + tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->domain, dns_cache->qtype); + } +} + void _dns_server_period_run_second(void) { static unsigned int sec = 0; sec++; if (sec % 2 == 0) { - dns_cache_invalidate(); + if (dns_conf_prefetch) { + dns_cache_invalidate(_dns_server_prefetch_domain, 3); + } else { + dns_cache_invalidate(NULL, 0); + } } } diff --git a/src/smartdns.c b/src/smartdns.c index 4f3ece5..6e7ba5b 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -198,8 +198,8 @@ int smartdns_init(void) goto errout; } - /* tlog_setlogscreen(1); */ - tlog_setlevel(dns_conf_log_level); + tlog_setlogscreen(1); + tlog_setlevel(TLOG_INFO); if (dns_conf_server_num <= 0) { if (smartdns_load_from_resolv() != 0) {