|
|
@@ -266,6 +266,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;
|
|
|
+ char ech[DNS_MAX_ECH_LEN];
|
|
|
+ int ech_len;
|
|
|
+};
|
|
|
+
|
|
|
struct dns_request {
|
|
|
atomic_t refcnt;
|
|
|
|
|
|
@@ -303,6 +315,8 @@ struct dns_request {
|
|
|
struct dns_opt_ecs ecs;
|
|
|
int edns0_do;
|
|
|
|
|
|
+ struct dns_request_https *https_svcb;
|
|
|
+
|
|
|
dns_result_callback result_callback;
|
|
|
void *user_ptr;
|
|
|
|
|
|
@@ -319,6 +333,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;
|
|
|
@@ -700,6 +715,16 @@ static int _dns_server_is_return_soa_qtype(struct dns_request *request, dns_type
|
|
|
return 0;
|
|
|
}
|
|
|
break;
|
|
|
+ case DNS_T_HTTPS:
|
|
|
+ if (flags & DOMAIN_FLAG_ADDR_HTTPS_SOA) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (flags & DOMAIN_FLAG_ADDR_HTTPS_IGN) {
|
|
|
+ request->skip_qtype_soa = 1;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
@@ -1072,6 +1097,64 @@ static int _dns_server_add_srv(struct dns_server_post_context *context)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+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_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;
|
|
|
@@ -1089,6 +1172,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);
|
|
|
@@ -1561,7 +1648,7 @@ static int _dns_server_request_update_cache(struct dns_request *request, int spe
|
|
|
int ttl = 0;
|
|
|
int ret = -1;
|
|
|
|
|
|
- if (qtype != DNS_T_A && qtype != DNS_T_AAAA) {
|
|
|
+ if (qtype != DNS_T_A && qtype != DNS_T_AAAA && qtype != DNS_T_HTTPS) {
|
|
|
goto errout;
|
|
|
}
|
|
|
|
|
|
@@ -1897,7 +1984,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);
|
|
|
}
|
|
|
|
|
|
@@ -1920,6 +2007,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 ipset_timeout_value, int nftset_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, ipset_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, ipset_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,
|
|
|
+ nftset_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, nftset_timeout_value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context *context)
|
|
|
{
|
|
|
int ttl = 0;
|
|
|
@@ -2048,21 +2176,8 @@ 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, ipset_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, nftset_timeout_value);
|
|
|
- }
|
|
|
+ _dns_server_add_ipset_nftset(request, rule, nftset_ip, addr, DNS_RR_A_LEN, ipset_timeout_value,
|
|
|
+ nftset_timeout_value);
|
|
|
} break;
|
|
|
case DNS_T_AAAA: {
|
|
|
unsigned char addr[16];
|
|
|
@@ -2073,26 +2188,44 @@ 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, ipset_timeout_value);
|
|
|
+ _dns_server_add_ipset_nftset(request, rule, nftset_ip6, addr, DNS_RR_AAAA_LEN, ipset_timeout_value,
|
|
|
+ nftset_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, nftset_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,
|
|
|
+ ipset_timeout_value, nftset_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,
|
|
|
+ ipset_timeout_value, nftset_timeout_value);
|
|
|
+ }
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
} break;
|
|
|
default:
|
|
|
@@ -2620,7 +2753,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)
|
|
|
{
|
|
|
@@ -2685,6 +2819,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);
|
|
|
atomic_dec(&server.request_num);
|
|
|
@@ -3014,6 +3151,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;
|
|
|
@@ -3031,7 +3169,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;
|
|
|
@@ -3049,6 +3187,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);
|
|
|
@@ -3057,7 +3196,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 {
|
|
|
@@ -3071,6 +3210,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);
|
|
|
@@ -3079,7 +3219,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;
|
|
|
}
|
|
|
}
|
|
|
@@ -3556,35 +3696,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 */
|
|
|
- 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 (strncasecmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncasecmp(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) {
|
|
|
@@ -3604,6 +3727,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 && request->conf->dns_force_no_cname == 0) {
|
|
|
@@ -3642,36 +3766,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 (strncasecmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncasecmp(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 */
|
|
|
@@ -3690,6 +3796,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 && request->conf->dns_force_no_cname == 0) {
|
|
|
@@ -3730,6 +3837,185 @@ 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) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 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 (strncasecmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncasecmp(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;
|
|
|
+ int no_ipv4 = 0;
|
|
|
+ int no_ipv6 = 0;
|
|
|
+ struct dns_https_record_rule *https_record_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_HTTPS);
|
|
|
+ if (https_record_rule) {
|
|
|
+ if (https_record_rule->filter.no_ipv4hint) {
|
|
|
+ no_ipv4 = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (https_record_rule->filter.no_ipv6hint) {
|
|
|
+ no_ipv6 = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) || no_ipv4 == 1) {
|
|
|
+ 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) || no_ipv6 == 1) {
|
|
|
+ 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)
|
|
|
{
|
|
|
@@ -3805,6 +4091,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)) {
|
|
|
@@ -3817,7 +4112,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);
|
|
|
@@ -3938,7 +4234,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]);
|
|
|
|
|
|
@@ -4098,7 +4395,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);
|
|
|
@@ -4920,6 +5218,10 @@ static void _dns_server_update_rule_by_flags(struct dns_request_domain_rule *req
|
|
|
request_domain_rule->rules[DOMAIN_RULE_ADDRESS_IPV6] = NULL;
|
|
|
}
|
|
|
|
|
|
+ if (flags & DOMAIN_FLAG_ADDR_HTTPS_IGN) {
|
|
|
+ request_domain_rule->rules[DOMAIN_RULE_HTTPS] = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
if (flags & DOMAIN_FLAG_IPSET_IGN) {
|
|
|
request_domain_rule->rules[DOMAIN_RULE_IPSET] = NULL;
|
|
|
}
|
|
|
@@ -5200,6 +5502,28 @@ static int _dns_server_pre_process_rule_flags(struct dns_request *request)
|
|
|
request->dualstack_selection = 0;
|
|
|
}
|
|
|
break;
|
|
|
+ case DNS_T_HTTPS:
|
|
|
+ if (flags & DOMAIN_FLAG_ADDR_HTTPS_IGN) {
|
|
|
+ /* ignore this domain for A request */
|
|
|
+ goto skip_soa_out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_dns_server_is_return_soa(request)) {
|
|
|
+ /* if HTTPS exists, return SOA with NOERROR*/
|
|
|
+ if (request->domain_rule.rules[DOMAIN_RULE_HTTPS] != NULL) {
|
|
|
+ goto soa;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ goto out;
|
|
|
+ break;
|
|
|
default:
|
|
|
goto out;
|
|
|
break;
|
|
|
@@ -5703,6 +6027,55 @@ errout:
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+static 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);
|
|
|
+
|
|
|
+ if (request->qtype != DNS_T_HTTPS) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (request->https_svcb != NULL) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ request->https_svcb = malloc(sizeof(*request->https_svcb));
|
|
|
+ if (request->https_svcb == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ memset(request->https_svcb, 0, sizeof(*request->https_svcb));
|
|
|
+
|
|
|
+ if (https_record_rule == NULL) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (https_record_rule->record.enable == 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ request->rcode = DNS_RC_NOERROR;
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
static int _dns_server_qtype_soa(struct dns_request *request)
|
|
|
{
|
|
|
if (request->skip_qtype_soa || request->conf->soa_table == NULL) {
|
|
|
@@ -6104,6 +6477,7 @@ 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_SVCB:
|
|
|
ret = _dns_server_process_svcb(request);
|
|
|
@@ -6163,7 +6537,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->edns0_do == 0) {
|
|
|
request->passthrough = 2;
|
|
|
}
|
|
|
@@ -6462,6 +6837,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;
|
|
|
+ }
|
|
|
+
|
|
|
if (_dns_server_process_smartdns_domain(request) == 0) {
|
|
|
goto clean_exit;
|
|
|
}
|
|
|
@@ -6700,7 +7079,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);
|