From 27c1aedd3ba2f59368c3d28cdadcf8d82a1483a6 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Wed, 18 Oct 2023 19:43:01 +0800 Subject: [PATCH] http-svcb: add https svcb RR suppport --- src/dns_server.c | 484 ++++++++++++++++++++++++++++------- test/cases/test-https.cc | 266 +++++++++++++++++++ test/cases/test-qtype-soa.cc | 52 ++++ 3 files changed, 712 insertions(+), 90 deletions(-) create mode 100644 test/cases/test-https.cc diff --git a/src/dns_server.c b/src/dns_server.c index 159bc20..0eeb8bd 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -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(¶m, 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(¶m, https_svcb->alpn, https_svcb->alpn_len); + if (ret != 0) { + return ret; + } + } + + if (https_svcb->port != 0) { + ret = dns_HTTPS_add_port(¶m, 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(¶m, addr, 1); + } + } + + if (https_svcb->ech[0] != 0 && https_svcb->ech_len > 0) { + ret = dns_HTTPS_add_ech(¶m, 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(¶m, addr, 1); + } + } + + dns_add_HTTPS_end(¶m); + 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); diff --git a/test/cases/test-https.cc b/test/cases/test-https.cc new file mode 100644 index 0000000..0086ef1 --- /dev/null +++ b/test/cases/test-https.cc @@ -0,0 +1,266 @@ +/************************************************************************* + * + * Copyright (C) 2018-2023 Ruilin Peng (Nick) . + * + * smartdns is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * smartdns is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "client.h" +#include "dns.h" +#include "include/utils.h" +#include "server.h" +#include "util.h" +#include "gtest/gtest.h" +#include + +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"); +} diff --git a/test/cases/test-qtype-soa.cc b/test/cases/test-qtype-soa.cc index 82298e2..d9bfe6b 100644 --- a/test/cases/test-qtype-soa.cc +++ b/test/cases/test-qtype-soa.cc @@ -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 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"); +}