Compare commits

..

5 Commits

Author SHA1 Message Date
Nick Peng
63db66546c Fix cache leak issue 2019-12-15 01:26:28 +08:00
Nick Peng
7c51259dff Update ReadMe 2019-12-15 01:26:27 +08:00
Nick Peng
cddc511e6b Fix prefetch issue 2019-12-15 01:26:27 +08:00
Nick Peng
c94fafb026 Add query retry feature 2019-12-15 01:26:26 +08:00
Nick Peng
76367847d1 optimize dualstack selection feature 2019-12-15 01:26:25 +08:00
6 changed files with 257 additions and 152 deletions

View File

@@ -3,7 +3,7 @@
![SmartDNS](doc/smartdns-banner.png)
SmartDNS is a local DNS server. SmartDNS accepts DNS query requests from local clients, obtains DNS query results from multiple upstream DNS servers, and returns the fastest access results to clients.
Avoiding DNS pollution and improving network access speed, supports high-performance ad filtering.
Unlike dnsmasq's all-servers, smartdns returns the fastest access resolution.
Unlike dnsmasq's all-servers, smartdns returns the fastest access resolution. [read more](#faq)
Support Raspberry Pi, openwrt, ASUS router, Windows and other devices.
@@ -106,7 +106,7 @@ From the comparison, smartdns found the fastest IP address to visit www.baidu.co
Support standard Linux system (Raspberry Pi), openwrt system various firmware, ASUS router native firmware. Support Windows 10 WSL (Windows Subsystem for Linux).
7. **Support IPV4, IPV6 dual stack**
Support IPV4, IPV6 network, support query A, AAAA record, dual-stack IP selection.
Support IPV4, IPV6 network, support query A, AAAA record, dual-stack IP selection, and disale IPV6 AAAA record.
8. **High performance, low resource consumption**
Multi-threaded asynchronous IO mode, cache cache query results.
@@ -597,8 +597,8 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
Nameserver /.office/office
```
1. How to use the dual stack IP optimization feature
At present, IPV6 network is not as fast as IPV4 in some cases. In order to get a better experience in the dual-stack network, SmartDNS provides a dual-stack IP optimization mechanism, the same domain name, and the speed of IPV4. Far faster than IPV6, then SmartDNS will block the resolution of IPV6, let the PC use IPV4, the feature is enabled by `dualstack-ip-selection yes`, `dualstack-ip-selection-threshold [time]` is for threshold.
1. How to use the IPV4, IPV6 dual stack IP optimization feature
At present, IPV6 network is not as fast as IPV4 in some cases. In order to get a better experience in the dual-stack network, SmartDNS provides a dual-stack IP optimization mechanism, the same domain name, and the speed of IPV4. Far faster than IPV6, then SmartDNS will block the resolution of IPV6, let the PC use IPV4, the feature is enabled by `dualstack-ip-selection yes`, `dualstack-ip-selection-threshold [time]` is for threshold. if you want to disable IPV6 AAAA record complete, please try `force-AAAA-SOA yes`.
1. How to improve cache performace
Smartdns provides a domain name caching mechanism to cache the queried domain name, and the caching time is in accordance with the DNS TTL specification. To increase the cache hit rate, the following configuration can be taken:

View File

@@ -3,7 +3,7 @@
![SmartDNS](doc/smartdns-banner.png)
SmartDNS是一个运行在本地的DNS服务器SmartDNS接受本地客户端的DNS查询请求从多个上游DNS服务器获取DNS查询结果并将访问速度最快的结果返回给客户端避免DNS污染提高网络访问速度。
同时支持指定特定域名IP地址并高性匹配达到过滤广告的效果。
与dnsmasq的all-servers不同smartdns返回的是访问速度最快的解析结果。
与dnsmasq的all-servers不同smartdns返回的是访问速度最快的解析结果。 (详细差异请看[FAQ](#faq))
支持树莓派openwrt华硕路由器windows等设备。
@@ -109,7 +109,7 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
支持标准Linux系统树莓派openwrt系统各种固件华硕路由器原生固件。以及支持Windows 10 WSL (Windows Subsystem for Linux)。
1. **支持IPV4, IPV6双栈**
支持IPV4IPV6网络支持查询A, AAAA记录支持双栈IP速度优化。
支持IPV4IPV6网络支持查询A, AAAA记录支持双栈IP速度优化并支持完全禁用IPV6 AAAA解析
1. **高性能,占用资源少**
多线程异步IO模式cache缓存查询结果。
@@ -531,7 +531,7 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
## FAQ
1. SmartDNS和DNSMASQ有什么区别
SMARTDNS在设计上并不是替换DNSMASQ的MARTDNS主要功能集中在DNS解析增强上增强部分有
SMARTDNS在设计上并不是替换DNSMASQ的SMARTDNS主要功能集中在DNS解析增强上增强部分有
* 多上游服务器并发请求,对结果进行测速后,返回最佳结果;
* addressipset域名匹配采用高效算法查询匹配更加快速高效路由器设备依然高效。
* 域名匹配支持忽略特定域名可单独匹配IPv4 IPV6支持多样化定制。
@@ -603,8 +603,8 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
通过上述配置即可实现DNS解析分流
1. 双栈IP优选功能如何使用
目前IPV6已经开始普及但IPV6网络在速度上某些情况下还不如IPV4为在双栈网络下获得较好的体验smartdns提供来双栈IP优选机制同一个域名若IPV4的速度远快与IPV6那么smartdns就会阻止IPV6的解析让PC使用IPV4访问具体配置文件通过`dualstack-ip-selection yes`启用此功能,通过`dualstack-ip-selection-threshold [time]`来修改阈值。
1. IPV4, IPV6双栈IP优选功能如何使用
目前IPV6已经开始普及但IPV6网络在速度上某些情况下还不如IPV4为在双栈网络下获得较好的体验smartdns提供来双栈IP优选机制同一个域名若IPV4的速度远快与IPV6那么smartdns就会阻止IPV6的解析让PC使用IPV4访问具体配置文件通过`dualstack-ip-selection yes`启用此功能,通过`dualstack-ip-selection-threshold [time]`来修改阈值。如果要完全禁止IPV6 AAAA记录解析可设置`force-AAAA-SOA yes`。
1. 如何提高cache效率加快访问速度
smartdns提供了域名缓存机制对查询的域名进行缓存缓存时间符合DNS TTL规范。为提高缓存命中率可采用如下措施

View File

@@ -5,7 +5,7 @@
struct dns_cache_head {
DECLARE_HASHTABLE(cache_hash, 10);
struct list_head cache_list;
int num;
atomic_t num;
int size;
pthread_mutex_t lock;
};
@@ -16,7 +16,7 @@ int dns_cache_init(int size)
{
INIT_LIST_HEAD(&dns_cache_head.cache_list);
hash_init(dns_cache_head.cache_hash);
dns_cache_head.num = 0;
atomic_set(&dns_cache_head.num, 0);
dns_cache_head.size = size;
pthread_mutex_init(&dns_cache_head.lock, NULL);
@@ -38,7 +38,7 @@ static void _dns_cache_delete(struct dns_cache *dns_cache)
{
hash_del(&dns_cache->node);
list_del_init(&dns_cache->list);
dns_cache_head.num--;
atomic_dec(&dns_cache_head.num);
free(dns_cache);
}
@@ -52,6 +52,9 @@ void dns_cache_get(struct dns_cache *dns_cache)
void dns_cache_release(struct dns_cache *dns_cache)
{
if (dns_cache == NULL) {
return;
}
if (!atomic_dec_and_test(&dns_cache->ref)) {
return;
}
@@ -66,7 +69,7 @@ static void _dns_cache_remove(struct dns_cache *dns_cache)
dns_cache_release(dns_cache);
}
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len)
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed)
{
struct dns_cache *dns_cache = NULL;
@@ -90,38 +93,40 @@ int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_typ
dns_cache->qtype = qtype;
dns_cache->ttl = ttl;
dns_cache->del_pending = 0;
dns_cache->speed = speed;
time(&dns_cache->insert_time);
pthread_mutex_unlock(&dns_cache_head.lock);
if (qtype == DNS_T_A) {
if (addr_len != DNS_RR_A_LEN) {
goto errout;
goto errout_unlock;
}
memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
} else if (qtype == DNS_T_AAAA) {
if (addr_len != DNS_RR_AAAA_LEN) {
goto errout;
goto errout_unlock;
}
memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
} else {
goto errout;
goto errout_unlock;
}
if (cname) {
strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
dns_cache->cname_ttl = cname_ttl;
}
pthread_mutex_unlock(&dns_cache_head.lock);
dns_cache_release(dns_cache);
return 0;
errout:
errout_unlock:
pthread_mutex_unlock(&dns_cache_head.lock);
//errout:
if (dns_cache) {
dns_cache_release(dns_cache);
}
return -1;
}
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len)
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed)
{
uint32_t key = 0;
struct dns_cache *dns_cache = NULL;
@@ -153,8 +158,9 @@ int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type
dns_cache->cname[0] = 0;
dns_cache->qtype = qtype;
dns_cache->ttl = ttl;
dns_cache->hitnum = 2;
atomic_set(&dns_cache->hitnum, 2);
dns_cache->del_pending = 0;
dns_cache->speed = speed;
atomic_set(&dns_cache->ref, 1);
time(&dns_cache->insert_time);
if (qtype == DNS_T_A) {
@@ -181,9 +187,8 @@ int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
INIT_LIST_HEAD(&dns_cache->check_list);
dns_cache_head.num++;
/* Release extra cache, remove oldest cache record */
if (dns_cache_head.num > dns_cache_head.size) {
if (atomic_inc_return(&dns_cache_head.num) > dns_cache_head.size) {
struct dns_cache *del_cache;
del_cache = _dns_cache_first();
if (del_cache) {
@@ -274,7 +279,7 @@ 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);
dns_cache->hitnum++;
atomic_inc(&dns_cache->hitnum);
}
pthread_mutex_unlock(&dns_cache_head.lock);
}

View File

@@ -19,8 +19,9 @@ struct dns_cache {
char domain[DNS_MAX_CNAME_LEN];
char cname[DNS_MAX_CNAME_LEN];
unsigned int cname_ttl;
unsigned int ttl;
int hitnum;
unsigned int ttl;;
int speed;
atomic_t hitnum;
int del_pending;
time_t insert_time;
dns_type_t qtype;
@@ -33,9 +34,9 @@ struct dns_cache {
int dns_cache_init(int size);
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len);
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed);
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len);
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed);
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype);

View File

@@ -54,6 +54,7 @@
#define DNS_TCP_IDLE_TIMEOUT (60 * 10)
#define DNS_TCP_CONNECT_TIMEOUT (5)
#define DNS_QUERY_TIMEOUT (500)
#define DNS_QUERY_RETRY (3)
#ifndef TCP_FASTOPEN_CONNECT
#define TCP_FASTOPEN_CONNECT 30
@@ -199,6 +200,12 @@ struct dns_query_struct {
dns_client_callback callback;
void *user_ptr;
/* retry count */
atomic_t retry_count;
/* has result */
int has_result;
/* replied hash table */
DECLARE_HASHTABLE(replied_map, 4);
};
@@ -260,8 +267,8 @@ static int _dns_client_server_exist(struct addrinfo *gai, dns_server_type_t serv
return -1;
}
static void _dns_client_server_update_ttl(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len,
int seqno, int ttl, struct timeval *tv, void *userptr)
static void _dns_client_server_update_ttl(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr,
socklen_t addr_len, int seqno, int ttl, struct timeval *tv, void *userptr)
{
struct dns_server_info *server_info = userptr;
if (result != PING_RESULT_RESPONSE || server_info == NULL) {
@@ -566,8 +573,8 @@ static void _dns_client_group_remove_all(void)
}
/* add dns server information */
static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, unsigned int server_flag, unsigned int result_flag, int ttl,
char *spki)
static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, unsigned int server_flag, unsigned int result_flag,
int ttl, char *spki)
{
struct dns_server_info *server_info = NULL;
unsigned char *spki_data = NULL;
@@ -773,7 +780,7 @@ static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_
}
static int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, unsigned int server_flag, unsigned int result_flag, int ttl,
char *spki, int operate)
char *spki, int operate)
{
char port_s[8];
int sock_type;
@@ -982,48 +989,6 @@ static void _dns_client_check_tcp(void)
pthread_mutex_unlock(&client.server_list_lock);
}
static void _dns_client_period_run_second(void)
{
_dns_client_check_tcp();
}
static void _dns_client_period_run(void)
{
struct dns_query_struct *query, *tmp;
static unsigned int msec = 0;
msec++;
LIST_HEAD(check_list);
unsigned long now = get_tick_count();
/* get query which timed out to check list */
pthread_mutex_lock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &client.dns_request_list, dns_request_list)
{
if ((now - DNS_QUERY_TIMEOUT >= query->send_tick) && query->send_tick > 0) {
list_add(&query->period_list, &check_list);
_dns_client_query_get(query);
}
}
pthread_mutex_unlock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &check_list, period_list)
{
/* free timed out query, and notify caller */
list_del_init(&query->period_list);
_dns_client_check_udp_nat(query);
_dns_client_query_remove(query);
_dns_client_query_release(query);
}
if (msec % 10 == 0) {
_dns_client_period_run_second();
}
return;
}
static struct dns_query_struct *_dns_client_get_request(unsigned short sid, char *domain)
{
struct dns_query_struct *query = NULL;
@@ -1163,6 +1128,10 @@ static int _dns_client_recv(struct dns_server_info *server_info, unsigned char *
/* if all server replied, or done, stop query, release resource */
_dns_client_query_remove(query);
}
if (ret == 0) {
query->has_result = 1;
}
}
_dns_client_query_release(query);
@@ -1301,7 +1270,7 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info)
if (connect(fd, (struct sockaddr *)&server_info->addr, server_info->ai_addrlen) != 0) {
if (errno != EINPROGRESS) {
tlog(TLOG_ERROR, "connect failed.");
tlog(TLOG_ERROR, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
}
@@ -1593,7 +1562,7 @@ static int _dns_client_process_tcp(struct dns_server_info *server_info, struct e
goto errout;
}
tlog(TLOG_ERROR, "recv failed, %s\n", strerror(errno));
tlog(TLOG_ERROR, "recv failed, server %s, %s\n", server_info->ip, strerror(errno));
goto errout;
}
@@ -1899,55 +1868,6 @@ static int _dns_client_process(struct dns_server_info *server_info, struct epoll
return 0;
}
static void *_dns_client_work(void *arg)
{
struct epoll_event events[DNS_MAX_EVENTS + 1];
int num;
int i;
unsigned long now = {0};
unsigned int sleep = 100;
int sleep_time;
unsigned long expect_time = 0;
sleep_time = sleep;
now = get_tick_count() - sleep;
expect_time = now + sleep;
while (client.run) {
now = get_tick_count();
if (now >= expect_time) {
_dns_client_period_run();
sleep_time = sleep - (now - expect_time);
if (sleep_time < 0) {
sleep_time = 0;
expect_time = now;
}
expect_time += sleep;
}
num = epoll_wait(client.epoll_fd, events, DNS_MAX_EVENTS, sleep_time);
if (num < 0) {
usleep(100000);
continue;
}
for (i = 0; i < num; i++) {
struct epoll_event *event = &events[i];
struct dns_server_info *server_info = (struct dns_server_info *)event->data.ptr;
if (server_info == NULL) {
tlog(TLOG_WARN, "server info is invalid.");
continue;
}
_dns_client_process(server_info, event, now);
}
}
close(client.epoll_fd);
client.epoll_fd = -1;
return NULL;
}
static int _dns_client_send_udp(struct dns_server_info *server_info, void *packet, int len)
{
int send_len = 0;
@@ -2221,12 +2141,14 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void
INIT_LIST_HEAD(&query->dns_request_list);
atomic_set(&query->refcnt, 0);
atomic_set(&query->dns_request_sent, 0);
atomic_set(&query->retry_count, DNS_QUERY_RETRY);
hash_init(query->replied_map);
strncpy(query->domain, domain, DNS_MAX_CNAME_LEN);
query->user_ptr = user_ptr;
query->callback = callback;
query->qtype = qtype;
query->send_tick = 0;
query->has_result = 0;
query->sid = atomic_inc_return(&dns_client_sid);
query->server_group = _dns_client_get_dnsserver_group(group_name);
if (query->server_group == NULL) {
@@ -2270,6 +2192,130 @@ errout:
return -1;
}
static void _dns_client_check_servers(void)
{
struct dns_server_info *server_info, *tmp;
static unsigned int second_count = 0;
second_count++;
if (second_count % 60 != 0) {
return;
}
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
if (server_info->type != DNS_SERVER_UDP) {
continue;
}
if (server_info->last_send - 600 > server_info->last_recv) {
server_info->recv_buff.len = 0;
server_info->send_buff.len = 0;
tlog(TLOG_DEBUG, "server %s may failure.", server_info->ip);
_dns_client_close_socket(server_info);
}
}
pthread_mutex_unlock(&client.server_list_lock);
}
static void _dns_client_period_run_second(void)
{
_dns_client_check_tcp();
_dns_client_check_servers();
}
static void _dns_client_period_run(void)
{
struct dns_query_struct *query, *tmp;
static unsigned int msec = 0;
msec++;
LIST_HEAD(check_list);
unsigned long now = get_tick_count();
/* get query which timed out to check list */
pthread_mutex_lock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &client.dns_request_list, dns_request_list)
{
if ((now - DNS_QUERY_TIMEOUT >= query->send_tick) && query->send_tick > 0) {
list_add(&query->period_list, &check_list);
_dns_client_query_get(query);
}
}
pthread_mutex_unlock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &check_list, period_list)
{
/* free timed out query, and notify caller */
list_del_init(&query->period_list);
_dns_client_check_udp_nat(query);
if (atomic_dec_and_test(&query->retry_count) || (query->has_result != 0)) {
_dns_client_query_remove(query);
} else {
tlog(TLOG_DEBUG, "retry query %s", query->domain);
_dns_client_send_query(query, query->domain);
}
_dns_client_query_release(query);
}
if (msec % 10 == 0) {
_dns_client_period_run_second();
}
return;
}
static void *_dns_client_work(void *arg)
{
struct epoll_event events[DNS_MAX_EVENTS + 1];
int num;
int i;
unsigned long now = {0};
unsigned int sleep = 100;
int sleep_time;
unsigned long expect_time = 0;
sleep_time = sleep;
now = get_tick_count() - sleep;
expect_time = now + sleep;
while (client.run) {
now = get_tick_count();
if (now >= expect_time) {
_dns_client_period_run();
sleep_time = sleep - (now - expect_time);
if (sleep_time < 0) {
sleep_time = 0;
expect_time = now;
}
expect_time += sleep;
}
num = epoll_wait(client.epoll_fd, events, DNS_MAX_EVENTS, sleep_time);
if (num < 0) {
usleep(100000);
continue;
}
for (i = 0; i < num; i++) {
struct epoll_event *event = &events[i];
struct dns_server_info *server_info = (struct dns_server_info *)event->data.ptr;
if (server_info == NULL) {
tlog(TLOG_WARN, "server info is invalid.");
continue;
}
_dns_client_process(server_info, event, now);
}
}
close(client.epoll_fd);
client.epoll_fd = -1;
return NULL;
}
int dns_client_set_ecs(char *ip, int subnet)
{
return 0;

View File

@@ -144,6 +144,8 @@ struct dns_request {
atomic_t adblock;
atomic_t soa_num;
/* send original raw packet to server/client like proxy */
int passthrough;
int request_wait;
@@ -550,33 +552,16 @@ static int _dns_server_request_complete(struct dns_request *request)
/* if doing prefetch, update cache only */
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN);
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN, request->ping_ttl_v4);
} else {
/* insert result to cache */
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN);
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN, request->ping_ttl_v4);
}
request->has_soa = 0;
}
} else if (request->qtype == DNS_T_AAAA) {
if (request->has_ipv4 && request->ping_ttl_v4 > 0) {
tlog(TLOG_INFO, "result: %s, rcode: %d, %d.%d.%d.%d\n", request->domain, request->rcode, request->ipv4_addr[0], request->ipv4_addr[1],
request->ipv4_addr[2], request->ipv4_addr[3]);
/* if ipv4 is fasting than ipv6, add ipv4 to cache, and return SOA for AAAA request */
if ((request->ping_ttl_v4 + (dns_conf_dualstack_ip_selection_threshold * 10)) < request->ping_ttl_v6 || request->ping_ttl_v6 < 0) {
tlog(TLOG_DEBUG, "Force IPV4 perfered.");
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN);
} else {
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN);
}
return _dns_server_reply_SOA(DNS_RC_NOERROR, request, NULL);
}
}
if (request->has_ipv6) {
tlog(TLOG_INFO, "result: %s, rcode: %d, %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", request->domain, request->rcode,
request->ipv6_addr[0], request->ipv6_addr[1], request->ipv6_addr[2], request->ipv6_addr[3], request->ipv6_addr[4], request->ipv6_addr[5],
@@ -589,15 +574,33 @@ static int _dns_server_request_complete(struct dns_request *request)
/* if doing prefetch, update cache only */
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN);
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN, request->ping_ttl_v6);
} else {
/* insert result to cache */
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN);
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN, request->ping_ttl_v6);
}
request->has_ipv4 = 0;
request->has_soa = 0;
}
if (request->has_ipv4 && (request->ping_ttl_v4 > 0)) {
tlog(TLOG_INFO, "result: %s, rcode: %d, %d.%d.%d.%d\n", request->domain, request->rcode, request->ipv4_addr[0], request->ipv4_addr[1],
request->ipv4_addr[2], request->ipv4_addr[3]);
/* if ipv4 is fasting than ipv6, add ipv4 to cache, and return SOA for AAAA request */
if ((request->ping_ttl_v4 + (dns_conf_dualstack_ip_selection_threshold * 10)) < request->ping_ttl_v6 || request->ping_ttl_v6 < 0) {
tlog(TLOG_DEBUG, "Force IPV4 perfered.");
if (request->prefetch) {
dns_cache_replace(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN, request->ping_ttl_v4);
} else {
dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN, request->ping_ttl_v4);
}
return _dns_server_reply_SOA(DNS_RC_NOERROR, request, NULL);
}
}
request->has_ipv4 = 0;
}
if (request->has_soa) {
@@ -949,6 +952,23 @@ match:
return 0;
}
static int _dns_server_is_adblock_ipv6(unsigned char addr[16])
{
int i = 0;
for (i = 0; i < 15; i++) {
if (addr[i]) {
return -1;
}
}
if (addr[15] == 0 || addr[15] == 1) {
return 0;
}
return -1;
}
static int _dns_server_process_answer(struct dns_request *request, char *domain, struct dns_packet *packet, unsigned int result_flag)
{
int ttl;
@@ -1086,6 +1106,15 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain,
}
}
/* Ad blocking result */
if (_dns_server_is_adblock_ipv6(addr) == 0) {
/* If half of the servers return the same result, then the domain name result is the IP address. */
if (atomic_inc_return(&request->adblock) <= dns_server_num() / 2) {
_dns_server_request_release(request);
break;
}
}
/* add this ip to reqeust */
if (_dns_ip_address_check_add(request, addr, DNS_T_AAAA) != 0) {
_dns_server_request_release(request);
@@ -1122,6 +1151,9 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain,
tlog(TLOG_DEBUG, "domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, expire: %d, minimum: %d", domain,
request->qtype, request->soa.mname, request->soa.rname, request->soa.serial, request->soa.refresh, request->soa.retry, request->soa.expire,
request->soa.minimum);
if (atomic_inc_return(&request->soa_num) >= (dns_server_num() / 2)) {
_dns_server_request_complete(request);
}
} break;
default:
tlog(TLOG_DEBUG, "%s, qtype: %d", name, rrs->type);
@@ -1435,6 +1467,7 @@ errout:
static int _dns_server_process_cache(struct dns_request *request, struct dns_packet *packet)
{
struct dns_cache *dns_cache = NULL;
struct dns_cache *dns_cache_A = NULL;
dns_cache = dns_cache_lookup(request->domain, request->qtype);
if (dns_cache == NULL) {
@@ -1445,6 +1478,18 @@ static int _dns_server_process_cache(struct dns_request *request, struct dns_pac
goto errout;
}
if (dns_conf_dualstack_ip_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) {
tlog(TLOG_DEBUG, "Force IPV4 perfered.");
dns_cache_release(dns_cache_A);
dns_cache_release(dns_cache);
return _dns_server_reply_SOA(DNS_RC_NOERROR, request, NULL);
}
}
}
/* Cache hits, returning results in the cache */
switch (request->qtype) {
case DNS_T_A:
@@ -1473,11 +1518,20 @@ static int _dns_server_process_cache(struct dns_request *request, struct dns_pac
dns_cache_update(dns_cache);
dns_cache_release(dns_cache);
if (dns_cache_A) {
dns_cache_release(dns_cache_A);
dns_cache_A = NULL;
}
return 0;
errout:
if (dns_cache) {
dns_cache_release(dns_cache);
}
if (dns_cache_A) {
dns_cache_release(dns_cache_A);
dns_cache_A = NULL;
}
return -1;
}
@@ -1523,6 +1577,7 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac
memset(request, 0, sizeof(*request));
pthread_mutex_init(&request->ip_map_lock, NULL);
atomic_set(&request->adblock, 0);
atomic_set(&request->soa_num, 0);
atomic_set(&request->refcnt, 0);
request->ping_ttl_v4 = -1;
request->ping_ttl_v6 = -1;
@@ -2021,14 +2076,12 @@ static void _dns_server_tcp_ping_check(struct dns_request *request)
static void _dns_server_prefetch_domain(struct dns_cache *dns_cache)
{
/* If there are still hits, continue pre-fetching */
if (dns_cache->hitnum <= 0) {
if (atomic_dec_return(&dns_cache->hitnum) <= 0) {
return;
}
dns_cache->hitnum--;
/* 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, dns_cache->hitnum);
tlog(TLOG_DEBUG, "prefetch by cache %s, qtype %d, ttl %d, hitnum %d", dns_cache->domain, dns_cache->qtype, dns_cache->ttl, atomic_read(&dns_cache->hitnum));
if (_dns_server_prefetch_request(dns_cache->domain, dns_cache->qtype) != 0) {
tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->domain, dns_cache->qtype);
}