>Number:         173118
>Category:       bin
>Synopsis:       Add EDNS Client Subnet support to BIND dig (patch included)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Fri Oct 26 09:10:00 UTC 2012
>Closed-Date:
>Last-Modified:
>Originator:     Steven Hartland
>Release:        8.3-RELEASE
>Organization:
Multiplay
>Environment:
FreeBSD dev 8.3-RELEASE-p4 FreeBSD 8.3-RELEASE-p4 #22: Mon Sep 17 17:18:32 UTC 
2012     root@dev:/usr/obj/usr/src/sys/MULTIPLAY  amd64
>Description:
Add the ability to send and receive DNS messages with edns-client-subnet 
options to BIND's dig utility.
>How-To-Repeat:
N/A
>Fix:
Apply the attached patch

Patch attached with submission follows:

A patch for bind-9.6.-ESV-R5-P1 (FreeBSD 8.3-RELEASE) based on the work by
Wilmer van der Gaast @
http://wilmer.gaa.st/edns-client-subnet/bind-9.7.1-dig-edns-client-subnet.diff

These patches add the ability to send and receive DNS messages with
edns-client-subnet options to BIND's dig utility.

Example:

wilmer@fiona:~/src/bind-ecs$ bin/dig/dig @ns1.google.com www.google.com 
+client=130.89.89.130

; <<>> DiG 9.7.1-P2 <<>> @ns1.google.com www.google.com +client=130.89.89.130
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 130.89.89.130/32/21
;; QUESTION SECTION:
;www.google.com.                        IN      A

;; ANSWER SECTION:
www.google.com.         604800  IN      CNAME   www.l.google.com.
www.l.google.com.       300     IN      A       74.125.79.104
www.l.google.com.       300     IN      A       74.125.79.99
www.l.google.com.       300     IN      A       74.125.79.147

Copyright 2010 Google Inc.
Author: Wilmer van der Gaast <[email protected]>
--- contrib/bind9/bin/dig/dig.c.orig    2012-05-10 14:21:33.000000000 +0000
+++ contrib/bind9/bin/dig/dig.c 2012-10-16 11:25:33.000000000 +0000
@@ -183,6 +183,7 @@
 "                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
 "                 +ndots=###          (Set NDOTS value)\n"
 "                 +edns=###           (Set EDNS version)\n"
+"                 +client=addr        (Set edns-client-subnet option)\n"
 "                 +[no]search         (Set whether to use searchlist)\n"
 "                 +[no]showsearch     (Search with intermediate results)\n"
 "                 +[no]defname        (Ditto)\n"
@@ -797,8 +798,25 @@
                        lookup->cdflag = state;
                        break;
                case 'l': /* cl */
-                       FULLCHECK("cl");
-                       noclass = ISC_TF(!state);
+                       switch (cmd[2]) {
+                       case 'i':/* client */
+                               FULLCHECK("client");
+                               if (value == NULL)
+                                       goto need_value;
+                               if (state && lookup->edns == -1)
+                                       lookup->edns = 0;
+                               if (parse_netprefix(&lookup->ecs_addr,
+                                                   &lookup->ecs_len,
+                                                   value) != ISC_R_SUCCESS)
+                                       fatal("Couldn't parse client");
+                               break;
+                       case '\0':
+                               FULLCHECK("cl");
+                               noclass = ISC_TF(!state);
+                               break;
+                       default:
+                               goto invalid_option;
+                       }
                        break;
                case 'm': /* cmd */
                        FULLCHECK("cmd");
--- contrib/bind9/bin/dig/dighost.c.orig        2012-05-10 14:21:34.000000000 
+0000
+++ contrib/bind9/bin/dig/dighost.c     2012-10-16 12:02:23.000000000 +0000
@@ -66,6 +66,7 @@
 
 #include <dst/dst.h>
 
+#include <isc/parseint.h>
 #include <isc/app.h>
 #include <isc/base64.h>
 #include <isc/entropy.h>
@@ -91,6 +92,9 @@
 
 #include <dig/dig.h>
 
+/* parse_netprefix */
+#include <netdb.h>
+
 #if ! defined(NS_INADDRSZ)
 #define NS_INADDRSZ     4
 #endif
@@ -763,6 +767,8 @@
        looknew->new_search = ISC_FALSE;
        looknew->done_as_is = ISC_FALSE;
        looknew->need_search = ISC_FALSE;
+       looknew->ecs_addr = NULL;
+       looknew->ecs_len = 0;
        ISC_LINK_INIT(looknew, link);
        ISC_LIST_INIT(looknew->q);
        ISC_LIST_INIT(looknew->my_server_list);
@@ -779,6 +785,7 @@
 dig_lookup_t *
 clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
        dig_lookup_t *looknew;
+       size_t len;
 
        debug("clone_lookup()");
 
@@ -839,6 +846,19 @@
        looknew->need_search = lookold->need_search;
        looknew->done_as_is = lookold->done_as_is;
 
+       if (lookold->ecs_addr) {
+               if (lookold->ecs_addr->sa_family == AF_INET)
+                       len = sizeof(struct sockaddr_in);
+               else if (lookold->ecs_addr->sa_family == AF_INET6)
+                       len = sizeof(struct sockaddr_in6);
+               else
+                       INSIST(0);
+
+               looknew->ecs_addr = isc_mem_allocate(mctx, len);
+               memcpy(looknew->ecs_addr, lookold->ecs_addr, len);
+               looknew->ecs_len = lookold->ecs_len;
+       }
+
        if (servers)
                clone_server_list(lookold->my_server_list,
                                  &looknew->my_server_list);
@@ -922,6 +942,48 @@
        isc_buffer_free(&namebuf);
 }
 
+isc_result_t
+parse_netprefix(struct sockaddr **sa, isc_uint32_t *netmask,
+                const char *value) {
+       struct addrinfo *res, hints;
+       char *addr, *slash;
+       isc_uint32_t result;
+       
+       addr = isc_mem_strdup(mctx, value);
+       if ((slash = strchr(addr, '/'))) {
+               *slash = '\0';
+               result = isc_parse_uint32(netmask, slash + 1, 10);
+               if (result != ISC_R_SUCCESS) {
+                       isc_mem_free(mctx, addr);
+                       printf("invalid %s '%s': %s\n", "prefix length",
+                              value, isc_result_totext(result));
+                       return (result);
+               }
+       } else {
+               *netmask = 128;
+       }
+       
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_flags = AI_NUMERICHOST;
+       if ((result = getaddrinfo(addr, NULL, &hints, &res)) != 0) {
+               isc_mem_free(mctx, addr);
+               printf("getaddrinfo() error: %s\n", gai_strerror(result));
+               return ISC_R_FAILURE;
+       }
+       isc_mem_free(mctx, addr);
+       
+       *sa = isc_mem_allocate(mctx, res->ai_addrlen);
+       memcpy(*sa, res->ai_addr, res->ai_addrlen);
+       
+       if (res->ai_family == AF_INET && *netmask > 32)
+               *netmask = 32;
+       else if (res->ai_family == AF_INET6 && *netmask > 128)
+               *netmask = 128;
+
+       freeaddrinfo(res);
+       return (ISC_R_SUCCESS);
+}
+
 static void
 setup_file_key(void) {
        isc_result_t result;
@@ -1174,12 +1236,14 @@
  */
 static void
 add_opt(dns_message_t *msg, isc_uint16_t udpsize, isc_uint16_t edns,
-       isc_boolean_t dnssec, isc_boolean_t nsid)
+       isc_boolean_t dnssec, isc_boolean_t nsid,
+       struct sockaddr *ecs_addr, isc_uint32_t ecs_len)
 {
        dns_rdataset_t *rdataset = NULL;
        dns_rdatalist_t *rdatalist = NULL;
        dns_rdata_t *rdata = NULL;
        isc_result_t result;
+       isc_buffer_t *b = NULL;
 
        debug("add_opt()");
        result = dns_message_gettemprdataset(msg, &rdataset);
@@ -1197,20 +1261,40 @@
        rdatalist->ttl = edns << 16;
        if (dnssec)
                rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO;
+
+       result = isc_buffer_allocate(mctx, &b, 64);
+       check_result(result, "isc_buffer_allocate");
        if (nsid) {
-               isc_buffer_t *b = NULL;
 
-               result = isc_buffer_allocate(mctx, &b, 4);
-               check_result(result, "isc_buffer_allocate");
                isc_buffer_putuint16(b, DNS_OPT_NSID);
                isc_buffer_putuint16(b, 0);
+       }
+       if (ecs_addr) {
+               size_t addrl = (ecs_len + 7) / 8;
+               
+               isc_buffer_putuint16(b, DNS_OPT_CLIENT_SUBNET);
+               isc_buffer_putuint16(b, 4 + addrl);
+               if (ecs_addr->sa_family == AF_INET) {
+                       struct sockaddr_in *ad = (struct sockaddr_in *) 
ecs_addr;
+                       isc_buffer_putuint16(b, 1);
+                       isc_buffer_putuint8(b, ecs_len);
+                       isc_buffer_putuint8(b, 0);
+                       isc_buffer_putmem(b, (isc_uint8_t*) &ad->sin_addr, 
addrl);
+               }
+               else /* if (ecs_addr->sa_family == AF_INET6) */ {
+                       struct sockaddr_in6 *ad = (struct sockaddr_in6 *) 
ecs_addr;
+                       isc_buffer_putuint16(b, 2);
+                       isc_buffer_putuint8(b, ecs_len);
+                       isc_buffer_putuint8(b, 0);
+                       isc_buffer_putmem(b, (isc_uint8_t*) &ad->sin6_addr, 
addrl);
+               }
+       }
+       if ((rdata->length = isc_buffer_usedlength(b)) > 0) {
                rdata->data = isc_buffer_base(b);
-               rdata->length = isc_buffer_usedlength(b);
                dns_message_takebuffer(msg, &b);
-       } else {
+       } else
                rdata->data = NULL;
-               rdata->length = 0;
-       }
+
        ISC_LIST_INIT(rdatalist->rdata);
        ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
        dns_rdatalist_tordataset(rdatalist, rdataset);
@@ -1359,6 +1443,9 @@
        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);
 }
 
@@ -2031,7 +2118,8 @@
                if (lookup->edns < 0)
                        lookup->edns = 0;
                add_opt(lookup->sendmsg, lookup->udpsize,
-                       lookup->edns, lookup->dnssec, lookup->nsid);
+                       lookup->edns, lookup->dnssec, lookup->nsid,
+                       lookup->ecs_addr, lookup->ecs_len);
        }
 
        result = dns_message_rendersection(lookup->sendmsg,
@@ -5336,7 +5424,6 @@
 #endif
 }
 
-
 /*
  * return 1  if name1  <  name2
  *       0  if name1  == name2
--- contrib/bind9/bin/dig/include/dig/dig.h.orig        2012-05-10 
14:21:34.000000000 +0000
+++ contrib/bind9/bin/dig/include/dig/dig.h     2012-10-16 11:25:33.000000000 
+0000
@@ -183,6 +183,8 @@
        isc_buffer_t *querysig;
        isc_uint32_t msgcounter;
        dns_fixedname_t fdomain;
+       struct sockaddr *ecs_addr; /*% edns-client-subnet */
+       isc_uint32_t ecs_len;
 };
 
 /*% The dig_query structure */
@@ -325,6 +327,10 @@
 void
 setup_libs(void);
 
+isc_result_t
+parse_netprefix(struct sockaddr **sa, isc_uint32_t *netmask,
+                const char *value);
+
 void
 setup_system(void);
 
--- contrib/bind9/lib/dns/message.c.orig        2012-05-10 14:21:48.000000000 
+0000
+++ contrib/bind9/lib/dns/message.c     2012-10-20 00:56:27.157757171 +0000
@@ -3189,6 +3189,35 @@
 
                        if (optcode == DNS_OPT_NSID) {
                                ADD_STRING(target, "; NSID");
+                       } else if (optcode == DNS_OPT_CLIENT_SUBNET) {
+                               int i;
+                               char addr[16], addr_text[64];
+                               isc_uint16_t family;
+                               isc_uint8_t addrlen, addrbytes, scopelen;
+                               
+                               family = isc_buffer_getuint16(&optbuf);
+                               addrlen = isc_buffer_getuint8(&optbuf);
+                               scopelen = isc_buffer_getuint8(&optbuf);
+                               addrbytes = (addrlen + 7) / 8;
+                               memset(addr, 0, sizeof(addr));
+                               for (i = 0; i < addrbytes; i ++)
+                                       addr[i] = isc_buffer_getuint8(&optbuf);
+                               
+                               ADD_STRING(target, "; CLIENT-SUBNET: ");
+                               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);
+                               sprintf(addr_text, "/%d/%d", addrlen, scopelen);
+                               ADD_STRING(target, addr_text);
+
+                               /* Disable the dumb byte representation below. 
*/
+                               optlen = 0;
                        } else {
                                ADD_STRING(target, "; OPT=");
                                sprintf(buf, "%u", optcode);
--- contrib/bind9/lib/dns/include/dns/message.h.orig    2012-05-10 
14:21:48.000000000 +0000
+++ contrib/bind9/lib/dns/include/dns/message.h 2012-10-20 00:56:27.155745851 
+0000
@@ -106,6 +106,7 @@
 
 /*%< EDNS0 extended OPT codes */
 #define DNS_OPT_NSID           0x0003          /*%< NSID opt code */
+#define DNS_OPT_CLIENT_SUBNET  0x50fa          /*%< client subnet opt code */
 
 #define DNS_MESSAGE_REPLYPRESERVE      (DNS_MESSAGEFLAG_RD|DNS_MESSAGEFLAG_CD)
 #define DNS_MESSAGEEXTFLAG_REPLYPRESERVE (DNS_MESSAGEEXTFLAG_DO)


>Release-Note:
>Audit-Trail:
>Unformatted:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "[email protected]"

Reply via email to