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 --- 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 +#include #include #include #include @@ -91,6 +92,9 @@ #include +/* parse_netprefix */ +#include + #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)