diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index 43528dea82..864306b783 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -199,6 +199,11 @@ struct dns_sortlist_arg { const dns_aclelement_t *element; }; +typedef struct dns_minttl { + bool is_set; + dns_ttl_t ttl; +} dns_minttl_t; + struct dns_message { /* public from here down */ unsigned int magic; @@ -279,6 +284,8 @@ struct dns_message { dns_sortlist_arg_t order_arg; dns_indent_t indent; + + dns_minttl_t minttl[DNS_SECTION_MAX]; }; struct dns_ednsopt { @@ -1483,4 +1490,28 @@ dns_message_clonebuffer(dns_message_t *msg); * \li msg be a valid message. */ +isc_result_t +dns_message_minttl(dns_message_t *msg, const dns_section_t sectionid, + dns_ttl_t *pttl); +/*%< + * Get the smallest TTL from the 'sectionid' section of a rendered + * message. + * + * Requires: + * \li msg be a valid rendered message; + * \li 'pttl != NULL'. + */ + +isc_result_t +dns_message_response_minttl(dns_message_t *msg, dns_ttl_t *pttl); +/*%< + * Get the smalled TTL from the Answer section of 'msg', or if empty, try + * the MIN(SOA TTL, SOA MINIMUM) value from an SOA record in the Authority + * section. If neither of these are set, return ISC_R_NOTFOUND. + * + * Requires: + * \li msg be a valid rendered message; + * \li 'pttl != NULL'. + */ + ISC_LANG_ENDDECLS diff --git a/lib/dns/message.c b/lib/dns/message.c index 3ecf79b273..afc1441948 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1913,6 +1914,18 @@ maybe_clear_ad(dns_message_t *msg, dns_section_t sectionid) { } } +static void +update_min_section_ttl(dns_message_t *restrict msg, + const dns_section_t sectionid, + dns_rdataset_t *restrict rdataset) { + if (!msg->minttl[sectionid].is_set || + rdataset->ttl < msg->minttl[sectionid].ttl) + { + msg->minttl[sectionid].is_set = true; + msg->minttl[sectionid].ttl = rdataset->ttl; + } +} + isc_result_t dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid, unsigned int options) { @@ -2012,6 +2025,9 @@ dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid, msg->counts[sectionid] += total; return (result); } + + update_min_section_ttl(msg, sectionid, rdataset); + rdataset->attributes |= DNS_RDATASETATTR_RENDERED; } } @@ -2107,6 +2123,9 @@ dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid, msg->flags &= ~DNS_MESSAGEFLAG_AD; } + update_min_section_ttl(msg, sectionid, + rdataset); + rdataset->attributes |= DNS_RDATASETATTR_RENDERED; @@ -4692,3 +4711,108 @@ dns_message_clonebuffer(dns_message_t *msg) { msg->free_query = 1; } } + +static isc_result_t +message_authority_soa_min(dns_message_t *msg, dns_ttl_t *pttl) { + isc_result_t result; + dns_rdataset_t *rdataset = NULL; + dns_name_t *name = NULL; + + if (msg->counts[DNS_SECTION_AUTHORITY] == 0) { + return (ISC_R_NOTFOUND); + } + + for (result = dns_message_firstname(msg, DNS_SECTION_AUTHORITY); + result == ISC_R_SUCCESS; + result = dns_message_nextname(msg, DNS_SECTION_AUTHORITY)) + { + name = NULL; + dns_message_currentname(msg, DNS_SECTION_AUTHORITY, &name); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + isc_result_t tresult; + + if ((rdataset->attributes & + DNS_RDATASETATTR_RENDERED) == 0) { + continue; + } + + /* loop over the rdatas */ + for (tresult = dns_rdataset_first(rdataset); + tresult == ISC_R_SUCCESS; + tresult = dns_rdataset_next(rdataset)) + { + dns_name_t tmp; + isc_region_t r = { 0 }; + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(rdataset, &rdata); + + switch (rdata.type) { + case dns_rdatatype_soa: + /* SOA rdataset */ + break; + case dns_rdatatype_none: + /* + * Negative cache rdataset: we need + * to inspect the rdata to determine + * whether it's an SOA. + */ + dns_rdata_toregion(&rdata, &r); + dns_name_init(&tmp, NULL); + dns_name_fromregion(&tmp, &r); + isc_region_consume(&r, tmp.length); + if (r.length < 2) { + continue; + } + rdata.type = r.base[0] << 8 | r.base[1]; + if (rdata.type != dns_rdatatype_soa) { + continue; + } + break; + default: + continue; + } + + if (rdata.type == dns_rdatatype_soa) { + *pttl = ISC_MIN( + rdataset->ttl, + dns_soa_getminimum(&rdata)); + return (ISC_R_SUCCESS); + } + } + } + } + + return (ISC_R_NOTFOUND); +} + +isc_result_t +dns_message_minttl(dns_message_t *msg, const dns_section_t sectionid, + dns_ttl_t *pttl) { + REQUIRE(DNS_MESSAGE_VALID(msg)); + REQUIRE(pttl != NULL); + + if (!msg->minttl[sectionid].is_set) { + return (ISC_R_NOTFOUND); + } + + *pttl = msg->minttl[sectionid].ttl; + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_message_response_minttl(dns_message_t *msg, dns_ttl_t *pttl) { + isc_result_t result; + + REQUIRE(DNS_MESSAGE_VALID(msg)); + REQUIRE(pttl != NULL); + + result = dns_message_minttl(msg, DNS_SECTION_ANSWER, pttl); + if (result != ISC_R_SUCCESS) { + return (message_authority_soa_min(msg, pttl)); + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/ns/client.c b/lib/ns/client.c index 83b5369530..233a9dfade 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -324,12 +324,21 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer, static void client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) { + isc_result_t result; isc_region_t r; + dns_ttl_t min_ttl = 0; REQUIRE(client->sendhandle == NULL); isc_buffer_usedregion(buffer, &r); isc_nmhandle_attach(client->handle, &client->sendhandle); + + if (isc_nm_is_http_handle(client->handle)) { + result = dns_message_response_minttl(client->message, &min_ttl); + if (result == ISC_R_SUCCESS) { + isc_nm_set_maxage(client->handle, min_ttl); + } + } isc_nm_send(client->handle, &r, client_senddone, client); }