Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 26 Oct 2012 09:03:44 GMT
From:      Steven Hartland <steven.hartland@multiplay.co.uk>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   bin/173118: Add EDNS Client Subnet support to BIND dig (patch included)
Message-ID:  <201210260903.q9Q93iiT019511@red.freebsd.org>
Resent-Message-ID: <201210260910.q9Q9A1Og082862@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>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 <wilmer@google.com>
--- 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:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201210260903.q9Q93iiT019511>