Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a067e99c7 | ||
|
|
1e88305df8 | ||
|
|
93d9ef4095 |
11
ReadMe.md
11
ReadMe.md
@@ -3,7 +3,7 @@
|
||||
**[English](ReadMe_en.md)**
|
||||
|
||||

|
||||
SmartDNS是一个运行在本地的DNS服务器,SmartDNS接受本地客户端的DNS查询请求,从多个上游DNS服务器获取DNS查询结果,并将访问速度最快的结果返回给客户端,避免DNS污染,提高网络访问速度。
|
||||
SmartDNS是一个运行在本地的DNS服务器,SmartDNS接受本地客户端的DNS查询请求,从多个上游DNS服务器获取DNS查询结果,并将访问速度最快的结果返回给客户端,提高网络访问速度。
|
||||
同时支持指定特定域名IP地址,并高性匹配,达到过滤广告的效果。
|
||||
与dnsmasq的all-servers不同,smartdns返回的是访问速度最快的解析结果。 (详细差异请看[FAQ](#faq))
|
||||
|
||||
@@ -93,10 +93,10 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
|
||||
支持配置多个上游DNS服务器,并同时进行查询,即使其中有DNS服务器异常,也不会影响查询。
|
||||
|
||||
1. **返回最快IP地址**
|
||||
支持从域名所属IP地址列表中查找到访问速度最快的IP地址,并返回给客户端,避免DNS污染,提高网络访问速度。
|
||||
支持从域名所属IP地址列表中查找到访问速度最快的IP地址,并返回给客户端,提高网络访问速度。
|
||||
|
||||
1. **支持多种查询协议**
|
||||
支持UDP,TCP,TLS, HTTPS查询,以及非53端口查询,有效避免DNS污染。
|
||||
支持UDP,TCP,TLS, HTTPS查询,以及非53端口查询。
|
||||
|
||||
1. **特定域名IP地址指定**
|
||||
支持指定域名的IP地址,达到广告过滤效果,避免恶意网站的效果。
|
||||
@@ -560,7 +560,7 @@ https://github.com/pymumu/smartdns/releases
|
||||
|audit-size|审计大小|128K|数字+K,M,G|audit-size 128K
|
||||
|audit-num|审计归档个数|2|数字|audit-num 2
|
||||
|conf-file|附加配置文件|无|文件路径|conf-file /etc/smartdns/smartdns.more.conf
|
||||
|server|上游UDP DNS|无|可重复<br>`[ip][:port]`:服务器IP,端口可选。<br>`[-blacklist-ip]`:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。<br>`[-check-edns]`:edns过滤。<br>`[-group [group] ...]`:DNS服务器所属组,比如office, foreign,和nameserver配套使用。<br>`[-exclude-default-group]`:将DNS服务器从默认组中排除| server 8.8.8.8:53 -blacklist-ip -check-edns -group g1
|
||||
|server|上游UDP DNS|无|可重复<br>`[ip][:port]`:服务器IP,端口可选。<br>`[-blacklist-ip]`:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。<br>`[-group [group] ...]`:DNS服务器所属组,比如office, foreign,和nameserver配套使用。<br>`[-exclude-default-group]`:将DNS服务器从默认组中排除| server 8.8.8.8:53 -blacklist-ip -group g1
|
||||
|server-tcp|上游TCP DNS|无|可重复<br>`[ip][:port]`:服务器IP,端口可选。<br>`[-blacklist-ip]`:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。<br>`[-group [group] ...]`:DNS服务器所属组,比如office, foreign,和nameserver配套使用。<br>`[-exclude-default-group]`:将DNS服务器从默认组中排除| server-tcp 8.8.8.8:53
|
||||
|server-tls|上游TLS DNS|无|可重复<br>`[ip][:port]`:服务器IP,端口可选。<br>`[-spki-pin [sha256-pin]]`: TLS合法性校验SPKI值,base64编码的sha256 SPKI pin值<br>`[host-name]`:TLS SNI名称<br>`[-blacklist-ip]`:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。<br>`[-group [group] ...]`:DNS服务器所属组,比如office, foreign,和nameserver配套使用。<br>`[-exclude-default-group]`:将DNS服务器从默认组中排除| server-tls 8.8.8.8:853
|
||||
|server-https|上游HTTPS DNS|无|可重复<br>`https://[host][:port]/path`:服务器IP,端口可选。<br>`[-spki-pin [sha256-pin]]`: TLS合法性校验SPKI值,base64编码的sha256 SPKI pin值<br>`[host-name]`:TLS SNI名称<br>`[http-host]`:http协议头主机名<br>`[-blacklist-ip]`:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。<br>`[-group [group] ...]`:DNS服务器所属组,比如office, foreign,和nameserver配套使用。<br>`[-exclude-default-group]`:将DNS服务器从默认组中排除| server-https https://cloudflare-dns.com/dns-query
|
||||
@@ -586,7 +586,6 @@ https://github.com/pymumu/smartdns/releases
|
||||
* 针对广告屏蔽功能做增强,返回SOA,屏蔽广告效果更佳;
|
||||
* IPV4,IPV6双栈IP优选机制,在双网情况下,选择最快的网络通讯。
|
||||
* 支持最新的TLS, HTTPS协议,提供安全的DNS查询能力。
|
||||
* DNS防抢答机制,及多种机制避免DNS污染。
|
||||
* ECS支持,是查询结果更佳准确。
|
||||
* IP黑名单,忽略IP机制,使域名查询更佳准确。
|
||||
* 域名预查询,访问常用网站更加快速。
|
||||
@@ -601,8 +600,6 @@ https://github.com/pymumu/smartdns/releases
|
||||
* 国内公共DNS,如`119.29.29.29`, `223.5.5.5`。
|
||||
* 国外公共DNS,如`8.8.8.8`, `8.8.4.4`。
|
||||
|
||||
对于特定的域名,如果有污染情况,可以启用防污染机制。
|
||||
|
||||
1. 如何启用审计日志
|
||||
审计日志记录客户端请求的域名,记录信息包括,请求时间,请求IP,请求域名,请求类型,如果要启用审计日志,在配置界面配置`audit-enable yes`启用,`audit-size`, `audit-file`, `audit-num`分别配置审计日志文件大小,审计日志文件路径,和审计日志文件个数。审计日志文件将会压缩存储以节省空间。
|
||||
|
||||
|
||||
@@ -555,7 +555,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
|
||||
|audit-size|audit log size|128K|number+K,M,G|audit-size 128K
|
||||
|audit-num|archived audit log number|2|Integer|audit-num 2
|
||||
|conf-file|additional conf file|None|File path|conf-file /etc/smartdns/smartdns.more.conf
|
||||
|server|Upstream UDP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-check-edns]`: edns filter. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server 8.8.8.8:53 -blacklist-ip -check-edns
|
||||
|server|Upstream UDP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server 8.8.8.8:53 -blacklist-ip
|
||||
|server-tcp|Upstream TCP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-tcp 8.8.8.8:53
|
||||
|server-tls|Upstream TLS DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[host-name]`:TLS Server name<br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-tls 8.8.8.8:853
|
||||
|server-https|Upstream HTTPS DNS server|None|Repeatable <br>`https://[host][:port]/path`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[host-name]`:TLS Server name<br>`[http-host]`:http header host<br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group| server-https https://cloudflare-dns.com/dns-query
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid "SmartDNS Server"
|
||||
msgstr "SmartDNS 服务器"
|
||||
|
||||
msgid "SmartDNS is a local high-performance DNS server, supports finding fastest IP, supports ad filtering, and supports avoiding DNS poisoning."
|
||||
msgstr "SmartDNS是一个本地高性能DNS服务器,支持避免域名污染,支持返回最快IP,支持广告过滤。"
|
||||
msgstr "SmartDNS是一个本地高性能DNS服务器,支持返回最快IP,支持广告过滤。"
|
||||
|
||||
msgid "Custom Settings"
|
||||
msgstr "自定义设置"
|
||||
|
||||
@@ -70,13 +70,13 @@ o.cfgvalue = function(...)
|
||||
end
|
||||
|
||||
---- anti-Answer-Forgery
|
||||
o = s:option(Flag, "check_edns", translate("Anti Answer Forgery"), translate("Anti answer forgery, if DNS does not work properly after enabling, please turn off this feature"))
|
||||
o.rmempty = false
|
||||
o.default = o.disabled
|
||||
o:depends("type", "udp")
|
||||
o.cfgvalue = function(...)
|
||||
return Flag.cfgvalue(...) or "0"
|
||||
end
|
||||
-- o = s:option(Flag, "check_edns", translate("Anti Answer Forgery"), translate("Anti answer forgery, if DNS does not work properly after enabling, please turn off this feature"))
|
||||
-- o.rmempty = false
|
||||
-- o.default = o.disabled
|
||||
-- o:depends("type", "udp")
|
||||
-- o.cfgvalue = function(...)
|
||||
-- return Flag.cfgvalue(...) or "0"
|
||||
-- end
|
||||
|
||||
---- SPKI pin
|
||||
o = s:option(Value, "spki_pin", translate("TLS SPKI Pinning"), translate("Used to verify the validity of the TLS server, The value is Base64 encoded SPKI fingerprint, leaving blank to indicate that the validity of TLS is not verified."))
|
||||
|
||||
373
src/dns_client.c
373
src/dns_client.c
@@ -20,6 +20,7 @@
|
||||
#include "atomic.h"
|
||||
#include "dns.h"
|
||||
#include "dns_conf.h"
|
||||
#include "dns_server.h"
|
||||
#include "fast_ping.h"
|
||||
#include "hashtable.h"
|
||||
#include "http_parse.h"
|
||||
@@ -94,6 +95,7 @@ struct dns_server_info {
|
||||
struct ping_host_struct *ping_host;
|
||||
|
||||
char ip[DNS_HOSTNAME_LEN];
|
||||
int port;
|
||||
/* server type */
|
||||
dns_server_type_t type;
|
||||
|
||||
@@ -125,6 +127,33 @@ struct dns_server_info {
|
||||
struct client_dns_server_flags flags;
|
||||
};
|
||||
|
||||
struct dns_server_pending_group {
|
||||
struct list_head list;
|
||||
char group_name[DNS_GROUP_NAME_LEN];
|
||||
};
|
||||
|
||||
struct dns_server_pending {
|
||||
struct list_head list;
|
||||
|
||||
char host[DNS_HOSTNAME_LEN];
|
||||
char ipv4[DNS_HOSTNAME_LEN];
|
||||
char ipv6[DNS_HOSTNAME_LEN];
|
||||
unsigned int ping_time_v6;
|
||||
unsigned int ping_time_v4;
|
||||
unsigned int has_v4;
|
||||
unsigned int has_v6;
|
||||
unsigned int query_v4;
|
||||
unsigned int query_v6;
|
||||
/* server type */
|
||||
dns_server_type_t type;
|
||||
|
||||
int port;
|
||||
|
||||
struct client_dns_server_flags flags;
|
||||
|
||||
struct list_head group_list;
|
||||
};
|
||||
|
||||
/* upstream server group member */
|
||||
struct dns_server_group_member {
|
||||
struct list_head list;
|
||||
@@ -211,6 +240,9 @@ struct dns_query_struct {
|
||||
|
||||
static struct dns_client client;
|
||||
static atomic_t dns_client_sid = ATOMIC_INIT(0);
|
||||
static LIST_HEAD(pending_servers);
|
||||
pthread_mutex_t pending_server_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int dns_client_has_bootstrap_dns = 0;
|
||||
|
||||
/* get addr info */
|
||||
static struct addrinfo *_dns_client_getaddr(const char *host, char *port, int type, int protocol)
|
||||
@@ -240,21 +272,17 @@ errout:
|
||||
}
|
||||
|
||||
/* check whether server exists */
|
||||
static int _dns_client_server_exist(struct addrinfo *gai, dns_server_type_t server_type)
|
||||
static int _dns_client_server_exist(const char *server_ip, int port, dns_server_type_t server_type)
|
||||
{
|
||||
struct dns_server_info *server_info, *tmp;
|
||||
pthread_mutex_lock(&client.server_list_lock);
|
||||
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
|
||||
{
|
||||
if (server_info->ai_addrlen != gai->ai_addrlen || server_info->ai_family != gai->ai_family) {
|
||||
if (server_info->port != port || server_info->type != server_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (server_info->type != server_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
|
||||
if (strncmp(server_info->ip, server_ip, DNS_HOSTNAME_LEN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -284,49 +312,15 @@ static struct dns_server_info *_dns_client_get_server(char *server_ip, int port,
|
||||
{
|
||||
struct dns_server_info *server_info, *tmp;
|
||||
struct dns_server_info *server_info_return = NULL;
|
||||
char port_s[8];
|
||||
int sock_type;
|
||||
struct addrinfo *gai = NULL;
|
||||
|
||||
if (server_type >= DNS_SERVER_TYPE_END) {
|
||||
tlog(TLOG_ERROR, "server type is invalid.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (server_type) {
|
||||
case DNS_SERVER_UDP:
|
||||
sock_type = SOCK_DGRAM;
|
||||
break;
|
||||
case DNS_SERVER_TCP:
|
||||
case DNS_SERVER_TLS:
|
||||
case DNS_SERVER_HTTPS:
|
||||
sock_type = SOCK_STREAM;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* get addr info */
|
||||
snprintf(port_s, 8, "%d", port);
|
||||
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
|
||||
if (gai == NULL) {
|
||||
tlog(TLOG_ERROR, "get address failed, %s:%d", server_ip, port);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&client.server_list_lock);
|
||||
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
|
||||
{
|
||||
if (server_info->ai_addrlen != gai->ai_addrlen || server_info->ai_family != gai->ai_family) {
|
||||
if (server_info->port != port || server_info->type != server_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (server_info->type != server_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
|
||||
if (strncmp(server_info->ip, server_ip, DNS_HOSTNAME_LEN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -337,13 +331,7 @@ static struct dns_server_info *_dns_client_get_server(char *server_ip, int port,
|
||||
|
||||
pthread_mutex_unlock(&client.server_list_lock);
|
||||
|
||||
freeaddrinfo(gai);
|
||||
return server_info_return;
|
||||
errout:
|
||||
if (gai) {
|
||||
freeaddrinfo(gai);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get server group by name */
|
||||
@@ -421,19 +409,69 @@ errout:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _dns_client_add_to_pending_group(char *group_name, char *server_ip, int port, dns_server_type_t server_type)
|
||||
{
|
||||
struct dns_server_pending *item, *tmp;
|
||||
struct dns_server_pending *pending = NULL;
|
||||
struct dns_server_pending_group *group = NULL;
|
||||
|
||||
pthread_mutex_lock(&pending_server_mutex);
|
||||
list_for_each_entry_safe(item, tmp, &pending_servers, list)
|
||||
{
|
||||
if (strncmp(item->host, server_ip, DNS_HOSTNAME_LEN) == 0 && item->port == port && item->type == server_type) {
|
||||
pending = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&pending_server_mutex);
|
||||
|
||||
if (pending == NULL) {
|
||||
tlog(TLOG_ERROR, "cannot found server for group %s: %s, %d, %d", group_name, server_ip, port, server_type);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
group = malloc(sizeof(*group));
|
||||
if (group == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
memset(group, 0, sizeof(*group));
|
||||
strncpy(group->group_name, group_name, DNS_GROUP_NAME_LEN);
|
||||
|
||||
pthread_mutex_lock(&pending_server_mutex);
|
||||
list_add_tail(&group->list, &pending->group_list);
|
||||
pthread_mutex_unlock(&pending_server_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
if (group) {
|
||||
free(group);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* add server to group */
|
||||
int dns_client_add_to_group(char *group_name, char *server_ip, int port, dns_server_type_t server_type)
|
||||
int _dns_client_add_to_group_pending(char *group_name, char *server_ip, int port, dns_server_type_t server_type, int ispending)
|
||||
{
|
||||
struct dns_server_info *server_info = NULL;
|
||||
|
||||
server_info = _dns_client_get_server(server_ip, port, server_type);
|
||||
if (server_info == NULL) {
|
||||
return -1;
|
||||
if (ispending == 0) {
|
||||
tlog(TLOG_ERROR, "add server %s:%d to group %s failed", server_ip, port, group_name);
|
||||
return -1;
|
||||
}
|
||||
return _dns_client_add_to_pending_group(group_name, server_ip, port, server_type);
|
||||
}
|
||||
|
||||
return _dns_client_add_to_group(group_name, server_info);
|
||||
}
|
||||
|
||||
int dns_client_add_to_group(char *group_name, char *server_ip, int port, dns_server_type_t server_type)
|
||||
{
|
||||
return _dns_client_add_to_group_pending(group_name, server_ip, port, server_type, 1);
|
||||
}
|
||||
|
||||
/* free group member */
|
||||
static int _dns_client_remove_member(struct dns_server_group_member *group_member)
|
||||
{
|
||||
@@ -618,12 +656,15 @@ static char *_dns_client_server_get_spki(struct dns_server_info *server_info, in
|
||||
}
|
||||
|
||||
/* add dns server information */
|
||||
static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, struct client_dns_server_flags *flags)
|
||||
static int _dns_client_server_add(char *server_ip, char *server_host, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags)
|
||||
{
|
||||
struct dns_server_info *server_info = NULL;
|
||||
struct addrinfo *gai = NULL;
|
||||
unsigned char *spki_data = NULL;
|
||||
int spki_data_len = 0;
|
||||
int ttl = 0;
|
||||
char port_s[8];
|
||||
int sock_type;
|
||||
|
||||
switch (server_type) {
|
||||
case DNS_SERVER_UDP: {
|
||||
@@ -634,20 +675,29 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
|
||||
} else if (ttl < -32) {
|
||||
ttl = -32;
|
||||
}
|
||||
|
||||
sock_type = SOCK_DGRAM;
|
||||
} break;
|
||||
case DNS_SERVER_HTTPS: {
|
||||
struct client_dns_server_flag_https *flag_https = &flags->https;
|
||||
spki_data_len = flag_https->spi_len;
|
||||
if (flag_https->httphost[0] == 0) {
|
||||
strncpy(flag_https->httphost, server_ip, DNS_MAX_CNAME_LEN);
|
||||
if (server_host) {
|
||||
strncpy(flag_https->httphost, server_host, DNS_MAX_CNAME_LEN);
|
||||
} else {
|
||||
strncpy(flag_https->httphost, server_ip, DNS_MAX_CNAME_LEN);
|
||||
}
|
||||
}
|
||||
sock_type = SOCK_STREAM;
|
||||
} break;
|
||||
case DNS_SERVER_TLS: {
|
||||
struct client_dns_server_flag_tls *flag_tls = &flags->tls;
|
||||
spki_data_len = flag_tls->spi_len;
|
||||
sock_type = SOCK_STREAM;
|
||||
} break;
|
||||
break;
|
||||
case DNS_SERVER_TCP:
|
||||
sock_type = SOCK_STREAM;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
@@ -660,10 +710,17 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
|
||||
}
|
||||
|
||||
/* if server exist, return */
|
||||
if (_dns_client_server_exist(gai, server_type) == 0) {
|
||||
if (_dns_client_server_exist(server_ip, port, server_type) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(port_s, 8, "%d", port);
|
||||
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
|
||||
if (gai == NULL) {
|
||||
tlog(TLOG_DEBUG, "get address failed, %s:%d", server_ip, port);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
server_info = malloc(sizeof(*server_info));
|
||||
if (server_info == NULL) {
|
||||
goto errout;
|
||||
@@ -675,6 +732,7 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
|
||||
|
||||
memset(server_info, 0, sizeof(*server_info));
|
||||
strncpy(server_info->ip, server_ip, sizeof(server_info->ip));
|
||||
server_info->port = port;
|
||||
server_info->ai_family = gai->ai_family;
|
||||
server_info->ai_addrlen = gai->ai_addrlen;
|
||||
server_info->type = server_type;
|
||||
@@ -729,6 +787,8 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
|
||||
pthread_mutex_unlock(&client.server_list_lock);
|
||||
|
||||
atomic_inc(&client.dns_server_num);
|
||||
freeaddrinfo(gai);
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
if (spki_data) {
|
||||
@@ -747,6 +807,10 @@ errout:
|
||||
free(server_info);
|
||||
}
|
||||
|
||||
if (gai) {
|
||||
freeaddrinfo(gai);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -812,7 +876,7 @@ static void _dns_client_server_remove_all(void)
|
||||
}
|
||||
|
||||
/* remove single server */
|
||||
static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type)
|
||||
static int _dns_client_server_remove(char *server_ip, int port, dns_server_type_t server_type)
|
||||
{
|
||||
struct dns_server_info *server_info, *tmp;
|
||||
|
||||
@@ -820,11 +884,11 @@ static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_
|
||||
pthread_mutex_lock(&client.server_list_lock);
|
||||
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
|
||||
{
|
||||
if (server_info->ai_addrlen != gai->ai_addrlen || server_info->ai_family != gai->ai_family) {
|
||||
if (server_info->port != port || server_info->type != server_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
|
||||
if (strncmp(server_info->ip, server_ip, DNS_HOSTNAME_LEN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -840,70 +904,79 @@ static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags, int operate)
|
||||
static int _dns_client_server_pending(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags)
|
||||
{
|
||||
struct dns_server_pending *pending = NULL;
|
||||
|
||||
pending = malloc(sizeof(*pending));
|
||||
if (pending == NULL) {
|
||||
tlog(TLOG_ERROR, "malloc failed");
|
||||
goto errout;
|
||||
}
|
||||
memset(pending, 0, sizeof(*pending));
|
||||
|
||||
strncpy(pending->host, server_ip, DNS_HOSTNAME_LEN);
|
||||
pending->port = port;
|
||||
pending->type = server_type;
|
||||
pending->ping_time_v4 = -1;
|
||||
pending->ping_time_v6 = -1;
|
||||
pending->ipv4[0] = 0;
|
||||
pending->ipv6[0] = 0;
|
||||
pending->has_v4 = 0;
|
||||
pending->has_v6 = 0;
|
||||
INIT_LIST_HEAD(&pending->group_list);
|
||||
memcpy(&pending->flags, flags, sizeof(struct client_dns_server_flags));
|
||||
|
||||
pthread_mutex_lock(&pending_server_mutex);
|
||||
list_add_tail(&pending->list, &pending_servers);
|
||||
pthread_mutex_unlock(&pending_server_mutex);
|
||||
return 0;
|
||||
errout:
|
||||
if (pending) {
|
||||
free(pending);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _dns_client_add_server_pending(char *server_ip, char *server_host, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags, int ispending)
|
||||
{
|
||||
char port_s[8];
|
||||
int sock_type;
|
||||
int ret;
|
||||
struct addrinfo *gai = NULL;
|
||||
|
||||
if (server_type >= DNS_SERVER_TYPE_END) {
|
||||
tlog(TLOG_ERROR, "server type is invalid.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (server_type) {
|
||||
case DNS_SERVER_UDP:
|
||||
sock_type = SOCK_DGRAM;
|
||||
break;
|
||||
case DNS_SERVER_TCP:
|
||||
case DNS_SERVER_TLS:
|
||||
case DNS_SERVER_HTTPS:
|
||||
sock_type = SOCK_STREAM;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
if (check_is_ipaddr(server_ip) && ispending) {
|
||||
ret = _dns_client_server_pending(server_ip, port, server_type, flags);
|
||||
if (ret == 0) {
|
||||
tlog(TLOG_INFO, "add pending server %s", server_ip);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* get addr info */
|
||||
snprintf(port_s, 8, "%d", port);
|
||||
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
|
||||
if (gai == NULL) {
|
||||
tlog(TLOG_ERROR, "get address failed, %s:%d", server_ip, port);
|
||||
/* add server */
|
||||
ret = _dns_client_server_add(server_ip, server_host, port, server_type, flags);
|
||||
if (ret != 0) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (operate == 0) {
|
||||
/* add server */
|
||||
ret = _dns_client_server_add(server_ip, gai, server_type, flags);
|
||||
if (ret != 0) {
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
/* remove server */
|
||||
ret = _dns_client_server_remove(server_ip, gai, server_type);
|
||||
if (ret != 0) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(gai);
|
||||
dns_client_has_bootstrap_dns = 1;
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
if (gai) {
|
||||
freeaddrinfo(gai);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags)
|
||||
{
|
||||
return _dns_client_server_operate(server_ip, port, server_type, flags, 0);
|
||||
return _dns_client_add_server_pending(server_ip, NULL, port, server_type, flags, 1);
|
||||
}
|
||||
|
||||
int dns_client_remove_server(char *server_ip, int port, dns_server_type_t server_type)
|
||||
{
|
||||
return _dns_client_server_operate(server_ip, port, server_type, NULL, 1);
|
||||
return _dns_client_server_remove(server_ip, port, server_type);
|
||||
}
|
||||
|
||||
int dns_server_num(void)
|
||||
@@ -1399,7 +1472,7 @@ static int _dns_client_create_socket(struct dns_server_info *server_info)
|
||||
struct client_dns_server_flag_https *flag_https;
|
||||
flag_https = &server_info->flags.https;
|
||||
return _DNS_client_create_socket_tls(server_info, flag_https->hostname);
|
||||
}else {
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2378,10 +2451,114 @@ static void _dns_client_check_servers(void)
|
||||
pthread_mutex_unlock(&client.server_list_lock);
|
||||
}
|
||||
|
||||
static int _dns_client_pending_server_resolve(char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip, unsigned int ping_time, void *user_ptr)
|
||||
{
|
||||
struct dns_server_pending *pending = user_ptr;
|
||||
|
||||
if (addr_type == DNS_T_A) {
|
||||
pending->has_v4 = 1;
|
||||
pending->ping_time_v4 = -1;
|
||||
if (rtcode == DNS_RC_NOERROR) {
|
||||
pending->ping_time_v4 = ping_time;
|
||||
strncpy(pending->ipv4, ip, DNS_HOSTNAME_LEN);
|
||||
}
|
||||
} else if (addr_type == DNS_T_AAAA) {
|
||||
pending->has_v6 = 1;
|
||||
pending->ping_time_v6 = -1;
|
||||
if (rtcode == DNS_RC_NOERROR) {
|
||||
pending->ping_time_v6 = ping_time;
|
||||
strncpy(pending->ipv6, ip, DNS_HOSTNAME_LEN);
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dns_client_add_pendings(struct dns_server_pending *pending, char *ip)
|
||||
{
|
||||
struct dns_server_pending_group *group, *tmp;
|
||||
|
||||
if (_dns_client_add_server_pending(ip, pending->host, pending->port, pending->type, &pending->flags, 0) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(group, tmp, &pending->group_list, list)
|
||||
{
|
||||
if (_dns_client_add_to_group_pending(group->group_name, ip, pending->port, pending->type, 0) != 0) {
|
||||
tlog(TLOG_WARN, "add server to group failed, skip add.");
|
||||
}
|
||||
|
||||
list_del_init(&group->list);
|
||||
free(group);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _dns_client_add_pending_servers(void)
|
||||
{
|
||||
struct dns_server_pending *pending, *tmp;
|
||||
static int dely = 0;
|
||||
|
||||
/* add pending server after 3 seconds */
|
||||
if (++dely < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&pending_server_mutex);
|
||||
list_for_each_entry_safe(pending, tmp, &pending_servers, list)
|
||||
{
|
||||
/* send dns type A, AAAA query to bootstrap DNS server */
|
||||
if (pending->query_v4 == 0) {
|
||||
pending->query_v4 = 1;
|
||||
dns_server_query(pending->host, DNS_T_A, _dns_client_pending_server_resolve, pending);
|
||||
}
|
||||
|
||||
if (pending->query_v6 == 0) {
|
||||
pending->query_v6 = 1;
|
||||
dns_server_query(pending->host, DNS_T_AAAA, _dns_client_pending_server_resolve, pending);
|
||||
}
|
||||
|
||||
/* if both A, AAAA has query result, select fastest IP address */
|
||||
if (pending->has_v4 && pending->has_v6) {
|
||||
char *ip = NULL;
|
||||
if (pending->ping_time_v4 <= pending->ping_time_v6 && pending->ipv4[0]) {
|
||||
ip = pending->ipv4;
|
||||
} else {
|
||||
ip = pending->ipv6;
|
||||
}
|
||||
|
||||
if (ip[0]) {
|
||||
if (_dns_client_add_pendings(pending, ip) != 0) {
|
||||
tlog(TLOG_WARN, "add pending DNS server %s failed.", pending->host);
|
||||
}
|
||||
}
|
||||
list_del_init(&pending->list);
|
||||
free(pending);
|
||||
}
|
||||
|
||||
/* if has no bootstrap DNS, just call getaddrinfo to get address */
|
||||
if (dns_client_has_bootstrap_dns == 0) {
|
||||
if (_dns_client_add_pendings(pending, pending->host) != 0) {
|
||||
tlog(TLOG_ERROR, "add pending DNS server %s failed", pending->host);
|
||||
exit(1);
|
||||
pthread_mutex_unlock(&pending_server_mutex);
|
||||
return;
|
||||
}
|
||||
list_del_init(&pending->list);
|
||||
free(pending);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&pending_server_mutex);
|
||||
}
|
||||
|
||||
static void _dns_client_period_run_second(void)
|
||||
{
|
||||
_dns_client_check_tcp();
|
||||
_dns_client_check_servers();
|
||||
_dns_client_add_pending_servers();
|
||||
}
|
||||
|
||||
static void _dns_client_period_run(void)
|
||||
|
||||
@@ -168,7 +168,10 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
|
||||
/* clang-format off */
|
||||
static struct option long_options[] = {
|
||||
{"blacklist-ip", no_argument, NULL, 'b'}, /* filtering with blacklist-ip */
|
||||
#ifdef FEATURE_CHECK_EDNS
|
||||
/* experimental feature */
|
||||
{"check-edns", no_argument, NULL, 'e'}, /* check edns */
|
||||
#endif
|
||||
{"spki-pin", required_argument, NULL, 'p'}, /* check SPKI pin */
|
||||
{"host-name", required_argument, NULL, 'h'}, /* host name */
|
||||
{"http-host", required_argument, NULL, 'H'}, /* http host */
|
||||
|
||||
@@ -528,6 +528,7 @@ static int _dns_setup_ipset(struct dns_request *request)
|
||||
static int _dns_result_callback(struct dns_request *request)
|
||||
{
|
||||
char ip[DNS_MAX_CNAME_LEN];
|
||||
unsigned int ping_time = -1;
|
||||
|
||||
if (request->result_callback == NULL) {
|
||||
return 0;
|
||||
@@ -540,8 +541,8 @@ static int _dns_result_callback(struct dns_request *request)
|
||||
}
|
||||
|
||||
sprintf(ip, "%d.%d.%d.%d", request->ipv4_addr[0], request->ipv4_addr[1], request->ipv4_addr[2], request->ipv4_addr[3]);
|
||||
|
||||
return request->result_callback(request->domain, request->rcode, request->qtype, ip, request->user_ptr);
|
||||
ping_time = request->ping_ttl_v4;
|
||||
return request->result_callback(request->domain, request->rcode, request->qtype, ip, ping_time, request->user_ptr);
|
||||
} else if (request->qtype == DNS_T_AAAA) {
|
||||
if (request->has_ipv6 == 0) {
|
||||
goto out;
|
||||
@@ -551,16 +552,16 @@ static int _dns_result_callback(struct dns_request *request)
|
||||
request->ipv6_addr[2], request->ipv6_addr[3], request->ipv6_addr[4], request->ipv6_addr[5], request->ipv6_addr[6], request->ipv6_addr[7],
|
||||
request->ipv6_addr[8], request->ipv6_addr[9], request->ipv6_addr[10], request->ipv6_addr[11], request->ipv6_addr[12], request->ipv6_addr[13],
|
||||
request->ipv6_addr[14], request->ipv6_addr[15]);
|
||||
|
||||
return request->result_callback(request->domain, request->rcode, request->qtype, ip, request->user_ptr);
|
||||
ping_time = request->ping_ttl_v6;
|
||||
return request->result_callback(request->domain, request->rcode, request->qtype, ip, ping_time, request->user_ptr);
|
||||
}
|
||||
|
||||
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, request->user_ptr);
|
||||
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, ping_time, request->user_ptr);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
|
||||
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, request->user_ptr);
|
||||
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, ping_time, request->user_ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ void dns_server_stop(void);
|
||||
void dns_server_exit(void);
|
||||
|
||||
/* query result notify function */
|
||||
typedef int (*dns_result_callback)(char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip, void *user_ptr);
|
||||
typedef int (*dns_result_callback)(char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip, unsigned int ping_time, void *user_ptr);
|
||||
|
||||
/* query domain */
|
||||
int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr);
|
||||
|
||||
92
src/util.c
92
src/util.c
@@ -193,6 +193,98 @@ int parse_ip(const char *value, char *ip, int *port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _check_is_ipv4(const char *ip)
|
||||
{
|
||||
const char *ptr = ip;
|
||||
char c = 0;
|
||||
int dot_num = 0;
|
||||
int dig_num = 0;
|
||||
|
||||
while ( (c = *ptr++) != '\0') {
|
||||
if (c == '.') {
|
||||
dot_num++;
|
||||
dig_num = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check number count of one field */
|
||||
if (dig_num >= 4) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
dig_num++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check field number */
|
||||
if (dot_num != 3) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int _check_is_ipv6(const char *ip)
|
||||
{
|
||||
const char *ptr = ip;
|
||||
char c = 0;
|
||||
int colon_num = 0;
|
||||
int dig_num = 0;
|
||||
|
||||
while ( (c = *ptr++) != '\0') {
|
||||
if (c == '[' || c == ']') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ':') {
|
||||
colon_num++;
|
||||
dig_num = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check number count of one field */
|
||||
if (dig_num >= 5) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dig_num++;
|
||||
if (c >= '0' && c <= '9') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check field number */
|
||||
if (colon_num > 7) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int check_is_ipaddr(const char *ip)
|
||||
{
|
||||
if (strstr(ip, ".")) {
|
||||
/* IPV4 */
|
||||
return _check_is_ipv4(ip);
|
||||
} else if (strstr(ip, ":")) {
|
||||
/* IPV6 */
|
||||
return _check_is_ipv6(ip);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parse_uri(char *value, char *scheme, char *host, int *port, char *path)
|
||||
{
|
||||
char *scheme_end = NULL;
|
||||
|
||||
@@ -16,6 +16,8 @@ int getaddr_by_host(char *host, struct sockaddr *addr, socklen_t *addr_len);
|
||||
|
||||
int parse_ip(const char *value, char *ip, int *port);
|
||||
|
||||
int check_is_ipaddr(const char *ip);
|
||||
|
||||
int parse_uri(char *value, char *scheme, char *host, int *port, char *path);
|
||||
|
||||
int set_fd_nonblock(int fd, int nonblock);
|
||||
|
||||
Reference in New Issue
Block a user