From 4465ce798af38e1bb184f2dab3e15c8319cf8ee0 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Thu, 21 Feb 2019 01:21:36 +0800 Subject: [PATCH] Support nameserver feature --- ReadMe.md | 7 +- ReadMe_zh-CN.md | 9 +- etc/smartdns/smartdns.conf | 15 +- src/dns_client.c | 399 +++++++++++++++++++++++++++++++++---- src/dns_client.h | 15 +- src/dns_conf.c | 218 ++++++++++++++++++-- src/dns_conf.h | 26 +++ src/dns_server.c | 22 +- src/smartdns.c | 28 ++- 9 files changed, 663 insertions(+), 76 deletions(-) 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; }