]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] ENDS client-subnet in dig
authorEvan Hunt <each@isc.org>
Wed, 19 Feb 2014 23:51:02 +0000 (15:51 -0800)
committerEvan Hunt <each@isc.org>
Wed, 19 Feb 2014 23:51:02 +0000 (15:51 -0800)
3749. [func] "dig +subnet" sends an EDNS client subnet option
containing the specified address/prefix when
                        querying. (Thanks to Wilmer van der Gaast.)
                        [RT #35415]

CHANGES
README
bin/dig/dig.c
bin/dig/dig.docbook
bin/dig/dighost.c
bin/dig/include/dig/dig.h
lib/dns/message.c

diff --git a/CHANGES b/CHANGES
index 01db1c2918d59bb0253ccfcb6650ed31e88c00c2..f4eaf4ce4fec9bcba64e3bee0298e1e3dfc445e0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+3749.  [func]          "dig +subnet" sends an EDNS client subnet option
+                       containing the specified address/prefix when
+                        querying. (Thanks to Wilmer van der Gaast.)
+                        [RT #35415]
+
 3748.  [test]          Use delve to test dns_client interfaces. [RT #35383]
 
 3747.  [bug]           A race condition could lead to a core dump when
diff --git a/README b/README
index 604ee31b263088da6159c2648c4fa3a924411e18..b98de2cb394fcb89619f321f79f7693f8797d9ae 100644 (file)
--- a/README
+++ b/README
@@ -127,6 +127,8 @@ BIND 9.10.0
           zones. This can simplify the process of rolling DNSSEC keys
           by guaranteeing that cached signatures will have expired
           within the specified amount of time.
+        - "dig +subnet" sends an EDNS client-subnet option when
+           querying.
         - New "dnssec-coverage" tool to check DNSSEC key coverage
           for a zone and report if a lapse in signing coverage has
           been inadvertently scheduled.
index adafdb660ddeba2dd4cc31ae104940a7d99cfbf7..dd1946fc9018c13dfe7765253eff45916b0546f4 100644 (file)
@@ -192,6 +192,7 @@ help(void) {
 "                 +domain=###         (Set default domainname)\n"
 "                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
 "                 +ndots=###          (Set NDOTS value)\n"
+"                 +subnet=addr        (Set edns-client-subnet option)\n"
 "                 +[no]edns[=###]     (Set EDNS version) [0]\n"
 "                 +[no]search         (Set whether to use searchlist)\n"
 "                 +[no]showsearch     (Search with intermediate results)\n"
@@ -1156,6 +1157,23 @@ plus_option(char *option, isc_boolean_t is_batchfile,
                        FULLCHECK("stats");
                        lookup->stats = state;
                        break;
+               case 'u': /* subnet */
+                       FULLCHECK("subnet");
+                       if (state && value == NULL)
+                               goto need_value;
+                       if (!state) {
+                               if (lookup->ecs_addr != NULL) {
+                                       isc_mem_free(mctx, lookup->ecs_addr);
+                                       lookup->ecs_addr = NULL;
+                               }
+                               break;
+                       }
+                       if (lookup->edns == -1)
+                               lookup->edns = 0;
+                       result = parse_netprefix(&lookup->ecs_addr, value);
+                       if (result != ISC_R_SUCCESS)
+                               fatal("Couldn't parse client");
+                       break;
                default:
                        goto invalid_option;
                }
index 2db8a5b9f273a469d466d805d511fc62f482b8e2..e918822839216e4387b95b051779cc24350360f5 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><option>+[no]subnet=addr/prefix<option></term>
+          <listitem>
+            <para>
+             Send an EDNS Client Subnet option with the speciifed
+              IP address or network prefix.
+            </para>
+          </listitem>
+        </varlistentry>
+
       </variablelist>
 
     </para>
index 79381accd2caef8148f3c7f7c01e74b8c51ddb1a..6eede932214505cf64595359b8fd42916a4c961c 100644 (file)
 #include <isc/lang.h>
 #include <isc/log.h>
 #include <isc/netaddr.h>
-#ifdef DIG_SIGCHASE
 #include <isc/netdb.h>
-#endif
 #include <isc/parseint.h>
 #include <isc/print.h>
 #include <isc/random.h>
 #include <isc/result.h>
 #include <isc/serial.h>
+#include <isc/sockaddr.h>
 #include <isc/string.h>
 #include <isc/task.h>
 #include <isc/timer.h>
@@ -807,6 +806,7 @@ make_empty_lookup(void) {
        looknew->new_search = ISC_FALSE;
        looknew->done_as_is = ISC_FALSE;
        looknew->need_search = ISC_FALSE;
+       looknew->ecs_addr = NULL;
 #ifdef ISC_PLATFORM_USESIT
        looknew->sitvalue = NULL;
 #endif
@@ -891,6 +891,12 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
        looknew->need_search = lookold->need_search;
        looknew->done_as_is = lookold->done_as_is;
 
+       if (lookold->ecs_addr != NULL) {
+               size_t len = sizeof(isc_sockaddr_t);
+               looknew->ecs_addr = isc_mem_allocate(mctx, len);
+               memmove(looknew->ecs_addr, lookold->ecs_addr, len);
+       }
+
        if (servers)
                clone_server_list(lookold->my_server_list,
                                  &looknew->my_server_list);
@@ -1005,6 +1011,65 @@ parse_bits(char *arg, const char *desc, isc_uint32_t max) {
        return (tmp);
 }
 
+isc_result_t
+parse_netprefix(isc_sockaddr_t **sap, const char *value) {
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_sockaddr_t *sa = NULL;
+       struct in_addr in4;
+       struct in6_addr in6;
+       isc_uint32_t netmask = 0;
+       char *slash = NULL;
+       isc_boolean_t parsed = ISC_FALSE;
+
+       if ((slash = strchr(value, '/'))) {
+               *slash = '\0';
+               result = isc_parse_uint32(&netmask, slash + 1, 10);
+               if (result != ISC_R_SUCCESS) {
+                       *slash = '/';
+                       fatal("invalid prefix length '%s': %s\n",
+                             value, isc_result_totext(result));
+               }
+       }
+
+       sa = isc_mem_allocate(mctx, sizeof(*sa));
+       if (inet_pton(AF_INET6, value, &in6) == 1) {
+               isc_sockaddr_fromin6(sa, &in6, 0);
+               parsed = ISC_TRUE;
+               if (netmask == 0 || netmask > 128)
+                       netmask = 128;
+       } else if (inet_pton(AF_INET, value, &in4) == 1) {
+               parsed = ISC_TRUE;
+               isc_sockaddr_fromin(sa, &in4, 0);
+               if (netmask == 0 || netmask > 32)
+                       netmask = 32;
+       } else if (netmask != 0) {
+               char buf[64];
+               int i;
+
+               strlcpy(buf, value, sizeof(buf));
+               for (i = 0; i < 3; i++) {
+                       strlcat(buf, ".0", sizeof(buf));
+                       if (inet_pton(AF_INET, buf, &in4) == 1) {
+                               parsed = ISC_TRUE;
+                               isc_sockaddr_fromin(sa, &in4, 0);
+                               break;
+                       }
+               }
+
+       }
+
+       if (slash != NULL)
+               *slash = '/';
+
+       if (!parsed)
+               fatal("invalid address '%s'", value);
+
+       sa->length = netmask;
+       *sap = sa;
+
+       return (ISC_R_SUCCESS);
+}
+
 
 /*
  * Parse HMAC algorithm specification
@@ -1394,7 +1459,8 @@ setup_libs(void) {
 
 /*%
  * Add EDNS0 option record to a message.  Currently, the only supported
- * options are UDP buffer size, the DO bit, and NSID request.
+ * options are UDP buffer size, the DO bit, and EDNS options
+ * (e.g., NSID, SIT, client-subnet)
  */
 static void
 add_opt(dns_message_t *msg, isc_uint16_t udpsize, isc_uint16_t edns,
@@ -1568,6 +1634,9 @@ destroy_lookup(dig_lookup_t *lookup) {
        if (lookup->tsigctx != NULL)
                dst_context_destroy(&lookup->tsigctx);
 
+       if (lookup->ecs_addr != NULL)
+               isc_mem_free(mctx, lookup->ecs_addr);
+
        isc_mem_free(mctx, lookup);
 }
 
@@ -2022,6 +2091,10 @@ setup_lookup(dig_lookup_t *lookup) {
        isc_buffer_t b;
        dns_compress_t cctx;
        char store[MXNAME];
+       char ecsbuf[20];
+#ifdef ISC_PLATFORM_USESIT
+       char sitbuf[256];
+#endif
 #ifdef WITH_IDN
        idn_result_t mr;
        char utf8_textname[MXNAME], utf8_origin[MXNAME], idn_textname[MXNAME];
@@ -2273,14 +2346,18 @@ setup_lookup(dig_lookup_t *lookup) {
        result = dns_message_renderbegin(lookup->sendmsg, &cctx,
                                         &lookup->renderbuf);
        check_result(result, "dns_message_renderbegin");
-       if (lookup->udpsize > 0 || lookup->dnssec || lookup->edns > -1) {
-#define EDNSOPTS 2
+       if (lookup->udpsize > 0 || lookup->dnssec ||
+           lookup->edns > -1 || lookup->ecs_addr != NULL)
+       {
+#define EDNSOPTS 3
                dns_ednsopt_t opts[EDNSOPTS];
                int i = 0;
+
                if (lookup->udpsize == 0)
                        lookup->udpsize = 4096;
                if (lookup->edns < 0)
                        lookup->edns = 0;
+
                if (lookup->nsid) {
                        INSIST(i < EDNSOPTS);
                        opts[i].code = DNS_OPT_NSID;
@@ -2288,15 +2365,56 @@ setup_lookup(dig_lookup_t *lookup) {
                        opts[i].value = NULL;
                        i++;
                }
+
+               if (lookup->ecs_addr != NULL) {
+                       isc_uint32_t prefixlen;
+                       struct sockaddr *sa;
+                       struct sockaddr_in *sin;
+                       struct sockaddr_in6 *sin6;
+                       size_t addrl;
+                       isc_buffer_t b;
+
+                       sa = &lookup->ecs_addr->type.sa;
+                       prefixlen = lookup->ecs_addr->length;
+
+                       /* Round up prefix len to a multiple of 8 */
+                       addrl = (prefixlen + 7) / 8;
+
+                       INSIST(i < EDNSOPTS);
+                       opts[i].code = DNS_OPT_CLIENT_SUBNET;
+                       opts[i].length = addrl + 4;
+                       check_result(result, "isc_buffer_allocate");
+                       isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf));
+                       if (sa->sa_family == AF_INET) {
+                               sin = (struct sockaddr_in *) sa;
+                               isc_buffer_putuint16(&b, 1);
+                               isc_buffer_putuint8(&b, prefixlen);
+                               isc_buffer_putuint8(&b, 0);
+                               isc_buffer_putmem(&b,
+                                         (isc_uint8_t *) &sin->sin_addr,
+                                         addrl);
+                       } else {
+                               sin6 = (struct sockaddr_in6 *) sa;
+                               isc_buffer_putuint16(&b, 2);
+                               isc_buffer_putuint8(&b, prefixlen);
+                               isc_buffer_putuint8(&b, 0);
+                               isc_buffer_putmem(&b,
+                                         (isc_uint8_t *) &sin6->sin6_addr,
+                                         addrl);
+                       }
+
+                       opts[i].value = (isc_uint8_t *) ecsbuf;
+                       i++;
+               }
+
 #ifdef ISC_PLATFORM_USESIT
                if (lookup->sit) {
                        INSIST(i < EDNSOPTS);
                        opts[i].code = DNS_OPT_SIT;
                        if (lookup->sitvalue != NULL) {
-                               char bb[256];
                                isc_buffer_t b;
 
-                               isc_buffer_init(&b, bb, sizeof(bb));
+                               isc_buffer_init(&b, sitbuf, sizeof(sitbuf));
                                result = isc_hex_decodestring(lookup->sitvalue,
                                                              &b);
                                check_result(result, "isc_hex_decodestring");
@@ -2310,6 +2428,7 @@ setup_lookup(dig_lookup_t *lookup) {
                        i++;
                }
 #endif
+
                add_opt(lookup->sendmsg, lookup->udpsize,
                        lookup->edns, lookup->dnssec, opts, i);
        }
@@ -2377,6 +2496,7 @@ setup_lookup(dig_lookup_t *lookup) {
                ISC_LINK_INIT(query, link);
                ISC_LIST_ENQUEUE(lookup->q, query, link);
        }
+
        /* XXX qrflag, print_query, etc... */
        if (!ISC_LIST_EMPTY(lookup->q) && qr) {
                extrabytes = 0;
index 83cd24d2ecf6200ff28e7f8716a4f0d55728d11c..ad4c7c2ab2171fb407d246c26a33a3de54cc8386 100644 (file)
@@ -187,6 +187,7 @@ isc_boolean_t       sigchase;
        isc_buffer_t *querysig;
        isc_uint32_t msgcounter;
        dns_fixedname_t fdomain;
+       isc_sockaddr_t *ecs_addr;
 #ifdef ISC_PLATFORM_USESIT
        char *sitvalue;
 #endif
@@ -343,6 +344,9 @@ isc_result_t
 parse_uint(isc_uint32_t *uip, const char *value, isc_uint32_t max,
           const char *desc);
 
+isc_result_t
+parse_netprefix(isc_sockaddr_t **sap, const char *value);
+
 void
 parse_hmac(const char *hmacstr);
 
index 20358195ca485e5ac05b6a08d6eadbe959f8f192..2b40cc64de1c40aa748e98b8251f6df7c9a3d46e 100644 (file)
@@ -3191,6 +3191,43 @@ dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
        return (result);
 }
 
+static isc_result_t
+render_ecs(isc_buffer_t *optbuf, isc_buffer_t *target) {
+       int i;
+       char addr[16], addr_text[64];
+       isc_uint16_t family;
+       isc_uint8_t addrlen, addrbytes, scopelen;
+
+       INSIST(isc_buffer_remaininglength(optbuf) >= 4);
+       family = isc_buffer_getuint16(optbuf);
+       addrlen = isc_buffer_getuint8(optbuf);
+       scopelen = isc_buffer_getuint8(optbuf);
+
+       addrbytes = (addrlen + 7) / 8;
+       INSIST(isc_buffer_remaininglength(optbuf) >= addrbytes);
+
+       memset(addr, 0, sizeof(addr));
+       for (i = 0; i < addrbytes; i ++)
+               addr[i] = isc_buffer_getuint8(optbuf);
+
+       if (family == 1)
+               inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text));
+       else if (family == 2)
+               inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text));
+       else {
+               snprintf(addr_text, sizeof(addr_text),
+                        "Unsupported family %d",
+                        family);
+               ADD_STRING(target, addr_text);
+               return (ISC_R_SUCCESS);
+       }
+
+       ADD_STRING(target, addr_text);
+       sprintf(addr_text, "/%d/%d", addrlen, scopelen);
+       ADD_STRING(target, addr_text);
+       return (ISC_R_SUCCESS);
+}
+
 isc_result_t
 dns_message_pseudosectiontotext(dns_message_t *msg,
                                dns_pseudosection_t section,
@@ -3257,6 +3294,11 @@ dns_message_pseudosectiontotext(dns_message_t *msg,
                                ADD_STRING(target, "; NSID");
                        } else if (optcode == DNS_OPT_SIT) {
                                ADD_STRING(target, "; SIT");
+                       } else if (optcode == DNS_OPT_CLIENT_SUBNET) {
+                               ADD_STRING(target, "; CLIENT-SUBNET: ");
+                               render_ecs(&optbuf, target);
+                               ADD_STRING(target, "\n");
+                               break;
                        } else {
                                ADD_STRING(target, "; OPT=");
                                sprintf(buf, "%u", optcode);