diff --git a/.gitignore b/.gitignore index d9e7a1f..a45f1be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .vscode -.o +*.o .DS_Store -.swp. +*.swp. diff --git a/ReadMe.md b/ReadMe.md index 9fd1ca1..2a0ce26 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -526,7 +526,7 @@ https://github.com/pymumu/smartdns/releases |speed-check-mode|测速模式选择|无|[ping\|tcp:[80]\|none]|speed-check-mode ping,tcp:80 |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|域名IPSET|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-`表示忽略|ipset /www.example.com/#4:dns4,#6:- |ipset-timeout|设置IPSET超时功能启用|auto|[yes]|ipset-timeout yes |domain-rules|设置域名规则|无|domain-rules /domain/ [-rules...]
`[-c\|-speed-check-mode]`: 测速模式,参考`speed-check-mode`配置
`[-a\|-address]`: 参考`address`配置
`[-n\|-nameserver]`: 参考`nameserver`配置
`[-p\|-ipset]`:参考`ipset`配置
`[-d\|-dualstack-ip-selection]`: 参考`dualstack-ip-selection`|domain-rules /www.example.com/ -speed-check-mode none |bogus-nxdomain|假冒IP地址过滤|无|[ip/subnet],可重复| bogus-nxdomain 1.2.3.4/16 diff --git a/ReadMe_en.md b/ReadMe_en.md index 5d02375..5acb8c6 100755 --- a/ReadMe_en.md +++ b/ReadMe_en.md @@ -520,7 +520,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |speed-check-mode|Speed ​​mode|None|[ping\|tcp:[80]\|none]|speed-check-mode ping,tcp:443 |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|Domain IPSet|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-` for ignore|ipset /www.example.com/#4:dns4,#6:- |ipset-timeout|ipset timeout enable|auto|[yes]|ipset-timeout yes |domain-rules|set domain rules|None|domain-rules /domain/ [-rules...]
`[-c\|-speed-check-mode]`: set speed check mode,same as parameter `speed-check-mode`
`[-a\|-address]`: same as parameter `address`
`[-n\|-nameserver]`: same as parameter `nameserver`
`[-p\|-ipset]`: same as parameter `ipset`
`[-d\|-dualstack-ip-selection]`: same as parameter `dualstack-ip-selection`|domain-rules /www.example.com/ -speed-check-mode none |bogus-nxdomain|bogus IP address|None|[IP/subnet], Repeatable| bogus-nxdomain 1.2.3.4/16 diff --git a/src/dns_conf.c b/src/dns_conf.c index 11d8595..c59a763 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -595,11 +595,40 @@ static int _conf_domain_rule_ipset(char *domain, const char *ipsetname) { struct dns_ipset_rule *ipset_rule = NULL; const char *ipset = NULL; + char *copied_name = NULL; + enum domain_rule type; + int ignore_flag; + + copied_name = strdup(ipsetname); + + if (copied_name == NULL) { + goto errout; + } + + for (char *tok = strtok(copied_name, ","); tok; tok = strtok(NULL, ",")) { + if (tok[0] == '#') { + if (strncmp(tok, "#6:", 3u) == 0) { + type = DOMAIN_RULE_IPSET_IPV6; + ignore_flag = DOMAIN_FLAG_IPSET_IPV6_IGN; + } else if (strncmp(tok, "#4:", 3u) == 0) { + type = DOMAIN_RULE_IPSET_IPV4; + ignore_flag = DOMAIN_FLAG_IPSET_IPV4_IGN; + } else { + goto errout; + } + tok += 3; + } else { + type = DOMAIN_RULE_IPSET; + ignore_flag = DOMAIN_FLAG_IPSET_IGN; + } + + if (strncmp(tok, "-", 1) == 0) { + _config_domain_rule_flag_set(domain, ignore_flag, 0); + continue; + } - /* Process domain option */ - if (strncmp(ipsetname, "-", sizeof("-")) != 0) { /* new ipset domain */ - ipset = _dns_conf_get_ipset(ipsetname); + ipset = _dns_conf_get_ipset(tok); if (ipset == NULL) { goto errout; } @@ -610,26 +639,26 @@ static int _conf_domain_rule_ipset(char *domain, const char *ipsetname) } ipset_rule->ipsetname = ipset; - } else { - /* ignore this domain */ - if (_config_domain_rule_flag_set(domain, DOMAIN_FLAG_IPSET_IGNORE, 0) != 0) { + + if (_config_domain_rule_add(domain, type, ipset_rule) != 0) { goto errout; } - - return 0; } - if (_config_domain_rule_add(domain, DOMAIN_RULE_IPSET, ipset_rule) != 0) { - goto errout; - } + goto clear; - return 0; errout: + tlog(TLOG_ERROR, "add ipset %s failed", ipsetname); + if (ipset_rule) { free(ipset_rule); } - tlog(TLOG_ERROR, "add ipset %s failed", ipsetname); +clear: + if (copied_name) { + free(copied_name); + } + return 0; } diff --git a/src/dns_conf.h b/src/dns_conf.h index 78e6f19..b86dcda 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -56,6 +56,8 @@ enum domain_rule { DOMAIN_RULE_ADDRESS_IPV4, DOMAIN_RULE_ADDRESS_IPV6, DOMAIN_RULE_IPSET, + DOMAIN_RULE_IPSET_IPV4, + DOMAIN_RULE_IPSET_IPV6, DOMAIN_RULE_NAMESERVER, DOMAIN_RULE_CHECKSPEED, DOMAIN_RULE_MAX, @@ -78,9 +80,11 @@ typedef enum { #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) -#define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 7) -#define DOMAIN_FLAG_DUALSTACK_SELECT (1 << 8) +#define DOMAIN_FLAG_IPSET_IGN (1 << 6) +#define DOMAIN_FLAG_IPSET_IPV4_IGN (1 << 7) +#define DOMAIN_FLAG_IPSET_IPV6_IGN (1 << 8) +#define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 9) +#define DOMAIN_FLAG_DUALSTACK_SELECT (1 << 10) #define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0) diff --git a/src/dns_server.c b/src/dns_server.c index a2141d4..8c209df 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -681,7 +681,7 @@ static int _dns_server_reply_SOA(int rcode, struct dns_request *request) /* add ip to specific ipset */ static int _dns_setup_ipset(struct dns_request *request) { - struct dns_ipset_rule *ipset_rule = NULL; + struct dns_ipset_rule *rule = NULL, *ipset_rule = NULL, *ipset_rule_v4 = NULL, *ipset_rule_v6 = NULL; struct dns_rule_flags *rule_flags = NULL; int ret = 0; @@ -691,32 +691,56 @@ static int _dns_setup_ipset(struct dns_request *request) /* check ipset rule */ rule_flags = request->domain_rule.rules[DOMAIN_RULE_FLAGS]; - if (rule_flags) { - if (rule_flags->flags & DOMAIN_FLAG_IPSET_IGNORE) { - return 0; - } + if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IGN) != 0) { + ipset_rule = request->domain_rule.rules[DOMAIN_RULE_IPSET]; + } + if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV4_IGN) != 0) { + ipset_rule_v4 = request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV4]; + } + if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV6_IGN) != 0) { + ipset_rule_v6 = request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV6]; } - ipset_rule = request->domain_rule.rules[DOMAIN_RULE_IPSET]; - if (ipset_rule == NULL) { + if (!(ipset_rule || ipset_rule_v4 || ipset_rule_v6)) { return 0; } /* add IPV4 to ipset */ if (request->has_ipv4 && request->qtype == DNS_T_A) { - ret |= ipset_add(ipset_rule->ipsetname, request->ipv4_addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule; + if (rule) { + ret |= ipset_add(rule->ipsetname, request->ipv4_addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + tlog(TLOG_DEBUG, "IPSET-MATCH: domain:%s, ipset:%s, IP: %d.%d.%d.%d, result: %d", request->domain, + rule->ipsetname, request->ipv4_addr[0], request->ipv4_addr[1], request->ipv4_addr[2], + request->ipv4_addr[3], ret); + } } /* add IPV6 to ipset */ if (request->has_ipv6 && request->qtype == DNS_T_AAAA) { if (request->has_ipv4) { - ret |= ipset_add(ipset_rule->ipsetname, request->ipv4_addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule; + if (rule) { + ret |= ipset_add(rule->ipsetname, request->ipv4_addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + tlog(TLOG_DEBUG, "IPSET-MATCH: domain:%s, ipset:%s, IP: %d.%d.%d.%d, result: %d", request->domain, + rule->ipsetname, request->ipv4_addr[0], request->ipv4_addr[1], request->ipv4_addr[2], + request->ipv4_addr[3], ret); + } + } + rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule; + if (rule) { + ret |= ipset_add(rule->ipsetname, request->ipv6_addr, DNS_RR_AAAA_LEN, request->ttl_v6 * 2); + tlog(TLOG_DEBUG, + "IPSET-MATCH: domain:%s, ipset:%s, IP: " + "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x, result: %d", + request->domain, rule->ipsetname, request->ipv6_addr[0], request->ipv6_addr[1], 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], ret); } - ret |= ipset_add(ipset_rule->ipsetname, request->ipv6_addr, DNS_RR_AAAA_LEN, request->ttl_v6 * 2); } - tlog(TLOG_DEBUG, "IPSET-MATCH: domain:%s, ipset:%s, result: %d", request->domain, ipset_rule->ipsetname, ret); - return ret; } @@ -840,8 +864,7 @@ static int _dns_server_request_complete_AAAA(struct dns_request *request) } /* if doing prefetch, update cache only */ - dns_cache_set_data_addr(cache_data, request->server_flags, cname, cname_ttl, request->ipv6_addr, - DNS_T_AAAA); + dns_cache_set_data_addr(cache_data, request->server_flags, cname, cname_ttl, request->ipv6_addr, DNS_T_AAAA); request->has_soa = 0; } else { @@ -1806,7 +1829,7 @@ static int _dns_server_setup_ipset_packet(struct dns_request *request, struct dn int i = 0; int j = 0; struct dns_rrs *rrs = NULL; - struct dns_ipset_rule *ipset_rule = NULL; + struct dns_ipset_rule *rule = NULL, *ipset_rule = NULL, *ipset_rule_v4 = NULL, *ipset_rule_v6 = NULL; struct dns_rule_flags *rule_flags = NULL; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) { @@ -1815,13 +1838,18 @@ static int _dns_server_setup_ipset_packet(struct dns_request *request, struct dn /* check ipset rule */ rule_flags = request->domain_rule.rules[DOMAIN_RULE_FLAGS]; if (rule_flags) { - if (rule_flags->flags & DOMAIN_FLAG_IPSET_IGNORE) { - return 0; + if ((rule_flags->flags & DOMAIN_FLAG_IPSET_IGN) == 0) { + ipset_rule = request->domain_rule.rules[DOMAIN_RULE_IPSET]; + } + if ((rule_flags->flags & DOMAIN_FLAG_IPSET_IPV4_IGN) == 0) { + ipset_rule_v4 = request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV4]; + } + if ((rule_flags->flags & DOMAIN_FLAG_IPSET_IPV6_IGN) == 0) { + ipset_rule_v6 = request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV6]; } } - ipset_rule = request->domain_rule.rules[DOMAIN_RULE_IPSET]; - if (ipset_rule == NULL) { + if (!(ipset_rule || ipset_rule_v4 || ipset_rule_v6)) { return 0; } @@ -1840,11 +1868,14 @@ static int _dns_server_setup_ipset_packet(struct dns_request *request, struct dn /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); - /* add IPV4 to ipset */ - ipset_add(ipset_rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule; - tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain, - ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3]); + if (rule) { + /* add IPV4 to ipset */ + ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain, + rule->ipsetname, addr[0], addr[1], addr[2], addr[3]); + } } break; case DNS_T_AAAA: { unsigned char addr[16]; @@ -1857,16 +1888,25 @@ static int _dns_server_setup_ipset_packet(struct dns_request *request, struct dn /* add IPV6 to ipset */ if (request->has_ipv6) { if (request->has_ipv4) { - ipset_add(ipset_rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule; + if (rule) { + /* add IPV4 to ipset */ + ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, request->ttl_v4 * 2); + tlog(TLOG_DEBUG, "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", + request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3]); + } + } + rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule; + if (rule) { + ipset_add(rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ttl_v6 * 2); + tlog(TLOG_DEBUG, + "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: " + "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", + request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], + addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], + addr[15]); } - ipset_add(ipset_rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ttl_v6 * 2); } - - tlog(TLOG_DEBUG, - "IPSET-MATCH-PASSTHROUTH: domain: %s, ipset: %s, IP: " - "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", - request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], - addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]); } break; default: break; @@ -2093,10 +2133,18 @@ static void _dns_server_update_rule_by_flags(struct dns_request *request) request->domain_rule.rules[DOMAIN_RULE_ADDRESS_IPV6] = NULL; } - if (flags & DOMAIN_FLAG_IPSET_IGNORE) { + if (flags & DOMAIN_FLAG_IPSET_IGN) { request->domain_rule.rules[DOMAIN_RULE_IPSET] = NULL; } + if (flags & DOMAIN_FLAG_IPSET_IPV4_IGN) { + request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV4] = NULL; + } + + if (flags & DOMAIN_FLAG_IPSET_IPV6_IGN) { + request->domain_rule.rules[DOMAIN_RULE_IPSET_IPV6] = NULL; + } + if (flags & DOMAIN_FLAG_NAMESERVER_IGNORE) { request->domain_rule.rules[DOMAIN_RULE_NAMESERVER] = NULL; }