From: Evan Hunt Date: Wed, 19 Nov 2025 07:29:12 +0000 (-0800) Subject: add dns_message API to add EDNS options X-Git-Tag: v9.21.16~37^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d3439ee028d48f0ede358e6e6477ff2da7c6216;p=thirdparty%2Fbind9.git add dns_message API to add EDNS options The new dns_message_ednsinit() and dns_message_ednsaddopt() functions allow EDNS options to be added to a message one at a time; it is no longer necessary to construct a full array of EDNS options and set them all at once. This allows us to simplify EDNS option handling code, and in the future it wlil allow plugins to add EDNS options to existing messages. --- diff --git a/bin/delv/delv.c b/bin/delv/delv.c index f462a13c165..a4c262b53e3 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -2085,8 +2085,8 @@ sendquery(void *arg) { mrdataset = NULL; mname = NULL; - CHECK(dns_message_buildopt(message, &opt, 0, 0, DNS_MESSAGEEXTFLAG_DO, - NULL, 0)); + dns_message_ednsinit(message, 0, 0, DNS_MESSAGEEXTFLAG_DO, 0); + CHECK(dns_message_buildopt(message, &opt)); CHECK(dns_message_setopt(message, opt)); CHECK(dns_requestmgr_create(isc_g_mctx, dispatchmgr, NULL, NULL, diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 12209c8e6a7..1499761f789 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -1437,25 +1437,6 @@ save_opt(dig_lookup_t *lookup, char *code, char *value) { lookup->ednsoptscnt++; } -/*% - * Add EDNS0 option record to a message. Currently, the only supported - * options are UDP buffer size, the DO bit, and EDNS options - * (e.g., NSID, COOKIE, client-subnet) - */ -static void -add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags, - dns_ednsopt_t *opts, size_t count) { - dns_rdataset_t *rdataset = NULL; - isc_result_t result; - - debug("add_opt()"); - result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags, - opts, count); - check_result(result, "dns_message_buildopt"); - result = dns_message_setopt(msg, rdataset); - check_result(result, "dns_message_setopt"); -} - /*% * Add a question section to a message, asking for the specified name, * type, and class. @@ -2134,6 +2115,7 @@ setup_lookup(dig_lookup_t *lookup) { char cookiebuf[256]; char *origin = NULL; char *textname = NULL; + dns_rdataset_t *rdataset = NULL; REQUIRE(lookup != NULL); @@ -2395,17 +2377,19 @@ setup_lookup(dig_lookup_t *lookup) { if (lookup->udpsize > -1 || lookup->dnssec || lookup->edns > -1 || lookup->ecs_addr != NULL) { -#define MAXOPTS (EDNSOPT_OPTIONS + DNS_EDNSOPTIONS) - dns_ednsopt_t opts[MAXOPTS]; - unsigned int flags; - unsigned int i = 0; + dns_ednsopt_t option; - /* - * There can't be more than MAXOPTS options to send: - * a maximum of EDNSOPT_OPTIONS set by +ednsopt - * and DNS_EDNSOPTIONS set by other arguments - * (+nsid, +cookie, etc). - */ + /* Set the EDNS flags */ + unsigned int flags = lookup->ednsflags; + flags &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO); + if (lookup->dnssec) { + flags |= DNS_MESSAGEEXTFLAG_DO; + } + if (lookup->coflag) { + flags |= DNS_MESSAGEEXTFLAG_CO; + } + + /* Set the EDNS UDP size */ if (lookup->udpsize < 0) { lookup->udpsize = DEFAULT_EDNS_BUFSIZE; } @@ -2414,21 +2398,33 @@ setup_lookup(dig_lookup_t *lookup) { DEFAULT_EDNS_VERSION; } + /* + * Initialize EDNS in the message. + * + * Allow space for EDNSOPT_OPTIONS options to be + * set by +ednsopt, plus DNS_EDNSOPTIONS to be set + * by other arguments (+nsid, +cookie, etc). + */ + constexpr size_t MAXOPTS = + (EDNSOPT_OPTIONS + DNS_EDNS_MAX_OPTIONS); + + dns_message_ednsinit(lookup->sendmsg, lookup->edns, + lookup->udpsize, flags, MAXOPTS); + if (lookup->nsid) { - INSIST(i < MAXOPTS); - opts[i].code = DNS_OPT_NSID; - opts[i].length = 0; - opts[i].value = NULL; - i++; + option = (dns_ednsopt_t){ .code = DNS_OPT_NSID }; + result = dns_message_ednsaddopt(lookup->sendmsg, + &option); + check_result(result, "dns_message_ednsaddopt"); } if (lookup->ecs_addr != NULL) { uint8_t addr[16]; uint16_t family = 0; uint32_t plen; - struct sockaddr *sa; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; + struct sockaddr *sa = NULL; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; size_t addrl; sa = &lookup->ecs_addr->type.sa; @@ -2437,10 +2433,8 @@ setup_lookup(dig_lookup_t *lookup) { /* Round up prefix len to a multiple of 8 */ addrl = (plen + 7) / 8; - INSIST(i < MAXOPTS); - opts[i].code = DNS_OPT_CLIENT_SUBNET; - opts[i].length = (uint16_t)addrl + 4; - check_result(result, "isc_buffer_allocate"); + option.code = DNS_OPT_CLIENT_SUBNET; + option.length = (uint16_t)addrl + 4; /* * XXXMUKS: According to RFC7871, "If there is @@ -2502,85 +2496,78 @@ setup_lookup(dig_lookup_t *lookup) { (unsigned int)addrl); } - opts[i].value = (uint8_t *)ecsbuf; - i++; + option.value = (uint8_t *)ecsbuf; + result = dns_message_ednsaddopt(lookup->sendmsg, + &option); + check_result(result, "dns_message_ednsaddopt"); } if (lookup->sendcookie) { - INSIST(i < MAXOPTS); - opts[i].code = DNS_OPT_COOKIE; + option.code = DNS_OPT_COOKIE; if (lookup->cookie != NULL) { isc_buffer_init(&b, cookiebuf, sizeof(cookiebuf)); result = isc_hex_decodestring(lookup->cookie, &b); check_result(result, "isc_hex_decodestring"); - opts[i].value = isc_buffer_base(&b); - opts[i].length = isc_buffer_usedlength(&b); + option.value = isc_buffer_base(&b); + option.length = isc_buffer_usedlength(&b); } else { compute_cookie(cookie, sizeof(cookie)); - opts[i].length = 8; - opts[i].value = cookie; + option.length = 8; + option.value = cookie; } - i++; + + result = dns_message_ednsaddopt(lookup->sendmsg, + &option); + check_result(result, "dns_message_ednsaddopt"); } if (lookup->expire) { - INSIST(i < MAXOPTS); - opts[i].code = DNS_OPT_EXPIRE; - opts[i].length = 0; - opts[i].value = NULL; - i++; + option = (dns_ednsopt_t){ .code = DNS_OPT_EXPIRE }; + result = dns_message_ednsaddopt(lookup->sendmsg, + &option); + check_result(result, "dns_message_ednsaddopt"); } if (lookup->tcp_keepalive) { - INSIST(i < MAXOPTS); - opts[i].code = DNS_OPT_TCP_KEEPALIVE; - opts[i].length = 0; - opts[i].value = NULL; - i++; + option = (dns_ednsopt_t){ + .code = DNS_OPT_TCP_KEEPALIVE, + }; + result = dns_message_ednsaddopt(lookup->sendmsg, + &option); + check_result(result, "dns_message_ednsaddopt"); } if (lookup->zoneversion) { - INSIST(i < MAXOPTS); - opts[i].code = DNS_OPT_ZONEVERSION; - opts[i].length = 0; - opts[i].value = NULL; - i++; + option = (dns_ednsopt_t){ + .code = DNS_OPT_ZONEVERSION, + }; + result = dns_message_ednsaddopt(lookup->sendmsg, + &option); + check_result(result, "dns_message_ednsaddopt"); } if (lookup->ednsoptscnt != 0) { - INSIST(i + lookup->ednsoptscnt <= MAXOPTS); - memmove(&opts[i], lookup->ednsopts, - sizeof(dns_ednsopt_t) * lookup->ednsoptscnt); - i += lookup->ednsoptscnt; - } - - if (lookup->padding != 0 && (i >= MAXOPTS)) { - debug("turned off padding because of EDNS overflow"); - lookup->padding = 0; + for (size_t i = 0; i < lookup->ednsoptscnt; i++) { + result = dns_message_ednsaddopt( + lookup->sendmsg, &lookup->ednsopts[i]); + check_result(result, "dns_message_ednsaddopt"); + } } if (lookup->padding != 0) { - INSIST(i < MAXOPTS); - opts[i].code = DNS_OPT_PAD; - opts[i].length = 0; - opts[i].value = NULL; - i++; + option = (dns_ednsopt_t){ .code = DNS_OPT_PAD }; + /* This can fail harmlessly */ + (void)dns_message_ednsaddopt(lookup->sendmsg, &option); dns_message_setpadding(lookup->sendmsg, lookup->padding); } - flags = lookup->ednsflags; - flags &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO); - if (lookup->dnssec) { - flags |= DNS_MESSAGEEXTFLAG_DO; - } - if (lookup->coflag) { - flags |= DNS_MESSAGEEXTFLAG_CO; - } - add_opt(lookup->sendmsg, lookup->udpsize, lookup->edns, flags, - opts, i); + result = dns_message_buildopt(lookup->sendmsg, &rdataset); + check_result(result, "dns_message_buildopt"); + result = dns_message_setopt(lookup->sendmsg, rdataset); + check_result(result, "dns_message_setopt"); } result = dns_message_rendersection(lookup->sendmsg, diff --git a/bin/dig/nslookup.c b/bin/dig/nslookup.c index d5ec48464d0..177cdeba6d4 100644 --- a/bin/dig/nslookup.c +++ b/bin/dig/nslookup.c @@ -264,6 +264,8 @@ detailsection(dig_query_t *query, dns_message_t *msg, bool headers, case DNS_SECTION_ADDITIONAL: puts(" ADDITIONAL RECORDS:"); break; + default: + UNREACHABLE(); } } diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c index 4ceff564661..f0f1a613a94 100644 --- a/bin/nsupdate/nsupdate.c +++ b/bin/nsupdate/nsupdate.c @@ -1493,35 +1493,28 @@ evaluate_prereq(char *cmdline) { static void updateopt(void) { isc_result_t result; - dns_ednsopt_t ednsopts[1]; + dns_rdataset_t *opt = NULL; unsigned char ul[8]; - unsigned int count = 0; + isc_buffer_t b; + dns_ednsopt_t option = { + .code = DNS_OPT_UL, + .length = keylease_set ? 8 : 4, + .value = ul, + }; - if (lease_set) { - isc_buffer_t b; - INSIST(count < ARRAY_SIZE(ednsopts)); - ednsopts[count++] = (dns_ednsopt_t){ .code = DNS_OPT_UL, - .length = keylease_set ? 8 - : 4, - .value = ul }; - - isc_buffer_init(&b, ul, sizeof(ul)); - isc_buffer_putuint32(&b, lease); - isc_buffer_putuint32(&b, keylease); - } - - if (count != 0) { - dns_rdataset_t *opt = NULL; - result = dns_message_buildopt(updatemsg, &opt, 0, - DEFAULT_EDNS_BUFSIZE, 0, ednsopts, - count); - check_result(result, "dns_message_buildopt"); - result = dns_message_setopt(updatemsg, opt); - check_result(result, "dns_message_setopt"); - } else { - result = dns_message_setopt(updatemsg, NULL); - check_result(result, "dns_message_setopt"); + if (!lease_set) { + return; } + + isc_buffer_init(&b, ul, sizeof(ul)); + isc_buffer_putuint32(&b, lease); + isc_buffer_putuint32(&b, keylease); + + dns_message_ednsinit(updatemsg, 0, DEFAULT_EDNS_BUFSIZE, 0, 0); + dns_message_ednsaddopt(updatemsg, &option); + result = dns_message_buildopt(updatemsg, &opt); + check_result(result, "dns_message_buildopt"); + result = dns_message_setopt(updatemsg, opt); } static uint16_t diff --git a/bin/tools/mdig.c b/bin/tools/mdig.c index ddfddee46de..2f4e79b20e3 100644 --- a/bin/tools/mdig.c +++ b/bin/tools/mdig.c @@ -524,24 +524,6 @@ cleanup: return; } -/*% - * Add EDNS0 option record to a message. Currently, the only supported - * options are UDP buffer size, the DO bit, and EDNS options - * (e.g., NSID, COOKIE, client-subnet) - */ -static void -add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags, - dns_ednsopt_t *opts, size_t count) { - dns_rdataset_t *rdataset = NULL; - isc_result_t result; - - result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags, - opts, count); - CHECK("dns_message_buildopt", result); - result = dns_message_setopt(msg, rdataset); - CHECK("dns_message_setopt", result); -} - static void compute_cookie(unsigned char *cookie, size_t len) { /* XXXMPA need to fix, should be per server. */ @@ -603,11 +585,16 @@ sendquery(struct query *query) { if (query->udpsize > 0 || query->dnssec || query->edns > -1 || query->ecs_addr != NULL) { - dns_ednsopt_t opts[EDNSOPTS + DNS_EDNSOPTIONS]; unsigned int flags; - int i = 0; char ecsbuf[20]; unsigned char cookie[40]; + dns_rdataset_t *rdataset = NULL; + + flags = query->ednsflags; + flags &= ~DNS_MESSAGEEXTFLAG_DO; + if (query->dnssec) { + flags |= DNS_MESSAGEEXTFLAG_DO; + } if (query->udpsize == 0) { query->udpsize = 1232; @@ -616,12 +603,13 @@ sendquery(struct query *query) { query->edns = 0; } + dns_message_ednsinit(message, query->edns, query->udpsize, + flags, 0); + if (query->nsid) { - INSIST(i < DNS_EDNSOPTIONS); - opts[i].code = DNS_OPT_NSID; - opts[i].length = 0; - opts[i].value = NULL; - i++; + dns_ednsopt_t option = { .code = DNS_OPT_NSID }; + result = dns_message_ednsaddopt(message, &option); + CHECK("dns_message_ednsaddopt", result); } if (query->ecs_addr != NULL) { @@ -639,10 +627,6 @@ sendquery(struct query *query) { /* Round up prefix len to a multiple of 8 */ addrl = (plen + 7) / 8; - INSIST(i < DNS_EDNSOPTIONS); - opts[i].code = DNS_OPT_CLIENT_SUBNET; - opts[i].length = (uint16_t)addrl + 4; - CHECK("isc_buffer_allocate", result); isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf)); if (sa->sa_family == AF_INET) { family = 1; @@ -675,13 +659,16 @@ sendquery(struct query *query) { (unsigned int)addrl); } - opts[i].value = (uint8_t *)ecsbuf; - i++; + dns_ednsopt_t option = { .code = DNS_OPT_CLIENT_SUBNET, + .value = (uint8_t *)ecsbuf, + .length = (uint16_t)addrl + + 4 }; + result = dns_message_ednsaddopt(message, &option); + CHECK("dns_message_ednsaddopt", result); } if (query->send_cookie) { - INSIST(i < DNS_EDNSOPTIONS); - opts[i].code = DNS_OPT_COOKIE; + dns_ednsopt_t option = { .code = DNS_OPT_COOKIE }; if (query->cookie != NULL) { isc_buffer_t b; @@ -689,36 +676,36 @@ sendquery(struct query *query) { result = isc_hex_decodestring(query->cookie, &b); CHECK("isc_hex_decodestring", result); - opts[i].value = isc_buffer_base(&b); - opts[i].length = isc_buffer_usedlength(&b); + option.value = isc_buffer_base(&b); + option.length = isc_buffer_usedlength(&b); } else { compute_cookie(cookie, 8); - opts[i].length = 8; - opts[i].value = cookie; + option.length = 8; + option.value = cookie; } - i++; + + result = dns_message_ednsaddopt(message, &option); + CHECK("dns_message_ednsaddopt", result); } if (query->expire) { - INSIST(i < DNS_EDNSOPTIONS); - opts[i].code = DNS_OPT_EXPIRE; - opts[i].length = 0; - opts[i].value = NULL; - i++; + dns_ednsopt_t option = { .code = DNS_OPT_EXPIRE }; + result = dns_message_ednsaddopt(message, &option); + CHECK("dns_message_ednsaddopt", result); } if (query->ednsoptscnt != 0) { - memmove(&opts[i], query->ednsopts, - sizeof(dns_ednsopt_t) * query->ednsoptscnt); - i += query->ednsoptscnt; + for (size_t i = 0; i < query->ednsoptscnt; i++) { + result = dns_message_ednsaddopt( + message, &query->ednsopts[i]); + CHECK("dns_message_ednsaddopt", result); + } } - flags = query->ednsflags; - flags &= ~DNS_MESSAGEEXTFLAG_DO; - if (query->dnssec) { - flags |= DNS_MESSAGEEXTFLAG_DO; - } - add_opt(message, query->udpsize, query->edns, flags, opts, i); + result = dns_message_buildopt(message, &rdataset); + CHECK("dns_message_buildopt", result); + result = dns_message_setopt(message, rdataset); + CHECK("dns_message_setopt", result); } if (tcp_mode) { diff --git a/lib/dns/ede.c b/lib/dns/ede.c index af7cc0b57f7..48fc1531886 100644 --- a/lib/dns/ede.c +++ b/lib/dns/ede.c @@ -34,7 +34,7 @@ dns__ede_checkandupdateedeused(dns_edectx_t *edectx, uint16_t code) { void dns_ede_add(dns_edectx_t *edectx, uint16_t code, const char *text) { REQUIRE(DNS_EDE_VALID(edectx)); - REQUIRE(code <= DNS_EDE_MAX_CODE); + REQUIRE(code < DNS_EDE_MAX_CODE); uint16_t becode = htobe16(code); dns_ednsopt_t *edns = NULL; diff --git a/lib/dns/include/dns/ede.h b/lib/dns/include/dns/ede.h index 37194dd5ef0..a9470306cb6 100644 --- a/lib/dns/include/dns/ede.h +++ b/lib/dns/include/dns/ede.h @@ -18,33 +18,34 @@ #include /*%< EDNS0 extended DNS errors */ -#define DNS_EDE_OTHER 0 /*%< Other Error */ -#define DNS_EDE_DNSKEYALG 1 /*%< Unsupported DNSKEY Algorithm */ -#define DNS_EDE_DSDIGESTTYPE 2 /*%< Unsupported DS Digest Type */ -#define DNS_EDE_STALEANSWER 3 /*%< Stale Answer */ -#define DNS_EDE_FORGEDANSWER 4 /*%< Forged Answer */ -#define DNS_EDE_DNSSECINDETERMINATE 5 /*%< DNSSEC Indeterminate */ -#define DNS_EDE_DNSSECBOGUS 6 /*%< DNSSEC Bogus */ -#define DNS_EDE_SIGNATUREEXPIRED 7 /*%< Signature Expired */ -#define DNS_EDE_SIGNATURENOTYETVALID 8 /*%< Signature Not Yet Valid */ -#define DNS_EDE_DNSKEYMISSING 9 /*%< DNSKEY Missing */ -#define DNS_EDE_RRSIGSMISSING 10 /*%< RRSIGs Missing */ -#define DNS_EDE_NOZONEKEYBITSET 11 /*%< No Zone Key Bit Set */ -#define DNS_EDE_NSECMISSING 12 /*%< NSEC Missing */ -#define DNS_EDE_CACHEDERROR 13 /*%< Cached Error */ -#define DNS_EDE_NOTREADY 14 /*%< Not Ready */ -#define DNS_EDE_BLOCKED 15 /*%< Blocked */ -#define DNS_EDE_CENSORED 16 /*%< Censored */ -#define DNS_EDE_FILTERED 17 /*%< Filtered */ -#define DNS_EDE_PROHIBITED 18 /*%< Prohibited */ -#define DNS_EDE_STALENXANSWER 19 /*%< Stale NXDomain Answer */ -#define DNS_EDE_NOTAUTH 20 /*%< Not Authoritative */ -#define DNS_EDE_NOTSUPPORTED 21 /*%< Not Supported */ -#define DNS_EDE_NOREACHABLEAUTH 22 /*%< No Reachable Authority */ -#define DNS_EDE_NETWORKERROR 23 /*%< Network Error */ -#define DNS_EDE_INVALIDDATA 24 /*%< Invalid Data */ - -#define DNS_EDE_MAX_CODE DNS_EDE_INVALIDDATA +enum { + DNS_EDE_OTHER = 0, /*%< Other Error */ + DNS_EDE_DNSKEYALG = 1, /*%< Unsupported DNSKEY Algorithm */ + DNS_EDE_DSDIGESTTYPE = 2, /*%< Unsupported DS Digest Type */ + DNS_EDE_STALEANSWER = 3, /*%< Stale Answer */ + DNS_EDE_FORGEDANSWER = 4, /*%< Forged Answer */ + DNS_EDE_DNSSECINDETERMINATE = 5, /*%< DNSSEC Indeterminate */ + DNS_EDE_DNSSECBOGUS = 6, /*%< DNSSEC Bogus */ + DNS_EDE_SIGNATUREEXPIRED = 7, /*%< Signature Expired */ + DNS_EDE_SIGNATURENOTYETVALID = 8, /*%< Signature Not Yet Valid */ + DNS_EDE_DNSKEYMISSING = 9, /*%< DNSKEY Missing */ + DNS_EDE_RRSIGSMISSING = 10, /*%< RRSIGs Missing */ + DNS_EDE_NOZONEKEYBITSET = 11, /*%< No Zone Key Bit Set */ + DNS_EDE_NSECMISSING = 12, /*%< NSEC Missing */ + DNS_EDE_CACHEDERROR = 13, /*%< Cached Error */ + DNS_EDE_NOTREADY = 14, /*%< Not Ready */ + DNS_EDE_BLOCKED = 15, /*%< Blocked */ + DNS_EDE_CENSORED = 16, /*%< Censored */ + DNS_EDE_FILTERED = 17, /*%< Filtered */ + DNS_EDE_PROHIBITED = 18, /*%< Prohibited */ + DNS_EDE_STALENXANSWER = 19, /*%< Stale NXDomain Answer */ + DNS_EDE_NOTAUTH = 20, /*%< Not Authoritative */ + DNS_EDE_NOTSUPPORTED = 21, /*%< Not Supported */ + DNS_EDE_NOREACHABLEAUTH = 22, /*%< No Reachable Authority */ + DNS_EDE_NETWORKERROR = 23, /*%< Network Error */ + DNS_EDE_INVALIDDATA = 24, /*%< Invalid Data */ + DNS_EDE_MAX_CODE +}; /* * From RFC 8914: @@ -56,8 +57,6 @@ */ #define DNS_EDE_EXTRATEXT_LEN 64 -#define DNS_EDE_MAX_ERRORS 3 - typedef struct dns_edectx dns_edectx_t; struct dns_edectx { int magic; diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index bfda3fbb56c..ef35346cb6f 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -24,7 +24,6 @@ #include #include -#include #include #include @@ -90,46 +89,55 @@ * section, move rdata from one section to another, remove rdata, etc. */ -#define DNS_MESSAGEFLAG_QR 0x8000U -#define DNS_MESSAGEFLAG_AA 0x0400U -#define DNS_MESSAGEFLAG_TC 0x0200U -#define DNS_MESSAGEFLAG_RD 0x0100U -#define DNS_MESSAGEFLAG_RA 0x0080U -#define DNS_MESSAGEFLAG_AD 0x0020U -#define DNS_MESSAGEFLAG_CD 0x0010U +enum { + DNS_MESSAGEFLAG_QR = 0x8000U, + DNS_MESSAGEFLAG_AA = 0x0400U, + DNS_MESSAGEFLAG_TC = 0x0200U, + DNS_MESSAGEFLAG_RD = 0x0100U, + DNS_MESSAGEFLAG_RA = 0x0080U, + DNS_MESSAGEFLAG_AD = 0x0020U, + DNS_MESSAGEFLAG_CD = 0x0010U, +}; /*%< EDNS0 extended message flags */ -#define DNS_MESSAGEEXTFLAG_DO 0x8000U /* DNSSEC OK */ -#define DNS_MESSAGEEXTFLAG_CO 0x4000U /* Compact denial of existence OK */ +enum { + DNS_MESSAGEEXTFLAG_DO = 0x8000U, /* DNSSEC OK */ + DNS_MESSAGEEXTFLAG_CO = 0x4000U, /* Compact denial of existence OK */ +}; /*%< EDNS0 extended OPT codes */ -#define DNS_OPT_LLQ 1 /*%< LLQ opt code */ -#define DNS_OPT_UL 2 /*%< UL opt code */ -#define DNS_OPT_NSID 3 /*%< NSID opt code */ -#define DNS_OPT_DAU 5 /*%< DNSSEC algorithm understood */ -#define DNS_OPT_DHU 6 /*%< DNSSEC hash understood */ -#define DNS_OPT_N3U 7 /*%< NSEC3 hash understood */ -#define DNS_OPT_CLIENT_SUBNET 8 /*%< client subnet opt code */ -#define DNS_OPT_EXPIRE 9 /*%< EXPIRE opt code */ -#define DNS_OPT_COOKIE 10 /*%< COOKIE opt code */ -#define DNS_OPT_TCP_KEEPALIVE 11 /*%< TCP keepalive opt code */ -#define DNS_OPT_PAD 12 /*%< PAD opt code */ -#define DNS_OPT_CHAIN 13 /*%< CHAIN opt code */ -#define DNS_OPT_KEY_TAG 14 /*%< Key tag opt code */ -#define DNS_OPT_EDE 15 /*%< Extended DNS Error opt code */ -#define DNS_OPT_CLIENT_TAG 16 /*%< Client tag opt code */ -#define DNS_OPT_SERVER_TAG 17 /*%< Server tag opt code */ -#define DNS_OPT_REPORT_CHANNEL 18 /*%< DNS Reporting Channel */ -#define DNS_OPT_ZONEVERSION 19 /*%< Zoneversion opt code */ +enum { + DNS_OPT_LLQ = 1, /*%< LLQ opt code */ + DNS_OPT_UL = 2, /*%< UL opt code */ + DNS_OPT_NSID = 3, /*%< NSID opt code */ + DNS_OPT_DAU = 5, /*%< DNSSEC algorithm understood */ + DNS_OPT_DHU = 6, /*%< DNSSEC hash understood */ + DNS_OPT_N3U = 7, /*%< NSEC3 hash understood */ + DNS_OPT_CLIENT_SUBNET = 8, /*%< client subnet opt code */ + DNS_OPT_EXPIRE = 9, /*%< EXPIRE opt code */ + DNS_OPT_COOKIE = 10, /*%< COOKIE opt code */ + DNS_OPT_TCP_KEEPALIVE = 11, /*%< TCP keepalive opt code */ + DNS_OPT_PAD = 12, /*%< PAD opt code */ + DNS_OPT_CHAIN = 13, /*%< CHAIN opt code */ + DNS_OPT_KEY_TAG = 14, /*%< Key tag opt code */ + DNS_OPT_EDE = 15, /*%< Extended DNS Error opt code */ + DNS_OPT_CLIENT_TAG = 16, /*%< Client tag opt code */ + DNS_OPT_SERVER_TAG = 17, /*%< Server tag opt code */ + DNS_OPT_REPORT_CHANNEL = 18, /*%< DNS Reporting Channel */ + DNS_OPT_ZONEVERSION = 19, /*%< Zoneversion opt code */ + + DNS_OPT_COUNT = 18, /*%< Number of elements defined in this enum. */ +}; /*%< Experimental options [65001...65534] as per RFC6891 */ /*%< * The maximum number of EDNS options we allow to set. Reserve space for the - * options we know about. Extended DNS Errors may occur multiple times, see - * DNS_EDE_MAX_ERRORS. + * options we know about. Extended DNS Errors may occur multiple times, + * controlled by DNS_EDE_MAX_ERRORS. */ -#define DNS_EDNSOPTIONS 9 + DNS_EDE_MAX_ERRORS +#define DNS_EDE_MAX_ERRORS 3 +#define DNS_EDNS_MAX_OPTIONS DNS_OPT_COUNT + DNS_EDE_MAX_ERRORS #define DNS_MESSAGE_REPLYPRESERVE (DNS_MESSAGEFLAG_RD | DNS_MESSAGEFLAG_CD) #define DNS_MESSAGEEXTFLAG_REPLYPRESERVE (DNS_MESSAGEEXTFLAG_DO) @@ -143,26 +151,32 @@ * Ordering here matters. DNS_SECTION_ANY must be the lowest and negative, * and DNS_SECTION_MAX must be one greater than the last used section. */ -typedef int dns_section_t; -#define DNS_SECTION_ANY (-1) -#define DNS_SECTION_QUESTION 0 -#define DNS_SECTION_ANSWER 1 -#define DNS_SECTION_AUTHORITY 2 -#define DNS_SECTION_ADDITIONAL 3 -#define DNS_SECTION_MAX 4 - -typedef int dns_pseudosection_t; -#define DNS_PSEUDOSECTION_ANY (-1) -#define DNS_PSEUDOSECTION_OPT 0 -#define DNS_PSEUDOSECTION_TSIG 1 -#define DNS_PSEUDOSECTION_SIG0 2 -#define DNS_PSEUDOSECTION_MAX 3 - -typedef int dns_messagetextflag_t; -#define DNS_MESSAGETEXTFLAG_NOCOMMENTS 0x0001 -#define DNS_MESSAGETEXTFLAG_NOHEADERS 0x0002 -#define DNS_MESSAGETEXTFLAG_ONESOA 0x0004 -#define DNS_MESSAGETEXTFLAG_OMITSOA 0x0008 +typedef enum { + DNS_SECTION_ANY = -1, + DNS_SECTION_QUESTION = 0, + DNS_SECTION_ZONE = DNS_SECTION_QUESTION, + DNS_SECTION_ANSWER = 1, + DNS_SECTION_PREREQUISITE = DNS_SECTION_ANSWER, + DNS_SECTION_AUTHORITY = 2, + DNS_SECTION_UPDATE = DNS_SECTION_AUTHORITY, + DNS_SECTION_ADDITIONAL = 3, + DNS_SECTION_MAX +} dns_section_t; + +typedef enum { + DNS_PSEUDOSECTION_ANY = -1, + DNS_PSEUDOSECTION_OPT = 0, + DNS_PSEUDOSECTION_TSIG = 1, + DNS_PSEUDOSECTION_SIG0 = 2, + DNS_PSEUDOSECTION_MAX +} dns_pseudosection_t; + +typedef enum { + DNS_MESSAGETEXTFLAG_NOCOMMENTS = 1 << 0, + DNS_MESSAGETEXTFLAG_NOHEADERS = 1 << 1, + DNS_MESSAGETEXTFLAG_ONESOA = 1 << 2, + DNS_MESSAGETEXTFLAG_OMITSOA = 1 << 3, +} dns_messagetextflag_t; /* * Dynamic update names for these sections. @@ -223,6 +237,12 @@ typedef struct dns_minttl { dns_ttl_t ttl; } dns_minttl_t; +struct dns_ednsopt { + uint16_t code; + uint16_t length; + uint8_t *value; +}; + struct dns_message { /* public from here down */ unsigned int magic; @@ -310,12 +330,15 @@ struct dns_message { dns_indent_t indent; dns_minttl_t minttl[DNS_SECTION_MAX]; -}; -struct dns_ednsopt { - uint16_t code; - uint16_t length; - uint8_t *value; + struct { + dns_ednsopt_t *opts; + uint8_t maxopts; + uint8_t count; + int16_t version; + uint16_t udpsize; + uint32_t flags; + } edns; }; typedef void (*dns_message_cb_t)(void *arg, isc_result_t result); @@ -1383,12 +1406,44 @@ dns_message_logpacketfromto(dns_message_t *message, const char *description, *\li 'mctx' to be a valid memory context. */ +void +dns_message_ednsinit(dns_message_t *msg, int16_t version, uint16_t udpsize, + uint32_t flags, uint8_t maxopts); +/*%< + * Initialize the EDNS data in 'msg'. EDNS version is set to 'version', + * UDP size is set to 'udpsize', flags is set to 'flags'; EDNS options + * are cleared, and ready to be added via subsequent calls to + * dns_message_ednsaddopt(). If 'maxopts' is nonzero, space is allocated + * for that many EDNS options; if it is zero, space is allocated for + * DNS_EDNS_MAX_OPTIONS options. + * + * If 'version' is -1, the message has no EDNS, any existing EDNS data + * is cleared, and no OPT record will be rendered. + * + * Requires: + *\li 'msg' be a valid message. + */ + +isc_result_t +dns_message_ednsaddopt(dns_message_t *msg, dns_ednsopt_t *ednsopt); +/*%< + * Add the EDNS option 'ednsopt' to 'msg->edns.opts' and increment + * 'msg->edns.count'. + * + * Requires: + *\li 'msg' to be a valid message. + *\li dns_message_ednsinit() to have been called. + * + * Returns: + *\li ISC_R_SUCCESS + *\li ISC_R_NOSPACE + */ + isc_result_t -dns_message_buildopt(dns_message_t *msg, dns_rdataset_t **opt, - unsigned int version, uint16_t udpsize, unsigned int flags, - dns_ednsopt_t *ednsopts, size_t count); +dns_message_buildopt(dns_message_t *msg, dns_rdataset_t **opt); + /*%< - * Built a opt record. + * Build an opt record. * * Requires: *\li msg be a valid message. diff --git a/lib/dns/message.c b/lib/dns/message.c index f0f27a9d733..5951487af49 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -516,6 +516,23 @@ msgresetsigs(dns_message_t *msg, bool replying) { } } +static void +msgresetedns(dns_message_t *msg) { + if (msg->edns.opts != NULL) { + INSIST(msg->edns.maxopts != 0); + for (size_t i = 0; i < msg->edns.count; i++) { + if (msg->edns.opts[i].value != NULL) { + isc_mem_put(msg->mctx, msg->edns.opts[i].value, + msg->edns.opts[i].length); + } + } + isc_mem_cput(msg->mctx, msg->edns.opts, + (size_t)msg->edns.maxopts, sizeof(dns_ednsopt_t)); + } + msg->edns.maxopts = 0; + msg->edns.count = 0; +} + /* * Free all but one (or everything) for this message. This is used by * both dns_message_reset() and dns__message_destroy(). @@ -528,6 +545,7 @@ msgreset(dns_message_t *msg, bool everything) { msgresetnames(msg, 0); msgresetopt(msg); msgresetsigs(msg, false); + msgresetedns(msg); /* * Clean up linked lists. @@ -4062,6 +4080,8 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, ADD_STRING(target, "\n"); } goto cleanup; + default: + break; } result = ISC_R_UNEXPECTED; @@ -4483,6 +4503,8 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section, ADD_STRING(target, "\n"); } return result; + default: + break; } result = ISC_R_UNEXPECTED; cleanup: @@ -4825,15 +4847,62 @@ logfmtpacket(dns_message_t *message, const char *description, } } +void +dns_message_ednsinit(dns_message_t *msg, int16_t version, uint16_t udpsize, + uint32_t flags, uint8_t maxopts) { + REQUIRE(DNS_MESSAGE_VALID(msg)); + + /* Clear existing EDNS data */ + msgresetedns(msg); + + msg->edns.version = version; + if (version == -1) { + return; + } + + if (maxopts == 0) { + maxopts = DNS_EDNS_MAX_OPTIONS; + } + + msg->edns.flags = flags; + msg->edns.udpsize = udpsize; + msg->edns.opts = isc_mem_cget(msg->mctx, (size_t)maxopts, + sizeof(dns_ednsopt_t)); + msg->edns.maxopts = maxopts; +} + isc_result_t -dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp, - unsigned int version, uint16_t udpsize, unsigned int flags, - dns_ednsopt_t *ednsopts, size_t count) { +dns_message_ednsaddopt(dns_message_t *msg, dns_ednsopt_t *option) { + REQUIRE(DNS_MESSAGE_VALID(msg)); + REQUIRE(msg->edns.opts != NULL); + + if (msg->edns.count >= msg->edns.maxopts) { + isc_log_write(ISC_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MESSAGE, + ISC_LOG_DEBUG(3), + "dns_message_ednsaddopt: limit of %u EDNS " + "options exceeded", + msg->edns.maxopts); + return ISC_R_NOSPACE; + } + + size_t i = msg->edns.count; + msg->edns.opts[i].code = option->code; + if (option->value != NULL) { + msg->edns.opts[i].value = isc_mem_get(msg->mctx, + option->length); + memmove(msg->edns.opts[i].value, option->value, option->length); + msg->edns.opts[i].length = option->length; + } + msg->edns.count++; + return ISC_R_SUCCESS; +} + +isc_result_t +dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp) { dns_rdataset_t *rdataset = NULL; dns_rdatalist_t *rdatalist = NULL; dns_rdata_t *rdata = NULL; isc_result_t result; - unsigned int len = 0, i; REQUIRE(DNS_MESSAGE_VALID(message)); REQUIRE(rdatasetp != NULL && *rdatasetp == NULL); @@ -4847,22 +4916,24 @@ dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp, /* * Set Maximum UDP buffer size. */ - rdatalist->rdclass = udpsize; + rdatalist->rdclass = message->edns.udpsize; /* * Set EXTENDED-RCODE and Z to 0. */ - rdatalist->ttl = (version << 16); - rdatalist->ttl |= (flags & 0xffff); + rdatalist->ttl = (message->edns.version << 16); + rdatalist->ttl |= (message->edns.flags & 0xffff); /* * Set EDNS options if applicable */ - if (count != 0U) { + if (message->edns.count != 0U) { isc_buffer_t *buf = NULL; bool seenpad = false; - for (i = 0; i < count; i++) { - len += ednsopts[i].length + 4; + size_t len = 0; + + for (size_t i = 0; i < message->edns.count; i++) { + len += message->edns.opts[i].length + 4; } if (len > 0xffffU) { @@ -4872,18 +4943,19 @@ dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp, isc_buffer_allocate(message->mctx, &buf, len); - for (i = 0; i < count; i++) { - if (ednsopts[i].code == DNS_OPT_PAD && - ednsopts[i].length == 0U && !seenpad) + for (size_t i = 0; i < message->edns.count; i++) { + if (message->edns.opts[i].code == DNS_OPT_PAD && + message->edns.opts[i].length == 0U && !seenpad) { seenpad = true; continue; } - isc_buffer_putuint16(buf, ednsopts[i].code); - isc_buffer_putuint16(buf, ednsopts[i].length); - if (ednsopts[i].length != 0) { - isc_buffer_putmem(buf, ednsopts[i].value, - ednsopts[i].length); + isc_buffer_putuint16(buf, message->edns.opts[i].code); + isc_buffer_putuint16(buf, message->edns.opts[i].length); + if (message->edns.opts[i].length != 0) { + isc_buffer_putmem(buf, + message->edns.opts[i].value, + message->edns.opts[i].length); } } diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index 8ec43e0bd28..5fd872fe3f8 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -2443,6 +2443,8 @@ dns_rdata_updateop(dns_rdata_t *rdata, dns_section_t section) { default: return "add"; } + default: + break; } return "invalid"; } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index c698edf742b..86efcb00786 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -1831,20 +1831,6 @@ detach: resquery_detach(&query); } -static isc_result_t -fctx_addopt(dns_message_t *message, unsigned int version, uint16_t udpsize, - dns_ednsopt_t *ednsopts, size_t count) { - dns_rdataset_t *rdataset = NULL; - isc_result_t result; - - result = dns_message_buildopt(message, &rdataset, version, udpsize, - DNS_MESSAGEEXTFLAG_DO, ednsopts, count); - if (result != ISC_R_SUCCESS) { - return result; - } - return dns_message_setopt(message, rdataset); -} - static void fctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) { unsigned int seconds, us; @@ -2328,8 +2314,6 @@ resquery_send(resquery_t *query) { dns_compress_t cctx; bool useedns; bool tcp = ((query->options & DNS_FETCHOPT_TCP) != 0); - dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; - unsigned int ednsopt = 0; uint16_t hint = 0, udpsize = 0; /* No EDNS */ isc_sockaddr_t localaddr, *la = NULL; #ifdef HAVE_DNSTAP @@ -2480,6 +2464,7 @@ resquery_send(resquery_t *query) { bool tcpkeepalive = false; unsigned char cookie[COOKIE_BUFFER_SIZE]; uint16_t padding = 0; + dns_rdataset_t *rdataset = NULL; /* * Set the default UDP size to what was @@ -2534,43 +2519,57 @@ resquery_send(resquery_t *query) { if (NOCOOKIE(query->addrinfo)) { sendcookie = false; } + + query->ednsversion = version; + dns_message_ednsinit(fctx->qmessage, version, udpsize, + DNS_MESSAGEEXTFLAG_DO, 0); + if (reqnsid) { - INSIST(ednsopt < DNS_EDNSOPTIONS); - ednsopts[ednsopt].code = DNS_OPT_NSID; - ednsopts[ednsopt].length = 0; - ednsopts[ednsopt].value = NULL; - ednsopt++; + dns_ednsopt_t option = { + .code = DNS_OPT_NSID, + }; + result = dns_message_ednsaddopt(fctx->qmessage, + &option); + if (result != ISC_R_SUCCESS) { + goto cleanup_message; + } } if (reqzoneversion) { - INSIST(ednsopt < DNS_EDNSOPTIONS); - ednsopts[ednsopt].code = DNS_OPT_ZONEVERSION; - ednsopts[ednsopt].length = 0; - ednsopts[ednsopt].value = NULL; - ednsopt++; + dns_ednsopt_t option = { + .code = DNS_OPT_ZONEVERSION, + }; + result = dns_message_ednsaddopt(fctx->qmessage, + &option); + if (result != ISC_R_SUCCESS) { + goto cleanup_message; + } } if (sendcookie) { - INSIST(ednsopt < DNS_EDNSOPTIONS); - ednsopts[ednsopt].code = DNS_OPT_COOKIE; - ednsopts[ednsopt].length = - (uint16_t)dns_adb_getcookie( - query->addrinfo, cookie, - sizeof(cookie)); - if (ednsopts[ednsopt].length != 0) { - ednsopts[ednsopt].value = cookie; + dns_ednsopt_t option = { + .code = DNS_OPT_COOKIE, + }; + option.length = (uint16_t)dns_adb_getcookie( + query->addrinfo, cookie, + sizeof(cookie)); + if (option.length != 0) { + option.value = cookie; inc_stats( fctx->res, dns_resstatscounter_cookieout); } else { compute_cc(query, cookie, CLIENT_COOKIE_SIZE); - ednsopts[ednsopt].value = cookie; - ednsopts[ednsopt].length = - CLIENT_COOKIE_SIZE; + option.value = cookie; + option.length = CLIENT_COOKIE_SIZE; inc_stats( fctx->res, dns_resstatscounter_cookienew); } - ednsopt++; + result = dns_message_ednsaddopt(fctx->qmessage, + &option); + if (result != ISC_R_SUCCESS) { + goto cleanup_message; + } } /* Add TCP keepalive option if appropriate */ @@ -2579,11 +2578,14 @@ resquery_send(resquery_t *query) { &tcpkeepalive); } if (tcpkeepalive) { - INSIST(ednsopt < DNS_EDNSOPTIONS); - ednsopts[ednsopt].code = DNS_OPT_TCP_KEEPALIVE; - ednsopts[ednsopt].length = 0; - ednsopts[ednsopt].value = NULL; - ednsopt++; + dns_ednsopt_t option = { + .code = DNS_OPT_TCP_KEEPALIVE, + }; + result = dns_message_ednsaddopt(fctx->qmessage, + &option); + if (result != ISC_R_SUCCESS) { + goto cleanup_message; + } } /* Add PAD for current peer? Require TCP for now @@ -2592,16 +2594,24 @@ resquery_send(resquery_t *query) { (void)dns_peer_getpadding(peer, &padding); } if (padding != 0) { - INSIST(ednsopt < DNS_EDNSOPTIONS); - ednsopts[ednsopt].code = DNS_OPT_PAD; - ednsopts[ednsopt].length = 0; - ednsopt++; + dns_ednsopt_t option = { + .code = DNS_OPT_PAD, + }; + result = dns_message_ednsaddopt(fctx->qmessage, + &option); + if (result != ISC_R_SUCCESS) { + goto cleanup_message; + } dns_message_setpadding(fctx->qmessage, padding); } - query->ednsversion = version; - result = fctx_addopt(fctx->qmessage, version, udpsize, - ednsopts, ednsopt); + result = dns_message_buildopt(fctx->qmessage, + &rdataset); + if (result == ISC_R_SUCCESS) { + result = dns_message_setopt(fctx->qmessage, + rdataset); + } + if (result == ISC_R_SUCCESS) { if (reqnsid) { query->options |= DNS_FETCHOPT_WANTNSID; @@ -2610,7 +2620,7 @@ resquery_send(resquery_t *query) { query->options |= DNS_FETCHOPT_WANTZONEVERSION; } - } else if (result != ISC_R_SUCCESS) { + } else { /* * We couldn't add the OPT, but we'll * press on. We're not using EDNS0, so diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index e1389f0422d..e06b466f28f 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -1553,26 +1553,26 @@ add_opt(dns_message_t *message, uint16_t udpsize, bool reqnsid, bool reqexpire) { isc_result_t result; dns_rdataset_t *rdataset = NULL; - dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; - int count = 0; + + dns_message_ednsinit(message, 0, udpsize, 0, 0); /* Set EDNS options if applicable. */ if (reqnsid) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_NSID; - ednsopts[count].length = 0; - ednsopts[count].value = NULL; - count++; + dns_ednsopt_t option = { .code = DNS_OPT_NSID }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } if (reqexpire) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_EXPIRE; - ednsopts[count].length = 0; - ednsopts[count].value = NULL; - count++; - } - result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0, - ednsopts, count); + dns_ednsopt_t option = { .code = DNS_OPT_EXPIRE }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } + } + + result = dns_message_buildopt(message, &rdataset); if (result != ISC_R_SUCCESS) { return result; } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 7c650805e02..8850844682a 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -12863,26 +12863,26 @@ add_opt(dns_message_t *message, uint16_t udpsize, bool reqnsid, bool reqexpire) { isc_result_t result; dns_rdataset_t *rdataset = NULL; - dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; - int count = 0; + + dns_message_ednsinit(message, 0, udpsize, 0, 0); /* Set EDNS options if applicable. */ if (reqnsid) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_NSID; - ednsopts[count].length = 0; - ednsopts[count].value = NULL; - count++; + dns_ednsopt_t option = { .code = DNS_OPT_NSID }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } if (reqexpire) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_EXPIRE; - ednsopts[count].length = 0; - ednsopts[count].value = NULL; - count++; + dns_ednsopt_t option = { .code = DNS_OPT_EXPIRE }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } - result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0, - ednsopts, count); + + result = dns_message_buildopt(message, &rdataset); if (result != ISC_R_SUCCESS) { return result; } diff --git a/lib/ns/client.c b/lib/ns/client.c index 0bd85a7c278..054bfc8deb4 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -1056,13 +1056,10 @@ isc_result_t ns_client_addopt(ns_client_t *client, dns_message_t *message, dns_rdataset_t **opt) { unsigned char ecs[ECS_SIZE]; - char nsid[_POSIX_HOST_NAME_MAX + 1], *nsidp = NULL; unsigned char cookie[COOKIE_SIZE]; isc_result_t result; dns_view_t *view = NULL; uint16_t udpsize; - dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; - int count = 0; unsigned int flags; unsigned char expire[4]; unsigned char advtimo[2]; @@ -1082,26 +1079,33 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message, flags = client->inner.extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE; + dns_message_ednsinit(message, 0, udpsize, flags, 0); + /* Set EDNS options if applicable */ if (WANTNSID(client)) { + char nsid[_POSIX_HOST_NAME_MAX + 1]; + char *nsidp = NULL; + if (client->manager->sctx->server_id != NULL) { nsidp = client->manager->sctx->server_id; - } else if (client->manager->sctx->usehostname) { - if (gethostname(nsid, sizeof(nsid)) != 0) { - goto no_nsid; - } + } else if (client->manager->sctx->usehostname && + gethostname(nsid, sizeof(nsid)) == 0) + { nsidp = nsid; - } else { - goto no_nsid; } - - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_NSID; - ednsopts[count].length = (uint16_t)strlen(nsidp); - ednsopts[count].value = (unsigned char *)nsidp; - count++; + if (nsidp != NULL) { + dns_ednsopt_t option = { + .code = DNS_OPT_NSID, + .value = (unsigned char *)nsidp, + .length = (uint16_t)strlen(nsidp), + }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } + } } -no_nsid: + if ((client->inner.attributes & NS_CLIENTATTR_WANTCOOKIE) != 0) { isc_buffer_t buf; isc_stdtime_t now = isc_stdtime_now(); @@ -1111,24 +1115,30 @@ no_nsid: compute_cookie(client, now, client->manager->sctx->secret, &buf); - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_COOKIE; - ednsopts[count].length = COOKIE_SIZE; - ednsopts[count].value = cookie; - count++; + dns_ednsopt_t option = { .code = DNS_OPT_COOKIE, + .length = COOKIE_SIZE, + .value = cookie }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } + if ((client->inner.attributes & NS_CLIENTATTR_HAVEEXPIRE) != 0) { isc_buffer_t buf; - INSIST(count < DNS_EDNSOPTIONS); - isc_buffer_init(&buf, expire, sizeof(expire)); isc_buffer_putuint32(&buf, client->inner.expire); - ednsopts[count].code = DNS_OPT_EXPIRE; - ednsopts[count].length = 4; - ednsopts[count].value = expire; - count++; + + dns_ednsopt_t option = { .code = DNS_OPT_EXPIRE, + .value = expire, + .length = 4 }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } + if (((client->inner.attributes & NS_CLIENTATTR_HAVEECS) != 0) && (client->inner.ecs.addr.family == AF_INET || client->inner.ecs.addr.family == AF_INET6 || @@ -1182,24 +1192,30 @@ no_nsid: isc_buffer_putmem(&buf, addr, (unsigned int)addrl); } - ednsopts[count].code = DNS_OPT_CLIENT_SUBNET; - ednsopts[count].length = addrl + 4; - ednsopts[count].value = ecs; - count++; + dns_ednsopt_t option = { .code = DNS_OPT_CLIENT_SUBNET, + .length = addrl + 4, + .value = ecs }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } + if (TCP_CLIENT(client) && USEKEEPALIVE(client)) { - isc_buffer_t buf; uint32_t advertised_timeout = isc_nm_getadvertisedtimeout(); - - INSIST(count < DNS_EDNSOPTIONS); + isc_buffer_t buf; advertised_timeout /= 100; /* units of 100 milliseconds */ isc_buffer_init(&buf, advtimo, sizeof(advtimo)); isc_buffer_putuint16(&buf, (uint16_t)advertised_timeout); - ednsopts[count].code = DNS_OPT_TCP_KEEPALIVE; - ednsopts[count].length = 2; - ednsopts[count].value = advtimo; - count++; + + dns_ednsopt_t option = { .code = DNS_OPT_TCP_KEEPALIVE, + .length = 2, + .value = advtimo }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } for (size_t i = 0; i < DNS_EDE_MAX_ERRORS; i++) { @@ -1209,18 +1225,24 @@ no_nsid: break; } - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_EDE; - ednsopts[count].length = ede->length; - ednsopts[count].value = ede->value; - count++; + dns_ednsopt_t option = { .code = DNS_OPT_EDE, + .length = ede->length, + .value = ede->value }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } if ((client->inner.attributes & NS_CLIENTATTR_HAVEZONEVERSION) != 0) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_ZONEVERSION; - ednsopts[count].length = client->inner.zoneversionlength; - ednsopts[count].value = client->inner.zoneversion; - count++; + dns_ednsopt_t option = { + .code = DNS_OPT_ZONEVERSION, + .length = client->inner.zoneversionlength, + .value = client->inner.zoneversion + }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } if (WANTRC(client)) { @@ -1229,11 +1251,15 @@ no_nsid: rad = &client->inner.rad; } if (rad != NULL && !dns_name_equal(rad, dns_rootname)) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_REPORT_CHANNEL; - ednsopts[count].length = rad->length; - ednsopts[count].value = rad->ndata; - count++; + dns_ednsopt_t option = { + .code = DNS_OPT_REPORT_CHANNEL, + .length = rad->length, + .value = rad->ndata, + }; + result = dns_message_ednsaddopt(message, &option); + if (result != ISC_R_SUCCESS) { + return result; + } } } @@ -1249,19 +1275,14 @@ no_nsid: result = dns_acl_match(&netaddr, NULL, view->pad_acl, env, &match, NULL); if (result == ISC_R_SUCCESS && match > 0) { - INSIST(count < DNS_EDNSOPTIONS); - - ednsopts[count].code = DNS_OPT_PAD; - ednsopts[count].length = 0; - ednsopts[count].value = NULL; - count++; - + dns_ednsopt_t option = { .code = DNS_OPT_PAD }; + /* This can fail harmlessly */ + dns_message_ednsaddopt(message, &option); dns_message_setpadding(message, view->padding); } } - result = dns_message_buildopt(message, opt, 0, udpsize, flags, ednsopts, - count); + result = dns_message_buildopt(message, opt); return result; }