diff --git a/ReadMe.md b/ReadMe.md index 164179b..3df12a1 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -504,6 +504,8 @@ https://github.com/pymumu/smartdns/releases |bind|DNS监听端口号|[::]:53|可绑定多个端口
`IP:PORT`: 服务器IP,端口号。
`[-group]`: 请求时使用的DNS服务器组。
`[-no-rule-addr]`:跳过address规则。
`[-no-rule-nameserver]`:跳过Nameserver规则。
`[-no-rule-ipset]`:跳过Ipset规则。
`[no-rule-soa]`:跳过SOA(#)规则.
`[no-dualstack-selection]`:停用双栈测速。
`[-no-speed-check]`:停用测速。
`[-no-cache]`:停止缓存|bind :53 |bind-tcp|TCP DNS监听端口号|[::]:53|可绑定多个端口
`IP:PORT`: 服务器IP,端口号。
`[-group]`: 请求时使用的DNS服务器组。
`[-no-rule-addr]`:跳过address规则。
`[-no-rule-nameserver]`:跳过Nameserver规则。
`[-no-rule-ipset]`:跳过Ipset规则。
`[no-rule-soa]`:跳过SOA(#)规则.
`[no-dualstack-selection]`:停用双栈测速。
`[-no-speed-check]`:停用测速。
`[-no-cache]`:停止缓存|bind-tcp :53 |cache-size|域名结果缓存个数|512|数字|cache-size 512 +|cache-persist|是否持久化缓存|no|[yes\|no]|cache-persist yes +|cache-file|缓存持久化文件路径|/tmp/smartdns.cache|路径|cache-file /tmp/smartdns.cache |tcp-idle-time|TCP链接空闲超时时间|120|数字|tcp-idle-time 120 |rr-ttl|域名结果TTL|远程查询结果|大于0的数字|rr-ttl 600 |rr-ttl-min|允许的最小TTL值|远程查询结果|大于0的数字|rr-ttl-min 60 diff --git a/ReadMe_en.md b/ReadMe_en.md index bc812cc..409e6fa 100755 --- a/ReadMe_en.md +++ b/ReadMe_en.md @@ -498,6 +498,8 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |bind|DNS listening port number|[::]:53|Support binding multiple ports
`IP:PORT`: server IP, port number.
`[-group]`: The DNS server group used when requesting.
`[-no-rule-addr]`: Skip the address rule.
`[-no-rule-nameserver]`: Skip the Nameserver rule.
`[-no-rule-ipset]`: Skip the Ipset rule.
`[-no-rule-soa]`: Skip address SOA(#) rules.
`[-no-dualstack-selection]`: Disable dualstack ip selection.
`[-no-speed-check]`: Disable speed measurement.
`[-no-cache]`: stop caching |bind :53 |bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports
`IP:PORT`: server IP, port number.
`[-group]`: The DNS server group used when requesting.
`[-no-rule-addr]`: Skip the address rule.
`[-no-rule-nameserver]`: Skip the Nameserver rule.
`[-no-rule-ipset]`: Skip the Ipset rule.
`[-no-rule-soa]`: Skip address SOA(#) rules.
`[-no-dualstack-selection]`: Disable dualstack ip selection.
`[-no-speed-check]`: Disable speed measurement.
`[-no-cache]`: stop caching |bind-tcp :53 |cache-size|Domain name result cache number|512|integer|cache-size 512 +|cache-persist|enable persist cache|no|[yes\|no]|cache-persist yes +|cache-file|cache persist file|/tmp/smartdns.cache|路径|cache-file /tmp/smartdns.cache |tcp-idle-time|TCP connection idle timeout|120|integer|tcp-idle-time 120 |rr-ttl|Domain name TTL|Remote query result|number greater than 0|rr-ttl 600 |rr-ttl-min|Domain name Minimum TTL|Remote query result|number greater than 0|rr-ttl-min 60 diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index c5af23f..dec24b1 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -38,7 +38,13 @@ bind [::]:53 # dns cache size # cache-size [number] # 0: for no cache -cache-size 512 +cache-size 4096 + +# enable persist cache when restart +# cache-persist yes + +# cache persist file +# cache-file /tmp/smartdns.cache # prefetch domain # prefetch-domain [yes|no] diff --git a/package/optware/S50smartdns b/package/optware/S50smartdns index 393b962..88157d8 100644 --- a/package/optware/S50smartdns +++ b/package/optware/S50smartdns @@ -332,9 +332,28 @@ case "$1" in return 0 fi - if [ ! -d "/proc/$pid" ]; then - return 0; + kill -15 "$pid" 2>/dev/null + SLEEP=`which usleep` + SLEEPTIME=200000 + if [ -z "$SLEEP" ]; then + SLEEP="sleep" + SLEEPTIME=0.2 fi + N=30 + while [ $N -gt 0 ] + do + pid="$(cat "$SMARTDNS_PID" | head -n 1 2>/dev/null)" + if [ -z "$pid" ]; then + return 0 + fi + + if [ ! -d "/proc/$pid" ]; then + return 0; + fi + + $SLEEP $SLEEPTIME 2>/dev/null + N=$((N-1)) + done kill -9 "$pid" 2>/dev/null ;; diff --git a/src/dns_cache.c b/src/dns_cache.c index 9315ada..c0abd12 100644 --- a/src/dns_cache.c +++ b/src/dns_cache.c @@ -19,7 +19,11 @@ #include "dns_cache.h" #include "stringutil.h" #include "tlog.h" +#include +#include #include +#include +#include #define DNS_CACHE_MAX_HITNUM 5000 #define DNS_CACHE_HITNUM_STEP 2 @@ -65,7 +69,7 @@ static __attribute__((unused)) struct dns_cache *_dns_cache_last(void) return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list); } -static struct dns_cache *_dns_cache_first(void) +static struct dns_cache *_dns_inactive_cache_first(void) { struct dns_cache *dns_cache = NULL; @@ -162,7 +166,7 @@ struct dns_cache_data *dns_cache_new_data_addr(uint32_t cache_flag, char *cname, cache_addr->head.cache_flag = cache_flag; cache_addr->head.cache_type = CACHE_TYPE_ADDR; - cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_head); + cache_addr->head.size = sizeof(struct dns_cache_addr) - sizeof(struct dns_cache_data_head); return (struct dns_cache_data *)cache_addr; @@ -219,12 +223,12 @@ int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct /* update cache data */ pthread_mutex_lock(&dns_cache_head.lock); - dns_cache->ttl = ttl; - dns_cache->qtype = qtype; - dns_cache->ttl = ttl; dns_cache->del_pending = 0; - dns_cache->speed = speed; - time(&dns_cache->insert_time); + dns_cache->info.ttl = ttl; + 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); @@ -236,21 +240,13 @@ int dns_cache_replace(char *domain, int ttl, dns_type_t qtype, int speed, struct return 0; } -int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *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; struct dns_cache *dns_cache = NULL; - if (cache_data == NULL || domain == NULL) { - return -1; - } - - if (dns_cache_head.size <= 0) { - return 0; - } - /* if cache already exists, free */ - dns_cache = dns_cache_lookup(domain, qtype); + dns_cache = dns_cache_lookup(info->domain, info->qtype); if (dns_cache) { dns_cache_delete(dns_cache); dns_cache_release(dns_cache); @@ -262,32 +258,22 @@ int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct goto errout; } - if (ttl < DNS_CACHE_TTL_MIN) { - ttl = DNS_CACHE_TTL_MIN; - } - memset(dns_cache, 0, sizeof(*dns_cache)); - key = hash_string(domain); - key = jhash(&qtype, sizeof(qtype), key); - safe_strncpy(dns_cache->domain, domain, DNS_MAX_CNAME_LEN); - atomic_set(&dns_cache->hitnum, 3); + key = hash_string(info->domain); + key = jhash(&info->qtype, sizeof(info->qtype), key); atomic_set(&dns_cache->ref, 1); - dns_cache->qtype = qtype; - dns_cache->ttl = ttl; - dns_cache->hitnum_update_add = DNS_CACHE_HITNUM_STEP; + memcpy(&dns_cache->info, info, sizeof(*info)); dns_cache->del_pending = 0; - dns_cache->speed = speed; dns_cache->cache_data = cache_data; - time(&dns_cache->insert_time); pthread_mutex_lock(&dns_cache_head.lock); hash_add(dns_cache_head.cache_hash, &dns_cache->node, key); - list_add_tail(&dns_cache->list, &dns_cache_head.cache_list); + 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; - del_cache = _dns_cache_first(); + del_cache = _dns_inactive_cache_first(); if (del_cache) { _dns_cache_remove(del_cache); } @@ -303,6 +289,33 @@ errout: return -1; } +int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, int speed, struct dns_cache_data *cache_data) +{ + struct dns_cache_info info; + + if (cache_data == NULL || domain == NULL) { + return -1; + } + + if (dns_cache_head.size <= 0) { + return 0; + } + + if (ttl < DNS_CACHE_TTL_MIN) { + ttl = DNS_CACHE_TTL_MIN; + } + + info.hitnum = 3; + safe_strncpy(info.domain, domain, DNS_MAX_CNAME_LEN); + info.qtype = qtype; + info.ttl = ttl; + info.hitnum_update_add = DNS_CACHE_HITNUM_STEP; + info.speed = speed; + time(&info.insert_time); + + return _dns_cache_insert(&info, cache_data, &dns_cache_head.cache_list); +} + struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype) { uint32_t key = 0; @@ -322,11 +335,11 @@ struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype) pthread_mutex_lock(&dns_cache_head.lock); hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key) { - if (dns_cache->qtype != qtype) { + if (dns_cache->info.qtype != qtype) { continue; } - if (strncmp(domain, dns_cache->domain, DNS_MAX_CNAME_LEN) != 0) { + if (strncmp(domain, dns_cache->info.domain, DNS_MAX_CNAME_LEN) != 0) { continue; } @@ -336,7 +349,7 @@ struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype) if (dns_cache_ret) { /* Return NULL if the cache times out */ - if (dns_cache_head.enable_inactive == 0 && (now - dns_cache_ret->insert_time > dns_cache_ret->ttl)) { + 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 { @@ -355,7 +368,7 @@ int dns_cache_get_ttl(struct dns_cache *dns_cache) int ttl = 0; time(&now); - ttl = dns_cache->insert_time + dns_cache->ttl - now; + ttl = dns_cache->info.insert_time + dns_cache->info.ttl - now; if (ttl < 0) { return 0; } @@ -377,15 +390,14 @@ void dns_cache_delete(struct dns_cache *dns_cache) int dns_cache_hitnum_dec_get(struct dns_cache *dns_cache) { - int hitnum = 0; pthread_mutex_lock(&dns_cache_head.lock); - hitnum = atomic_dec_return(&dns_cache->hitnum); - if (dns_cache->hitnum_update_add > DNS_CACHE_HITNUM_STEP) { - dns_cache->hitnum_update_add--; + dns_cache->info.hitnum--; + if (dns_cache->info.hitnum_update_add > DNS_CACHE_HITNUM_STEP) { + dns_cache->info.hitnum_update_add--; } pthread_mutex_unlock(&dns_cache_head.lock); - return hitnum; + return dns_cache->info.hitnum; } void dns_cache_update(struct dns_cache *dns_cache) @@ -394,13 +406,13 @@ void dns_cache_update(struct dns_cache *dns_cache) if (!list_empty(&dns_cache->list)) { list_del_init(&dns_cache->list); list_add_tail(&dns_cache->list, &dns_cache_head.cache_list); - atomic_add(dns_cache->hitnum_update_add, &dns_cache->hitnum); - if (atomic_read(&dns_cache->hitnum) > DNS_CACHE_MAX_HITNUM) { - atomic_set(&dns_cache->hitnum, DNS_CACHE_MAX_HITNUM); + 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; } - if (dns_cache->hitnum_update_add < DNS_CACHE_HITNUM_STEP_MAX) { - dns_cache->hitnum_update_add++; + if (dns_cache->info.hitnum_update_add < DNS_CACHE_HITNUM_STEP_MAX) { + dns_cache->info.hitnum_update_add++; } } pthread_mutex_unlock(&dns_cache_head.lock); @@ -414,7 +426,7 @@ void _dns_cache_remove_expired_ttl(time_t *now) list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list) { - ttl = dns_cache->insert_time + dns_cache->ttl - *now; + ttl = dns_cache->info.insert_time + dns_cache->info.ttl - *now; if (ttl > 0) { continue; } @@ -443,7 +455,7 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre) pthread_mutex_lock(&dns_cache_head.lock); list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list) { - ttl = dns_cache->insert_time + dns_cache->ttl - now; + 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) { @@ -479,6 +491,216 @@ void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre) } } +static int _dns_cache_read_record(int fd, uint32_t cache_number) +{ + + int i = 0; + int ret = 0; + struct dns_cache_record cache_record; + struct dns_cache_data_head data_head; + struct dns_cache_data *cache_data = NULL; + struct list_head *head = NULL; + + for (i = 0; i < cache_number; i++) { + ret = read(fd, &cache_record, sizeof(cache_record)); + if (ret != sizeof(cache_record)) { + tlog(TLOG_ERROR, "read cache failed, %s", strerror(errno)); + goto errout; + } + + if (cache_record.magic != MAGIC_CACHE_DATA) { + tlog(TLOG_ERROR, "magic is invalid."); + goto errout; + } + + if (cache_record.type == CACHE_RECORD_TYPE_ACTIVE) { + head = &dns_cache_head.cache_list; + } else { + head = &dns_cache_head.inactive_list; + } + + ret = read(fd, &data_head, sizeof(data_head)); + if (ret != sizeof(data_head)) { + tlog(TLOG_ERROR, "read data head failed, %s", strerror(errno)); + goto errout; + } + + if (data_head.size > 1024 * 8) { + tlog(TLOG_ERROR, "data may invalid, skip load cache."); + goto errout; + } + + cache_data = malloc(data_head.size + sizeof(data_head)); + if (cache_data == NULL) { + tlog(TLOG_ERROR, "malloc cache data failed %s", strerror(errno)); + goto errout; + } + + memcpy(&cache_data->head, &data_head, sizeof(data_head)); + ret = read(fd, cache_data->data, data_head.size); + if (ret != data_head.size) { + tlog(TLOG_ERROR, "read cache data failed, %s", strerror(errno)); + goto errout; + } + + if (_dns_cache_insert(&cache_record.info, cache_data, head) != 0) { + tlog(TLOG_ERROR, "insert cache data failed."); + cache_data = NULL; + goto errout; + } + + cache_data = NULL; + } + + return 0; +errout: + if (cache_data) { + free(cache_data); + } + return -1; +} + +int dns_cache_load(const char *file) +{ + int fd = -1; + int ret = 0; + fd = open(file, O_RDONLY); + if (fd < 0) { + return 0; + } + + struct dns_cache_file cache_file; + ret = read(fd, &cache_file, sizeof(cache_file)); + if (ret != sizeof(cache_file)) { + tlog(TLOG_ERROR, "read cache head failed."); + goto errout; + } + + if (cache_file.magic != MAGIC_NUMBER) { + tlog(TLOG_ERROR, "cache file is invalid."); + goto errout; + } + + if (strncmp(cache_file.version, __TIMESTAMP__, DNS_CACHE_VERSION_LEN) != 0) { + tlog(TLOG_WARN, "cache version is different, skip load cache."); + goto errout; + } + + if (_dns_cache_read_record(fd, cache_file.cache_number) != 0) { + goto errout; + } + + close(fd); + return 0; +errout: + if (fd > 0) { + close(fd); + } + + return -1; +} + +static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RECORD_TYPE type, struct list_head *head) +{ + struct dns_cache *dns_cache = NULL; + struct dns_cache *tmp = NULL; + struct dns_cache_record cache_record; + + pthread_mutex_lock(&dns_cache_head.lock); + list_for_each_entry_safe_reverse(dns_cache, tmp, head, list) + { + cache_record.magic = MAGIC_CACHE_DATA; + cache_record.type = type; + memcpy(&cache_record.info, &dns_cache->info, sizeof(struct dns_cache_info)); + int ret = write(fd, &cache_record, sizeof(cache_record)); + if (ret != sizeof(cache_record)) { + tlog(TLOG_ERROR, "write cache failed, %s", strerror(errno)); + goto errout; + } + + struct dns_cache_data *cache_data = dns_cache->cache_data; + ret = write(fd, cache_data, sizeof(*cache_data) + cache_data->head.size); + if (ret != sizeof(*cache_data) + cache_data->head.size) { + tlog(TLOG_ERROR, "write cache data failed, %s", strerror(errno)); + goto errout; + } + + (*cache_number)++; + } + + pthread_mutex_unlock(&dns_cache_head.lock); + return 0; + +errout: + pthread_mutex_unlock(&dns_cache_head.lock); + return -1; +} + +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) { + return -1; + } + + return 0; +} + +int dns_cache_save(const char *file) +{ + int fd = -1; + uint32_t cache_number = 0; + tlog(TLOG_DEBUG, "write cache file %s", file); + + fd = open(file, O_TRUNC | O_CREAT | O_WRONLY | O_CLOEXEC, 0640); + if (fd < 0) { + tlog(TLOG_ERROR, "create file %s failed, %s", file, strerror(errno)); + goto errout; + } + + struct dns_cache_file cache_file; + memset(&cache_file, 0, sizeof(cache_file)); + cache_file.magic = MAGIC_NUMBER; + safe_strncpy(cache_file.version, __TIMESTAMP__, DNS_CACHE_VERSION_LEN); + cache_file.cache_number = 0; + + if (lseek(fd, sizeof(cache_file), SEEK_SET) < 0) { + tlog(TLOG_ERROR, "seek file %s failed, %s", file, strerror(errno)); + goto errout; + } + + if (_dns_cache_write_records(fd, &cache_number) != 0) { + tlog(TLOG_ERROR, "write record to file %s failed.", file); + goto errout; + } + + if (lseek(fd, 0, SEEK_SET) < 0) { + tlog(TLOG_ERROR, "seek file %s failed, %s", file, strerror(errno)); + goto errout; + } + + cache_file.cache_number = cache_number; + if (write(fd, &cache_file, sizeof(cache_file)) != sizeof(cache_file)) { + tlog(TLOG_ERROR, "write file head %s failed, %s, %d", file, strerror(errno), fd); + goto errout; + } + + tlog(TLOG_DEBUG, "wrote total %d records.", cache_number); + + close(fd); + return 0; +errout: + if (fd > 0) { + close(fd); + } + + return -1; +} + void dns_cache_destroy(void) { struct dns_cache *dns_cache = NULL; diff --git a/src/dns_cache.h b/src/dns_cache.h index efcd0d4..519cb68 100644 --- a/src/dns_cache.h +++ b/src/dns_cache.h @@ -32,12 +32,21 @@ extern "C" { #endif #define DNS_CACHE_TTL_MIN 30 +#define DNS_CACHE_VERSION_LEN 32 +#define MAGIC_NUMBER 0x6548634163536e44 +#define MAGIC_CACHE_DATA 0x44615461 enum CACHE_TYPE { CACHE_TYPE_NONE, CACHE_TYPE_ADDR, CACHE_TYPE_PACKET, }; + +enum CACHE_RECORD_TYPE { + CACHE_RECORD_TYPE_ACTIVE, + CACHE_RECORD_TYPE_INACTIVE, +}; + struct dns_cache_data_head { uint32_t cache_flag; enum CACHE_TYPE cache_type; @@ -67,32 +76,48 @@ struct dns_cache_packet { unsigned char data[0]; }; +struct dns_cache_info { + char domain[DNS_MAX_CNAME_LEN]; + int ttl; + int hitnum; + int speed; + int hitnum_update_add; + time_t insert_time; + dns_type_t qtype; +}; + +struct dns_cache_record { + uint32_t magic; + enum CACHE_RECORD_TYPE type; + struct dns_cache_info info; +}; + struct dns_cache { struct hlist_node node; struct list_head list; struct list_head check_list; - - atomic_t ref; - atomic_t hitnum; - char domain[DNS_MAX_CNAME_LEN]; - int ttl; - int speed; - int hitnum_update_add; + atomic_t ref; int del_pending; - time_t insert_time; - - dns_type_t qtype; + + struct dns_cache_info info; struct dns_cache_data *cache_data; }; +struct dns_cache_file { + uint64_t magic; + char version[DNS_CACHE_VERSION_LEN]; + uint32_t cache_number; +}; + enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data); uint32_t dns_cache_get_cache_flag(struct dns_cache_data *cache_data); void dns_cache_data_free(struct dns_cache_data *data); -struct dns_cache_data *dns_cache_new_data_addr(uint32_t cache_flag, char *cname, int cname_ttl, unsigned char *addr, int addr_len); +struct dns_cache_data *dns_cache_new_data_addr(uint32_t cache_flag, char *cname, int cname_ttl, unsigned char *addr, + int addr_len); struct dns_cache_data *dns_cache_new_data_packet(uint32_t cache_flag, void *packet, size_t packet_len); @@ -124,6 +149,10 @@ struct dns_cache_data *dns_cache_get_data(struct dns_cache *dns_cache); void dns_cache_destroy(void); +int dns_cache_load(const char *file); + +int dns_cache_save(const char *file); + #ifdef __cpluscplus } #endif diff --git a/src/dns_conf.c b/src/dns_conf.c index 28d9e4f..bd9641c 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -72,6 +72,9 @@ int dns_conf_log_num = 8; char dns_conf_ca_file[DNS_MAX_PATH]; char dns_conf_ca_path[DNS_MAX_PATH]; +char dns_conf_cache_file[DNS_MAX_PATH]; +int dns_conf_cache_persist = 2; + /* auditing */ int dns_conf_audit_enable = 0; int dns_conf_audit_log_SOA; @@ -107,6 +110,10 @@ static int _get_domain(char *value, char *domain, int max_dmain_size, char **ptr char *end = NULL; int len = 0; + if (value == NULL || domain == NULL) { + goto errout; + } + /* first field */ begin = strstr(value, "/"); if (begin == NULL) { @@ -1354,6 +1361,8 @@ static struct config_item _config_item[] = { CONF_CUSTOM("speed-check-mode", _config_speed_check_mode, NULL), CONF_INT("tcp-idle-time", &dns_conf_tcp_idle_time, 0, 3600), CONF_INT("cache-size", &dns_conf_cachesize, 0, CONF_INT_MAX), + CONF_STRING("cache-file", (char *)&dns_conf_cache_file, DNS_MAX_PATH), + CONF_YESNO("cache-persist", &dns_conf_cache_persist), CONF_YESNO("prefetch-domain", &dns_conf_prefetch), CONF_YESNO("serve-expired", &dns_conf_serve_expired), CONF_INT("serve-expired-ttl", &dns_conf_serve_expired_ttl, 0, CONF_INT_MAX), diff --git a/src/dns_conf.h b/src/dns_conf.h index 9658111..e8aef86 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -49,6 +49,7 @@ extern "C" { #define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf" #define SMARTDNS_LOG_FILE "/var/log/smartdns.log" #define SMARTDNS_AUDIT_FILE "/var/log/smartdns-audit.log" +#define SMARTDNS_CACHE_FILE "/tmp/smartdns.cache" enum domain_rule { DOMAIN_RULE_FLAGS = 0, @@ -90,7 +91,7 @@ typedef enum { #define BIND_FLAG_NO_SPEED_CHECK (1 << 5) #define BIND_FLAG_NO_CACHE (1 << 6) #define BIND_FLAG_NO_DUALSTACK_SELECTION (1 << 7) -#define BIND_FLAG_FORCE_AAAA_SOA (1 << 8) +#define BIND_FLAG_FORCE_AAAA_SOA (1 << 8) struct dns_rule_flags { unsigned int flags; @@ -215,6 +216,9 @@ extern int dns_conf_log_num; extern char dns_conf_ca_file[DNS_MAX_PATH]; extern char dns_conf_ca_path[DNS_MAX_PATH]; +extern char dns_conf_cache_file[DNS_MAX_PATH]; +extern int dns_conf_cache_persist; + extern struct dns_domain_check_order dns_conf_check_order; extern struct dns_server_groups dns_conf_server_groups[DNS_NAX_GROUP_NUMBER]; diff --git a/src/dns_server.c b/src/dns_server.c index b0efd0e..455e564 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -52,6 +52,7 @@ #define DNS_PING_SECOND_TIMEOUT (DNS_REQUEST_MAX_TIMEOUT - DNS_TCPPING_START) #define SOCKET_IP_TOS (IPTOS_LOWDELAY | IPTOS_RELIABILITY) #define SOCKET_PRIORITY (6) +#define CACHE_AUTO_ENABLE_SIZE (1024 * 1024 * 128) #define RECV_ERROR_AGAIN 1 #define RECV_ERROR_OK 0 @@ -490,7 +491,7 @@ static int _dns_server_reply_udp(struct dns_request *request, struct dns_server_ send_len = sendto(udpserver->head.fd, inpacket, inpacket_len, 0, (struct sockaddr *)&request->addr, request->addr_len); if (send_len != inpacket_len) { - tlog(TLOG_ERROR, "send failed."); + tlog(TLOG_ERROR, "send failed, %s", strerror(errno)); return -1; } @@ -1883,7 +1884,7 @@ static int _dns_server_reply_passthrouth(struct dns_request *request, struct dns } } - if(_dns_server_setup_ipset_packet(request, packet) != 0) { + if (_dns_server_setup_ipset_packet(request, packet) != 0) { tlog(TLOG_DEBUG, "setup ipset failed."); } @@ -2314,7 +2315,7 @@ static int _dns_server_process_cache_packet(struct dns_request *request, struct goto errout; } - if (dns_cache->qtype != request->qtype) { + if (dns_cache->info.qtype != request->qtype) { goto errout; } @@ -2405,15 +2406,15 @@ static int _dns_server_process_cache(struct dns_request *request) goto out; } - if (request->qtype != dns_cache->qtype) { + if (request->qtype != dns_cache->info.qtype) { goto out; } if (request->dualstack_selection && request->qtype == DNS_T_AAAA) { dns_cache_A = dns_cache_lookup(request->domain, DNS_T_A); - if (dns_cache_A && (dns_cache_A->speed > 0)) { - if ((dns_cache_A->speed + (dns_conf_dualstack_ip_selection_threshold * 10)) < dns_cache->speed || - dns_cache->speed < 0) { + if (dns_cache_A && (dns_cache_A->info.speed > 0)) { + if ((dns_cache_A->info.speed + (dns_conf_dualstack_ip_selection_threshold * 10)) < dns_cache->info.speed || + dns_cache->info.speed < 0) { tlog(TLOG_DEBUG, "Force IPV4 perfered."); ret = _dns_server_reply_SOA(DNS_RC_NOERROR, request); goto out_update_cache; @@ -2430,7 +2431,7 @@ out_update_cache: if (dns_cache_get_ttl(dns_cache) == 0) { uint32_t server_flags = request->server_flags; if (request->conn == NULL) { - server_flags = dns_cache_get_cache_flag(dns_cache_A->cache_data); + server_flags = dns_cache_get_cache_flag(dns_cache->cache_data); } _dns_server_prefetch_request(request->domain, request->qtype, server_flags); } else { @@ -3158,11 +3159,11 @@ static void _dns_server_prefetch_domain(struct dns_cache *dns_cache) } /* start prefetch domain */ - tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->domain, dns_cache->qtype, - dns_cache->ttl, hitnum); - if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype, + 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, dns_cache_get_cache_flag(dns_cache->cache_data)) != 0) { - tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->domain, dns_cache->qtype); + tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype); } } @@ -3568,6 +3569,60 @@ static int _dns_server_audit_init(void) return 0; } +static int _dns_server_cache_init(void) +{ + if (dns_cache_init(dns_conf_cachesize, dns_conf_serve_expired, dns_conf_serve_expired_ttl) != 0) { + tlog(TLOG_ERROR, "init cache failed."); + return -1; + } + + char *dns_cache_file = SMARTDNS_CACHE_FILE; + if (dns_conf_cache_file[0] != 0) { + dns_cache_file = dns_conf_cache_file; + } + + if (dns_conf_cache_persist == 2) { + uint64_t freespace = get_free_space(dns_cache_file); + if (freespace >= CACHE_AUTO_ENABLE_SIZE) { + tlog(TLOG_INFO, "auto enable cache persist."); + dns_conf_cache_persist = 1; + } + } + + if (dns_conf_cachesize <= 0 || dns_conf_cache_persist == 0) { + return 0; + } + + if (dns_cache_load(dns_cache_file) != 0) { + tlog(TLOG_WARN, "Load cache failed."); + return 0; + } + + return 0; +} + +static int _dns_server_cache_save(void) +{ + char *dns_cache_file = SMARTDNS_CACHE_FILE; + if (dns_conf_cache_file[0] != 0) { + dns_cache_file = dns_conf_cache_file; + } + + if (dns_conf_cache_persist == 0 || dns_conf_cachesize <= 0) { + if (access(dns_cache_file, F_OK) == 0) { + unlink(dns_cache_file); + } + return 0; + } + + if (dns_cache_save(dns_cache_file) != 0) { + tlog(TLOG_WARN, "save cache failed."); + return -1; + } + + return 0; +} + int dns_server_init(void) { pthread_attr_t attr; @@ -3578,9 +3633,9 @@ int dns_server_init(void) return -1; } - if (dns_cache_init(dns_conf_cachesize, dns_conf_serve_expired, dns_conf_serve_expired_ttl) != 0) { - tlog(TLOG_ERROR, "init cache failed."); - return -1; + if (_dns_server_cache_init() != 0) { + tlog(TLOG_ERROR, "init dns cache filed."); + goto errout; } if (_dns_server_audit_init() != 0) { @@ -3656,5 +3711,6 @@ void dns_server_exit(void) pthread_mutex_destroy(&server.request_list_lock); + _dns_server_cache_save(); dns_cache_destroy(); } diff --git a/src/smartdns.c b/src/smartdns.c index df14b94..70508d2 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -452,6 +452,7 @@ int main(int argc, char *argv[]) } signal(SIGINT, _sig_exit); + signal(SIGTERM, _sig_exit); atexit(_smartdns_exit); return _smartdns_run(); diff --git a/src/util.c b/src/util.c index 66212f3..cf36b4f 100644 --- a/src/util.c +++ b/src/util.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -994,4 +995,17 @@ int set_sock_lingertime(int fd, int time) } return 0; -} \ No newline at end of file +} + +uint64_t get_free_space(const char *path) +{ + uint64_t size = 0; + struct statvfs buf; + if (statvfs(path, &buf) != 0) { + return 0; + } + + size = (uint64_t)buf.f_frsize * buf.f_bavail; + + return size; +} diff --git a/src/util.h b/src/util.h index 2325daa..9efa737 100644 --- a/src/util.h +++ b/src/util.h @@ -106,6 +106,8 @@ int set_sock_keepalive(int fd, int keepidle, int keepinterval, int keepcnt); int set_sock_lingertime(int fd, int time); +uint64_t get_free_space(const char *path); + #ifdef __cplusplus } #endif /*__cplusplus */ diff --git a/systemd/smartdns.service.in b/systemd/smartdns.service.in index 052d9ea..79e218c 100644 --- a/systemd/smartdns.service.in +++ b/systemd/smartdns.service.in @@ -1,8 +1,9 @@ [Unit] -Description=smart dns server +Description=smartdns server After=network.target StartLimitBurst=0 StartLimitIntervalSec=60 +TimeoutStopSec=5 [Service] Type=forking