From: Mark Andrews Date: Thu, 20 Feb 2014 03:56:20 +0000 (+1100) Subject: 3750. [experimental] Partially implement EDNS EXPIRE option as described X-Git-Tag: v9.10.0b1~57 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=16134801ce8fffbb6c42bb54d544c3397a45ad06;p=thirdparty%2Fbind9.git 3750. [experimental] Partially implement EDNS EXPIRE option as described in draft-andrews-dnsext-expire-00. Retrivial of remaining time to expiry from slave zones is supported. EXPIRE uses an experimental option code (65002) and is subject to change. [RT #35416] --- diff --git a/CHANGES b/CHANGES index 2b63f30d298..b4900063e9f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +3750. [experimental] Partially implement EDNS EXPIRE option as described + in draft-andrews-dnsext-expire-00. Retrivial of + remaining time to expiry from slave zones is supported. + + EXPIRE uses an experimental option code (65002) and + is subject to change. [RT #35416] + 3749. [func] "dig +subnet" sends an EDNS client subnet option containing the specified address/prefix when querying. (Thanks to Wilmer van der Gaast.) diff --git a/bin/dig/dig.c b/bin/dig/dig.c index dd1946fc901..007bcdd7678 100644 --- a/bin/dig/dig.c +++ b/bin/dig/dig.c @@ -226,6 +226,7 @@ help(void) { " +[no]identify (ID responders in short answers)\n" " +[no]trace (Trace delegation down from root [+dnssec])\n" " +[no]dnssec (Request DNSSEC records)\n" +" +[no]expire (Request time to expire)\n" " +[no]nsid (Request Name Server ID)\n" #ifdef ISC_PLATFORM_USESIT " +[no]sit (Request a Source Identity Token)\n" @@ -919,19 +920,29 @@ plus_option(char *option, isc_boolean_t is_batchfile, } break; case 'e': - FULLCHECK("edns"); - if (!state) { - lookup->edns = -1; + switch (cmd[1]) { + case 'd': + FULLCHECK("edns"); + if (!state) { + lookup->edns = -1; + break; + } + if (value == NULL) { + lookup->edns = 0; + break; + } + result = parse_uint(&num, value, 255, "edns"); + if (result != ISC_R_SUCCESS) + fatal("Couldn't parse edns"); + lookup->edns = num; break; - } - if (value == NULL) { - lookup->edns = 0; + case 'x': + FULLCHECK("expire"); + lookup->expire = state; break; + default: + goto invalid_option; } - result = parse_uint(&num, value, 255, "edns"); - if (result != ISC_R_SUCCESS) - fatal("Couldn't parse edns"); - lookup->edns = num; break; case 'f': /* fail */ FULLCHECK("fail"); diff --git a/bin/dig/dig.docbook b/bin/dig/dig.docbook index 9ab93d64c3b..5836e7dca7a 100644 --- a/bin/dig/dig.docbook +++ b/bin/dig/dig.docbook @@ -931,7 +931,8 @@ Send a Source Identity Token EDNS option, with optional value. Replaying a SIT from a previous response will allow the server to identify a previous client. The default is - . + . Currently using experimental value + 65001 for the option code. @@ -946,6 +947,16 @@ + + + + + Send an EDNS Expire option. Currently using experimental + value 65002 for the option code. + + + + diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index e6bb08df319..337bc2936a7 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -767,6 +767,7 @@ make_empty_lookup(void) { looknew->servfail_stops = ISC_TRUE; looknew->besteffort = ISC_TRUE; looknew->dnssec = ISC_FALSE; + looknew->expire = ISC_FALSE; looknew->nsid = ISC_FALSE; #ifdef ISC_PLATFORM_USESIT looknew->sit = ISC_FALSE; @@ -855,6 +856,7 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) { looknew->servfail_stops = lookold->servfail_stops; looknew->besteffort = lookold->besteffort; looknew->dnssec = lookold->dnssec; + looknew->expire = lookold->expire; looknew->nsid = lookold->nsid; #ifdef ISC_PLATFORM_USESIT looknew->sit = lookold->sit; @@ -2428,6 +2430,14 @@ setup_lookup(dig_lookup_t *lookup) { } #endif + if (lookup->expire) { + INSIST(i < DNS_EDNSOPTIONS); + opts[i].code = DNS_OPT_EXPIRE; + opts[i].length = 0; + opts[i].value = NULL; + i++; + } + add_opt(lookup->sendmsg, lookup->udpsize, lookup->edns, lookup->dnssec, opts, i); } diff --git a/bin/dig/include/dig/dig.h b/bin/dig/include/dig/dig.h index ad4c7c2ab21..205e6cd2aeb 100644 --- a/bin/dig/include/dig/dig.h +++ b/bin/dig/include/dig/dig.h @@ -130,6 +130,7 @@ struct dig_lookup { done_as_is, besteffort, dnssec, + expire, #ifdef ISC_PLATFORM_USESIT sit, #endif diff --git a/bin/named/client.c b/bin/named/client.c index 52d813bca8d..6e091c9b3c5 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -249,6 +249,7 @@ allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl); static void compute_sit(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce, isc_buffer_t *buf); #endif +static inline isc_result_t client_addopt(ns_client_t *client); void ns_client_recursing(ns_client_t *client) { @@ -1013,6 +1014,15 @@ client_send(ns_client_t *client) { } #endif + /* + * Create an OPT for our reply. + */ + if ((client->attributes & NS_CLIENTATTR_WANTOPT) != 0) { + result = client_addopt(client); + if (result != ISC_R_SUCCESS) + goto done; + } + /* * XXXRTH The following doesn't deal with TCP buffer resizing. */ @@ -1380,6 +1390,7 @@ client_addopt(ns_client_t *client) { dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; int count = 0; unsigned int flags; + unsigned char expire[4]; REQUIRE(client->opt == NULL); /* XXXRTH free old. */ @@ -1432,6 +1443,18 @@ client_addopt(ns_client_t *client) { count++; } #endif + if ((client->attributes & NS_CLIENTATTR_HAVEEXPIRE) != 0) { + isc_buffer_t buf; + + INSIST(count < DNS_EDNSOPTIONS); + + isc_buffer_init(&buf, expire, sizeof(expire)); + isc_buffer_putuint32(&buf, client->expire); + ednsopts[count].code = DNS_OPT_EXPIRE; + ednsopts[count].length = 4; + ednsopts[count].value = expire; + count++; + } result = dns_message_buildopt(client->message, &client->opt, 0, udpsize, flags, ednsopts, count); @@ -1752,6 +1775,10 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) { process_sit(client, &optbuf, optlen); break; #endif + case DNS_OPT_EXPIRE: + client->attributes |= NS_CLIENTATTR_WANTEXPIRE; + isc_buffer_forward(&optbuf, optlen); + break; default: isc_buffer_forward(&optbuf, optlen); break; @@ -1760,15 +1787,8 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) { } isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_edns0in); + client->attributes |= NS_CLIENTATTR_WANTOPT; - /* - * Create an OPT for our reply. - */ - result = client_addopt(client); - if (result != ISC_R_SUCCESS) { - ns_client_error(client, result); - goto cleanup; - } cleanup: return (result); } diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h index 5707d8ec5be..c526fe0a8eb 100644 --- a/bin/named/include/named/client.h +++ b/bin/named/include/named/client.h @@ -162,6 +162,7 @@ struct ns_client { ISC_LINK(ns_client_t) rlink; ISC_QLINK(ns_client_t) ilink; unsigned char cookie[8]; + isc_uint32_t expire; }; typedef ISC_QUEUE(ns_client_t) client_queue_t; @@ -170,19 +171,22 @@ typedef ISC_LIST(ns_client_t) client_list_t; #define NS_CLIENT_MAGIC ISC_MAGIC('N','S','C','c') #define NS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, NS_CLIENT_MAGIC) -#define NS_CLIENTATTR_TCP 0x001 -#define NS_CLIENTATTR_RA 0x002 /*%< Client gets recursive service */ -#define NS_CLIENTATTR_PKTINFO 0x004 /*%< pktinfo is valid */ -#define NS_CLIENTATTR_MULTICAST 0x008 /*%< recv'd from multicast */ -#define NS_CLIENTATTR_WANTDNSSEC 0x010 /*%< include dnssec records */ -#define NS_CLIENTATTR_WANTNSID 0x020 /*%< include nameserver ID */ +#define NS_CLIENTATTR_TCP 0x0001 +#define NS_CLIENTATTR_RA 0x0002 /*%< Client gets recursive service */ +#define NS_CLIENTATTR_PKTINFO 0x0004 /*%< pktinfo is valid */ +#define NS_CLIENTATTR_MULTICAST 0x0008 /*%< recv'd from multicast */ +#define NS_CLIENTATTR_WANTDNSSEC 0x0010 /*%< include dnssec records */ +#define NS_CLIENTATTR_WANTNSID 0x0020 /*%< include nameserver ID */ #ifdef ALLOW_FILTER_AAAA -#define NS_CLIENTATTR_FILTER_AAAA 0x040 /*%< suppress AAAAs */ -#define NS_CLIENTATTR_FILTER_AAAA_RC 0x080 /*%< recursing for A against AAAA */ +#define NS_CLIENTATTR_FILTER_AAAA 0x0040 /*%< suppress AAAAs */ +#define NS_CLIENTATTR_FILTER_AAAA_RC 0x0080 /*%< recursing for A against AAAA */ #endif -#define NS_CLIENTATTR_WANTAD 0x100 /*%< want AD in response if possible */ -#define NS_CLIENTATTR_WANTSIT 0x200 /*%< include SIT */ -#define NS_CLIENTATTR_HAVESIT 0x400 /*%< has a valid SIT */ +#define NS_CLIENTATTR_WANTAD 0x0100 /*%< want AD in response if possible */ +#define NS_CLIENTATTR_WANTSIT 0x0200 /*%< include SIT */ +#define NS_CLIENTATTR_HAVESIT 0x0400 /*%< has a valid SIT */ +#define NS_CLIENTATTR_WANTEXPIRE 0x0800 /*%< return seconds to expire */ +#define NS_CLIENTATTR_HAVEEXPIRE 0x1000 /*%< return seconds to expire */ +#define NS_CLIENTATTR_WANTOPT 0x2000 /*%< add opt to reply */ extern unsigned int ns_client_requests; diff --git a/bin/named/query.c b/bin/named/query.c index 7b3a825f795..08c5675cdba 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -7864,6 +7864,20 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) dns_name_equal(client->query.qname, dns_rootname)) client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; + if (is_zone && qtype == dns_rdatatype_soa && + (client->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0 && + client->query.restarts == 0 && + dns_zone_gettype(zone) == dns_zone_slave) { + isc_time_t expiretime; + isc_uint32_t secs; + dns_zone_getexpiretime(zone, &expiretime); + secs = isc_time_seconds(&expiretime); + if (secs >= client->now && result == ISC_R_SUCCESS) { + client->attributes |= NS_CLIENTATTR_HAVEEXPIRE; + client->expire = secs - client->now; + } + } + if (dns64) { qtype = type = dns_rdatatype_aaaa; result = query_dns64(client, &fname, rdataset, diff --git a/bin/tests/system/cacheclean/clean.sh b/bin/tests/system/cacheclean/clean.sh index 7e0b949903b..5640af2b571 100644 --- a/bin/tests/system/cacheclean/clean.sh +++ b/bin/tests/system/cacheclean/clean.sh @@ -22,5 +22,6 @@ # rm -f dig.out.ns2 +rm -f dig.out.expire rm -f */named.memstats rm -f ns2/named_dump.db diff --git a/bin/tests/system/cacheclean/ns1/expire-test.db b/bin/tests/system/cacheclean/ns1/expire-test.db new file mode 100644 index 00000000000..d86da4cd509 --- /dev/null +++ b/bin/tests/system/cacheclean/ns1/expire-test.db @@ -0,0 +1,24 @@ +; Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 3600 +@ IN SOA hostmaster.ns ns ( + 2011072900 + 600 + 600 + 1200 + 3600 + ) + NS ns +ns IN A 10.53.0.1 diff --git a/bin/tests/system/cacheclean/ns1/named.conf b/bin/tests/system/cacheclean/ns1/named.conf index 708de1a3ab1..616875f1db7 100644 --- a/bin/tests/system/cacheclean/ns1/named.conf +++ b/bin/tests/system/cacheclean/ns1/named.conf @@ -41,3 +41,8 @@ zone "flushtest.example" { type master; file "flushtest.db"; }; + +zone "expire-test" { + type master; + file "expire-test.db"; +}; diff --git a/bin/tests/system/cacheclean/ns2/named.conf b/bin/tests/system/cacheclean/ns2/named.conf index b0e07041002..376dd8bc1ee 100644 --- a/bin/tests/system/cacheclean/ns2/named.conf +++ b/bin/tests/system/cacheclean/ns2/named.conf @@ -50,3 +50,8 @@ zone "flushtest.example" { type forward; forwarders { 10.53.0.1; }; }; + +zone "expire-test" { + type slave; + masters { 10.53.0.1; }; +}; diff --git a/bin/tests/system/cacheclean/tests.sh b/bin/tests/system/cacheclean/tests.sh index 5436852850c..43ca679de18 100644 --- a/bin/tests/system/cacheclean/tests.sh +++ b/bin/tests/system/cacheclean/tests.sh @@ -201,5 +201,12 @@ awk '/plain success\/timeout/ {getline; getline; if ($2 == "ns.flushtest.example if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check expire option returned from slave zone" +ret=0 +$DIG @10.53.0.2 -p 5300 +expire soa expire-test > dig.out.expire +grep EXPIRE: dig.out.expire > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:exit status: $status" exit $status diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index a6cc1b4241d..88fb31ae683 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -108,9 +108,10 @@ #define DNS_OPT_CLIENT_SUBNET 0x0008 /*%< client subnet opt code */ /*%< Experimental options [65001...65534] as per RFC6891 */ #define DNS_OPT_SIT 65001 /*%< SIT opt code */ +#define DNS_OPT_EXPIRE 65002 /*%< EXPIRE opt code */ /*%< The number of EDNS options we know about. */ -#define DNS_EDNSOPTIONS 3 +#define DNS_EDNSOPTIONS 4 #define DNS_MESSAGE_REPLYPRESERVE (DNS_MESSAGEFLAG_RD|DNS_MESSAGEFLAG_CD) #define DNS_MESSAGEEXTFLAG_REPLYPRESERVE (DNS_MESSAGEEXTFLAG_DO) diff --git a/lib/dns/message.c b/lib/dns/message.c index 2b40cc64de1..c77c891b402 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3299,6 +3299,18 @@ dns_message_pseudosectiontotext(dns_message_t *msg, render_ecs(&optbuf, target); ADD_STRING(target, "\n"); break; + } else if (optcode == DNS_OPT_EXPIRE) { + if (optlen == 4) { + char buf[sizeof("4294967296")]; + isc_uint32_t secs; + secs = isc_buffer_getuint32(&optbuf); + ADD_STRING(target, "; EXPIRE: "); + snprintf(buf, sizeof(buf), "%u", secs); + ADD_STRING(target, buf); + ADD_STRING(target, "\n"); + break; + } + ADD_STRING(target, "; EXPIRE"); } else { ADD_STRING(target, "; OPT="); sprintf(buf, "%u", optcode);