diff --git a/ReadMe.md b/ReadMe.md
index f8d39b1..44bb1f7 100755
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -446,10 +446,11 @@ 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|[ip][:port] [-blacklist-ip][-check-edns], Repeatable, blacklist-ip parameter represents filtering the result of IPs with blacklist-ip configuration.| server 8.8.8.8:53 -blacklist-ip -check-edns
-|server-tcp|Upstream TCP DNS server|None|[IP][:port] [-blacklist-ip][-check-edns], Repeatable, blacklist-ip parameter represents filtering the result of IPs with blacklist-ip configuration.| server-tcp 8.8.8.8:53
-|server-tls|Upstream TLS DNS server|None|[IP][:port] [-blacklist-ip][-check-edns], Repeatable, blacklist-ip parameter represents filtering the result of IPs with blacklist-ip configuration.| server-tls 8.8.8.8:853
+|server|Upstream UDP DNS server|None|Repeatable
[ip][:port]: Server IP, port optional.
[-blacklist-ip]: The `-blacklist-ip` parameter is to filtering IPs which is configured by `blacklist-ip`.
[-check-edns]: edns filter.
[-group [group] ...]: The group to which the DNS server belongs, such as office, foreign, use with nameserver.
[-exclude-default-group]: Exclude DNS servers from the default group| server 8.8.8.8:53 -blacklist-ip -check-edns
+|server-tcp|Upstream TCP DNS server|None|Repeatable
[ip][:port]: Server IP, port optional.
[-blacklist-ip]: The `-blacklist-ip` parameter is to filtering IPs which is configured by `blacklist-ip`.
[-group [group] ...]: The group to which the DNS server belongs, such as office, foreign, use with nameserver.
[-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
[ip][:port]: Server IP, port optional.
[-blacklist-ip]: The `-blacklist-ip` parameter is to filtering IPs which is configured by `blacklist-ip`.
[-group [group] ...]: The group to which the DNS server belongs, such as office, foreign, use with nameserver.
[-exclude-default-group]: Exclude DNS servers from the default group| server-tls 8.8.8.8:853
|address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4
+|nameserver|To query domain with specific server group|None|nameserver /domain/[group\|-], `group` is the group name, `-` means ignore this rule, use the `-group` parameter in the related server|nameserver /www.example.com/office
|ipset|Domain IPSet|None|ipset /domain/[ipset\|-], `-` for ignore|ipset /www.example.com/pass
|ipset-timeout|ipset timeout enable|auto|[yes]|ipset-timeout yes
|bogus-nxdomain|bogus IP address|None|[IP/subnet], Repeatable| bogus-nxdomain 1.2.3.4/16
diff --git a/ReadMe_zh-CN.md b/ReadMe_zh-CN.md
index 2862539..c690228 100644
--- a/ReadMe_zh-CN.md
+++ b/ReadMe_zh-CN.md
@@ -447,10 +447,11 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
|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|无|[ip][:port] [-blacklist-ip][-check-edns],可重复,blacklist-ip参数指定使用blacklist-ip配置IP过滤结果| server 8.8.8.8:53 -blacklist-ip -check-edns
-|server-tcp|上游TCP DNS|无|[IP][:port] [-blacklist-ip][-check-edns],可重复,blacklist-ip参数指定使用blacklist-ip配置IP过滤结果| server-tcp 8.8.8.8:53
-|server-tls|上游TLS DNS|无|[IP][:port] [-blacklist-ip][-check-edns],可重复,blacklist-ip参数指定使用blacklist-ip配置IP过滤结果| server-tls 8.8.8.8:853
-|address|指定域名IP地址|无|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-`表示忽略, `#`表示返回SOA, `4`表示IPV4, `6`表示IPV6| address /www.example.com/1.2.3.4
+|server|上游UDP DNS|无|可重复
[ip][:port]:服务器IP,端口可选。
[-blacklist-ip]:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。
[-check-edns]:edns过滤。
[-group [group] ...]:DNS服务器所属组,比如office, foreign,和nameserver配套使用。
[-exclude-default-group]:将DNS服务器从默认组中排除| server 8.8.8.8:53 -blacklist-ip -check-edns -group g1
+|server-tcp|上游TCP DNS|无|可重复
[ip][:port]:服务器IP,端口可选。
[-blacklist-ip]:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。
[-group [group] ...]:DNS服务器所属组,比如office, foreign,和nameserver配套使用。
[-exclude-default-group]:将DNS服务器从默认组中排除| server-tcp 8.8.8.8:53
+|server-tls|上游TLS DNS|无|可重复
[ip][:port]:服务器IP,端口可选。
[-blacklist-ip]:blacklist-ip参数指定使用blacklist-ip配置IP过滤结果。
[-group [group] ...]:DNS服务器所属组,比如office, foreign,和nameserver配套使用。
[-exclude-default-group]:将DNS服务器从默认组中排除| server-tls 8.8.8.8:853
+|address|指定域名IP地址|无|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6]
`-`表示忽略
`#`表示返回SOA
`4`表示IPV4
`6`表示IPV6| address /www.example.com/1.2.3.4
+|nameserver|指定域名使用server组解析|无|nameserver /domain/[group\|-], `group`为组名,`-`表示忽略此规则,配套server中的`-group`参数使用| nameserver /www.example.com/office
|ipset|域名IPSET|None|ipset /domain/[ipset\|-], `-`表示忽略|ipset /www.example.com/pass
|ipset-timeout|设置IPSET超时功能启用|auto|[yes]|ipset-timeout yes
|bogus-nxdomain|假冒IP地址过滤|无|[ip/subnet],可重复| bogus-nxdomain 1.2.3.4/16
diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf
index 3363e03..bc2a1cf 100644
--- a/etc/smartdns/smartdns.conf
+++ b/etc/smartdns/smartdns.conf
@@ -79,23 +79,30 @@ log-level info
# audit-num 2
# remote udp dns server list
-# server [IP]:[PORT] [-blacklist-ip] [-check-edns]
+# server [IP]:[PORT] [-blacklist-ip] [-check-edns] [-group [group] ...] [-exclude-default-group]
# default port is 53
# -blacklist-ip: filter result with blacklist ip
# -check-edns: result must exist edns RR, or discard result.
-# server 8.8.8.8 -blacklist-ip -check-edns
+# -group [group]: set server to group, use with nameserver /domain/group.
+# -exclude-default-group: exclude this server from default group.
+# server 8.8.8.8 -blacklist-ip -check-edns -group g1 -group g2
# remote tcp dns server list
-# server-tcp [IP]:[PORT] [-blacklist-ip]
+# server-tcp [IP]:[PORT] [-blacklist-ip] [-group [group] ...] [-exclude-default-group]
# default port is 53
# server-tcp 8.8.8.8
# remote tls dns server list
-# server-tls [IP]:[PORT] [-blacklist-ip] [-check-edns]
+# server-tls [IP]:[PORT] [-blacklist-ip] [-group [group] ...] [-exclude-default-group]
# default port is 853
# server-tls 8.8.8.8
# server-tls 1.0.0.1
+# specific nameserver to domain
+# nameserver /domain/[group|-]
+# nameserver /www.example.com/office, set nameserver with nameserver name of block
+# nameserver /www.example.com/-, ignore this domain
+
# specific address to domain
# address /domain/[ip|-|-4|-6|#|#4|#6]
# address /www.example.com/1.2.3.4, return ip 1.2.3.4 to client
diff --git a/src/dns_client.c b/src/dns_client.c
index f42f933..502a6f1 100644
--- a/src/dns_client.c
+++ b/src/dns_client.c
@@ -24,6 +24,7 @@
#include "list.h"
#include "tlog.h"
#include "util.h"
+#include "dns_conf.h"
#include
#include
#include
@@ -69,30 +70,6 @@ struct dns_client_ecs {
};
};
-/* dns client */
-struct dns_client {
- pthread_t tid;
- int run;
- int epoll_fd;
-
- /* dns server list */
- pthread_mutex_t server_list_lock;
- struct list_head dns_server_list;
-
- /* query list */
- pthread_mutex_t dns_request_lock;
- struct list_head dns_request_list;
- atomic_t dns_server_num;
-
- /* ECS */
- struct dns_client_ecs ecs_ipv4;
- struct dns_client_ecs ecs_ipv6;
-
- /* query doman hash table, key: sid + domain */
- pthread_mutex_t domain_map_lock;
- DECLARE_HASHTABLE(domain_map, 6);
-};
-
struct dns_server_buff {
unsigned char data[DNS_TCP_BUFFER];
unsigned short len;
@@ -143,6 +120,43 @@ struct dns_server_info {
};
};
+struct dns_server_group_member {
+ struct list_head list;
+ struct dns_server_info *server;
+};
+
+struct dns_server_group {
+ char group_name[DNS_GROUP_NAME_LEN];
+ struct hlist_node node;
+ struct list_head head;
+};
+
+/* dns client */
+struct dns_client {
+ pthread_t tid;
+ int run;
+ int epoll_fd;
+
+ /* dns server list */
+ pthread_mutex_t server_list_lock;
+ struct list_head dns_server_list;
+ struct dns_server_group *default_group;
+
+ /* query list */
+ pthread_mutex_t dns_request_lock;
+ struct list_head dns_request_list;
+ atomic_t dns_server_num;
+
+ /* ECS */
+ struct dns_client_ecs ecs_ipv4;
+ struct dns_client_ecs ecs_ipv6;
+
+ /* query doman hash table, key: sid + domain */
+ pthread_mutex_t domain_map_lock;
+ DECLARE_HASHTABLE(domain_map, 6);
+ DECLARE_HASHTABLE(group, 4);
+};
+
/* dns replied server info */
struct dns_query_replied {
struct hlist_node node;
@@ -156,13 +170,15 @@ struct dns_query_replied {
/* query struct */
struct dns_query_struct {
+ struct list_head dns_request_list;
atomic_t refcnt;
+ struct dns_server_group *server_group;
+
/* query id, hash key sid + domain*/
char domain[DNS_MAX_CNAME_LEN];
unsigned short sid;
struct hlist_node domain_node;
- struct list_head dns_request_list;
struct list_head period_list;
/* dns query type */
@@ -250,8 +266,276 @@ void _dns_client_server_update_ttl(struct ping_host_struct *ping_host, const cha
server_info->ttl = ttl;
}
+/* get server control block by ip and port, type */
+struct dns_server_info *_dns_client_get_server(char *server_ip, int port, dns_server_type_t server_type)
+{
+ 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_TLS:
+ case DNS_SERVER_TCP:
+ 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) {
+ continue;
+ }
+
+ if (server_info->type != server_type) {
+ continue;
+ }
+
+ if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
+ continue;
+ }
+
+ pthread_mutex_unlock(&client.server_list_lock);
+ server_info_return = server_info;
+ break;
+ }
+
+ pthread_mutex_unlock(&client.server_list_lock);
+
+ freeaddrinfo(gai);
+ return server_info_return;
+errout:
+ if (gai) {
+ freeaddrinfo(gai);
+ }
+ return NULL;
+}
+
+struct dns_server_group *_dns_client_get_group(const char *group_name)
+{
+ unsigned long key;
+ struct dns_server_group *group = NULL;
+ struct hlist_node *tmp = NULL;
+
+ if (group_name == NULL) {
+ return NULL;
+ }
+
+ key = hash_string(group_name);
+ hash_for_each_possible_safe(client.group, group, tmp, node, key)
+ {
+ if (strncmp(group->group_name, group_name, DNS_GROUP_NAME_LEN) != 0) {
+ continue;
+ }
+
+ return group;
+ }
+
+ return NULL;
+}
+
+int _dns_client_add_to_group(char *group_name, struct dns_server_info *server_info)
+{
+ struct dns_server_group *group = NULL;
+ struct dns_server_group_member *group_member = NULL;
+
+ group = _dns_client_get_group(group_name);
+ if (group == NULL) {
+ tlog(TLOG_ERROR, "group %s not exist.", group_name);
+ return -1;
+ }
+
+ group_member = malloc(sizeof(*group_member));
+ if (group_member == NULL) {
+ tlog(TLOG_ERROR, "malloc memory failed.");
+ goto errout;
+ }
+
+ memset(group_member, 0, sizeof(*group_member));
+ group_member->server = server_info;
+ list_add(&group_member->list, &group->head);
+
+ return 0;
+errout:
+ if (group_member) {
+ free(group_member);
+ }
+
+ return -1;
+}
+
+int dns_client_add_to_group(char *group_name, char *server_ip, int port, dns_server_type_t server_type)
+{
+ struct dns_server_info *server_info = NULL;
+
+ server_info = _dns_client_get_server(server_ip, port, server_type);
+ if (server_info == NULL) {
+ return -1;
+ }
+
+ return _dns_client_add_to_group(group_name, server_info);
+}
+
+int _dns_client_remove_member(struct dns_server_group_member *group_member)
+{
+ list_del_init(&group_member->list);
+ free(group_member);
+
+ return 0;
+}
+
+int _dns_client_remove_from_group(struct dns_server_group *group, struct dns_server_info *server_info)
+{
+ struct dns_server_group_member *group_member;
+ struct dns_server_group_member *tmp;
+
+ list_for_each_entry_safe(group_member, tmp, &group->head, list)
+ {
+ if (group_member->server != server_info) {
+ continue;
+ }
+
+ _dns_client_remove_member(group_member);
+ }
+
+ return 0;
+}
+
+int _dns_client_remove_server_from_groups(struct dns_server_info *server_info)
+{
+ struct dns_server_group *group;
+ struct hlist_node *tmp = NULL;
+ int i = 0;
+
+ hash_for_each_safe(client.group, i, tmp, group, node)
+ {
+ _dns_client_remove_from_group(group, server_info);
+ }
+
+ return 0;
+}
+
+int dns_client_remove_from_group(char *group_name, char *server_ip, int port, dns_server_type_t server_type)
+{
+ struct dns_server_info *server_info = NULL;
+ struct dns_server_group *group = NULL;
+
+ server_info = _dns_client_get_server(server_ip, port, server_type);
+ if (server_info == NULL) {
+ return -1;
+ }
+
+ group = _dns_client_get_group(group_name);
+ if (group == NULL) {
+ return -1;
+ }
+
+ return _dns_client_remove_from_group(group, server_info);
+}
+
+int dns_client_add_group(char *group_name)
+{
+ unsigned long key;
+ struct dns_server_group *group = NULL;
+
+ if (_dns_client_get_group(group_name) != NULL) {
+ tlog(TLOG_ERROR, "add group %s failed, group already exists", group_name);
+ return -1;
+ }
+
+ group = malloc(sizeof(*group));
+ if (group == NULL) {
+ goto errout;
+ }
+
+ memset(group, 0, sizeof(*group));
+ INIT_LIST_HEAD(&group->head);
+ strncpy(group->group_name, group_name, DNS_GROUP_NAME_LEN);
+ key = hash_string(group_name);
+ hash_add(client.group, &group->node, key);
+
+ return 0;
+errout:
+ if (group) {
+ free(group);
+ group = NULL;
+ }
+
+ return -1;
+}
+
+int _dns_client_remove_group(struct dns_server_group *group)
+{
+ struct dns_server_group_member *group_member;
+ struct dns_server_group_member *tmp;
+
+ list_for_each_entry_safe(group_member, tmp, &group->head, list)
+ {
+ _dns_client_remove_member(group_member);
+ }
+
+ hash_del(&group->node);
+ free(group);
+
+ return 0;
+}
+
+int dns_remove_group(char *group_name)
+{
+ unsigned long key;
+ struct dns_server_group *group = NULL;
+ struct hlist_node *tmp = NULL;
+
+ key = hash_string(group_name);
+ hash_for_each_possible_safe(client.group, group, tmp, node, key)
+ {
+ if (strncmp(group->group_name, group_name, DNS_GROUP_NAME_LEN) != 0) {
+ continue;
+ }
+
+ _dns_client_remove_group(group);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+void _dns_client_group_remove_all(void)
+{
+ struct dns_server_group *group;
+ struct hlist_node *tmp = NULL;
+ int i = 0;
+
+ hash_for_each_safe(client.group, i, tmp, group, node)
+ {
+ _dns_client_remove_group(group);
+ }
+}
+
/* add dns server information */
-int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, unsigned int result_flag, int ttl)
+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)
{
struct dns_server_info *server_info = NULL;
@@ -279,6 +563,13 @@ int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_typ
server_info->ttl = ttl;
server_info->ttl_range = 0;
+ if ((server_flag & SERVER_FLAG_EXCLUDE_DEFAULT) == 0) {
+ if (_dns_client_add_to_group(DNS_SERVER_GROUP_DEFAULT, server_info) != 0) {
+ tlog(TLOG_ERROR, "add server to default group failed.");
+ goto errout;
+ }
+ }
+
if (server_type == DNS_SERVER_TLS) {
server_info->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (server_info->ssl_ctx == NULL) {
@@ -406,6 +697,8 @@ int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_
list_del(&server_info->list);
_dns_client_server_close(server_info);
+ pthread_mutex_unlock(&client.server_list_lock);
+ _dns_client_remove_server_from_groups(server_info);
free(server_info);
atomic_dec(&client.dns_server_num);
return 0;
@@ -414,7 +707,7 @@ int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_
return -1;
}
-int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, int result_flag, int ttl, int operate)
+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, int operate)
{
char port_s[8];
int sock_type;
@@ -448,7 +741,7 @@ int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t serv
}
if (operate == 0) {
- ret = _dns_client_server_add(server_ip, gai, server_type, result_flag, ttl);
+ ret = _dns_client_server_add(server_ip, gai, server_type, server_flag, result_flag, ttl);
if (ret != 0) {
goto errout;
}
@@ -467,14 +760,14 @@ errout:
return -1;
}
-int dns_add_server(char *server_ip, int port, dns_server_type_t server_type, int result_flag, int ttl)
+int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, unsigned server_flag, unsigned int result_flag, int ttl)
{
- return _dns_client_server_operate(server_ip, port, server_type, result_flag, ttl, 0);
+ return _dns_client_server_operate(server_ip, port, server_type, server_flag, result_flag, ttl, 0);
}
-int dns_remove_server(char *server_ip, int port, dns_server_type_t server_type)
+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, 0, 0, 1);
+ return _dns_client_server_operate(server_ip, port, server_type, 0, 0, 0, 1);
}
int dns_server_num(void)
@@ -561,17 +854,20 @@ void _dns_client_query_remove_all(void)
return;
}
-void _dns_client_check_udp_nat(void)
+void _dns_client_check_udp_nat(struct dns_query_struct *query)
{
- struct dns_server_info *server_info;
+ struct dns_server_info *server_info = NULL;
+ struct dns_server_group_member *group_member = NULL;
+
/* For udp nat case.
* when router reconnect to internet, udp port may always marked as UNREPLIED.
* dns query will timeout, and cannot reconnect again,
* create a new socket to communicate.
*/
pthread_mutex_lock(&client.server_list_lock);
- list_for_each_entry(server_info, &client.dns_server_list, list)
+ list_for_each_entry(group_member, &query->server_group->head, list)
{
+ server_info = group_member->server;
if (server_info->type != DNS_SERVER_UDP) {
continue;
}
@@ -646,10 +942,10 @@ void _dns_client_period_run(void)
{
/* 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);
- _dns_client_check_udp_nat();
}
@@ -814,7 +1110,7 @@ static int _dns_client_create_socket_udp(struct dns_server_info *server_info)
fd = socket(server_info->ai_family, SOCK_DGRAM, 0);
if (fd < 0) {
- tlog(TLOG_ERROR, "create socket failed.");
+ tlog(TLOG_ERROR, "create socket failed, %s", strerror(errno));
goto errout;
}
@@ -1534,6 +1830,10 @@ static void *_dns_client_work(void *arg)
static int _dns_client_send_udp(struct dns_server_info *server_info, void *packet, int len)
{
int send_len = 0;
+ if (server_info->fd <= 0) {
+ return -1;
+ }
+
send_len = sendto(server_info->fd, packet, len, 0, (struct sockaddr *)&server_info->addr, server_info->ai_addrlen);
if (send_len != len) {
return -1;
@@ -1653,7 +1953,9 @@ static int _dns_client_send_tls(struct dns_server_info *server_info, void *packe
static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, int len)
{
- struct dns_server_info *server_info, *tmp;
+ struct dns_server_info *server_info = NULL;
+ struct dns_server_group_member *group_member = NULL;
+ struct dns_server_group_member *tmp = NULL;
int ret = 0;
int send_err = 0;
@@ -1661,8 +1963,9 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
/* send query to all dns servers */
pthread_mutex_lock(&client.server_list_lock);
- list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
+ list_for_each_entry_safe(group_member, tmp, &query->server_group->head, list)
{
+ server_info = group_member->server;
if (server_info->fd <= 0) {
ret = _dns_client_create_socket(server_info);
if (ret != 0) {
@@ -1781,7 +2084,7 @@ static int _dns_client_send_query(struct dns_query_struct *query, char *doamin)
return _dns_client_send_packet(query, inpacket, encode_len);
}
-int dns_client_query(char *domain, int qtype, dns_client_callback callback, void *user_ptr)
+int dns_client_query(char *domain, int qtype, dns_client_callback callback, void *user_ptr, const char *group_name)
{
struct dns_query_struct *query = NULL;
int ret = 0;
@@ -1804,6 +2107,13 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void
query->qtype = qtype;
query->send_tick = 0;
query->sid = atomic_inc_return(&dns_client_sid);
+ query->server_group = _dns_client_get_group(group_name);
+ if (query->server_group == NULL) {
+ query->server_group = client.default_group;
+ tlog(TLOG_DEBUG, "send query to group %s", DNS_SERVER_GROUP_DEFAULT);
+ } else {
+ tlog(TLOG_DEBUG, "send query to group %s", group_name);
+ }
_dns_client_query_get(query);
/* add query to hashtable */
@@ -1843,7 +2153,6 @@ errout:
int dns_client_set_ecs(char *ip, int subnet)
{
-
return 0;
}
@@ -1872,8 +2181,15 @@ int dns_client_init()
pthread_mutex_init(&client.domain_map_lock, 0);
hash_init(client.domain_map);
+ hash_init(client.group);
INIT_LIST_HEAD(&client.dns_request_list);
+ if (dns_client_add_group(DNS_SERVER_GROUP_DEFAULT) != 0) {
+ tlog(TLOG_ERROR, "add default server group failed.");
+ goto errout;
+ }
+
+ client.default_group = _dns_client_get_group(DNS_SERVER_GROUP_DEFAULT);
client.epoll_fd = epollfd;
client.run = 1;
@@ -1913,6 +2229,7 @@ void dns_client_exit()
/* free all resouces */
_dns_client_server_remove_all();
_dns_client_query_remove_all();
+ _dns_client_group_remove_all();
pthread_mutex_destroy(&client.server_list_lock);
pthread_mutex_destroy(&client.domain_map_lock);
diff --git a/src/dns_client.h b/src/dns_client.h
index 3b9112a..774cea4 100644
--- a/src/dns_client.h
+++ b/src/dns_client.h
@@ -2,6 +2,7 @@
#define _SMART_DNS_CLIENT_H
#include "dns.h"
+#define DNS_SERVER_GROUP_DEFAULT "default"
typedef enum {
DNS_SERVER_UDP,
@@ -30,15 +31,23 @@ typedef int (*dns_client_callback)(char *domain, dns_result_type rtype, unsigned
int inpacket_len, void *user_ptr);
/* query domain */
-int dns_client_query(char *domain, int qtype, dns_client_callback callback, void *user_ptr);
+int dns_client_query(char *domain, int qtype, dns_client_callback callback, void *user_ptr, const char *group_name);
void dns_client_exit(void);
/* add remote dns server */
-int dns_add_server(char *server_ip, int port, dns_server_type_t server_type, int result_flag, int ttl);
+int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, unsigned int server_flag, unsigned int result_flag, int ttl);
/* remove remote dns server */
-int dns_remove_server(char *server_ip, int port, dns_server_type_t server_type);
+int dns_client_remove_server(char *server_ip, int port, dns_server_type_t server_type);
+
+int dns_client_add_group(char *group_name);
+
+int dns_client_add_to_group(char *group_name, char *server_ip, int port, dns_server_type_t server_type);
+
+int dns_client_remove_from_group(char *group_name, char *server_ip, int port, dns_server_type_t server_type);
+
+int dns_client_remove_group(char *group_name);
int dns_server_num(void);
diff --git a/src/dns_conf.c b/src/dns_conf.c
index 92428f9..d3ae495 100644
--- a/src/dns_conf.c
+++ b/src/dns_conf.c
@@ -16,6 +16,7 @@ struct dns_ipset_table {
DECLARE_HASHTABLE(ipset, 8);
};
struct dns_ipset_table dns_ipset_table;
+struct dns_group_table dns_group_table;
char dns_conf_server_ip[DNS_MAX_IPLEN];
char dns_conf_server_tcp_ip[DNS_MAX_IPLEN];
@@ -50,6 +51,88 @@ int dns_conf_ipset_timeout_enable;
struct dns_edns_client_subnet dns_conf_ipv4_ecs;
struct dns_edns_client_subnet dns_conf_ipv6_ecs;
+struct dns_server_groups *dns_conf_get_group(const char *group_name)
+{
+ uint32_t key = 0;
+ struct dns_server_groups *group = NULL;
+
+ key = hash_string(group_name);
+ hash_for_each_possible(dns_group_table.group, group, node, key)
+ {
+ if (strncmp(group->group_name, group_name, DNS_MAX_IPLEN) == 0) {
+ return group;
+ }
+ }
+
+ group = malloc(sizeof(*group));
+ if (group == NULL) {
+ goto errout;
+ }
+
+ memset(group, 0, sizeof(*group));
+ strncpy(group->group_name, group_name, DNS_GROUP_NAME_LEN);
+ hash_add(dns_group_table.group, &group->node, key);
+
+ return group;
+errout:
+ if (group) {
+ free(group);
+ }
+
+ return NULL;
+}
+
+int dns_conf_get_group_set(const char *group_name, struct dns_servers *server)
+{
+ struct dns_server_groups *group = NULL;
+ int i = 0;
+
+ group = dns_conf_get_group(group_name);
+ if (group == NULL) {
+ return -1;
+ }
+
+ for (i = 0; i < group->server_num; i++) {
+ if (group->servers[i] == server) {
+ return 0;
+ }
+ }
+
+ if (group->server_num >= DNS_MAX_SERVERS) {
+ return -1;
+ }
+
+ group->servers[group->server_num] = server;
+ group->server_num++;
+
+ return 0;
+}
+
+const char *dns_conf_get_group_name(const char *group_name)
+{
+ struct dns_server_groups *group = NULL;
+
+ group = dns_conf_get_group(group_name);
+ if (group == NULL) {
+ return NULL;
+ }
+
+ return group->group_name;
+}
+
+void config_group_table_destroy(void)
+{
+ struct dns_server_groups *group = NULL;
+ struct hlist_node *tmp = NULL;
+ int i;
+
+ hash_for_each_safe(dns_group_table.group, i, tmp, group, node)
+ {
+ hlist_del_init(&group->node);
+ free(group);
+ }
+}
+
int config_server(int argc, char *argv[], dns_server_type_t type, int default_port)
{
int index = dns_conf_server_num;
@@ -57,13 +140,16 @@ int config_server(int argc, char *argv[], dns_server_type_t type, int default_po
int port = -1;
char *ip = NULL;
int opt = 0;
- int result_flag = 0;
+ unsigned int result_flag = 0;
+ unsigned int server_flag = 0;
int ttl = 0;
/* clang-format off */
static struct option long_options[] = {
{"blacklist-ip", 0, 0, 'b'},
{"check-edns", 0, 0, 'e'},
{"check-ttl", required_argument, 0, 't'},
+ {"group", required_argument, 0, 'g'},
+ {"exclude-default-group", 0, 0, 'E'},
{0, 0, 0, 0}
};
/* clang-format on */
@@ -72,7 +158,24 @@ int config_server(int argc, char *argv[], dns_server_type_t type, int default_po
return -1;
}
+ if (index >= DNS_MAX_SERVERS) {
+ tlog(TLOG_WARN, "exceeds max server number, %s", ip);
+ return 0;
+ }
+
+ server = &dns_conf_servers[index];
ip = argv[1];
+
+ /* parse ip, port from ip */
+ if (parse_ip(ip, server->server, &port) != 0) {
+ return -1;
+ }
+
+ /* if port is not defined, set port to default 53 */
+ if (port == PORT_NOT_DEFINED) {
+ port = default_port;
+ }
+
optind = 1;
while (1) {
opt = getopt_long_only(argc, argv, "", long_options, NULL);
@@ -100,29 +203,28 @@ int config_server(int argc, char *argv[], dns_server_type_t type, int default_po
return -1;
}
result_flag |= DNSSERVER_FLAG_CHECK_TTL;
+ break;
}
+ case 'E': {
+ server_flag |= SERVER_FLAG_EXCLUDE_DEFAULT;
+ break;
+ }
+ case 'g': {
+ if (dns_conf_get_group_set(optarg, server) != 0) {
+ tlog(TLOG_ERROR, "add group failed.");
+ return -1;
+ }
+ break;
+ }
+ default:
+ break;
}
- }
-
- if (index >= DNS_MAX_SERVERS) {
- tlog(TLOG_WARN, "exceeds max server number, %s", ip);
- return 0;
- }
-
- server = &dns_conf_servers[index];
- /* parse ip, port from ip */
- if (parse_ip(ip, server->server, &port) != 0) {
- return -1;
- }
-
- /* if port is not defined, set port to default 53 */
- if (port == PORT_NOT_DEFINED) {
- port = default_port;
}
server->type = type;
server->port = port;
server->result_flag = result_flag;
+ server->server_flag = server_flag;
server->ttl = ttl;
dns_conf_server_num++;
tlog(TLOG_DEBUG, "add server %s, flag: %X, ttl: %d", ip, result_flag, ttl);
@@ -140,6 +242,10 @@ int config_domain_iter_cb(void *data, const unsigned char *key, uint32_t key_len
}
for (i = 0; i < DOMAIN_RULE_MAX; i++) {
+ if (domain_rule->rules[i] == NULL) {
+ continue;
+ }
+
free(domain_rule->rules[i]);
}
@@ -541,6 +647,81 @@ int config_server_tls(void *data, int argc, char *argv[])
return config_server(argc, argv, DNS_SERVER_TLS, DEFAULT_DNS_TLS_PORT);
}
+int config_nameserver(void *data, int argc, char *argv[])
+{
+ struct dns_nameserver_rule *nameserver_rule = NULL;
+ char domain[DNS_MAX_CONF_CNAME_LEN];
+ char group_name[DNS_GROUP_NAME_LEN];
+ const char *group = NULL;
+ char *begin = NULL;
+ char *end = NULL;
+ int len = 0;
+ char *value = argv[1];
+
+ if (argc <= 1) {
+ goto errout;
+ }
+
+ begin = strstr(value, "/");
+ if (begin == NULL) {
+ goto errout;
+ }
+
+ begin++;
+ end = strstr(begin, "/");
+ if (end == NULL) {
+ goto errout;
+ }
+
+ /* remove prefix . */
+ while (*begin == '.') {
+ begin++;
+ }
+
+ len = end - begin;
+ memcpy(domain, begin, len);
+ domain[len] = '\0';
+
+ len = strlen(end + 1);
+ if (len <= 0) {
+ goto errout;
+ }
+
+ if (strncmp(end + 1, "-", sizeof("-")) != 0) {
+ strncpy(group_name, end + 1, DNS_GROUP_NAME_LEN);
+ group = dns_conf_get_group_name(group_name);
+ if (group == NULL) {
+ goto errout;
+ }
+
+ nameserver_rule = malloc(sizeof(*nameserver_rule));
+ if (nameserver_rule == NULL) {
+ goto errout;
+ }
+
+ nameserver_rule->group_name = group;
+ } else {
+ if (config_domain_rule_flag_set(domain, DOMAIN_FLAG_NAMESERVER_IGNORE) != 0 ) {
+ goto errout;
+ }
+
+ return 0;
+ }
+
+ if (config_domain_rule_add(domain, DOMAIN_RULE_NAMESERVER, nameserver_rule) != 0) {
+ goto errout;
+ }
+
+ return 0;
+errout:
+ if (nameserver_rule) {
+ free(nameserver_rule);
+ }
+
+ tlog(TLOG_ERROR, "add nameserver %s failed", value);
+ return 0;
+}
+
radix_node_t *create_addr_node(char *addr)
{
radix_node_t *node;
@@ -707,6 +888,7 @@ struct config_item config_item[] = {
CONF_CUSTOM("server", config_server_udp, NULL),
CONF_CUSTOM("server-tcp", config_server_tcp, NULL),
CONF_CUSTOM("server-tls", config_server_tls, NULL),
+ CONF_CUSTOM("nameserver", config_nameserver, NULL),
CONF_CUSTOM("address", config_address, NULL),
CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable),
CONF_CUSTOM("ipset", config_ipset, NULL),
@@ -774,6 +956,7 @@ int _dns_server_load_conf_init(void)
art_tree_init(&dns_conf_domain_rule);
hash_init(dns_ipset_table.ipset);
+ hash_init(dns_group_table.group);
return 0;
}
@@ -784,6 +967,7 @@ void dns_server_load_exit(void)
Destroy_Radix(dns_conf_address_rule.ipv4, config_address_destroy, NULL);
Destroy_Radix(dns_conf_address_rule.ipv6, config_address_destroy, NULL);
config_ipset_table_destroy();
+ config_group_table_destroy();
}
int dns_server_load_conf(const char *file)
diff --git a/src/dns_conf.h b/src/dns_conf.h
index 19be534..588ffaa 100644
--- a/src/dns_conf.h
+++ b/src/dns_conf.h
@@ -12,6 +12,8 @@
#define DNS_MAX_SERVERS 64
#define DNS_MAX_IPSET_NAMELEN 32
+#define DNS_GROUP_NAME_LEN 32
+#define DNS_NAX_GROUP_NUMBER 16
#define DNS_MAX_IPLEN 64
#define DNS_MAX_PATH 1024
#define DEFAULT_DNS_PORT 53
@@ -26,6 +28,7 @@ enum domain_rule {
DOMAIN_RULE_ADDRESS_IPV4,
DOMAIN_RULE_ADDRESS_IPV6,
DOMAIN_RULE_IPSET,
+ DOMAIN_RULE_NAMESERVER,
DOMAIN_RULE_MAX,
};
@@ -36,6 +39,9 @@ enum domain_rule {
#define DOMAIN_FLAG_ADDR_IPV4_IGN (1 << 4)
#define DOMAIN_FLAG_ADDR_IPV6_IGN (1 << 5)
#define DOMAIN_FLAG_IPSET_IGNORE (1 << 6)
+#define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 7)
+
+#define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0)
struct dns_rule_flags {
unsigned int flags;
@@ -62,10 +68,27 @@ struct dns_domain_rule {
void *rules[DOMAIN_RULE_MAX];
};
+struct dns_nameserver_rule {
+ const char *group_name;
+};
+
+struct dns_server_groups {
+ struct hlist_node node;
+ char group_name[DNS_GROUP_NAME_LEN];
+ int server_num;
+ struct dns_servers *servers[DNS_MAX_SERVERS];
+};
+
+struct dns_group_table {
+ DECLARE_HASHTABLE(group, 8);
+};
+extern struct dns_group_table dns_group_table;
+
struct dns_servers {
char server[DNS_MAX_IPLEN];
unsigned short port;
unsigned int result_flag;
+ unsigned int server_flag;
int ttl;
dns_server_type_t type;
};
@@ -117,6 +140,9 @@ extern char dns_conf_log_file[DNS_MAX_PATH];
extern size_t dns_conf_log_size;
extern int dns_conf_log_num;
+extern struct dns_server_groups dns_conf_server_groups[DNS_NAX_GROUP_NUMBER];
+extern int dns_conf_server_group_num;
+
extern int dns_conf_audit_enable;
extern char dns_conf_audit_file[DNS_MAX_PATH];
extern size_t dns_conf_audit_size;
diff --git a/src/dns_server.c b/src/dns_server.c
index 95f9485..e1adc31 100644
--- a/src/dns_server.c
+++ b/src/dns_server.c
@@ -1436,6 +1436,7 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac
struct dns_packet *packet = (struct dns_packet *)packet_buff;
struct dns_request *request = NULL;
struct dns_rrs *rrs;
+ const char *group_name = NULL;
int rr_count = 0;
int i = 0;
int qclass;
@@ -1535,6 +1536,13 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac
goto clean_exit;
}
+ if (request->domain_rule) {
+ if (request->domain_rule->rules[DOMAIN_RULE_NAMESERVER]) {
+ struct dns_nameserver_rule *nameserver_rule = request->domain_rule->rules[DOMAIN_RULE_NAMESERVER];
+ group_name = nameserver_rule->group_name;
+ }
+ }
+
tlog(TLOG_INFO, "query server %s from %s, qtype = %d\n", request->domain, name, qtype);
_dns_server_request_get(request);
@@ -1548,14 +1556,14 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac
if (qtype == DNS_T_AAAA && dns_conf_dualstack_ip_selection) {
_dns_server_request_get(request);
request->request_wait++;
- if (dns_client_query(request->domain, DNS_T_A, dns_server_resolve_callback, request) != 0) {
+ if (dns_client_query(request->domain, DNS_T_A, dns_server_resolve_callback, request, group_name) != 0) {
_dns_server_request_release(request);
request->request_wait--;
}
}
request->request_wait++;
- if (dns_client_query(request->domain, qtype, dns_server_resolve_callback, request) != 0) {
+ if (dns_client_query(request->domain, qtype, dns_server_resolve_callback, request, group_name) != 0) {
_dns_server_request_release(request);
_dns_server_request_remove(request);
request = NULL;
@@ -1585,6 +1593,7 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
{
int ret = -1;
struct dns_request *request = NULL;
+ const char *group_name = NULL;
request = malloc(sizeof(*request));
if (request == NULL) {
@@ -1616,7 +1625,14 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype)
_dns_server_request_get(request);
request->send_tick = get_tick_count();
request->request_wait++;
- dns_client_query(request->domain, qtype, dns_server_resolve_callback, request);
+
+ if (request->domain_rule) {
+ if (request->domain_rule->rules[DOMAIN_RULE_NAMESERVER]) {
+ struct dns_nameserver_rule *nameserver_rule = request->domain_rule->rules[DOMAIN_RULE_NAMESERVER];
+ group_name = nameserver_rule->group_name;
+ }
+ }
+ dns_client_query(request->domain, qtype, dns_server_resolve_callback, request, group_name);
return 0;
errout:
diff --git a/src/smartdns.c b/src/smartdns.c
index 30ba26a..3366cba 100644
--- a/src/smartdns.c
+++ b/src/smartdns.c
@@ -123,9 +123,13 @@ int smartdns_load_from_resolv(void)
int smartdns_add_servers(void)
{
int i = 0;
+ int j = 0;
int ret = 0;
+ struct dns_server_groups *group = NULL;
+ struct dns_servers *server = NULL;
+
for (i = 0; i < dns_conf_server_num; i++) {
- ret = dns_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, dns_conf_servers[i].result_flag,
+ ret = dns_client_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, dns_conf_servers[i].server_flag, dns_conf_servers[i].result_flag,
dns_conf_servers[i].ttl);
if (ret != 0) {
tlog(TLOG_ERROR, "add server failed, %s:%d", dns_conf_servers[i].server, dns_conf_servers[i].port);
@@ -133,6 +137,28 @@ int smartdns_add_servers(void)
}
}
+ hash_for_each(dns_group_table.group, i, group, node)
+ {
+ ret = dns_client_add_group(group->group_name);
+ if (ret != 0) {
+ tlog(TLOG_ERROR, "add group failed, %s", group->group_name);
+ return -1;
+ }
+
+ for (j = 0; j < group->server_num; j++) {
+ server = group->servers[j];
+ if (server == NULL) {
+ continue;
+ }
+ ret = dns_client_add_to_group(group->group_name, server->server, server->port, server->type);
+ if (ret != 0) {
+ tlog(TLOG_ERROR, "add server %s to group %s failed", server->server, group->group_name);
+ return -1;
+ }
+ }
+ }
+
+
return 0;
}