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)