Commit d7b9756a authored by Evan Hunt's avatar Evan Hunt

[master] ENDS client-subnet in dig

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]
parent 1361e038
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
......
......@@ -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.
......
......@@ -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;
}
......
......@@ -936,6 +936,16 @@
</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>
......
......@@ -78,14 +78,13 @@
#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;
......
......@@ -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);
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment