http-svcb: add https svcb RR suppport

This commit is contained in:
Nick Peng
2023-10-18 19:43:01 +08:00
parent 62171f2a4d
commit 27c1aedd3b
3 changed files with 712 additions and 90 deletions

View File

@@ -224,6 +224,18 @@ struct dns_request_domain_rule {
typedef DNS_CHILD_POST_RESULT (*child_request_callback)(struct dns_request *request, struct dns_request *child_request,
int is_first_resp);
struct dns_request_https {
char domain[DNS_MAX_CNAME_LEN];
char target[DNS_MAX_CNAME_LEN];
int ttl;
int priority;
char alpn[DNS_MAX_ALPN_LEN];
int alpn_len;
int port;
int ech[DNS_MAX_ECH_LEN];
int ech_len;
};
struct dns_request {
atomic_t refcnt;
@@ -258,6 +270,7 @@ struct dns_request {
struct sockaddr_storage localaddr;
int has_ecs;
struct dns_opt_ecs ecs;
struct dns_request_https *https_svcb;
dns_result_callback result_callback;
void *user_ptr;
@@ -275,6 +288,7 @@ struct dns_request {
int ping_time;
int ip_ttl;
unsigned char ip_addr[DNS_RR_AAAA_LEN];
int ip_addr_type;
struct dns_soa soa;
int has_soa;
@@ -945,6 +959,64 @@ static void _dns_server_setup_soa(struct dns_request *request)
soa->minimum = 86400;
}
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;
int ret = 0;
struct dns_rr_nested param;
if (https_svcb == NULL || 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);
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 (request->has_ip) {
unsigned char *addr[1];
addr[0] = request->ip_addr;
if (request->ip_addr_type == DNS_T_A) {
ret = dns_HTTPS_add_ipv4hint(&param, addr, 1);
}
}
if (https_svcb->ech[0] != 0 && https_svcb->ech_len > 0) {
ret = dns_HTTPS_add_ech(&param, https_svcb->ech, https_svcb->ech_len);
if (ret != 0) {
return ret;
}
}
if (request->has_ip) {
unsigned char *addr[1];
addr[0] = request->ip_addr;
if (request->ip_addr_type == DNS_T_AAAA) {
ret = dns_HTTPS_add_ipv6hint(&param, addr, 1);
}
}
dns_add_HTTPS_end(&param);
return 0;
}
static int _dns_add_rrs(struct dns_server_post_context *context)
{
struct dns_request *request = context->request;
@@ -962,6 +1034,10 @@ static int _dns_add_rrs(struct dns_server_post_context *context)
domain = request->cname;
}
if (request->https_svcb != NULL) {
ret = _dns_add_rrs_HTTPS(context);
}
/* add A record */
if (request->has_ip && context->do_force_soa == 0) {
_dns_server_context_add_ip(context, request->ip_addr);
@@ -1306,7 +1382,7 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ
int ttl = 0;
int speed = 0;
if (qtype != DNS_T_A && qtype != DNS_T_AAAA) {
if (qtype != DNS_T_A && qtype != DNS_T_AAAA && qtype != DNS_T_HTTPS) {
goto errout;
}
@@ -1631,7 +1707,7 @@ static int _dns_cache_reply_packet(struct dns_server_post_context *context)
return _dns_cache_packet(context);
}
if (context->qtype != DNS_T_AAAA && context->qtype != DNS_T_A) {
if (context->qtype != DNS_T_AAAA && context->qtype != DNS_T_A && context->qtype != DNS_T_HTTPS) {
return _dns_cache_specify_packet(context);
}
@@ -1657,6 +1733,47 @@ static int _dns_cache_reply_packet(struct dns_server_post_context *context)
return 0;
}
static void _dns_server_add_ipset_nftset(struct dns_request *request, struct dns_ipset_rule *ipset_rule,
struct dns_nftset_rule *nftset_rule, const unsigned char addr[], int addr_len,
int timeout_value)
{
if (ipset_rule != NULL) {
/* add IPV4 to ipset */
if (addr_len == DNS_RR_A_LEN) {
tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain,
ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
ipset_add(ipset_rule->ipsetname, addr, DNS_RR_A_LEN, timeout_value);
} else if (addr_len == DNS_RR_AAAA_LEN) {
tlog(TLOG_DEBUG,
"IPSET-MATCH: domain: %s, ipset: %s, IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, ipset_rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6],
addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
ipset_add(ipset_rule->ipsetname, addr, DNS_RR_AAAA_LEN, timeout_value);
}
}
if (nftset_rule != NULL) {
/* add IPV4 to ipset */
if (addr_len == DNS_RR_A_LEN) {
tlog(TLOG_DEBUG, "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: %d.%d.%d.%d", request->domain,
nftset_rule->familyname, nftset_rule->nfttablename, nftset_rule->nftsetname, addr[0], addr[1], addr[2],
addr[3]);
nftset_add(nftset_rule->familyname, nftset_rule->nfttablename, nftset_rule->nftsetname, addr, DNS_RR_A_LEN,
timeout_value);
} else if (addr_len == DNS_RR_AAAA_LEN) {
tlog(TLOG_DEBUG,
"NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, nftset_rule->familyname, nftset_rule->nfttablename, nftset_rule->nftsetname, 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]);
nftset_add(nftset_rule->familyname, nftset_rule->nfttablename, nftset_rule->nftsetname, addr,
DNS_RR_AAAA_LEN, timeout_value);
}
}
}
static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context *context)
{
int ttl = 0;
@@ -1768,21 +1885,7 @@ static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context
dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule;
if (rule != NULL) {
/* add IPV4 to ipset */
tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain,
rule->ipsetname, addr[0], addr[1], addr[2], addr[3]);
ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, timeout_value);
}
if (nftset_ip != NULL) {
/* add IPV4 to ipset */
tlog(TLOG_DEBUG, "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: %d.%d.%d.%d", request->domain,
nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr[0], addr[1],
addr[2], addr[3]);
nftset_add(nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr,
DNS_RR_A_LEN, timeout_value);
}
_dns_server_add_ipset_nftset(request, rule, nftset_ip, addr, DNS_RR_A_LEN, timeout_value);
} break;
case DNS_T_AAAA: {
unsigned char addr[16];
@@ -1793,26 +1896,42 @@ static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context
dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule;
if (rule != NULL) {
tlog(TLOG_DEBUG,
"IPSET-MATCH: domain: %s, ipset: %s, IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14],
addr[15]);
ipset_add(rule->ipsetname, addr, DNS_RR_AAAA_LEN, timeout_value);
_dns_server_add_ipset_nftset(request, rule, nftset_ip6, addr, DNS_RR_AAAA_LEN, timeout_value);
} break;
case DNS_T_HTTPS: {
char target[DNS_MAX_CNAME_LEN] = {0};
struct dns_https_param *p = NULL;
int priority = 0;
int ret = dns_get_HTTPS_svcparm_start(rrs, &p, name, DNS_MAX_CNAME_LEN, &ttl, &priority, target,
DNS_MAX_CNAME_LEN);
if (ret != 0) {
tlog(TLOG_WARN, "get HTTPS svcparm failed");
return -1;
}
if (nftset_ip6 != NULL) {
/* add IPV6 to ipset */
tlog(TLOG_DEBUG,
"NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->domain, nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname,
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]);
nftset_add(nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname, addr,
DNS_RR_AAAA_LEN, timeout_value);
for (; p; p = dns_get_HTTPS_svcparm_next(rrs, p)) {
switch (p->key) {
case DNS_HTTPS_T_IPV4HINT: {
unsigned char *addr;
for (int k = 0; k < p->len / 4; k++) {
addr = p->value + k * 4;
rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule;
_dns_server_add_ipset_nftset(request, rule, nftset_ip, addr, DNS_RR_A_LEN, timeout_value);
}
} break;
case DNS_HTTPS_T_IPV6HINT: {
unsigned char *addr;
for (int k = 0; k < p->len / 16; k++) {
addr = p->value + k * 16;
rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule;
_dns_server_add_ipset_nftset(request, rule, nftset_ip6, addr, DNS_RR_AAAA_LEN,
timeout_value);
}
} break;
default:
break;
}
}
} break;
default:
@@ -2310,7 +2429,8 @@ static void _dns_server_select_possible_ipaddress(struct dns_request *request)
}
/* Return the most likely correct IP address */
/* Returns the IP with the most hits, or the last returned record is considered to be the most likely correct. */
/* Returns the IP with the most hits, or the last returned record is considered to be the most likely
* correct. */
pthread_mutex_lock(&request->ip_map_lock);
hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
{
@@ -2375,6 +2495,9 @@ static void _dns_server_delete_request(struct dns_request *request)
_dns_server_conn_release(request->conn);
}
pthread_mutex_destroy(&request->ip_map_lock);
if (request->https_svcb) {
free(request->https_svcb);
}
memset(request, 0, sizeof(*request));
free(request);
}
@@ -2629,6 +2752,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
if (request->ping_time > rtt || request->ping_time == -1) {
memcpy(request->ip_addr, &addr_in->sin_addr.s_addr, 4);
request->ip_addr_type = DNS_T_A;
request->ping_time = rtt;
request->has_cname = 0;
request->has_ip = 1;
@@ -2646,7 +2770,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
}
}
if (request->qtype == DNS_T_A) {
if (request->qtype == DNS_T_A || request->qtype == DNS_T_HTTPS) {
request->has_ping_result = 1;
}
} break;
@@ -2664,6 +2788,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
request->has_cname = 0;
request->has_ip = 1;
memcpy(request->ip_addr, addr_in6->sin6_addr.s6_addr + 12, 4);
request->ip_addr_type = DNS_T_A;
if (addr_map && addr_map->cname[0] != 0) {
request->has_cname = 1;
safe_strncpy(request->cname, addr_map->cname, DNS_MAX_CNAME_LEN);
@@ -2672,7 +2797,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
}
}
if (request->qtype == DNS_T_A) {
if (request->qtype == DNS_T_A || request->qtype == DNS_T_HTTPS) {
request->has_ping_result = 1;
}
} else {
@@ -2686,6 +2811,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
request->has_cname = 0;
request->has_ip = 1;
memcpy(request->ip_addr, addr_in6->sin6_addr.s6_addr, 16);
request->ip_addr_type = DNS_T_AAAA;
if (addr_map && addr_map->cname[0] != 0) {
request->has_cname = 1;
safe_strncpy(request->cname, addr_map->cname, DNS_MAX_CNAME_LEN);
@@ -2694,7 +2820,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch
}
}
if (request->qtype == DNS_T_AAAA) {
if (request->qtype == DNS_T_AAAA || request->qtype == DNS_T_HTTPS) {
request->has_ping_result = 1;
}
}
@@ -2941,37 +3067,18 @@ static int _dns_server_is_adblock_ipv6(const unsigned char addr[16])
return -1;
}
static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request *request, const char *domain,
char *cname, unsigned int result_flag)
static int _dns_server_process_answer_A_IP(struct dns_request *request, char *cname, unsigned char addr[4], int ttl,
unsigned int result_flag)
{
int ttl = 0;
char ip[DNS_MAX_CNAME_LEN] = {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 */
if (request->dualstack_selection == 0) {
return 0;
}
}
/* 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) {
return -1;
}
/* ip rule check */
ip_check_result = _dns_server_process_ip_rule(request, addr, 4, DNS_T_A, result_flag, &alias);
if (ip_check_result == 0) {
@@ -2991,6 +3098,7 @@ static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request
unsigned char *paddr = paddrs[i];
if (atomic_read(&request->ip_map_num) == 0) {
request->has_ip = 1;
request->ip_addr_type = DNS_T_A;
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) {
@@ -3029,36 +3137,18 @@ static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request
return 0;
}
static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_request *request, const char *domain,
char *cname, unsigned int result_flag)
static int _dns_server_process_answer_AAAA_IP(struct dns_request *request, char *cname, unsigned char addr[16], int ttl,
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;
unsigned char *paddrs[MAX_IP_NUM];
struct dns_iplist_ip_addresses *alias = NULL;
int paddr_num = 0;
if (request->qtype != DNS_T_AAAA) {
/* ignore non-matched query type */
return -1;
}
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],
addr[11], addr[12], addr[13], addr[14], addr[15]);
/* if domain is not match */
if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) {
return -1;
}
ip_check_result = _dns_server_process_ip_rule(request, addr, 16, DNS_T_AAAA, result_flag, &alias);
if (ip_check_result == 0) {
/* match */
@@ -3077,6 +3167,7 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque
unsigned char *paddr = paddrs[i];
if (atomic_read(&request->ip_map_num) == 0) {
request->has_ip = 1;
request->ip_addr_type = DNS_T_AAAA;
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) {
@@ -3117,6 +3208,176 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque
return 0;
}
static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request *request, const char *domain,
char *cname, unsigned int result_flag)
{
int ttl = 0;
unsigned char addr[4];
char name[DNS_MAX_CNAME_LEN] = {0};
if (request->qtype != DNS_T_A) {
/* ignore non-matched query type */
if (request->dualstack_selection == 0) {
return 0;
}
}
/* get A result */
dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
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) {
return -1;
}
_dns_server_request_get(request);
int ret = _dns_server_process_answer_A_IP(request, cname, addr, ttl, result_flag);
_dns_server_request_release(request);
return ret;
}
static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_request *request, const char *domain,
char *cname, unsigned int result_flag)
{
unsigned char addr[16];
char name[DNS_MAX_CNAME_LEN] = {0};
int ttl = 0;
if (request->qtype != DNS_T_AAAA) {
/* ignore non-matched query type */
return -1;
}
dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr);
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],
addr[11], addr[12], addr[13], addr[14], addr[15]);
/* if domain is not match */
if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) {
return -1;
}
_dns_server_request_get(request);
int ret = _dns_server_process_answer_AAAA_IP(request, cname, addr, ttl, result_flag);
_dns_server_request_release(request);
return ret;
}
static int _dns_server_process_answer_HTTPS(struct dns_rrs *rrs, struct dns_request *request, const char *domain,
char *cname, unsigned int result_flag)
{
int ttl = 0;
int ret = -1;
char name[DNS_MAX_CNAME_LEN] = {0};
char target[DNS_MAX_CNAME_LEN] = {0};
struct dns_https_param *p = NULL;
int priority = 0;
struct dns_request_https *https_svcb;
ret = dns_get_HTTPS_svcparm_start(rrs, &p, name, DNS_MAX_CNAME_LEN, &ttl, &priority, target, DNS_MAX_CNAME_LEN);
if (ret != 0) {
tlog(TLOG_WARN, "get HTTPS svcparm failed");
return -1;
}
https_svcb = request->https_svcb;
if (https_svcb == 0) {
/* ignore non-matched query type */
tlog(TLOG_WARN, "https svcb not set");
return -1;
}
tlog(TLOG_DEBUG, "domain: %s HTTPS: %s TTL: %d priority: %d", name, target, ttl, priority);
https_svcb->ttl = ttl;
https_svcb->priority = priority;
safe_strncpy(https_svcb->target, target, sizeof(https_svcb->target));
safe_strncpy(https_svcb->domain, name, sizeof(https_svcb->domain));
request->ip_ttl = ttl;
_dns_server_request_get(request);
for (; p; p = dns_get_HTTPS_svcparm_next(rrs, p)) {
switch (p->key) {
case DNS_HTTPS_T_MANDATORY: {
} break;
case DNS_HTTPS_T_ALPN: {
memcpy(https_svcb->alpn, p->value, sizeof(https_svcb->alpn));
https_svcb->alpn_len = p->len;
} break;
case DNS_HTTPS_T_NO_DEFAULT_ALPN: {
} break;
case DNS_HTTPS_T_PORT: {
int port = *(unsigned short *)(p->value);
https_svcb->port = ntohs(port);
} break;
case DNS_HTTPS_T_IPV4HINT: {
struct dns_rule_address_IPV4 *address_ipv4 = NULL;
if (_dns_server_is_return_soa_qtype(request, DNS_T_A)) {
break;
}
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_ADDR) == 0) {
break;
}
address_ipv4 = _dns_server_get_dns_rule(request, DOMAIN_RULE_ADDRESS_IPV4);
if (address_ipv4 != NULL) {
memcpy(request->ip_addr, address_ipv4->ipv4_addr, DNS_RR_A_LEN);
request->has_ip = 1;
request->ip_addr_type = DNS_T_A;
break;
}
for (int k = 0; k < p->len / 4; k++) {
_dns_server_process_answer_A_IP(request, cname, p->value + k * 4, ttl, result_flag);
}
} break;
case DNS_HTTPS_T_ECH: {
if (p->len > sizeof(https_svcb->ech)) {
tlog(TLOG_WARN, "ech too long");
break;
}
memcpy(https_svcb->ech, p->value, p->len);
https_svcb->ech_len = p->len;
} break;
case DNS_HTTPS_T_IPV6HINT: {
struct dns_rule_address_IPV6 *address_ipv6 = NULL;
if (_dns_server_is_return_soa_qtype(request, DNS_T_AAAA)) {
break;
}
if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_ADDR) == 0) {
break;
}
address_ipv6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_ADDRESS_IPV6);
if (address_ipv6 != NULL) {
memcpy(request->ip_addr, address_ipv6->ipv6_addr, DNS_RR_AAAA_LEN);
request->has_ip = 1;
request->ip_addr_type = DNS_T_AAAA;
break;
}
for (int k = 0; k < p->len / 16; k++) {
_dns_server_process_answer_AAAA_IP(request, cname, p->value + k * 16, ttl, result_flag);
}
} break;
}
}
_dns_server_request_release(request);
return 0;
}
static int _dns_server_process_answer(struct dns_request *request, const char *domain, struct dns_packet *packet,
unsigned int result_flag)
{
@@ -3187,6 +3448,15 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d
request->ttl_cname = _dns_server_get_conf_ttl(request, ttl);
tlog(TLOG_DEBUG, "name: %s ttl: %d cname: %s\n", domain_name, ttl, cname);
} break;
case DNS_T_HTTPS: {
ret = _dns_server_process_answer_HTTPS(rrs, request, domain, cname, result_flag);
if (ret == -1) {
break;
} else if (ret == -2) {
continue;
}
request->rcode = packet->head.rcode;
} break;
case DNS_T_SOA: {
/* if DNS64 enabled, skip check SOA. */
if (_dns_server_is_dns64_request(request)) {
@@ -3199,7 +3469,8 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d
}
dns_get_SOA(rrs, name, 128, &ttl, &request->soa);
tlog(TLOG_DEBUG,
"domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, expire: "
"domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, "
"expire: "
"%d, minimum: %d",
domain, request->qtype, request->soa.mname, request->soa.rname, request->soa.serial,
request->soa.refresh, request->soa.retry, request->soa.expire, request->soa.minimum);
@@ -3311,7 +3582,8 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const
}
tlog(TLOG_DEBUG,
"domain: %s TTL: %d IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
"domain: %s TTL: %d IP: "
"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
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]);
@@ -3466,7 +3738,8 @@ static int _dns_server_get_answer(struct dns_server_post_context *context)
}
dns_get_SOA(rrs, name, 128, &ttl, &request->soa);
tlog(TLOG_DEBUG,
"domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, expire: "
"domain: %s, qtype: %d, SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, "
"expire: "
"%d, minimum: %d",
request->domain, request->qtype, request->soa.mname, request->soa.rname, request->soa.serial,
request->soa.refresh, request->soa.retry, request->soa.expire, request->soa.minimum);
@@ -3901,7 +4174,8 @@ static int _dns_server_process_local_ptr(struct dns_request *request)
} else {
addr = addr_in6->sin6_addr.s6_addr;
snprintf(reverse_addr, sizeof(reverse_addr),
"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%"
"x.%x.%x."
"%x.ip6.arpa",
addr[15] & 0xF, (addr[15] >> 4) & 0xF, addr[14] & 0xF, (addr[14] >> 4) & 0xF, addr[13] & 0xF,
(addr[13] >> 4) & 0xF, addr[12] & 0xF, (addr[12] >> 4) & 0xF, addr[11] & 0xF,
@@ -4268,6 +4542,13 @@ static int _dns_server_pre_process_rule_flags(struct dns_request *request)
request->dualstack_selection = 0;
}
break;
case DNS_T_HTTPS:
if (_dns_server_is_return_soa_qtype(request, DNS_T_A) && _dns_server_is_return_soa_qtype(request, DNS_T_AAAA)) {
/* return SOA for HTTPS request */
rcode = DNS_RC_NXDOMAIN;
goto soa;
}
break;
default:
goto out;
break;
@@ -4796,6 +5077,19 @@ errout:
return -1;
}
static int _dns_server_process_https_svcb(struct dns_request *request)
{
if (request->https_svcb == NULL) {
request->https_svcb = malloc(sizeof(*request->https_svcb));
if (request->https_svcb == NULL) {
return -1;
}
memset(request->https_svcb, 0, sizeof(*request->https_svcb));
}
return 0;
}
static int _dns_server_qtype_soa(struct dns_request *request)
{
if (request->skip_qtype_soa || dns_qtype_soa_table == NULL) {
@@ -5002,6 +5296,10 @@ static int _dns_server_process_cache(struct dns_request *request)
goto reply_cache;
}
if (request->qtype == DNS_T_HTTPS) {
goto reply_cache;
}
if (request->dualstack_selection) {
int dualstack_qtype = 0;
if (request->qtype == DNS_T_A) {
@@ -5234,11 +5532,11 @@ static int _dns_server_process_special_query(struct dns_request *request)
/* pass to upstream server */
request->passthrough = 1;
}
case DNS_T_HTTPS:
break;
case DNS_T_A:
break;
case DNS_T_AAAA:
break;
default:
tlog(TLOG_DEBUG, "unsupported qtype: %d, domain: %s", request->qtype, request->domain);
@@ -5285,7 +5583,8 @@ static void _dns_server_check_set_passthrough(struct dns_request *request)
request->dualstack_selection = 0;
}
if (request->passthrough == 1 && (request->qtype == DNS_T_A || request->qtype == DNS_T_AAAA)) {
if (request->passthrough == 1 &&
(request->qtype == DNS_T_A || request->qtype == DNS_T_AAAA || request->qtype == DNS_T_HTTPS)) {
request->passthrough = 2;
}
}
@@ -5506,6 +5805,10 @@ static int _dns_server_do_query(struct dns_request *request, int skip_notify_eve
goto clean_exit;
}
if (_dns_server_process_https_svcb(request) != 0) {
goto clean_exit;
}
// setup options
_dns_server_setup_query_option(request, &options);
@@ -5638,7 +5941,8 @@ static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *in
}
tlog(TLOG_DEBUG,
"request qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d, id = %d, tc = %d, rd = %d, ra = "
"request qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d, id = %d, tc = %d, rd = %d, "
"ra = "
"%d, rcode = %d\n",
packet->head.qdcount, packet->head.ancount, packet->head.nscount, packet->head.nrcount, inpacket_len,
packet->head.id, packet->head.tc, packet->head.rd, packet->head.ra, packet->head.rcode);

266
test/cases/test-https.cc Normal file
View File

@@ -0,0 +1,266 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* 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 "util.h"
#include "gtest/gtest.h"
#include <fstream>
class HTTPS : public ::testing::Test
{
protected:
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(HTTPS, ipv4_speed_prefer)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_HTTPS) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_packet *packet = request->response_packet;
struct dns_rr_nested svcparam_buffer;
dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 3, 1, "b.com");
const char alph[] = "\x02h2\x05h3-19";
int alph_len = sizeof(alph) - 1;
dns_HTTPS_add_alpn(&svcparam_buffer, alph, alph_len);
dns_HTTPS_add_port(&svcparam_buffer, 443);
unsigned char add_v4[] = {1, 2, 3, 4};
unsigned char *addr[1] = {add_v4};
dns_HTTPS_add_ipv4hint(&svcparam_buffer, addr, 1);
unsigned char ech[] = {0x00, 0x45, 0xfe, 0x0d, 0x00};
dns_HTTPS_add_ech(&svcparam_buffer, (void *)ech, sizeof(ech));
unsigned char add_v6[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
addr[0] = add_v6;
dns_HTTPS_add_ipv6hint(&svcparam_buffer, addr, 1);
dns_add_HTTPS_end(&svcparam_buffer);
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10);
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-console yes
dualstack-ip-selection no
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com HTTPS", 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].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=");
}
TEST_F(HTTPS, ipv6_speed_prefer)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_HTTPS) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_packet *packet = request->response_packet;
struct dns_rr_nested svcparam_buffer;
dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 3, 1, "b.com");
const char alph[] = "\x02h2\x05h3-19";
int alph_len = sizeof(alph) - 1;
dns_HTTPS_add_alpn(&svcparam_buffer, alph, alph_len);
dns_HTTPS_add_port(&svcparam_buffer, 443);
unsigned char add_v4[] = {1, 2, 3, 4};
unsigned char *addr[1] = {add_v4};
dns_HTTPS_add_ipv4hint(&svcparam_buffer, addr, 1);
unsigned char ech[] = {0x00, 0x45, 0xfe, 0x0d, 0x00};
dns_HTTPS_add_ech(&svcparam_buffer, (void *)ech, sizeof(ech));
unsigned char add_v6[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
addr[0] = add_v6;
dns_HTTPS_add_ipv6hint(&svcparam_buffer, addr, 1);
dns_add_HTTPS_end(&svcparam_buffer);
return smartdns::SERVER_REQUEST_OK;
});
server.MockPing(PING_TYPE_ICMP, "102:304:506:708:90a:b0c:d0e:f10", 60, 10);
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-console yes
dualstack-ip-selection no
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com HTTPS", 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].GetTTL(), 3);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "HTTPS");
EXPECT_EQ(client.GetAnswer()[0].GetData(),
"1 b.com. alpn=\"h2,h3-19\" port=443 ech=AEX+DQA= ipv6hint=102:304:506:708:90a:b0c:d0e:f10");
}
TEST_F(HTTPS, ipv4_SOA)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_HTTPS) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_packet *packet = request->response_packet;
struct dns_rr_nested svcparam_buffer;
dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 3, 1, "a.com");
const char alph[] = "\x02h2\x05h3-19";
int alph_len = sizeof(alph) - 1;
dns_HTTPS_add_alpn(&svcparam_buffer, alph, alph_len);
dns_HTTPS_add_port(&svcparam_buffer, 443);
unsigned char add_v4[] = {1, 2, 3, 4};
unsigned char *addr[1] = {add_v4};
dns_HTTPS_add_ipv4hint(&svcparam_buffer, addr, 1);
unsigned char ech[] = {0x00, 0x45, 0xfe, 0x0d, 0x00};
dns_HTTPS_add_ech(&svcparam_buffer, (void *)ech, sizeof(ech));
unsigned char add_v6[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
addr[0] = add_v6;
dns_HTTPS_add_ipv6hint(&svcparam_buffer, addr, 1);
dns_add_HTTPS_end(&svcparam_buffer);
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-console yes
dualstack-ip-selection no
address /a.com/#4
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com HTTPS", 61053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
auto result_check = client.GetAnswer()[0].GetData();
ASSERT_TRUE(client.Query("a.com HTTPS", 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].GetTTL(), 3);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "HTTPS");
EXPECT_EQ(client.GetAnswer()[0].GetData(),
"1 a.com. alpn=\"h2,h3-19\" port=443 ech=AEX+DQA= ipv6hint=102:304:506:708:90a:b0c:d0e:f10");
}
TEST_F(HTTPS, ipv6_SOA)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_HTTPS) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_packet *packet = request->response_packet;
struct dns_rr_nested svcparam_buffer;
dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 3, 1, "a.com");
const char alph[] = "\x02h2\x05h3-19";
int alph_len = sizeof(alph) - 1;
dns_HTTPS_add_alpn(&svcparam_buffer, alph, alph_len);
dns_HTTPS_add_port(&svcparam_buffer, 443);
unsigned char add_v4[] = {1, 2, 3, 4};
unsigned char *addr[1] = {add_v4};
dns_HTTPS_add_ipv4hint(&svcparam_buffer, addr, 1);
unsigned char ech[] = {0x00, 0x45, 0xfe, 0x0d, 0x00};
dns_HTTPS_add_ech(&svcparam_buffer, (void *)ech, sizeof(ech));
unsigned char add_v6[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
addr[0] = add_v6;
dns_HTTPS_add_ipv6hint(&svcparam_buffer, addr, 1);
dns_add_HTTPS_end(&svcparam_buffer);
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-console yes
dualstack-ip-selection no
address /a.com/#6
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com HTTPS", 61053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAnswerNum(), 1);
auto result_check = client.GetAnswer()[0].GetData();
ASSERT_TRUE(client.Query("a.com HTTPS", 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].GetTTL(), 3);
EXPECT_EQ(client.GetAnswer()[0].GetType(), "HTTPS");
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, SOA)
{
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
address /a.com/#6
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com HTTPS", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAuthorityNum(), 1);
EXPECT_EQ(client.GetStatus(), "NXDOMAIN");
EXPECT_EQ(client.GetAuthority()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAuthority()[0].GetTTL(), 60);
EXPECT_EQ(client.GetAuthority()[0].GetType(), "SOA");
}

View File

@@ -255,3 +255,55 @@ cache-persist no)""");
EXPECT_EQ(client.GetAuthority()[0].GetTTL(), 30);
EXPECT_EQ(client.GetAuthority()[0].GetType(), "SOA");
}
TEST_F(QtypeSOA, HTTPS_SOA)
{
smartdns::MockServer server_upstream;
smartdns::Server server;
std::map<int, int> qid_map;
server_upstream.Start("udp://0.0.0.0:61053", [&](struct smartdns::ServerRequestContext *request) {
if (request->qtype != DNS_T_HTTPS) {
return smartdns::SERVER_REQUEST_SOA;
}
struct dns_packet *packet = request->response_packet;
struct dns_rr_nested svcparam_buffer;
dns_add_HTTPS_start(&svcparam_buffer, packet, DNS_RRS_AN, request->domain.c_str(), 3, 1, "a.com");
const char alph[] = "\x02h2\x05h3-19";
int alph_len = sizeof(alph) - 1;
dns_HTTPS_add_alpn(&svcparam_buffer, alph, alph_len);
dns_HTTPS_add_port(&svcparam_buffer, 443);
unsigned char add_v4[] = {1, 2, 3, 4};
unsigned char *addr[1] = {add_v4};
dns_HTTPS_add_ipv4hint(&svcparam_buffer, addr, 1);
unsigned char ech[] = {0x00, 0x45, 0xfe, 0x0d, 0x00};
dns_HTTPS_add_ech(&svcparam_buffer, (void *)ech, sizeof(ech));
unsigned char add_v6[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
addr[0] = add_v6;
dns_HTTPS_add_ipv6hint(&svcparam_buffer, addr, 1);
dns_add_HTTPS_end(&svcparam_buffer);
return smartdns::SERVER_REQUEST_OK;
});
server.Start(R"""(bind [::]:60053
server 127.0.0.1:61053
log-console yes
dualstack-ip-selection no
speed-check-mode none
address /a.com/#
log-level debug
cache-persist no)""");
smartdns::Client client;
ASSERT_TRUE(client.Query("a.com HTTPS", 60053));
std::cout << client.GetResult() << std::endl;
ASSERT_EQ(client.GetAuthorityNum(), 1);
EXPECT_EQ(client.GetStatus(), "NXDOMAIN");
EXPECT_EQ(client.GetAuthority()[0].GetName(), "a.com");
EXPECT_EQ(client.GetAuthority()[0].GetTTL(), 30);
EXPECT_EQ(client.GetAuthority()[0].GetType(), "SOA");
}