]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] fix ECS with family==0
authorEvan Hunt <each@isc.org>
Wed, 23 Mar 2016 15:54:46 +0000 (08:54 -0700)
committerEvan Hunt <each@isc.org>
Wed, 23 Mar 2016 15:54:46 +0000 (08:54 -0700)
4341. [bug] Correct the handling of ECS options with
address family 0. [RT #41377]

CHANGES
bin/dig/dighost.c
bin/named/client.c
bin/tools/mdig.c
lib/dns/message.c
lib/dns/rdata/generic/opt_41.c

diff --git a/CHANGES b/CHANGES
index 98d58a5925d34159f3c426b513e22b0f869bf168..a5d88d80ebcc78f4645ed37f87903d3e4951d31a 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,8 @@
        --- 9.11.0a1 released ---
 
+4341.  [bug]           Correct the handling of ECS options with
+                       address family 0. [RT #41377]
+
 4340.  [performance]   Implement adaptive read-write locks, reducing the
                        overhead of locks that are only held briefly.
                        [RT #37329]
index 665b1ce37008bf1702028ea43e44bbd8574cbfa1..a6ecf15114892c254935e2f1d60eb680083c70c5 100644 (file)
@@ -2527,14 +2527,14 @@ setup_lookup(dig_lookup_t *lookup) {
                        if (sa->sa_family == AF_INET) {
                                family = 1;
                                sin = (struct sockaddr_in *) sa;
-                               memcpy(addr, &sin->sin_addr, 4);
+                               memmove(addr, &sin->sin_addr, 4);
                                if ((plen % 8) != 0)
                                        addr[addrl-1] &=
                                                ~0 << (8 - (plen % 8));
                        } else {
                                family = 2;
                                sin6 = (struct sockaddr_in6 *) sa;
-                               memcpy(addr, &sin6->sin6_addr, 16);
+                               memmove(addr, &sin6->sin6_addr, 16);
                        }
 
                        /* Mask off last address byte */
index 1a25ee21f74c34b250cce0d80bd3a110bb28d669..159ee8b9be808714a9044efbcaac53343d90298a 100644 (file)
@@ -1894,7 +1894,6 @@ process_ecs(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
        isc_uint16_t family;
        isc_uint8_t addrlen, addrbytes, scope, *paddr;
        isc_netaddr_t caddr;
-       int i;
 
        /*
         * If we have already seen a ECS option skip this ECS option.
@@ -1929,21 +1928,51 @@ process_ecs(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
                return (DNS_R_OPTERR);
        }
 
+       if (addrlen == 0 && family != 0) {
+               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+                             "EDNS client-subnet option: "
+                             "source == 0 but family != 0");
+               return (DNS_R_OPTERR);
+       }
+
        memset(&caddr, 0, sizeof(caddr));
        switch (family) {
+       case 0:
+               /*
+                * XXXMUKS: In queries, if FAMILY is set to 0, SOURCE
+                * 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) {
+                       ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+                                     "EDNS client-subnet option: invalid "
+                                     "address length (%u) for FAMILY=0",
+                                     addrlen);
+                       return (DNS_R_OPTERR);
+               }
+               caddr.family = AF_UNSPEC;
+               break;
        case 1:
-               if (addrlen > 32U)
-                       goto invalid_length;
+               if (addrlen > 32U) {
+                       ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+                                     "EDNS client-subnet option: invalid "
+                                     "address length (%u) for IPv4",
+                                     addrlen);
+                       return (DNS_R_OPTERR);
+               }
                caddr.family = AF_INET;
                break;
        case 2:
                if (addrlen > 128U) {
-       invalid_length:
                        ns_client_log(client, NS_LOGCATEGORY_CLIENT,
                                      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
                                      "EDNS client-subnet option: invalid "
-                                     "address length (%u) for %s",
-                                     addrlen, family == 1 ? "IPv4" : "IPv6");
+                                     "address length (%u) for IPv6",
+                                     addrlen);
                        return (DNS_R_OPTERR);
                }
                caddr.family = AF_INET6;
@@ -1964,16 +1993,17 @@ process_ecs(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
        }
 
        paddr = (isc_uint8_t *) &caddr.type;
-       for (i = 0; i < addrbytes; i++) {
-               paddr[i] = isc_buffer_getuint8(buf);
-               optlen--;
-       }
-
-       if (addrbytes != 0U && (addrlen % 8) != 0) {
-               isc_uint8_t bits = ~0 << (8 - (addrlen % 8));
-               bits &= paddr[addrbytes - 1];
-               if (bits != paddr[addrbytes - 1])
-                       return (DNS_R_OPTERR);
+       if (addrbytes != 0U) {
+               memmove(paddr, isc_buffer_current(buf), addrbytes);
+               isc_buffer_forward(buf, addrbytes);
+               optlen -= addrbytes;
+
+               if ((addrlen % 8) != 0) {
+                       isc_uint8_t bits = ~0 << (8 - (addrlen % 8));
+                       bits &= paddr[addrbytes - 1];
+                       if (bits != paddr[addrbytes - 1])
+                               return (DNS_R_OPTERR);
+               }
        }
 
        memmove(&client->ecs_addr, &caddr, sizeof(caddr));
index 6e2d7d75007a6c07a6f594ac897f841153040644..d60182d5b057b6e75ec417f206bd4a99ee81cf52 100644 (file)
@@ -611,14 +611,14 @@ sendquery(struct query *query, isc_task_t *task)
                        if (sa->sa_family == AF_INET) {
                                family = 1;
                                sin = (struct sockaddr_in *) sa;
-                               memcpy(addr, &sin->sin_addr, 4);
+                               memmove(addr, &sin->sin_addr, 4);
                                if ((plen % 8) != 0)
                                        addr[addrl-1] &=
                                                ~0 << (8 - (plen % 8));
                        } else {
                                family = 2;
                                sin6 = (struct sockaddr_in6 *) sa;
-                               memcpy(addr, &sin6->sin6_addr, 16);
+                               memmove(addr, &sin6->sin6_addr, 16);
                        }
 
                        /* Mask off last address byte */
index d05f218d8738090fd5d935c3dab2ecc645c062e4..0abd1671ca8ac467aa06655b63de85b83ce3f900 100644 (file)
@@ -3244,6 +3244,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);
@@ -3256,15 +3259,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);
index bc2de3790884d47d5613d643691da026d3c12a7f..55e3718e7689fabbaf80ab51536b96d3fccafc83 100644 (file)
@@ -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);