diff --git a/src/dns_cache.c b/src/dns_cache.c index 81c6e40..d090b1f 100644 --- a/src/dns_cache.c +++ b/src/dns_cache.c @@ -243,7 +243,7 @@ struct dns_cache_data *dns_cache_new_data_packet(uint32_t cache_flag, void *pack 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) +int _dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, int inactive, struct dns_cache_data *cache_data) { struct dns_cache *dns_cache = NULL; struct dns_cache_data *old_cache_data = NULL; @@ -269,11 +269,19 @@ int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache->info.qtype = qtype; dns_cache->info.ttl = ttl; dns_cache->info.speed = speed; - time(&dns_cache->info.insert_time); 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); + + if (inactive == 0) { + time(&dns_cache->info.insert_time); + time(&dns_cache->info.replace_time); + list_add_tail(&dns_cache->list, &dns_cache_head.cache_list); + } else { + time(&dns_cache->info.replace_time); + list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list); + } + pthread_mutex_unlock(&dns_cache_head.lock); dns_cache_data_free(old_cache_data); @@ -281,6 +289,16 @@ int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct return 0; } +int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data) +{ + return _dns_cache_replace(domain, ttl, qtype, speed, 0, cache_data); +} + +int dns_cache_replace_inactive(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data) +{ + return _dns_cache_replace(domain, ttl, qtype, speed, 1, cache_data); +} + int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data *cache_data, struct list_head *head) { uint32_t key = 0; @@ -354,6 +372,7 @@ int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct info.hitnum_update_add = DNS_CACHE_HITNUM_STEP; info.speed = speed; time(&info.insert_time); + time(&info.replace_time); return _dns_cache_insert(&info, cache_data, &dns_cache_head.cache_list); } @@ -418,7 +437,7 @@ int dns_cache_get_ttl(struct dns_cache *dns_cache) return ttl; } -int dns_cache_get_cname_ttl(struct dns_cache *dns_cache) +int dns_cache_get_cname_ttl(struct dns_cache *dns_cache) { time_t now; int ttl = 0; @@ -444,10 +463,10 @@ int dns_cache_get_cname_ttl(struct dns_cache *dns_cache) return 0; } - return ttl; + return ttl; } -int dns_cache_is_soa(struct dns_cache *dns_cache) +int dns_cache_is_soa(struct dns_cache *dns_cache) { if (dns_cache == NULL) { return 0; @@ -502,12 +521,14 @@ void dns_cache_update(struct dns_cache *dns_cache) pthread_mutex_unlock(&dns_cache_head.lock); } -void _dns_cache_remove_expired_ttl(time_t *now) +void _dns_cache_remove_expired_ttl(dns_cache_callback inactive_precallback, int ttl_inactive_pre, time_t *now) { struct dns_cache *dns_cache = NULL; struct dns_cache *tmp; int ttl = 0; + LIST_HEAD(checklist); + pthread_mutex_lock(&dns_cache_head.lock); list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list) { ttl = dns_cache->info.insert_time + dns_cache->info.ttl - *now; @@ -515,15 +536,42 @@ void _dns_cache_remove_expired_ttl(time_t *now) continue; } - if (dns_cache_head.inactive_list_expired + ttl > 0) { + if (dns_cache_head.inactive_list_expired + ttl < 0) { + _dns_cache_remove(dns_cache); continue; } - _dns_cache_remove(dns_cache); + ttl = dns_cache->info.replace_time + dns_cache->info.ttl - *now; + if (ttl > 0) { + continue; + } + + if (dns_cache->del_pending == 1) { + continue; + } + + /* If the TTL time is in the pre-timeout range, call callback function */ + if (inactive_precallback && ttl_inactive_pre < -ttl) { + list_add_tail(&dns_cache->check_list, &checklist); + dns_cache_get(dns_cache); + dns_cache->del_pending = 1; + continue; + } + } + pthread_mutex_unlock(&dns_cache_head.lock); + + list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list) + { + /* run inactive_precallback */ + if (inactive_precallback) { + inactive_precallback(dns_cache); + } + dns_cache_release(dns_cache); } } -void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre) +void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, dns_cache_callback inactive_precallback, + int ttl_inactive_pre) { struct dns_cache *dns_cache = NULL; struct dns_cache *tmp; @@ -542,7 +590,7 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre) ttl = dns_cache->info.insert_time + dns_cache->info.ttl - now; if (ttl > 0 && ttl < ttl_pre) { /* If the TTL time is in the pre-timeout range, call callback function */ - if (callback && dns_cache->del_pending == 0) { + if (precallback && dns_cache->del_pending == 0) { list_add_tail(&dns_cache->check_list, &checklist); dns_cache_get(dns_cache); dns_cache->del_pending = 1; @@ -558,18 +606,17 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre) } } } + pthread_mutex_unlock(&dns_cache_head.lock); if (dns_cache_head.enable_inactive && dns_cache_head.inactive_list_expired != 0) { - _dns_cache_remove_expired_ttl(&now); + _dns_cache_remove_expired_ttl(inactive_precallback, ttl_inactive_pre, &now); } - pthread_mutex_unlock(&dns_cache_head.lock); - list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list) { /* run callback */ - if (callback) { - callback(dns_cache); + if (precallback) { + precallback(dns_cache); } dns_cache_release(dns_cache); } diff --git a/src/dns_cache.h b/src/dns_cache.h index ff2812c..036f354 100644 --- a/src/dns_cache.h +++ b/src/dns_cache.h @@ -85,6 +85,7 @@ struct dns_cache_info { int speed; int hitnum_update_add; time_t insert_time; + time_t replace_time; dns_type_t qtype; }; @@ -124,6 +125,8 @@ int dns_cache_init(int size, int enable_inactive, int inactive_list_expired); int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data); +int dns_cache_replace_inactive(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data); + 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); @@ -138,9 +141,10 @@ int dns_cache_hitnum_dec_get(struct dns_cache *dns_cache); void dns_cache_update(struct dns_cache *dns_cache); -typedef void dns_cache_preinvalid_callback(struct dns_cache *dns_cache); +typedef void dns_cache_callback(struct dns_cache *dns_cache); -void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre); +void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, dns_cache_callback inactive_precallback, + int ttl_inactive_pre); int dns_cache_get_ttl(struct dns_cache *dns_cache); diff --git a/src/dns_conf.c b/src/dns_conf.c index f715da5..34765d5 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -63,7 +63,7 @@ int dns_conf_max_reply_ip_num = DNS_MAX_REPLY_IP_NUM; int dns_conf_cachesize = DEFAULT_DNS_CACHE_SIZE; int dns_conf_prefetch = 0; int dns_conf_serve_expired = 1; -int dns_conf_serve_expired_ttl = 0; +int dns_conf_serve_expired_ttl = 24 * 3600; /* 1 day */ int dns_conf_serve_expired_reply_ttl = 3; /* upstream servers */ diff --git a/src/dns_server.c b/src/dns_server.c index 08f4950..283c6ad 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -55,6 +55,7 @@ #define SOCKET_IP_TOS (IPTOS_LOWDELAY | IPTOS_RELIABILITY) #define SOCKET_PRIORITY (6) #define CACHE_AUTO_ENABLE_SIZE (1024 * 1024 * 128) +#define EXPIRED_DOMAIN_PREFTCH_TIME (3600 * 8) #define RECV_ERROR_AGAIN 1 #define RECV_ERROR_OK 0 @@ -216,6 +217,7 @@ struct dns_request { int passthrough; int request_wait; int prefetch; + int prefetch_expired_domain; int dualstack_selection; int dualstack_selection_force_soa; @@ -256,7 +258,7 @@ static tlog_log *dns_audit; static int is_ipv6_ready; -static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags, +static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, int expired_domain, uint32_t server_flags, struct dns_query_options *options); static int _dns_server_get_answer(struct dns_server_post_context *context); static void _dns_server_request_get(struct dns_request *request); @@ -314,7 +316,7 @@ static void _dns_server_set_dualstack_selection(struct dns_request *request) { struct dns_rule_flags *rule_flag = NULL; - if (request->dualstack_selection_query) { + if (request->dualstack_selection_query || request->prefetch_expired_domain == 1) { request->dualstack_selection = 0; return; } @@ -931,8 +933,14 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ /* if doing prefetch, update cache only */ if (request->prefetch) { - if (dns_cache_replace(request->domain, ttl, qtype, speed, cache_data) != 0) { - goto errout; + if (request->prefetch_expired_domain == 0) { + if (dns_cache_replace(request->domain, ttl, qtype, speed, cache_data) != 0) { + goto errout; + } + } else { + if (dns_cache_replace_inactive(request->domain, ttl, qtype, speed, cache_data) != 0) { + goto errout; + } } } else { /* insert result to cache */ @@ -1058,8 +1066,14 @@ int _dns_cache_cname_packet(struct dns_server_post_context *context) /* if doing prefetch, update cache only */ if (request->prefetch) { - if (dns_cache_replace(request->cname, ttl, context->qtype, speed, cache_packet) != 0) { - goto errout; + if (request->prefetch_expired_domain == 0) { + if (dns_cache_replace(request->cname, ttl, context->qtype, speed, cache_packet) != 0) { + goto errout; + } + } else { + if (dns_cache_replace_inactive(request->cname, ttl, context->qtype, speed, cache_packet) != 0) { + goto errout; + } } } else { /* insert result to cache */ @@ -3480,7 +3494,7 @@ out_update_cache: options.enable_flag |= DNS_QUEY_OPTION_ECS_DNS; memcpy(&options.ecs_dns, &request->ecs, sizeof(options.ecs_dns)); } - _dns_server_prefetch_request(request->domain, request->qtype, server_flags, &options); + _dns_server_prefetch_request(request->domain, request->qtype, 0, server_flags, &options); } else { dns_cache_update(dns_cache); } @@ -3564,9 +3578,10 @@ static void _dns_server_request_set_id(struct dns_request *request, unsigned sho request->id = id; } -static void _dns_server_request_set_enable_prefetch(struct dns_request *request) +static void _dns_server_request_set_enable_prefetch(struct dns_request *request, int expired_domain) { request->prefetch = 1; + request->prefetch_expired_domain = expired_domain; } static int _dns_server_request_set_client_addr(struct dns_request *request, struct sockaddr_storage *from, @@ -3794,6 +3809,7 @@ int _dns_server_query_dualstack(struct dns_request *request) request_dualstack->qtype = qtype; request_dualstack->dualstack_selection_query = 1; request_dualstack->prefetch = request->prefetch; + request_dualstack->prefetch_expired_domain = request->prefetch_expired_domain; _dns_server_request_get(request); request_dualstack->dualstack_request = request; _dns_server_request_set_callback(request_dualstack, dns_server_dualstack_callback, request); @@ -4034,7 +4050,7 @@ static int _dns_server_prefetch_setup_options(struct dns_request *request, struc return 0; } -static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t server_flags, +static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, int expired_domain, uint32_t server_flags, struct dns_query_options *options) { int ret = -1; @@ -4050,7 +4066,7 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype, uint32_t request->qtype = qtype; request->server_flags = server_flags; _dns_server_prefetch_setup_options(request, options); - _dns_server_request_set_enable_prefetch(request); + _dns_server_request_set_enable_prefetch(request, expired_domain); ret = _dns_server_do_query(request); if (ret != 0) { tlog(TLOG_ERROR, "do query %s failed.\n", request->domain); @@ -4480,7 +4496,19 @@ 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->info.domain, dns_cache->info.qtype, dns_cache->info.ttl, hitnum); - if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, + if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 0, + dns_cache_get_cache_flag(dns_cache->cache_data), NULL) != 0) { + tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); + } +} + +static void _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache) +{ + /* start prefetch domain */ + tlog(TLOG_DEBUG, "expired domain, prefetch by cache %s, qtype %d, ttl %d", dns_cache->info.domain, + dns_cache->info.qtype, dns_cache->info.ttl); + + if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 1, dns_cache_get_cache_flag(dns_cache->cache_data), NULL) != 0) { tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); } @@ -4523,7 +4551,7 @@ static void _dns_server_period_run_second(void) } if (now - 180 > last) { - dns_cache_invalidate(NULL, 0); + dns_cache_invalidate(NULL, 0, NULL, 0); tlog(TLOG_WARN, "Service paused for 180s, force invalidate cache."); } @@ -4532,9 +4560,17 @@ static void _dns_server_period_run_second(void) if (sec % 2 == 0) { if (dns_conf_prefetch) { /* do pre-fetching */ - dns_cache_invalidate(_dns_server_prefetch_domain, 3); + if (dns_conf_serve_expired) { + int prefetch_time = dns_conf_serve_expired_ttl / 2; + if (prefetch_time == 0 || prefetch_time > EXPIRED_DOMAIN_PREFTCH_TIME) { + prefetch_time = EXPIRED_DOMAIN_PREFTCH_TIME; + } + dns_cache_invalidate(NULL, 0, _dns_server_prefetch_expired_domain, prefetch_time); + } else { + dns_cache_invalidate(_dns_server_prefetch_domain, 3, NULL, 0); + } } else { - dns_cache_invalidate(NULL, 0); + dns_cache_invalidate(NULL, 0, NULL, 0); } }