From 1054229efb31a4f9cddb4f2f163a4c225209c45f Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Wed, 16 Aug 2023 22:47:47 +0800 Subject: [PATCH] feature: add ip-alias option. --- etc/smartdns/smartdns.conf | 4 + src/dns_conf.c | 149 ++++++++++++++++++++-- src/dns_conf.h | 18 +++ src/dns_server.c | 237 +++++++++++++++++++++++----------- src/smartdns.c | 10 +- src/smartdns.h | 2 +- src/util.c | 49 +++++++ src/util.h | 5 + test/cases/test-ip-alias.cc | 248 ++++++++++++++++++++++++++++++++++++ test/server.cc | 8 +- 10 files changed, 636 insertions(+), 94 deletions(-) create mode 100644 test/cases/test-ip-alias.cc diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 7a894daadb..4f82ea2063 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -100,6 +100,10 @@ bind [::]:53 # List of IPs that will be ignored # ignore-ip [ip/subnet] +# alias of IPs +# ip-alias [ip/subnet] [ip1[,ip2]...] +# ip-alias 192.168.0.1/24 10.9.0.1,10.9.0.2 + # speed check mode # speed-check-mode [ping|tcp:port|none|,] # example: diff --git a/src/dns_conf.c b/src/dns_conf.c index ace75e85bc..7704a4f14b 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -252,6 +252,38 @@ static void _dns_rule_put(struct dns_rule *rule) } } +static struct dns_iplist_ip_addresses *_new_dns_iplist_ip_addresses(void) +{ + struct dns_iplist_ip_addresses *iplist; + + iplist = malloc(sizeof(struct dns_iplist_ip_addresses)); + if (!iplist) { + return NULL; + } + memset(iplist, 0, sizeof(struct dns_iplist_ip_addresses)); + atomic_set(&iplist->refcnt, 1); + return iplist; +} + +static void _dns_iplist_ip_addresses_put(struct dns_iplist_ip_addresses *iplist) +{ + if (atomic_dec_and_test(&iplist->refcnt)) { + free(iplist); + } +} + +static void _dns_iplist_ip_address_add(struct dns_iplist_ip_addresses *iplist, unsigned char addr[], int addr_len) +{ + iplist->ipaddr = realloc(iplist->ipaddr, (iplist->ipaddr_num + 1) * sizeof(struct dns_iplist_ip_address)); + if (iplist->ipaddr == NULL) { + return; + } + memset(&iplist->ipaddr[iplist->ipaddr_num], 0, sizeof(struct dns_iplist_ip_address)); + iplist->ipaddr[iplist->ipaddr_num].addr_len = addr_len; + memcpy(iplist->ipaddr[iplist->ipaddr_num].addr, addr, addr_len); + iplist->ipaddr_num++; +} + static int _get_domain(char *value, char *domain, int max_domain_size, char **ptr_after_domain) { char *begin = NULL; @@ -768,6 +800,7 @@ static void _config_domain_destroy(void) static void _config_address_destroy(radix_node_t *node, void *cbctx) { + struct dns_ip_address_rule *address_rule = NULL; if (node == NULL) { return; } @@ -776,6 +809,12 @@ static void _config_address_destroy(radix_node_t *node, void *cbctx) return; } + address_rule = node->data; + if (address_rule->ip_alias) { + _dns_iplist_ip_addresses_put(address_rule->ip_alias); + address_rule->ip_alias = NULL; + } + free(node->data); node->data = NULL; } @@ -2461,20 +2500,20 @@ static radix_node_t *_create_addr_node(char *addr) return node; } -static int _config_iplist_rule(char *subnet, enum address_rule rule) +static struct dns_ip_address_rule *_config_iplist_rule(char *subnet, enum address_rule rule) { radix_node_t *node = NULL; struct dns_ip_address_rule *ip_rule = NULL; node = _create_addr_node(subnet); if (node == NULL) { - return -1; + return NULL; } if (node->data == NULL) { ip_rule = malloc(sizeof(*ip_rule)); if (ip_rule == NULL) { - return -1; + return NULL; } node->data = ip_rule; @@ -2496,11 +2535,15 @@ static int _config_iplist_rule(char *subnet, enum address_rule rule) case ADDRESS_RULE_IP_IGNORE: ip_rule->ip_ignore = 1; break; + case ADDRESS_RULE_IP_ALIAS: { + ip_rule->ip_alias = _new_dns_iplist_ip_addresses(); + ip_rule->ip_alias_enable = 1; + } break; default: - return -1; + return NULL; } - return 0; + return ip_rule; } static int _config_qtype_soa(void *data, int argc, char *argv[]) @@ -2582,7 +2625,11 @@ static int _config_blacklist_ip(void *data, int argc, char *argv[]) return -1; } - return _config_iplist_rule(argv[1], ADDRESS_RULE_BLACKLIST); + if (_config_iplist_rule(argv[1], ADDRESS_RULE_BLACKLIST) == NULL) { + return -1; + } + + return 0; } static int _conf_bogus_nxdomain(void *data, int argc, char *argv[]) @@ -2591,7 +2638,11 @@ static int _conf_bogus_nxdomain(void *data, int argc, char *argv[]) return -1; } - return _config_iplist_rule(argv[1], ADDRESS_RULE_BOGUS); + if (_config_iplist_rule(argv[1], ADDRESS_RULE_BOGUS) == NULL) { + return -1; + } + + return 0; } static int _conf_ip_ignore(void *data, int argc, char *argv[]) @@ -2600,7 +2651,11 @@ static int _conf_ip_ignore(void *data, int argc, char *argv[]) return -1; } - return _config_iplist_rule(argv[1], ADDRESS_RULE_IP_IGNORE); + if (_config_iplist_rule(argv[1], ADDRESS_RULE_IP_IGNORE) == NULL) { + return -1; + } + + return 0; } static int _conf_whitelist_ip(void *data, int argc, char *argv[]) @@ -2609,7 +2664,82 @@ static int _conf_whitelist_ip(void *data, int argc, char *argv[]) return -1; } - return _config_iplist_rule(argv[1], ADDRESS_RULE_WHITELIST); + if (_config_iplist_rule(argv[1], ADDRESS_RULE_WHITELIST) == NULL) { + return -1; + } + + return 0; +} + +static int _conf_ip_alias(void *data, int argc, char *argv[]) +{ + struct dns_ip_address_rule *ip_rule = NULL; + struct dns_iplist_ip_addresses *ip_alias = NULL; + char *target_ips = NULL; + + if (argc <= 2) { + return -1; + } + + ip_rule = _config_iplist_rule(argv[1], ADDRESS_RULE_IP_ALIAS); + if (ip_rule == NULL) { + return -1; + } + + ip_alias = ip_rule->ip_alias; + if (ip_alias == NULL) { + tlog(TLOG_ERROR, "cannot malloc memory"); + goto errout; + } + + target_ips = strdup(argv[2]); + if (target_ips == NULL) { + goto errout; + } + + for (char *tok = strtok(target_ips, ","); tok != NULL; tok = strtok(NULL, ",")) { + struct sockaddr_storage addr; + socklen_t addr_len; + unsigned char *paddr = NULL; + int ret = 0; + + ret = getaddr_by_host(tok, (struct sockaddr *)&addr, &addr_len); + if (ret != 0) { + goto errout; + } + + switch (addr.ss_family) { + case AF_INET: { + struct sockaddr_in *addr_in = NULL; + addr_in = (struct sockaddr_in *)&addr; + paddr = (unsigned char *)&(addr_in->sin_addr.s_addr); + _dns_iplist_ip_address_add(ip_alias, paddr, DNS_RR_A_LEN); + } break; + case AF_INET6: { + struct sockaddr_in6 *addr_in6 = NULL; + addr_in6 = (struct sockaddr_in6 *)&addr; + if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) { + paddr = addr_in6->sin6_addr.s6_addr + 12; + _dns_iplist_ip_address_add(ip_alias, paddr, DNS_RR_A_LEN); + } else { + paddr = addr_in6->sin6_addr.s6_addr; + _dns_iplist_ip_address_add(ip_alias, paddr, DNS_RR_AAAA_LEN); + } + } break; + default: + goto errout; + break; + } + } + + free(target_ips); + return 0; +errout: + if (target_ips) { + free(target_ips); + } + + return -1; } static int _conf_client_subnet(char *subnet, struct dns_edns_client_subnet *ipv4_ecs, @@ -3591,6 +3721,7 @@ static struct config_item _config_item[] = { CONF_CUSTOM("force-qtype-SOA", _config_qtype_soa, NULL), CONF_CUSTOM("blacklist-ip", _config_blacklist_ip, NULL), CONF_CUSTOM("whitelist-ip", _conf_whitelist_ip, NULL), + CONF_CUSTOM("ip-alias", _conf_ip_alias, NULL), CONF_CUSTOM("bogus-nxdomain", _conf_bogus_nxdomain, NULL), CONF_CUSTOM("ignore-ip", _conf_ip_ignore, NULL), CONF_CUSTOM("edns-client-subnet", _conf_edns_client_subnet, NULL), diff --git a/src/dns_conf.h b/src/dns_conf.h index e8d62bb277..8167814f52 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -355,6 +355,22 @@ enum address_rule { ADDRESS_RULE_WHITELIST = 2, ADDRESS_RULE_BOGUS = 3, ADDRESS_RULE_IP_IGNORE = 4, + ADDRESS_RULE_IP_ALIAS = 5, +}; + +struct dns_iplist_ip_address { + int addr_len; + union { + unsigned char ipv4_addr[DNS_RR_A_LEN]; + unsigned char ipv6_addr[DNS_RR_AAAA_LEN]; + unsigned char addr[0]; + }; +}; + +struct dns_iplist_ip_addresses { + atomic_t refcnt; + int ipaddr_num; + struct dns_iplist_ip_address *ipaddr; }; struct dns_ip_address_rule { @@ -362,6 +378,8 @@ struct dns_ip_address_rule { unsigned int whitelist : 1; unsigned int bogus : 1; unsigned int ip_ignore : 1; + unsigned int ip_alias_enable : 1; + struct dns_iplist_ip_addresses *ip_alias; }; struct dns_conf_address_rule { diff --git a/src/dns_server.c b/src/dns_server.c index 97ca521127..d5a203af9a 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -2732,8 +2732,8 @@ static int _dns_server_check_speed(struct dns_request *request, char *ip) return -1; } -static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char *addr, int addr_len, - dns_type_t addr_type, int result_flag) +static struct dns_ip_address_rule *_dns_server_ip_rule_get(struct dns_request *request, unsigned char *addr, + int addr_len, dns_type_t addr_type) { prefix_t prefix; radix_node_t *node = NULL; @@ -2741,7 +2741,7 @@ static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char /* Match IP address rules */ if (prefix_from_blob(addr, addr_len, addr_len * 8, &prefix) == NULL) { - return -1; + return NULL; } switch (prefix.family) { @@ -2756,15 +2756,24 @@ static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char } if (node == NULL) { - goto rule_not_found; + return NULL; } if (node->data == NULL) { - goto rule_not_found; + return NULL; } - /* bogus-nxdomain */ rule = node->data; + + return rule; +} + +static int _dns_server_ip_rule_check(struct dns_request *request, struct dns_ip_address_rule *rule, int result_flag) +{ + if (rule == NULL) { + goto rule_not_found; + } + if (rule->bogus) { request->rcode = DNS_RC_NXDOMAIN; request->has_soa = 1; @@ -2785,6 +2794,10 @@ static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char goto skip; } + if (rule->ip_alias_enable) { + goto match; + } + rule_not_found: if (result_flag & DNSSERVER_FLAG_WHITELIST_IP) { if (rule == NULL) { @@ -2807,6 +2820,60 @@ static int _dns_server_ip_rule_check(struct dns_request *request, unsigned char return 0; } +static int _dns_server_process_ip_alias(struct dns_request *request, struct dns_iplist_ip_addresses *alias, + unsigned char **paddrs, int *paddr_num, int max_paddr_num, int addr_len) +{ + int addr_num = 0; + + if (alias == 0) { + return 0; + } + + if (request == NULL) { + return -1; + } + + if (alias->ipaddr_num <= 0) { + return 0; + } + + for (int i = 0; i < alias->ipaddr_num && i < max_paddr_num; i++) { + if (alias->ipaddr[i].addr_len != addr_len) { + continue; + } + paddrs[i] = alias->ipaddr[i].addr; + addr_num++; + } + + *paddr_num = addr_num; + return 0; +} + +static int _dns_server_process_ip_rule(struct dns_request *request, unsigned char *addr, int addr_len, + dns_type_t addr_type, int result_flag, struct dns_iplist_ip_addresses **alias) +{ + struct dns_ip_address_rule *rule = NULL; + int ret = 0; + + rule = _dns_server_ip_rule_get(request, addr, addr_len, addr_type); + ret = _dns_server_ip_rule_check(request, rule, result_flag); + if (ret != 0) { + return ret; + } + + if (rule->ip_alias_enable && alias != NULL) { + *alias = rule->ip_alias; + if (alias == NULL) { + return 0; + } + + /* need process ip alias */ + return -1; + } + + return 0; +} + static int _dns_server_is_adblock_ipv6(const unsigned char addr[16]) { int i = 0; @@ -2830,8 +2897,11 @@ static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request int ttl = 0; int ip_check_result = 0; unsigned char addr[4]; + unsigned char *paddrs[MAX_IP_NUM]; + int paddr_num = 0; char name[DNS_MAX_CNAME_LEN] = {0}; char ip[DNS_MAX_CNAME_LEN] = {0}; + struct dns_iplist_ip_addresses *alias = NULL; if (request->qtype != DNS_T_A) { /* ignore non-matched query type */ @@ -2839,65 +2909,71 @@ static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request return 0; } } - _dns_server_request_get(request); + /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); + paddrs[paddr_num] = addr; + paddr_num = 1; tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %d.%d.%d.%d", name, ttl, addr[0], addr[1], addr[2], addr[3]); /* if domain is not match */ if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { - _dns_server_request_release(request); return -1; } /* ip rule check */ - ip_check_result = _dns_server_ip_rule_check(request, addr, 4, DNS_T_A, result_flag); + ip_check_result = _dns_server_process_ip_rule(request, addr, 4, DNS_T_A, result_flag, &alias); if (ip_check_result == 0) { /* match */ - _dns_server_request_release(request); return -1; } else if (ip_check_result == -2 || ip_check_result == -3) { /* skip, nxdomain */ - _dns_server_request_release(request); return ip_check_result; } - if (atomic_read(&request->ip_map_num) == 0) { - request->has_ip = 1; - memcpy(request->ip_addr, addr, DNS_RR_A_LEN); - request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); - if (cname[0] != 0 && request->has_cname == 0 && dns_conf_force_no_cname == 0) { - request->has_cname = 1; - safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); - } - } else { - if (ttl < request->ip_ttl) { + int ret = _dns_server_process_ip_alias(request, alias, paddrs, &paddr_num, MAX_IP_NUM, DNS_RR_A_LEN); + if (ret != 0) { + return ret; + } + + for (int i = 0; i < paddr_num; i++) { + unsigned char *paddr = paddrs[i]; + if (atomic_read(&request->ip_map_num) == 0) { + request->has_ip = 1; + memcpy(request->ip_addr, paddr, DNS_RR_A_LEN); request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); + if (cname[0] != 0 && request->has_cname == 0 && dns_conf_force_no_cname == 0) { + request->has_cname = 1; + safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); + } + } else { + if (ttl < request->ip_ttl) { + request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); + } } - } - /* Ad blocking result */ - if (addr[0] == 0 || addr[0] == 127) { - /* If half of the servers return the same result, then ignore this address */ - if (atomic_inc_return(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { - request->rcode = DNS_RC_NOERROR; - _dns_server_request_release(request); - return -1; + /* Ad blocking result */ + if (paddr[0] == 0 || paddr[0] == 127) { + /* If half of the servers return the same result, then ignore this address */ + if (atomic_inc_return(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { + request->rcode = DNS_RC_NOERROR; + return -1; + } } - } - /* add this ip to request */ - if (_dns_ip_address_check_add(request, cname, addr, DNS_T_A, 0, NULL) != 0) { - _dns_server_request_release(request); - return -1; - } + /* add this ip to request */ + if (_dns_ip_address_check_add(request, cname, paddr, DNS_T_A, 0, NULL) != 0) { + return -1; + } - sprintf(ip, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + sprintf(ip, "%d.%d.%d.%d", paddr[0], paddr[1], paddr[2], paddr[3]); - /* start ping */ - if (_dns_server_check_speed(request, ip) != 0) { - _dns_server_request_release(request); + /* start ping */ + _dns_server_request_get(request); + if (_dns_server_check_speed(request, ip) != 0) { + _dns_server_request_release(request); + } } return 0; @@ -2907,17 +2983,22 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque char *cname, unsigned int result_flag) { unsigned char addr[16]; + unsigned char *paddrs[MAX_IP_NUM]; + int paddr_num = 0; char name[DNS_MAX_CNAME_LEN] = {0}; char ip[DNS_MAX_CNAME_LEN] = {0}; int ttl = 0; int ip_check_result = 0; + struct dns_iplist_ip_addresses *alias = NULL; if (request->qtype != DNS_T_AAAA) { /* ignore non-matched query type */ return -1; } - _dns_server_request_get(request); + dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); + paddrs[paddr_num] = addr; + paddr_num = 1; tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", name, ttl, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], @@ -2925,58 +3006,62 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque /* if domain is not match */ if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { - _dns_server_request_release(request); return -1; } - ip_check_result = _dns_server_ip_rule_check(request, addr, 16, DNS_T_AAAA, result_flag); + ip_check_result = _dns_server_process_ip_rule(request, addr, 16, DNS_T_AAAA, result_flag, &alias); if (ip_check_result == 0) { /* match */ - _dns_server_request_release(request); return -1; } else if (ip_check_result == -2 || ip_check_result == -3) { /* skip, nxdomain */ - _dns_server_request_release(request); return ip_check_result; } - if (atomic_read(&request->ip_map_num) == 0) { - request->has_ip = 1; - memcpy(request->ip_addr, addr, DNS_RR_AAAA_LEN); - request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); - if (cname[0] != 0 && request->has_cname == 0 && dns_conf_force_no_cname == 0) { - request->has_cname = 1; - safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); - } - } else { - if (ttl < request->ip_ttl) { + int ret = _dns_server_process_ip_alias(request, alias, paddrs, &paddr_num, MAX_IP_NUM, DNS_RR_AAAA_LEN); + if (ret != 0) { + return ret; + } + + for (int i = 0; i < paddr_num; i++) { + unsigned char *paddr = paddrs[i]; + if (atomic_read(&request->ip_map_num) == 0) { + request->has_ip = 1; + memcpy(request->ip_addr, paddr, DNS_RR_AAAA_LEN); request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); + if (cname[0] != 0 && request->has_cname == 0 && dns_conf_force_no_cname == 0) { + request->has_cname = 1; + safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); + } + } else { + if (ttl < request->ip_ttl) { + request->ip_ttl = _dns_server_get_conf_ttl(request, ttl); + } } - } - /* Ad blocking result */ - if (_dns_server_is_adblock_ipv6(addr) == 0) { - /* If half of the servers return the same result, then ignore this address */ - if (atomic_inc_return(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { - request->rcode = DNS_RC_NOERROR; - _dns_server_request_release(request); - return -1; + /* Ad blocking result */ + if (_dns_server_is_adblock_ipv6(paddr) == 0) { + /* If half of the servers return the same result, then ignore this address */ + if (atomic_inc_return(&request->adblock) <= (dns_server_alive_num() / 2 + dns_server_alive_num() % 2)) { + request->rcode = DNS_RC_NOERROR; + return -1; + } } - } - /* add this ip to request */ - if (_dns_ip_address_check_add(request, cname, addr, DNS_T_AAAA, 0, NULL) != 0) { - _dns_server_request_release(request); - return -1; - } + /* add this ip to request */ + if (_dns_ip_address_check_add(request, cname, paddr, DNS_T_AAAA, 0, NULL) != 0) { + return -1; + } - sprintf(ip, "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", 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]); + sprintf(ip, "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", paddr[0], paddr[1], + paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10], paddr[11], + paddr[12], paddr[13], paddr[14], paddr[15]); - /* start ping */ - if (_dns_server_check_speed(request, ip) != 0) { - _dns_server_request_release(request); + /* start ping */ + _dns_server_request_get(request); + if (_dns_server_check_speed(request, ip) != 0) { + _dns_server_request_release(request); + } } return 0; @@ -3140,7 +3225,7 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const addr[3]); /* ip rule check */ - ip_check_result = _dns_server_ip_rule_check(request, addr, 4, DNS_T_A, result_flag); + ip_check_result = _dns_server_process_ip_rule(request, addr, 4, DNS_T_A, result_flag, NULL); if (ip_check_result == 0 || ip_check_result == -2 || ip_check_result == -3) { /* match, skip, nxdomain */ _dns_server_request_release(request); @@ -3180,7 +3265,7 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const name, ttl_tmp, 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]); - ip_check_result = _dns_server_ip_rule_check(request, addr, 16, DNS_T_AAAA, result_flag); + ip_check_result = _dns_server_process_ip_rule(request, addr, 16, DNS_T_AAAA, result_flag, NULL); if (ip_check_result == 0 || ip_check_result == -2 || ip_check_result == -3) { /* match, skip, nxdomain */ _dns_server_request_release(request); diff --git a/src/smartdns.c b/src/smartdns.c index 730ca81001..96733bff08 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -743,11 +743,15 @@ static void smartdns_test_notify_func(int fd_notify, uint64_t retval) } } -#define smartdns_close_allfds() close_all_fd(fd_notify); -int smartdns_main(int argc, char *argv[], int fd_notify) +#define smartdns_close_allfds() \ + if (no_close_allfds == 0) { \ + close_all_fd(fd_notify); \ + } + +int smartdns_main(int argc, char *argv[], int fd_notify, int no_close_allfds) #else #define smartdns_test_notify(retval) -#define smartdns_close_allfds() close_all_fd(-1); +#define smartdns_close_allfds() close_all_fd(-1) int main(int argc, char *argv[]) #endif { diff --git a/src/smartdns.h b/src/smartdns.h index 519d646015..a921510a58 100644 --- a/src/smartdns.h +++ b/src/smartdns.h @@ -29,7 +29,7 @@ typedef void (*smartdns_post_func)(void *arg); int smartdns_reg_post_func(smartdns_post_func func, void *arg); -int smartdns_main(int argc, char *argv[], int fd_notify); +int smartdns_main(int argc, char *argv[], int fd_notify, int no_close_allfds); #endif diff --git a/src/util.c b/src/util.c index 3be1004e87..ade576a9fc 100644 --- a/src/util.c +++ b/src/util.c @@ -156,6 +156,55 @@ char *get_host_by_addr(char *host, int maxsize, struct sockaddr *addr) return NULL; } +int generate_random_addr(unsigned char *addr, int addr_len, int mask) +{ + if (mask / 8 > addr_len) { + return -1; + } + + int offset = mask / 8; + int bit = 0; + + for (int i = offset; i < addr_len; i++) { + bit = 0xFF; + if (i == offset) { + bit = ~(0xFF << (8 - mask % 8)) & 0xFF; + } + addr[i] = jhash(&addr[i], 1, 0) & bit; + } + + return 0; +} + +int generate_addr_map(unsigned char *addr_from, unsigned char *addr_to, unsigned char *addr_out, int addr_len, int mask) +{ + if ((mask / 8) >= addr_len) { + if (mask % 8 != 0) { + return -1; + } + } + + int offset = mask / 8; + int bit = mask % 8; + for (int i = 0; i < offset; i++) { + addr_out[i] = addr_to[i]; + } + + if (bit != 0) { + int mask1 = 0xFF >> bit; + int mask2 = (0xFF << (8 - bit)) & 0xFF; + addr_out[offset] = addr_from[offset] & mask1; + addr_out[offset] |= addr_to[offset] & mask2; + offset = offset + 1; + } + + for (int i = offset; i < addr_len; i++) { + addr_out[i] = addr_from[i]; + } + + return 0; +} + int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len) { struct addrinfo hints; diff --git a/src/util.h b/src/util.h index a18667d99b..417285d0c2 100644 --- a/src/util.h +++ b/src/util.h @@ -59,6 +59,11 @@ char *dir_name(char *path); char *get_host_by_addr(char *host, int maxsize, struct sockaddr *addr); +int generate_random_addr(unsigned char *addr, int addr_len, int mask); + +int generate_addr_map(unsigned char *addr_from, unsigned char *addr_to, unsigned char *addr_out, int addr_len, + int mask); + int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len); int getsocket_inet(int fd, struct sockaddr *addr, socklen_t *addr_len); diff --git a/test/cases/test-ip-alias.cc b/test/cases/test-ip-alias.cc new file mode 100644 index 0000000000..4d50761d37 --- /dev/null +++ b/test/cases/test-ip-alias.cc @@ -0,0 +1,248 @@ +/************************************************************************* + * + * Copyright (C) 2018-2023 Ruilin Peng (Nick) . + * + * smartdns is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * smartdns is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "client.h" +#include "dns.h" +#include "include/utils.h" +#include "server.h" +#include "gtest/gtest.h" +#include + +class IPAlias : public ::testing::Test +{ + protected: + virtual void SetUp() {} + virtual void TearDown() {} +}; + +TEST(IPAlias, map_multiip_nospeed_check) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) { + std::string domain = request->domain; + if (request->domain.length() == 0) { + return smartdns::SERVER_REQUEST_ERROR; + } + + if (request->qtype == DNS_T_A) { + unsigned char addr[][4] = {{1, 2, 3, 1}, {1, 2, 3, 2}, {1, 2, 3, 3}}; + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]); + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]); + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]); + } else if (request->qtype == DNS_T_AAAA) { + unsigned char addr[][16] = {{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}, + {1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3}}; + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]); + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]); + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]); + } else { + return smartdns::SERVER_REQUEST_ERROR; + } + + request->response_packet->head.rcode = DNS_RC_NOERROR; + return smartdns::SERVER_REQUEST_OK; + }); + + server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100); + server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110); + server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 140); + server.MockPing(PING_TYPE_ICMP, "10.10.10.10", 60, 120); + server.MockPing(PING_TYPE_ICMP, "11.11.11.11", 60, 150); + server.MockPing(PING_TYPE_ICMP, "0102:0304:0500::", 60, 100); + server.MockPing(PING_TYPE_ICMP, "0506:0708:0900::", 60, 110); + server.MockPing(PING_TYPE_ICMP, "0a0b:0c0d:0e00::", 60, 140); + server.MockPing(PING_TYPE_ICMP, "ffff::1", 60, 120); + server.MockPing(PING_TYPE_ICMP, "ffff::2", 60, 150); + + server.Start(R"""(bind [::]:60053 +server 127.0.0.1:61053 +log-num 0 +log-console yes +log-level debug +dualstack-ip-selection no +speed-check-mode none +ip-alias 1.2.3.0/24 10.10.10.10,12.12.12.12,13.13.13.13,15.15.15.15 +ip-alias 0102::/16 FFFF::0001,FFFF::0002,FFFF::0003,FFFF::0004 +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 4); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "10.10.10.10"); + EXPECT_EQ(client.GetAnswer()[1].GetData(), "12.12.12.12"); + EXPECT_EQ(client.GetAnswer()[2].GetData(), "15.15.15.15"); + EXPECT_EQ(client.GetAnswer()[3].GetData(), "13.13.13.13"); + + ASSERT_TRUE(client.Query("a.com AAAA", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 4); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "ffff::1"); + EXPECT_EQ(client.GetAnswer()[1].GetData(), "ffff::3"); + EXPECT_EQ(client.GetAnswer()[2].GetData(), "ffff::2"); + EXPECT_EQ(client.GetAnswer()[3].GetData(), "ffff::4"); +} + +TEST(IPAlias, map_single_ip_nospeed_check) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) { + std::string domain = request->domain; + if (request->domain.length() == 0) { + return smartdns::SERVER_REQUEST_ERROR; + } + + if (request->qtype == DNS_T_A) { + unsigned char addr[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]); + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]); + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]); + } else if (request->qtype == DNS_T_AAAA) { + unsigned char addr[][16] = {{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]); + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]); + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]); + } else { + return smartdns::SERVER_REQUEST_ERROR; + } + + request->response_packet->head.rcode = DNS_RC_NOERROR; + return smartdns::SERVER_REQUEST_OK; + }); + + server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100); + server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110); + server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 140); + server.MockPing(PING_TYPE_ICMP, "10.10.10.10", 60, 120); + server.MockPing(PING_TYPE_ICMP, "11.11.11.11", 60, 150); + server.MockPing(PING_TYPE_ICMP, "0102:0304:0500::", 60, 100); + server.MockPing(PING_TYPE_ICMP, "0506:0708:0900::", 60, 110); + server.MockPing(PING_TYPE_ICMP, "0a0b:0c0d:0e00::", 60, 140); + server.MockPing(PING_TYPE_ICMP, "ffff::1", 60, 120); + server.MockPing(PING_TYPE_ICMP, "ffff::2", 60, 150); + + server.Start(R"""(bind [::]:60053 +server 127.0.0.1:61053 +log-num 0 +log-console yes +log-level debug +dualstack-ip-selection no +speed-check-mode none +ip-alias 1.2.3.4 10.10.10.10 +ip-alias 5.6.7.8/32 11.11.11.11 +ip-alias 0102:0304:0500:: ffff::1 +ip-alias 0506:0708:0900:: ffff::2 +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 3); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "10.10.10.10"); + EXPECT_EQ(client.GetAnswer()[1].GetData(), "11.11.11.11"); + EXPECT_EQ(client.GetAnswer()[2].GetData(), "9.10.11.12"); + + ASSERT_TRUE(client.Query("a.com AAAA", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 3); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "ffff::1"); + EXPECT_EQ(client.GetAnswer()[1].GetData(), "a0b:c0d:e00::"); + EXPECT_EQ(client.GetAnswer()[2].GetData(), "ffff::2"); +} + +TEST(IPAlias, mapip_withspeed_check) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) { + std::string domain = request->domain; + if (request->domain.length() == 0) { + return smartdns::SERVER_REQUEST_ERROR; + } + + if (request->qtype == DNS_T_A) { + unsigned char addr[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]); + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]); + dns_add_A(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]); + } else if (request->qtype == DNS_T_AAAA) { + unsigned char addr[][16] = {{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[0]); + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[1]); + dns_add_AAAA(request->response_packet, DNS_RRS_AN, domain.c_str(), 61, addr[2]); + } else { + return smartdns::SERVER_REQUEST_ERROR; + } + + request->response_packet->head.rcode = DNS_RC_NOERROR; + return smartdns::SERVER_REQUEST_OK; + }); + + server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 100); + server.MockPing(PING_TYPE_ICMP, "5.6.7.8", 60, 110); + server.MockPing(PING_TYPE_ICMP, "9.10.11.12", 60, 140); + server.MockPing(PING_TYPE_ICMP, "10.10.10.10", 60, 120); + server.MockPing(PING_TYPE_ICMP, "11.11.11.11", 60, 150); + server.MockPing(PING_TYPE_ICMP, "0102:0304:0500::", 60, 100); + server.MockPing(PING_TYPE_ICMP, "0506:0708:0900::", 60, 110); + server.MockPing(PING_TYPE_ICMP, "0a0b:0c0d:0e00::", 60, 140); + server.MockPing(PING_TYPE_ICMP, "ffff::1", 60, 120); + server.MockPing(PING_TYPE_ICMP, "ffff::2", 60, 150); + + server.Start(R"""(bind [::]:60053 +server 127.0.0.1:61053 +log-num 0 +log-console yes +log-level debug +dualstack-ip-selection no +ip-alias 1.2.3.4 10.10.10.10 +ip-alias 5.6.7.8/32 11.11.11.11 +ip-alias 0102::/16 ffff::1 +ip-alias 0506::/16 ffff::2 +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "10.10.10.10"); + + ASSERT_TRUE(client.Query("a.com AAAA", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "ffff::1"); +} diff --git a/test/server.cc b/test/server.cc index 880eb09af0..8f2245c510 100644 --- a/test/server.cc +++ b/test/server.cc @@ -376,23 +376,21 @@ bool Server::Start(const std::string &conf, enum CONF_TYPE type) } smartdns_reg_post_func(Server::StartPost, this); - smartdns_main(args.size(), argv, fds[1]); + smartdns_main(args.size(), argv, fds[1], 0); _exit(1); } else if (pid < 0) { return false; } } else if (mode_ == CREATE_MODE_THREAD) { thread_ = std::thread([&]() { - std::vector args = { - "smartdns", "-f", "-x", "-c", conf_file_, "-p", "-", - }; + std::vector args = {"smartdns", "-f", "-x", "-c", conf_file_, "-p", "-", "-S"}; char *argv[args.size() + 1]; for (size_t i = 0; i < args.size(); i++) { argv[i] = (char *)args[i].c_str(); } smartdns_reg_post_func(Server::StartPost, this); - smartdns_main(args.size(), argv, fds[1]); + smartdns_main(args.size(), argv, fds[1], 1); smartdns_reg_post_func(nullptr, nullptr); }); } else {