2
0
Эх сурвалжийг харах

feature: add ip-alias option.

Nick Peng 2 жил өмнө
parent
commit
1054229efb

+ 4 - 0
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:

+ 140 - 9
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),

+ 18 - 0
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 {

+ 161 - 76
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 @@ match:
 	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);

+ 7 - 3
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
 {

+ 1 - 1
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
 

+ 49 - 0
src/util.c

@@ -156,6 +156,55 @@ errout:
 	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;

+ 5 - 0
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);

+ 248 - 0
test/cases/test-ip-alias.cc

@@ -0,0 +1,248 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "client.h"
+#include "dns.h"
+#include "include/utils.h"
+#include "server.h"
+#include "gtest/gtest.h"
+#include <fstream>
+
+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");
+}

+ 3 - 5
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<std::string> args = {
-				"smartdns", "-f", "-x", "-c", conf_file_, "-p", "-",
-			};
+			std::vector<std::string> 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 {