diff --git a/src/Makefile b/src/Makefile index c6b60cb..15e92b1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -15,8 +15,8 @@ # along with this program. If not, see . BIN=smartdns -OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o -OBJS_MAIN=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o lib/conf.o lib/nftset.o +OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/timer_wheel.o +OBJS_MAIN=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o timer.o lib/conf.o lib/nftset.o OBJS=$(OBJS_MAIN) $(OBJS_LIB) # cflags diff --git a/src/dns_cache.c b/src/dns_cache.c index e62801d..9af5e11 100644 --- a/src/dns_cache.c +++ b/src/dns_cache.c @@ -18,6 +18,7 @@ #include "dns_cache.h" #include "stringutil.h" +#include "timer.h" #include "tlog.h" #include "util.h" #include @@ -27,31 +28,28 @@ #include #include -#define DNS_CACHE_MAX_HITNUM 5000 -#define DNS_CACHE_HITNUM_STEP 2 +#define DNS_CACHE_MAX_HITNUM 6000 +#define DNS_CACHE_HITNUM_STEP 3 #define DNS_CACHE_HITNUM_STEP_MAX 6 +#define DNS_CACHE_READ_TIMEOUT 60 struct dns_cache_head { struct hash_table cache_hash; struct list_head cache_list; - struct list_head inactive_list; atomic_t num; int size; - int enable_inactive; - int inactive_list_expired; pthread_mutex_t lock; - struct dns_cache *last_active_inserted; + dns_cache_callback timeout_callback; }; typedef int (*dns_cache_read_callback)(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data); static struct dns_cache_head dns_cache_head; -int dns_cache_init(int size, int enable_inactive, int inactive_list_expired) +int dns_cache_init(int size, dns_cache_callback timeout_callback) { int bits = 0; INIT_LIST_HEAD(&dns_cache_head.cache_list); - INIT_LIST_HEAD(&dns_cache_head.inactive_list); bits = ilog2(size) - 1; if (bits >= 20) { @@ -63,35 +61,14 @@ int dns_cache_init(int size, int enable_inactive, int inactive_list_expired) hash_table_init(dns_cache_head.cache_hash, bits, malloc); atomic_set(&dns_cache_head.num, 0); dns_cache_head.size = size; - dns_cache_head.enable_inactive = enable_inactive; - dns_cache_head.inactive_list_expired = inactive_list_expired; - dns_cache_head.last_active_inserted = NULL; + dns_cache_head.timeout_callback = timeout_callback; pthread_mutex_init(&dns_cache_head.lock, NULL); return 0; } -static __attribute__((unused)) struct dns_cache *_dns_cache_last(void) +static struct dns_cache *_dns_cache_first(void) { - struct dns_cache *dns_cache = NULL; - - dns_cache = list_last_entry(&dns_cache_head.inactive_list, struct dns_cache, list); - if (dns_cache) { - return dns_cache; - } - - return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list); -} - -static struct dns_cache *_dns_inactive_cache_first(void) -{ - struct dns_cache *dns_cache = NULL; - - dns_cache = list_first_entry_or_null(&dns_cache_head.inactive_list, struct dns_cache, list); - if (dns_cache) { - return dns_cache; - } - return list_first_entry_or_null(&dns_cache_head.cache_list, struct dns_cache, list); } @@ -117,6 +94,7 @@ void dns_cache_release(struct dns_cache *dns_cache) if (dns_cache == NULL) { return; } + if (!atomic_dec_and_test(&dns_cache->ref)) { return; } @@ -128,23 +106,8 @@ static void _dns_cache_remove(struct dns_cache *dns_cache) { hash_del(&dns_cache->node); list_del_init(&dns_cache->list); + dns_timer_del(&dns_cache->timer); dns_cache_release(dns_cache); - - if (dns_cache == dns_cache_head.last_active_inserted) { - dns_cache_release(dns_cache_head.last_active_inserted); - dns_cache_head.last_active_inserted = NULL; - } -} - -static void _dns_cache_move_inactive(struct dns_cache *dns_cache) -{ - list_del(&dns_cache->list); - list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list); - if (dns_cache == dns_cache_head.last_active_inserted) { - dns_cache_release(dns_cache_head.last_active_inserted); - dns_cache_head.last_active_inserted = NULL; - } - time(&dns_cache->info.replace_time); } enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data) @@ -271,41 +234,33 @@ struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len return (struct dns_cache_data *)cache_packet; } -static void _dns_cache_insert_sorted(struct dns_cache *dns_cache, struct list_head *head) +static void dns_cache_timer_relase(struct tw_timer_list *timer, void *data) { - time_t ttl; - struct dns_cache *tmp = NULL; - struct list_head *insert_head = head; - - ttl = dns_cache->info.insert_time + dns_cache->info.ttl; - if (dns_cache_head.last_active_inserted && dns_cache != dns_cache_head.last_active_inserted) { - time_t ttl_last = - dns_cache_head.last_active_inserted->info.insert_time + dns_cache_head.last_active_inserted->info.ttl; - if (ttl == ttl_last) { - insert_head = &(dns_cache_head.last_active_inserted->list); - goto out; - } - } - - /* ascending order */ - list_for_each_entry_reverse(tmp, head, list) - { - if ((tmp->info.insert_time + tmp->info.ttl) <= ttl) { - insert_head = &tmp->list; - break; - } - } - -out: - if (dns_cache_head.last_active_inserted) { - dns_cache_release(dns_cache_head.last_active_inserted); - } - list_add(&dns_cache->list, insert_head); - dns_cache_head.last_active_inserted = dns_cache; - dns_cache_get(dns_cache); + struct dns_cache *dns_cache = data; + dns_cache_release(dns_cache); } -static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive, int inactive, +static void dns_cache_expired(struct tw_timer_list *timer, void *data, unsigned long timestamp) +{ + struct dns_cache *dns_cache = data; + + if (dns_cache->del_pending == 1) { + dns_cache_release(dns_cache); + return; + } + + if (dns_cache_head.timeout_callback) { + if (dns_cache_head.timeout_callback(dns_cache) != 0) { + dns_cache_release(dns_cache); + return; + } + } + + dns_cache->del_pending = 1; + dns_timer_mod(&dns_cache->timer, 3); +} + +static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int timeout, int update_time, struct dns_cache_data *cache_data) { struct dns_cache *dns_cache = NULL; @@ -318,7 +273,7 @@ static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int spee /* lookup existing cache */ dns_cache = dns_cache_lookup(cache_key); if (dns_cache == NULL) { - return dns_cache_insert(cache_key, ttl, speed, no_inactive, cache_data); + return dns_cache_insert(cache_key, ttl, speed, timeout, cache_data); } if (ttl < DNS_CACHE_TTL_MIN) { @@ -333,38 +288,35 @@ static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int spee dns_cache->info.query_flag = cache_key->query_flag; dns_cache->info.ttl = ttl; dns_cache->info.speed = speed; - dns_cache->info.no_inactive = no_inactive; + dns_cache->info.timeout = timeout; dns_cache->info.is_visited = 1; - old_cache_data = dns_cache->cache_data; - dns_cache->cache_data = cache_data; - list_del(&dns_cache->list); - - if (inactive == 0) { - time(&dns_cache->info.insert_time); - time(&dns_cache->info.replace_time); - _dns_cache_insert_sorted(dns_cache, &dns_cache_head.cache_list); - } else { - time(&dns_cache->info.replace_time); - list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list); + if (cache_data) { + old_cache_data = dns_cache->cache_data; + dns_cache->cache_data = cache_data; + if (old_cache_data == cache_data) { + old_cache_data = NULL; + } } - + if (update_time) { + time(&dns_cache->info.insert_time); + } + time(&dns_cache->info.replace_time); + list_del(&dns_cache->list); + list_add_tail(&dns_cache->list, &dns_cache_head.cache_list); + dns_timer_mod(&dns_cache->timer, timeout); pthread_mutex_unlock(&dns_cache_head.lock); - dns_cache_data_free(old_cache_data); + if (old_cache_data) { + dns_cache_data_free(old_cache_data); + } dns_cache_release(dns_cache); return 0; } -int dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive, +int dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int timeout, int update_time, struct dns_cache_data *cache_data) { - return _dns_cache_replace(cache_key, ttl, speed, no_inactive, 0, cache_data); -} - -int dns_cache_replace_inactive(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive, - struct dns_cache_data *cache_data) -{ - return _dns_cache_replace(cache_key, ttl, speed, no_inactive, 1, cache_data); + return _dns_cache_replace(cache_key, ttl, speed, timeout, update_time, cache_data); } static void _dns_cache_remove_by_domain(struct dns_cache_key *cache_key) @@ -430,24 +382,26 @@ static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data memcpy(&dns_cache->info, info, sizeof(*info)); dns_cache->del_pending = 0; dns_cache->cache_data = cache_data; + dns_cache->timer.function = dns_cache_expired; + dns_cache->timer.del_function = dns_cache_timer_relase; + dns_cache->timer.expires = info->timeout; + dns_cache->timer.data = dns_cache; pthread_mutex_lock(&dns_cache_head.lock); hash_table_add(dns_cache_head.cache_hash, &dns_cache->node, key); - if (head == &dns_cache_head.inactive_list) { - list_add_tail(&dns_cache->list, head); - } else { - _dns_cache_insert_sorted(dns_cache, head); - } + list_add_tail(&dns_cache->list, head); INIT_LIST_HEAD(&dns_cache->check_list); /* Release extra cache, remove oldest cache record */ if (atomic_inc_return(&dns_cache_head.num) > dns_cache_head.size) { struct dns_cache *del_cache = NULL; - del_cache = _dns_inactive_cache_first(); + del_cache = _dns_cache_first(); if (del_cache) { _dns_cache_remove(del_cache); } } pthread_mutex_unlock(&dns_cache_head.lock); + dns_cache_get(dns_cache); + dns_timer_add(&dns_cache->timer); return 0; errout: @@ -458,7 +412,7 @@ errout: return -1; } -int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive, +int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int timeout, struct dns_cache_data *cache_data) { struct dns_cache_info info; @@ -485,7 +439,7 @@ int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int no info.ttl = ttl; info.hitnum_update_add = DNS_CACHE_HITNUM_STEP; info.speed = speed; - info.no_inactive = no_inactive; + info.timeout = timeout; info.is_visited = 1; time(&info.insert_time); time(&info.replace_time); @@ -535,13 +489,7 @@ struct dns_cache *dns_cache_lookup(struct dns_cache_key *cache_key) } if (dns_cache_ret) { - /* Return NULL if the cache times out */ - if (dns_cache_head.enable_inactive == 0 && (now - dns_cache_ret->info.insert_time > dns_cache_ret->info.ttl)) { - _dns_cache_remove(dns_cache_ret); - dns_cache_ret = NULL; - } else { - dns_cache_get(dns_cache_ret); - } + dns_cache_get(dns_cache_ret); } pthread_mutex_unlock(&dns_cache_head.lock); @@ -638,6 +586,8 @@ void dns_cache_update(struct dns_cache *dns_cache) { pthread_mutex_lock(&dns_cache_head.lock); if (!list_empty(&dns_cache->list)) { + list_del_init(&dns_cache->list); + list_add_tail(&dns_cache->list, &dns_cache_head.cache_list); dns_cache->info.hitnum += dns_cache->info.hitnum_update_add; if (dns_cache->info.hitnum > DNS_CACHE_MAX_HITNUM) { dns_cache->info.hitnum = DNS_CACHE_MAX_HITNUM; @@ -651,141 +601,20 @@ void dns_cache_update(struct dns_cache *dns_cache) pthread_mutex_unlock(&dns_cache_head.lock); } -static void _dns_cache_remove_expired_ttl(dns_cache_callback inactive_precallback, int ttl_inactive_pre, - unsigned int max_callback_num, const time_t *now) -{ - struct dns_cache *dns_cache = NULL; - struct dns_cache *tmp = NULL; - unsigned int callback_num = 0; - 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; - if (ttl > 0) { - continue; - } - - if (dns_cache_head.inactive_list_expired + ttl < 0) { - _dns_cache_remove(dns_cache); - continue; - } - - if (inactive_precallback == NULL) { - if (dns_cache_head.inactive_list_expired + ttl > 0) { - break; - } - continue; - } - - ttl = *now - dns_cache->info.replace_time; - if (ttl < ttl_inactive_pre) { - break; - } - - if (callback_num >= max_callback_num) { - break; - } - - if (dns_cache->del_pending == 1) { - continue; - } - - /* If the TTL time is in the pre-timeout range, call callback function */ - dns_cache_get(dns_cache); - list_add_tail(&dns_cache->check_list, &checklist); - dns_cache->del_pending = 1; - callback_num++; - } - 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_callback precallback, int ttl_pre, unsigned int max_callback_num, - dns_cache_callback inactive_precallback, int ttl_inactive_pre) -{ - struct dns_cache *dns_cache = NULL; - struct dns_cache *tmp = NULL; - time_t now = 0; - int ttl = 0; - LIST_HEAD(checklist); - unsigned int callback_num = 0; - - if (max_callback_num <= 0) { - max_callback_num = -1; - } - - if (dns_cache_head.size <= 0) { - return; - } - - time(&now); - pthread_mutex_lock(&dns_cache_head.lock); - list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list) - { - ttl = dns_cache->info.insert_time + dns_cache->info.ttl - now; - if (ttl > ttl_pre) { - break; - } - - if (ttl > 0 && ttl < ttl_pre) { - /* If the TTL time is in the pre-timeout range, call callback function */ - if (precallback && dns_cache->del_pending == 0 && callback_num < max_callback_num) { - list_add_tail(&dns_cache->check_list, &checklist); - dns_cache_get(dns_cache); - dns_cache->del_pending = 1; - callback_num++; - continue; - } - } - - if (ttl <= 0) { - if (dns_cache_head.enable_inactive && dns_cache->info.no_inactive == 0) { - _dns_cache_move_inactive(dns_cache); - } else { - _dns_cache_remove(dns_cache); - } - } - } - 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(inactive_precallback, ttl_inactive_pre, max_callback_num, &now); - } - - list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list) - { - /* run callback */ - if (precallback) { - precallback(dns_cache); - } - list_del(&dns_cache->check_list); - dns_cache_release(dns_cache); - } -} - static int _dns_cache_read_to_cache(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data) { struct list_head *head = NULL; + head = &dns_cache_head.cache_list; + struct dns_cache_info *info = &cache_record->info; - if (cache_record->type == CACHE_RECORD_TYPE_ACTIVE) { - head = &dns_cache_head.cache_list; - } else if (cache_record->type == CACHE_RECORD_TYPE_INACTIVE) { - head = &dns_cache_head.inactive_list; - } else { - tlog(TLOG_ERROR, "read cache record type is invalid."); - goto errout; + time_t now = time(NULL); + unsigned int seed_tmp = now; + int passed_time = now - info->replace_time; + int timeout = info->timeout - passed_time; + if (timeout < DNS_CACHE_READ_TIMEOUT * 2) { + timeout = DNS_CACHE_READ_TIMEOUT + (rand_r(&seed_tmp) % DNS_CACHE_READ_TIMEOUT); } + info->timeout = timeout; if (_dns_cache_insert(&cache_record->info, cache_data, head) != 0) { tlog(TLOG_ERROR, "insert cache data failed."); @@ -854,11 +683,6 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number, dns_cache_read_ cache_record.info.is_visited = 0; cache_record.info.domain[DNS_MAX_CNAME_LEN - 1] = '\0'; cache_record.info.dns_group_name[DNS_GROUP_NAME_LEN - 1] = '\0'; - if (cache_record.type >= CACHE_RECORD_TYPE_END) { - tlog(TLOG_ERROR, "read cache record type is invalid."); - goto errout; - } - ret = callback(&cache_record, cache_data); if (ret == -2) { cache_data = NULL; @@ -930,7 +754,7 @@ int dns_cache_load(const char *file) return _dns_cache_file_read(file, _dns_cache_read_to_cache); } -static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RECORD_TYPE type, struct list_head *head) +static int _dns_cache_write_record(int fd, uint32_t *cache_number, struct list_head *head) { struct dns_cache *dns_cache = NULL; struct dns_cache *tmp = NULL; @@ -940,7 +764,6 @@ static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RE list_for_each_entry_safe(dns_cache, tmp, head, list) { cache_record.magic = MAGIC_RECORD; - cache_record.type = type; memcpy(&cache_record.info, &dns_cache->info, sizeof(struct dns_cache_info)); ssize_t ret = write(fd, &cache_record, sizeof(cache_record)); if (ret != sizeof(cache_record)) { @@ -968,11 +791,7 @@ errout: static int _dns_cache_write_records(int fd, uint32_t *cache_number) { - if (_dns_cache_write_record(fd, cache_number, CACHE_RECORD_TYPE_ACTIVE, &dns_cache_head.cache_list) != 0) { - return -1; - } - - if (_dns_cache_write_record(fd, cache_number, CACHE_RECORD_TYPE_INACTIVE, &dns_cache_head.inactive_list) != 0) { + if (_dns_cache_write_record(fd, cache_number, &dns_cache_head.cache_list) != 0) { return -1; } @@ -1060,17 +879,7 @@ void dns_cache_destroy(void) struct dns_cache *dns_cache = NULL; struct dns_cache *tmp = NULL; - if (dns_cache_head.last_active_inserted) { - dns_cache_release(dns_cache_head.last_active_inserted); - dns_cache_head.last_active_inserted = NULL; - } - pthread_mutex_lock(&dns_cache_head.lock); - list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list) - { - _dns_cache_delete(dns_cache); - } - list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list) { _dns_cache_delete(dns_cache); @@ -1083,6 +892,6 @@ void dns_cache_destroy(void) const char *dns_cache_file_version(void) { - const char *version = "cache ver 1.0"; + const char *version = "cache ver 1.2"; return version; } diff --git a/src/dns_cache.h b/src/dns_cache.h index 1eb67ab..a74f782 100644 --- a/src/dns_cache.h +++ b/src/dns_cache.h @@ -25,6 +25,7 @@ #include "hash.h" #include "hashtable.h" #include "list.h" +#include "timer.h" #include #include @@ -45,12 +46,6 @@ enum CACHE_TYPE { CACHE_TYPE_PACKET, }; -enum CACHE_RECORD_TYPE { - CACHE_RECORD_TYPE_ACTIVE, - CACHE_RECORD_TYPE_INACTIVE, - CACHE_RECORD_TYPE_END, -}; - struct dns_cache_data_head { enum CACHE_TYPE cache_type; int is_soa; @@ -90,7 +85,7 @@ struct dns_cache_info { int ttl; int hitnum; int speed; - int no_inactive; + int timeout; int hitnum_update_add; int is_visited; time_t insert_time; @@ -99,7 +94,6 @@ struct dns_cache_info { struct dns_cache_record { uint32_t magic; - enum CACHE_RECORD_TYPE type; struct dns_cache_info info; }; @@ -113,6 +107,8 @@ struct dns_cache { struct dns_cache_info info; struct dns_cache_data *cache_data; + + struct tw_timer_list timer; }; struct dns_cache_file { @@ -138,15 +134,14 @@ void dns_cache_data_free(struct dns_cache_data *data); struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len); -int dns_cache_init(int size, int enable_inactive, int inactive_list_expired); +typedef int (*dns_cache_callback)(struct dns_cache *dns_cache); -int dns_cache_replace(struct dns_cache_key *key, int ttl, int speed, int no_inactive, +int dns_cache_init(int size, dns_cache_callback timeout_callback); + +int dns_cache_replace(struct dns_cache_key *key, int ttl, int speed, int tiemout, int update_time, struct dns_cache_data *cache_data); -int dns_cache_replace_inactive(struct dns_cache_key *key, int ttl, int speed, int no_inactive, - struct dns_cache_data *cache_data); - -int dns_cache_insert(struct dns_cache_key *key, int ttl, int speed, int no_inactive, struct dns_cache_data *cache_data); +int dns_cache_insert(struct dns_cache_key *key, int ttl, int speed, int timeout, struct dns_cache_data *cache_data); struct dns_cache *dns_cache_lookup(struct dns_cache_key *key); @@ -162,11 +157,6 @@ int dns_cache_is_visited(struct dns_cache *dns_cache); void dns_cache_update(struct dns_cache *dns_cache); -typedef void dns_cache_callback(struct dns_cache *dns_cache); - -void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, unsigned int max_callback_num, - dns_cache_callback inactive_precallback, int ttl_inactive_pre); - int dns_cache_get_ttl(struct dns_cache *dns_cache); int dns_cache_get_cname_ttl(struct dns_cache *dns_cache); diff --git a/src/dns_server.c b/src/dns_server.c index c221fe8..88f466e 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -1253,6 +1253,43 @@ static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpac return ret; } +static int _dns_server_get_cache_timeout(struct dns_request *request, int ttl) +{ + int timeout = 0; + if (dns_conf_prefetch) { + if (dns_conf_serve_expired) { + timeout = dns_conf_serve_expired_prefetch_time; + if (timeout == 0) { + timeout = dns_conf_serve_expired_ttl / 2; + if (timeout == 0 || timeout > EXPIRED_DOMAIN_PREFETCH_TIME) { + timeout = EXPIRED_DOMAIN_PREFETCH_TIME; + } + } + + if (request->prefetch == 0) { + timeout += ttl; + } + } else { + timeout = ttl - 3; + } + } else { + timeout = ttl; + if (dns_conf_serve_expired) { + timeout += dns_conf_serve_expired_ttl; + } + } + + if (request->prefetch) { + timeout -= 1; + } + + if (timeout <= 0) { + timeout = 1; + } + + return timeout; +} + static int _dns_server_request_update_cache(struct dns_request *request, dns_type_t qtype, struct dns_cache_data *cache_data, int has_soa, int cache_ttl) { @@ -1293,18 +1330,13 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ cache_key.query_flag = request->server_flags; if (request->prefetch) { - if (request->prefetch_expired_domain == 0) { - if (dns_cache_replace(&cache_key, ttl, speed, request->no_serve_expired, cache_data) != 0) { - goto errout; - } - } else { - if (dns_cache_replace_inactive(&cache_key, ttl, speed, request->no_serve_expired, cache_data) != 0) { - goto errout; - } + if (dns_cache_replace(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl), + !request->prefetch_expired_domain, cache_data) != 0) { + goto errout; } } else { /* insert result to cache */ - if (dns_cache_insert(&cache_key, ttl, speed, request->no_serve_expired, cache_data) != 0) { + if (dns_cache_insert(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl), cache_data) != 0) { goto errout; } } @@ -1441,18 +1473,13 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context) cache_key.query_flag = request->server_flags; if (request->prefetch) { - if (request->prefetch_expired_domain == 0) { - if (dns_cache_replace(&cache_key, ttl, speed, request->no_serve_expired, cache_packet) != 0) { - goto errout; - } - } else { - if (dns_cache_replace_inactive(&cache_key, ttl, speed, request->no_serve_expired, cache_packet) != 0) { - goto errout; - } + if (dns_cache_replace(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl), + !request->prefetch_expired_domain, cache_packet) != 0) { + goto errout; } } else { /* insert result to cache */ - if (dns_cache_insert(&cache_key, ttl, speed, request->no_serve_expired, cache_packet) != 0) { + if (dns_cache_insert(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl), cache_packet) != 0) { goto errout; } } @@ -1484,12 +1511,15 @@ static int _dns_cache_packet(struct dns_server_post_context *context) cache_key.query_flag = request->server_flags; if (request->prefetch) { - if (dns_cache_replace(&cache_key, context->reply_ttl, -1, request->no_serve_expired, cache_packet) != 0) { + if (dns_cache_replace(&cache_key, context->reply_ttl, -1, + _dns_server_get_cache_timeout(request, context->reply_ttl), + !request->prefetch_expired_domain, cache_packet) != 0) { goto errout; } } else { /* insert result to cache */ - if (dns_cache_insert(&cache_key, context->reply_ttl, -1, request->no_serve_expired, cache_packet) != 0) { + if (dns_cache_insert(&cache_key, context->reply_ttl, -1, + _dns_server_get_cache_timeout(request, context->reply_ttl), cache_packet) != 0) { goto errout; } } @@ -3061,9 +3091,9 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque return -1; } - snprintf(ip, sizeof(ip), "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", paddr[0], paddr[1], - paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10], paddr[11], - paddr[12], paddr[13], paddr[14], paddr[15]); + snprintf(ip, sizeof(ip), "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", paddr[0], + paddr[1], paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10], + paddr[11], paddr[12], paddr[13], paddr[14], paddr[15]); /* start ping */ _dns_server_request_get(request); @@ -6450,13 +6480,13 @@ static int _dns_server_second_ping_check(struct dns_request *request) return ret; } -static void _dns_server_prefetch_domain(struct dns_cache *dns_cache) +static int _dns_server_prefetch_domain(struct dns_cache *dns_cache) { /* If there are still hits, continue pre-fetching */ struct dns_server_query_option server_query_option; int hitnum = dns_cache_hitnum_dec_get(dns_cache); if (hitnum <= 0) { - return; + return -1; } /* start prefetch domain */ @@ -6467,11 +6497,19 @@ static void _dns_server_prefetch_domain(struct dns_cache *dns_cache) server_query_option.ecs_enable_flag = 0; if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 0, &server_query_option) != 0) { tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); + return -1; } + + return 0; } -static void _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache) +static int _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache) { + time_t ttl = dns_cache->info.insert_time + dns_cache->info.ttl + dns_conf_serve_expired_ttl - time(NULL); + if (ttl < 0) { + return -1; + } + /* 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); @@ -6483,7 +6521,23 @@ static void _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache) if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 1, &server_query_option) != 0) { tlog(TLOG_DEBUG, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); + return -1; } + + return 0; +} + +static int _dns_server_cache_expired(struct dns_cache *dns_cache) +{ + if (dns_conf_prefetch == 1) { + if (dns_conf_serve_expired == 1) { + return _dns_server_prefetch_expired_domain(dns_cache); + } else { + return _dns_server_prefetch_domain(dns_cache); + } + } + + return -1; } static void _dns_server_tcp_idle_check(void) @@ -6592,44 +6646,8 @@ static void _dns_server_save_cache_to_file(void) static void _dns_server_period_run_second(void) { static unsigned int sec = 0; - static time_t last = 0; - time_t now = 0; sec++; - time(&now); - if (last == 0) { - last = now; - } - - if (now - 180 > last) { - dns_cache_invalidate(NULL, 0, 0, NULL, 0); - tlog(TLOG_WARN, "Service paused for 180s, force invalidate cache."); - } - - last = now; - - if (sec % 2 == 0) { - if (dns_conf_prefetch) { - /* do pre-fetching */ - if (dns_conf_serve_expired) { - int prefetch_time = dns_conf_serve_expired_prefetch_time; - - if (prefetch_time == 0) { - prefetch_time = dns_conf_serve_expired_ttl / 2; - if (prefetch_time == 0 || prefetch_time > EXPIRED_DOMAIN_PREFETCH_TIME) { - prefetch_time = EXPIRED_DOMAIN_PREFETCH_TIME; - } - } - dns_cache_invalidate(NULL, 0, DNS_MAX_DOMAIN_REFETCH_NUM, _dns_server_prefetch_expired_domain, - prefetch_time); - } else { - dns_cache_invalidate(_dns_server_prefetch_domain, 3, DNS_MAX_DOMAIN_REFETCH_NUM, NULL, 0); - } - } else { - dns_cache_invalidate(NULL, 0, 0, NULL, 0); - } - } - _dns_server_tcp_idle_check(); _dns_server_check_need_exit(); @@ -7212,7 +7230,7 @@ static int _dns_server_audit_init(void) static int _dns_server_cache_init(void) { - if (dns_cache_init(dns_conf_cachesize, dns_conf_serve_expired, dns_conf_serve_expired_ttl) != 0) { + if (dns_cache_init(dns_conf_cachesize, _dns_server_cache_expired) != 0) { tlog(TLOG_ERROR, "init cache failed."); return -1; } diff --git a/src/include/timer_wheel.h b/src/include/timer_wheel.h new file mode 100644 index 0000000..f5b8367 --- /dev/null +++ b/src/include/timer_wheel.h @@ -0,0 +1,50 @@ +/************************************************************************* + * + * Copyright (C) 2018-2023 Ruilin Peng (Nick) . + * + * smartdns is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * smartdns is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TIMER_WHEEL_H +#define __TIMER_WHEEL_H + +#include "list.h" + +struct tw_base; +struct tw_timer_list; + +typedef void (*tw_func)(struct tw_timer_list *, void *, unsigned long); +typedef void (*tw_del_func)(struct tw_timer_list *, void *); + +struct tw_timer_list { + void *data; + unsigned long expires; + tw_func function; + tw_del_func del_function; + struct list_head entry; +}; + +struct tw_base *tw_init_timers(void); + +int tw_cleanup_timers(struct tw_base *); + +void tw_add_timer(struct tw_base *, struct tw_timer_list *); + +int tw_del_timer(struct tw_base *, struct tw_timer_list *); + +int tw_mod_timer_pending(struct tw_base *, struct tw_timer_list *, unsigned long); + +int tw_mod_timer(struct tw_base *, struct tw_timer_list *, unsigned long); + +#endif diff --git a/src/lib/timer_wheel.c b/src/lib/timer_wheel.c new file mode 100644 index 0000000..f1fb832 --- /dev/null +++ b/src/lib/timer_wheel.c @@ -0,0 +1,419 @@ +/************************************************************************* + * + * Copyright (C) 2018-2023 Ruilin Peng (Nick) . + * + * smartdns is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * smartdns is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "bitops.h" +#include +#include +#include +#include +#include +#include +#include + +#include "timer_wheel.h" + +#define TVR_BITS 8 +#define TVN_BITS 6 +#define TVR_SIZE (1 << TVR_BITS) +#define TVN_SIZE (1 << TVN_BITS) +#define TVR_MASK (TVR_SIZE - 1) +#define TVN_MASK (TVN_SIZE - 1) +#define INDEX(N) ((base->jiffies >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK) + +struct tvec { + struct list_head vec[TVN_SIZE]; +}; + +struct tvec_root { + struct list_head vec[TVR_SIZE]; +}; + +struct tw_base { + pthread_spinlock_t lock; + + pthread_t runner; + + unsigned long jiffies; + + struct tvec_root tv1; + struct tvec tv2; + struct tvec tv3; + struct tvec tv4; + struct tvec tv5; +}; + +static inline void _tw_add_timer(struct tw_base *base, struct tw_timer_list *timer) +{ + int i; + unsigned long idx; + unsigned long expires; + struct list_head *vec; + + expires = timer->expires; + + idx = expires - base->jiffies; + + if (idx < TVR_SIZE) { + i = expires & TVR_MASK; + vec = base->tv1.vec + i; + } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { + i = (expires >> TVR_BITS) & TVN_MASK; + vec = base->tv2.vec + i; + } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { + i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; + vec = base->tv3.vec + i; + } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { + i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; + vec = base->tv4.vec + i; + } else if ((long)idx < 0) { + vec = base->tv1.vec + (base->jiffies & TVR_MASK); + } else { + i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; + vec = base->tv5.vec + i; + } + + list_add_tail(&timer->entry, vec); +} + +static inline unsigned long _apply_slack(struct tw_base *base, struct tw_timer_list *timer) +{ + long delta; + unsigned long mask, expires, expires_limit; + + expires = timer->expires; + + delta = expires - base->jiffies; + if (delta < 256) { + return expires; + } + + expires_limit = expires + delta / 256; + mask = expires ^ expires_limit; + if (mask == 0) { + return expires; + } + + int bit = fls_long(mask); + mask = (1UL << bit) - 1; + + expires_limit = expires_limit & ~(mask); + return expires_limit; +} + +static inline void _tw_detach_timer(struct tw_timer_list *timer) +{ + struct list_head *entry = &timer->entry; + + list_del(entry); + entry->next = NULL; +} + +static inline int _tw_cascade(struct tw_base *base, struct tvec *tv, int index) +{ + struct tw_timer_list *timer, *tmp; + struct list_head tv_list; + + list_replace_init(tv->vec + index, &tv_list); + + list_for_each_entry_safe(timer, tmp, &tv_list, entry) + { + _tw_add_timer(base, timer); + } + + return index; +} + +static inline int timer_pending(struct tw_timer_list *timer) +{ + struct list_head *entry = &timer->entry; + + return (entry->next != NULL); +} + +static inline int __detach_if_pending(struct tw_timer_list *timer) +{ + if (!timer_pending(timer)) { + return 0; + } + + _tw_detach_timer(timer); + return 1; +} + +static inline int __mod_timer(struct tw_base *base, struct tw_timer_list *timer, int pending_only) +{ + int ret = 0; + + ret = __detach_if_pending(timer); + if (!ret && pending_only) { + goto done; + } + + ret = 1; + _tw_add_timer(base, timer); + +done: + return ret; +} + +void tw_add_timer(struct tw_base *base, struct tw_timer_list *timer) +{ + if (timer->function == NULL) { + return; + } + + pthread_spin_lock(&base->lock); + { + timer->expires += base->jiffies; + timer->expires = _apply_slack(base, timer); + _tw_add_timer(base, timer); + } + pthread_spin_unlock(&base->lock); +} + +int tw_del_timer(struct tw_base *base, struct tw_timer_list *timer) +{ + int ret = 0; + + pthread_spin_lock(&base->lock); + { + if (timer_pending(timer)) { + ret = 1; + _tw_detach_timer(timer); + } + } + pthread_spin_unlock(&base->lock); + + if (ret == 1 && timer->del_function) { + timer->del_function(timer, timer->data); + } + + return ret; +} + +int tw_mod_timer_pending(struct tw_base *base, struct tw_timer_list *timer, unsigned long expires) +{ + int ret = 1; + + pthread_spin_lock(&base->lock); + { + timer->expires = expires + base->jiffies; + timer->expires = _apply_slack(base, timer); + + ret = __mod_timer(base, timer, 1); + } + pthread_spin_unlock(&base->lock); + + return ret; +} + +int tw_mod_timer(struct tw_base *base, struct tw_timer_list *timer, unsigned long expires) +{ + int ret = 1; + + pthread_spin_lock(&base->lock); + { + if (timer_pending(timer) && timer->expires == expires) { + goto unblock; + } + + timer->expires = expires + base->jiffies; + timer->expires = _apply_slack(base, timer); + + ret = __mod_timer(base, timer, 0); + } +unblock: + pthread_spin_unlock(&base->lock); + + return ret; +} + +int tw_cleanup_timers(struct tw_base *base) +{ + int ret = 0; + void *res = NULL; + + ret = pthread_cancel(base->runner); + if (ret != 0) { + goto errout; + } + ret = pthread_join(base->runner, &res); + if (ret != 0) { + goto errout; + } + if (res != PTHREAD_CANCELED) { + goto errout; + } + + ret = pthread_spin_destroy(&base->lock); + if (ret != 0) { + goto errout; + } + + free(base); + return 0; + +errout: + return -1; +} + +static inline void run_timers(struct tw_base *base) +{ + unsigned long index, call_time; + struct tw_timer_list *timer; + + struct list_head work_list; + struct list_head *head = &work_list; + + pthread_spin_lock(&base->lock); + { + index = base->jiffies & TVR_MASK; + + if (!index && (!_tw_cascade(base, &base->tv2, INDEX(0))) && (!_tw_cascade(base, &base->tv3, INDEX(1))) && + (!_tw_cascade(base, &base->tv4, INDEX(2)))) + _tw_cascade(base, &base->tv5, INDEX(3)); + + call_time = base->jiffies++; + list_replace_init(base->tv1.vec + index, head); + while (!list_empty(head)) { + tw_func fn; + void *data; + + timer = list_first_entry(head, struct tw_timer_list, entry); + fn = timer->function; + data = timer->data; + + _tw_detach_timer(timer); + pthread_spin_unlock(&base->lock); + { + fn(timer, data, call_time); + } + + pthread_spin_lock(&base->lock); + if ( (timer_pending(timer) == 0 && timer->del_function) ) { + pthread_spin_unlock(&base->lock); + timer->del_function(timer, timer->data); + pthread_spin_lock(&base->lock); + } + } + } + pthread_spin_unlock(&base->lock); +} + +static unsigned long _tw_tick_count(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); +} + +static void *timer_work(void *arg) +{ + struct tw_base *base = arg; + int sleep = 1000; + int sleep_time = 0; + unsigned long now = {0}; + unsigned long last = {0}; + unsigned long expect_time = 0; + + sleep_time = sleep; + now = _tw_tick_count() - sleep; + last = now; + expect_time = now + sleep; + while (1) { + run_timers(base); + + now = _tw_tick_count(); + if (sleep_time > 0) { + sleep_time -= now - last; + if (sleep_time <= 0) { + sleep_time = 0; + } + + int cnt = sleep_time / sleep; + expect_time -= cnt * sleep; + sleep_time -= cnt * sleep; + } + + if (now >= expect_time) { + sleep_time = sleep - (now - expect_time); + if (sleep_time < 0) { + sleep_time = 0; + expect_time = now; + } + expect_time += sleep; + } + last = now; + + usleep(sleep_time * 1000); + } + + return NULL; +} + +struct tw_base *tw_init_timers(void) +{ + int j = 0; + int ret = 0; + struct timeval tv = { + 0, + }; + struct tw_base *base = NULL; + + base = malloc(sizeof(*base)); + if (!base) { + goto errout; + } + + ret = pthread_spin_init(&base->lock, 0); + if (ret != 0) { + goto errout2; + } + + for (j = 0; j < TVN_SIZE; j++) { + INIT_LIST_HEAD(base->tv5.vec + j); + INIT_LIST_HEAD(base->tv4.vec + j); + INIT_LIST_HEAD(base->tv3.vec + j); + INIT_LIST_HEAD(base->tv2.vec + j); + } + + for (j = 0; j < TVR_SIZE; j++) { + INIT_LIST_HEAD(base->tv1.vec + j); + } + + ret = gettimeofday(&tv, 0); + if (ret < 0) { + goto errout1; + } + base->jiffies = tv.tv_sec; + + ret = pthread_create(&base->runner, NULL, timer_work, base); + if (ret != 0) { + goto errout1; + } + return base; + +errout1: + (void)pthread_spin_destroy(&base->lock); +errout2: + free(base); +errout: + return NULL; +} \ No newline at end of file diff --git a/src/smartdns.c b/src/smartdns.c index b4cd288..49c4f04 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -30,6 +30,7 @@ #include "rbtree.h" #include "tlog.h" #include "util.h" +#include "timer.h" #include #include #include @@ -486,6 +487,11 @@ static int _smartdns_init(void) tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng , build: %s %s)", __DATE__, __TIME__); + if (dns_timer_init() != 0) { + tlog(TLOG_ERROR, "init timer failed."); + goto errout; + } + if (_smartdns_init_ssl() != 0) { tlog(TLOG_ERROR, "init ssl failed."); goto errout; @@ -573,6 +579,7 @@ static void _smartdns_exit(void) fast_ping_exit(); dns_server_exit(); _smartdns_destroy_ssl(); + dns_timer_destroy(); tlog_exit(); dns_server_load_exit(); } diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..3d8d4fe --- /dev/null +++ b/src/timer.c @@ -0,0 +1,70 @@ +/************************************************************************* + * + * Copyright (C) 2018-2023 Ruilin Peng (Nick) . + * + * smartdns is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * smartdns is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "timer.h" +#include "timer_wheel.h" + +static struct tw_base *dns_timer_base = NULL; + +int dns_timer_init(void) +{ + struct tw_base *tw = tw_init_timers(); + if (tw == NULL) { + return -1; + } + + dns_timer_base = tw; + + return 0; +} + +void dns_timer_destroy(void) +{ + if (dns_timer_base != NULL) { + tw_cleanup_timers(dns_timer_base); + dns_timer_base = NULL; + } +} + +void dns_timer_add(struct tw_timer_list *timer) +{ + if (dns_timer_base == NULL) { + return; + } + + tw_add_timer(dns_timer_base, timer); +} + +int dns_timer_del(struct tw_timer_list *timer) +{ + if (dns_timer_base == NULL) { + return 0; + } + + return tw_del_timer(dns_timer_base, timer); +} + +int dns_timer_mod(struct tw_timer_list *timer, unsigned long expires) +{ + if (dns_timer_base == NULL) { + return 0; + } + + return tw_mod_timer(dns_timer_base, timer, expires); +} + diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..90f33fc --- /dev/null +++ b/src/timer.h @@ -0,0 +1,41 @@ +/************************************************************************* + * + * Copyright (C) 2018-2023 Ruilin Peng (Nick) . + * + * smartdns is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * smartdns is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SMART_DNS_TIMER_H +#define SMART_DNS_TIMER_H + +#include "timer_wheel.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus */ + +int dns_timer_init(void); + +void dns_timer_add(struct tw_timer_list *timer); + +int dns_timer_del(struct tw_timer_list *timer); + +int dns_timer_mod(struct tw_timer_list *timer, unsigned long expires); + +void dns_timer_destroy(void); + +#ifdef __cplusplus +} +#endif /*__cplusplus */ +#endif diff --git a/test/Makefile b/test/Makefile index d89847e..3449f59 100644 --- a/test/Makefile +++ b/test/Makefile @@ -23,8 +23,8 @@ CXXFLAGS += -g CXXFLAGS += -DTEST CXXFLAGS += -I./ -I../src -I../src/include -SMARTDNS_OBJS = lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o -SMARTDNS_OBJS += smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o +SMARTDNS_OBJS = lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o lib/timer_wheel.o +SMARTDNS_OBJS += smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o timer.o OBJS = $(addprefix ../src/, $(SMARTDNS_OBJS)) TEST_SOURCES := $(wildcard *.cc) $(wildcard */*.cc) $(wildcard */*/*.cc)