Kaynağa Gözat

fix: fix http-record test case fail issue.

Nick Peng 2 ay önce
ebeveyn
işleme
08b5482a51

+ 0 - 2
src/dns_conf/dns_conf.c

@@ -330,7 +330,6 @@ static int _dns_server_load_conf_init(void)
 	_config_ptr_table_init();
 	_config_domain_set_name_table_init();
 	_config_ip_set_name_table_init();
-	_config_srv_record_table_init();
 	_config_plugin_table_init();
 
 	if (_config_current_group_push_default() != 0) {
@@ -351,7 +350,6 @@ void dns_server_load_exit(void)
 	_config_ptr_table_destroy(0);
 	_config_host_table_destroy(0);
 	_config_proxy_table_destroy();
-	_config_srv_record_table_destroy();
 	_config_plugin_table_destroy();
 	_config_plugin_table_conf_destroy();
 

+ 54 - 1
src/dns_conf/domain_rule.c

@@ -158,6 +158,9 @@ void *_new_dns_rule_ext(enum domain_rule domain_rule, int ext_size)
 	case DOMAIN_RULE_HTTPS:
 		size = sizeof(struct dns_https_record_rule);
 		break;
+	case DOMAIN_RULE_SRV:
+		size = sizeof(struct dns_srv_record_rule);
+		break;
 	case DOMAIN_RULE_TTL:
 		size = sizeof(struct dns_ttl_rule);
 		break;
@@ -185,10 +188,36 @@ void _dns_rule_get(struct dns_rule *rule)
 	atomic_inc(&rule->refcnt);
 }
 
+static void _dns_rule_free(struct dns_rule *rule)
+{
+	if (rule->rule == DOMAIN_RULE_HTTPS) {
+		struct dns_https_record_rule *https_rule = (struct dns_https_record_rule *)rule;
+		struct dns_https_record *record, *tmp;
+		if (https_rule->record_list.next != NULL && https_rule->record_list.prev != NULL) {
+			list_for_each_entry_safe(record, tmp, &https_rule->record_list, list)
+			{
+				list_del(&record->list);
+				free(record);
+			}
+		}
+	} else if (rule->rule == DOMAIN_RULE_SRV) {
+		struct dns_srv_record_rule *srv_rule = (struct dns_srv_record_rule *)rule;
+		struct dns_srv_record *record, *tmp;
+		if (srv_rule->record_list.next != NULL && srv_rule->record_list.prev != NULL) {
+			list_for_each_entry_safe(record, tmp, &srv_rule->record_list, list)
+			{
+				list_del(&record->list);
+				free(record);
+			}
+		}
+	}
+	free(rule);
+}
+
 void _dns_rule_put(struct dns_rule *rule)
 {
 	if (atomic_dec_and_test(&rule->refcnt)) {
-		free(rule);
+		_dns_rule_free(rule);
 	}
 }
 
@@ -1034,3 +1063,27 @@ errout:
 	}
 	return -1;
 }
+
+void *dns_conf_get_domain_rule(const char *domain, enum domain_rule type)
+{
+	struct dns_domain_rule *domain_rule = NULL;
+	char domain_key[DNS_MAX_CONF_CNAME_LEN] = {0};
+	int len = 0;
+	int sub_rule_only = 0;
+	int root_rule_only = 0;
+
+	if (_config_setup_domain_key(domain, domain_key, sizeof(domain_key), &len, &root_rule_only, &sub_rule_only) != 0) {
+		return NULL;
+	}
+
+	domain_rule = art_search(&_config_current_rule_group()->domain_rule.tree, (unsigned char *)domain_key, len);
+	if (domain_rule == NULL) {
+		return NULL;
+	}
+
+	if (type >= DOMAIN_RULE_MAX || type >= domain_rule->capacity) {
+		return NULL;
+	}
+
+	return domain_rule->rules[type];
+}

+ 1 - 0
src/dns_conf/domain_rule.h

@@ -39,6 +39,7 @@ int _config_domain_rule_flag_set(const char *domain, unsigned int flag, unsigned
 int _config_domain_rules(void *data, int argc, char *argv[]);
 int _config_domain_rule_delete(const char *domain);
 int _conf_domain_rule_group(const char *domain, const char *group_name);
+void *dns_conf_get_domain_rule(const char *domain, enum domain_rule type);
 int _config_domain_rule_free(struct dns_domain_rule *domain_rule);
 
 int _conf_domain_rule_speed_check(char *domain, const char *mode);

+ 37 - 20
src/dns_conf/https_record.c

@@ -63,6 +63,7 @@ static int _conf_domain_rule_https_copy_alpn(char *alpn_data, int max_alpn_len,
 int _conf_domain_rule_https_record(const char *domain, const char *host)
 {
 	struct dns_https_record_rule *https_record_rule = NULL;
+	struct dns_https_record *record = NULL;
 	enum domain_rule type = DOMAIN_RULE_HTTPS;
 	char buff[4096];
 	int key_num = 0;
@@ -71,11 +72,22 @@ int _conf_domain_rule_https_record(const char *domain, const char *host)
 	int priority = -1;
 	/*mode_type, 0: alias mode, 1: service mode */
 	int mode_type = 0;
+	int is_new = 0;
 
 	safe_strncpy(buff, host, sizeof(buff));
 
-	https_record_rule = _new_dns_rule(type);
+	https_record_rule = dns_conf_get_domain_rule(domain, type);
 	if (https_record_rule == NULL) {
+		https_record_rule = _new_dns_rule(type);
+		if (https_record_rule == NULL) {
+			goto errout;
+		}
+		INIT_LIST_HEAD(&https_record_rule->record_list);
+		is_new = 1;
+	}
+
+	record = zalloc(1, sizeof(*record));
+	if (record == NULL) {
 		goto errout;
 	}
 
@@ -107,8 +119,8 @@ int _conf_domain_rule_https_record(const char *domain, const char *host)
 			}
 
 		} else if (strncmp(key, "target", sizeof("target")) == 0) {
-			safe_strncpy(https_record_rule->record.target, val, DNS_MAX_CONF_CNAME_LEN);
-			https_record_rule->record.enable = 1;
+			safe_strncpy(record->target, val, DNS_MAX_CONF_CNAME_LEN);
+			record->enable = 1;
 		} else if (strncmp(key, "noipv4hint", sizeof("noipv4hint")) == 0) {
 			https_record_rule->filter.no_ipv4hint = 1;
 		} else if (strncmp(key, "noipv6hint", sizeof("noipv6hint")) == 0) {
@@ -120,29 +132,29 @@ int _conf_domain_rule_https_record(const char *domain, const char *host)
 			https_record_rule->filter.no_ech = 1;
 		} else {
 			mode_type = 1;
-			https_record_rule->record.enable = 1;
+			record->enable = 1;
 			if (strncmp(key, "priority", sizeof("priority")) == 0) {
 				priority = atoi(val);
 			} else if (strncmp(key, "port", sizeof("port")) == 0) {
-				https_record_rule->record.port = atoi(val);
+				record->port = atoi(val);
 
 			} else if (strncmp(key, "alpn", sizeof("alpn")) == 0) {
-				int alpn_len = _conf_domain_rule_https_copy_alpn(https_record_rule->record.alpn, DNS_MAX_ALPN_LEN, val);
+				int alpn_len = _conf_domain_rule_https_copy_alpn(record->alpn, DNS_MAX_ALPN_LEN, val);
 				if (alpn_len <= 0) {
 					tlog(TLOG_ERROR, "invalid option value for %s.", key);
 					goto errout;
 				}
-				https_record_rule->record.alpn_len = alpn_len;
+				record->alpn_len = alpn_len;
 			} else if (strncmp(key, "ech", sizeof("ech")) == 0) {
-				int ech_len = SSL_base64_decode(val, https_record_rule->record.ech, DNS_MAX_ECH_LEN);
+				int ech_len = SSL_base64_decode(val, record->ech, DNS_MAX_ECH_LEN);
 				if (ech_len < 0) {
 					tlog(TLOG_ERROR, "invalid option value for %s.", key);
 					goto errout;
 				}
-				https_record_rule->record.ech_len = ech_len;
+				record->ech_len = ech_len;
 			} else if (strncmp(key, "ipv4hint", sizeof("ipv4hint")) == 0) {
 				int addr_len = DNS_RR_A_LEN;
-				if (get_raw_addr_by_ip(val, https_record_rule->record.ipv4_addr, &addr_len) != 0) {
+				if (get_raw_addr_by_ip(val, record->ipv4_addr, &addr_len) != 0) {
 					tlog(TLOG_ERROR, "invalid option value for %s, value: %s", key, val);
 					goto errout;
 				}
@@ -151,10 +163,10 @@ int _conf_domain_rule_https_record(const char *domain, const char *host)
 					tlog(TLOG_ERROR, "invalid option value for %s, value: %s", key, val);
 					goto errout;
 				}
-				https_record_rule->record.has_ipv4 = 1;
+				record->has_ipv4 = 1;
 			} else if (strncmp(key, "ipv6hint", sizeof("ipv6hint")) == 0) {
 				int addr_len = DNS_RR_AAAA_LEN;
-				if (get_raw_addr_by_ip(val, https_record_rule->record.ipv6_addr, &addr_len) != 0) {
+				if (get_raw_addr_by_ip(val, record->ipv6_addr, &addr_len) != 0) {
 					tlog(TLOG_ERROR, "invalid option value for %s, value: %s", key, val);
 					goto errout;
 				}
@@ -163,7 +175,7 @@ int _conf_domain_rule_https_record(const char *domain, const char *host)
 					tlog(TLOG_ERROR, "invalid option value for %s, value: %s", key, val);
 					goto errout;
 				}
-				https_record_rule->record.has_ipv6 = 1;
+				record->has_ipv6 = 1;
 			} else {
 				tlog(TLOG_WARN, "invalid parameter %s for https-record.", key);
 				continue;
@@ -184,20 +196,25 @@ int _conf_domain_rule_https_record(const char *domain, const char *host)
 		}
 	}
 
-	https_record_rule->record.priority = priority;
+	record->priority = priority;
+	list_add_tail(&record->list, &https_record_rule->record_list);
 
-	if (_config_domain_rule_add(domain, type, https_record_rule) != 0) {
-		goto errout;
+	if (is_new) {
+		if (_config_domain_rule_add(domain, type, https_record_rule) != 0) {
+			goto errout;
+		}
+		_dns_rule_put(&https_record_rule->head);
+		https_record_rule = NULL;
 	}
 
-	_dns_rule_put(&https_record_rule->head);
-	https_record_rule = NULL;
-
 	return 0;
 errout:
-	if (https_record_rule) {
+	if (is_new && https_record_rule) {
 		_dns_rule_put(&https_record_rule->head);
 	}
+	if (record && record->list.next == NULL) {
+		free(record);
+	}
 
 	return -1;
 }

+ 23 - 55
src/dns_conf/srv_record.c

@@ -20,43 +20,25 @@
 #include "set_file.h"
 #include "smartdns/lib/stringutil.h"
 #include "smartdns/util.h"
+#include "domain_rule.h"
 
-/* SRV-HOST */
-struct dns_srv_record_table dns_conf_srv_record_table;
-
-struct dns_srv_records *dns_server_get_srv_record(const char *domain)
-{
-	uint32_t key = 0;
-
-	key = hash_string(domain);
-	struct dns_srv_records *srv_records = NULL;
-	hash_for_each_possible(dns_conf_srv_record_table.srv, srv_records, node, key)
-	{
-		if (strncmp(srv_records->domain, domain, DNS_MAX_CONF_CNAME_LEN) == 0) {
-			return srv_records;
-		}
-	}
-
-	return NULL;
-}
 
 static int _confg_srv_record_add(const char *domain, const char *host, unsigned short priority, unsigned short weight,
 								 unsigned short port)
 {
-	struct dns_srv_records *srv_records = NULL;
+	struct dns_srv_record_rule *srv_rule = NULL;
 	struct dns_srv_record *srv_record = NULL;
-	uint32_t key = 0;
+	int is_new = 0;
+	enum domain_rule type = DOMAIN_RULE_SRV;
 
-	srv_records = dns_server_get_srv_record(domain);
-	if (srv_records == NULL) {
-		srv_records = zalloc(1, sizeof(*srv_records));
-		if (srv_records == NULL) {
+	srv_rule = dns_conf_get_domain_rule(domain, type);
+	if (srv_rule == NULL) {
+		srv_rule = _new_dns_rule(type);
+		if (srv_rule == NULL) {
 			goto errout;
 		}
-		safe_strncpy(srv_records->domain, domain, DNS_MAX_CONF_CNAME_LEN);
-		INIT_LIST_HEAD(&srv_records->list);
-		key = hash_string(domain);
-		hash_add(dns_conf_srv_record_table.srv, &srv_records->node, key);
+		INIT_LIST_HEAD(&srv_rule->record_list);
+		is_new = 1;
 	}
 
 	srv_record = zalloc(1, sizeof(*srv_record));
@@ -67,11 +49,22 @@ static int _confg_srv_record_add(const char *domain, const char *host, unsigned
 	srv_record->priority = priority;
 	srv_record->weight = weight;
 	srv_record->port = port;
-	list_add_tail(&srv_record->list, &srv_records->list);
+	list_add_tail(&srv_record->list, &srv_rule->record_list);
+
+	if (is_new) {
+		if (_config_domain_rule_add(domain, type, srv_rule) != 0) {
+			goto errout;
+		}
+		_dns_rule_put(&srv_rule->head);
+		srv_rule = NULL;
+	}
 
 	return 0;
 errout:
-	if (srv_record != NULL) {
+	if (is_new && srv_rule != NULL) {
+		_dns_rule_put(&srv_rule->head);
+	}
+	if (srv_record && srv_record->list.next == NULL) {
 		free(srv_record);
 	}
 	return -1;
@@ -137,28 +130,3 @@ errout:
 	tlog(TLOG_ERROR, "add srv-record %s:%s failed", domain, value);
 	return -1;
 }
-
-void _config_srv_record_table_init(void)
-{
-	hash_init(dns_conf_srv_record_table.srv);
-}
-
-void _config_srv_record_table_destroy(void)
-{
-	struct dns_srv_records *srv_records = NULL;
-	struct dns_srv_record *srv_record, *tmp1 = NULL;
-	struct hlist_node *tmp = NULL;
-	unsigned int i;
-
-	hash_for_each_safe(dns_conf_srv_record_table.srv, i, tmp, srv_records, node)
-	{
-		list_for_each_entry_safe(srv_record, tmp1, &srv_records->list, list)
-		{
-			list_del(&srv_record->list);
-			free(srv_record);
-		}
-
-		hlist_del_init(&srv_records->node);
-		free(srv_records);
-	}
-}

+ 0 - 2
src/dns_conf/srv_record.h

@@ -28,8 +28,6 @@ extern "C" {
 
 int _config_srv_record(void *data, int argc, char *argv[]);
 
-void _config_srv_record_table_init(void);
-void _config_srv_record_table_destroy(void);
 
 #ifdef __cplusplus
 }

+ 3 - 31
src/dns_server/answer.c

@@ -257,12 +257,12 @@ static int _dns_server_process_answer_HTTPS(struct dns_rrs *rrs, struct dns_requ
 		return -1;
 	}
 
-	https_svcb = request->https_svcb;
+	https_svcb = zalloc(1, sizeof(*https_svcb));
 	if (https_svcb == NULL) {
-		/* ignore non-matched query type */
-		tlog(TLOG_WARN, "https svcb not set");
 		return -1;
 	}
+	INIT_LIST_HEAD(&https_svcb->list);
+	list_add_tail(&https_svcb->list, &request->https_svcb_list);
 
 	tlog(TLOG_DEBUG, "domain: %s HTTPS: %s TTL: %d priority: %d", name, target, ttl, priority);
 	https_svcb->ttl = ttl;
@@ -383,30 +383,6 @@ int _dns_server_process_answer(struct dns_request *request, const char *domain,
 		return DNS_CLIENT_ACTION_UNDEFINE;
 	}
 
-	/* when QTYPE is HTTPS, check if support */
-	if (request->qtype == DNS_T_HTTPS) {
-		int https_svcb_record_num = 0;
-		for (j = 1; j < DNS_RRS_OPT; j++) {
-			rrs = dns_get_rrs_start(packet, j, &rr_count);
-			for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) {
-				switch (rrs->type) {
-				case DNS_T_HTTPS: {
-					https_svcb_record_num++;
-					if (https_svcb_record_num <= 1) {
-						continue;
-					}
-
-					/* CURRENT NOT SUPPORT MUTI HTTPS RECORD */
-					*need_passthrouh = 1;
-					return DNS_CLIENT_ACTION_OK;
-				}
-				default:
-					break;
-				}
-			}
-		}
-	}
-
 	for (j = 1; j < DNS_RRS_OPT; j++) {
 		rrs = dns_get_rrs_start(packet, j, &rr_count);
 		for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) {
@@ -465,10 +441,6 @@ int _dns_server_process_answer(struct dns_request *request, const char *domain,
 				}
 				request->rcode = packet->head.rcode;
 				is_rcode_set = 1;
-				if (request->has_ip == 0) {
-					request->passthrough = 1;
-					_dns_server_request_complete(request);
-				}
 			} break;
 			case DNS_T_SOA: {
 				/* if DNS64 enabled, skip check SOA. */

+ 47 - 38
src/dns_server/context.c

@@ -17,6 +17,7 @@
  */
 
 #include "context.h"
+#include "smartdns/dns_conf.h"
 #include "address.h"
 #include "audit.h"
 #include "cache.h"
@@ -177,18 +178,12 @@ static int _dns_rrs_add_all_best_ip(struct dns_server_post_context *context)
 static int _dns_server_add_srv(struct dns_server_post_context *context)
 {
 	struct dns_request *request = context->request;
-	struct dns_srv_records *srv_records = request->srv_records;
-	struct dns_srv_record *srv_record = NULL;
+	struct dns_request_srv *srv = NULL;
 	int ret = 0;
 
-	if (srv_records == NULL) {
-		return 0;
-	}
-
-	list_for_each_entry(srv_record, &srv_records->list, list)
-	{
-		ret = dns_add_SRV(context->packet, DNS_RRS_AN, request->domain, request->ip_ttl, srv_record->priority,
-						  srv_record->weight, srv_record->port, srv_record->host);
+	list_for_each_entry(srv, &request->srv_list, list) {
+		ret = dns_add_SRV(context->packet, DNS_RRS_AN, request->domain, request->ip_ttl, srv->priority, srv->weight,
+						  srv->port, srv->host);
 		if (ret != 0) {
 			return -1;
 		}
@@ -262,52 +257,66 @@ static int _dns_add_rrs_ip_hint(struct dns_server_post_context *context, struct
 static int _dns_add_rrs_HTTPS(struct dns_server_post_context *context)
 {
 	struct dns_request *request = context->request;
-	struct dns_request_https *https_svcb = request->https_svcb;
+	struct dns_request_https *https_svcb;
 	int ret = 0;
 	struct dns_rr_nested param;
 
-	if (https_svcb == NULL || request->qtype != DNS_T_HTTPS) {
+	if (request->qtype != DNS_T_HTTPS) {
 		return 0;
 	}
 
-	ret = dns_add_HTTPS_start(&param, context->packet, DNS_RRS_AN, https_svcb->domain, https_svcb->ttl,
-							  https_svcb->priority, https_svcb->target);
-	if (ret != 0) {
-		return ret;
-	}
-
-	if (https_svcb->alpn[0] != '\0' && https_svcb->alpn_len > 0) {
-		ret = dns_HTTPS_add_alpn(&param, https_svcb->alpn, https_svcb->alpn_len);
+	list_for_each_entry(https_svcb, &request->https_svcb_list, list) {
+		ret = dns_add_HTTPS_start(&param, context->packet, DNS_RRS_AN, https_svcb->domain, https_svcb->ttl,
+								  https_svcb->priority, https_svcb->target);
 		if (ret != 0) {
 			return ret;
 		}
-	}
 
-	if (https_svcb->port != 0) {
-		ret = dns_HTTPS_add_port(&param, https_svcb->port);
+		if (https_svcb->alpn[0] != '\0' && https_svcb->alpn_len > 0) {
+			ret = dns_HTTPS_add_alpn(&param, https_svcb->alpn, https_svcb->alpn_len);
+			if (ret != 0) {
+				return ret;
+			}
+		}
+
+		if (https_svcb->port != 0) {
+			ret = dns_HTTPS_add_port(&param, https_svcb->port);
+			if (ret != 0) {
+				return ret;
+			}
+		}
+
+		if (https_svcb->has_ipv4) {
+			unsigned char *addr[1];
+			addr[0] = https_svcb->ipv4_addr;
+			ret = dns_HTTPS_add_ipv4hint(&param, addr, 1);
+		} else {
+			ret = _dns_add_rrs_ip_hint(context, &param, DNS_T_A);
+		}
 		if (ret != 0) {
 			return ret;
 		}
-	}
 
-	ret = _dns_add_rrs_ip_hint(context, &param, DNS_T_A);
-	if (ret != 0) {
-		return ret;
-	}
+		if (https_svcb->ech_len > 0) {
+			ret = dns_HTTPS_add_ech(&param, https_svcb->ech, https_svcb->ech_len);
+			if (ret != 0) {
+				return ret;
+			}
+		}
 
-	if (https_svcb->ech_len > 0) {
-		ret = dns_HTTPS_add_ech(&param, https_svcb->ech, https_svcb->ech_len);
+		if (https_svcb->has_ipv6) {
+			unsigned char *addr[1];
+			addr[0] = https_svcb->ipv6_addr;
+			ret = dns_HTTPS_add_ipv6hint(&param, addr, 1);
+		} else {
+			ret = _dns_add_rrs_ip_hint(context, &param, DNS_T_AAAA);
+		}
 		if (ret != 0) {
 			return ret;
 		}
-	}
 
-	ret = _dns_add_rrs_ip_hint(context, &param, DNS_T_AAAA);
-	if (ret != 0) {
-		return ret;
+		dns_add_HTTPS_end(&param);
 	}
-
-	dns_add_HTTPS_end(&param);
 	return 0;
 }
 
@@ -328,7 +337,7 @@ static int _dns_add_rrs(struct dns_server_post_context *context)
 		domain = request->cname;
 	}
 
-	if (request->https_svcb != NULL) {
+	if (!list_empty(&request->https_svcb_list)) {
 		ret = _dns_add_rrs_HTTPS(context);
 	}
 
@@ -377,7 +386,7 @@ static int _dns_add_rrs(struct dns_server_post_context *context)
 		ret |= dns_add_OPT_ECS(context->packet, &request->ecs);
 	}
 
-	if (request->srv_records != NULL) {
+	if (!list_empty(&request->srv_list)) {
 		ret |= _dns_server_add_srv(context);
 	}
 

+ 16 - 2
src/dns_server/dns_server.h

@@ -250,6 +250,7 @@ typedef DNS_CHILD_POST_RESULT (*child_request_callback)(struct dns_request *requ
 														int is_first_resp);
 
 struct dns_request_https {
+	struct list_head list;
 	char domain[DNS_MAX_CNAME_LEN];
 	char target[DNS_MAX_CNAME_LEN];
 	int ttl;
@@ -259,6 +260,19 @@ struct dns_request_https {
 	int port;
 	char ech[DNS_MAX_ECH_LEN];
 	int ech_len;
+	
+	int has_ipv4;
+	unsigned char ipv4_addr[DNS_RR_A_LEN];
+	int has_ipv6;
+	unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
+};
+
+struct dns_request_srv {
+	struct list_head list;
+	char host[DNS_MAX_CNAME_LEN];
+	unsigned short priority;
+	unsigned short weight;
+	unsigned short port;
 };
 
 struct dns_request {
@@ -300,7 +314,7 @@ struct dns_request {
 	struct dns_opt_ecs ecs;
 	int edns0_do;
 
-	struct dns_request_https *https_svcb;
+	struct list_head https_svcb_list;
 
 	dns_result_callback result_callback;
 	void *user_ptr;
@@ -328,7 +342,7 @@ struct dns_request {
 
 	int is_cache_reply;
 
-	struct dns_srv_records *srv_records;
+	struct list_head srv_list;
 
 	atomic_t notified;
 	atomic_t do_callback;

+ 72 - 32
src/dns_server/request.c

@@ -154,8 +154,18 @@ static void _dns_server_delete_request(struct dns_request *request)
 
 	pthread_mutex_destroy(&request->ip_map_lock);
 
-	if (request->https_svcb) {
-		free(request->https_svcb);
+	struct dns_request_https *https_svcb, *tmp_https;
+	list_for_each_entry_safe(https_svcb, tmp_https, &request->https_svcb_list, list)
+	{
+		list_del(&https_svcb->list);
+		free(https_svcb);
+	}
+
+	struct dns_request_srv *srv, *tmp_srv;
+	list_for_each_entry_safe(srv, tmp_srv, &request->srv_list, list)
+	{
+		list_del(&srv->list);
+		free(srv);
 	}
 
 	if (request->original_domain) {
@@ -478,6 +488,8 @@ struct dns_request *_dns_server_new_request(void)
 	INIT_LIST_HEAD(&request->list);
 	INIT_LIST_HEAD(&request->pending_list);
 	INIT_LIST_HEAD(&request->check_list);
+	INIT_LIST_HEAD(&request->https_svcb_list);
+	INIT_LIST_HEAD(&request->srv_list);
 	hash_init(request->ip_map);
 	_dns_server_request_get(request);
 	atomic_add(1, &server.request_num);
@@ -658,14 +670,29 @@ static int _dns_server_process_local_SOA(struct dns_request *request)
 
 int _dns_server_process_srv(struct dns_request *request)
 {
-	struct dns_srv_records *srv_records = dns_server_get_srv_record(request->domain);
-	if (srv_records == NULL) {
+	struct dns_srv_record *srv_record;
+	struct dns_request_srv *srv;
+	struct dns_srv_record_rule *srv_rule =
+		(struct dns_srv_record_rule *)_dns_server_get_dns_rule(request, DOMAIN_RULE_SRV);
+	if (srv_rule == NULL) {
 		return -1;
 	}
 
 	request->rcode = DNS_RC_NOERROR;
 	request->ip_ttl = _dns_server_get_local_ttl(request);
-	request->srv_records = srv_records;
+
+	list_for_each_entry(srv_record, &srv_rule->record_list, list)
+	{
+		srv = zalloc(1, sizeof(*srv));
+		if (srv == NULL) {
+			continue;
+		}
+		safe_strncpy(srv->host, srv_record->host, sizeof(srv->host));
+		srv->priority = srv_record->priority;
+		srv->weight = srv_record->weight;
+		srv->port = srv_record->port;
+		list_add_tail(&srv->list, &request->srv_list);
+	}
 
 	struct dns_server_post_context context;
 	_dns_server_post_context_init(&context, request);
@@ -849,49 +876,62 @@ int _dns_server_get_expired_ttl_reply(struct dns_request *request, struct dns_ca
 int _dns_server_process_https_svcb(struct dns_request *request)
 {
 	struct dns_https_record_rule *https_record_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_HTTPS);
+	struct dns_https_record *record;
+	struct dns_request_https *svcb;
 
 	if (request->qtype != DNS_T_HTTPS) {
 		return 0;
 	}
 
-	if (request->https_svcb != NULL) {
+	if (!list_empty(&request->https_svcb_list)) {
 		return 0;
 	}
 
-	request->https_svcb = zalloc(1, sizeof(*request->https_svcb));
-	if (request->https_svcb == NULL) {
-		return -1;
-	}
-
 	if (https_record_rule == NULL) {
 		return 0;
 	}
 
-	if (https_record_rule->record.enable == 0) {
-		return 0;
-	}
+	list_for_each_entry(record, &https_record_rule->record_list, list)
+	{
+		if (record->enable == 0) {
+			continue;
+		}
 
-	safe_strncpy(request->https_svcb->domain, request->domain, sizeof(request->https_svcb->domain));
-	safe_strncpy(request->https_svcb->target, https_record_rule->record.target, sizeof(request->https_svcb->target));
-	request->https_svcb->priority = https_record_rule->record.priority;
-	request->https_svcb->port = https_record_rule->record.port;
-	memcpy(request->https_svcb->ech, https_record_rule->record.ech, https_record_rule->record.ech_len);
-	request->https_svcb->ech_len = https_record_rule->record.ech_len;
-	memcpy(request->https_svcb->alpn, https_record_rule->record.alpn, sizeof(request->https_svcb->alpn));
-	request->https_svcb->alpn_len = https_record_rule->record.alpn_len;
-	if (https_record_rule->record.has_ipv4) {
-		memcpy(request->ip_addr, https_record_rule->record.ipv4_addr, DNS_RR_A_LEN);
-		request->ip_addr_type = DNS_T_A;
-		request->has_ip = 1;
-	} else if (https_record_rule->record.has_ipv6) {
-		memcpy(request->ip_addr, https_record_rule->record.ipv6_addr, DNS_RR_AAAA_LEN);
-		request->ip_addr_type = DNS_T_AAAA;
-		request->has_ip = 1;
+		svcb = zalloc(1, sizeof(*svcb));
+		if (svcb == NULL) {
+			continue;
+		}
+
+		safe_strncpy(svcb->domain, request->domain, sizeof(svcb->domain));
+		safe_strncpy(svcb->target, record->target, sizeof(svcb->target));
+		svcb->priority = record->priority;
+		svcb->port = record->port;
+		memcpy(svcb->ech, record->ech, record->ech_len);
+		svcb->ech_len = record->ech_len;
+		memcpy(svcb->alpn, record->alpn, sizeof(svcb->alpn));
+		svcb->alpn_len = record->alpn_len;
+
+		if (record->has_ipv4) {
+			svcb->has_ipv4 = 1;
+			memcpy(svcb->ipv4_addr, record->ipv4_addr, sizeof(svcb->ipv4_addr));
+			request->has_ip = 1;
+		}
+
+		if (record->has_ipv6) {
+			svcb->has_ipv6 = 1;
+			memcpy(svcb->ipv6_addr, record->ipv6_addr, sizeof(svcb->ipv6_addr));
+			request->has_ip = 1;
+		}
+
+		list_add_tail(&svcb->list, &request->https_svcb_list);
 	}
 
-	request->rcode = DNS_RC_NOERROR;
+	if (!list_empty(&request->https_svcb_list)) {
+		request->rcode = DNS_RC_NOERROR;
+		return -1;
+	}
 
-	return -1;
+	return 0;
 }
 
 void _dns_server_request_set_client(struct dns_request *request, struct dns_server_conn_head *conn)

+ 8 - 12
src/include/smartdns/dns_conf.h

@@ -95,6 +95,7 @@ enum domain_rule {
 	DOMAIN_RULE_IPSET_IPV6, /* IPv6 IPSet */
 
 	DOMAIN_RULE_HTTPS,         /* HTTPS record */
+	DOMAIN_RULE_SRV,           /* SRV record */
 	DOMAIN_RULE_RESPONSE_MODE, /* Response mode */
 	DOMAIN_RULE_CNAME,         /* CNAME rule */
 	DOMAIN_RULE_TTL,           /* TTL control */
@@ -307,6 +308,7 @@ struct dns_response_mode_rule {
 };
 
 struct dns_https_record {
+	struct list_head list;
 	int enable;
 	char target[DNS_MAX_CNAME_LEN];
 	int priority;
@@ -329,10 +331,15 @@ struct dns_https_filter {
 
 struct dns_https_record_rule {
 	struct dns_rule head;
-	struct dns_https_record record;
+	struct list_head record_list;
 	struct dns_https_filter filter;
 };
 
+struct dns_srv_record_rule {
+	struct dns_rule head;
+	struct list_head record_list;
+};
+
 struct dns_group_table {
 	DECLARE_HASHTABLE(group, 8);
 };
@@ -656,16 +663,6 @@ struct dns_srv_record {
 	unsigned short port;
 };
 
-struct dns_srv_records {
-	char domain[DNS_MAX_CNAME_LEN];
-	struct hlist_node node;
-	struct list_head list;
-};
-
-struct dns_srv_record_table {
-	DECLARE_HASHTABLE(srv, 4);
-};
-extern struct dns_srv_record_table dns_conf_srv_record_table;
 
 struct dns_conf_plugin {
 	struct hlist_node node;
@@ -778,7 +775,6 @@ int dns_server_check_update_hosts(void);
 
 struct dns_proxy_names *dns_server_get_proxy_names(const char *proxyname);
 
-struct dns_srv_records *dns_server_get_srv_record(const char *domain);
 
 struct dns_conf_group *dns_server_get_rule_group(const char *group_name);
 

+ 44 - 7
test/cases/test-https.cc

@@ -517,6 +517,43 @@ cache-persist no)""");
 	EXPECT_EQ(client.GetAnswer()[0].GetData(), "1 a.com. alpn=\"h2,h3-19\" port=443 ipv4hint=1.2.3.4 ech=AEX+DQA=");
 }
 
+TEST_F(HTTPS, multi_https_record)
+{
+	smartdns::MockServer server_upstream;
+	smartdns::Server server;
+
+	server_upstream.Start("udp://0.0.0.0:61053",
+						  [&](struct smartdns::ServerRequestContext *request) { return smartdns::SERVER_REQUEST_SOA; });
+
+	server.Start(R"""(bind [::]:60053
+server 127.0.0.1:61053
+log-console yes
+dualstack-ip-selection no
+force-qtype-SOA 65
+https-record /a.com/target=b.com,priority=1,port=1443,alpn=\"h2,h3-19\",ech=\"AEX+DQA=\",ipv4hint=1.2.3.4
+https-record /a.com/target=b.com,priority=2,port=2443,alpn=\"h2,h3-19\",ech=\"AEX+DQA=\",ipv4hint=1.2.3.4
+log-level debug
+cache-persist no)""");
+	smartdns::Client client;
+
+	ASSERT_TRUE(client.Query("b.com HTTPS", 60053));
+	std::cout << client.GetResult() << std::endl;
+	ASSERT_EQ(client.GetAuthorityNum(), 1);
+	EXPECT_EQ(client.GetStatus(), "NOERROR");
+	EXPECT_EQ(client.GetAuthority()[0].GetName(), "b.com");
+	EXPECT_EQ(client.GetAuthority()[0].GetTTL(), 30);
+	EXPECT_EQ(client.GetAuthority()[0].GetType(), "SOA");
+
+	ASSERT_TRUE(client.Query("a.com HTTPS", 60053));
+	std::cout << client.GetResult() << std::endl;
+	ASSERT_EQ(client.GetAnswerNum(), 2);
+	EXPECT_EQ(client.GetStatus(), "NOERROR");
+	EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
+	EXPECT_EQ(client.GetAnswer()[0].GetType(), "HTTPS");
+	EXPECT_EQ(client.GetAnswer()[0].GetData(), "1 b.com. alpn=\"h2,h3-19\" port=1443 ipv4hint=1.2.3.4 ech=AEX+DQA=");
+	EXPECT_EQ(client.GetAnswer()[1].GetData(), "2 b.com. alpn=\"h2,h3-19\" port=2443 ipv4hint=1.2.3.4 ech=AEX+DQA=");
+}
+
 TEST_F(HTTPS, https_record)
 {
 	smartdns::MockServer server_upstream;
@@ -602,7 +639,7 @@ cache-persist no)""");
 	EXPECT_EQ(client.GetAnswer()[0].GetData(), "1 b.com. alpn=\"h2,h3-19\" port=443 ech=AEX+DQA=");
 }
 
-TEST_F(HTTPS, multi_not_support)
+TEST_F(HTTPS, multi_filter_ip)
 {
 	smartdns::MockServer server_upstream;
 	smartdns::Server server;
@@ -616,7 +653,7 @@ TEST_F(HTTPS, multi_not_support)
 		struct dns_rr_nested svcparam_buffer;
 
 		{
-			dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 3, 1, "b.com");
+			dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 300, 1, "b.com");
 			const char alph[] = "\x02h2\x05h3-19";
 			int alph_len = sizeof(alph) - 1;
 			dns_HTTPS_add_alpn(&svcparam_buffer, alph, alph_len);
@@ -634,7 +671,7 @@ TEST_F(HTTPS, multi_not_support)
 
 		{
 
-			dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 3, 1, "c.com");
+			dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 300, 2, "c.com");
 			const char alph[] = "\x02h2\x05h3-19";
 			int alph_len = sizeof(alph) - 1;
 			dns_HTTPS_add_alpn(&svcparam_buffer, alph, alph_len);
@@ -668,18 +705,18 @@ cache-persist no)""");
 	ASSERT_EQ(client.GetAnswerNum(), 2);
 	EXPECT_EQ(client.GetStatus(), "NOERROR");
 	EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com");
-	EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 600);
+	EXPECT_EQ(client.GetAnswer()[0].GetTTL(), 3);
 	EXPECT_EQ(client.GetAnswer()[0].GetType(), "HTTPS");
 	EXPECT_EQ(
 		client.GetAnswer()[0].GetData(),
-		"1 b.com. alpn=\"h2,h3-19\" port=443 ipv4hint=1.2.3.4 ech=AEX+DQA= ipv6hint=102:304:506:708:90a:b0c:d0e:f10");
+		"1 b.com. alpn=\"h2,h3-19\" port=443");
 
 	EXPECT_EQ(client.GetAnswer()[1].GetName(), "a.com");
-	EXPECT_EQ(client.GetAnswer()[1].GetTTL(), 600);
+	EXPECT_EQ(client.GetAnswer()[1].GetTTL(), 3);
 	EXPECT_EQ(client.GetAnswer()[1].GetType(), "HTTPS");
 	EXPECT_EQ(
 		client.GetAnswer()[1].GetData(),
-		"1 c.com. alpn=\"h2,h3-19\" port=443 ipv4hint=5.6.7.8 ech=AEX+DQA= ipv6hint=102:304:506:708:90a:b0c:d0e:f11");
+		"2 c.com. alpn=\"h2,h3-19\" port=443");
 }
 
 TEST_F(HTTPS, BIND_FORCE_SOA)

+ 1 - 1
test/cases/test-ip-rule.cc

@@ -106,7 +106,7 @@ whitelist-ip 4.5.6.7/24
 	ASSERT_TRUE(client.Query("a.com", 60053));
 	std::cout << client.GetResult() << std::endl;
 	ASSERT_EQ(client.GetAnswerNum(), 0);
-	EXPECT_EQ(client.GetStatus(), "SERVFAIL");
+	EXPECT_EQ(client.GetStatus(), "NOERROR");
 }
 
 TEST_F(IPRule, black_list)