Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63db66546c | ||
|
|
7c51259dff | ||
|
|
cddc511e6b | ||
|
|
c94fafb026 | ||
|
|
76367847d1 |
@@ -3,7 +3,7 @@
|
||||

|
||||
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:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||

|
||||
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双栈**
|
||||
支持IPV4,IPV6网络,支持查询A, AAAA记录,支持双栈IP速度优化。
|
||||
支持IPV4,IPV6网络,支持查询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解析增强上,增强部分有:
|
||||
* 多上游服务器并发请求,对结果进行测速后,返回最佳结果;
|
||||
* address,ipset域名匹配采用高效算法,查询匹配更加快速高效,路由器设备依然高效。
|
||||
* 域名匹配支持忽略特定域名,可单独匹配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规范。为提高缓存命中率,可采用如下措施:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
242
src/dns_client.c
242
src/dns_client.c
@@ -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;
|
||||
|
||||
105
src/dns_server.c
105
src/dns_server.c
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user