From a19ac7eb075a772dd6981ab40d7b3f524587fc3d Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Sat, 16 Dec 2023 18:47:46 +0800 Subject: [PATCH] dns-server: simple support proxy dnssec --- src/dns.c | 27 ++++++++++++++-- src/dns.h | 10 ++++++ src/dns_client.c | 18 ++++++++++- src/dns_client.h | 1 + src/dns_server.c | 82 ++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/src/dns.c b/src/dns.c index d4c26062bb..7746d216c0 100644 --- a/src/dns.c +++ b/src/dns.c @@ -34,6 +34,9 @@ #define TC_MASK 0x0200 #define RD_MASK 0x0100 #define RA_MASK 0x0080 +#define Z_MASK 0x0040 +#define AD_MASK 0x0020 +#define CD_MASK 0x0010 #define RCODE_MASK 0x000F #define DNS_RR_END (0XFFFF) @@ -1011,6 +1014,17 @@ int dns_get_OPT_payload_size(struct dns_packet *packet) return packet->payloadsize; } +int dns_set_OPT_option(struct dns_packet *packet, unsigned int option) +{ + packet->opt_option = option; + return 0; +} + +unsigned int dns_get_OPT_option(struct dns_packet *packet) +{ + return packet->opt_option; +} + int dns_add_OPT_ECS(struct dns_packet *packet, struct dns_opt_ecs *ecs) { unsigned char opt_data[DNS_MAX_OPT_LEN]; @@ -1402,6 +1416,9 @@ static int _dns_decode_head(struct dns_context *context) head->tc = (fields & TC_MASK) >> 9; head->rd = (fields & RD_MASK) >> 8; head->ra = (fields & RA_MASK) >> 7; + head->z = (fields & Z_MASK) >> 6; + head->ad = (fields & AD_MASK) >> 5; + head->cd = (fields & CD_MASK) >> 4; head->rcode = (fields & RCODE_MASK) >> 0; head->qdcount = _dns_read_short(&context->ptr); head->ancount = _dns_read_short(&context->ptr); @@ -1429,6 +1446,9 @@ static int _dns_encode_head(struct dns_context *context) fields |= (head->tc << 9) & TC_MASK; fields |= (head->rd << 8) & RD_MASK; fields |= (head->ra << 7) & RA_MASK; + fields |= (head->z << 6) & Z_MASK; + fields |= (head->ad << 5) & AD_MASK; + fields |= (head->cd << 4) & CD_MASK; fields |= (head->rcode << 0) & RCODE_MASK; _dns_write_short(&context->ptr, fields); @@ -1976,7 +1996,7 @@ static int _dns_encode_opts(struct dns_packet *packet, struct dns_context *conte int i = 0; int len = 0; int ret = 0; - unsigned int rcode = 0; + unsigned int rcode = packet->opt_option; int rr_len = 0; int payloadsize = packet->payloadsize; unsigned char *rr_len_ptr = NULL; @@ -2429,7 +2449,7 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type) tlog(TLOG_DEBUG, "opt length mismatch, %s\n", domain); return -1; } - + dns_set_OPT_option(packet, ttl); dns_set_OPT_payload_size(packet, qclass); } break; case DNS_T_HTTPS: { @@ -2680,6 +2700,9 @@ int dns_packet_init(struct dns_packet *packet, int size, struct dns_head *head) init_head->tc = head->tc; init_head->rd = head->rd; init_head->ra = head->ra; + init_head->z = head->z; + init_head->ad = head->ad; + init_head->cd = head->cd; init_head->rcode = head->rcode; packet->questions = DNS_RR_END; packet->answers = DNS_RR_END; diff --git a/src/dns.h b/src/dns.h index c6cfec93a7..268c9f8f4a 100644 --- a/src/dns.h +++ b/src/dns.h @@ -33,6 +33,8 @@ extern "C" { #define DNS_MAX_ALPN_LEN 32 #define DNS_MAX_ECH_LEN 256 +#define DNS_OPT_FLAG_DO 0x8000 + #define DNS_ADDR_FAMILY_IP 1 #define DNS_ADDR_FAMILY_IPV6 2 @@ -132,6 +134,10 @@ struct dns_head { unsigned char tc; /* Truncation Flag */ unsigned char rd; /* Recursion Desired */ unsigned char ra; /* Recursion Available */ + unsigned char z; /* Reserved for future use. Must be Zero! */ + unsigned char ad; /* Authentic Data Flag */ + unsigned char cd; /* Checking Disabled Flag */ + unsigned char padding; /* Padding */ unsigned short rcode; /* Response Code */ unsigned short qdcount; /* number of question entries */ unsigned short ancount; /* number of answer entries */ @@ -160,6 +166,7 @@ struct dns_packet { unsigned short optcount; unsigned short optional; unsigned short payloadsize; + unsigned int opt_option; struct dns_packet_dict namedict; int size; int len; @@ -276,6 +283,9 @@ int dns_get_SOA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, struct int dns_add_NS(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, const char *cname); int dns_get_NS(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size); +int dns_set_OPT_option(struct dns_packet *packet, unsigned int option); +unsigned int dns_get_OPT_option(struct dns_packet *packet); + int dns_set_OPT_payload_size(struct dns_packet *packet, int payload_size); int dns_get_OPT_payload_size(struct dns_packet *packet); diff --git a/src/dns_client.c b/src/dns_client.c index 1e6cb6018c..95237d5e82 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -265,6 +265,9 @@ struct dns_query_struct { /* ECS */ struct dns_client_ecs ecs; + /* EDNS0_DO */ + int edns0_do; + /* replied hash table */ DECLARE_HASHTABLE(replied_map, 4); }; @@ -2460,7 +2463,8 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf } #endif - tlog(TLOG_WARN, "SSL read fail error no: %s(%lx), reason: %d\n", ERR_reason_error_string(ssl_err), ssl_err, ssl_reason); + tlog(TLOG_WARN, "SSL read fail error no: %s(%lx), reason: %d\n", ERR_reason_error_string(ssl_err), ssl_err, + ssl_reason); errno = EFAULT; ret = -1; break; @@ -3430,6 +3434,7 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info, head.aa = 0; head.rd = 1; head.ra = 0; + head.ad = query->edns0_do; head.rcode = 0; if (dns_packet_init(packet, DNS_PACKSIZE, &head) != 0) { @@ -3449,6 +3454,9 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info, } dns_set_OPT_payload_size(packet, DNS_IN_PACKSIZE); + if (query->edns0_do) { + dns_set_OPT_option(packet, DNS_OPT_FLAG_DO); + } if (server_info->type != DNS_SERVER_UDP) { dns_add_OPT_TCP_KEEPALIVE(packet, 6000); @@ -3651,6 +3659,7 @@ static int _dns_client_send_query(struct dns_query_struct *query) head.aa = 0; head.rd = 1; head.ra = 0; + head.ad = query->edns0_do; head.rcode = 0; if (dns_packet_init(packet, DNS_PACKSIZE, &head) != 0) { @@ -3665,6 +3674,9 @@ static int _dns_client_send_query(struct dns_query_struct *query) } dns_set_OPT_payload_size(packet, DNS_IN_PACKSIZE); + if (query->edns0_do) { + dns_set_OPT_option(packet, DNS_OPT_FLAG_DO); + } /* dns_add_OPT_TCP_KEEPALIVE(packet, 1200); */ if (_dns_client_dns_add_ecs(query, packet) != 0) { tlog(TLOG_ERROR, "add ecs failed."); @@ -3777,6 +3789,10 @@ static int _dns_client_query_parser_options(struct dns_query_struct *query, stru _dns_client_query_setup_default_ecs(query); } + if (options->enable_flag & DNS_QUEY_OPTION_EDNS0_DO) { + query->edns0_do = 1; + } + return 0; } diff --git a/src/dns_client.h b/src/dns_client.h index f3a22326fd..aeb132e279 100644 --- a/src/dns_client.h +++ b/src/dns_client.h @@ -49,6 +49,7 @@ typedef enum dns_result_type { #define DNS_QUEY_OPTION_ECS_DNS (1 << 0) #define DNS_QUEY_OPTION_ECS_IP (1 << 1) +#define DNS_QUEY_OPTION_EDNS0_DO (1 << 2) int dns_client_init(void); diff --git a/src/dns_server.c b/src/dns_server.c index 2a0b9206f5..6a974d6d49 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -76,6 +76,7 @@ #define PREFETCH_FLAGS_NO_DUALSTACK (1 << 0) #define PREFETCH_FLAGS_EXPIRED (1 << 1) +#define PREFETCH_FLAGS_NOPREFETCH (1 << 2) #define RECV_ERROR_AGAIN 1 #define RECV_ERROR_OK 0 @@ -256,6 +257,7 @@ struct dns_request { struct sockaddr_storage localaddr; int has_ecs; struct dns_opt_ecs ecs; + int edns0_do; dns_result_callback result_callback; void *user_ptr; @@ -422,21 +424,6 @@ static void *_dns_server_get_bind_ipset_nftset_rule(struct dns_request *request, return NULL; } -static int _dns_server_get_reply_ttl(struct dns_request *request, int ttl) -{ - int reply_ttl = ttl; - - if ((request->passthrough == 0 || request->passthrough == 2) && dns_conf_cachesize > 0 && - request->check_order_list->orders[0].type != DOMAIN_CHECK_NONE) { - reply_ttl = dns_conf_serve_expired_reply_ttl; - if (reply_ttl < 2) { - reply_ttl = 2; - } - } - - return reply_ttl; -} - static int _dns_server_get_conf_ttl(struct dns_request *request, int ttl) { int rr_ttl = dns_conf_rr_ttl; @@ -483,6 +470,26 @@ static int _dns_server_get_conf_ttl(struct dns_request *request, int ttl) return ttl; } +static int _dns_server_get_reply_ttl(struct dns_request *request, int ttl) +{ + int reply_ttl = ttl; + + if ((request->passthrough == 0 || request->passthrough == 2) && dns_conf_cachesize > 0 && + request->check_order_list->orders[0].type != DOMAIN_CHECK_NONE) { + reply_ttl = dns_conf_serve_expired_reply_ttl; + if (reply_ttl < 2) { + reply_ttl = 2; + } + } + + int rr_ttl = _dns_server_get_conf_ttl(request, ttl); + if (reply_ttl > rr_ttl) { + reply_ttl = rr_ttl; + } + + return reply_ttl; +} + static int _dns_server_epoll_ctl(struct dns_server_conn_head *head, int op, uint32_t events) { struct epoll_event event; @@ -1396,12 +1403,25 @@ static int _dns_cache_is_specify_packet(int qtype) static int _dns_server_get_cache_timeout(struct dns_request *request, struct dns_cache_key *cache_key, int ttl) { int timeout = 0; + int prefetch_time = 0; if (request->rcode != DNS_RC_NOERROR) { return ttl + 1; } if (dns_conf_prefetch && _dns_cache_is_specify_packet(request->qtype) != 0) { + prefetch_time = 1; + } + + if ((request->prefetch_flags & PREFETCH_FLAGS_NOPREFETCH)) { + prefetch_time = 0; + } + + if (request->edns0_do == 1) { + prefetch_time = 0; + } + + if (prefetch_time == 1) { if (dns_conf_serve_expired) { timeout = dns_conf_serve_expired_prefetch_time; if (timeout == 0) { @@ -1431,6 +1451,8 @@ static int _dns_server_get_cache_timeout(struct dns_request *request, struct dns if (dns_conf_serve_expired) { timeout += dns_conf_serve_expired_ttl; } + + timeout += 3; } if (timeout <= 0) { @@ -5066,6 +5088,13 @@ static int _dns_server_process_cache_packet(struct dns_request *request, struct goto out; } + /* Check if records in cache contain DNSSEC, if not exist, skip cache */ + if (request->passthrough == 1) { + if ((dns_get_OPT_option(context.packet) & DNS_OPT_FLAG_DO) == 0 && request->edns0_do == 1) { + goto out; + } + } + request->rcode = context.packet->head.rcode; context.do_cache = 0; context.do_ipset = do_ipset; @@ -5173,6 +5202,7 @@ static int _dns_server_process_cache(struct dns_request *request) out_update_cache: if (dns_cache_get_ttl(dns_cache) == 0) { struct dns_server_query_option dns_query_options; + int prefetch_flags = 0; dns_query_options.server_flags = request->server_flags; dns_query_options.dns_group_name = request->dns_group_name; if (request->conn == NULL) { @@ -5186,7 +5216,12 @@ static int _dns_server_process_cache(struct dns_request *request) memcpy(&dns_query_options.ecs_dns, &request->ecs, sizeof(dns_query_options.ecs_dns)); } - _dns_server_prefetch_request(request->domain, request->qtype, &dns_query_options, 0); + if (request->edns0_do) { + dns_query_options.ecs_enable_flag |= DNS_QUEY_OPTION_EDNS0_DO; + prefetch_flags |= PREFETCH_FLAGS_NOPREFETCH; + } + + _dns_server_prefetch_request(request->domain, request->qtype, &dns_query_options, prefetch_flags); } else { dns_cache_update(dns_cache); } @@ -5407,7 +5442,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->edns0_do == 0) { request->passthrough = 2; } } @@ -5485,6 +5521,10 @@ static int _dns_server_setup_query_option(struct dns_request *request, struct dn options->enable_flag |= DNS_QUEY_OPTION_ECS_DNS; } + if (request->edns0_do) { + options->enable_flag |= DNS_QUEY_OPTION_EDNS0_DO; + } + return 0; } @@ -5718,6 +5758,10 @@ static int _dns_server_parser_request(struct dns_request *request, struct dns_pa goto errout; } + if ((dns_get_OPT_option(packet) & DNS_OPT_FLAG_DO) && packet->head.ad == 1) { + request->edns0_do = 1; + } + /* get request opts */ rr_count = 0; rrs = dns_get_rrs_start(packet, DNS_RRS_OPT, &rr_count); @@ -5836,6 +5880,10 @@ static int _dns_server_setup_server_query_options(struct dns_request *request, memcpy(&request->ecs, &server_query_option->ecs_dns, sizeof(request->ecs)); } + if (server_query_option->ecs_enable_flag & DNS_QUEY_OPTION_EDNS0_DO) { + request->edns0_do = 1; + } + return 0; }