Commit 43b9737b authored by Mark Andrews's avatar Mark Andrews
Browse files

3911. [func] Implement EDNS EXPIRE option client side. [RT #35925]

parent a338c2d9
3911. [func] Implement EDNS EXPIRE option client side. [RT #35925]
3910. [bug] Fix races to free event during shutdown. [RT#36720]
3909. [bug] When computing the number of elements required for a
......
......@@ -143,6 +143,7 @@ options {\n\
recursion true;\n\
provide-ixfr true;\n\
request-ixfr true;\n\
request-expire true;\n\
fetch-glue no;\n\
rfc2308-type1 no;\n\
additional-from-auth true;\n\
......
......@@ -1177,6 +1177,11 @@ configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
if (obj != NULL)
CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
obj = NULL;
(void)cfg_map_get(cpeer, "request-expire", &obj);
if (obj != NULL)
CHECK(dns_peer_setrequestexpire(peer, cfg_obj_asboolean(obj)));
obj = NULL;
(void)cfg_map_get(cpeer, "request-ixfr", &obj);
if (obj != NULL)
......
......@@ -1256,6 +1256,11 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS,
ixfrdiff);
obj = NULL;
result = ns_config_get(maps, "request-expire", &obj);
INSIST(result == ISC_R_SUCCESS);
dns_zone_setrequestexpire(zone, cfg_obj_asboolean(obj));
obj = NULL;
result = ns_config_get(maps, "request-ixfr", &obj);
INSIST(result == ISC_R_SUCCESS);
......
......@@ -26,6 +26,7 @@ rm -f dig.out.ns5 dig.out.ns6 dig.out.ns7
rm -f dig.out.soa.ns3
rm -f axfr.out
rm -f ns1/slave.db ns2/slave.db
rm -f ns1/edns-expire.db
rm -f ns2/example.db ns2/tsigzone.db ns2/example.db.jnl
rm -f ns3/example.bk ns3/tsigzone.bk ns3/example.bk.jnl
rm -f ns3/master.bk ns3/master.bk.jnl
......
......@@ -44,3 +44,8 @@ zone "slave" {
type master;
file "slave.db";
};
zone "edns-expire" {
type master;
file "edns-expire.db";
};
......@@ -19,7 +19,7 @@
include "../../common/rndc.key";
controls {
inet 10.53.0.6 port 9953 allow { any; } keys { rndc_key; };
inet 10.53.0.6 port 9953 allow { any; } keys { rndc_key; };
};
options {
......@@ -52,3 +52,9 @@ zone "slave" {
masters { 10.53.0.1; };
file "slave.bk";
};
zone "edns-expire" {
type slave;
masters { 10.53.0.1; };
file "edns-expire.bk";
};
......@@ -51,3 +51,9 @@ zone "slave" {
masters { 10.53.0.1; };
file "slave.bk";
};
zone "edns-expire" {
type slave;
masters { 10.53.0.6; };
file "edns-expire.bk";
};
......@@ -21,6 +21,7 @@ SYSTEMTESTTOP=..
$SHELL clean.sh
$SHELL ../genzone.sh 1 6 7 >ns1/slave.db
$SHELL ../genzone.sh 1 6 7 >ns1/edns-expire.db
$SHELL ../genzone.sh 2 3 >ns2/example.db
$SHELL ../genzone.sh 2 3 >ns2/tsigzone.db
$SHELL ../genzone.sh 6 3 >ns6/master.db
......
......@@ -331,5 +331,17 @@ $DIGCMD nil. TXT | grep 'incorrect key AXFR' >/dev/null && {
status=1
}
echo "I:check that we ask for and get a EDNS EXPIRE response"
# force a refresh query
$RNDC -s 10.53.0.7 -p 9953 -c ../common/rndc.conf refresh edns-expire 2>&1 | sed 's/^/I:ns7 /'
sleep 10
# there may be multiple log entries so get the last one.
expire=`awk '/edns-expire\/IN: got EDNS EXPIRE of/ { x=$9 } END { print x }' ns7/named.run`
test ${expire:-0} -gt 0 -a ${expire:-0} -lt 1814400 || {
echo "I:failed (expire=${expire:-0})"
status=1
}
echo "I:exit status: $status"
exit $status
......@@ -4934,6 +4934,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
<optional> use-ixfr <replaceable>yes_or_no</replaceable> ; </optional>
<optional> provide-ixfr <replaceable>yes_or_no</replaceable>; </optional>
<optional> request-ixfr <replaceable>yes_or_no</replaceable>; </optional>
<optional> request-expire <replaceable>yes_or_no</replaceable>; </optional>
<optional> treat-cr-as-space <replaceable>yes_or_no</replaceable> ; </optional>
<optional> min-refresh-time <replaceable>number</replaceable> ; </optional>
<optional> max-refresh-time <replaceable>number</replaceable> ; </optional>
......@@ -6476,6 +6477,17 @@ options {
</listitem>
</varlistentry>
<varlistentry>
<term><command>request-expire</command></term>
<listitem>
<para>
See the description of
<command>request-expire</command> in
<xref linkend="server_statement_definition_and_usage"/>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>treat-cr-as-space</command></term>
<listitem>
......@@ -10390,6 +10402,7 @@ rate-limit {
<optional> bogus <replaceable>yes_or_no</replaceable> ; </optional>
<optional> provide-ixfr <replaceable>yes_or_no</replaceable> ; </optional>
<optional> request-ixfr <replaceable>yes_or_no</replaceable> ; </optional>
<optional> request-expire <replaceable>yes_or_no</replaceable> ; </optional>
<optional> request-nsid <replaceable>yes_or_no</replaceable> ; </optional>
<optional> request-sit <replaceable>yes_or_no</replaceable> ; </optional>
<optional> edns <replaceable>yes_or_no</replaceable> ; </optional>
......@@ -10494,6 +10507,12 @@ rate-limit {
is buggy and crashes or corrupts data when IXFR is used.
</para>
<para>
The <command>request-expire</command> clause determines
whether the local server, acting as a slave, will request
the EDNS EXPIRE value.
</para>
<para>
The <command>edns</command> clause determines whether
the local server will attempt to use EDNS when communicating
......
......@@ -1513,6 +1513,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
{ "notify-source", MASTERZONE | SLAVEZONE },
{ "notify-source-v6", MASTERZONE | SLAVEZONE },
{ "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
{ "request-expire", SLAVEZONE | REDIRECTZONE },
{ "request-ixfr", SLAVEZONE | REDIRECTZONE },
{ "server-addresses", STATICSTUBZONE },
{ "server-names", STATICSTUBZONE },
......@@ -1534,8 +1535,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
static optionstable dialups[] = {
{ "notify", MASTERZONE | SLAVEZONE | STREDIRECTZONE },
{ "notify-passive", SLAVEZONE | STREDIRECTZONE },
{ "refresh", SLAVEZONE | STUBZONE | STREDIRECTZONE },
{ "passive", SLAVEZONE | STUBZONE | STREDIRECTZONE },
{ "refresh", SLAVEZONE | STUBZONE | STREDIRECTZONE },
};
znamestr = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
......
......@@ -75,6 +75,7 @@ struct dns_peer {
isc_boolean_t support_edns;
isc_boolean_t request_nsid;
isc_boolean_t request_sit;
isc_boolean_t request_expire;
dns_name_t *key;
isc_sockaddr_t *transfer_source;
isc_dscp_t transfer_dscp;
......@@ -166,6 +167,12 @@ dns_peer_setrequestsit(dns_peer_t *peer, isc_boolean_t newval);
isc_result_t
dns_peer_getrequestsit(dns_peer_t *peer, isc_boolean_t *retval);
isc_result_t
dns_peer_setrequestexpire(dns_peer_t *peer, isc_boolean_t newval);
isc_result_t
dns_peer_getrequestexpire(dns_peer_t *peer, isc_boolean_t *retval);
isc_result_t
dns_peer_setsupportedns(dns_peer_t *peer, isc_boolean_t newval);
......
......@@ -2184,6 +2184,26 @@ dns_zone_setrefreshkeyinterval(dns_zone_t *zone, isc_uint32_t interval);
* \li 'zone' to be valid.
*/
isc_boolean_t
dns_zone_getrequestexpire(dns_zone_t *zone);
/*%
* Returns the true/false value of the request-expire option in the zone.
*
* Requires:
* \li 'zone' to be valid.
*/
void
dns_zone_setrequestexpire(dns_zone_t *zone, isc_boolean_t flag);
/*%
* Sets the request-expire option for the zone. Either true or false. The
* default value is determined by the setting of this option in the view.
*
* Requires:
* \li 'zone' to be valid.
*/
isc_boolean_t
dns_zone_getrequestixfr(dns_zone_t *zone);
/*%
......
......@@ -47,6 +47,7 @@
#define NOTIFY_DSCP_BIT 10
#define TRANSFER_DSCP_BIT 11
#define QUERY_DSCP_BIT 12
#define REQUEST_EXPIRE_BIT 13
static void
peerlist_delete(dns_peerlist_t **list);
......@@ -477,6 +478,32 @@ dns_peer_getrequestsit(dns_peer_t *peer, isc_boolean_t *retval) {
return (ISC_R_NOTFOUND);
}
isc_result_t
dns_peer_setrequestexpire(dns_peer_t *peer, isc_boolean_t newval) {
isc_boolean_t existed;
REQUIRE(DNS_PEER_VALID(peer));
existed = DNS_BIT_CHECK(REQUEST_EXPIRE_BIT, &peer->bitflags);
peer->request_expire = newval;
DNS_BIT_SET(REQUEST_EXPIRE_BIT, &peer->bitflags);
return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
}
isc_result_t
dns_peer_getrequestexpire(dns_peer_t *peer, isc_boolean_t *retval) {
REQUIRE(DNS_PEER_VALID(peer));
REQUIRE(retval != NULL);
if (DNS_BIT_CHECK(REQUEST_EXPIRE_BIT, &peer->bitflags)) {
*retval = peer->request_expire;
return (ISC_R_SUCCESS);
} else
return (ISC_R_NOTFOUND);
}
isc_result_t
dns_peer_settransfers(dns_peer_t *peer, isc_uint32_t newval) {
isc_boolean_t existed;
......
......@@ -392,6 +392,11 @@ struct dns_zone {
*/
isc_boolean_t requestixfr;
/*%
* whether EDNS EXPIRE is requested
*/
isc_boolean_t requestexpire;
/*%
* Outstanding forwarded UPDATE requests.
*/
......@@ -999,6 +1004,8 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
zone->secure = NULL;
zone->sourceserial = 0;
zone->sourceserialset = ISC_FALSE;
zone->requestixfr = ISC_TRUE;
zone->requestexpire = ISC_TRUE;
zone->magic = ZONE_MAGIC;
......@@ -10934,6 +10941,90 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
return;
}
/*
* Get the EDNS EXPIRE option from the response and if it exists trim
* expire to be not more than it.
*/
static void
get_edns_expire(dns_zone_t * zone, dns_message_t *message,
isc_uint32_t *expirep)
{
isc_result_t result;
isc_uint32_t expire;
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_buffer_t optbuf;
isc_uint16_t optcode;
isc_uint16_t optlen;
REQUIRE(expirep != NULL);
REQUIRE(message != NULL);
if (message->opt == NULL)
return;
result = dns_rdataset_first(message->opt);
if (result == ISC_R_SUCCESS) {
dns_rdataset_current(message->opt, &rdata);
isc_buffer_init(&optbuf, rdata.data, rdata.length);
isc_buffer_add(&optbuf, rdata.length);
while (isc_buffer_remaininglength(&optbuf) >= 4) {
optcode = isc_buffer_getuint16(&optbuf);
optlen = isc_buffer_getuint16(&optbuf);
/*
* A EDNS EXPIRE response has a length of 4.
*/
if (optcode != DNS_OPT_EXPIRE || optlen != 4) {
isc_buffer_forward(&optbuf, optlen);
continue;
}
expire = isc_buffer_getuint32(&optbuf);
dns_zone_log(zone, ISC_LOG_DEBUG(1),
"got EDNS EXPIRE of %u\n", expire);
/*
* Trim *expirep?
*/
if (expire < *expirep)
*expirep = expire;
break;
}
}
}
/*
* Set the file modification time zone->expire seconds before expiretime.
*/
static void
setmodtime(dns_zone_t *zone, isc_time_t *expiretime) {
isc_result_t result;
isc_time_t when;
isc_interval_t i;
isc_interval_set(&i, zone->expire, 0);
result = isc_time_subtract(expiretime, &i, &when);
if (result != ISC_R_SUCCESS)
return;
result = ISC_R_FAILURE;
if (zone->journal != NULL)
result = isc_file_settime(zone->journal, &when);
if (result == ISC_R_SUCCESS &&
!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP))
result = isc_file_settime(zone->masterfile, &when);
else if (result != ISC_R_SUCCESS)
result = isc_file_settime(zone->masterfile, &when);
/*
* Someone removed the file from underneath us!
*/
if (result == ISC_R_FILENOTFOUND) {
zone_needdump(zone, DNS_DUMP_DELAY);
} else if (result != ISC_R_SUCCESS)
dns_zone_log(zone, ISC_LOG_ERROR, "refresh: could not set "
"file modification time of '%s': %s",
zone->masterfile, dns_result_totext(result));
}
/*
* An SOA query has finished (successfully or not).
*/
......@@ -11054,6 +11145,15 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
goto same_master;
}
if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS) &&
msg->rcode == dns_rcode_badvers) {
dns_zone_log(zone, ISC_LOG_DEBUG(1),
"refresh: rcode (%.*s) retrying without "
"EDNS EXPIRE OPTION master %s (source %s)",
(int)rb.used, rcode, master, source);
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
goto same_master;
}
dns_zone_log(zone, ISC_LOG_INFO,
"refresh: unexpected rcode (%.*s) from "
"master %s (source %s)", (int)rb.used, rcode,
......@@ -11219,30 +11319,26 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
if (msg != NULL)
dns_message_destroy(&msg);
} else if (isc_serial_eq(soa.serial, oldserial)) {
if (zone->masterfile != NULL) {
result = ISC_R_FAILURE;
if (zone->journal != NULL)
result = isc_file_settime(zone->journal, &now);
if (result == ISC_R_SUCCESS &&
!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) {
result = isc_file_settime(zone->masterfile,
&now);
} else if (result != ISC_R_SUCCESS)
result = isc_file_settime(zone->masterfile,
&now);
/* Someone removed the file from underneath us! */
if (result == ISC_R_FILENOTFOUND) {
zone_needdump(zone, DNS_DUMP_DELAY);
} else if (result != ISC_R_SUCCESS)
dns_zone_log(zone, ISC_LOG_ERROR,
"refresh: could not set file "
"modification time of '%s': %s",
zone->masterfile,
dns_result_totext(result));
isc_time_t expiretime;
isc_uint32_t expire;
/*
* Compute the new expire time based on this response.
*/
expire = zone->expire;
get_edns_expire(zone, msg, &expire);
DNS_ZONE_TIME_ADD(&now, expire, &expiretime);
/*
* Has the expire time improved?
*/
if (isc_time_compare(&expiretime, &zone->expiretime) > 0) {
zone->expiretime = expiretime;
if (zone->masterfile != NULL)
setmodtime(zone, &expiretime);
}
DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
DNS_ZONE_TIME_ADD(&now, zone->expire, &zone->expiretime);
zone->mastersok[zone->curmaster] = ISC_TRUE;
goto next_master;
} else {
......@@ -11416,7 +11512,9 @@ create_query(dns_zone_t *zone, dns_rdatatype_t rdtype,
}
static isc_result_t
add_opt(dns_message_t *message, isc_uint16_t udpsize, isc_boolean_t reqnsid) {
add_opt(dns_message_t *message, isc_uint16_t udpsize, isc_boolean_t reqnsid,
isc_boolean_t reqexpire)
{
isc_result_t result;
dns_rdataset_t *rdataset = NULL;
dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
......@@ -11430,6 +11528,13 @@ add_opt(dns_message_t *message, isc_uint16_t udpsize, isc_boolean_t reqnsid) {
ednsopts[count].value = NULL;
count++;
}
if (reqexpire) {
INSIST(count < DNS_EDNSOPTIONS);
ednsopts[count].code = DNS_OPT_EXPIRE;
ednsopts[count].length = 0;
ednsopts[count].value = NULL;
count++;
}
result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0,
ednsopts, count);
if (result != ISC_R_SUCCESS)
......@@ -11450,7 +11555,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
isc_uint32_t options;
isc_boolean_t cancel = ISC_TRUE;
int timeout;
isc_boolean_t have_xfrsource, have_xfrdscp, reqnsid;
isc_boolean_t have_xfrsource, have_xfrdscp, reqnsid, reqexpire;
isc_uint16_t udpsize = SEND_BUFFER_SIZE;
isc_dscp_t dscp = -1;
......@@ -11513,6 +11618,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
have_xfrsource = have_xfrdscp = ISC_FALSE;
reqnsid = zone->view->requestnsid;
reqexpire = zone->requestexpire;
if (zone->view->peers != NULL) {
dns_peer_t *peer = NULL;
isc_boolean_t edns;
......@@ -11534,6 +11640,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
dns_resolver_getudpsize(zone->view->resolver);
(void)dns_peer_getudpsize(peer, &udpsize);
(void)dns_peer_getrequestnsid(peer, &reqnsid);
(void)dns_peer_getrequestexpire(peer, &reqexpire);
}
}
......@@ -11575,7 +11682,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
DNS_REQUESTOPT_TCP : 0;
if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
result = add_opt(message, udpsize, reqnsid);
result = add_opt(message, udpsize, reqnsid, reqexpire);
if (result != ISC_R_SUCCESS)
zone_debuglog(zone, me, 1,
"unable to add opt record: %s",
......@@ -11790,7 +11897,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
}
if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
result = add_opt(message, udpsize, reqnsid);
result = add_opt(message, udpsize, reqnsid, ISC_FALSE);
if (result != ISC_R_SUCCESS)
zone_debuglog(zone, me, 1,
"unable to add opt record: %s",
......@@ -17308,6 +17415,18 @@ dns_zone_getrequestixfr(dns_zone_t *zone) {
return (zone->requestixfr);
}
void
dns_zone_setrequestexpire(dns_zone_t *zone, isc_boolean_t flag) {
REQUIRE(DNS_ZONE_VALID(zone));
zone->requestexpire = flag;
}
isc_boolean_t
dns_zone_getrequestexpire(dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));
return (zone->requestexpire);
}
void
dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method) {
REQUIRE(DNS_ZONE_VALID(zone));
......
......@@ -1675,6 +1675,7 @@ zone_clauses[] = {
{ "notify-to-soa", &cfg_type_boolean, 0 },
{ "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY },
{ "serial-update-method", &cfg_type_updatemethod, 0 },
{ "request-expire", &cfg_type_boolean, 0 },
{ "request-ixfr", &cfg_type_boolean, 0 },
{ "sig-signing-nodes", &cfg_type_uint32, 0 },
{ "sig-signing-signatures", &cfg_type_uint32, 0 },
......@@ -1851,6 +1852,7 @@ server_clauses[] = {
{ "provide-ixfr", &cfg_type_boolean, 0 },
{ "query-source", &cfg_type_querysource4, 0 },
{ "query-source-v6", &cfg_type_querysource6, 0 },
{ "request-expire", &cfg_type_boolean, 0 },
{ "request-ixfr", &cfg_type_boolean, 0 },
{ "request-nsid", &cfg_type_boolean, 0 },
#ifdef ISC_PLATFORM_USESIT
......
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