diff --git a/ReadMe.md b/ReadMe.md index 5d4cfa4..8a8337e 100755 --- a/ReadMe.md +++ b/ReadMe.md @@ -391,8 +391,8 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |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 -|address|Domain IP address|None|address /domain/ip| address /www.example.com/1.2.3.4 -|ipset|Domain IPSet|None|ipset /domain/ipset|ipset /www.example.com/pass +|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 +|ipset|Domain IPSet|None|ipset /domain/[ipset\|-], `-` for ignore|ipset /www.example.com/pass |bogus-nxdomain|bogus IP address|None|[IP/subnet], Repeatable| bogus-nxdomain 1.2.3.4/16 |blacklist-ip|ip blacklist|None|[ip/subnet], Repeatable,When the filtering server responds IPs in the IP blacklist, The result will be discarded directly| blacklist-ip 1.2.3.4/16 |force-AAAA-SOA|force AAAA query return SOA|no|[yes\|no]|force-AAAA-SOA yes diff --git a/ReadMe_zh-CN.md b/ReadMe_zh-CN.md index ff9c401..1c0cc95 100644 --- a/ReadMe_zh-CN.md +++ b/ReadMe_zh-CN.md @@ -391,8 +391,8 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms |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| address /www.example.com/1.2.3.4 -|ipset|域名IPSET|None|ipset /domain/ipset|ipset /www.example.com/pass +|address|指定域名IP地址|无|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-`表示忽略, `#`表示返回SOA, `4`表示IPV4, `6`表示IPV6| address /www.example.com/1.2.3.4 +|ipset|域名IPSET|None|ipset /domain/[ipset\|-], `-`表示忽略|ipset /www.example.com/pass |bogus-nxdomain|假冒IP地址过滤|无|[ip/subnet],可重复| bogus-nxdomain 1.2.3.4/16 |blacklist-ip|黑名单IP地址|无|[ip/subnet],可重复| blacklist-ip 1.2.3.4/16 |force-AAAA-SOA|强制AAAA地址返回SOA|no|[yes\|no]|force-AAAA-SOA yes diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 7bf629e..6d1584c 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -94,9 +94,12 @@ log-level info # server-tls 1.0.0.1 # specific address to domain -# address /domain/ip -# address /www.example.com/1.2.3.4 +# address /domain/[ip|-|-4|-6|#|#4|#6] +# address /www.example.com/1.2.3.4, return ip 1.2.3.4 to client +# address /www.example.com/-, ignore address, query from upstream, suffix 4, for ipv4, 6 for ipv6, none for all +# address /www.example.com/#, return SOA to client, suffix 4, for ipv4, 6 for ipv6, none for all # specific ipset to domain -# ipset /domain/ipset -# ipset /www.example.com/block \ No newline at end of file +# ipset /domain/[ipset|-] +# ipset /www.example.com/block, set ipset with ipset name of block +# ipset /www.example.com/-, ignore this domain \ No newline at end of file diff --git a/package/openwrt/address.conf b/package/openwrt/address.conf index 2bf0e2d..0aecc94 100644 --- a/package/openwrt/address.conf +++ b/package/openwrt/address.conf @@ -1,3 +1,11 @@ # Add domains which you want to force to an IP address here. # The example below send any host in example.com to a local webserver. -# address /example.com/127.0.0.1 \ No newline at end of file +# address /domain/[ip|-|-4|-6|#|#4|#6] +# address /www.example.com/1.2.3.4, return ip 1.2.3.4 to client +# address /www.example.com/-, ignore address, query from upstream, suffix 4, for ipv4, 6 for ipv6, none for all +# address /www.example.com/#, return SOA to client, suffix 4, for ipv4, 6 for ipv6, none for all + +# specific ipset to domain +# ipset /domain/[ipset|-] +# ipset /www.example.com/block, set ipset with ipset name of block +# ipset /www.example.com/-, ignore this domain \ No newline at end of file diff --git a/src/dns_conf.c b/src/dns_conf.c index c5e60f6..d296d46 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -213,7 +213,58 @@ errout: free(add_domain_rule); } - tlog(TLOG_ERROR, "add doamin %s failed", domain); + tlog(TLOG_ERROR, "add doamin %s rule failed", domain); + return 0; +} + +int config_domain_rule_flag_set(char *domain, unsigned int flag) +{ + struct dns_domain_rule *domain_rule = NULL; + struct dns_domain_rule *old_domain_rule = NULL; + struct dns_domain_rule *add_domain_rule = NULL; + struct dns_rule_flags *rule_flags = NULL; + + char domain_key[DNS_MAX_CONF_CNAME_LEN]; + int len = 0; + + len = strlen(domain); + reverse_string(domain_key, domain, len); + domain_key[len] = '.'; + len++; + domain_key[len] = 0; + + domain_rule = art_search(&dns_conf_domain_rule, (unsigned char *)domain_key, len); + if (domain_rule == NULL) { + add_domain_rule = malloc(sizeof(*add_domain_rule)); + if (add_domain_rule == NULL) { + goto errout; + } + memset(add_domain_rule, 0, sizeof(*add_domain_rule)); + domain_rule = add_domain_rule; + } + + if (domain_rule->rules[DOMAIN_RULE_FLAGS] == NULL) { + rule_flags = malloc(sizeof(*rule_flags)); + rule_flags->flags = 0; + } + + domain_rule->rules[DOMAIN_RULE_FLAGS] = rule_flags; + rule_flags->flags |= flag; + + if (add_domain_rule) { + old_domain_rule = art_insert(&dns_conf_domain_rule, (unsigned char *)domain_key, len, add_domain_rule); + if (old_domain_rule) { + free(old_domain_rule); + } + } + + return 0; +errout: + if (add_domain_rule) { + free(add_domain_rule); + } + + tlog(TLOG_ERROR, "add doamin %s rule failed", domain); return 0; } @@ -301,18 +352,26 @@ int config_ipset(void *data, int argc, char *argv[]) goto errout; } - strncpy(ipsetname, end + 1, DNS_MAX_IPSET_NAMELEN); - ipset = dns_conf_get_ipset(ipsetname); - if (ipset == NULL) { - goto errout; - } + if (strncmp(end + 1, "-", sizeof("-")) != 0) { + strncpy(ipsetname, end + 1, DNS_MAX_IPSET_NAMELEN); + ipset = dns_conf_get_ipset(ipsetname); + if (ipset == NULL) { + goto errout; + } - ipset_rule = malloc(sizeof(*ipset_rule)); - if (ipset_rule == NULL) { - goto errout; - } + ipset_rule = malloc(sizeof(*ipset_rule)); + if (ipset_rule == NULL) { + goto errout; + } - ipset_rule->ipsetname = ipset; + ipset_rule->ipsetname = ipset; + } else { + if (config_domain_rule_flag_set(domain, DOMAIN_FLAG_IPSET_IGNORE) != 0 ) { + goto errout; + } + + return 0; + } if (config_domain_rule_add(domain, DOMAIN_RULE_IPSET, ipset_rule) != 0) { goto errout; @@ -343,6 +402,7 @@ int config_address(void *data, int argc, char *argv[]) struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); enum domain_rule type = 0; + unsigned int flag = 0; if (argc <= 1) { goto errout; @@ -368,51 +428,81 @@ int config_address(void *data, int argc, char *argv[]) memcpy(domain, begin, len); domain[len] = 0; - if (parse_ip(end + 1, ip, &port) != 0) { - goto errout; - } + if (strncmp(end + 1, "#", sizeof("#")) == 0) { + if (strncmp(end + 1, "#4", sizeof("#4")) == 0) { + flag = DOMAIN_FLAG_ADDR_IPV4_SOA; + } else if (strncmp(end + 1, "#6", sizeof("#6")) == 0) { + flag = DOMAIN_FLAG_ADDR_IPV6_SOA; + } else { + flag = DOMAIN_FLAG_ADDR_SOA; + } - if (getaddr_by_host(ip, (struct sockaddr *)&addr, &addr_len) != 0) { - goto errout; - } - - switch (addr.ss_family) { - case AF_INET: { - struct sockaddr_in *addr_in; - address_ipv4 = malloc(sizeof(*address_ipv4)); - if (address_ipv4 == NULL) { + if (config_domain_rule_flag_set(domain, flag) != 0 ) { goto errout; } - addr_in = (struct sockaddr_in *)&addr; - memcpy(address_ipv4->ipv4_addr, &addr_in->sin_addr.s_addr, 4); - type = DOMAIN_RULE_ADDRESS_IPV4; - address = address_ipv4; - } break; - case AF_INET6: { - struct sockaddr_in6 *addr_in6; - addr_in6 = (struct sockaddr_in6 *)&addr; - if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { + return 0; + } else if (strncmp(end + 1, "-", sizeof("-")) == 0) { + if (strncmp(end + 1, "-4", sizeof("-4")) == 0) { + flag = DOMAIN_FLAG_ADDR_IPV4_IGN; + } else if (strncmp(end + 1, "-6", sizeof("-6")) == 0) { + flag = DOMAIN_FLAG_ADDR_IPV6_IGN; + } else { + flag = DOMAIN_FLAG_ADDR_IGN; + } + + if (config_domain_rule_flag_set(domain, flag) != 0 ) { + goto errout; + } + + return 0; + } else { + if (parse_ip(end + 1, ip, &port) != 0) { + goto errout; + } + + if (getaddr_by_host(ip, (struct sockaddr *)&addr, &addr_len) != 0) { + goto errout; + } + + switch (addr.ss_family) { + case AF_INET: { + struct sockaddr_in *addr_in; address_ipv4 = malloc(sizeof(*address_ipv4)); if (address_ipv4 == NULL) { goto errout; } - memcpy(address_ipv4->ipv4_addr, addr_in6->sin6_addr.s6_addr + 12, 4); + + addr_in = (struct sockaddr_in *)&addr; + memcpy(address_ipv4->ipv4_addr, &addr_in->sin_addr.s_addr, 4); type = DOMAIN_RULE_ADDRESS_IPV4; address = address_ipv4; - } else { - address_ipv6 = malloc(sizeof(*address_ipv6)); - if (address_ipv6 == NULL) { - goto errout; + } break; + case AF_INET6: { + struct sockaddr_in6 *addr_in6; + addr_in6 = (struct sockaddr_in6 *)&addr; + if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { + address_ipv4 = malloc(sizeof(*address_ipv4)); + if (address_ipv4 == NULL) { + goto errout; + } + memcpy(address_ipv4->ipv4_addr, addr_in6->sin6_addr.s6_addr + 12, 4); + type = DOMAIN_RULE_ADDRESS_IPV4; + address = address_ipv4; + } else { + address_ipv6 = malloc(sizeof(*address_ipv6)); + if (address_ipv6 == NULL) { + goto errout; + } + memcpy(address_ipv6->ipv6_addr, addr_in6->sin6_addr.s6_addr, 16); + type = DOMAIN_RULE_ADDRESS_IPV6; + address = address_ipv6; } - memcpy(address_ipv6->ipv6_addr, addr_in6->sin6_addr.s6_addr, 16); - type = DOMAIN_RULE_ADDRESS_IPV6; - address = address_ipv6; + } break; + default: + goto errout; } - } break; - default: - goto errout; - } + } if (config_domain_rule_add(domain, type, address) != 0) { goto errout; diff --git a/src/dns_conf.h b/src/dns_conf.h index 4e06265..3b01144 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -22,12 +22,25 @@ #define SMARTDNS_AUDIT_FILE "/var/log/smartdns-audit.log" enum domain_rule { - DOMAIN_RULE_ADDRESS_IPV4 = 1, - DOMAIN_RULE_ADDRESS_IPV6 = 2, - DOMAIN_RULE_IPSET = 3, + DOMAIN_RULE_FLAGS = 0, + DOMAIN_RULE_ADDRESS_IPV4, + DOMAIN_RULE_ADDRESS_IPV6, + DOMAIN_RULE_IPSET, DOMAIN_RULE_MAX, }; +#define DOMAIN_FLAG_ADDR_SOA (1 << 0) +#define DOMAIN_FLAG_ADDR_IPV4_SOA (1 << 1) +#define DOMAIN_FLAG_ADDR_IPV6_SOA (1 << 2) +#define DOMAIN_FLAG_ADDR_IGN (1 << 3) +#define DOMAIN_FLAG_ADDR_IPV4_IGN (1 << 4) +#define DOMAIN_FLAG_ADDR_IPV6_IGN (1 << 5) +#define DOMAIN_FLAG_IPSET_IGNORE (1 << 6) + +struct dns_rule_flags { + unsigned int flags; +}; + struct dns_address_IPV4 { unsigned char ipv4_addr[DNS_RR_A_LEN]; }; diff --git a/src/dns_server.c b/src/dns_server.c index b247627..9082db3 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -464,12 +464,20 @@ static int _dns_server_reply_SOA(int rcode, struct dns_request *request, struct static int _dns_setup_ipset(struct dns_request *request) { struct dns_ipset_rule *ipset_rule = NULL; + struct dns_rule_flags *rule_flags = NULL; int ret = 0; if (request->domain_rule == NULL) { return 0; } + rule_flags = request->domain_rule->rules[DOMAIN_RULE_FLAGS]; + if (rule_flags) { + if (rule_flags->flags & DOMAIN_FLAG_IPSET_IGNORE) { + return 0; + } + } + ipset_rule = request->domain_rule->rules[DOMAIN_RULE_IPSET]; if (ipset_rule == NULL) { return 0; @@ -1196,6 +1204,59 @@ static struct dns_domain_rule *_dns_server_get_domain_rule(char *domain) return domain_rule; } +static int _dns_server_pre_process_rule_flags(struct dns_request *request, struct dns_packet *packet) +{ + struct dns_rule_flags *rule_flag = NULL; + unsigned int flags = 0; + if (request->domain_rule == NULL) { + goto errout; + } + + rule_flag = request->domain_rule->rules[DOMAIN_RULE_FLAGS]; + if (rule_flag == NULL) { + goto errout; + } + + flags = rule_flag->flags; + if (flags & DOMAIN_FLAG_ADDR_IGN) { + goto errout; + } + + if (flags & DOMAIN_FLAG_ADDR_SOA) { + _dns_server_reply_SOA(DNS_RC_NOERROR, request, packet); + return 0; + } + + switch (request->qtype) { + case DNS_T_A: + if (flags & DOMAIN_FLAG_ADDR_IPV4_IGN) { + goto errout; + } + + if (flags & DOMAIN_FLAG_ADDR_IPV4_SOA) { + _dns_server_reply_SOA(DNS_RC_NOERROR, request, packet); + return 0; + } + break; + case DNS_T_AAAA: + if (flags & DOMAIN_FLAG_ADDR_IPV6_IGN) { + goto errout; + } + + if (flags & DOMAIN_FLAG_ADDR_IPV6_SOA) { + _dns_server_reply_SOA(DNS_RC_NOERROR, request, packet); + return 0; + } + break; + default: + goto errout; + break; + } + +errout: + return -1; +} + static int _dns_server_process_address(struct dns_request *request, struct dns_packet *packet) { struct dns_address_IPV4 *address_ipv4 = NULL; @@ -1369,6 +1430,7 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac _dns_server_reply_SOA(DNS_RC_NOERROR, request, packet); goto clean_exit; } + break; default: tlog(TLOG_DEBUG, "unsupport qtype: %d, domain: %s", qtype, request->domain); @@ -1376,6 +1438,10 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac break; } + if (_dns_server_pre_process_rule_flags(request, packet) == 0) { + goto clean_exit; + } + if (_dns_server_process_address(request, packet) == 0) { goto clean_exit; } diff --git a/src/lib/art.c b/src/lib/art.c index f936e81..12851f8 100644 --- a/src/lib/art.c +++ b/src/lib/art.c @@ -1027,7 +1027,6 @@ void *art_substring(const art_tree *t, const unsigned char *str, int str_len, un // Check if the expanded path matches if (!str_prefix_matches((art_leaf*)n, str, str_len)) { found = (art_leaf*)n; - art_copy_key(found, key, key_len); } break; } @@ -1040,7 +1039,6 @@ void *art_substring(const art_tree *t, const unsigned char *str, int str_len, un // Check if the expanded path matches if (!str_prefix_matches((art_leaf*)m, str, str_len)) { found = (art_leaf*)m; - art_copy_key(found, key, key_len); } } @@ -1062,5 +1060,7 @@ void *art_substring(const art_tree *t, const unsigned char *str, int str_len, un return NULL; } + art_copy_key(found, key, key_len); + return found->value; }