diff --git a/lib/dns/clientinfo.c b/lib/dns/clientinfo.c index 372aab1061..fe81d591c6 100644 --- a/lib/dns/clientinfo.c +++ b/lib/dns/clientinfo.c @@ -14,6 +14,7 @@ /*! \file */ #include +#include void dns_clientinfomethods_init(dns_clientinfomethods_t *methods, @@ -24,8 +25,14 @@ dns_clientinfomethods_init(dns_clientinfomethods_t *methods, } void -dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp) { +dns_clientinfo_init(dns_clientinfo_t *ci, void *data, dns_ecs_t *ecs, + void *versionp) { ci->version = DNS_CLIENTINFO_VERSION; ci->data = data; ci->dbversion = versionp; + if (ecs != NULL) { + ci->ecs = *ecs; + } else { + dns_ecs_init(&ci->ecs); + } } diff --git a/lib/dns/ecs.c b/lib/dns/ecs.c index 2171a89dd5..ae88c4713e 100644 --- a/lib/dns/ecs.c +++ b/lib/dns/ecs.c @@ -15,36 +15,99 @@ #include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include #include void dns_ecs_init(dns_ecs_t *ecs) { isc_netaddr_unspec(&ecs->addr); ecs->source = 0; + ecs->scope = 0xff; +} + +bool +dns_ecs_equals(const dns_ecs_t *ecs1, const dns_ecs_t *ecs2) { + const unsigned char *addr1, *addr2; + uint8_t mask; + size_t alen; + + REQUIRE(ecs1 != NULL && ecs2 != NULL); + + if (ecs1->source != ecs2->source || + ecs1->addr.family != ecs2->addr.family) { + return (false); + } + + alen = (ecs1->source + 7) / 8; + if (alen == 0) { + return (true); + } + + switch (ecs1->addr.family) { + case AF_INET: + INSIST(alen <= 4); + addr1 = (const unsigned char *)&ecs1->addr.type.in; + addr2 = (const unsigned char *)&ecs2->addr.type.in; + break; + case AF_INET6: + INSIST(alen <= 16); + addr1 = (const unsigned char *)&ecs1->addr.type.in6; + addr2 = (const unsigned char *)&ecs2->addr.type.in6; + break; + default: + INSIST(0); + ISC_UNREACHABLE(); + } + /* - * XXXMUKS: Fix me when resolver ECS gets merged where scope - * gets initialized to 0xff. + * Compare all octets except the final octet of the address + * prefix. */ - ecs->scope = 0; + if (alen > 1 && memcmp(addr1, addr2, alen - 1) != 0) { + return (false); + } + + /* + * It should not be necessary to mask the final octet; all + * bits past the source prefix length are supposed to be 0. + * However, it seems prudent not to omit them from the + * comparison anyway. + */ + mask = (~0U << (8 - (ecs1->source % 8))) & 0xff; + if (mask == 0) { + mask = 0xff; + } + + if ((addr1[alen - 1] & mask) != (addr2[alen - 1] & mask)) { + return (false); + } + + return (true); } void -dns_ecs_format(dns_ecs_t *ecs, char *buf, size_t size) { +dns_ecs_format(const dns_ecs_t *ecs, char *buf, size_t size) { size_t len; + char *p; REQUIRE(ecs != NULL); REQUIRE(buf != NULL); REQUIRE(size >= DNS_ECS_FORMATSIZE); - isc_netaddr_format(&ecs->addr, buf, (unsigned int)size); + isc_netaddr_format(&ecs->addr, buf, size); len = strlen(buf); - INSIST(size >= len); - buf += len; - size -= len; - snprintf(buf, size, "/%u/%u", ecs->source, ecs->scope); + p = buf + len; + snprintf(p, size - len, "/%d/%d", ecs->source, + ecs->scope == 0xff ? 0 : ecs->scope); } diff --git a/lib/dns/include/dns/clientinfo.h b/lib/dns/include/dns/clientinfo.h index 03891c7f60..1196503838 100644 --- a/lib/dns/include/dns/clientinfo.h +++ b/lib/dns/include/dns/clientinfo.h @@ -42,17 +42,20 @@ #include #include +#include + ISC_LANG_BEGINDECLS /***** ***** Types *****/ -#define DNS_CLIENTINFO_VERSION 2 +#define DNS_CLIENTINFO_VERSION 3 typedef struct dns_clientinfo { - uint16_t version; - void *data; - void *dbversion; + uint16_t version; + void *data; + void *dbversion; + dns_ecs_t ecs; } dns_clientinfo_t; typedef isc_result_t (*dns_clientinfo_sourceip_t)(dns_clientinfo_t *client, @@ -75,6 +78,7 @@ dns_clientinfomethods_init(dns_clientinfomethods_t *methods, dns_clientinfo_sourceip_t sourceip); void -dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp); +dns_clientinfo_init(dns_clientinfo_t *ci, void *data, dns_ecs_t *ecs, + void *versionp); ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/ecs.h b/lib/dns/include/dns/ecs.h index 2644a1ac74..d4cfe65233 100644 --- a/lib/dns/include/dns/ecs.h +++ b/lib/dns/include/dns/ecs.h @@ -19,17 +19,28 @@ #include #include +#include #include +/*% + * Maximum scope values for IPv4 and IPv6. + */ +#ifndef ECS_MAX_V4_SCOPE +#define ECS_MAX_V4_SCOPE 24 +#endif + +#ifndef ECS_MAX_V6_SCOPE +#define ECS_MAX_V6_SCOPE 56 +#endif + struct dns_ecs { isc_netaddr_t addr; uint8_t source; uint8_t scope; }; -#define DNS_ECS_FORMATSIZE \ - (ISC_NETADDR_FORMATSIZE + 8) /*
/NNN/NNN \ - */ +/*
/NNN/NNN */ +#define DNS_ECS_FORMATSIZE (ISC_NETADDR_FORMATSIZE + 9) ISC_LANG_BEGINDECLS @@ -42,8 +53,19 @@ dns_ecs_init(dns_ecs_t *ecs); * \li 'ecs' is not NULL and points to a valid dns_ecs structure. */ +bool +dns_ecs_equals(const dns_ecs_t *ecs1, const dns_ecs_t *ecs2); +/*%< + * Determine whether two ECS address prefixes are equal (except the + * scope prefix-length field). + * + * 'ecs1->source' must exactly match 'ecs2->source'; the address families + * must match; and the first 'ecs1->source' bits of the addresses must + * match. Subsequent address bits and the 'scope' values are ignored. + */ + void -dns_ecs_format(dns_ecs_t *ecs, char *buf, size_t size); +dns_ecs_format(const dns_ecs_t *ecs, char *buf, size_t size); /*%< * Format an ECS record as text. Result is guaranteed to be null-terminated. * @@ -52,5 +74,4 @@ dns_ecs_format(dns_ecs_t *ecs, char *buf, size_t size); * \li 'buf' is not NULL. * \li 'size' is at least DNS_ECS_FORMATSIZE */ - ISC_LANG_ENDDECLS diff --git a/lib/ns/query.c b/lib/ns/query.c index 8fbbc09926..d87d1f8ec9 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -1389,7 +1389,7 @@ query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, dns_db_t *tdbp; dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, &client->ecs, NULL); tdbp = NULL; tresult = dns_view_searchdlz(client->view, name, zonelabels, @@ -1530,7 +1530,7 @@ query_additionalauthfind(dns_db_t *db, dns_dbversion_t *version, isc_result_t result; dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * Since we are looking for authoritative data, we do not set @@ -1693,7 +1693,7 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype, CTRACE(ISC_LOG_DEBUG(3), "query_additional_cb"); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * We treat type A additional section processing as if it @@ -2285,7 +2285,7 @@ mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name, rdataset->trust = dns_trust_secure; sigrdataset->trust = dns_trust_secure; dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * Save the updated secure state. Ignore failures. @@ -2322,7 +2322,7 @@ get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig, dns_clientinfo_t ci; dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); if (!dns_rdataset_isassociated(keyrdataset)) { result = dns_db_findnodeext(db, &rrsig->signer, false, &cm, &ci, @@ -2876,7 +2876,7 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, node = NULL; found = dns_fixedname_initname(&fixed); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK, client->now, &node, found, &cm, &ci, *rdatasetp, NULL); @@ -3025,7 +3025,7 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype, CTRACE(ISC_LOG_DEBUG(3), "rpz_find_p"); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * Try to find either a CNAME or the type of record demanded by the @@ -4628,7 +4628,7 @@ query_findclosestnsec3(dns_name_t *qname, dns_db_t *db, dns_name_clone(qname, &name); labels = dns_name_countlabels(&name); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * Map unknown algorithm to known value. @@ -4828,7 +4828,7 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdataset_init(&trdataset); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, &client->ecs, NULL); if (WANTDNSSEC(client) && dns_db_iszone(*dbp) && dns_db_issecure(*dbp)) { @@ -4966,7 +4966,7 @@ redirect2(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdataset_init(&trdataset); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, &client->ecs, NULL); if (WANTDNSSEC(client) && dns_db_iszone(*dbp) && dns_db_issecure(*dbp)) { @@ -5787,7 +5787,9 @@ query_lookup(query_ctx_t *qctx) { CALL_HOOK(NS_QUERY_LOOKUP_BEGIN, qctx); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, qctx->client, NULL); + dns_clientinfo_init(&ci, qctx->client, + HAVEECS(qctx->client) ? &qctx->client->ecs : NULL, + NULL); /* * We'll need some resources... @@ -8596,7 +8598,7 @@ query_notfound(query_ctx_t *qctx) { dns_clientinfo_t ci; dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, qctx->client, NULL); + dns_clientinfo_init(&ci, qctx->client, NULL, NULL); dns_db_attach(qctx->view->hints, &qctx->db); result = dns_db_findext(qctx->db, dns_rootname, NULL, @@ -10065,7 +10067,7 @@ query_coveringnsec(query_ctx_t *qctx) { nowild = dns_fixedname_initname(&fnowild); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, qctx->client, NULL); + dns_clientinfo_init(&ci, qctx->client, NULL, NULL); /* * All signer names must be the same to accept. @@ -10788,7 +10790,7 @@ query_addsoa(query_ctx_t *qctx, unsigned int override_ttl, node = NULL; dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * Don't add the SOA record for test which set "-T nosoa". @@ -10937,7 +10939,7 @@ query_addns(query_ctx_t *qctx) { fname = dns_fixedname_initname(&foundname); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * Get resources and make 'name' be the database origin. @@ -11039,7 +11041,7 @@ query_addbestns(query_ctx_t *qctx) { CTRACE(ISC_LOG_DEBUG(3), "query_addbestns"); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * Find the right database. @@ -11244,7 +11246,7 @@ query_addwildcardproof(query_ctx_t *qctx, bool ispositive, bool nodata) { CTRACE(ISC_LOG_DEBUG(3), "query_addwildcardproof"); dns_clientinfomethods_init(&cm, ns_client_sourceip); - dns_clientinfo_init(&ci, client, NULL); + dns_clientinfo_init(&ci, client, NULL, NULL); /* * If a name has been specifically flagged as needing diff --git a/lib/ns/update.c b/lib/ns/update.c index b3b53e507a..47f9fcafeb 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -594,7 +594,7 @@ foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, * different from the current version */ dns_db_currentversion(db, &oldver); - dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL); + dns_clientinfo_init(&ci, NULL, NULL, (ver != oldver) ? ver : NULL); dns_db_closeversion(db, &oldver, false); node = NULL; @@ -685,7 +685,7 @@ foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, * different from the current version */ dns_db_currentversion(db, &oldver); - dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL); + dns_clientinfo_init(&ci, NULL, NULL, (ver != oldver) ? ver : NULL); dns_db_closeversion(db, &oldver, false); if (type == dns_rdatatype_any) {