Package: dnsutils
Version: 1:9.9.5.dfsg-8
Severity: wishlist
Tags: patch
X-Debbugs-CC: lin...@debian.org
Hi maintainers,
There's a patch available to add support for the "edns-client-subnet"
extension to the `dig` command. The edns-client-subnet extension lets an
intermediate DNS server (like Google Public DNS or OpenDNS) relay the
client's original subnet to the destination DNS server, so that DNS-based
CDNs can geolocate the actual client, not the IP address of the
intermediate DNS server. There's more discussion and a link to the patch
and the Internet-Draft on this site:
http://www.afasterinternet.com/howitworks.htm
For testing purposes it's handy to be able to issue these queries from the
command line, so you can see what DNS results would look like if you had a
different IP address.
The patches linked above are minimal and easy to review (it doesn't look
unreasonable at a casual glance, but I'm completely unfamiliar with BIND
source), and apply cleanly to the version in unstable, except for one
#define that was equivalently added upstream. I've built it locally and
confirmed that it works, and I've attached my debdiff if that helps.
Can you consider including this? Or if you want upstream to weigh in, do
you mind forwarding this request upstream? It looks like BIND has no
public bugtracker, so I don't quite understand the process of requesting
this from upstream.
Cc'ing the patch author -- I'm curious if there was any particular reason
you didn't submit it to Debian (or if I missed the bug where you did), or
you just didn't think there was interest or something.
Thanks,
--
Geoffrey Thomas
https://ldpreload.com
geo...@ldpreload.com
diff -u bind9-9.9.5.dfsg/debian/changelog bind9-9.9.5.dfsg/debian/changelog
--- bind9-9.9.5.dfsg/debian/changelog
+++ bind9-9.9.5.dfsg/debian/changelog
@@ -1,3 +1,10 @@
+bind9 (1:9.9.5.dfsg-8+geofft1) unstable; urgency=medium
+
+ * Apply the EDNS client subnet patch from
+ http://wilmer.gaa.st/edns-client-subnet/
+
+ -- Geoffrey Thomas <geo...@ldpreload.com> Sat, 14 Feb 2015 17:27:30 -0500
+
bind9 (1:9.9.5.dfsg-8) unstable; urgency=medium
* Launch rndc command in the background in networking scripts to avoid a
only in patch2:
unchanged:
--- bind9-9.9.5.dfsg.orig/bin/dig/dig.c
+++ bind9-9.9.5.dfsg/bin/dig/dig.c
@@ -187,6 +187,7 @@
" +domain=### (Set default domainname)\n"
" +bufsize=### (Set EDNS0 Max UDP packet size)\n"
" +ndots=### (Set NDOTS value)\n"
+" +client=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"
@@ -837,8 +838,25 @@
}
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");
only in patch2:
unchanged:
--- bind9-9.9.5.dfsg.orig/bin/dig/dighost.c
+++ bind9-9.9.5.dfsg/bin/dig/dighost.c
@@ -100,6 +100,9 @@
#include <dig/dig.h>
+/* parse_netprefix */
+#include <netdb.h>
+
#if ! defined(NS_INADDRSZ)
#define NS_INADDRSZ 4
#endif
@@ -801,6 +804,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->connecting);
@@ -818,6 +823,7 @@
dig_lookup_t *
clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
dig_lookup_t *looknew;
+ size_t len;
debug("clone_lookup()");
@@ -878,6 +884,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);
@@ -992,6 +1011,48 @@
return (tmp);
}
+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);
+}
+
/*
* Parse HMAC algorithm specification
@@ -1377,15 +1438,17 @@
/*%
* 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 NSID/edns-client-subnet.
*/
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_buffer_t *buf = NULL;
isc_result_t result;
debug("add_opt()");
@@ -1404,20 +1467,42 @@
rdatalist->ttl = edns << 16;
if (dnssec)
rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO;
+
+ result = isc_buffer_allocate(mctx, &buf, 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);
- rdata->data = isc_buffer_base(b);
- rdata->length = isc_buffer_usedlength(b);
- dns_message_takebuffer(msg, &b);
- } else {
- rdata->data = NULL;
- rdata->length = 0;
+ isc_buffer_putuint16(buf, DNS_OPT_NSID);
+ isc_buffer_putuint16(buf, 0);
}
+
+ if (ecs_addr) {
+ size_t addrl = (ecs_len + 7) / 8;
+
+ isc_buffer_putuint16(buf, DNS_OPT_CLIENT_SUBNET);
+ isc_buffer_putuint16(buf, 4 + addrl);
+ if (ecs_addr->sa_family == AF_INET) {
+ struct sockaddr_in *ad = (struct sockaddr_in *)
ecs_addr;
+ isc_buffer_putuint16(buf, 1);
+ isc_buffer_putuint8(buf, ecs_len);
+ isc_buffer_putuint8(buf, 0);
+ isc_buffer_putmem(buf, (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(buf, 2);
+ isc_buffer_putuint8(buf, ecs_len);
+ isc_buffer_putuint8(buf, 0);
+ isc_buffer_putmem(buf, (isc_uint8_t*) &ad->sin6_addr,
addrl);
+ }
+ }
+
+ if ((rdata->length = isc_buffer_usedlength(buf)) > 0)
+ rdata->data = isc_buffer_base(buf);
+ else
+ rdata->data = NULL;
+ dns_message_takebuffer(msg, &buf);
+
ISC_LIST_INIT(rdatalist->rdata);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
dns_rdatalist_tordataset(rdatalist, rdataset);
@@ -1579,6 +1664,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);
}
@@ -2281,7 +2369,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,
only in patch2:
unchanged:
--- bind9-9.9.5.dfsg.orig/bin/dig/include/dig/dig.h
+++ bind9-9.9.5.dfsg/bin/dig/include/dig/dig.h
@@ -184,6 +184,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 */
@@ -337,6 +339,10 @@
parse_uint(isc_uint32_t *uip, const char *value, isc_uint32_t max,
const char *desc);
+isc_result_t
+parse_netprefix(struct sockaddr **sa, isc_uint32_t *netmask,
+ const char *value);
+
void
parse_hmac(const char *hmacstr);
only in patch2:
unchanged:
--- bind9-9.9.5.dfsg.orig/lib/dns/message.c
+++ bind9-9.9.5.dfsg/lib/dns/message.c
@@ -3255,6 +3255,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);