diff --git a/CHANGES b/CHANGES index 2e0e7c09e2..e349d1c24b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ 4342. [bug] 'rndc flushtree' could fail to clean the tree if there wasn't a node at the specified name. [RT #41846] +4341. [bug] Correct the handling of ECS options with + address family 0. [RT #41377] + 4338. [bug] Reimplement change 4324 as it wasn't properly doing all the required book keeping. [RT #41941] diff --git a/bin/dig/dig.1 b/bin/dig/dig.1 index a61350b4d0..f6d4131f87 100644 --- a/bin/dig/dig.1 +++ b/bin/dig/dig.1 @@ -587,13 +587,15 @@ causes fields not to be split at all\&. The default is 56 characters, or 44 char This query option toggles the printing of statistics: when the query was made, the size of the reply and so on\&. The default behavior is to print the query statistics\&. .RE .PP -\fB+[no]subnet=addr[/netmask]\fR +\fB+[no]subnet=addr[/prefix\-length]\fR .RS 4 Send (don\*(Aqt send) an EDNS Client Subnet option with the specified IP address or network prefix\&. .sp \fBdig +subnet=0\&.0\&.0\&.0/0\fR, or simply \fBdig +subnet=0\fR -for short, sends a Client Subnet option with an empty address and a source prefix length of zero, which signals a resolver that the EDNS Client Subnet option should not be used when resolving this query\&. +for short, sends an EDNS client\-subnet option with an empty address and a source prefix\-length of zero, which signals a resolver that the client\*(Aqs address information must +\fInot\fR +be used when resolving this query\&. .RE .PP \fB+[no]tcp\fR diff --git a/bin/dig/dig.docbook b/bin/dig/dig.docbook index edfd894028..c54d677793 100644 --- a/bin/dig/dig.docbook +++ b/bin/dig/dig.docbook @@ -977,19 +977,20 @@ - + Send (don't send) an EDNS Client Subnet option with the - specified IP address or network prefix. + specified IP address or network prefix. dig +subnet=0.0.0.0/0, or simply - dig +subnet=0 for short, sends a - Client Subnet option with an empty address and a - source prefix length of zero, which signals a resolver - that the EDNS Client Subnet option should not be used - when resolving this query. + dig +subnet=0 for short, sends an EDNS + client-subnet option with an empty address and a source + prefix-length of zero, which signals a resolver that + the client's address information must + not be used when resolving + this query. diff --git a/bin/dig/dig.html b/bin/dig/dig.html index c66e385f65..8a91671518 100644 --- a/bin/dig/dig.html +++ b/bin/dig/dig.html @@ -590,19 +590,20 @@ so on. The default behavior is to print the query statistics.

-
+[no]subnet=addr[/netmask]
+
+[no]subnet=addr[/prefix-length]

Send (don't send) an EDNS Client Subnet option with the - specified IP address or network prefix. + specified IP address or network prefix.

dig +subnet=0.0.0.0/0, or simply - dig +subnet=0 for short, sends a - Client Subnet option with an empty address and a - source prefix length of zero, which signals a resolver - that the EDNS Client Subnet option should not be used - when resolving this query. + dig +subnet=0 for short, sends an EDNS + client-subnet option with an empty address and a source + prefix-length of zero, which signals a resolver that + the client's address information must + not be used when resolving + this query.

+[no]tcp
diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index c3f92c87fd..3ca7cb9448 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -1065,7 +1065,7 @@ parse_netprefix(isc_sockaddr_t **sap, const char *value) { isc_sockaddr_t *sa = NULL; struct in_addr in4; struct in6_addr in6; - isc_uint32_t netmask = 0xffffffff; + isc_uint32_t prefix_length = 0xffffffff; char *slash = NULL; isc_boolean_t parsed = ISC_FALSE; char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")]; @@ -1076,13 +1076,16 @@ parse_netprefix(isc_sockaddr_t **sap, const char *value) { slash = strchr(buf, '/'); if (slash != NULL) { *slash = '\0'; - result = isc_parse_uint32(&netmask, slash + 1, 10); + result = isc_parse_uint32(&prefix_length, slash + 1, 10); if (result != ISC_R_SUCCESS) { fatal("invalid prefix length in '%s': %s\n", value, isc_result_totext(result)); } - } else if (strcmp(value, "0") == 0) { - netmask = 0; + } + + if (strcmp(buf, "0") == 0) { + parsed = ISC_TRUE; + prefix_length = 0; } sa = isc_mem_allocate(mctx, sizeof(*sa)); @@ -1091,14 +1094,14 @@ parse_netprefix(isc_sockaddr_t **sap, const char *value) { if (inet_pton(AF_INET6, buf, &in6) == 1) { parsed = ISC_TRUE; isc_sockaddr_fromin6(sa, &in6, 0); - if (netmask > 128) - netmask = 128; + if (prefix_length > 128) + prefix_length = 128; } else if (inet_pton(AF_INET, buf, &in4) == 1) { parsed = ISC_TRUE; isc_sockaddr_fromin(sa, &in4, 0); - if (netmask > 32) - netmask = 32; - } else if (netmask != 0xffffffff) { + if (prefix_length > 32) + prefix_length = 32; + } else if (prefix_length != 0xffffffff && prefix_length != 0) { int i; for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) { @@ -1110,14 +1113,17 @@ parse_netprefix(isc_sockaddr_t **sap, const char *value) { } } - if (netmask > 32) - netmask = 32; + if (prefix_length > 32) + prefix_length = 32; } if (!parsed) fatal("invalid address '%s'", value); - sa->length = netmask; + sa->length = prefix_length; + if (prefix_length == 0) + sa->type.sa.sa_family = AF_UNSPEC; + *sap = sa; return (ISC_R_SUCCESS); @@ -2467,7 +2473,7 @@ setup_lookup(dig_lookup_t *lookup) { } if (lookup->ecs_addr != NULL) { - isc_uint8_t addr[16], family; + isc_uint8_t addr[16], family, proto; isc_uint32_t plen; struct sockaddr *sa; struct sockaddr_in *sin; @@ -2485,17 +2491,29 @@ setup_lookup(dig_lookup_t *lookup) { opts[i].length = (isc_uint16_t) addrl + 4; check_result(result, "isc_buffer_allocate"); isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf)); - if (sa->sa_family == AF_INET) { + + /* If prefix length is zero, don't set family */ + proto = sa->sa_family; + if (plen == 0) + proto = AF_UNSPEC; + + switch (proto) { + case AF_UNSPEC: + INSIST(plen == 0); + family = 0; + break; + case AF_INET: family = 1; sin = (struct sockaddr_in *) sa; - memcpy(addr, &sin->sin_addr, 4); - if ((plen % 8) != 0) - addr[addrl-1] &= - ~0 << (8 - (plen % 8)); - } else { + memmove(addr, &sin->sin_addr, 4); + break; + case AF_INET6: family = 2; sin6 = (struct sockaddr_in6 *) sa; - memcpy(addr, &sin6->sin6_addr, 16); + memmove(addr, &sin6->sin6_addr, 16); + break; + default: + INSIST(0); } /* Mask off last address byte */ diff --git a/bin/named/client.c b/bin/named/client.c index e0262b3aa8..a824eae8c9 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -1678,7 +1678,7 @@ process_cookie(ns_client_t *client, isc_buffer_t *buf, size_t optlen) { isc_buffer_t db; /* - * If we have already seen a ECS option skip this ECS option. + * If we have already seen a SIT option skip this SIT option. */ if ((client->attributes & NS_CLIENTATTR_WANTSIT) != 0) { isc_buffer_forward(buf, (isc_uint32_t)optlen); diff --git a/lib/dns/message.c b/lib/dns/message.c index d591984a39..c49bfa2878 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3234,6 +3234,9 @@ render_ecs(isc_buffer_t *ecsbuf, isc_buffer_t *target) { addrlen = isc_buffer_getuint8(ecsbuf); scopelen = isc_buffer_getuint8(ecsbuf); + if (addrlen == 0 && family != 0) + return (DNS_R_OPTERR); + addrbytes = (addrlen + 7) / 8; if (isc_buffer_remaininglength(ecsbuf) < addrbytes) return (DNS_R_OPTERR); @@ -3246,15 +3249,23 @@ render_ecs(isc_buffer_t *ecsbuf, isc_buffer_t *target) { for (i = 0; i < addrbytes; i ++) addr[i] = isc_buffer_getuint8(ecsbuf); - if (family == 1) { + switch (family) { + case 0: + if (addrlen != 0U || scopelen != 0U) + return (DNS_R_OPTERR); + strlcpy(addr_text, "0", sizeof(addr_text)); + break; + case 1: if (addrlen > 32 || scopelen > 32) return (DNS_R_OPTERR); inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text)); - } else if (family == 2) { + break; + case 2: if (addrlen > 128 || scopelen > 128) return (DNS_R_OPTERR); inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text)); - } else { + break; + default: snprintf(addr_text, sizeof(addr_text), "Unsupported family %u", family); ADD_STRING(target, addr_text); diff --git a/lib/dns/rdata/generic/opt_41.c b/lib/dns/rdata/generic/opt_41.c index 8a00fdb658..ea0e8cd44c 100644 --- a/lib/dns/rdata/generic/opt_41.c +++ b/lib/dns/rdata/generic/opt_41.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2011-2015 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011-2016 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ - /* Reviewed: Thu Mar 16 14:06:44 PST 2000 by gson */ /* RFC2671 */ @@ -135,7 +133,24 @@ fromwire_opt(ARGS_FROMWIRE) { isc_region_consume(&sregion, 1); scope = uint8_fromregion(&sregion); isc_region_consume(&sregion, 1); + + if (addrlen == 0U && family != 0U) + return (DNS_R_OPTERR); + switch (family) { + case 0: + /* + * XXXMUKS: In queries and replies, if + * FAMILY is set to 0, SOURCE + * PREFIX-LENGTH and SCOPE PREFIX-LENGTH + * must be 0 and ADDRESS should not be + * present as the address and prefix + * lengths don't make sense because the + * family is unknown. + */ + if (addrlen != 0U || scope != 0U) + return (DNS_R_OPTERR); + break; case 1: if (addrlen > 32U || scope > 32U) return (DNS_R_OPTERR); @@ -144,6 +159,8 @@ fromwire_opt(ARGS_FROMWIRE) { if (addrlen > 128U || scope > 128U) return (DNS_R_OPTERR); break; + default: + return (DNS_R_OPTERR); } addrbytes = (addrlen + 7) / 8; if (addrbytes + 4 != length) @@ -166,6 +183,11 @@ fromwire_opt(ARGS_FROMWIRE) { return (DNS_R_OPTERR); isc_region_consume(&sregion, length); break; + case DNS_OPT_COOKIE: + if (length != 8 && (length < 16 || length > 40)) + return (DNS_R_OPTERR); + isc_region_consume(&sregion, length); + break; default: isc_region_consume(&sregion, length); break;