diff options
Diffstat (limited to 'lib/dns')
37 files changed, 20499 insertions, 18949 deletions
diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index bb4af143..44f294f4 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.157 2007/06/19 23:47:16 tbox Exp $ +# $Id: Makefile.in,v 1.158 2007/09/12 01:09:08 each Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -53,8 +53,8 @@ DSTOBJS = @DST_EXTRA_OBJS@ \ DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ cache.@O@ callbacks.@O@ compress.@O@ \ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \ - dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \ - lib.@O@ log.@O@ lookup.@O@ \ + dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ iptable.@O@ journal.@O@ \ + keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \ master.@O@ masterdump.@O@ message.@O@ \ name.@O@ ncache.@O@ nsec.@O@ order.@O@ peer.@O@ portlist.@O@ \ rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \ @@ -79,8 +79,8 @@ DSTSRCS = @DST_EXTRA_SRCS@ \ DNSSRCS = acache.c acl.c adb.c byaddr.c \ cache.c callbacks.c compress.c \ db.c dbiterator.c dbtable.c diff.c dispatch.c \ - dlz.c dnssec.c ds.c forward.c journal.c keytable.c \ - lib.c log.c lookup.c \ + dlz.c dnssec.c ds.c forward.c iptable.c journal.c \ + keytable.c lib.c log.c lookup.c \ master.c masterdump.c message.c \ name.c ncache.c nsec.c order.c peer.c portlist.c \ rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \ diff --git a/lib/dns/acl.c b/lib/dns/acl.c index 5a379108..6bb169c7 100644 --- a/lib/dns/acl.c +++ b/lib/dns/acl.c @@ -15,18 +15,25 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: acl.c,v 1.32 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: acl.c,v 1.35 2007/09/19 03:03:29 marka Exp $ */ /*! \file */ #include <config.h> #include <isc/mem.h> +#include <isc/once.h> #include <isc/string.h> #include <isc/util.h> #include <dns/acl.h> +#include <dns/iptable.h> +/* + * Create a new ACL, including an IP table and an array with room + * for 'n' ACL elements. The elements are uninitialized and the + * length is 0. + */ isc_result_t dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) { isc_result_t result; @@ -43,11 +50,19 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) { return (ISC_R_NOMEMORY); acl->mctx = mctx; acl->name = NULL; + result = isc_refcount_init(&acl->refcount, 1); if (result != ISC_R_SUCCESS) { isc_mem_put(mctx, acl, sizeof(*acl)); return (result); } + + result = dns_iptable_create(mctx, &acl->iptable); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, acl, sizeof(*acl)); + return (result); + } + acl->elements = NULL; acl->alloc = 0; acl->length = 0; @@ -73,111 +88,237 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) { return (result); } -isc_result_t -dns_acl_appendelement(dns_acl_t *acl, const dns_aclelement_t *elt) { - if (acl->length + 1 > acl->alloc) { - /* - * Resize the ACL. - */ - unsigned int newalloc; - void *newmem; - - newalloc = acl->alloc * 2; - if (newalloc < 4) - newalloc = 4; - newmem = isc_mem_get(acl->mctx, - newalloc * sizeof(dns_aclelement_t)); - if (newmem == NULL) - return (ISC_R_NOMEMORY); - memcpy(newmem, acl->elements, - acl->length * sizeof(dns_aclelement_t)); - isc_mem_put(acl->mctx, acl->elements, - acl->alloc * sizeof(dns_aclelement_t)); - acl->elements = newmem; - acl->alloc = newalloc; - } - /* - * Append the new element. - */ - acl->elements[acl->length++] = *elt; - - return (ISC_R_SUCCESS); -} - +/* + * Create a new ACL and initialize it with the value "any" or "none", + * depending on the value of the "neg" parameter. + * "any" is a positive iptable entry with bit length 0. + * "none" is the same as "!any". + */ static isc_result_t dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) { isc_result_t result; dns_acl_t *acl = NULL; - result = dns_acl_create(mctx, 1, &acl); + result = dns_acl_create(mctx, 0, &acl); if (result != ISC_R_SUCCESS) return (result); - acl->elements[0].negative = neg; - acl->elements[0].type = dns_aclelementtype_any; - acl->length = 1; + dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg)); *target = acl; return (result); } +/* + * Create a new ACL that matches everything. + */ isc_result_t dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) { return (dns_acl_anyornone(mctx, ISC_FALSE, target)); } +/* + * Create a new ACL that matches nothing. + */ isc_result_t dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) { return (dns_acl_anyornone(mctx, ISC_TRUE, target)); } +/* + * If pos is ISC_TRUE, test whether acl is set to "{ any; }" + * If pos is ISC_FALSE, test whether acl is set to "{ none; }" + */ +static isc_boolean_t +dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos) +{ + /* Should never happen but let's be safe */ + if (acl == NULL || + acl->iptable == NULL || + acl->iptable->radix == NULL || + acl->iptable->radix->head == NULL || + acl->iptable->radix->head->prefix == NULL) + return (ISC_FALSE); + + if (acl->length != 0 && acl->node_count != 1) + return (ISC_FALSE); + + if (acl->iptable->radix->head->prefix->bitlen == 0 && + *(isc_boolean_t *) (acl->iptable->radix->head->data) == pos) + return (ISC_TRUE); + + return (ISC_FALSE); /* All others */ +} + +/* + * Test whether acl is set to "{ any; }" + */ +isc_boolean_t +dns_acl_isany(dns_acl_t *acl) +{ + return (dns_acl_isanyornone(acl, ISC_TRUE)); +} + +/* + * Test whether acl is set to "{ none; }" + */ +isc_boolean_t +dns_acl_isnone(dns_acl_t *acl) +{ + return (dns_acl_isanyornone(acl, ISC_FALSE)); +} + +/* + * Determine whether a given address or signer matches a given ACL. + * For a match with a positive ACL element or iptable radix entry, + * return with a positive value in match; for a match with a negated ACL + * element or radix entry, return with a negative value in match. + */ isc_result_t dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, const dns_acl_t *acl, const dns_aclenv_t *env, int *match, - dns_aclelement_t const**matchelt) + const dns_aclelement_t **matchelt) { + isc_uint16_t bitlen; + isc_prefix_t pfx; + isc_radix_node_t *node; + const isc_netaddr_t *addr; + isc_netaddr_t v4addr; + isc_result_t result; + int match_num = -1; unsigned int i; REQUIRE(reqaddr != NULL); REQUIRE(matchelt == NULL || *matchelt == NULL); - + + if (env == NULL || env->match_mapped == ISC_FALSE || + reqaddr->family != AF_INET6 || + !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6)) + addr = reqaddr; + else { + isc_netaddr_fromv4mapped(&v4addr, reqaddr); + addr = &v4addr; + } + + /* Always match with host addresses. */ + bitlen = reqaddr->family == AF_INET6 ? 128 : 32; + NETADDR_TO_PREFIX_T(addr, pfx, bitlen); + + /* Assume no match. */ + *match = 0; + + /* Search radix. */ + result = isc_radix_search(acl->iptable->radix, &node, &pfx); + + /* Found a match. */ + if (result == ISC_R_SUCCESS && node != NULL) { + match_num = node->node_num; + if (*(isc_boolean_t *) node->data == ISC_TRUE) + *match = match_num; + else + *match = -match_num; + } + + /* Now search non-radix elements for a match with a lower node_num. */ for (i = 0; i < acl->length; i++) { dns_aclelement_t *e = &acl->elements[i]; if (dns_aclelement_match(reqaddr, reqsigner, e, env, matchelt)) { - *match = e->negative ? -((int)i+1) : ((int)i+1); + if (match_num == -1 || e->node_num < match_num) { + if (e->negative) + *match = -e->node_num; + else + *match = e->node_num; + } return (ISC_R_SUCCESS); } } - /* No match. */ - *match = 0; + return (ISC_R_SUCCESS); } +/* + * Merge the contents of one ACL into another. Call dns_iptable_merge() + * for the IP tables, then concatenate the element arrays. + * + * If pos is set to false, then the nested ACL is to be negated. This + * means reverse the sense of each *positive* element or IP table node, + * but leave negatives alone, so as to prevent a double-negative causing + * an unexpected postive match in the parent ACL. + */ isc_result_t -dns_acl_elementmatch(const dns_acl_t *acl, - const dns_aclelement_t *elt, - const dns_aclelement_t **matchelt) +dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos) { - unsigned int i; - - REQUIRE(elt != NULL); - REQUIRE(matchelt == NULL || *matchelt == NULL); - - for (i = 0; i < acl->length; i++) { - dns_aclelement_t *e = &acl->elements[i]; - - if (dns_aclelement_equal(e, elt) == ISC_TRUE) { - if (matchelt != NULL) - *matchelt = e; - return (ISC_R_SUCCESS); - } - } - - return (ISC_R_NOTFOUND); + isc_result_t result; + unsigned int newalloc, nelem, i; + int max_node = 0, nodes; + + /* Resize the element array if needed. */ + if (dest->length + source->length > dest->alloc) { + void *newmem; + + newalloc = dest->alloc + source->alloc; + if (newalloc < 4) + newalloc = 4; + + newmem = isc_mem_get(dest->mctx, + newalloc * sizeof(dns_aclelement_t)); + if (newmem == NULL) + return (ISC_R_NOMEMORY); + + /* Copy in the original elements */ + memcpy(newmem, dest->elements, + dest->length * sizeof(dns_aclelement_t)); + + /* Release the memory for the old elements array */ + isc_mem_put(dest->mctx, dest->elements, + dest->alloc * sizeof(dns_aclelement_t)); + dest->elements = newmem; + dest->alloc = newalloc; + } + + /* + * Now copy in the new elements, increasing their node_num + * values so as to keep the new ACL consistent. If we're + * negating, then negate positive elements, but keep negative + * elements the same for security reasons. + */ + nelem = dest->length; + memcpy(&dest->elements[nelem], source->elements, + (source->length * sizeof(dns_aclelement_t))); + for (i = 0; i < source->length; i++) { + dest->elements[nelem + i].node_num = + source->elements[i].node_num + dest->node_count; + if (source->elements[i].node_num > max_node) + max_node = source->elements[i].node_num; + if (!pos && source->elements[i].negative == ISC_FALSE) + dest->elements[nelem + i].negative = ISC_TRUE; + } + + /* + * Merge the iptables. Make sure the destination ACL's + * node_count value is set correctly afterward. + */ + nodes = max_node + dest->node_count; + result = dns_iptable_merge(dest->iptable, source->iptable, pos); + if (result != ISC_R_SUCCESS) + return (result); + if (nodes > dest->node_count) + dest->node_count = nodes; + + return (ISC_R_SUCCESS); } +/* + * Like dns_acl_match, but matches against the single ACL element 'e' + * rather than a complete ACL, and returns ISC_TRUE iff it matched. + * + * To determine whether the match was prositive or negative, the + * caller should examine e->negative. Since the element 'e' may be + * a reference to a named ACL or a nested ACL, a matching element + * returned through 'matchelt' is not necessarily 'e' itself. + */ isc_boolean_t dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, @@ -186,90 +327,66 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_aclelement_t **matchelt) { dns_acl_t *inner = NULL; - const isc_netaddr_t *addr; - isc_netaddr_t v4addr; int indirectmatch; isc_result_t result; - switch (e->type) { - case dns_aclelementtype_ipprefix: - if (env == NULL || - env->match_mapped == ISC_FALSE || - reqaddr->family != AF_INET6 || - !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6)) - addr = reqaddr; - else { - isc_netaddr_fromv4mapped(&v4addr, reqaddr); - addr = &v4addr; - } - - if (isc_netaddr_eqprefix(addr, - &e->u.ip_prefix.address, - e->u.ip_prefix.prefixlen)) - goto matched; - break; - - case dns_aclelementtype_keyname: + switch (e->type) { + case dns_aclelementtype_keyname: if (reqsigner != NULL && - dns_name_equal(reqsigner, &e->u.keyname)) - goto matched; - break; - - case dns_aclelementtype_nestedacl: - inner = e->u.nestedacl; - nested: - result = dns_acl_match(reqaddr, reqsigner, - inner, - env, - &indirectmatch, matchelt); - INSIST(result == ISC_R_SUCCESS); - - /* - * Treat negative matches in indirect ACLs as - * "no match". - * That way, a negated indirect ACL will never become - * a surprise positive match through double negation. - * XXXDCL this should be documented. - */ - if (indirectmatch > 0) - goto matchelt_set; - - /* - * A negative indirect match may have set *matchelt, - * but we don't want it set when we return. - */ - if (matchelt != NULL) - *matchelt = NULL; - break; + dns_name_equal(reqsigner, &e->keyname)) { + if (matchelt != NULL) + *matchelt = e; + return (ISC_TRUE); + } else { + return (ISC_FALSE); + } + + case dns_aclelementtype_nestedacl: + inner = e->nestedacl; + break; + + case dns_aclelementtype_localhost: + if (env == NULL || env->localhost == NULL) + return (ISC_FALSE); + inner = env->localhost; + break; + + case dns_aclelementtype_localnets: + if (env == NULL || env->localnets == NULL) + return (ISC_FALSE); + inner = env->localnets; + break; + + default: + /* Should be impossible */ + INSIST(0); + } - case dns_aclelementtype_any: - matched: - if (matchelt != NULL) - *matchelt = e; - matchelt_set: - return (ISC_TRUE); - - case dns_aclelementtype_localhost: - if (env != NULL && env->localhost != NULL) { - inner = env->localhost; - goto nested; - } else { - break; - } + result = dns_acl_match(reqaddr, reqsigner, inner, env, + &indirectmatch, matchelt); + INSIST(result == ISC_R_SUCCESS); + + /* + * Treat negative matches in indirect ACLs as "no match". + * That way, a negated indirect ACL will never become a + * surprise positive match through double negation. + * XXXDCL this should be documented. + */ + + if (indirectmatch > 0) { + if (matchelt != NULL) + *matchelt = e; + return (ISC_TRUE); + } - case dns_aclelementtype_localnets: - if (env != NULL && env->localnets != NULL) { - inner = env->localnets; - goto nested; - } else { - break; - } - - default: - INSIST(0); - break; - } + /* + * A negative indirect match may have set *matchelt, but we don't + * want it set when we return. + */ + if (matchelt != NULL) + *matchelt = NULL; + return (ISC_FALSE); } @@ -285,15 +402,10 @@ destroy(dns_acl_t *dacl) { unsigned int i; for (i = 0; i < dacl->length; i++) { dns_aclelement_t *de = &dacl->elements[i]; - switch (de->type) { - case dns_aclelementtype_keyname: - dns_name_free(&de->u.keyname, dacl->mctx); - break; - case dns_aclelementtype_nestedacl: - dns_acl_detach(&de->u.nestedacl); - break; - default: - break; + if (de->type == dns_aclelementtype_keyname) { + dns_name_free(&de->keyname, dacl->mctx); + } else if (de->type == dns_aclelementtype_nestedacl) { + dns_acl_detach(&de->nestedacl); } } if (dacl->elements != NULL) @@ -301,6 +413,8 @@ destroy(dns_acl_t *dacl) { dacl->alloc * sizeof(dns_aclelement_t)); if (dacl->name != NULL) isc_mem_free(dacl->mctx, dacl->name); + if (dacl->iptable != NULL) + dns_iptable_detach(&dacl->iptable); isc_refcount_destroy(&dacl->refcount); dacl->magic = 0; isc_mem_put(dacl->mctx, dacl, sizeof(*dacl)); @@ -317,69 +431,79 @@ dns_acl_detach(dns_acl_t **aclp) { *aclp = NULL; } -isc_boolean_t -dns_aclelement_equal(const dns_aclelement_t *ea, const dns_aclelement_t *eb) { - if (ea->type != eb->type) - return (ISC_FALSE); - switch (ea->type) { - case dns_aclelementtype_ipprefix: - if (ea->u.ip_prefix.prefixlen != - eb->u.ip_prefix.prefixlen) - return (ISC_FALSE); - return (isc_netaddr_eqprefix(&ea->u.ip_prefix.address, - &eb->u.ip_prefix.address, - ea->u.ip_prefix.prefixlen)); - case dns_aclelementtype_keyname: - return (dns_name_equal(&ea->u.keyname, &eb->u.keyname)); - case dns_aclelementtype_nestedacl: - return (dns_acl_equal(ea->u.nestedacl, eb->u.nestedacl)); - case dns_aclelementtype_localhost: - case dns_aclelementtype_localnets: - case dns_aclelementtype_any: - return (ISC_TRUE); - default: - INSIST(0); - return (ISC_FALSE); - } -} -isc_boolean_t -dns_acl_equal(const dns_acl_t *a, const dns_acl_t *b) { - unsigned int i; - if (a == b) - return (ISC_TRUE); - if (a->length != b->length) - return (ISC_FALSE); - for (i = 0; i < a->length; i++) { - if (! dns_aclelement_equal(&a->elements[i], - &b->elements[i])) - return (ISC_FALSE); - } - return (ISC_TRUE); +static isc_once_t insecure_prefix_once = ISC_ONCE_INIT; +static isc_mutex_t insecure_prefix_lock; +static isc_boolean_t insecure_prefix_found; + +static void +initialize_action(void) { + RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS); } -static isc_boolean_t -is_loopback(const dns_aclipprefix_t *p) { - switch (p->address.family) { - case AF_INET: - if (p->prefixlen == 32 && - htonl(p->address.type.in.s_addr) == INADDR_LOOPBACK) - return (ISC_TRUE); +/* + * Called via isc_radix_walk() to find IP table nodes that are + * insecure. + */ +static void +is_insecure(isc_prefix_t *prefix, void *data) { + isc_boolean_t secure = * (isc_boolean_t *)data; + + /* Negated entries are always secure */ + if (!secure) { + return; + } + + /* If loopback prefix found, return */ + switch (prefix->family) { + case AF_INET: + if (prefix->bitlen == 32 && + htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK) + return; break; - case AF_INET6: - if (p->prefixlen == 128 && - IN6_IS_ADDR_LOOPBACK(&p->address.type.in6)) - return (ISC_TRUE); + case AF_INET6: + if (prefix->bitlen == 128 && + IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6)) + return; break; - default: + default: break; } - return (ISC_FALSE); + + /* Non-negated, non-loopback */ + insecure_prefix_found = ISC_TRUE; + return; } +/* + * Return ISC_TRUE iff the acl 'a' is considered insecure, that is, + * if it contains IP addresses other than those of the local host. + * This is intended for applications such as printing warning + * messages for suspect ACLs; it is not intended for making access + * control decisions. We make no guarantee that an ACL for which + * this function returns ISC_FALSE is safe. + */ isc_boolean_t dns_acl_isinsecure(const dns_acl_t *a) { unsigned int i; + isc_boolean_t insecure; + + RUNTIME_CHECK(isc_once_do(&insecure_prefix_once, + initialize_action) == ISC_R_SUCCESS); + + /* + * Walk radix tree to find out if there are any non-negated, + * non-loopback prefixes. + */ + LOCK(&insecure_prefix_lock); + insecure_prefix_found = ISC_FALSE; + isc_radix_process(a->iptable->radix, is_insecure); + insecure = insecure_prefix_found; + UNLOCK(&insecure_prefix_lock); + if (insecure) + return(ISC_TRUE); + + /* Now check non-radix elements */ for (i = 0; i < a->length; i++) { dns_aclelement_t *e = &a->elements[i]; @@ -388,34 +512,31 @@ dns_acl_isinsecure(const dns_acl_t *a) { continue; switch (e->type) { - case dns_aclelementtype_ipprefix: - /* The loopback address is considered secure. */ - if (! is_loopback(&e->u.ip_prefix)) - return (ISC_TRUE); - continue; - - case dns_aclelementtype_keyname: - case dns_aclelementtype_localhost: + case dns_aclelementtype_keyname: + case dns_aclelementtype_localhost: continue; - case dns_aclelementtype_nestedacl: - if (dns_acl_isinsecure(e->u.nestedacl)) - return (ISC_TRUE); - continue; - - case dns_aclelementtype_localnets: - case dns_aclelementtype_any: + case dns_aclelementtype_nestedacl: + if (dns_acl_isinsecure(e->nestedacl)) + return (ISC_TRUE); + continue; + + case dns_aclelementtype_localnets: return (ISC_TRUE); - default: + default: INSIST(0); return (ISC_TRUE); } } + /* No insecure elements were found. */ return (ISC_FALSE); } +/* + * Initialize ACL environment, setting up localhost and localnets ACLs + */ isc_result_t dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) { isc_result_t result; diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 79b6a19b..182f1c53 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -15,9 +15,9 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: adb.c,v 1.231 2007/06/18 23:47:40 tbox Exp $ */ +/* $Id: adb.c,v 1.233 2007/10/19 17:15:53 explorer Exp $ */ -/*! \file +/*! \file * * \note * In finds, if task == NULL, no events will be generated, and no events @@ -40,7 +40,7 @@ #include <isc/mutexblock.h> #include <isc/netaddr.h> #include <isc/random.h> -#include <isc/string.h> /* Required for HP/UX (and others?) */ +#include <isc/string.h> /* Required for HP/UX (and others?) */ #include <isc/task.h> #include <isc/timer.h> #include <isc/util.h> @@ -56,27 +56,27 @@ #include <dns/resolver.h> #include <dns/result.h> -#define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b') -#define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC) -#define DNS_ADBNAME_MAGIC ISC_MAGIC('a', 'd', 'b', 'N') -#define DNS_ADBNAME_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAME_MAGIC) -#define DNS_ADBNAMEHOOK_MAGIC ISC_MAGIC('a', 'd', 'N', 'H') +#define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b') +#define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC) +#define DNS_ADBNAME_MAGIC ISC_MAGIC('a', 'd', 'b', 'N') +#define DNS_ADBNAME_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAME_MAGIC) +#define DNS_ADBNAMEHOOK_MAGIC ISC_MAGIC('a', 'd', 'N', 'H') #define DNS_ADBNAMEHOOK_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAMEHOOK_MAGIC) -#define DNS_ADBLAMEINFO_MAGIC ISC_MAGIC('a', 'd', 'b', 'Z') +#define DNS_ADBLAMEINFO_MAGIC ISC_MAGIC('a', 'd', 'b', 'Z') #define DNS_ADBLAMEINFO_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBLAMEINFO_MAGIC) -#define DNS_ADBENTRY_MAGIC ISC_MAGIC('a', 'd', 'b', 'E') -#define DNS_ADBENTRY_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBENTRY_MAGIC) -#define DNS_ADBFETCH_MAGIC ISC_MAGIC('a', 'd', 'F', '4') -#define DNS_ADBFETCH_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH_MAGIC) -#define DNS_ADBFETCH6_MAGIC ISC_MAGIC('a', 'd', 'F', '6') -#define DNS_ADBFETCH6_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH6_MAGIC) - -/*! +#define DNS_ADBENTRY_MAGIC ISC_MAGIC('a', 'd', 'b', 'E') +#define DNS_ADBENTRY_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBENTRY_MAGIC) +#define DNS_ADBFETCH_MAGIC ISC_MAGIC('a', 'd', 'F', '4') +#define DNS_ADBFETCH_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH_MAGIC) +#define DNS_ADBFETCH6_MAGIC ISC_MAGIC('a', 'd', 'F', '6') +#define DNS_ADBFETCH6_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH6_MAGIC) + +/*! * The number of buckets needs to be a prime (for good hashing). * * XXXRTH How many buckets do we need? */ -#define NBUCKETS 1009 /*%< how many buckets for names/addrs */ +#define NBUCKETS 1009 /*%< how many buckets for names/addrs */ /*! * For type 3 negative cache entries, we will remember that the address is @@ -84,26 +84,35 @@ * The intent is to keep us from constantly asking about A/AAAA records * if the zone has extremely low TTLs. */ -#define ADB_CACHE_MINIMUM 10 /*%< seconds */ -#define ADB_CACHE_MAXIMUM 86400 /*%< seconds (86400 = 24 hours) */ -#define ADB_ENTRY_WINDOW 1800 /*%< seconds */ +#define ADB_CACHE_MINIMUM 10 /*%< seconds */ +#define ADB_CACHE_MAXIMUM 86400 /*%< seconds (86400 = 24 hours) */ +#define ADB_ENTRY_WINDOW 1800 /*%< seconds */ /*% * Wake up every CLEAN_SECONDS and clean CLEAN_BUCKETS buckets, so that all * buckets are cleaned in CLEAN_PERIOD seconds. */ -#define CLEAN_PERIOD 3600 +#define CLEAN_PERIOD 3600 /*% See #CLEAN_PERIOD */ -#define CLEAN_SECONDS 30 +#define CLEAN_SECONDS 30 /*% See #CLEAN_PERIOD */ -#define CLEAN_BUCKETS ((NBUCKETS * CLEAN_SECONDS) / CLEAN_PERIOD) +#define CLEAN_BUCKETS ((NBUCKETS * CLEAN_SECONDS) / CLEAN_PERIOD) + +/*% + * The period in seconds after which an ADB name entry is regarded as stale + * and forced to be cleaned up. + * TODO: This should probably be configurable at run-time. + */ +#ifndef ADB_STALE_MARGIN +#define ADB_STALE_MARGIN 1800 +#endif -#define FREE_ITEMS 64 /*%< free count for memory pools */ -#define FILL_COUNT 16 /*%< fill count for memory pools */ +#define FREE_ITEMS 64 /*%< free count for memory pools */ +#define FILL_COUNT 16 /*%< fill count for memory pools */ -#define DNS_ADB_INVALIDBUCKET (-1) /*%< invalid bucket address */ +#define DNS_ADB_INVALIDBUCKET (-1) /*%< invalid bucket address */ -#define DNS_ADB_MINADBSIZE (1024*1024) /*%< 1 Megabyte */ +#define DNS_ADB_MINADBSIZE (1024*1024) /*%< 1 Megabyte */ typedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t; typedef struct dns_adbnamehook dns_adbnamehook_t; @@ -115,60 +124,78 @@ typedef struct dns_adbfetch6 dns_adbfetch6_t; /*% dns adb structure */ struct dns_adb { - unsigned int magic; - - isc_mutex_t lock; - isc_mutex_t reflock; /*%< Covers irefcnt, erefcnt */ - isc_mem_t *mctx; - dns_view_t *view; - isc_timermgr_t *timermgr; - isc_timer_t *timer; - isc_taskmgr_t *taskmgr; - isc_task_t *task; - isc_boolean_t overmem; - - isc_interval_t tick_interval; - int next_cleanbucket; - - unsigned int irefcnt; - unsigned int erefcnt; - - isc_mutex_t mplock; - isc_mempool_t *nmp; /*%< dns_adbname_t */ - isc_mempool_t *nhmp; /*%< dns_adbnamehook_t */ - isc_mempool_t *limp; /*%< dns_adblameinfo_t */ - isc_mempool_t *emp; /*%< dns_adbentry_t */ - isc_mempool_t *ahmp; /*%< dns_adbfind_t */ - isc_mempool_t *aimp; /*%< dns_adbaddrinfo_t */ - isc_mempool_t *afmp; /*%< dns_adbfetch_t */ - - /*! - * Bucketized locks and lists for names. - * - * XXXRTH Have a per-bucket structure that contains all of these? - */ - dns_adbnamelist_t names[NBUCKETS]; - /*% See dns_adbnamelist_t */ - isc_mutex_t namelocks[NBUCKETS]; - /*% See dns_adbnamelist_t */ - isc_boolean_t name_sd[NBUCKETS]; - /*% See dns_adbnamelist_t */ - unsigned int name_refcnt[NBUCKETS]; - - /*! - * Bucketized locks for entries. - * - * XXXRTH Have a per-bucket structure that contains all of these? - */ - dns_adbentrylist_t entries[NBUCKETS]; - isc_mutex_t entrylocks[NBUCKETS]; - isc_boolean_t entry_sd[NBUCKETS]; /*%< shutting down */ - unsigned int entry_refcnt[NBUCKETS]; - - isc_event_t cevent; - isc_boolean_t cevent_sent; - isc_boolean_t shutting_down; - isc_eventlist_t whenshutdown; + unsigned int magic; + + isc_mutex_t lock; + isc_mutex_t reflock; /*%< Covers irefcnt, erefcnt */ + isc_mem_t *mctx; + dns_view_t *view; + isc_timermgr_t *timermgr; + isc_timer_t *timer; + +#ifdef LRU_DEBUG + isc_timer_t *dump_timer; /* for test */ + isc_time_t dump_time; /* for test */ +#define DUMP_INTERVAL 30 /* seconds */ +#endif + + isc_taskmgr_t *taskmgr; + isc_task_t *task; + isc_boolean_t overmem; + + isc_interval_t tick_interval; + int next_cleanbucket; + + unsigned int irefcnt; + unsigned int erefcnt; + + isc_mutex_t mplock; + isc_mempool_t *nmp; /*%< dns_adbname_t */ + isc_mempool_t *nhmp; /*%< dns_adbnamehook_t */ + isc_mempool_t *limp; /*%< dns_adblameinfo_t */ + isc_mempool_t *emp; /*%< dns_adbentry_t */ + isc_mempool_t *ahmp; /*%< dns_adbfind_t */ + isc_mempool_t *aimp; /*%< dns_adbaddrinfo_t */ + isc_mempool_t *afmp; /*%< dns_adbfetch_t */ + + /*! + * Bucketized locks and lists for names. + * + * XXXRTH Have a per-bucket structure that contains all of these? + */ + dns_adbnamelist_t names[NBUCKETS]; + /*% See dns_adbnamelist_t */ + isc_mutex_t namelocks[NBUCKETS]; + /*% See dns_adbnamelist_t */ + isc_boolean_t name_sd[NBUCKETS]; + /*% See dns_adbnamelist_t */ + unsigned int name_refcnt[NBUCKETS]; + + /*! + * Bucketized locks for entries. + * + * XXXRTH Have a per-bucket structure that contains all of these? + */ + dns_adbentrylist_t entries[NBUCKETS]; + isc_mutex_t entrylocks[NBUCKETS]; + isc_boolean_t entry_sd[NBUCKETS]; /*%< shutting down */ + unsigned int entry_refcnt[NBUCKETS]; + + isc_event_t cevent; + isc_boolean_t cevent_sent; + isc_boolean_t shutting_down; + isc_eventlist_t whenshutdown; + +#ifdef LRU_DEBUG + unsigned int stale_purge; + unsigned int stale_scan; + unsigned int stale_expire; + unsigned int stale_lru; + + unsigned int nname, nname_total; + unsigned int nentry, nentry_total; + unsigned int nameuses, entryuses; +#endif }; /* @@ -177,34 +204,37 @@ struct dns_adb { /*% dns_adbname structure */ struct dns_adbname { - unsigned int magic; - dns_name_t name; - dns_adb_t *adb; - unsigned int partial_result; - unsigned int flags; - int lock_bucket; - dns_name_t target; - isc_stdtime_t expire_target; - isc_stdtime_t expire_v4; - isc_stdtime_t expire_v6; - unsigned int chains; - dns_adbnamehooklist_t v4; - dns_adbnamehooklist_t v6; - dns_adbfetch_t *fetch_a; - dns_adbfetch_t *fetch_aaaa; - unsigned int fetch_err; - unsigned int fetch6_err; - dns_adbfindlist_t finds; - ISC_LINK(dns_adbname_t) plink; + unsigned int magic; + dns_name_t name; + dns_adb_t *adb; + unsigned int partial_result; + unsigned int flags; + int lock_bucket; + dns_name_t target; + isc_stdtime_t expire_target; + isc_stdtime_t expire_v4; + isc_stdtime_t expire_v6; + unsigned int chains; + dns_adbnamehooklist_t v4; + dns_adbnamehooklist_t v6; + dns_adbfetch_t *fetch_a; + dns_adbfetch_t *fetch_aaaa; + unsigned int fetch_err; + unsigned int fetch6_err; + dns_adbfindlist_t finds; + /* for LRU-based management */ + isc_stdtime_t last_used; + + ISC_LINK(dns_adbname_t) plink; }; /*% The adbfetch structure */ struct dns_adbfetch { - unsigned int magic; - dns_adbnamehook_t *namehook; - dns_adbentry_t *entry; - dns_fetch_t *fetch; - dns_rdataset_t rdataset; + unsigned int magic; + dns_adbnamehook_t *namehook; + dns_adbentry_t *entry; + dns_fetch_t *fetch; + dns_rdataset_t rdataset; }; /*% @@ -213,9 +243,9 @@ struct dns_adbfetch { * namehook that will contain the next address this host has. */ struct dns_adbnamehook { - unsigned int magic; - dns_adbentry_t *entry; - ISC_LINK(dns_adbnamehook_t) plink; + unsigned int magic; + dns_adbentry_t *entry; + ISC_LINK(dns_adbnamehook_t) plink; }; /*% @@ -224,13 +254,13 @@ struct dns_adbnamehook { * extended to other types of information about zones. */ struct dns_adblameinfo { - unsigned int magic; + unsigned int magic; - dns_name_t qname; - dns_rdatatype_t qtype; - isc_stdtime_t lame_timer; + dns_name_t qname; + dns_rdatatype_t qtype; + isc_stdtime_t lame_timer; - ISC_LINK(dns_adblameinfo_t) plink; + ISC_LINK(dns_adblameinfo_t) plink; }; /*% @@ -239,26 +269,26 @@ struct dns_adblameinfo { * the host. */ struct dns_adbentry { - unsigned int magic; - - int lock_bucket; - unsigned int refcnt; - - unsigned int flags; - unsigned int srtt; - isc_sockaddr_t sockaddr; - - isc_stdtime_t expires; - /*%< - * A nonzero 'expires' field indicates that the entry should - * persist until that time. This allows entries found - * using dns_adb_findaddrinfo() to persist for a limited time - * even though they are not necessarily associated with a - * name. - */ - - ISC_LIST(dns_adblameinfo_t) lameinfo; - ISC_LINK(dns_adbentry_t) plink; + unsigned int magic; + + int lock_bucket; + unsigned int refcnt; + + unsigned int flags; + unsigned int srtt; + isc_sockaddr_t sockaddr; + + isc_stdtime_t expires; + /*%< + * A nonzero 'expires' field indicates that the entry should + * persist until that time. This allows entries found + * using dns_adb_findaddrinfo() to persist for a limited time + * even though they are not necessarily associated with a + * name. + */ + + ISC_LIST(dns_adblameinfo_t) lameinfo; + ISC_LINK(dns_adbentry_t) plink; }; /* @@ -267,50 +297,50 @@ struct dns_adbentry { static inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *); static inline void free_adbname(dns_adb_t *, dns_adbname_t **); static inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *, - dns_adbentry_t *); + dns_adbentry_t *); static inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **); static inline dns_adblameinfo_t *new_adblameinfo(dns_adb_t *, dns_name_t *, - dns_rdatatype_t); + dns_rdatatype_t); static inline void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **); static inline dns_adbentry_t *new_adbentry(dns_adb_t *); static inline void free_adbentry(dns_adb_t *, dns_adbentry_t **); static inline dns_adbfind_t *new_adbfind(dns_adb_t *); static inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **); static inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *, - in_port_t); + in_port_t); static inline dns_adbfetch_t *new_adbfetch(dns_adb_t *); static inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **); static inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *, - unsigned int, int *); + unsigned int, int *); static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *, - isc_sockaddr_t *, int *); + isc_sockaddr_t *, int *); static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t); static void print_dns_name(FILE *, dns_name_t *); static void print_namehook_list(FILE *, const char *legend, - dns_adbnamehooklist_t *list, - isc_boolean_t debug, - isc_stdtime_t now); + dns_adbnamehooklist_t *list, + isc_boolean_t debug, + isc_stdtime_t now); static void print_find_list(FILE *, dns_adbname_t *); static void print_fetch_list(FILE *, dns_adbname_t *); static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *); static inline void inc_adb_irefcnt(dns_adb_t *); static inline void inc_adb_erefcnt(dns_adb_t *); static inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *, - isc_boolean_t); + isc_boolean_t); static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *, - isc_boolean_t); + isc_boolean_t); static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *); -static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *); +static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *, + isc_boolean_t); static void clean_target(dns_adb_t *, dns_name_t *); static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, - unsigned int); -static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t, - isc_boolean_t); + unsigned int); +static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t); static void cancel_fetches_at_name(dns_adbname_t *); static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t, - dns_rdatatype_t); + dns_rdatatype_t); static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t, - dns_rdatatype_t); + dns_rdatatype_t); static inline void check_exit(dns_adb_t *); static void timer_cleanup(isc_task_t *, isc_event_t *); static void destroy(dns_adb_t *); @@ -320,35 +350,40 @@ static inline void link_name(dns_adb_t *, int, dns_adbname_t *); static inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *); static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *); static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *); -static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t); +static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t, + isc_boolean_t); static void water(void *, int); static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); +#ifdef LRU_DEBUG +static void timer_dump(isc_task_t *, isc_event_t *); +#endif + /* * MUST NOT overlap DNS_ADBFIND_* flags! */ -#define FIND_EVENT_SENT 0x40000000 -#define FIND_EVENT_FREED 0x80000000 -#define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0) -#define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0) - -#define NAME_NEEDS_POKE 0x80000000 -#define NAME_IS_DEAD 0x40000000 -#define NAME_HINT_OK DNS_ADBFIND_HINTOK -#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK -#define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE -#define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0) -#define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0) -#define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0) -#define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0) +#define FIND_EVENT_SENT 0x40000000 +#define FIND_EVENT_FREED 0x80000000 +#define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0) +#define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0) + +#define NAME_NEEDS_POKE 0x80000000 +#define NAME_IS_DEAD 0x40000000 +#define NAME_HINT_OK DNS_ADBFIND_HINTOK +#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK +#define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE +#define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0) +#define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0) +#define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0) +#define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0) /* * To the name, address classes are all that really exist. If it has a * V6 address it doesn't care if it came from a AAAA query. */ -#define NAME_HAS_V4(n) (!ISC_LIST_EMPTY((n)->v4)) -#define NAME_HAS_V6(n) (!ISC_LIST_EMPTY((n)->v6)) -#define NAME_HAS_ADDRS(n) (NAME_HAS_V4(n) || NAME_HAS_V6(n)) +#define NAME_HAS_V4(n) (!ISC_LIST_EMPTY((n)->v4)) +#define NAME_HAS_V6(n) (!ISC_LIST_EMPTY((n)->v6)) +#define NAME_HAS_ADDRS(n) (NAME_HAS_V4(n) || NAME_HAS_V6(n)) /* * Fetches are broken out into A and AAAA types. In some cases, @@ -357,34 +392,34 @@ static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); * Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA * are now equal to FETCH_V4 and FETCH_V6, respectively. */ -#define NAME_FETCH_A(n) ((n)->fetch_a != NULL) -#define NAME_FETCH_AAAA(n) ((n)->fetch_aaaa != NULL) -#define NAME_FETCH_V4(n) (NAME_FETCH_A(n)) -#define NAME_FETCH_V6(n) (NAME_FETCH_AAAA(n)) -#define NAME_FETCH(n) (NAME_FETCH_V4(n) || NAME_FETCH_V6(n)) +#define NAME_FETCH_A(n) ((n)->fetch_a != NULL) +#define NAME_FETCH_AAAA(n) ((n)->fetch_aaaa != NULL) +#define NAME_FETCH_V4(n) (NAME_FETCH_A(n)) +#define NAME_FETCH_V6(n) (NAME_FETCH_AAAA(n)) +#define NAME_FETCH(n) (NAME_FETCH_V4(n) || NAME_FETCH_V6(n)) /* * Find options and tests to see if there are addresses on the list. */ -#define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0) -#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0) -#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \ - != 0) -#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \ - != 0) -#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0) -#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0) -#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) -#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0) +#define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0) +#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0) +#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \ + != 0) +#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \ + != 0) +#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0) +#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0) +#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) +#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0) /* * These are currently used on simple unsigned ints, so they are * not really associated with any particular type. */ -#define WANT_INET(x) (((x) & DNS_ADBFIND_INET) != 0) -#define WANT_INET6(x) (((x) & DNS_ADBFIND_INET6) != 0) +#define WANT_INET(x) (((x) & DNS_ADBFIND_INET) != 0) +#define WANT_INET6(x) (((x) & DNS_ADBFIND_INET6) != 0) -#define EXPIRE_OK(exp, now) ((exp == INT_MAX) || (exp < now)) +#define EXPIRE_OK(exp, now) ((exp == INT_MAX) || (exp < now)) /* * Find out if the flags on a name (nf) indicate if it is a hint or @@ -395,57 +430,57 @@ static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); #define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o) & DNS_ADBFIND_HINTOK) != 0)) #define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o)) #define STARTATZONE_MATCHES(nf, o) (((nf)->flags & NAME_STARTATZONE) == \ - ((o) & DNS_ADBFIND_STARTATZONE)) - -#define ENTER_LEVEL ISC_LOG_DEBUG(50) -#define EXIT_LEVEL ENTER_LEVEL -#define CLEAN_LEVEL ISC_LOG_DEBUG(100) -#define DEF_LEVEL ISC_LOG_DEBUG(5) -#define NCACHE_LEVEL ISC_LOG_DEBUG(20) - -#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \ - (r) == DNS_R_NCACHENXRRSET) -#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \ - (r) == DNS_R_NXRRSET) -#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \ - (r) == DNS_R_NCACHENXDOMAIN) -#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \ - (r) == DNS_R_NXRRSET || \ - (r) == DNS_R_HINTNXRRSET) + ((o) & DNS_ADBFIND_STARTATZONE)) + +#define ENTER_LEVEL ISC_LOG_DEBUG(50) +#define EXIT_LEVEL ENTER_LEVEL +#define CLEAN_LEVEL ISC_LOG_DEBUG(100) +#define DEF_LEVEL ISC_LOG_DEBUG(5) +#define NCACHE_LEVEL ISC_LOG_DEBUG(20) + +#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \ + (r) == DNS_R_NCACHENXRRSET) +#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \ + (r) == DNS_R_NXRRSET) +#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \ + (r) == DNS_R_NCACHENXDOMAIN) +#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \ + (r) == DNS_R_NXRRSET || \ + (r) == DNS_R_HINTNXRRSET) /* * Error state rankings. */ -#define FIND_ERR_SUCCESS 0 /* highest rank */ -#define FIND_ERR_CANCELED 1 -#define FIND_ERR_FAILURE 2 -#define FIND_ERR_NXDOMAIN 3 -#define FIND_ERR_NXRRSET 4 -#define FIND_ERR_UNEXPECTED 5 -#define FIND_ERR_NOTFOUND 6 -#define FIND_ERR_MAX 7 +#define FIND_ERR_SUCCESS 0 /* highest rank */ +#define FIND_ERR_CANCELED 1 +#define FIND_ERR_FAILURE 2 +#define FIND_ERR_NXDOMAIN 3 +#define FIND_ERR_NXRRSET 4 +#define FIND_ERR_UNEXPECTED 5 +#define FIND_ERR_NOTFOUND 6 +#define FIND_ERR_MAX 7 static const char *errnames[] = { - "success", - "canceled", - "failure", - "nxdomain", - "nxrrset", - "unexpected", - "not_found" + "success", + "canceled", + "failure", + "nxdomain", + "nxrrset", + "unexpected", + "not_found" }; -#define NEWERR(old, new) (ISC_MIN((old), (new))) +#define NEWERR(old, new) (ISC_MIN((old), (new))) static isc_result_t find_err_map[FIND_ERR_MAX] = { - ISC_R_SUCCESS, - ISC_R_CANCELED, - ISC_R_FAILURE, - DNS_R_NXDOMAIN, - DNS_R_NXRRSET, - ISC_R_UNEXPECTED, - ISC_R_NOTFOUND /* not YET found */ + ISC_R_SUCCESS, + ISC_R_CANCELED, + ISC_R_FAILURE, + DNS_R_NXDOMAIN, + DNS_R_NXRRSET, + ISC_R_UNEXPECTED, + ISC_R_NOTFOUND /* not YET found */ }; static void @@ -453,23 +488,23 @@ DP(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); static void DP(int level, const char *format, ...) { - va_list args; + va_list args; - va_start(args, format); - isc_log_vwrite(dns_lctx, - DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB, - level, format, args); - va_end(args); + va_start(args, format); + isc_log_vwrite(dns_lctx, + DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB, + level, format, args); + va_end(args); } static inline dns_ttl_t ttlclamp(dns_ttl_t ttl) { - if (ttl < ADB_CACHE_MINIMUM) - ttl = ADB_CACHE_MINIMUM; - if (ttl > ADB_CACHE_MAXIMUM) - ttl = ADB_CACHE_MAXIMUM; + if (ttl < ADB_CACHE_MINIMUM) + ttl = ADB_CACHE_MINIMUM; + if (ttl > ADB_CACHE_MAXIMUM) + ttl = ADB_CACHE_MAXIMUM; - return (ttl); + return (ttl); } /* @@ -479,255 +514,241 @@ ttlclamp(dns_ttl_t ttl) { */ static isc_result_t import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, - isc_stdtime_t now) + isc_stdtime_t now) { - isc_result_t result; - dns_adb_t *adb; - dns_adbnamehook_t *nh; - dns_adbnamehook_t *anh; - dns_rdata_t rdata = DNS_RDATA_INIT; - struct in_addr ina; - struct in6_addr in6a; - isc_sockaddr_t sockaddr; - dns_adbentry_t *foundentry; /* NO CLEAN UP! */ - int addr_bucket; - isc_boolean_t new_addresses_added; - dns_rdatatype_t rdtype; - unsigned int findoptions; - - INSIST(DNS_ADBNAME_VALID(adbname)); - adb = adbname->adb; - INSIST(DNS_ADB_VALID(adb)); - - rdtype = rdataset->type; - INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); - if (rdtype == dns_rdatatype_a) - findoptions = DNS_ADBFIND_INET; - else - findoptions = DNS_ADBFIND_INET6; - - addr_bucket = DNS_ADB_INVALIDBUCKET; - new_addresses_added = ISC_FALSE; - - nh = NULL; - result = dns_rdataset_first(rdataset); - while (result == ISC_R_SUCCESS) { - dns_rdata_reset(&rdata); - dns_rdataset_current(rdataset, &rdata); - if (rdtype == dns_rdatatype_a) { - INSIST(rdata.length == 4); - memcpy(&ina.s_addr, rdata.data, 4); - isc_sockaddr_fromin(&sockaddr, &ina, 0); - } else { - INSIST(rdata.length == 16); - memcpy(in6a.s6_addr, rdata.data, 16); - isc_sockaddr_fromin6(&sockaddr, &in6a, 0); - } - - INSIST(nh == NULL); - nh = new_adbnamehook(adb, NULL); - if (nh == NULL) { - adbname->partial_result |= findoptions; - result = ISC_R_NOMEMORY; - goto fail; - } - - foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket); - if (foundentry == NULL) { - dns_adbentry_t *entry; - - entry = new_adbentry(adb); - if (entry == NULL) { - adbname->partial_result |= findoptions; - result = ISC_R_NOMEMORY; - goto fail; - } - - entry->sockaddr = sockaddr; - entry->refcnt = 1; - - nh->entry = entry; - - link_entry(adb, addr_bucket, entry); - } else { - for (anh = ISC_LIST_HEAD(adbname->v4); - anh != NULL; - anh = ISC_LIST_NEXT(anh, plink)) - if (anh->entry == foundentry) - break; - if (anh == NULL) { - foundentry->refcnt++; - nh->entry = foundentry; - } else - free_adbnamehook(adb, &nh); - } - - new_addresses_added = ISC_TRUE; - if (nh != NULL) { - if (rdtype == dns_rdatatype_a) - ISC_LIST_APPEND(adbname->v4, nh, plink); - else - ISC_LIST_APPEND(adbname->v6, nh, plink); - } - nh = NULL; - result = dns_rdataset_next(rdataset); - } + isc_result_t result; + dns_adb_t *adb; + dns_adbnamehook_t *nh; + dns_adbnamehook_t *anh; + dns_rdata_t rdata = DNS_RDATA_INIT; + struct in_addr ina; + struct in6_addr in6a; + isc_sockaddr_t sockaddr; + dns_adbentry_t *foundentry; /* NO CLEAN UP! */ + int addr_bucket; + isc_boolean_t new_addresses_added; + dns_rdatatype_t rdtype; + unsigned int findoptions; + + INSIST(DNS_ADBNAME_VALID(adbname)); + adb = adbname->adb; + INSIST(DNS_ADB_VALID(adb)); + + rdtype = rdataset->type; + INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); + if (rdtype == dns_rdatatype_a) + findoptions = DNS_ADBFIND_INET; + else + findoptions = DNS_ADBFIND_INET6; + + addr_bucket = DNS_ADB_INVALIDBUCKET; + new_addresses_added = ISC_FALSE; + + nh = NULL; + result = dns_rdataset_first(rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); + dns_rdataset_current(rdataset, &rdata); + if (rdtype == dns_rdatatype_a) { + INSIST(rdata.length == 4); + memcpy(&ina.s_addr, rdata.data, 4); + isc_sockaddr_fromin(&sockaddr, &ina, 0); + } else { + INSIST(rdata.length == 16); + memcpy(in6a.s6_addr, rdata.data, 16); + isc_sockaddr_fromin6(&sockaddr, &in6a, 0); + } + + INSIST(nh == NULL); + nh = new_adbnamehook(adb, NULL); + if (nh == NULL) { + adbname->partial_result |= findoptions; + result = ISC_R_NOMEMORY; + goto fail; + } + + foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket); + if (foundentry == NULL) { + dns_adbentry_t *entry; + + entry = new_adbentry(adb); + if (entry == NULL) { + adbname->partial_result |= findoptions; + result = ISC_R_NOMEMORY; + goto fail; + } + + entry->sockaddr = sockaddr; + entry->refcnt = 1; + + nh->entry = entry; + + link_entry(adb, addr_bucket, entry); + } else { + for (anh = ISC_LIST_HEAD(adbname->v4); + anh != NULL; + anh = ISC_LIST_NEXT(anh, plink)) + if (anh->entry == foundentry) + break; + if (anh == NULL) { + foundentry->refcnt++; + nh->entry = foundentry; + } else + free_adbnamehook(adb, &nh); + } + + new_addresses_added = ISC_TRUE; + if (nh != NULL) { + if (rdtype == dns_rdatatype_a) + ISC_LIST_APPEND(adbname->v4, nh, plink); + else + ISC_LIST_APPEND(adbname->v6, nh, plink); + } + nh = NULL; + result = dns_rdataset_next(rdataset); + } fail: - if (nh != NULL) - free_adbnamehook(adb, &nh); - - if (addr_bucket != DNS_ADB_INVALIDBUCKET) - UNLOCK(&adb->entrylocks[addr_bucket]); - - if (rdataset->trust == dns_trust_glue || - rdataset->trust == dns_trust_additional) - rdataset->ttl = ADB_CACHE_MINIMUM; - else - rdataset->ttl = ttlclamp(rdataset->ttl); - - if (rdtype == dns_rdatatype_a) { - DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset", - adbname->expire_v4, now + rdataset->ttl); - adbname->expire_v4 = ISC_MIN(adbname->expire_v4, - now + rdataset->ttl); - } else { - DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", - adbname->expire_v6, now + rdataset->ttl); - adbname->expire_v6 = ISC_MIN(adbname->expire_v6, - now + rdataset->ttl); - } - - if (new_addresses_added) { - /* - * Lie a little here. This is more or less so code that cares - * can find out if any new information was added or not. - */ - return (ISC_R_SUCCESS); - } - - return (result); + if (nh != NULL) + free_adbnamehook(adb, &nh); + + if (addr_bucket != DNS_ADB_INVALIDBUCKET) + UNLOCK(&adb->entrylocks[addr_bucket]); + + if (rdataset->trust == dns_trust_glue || + rdataset->trust == dns_trust_additional) + rdataset->ttl = ADB_CACHE_MINIMUM; + else + rdataset->ttl = ttlclamp(rdataset->ttl); + + if (rdtype == dns_rdatatype_a) { + DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset", + adbname->expire_v4, now + rdataset->ttl); + adbname->expire_v4 = ISC_MIN(adbname->expire_v4, + now + rdataset->ttl); + } else { + DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", + adbname->expire_v6, now + rdataset->ttl); + adbname->expire_v6 = ISC_MIN(adbname->expire_v6, + now + rdataset->ttl); + } + + if (new_addresses_added) { + /* + * Lie a little here. This is more or less so code that cares + * can find out if any new information was added or not. + */ + return (ISC_R_SUCCESS); + } + + return (result); } /* * Requires the name's bucket be locked. */ static isc_boolean_t -kill_name(dns_adbname_t **n, isc_eventtype_t ev) { - dns_adbname_t *name; - isc_boolean_t result = ISC_FALSE; - isc_boolean_t result4, result6; - dns_adb_t *adb; - - INSIST(n != NULL); - name = *n; - *n = NULL; - INSIST(DNS_ADBNAME_VALID(name)); - adb = name->adb; - INSIST(DNS_ADB_VALID(adb)); - - DP(DEF_LEVEL, "killing name %p", name); - - /* - * If we're dead already, just check to see if we should go - * away now or not. - */ - if (NAME_DEAD(name) && !NAME_FETCH(name)) { - result = unlink_name(adb, name); - free_adbname(adb, &name); - if (result) - result = dec_adb_irefcnt(adb); - return (result); - } - - /* - * Clean up the name's various lists. These two are destructive - * in that they will always empty the list. - */ - clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK); - result4 = clean_namehooks(adb, &name->v4); - result6 = clean_namehooks(adb, &name->v6); - clean_target(adb, &name->target); - result = ISC_TF(result4 || result6); - - /* - * If fetches are running, cancel them. If none are running, we can - * just kill the name here. - */ - if (!NAME_FETCH(name)) { - INSIST(result == ISC_FALSE); - result = unlink_name(adb, name); - free_adbname(adb, &name); - if (result) - result = dec_adb_irefcnt(adb); - } else { - name->flags |= NAME_IS_DEAD; - cancel_fetches_at_name(name); - } - return (result); +kill_name(dns_adbname_t **n, isc_eventtype_t ev, isc_boolean_t is_purge) { + dns_adbname_t *name; + isc_boolean_t result = ISC_FALSE; + isc_boolean_t result4, result6; + dns_adb_t *adb; + + INSIST(n != NULL); + name = *n; + *n = NULL; + INSIST(DNS_ADBNAME_VALID(name)); + adb = name->adb; + INSIST(DNS_ADB_VALID(adb)); + + DP(DEF_LEVEL, "killing name %p", name); + + /* + * If we're dead already, just check to see if we should go + * away now or not. + */ + if (NAME_DEAD(name) && !NAME_FETCH(name)) { + result = unlink_name(adb, name); + free_adbname(adb, &name); + if (result) + result = dec_adb_irefcnt(adb); + return (result); + } + + /* + * Clean up the name's various lists. These two are destructive + * in that they will always empty the list. + */ + clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK); + result4 = clean_namehooks(adb, &name->v4, is_purge); + result6 = clean_namehooks(adb, &name->v6, is_purge); + clean_target(adb, &name->target); + result = ISC_TF(result4 || result6); + + /* + * If fetches are running, cancel them. If none are running, we can + * just kill the name here. + */ + if (!NAME_FETCH(name)) { + INSIST(result == ISC_FALSE); + result = unlink_name(adb, name); + free_adbname(adb, &name); + if (result) + result = dec_adb_irefcnt(adb); + } else { + name->flags |= NAME_IS_DEAD; + cancel_fetches_at_name(name); + } + return (result); } /* * Requires the name's bucket be locked and no entry buckets be locked. */ static isc_boolean_t -check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now, - isc_boolean_t overmem) -{ - dns_adb_t *adb; - isc_boolean_t expire; - isc_boolean_t result4 = ISC_FALSE; - isc_boolean_t result6 = ISC_FALSE; - - INSIST(DNS_ADBNAME_VALID(name)); - adb = name->adb; - INSIST(DNS_ADB_VALID(adb)); - - if (overmem) { - isc_uint32_t val; - - isc_random_get(&val); - - expire = ISC_TF((val % 4) == 0); - } else - expire = ISC_FALSE; - - /* - * Check to see if we need to remove the v4 addresses - */ - if (!NAME_FETCH_V4(name) && - (expire || EXPIRE_OK(name->expire_v4, now))) { - if (NAME_HAS_V4(name)) { - DP(DEF_LEVEL, "expiring v4 for name %p", name); - result4 = clean_namehooks(adb, &name->v4); - name->partial_result &= ~DNS_ADBFIND_INET; - } - name->expire_v4 = INT_MAX; - name->fetch_err = FIND_ERR_UNEXPECTED; - } - - /* - * Check to see if we need to remove the v6 addresses - */ - if (!NAME_FETCH_V6(name) && - (expire || EXPIRE_OK(name->expire_v6, now))) { - if (NAME_HAS_V6(name)) { - DP(DEF_LEVEL, "expiring v6 for name %p", name); - result6 = clean_namehooks(adb, &name->v6); - name->partial_result &= ~DNS_ADBFIND_INET6; - } - name->expire_v6 = INT_MAX; - name->fetch6_err = FIND_ERR_UNEXPECTED; - } - - /* - * Check to see if we need to remove the alias target. - */ - if (expire || EXPIRE_OK(name->expire_target, now)) { - clean_target(adb, &name->target); - name->expire_target = INT_MAX; - } - return (ISC_TF(result4 || result6)); +check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) { + dns_adb_t *adb; + isc_boolean_t result4 = ISC_FALSE; + isc_boolean_t result6 = ISC_FALSE; + + INSIST(DNS_ADBNAME_VALID(name)); + adb = name->adb; + INSIST(DNS_ADB_VALID(adb)); + + /* + * Check to see if we need to remove the v4 addresses + */ + if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) { + if (NAME_HAS_V4(name)) { + DP(DEF_LEVEL, "expiring v4 for name %p", name); + result4 = clean_namehooks(adb, &name->v4, ISC_FALSE); + name->partial_result &= ~DNS_ADBFIND_INET; + } + name->expire_v4 = INT_MAX; + name->fetch_err = FIND_ERR_UNEXPECTED; + } + + /* + * Check to see if we need to remove the v6 addresses + */ + if (!NAME_FETCH_V6(name) && EXPIRE_OK(name->expire_v6, now)) { + if (NAME_HAS_V6(name)) { + DP(DEF_LEVEL, "expiring v6 for name %p", name); + result6 = clean_namehooks(adb, &name->v6, ISC_FALSE); + name->partial_result &= ~DNS_ADBFIND_INET6; + } + name->expire_v6 = INT_MAX; + name->fetch6_err = FIND_ERR_UNEXPECTED; + } + + /* + * Check to see if we need to remove the alias target. + */ + if (EXPIRE_OK(name->expire_target, now)) { + clean_target(adb, &name->target); + name->expire_target = INT_MAX; + } + return (ISC_TF(result4 || result6)); } /* @@ -735,11 +756,11 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now, */ static inline void link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) { - INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET); + INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET); - ISC_LIST_PREPEND(adb->names[bucket], name, plink); - name->lock_bucket = bucket; - adb->name_refcnt[bucket]++; + ISC_LIST_PREPEND(adb->names[bucket], name, plink); + name->lock_bucket = bucket; + adb->name_refcnt[bucket]++; } /* @@ -747,19 +768,19 @@ link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) { */ static inline isc_boolean_t unlink_name(dns_adb_t *adb, dns_adbname_t *name) { - int bucket; - isc_boolean_t result = ISC_FALSE; + int bucket; + isc_boolean_t result = ISC_FALSE; - bucket = name->lock_bucket; - INSIST(bucket != DNS_ADB_INVALIDBUCKET); + bucket = name->lock_bucket; + INSIST(bucket != DNS_ADB_INVALIDBUCKET); - ISC_LIST_UNLINK(adb->names[bucket], name, plink); - name->lock_bucket = DNS_ADB_INVALIDBUCKET; - INSIST(adb->name_refcnt[bucket] > 0); - adb->name_refcnt[bucket]--; - if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0) - result = ISC_TRUE; - return (result); + ISC_LIST_UNLINK(adb->names[bucket], name, plink); + name->lock_bucket = DNS_ADB_INVALIDBUCKET; + INSIST(adb->name_refcnt[bucket] > 0); + adb->name_refcnt[bucket]--; + if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0) + result = ISC_TRUE; + return (result); } /* @@ -767,9 +788,9 @@ unlink_name(dns_adb_t *adb, dns_adbname_t *name) { */ static inline void link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) { - ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); - entry->lock_bucket = bucket; - adb->entry_refcnt[bucket]++; + ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); + entry->lock_bucket = bucket; + adb->entry_refcnt[bucket]++; } /* @@ -777,28 +798,28 @@ link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) { */ static inline isc_boolean_t unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) { - int bucket; - isc_boolean_t result = ISC_FALSE; + int bucket; + isc_boolean_t result = ISC_FALSE; - bucket = entry->lock_bucket; - INSIST(bucket != DNS_ADB_INVALIDBUCKET); + bucket = entry->lock_bucket; + INSIST(bucket != DNS_ADB_INVALIDBUCKET); - ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); - entry->lock_bucket = DNS_ADB_INVALIDBUCKET; - INSIST(adb->entry_refcnt[bucket] > 0); - adb->entry_refcnt[bucket]--; - if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0) - result = ISC_TRUE; - return (result); + ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); + entry->lock_bucket = DNS_ADB_INVALIDBUCKET; + INSIST(adb->entry_refcnt[bucket] > 0); + adb->entry_refcnt[bucket]--; + if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0) + result = ISC_TRUE; + return (result); } static inline void violate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) { - if (isc_mutex_trylock(want) != ISC_R_SUCCESS) { - UNLOCK(have); - LOCK(want); - LOCK(have); - } + if (isc_mutex_trylock(want) != ISC_R_SUCCESS) { + UNLOCK(have); + LOCK(want); + LOCK(have); + } } /* @@ -807,43 +828,44 @@ violate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) { */ static isc_boolean_t shutdown_names(dns_adb_t *adb) { - int bucket; - isc_boolean_t result = ISC_FALSE; - dns_adbname_t *name; - dns_adbname_t *next_name; - - for (bucket = 0; bucket < NBUCKETS; bucket++) { - LOCK(&adb->namelocks[bucket]); - adb->name_sd[bucket] = ISC_TRUE; - - name = ISC_LIST_HEAD(adb->names[bucket]); - if (name == NULL) { - /* - * This bucket has no names. We must decrement the - * irefcnt ourselves, since it will not be - * automatically triggered by a name being unlinked. - */ - INSIST(result == ISC_FALSE); - result = dec_adb_irefcnt(adb); - } else { - /* - * Run through the list. For each name, clean up finds - * found there, and cancel any fetches running. When - * all the fetches are canceled, the name will destroy - * itself. - */ - while (name != NULL) { - next_name = ISC_LIST_NEXT(name, plink); - INSIST(result == ISC_FALSE); - result = kill_name(&name, - DNS_EVENT_ADBSHUTDOWN); - name = next_name; - } - } - - UNLOCK(&adb->namelocks[bucket]); - } - return (result); + int bucket; + isc_boolean_t result = ISC_FALSE; + dns_adbname_t *name; + dns_adbname_t *next_name; + + for (bucket = 0; bucket < NBUCKETS; bucket++) { + LOCK(&adb->namelocks[bucket]); + adb->name_sd[bucket] = ISC_TRUE; + + name = ISC_LIST_HEAD(adb->names[bucket]); + if (name == NULL) { + /* + * This bucket has no names. We must decrement the + * irefcnt ourselves, since it will not be + * automatically triggered by a name being unlinked. + */ + INSIST(result == ISC_FALSE); + result = dec_adb_irefcnt(adb); + } else { + /* + * Run through the list. For each name, clean up finds + * found there, and cancel any fetches running. When + * all the fetches are canceled, the name will destroy + * itself. + */ + while (name != NULL) { + next_name = ISC_LIST_NEXT(name, plink); + INSIST(result == ISC_FALSE); + result = kill_name(&name, + DNS_EVENT_ADBSHUTDOWN, + ISC_FALSE); + name = next_name; + } + } + + UNLOCK(&adb->namelocks[bucket]); + } + return (result); } /* @@ -852,44 +874,44 @@ shutdown_names(dns_adb_t *adb) { */ static isc_boolean_t shutdown_entries(dns_adb_t *adb) { - int bucket; - isc_boolean_t result = ISC_FALSE; - dns_adbentry_t *entry; - dns_adbentry_t *next_entry; - - for (bucket = 0; bucket < NBUCKETS; bucket++) { - LOCK(&adb->entrylocks[bucket]); - adb->entry_sd[bucket] = ISC_TRUE; - - entry = ISC_LIST_HEAD(adb->entries[bucket]); - if (entry == NULL) { - /* - * This bucket has no entries. We must decrement the - * irefcnt ourselves, since it will not be - * automatically triggered by an entry being unlinked. - */ - result = dec_adb_irefcnt(adb); - } else { - /* - * Run through the list. Cleanup any entries not - * associated with names, and which are not in use. - */ - while (entry != NULL) { - next_entry = ISC_LIST_NEXT(entry, plink); - if (entry->refcnt == 0 && - entry->expires != 0) { - result = unlink_entry(adb, entry); - free_adbentry(adb, &entry); - if (result) - result = dec_adb_irefcnt(adb); - } - entry = next_entry; - } - } - - UNLOCK(&adb->entrylocks[bucket]); - } - return (result); + int bucket; + isc_boolean_t result = ISC_FALSE; + dns_adbentry_t *entry; + dns_adbentry_t *next_entry; + + for (bucket = 0; bucket < NBUCKETS; bucket++) { + LOCK(&adb->entrylocks[bucket]); + adb->entry_sd[bucket] = ISC_TRUE; + + entry = ISC_LIST_HEAD(adb->entries[bucket]); + if (entry == NULL) { + /* + * This bucket has no entries. We must decrement the + * irefcnt ourselves, since it will not be + * automatically triggered by an entry being unlinked. + */ + result = dec_adb_irefcnt(adb); + } else { + /* + * Run through the list. Cleanup any entries not + * associated with names, and which are not in use. + */ + while (entry != NULL) { + next_entry = ISC_LIST_NEXT(entry, plink); + if (entry->refcnt == 0 && + entry->expires != 0) { + result = unlink_entry(adb, entry); + free_adbentry(adb, &entry); + if (result) + result = dec_adb_irefcnt(adb); + } + entry = next_entry; + } + } + + UNLOCK(&adb->entrylocks[bucket]); + } + return (result); } /* @@ -897,134 +919,143 @@ shutdown_entries(dns_adb_t *adb) { */ static void cancel_fetches_at_name(dns_adbname_t *name) { - if (NAME_FETCH_A(name)) - dns_resolver_cancelfetch(name->fetch_a->fetch); + if (NAME_FETCH_A(name)) + dns_resolver_cancelfetch(name->fetch_a->fetch); - if (NAME_FETCH_AAAA(name)) - dns_resolver_cancelfetch(name->fetch_aaaa->fetch); + if (NAME_FETCH_AAAA(name)) + dns_resolver_cancelfetch(name->fetch_aaaa->fetch); } /* * Assumes the name bucket is locked. */ static isc_boolean_t -clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { - dns_adbentry_t *entry; - dns_adbnamehook_t *namehook; - int addr_bucket; - isc_boolean_t result = ISC_FALSE; - - addr_bucket = DNS_ADB_INVALIDBUCKET; - namehook = ISC_LIST_HEAD(*namehooks); - while (namehook != NULL) { - INSIST(DNS_ADBNAMEHOOK_VALID(namehook)); - - /* - * Clean up the entry if needed. - */ - entry = namehook->entry; - if (entry != NULL) { - INSIST(DNS_ADBENTRY_VALID(entry)); - - if (addr_bucket != entry->lock_bucket) { - if (addr_bucket != DNS_ADB_INVALIDBUCKET) - UNLOCK(&adb->entrylocks[addr_bucket]); - addr_bucket = entry->lock_bucket; - LOCK(&adb->entrylocks[addr_bucket]); - } - - result = dec_entry_refcnt(adb, entry, ISC_FALSE); - } - - /* - * Free the namehook - */ - namehook->entry = NULL; - ISC_LIST_UNLINK(*namehooks, namehook, plink); - free_adbnamehook(adb, &namehook); - - namehook = ISC_LIST_HEAD(*namehooks); - } - - if (addr_bucket != DNS_ADB_INVALIDBUCKET) - UNLOCK(&adb->entrylocks[addr_bucket]); - return (result); +clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks, + isc_boolean_t is_purge) +{ + dns_adbentry_t *entry; + dns_adbnamehook_t *namehook; + int addr_bucket; + isc_boolean_t result = ISC_FALSE; + + addr_bucket = DNS_ADB_INVALIDBUCKET; + namehook = ISC_LIST_HEAD(*namehooks); + while (namehook != NULL) { + INSIST(DNS_ADBNAMEHOOK_VALID(namehook)); + + /* + * Clean up the entry if needed. + */ + entry = namehook->entry; + if (entry != NULL) { + INSIST(DNS_ADBENTRY_VALID(entry)); + + if (addr_bucket != entry->lock_bucket) { + if (addr_bucket != DNS_ADB_INVALIDBUCKET) + UNLOCK(&adb->entrylocks[addr_bucket]); + addr_bucket = entry->lock_bucket; + LOCK(&adb->entrylocks[addr_bucket]); + } + + /* + * If we are in an overmem situation, force expiration + * so that # of names and # of entries are well + * balanced. + */ + if (is_purge) + entry->expires = 0; + result = dec_entry_refcnt(adb, entry, ISC_FALSE); + } + + /* + * Free the namehook + */ + namehook->entry = NULL; + ISC_LIST_UNLINK(*namehooks, namehook, plink); + free_adbnamehook(adb, &namehook); + + namehook = ISC_LIST_HEAD(*namehooks); + } + + if (addr_bucket != DNS_ADB_INVALIDBUCKET) + UNLOCK(&adb->entrylocks[addr_bucket]); + return (result); } static void clean_target(dns_adb_t *adb, dns_name_t *target) { - if (dns_name_countlabels(target) > 0) { - dns_name_free(target, adb->mctx); - dns_name_init(target, NULL); - } + if (dns_name_countlabels(target) > 0) { + dns_name_free(target, adb->mctx); + dns_name_init(target, NULL); + } } static isc_result_t set_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname, - dns_rdataset_t *rdataset, dns_name_t *target) + dns_rdataset_t *rdataset, dns_name_t *target) { - isc_result_t result; - dns_namereln_t namereln; - unsigned int nlabels; - int order; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_fixedname_t fixed1, fixed2; - dns_name_t *prefix, *new_target; - - REQUIRE(dns_name_countlabels(target) == 0); - - if (rdataset->type == dns_rdatatype_cname) { - dns_rdata_cname_t cname; - - /* - * Copy the CNAME's target into the target name. - */ - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &rdata); - result = dns_rdata_tostruct(&rdata, &cname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - result = dns_name_dup(&cname.cname, adb->mctx, target); - dns_rdata_freestruct(&cname); - if (result != ISC_R_SUCCESS) - return (result); - } else { - dns_rdata_dname_t dname; - - INSIST(rdataset->type == dns_rdatatype_dname); - namereln = dns_name_fullcompare(name, fname, &order, &nlabels); - INSIST(namereln == dns_namereln_subdomain); - /* - * Get the target name of the DNAME. - */ - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &rdata); - result = dns_rdata_tostruct(&rdata, &dname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - /* - * Construct the new target name. - */ - dns_fixedname_init(&fixed1); - prefix = dns_fixedname_name(&fixed1); - dns_fixedname_init(&fixed2); - new_target = dns_fixedname_name(&fixed2); - dns_name_split(name, nlabels, prefix, NULL); - result = dns_name_concatenate(prefix, &dname.dname, new_target, - NULL); - dns_rdata_freestruct(&dname); - if (result != ISC_R_SUCCESS) - return (result); - result = dns_name_dup(new_target, adb->mctx, target); - if (result != ISC_R_SUCCESS) - return (result); - } - - return (ISC_R_SUCCESS); + isc_result_t result; + dns_namereln_t namereln; + unsigned int nlabels; + int order; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_fixedname_t fixed1, fixed2; + dns_name_t *prefix, *new_target; + + REQUIRE(dns_name_countlabels(target) == 0); + + if (rdataset->type == dns_rdatatype_cname) { + dns_rdata_cname_t cname; + + /* + * Copy the CNAME's target into the target name. + */ + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) + return (result); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, NULL); + if (result != ISC_R_SUCCESS) + return (result); + result = dns_name_dup(&cname.cname, adb->mctx, target); + dns_rdata_freestruct(&cname); + if (result != ISC_R_SUCCESS) + return (result); + } else { + dns_rdata_dname_t dname; + + INSIST(rdataset->type == dns_rdatatype_dname); + namereln = dns_name_fullcompare(name, fname, &order, &nlabels); + INSIST(namereln == dns_namereln_subdomain); + /* + * Get the target name of the DNAME. + */ + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) + return (result); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &dname, NULL); + if (result != ISC_R_SUCCESS) + return (result); + /* + * Construct the new target name. + */ + dns_fixedname_init(&fixed1); + prefix = dns_fixedname_name(&fixed1); + dns_fixedname_init(&fixed2); + new_target = dns_fixedname_name(&fixed2); + dns_name_split(name, nlabels, prefix, NULL); + result = dns_name_concatenate(prefix, &dname.dname, new_target, + NULL); + dns_rdata_freestruct(&dname); + if (result != ISC_R_SUCCESS) + return (result); + result = dns_name_dup(new_target, adb->mctx, target); + if (result != ISC_R_SUCCESS) + return (result); + } + + return (ISC_R_SUCCESS); } /* @@ -1032,16 +1063,16 @@ set_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname, */ static void event_free(isc_event_t *event) { - dns_adbfind_t *find; + dns_adbfind_t *find; - INSIST(event != NULL); - find = event->ev_destroy_arg; - INSIST(DNS_ADBFIND_VALID(find)); + INSIST(event != NULL); + find = event->ev_destroy_arg; + INSIST(DNS_ADBFIND_VALID(find)); - LOCK(&find->lock); - find->flags |= FIND_EVENT_FREED; - event->ev_destroy_arg = NULL; - UNLOCK(&find->lock); + LOCK(&find->lock); + find->flags |= FIND_EVENT_FREED; + event->ev_destroy_arg = NULL; + UNLOCK(&find->lock); } /* @@ -1049,487 +1080,502 @@ event_free(isc_event_t *event) { */ static void clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, - unsigned int addrs) + unsigned int addrs) { - isc_event_t *ev; - isc_task_t *task; - dns_adbfind_t *find; - dns_adbfind_t *next_find; - isc_boolean_t process; - unsigned int wanted, notify; - - DP(ENTER_LEVEL, - "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x", - name, evtype, addrs); - - find = ISC_LIST_HEAD(name->finds); - while (find != NULL) { - LOCK(&find->lock); - next_find = ISC_LIST_NEXT(find, plink); - - process = ISC_FALSE; - wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; - notify = wanted & addrs; - - switch (evtype) { - case DNS_EVENT_ADBMOREADDRESSES: - DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES"); - if ((notify) != 0) { - find->flags &= ~addrs; - process = ISC_TRUE; - } - break; - case DNS_EVENT_ADBNOMOREADDRESSES: - DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES"); - find->flags &= ~addrs; - wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; - if (wanted == 0) - process = ISC_TRUE; - break; - default: - find->flags &= ~addrs; - process = ISC_TRUE; - } - - if (process) { - DP(DEF_LEVEL, "cfan: processing find %p", find); - /* - * Unlink the find from the name, letting the caller - * call dns_adb_destroyfind() on it to clean it up - * later. - */ - ISC_LIST_UNLINK(name->finds, find, plink); - find->adbname = NULL; - find->name_bucket = DNS_ADB_INVALIDBUCKET; - - INSIST(!FIND_EVENTSENT(find)); - - ev = &find->event; - task = ev->ev_sender; - ev->ev_sender = find; - find->result_v4 = find_err_map[name->fetch_err]; - find->result_v6 = find_err_map[name->fetch6_err]; - ev->ev_type = evtype; - ev->ev_destroy = event_free; - ev->ev_destroy_arg = find; - - DP(DEF_LEVEL, - "sending event %p to task %p for find %p", - ev, task, find); - - isc_task_sendanddetach(&task, (isc_event_t **)&ev); - } else { - DP(DEF_LEVEL, "cfan: skipping find %p", find); - } - - UNLOCK(&find->lock); - find = next_find; - } - - DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name); + isc_event_t *ev; + isc_task_t *task; + dns_adbfind_t *find; + dns_adbfind_t *next_find; + isc_boolean_t process; + unsigned int wanted, notify; + + DP(ENTER_LEVEL, + "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x", + name, evtype, addrs); + + find = ISC_LIST_HEAD(name->finds); + while (find != NULL) { + LOCK(&find->lock); + next_find = ISC_LIST_NEXT(find, plink); + + process = ISC_FALSE; + wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; + notify = wanted & addrs; + + switch (evtype) { + case DNS_EVENT_ADBMOREADDRESSES: + DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES"); + if ((notify) != 0) { + find->flags &= ~addrs; + process = ISC_TRUE; + } + break; + case DNS_EVENT_ADBNOMOREADDRESSES: + DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES"); + find->flags &= ~addrs; + wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; + if (wanted == 0) + process = ISC_TRUE; + break; + default: + find->flags &= ~addrs; + process = ISC_TRUE; + } + + if (process) { + DP(DEF_LEVEL, "cfan: processing find %p", find); + /* + * Unlink the find from the name, letting the caller + * call dns_adb_destroyfind() on it to clean it up + * later. + */ + ISC_LIST_UNLINK(name->finds, find, plink); + find->adbname = NULL; + find->name_bucket = DNS_ADB_INVALIDBUCKET; + + INSIST(!FIND_EVENTSENT(find)); + + ev = &find->event; + task = ev->ev_sender; + ev->ev_sender = find; + find->result_v4 = find_err_map[name->fetch_err]; + find->result_v6 = find_err_map[name->fetch6_err]; + ev->ev_type = evtype; + ev->ev_destroy = event_free; + ev->ev_destroy_arg = find; + + DP(DEF_LEVEL, + "sending event %p to task %p for find %p", + ev, task, find); + + isc_task_sendanddetach(&task, (isc_event_t **)&ev); + } else { + DP(DEF_LEVEL, "cfan: skipping find %p", find); + } + + UNLOCK(&find->lock); + find = next_find; + } + + DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name); } static inline void check_exit(dns_adb_t *adb) { - isc_event_t *event; - /* - * The caller must be holding the adb lock. - */ - if (adb->shutting_down) { - /* - * If there aren't any external references either, we're - * done. Send the control event to initiate shutdown. - */ - INSIST(!adb->cevent_sent); /* Sanity check. */ - event = &adb->cevent; - isc_task_send(adb->task, &event); - adb->cevent_sent = ISC_TRUE; - } + isc_event_t *event; + /* + * The caller must be holding the adb lock. + */ + if (adb->shutting_down) { + /* + * If there aren't any external references either, we're + * done. Send the control event to initiate shutdown. + */ + INSIST(!adb->cevent_sent); /* Sanity check. */ + event = &adb->cevent; + isc_task_send(adb->task, &event); + adb->cevent_sent = ISC_TRUE; + } } static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *adb) { - isc_event_t *event; - isc_task_t *etask; - isc_boolean_t result = ISC_FALSE; + isc_event_t *event; + isc_task_t *etask; + isc_boolean_t result = ISC_FALSE; - LOCK(&adb->reflock); + LOCK(&adb->reflock); - INSIST(adb->irefcnt > 0); - adb->irefcnt--; + INSIST(adb->irefcnt > 0); + adb->irefcnt--; - if (adb->irefcnt == 0) { - event = ISC_LIST_HEAD(adb->whenshutdown); - while (event != NULL) { - ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link); - etask = event->ev_sender; - event->ev_sender = adb; - isc_task_sendanddetach(&etask, &event); - event = ISC_LIST_HEAD(adb->whenshutdown); - } - } + if (adb->irefcnt == 0) { + event = ISC_LIST_HEAD(adb->whenshutdown); + while (event != NULL) { + ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link); + etask = event->ev_sender; + event->ev_sender = adb; + isc_task_sendanddetach(&etask, &event); + event = ISC_LIST_HEAD(adb->whenshutdown); + } + } - if (adb->irefcnt == 0 && adb->erefcnt == 0) - result = ISC_TRUE; - UNLOCK(&adb->reflock); - return (result); + if (adb->irefcnt == 0 && adb->erefcnt == 0) + result = ISC_TRUE; + UNLOCK(&adb->reflock); + return (result); } static inline void inc_adb_irefcnt(dns_adb_t *adb) { - LOCK(&adb->reflock); - adb->irefcnt++; - UNLOCK(&adb->reflock); + LOCK(&adb->reflock); + adb->irefcnt++; + UNLOCK(&adb->reflock); } static inline void inc_adb_erefcnt(dns_adb_t *adb) { - LOCK(&adb->reflock); - adb->erefcnt++; - UNLOCK(&adb->reflock); + LOCK(&adb->reflock); + adb->erefcnt++; + UNLOCK(&adb->reflock); } static inline void inc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { - int bucket; + int bucket; - bucket = entry->lock_bucket; + bucket = entry->lock_bucket; - if (lock) - LOCK(&adb->entrylocks[bucket]); + if (lock) + LOCK(&adb->entrylocks[bucket]); - entry->refcnt++; + entry->refcnt++; - if (lock) - UNLOCK(&adb->entrylocks[bucket]); + if (lock) + UNLOCK(&adb->entrylocks[bucket]); } static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { - int bucket; - isc_boolean_t destroy_entry; - isc_boolean_t result = ISC_FALSE; + int bucket; + isc_boolean_t destroy_entry; + isc_boolean_t result = ISC_FALSE; - bucket = entry->lock_bucket; + bucket = entry->lock_bucket; - if (lock) - LOCK(&adb->entrylocks[bucket]); + if (lock) + LOCK(&adb->entrylocks[bucket]); - INSIST(entry->refcnt > 0); - entry->refcnt--; + INSIST(entry->refcnt > 0); + entry->refcnt--; - destroy_entry = ISC_FALSE; - if (entry->refcnt == 0 && - (adb->entry_sd[bucket] || entry->expires == 0)) { - destroy_entry = ISC_TRUE; - result = unlink_entry(adb, entry); - } + destroy_entry = ISC_FALSE; + if (entry->refcnt == 0 && + (adb->entry_sd[bucket] || entry->expires == 0)) { + destroy_entry = ISC_TRUE; + result = unlink_entry(adb, entry); + } - if (lock) - UNLOCK(&adb->entrylocks[bucket]); + if (lock) + UNLOCK(&adb->entrylocks[bucket]); - if (!destroy_entry) - return (result); + if (!destroy_entry) + return (result); - entry->lock_bucket = DNS_ADB_INVALIDBUCKET; + entry->lock_bucket = DNS_ADB_INVALIDBUCKET; - free_adbentry(adb, &entry); - if (result) - result =dec_adb_irefcnt(adb); + free_adbentry(adb, &entry); + if (result) + result =dec_adb_irefcnt(adb); - return (result); + return (result); } static inline dns_adbname_t * new_adbname(dns_adb_t *adb, dns_name_t *dnsname) { - dns_adbname_t *name; - - name = isc_mempool_get(adb->nmp); - if (name == NULL) - return (NULL); - - dns_name_init(&name->name, NULL); - if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) { - isc_mempool_put(adb->nmp, name); - return (NULL); - } - dns_name_init(&name->target, NULL); - name->magic = DNS_ADBNAME_MAGIC; - name->adb = adb; - name->partial_result = 0; - name->flags = 0; - name->expire_v4 = INT_MAX; - name->expire_v6 = INT_MAX; - name->expire_target = INT_MAX; - name->chains = 0; - name->lock_bucket = DNS_ADB_INVALIDBUCKET; - ISC_LIST_INIT(name->v4); - ISC_LIST_INIT(name->v6); - name->fetch_a = NULL; - name->fetch_aaaa = NULL; - name->fetch_err = FIND_ERR_UNEXPECTED; - name->fetch6_err = FIND_ERR_UNEXPECTED; - ISC_LIST_INIT(name->finds); - ISC_LINK_INIT(name, plink); - - return (name); + dns_adbname_t *name; + + name = isc_mempool_get(adb->nmp); + if (name == NULL) + return (NULL); + + dns_name_init(&name->name, NULL); + if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) { + isc_mempool_put(adb->nmp, name); + return (NULL); + } + dns_name_init(&name->target, NULL); + name->magic = DNS_ADBNAME_MAGIC; + name->adb = adb; + name->partial_result = 0; + name->flags = 0; + name->expire_v4 = INT_MAX; + name->expire_v6 = INT_MAX; + name->expire_target = INT_MAX; + name->chains = 0; + name->lock_bucket = DNS_ADB_INVALIDBUCKET; + ISC_LIST_INIT(name->v4); + ISC_LIST_INIT(name->v6); + name->fetch_a = NULL; + name->fetch_aaaa = NULL; + name->fetch_err = FIND_ERR_UNEXPECTED; + name->fetch6_err = FIND_ERR_UNEXPECTED; + ISC_LIST_INIT(name->finds); + ISC_LINK_INIT(name, plink); + + return (name); } static inline void free_adbname(dns_adb_t *adb, dns_adbname_t **name) { - dns_adbname_t *n; - - INSIST(name != NULL && DNS_ADBNAME_VALID(*name)); - n = *name; - *name = NULL; - - INSIST(!NAME_HAS_V4(n)); - INSIST(!NAME_HAS_V6(n)); - INSIST(!NAME_FETCH(n)); - INSIST(ISC_LIST_EMPTY(n->finds)); - INSIST(!ISC_LINK_LINKED(n, plink)); - INSIST(n->lock_bucket == DNS_ADB_INVALIDBUCKET); - INSIST(n->adb == adb); + dns_adbname_t *n; + + INSIST(name != NULL && DNS_ADBNAME_VALID(*name)); + n = *name; + *name = NULL; + + INSIST(!NAME_HAS_V4(n)); + INSIST(!NAME_HAS_V6(n)); + INSIST(!NAME_FETCH(n)); + INSIST(ISC_LIST_EMPTY(n->finds)); + INSIST(!ISC_LINK_LINKED(n, plink)); + INSIST(n->lock_bucket == DNS_ADB_INVALIDBUCKET); + INSIST(n->adb == adb); + +#ifdef LRU_DEBUG + adb->nname--; /* XXX: omit ADB lock for brevity */ + INSIST((int)adb->nname >= 0); +#endif - n->magic = 0; - dns_name_free(&n->name, adb->mctx); + n->magic = 0; + dns_name_free(&n->name, adb->mctx); - isc_mempool_put(adb->nmp, n); + isc_mempool_put(adb->nmp, n); } static inline dns_adbnamehook_t * new_adbnamehook(dns_adb_t *adb, dns_adbentry_t *entry) { - dns_adbnamehook_t *nh; + dns_adbnamehook_t *nh; - nh = isc_mempool_get(adb->nhmp); - if (nh == NULL) - return (NULL); + nh = isc_mempool_get(adb->nhmp); + if (nh == NULL) + return (NULL); - nh->magic = DNS_ADBNAMEHOOK_MAGIC; - nh->entry = entry; - ISC_LINK_INIT(nh, plink); + nh->magic = DNS_ADBNAMEHOOK_MAGIC; + nh->entry = entry; + ISC_LINK_INIT(nh, plink); - return (nh); + return (nh); } static inline void free_adbnamehook(dns_adb_t *adb, dns_adbnamehook_t **namehook) { - dns_adbnamehook_t *nh; + dns_adbnamehook_t *nh; - INSIST(namehook != NULL && DNS_ADBNAMEHOOK_VALID(*namehook)); - nh = *namehook; - *namehook = NULL; + INSIST(namehook != NULL && DNS_ADBNAMEHOOK_VALID(*namehook)); + nh = *namehook; + *namehook = NULL; - INSIST(nh->entry == NULL); - INSIST(!ISC_LINK_LINKED(nh, plink)); + INSIST(nh->entry == NULL); + INSIST(!ISC_LINK_LINKED(nh, plink)); - nh->magic = 0; - isc_mempool_put(adb->nhmp, nh); + nh->magic = 0; + isc_mempool_put(adb->nhmp, nh); } static inline dns_adblameinfo_t * new_adblameinfo(dns_adb_t *adb, dns_name_t *qname, dns_rdatatype_t qtype) { - dns_adblameinfo_t *li; + dns_adblameinfo_t *li; - li = isc_mempool_get(adb->limp); - if (li == NULL) - return (NULL); + li = isc_mempool_get(adb->limp); + if (li == NULL) + return (NULL); - dns_name_init(&li->qname, NULL); - if (dns_name_dup(qname, adb->mctx, &li->qname) != ISC_R_SUCCESS) { - isc_mempool_put(adb->limp, li); - return (NULL); - } - li->magic = DNS_ADBLAMEINFO_MAGIC; - li->lame_timer = 0; - li->qtype = qtype; - ISC_LINK_INIT(li, plink); + dns_name_init(&li->qname, NULL); + if (dns_name_dup(qname, adb->mctx, &li->qname) != ISC_R_SUCCESS) { + isc_mempool_put(adb->limp, li); + return (NULL); + } + li->magic = DNS_ADBLAMEINFO_MAGIC; + li->lame_timer = 0; + li->qtype = qtype; + ISC_LINK_INIT(li, plink); - return (li); + return (li); } static inline void free_adblameinfo(dns_adb_t *adb, dns_adblameinfo_t **lameinfo) { - dns_adblameinfo_t *li; + dns_adblameinfo_t *li; - INSIST(lameinfo != NULL && DNS_ADBLAMEINFO_VALID(*lameinfo)); - li = *lameinfo; - *lameinfo = NULL; + INSIST(lameinfo != NULL && DNS_ADBLAMEINFO_VALID(*lameinfo)); + li = *lameinfo; + *lameinfo = NULL; - INSIST(!ISC_LINK_LINKED(li, plink)); + INSIST(!ISC_LINK_LINKED(li, plink)); - dns_name_free(&li->qname, adb->mctx); + dns_name_free(&li->qname, adb->mctx); - li->magic = 0; + li->magic = 0; - isc_mempool_put(adb->limp, li); + isc_mempool_put(adb->limp, li); } static inline dns_adbentry_t * new_adbentry(dns_adb_t *adb) { - dns_adbentry_t *e; - isc_uint32_t r; - - e = isc_mempool_get(adb->emp); - if (e == NULL) - return (NULL); - - e->magic = DNS_ADBENTRY_MAGIC; - e->lock_bucket = DNS_ADB_INVALIDBUCKET; - e->refcnt = 0; - e->flags = 0; - isc_random_get(&r); - e->srtt = (r & 0x1f) + 1; - e->expires = 0; - ISC_LIST_INIT(e->lameinfo); - ISC_LINK_INIT(e, plink); + dns_adbentry_t *e; + isc_uint32_t r; + + e = isc_mempool_get(adb->emp); + if (e == NULL) + return (NULL); + + e->magic = DNS_ADBENTRY_MAGIC; + e->lock_bucket = DNS_ADB_INVALIDBUCKET; + e->refcnt = 0; + e->flags = 0; + isc_random_get(&r); + e->srtt = (r & 0x1f) + 1; + e->expires = 0; + ISC_LIST_INIT(e->lameinfo); + ISC_LINK_INIT(e, plink); + +#ifdef LRU_DEBUG + adb->nentry++; /* XXX: omit ADB lock for brevity */ + adb->nentry_total++; +#endif - return (e); + return (e); } static inline void free_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) { - dns_adbentry_t *e; - dns_adblameinfo_t *li; + dns_adbentry_t *e; + dns_adblameinfo_t *li; + + INSIST(entry != NULL && DNS_ADBENTRY_VALID(*entry)); + e = *entry; + *entry = NULL; - INSIST(entry != NULL && DNS_ADBENTRY_VALID(*entry)); - e = *entry; - *entry = NULL; + INSIST(e->lock_bucket == DNS_ADB_INVALIDBUCKET); + INSIST(e->refcnt == 0); + INSIST(!ISC_LINK_LINKED(e, plink)); - INSIST(e->lock_bucket == DNS_ADB_INVALIDBUCKET); - INSIST(e->refcnt == 0); - INSIST(!ISC_LINK_LINKED(e, plink)); + e->magic = 0; - e->magic = 0; + li = ISC_LIST_HEAD(e->lameinfo); + while (li != NULL) { + ISC_LIST_UNLINK(e->lameinfo, li, plink); + free_adblameinfo(adb, &li); + li = ISC_LIST_HEAD(e->lameinfo); + } - li = ISC_LIST_HEAD(e->lameinfo); - while (li != NULL) { - ISC_LIST_UNLINK(e->lameinfo, li, plink); - free_adblameinfo(adb, &li); - li = ISC_LIST_HEAD(e->lameinfo); - } +#ifdef LRU_DEBUG + adb->nentry--; /* XXX: omit ADB lock for brevity */ + INSIST((int)adb->nentry >= 0); +#endif - isc_mempool_put(adb->emp, e); + isc_mempool_put(adb->emp, e); } static inline dns_adbfind_t * new_adbfind(dns_adb_t *adb) { - dns_adbfind_t *h; - isc_result_t result; - - h = isc_mempool_get(adb->ahmp); - if (h == NULL) - return (NULL); - - /* - * Public members. - */ - h->magic = 0; - h->adb = adb; - h->partial_result = 0; - h->options = 0; - h->flags = 0; - h->result_v4 = ISC_R_UNEXPECTED; - h->result_v6 = ISC_R_UNEXPECTED; - ISC_LINK_INIT(h, publink); - ISC_LINK_INIT(h, plink); - ISC_LIST_INIT(h->list); - h->adbname = NULL; - h->name_bucket = DNS_ADB_INVALIDBUCKET; - - /* - * private members - */ - result = isc_mutex_init(&h->lock); - if (result != ISC_R_SUCCESS) { - isc_mempool_put(adb->ahmp, h); - return (NULL); - } - - ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL, - NULL, NULL, h); - - inc_adb_irefcnt(adb); - h->magic = DNS_ADBFIND_MAGIC; - return (h); + dns_adbfind_t *h; + isc_result_t result; + + h = isc_mempool_get(adb->ahmp); + if (h == NULL) + return (NULL); + + /* + * Public members. + */ + h->magic = 0; + h->adb = adb; + h->partial_result = 0; + h->options = 0; + h->flags = 0; + h->result_v4 = ISC_R_UNEXPECTED; + h->result_v6 = ISC_R_UNEXPECTED; + ISC_LINK_INIT(h, publink); + ISC_LINK_INIT(h, plink); + ISC_LIST_INIT(h->list); + h->adbname = NULL; + h->name_bucket = DNS_ADB_INVALIDBUCKET; + + /* + * private members + */ + result = isc_mutex_init(&h->lock); + if (result != ISC_R_SUCCESS) { + isc_mempool_put(adb->ahmp, h); + return (NULL); + } + + ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL, + NULL, NULL, h); + + inc_adb_irefcnt(adb); + h->magic = DNS_ADBFIND_MAGIC; + return (h); } static inline dns_adbfetch_t * new_adbfetch(dns_adb_t *adb) { - dns_adbfetch_t *f; + dns_adbfetch_t *f; - f = isc_mempool_get(adb->afmp); - if (f == NULL) - return (NULL); + f = isc_mempool_get(adb->afmp); + if (f == NULL) + return (NULL); - f->magic = 0; - f->namehook = NULL; - f->entry = NULL; - f->fetch = NULL; + f->magic = 0; + f->namehook = NULL; + f->entry = NULL; + f->fetch = NULL; - f->namehook = new_adbnamehook(adb, NULL); - if (f->namehook == NULL) - goto err; + f->namehook = new_adbnamehook(adb, NULL); + if (f->namehook == NULL) + goto err; - f->entry = new_adbentry(adb); - if (f->entry == NULL) - goto err; + f->entry = new_adbentry(adb); + if (f->entry == NULL) + goto err; - dns_rdataset_init(&f->rdataset); + dns_rdataset_init(&f->rdataset); - f->magic = DNS_ADBFETCH_MAGIC; + f->magic = DNS_ADBFETCH_MAGIC; - return (f); + return (f); err: - if (f->namehook != NULL) - free_adbnamehook(adb, &f->namehook); - if (f->entry != NULL) - free_adbentry(adb, &f->entry); - isc_mempool_put(adb->afmp, f); - return (NULL); + if (f->namehook != NULL) + free_adbnamehook(adb, &f->namehook); + if (f->entry != NULL) + free_adbentry(adb, &f->entry); + isc_mempool_put(adb->afmp, f); + return (NULL); } static inline void free_adbfetch(dns_adb_t *adb, dns_adbfetch_t **fetch) { - dns_adbfetch_t *f; + dns_adbfetch_t *f; - INSIST(fetch != NULL && DNS_ADBFETCH_VALID(*fetch)); - f = *fetch; - *fetch = NULL; + INSIST(fetch != NULL && DNS_ADBFETCH_VALID(*fetch)); + f = *fetch; + *fetch = NULL; - f->magic = 0; + f->magic = 0; - if (f->namehook != NULL) - free_adbnamehook(adb, &f->namehook); - if (f->entry != NULL) - free_adbentry(adb, &f->entry); + if (f->namehook != NULL) + free_adbnamehook(adb, &f->namehook); + if (f->entry != NULL) + free_adbentry(adb, &f->entry); - if (dns_rdataset_isassociated(&f->rdataset)) - dns_rdataset_disassociate(&f->rdataset); + if (dns_rdataset_isassociated(&f->rdataset)) + dns_rdataset_disassociate(&f->rdataset); - isc_mempool_put(adb->afmp, f); + isc_mempool_put(adb->afmp, f); } static inline isc_boolean_t free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) { - dns_adbfind_t *find; + dns_adbfind_t *find; - INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp)); - find = *findp; - *findp = NULL; + INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp)); + find = *findp; + *findp = NULL; - INSIST(!FIND_HAS_ADDRS(find)); - INSIST(!ISC_LINK_LINKED(find, publink)); - INSIST(!ISC_LINK_LINKED(find, plink)); - INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET); - INSIST(find->adbname == NULL); + INSIST(!FIND_HAS_ADDRS(find)); + INSIST(!ISC_LINK_LINKED(find, publink)); + INSIST(!ISC_LINK_LINKED(find, plink)); + INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET); + INSIST(find->adbname == NULL); - find->magic = 0; + find->magic = 0; - DESTROYLOCK(&find->lock); - isc_mempool_put(adb->ahmp, find); - return (dec_adb_irefcnt(adb)); + DESTROYLOCK(&find->lock); + isc_mempool_put(adb->ahmp, find); + return (dec_adb_irefcnt(adb)); } /* @@ -1539,37 +1585,41 @@ free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) { */ static inline dns_adbaddrinfo_t * new_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) { - dns_adbaddrinfo_t *ai; - - ai = isc_mempool_get(adb->aimp); - if (ai == NULL) - return (NULL); - - ai->magic = DNS_ADBADDRINFO_MAGIC; - ai->sockaddr = entry->sockaddr; - isc_sockaddr_setport(&ai->sockaddr, port); - ai->srtt = entry->srtt; - ai->flags = entry->flags; - ai->entry = entry; - ISC_LINK_INIT(ai, publink); + dns_adbaddrinfo_t *ai; + + ai = isc_mempool_get(adb->aimp); + if (ai == NULL) + return (NULL); + + ai->magic = DNS_ADBADDRINFO_MAGIC; + ai->sockaddr = entry->sockaddr; + isc_sockaddr_setport(&ai->sockaddr, port); + ai->srtt = entry->srtt; + ai->flags = entry->flags; + ai->entry = entry; + ISC_LINK_INIT(ai, publink); + +#ifdef LRU_DEBUG + adb->entryuses++; /* for debug */ +#endif - return (ai); + return (ai); } static inline void free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { - dns_adbaddrinfo_t *ai; + dns_adbaddrinfo_t *ai; - INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo)); - ai = *ainfo; - *ainfo = NULL; + INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo)); + ai = *ainfo; + *ainfo = NULL; - INSIST(ai->entry == NULL); - INSIST(!ISC_LINK_LINKED(ai, publink)); + INSIST(ai->entry == NULL); + INSIST(!ISC_LINK_LINKED(ai, publink)); - ai->magic = 0; + ai->magic = 0; - isc_mempool_put(adb->aimp, ai); + isc_mempool_put(adb->aimp, ai); } /* @@ -1581,34 +1631,34 @@ free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { */ static inline dns_adbname_t * find_name_and_lock(dns_adb_t *adb, dns_name_t *name, - unsigned int options, int *bucketp) + unsigned int options, int *bucketp) { - dns_adbname_t *adbname; - int bucket; - - bucket = dns_name_fullhash(name, ISC_FALSE) % NBUCKETS; - - if (*bucketp == DNS_ADB_INVALIDBUCKET) { - LOCK(&adb->namelocks[bucket]); - *bucketp = bucket; - } else if (*bucketp != bucket) { - UNLOCK(&adb->namelocks[*bucketp]); - LOCK(&adb->namelocks[bucket]); - *bucketp = bucket; - } - - adbname = ISC_LIST_HEAD(adb->names[bucket]); - while (adbname != NULL) { - if (!NAME_DEAD(adbname)) { - if (dns_name_equal(name, &adbname->name) - && GLUEHINT_OK(adbname, options) - && STARTATZONE_MATCHES(adbname, options)) - return (adbname); - } - adbname = ISC_LIST_NEXT(adbname, plink); - } - - return (NULL); + dns_adbname_t *adbname; + int bucket; + + bucket = dns_name_fullhash(name, ISC_FALSE) % NBUCKETS; + + if (*bucketp == DNS_ADB_INVALIDBUCKET) { + LOCK(&adb->namelocks[bucket]); + *bucketp = bucket; + } else if (*bucketp != bucket) { + UNLOCK(&adb->namelocks[*bucketp]); + LOCK(&adb->namelocks[bucket]); + *bucketp = bucket; + } + + adbname = ISC_LIST_HEAD(adb->names[bucket]); + while (adbname != NULL) { + if (!NAME_DEAD(adbname)) { + if (dns_name_equal(name, &adbname->name) + && GLUEHINT_OK(adbname, options) + && STARTATZONE_MATCHES(adbname, options)) + return (adbname); + } + adbname = ISC_LIST_NEXT(adbname, plink); + } + + return (NULL); } /* @@ -1623,28 +1673,28 @@ find_name_and_lock(dns_adb_t *adb, dns_name_t *name, */ static inline dns_adbentry_t * find_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp) { - dns_adbentry_t *entry; - int bucket; + dns_adbentry_t *entry; + int bucket; - bucket = isc_sockaddr_hash(addr, ISC_TRUE) % NBUCKETS; + bucket = isc_sockaddr_hash(addr, ISC_TRUE) % NBUCKETS; - if (*bucketp == DNS_ADB_INVALIDBUCKET) { - LOCK(&adb->entrylocks[bucket]); - *bucketp = bucket; - } else if (*bucketp != bucket) { - UNLOCK(&adb->entrylocks[*bucketp]); - LOCK(&adb->entrylocks[bucket]); - *bucketp = bucket; - } + if (*bucketp == DNS_ADB_INVALIDBUCKET) { + LOCK(&adb->entrylocks[bucket]); + *bucketp = bucket; + } else if (*bucketp != bucket) { + UNLOCK(&adb->entrylocks[*bucketp]); + LOCK(&adb->entrylocks[bucket]); + *bucketp = bucket; + } - entry = ISC_LIST_HEAD(adb->entries[bucket]); - while (entry != NULL) { - if (isc_sockaddr_equal(addr, &entry->sockaddr)) - return (entry); - entry = ISC_LIST_NEXT(entry, plink); - } + entry = ISC_LIST_HEAD(adb->entries[bucket]); + while (entry != NULL) { + if (isc_sockaddr_equal(addr, &entry->sockaddr)) + return (entry); + entry = ISC_LIST_NEXT(entry, plink); + } - return (NULL); + return (NULL); } /* @@ -1652,137 +1702,140 @@ find_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp) { */ static isc_boolean_t entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname, - dns_rdatatype_t qtype, isc_stdtime_t now) + dns_rdatatype_t qtype, isc_stdtime_t now) { - dns_adblameinfo_t *li, *next_li; - isc_boolean_t is_bad; + dns_adblameinfo_t *li, *next_li; + isc_boolean_t is_bad; - is_bad = ISC_FALSE; + is_bad = ISC_FALSE; - li = ISC_LIST_HEAD(entry->lameinfo); - if (li == NULL) - return (ISC_FALSE); - while (li != NULL) { - next_li = ISC_LIST_NEXT(li, plink); + li = ISC_LIST_HEAD(entry->lameinfo); + if (li == NULL) + return (ISC_FALSE); + while (li != NULL) { + next_li = ISC_LIST_NEXT(li, plink); - /* - * Has the entry expired? - */ - if (li->lame_timer < now) { - ISC_LIST_UNLINK(entry->lameinfo, li, plink); - free_adblameinfo(adb, &li); - } + /* + * Has the entry expired? + */ + if (li->lame_timer < now) { + ISC_LIST_UNLINK(entry->lameinfo, li, plink); + free_adblameinfo(adb, &li); + } - /* - * Order tests from least to most expensive. - * - * We do not break out of the main loop here as - * we use the loop for house keeping. - */ - if (li != NULL && !is_bad && li->qtype == qtype && - dns_name_equal(qname, &li->qname)) - is_bad = ISC_TRUE; + /* + * Order tests from least to most expensive. + * + * We do not break out of the main loop here as + * we use the loop for house keeping. + */ + if (li != NULL && !is_bad && li->qtype == qtype && + dns_name_equal(qname, &li->qname)) + is_bad = ISC_TRUE; - li = next_li; - } + li = next_li; + } - return (is_bad); + return (is_bad); } static void copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname, - dns_rdatatype_t qtype, dns_adbname_t *name, - isc_stdtime_t now) + dns_rdatatype_t qtype, dns_adbname_t *name, + isc_stdtime_t now) { - dns_adbnamehook_t *namehook; - dns_adbaddrinfo_t *addrinfo; - dns_adbentry_t *entry; - int bucket; - - bucket = DNS_ADB_INVALIDBUCKET; - - if (find->options & DNS_ADBFIND_INET) { - namehook = ISC_LIST_HEAD(name->v4); - while (namehook != NULL) { - entry = namehook->entry; - bucket = entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); - - if (!FIND_RETURNLAME(find) - && entry_is_lame(adb, entry, qname, qtype, now)) { - find->options |= DNS_ADBFIND_LAMEPRUNED; - goto nextv4; - } - addrinfo = new_adbaddrinfo(adb, entry, find->port); - if (addrinfo == NULL) { - find->partial_result |= DNS_ADBFIND_INET; - goto out; - } - /* - * Found a valid entry. Add it to the find's list. - */ - inc_entry_refcnt(adb, entry, ISC_FALSE); - ISC_LIST_APPEND(find->list, addrinfo, publink); - addrinfo = NULL; - nextv4: - UNLOCK(&adb->entrylocks[bucket]); - bucket = DNS_ADB_INVALIDBUCKET; - namehook = ISC_LIST_NEXT(namehook, plink); - } - } - - if (find->options & DNS_ADBFIND_INET6) { - namehook = ISC_LIST_HEAD(name->v6); - while (namehook != NULL) { - entry = namehook->entry; - bucket = entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); - - if (entry_is_lame(adb, entry, qname, qtype, now)) - goto nextv6; - addrinfo = new_adbaddrinfo(adb, entry, find->port); - if (addrinfo == NULL) { - find->partial_result |= DNS_ADBFIND_INET6; - goto out; - } - /* - * Found a valid entry. Add it to the find's list. - */ - inc_entry_refcnt(adb, entry, ISC_FALSE); - ISC_LIST_APPEND(find->list, addrinfo, publink); - addrinfo = NULL; - nextv6: - UNLOCK(&adb->entrylocks[bucket]); - bucket = DNS_ADB_INVALIDBUCKET; - namehook = ISC_LIST_NEXT(namehook, plink); - } - } + dns_adbnamehook_t *namehook; + dns_adbaddrinfo_t *addrinfo; + dns_adbentry_t *entry; + int bucket; + + bucket = DNS_ADB_INVALIDBUCKET; + + if (find->options & DNS_ADBFIND_INET) { + namehook = ISC_LIST_HEAD(name->v4); + while (namehook != NULL) { + entry = namehook->entry; + bucket = entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + + if (!FIND_RETURNLAME(find) + && entry_is_lame(adb, entry, qname, qtype, now)) { + find->options |= DNS_ADBFIND_LAMEPRUNED; + goto nextv4; + } + addrinfo = new_adbaddrinfo(adb, entry, find->port); + if (addrinfo == NULL) { + find->partial_result |= DNS_ADBFIND_INET; + goto out; + } + /* + * Found a valid entry. Add it to the find's list. + */ + inc_entry_refcnt(adb, entry, ISC_FALSE); + ISC_LIST_APPEND(find->list, addrinfo, publink); + addrinfo = NULL; + nextv4: + UNLOCK(&adb->entrylocks[bucket]); + bucket = DNS_ADB_INVALIDBUCKET; + namehook = ISC_LIST_NEXT(namehook, plink); + } + } + + if (find->options & DNS_ADBFIND_INET6) { + namehook = ISC_LIST_HEAD(name->v6); + while (namehook != NULL) { + entry = namehook->entry; + bucket = entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + + if (entry_is_lame(adb, entry, qname, qtype, now)) + goto nextv6; + addrinfo = new_adbaddrinfo(adb, entry, find->port); + if (addrinfo == NULL) { + find->partial_result |= DNS_ADBFIND_INET6; + goto out; + } + /* + * Found a valid entry. Add it to the find's list. + */ + inc_entry_refcnt(adb, entry, ISC_FALSE); + ISC_LIST_APPEND(find->list, addrinfo, publink); + addrinfo = NULL; + nextv6: + UNLOCK(&adb->entrylocks[bucket]); + bucket = DNS_ADB_INVALIDBUCKET; + namehook = ISC_LIST_NEXT(namehook, plink); + } + } out: - if (bucket != DNS_ADB_INVALIDBUCKET) - UNLOCK(&adb->entrylocks[bucket]); + if (bucket != DNS_ADB_INVALIDBUCKET) + UNLOCK(&adb->entrylocks[bucket]); } static void shutdown_task(isc_task_t *task, isc_event_t *ev) { - dns_adb_t *adb; - - UNUSED(task); - - adb = ev->ev_arg; - INSIST(DNS_ADB_VALID(adb)); - - /* - * Kill the timer, and then the ADB itself. Note that this implies - * that this task was the one scheduled to get timer events. If - * this is not true (and it is unfortunate there is no way to INSIST() - * this) badness will occur. - */ - LOCK(&adb->lock); - isc_timer_detach(&adb->timer); - UNLOCK(&adb->lock); - isc_event_free(&ev); - destroy(adb); + dns_adb_t *adb; + + UNUSED(task); + + adb = ev->ev_arg; + INSIST(DNS_ADB_VALID(adb)); + + /* + * Kill the timer, and then the ADB itself. Note that this implies + * that this task was the one scheduled to get timer events. If + * this is not true (and it is unfortunate there is no way to INSIST() + * this) badness will occur. + */ + LOCK(&adb->lock); + isc_timer_detach(&adb->timer); +#ifdef LRU_DEBUG + isc_timer_detach(&adb->dump_timer); +#endif + UNLOCK(&adb->lock); + isc_event_free(&ev); + destroy(adb); } /* @@ -1790,34 +1843,110 @@ shutdown_task(isc_task_t *task, isc_event_t *ev) { */ static isc_boolean_t check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { - dns_adbname_t *name; - isc_boolean_t result = ISC_FALSE; - - INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep)); - name = *namep; - - if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) - return (result); - if (NAME_FETCH(name)) - return (result); - if (!EXPIRE_OK(name->expire_v4, now)) - return (result); - if (!EXPIRE_OK(name->expire_v6, now)) - return (result); - if (!EXPIRE_OK(name->expire_target, now)) - return (result); - - /* - * The name is empty. Delete it. - */ - result = kill_name(&name, DNS_EVENT_ADBEXPIRED); - *namep = NULL; - - /* - * Our caller, or one of its callers, will be calling check_exit() at - * some point, so we don't need to do it here. - */ - return (result); + dns_adbname_t *name; + isc_boolean_t result = ISC_FALSE; + + INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep)); + name = *namep; + + if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) + return (result); + if (NAME_FETCH(name)) + return (result); + if (!EXPIRE_OK(name->expire_v4, now)) + return (result); + if (!EXPIRE_OK(name->expire_v6, now)) + return (result); + if (!EXPIRE_OK(name->expire_target, now)) + return (result); + + /* + * The name is empty. Delete it. + */ + result = kill_name(&name, DNS_EVENT_ADBEXPIRED, ISC_FALSE); + *namep = NULL; + + /* + * Our caller, or one of its callers, will be calling check_exit() at + * some point, so we don't need to do it here. + */ + return (result); +} + +/*% + * Examine the tail entry of the LRU list to see if it expires or is stale + * (unused for some period); if so, the name entry will be freed. If the ADB + * is in the overmem condition, the tail and the next to tail entries + * will be unconditionally removed (unless they have an outstanding fetch). + * We don't care about a race on 'overmem' at the risk of causing some + * collateral damage or a small delay in starting cleanup, so we don't bother + * to lock ADB (if it's not locked). + * + * Name bucket must be locked; adb may be locked; no other locks held. + */ +static void +check_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) { + int victims, max_victims; + isc_boolean_t result; + dns_adbname_t *victim, *next_victim; + isc_boolean_t overmem = adb->overmem; + int scans = 0; + + INSIST(bucket != DNS_ADB_INVALIDBUCKET); + + max_victims = overmem ? 2 : 1; + + /* + * We limit the number of scanned entries to 10 (arbitrary choice) + * in order to avoid examining too many entries when there are many + * tail entries that have fetches (this should be rare, but could + * happen). + */ + victim = ISC_LIST_TAIL(adb->names[bucket]); + for (victims = 0; + victim != NULL && victims < max_victims && scans < 10; + victim = next_victim) { + scans++; + next_victim = ISC_LIST_PREV(victim, plink); + + /* + * If the victim is already dead, it simply waits for some + * final events. Ignore it. + */ + if (NAME_DEAD(victim)) + goto next; + + result = check_expire_name(&victim, now); + if (victim == NULL) { +#ifdef LRU_DEBUG + adb->stale_expire++; +#endif + victims++; + goto next; + } + + if (!NAME_FETCH(victim) && + (overmem || victim->last_used + ADB_STALE_MARGIN <= now)) { + RUNTIME_CHECK(kill_name(&victim, + DNS_EVENT_ADBCANCELED, + ISC_TRUE) == + ISC_FALSE); +#ifdef LRU_DEBUG + adb->stale_lru++; +#endif + victims++; + } + + next: + if (!overmem) + break; + } + +#ifdef LRU_DEBUG + /* XXX: omit lock for brevity */ + adb->stale_scan += scans; + adb->stale_purge += victims; +#endif } /* @@ -1826,39 +1955,29 @@ check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { static isc_boolean_t check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) { - dns_adbentry_t *entry; - isc_boolean_t expire; - isc_boolean_t result = ISC_FALSE; - - INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); - entry = *entryp; - - if (entry->refcnt != 0) - return (result); + dns_adbentry_t *entry; + isc_boolean_t result = ISC_FALSE; - if (adb->overmem) { - isc_uint32_t val; + INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); + entry = *entryp; - isc_random_get(&val); + if (entry->refcnt != 0) + return (result); - expire = ISC_TF((val % 4) == 0); - } else - expire = ISC_FALSE; + if (entry->expires == 0 || entry->expires > now) + return (result); - if (entry->expires == 0 || (! expire && entry->expires > now)) - return (result); - - /* - * The entry is not in use. Delete it. - */ - DP(DEF_LEVEL, "killing entry %p", entry); - INSIST(ISC_LINK_LINKED(entry, plink)); - result = unlink_entry(adb, entry); - free_adbentry(adb, &entry); - if (result) - dec_adb_irefcnt(adb); - *entryp = NULL; - return (result); + /* + * The entry is not in use. Delete it. + */ + DP(DEF_LEVEL, "killing entry %p", entry); + INSIST(ISC_LINK_LINKED(entry, plink)); + result = unlink_entry(adb, entry); + free_adbentry(adb, &entry); + if (result) + dec_adb_irefcnt(adb); + *entryp = NULL; + return (result); } /* @@ -1866,29 +1985,29 @@ check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) */ static isc_boolean_t cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { - dns_adbname_t *name; - dns_adbname_t *next_name; - isc_boolean_t result = ISC_FALSE; - - DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket); - - LOCK(&adb->namelocks[bucket]); - if (adb->name_sd[bucket]) { - UNLOCK(&adb->namelocks[bucket]); - return (result); - } - - name = ISC_LIST_HEAD(adb->names[bucket]); - while (name != NULL) { - next_name = ISC_LIST_NEXT(name, plink); - INSIST(result == ISC_FALSE); - result = check_expire_namehooks(name, now, adb->overmem); - if (!result) - result = check_expire_name(&name, now); - name = next_name; - } - UNLOCK(&adb->namelocks[bucket]); - return (result); + dns_adbname_t *name; + dns_adbname_t *next_name; + isc_boolean_t result = ISC_FALSE; + + DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket); + + LOCK(&adb->namelocks[bucket]); + if (adb->name_sd[bucket]) { + UNLOCK(&adb->namelocks[bucket]); + return (result); + } + + name = ISC_LIST_HEAD(adb->names[bucket]); + while (name != NULL) { + next_name = ISC_LIST_NEXT(name, plink); + INSIST(result == ISC_FALSE); + result = check_expire_namehooks(name, now); + if (!result) + result = check_expire_name(&name, now); + name = next_name; + } + UNLOCK(&adb->namelocks[bucket]); + return (result); } /* @@ -1896,102 +2015,131 @@ cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { */ static isc_boolean_t cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) { - dns_adbentry_t *entry, *next_entry; - isc_boolean_t result = ISC_FALSE; + dns_adbentry_t *entry, *next_entry; + isc_boolean_t result = ISC_FALSE; - DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket); + DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket); - LOCK(&adb->entrylocks[bucket]); - entry = ISC_LIST_HEAD(adb->entries[bucket]); - while (entry != NULL) { - next_entry = ISC_LIST_NEXT(entry, plink); - INSIST(result == ISC_FALSE); - result = check_expire_entry(adb, &entry, now); - entry = next_entry; - } - UNLOCK(&adb->entrylocks[bucket]); - return (result); + LOCK(&adb->entrylocks[bucket]); + entry = ISC_LIST_HEAD(adb->entries[bucket]); + while (entry != NULL) { + next_entry = ISC_LIST_NEXT(entry, plink); + INSIST(result == ISC_FALSE); + result = check_expire_entry(adb, &entry, now); + entry = next_entry; + } + UNLOCK(&adb->entrylocks[bucket]); + return (result); } +#if 1 +static void +timer_cleanup(isc_task_t *task, isc_event_t *ev) { + UNUSED(task); + + isc_event_free(&ev); +} +#else static void timer_cleanup(isc_task_t *task, isc_event_t *ev) { - dns_adb_t *adb; - isc_stdtime_t now; - unsigned int i; - isc_interval_t interval; - - UNUSED(task); - - adb = ev->ev_arg; - INSIST(DNS_ADB_VALID(adb)); - - LOCK(&adb->lock); - - isc_stdtime_get(&now); - - for (i = 0; i < CLEAN_BUCKETS; i++) { - /* - * Call our cleanup routines. - */ - RUNTIME_CHECK(cleanup_names(adb, adb->next_cleanbucket, now) == - ISC_FALSE); - RUNTIME_CHECK(cleanup_entries(adb, adb->next_cleanbucket, now) - == ISC_FALSE); - - /* - * Set the next bucket to be cleaned. - */ - adb->next_cleanbucket++; - if (adb->next_cleanbucket >= NBUCKETS) { - adb->next_cleanbucket = 0; + dns_adb_t *adb; + isc_stdtime_t now; + unsigned int i; + isc_interval_t interval; + + UNUSED(task); + + adb = ev->ev_arg; + INSIST(DNS_ADB_VALID(adb)); + + LOCK(&adb->lock); + + isc_stdtime_get(&now); + + for (i = 0; i < CLEAN_BUCKETS; i++) { + /* + * Call our cleanup routines. + */ + RUNTIME_CHECK(cleanup_names(adb, adb->next_cleanbucket, now) == + ISC_FALSE); + RUNTIME_CHECK(cleanup_entries(adb, adb->next_cleanbucket, now) + == ISC_FALSE); + + /* + * Set the next bucket to be cleaned. + */ + adb->next_cleanbucket++; + if (adb->next_cleanbucket >= NBUCKETS) { + adb->next_cleanbucket = 0; #ifdef DUMP_ADB_AFTER_CLEANING - dump_adb(adb, stdout, ISC_TRUE, now); + dump_adb(adb, stdout, ISC_TRUE, now); #endif - } - } + } + } - /* - * Reset the timer. - * XXXDCL isc_timer_reset might return ISC_R_UNEXPECTED or - * ISC_R_NOMEMORY, but it isn't clear what could be done here - * if either one of those things happened. - */ - interval = adb->tick_interval; - if (adb->overmem) - isc_interval_set(&interval, 0, 1); - (void)isc_timer_reset(adb->timer, isc_timertype_once, NULL, - &interval, ISC_FALSE); + /* + * Reset the timer. + * XXXDCL isc_timer_reset might return ISC_R_UNEXPECTED or + * ISC_R_NOMEMORY, but it isn't clear what could be done here + * if either one of those things happened. + */ + interval = adb->tick_interval; + if (adb->overmem) + isc_interval_set(&interval, 0, 1); + (void)isc_timer_reset(adb->timer, isc_timertype_once, NULL, + &interval, ISC_FALSE); - UNLOCK(&adb->lock); + UNLOCK(&adb->lock); - isc_event_free(&ev); + isc_event_free(&ev); } +#endif static void destroy(dns_adb_t *adb) { - adb->magic = 0; + adb->magic = 0; + +#ifdef LRU_DEBUG + /* for debug: print statistics */ + if (adb->nname_total > 0) { + INSIST(adb->nname == 0 && adb->nentry == 0); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_ADB, ISC_LOG_INFO, + "ADB %p name hit %.2f, entry hit %.2f", adb, + (double)adb->nameuses / + (adb->nname_total + adb->nameuses), + adb->entryuses > 0 ? + (double)adb->entryuses / + (adb->nentry_total + adb->entryuses) : 0); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_ADB, ISC_LOG_INFO, + "ADB %p stale name purges: %u(%u,%u)/%u", + adb, adb->stale_purge, adb->stale_expire, + adb->stale_lru, adb->stale_scan); + } +#endif - /* - * The timer is already dead, from the task's shutdown callback. - */ - isc_task_detach(&adb->task); + /* + * The timer is already dead, from the task's shutdown callback. + */ + isc_task_detach(&adb->task); - isc_mempool_destroy(&adb->nmp); - isc_mempool_destroy(&adb->nhmp); - isc_mempool_destroy(&adb->limp); - isc_mempool_destroy(&adb->emp); - isc_mempool_destroy(&adb->ahmp); - isc_mempool_destroy(&adb->aimp); - isc_mempool_destroy(&adb->afmp); + isc_mempool_destroy(&adb->nmp); + isc_mempool_destroy(&adb->nhmp); + isc_mempool_destroy(&adb->limp); + isc_mempool_destroy(&adb->emp); + isc_mempool_destroy(&adb->ahmp); + isc_mempool_destroy(&adb->aimp); + isc_mempool_destroy(&adb->afmp); - DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); - DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); + DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); + DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); - DESTROYLOCK(&adb->reflock); - DESTROYLOCK(&adb->lock); - DESTROYLOCK(&adb->mplock); + DESTROYLOCK(&adb->reflock); + DESTROYLOCK(&adb->lock); + DESTROYLOCK(&adb->mplock); - isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); + isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); } @@ -2001,1341 +2149,1393 @@ destroy(dns_adb_t *adb) { isc_result_t dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, - isc_taskmgr_t *taskmgr, dns_adb_t **newadb) + isc_taskmgr_t *taskmgr, dns_adb_t **newadb) { - dns_adb_t *adb; - isc_result_t result; - int i; - - REQUIRE(mem != NULL); - REQUIRE(view != NULL); - REQUIRE(timermgr != NULL); - REQUIRE(taskmgr != NULL); - REQUIRE(newadb != NULL && *newadb == NULL); - - adb = isc_mem_get(mem, sizeof(dns_adb_t)); - if (adb == NULL) - return (ISC_R_NOMEMORY); - - /* - * Initialize things here that cannot fail, and especially things - * that must be NULL for the error return to work properly. - */ - adb->magic = 0; - adb->erefcnt = 1; - adb->irefcnt = 0; - adb->nmp = NULL; - adb->nhmp = NULL; - adb->limp = NULL; - adb->emp = NULL; - adb->ahmp = NULL; - adb->aimp = NULL; - adb->afmp = NULL; - adb->task = NULL; - adb->timer = NULL; - adb->mctx = NULL; - adb->view = view; - adb->timermgr = timermgr; - adb->taskmgr = taskmgr; - adb->next_cleanbucket = 0; - ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, - DNS_EVENT_ADBCONTROL, shutdown_task, adb, - adb, NULL, NULL); - adb->cevent_sent = ISC_FALSE; - adb->shutting_down = ISC_FALSE; - adb->overmem = ISC_FALSE; - ISC_LIST_INIT(adb->whenshutdown); - - isc_mem_attach(mem, &adb->mctx); - - result = isc_mutex_init(&adb->lock); - if (result != ISC_R_SUCCESS) - goto fail0b; - - result = isc_mutex_init(&adb->mplock); - if (result != ISC_R_SUCCESS) - goto fail0c; - - result = isc_mutex_init(&adb->reflock); - if (result != ISC_R_SUCCESS) - goto fail0d; - - /* - * Initialize the bucket locks for names and elements. - * May as well initialize the list heads, too. - */ - result = isc_mutexblock_init(adb->namelocks, NBUCKETS); - if (result != ISC_R_SUCCESS) - goto fail1; - for (i = 0; i < NBUCKETS; i++) { - ISC_LIST_INIT(adb->names[i]); - adb->name_sd[i] = ISC_FALSE; - adb->name_refcnt[i] = 0; - adb->irefcnt++; - } - for (i = 0; i < NBUCKETS; i++) { - ISC_LIST_INIT(adb->entries[i]); - adb->entry_sd[i] = ISC_FALSE; - adb->entry_refcnt[i] = 0; - adb->irefcnt++; - } - result = isc_mutexblock_init(adb->entrylocks, NBUCKETS); - if (result != ISC_R_SUCCESS) - goto fail2; - - /* - * Memory pools - */ + dns_adb_t *adb; + isc_result_t result; + int i; + + REQUIRE(mem != NULL); + REQUIRE(view != NULL); + REQUIRE(timermgr != NULL); + REQUIRE(taskmgr != NULL); + REQUIRE(newadb != NULL && *newadb == NULL); + + adb = isc_mem_get(mem, sizeof(dns_adb_t)); + if (adb == NULL) + return (ISC_R_NOMEMORY); + + /* + * Initialize things here that cannot fail, and especially things + * that must be NULL for the error return to work properly. + */ + adb->magic = 0; + adb->erefcnt = 1; + adb->irefcnt = 0; + adb->nmp = NULL; + adb->nhmp = NULL; + adb->limp = NULL; + adb->emp = NULL; + adb->ahmp = NULL; + adb->aimp = NULL; + adb->afmp = NULL; + adb->task = NULL; + adb->timer = NULL; +#ifdef LRU_DEBUG + adb->dump_timer = NULL; +#endif + adb->mctx = NULL; + adb->view = view; + adb->timermgr = timermgr; + adb->taskmgr = taskmgr; + adb->next_cleanbucket = 0; + ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, + DNS_EVENT_ADBCONTROL, shutdown_task, adb, + adb, NULL, NULL); + adb->cevent_sent = ISC_FALSE; + adb->shutting_down = ISC_FALSE; + adb->overmem = ISC_FALSE; + ISC_LIST_INIT(adb->whenshutdown); + +#ifdef LRU_DEBUG + /* for debug */ + adb->nname = 0; + adb->nname_total = 0; + adb->nentry = 0; + adb->nentry_total = 0; + adb->stale_purge = 0; + adb->stale_scan = 0; + adb->stale_expire = 0; + adb->stale_lru = 0; + adb->nameuses = 0; + adb->entryuses = 0; +#endif + + isc_mem_attach(mem, &adb->mctx); + + result = isc_mutex_init(&adb->lock); + if (result != ISC_R_SUCCESS) + goto fail0b; + + result = isc_mutex_init(&adb->mplock); + if (result != ISC_R_SUCCESS) + goto fail0c; + + result = isc_mutex_init(&adb->reflock); + if (result != ISC_R_SUCCESS) + goto fail0d; + + /* + * Initialize the bucket locks for names and elements. + * May as well initialize the list heads, too. + */ + result = isc_mutexblock_init(adb->namelocks, NBUCKETS); + if (result != ISC_R_SUCCESS) + goto fail1; + for (i = 0; i < NBUCKETS; i++) { + ISC_LIST_INIT(adb->names[i]); + adb->name_sd[i] = ISC_FALSE; + adb->name_refcnt[i] = 0; + adb->irefcnt++; + } + for (i = 0; i < NBUCKETS; i++) { + ISC_LIST_INIT(adb->entries[i]); + adb->entry_sd[i] = ISC_FALSE; + adb->entry_refcnt[i] = 0; + adb->irefcnt++; + } + result = isc_mutexblock_init(adb->entrylocks, NBUCKETS); + if (result != ISC_R_SUCCESS) + goto fail2; + + /* + * Memory pools + */ #define MPINIT(t, p, n) do { \ - result = isc_mempool_create(mem, sizeof(t), &(p)); \ - if (result != ISC_R_SUCCESS) \ - goto fail3; \ - isc_mempool_setfreemax((p), FREE_ITEMS); \ - isc_mempool_setfillcount((p), FILL_COUNT); \ - isc_mempool_setname((p), n); \ - isc_mempool_associatelock((p), &adb->mplock); \ + result = isc_mempool_create(mem, sizeof(t), &(p)); \ + if (result != ISC_R_SUCCESS) \ + goto fail3; \ + isc_mempool_setfreemax((p), FREE_ITEMS); \ + isc_mempool_setfillcount((p), FILL_COUNT); \ + isc_mempool_setname((p), n); \ + isc_mempool_associatelock((p), &adb->mplock); \ } while (0) - MPINIT(dns_adbname_t, adb->nmp, "adbname"); - MPINIT(dns_adbnamehook_t, adb->nhmp, "adbnamehook"); - MPINIT(dns_adblameinfo_t, adb->limp, "adblameinfo"); - MPINIT(dns_adbentry_t, adb->emp, "adbentry"); - MPINIT(dns_adbfind_t, adb->ahmp, "adbfind"); - MPINIT(dns_adbaddrinfo_t, adb->aimp, "adbaddrinfo"); - MPINIT(dns_adbfetch_t, adb->afmp, "adbfetch"); + MPINIT(dns_adbname_t, adb->nmp, "adbname"); + MPINIT(dns_adbnamehook_t, adb->nhmp, "adbnamehook"); + MPINIT(dns_adblameinfo_t, adb->limp, "adblameinfo"); + MPINIT(dns_adbentry_t, adb->emp, "adbentry"); + MPINIT(dns_adbfind_t, adb->ahmp, "adbfind"); + MPINIT(dns_adbaddrinfo_t, adb->aimp, "adbaddrinfo"); + MPINIT(dns_adbfetch_t, adb->afmp, "adbfetch"); #undef MPINIT - /* - * Allocate a timer and a task for our periodic cleanup. - */ - result = isc_task_create(adb->taskmgr, 0, &adb->task); - if (result != ISC_R_SUCCESS) - goto fail3; - isc_task_setname(adb->task, "ADB", adb); - /* - * XXXMLG When this is changed to be a config file option, - */ - isc_interval_set(&adb->tick_interval, CLEAN_SECONDS, 0); - result = isc_timer_create(adb->timermgr, isc_timertype_once, - NULL, &adb->tick_interval, adb->task, - timer_cleanup, adb, &adb->timer); - if (result != ISC_R_SUCCESS) - goto fail3; - - DP(ISC_LOG_DEBUG(5), "cleaning interval for adb: " - "%u buckets every %u seconds, %u buckets in system, %u cl.interval", - CLEAN_BUCKETS, CLEAN_SECONDS, NBUCKETS, CLEAN_PERIOD); - - /* - * Normal return. - */ - adb->magic = DNS_ADB_MAGIC; - *newadb = adb; - return (ISC_R_SUCCESS); + /* + * Allocate a timer and a task for our periodic cleanup. + */ + result = isc_task_create(adb->taskmgr, 0, &adb->task); + if (result != ISC_R_SUCCESS) + goto fail3; + isc_task_setname(adb->task, "ADB", adb); + /* + * XXXMLG When this is changed to be a config file option, + */ + isc_interval_set(&adb->tick_interval, CLEAN_SECONDS, 0); + result = isc_timer_create(adb->timermgr, isc_timertype_once, + NULL, &adb->tick_interval, adb->task, + timer_cleanup, adb, &adb->timer); + if (result != ISC_R_SUCCESS) + goto fail3; + +#ifdef LRU_DEBUG + { + isc_interval_t interval; + + interval.seconds = DUMP_INTERVAL; + interval.nanoseconds = 0; + RUNTIME_CHECK(isc_time_nowplusinterval(&adb->dump_time, + &interval) == + ISC_R_SUCCESS); + + result = isc_timer_create(adb->timermgr, isc_timertype_once, + &adb->dump_time, NULL, adb->task, + timer_dump, adb, &adb->dump_timer); + } +#endif + + DP(ISC_LOG_DEBUG(5), "cleaning interval for adb: " + "%u buckets every %u seconds, %u buckets in system, %u cl.interval", + CLEAN_BUCKETS, CLEAN_SECONDS, NBUCKETS, CLEAN_PERIOD); + + /* + * Normal return. + */ + adb->magic = DNS_ADB_MAGIC; + *newadb = adb; + return (ISC_R_SUCCESS); fail3: - if (adb->task != NULL) - isc_task_detach(&adb->task); - if (adb->timer != NULL) - isc_timer_detach(&adb->timer); + if (adb->task != NULL) + isc_task_detach(&adb->task); + if (adb->timer != NULL) + isc_timer_detach(&adb->timer); - /* clean up entrylocks */ - DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); + /* clean up entrylocks */ + DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); fail2: /* clean up namelocks */ - DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); + DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); fail1: /* clean up only allocated memory */ - if (adb->nmp != NULL) - isc_mempool_destroy(&adb->nmp); - if (adb->nhmp != NULL) - isc_mempool_destroy(&adb->nhmp); - if (adb->limp != NULL) - isc_mempool_destroy(&adb->limp); - if (adb->emp != NULL) - isc_mempool_destroy(&adb->emp); - if (adb->ahmp != NULL) - isc_mempool_destroy(&adb->ahmp); - if (adb->aimp != NULL) - isc_mempool_destroy(&adb->aimp); - if (adb->afmp != NULL) - isc_mempool_destroy(&adb->afmp); - - DESTROYLOCK(&adb->reflock); + if (adb->nmp != NULL) + isc_mempool_destroy(&adb->nmp); + if (adb->nhmp != NULL) + isc_mempool_destroy(&adb->nhmp); + if (adb->limp != NULL) + isc_mempool_destroy(&adb->limp); + if (adb->emp != NULL) + isc_mempool_destroy(&adb->emp); + if (adb->ahmp != NULL) + isc_mempool_destroy(&adb->ahmp); + if (adb->aimp != NULL) + isc_mempool_destroy(&adb->aimp); + if (adb->afmp != NULL) + isc_mempool_destroy(&adb->afmp); + + DESTROYLOCK(&adb->reflock); fail0d: - DESTROYLOCK(&adb->mplock); + DESTROYLOCK(&adb->mplock); fail0c: - DESTROYLOCK(&adb->lock); + DESTROYLOCK(&adb->lock); fail0b: - isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); + isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); - return (result); + return (result); } void dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) { - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(adbx != NULL && *adbx == NULL); + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(adbx != NULL && *adbx == NULL); - inc_adb_erefcnt(adb); - *adbx = adb; + inc_adb_erefcnt(adb); + *adbx = adb; } void dns_adb_detach(dns_adb_t **adbx) { - dns_adb_t *adb; - isc_boolean_t need_exit_check; + dns_adb_t *adb; + isc_boolean_t need_exit_check; - REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx)); + REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx)); - adb = *adbx; - *adbx = NULL; + adb = *adbx; + *adbx = NULL; - INSIST(adb->erefcnt > 0); + INSIST(adb->erefcnt > 0); - LOCK(&adb->reflock); - adb->erefcnt--; - need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0); - UNLOCK(&adb->reflock); + LOCK(&adb->reflock); + adb->erefcnt--; + need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0); + UNLOCK(&adb->reflock); - if (need_exit_check) { - LOCK(&adb->lock); - INSIST(adb->shutting_down); - check_exit(adb); - UNLOCK(&adb->lock); - } + if (need_exit_check) { + LOCK(&adb->lock); + INSIST(adb->shutting_down); + check_exit(adb); + UNLOCK(&adb->lock); + } } void dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { - isc_task_t *clone; - isc_event_t *event; - isc_boolean_t zeroirefcnt = ISC_FALSE; + isc_task_t *clone; + isc_event_t *event; + isc_boolean_t zeroirefcnt = ISC_FALSE; - /* - * Send '*eventp' to 'task' when 'adb' has shutdown. - */ + /* + * Send '*eventp' to 'task' when 'adb' has shutdown. + */ - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(eventp != NULL); + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(eventp != NULL); - event = *eventp; - *eventp = NULL; + event = *eventp; + *eventp = NULL; - LOCK(&adb->lock); + LOCK(&adb->lock); - LOCK(&adb->reflock); - zeroirefcnt = ISC_TF(adb->irefcnt == 0); + LOCK(&adb->reflock); + zeroirefcnt = ISC_TF(adb->irefcnt == 0); - if (adb->shutting_down && zeroirefcnt && - isc_mempool_getallocated(adb->ahmp) == 0) { - /* - * We're already shutdown. Send the event. - */ - event->ev_sender = adb; - isc_task_send(task, &event); - } else { - clone = NULL; - isc_task_attach(task, &clone); - event->ev_sender = clone; - ISC_LIST_APPEND(adb->whenshutdown, event, ev_link); - } + if (adb->shutting_down && zeroirefcnt && + isc_mempool_getallocated(adb->ahmp) == 0) { + /* + * We're already shutdown. Send the event. + */ + event->ev_sender = adb; + isc_task_send(task, &event); + } else { + clone = NULL; + isc_task_attach(task, &clone); + event->ev_sender = clone; + ISC_LIST_APPEND(adb->whenshutdown, event, ev_link); + } - UNLOCK(&adb->reflock); - UNLOCK(&adb->lock); + UNLOCK(&adb->reflock); + UNLOCK(&adb->lock); } void dns_adb_shutdown(dns_adb_t *adb) { - isc_boolean_t need_check_exit; + isc_boolean_t need_check_exit; - /* - * Shutdown 'adb'. - */ + /* + * Shutdown 'adb'. + */ - LOCK(&adb->lock); + LOCK(&adb->lock); - if (!adb->shutting_down) { - adb->shutting_down = ISC_TRUE; - isc_mem_setwater(adb->mctx, water, adb, 0, 0); - need_check_exit = shutdown_names(adb); - if (!need_check_exit) - need_check_exit = shutdown_entries(adb); - if (need_check_exit) - check_exit(adb); - } + if (!adb->shutting_down) { + adb->shutting_down = ISC_TRUE; + isc_mem_setwater(adb->mctx, water, adb, 0, 0); + need_check_exit = shutdown_names(adb); + if (!need_check_exit) + need_check_exit = shutdown_entries(adb); + if (need_check_exit) + check_exit(adb); + } - UNLOCK(&adb->lock); + UNLOCK(&adb->lock); } isc_result_t dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, - void *arg, dns_name_t *name, dns_name_t *qname, - dns_rdatatype_t qtype, unsigned int options, - isc_stdtime_t now, dns_name_t *target, - in_port_t port, dns_adbfind_t **findp) + void *arg, dns_name_t *name, dns_name_t *qname, + dns_rdatatype_t qtype, unsigned int options, + isc_stdtime_t now, dns_name_t *target, + in_port_t port, dns_adbfind_t **findp) { - dns_adbfind_t *find; - dns_adbname_t *adbname; - int bucket; - isc_boolean_t want_event, start_at_zone, alias, have_address; - isc_result_t result; - unsigned int wanted_addresses; - unsigned int wanted_fetches; - unsigned int query_pending; - - REQUIRE(DNS_ADB_VALID(adb)); - if (task != NULL) { - REQUIRE(action != NULL); - } - REQUIRE(name != NULL); - REQUIRE(qname != NULL); - REQUIRE(findp != NULL && *findp == NULL); - REQUIRE(target == NULL || dns_name_hasbuffer(target)); - - REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0); - - result = ISC_R_UNEXPECTED; - wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK); - wanted_fetches = 0; - query_pending = 0; - want_event = ISC_FALSE; - start_at_zone = ISC_FALSE; - alias = ISC_FALSE; - - if (now == 0) - isc_stdtime_get(&now); - - /* - * XXXMLG Move this comment somewhere else! - * - * Look up the name in our internal database. - * - * Possibilities: Note that these are not always exclusive. - * - * No name found. In this case, allocate a new name header and - * an initial namehook or two. If any of these allocations - * fail, clean up and return ISC_R_NOMEMORY. - * - * Name found, valid addresses present. Allocate one addrinfo - * structure for each found and append it to the linked list - * of addresses for this header. - * - * Name found, queries pending. In this case, if a task was - * passed in, allocate a job id, attach it to the name's job - * list and remember to tell the caller that there will be - * more info coming later. - */ - - find = new_adbfind(adb); - if (find == NULL) - return (ISC_R_NOMEMORY); - - find->port = port; - - /* - * Remember what types of addresses we are interested in. - */ - find->options = options; - find->flags |= wanted_addresses; - if (FIND_WANTEVENT(find)) { - REQUIRE(task != NULL); - } - - /* - * Try to see if we know anything about this name at all. - */ - bucket = DNS_ADB_INVALIDBUCKET; - adbname = find_name_and_lock(adb, name, find->options, &bucket); - if (adb->name_sd[bucket]) { - DP(DEF_LEVEL, - "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN"); - RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); - result = ISC_R_SHUTTINGDOWN; - goto out; - } - - /* - * Nothing found. Allocate a new adbname structure for this name. - */ - if (adbname == NULL) { - adbname = new_adbname(adb, name); - if (adbname == NULL) { - RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); - result = ISC_R_NOMEMORY; - goto out; - } - link_name(adb, bucket, adbname); - if (FIND_HINTOK(find)) - adbname->flags |= NAME_HINT_OK; - if (FIND_GLUEOK(find)) - adbname->flags |= NAME_GLUE_OK; - if (FIND_STARTATZONE(find)) - adbname->flags |= NAME_STARTATZONE; - } - - /* - * Expire old entries, etc. - */ - RUNTIME_CHECK(check_expire_namehooks(adbname, now, adb->overmem) == - ISC_FALSE); - - /* - * Do we know that the name is an alias? - */ - if (!EXPIRE_OK(adbname->expire_target, now)) { - /* - * Yes, it is. - */ - DP(DEF_LEVEL, - "dns_adb_createfind: name %p is an alias (cached)", - adbname); - alias = ISC_TRUE; - goto post_copy; - } - - /* - * Try to populate the name from the database and/or - * start fetches. First try looking for an A record - * in the database. - */ - if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now) - && WANT_INET(wanted_addresses)) { - result = dbfind_name(adbname, now, dns_rdatatype_a); - if (result == ISC_R_SUCCESS) { - DP(DEF_LEVEL, - "dns_adb_createfind: found A for name %p in db", - adbname); - goto v6; - } - - /* - * Did we get a CNAME or DNAME? - */ - if (result == DNS_R_ALIAS) { - DP(DEF_LEVEL, - "dns_adb_createfind: name %p is an alias", - adbname); - alias = ISC_TRUE; - goto post_copy; - } - - /* - * If the name doesn't exist at all, don't bother with - * v6 queries; they won't work. - * - * If the name does exist but we didn't get our data, go - * ahead and try AAAA. - * - * If the result is neither of these, try a fetch for A. - */ - if (NXDOMAIN_RESULT(result)) - goto fetch; - else if (NXRRSET_RESULT(result)) - goto v6; - - if (!NAME_FETCH_V4(adbname)) - wanted_fetches |= DNS_ADBFIND_INET; - } + dns_adbfind_t *find; + dns_adbname_t *adbname; + int bucket; + isc_boolean_t want_event, start_at_zone, alias, have_address; + isc_result_t result; + unsigned int wanted_addresses; + unsigned int wanted_fetches; + unsigned int query_pending; + + REQUIRE(DNS_ADB_VALID(adb)); + if (task != NULL) { + REQUIRE(action != NULL); + } + REQUIRE(name != NULL); + REQUIRE(qname != NULL); + REQUIRE(findp != NULL && *findp == NULL); + REQUIRE(target == NULL || dns_name_hasbuffer(target)); + + REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0); + + result = ISC_R_UNEXPECTED; + wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK); + wanted_fetches = 0; + query_pending = 0; + want_event = ISC_FALSE; + start_at_zone = ISC_FALSE; + alias = ISC_FALSE; + + if (now == 0) + isc_stdtime_get(&now); + + /* + * XXXMLG Move this comment somewhere else! + * + * Look up the name in our internal database. + * + * Possibilities: Note that these are not always exclusive. + * + * No name found. In this case, allocate a new name header and + * an initial namehook or two. If any of these allocations + * fail, clean up and return ISC_R_NOMEMORY. + * + * Name found, valid addresses present. Allocate one addrinfo + * structure for each found and append it to the linked list + * of addresses for this header. + * + * Name found, queries pending. In this case, if a task was + * passed in, allocate a job id, attach it to the name's job + * list and remember to tell the caller that there will be + * more info coming later. + */ + + find = new_adbfind(adb); + if (find == NULL) + return (ISC_R_NOMEMORY); + + find->port = port; + + /* + * Remember what types of addresses we are interested in. + */ + find->options = options; + find->flags |= wanted_addresses; + if (FIND_WANTEVENT(find)) { + REQUIRE(task != NULL); + } + + /* + * Try to see if we know anything about this name at all. + */ + bucket = DNS_ADB_INVALIDBUCKET; + adbname = find_name_and_lock(adb, name, find->options, &bucket); + if (adb->name_sd[bucket]) { + DP(DEF_LEVEL, + "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN"); + RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); + result = ISC_R_SHUTTINGDOWN; + goto out; + } + + /* + * Nothing found. Allocate a new adbname structure for this name. + */ + if (adbname == NULL) { + /* + * See if there is any stale name at the end of list, and purge + * it if so. + */ + check_stale_name(adb, bucket, now); + + adbname = new_adbname(adb, name); + if (adbname == NULL) { + RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); + result = ISC_R_NOMEMORY; + goto out; + } + link_name(adb, bucket, adbname); + if (FIND_HINTOK(find)) + adbname->flags |= NAME_HINT_OK; + if (FIND_GLUEOK(find)) + adbname->flags |= NAME_GLUE_OK; + if (FIND_STARTATZONE(find)) + adbname->flags |= NAME_STARTATZONE; + +#ifdef LRU_DEBUG + adb->nname++; /* XXX: omit ADB lock for brevity */ + adb->nname_total++; +#endif + } else { + /* Move this name forward in the LRU list */ + ISC_LIST_UNLINK(adb->names[bucket], adbname, plink); + ISC_LIST_PREPEND(adb->names[bucket], adbname, plink); +#ifdef LRU_DEBUG + adb->nameuses++; +#endif + } + adbname->last_used = now; + + /* + * Expire old entries, etc. + */ + RUNTIME_CHECK(check_expire_namehooks(adbname, now) == ISC_FALSE); + + /* + * Do we know that the name is an alias? + */ + if (!EXPIRE_OK(adbname->expire_target, now)) { + /* + * Yes, it is. + */ + DP(DEF_LEVEL, + "dns_adb_createfind: name %p is an alias (cached)", + adbname); + alias = ISC_TRUE; + goto post_copy; + } + + /* + * Try to populate the name from the database and/or + * start fetches. First try looking for an A record + * in the database. + */ + if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now) + && WANT_INET(wanted_addresses)) { + result = dbfind_name(adbname, now, dns_rdatatype_a); + if (result == ISC_R_SUCCESS) { + DP(DEF_LEVEL, + "dns_adb_createfind: found A for name %p in db", + adbname); + goto v6; + } + + /* + * Did we get a CNAME or DNAME? + */ + if (result == DNS_R_ALIAS) { + DP(DEF_LEVEL, + "dns_adb_createfind: name %p is an alias", + adbname); + alias = ISC_TRUE; + goto post_copy; + } + + /* + * If the name doesn't exist at all, don't bother with + * v6 queries; they won't work. + * + * If the name does exist but we didn't get our data, go + * ahead and try AAAA. + * + * If the result is neither of these, try a fetch for A. + */ + if (NXDOMAIN_RESULT(result)) + goto fetch; + else if (NXRRSET_RESULT(result)) + goto v6; + + if (!NAME_FETCH_V4(adbname)) + wanted_fetches |= DNS_ADBFIND_INET; + } v6: - if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now) - && WANT_INET6(wanted_addresses)) { - result = dbfind_name(adbname, now, dns_rdatatype_aaaa); - if (result == ISC_R_SUCCESS) { - DP(DEF_LEVEL, - "dns_adb_createfind: found AAAA for name %p", - adbname); - goto fetch; - } - - /* - * Did we get a CNAME or DNAME? - */ - if (result == DNS_R_ALIAS) { - DP(DEF_LEVEL, - "dns_adb_createfind: name %p is an alias", - adbname); - alias = ISC_TRUE; - goto post_copy; - } - - /* - * Listen to negative cache hints, and don't start - * another query. - */ - if (NCACHE_RESULT(result) || AUTH_NX(result)) - goto fetch; - - if (!NAME_FETCH_V6(adbname)) - wanted_fetches |= DNS_ADBFIND_INET6; - } + if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now) + && WANT_INET6(wanted_addresses)) { + result = dbfind_name(adbname, now, dns_rdatatype_aaaa); + if (result == ISC_R_SUCCESS) { + DP(DEF_LEVEL, + "dns_adb_createfind: found AAAA for name %p", + adbname); + goto fetch; + } + + /* + * Did we get a CNAME or DNAME? + */ + if (result == DNS_R_ALIAS) { + DP(DEF_LEVEL, + "dns_adb_createfind: name %p is an alias", + adbname); + alias = ISC_TRUE; + goto post_copy; + } + + /* + * Listen to negative cache hints, and don't start + * another query. + */ + if (NCACHE_RESULT(result) || AUTH_NX(result)) + goto fetch; + + if (!NAME_FETCH_V6(adbname)) + wanted_fetches |= DNS_ADBFIND_INET6; + } fetch: - if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) || - (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname))) - have_address = ISC_TRUE; - else - have_address = ISC_FALSE; - if (wanted_fetches != 0 && - ! (FIND_AVOIDFETCHES(find) && have_address)) { - /* - * We're missing at least one address family. Either the - * caller hasn't instructed us to avoid fetches, or we don't - * know anything about any of the address families that would - * be acceptable so we have to launch fetches. - */ - - if (FIND_STARTATZONE(find)) - start_at_zone = ISC_TRUE; - - /* - * Start V4. - */ - if (WANT_INET(wanted_fetches) && - fetch_name(adbname, start_at_zone, - dns_rdatatype_a) == ISC_R_SUCCESS) { - DP(DEF_LEVEL, - "dns_adb_createfind: started A fetch for name %p", - adbname); - } - - /* - * Start V6. - */ - if (WANT_INET6(wanted_fetches) && - fetch_name(adbname, start_at_zone, - dns_rdatatype_aaaa) == ISC_R_SUCCESS) { - DP(DEF_LEVEL, - "dns_adb_createfind: " - "started AAAA fetch for name %p", - adbname); - } - } - - /* - * Run through the name and copy out the bits we are - * interested in. - */ - copy_namehook_lists(adb, find, qname, qtype, adbname, now); + if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) || + (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname))) + have_address = ISC_TRUE; + else + have_address = ISC_FALSE; + if (wanted_fetches != 0 && + ! (FIND_AVOIDFETCHES(find) && have_address)) { + /* + * We're missing at least one address family. Either the + * caller hasn't instructed us to avoid fetches, or we don't + * know anything about any of the address families that would + * be acceptable so we have to launch fetches. + */ + + if (FIND_STARTATZONE(find)) + start_at_zone = ISC_TRUE; + + /* + * Start V4. + */ + if (WANT_INET(wanted_fetches) && + fetch_name(adbname, start_at_zone, + dns_rdatatype_a) == ISC_R_SUCCESS) { + DP(DEF_LEVEL, + "dns_adb_createfind: started A fetch for name %p", + adbname); + } + + /* + * Start V6. + */ + if (WANT_INET6(wanted_fetches) && + fetch_name(adbname, start_at_zone, + dns_rdatatype_aaaa) == ISC_R_SUCCESS) { + DP(DEF_LEVEL, + "dns_adb_createfind: " + "started AAAA fetch for name %p", + adbname); + } + } + + /* + * Run through the name and copy out the bits we are + * interested in. + */ + copy_namehook_lists(adb, find, qname, qtype, adbname, now); post_copy: - if (NAME_FETCH_V4(adbname)) - query_pending |= DNS_ADBFIND_INET; - if (NAME_FETCH_V6(adbname)) - query_pending |= DNS_ADBFIND_INET6; - - /* - * Attach to the name's query list if there are queries - * already running, and we have been asked to. - */ - want_event = ISC_TRUE; - if (!FIND_WANTEVENT(find)) - want_event = ISC_FALSE; - if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find)) - want_event = ISC_FALSE; - if ((wanted_addresses & query_pending) == 0) - want_event = ISC_FALSE; - if (alias) - want_event = ISC_FALSE; - if (want_event) { - find->adbname = adbname; - find->name_bucket = bucket; - ISC_LIST_APPEND(adbname->finds, find, plink); - find->query_pending = (query_pending & wanted_addresses); - find->flags &= ~DNS_ADBFIND_ADDRESSMASK; - find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK); - DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p", - find, adbname); - } else { - /* - * Remove the flag so the caller knows there will never - * be an event, and set internal flags to fake that - * the event was sent and freed, so dns_adb_destroyfind() will - * do the right thing. - */ - find->query_pending = (query_pending & wanted_addresses); - find->options &= ~DNS_ADBFIND_WANTEVENT; - find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED); - find->flags &= ~DNS_ADBFIND_ADDRESSMASK; - } - - find->partial_result |= (adbname->partial_result & wanted_addresses); - if (alias) { - if (target != NULL) { - result = dns_name_copy(&adbname->target, target, NULL); - if (result != ISC_R_SUCCESS) - goto out; - } - result = DNS_R_ALIAS; - } else - result = ISC_R_SUCCESS; - - /* - * Copy out error flags from the name structure into the find. - */ - find->result_v4 = find_err_map[adbname->fetch_err]; - find->result_v6 = find_err_map[adbname->fetch6_err]; + if (NAME_FETCH_V4(adbname)) + query_pending |= DNS_ADBFIND_INET; + if (NAME_FETCH_V6(adbname)) + query_pending |= DNS_ADBFIND_INET6; + + /* + * Attach to the name's query list if there are queries + * already running, and we have been asked to. + */ + want_event = ISC_TRUE; + if (!FIND_WANTEVENT(find)) + want_event = ISC_FALSE; + if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find)) + want_event = ISC_FALSE; + if ((wanted_addresses & query_pending) == 0) + want_event = ISC_FALSE; + if (alias) + want_event = ISC_FALSE; + if (want_event) { + find->adbname = adbname; + find->name_bucket = bucket; + ISC_LIST_APPEND(adbname->finds, find, plink); + find->query_pending = (query_pending & wanted_addresses); + find->flags &= ~DNS_ADBFIND_ADDRESSMASK; + find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK); + DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p", + find, adbname); + } else { + /* + * Remove the flag so the caller knows there will never + * be an event, and set internal flags to fake that + * the event was sent and freed, so dns_adb_destroyfind() will + * do the right thing. + */ + find->query_pending = (query_pending & wanted_addresses); + find->options &= ~DNS_ADBFIND_WANTEVENT; + find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED); + find->flags &= ~DNS_ADBFIND_ADDRESSMASK; + } + + find->partial_result |= (adbname->partial_result & wanted_addresses); + if (alias) { + if (target != NULL) { + result = dns_name_copy(&adbname->target, target, NULL); + if (result != ISC_R_SUCCESS) + goto out; + } + result = DNS_R_ALIAS; + } else + result = ISC_R_SUCCESS; + + /* + * Copy out error flags from the name structure into the find. + */ + find->result_v4 = find_err_map[adbname->fetch_err]; + find->result_v6 = find_err_map[adbname->fetch6_err]; out: - if (find != NULL) { - *findp = find; + if (find != NULL) { + *findp = find; - if (want_event) { - isc_task_t *taskp; + if (want_event) { + isc_task_t *taskp; - INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0); - taskp = NULL; - isc_task_attach(task, &taskp); - find->event.ev_sender = taskp; - find->event.ev_action = action; - find->event.ev_arg = arg; - } - } + INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0); + taskp = NULL; + isc_task_attach(task, &taskp); + find->event.ev_sender = taskp; + find->event.ev_action = action; + find->event.ev_arg = arg; + } + } - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&adb->namelocks[bucket]); - return (result); + return (result); } void dns_adb_destroyfind(dns_adbfind_t **findp) { - dns_adbfind_t *find; - dns_adbentry_t *entry; - dns_adbaddrinfo_t *ai; - int bucket; - dns_adb_t *adb; - - REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp)); - find = *findp; - *findp = NULL; - - LOCK(&find->lock); - - DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find); - - adb = find->adb; - REQUIRE(DNS_ADB_VALID(adb)); - - REQUIRE(FIND_EVENTFREED(find)); - - bucket = find->name_bucket; - INSIST(bucket == DNS_ADB_INVALIDBUCKET); - - UNLOCK(&find->lock); - - /* - * The find doesn't exist on any list, and nothing is locked. - * Return the find to the memory pool, and decrement the adb's - * reference count. - */ - ai = ISC_LIST_HEAD(find->list); - while (ai != NULL) { - ISC_LIST_UNLINK(find->list, ai, publink); - entry = ai->entry; - ai->entry = NULL; - INSIST(DNS_ADBENTRY_VALID(entry)); - RUNTIME_CHECK(dec_entry_refcnt(adb, entry, ISC_TRUE) == - ISC_FALSE); - free_adbaddrinfo(adb, &ai); - ai = ISC_LIST_HEAD(find->list); - } - - /* - * WARNING: The find is freed with the adb locked. This is done - * to avoid a race condition where we free the find, some other - * thread tests to see if it should be destroyed, detects it should - * be, destroys it, and then we try to lock it for our check, but the - * lock is destroyed. - */ - LOCK(&adb->lock); - if (free_adbfind(adb, &find)) - check_exit(adb); - UNLOCK(&adb->lock); + dns_adbfind_t *find; + dns_adbentry_t *entry; + dns_adbaddrinfo_t *ai; + int bucket; + dns_adb_t *adb; + + REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp)); + find = *findp; + *findp = NULL; + + LOCK(&find->lock); + + DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find); + + adb = find->adb; + REQUIRE(DNS_ADB_VALID(adb)); + + REQUIRE(FIND_EVENTFREED(find)); + + bucket = find->name_bucket; + INSIST(bucket == DNS_ADB_INVALIDBUCKET); + + UNLOCK(&find->lock); + + /* + * The find doesn't exist on any list, and nothing is locked. + * Return the find to the memory pool, and decrement the adb's + * reference count. + */ + ai = ISC_LIST_HEAD(find->list); + while (ai != NULL) { + ISC_LIST_UNLINK(find->list, ai, publink); + entry = ai->entry; + ai->entry = NULL; + INSIST(DNS_ADBENTRY_VALID(entry)); + RUNTIME_CHECK(dec_entry_refcnt(adb, entry, ISC_TRUE) == + ISC_FALSE); + free_adbaddrinfo(adb, &ai); + ai = ISC_LIST_HEAD(find->list); + } + + /* + * WARNING: The find is freed with the adb locked. This is done + * to avoid a race condition where we free the find, some other + * thread tests to see if it should be destroyed, detects it should + * be, destroys it, and then we try to lock it for our check, but the + * lock is destroyed. + */ + LOCK(&adb->lock); + if (free_adbfind(adb, &find)) + check_exit(adb); + UNLOCK(&adb->lock); } void dns_adb_cancelfind(dns_adbfind_t *find) { - isc_event_t *ev; - isc_task_t *task; - dns_adb_t *adb; - int bucket; - int unlock_bucket; - - LOCK(&find->lock); - - DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find); - - adb = find->adb; - REQUIRE(DNS_ADB_VALID(adb)); - - REQUIRE(!FIND_EVENTFREED(find)); - REQUIRE(FIND_WANTEVENT(find)); - - bucket = find->name_bucket; - if (bucket == DNS_ADB_INVALIDBUCKET) - goto cleanup; - - /* - * We need to get the adbname's lock to unlink the find. - */ - unlock_bucket = bucket; - violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]); - bucket = find->name_bucket; - if (bucket != DNS_ADB_INVALIDBUCKET) { - ISC_LIST_UNLINK(find->adbname->finds, find, plink); - find->adbname = NULL; - find->name_bucket = DNS_ADB_INVALIDBUCKET; - } - UNLOCK(&adb->namelocks[unlock_bucket]); - bucket = DNS_ADB_INVALIDBUCKET; + isc_event_t *ev; + isc_task_t *task; + dns_adb_t *adb; + int bucket; + int unlock_bucket; + + LOCK(&find->lock); + + DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find); + + adb = find->adb; + REQUIRE(DNS_ADB_VALID(adb)); + + REQUIRE(!FIND_EVENTFREED(find)); + REQUIRE(FIND_WANTEVENT(find)); + + bucket = find->name_bucket; + if (bucket == DNS_ADB_INVALIDBUCKET) + goto cleanup; + + /* + * We need to get the adbname's lock to unlink the find. + */ + unlock_bucket = bucket; + violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]); + bucket = find->name_bucket; + if (bucket != DNS_ADB_INVALIDBUCKET) { + ISC_LIST_UNLINK(find->adbname->finds, find, plink); + find->adbname = NULL; + find->name_bucket = DNS_ADB_INVALIDBUCKET; + } + UNLOCK(&adb->namelocks[unlock_bucket]); + bucket = DNS_ADB_INVALIDBUCKET; cleanup: - if (!FIND_EVENTSENT(find)) { - ev = &find->event; - task = ev->ev_sender; - ev->ev_sender = find; - ev->ev_type = DNS_EVENT_ADBCANCELED; - ev->ev_destroy = event_free; - ev->ev_destroy_arg = find; - find->result_v4 = ISC_R_CANCELED; - find->result_v6 = ISC_R_CANCELED; + if (!FIND_EVENTSENT(find)) { + ev = &find->event; + task = ev->ev_sender; + ev->ev_sender = find; + ev->ev_type = DNS_EVENT_ADBCANCELED; + ev->ev_destroy = event_free; + ev->ev_destroy_arg = find; + find->result_v4 = ISC_R_CANCELED; + find->result_v6 = ISC_R_CANCELED; - DP(DEF_LEVEL, "sending event %p to task %p for find %p", - ev, task, find); + DP(DEF_LEVEL, "sending event %p to task %p for find %p", + ev, task, find); - isc_task_sendanddetach(&task, (isc_event_t **)&ev); - } + isc_task_sendanddetach(&task, (isc_event_t **)&ev); + } - UNLOCK(&find->lock); + UNLOCK(&find->lock); } void dns_adb_dump(dns_adb_t *adb, FILE *f) { - int i; - isc_stdtime_t now; + int i; + isc_stdtime_t now; - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(f != NULL); + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(f != NULL); - /* - * Lock the adb itself, lock all the name buckets, then lock all - * the entry buckets. This should put the adb into a state where - * nothing can change, so we can iterate through everything and - * print at our leisure. - */ + /* + * Lock the adb itself, lock all the name buckets, then lock all + * the entry buckets. This should put the adb into a state where + * nothing can change, so we can iterate through everything and + * print at our leisure. + */ - LOCK(&adb->lock); - isc_stdtime_get(&now); + LOCK(&adb->lock); + isc_stdtime_get(&now); - for (i = 0; i < NBUCKETS; i++) - RUNTIME_CHECK(cleanup_names(adb, i, now) == ISC_FALSE); - for (i = 0; i < NBUCKETS; i++) - RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE); + for (i = 0; i < NBUCKETS; i++) + RUNTIME_CHECK(cleanup_names(adb, i, now) == ISC_FALSE); + for (i = 0; i < NBUCKETS; i++) + RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE); - dump_adb(adb, f, ISC_FALSE, now); - UNLOCK(&adb->lock); + dump_adb(adb, f, ISC_FALSE, now); + UNLOCK(&adb->lock); } static void dump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) { - if (value == INT_MAX) - return; - fprintf(f, " [%s TTL %d]", legend, value - now); + if (value == INT_MAX) + return; + fprintf(f, " [%s TTL %d]", legend, value - now); } static void dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) { - int i; - dns_adbname_t *name; - dns_adbentry_t *entry; - - fprintf(f, ";\n; Address database dump\n;\n"); - if (debug) - fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n", - adb, adb->erefcnt, adb->irefcnt, - isc_mempool_getallocated(adb->nhmp)); - - for (i = 0; i < NBUCKETS; i++) - LOCK(&adb->namelocks[i]); - for (i = 0; i < NBUCKETS; i++) - LOCK(&adb->entrylocks[i]); - - /* - * Dump the names - */ - for (i = 0; i < NBUCKETS; i++) { - name = ISC_LIST_HEAD(adb->names[i]); - if (name == NULL) - continue; - if (debug) - fprintf(f, "; bucket %d\n", i); - for (; - name != NULL; - name = ISC_LIST_NEXT(name, plink)) - { - if (debug) - fprintf(f, "; name %p (flags %08x)\n", - name, name->flags); - - fprintf(f, "; "); - print_dns_name(f, &name->name); - if (dns_name_countlabels(&name->target) > 0) { - fprintf(f, " alias "); - print_dns_name(f, &name->target); - } - - dump_ttl(f, "v4", name->expire_v4, now); - dump_ttl(f, "v6", name->expire_v6, now); - dump_ttl(f, "target", name->expire_target, now); - - fprintf(f, " [v4 %s] [v6 %s]", - errnames[name->fetch_err], - errnames[name->fetch6_err]); - - fprintf(f, "\n"); - - print_namehook_list(f, "v4", &name->v4, debug, now); - print_namehook_list(f, "v6", &name->v6, debug, now); - - if (debug) - print_fetch_list(f, name); - if (debug) - print_find_list(f, name); - - } - } - - fprintf(f, ";\n; Unassociated entries\n;\n"); - - for (i = 0; i < NBUCKETS; i++) { - entry = ISC_LIST_HEAD(adb->entries[i]); - while (entry != NULL) { - if (entry->refcnt == 0) - dump_entry(f, entry, debug, now); - entry = ISC_LIST_NEXT(entry, plink); - } - } - - /* - * Unlock everything - */ - for (i = 0; i < NBUCKETS; i++) - UNLOCK(&adb->entrylocks[i]); - for (i = 0; i < NBUCKETS; i++) - UNLOCK(&adb->namelocks[i]); + int i; + dns_adbname_t *name; + dns_adbentry_t *entry; + + fprintf(f, ";\n; Address database dump\n;\n"); + if (debug) + fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n", + adb, adb->erefcnt, adb->irefcnt, + isc_mempool_getallocated(adb->nhmp)); + + for (i = 0; i < NBUCKETS; i++) + LOCK(&adb->namelocks[i]); + for (i = 0; i < NBUCKETS; i++) + LOCK(&adb->entrylocks[i]); + + /* + * Dump the names + */ + for (i = 0; i < NBUCKETS; i++) { + name = ISC_LIST_HEAD(adb->names[i]); + if (name == NULL) + continue; + if (debug) + fprintf(f, "; bucket %d\n", i); + for (; + name != NULL; + name = ISC_LIST_NEXT(name, plink)) + { + if (debug) + fprintf(f, "; name %p (flags %08x)\n", + name, name->flags); + + fprintf(f, "; "); + print_dns_name(f, &name->name); + if (dns_name_countlabels(&name->target) > 0) { + fprintf(f, " alias "); + print_dns_name(f, &name->target); + } + + dump_ttl(f, "v4", name->expire_v4, now); + dump_ttl(f, "v6", name->expire_v6, now); + dump_ttl(f, "target", name->expire_target, now); + + fprintf(f, " [v4 %s] [v6 %s]", + errnames[name->fetch_err], + errnames[name->fetch6_err]); + + fprintf(f, "\n"); + + print_namehook_list(f, "v4", &name->v4, debug, now); + print_namehook_list(f, "v6", &name->v6, debug, now); + + if (debug) + print_fetch_list(f, name); + if (debug) + print_find_list(f, name); + + } + } + + fprintf(f, ";\n; Unassociated entries\n;\n"); + + for (i = 0; i < NBUCKETS; i++) { + entry = ISC_LIST_HEAD(adb->entries[i]); + while (entry != NULL) { + if (entry->refcnt == 0) + dump_entry(f, entry, debug, now); + entry = ISC_LIST_NEXT(entry, plink); + } + } + + /* + * Unlock everything + */ + for (i = 0; i < NBUCKETS; i++) + UNLOCK(&adb->entrylocks[i]); + for (i = 0; i < NBUCKETS; i++) + UNLOCK(&adb->namelocks[i]); } static void dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug, - isc_stdtime_t now) + isc_stdtime_t now) { - char addrbuf[ISC_NETADDR_FORMATSIZE]; - char typebuf[DNS_RDATATYPE_FORMATSIZE]; - isc_netaddr_t netaddr; - dns_adblameinfo_t *li; - - isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); - isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); - - if (debug) - fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); - - fprintf(f, ";\t%s [srtt %u] [flags %08x]", - addrbuf, entry->srtt, entry->flags); - if (entry->expires != 0) - fprintf(f, " [ttl %d]", entry->expires - now); - fprintf(f, "\n"); - for (li = ISC_LIST_HEAD(entry->lameinfo); - li != NULL; - li = ISC_LIST_NEXT(li, plink)) { - fprintf(f, ";\t\t"); - print_dns_name(f, &li->qname); - dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf)); - fprintf(f, " %s [lame TTL %d]\n", typebuf, - li->lame_timer - now); - } + char addrbuf[ISC_NETADDR_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + isc_netaddr_t netaddr; + dns_adblameinfo_t *li; + + isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); + isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); + + if (debug) + fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); + + fprintf(f, ";\t%s [srtt %u] [flags %08x]", + addrbuf, entry->srtt, entry->flags); + if (entry->expires != 0) + fprintf(f, " [ttl %d]", entry->expires - now); + fprintf(f, "\n"); + for (li = ISC_LIST_HEAD(entry->lameinfo); + li != NULL; + li = ISC_LIST_NEXT(li, plink)) { + fprintf(f, ";\t\t"); + print_dns_name(f, &li->qname); + dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf)); + fprintf(f, " %s [lame TTL %d]\n", typebuf, + li->lame_timer - now); + } } void dns_adb_dumpfind(dns_adbfind_t *find, FILE *f) { - char tmp[512]; - const char *tmpp; - dns_adbaddrinfo_t *ai; - isc_sockaddr_t *sa; - - /* - * Not used currently, in the API Just In Case we - * want to dump out the name and/or entries too. - */ - - LOCK(&find->lock); - - fprintf(f, ";Find %p\n", find); - fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n", - find->query_pending, find->partial_result, - find->options, find->flags); - fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n", - find->name_bucket, find->adbname, find->event.ev_sender); - - ai = ISC_LIST_HEAD(find->list); - if (ai != NULL) - fprintf(f, "\tAddresses:\n"); - while (ai != NULL) { - sa = &ai->sockaddr; - switch (sa->type.sa.sa_family) { - case AF_INET: - tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr, - tmp, sizeof(tmp)); - break; - case AF_INET6: - tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr, - tmp, sizeof(tmp)); - break; - default: - tmpp = "UnkFamily"; - } - - if (tmpp == NULL) - tmpp = "BadAddress"; - - fprintf(f, "\t\tentry %p, flags %08x" - " srtt %u addr %s\n", - ai->entry, ai->flags, ai->srtt, tmpp); - - ai = ISC_LIST_NEXT(ai, publink); - } - - UNLOCK(&find->lock); + char tmp[512]; + const char *tmpp; + dns_adbaddrinfo_t *ai; + isc_sockaddr_t *sa; + + /* + * Not used currently, in the API Just In Case we + * want to dump out the name and/or entries too. + */ + + LOCK(&find->lock); + + fprintf(f, ";Find %p\n", find); + fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n", + find->query_pending, find->partial_result, + find->options, find->flags); + fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n", + find->name_bucket, find->adbname, find->event.ev_sender); + + ai = ISC_LIST_HEAD(find->list); + if (ai != NULL) + fprintf(f, "\tAddresses:\n"); + while (ai != NULL) { + sa = &ai->sockaddr; + switch (sa->type.sa.sa_family) { + case AF_INET: + tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr, + tmp, sizeof(tmp)); + break; + case AF_INET6: + tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr, + tmp, sizeof(tmp)); + break; + default: + tmpp = "UnkFamily"; + } + + if (tmpp == NULL) + tmpp = "BadAddress"; + + fprintf(f, "\t\tentry %p, flags %08x" + " srtt %u addr %s\n", + ai->entry, ai->flags, ai->srtt, tmpp); + + ai = ISC_LIST_NEXT(ai, publink); + } + + UNLOCK(&find->lock); } static void print_dns_name(FILE *f, dns_name_t *name) { - char buf[DNS_NAME_FORMATSIZE]; + char buf[DNS_NAME_FORMATSIZE]; - INSIST(f != NULL); + INSIST(f != NULL); - dns_name_format(name, buf, sizeof(buf)); - fprintf(f, "%s", buf); + dns_name_format(name, buf, sizeof(buf)); + fprintf(f, "%s", buf); } static void print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list, - isc_boolean_t debug, isc_stdtime_t now) + isc_boolean_t debug, isc_stdtime_t now) { - dns_adbnamehook_t *nh; + dns_adbnamehook_t *nh; - for (nh = ISC_LIST_HEAD(*list); - nh != NULL; - nh = ISC_LIST_NEXT(nh, plink)) - { - if (debug) - fprintf(f, ";\tHook(%s) %p\n", legend, nh); - dump_entry(f, nh->entry, debug, now); - } + for (nh = ISC_LIST_HEAD(*list); + nh != NULL; + nh = ISC_LIST_NEXT(nh, plink)) + { + if (debug) + fprintf(f, ";\tHook(%s) %p\n", legend, nh); + dump_entry(f, nh->entry, debug, now); + } } static inline void print_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) { - fprintf(f, "\t\tFetch(%s): %p -> { nh %p, entry %p, fetch %p }\n", - type, ft, ft->namehook, ft->entry, ft->fetch); + fprintf(f, "\t\tFetch(%s): %p -> { nh %p, entry %p, fetch %p }\n", + type, ft, ft->namehook, ft->entry, ft->fetch); } static void print_fetch_list(FILE *f, dns_adbname_t *n) { - if (NAME_FETCH_A(n)) - print_fetch(f, n->fetch_a, "A"); - if (NAME_FETCH_AAAA(n)) - print_fetch(f, n->fetch_aaaa, "AAAA"); + if (NAME_FETCH_A(n)) + print_fetch(f, n->fetch_a, "A"); + if (NAME_FETCH_AAAA(n)) + print_fetch(f, n->fetch_aaaa, "AAAA"); } static void print_find_list(FILE *f, dns_adbname_t *name) { - dns_adbfind_t *find; + dns_adbfind_t *find; - find = ISC_LIST_HEAD(name->finds); - while (find != NULL) { - dns_adb_dumpfind(find, f); - find = ISC_LIST_NEXT(find, plink); - } + find = ISC_LIST_HEAD(name->finds); + while (find != NULL) { + dns_adb_dumpfind(find, f); + find = ISC_LIST_NEXT(find, plink); + } } static isc_result_t dbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) { - isc_result_t result; - dns_rdataset_t rdataset; - dns_adb_t *adb; - dns_fixedname_t foundname; - dns_name_t *fname; - - INSIST(DNS_ADBNAME_VALID(adbname)); - adb = adbname->adb; - INSIST(DNS_ADB_VALID(adb)); - INSIST(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa); - - dns_fixedname_init(&foundname); - fname = dns_fixedname_name(&foundname); - dns_rdataset_init(&rdataset); - - if (rdtype == dns_rdatatype_a) - adbname->fetch_err = FIND_ERR_UNEXPECTED; - else - adbname->fetch6_err = FIND_ERR_UNEXPECTED; - - result = dns_view_find(adb->view, &adbname->name, rdtype, now, - NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0, - ISC_TF(NAME_HINTOK(adbname)), - NULL, NULL, fname, &rdataset, NULL); - - /* XXXVIX this switch statement is too sparse to gen a jump table. */ - switch (result) { - case DNS_R_GLUE: - case DNS_R_HINT: - case ISC_R_SUCCESS: - /* - * Found in the database. Even if we can't copy out - * any information, return success, or else a fetch - * will be made, which will only make things worse. - */ - if (rdtype == dns_rdatatype_a) - adbname->fetch_err = FIND_ERR_SUCCESS; - else - adbname->fetch6_err = FIND_ERR_SUCCESS; - result = import_rdataset(adbname, &rdataset, now); - break; - case DNS_R_NXDOMAIN: - case DNS_R_NXRRSET: - /* - * We're authoritative and the data doesn't exist. - * Make up a negative cache entry so we don't ask again - * for a while. - * - * XXXRTH What time should we use? I'm putting in 30 seconds - * for now. - */ - if (rdtype == dns_rdatatype_a) { - adbname->expire_v4 = now + 30; - DP(NCACHE_LEVEL, - "adb name %p: Caching auth negative entry for A", - adbname); - if (result == DNS_R_NXDOMAIN) - adbname->fetch_err = FIND_ERR_NXDOMAIN; - else - adbname->fetch_err = FIND_ERR_NXRRSET; - } else { - DP(NCACHE_LEVEL, - "adb name %p: Caching auth negative entry for AAAA", - adbname); - adbname->expire_v6 = now + 30; - if (result == DNS_R_NXDOMAIN) - adbname->fetch6_err = FIND_ERR_NXDOMAIN; - else - adbname->fetch6_err = FIND_ERR_NXRRSET; - } - break; - case DNS_R_NCACHENXDOMAIN: - case DNS_R_NCACHENXRRSET: - /* - * We found a negative cache entry. Pull the TTL from it - * so we won't ask again for a while. - */ - rdataset.ttl = ttlclamp(rdataset.ttl); - if (rdtype == dns_rdatatype_a) { - adbname->expire_v4 = rdataset.ttl + now; - if (result == DNS_R_NCACHENXDOMAIN) - adbname->fetch_err = FIND_ERR_NXDOMAIN; - else - adbname->fetch_err = FIND_ERR_NXRRSET; - DP(NCACHE_LEVEL, - "adb name %p: Caching negative entry for A (ttl %u)", - adbname, rdataset.ttl); - } else { - DP(NCACHE_LEVEL, - "adb name %p: Caching negative entry for AAAA (ttl %u)", - adbname, rdataset.ttl); - adbname->expire_v6 = rdataset.ttl + now; - if (result == DNS_R_NCACHENXDOMAIN) - adbname->fetch6_err = FIND_ERR_NXDOMAIN; - else - adbname->fetch6_err = FIND_ERR_NXRRSET; - } - break; - case DNS_R_CNAME: - case DNS_R_DNAME: - /* - * Clear the hint and glue flags, so this will match - * more often. - */ - adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK); - - rdataset.ttl = ttlclamp(rdataset.ttl); - clean_target(adb, &adbname->target); - adbname->expire_target = INT_MAX; - result = set_target(adb, &adbname->name, fname, &rdataset, - &adbname->target); - if (result == ISC_R_SUCCESS) { - result = DNS_R_ALIAS; - DP(NCACHE_LEVEL, - "adb name %p: caching alias target", - adbname); - adbname->expire_target = rdataset.ttl + now; - } - if (rdtype == dns_rdatatype_a) - adbname->fetch_err = FIND_ERR_SUCCESS; - else - adbname->fetch6_err = FIND_ERR_SUCCESS; - break; - } - - if (dns_rdataset_isassociated(&rdataset)) - dns_rdataset_disassociate(&rdataset); - - return (result); + isc_result_t result; + dns_rdataset_t rdataset; + dns_adb_t *adb; + dns_fixedname_t foundname; + dns_name_t *fname; + + INSIST(DNS_ADBNAME_VALID(adbname)); + adb = adbname->adb; + INSIST(DNS_ADB_VALID(adb)); + INSIST(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa); + + dns_fixedname_init(&foundname); + fname = dns_fixedname_name(&foundname); + dns_rdataset_init(&rdataset); + + if (rdtype == dns_rdatatype_a) + adbname->fetch_err = FIND_ERR_UNEXPECTED; + else + adbname->fetch6_err = FIND_ERR_UNEXPECTED; + + result = dns_view_find(adb->view, &adbname->name, rdtype, now, + NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0, + ISC_TF(NAME_HINTOK(adbname)), + NULL, NULL, fname, &rdataset, NULL); + + /* XXXVIX this switch statement is too sparse to gen a jump table. */ + switch (result) { + case DNS_R_GLUE: + case DNS_R_HINT: + case ISC_R_SUCCESS: + /* + * Found in the database. Even if we can't copy out + * any information, return success, or else a fetch + * will be made, which will only make things worse. + */ + if (rdtype == dns_rdatatype_a) + adbname->fetch_err = FIND_ERR_SUCCESS; + else + adbname->fetch6_err = FIND_ERR_SUCCESS; + result = import_rdataset(adbname, &rdataset, now); + break; + case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: + /* + * We're authoritative and the data doesn't exist. + * Make up a negative cache entry so we don't ask again + * for a while. + * + * XXXRTH What time should we use? I'm putting in 30 seconds + * for now. + */ + if (rdtype == dns_rdatatype_a) { + adbname->expire_v4 = now + 30; + DP(NCACHE_LEVEL, + "adb name %p: Caching auth negative entry for A", + adbname); + if (result == DNS_R_NXDOMAIN) + adbname->fetch_err = FIND_ERR_NXDOMAIN; + else + adbname->fetch_err = FIND_ERR_NXRRSET; + } else { + DP(NCACHE_LEVEL, + "adb name %p: Caching auth negative entry for AAAA", + adbname); + adbname->expire_v6 = now + 30; + if (result == DNS_R_NXDOMAIN) + adbname->fetch6_err = FIND_ERR_NXDOMAIN; + else + adbname->fetch6_err = FIND_ERR_NXRRSET; + } + break; + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + /* + * We found a negative cache entry. Pull the TTL from it + * so we won't ask again for a while. + */ + rdataset.ttl = ttlclamp(rdataset.ttl); + if (rdtype == dns_rdatatype_a) { + adbname->expire_v4 = rdataset.ttl + now; + if (result == DNS_R_NCACHENXDOMAIN) + adbname->fetch_err = FIND_ERR_NXDOMAIN; + else + adbname->fetch_err = FIND_ERR_NXRRSET; + DP(NCACHE_LEVEL, + "adb name %p: Caching negative entry for A (ttl %u)", + adbname, rdataset.ttl); + } else { + DP(NCACHE_LEVEL, + "adb name %p: Caching negative entry for AAAA (ttl %u)", + adbname, rdataset.ttl); + adbname->expire_v6 = rdataset.ttl + now; + if (result == DNS_R_NCACHENXDOMAIN) + adbname->fetch6_err = FIND_ERR_NXDOMAIN; + else + adbname->fetch6_err = FIND_ERR_NXRRSET; + } + break; + case DNS_R_CNAME: + case DNS_R_DNAME: + /* + * Clear the hint and glue flags, so this will match + * more often. + */ + adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK); + + rdataset.ttl = ttlclamp(rdataset.ttl); + clean_target(adb, &adbname->target); + adbname->expire_target = INT_MAX; + result = set_target(adb, &adbname->name, fname, &rdataset, + &adbname->target); + if (result == ISC_R_SUCCESS) { + result = DNS_R_ALIAS; + DP(NCACHE_LEVEL, + "adb name %p: caching alias target", + adbname); + adbname->expire_target = rdataset.ttl + now; + } + if (rdtype == dns_rdatatype_a) + adbname->fetch_err = FIND_ERR_SUCCESS; + else + adbname->fetch6_err = FIND_ERR_SUCCESS; + break; + } + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + + return (result); } static void fetch_callback(isc_task_t *task, isc_event_t *ev) { - dns_fetchevent_t *dev; - dns_adbname_t *name; - dns_adb_t *adb; - dns_adbfetch_t *fetch; - int bucket; - isc_eventtype_t ev_status; - isc_stdtime_t now; - isc_result_t result; - unsigned int address_type; - isc_boolean_t want_check_exit = ISC_FALSE; - - UNUSED(task); - - INSIST(ev->ev_type == DNS_EVENT_FETCHDONE); - dev = (dns_fetchevent_t *)ev; - name = ev->ev_arg; - INSIST(DNS_ADBNAME_VALID(name)); - adb = name->adb; - INSIST(DNS_ADB_VALID(adb)); - - bucket = name->lock_bucket; - LOCK(&adb->namelocks[bucket]); - - INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name)); - address_type = 0; - if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) { - address_type = DNS_ADBFIND_INET; - fetch = name->fetch_a; - name->fetch_a = NULL; - } else if (NAME_FETCH_AAAA(name) - && (name->fetch_aaaa->fetch == dev->fetch)) { - address_type = DNS_ADBFIND_INET6; - fetch = name->fetch_aaaa; - name->fetch_aaaa = NULL; - } - INSIST(address_type != 0); - - dns_resolver_destroyfetch(&fetch->fetch); - dev->fetch = NULL; - - ev_status = DNS_EVENT_ADBNOMOREADDRESSES; - - /* - * Cleanup things we don't care about. - */ - if (dev->node != NULL) - dns_db_detachnode(dev->db, &dev->node); - if (dev->db != NULL) - dns_db_detach(&dev->db); - - /* - * If this name is marked as dead, clean up, throwing away - * potentially good data. - */ - if (NAME_DEAD(name)) { - free_adbfetch(adb, &fetch); - isc_event_free(&ev); - - want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED); - - UNLOCK(&adb->namelocks[bucket]); - - if (want_check_exit) { - LOCK(&adb->lock); - check_exit(adb); - UNLOCK(&adb->lock); - } - - return; - } - - isc_stdtime_get(&now); - - /* - * If we got a negative cache response, remember it. - */ - if (NCACHE_RESULT(dev->result)) { - dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); - if (address_type == DNS_ADBFIND_INET) { - DP(NCACHE_LEVEL, "adb fetch name %p: " - "caching negative entry for A (ttl %u)", - name, dev->rdataset->ttl); - name->expire_v4 = ISC_MIN(name->expire_v4, - dev->rdataset->ttl + now); - if (dev->result == DNS_R_NCACHENXDOMAIN) - name->fetch_err = FIND_ERR_NXDOMAIN; - else - name->fetch_err = FIND_ERR_NXRRSET; - } else { - DP(NCACHE_LEVEL, "adb fetch name %p: " - "caching negative entry for AAAA (ttl %u)", - name, dev->rdataset->ttl); - name->expire_v6 = ISC_MIN(name->expire_v6, - dev->rdataset->ttl + now); - if (dev->result == DNS_R_NCACHENXDOMAIN) - name->fetch6_err = FIND_ERR_NXDOMAIN; - else - name->fetch6_err = FIND_ERR_NXRRSET; - } - goto out; - } - - /* - * Handle CNAME/DNAME. - */ - if (dev->result == DNS_R_CNAME || dev->result == DNS_R_DNAME) { - dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); - clean_target(adb, &name->target); - name->expire_target = INT_MAX; - result = set_target(adb, &name->name, - dns_fixedname_name(&dev->foundname), - dev->rdataset, - &name->target); - if (result == ISC_R_SUCCESS) { - DP(NCACHE_LEVEL, - "adb fetch name %p: caching alias target", - name); - name->expire_target = dev->rdataset->ttl + now; - } - goto check_result; - } - - /* - * Did we get back junk? If so, and there are no more fetches - * sitting out there, tell all the finds about it. - */ - if (dev->result != ISC_R_SUCCESS) { - char buf[DNS_NAME_FORMATSIZE]; - - dns_name_format(&name->name, buf, sizeof(buf)); - DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s", - buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA", - dns_result_totext(dev->result)); - /* XXXMLG Don't pound on bad servers. */ - if (address_type == DNS_ADBFIND_INET) { - name->expire_v4 = ISC_MIN(name->expire_v4, now + 300); - name->fetch_err = FIND_ERR_FAILURE; - } else { - name->expire_v6 = ISC_MIN(name->expire_v6, now + 300); - name->fetch6_err = FIND_ERR_FAILURE; - } - goto out; - } - - /* - * We got something potentially useful. - */ - result = import_rdataset(name, &fetch->rdataset, now); + dns_fetchevent_t *dev; + dns_adbname_t *name; + dns_adb_t *adb; + dns_adbfetch_t *fetch; + int bucket; + isc_eventtype_t ev_status; + isc_stdtime_t now; + isc_result_t result; + unsigned int address_type; + isc_boolean_t want_check_exit = ISC_FALSE; + + UNUSED(task); + + INSIST(ev->ev_type == DNS_EVENT_FETCHDONE); + dev = (dns_fetchevent_t *)ev; + name = ev->ev_arg; + INSIST(DNS_ADBNAME_VALID(name)); + adb = name->adb; + INSIST(DNS_ADB_VALID(adb)); + + bucket = name->lock_bucket; + LOCK(&adb->namelocks[bucket]); + + INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name)); + address_type = 0; + if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) { + address_type = DNS_ADBFIND_INET; + fetch = name->fetch_a; + name->fetch_a = NULL; + } else if (NAME_FETCH_AAAA(name) + && (name->fetch_aaaa->fetch == dev->fetch)) { + address_type = DNS_ADBFIND_INET6; + fetch = name->fetch_aaaa; + name->fetch_aaaa = NULL; + } + INSIST(address_type != 0); + + dns_resolver_destroyfetch(&fetch->fetch); + dev->fetch = NULL; + + ev_status = DNS_EVENT_ADBNOMOREADDRESSES; + + /* + * Cleanup things we don't care about. + */ + if (dev->node != NULL) + dns_db_detachnode(dev->db, &dev->node); + if (dev->db != NULL) + dns_db_detach(&dev->db); + + /* + * If this name is marked as dead, clean up, throwing away + * potentially good data. + */ + if (NAME_DEAD(name)) { + free_adbfetch(adb, &fetch); + isc_event_free(&ev); + + want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED, + ISC_FALSE); + + UNLOCK(&adb->namelocks[bucket]); + + if (want_check_exit) { + LOCK(&adb->lock); + check_exit(adb); + UNLOCK(&adb->lock); + } + + return; + } + + isc_stdtime_get(&now); + + /* + * If we got a negative cache response, remember it. + */ + if (NCACHE_RESULT(dev->result)) { + dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); + if (address_type == DNS_ADBFIND_INET) { + DP(NCACHE_LEVEL, "adb fetch name %p: " + "caching negative entry for A (ttl %u)", + name, dev->rdataset->ttl); + name->expire_v4 = ISC_MIN(name->expire_v4, + dev->rdataset->ttl + now); + if (dev->result == DNS_R_NCACHENXDOMAIN) + name->fetch_err = FIND_ERR_NXDOMAIN; + else + name->fetch_err = FIND_ERR_NXRRSET; + } else { + DP(NCACHE_LEVEL, "adb fetch name %p: " + "caching negative entry for AAAA (ttl %u)", + name, dev->rdataset->ttl); + name->expire_v6 = ISC_MIN(name->expire_v6, + dev->rdataset->ttl + now); + if (dev->result == DNS_R_NCACHENXDOMAIN) + name->fetch6_err = FIND_ERR_NXDOMAIN; + else + name->fetch6_err = FIND_ERR_NXRRSET; + } + goto out; + } + + /* + * Handle CNAME/DNAME. + */ + if (dev->result == DNS_R_CNAME || dev->result == DNS_R_DNAME) { + dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); + clean_target(adb, &name->target); + name->expire_target = INT_MAX; + result = set_target(adb, &name->name, + dns_fixedname_name(&dev->foundname), + dev->rdataset, + &name->target); + if (result == ISC_R_SUCCESS) { + DP(NCACHE_LEVEL, + "adb fetch name %p: caching alias target", + name); + name->expire_target = dev->rdataset->ttl + now; + } + goto check_result; + } + + /* + * Did we get back junk? If so, and there are no more fetches + * sitting out there, tell all the finds about it. + */ + if (dev->result != ISC_R_SUCCESS) { + char buf[DNS_NAME_FORMATSIZE]; + + dns_name_format(&name->name, buf, sizeof(buf)); + DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s", + buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA", + dns_result_totext(dev->result)); + /* XXXMLG Don't pound on bad servers. */ + if (address_type == DNS_ADBFIND_INET) { + name->expire_v4 = ISC_MIN(name->expire_v4, now + 300); + name->fetch_err = FIND_ERR_FAILURE; + } else { + name->expire_v6 = ISC_MIN(name->expire_v6, now + 300); + name->fetch6_err = FIND_ERR_FAILURE; + } + goto out; + } + + /* + * We got something potentially useful. + */ + result = import_rdataset(name, &fetch->rdataset, now); check_result: - if (result == ISC_R_SUCCESS) { - ev_status = DNS_EVENT_ADBMOREADDRESSES; - if (address_type == DNS_ADBFIND_INET) - name->fetch_err = FIND_ERR_SUCCESS; - else - name->fetch6_err = FIND_ERR_SUCCESS; - } + if (result == ISC_R_SUCCESS) { + ev_status = DNS_EVENT_ADBMOREADDRESSES; + if (address_type == DNS_ADBFIND_INET) + name->fetch_err = FIND_ERR_SUCCESS; + else + name->fetch6_err = FIND_ERR_SUCCESS; + } out: - free_adbfetch(adb, &fetch); - isc_event_free(&ev); + free_adbfetch(adb, &fetch); + isc_event_free(&ev); - clean_finds_at_name(name, ev_status, address_type); + clean_finds_at_name(name, ev_status, address_type); - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&adb->namelocks[bucket]); } static isc_result_t fetch_name(dns_adbname_t *adbname, - isc_boolean_t start_at_zone, - dns_rdatatype_t type) + isc_boolean_t start_at_zone, + dns_rdatatype_t type) { - isc_result_t result; - dns_adbfetch_t *fetch = NULL; - dns_adb_t *adb; - dns_fixedname_t fixed; - dns_name_t *name; - dns_rdataset_t rdataset; - dns_rdataset_t *nameservers; - unsigned int options; - - INSIST(DNS_ADBNAME_VALID(adbname)); - adb = adbname->adb; - INSIST(DNS_ADB_VALID(adb)); - - INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) || - (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname))); - - adbname->fetch_err = FIND_ERR_NOTFOUND; - - name = NULL; - nameservers = NULL; - dns_rdataset_init(&rdataset); - - options = DNS_FETCHOPT_NOVALIDATE; - if (start_at_zone) { - DP(ENTER_LEVEL, - "fetch_name: starting at zone for name %p", - adbname); - dns_fixedname_init(&fixed); - name = dns_fixedname_name(&fixed); - result = dns_view_findzonecut2(adb->view, &adbname->name, name, - 0, 0, ISC_TRUE, ISC_FALSE, - &rdataset, NULL); - if (result != ISC_R_SUCCESS && result != DNS_R_HINT) - goto cleanup; - nameservers = &rdataset; - options |= DNS_FETCHOPT_UNSHARED; - } - - fetch = new_adbfetch(adb); - if (fetch == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; - } - - result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, - type, name, nameservers, NULL, - options, adb->task, fetch_callback, - adbname, &fetch->rdataset, NULL, - &fetch->fetch); - if (result != ISC_R_SUCCESS) - goto cleanup; - - if (type == dns_rdatatype_a) - adbname->fetch_a = fetch; - else - adbname->fetch_aaaa = fetch; - fetch = NULL; /* Keep us from cleaning this up below. */ + isc_result_t result; + dns_adbfetch_t *fetch = NULL; + dns_adb_t *adb; + dns_fixedname_t fixed; + dns_name_t *name; + dns_rdataset_t rdataset; + dns_rdataset_t *nameservers; + unsigned int options; + + INSIST(DNS_ADBNAME_VALID(adbname)); + adb = adbname->adb; + INSIST(DNS_ADB_VALID(adb)); + + INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) || + (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname))); + + adbname->fetch_err = FIND_ERR_NOTFOUND; + + name = NULL; + nameservers = NULL; + dns_rdataset_init(&rdataset); + + options = DNS_FETCHOPT_NOVALIDATE; + if (start_at_zone) { + DP(ENTER_LEVEL, + "fetch_name: starting at zone for name %p", + adbname); + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + result = dns_view_findzonecut2(adb->view, &adbname->name, name, + 0, 0, ISC_TRUE, ISC_FALSE, + &rdataset, NULL); + if (result != ISC_R_SUCCESS && result != DNS_R_HINT) + goto cleanup; + nameservers = &rdataset; + options |= DNS_FETCHOPT_UNSHARED; + } + + fetch = new_adbfetch(adb); + if (fetch == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, + type, name, nameservers, NULL, + options, adb->task, fetch_callback, + adbname, &fetch->rdataset, NULL, + &fetch->fetch); + if (result != ISC_R_SUCCESS) + goto cleanup; + + if (type == dns_rdatatype_a) + adbname->fetch_a = fetch; + else + adbname->fetch_aaaa = fetch; + fetch = NULL; /* Keep us from cleaning this up below. */ cleanup: - if (fetch != NULL) - free_adbfetch(adb, &fetch); - if (dns_rdataset_isassociated(&rdataset)) - dns_rdataset_disassociate(&rdataset); + if (fetch != NULL) + free_adbfetch(adb, &fetch); + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); - return (result); + return (result); } /* @@ -3344,265 +3544,315 @@ fetch_name(dns_adbname_t *adbname, */ isc_result_t dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *qname, - dns_rdatatype_t qtype, isc_stdtime_t expire_time) + dns_rdatatype_t qtype, isc_stdtime_t expire_time) { - dns_adblameinfo_t *li; - int bucket; - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - REQUIRE(qname != NULL); - - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); - li = ISC_LIST_HEAD(addr->entry->lameinfo); - while (li != NULL && - (li->qtype != qtype || !dns_name_equal(qname, &li->qname))) - li = ISC_LIST_NEXT(li, plink); - if (li != NULL) { - if (expire_time > li->lame_timer) - li->lame_timer = expire_time; - goto unlock; - } - li = new_adblameinfo(adb, qname, qtype); - if (li == NULL) { - result = ISC_R_NOMEMORY; - goto unlock; - } - - li->lame_timer = expire_time; - - ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink); + dns_adblameinfo_t *li; + int bucket; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + REQUIRE(qname != NULL); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + li = ISC_LIST_HEAD(addr->entry->lameinfo); + while (li != NULL && + (li->qtype != qtype || !dns_name_equal(qname, &li->qname))) + li = ISC_LIST_NEXT(li, plink); + if (li != NULL) { + if (expire_time > li->lame_timer) + li->lame_timer = expire_time; + goto unlock; + } + li = new_adblameinfo(adb, qname, qtype); + if (li == NULL) { + result = ISC_R_NOMEMORY; + goto unlock; + } + + li->lame_timer = expire_time; + + ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink); unlock: - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&adb->entrylocks[bucket]); - return (result); + return (result); } void dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, - unsigned int rtt, unsigned int factor) + unsigned int rtt, unsigned int factor) { - int bucket; - unsigned int new_srtt; - isc_stdtime_t now; + int bucket; + unsigned int new_srtt; + isc_stdtime_t now; - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - REQUIRE(factor <= 10); + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + REQUIRE(factor <= 10); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); - if (factor == DNS_ADB_RTTADJAGE) - new_srtt = addr->entry->srtt * 98 / 100; - else - new_srtt = (addr->entry->srtt / 10 * factor) - + (rtt / 10 * (10 - factor)); + if (factor == DNS_ADB_RTTADJAGE) + new_srtt = addr->entry->srtt * 98 / 100; + else + new_srtt = (addr->entry->srtt / 10 * factor) + + (rtt / 10 * (10 - factor)); - addr->entry->srtt = new_srtt; - addr->srtt = new_srtt; + addr->entry->srtt = new_srtt; + addr->srtt = new_srtt; - isc_stdtime_get(&now); - addr->entry->expires = now + ADB_ENTRY_WINDOW; + isc_stdtime_get(&now); + addr->entry->expires = now + ADB_ENTRY_WINDOW; - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&adb->entrylocks[bucket]); } void dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, - unsigned int bits, unsigned int mask) + unsigned int bits, unsigned int mask) { - int bucket; + int bucket; - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); - addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); - /* - * Note that we do not update the other bits in addr->flags with - * the most recent values from addr->entry->flags. - */ - addr->flags = (addr->flags & ~mask) | (bits & mask); + addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); + /* + * Note that we do not update the other bits in addr->flags with + * the most recent values from addr->entry->flags. + */ + addr->flags = (addr->flags & ~mask) | (bits & mask); - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&adb->entrylocks[bucket]); } isc_result_t dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, - dns_adbaddrinfo_t **addrp, isc_stdtime_t now) + dns_adbaddrinfo_t **addrp, isc_stdtime_t now) { - int bucket; - dns_adbentry_t *entry; - dns_adbaddrinfo_t *addr; - isc_result_t result; - in_port_t port; - - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(addrp != NULL && *addrp == NULL); - - UNUSED(now); - - result = ISC_R_SUCCESS; - bucket = DNS_ADB_INVALIDBUCKET; - entry = find_entry_and_lock(adb, sa, &bucket); - if (adb->entry_sd[bucket]) { - result = ISC_R_SHUTTINGDOWN; - goto unlock; - } - if (entry == NULL) { - /* - * We don't know anything about this address. - */ - entry = new_adbentry(adb); - if (entry == NULL) { - result = ISC_R_NOMEMORY; - goto unlock; - } - entry->sockaddr = *sa; - link_entry(adb, bucket, entry); - DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry); - } else - DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry); - - port = isc_sockaddr_getport(sa); - addr = new_adbaddrinfo(adb, entry, port); - if (addr != NULL) { - inc_entry_refcnt(adb, entry, ISC_FALSE); - *addrp = addr; - } + int bucket; + dns_adbentry_t *entry; + dns_adbaddrinfo_t *addr; + isc_result_t result; + in_port_t port; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(addrp != NULL && *addrp == NULL); + + UNUSED(now); + + result = ISC_R_SUCCESS; + bucket = DNS_ADB_INVALIDBUCKET; + entry = find_entry_and_lock(adb, sa, &bucket); + if (adb->entry_sd[bucket]) { + result = ISC_R_SHUTTINGDOWN; + goto unlock; + } + if (entry == NULL) { + /* + * We don't know anything about this address. + */ + entry = new_adbentry(adb); + if (entry == NULL) { + result = ISC_R_NOMEMORY; + goto unlock; + } + entry->sockaddr = *sa; + link_entry(adb, bucket, entry); + DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry); + } else + DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry); + + port = isc_sockaddr_getport(sa); + addr = new_adbaddrinfo(adb, entry, port); + if (addr == NULL) { + result = ISC_R_NOMEMORY; + } else { + inc_entry_refcnt(adb, entry, ISC_FALSE); + *addrp = addr; + } unlock: - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&adb->entrylocks[bucket]); - return (result); + return (result); } void dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) { - dns_adbaddrinfo_t *addr; - dns_adbentry_t *entry; - int bucket; - isc_stdtime_t now; - isc_boolean_t want_check_exit = ISC_FALSE; + dns_adbaddrinfo_t *addr; + dns_adbentry_t *entry; + int bucket; + isc_stdtime_t now; + isc_boolean_t want_check_exit = ISC_FALSE; - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(addrp != NULL); - addr = *addrp; - REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - entry = addr->entry; - REQUIRE(DNS_ADBENTRY_VALID(entry)); + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(addrp != NULL); + addr = *addrp; + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + entry = addr->entry; + REQUIRE(DNS_ADBENTRY_VALID(entry)); - isc_stdtime_get(&now); + isc_stdtime_get(&now); - *addrp = NULL; + *addrp = NULL; - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); - entry->expires = now + ADB_ENTRY_WINDOW; + entry->expires = now + ADB_ENTRY_WINDOW; - want_check_exit = dec_entry_refcnt(adb, entry, ISC_FALSE); + want_check_exit = dec_entry_refcnt(adb, entry, ISC_FALSE); - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&adb->entrylocks[bucket]); - addr->entry = NULL; - free_adbaddrinfo(adb, &addr); + addr->entry = NULL; + free_adbaddrinfo(adb, &addr); - if (want_check_exit) { - LOCK(&adb->lock); - check_exit(adb); - UNLOCK(&adb->lock); - } + if (want_check_exit) { + LOCK(&adb->lock); + check_exit(adb); + UNLOCK(&adb->lock); + } } void dns_adb_flush(dns_adb_t *adb) { - unsigned int i; + unsigned int i; - INSIST(DNS_ADB_VALID(adb)); + INSIST(DNS_ADB_VALID(adb)); - LOCK(&adb->lock); + LOCK(&adb->lock); - /* - * Call our cleanup routines. - */ - for (i = 0; i < NBUCKETS; i++) - RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE); - for (i = 0; i < NBUCKETS; i++) - RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE); + /* + * Call our cleanup routines. + */ + for (i = 0; i < NBUCKETS; i++) + RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE); + for (i = 0; i < NBUCKETS; i++) + RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE); #ifdef DUMP_ADB_AFTER_CLEANING - dump_adb(adb, stdout, ISC_TRUE, INT_MAX); + dump_adb(adb, stdout, ISC_TRUE, INT_MAX); #endif - UNLOCK(&adb->lock); + UNLOCK(&adb->lock); } void dns_adb_flushname(dns_adb_t *adb, dns_name_t *name) { - dns_adbname_t *adbname; - dns_adbname_t *nextname; - int bucket; - - INSIST(DNS_ADB_VALID(adb)); - - LOCK(&adb->lock); - bucket = dns_name_hash(name, ISC_FALSE) % NBUCKETS; - LOCK(&adb->namelocks[bucket]); - adbname = ISC_LIST_HEAD(adb->names[bucket]); - while (adbname != NULL) { - nextname = ISC_LIST_NEXT(adbname, plink); - if (!NAME_DEAD(adbname) && - dns_name_equal(name, &adbname->name)) { - RUNTIME_CHECK(kill_name(&adbname, - DNS_EVENT_ADBCANCELED) == - ISC_FALSE); - } - adbname = nextname; - } - UNLOCK(&adb->namelocks[bucket]); - UNLOCK(&adb->lock); + dns_adbname_t *adbname; + dns_adbname_t *nextname; + int bucket; + + INSIST(DNS_ADB_VALID(adb)); + + LOCK(&adb->lock); + bucket = dns_name_hash(name, ISC_FALSE) % NBUCKETS; + LOCK(&adb->namelocks[bucket]); + adbname = ISC_LIST_HEAD(adb->names[bucket]); + while (adbname != NULL) { + nextname = ISC_LIST_NEXT(adbname, plink); + if (!NAME_DEAD(adbname) && + dns_name_equal(name, &adbname->name)) { + RUNTIME_CHECK(kill_name(&adbname, + DNS_EVENT_ADBCANCELED, + ISC_TRUE) == + ISC_FALSE); + } + adbname = nextname; + } + UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&adb->lock); } static void water(void *arg, int mark) { - dns_adb_t *adb = arg; - isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); - isc_interval_t interval; + dns_adb_t *adb = arg; + isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); + + REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(DNS_ADB_VALID(adb)); + DP(ISC_LOG_DEBUG(1), + "adb reached %s water mark", overmem ? "high" : "low"); - DP(ISC_LOG_DEBUG(1), - "adb reached %s water mark", overmem ? "high" : "low"); + adb->overmem = overmem; +#if 0 /* we don't need this timer for the new cleaning policy. */ + if (overmem) { + isc_interval_t interval; - adb->overmem = overmem; - if (overmem) { - isc_interval_set(&interval, 0, 1); - (void)isc_timer_reset(adb->timer, isc_timertype_once, NULL, - &interval, ISC_TRUE); - } + isc_interval_set(&interval, 0, 1); + (void)isc_timer_reset(adb->timer, isc_timertype_once, NULL, + &interval, ISC_TRUE); + } +#endif } void dns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size) { - isc_uint32_t hiwater; - isc_uint32_t lowater; + isc_uint32_t hiwater; + isc_uint32_t lowater; - INSIST(DNS_ADB_VALID(adb)); + INSIST(DNS_ADB_VALID(adb)); - if (size != 0 && size < DNS_ADB_MINADBSIZE) - size = DNS_ADB_MINADBSIZE; + if (size != 0 && size < DNS_ADB_MINADBSIZE) + size = DNS_ADB_MINADBSIZE; - hiwater = size - (size >> 3); /* Approximately 7/8ths. */ - lowater = size - (size >> 2); /* Approximately 3/4ths. */ + hiwater = size - (size >> 3); /* Approximately 7/8ths. */ + lowater = size - (size >> 2); /* Approximately 3/4ths. */ - if (size == 0 || hiwater == 0 || lowater == 0) - isc_mem_setwater(adb->mctx, water, adb, 0, 0); - else - isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater); + if (size == 0 || hiwater == 0 || lowater == 0) + isc_mem_setwater(adb->mctx, water, adb, 0, 0); + else + isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater); } + +#ifdef LRU_DEBUG +/* + * Periodic dumping of the internal state of the statistics. + * This will dump the cache contents, uses, record types, etc. + */ +static void +timer_dump(isc_task_t *task, isc_event_t *ev) { + dns_adb_t *adb; + isc_interval_t interval; + isc_time_t nexttime; + + UNUSED(task); + + adb = ev->ev_arg; + INSIST(DNS_ADB_VALID(adb)); + + LOCK(&adb->lock); + if (adb->nname > 0 || adb->nentry > 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_ADB, ISC_LOG_INFO, + "ADB memory usage %p: mem inuse %lu, " + "%u/%u names, %u/%u entries, " + "purge/scan=%u(%u,%u)/%u, overmem=%d", + adb, (unsigned long)isc_mem_inuse(adb->mctx), + adb->nname, adb->nname_total, + adb->nentry, adb->nentry_total, + adb->stale_purge, adb->stale_expire, + adb->stale_lru, adb->stale_scan, adb->overmem); + } + + interval.seconds = DUMP_INTERVAL; + interval.nanoseconds = 0; + + RUNTIME_CHECK(isc_time_add(&adb->dump_time, &interval, &nexttime) == + ISC_R_SUCCESS); /* XXX: this is not always true */ + adb->dump_time = nexttime; + (void)isc_timer_reset(adb->dump_timer, isc_timertype_once, + &adb->dump_time, NULL, ISC_FALSE); + UNLOCK(&adb->lock); + + isc_event_free(&ev); +} +#endif diff --git a/lib/dns/cache.c b/lib/dns/cache.c index 5f6e6c6d..cd384205 100644 --- a/lib/dns/cache.c +++ b/lib/dns/cache.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: cache.c,v 1.75 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: cache.c,v 1.76 2007/10/19 17:15:53 explorer Exp $ */ /*! \file */ @@ -39,24 +39,24 @@ #include <dns/rdatasetiter.h> #include <dns/result.h> -#define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$') -#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC) +#define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$') +#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC) -/*! +/*! * Control incremental cleaning. * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize(). * See also DNS_CACHE_CLEANERINCREMENT */ -#define DNS_CACHE_MINSIZE 2097152 /*%< Bytes. 2097152 = 2 MB */ -/*! +#define DNS_CACHE_MINSIZE 2097152 /*%< Bytes. 2097152 = 2 MB */ +/*! * Control incremental cleaning. * CLEANERINCREMENT is how many nodes are examined in one pass. - * See also DNS_CACHE_MINSIZE + * See also DNS_CACHE_MINSIZE */ -#define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */ +#define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */ /*** - *** Types + *** Types ***/ /* @@ -67,48 +67,40 @@ typedef struct cache_cleaner cache_cleaner_t; typedef enum { - cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */ - cleaner_s_busy, /*%< Currently cleaning. */ - cleaner_s_done /*%< Freed enough memory after being overmem. */ + cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */ + cleaner_s_busy, /*%< Currently cleaning. */ + cleaner_s_done /*%< Freed enough memory after being overmem. */ } cleaner_state_t; /* * Convenience macros for comprehensive assertion checking. */ -#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \ - (c)->resched_event != NULL) -#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \ - (c)->iterator != NULL && \ - (c)->resched_event == NULL) +#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle) +#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy) /*% * Accesses to a cache cleaner object are synchronized through * task/event serialization, or locked from the cache object. */ struct cache_cleaner { - isc_mutex_t lock; - /*%< - * Locks overmem_event, overmem. Note: never allocate memory - * while holding this lock - that could lead to deadlock since - * the lock is take by water() which is called from the memory - * allocator. - */ - - dns_cache_t *cache; - isc_task_t *task; - unsigned int cleaning_interval; /*% The cleaning-interval from - named.conf, in seconds. */ - isc_timer_t *cleaning_timer; - isc_event_t *resched_event; /*% Sent by cleaner task to - itself to reschedule */ - isc_event_t *overmem_event; - - dns_dbiterator_t *iterator; - unsigned int increment; /*% Number of names to - clean in one increment */ - cleaner_state_t state; /*% Idle/Busy. */ - isc_boolean_t overmem; /*% The cache is in an overmem state. */ - isc_boolean_t replaceiterator; + isc_mutex_t lock; + /*%< + * Locks overmem. Note: never allocate memory + * while holding this lock - that could lead to deadlock since + * the lock is take by water() which is called from the memory + * allocator. + */ + + dns_cache_t *cache; + isc_task_t *task; + unsigned int cleaning_interval; /*% The cleaning-interval from + named.conf, in seconds. */ + isc_timer_t *cleaning_timer; + + unsigned int increment; /*% Number of names to + clean in one increment */ + cleaner_state_t state; /*% Idle/Busy. */ + isc_boolean_t overmem; /*% The cache is in an overmem state. */ }; /*% @@ -116,47 +108,54 @@ struct cache_cleaner { */ struct dns_cache { - /* Unlocked. */ - unsigned int magic; - isc_mutex_t lock; - isc_mutex_t filelock; - isc_mem_t *mctx; - - /* Locked by 'lock'. */ - int references; - int live_tasks; - dns_rdataclass_t rdclass; - dns_db_t *db; - cache_cleaner_t cleaner; - char *db_type; - int db_argc; - char **db_argv; - - /* Locked by 'filelock'. */ - char * filename; - /* Access to the on-disk cache file is also locked by 'filelock'. */ + /* Unlocked. */ + unsigned int magic; + isc_mutex_t lock; + isc_mutex_t filelock; + isc_mem_t *mctx; + + /* Locked by 'lock'. */ + int references; + int live_tasks; + dns_rdataclass_t rdclass; + dns_db_t *db; + cache_cleaner_t cleaner; + char *db_type; + int db_argc; + char **db_argv; + + /* Locked by 'filelock'. */ + char * filename; + /* Access to the on-disk cache file is also locked by 'filelock'. */ + +#ifdef LRU_DEBUG +#define DUMP_INTERVAL 30 /* seconds */ + isc_timer_t *dump_timer; /* for test */ + isc_time_t dump_time; /* for test */ +#endif }; /*** - *** Functions + *** Functions ***/ static isc_result_t cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr, - isc_timermgr_t *timermgr, cache_cleaner_t *cleaner); + isc_timermgr_t *timermgr, cache_cleaner_t *cleaner); static void cleaning_timer_action(isc_task_t *task, isc_event_t *event); static void -incremental_cleaning_action(isc_task_t *task, isc_event_t *event); - -static void cleaner_shutdown_action(isc_task_t *task, isc_event_t *event); +#ifdef LRU_DEBUG static void -overmem_cleaning_action(isc_task_t *task, isc_event_t *event); +timer_dump(isc_task_t *task, isc_event_t *event); +#endif +#if 0 /* This is no longer needed. When LRU_TEST is cleaned up, + * this should be as well. XXXMLG */ /*% * Work out how many nodes can be cleaned in the time between two * requests to the nameserver. Smooth the resulting number and use @@ -165,386 +164,378 @@ overmem_cleaning_action(isc_task_t *task, isc_event_t *event); */ static void adjust_increment(cache_cleaner_t *cleaner, unsigned int remaining, - isc_time_t *start) + isc_time_t *start) { - isc_time_t end; - isc_uint64_t usecs; - isc_uint64_t new; - unsigned int pps = dns_pps; - unsigned int interval; - unsigned int names; - - /* - * Tune for minumum of 100 packets per second (pps). - */ - if (pps < 100) - pps = 100; - - isc_time_now(&end); - - interval = 1000000 / pps; /* Interval between packets in usecs. */ - if (interval == 0) - interval = 1; - - INSIST(cleaner->increment >= remaining); - names = cleaner->increment - remaining; - usecs = isc_time_microdiff(&end, start); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "adjust_increment interval=%u " - "names=%u usec=%" ISC_PLATFORM_QUADFORMAT "u", - interval, names, usecs); - - if (usecs == 0) { - /* - * If we cleaned all the nodes in unmeasurable time - * double the number of nodes to be cleaned next time. - */ - if (names == cleaner->increment) { - cleaner->increment *= 2; - if (cleaner->increment > DNS_CACHE_CLEANERINCREMENT) - cleaner->increment = DNS_CACHE_CLEANERINCREMENT; - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "%p:new cleaner->increment = %u\n", - cleaner, cleaner->increment); - } - return; - } - - new = (names * interval); - new /= (usecs * 2); - if (new == 0) - new = 1; - - /* Smooth */ - new = (new + cleaner->increment * 7) / 8; - - if (new > DNS_CACHE_CLEANERINCREMENT) - new = DNS_CACHE_CLEANERINCREMENT; - - cleaner->increment = (unsigned int)new; - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "%p:new cleaner->increment = %u\n", - cleaner, cleaner->increment); + isc_time_t end; + isc_uint64_t usecs; + isc_uint64_t new; + unsigned int pps = dns_pps; + unsigned int interval; + unsigned int names; + + /* + * Tune for minumum of 100 packets per second (pps). + */ + if (pps < 100) + pps = 100; + + isc_time_now(&end); + + interval = 1000000 / pps; /* Interval between packets in usecs. */ + if (interval == 0) + interval = 1; + + INSIST(cleaner->increment >= remaining); + names = cleaner->increment - remaining; + usecs = isc_time_microdiff(&end, start); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, + ISC_LOG_DEBUG(1), "adjust_increment interval=%u " + "names=%u usec=%" ISC_PLATFORM_QUADFORMAT "u", + interval, names, usecs); + + if (usecs == 0) { + /* + * If we cleaned all the nodes in unmeasurable time + * double the number of nodes to be cleaned next time. + */ + if (names == cleaner->increment) { + cleaner->increment *= 2; + if (cleaner->increment > DNS_CACHE_CLEANERINCREMENT) + cleaner->increment = DNS_CACHE_CLEANERINCREMENT; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), + "%p:new cleaner->increment = %u\n", + cleaner, cleaner->increment); + } + return; + } + + new = (names * interval); + new /= (usecs * 2); + if (new == 0) + new = 1; + + /* Smooth */ + new = (new + cleaner->increment * 7) / 8; + + if (new > DNS_CACHE_CLEANERINCREMENT) + new = DNS_CACHE_CLEANERINCREMENT; + + cleaner->increment = (unsigned int)new; + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, + ISC_LOG_DEBUG(1), "%p:new cleaner->increment = %u\n", + cleaner, cleaner->increment); } +#endif static inline isc_result_t cache_create_db(dns_cache_t *cache, dns_db_t **db) { - return (dns_db_create(cache->mctx, cache->db_type, dns_rootname, - dns_dbtype_cache, cache->rdclass, - cache->db_argc, cache->db_argv, db)); + return (dns_db_create(cache->mctx, cache->db_type, dns_rootname, + dns_dbtype_cache, cache->rdclass, + cache->db_argc, cache->db_argv, db)); } isc_result_t dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, - isc_timermgr_t *timermgr, dns_rdataclass_t rdclass, - const char *db_type, unsigned int db_argc, char **db_argv, - dns_cache_t **cachep) + isc_timermgr_t *timermgr, dns_rdataclass_t rdclass, + const char *db_type, unsigned int db_argc, char **db_argv, + dns_cache_t **cachep) { - isc_result_t result; - dns_cache_t *cache; - int i; - - REQUIRE(cachep != NULL); - REQUIRE(*cachep == NULL); - REQUIRE(mctx != NULL); - - cache = isc_mem_get(mctx, sizeof(*cache)); - if (cache == NULL) - return (ISC_R_NOMEMORY); - - cache->mctx = NULL; - isc_mem_attach(mctx, &cache->mctx); - - result = isc_mutex_init(&cache->lock); - if (result != ISC_R_SUCCESS) - goto cleanup_mem; - - result = isc_mutex_init(&cache->filelock); - if (result != ISC_R_SUCCESS) - goto cleanup_lock; - - cache->references = 1; - cache->live_tasks = 0; - cache->rdclass = rdclass; - - cache->db_type = isc_mem_strdup(mctx, db_type); - if (cache->db_type == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup_filelock; - } - - cache->db_argc = db_argc; - if (cache->db_argc == 0) - cache->db_argv = NULL; - else { - cache->db_argv = isc_mem_get(mctx, - cache->db_argc * sizeof(char *)); - if (cache->db_argv == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup_dbtype; - } - for (i = 0; i < cache->db_argc; i++) - cache->db_argv[i] = NULL; - for (i = 0; i < cache->db_argc; i++) { - cache->db_argv[i] = isc_mem_strdup(mctx, db_argv[i]); - if (cache->db_argv[i] == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup_dbargv; - } - } - } - - cache->db = NULL; - result = cache_create_db(cache, &cache->db); - if (result != ISC_R_SUCCESS) - goto cleanup_dbargv; - - cache->filename = NULL; - - cache->magic = CACHE_MAGIC; - - result = cache_cleaner_init(cache, taskmgr, timermgr, &cache->cleaner); - if (result != ISC_R_SUCCESS) - goto cleanup_db; - - *cachep = cache; - return (ISC_R_SUCCESS); + isc_result_t result; + dns_cache_t *cache; + int i; + + REQUIRE(cachep != NULL); + REQUIRE(*cachep == NULL); + REQUIRE(mctx != NULL); + + cache = isc_mem_get(mctx, sizeof(*cache)); + if (cache == NULL) + return (ISC_R_NOMEMORY); + + cache->mctx = NULL; + isc_mem_attach(mctx, &cache->mctx); + + result = isc_mutex_init(&cache->lock); + if (result != ISC_R_SUCCESS) + goto cleanup_mem; + + result = isc_mutex_init(&cache->filelock); + if (result != ISC_R_SUCCESS) + goto cleanup_lock; + + cache->references = 1; + cache->live_tasks = 0; + cache->rdclass = rdclass; + + cache->db_type = isc_mem_strdup(mctx, db_type); + if (cache->db_type == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_filelock; + } + + cache->db_argc = db_argc; + if (cache->db_argc == 0) + cache->db_argv = NULL; + else { + cache->db_argv = isc_mem_get(mctx, + cache->db_argc * sizeof(char *)); + if (cache->db_argv == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_dbtype; + } + for (i = 0; i < cache->db_argc; i++) + cache->db_argv[i] = NULL; + for (i = 0; i < cache->db_argc; i++) { + cache->db_argv[i] = isc_mem_strdup(mctx, db_argv[i]); + if (cache->db_argv[i] == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_dbargv; + } + } + } + + cache->db = NULL; + result = cache_create_db(cache, &cache->db); + if (result != ISC_R_SUCCESS) + goto cleanup_dbargv; + + cache->filename = NULL; + + cache->magic = CACHE_MAGIC; + + result = cache_cleaner_init(cache, taskmgr, timermgr, &cache->cleaner); + if (result != ISC_R_SUCCESS) + goto cleanup_db; + + *cachep = cache; + return (ISC_R_SUCCESS); cleanup_db: - dns_db_detach(&cache->db); + dns_db_detach(&cache->db); cleanup_dbargv: - for (i = 0; i < cache->db_argc; i++) - if (cache->db_argv[i] != NULL) - isc_mem_free(mctx, cache->db_argv[i]); - if (cache->db_argv != NULL) - isc_mem_put(mctx, cache->db_argv, - cache->db_argc * sizeof(char *)); + for (i = 0; i < cache->db_argc; i++) + if (cache->db_argv[i] != NULL) + isc_mem_free(mctx, cache->db_argv[i]); + if (cache->db_argv != NULL) + isc_mem_put(mctx, cache->db_argv, + cache->db_argc * sizeof(char *)); cleanup_dbtype: - isc_mem_free(mctx, cache->db_type); + isc_mem_free(mctx, cache->db_type); cleanup_filelock: - DESTROYLOCK(&cache->filelock); + DESTROYLOCK(&cache->filelock); cleanup_lock: - DESTROYLOCK(&cache->lock); + DESTROYLOCK(&cache->lock); cleanup_mem: - isc_mem_put(mctx, cache, sizeof(*cache)); - isc_mem_detach(&mctx); - return (result); + isc_mem_put(mctx, cache, sizeof(*cache)); + isc_mem_detach(&mctx); + return (result); } static void cache_free(dns_cache_t *cache) { - isc_mem_t *mctx; - int i; - - REQUIRE(VALID_CACHE(cache)); - REQUIRE(cache->references == 0); - - isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0); - - if (cache->cleaner.task != NULL) - isc_task_detach(&cache->cleaner.task); + isc_mem_t *mctx; + int i; - if (cache->cleaner.overmem_event != NULL) - isc_event_free(&cache->cleaner.overmem_event); + REQUIRE(VALID_CACHE(cache)); + REQUIRE(cache->references == 0); - if (cache->cleaner.resched_event != NULL) - isc_event_free(&cache->cleaner.resched_event); + isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0); - if (cache->cleaner.iterator != NULL) - dns_dbiterator_destroy(&cache->cleaner.iterator); + if (cache->cleaner.task != NULL) + isc_task_detach(&cache->cleaner.task); - DESTROYLOCK(&cache->cleaner.lock); + DESTROYLOCK(&cache->cleaner.lock); - if (cache->filename) { - isc_mem_free(cache->mctx, cache->filename); - cache->filename = NULL; - } + if (cache->filename) { + isc_mem_free(cache->mctx, cache->filename); + cache->filename = NULL; + } - if (cache->db != NULL) - dns_db_detach(&cache->db); + if (cache->db != NULL) + dns_db_detach(&cache->db); - if (cache->db_argv != NULL) { - for (i = 0; i < cache->db_argc; i++) - if (cache->db_argv[i] != NULL) - isc_mem_free(cache->mctx, cache->db_argv[i]); - isc_mem_put(cache->mctx, cache->db_argv, - cache->db_argc * sizeof(char *)); - } + if (cache->db_argv != NULL) { + for (i = 0; i < cache->db_argc; i++) + if (cache->db_argv[i] != NULL) + isc_mem_free(cache->mctx, cache->db_argv[i]); + isc_mem_put(cache->mctx, cache->db_argv, + cache->db_argc * sizeof(char *)); + } - if (cache->db_type != NULL) - isc_mem_free(cache->mctx, cache->db_type); + if (cache->db_type != NULL) + isc_mem_free(cache->mctx, cache->db_type); - DESTROYLOCK(&cache->lock); - DESTROYLOCK(&cache->filelock); - cache->magic = 0; - mctx = cache->mctx; - isc_mem_put(cache->mctx, cache, sizeof(*cache)); - isc_mem_detach(&mctx); + DESTROYLOCK(&cache->lock); + DESTROYLOCK(&cache->filelock); + cache->magic = 0; + mctx = cache->mctx; + isc_mem_put(cache->mctx, cache, sizeof(*cache)); + isc_mem_detach(&mctx); } void dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) { - REQUIRE(VALID_CACHE(cache)); - REQUIRE(targetp != NULL && *targetp == NULL); + REQUIRE(VALID_CACHE(cache)); + REQUIRE(targetp != NULL && *targetp == NULL); - LOCK(&cache->lock); - cache->references++; - UNLOCK(&cache->lock); + LOCK(&cache->lock); + cache->references++; + UNLOCK(&cache->lock); - *targetp = cache; + *targetp = cache; } void dns_cache_detach(dns_cache_t **cachep) { - dns_cache_t *cache; - isc_boolean_t free_cache = ISC_FALSE; - - REQUIRE(cachep != NULL); - cache = *cachep; - REQUIRE(VALID_CACHE(cache)); - - LOCK(&cache->lock); - REQUIRE(cache->references > 0); - cache->references--; - if (cache->references == 0) { - cache->cleaner.overmem = ISC_FALSE; - free_cache = ISC_TRUE; - } - - *cachep = NULL; - - if (free_cache) { - /* - * When the cache is shut down, dump it to a file if one is - * specified. - */ - isc_result_t result = dns_cache_dump(cache); - if (result != ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "error dumping cache: %s ", - isc_result_totext(result)); - - /* - * If the cleaner task exists, let it free the cache. - */ - if (cache->live_tasks > 0) { - isc_task_shutdown(cache->cleaner.task); - free_cache = ISC_FALSE; - } - } - - UNLOCK(&cache->lock); - - if (free_cache) - cache_free(cache); + dns_cache_t *cache; + isc_boolean_t free_cache = ISC_FALSE; + + REQUIRE(cachep != NULL); + cache = *cachep; + REQUIRE(VALID_CACHE(cache)); + + LOCK(&cache->lock); + REQUIRE(cache->references > 0); + cache->references--; + if (cache->references == 0) { + cache->cleaner.overmem = ISC_FALSE; + free_cache = ISC_TRUE; + } + + *cachep = NULL; + + if (free_cache) { + /* + * When the cache is shut down, dump it to a file if one is + * specified. + */ + isc_result_t result = dns_cache_dump(cache); + if (result != ISC_R_SUCCESS) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, + "error dumping cache: %s ", + isc_result_totext(result)); + + /* + * If the cleaner task exists, let it free the cache. + */ + if (cache->live_tasks > 0) { + isc_task_shutdown(cache->cleaner.task); + free_cache = ISC_FALSE; + } + } + + UNLOCK(&cache->lock); + + if (free_cache) + cache_free(cache); } void dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) { - REQUIRE(VALID_CACHE(cache)); - REQUIRE(dbp != NULL && *dbp == NULL); - REQUIRE(cache->db != NULL); + REQUIRE(VALID_CACHE(cache)); + REQUIRE(dbp != NULL && *dbp == NULL); + REQUIRE(cache->db != NULL); - LOCK(&cache->lock); - dns_db_attach(cache->db, dbp); - UNLOCK(&cache->lock); + LOCK(&cache->lock); + dns_db_attach(cache->db, dbp); + UNLOCK(&cache->lock); } isc_result_t dns_cache_setfilename(dns_cache_t *cache, const char *filename) { - char *newname; + char *newname; - REQUIRE(VALID_CACHE(cache)); - REQUIRE(filename != NULL); + REQUIRE(VALID_CACHE(cache)); + REQUIRE(filename != NULL); - newname = isc_mem_strdup(cache->mctx, filename); - if (newname == NULL) - return (ISC_R_NOMEMORY); + newname = isc_mem_strdup(cache->mctx, filename); + if (newname == NULL) + return (ISC_R_NOMEMORY); - LOCK(&cache->filelock); - if (cache->filename) - isc_mem_free(cache->mctx, cache->filename); - cache->filename = newname; - UNLOCK(&cache->filelock); + LOCK(&cache->filelock); + if (cache->filename) + isc_mem_free(cache->mctx, cache->filename); + cache->filename = newname; + UNLOCK(&cache->filelock); - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } isc_result_t dns_cache_load(dns_cache_t *cache) { - isc_result_t result; + isc_result_t result; - REQUIRE(VALID_CACHE(cache)); + REQUIRE(VALID_CACHE(cache)); - if (cache->filename == NULL) - return (ISC_R_SUCCESS); + if (cache->filename == NULL) + return (ISC_R_SUCCESS); - LOCK(&cache->filelock); - result = dns_db_load(cache->db, cache->filename); - UNLOCK(&cache->filelock); + LOCK(&cache->filelock); + result = dns_db_load(cache->db, cache->filename); + UNLOCK(&cache->filelock); - return (result); + return (result); } isc_result_t dns_cache_dump(dns_cache_t *cache) { - isc_result_t result; + isc_result_t result; - REQUIRE(VALID_CACHE(cache)); + REQUIRE(VALID_CACHE(cache)); - if (cache->filename == NULL) - return (ISC_R_SUCCESS); + if (cache->filename == NULL) + return (ISC_R_SUCCESS); - LOCK(&cache->filelock); - result = dns_master_dump(cache->mctx, cache->db, NULL, - &dns_master_style_cache, cache->filename); - UNLOCK(&cache->filelock); + LOCK(&cache->filelock); + result = dns_master_dump(cache->mctx, cache->db, NULL, + &dns_master_style_cache, cache->filename); + UNLOCK(&cache->filelock); - return (result); + return (result); } void dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) { - isc_interval_t interval; - isc_result_t result; - - LOCK(&cache->lock); - - /* - * It may be the case that the cache has already shut down. - * If so, it has no timer. - */ - if (cache->cleaner.cleaning_timer == NULL) - goto unlock; - - cache->cleaner.cleaning_interval = t; - - if (t == 0) { - result = isc_timer_reset(cache->cleaner.cleaning_timer, - isc_timertype_inactive, - NULL, NULL, ISC_TRUE); - } else { - isc_interval_set(&interval, cache->cleaner.cleaning_interval, - 0); - result = isc_timer_reset(cache->cleaner.cleaning_timer, - isc_timertype_ticker, - NULL, &interval, ISC_FALSE); - } - if (result != ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "could not set cache cleaning interval: %s", - isc_result_totext(result)); + isc_interval_t interval; + isc_result_t result; + + LOCK(&cache->lock); + + /* + * It may be the case that the cache has already shut down. + * If so, it has no timer. + */ + if (cache->cleaner.cleaning_timer == NULL) + goto unlock; + + cache->cleaner.cleaning_interval = t; + + if (t == 0) { + result = isc_timer_reset(cache->cleaner.cleaning_timer, + isc_timertype_inactive, + NULL, NULL, ISC_TRUE); + } else { + isc_interval_set(&interval, cache->cleaner.cleaning_interval, + 0); + result = isc_timer_reset(cache->cleaner.cleaning_timer, + isc_timertype_ticker, + NULL, &interval, ISC_FALSE); + } + if (result != ISC_R_SUCCESS) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, + "could not set cache cleaning interval: %s", + isc_result_totext(result)); unlock: - UNLOCK(&cache->lock); + UNLOCK(&cache->lock); } /* @@ -554,181 +545,86 @@ dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) { static isc_result_t cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr, - isc_timermgr_t *timermgr, cache_cleaner_t *cleaner) + isc_timermgr_t *timermgr, cache_cleaner_t *cleaner) { - isc_result_t result; - - result = isc_mutex_init(&cleaner->lock); - if (result != ISC_R_SUCCESS) - goto fail; - - cleaner->increment = DNS_CACHE_CLEANERINCREMENT; - cleaner->state = cleaner_s_idle; - cleaner->cache = cache; - cleaner->iterator = NULL; - cleaner->overmem = ISC_FALSE; - cleaner->replaceiterator = ISC_FALSE; - - cleaner->task = NULL; - cleaner->cleaning_timer = NULL; - cleaner->resched_event = NULL; - cleaner->overmem_event = NULL; - - result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE, - &cleaner->iterator); - if (result != ISC_R_SUCCESS) - goto cleanup; - - if (taskmgr != NULL && timermgr != NULL) { - result = isc_task_create(taskmgr, 1, &cleaner->task); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_task_create() failed: %s", - dns_result_totext(result)); - result = ISC_R_UNEXPECTED; - goto cleanup; - } - cleaner->cache->live_tasks++; - isc_task_setname(cleaner->task, "cachecleaner", cleaner); - - result = isc_task_onshutdown(cleaner->task, - cleaner_shutdown_action, cache); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "cache cleaner: " - "isc_task_onshutdown() failed: %s", - dns_result_totext(result)); - goto cleanup; - } - - cleaner->cleaning_interval = 0; /* Initially turned off. */ - result = isc_timer_create(timermgr, isc_timertype_inactive, - NULL, NULL, - cleaner->task, - cleaning_timer_action, cleaner, - &cleaner->cleaning_timer); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_timer_create() failed: %s", - dns_result_totext(result)); - result = ISC_R_UNEXPECTED; - goto cleanup; - } - - cleaner->resched_event = - isc_event_allocate(cache->mctx, cleaner, - DNS_EVENT_CACHECLEAN, - incremental_cleaning_action, - cleaner, sizeof(isc_event_t)); - if (cleaner->resched_event == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; - } - - cleaner->overmem_event = - isc_event_allocate(cache->mctx, cleaner, - DNS_EVENT_CACHEOVERMEM, - overmem_cleaning_action, - cleaner, sizeof(isc_event_t)); - if (cleaner->overmem_event == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; - } - } - - return (ISC_R_SUCCESS); + isc_result_t result; +#ifdef LRU_DEBUG + isc_interval_t interval; +#endif + + result = isc_mutex_init(&cleaner->lock); + if (result != ISC_R_SUCCESS) + goto fail; + + cleaner->increment = DNS_CACHE_CLEANERINCREMENT; + cleaner->state = cleaner_s_idle; + cleaner->cache = cache; + cleaner->overmem = ISC_FALSE; + + cleaner->task = NULL; + cleaner->cleaning_timer = NULL; + + if (taskmgr != NULL && timermgr != NULL) { + result = isc_task_create(taskmgr, 1, &cleaner->task); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_task_create() failed: %s", + dns_result_totext(result)); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + cleaner->cache->live_tasks++; + isc_task_setname(cleaner->task, "cachecleaner", cleaner); + + result = isc_task_onshutdown(cleaner->task, + cleaner_shutdown_action, cache); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "cache cleaner: " + "isc_task_onshutdown() failed: %s", + dns_result_totext(result)); + goto cleanup; + } + + cleaner->cleaning_interval = 0; /* Initially turned off. */ + result = isc_timer_create(timermgr, isc_timertype_inactive, + NULL, NULL, + cleaner->task, + cleaning_timer_action, cleaner, + &cleaner->cleaning_timer); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_timer_create() failed: %s", + dns_result_totext(result)); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + +#ifdef LRU_DEBUG + interval.seconds = DUMP_INTERVAL; + interval.nanoseconds = 0; + RUNTIME_CHECK(isc_time_nowplusinterval(&cache->dump_time, + &interval) == + ISC_R_SUCCESS); + cache->dump_timer = NULL; + result = isc_timer_create(timermgr, isc_timertype_once, + &cache->dump_time, NULL, + cleaner->task, timer_dump, + cache, &cache->dump_timer); + RUNTIME_CHECK(result == ISC_R_SUCCESS); /* for brevity */ +#endif + } + + return (ISC_R_SUCCESS); cleanup: - if (cleaner->overmem_event != NULL) - isc_event_free(&cleaner->overmem_event); - if (cleaner->resched_event != NULL) - isc_event_free(&cleaner->resched_event); - if (cleaner->cleaning_timer != NULL) - isc_timer_detach(&cleaner->cleaning_timer); - if (cleaner->task != NULL) - isc_task_detach(&cleaner->task); - if (cleaner->iterator != NULL) - dns_dbiterator_destroy(&cleaner->iterator); - DESTROYLOCK(&cleaner->lock); + if (cleaner->cleaning_timer != NULL) + isc_timer_detach(&cleaner->cleaning_timer); + if (cleaner->task != NULL) + isc_task_detach(&cleaner->task); + DESTROYLOCK(&cleaner->lock); fail: - return (result); -} - -static void -begin_cleaning(cache_cleaner_t *cleaner) { - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(CLEANER_IDLE(cleaner)); - - /* - * Create an iterator, if it does not already exist, and - * position it at the beginning of the cache. - */ - if (cleaner->iterator == NULL) - result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE, - &cleaner->iterator); - if (result != ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "cache cleaner could not create " - "iterator: %s", isc_result_totext(result)); - else { - dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE); - result = dns_dbiterator_first(cleaner->iterator); - } - if (result != ISC_R_SUCCESS) { - /* - * If the result is ISC_R_NOMORE, the database is empty, - * so there is nothing to be cleaned. - */ - if (result != ISC_R_NOMORE && cleaner->iterator != NULL) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "cache cleaner: " - "dns_dbiterator_first() failed: %s", - dns_result_totext(result)); - dns_dbiterator_destroy(&cleaner->iterator); - } else if (cleaner->iterator != NULL) { - result = dns_dbiterator_pause(cleaner->iterator); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - } - } else { - /* - * Pause the iterator to free its lock. - */ - result = dns_dbiterator_pause(cleaner->iterator); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "begin cache cleaning, mem inuse %lu", - (unsigned long)isc_mem_inuse(cleaner->cache->mctx)); - cleaner->state = cleaner_s_busy; - isc_task_send(cleaner->task, &cleaner->resched_event); - } - - return; -} - -static void -end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) { - isc_result_t result; - - REQUIRE(CLEANER_BUSY(cleaner)); - REQUIRE(event != NULL); - - result = dns_dbiterator_pause(cleaner->iterator); - if (result != ISC_R_SUCCESS) - dns_dbiterator_destroy(&cleaner->iterator); - - dns_cache_setcleaninginterval(cleaner->cache, - cleaner->cleaning_interval); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu", - (unsigned long)isc_mem_inuse(cleaner->cache->mctx)); - - cleaner->state = cleaner_s_idle; - cleaner->resched_event = event; + return (result); } /* @@ -736,187 +632,18 @@ end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) { */ static void cleaning_timer_action(isc_task_t *task, isc_event_t *event) { - cache_cleaner_t *cleaner = event->ev_arg; - - UNUSED(task); + cache_cleaner_t *cleaner = event->ev_arg; - INSIST(task == cleaner->task); - INSIST(event->ev_type == ISC_TIMEREVENT_TICK); + UNUSED(task); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "cache cleaning timer fired, " - "cleaner state = %d", cleaner->state); + INSIST(task == cleaner->task); + INSIST(event->ev_type == ISC_TIMEREVENT_TICK); - if (cleaner->state == cleaner_s_idle) - begin_cleaning(cleaner); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, + ISC_LOG_DEBUG(1), "cache cleaning timer fired, " + "cleaner state = %d", cleaner->state); - isc_event_free(&event); -} - -/* - * This is called when the cache either surpasses its upper limit - * or shrinks beyond its lower limit. - */ -static void -overmem_cleaning_action(isc_task_t *task, isc_event_t *event) { - cache_cleaner_t *cleaner = event->ev_arg; - isc_boolean_t want_cleaning = ISC_FALSE; - - UNUSED(task); - - INSIST(task == cleaner->task); - INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM); - INSIST(cleaner->overmem_event == NULL); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "overmem_cleaning_action called, " - "overmem = %d, state = %d", cleaner->overmem, - cleaner->state); - - LOCK(&cleaner->lock); - - if (cleaner->overmem) { - if (cleaner->state == cleaner_s_idle) - want_cleaning = ISC_TRUE; - } else { - if (cleaner->state == cleaner_s_busy) - /* - * end_cleaning() can't be called here because - * then both cleaner->overmem_event and - * cleaner->resched_event will point to this - * event. Set the state to done, and then - * when the incremental_cleaning_action() event - * is posted, it will handle the end_cleaning. - */ - cleaner->state = cleaner_s_done; - } - - cleaner->overmem_event = event; - - UNLOCK(&cleaner->lock); - - if (want_cleaning) - begin_cleaning(cleaner); -} - -/* - * Do incremental cleaning. - */ -static void -incremental_cleaning_action(isc_task_t *task, isc_event_t *event) { - cache_cleaner_t *cleaner = event->ev_arg; - isc_result_t result; - unsigned int n_names; - isc_time_t start; - - UNUSED(task); - - INSIST(task == cleaner->task); - INSIST(event->ev_type == DNS_EVENT_CACHECLEAN); - - if (cleaner->state == cleaner_s_done) { - cleaner->state = cleaner_s_busy; - end_cleaning(cleaner, event); - LOCK(&cleaner->cache->lock); - LOCK(&cleaner->lock); - if (cleaner->replaceiterator) { - dns_dbiterator_destroy(&cleaner->iterator); - (void) dns_db_createiterator(cleaner->cache->db, - ISC_FALSE, - &cleaner->iterator); - cleaner->replaceiterator = ISC_FALSE; - } - UNLOCK(&cleaner->lock); - UNLOCK(&cleaner->cache->lock); - return; - } - - INSIST(CLEANER_BUSY(cleaner)); - - n_names = cleaner->increment; - - REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator)); - - isc_time_now(&start); - while (n_names-- > 0) { - dns_dbnode_t *node = NULL; - - result = dns_dbiterator_current(cleaner->iterator, &node, - NULL); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "cache cleaner: dns_dbiterator_current() " - "failed: %s", dns_result_totext(result)); - - adjust_increment(cleaner, n_names, &start); - end_cleaning(cleaner, event); - return; - } - - /* - * The node was not needed, but was required by - * dns_dbiterator_current(). Give up its reference. - */ - dns_db_detachnode(cleaner->cache->db, &node); - - /* - * Step to the next node. - */ - result = dns_dbiterator_next(cleaner->iterator); - - if (result != ISC_R_SUCCESS) { - /* - * Either the end was reached (ISC_R_NOMORE) or - * some error was signaled. If the cache is still - * overmem and no error was encountered, - * keep trying to clean it, otherwise stop cleaning. - */ - if (result != ISC_R_NOMORE) - UNEXPECTED_ERROR(__FILE__, __LINE__, - "cache cleaner: " - "dns_dbiterator_next() " - "failed: %s", - dns_result_totext(result)); - else if (cleaner->overmem) { - result = dns_dbiterator_first(cleaner-> - iterator); - if (result == ISC_R_SUCCESS) { - isc_log_write(dns_lctx, - DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), - "cache cleaner: " - "still overmem, " - "reset and try again"); - continue; - } - } - - adjust_increment(cleaner, n_names, &start); - end_cleaning(cleaner, event); - return; - } - } - - adjust_increment(cleaner, 0U, &start); - - /* - * We have successfully performed a cleaning increment but have - * not gone through the entire cache. Free the iterator locks - * and reschedule another batch. If it fails, just try to continue - * anyway. - */ - result = dns_dbiterator_pause(cleaner->iterator); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, " - "mem inuse %lu, sleeping", cleaner->increment, - (unsigned long)isc_mem_inuse(cleaner->cache->mctx)); - - isc_task_send(task, &event); - INSIST(CLEANER_BUSY(cleaner)); - return; + isc_event_free(&event); } /* @@ -924,108 +651,104 @@ incremental_cleaning_action(isc_task_t *task, isc_event_t *event) { */ isc_result_t dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) { - isc_result_t result; - dns_dbiterator_t *iterator = NULL; - - REQUIRE(VALID_CACHE(cache)); - - result = dns_db_createiterator(cache->db, ISC_FALSE, &iterator); - if (result != ISC_R_SUCCESS) - return result; - - result = dns_dbiterator_first(iterator); - - while (result == ISC_R_SUCCESS) { - dns_dbnode_t *node = NULL; - result = dns_dbiterator_current(iterator, &node, - (dns_name_t *)NULL); - if (result != ISC_R_SUCCESS) - break; - - /* - * Check TTLs, mark expired rdatasets stale. - */ - result = dns_db_expirenode(cache->db, node, now); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "cache cleaner: dns_db_expirenode() " - "failed: %s", - dns_result_totext(result)); - /* - * Continue anyway. - */ - } - - /* - * This is where the actual freeing takes place. - */ - dns_db_detachnode(cache->db, &node); - - result = dns_dbiterator_next(iterator); - } - - dns_dbiterator_destroy(&iterator); - - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - - return (result); + isc_result_t result; + dns_dbiterator_t *iterator = NULL; + + REQUIRE(VALID_CACHE(cache)); + + result = dns_db_createiterator(cache->db, ISC_FALSE, &iterator); + if (result != ISC_R_SUCCESS) + return result; + + result = dns_dbiterator_first(iterator); + + while (result == ISC_R_SUCCESS) { + dns_dbnode_t *node = NULL; + result = dns_dbiterator_current(iterator, &node, + (dns_name_t *)NULL); + if (result != ISC_R_SUCCESS) + break; + + /* + * Check TTLs, mark expired rdatasets stale. + */ + result = dns_db_expirenode(cache->db, node, now); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "cache cleaner: dns_db_expirenode() " + "failed: %s", + dns_result_totext(result)); + /* + * Continue anyway. + */ + } + + /* + * This is where the actual freeing takes place. + */ + dns_db_detachnode(cache->db, &node); + + result = dns_dbiterator_next(iterator); + } + + dns_dbiterator_destroy(&iterator); + + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + return (result); } static void water(void *arg, int mark) { - dns_cache_t *cache = arg; - isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); + dns_cache_t *cache = arg; + isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); - REQUIRE(VALID_CACHE(cache)); + REQUIRE(VALID_CACHE(cache)); - LOCK(&cache->cleaner.lock); - - dns_db_overmem(cache->db, overmem); - cache->cleaner.overmem = overmem; + LOCK(&cache->cleaner.lock); - if (cache->cleaner.overmem_event != NULL) - isc_task_send(cache->cleaner.task, - &cache->cleaner.overmem_event); + dns_db_overmem(cache->db, overmem); + cache->cleaner.overmem = overmem; - UNLOCK(&cache->cleaner.lock); + UNLOCK(&cache->cleaner.lock); } void dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) { - isc_uint32_t lowater; - isc_uint32_t hiwater; - - REQUIRE(VALID_CACHE(cache)); - - /* - * Impose a minumum cache size; pathological things happen if there - * is too little room. - */ - if (size != 0 && size < DNS_CACHE_MINSIZE) - size = DNS_CACHE_MINSIZE; - - hiwater = size - (size >> 3); /* Approximately 7/8ths. */ - lowater = size - (size >> 2); /* Approximately 3/4ths. */ - - /* - * If the cache was overmem and cleaning, but now with the new limits - * it is no longer in an overmem condition, then the next - * isc_mem_put for cache memory will do the right thing and trigger - * water(). - */ - - if (size == 0 || hiwater == 0 || lowater == 0) - /* - * Disable cache memory limiting. - */ - isc_mem_setwater(cache->mctx, water, cache, 0, 0); - else - /* - * Establish new cache memory limits (either for the first - * time, or replacing other limits). - */ - isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater); + isc_uint32_t lowater; + isc_uint32_t hiwater; + + REQUIRE(VALID_CACHE(cache)); + + /* + * Impose a minumum cache size; pathological things happen if there + * is too little room. + */ + if (size != 0 && size < DNS_CACHE_MINSIZE) + size = DNS_CACHE_MINSIZE; + + hiwater = size - (size >> 3); /* Approximately 7/8ths. */ + lowater = size - (size >> 2); /* Approximately 3/4ths. */ + + /* + * If the cache was overmem and cleaning, but now with the new limits + * it is no longer in an overmem condition, then the next + * isc_mem_put for cache memory will do the right thing and trigger + * water(). + */ + + if (size == 0 || hiwater == 0 || lowater == 0) + /* + * Disable cache memory limiting. + */ + isc_mem_setwater(cache->mctx, water, cache, 0, 0); + else + /* + * Establish new cache memory limits (either for the first + * time, or replacing other limits). + */ + isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater); } /* @@ -1033,122 +756,148 @@ dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) { */ static void cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) { - dns_cache_t *cache = event->ev_arg; - isc_boolean_t should_free = ISC_FALSE; + dns_cache_t *cache = event->ev_arg; + isc_boolean_t should_free = ISC_FALSE; + + UNUSED(task); - UNUSED(task); + INSIST(task == cache->cleaner.task); + INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN); - INSIST(task == cache->cleaner.task); - INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN); + LOCK(&cache->lock); - if (CLEANER_BUSY(&cache->cleaner)) - end_cleaning(&cache->cleaner, event); - else - isc_event_free(&event); + cache->live_tasks--; + INSIST(cache->live_tasks == 0); - LOCK(&cache->lock); + if (cache->references == 0) + should_free = ISC_TRUE; - cache->live_tasks--; - INSIST(cache->live_tasks == 0); + /* + * By detaching the timer in the context of its task, + * we are guaranteed that there will be no further timer + * events. + */ + if (cache->cleaner.cleaning_timer != NULL) + isc_timer_detach(&cache->cleaner.cleaning_timer); - if (cache->references == 0) - should_free = ISC_TRUE; +#ifdef LRU_DEBUG + isc_timer_detach(&cache->dump_timer); +#endif - /* - * By detaching the timer in the context of its task, - * we are guaranteed that there will be no further timer - * events. - */ - if (cache->cleaner.cleaning_timer != NULL) - isc_timer_detach(&cache->cleaner.cleaning_timer); + /* Make sure we don't reschedule anymore. */ + (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL); - /* Make sure we don't reschedule anymore. */ - (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL); + UNLOCK(&cache->lock); - UNLOCK(&cache->lock); + if (should_free) + cache_free(cache); - if (should_free) - cache_free(cache); + isc_event_free(&event); } isc_result_t dns_cache_flush(dns_cache_t *cache) { - dns_db_t *db = NULL; - isc_result_t result; - - result = cache_create_db(cache, &db); - if (result != ISC_R_SUCCESS) - return (result); - - LOCK(&cache->lock); - LOCK(&cache->cleaner.lock); - if (cache->cleaner.state == cleaner_s_idle) { - if (cache->cleaner.iterator != NULL) - dns_dbiterator_destroy(&cache->cleaner.iterator); - (void) dns_db_createiterator(db, ISC_FALSE, - &cache->cleaner.iterator); - } else { - if (cache->cleaner.state == cleaner_s_busy) - cache->cleaner.state = cleaner_s_done; - cache->cleaner.replaceiterator = ISC_TRUE; - } - dns_db_detach(&cache->db); - cache->db = db; - UNLOCK(&cache->cleaner.lock); - UNLOCK(&cache->lock); - - return (ISC_R_SUCCESS); + dns_db_t *db = NULL; + isc_result_t result; + + result = cache_create_db(cache, &db); + if (result != ISC_R_SUCCESS) + return (result); + + LOCK(&cache->lock); + LOCK(&cache->cleaner.lock); + if (cache->cleaner.state == cleaner_s_idle) { + /* XXXMLG do something */ + } else if (cache->cleaner.state == cleaner_s_busy) { + /* XXXMLG do something else */ + } + dns_db_detach(&cache->db); + cache->db = db; + UNLOCK(&cache->cleaner.lock); + UNLOCK(&cache->lock); + + return (ISC_R_SUCCESS); } isc_result_t dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) { - isc_result_t result; - dns_rdatasetiter_t *iter = NULL; - dns_dbnode_t *node = NULL; - dns_db_t *db = NULL; - - LOCK(&cache->lock); - if (cache->db != NULL) - dns_db_attach(cache->db, &db); - UNLOCK(&cache->lock); - if (db == NULL) - return (ISC_R_SUCCESS); - result = dns_db_findnode(cache->db, name, ISC_FALSE, &node); - if (result == ISC_R_NOTFOUND) { - result = ISC_R_SUCCESS; - goto cleanup_db; - } - if (result != ISC_R_SUCCESS) - goto cleanup_db; - - result = dns_db_allrdatasets(cache->db, node, NULL, - (isc_stdtime_t)0, &iter); - if (result != ISC_R_SUCCESS) - goto cleanup_node; - - for (result = dns_rdatasetiter_first(iter); - result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(iter)) - { - dns_rdataset_t rdataset; - dns_rdataset_init(&rdataset); - - dns_rdatasetiter_current(iter, &rdataset); - result = dns_db_deleterdataset(cache->db, node, NULL, - rdataset.type, rdataset.covers); - dns_rdataset_disassociate(&rdataset); - if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) - break; - } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - - dns_rdatasetiter_destroy(&iter); + isc_result_t result; + dns_rdatasetiter_t *iter = NULL; + dns_dbnode_t *node = NULL; + dns_db_t *db = NULL; + + LOCK(&cache->lock); + if (cache->db != NULL) + dns_db_attach(cache->db, &db); + UNLOCK(&cache->lock); + if (db == NULL) + return (ISC_R_SUCCESS); + result = dns_db_findnode(cache->db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + goto cleanup_db; + } + if (result != ISC_R_SUCCESS) + goto cleanup_db; + + result = dns_db_allrdatasets(cache->db, node, NULL, + (isc_stdtime_t)0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdataset_t rdataset; + dns_rdataset_init(&rdataset); + + dns_rdatasetiter_current(iter, &rdataset); + result = dns_db_deleterdataset(cache->db, node, NULL, + rdataset.type, rdataset.covers); + dns_rdataset_disassociate(&rdataset); + if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) + break; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + dns_rdatasetiter_destroy(&iter); cleanup_node: - dns_db_detachnode(cache->db, &node); + dns_db_detachnode(cache->db, &node); cleanup_db: - dns_db_detach(&db); - return (result); + dns_db_detach(&db); + return (result); +} + +#ifdef LRU_DEBUG +static void +timer_dump(isc_task_t *task, isc_event_t *event) { + dns_cache_t *cache; + isc_interval_t interval; + isc_time_t nexttime; + + UNUSED(task); + + cache = event->ev_arg; + INSIST(VALID_CACHE(cache)); + +#ifdef LRU_DEBUG + /* XXX: abuse existing overmem method */ + dns_db_overmem(cache->db, (isc_boolean_t)-1); +#endif + + interval.seconds = DUMP_INTERVAL; + interval.nanoseconds = 0; + + RUNTIME_CHECK(isc_time_add(&cache->dump_time, &interval, &nexttime) == + ISC_R_SUCCESS); /* XXX: this is not always true */ + cache->dump_time = nexttime; + (void)isc_timer_reset(cache->dump_timer, isc_timertype_once, + &cache->dump_time, NULL, ISC_FALSE); + + isc_event_free(&event); } +#endif diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 405362cc..98d7579a 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -16,7 +16,7 @@ */ /* - * $Id: dnssec.c,v 1.90 2007/06/18 23:47:40 tbox Exp $ + * $Id: dnssec.c,v 1.91 2007/09/14 04:32:50 marka Exp $ */ /*! \file */ @@ -406,16 +406,11 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, */ dns_fixedname_init(&fnewname); labels = dns_name_countlabels(name) - 1; - if (labels - sig.labels > 0) { - dns_name_split(name, sig.labels + 1, NULL, - dns_fixedname_name(&fnewname)); - RUNTIME_CHECK(dns_name_downcase(dns_fixedname_name(&fnewname), - dns_fixedname_name(&fnewname), - NULL) - == ISC_R_SUCCESS); - } - else - dns_name_downcase(name, dns_fixedname_name(&fnewname), NULL); + RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname), + NULL) == ISC_R_SUCCESS); + if (labels - sig.labels > 0) + dns_name_split(dns_fixedname_name(&fnewname), sig.labels + 1, + NULL, dns_fixedname_name(&fnewname)); dns_name_toregion(dns_fixedname_name(&fnewname), &r); diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 4dfd015d..0dfadb48 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -31,7 +31,7 @@ /* * Principal Author: Brian Wellington - * $Id: dst_api.c,v 1.10 2007/06/19 23:47:16 tbox Exp $ + * $Id: dst_api.c,v 1.11 2007/08/28 07:20:42 tbox Exp $ */ /*! \file */ diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index 38fa94e0..ecbcedaf 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -29,7 +29,7 @@ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dst_internal.h,v 1.8 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: dst_internal.h,v 1.9 2007/08/28 07:20:42 tbox Exp $ */ #ifndef DST_DST_INTERNAL_H #define DST_DST_INTERNAL_H 1 diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c index 9240ecbd..4569fc8a 100644 --- a/lib/dns/dst_parse.c +++ b/lib/dns/dst_parse.c @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -31,7 +31,7 @@ /*% * Principal Author: Brian Wellington - * $Id: dst_parse.c,v 1.9 2007/06/19 23:47:16 tbox Exp $ + * $Id: dst_parse.c,v 1.10 2007/08/28 07:20:42 tbox Exp $ */ #include <config.h> diff --git a/lib/dns/dst_parse.h b/lib/dns/dst_parse.h index 803c0ee3..0fd474fd 100644 --- a/lib/dns/dst_parse.h +++ b/lib/dns/dst_parse.h @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -29,7 +29,7 @@ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dst_parse.h,v 1.7 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: dst_parse.h,v 1.8 2007/08/28 07:20:42 tbox Exp $ */ /*! \file */ #ifndef DST_DST_PARSE_H diff --git a/lib/dns/hmac_link.c b/lib/dns/hmac_link.c index a531e3df..18b3dfad 100644 --- a/lib/dns/hmac_link.c +++ b/lib/dns/hmac_link.c @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -31,7 +31,7 @@ /* * Principal Author: Brian Wellington - * $Id: hmac_link.c,v 1.8 2007/06/19 23:47:16 tbox Exp $ + * $Id: hmac_link.c,v 1.9 2007/08/28 07:20:42 tbox Exp $ */ #include <config.h> diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index 03a081ef..7397d56d 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.52 2007/06/19 23:47:16 tbox Exp $ +# $Id: Makefile.in,v 1.53 2007/09/12 01:09:08 each Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -24,7 +24,7 @@ top_srcdir = @top_srcdir@ HEADERS = acl.h adb.h byaddr.h cache.h callbacks.h \ cert.h compress.h \ db.h dbiterator.h dbtable.h diff.h dispatch.h \ - dnssec.h ds.h events.h fixedname.h journal.h keyflags.h \ + dnssec.h ds.h events.h fixedname.h iptable.h journal.h keyflags.h \ keytable.h keyvalues.h lib.h log.h master.h masterdump.h \ message.h name.h ncache.h \ nsec.h peer.h portlist.h rbt.h rcode.h \ diff --git a/lib/dns/include/dns/acl.h b/lib/dns/include/dns/acl.h index 81422322..058e4aac 100644 --- a/lib/dns/include/dns/acl.h +++ b/lib/dns/include/dns/acl.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: acl.h,v 1.29 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: acl.h,v 1.30 2007/09/12 01:09:08 each Exp $ */ #ifndef DNS_ACL_H #define DNS_ACL_H 1 @@ -40,6 +40,7 @@ #include <dns/name.h> #include <dns/types.h> +#include <dns/iptable.h> /*** *** Types @@ -64,17 +65,17 @@ struct dns_aclipprefix { struct dns_aclelement { dns_aclelemettype_t type; isc_boolean_t negative; - union { - dns_aclipprefix_t ip_prefix; - dns_name_t keyname; - dns_acl_t *nestedacl; - } u; + dns_name_t keyname; + dns_acl_t *nestedacl; + int node_num; }; struct dns_acl { unsigned int magic; isc_mem_t *mctx; isc_refcount_t refcount; + dns_iptable_t *iptable; +#define node_count iptable->radix->num_added_node dns_aclelement_t *elements; unsigned int alloc; /*%< Elements allocated */ unsigned int length; /*%< Elements initialized */ @@ -100,14 +101,9 @@ ISC_LANG_BEGINDECLS isc_result_t dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target); /*%< - * Create a new ACL with room for 'n' elements. - * The elements are uninitialized and the length is 0. - */ - -isc_result_t -dns_acl_appendelement(dns_acl_t *acl, const dns_aclelement_t *elt); -/*%< - * Append an element to an existing ACL. + * Create a new ACL, including an IP table and an array with room + * for 'n' ACL elements. The elements are uninitialized and the + * length is 0. */ isc_result_t @@ -122,6 +118,30 @@ dns_acl_none(isc_mem_t *mctx, dns_acl_t **target); * Create a new ACL that matches nothing. */ +isc_boolean_t +dns_acl_isany(dns_acl_t *acl); +/*%< + * Test whether ACL is set to "{ any; }" + */ + +isc_boolean_t +dns_acl_isnone(dns_acl_t *acl); +/*%< + * Test whether ACL is set to "{ none; }" + */ + +isc_result_t +dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos); +/*%< + * Merge the contents of one ACL into another. Call dns_iptable_merge() + * for the IP tables, then concatenate the element arrays. + * + * If pos is set to false, then the nested ACL is to be negated. This + * means reverse the sense of each *positive* element or IP table node, + * but leave negatives alone, so as to prevent a double-negative causing + * an unexpected postive match in the parent ACL. + */ + void dns_acl_attach(dns_acl_t *source, dns_acl_t **target); @@ -129,12 +149,6 @@ void dns_acl_detach(dns_acl_t **aclp); isc_boolean_t -dns_aclelement_equal(const dns_aclelement_t *ea, const dns_aclelement_t *eb); - -isc_boolean_t -dns_acl_equal(const dns_acl_t *a, const dns_acl_t *b); - -isc_boolean_t dns_acl_isinsecure(const dns_acl_t *a); /*%< * Return #ISC_TRUE iff the acl 'a' is considered insecure, that is, @@ -147,6 +161,9 @@ dns_acl_isinsecure(const dns_acl_t *a); isc_result_t dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env); +/*%< + * Initialize ACL environment, setting up localhost and localnets ACLs + */ void dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s); @@ -168,19 +185,17 @@ dns_acl_match(const isc_netaddr_t *reqaddr, * Match the address 'reqaddr', and optionally the key name 'reqsigner', * against 'acl'. 'reqsigner' may be NULL. * - * If there is a positive match, '*match' will be set to a positive value - * indicating the distance from the beginning of the list. - * - * If there is a negative match, '*match' will be set to a negative value - * whose absolute value indicates the distance from the beginning of - * the list. - * - * If there is a match (either positive or negative) and 'matchelt' is - * non-NULL, *matchelt will be attached to the primitive - * (non-indirect) address match list element that matched. + * If there is a match, '*match' will be set to an integer whose absolute + * value corresponds to the order in which the matching value was inserted + * into the ACL. For a positive match, this value will be positive; for a + * negative match, it will be negative. * * If there is no match, *match will be set to zero. * + * If there is a match in the element list (either positive or negative) + * and 'matchelt' is non-NULL, *matchelt will be pointed to the matching + * element. + * * Returns: *\li #ISC_R_SUCCESS Always succeeds. */ @@ -193,30 +208,14 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_aclelement_t **matchelt); /*%< * Like dns_acl_match, but matches against the single ACL element 'e' - * rather than a complete list and returns ISC_TRUE iff it matched. + * rather than a complete ACL, and returns ISC_TRUE iff it matched. + * * To determine whether the match was prositive or negative, the * caller should examine e->negative. Since the element 'e' may be - * a reference to a named ACL or a nested ACL, the matching element + * a reference to a named ACL or a nested ACL, a matching element * returned through 'matchelt' is not necessarily 'e' itself. */ -isc_result_t -dns_acl_elementmatch(const dns_acl_t *acl, - const dns_aclelement_t *elt, - const dns_aclelement_t **matchelt); -/*%< - * Search for an ACL element in 'acl' which is exactly the same as 'elt'. - * If there is one, and 'matchelt' is non NULL, then '*matchelt' will point - * to the entry. - * - * This function is intended to be used for avoiding duplicated ACL entries - * before adding an entry. - * - * Returns: - *\li #ISC_R_SUCCESS Match succeeds. - *\li #ISC_R_NOTFOUND Match fails. - */ - ISC_LANG_ENDDECLS #endif /* DNS_ACL_H */ diff --git a/lib/dns/include/dns/iptable.h b/lib/dns/include/dns/iptable.h new file mode 100644 index 00000000..d7eb140d --- /dev/null +++ b/lib/dns/include/dns/iptable.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 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. + */ + +/* $Id: iptable.h,v 1.4 2007/09/14 01:46:05 marka Exp $ */ + +#ifndef DNS_IPTABLE_H +#define DNS_IPTABLE_H 1 + +#include <isc/lang.h> +#include <isc/magic.h> +#include <isc/radix.h> + +struct dns_iptable { + unsigned int magic; + isc_mem_t *mctx; + isc_refcount_t refcount; + isc_radix_tree_t *radix; + ISC_LINK(dns_iptable_t) nextincache; +}; + +#define DNS_IPTABLE_MAGIC ISC_MAGIC('T','a','b','l') +#define DNS_IPTABLE_VALID(a) ISC_MAGIC_VALID(a, DNS_IPTABLE_MAGIC) + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS + +isc_result_t +dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target); +/* + * Create a new IP table and the underlying radix structure + */ + +isc_result_t +dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr, + isc_uint16_t bitlen, isc_boolean_t pos); +/* + * Add an IP prefix to an existing IP table + */ + +isc_result_t +dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos); +/* + * Merge one IP table into another one. + */ + +void +dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target); + +void +dns_iptable_detach(dns_iptable_t **tabp); + +ISC_LANG_ENDDECLS + +#endif /* DNS_IPTABLE_H */ diff --git a/lib/dns/include/dns/rbt.h b/lib/dns/include/dns/rbt.h index f308df0d..f5f00a9f 100644 --- a/lib/dns/include/dns/rbt.h +++ b/lib/dns/include/dns/rbt.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbt.h,v 1.68 2007/06/19 23:47:17 tbox Exp $ */ +/* $Id: rbt.h,v 1.69 2007/10/19 17:15:53 explorer Exp $ */ #ifndef DNS_RBT_H #define DNS_RBT_H 1 @@ -37,10 +37,10 @@ ISC_LANG_BEGINDECLS * Option values for dns_rbt_findnode() and dns_rbt_findname(). * These are used to form a bitmask. */ -#define DNS_RBTFIND_NOOPTIONS 0x00 -#define DNS_RBTFIND_EMPTYDATA 0x01 -#define DNS_RBTFIND_NOEXACT 0x02 -#define DNS_RBTFIND_NOPREDECESSOR 0x04 +#define DNS_RBTFIND_NOOPTIONS 0x00 +#define DNS_RBTFIND_EMPTYDATA 0x01 +#define DNS_RBTFIND_NOEXACT 0x02 +#define DNS_RBTFIND_NOPREDECESSOR 0x04 /*@}*/ #ifndef DNS_RBT_USEISCREFCOUNT @@ -52,14 +52,14 @@ ISC_LANG_BEGINDECLS /* * These should add up to 30. */ -#define DNS_RBT_LOCKLENGTH 10 -#define DNS_RBT_REFLENGTH 20 +#define DNS_RBT_LOCKLENGTH 10 +#define DNS_RBT_REFLENGTH 20 -#define DNS_RBTNODE_MAGIC ISC_MAGIC('R','B','N','O') +#define DNS_RBTNODE_MAGIC ISC_MAGIC('R','B','N','O') #if DNS_RBT_USEMAGIC -#define DNS_RBTNODE_VALID(n) ISC_MAGIC_VALID(n, DNS_RBTNODE_MAGIC) +#define DNS_RBTNODE_VALID(n) ISC_MAGIC_VALID(n, DNS_RBTNODE_MAGIC) #else -#define DNS_RBTNODE_VALID(n) ISC_TRUE +#define DNS_RBTNODE_VALID(n) ISC_TRUE #endif /*% @@ -69,66 +69,75 @@ ISC_LANG_BEGINDECLS * appended to this structure. Allocating a contiguous block of memory for * multiple dns_rbtnode structures will not work. */ -typedef struct dns_rbtnode { +typedef struct dns_rbtnode dns_rbtnode_t; +struct dns_rbtnode { #if DNS_RBT_USEMAGIC - unsigned int magic; + unsigned int magic; #endif - struct dns_rbtnode *parent; - struct dns_rbtnode *left; - struct dns_rbtnode *right; - struct dns_rbtnode *down; + dns_rbtnode_t *parent; + dns_rbtnode_t *left; + dns_rbtnode_t *right; + dns_rbtnode_t *down; #ifdef DNS_RBT_USEHASH - struct dns_rbtnode *hashnext; + dns_rbtnode_t *hashnext; #endif - /*@{*/ - /*! - * The following bitfields add up to a total bitwidth of 32. - * The range of values necessary for each item is indicated, - * but in the case of "attributes" the field is wider to accomodate - * possible future expansion. "offsetlen" could be one bit - * narrower by always adjusting its value by 1 to find the real - * offsetlen, but doing so does not gain anything (except perhaps - * another bit for "attributes", which doesn't yet need any more). - * - * In each case below the "range" indicated is what's _necessary_ for - * the bitfield to hold, not what it actually _can_ hold. - */ - unsigned int is_root : 1; /*%< range is 0..1 */ - unsigned int color : 1; /*%< range is 0..1 */ - unsigned int find_callback : 1; /*%< range is 0..1 */ - unsigned int attributes : 4; /*%< range is 0..2 */ - unsigned int namelen : 8; /*%< range is 1..255 */ - unsigned int offsetlen : 8; /*%< range is 1..128 */ - unsigned int padbytes : 9; /*%< range is 0..380 */ - /*@}*/ + + /*% + * Used for LRU cache. This linked list is used to mark nodes which + * have no data any longer, but we cannot unlink at that exact moment + * because we did not or could not obtain a write lock on the tree. + */ + ISC_LINK(dns_rbtnode_t) deadlink; + + /*@{*/ + /*! + * The following bitfields add up to a total bitwidth of 32. + * The range of values necessary for each item is indicated, + * but in the case of "attributes" the field is wider to accomodate + * possible future expansion. "offsetlen" could be one bit + * narrower by always adjusting its value by 1 to find the real + * offsetlen, but doing so does not gain anything (except perhaps + * another bit for "attributes", which doesn't yet need any more). + * + * In each case below the "range" indicated is what's _necessary_ for + * the bitfield to hold, not what it actually _can_ hold. + */ + unsigned int is_root : 1; /*%< range is 0..1 */ + unsigned int color : 1; /*%< range is 0..1 */ + unsigned int find_callback : 1; /*%< range is 0..1 */ + unsigned int attributes : 4; /*%< range is 0..2 */ + unsigned int namelen : 8; /*%< range is 1..255 */ + unsigned int offsetlen : 8; /*%< range is 1..128 */ + unsigned int padbytes : 9; /*%< range is 0..380 */ + /*@}*/ #ifdef DNS_RBT_USEHASH - unsigned int hashval; + unsigned int hashval; #endif - /*@{*/ - /*! - * These values are used in the RBT DB implementation. The appropriate - * node lock must be held before accessing them. - */ - void *data; - unsigned int dirty:1; - unsigned int wild:1; - unsigned int locknum:DNS_RBT_LOCKLENGTH; + /*@{*/ + /*! + * These values are used in the RBT DB implementation. The appropriate + * node lock must be held before accessing them. + */ + void *data; + unsigned int dirty:1; + unsigned int wild:1; + unsigned int locknum:DNS_RBT_LOCKLENGTH; #ifndef DNS_RBT_USEISCREFCOUNT - unsigned int references:DNS_RBT_REFLENGTH; + unsigned int references:DNS_RBT_REFLENGTH; #else - isc_refcount_t references; /* note that this is not in the bitfield */ + isc_refcount_t references; /* note that this is not in the bitfield */ #endif - /*@}*/ -} dns_rbtnode_t; + /*@}*/ +}; typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node, - dns_name_t *name, - void *callback_arg); + dns_name_t *name, + void *callback_arg); /***** - ***** Chain Info + ***** Chain Info *****/ /*! @@ -182,41 +191,41 @@ typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node, #define DNS_RBT_LEVELBLOCK 254 typedef struct dns_rbtnodechain { - unsigned int magic; - isc_mem_t * mctx; - /*% - * The terminal node of the chain. It is not in levels[]. - * This is ostensibly private ... but in a pinch it could be - * used tell that the chain points nowhere without needing to - * call dns_rbtnodechain_current(). - */ - dns_rbtnode_t * end; - /*% - * The maximum number of labels in a name is 128; bitstrings mean - * a conceptually very large number (which I have not bothered to - * compute) of logical levels because splitting can potentially occur - * at each bit. However, DNSSEC restricts the number of "logical" - * labels in a name to 255, meaning only 254 pointers are needed - * in the worst case. - */ - dns_rbtnode_t * levels[DNS_RBT_LEVELBLOCK]; - /*% - * level_count indicates how deep the chain points into the - * tree of trees, and is the index into the levels[] array. - * Thus, levels[level_count - 1] is the last level node stored. - * A chain that points to the top level of the tree of trees has - * a level_count of 0, the first level has a level_count of 1, and - * so on. - */ - unsigned int level_count; - /*% - * level_matches tells how many levels matched above the node - * returned by dns_rbt_findnode(). A match (partial or exact) found - * in the first level thus results in level_matches being set to 1. - * This is used by the rbtdb to set the start point for a recursive - * search of superdomains until the RR it is looking for is found. - */ - unsigned int level_matches; + unsigned int magic; + isc_mem_t * mctx; + /*% + * The terminal node of the chain. It is not in levels[]. + * This is ostensibly private ... but in a pinch it could be + * used tell that the chain points nowhere without needing to + * call dns_rbtnodechain_current(). + */ + dns_rbtnode_t * end; + /*% + * The maximum number of labels in a name is 128; bitstrings mean + * a conceptually very large number (which I have not bothered to + * compute) of logical levels because splitting can potentially occur + * at each bit. However, DNSSEC restricts the number of "logical" + * labels in a name to 255, meaning only 254 pointers are needed + * in the worst case. + */ + dns_rbtnode_t * levels[DNS_RBT_LEVELBLOCK]; + /*% + * level_count indicates how deep the chain points into the + * tree of trees, and is the index into the levels[] array. + * Thus, levels[level_count - 1] is the last level node stored. + * A chain that points to the top level of the tree of trees has + * a level_count of 0, the first level has a level_count of 1, and + * so on. + */ + unsigned int level_count; + /*% + * level_matches tells how many levels matched above the node + * returned by dns_rbt_findnode(). A match (partial or exact) found + * in the first level thus results in level_matches being set to 1. + * This is used by the rbtdb to set the start point for a recursive + * search of superdomains until the RR it is looking for is found. + */ + unsigned int level_matches; } dns_rbtnodechain_t; /***** @@ -224,32 +233,32 @@ typedef struct dns_rbtnodechain { *****/ isc_result_t dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *), - void *deleter_arg, dns_rbt_t **rbtp); + void *deleter_arg, dns_rbt_t **rbtp); /*%< * Initialize a red-black tree of trees. * * Notes: - *\li The deleter argument, if non-null, points to a function that is - * responsible for cleaning up any memory associated with the data - * pointer of a node when the node is deleted. It is passed the - * deleted node's data pointer as its first argument and deleter_arg - * as its second argument. + *\li The deleter argument, if non-null, points to a function that is + * responsible for cleaning up any memory associated with the data + * pointer of a node when the node is deleted. It is passed the + * deleted node's data pointer as its first argument and deleter_arg + * as its second argument. * * Requires: - * \li mctx is a pointer to a valid memory context. - *\li rbtp != NULL && *rbtp == NULL - *\li arg == NULL iff deleter == NULL + * \li mctx is a pointer to a valid memory context. + *\li rbtp != NULL && *rbtp == NULL + *\li arg == NULL iff deleter == NULL * * Ensures: - *\li If result is ISC_R_SUCCESS: - * *rbtp points to a valid red-black tree manager + *\li If result is ISC_R_SUCCESS: + * *rbtp points to a valid red-black tree manager * - *\li If result is failure: - * *rbtp does not point to a valid red-black tree manager. + *\li If result is failure: + * *rbtp does not point to a valid red-black tree manager. * * Returns: - *\li #ISC_R_SUCCESS Success - *\li #ISC_R_NOMEMORY Resource limit: Out of Memory + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource limit: Out of Memory */ isc_result_t @@ -258,38 +267,38 @@ dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data); * Add 'name' to the tree of trees, associated with 'data'. * * Notes: - *\li 'data' is never required to be non-NULL, but specifying it - * when the name is added is faster than searching for 'name' - * again and then setting the data pointer. The lack of a data pointer - * for a node also has other ramifications regarding whether - * dns_rbt_findname considers a node to exist, or dns_rbt_deletename - * joins nodes. + *\li 'data' is never required to be non-NULL, but specifying it + * when the name is added is faster than searching for 'name' + * again and then setting the data pointer. The lack of a data pointer + * for a node also has other ramifications regarding whether + * dns_rbt_findname considers a node to exist, or dns_rbt_deletename + * joins nodes. * * Requires: - *\li rbt is a valid rbt manager. - *\li dns_name_isabsolute(name) == TRUE + *\li rbt is a valid rbt manager. + *\li dns_name_isabsolute(name) == TRUE * * Ensures: - *\li 'name' is not altered in any way. + *\li 'name' is not altered in any way. * - *\li Any external references to nodes in the tree are unaffected by - * node splits that are necessary to insert the new name. + *\li Any external references to nodes in the tree are unaffected by + * node splits that are necessary to insert the new name. * - *\li If result is #ISC_R_SUCCESS: - * 'name' is findable in the red/black tree of trees in O(log N). - * The data pointer of the node for 'name' is set to 'data'. + *\li If result is #ISC_R_SUCCESS: + * 'name' is findable in the red/black tree of trees in O(log N). + * The data pointer of the node for 'name' is set to 'data'. * - *\li If result is #ISC_R_EXISTS or #ISC_R_NOSPACE: - * The tree of trees is unaltered. + *\li If result is #ISC_R_EXISTS or #ISC_R_NOSPACE: + * The tree of trees is unaltered. * - *\li If result is #ISC_R_NOMEMORY: - * No guarantees. + *\li If result is #ISC_R_NOMEMORY: + * No guarantees. * * Returns: - *\li #ISC_R_SUCCESS Success - *\li #ISC_R_EXISTS The name already exists with associated data. - *\li #ISC_R_NOSPACE The name had more logical labels than are allowed. - *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_EXISTS The name already exists with associated data. + *\li #ISC_R_NOSPACE The name had more logical labels than are allowed. + *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory */ isc_result_t @@ -299,175 +308,175 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep); * Just like dns_rbt_addname, but returns the address of the node. * * Requires: - *\li rbt is a valid rbt structure. - *\li dns_name_isabsolute(name) == TRUE - *\li nodep != NULL && *nodep == NULL + *\li rbt is a valid rbt structure. + *\li dns_name_isabsolute(name) == TRUE + *\li nodep != NULL && *nodep == NULL * * Ensures: - *\li 'name' is not altered in any way. + *\li 'name' is not altered in any way. * - *\li Any external references to nodes in the tree are unaffected by - * node splits that are necessary to insert the new name. + *\li Any external references to nodes in the tree are unaffected by + * node splits that are necessary to insert the new name. * - *\li If result is ISC_R_SUCCESS: - * 'name' is findable in the red/black tree of trees in O(log N). - * *nodep is the node that was added for 'name'. + *\li If result is ISC_R_SUCCESS: + * 'name' is findable in the red/black tree of trees in O(log N). + * *nodep is the node that was added for 'name'. * - *\li If result is ISC_R_EXISTS: - * The tree of trees is unaltered. - * *nodep is the existing node for 'name'. + *\li If result is ISC_R_EXISTS: + * The tree of trees is unaltered. + * *nodep is the existing node for 'name'. * - *\li If result is ISC_R_NOMEMORY: - * No guarantees. + *\li If result is ISC_R_NOMEMORY: + * No guarantees. * * Returns: - *\li #ISC_R_SUCCESS Success - *\li #ISC_R_EXISTS The name already exists, possibly without data. - *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_EXISTS The name already exists, possibly without data. + *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory */ isc_result_t dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options, - dns_name_t *foundname, void **data); + dns_name_t *foundname, void **data); /*%< * Get the data pointer associated with 'name'. * * Notes: - *\li When #DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is + *\li When #DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is * returned (also subject to #DNS_RBTFIND_EMPTYDATA), even when there is - * an exact match in the tree. + * an exact match in the tree. * *\li A node that has no data is considered not to exist for this function, * unless the #DNS_RBTFIND_EMPTYDATA option is set. * * Requires: - *\li rbt is a valid rbt manager. - *\li dns_name_isabsolute(name) == TRUE - *\li data != NULL && *data == NULL + *\li rbt is a valid rbt manager. + *\li dns_name_isabsolute(name) == TRUE + *\li data != NULL && *data == NULL * * Ensures: - *\li 'name' and the tree are not altered in any way. + *\li 'name' and the tree are not altered in any way. * - *\li If result is ISC_R_SUCCESS: - * *data is the data associated with 'name'. + *\li If result is ISC_R_SUCCESS: + * *data is the data associated with 'name'. * - *\li If result is DNS_R_PARTIALMATCH: - * *data is the data associated with the deepest superdomain - * of 'name' which has data. + *\li If result is DNS_R_PARTIALMATCH: + * *data is the data associated with the deepest superdomain + * of 'name' which has data. * - *\li If result is ISC_R_NOTFOUND: - * Neither the name nor a superdomain was found with data. + *\li If result is ISC_R_NOTFOUND: + * Neither the name nor a superdomain was found with data. * * Returns: - *\li #ISC_R_SUCCESS Success - *\li #DNS_R_PARTIALMATCH Superdomain found with data - *\li #ISC_R_NOTFOUND No match - *\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed + *\li #ISC_R_SUCCESS Success + *\li #DNS_R_PARTIALMATCH Superdomain found with data + *\li #ISC_R_NOTFOUND No match + *\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed */ isc_result_t dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname, - dns_rbtnode_t **node, dns_rbtnodechain_t *chain, - unsigned int options, dns_rbtfindcallback_t callback, - void *callback_arg); + dns_rbtnode_t **node, dns_rbtnodechain_t *chain, + unsigned int options, dns_rbtfindcallback_t callback, + void *callback_arg); /*%< * Find the node for 'name'. * * Notes: - *\li A node that has no data is considered not to exist for this function, - * unless the DNS_RBTFIND_EMPTYDATA option is set. This applies to both - * exact matches and partial matches. - * - *\li If the chain parameter is non-NULL, then the path through the tree - * to the DNSSEC predecessor of the searched for name is maintained, - * unless the DNS_RBTFIND_NOPREDECESSOR or DNS_RBTFIND_NOEXACT option - * is used. (For more details on those options, see below.) - * - *\li If there is no predecessor, then the chain will point to nowhere, as - * indicated by chain->end being NULL or dns_rbtnodechain_current - * returning ISC_R_NOTFOUND. Note that in a normal Internet DNS RBT - * there will always be a predecessor for all names except the root - * name, because '.' will exist and '.' is the predecessor of - * everything. But you can certainly construct a trivial tree and a - * search for it that has no predecessor. - * - *\li Within the chain structure, the 'levels' member of the structure holds - * the root node of each level except the first. - * - *\li The 'level_count' of the chain indicates how deep the chain to the - * predecessor name is, as an index into the 'levels[]' array. It does - * not count name elements, per se, but only levels of the tree of trees, - * the distinction arrising because multiple labels from a name can be - * stored on only one level. It is also does not include the level - * that has the node, since that level is not stored in levels[]. - * - *\li The chain's 'level_matches' is not directly related to the predecessor. - * It is the number of levels above the level of the found 'node', - * regardless of whether it was a partial match or exact match. When - * the node is found in the top level tree, or no node is found at all, - * level_matches is 0. - * - *\li When DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is + *\li A node that has no data is considered not to exist for this function, + * unless the DNS_RBTFIND_EMPTYDATA option is set. This applies to both + * exact matches and partial matches. + * + *\li If the chain parameter is non-NULL, then the path through the tree + * to the DNSSEC predecessor of the searched for name is maintained, + * unless the DNS_RBTFIND_NOPREDECESSOR or DNS_RBTFIND_NOEXACT option + * is used. (For more details on those options, see below.) + * + *\li If there is no predecessor, then the chain will point to nowhere, as + * indicated by chain->end being NULL or dns_rbtnodechain_current + * returning ISC_R_NOTFOUND. Note that in a normal Internet DNS RBT + * there will always be a predecessor for all names except the root + * name, because '.' will exist and '.' is the predecessor of + * everything. But you can certainly construct a trivial tree and a + * search for it that has no predecessor. + * + *\li Within the chain structure, the 'levels' member of the structure holds + * the root node of each level except the first. + * + *\li The 'level_count' of the chain indicates how deep the chain to the + * predecessor name is, as an index into the 'levels[]' array. It does + * not count name elements, per se, but only levels of the tree of trees, + * the distinction arrising because multiple labels from a name can be + * stored on only one level. It is also does not include the level + * that has the node, since that level is not stored in levels[]. + * + *\li The chain's 'level_matches' is not directly related to the predecessor. + * It is the number of levels above the level of the found 'node', + * regardless of whether it was a partial match or exact match. When + * the node is found in the top level tree, or no node is found at all, + * level_matches is 0. + * + *\li When DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is * returned (also subject to DNS_RBTFIND_EMPTYDATA), even when * there is an exact match in the tree. In this case, the chain - * will not point to the DNSSEC predecessor, but will instead point - * to the exact match, if there was any. Thus the preceding paragraphs - * should have "exact match" substituted for "predecessor" to describe - * how the various elements of the chain are set. This was done to - * ensure that the chain's state was sane, and to prevent problems that - * occurred when running the predecessor location code under conditions - * it was not designed for. It is not clear *where* the chain should - * point when DNS_RBTFIND_NOEXACT is set, so if you end up using a chain - * with this option because you want a particular node, let us know - * where you want the chain pointed, so this can be made more firm. + * will not point to the DNSSEC predecessor, but will instead point + * to the exact match, if there was any. Thus the preceding paragraphs + * should have "exact match" substituted for "predecessor" to describe + * how the various elements of the chain are set. This was done to + * ensure that the chain's state was sane, and to prevent problems that + * occurred when running the predecessor location code under conditions + * it was not designed for. It is not clear *where* the chain should + * point when DNS_RBTFIND_NOEXACT is set, so if you end up using a chain + * with this option because you want a particular node, let us know + * where you want the chain pointed, so this can be made more firm. * * Requires: - *\li rbt is a valid rbt manager. - *\li dns_name_isabsolute(name) == TRUE. - *\li node != NULL && *node == NULL. - *\li #DNS_RBTFIND_NOEXACT and DNS_RBTFIND_NOPREDECESSOR are mutally - * exclusive. + *\li rbt is a valid rbt manager. + *\li dns_name_isabsolute(name) == TRUE. + *\li node != NULL && *node == NULL. + *\li #DNS_RBTFIND_NOEXACT and DNS_RBTFIND_NOPREDECESSOR are mutally + * exclusive. * * Ensures: - *\li 'name' and the tree are not altered in any way. + *\li 'name' and the tree are not altered in any way. * - *\li If result is ISC_R_SUCCESS: + *\li If result is ISC_R_SUCCESS: *\verbatim - * *node is the terminal node for 'name'. + * *node is the terminal node for 'name'. - * 'foundname' and 'name' represent the same name (though not - * the same memory). + * 'foundname' and 'name' represent the same name (though not + * the same memory). - * 'chain' points to the DNSSEC predecessor, if any, of 'name'. + * 'chain' points to the DNSSEC predecessor, if any, of 'name'. * - * chain->level_matches and chain->level_count are equal. + * chain->level_matches and chain->level_count are equal. *\endverbatim * - * If result is DNS_R_PARTIALMATCH: + * If result is DNS_R_PARTIALMATCH: *\verbatim - * *node is the data associated with the deepest superdomain - * of 'name' which has data. + * *node is the data associated with the deepest superdomain + * of 'name' which has data. * - * 'foundname' is the name of deepest superdomain (which has - * data, unless the DNS_RBTFIND_EMPTYDATA option is set). + * 'foundname' is the name of deepest superdomain (which has + * data, unless the DNS_RBTFIND_EMPTYDATA option is set). * - * 'chain' points to the DNSSEC predecessor, if any, of 'name'. + * 'chain' points to the DNSSEC predecessor, if any, of 'name'. *\endverbatim * - *\li If result is ISC_R_NOTFOUND: + *\li If result is ISC_R_NOTFOUND: *\verbatim - * Neither the name nor a superdomain was found. *node is NULL. + * Neither the name nor a superdomain was found. *node is NULL. * - * 'chain' points to the DNSSEC predecessor, if any, of 'name'. + * 'chain' points to the DNSSEC predecessor, if any, of 'name'. * - * chain->level_matches is 0. + * chain->level_matches is 0. *\endverbatim * * Returns: - *\li #ISC_R_SUCCESS Success - *\li #DNS_R_PARTIALMATCH Superdomain found with data - *\li #ISC_R_NOTFOUND No match, or superdomain with no data - *\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed + *\li #ISC_R_SUCCESS Success + *\li #DNS_R_PARTIALMATCH Superdomain found with data + *\li #ISC_R_NOTFOUND No match, or superdomain with no data + *\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed */ isc_result_t @@ -476,41 +485,41 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse); * Delete 'name' from the tree of trees. * * Notes: - *\li When 'name' is removed, if recurse is ISC_TRUE then all of its + *\li When 'name' is removed, if recurse is ISC_TRUE then all of its * subnames are removed too. * * Requires: - *\li rbt is a valid rbt manager. - *\li dns_name_isabsolute(name) == TRUE + *\li rbt is a valid rbt manager. + *\li dns_name_isabsolute(name) == TRUE * * Ensures: - *\li 'name' is not altered in any way. + *\li 'name' is not altered in any way. * - *\li Does NOT ensure that any external references to nodes in the tree - * are unaffected by node joins. + *\li Does NOT ensure that any external references to nodes in the tree + * are unaffected by node joins. * - *\li If result is ISC_R_SUCCESS: - * 'name' does not appear in the tree with data; however, - * the node for the name might still exist which can be - * found with dns_rbt_findnode (but not dns_rbt_findname). + *\li If result is ISC_R_SUCCESS: + * 'name' does not appear in the tree with data; however, + * the node for the name might still exist which can be + * found with dns_rbt_findnode (but not dns_rbt_findname). * - *\li If result is ISC_R_NOTFOUND: - * 'name' does not appear in the tree with data, because - * it did not appear in the tree before the function was called. + *\li If result is ISC_R_NOTFOUND: + * 'name' does not appear in the tree with data, because + * it did not appear in the tree before the function was called. * - *\li If result is something else: - * See result codes for dns_rbt_findnode (if it fails, the - * node is not deleted) or dns_rbt_deletenode (if it fails, - * the node is deleted, but the tree is not optimized when - * it could have been). + *\li If result is something else: + * See result codes for dns_rbt_findnode (if it fails, the + * node is not deleted) or dns_rbt_deletenode (if it fails, + * the node is deleted, but the tree is not optimized when + * it could have been). * * Returns: - *\li #ISC_R_SUCCESS Success - *\li #ISC_R_NOTFOUND No match - *\li something_else Any return code from dns_rbt_findnode except - * DNS_R_PARTIALMATCH (which causes ISC_R_NOTFOUND - * to be returned instead), and any code from - * dns_rbt_deletenode. + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOTFOUND No match + *\li something_else Any return code from dns_rbt_findnode except + * DNS_R_PARTIALMATCH (which causes ISC_R_NOTFOUND + * to be returned instead), and any code from + * dns_rbt_deletenode. */ isc_result_t @@ -519,32 +528,32 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse); * Delete 'node' from the tree of trees. * * Notes: - *\li When 'node' is removed, if recurse is ISC_TRUE then all nodes - * in levels down from it are removed too. + *\li When 'node' is removed, if recurse is ISC_TRUE then all nodes + * in levels down from it are removed too. * * Requires: - *\li rbt is a valid rbt manager. - *\li node != NULL. + *\li rbt is a valid rbt manager. + *\li node != NULL. * * Ensures: - *\li Does NOT ensure that any external references to nodes in the tree - * are unaffected by node joins. + *\li Does NOT ensure that any external references to nodes in the tree + * are unaffected by node joins. * - *\li If result is ISC_R_SUCCESS: - * 'node' does not appear in the tree with data; however, - * the node might still exist if it serves as a pointer to - * a lower tree level as long as 'recurse' was false, hence - * the node could can be found with dns_rbt_findnode whem - * that function's empty_data_ok parameter is true. + *\li If result is ISC_R_SUCCESS: + * 'node' does not appear in the tree with data; however, + * the node might still exist if it serves as a pointer to + * a lower tree level as long as 'recurse' was false, hence + * the node could can be found with dns_rbt_findnode whem + * that function's empty_data_ok parameter is true. * - *\li If result is ISC_R_NOMEMORY or ISC_R_NOSPACE: - * The node was deleted, but the tree structure was not - * optimized. + *\li If result is ISC_R_NOMEMORY or ISC_R_NOSPACE: + * The node was deleted, but the tree structure was not + * optimized. * * Returns: - *\li #ISC_R_SUCCESS Success - *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory when joining nodes. - *\li #ISC_R_NOSPACE dns_name_concatenate failed when joining nodes. + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory when joining nodes. + *\li #ISC_R_NOSPACE dns_name_concatenate failed when joining nodes. */ void @@ -553,24 +562,24 @@ dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name); * Convert the sequence of labels stored at 'node' into a 'name'. * * Notes: - *\li This function does not return the full name, from the root, but - * just the labels at the indicated node. + *\li This function does not return the full name, from the root, but + * just the labels at the indicated node. * - *\li The name data pointed to by 'name' is the information stored - * in the node, not a copy. Altering the data at this pointer - * will likely cause grief. + *\li The name data pointed to by 'name' is the information stored + * in the node, not a copy. Altering the data at this pointer + * will likely cause grief. * * Requires: - * \li name->offsets == NULL + * \li name->offsets == NULL * * Ensures: - * \li 'name' is DNS_NAMEATTR_READONLY. + * \li 'name' is DNS_NAMEATTR_READONLY. * - * \li 'name' will point directly to the labels stored after the - * dns_rbtnode_t struct. + * \li 'name' will point directly to the labels stored after the + * dns_rbtnode_t struct. * - * \li 'name' will have offsets that also point to the information stored - * as part of the node. + * \li 'name' will have offsets that also point to the information stored + * as part of the node. */ isc_result_t @@ -579,35 +588,35 @@ dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name); * Like dns_rbt_namefromnode, but returns the full name from the root. * * Notes: - * \li Unlike dns_rbt_namefromnode, the name will not point directly - * to node data. Rather, dns_name_concatenate will be used to copy - * the name data from each node into the 'name' argument. + * \li Unlike dns_rbt_namefromnode, the name will not point directly + * to node data. Rather, dns_name_concatenate will be used to copy + * the name data from each node into the 'name' argument. * * Requires: - * \li name != NULL - * \li name has a dedicated buffer. + * \li name != NULL + * \li name has a dedicated buffer. * * Returns: - * \li ISC_R_SUCCESS - * \li ISC_R_NOSPACE (possible via dns_name_concatenate) - * \li DNS_R_NAMETOOLONG (possible via dns_name_concatenate) + * \li ISC_R_SUCCESS + * \li ISC_R_NOSPACE (possible via dns_name_concatenate) + * \li DNS_R_NAMETOOLONG (possible via dns_name_concatenate) */ char * dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, - unsigned int size); + unsigned int size); /*%< * Format the full name of a node for printing, using dns_name_format(). * * Notes: - * \li 'size' is the length of the printname buffer. This should be - * DNS_NAME_FORMATSIZE or larger. + * \li 'size' is the length of the printname buffer. This should be + * DNS_NAME_FORMATSIZE or larger. * * Requires: - * \li node and printname are not NULL. + * \li node and printname are not NULL. * * Returns: - * \li The 'printname' pointer. + * \li The 'printname' pointer. */ unsigned int @@ -616,7 +625,7 @@ dns_rbt_nodecount(dns_rbt_t *rbt); * Obtain the number of nodes in the tree of trees. * * Requires: - * \li rbt is a valid rbt manager. + * \li rbt is a valid rbt manager. */ void @@ -624,25 +633,25 @@ dns_rbt_destroy(dns_rbt_t **rbtp); isc_result_t dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum); /*%< - * Stop working with a red-black tree of trees. + * Stop working with a red-black tree of trees. * If 'quantum' is zero then the entire tree will be destroyed. * If 'quantum' is non zero then up to 'quantum' nodes will be destroyed * allowing the rbt to be incrementally destroyed by repeated calls to * dns_rbt_destroy2(). Once dns_rbt_destroy2() has been called no other * operations than dns_rbt_destroy()/dns_rbt_destroy2() should be * performed on the tree of trees. - * + * * Requires: - * \li *rbt is a valid rbt manager. + * \li *rbt is a valid rbt manager. * * Ensures on ISC_R_SUCCESS: - * \li All space allocated by the RBT library has been returned. + * \li All space allocated by the RBT library has been returned. * - * \li *rbt is invalidated as an rbt manager. + * \li *rbt is invalidated as an rbt manager. * * Returns: - * \li ISC_R_SUCCESS - * \li ISC_R_QUOTA if 'quantum' nodes have been destroyed. + * \li ISC_R_SUCCESS + * \li ISC_R_QUOTA if 'quantum' nodes have been destroyed. */ void @@ -652,10 +661,10 @@ dns_rbt_printall(dns_rbt_t *rbt); * tree of trees. * * Notes: - * \li The name stored at each node, along with the node's color, is printed. - * Then the down pointer, left and right pointers are displayed - * recursively in turn. NULL down pointers are silently omitted; - * NULL left and right pointers are printed. + * \li The name stored at each node, along with the node's color, is printed. + * Then the down pointer, left and right pointers are displayed + * recursively in turn. NULL down pointers are silently omitted; + * NULL left and right pointers are printed. */ /***** @@ -668,12 +677,12 @@ dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx); * Initialize 'chain'. * * Requires: - *\li 'chain' is a valid pointer. + *\li 'chain' is a valid pointer. * - *\li 'mctx' is a valid memory context. + *\li 'mctx' is a valid memory context. * * Ensures: - *\li 'chain' is suitable for use. + *\li 'chain' is suitable for use. */ void @@ -683,10 +692,10 @@ dns_rbtnodechain_reset(dns_rbtnodechain_t *chain); * 'chain'. * * Requires: - *\li 'chain' is a valid pointer. + *\li 'chain' is a valid pointer. * * Ensures: - *\li 'chain' is suitable for use, and uses no dynamic storage. + *\li 'chain' is suitable for use, and uses no dynamic storage. */ void @@ -695,163 +704,163 @@ dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain); * Free any dynamic storage associated with 'chain', and then invalidates it. * * Notes: - *\li Future calls to any dns_rbtnodechain_ function will need to call - * dns_rbtnodechain_init on the chain first (except, of course, - * dns_rbtnodechain_init itself). + *\li Future calls to any dns_rbtnodechain_ function will need to call + * dns_rbtnodechain_init on the chain first (except, of course, + * dns_rbtnodechain_init itself). * * Requires: - *\li 'chain' is a valid chain. + *\li 'chain' is a valid chain. * * Ensures: - *\li 'chain' is no longer suitable for use, and uses no dynamic storage. + *\li 'chain' is no longer suitable for use, and uses no dynamic storage. */ isc_result_t dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin, dns_rbtnode_t **node); + dns_name_t *origin, dns_rbtnode_t **node); /*%< * Provide the name, origin and node to which the chain is currently pointed. * * Notes: - *\li The tree need not have be locked against additions for the chain - * to remain valid, however there are no guarantees if any deletion - * has been made since the chain was established. + *\li The tree need not have be locked against additions for the chain + * to remain valid, however there are no guarantees if any deletion + * has been made since the chain was established. * * Requires: - *\li 'chain' is a valid chain. + *\li 'chain' is a valid chain. * * Ensures: - *\li 'node', if non-NULL, is the node to which the chain was pointed - * by dns_rbt_findnode, dns_rbtnodechain_first or dns_rbtnodechain_last. - * If none were called for the chain since it was initialized or reset, - * or if the was no predecessor to the name searched for with - * dns_rbt_findnode, then '*node' is NULL and ISC_R_NOTFOUND is returned. + *\li 'node', if non-NULL, is the node to which the chain was pointed + * by dns_rbt_findnode, dns_rbtnodechain_first or dns_rbtnodechain_last. + * If none were called for the chain since it was initialized or reset, + * or if the was no predecessor to the name searched for with + * dns_rbt_findnode, then '*node' is NULL and ISC_R_NOTFOUND is returned. * - *\li 'name', if non-NULL, is the name stored at the terminal level of - * the chain. This is typically a single label, like the "www" of - * "www.isc.org", but need not be so. At the root of the tree of trees, - * if the node is "." then 'name' is ".", otherwise it is relative to ".". - * (Minimalist and atypical case: if the tree has just the name - * "isc.org." then the root node's stored name is "isc.org." but 'name' - * will be "isc.org".) + *\li 'name', if non-NULL, is the name stored at the terminal level of + * the chain. This is typically a single label, like the "www" of + * "www.isc.org", but need not be so. At the root of the tree of trees, + * if the node is "." then 'name' is ".", otherwise it is relative to ".". + * (Minimalist and atypical case: if the tree has just the name + * "isc.org." then the root node's stored name is "isc.org." but 'name' + * will be "isc.org".) * - *\li 'origin', if non-NULL, is the sequence of labels in the levels - * above the terminal level, such as "isc.org." in the above example. - * 'origin' is always "." for the root node. + *\li 'origin', if non-NULL, is the sequence of labels in the levels + * above the terminal level, such as "isc.org." in the above example. + * 'origin' is always "." for the root node. * * * Returns: - *\li #ISC_R_SUCCESS name, origin & node were successfully set. - *\li #ISC_R_NOTFOUND The chain does not point to any node. - *\li <something_else> Any error return from dns_name_concatenate. + *\li #ISC_R_SUCCESS name, origin & node were successfully set. + *\li #ISC_R_NOTFOUND The chain does not point to any node. + *\li <something_else> Any error return from dns_name_concatenate. */ isc_result_t dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin); + dns_name_t *name, dns_name_t *origin); /*%< * Set the chain to the lexically first node in the tree of trees. * * Notes: - *\li By the definition of ordering for DNS names, the root of the tree of - * trees is the very first node, since everything else in the megatree - * uses it as a common suffix. + *\li By the definition of ordering for DNS names, the root of the tree of + * trees is the very first node, since everything else in the megatree + * uses it as a common suffix. * * Requires: - *\li 'chain' is a valid chain. - *\li 'rbt' is a valid rbt manager. + *\li 'chain' is a valid chain. + *\li 'rbt' is a valid rbt manager. * * Ensures: - *\li The chain points to the very first node of the tree. + *\li The chain points to the very first node of the tree. * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. Thus 'origin' will always be ".". + *\li 'name' and 'origin', if non-NULL, are set as described for + * dns_rbtnodechain_current. Thus 'origin' will always be ".". * * Returns: - *\li #DNS_R_NEWORIGIN The name & origin were successfully set. - *\li <something_else> Any error result from dns_rbtnodechain_current. + *\li #DNS_R_NEWORIGIN The name & origin were successfully set. + *\li <something_else> Any error result from dns_rbtnodechain_current. */ isc_result_t dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin); + dns_name_t *name, dns_name_t *origin); /*%< * Set the chain to the lexically last node in the tree of trees. * * Requires: - *\li 'chain' is a valid chain. - *\li 'rbt' is a valid rbt manager. + *\li 'chain' is a valid chain. + *\li 'rbt' is a valid rbt manager. * * Ensures: - *\li The chain points to the very last node of the tree. + *\li The chain points to the very last node of the tree. * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. + *\li 'name' and 'origin', if non-NULL, are set as described for + * dns_rbtnodechain_current. * * Returns: - *\li #DNS_R_NEWORIGIN The name & origin were successfully set. - *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory building chain. - *\li <something_else> Any error result from dns_name_concatenate. + *\li #DNS_R_NEWORIGIN The name & origin were successfully set. + *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory building chain. + *\li <something_else> Any error result from dns_name_concatenate. */ isc_result_t dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin); + dns_name_t *origin); /*%< * Adjusts chain to point the DNSSEC predecessor of the name to which it * is currently pointed. * * Requires: - *\li 'chain' is a valid chain. - *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode, - * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that - * dns_rbt_findnode is not guaranteed to point the chain somewhere, - * since there may have been no predecessor to the searched for name. + *\li 'chain' is a valid chain. + *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode, + * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that + * dns_rbt_findnode is not guaranteed to point the chain somewhere, + * since there may have been no predecessor to the searched for name. * * Ensures: - *\li The chain is pointed to the predecessor of its current target. + *\li The chain is pointed to the predecessor of its current target. * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. + *\li 'name' and 'origin', if non-NULL, are set as described for + * dns_rbtnodechain_current. * - *\li 'origin' is only if a new origin was found. + *\li 'origin' is only if a new origin was found. * * Returns: - *\li #ISC_R_SUCCESS The predecessor was found and 'name' was set. - *\li #DNS_R_NEWORIGIN The predecessor was found with a different - * origin and 'name' and 'origin' were set. - *\li #ISC_R_NOMORE There was no predecessor. - *\li <something_else> Any error result from dns_rbtnodechain_current. + *\li #ISC_R_SUCCESS The predecessor was found and 'name' was set. + *\li #DNS_R_NEWORIGIN The predecessor was found with a different + * origin and 'name' and 'origin' were set. + *\li #ISC_R_NOMORE There was no predecessor. + *\li <something_else> Any error result from dns_rbtnodechain_current. */ isc_result_t dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin); + dns_name_t *origin); /*%< * Adjusts chain to point the DNSSEC successor of the name to which it * is currently pointed. * * Requires: - *\li 'chain' is a valid chain. - *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode, - * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that - * dns_rbt_findnode is not guaranteed to point the chain somewhere, - * since there may have been no predecessor to the searched for name. + *\li 'chain' is a valid chain. + *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode, + * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that + * dns_rbt_findnode is not guaranteed to point the chain somewhere, + * since there may have been no predecessor to the searched for name. * * Ensures: - *\li The chain is pointed to the successor of its current target. + *\li The chain is pointed to the successor of its current target. * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. + *\li 'name' and 'origin', if non-NULL, are set as described for + * dns_rbtnodechain_current. * - *\li 'origin' is only if a new origin was found. + *\li 'origin' is only if a new origin was found. * * Returns: - *\li #ISC_R_SUCCESS The successor was found and 'name' was set. - *\li #DNS_R_NEWORIGIN The successor was found with a different - * origin and 'name' and 'origin' were set. - *\li #ISC_R_NOMORE There was no successor. - *\li <something_else> Any error result from dns_name_concatenate. + *\li #ISC_R_SUCCESS The successor was found and 'name' was set. + *\li #DNS_R_NEWORIGIN The successor was found with a different + * origin and 'name' and 'origin' were set. + *\li #ISC_R_NOMORE There was no successor. + *\li <something_else> Any error result from dns_name_concatenate. */ /* @@ -862,53 +871,53 @@ dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name, * hiding the back-end. The usage is the same as that of isc_refcount_xxx(). */ #ifdef DNS_RBT_USEISCREFCOUNT -#define dns_rbtnode_refinit(node, n) \ - do { \ - isc_refcount_init(&(node)->references, (n)); \ - } while (0) -#define dns_rbtnode_refdestroy(node) \ - do { \ - isc_refcount_destroy(&(node)->references); \ - } while (0) -#define dns_rbtnode_refcurrent(node) \ - isc_refcount_current(&(node)->references) -#define dns_rbtnode_refincrement0(node, refs) \ - do { \ - isc_refcount_increment0(&(node)->references, (refs)); \ - } while (0) -#define dns_rbtnode_refincrement(node, refs) \ - do { \ - isc_refcount_increment(&(node)->references, (refs)); \ - } while (0) -#define dns_rbtnode_refdecrement(node, refs) \ - do { \ - isc_refcount_decrement(&(node)->references, (refs)); \ - } while (0) +#define dns_rbtnode_refinit(node, n) \ + do { \ + isc_refcount_init(&(node)->references, (n)); \ + } while (0) +#define dns_rbtnode_refdestroy(node) \ + do { \ + isc_refcount_destroy(&(node)->references); \ + } while (0) +#define dns_rbtnode_refcurrent(node) \ + isc_refcount_current(&(node)->references) +#define dns_rbtnode_refincrement0(node, refs) \ + do { \ + isc_refcount_increment0(&(node)->references, (refs)); \ + } while (0) +#define dns_rbtnode_refincrement(node, refs) \ + do { \ + isc_refcount_increment(&(node)->references, (refs)); \ + } while (0) +#define dns_rbtnode_refdecrement(node, refs) \ + do { \ + isc_refcount_decrement(&(node)->references, (refs)); \ + } while (0) #else /* DNS_RBT_USEISCREFCOUNT */ -#define dns_rbtnode_refinit(node, n) ((node)->references = (n)) -#define dns_rbtnode_refdestroy(node) (REQUIRE((node)->references == 0)) -#define dns_rbtnode_refcurrent(node) ((node)->references) -#define dns_rbtnode_refincrement0(node, refs) \ - do { \ - unsigned int *_tmp = (unsigned int *)(refs); \ - (node)->references++; \ - if ((_tmp) != NULL) \ - (*_tmp) = (node)->references; \ - } while (0) -#define dns_rbtnode_refincrement(node, refs) \ - do { \ - REQUIRE((node)->references > 0); \ - (node)->references++; \ - if ((refs) != NULL) \ - (*refs) = (node)->references; \ - } while (0) -#define dns_rbtnode_refdecrement(node, refs) \ - do { \ - REQUIRE((node)->references > 0); \ - (node)->references--; \ - if ((refs) != NULL) \ - (*refs) = (node)->references; \ - } while (0) +#define dns_rbtnode_refinit(node, n) ((node)->references = (n)) +#define dns_rbtnode_refdestroy(node) (REQUIRE((node)->references == 0)) +#define dns_rbtnode_refcurrent(node) ((node)->references) +#define dns_rbtnode_refincrement0(node, refs) \ + do { \ + unsigned int *_tmp = (unsigned int *)(refs); \ + (node)->references++; \ + if ((_tmp) != NULL) \ + (*_tmp) = (node)->references; \ + } while (0) +#define dns_rbtnode_refincrement(node, refs) \ + do { \ + REQUIRE((node)->references > 0); \ + (node)->references++; \ + if ((refs) != NULL) \ + (*refs) = (node)->references; \ + } while (0) +#define dns_rbtnode_refdecrement(node, refs) \ + do { \ + REQUIRE((node)->references > 0); \ + (node)->references--; \ + if ((refs) != NULL) \ + (*refs) = (node)->references; \ + } while (0) #endif /* DNS_RBT_USEISCREFCOUNT */ ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 8a3e3aaa..fdaefd43 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: types.h,v 1.125 2007/06/19 23:47:17 tbox Exp $ */ +/* $Id: types.h,v 1.126 2007/09/12 01:09:08 each Exp $ */ #ifndef DNS_TYPES_H #define DNS_TYPES_H 1 @@ -68,6 +68,7 @@ typedef struct dns_fetch dns_fetch_t; typedef struct dns_fixedname dns_fixedname_t; typedef struct dns_forwarders dns_forwarders_t; typedef struct dns_fwdtable dns_fwdtable_t; +typedef struct dns_iptable dns_iptable_t; typedef isc_uint16_t dns_keyflags_t; typedef struct dns_keynode dns_keynode_t; typedef struct dns_keytable dns_keytable_t; diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index e749c7f3..167e2425 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: validator.h,v 1.38 2007/06/18 23:47:42 tbox Exp $ */ +/* $Id: validator.h,v 1.39 2007/09/19 03:38:56 marka Exp $ */ #ifndef DNS_VALIDATOR_H #define DNS_VALIDATOR_H 1 @@ -81,11 +81,24 @@ typedef struct dns_validatorevent { ISC_EVENT_COMMON(struct dns_validatorevent); dns_validator_t * validator; isc_result_t result; + /* + * Name and type of the response to be validated. + */ dns_name_t * name; dns_rdatatype_t type; + /* + * Rdata and RRSIG (if any) for positive responses. + */ dns_rdataset_t * rdataset; dns_rdataset_t * sigrdataset; + /* + * The full response. Required for negative responses. + * Also required for positive wildcard responses. + */ dns_message_t * message; + /* + * Proofs to be cached. + */ dns_name_t * proofs[3]; } dns_validatorevent_t; diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 2c57a401..041d4022 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.h,v 1.152 2007/06/18 23:47:42 tbox Exp $ */ +/* $Id: zone.h,v 1.153 2007/09/18 00:22:31 marka Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -68,6 +68,7 @@ typedef enum { #define DNS_ZONEOPT_IGNORESRVCNAME 0x00400000U /*%< ignore SRV CNAME check */ #define DNS_ZONEOPT_UPDATECHECKKSK 0x00800000U /*%< check dnskey KSK flag */ #define DNS_ZONEOPT_TRYTCPREFRESH 0x01000000U /*%< try tcp refresh on udp failure */ +#define DNS_ZONEOPT_NOTIFYTOSOA 0x02000000U /*%< Notify the SOA MNAME */ #ifndef NOMINUM_PUBLIC /* diff --git a/lib/dns/iptable.c b/lib/dns/iptable.c new file mode 100644 index 00000000..8cd6c903 --- /dev/null +++ b/lib/dns/iptable.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2007 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. + */ + +/* $Id: iptable.c,v 1.5 2007/09/28 00:11:32 each Exp $ */ + +#include <isc/mem.h> +#include <isc/radix.h> + +#include <dns/acl.h> + +static void destroy_iptable(dns_iptable_t *dtab); + +/* + * Create a new IP table and the underlying radix structure + */ +isc_result_t +dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target) { + isc_result_t result; + dns_iptable_t *tab; + + tab = isc_mem_get(mctx, sizeof(*tab)); + if (tab == NULL) + return (ISC_R_NOMEMORY); + tab->mctx = mctx; + isc_refcount_init(&tab->refcount, 1); + tab->magic = DNS_IPTABLE_MAGIC; + + result = isc_radix_create(mctx, &tab->radix, RADIX_MAXBITS); + if (result != ISC_R_SUCCESS) + goto cleanup; + + *target = tab; + return (ISC_R_SUCCESS); + + cleanup: + dns_iptable_detach(&tab); + return (result); +} + +isc_boolean_t dns_iptable_neg = ISC_FALSE; +isc_boolean_t dns_iptable_pos = ISC_TRUE; + +/* + * Add an IP prefix to an existing IP table + */ +isc_result_t +dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr, + isc_uint16_t bitlen, isc_boolean_t pos) +{ + isc_result_t result; + isc_prefix_t pfx; + isc_radix_node_t *node; + + INSIST(DNS_IPTABLE_VALID(tab)); + INSIST(tab->radix); + + NETADDR_TO_PREFIX_T(addr, pfx, bitlen); + + result = isc_radix_insert(tab->radix, &node, NULL, &pfx); + + if (result != ISC_R_SUCCESS) + return(result); + + /* If the node already contains data, don't overwrite it */ + if (node->data == NULL) { + if (pos) + node->data = &dns_iptable_pos; + else + node->data = &dns_iptable_neg; + } + + return (ISC_R_SUCCESS); +} + +/* + * Merge one IP table into another one. + */ +isc_result_t +dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos) +{ + isc_result_t result; + isc_radix_node_t *node, *new_node; + int max_node = 0; + + RADIX_WALK (source->radix->head, node) { + result = isc_radix_insert (tab->radix, &new_node, node, NULL); + + if (result != ISC_R_SUCCESS) + return(result); + + /* + * If we're negating a nested ACL, then we should + * reverse the sense of every node. However, this + * could lead to a negative node in a nested ACL + * becoming a positive match in the parent, which + * could be a security risk. To prevent this, we + * just leave the negative nodes negative. + */ + if (!pos && + node->data && + *(isc_boolean_t *) node->data == ISC_TRUE) + new_node->data = &dns_iptable_neg; + else + new_node->data = node->data; + + if (node->node_num > max_node) + max_node = node->node_num; + } RADIX_WALK_END; + + tab->radix->num_added_node += max_node; + return (ISC_R_SUCCESS); +} + +void +dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target) { + REQUIRE(DNS_IPTABLE_VALID(source)); + isc_refcount_increment(&source->refcount, NULL); + *target = source; +} + +void +dns_iptable_detach(dns_iptable_t **tabp) { + dns_iptable_t *tab = *tabp; + unsigned int refs; + REQUIRE(DNS_IPTABLE_VALID(tab)); + isc_refcount_decrement(&tab->refcount, &refs); + if (refs == 0) + destroy_iptable(tab); + *tabp = NULL; +} + +static void +destroy_iptable(dns_iptable_t *dtab) { + + REQUIRE(DNS_IPTABLE_VALID(dtab)); + + if (dtab->radix != NULL) { + isc_radix_destroy(dtab->radix, NULL); + dtab->radix = NULL; + } + + isc_refcount_destroy(&dtab->refcount); + dtab->magic = 0; + isc_mem_put(dtab->mctx, dtab, sizeof(*dtab)); +} diff --git a/lib/dns/journal.c b/lib/dns/journal.c index 83f1320e..05b73e48 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -15,12 +15,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.c,v 1.96 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: journal.c,v 1.99 2007/09/07 05:14:33 marka Exp $ */ #include <config.h> #include <stdlib.h> #include <unistd.h> +#include <errno.h> #include <isc/file.h> #include <isc/mem.h> @@ -670,7 +671,23 @@ journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, isc_result_t dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, dns_journal_t **journalp) { - return (journal_open(mctx, filename, write, write, journalp)); + isc_result_t result; + int namelen; + char backup[1024]; + + result = journal_open(mctx, filename, write, write, journalp); + if (result == ISC_R_NOTFOUND) { + namelen = strlen(filename); + if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) + namelen -= 4; + + result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", + namelen, filename); + if (result != ISC_R_SUCCESS) + return (result); + result = journal_open(mctx, backup, write, write, journalp); + } + return (result); } /* @@ -1612,6 +1629,8 @@ read_one_rr(dns_journal_t *j) { /* * Parse the rdata. */ + if (isc_buffer_remaininglength(&j->it.source) != rdlen) + FAIL(DNS_R_FORMERR); isc_buffer_setactive(&j->it.source, rdlen); dns_rdata_reset(&j->it.rdata); CHECK(dns_rdata_fromwire(&j->it.rdata, rdclass, @@ -1927,15 +1946,39 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, journal_pos_t best_guess; journal_pos_t current_pos; dns_journal_t *j = NULL; + dns_journal_t *new = NULL; journal_rawheader_t rawheader; unsigned int copy_length; - unsigned int len; + int namelen; char *buf = NULL; unsigned int size = 0; isc_result_t result; unsigned int indexend; + char newname[1024]; + char backup[1024]; + isc_boolean_t is_backup = ISC_FALSE; + + namelen = strlen(filename); + if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) + namelen -= 4; + + result = isc_string_printf(newname, sizeof(newname), "%.*s.jnw", + namelen, filename); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", + namelen, filename); + if (result != ISC_R_SUCCESS) + return (result); - CHECK(journal_open(mctx, filename, ISC_TRUE, ISC_FALSE, &j)); + result = journal_open(mctx, filename, ISC_FALSE, ISC_FALSE, &j); + if (result == ISC_R_NOTFOUND) { + is_backup = ISC_TRUE; + result = journal_open(mctx, backup, ISC_FALSE, ISC_FALSE, &j); + } + if (result != ISC_R_SUCCESS) + return (result); if (JOURNAL_EMPTY(&j->header)) { dns_journal_destroy(&j); @@ -1963,6 +2006,8 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, dns_journal_destroy(&j); return (ISC_R_SUCCESS); } + + CHECK(journal_open(mctx, newname, ISC_TRUE, ISC_TRUE, &new)); /* * Remove overhead so space test below can succeed. @@ -2003,47 +2048,12 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, CHECK(journal_next(j, &best_guess)); /* - * Enough space to proceed? + * We should now be roughly half target_size provided + * we did not reach 'serial'. If not we will just copy + * all uncommitted deltas regardless of the size. */ - if ((isc_uint32_t) (j->header.end.offset - best_guess.offset) > - (isc_uint32_t) (best_guess.offset - indexend)) { - dns_journal_destroy(&j); - return (ISC_R_NOSPACE); - } - copy_length = j->header.end.offset - best_guess.offset; - /* - * Invalidate entire index, will be rebuilt at end. - */ - for (i = 0; i < j->header.index_size; i++) { - if (POS_VALID(j->index[i])) - POS_INVALIDATE(j->index[i]); - } - - /* - * Convert the index into on-disk format and write - * it to disk. - */ - CHECK(index_to_disk(j)); - CHECK(journal_fsync(j)); - - /* - * Update the journal header. - */ - if (copy_length == 0) { - j->header.begin.serial = 0; - j->header.end.serial = 0; - j->header.begin.offset = 0; - j->header.end.offset = 0; - } else { - j->header.begin = best_guess; - } - journal_header_encode(&j->header, &rawheader); - CHECK(journal_seek(j, 0)); - CHECK(journal_write(j, &rawheader, sizeof(rawheader))); - CHECK(journal_fsync(j)); - if (copy_length != 0) { /* * Copy best_guess to end into space just freed. @@ -2057,56 +2067,90 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, goto failure; } + CHECK(journal_seek(j, best_guess.offset)); + CHECK(journal_seek(new, indexend)); for (i = 0; i < copy_length; i += size) { - len = (copy_length - i) > size ? size : + unsigned int len = (copy_length - i) > size ? size : (copy_length - i); - CHECK(journal_seek(j, best_guess.offset + i)); CHECK(journal_read(j, buf, len)); - CHECK(journal_seek(j, indexend + i)); - CHECK(journal_write(j, buf, len)); + CHECK(journal_write(new, buf, len)); } - CHECK(journal_fsync(j)); + CHECK(journal_fsync(new)); /* * Compute new header. */ - j->header.begin.offset = indexend; - j->header.end.offset = indexend + copy_length; + new->header.begin.serial = best_guess.serial; + new->header.begin.offset = indexend; + new->header.end.serial = j->header.end.serial; + new->header.end.offset = indexend + copy_length; + /* * Update the journal header. */ - journal_header_encode(&j->header, &rawheader); - CHECK(journal_seek(j, 0)); - CHECK(journal_write(j, &rawheader, sizeof(rawheader))); - CHECK(journal_fsync(j)); + journal_header_encode(&new->header, &rawheader); + CHECK(journal_seek(new, 0)); + CHECK(journal_write(new, &rawheader, sizeof(rawheader))); + CHECK(journal_fsync(new)); /* * Build new index. */ - current_pos = j->header.begin; - while (current_pos.serial != j->header.end.serial) { - index_add(j, ¤t_pos); - CHECK(journal_next(j, ¤t_pos)); + current_pos = new->header.begin; + while (current_pos.serial != new->header.end.serial) { + index_add(new, ¤t_pos); + CHECK(journal_next(new, ¤t_pos)); } /* * Write index. */ - CHECK(index_to_disk(j)); - CHECK(journal_fsync(j)); + CHECK(index_to_disk(new)); + CHECK(journal_fsync(new)); + + indexend = new->header.end.offset; + } + dns_journal_destroy(&new); - indexend = j->header.end.offset; + /* + * With a UFS file system this should just succeed and be atomic. + * Any IXFR outs will just continue and the old journal will be + * removed on final close. + * + * With MSDOS / NTFS we need to do a two stage rename triggered + * bu EEXISTS. Hopefully all IXFR's that were active at the last + * rename are now complete. + */ + if (rename(newname, filename) == -1) { + if (errno == EACCES && !is_backup) { + result = isc_file_remove(backup); + if (result != ISC_R_SUCCESS && + result != ISC_R_FILENOTFOUND) + goto failure; + if (rename(filename, backup) == -1) + goto maperrno; + if (rename(newname, filename) == -1) + goto maperrno; + (void)isc_file_remove(backup); + } else { + maperrno: + result = ISC_R_FAILURE; + goto failure; + } } + dns_journal_destroy(&j); - (void)isc_file_truncate(filename, (isc_offset_t)indexend); result = ISC_R_SUCCESS; failure: + (void)isc_file_remove(newname); if (buf != NULL) isc_mem_put(mctx, buf, size); if (j != NULL) dns_journal_destroy(&j); + if (new != NULL) + dns_journal_destroy(&new); return (result); } diff --git a/lib/dns/master.c b/lib/dns/master.c index 84e946a2..432d9278 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.c,v 1.165 2007/06/18 23:47:40 tbox Exp $ */ +/* $Id: master.c,v 1.166 2007/08/27 03:32:26 marka Exp $ */ /*! \file */ @@ -2349,8 +2349,7 @@ dns_master_loadfile2(const char *master_file, dns_name_t *top, INSIST(result != DNS_R_CONTINUE); cleanup: - if (lctx != NULL) - dns_loadctx_detach(&lctx); + dns_loadctx_detach(&lctx); return (result); } @@ -2397,8 +2396,7 @@ dns_master_loadfileinc2(const char *master_file, dns_name_t *top, } cleanup: - if (lctx != NULL) - dns_loadctx_detach(&lctx); + dns_loadctx_detach(&lctx); return (result); } @@ -2492,8 +2490,7 @@ dns_master_loadbuffer(isc_buffer_t *buffer, dns_name_t *top, INSIST(result != DNS_R_CONTINUE); cleanup: - if (lctx != NULL) - dns_loadctx_detach(&lctx); + dns_loadctx_detach(&lctx); return (result); } @@ -2529,8 +2526,7 @@ dns_master_loadbufferinc(isc_buffer_t *buffer, dns_name_t *top, } cleanup: - if (lctx != NULL) - dns_loadctx_detach(&lctx); + dns_loadctx_detach(&lctx); return (result); } diff --git a/lib/dns/message.c b/lib/dns/message.c index dd15e522..e9ba3fd0 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: message.c,v 1.236 2007/06/18 23:47:40 tbox Exp $ */ +/* $Id: message.c,v 1.237 2007/08/14 00:25:08 marka Exp $ */ /*! \file */ @@ -1337,6 +1337,11 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, rdata->type = rdtype; rdata->flags = DNS_RDATA_UPDATE; result = ISC_R_SUCCESS; + } else if (rdclass == dns_rdataclass_none && + msg->opcode == dns_opcode_update && + sectionid == DNS_SECTION_UPDATE) { + result = getrdata(source, msg, dctx, msg->rdclass, + rdtype, rdatalen, rdata); } else result = getrdata(source, msg, dctx, rdclass, rdtype, rdatalen, rdata); diff --git a/lib/dns/openssl_link.c b/lib/dns/openssl_link.c index d3e00023..b59e524e 100644 --- a/lib/dns/openssl_link.c +++ b/lib/dns/openssl_link.c @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -31,7 +31,7 @@ /* * Principal Author: Brian Wellington - * $Id: openssl_link.c,v 1.14 2007/06/19 23:47:16 tbox Exp $ + * $Id: openssl_link.c,v 1.16 2007/08/28 07:20:42 tbox Exp $ */ #ifdef OPENSSL @@ -197,6 +197,7 @@ dst__openssl_init() { mem_free(rm); #endif cleanup_mutexinit: + CRYPTO_set_locking_callback(NULL); DESTROYMUTEXBLOCK(locks, nlocks); cleanup_mutexalloc: mem_free(locks); @@ -240,16 +241,17 @@ dst__openssl_destroy() { } #endif #endif - if (locks != NULL) { - DESTROYMUTEXBLOCK(locks, nlocks); - mem_free(locks); - } if (rm != NULL) { #if OPENSSL_VERSION_NUMBER >= 0x00907000L RAND_cleanup(); #endif mem_free(rm); } + if (locks != NULL) { + CRYPTO_set_locking_callback(NULL); + DESTROYMUTEXBLOCK(locks, nlocks); + mem_free(locks); + } } isc_result_t diff --git a/lib/dns/openssldh_link.c b/lib/dns/openssldh_link.c index ccd5b076..c76441f8 100644 --- a/lib/dns/openssldh_link.c +++ b/lib/dns/openssldh_link.c @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -31,7 +31,7 @@ /* * Principal Author: Brian Wellington - * $Id: openssldh_link.c,v 1.11 2007/06/18 23:47:41 tbox Exp $ + * $Id: openssldh_link.c,v 1.12 2007/08/28 07:20:42 tbox Exp $ */ #ifdef OPENSSL diff --git a/lib/dns/openssldsa_link.c b/lib/dns/openssldsa_link.c index 83558fab..1d971f1d 100644 --- a/lib/dns/openssldsa_link.c +++ b/lib/dns/openssldsa_link.c @@ -16,7 +16,7 @@ * * Portions Copyright (C) 1995-2000 by Network Associates, Inc. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -29,7 +29,7 @@ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: openssldsa_link.c,v 1.10 2007/06/18 23:47:41 tbox Exp $ */ +/* $Id: openssldsa_link.c,v 1.11 2007/08/28 07:20:42 tbox Exp $ */ #ifdef OPENSSL diff --git a/lib/dns/rbt.c b/lib/dns/rbt.c index 5d695b60..e12b5f75 100644 --- a/lib/dns/rbt.c +++ b/lib/dns/rbt.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbt.c,v 1.137 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: rbt.c,v 1.138 2007/10/19 17:15:53 explorer Exp $ */ /*! \file */ @@ -37,36 +37,37 @@ #define DNS_NAME_USEINLINE 1 #include <dns/fixedname.h> +#include <dns/log.h> #include <dns/rbt.h> #include <dns/result.h> -#define RBT_MAGIC ISC_MAGIC('R', 'B', 'T', '+') -#define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC) +#define RBT_MAGIC ISC_MAGIC('R', 'B', 'T', '+') +#define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC) /* * XXXDCL Since parent pointers were added in again, I could remove all of the * chain junk, and replace with dns_rbt_firstnode, _previousnode, _nextnode, * _lastnode. This would involve pretty major change to the API. */ -#define CHAIN_MAGIC ISC_MAGIC('0', '-', '0', '-') -#define VALID_CHAIN(chain) ISC_MAGIC_VALID(chain, CHAIN_MAGIC) +#define CHAIN_MAGIC ISC_MAGIC('0', '-', '0', '-') +#define VALID_CHAIN(chain) ISC_MAGIC_VALID(chain, CHAIN_MAGIC) -#define RBT_HASH_SIZE 64 +#define RBT_HASH_SIZE 64 #ifdef RBT_MEM_TEST #undef RBT_HASH_SIZE -#define RBT_HASH_SIZE 2 /*%< To give the reallocation code a workout. */ +#define RBT_HASH_SIZE 2 /*%< To give the reallocation code a workout. */ #endif struct dns_rbt { - unsigned int magic; - isc_mem_t * mctx; - dns_rbtnode_t * root; - void (*data_deleter)(void *, void *); - void * deleter_arg; - unsigned int nodecount; - unsigned int hashsize; - dns_rbtnode_t ** hashtable; + unsigned int magic; + isc_mem_t * mctx; + dns_rbtnode_t * root; + void (*data_deleter)(void *, void *); + void * deleter_arg; + unsigned int nodecount; + unsigned int hashsize; + dns_rbtnode_t ** hashtable; }; #define RED 0 @@ -75,45 +76,45 @@ struct dns_rbt { /*% * Elements of the rbtnode structure. */ -#define PARENT(node) ((node)->parent) -#define LEFT(node) ((node)->left) -#define RIGHT(node) ((node)->right) -#define DOWN(node) ((node)->down) -#define DATA(node) ((node)->data) -#define HASHNEXT(node) ((node)->hashnext) -#define HASHVAL(node) ((node)->hashval) -#define COLOR(node) ((node)->color) -#define NAMELEN(node) ((node)->namelen) -#define OFFSETLEN(node) ((node)->offsetlen) -#define ATTRS(node) ((node)->attributes) -#define PADBYTES(node) ((node)->padbytes) -#define IS_ROOT(node) ISC_TF((node)->is_root == 1) -#define FINDCALLBACK(node) ISC_TF((node)->find_callback == 1) +#define PARENT(node) ((node)->parent) +#define LEFT(node) ((node)->left) +#define RIGHT(node) ((node)->right) +#define DOWN(node) ((node)->down) +#define DATA(node) ((node)->data) +#define HASHNEXT(node) ((node)->hashnext) +#define HASHVAL(node) ((node)->hashval) +#define COLOR(node) ((node)->color) +#define NAMELEN(node) ((node)->namelen) +#define OFFSETLEN(node) ((node)->offsetlen) +#define ATTRS(node) ((node)->attributes) +#define PADBYTES(node) ((node)->padbytes) +#define IS_ROOT(node) ISC_TF((node)->is_root == 1) +#define FINDCALLBACK(node) ISC_TF((node)->find_callback == 1) /*% * Structure elements from the rbtdb.c, not * used as part of the rbt.c algorithms. */ -#define DIRTY(node) ((node)->dirty) -#define WILD(node) ((node)->wild) -#define LOCKNUM(node) ((node)->locknum) +#define DIRTY(node) ((node)->dirty) +#define WILD(node) ((node)->wild) +#define LOCKNUM(node) ((node)->locknum) /*% * The variable length stuff stored after the node. */ -#define NAME(node) ((unsigned char *)((node) + 1)) -#define OFFSETS(node) (NAME(node) + NAMELEN(node)) +#define NAME(node) ((unsigned char *)((node) + 1)) +#define OFFSETS(node) (NAME(node) + NAMELEN(node)) -#define NODE_SIZE(node) (sizeof(*node) + \ - NAMELEN(node) + OFFSETLEN(node) + PADBYTES(node)) +#define NODE_SIZE(node) (sizeof(*node) + \ + NAMELEN(node) + OFFSETLEN(node) + PADBYTES(node)) /*% * Color management. */ -#define IS_RED(node) ((node) != NULL && (node)->color == RED) -#define IS_BLACK(node) ((node) == NULL || (node)->color == BLACK) -#define MAKE_RED(node) ((node)->color = RED) -#define MAKE_BLACK(node) ((node)->color = BLACK) +#define IS_RED(node) ((node) != NULL && (node)->color == RED) +#define IS_BLACK(node) ((node) == NULL || (node)->color == BLACK) +#define MAKE_RED(node) ((node)->color = RED) +#define MAKE_BLACK(node) ((node)->color = BLACK) /*% * Chain management. @@ -123,7 +124,7 @@ struct dns_rbt { * of memory concerns, when chains were first implemented). */ #define ADD_LEVEL(chain, node) \ - (chain)->levels[(chain)->level_count++] = (node) + (chain)->levels[(chain)->level_count++] = (node) /*% * The following macros directly access normally private name variables. @@ -133,12 +134,12 @@ struct dns_rbt { #define NODENAME(node, name) \ do { \ - (name)->length = NAMELEN(node); \ - (name)->labels = OFFSETLEN(node); \ - (name)->ndata = NAME(node); \ - (name)->offsets = OFFSETS(node); \ - (name)->attributes = ATTRS(node); \ - (name)->attributes |= DNS_NAMEATTR_READONLY; \ + (name)->length = NAMELEN(node); \ + (name)->labels = OFFSETLEN(node); \ + (name)->ndata = NAME(node); \ + (name)->offsets = OFFSETS(node); \ + (name)->attributes = ATTRS(node); \ + (name)->attributes |= DNS_NAMEATTR_READONLY; \ } while (0) #ifdef DNS_RBT_USEHASH @@ -154,13 +155,13 @@ inithash(dns_rbt_t *rbt); dns_name_t Name(dns_rbtnode_t *node); dns_name_t Name(dns_rbtnode_t *node) { - dns_name_t name; + dns_name_t name; - dns_name_init(&name, NULL); - if (node != NULL) - NODENAME(node, &name); + dns_name_init(&name, NULL); + if (node != NULL) + NODENAME(node, &name); - return (name); + return (name); } static void dns_rbt_printnodename(dns_rbtnode_t *node); @@ -168,17 +169,17 @@ static void dns_rbt_printnodename(dns_rbtnode_t *node); static inline dns_rbtnode_t * find_up(dns_rbtnode_t *node) { - dns_rbtnode_t *root; + dns_rbtnode_t *root; - /* - * Return the node in the level above the argument node that points - * to the level the argument node is in. If the argument node is in - * the top level, the return value is NULL. - */ - for (root = node; ! IS_ROOT(root); root = PARENT(root)) - ; /* Nothing. */ + /* + * Return the node in the level above the argument node that points + * to the level the argument node is in. If the argument node is in + * the top level, the return value is NULL. + */ + for (root = node; ! IS_ROOT(root); root = PARENT(root)) + ; /* Nothing. */ - return (PARENT(root)); + return (PARENT(root)); } /* @@ -203,8 +204,8 @@ static inline void rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp); static void -dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, - dns_rbtnode_t **rootp); +dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, + dns_rbtnode_t **rootp); static void dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp); @@ -214,48 +215,50 @@ dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node); static void dns_rbt_deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, - dns_rbtnode_t **nodep); + dns_rbtnode_t **nodep); /* * Initialize a red/black tree of trees. */ isc_result_t dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *), - void *deleter_arg, dns_rbt_t **rbtp) + void *deleter_arg, dns_rbt_t **rbtp) { #ifdef DNS_RBT_USEHASH - isc_result_t result; + isc_result_t result; #endif - dns_rbt_t *rbt; - - - REQUIRE(mctx != NULL); - REQUIRE(rbtp != NULL && *rbtp == NULL); - REQUIRE(deleter == NULL ? deleter_arg == NULL : 1); - - rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt)); - if (rbt == NULL) - return (ISC_R_NOMEMORY); - - rbt->mctx = mctx; - rbt->data_deleter = deleter; - rbt->deleter_arg = deleter_arg; - rbt->root = NULL; - rbt->nodecount = 0; - rbt->hashtable = NULL; - rbt->hashsize = 0; + dns_rbt_t *rbt; + + + REQUIRE(mctx != NULL); + REQUIRE(rbtp != NULL && *rbtp == NULL); + REQUIRE(deleter == NULL ? deleter_arg == NULL : 1); + + rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt)); + if (rbt == NULL) + return (ISC_R_NOMEMORY); + + rbt->mctx = mctx; + rbt->data_deleter = deleter; + rbt->deleter_arg = deleter_arg; + rbt->root = NULL; + rbt->nodecount = 0; + rbt->hashtable = NULL; + rbt->hashsize = 0; + #ifdef DNS_RBT_USEHASH - result = inithash(rbt); - if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, rbt, sizeof(*rbt)); - return (result); - } + result = inithash(rbt); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, rbt, sizeof(*rbt)); + return (result); + } #endif - rbt->magic = RBT_MAGIC; - *rbtp = rbt; + rbt->magic = RBT_MAGIC; + + *rbtp = rbt; - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } /* @@ -263,88 +266,88 @@ dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *), */ void dns_rbt_destroy(dns_rbt_t **rbtp) { - RUNTIME_CHECK(dns_rbt_destroy2(rbtp, 0) == ISC_R_SUCCESS); + RUNTIME_CHECK(dns_rbt_destroy2(rbtp, 0) == ISC_R_SUCCESS); } isc_result_t dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) { - dns_rbt_t *rbt; + dns_rbt_t *rbt; - REQUIRE(rbtp != NULL && VALID_RBT(*rbtp)); + REQUIRE(rbtp != NULL && VALID_RBT(*rbtp)); - rbt = *rbtp; + rbt = *rbtp; - dns_rbt_deletetreeflat(rbt, quantum, &rbt->root); - if (rbt->root != NULL) - return (ISC_R_QUOTA); + dns_rbt_deletetreeflat(rbt, quantum, &rbt->root); + if (rbt->root != NULL) + return (ISC_R_QUOTA); - INSIST(rbt->nodecount == 0); + INSIST(rbt->nodecount == 0); - if (rbt->hashtable != NULL) - isc_mem_put(rbt->mctx, rbt->hashtable, - rbt->hashsize * sizeof(dns_rbtnode_t *)); + if (rbt->hashtable != NULL) + isc_mem_put(rbt->mctx, rbt->hashtable, + rbt->hashsize * sizeof(dns_rbtnode_t *)); - rbt->magic = 0; + rbt->magic = 0; - isc_mem_put(rbt->mctx, rbt, sizeof(*rbt)); - *rbtp = NULL; - return (ISC_R_SUCCESS); + isc_mem_put(rbt->mctx, rbt, sizeof(*rbt)); + *rbtp = NULL; + return (ISC_R_SUCCESS); } unsigned int dns_rbt_nodecount(dns_rbt_t *rbt) { - REQUIRE(VALID_RBT(rbt)); - return (rbt->nodecount); + REQUIRE(VALID_RBT(rbt)); + return (rbt->nodecount); } static inline isc_result_t chain_name(dns_rbtnodechain_t *chain, dns_name_t *name, - isc_boolean_t include_chain_end) + isc_boolean_t include_chain_end) { - dns_name_t nodename; - isc_result_t result = ISC_R_SUCCESS; - int i; - - dns_name_init(&nodename, NULL); - - if (include_chain_end && chain->end != NULL) { - NODENAME(chain->end, &nodename); - result = dns_name_copy(&nodename, name, NULL); - if (result != ISC_R_SUCCESS) - return (result); - } else - dns_name_reset(name); - - for (i = (int)chain->level_count - 1; i >= 0; i--) { - NODENAME(chain->levels[i], &nodename); - result = dns_name_concatenate(name, &nodename, name, NULL); - - if (result != ISC_R_SUCCESS) - return (result); - } - return (result); + dns_name_t nodename; + isc_result_t result = ISC_R_SUCCESS; + int i; + + dns_name_init(&nodename, NULL); + + if (include_chain_end && chain->end != NULL) { + NODENAME(chain->end, &nodename); + result = dns_name_copy(&nodename, name, NULL); + if (result != ISC_R_SUCCESS) + return (result); + } else + dns_name_reset(name); + + for (i = (int)chain->level_count - 1; i >= 0; i--) { + NODENAME(chain->levels[i], &nodename); + result = dns_name_concatenate(name, &nodename, name, NULL); + + if (result != ISC_R_SUCCESS) + return (result); + } + return (result); } static inline isc_result_t move_chain_to_last(dns_rbtnodechain_t *chain, dns_rbtnode_t *node) { - do { - /* - * Go as far right and then down as much as possible, - * as long as the rightmost node has a down pointer. - */ - while (RIGHT(node) != NULL) - node = RIGHT(node); + do { + /* + * Go as far right and then down as much as possible, + * as long as the rightmost node has a down pointer. + */ + while (RIGHT(node) != NULL) + node = RIGHT(node); - if (DOWN(node) == NULL) - break; + if (DOWN(node) == NULL) + break; - ADD_LEVEL(chain, node); - node = DOWN(node); - } while (1); + ADD_LEVEL(chain, node); + node = DOWN(node); + } while (1); - chain->end = node; + chain->end = node; - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } /* @@ -353,281 +356,281 @@ move_chain_to_last(dns_rbtnodechain_t *chain, dns_rbtnode_t *node) { isc_result_t dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) { - /* - * Does this thing have too many variables or what? - */ - dns_rbtnode_t **root, *parent, *child, *current, *new_current; - dns_name_t *add_name, *new_name, current_name, *prefix, *suffix; - dns_fixedname_t fixedcopy, fixedprefix, fixedsuffix, fnewname; - dns_offsets_t current_offsets; - dns_namereln_t compared; - isc_result_t result = ISC_R_SUCCESS; - dns_rbtnodechain_t chain; - unsigned int common_labels; - unsigned int nlabels, hlabels; - int order; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(dns_name_isabsolute(name)); - REQUIRE(nodep != NULL && *nodep == NULL); - - /* - * Create a copy of the name so the original name structure is - * not modified. - */ - dns_fixedname_init(&fixedcopy); - add_name = dns_fixedname_name(&fixedcopy); - dns_name_clone(name, add_name); - - if (rbt->root == NULL) { - result = create_node(rbt->mctx, add_name, &new_current); - if (result == ISC_R_SUCCESS) { - rbt->nodecount++; - new_current->is_root = 1; - rbt->root = new_current; - *nodep = new_current; - hash_node(rbt, new_current, name); - } - return (result); - } - - dns_rbtnodechain_init(&chain, rbt->mctx); - - dns_fixedname_init(&fixedprefix); - dns_fixedname_init(&fixedsuffix); - prefix = dns_fixedname_name(&fixedprefix); - suffix = dns_fixedname_name(&fixedsuffix); - - root = &rbt->root; - INSIST(IS_ROOT(*root)); - parent = NULL; - current = NULL; - child = *root; - dns_name_init(¤t_name, current_offsets); - dns_fixedname_init(&fnewname); - new_name = dns_fixedname_name(&fnewname); - nlabels = dns_name_countlabels(name); - hlabels = 0; - - do { - current = child; - - NODENAME(current, ¤t_name); - compared = dns_name_fullcompare(add_name, ¤t_name, - &order, &common_labels); - - if (compared == dns_namereln_equal) { - *nodep = current; - result = ISC_R_EXISTS; - break; - - } - - if (compared == dns_namereln_none) { - - if (order < 0) { - parent = current; - child = LEFT(current); - - } else if (order > 0) { - parent = current; - child = RIGHT(current); - - } - - } else { - /* - * This name has some suffix in common with the - * name at the current node. If the name at - * the current node is shorter, that means the - * new name should be in a subtree. If the - * name at the current node is longer, that means - * the down pointer to this tree should point - * to a new tree that has the common suffix, and - * the non-common parts of these two names should - * start a new tree. - */ - hlabels += common_labels; - if (compared == dns_namereln_subdomain) { - /* - * All of the existing labels are in common, - * so the new name is in a subtree. - * Whack off the common labels for the - * not-in-common part to be searched for - * in the next level. - */ - dns_name_split(add_name, common_labels, - add_name, NULL); - - /* - * Follow the down pointer (possibly NULL). - */ - root = &DOWN(current); - - INSIST(*root == NULL || - (IS_ROOT(*root) && - PARENT(*root) == current)); - - parent = NULL; - child = DOWN(current); - ADD_LEVEL(&chain, current); - - } else { - /* - * The number of labels in common is fewer - * than the number of labels at the current - * node, so the current node must be adjusted - * to have just the common suffix, and a down - * pointer made to a new tree. - */ - - INSIST(compared == dns_namereln_commonancestor - || compared == dns_namereln_contains); - - /* - * Ensure the number of levels in the tree - * does not exceed the number of logical - * levels allowed by DNSSEC. - * - * XXXDCL need a better error result? - * - * XXXDCL Since chain ancestors were removed, - * no longer used by dns_rbt_addonlevel(), - * this is the only real use of chains in the - * function. It could be done instead with - * a simple integer variable, but I am pressed - * for time. - */ - if (chain.level_count == - (sizeof(chain.levels) / - sizeof(*chain.levels))) { - result = ISC_R_NOSPACE; - break; - } - - /* - * Split the name into two parts, a prefix - * which is the not-in-common parts of the - * two names and a suffix that is the common - * parts of them. - */ - dns_name_split(¤t_name, common_labels, - prefix, suffix); - result = create_node(rbt->mctx, suffix, - &new_current); - - if (result != ISC_R_SUCCESS) - break; - - /* - * Reproduce the tree attributes of the - * current node. - */ - new_current->is_root = current->is_root; - PARENT(new_current) = PARENT(current); - LEFT(new_current) = LEFT(current); - RIGHT(new_current) = RIGHT(current); - COLOR(new_current) = COLOR(current); - - /* - * Fix pointers that were to the current node. - */ - if (parent != NULL) { - if (LEFT(parent) == current) - LEFT(parent) = new_current; - else - RIGHT(parent) = new_current; - } - if (LEFT(new_current) != NULL) - PARENT(LEFT(new_current)) = - new_current; - if (RIGHT(new_current) != NULL) - PARENT(RIGHT(new_current)) = - new_current; - if (*root == current) - *root = new_current; - - NAMELEN(current) = prefix->length; - OFFSETLEN(current) = prefix->labels; - memcpy(OFFSETS(current), prefix->offsets, - prefix->labels); - PADBYTES(current) += - (current_name.length - prefix->length) + - (current_name.labels - prefix->labels); - - /* - * Set up the new root of the next level. - * By definition it will not be the top - * level tree, so clear DNS_NAMEATTR_ABSOLUTE. - */ - current->is_root = 1; - PARENT(current) = new_current; - DOWN(new_current) = current; - root = &DOWN(new_current); - - ADD_LEVEL(&chain, new_current); - - LEFT(current) = NULL; - RIGHT(current) = NULL; - - MAKE_BLACK(current); - ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE; - - rbt->nodecount++; - dns_name_getlabelsequence(name, - nlabels - hlabels, - hlabels, new_name); - hash_node(rbt, new_current, new_name); - - if (common_labels == - dns_name_countlabels(add_name)) { - /* - * The name has been added by pushing - * the not-in-common parts down to - * a new level. - */ - *nodep = new_current; - return (ISC_R_SUCCESS); - - } else { - /* - * The current node has no data, - * because it is just a placeholder. - * Its data pointer is already NULL - * from create_node()), so there's - * nothing more to do to it. - */ - - /* - * The not-in-common parts of the new - * name will be inserted into the new - * level following this loop (unless - * result != ISC_R_SUCCESS, which - * is tested after the loop ends). - */ - dns_name_split(add_name, common_labels, - add_name, NULL); - - break; - } - - } - - } - - } while (child != NULL); - - if (result == ISC_R_SUCCESS) - result = create_node(rbt->mctx, add_name, &new_current); - - if (result == ISC_R_SUCCESS) { - dns_rbt_addonlevel(new_current, current, order, root); - rbt->nodecount++; - *nodep = new_current; - hash_node(rbt, new_current, name); - } - - return (result); + /* + * Does this thing have too many variables or what? + */ + dns_rbtnode_t **root, *parent, *child, *current, *new_current; + dns_name_t *add_name, *new_name, current_name, *prefix, *suffix; + dns_fixedname_t fixedcopy, fixedprefix, fixedsuffix, fnewname; + dns_offsets_t current_offsets; + dns_namereln_t compared; + isc_result_t result = ISC_R_SUCCESS; + dns_rbtnodechain_t chain; + unsigned int common_labels; + unsigned int nlabels, hlabels; + int order; + + REQUIRE(VALID_RBT(rbt)); + REQUIRE(dns_name_isabsolute(name)); + REQUIRE(nodep != NULL && *nodep == NULL); + + /* + * Create a copy of the name so the original name structure is + * not modified. + */ + dns_fixedname_init(&fixedcopy); + add_name = dns_fixedname_name(&fixedcopy); + dns_name_clone(name, add_name); + + if (rbt->root == NULL) { + result = create_node(rbt->mctx, add_name, &new_current); + if (result == ISC_R_SUCCESS) { + rbt->nodecount++; + new_current->is_root = 1; + rbt->root = new_current; + *nodep = new_current; + hash_node(rbt, new_current, name); + } + return (result); + } + + dns_rbtnodechain_init(&chain, rbt->mctx); + + dns_fixedname_init(&fixedprefix); + dns_fixedname_init(&fixedsuffix); + prefix = dns_fixedname_name(&fixedprefix); + suffix = dns_fixedname_name(&fixedsuffix); + + root = &rbt->root; + INSIST(IS_ROOT(*root)); + parent = NULL; + current = NULL; + child = *root; + dns_name_init(¤t_name, current_offsets); + dns_fixedname_init(&fnewname); + new_name = dns_fixedname_name(&fnewname); + nlabels = dns_name_countlabels(name); + hlabels = 0; + + do { + current = child; + + NODENAME(current, ¤t_name); + compared = dns_name_fullcompare(add_name, ¤t_name, + &order, &common_labels); + + if (compared == dns_namereln_equal) { + *nodep = current; + result = ISC_R_EXISTS; + break; + + } + + if (compared == dns_namereln_none) { + + if (order < 0) { + parent = current; + child = LEFT(current); + + } else if (order > 0) { + parent = current; + child = RIGHT(current); + + } + + } else { + /* + * This name has some suffix in common with the + * name at the current node. If the name at + * the current node is shorter, that means the + * new name should be in a subtree. If the + * name at the current node is longer, that means + * the down pointer to this tree should point + * to a new tree that has the common suffix, and + * the non-common parts of these two names should + * start a new tree. + */ + hlabels += common_labels; + if (compared == dns_namereln_subdomain) { + /* + * All of the existing labels are in common, + * so the new name is in a subtree. + * Whack off the common labels for the + * not-in-common part to be searched for + * in the next level. + */ + dns_name_split(add_name, common_labels, + add_name, NULL); + + /* + * Follow the down pointer (possibly NULL). + */ + root = &DOWN(current); + + INSIST(*root == NULL || + (IS_ROOT(*root) && + PARENT(*root) == current)); + + parent = NULL; + child = DOWN(current); + ADD_LEVEL(&chain, current); + + } else { + /* + * The number of labels in common is fewer + * than the number of labels at the current + * node, so the current node must be adjusted + * to have just the common suffix, and a down + * pointer made to a new tree. + */ + + INSIST(compared == dns_namereln_commonancestor + || compared == dns_namereln_contains); + + /* + * Ensure the number of levels in the tree + * does not exceed the number of logical + * levels allowed by DNSSEC. + * + * XXXDCL need a better error result? + * + * XXXDCL Since chain ancestors were removed, + * no longer used by dns_rbt_addonlevel(), + * this is the only real use of chains in the + * function. It could be done instead with + * a simple integer variable, but I am pressed + * for time. + */ + if (chain.level_count == + (sizeof(chain.levels) / + sizeof(*chain.levels))) { + result = ISC_R_NOSPACE; + break; + } + + /* + * Split the name into two parts, a prefix + * which is the not-in-common parts of the + * two names and a suffix that is the common + * parts of them. + */ + dns_name_split(¤t_name, common_labels, + prefix, suffix); + result = create_node(rbt->mctx, suffix, + &new_current); + + if (result != ISC_R_SUCCESS) + break; + + /* + * Reproduce the tree attributes of the + * current node. + */ + new_current->is_root = current->is_root; + PARENT(new_current) = PARENT(current); + LEFT(new_current) = LEFT(current); + RIGHT(new_current) = RIGHT(current); + COLOR(new_current) = COLOR(current); + + /* + * Fix pointers that were to the current node. + */ + if (parent != NULL) { + if (LEFT(parent) == current) + LEFT(parent) = new_current; + else + RIGHT(parent) = new_current; + } + if (LEFT(new_current) != NULL) + PARENT(LEFT(new_current)) = + new_current; + if (RIGHT(new_current) != NULL) + PARENT(RIGHT(new_current)) = + new_current; + if (*root == current) + *root = new_current; + + NAMELEN(current) = prefix->length; + OFFSETLEN(current) = prefix->labels; + memcpy(OFFSETS(current), prefix->offsets, + prefix->labels); + PADBYTES(current) += + (current_name.length - prefix->length) + + (current_name.labels - prefix->labels); + + /* + * Set up the new root of the next level. + * By definition it will not be the top + * level tree, so clear DNS_NAMEATTR_ABSOLUTE. + */ + current->is_root = 1; + PARENT(current) = new_current; + DOWN(new_current) = current; + root = &DOWN(new_current); + + ADD_LEVEL(&chain, new_current); + + LEFT(current) = NULL; + RIGHT(current) = NULL; + + MAKE_BLACK(current); + ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE; + + rbt->nodecount++; + dns_name_getlabelsequence(name, + nlabels - hlabels, + hlabels, new_name); + hash_node(rbt, new_current, new_name); + + if (common_labels == + dns_name_countlabels(add_name)) { + /* + * The name has been added by pushing + * the not-in-common parts down to + * a new level. + */ + *nodep = new_current; + return (ISC_R_SUCCESS); + + } else { + /* + * The current node has no data, + * because it is just a placeholder. + * Its data pointer is already NULL + * from create_node()), so there's + * nothing more to do to it. + */ + + /* + * The not-in-common parts of the new + * name will be inserted into the new + * level following this loop (unless + * result != ISC_R_SUCCESS, which + * is tested after the loop ends). + */ + dns_name_split(add_name, common_labels, + add_name, NULL); + + break; + } + + } + + } + + } while (child != NULL); + + if (result == ISC_R_SUCCESS) + result = create_node(rbt->mctx, add_name, &new_current); + + if (result == ISC_R_SUCCESS) { + dns_rbt_addonlevel(new_current, current, order, root); + rbt->nodecount++; + *nodep = new_current; + hash_node(rbt, new_current, name); + } + + return (result); } /* @@ -635,29 +638,29 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) { */ isc_result_t dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) { - isc_result_t result; - dns_rbtnode_t *node; + isc_result_t result; + dns_rbtnode_t *node; - REQUIRE(VALID_RBT(rbt)); - REQUIRE(dns_name_isabsolute(name)); + REQUIRE(VALID_RBT(rbt)); + REQUIRE(dns_name_isabsolute(name)); - node = NULL; + node = NULL; - result = dns_rbt_addnode(rbt, name, &node); + result = dns_rbt_addnode(rbt, name, &node); - /* - * dns_rbt_addnode will report the node exists even when - * it does not have data associated with it, but the - * dns_rbt_*name functions all behave depending on whether - * there is data associated with a node. - */ - if (result == ISC_R_SUCCESS || - (result == ISC_R_EXISTS && DATA(node) == NULL)) { - DATA(node) = data; - result = ISC_R_SUCCESS; - } + /* + * dns_rbt_addnode will report the node exists even when + * it does not have data associated with it, but the + * dns_rbt_*name functions all behave depending on whether + * there is data associated with a node. + */ + if (result == ISC_R_SUCCESS || + (result == ISC_R_EXISTS && DATA(node) == NULL)) { + DATA(node) = data; + result = ISC_R_SUCCESS; + } - return (result); + return (result); } /* @@ -665,500 +668,500 @@ dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) { */ isc_result_t dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname, - dns_rbtnode_t **node, dns_rbtnodechain_t *chain, - unsigned int options, dns_rbtfindcallback_t callback, - void *callback_arg) + dns_rbtnode_t **node, dns_rbtnodechain_t *chain, + unsigned int options, dns_rbtfindcallback_t callback, + void *callback_arg) { - dns_rbtnode_t *current, *last_compared, *current_root; - dns_rbtnodechain_t localchain; - dns_name_t *search_name, current_name, *callback_name; - dns_fixedname_t fixedcallbackname, fixedsearchname; - dns_namereln_t compared; - isc_result_t result, saved_result; - unsigned int common_labels; - unsigned int hlabels = 0; - int order; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(dns_name_isabsolute(name)); - REQUIRE(node != NULL && *node == NULL); - REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)) - != (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)); - - /* - * If there is a chain it needs to appear to be in a sane state, - * otherwise a chain is still needed to generate foundname and - * callback_name. - */ - if (chain == NULL) { - options |= DNS_RBTFIND_NOPREDECESSOR; - chain = &localchain; - dns_rbtnodechain_init(chain, rbt->mctx); - } else - dns_rbtnodechain_reset(chain); - - if (rbt->root == NULL) - return (ISC_R_NOTFOUND); - else { - /* - * Appease GCC about variables it incorrectly thinks are - * possibly used uninitialized. - */ - compared = dns_namereln_none; - last_compared = NULL; - } - - dns_fixedname_init(&fixedcallbackname); - callback_name = dns_fixedname_name(&fixedcallbackname); - - /* - * search_name is the name segment being sought in each tree level. - * By using a fixedname, the search_name will definitely have offsets - * for use by any splitting. - * By using dns_name_clone, no name data should be copied thanks to - * the lack of bitstring labels. - */ - dns_fixedname_init(&fixedsearchname); - search_name = dns_fixedname_name(&fixedsearchname); - dns_name_clone(name, search_name); - - dns_name_init(¤t_name, NULL); - - saved_result = ISC_R_SUCCESS; - current = rbt->root; - current_root = rbt->root; - - while (current != NULL) { - NODENAME(current, ¤t_name); - compared = dns_name_fullcompare(search_name, ¤t_name, - &order, &common_labels); - last_compared = current; - - if (compared == dns_namereln_equal) - break; - - if (compared == dns_namereln_none) { + dns_rbtnode_t *current, *last_compared, *current_root; + dns_rbtnodechain_t localchain; + dns_name_t *search_name, current_name, *callback_name; + dns_fixedname_t fixedcallbackname, fixedsearchname; + dns_namereln_t compared; + isc_result_t result, saved_result; + unsigned int common_labels; + unsigned int hlabels = 0; + int order; + + REQUIRE(VALID_RBT(rbt)); + REQUIRE(dns_name_isabsolute(name)); + REQUIRE(node != NULL && *node == NULL); + REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)) + != (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)); + + /* + * If there is a chain it needs to appear to be in a sane state, + * otherwise a chain is still needed to generate foundname and + * callback_name. + */ + if (chain == NULL) { + options |= DNS_RBTFIND_NOPREDECESSOR; + chain = &localchain; + dns_rbtnodechain_init(chain, rbt->mctx); + } else + dns_rbtnodechain_reset(chain); + + if (rbt->root == NULL) + return (ISC_R_NOTFOUND); + else { + /* + * Appease GCC about variables it incorrectly thinks are + * possibly used uninitialized. + */ + compared = dns_namereln_none; + last_compared = NULL; + } + + dns_fixedname_init(&fixedcallbackname); + callback_name = dns_fixedname_name(&fixedcallbackname); + + /* + * search_name is the name segment being sought in each tree level. + * By using a fixedname, the search_name will definitely have offsets + * for use by any splitting. + * By using dns_name_clone, no name data should be copied thanks to + * the lack of bitstring labels. + */ + dns_fixedname_init(&fixedsearchname); + search_name = dns_fixedname_name(&fixedsearchname); + dns_name_clone(name, search_name); + + dns_name_init(¤t_name, NULL); + + saved_result = ISC_R_SUCCESS; + current = rbt->root; + current_root = rbt->root; + + while (current != NULL) { + NODENAME(current, ¤t_name); + compared = dns_name_fullcompare(search_name, ¤t_name, + &order, &common_labels); + last_compared = current; + + if (compared == dns_namereln_equal) + break; + + if (compared == dns_namereln_none) { #ifdef DNS_RBT_USEHASH - dns_name_t hash_name; - dns_rbtnode_t *hnode; - dns_rbtnode_t *up_current; - unsigned int nlabels; - unsigned int tlabels = 1; - unsigned int hash; - - /* - * If there is no hash table, hashing can't be done. - */ - if (rbt->hashtable == NULL) - goto nohash; - - /* - * The case of current != current_root, that - * means a left or right pointer was followed, - * only happens when the algorithm fell through to - * the traditional binary search because of a - * bitstring label. Since we dropped the bitstring - * support, this should not happen. - */ - INSIST(current == current_root); - - nlabels = dns_name_countlabels(search_name); - - /* - * current_root is the root of the current level, so - * it's parent is the same as it's "up" pointer. - */ - up_current = PARENT(current_root); - dns_name_init(&hash_name, NULL); - - hashagain: - /* - * Hash includes tail. - */ - dns_name_getlabelsequence(name, - nlabels - tlabels, - hlabels + tlabels, - &hash_name); - hash = dns_name_fullhash(&hash_name, ISC_FALSE); - dns_name_getlabelsequence(search_name, - nlabels - tlabels, - tlabels, &hash_name); - - for (hnode = rbt->hashtable[hash % rbt->hashsize]; - hnode != NULL; - hnode = hnode->hashnext) - { - dns_name_t hnode_name; - - if (hash != HASHVAL(hnode)) - continue; - if (find_up(hnode) != up_current) - continue; - dns_name_init(&hnode_name, NULL); - NODENAME(hnode, &hnode_name); - if (dns_name_equal(&hnode_name, &hash_name)) - break; - } - - if (hnode != NULL) { - current = hnode; - /* - * This is an optimization. If hashing found - * the right node, the next call to - * dns_name_fullcompare() would obviously - * return _equal or _subdomain. Determine - * which of those would be the case by - * checking if the full name was hashed. Then - * make it look like dns_name_fullcompare - * was called and jump to the right place. - */ - if (tlabels == nlabels) { - compared = dns_namereln_equal; - break; - } else { - common_labels = tlabels; - compared = dns_namereln_subdomain; - goto subdomain; - } - } - - if (tlabels++ < nlabels) - goto hashagain; - - /* - * All of the labels have been tried against the hash - * table. Since we dropped the support of bitstring - * labels, the name isn't in the table. - */ - current = NULL; - continue; - - nohash: + dns_name_t hash_name; + dns_rbtnode_t *hnode; + dns_rbtnode_t *up_current; + unsigned int nlabels; + unsigned int tlabels = 1; + unsigned int hash; + + /* + * If there is no hash table, hashing can't be done. + */ + if (rbt->hashtable == NULL) + goto nohash; + + /* + * The case of current != current_root, that + * means a left or right pointer was followed, + * only happens when the algorithm fell through to + * the traditional binary search because of a + * bitstring label. Since we dropped the bitstring + * support, this should not happen. + */ + INSIST(current == current_root); + + nlabels = dns_name_countlabels(search_name); + + /* + * current_root is the root of the current level, so + * it's parent is the same as it's "up" pointer. + */ + up_current = PARENT(current_root); + dns_name_init(&hash_name, NULL); + + hashagain: + /* + * Hash includes tail. + */ + dns_name_getlabelsequence(name, + nlabels - tlabels, + hlabels + tlabels, + &hash_name); + hash = dns_name_fullhash(&hash_name, ISC_FALSE); + dns_name_getlabelsequence(search_name, + nlabels - tlabels, + tlabels, &hash_name); + + for (hnode = rbt->hashtable[hash % rbt->hashsize]; + hnode != NULL; + hnode = hnode->hashnext) + { + dns_name_t hnode_name; + + if (hash != HASHVAL(hnode)) + continue; + if (find_up(hnode) != up_current) + continue; + dns_name_init(&hnode_name, NULL); + NODENAME(hnode, &hnode_name); + if (dns_name_equal(&hnode_name, &hash_name)) + break; + } + + if (hnode != NULL) { + current = hnode; + /* + * This is an optimization. If hashing found + * the right node, the next call to + * dns_name_fullcompare() would obviously + * return _equal or _subdomain. Determine + * which of those would be the case by + * checking if the full name was hashed. Then + * make it look like dns_name_fullcompare + * was called and jump to the right place. + */ + if (tlabels == nlabels) { + compared = dns_namereln_equal; + break; + } else { + common_labels = tlabels; + compared = dns_namereln_subdomain; + goto subdomain; + } + } + + if (tlabels++ < nlabels) + goto hashagain; + + /* + * All of the labels have been tried against the hash + * table. Since we dropped the support of bitstring + * labels, the name isn't in the table. + */ + current = NULL; + continue; + + nohash: #endif /* DNS_RBT_USEHASH */ - /* - * Standard binary search tree movement. - */ - if (order < 0) - current = LEFT(current); - else - current = RIGHT(current); - - } else { - /* - * The names have some common suffix labels. - * - * If the number in common are equal in length to - * the current node's name length, then follow the - * down pointer and search in the new tree. - */ - if (compared == dns_namereln_subdomain) { - subdomain: - /* - * Whack off the current node's common parts - * for the name to search in the next level. - */ - dns_name_split(search_name, common_labels, - search_name, NULL); - hlabels += common_labels; - /* - * This might be the closest enclosing name. - */ - if (DATA(current) != NULL || - (options & DNS_RBTFIND_EMPTYDATA) != 0) - *node = current; - - /* - * Point the chain to the next level. This - * needs to be done before 'current' is pointed - * there because the callback in the next - * block of code needs the current 'current', - * but in the event the callback requests that - * the search be stopped then the - * DNS_R_PARTIALMATCH code at the end of this - * function needs the chain pointed to the - * next level. - */ - ADD_LEVEL(chain, current); - - /* - * The caller may want to interrupt the - * downward search when certain special nodes - * are traversed. If this is a special node, - * the callback is used to learn what the - * caller wants to do. - */ - if (callback != NULL && - FINDCALLBACK(current)) { - result = chain_name(chain, - callback_name, - ISC_FALSE); - if (result != ISC_R_SUCCESS) { - dns_rbtnodechain_reset(chain); - return (result); - } - - result = (callback)(current, - callback_name, - callback_arg); - if (result != DNS_R_CONTINUE) { - saved_result = result; - /* - * Treat this node as if it - * had no down pointer. - */ - current = NULL; - break; - } - } - - /* - * Finally, head to the next tree level. - */ - current = DOWN(current); - current_root = current; - - } else { - /* - * Though there are labels in common, the - * entire name at this node is not common - * with the search name so the search - * name does not exist in the tree. - */ - INSIST(compared == dns_namereln_commonancestor - || compared == dns_namereln_contains); - - current = NULL; - } - } - } - - /* - * If current is not NULL, NOEXACT is not disallowing exact matches, - * and either the node has data or an empty node is ok, return - * ISC_R_SUCCESS to indicate an exact match. - */ - if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 && - (DATA(current) != NULL || - (options & DNS_RBTFIND_EMPTYDATA) != 0)) { - /* - * Found an exact match. - */ - chain->end = current; - chain->level_matches = chain->level_count; - - if (foundname != NULL) - result = chain_name(chain, foundname, ISC_TRUE); - else - result = ISC_R_SUCCESS; - - if (result == ISC_R_SUCCESS) { - *node = current; - result = saved_result; - } else - *node = NULL; - } else { - /* - * Did not find an exact match (or did not want one). - */ - if (*node != NULL) { - /* - * ... but found a partially matching superdomain. - * Unwind the chain to the partial match node - * to set level_matches to the level above the node, - * and then to derive the name. - * - * chain->level_count is guaranteed to be at least 1 - * here because by definition of finding a superdomain, - * the chain is pointed to at least the first subtree. - */ - chain->level_matches = chain->level_count - 1; - - while (chain->levels[chain->level_matches] != *node) { - INSIST(chain->level_matches > 0); - chain->level_matches--; - } - - if (foundname != NULL) { - unsigned int saved_count = chain->level_count; - - chain->level_count = chain->level_matches + 1; - - result = chain_name(chain, foundname, - ISC_FALSE); - - chain->level_count = saved_count; - } else - result = ISC_R_SUCCESS; - - if (result == ISC_R_SUCCESS) - result = DNS_R_PARTIALMATCH; - - } else - result = ISC_R_NOTFOUND; - - if (current != NULL) { - /* - * There was an exact match but either - * DNS_RBTFIND_NOEXACT was set, or - * DNS_RBTFIND_EMPTYDATA was set and the node had no - * data. A policy decision was made to set the - * chain to the exact match, but this is subject - * to change if it becomes apparent that something - * else would be more useful. It is important that - * this case is handled here, because the predecessor - * setting code below assumes the match was not exact. - */ - INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) || - ((options & DNS_RBTFIND_EMPTYDATA) == 0 && - DATA(current) == NULL)); - chain->end = current; - - } else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) { - /* - * Ensure the chain points nowhere. - */ - chain->end = NULL; - - } else { - /* - * Since there was no exact match, the chain argument - * needs to be pointed at the DNSSEC predecessor of - * the search name. - */ - if (compared == dns_namereln_subdomain) { - /* - * Attempted to follow a down pointer that was - * NULL, which means the searched for name was - * a subdomain of a terminal name in the tree. - * Since there are no existing subdomains to - * order against, the terminal name is the - * predecessor. - */ - INSIST(chain->level_count > 0); - INSIST(chain->level_matches < - chain->level_count); - chain->end = - chain->levels[--chain->level_count]; - - } else { - isc_result_t result2; - - /* - * Point current to the node that stopped - * the search. - * - * With the hashing modification that has been - * added to the algorithm, the stop node of a - * standard binary search is not known. So it - * has to be found. There is probably a more - * clever way of doing this. - * - * The assignment of current to NULL when - * the relationship is *not* dns_namereln_none, - * even though it later gets set to the same - * last_compared anyway, is simply to not push - * the while loop in one more level of - * indentation. - */ - if (compared == dns_namereln_none) - current = last_compared; - else - current = NULL; - - while (current != NULL) { - NODENAME(current, ¤t_name); - compared = dns_name_fullcompare( - search_name, - ¤t_name, - &order, - &common_labels); - - last_compared = current; - - /* - * Standard binary search movement. - */ - if (order < 0) - current = LEFT(current); - else - current = RIGHT(current); - - } - - current = last_compared; - - /* - * Reached a point within a level tree that - * positively indicates the name is not - * present, but the stop node could be either - * less than the desired name (order > 0) or - * greater than the desired name (order < 0). - * - * If the stop node is less, it is not - * necessarily the predecessor. If the stop - * node has a down pointer, then the real - * predecessor is at the end of a level below - * (not necessarily the next level). - * Move down levels until the rightmost node - * does not have a down pointer. - * - * When the stop node is greater, it is - * the successor. All the logic for finding - * the predecessor is handily encapsulated - * in dns_rbtnodechain_prev. In the event - * that the search name is less than anything - * else in the tree, the chain is reset. - * XXX DCL What is the best way for the caller - * to know that the search name has - * no predecessor? - */ - - - if (order > 0) { - if (DOWN(current) != NULL) { - ADD_LEVEL(chain, current); - - result2 = - move_chain_to_last(chain, - DOWN(current)); - - if (result2 != ISC_R_SUCCESS) - result = result2; - } else - /* - * Ah, the pure and simple - * case. The stop node is the - * predecessor. - */ - chain->end = current; - - } else { - INSIST(order < 0); - - chain->end = current; - - result2 = dns_rbtnodechain_prev(chain, - NULL, - NULL); - if (result2 == ISC_R_SUCCESS || - result2 == DNS_R_NEWORIGIN) - ; /* Nothing. */ - else if (result2 == ISC_R_NOMORE) - /* - * There is no predecessor. - */ - dns_rbtnodechain_reset(chain); - else - result = result2; - } - - } - } - } - - ENSURE(*node == NULL || DNS_RBTNODE_VALID(*node)); - - return (result); + /* + * Standard binary search tree movement. + */ + if (order < 0) + current = LEFT(current); + else + current = RIGHT(current); + + } else { + /* + * The names have some common suffix labels. + * + * If the number in common are equal in length to + * the current node's name length, then follow the + * down pointer and search in the new tree. + */ + if (compared == dns_namereln_subdomain) { + subdomain: + /* + * Whack off the current node's common parts + * for the name to search in the next level. + */ + dns_name_split(search_name, common_labels, + search_name, NULL); + hlabels += common_labels; + /* + * This might be the closest enclosing name. + */ + if (DATA(current) != NULL || + (options & DNS_RBTFIND_EMPTYDATA) != 0) + *node = current; + + /* + * Point the chain to the next level. This + * needs to be done before 'current' is pointed + * there because the callback in the next + * block of code needs the current 'current', + * but in the event the callback requests that + * the search be stopped then the + * DNS_R_PARTIALMATCH code at the end of this + * function needs the chain pointed to the + * next level. + */ + ADD_LEVEL(chain, current); + + /* + * The caller may want to interrupt the + * downward search when certain special nodes + * are traversed. If this is a special node, + * the callback is used to learn what the + * caller wants to do. + */ + if (callback != NULL && + FINDCALLBACK(current)) { + result = chain_name(chain, + callback_name, + ISC_FALSE); + if (result != ISC_R_SUCCESS) { + dns_rbtnodechain_reset(chain); + return (result); + } + + result = (callback)(current, + callback_name, + callback_arg); + if (result != DNS_R_CONTINUE) { + saved_result = result; + /* + * Treat this node as if it + * had no down pointer. + */ + current = NULL; + break; + } + } + + /* + * Finally, head to the next tree level. + */ + current = DOWN(current); + current_root = current; + + } else { + /* + * Though there are labels in common, the + * entire name at this node is not common + * with the search name so the search + * name does not exist in the tree. + */ + INSIST(compared == dns_namereln_commonancestor + || compared == dns_namereln_contains); + + current = NULL; + } + } + } + + /* + * If current is not NULL, NOEXACT is not disallowing exact matches, + * and either the node has data or an empty node is ok, return + * ISC_R_SUCCESS to indicate an exact match. + */ + if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 && + (DATA(current) != NULL || + (options & DNS_RBTFIND_EMPTYDATA) != 0)) { + /* + * Found an exact match. + */ + chain->end = current; + chain->level_matches = chain->level_count; + + if (foundname != NULL) + result = chain_name(chain, foundname, ISC_TRUE); + else + result = ISC_R_SUCCESS; + + if (result == ISC_R_SUCCESS) { + *node = current; + result = saved_result; + } else + *node = NULL; + } else { + /* + * Did not find an exact match (or did not want one). + */ + if (*node != NULL) { + /* + * ... but found a partially matching superdomain. + * Unwind the chain to the partial match node + * to set level_matches to the level above the node, + * and then to derive the name. + * + * chain->level_count is guaranteed to be at least 1 + * here because by definition of finding a superdomain, + * the chain is pointed to at least the first subtree. + */ + chain->level_matches = chain->level_count - 1; + + while (chain->levels[chain->level_matches] != *node) { + INSIST(chain->level_matches > 0); + chain->level_matches--; + } + + if (foundname != NULL) { + unsigned int saved_count = chain->level_count; + + chain->level_count = chain->level_matches + 1; + + result = chain_name(chain, foundname, + ISC_FALSE); + + chain->level_count = saved_count; + } else + result = ISC_R_SUCCESS; + + if (result == ISC_R_SUCCESS) + result = DNS_R_PARTIALMATCH; + + } else + result = ISC_R_NOTFOUND; + + if (current != NULL) { + /* + * There was an exact match but either + * DNS_RBTFIND_NOEXACT was set, or + * DNS_RBTFIND_EMPTYDATA was set and the node had no + * data. A policy decision was made to set the + * chain to the exact match, but this is subject + * to change if it becomes apparent that something + * else would be more useful. It is important that + * this case is handled here, because the predecessor + * setting code below assumes the match was not exact. + */ + INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) || + ((options & DNS_RBTFIND_EMPTYDATA) == 0 && + DATA(current) == NULL)); + chain->end = current; + + } else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) { + /* + * Ensure the chain points nowhere. + */ + chain->end = NULL; + + } else { + /* + * Since there was no exact match, the chain argument + * needs to be pointed at the DNSSEC predecessor of + * the search name. + */ + if (compared == dns_namereln_subdomain) { + /* + * Attempted to follow a down pointer that was + * NULL, which means the searched for name was + * a subdomain of a terminal name in the tree. + * Since there are no existing subdomains to + * order against, the terminal name is the + * predecessor. + */ + INSIST(chain->level_count > 0); + INSIST(chain->level_matches < + chain->level_count); + chain->end = + chain->levels[--chain->level_count]; + + } else { + isc_result_t result2; + + /* + * Point current to the node that stopped + * the search. + * + * With the hashing modification that has been + * added to the algorithm, the stop node of a + * standard binary search is not known. So it + * has to be found. There is probably a more + * clever way of doing this. + * + * The assignment of current to NULL when + * the relationship is *not* dns_namereln_none, + * even though it later gets set to the same + * last_compared anyway, is simply to not push + * the while loop in one more level of + * indentation. + */ + if (compared == dns_namereln_none) + current = last_compared; + else + current = NULL; + + while (current != NULL) { + NODENAME(current, ¤t_name); + compared = dns_name_fullcompare( + search_name, + ¤t_name, + &order, + &common_labels); + + last_compared = current; + + /* + * Standard binary search movement. + */ + if (order < 0) + current = LEFT(current); + else + current = RIGHT(current); + + } + + current = last_compared; + + /* + * Reached a point within a level tree that + * positively indicates the name is not + * present, but the stop node could be either + * less than the desired name (order > 0) or + * greater than the desired name (order < 0). + * + * If the stop node is less, it is not + * necessarily the predecessor. If the stop + * node has a down pointer, then the real + * predecessor is at the end of a level below + * (not necessarily the next level). + * Move down levels until the rightmost node + * does not have a down pointer. + * + * When the stop node is greater, it is + * the successor. All the logic for finding + * the predecessor is handily encapsulated + * in dns_rbtnodechain_prev. In the event + * that the search name is less than anything + * else in the tree, the chain is reset. + * XXX DCL What is the best way for the caller + * to know that the search name has + * no predecessor? + */ + + + if (order > 0) { + if (DOWN(current) != NULL) { + ADD_LEVEL(chain, current); + + result2 = + move_chain_to_last(chain, + DOWN(current)); + + if (result2 != ISC_R_SUCCESS) + result = result2; + } else + /* + * Ah, the pure and simple + * case. The stop node is the + * predecessor. + */ + chain->end = current; + + } else { + INSIST(order < 0); + + chain->end = current; + + result2 = dns_rbtnodechain_prev(chain, + NULL, + NULL); + if (result2 == ISC_R_SUCCESS || + result2 == DNS_R_NEWORIGIN) + ; /* Nothing. */ + else if (result2 == ISC_R_NOMORE) + /* + * There is no predecessor. + */ + dns_rbtnodechain_reset(chain); + else + result = result2; + } + + } + } + } + + ENSURE(*node == NULL || DNS_RBTNODE_VALID(*node)); + + return (result); } /* @@ -1166,22 +1169,22 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname, */ isc_result_t dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options, - dns_name_t *foundname, void **data) { - dns_rbtnode_t *node = NULL; - isc_result_t result; + dns_name_t *foundname, void **data) { + dns_rbtnode_t *node = NULL; + isc_result_t result; - REQUIRE(data != NULL && *data == NULL); + REQUIRE(data != NULL && *data == NULL); - result = dns_rbt_findnode(rbt, name, foundname, &node, NULL, - options, NULL, NULL); + result = dns_rbt_findnode(rbt, name, foundname, &node, NULL, + options, NULL, NULL); - if (node != NULL && - (DATA(node) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0)) - *data = DATA(node); - else - result = ISC_R_NOTFOUND; + if (node != NULL && + (DATA(node) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0)) + *data = DATA(node); + else + result = ISC_R_NOTFOUND; - return (result); + return (result); } /* @@ -1189,39 +1192,39 @@ dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options, */ isc_result_t dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) { - dns_rbtnode_t *node = NULL; - isc_result_t result; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(dns_name_isabsolute(name)); - - /* - * First, find the node. - * - * When searching, the name might not have an exact match: - * consider a.b.a.com, b.b.a.com and c.b.a.com as the only - * elements of a tree, which would make layer 1 a single - * node tree of "b.a.com" and layer 2 a three node tree of - * a, b, and c. Deleting a.com would find only a partial depth - * match in the first layer. Should it be a requirement that - * that the name to be deleted have data? For now, it is. - * - * ->dirty, ->locknum and ->references are ignored; they are - * solely the province of rbtdb.c. - */ - result = dns_rbt_findnode(rbt, name, NULL, &node, NULL, - DNS_RBTFIND_NOOPTIONS, NULL, NULL); - - if (result == ISC_R_SUCCESS) { - if (DATA(node) != NULL) - result = dns_rbt_deletenode(rbt, node, recurse); - else - result = ISC_R_NOTFOUND; - - } else if (result == DNS_R_PARTIALMATCH) - result = ISC_R_NOTFOUND; - - return (result); + dns_rbtnode_t *node = NULL; + isc_result_t result; + + REQUIRE(VALID_RBT(rbt)); + REQUIRE(dns_name_isabsolute(name)); + + /* + * First, find the node. + * + * When searching, the name might not have an exact match: + * consider a.b.a.com, b.b.a.com and c.b.a.com as the only + * elements of a tree, which would make layer 1 a single + * node tree of "b.a.com" and layer 2 a three node tree of + * a, b, and c. Deleting a.com would find only a partial depth + * match in the first layer. Should it be a requirement that + * that the name to be deleted have data? For now, it is. + * + * ->dirty, ->locknum and ->references are ignored; they are + * solely the province of rbtdb.c. + */ + result = dns_rbt_findnode(rbt, name, NULL, &node, NULL, + DNS_RBTFIND_NOOPTIONS, NULL, NULL); + + if (result == ISC_R_SUCCESS) { + if (DATA(node) != NULL) + result = dns_rbt_deletenode(rbt, node, recurse); + else + result = ISC_R_NOTFOUND; + + } else if (result == DNS_R_PARTIALMATCH) + result = ISC_R_NOTFOUND; + + return (result); } /* @@ -1263,378 +1266,379 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) { isc_result_t dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse) { - dns_rbtnode_t *parent; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(DNS_RBTNODE_VALID(node)); - - if (DOWN(node) != NULL) { - if (recurse) - RUNTIME_CHECK(dns_rbt_deletetree(rbt, DOWN(node)) - == ISC_R_SUCCESS); - else { - if (DATA(node) != NULL && rbt->data_deleter != NULL) - rbt->data_deleter(DATA(node), - rbt->deleter_arg); - DATA(node) = NULL; - - /* - * Since there is at least one node below this one and - * no recursion was requested, the deletion is - * complete. The down node from this node might be all - * by itself on a single level, so join_nodes() could - * be used to collapse the tree (with all the caveats - * of the comment at the start of this function). - */ - return (ISC_R_SUCCESS); - } - } - - /* - * Note the node that points to the level of the node that is being - * deleted. If the deleted node is the top level, parent will be set - * to NULL. - */ - parent = find_up(node); - - /* - * This node now has no down pointer (either because it didn't - * have one to start, or because it was recursively removed). - * So now the node needs to be removed from this level. - */ - dns_rbt_deletefromlevel(node, parent == NULL ? &rbt->root : - &DOWN(parent)); - - if (DATA(node) != NULL && rbt->data_deleter != NULL) - rbt->data_deleter(DATA(node), rbt->deleter_arg); - - unhash_node(rbt, node); + dns_rbtnode_t *parent; + + REQUIRE(VALID_RBT(rbt)); + REQUIRE(DNS_RBTNODE_VALID(node)); + + if (DOWN(node) != NULL) { + if (recurse) + RUNTIME_CHECK(dns_rbt_deletetree(rbt, DOWN(node)) + == ISC_R_SUCCESS); + else { + if (DATA(node) != NULL && rbt->data_deleter != NULL) + rbt->data_deleter(DATA(node), rbt->deleter_arg); + DATA(node) = NULL; + + /* + * Since there is at least one node below this one and + * no recursion was requested, the deletion is + * complete. The down node from this node might be all + * by itself on a single level, so join_nodes() could + * be used to collapse the tree (with all the caveats + * of the comment at the start of this function). + */ + return (ISC_R_SUCCESS); + } + } + + /* + * Note the node that points to the level of the node that is being + * deleted. If the deleted node is the top level, parent will be set + * to NULL. + */ + parent = find_up(node); + + /* + * This node now has no down pointer (either because it didn't + * have one to start, or because it was recursively removed). + * So now the node needs to be removed from this level. + */ + dns_rbt_deletefromlevel(node, parent == NULL ? &rbt->root : + &DOWN(parent)); + + if (DATA(node) != NULL && rbt->data_deleter != NULL) + rbt->data_deleter(DATA(node), rbt->deleter_arg); + + unhash_node(rbt, node); #if DNS_RBT_USEMAGIC - node->magic = 0; + node->magic = 0; #endif - dns_rbtnode_refdestroy(node); - isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); - rbt->nodecount--; - - /* - * There are now two special cases that can exist that would - * not have existed if the tree had been created using only - * the names that now exist in it. (This is all related to - * join_nodes() as described in this function's introductory comment.) - * Both cases exist when the deleted node's parent (the node - * that pointed to the deleted node's level) is not null but - * it has no data: parent != NULL && DATA(parent) == NULL. - * - * The first case is that the deleted node was the last on its level: - * DOWN(parent) == NULL. This case can only exist if the parent was - * previously deleted -- and so now, apparently, the parent should go - * away. That can't be done though because there might be external - * references to it, such as through a nodechain. - * - * The other case also involves a parent with no data, but with the - * deleted node being the next-to-last node instead of the last: - * LEFT(DOWN(parent)) == NULL && RIGHT(DOWN(parent)) == NULL. - * Presumably now the remaining node on the level should be joined - * with the parent, but it's already been described why that can't be - * done. - */ - - /* - * This function never fails. - */ - return (ISC_R_SUCCESS); + dns_rbtnode_refdestroy(node); + isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); + rbt->nodecount--; + + /* + * There are now two special cases that can exist that would + * not have existed if the tree had been created using only + * the names that now exist in it. (This is all related to + * join_nodes() as described in this function's introductory comment.) + * Both cases exist when the deleted node's parent (the node + * that pointed to the deleted node's level) is not null but + * it has no data: parent != NULL && DATA(parent) == NULL. + * + * The first case is that the deleted node was the last on its level: + * DOWN(parent) == NULL. This case can only exist if the parent was + * previously deleted -- and so now, apparently, the parent should go + * away. That can't be done though because there might be external + * references to it, such as through a nodechain. + * + * The other case also involves a parent with no data, but with the + * deleted node being the next-to-last node instead of the last: + * LEFT(DOWN(parent)) == NULL && RIGHT(DOWN(parent)) == NULL. + * Presumably now the remaining node on the level should be joined + * with the parent, but it's already been described why that can't be + * done. + */ + + /* + * This function never fails. + */ + return (ISC_R_SUCCESS); } void dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) { - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(name != NULL); - REQUIRE(name->offsets == NULL); + REQUIRE(DNS_RBTNODE_VALID(node)); + REQUIRE(name != NULL); + REQUIRE(name->offsets == NULL); - NODENAME(node, name); + NODENAME(node, name); } isc_result_t dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) { - dns_name_t current; - isc_result_t result; + dns_name_t current; + isc_result_t result; - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(name != NULL); - REQUIRE(name->buffer != NULL); + REQUIRE(DNS_RBTNODE_VALID(node)); + REQUIRE(name != NULL); + REQUIRE(name->buffer != NULL); - dns_name_init(¤t, NULL); - dns_name_reset(name); + dns_name_init(¤t, NULL); + dns_name_reset(name); - do { - INSIST(node != NULL); + do { + INSIST(node != NULL); - NODENAME(node, ¤t); + NODENAME(node, ¤t); - result = dns_name_concatenate(name, ¤t, name, NULL); - if (result != ISC_R_SUCCESS) - break; - - node = find_up(node); - } while (! dns_name_isabsolute(name)); + result = dns_name_concatenate(name, ¤t, name, NULL); + if (result != ISC_R_SUCCESS) + break; - return (result); + node = find_up(node); + } while (! dns_name_isabsolute(name)); + + return (result); } char * dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, unsigned int size) { - dns_fixedname_t fixedname; - dns_name_t *name; - isc_result_t result; - - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(printname != NULL); - - dns_fixedname_init(&fixedname); - name = dns_fixedname_name(&fixedname); - result = dns_rbt_fullnamefromnode(node, name); - if (result == ISC_R_SUCCESS) - dns_name_format(name, printname, size); - else - snprintf(printname, size, "<error building name: %s>", - dns_result_totext(result)); - - return (printname); + dns_fixedname_t fixedname; + dns_name_t *name; + isc_result_t result; + + REQUIRE(DNS_RBTNODE_VALID(node)); + REQUIRE(printname != NULL); + + dns_fixedname_init(&fixedname); + name = dns_fixedname_name(&fixedname); + result = dns_rbt_fullnamefromnode(node, name); + if (result == ISC_R_SUCCESS) + dns_name_format(name, printname, size); + else + snprintf(printname, size, "<error building name: %s>", + dns_result_totext(result)); + + return (printname); } static isc_result_t create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) { - dns_rbtnode_t *node; - isc_region_t region; - unsigned int labels; - - REQUIRE(name->offsets != NULL); - - dns_name_toregion(name, ®ion); - labels = dns_name_countlabels(name); - ENSURE(labels > 0); - - /* - * Allocate space for the node structure, the name, and the offsets. - */ - node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) + - region.length + labels); - - if (node == NULL) - return (ISC_R_NOMEMORY); - - node->is_root = 0; - PARENT(node) = NULL; - RIGHT(node) = NULL; - LEFT(node) = NULL; - DOWN(node) = NULL; - DATA(node) = NULL; + dns_rbtnode_t *node; + isc_region_t region; + unsigned int labels; + + REQUIRE(name->offsets != NULL); + + dns_name_toregion(name, ®ion); + labels = dns_name_countlabels(name); + ENSURE(labels > 0); + + /* + * Allocate space for the node structure, the name, and the offsets. + */ + node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) + + region.length + labels); + + if (node == NULL) + return (ISC_R_NOMEMORY); + + node->is_root = 0; + PARENT(node) = NULL; + RIGHT(node) = NULL; + LEFT(node) = NULL; + DOWN(node) = NULL; + DATA(node) = NULL; #ifdef DNS_RBT_USEHASH - HASHNEXT(node) = NULL; - HASHVAL(node) = 0; + HASHNEXT(node) = NULL; + HASHVAL(node) = 0; #endif - LOCKNUM(node) = 0; - WILD(node) = 0; - DIRTY(node) = 0; - dns_rbtnode_refinit(node, 0); - node->find_callback = 0; - - MAKE_BLACK(node); - - /* - * The following is stored to make reconstructing a name from the - * stored value in the node easy: the length of the name, the number - * of labels, whether the name is absolute or not, the name itself, - * and the name's offsets table. - * - * XXX RTH - * The offsets table could be made smaller by eliminating the - * first offset, which is always 0. This requires changes to - * lib/dns/name.c. - */ - NAMELEN(node) = region.length; - PADBYTES(node) = 0; - OFFSETLEN(node) = labels; - ATTRS(node) = name->attributes; - - memcpy(NAME(node), region.base, region.length); - memcpy(OFFSETS(node), name->offsets, labels); + ISC_LINK_INIT(node, deadlink); + + LOCKNUM(node) = 0; + WILD(node) = 0; + DIRTY(node) = 0; + dns_rbtnode_refinit(node, 0); + node->find_callback = 0; + + MAKE_BLACK(node); + + /* + * The following is stored to make reconstructing a name from the + * stored value in the node easy: the length of the name, the number + * of labels, whether the name is absolute or not, the name itself, + * and the name's offsets table. + * + * XXX RTH + * The offsets table could be made smaller by eliminating the + * first offset, which is always 0. This requires changes to + * lib/dns/name.c. + */ + NAMELEN(node) = region.length; + PADBYTES(node) = 0; + OFFSETLEN(node) = labels; + ATTRS(node) = name->attributes; + + memcpy(NAME(node), region.base, region.length); + memcpy(OFFSETS(node), name->offsets, labels); #if DNS_RBT_USEMAGIC - node->magic = DNS_RBTNODE_MAGIC; + node->magic = DNS_RBTNODE_MAGIC; #endif - *nodep = node; + *nodep = node; - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } #ifdef DNS_RBT_USEHASH static inline void hash_add_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name) { - unsigned int hash; + unsigned int hash; - HASHVAL(node) = dns_name_fullhash(name, ISC_FALSE); + HASHVAL(node) = dns_name_fullhash(name, ISC_FALSE); - hash = HASHVAL(node) % rbt->hashsize; - HASHNEXT(node) = rbt->hashtable[hash]; + hash = HASHVAL(node) % rbt->hashsize; + HASHNEXT(node) = rbt->hashtable[hash]; - rbt->hashtable[hash] = node; + rbt->hashtable[hash] = node; } static isc_result_t inithash(dns_rbt_t *rbt) { - unsigned int bytes; + unsigned int bytes; - rbt->hashsize = RBT_HASH_SIZE; - bytes = rbt->hashsize * sizeof(dns_rbtnode_t *); - rbt->hashtable = isc_mem_get(rbt->mctx, bytes); + rbt->hashsize = RBT_HASH_SIZE; + bytes = rbt->hashsize * sizeof(dns_rbtnode_t *); + rbt->hashtable = isc_mem_get(rbt->mctx, bytes); - if (rbt->hashtable == NULL) - return (ISC_R_NOMEMORY); + if (rbt->hashtable == NULL) + return (ISC_R_NOMEMORY); - memset(rbt->hashtable, 0, bytes); + memset(rbt->hashtable, 0, bytes); - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } static void rehash(dns_rbt_t *rbt) { - unsigned int oldsize; - dns_rbtnode_t **oldtable; - dns_rbtnode_t *node; - unsigned int hash; - unsigned int i; - - oldsize = rbt->hashsize; - oldtable = rbt->hashtable; - rbt->hashsize *= 2 + 1; - rbt->hashtable = isc_mem_get(rbt->mctx, - rbt->hashsize * sizeof(dns_rbtnode_t *)); - if (rbt->hashtable == NULL) { - rbt->hashtable = oldtable; - rbt->hashsize = oldsize; - return; - } - - for (i = 0; i < rbt->hashsize; i++) - rbt->hashtable[i] = NULL; - - for (i = 0; i < oldsize; i++) { - node = oldtable[i]; - while (node != NULL) { - hash = HASHVAL(node) % rbt->hashsize; - oldtable[i] = HASHNEXT(node); - HASHNEXT(node) = rbt->hashtable[hash]; - rbt->hashtable[hash] = node; - node = oldtable[i]; - } - } - - isc_mem_put(rbt->mctx, oldtable, oldsize * sizeof(dns_rbtnode_t *)); + unsigned int oldsize; + dns_rbtnode_t **oldtable; + dns_rbtnode_t *node; + unsigned int hash; + unsigned int i; + + oldsize = rbt->hashsize; + oldtable = rbt->hashtable; + rbt->hashsize *= 2 + 1; + rbt->hashtable = isc_mem_get(rbt->mctx, + rbt->hashsize * sizeof(dns_rbtnode_t *)); + if (rbt->hashtable == NULL) { + rbt->hashtable = oldtable; + rbt->hashsize = oldsize; + return; + } + + for (i = 0; i < rbt->hashsize; i++) + rbt->hashtable[i] = NULL; + + for (i = 0; i < oldsize; i++) { + node = oldtable[i]; + while (node != NULL) { + hash = HASHVAL(node) % rbt->hashsize; + oldtable[i] = HASHNEXT(node); + HASHNEXT(node) = rbt->hashtable[hash]; + rbt->hashtable[hash] = node; + node = oldtable[i]; + } + } + + isc_mem_put(rbt->mctx, oldtable, oldsize * sizeof(dns_rbtnode_t *)); } static inline void hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name) { - REQUIRE(DNS_RBTNODE_VALID(node)); + REQUIRE(DNS_RBTNODE_VALID(node)); - if (rbt->nodecount >= (rbt->hashsize *3)) - rehash(rbt); + if (rbt->nodecount >= (rbt->hashsize *3)) + rehash(rbt); - hash_add_node(rbt, node, name); + hash_add_node(rbt, node, name); } static inline void unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) { - unsigned int bucket; - dns_rbtnode_t *bucket_node; - - REQUIRE(DNS_RBTNODE_VALID(node)); - - if (rbt->hashtable != NULL) { - bucket = HASHVAL(node) % rbt->hashsize; - bucket_node = rbt->hashtable[bucket]; - - if (bucket_node == node) - rbt->hashtable[bucket] = HASHNEXT(node); - else { - while (HASHNEXT(bucket_node) != node) { - INSIST(HASHNEXT(bucket_node) != NULL); - bucket_node = HASHNEXT(bucket_node); - } - HASHNEXT(bucket_node) = HASHNEXT(node); - } - } + unsigned int bucket; + dns_rbtnode_t *bucket_node; + + REQUIRE(DNS_RBTNODE_VALID(node)); + + if (rbt->hashtable != NULL) { + bucket = HASHVAL(node) % rbt->hashsize; + bucket_node = rbt->hashtable[bucket]; + + if (bucket_node == node) + rbt->hashtable[bucket] = HASHNEXT(node); + else { + while (HASHNEXT(bucket_node) != node) { + INSIST(HASHNEXT(bucket_node) != NULL); + bucket_node = HASHNEXT(bucket_node); + } + HASHNEXT(bucket_node) = HASHNEXT(node); + } + } } #endif /* DNS_RBT_USEHASH */ static inline void rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp) { - dns_rbtnode_t *child; + dns_rbtnode_t *child; - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(rootp != NULL); + REQUIRE(DNS_RBTNODE_VALID(node)); + REQUIRE(rootp != NULL); - child = RIGHT(node); - INSIST(child != NULL); + child = RIGHT(node); + INSIST(child != NULL); - RIGHT(node) = LEFT(child); - if (LEFT(child) != NULL) - PARENT(LEFT(child)) = node; - LEFT(child) = node; + RIGHT(node) = LEFT(child); + if (LEFT(child) != NULL) + PARENT(LEFT(child)) = node; + LEFT(child) = node; - if (child != NULL) - PARENT(child) = PARENT(node); + if (child != NULL) + PARENT(child) = PARENT(node); - if (IS_ROOT(node)) { - *rootp = child; - child->is_root = 1; - node->is_root = 0; + if (IS_ROOT(node)) { + *rootp = child; + child->is_root = 1; + node->is_root = 0; - } else { - if (LEFT(PARENT(node)) == node) - LEFT(PARENT(node)) = child; - else - RIGHT(PARENT(node)) = child; - } + } else { + if (LEFT(PARENT(node)) == node) + LEFT(PARENT(node)) = child; + else + RIGHT(PARENT(node)) = child; + } - PARENT(node) = child; + PARENT(node) = child; } static inline void rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) { - dns_rbtnode_t *child; + dns_rbtnode_t *child; - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(rootp != NULL); + REQUIRE(DNS_RBTNODE_VALID(node)); + REQUIRE(rootp != NULL); - child = LEFT(node); - INSIST(child != NULL); + child = LEFT(node); + INSIST(child != NULL); - LEFT(node) = RIGHT(child); - if (RIGHT(child) != NULL) - PARENT(RIGHT(child)) = node; - RIGHT(child) = node; + LEFT(node) = RIGHT(child); + if (RIGHT(child) != NULL) + PARENT(RIGHT(child)) = node; + RIGHT(child) = node; - if (child != NULL) - PARENT(child) = PARENT(node); + if (child != NULL) + PARENT(child) = PARENT(node); - if (IS_ROOT(node)) { - *rootp = child; - child->is_root = 1; - node->is_root = 0; + if (IS_ROOT(node)) { + *rootp = child; + child->is_root = 1; + node->is_root = 0; - } else { - if (LEFT(PARENT(node)) == node) - LEFT(PARENT(node)) = child; - else - RIGHT(PARENT(node)) = child; - } + } else { + if (LEFT(PARENT(node)) == node) + LEFT(PARENT(node)) = child; + else + RIGHT(PARENT(node)) = child; + } - PARENT(node) = child; + PARENT(node) = child; } /* @@ -1642,105 +1646,105 @@ rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) { * true red/black tree on a single level. */ static void -dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, - dns_rbtnode_t **rootp) +dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, + dns_rbtnode_t **rootp) { - dns_rbtnode_t *child, *root, *parent, *grandparent; - dns_name_t add_name, current_name; - dns_offsets_t add_offsets, current_offsets; - - REQUIRE(rootp != NULL); - REQUIRE(DNS_RBTNODE_VALID(node) && LEFT(node) == NULL && - RIGHT(node) == NULL); - REQUIRE(current != NULL); - - root = *rootp; - if (root == NULL) { - /* - * First node of a level. - */ - MAKE_BLACK(node); - node->is_root = 1; - PARENT(node) = current; - *rootp = node; - return; - } - - child = root; - - dns_name_init(&add_name, add_offsets); - NODENAME(node, &add_name); - - dns_name_init(¤t_name, current_offsets); - NODENAME(current, ¤t_name); - - if (order < 0) { - INSIST(LEFT(current) == NULL); - LEFT(current) = node; - } else { - INSIST(RIGHT(current) == NULL); - RIGHT(current) = node; - } - - INSIST(PARENT(node) == NULL); - PARENT(node) = current; - - MAKE_RED(node); - - while (node != root && IS_RED(PARENT(node))) { - /* - * XXXDCL could do away with separate parent and grandparent - * variables. They are vestiges of the days before parent - * pointers. However, they make the code a little clearer. - */ - - parent = PARENT(node); - grandparent = PARENT(parent); - - if (parent == LEFT(grandparent)) { - child = RIGHT(grandparent); - if (child != NULL && IS_RED(child)) { - MAKE_BLACK(parent); - MAKE_BLACK(child); - MAKE_RED(grandparent); - node = grandparent; - } else { - if (node == RIGHT(parent)) { - rotate_left(parent, &root); - node = parent; - parent = PARENT(node); - grandparent = PARENT(parent); - } - MAKE_BLACK(parent); - MAKE_RED(grandparent); - rotate_right(grandparent, &root); - } - } else { - child = LEFT(grandparent); - if (child != NULL && IS_RED(child)) { - MAKE_BLACK(parent); - MAKE_BLACK(child); - MAKE_RED(grandparent); - node = grandparent; - } else { - if (node == LEFT(parent)) { - rotate_right(parent, &root); - node = parent; - parent = PARENT(node); - grandparent = PARENT(parent); - } - MAKE_BLACK(parent); - MAKE_RED(grandparent); - rotate_left(grandparent, &root); - } - } - } - - MAKE_BLACK(root); - ENSURE(IS_ROOT(root)); - *rootp = root; - - return; + dns_rbtnode_t *child, *root, *parent, *grandparent; + dns_name_t add_name, current_name; + dns_offsets_t add_offsets, current_offsets; + + REQUIRE(rootp != NULL); + REQUIRE(DNS_RBTNODE_VALID(node) && LEFT(node) == NULL && + RIGHT(node) == NULL); + REQUIRE(current != NULL); + + root = *rootp; + if (root == NULL) { + /* + * First node of a level. + */ + MAKE_BLACK(node); + node->is_root = 1; + PARENT(node) = current; + *rootp = node; + return; + } + + child = root; + + dns_name_init(&add_name, add_offsets); + NODENAME(node, &add_name); + + dns_name_init(¤t_name, current_offsets); + NODENAME(current, ¤t_name); + + if (order < 0) { + INSIST(LEFT(current) == NULL); + LEFT(current) = node; + } else { + INSIST(RIGHT(current) == NULL); + RIGHT(current) = node; + } + + INSIST(PARENT(node) == NULL); + PARENT(node) = current; + + MAKE_RED(node); + + while (node != root && IS_RED(PARENT(node))) { + /* + * XXXDCL could do away with separate parent and grandparent + * variables. They are vestiges of the days before parent + * pointers. However, they make the code a little clearer. + */ + + parent = PARENT(node); + grandparent = PARENT(parent); + + if (parent == LEFT(grandparent)) { + child = RIGHT(grandparent); + if (child != NULL && IS_RED(child)) { + MAKE_BLACK(parent); + MAKE_BLACK(child); + MAKE_RED(grandparent); + node = grandparent; + } else { + if (node == RIGHT(parent)) { + rotate_left(parent, &root); + node = parent; + parent = PARENT(node); + grandparent = PARENT(parent); + } + MAKE_BLACK(parent); + MAKE_RED(grandparent); + rotate_right(grandparent, &root); + } + } else { + child = LEFT(grandparent); + if (child != NULL && IS_RED(child)) { + MAKE_BLACK(parent); + MAKE_BLACK(child); + MAKE_RED(grandparent); + node = grandparent; + } else { + if (node == LEFT(parent)) { + rotate_right(parent, &root); + node = parent; + parent = PARENT(node); + grandparent = PARENT(parent); + } + MAKE_BLACK(parent); + MAKE_RED(grandparent); + rotate_left(grandparent, &root); + } + } + } + + MAKE_BLACK(root); + ENSURE(IS_ROOT(root)); + *rootp = root; + + return; } /* @@ -1749,230 +1753,230 @@ dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, */ static void dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) { - dns_rbtnode_t *child, *sibling, *parent; - dns_rbtnode_t *successor; - - REQUIRE(delete != NULL); - - /* - * Verify that the parent history is (apparently) correct. - */ - INSIST((IS_ROOT(delete) && *rootp == delete) || - (! IS_ROOT(delete) && - (LEFT(PARENT(delete)) == delete || - RIGHT(PARENT(delete)) == delete))); - - child = NULL; - - if (LEFT(delete) == NULL) { - if (RIGHT(delete) == NULL) { - if (IS_ROOT(delete)) { - /* - * This is the only item in the tree. - */ - *rootp = NULL; - return; - } - } else - /* - * This node has one child, on the right. - */ - child = RIGHT(delete); - - } else if (RIGHT(delete) == NULL) - /* - * This node has one child, on the left. - */ - child = LEFT(delete); - else { - dns_rbtnode_t holder, *tmp = &holder; - - /* - * This node has two children, so it cannot be directly - * deleted. Find its immediate in-order successor and - * move it to this location, then do the deletion at the - * old site of the successor. - */ - successor = RIGHT(delete); - while (LEFT(successor) != NULL) - successor = LEFT(successor); - - /* - * The successor cannot possibly have a left child; - * if there is any child, it is on the right. - */ - if (RIGHT(successor) != NULL) - child = RIGHT(successor); - - /* - * Swap the two nodes; it would be simpler to just replace - * the value being deleted with that of the successor, - * but this rigamarole is done so the caller has complete - * control over the pointers (and memory allocation) of - * all of nodes. If just the key value were removed from - * the tree, the pointer to the node would be unchanged. - */ - - /* - * First, put the successor in the tree location of the - * node to be deleted. Save its existing tree pointer - * information, which will be needed when linking up - * delete to the successor's old location. - */ - memcpy(tmp, successor, sizeof(dns_rbtnode_t)); - - if (IS_ROOT(delete)) { - *rootp = successor; - successor->is_root = ISC_TRUE; - delete->is_root = ISC_FALSE; - - } else - if (LEFT(PARENT(delete)) == delete) - LEFT(PARENT(delete)) = successor; - else - RIGHT(PARENT(delete)) = successor; - - PARENT(successor) = PARENT(delete); - LEFT(successor) = LEFT(delete); - RIGHT(successor) = RIGHT(delete); - COLOR(successor) = COLOR(delete); - - if (LEFT(successor) != NULL) - PARENT(LEFT(successor)) = successor; - if (RIGHT(successor) != successor) - PARENT(RIGHT(successor)) = successor; - - /* - * Now relink the node to be deleted into the - * successor's previous tree location. PARENT(tmp) - * is the successor's original parent. - */ - INSIST(! IS_ROOT(delete)); - - if (PARENT(tmp) == delete) { - /* - * Node being deleted was successor's parent. - */ - RIGHT(successor) = delete; - PARENT(delete) = successor; - - } else { - LEFT(PARENT(tmp)) = delete; - PARENT(delete) = PARENT(tmp); - } - - /* - * Original location of successor node has no left. - */ - LEFT(delete) = NULL; - RIGHT(delete) = RIGHT(tmp); - COLOR(delete) = COLOR(tmp); - } - - /* - * Remove the node by removing the links from its parent. - */ - if (! IS_ROOT(delete)) { - if (LEFT(PARENT(delete)) == delete) - LEFT(PARENT(delete)) = child; - else - RIGHT(PARENT(delete)) = child; - - if (child != NULL) - PARENT(child) = PARENT(delete); - - } else { - /* - * This is the root being deleted, and at this point - * it is known to have just one child. - */ - *rootp = child; - child->is_root = 1; - PARENT(child) = PARENT(delete); - } - - /* - * Fix color violations. - */ - if (IS_BLACK(delete)) { - parent = PARENT(delete); - - while (child != *rootp && IS_BLACK(child)) { - INSIST(child == NULL || ! IS_ROOT(child)); - - if (LEFT(parent) == child) { - sibling = RIGHT(parent); - - if (IS_RED(sibling)) { - MAKE_BLACK(sibling); - MAKE_RED(parent); - rotate_left(parent, rootp); - sibling = RIGHT(parent); - } - - if (IS_BLACK(LEFT(sibling)) && - IS_BLACK(RIGHT(sibling))) { - MAKE_RED(sibling); - child = parent; - - } else { - - if (IS_BLACK(RIGHT(sibling))) { - MAKE_BLACK(LEFT(sibling)); - MAKE_RED(sibling); - rotate_right(sibling, rootp); - sibling = RIGHT(parent); - } - - COLOR(sibling) = COLOR(parent); - MAKE_BLACK(parent); - MAKE_BLACK(RIGHT(sibling)); - rotate_left(parent, rootp); - child = *rootp; - } - - } else { - /* - * Child is parent's right child. - * Everything is doen the same as above, - * except mirrored. - */ - sibling = LEFT(parent); - - if (IS_RED(sibling)) { - MAKE_BLACK(sibling); - MAKE_RED(parent); - rotate_right(parent, rootp); - sibling = LEFT(parent); - } - - if (IS_BLACK(LEFT(sibling)) && - IS_BLACK(RIGHT(sibling))) { - MAKE_RED(sibling); - child = parent; - - } else { - if (IS_BLACK(LEFT(sibling))) { - MAKE_BLACK(RIGHT(sibling)); - MAKE_RED(sibling); - rotate_left(sibling, rootp); - sibling = LEFT(parent); - } - - COLOR(sibling) = COLOR(parent); - MAKE_BLACK(parent); - MAKE_BLACK(LEFT(sibling)); - rotate_right(parent, rootp); - child = *rootp; - } - } - - parent = PARENT(child); - } - - if (IS_RED(child)) - MAKE_BLACK(child); - } + dns_rbtnode_t *child, *sibling, *parent; + dns_rbtnode_t *successor; + + REQUIRE(delete != NULL); + + /* + * Verify that the parent history is (apparently) correct. + */ + INSIST((IS_ROOT(delete) && *rootp == delete) || + (! IS_ROOT(delete) && + (LEFT(PARENT(delete)) == delete || + RIGHT(PARENT(delete)) == delete))); + + child = NULL; + + if (LEFT(delete) == NULL) { + if (RIGHT(delete) == NULL) { + if (IS_ROOT(delete)) { + /* + * This is the only item in the tree. + */ + *rootp = NULL; + return; + } + } else + /* + * This node has one child, on the right. + */ + child = RIGHT(delete); + + } else if (RIGHT(delete) == NULL) + /* + * This node has one child, on the left. + */ + child = LEFT(delete); + else { + dns_rbtnode_t holder, *tmp = &holder; + + /* + * This node has two children, so it cannot be directly + * deleted. Find its immediate in-order successor and + * move it to this location, then do the deletion at the + * old site of the successor. + */ + successor = RIGHT(delete); + while (LEFT(successor) != NULL) + successor = LEFT(successor); + + /* + * The successor cannot possibly have a left child; + * if there is any child, it is on the right. + */ + if (RIGHT(successor) != NULL) + child = RIGHT(successor); + + /* + * Swap the two nodes; it would be simpler to just replace + * the value being deleted with that of the successor, + * but this rigamarole is done so the caller has complete + * control over the pointers (and memory allocation) of + * all of nodes. If just the key value were removed from + * the tree, the pointer to the node would be unchanged. + */ + + /* + * First, put the successor in the tree location of the + * node to be deleted. Save its existing tree pointer + * information, which will be needed when linking up + * delete to the successor's old location. + */ + memcpy(tmp, successor, sizeof(dns_rbtnode_t)); + + if (IS_ROOT(delete)) { + *rootp = successor; + successor->is_root = ISC_TRUE; + delete->is_root = ISC_FALSE; + + } else + if (LEFT(PARENT(delete)) == delete) + LEFT(PARENT(delete)) = successor; + else + RIGHT(PARENT(delete)) = successor; + + PARENT(successor) = PARENT(delete); + LEFT(successor) = LEFT(delete); + RIGHT(successor) = RIGHT(delete); + COLOR(successor) = COLOR(delete); + + if (LEFT(successor) != NULL) + PARENT(LEFT(successor)) = successor; + if (RIGHT(successor) != successor) + PARENT(RIGHT(successor)) = successor; + + /* + * Now relink the node to be deleted into the + * successor's previous tree location. PARENT(tmp) + * is the successor's original parent. + */ + INSIST(! IS_ROOT(delete)); + + if (PARENT(tmp) == delete) { + /* + * Node being deleted was successor's parent. + */ + RIGHT(successor) = delete; + PARENT(delete) = successor; + + } else { + LEFT(PARENT(tmp)) = delete; + PARENT(delete) = PARENT(tmp); + } + + /* + * Original location of successor node has no left. + */ + LEFT(delete) = NULL; + RIGHT(delete) = RIGHT(tmp); + COLOR(delete) = COLOR(tmp); + } + + /* + * Remove the node by removing the links from its parent. + */ + if (! IS_ROOT(delete)) { + if (LEFT(PARENT(delete)) == delete) + LEFT(PARENT(delete)) = child; + else + RIGHT(PARENT(delete)) = child; + + if (child != NULL) + PARENT(child) = PARENT(delete); + + } else { + /* + * This is the root being deleted, and at this point + * it is known to have just one child. + */ + *rootp = child; + child->is_root = 1; + PARENT(child) = PARENT(delete); + } + + /* + * Fix color violations. + */ + if (IS_BLACK(delete)) { + parent = PARENT(delete); + + while (child != *rootp && IS_BLACK(child)) { + INSIST(child == NULL || ! IS_ROOT(child)); + + if (LEFT(parent) == child) { + sibling = RIGHT(parent); + + if (IS_RED(sibling)) { + MAKE_BLACK(sibling); + MAKE_RED(parent); + rotate_left(parent, rootp); + sibling = RIGHT(parent); + } + + if (IS_BLACK(LEFT(sibling)) && + IS_BLACK(RIGHT(sibling))) { + MAKE_RED(sibling); + child = parent; + + } else { + + if (IS_BLACK(RIGHT(sibling))) { + MAKE_BLACK(LEFT(sibling)); + MAKE_RED(sibling); + rotate_right(sibling, rootp); + sibling = RIGHT(parent); + } + + COLOR(sibling) = COLOR(parent); + MAKE_BLACK(parent); + MAKE_BLACK(RIGHT(sibling)); + rotate_left(parent, rootp); + child = *rootp; + } + + } else { + /* + * Child is parent's right child. + * Everything is doen the same as above, + * except mirrored. + */ + sibling = LEFT(parent); + + if (IS_RED(sibling)) { + MAKE_BLACK(sibling); + MAKE_RED(parent); + rotate_right(parent, rootp); + sibling = LEFT(parent); + } + + if (IS_BLACK(LEFT(sibling)) && + IS_BLACK(RIGHT(sibling))) { + MAKE_RED(sibling); + child = parent; + + } else { + if (IS_BLACK(LEFT(sibling))) { + MAKE_BLACK(RIGHT(sibling)); + MAKE_RED(sibling); + rotate_left(sibling, rootp); + sibling = LEFT(parent); + } + + COLOR(sibling) = COLOR(parent); + MAKE_BLACK(parent); + MAKE_BLACK(LEFT(sibling)); + rotate_right(parent, rootp); + child = *rootp; + } + } + + parent = PARENT(child); + } + + if (IS_RED(child)) + MAKE_BLACK(child); + } } /* @@ -1992,187 +1996,189 @@ dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) { */ static isc_result_t dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) { - isc_result_t result = ISC_R_SUCCESS; - REQUIRE(VALID_RBT(rbt)); - - if (node == NULL) - return (result); - - if (LEFT(node) != NULL) { - result = dns_rbt_deletetree(rbt, LEFT(node)); - if (result != ISC_R_SUCCESS) - goto done; - LEFT(node) = NULL; - } - if (RIGHT(node) != NULL) { - result = dns_rbt_deletetree(rbt, RIGHT(node)); - if (result != ISC_R_SUCCESS) - goto done; - RIGHT(node) = NULL; - } - if (DOWN(node) != NULL) { - result = dns_rbt_deletetree(rbt, DOWN(node)); - if (result != ISC_R_SUCCESS) - goto done; - DOWN(node) = NULL; - } + isc_result_t result = ISC_R_SUCCESS; + REQUIRE(VALID_RBT(rbt)); + + if (node == NULL) + return (result); + + if (LEFT(node) != NULL) { + result = dns_rbt_deletetree(rbt, LEFT(node)); + if (result != ISC_R_SUCCESS) + goto done; + LEFT(node) = NULL; + } + if (RIGHT(node) != NULL) { + result = dns_rbt_deletetree(rbt, RIGHT(node)); + if (result != ISC_R_SUCCESS) + goto done; + RIGHT(node) = NULL; + } + if (DOWN(node) != NULL) { + result = dns_rbt_deletetree(rbt, DOWN(node)); + if (result != ISC_R_SUCCESS) + goto done; + DOWN(node) = NULL; + } done: - if (result != ISC_R_SUCCESS) - return (result); + if (result != ISC_R_SUCCESS) + return (result); - if (DATA(node) != NULL && rbt->data_deleter != NULL) - rbt->data_deleter(DATA(node), rbt->deleter_arg); + if (DATA(node) != NULL && rbt->data_deleter != NULL) + rbt->data_deleter(DATA(node), rbt->deleter_arg); - unhash_node(rbt, node); + unhash_node(rbt, node); #if DNS_RBT_USEMAGIC - node->magic = 0; + node->magic = 0; #endif - isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); - rbt->nodecount--; - return (result); + + isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); + rbt->nodecount--; + return (result); } static void dns_rbt_deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, - dns_rbtnode_t **nodep) + dns_rbtnode_t **nodep) { - dns_rbtnode_t *parent; - dns_rbtnode_t *node = *nodep; - REQUIRE(VALID_RBT(rbt)); + dns_rbtnode_t *parent; + dns_rbtnode_t *node = *nodep; + REQUIRE(VALID_RBT(rbt)); again: - if (node == NULL) { - *nodep = NULL; - return; - } + if (node == NULL) { + *nodep = NULL; + return; + } traverse: - if (LEFT(node) != NULL) { - node = LEFT(node); - goto traverse; - } - if (RIGHT(node) != NULL) { - node = RIGHT(node); - goto traverse; - } - if (DOWN(node) != NULL) { - node = DOWN(node); - goto traverse; - } - - if (DATA(node) != NULL && rbt->data_deleter != NULL) - rbt->data_deleter(DATA(node), rbt->deleter_arg); - - /* - * Note: we don't call unhash_node() here as we are destroying - * the complete rbt tree. + if (LEFT(node) != NULL) { + node = LEFT(node); + goto traverse; + } + if (RIGHT(node) != NULL) { + node = RIGHT(node); + goto traverse; + } + if (DOWN(node) != NULL) { + node = DOWN(node); + goto traverse; + } + + if (DATA(node) != NULL && rbt->data_deleter != NULL) + rbt->data_deleter(DATA(node), rbt->deleter_arg); + + /* + * Note: we don't call unhash_node() here as we are destroying + * the complete rbt tree. */ #if DNS_RBT_USEMAGIC - node->magic = 0; + node->magic = 0; #endif - parent = PARENT(node); - if (parent != NULL) { - if (LEFT(parent) == node) - LEFT(parent) = NULL; - else if (DOWN(parent) == node) - DOWN(parent) = NULL; - else if (RIGHT(parent) == node) - RIGHT(parent) = NULL; - } - isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); - rbt->nodecount--; - node = parent; - if (quantum != 0 && --quantum == 0) { - *nodep = node; - return; - } - goto again; + parent = PARENT(node); + if (parent != NULL) { + if (LEFT(parent) == node) + LEFT(parent) = NULL; + else if (DOWN(parent) == node) + DOWN(parent) = NULL; + else if (RIGHT(parent) == node) + RIGHT(parent) = NULL; + } + + isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); + rbt->nodecount--; + node = parent; + if (quantum != 0 && --quantum == 0) { + *nodep = node; + return; + } + goto again; } static void dns_rbt_indent(int depth) { - int i; + int i; - for (i = 0; i < depth; i++) - putchar('\t'); + for (i = 0; i < depth; i++) + putchar('\t'); } static void dns_rbt_printnodename(dns_rbtnode_t *node) { - isc_region_t r; - dns_name_t name; - char buffer[DNS_NAME_FORMATSIZE]; - dns_offsets_t offsets; + isc_region_t r; + dns_name_t name; + char buffer[DNS_NAME_FORMATSIZE]; + dns_offsets_t offsets; - r.length = NAMELEN(node); - r.base = NAME(node); + r.length = NAMELEN(node); + r.base = NAME(node); - dns_name_init(&name, offsets); - dns_name_fromregion(&name, &r); + dns_name_init(&name, offsets); + dns_name_fromregion(&name, &r); - dns_name_format(&name, buffer, sizeof(buffer)); + dns_name_format(&name, buffer, sizeof(buffer)); - printf("%s", buffer); + printf("%s", buffer); } static void dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent, int depth) { - dns_rbt_indent(depth); - - if (root != NULL) { - dns_rbt_printnodename(root); - printf(" (%s", IS_RED(root) ? "RED" : "black"); - if (parent) { - printf(" from "); - dns_rbt_printnodename(parent); - } - - if ((! IS_ROOT(root) && PARENT(root) != parent) || - ( IS_ROOT(root) && depth > 0 && - DOWN(PARENT(root)) != root)) { - - printf(" (BAD parent pointer! -> "); - if (PARENT(root) != NULL) - dns_rbt_printnodename(PARENT(root)); - else - printf("NULL"); - printf(")"); - } - - printf(")\n"); - - - depth++; - - if (DOWN(root)) { - dns_rbt_indent(depth); - printf("++ BEG down from "); - dns_rbt_printnodename(root); - printf("\n"); - dns_rbt_printtree(DOWN(root), NULL, depth); - dns_rbt_indent(depth); - printf("-- END down from "); - dns_rbt_printnodename(root); - printf("\n"); - } - - if (IS_RED(root) && IS_RED(LEFT(root))) - printf("** Red/Red color violation on left\n"); - dns_rbt_printtree(LEFT(root), root, depth); - - if (IS_RED(root) && IS_RED(RIGHT(root))) - printf("** Red/Red color violation on right\n"); - dns_rbt_printtree(RIGHT(root), root, depth); - - } else - printf("NULL\n"); + dns_rbt_indent(depth); + + if (root != NULL) { + dns_rbt_printnodename(root); + printf(" (%s", IS_RED(root) ? "RED" : "black"); + if (parent) { + printf(" from "); + dns_rbt_printnodename(parent); + } + + if ((! IS_ROOT(root) && PARENT(root) != parent) || + ( IS_ROOT(root) && depth > 0 && + DOWN(PARENT(root)) != root)) { + + printf(" (BAD parent pointer! -> "); + if (PARENT(root) != NULL) + dns_rbt_printnodename(PARENT(root)); + else + printf("NULL"); + printf(")"); + } + + printf(")\n"); + + + depth++; + + if (DOWN(root)) { + dns_rbt_indent(depth); + printf("++ BEG down from "); + dns_rbt_printnodename(root); + printf("\n"); + dns_rbt_printtree(DOWN(root), NULL, depth); + dns_rbt_indent(depth); + printf("-- END down from "); + dns_rbt_printnodename(root); + printf("\n"); + } + + if (IS_RED(root) && IS_RED(LEFT(root))) + printf("** Red/Red color violation on left\n"); + dns_rbt_printtree(LEFT(root), root, depth); + + if (IS_RED(root) && IS_RED(RIGHT(root))) + printf("** Red/Red color violation on right\n"); + dns_rbt_printtree(RIGHT(root), root, depth); + + } else + printf("NULL\n"); } void dns_rbt_printall(dns_rbt_t *rbt) { - REQUIRE(VALID_RBT(rbt)); + REQUIRE(VALID_RBT(rbt)); - dns_rbt_printtree(rbt->root, NULL, 0); + dns_rbt_printtree(rbt->root, NULL, 0); } /* @@ -2181,364 +2187,364 @@ dns_rbt_printall(dns_rbt_t *rbt) { void dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) { - /* - * Initialize 'chain'. - */ + /* + * Initialize 'chain'. + */ - REQUIRE(chain != NULL); + REQUIRE(chain != NULL); - chain->mctx = mctx; - chain->end = NULL; - chain->level_count = 0; - chain->level_matches = 0; + chain->mctx = mctx; + chain->end = NULL; + chain->level_count = 0; + chain->level_matches = 0; - chain->magic = CHAIN_MAGIC; + chain->magic = CHAIN_MAGIC; } isc_result_t dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin, dns_rbtnode_t **node) + dns_name_t *origin, dns_rbtnode_t **node) { - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(VALID_CHAIN(chain)); - - if (node != NULL) - *node = chain->end; - - if (chain->end == NULL) - return (ISC_R_NOTFOUND); - - if (name != NULL) { - NODENAME(chain->end, name); - - if (chain->level_count == 0) { - /* - * Names in the top level tree are all absolute. - * Always make 'name' relative. - */ - INSIST(dns_name_isabsolute(name)); - - /* - * This is cheaper than dns_name_getlabelsequence(). - */ - name->labels--; - name->length--; - name->attributes &= ~DNS_NAMEATTR_ABSOLUTE; - } - } - - if (origin != NULL) { - if (chain->level_count > 0) - result = chain_name(chain, origin, ISC_FALSE); - else - result = dns_name_copy(dns_rootname, origin, NULL); - } - - return (result); + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_CHAIN(chain)); + + if (node != NULL) + *node = chain->end; + + if (chain->end == NULL) + return (ISC_R_NOTFOUND); + + if (name != NULL) { + NODENAME(chain->end, name); + + if (chain->level_count == 0) { + /* + * Names in the top level tree are all absolute. + * Always make 'name' relative. + */ + INSIST(dns_name_isabsolute(name)); + + /* + * This is cheaper than dns_name_getlabelsequence(). + */ + name->labels--; + name->length--; + name->attributes &= ~DNS_NAMEATTR_ABSOLUTE; + } + } + + if (origin != NULL) { + if (chain->level_count > 0) + result = chain_name(chain, origin, ISC_FALSE); + else + result = dns_name_copy(dns_rootname, origin, NULL); + } + + return (result); } isc_result_t dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin) + dns_name_t *origin) { - dns_rbtnode_t *current, *previous, *predecessor; - isc_result_t result = ISC_R_SUCCESS; - isc_boolean_t new_origin = ISC_FALSE; - - REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); - - predecessor = NULL; - - current = chain->end; - - if (LEFT(current) != NULL) { - /* - * Moving left one then right as far as possible is the - * previous node, at least for this level. - */ - current = LEFT(current); - - while (RIGHT(current) != NULL) - current = RIGHT(current); - - predecessor = current; - - } else { - /* - * No left links, so move toward the root. If at any point on - * the way there the link from parent to child is a right - * link, then the parent is the previous node, at least - * for this level. - */ - while (! IS_ROOT(current)) { - previous = current; - current = PARENT(current); - - if (RIGHT(current) == previous) { - predecessor = current; - break; - } - } - } - - if (predecessor != NULL) { - /* - * Found a predecessor node in this level. It might not - * really be the predecessor, however. - */ - if (DOWN(predecessor) != NULL) { - /* - * The predecessor is really down at least one level. - * Go down and as far right as possible, and repeat - * as long as the rightmost node has a down pointer. - */ - do { - /* - * XXX DCL Need to do something about origins - * here. See whether to go down, and if so - * whether it is truly what Bob calls a - * new origin. - */ - ADD_LEVEL(chain, predecessor); - predecessor = DOWN(predecessor); - - /* XXX DCL duplicated from above; clever - * way to unduplicate? */ - - while (RIGHT(predecessor) != NULL) - predecessor = RIGHT(predecessor); - } while (DOWN(predecessor) != NULL); - - /* XXX DCL probably needs work on the concept */ - if (origin != NULL) - new_origin = ISC_TRUE; - } - - } else if (chain->level_count > 0) { - /* - * Dang, didn't find a predecessor in this level. - * Got to the root of this level without having traversed - * any right links. Ascend the tree one level; the - * node that points to this tree is the predecessor. - */ - INSIST(chain->level_count > 0 && IS_ROOT(current)); - predecessor = chain->levels[--chain->level_count]; - - /* XXX DCL probably needs work on the concept */ - /* - * Don't declare an origin change when the new origin is "." - * at the top level tree, because "." is declared as the origin - * for the second level tree. - */ - if (origin != NULL && - (chain->level_count > 0 || OFFSETLEN(predecessor) > 1)) - new_origin = ISC_TRUE; - } - - if (predecessor != NULL) { - chain->end = predecessor; - - if (new_origin) { - result = dns_rbtnodechain_current(chain, name, origin, - NULL); - if (result == ISC_R_SUCCESS) - result = DNS_R_NEWORIGIN; - - } else - result = dns_rbtnodechain_current(chain, name, NULL, - NULL); - - } else - result = ISC_R_NOMORE; - - return (result); + dns_rbtnode_t *current, *previous, *predecessor; + isc_result_t result = ISC_R_SUCCESS; + isc_boolean_t new_origin = ISC_FALSE; + + REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); + + predecessor = NULL; + + current = chain->end; + + if (LEFT(current) != NULL) { + /* + * Moving left one then right as far as possible is the + * previous node, at least for this level. + */ + current = LEFT(current); + + while (RIGHT(current) != NULL) + current = RIGHT(current); + + predecessor = current; + + } else { + /* + * No left links, so move toward the root. If at any point on + * the way there the link from parent to child is a right + * link, then the parent is the previous node, at least + * for this level. + */ + while (! IS_ROOT(current)) { + previous = current; + current = PARENT(current); + + if (RIGHT(current) == previous) { + predecessor = current; + break; + } + } + } + + if (predecessor != NULL) { + /* + * Found a predecessor node in this level. It might not + * really be the predecessor, however. + */ + if (DOWN(predecessor) != NULL) { + /* + * The predecessor is really down at least one level. + * Go down and as far right as possible, and repeat + * as long as the rightmost node has a down pointer. + */ + do { + /* + * XXX DCL Need to do something about origins + * here. See whether to go down, and if so + * whether it is truly what Bob calls a + * new origin. + */ + ADD_LEVEL(chain, predecessor); + predecessor = DOWN(predecessor); + + /* XXX DCL duplicated from above; clever + * way to unduplicate? */ + + while (RIGHT(predecessor) != NULL) + predecessor = RIGHT(predecessor); + } while (DOWN(predecessor) != NULL); + + /* XXX DCL probably needs work on the concept */ + if (origin != NULL) + new_origin = ISC_TRUE; + } + + } else if (chain->level_count > 0) { + /* + * Dang, didn't find a predecessor in this level. + * Got to the root of this level without having traversed + * any right links. Ascend the tree one level; the + * node that points to this tree is the predecessor. + */ + INSIST(chain->level_count > 0 && IS_ROOT(current)); + predecessor = chain->levels[--chain->level_count]; + + /* XXX DCL probably needs work on the concept */ + /* + * Don't declare an origin change when the new origin is "." + * at the top level tree, because "." is declared as the origin + * for the second level tree. + */ + if (origin != NULL && + (chain->level_count > 0 || OFFSETLEN(predecessor) > 1)) + new_origin = ISC_TRUE; + } + + if (predecessor != NULL) { + chain->end = predecessor; + + if (new_origin) { + result = dns_rbtnodechain_current(chain, name, origin, + NULL); + if (result == ISC_R_SUCCESS) + result = DNS_R_NEWORIGIN; + + } else + result = dns_rbtnodechain_current(chain, name, NULL, + NULL); + + } else + result = ISC_R_NOMORE; + + return (result); } isc_result_t dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin) + dns_name_t *origin) { - dns_rbtnode_t *current, *previous, *successor; - isc_result_t result = ISC_R_SUCCESS; - isc_boolean_t new_origin = ISC_FALSE; - - REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); - - successor = NULL; - - current = chain->end; - - /* - * If there is a level below this node, the next node is the leftmost - * node of the next level. - */ - if (DOWN(current) != NULL) { - /* - * Don't declare an origin change when the new origin is "." - * at the second level tree, because "." is already declared - * as the origin for the top level tree. - */ - if (chain->level_count > 0 || - OFFSETLEN(current) > 1) - new_origin = ISC_TRUE; - - ADD_LEVEL(chain, current); - current = DOWN(current); - - while (LEFT(current) != NULL) - current = LEFT(current); - - successor = current; - - } else if (RIGHT(current) == NULL) { - /* - * The successor is up, either in this level or a previous one. - * Head back toward the root of the tree, looking for any path - * that was via a left link; the successor is the node that has - * that left link. In the event the root of the level is - * reached without having traversed any left links, ascend one - * level and look for either a right link off the point of - * ascent, or search for a left link upward again, repeating - * ascents until either case is true. - */ - do { - while (! IS_ROOT(current)) { - previous = current; - current = PARENT(current); - - if (LEFT(current) == previous) { - successor = current; - break; - } - } - - if (successor == NULL) { - /* - * Reached the root without having traversed - * any left pointers, so this level is done. - */ - if (chain->level_count == 0) - break; - - current = chain->levels[--chain->level_count]; - new_origin = ISC_TRUE; - - if (RIGHT(current) != NULL) - break; - } - } while (successor == NULL); - } - - if (successor == NULL && RIGHT(current) != NULL) { - current = RIGHT(current); - - while (LEFT(current) != NULL) - current = LEFT(current); - - successor = current; - } - - if (successor != NULL) { - chain->end = successor; - - /* - * It is not necessary to use dns_rbtnodechain_current like - * the other functions because this function will never - * find a node in the topmost level. This is because the - * root level will never be more than one name, and everything - * in the megatree is a successor to that node, down at - * the second level or below. - */ - - if (name != NULL) - NODENAME(chain->end, name); - - if (new_origin) { - if (origin != NULL) - result = chain_name(chain, origin, ISC_FALSE); - - if (result == ISC_R_SUCCESS) - result = DNS_R_NEWORIGIN; - - } else - result = ISC_R_SUCCESS; - - } else - result = ISC_R_NOMORE; - - return (result); + dns_rbtnode_t *current, *previous, *successor; + isc_result_t result = ISC_R_SUCCESS; + isc_boolean_t new_origin = ISC_FALSE; + + REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); + + successor = NULL; + + current = chain->end; + + /* + * If there is a level below this node, the next node is the leftmost + * node of the next level. + */ + if (DOWN(current) != NULL) { + /* + * Don't declare an origin change when the new origin is "." + * at the second level tree, because "." is already declared + * as the origin for the top level tree. + */ + if (chain->level_count > 0 || + OFFSETLEN(current) > 1) + new_origin = ISC_TRUE; + + ADD_LEVEL(chain, current); + current = DOWN(current); + + while (LEFT(current) != NULL) + current = LEFT(current); + + successor = current; + + } else if (RIGHT(current) == NULL) { + /* + * The successor is up, either in this level or a previous one. + * Head back toward the root of the tree, looking for any path + * that was via a left link; the successor is the node that has + * that left link. In the event the root of the level is + * reached without having traversed any left links, ascend one + * level and look for either a right link off the point of + * ascent, or search for a left link upward again, repeating + * ascents until either case is true. + */ + do { + while (! IS_ROOT(current)) { + previous = current; + current = PARENT(current); + + if (LEFT(current) == previous) { + successor = current; + break; + } + } + + if (successor == NULL) { + /* + * Reached the root without having traversed + * any left pointers, so this level is done. + */ + if (chain->level_count == 0) + break; + + current = chain->levels[--chain->level_count]; + new_origin = ISC_TRUE; + + if (RIGHT(current) != NULL) + break; + } + } while (successor == NULL); + } + + if (successor == NULL && RIGHT(current) != NULL) { + current = RIGHT(current); + + while (LEFT(current) != NULL) + current = LEFT(current); + + successor = current; + } + + if (successor != NULL) { + chain->end = successor; + + /* + * It is not necessary to use dns_rbtnodechain_current like + * the other functions because this function will never + * find a node in the topmost level. This is because the + * root level will never be more than one name, and everything + * in the megatree is a successor to that node, down at + * the second level or below. + */ + + if (name != NULL) + NODENAME(chain->end, name); + + if (new_origin) { + if (origin != NULL) + result = chain_name(chain, origin, ISC_FALSE); + + if (result == ISC_R_SUCCESS) + result = DNS_R_NEWORIGIN; + + } else + result = ISC_R_SUCCESS; + + } else + result = ISC_R_NOMORE; + + return (result); } isc_result_t dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin) + dns_name_t *name, dns_name_t *origin) { - isc_result_t result; + isc_result_t result; - REQUIRE(VALID_RBT(rbt)); - REQUIRE(VALID_CHAIN(chain)); + REQUIRE(VALID_RBT(rbt)); + REQUIRE(VALID_CHAIN(chain)); - dns_rbtnodechain_reset(chain); + dns_rbtnodechain_reset(chain); - chain->end = rbt->root; + chain->end = rbt->root; - result = dns_rbtnodechain_current(chain, name, origin, NULL); + result = dns_rbtnodechain_current(chain, name, origin, NULL); - if (result == ISC_R_SUCCESS) - result = DNS_R_NEWORIGIN; + if (result == ISC_R_SUCCESS) + result = DNS_R_NEWORIGIN; - return (result); + return (result); } isc_result_t dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin) + dns_name_t *name, dns_name_t *origin) { - isc_result_t result; + isc_result_t result; - REQUIRE(VALID_RBT(rbt)); - REQUIRE(VALID_CHAIN(chain)); + REQUIRE(VALID_RBT(rbt)); + REQUIRE(VALID_CHAIN(chain)); - dns_rbtnodechain_reset(chain); + dns_rbtnodechain_reset(chain); - result = move_chain_to_last(chain, rbt->root); - if (result != ISC_R_SUCCESS) - return (result); + result = move_chain_to_last(chain, rbt->root); + if (result != ISC_R_SUCCESS) + return (result); - result = dns_rbtnodechain_current(chain, name, origin, NULL); + result = dns_rbtnodechain_current(chain, name, origin, NULL); - if (result == ISC_R_SUCCESS) - result = DNS_R_NEWORIGIN; + if (result == ISC_R_SUCCESS) + result = DNS_R_NEWORIGIN; - return (result); + return (result); } void dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) { - /* - * Free any dynamic storage associated with 'chain', and then - * reinitialize 'chain'. - */ + /* + * Free any dynamic storage associated with 'chain', and then + * reinitialize 'chain'. + */ - REQUIRE(VALID_CHAIN(chain)); + REQUIRE(VALID_CHAIN(chain)); - chain->end = NULL; - chain->level_count = 0; - chain->level_matches = 0; + chain->end = NULL; + chain->level_count = 0; + chain->level_matches = 0; } void dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) { - /* - * Free any dynamic storage associated with 'chain', and then - * invalidate 'chain'. - */ + /* + * Free any dynamic storage associated with 'chain', and then + * invalidate 'chain'. + */ - dns_rbtnodechain_reset(chain); + dns_rbtnodechain_reset(chain); - chain->magic = 0; + chain->magic = 0; } diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 6b7d6403..f6b06dab 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.246 2007/06/19 06:19:29 marka Exp $ */ +/* $Id: rbtdb.c,v 1.247 2007/10/19 17:15:53 explorer Exp $ */ /*! \file */ @@ -25,8 +25,10 @@ #include <config.h> +#include <isc/heap.h> #include <isc/event.h> #include <isc/mem.h> +#include <isc/platform.h> #include <isc/print.h> #include <isc/mutex.h> #include <isc/random.h> @@ -62,20 +64,20 @@ #endif #ifdef DNS_RBTDB_VERSION64 -#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '8') +#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '8') #else -#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '4') +#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '4') #endif /*% * Note that "impmagic" is not the first four bytes of the struct, so * ISC_MAGIC_VALID cannot be used. */ -#define VALID_RBTDB(rbtdb) ((rbtdb) != NULL && \ - (rbtdb)->common.impmagic == RBTDB_MAGIC) +#define VALID_RBTDB(rbtdb) ((rbtdb) != NULL && \ + (rbtdb)->common.impmagic == RBTDB_MAGIC) #ifdef DNS_RBTDB_VERSION64 -typedef isc_uint64_t rbtdb_serial_t; +typedef isc_uint64_t rbtdb_serial_t; /*% * Make casting easier in symbolic debuggers by using different names * for the 64 bit version. @@ -84,25 +86,25 @@ typedef isc_uint64_t rbtdb_serial_t; #define rdatasetheader_t rdatasetheader64_t #define rbtdb_version_t rbtdb_version64_t #else -typedef isc_uint32_t rbtdb_serial_t; +typedef isc_uint32_t rbtdb_serial_t; #endif -typedef isc_uint32_t rbtdb_rdatatype_t; +typedef isc_uint32_t rbtdb_rdatatype_t; -#define RBTDB_RDATATYPE_BASE(type) ((dns_rdatatype_t)((type) & 0xFFFF)) -#define RBTDB_RDATATYPE_EXT(type) ((dns_rdatatype_t)((type) >> 16)) -#define RBTDB_RDATATYPE_VALUE(b, e) (((e) << 16) | (b)) +#define RBTDB_RDATATYPE_BASE(type) ((dns_rdatatype_t)((type) & 0xFFFF)) +#define RBTDB_RDATATYPE_EXT(type) ((dns_rdatatype_t)((type) >> 16)) +#define RBTDB_RDATATYPE_VALUE(b, e) (((e) << 16) | (b)) #define RBTDB_RDATATYPE_SIGNSEC \ - RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec) + RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec) #define RBTDB_RDATATYPE_SIGNS \ - RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns) + RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns) #define RBTDB_RDATATYPE_SIGCNAME \ - RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname) + RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname) #define RBTDB_RDATATYPE_SIGDNAME \ - RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dname) + RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dname) #define RBTDB_RDATATYPE_NCACHEANY \ - RBTDB_RDATATYPE_VALUE(0, dns_rdatatype_any) + RBTDB_RDATATYPE_VALUE(0, dns_rdatatype_any) /* * We use rwlock for DB lock only when ISC_RWLOCK_USEATOMIC is non 0. @@ -119,15 +121,15 @@ typedef isc_uint32_t rbtdb_rdatatype_t; #endif #if DNS_RBTDB_USERWLOCK -#define RBTDB_INITLOCK(l) isc_rwlock_init((l), 0, 0) -#define RBTDB_DESTROYLOCK(l) isc_rwlock_destroy(l) -#define RBTDB_LOCK(l, t) RWLOCK((l), (t)) -#define RBTDB_UNLOCK(l, t) RWUNLOCK((l), (t)) +#define RBTDB_INITLOCK(l) isc_rwlock_init((l), 0, 0) +#define RBTDB_DESTROYLOCK(l) isc_rwlock_destroy(l) +#define RBTDB_LOCK(l, t) RWLOCK((l), (t)) +#define RBTDB_UNLOCK(l, t) RWUNLOCK((l), (t)) #else -#define RBTDB_INITLOCK(l) isc_mutex_init(l) -#define RBTDB_DESTROYLOCK(l) DESTROYLOCK(l) -#define RBTDB_LOCK(l, t) LOCK(l) -#define RBTDB_UNLOCK(l, t) UNLOCK(l) +#define RBTDB_INITLOCK(l) isc_mutex_init(l) +#define RBTDB_DESTROYLOCK(l) DESTROYLOCK(l) +#define RBTDB_LOCK(l, t) LOCK(l) +#define RBTDB_UNLOCK(l, t) UNLOCK(l) #endif /* @@ -152,31 +154,31 @@ typedef isc_uint32_t rbtdb_rdatatype_t; #if defined(ISC_RWLOCK_USEATOMIC) && defined(DNS_RBT_USEISCREFCOUNT) typedef isc_rwlock_t nodelock_t; -#define NODE_INITLOCK(l) isc_rwlock_init((l), 0, 0) -#define NODE_DESTROYLOCK(l) isc_rwlock_destroy(l) -#define NODE_LOCK(l, t) RWLOCK((l), (t)) -#define NODE_UNLOCK(l, t) RWUNLOCK((l), (t)) -#define NODE_TRYUPGRADE(l) isc_rwlock_tryupgrade(l) - -#define NODE_STRONGLOCK(l) ((void)0) -#define NODE_STRONGUNLOCK(l) ((void)0) -#define NODE_WEAKLOCK(l, t) NODE_LOCK(l, t) -#define NODE_WEAKUNLOCK(l, t) NODE_UNLOCK(l, t) -#define NODE_WEAKDOWNGRADE(l) isc_rwlock_downgrade(l) +#define NODE_INITLOCK(l) isc_rwlock_init((l), 0, 0) +#define NODE_DESTROYLOCK(l) isc_rwlock_destroy(l) +#define NODE_LOCK(l, t) RWLOCK((l), (t)) +#define NODE_UNLOCK(l, t) RWUNLOCK((l), (t)) +#define NODE_TRYUPGRADE(l) isc_rwlock_tryupgrade(l) + +#define NODE_STRONGLOCK(l) ((void)0) +#define NODE_STRONGUNLOCK(l) ((void)0) +#define NODE_WEAKLOCK(l, t) NODE_LOCK(l, t) +#define NODE_WEAKUNLOCK(l, t) NODE_UNLOCK(l, t) +#define NODE_WEAKDOWNGRADE(l) isc_rwlock_downgrade(l) #else typedef isc_mutex_t nodelock_t; -#define NODE_INITLOCK(l) isc_mutex_init(l) -#define NODE_DESTROYLOCK(l) DESTROYLOCK(l) -#define NODE_LOCK(l, t) LOCK(l) -#define NODE_UNLOCK(l, t) UNLOCK(l) -#define NODE_TRYUPGRADE(l) ISC_R_SUCCESS - -#define NODE_STRONGLOCK(l) LOCK(l) -#define NODE_STRONGUNLOCK(l) UNLOCK(l) -#define NODE_WEAKLOCK(l, t) ((void)0) -#define NODE_WEAKUNLOCK(l, t) ((void)0) -#define NODE_WEAKDOWNGRADE(l) ((void)0) +#define NODE_INITLOCK(l) isc_mutex_init(l) +#define NODE_DESTROYLOCK(l) DESTROYLOCK(l) +#define NODE_LOCK(l, t) LOCK(l) +#define NODE_UNLOCK(l, t) UNLOCK(l) +#define NODE_TRYUPGRADE(l) ISC_R_SUCCESS + +#define NODE_STRONGLOCK(l) LOCK(l) +#define NODE_STRONGUNLOCK(l) UNLOCK(l) +#define NODE_WEAKLOCK(l, t) ((void)0) +#define NODE_WEAKUNLOCK(l, t) ((void)0) +#define NODE_WEAKDOWNGRADE(l) ((void)0) #endif #ifndef DNS_RDATASET_FIXED @@ -184,78 +186,105 @@ typedef isc_mutex_t nodelock_t; #endif /* - * Allow clients with a virtual time of upto 5 minutes in the past to see + * Allow clients with a virtual time of up to 5 minutes in the past to see * records that would have otherwise have expired. */ #define RBTDB_VIRTUAL 300 struct noqname { - dns_name_t name; - void * nsec; - void * nsecsig; + dns_name_t name; + void * nsec; + void * nsecsig; }; -typedef struct acachectl acachectl_t; +typedef struct acachectl acachectl_t; typedef struct rdatasetheader { - /*% - * Locked by the owning node's lock. - */ - rbtdb_serial_t serial; - dns_ttl_t ttl; - rbtdb_rdatatype_t type; - isc_uint16_t attributes; - dns_trust_t trust; - struct noqname *noqname; - /*%< - * We don't use the LIST macros, because the LIST structure has - * both head and tail pointers, and is doubly linked. - */ - - struct rdatasetheader *next; - /*%< - * If this is the top header for an rdataset, 'next' points - * to the top header for the next rdataset (i.e., the next type). - * Otherwise, it points up to the header whose down pointer points - * at this header. - */ - - struct rdatasetheader *down; - /*%< - * Points to the header for the next older version of - * this rdataset. - */ - - isc_uint32_t count; - /*%< - * Monotonously increased every time this rdataset is bound so that - * it is used as the base of the starting point in DNS responses - * when the "cyclic" rrset-order is required. Since the ordering - * should not be so crucial, no lock is set for the counter for - * performance reasons. - */ - - acachectl_t *additional_auth; - acachectl_t *additional_glue; + /*% + * Locked by the owning node's lock. + */ + rbtdb_serial_t serial; + dns_ttl_t rdh_ttl; + rbtdb_rdatatype_t type; + isc_uint16_t attributes; + dns_trust_t trust; + struct noqname *noqname; + /*%< + * We don't use the LIST macros, because the LIST structure has + * both head and tail pointers, and is doubly linked. + */ + + struct rdatasetheader *next; + /*%< + * If this is the top header for an rdataset, 'next' points + * to the top header for the next rdataset (i.e., the next type). + * Otherwise, it points up to the header whose down pointer points + * at this header. + */ + + struct rdatasetheader *down; + /*%< + * Points to the header for the next older version of + * this rdataset. + */ + + isc_uint32_t count; + /*%< + * Monotonously increased every time this rdataset is bound so that + * it is used as the base of the starting point in DNS responses + * when the "cyclic" rrset-order is required. Since the ordering + * should not be so crucial, no lock is set for the counter for + * performance reasons. + */ + + acachectl_t *additional_auth; + acachectl_t *additional_glue; + + dns_rbtnode_t *node; + isc_stdtime_t last_used; + ISC_LINK(struct rdatasetheader) lru_link; + /*%< + * Used for LRU-based cache management. We should probably make + * these cache-DB specific. We might also make it a pointer and + * ensure only the top header has a valid link to save memory. + * The linked-list is locked by the rbtdb->lrulock. + */ + + /* + * It's possible this should not be here anymore, but instead + * referenced from the bucket's heap directly. + */ +#if 0 + isc_heap_t *heap; +#endif + unsigned int heap_index; + /*%< + * Used for TTL-based cache cleaning. + */ } rdatasetheader_t; -#define RDATASET_ATTR_NONEXISTENT 0x0001 -#define RDATASET_ATTR_STALE 0x0002 -#define RDATASET_ATTR_IGNORE 0x0004 -#define RDATASET_ATTR_RETAIN 0x0008 -#define RDATASET_ATTR_NXDOMAIN 0x0010 +typedef ISC_LIST(rdatasetheader_t) rdatasetheaderlist_t; +typedef ISC_LIST(dns_rbtnode_t) rbtnodelist_t; + +#define RDATASET_ATTR_NONEXISTENT 0x0001 +#define RDATASET_ATTR_STALE 0x0002 +#define RDATASET_ATTR_IGNORE 0x0004 +#define RDATASET_ATTR_RETAIN 0x0008 +#define RDATASET_ATTR_NXDOMAIN 0x0010 +#define RDATASET_ATTR_CACHE 0x1000 /* for debug */ +#define RDATASET_ATTR_CANCELED 0x2000 /* for debug */ typedef struct acache_cbarg { - dns_rdatasetadditional_t type; - unsigned int count; - dns_db_t *db; - dns_dbnode_t *node; - rdatasetheader_t *header; + dns_rdatasetadditional_t type; + unsigned int count; + dns_db_t *db; + dns_dbnode_t *node; + rdatasetheader_t *header; } acache_cbarg_t; struct acachectl { - dns_acacheentry_t *entry; - acache_cbarg_t *cbarg; + dns_acacheentry_t *entry; + acache_cbarg_t *cbarg; }; /* @@ -266,118 +295,162 @@ struct acachectl { * expired. */ -#undef IGNORE /* WIN32 winbase.h defines this. */ +#undef IGNORE /* WIN32 winbase.h defines this. */ #define EXISTS(header) \ - (((header)->attributes & RDATASET_ATTR_NONEXISTENT) == 0) + (((header)->attributes & RDATASET_ATTR_NONEXISTENT) == 0) #define NONEXISTENT(header) \ - (((header)->attributes & RDATASET_ATTR_NONEXISTENT) != 0) + (((header)->attributes & RDATASET_ATTR_NONEXISTENT) != 0) #define IGNORE(header) \ - (((header)->attributes & RDATASET_ATTR_IGNORE) != 0) + (((header)->attributes & RDATASET_ATTR_IGNORE) != 0) #define RETAIN(header) \ - (((header)->attributes & RDATASET_ATTR_RETAIN) != 0) + (((header)->attributes & RDATASET_ATTR_RETAIN) != 0) #define NXDOMAIN(header) \ - (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0) + (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0) -#define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */ -#define DEFAULT_CACHE_NODE_LOCK_COUNT 1009 /*%< Should be prime. */ +#define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */ +#define DEFAULT_CACHE_NODE_LOCK_COUNT 1009 /*%< Should be prime. */ typedef struct { - nodelock_t lock; - /* Protected in the refcount routines. */ - isc_refcount_t references; - /* Locked by lock. */ - isc_boolean_t exiting; + nodelock_t lock; + /* Protected in the refcount routines. */ + isc_refcount_t references; + /* Locked by lock. */ + isc_boolean_t exiting; } rbtdb_nodelock_t; typedef struct rbtdb_changed { - dns_rbtnode_t * node; - isc_boolean_t dirty; - ISC_LINK(struct rbtdb_changed) link; + dns_rbtnode_t * node; + isc_boolean_t dirty; + ISC_LINK(struct rbtdb_changed) link; } rbtdb_changed_t; -typedef ISC_LIST(rbtdb_changed_t) rbtdb_changedlist_t; +typedef ISC_LIST(rbtdb_changed_t) rbtdb_changedlist_t; typedef struct rbtdb_version { - /* Not locked */ - rbtdb_serial_t serial; - /* - * Protected in the refcount routines. - * XXXJT: should we change the lock policy based on the refcount - * performance? - */ - isc_refcount_t references; - /* Locked by database lock. */ - isc_boolean_t writer; - isc_boolean_t commit_ok; - rbtdb_changedlist_t changed_list; - ISC_LINK(struct rbtdb_version) link; + /* Not locked */ + rbtdb_serial_t serial; + /* + * Protected in the refcount routines. + * XXXJT: should we change the lock policy based on the refcount + * performance? + */ + isc_refcount_t references; + /* Locked by database lock. */ + isc_boolean_t writer; + isc_boolean_t commit_ok; + rbtdb_changedlist_t changed_list; + ISC_LINK(struct rbtdb_version) link; } rbtdb_version_t; -typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t; +typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t; + +#ifdef LRU_DEBUG +/* statistics info for testing */ +struct cachestat { + unsigned int cache_total; + int cache_current; + unsigned int ncache_total; + int ncache_current; + unsigned int a_total; + int a_current; + unsigned int aaaa_total; + int aaaa_current; + unsigned int ns_total; + int ns_current; + unsigned int ptr_total; + int ptr_current; + unsigned int glue_total; + int glue_current; + unsigned int additional_total; + int additional_current; + + unsigned int stale_purge; + unsigned int stale_scan; + unsigned int stale_expire; + unsigned int stale_lru; +}; +#endif typedef struct { - /* Unlocked. */ - dns_db_t common; + /* Unlocked. */ + dns_db_t common; #if DNS_RBTDB_USERWLOCK - isc_rwlock_t lock; + isc_rwlock_t lock; #else - isc_mutex_t lock; + isc_mutex_t lock; +#endif + isc_rwlock_t tree_lock; + unsigned int node_lock_count; + rbtdb_nodelock_t * node_locks; + dns_rbtnode_t * origin_node; + /* Locked by lock. */ + unsigned int active; + isc_refcount_t references; + unsigned int attributes; + rbtdb_serial_t current_serial; + rbtdb_serial_t least_serial; + rbtdb_serial_t next_serial; + rbtdb_version_t * current_version; + rbtdb_version_t * future_version; + rbtdb_versionlist_t open_versions; + isc_boolean_t overmem; + isc_task_t * task; + dns_dbnode_t *soanode; + dns_dbnode_t *nsnode; + + /* + * This is a linked list used to implement the LRU cache. There will + * be node_lock_count linked lists here. Nodes in bucket 1 will be + * placed on the linked list rdatasets[1]. + */ + rdatasetheaderlist_t *rdatasets; + rbtnodelist_t *deadnodes; + + /* + * Heaps. Each of these is used for TTL based expiry. + */ + isc_heap_t **heaps; + + /* Locked by tree_lock. */ + dns_rbt_t * tree; + isc_boolean_t secure; + + /* Unlocked */ + unsigned int quantum; +#ifdef LRU_DEBUG + struct cachestat cachestat; #endif - isc_rwlock_t tree_lock; - unsigned int node_lock_count; - rbtdb_nodelock_t * node_locks; - dns_rbtnode_t * origin_node; - /* Locked by lock. */ - unsigned int active; - isc_refcount_t references; - unsigned int attributes; - rbtdb_serial_t current_serial; - rbtdb_serial_t least_serial; - rbtdb_serial_t next_serial; - rbtdb_version_t * current_version; - rbtdb_version_t * future_version; - rbtdb_versionlist_t open_versions; - isc_boolean_t overmem; - isc_task_t * task; - dns_dbnode_t *soanode; - dns_dbnode_t *nsnode; - /* Locked by tree_lock. */ - dns_rbt_t * tree; - isc_boolean_t secure; - - /* Unlocked */ - unsigned int quantum; } dns_rbtdb_t; -#define RBTDB_ATTR_LOADED 0x01 -#define RBTDB_ATTR_LOADING 0x02 +#define RBTDB_ATTR_LOADED 0x01 +#define RBTDB_ATTR_LOADING 0x02 /*% * Search Context */ typedef struct { - dns_rbtdb_t * rbtdb; - rbtdb_version_t * rbtversion; - rbtdb_serial_t serial; - unsigned int options; - dns_rbtnodechain_t chain; - isc_boolean_t copy_name; - isc_boolean_t need_cleanup; - isc_boolean_t wild; - dns_rbtnode_t * zonecut; - rdatasetheader_t * zonecut_rdataset; - rdatasetheader_t * zonecut_sigrdataset; - dns_fixedname_t zonecut_name; - isc_stdtime_t now; + dns_rbtdb_t * rbtdb; + rbtdb_version_t * rbtversion; + rbtdb_serial_t serial; + unsigned int options; + dns_rbtnodechain_t chain; + isc_boolean_t copy_name; + isc_boolean_t need_cleanup; + isc_boolean_t wild; + dns_rbtnode_t * zonecut; + rdatasetheader_t * zonecut_rdataset; + rdatasetheader_t * zonecut_sigrdataset; + dns_fixedname_t zonecut_name; + isc_stdtime_t now; } rbtdb_search_t; /*% * Load Context */ typedef struct { - dns_rbtdb_t * rbtdb; - isc_stdtime_t now; + dns_rbtdb_t * rbtdb; + isc_stdtime_t now; } rbtdb_load_t; static void rdataset_disassociate(dns_rdataset_t *rdataset); @@ -387,90 +460,96 @@ static void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata); static void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target); static unsigned int rdataset_count(dns_rdataset_t *rdataset); static isc_result_t rdataset_getnoqname(dns_rdataset_t *rdataset, - dns_name_t *name, - dns_rdataset_t *nsec, - dns_rdataset_t *nsecsig); + dns_name_t *name, + dns_rdataset_t *nsec, + dns_rdataset_t *nsecsig); static isc_result_t rdataset_getadditional(dns_rdataset_t *rdataset, - dns_rdatasetadditional_t type, - dns_rdatatype_t qtype, - dns_acache_t *acache, - dns_zone_t **zonep, - dns_db_t **dbp, - dns_dbversion_t **versionp, - dns_dbnode_t **nodep, - dns_name_t *fname, - dns_message_t *msg, - isc_stdtime_t now); + dns_rdatasetadditional_t type, + dns_rdatatype_t qtype, + dns_acache_t *acache, + dns_zone_t **zonep, + dns_db_t **dbp, + dns_dbversion_t **versionp, + dns_dbnode_t **nodep, + dns_name_t *fname, + dns_message_t *msg, + isc_stdtime_t now); static isc_result_t rdataset_setadditional(dns_rdataset_t *rdataset, - dns_rdatasetadditional_t type, - dns_rdatatype_t qtype, - dns_acache_t *acache, - dns_zone_t *zone, - dns_db_t *db, - dns_dbversion_t *version, - dns_dbnode_t *node, - dns_name_t *fname); + dns_rdatasetadditional_t type, + dns_rdatatype_t qtype, + dns_acache_t *acache, + dns_zone_t *zone, + dns_db_t *db, + dns_dbversion_t *version, + dns_dbnode_t *node, + dns_name_t *fname); static isc_result_t rdataset_putadditional(dns_acache_t *acache, - dns_rdataset_t *rdataset, - dns_rdatasetadditional_t type, - dns_rdatatype_t qtype); + dns_rdataset_t *rdataset, + dns_rdatasetadditional_t type, + dns_rdatatype_t qtype); +static inline isc_boolean_t need_headerupdate(rdatasetheader_t *header, + isc_stdtime_t now); +static void update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, + isc_stdtime_t now); +static void check_stale_cache(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, + isc_stdtime_t now, isc_boolean_t tree_locked); static dns_rdatasetmethods_t rdataset_methods = { - rdataset_disassociate, - rdataset_first, - rdataset_next, - rdataset_current, - rdataset_clone, - rdataset_count, - NULL, - rdataset_getnoqname, - rdataset_getadditional, - rdataset_setadditional, - rdataset_putadditional + rdataset_disassociate, + rdataset_first, + rdataset_next, + rdataset_current, + rdataset_clone, + rdataset_count, + NULL, + rdataset_getnoqname, + rdataset_getadditional, + rdataset_setadditional, + rdataset_putadditional }; static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp); static isc_result_t rdatasetiter_first(dns_rdatasetiter_t *iterator); static isc_result_t rdatasetiter_next(dns_rdatasetiter_t *iterator); static void rdatasetiter_current(dns_rdatasetiter_t *iterator, - dns_rdataset_t *rdataset); + dns_rdataset_t *rdataset); static dns_rdatasetitermethods_t rdatasetiter_methods = { - rdatasetiter_destroy, - rdatasetiter_first, - rdatasetiter_next, - rdatasetiter_current + rdatasetiter_destroy, + rdatasetiter_first, + rdatasetiter_next, + rdatasetiter_current }; typedef struct rbtdb_rdatasetiter { - dns_rdatasetiter_t common; - rdatasetheader_t * current; + dns_rdatasetiter_t common; + rdatasetheader_t * current; } rbtdb_rdatasetiter_t; -static void dbiterator_destroy(dns_dbiterator_t **iteratorp); -static isc_result_t dbiterator_first(dns_dbiterator_t *iterator); -static isc_result_t dbiterator_last(dns_dbiterator_t *iterator); -static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, - dns_name_t *name); -static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator); -static isc_result_t dbiterator_next(dns_dbiterator_t *iterator); -static isc_result_t dbiterator_current(dns_dbiterator_t *iterator, - dns_dbnode_t **nodep, - dns_name_t *name); -static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator); -static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, - dns_name_t *name); +static void dbiterator_destroy(dns_dbiterator_t **iteratorp); +static isc_result_t dbiterator_first(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_last(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, + dns_name_t *name); +static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_next(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_current(dns_dbiterator_t *iterator, + dns_dbnode_t **nodep, + dns_name_t *name); +static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, + dns_name_t *name); static dns_dbiteratormethods_t dbiterator_methods = { - dbiterator_destroy, - dbiterator_first, - dbiterator_last, - dbiterator_seek, - dbiterator_prev, - dbiterator_next, - dbiterator_current, - dbiterator_pause, - dbiterator_origin + dbiterator_destroy, + dbiterator_first, + dbiterator_last, + dbiterator_seek, + dbiterator_prev, + dbiterator_next, + dbiterator_current, + dbiterator_pause, + dbiterator_origin }; #define DELETION_BATCH_MAX 64 @@ -479,17 +558,17 @@ static dns_dbiteratormethods_t dbiterator_methods = { * If 'paused' is ISC_TRUE, then the tree lock is not being held. */ typedef struct rbtdb_dbiterator { - dns_dbiterator_t common; - isc_boolean_t paused; - isc_boolean_t new_origin; - isc_rwlocktype_t tree_locked; - isc_result_t result; - dns_fixedname_t name; - dns_fixedname_t origin; - dns_rbtnodechain_t chain; - dns_rbtnode_t *node; - dns_rbtnode_t *deletions[DELETION_BATCH_MAX]; - int delete; + dns_dbiterator_t common; + isc_boolean_t paused; + isc_boolean_t new_origin; + isc_rwlocktype_t tree_locked; + isc_result_t result; + dns_fixedname_t name; + dns_fixedname_t origin; + dns_rbtnodechain_t chain; + dns_rbtnode_t *node; + dns_rbtnode_t *deletions[DELETION_BATCH_MAX]; + int delete; } rbtdb_dbiterator_t; @@ -497,7 +576,8 @@ typedef struct rbtdb_dbiterator { #define IS_CACHE(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_CACHE) != 0) static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, - isc_event_t *event); + isc_event_t *event); +static void overmem(dns_db_t *db, isc_boolean_t overmem); /*% * 'init_count' is used to initialize 'newheader->count' which inturn @@ -505,10 +585,10 @@ static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, * We don't lock this as we don't care about simultanious updates. * * Note: - * Both init_count and header->count can be ISC_UINT32_MAX. + * Both init_count and header->count can be ISC_UINT32_MAX. * The count on the returned rdataset however can't be as - * that indicates that the database does not implement cyclic - * processing. + * that indicates that the database does not implement cyclic + * processing. */ static unsigned int init_count; @@ -518,12 +598,12 @@ static unsigned int init_count; * If a routine is going to lock more than one lock in this module, then * the locking must be done in the following order: * - * Tree Lock + * Tree Lock * - * Node Lock (Only one from the set may be locked at one time by - * any caller) + * Node Lock (Only one from the set may be locked at one time by + * any caller) * - * Database Lock + * Database Lock * * Failure to follow this hierarchy can result in deadlock. */ @@ -531,11 +611,7 @@ static unsigned int init_count; /* * Deleting Nodes * - * Currently there is no deletion of nodes from the database, except when - * the database is being destroyed. - * - * If node deletion is added in the future, then for zone databases the node - * for the origin of the zone MUST NOT be deleted. + * For zone databases the node for the origin of the zone MUST NOT be deleted. */ @@ -545,22 +621,73 @@ static unsigned int init_count; static void attach(dns_db_t *source, dns_db_t **targetp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)source; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)source; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - isc_refcount_increment(&rbtdb->references, NULL); + isc_refcount_increment(&rbtdb->references, NULL); - *targetp = source; + *targetp = source; } static void free_rbtdb_callback(isc_task_t *task, isc_event_t *event) { - dns_rbtdb_t *rbtdb = event->ev_arg; + dns_rbtdb_t *rbtdb = event->ev_arg; - UNUSED(task); + UNUSED(task); - free_rbtdb(rbtdb, ISC_TRUE, event); + free_rbtdb(rbtdb, ISC_TRUE, event); +} + +static void +set_ttl(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, dns_ttl_t newttl) { + int idx; + isc_heap_t *heap; + dns_ttl_t oldttl; + + oldttl = header->rdh_ttl; + header->rdh_ttl = newttl; + + /* + * It's possible the rbtdb is not a cache. If this is the case, + * we will not have a heap, and we move on. If we do, though, + * we might need to adjust things. + */ + if (header->heap_index == 0 || newttl == oldttl) + return; + idx = header->node->locknum; + if (rbtdb->heaps == NULL || rbtdb->heaps[idx] == NULL) + return; + heap = rbtdb->heaps[idx]; + + if (newttl < oldttl) + isc_heap_increased(heap, header->heap_index); + else + isc_heap_decreased(heap, header->heap_index); +} + +/*% + * This function allows the heap code to rank the priority of each + * element. It returns ISC_TRUE if v1 happens "sooner" than v2. + */ +static isc_boolean_t +ttl_sooner(void *v1, void *v2) { + rdatasetheader_t *h1 = v1; + rdatasetheader_t *h2 = v2; + + if (h1->rdh_ttl < h2->rdh_ttl) + return (ISC_TRUE); + return (ISC_FALSE); +} + +/*% + * This function sets the heap index into the header. + */ +static void +ttl_set_index(void *what, unsigned int index) { + rdatasetheader_t *h = what; + + h->heap_index = index; } /*% @@ -571,562 +698,792 @@ free_rbtdb_callback(isc_task_t *task, isc_event_t *event) { */ static unsigned int adjust_quantum(unsigned int old, isc_time_t *start) { - unsigned int pps = dns_pps; /* packets per second */ - unsigned int interval; - isc_uint64_t usecs; - isc_time_t end; - unsigned int new; - - if (pps < 100) - pps = 100; - isc_time_now(&end); - - interval = 1000000 / pps; /* interval in usec */ - if (interval == 0) - interval = 1; - usecs = isc_time_microdiff(&end, start); - if (usecs == 0) { - /* - * We were unable to measure the amount of time taken. - * Double the nodes deleted next time. - */ - old *= 2; - if (old > 1000) - old = 1000; - return (old); - } - new = old * interval; - new /= (unsigned int)usecs; - if (new == 0) - new = 1; - else if (new > 1000) - new = 1000; - - /* Smooth */ - new = (new + old * 3) / 4; - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "adjust_quantum -> %d", new); - - return (new); + unsigned int pps = dns_pps; /* packets per second */ + unsigned int interval; + isc_uint64_t usecs; + isc_time_t end; + unsigned int new; + + if (pps < 100) + pps = 100; + isc_time_now(&end); + + interval = 1000000 / pps; /* interval in usec */ + if (interval == 0) + interval = 1; + usecs = isc_time_microdiff(&end, start); + if (usecs == 0) { + /* + * We were unable to measure the amount of time taken. + * Double the nodes deleted next time. + */ + old *= 2; + if (old > 1000) + old = 1000; + return (old); + } + new = old * interval; + new /= (unsigned int)usecs; + if (new == 0) + new = 1; + else if (new > 1000) + new = 1000; + + /* Smooth */ + new = (new + old * 3) / 4; + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, + ISC_LOG_DEBUG(1), "adjust_quantum -> %d", new); + + return (new); } - + static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { - unsigned int i; - isc_ondestroy_t ondest; - isc_result_t result; - char buf[DNS_NAME_FORMATSIZE]; - isc_time_t start; - - REQUIRE(rbtdb->current_version != NULL || EMPTY(rbtdb->open_versions)); - REQUIRE(rbtdb->future_version == NULL); - - if (rbtdb->current_version != NULL) { - unsigned int refs; - - isc_refcount_decrement(&rbtdb->current_version->references, - &refs); - INSIST(refs == 0); - UNLINK(rbtdb->open_versions, rbtdb->current_version, link); - isc_refcount_destroy(&rbtdb->current_version->references); - isc_mem_put(rbtdb->common.mctx, rbtdb->current_version, - sizeof(rbtdb_version_t)); - } - if (event == NULL) - rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0; + unsigned int i; + isc_ondestroy_t ondest; + isc_result_t result; + char buf[DNS_NAME_FORMATSIZE]; + isc_time_t start; + + if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in) + overmem((dns_db_t *)rbtdb, (isc_boolean_t)-1); + + REQUIRE(rbtdb->current_version != NULL || EMPTY(rbtdb->open_versions)); + REQUIRE(rbtdb->future_version == NULL); + + if (rbtdb->current_version != NULL) { + unsigned int refs; + + isc_refcount_decrement(&rbtdb->current_version->references, + &refs); + INSIST(refs == 0); + UNLINK(rbtdb->open_versions, rbtdb->current_version, link); + isc_refcount_destroy(&rbtdb->current_version->references); + isc_mem_put(rbtdb->common.mctx, rbtdb->current_version, + sizeof(rbtdb_version_t)); + } + if (IS_CACHE(rbtdb)) { + /* + * We assume the number of remaining dead nodes is reasonably + * small; the overhead of unlinking all nodes here should be + * negligible. + */ + for (i = 0; i < rbtdb->node_lock_count; i++) { + dns_rbtnode_t *node; + + node = ISC_LIST_HEAD(rbtdb->deadnodes[i]); + while (node != NULL) { + ISC_LIST_UNLINK(rbtdb->deadnodes[i], node, + deadlink); + node = ISC_LIST_HEAD(rbtdb->deadnodes[i]); + } + } + } + if (event == NULL) + rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0; again: - if (rbtdb->tree != NULL) { - isc_time_now(&start); - result = dns_rbt_destroy2(&rbtdb->tree, rbtdb->quantum); - if (result == ISC_R_QUOTA) { - INSIST(rbtdb->task != NULL); - if (rbtdb->quantum != 0) - rbtdb->quantum = adjust_quantum(rbtdb->quantum, - &start); - if (event == NULL) - event = isc_event_allocate(rbtdb->common.mctx, - NULL, - DNS_EVENT_FREESTORAGE, - free_rbtdb_callback, - rbtdb, - sizeof(isc_event_t)); - if (event == NULL) - goto again; - isc_task_send(rbtdb->task, &event); - return; - } - INSIST(result == ISC_R_SUCCESS && rbtdb->tree == NULL); - } - if (event != NULL) - isc_event_free(&event); - if (log) { - if (dns_name_dynamic(&rbtdb->common.origin)) - dns_name_format(&rbtdb->common.origin, buf, - sizeof(buf)); - else - strcpy(buf, "<UNKNOWN>"); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "done free_rbtdb(%s)", buf); - } - if (dns_name_dynamic(&rbtdb->common.origin)) - dns_name_free(&rbtdb->common.origin, rbtdb->common.mctx); - for (i = 0; i < rbtdb->node_lock_count; i++) { - isc_refcount_destroy(&rbtdb->node_locks[i].references); - NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); - } - isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks, - rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); - isc_rwlock_destroy(&rbtdb->tree_lock); - isc_refcount_destroy(&rbtdb->references); - if (rbtdb->task != NULL) - isc_task_detach(&rbtdb->task); - RBTDB_DESTROYLOCK(&rbtdb->lock); - rbtdb->common.magic = 0; - rbtdb->common.impmagic = 0; - ondest = rbtdb->common.ondest; - isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb)); - isc_ondestroy_notify(&ondest, rbtdb); + if (rbtdb->tree != NULL) { + isc_time_now(&start); + result = dns_rbt_destroy2(&rbtdb->tree, rbtdb->quantum); + if (result == ISC_R_QUOTA) { + INSIST(rbtdb->task != NULL); + if (rbtdb->quantum != 0) + rbtdb->quantum = adjust_quantum(rbtdb->quantum, + &start); + if (event == NULL) + event = isc_event_allocate(rbtdb->common.mctx, + NULL, + DNS_EVENT_FREESTORAGE, + free_rbtdb_callback, + rbtdb, + sizeof(isc_event_t)); + if (event == NULL) + goto again; + isc_task_send(rbtdb->task, &event); + return; + } + INSIST(result == ISC_R_SUCCESS && rbtdb->tree == NULL); + } + if (event != NULL) + isc_event_free(&event); + if (log) { + if (dns_name_dynamic(&rbtdb->common.origin)) + dns_name_format(&rbtdb->common.origin, buf, + sizeof(buf)); + else + strcpy(buf, "<UNKNOWN>"); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), + "done free_rbtdb(%s)", buf); + } + if (dns_name_dynamic(&rbtdb->common.origin)) + dns_name_free(&rbtdb->common.origin, rbtdb->common.mctx); + for (i = 0; i < rbtdb->node_lock_count; i++) { + isc_refcount_destroy(&rbtdb->node_locks[i].references); + NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); + } + + /* + * Clean up LRU cache objects. + */ + if (rbtdb->rdatasets != NULL) { + for (i = 0; i < rbtdb->node_lock_count; i++) + INSIST(ISC_LIST_EMPTY(rbtdb->rdatasets[i])); + isc_mem_put(rbtdb->common.mctx, rbtdb->rdatasets, + rbtdb->node_lock_count * + sizeof(rdatasetheaderlist_t)); + } + /* + * Clean up dead node buckets. + */ + if (rbtdb->deadnodes != NULL) { + for (i = 0; i < rbtdb->node_lock_count; i++) + INSIST(ISC_LIST_EMPTY(rbtdb->deadnodes[i])); + isc_mem_put(rbtdb->common.mctx, rbtdb->deadnodes, + rbtdb->node_lock_count * sizeof(rbtnodelist_t)); + } + /* + * Clean up TTL heap cache objects. + */ + if (rbtdb->heaps != NULL) { + for (i = 0; i < rbtdb->node_lock_count; i++) + isc_heap_destroy(&rbtdb->heaps[i]); + isc_mem_put(rbtdb->common.mctx, rbtdb->heaps, + rbtdb->node_lock_count * + sizeof(isc_heap_t *)); + } + + isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks, + rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); + isc_rwlock_destroy(&rbtdb->tree_lock); + isc_refcount_destroy(&rbtdb->references); + if (rbtdb->task != NULL) + isc_task_detach(&rbtdb->task); + +#ifdef LRU_DEBUG + /* Experimental logging about memory usage */ + if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_INFO, + "cache DB %p: mem inuse %lu, XXX node, " + "%d/%u current/total cache, %d/%u neg, %d/%u A, %d/%u AAAA, " + "%d/%u NS, %d/%u PTR, %d/%u glue, " + "%d/%u additional, purge/scan=%u(%u expiry, %u lru)/%u, " + "overmem=%d", + rbtdb, + (unsigned long)isc_mem_inuse(rbtdb->common.mctx), + rbtdb->cachestat.cache_current, rbtdb->cachestat.cache_total, + rbtdb->cachestat.ncache_current, rbtdb->cachestat.ncache_total, + rbtdb->cachestat.a_current, rbtdb->cachestat.a_total, + rbtdb->cachestat.aaaa_current, rbtdb->cachestat.aaaa_total, + rbtdb->cachestat.ns_current, rbtdb->cachestat.ns_total, + rbtdb->cachestat.ptr_current, rbtdb->cachestat.ptr_total, + rbtdb->cachestat.glue_current, rbtdb->cachestat.glue_total, + rbtdb->cachestat.additional_current, + rbtdb->cachestat.additional_total, + rbtdb->cachestat.stale_purge, rbtdb->cachestat.stale_expire, + rbtdb->cachestat.stale_lru, rbtdb->cachestat.stale_scan, + rbtdb->overmem); + INSIST(rbtdb->cachestat.cache_current == 0); + INSIST(rbtdb->cachestat.ncache_current == 0); + INSIST(rbtdb->cachestat.a_current == 0); + INSIST(rbtdb->cachestat.aaaa_current == 0); + INSIST(rbtdb->cachestat.ns_current == 0); + INSIST(rbtdb->cachestat.ptr_current == 0); + INSIST(rbtdb->cachestat.glue_current == 0); + INSIST(rbtdb->cachestat.additional_current == 0); + } +#endif + + RBTDB_DESTROYLOCK(&rbtdb->lock); + rbtdb->common.magic = 0; + rbtdb->common.impmagic = 0; + ondest = rbtdb->common.ondest; + isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb)); + isc_ondestroy_notify(&ondest, rbtdb); } static inline void maybe_free_rbtdb(dns_rbtdb_t *rbtdb) { - isc_boolean_t want_free = ISC_FALSE; - unsigned int i; - unsigned int inactive = 0; - - /* XXX check for open versions here */ - - if (rbtdb->soanode != NULL) - dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->soanode); - if (rbtdb->nsnode != NULL) - dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->nsnode); - - /* - * Even though there are no external direct references, there still - * may be nodes in use. - */ - for (i = 0; i < rbtdb->node_lock_count; i++) { - NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write); - rbtdb->node_locks[i].exiting = ISC_TRUE; - NODE_UNLOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write); - if (isc_refcount_current(&rbtdb->node_locks[i].references) - == 0) { - inactive++; - } - } - - if (inactive != 0) { - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - rbtdb->active -= inactive; - if (rbtdb->active == 0) - want_free = ISC_TRUE; - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); - if (want_free) { - char buf[DNS_NAME_FORMATSIZE]; - if (dns_name_dynamic(&rbtdb->common.origin)) - dns_name_format(&rbtdb->common.origin, buf, - sizeof(buf)); - else - strcpy(buf, "<UNKNOWN>"); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "calling free_rbtdb(%s)", buf); - free_rbtdb(rbtdb, ISC_TRUE, NULL); - } - } + isc_boolean_t want_free = ISC_FALSE; + unsigned int i; + unsigned int inactive = 0; + + /* XXX check for open versions here */ + + if (rbtdb->soanode != NULL) + dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->soanode); + if (rbtdb->nsnode != NULL) + dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->nsnode); + + /* + * Even though there are no external direct references, there still + * may be nodes in use. + */ + for (i = 0; i < rbtdb->node_lock_count; i++) { + NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write); + rbtdb->node_locks[i].exiting = ISC_TRUE; + NODE_UNLOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write); + if (isc_refcount_current(&rbtdb->node_locks[i].references) + == 0) { + inactive++; + } + } + + if (inactive != 0) { + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + rbtdb->active -= inactive; + if (rbtdb->active == 0) + want_free = ISC_TRUE; + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + if (want_free) { + char buf[DNS_NAME_FORMATSIZE]; + if (dns_name_dynamic(&rbtdb->common.origin)) + dns_name_format(&rbtdb->common.origin, buf, + sizeof(buf)); + else + strcpy(buf, "<UNKNOWN>"); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), + "calling free_rbtdb(%s)", buf); + free_rbtdb(rbtdb, ISC_TRUE, NULL); + } + } } static void detach(dns_db_t **dbp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(*dbp); - unsigned int refs; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(*dbp); + unsigned int refs; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - isc_refcount_decrement(&rbtdb->references, &refs); + isc_refcount_decrement(&rbtdb->references, &refs); - if (refs == 0) - maybe_free_rbtdb(rbtdb); + if (refs == 0) + maybe_free_rbtdb(rbtdb); - *dbp = NULL; + *dbp = NULL; } static void currentversion(dns_db_t *db, dns_dbversion_t **versionp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - rbtdb_version_t *version; - unsigned int refs; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + rbtdb_version_t *version; + unsigned int refs; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); - version = rbtdb->current_version; - isc_refcount_increment(&version->references, &refs); - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); + version = rbtdb->current_version; + isc_refcount_increment(&version->references, &refs); + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); - *versionp = (dns_dbversion_t *)version; + *versionp = (dns_dbversion_t *)version; } static inline rbtdb_version_t * allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial, - unsigned int references, isc_boolean_t writer) + unsigned int references, isc_boolean_t writer) { - isc_result_t result; - rbtdb_version_t *version; - - version = isc_mem_get(mctx, sizeof(*version)); - if (version == NULL) - return (NULL); - version->serial = serial; - result = isc_refcount_init(&version->references, references); - if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, version, sizeof(*version)); - return (NULL); - } - version->writer = writer; - version->commit_ok = ISC_FALSE; - ISC_LIST_INIT(version->changed_list); - ISC_LINK_INIT(version, link); - - return (version); + isc_result_t result; + rbtdb_version_t *version; + + version = isc_mem_get(mctx, sizeof(*version)); + if (version == NULL) + return (NULL); + version->serial = serial; + result = isc_refcount_init(&version->references, references); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, version, sizeof(*version)); + return (NULL); + } + version->writer = writer; + version->commit_ok = ISC_FALSE; + ISC_LIST_INIT(version->changed_list); + ISC_LINK_INIT(version, link); + + return (version); } static isc_result_t newversion(dns_db_t *db, dns_dbversion_t **versionp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - rbtdb_version_t *version; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(versionp != NULL && *versionp == NULL); - REQUIRE(rbtdb->future_version == NULL); - - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - RUNTIME_CHECK(rbtdb->next_serial != 0); /* XXX Error? */ - version = allocate_version(rbtdb->common.mctx, rbtdb->next_serial, 1, - ISC_TRUE); - if (version != NULL) { - version->commit_ok = ISC_TRUE; - rbtdb->next_serial++; - rbtdb->future_version = version; - } - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); - - if (version == NULL) - return (ISC_R_NOMEMORY); - - *versionp = version; - - return (ISC_R_SUCCESS); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + rbtdb_version_t *version; + + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(versionp != NULL && *versionp == NULL); + REQUIRE(rbtdb->future_version == NULL); + + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + RUNTIME_CHECK(rbtdb->next_serial != 0); /* XXX Error? */ + version = allocate_version(rbtdb->common.mctx, rbtdb->next_serial, 1, + ISC_TRUE); + if (version != NULL) { + version->commit_ok = ISC_TRUE; + rbtdb->next_serial++; + rbtdb->future_version = version; + } + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + + if (version == NULL) + return (ISC_R_NOMEMORY); + + *versionp = version; + + return (ISC_R_SUCCESS); } static void attachversion(dns_db_t *db, dns_dbversion_t *source, - dns_dbversion_t **targetp) + dns_dbversion_t **targetp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - rbtdb_version_t *rbtversion = source; - unsigned int refs; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + rbtdb_version_t *rbtversion = source; + unsigned int refs; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - isc_refcount_increment(&rbtversion->references, &refs); - INSIST(refs > 1); + isc_refcount_increment(&rbtversion->references, &refs); + INSIST(refs > 1); - *targetp = rbtversion; + *targetp = rbtversion; } static rbtdb_changed_t * add_changed(dns_rbtdb_t *rbtdb, rbtdb_version_t *version, - dns_rbtnode_t *node) + dns_rbtnode_t *node) { - rbtdb_changed_t *changed; - unsigned int refs; + rbtdb_changed_t *changed; + unsigned int refs; - /* - * Caller must be holding the node lock if its reference must be - * protected by the lock. - */ + /* + * Caller must be holding the node lock if its reference must be + * protected by the lock. + */ - changed = isc_mem_get(rbtdb->common.mctx, sizeof(*changed)); + changed = isc_mem_get(rbtdb->common.mctx, sizeof(*changed)); - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - REQUIRE(version->writer); + REQUIRE(version->writer); - if (changed != NULL) { - dns_rbtnode_refincrement(node, &refs); - INSIST(refs != 0); - changed->node = node; - changed->dirty = ISC_FALSE; - ISC_LIST_INITANDAPPEND(version->changed_list, changed, link); - } else - version->commit_ok = ISC_FALSE; + if (changed != NULL) { + dns_rbtnode_refincrement(node, &refs); + INSIST(refs != 0); + changed->node = node; + changed->dirty = ISC_FALSE; + ISC_LIST_INITANDAPPEND(version->changed_list, changed, link); + } else + version->commit_ok = ISC_FALSE; - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); - return (changed); + return (changed); } static void free_acachearray(isc_mem_t *mctx, rdatasetheader_t *header, - acachectl_t *array) + acachectl_t *array) { - unsigned int count; - unsigned int i; - unsigned char *raw; /* RDATASLAB */ + unsigned int count; + unsigned int i; + unsigned char *raw; /* RDATASLAB */ - /* - * The caller must be holding the corresponding node lock. - */ + /* + * The caller must be holding the corresponding node lock. + */ - if (array == NULL) - return; + if (array == NULL) + return; - raw = (unsigned char *)header + sizeof(*header); - count = raw[0] * 256 + raw[1]; + raw = (unsigned char *)header + sizeof(*header); + count = raw[0] * 256 + raw[1]; - /* - * Sanity check: since an additional cache entry has a reference to - * the original DB node (in the callback arg), there should be no - * acache entries when the node can be freed. - */ - for (i = 0; i < count; i++) - INSIST(array[i].entry == NULL && array[i].cbarg == NULL); + /* + * Sanity check: since an additional cache entry has a reference to + * the original DB node (in the callback arg), there should be no + * acache entries when the node can be freed. + */ + for (i = 0; i < count; i++) + INSIST(array[i].entry == NULL && array[i].cbarg == NULL); - isc_mem_put(mctx, array, count * sizeof(acachectl_t)); + isc_mem_put(mctx, array, count * sizeof(acachectl_t)); } static inline void free_noqname(isc_mem_t *mctx, struct noqname **noqname) { - if (dns_name_dynamic(&(*noqname)->name)) - dns_name_free(&(*noqname)->name, mctx); - if ((*noqname)->nsec != NULL) - isc_mem_put(mctx, (*noqname)->nsec, - dns_rdataslab_size((*noqname)->nsec, 0)); - if ((*noqname)->nsecsig != NULL) - isc_mem_put(mctx, (*noqname)->nsecsig, - dns_rdataslab_size((*noqname)->nsecsig, 0)); - isc_mem_put(mctx, *noqname, sizeof(**noqname)); - *noqname = NULL; + if (dns_name_dynamic(&(*noqname)->name)) + dns_name_free(&(*noqname)->name, mctx); + if ((*noqname)->nsec != NULL) + isc_mem_put(mctx, (*noqname)->nsec, + dns_rdataslab_size((*noqname)->nsec, 0)); + if ((*noqname)->nsecsig != NULL) + isc_mem_put(mctx, (*noqname)->nsecsig, + dns_rdataslab_size((*noqname)->nsecsig, 0)); + isc_mem_put(mctx, *noqname, sizeof(**noqname)); + *noqname = NULL; } static inline void -free_rdataset(isc_mem_t *mctx, rdatasetheader_t *rdataset) { - unsigned int size; +init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h) +{ + ISC_LINK_INIT(h, lru_link); + h->heap_index = 0; + +#if TRACE_HEADER + if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in) + fprintf(stderr, "initialized header: %p\n", h); +#else + UNUSED(rbtdb); +#endif +} - if (rdataset->noqname != NULL) - free_noqname(mctx, &rdataset->noqname); +static inline rdatasetheader_t * +new_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx) +{ + rdatasetheader_t *h; - free_acachearray(mctx, rdataset, rdataset->additional_auth); - free_acachearray(mctx, rdataset, rdataset->additional_glue); + h = isc_mem_get(mctx, sizeof(*h)); + if (h == NULL) + return (NULL); - if ((rdataset->attributes & RDATASET_ATTR_NONEXISTENT) != 0) - size = sizeof(*rdataset); - else - size = dns_rdataslab_size((unsigned char *)rdataset, - sizeof(*rdataset)); - isc_mem_put(mctx, rdataset, size); +#if TRACE_HEADER + if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in) + fprintf(stderr, "allocated header: %p\n", h); +#endif + init_rdataset(rbtdb, h); + return (h); +} + +static inline void +free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset) +{ + unsigned int size; + +#ifdef LRU_DEBUG + /* + * for debug: statistics update. + * Nothing in this block should have any side-effects. + */ + if (EXISTS(rdataset) && + (rdataset->attributes & RDATASET_ATTR_CACHE) != 0) { + rbtdb->cachestat.cache_current--; + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) != 0) + rbtdb->cachestat.cache_total--; + if (RBTDB_RDATATYPE_BASE(rdataset->type) == 0) { + rbtdb->cachestat.ncache_current--; + INSIST(rbtdb->cachestat.ncache_current >= 0); + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) + != 0) + rbtdb->cachestat.ncache_total--; + } + if (rdataset->type == dns_rdatatype_a) { + rbtdb->cachestat.a_current--; + INSIST(rbtdb->cachestat.a_current >= 0); + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) + != 0) + rbtdb->cachestat.a_total--; + } else if (rdataset->type == dns_rdatatype_aaaa) { + rbtdb->cachestat.aaaa_current--; + INSIST(rbtdb->cachestat.aaaa_current >= 0); + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) + != 0) + rbtdb->cachestat.aaaa_total--; + } else if (rdataset->type == dns_rdatatype_ptr) { + rbtdb->cachestat.ptr_current--; + INSIST(rbtdb->cachestat.ptr_current >= 0); + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) + != 0) + rbtdb->cachestat.ptr_total--; + } else if (rdataset->type == dns_rdatatype_ns) { + rbtdb->cachestat.ns_current--; + INSIST(rbtdb->cachestat.ns_current >= 0); + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) + != 0) + rbtdb->cachestat.ns_total--; + } + if (rdataset->trust == dns_trust_glue && + (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa)) { + rbtdb->cachestat.glue_current--; + INSIST(rbtdb->cachestat.glue_current >= 0); + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) + != 0) + rbtdb->cachestat.glue_total--; + } + if (rdataset->trust == dns_trust_additional && + (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa)) { + rbtdb->cachestat.additional_current--; + INSIST(rbtdb->cachestat.additional_current >= 0); + if ((rdataset->attributes & RDATASET_ATTR_CANCELED) + != 0) + rbtdb->cachestat.additional_total--; + } + } +#endif + + if (IS_CACHE(rbtdb) && ISC_LINK_LINKED(rdataset, lru_link)) { + int idx = rdataset->node->locknum; + ISC_LIST_UNLINK(rbtdb->rdatasets[idx], rdataset, lru_link); + if (rdataset->heap_index != 0) { + isc_heap_delete(rbtdb->heaps[idx], + rdataset->heap_index); + } + rdataset->heap_index = 0; + } + + if (rdataset->noqname != NULL) + free_noqname(mctx, &rdataset->noqname); + + free_acachearray(mctx, rdataset, rdataset->additional_auth); + free_acachearray(mctx, rdataset, rdataset->additional_glue); + + if ((rdataset->attributes & RDATASET_ATTR_NONEXISTENT) != 0) + size = sizeof(*rdataset); + else + size = dns_rdataslab_size((unsigned char *)rdataset, + sizeof(*rdataset)); + isc_mem_put(mctx, rdataset, size); } static inline void rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) { - rdatasetheader_t *header, *dcurrent; - isc_boolean_t make_dirty = ISC_FALSE; - - /* - * Caller must hold the node lock. - */ - - /* - * We set the IGNORE attribute on rdatasets with serial number - * 'serial'. When the reference count goes to zero, these rdatasets - * will be cleaned up; until that time, they will be ignored. - */ - for (header = node->data; header != NULL; header = header->next) { - if (header->serial == serial) { - header->attributes |= RDATASET_ATTR_IGNORE; - make_dirty = ISC_TRUE; - } - for (dcurrent = header->down; - dcurrent != NULL; - dcurrent = dcurrent->down) { - if (dcurrent->serial == serial) { - dcurrent->attributes |= RDATASET_ATTR_IGNORE; - make_dirty = ISC_TRUE; - } - } - } - if (make_dirty) - node->dirty = 1; + rdatasetheader_t *header, *dcurrent; + isc_boolean_t make_dirty = ISC_FALSE; + + /* + * Caller must hold the node lock. + */ + + /* + * We set the IGNORE attribute on rdatasets with serial number + * 'serial'. When the reference count goes to zero, these rdatasets + * will be cleaned up; until that time, they will be ignored. + */ + for (header = node->data; header != NULL; header = header->next) { + if (header->serial == serial) { + header->attributes |= RDATASET_ATTR_IGNORE; + make_dirty = ISC_TRUE; + } + for (dcurrent = header->down; + dcurrent != NULL; + dcurrent = dcurrent->down) { + if (dcurrent->serial == serial) { + dcurrent->attributes |= RDATASET_ATTR_IGNORE; + make_dirty = ISC_TRUE; + } + } + } + if (make_dirty) + node->dirty = 1; } static inline void -clean_stale_headers(isc_mem_t *mctx, rdatasetheader_t *top) { - rdatasetheader_t *d, *down_next; - - for (d = top->down; d != NULL; d = down_next) { - down_next = d->down; - free_rdataset(mctx, d); - } - top->down = NULL; +clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *top) +{ + rdatasetheader_t *d, *down_next; + + for (d = top->down; d != NULL; d = down_next) { + down_next = d->down; + free_rdataset(rbtdb, mctx, d); + } + top->down = NULL; } static inline void clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { - rdatasetheader_t *current, *top_prev, *top_next; - isc_mem_t *mctx = rbtdb->common.mctx; - - /* - * Caller must be holding the node lock. - */ - - top_prev = NULL; - for (current = node->data; current != NULL; current = top_next) { - top_next = current->next; - clean_stale_headers(mctx, current); - /* - * If current is nonexistent or stale, we can clean it up. - */ - if ((current->attributes & - (RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0) { - if (top_prev != NULL) - top_prev->next = current->next; - else - node->data = current->next; - free_rdataset(mctx, current); - } else - top_prev = current; - } - node->dirty = 0; + rdatasetheader_t *current, *top_prev, *top_next; + isc_mem_t *mctx = rbtdb->common.mctx; + + /* + * Caller must be holding the node lock. + */ + + top_prev = NULL; + for (current = node->data; current != NULL; current = top_next) { + top_next = current->next; + clean_stale_headers(rbtdb, mctx, current); + /* + * If current is nonexistent or stale, we can clean it up. + */ + if ((current->attributes & + (RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0) { + if (top_prev != NULL) + top_prev->next = current->next; + else + node->data = current->next; + free_rdataset(rbtdb, mctx, current); + } else + top_prev = current; + } + node->dirty = 0; } static inline void clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - rbtdb_serial_t least_serial) + rbtdb_serial_t least_serial) { - rdatasetheader_t *current, *dcurrent, *down_next, *dparent; - rdatasetheader_t *top_prev, *top_next; - isc_mem_t *mctx = rbtdb->common.mctx; - isc_boolean_t still_dirty = ISC_FALSE; - - /* - * Caller must be holding the node lock. - */ - REQUIRE(least_serial != 0); - - top_prev = NULL; - for (current = node->data; current != NULL; current = top_next) { - top_next = current->next; - - /* - * First, we clean up any instances of multiple rdatasets - * with the same serial number, or that have the IGNORE - * attribute. - */ - dparent = current; - for (dcurrent = current->down; - dcurrent != NULL; - dcurrent = down_next) { - down_next = dcurrent->down; - INSIST(dcurrent->serial <= dparent->serial); - if (dcurrent->serial == dparent->serial || - IGNORE(dcurrent)) { - if (down_next != NULL) - down_next->next = dparent; - dparent->down = down_next; - free_rdataset(mctx, dcurrent); - } else - dparent = dcurrent; - } - - /* - * We've now eliminated all IGNORE datasets with the possible - * exception of current, which we now check. - */ - if (IGNORE(current)) { - down_next = current->down; - if (down_next == NULL) { - if (top_prev != NULL) - top_prev->next = current->next; - else - node->data = current->next; - free_rdataset(mctx, current); - /* - * current no longer exists, so we can - * just continue with the loop. - */ - continue; - } else { - /* - * Pull up current->down, making it the new - * current. - */ - if (top_prev != NULL) - top_prev->next = down_next; - else - node->data = down_next; - down_next->next = top_next; - free_rdataset(mctx, current); - current = down_next; - } - } - - /* - * We now try to find the first down node less than the - * least serial. - */ - dparent = current; - for (dcurrent = current->down; - dcurrent != NULL; - dcurrent = down_next) { - down_next = dcurrent->down; - if (dcurrent->serial < least_serial) - break; - dparent = dcurrent; - } - - /* - * If there is a such an rdataset, delete it and any older - * versions. - */ - if (dcurrent != NULL) { - do { - down_next = dcurrent->down; - INSIST(dcurrent->serial <= least_serial); - free_rdataset(mctx, dcurrent); - dcurrent = down_next; - } while (dcurrent != NULL); - dparent->down = NULL; - } - - /* - * Note. The serial number of 'current' might be less than - * least_serial too, but we cannot delete it because it is - * the most recent version, unless it is a NONEXISTENT - * rdataset. - */ - if (current->down != NULL) { - still_dirty = ISC_TRUE; - top_prev = current; - } else { - /* - * If this is a NONEXISTENT rdataset, we can delete it. - */ - if (NONEXISTENT(current)) { - if (top_prev != NULL) - top_prev->next = current->next; - else - node->data = current->next; - free_rdataset(mctx, current); - } else - top_prev = current; - } - } - if (!still_dirty) - node->dirty = 0; + rdatasetheader_t *current, *dcurrent, *down_next, *dparent; + rdatasetheader_t *top_prev, *top_next; + isc_mem_t *mctx = rbtdb->common.mctx; + isc_boolean_t still_dirty = ISC_FALSE; + + /* + * Caller must be holding the node lock. + */ + REQUIRE(least_serial != 0); + + top_prev = NULL; + for (current = node->data; current != NULL; current = top_next) { + top_next = current->next; + + /* + * First, we clean up any instances of multiple rdatasets + * with the same serial number, or that have the IGNORE + * attribute. + */ + dparent = current; + for (dcurrent = current->down; + dcurrent != NULL; + dcurrent = down_next) { + down_next = dcurrent->down; + INSIST(dcurrent->serial <= dparent->serial); + if (dcurrent->serial == dparent->serial || + IGNORE(dcurrent)) { + if (down_next != NULL) + down_next->next = dparent; + dparent->down = down_next; + free_rdataset(rbtdb, mctx, dcurrent); + } else + dparent = dcurrent; + } + + /* + * We've now eliminated all IGNORE datasets with the possible + * exception of current, which we now check. + */ + if (IGNORE(current)) { + down_next = current->down; + if (down_next == NULL) { + if (top_prev != NULL) + top_prev->next = current->next; + else + node->data = current->next; + free_rdataset(rbtdb, mctx, current); + /* + * current no longer exists, so we can + * just continue with the loop. + */ + continue; + } else { + /* + * Pull up current->down, making it the new + * current. + */ + if (top_prev != NULL) + top_prev->next = down_next; + else + node->data = down_next; + down_next->next = top_next; + free_rdataset(rbtdb, mctx, current); + current = down_next; + } + } + + /* + * We now try to find the first down node less than the + * least serial. + */ + dparent = current; + for (dcurrent = current->down; + dcurrent != NULL; + dcurrent = down_next) { + down_next = dcurrent->down; + if (dcurrent->serial < least_serial) + break; + dparent = dcurrent; + } + + /* + * If there is a such an rdataset, delete it and any older + * versions. + */ + if (dcurrent != NULL) { + do { + down_next = dcurrent->down; + INSIST(dcurrent->serial <= least_serial); + free_rdataset(rbtdb, mctx, dcurrent); + dcurrent = down_next; + } while (dcurrent != NULL); + dparent->down = NULL; + } + + /* + * Note. The serial number of 'current' might be less than + * least_serial too, but we cannot delete it because it is + * the most recent version, unless it is a NONEXISTENT + * rdataset. + */ + if (current->down != NULL) { + still_dirty = ISC_TRUE; + top_prev = current; + } else { + /* + * If this is a NONEXISTENT rdataset, we can delete it. + */ + if (NONEXISTENT(current)) { + if (top_prev != NULL) + top_prev->next = current->next; + else + node->data = current->next; + free_rdataset(rbtdb, mctx, current); + } else + top_prev = current; + } + } + if (!still_dirty) + node->dirty = 0; +} + +/*% + * Clean up dead nodes. These are nodes which have no references, and + * have no data. They are dead but we could not or chose not to delete + * them when we deleted all the data at that node because we did not want + * to wait for the tree write lock. + * + * The caller must hold a tree write lock and bucketnum'th node (write) lock. + */ +static void +cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) { + dns_rbtnode_t *node; + isc_result_t result; + int count = 10; /* XXXJT: should be adjustable */ + + node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]); + while (node != NULL && count > 0) { + ISC_LIST_UNLINK(rbtdb->deadnodes[bucketnum], node, deadlink); + + /* + * Since we're holding a tree write lock, it should be + * impossible for this node to be referenced by others. + */ + INSIST(dns_rbtnode_refcurrent(node) == 0 && + node->data == NULL); + + result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); + if (result != ISC_R_SUCCESS) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, + "cleanup_dead_nodes: " + "dns_rbt_deletenode: %s", + isc_result_totext(result)); + node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]); + count--; + } } /* @@ -1135,16 +1492,16 @@ clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, */ static inline void new_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { - unsigned int lockrefs, noderefs; - isc_refcount_t *lockref; - - dns_rbtnode_refincrement0(node, &noderefs); - if (noderefs == 1) { /* this is the first reference to the node */ - lockref = &rbtdb->node_locks[node->locknum].references; - isc_refcount_increment0(lockref, &lockrefs); - INSIST(lockrefs != 0); - } - INSIST(noderefs != 0); + unsigned int lockrefs, noderefs; + isc_refcount_t *lockref; + + dns_rbtnode_refincrement0(node, &noderefs); + if (noderefs == 1) { /* this is the first reference to the node */ + lockref = &rbtdb->node_locks[node->locknum].references; + isc_refcount_increment0(lockref, &lockrefs); + INSIST(lockrefs != 0); + } + INSIST(noderefs != 0); } /* @@ -1159,420 +1516,426 @@ new_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { */ static isc_boolean_t decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - rbtdb_serial_t least_serial, - isc_rwlocktype_t nlock, isc_rwlocktype_t tlock) + rbtdb_serial_t least_serial, + isc_rwlocktype_t nlock, isc_rwlocktype_t tlock) { - isc_result_t result; - isc_boolean_t write_locked; - rbtdb_nodelock_t *nodelock; - unsigned int refs, nrefs; - - nodelock = &rbtdb->node_locks[node->locknum]; - - /* Handle easy and typical case first. */ - if (!node->dirty && (node->data != NULL || node->down != NULL)) { - dns_rbtnode_refdecrement(node, &nrefs); - INSIST((int)nrefs >= 0); - if (nrefs == 0) { - isc_refcount_decrement(&nodelock->references, &refs); - INSIST((int)refs >= 0); - } - return ((nrefs == 0) ? ISC_TRUE : ISC_FALSE); - } - - /* Upgrade the lock? */ - if (nlock == isc_rwlocktype_read) { - NODE_WEAKUNLOCK(&nodelock->lock, isc_rwlocktype_read); - NODE_WEAKLOCK(&nodelock->lock, isc_rwlocktype_write); - } - dns_rbtnode_refdecrement(node, &nrefs); - INSIST((int)nrefs >= 0); - if (nrefs > 0) { - /* Restore the lock? */ - if (nlock == isc_rwlocktype_read) - NODE_WEAKDOWNGRADE(&nodelock->lock); - return (ISC_FALSE); - } - - if (node->dirty && dns_rbtnode_refcurrent(node) == 0) { - if (IS_CACHE(rbtdb)) - clean_cache_node(rbtdb, node); - else { - if (least_serial == 0) { - /* - * Caller doesn't know the least serial. - * Get it. - */ - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); - least_serial = rbtdb->least_serial; - RBTDB_UNLOCK(&rbtdb->lock, - isc_rwlocktype_read); - } - clean_zone_node(rbtdb, node, least_serial); - } - } - - isc_refcount_decrement(&nodelock->references, &refs); - INSIST((int)refs >= 0); - - /* - * XXXDCL should this only be done for cache zones? - */ - if (node->data != NULL || node->down != NULL) { - /* Restore the lock? */ - if (nlock == isc_rwlocktype_read) - NODE_WEAKDOWNGRADE(&nodelock->lock); - return (ISC_TRUE); - } - - /* - * XXXDCL need to add a deferred delete method for ISC_R_LOCKBUSY. - */ - if (tlock != isc_rwlocktype_write) { - /* - * Locking hierarchy notwithstanding, we don't need to free - * the node lock before acquiring the tree write lock because - * we only do a trylock. - */ - if (tlock == isc_rwlocktype_read) - result = isc_rwlock_tryupgrade(&rbtdb->tree_lock); - else - result = isc_rwlock_trylock(&rbtdb->tree_lock, - isc_rwlocktype_write); - RUNTIME_CHECK(result == ISC_R_SUCCESS || - result == ISC_R_LOCKBUSY); - - write_locked = ISC_TF(result == ISC_R_SUCCESS); - } else - write_locked = ISC_TRUE; - - if (write_locked && dns_rbtnode_refcurrent(node) == 0) { - /* - * We can now delete the node if the reference counter is - * zero. This should be typically the case, but a different - * thread may still gain a (new) reference just before the - * current thread locks the tree (e.g., in findnode()). - */ - - if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) { - char printname[DNS_NAME_FORMATSIZE]; - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "decrement_reference: " - "delete from rbt: %p %s", - node, - dns_rbt_formatnodename(node, printname, - sizeof(printname))); - } - - result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); - if (result != ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "decrement_reference: " - "dns_rbt_deletenode: %s", - isc_result_totext(result)); - } - - /* Restore the lock? */ - if (nlock == isc_rwlocktype_read) - NODE_WEAKDOWNGRADE(&nodelock->lock); - - /* - * Relock a read lock, or unlock the write lock if no lock was held. - */ - if (tlock == isc_rwlocktype_none) - if (write_locked) - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - - if (tlock == isc_rwlocktype_read) - if (write_locked) - isc_rwlock_downgrade(&rbtdb->tree_lock); - - return (ISC_TRUE); + isc_result_t result; + isc_boolean_t write_locked; + rbtdb_nodelock_t *nodelock; + unsigned int refs, nrefs; + int bucket = node->locknum; + + nodelock = &rbtdb->node_locks[bucket]; + + /* Handle easy and typical case first. */ + if (!node->dirty && (node->data != NULL || node->down != NULL)) { + dns_rbtnode_refdecrement(node, &nrefs); + INSIST((int)nrefs >= 0); + if (nrefs == 0) { + isc_refcount_decrement(&nodelock->references, &refs); + INSIST((int)refs >= 0); + } + return ((nrefs == 0) ? ISC_TRUE : ISC_FALSE); + } + + /* Upgrade the lock? */ + if (nlock == isc_rwlocktype_read) { + NODE_WEAKUNLOCK(&nodelock->lock, isc_rwlocktype_read); + NODE_WEAKLOCK(&nodelock->lock, isc_rwlocktype_write); + } + dns_rbtnode_refdecrement(node, &nrefs); + INSIST((int)nrefs >= 0); + if (nrefs > 0) { + /* Restore the lock? */ + if (nlock == isc_rwlocktype_read) + NODE_WEAKDOWNGRADE(&nodelock->lock); + return (ISC_FALSE); + } + + if (node->dirty && dns_rbtnode_refcurrent(node) == 0) { + if (IS_CACHE(rbtdb)) + clean_cache_node(rbtdb, node); + else { + if (least_serial == 0) { + /* + * Caller doesn't know the least serial. + * Get it. + */ + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); + least_serial = rbtdb->least_serial; + RBTDB_UNLOCK(&rbtdb->lock, + isc_rwlocktype_read); + } + clean_zone_node(rbtdb, node, least_serial); + } + } + + isc_refcount_decrement(&nodelock->references, &refs); + INSIST((int)refs >= 0); + + /* + * XXXDCL should this only be done for cache zones? + */ + if (node->data != NULL || node->down != NULL) { + /* Restore the lock? */ + if (nlock == isc_rwlocktype_read) + NODE_WEAKDOWNGRADE(&nodelock->lock); + return (ISC_TRUE); + } + + /* + * Attempt to switch to a write lock on the tree. If this fails, + * we will add this node to a linked list of nodes in this locking + * bucket which we will free later. + */ + if (tlock != isc_rwlocktype_write) { + /* + * Locking hierarchy notwithstanding, we don't need to free + * the node lock before acquiring the tree write lock because + * we only do a trylock. + */ + if (tlock == isc_rwlocktype_read) + result = isc_rwlock_tryupgrade(&rbtdb->tree_lock); + else + result = isc_rwlock_trylock(&rbtdb->tree_lock, + isc_rwlocktype_write); + RUNTIME_CHECK(result == ISC_R_SUCCESS || + result == ISC_R_LOCKBUSY); + + write_locked = ISC_TF(result == ISC_R_SUCCESS); + } else + write_locked = ISC_TRUE; + + if (write_locked && dns_rbtnode_refcurrent(node) == 0) { + /* + * We can now delete the node if the reference counter is + * zero. This should be typically the case, but a different + * thread may still gain a (new) reference just before the + * current thread locks the tree (e.g., in findnode()). + */ + + if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) { + char printname[DNS_NAME_FORMATSIZE]; + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), + "decrement_reference: " + "delete from rbt: %p %s", + node, + dns_rbt_formatnodename(node, printname, + sizeof(printname))); + } + + result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); + if (result != ISC_R_SUCCESS) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, + "decrement_reference: " + "dns_rbt_deletenode: %s", + isc_result_totext(result)); + } else if (dns_rbtnode_refcurrent(node) == 0) { + INSIST(!ISC_LINK_LINKED(node, deadlink)); + ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node, deadlink); + } + + /* Restore the lock? */ + if (nlock == isc_rwlocktype_read) + NODE_WEAKDOWNGRADE(&nodelock->lock); + + /* + * Relock a read lock, or unlock the write lock if no lock was held. + */ + if (tlock == isc_rwlocktype_none) + if (write_locked) + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); + + if (tlock == isc_rwlocktype_read) + if (write_locked) + isc_rwlock_downgrade(&rbtdb->tree_lock); + + return (ISC_TRUE); } static inline void make_least_version(dns_rbtdb_t *rbtdb, rbtdb_version_t *version, - rbtdb_changedlist_t *cleanup_list) + rbtdb_changedlist_t *cleanup_list) { - /* - * Caller must be holding the database lock. - */ + /* + * Caller must be holding the database lock. + */ - rbtdb->least_serial = version->serial; - *cleanup_list = version->changed_list; - ISC_LIST_INIT(version->changed_list); + rbtdb->least_serial = version->serial; + *cleanup_list = version->changed_list; + ISC_LIST_INIT(version->changed_list); } static inline void cleanup_nondirty(rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list) { - rbtdb_changed_t *changed, *next_changed; - - /* - * If the changed record is dirty, then - * an update created multiple versions of - * a given rdataset. We keep this list - * until we're the least open version, at - * which point it's safe to get rid of any - * older versions. - * - * If the changed record isn't dirty, then - * we don't need it anymore since we're - * committing and not rolling back. - * - * The caller must be holding the database lock. - */ - for (changed = HEAD(version->changed_list); - changed != NULL; - changed = next_changed) { - next_changed = NEXT(changed, link); - if (!changed->dirty) { - UNLINK(version->changed_list, - changed, link); - APPEND(*cleanup_list, - changed, link); - } - } + rbtdb_changed_t *changed, *next_changed; + + /* + * If the changed record is dirty, then + * an update created multiple versions of + * a given rdataset. We keep this list + * until we're the least open version, at + * which point it's safe to get rid of any + * older versions. + * + * If the changed record isn't dirty, then + * we don't need it anymore since we're + * committing and not rolling back. + * + * The caller must be holding the database lock. + */ + for (changed = HEAD(version->changed_list); + changed != NULL; + changed = next_changed) { + next_changed = NEXT(changed, link); + if (!changed->dirty) { + UNLINK(version->changed_list, + changed, link); + APPEND(*cleanup_list, + changed, link); + } + } } static isc_boolean_t iszonesecure(dns_db_t *db, dns_dbnode_t *origin) { - dns_rdataset_t keyset; - dns_rdataset_t nsecset, signsecset; - isc_boolean_t haszonekey = ISC_FALSE; - isc_boolean_t hasnsec = ISC_FALSE; - isc_result_t result; - - dns_rdataset_init(&keyset); - result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_dnskey, 0, - 0, &keyset, NULL); - if (result == ISC_R_SUCCESS) { - dns_rdata_t keyrdata = DNS_RDATA_INIT; - result = dns_rdataset_first(&keyset); - while (result == ISC_R_SUCCESS) { - dns_rdataset_current(&keyset, &keyrdata); - if (dns_zonekey_iszonekey(&keyrdata)) { - haszonekey = ISC_TRUE; - break; - } - result = dns_rdataset_next(&keyset); - } - dns_rdataset_disassociate(&keyset); - } - if (!haszonekey) - return (ISC_FALSE); - - dns_rdataset_init(&nsecset); - dns_rdataset_init(&signsecset); - result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_nsec, 0, - 0, &nsecset, &signsecset); - if (result == ISC_R_SUCCESS) { - if (dns_rdataset_isassociated(&signsecset)) { - hasnsec = ISC_TRUE; - dns_rdataset_disassociate(&signsecset); - } - dns_rdataset_disassociate(&nsecset); - } - return (hasnsec); + dns_rdataset_t keyset; + dns_rdataset_t nsecset, signsecset; + isc_boolean_t haszonekey = ISC_FALSE; + isc_boolean_t hasnsec = ISC_FALSE; + isc_result_t result; + + dns_rdataset_init(&keyset); + result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_dnskey, 0, + 0, &keyset, NULL); + if (result == ISC_R_SUCCESS) { + dns_rdata_t keyrdata = DNS_RDATA_INIT; + result = dns_rdataset_first(&keyset); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(&keyset, &keyrdata); + if (dns_zonekey_iszonekey(&keyrdata)) { + haszonekey = ISC_TRUE; + break; + } + result = dns_rdataset_next(&keyset); + } + dns_rdataset_disassociate(&keyset); + } + if (!haszonekey) + return (ISC_FALSE); + + dns_rdataset_init(&nsecset); + dns_rdataset_init(&signsecset); + result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_nsec, 0, + 0, &nsecset, &signsecset); + if (result == ISC_R_SUCCESS) { + if (dns_rdataset_isassociated(&signsecset)) { + hasnsec = ISC_TRUE; + dns_rdataset_disassociate(&signsecset); + } + dns_rdataset_disassociate(&nsecset); + } + return (hasnsec); } static void closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - rbtdb_version_t *version, *cleanup_version, *least_greater; - isc_boolean_t rollback = ISC_FALSE; - rbtdb_changedlist_t cleanup_list; - rbtdb_changed_t *changed, *next_changed; - rbtdb_serial_t serial, least_serial; - dns_rbtnode_t *rbtnode; - unsigned int refs; - - REQUIRE(VALID_RBTDB(rbtdb)); - version = (rbtdb_version_t *)*versionp; - - cleanup_version = NULL; - ISC_LIST_INIT(cleanup_list); - - isc_refcount_decrement(&version->references, &refs); - if (refs > 0) { /* typical and easy case first */ - if (commit) { - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); - INSIST(!version->writer); - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); - } - goto end; - } - - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - serial = version->serial; - if (version->writer) { - if (commit) { - unsigned cur_ref; - rbtdb_version_t *cur_version; - - INSIST(version->commit_ok); - INSIST(version == rbtdb->future_version); - /* - * The current version is going to be replaced. - * Release the (likely last) reference to it from the - * DB itself and unlink it from the open list. - */ - cur_version = rbtdb->current_version; - isc_refcount_decrement(&cur_version->references, - &cur_ref); - if (cur_ref == 0) { - if (cur_version->serial == rbtdb->least_serial) - INSIST(EMPTY(cur_version->changed_list)); - UNLINK(rbtdb->open_versions, - cur_version, link); - } - if (EMPTY(rbtdb->open_versions)) { - /* - * We're going to become the least open - * version. - */ - make_least_version(rbtdb, version, - &cleanup_list); - } else { - /* - * Some other open version is the - * least version. We can't cleanup - * records that were changed in this - * version because the older versions - * may still be in use by an open - * version. - * - * We can, however, discard the - * changed records for things that - * we've added that didn't exist in - * prior versions. - */ - cleanup_nondirty(version, &cleanup_list); - } - /* - * If the (soon to be former) current version - * isn't being used by anyone, we can clean - * it up. - */ - if (cur_ref == 0) { - cleanup_version = cur_version; - APPENDLIST(version->changed_list, - cleanup_version->changed_list, - link); - } - /* - * Become the current version. - */ - version->writer = ISC_FALSE; - rbtdb->current_version = version; - rbtdb->current_serial = version->serial; - rbtdb->future_version = NULL; - - /* - * Keep the current version in the open list, and - * gain a reference for the DB itself (see the DB - * creation function below). This must be the only - * case where we need to increment the counter from - * zero and need to use isc_refcount_increment0(). - */ - isc_refcount_increment0(&version->references, - &cur_ref); - INSIST(cur_ref == 1); - PREPEND(rbtdb->open_versions, - rbtdb->current_version, link); - } else { - /* - * We're rolling back this transaction. - */ - cleanup_list = version->changed_list; - ISC_LIST_INIT(version->changed_list); - rollback = ISC_TRUE; - cleanup_version = version; - rbtdb->future_version = NULL; - } - } else { - if (version != rbtdb->current_version) { - /* - * There are no external or internal references - * to this version and it can be cleaned up. - */ - cleanup_version = version; - - /* - * Find the version with the least serial - * number greater than ours. - */ - least_greater = PREV(version, link); - if (least_greater == NULL) - least_greater = rbtdb->current_version; - - INSIST(version->serial < least_greater->serial); - /* - * Is this the least open version? - */ - if (version->serial == rbtdb->least_serial) { - /* - * Yes. Install the new least open - * version. - */ - make_least_version(rbtdb, - least_greater, - &cleanup_list); - } else { - /* - * Add any unexecuted cleanups to - * those of the least greater version. - */ - APPENDLIST(least_greater->changed_list, - version->changed_list, - link); - } - } else if (version->serial == rbtdb->least_serial) - INSIST(EMPTY(version->changed_list)); - UNLINK(rbtdb->open_versions, version, link); - } - least_serial = rbtdb->least_serial; - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); - - /* - * Update the zone's secure status. - */ - if (version->writer && commit && !IS_CACHE(rbtdb)) - rbtdb->secure = iszonesecure(db, rbtdb->origin_node); - - if (cleanup_version != NULL) { - INSIST(EMPTY(cleanup_version->changed_list)); - isc_mem_put(rbtdb->common.mctx, cleanup_version, - sizeof(*cleanup_version)); - } - - if (!EMPTY(cleanup_list)) { - for (changed = HEAD(cleanup_list); - changed != NULL; - changed = next_changed) { - nodelock_t *lock; - - next_changed = NEXT(changed, link); - rbtnode = changed->node; - lock = &rbtdb->node_locks[rbtnode->locknum].lock; - - NODE_LOCK(lock, isc_rwlocktype_write); - if (rollback) - rollback_node(rbtnode, serial); - decrement_reference(rbtdb, rbtnode, least_serial, - isc_rwlocktype_write, - isc_rwlocktype_none); - NODE_UNLOCK(lock, isc_rwlocktype_write); - - isc_mem_put(rbtdb->common.mctx, changed, - sizeof(*changed)); - } - } + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + rbtdb_version_t *version, *cleanup_version, *least_greater; + isc_boolean_t rollback = ISC_FALSE; + rbtdb_changedlist_t cleanup_list; + rbtdb_changed_t *changed, *next_changed; + rbtdb_serial_t serial, least_serial; + dns_rbtnode_t *rbtnode; + unsigned int refs; + + REQUIRE(VALID_RBTDB(rbtdb)); + version = (rbtdb_version_t *)*versionp; + + cleanup_version = NULL; + ISC_LIST_INIT(cleanup_list); + + isc_refcount_decrement(&version->references, &refs); + if (refs > 0) { /* typical and easy case first */ + if (commit) { + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); + INSIST(!version->writer); + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); + } + goto end; + } + + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + serial = version->serial; + if (version->writer) { + if (commit) { + unsigned cur_ref; + rbtdb_version_t *cur_version; + + INSIST(version->commit_ok); + INSIST(version == rbtdb->future_version); + /* + * The current version is going to be replaced. + * Release the (likely last) reference to it from the + * DB itself and unlink it from the open list. + */ + cur_version = rbtdb->current_version; + isc_refcount_decrement(&cur_version->references, + &cur_ref); + if (cur_ref == 0) { + if (cur_version->serial == rbtdb->least_serial) + INSIST(EMPTY(cur_version->changed_list)); + UNLINK(rbtdb->open_versions, + cur_version, link); + } + if (EMPTY(rbtdb->open_versions)) { + /* + * We're going to become the least open + * version. + */ + make_least_version(rbtdb, version, + &cleanup_list); + } else { + /* + * Some other open version is the + * least version. We can't cleanup + * records that were changed in this + * version because the older versions + * may still be in use by an open + * version. + * + * We can, however, discard the + * changed records for things that + * we've added that didn't exist in + * prior versions. + */ + cleanup_nondirty(version, &cleanup_list); + } + /* + * If the (soon to be former) current version + * isn't being used by anyone, we can clean + * it up. + */ + if (cur_ref == 0) { + cleanup_version = cur_version; + APPENDLIST(version->changed_list, + cleanup_version->changed_list, + link); + } + /* + * Become the current version. + */ + version->writer = ISC_FALSE; + rbtdb->current_version = version; + rbtdb->current_serial = version->serial; + rbtdb->future_version = NULL; + + /* + * Keep the current version in the open list, and + * gain a reference for the DB itself (see the DB + * creation function below). This must be the only + * case where we need to increment the counter from + * zero and need to use isc_refcount_increment0(). + */ + isc_refcount_increment0(&version->references, + &cur_ref); + INSIST(cur_ref == 1); + PREPEND(rbtdb->open_versions, + rbtdb->current_version, link); + } else { + /* + * We're rolling back this transaction. + */ + cleanup_list = version->changed_list; + ISC_LIST_INIT(version->changed_list); + rollback = ISC_TRUE; + cleanup_version = version; + rbtdb->future_version = NULL; + } + } else { + if (version != rbtdb->current_version) { + /* + * There are no external or internal references + * to this version and it can be cleaned up. + */ + cleanup_version = version; + + /* + * Find the version with the least serial + * number greater than ours. + */ + least_greater = PREV(version, link); + if (least_greater == NULL) + least_greater = rbtdb->current_version; + + INSIST(version->serial < least_greater->serial); + /* + * Is this the least open version? + */ + if (version->serial == rbtdb->least_serial) { + /* + * Yes. Install the new least open + * version. + */ + make_least_version(rbtdb, + least_greater, + &cleanup_list); + } else { + /* + * Add any unexecuted cleanups to + * those of the least greater version. + */ + APPENDLIST(least_greater->changed_list, + version->changed_list, + link); + } + } else if (version->serial == rbtdb->least_serial) + INSIST(EMPTY(version->changed_list)); + UNLINK(rbtdb->open_versions, version, link); + } + least_serial = rbtdb->least_serial; + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + + /* + * Update the zone's secure status. + */ + if (version->writer && commit && !IS_CACHE(rbtdb)) + rbtdb->secure = iszonesecure(db, rbtdb->origin_node); + + if (cleanup_version != NULL) { + INSIST(EMPTY(cleanup_version->changed_list)); + isc_mem_put(rbtdb->common.mctx, cleanup_version, + sizeof(*cleanup_version)); + } + + if (!EMPTY(cleanup_list)) { + for (changed = HEAD(cleanup_list); + changed != NULL; + changed = next_changed) { + nodelock_t *lock; + + next_changed = NEXT(changed, link); + rbtnode = changed->node; + lock = &rbtdb->node_locks[rbtnode->locknum].lock; + + NODE_LOCK(lock, isc_rwlocktype_write); + if (rollback) + rollback_node(rbtnode, serial); + decrement_reference(rbtdb, rbtnode, least_serial, + isc_rwlocktype_write, + isc_rwlocktype_none); + NODE_UNLOCK(lock, isc_rwlocktype_write); + + isc_mem_put(rbtdb->common.mctx, changed, + sizeof(*changed)); + } + } end: - *versionp = NULL; + *versionp = NULL; } /* @@ -1590,3851 +1953,4130 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { */ static isc_result_t add_wildcard_magic(dns_rbtdb_t *rbtdb, dns_name_t *name) { - isc_result_t result; - dns_name_t foundname; - dns_offsets_t offsets; - unsigned int n; - dns_rbtnode_t *node = NULL; - - dns_name_init(&foundname, offsets); - n = dns_name_countlabels(name); - INSIST(n >= 2); - n--; - dns_name_getlabelsequence(name, 1, n, &foundname); - result = dns_rbt_addnode(rbtdb->tree, &foundname, &node); - if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) - return (result); - node->find_callback = 1; - node->wild = 1; - return (ISC_R_SUCCESS); + isc_result_t result; + dns_name_t foundname; + dns_offsets_t offsets; + unsigned int n; + dns_rbtnode_t *node = NULL; + + dns_name_init(&foundname, offsets); + n = dns_name_countlabels(name); + INSIST(n >= 2); + n--; + dns_name_getlabelsequence(name, 1, n, &foundname); + result = dns_rbt_addnode(rbtdb->tree, &foundname, &node); + if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) + return (result); + node->find_callback = 1; + node->wild = 1; + return (ISC_R_SUCCESS); } static isc_result_t add_empty_wildcards(dns_rbtdb_t *rbtdb, dns_name_t *name) { - isc_result_t result; - dns_name_t foundname; - dns_offsets_t offsets; - unsigned int n, l, i; - - dns_name_init(&foundname, offsets); - n = dns_name_countlabels(name); - l = dns_name_countlabels(&rbtdb->common.origin); - i = l + 1; - while (i < n) { - dns_rbtnode_t *node = NULL; /* dummy */ - dns_name_getlabelsequence(name, n - i, i, &foundname); - if (dns_name_iswildcard(&foundname)) { - result = add_wildcard_magic(rbtdb, &foundname); - if (result != ISC_R_SUCCESS) - return (result); - result = dns_rbt_addnode(rbtdb->tree, &foundname, - &node); - if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) - return (result); - } - i++; - } - return (ISC_R_SUCCESS); + isc_result_t result; + dns_name_t foundname; + dns_offsets_t offsets; + unsigned int n, l, i; + + dns_name_init(&foundname, offsets); + n = dns_name_countlabels(name); + l = dns_name_countlabels(&rbtdb->common.origin); + i = l + 1; + while (i < n) { + dns_rbtnode_t *node = NULL; /* dummy */ + dns_name_getlabelsequence(name, n - i, i, &foundname); + if (dns_name_iswildcard(&foundname)) { + result = add_wildcard_magic(rbtdb, &foundname); + if (result != ISC_R_SUCCESS) + return (result); + result = dns_rbt_addnode(rbtdb->tree, &foundname, + &node); + if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) + return (result); + } + i++; + } + return (ISC_R_SUCCESS); } static isc_result_t findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, - dns_dbnode_t **nodep) + dns_dbnode_t **nodep) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *node = NULL; - dns_name_t nodename; - isc_result_t result; - isc_rwlocktype_t locktype = isc_rwlocktype_read; - - REQUIRE(VALID_RBTDB(rbtdb)); - - dns_name_init(&nodename, NULL); - RWLOCK(&rbtdb->tree_lock, locktype); - result = dns_rbt_findnode(rbtdb->tree, name, NULL, &node, NULL, - DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result != ISC_R_SUCCESS) { - RWUNLOCK(&rbtdb->tree_lock, locktype); - if (!create) { - if (result == DNS_R_PARTIALMATCH) - result = ISC_R_NOTFOUND; - return (result); - } - /* - * It would be nice to try to upgrade the lock instead of - * unlocking then relocking. - */ - locktype = isc_rwlocktype_write; - RWLOCK(&rbtdb->tree_lock, locktype); - node = NULL; - result = dns_rbt_addnode(rbtdb->tree, name, &node); - if (result == ISC_R_SUCCESS) { - dns_rbt_namefromnode(node, &nodename); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *node = NULL; + dns_name_t nodename; + isc_result_t result; + isc_rwlocktype_t locktype = isc_rwlocktype_read; + + REQUIRE(VALID_RBTDB(rbtdb)); + + dns_name_init(&nodename, NULL); + RWLOCK(&rbtdb->tree_lock, locktype); + result = dns_rbt_findnode(rbtdb->tree, name, NULL, &node, NULL, + DNS_RBTFIND_EMPTYDATA, NULL, NULL); + if (result != ISC_R_SUCCESS) { + RWUNLOCK(&rbtdb->tree_lock, locktype); + if (!create) { + if (result == DNS_R_PARTIALMATCH) + result = ISC_R_NOTFOUND; + return (result); + } + /* + * It would be nice to try to upgrade the lock instead of + * unlocking then relocking. + */ + locktype = isc_rwlocktype_write; + RWLOCK(&rbtdb->tree_lock, locktype); + node = NULL; + result = dns_rbt_addnode(rbtdb->tree, name, &node); + if (result == ISC_R_SUCCESS) { + dns_rbt_namefromnode(node, &nodename); #ifdef DNS_RBT_USEHASH - node->locknum = node->hashval % rbtdb->node_lock_count; + node->locknum = node->hashval % rbtdb->node_lock_count; #else - node->locknum = dns_name_hash(&nodename, ISC_TRUE) % - rbtdb->node_lock_count; + node->locknum = dns_name_hash(&nodename, ISC_TRUE) % + rbtdb->node_lock_count; #endif - add_empty_wildcards(rbtdb, name); - - if (dns_name_iswildcard(name)) { - result = add_wildcard_magic(rbtdb, name); - if (result != ISC_R_SUCCESS) { - RWUNLOCK(&rbtdb->tree_lock, locktype); - return (result); - } - } - } else if (result != ISC_R_EXISTS) { - RWUNLOCK(&rbtdb->tree_lock, locktype); - return (result); - } - } - NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); - new_reference(rbtdb, node); - NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); - RWUNLOCK(&rbtdb->tree_lock, locktype); - - *nodep = (dns_dbnode_t *)node; - - return (ISC_R_SUCCESS); + add_empty_wildcards(rbtdb, name); + + if (dns_name_iswildcard(name)) { + result = add_wildcard_magic(rbtdb, name); + if (result != ISC_R_SUCCESS) { + RWUNLOCK(&rbtdb->tree_lock, locktype); + return (result); + } + } + } else if (result != ISC_R_EXISTS) { + RWUNLOCK(&rbtdb->tree_lock, locktype); + return (result); + } + } + NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); + new_reference(rbtdb, node); + + /* + * If the node just found is in the deadnode list, we need to retrieve + * it from the list because we are going to use the node. There are + * other cases where a node is newly referenced, but this should be + * the only case where it can be in the deadnode list. Also, if we + * happen to hold a write lock on the tree, it's a good chance to purge + * dead nodes. + */ + if (IS_CACHE(rbtdb)) { + isc_boolean_t need_relock = ISC_FALSE; + + NODE_WEAKLOCK(&rbtdb->node_locks[node->locknum].lock, + isc_rwlocktype_read); + if (ISC_LINK_LINKED(node, deadlink) && isc_rwlocktype_write) + need_relock = ISC_TRUE; + else if (!ISC_LIST_EMPTY(rbtdb->deadnodes[node->locknum]) && + locktype == isc_rwlocktype_write) + need_relock = ISC_TRUE; + NODE_WEAKUNLOCK(&rbtdb->node_locks[node->locknum].lock, + isc_rwlocktype_read); + if (need_relock) { + NODE_WEAKLOCK(&rbtdb->node_locks[node->locknum].lock, + isc_rwlocktype_write); + if (ISC_LINK_LINKED(node, deadlink)) + ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum], + node, deadlink); + if (locktype == isc_rwlocktype_write) + cleanup_dead_nodes(rbtdb, node->locknum); + NODE_WEAKUNLOCK(&rbtdb->node_locks[node->locknum].lock, + isc_rwlocktype_write); + } + } + + NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); + RWUNLOCK(&rbtdb->tree_lock, locktype); + + *nodep = (dns_dbnode_t *)node; + + return (ISC_R_SUCCESS); } static isc_result_t zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) { - rbtdb_search_t *search = arg; - rdatasetheader_t *header, *header_next; - rdatasetheader_t *dname_header, *sigdname_header, *ns_header; - rdatasetheader_t *found; - isc_result_t result; - dns_rbtnode_t *onode; - - /* - * We only want to remember the topmost zone cut, since it's the one - * that counts, so we'll just continue if we've already found a - * zonecut. - */ - if (search->zonecut != NULL) - return (DNS_R_CONTINUE); - - found = NULL; - result = DNS_R_CONTINUE; - onode = search->rbtdb->origin_node; - - NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - - /* - * Look for an NS or DNAME rdataset active in our version. - */ - ns_header = NULL; - dname_header = NULL; - sigdname_header = NULL; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (header->type == dns_rdatatype_ns || - header->type == dns_rdatatype_dname || - header->type == RBTDB_RDATATYPE_SIGDNAME) { - do { - if (header->serial <= search->serial && - !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) { - if (header->type == dns_rdatatype_dname) - dname_header = header; - else if (header->type == - RBTDB_RDATATYPE_SIGDNAME) - sigdname_header = header; - else if (node != onode || - IS_STUB(search->rbtdb)) { - /* - * We've found an NS rdataset that - * isn't at the origin node. We check - * that they're not at the origin node, - * because otherwise we'd erroneously - * treat the zone top as if it were - * a delegation. - */ - ns_header = header; - } - } - } - } - - /* - * Did we find anything? - */ - if (dname_header != NULL) { - /* - * Note that DNAME has precedence over NS if both exist. - */ - found = dname_header; - search->zonecut_sigrdataset = sigdname_header; - } else if (ns_header != NULL) { - found = ns_header; - search->zonecut_sigrdataset = NULL; - } - - if (found != NULL) { - /* - * We increment the reference count on node to ensure that - * search->zonecut_rdataset will still be valid later. - */ - new_reference(search->rbtdb, node); - search->zonecut = node; - search->zonecut_rdataset = found; - search->need_cleanup = ISC_TRUE; - /* - * Since we've found a zonecut, anything beneath it is - * glue and is not subject to wildcard matching, so we - * may clear search->wild. - */ - search->wild = ISC_FALSE; - if ((search->options & DNS_DBFIND_GLUEOK) == 0) { - /* - * If the caller does not want to find glue, then - * this is the best answer and the search should - * stop now. - */ - result = DNS_R_PARTIALMATCH; - } else { - dns_name_t *zcname; - - /* - * The search will continue beneath the zone cut. - * This may or may not be the best match. In case it - * is, we need to remember the node name. - */ - zcname = dns_fixedname_name(&search->zonecut_name); - RUNTIME_CHECK(dns_name_copy(name, zcname, NULL) == - ISC_R_SUCCESS); - search->copy_name = ISC_TRUE; - } - } else { - /* - * There is no zonecut at this node which is active in this - * version. - * - * If this is a "wild" node and the caller hasn't disabled - * wildcard matching, remember that we've seen a wild node - * in case we need to go searching for wildcard matches - * later on. - */ - if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0) - search->wild = ISC_TRUE; - } - - NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - - return (result); + rbtdb_search_t *search = arg; + rdatasetheader_t *header, *header_next; + rdatasetheader_t *dname_header, *sigdname_header, *ns_header; + rdatasetheader_t *found; + isc_result_t result; + dns_rbtnode_t *onode; + + /* + * We only want to remember the topmost zone cut, since it's the one + * that counts, so we'll just continue if we've already found a + * zonecut. + */ + if (search->zonecut != NULL) + return (DNS_R_CONTINUE); + + found = NULL; + result = DNS_R_CONTINUE; + onode = search->rbtdb->origin_node; + + NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + + /* + * Look for an NS or DNAME rdataset active in our version. + */ + ns_header = NULL; + dname_header = NULL; + sigdname_header = NULL; + for (header = node->data; header != NULL; header = header_next) { + header_next = header->next; + if (header->type == dns_rdatatype_ns || + header->type == dns_rdatatype_dname || + header->type == RBTDB_RDATATYPE_SIGDNAME) { + do { + if (header->serial <= search->serial && + !IGNORE(header)) { + /* + * Is this a "this rdataset doesn't + * exist" record? + */ + if (NONEXISTENT(header)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) { + if (header->type == dns_rdatatype_dname) + dname_header = header; + else if (header->type == + RBTDB_RDATATYPE_SIGDNAME) + sigdname_header = header; + else if (node != onode || + IS_STUB(search->rbtdb)) { + /* + * We've found an NS rdataset that + * isn't at the origin node. We check + * that they're not at the origin node, + * because otherwise we'd erroneously + * treat the zone top as if it were + * a delegation. + */ + ns_header = header; + } + } + } + } + + /* + * Did we find anything? + */ + if (dname_header != NULL) { + /* + * Note that DNAME has precedence over NS if both exist. + */ + found = dname_header; + search->zonecut_sigrdataset = sigdname_header; + } else if (ns_header != NULL) { + found = ns_header; + search->zonecut_sigrdataset = NULL; + } + + if (found != NULL) { + /* + * We increment the reference count on node to ensure that + * search->zonecut_rdataset will still be valid later. + */ + new_reference(search->rbtdb, node); + search->zonecut = node; + search->zonecut_rdataset = found; + search->need_cleanup = ISC_TRUE; + /* + * Since we've found a zonecut, anything beneath it is + * glue and is not subject to wildcard matching, so we + * may clear search->wild. + */ + search->wild = ISC_FALSE; + if ((search->options & DNS_DBFIND_GLUEOK) == 0) { + /* + * If the caller does not want to find glue, then + * this is the best answer and the search should + * stop now. + */ + result = DNS_R_PARTIALMATCH; + } else { + dns_name_t *zcname; + + /* + * The search will continue beneath the zone cut. + * This may or may not be the best match. In case it + * is, we need to remember the node name. + */ + zcname = dns_fixedname_name(&search->zonecut_name); + RUNTIME_CHECK(dns_name_copy(name, zcname, NULL) == + ISC_R_SUCCESS); + search->copy_name = ISC_TRUE; + } + } else { + /* + * There is no zonecut at this node which is active in this + * version. + * + * If this is a "wild" node and the caller hasn't disabled + * wildcard matching, remember that we've seen a wild node + * in case we need to go searching for wildcard matches + * later on. + */ + if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0) + search->wild = ISC_TRUE; + } + + NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + + return (result); } static inline void bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - rdatasetheader_t *header, isc_stdtime_t now, - dns_rdataset_t *rdataset) + rdatasetheader_t *header, isc_stdtime_t now, + dns_rdataset_t *rdataset) { - unsigned char *raw; /* RDATASLAB */ - - /* - * Caller must be holding the node reader lock. - * XXXJT: technically, we need a writer lock, since we'll increment - * the header count below. However, since the actual counter value - * doesn't matter, we prioritize performance here. (We may want to - * use atomic increment when available). - */ - - if (rdataset == NULL) - return; - - new_reference(rbtdb, node); - - INSIST(rdataset->methods == NULL); /* We must be disassociated. */ - - rdataset->methods = &rdataset_methods; - rdataset->rdclass = rbtdb->common.rdclass; - rdataset->type = RBTDB_RDATATYPE_BASE(header->type); - rdataset->covers = RBTDB_RDATATYPE_EXT(header->type); - rdataset->ttl = header->ttl - now; - rdataset->trust = header->trust; - if (NXDOMAIN(header)) - rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN; - rdataset->private1 = rbtdb; - rdataset->private2 = node; - raw = (unsigned char *)header + sizeof(*header); - rdataset->private3 = raw; - rdataset->count = header->count++; - if (rdataset->count == ISC_UINT32_MAX) - rdataset->count = 0; - - /* - * Reset iterator state. - */ - rdataset->privateuint4 = 0; - rdataset->private5 = NULL; - - /* - * Add noqname proof. - */ - rdataset->private6 = header->noqname; - if (rdataset->private6 != NULL) - rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; + unsigned char *raw; /* RDATASLAB */ + + /* + * Caller must be holding the node reader lock. + * XXXJT: technically, we need a writer lock, since we'll increment + * the header count below. However, since the actual counter value + * doesn't matter, we prioritize performance here. (We may want to + * use atomic increment when available). + */ + + if (rdataset == NULL) + return; + + new_reference(rbtdb, node); + + INSIST(rdataset->methods == NULL); /* We must be disassociated. */ + + rdataset->methods = &rdataset_methods; + rdataset->rdclass = rbtdb->common.rdclass; + rdataset->type = RBTDB_RDATATYPE_BASE(header->type); + rdataset->covers = RBTDB_RDATATYPE_EXT(header->type); + rdataset->ttl = header->rdh_ttl - now; + rdataset->trust = header->trust; + if (NXDOMAIN(header)) + rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN; + rdataset->private1 = rbtdb; + rdataset->private2 = node; + raw = (unsigned char *)header + sizeof(*header); + rdataset->private3 = raw; + rdataset->count = header->count++; + if (rdataset->count == ISC_UINT32_MAX) + rdataset->count = 0; + + /* + * Reset iterator state. + */ + rdataset->privateuint4 = 0; + rdataset->private5 = NULL; + + /* + * Add noqname proof. + */ + rdataset->private6 = header->noqname; + if (rdataset->private6 != NULL) + rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; } static inline isc_result_t setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset) + dns_name_t *foundname, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset) { - isc_result_t result; - dns_name_t *zcname; - rbtdb_rdatatype_t type; - dns_rbtnode_t *node; - - /* - * The caller MUST NOT be holding any node locks. - */ - - node = search->zonecut; - type = search->zonecut_rdataset->type; - - /* - * If we have to set foundname, we do it before anything else. - * If we were to set foundname after we had set nodep or bound the - * rdataset, then we'd have to undo that work if dns_name_copy() - * failed. By setting foundname first, there's nothing to undo if - * we have trouble. - */ - if (foundname != NULL && search->copy_name) { - zcname = dns_fixedname_name(&search->zonecut_name); - result = dns_name_copy(zcname, foundname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - } - if (nodep != NULL) { - /* - * Note that we don't have to increment the node's reference - * count here because we're going to use the reference we - * already have in the search block. - */ - *nodep = node; - search->need_cleanup = ISC_FALSE; - } - if (rdataset != NULL) { - NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - bind_rdataset(search->rbtdb, node, search->zonecut_rdataset, - search->now, rdataset); - if (sigrdataset != NULL && search->zonecut_sigrdataset != NULL) - bind_rdataset(search->rbtdb, node, - search->zonecut_sigrdataset, - search->now, sigrdataset); - NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - } - - if (type == dns_rdatatype_dname) - return (DNS_R_DNAME); - return (DNS_R_DELEGATION); + isc_result_t result; + dns_name_t *zcname; + rbtdb_rdatatype_t type; + dns_rbtnode_t *node; + + /* + * The caller MUST NOT be holding any node locks. + */ + + node = search->zonecut; + type = search->zonecut_rdataset->type; + + /* + * If we have to set foundname, we do it before anything else. + * If we were to set foundname after we had set nodep or bound the + * rdataset, then we'd have to undo that work if dns_name_copy() + * failed. By setting foundname first, there's nothing to undo if + * we have trouble. + */ + if (foundname != NULL && search->copy_name) { + zcname = dns_fixedname_name(&search->zonecut_name); + result = dns_name_copy(zcname, foundname, NULL); + if (result != ISC_R_SUCCESS) + return (result); + } + if (nodep != NULL) { + /* + * Note that we don't have to increment the node's reference + * count here because we're going to use the reference we + * already have in the search block. + */ + *nodep = node; + search->need_cleanup = ISC_FALSE; + } + if (rdataset != NULL) { + NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + bind_rdataset(search->rbtdb, node, search->zonecut_rdataset, + search->now, rdataset); + if (sigrdataset != NULL && search->zonecut_sigrdataset != NULL) + bind_rdataset(search->rbtdb, node, + search->zonecut_sigrdataset, + search->now, sigrdataset); + NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + } + + if (type == dns_rdatatype_dname) + return (DNS_R_DNAME); + return (DNS_R_DELEGATION); } static inline isc_boolean_t valid_glue(rbtdb_search_t *search, dns_name_t *name, rbtdb_rdatatype_t type, - dns_rbtnode_t *node) + dns_rbtnode_t *node) { - unsigned char *raw; /* RDATASLAB */ - unsigned int count, size; - dns_name_t ns_name; - isc_boolean_t valid = ISC_FALSE; - dns_offsets_t offsets; - isc_region_t region; - rdatasetheader_t *header; - - /* - * No additional locking is required. - */ - - /* - * Valid glue types are A, AAAA, A6. NS is also a valid glue type - * if it occurs at a zone cut, but is not valid below it. - */ - if (type == dns_rdatatype_ns) { - if (node != search->zonecut) { - return (ISC_FALSE); - } - } else if (type != dns_rdatatype_a && - type != dns_rdatatype_aaaa && - type != dns_rdatatype_a6) { - return (ISC_FALSE); - } - - header = search->zonecut_rdataset; - raw = (unsigned char *)header + sizeof(*header); - count = raw[0] * 256 + raw[1]; + unsigned char *raw; /* RDATASLAB */ + unsigned int count, size; + dns_name_t ns_name; + isc_boolean_t valid = ISC_FALSE; + dns_offsets_t offsets; + isc_region_t region; + rdatasetheader_t *header; + + /* + * No additional locking is required. + */ + + /* + * Valid glue types are A, AAAA, A6. NS is also a valid glue type + * if it occurs at a zone cut, but is not valid below it. + */ + if (type == dns_rdatatype_ns) { + if (node != search->zonecut) { + return (ISC_FALSE); + } + } else if (type != dns_rdatatype_a && + type != dns_rdatatype_aaaa && + type != dns_rdatatype_a6) { + return (ISC_FALSE); + } + + header = search->zonecut_rdataset; + raw = (unsigned char *)header + sizeof(*header); + count = raw[0] * 256 + raw[1]; #if DNS_RDATASET_FIXED - raw += 2 + (4 * count); -#else - raw += 2; + raw += 2 + (4 * count); +#else + raw += 2; #endif - while (count > 0) { - count--; - size = raw[0] * 256 + raw[1]; + while (count > 0) { + count--; + size = raw[0] * 256 + raw[1]; #if DNS_RDATASET_FIXED - raw += 4; + raw += 4; #else - raw += 2; + raw += 2; #endif - region.base = raw; - region.length = size; - raw += size; - /* - * XXX Until we have rdata structures, we have no choice but - * to directly access the rdata format. - */ - dns_name_init(&ns_name, offsets); - dns_name_fromregion(&ns_name, ®ion); - if (dns_name_compare(&ns_name, name) == 0) { - valid = ISC_TRUE; - break; - } - } - - return (valid); + region.base = raw; + region.length = size; + raw += size; + /* + * XXX Until we have rdata structures, we have no choice but + * to directly access the rdata format. + */ + dns_name_init(&ns_name, offsets); + dns_name_fromregion(&ns_name, ®ion); + if (dns_name_compare(&ns_name, name) == 0) { + valid = ISC_TRUE; + break; + } + } + + return (valid); } static inline isc_boolean_t activeempty(rbtdb_search_t *search, dns_rbtnodechain_t *chain, - dns_name_t *name) + dns_name_t *name) { - dns_fixedname_t fnext; - dns_fixedname_t forigin; - dns_name_t *next; - dns_name_t *origin; - dns_name_t prefix; - dns_rbtdb_t *rbtdb; - dns_rbtnode_t *node; - isc_result_t result; - isc_boolean_t answer = ISC_FALSE; - rdatasetheader_t *header; - - rbtdb = search->rbtdb; - - dns_name_init(&prefix, NULL); - dns_fixedname_init(&fnext); - next = dns_fixedname_name(&fnext); - dns_fixedname_init(&forigin); - origin = dns_fixedname_name(&forigin); - - result = dns_rbtnodechain_next(chain, NULL, NULL); - while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - node = NULL; - result = dns_rbtnodechain_current(chain, &prefix, - origin, &node); - if (result != ISC_R_SUCCESS) - break; - NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - for (header = node->data; - header != NULL; - header = header->next) { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) - break; - } - NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - if (header != NULL) - break; - result = dns_rbtnodechain_next(chain, NULL, NULL); - } - if (result == ISC_R_SUCCESS) - result = dns_name_concatenate(&prefix, origin, next, NULL); - if (result == ISC_R_SUCCESS && dns_name_issubdomain(next, name)) - answer = ISC_TRUE; - return (answer); + dns_fixedname_t fnext; + dns_fixedname_t forigin; + dns_name_t *next; + dns_name_t *origin; + dns_name_t prefix; + dns_rbtdb_t *rbtdb; + dns_rbtnode_t *node; + isc_result_t result; + isc_boolean_t answer = ISC_FALSE; + rdatasetheader_t *header; + + rbtdb = search->rbtdb; + + dns_name_init(&prefix, NULL); + dns_fixedname_init(&fnext); + next = dns_fixedname_name(&fnext); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + + result = dns_rbtnodechain_next(chain, NULL, NULL); + while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { + node = NULL; + result = dns_rbtnodechain_current(chain, &prefix, + origin, &node); + if (result != ISC_R_SUCCESS) + break; + NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + for (header = node->data; + header != NULL; + header = header->next) { + if (header->serial <= search->serial && + !IGNORE(header) && EXISTS(header)) + break; + } + NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + if (header != NULL) + break; + result = dns_rbtnodechain_next(chain, NULL, NULL); + } + if (result == ISC_R_SUCCESS) + result = dns_name_concatenate(&prefix, origin, next, NULL); + if (result == ISC_R_SUCCESS && dns_name_issubdomain(next, name)) + answer = ISC_TRUE; + return (answer); } static inline isc_boolean_t activeemtpynode(rbtdb_search_t *search, dns_name_t *qname, dns_name_t *wname) { - dns_fixedname_t fnext; - dns_fixedname_t forigin; - dns_fixedname_t fprev; - dns_name_t *next; - dns_name_t *origin; - dns_name_t *prev; - dns_name_t name; - dns_name_t rname; - dns_name_t tname; - dns_rbtdb_t *rbtdb; - dns_rbtnode_t *node; - dns_rbtnodechain_t chain; - isc_boolean_t check_next = ISC_TRUE; - isc_boolean_t check_prev = ISC_TRUE; - isc_boolean_t answer = ISC_FALSE; - isc_result_t result; - rdatasetheader_t *header; - unsigned int n; - - rbtdb = search->rbtdb; - - dns_name_init(&name, NULL); - dns_name_init(&tname, NULL); - dns_name_init(&rname, NULL); - dns_fixedname_init(&fnext); - next = dns_fixedname_name(&fnext); - dns_fixedname_init(&fprev); - prev = dns_fixedname_name(&fprev); - dns_fixedname_init(&forigin); - origin = dns_fixedname_name(&forigin); - - /* - * Find if qname is at or below a empty node. - * Use our own copy of the chain. - */ - - chain = search->chain; - do { - node = NULL; - result = dns_rbtnodechain_current(&chain, &name, - origin, &node); - if (result != ISC_R_SUCCESS) - break; - NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - for (header = node->data; - header != NULL; - header = header->next) { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) - break; - } - NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - if (header != NULL) - break; - result = dns_rbtnodechain_prev(&chain, NULL, NULL); - } while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN); - if (result == ISC_R_SUCCESS) - result = dns_name_concatenate(&name, origin, prev, NULL); - if (result != ISC_R_SUCCESS) - check_prev = ISC_FALSE; - - result = dns_rbtnodechain_next(&chain, NULL, NULL); - while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - node = NULL; - result = dns_rbtnodechain_current(&chain, &name, - origin, &node); - if (result != ISC_R_SUCCESS) - break; - NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - for (header = node->data; - header != NULL; - header = header->next) { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) - break; - } - NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - if (header != NULL) - break; - result = dns_rbtnodechain_next(&chain, NULL, NULL); - } - if (result == ISC_R_SUCCESS) - result = dns_name_concatenate(&name, origin, next, NULL); - if (result != ISC_R_SUCCESS) - check_next = ISC_FALSE; - - dns_name_clone(qname, &rname); - - /* - * Remove the wildcard label to find the terminal name. - */ - n = dns_name_countlabels(wname); - dns_name_getlabelsequence(wname, 1, n - 1, &tname); - - do { - if ((check_prev && dns_name_issubdomain(prev, &rname)) || - (check_next && dns_name_issubdomain(next, &rname))) { - answer = ISC_TRUE; - break; - } - /* - * Remove the left hand label. - */ - n = dns_name_countlabels(&rname); - dns_name_getlabelsequence(&rname, 1, n - 1, &rname); - } while (!dns_name_equal(&rname, &tname)); - return (answer); + dns_fixedname_t fnext; + dns_fixedname_t forigin; + dns_fixedname_t fprev; + dns_name_t *next; + dns_name_t *origin; + dns_name_t *prev; + dns_name_t name; + dns_name_t rname; + dns_name_t tname; + dns_rbtdb_t *rbtdb; + dns_rbtnode_t *node; + dns_rbtnodechain_t chain; + isc_boolean_t check_next = ISC_TRUE; + isc_boolean_t check_prev = ISC_TRUE; + isc_boolean_t answer = ISC_FALSE; + isc_result_t result; + rdatasetheader_t *header; + unsigned int n; + + rbtdb = search->rbtdb; + + dns_name_init(&name, NULL); + dns_name_init(&tname, NULL); + dns_name_init(&rname, NULL); + dns_fixedname_init(&fnext); + next = dns_fixedname_name(&fnext); + dns_fixedname_init(&fprev); + prev = dns_fixedname_name(&fprev); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + + /* + * Find if qname is at or below a empty node. + * Use our own copy of the chain. + */ + + chain = search->chain; + do { + node = NULL; + result = dns_rbtnodechain_current(&chain, &name, + origin, &node); + if (result != ISC_R_SUCCESS) + break; + NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + for (header = node->data; + header != NULL; + header = header->next) { + if (header->serial <= search->serial && + !IGNORE(header) && EXISTS(header)) + break; + } + NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + if (header != NULL) + break; + result = dns_rbtnodechain_prev(&chain, NULL, NULL); + } while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN); + if (result == ISC_R_SUCCESS) + result = dns_name_concatenate(&name, origin, prev, NULL); + if (result != ISC_R_SUCCESS) + check_prev = ISC_FALSE; + + result = dns_rbtnodechain_next(&chain, NULL, NULL); + while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { + node = NULL; + result = dns_rbtnodechain_current(&chain, &name, + origin, &node); + if (result != ISC_R_SUCCESS) + break; + NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + for (header = node->data; + header != NULL; + header = header->next) { + if (header->serial <= search->serial && + !IGNORE(header) && EXISTS(header)) + break; + } + NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + if (header != NULL) + break; + result = dns_rbtnodechain_next(&chain, NULL, NULL); + } + if (result == ISC_R_SUCCESS) + result = dns_name_concatenate(&name, origin, next, NULL); + if (result != ISC_R_SUCCESS) + check_next = ISC_FALSE; + + dns_name_clone(qname, &rname); + + /* + * Remove the wildcard label to find the terminal name. + */ + n = dns_name_countlabels(wname); + dns_name_getlabelsequence(wname, 1, n - 1, &tname); + + do { + if ((check_prev && dns_name_issubdomain(prev, &rname)) || + (check_next && dns_name_issubdomain(next, &rname))) { + answer = ISC_TRUE; + break; + } + /* + * Remove the left hand label. + */ + n = dns_name_countlabels(&rname); + dns_name_getlabelsequence(&rname, 1, n - 1, &rname); + } while (!dns_name_equal(&rname, &tname)); + return (answer); } static inline isc_result_t find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, - dns_name_t *qname) + dns_name_t *qname) { - unsigned int i, j; - dns_rbtnode_t *node, *level_node, *wnode; - rdatasetheader_t *header; - isc_result_t result = ISC_R_NOTFOUND; - dns_name_t name; - dns_name_t *wname; - dns_fixedname_t fwname; - dns_rbtdb_t *rbtdb; - isc_boolean_t done, wild, active; - dns_rbtnodechain_t wchain; - - /* - * Caller must be holding the tree lock and MUST NOT be holding - * any node locks. - */ - - /* - * Examine each ancestor level. If the level's wild bit - * is set, then construct the corresponding wildcard name and - * search for it. If the wildcard node exists, and is active in - * this version, we're done. If not, then we next check to see - * if the ancestor is active in this version. If so, then there - * can be no possible wildcard match and again we're done. If not, - * continue the search. - */ - - rbtdb = search->rbtdb; - i = search->chain.level_matches; - done = ISC_FALSE; - node = *nodep; - do { - NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - - /* - * First we try to figure out if this node is active in - * the search's version. We do this now, even though we - * may not need the information, because it simplifies the - * locking and code flow. - */ - for (header = node->data; - header != NULL; - header = header->next) { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) - break; - } - if (header != NULL) - active = ISC_TRUE; - else - active = ISC_FALSE; - - if (node->wild) - wild = ISC_TRUE; - else - wild = ISC_FALSE; - - NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - - if (wild) { - /* - * Construct the wildcard name for this level. - */ - dns_name_init(&name, NULL); - dns_rbt_namefromnode(node, &name); - dns_fixedname_init(&fwname); - wname = dns_fixedname_name(&fwname); - result = dns_name_concatenate(dns_wildcardname, &name, - wname, NULL); - j = i; - while (result == ISC_R_SUCCESS && j != 0) { - j--; - level_node = search->chain.levels[j]; - dns_name_init(&name, NULL); - dns_rbt_namefromnode(level_node, &name); - result = dns_name_concatenate(wname, - &name, - wname, - NULL); - } - if (result != ISC_R_SUCCESS) - break; - - wnode = NULL; - dns_rbtnodechain_init(&wchain, NULL); - result = dns_rbt_findnode(rbtdb->tree, wname, - NULL, &wnode, &wchain, - DNS_RBTFIND_EMPTYDATA, - NULL, NULL); - if (result == ISC_R_SUCCESS) { - nodelock_t *lock; - - /* - * We have found the wildcard node. If it - * is active in the search's version, we're - * done. - */ - lock = &rbtdb->node_locks[wnode->locknum].lock; - NODE_LOCK(lock, isc_rwlocktype_read); - for (header = wnode->data; - header != NULL; - header = header->next) { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) - break; - } - NODE_UNLOCK(lock, isc_rwlocktype_read); - if (header != NULL || - activeempty(search, &wchain, wname)) { - if (activeemtpynode(search, qname, - wname)) { - return (ISC_R_NOTFOUND); - } - /* - * The wildcard node is active! - * - * Note: result is still ISC_R_SUCCESS - * so we don't have to set it. - */ - *nodep = wnode; - break; - } - } else if (result != ISC_R_NOTFOUND && - result != DNS_R_PARTIALMATCH) { - /* - * An error has occurred. Bail out. - */ - break; - } - } - - if (active) { - /* - * The level node is active. Any wildcarding - * present at higher levels has no - * effect and we're done. - */ - result = ISC_R_NOTFOUND; - break; - } - - if (i > 0) { - i--; - node = search->chain.levels[i]; - } else - done = ISC_TRUE; - } while (!done); - - return (result); + unsigned int i, j; + dns_rbtnode_t *node, *level_node, *wnode; + rdatasetheader_t *header; + isc_result_t result = ISC_R_NOTFOUND; + dns_name_t name; + dns_name_t *wname; + dns_fixedname_t fwname; + dns_rbtdb_t *rbtdb; + isc_boolean_t done, wild, active; + dns_rbtnodechain_t wchain; + + /* + * Caller must be holding the tree lock and MUST NOT be holding + * any node locks. + */ + + /* + * Examine each ancestor level. If the level's wild bit + * is set, then construct the corresponding wildcard name and + * search for it. If the wildcard node exists, and is active in + * this version, we're done. If not, then we next check to see + * if the ancestor is active in this version. If so, then there + * can be no possible wildcard match and again we're done. If not, + * continue the search. + */ + + rbtdb = search->rbtdb; + i = search->chain.level_matches; + done = ISC_FALSE; + node = *nodep; + do { + NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + + /* + * First we try to figure out if this node is active in + * the search's version. We do this now, even though we + * may not need the information, because it simplifies the + * locking and code flow. + */ + for (header = node->data; + header != NULL; + header = header->next) { + if (header->serial <= search->serial && + !IGNORE(header) && EXISTS(header)) + break; + } + if (header != NULL) + active = ISC_TRUE; + else + active = ISC_FALSE; + + if (node->wild) + wild = ISC_TRUE; + else + wild = ISC_FALSE; + + NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + + if (wild) { + /* + * Construct the wildcard name for this level. + */ + dns_name_init(&name, NULL); + dns_rbt_namefromnode(node, &name); + dns_fixedname_init(&fwname); + wname = dns_fixedname_name(&fwname); + result = dns_name_concatenate(dns_wildcardname, &name, + wname, NULL); + j = i; + while (result == ISC_R_SUCCESS && j != 0) { + j--; + level_node = search->chain.levels[j]; + dns_name_init(&name, NULL); + dns_rbt_namefromnode(level_node, &name); + result = dns_name_concatenate(wname, + &name, + wname, + NULL); + } + if (result != ISC_R_SUCCESS) + break; + + wnode = NULL; + dns_rbtnodechain_init(&wchain, NULL); + result = dns_rbt_findnode(rbtdb->tree, wname, + NULL, &wnode, &wchain, + DNS_RBTFIND_EMPTYDATA, + NULL, NULL); + if (result == ISC_R_SUCCESS) { + nodelock_t *lock; + + /* + * We have found the wildcard node. If it + * is active in the search's version, we're + * done. + */ + lock = &rbtdb->node_locks[wnode->locknum].lock; + NODE_LOCK(lock, isc_rwlocktype_read); + for (header = wnode->data; + header != NULL; + header = header->next) { + if (header->serial <= search->serial && + !IGNORE(header) && EXISTS(header)) + break; + } + NODE_UNLOCK(lock, isc_rwlocktype_read); + if (header != NULL || + activeempty(search, &wchain, wname)) { + if (activeemtpynode(search, qname, + wname)) { + return (ISC_R_NOTFOUND); + } + /* + * The wildcard node is active! + * + * Note: result is still ISC_R_SUCCESS + * so we don't have to set it. + */ + *nodep = wnode; + break; + } + } else if (result != ISC_R_NOTFOUND && + result != DNS_R_PARTIALMATCH) { + /* + * An error has occurred. Bail out. + */ + break; + } + } + + if (active) { + /* + * The level node is active. Any wildcarding + * present at higher levels has no + * effect and we're done. + */ + result = ISC_R_NOTFOUND; + break; + } + + if (i > 0) { + i--; + node = search->chain.levels[i]; + } else + done = ISC_TRUE; + } while (!done); + + return (result); } static inline isc_result_t find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset, isc_boolean_t need_sig) + dns_name_t *foundname, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, isc_boolean_t need_sig) { - dns_rbtnode_t *node; - rdatasetheader_t *header, *header_next, *found, *foundsig; - isc_boolean_t empty_node; - isc_result_t result; - dns_fixedname_t fname, forigin; - dns_name_t *name, *origin; - - do { - node = NULL; - dns_fixedname_init(&fname); - name = dns_fixedname_name(&fname); - dns_fixedname_init(&forigin); - origin = dns_fixedname_name(&forigin); - result = dns_rbtnodechain_current(&search->chain, name, - origin, &node); - if (result != ISC_R_SUCCESS) - return (result); - NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - found = NULL; - foundsig = NULL; - empty_node = ISC_TRUE; - for (header = node->data; - header != NULL; - header = header_next) { - header_next = header->next; - /* - * Look for an active, extant NSEC or RRSIG NSEC. - */ - do { - if (header->serial <= search->serial && - !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) { - /* - * We now know that there is at least one - * active rdataset at this node. - */ - empty_node = ISC_FALSE; - if (header->type == dns_rdatatype_nsec) { - found = header; - if (foundsig != NULL) - break; - } else if (header->type == - RBTDB_RDATATYPE_SIGNSEC) { - foundsig = header; - if (found != NULL) - break; - } - } - } - if (!empty_node) { - if (found != NULL && - (foundsig != NULL || !need_sig)) - { - /* - * We've found the right NSEC record. - * - * Note: for this to really be the right - * NSEC record, it's essential that the NSEC - * records of any nodes obscured by a zone - * cut have been removed; we assume this is - * the case. - */ - result = dns_name_concatenate(name, origin, - foundname, NULL); - if (result == ISC_R_SUCCESS) { - if (nodep != NULL) { - new_reference(search->rbtdb, - node); - *nodep = node; - } - bind_rdataset(search->rbtdb, node, - found, search->now, - rdataset); - if (foundsig != NULL) - bind_rdataset(search->rbtdb, - node, - foundsig, - search->now, - sigrdataset); - } - } else if (found == NULL && foundsig == NULL) { - /* - * This node is active, but has no NSEC or - * RRSIG NSEC. That means it's glue or - * other obscured zone data that isn't - * relevant for our search. Treat the - * node as if it were empty and keep looking. - */ - empty_node = ISC_TRUE; - result = dns_rbtnodechain_prev(&search->chain, - NULL, NULL); - } else { - /* - * We found an active node, but either the - * NSEC or the RRSIG NSEC is missing. This - * shouldn't happen. - */ - result = DNS_R_BADDB; - } - } else { - /* - * This node isn't active. We've got to keep - * looking. - */ - result = dns_rbtnodechain_prev(&search->chain, NULL, - NULL); - } - NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - } while (empty_node && result == ISC_R_SUCCESS); - - /* - * If the result is ISC_R_NOMORE, then we got to the beginning of - * the database and didn't find a NSEC record. This shouldn't - * happen. - */ - if (result == ISC_R_NOMORE) - result = DNS_R_BADDB; - - return (result); + dns_rbtnode_t *node; + rdatasetheader_t *header, *header_next, *found, *foundsig; + isc_boolean_t empty_node; + isc_result_t result; + dns_fixedname_t fname, forigin; + dns_name_t *name, *origin; + + do { + node = NULL; + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + result = dns_rbtnodechain_current(&search->chain, name, + origin, &node); + if (result != ISC_R_SUCCESS) + return (result); + NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + found = NULL; + foundsig = NULL; + empty_node = ISC_TRUE; + for (header = node->data; + header != NULL; + header = header_next) { + header_next = header->next; + /* + * Look for an active, extant NSEC or RRSIG NSEC. + */ + do { + if (header->serial <= search->serial && + !IGNORE(header)) { + /* + * Is this a "this rdataset doesn't + * exist" record? + */ + if (NONEXISTENT(header)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) { + /* + * We now know that there is at least one + * active rdataset at this node. + */ + empty_node = ISC_FALSE; + if (header->type == dns_rdatatype_nsec) { + found = header; + if (foundsig != NULL) + break; + } else if (header->type == + RBTDB_RDATATYPE_SIGNSEC) { + foundsig = header; + if (found != NULL) + break; + } + } + } + if (!empty_node) { + if (found != NULL && + (foundsig != NULL || !need_sig)) + { + /* + * We've found the right NSEC record. + * + * Note: for this to really be the right + * NSEC record, it's essential that the NSEC + * records of any nodes obscured by a zone + * cut have been removed; we assume this is + * the case. + */ + result = dns_name_concatenate(name, origin, + foundname, NULL); + if (result == ISC_R_SUCCESS) { + if (nodep != NULL) { + new_reference(search->rbtdb, + node); + *nodep = node; + } + bind_rdataset(search->rbtdb, node, + found, search->now, + rdataset); + if (foundsig != NULL) + bind_rdataset(search->rbtdb, + node, + foundsig, + search->now, + sigrdataset); + } + } else if (found == NULL && foundsig == NULL) { + /* + * This node is active, but has no NSEC or + * RRSIG NSEC. That means it's glue or + * other obscured zone data that isn't + * relevant for our search. Treat the + * node as if it were empty and keep looking. + */ + empty_node = ISC_TRUE; + result = dns_rbtnodechain_prev(&search->chain, + NULL, NULL); + } else { + /* + * We found an active node, but either the + * NSEC or the RRSIG NSEC is missing. This + * shouldn't happen. + */ + result = DNS_R_BADDB; + } + } else { + /* + * This node isn't active. We've got to keep + * looking. + */ + result = dns_rbtnodechain_prev(&search->chain, NULL, + NULL); + } + NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + } while (empty_node && result == ISC_R_SUCCESS); + + /* + * If the result is ISC_R_NOMORE, then we got to the beginning of + * the database and didn't find a NSEC record. This shouldn't + * happen. + */ + if (result == ISC_R_NOMORE) + result = DNS_R_BADDB; + + return (result); } static isc_result_t zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, - dns_dbnode_t **nodep, dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - dns_rbtnode_t *node = NULL; - isc_result_t result; - rbtdb_search_t search; - isc_boolean_t cname_ok = ISC_TRUE; - isc_boolean_t close_version = ISC_FALSE; - isc_boolean_t maybe_zonecut = ISC_FALSE; - isc_boolean_t at_zonecut = ISC_FALSE; - isc_boolean_t wild; - isc_boolean_t empty_node; - rdatasetheader_t *header, *header_next, *found, *nsecheader; - rdatasetheader_t *foundsig, *cnamesig, *nsecsig; - rbtdb_rdatatype_t sigtype; - isc_boolean_t active; - dns_rbtnodechain_t chain; - nodelock_t *lock; - - - search.rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(search.rbtdb)); - - /* - * We don't care about 'now'. - */ - UNUSED(now); - - /* - * If the caller didn't supply a version, attach to the current - * version. - */ - if (version == NULL) { - currentversion(db, &version); - close_version = ISC_TRUE; - } - - search.rbtversion = version; - search.serial = search.rbtversion->serial; - search.options = options; - search.copy_name = ISC_FALSE; - search.need_cleanup = ISC_FALSE; - search.wild = ISC_FALSE; - search.zonecut = NULL; - dns_fixedname_init(&search.zonecut_name); - dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx); - search.now = 0; - - /* - * 'wild' will be true iff. we've matched a wildcard. - */ - wild = ISC_FALSE; - - RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); - - /* - * Search down from the root of the tree. If, while going down, we - * encounter a callback node, zone_zonecut_callback() will search the - * rdatasets at the zone cut for active DNAME or NS rdatasets. - */ - result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node, - &search.chain, DNS_RBTFIND_EMPTYDATA, - zone_zonecut_callback, &search); - - if (result == DNS_R_PARTIALMATCH) { - partial_match: - if (search.zonecut != NULL) { - result = setup_delegation(&search, nodep, foundname, - rdataset, sigrdataset); - goto tree_exit; - } - - if (search.wild) { - /* - * At least one of the levels in the search chain - * potentially has a wildcard. For each such level, - * we must see if there's a matching wildcard active - * in the current version. - */ - result = find_wildcard(&search, &node, name); - if (result == ISC_R_SUCCESS) { - result = dns_name_copy(name, foundname, NULL); - if (result != ISC_R_SUCCESS) - goto tree_exit; - wild = ISC_TRUE; - goto found; - } - else if (result != ISC_R_NOTFOUND) - goto tree_exit; - } - - chain = search.chain; - active = activeempty(&search, &chain, name); - - /* - * If we're here, then the name does not exist, is not - * beneath a zonecut, and there's no matching wildcard. - */ - if (search.rbtdb->secure || - (search.options & DNS_DBFIND_FORCENSEC) != 0) - { - result = find_closest_nsec(&search, nodep, foundname, - rdataset, sigrdataset, - search.rbtdb->secure); - if (result == ISC_R_SUCCESS) - result = active ? DNS_R_EMPTYNAME : - DNS_R_NXDOMAIN; - } else - result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN; - goto tree_exit; - } else if (result != ISC_R_SUCCESS) - goto tree_exit; + dns_rbtnode_t *node = NULL; + isc_result_t result; + rbtdb_search_t search; + isc_boolean_t cname_ok = ISC_TRUE; + isc_boolean_t close_version = ISC_FALSE; + isc_boolean_t maybe_zonecut = ISC_FALSE; + isc_boolean_t at_zonecut = ISC_FALSE; + isc_boolean_t wild; + isc_boolean_t empty_node; + rdatasetheader_t *header, *header_next, *found, *nsecheader; + rdatasetheader_t *foundsig, *cnamesig, *nsecsig; + rbtdb_rdatatype_t sigtype; + isc_boolean_t active; + dns_rbtnodechain_t chain; + nodelock_t *lock; + + + search.rbtdb = (dns_rbtdb_t *)db; + + REQUIRE(VALID_RBTDB(search.rbtdb)); + + /* + * We don't care about 'now'. + */ + UNUSED(now); + + /* + * If the caller didn't supply a version, attach to the current + * version. + */ + if (version == NULL) { + currentversion(db, &version); + close_version = ISC_TRUE; + } + + search.rbtversion = version; + search.serial = search.rbtversion->serial; + search.options = options; + search.copy_name = ISC_FALSE; + search.need_cleanup = ISC_FALSE; + search.wild = ISC_FALSE; + search.zonecut = NULL; + dns_fixedname_init(&search.zonecut_name); + dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx); + search.now = 0; + + /* + * 'wild' will be true iff. we've matched a wildcard. + */ + wild = ISC_FALSE; + + RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); + + /* + * Search down from the root of the tree. If, while going down, we + * encounter a callback node, zone_zonecut_callback() will search the + * rdatasets at the zone cut for active DNAME or NS rdatasets. + */ + result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node, + &search.chain, DNS_RBTFIND_EMPTYDATA, + zone_zonecut_callback, &search); + + if (result == DNS_R_PARTIALMATCH) { + partial_match: + if (search.zonecut != NULL) { + result = setup_delegation(&search, nodep, foundname, + rdataset, sigrdataset); + goto tree_exit; + } + + if (search.wild) { + /* + * At least one of the levels in the search chain + * potentially has a wildcard. For each such level, + * we must see if there's a matching wildcard active + * in the current version. + */ + result = find_wildcard(&search, &node, name); + if (result == ISC_R_SUCCESS) { + result = dns_name_copy(name, foundname, NULL); + if (result != ISC_R_SUCCESS) + goto tree_exit; + wild = ISC_TRUE; + goto found; + } + else if (result != ISC_R_NOTFOUND) + goto tree_exit; + } + + chain = search.chain; + active = activeempty(&search, &chain, name); + + /* + * If we're here, then the name does not exist, is not + * beneath a zonecut, and there's no matching wildcard. + */ + if (search.rbtdb->secure || + (search.options & DNS_DBFIND_FORCENSEC) != 0) + { + result = find_closest_nsec(&search, nodep, foundname, + rdataset, sigrdataset, + search.rbtdb->secure); + if (result == ISC_R_SUCCESS) + result = active ? DNS_R_EMPTYNAME : + DNS_R_NXDOMAIN; + } else + result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN; + goto tree_exit; + } else if (result != ISC_R_SUCCESS) + goto tree_exit; found: - /* - * We have found a node whose name is the desired name, or we - * have matched a wildcard. - */ - - if (search.zonecut != NULL) { - /* - * If we're beneath a zone cut, we don't want to look for - * CNAMEs because they're not legitimate zone glue. - */ - cname_ok = ISC_FALSE; - } else { - /* - * The node may be a zone cut itself. If it might be one, - * make sure we check for it later. - */ - if (node->find_callback && - (node != search.rbtdb->origin_node || - IS_STUB(search.rbtdb)) && - !dns_rdatatype_atparent(type)) - maybe_zonecut = ISC_TRUE; - } - - /* - * Certain DNSSEC types are not subject to CNAME matching - * (RFC4035, section 2.5 and RFC3007). - * - * We don't check for RRSIG, because we don't store RRSIG records - * directly. - */ - if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) - cname_ok = ISC_FALSE; - - /* - * We now go looking for rdata... - */ - - NODE_LOCK(&(search.rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); - - found = NULL; - foundsig = NULL; - sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); - nsecheader = NULL; - nsecsig = NULL; - cnamesig = NULL; - empty_node = ISC_TRUE; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - /* - * Look for an active, extant rdataset. - */ - do { - if (header->serial <= search.serial && - !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) { - /* - * We now know that there is at least one active - * rdataset at this node. - */ - empty_node = ISC_FALSE; - - /* - * Do special zone cut handling, if requested. - */ - if (maybe_zonecut && - header->type == dns_rdatatype_ns) { - /* - * We increment the reference count on node to - * ensure that search->zonecut_rdataset will - * still be valid later. - */ - new_reference(search.rbtdb, node); - search.zonecut = node; - search.zonecut_rdataset = header; - search.zonecut_sigrdataset = NULL; - search.need_cleanup = ISC_TRUE; - maybe_zonecut = ISC_FALSE; - at_zonecut = ISC_TRUE; - /* - * It is not clear if KEY should still be - * allowed at the parent side of the zone - * cut or not. It is needed for RFC3007 - * validated updates. - */ - if ((search.options & DNS_DBFIND_GLUEOK) == 0 - && type != dns_rdatatype_nsec - && type != dns_rdatatype_key) { - /* - * Glue is not OK, but any answer we - * could return would be glue. Return - * the delegation. - */ - found = NULL; - break; - } - if (found != NULL && foundsig != NULL) - break; - } - - /* - * If we found a type we were looking for, - * remember it. - */ - if (header->type == type || - type == dns_rdatatype_any || - (header->type == dns_rdatatype_cname && - cname_ok)) { - /* - * We've found the answer! - */ - found = header; - if (header->type == dns_rdatatype_cname && - cname_ok) { - /* - * We may be finding a CNAME instead - * of the desired type. - * - * If we've already got the CNAME RRSIG, - * use it, otherwise change sigtype - * so that we find it. - */ - if (cnamesig != NULL) - foundsig = cnamesig; - else - sigtype = - RBTDB_RDATATYPE_SIGCNAME; - } - /* - * If we've got all we need, end the search. - */ - if (!maybe_zonecut && foundsig != NULL) - break; - } else if (header->type == sigtype) { - /* - * We've found the RRSIG rdataset for our - * target type. Remember it. - */ - foundsig = header; - /* - * If we've got all we need, end the search. - */ - if (!maybe_zonecut && found != NULL) - break; - } else if (header->type == dns_rdatatype_nsec) { - /* - * Remember a NSEC rdataset even if we're - * not specifically looking for it, because - * we might need it later. - */ - nsecheader = header; - } else if (header->type == RBTDB_RDATATYPE_SIGNSEC) { - /* - * If we need the NSEC rdataset, we'll also - * need its signature. - */ - nsecsig = header; - } else if (cname_ok && - header->type == RBTDB_RDATATYPE_SIGCNAME) { - /* - * If we get a CNAME match, we'll also need - * its signature. - */ - cnamesig = header; - } - } - } - - if (empty_node) { - /* - * We have an exact match for the name, but there are no - * active rdatasets in the desired version. That means that - * this node doesn't exist in the desired version, and that - * we really have a partial match. - */ - if (!wild) { - lock = &search.rbtdb->node_locks[node->locknum].lock; - NODE_UNLOCK(lock, isc_rwlocktype_read); - goto partial_match; - } - } - - /* - * If we didn't find what we were looking for... - */ - if (found == NULL) { - if (search.zonecut != NULL) { - /* - * We were trying to find glue at a node beneath a - * zone cut, but didn't. - * - * Return the delegation. - */ - lock = &search.rbtdb->node_locks[node->locknum].lock; - NODE_UNLOCK(lock, isc_rwlocktype_read); - result = setup_delegation(&search, nodep, foundname, - rdataset, sigrdataset); - goto tree_exit; - } - /* - * The desired type doesn't exist. - */ - result = DNS_R_NXRRSET; - if (search.rbtdb->secure && - (nsecheader == NULL || nsecsig == NULL)) { - /* - * The zone is secure but there's no NSEC, - * or the NSEC has no signature! - */ - if (!wild) { - result = DNS_R_BADDB; - goto node_exit; - } - - lock = &search.rbtdb->node_locks[node->locknum].lock; - NODE_UNLOCK(lock, isc_rwlocktype_read); - result = find_closest_nsec(&search, nodep, foundname, - rdataset, sigrdataset, - search.rbtdb->secure); - if (result == ISC_R_SUCCESS) - result = DNS_R_EMPTYWILD; - goto tree_exit; - } - if ((search.options & DNS_DBFIND_FORCENSEC) != 0 && - nsecheader == NULL) - { - /* - * There's no NSEC record, and we were told - * to find one. - */ - result = DNS_R_BADDB; - goto node_exit; - } - if (nodep != NULL) { - new_reference(search.rbtdb, node); - *nodep = node; - } - if (search.rbtdb->secure || - (search.options & DNS_DBFIND_FORCENSEC) != 0) - { - bind_rdataset(search.rbtdb, node, nsecheader, - 0, rdataset); - if (nsecsig != NULL) - bind_rdataset(search.rbtdb, node, - nsecsig, 0, sigrdataset); - } - if (wild) - foundname->attributes |= DNS_NAMEATTR_WILDCARD; - goto node_exit; - } - - /* - * We found what we were looking for, or we found a CNAME. - */ - - if (type != found->type && - type != dns_rdatatype_any && - found->type == dns_rdatatype_cname) { - /* - * We weren't doing an ANY query and we found a CNAME instead - * of the type we were looking for, so we need to indicate - * that result to the caller. - */ - result = DNS_R_CNAME; - } else if (search.zonecut != NULL) { - /* - * If we're beneath a zone cut, we must indicate that the - * result is glue, unless we're actually at the zone cut - * and the type is NSEC or KEY. - */ - if (search.zonecut == node) { - /* - * It is not clear if KEY should still be - * allowed at the parent side of the zone - * cut or not. It is needed for RFC3007 - * validated updates. - */ - if (type == dns_rdatatype_nsec || - type == dns_rdatatype_key) - result = ISC_R_SUCCESS; - else if (type == dns_rdatatype_any) - result = DNS_R_ZONECUT; - else - result = DNS_R_GLUE; - } else - result = DNS_R_GLUE; - /* - * We might have found data that isn't glue, but was occluded - * by a dynamic update. If the caller cares about this, they - * will have told us to validate glue. - * - * XXX We should cache the glue validity state! - */ - if (result == DNS_R_GLUE && - (search.options & DNS_DBFIND_VALIDATEGLUE) != 0 && - !valid_glue(&search, foundname, type, node)) { - lock = &search.rbtdb->node_locks[node->locknum].lock; - NODE_UNLOCK(lock, isc_rwlocktype_read); - result = setup_delegation(&search, nodep, foundname, - rdataset, sigrdataset); - goto tree_exit; - } - } else { - /* - * An ordinary successful query! - */ - result = ISC_R_SUCCESS; - } - - if (nodep != NULL) { - if (!at_zonecut) - new_reference(search.rbtdb, node); - else - search.need_cleanup = ISC_FALSE; - *nodep = node; - } - - if (type != dns_rdatatype_any) { - bind_rdataset(search.rbtdb, node, found, 0, rdataset); - if (foundsig != NULL) - bind_rdataset(search.rbtdb, node, foundsig, 0, - sigrdataset); - } - - if (wild) - foundname->attributes |= DNS_NAMEATTR_WILDCARD; + /* + * We have found a node whose name is the desired name, or we + * have matched a wildcard. + */ + + if (search.zonecut != NULL) { + /* + * If we're beneath a zone cut, we don't want to look for + * CNAMEs because they're not legitimate zone glue. + */ + cname_ok = ISC_FALSE; + } else { + /* + * The node may be a zone cut itself. If it might be one, + * make sure we check for it later. + */ + if (node->find_callback && + (node != search.rbtdb->origin_node || + IS_STUB(search.rbtdb)) && + !dns_rdatatype_atparent(type)) + maybe_zonecut = ISC_TRUE; + } + + /* + * Certain DNSSEC types are not subject to CNAME matching + * (RFC4035, section 2.5 and RFC3007). + * + * We don't check for RRSIG, because we don't store RRSIG records + * directly. + */ + if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) + cname_ok = ISC_FALSE; + + /* + * We now go looking for rdata... + */ + + NODE_LOCK(&(search.rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); + + found = NULL; + foundsig = NULL; + sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); + nsecheader = NULL; + nsecsig = NULL; + cnamesig = NULL; + empty_node = ISC_TRUE; + for (header = node->data; header != NULL; header = header_next) { + header_next = header->next; + /* + * Look for an active, extant rdataset. + */ + do { + if (header->serial <= search.serial && + !IGNORE(header)) { + /* + * Is this a "this rdataset doesn't + * exist" record? + */ + if (NONEXISTENT(header)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) { + /* + * We now know that there is at least one active + * rdataset at this node. + */ + empty_node = ISC_FALSE; + + /* + * Do special zone cut handling, if requested. + */ + if (maybe_zonecut && + header->type == dns_rdatatype_ns) { + /* + * We increment the reference count on node to + * ensure that search->zonecut_rdataset will + * still be valid later. + */ + new_reference(search.rbtdb, node); + search.zonecut = node; + search.zonecut_rdataset = header; + search.zonecut_sigrdataset = NULL; + search.need_cleanup = ISC_TRUE; + maybe_zonecut = ISC_FALSE; + at_zonecut = ISC_TRUE; + /* + * It is not clear if KEY should still be + * allowed at the parent side of the zone + * cut or not. It is needed for RFC3007 + * validated updates. + */ + if ((search.options & DNS_DBFIND_GLUEOK) == 0 + && type != dns_rdatatype_nsec + && type != dns_rdatatype_key) { + /* + * Glue is not OK, but any answer we + * could return would be glue. Return + * the delegation. + */ + found = NULL; + break; + } + if (found != NULL && foundsig != NULL) + break; + } + + /* + * If we found a type we were looking for, + * remember it. + */ + if (header->type == type || + type == dns_rdatatype_any || + (header->type == dns_rdatatype_cname && + cname_ok)) { + /* + * We've found the answer! + */ + found = header; + if (header->type == dns_rdatatype_cname && + cname_ok) { + /* + * We may be finding a CNAME instead + * of the desired type. + * + * If we've already got the CNAME RRSIG, + * use it, otherwise change sigtype + * so that we find it. + */ + if (cnamesig != NULL) + foundsig = cnamesig; + else + sigtype = + RBTDB_RDATATYPE_SIGCNAME; + } + /* + * If we've got all we need, end the search. + */ + if (!maybe_zonecut && foundsig != NULL) + break; + } else if (header->type == sigtype) { + /* + * We've found the RRSIG rdataset for our + * target type. Remember it. + */ + foundsig = header; + /* + * If we've got all we need, end the search. + */ + if (!maybe_zonecut && found != NULL) + break; + } else if (header->type == dns_rdatatype_nsec) { + /* + * Remember a NSEC rdataset even if we're + * not specifically looking for it, because + * we might need it later. + */ + nsecheader = header; + } else if (header->type == RBTDB_RDATATYPE_SIGNSEC) { + /* + * If we need the NSEC rdataset, we'll also + * need its signature. + */ + nsecsig = header; + } else if (cname_ok && + header->type == RBTDB_RDATATYPE_SIGCNAME) { + /* + * If we get a CNAME match, we'll also need + * its signature. + */ + cnamesig = header; + } + } + } + + if (empty_node) { + /* + * We have an exact match for the name, but there are no + * active rdatasets in the desired version. That means that + * this node doesn't exist in the desired version, and that + * we really have a partial match. + */ + if (!wild) { + lock = &search.rbtdb->node_locks[node->locknum].lock; + NODE_UNLOCK(lock, isc_rwlocktype_read); + goto partial_match; + } + } + + /* + * If we didn't find what we were looking for... + */ + if (found == NULL) { + if (search.zonecut != NULL) { + /* + * We were trying to find glue at a node beneath a + * zone cut, but didn't. + * + * Return the delegation. + */ + lock = &search.rbtdb->node_locks[node->locknum].lock; + NODE_UNLOCK(lock, isc_rwlocktype_read); + result = setup_delegation(&search, nodep, foundname, + rdataset, sigrdataset); + goto tree_exit; + } + /* + * The desired type doesn't exist. + */ + result = DNS_R_NXRRSET; + if (search.rbtdb->secure && + (nsecheader == NULL || nsecsig == NULL)) { + /* + * The zone is secure but there's no NSEC, + * or the NSEC has no signature! + */ + if (!wild) { + result = DNS_R_BADDB; + goto node_exit; + } + + lock = &search.rbtdb->node_locks[node->locknum].lock; + NODE_UNLOCK(lock, isc_rwlocktype_read); + result = find_closest_nsec(&search, nodep, foundname, + rdataset, sigrdataset, + search.rbtdb->secure); + if (result == ISC_R_SUCCESS) + result = DNS_R_EMPTYWILD; + goto tree_exit; + } + if ((search.options & DNS_DBFIND_FORCENSEC) != 0 && + nsecheader == NULL) + { + /* + * There's no NSEC record, and we were told + * to find one. + */ + result = DNS_R_BADDB; + goto node_exit; + } + if (nodep != NULL) { + new_reference(search.rbtdb, node); + *nodep = node; + } + if (search.rbtdb->secure || + (search.options & DNS_DBFIND_FORCENSEC) != 0) + { + bind_rdataset(search.rbtdb, node, nsecheader, + 0, rdataset); + if (nsecsig != NULL) + bind_rdataset(search.rbtdb, node, + nsecsig, 0, sigrdataset); + } + if (wild) + foundname->attributes |= DNS_NAMEATTR_WILDCARD; + goto node_exit; + } + + /* + * We found what we were looking for, or we found a CNAME. + */ + + if (type != found->type && + type != dns_rdatatype_any && + found->type == dns_rdatatype_cname) { + /* + * We weren't doing an ANY query and we found a CNAME instead + * of the type we were looking for, so we need to indicate + * that result to the caller. + */ + result = DNS_R_CNAME; + } else if (search.zonecut != NULL) { + /* + * If we're beneath a zone cut, we must indicate that the + * result is glue, unless we're actually at the zone cut + * and the type is NSEC or KEY. + */ + if (search.zonecut == node) { + /* + * It is not clear if KEY should still be + * allowed at the parent side of the zone + * cut or not. It is needed for RFC3007 + * validated updates. + */ + if (type == dns_rdatatype_nsec || + type == dns_rdatatype_key) + result = ISC_R_SUCCESS; + else if (type == dns_rdatatype_any) + result = DNS_R_ZONECUT; + else + result = DNS_R_GLUE; + } else + result = DNS_R_GLUE; + /* + * We might have found data that isn't glue, but was occluded + * by a dynamic update. If the caller cares about this, they + * will have told us to validate glue. + * + * XXX We should cache the glue validity state! + */ + if (result == DNS_R_GLUE && + (search.options & DNS_DBFIND_VALIDATEGLUE) != 0 && + !valid_glue(&search, foundname, type, node)) { + lock = &search.rbtdb->node_locks[node->locknum].lock; + NODE_UNLOCK(lock, isc_rwlocktype_read); + result = setup_delegation(&search, nodep, foundname, + rdataset, sigrdataset); + goto tree_exit; + } + } else { + /* + * An ordinary successful query! + */ + result = ISC_R_SUCCESS; + } + + if (nodep != NULL) { + if (!at_zonecut) + new_reference(search.rbtdb, node); + else + search.need_cleanup = ISC_FALSE; + *nodep = node; + } + + if (type != dns_rdatatype_any) { + bind_rdataset(search.rbtdb, node, found, 0, rdataset); + if (foundsig != NULL) + bind_rdataset(search.rbtdb, node, foundsig, 0, + sigrdataset); + } + + if (wild) + foundname->attributes |= DNS_NAMEATTR_WILDCARD; node_exit: - NODE_UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock), - isc_rwlocktype_read); + NODE_UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock), + isc_rwlocktype_read); tree_exit: - RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); + RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); - /* - * If we found a zonecut but aren't going to use it, we have to - * let go of it. - */ - if (search.need_cleanup) { - node = search.zonecut; - lock = &(search.rbtdb->node_locks[node->locknum].lock); + /* + * If we found a zonecut but aren't going to use it, we have to + * let go of it. + */ + if (search.need_cleanup) { + node = search.zonecut; + lock = &(search.rbtdb->node_locks[node->locknum].lock); - NODE_LOCK(lock, isc_rwlocktype_read); - decrement_reference(search.rbtdb, node, 0, - isc_rwlocktype_read, isc_rwlocktype_none); - NODE_UNLOCK(lock, isc_rwlocktype_read); - } + NODE_LOCK(lock, isc_rwlocktype_read); + decrement_reference(search.rbtdb, node, 0, + isc_rwlocktype_read, isc_rwlocktype_none); + NODE_UNLOCK(lock, isc_rwlocktype_read); + } - if (close_version) - closeversion(db, &version, ISC_FALSE); + if (close_version) + closeversion(db, &version, ISC_FALSE); - dns_rbtnodechain_reset(&search.chain); + dns_rbtnodechain_reset(&search.chain); - return (result); + return (result); } static isc_result_t zone_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, - isc_stdtime_t now, dns_dbnode_t **nodep, - dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) + isc_stdtime_t now, dns_dbnode_t **nodep, + dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - UNUSED(db); - UNUSED(name); - UNUSED(options); - UNUSED(now); - UNUSED(nodep); - UNUSED(foundname); - UNUSED(rdataset); - UNUSED(sigrdataset); - - FATAL_ERROR(__FILE__, __LINE__, "zone_findzonecut() called!"); - - return (ISC_R_NOTIMPLEMENTED); + UNUSED(db); + UNUSED(name); + UNUSED(options); + UNUSED(now); + UNUSED(nodep); + UNUSED(foundname); + UNUSED(rdataset); + UNUSED(sigrdataset); + + FATAL_ERROR(__FILE__, __LINE__, "zone_findzonecut() called!"); + + return (ISC_R_NOTIMPLEMENTED); } static isc_result_t cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) { - rbtdb_search_t *search = arg; - rdatasetheader_t *header, *header_prev, *header_next; - rdatasetheader_t *dname_header, *sigdname_header; - isc_result_t result; - nodelock_t *lock; - isc_rwlocktype_t locktype; - - /* XXX comment */ - - REQUIRE(search->zonecut == NULL); - - /* - * Keep compiler silent. - */ - UNUSED(name); - - lock = &(search->rbtdb->node_locks[node->locknum].lock); - locktype = isc_rwlocktype_read; - NODE_LOCK(lock, locktype); - - /* - * Look for a DNAME or RRSIG DNAME rdataset. - */ - dname_header = NULL; - sigdname_header = NULL; - header_prev = NULL; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (header->ttl <= search->now) { - /* - * This rdataset is stale. If no one else is - * using the node, we can clean it up right - * now, otherwise we mark it as stale, and - * the node as dirty, so it will get cleaned - * up later. - */ - if ((header->ttl <= search->now - RBTDB_VIRTUAL) && - (locktype == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { - /* - * We update the node's status only when we - * can get write access; otherwise, we leave - * others to this work. Periodical cleaning - * will eventually take the job as the last - * resort. - * We won't downgrade the lock, since other - * rdatasets are probably stale, too. - */ - locktype = isc_rwlocktype_write; - - if (dns_rbtnode_refcurrent(node) == 0) { - isc_mem_t *mctx; - - /* - * header->down can be non-NULL if the - * refcount has just decremented to 0 - * but decrement_reference() has not - * performed clean_cache_node(), in - * which case we need to purge the - * stale headers first. - */ - mctx = search->rbtdb->common.mctx; - clean_stale_headers(mctx, header); - if (header_prev != NULL) - header_prev->next = - header->next; - else - node->data = header->next; - free_rdataset(mctx, header); - } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; - header_prev = header; - } - } else - header_prev = header; - } else if (header->type == dns_rdatatype_dname && - EXISTS(header)) { - dname_header = header; - header_prev = header; - } else if (header->type == RBTDB_RDATATYPE_SIGDNAME && - EXISTS(header)) { - sigdname_header = header; - header_prev = header; - } else - header_prev = header; - } - - if (dname_header != NULL && - (dname_header->trust != dns_trust_pending || - (search->options & DNS_DBFIND_PENDINGOK) != 0)) { - /* - * We increment the reference count on node to ensure that - * search->zonecut_rdataset will still be valid later. - */ - new_reference(search->rbtdb, node); - search->zonecut = node; - search->zonecut_rdataset = dname_header; - search->zonecut_sigrdataset = sigdname_header; - search->need_cleanup = ISC_TRUE; - result = DNS_R_PARTIALMATCH; - } else - result = DNS_R_CONTINUE; - - NODE_UNLOCK(lock, locktype); - - return (result); + rbtdb_search_t *search = arg; + rdatasetheader_t *header, *header_prev, *header_next; + rdatasetheader_t *dname_header, *sigdname_header; + isc_result_t result; + nodelock_t *lock; + isc_rwlocktype_t locktype; + + /* XXX comment */ + + REQUIRE(search->zonecut == NULL); + + /* + * Keep compiler silent. + */ + UNUSED(name); + + lock = &(search->rbtdb->node_locks[node->locknum].lock); + locktype = isc_rwlocktype_read; + NODE_LOCK(lock, locktype); + + /* + * Look for a DNAME or RRSIG DNAME rdataset. + */ + dname_header = NULL; + sigdname_header = NULL; + header_prev = NULL; + for (header = node->data; header != NULL; header = header_next) { + header_next = header->next; + if (header->rdh_ttl <= search->now) { + /* + * This rdataset is stale. If no one else is + * using the node, we can clean it up right + * now, otherwise we mark it as stale, and + * the node as dirty, so it will get cleaned + * up later. + */ + if ((header->rdh_ttl <= search->now - RBTDB_VIRTUAL) && + (locktype == isc_rwlocktype_write || + NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { + /* + * We update the node's status only when we + * can get write access; otherwise, we leave + * others to this work. Periodical cleaning + * will eventually take the job as the last + * resort. + * We won't downgrade the lock, since other + * rdatasets are probably stale, too. + */ + locktype = isc_rwlocktype_write; + + if (dns_rbtnode_refcurrent(node) == 0) { + isc_mem_t *mctx; + + /* + * header->down can be non-NULL if the + * refcount has just decremented to 0 + * but decrement_reference() has not + * performed clean_cache_node(), in + * which case we need to purge the + * stale headers first. + */ + mctx = search->rbtdb->common.mctx; + clean_stale_headers(search->rbtdb, + mctx, + header); + if (header_prev != NULL) + header_prev->next = + header->next; + else + node->data = header->next; + free_rdataset(search->rbtdb, mctx, + header); + } else { + header->attributes |= + RDATASET_ATTR_STALE; + node->dirty = 1; + header_prev = header; + } + } else + header_prev = header; + } else if (header->type == dns_rdatatype_dname && + EXISTS(header)) { + dname_header = header; + header_prev = header; + } else if (header->type == RBTDB_RDATATYPE_SIGDNAME && + EXISTS(header)) { + sigdname_header = header; + header_prev = header; + } else + header_prev = header; + } + + if (dname_header != NULL && + (dname_header->trust != dns_trust_pending || + (search->options & DNS_DBFIND_PENDINGOK) != 0)) { + /* + * We increment the reference count on node to ensure that + * search->zonecut_rdataset will still be valid later. + */ + new_reference(search->rbtdb, node); + search->zonecut = node; + search->zonecut_rdataset = dname_header; + search->zonecut_sigrdataset = sigdname_header; + search->need_cleanup = ISC_TRUE; + result = DNS_R_PARTIALMATCH; + } else + result = DNS_R_CONTINUE; + + NODE_UNLOCK(lock, locktype); + + return (result); } static inline isc_result_t find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, - dns_dbnode_t **nodep, dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - unsigned int i; - dns_rbtnode_t *level_node; - rdatasetheader_t *header, *header_prev, *header_next; - rdatasetheader_t *found, *foundsig; - isc_result_t result = ISC_R_NOTFOUND; - dns_name_t name; - dns_rbtdb_t *rbtdb; - isc_boolean_t done; - nodelock_t *lock; - isc_rwlocktype_t locktype; - - /* - * Caller must be holding the tree lock. - */ - - rbtdb = search->rbtdb; - i = search->chain.level_matches; - done = ISC_FALSE; - do { - locktype = isc_rwlocktype_read; - lock = &rbtdb->node_locks[node->locknum].lock; - NODE_LOCK(lock, locktype); - - /* - * Look for NS and RRSIG NS rdatasets. - */ - found = NULL; - foundsig = NULL; - header_prev = NULL; - for (header = node->data; - header != NULL; - header = header_next) { - header_next = header->next; - if (header->ttl <= search->now) { - /* - * This rdataset is stale. If no one else is - * using the node, we can clean it up right - * now, otherwise we mark it as stale, and - * the node as dirty, so it will get cleaned - * up later. - */ - if ((header->ttl <= search->now - - RBTDB_VIRTUAL) && - (locktype == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { - /* - * We update the node's status only - * when we can get write access. - */ - locktype = isc_rwlocktype_write; - - if (dns_rbtnode_refcurrent(node) - == 0) { - isc_mem_t *m; - - m = search->rbtdb->common.mctx; - clean_stale_headers(m, header); - if (header_prev != NULL) - header_prev->next = - header->next; - else - node->data = - header->next; - free_rdataset(m, header); - } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; - header_prev = header; - } - } else - header_prev = header; - } else if (EXISTS(header)) { - /* - * We've found an extant rdataset. See if - * we're interested in it. - */ - if (header->type == dns_rdatatype_ns) { - found = header; - if (foundsig != NULL) - break; - } else if (header->type == - RBTDB_RDATATYPE_SIGNS) { - foundsig = header; - if (found != NULL) - break; - } - header_prev = header; - } else - header_prev = header; - } - - if (found != NULL) { - /* - * If we have to set foundname, we do it before - * anything else. If we were to set foundname after - * we had set nodep or bound the rdataset, then we'd - * have to undo that work if dns_name_concatenate() - * failed. By setting foundname first, there's - * nothing to undo if we have trouble. - */ - if (foundname != NULL) { - dns_name_init(&name, NULL); - dns_rbt_namefromnode(node, &name); - result = dns_name_copy(&name, foundname, NULL); - while (result == ISC_R_SUCCESS && i > 0) { - i--; - level_node = search->chain.levels[i]; - dns_name_init(&name, NULL); - dns_rbt_namefromnode(level_node, - &name); - result = - dns_name_concatenate(foundname, - &name, - foundname, - NULL); - } - if (result != ISC_R_SUCCESS) { - *nodep = NULL; - goto node_exit; - } - } - result = DNS_R_DELEGATION; - if (nodep != NULL) { - new_reference(search->rbtdb, node); - *nodep = node; - } - bind_rdataset(search->rbtdb, node, found, search->now, - rdataset); - if (foundsig != NULL) - bind_rdataset(search->rbtdb, node, foundsig, - search->now, sigrdataset); - } - - node_exit: - NODE_UNLOCK(lock, locktype); - - if (found == NULL && i > 0) { - i--; - node = search->chain.levels[i]; - } else - done = ISC_TRUE; - - } while (!done); - - return (result); + unsigned int i; + dns_rbtnode_t *level_node; + rdatasetheader_t *header, *header_prev, *header_next; + rdatasetheader_t *found, *foundsig; + isc_result_t result = ISC_R_NOTFOUND; + dns_name_t name; + dns_rbtdb_t *rbtdb; + isc_boolean_t done; + nodelock_t *lock; + isc_rwlocktype_t locktype; + + /* + * Caller must be holding the tree lock. + */ + + rbtdb = search->rbtdb; + i = search->chain.level_matches; + done = ISC_FALSE; + do { + locktype = isc_rwlocktype_read; + lock = &rbtdb->node_locks[node->locknum].lock; + NODE_LOCK(lock, locktype); + + /* + * Look for NS and RRSIG NS rdatasets. + */ + found = NULL; + foundsig = NULL; + header_prev = NULL; + for (header = node->data; + header != NULL; + header = header_next) { + header_next = header->next; + if (header->rdh_ttl <= search->now) { + /* + * This rdataset is stale. If no one else is + * using the node, we can clean it up right + * now, otherwise we mark it as stale, and + * the node as dirty, so it will get cleaned + * up later. + */ + if ((header->rdh_ttl <= search->now - + RBTDB_VIRTUAL) && + (locktype == isc_rwlocktype_write || + NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { + /* + * We update the node's status only + * when we can get write access. + */ + locktype = isc_rwlocktype_write; + + if (dns_rbtnode_refcurrent(node) + == 0) { + isc_mem_t *m; + + m = search->rbtdb->common.mctx; + clean_stale_headers( + search->rbtdb, + m, header); + if (header_prev != NULL) + header_prev->next = + header->next; + else + node->data = + header->next; + free_rdataset(rbtdb, m, + header); + } else { + header->attributes |= + RDATASET_ATTR_STALE; + node->dirty = 1; + header_prev = header; + } + } else + header_prev = header; + } else if (EXISTS(header)) { + /* + * We've found an extant rdataset. See if + * we're interested in it. + */ + if (header->type == dns_rdatatype_ns) { + found = header; + if (foundsig != NULL) + break; + } else if (header->type == + RBTDB_RDATATYPE_SIGNS) { + foundsig = header; + if (found != NULL) + break; + } + header_prev = header; + } else + header_prev = header; + } + + if (found != NULL) { + /* + * If we have to set foundname, we do it before + * anything else. If we were to set foundname after + * we had set nodep or bound the rdataset, then we'd + * have to undo that work if dns_name_concatenate() + * failed. By setting foundname first, there's + * nothing to undo if we have trouble. + */ + if (foundname != NULL) { + dns_name_init(&name, NULL); + dns_rbt_namefromnode(node, &name); + result = dns_name_copy(&name, foundname, NULL); + while (result == ISC_R_SUCCESS && i > 0) { + i--; + level_node = search->chain.levels[i]; + dns_name_init(&name, NULL); + dns_rbt_namefromnode(level_node, + &name); + result = + dns_name_concatenate(foundname, + &name, + foundname, + NULL); + } + if (result != ISC_R_SUCCESS) { + *nodep = NULL; + goto node_exit; + } + } + result = DNS_R_DELEGATION; + if (nodep != NULL) { + new_reference(search->rbtdb, node); + *nodep = node; + } + bind_rdataset(search->rbtdb, node, found, search->now, + rdataset); + if (foundsig != NULL) + bind_rdataset(search->rbtdb, node, foundsig, + search->now, sigrdataset); + if (need_headerupdate(found, search->now) || + (foundsig != NULL && + need_headerupdate(foundsig, search->now))) { + if (locktype != isc_rwlocktype_write) { + NODE_UNLOCK(lock, locktype); + NODE_LOCK(lock, isc_rwlocktype_write); + locktype = isc_rwlocktype_write; + } + if (need_headerupdate(found, search->now)) + update_header(search->rbtdb, found, + search->now); + if (foundsig != NULL && + need_headerupdate(foundsig, search->now)) { + update_header(search->rbtdb, foundsig, + search->now); + } + } + } + + node_exit: + NODE_UNLOCK(lock, locktype); + + if (found == NULL && i > 0) { + i--; + node = search->chain.levels[i]; + } else + done = ISC_TRUE; + + } while (!done); + + return (result); } static isc_result_t find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, - isc_stdtime_t now, dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) + isc_stdtime_t now, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - dns_rbtnode_t *node; - rdatasetheader_t *header, *header_next, *header_prev; - rdatasetheader_t *found, *foundsig; - isc_boolean_t empty_node; - isc_result_t result; - dns_fixedname_t fname, forigin; - dns_name_t *name, *origin; - rbtdb_rdatatype_t matchtype, sigmatchtype; - nodelock_t *lock; - isc_rwlocktype_t locktype; - - matchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_nsec, 0); - sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, - dns_rdatatype_nsec); - - do { - node = NULL; - dns_fixedname_init(&fname); - name = dns_fixedname_name(&fname); - dns_fixedname_init(&forigin); - origin = dns_fixedname_name(&forigin); - result = dns_rbtnodechain_current(&search->chain, name, - origin, &node); - if (result != ISC_R_SUCCESS) - return (result); - locktype = isc_rwlocktype_read; - lock = &(search->rbtdb->node_locks[node->locknum].lock); - NODE_LOCK(lock, locktype); - found = NULL; - foundsig = NULL; - empty_node = ISC_TRUE; - header_prev = NULL; - for (header = node->data; - header != NULL; - header = header_next) { - header_next = header->next; - if (header->ttl <= now) { - /* - * This rdataset is stale. If no one else is - * using the node, we can clean it up right - * now, otherwise we mark it as stale, and the - * node as dirty, so it will get cleaned up - * later. - */ - if ((header->ttl <= now - RBTDB_VIRTUAL) && - (locktype == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { - /* - * We update the node's status only - * when we can get write access. - */ - locktype = isc_rwlocktype_write; - - if (dns_rbtnode_refcurrent(node) - == 0) { - isc_mem_t *m; - - m = search->rbtdb->common.mctx; - clean_stale_headers(m, header); - if (header_prev != NULL) - header_prev->next = - header->next; - else - node->data = header->next; - free_rdataset(m, header); - } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; - header_prev = header; - } - } else - header_prev = header; - continue; - } - if (NONEXISTENT(header) || - RBTDB_RDATATYPE_BASE(header->type) == 0) { - header_prev = header; - continue; - } - empty_node = ISC_FALSE; - if (header->type == matchtype) - found = header; - else if (header->type == sigmatchtype) - foundsig = header; - header_prev = header; - } - if (found != NULL) { - result = dns_name_concatenate(name, origin, - foundname, NULL); - if (result != ISC_R_SUCCESS) - goto unlock_node; - bind_rdataset(search->rbtdb, node, found, - now, rdataset); - if (foundsig != NULL) - bind_rdataset(search->rbtdb, node, foundsig, - now, sigrdataset); - new_reference(search->rbtdb, node); - *nodep = node; - result = DNS_R_COVERINGNSEC; - } else if (!empty_node) { - result = ISC_R_NOTFOUND; - } else - result = dns_rbtnodechain_prev(&search->chain, NULL, - NULL); + dns_rbtnode_t *node; + rdatasetheader_t *header, *header_next, *header_prev; + rdatasetheader_t *found, *foundsig; + isc_boolean_t empty_node; + isc_result_t result; + dns_fixedname_t fname, forigin; + dns_name_t *name, *origin; + rbtdb_rdatatype_t matchtype, sigmatchtype; + nodelock_t *lock; + isc_rwlocktype_t locktype; + + matchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_nsec, 0); + sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, + dns_rdatatype_nsec); + + do { + node = NULL; + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + result = dns_rbtnodechain_current(&search->chain, name, + origin, &node); + if (result != ISC_R_SUCCESS) + return (result); + locktype = isc_rwlocktype_read; + lock = &(search->rbtdb->node_locks[node->locknum].lock); + NODE_LOCK(lock, locktype); + found = NULL; + foundsig = NULL; + empty_node = ISC_TRUE; + header_prev = NULL; + for (header = node->data; + header != NULL; + header = header_next) { + header_next = header->next; + if (header->rdh_ttl <= now) { + /* + * This rdataset is stale. If no one else is + * using the node, we can clean it up right + * now, otherwise we mark it as stale, and the + * node as dirty, so it will get cleaned up + * later. + */ + if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + (locktype == isc_rwlocktype_write || + NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { + /* + * We update the node's status only + * when we can get write access. + */ + locktype = isc_rwlocktype_write; + + if (dns_rbtnode_refcurrent(node) + == 0) { + isc_mem_t *m; + + m = search->rbtdb->common.mctx; + clean_stale_headers( + search->rbtdb, + m, header); + if (header_prev != NULL) + header_prev->next = + header->next; + else + node->data = header->next; + free_rdataset(search->rbtdb, m, + header); + } else { + header->attributes |= + RDATASET_ATTR_STALE; + node->dirty = 1; + header_prev = header; + } + } else + header_prev = header; + continue; + } + if (NONEXISTENT(header) || + RBTDB_RDATATYPE_BASE(header->type) == 0) { + header_prev = header; + continue; + } + empty_node = ISC_FALSE; + if (header->type == matchtype) + found = header; + else if (header->type == sigmatchtype) + foundsig = header; + header_prev = header; + } + if (found != NULL) { + result = dns_name_concatenate(name, origin, + foundname, NULL); + if (result != ISC_R_SUCCESS) + goto unlock_node; + bind_rdataset(search->rbtdb, node, found, + now, rdataset); + if (foundsig != NULL) + bind_rdataset(search->rbtdb, node, foundsig, + now, sigrdataset); + new_reference(search->rbtdb, node); + *nodep = node; + result = DNS_R_COVERINGNSEC; + } else if (!empty_node) { + result = ISC_R_NOTFOUND; + } else + result = dns_rbtnodechain_prev(&search->chain, NULL, + NULL); unlock_node: - NODE_UNLOCK(lock, locktype); - } while (empty_node && result == ISC_R_SUCCESS); - return (result); + NODE_UNLOCK(lock, locktype); + } while (empty_node && result == ISC_R_SUCCESS); + return (result); } static isc_result_t cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, - dns_dbnode_t **nodep, dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - dns_rbtnode_t *node = NULL; - isc_result_t result; - rbtdb_search_t search; - isc_boolean_t cname_ok = ISC_TRUE; - isc_boolean_t empty_node; - nodelock_t *lock; - isc_rwlocktype_t locktype; - rdatasetheader_t *header, *header_prev, *header_next; - rdatasetheader_t *found, *nsheader; - rdatasetheader_t *foundsig, *nssig, *cnamesig; - rbtdb_rdatatype_t sigtype, negtype; - - UNUSED(version); - - search.rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(search.rbtdb)); - REQUIRE(version == NULL); - - if (now == 0) - isc_stdtime_get(&now); - - search.rbtversion = NULL; - search.serial = 1; - search.options = options; - search.copy_name = ISC_FALSE; - search.need_cleanup = ISC_FALSE; - search.wild = ISC_FALSE; - search.zonecut = NULL; - dns_fixedname_init(&search.zonecut_name); - dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx); - search.now = now; - - RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); - - /* - * Search down from the root of the tree. If, while going down, we - * encounter a callback node, cache_zonecut_callback() will search the - * rdatasets at the zone cut for a DNAME rdataset. - */ - result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node, - &search.chain, DNS_RBTFIND_EMPTYDATA, - cache_zonecut_callback, &search); - - if (result == DNS_R_PARTIALMATCH) { - if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) { - result = find_coveringnsec(&search, nodep, now, - foundname, rdataset, - sigrdataset); - if (result == DNS_R_COVERINGNSEC) - goto tree_exit; - } - if (search.zonecut != NULL) { - result = setup_delegation(&search, nodep, foundname, - rdataset, sigrdataset); - goto tree_exit; - } else { - find_ns: - result = find_deepest_zonecut(&search, node, nodep, - foundname, rdataset, - sigrdataset); - goto tree_exit; - } - } else if (result != ISC_R_SUCCESS) - goto tree_exit; - - /* - * Certain DNSSEC types are not subject to CNAME matching - * (RFC4035, section 2.5 and RFC3007). - * - * We don't check for RRSIG, because we don't store RRSIG records - * directly. - */ - if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) - cname_ok = ISC_FALSE; - - /* - * We now go looking for rdata... - */ - - lock = &(search.rbtdb->node_locks[node->locknum].lock); - locktype = isc_rwlocktype_read; - NODE_LOCK(lock, locktype); - - found = NULL; - foundsig = NULL; - sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); - negtype = RBTDB_RDATATYPE_VALUE(0, type); - nsheader = NULL; - nssig = NULL; - cnamesig = NULL; - empty_node = ISC_TRUE; - header_prev = NULL; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (header->ttl <= now) { - /* - * This rdataset is stale. If no one else is using the - * node, we can clean it up right now, otherwise we - * mark it as stale, and the node as dirty, so it will - * get cleaned up later. - */ - if ((header->ttl <= now - RBTDB_VIRTUAL) && - (locktype == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { - /* - * We update the node's status only when we - * can get write access. - */ - locktype = isc_rwlocktype_write; - - if (dns_rbtnode_refcurrent(node) == 0) { - isc_mem_t *mctx; - - mctx = search.rbtdb->common.mctx; - clean_stale_headers(mctx, header); - if (header_prev != NULL) - header_prev->next = - header->next; - else - node->data = header->next; - free_rdataset(mctx, header); - } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; - header_prev = header; - } - } else - header_prev = header; - } else if (EXISTS(header)) { - /* - * We now know that there is at least one active - * non-stale rdataset at this node. - */ - empty_node = ISC_FALSE; - - /* - * If we found a type we were looking for, remember - * it. - */ - if (header->type == type || - (type == dns_rdatatype_any && - RBTDB_RDATATYPE_BASE(header->type) != 0) || - (cname_ok && header->type == - dns_rdatatype_cname)) { - /* - * We've found the answer. - */ - found = header; - if (header->type == dns_rdatatype_cname && - cname_ok && - cnamesig != NULL) { - /* - * If we've already got the CNAME RRSIG, - * use it, otherwise change sigtype - * so that we find it. - */ - if (cnamesig != NULL) - foundsig = cnamesig; - else - sigtype = - RBTDB_RDATATYPE_SIGCNAME; - foundsig = cnamesig; - } - } else if (header->type == sigtype) { - /* - * We've found the RRSIG rdataset for our - * target type. Remember it. - */ - foundsig = header; - } else if (header->type == RBTDB_RDATATYPE_NCACHEANY || - header->type == negtype) { - /* - * We've found a negative cache entry. - */ - found = header; - } else if (header->type == dns_rdatatype_ns) { - /* - * Remember a NS rdataset even if we're - * not specifically looking for it, because - * we might need it later. - */ - nsheader = header; - } else if (header->type == RBTDB_RDATATYPE_SIGNS) { - /* - * If we need the NS rdataset, we'll also - * need its signature. - */ - nssig = header; - } else if (cname_ok && - header->type == RBTDB_RDATATYPE_SIGCNAME) { - /* - * If we get a CNAME match, we'll also need - * its signature. - */ - cnamesig = header; - } - header_prev = header; - } else - header_prev = header; - } - - if (empty_node) { - /* - * We have an exact match for the name, but there are no - * extant rdatasets. That means that this node doesn't - * meaningfully exist, and that we really have a partial match. - */ - NODE_UNLOCK(lock, locktype); - goto find_ns; - } - - /* - * If we didn't find what we were looking for... - */ - if (found == NULL || - (found->trust == dns_trust_glue && - ((options & DNS_DBFIND_GLUEOK) == 0)) || - (found->trust == dns_trust_pending && - ((options & DNS_DBFIND_PENDINGOK) == 0))) { - /* - * If there is an NS rdataset at this node, then this is the - * deepest zone cut. - */ - if (nsheader != NULL) { - if (nodep != NULL) { - new_reference(search.rbtdb, node); - *nodep = node; - } - bind_rdataset(search.rbtdb, node, nsheader, search.now, - rdataset); - if (nssig != NULL) - bind_rdataset(search.rbtdb, node, nssig, - search.now, sigrdataset); - result = DNS_R_DELEGATION; - goto node_exit; - } - - /* - * Go find the deepest zone cut. - */ - NODE_UNLOCK(lock, locktype); - goto find_ns; - } - - /* - * We found what we were looking for, or we found a CNAME. - */ - - if (nodep != NULL) { - new_reference(search.rbtdb, node); - *nodep = node; - } - - if (RBTDB_RDATATYPE_BASE(found->type) == 0) { - /* - * We found a negative cache entry. - */ - if (NXDOMAIN(found)) - result = DNS_R_NCACHENXDOMAIN; - else - result = DNS_R_NCACHENXRRSET; - } else if (type != found->type && - type != dns_rdatatype_any && - found->type == dns_rdatatype_cname) { - /* - * We weren't doing an ANY query and we found a CNAME instead - * of the type we were looking for, so we need to indicate - * that result to the caller. - */ - result = DNS_R_CNAME; - } else { - /* - * An ordinary successful query! - */ - result = ISC_R_SUCCESS; - } - - if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN || - result == DNS_R_NCACHENXRRSET) { - bind_rdataset(search.rbtdb, node, found, search.now, - rdataset); - if (foundsig != NULL) - bind_rdataset(search.rbtdb, node, foundsig, search.now, - sigrdataset); - } + dns_rbtnode_t *node = NULL; + isc_result_t result; + rbtdb_search_t search; + isc_boolean_t cname_ok = ISC_TRUE; + isc_boolean_t empty_node; + nodelock_t *lock; + isc_rwlocktype_t locktype; + rdatasetheader_t *header, *header_prev, *header_next; + rdatasetheader_t *found, *nsheader; + rdatasetheader_t *foundsig, *nssig, *cnamesig; + rdatasetheader_t *update, *updatesig; + rbtdb_rdatatype_t sigtype, negtype; + + UNUSED(version); + + search.rbtdb = (dns_rbtdb_t *)db; + + REQUIRE(VALID_RBTDB(search.rbtdb)); + REQUIRE(version == NULL); + + if (now == 0) + isc_stdtime_get(&now); + + search.rbtversion = NULL; + search.serial = 1; + search.options = options; + search.copy_name = ISC_FALSE; + search.need_cleanup = ISC_FALSE; + search.wild = ISC_FALSE; + search.zonecut = NULL; + dns_fixedname_init(&search.zonecut_name); + dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx); + search.now = now; + update = NULL; + updatesig = NULL; + + RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); + + /* + * Search down from the root of the tree. If, while going down, we + * encounter a callback node, cache_zonecut_callback() will search the + * rdatasets at the zone cut for a DNAME rdataset. + */ + result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node, + &search.chain, DNS_RBTFIND_EMPTYDATA, + cache_zonecut_callback, &search); + + if (result == DNS_R_PARTIALMATCH) { + if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) { + result = find_coveringnsec(&search, nodep, now, + foundname, rdataset, + sigrdataset); + if (result == DNS_R_COVERINGNSEC) + goto tree_exit; + } + if (search.zonecut != NULL) { + result = setup_delegation(&search, nodep, foundname, + rdataset, sigrdataset); + goto tree_exit; + } else { + find_ns: + result = find_deepest_zonecut(&search, node, nodep, + foundname, rdataset, + sigrdataset); + goto tree_exit; + } + } else if (result != ISC_R_SUCCESS) + goto tree_exit; + + /* + * Certain DNSSEC types are not subject to CNAME matching + * (RFC4035, section 2.5 and RFC3007). + * + * We don't check for RRSIG, because we don't store RRSIG records + * directly. + */ + if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) + cname_ok = ISC_FALSE; + + /* + * We now go looking for rdata... + */ + + lock = &(search.rbtdb->node_locks[node->locknum].lock); + locktype = isc_rwlocktype_read; + NODE_LOCK(lock, locktype); + + found = NULL; + foundsig = NULL; + sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); + negtype = RBTDB_RDATATYPE_VALUE(0, type); + nsheader = NULL; + nssig = NULL; + cnamesig = NULL; + empty_node = ISC_TRUE; + header_prev = NULL; + for (header = node->data; header != NULL; header = header_next) { + header_next = header->next; + if (header->rdh_ttl <= now) { + /* + * This rdataset is stale. If no one else is using the + * node, we can clean it up right now, otherwise we + * mark it as stale, and the node as dirty, so it will + * get cleaned up later. + */ + if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + (locktype == isc_rwlocktype_write || + NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { + /* + * We update the node's status only when we + * can get write access. + */ + locktype = isc_rwlocktype_write; + + if (dns_rbtnode_refcurrent(node) == 0) { + isc_mem_t *mctx; + + mctx = search.rbtdb->common.mctx; + clean_stale_headers(search.rbtdb, mctx, + header); + if (header_prev != NULL) + header_prev->next = + header->next; + else + node->data = header->next; + free_rdataset(search.rbtdb, mctx, + header); + } else { + header->attributes |= + RDATASET_ATTR_STALE; + node->dirty = 1; + header_prev = header; + } + } else + header_prev = header; + } else if (EXISTS(header)) { + /* + * We now know that there is at least one active + * non-stale rdataset at this node. + */ + empty_node = ISC_FALSE; + + /* + * If we found a type we were looking for, remember + * it. + */ + if (header->type == type || + (type == dns_rdatatype_any && + RBTDB_RDATATYPE_BASE(header->type) != 0) || + (cname_ok && header->type == + dns_rdatatype_cname)) { + /* + * We've found the answer. + */ + found = header; + if (header->type == dns_rdatatype_cname && + cname_ok && + cnamesig != NULL) { + /* + * If we've already got the CNAME RRSIG, + * use it, otherwise change sigtype + * so that we find it. + */ + if (cnamesig != NULL) + foundsig = cnamesig; + else + sigtype = + RBTDB_RDATATYPE_SIGCNAME; + foundsig = cnamesig; + } + } else if (header->type == sigtype) { + /* + * We've found the RRSIG rdataset for our + * target type. Remember it. + */ + foundsig = header; + } else if (header->type == RBTDB_RDATATYPE_NCACHEANY || + header->type == negtype) { + /* + * We've found a negative cache entry. + */ + found = header; + } else if (header->type == dns_rdatatype_ns) { + /* + * Remember a NS rdataset even if we're + * not specifically looking for it, because + * we might need it later. + */ + nsheader = header; + } else if (header->type == RBTDB_RDATATYPE_SIGNS) { + /* + * If we need the NS rdataset, we'll also + * need its signature. + */ + nssig = header; + } else if (cname_ok && + header->type == RBTDB_RDATATYPE_SIGCNAME) { + /* + * If we get a CNAME match, we'll also need + * its signature. + */ + cnamesig = header; + } + header_prev = header; + } else + header_prev = header; + } + + if (empty_node) { + /* + * We have an exact match for the name, but there are no + * extant rdatasets. That means that this node doesn't + * meaningfully exist, and that we really have a partial match. + */ + NODE_UNLOCK(lock, locktype); + goto find_ns; + } + + /* + * If we didn't find what we were looking for... + */ + if (found == NULL || + (found->trust == dns_trust_glue && + ((options & DNS_DBFIND_GLUEOK) == 0)) || + (found->trust == dns_trust_pending && + ((options & DNS_DBFIND_PENDINGOK) == 0))) { + /* + * If there is an NS rdataset at this node, then this is the + * deepest zone cut. + */ + if (nsheader != NULL) { + if (nodep != NULL) { + new_reference(search.rbtdb, node); + *nodep = node; + } + bind_rdataset(search.rbtdb, node, nsheader, search.now, + rdataset); + if (need_headerupdate(nsheader, search.now)) + update = nsheader; + if (nssig != NULL) { + bind_rdataset(search.rbtdb, node, nssig, + search.now, sigrdataset); + if (need_headerupdate(nssig, search.now)) + updatesig = nssig; + } + result = DNS_R_DELEGATION; + goto node_exit; + } + + /* + * Go find the deepest zone cut. + */ + NODE_UNLOCK(lock, locktype); + goto find_ns; + } + + /* + * We found what we were looking for, or we found a CNAME. + */ + + if (nodep != NULL) { + new_reference(search.rbtdb, node); + *nodep = node; + } + + if (RBTDB_RDATATYPE_BASE(found->type) == 0) { + /* + * We found a negative cache entry. + */ + if (NXDOMAIN(found)) + result = DNS_R_NCACHENXDOMAIN; + else + result = DNS_R_NCACHENXRRSET; + } else if (type != found->type && + type != dns_rdatatype_any && + found->type == dns_rdatatype_cname) { + /* + * We weren't doing an ANY query and we found a CNAME instead + * of the type we were looking for, so we need to indicate + * that result to the caller. + */ + result = DNS_R_CNAME; + } else { + /* + * An ordinary successful query! + */ + result = ISC_R_SUCCESS; + } + + if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN || + result == DNS_R_NCACHENXRRSET) { + bind_rdataset(search.rbtdb, node, found, search.now, + rdataset); + if (need_headerupdate(found, search.now)) + update = found; + if (foundsig != NULL) { + bind_rdataset(search.rbtdb, node, foundsig, search.now, + sigrdataset); + if (need_headerupdate(foundsig, search.now)) + updatesig = foundsig; + } + } node_exit: - NODE_UNLOCK(lock, locktype); + if ((update != NULL || updatesig != NULL) && + locktype != isc_rwlocktype_write) { + NODE_UNLOCK(lock, locktype); + NODE_LOCK(lock, isc_rwlocktype_write); + locktype = isc_rwlocktype_write; + } + if (update != NULL && need_headerupdate(update, search.now)) + update_header(search.rbtdb, update, search.now); + if (updatesig != NULL && need_headerupdate(updatesig, search.now)) + update_header(search.rbtdb, updatesig, search.now); + + NODE_UNLOCK(lock, locktype); tree_exit: - RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); + RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); - /* - * If we found a zonecut but aren't going to use it, we have to - * let go of it. - */ - if (search.need_cleanup) { - node = search.zonecut; - lock = &(search.rbtdb->node_locks[node->locknum].lock); + /* + * If we found a zonecut but aren't going to use it, we have to + * let go of it. + */ + if (search.need_cleanup) { + node = search.zonecut; + lock = &(search.rbtdb->node_locks[node->locknum].lock); - NODE_LOCK(lock, isc_rwlocktype_read); - decrement_reference(search.rbtdb, node, 0, - isc_rwlocktype_read, isc_rwlocktype_none); - NODE_UNLOCK(lock, isc_rwlocktype_read); - } + NODE_LOCK(lock, isc_rwlocktype_read); + decrement_reference(search.rbtdb, node, 0, + isc_rwlocktype_read, isc_rwlocktype_none); + NODE_UNLOCK(lock, isc_rwlocktype_read); + } - dns_rbtnodechain_reset(&search.chain); + dns_rbtnodechain_reset(&search.chain); - return (result); + return (result); } static isc_result_t cache_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, - isc_stdtime_t now, dns_dbnode_t **nodep, - dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) + isc_stdtime_t now, dns_dbnode_t **nodep, + dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - dns_rbtnode_t *node = NULL; - nodelock_t *lock; - isc_result_t result; - rbtdb_search_t search; - rdatasetheader_t *header, *header_prev, *header_next; - rdatasetheader_t *found, *foundsig; - unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA; - isc_rwlocktype_t locktype; - - search.rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(search.rbtdb)); - - if (now == 0) - isc_stdtime_get(&now); - - search.rbtversion = NULL; - search.serial = 1; - search.options = options; - search.copy_name = ISC_FALSE; - search.need_cleanup = ISC_FALSE; - search.wild = ISC_FALSE; - search.zonecut = NULL; - dns_fixedname_init(&search.zonecut_name); - dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx); - search.now = now; - - if ((options & DNS_DBFIND_NOEXACT) != 0) - rbtoptions |= DNS_RBTFIND_NOEXACT; - - RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); - - /* - * Search down from the root of the tree. - */ - result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node, - &search.chain, rbtoptions, NULL, &search); - - if (result == DNS_R_PARTIALMATCH) { - find_ns: - result = find_deepest_zonecut(&search, node, nodep, foundname, - rdataset, sigrdataset); - goto tree_exit; - } else if (result != ISC_R_SUCCESS) - goto tree_exit; - - /* - * We now go looking for an NS rdataset at the node. - */ - - lock = &(search.rbtdb->node_locks[node->locknum].lock); - locktype = isc_rwlocktype_read; - NODE_LOCK(lock, locktype); - - found = NULL; - foundsig = NULL; - header_prev = NULL; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (header->ttl <= now) { - /* - * This rdataset is stale. If no one else is using the - * node, we can clean it up right now, otherwise we - * mark it as stale, and the node as dirty, so it will - * get cleaned up later. - */ - if ((header->ttl <= now - RBTDB_VIRTUAL) && - (locktype == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { - /* - * We update the node's status only when we - * can get write access. - */ - locktype = isc_rwlocktype_write; - - if (dns_rbtnode_refcurrent(node) == 0) { - isc_mem_t *mctx; - - mctx = search.rbtdb->common.mctx; - clean_stale_headers(mctx, header); - if (header_prev != NULL) - header_prev->next = - header->next; - else - node->data = header->next; - free_rdataset(mctx, header); - } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; - header_prev = header; - } - } else - header_prev = header; - } else if (EXISTS(header)) { - /* - * If we found a type we were looking for, remember - * it. - */ - if (header->type == dns_rdatatype_ns) { - /* - * Remember a NS rdataset even if we're - * not specifically looking for it, because - * we might need it later. - */ - found = header; - } else if (header->type == RBTDB_RDATATYPE_SIGNS) { - /* - * If we need the NS rdataset, we'll also - * need its signature. - */ - foundsig = header; - } - header_prev = header; - } else - header_prev = header; - } - - if (found == NULL) { - /* - * No NS records here. - */ - NODE_UNLOCK(lock, locktype); - goto find_ns; - } - - if (nodep != NULL) { - new_reference(search.rbtdb, node); - *nodep = node; - } - - bind_rdataset(search.rbtdb, node, found, search.now, rdataset); - if (foundsig != NULL) - bind_rdataset(search.rbtdb, node, foundsig, search.now, - sigrdataset); - - NODE_UNLOCK(lock, locktype); + dns_rbtnode_t *node = NULL; + nodelock_t *lock; + isc_result_t result; + rbtdb_search_t search; + rdatasetheader_t *header, *header_prev, *header_next; + rdatasetheader_t *found, *foundsig; + unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA; + isc_rwlocktype_t locktype; + + search.rbtdb = (dns_rbtdb_t *)db; + + REQUIRE(VALID_RBTDB(search.rbtdb)); + + if (now == 0) + isc_stdtime_get(&now); + + search.rbtversion = NULL; + search.serial = 1; + search.options = options; + search.copy_name = ISC_FALSE; + search.need_cleanup = ISC_FALSE; + search.wild = ISC_FALSE; + search.zonecut = NULL; + dns_fixedname_init(&search.zonecut_name); + dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx); + search.now = now; + + if ((options & DNS_DBFIND_NOEXACT) != 0) + rbtoptions |= DNS_RBTFIND_NOEXACT; + + RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); + + /* + * Search down from the root of the tree. + */ + result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node, + &search.chain, rbtoptions, NULL, &search); + + if (result == DNS_R_PARTIALMATCH) { + find_ns: + result = find_deepest_zonecut(&search, node, nodep, foundname, + rdataset, sigrdataset); + goto tree_exit; + } else if (result != ISC_R_SUCCESS) + goto tree_exit; + + /* + * We now go looking for an NS rdataset at the node. + */ + + lock = &(search.rbtdb->node_locks[node->locknum].lock); + locktype = isc_rwlocktype_read; + NODE_LOCK(lock, locktype); + + found = NULL; + foundsig = NULL; + header_prev = NULL; + for (header = node->data; header != NULL; header = header_next) { + header_next = header->next; + if (header->rdh_ttl <= now) { + /* + * This rdataset is stale. If no one else is using the + * node, we can clean it up right now, otherwise we + * mark it as stale, and the node as dirty, so it will + * get cleaned up later. + */ + if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + (locktype == isc_rwlocktype_write || + NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { + /* + * We update the node's status only when we + * can get write access. + */ + locktype = isc_rwlocktype_write; + + if (dns_rbtnode_refcurrent(node) == 0) { + isc_mem_t *mctx; + + mctx = search.rbtdb->common.mctx; + clean_stale_headers(search.rbtdb, mctx, + header); + if (header_prev != NULL) + header_prev->next = + header->next; + else + node->data = header->next; + free_rdataset(search.rbtdb, mctx, + header); + } else { + header->attributes |= + RDATASET_ATTR_STALE; + node->dirty = 1; + header_prev = header; + } + } else + header_prev = header; + } else if (EXISTS(header)) { + /* + * If we found a type we were looking for, remember + * it. + */ + if (header->type == dns_rdatatype_ns) { + /* + * Remember a NS rdataset even if we're + * not specifically looking for it, because + * we might need it later. + */ + found = header; + } else if (header->type == RBTDB_RDATATYPE_SIGNS) { + /* + * If we need the NS rdataset, we'll also + * need its signature. + */ + foundsig = header; + } + header_prev = header; + } else + header_prev = header; + } + + if (found == NULL) { + /* + * No NS records here. + */ + NODE_UNLOCK(lock, locktype); + goto find_ns; + } + + if (nodep != NULL) { + new_reference(search.rbtdb, node); + *nodep = node; + } + + bind_rdataset(search.rbtdb, node, found, search.now, rdataset); + if (foundsig != NULL) + bind_rdataset(search.rbtdb, node, foundsig, search.now, + sigrdataset); + + if (need_headerupdate(found, search.now) || + (foundsig != NULL && need_headerupdate(foundsig, search.now))) { + if (locktype != isc_rwlocktype_write) { + NODE_UNLOCK(lock, locktype); + NODE_LOCK(lock, isc_rwlocktype_write); + locktype = isc_rwlocktype_write; + } + if (need_headerupdate(found, search.now)) + update_header(search.rbtdb, found, search.now); + if (foundsig != NULL && + need_headerupdate(foundsig, search.now)) { + update_header(search.rbtdb, foundsig, search.now); + } + } + + NODE_UNLOCK(lock, locktype); tree_exit: - RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); + RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read); - INSIST(!search.need_cleanup); + INSIST(!search.need_cleanup); - dns_rbtnodechain_reset(&search.chain); + dns_rbtnodechain_reset(&search.chain); - if (result == DNS_R_DELEGATION) - result = ISC_R_SUCCESS; + if (result == DNS_R_DELEGATION) + result = ISC_R_SUCCESS; - return (result); + return (result); } static void attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *node = (dns_rbtnode_t *)source; - unsigned int refs; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *node = (dns_rbtnode_t *)source; + unsigned int refs; - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(targetp != NULL && *targetp == NULL); + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(targetp != NULL && *targetp == NULL); - NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); - dns_rbtnode_refincrement(node, &refs); - INSIST(refs != 0); - NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); + NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); + dns_rbtnode_refincrement(node, &refs); + INSIST(refs != 0); + NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); - *targetp = source; + *targetp = source; } static void detachnode(dns_db_t *db, dns_dbnode_t **targetp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *node; - isc_boolean_t want_free = ISC_FALSE; - isc_boolean_t inactive = ISC_FALSE; - rbtdb_nodelock_t *nodelock; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(targetp != NULL && *targetp != NULL); - - node = (dns_rbtnode_t *)(*targetp); - nodelock = &rbtdb->node_locks[node->locknum]; - - NODE_LOCK(&nodelock->lock, isc_rwlocktype_read); - - if (decrement_reference(rbtdb, node, 0, isc_rwlocktype_read, - isc_rwlocktype_none)) { - if (isc_refcount_current(&nodelock->references) == 0 && - nodelock->exiting) { - inactive = ISC_TRUE; - } - } - - NODE_UNLOCK(&nodelock->lock, isc_rwlocktype_read); - - *targetp = NULL; - - if (inactive) { - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - rbtdb->active--; - if (rbtdb->active == 0) - want_free = ISC_TRUE; - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); - if (want_free) { - char buf[DNS_NAME_FORMATSIZE]; - if (dns_name_dynamic(&rbtdb->common.origin)) - dns_name_format(&rbtdb->common.origin, buf, - sizeof(buf)); - else - strcpy(buf, "<UNKNOWN>"); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "calling free_rbtdb(%s)", buf); - free_rbtdb(rbtdb, ISC_TRUE, NULL); - } - } + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *node; + isc_boolean_t want_free = ISC_FALSE; + isc_boolean_t inactive = ISC_FALSE; + rbtdb_nodelock_t *nodelock; + + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(targetp != NULL && *targetp != NULL); + + node = (dns_rbtnode_t *)(*targetp); + nodelock = &rbtdb->node_locks[node->locknum]; + + NODE_LOCK(&nodelock->lock, isc_rwlocktype_read); + + if (decrement_reference(rbtdb, node, 0, isc_rwlocktype_read, + isc_rwlocktype_none)) { + if (isc_refcount_current(&nodelock->references) == 0 && + nodelock->exiting) { + inactive = ISC_TRUE; + } + } + + NODE_UNLOCK(&nodelock->lock, isc_rwlocktype_read); + + *targetp = NULL; + + if (inactive) { + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + rbtdb->active--; + if (rbtdb->active == 0) + want_free = ISC_TRUE; + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + if (want_free) { + char buf[DNS_NAME_FORMATSIZE]; + if (dns_name_dynamic(&rbtdb->common.origin)) + dns_name_format(&rbtdb->common.origin, buf, + sizeof(buf)); + else + strcpy(buf, "<UNKNOWN>"); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), + "calling free_rbtdb(%s)", buf); + free_rbtdb(rbtdb, ISC_TRUE, NULL); + } + } } static isc_result_t expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = node; - rdatasetheader_t *header; - isc_boolean_t force_expire = ISC_FALSE; - /* - * These are the category and module used by the cache cleaner. - */ - isc_boolean_t log = ISC_FALSE; - isc_logcategory_t *category = DNS_LOGCATEGORY_DATABASE; - isc_logmodule_t *module = DNS_LOGMODULE_CACHE; - int level = ISC_LOG_DEBUG(2); - char printname[DNS_NAME_FORMATSIZE]; - - REQUIRE(VALID_RBTDB(rbtdb)); - - /* - * Caller must hold a tree lock. - */ - - if (now == 0) - isc_stdtime_get(&now); - - if (rbtdb->overmem) { - isc_uint32_t val; - - isc_random_get(&val); - /* - * XXXDCL Could stand to have a better policy, like LRU. - */ - force_expire = ISC_TF(rbtnode->down == NULL && val % 4 == 0); - - /* - * Note that 'log' can be true IFF rbtdb->overmem is also true. - * rbtdb->ovemem can currently only be true for cache databases - * -- hence all of the "overmem cache" log strings. - */ - log = ISC_TF(isc_log_wouldlog(dns_lctx, level)); - if (log) - isc_log_write(dns_lctx, category, module, level, - "overmem cache: %s %s", - force_expire ? "FORCE" : "check", - dns_rbt_formatnodename(rbtnode, - printname, - sizeof(printname))); - } - - /* - * We may not need write access, but this code path is not performance - * sensitive, so it should be okay to always lock as a writer. - */ - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - - for (header = rbtnode->data; header != NULL; header = header->next) - if (header->ttl <= now - RBTDB_VIRTUAL) { - /* - * We don't check if refcurrent(rbtnode) == 0 and try - * to free like we do in cache_find(), because - * refcurrent(rbtnode) must be non-zero. This is so - * because 'node' is an argument to the function. - */ - header->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; - if (log) - isc_log_write(dns_lctx, category, module, - level, "overmem cache: stale %s", - printname); - } else if (force_expire) { - if (! RETAIN(header)) { - header->ttl = 0; - header->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; - } else if (log) { - isc_log_write(dns_lctx, category, module, - level, "overmem cache: " - "reprieve by RETAIN() %s", - printname); - } - } else if (rbtdb->overmem && log) - isc_log_write(dns_lctx, category, module, level, - "overmem cache: saved %s", printname); - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - - return (ISC_R_SUCCESS); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = node; + rdatasetheader_t *header; + isc_boolean_t force_expire = ISC_FALSE; + /* + * These are the category and module used by the cache cleaner. + */ + isc_boolean_t log = ISC_FALSE; + isc_logcategory_t *category = DNS_LOGCATEGORY_DATABASE; + isc_logmodule_t *module = DNS_LOGMODULE_CACHE; + int level = ISC_LOG_DEBUG(2); + char printname[DNS_NAME_FORMATSIZE]; + + REQUIRE(VALID_RBTDB(rbtdb)); + + /* + * Caller must hold a tree lock. + */ + + if (now == 0) + isc_stdtime_get(&now); + + if (rbtdb->overmem) { + isc_uint32_t val; + + isc_random_get(&val); + /* + * XXXDCL Could stand to have a better policy, like LRU. + */ + force_expire = ISC_TF(rbtnode->down == NULL && val % 4 == 0); + + /* + * Note that 'log' can be true IFF rbtdb->overmem is also true. + * rbtdb->ovemem can currently only be true for cache databases + * -- hence all of the "overmem cache" log strings. + */ + log = ISC_TF(isc_log_wouldlog(dns_lctx, level)); + if (log) + isc_log_write(dns_lctx, category, module, level, + "overmem cache: %s %s", + force_expire ? "FORCE" : "check", + dns_rbt_formatnodename(rbtnode, + printname, + sizeof(printname))); + } + + /* + * We may not need write access, but this code path is not performance + * sensitive, so it should be okay to always lock as a writer. + */ + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + + for (header = rbtnode->data; header != NULL; header = header->next) + if (header->rdh_ttl <= now - RBTDB_VIRTUAL) { + /* + * We don't check if refcurrent(rbtnode) == 0 and try + * to free like we do in cache_find(), because + * refcurrent(rbtnode) must be non-zero. This is so + * because 'node' is an argument to the function. + */ + header->attributes |= RDATASET_ATTR_STALE; + rbtnode->dirty = 1; + if (log) + isc_log_write(dns_lctx, category, module, + level, "overmem cache: stale %s", + printname); + } else if (force_expire) { + if (! RETAIN(header)) { + set_ttl(rbtdb, header, 0); + header->attributes |= RDATASET_ATTR_STALE; + rbtnode->dirty = 1; + } else if (log) { + isc_log_write(dns_lctx, category, module, + level, "overmem cache: " + "reprieve by RETAIN() %s", + printname); + } + } else if (rbtdb->overmem && log) + isc_log_write(dns_lctx, category, module, level, + "overmem cache: saved %s", printname); + + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + + return (ISC_R_SUCCESS); } static void overmem(dns_db_t *db, isc_boolean_t overmem) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + +#ifdef LRU_DEBUG + /* XXX: see cache.c:timer_dump() */ + if ((int)overmem == -1) { + if (!IS_CACHE(rbtdb) || db->rdclass != dns_rdataclass_in) + return; /* for brevity */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_INFO, + "cache DB %p: mem inuse %lu, %u node, " + "%d/%u current/total cache, %d/%u neg, %d/%u A, %d/%u AAAA, " + "%d/%u NS, %d/%u PTR, %d/%u glue, " + "%d/%u additional, purge/scan=%u(%u expiry, %u lru)/%u, " + "overmem=%d", + rbtdb, + (unsigned long)isc_mem_inuse(rbtdb->common.mctx), + dns_rbt_nodecount(rbtdb->tree), + rbtdb->cachestat.cache_current, rbtdb->cachestat.cache_total, + rbtdb->cachestat.ncache_current, rbtdb->cachestat.ncache_total, + rbtdb->cachestat.a_current, rbtdb->cachestat.a_total, + rbtdb->cachestat.aaaa_current, rbtdb->cachestat.aaaa_total, + rbtdb->cachestat.ns_current, rbtdb->cachestat.ns_total, + rbtdb->cachestat.ptr_current, rbtdb->cachestat.ptr_total, + rbtdb->cachestat.glue_current, rbtdb->cachestat.glue_total, + rbtdb->cachestat.additional_current, + rbtdb->cachestat.additional_total, + rbtdb->cachestat.stale_purge, rbtdb->cachestat.stale_expire, + rbtdb->cachestat.stale_lru, rbtdb->cachestat.stale_scan, + rbtdb->overmem); + return; + } +#endif - if (IS_CACHE(rbtdb)) { - rbtdb->overmem = overmem; - } + if (IS_CACHE(rbtdb)) { + rbtdb->overmem = overmem; + } } static void printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = node; - isc_boolean_t first; - - REQUIRE(VALID_RBTDB(rbtdb)); - - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); - - fprintf(out, "node %p, %u references, locknum = %u\n", - rbtnode, dns_rbtnode_refcurrent(rbtnode), - rbtnode->locknum); - if (rbtnode->data != NULL) { - rdatasetheader_t *current, *top_next; - - for (current = rbtnode->data; current != NULL; - current = top_next) { - top_next = current->next; - first = ISC_TRUE; - fprintf(out, "\ttype %u", current->type); - do { - if (!first) - fprintf(out, "\t"); - first = ISC_FALSE; - fprintf(out, - "\tserial = %lu, ttl = %u, " - "trust = %u, attributes = %u\n", - (unsigned long)current->serial, - current->ttl, - current->trust, - current->attributes); - current = current->down; - } while (current != NULL); - } - } else - fprintf(out, "(empty)\n"); - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = node; + isc_boolean_t first; + + REQUIRE(VALID_RBTDB(rbtdb)); + + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + + fprintf(out, "node %p, %u references, locknum = %u\n", + rbtnode, dns_rbtnode_refcurrent(rbtnode), + rbtnode->locknum); + if (rbtnode->data != NULL) { + rdatasetheader_t *current, *top_next; + + for (current = rbtnode->data; current != NULL; + current = top_next) { + top_next = current->next; + first = ISC_TRUE; + fprintf(out, "\ttype %u", current->type); + do { + if (!first) + fprintf(out, "\t"); + first = ISC_FALSE; + fprintf(out, + "\tserial = %lu, ttl = %u, " + "trust = %u, attributes = %u\n", + (unsigned long)current->serial, + current->rdh_ttl, + current->trust, + current->attributes); + current = current->down; + } while (current != NULL); + } + } else + fprintf(out, "(empty)\n"); + + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); } static isc_result_t createiterator(dns_db_t *db, isc_boolean_t relative_names, - dns_dbiterator_t **iteratorp) + dns_dbiterator_t **iteratorp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - rbtdb_dbiterator_t *rbtdbiter; - - REQUIRE(VALID_RBTDB(rbtdb)); - - rbtdbiter = isc_mem_get(rbtdb->common.mctx, sizeof(*rbtdbiter)); - if (rbtdbiter == NULL) - return (ISC_R_NOMEMORY); - - rbtdbiter->common.methods = &dbiterator_methods; - rbtdbiter->common.db = NULL; - dns_db_attach(db, &rbtdbiter->common.db); - rbtdbiter->common.relative_names = relative_names; - rbtdbiter->common.magic = DNS_DBITERATOR_MAGIC; - rbtdbiter->common.cleaning = ISC_FALSE; - rbtdbiter->paused = ISC_TRUE; - rbtdbiter->tree_locked = isc_rwlocktype_none; - rbtdbiter->result = ISC_R_SUCCESS; - dns_fixedname_init(&rbtdbiter->name); - dns_fixedname_init(&rbtdbiter->origin); - rbtdbiter->node = NULL; - rbtdbiter->delete = 0; - memset(rbtdbiter->deletions, 0, sizeof(rbtdbiter->deletions)); - dns_rbtnodechain_init(&rbtdbiter->chain, db->mctx); - - *iteratorp = (dns_dbiterator_t *)rbtdbiter; - - return (ISC_R_SUCCESS); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + rbtdb_dbiterator_t *rbtdbiter; + + REQUIRE(VALID_RBTDB(rbtdb)); + + rbtdbiter = isc_mem_get(rbtdb->common.mctx, sizeof(*rbtdbiter)); + if (rbtdbiter == NULL) + return (ISC_R_NOMEMORY); + + rbtdbiter->common.methods = &dbiterator_methods; + rbtdbiter->common.db = NULL; + dns_db_attach(db, &rbtdbiter->common.db); + rbtdbiter->common.relative_names = relative_names; + rbtdbiter->common.magic = DNS_DBITERATOR_MAGIC; + rbtdbiter->common.cleaning = ISC_FALSE; + rbtdbiter->paused = ISC_TRUE; + rbtdbiter->tree_locked = isc_rwlocktype_none; + rbtdbiter->result = ISC_R_SUCCESS; + dns_fixedname_init(&rbtdbiter->name); + dns_fixedname_init(&rbtdbiter->origin); + rbtdbiter->node = NULL; + rbtdbiter->delete = 0; + memset(rbtdbiter->deletions, 0, sizeof(rbtdbiter->deletions)); + dns_rbtnodechain_init(&rbtdbiter->chain, db->mctx); + + *iteratorp = (dns_dbiterator_t *)rbtdbiter; + + return (ISC_R_SUCCESS); } static isc_result_t zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdatatype_t type, dns_rdatatype_t covers, - isc_stdtime_t now, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset) + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_stdtime_t now, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - rdatasetheader_t *header, *header_next, *found, *foundsig; - rbtdb_serial_t serial; - rbtdb_version_t *rbtversion = version; - isc_boolean_t close_version = ISC_FALSE; - rbtdb_rdatatype_t matchtype, sigmatchtype; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(type != dns_rdatatype_any); - - if (rbtversion == NULL) { - currentversion(db, (dns_dbversion_t **) (void *)(&rbtversion)); - close_version = ISC_TRUE; - } - serial = rbtversion->serial; - now = 0; - - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); - - found = NULL; - foundsig = NULL; - matchtype = RBTDB_RDATATYPE_VALUE(type, covers); - if (covers == 0) - sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); - else - sigmatchtype = 0; - - for (header = rbtnode->data; header != NULL; header = header_next) { - header_next = header->next; - do { - if (header->serial <= serial && - !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) { - /* - * We have an active, extant rdataset. If it's a - * type we're looking for, remember it. - */ - if (header->type == matchtype) { - found = header; - if (foundsig != NULL) - break; - } else if (header->type == sigmatchtype) { - foundsig = header; - if (found != NULL) - break; - } - } - } - if (found != NULL) { - bind_rdataset(rbtdb, rbtnode, found, now, rdataset); - if (foundsig != NULL) - bind_rdataset(rbtdb, rbtnode, foundsig, now, - sigrdataset); - } - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); - - if (close_version) - closeversion(db, (dns_dbversion_t **) (void *)(&rbtversion), - ISC_FALSE); - - if (found == NULL) - return (ISC_R_NOTFOUND); - - return (ISC_R_SUCCESS); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; + rdatasetheader_t *header, *header_next, *found, *foundsig; + rbtdb_serial_t serial; + rbtdb_version_t *rbtversion = version; + isc_boolean_t close_version = ISC_FALSE; + rbtdb_rdatatype_t matchtype, sigmatchtype; + + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(type != dns_rdatatype_any); + + if (rbtversion == NULL) { + currentversion(db, (dns_dbversion_t **) (void *)(&rbtversion)); + close_version = ISC_TRUE; + } + serial = rbtversion->serial; + now = 0; + + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + + found = NULL; + foundsig = NULL; + matchtype = RBTDB_RDATATYPE_VALUE(type, covers); + if (covers == 0) + sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); + else + sigmatchtype = 0; + + for (header = rbtnode->data; header != NULL; header = header_next) { + header_next = header->next; + do { + if (header->serial <= serial && + !IGNORE(header)) { + /* + * Is this a "this rdataset doesn't + * exist" record? + */ + if (NONEXISTENT(header)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) { + /* + * We have an active, extant rdataset. If it's a + * type we're looking for, remember it. + */ + if (header->type == matchtype) { + found = header; + if (foundsig != NULL) + break; + } else if (header->type == sigmatchtype) { + foundsig = header; + if (found != NULL) + break; + } + } + } + if (found != NULL) { + bind_rdataset(rbtdb, rbtnode, found, now, rdataset); + if (foundsig != NULL) + bind_rdataset(rbtdb, rbtnode, foundsig, now, + sigrdataset); + } + + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + + if (close_version) + closeversion(db, (dns_dbversion_t **) (void *)(&rbtversion), + ISC_FALSE); + + if (found == NULL) + return (ISC_R_NOTFOUND); + + return (ISC_R_SUCCESS); } static isc_result_t cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdatatype_t type, dns_rdatatype_t covers, - isc_stdtime_t now, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset) + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_stdtime_t now, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - rdatasetheader_t *header, *header_next, *found, *foundsig; - rbtdb_rdatatype_t matchtype, sigmatchtype, negtype; - isc_result_t result; - nodelock_t *lock; - isc_rwlocktype_t locktype; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(type != dns_rdatatype_any); - - UNUSED(version); - - result = ISC_R_SUCCESS; - - if (now == 0) - isc_stdtime_get(&now); - - lock = &rbtdb->node_locks[rbtnode->locknum].lock; - locktype = isc_rwlocktype_read; - NODE_LOCK(lock, locktype); - - found = NULL; - foundsig = NULL; - matchtype = RBTDB_RDATATYPE_VALUE(type, covers); - negtype = RBTDB_RDATATYPE_VALUE(0, type); - if (covers == 0) - sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); - else - sigmatchtype = 0; - - for (header = rbtnode->data; header != NULL; header = header_next) { - header_next = header->next; - if (header->ttl <= now) { - if ((header->ttl <= now - RBTDB_VIRTUAL) && - (locktype == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { - /* - * We update the node's status only when we - * can get write access. - */ - locktype = isc_rwlocktype_write; - - /* - * We don't check if refcurrent(rbtnode) == 0 - * and try to free like we do in cache_find(), - * because refcurrent(rbtnode) must be - * non-zero. This is so because 'node' is an - * argument to the function. - */ - header->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; - } - } else if (EXISTS(header)) { - if (header->type == matchtype) - found = header; - else if (header->type == RBTDB_RDATATYPE_NCACHEANY || - header->type == negtype) - found = header; - else if (header->type == sigmatchtype) - foundsig = header; - } - } - if (found != NULL) { - bind_rdataset(rbtdb, rbtnode, found, now, rdataset); - if (foundsig != NULL) - bind_rdataset(rbtdb, rbtnode, foundsig, now, - sigrdataset); - } - - NODE_UNLOCK(lock, locktype); - - if (found == NULL) - return (ISC_R_NOTFOUND); - - if (RBTDB_RDATATYPE_BASE(found->type) == 0) { - /* - * We found a negative cache entry. - */ - if (NXDOMAIN(found)) - result = DNS_R_NCACHENXDOMAIN; - else - result = DNS_R_NCACHENXRRSET; - } - - return (result); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; + rdatasetheader_t *header, *header_next, *found, *foundsig; + rbtdb_rdatatype_t matchtype, sigmatchtype, negtype; + isc_result_t result; + nodelock_t *lock; + isc_rwlocktype_t locktype; + + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(type != dns_rdatatype_any); + + UNUSED(version); + + result = ISC_R_SUCCESS; + + if (now == 0) + isc_stdtime_get(&now); + + lock = &rbtdb->node_locks[rbtnode->locknum].lock; + locktype = isc_rwlocktype_read; + NODE_LOCK(lock, locktype); + + found = NULL; + foundsig = NULL; + matchtype = RBTDB_RDATATYPE_VALUE(type, covers); + negtype = RBTDB_RDATATYPE_VALUE(0, type); + if (covers == 0) + sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); + else + sigmatchtype = 0; + + for (header = rbtnode->data; header != NULL; header = header_next) { + header_next = header->next; + if (header->rdh_ttl <= now) { + if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + (locktype == isc_rwlocktype_write || + NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { + /* + * We update the node's status only when we + * can get write access. + */ + locktype = isc_rwlocktype_write; + + /* + * We don't check if refcurrent(rbtnode) == 0 + * and try to free like we do in cache_find(), + * because refcurrent(rbtnode) must be + * non-zero. This is so because 'node' is an + * argument to the function. + */ + header->attributes |= RDATASET_ATTR_STALE; + rbtnode->dirty = 1; + } + } else if (EXISTS(header)) { + if (header->type == matchtype) + found = header; + else if (header->type == RBTDB_RDATATYPE_NCACHEANY || + header->type == negtype) + found = header; + else if (header->type == sigmatchtype) + foundsig = header; + } + } + if (found != NULL) { + bind_rdataset(rbtdb, rbtnode, found, now, rdataset); + if (foundsig != NULL) + bind_rdataset(rbtdb, rbtnode, foundsig, now, + sigrdataset); + } + + NODE_UNLOCK(lock, locktype); + + if (found == NULL) + return (ISC_R_NOTFOUND); + + if (RBTDB_RDATATYPE_BASE(found->type) == 0) { + /* + * We found a negative cache entry. + */ + if (NXDOMAIN(found)) + result = DNS_R_NCACHENXDOMAIN; + else + result = DNS_R_NCACHENXRRSET; + } + + return (result); } static isc_result_t allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - isc_stdtime_t now, dns_rdatasetiter_t **iteratorp) + isc_stdtime_t now, dns_rdatasetiter_t **iteratorp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - rbtdb_version_t *rbtversion = version; - rbtdb_rdatasetiter_t *iterator; - unsigned int refs; - - REQUIRE(VALID_RBTDB(rbtdb)); - - iterator = isc_mem_get(rbtdb->common.mctx, sizeof(*iterator)); - if (iterator == NULL) - return (ISC_R_NOMEMORY); - - if ((db->attributes & DNS_DBATTR_CACHE) == 0) { - now = 0; - if (rbtversion == NULL) - currentversion(db, - (dns_dbversion_t **) (void *)(&rbtversion)); - else { - unsigned int refs; - - isc_refcount_increment(&rbtversion->references, - &refs); - INSIST(refs > 1); - } - } else { - if (now == 0) - isc_stdtime_get(&now); - rbtversion = NULL; - } - - iterator->common.magic = DNS_RDATASETITER_MAGIC; - iterator->common.methods = &rdatasetiter_methods; - iterator->common.db = db; - iterator->common.node = node; - iterator->common.version = (dns_dbversion_t *)rbtversion; - iterator->common.now = now; - - NODE_STRONGLOCK(&rbtdb->node_locks[rbtnode->locknum].lock); - - dns_rbtnode_refincrement(rbtnode, &refs); - INSIST(refs != 0); - - iterator->current = NULL; - - NODE_STRONGUNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock); - - *iteratorp = (dns_rdatasetiter_t *)iterator; - - return (ISC_R_SUCCESS); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; + rbtdb_version_t *rbtversion = version; + rbtdb_rdatasetiter_t *iterator; + unsigned int refs; + + REQUIRE(VALID_RBTDB(rbtdb)); + + iterator = isc_mem_get(rbtdb->common.mctx, sizeof(*iterator)); + if (iterator == NULL) + return (ISC_R_NOMEMORY); + + if ((db->attributes & DNS_DBATTR_CACHE) == 0) { + now = 0; + if (rbtversion == NULL) + currentversion(db, + (dns_dbversion_t **) (void *)(&rbtversion)); + else { + unsigned int refs; + + isc_refcount_increment(&rbtversion->references, + &refs); + INSIST(refs > 1); + } + } else { + if (now == 0) + isc_stdtime_get(&now); + rbtversion = NULL; + } + + iterator->common.magic = DNS_RDATASETITER_MAGIC; + iterator->common.methods = &rdatasetiter_methods; + iterator->common.db = db; + iterator->common.node = node; + iterator->common.version = (dns_dbversion_t *)rbtversion; + iterator->common.now = now; + + NODE_STRONGLOCK(&rbtdb->node_locks[rbtnode->locknum].lock); + + dns_rbtnode_refincrement(rbtnode, &refs); + INSIST(refs != 0); + + iterator->current = NULL; + + NODE_STRONGUNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock); + + *iteratorp = (dns_rdatasetiter_t *)iterator; + + return (ISC_R_SUCCESS); } static isc_boolean_t cname_and_other_data(dns_rbtnode_t *node, rbtdb_serial_t serial) { - rdatasetheader_t *header, *header_next; - isc_boolean_t cname, other_data; - dns_rdatatype_t rdtype; - - /* - * The caller must hold the node lock. - */ - - /* - * Look for CNAME and "other data" rdatasets active in our version. - */ - cname = ISC_FALSE; - other_data = ISC_FALSE; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (header->type == dns_rdatatype_cname) { - /* - * Look for an active extant CNAME. - */ - do { - if (header->serial <= serial && - !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) - cname = ISC_TRUE; - } else { - /* - * Look for active extant "other data". - * - * "Other data" is any rdataset whose type is not - * KEY, RRSIG KEY, NSEC, RRSIG NSEC or RRSIG CNAME. - */ - rdtype = RBTDB_RDATATYPE_BASE(header->type); - if (rdtype == dns_rdatatype_rrsig || - rdtype == dns_rdatatype_sig) - rdtype = RBTDB_RDATATYPE_EXT(header->type); - if (rdtype != dns_rdatatype_nsec && - rdtype != dns_rdatatype_key && - rdtype != dns_rdatatype_cname) { - /* - * We've found a type that isn't - * NSEC, KEY, CNAME, or one of their - * signatures. Is it active and extant? - */ - do { - if (header->serial <= serial && - !IGNORE(header)) { - /* - * Is this a "this rdataset - * doesn't exist" record? - */ - if (NONEXISTENT(header)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) - other_data = ISC_TRUE; - } - } - } - - if (cname && other_data) - return (ISC_TRUE); - - return (ISC_FALSE); + rdatasetheader_t *header, *header_next; + isc_boolean_t cname, other_data; + dns_rdatatype_t rdtype; + + /* + * The caller must hold the node lock. + */ + + /* + * Look for CNAME and "other data" rdatasets active in our version. + */ + cname = ISC_FALSE; + other_data = ISC_FALSE; + for (header = node->data; header != NULL; header = header_next) { + header_next = header->next; + if (header->type == dns_rdatatype_cname) { + /* + * Look for an active extant CNAME. + */ + do { + if (header->serial <= serial && + !IGNORE(header)) { + /* + * Is this a "this rdataset doesn't + * exist" record? + */ + if (NONEXISTENT(header)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) + cname = ISC_TRUE; + } else { + /* + * Look for active extant "other data". + * + * "Other data" is any rdataset whose type is not + * KEY, RRSIG KEY, NSEC, RRSIG NSEC or RRSIG CNAME. + */ + rdtype = RBTDB_RDATATYPE_BASE(header->type); + if (rdtype == dns_rdatatype_rrsig || + rdtype == dns_rdatatype_sig) + rdtype = RBTDB_RDATATYPE_EXT(header->type); + if (rdtype != dns_rdatatype_nsec && + rdtype != dns_rdatatype_key && + rdtype != dns_rdatatype_cname) { + /* + * We've found a type that isn't + * NSEC, KEY, CNAME, or one of their + * signatures. Is it active and extant? + */ + do { + if (header->serial <= serial && + !IGNORE(header)) { + /* + * Is this a "this rdataset + * doesn't exist" record? + */ + if (NONEXISTENT(header)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) + other_data = ISC_TRUE; + } + } + } + + if (cname && other_data) + return (ISC_TRUE); + + return (ISC_FALSE); +} + +#ifdef LRU_DEBUG +static void +cachestat_update(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) { + if ((header->attributes & RDATASET_ATTR_CACHE) == 0) + return; + + /* XXX: don't use lock for brevity */ + rbtdb->cachestat.cache_total++; + if (RBTDB_RDATATYPE_BASE(header->type) == 0) + rbtdb->cachestat.ncache_total++; + if (header->type == dns_rdatatype_a) + rbtdb->cachestat.a_total++; + else if (header->type == dns_rdatatype_aaaa) + rbtdb->cachestat.aaaa_total++; + else if (header->type == dns_rdatatype_ns) + rbtdb->cachestat.ns_total++; + else if (header->type == dns_rdatatype_ptr) + rbtdb->cachestat.ptr_total++; + + if (header->trust == dns_trust_glue && + (header->type == dns_rdatatype_a || + header->type == dns_rdatatype_aaaa)) { + rbtdb->cachestat.glue_total++; + } + if (header->trust == dns_trust_additional && + (header->type == dns_rdatatype_a || + header->type == dns_rdatatype_aaaa)) { + rbtdb->cachestat.additional_total++; + } } +#endif static isc_result_t add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, rdatasetheader_t *newheader, unsigned int options, isc_boolean_t loading, dns_rdataset_t *addedrdataset, isc_stdtime_t now) { - rbtdb_changed_t *changed = NULL; - rdatasetheader_t *topheader, *topheader_prev, *header; - unsigned char *merged; - isc_result_t result; - isc_boolean_t header_nx; - isc_boolean_t newheader_nx; - isc_boolean_t merge; - dns_rdatatype_t rdtype, covers; - rbtdb_rdatatype_t negtype; - dns_trust_t trust; - - /* - * Add an rdatasetheader_t to a node. - */ - - /* - * Caller must be holding the node lock. - */ - - if ((options & DNS_DBADD_MERGE) != 0) { - REQUIRE(rbtversion != NULL); - merge = ISC_TRUE; - } else - merge = ISC_FALSE; - - if ((options & DNS_DBADD_FORCE) != 0) - trust = dns_trust_ultimate; - else - trust = newheader->trust; - - if (rbtversion != NULL && !loading) { - /* - * We always add a changed record, even if no changes end up - * being made to this node, because it's harmless and - * simplifies the code. - */ - changed = add_changed(rbtdb, rbtversion, rbtnode); - if (changed == NULL) { - free_rdataset(rbtdb->common.mctx, newheader); - return (ISC_R_NOMEMORY); - } - } - - newheader_nx = NONEXISTENT(newheader) ? ISC_TRUE : ISC_FALSE; - topheader_prev = NULL; - - negtype = 0; - if (rbtversion == NULL && !newheader_nx) { - rdtype = RBTDB_RDATATYPE_BASE(newheader->type); - if (rdtype == 0) { - /* - * We're adding a negative cache entry. - */ - covers = RBTDB_RDATATYPE_EXT(newheader->type); - if (covers == dns_rdatatype_any) { - /* - * We're adding an negative cache entry - * which covers all types (NXDOMAIN, - * NODATA(QTYPE=ANY)). - * - * We make all other data stale so that the - * only rdataset that can be found at this - * node is the negative cache entry. - */ - for (topheader = rbtnode->data; - topheader != NULL; - topheader = topheader->next) { - topheader->ttl = 0; - topheader->attributes |= - RDATASET_ATTR_STALE; - } - rbtnode->dirty = 1; - goto find_header; - } - negtype = RBTDB_RDATATYPE_VALUE(covers, 0); - } else { - /* - * We're adding something that isn't a - * negative cache entry. Look for an extant - * non-stale NXDOMAIN/NODATA(QTYPE=ANY) negative - * cache entry. - */ - for (topheader = rbtnode->data; - topheader != NULL; - topheader = topheader->next) { - if (topheader->type == - RBTDB_RDATATYPE_NCACHEANY) - break; - } - if (topheader != NULL && EXISTS(topheader) && - topheader->ttl > now) { - /* - * Found one. - */ - if (trust < topheader->trust) { - /* - * The NXDOMAIN/NODATA(QTYPE=ANY) - * is more trusted. - */ - - free_rdataset(rbtdb->common.mctx, - newheader); - if (addedrdataset != NULL) - bind_rdataset(rbtdb, rbtnode, - topheader, now, - addedrdataset); - return (DNS_R_UNCHANGED); - } - /* - * The new rdataset is better. Expire the - * NXDOMAIN/NODATA(QTYPE=ANY). - */ - topheader->ttl = 0; - topheader->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; - topheader = NULL; - goto find_header; - } - negtype = RBTDB_RDATATYPE_VALUE(0, rdtype); - } - } - - for (topheader = rbtnode->data; - topheader != NULL; - topheader = topheader->next) { - if (topheader->type == newheader->type || - topheader->type == negtype) - break; - topheader_prev = topheader; - } + rbtdb_changed_t *changed = NULL; + rdatasetheader_t *topheader, *topheader_prev, *header; + unsigned char *merged; + isc_result_t result; + isc_boolean_t header_nx; + isc_boolean_t newheader_nx; + isc_boolean_t merge; + dns_rdatatype_t rdtype, covers; + rbtdb_rdatatype_t negtype; + dns_trust_t trust; + + /* + * Add an rdatasetheader_t to a node. + */ + + /* + * Caller must be holding the node lock. + */ + + if ((options & DNS_DBADD_MERGE) != 0) { + REQUIRE(rbtversion != NULL); + merge = ISC_TRUE; + } else + merge = ISC_FALSE; + + if ((options & DNS_DBADD_FORCE) != 0) + trust = dns_trust_ultimate; + else + trust = newheader->trust; + + if (rbtversion != NULL && !loading) { + /* + * We always add a changed record, even if no changes end up + * being made to this node, because it's harmless and + * simplifies the code. + */ + changed = add_changed(rbtdb, rbtversion, rbtnode); + if (changed == NULL) { + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + return (ISC_R_NOMEMORY); + } + } + + newheader_nx = NONEXISTENT(newheader) ? ISC_TRUE : ISC_FALSE; + topheader_prev = NULL; + + negtype = 0; + if (rbtversion == NULL && !newheader_nx) { + rdtype = RBTDB_RDATATYPE_BASE(newheader->type); + if (rdtype == 0) { + /* + * We're adding a negative cache entry. + */ + covers = RBTDB_RDATATYPE_EXT(newheader->type); + if (covers == dns_rdatatype_any) { + /* + * We're adding an negative cache entry + * which covers all types (NXDOMAIN, + * NODATA(QTYPE=ANY)). + * + * We make all other data stale so that the + * only rdataset that can be found at this + * node is the negative cache entry. + */ + for (topheader = rbtnode->data; + topheader != NULL; + topheader = topheader->next) { + set_ttl(rbtdb, topheader, 0); + topheader->attributes |= + RDATASET_ATTR_STALE; + } + rbtnode->dirty = 1; + goto find_header; + } + negtype = RBTDB_RDATATYPE_VALUE(covers, 0); + } else { + /* + * We're adding something that isn't a + * negative cache entry. Look for an extant + * non-stale NXDOMAIN/NODATA(QTYPE=ANY) negative + * cache entry. + */ + for (topheader = rbtnode->data; + topheader != NULL; + topheader = topheader->next) { + if (topheader->type == + RBTDB_RDATATYPE_NCACHEANY) + break; + } + if (topheader != NULL && EXISTS(topheader) && + topheader->rdh_ttl > now) { + /* + * Found one. + */ + if (trust < topheader->trust) { + /* + * The NXDOMAIN/NODATA(QTYPE=ANY) + * is more trusted. + */ + /* set the flag for debug */ + newheader->attributes |= + RDATASET_ATTR_CANCELED; + free_rdataset(rbtdb, + rbtdb->common.mctx, + newheader); + if (addedrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, + topheader, now, + addedrdataset); + return (DNS_R_UNCHANGED); + } + /* + * The new rdataset is better. Expire the + * NXDOMAIN/NODATA(QTYPE=ANY). + */ + set_ttl(rbtdb, topheader, 0); + topheader->attributes |= RDATASET_ATTR_STALE; + rbtnode->dirty = 1; + topheader = NULL; + goto find_header; + } + negtype = RBTDB_RDATATYPE_VALUE(0, rdtype); + } + } + + for (topheader = rbtnode->data; + topheader != NULL; + topheader = topheader->next) { + if (topheader->type == newheader->type || + topheader->type == negtype) + break; + topheader_prev = topheader; + } find_header: - /* - * If header isn't NULL, we've found the right type. There may be - * IGNORE rdatasets between the top of the chain and the first real - * data. We skip over them. - */ - header = topheader; - while (header != NULL && IGNORE(header)) - header = header->down; - if (header != NULL) { - header_nx = NONEXISTENT(header) ? ISC_TRUE : ISC_FALSE; - - /* - * Deleting an already non-existent rdataset has no effect. - */ - if (header_nx && newheader_nx) { - free_rdataset(rbtdb->common.mctx, newheader); - return (DNS_R_UNCHANGED); - } - - /* - * Trying to add an rdataset with lower trust to a cache DB - * has no effect, provided that the cache data isn't stale. - */ - if (rbtversion == NULL && trust < header->trust && - (header->ttl > now || header_nx)) { - free_rdataset(rbtdb->common.mctx, newheader); - if (addedrdataset != NULL) - bind_rdataset(rbtdb, rbtnode, header, now, - addedrdataset); - return (DNS_R_UNCHANGED); - } - - /* - * Don't merge if a nonexistent rdataset is involved. - */ - if (merge && (header_nx || newheader_nx)) - merge = ISC_FALSE; - - /* - * If 'merge' is ISC_TRUE, we'll try to create a new rdataset - * that is the union of 'newheader' and 'header'. - */ - if (merge) { - unsigned int flags = 0; - INSIST(rbtversion->serial >= header->serial); - merged = NULL; - result = ISC_R_SUCCESS; - - if ((options & DNS_DBADD_EXACT) != 0) - flags |= DNS_RDATASLAB_EXACT; - if ((options & DNS_DBADD_EXACTTTL) != 0 && - newheader->ttl != header->ttl) - result = DNS_R_NOTEXACT; - else if (newheader->ttl != header->ttl) - flags |= DNS_RDATASLAB_FORCE; - if (result == ISC_R_SUCCESS) - result = dns_rdataslab_merge( - (unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)), - rbtdb->common.mctx, - rbtdb->common.rdclass, - (dns_rdatatype_t)header->type, - flags, &merged); - if (result == ISC_R_SUCCESS) { - /* - * If 'header' has the same serial number as - * we do, we could clean it up now if we knew - * that our caller had no references to it. - * We don't know this, however, so we leave it - * alone. It will get cleaned up when - * clean_zone_node() runs. - */ - free_rdataset(rbtdb->common.mctx, newheader); - newheader = (rdatasetheader_t *)merged; - } else { - free_rdataset(rbtdb->common.mctx, newheader); - return (result); - } - } - /* - * Don't replace existing NS, A and AAAA RRsets - * in the cache if they are already exist. This - * prevents named being locked to old servers. - * Don't lower trust of existing record if the - * update is forced. - */ - if (IS_CACHE(rbtdb) && header->ttl > now && - header->type == dns_rdatatype_ns && - !header_nx && !newheader_nx && - header->trust >= newheader->trust && - dns_rdataslab_equalx((unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)), - rbtdb->common.rdclass, - (dns_rdatatype_t)header->type)) { - /* - * Honour the new ttl if it is less than the - * older one. - */ - if (header->ttl > newheader->ttl) - header->ttl = newheader->ttl; - if (header->noqname == NULL && - newheader->noqname != NULL) { - header->noqname = newheader->noqname; - newheader->noqname = NULL; - } - free_rdataset(rbtdb->common.mctx, newheader); - if (addedrdataset != NULL) - bind_rdataset(rbtdb, rbtnode, header, now, - addedrdataset); - return (ISC_R_SUCCESS); - } - if (IS_CACHE(rbtdb) && header->ttl > now && - (header->type == dns_rdatatype_a || - header->type == dns_rdatatype_aaaa) && - !header_nx && !newheader_nx && - header->trust >= newheader->trust && - dns_rdataslab_equal((unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)))) { - /* - * Honour the new ttl if it is less than the - * older one. - */ - if (header->ttl > newheader->ttl) - header->ttl = newheader->ttl; - if (header->noqname == NULL && - newheader->noqname != NULL) { - header->noqname = newheader->noqname; - newheader->noqname = NULL; - } - free_rdataset(rbtdb->common.mctx, newheader); - if (addedrdataset != NULL) - bind_rdataset(rbtdb, rbtnode, header, now, - addedrdataset); - return (ISC_R_SUCCESS); - } - INSIST(rbtversion == NULL || - rbtversion->serial >= topheader->serial); - if (topheader_prev != NULL) - topheader_prev->next = newheader; - else - rbtnode->data = newheader; - newheader->next = topheader->next; - if (loading) { - /* - * There are no other references to 'header' when - * loading, so we MAY clean up 'header' now. - * Since we don't generate changed records when - * loading, we MUST clean up 'header' now. - */ - newheader->down = NULL; - free_rdataset(rbtdb->common.mctx, header); - } else { - newheader->down = topheader; - topheader->next = newheader; - rbtnode->dirty = 1; - if (changed != NULL) - changed->dirty = ISC_TRUE; - if (rbtversion == NULL) { - header->ttl = 0; - header->attributes |= RDATASET_ATTR_STALE; - } - } - } else { - /* - * No non-IGNORED rdatasets of the given type exist at - * this node. - */ - - /* - * If we're trying to delete the type, don't bother. - */ - if (newheader_nx) { - free_rdataset(rbtdb->common.mctx, newheader); - return (DNS_R_UNCHANGED); - } - - if (topheader != NULL) { - /* - * We have an list of rdatasets of the given type, - * but they're all marked IGNORE. We simply insert - * the new rdataset at the head of the list. - * - * Ignored rdatasets cannot occur during loading, so - * we INSIST on it. - */ - INSIST(!loading); - INSIST(rbtversion == NULL || - rbtversion->serial >= topheader->serial); - if (topheader_prev != NULL) - topheader_prev->next = newheader; - else - rbtnode->data = newheader; - newheader->next = topheader->next; - newheader->down = topheader; - topheader->next = newheader; - rbtnode->dirty = 1; - if (changed != NULL) - changed->dirty = ISC_TRUE; - } else { - /* - * No rdatasets of the given type exist at the node. - */ - newheader->next = rbtnode->data; - newheader->down = NULL; - rbtnode->data = newheader; - } - } - - /* - * Check if the node now contains CNAME and other data. - */ - if (rbtversion != NULL && - cname_and_other_data(rbtnode, rbtversion->serial)) - return (DNS_R_CNAMEANDOTHER); - - if (addedrdataset != NULL) - bind_rdataset(rbtdb, rbtnode, newheader, now, addedrdataset); - - return (ISC_R_SUCCESS); + /* + * If header isn't NULL, we've found the right type. There may be + * IGNORE rdatasets between the top of the chain and the first real + * data. We skip over them. + */ + header = topheader; + while (header != NULL && IGNORE(header)) + header = header->down; + if (header != NULL) { + header_nx = NONEXISTENT(header) ? ISC_TRUE : ISC_FALSE; + + /* + * Deleting an already non-existent rdataset has no effect. + */ + if (header_nx && newheader_nx) { + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + return (DNS_R_UNCHANGED); + } + + /* + * Trying to add an rdataset with lower trust to a cache DB + * has no effect, provided that the cache data isn't stale. + */ + if (rbtversion == NULL && trust < header->trust && + (header->rdh_ttl > now || header_nx)) { + newheader->attributes |= RDATASET_ATTR_CANCELED; + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + if (addedrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, header, now, + addedrdataset); + return (DNS_R_UNCHANGED); + } + + /* + * Don't merge if a nonexistent rdataset is involved. + */ + if (merge && (header_nx || newheader_nx)) + merge = ISC_FALSE; + + /* + * If 'merge' is ISC_TRUE, we'll try to create a new rdataset + * that is the union of 'newheader' and 'header'. + */ + if (merge) { + unsigned int flags = 0; + INSIST(rbtversion->serial >= header->serial); + merged = NULL; + result = ISC_R_SUCCESS; + + if ((options & DNS_DBADD_EXACT) != 0) + flags |= DNS_RDATASLAB_EXACT; + if ((options & DNS_DBADD_EXACTTTL) != 0 && + newheader->rdh_ttl != header->rdh_ttl) + result = DNS_R_NOTEXACT; + else if (newheader->rdh_ttl != header->rdh_ttl) + flags |= DNS_RDATASLAB_FORCE; + if (result == ISC_R_SUCCESS) + result = dns_rdataslab_merge( + (unsigned char *)header, + (unsigned char *)newheader, + (unsigned int)(sizeof(*newheader)), + rbtdb->common.mctx, + rbtdb->common.rdclass, + (dns_rdatatype_t)header->type, + flags, &merged); + if (result == ISC_R_SUCCESS) { + /* + * If 'header' has the same serial number as + * we do, we could clean it up now if we knew + * that our caller had no references to it. + * We don't know this, however, so we leave it + * alone. It will get cleaned up when + * clean_zone_node() runs. + */ + free_rdataset(rbtdb, rbtdb->common.mctx, + newheader); + newheader = (rdatasetheader_t *)merged; + } else { + free_rdataset(rbtdb, rbtdb->common.mctx, + newheader); + return (result); + } + } + /* + * Don't replace existing NS, A and AAAA RRsets + * in the cache if they are already exist. This + * prevents named being locked to old servers. + * Don't lower trust of existing record if the + * update is forced. + */ + if (IS_CACHE(rbtdb) && header->rdh_ttl > now && + header->type == dns_rdatatype_ns && + !header_nx && !newheader_nx && + header->trust >= newheader->trust && + dns_rdataslab_equalx((unsigned char *)header, + (unsigned char *)newheader, + (unsigned int)(sizeof(*newheader)), + rbtdb->common.rdclass, + (dns_rdatatype_t)header->type)) { + /* + * Honour the new ttl if it is less than the + * older one. + */ + if (header->rdh_ttl > newheader->rdh_ttl) + set_ttl(rbtdb, header, newheader->rdh_ttl); + if (header->noqname == NULL && + newheader->noqname != NULL) { + header->noqname = newheader->noqname; + newheader->noqname = NULL; + } + newheader->attributes |= RDATASET_ATTR_CANCELED; + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + if (addedrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, header, now, + addedrdataset); + return (ISC_R_SUCCESS); + } + if (IS_CACHE(rbtdb) && header->rdh_ttl > now && + (header->type == dns_rdatatype_a || + header->type == dns_rdatatype_aaaa) && + !header_nx && !newheader_nx && + header->trust >= newheader->trust && + dns_rdataslab_equal((unsigned char *)header, + (unsigned char *)newheader, + (unsigned int)(sizeof(*newheader)))) { + /* + * Honour the new ttl if it is less than the + * older one. + */ + if (header->rdh_ttl > newheader->rdh_ttl) + set_ttl(rbtdb, header, newheader->rdh_ttl); + if (header->noqname == NULL && + newheader->noqname != NULL) { + header->noqname = newheader->noqname; + newheader->noqname = NULL; + } + newheader->attributes |= RDATASET_ATTR_CANCELED; + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + if (addedrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, header, now, + addedrdataset); + return (ISC_R_SUCCESS); + } + INSIST(rbtversion == NULL || + rbtversion->serial >= topheader->serial); + if (topheader_prev != NULL) + topheader_prev->next = newheader; + else + rbtnode->data = newheader; + newheader->next = topheader->next; + if (loading) { + /* + * There are no other references to 'header' when + * loading, so we MAY clean up 'header' now. + * Since we don't generate changed records when + * loading, we MUST clean up 'header' now. + */ + newheader->down = NULL; + free_rdataset(rbtdb, rbtdb->common.mctx, header); + } else { + newheader->down = topheader; + topheader->next = newheader; + rbtnode->dirty = 1; + if (changed != NULL) + changed->dirty = ISC_TRUE; + if (rbtversion == NULL) { + set_ttl(rbtdb, header, 0); + header->attributes |= RDATASET_ATTR_STALE; + } + if (IS_CACHE(rbtdb)) { + int idx = newheader->node->locknum; + + ISC_LIST_PREPEND(rbtdb->rdatasets[idx], + newheader, lru_link); + + /* + * XXXMLG We don't check the return value + * here. If it fails, we will not do TTL + * based expiry on this node. However, we + * will do it on the LRU side, so memory + * will not leak... for long. + */ + isc_heap_insert(rbtdb->heaps[idx], newheader); + } +#ifdef LRU_DEBUG + cachestat_update(rbtdb, newheader); +#endif + } + } else { + /* + * No non-IGNORED rdatasets of the given type exist at + * this node. + */ + + /* + * If we're trying to delete the type, don't bother. + */ + if (newheader_nx) { + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + return (DNS_R_UNCHANGED); + } + + if (topheader != NULL) { + /* + * We have an list of rdatasets of the given type, + * but they're all marked IGNORE. We simply insert + * the new rdataset at the head of the list. + * + * Ignored rdatasets cannot occur during loading, so + * we INSIST on it. + */ + INSIST(!loading); + INSIST(rbtversion == NULL || + rbtversion->serial >= topheader->serial); + if (topheader_prev != NULL) + topheader_prev->next = newheader; + else + rbtnode->data = newheader; + newheader->next = topheader->next; + newheader->down = topheader; + topheader->next = newheader; + rbtnode->dirty = 1; + if (changed != NULL) + changed->dirty = ISC_TRUE; + } else { + /* + * No rdatasets of the given type exist at the node. + */ + newheader->next = rbtnode->data; + newheader->down = NULL; + rbtnode->data = newheader; + } + if (IS_CACHE(rbtdb)) { + int idx = newheader->node->locknum; + ISC_LIST_PREPEND(rbtdb->rdatasets[idx], + newheader, lru_link); + isc_heap_insert(rbtdb->heaps[idx], newheader); + } +#ifdef LRU_DEBUG + cachestat_update(rbtdb, newheader); +#endif + } + + /* + * Check if the node now contains CNAME and other data. + */ + if (rbtversion != NULL && + cname_and_other_data(rbtnode, rbtversion->serial)) + return (DNS_R_CNAMEANDOTHER); + + if (addedrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, newheader, now, addedrdataset); + + return (ISC_R_SUCCESS); } static inline isc_boolean_t delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - rbtdb_rdatatype_t type) + rbtdb_rdatatype_t type) { - if (IS_CACHE(rbtdb)) { - if (type == dns_rdatatype_dname) - return (ISC_TRUE); - else - return (ISC_FALSE); - } else if (type == dns_rdatatype_dname || - (type == dns_rdatatype_ns && - (node != rbtdb->origin_node || IS_STUB(rbtdb)))) - return (ISC_TRUE); - return (ISC_FALSE); + if (IS_CACHE(rbtdb)) { + if (type == dns_rdatatype_dname) + return (ISC_TRUE); + else + return (ISC_FALSE); + } else if (type == dns_rdatatype_dname || + (type == dns_rdatatype_ns && + (node != rbtdb->origin_node || IS_STUB(rbtdb)))) + return (ISC_TRUE); + return (ISC_FALSE); } static inline isc_result_t addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, - dns_rdataset_t *rdataset) + dns_rdataset_t *rdataset) { - struct noqname *noqname; - isc_mem_t *mctx = rbtdb->common.mctx; - dns_name_t name; - dns_rdataset_t nsec, nsecsig; - isc_result_t result; - isc_region_t r; - - dns_name_init(&name, NULL); - dns_rdataset_init(&nsec); - dns_rdataset_init(&nsecsig); - - result = dns_rdataset_getnoqname(rdataset, &name, &nsec, &nsecsig); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - noqname = isc_mem_get(mctx, sizeof(*noqname)); - if (noqname == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; - } - dns_name_init(&noqname->name, NULL); - noqname->nsec = NULL; - noqname->nsecsig = NULL; - result = dns_name_dup(&name, mctx, &noqname->name); - if (result != ISC_R_SUCCESS) - goto cleanup; - result = dns_rdataslab_fromrdataset(&nsec, mctx, &r, 0); - if (result != ISC_R_SUCCESS) - goto cleanup; - noqname->nsec = r.base; - result = dns_rdataslab_fromrdataset(&nsecsig, mctx, &r, 0); - if (result != ISC_R_SUCCESS) - goto cleanup; - noqname->nsecsig = r.base; - dns_rdataset_disassociate(&nsec); - dns_rdataset_disassociate(&nsecsig); - newheader->noqname = noqname; - return (ISC_R_SUCCESS); + struct noqname *noqname; + isc_mem_t *mctx = rbtdb->common.mctx; + dns_name_t name; + dns_rdataset_t nsec, nsecsig; + isc_result_t result; + isc_region_t r; + + dns_name_init(&name, NULL); + dns_rdataset_init(&nsec); + dns_rdataset_init(&nsecsig); + + result = dns_rdataset_getnoqname(rdataset, &name, &nsec, &nsecsig); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + noqname = isc_mem_get(mctx, sizeof(*noqname)); + if (noqname == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + dns_name_init(&noqname->name, NULL); + noqname->nsec = NULL; + noqname->nsecsig = NULL; + result = dns_name_dup(&name, mctx, &noqname->name); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = dns_rdataslab_fromrdataset(&nsec, mctx, &r, 0); + if (result != ISC_R_SUCCESS) + goto cleanup; + noqname->nsec = r.base; + result = dns_rdataslab_fromrdataset(&nsecsig, mctx, &r, 0); + if (result != ISC_R_SUCCESS) + goto cleanup; + noqname->nsecsig = r.base; + dns_rdataset_disassociate(&nsec); + dns_rdataset_disassociate(&nsecsig); + newheader->noqname = noqname; + return (ISC_R_SUCCESS); cleanup: - dns_rdataset_disassociate(&nsec); - dns_rdataset_disassociate(&nsecsig); - free_noqname(mctx, &noqname); - return(result); + dns_rdataset_disassociate(&nsec); + dns_rdataset_disassociate(&nsecsig); + free_noqname(mctx, &noqname); + return(result); } static isc_result_t addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options, - dns_rdataset_t *addedrdataset) + isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options, + dns_rdataset_t *addedrdataset) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - rbtdb_version_t *rbtversion = version; - isc_region_t region; - rdatasetheader_t *newheader; - isc_result_t result; - isc_boolean_t delegating; - - REQUIRE(VALID_RBTDB(rbtdb)); - - if (rbtversion == NULL) { - if (now == 0) - isc_stdtime_get(&now); - } else - now = 0; - - result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, - sizeof(rdatasetheader_t)); - if (result != ISC_R_SUCCESS) - return (result); - - newheader = (rdatasetheader_t *)region.base; - newheader->ttl = rdataset->ttl + now; - newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type, - rdataset->covers); - newheader->attributes = 0; - newheader->noqname = NULL; - newheader->count = init_count++; - newheader->trust = rdataset->trust; - newheader->additional_auth = NULL; - newheader->additional_glue = NULL; - if (rbtversion != NULL) { - newheader->serial = rbtversion->serial; - now = 0; - } else { - newheader->serial = 1; - if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) - newheader->attributes |= RDATASET_ATTR_NXDOMAIN; - if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) { - result = addnoqname(rbtdb, newheader, rdataset); - if (result != ISC_R_SUCCESS) { - free_rdataset(rbtdb->common.mctx, newheader); - return (result); - } - } - } - - /* - * If we're adding a delegation type (e.g. NS or DNAME for a zone, - * just DNAME for the cache), then we need to set the callback bit - * on the node, and to do that we must be holding an exclusive lock - * on the tree. - */ - if (delegating_type(rbtdb, rbtnode, rdataset->type)) { - delegating = ISC_TRUE; - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - } else - delegating = ISC_FALSE; - - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - - result = add(rbtdb, rbtnode, rbtversion, newheader, options, ISC_FALSE, - addedrdataset, now); - if (result == ISC_R_SUCCESS && delegating) - rbtnode->find_callback = 1; - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - - if (delegating) - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - - /* - * Update the zone's secure status. If version is non-NULL - * this is defered until closeversion() is called. - */ - if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb)) - rbtdb->secure = iszonesecure(db, rbtdb->origin_node); - - return (result); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; + rbtdb_version_t *rbtversion = version; + isc_region_t region; + rdatasetheader_t *newheader; + isc_result_t result; + isc_boolean_t delegating; + isc_boolean_t tree_locked = ISC_FALSE; + + REQUIRE(VALID_RBTDB(rbtdb)); + + if (rbtversion == NULL) { + if (now == 0) + isc_stdtime_get(&now); + } else + now = 0; + + result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, + ®ion, + sizeof(rdatasetheader_t)); + if (result != ISC_R_SUCCESS) + return (result); + + newheader = (rdatasetheader_t *)region.base; + init_rdataset(rbtdb, newheader); + set_ttl(rbtdb, newheader, rdataset->ttl + now); + newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type, + rdataset->covers); + newheader->attributes = 0; + newheader->noqname = NULL; + newheader->count = init_count++; + newheader->trust = rdataset->trust; + newheader->additional_auth = NULL; + newheader->additional_glue = NULL; + newheader->last_used = now; + newheader->node = rbtnode; + if (rbtversion != NULL) { + newheader->serial = rbtversion->serial; + now = 0; + } else { + newheader->serial = 1; + if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) + newheader->attributes |= RDATASET_ATTR_NXDOMAIN; + if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) { + result = addnoqname(rbtdb, newheader, rdataset); + if (result != ISC_R_SUCCESS) { + free_rdataset(rbtdb, rbtdb->common.mctx, + newheader); + return (result); + } + } + } + + /* + * If we're adding a delegation type (e.g. NS or DNAME for a zone, + * just DNAME for the cache), then we need to set the callback bit + * on the node. + */ + if (delegating_type(rbtdb, rbtnode, rdataset->type)) + delegating = ISC_TRUE; + else + delegating = ISC_FALSE; + + /* + * If we're adding a delegation type or the DB is a cache in an overmem + * state, hold an exclusive lock on the tree. In the latter case + * the lock does not necessarily have to be acquired but it will help + * purge stale entries more effectively. + */ + if (delegating || (IS_CACHE(rbtdb) && rbtdb->overmem)) { + tree_locked = ISC_TRUE; + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); + } + + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + +#ifdef LRU_DEBUG + /* for debug: statistics update */ + if (IS_CACHE(rbtdb) && rdataset->rdclass == dns_rdataclass_in) { + /* XXX: don't use lock for brevity */ + newheader->attributes |= RDATASET_ATTR_CACHE; + rbtdb->cachestat.cache_total++; + rbtdb->cachestat.cache_current++; + if (rdataset->type == 0) { + rbtdb->cachestat.ncache_total++; + rbtdb->cachestat.ncache_current++; + } + if (rdataset->type == dns_rdatatype_a) { + rbtdb->cachestat.a_total++; + rbtdb->cachestat.a_current++; + } else if (rdataset->type == dns_rdatatype_aaaa) { + rbtdb->cachestat.aaaa_total++; + rbtdb->cachestat.aaaa_current++; + } else if (rdataset->type == dns_rdatatype_ns) { + rbtdb->cachestat.ns_total++; + rbtdb->cachestat.ns_current++; + } else if (rdataset->type == dns_rdatatype_ptr) { + rbtdb->cachestat.ptr_total++; + rbtdb->cachestat.ptr_current++; + } + if (rdataset->trust == dns_trust_glue && + (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa)) { + rbtdb->cachestat.glue_total++; + rbtdb->cachestat.glue_current++; + } + if (rdataset->trust == dns_trust_additional && + (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa)) { + rbtdb->cachestat.additional_total++; + rbtdb->cachestat.additional_current++; + } + } +#endif + + if (IS_CACHE(rbtdb)) { + if (tree_locked) + cleanup_dead_nodes(rbtdb, rbtnode->locknum); + check_stale_cache(rbtdb, rbtnode, now, tree_locked); + + /* + * If we've been holding a write lock on the tree just for + * cleaning, we can release it now. However, we still need the + * node lock. + */ + if (tree_locked && !delegating) { + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); + tree_locked = ISC_FALSE; + } + } + + result = add(rbtdb, rbtnode, rbtversion, newheader, options, ISC_FALSE, + addedrdataset, now); + if (result == ISC_R_SUCCESS && delegating) + rbtnode->find_callback = 1; + + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + + if (tree_locked) + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); + + /* + * Update the zone's secure status. If version is non-NULL + * this is defered until closeversion() is called. + */ + if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb)) + rbtdb->secure = iszonesecure(db, rbtdb->origin_node); + + return (result); } static isc_result_t subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdataset_t *rdataset, unsigned int options, - dns_rdataset_t *newrdataset) + dns_rdataset_t *rdataset, unsigned int options, + dns_rdataset_t *newrdataset) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - rbtdb_version_t *rbtversion = version; - rdatasetheader_t *topheader, *topheader_prev, *header, *newheader; - unsigned char *subresult; - isc_region_t region; - isc_result_t result; - rbtdb_changed_t *changed; - - REQUIRE(VALID_RBTDB(rbtdb)); - - result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, - sizeof(rdatasetheader_t)); - if (result != ISC_R_SUCCESS) - return (result); - newheader = (rdatasetheader_t *)region.base; - newheader->ttl = rdataset->ttl; - newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type, - rdataset->covers); - newheader->attributes = 0; - newheader->serial = rbtversion->serial; - newheader->trust = 0; - newheader->noqname = NULL; - newheader->count = init_count++; - newheader->additional_auth = NULL; - newheader->additional_glue = NULL; - - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - - changed = add_changed(rbtdb, rbtversion, rbtnode); - if (changed == NULL) { - free_rdataset(rbtdb->common.mctx, newheader); - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - return (ISC_R_NOMEMORY); - } - - topheader_prev = NULL; - for (topheader = rbtnode->data; - topheader != NULL; - topheader = topheader->next) { - if (topheader->type == newheader->type) - break; - topheader_prev = topheader; - } - /* - * If header isn't NULL, we've found the right type. There may be - * IGNORE rdatasets between the top of the chain and the first real - * data. We skip over them. - */ - header = topheader; - while (header != NULL && IGNORE(header)) - header = header->down; - if (header != NULL && EXISTS(header)) { - unsigned int flags = 0; - subresult = NULL; - result = ISC_R_SUCCESS; - if ((options & DNS_DBSUB_EXACT) != 0) { - flags |= DNS_RDATASLAB_EXACT; - if (newheader->ttl != header->ttl) - result = DNS_R_NOTEXACT; - } - if (result == ISC_R_SUCCESS) - result = dns_rdataslab_subtract( - (unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)), - rbtdb->common.mctx, - rbtdb->common.rdclass, - (dns_rdatatype_t)header->type, - flags, &subresult); - if (result == ISC_R_SUCCESS) { - free_rdataset(rbtdb->common.mctx, newheader); - newheader = (rdatasetheader_t *)subresult; - /* - * We have to set the serial since the rdataslab - * subtraction routine copies the reserved portion of - * header, not newheader. - */ - newheader->serial = rbtversion->serial; - /* - * XXXJT: dns_rdataslab_subtract() copied the pointers - * to additional info. We need to clear these fields - * to avoid having duplicated references. - */ - newheader->additional_auth = NULL; - newheader->additional_glue = NULL; - } else if (result == DNS_R_NXRRSET) { - /* - * This subtraction would remove all of the rdata; - * add a nonexistent header instead. - */ - free_rdataset(rbtdb->common.mctx, newheader); - newheader = isc_mem_get(rbtdb->common.mctx, - sizeof(*newheader)); - if (newheader == NULL) { - result = ISC_R_NOMEMORY; - goto unlock; - } - newheader->ttl = 0; - newheader->type = topheader->type; - newheader->attributes = RDATASET_ATTR_NONEXISTENT; - newheader->trust = 0; - newheader->serial = rbtversion->serial; - newheader->noqname = NULL; - newheader->count = 0; - newheader->additional_auth = NULL; - newheader->additional_glue = NULL; - } else { - free_rdataset(rbtdb->common.mctx, newheader); - goto unlock; - } - - /* - * If we're here, we want to link newheader in front of - * topheader. - */ - INSIST(rbtversion->serial >= topheader->serial); - if (topheader_prev != NULL) - topheader_prev->next = newheader; - else - rbtnode->data = newheader; - newheader->next = topheader->next; - newheader->down = topheader; - topheader->next = newheader; - rbtnode->dirty = 1; - changed->dirty = ISC_TRUE; - } else { - /* - * The rdataset doesn't exist, so we don't need to do anything - * to satisfy the deletion request. - */ - free_rdataset(rbtdb->common.mctx, newheader); - if ((options & DNS_DBSUB_EXACT) != 0) - result = DNS_R_NOTEXACT; - else - result = DNS_R_UNCHANGED; - } - - if (result == ISC_R_SUCCESS && newrdataset != NULL) - bind_rdataset(rbtdb, rbtnode, newheader, 0, newrdataset); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; + rbtdb_version_t *rbtversion = version; + rdatasetheader_t *topheader, *topheader_prev, *header, *newheader; + unsigned char *subresult; + isc_region_t region; + isc_result_t result; + rbtdb_changed_t *changed; + + REQUIRE(VALID_RBTDB(rbtdb)); + + result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, + ®ion, + sizeof(rdatasetheader_t)); + if (result != ISC_R_SUCCESS) + return (result); + newheader = (rdatasetheader_t *)region.base; + init_rdataset(rbtdb, newheader); + set_ttl(rbtdb, newheader, rdataset->ttl); + newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type, + rdataset->covers); + newheader->attributes = 0; + newheader->serial = rbtversion->serial; + newheader->trust = 0; + newheader->noqname = NULL; + newheader->count = init_count++; + newheader->additional_auth = NULL; + newheader->additional_glue = NULL; + newheader->last_used = 0; + newheader->node = rbtnode; + + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + + changed = add_changed(rbtdb, rbtversion, rbtnode); + if (changed == NULL) { + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + return (ISC_R_NOMEMORY); + } + + topheader_prev = NULL; + for (topheader = rbtnode->data; + topheader != NULL; + topheader = topheader->next) { + if (topheader->type == newheader->type) + break; + topheader_prev = topheader; + } + /* + * If header isn't NULL, we've found the right type. There may be + * IGNORE rdatasets between the top of the chain and the first real + * data. We skip over them. + */ + header = topheader; + while (header != NULL && IGNORE(header)) + header = header->down; + if (header != NULL && EXISTS(header)) { + unsigned int flags = 0; + subresult = NULL; + result = ISC_R_SUCCESS; + if ((options & DNS_DBSUB_EXACT) != 0) { + flags |= DNS_RDATASLAB_EXACT; + if (newheader->rdh_ttl != header->rdh_ttl) + result = DNS_R_NOTEXACT; + } + if (result == ISC_R_SUCCESS) + result = dns_rdataslab_subtract( + (unsigned char *)header, + (unsigned char *)newheader, + (unsigned int)(sizeof(*newheader)), + rbtdb->common.mctx, + rbtdb->common.rdclass, + (dns_rdatatype_t)header->type, + flags, &subresult); + if (result == ISC_R_SUCCESS) { + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + newheader = (rdatasetheader_t *)subresult; + /* + * We have to set the serial since the rdataslab + * subtraction routine copies the reserved portion of + * header, not newheader. + */ + newheader->serial = rbtversion->serial; + /* + * XXXJT: dns_rdataslab_subtract() copied the pointers + * to additional info. We need to clear these fields + * to avoid having duplicated references. + */ + newheader->additional_auth = NULL; + newheader->additional_glue = NULL; + } else if (result == DNS_R_NXRRSET) { + /* + * This subtraction would remove all of the rdata; + * add a nonexistent header instead. + */ + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + newheader = new_rdataset(rbtdb, rbtdb->common.mctx); + if (newheader == NULL) { + result = ISC_R_NOMEMORY; + goto unlock; + } + set_ttl(rbtdb, newheader, 0); + newheader->type = topheader->type; + newheader->attributes = RDATASET_ATTR_NONEXISTENT; + newheader->trust = 0; + newheader->serial = rbtversion->serial; + newheader->noqname = NULL; + newheader->count = 0; + newheader->additional_auth = NULL; + newheader->additional_glue = NULL; + } else { + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + goto unlock; + } + + /* + * If we're here, we want to link newheader in front of + * topheader. + */ + INSIST(rbtversion->serial >= topheader->serial); + if (topheader_prev != NULL) + topheader_prev->next = newheader; + else + rbtnode->data = newheader; + newheader->next = topheader->next; + newheader->down = topheader; + topheader->next = newheader; + rbtnode->dirty = 1; + changed->dirty = ISC_TRUE; + } else { + /* + * The rdataset doesn't exist, so we don't need to do anything + * to satisfy the deletion request. + */ + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + if ((options & DNS_DBSUB_EXACT) != 0) + result = DNS_R_NOTEXACT; + else + result = DNS_R_UNCHANGED; + } + + if (result == ISC_R_SUCCESS && newrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, newheader, 0, newrdataset); unlock: - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); - /* - * Update the zone's secure status. If version is non-NULL - * this is defered until closeversion() is called. - */ - if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb)) - rbtdb->secure = iszonesecure(db, rbtdb->origin_node); + /* + * Update the zone's secure status. If version is non-NULL + * this is defered until closeversion() is called. + */ + if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb)) + rbtdb->secure = iszonesecure(db, rbtdb->origin_node); - return (result); + return (result); } static isc_result_t deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdatatype_t type, dns_rdatatype_t covers) + dns_rdatatype_t type, dns_rdatatype_t covers) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - rbtdb_version_t *rbtversion = version; - isc_result_t result; - rdatasetheader_t *newheader; - - REQUIRE(VALID_RBTDB(rbtdb)); - - if (type == dns_rdatatype_any) - return (ISC_R_NOTIMPLEMENTED); - if (type == dns_rdatatype_rrsig && covers == 0) - return (ISC_R_NOTIMPLEMENTED); - - newheader = isc_mem_get(rbtdb->common.mctx, sizeof(*newheader)); - if (newheader == NULL) - return (ISC_R_NOMEMORY); - newheader->ttl = 0; - newheader->type = RBTDB_RDATATYPE_VALUE(type, covers); - newheader->attributes = RDATASET_ATTR_NONEXISTENT; - newheader->trust = 0; - newheader->noqname = NULL; - newheader->additional_auth = NULL; - newheader->additional_glue = NULL; - if (rbtversion != NULL) - newheader->serial = rbtversion->serial; - else - newheader->serial = 0; - newheader->count = 0; - - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - - result = add(rbtdb, rbtnode, rbtversion, newheader, DNS_DBADD_FORCE, - ISC_FALSE, NULL, 0); - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_write); - - /* - * Update the zone's secure status. If version is non-NULL - * this is defered until closeversion() is called. - */ - if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb)) - rbtdb->secure = iszonesecure(db, rbtdb->origin_node); - - return (result); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; + rbtdb_version_t *rbtversion = version; + isc_result_t result; + rdatasetheader_t *newheader; + + REQUIRE(VALID_RBTDB(rbtdb)); + + if (type == dns_rdatatype_any) + return (ISC_R_NOTIMPLEMENTED); + if (type == dns_rdatatype_rrsig && covers == 0) + return (ISC_R_NOTIMPLEMENTED); + + newheader = new_rdataset(rbtdb, rbtdb->common.mctx); + if (newheader == NULL) + return (ISC_R_NOMEMORY); + set_ttl(rbtdb, newheader, 0); + newheader->type = RBTDB_RDATATYPE_VALUE(type, covers); + newheader->attributes = RDATASET_ATTR_NONEXISTENT; + newheader->trust = 0; + newheader->noqname = NULL; + newheader->additional_auth = NULL; + newheader->additional_glue = NULL; + if (rbtversion != NULL) + newheader->serial = rbtversion->serial; + else + newheader->serial = 0; + newheader->count = 0; + newheader->last_used = 0; + newheader->node = rbtnode; + + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + + result = add(rbtdb, rbtnode, rbtversion, newheader, DNS_DBADD_FORCE, + ISC_FALSE, NULL, 0); + + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_write); + + /* + * Update the zone's secure status. If version is non-NULL + * this is defered until closeversion() is called. + */ + if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb)) + rbtdb->secure = iszonesecure(db, rbtdb->origin_node); + + return (result); } static isc_result_t loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) { - rbtdb_load_t *loadctx = arg; - dns_rbtdb_t *rbtdb = loadctx->rbtdb; - dns_rbtnode_t *node; - isc_result_t result; - isc_region_t region; - rdatasetheader_t *newheader; - - /* - * This routine does no node locking. See comments in - * 'load' below for more information on loading and - * locking. - */ - - - /* - * SOA records are only allowed at top of zone. - */ - if (rdataset->type == dns_rdatatype_soa && - !IS_CACHE(rbtdb) && !dns_name_equal(name, &rbtdb->common.origin)) - return (DNS_R_NOTZONETOP); - - add_empty_wildcards(rbtdb, name); - - if (dns_name_iswildcard(name)) { - /* - * NS record owners cannot legally be wild cards. - */ - if (rdataset->type == dns_rdatatype_ns) - return (DNS_R_INVALIDNS); - result = add_wildcard_magic(rbtdb, name); - if (result != ISC_R_SUCCESS) - return (result); - } - - node = NULL; - result = dns_rbt_addnode(rbtdb->tree, name, &node); - if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) - return (result); - if (result != ISC_R_EXISTS) { - dns_name_t foundname; - dns_name_init(&foundname, NULL); - dns_rbt_namefromnode(node, &foundname); + rbtdb_load_t *loadctx = arg; + dns_rbtdb_t *rbtdb = loadctx->rbtdb; + dns_rbtnode_t *node; + isc_result_t result; + isc_region_t region; + rdatasetheader_t *newheader; + + /* + * This routine does no node locking. See comments in + * 'load' below for more information on loading and + * locking. + */ + + + /* + * SOA records are only allowed at top of zone. + */ + if (rdataset->type == dns_rdatatype_soa && + !IS_CACHE(rbtdb) && !dns_name_equal(name, &rbtdb->common.origin)) + return (DNS_R_NOTZONETOP); + + add_empty_wildcards(rbtdb, name); + + if (dns_name_iswildcard(name)) { + /* + * NS record owners cannot legally be wild cards. + */ + if (rdataset->type == dns_rdatatype_ns) + return (DNS_R_INVALIDNS); + result = add_wildcard_magic(rbtdb, name); + if (result != ISC_R_SUCCESS) + return (result); + } + + node = NULL; + result = dns_rbt_addnode(rbtdb->tree, name, &node); + if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) + return (result); + if (result != ISC_R_EXISTS) { + dns_name_t foundname; + dns_name_init(&foundname, NULL); + dns_rbt_namefromnode(node, &foundname); #ifdef DNS_RBT_USEHASH - node->locknum = node->hashval % rbtdb->node_lock_count; + node->locknum = node->hashval % rbtdb->node_lock_count; #else - node->locknum = dns_name_hash(&foundname, ISC_TRUE) % - rbtdb->node_lock_count; + node->locknum = dns_name_hash(&foundname, ISC_TRUE) % + rbtdb->node_lock_count; #endif - } - - result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, - sizeof(rdatasetheader_t)); - if (result != ISC_R_SUCCESS) - return (result); - newheader = (rdatasetheader_t *)region.base; - newheader->ttl = rdataset->ttl + loadctx->now; /* XXX overflow check */ - newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type, - rdataset->covers); - newheader->attributes = 0; - newheader->trust = rdataset->trust; - newheader->serial = 1; - newheader->noqname = NULL; - newheader->count = init_count++; - newheader->additional_auth = NULL; - newheader->additional_glue = NULL; - - result = add(rbtdb, node, rbtdb->current_version, newheader, - DNS_DBADD_MERGE, ISC_TRUE, NULL, 0); - if (result == ISC_R_SUCCESS && - delegating_type(rbtdb, node, rdataset->type)) - node->find_callback = 1; - else if (result == DNS_R_UNCHANGED) - result = ISC_R_SUCCESS; - - return (result); + } + + result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, + ®ion, + sizeof(rdatasetheader_t)); + if (result != ISC_R_SUCCESS) + return (result); + newheader = (rdatasetheader_t *)region.base; + init_rdataset(rbtdb, newheader); + set_ttl(rbtdb, newheader, + rdataset->ttl + loadctx->now); /* XXX overflow check */ + newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type, + rdataset->covers); + newheader->attributes = 0; + newheader->trust = rdataset->trust; + newheader->serial = 1; + newheader->noqname = NULL; + newheader->count = init_count++; + newheader->additional_auth = NULL; + newheader->additional_glue = NULL; + /* won't be used, but initialize anyway */ + newheader->last_used = 0; + newheader->node = node; + + result = add(rbtdb, node, rbtdb->current_version, newheader, + DNS_DBADD_MERGE, ISC_TRUE, NULL, 0); + if (result == ISC_R_SUCCESS && + delegating_type(rbtdb, node, rdataset->type)) + node->find_callback = 1; + else if (result == DNS_R_UNCHANGED) + result = ISC_R_SUCCESS; + + return (result); } static isc_result_t beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp) { - rbtdb_load_t *loadctx; - dns_rbtdb_t *rbtdb; + rbtdb_load_t *loadctx; + dns_rbtdb_t *rbtdb; - rbtdb = (dns_rbtdb_t *)db; + rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - loadctx = isc_mem_get(rbtdb->common.mctx, sizeof(*loadctx)); - if (loadctx == NULL) - return (ISC_R_NOMEMORY); + loadctx = isc_mem_get(rbtdb->common.mctx, sizeof(*loadctx)); + if (loadctx == NULL) + return (ISC_R_NOMEMORY); - loadctx->rbtdb = rbtdb; - if (IS_CACHE(rbtdb)) - isc_stdtime_get(&loadctx->now); - else - loadctx->now = 0; + loadctx->rbtdb = rbtdb; + if (IS_CACHE(rbtdb)) + isc_stdtime_get(&loadctx->now); + else + loadctx->now = 0; - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING)) - == 0); - rbtdb->attributes |= RBTDB_ATTR_LOADING; + REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING)) + == 0); + rbtdb->attributes |= RBTDB_ATTR_LOADING; - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); - *addp = loading_addrdataset; - *dbloadp = loadctx; + *addp = loading_addrdataset; + *dbloadp = loadctx; - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } static isc_result_t endload(dns_db_t *db, dns_dbload_t **dbloadp) { - rbtdb_load_t *loadctx; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + rbtdb_load_t *loadctx; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(dbloadp != NULL); - loadctx = *dbloadp; - REQUIRE(loadctx->rbtdb == rbtdb); + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(dbloadp != NULL); + loadctx = *dbloadp; + REQUIRE(loadctx->rbtdb == rbtdb); - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADING) != 0); - REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADED) == 0); + REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADING) != 0); + REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADED) == 0); - rbtdb->attributes &= ~RBTDB_ATTR_LOADING; - rbtdb->attributes |= RBTDB_ATTR_LOADED; + rbtdb->attributes &= ~RBTDB_ATTR_LOADING; + rbtdb->attributes |= RBTDB_ATTR_LOADED; - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); - /* - * If there's a KEY rdataset at the zone origin containing a - * zone key, we consider the zone secure. - */ - if (! IS_CACHE(rbtdb)) - rbtdb->secure = iszonesecure(db, rbtdb->origin_node); + /* + * If there's a KEY rdataset at the zone origin containing a + * zone key, we consider the zone secure. + */ + if (! IS_CACHE(rbtdb)) + rbtdb->secure = iszonesecure(db, rbtdb->origin_node); - *dbloadp = NULL; + *dbloadp = NULL; - isc_mem_put(rbtdb->common.mctx, loadctx, sizeof(*loadctx)); + isc_mem_put(rbtdb->common.mctx, loadctx, sizeof(*loadctx)); - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } static isc_result_t dump(dns_db_t *db, dns_dbversion_t *version, const char *filename, dns_masterformat_t masterformat) { - dns_rbtdb_t *rbtdb; + dns_rbtdb_t *rbtdb; - rbtdb = (dns_rbtdb_t *)db; + rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - return (dns_master_dump2(rbtdb->common.mctx, db, version, - &dns_master_style_default, - filename, masterformat)); + return (dns_master_dump2(rbtdb->common.mctx, db, version, + &dns_master_style_default, + filename, masterformat)); } static void delete_callback(void *data, void *arg) { - dns_rbtdb_t *rbtdb = arg; - rdatasetheader_t *current, *next; + dns_rbtdb_t *rbtdb = arg; + rdatasetheader_t *current, *next; - for (current = data; current != NULL; current = next) { - next = current->next; - free_rdataset(rbtdb->common.mctx, current); - } + for (current = data; current != NULL; current = next) { + next = current->next; + free_rdataset(rbtdb, rbtdb->common.mctx, current); + } } static isc_boolean_t issecure(dns_db_t *db) { - dns_rbtdb_t *rbtdb; - isc_boolean_t secure; + dns_rbtdb_t *rbtdb; + isc_boolean_t secure; - rbtdb = (dns_rbtdb_t *)db; + rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - secure = rbtdb->secure; - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + secure = rbtdb->secure; + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - return (secure); + return (secure); } static unsigned int nodecount(dns_db_t *db) { - dns_rbtdb_t *rbtdb; - unsigned int count; + dns_rbtdb_t *rbtdb; + unsigned int count; - rbtdb = (dns_rbtdb_t *)db; + rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - count = dns_rbt_nodecount(rbtdb->tree); - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + count = dns_rbt_nodecount(rbtdb->tree); + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - return (count); + return (count); } static void settask(dns_db_t *db, isc_task_t *task) { - dns_rbtdb_t *rbtdb; + dns_rbtdb_t *rbtdb; - rbtdb = (dns_rbtdb_t *)db; + rbtdb = (dns_rbtdb_t *)db; - REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(VALID_RBTDB(rbtdb)); - RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); - if (rbtdb->task != NULL) - isc_task_detach(&rbtdb->task); - if (task != NULL) - isc_task_attach(task, &rbtdb->task); - RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); + if (rbtdb->task != NULL) + isc_task_detach(&rbtdb->task); + if (task != NULL) + isc_task_attach(task, &rbtdb->task); + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); } static isc_boolean_t ispersistent(dns_db_t *db) { - UNUSED(db); - return (ISC_FALSE); + UNUSED(db); + return (ISC_FALSE); } static isc_result_t getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *onode; - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(nodep != NULL && *nodep == NULL); - - /* Note that the access to origin_node doesn't require a DB lock */ - onode = (dns_rbtnode_t *)rbtdb->origin_node; - if (onode != NULL) { - NODE_STRONGLOCK(&rbtdb->node_locks[onode->locknum].lock); - new_reference(rbtdb, onode); - NODE_STRONGUNLOCK(&rbtdb->node_locks[onode->locknum].lock); - - *nodep = rbtdb->origin_node; - } else { - INSIST(!IS_CACHE(rbtdb)); - result = ISC_R_NOTFOUND; - } - - return (result); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *onode; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(nodep != NULL && *nodep == NULL); + + /* Note that the access to origin_node doesn't require a DB lock */ + onode = (dns_rbtnode_t *)rbtdb->origin_node; + if (onode != NULL) { + NODE_STRONGLOCK(&rbtdb->node_locks[onode->locknum].lock); + new_reference(rbtdb, onode); + NODE_STRONGUNLOCK(&rbtdb->node_locks[onode->locknum].lock); + + *nodep = rbtdb->origin_node; + } else { + INSIST(!IS_CACHE(rbtdb)); + result = ISC_R_NOTFOUND; + } + + return (result); } static dns_dbmethods_t zone_methods = { - attach, - detach, - beginload, - endload, - dump, - currentversion, - newversion, - attachversion, - closeversion, - findnode, - zone_find, - zone_findzonecut, - attachnode, - detachnode, - expirenode, - printnode, - createiterator, - zone_findrdataset, - allrdatasets, - addrdataset, - subtractrdataset, - deleterdataset, - issecure, - nodecount, - ispersistent, - overmem, - settask, - getoriginnode, - NULL, + attach, + detach, + beginload, + endload, + dump, + currentversion, + newversion, + attachversion, + closeversion, + findnode, + zone_find, + zone_findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + zone_findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem, + settask, + getoriginnode, + NULL, }; static dns_dbmethods_t cache_methods = { - attach, - detach, - beginload, - endload, - dump, - currentversion, - newversion, - attachversion, - closeversion, - findnode, - cache_find, - cache_findzonecut, - attachnode, - detachnode, - expirenode, - printnode, - createiterator, - cache_findrdataset, - allrdatasets, - addrdataset, - subtractrdataset, - deleterdataset, - issecure, - nodecount, - ispersistent, - overmem, - settask, - getoriginnode, - NULL + attach, + detach, + beginload, + endload, + dump, + currentversion, + newversion, + attachversion, + closeversion, + findnode, + cache_find, + cache_findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + cache_findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem, + settask, + getoriginnode, + NULL }; isc_result_t @@ -5443,201 +6085,263 @@ dns_rbtdb64_create #else dns_rbtdb_create #endif - (isc_mem_t *mctx, dns_name_t *origin, dns_dbtype_t type, - dns_rdataclass_t rdclass, unsigned int argc, char *argv[], - void *driverarg, dns_db_t **dbp) + (isc_mem_t *mctx, dns_name_t *origin, dns_dbtype_t type, + dns_rdataclass_t rdclass, unsigned int argc, char *argv[], + void *driverarg, dns_db_t **dbp) { - dns_rbtdb_t *rbtdb; - isc_result_t result; - int i; - dns_name_t name; - - /* Keep the compiler happy. */ - UNUSED(argc); - UNUSED(argv); - UNUSED(driverarg); - - rbtdb = isc_mem_get(mctx, sizeof(*rbtdb)); - if (rbtdb == NULL) - return (ISC_R_NOMEMORY); - - memset(rbtdb, '\0', sizeof(*rbtdb)); - dns_name_init(&rbtdb->common.origin, NULL); - rbtdb->common.attributes = 0; - if (type == dns_dbtype_cache) { - rbtdb->common.methods = &cache_methods; - rbtdb->common.attributes |= DNS_DBATTR_CACHE; - } else if (type == dns_dbtype_stub) { - rbtdb->common.methods = &zone_methods; - rbtdb->common.attributes |= DNS_DBATTR_STUB; - } else - rbtdb->common.methods = &zone_methods; - rbtdb->common.rdclass = rdclass; - rbtdb->common.mctx = NULL; - - result = RBTDB_INITLOCK(&rbtdb->lock); - if (result != ISC_R_SUCCESS) - goto cleanup_rbtdb; - - result = isc_rwlock_init(&rbtdb->tree_lock, 0, 0); - if (result != ISC_R_SUCCESS) - goto cleanup_lock; - - if (rbtdb->node_lock_count == 0) { - if (IS_CACHE(rbtdb)) - rbtdb->node_lock_count = DEFAULT_CACHE_NODE_LOCK_COUNT; - else - rbtdb->node_lock_count = DEFAULT_NODE_LOCK_COUNT; - } - INSIST(rbtdb->node_lock_count < (1 << DNS_RBT_LOCKLENGTH)); - rbtdb->node_locks = isc_mem_get(mctx, rbtdb->node_lock_count * - sizeof(rbtdb_nodelock_t)); - if (rbtdb->node_locks == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup_tree_lock; - } - - rbtdb->active = rbtdb->node_lock_count; - - for (i = 0; i < (int)(rbtdb->node_lock_count); i++) { - result = NODE_INITLOCK(&rbtdb->node_locks[i].lock); - if (result == ISC_R_SUCCESS) { - result = isc_refcount_init(&rbtdb->node_locks[i].references, 0); - if (result != ISC_R_SUCCESS) - NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); - } - if (result != ISC_R_SUCCESS) { - while (i-- > 0) { - NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); - isc_refcount_decrement(&rbtdb->node_locks[i].references, NULL); - isc_refcount_destroy(&rbtdb->node_locks[i].references); - } - goto cleanup_node_locks; - } - rbtdb->node_locks[i].exiting = ISC_FALSE; - } - - /* - * Attach to the mctx. The database will persist so long as there - * are references to it, and attaching to the mctx ensures that our - * mctx won't disappear out from under us. - */ - isc_mem_attach(mctx, &rbtdb->common.mctx); - - /* - * Must be initalized before free_rbtdb() is called. - */ - isc_ondestroy_init(&rbtdb->common.ondest); - - /* - * Make a copy of the origin name. - */ - result = dns_name_dupwithoffsets(origin, mctx, &rbtdb->common.origin); - if (result != ISC_R_SUCCESS) { - free_rbtdb(rbtdb, ISC_FALSE, NULL); - return (result); - } - - /* - * Make the Red-Black Tree. - */ - result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->tree); - if (result != ISC_R_SUCCESS) { - free_rbtdb(rbtdb, ISC_FALSE, NULL); - return (result); - } - /* - * In order to set the node callback bit correctly in zone databases, - * we need to know if the node has the origin name of the zone. - * In loading_addrdataset() we could simply compare the new name - * to the origin name, but this is expensive. Also, we don't know the - * node name in addrdataset(), so we need another way of knowing the - * zone's top. - * - * We now explicitly create a node for the zone's origin, and then - * we simply remember the node's address. This is safe, because - * the top-of-zone node can never be deleted, nor can its address - * change. - */ - if (!IS_CACHE(rbtdb)) { - rbtdb->origin_node = NULL; - result = dns_rbt_addnode(rbtdb->tree, &rbtdb->common.origin, - &rbtdb->origin_node); - if (result != ISC_R_SUCCESS) { - INSIST(result != ISC_R_EXISTS); - free_rbtdb(rbtdb, ISC_FALSE, NULL); - return (result); - } - /* - * We need to give the origin node the right locknum. - */ - dns_name_init(&name, NULL); - dns_rbt_namefromnode(rbtdb->origin_node, &name); + dns_rbtdb_t *rbtdb; + isc_result_t result; + int i; + dns_name_t name; + + /* Keep the compiler happy. */ + UNUSED(argc); + UNUSED(argv); + UNUSED(driverarg); + + rbtdb = isc_mem_get(mctx, sizeof(*rbtdb)); + if (rbtdb == NULL) + return (ISC_R_NOMEMORY); + + memset(rbtdb, '\0', sizeof(*rbtdb)); + dns_name_init(&rbtdb->common.origin, NULL); + rbtdb->common.attributes = 0; + if (type == dns_dbtype_cache) { + rbtdb->common.methods = &cache_methods; + rbtdb->common.attributes |= DNS_DBATTR_CACHE; + } else if (type == dns_dbtype_stub) { + rbtdb->common.methods = &zone_methods; + rbtdb->common.attributes |= DNS_DBATTR_STUB; + } else + rbtdb->common.methods = &zone_methods; + rbtdb->common.rdclass = rdclass; + rbtdb->common.mctx = NULL; + + result = RBTDB_INITLOCK(&rbtdb->lock); + if (result != ISC_R_SUCCESS) + goto cleanup_rbtdb; + + result = isc_rwlock_init(&rbtdb->tree_lock, 0, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_lock; + + if (rbtdb->node_lock_count == 0) { + if (IS_CACHE(rbtdb)) + rbtdb->node_lock_count = DEFAULT_CACHE_NODE_LOCK_COUNT; + else + rbtdb->node_lock_count = DEFAULT_NODE_LOCK_COUNT; + } + INSIST(rbtdb->node_lock_count < (1 << DNS_RBT_LOCKLENGTH)); + rbtdb->node_locks = isc_mem_get(mctx, rbtdb->node_lock_count * + sizeof(rbtdb_nodelock_t)); + if (rbtdb->node_locks == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_tree_lock; + } + + if (IS_CACHE(rbtdb)) { + rbtdb->rdatasets = isc_mem_get(mctx, rbtdb->node_lock_count * + sizeof(rdatasetheaderlist_t)); + if (rbtdb->rdatasets == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_node_locks; + } + for (i = 0; i < (int)rbtdb->node_lock_count; i++) + ISC_LIST_INIT(rbtdb->rdatasets[i]); + + rbtdb->deadnodes = isc_mem_get(mctx, rbtdb->node_lock_count * + sizeof(rbtnodelist_t)); + if (rbtdb->deadnodes == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_rdatasets; + } + for (i = 0; i < (int)rbtdb->node_lock_count; i++) + ISC_LIST_INIT(rbtdb->deadnodes[i]); + + /* + * Create the heaps. + */ + rbtdb->heaps = isc_mem_get(mctx, rbtdb->node_lock_count * + sizeof(isc_heap_t *)); + if (rbtdb->heaps == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_deadnodes; + } + for (i = 0; i < (int)rbtdb->node_lock_count; i++) + rbtdb->heaps[i] = NULL; + for (i = 0; i < (int)rbtdb->node_lock_count; i++) { + result = isc_heap_create(mctx, ttl_sooner, + ttl_set_index, 0, + &rbtdb->heaps[i]); + if (result != ISC_R_SUCCESS) + goto cleanup_heaps; + } + } else { + rbtdb->rdatasets = NULL; + rbtdb->deadnodes = NULL; + rbtdb->heaps = NULL; + } + + rbtdb->active = rbtdb->node_lock_count; + + for (i = 0; i < (int)(rbtdb->node_lock_count); i++) { + result = NODE_INITLOCK(&rbtdb->node_locks[i].lock); + if (result == ISC_R_SUCCESS) { + result = isc_refcount_init(&rbtdb->node_locks[i].references, 0); + if (result != ISC_R_SUCCESS) + NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); + } + if (result != ISC_R_SUCCESS) { + while (i-- > 0) { + NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); + isc_refcount_decrement(&rbtdb->node_locks[i].references, NULL); + isc_refcount_destroy(&rbtdb->node_locks[i].references); + } + goto cleanup_heaps; + } + rbtdb->node_locks[i].exiting = ISC_FALSE; + } + + /* + * Attach to the mctx. The database will persist so long as there + * are references to it, and attaching to the mctx ensures that our + * mctx won't disappear out from under us. + */ + isc_mem_attach(mctx, &rbtdb->common.mctx); + + /* + * Must be initalized before free_rbtdb() is called. + */ + isc_ondestroy_init(&rbtdb->common.ondest); + + /* + * Make a copy of the origin name. + */ + result = dns_name_dupwithoffsets(origin, mctx, &rbtdb->common.origin); + if (result != ISC_R_SUCCESS) { + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (result); + } + + /* + * Make the Red-Black Tree. + */ + result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->tree); + if (result != ISC_R_SUCCESS) { + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (result); + } + /* + * In order to set the node callback bit correctly in zone databases, + * we need to know if the node has the origin name of the zone. + * In loading_addrdataset() we could simply compare the new name + * to the origin name, but this is expensive. Also, we don't know the + * node name in addrdataset(), so we need another way of knowing the + * zone's top. + * + * We now explicitly create a node for the zone's origin, and then + * we simply remember the node's address. This is safe, because + * the top-of-zone node can never be deleted, nor can its address + * change. + */ + if (!IS_CACHE(rbtdb)) { + rbtdb->origin_node = NULL; + result = dns_rbt_addnode(rbtdb->tree, &rbtdb->common.origin, + &rbtdb->origin_node); + if (result != ISC_R_SUCCESS) { + INSIST(result != ISC_R_EXISTS); + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (result); + } + /* + * We need to give the origin node the right locknum. + */ + dns_name_init(&name, NULL); + dns_rbt_namefromnode(rbtdb->origin_node, &name); #ifdef DNS_RBT_USEHASH - rbtdb->origin_node->locknum = - rbtdb->origin_node->hashval % - rbtdb->node_lock_count; + rbtdb->origin_node->locknum = + rbtdb->origin_node->hashval % + rbtdb->node_lock_count; #else - rbtdb->origin_node->locknum = - dns_name_hash(&name, ISC_TRUE) % - rbtdb->node_lock_count; + rbtdb->origin_node->locknum = + dns_name_hash(&name, ISC_TRUE) % + rbtdb->node_lock_count; #endif - } - - /* - * Misc. Initialization. - */ - result = isc_refcount_init(&rbtdb->references, 1); - if (result != ISC_R_SUCCESS) { - free_rbtdb(rbtdb, ISC_FALSE, NULL); - return (result); - } - rbtdb->attributes = 0; - rbtdb->secure = ISC_FALSE; - rbtdb->overmem = ISC_FALSE; - rbtdb->task = NULL; - - /* - * Version Initialization. - */ - rbtdb->current_serial = 1; - rbtdb->least_serial = 1; - rbtdb->next_serial = 2; - rbtdb->current_version = allocate_version(mctx, 1, 1, ISC_FALSE); - if (rbtdb->current_version == NULL) { - isc_refcount_decrement(&rbtdb->references, NULL); - isc_refcount_destroy(&rbtdb->references); - free_rbtdb(rbtdb, ISC_FALSE, NULL); - return (ISC_R_NOMEMORY); - } - rbtdb->future_version = NULL; - ISC_LIST_INIT(rbtdb->open_versions); - /* - * Keep the current version in the open list so that list operation - * won't happen in normal lookup operations. - */ - PREPEND(rbtdb->open_versions, rbtdb->current_version, link); - - rbtdb->common.magic = DNS_DB_MAGIC; - rbtdb->common.impmagic = RBTDB_MAGIC; - - *dbp = (dns_db_t *)rbtdb; - - return (ISC_R_SUCCESS); + } + + /* + * Misc. Initialization. + */ + result = isc_refcount_init(&rbtdb->references, 1); + if (result != ISC_R_SUCCESS) { + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (result); + } + rbtdb->attributes = 0; + rbtdb->secure = ISC_FALSE; + rbtdb->overmem = ISC_FALSE; + rbtdb->task = NULL; + + /* + * Version Initialization. + */ + rbtdb->current_serial = 1; + rbtdb->least_serial = 1; + rbtdb->next_serial = 2; + rbtdb->current_version = allocate_version(mctx, 1, 1, ISC_FALSE); + if (rbtdb->current_version == NULL) { + isc_refcount_decrement(&rbtdb->references, NULL); + isc_refcount_destroy(&rbtdb->references); + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (ISC_R_NOMEMORY); + } + rbtdb->future_version = NULL; + ISC_LIST_INIT(rbtdb->open_versions); + /* + * Keep the current version in the open list so that list operation + * won't happen in normal lookup operations. + */ + PREPEND(rbtdb->open_versions, rbtdb->current_version, link); + + rbtdb->common.magic = DNS_DB_MAGIC; + rbtdb->common.impmagic = RBTDB_MAGIC; + + *dbp = (dns_db_t *)rbtdb; + + return (ISC_R_SUCCESS); + + cleanup_heaps: + if (rbtdb->heaps != NULL) { + for (i = 0 ; i < (int)rbtdb->node_lock_count ; i++) + if (rbtdb->heaps[i] != NULL) + isc_heap_destroy(&rbtdb->heaps[i]); + isc_mem_put(mctx, rbtdb->heaps, + rbtdb->node_lock_count * sizeof(isc_heap_t *)); + } + + cleanup_deadnodes: + if (rbtdb->deadnodes != NULL) + isc_mem_put(mctx, rbtdb->deadnodes, + rbtdb->node_lock_count * sizeof(rbtnodelist_t)); + + cleanup_rdatasets: + if (rbtdb->rdatasets != NULL) + isc_mem_put(mctx, rbtdb->rdatasets, rbtdb->node_lock_count * + sizeof(rdatasetheaderlist_t)); cleanup_node_locks: - isc_mem_put(mctx, rbtdb->node_locks, - rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); + isc_mem_put(mctx, rbtdb->node_locks, + rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); cleanup_tree_lock: - isc_rwlock_destroy(&rbtdb->tree_lock); + isc_rwlock_destroy(&rbtdb->tree_lock); cleanup_lock: - RBTDB_DESTROYLOCK(&rbtdb->lock); + RBTDB_DESTROYLOCK(&rbtdb->lock); cleanup_rbtdb: - isc_mem_put(mctx, rbtdb, sizeof(*rbtdb)); - return (result); + isc_mem_put(mctx, rbtdb, sizeof(*rbtdb)); + return (result); } @@ -5647,178 +6351,178 @@ dns_rbtdb_create static void rdataset_disassociate(dns_rdataset_t *rdataset) { - dns_db_t *db = rdataset->private1; - dns_dbnode_t *node = rdataset->private2; + dns_db_t *db = rdataset->private1; + dns_dbnode_t *node = rdataset->private2; - detachnode(db, &node); + detachnode(db, &node); } static isc_result_t rdataset_first(dns_rdataset_t *rdataset) { - unsigned char *raw = rdataset->private3; /* RDATASLAB */ - unsigned int count; - - count = raw[0] * 256 + raw[1]; - if (count == 0) { - rdataset->private5 = NULL; - return (ISC_R_NOMORE); - } - + unsigned char *raw = rdataset->private3; /* RDATASLAB */ + unsigned int count; + + count = raw[0] * 256 + raw[1]; + if (count == 0) { + rdataset->private5 = NULL; + return (ISC_R_NOMORE); + } + #if DNS_RDATASET_FIXED - if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0) - raw += 2 + (4 * count); - else + if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0) + raw += 2 + (4 * count); + else #endif - raw += 2; - - /* - * The privateuint4 field is the number of rdata beyond the - * cursor position, so we decrement the total count by one - * before storing it. - * - * If DNS_RDATASETATTR_LOADORDER is not set 'raw' points to the - * first record. If DNS_RDATASETATTR_LOADORDER is set 'raw' points - * to the first entry in the offset table. - */ - count--; - rdataset->privateuint4 = count; - rdataset->private5 = raw; - - return (ISC_R_SUCCESS); + raw += 2; + + /* + * The privateuint4 field is the number of rdata beyond the + * cursor position, so we decrement the total count by one + * before storing it. + * + * If DNS_RDATASETATTR_LOADORDER is not set 'raw' points to the + * first record. If DNS_RDATASETATTR_LOADORDER is set 'raw' points + * to the first entry in the offset table. + */ + count--; + rdataset->privateuint4 = count; + rdataset->private5 = raw; + + return (ISC_R_SUCCESS); } static isc_result_t rdataset_next(dns_rdataset_t *rdataset) { - unsigned int count; - unsigned int length; - unsigned char *raw; /* RDATASLAB */ - - count = rdataset->privateuint4; - if (count == 0) - return (ISC_R_NOMORE); - count--; - rdataset->privateuint4 = count; - - /* - * Skip forward one record (length + 4) or one offset (4). - */ - raw = rdataset->private5; + unsigned int count; + unsigned int length; + unsigned char *raw; /* RDATASLAB */ + + count = rdataset->privateuint4; + if (count == 0) + return (ISC_R_NOMORE); + count--; + rdataset->privateuint4 = count; + + /* + * Skip forward one record (length + 4) or one offset (4). + */ + raw = rdataset->private5; #if DNS_RDATASET_FIXED - if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0) { + if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0) { #endif - length = raw[0] * 256 + raw[1]; - raw += length; + length = raw[0] * 256 + raw[1]; + raw += length; #if DNS_RDATASET_FIXED - } - rdataset->private5 = raw + 4; /* length(2) + order(2) */ + } + rdataset->private5 = raw + 4; /* length(2) + order(2) */ #else - rdataset->private5 = raw + 2; /* length(2) */ + rdataset->private5 = raw + 2; /* length(2) */ #endif - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } static void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { - unsigned char *raw = rdataset->private5; /* RDATASLAB */ + unsigned char *raw = rdataset->private5; /* RDATASLAB */ #if DNS_RDATASET_FIXED - unsigned int offset; + unsigned int offset; #endif - isc_region_t r; + isc_region_t r; - REQUIRE(raw != NULL); + REQUIRE(raw != NULL); - /* - * Find the start of the record if not already in private5 - * then skip the length and order fields. - */ + /* + * Find the start of the record if not already in private5 + * then skip the length and order fields. + */ #if DNS_RDATASET_FIXED - if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) != 0) { - offset = (raw[0] << 24) + (raw[1] << 16) + - (raw[2] << 8) + raw[3]; - raw = rdataset->private3; - raw += offset; - } + if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) != 0) { + offset = (raw[0] << 24) + (raw[1] << 16) + + (raw[2] << 8) + raw[3]; + raw = rdataset->private3; + raw += offset; + } #endif - r.length = raw[0] * 256 + raw[1]; + r.length = raw[0] * 256 + raw[1]; #if DNS_RDATASET_FIXED - raw += 4; + raw += 4; #else - raw += 2; + raw += 2; #endif - r.base = raw; - dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r); + r.base = raw; + dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r); } static void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { - dns_db_t *db = source->private1; - dns_dbnode_t *node = source->private2; - dns_dbnode_t *cloned_node = NULL; - - attachnode(db, node, &cloned_node); - *target = *source; - - /* - * Reset iterator state. - */ - target->privateuint4 = 0; - target->private5 = NULL; + dns_db_t *db = source->private1; + dns_dbnode_t *node = source->private2; + dns_dbnode_t *cloned_node = NULL; + + attachnode(db, node, &cloned_node); + *target = *source; + + /* + * Reset iterator state. + */ + target->privateuint4 = 0; + target->private5 = NULL; } static unsigned int rdataset_count(dns_rdataset_t *rdataset) { - unsigned char *raw = rdataset->private3; /* RDATASLAB */ - unsigned int count; + unsigned char *raw = rdataset->private3; /* RDATASLAB */ + unsigned int count; - count = raw[0] * 256 + raw[1]; + count = raw[0] * 256 + raw[1]; - return (count); + return (count); } static isc_result_t rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, - dns_rdataset_t *nsec, dns_rdataset_t *nsecsig) + dns_rdataset_t *nsec, dns_rdataset_t *nsecsig) { - dns_db_t *db = rdataset->private1; - dns_dbnode_t *node = rdataset->private2; - dns_dbnode_t *cloned_node; - struct noqname *noqname = rdataset->private6; - - cloned_node = NULL; - attachnode(db, node, &cloned_node); - nsec->methods = &rdataset_methods; - nsec->rdclass = db->rdclass; - nsec->type = dns_rdatatype_nsec; - nsec->covers = 0; - nsec->ttl = rdataset->ttl; - nsec->trust = rdataset->trust; - nsec->private1 = rdataset->private1; - nsec->private2 = rdataset->private2; - nsec->private3 = noqname->nsec; - nsec->privateuint4 = 0; - nsec->private5 = NULL; - nsec->private6 = NULL; - - cloned_node = NULL; - attachnode(db, node, &cloned_node); - nsecsig->methods = &rdataset_methods; - nsecsig->rdclass = db->rdclass; - nsecsig->type = dns_rdatatype_rrsig; - nsecsig->covers = dns_rdatatype_nsec; - nsecsig->ttl = rdataset->ttl; - nsecsig->trust = rdataset->trust; - nsecsig->private1 = rdataset->private1; - nsecsig->private2 = rdataset->private2; - nsecsig->private3 = noqname->nsecsig; - nsecsig->privateuint4 = 0; - nsecsig->private5 = NULL; - nsec->private6 = NULL; - - dns_name_clone(&noqname->name, name); - - return (ISC_R_SUCCESS); + dns_db_t *db = rdataset->private1; + dns_dbnode_t *node = rdataset->private2; + dns_dbnode_t *cloned_node; + struct noqname *noqname = rdataset->private6; + + cloned_node = NULL; + attachnode(db, node, &cloned_node); + nsec->methods = &rdataset_methods; + nsec->rdclass = db->rdclass; + nsec->type = dns_rdatatype_nsec; + nsec->covers = 0; + nsec->ttl = rdataset->ttl; + nsec->trust = rdataset->trust; + nsec->private1 = rdataset->private1; + nsec->private2 = rdataset->private2; + nsec->private3 = noqname->nsec; + nsec->privateuint4 = 0; + nsec->private5 = NULL; + nsec->private6 = NULL; + + cloned_node = NULL; + attachnode(db, node, &cloned_node); + nsecsig->methods = &rdataset_methods; + nsecsig->rdclass = db->rdclass; + nsecsig->type = dns_rdatatype_rrsig; + nsecsig->covers = dns_rdatatype_nsec; + nsecsig->ttl = rdataset->ttl; + nsecsig->trust = rdataset->trust; + nsecsig->private1 = rdataset->private1; + nsecsig->private2 = rdataset->private2; + nsecsig->private3 = noqname->nsecsig; + nsecsig->privateuint4 = 0; + nsecsig->private5 = NULL; + nsec->private6 = NULL; + + dns_name_clone(&noqname->name, name); + + return (ISC_R_SUCCESS); } /* @@ -5827,172 +6531,172 @@ rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) { - rbtdb_rdatasetiter_t *rbtiterator; + rbtdb_rdatasetiter_t *rbtiterator; - rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp); + rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp); - if (rbtiterator->common.version != NULL) - closeversion(rbtiterator->common.db, - &rbtiterator->common.version, ISC_FALSE); - detachnode(rbtiterator->common.db, &rbtiterator->common.node); - isc_mem_put(rbtiterator->common.db->mctx, rbtiterator, - sizeof(*rbtiterator)); + if (rbtiterator->common.version != NULL) + closeversion(rbtiterator->common.db, + &rbtiterator->common.version, ISC_FALSE); + detachnode(rbtiterator->common.db, &rbtiterator->common.node); + isc_mem_put(rbtiterator->common.db->mctx, rbtiterator, + sizeof(*rbtiterator)); - *iteratorp = NULL; + *iteratorp = NULL; } static isc_result_t rdatasetiter_first(dns_rdatasetiter_t *iterator) { - rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); - dns_rbtnode_t *rbtnode = rbtiterator->common.node; - rbtdb_version_t *rbtversion = rbtiterator->common.version; - rdatasetheader_t *header, *top_next; - rbtdb_serial_t serial; - isc_stdtime_t now; - - if (IS_CACHE(rbtdb)) { - serial = 1; - now = rbtiterator->common.now; - } else { - serial = rbtversion->serial; - now = 0; - } - - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); - - for (header = rbtnode->data; header != NULL; header = top_next) { - top_next = header->next; - do { - if (header->serial <= serial && !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't exist" - * record? Or is it too old in the cache? - * - * Note: unlike everywhere else, we - * check for now > header->ttl instead - * of now >= header->ttl. This allows - * ANY and RRSIG queries for 0 TTL - * rdatasets to work. - */ - if (NONEXISTENT(header) || - (now != 0 && now > header->ttl)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) - break; - } - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); - - rbtiterator->current = header; - - if (header == NULL) - return (ISC_R_NOMORE); - - return (ISC_R_SUCCESS); + rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); + dns_rbtnode_t *rbtnode = rbtiterator->common.node; + rbtdb_version_t *rbtversion = rbtiterator->common.version; + rdatasetheader_t *header, *top_next; + rbtdb_serial_t serial; + isc_stdtime_t now; + + if (IS_CACHE(rbtdb)) { + serial = 1; + now = rbtiterator->common.now; + } else { + serial = rbtversion->serial; + now = 0; + } + + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + + for (header = rbtnode->data; header != NULL; header = top_next) { + top_next = header->next; + do { + if (header->serial <= serial && !IGNORE(header)) { + /* + * Is this a "this rdataset doesn't exist" + * record? Or is it too old in the cache? + * + * Note: unlike everywhere else, we + * check for now > header->ttl instead + * of now >= header->ttl. This allows + * ANY and RRSIG queries for 0 TTL + * rdatasets to work. + */ + if (NONEXISTENT(header) || + (now != 0 && now > header->rdh_ttl)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) + break; + } + + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + + rbtiterator->current = header; + + if (header == NULL) + return (ISC_R_NOMORE); + + return (ISC_R_SUCCESS); } static isc_result_t rdatasetiter_next(dns_rdatasetiter_t *iterator) { - rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); - dns_rbtnode_t *rbtnode = rbtiterator->common.node; - rbtdb_version_t *rbtversion = rbtiterator->common.version; - rdatasetheader_t *header, *top_next; - rbtdb_serial_t serial; - isc_stdtime_t now; - rbtdb_rdatatype_t type, negtype; - dns_rdatatype_t rdtype, covers; - - header = rbtiterator->current; - if (header == NULL) - return (ISC_R_NOMORE); - - if (IS_CACHE(rbtdb)) { - serial = 1; - now = rbtiterator->common.now; - } else { - serial = rbtversion->serial; - now = 0; - } - - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); - - type = header->type; - rdtype = RBTDB_RDATATYPE_BASE(header->type); - if (rdtype == 0) { - covers = RBTDB_RDATATYPE_EXT(header->type); - negtype = RBTDB_RDATATYPE_VALUE(covers, 0); - } else - negtype = RBTDB_RDATATYPE_VALUE(0, rdtype); - for (header = header->next; header != NULL; header = top_next) { - top_next = header->next; - /* - * If not walking back up the down list. - */ - if (header->type != type && header->type != negtype) { - do { - if (header->serial <= serial && - !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't - * exist" record? - * - * Note: unlike everywhere else, we - * check for now > header->ttl instead - * of now >= header->ttl. This allows - * ANY and RRSIG queries for 0 TTL - * rdatasets to work. - */ - if ((header->attributes & - RDATASET_ATTR_NONEXISTENT) != 0 || - (now != 0 && now > header->ttl)) - header = NULL; - break; - } else - header = header->down; - } while (header != NULL); - if (header != NULL) - break; - } - } - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); - - rbtiterator->current = header; - - if (header == NULL) - return (ISC_R_NOMORE); - - return (ISC_R_SUCCESS); + rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); + dns_rbtnode_t *rbtnode = rbtiterator->common.node; + rbtdb_version_t *rbtversion = rbtiterator->common.version; + rdatasetheader_t *header, *top_next; + rbtdb_serial_t serial; + isc_stdtime_t now; + rbtdb_rdatatype_t type, negtype; + dns_rdatatype_t rdtype, covers; + + header = rbtiterator->current; + if (header == NULL) + return (ISC_R_NOMORE); + + if (IS_CACHE(rbtdb)) { + serial = 1; + now = rbtiterator->common.now; + } else { + serial = rbtversion->serial; + now = 0; + } + + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + + type = header->type; + rdtype = RBTDB_RDATATYPE_BASE(header->type); + if (rdtype == 0) { + covers = RBTDB_RDATATYPE_EXT(header->type); + negtype = RBTDB_RDATATYPE_VALUE(covers, 0); + } else + negtype = RBTDB_RDATATYPE_VALUE(0, rdtype); + for (header = header->next; header != NULL; header = top_next) { + top_next = header->next; + /* + * If not walking back up the down list. + */ + if (header->type != type && header->type != negtype) { + do { + if (header->serial <= serial && + !IGNORE(header)) { + /* + * Is this a "this rdataset doesn't + * exist" record? + * + * Note: unlike everywhere else, we + * check for now > header->ttl instead + * of now >= header->ttl. This allows + * ANY and RRSIG queries for 0 TTL + * rdatasets to work. + */ + if ((header->attributes & + RDATASET_ATTR_NONEXISTENT) != 0 || + (now != 0 && now > header->rdh_ttl)) + header = NULL; + break; + } else + header = header->down; + } while (header != NULL); + if (header != NULL) + break; + } + } + + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + + rbtiterator->current = header; + + if (header == NULL) + return (ISC_R_NOMORE); + + return (ISC_R_SUCCESS); } static void rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) { - rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); - dns_rbtnode_t *rbtnode = rbtiterator->common.node; - rdatasetheader_t *header; + rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); + dns_rbtnode_t *rbtnode = rbtiterator->common.node; + rdatasetheader_t *header; - header = rbtiterator->current; - REQUIRE(header != NULL); + header = rbtiterator->current; + REQUIRE(header != NULL); - NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); - bind_rdataset(rbtdb, rbtnode, header, rbtiterator->common.now, - rdataset); + bind_rdataset(rbtdb, rbtnode, header, rbtiterator->common.now, + rdataset); - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - isc_rwlocktype_read); + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); } @@ -6002,410 +6706,410 @@ rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) { static inline void reference_iter_node(rbtdb_dbiterator_t *rbtdbiter) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - dns_rbtnode_t *node = rbtdbiter->node; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; + dns_rbtnode_t *node = rbtdbiter->node; - if (node == NULL) - return; + if (node == NULL) + return; - INSIST(rbtdbiter->tree_locked != isc_rwlocktype_none); - NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); - new_reference(rbtdb, node); - NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); + INSIST(rbtdbiter->tree_locked != isc_rwlocktype_none); + NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); + new_reference(rbtdb, node); + NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); } static inline void dereference_iter_node(rbtdb_dbiterator_t *rbtdbiter) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - dns_rbtnode_t *node = rbtdbiter->node; - nodelock_t *lock; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; + dns_rbtnode_t *node = rbtdbiter->node; + nodelock_t *lock; - if (node == NULL) - return; + if (node == NULL) + return; - lock = &rbtdb->node_locks[node->locknum].lock; - NODE_LOCK(lock, isc_rwlocktype_read); - decrement_reference(rbtdb, node, 0, isc_rwlocktype_read, - rbtdbiter->tree_locked); - NODE_UNLOCK(lock, isc_rwlocktype_read); + lock = &rbtdb->node_locks[node->locknum].lock; + NODE_LOCK(lock, isc_rwlocktype_read); + decrement_reference(rbtdb, node, 0, isc_rwlocktype_read, + rbtdbiter->tree_locked); + NODE_UNLOCK(lock, isc_rwlocktype_read); - rbtdbiter->node = NULL; + rbtdbiter->node = NULL; } static void flush_deletions(rbtdb_dbiterator_t *rbtdbiter) { - dns_rbtnode_t *node; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - isc_boolean_t was_read_locked = ISC_FALSE; - nodelock_t *lock; - int i; - - if (rbtdbiter->delete != 0) { - /* - * Note that "%d node of %d in tree" can report things like - * "flush_deletions: 59 nodes of 41 in tree". This means - * That some nodes appear on the deletions list more than - * once. Only the last occurence will actually be deleted. - */ - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "flush_deletions: %d nodes of %d in tree", - rbtdbiter->delete, - dns_rbt_nodecount(rbtdb->tree)); - - if (rbtdbiter->tree_locked == isc_rwlocktype_read) { - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - was_read_locked = ISC_TRUE; - } - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - rbtdbiter->tree_locked = isc_rwlocktype_write; - - for (i = 0; i < rbtdbiter->delete; i++) { - node = rbtdbiter->deletions[i]; - lock = &rbtdb->node_locks[node->locknum].lock; - - NODE_LOCK(lock, isc_rwlocktype_read); - decrement_reference(rbtdb, node, 0, - isc_rwlocktype_read, - rbtdbiter->tree_locked); - NODE_UNLOCK(lock, isc_rwlocktype_read); - } - - rbtdbiter->delete = 0; - - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); - if (was_read_locked) { - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - rbtdbiter->tree_locked = isc_rwlocktype_read; - - } else { - rbtdbiter->tree_locked = isc_rwlocktype_none; - } - } + dns_rbtnode_t *node; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; + isc_boolean_t was_read_locked = ISC_FALSE; + nodelock_t *lock; + int i; + + if (rbtdbiter->delete != 0) { + /* + * Note that "%d node of %d in tree" can report things like + * "flush_deletions: 59 nodes of 41 in tree". This means + * That some nodes appear on the deletions list more than + * once. Only the last occurence will actually be deleted. + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), + "flush_deletions: %d nodes of %d in tree", + rbtdbiter->delete, + dns_rbt_nodecount(rbtdb->tree)); + + if (rbtdbiter->tree_locked == isc_rwlocktype_read) { + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + was_read_locked = ISC_TRUE; + } + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); + rbtdbiter->tree_locked = isc_rwlocktype_write; + + for (i = 0; i < rbtdbiter->delete; i++) { + node = rbtdbiter->deletions[i]; + lock = &rbtdb->node_locks[node->locknum].lock; + + NODE_LOCK(lock, isc_rwlocktype_read); + decrement_reference(rbtdb, node, 0, + isc_rwlocktype_read, + rbtdbiter->tree_locked); + NODE_UNLOCK(lock, isc_rwlocktype_read); + } + + rbtdbiter->delete = 0; + + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); + if (was_read_locked) { + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + rbtdbiter->tree_locked = isc_rwlocktype_read; + + } else { + rbtdbiter->tree_locked = isc_rwlocktype_none; + } + } } static inline void resume_iteration(rbtdb_dbiterator_t *rbtdbiter) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - REQUIRE(rbtdbiter->paused); - REQUIRE(rbtdbiter->tree_locked == isc_rwlocktype_none); + REQUIRE(rbtdbiter->paused); + REQUIRE(rbtdbiter->tree_locked == isc_rwlocktype_none); - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - rbtdbiter->tree_locked = isc_rwlocktype_read; + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + rbtdbiter->tree_locked = isc_rwlocktype_read; - rbtdbiter->paused = ISC_FALSE; + rbtdbiter->paused = ISC_FALSE; } static void dbiterator_destroy(dns_dbiterator_t **iteratorp) { - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)(*iteratorp); - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - dns_db_t *db = NULL; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)(*iteratorp); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; + dns_db_t *db = NULL; - if (rbtdbiter->tree_locked == isc_rwlocktype_read) { - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - rbtdbiter->tree_locked = isc_rwlocktype_none; - } else - INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none); + if (rbtdbiter->tree_locked == isc_rwlocktype_read) { + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + rbtdbiter->tree_locked = isc_rwlocktype_none; + } else + INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none); - dereference_iter_node(rbtdbiter); + dereference_iter_node(rbtdbiter); - flush_deletions(rbtdbiter); + flush_deletions(rbtdbiter); - dns_db_attach(rbtdbiter->common.db, &db); - dns_db_detach(&rbtdbiter->common.db); + dns_db_attach(rbtdbiter->common.db, &db); + dns_db_detach(&rbtdbiter->common.db); - dns_rbtnodechain_reset(&rbtdbiter->chain); - isc_mem_put(db->mctx, rbtdbiter, sizeof(*rbtdbiter)); - dns_db_detach(&db); + dns_rbtnodechain_reset(&rbtdbiter->chain); + isc_mem_put(db->mctx, rbtdbiter, sizeof(*rbtdbiter)); + dns_db_detach(&db); - *iteratorp = NULL; + *iteratorp = NULL; } static isc_result_t dbiterator_first(dns_dbiterator_t *iterator) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - dns_name_t *name, *origin; - - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOMORE) - return (rbtdbiter->result); - - if (rbtdbiter->paused) - resume_iteration(rbtdbiter); - - dereference_iter_node(rbtdbiter); - - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - dns_rbtnodechain_reset(&rbtdbiter->chain); - - result = dns_rbtnodechain_first(&rbtdbiter->chain, rbtdb->tree, name, - origin); - - if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, - NULL, &rbtdbiter->node); - if (result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = ISC_TRUE; - reference_iter_node(rbtdbiter); - } - } else { - INSIST(result == ISC_R_NOTFOUND); - result = ISC_R_NOMORE; /* The tree is empty. */ - } - - rbtdbiter->result = result; - - return (result); + isc_result_t result; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; + dns_name_t *name, *origin; + + if (rbtdbiter->result != ISC_R_SUCCESS && + rbtdbiter->result != ISC_R_NOMORE) + return (rbtdbiter->result); + + if (rbtdbiter->paused) + resume_iteration(rbtdbiter); + + dereference_iter_node(rbtdbiter); + + name = dns_fixedname_name(&rbtdbiter->name); + origin = dns_fixedname_name(&rbtdbiter->origin); + dns_rbtnodechain_reset(&rbtdbiter->chain); + + result = dns_rbtnodechain_first(&rbtdbiter->chain, rbtdb->tree, name, + origin); + + if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { + result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, + NULL, &rbtdbiter->node); + if (result == ISC_R_SUCCESS) { + rbtdbiter->new_origin = ISC_TRUE; + reference_iter_node(rbtdbiter); + } + } else { + INSIST(result == ISC_R_NOTFOUND); + result = ISC_R_NOMORE; /* The tree is empty. */ + } + + rbtdbiter->result = result; + + return (result); } static isc_result_t dbiterator_last(dns_dbiterator_t *iterator) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - dns_name_t *name, *origin; - - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOMORE) - return (rbtdbiter->result); - - if (rbtdbiter->paused) - resume_iteration(rbtdbiter); - - dereference_iter_node(rbtdbiter); - - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - dns_rbtnodechain_reset(&rbtdbiter->chain); - - result = dns_rbtnodechain_last(&rbtdbiter->chain, rbtdb->tree, name, - origin); - if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, - NULL, &rbtdbiter->node); - if (result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = ISC_TRUE; - reference_iter_node(rbtdbiter); - } - } else { - INSIST(result == ISC_R_NOTFOUND); - result = ISC_R_NOMORE; /* The tree is empty. */ - } - - rbtdbiter->result = result; - - return (result); + isc_result_t result; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; + dns_name_t *name, *origin; + + if (rbtdbiter->result != ISC_R_SUCCESS && + rbtdbiter->result != ISC_R_NOMORE) + return (rbtdbiter->result); + + if (rbtdbiter->paused) + resume_iteration(rbtdbiter); + + dereference_iter_node(rbtdbiter); + + name = dns_fixedname_name(&rbtdbiter->name); + origin = dns_fixedname_name(&rbtdbiter->origin); + dns_rbtnodechain_reset(&rbtdbiter->chain); + + result = dns_rbtnodechain_last(&rbtdbiter->chain, rbtdb->tree, name, + origin); + if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { + result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, + NULL, &rbtdbiter->node); + if (result == ISC_R_SUCCESS) { + rbtdbiter->new_origin = ISC_TRUE; + reference_iter_node(rbtdbiter); + } + } else { + INSIST(result == ISC_R_NOTFOUND); + result = ISC_R_NOMORE; /* The tree is empty. */ + } + + rbtdbiter->result = result; + + return (result); } static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - dns_name_t *iname, *origin; + isc_result_t result; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; + dns_name_t *iname, *origin; - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOMORE) - return (rbtdbiter->result); + if (rbtdbiter->result != ISC_R_SUCCESS && + rbtdbiter->result != ISC_R_NOMORE) + return (rbtdbiter->result); - if (rbtdbiter->paused) - resume_iteration(rbtdbiter); + if (rbtdbiter->paused) + resume_iteration(rbtdbiter); - dereference_iter_node(rbtdbiter); + dereference_iter_node(rbtdbiter); - iname = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - dns_rbtnodechain_reset(&rbtdbiter->chain); + iname = dns_fixedname_name(&rbtdbiter->name); + origin = dns_fixedname_name(&rbtdbiter->origin); + dns_rbtnodechain_reset(&rbtdbiter->chain); - result = dns_rbt_findnode(rbtdb->tree, name, NULL, &rbtdbiter->node, - &rbtdbiter->chain, DNS_RBTFIND_EMPTYDATA, - NULL, NULL); - if (result == ISC_R_SUCCESS) { - result = dns_rbtnodechain_current(&rbtdbiter->chain, iname, - origin, NULL); - if (result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = ISC_TRUE; - reference_iter_node(rbtdbiter); - } + result = dns_rbt_findnode(rbtdb->tree, name, NULL, &rbtdbiter->node, + &rbtdbiter->chain, DNS_RBTFIND_EMPTYDATA, + NULL, NULL); + if (result == ISC_R_SUCCESS) { + result = dns_rbtnodechain_current(&rbtdbiter->chain, iname, + origin, NULL); + if (result == ISC_R_SUCCESS) { + rbtdbiter->new_origin = ISC_TRUE; + reference_iter_node(rbtdbiter); + } - } else if (result == DNS_R_PARTIALMATCH) - result = ISC_R_NOTFOUND; + } else if (result == DNS_R_PARTIALMATCH) + result = ISC_R_NOTFOUND; - rbtdbiter->result = result; + rbtdbiter->result = result; - return (result); + return (result); } static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_name_t *name, *origin; + isc_result_t result; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_name_t *name, *origin; - REQUIRE(rbtdbiter->node != NULL); + REQUIRE(rbtdbiter->node != NULL); - if (rbtdbiter->result != ISC_R_SUCCESS) - return (rbtdbiter->result); + if (rbtdbiter->result != ISC_R_SUCCESS) + return (rbtdbiter->result); - if (rbtdbiter->paused) - resume_iteration(rbtdbiter); + if (rbtdbiter->paused) + resume_iteration(rbtdbiter); - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - result = dns_rbtnodechain_prev(&rbtdbiter->chain, name, origin); + name = dns_fixedname_name(&rbtdbiter->name); + origin = dns_fixedname_name(&rbtdbiter->origin); + result = dns_rbtnodechain_prev(&rbtdbiter->chain, name, origin); - dereference_iter_node(rbtdbiter); + dereference_iter_node(rbtdbiter); - if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN); - result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, - NULL, &rbtdbiter->node); - } + if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { + rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN); + result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, + NULL, &rbtdbiter->node); + } - if (result == ISC_R_SUCCESS) - reference_iter_node(rbtdbiter); + if (result == ISC_R_SUCCESS) + reference_iter_node(rbtdbiter); - rbtdbiter->result = result; + rbtdbiter->result = result; - return (result); + return (result); } static isc_result_t dbiterator_next(dns_dbiterator_t *iterator) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_name_t *name, *origin; + isc_result_t result; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_name_t *name, *origin; - REQUIRE(rbtdbiter->node != NULL); + REQUIRE(rbtdbiter->node != NULL); - if (rbtdbiter->result != ISC_R_SUCCESS) - return (rbtdbiter->result); + if (rbtdbiter->result != ISC_R_SUCCESS) + return (rbtdbiter->result); - if (rbtdbiter->paused) - resume_iteration(rbtdbiter); + if (rbtdbiter->paused) + resume_iteration(rbtdbiter); - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - result = dns_rbtnodechain_next(&rbtdbiter->chain, name, origin); + name = dns_fixedname_name(&rbtdbiter->name); + origin = dns_fixedname_name(&rbtdbiter->origin); + result = dns_rbtnodechain_next(&rbtdbiter->chain, name, origin); - dereference_iter_node(rbtdbiter); + dereference_iter_node(rbtdbiter); - if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN); - result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, - NULL, &rbtdbiter->node); - } - if (result == ISC_R_SUCCESS) - reference_iter_node(rbtdbiter); + if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { + rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN); + result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL, + NULL, &rbtdbiter->node); + } + if (result == ISC_R_SUCCESS) + reference_iter_node(rbtdbiter); - rbtdbiter->result = result; + rbtdbiter->result = result; - return (result); + return (result); } static isc_result_t dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, - dns_name_t *name) + dns_name_t *name) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtnode_t *node = rbtdbiter->node; - isc_result_t result; - dns_name_t *nodename = dns_fixedname_name(&rbtdbiter->name); - dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin); - - REQUIRE(rbtdbiter->result == ISC_R_SUCCESS); - REQUIRE(rbtdbiter->node != NULL); - - if (rbtdbiter->paused) - resume_iteration(rbtdbiter); - - if (name != NULL) { - if (rbtdbiter->common.relative_names) - origin = NULL; - result = dns_name_concatenate(nodename, origin, name, NULL); - if (result != ISC_R_SUCCESS) - return (result); - if (rbtdbiter->common.relative_names && rbtdbiter->new_origin) - result = DNS_R_NEWORIGIN; - } else - result = ISC_R_SUCCESS; - - NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); - new_reference(rbtdb, node); - NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); - - *nodep = rbtdbiter->node; - - if (iterator->cleaning && result == ISC_R_SUCCESS) { - isc_result_t expire_result; - - /* - * If the deletion array is full, flush it before trying - * to expire the current node. The current node can't - * fully deleted while the iteration cursor is still on it. - */ - if (rbtdbiter->delete == DELETION_BATCH_MAX) - flush_deletions(rbtdbiter); - - expire_result = expirenode(iterator->db, *nodep, 0); - - /* - * expirenode() currently always returns success. - */ - if (expire_result == ISC_R_SUCCESS && node->down == NULL) { - unsigned int refs; - - rbtdbiter->deletions[rbtdbiter->delete++] = node; - NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); - dns_rbtnode_refincrement(node, &refs); - INSIST(refs != 0); - NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); - } - } - - return (result); + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_rbtnode_t *node = rbtdbiter->node; + isc_result_t result; + dns_name_t *nodename = dns_fixedname_name(&rbtdbiter->name); + dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin); + + REQUIRE(rbtdbiter->result == ISC_R_SUCCESS); + REQUIRE(rbtdbiter->node != NULL); + + if (rbtdbiter->paused) + resume_iteration(rbtdbiter); + + if (name != NULL) { + if (rbtdbiter->common.relative_names) + origin = NULL; + result = dns_name_concatenate(nodename, origin, name, NULL); + if (result != ISC_R_SUCCESS) + return (result); + if (rbtdbiter->common.relative_names && rbtdbiter->new_origin) + result = DNS_R_NEWORIGIN; + } else + result = ISC_R_SUCCESS; + + NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); + new_reference(rbtdb, node); + NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); + + *nodep = rbtdbiter->node; + + if (iterator->cleaning && result == ISC_R_SUCCESS) { + isc_result_t expire_result; + + /* + * If the deletion array is full, flush it before trying + * to expire the current node. The current node can't + * fully deleted while the iteration cursor is still on it. + */ + if (rbtdbiter->delete == DELETION_BATCH_MAX) + flush_deletions(rbtdbiter); + + expire_result = expirenode(iterator->db, *nodep, 0); + + /* + * expirenode() currently always returns success. + */ + if (expire_result == ISC_R_SUCCESS && node->down == NULL) { + unsigned int refs; + + rbtdbiter->deletions[rbtdbiter->delete++] = node; + NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); + dns_rbtnode_refincrement(node, &refs); + INSIST(refs != 0); + NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); + } + } + + return (result); } static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOMORE) - return (rbtdbiter->result); + if (rbtdbiter->result != ISC_R_SUCCESS && + rbtdbiter->result != ISC_R_NOMORE) + return (rbtdbiter->result); - if (rbtdbiter->paused) - return (ISC_R_SUCCESS); + if (rbtdbiter->paused) + return (ISC_R_SUCCESS); - rbtdbiter->paused = ISC_TRUE; + rbtdbiter->paused = ISC_TRUE; - if (rbtdbiter->tree_locked != isc_rwlocktype_none) { - INSIST(rbtdbiter->tree_locked == isc_rwlocktype_read); - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - rbtdbiter->tree_locked = isc_rwlocktype_none; - } + if (rbtdbiter->tree_locked != isc_rwlocktype_none) { + INSIST(rbtdbiter->tree_locked == isc_rwlocktype_read); + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + rbtdbiter->tree_locked = isc_rwlocktype_none; + } - flush_deletions(rbtdbiter); + flush_deletions(rbtdbiter); - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin); + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin); - if (rbtdbiter->result != ISC_R_SUCCESS) - return (rbtdbiter->result); + if (rbtdbiter->result != ISC_R_SUCCESS) + return (rbtdbiter->result); - return (dns_name_copy(origin, name, NULL)); + return (dns_name_copy(origin, name, NULL)); } /*% @@ -6413,350 +7117,513 @@ dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { */ static isc_result_t rdataset_getadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type, - dns_rdatatype_t qtype, dns_acache_t *acache, - dns_zone_t **zonep, dns_db_t **dbp, - dns_dbversion_t **versionp, dns_dbnode_t **nodep, - dns_name_t *fname, dns_message_t *msg, - isc_stdtime_t now) + dns_rdatatype_t qtype, dns_acache_t *acache, + dns_zone_t **zonep, dns_db_t **dbp, + dns_dbversion_t **versionp, dns_dbnode_t **nodep, + dns_name_t *fname, dns_message_t *msg, + isc_stdtime_t now) { - dns_rbtdb_t *rbtdb = rdataset->private1; - dns_rbtnode_t *rbtnode = rdataset->private2; - unsigned char *raw = rdataset->private3; /* RDATASLAB */ - unsigned int current_count = rdataset->privateuint4; - unsigned int count; - rdatasetheader_t *header; - nodelock_t *nodelock; - unsigned int total_count; - acachectl_t *acarray; - dns_acacheentry_t *entry; - isc_result_t result; - - UNUSED(qtype); /* we do not use this value at least for now */ - UNUSED(acache); - - header = (struct rdatasetheader *)(raw - sizeof(*header)); - - total_count = raw[0] * 256 + raw[1]; - INSIST(total_count > current_count); - count = total_count - current_count - 1; - - acarray = NULL; - - nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; - NODE_LOCK(nodelock, isc_rwlocktype_read); - - switch (type) { - case dns_rdatasetadditional_fromauth: - acarray = header->additional_auth; - break; - case dns_rdatasetadditional_fromcache: - acarray = NULL; - break; - case dns_rdatasetadditional_fromglue: - acarray = header->additional_glue; - break; - default: - INSIST(0); - } - - if (acarray == NULL) { - if (type != dns_rdatasetadditional_fromcache) - dns_acache_countquerymiss(acache); - NODE_UNLOCK(nodelock, isc_rwlocktype_read); - return (ISC_R_NOTFOUND); - } - - if (acarray[count].entry == NULL) { - dns_acache_countquerymiss(acache); - NODE_UNLOCK(nodelock, isc_rwlocktype_read); - return (ISC_R_NOTFOUND); - } - - entry = NULL; - dns_acache_attachentry(acarray[count].entry, &entry); - - NODE_UNLOCK(nodelock, isc_rwlocktype_read); - - result = dns_acache_getentry(entry, zonep, dbp, versionp, - nodep, fname, msg, now); - - dns_acache_detachentry(&entry); - - return (result); + dns_rbtdb_t *rbtdb = rdataset->private1; + dns_rbtnode_t *rbtnode = rdataset->private2; + unsigned char *raw = rdataset->private3; /* RDATASLAB */ + unsigned int current_count = rdataset->privateuint4; + unsigned int count; + rdatasetheader_t *header; + nodelock_t *nodelock; + unsigned int total_count; + acachectl_t *acarray; + dns_acacheentry_t *entry; + isc_result_t result; + + UNUSED(qtype); /* we do not use this value at least for now */ + UNUSED(acache); + + header = (struct rdatasetheader *)(raw - sizeof(*header)); + + total_count = raw[0] * 256 + raw[1]; + INSIST(total_count > current_count); + count = total_count - current_count - 1; + + acarray = NULL; + + nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; + NODE_LOCK(nodelock, isc_rwlocktype_read); + + switch (type) { + case dns_rdatasetadditional_fromauth: + acarray = header->additional_auth; + break; + case dns_rdatasetadditional_fromcache: + acarray = NULL; + break; + case dns_rdatasetadditional_fromglue: + acarray = header->additional_glue; + break; + default: + INSIST(0); + } + + if (acarray == NULL) { + if (type != dns_rdatasetadditional_fromcache) + dns_acache_countquerymiss(acache); + NODE_UNLOCK(nodelock, isc_rwlocktype_read); + return (ISC_R_NOTFOUND); + } + + if (acarray[count].entry == NULL) { + dns_acache_countquerymiss(acache); + NODE_UNLOCK(nodelock, isc_rwlocktype_read); + return (ISC_R_NOTFOUND); + } + + entry = NULL; + dns_acache_attachentry(acarray[count].entry, &entry); + + NODE_UNLOCK(nodelock, isc_rwlocktype_read); + + result = dns_acache_getentry(entry, zonep, dbp, versionp, + nodep, fname, msg, now); + + dns_acache_detachentry(&entry); + + return (result); } static void acache_callback(dns_acacheentry_t *entry, void **arg) { - dns_rbtdb_t *rbtdb; - dns_rbtnode_t *rbtnode; - nodelock_t *nodelock; - acachectl_t *acarray = NULL; - acache_cbarg_t *cbarg; - unsigned int count; - - REQUIRE(arg != NULL); - cbarg = *arg; - - /* - * The caller must hold the entry lock. - */ - - rbtdb = (dns_rbtdb_t *)cbarg->db; - rbtnode = (dns_rbtnode_t *)cbarg->node; - - nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; - NODE_LOCK(nodelock, isc_rwlocktype_write); - - switch (cbarg->type) { - case dns_rdatasetadditional_fromauth: - acarray = cbarg->header->additional_auth; - break; - case dns_rdatasetadditional_fromglue: - acarray = cbarg->header->additional_glue; - break; - default: - INSIST(0); - } - - count = cbarg->count; - if (acarray[count].entry == entry) - acarray[count].entry = NULL; - INSIST(acarray[count].cbarg != NULL); - isc_mem_put(rbtdb->common.mctx, acarray[count].cbarg, - sizeof(acache_cbarg_t)); - acarray[count].cbarg = NULL; - - dns_acache_detachentry(&entry); - - NODE_UNLOCK(nodelock, isc_rwlocktype_write); - - dns_db_detachnode((dns_db_t *)rbtdb, (dns_dbnode_t **)(void*)&rbtnode); - dns_db_detach((dns_db_t **)(void*)&rbtdb); - - *arg = NULL; + dns_rbtdb_t *rbtdb; + dns_rbtnode_t *rbtnode; + nodelock_t *nodelock; + acachectl_t *acarray = NULL; + acache_cbarg_t *cbarg; + unsigned int count; + + REQUIRE(arg != NULL); + cbarg = *arg; + + /* + * The caller must hold the entry lock. + */ + + rbtdb = (dns_rbtdb_t *)cbarg->db; + rbtnode = (dns_rbtnode_t *)cbarg->node; + + nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; + NODE_LOCK(nodelock, isc_rwlocktype_write); + + switch (cbarg->type) { + case dns_rdatasetadditional_fromauth: + acarray = cbarg->header->additional_auth; + break; + case dns_rdatasetadditional_fromglue: + acarray = cbarg->header->additional_glue; + break; + default: + INSIST(0); + } + + count = cbarg->count; + if (acarray[count].entry == entry) + acarray[count].entry = NULL; + INSIST(acarray[count].cbarg != NULL); + isc_mem_put(rbtdb->common.mctx, acarray[count].cbarg, + sizeof(acache_cbarg_t)); + acarray[count].cbarg = NULL; + + dns_acache_detachentry(&entry); + + NODE_UNLOCK(nodelock, isc_rwlocktype_write); + + dns_db_detachnode((dns_db_t *)rbtdb, (dns_dbnode_t **)(void*)&rbtnode); + dns_db_detach((dns_db_t **)(void*)&rbtdb); + + *arg = NULL; } static void acache_cancelentry(isc_mem_t *mctx, dns_acacheentry_t *entry, - acache_cbarg_t **cbargp) + acache_cbarg_t **cbargp) { - acache_cbarg_t *cbarg; + acache_cbarg_t *cbarg; - REQUIRE(mctx != NULL); - REQUIRE(entry != NULL); - REQUIRE(cbargp != NULL && *cbargp != NULL); + REQUIRE(mctx != NULL); + REQUIRE(entry != NULL); + REQUIRE(cbargp != NULL && *cbargp != NULL); - cbarg = *cbargp; + cbarg = *cbargp; - dns_acache_cancelentry(entry); - dns_db_detachnode(cbarg->db, &cbarg->node); - dns_db_detach(&cbarg->db); + dns_acache_cancelentry(entry); + dns_db_detachnode(cbarg->db, &cbarg->node); + dns_db_detach(&cbarg->db); - isc_mem_put(mctx, cbarg, sizeof(acache_cbarg_t)); + isc_mem_put(mctx, cbarg, sizeof(acache_cbarg_t)); - *cbargp = NULL; + *cbargp = NULL; } static isc_result_t rdataset_setadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type, - dns_rdatatype_t qtype, dns_acache_t *acache, - dns_zone_t *zone, dns_db_t *db, - dns_dbversion_t *version, dns_dbnode_t *node, - dns_name_t *fname) + dns_rdatatype_t qtype, dns_acache_t *acache, + dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *version, dns_dbnode_t *node, + dns_name_t *fname) { - dns_rbtdb_t *rbtdb = rdataset->private1; - dns_rbtnode_t *rbtnode = rdataset->private2; - unsigned char *raw = rdataset->private3; /* RDATASLAB */ - unsigned int current_count = rdataset->privateuint4; - rdatasetheader_t *header; - unsigned int total_count, count; - nodelock_t *nodelock; - isc_result_t result; - acachectl_t *acarray; - dns_acacheentry_t *newentry, *oldentry = NULL; - acache_cbarg_t *newcbarg, *oldcbarg = NULL; - - UNUSED(qtype); - - if (type == dns_rdatasetadditional_fromcache) - return (ISC_R_SUCCESS); - - header = (struct rdatasetheader *)(raw - sizeof(*header)); - - total_count = raw[0] * 256 + raw[1]; - INSIST(total_count > current_count); - count = total_count - current_count - 1; /* should be private data */ - - newcbarg = isc_mem_get(rbtdb->common.mctx, sizeof(*newcbarg)); - if (newcbarg == NULL) - return (ISC_R_NOMEMORY); - newcbarg->type = type; - newcbarg->count = count; - newcbarg->header = header; - newcbarg->db = NULL; - dns_db_attach((dns_db_t *)rbtdb, &newcbarg->db); - newcbarg->node = NULL; - dns_db_attachnode((dns_db_t *)rbtdb, (dns_dbnode_t *)rbtnode, - &newcbarg->node); - newentry = NULL; - result = dns_acache_createentry(acache, (dns_db_t *)rbtdb, - acache_callback, newcbarg, &newentry); - if (result != ISC_R_SUCCESS) - goto fail; - /* Set cache data in the new entry. */ - result = dns_acache_setentry(acache, newentry, zone, db, - version, node, fname); - if (result != ISC_R_SUCCESS) - goto fail; - - nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; - NODE_LOCK(nodelock, isc_rwlocktype_write); - - acarray = NULL; - switch (type) { - case dns_rdatasetadditional_fromauth: - acarray = header->additional_auth; - break; - case dns_rdatasetadditional_fromglue: - acarray = header->additional_glue; - break; - default: - INSIST(0); - } - - if (acarray == NULL) { - unsigned int i; - - acarray = isc_mem_get(rbtdb->common.mctx, total_count * - sizeof(acachectl_t)); - - if (acarray == NULL) { - NODE_UNLOCK(nodelock, isc_rwlocktype_write); - goto fail; - } - - for (i = 0; i < total_count; i++) { - acarray[i].entry = NULL; - acarray[i].cbarg = NULL; - } - } - switch (type) { - case dns_rdatasetadditional_fromauth: - header->additional_auth = acarray; - break; - case dns_rdatasetadditional_fromglue: - header->additional_glue = acarray; - break; - default: - INSIST(0); - } - - if (acarray[count].entry != NULL) { - /* - * Swap the entry. Delay cleaning-up the old entry since - * it would require a node lock. - */ - oldentry = acarray[count].entry; - INSIST(acarray[count].cbarg != NULL); - oldcbarg = acarray[count].cbarg; - } - acarray[count].entry = newentry; - acarray[count].cbarg = newcbarg; - - NODE_UNLOCK(nodelock, isc_rwlocktype_write); - - if (oldentry != NULL) { - if (oldcbarg != NULL) - acache_cancelentry(rbtdb->common.mctx, oldentry, - &oldcbarg); - dns_acache_detachentry(&oldentry); - } - - return (ISC_R_SUCCESS); + dns_rbtdb_t *rbtdb = rdataset->private1; + dns_rbtnode_t *rbtnode = rdataset->private2; + unsigned char *raw = rdataset->private3; /* RDATASLAB */ + unsigned int current_count = rdataset->privateuint4; + rdatasetheader_t *header; + unsigned int total_count, count; + nodelock_t *nodelock; + isc_result_t result; + acachectl_t *acarray; + dns_acacheentry_t *newentry, *oldentry = NULL; + acache_cbarg_t *newcbarg, *oldcbarg = NULL; + + UNUSED(qtype); + + if (type == dns_rdatasetadditional_fromcache) + return (ISC_R_SUCCESS); + + header = (struct rdatasetheader *)(raw - sizeof(*header)); + + total_count = raw[0] * 256 + raw[1]; + INSIST(total_count > current_count); + count = total_count - current_count - 1; /* should be private data */ + + newcbarg = isc_mem_get(rbtdb->common.mctx, sizeof(*newcbarg)); + if (newcbarg == NULL) + return (ISC_R_NOMEMORY); + newcbarg->type = type; + newcbarg->count = count; + newcbarg->header = header; + newcbarg->db = NULL; + dns_db_attach((dns_db_t *)rbtdb, &newcbarg->db); + newcbarg->node = NULL; + dns_db_attachnode((dns_db_t *)rbtdb, (dns_dbnode_t *)rbtnode, + &newcbarg->node); + newentry = NULL; + result = dns_acache_createentry(acache, (dns_db_t *)rbtdb, + acache_callback, newcbarg, &newentry); + if (result != ISC_R_SUCCESS) + goto fail; + /* Set cache data in the new entry. */ + result = dns_acache_setentry(acache, newentry, zone, db, + version, node, fname); + if (result != ISC_R_SUCCESS) + goto fail; + + nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; + NODE_LOCK(nodelock, isc_rwlocktype_write); + + acarray = NULL; + switch (type) { + case dns_rdatasetadditional_fromauth: + acarray = header->additional_auth; + break; + case dns_rdatasetadditional_fromglue: + acarray = header->additional_glue; + break; + default: + INSIST(0); + } + + if (acarray == NULL) { + unsigned int i; + + acarray = isc_mem_get(rbtdb->common.mctx, total_count * + sizeof(acachectl_t)); + + if (acarray == NULL) { + NODE_UNLOCK(nodelock, isc_rwlocktype_write); + goto fail; + } + + for (i = 0; i < total_count; i++) { + acarray[i].entry = NULL; + acarray[i].cbarg = NULL; + } + } + switch (type) { + case dns_rdatasetadditional_fromauth: + header->additional_auth = acarray; + break; + case dns_rdatasetadditional_fromglue: + header->additional_glue = acarray; + break; + default: + INSIST(0); + } + + if (acarray[count].entry != NULL) { + /* + * Swap the entry. Delay cleaning-up the old entry since + * it would require a node lock. + */ + oldentry = acarray[count].entry; + INSIST(acarray[count].cbarg != NULL); + oldcbarg = acarray[count].cbarg; + } + acarray[count].entry = newentry; + acarray[count].cbarg = newcbarg; + + NODE_UNLOCK(nodelock, isc_rwlocktype_write); + + if (oldentry != NULL) { + if (oldcbarg != NULL) + acache_cancelentry(rbtdb->common.mctx, oldentry, + &oldcbarg); + dns_acache_detachentry(&oldentry); + } + + return (ISC_R_SUCCESS); fail: - if (newcbarg != NULL) { - if (newentry != NULL) { - acache_cancelentry(rbtdb->common.mctx, newentry, - &newcbarg); - dns_acache_detachentry(&newentry); - } else { - dns_db_detachnode((dns_db_t *)rbtdb, &newcbarg->node); - dns_db_detach(&newcbarg->db); - isc_mem_put(rbtdb->common.mctx, newcbarg, - sizeof(*newcbarg)); - } - } - - return (result); + if (newcbarg != NULL) { + if (newentry != NULL) { + acache_cancelentry(rbtdb->common.mctx, newentry, + &newcbarg); + dns_acache_detachentry(&newentry); + } else { + dns_db_detachnode((dns_db_t *)rbtdb, &newcbarg->node); + dns_db_detach(&newcbarg->db); + isc_mem_put(rbtdb->common.mctx, newcbarg, + sizeof(*newcbarg)); + } + } + + return (result); } static isc_result_t rdataset_putadditional(dns_acache_t *acache, dns_rdataset_t *rdataset, - dns_rdatasetadditional_t type, dns_rdatatype_t qtype) -{ - dns_rbtdb_t *rbtdb = rdataset->private1; - dns_rbtnode_t *rbtnode = rdataset->private2; - unsigned char *raw = rdataset->private3; /* RDATASLAB */ - unsigned int current_count = rdataset->privateuint4; - rdatasetheader_t *header; - nodelock_t *nodelock; - unsigned int total_count, count; - acachectl_t *acarray; - dns_acacheentry_t *entry; - acache_cbarg_t *cbarg; - - UNUSED(qtype); /* we do not use this value at least for now */ - UNUSED(acache); - - if (type == dns_rdatasetadditional_fromcache) - return (ISC_R_SUCCESS); - - header = (struct rdatasetheader *)(raw - sizeof(*header)); - - total_count = raw[0] * 256 + raw[1]; - INSIST(total_count > current_count); - count = total_count - current_count - 1; - - acarray = NULL; - entry = NULL; - - nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; - NODE_LOCK(nodelock, isc_rwlocktype_write); - - switch (type) { - case dns_rdatasetadditional_fromauth: - acarray = header->additional_auth; - break; - case dns_rdatasetadditional_fromglue: - acarray = header->additional_glue; - break; - default: - INSIST(0); - } - - if (acarray == NULL) { - NODE_UNLOCK(nodelock, isc_rwlocktype_write); - return (ISC_R_NOTFOUND); - } - - entry = acarray[count].entry; - if (entry == NULL) { - NODE_UNLOCK(nodelock, isc_rwlocktype_write); - return (ISC_R_NOTFOUND); - } - - acarray[count].entry = NULL; - cbarg = acarray[count].cbarg; - acarray[count].cbarg = NULL; - - NODE_UNLOCK(nodelock, isc_rwlocktype_write); - - if (entry != NULL) { - if (cbarg != NULL) - acache_cancelentry(rbtdb->common.mctx, entry, &cbarg); - dns_acache_detachentry(&entry); - } - - return (ISC_R_SUCCESS); + dns_rdatasetadditional_t type, dns_rdatatype_t qtype) +{ + dns_rbtdb_t *rbtdb = rdataset->private1; + dns_rbtnode_t *rbtnode = rdataset->private2; + unsigned char *raw = rdataset->private3; /* RDATASLAB */ + unsigned int current_count = rdataset->privateuint4; + rdatasetheader_t *header; + nodelock_t *nodelock; + unsigned int total_count, count; + acachectl_t *acarray; + dns_acacheentry_t *entry; + acache_cbarg_t *cbarg; + + UNUSED(qtype); /* we do not use this value at least for now */ + UNUSED(acache); + + if (type == dns_rdatasetadditional_fromcache) + return (ISC_R_SUCCESS); + + header = (struct rdatasetheader *)(raw - sizeof(*header)); + + total_count = raw[0] * 256 + raw[1]; + INSIST(total_count > current_count); + count = total_count - current_count - 1; + + acarray = NULL; + entry = NULL; + + nodelock = &rbtdb->node_locks[rbtnode->locknum].lock; + NODE_LOCK(nodelock, isc_rwlocktype_write); + + switch (type) { + case dns_rdatasetadditional_fromauth: + acarray = header->additional_auth; + break; + case dns_rdatasetadditional_fromglue: + acarray = header->additional_glue; + break; + default: + INSIST(0); + } + + if (acarray == NULL) { + NODE_UNLOCK(nodelock, isc_rwlocktype_write); + return (ISC_R_NOTFOUND); + } + + entry = acarray[count].entry; + if (entry == NULL) { + NODE_UNLOCK(nodelock, isc_rwlocktype_write); + return (ISC_R_NOTFOUND); + } + + acarray[count].entry = NULL; + cbarg = acarray[count].cbarg; + acarray[count].cbarg = NULL; + + NODE_UNLOCK(nodelock, isc_rwlocktype_write); + + if (entry != NULL) { + if (cbarg != NULL) + acache_cancelentry(rbtdb->common.mctx, entry, &cbarg); + dns_acache_detachentry(&entry); + } + + return (ISC_R_SUCCESS); +} + +/*% + * Routines for LRU-based cache management. + */ + +/*% + * See if a given cache entry that is being reused needs to be updated + * in the LRU-list. For the non-threaded case this is always true unless the + * entry has already been marked as stale; for the threaded case, updating + * the entry every time it is referenced might be expensive because it requires + * a node write lock. Thus this function returns true if the entry has not been + * updated for some period of time. We differentiate the NS or glue address + * case and the others since experiments have shown that the former tends to be + * accessed relatively infrequently and the cost of cache miss is higher + * (e.g., a missing NS records may cause external queries at a higher level + * zone, involving more transactions). + * + * Caller must hold the node (read or write) lock. + */ +static inline isc_boolean_t +need_headerupdate(rdatasetheader_t *header, isc_stdtime_t now) { + if ((header->attributes & + (RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0) + return (ISC_FALSE); + +#ifdef ISC_PLATFORM_USETHREADS + if (header->type == dns_rdatatype_ns || + (header->trust == dns_trust_glue && + (header->type == dns_rdatatype_a || + header->type == dns_rdatatype_aaaa))) { + /* + * Glue records are updated if at least 60 seconds have passed + * since the previous update time. + */ + return (header->last_used + 60 <= now); + } + + /* Other records are updated if 5 minutes have passed. */ + return (header->last_used + 300 <= now); +#else + UNUSED(now); + + return (ISC_TRUE); +#endif +} + +/*% + * Update the timestamp of a given cache entry and move it to the head + * of the corresponding LRU list. + * + * Caller must hold the node (write) lock. + * + * Note that the we do NOT touch the heap here, as the TTL has not changed. + */ +static void +update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, + isc_stdtime_t now) +{ + /* To be checked: can we really assume this? XXXMLG */ + INSIST(ISC_LINK_LINKED(header, lru_link)); + + ISC_LIST_UNLINK(rbtdb->rdatasets[header->node->locknum], + header, lru_link); + header->last_used = now; + ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum], + header, lru_link); +} + +/*% + * Examine the tail entry of the LRU list to see if it expires or is stale + * (unused for some period). If so, it's marked as stale and possibly freed. + * If the DB is in the overmem condition, the tail and the next to tail entries + * will be unconditionally marked. We don't care about a race on 'overmem' + * at the risk of causing some collateral damage or a small delay in starting + * cleanup, so we don't bother to lock rbtdb. + * + * Caller must hold the node (write) lock. + * + * We can get away with locking only one node here, since it will lock all + * other nodes in that lock pool bucket. + */ +static void +check_stale_cache(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, + isc_stdtime_t now, isc_boolean_t tree_locked) +{ + rdatasetheader_t *victim; + isc_boolean_t overmem = rbtdb->overmem; + int scans = 0; /* for debug */ + int victims = 0; + + /* + * Check for TTL-based expiry. + */ + victim = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1); + if (victim != NULL && victim->rdh_ttl <= now - RBTDB_VIRTUAL) { + INSIST(victim->node->locknum == rbtnode->locknum); + +#ifdef LRU_DEBUG + /* for debug */ + rbtdb->cachestat.stale_expire++; +#endif + victims++; + + set_ttl(rbtdb, victim, 0); + victim->attributes |= RDATASET_ATTR_STALE; + victim->node->dirty = 1; + + if (dns_rbtnode_refcurrent(victim->node) == 0) { + INSIST(rbtnode != victim->node); + /* + * If no one else is using the node, we can + * clean it up now. We first need to gain + * a new reference to the node to meet a + * requirement of decrement_reference(). + */ + new_reference(rbtdb, victim->node); + decrement_reference(rbtdb, victim->node, 0, + isc_rwlocktype_write, + tree_locked ? isc_rwlocktype_write : + isc_rwlocktype_none); + } + } + + /* + * If we are over memory, delete the end entry from the LRU. + */ + victim = ISC_LIST_TAIL(rbtdb->rdatasets[rbtnode->locknum]); + if (victim != NULL && overmem) { + INSIST(victim->node->locknum == rbtnode->locknum); + +#ifdef LRU_DEBUG + /* for debug */ + rbtdb->cachestat.stale_lru++; +#endif + victims++; + scans++; + + set_ttl(rbtdb, victim, 0); + victim->attributes |= RDATASET_ATTR_STALE; + victim->node->dirty = 1; + + if (dns_rbtnode_refcurrent(victim->node) == 0) { + INSIST(rbtnode != victim->node); + /* + * If no one else is using the node, we can + * clean it up now. We first need to gain + * a new reference to the node to meet a + * requirement of decrement_reference(). + */ + new_reference(rbtdb, victim->node); + decrement_reference(rbtdb, victim->node, 0, + isc_rwlocktype_write, + tree_locked ? isc_rwlocktype_write : + isc_rwlocktype_none); + } + } + +#ifdef LRU_DEBUG + /* update statistics for debug (no lock for brevity) */ + rbtdb->cachestat.stale_scan += scans; + rbtdb->cachestat.stale_purge += victims; +#endif } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 330b9f78..8c6a0ea4 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.349 2007/06/18 23:47:41 tbox Exp $ */ +/* $Id: resolver.c,v 1.353 2007/10/19 17:15:53 explorer Exp $ */ /*! \file */ @@ -56,44 +56,46 @@ #include <dns/tsig.h> #include <dns/validator.h> +#define inline /* XXXMPA remove for 9.4.2 */ + #define DNS_RESOLVER_TRACE #ifdef DNS_RESOLVER_TRACE -#define RTRACE(m) isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_RESOLVER, \ - DNS_LOGMODULE_RESOLVER, \ - ISC_LOG_DEBUG(3), \ - "res %p: %s", res, (m)) -#define RRTRACE(r, m) isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_RESOLVER, \ - DNS_LOGMODULE_RESOLVER, \ - ISC_LOG_DEBUG(3), \ - "res %p: %s", (r), (m)) -#define FCTXTRACE(m) isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_RESOLVER, \ - DNS_LOGMODULE_RESOLVER, \ - ISC_LOG_DEBUG(3), \ - "fctx %p(%s'): %s", fctx, fctx->info, (m)) +#define RTRACE(m) isc_log_write(dns_lctx, \ + DNS_LOGCATEGORY_RESOLVER, \ + DNS_LOGMODULE_RESOLVER, \ + ISC_LOG_DEBUG(3), \ + "res %p: %s", res, (m)) +#define RRTRACE(r, m) isc_log_write(dns_lctx, \ + DNS_LOGCATEGORY_RESOLVER, \ + DNS_LOGMODULE_RESOLVER, \ + ISC_LOG_DEBUG(3), \ + "res %p: %s", (r), (m)) +#define FCTXTRACE(m) isc_log_write(dns_lctx, \ + DNS_LOGCATEGORY_RESOLVER, \ + DNS_LOGMODULE_RESOLVER, \ + ISC_LOG_DEBUG(3), \ + "fctx %p(%s'): %s", fctx, fctx->info, (m)) #define FCTXTRACE2(m1, m2) \ - isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_RESOLVER, \ - DNS_LOGMODULE_RESOLVER, \ - ISC_LOG_DEBUG(3), \ - "fctx %p(%s): %s %s", \ - fctx, fctx->info, (m1), (m2)) -#define FTRACE(m) isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_RESOLVER, \ - DNS_LOGMODULE_RESOLVER, \ - ISC_LOG_DEBUG(3), \ - "fetch %p (fctx %p(%s)): %s", \ - fetch, fetch->private, \ - fetch->private->info, (m)) -#define QTRACE(m) isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_RESOLVER, \ - DNS_LOGMODULE_RESOLVER, \ - ISC_LOG_DEBUG(3), \ - "resquery %p (fctx %p(%s)): %s", \ - query, query->fctx, \ - query->fctx->info, (m)) + isc_log_write(dns_lctx, \ + DNS_LOGCATEGORY_RESOLVER, \ + DNS_LOGMODULE_RESOLVER, \ + ISC_LOG_DEBUG(3), \ + "fctx %p(%s): %s %s", \ + fctx, fctx->info, (m1), (m2)) +#define FTRACE(m) isc_log_write(dns_lctx, \ + DNS_LOGCATEGORY_RESOLVER, \ + DNS_LOGMODULE_RESOLVER, \ + ISC_LOG_DEBUG(3), \ + "fetch %p (fctx %p(%s)): %s", \ + fetch, fetch->private, \ + fetch->private->info, (m)) +#define QTRACE(m) isc_log_write(dns_lctx, \ + DNS_LOGCATEGORY_RESOLVER, \ + DNS_LOGMODULE_RESOLVER, \ + ISC_LOG_DEBUG(3), \ + "resquery %p (fctx %p(%s)): %s", \ + query, query->fctx, \ + query->fctx->info, (m)) #else #define RTRACE(m) #define RRTRACE(r, m) @@ -105,190 +107,191 @@ /*% * Maximum EDNS0 input packet size. */ -#define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. */ +#define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. */ /*% * This defines the maximum number of timeouts we will permit before we * disable EDNS0 on the query. */ -#define MAX_EDNS0_TIMEOUTS 3 +#define MAX_EDNS0_TIMEOUTS 3 typedef struct fetchctx fetchctx_t; typedef struct query { - /* Locked by task event serialization. */ - unsigned int magic; - fetchctx_t * fctx; - isc_mem_t * mctx; - dns_dispatchmgr_t * dispatchmgr; - dns_dispatch_t * dispatch; - dns_adbaddrinfo_t * addrinfo; - isc_socket_t * tcpsocket; - isc_time_t start; - dns_messageid_t id; - dns_dispentry_t * dispentry; - ISC_LINK(struct query) link; - isc_buffer_t buffer; - isc_buffer_t *tsig; - dns_tsigkey_t *tsigkey; - unsigned int options; - unsigned int attributes; - unsigned int sends; - unsigned int connects; - unsigned char data[512]; + /* Locked by task event serialization. */ + unsigned int magic; + fetchctx_t * fctx; + isc_mem_t * mctx; + dns_dispatchmgr_t * dispatchmgr; + dns_dispatch_t * dispatch; + dns_adbaddrinfo_t * addrinfo; + isc_socket_t * tcpsocket; + isc_time_t start; + dns_messageid_t id; + dns_dispentry_t * dispentry; + ISC_LINK(struct query) link; + isc_buffer_t buffer; + isc_buffer_t *tsig; + dns_tsigkey_t *tsigkey; + unsigned int options; + unsigned int attributes; + unsigned int sends; + unsigned int connects; + unsigned char data[512]; } resquery_t; -#define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!') -#define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC) +#define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!') +#define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC) -#define RESQUERY_ATTR_CANCELED 0x02 +#define RESQUERY_ATTR_CANCELED 0x02 -#define RESQUERY_CONNECTING(q) ((q)->connects > 0) -#define RESQUERY_CANCELED(q) (((q)->attributes & \ - RESQUERY_ATTR_CANCELED) != 0) -#define RESQUERY_SENDING(q) ((q)->sends > 0) +#define RESQUERY_CONNECTING(q) ((q)->connects > 0) +#define RESQUERY_CANCELED(q) (((q)->attributes & \ + RESQUERY_ATTR_CANCELED) != 0) +#define RESQUERY_SENDING(q) ((q)->sends > 0) typedef enum { - fetchstate_init = 0, /*%< Start event has not run yet. */ - fetchstate_active, - fetchstate_done /*%< FETCHDONE events posted. */ + fetchstate_init = 0, /*%< Start event has not run yet. */ + fetchstate_active, + fetchstate_done /*%< FETCHDONE events posted. */ } fetchstate; struct fetchctx { - /*% Not locked. */ - unsigned int magic; - dns_resolver_t * res; - dns_name_t name; - dns_rdatatype_t type; - unsigned int options; - unsigned int bucketnum; - char * info; - /*% Locked by appropriate bucket lock. */ - fetchstate state; - isc_boolean_t want_shutdown; - isc_boolean_t cloned; - isc_boolean_t spilled; - unsigned int references; - isc_event_t control_event; - ISC_LINK(struct fetchctx) link; - ISC_LIST(dns_fetchevent_t) events; - /*% Locked by task event serialization. */ - dns_name_t domain; - dns_rdataset_t nameservers; - unsigned int attributes; - isc_timer_t * timer; - isc_time_t expires; - isc_interval_t interval; - dns_message_t * qmessage; - dns_message_t * rmessage; - ISC_LIST(resquery_t) queries; - dns_adbfindlist_t finds; - dns_adbfind_t * find; - dns_adbfindlist_t altfinds; - dns_adbfind_t * altfind; - dns_adbaddrinfolist_t forwaddrs; - dns_adbaddrinfolist_t altaddrs; - isc_sockaddrlist_t forwarders; - dns_fwdpolicy_t fwdpolicy; - isc_sockaddrlist_t bad; - isc_sockaddrlist_t edns; - isc_sockaddrlist_t edns512; - ISC_LIST(dns_validator_t) validators; - dns_db_t * cache; - dns_adb_t * adb; - - /*% - * The number of events we're waiting for. - */ - unsigned int pending; - - /*% - * The number of times we've "restarted" the current - * nameserver set. This acts as a failsafe to prevent - * us from pounding constantly on a particular set of - * servers that, for whatever reason, are not giving - * us useful responses, but are responding in such a - * way that they are not marked "bad". - */ - unsigned int restarts; - - /*% - * The number of timeouts that have occurred since we - * last successfully received a response packet. This - * is used for EDNS0 black hole detection. - */ - unsigned int timeouts; - /*% - * Look aside state for DS lookups. - */ - dns_name_t nsname; - dns_fetch_t * nsfetch; - dns_rdataset_t nsrrset; - - /*% - * Number of queries that reference this context. - */ - unsigned int nqueries; + /*% Not locked. */ + unsigned int magic; + dns_resolver_t * res; + dns_name_t name; + dns_rdatatype_t type; + unsigned int options; + unsigned int bucketnum; + char * info; + /*% Locked by appropriate bucket lock. */ + fetchstate state; + isc_boolean_t want_shutdown; + isc_boolean_t cloned; + isc_boolean_t spilled; + unsigned int references; + isc_event_t control_event; + ISC_LINK(struct fetchctx) link; + ISC_LIST(dns_fetchevent_t) events; + /*% Locked by task event serialization. */ + dns_name_t domain; + dns_rdataset_t nameservers; + unsigned int attributes; + isc_timer_t * timer; + isc_time_t expires; + isc_interval_t interval; + dns_message_t * qmessage; + dns_message_t * rmessage; + ISC_LIST(resquery_t) queries; + dns_adbfindlist_t finds; + dns_adbfind_t * find; + dns_adbfindlist_t altfinds; + dns_adbfind_t * altfind; + dns_adbaddrinfolist_t forwaddrs; + dns_adbaddrinfolist_t altaddrs; + isc_sockaddrlist_t forwarders; + dns_fwdpolicy_t fwdpolicy; + isc_sockaddrlist_t bad; + isc_sockaddrlist_t edns; + isc_sockaddrlist_t edns512; + dns_validator_t *validator; + ISC_LIST(dns_validator_t) validators; + dns_db_t * cache; + dns_adb_t * adb; + + /*% + * The number of events we're waiting for. + */ + unsigned int pending; + + /*% + * The number of times we've "restarted" the current + * nameserver set. This acts as a failsafe to prevent + * us from pounding constantly on a particular set of + * servers that, for whatever reason, are not giving + * us useful responses, but are responding in such a + * way that they are not marked "bad". + */ + unsigned int restarts; + + /*% + * The number of timeouts that have occurred since we + * last successfully received a response packet. This + * is used for EDNS0 black hole detection. + */ + unsigned int timeouts; + /*% + * Look aside state for DS lookups. + */ + dns_name_t nsname; + dns_fetch_t * nsfetch; + dns_rdataset_t nsrrset; + + /*% + * Number of queries that reference this context. + */ + unsigned int nqueries; }; -#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!') -#define VALID_FCTX(fctx) ISC_MAGIC_VALID(fctx, FCTX_MAGIC) - -#define FCTX_ATTR_HAVEANSWER 0x0001 -#define FCTX_ATTR_GLUING 0x0002 -#define FCTX_ATTR_ADDRWAIT 0x0004 -#define FCTX_ATTR_SHUTTINGDOWN 0x0008 -#define FCTX_ATTR_WANTCACHE 0x0010 -#define FCTX_ATTR_WANTNCACHE 0x0020 -#define FCTX_ATTR_NEEDEDNS0 0x0040 -#define FCTX_ATTR_TRIEDFIND 0x0080 -#define FCTX_ATTR_TRIEDALT 0x0100 - -#define HAVE_ANSWER(f) (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \ - 0) -#define GLUING(f) (((f)->attributes & FCTX_ATTR_GLUING) != \ - 0) -#define ADDRWAIT(f) (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \ - 0) -#define SHUTTINGDOWN(f) (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \ - != 0) -#define WANTCACHE(f) (((f)->attributes & FCTX_ATTR_WANTCACHE) != 0) -#define WANTNCACHE(f) (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0) -#define NEEDEDNS0(f) (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0) -#define TRIEDFIND(f) (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0) -#define TRIEDALT(f) (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0) +#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!') +#define VALID_FCTX(fctx) ISC_MAGIC_VALID(fctx, FCTX_MAGIC) + +#define FCTX_ATTR_HAVEANSWER 0x0001 +#define FCTX_ATTR_GLUING 0x0002 +#define FCTX_ATTR_ADDRWAIT 0x0004 +#define FCTX_ATTR_SHUTTINGDOWN 0x0008 +#define FCTX_ATTR_WANTCACHE 0x0010 +#define FCTX_ATTR_WANTNCACHE 0x0020 +#define FCTX_ATTR_NEEDEDNS0 0x0040 +#define FCTX_ATTR_TRIEDFIND 0x0080 +#define FCTX_ATTR_TRIEDALT 0x0100 + +#define HAVE_ANSWER(f) (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \ + 0) +#define GLUING(f) (((f)->attributes & FCTX_ATTR_GLUING) != \ + 0) +#define ADDRWAIT(f) (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \ + 0) +#define SHUTTINGDOWN(f) (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \ + != 0) +#define WANTCACHE(f) (((f)->attributes & FCTX_ATTR_WANTCACHE) != 0) +#define WANTNCACHE(f) (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0) +#define NEEDEDNS0(f) (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0) +#define TRIEDFIND(f) (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0) +#define TRIEDALT(f) (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0) typedef struct { - dns_adbaddrinfo_t * addrinfo; - fetchctx_t * fctx; + dns_adbaddrinfo_t * addrinfo; + fetchctx_t * fctx; } dns_valarg_t; struct dns_fetch { - unsigned int magic; - fetchctx_t * private; + unsigned int magic; + fetchctx_t * private; }; -#define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h') -#define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC) +#define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h') +#define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC) typedef struct fctxbucket { - isc_task_t * task; - isc_mutex_t lock; - ISC_LIST(fetchctx_t) fctxs; - isc_boolean_t exiting; - isc_mem_t * mctx; + isc_task_t * task; + isc_mutex_t lock; + ISC_LIST(fetchctx_t) fctxs; + isc_boolean_t exiting; + isc_mem_t * mctx; } fctxbucket_t; typedef struct alternate { - isc_boolean_t isaddress; - union { - isc_sockaddr_t addr; - struct { - dns_name_t name; - in_port_t port; - } _n; - } _u; - ISC_LINK(struct alternate) link; + isc_boolean_t isaddress; + union { + isc_sockaddr_t addr; + struct { + dns_name_t name; + in_port_t port; + } _n; + } _u; + ISC_LINK(struct alternate) link; } alternate_t; #ifdef ISC_RWLOCK_USEATOMIC @@ -298,88 +301,102 @@ typedef struct alternate { #endif #if DNS_RESOLVER_USERWLOCK -#define RES_INITLOCK(l) isc_rwlock_init((l), 0, 0) -#define RES_DESTROYLOCK(l) isc_rwlock_destroy(l) -#define RES_LOCK(l, t) RWLOCK((l), (t)) -#define RES_UNLOCK(l, t) RWUNLOCK((l), (t)) +#define RES_INITLOCK(l) isc_rwlock_init((l), 0, 0) +#define RES_DESTROYLOCK(l) isc_rwlock_destroy(l) +#define RES_LOCK(l, t) RWLOCK((l), (t)) +#define RES_UNLOCK(l, t) RWUNLOCK((l), (t)) #else -#define RES_INITLOCK(l) isc_mutex_init(l) -#define RES_DESTROYLOCK(l) DESTROYLOCK(l) -#define RES_LOCK(l, t) LOCK(l) -#define RES_UNLOCK(l, t) UNLOCK(l) +#define RES_INITLOCK(l) isc_mutex_init(l) +#define RES_DESTROYLOCK(l) DESTROYLOCK(l) +#define RES_LOCK(l, t) LOCK(l) +#define RES_UNLOCK(l, t) UNLOCK(l) #endif struct dns_resolver { - /* Unlocked. */ - unsigned int magic; - isc_mem_t * mctx; - isc_mutex_t lock; - isc_mutex_t nlock; - isc_mutex_t primelock; + /* Unlocked. */ + unsigned int magic; + isc_mem_t * mctx; + isc_mutex_t lock; + isc_mutex_t nlock; + isc_mutex_t primelock; #if DNS_RESOLVER_USERWLOCK - isc_rwlock_t poollock; + isc_rwlock_t poollock; #else - isc_mutex_t poollock; + isc_mutex_t poollock; #endif - dns_rdataclass_t rdclass; - isc_socketmgr_t * socketmgr; - isc_timermgr_t * timermgr; - isc_taskmgr_t * taskmgr; - dns_view_t * view; - isc_boolean_t frozen; - unsigned int options; - dns_dispatchmgr_t * dispatchmgr; - dns_dispatch_t * dispatchv4; - dns_dispatch_t * dispatchv6; - unsigned int ndisps; - unsigned int nbuckets; - fctxbucket_t * buckets; - isc_uint32_t lame_ttl; - ISC_LIST(alternate_t) alternates; - isc_uint16_t udpsize; + dns_rdataclass_t rdclass; + isc_socketmgr_t * socketmgr; + isc_timermgr_t * timermgr; + isc_taskmgr_t * taskmgr; + dns_view_t * view; + isc_boolean_t frozen; + unsigned int options; + dns_dispatchmgr_t * dispatchmgr; + dns_dispatch_t * dispatchv4; + dns_dispatch_t * dispatchv6; + unsigned int ndisps; + unsigned int nbuckets; + fctxbucket_t * buckets; + isc_uint32_t lame_ttl; + ISC_LIST(alternate_t) alternates; + isc_uint16_t udpsize; #if USE_ALGLOCK - isc_rwlock_t alglock; + isc_rwlock_t alglock; #endif - dns_rbt_t * algorithms; + dns_rbt_t * algorithms; #if USE_MBSLOCK - isc_rwlock_t mbslock; + isc_rwlock_t mbslock; +#endif + dns_rbt_t * mustbesecure; + unsigned int spillatmax; + unsigned int spillatmin; + isc_timer_t * spillattimer; + isc_boolean_t zero_no_soa_ttl; + isc_timer_t * disppooltimer; +#ifdef LRU_DEBUG +#define DUMP_INTERVAL 30 /* seconds */ + isc_timer_t * dumptimer; + isc_time_t dump_time; +#endif + /* Locked by lock. */ + unsigned int references; + isc_boolean_t exiting; + isc_eventlist_t whenshutdown; + unsigned int activebuckets; + isc_boolean_t priming; + unsigned int spillat; + unsigned int nextdisp; + /* Locked by primelock. */ + dns_fetch_t * primefetch; + /* Locked by nlock. */ + unsigned int nfctx; + /* Locked by poollock. */ + dns_dispatch_t ** dispatchv4pool; + dns_dispatch_t ** dispatchv6pool; + +#ifdef LRU_DEBUG + /* Unlocked: just for debug */ + unsigned int extqueries; + unsigned int extqueries_ns; + unsigned int extqueries_soa; + unsigned int extqueries_a; + unsigned int extqueries_aaaa; #endif - dns_rbt_t * mustbesecure; - unsigned int spillatmax; - unsigned int spillatmin; - isc_timer_t * spillattimer; - isc_boolean_t zero_no_soa_ttl; - isc_timer_t * disppooltimer; - /* Locked by lock. */ - unsigned int references; - isc_boolean_t exiting; - isc_eventlist_t whenshutdown; - unsigned int activebuckets; - isc_boolean_t priming; - unsigned int spillat; - unsigned int nextdisp; - /* Locked by primelock. */ - dns_fetch_t * primefetch; - /* Locked by nlock. */ - unsigned int nfctx; - /* Locked by poollock. */ - dns_dispatch_t ** dispatchv4pool; - dns_dispatch_t ** dispatchv6pool; }; -#define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!') -#define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC) +#define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!') +#define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC) /*% * Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0, * which we also use as an addrinfo flag. */ -#define FCTX_ADDRINFO_MARK 0x0001 -#define FCTX_ADDRINFO_FORWARDER 0x1000 -#define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \ - == 0) -#define ISFORWARDER(a) (((a)->flags & \ - FCTX_ADDRINFO_FORWARDER) != 0) +#define FCTX_ADDRINFO_MARK 0x0001 +#define FCTX_ADDRINFO_FORWARDER 0x1000 +#define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \ + == 0) +#define ISFORWARDER(a) (((a)->flags & \ + FCTX_ADDRINFO_FORWARDER) != 0) #define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) @@ -391,2320 +408,2347 @@ static void resquery_connected(isc_task_t *task, isc_event_t *event); static void fctx_try(fetchctx_t *fctx); static isc_boolean_t fctx_destroy(fetchctx_t *fctx); static isc_result_t ncache_adderesult(dns_message_t *message, - dns_db_t *cache, dns_dbnode_t *node, - dns_rdatatype_t covers, - isc_stdtime_t now, dns_ttl_t maxttl, - dns_rdataset_t *ardataset, - isc_result_t *eresultp); -static void validated(isc_task_t *task, isc_event_t *event); + dns_db_t *cache, dns_dbnode_t *node, + dns_rdatatype_t covers, + isc_stdtime_t now, dns_ttl_t maxttl, + dns_rdataset_t *ardataset, + isc_result_t *eresultp); +static void validated(isc_task_t *task, isc_event_t *event); static void maybe_destroy(fetchctx_t *fctx); +#ifdef LRU_DEBUG +static void timer_dump(isc_task_t *task, isc_event_t *ev); +#endif + static isc_result_t valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name, - dns_rdatatype_t type, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset, unsigned int valoptions, - isc_task_t *task) + dns_rdatatype_t type, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, unsigned int valoptions, + isc_task_t *task) { - dns_validator_t *validator = NULL; - dns_valarg_t *valarg; - isc_result_t result; - - valarg = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, - sizeof(*valarg)); - if (valarg == NULL) - return (ISC_R_NOMEMORY); - - valarg->fctx = fctx; - valarg->addrinfo = addrinfo; - - if (!ISC_LIST_EMPTY(fctx->validators)) - INSIST((valoptions & DNS_VALIDATOR_DEFER) != 0); - - result = dns_validator_create(fctx->res->view, name, type, rdataset, - sigrdataset, fctx->rmessage, - valoptions, task, validated, valarg, - &validator); - if (result == ISC_R_SUCCESS) - ISC_LIST_APPEND(fctx->validators, validator, link); - else - isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx, - valarg, sizeof(*valarg)); - return (result); + dns_validator_t *validator = NULL; + dns_valarg_t *valarg; + isc_result_t result; + + valarg = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, + sizeof(*valarg)); + if (valarg == NULL) + return (ISC_R_NOMEMORY); + + valarg->fctx = fctx; + valarg->addrinfo = addrinfo; + + if (!ISC_LIST_EMPTY(fctx->validators)) + INSIST((valoptions & DNS_VALIDATOR_DEFER) != 0); + + result = dns_validator_create(fctx->res->view, name, type, rdataset, + sigrdataset, fctx->rmessage, + valoptions, task, validated, valarg, + &validator); + if (result == ISC_R_SUCCESS) { + if ((valoptions & DNS_VALIDATOR_DEFER) == 0) { + INSIST(fctx->validator == NULL); + fctx->validator = validator; + } + ISC_LIST_APPEND(fctx->validators, validator, link); + } else + isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx, + valarg, sizeof(*valarg)); + return (result); } static isc_boolean_t fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) { - dns_name_t *name; - dns_name_t *domain = &fctx->domain; - dns_rdataset_t *rdataset; - dns_rdatatype_t type; - isc_result_t result; - isc_boolean_t keep_auth = ISC_FALSE; - - if (message->rcode == dns_rcode_nxdomain) - return (ISC_FALSE); - - /* - * Look for BIND 8 style delegations. - * Also look for answers to ANY queries where the duplicate NS RRset - * may have been stripped from the authority section. - */ - if (message->counts[DNS_SECTION_ANSWER] != 0 && - (fctx->type == dns_rdatatype_ns || - fctx->type == dns_rdatatype_any)) { - result = dns_message_firstname(message, DNS_SECTION_ANSWER); - while (result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(message, DNS_SECTION_ANSWER, - &name); - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - type = rdataset->type; - if (type != dns_rdatatype_ns) - continue; - if (dns_name_issubdomain(name, domain)) - return (ISC_FALSE); - } - result = dns_message_nextname(message, - DNS_SECTION_ANSWER); - } - } - - /* Look for referral. */ - if (message->counts[DNS_SECTION_AUTHORITY] == 0) - goto munge; - - result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); - while (result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - type = rdataset->type; - if (type == dns_rdatatype_soa && - dns_name_equal(name, domain)) - keep_auth = ISC_TRUE; - if (type != dns_rdatatype_ns && - type != dns_rdatatype_soa) - continue; - if (dns_name_equal(name, domain)) - goto munge; - if (dns_name_issubdomain(name, domain)) - return (ISC_FALSE); - } - result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); - } + dns_name_t *name; + dns_name_t *domain = &fctx->domain; + dns_rdataset_t *rdataset; + dns_rdatatype_t type; + isc_result_t result; + isc_boolean_t keep_auth = ISC_FALSE; + + if (message->rcode == dns_rcode_nxdomain) + return (ISC_FALSE); + + /* + * Look for BIND 8 style delegations. + * Also look for answers to ANY queries where the duplicate NS RRset + * may have been stripped from the authority section. + */ + if (message->counts[DNS_SECTION_ANSWER] != 0 && + (fctx->type == dns_rdatatype_ns || + fctx->type == dns_rdatatype_any)) { + result = dns_message_firstname(message, DNS_SECTION_ANSWER); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, DNS_SECTION_ANSWER, + &name); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + type = rdataset->type; + if (type != dns_rdatatype_ns) + continue; + if (dns_name_issubdomain(name, domain)) + return (ISC_FALSE); + } + result = dns_message_nextname(message, + DNS_SECTION_ANSWER); + } + } + + /* Look for referral. */ + if (message->counts[DNS_SECTION_AUTHORITY] == 0) + goto munge; + + result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + type = rdataset->type; + if (type == dns_rdatatype_soa && + dns_name_equal(name, domain)) + keep_auth = ISC_TRUE; + if (type != dns_rdatatype_ns && + type != dns_rdatatype_soa) + continue; + if (dns_name_equal(name, domain)) + goto munge; + if (dns_name_issubdomain(name, domain)) + return (ISC_FALSE); + } + result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); + } munge: - message->rcode = dns_rcode_nxdomain; - message->counts[DNS_SECTION_ANSWER] = 0; - if (!keep_auth) - message->counts[DNS_SECTION_AUTHORITY] = 0; - message->counts[DNS_SECTION_ADDITIONAL] = 0; - return (ISC_TRUE); + message->rcode = dns_rcode_nxdomain; + message->counts[DNS_SECTION_ANSWER] = 0; + if (!keep_auth) + message->counts[DNS_SECTION_AUTHORITY] = 0; + message->counts[DNS_SECTION_ADDITIONAL] = 0; + return (ISC_TRUE); } static inline isc_result_t fctx_starttimer(fetchctx_t *fctx) { - /* - * Start the lifetime timer for fctx. - * - * This is also used for stopping the idle timer; in that - * case we must purge events already posted to ensure that - * no further idle events are delivered. - */ - return (isc_timer_reset(fctx->timer, isc_timertype_once, - &fctx->expires, NULL, ISC_TRUE)); + /* + * Start the lifetime timer for fctx. + * + * This is also used for stopping the idle timer; in that + * case we must purge events already posted to ensure that + * no further idle events are delivered. + */ + return (isc_timer_reset(fctx->timer, isc_timertype_once, + &fctx->expires, NULL, ISC_TRUE)); } static inline void fctx_stoptimer(fetchctx_t *fctx) { - isc_result_t result; - - /* - * We don't return a result if resetting the timer to inactive fails - * since there's nothing to be done about it. Resetting to inactive - * should never fail anyway, since the code as currently written - * cannot fail in that case. - */ - result = isc_timer_reset(fctx->timer, isc_timertype_inactive, - NULL, NULL, ISC_TRUE); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_timer_reset(): %s", - isc_result_totext(result)); - } + isc_result_t result; + + /* + * We don't return a result if resetting the timer to inactive fails + * since there's nothing to be done about it. Resetting to inactive + * should never fail anyway, since the code as currently written + * cannot fail in that case. + */ + result = isc_timer_reset(fctx->timer, isc_timertype_inactive, + NULL, NULL, ISC_TRUE); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_timer_reset(): %s", + isc_result_totext(result)); + } } static inline isc_result_t fctx_startidletimer(fetchctx_t *fctx) { - /* - * Start the idle timer for fctx. The lifetime timer continues - * to be in effect. - */ - return (isc_timer_reset(fctx->timer, isc_timertype_once, - &fctx->expires, &fctx->interval, - ISC_FALSE)); + /* + * Start the idle timer for fctx. The lifetime timer continues + * to be in effect. + */ + return (isc_timer_reset(fctx->timer, isc_timertype_once, + &fctx->expires, &fctx->interval, + ISC_FALSE)); } /* * Stopping the idle timer is equivalent to calling fctx_starttimer(), but * we use fctx_stopidletimer for readability in the code below. */ -#define fctx_stopidletimer fctx_starttimer +#define fctx_stopidletimer fctx_starttimer static inline void resquery_destroy(resquery_t **queryp) { - resquery_t *query; + resquery_t *query; - REQUIRE(queryp != NULL); - query = *queryp; - REQUIRE(!ISC_LINK_LINKED(query, link)); + REQUIRE(queryp != NULL); + query = *queryp; + REQUIRE(!ISC_LINK_LINKED(query, link)); - INSIST(query->tcpsocket == NULL); + INSIST(query->tcpsocket == NULL); - query->fctx->nqueries--; - if (SHUTTINGDOWN(query->fctx)) - maybe_destroy(query->fctx); /* Locks bucket. */ - query->magic = 0; - isc_mem_put(query->mctx, query, sizeof(*query)); - *queryp = NULL; + query->fctx->nqueries--; + if (SHUTTINGDOWN(query->fctx)) + maybe_destroy(query->fctx); /* Locks bucket. */ + query->magic = 0; + isc_mem_put(query->mctx, query, sizeof(*query)); + *queryp = NULL; } static void fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, - isc_time_t *finish, isc_boolean_t no_response) + isc_time_t *finish, isc_boolean_t no_response) { - fetchctx_t *fctx; - resquery_t *query; - unsigned int rtt; - unsigned int factor; - dns_adbfind_t *find; - dns_adbaddrinfo_t *addrinfo; - - query = *queryp; - fctx = query->fctx; - - FCTXTRACE("cancelquery"); - - REQUIRE(!RESQUERY_CANCELED(query)); - - query->attributes |= RESQUERY_ATTR_CANCELED; - - /* - * Should we update the RTT? - */ - if (finish != NULL || no_response) { - if (finish != NULL) { - /* - * We have both the start and finish times for this - * packet, so we can compute a real RTT. - */ - rtt = (unsigned int)isc_time_microdiff(finish, - &query->start); - factor = DNS_ADB_RTTADJDEFAULT; - } else { - /* - * We don't have an RTT for this query. Maybe the - * packet was lost, or maybe this server is very - * slow. We don't know. Increase the RTT. - */ - INSIST(no_response); - rtt = query->addrinfo->srtt + 200000; - if (rtt > 10000000) - rtt = 10000000; - /* - * Replace the current RTT with our value. - */ - factor = DNS_ADB_RTTADJREPLACE; - } - dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor); - } - - /* - * Age RTTs of servers not tried. - */ - factor = DNS_ADB_RTTADJAGE; - if (finish != NULL) - for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) - if (UNMARKED(addrinfo)) - dns_adb_adjustsrtt(fctx->adb, addrinfo, - 0, factor); - - if (finish != NULL && TRIEDFIND(fctx)) - for (find = ISC_LIST_HEAD(fctx->finds); - find != NULL; - find = ISC_LIST_NEXT(find, publink)) - for (addrinfo = ISC_LIST_HEAD(find->list); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) - if (UNMARKED(addrinfo)) - dns_adb_adjustsrtt(fctx->adb, addrinfo, - 0, factor); - - if (finish != NULL && TRIEDALT(fctx)) { - for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) - if (UNMARKED(addrinfo)) - dns_adb_adjustsrtt(fctx->adb, addrinfo, - 0, factor); - for (find = ISC_LIST_HEAD(fctx->altfinds); - find != NULL; - find = ISC_LIST_NEXT(find, publink)) - for (addrinfo = ISC_LIST_HEAD(find->list); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) - if (UNMARKED(addrinfo)) - dns_adb_adjustsrtt(fctx->adb, addrinfo, - 0, factor); - } - - if (query->dispentry != NULL) - dns_dispatch_removeresponse(&query->dispentry, deventp); - - ISC_LIST_UNLINK(fctx->queries, query, link); - - if (query->tsig != NULL) - isc_buffer_free(&query->tsig); - - if (query->tsigkey != NULL) - dns_tsigkey_detach(&query->tsigkey); - - /* - * Check for any outstanding socket events. If they exist, cancel - * them and let the event handlers finish the cleanup. The resolver - * only needs to worry about managing the connect and send events; - * the dispatcher manages the recv events. - */ - if (RESQUERY_CONNECTING(query)) - /* - * Cancel the connect. - */ - isc_socket_cancel(query->tcpsocket, NULL, - ISC_SOCKCANCEL_CONNECT); - else if (RESQUERY_SENDING(query)) - /* - * Cancel the pending send. - */ - isc_socket_cancel(dns_dispatch_getsocket(query->dispatch), - NULL, ISC_SOCKCANCEL_SEND); - - if (query->dispatch != NULL) - dns_dispatch_detach(&query->dispatch); - - if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query))) - /* - * It's safe to destroy the query now. - */ - resquery_destroy(&query); + fetchctx_t *fctx; + resquery_t *query; + unsigned int rtt; + unsigned int factor; + dns_adbfind_t *find; + dns_adbaddrinfo_t *addrinfo; + + query = *queryp; + fctx = query->fctx; + + FCTXTRACE("cancelquery"); + + REQUIRE(!RESQUERY_CANCELED(query)); + + query->attributes |= RESQUERY_ATTR_CANCELED; + + /* + * Should we update the RTT? + */ + if (finish != NULL || no_response) { + if (finish != NULL) { + /* + * We have both the start and finish times for this + * packet, so we can compute a real RTT. + */ + rtt = (unsigned int)isc_time_microdiff(finish, + &query->start); + factor = DNS_ADB_RTTADJDEFAULT; + } else { + /* + * We don't have an RTT for this query. Maybe the + * packet was lost, or maybe this server is very + * slow. We don't know. Increase the RTT. + */ + INSIST(no_response); + rtt = query->addrinfo->srtt + 200000; + if (rtt > 10000000) + rtt = 10000000; + /* + * Replace the current RTT with our value. + */ + factor = DNS_ADB_RTTADJREPLACE; + } + dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor); + } + + /* + * Age RTTs of servers not tried. + */ + factor = DNS_ADB_RTTADJAGE; + if (finish != NULL) + for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) + if (UNMARKED(addrinfo)) + dns_adb_adjustsrtt(fctx->adb, addrinfo, + 0, factor); + + if (finish != NULL && TRIEDFIND(fctx)) + for (find = ISC_LIST_HEAD(fctx->finds); + find != NULL; + find = ISC_LIST_NEXT(find, publink)) + for (addrinfo = ISC_LIST_HEAD(find->list); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) + if (UNMARKED(addrinfo)) + dns_adb_adjustsrtt(fctx->adb, addrinfo, + 0, factor); + + if (finish != NULL && TRIEDALT(fctx)) { + for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) + if (UNMARKED(addrinfo)) + dns_adb_adjustsrtt(fctx->adb, addrinfo, + 0, factor); + for (find = ISC_LIST_HEAD(fctx->altfinds); + find != NULL; + find = ISC_LIST_NEXT(find, publink)) + for (addrinfo = ISC_LIST_HEAD(find->list); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) + if (UNMARKED(addrinfo)) + dns_adb_adjustsrtt(fctx->adb, addrinfo, + 0, factor); + } + + if (query->dispentry != NULL) + dns_dispatch_removeresponse(&query->dispentry, deventp); + + ISC_LIST_UNLINK(fctx->queries, query, link); + + if (query->tsig != NULL) + isc_buffer_free(&query->tsig); + + if (query->tsigkey != NULL) + dns_tsigkey_detach(&query->tsigkey); + + /* + * Check for any outstanding socket events. If they exist, cancel + * them and let the event handlers finish the cleanup. The resolver + * only needs to worry about managing the connect and send events; + * the dispatcher manages the recv events. + */ + if (RESQUERY_CONNECTING(query)) + /* + * Cancel the connect. + */ + isc_socket_cancel(query->tcpsocket, NULL, + ISC_SOCKCANCEL_CONNECT); + else if (RESQUERY_SENDING(query)) + /* + * Cancel the pending send. + */ + isc_socket_cancel(dns_dispatch_getsocket(query->dispatch), + NULL, ISC_SOCKCANCEL_SEND); + + if (query->dispatch != NULL) + dns_dispatch_detach(&query->dispatch); + + if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query))) + /* + * It's safe to destroy the query now. + */ + resquery_destroy(&query); } static void fctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) { - resquery_t *query, *next_query; + resquery_t *query, *next_query; - FCTXTRACE("cancelqueries"); + FCTXTRACE("cancelqueries"); - for (query = ISC_LIST_HEAD(fctx->queries); - query != NULL; - query = next_query) { - next_query = ISC_LIST_NEXT(query, link); - fctx_cancelquery(&query, NULL, NULL, no_response); - } + for (query = ISC_LIST_HEAD(fctx->queries); + query != NULL; + query = next_query) { + next_query = ISC_LIST_NEXT(query, link); + fctx_cancelquery(&query, NULL, NULL, no_response); + } } static void fctx_cleanupfinds(fetchctx_t *fctx) { - dns_adbfind_t *find, *next_find; - - REQUIRE(ISC_LIST_EMPTY(fctx->queries)); - - for (find = ISC_LIST_HEAD(fctx->finds); - find != NULL; - find = next_find) { - next_find = ISC_LIST_NEXT(find, publink); - ISC_LIST_UNLINK(fctx->finds, find, publink); - dns_adb_destroyfind(&find); - } - fctx->find = NULL; + dns_adbfind_t *find, *next_find; + + REQUIRE(ISC_LIST_EMPTY(fctx->queries)); + + for (find = ISC_LIST_HEAD(fctx->finds); + find != NULL; + find = next_find) { + next_find = ISC_LIST_NEXT(find, publink); + ISC_LIST_UNLINK(fctx->finds, find, publink); + dns_adb_destroyfind(&find); + } + fctx->find = NULL; } static void fctx_cleanupaltfinds(fetchctx_t *fctx) { - dns_adbfind_t *find, *next_find; - - REQUIRE(ISC_LIST_EMPTY(fctx->queries)); - - for (find = ISC_LIST_HEAD(fctx->altfinds); - find != NULL; - find = next_find) { - next_find = ISC_LIST_NEXT(find, publink); - ISC_LIST_UNLINK(fctx->altfinds, find, publink); - dns_adb_destroyfind(&find); - } - fctx->altfind = NULL; + dns_adbfind_t *find, *next_find; + + REQUIRE(ISC_LIST_EMPTY(fctx->queries)); + + for (find = ISC_LIST_HEAD(fctx->altfinds); + find != NULL; + find = next_find) { + next_find = ISC_LIST_NEXT(find, publink); + ISC_LIST_UNLINK(fctx->altfinds, find, publink); + dns_adb_destroyfind(&find); + } + fctx->altfind = NULL; } static void fctx_cleanupforwaddrs(fetchctx_t *fctx) { - dns_adbaddrinfo_t *addr, *next_addr; + dns_adbaddrinfo_t *addr, *next_addr; - REQUIRE(ISC_LIST_EMPTY(fctx->queries)); + REQUIRE(ISC_LIST_EMPTY(fctx->queries)); - for (addr = ISC_LIST_HEAD(fctx->forwaddrs); - addr != NULL; - addr = next_addr) { - next_addr = ISC_LIST_NEXT(addr, publink); - ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink); - dns_adb_freeaddrinfo(fctx->adb, &addr); - } + for (addr = ISC_LIST_HEAD(fctx->forwaddrs); + addr != NULL; + addr = next_addr) { + next_addr = ISC_LIST_NEXT(addr, publink); + ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink); + dns_adb_freeaddrinfo(fctx->adb, &addr); + } } static void fctx_cleanupaltaddrs(fetchctx_t *fctx) { - dns_adbaddrinfo_t *addr, *next_addr; + dns_adbaddrinfo_t *addr, *next_addr; - REQUIRE(ISC_LIST_EMPTY(fctx->queries)); + REQUIRE(ISC_LIST_EMPTY(fctx->queries)); - for (addr = ISC_LIST_HEAD(fctx->altaddrs); - addr != NULL; - addr = next_addr) { - next_addr = ISC_LIST_NEXT(addr, publink); - ISC_LIST_UNLINK(fctx->altaddrs, addr, publink); - dns_adb_freeaddrinfo(fctx->adb, &addr); - } + for (addr = ISC_LIST_HEAD(fctx->altaddrs); + addr != NULL; + addr = next_addr) { + next_addr = ISC_LIST_NEXT(addr, publink); + ISC_LIST_UNLINK(fctx->altaddrs, addr, publink); + dns_adb_freeaddrinfo(fctx->adb, &addr); + } } static inline void fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) { - FCTXTRACE("stopeverything"); - fctx_cancelqueries(fctx, no_response); - fctx_cleanupfinds(fctx); - fctx_cleanupaltfinds(fctx); - fctx_cleanupforwaddrs(fctx); - fctx_cleanupaltaddrs(fctx); - fctx_stoptimer(fctx); + FCTXTRACE("stopeverything"); + fctx_cancelqueries(fctx, no_response); + fctx_cleanupfinds(fctx); + fctx_cleanupaltfinds(fctx); + fctx_cleanupforwaddrs(fctx); + fctx_cleanupaltaddrs(fctx); + fctx_stoptimer(fctx); } static inline void fctx_sendevents(fetchctx_t *fctx, isc_result_t result) { - dns_fetchevent_t *event, *next_event; - isc_task_t *task; - unsigned int count = 0; - isc_interval_t i; - isc_boolean_t logit = ISC_FALSE; - - /* - * Caller must be holding the appropriate bucket lock. - */ - REQUIRE(fctx->state == fetchstate_done); - - FCTXTRACE("sendevents"); - - for (event = ISC_LIST_HEAD(fctx->events); - event != NULL; - event = next_event) { - next_event = ISC_LIST_NEXT(event, ev_link); - ISC_LIST_UNLINK(fctx->events, event, ev_link); - task = event->ev_sender; - event->ev_sender = fctx; - if (!HAVE_ANSWER(fctx)) - event->result = result; - - INSIST(result != ISC_R_SUCCESS || - dns_rdataset_isassociated(event->rdataset) || - fctx->type == dns_rdatatype_any || - fctx->type == dns_rdatatype_rrsig || - fctx->type == dns_rdatatype_sig); - - /* - * Negative results must be indicated in event->result. - */ - if (dns_rdataset_isassociated(event->rdataset) && - event->rdataset->type == dns_rdatatype_none) { - INSIST(event->result == DNS_R_NCACHENXDOMAIN || - event->result == DNS_R_NCACHENXRRSET); - } - - isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event)); - count++; - } - - if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 && - fctx->spilled && - (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) { - LOCK(&fctx->res->lock); - if (count == fctx->res->spillat && !fctx->res->exiting) { - fctx->res->spillat += 5; - if (fctx->res->spillat > fctx->res->spillatmax && - fctx->res->spillatmax != 0) - fctx->res->spillat = fctx->res->spillatmax; - isc_interval_set(&i, 20 * 60, 0); - result = isc_timer_reset(fctx->res->spillattimer, - isc_timertype_ticker, NULL, - &i, ISC_TRUE); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - logit = ISC_TRUE; - } - UNLOCK(&fctx->res->lock); - if (logit) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, - "clients-per-query increased to %u", - count + 1); - } + dns_fetchevent_t *event, *next_event; + isc_task_t *task; + unsigned int count = 0; + isc_interval_t i; + isc_boolean_t logit = ISC_FALSE; + + /* + * Caller must be holding the appropriate bucket lock. + */ + REQUIRE(fctx->state == fetchstate_done); + + FCTXTRACE("sendevents"); + + for (event = ISC_LIST_HEAD(fctx->events); + event != NULL; + event = next_event) { + next_event = ISC_LIST_NEXT(event, ev_link); + ISC_LIST_UNLINK(fctx->events, event, ev_link); + task = event->ev_sender; + event->ev_sender = fctx; + if (!HAVE_ANSWER(fctx)) + event->result = result; + + INSIST(result != ISC_R_SUCCESS || + dns_rdataset_isassociated(event->rdataset) || + fctx->type == dns_rdatatype_any || + fctx->type == dns_rdatatype_rrsig || + fctx->type == dns_rdatatype_sig); + + /* + * Negative results must be indicated in event->result. + */ + if (dns_rdataset_isassociated(event->rdataset) && + event->rdataset->type == dns_rdatatype_none) { + INSIST(event->result == DNS_R_NCACHENXDOMAIN || + event->result == DNS_R_NCACHENXRRSET); + } + + isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event)); + count++; + } + + if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 && + fctx->spilled && + (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) { + LOCK(&fctx->res->lock); + if (count == fctx->res->spillat && !fctx->res->exiting) { + fctx->res->spillat += 5; + if (fctx->res->spillat > fctx->res->spillatmax && + fctx->res->spillatmax != 0) + fctx->res->spillat = fctx->res->spillatmax; + isc_interval_set(&i, 20 * 60, 0); + result = isc_timer_reset(fctx->res->spillattimer, + isc_timertype_ticker, NULL, + &i, ISC_TRUE); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + logit = ISC_TRUE; + } + UNLOCK(&fctx->res->lock); + if (logit) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, + "clients-per-query increased to %u", + count + 1); + } } static void fctx_done(fetchctx_t *fctx, isc_result_t result) { - dns_resolver_t *res; - isc_boolean_t no_response; + dns_resolver_t *res; + isc_boolean_t no_response; - FCTXTRACE("done"); + FCTXTRACE("done"); - res = fctx->res; + res = fctx->res; - if (result == ISC_R_SUCCESS) - no_response = ISC_TRUE; - else - no_response = ISC_FALSE; - fctx_stopeverything(fctx, no_response); + if (result == ISC_R_SUCCESS) + no_response = ISC_TRUE; + else + no_response = ISC_FALSE; + fctx_stopeverything(fctx, no_response); - LOCK(&res->buckets[fctx->bucketnum].lock); + LOCK(&res->buckets[fctx->bucketnum].lock); - fctx->state = fetchstate_done; - fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - fctx_sendevents(fctx, result); + fctx->state = fetchstate_done; + fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; + fctx_sendevents(fctx, result); - UNLOCK(&res->buckets[fctx->bucketnum].lock); + UNLOCK(&res->buckets[fctx->bucketnum].lock); } static void resquery_senddone(isc_task_t *task, isc_event_t *event) { - isc_socketevent_t *sevent = (isc_socketevent_t *)event; - resquery_t *query = event->ev_arg; - isc_boolean_t retry = ISC_FALSE; - isc_result_t result; - fetchctx_t *fctx; - - REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); - - QTRACE("senddone"); - - /* - * XXXRTH - * - * Currently we don't wait for the senddone event before retrying - * a query. This means that if we get really behind, we may end - * up doing extra work! - */ - - UNUSED(task); - - INSIST(RESQUERY_SENDING(query)); - - query->sends--; - fctx = query->fctx; - - if (RESQUERY_CANCELED(query)) { - if (query->sends == 0) { - /* - * This query was canceled while the - * isc_socket_sendto() was in progress. - */ - if (query->tcpsocket != NULL) - isc_socket_detach(&query->tcpsocket); - resquery_destroy(&query); - } - } else - switch (sevent->result) { - case ISC_R_SUCCESS: - break; - - case ISC_R_HOSTUNREACH: - case ISC_R_NETUNREACH: - case ISC_R_NOPERM: - case ISC_R_ADDRNOTAVAIL: - case ISC_R_CONNREFUSED: - - /* - * No route to remote. - */ - fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); - retry = ISC_TRUE; - break; - - default: - fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); - break; - } - - isc_event_free(&event); - - if (retry) { - /* - * Behave as if the idle timer has expired. For TCP - * this may not actually reflect the latest timer. - */ - fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - result = fctx_stopidletimer(fctx); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - else - fctx_try(fctx); - } + isc_socketevent_t *sevent = (isc_socketevent_t *)event; + resquery_t *query = event->ev_arg; + isc_boolean_t retry = ISC_FALSE; + isc_result_t result; + fetchctx_t *fctx; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); + + QTRACE("senddone"); + + /* + * XXXRTH + * + * Currently we don't wait for the senddone event before retrying + * a query. This means that if we get really behind, we may end + * up doing extra work! + */ + + UNUSED(task); + + INSIST(RESQUERY_SENDING(query)); + + query->sends--; + fctx = query->fctx; + + if (RESQUERY_CANCELED(query)) { + if (query->sends == 0) { + /* + * This query was canceled while the + * isc_socket_sendto() was in progress. + */ + if (query->tcpsocket != NULL) + isc_socket_detach(&query->tcpsocket); + resquery_destroy(&query); + } + } else + switch (sevent->result) { + case ISC_R_SUCCESS: + break; + + case ISC_R_HOSTUNREACH: + case ISC_R_NETUNREACH: + case ISC_R_NOPERM: + case ISC_R_ADDRNOTAVAIL: + case ISC_R_CONNREFUSED: + + /* + * No route to remote. + */ + fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); + retry = ISC_TRUE; + break; + + default: + fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); + break; + } + + isc_event_free(&event); + + if (retry) { + /* + * Behave as if the idle timer has expired. For TCP + * this may not actually reflect the latest timer. + */ + fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; + result = fctx_stopidletimer(fctx); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + else + fctx_try(fctx); + } } static inline isc_result_t fctx_addopt(dns_message_t *message, unsigned int version, isc_uint16_t udpsize) -{ - dns_rdataset_t *rdataset; - dns_rdatalist_t *rdatalist; - dns_rdata_t *rdata; - isc_result_t result; - - rdatalist = NULL; - result = dns_message_gettemprdatalist(message, &rdatalist); - if (result != ISC_R_SUCCESS) - return (result); - rdata = NULL; - result = dns_message_gettemprdata(message, &rdata); - if (result != ISC_R_SUCCESS) - return (result); - rdataset = NULL; - result = dns_message_gettemprdataset(message, &rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_init(rdataset); - - rdatalist->type = dns_rdatatype_opt; - rdatalist->covers = 0; - - /* - * Set Maximum UDP buffer size. - */ - rdatalist->rdclass = udpsize; - - /* - * Set EXTENDED-RCODE and Z to 0, DO to 1. - */ - rdatalist->ttl = (version << 16); - rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO; - - /* - * No EDNS options. - */ - rdata->data = NULL; - rdata->length = 0; - rdata->rdclass = rdatalist->rdclass; - rdata->type = rdatalist->type; - rdata->flags = 0; - - ISC_LIST_INIT(rdatalist->rdata); - ISC_LIST_APPEND(rdatalist->rdata, rdata, link); - RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ISC_R_SUCCESS); - - return (dns_message_setopt(message, rdataset)); +{ + dns_rdataset_t *rdataset; + dns_rdatalist_t *rdatalist; + dns_rdata_t *rdata; + isc_result_t result; + + rdatalist = NULL; + result = dns_message_gettemprdatalist(message, &rdatalist); + if (result != ISC_R_SUCCESS) + return (result); + rdata = NULL; + result = dns_message_gettemprdata(message, &rdata); + if (result != ISC_R_SUCCESS) + return (result); + rdataset = NULL; + result = dns_message_gettemprdataset(message, &rdataset); + if (result != ISC_R_SUCCESS) + return (result); + dns_rdataset_init(rdataset); + + rdatalist->type = dns_rdatatype_opt; + rdatalist->covers = 0; + + /* + * Set Maximum UDP buffer size. + */ + rdatalist->rdclass = udpsize; + + /* + * Set EXTENDED-RCODE and Z to 0, DO to 1. + */ + rdatalist->ttl = (version << 16); + rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO; + + /* + * No EDNS options. + */ + rdata->data = NULL; + rdata->length = 0; + rdata->rdclass = rdatalist->rdclass; + rdata->type = rdatalist->type; + rdata->flags = 0; + + ISC_LIST_INIT(rdatalist->rdata); + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ISC_R_SUCCESS); + + return (dns_message_setopt(message, rdataset)); } static inline void fctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) { - unsigned int seconds; - unsigned int us; - - /* - * We retry every .5 seconds the first two times through the address - * list, and then we do exponential back-off. - */ - if (fctx->restarts < 3) - us = 500000; - else - us = (500000 << (fctx->restarts - 2)); - - /* - * Double the round-trip time. - */ - rtt *= 2; - - /* - * Always wait for at least the doubled round-trip time. - */ - if (us < rtt) - us = rtt; - - /* - * But don't ever wait for more than 10 seconds. - */ - if (us > 10000000) - us = 10000000; - - seconds = us / 1000000; - us -= seconds * 1000000; - isc_interval_set(&fctx->interval, seconds, us * 1000); + unsigned int seconds; + unsigned int us; + + /* + * We retry every .5 seconds the first two times through the address + * list, and then we do exponential back-off. + */ + if (fctx->restarts < 3) + us = 500000; + else + us = (500000 << (fctx->restarts - 2)); + + /* + * Double the round-trip time. + */ + rtt *= 2; + + /* + * Always wait for at least the doubled round-trip time. + */ + if (us < rtt) + us = rtt; + + /* + * But don't ever wait for more than 10 seconds. + */ + if (us > 10000000) + us = 10000000; + + seconds = us / 1000000; + us -= seconds * 1000000; + isc_interval_set(&fctx->interval, seconds, us * 1000); } static isc_result_t fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, - unsigned int options) + unsigned int options) { - dns_resolver_t *res; - isc_task_t *task; - isc_result_t result; - resquery_t *query; - isc_sockaddr_t addr; - isc_boolean_t have_addr = ISC_FALSE; - - FCTXTRACE("query"); - - res = fctx->res; - task = res->buckets[fctx->bucketnum].task; - - fctx_setretryinterval(fctx, addrinfo->srtt); - result = fctx_startidletimer(fctx); - if (result != ISC_R_SUCCESS) - return (result); - - INSIST(ISC_LIST_EMPTY(fctx->validators)); - - dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE); - - query = isc_mem_get(res->buckets[fctx->bucketnum].mctx, - sizeof(*query)); - if (query == NULL) { - result = ISC_R_NOMEMORY; - goto stop_idle_timer; - } - query->mctx = res->buckets[fctx->bucketnum].mctx; - query->options = options; - query->attributes = 0; - query->sends = 0; - query->connects = 0; - /* - * Note that the caller MUST guarantee that 'addrinfo' will remain - * valid until this query is canceled. - */ - query->addrinfo = addrinfo; - TIME_NOW(&query->start); - - /* - * If this is a TCP query, then we need to make a socket and - * a dispatch for it here. Otherwise we use the resolver's - * shared dispatch. - */ - query->dispatchmgr = res->dispatchmgr; - query->dispatch = NULL; - query->tcpsocket = NULL; - if (res->view->peers != NULL) { - dns_peer_t *peer = NULL; - isc_netaddr_t dstip; - isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr); - result = dns_peerlist_peerbyaddr(res->view->peers, - &dstip, &peer); - if (result == ISC_R_SUCCESS) { - result = dns_peer_getquerysource(peer, &addr); - if (result == ISC_R_SUCCESS) - have_addr = ISC_TRUE; - } - } - - if ((query->options & DNS_FETCHOPT_TCP) != 0) { - int pf; - - pf = isc_sockaddr_pf(&addrinfo->sockaddr); - if (!have_addr) { - switch (pf) { - case PF_INET: - result = - dns_dispatch_getlocaladdress(res->dispatchv4, - &addr); - break; - case PF_INET6: - result = - dns_dispatch_getlocaladdress(res->dispatchv6, - &addr); - break; - default: - result = ISC_R_NOTIMPLEMENTED; - break; - } - if (result != ISC_R_SUCCESS) - goto cleanup_query; - } - isc_sockaddr_setport(&addr, 0); - - result = isc_socket_create(res->socketmgr, pf, - isc_sockettype_tcp, - &query->tcpsocket); - if (result != ISC_R_SUCCESS) - goto cleanup_query; + dns_resolver_t *res; + isc_task_t *task; + isc_result_t result; + resquery_t *query; + isc_sockaddr_t addr; + isc_boolean_t have_addr = ISC_FALSE; + + FCTXTRACE("query"); + + res = fctx->res; + task = res->buckets[fctx->bucketnum].task; + + fctx_setretryinterval(fctx, addrinfo->srtt); + result = fctx_startidletimer(fctx); + if (result != ISC_R_SUCCESS) + return (result); + + INSIST(ISC_LIST_EMPTY(fctx->validators)); + + dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE); + + query = isc_mem_get(res->buckets[fctx->bucketnum].mctx, + sizeof(*query)); + if (query == NULL) { + result = ISC_R_NOMEMORY; + goto stop_idle_timer; + } + query->mctx = res->buckets[fctx->bucketnum].mctx; + query->options = options; + query->attributes = 0; + query->sends = 0; + query->connects = 0; + /* + * Note that the caller MUST guarantee that 'addrinfo' will remain + * valid until this query is canceled. + */ + query->addrinfo = addrinfo; + TIME_NOW(&query->start); + + /* + * If this is a TCP query, then we need to make a socket and + * a dispatch for it here. Otherwise we use the resolver's + * shared dispatch. + */ + query->dispatchmgr = res->dispatchmgr; + query->dispatch = NULL; + query->tcpsocket = NULL; + if (res->view->peers != NULL) { + dns_peer_t *peer = NULL; + isc_netaddr_t dstip; + isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr); + result = dns_peerlist_peerbyaddr(res->view->peers, + &dstip, &peer); + if (result == ISC_R_SUCCESS) { + result = dns_peer_getquerysource(peer, &addr); + if (result == ISC_R_SUCCESS) + have_addr = ISC_TRUE; + } + } + + if ((query->options & DNS_FETCHOPT_TCP) != 0) { + int pf; + + pf = isc_sockaddr_pf(&addrinfo->sockaddr); + if (!have_addr) { + switch (pf) { + case PF_INET: + result = + dns_dispatch_getlocaladdress(res->dispatchv4, + &addr); + break; + case PF_INET6: + result = + dns_dispatch_getlocaladdress(res->dispatchv6, + &addr); + break; + default: + result = ISC_R_NOTIMPLEMENTED; + break; + } + if (result != ISC_R_SUCCESS) + goto cleanup_query; + } + isc_sockaddr_setport(&addr, 0); + + result = isc_socket_create(res->socketmgr, pf, + isc_sockettype_tcp, + &query->tcpsocket); + if (result != ISC_R_SUCCESS) + goto cleanup_query; #ifndef BROKEN_TCP_BIND_BEFORE_CONNECT - result = isc_socket_bind(query->tcpsocket, &addr); - if (result != ISC_R_SUCCESS) - goto cleanup_socket; + result = isc_socket_bind(query->tcpsocket, &addr); + if (result != ISC_R_SUCCESS) + goto cleanup_socket; #endif - /* - * A dispatch will be created once the connect succeeds. - */ - } else { - if (have_addr) { - unsigned int attrs, attrmask; - attrs = DNS_DISPATCHATTR_UDP; - switch (isc_sockaddr_pf(&addr)) { - case AF_INET: - attrs |= DNS_DISPATCHATTR_IPV4; - break; - case AF_INET6: - attrs |= DNS_DISPATCHATTR_IPV6; - break; - default: - result = ISC_R_NOTIMPLEMENTED; - goto cleanup_query; - } - attrmask = DNS_DISPATCHATTR_UDP; - attrmask |= DNS_DISPATCHATTR_TCP; - attrmask |= DNS_DISPATCHATTR_IPV4; - attrmask |= DNS_DISPATCHATTR_IPV6; - result = dns_dispatch_getudp(res->dispatchmgr, - res->socketmgr, - res->taskmgr, &addr, - 4096, 1000, 32768, 16411, - 16433, attrs, attrmask, - &query->dispatch); - if (result != ISC_R_SUCCESS) - goto cleanup_query; - } else { - int did = 0; - isc_uint32_t val; - - if (res->ndisps > 0) { - isc_random_get(&val); - did = val % res->ndisps; - } - switch (isc_sockaddr_pf(&addrinfo->sockaddr)) { - case PF_INET: - if (res->dispatchv4pool != NULL) { - RES_LOCK(&res->poollock, - isc_rwlocktype_read); - dns_dispatch_attach(res->dispatchv4pool[did], - &query->dispatch); - RES_UNLOCK(&res->poollock, - isc_rwlocktype_read); - } else { - dns_dispatch_attach(res->dispatchv4, - &query->dispatch); - } - break; - case PF_INET6: - if (res->dispatchv6pool != NULL) { - RES_LOCK(&res->poollock, - isc_rwlocktype_read); - dns_dispatch_attach(res->dispatchv6pool[did], - &query->dispatch); - RES_UNLOCK(&res->poollock, - isc_rwlocktype_read); - } else { - dns_dispatch_attach(res->dispatchv6, - &query->dispatch); - } - break; - default: - result = ISC_R_NOTIMPLEMENTED; - goto cleanup_query; - } - } - /* - * We should always have a valid dispatcher here. If we - * don't support a protocol family, then its dispatcher - * will be NULL, but we shouldn't be finding addresses for - * protocol types we don't support, so the dispatcher - * we found should never be NULL. - */ - INSIST(query->dispatch != NULL); - } - - query->dispentry = NULL; - query->fctx = fctx; - query->tsig = NULL; - query->tsigkey = NULL; - ISC_LINK_INIT(query, link); - query->magic = QUERY_MAGIC; - - if ((query->options & DNS_FETCHOPT_TCP) != 0) { - /* - * Connect to the remote server. - * - * XXXRTH Should we attach to the socket? - */ - result = isc_socket_connect(query->tcpsocket, - &addrinfo->sockaddr, task, - resquery_connected, query); - if (result != ISC_R_SUCCESS) - goto cleanup_socket; - query->connects++; - QTRACE("connecting via TCP"); - } else { - result = resquery_send(query); - if (result != ISC_R_SUCCESS) - goto cleanup_dispatch; - } - - ISC_LIST_APPEND(fctx->queries, query, link); - query->fctx->nqueries++; - - return (ISC_R_SUCCESS); + /* + * A dispatch will be created once the connect succeeds. + */ + } else { + if (have_addr) { + unsigned int attrs, attrmask; + attrs = DNS_DISPATCHATTR_UDP; + switch (isc_sockaddr_pf(&addr)) { + case AF_INET: + attrs |= DNS_DISPATCHATTR_IPV4; + break; + case AF_INET6: + attrs |= DNS_DISPATCHATTR_IPV6; + break; + default: + result = ISC_R_NOTIMPLEMENTED; + goto cleanup_query; + } + attrmask = DNS_DISPATCHATTR_UDP; + attrmask |= DNS_DISPATCHATTR_TCP; + attrmask |= DNS_DISPATCHATTR_IPV4; + attrmask |= DNS_DISPATCHATTR_IPV6; + result = dns_dispatch_getudp(res->dispatchmgr, + res->socketmgr, + res->taskmgr, &addr, + 4096, 1000, 32768, 16411, + 16433, attrs, attrmask, + &query->dispatch); + if (result != ISC_R_SUCCESS) + goto cleanup_query; + } else { + int did = 0; + isc_uint32_t val; + + if (res->ndisps > 0) { + isc_random_get(&val); + did = val % res->ndisps; + } + switch (isc_sockaddr_pf(&addrinfo->sockaddr)) { + case PF_INET: + if (res->dispatchv4pool != NULL) { + RES_LOCK(&res->poollock, + isc_rwlocktype_read); + dns_dispatch_attach(res->dispatchv4pool[did], + &query->dispatch); + RES_UNLOCK(&res->poollock, + isc_rwlocktype_read); + } else { + dns_dispatch_attach(res->dispatchv4, + &query->dispatch); + } + break; + case PF_INET6: + if (res->dispatchv6pool != NULL) { + RES_LOCK(&res->poollock, + isc_rwlocktype_read); + dns_dispatch_attach(res->dispatchv6pool[did], + &query->dispatch); + RES_UNLOCK(&res->poollock, + isc_rwlocktype_read); + } else { + dns_dispatch_attach(res->dispatchv6, + &query->dispatch); + } + break; + default: + result = ISC_R_NOTIMPLEMENTED; + goto cleanup_query; + } + } + /* + * We should always have a valid dispatcher here. If we + * don't support a protocol family, then its dispatcher + * will be NULL, but we shouldn't be finding addresses for + * protocol types we don't support, so the dispatcher + * we found should never be NULL. + */ + INSIST(query->dispatch != NULL); + } + + query->dispentry = NULL; + query->fctx = fctx; + query->tsig = NULL; + query->tsigkey = NULL; + ISC_LINK_INIT(query, link); + query->magic = QUERY_MAGIC; + + if ((query->options & DNS_FETCHOPT_TCP) != 0) { + /* + * Connect to the remote server. + * + * XXXRTH Should we attach to the socket? + */ + result = isc_socket_connect(query->tcpsocket, + &addrinfo->sockaddr, task, + resquery_connected, query); + if (result != ISC_R_SUCCESS) + goto cleanup_socket; + query->connects++; + QTRACE("connecting via TCP"); + } else { + result = resquery_send(query); + if (result != ISC_R_SUCCESS) + goto cleanup_dispatch; + } + + ISC_LIST_APPEND(fctx->queries, query, link); + query->fctx->nqueries++; + + return (ISC_R_SUCCESS); cleanup_socket: - isc_socket_detach(&query->tcpsocket); + isc_socket_detach(&query->tcpsocket); cleanup_dispatch: - if (query->dispatch != NULL) - dns_dispatch_detach(&query->dispatch); + if (query->dispatch != NULL) + dns_dispatch_detach(&query->dispatch); cleanup_query: - query->magic = 0; - isc_mem_put(res->buckets[fctx->bucketnum].mctx, - query, sizeof(*query)); + query->magic = 0; + isc_mem_put(res->buckets[fctx->bucketnum].mctx, + query, sizeof(*query)); stop_idle_timer: - RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS); + RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS); - return (result); + return (result); } static isc_boolean_t triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + isc_sockaddr_t *sa; - for (sa = ISC_LIST_HEAD(fctx->edns); - sa != NULL; - sa = ISC_LIST_NEXT(sa, link)) { - if (isc_sockaddr_equal(sa, address)) - return (ISC_TRUE); - } + for (sa = ISC_LIST_HEAD(fctx->edns); + sa != NULL; + sa = ISC_LIST_NEXT(sa, link)) { + if (isc_sockaddr_equal(sa, address)) + return (ISC_TRUE); + } - return (ISC_FALSE); + return (ISC_FALSE); } static void add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + isc_sockaddr_t *sa; - if (triededns(fctx, address)) - return; + if (triededns(fctx, address)) + return; - sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, - sizeof(*sa)); - if (sa == NULL) - return; + sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, + sizeof(*sa)); + if (sa == NULL) + return; - *sa = *address; - ISC_LIST_INITANDAPPEND(fctx->edns, sa, link); + *sa = *address; + ISC_LIST_INITANDAPPEND(fctx->edns, sa, link); } static isc_boolean_t triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + isc_sockaddr_t *sa; - for (sa = ISC_LIST_HEAD(fctx->edns512); - sa != NULL; - sa = ISC_LIST_NEXT(sa, link)) { - if (isc_sockaddr_equal(sa, address)) - return (ISC_TRUE); - } + for (sa = ISC_LIST_HEAD(fctx->edns512); + sa != NULL; + sa = ISC_LIST_NEXT(sa, link)) { + if (isc_sockaddr_equal(sa, address)) + return (ISC_TRUE); + } - return (ISC_FALSE); + return (ISC_FALSE); } static void add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + isc_sockaddr_t *sa; - if (triededns512(fctx, address)) - return; + if (triededns512(fctx, address)) + return; - sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, - sizeof(*sa)); - if (sa == NULL) - return; + sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, + sizeof(*sa)); + if (sa == NULL) + return; - *sa = *address; - ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link); + *sa = *address; + ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link); } static inline void log_edns(fetchctx_t *fctx) { - char domainbuf[DNS_NAME_FORMATSIZE]; - - dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_EDNS_DISABLED, - DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, - "too many timeouts resolving '%s' (in '%s'?): " - "disabling EDNS", fctx->info, domainbuf); + char domainbuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_EDNS_DISABLED, + DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, + "too many timeouts resolving '%s' (in '%s'?): " + "disabling EDNS", fctx->info, domainbuf); } static isc_result_t resquery_send(resquery_t *query) { - fetchctx_t *fctx; - isc_result_t result; - dns_name_t *qname = NULL; - dns_rdataset_t *qrdataset = NULL; - isc_region_t r; - dns_resolver_t *res; - isc_task_t *task; - isc_socket_t *socket; - isc_buffer_t tcpbuffer; - isc_sockaddr_t *address; - isc_buffer_t *buffer; - isc_netaddr_t ipaddr; - dns_tsigkey_t *tsigkey = NULL; - dns_peer_t *peer = NULL; - isc_boolean_t useedns; - dns_compress_t cctx; - isc_boolean_t cleanup_cctx = ISC_FALSE; - isc_boolean_t secure_domain; - - fctx = query->fctx; - QTRACE("send"); - - res = fctx->res; - task = res->buckets[fctx->bucketnum].task; - address = NULL; - - if ((query->options & DNS_FETCHOPT_TCP) != 0) { - /* - * Reserve space for the TCP message length. - */ - isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data)); - isc_buffer_init(&query->buffer, query->data + 2, - sizeof(query->data) - 2); - buffer = &tcpbuffer; - } else { - isc_buffer_init(&query->buffer, query->data, - sizeof(query->data)); - buffer = &query->buffer; - } - - result = dns_message_gettempname(fctx->qmessage, &qname); - if (result != ISC_R_SUCCESS) - goto cleanup_temps; - result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset); - if (result != ISC_R_SUCCESS) - goto cleanup_temps; - - /* - * Get a query id from the dispatch. - */ - result = dns_dispatch_addresponse(query->dispatch, - &query->addrinfo->sockaddr, - task, - resquery_response, - query, - &query->id, - &query->dispentry); - if (result != ISC_R_SUCCESS) - goto cleanup_temps; - - fctx->qmessage->opcode = dns_opcode_query; - - /* - * Set up question. - */ - dns_name_init(qname, NULL); - dns_name_clone(&fctx->name, qname); - dns_rdataset_init(qrdataset); - dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type); - ISC_LIST_APPEND(qname->list, qrdataset, link); - dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION); - qname = NULL; - qrdataset = NULL; - - /* - * Set RD if the client has requested that we do a recursive query, - * or if we're sending to a forwarder. - */ - if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 || - ISFORWARDER(query->addrinfo)) - fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD; - - /* - * Set CD if the client says don't validate or the question is - * under a secure entry point. - */ - if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) { - fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; - } else if (res->view->enablevalidation) { - result = dns_keytable_issecuredomain(res->view->secroots, - &fctx->name, - &secure_domain); - if (result != ISC_R_SUCCESS) - secure_domain = ISC_FALSE; - if (res->view->dlv != NULL) - secure_domain = ISC_TRUE; - if (secure_domain) - fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; - } - - /* - * We don't have to set opcode because it defaults to query. - */ - fctx->qmessage->id = query->id; - - /* - * Convert the question to wire format. - */ - result = dns_compress_init(&cctx, -1, fctx->res->mctx); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - cleanup_cctx = ISC_TRUE; - - result = dns_message_renderbegin(fctx->qmessage, &cctx, - &query->buffer); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - - result = dns_message_rendersection(fctx->qmessage, - DNS_SECTION_QUESTION, 0); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - - peer = NULL; - isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr); - (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer); - - /* - * The ADB does not know about servers with "edns no". Check this, - * and then inform the ADB for future use. - */ - if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 && - peer != NULL && - dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS && - !useedns) - { - query->options |= DNS_FETCHOPT_NOEDNS0; - dns_adb_changeflags(fctx->adb, - query->addrinfo, - DNS_FETCHOPT_NOEDNS0, - DNS_FETCHOPT_NOEDNS0); - } - - /* - * Use EDNS0, unless the caller doesn't want it, or we know that - * the remote server doesn't like it. - */ - - if ((triededns512(fctx, &query->addrinfo->sockaddr) || - fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) && - (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { - query->options |= DNS_FETCHOPT_NOEDNS0; - log_edns(fctx); - } else if ((triededns(fctx, &query->addrinfo->sockaddr) || - fctx->timeouts >= MAX_EDNS0_TIMEOUTS) && - (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { - query->options |= DNS_FETCHOPT_EDNS512; - FCTXTRACE("too many timeouts, setting EDNS size to 512"); - } - - if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { - if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) { - unsigned int version = 0; /* Default version. */ - unsigned int flags; - isc_uint16_t udpsize = res->udpsize; - - flags = query->addrinfo->flags; - if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) { - version = flags & DNS_FETCHOPT_EDNSVERSIONMASK; - version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT; - } - if ((query->options & DNS_FETCHOPT_EDNS512) != 0) - udpsize = 512; - else if (peer != NULL) - (void)dns_peer_getudpsize(peer, &udpsize); - result = fctx_addopt(fctx->qmessage, version, udpsize); - if (result != ISC_R_SUCCESS) { - /* - * We couldn't add the OPT, but we'll press on. - * We're not using EDNS0, so set the NOEDNS0 - * bit. - */ - query->options |= DNS_FETCHOPT_NOEDNS0; - } - } else { - /* - * We know this server doesn't like EDNS0, so we - * won't use it. Set the NOEDNS0 bit since we're - * not using EDNS0. - */ - query->options |= DNS_FETCHOPT_NOEDNS0; - } - } - - /* - * If we need EDNS0 to do this query and aren't using it, we lose. - */ - if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) { - result = DNS_R_SERVFAIL; - goto cleanup_message; - } - - if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) - add_triededns(fctx, &query->addrinfo->sockaddr); - - if ((query->options & DNS_FETCHOPT_EDNS512) != 0) - add_triededns512(fctx, &query->addrinfo->sockaddr); - - /* - * Clear CD if EDNS is not in use. - */ - if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0) - fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD; - - /* - * Add TSIG record tailored to the current recipient. - */ - result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) - goto cleanup_message; - - if (tsigkey != NULL) { - result = dns_message_settsigkey(fctx->qmessage, tsigkey); - dns_tsigkey_detach(&tsigkey); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - } - - result = dns_message_rendersection(fctx->qmessage, - DNS_SECTION_ADDITIONAL, 0); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - - result = dns_message_renderend(fctx->qmessage); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - - dns_compress_invalidate(&cctx); - cleanup_cctx = ISC_FALSE; - - if (dns_message_gettsigkey(fctx->qmessage) != NULL) { - dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage), - &query->tsigkey); - result = dns_message_getquerytsig(fctx->qmessage, - fctx->res->mctx, - &query->tsig); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - } - - /* - * If using TCP, write the length of the message at the beginning - * of the buffer. - */ - if ((query->options & DNS_FETCHOPT_TCP) != 0) { - isc_buffer_usedregion(&query->buffer, &r); - isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length); - isc_buffer_add(&tcpbuffer, r.length); - } - - /* - * We're now done with the query message. - */ - dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); - - socket = dns_dispatch_getsocket(query->dispatch); - /* - * Send the query! - */ - if ((query->options & DNS_FETCHOPT_TCP) == 0) - address = &query->addrinfo->sockaddr; - isc_buffer_usedregion(buffer, &r); - - /* - * XXXRTH Make sure we don't send to ourselves! We should probably - * prune out these addresses when we get them from the ADB. - */ - result = isc_socket_sendto(socket, &r, task, resquery_senddone, - query, address, NULL); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - query->sends++; - QTRACE("sent"); - - return (ISC_R_SUCCESS); + fetchctx_t *fctx; + isc_result_t result; + dns_name_t *qname = NULL; + dns_rdataset_t *qrdataset = NULL; + isc_region_t r; + dns_resolver_t *res; + isc_task_t *task; + isc_socket_t *socket; + isc_buffer_t tcpbuffer; + isc_sockaddr_t *address; + isc_buffer_t *buffer; + isc_netaddr_t ipaddr; + dns_tsigkey_t *tsigkey = NULL; + dns_peer_t *peer = NULL; + isc_boolean_t useedns; + dns_compress_t cctx; + isc_boolean_t cleanup_cctx = ISC_FALSE; + isc_boolean_t secure_domain; + + fctx = query->fctx; + QTRACE("send"); + + res = fctx->res; + task = res->buckets[fctx->bucketnum].task; + address = NULL; + + if ((query->options & DNS_FETCHOPT_TCP) != 0) { + /* + * Reserve space for the TCP message length. + */ + isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data)); + isc_buffer_init(&query->buffer, query->data + 2, + sizeof(query->data) - 2); + buffer = &tcpbuffer; + } else { + isc_buffer_init(&query->buffer, query->data, + sizeof(query->data)); + buffer = &query->buffer; + } + + result = dns_message_gettempname(fctx->qmessage, &qname); + if (result != ISC_R_SUCCESS) + goto cleanup_temps; + result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset); + if (result != ISC_R_SUCCESS) + goto cleanup_temps; + + /* + * Get a query id from the dispatch. + */ + result = dns_dispatch_addresponse(query->dispatch, + &query->addrinfo->sockaddr, + task, + resquery_response, + query, + &query->id, + &query->dispentry); + if (result != ISC_R_SUCCESS) + goto cleanup_temps; + + fctx->qmessage->opcode = dns_opcode_query; + + /* + * Set up question. + */ + dns_name_init(qname, NULL); + dns_name_clone(&fctx->name, qname); + dns_rdataset_init(qrdataset); + dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type); + ISC_LIST_APPEND(qname->list, qrdataset, link); + dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION); + qname = NULL; + qrdataset = NULL; + + /* + * Set RD if the client has requested that we do a recursive query, + * or if we're sending to a forwarder. + */ + if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 || + ISFORWARDER(query->addrinfo)) + fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD; + + /* + * Set CD if the client says don't validate or the question is + * under a secure entry point. + */ + if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) { + fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; + } else if (res->view->enablevalidation) { + result = dns_keytable_issecuredomain(res->view->secroots, + &fctx->name, + &secure_domain); + if (result != ISC_R_SUCCESS) + secure_domain = ISC_FALSE; + if (res->view->dlv != NULL) + secure_domain = ISC_TRUE; + if (secure_domain) + fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; + } + + /* + * We don't have to set opcode because it defaults to query. + */ + fctx->qmessage->id = query->id; + + /* + * Convert the question to wire format. + */ + result = dns_compress_init(&cctx, -1, fctx->res->mctx); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + cleanup_cctx = ISC_TRUE; + + result = dns_message_renderbegin(fctx->qmessage, &cctx, + &query->buffer); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + + result = dns_message_rendersection(fctx->qmessage, + DNS_SECTION_QUESTION, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + + peer = NULL; + isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr); + (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer); + + /* + * The ADB does not know about servers with "edns no". Check this, + * and then inform the ADB for future use. + */ + if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 && + peer != NULL && + dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS && + !useedns) + { + query->options |= DNS_FETCHOPT_NOEDNS0; + dns_adb_changeflags(fctx->adb, + query->addrinfo, + DNS_FETCHOPT_NOEDNS0, + DNS_FETCHOPT_NOEDNS0); + } + + /* + * Use EDNS0, unless the caller doesn't want it, or we know that + * the remote server doesn't like it. + */ + + if ((triededns512(fctx, &query->addrinfo->sockaddr) || + fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) && + (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + query->options |= DNS_FETCHOPT_NOEDNS0; + log_edns(fctx); + } else if ((triededns(fctx, &query->addrinfo->sockaddr) || + fctx->timeouts >= MAX_EDNS0_TIMEOUTS) && + (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + query->options |= DNS_FETCHOPT_EDNS512; + FCTXTRACE("too many timeouts, setting EDNS size to 512"); + } + + if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) { + unsigned int version = 0; /* Default version. */ + unsigned int flags; + isc_uint16_t udpsize = res->udpsize; + + flags = query->addrinfo->flags; + if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) { + version = flags & DNS_FETCHOPT_EDNSVERSIONMASK; + version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT; + } + if ((query->options & DNS_FETCHOPT_EDNS512) != 0) + udpsize = 512; + else if (peer != NULL) + (void)dns_peer_getudpsize(peer, &udpsize); + result = fctx_addopt(fctx->qmessage, version, udpsize); + if (result != ISC_R_SUCCESS) { + /* + * We couldn't add the OPT, but we'll press on. + * We're not using EDNS0, so set the NOEDNS0 + * bit. + */ + query->options |= DNS_FETCHOPT_NOEDNS0; + } + } else { + /* + * We know this server doesn't like EDNS0, so we + * won't use it. Set the NOEDNS0 bit since we're + * not using EDNS0. + */ + query->options |= DNS_FETCHOPT_NOEDNS0; + } + } + + /* + * If we need EDNS0 to do this query and aren't using it, we lose. + */ + if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) { + result = DNS_R_SERVFAIL; + goto cleanup_message; + } + + if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) + add_triededns(fctx, &query->addrinfo->sockaddr); + + if ((query->options & DNS_FETCHOPT_EDNS512) != 0) + add_triededns512(fctx, &query->addrinfo->sockaddr); + + /* + * Clear CD if EDNS is not in use. + */ + if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0) + fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD; + + /* + * Add TSIG record tailored to the current recipient. + */ + result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto cleanup_message; + + if (tsigkey != NULL) { + result = dns_message_settsigkey(fctx->qmessage, tsigkey); + dns_tsigkey_detach(&tsigkey); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + } + + result = dns_message_rendersection(fctx->qmessage, + DNS_SECTION_ADDITIONAL, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + + result = dns_message_renderend(fctx->qmessage); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + + dns_compress_invalidate(&cctx); + cleanup_cctx = ISC_FALSE; + + if (dns_message_gettsigkey(fctx->qmessage) != NULL) { + dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage), + &query->tsigkey); + result = dns_message_getquerytsig(fctx->qmessage, + fctx->res->mctx, + &query->tsig); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + } + + /* + * If using TCP, write the length of the message at the beginning + * of the buffer. + */ + if ((query->options & DNS_FETCHOPT_TCP) != 0) { + isc_buffer_usedregion(&query->buffer, &r); + isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length); + isc_buffer_add(&tcpbuffer, r.length); + } + + /* + * We're now done with the query message. + */ + dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); + + socket = dns_dispatch_getsocket(query->dispatch); + /* + * Send the query! + */ + if ((query->options & DNS_FETCHOPT_TCP) == 0) + address = &query->addrinfo->sockaddr; + isc_buffer_usedregion(buffer, &r); + + /* + * XXXRTH Make sure we don't send to ourselves! We should probably + * prune out these addresses when we get them from the ADB. + */ + result = isc_socket_sendto(socket, &r, task, resquery_senddone, + query, address, NULL); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + +#ifdef LRU_DEBUG + res->extqueries++; + switch (fctx->type) { + case dns_rdatatype_ns: + res->extqueries_ns++; + break; + case dns_rdatatype_soa: + res->extqueries_soa++; + break; + case dns_rdatatype_a: + res->extqueries_a++; + break; + case dns_rdatatype_aaaa: + res->extqueries_aaaa++; + break; + } +#endif + query->sends++; + + QTRACE("sent"); + + return (ISC_R_SUCCESS); cleanup_message: - if (cleanup_cctx) - dns_compress_invalidate(&cctx); + if (cleanup_cctx) + dns_compress_invalidate(&cctx); - dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); + dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); - /* - * Stop the dispatcher from listening. - */ - dns_dispatch_removeresponse(&query->dispentry, NULL); + /* + * Stop the dispatcher from listening. + */ + dns_dispatch_removeresponse(&query->dispentry, NULL); cleanup_temps: - if (qname != NULL) - dns_message_puttempname(fctx->qmessage, &qname); - if (qrdataset != NULL) - dns_message_puttemprdataset(fctx->qmessage, &qrdataset); + if (qname != NULL) + dns_message_puttempname(fctx->qmessage, &qname); + if (qrdataset != NULL) + dns_message_puttemprdataset(fctx->qmessage, &qrdataset); - return (result); + return (result); } static void resquery_connected(isc_task_t *task, isc_event_t *event) { - isc_socketevent_t *sevent = (isc_socketevent_t *)event; - resquery_t *query = event->ev_arg; - isc_boolean_t retry = ISC_FALSE; - isc_result_t result; - unsigned int attrs; - fetchctx_t *fctx; - - REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); - REQUIRE(VALID_QUERY(query)); - - QTRACE("connected"); - - UNUSED(task); - - /* - * XXXRTH - * - * Currently we don't wait for the connect event before retrying - * a query. This means that if we get really behind, we may end - * up doing extra work! - */ - - query->connects--; - fctx = query->fctx; - - if (RESQUERY_CANCELED(query)) { - /* - * This query was canceled while the connect() was in - * progress. - */ - isc_socket_detach(&query->tcpsocket); - resquery_destroy(&query); - } else { - switch (sevent->result) { - case ISC_R_SUCCESS: - /* - * We are connected. Create a dispatcher and - * send the query. - */ - attrs = 0; - attrs |= DNS_DISPATCHATTR_TCP; - attrs |= DNS_DISPATCHATTR_PRIVATE; - attrs |= DNS_DISPATCHATTR_CONNECTED; - if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == - AF_INET) - attrs |= DNS_DISPATCHATTR_IPV4; - else - attrs |= DNS_DISPATCHATTR_IPV6; - attrs |= DNS_DISPATCHATTR_MAKEQUERY; - - result = dns_dispatch_createtcp(query->dispatchmgr, - query->tcpsocket, - query->fctx->res->taskmgr, - 4096, 2, 1, 1, 3, attrs, - &query->dispatch); - - /* - * Regardless of whether dns_dispatch_create() - * succeeded or not, we don't need our reference - * to the socket anymore. - */ - isc_socket_detach(&query->tcpsocket); - - if (result == ISC_R_SUCCESS) - result = resquery_send(query); - - if (result != ISC_R_SUCCESS) { - fctx_cancelquery(&query, NULL, NULL, - ISC_FALSE); - fctx_done(fctx, result); - } - break; - - case ISC_R_NETUNREACH: - case ISC_R_HOSTUNREACH: - case ISC_R_CONNREFUSED: - case ISC_R_NOPERM: - case ISC_R_ADDRNOTAVAIL: - case ISC_R_CONNECTIONRESET: - /* - * No route to remote. - */ - isc_socket_detach(&query->tcpsocket); - fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); - retry = ISC_TRUE; - break; - - default: - isc_socket_detach(&query->tcpsocket); - fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); - break; - } - } - - isc_event_free(&event); - - if (retry) { - /* - * Behave as if the idle timer has expired. For TCP - * connections this may not actually reflect the latest timer. - */ - fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - result = fctx_stopidletimer(fctx); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - else - fctx_try(fctx); - } + isc_socketevent_t *sevent = (isc_socketevent_t *)event; + resquery_t *query = event->ev_arg; + isc_boolean_t retry = ISC_FALSE; + isc_result_t result; + unsigned int attrs; + fetchctx_t *fctx; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); + REQUIRE(VALID_QUERY(query)); + + QTRACE("connected"); + + UNUSED(task); + + /* + * XXXRTH + * + * Currently we don't wait for the connect event before retrying + * a query. This means that if we get really behind, we may end + * up doing extra work! + */ + + query->connects--; + fctx = query->fctx; + + if (RESQUERY_CANCELED(query)) { + /* + * This query was canceled while the connect() was in + * progress. + */ + isc_socket_detach(&query->tcpsocket); + resquery_destroy(&query); + } else { + switch (sevent->result) { + case ISC_R_SUCCESS: + /* + * We are connected. Create a dispatcher and + * send the query. + */ + attrs = 0; + attrs |= DNS_DISPATCHATTR_TCP; + attrs |= DNS_DISPATCHATTR_PRIVATE; + attrs |= DNS_DISPATCHATTR_CONNECTED; + if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == + AF_INET) + attrs |= DNS_DISPATCHATTR_IPV4; + else + attrs |= DNS_DISPATCHATTR_IPV6; + attrs |= DNS_DISPATCHATTR_MAKEQUERY; + + result = dns_dispatch_createtcp(query->dispatchmgr, + query->tcpsocket, + query->fctx->res->taskmgr, + 4096, 2, 1, 1, 3, attrs, + &query->dispatch); + + /* + * Regardless of whether dns_dispatch_create() + * succeeded or not, we don't need our reference + * to the socket anymore. + */ + isc_socket_detach(&query->tcpsocket); + + if (result == ISC_R_SUCCESS) + result = resquery_send(query); + + if (result != ISC_R_SUCCESS) { + fctx_cancelquery(&query, NULL, NULL, + ISC_FALSE); + fctx_done(fctx, result); + } + break; + + case ISC_R_NETUNREACH: + case ISC_R_HOSTUNREACH: + case ISC_R_CONNREFUSED: + case ISC_R_NOPERM: + case ISC_R_ADDRNOTAVAIL: + case ISC_R_CONNECTIONRESET: + /* + * No route to remote. + */ + isc_socket_detach(&query->tcpsocket); + fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); + retry = ISC_TRUE; + break; + + default: + isc_socket_detach(&query->tcpsocket); + fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); + break; + } + } + + isc_event_free(&event); + + if (retry) { + /* + * Behave as if the idle timer has expired. For TCP + * connections this may not actually reflect the latest timer. + */ + fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; + result = fctx_stopidletimer(fctx); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + else + fctx_try(fctx); + } } static void fctx_finddone(isc_task_t *task, isc_event_t *event) { - fetchctx_t *fctx; - dns_adbfind_t *find; - dns_resolver_t *res; - isc_boolean_t want_try = ISC_FALSE; - isc_boolean_t want_done = ISC_FALSE; - isc_boolean_t bucket_empty = ISC_FALSE; - unsigned int bucketnum; - - find = event->ev_sender; - fctx = event->ev_arg; - REQUIRE(VALID_FCTX(fctx)); - res = fctx->res; - - UNUSED(task); - - FCTXTRACE("finddone"); - - INSIST(fctx->pending > 0); - fctx->pending--; - - if (ADDRWAIT(fctx)) { - /* - * The fetch is waiting for a name to be found. - */ - INSIST(!SHUTTINGDOWN(fctx)); - fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) - want_try = ISC_TRUE; - else if (fctx->pending == 0) { - /* - * We've got nothing else to wait for and don't - * know the answer. There's nothing to do but - * fail the fctx. - */ - want_done = ISC_TRUE; - } - } else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 && - fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) { - bucketnum = fctx->bucketnum; - LOCK(&res->buckets[bucketnum].lock); - /* - * Note that we had to wait until we had the lock before - * looking at fctx->references. - */ - if (fctx->references == 0) - bucket_empty = fctx_destroy(fctx); - UNLOCK(&res->buckets[bucketnum].lock); - } - - isc_event_free(&event); - dns_adb_destroyfind(&find); - - if (want_try) - fctx_try(fctx); - else if (want_done) - fctx_done(fctx, ISC_R_FAILURE); - else if (bucket_empty) - empty_bucket(res); + fetchctx_t *fctx; + dns_adbfind_t *find; + dns_resolver_t *res; + isc_boolean_t want_try = ISC_FALSE; + isc_boolean_t want_done = ISC_FALSE; + isc_boolean_t bucket_empty = ISC_FALSE; + unsigned int bucketnum; + + find = event->ev_sender; + fctx = event->ev_arg; + REQUIRE(VALID_FCTX(fctx)); + res = fctx->res; + + UNUSED(task); + + FCTXTRACE("finddone"); + + INSIST(fctx->pending > 0); + fctx->pending--; + + if (ADDRWAIT(fctx)) { + /* + * The fetch is waiting for a name to be found. + */ + INSIST(!SHUTTINGDOWN(fctx)); + fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; + if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) + want_try = ISC_TRUE; + else if (fctx->pending == 0) { + /* + * We've got nothing else to wait for and don't + * know the answer. There's nothing to do but + * fail the fctx. + */ + want_done = ISC_TRUE; + } + } else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 && + fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) { + bucketnum = fctx->bucketnum; + LOCK(&res->buckets[bucketnum].lock); + /* + * Note that we had to wait until we had the lock before + * looking at fctx->references. + */ + if (fctx->references == 0) + bucket_empty = fctx_destroy(fctx); + UNLOCK(&res->buckets[bucketnum].lock); + } + + isc_event_free(&event); + dns_adb_destroyfind(&find); + + if (want_try) + fctx_try(fctx); + else if (want_done) + fctx_done(fctx, ISC_R_FAILURE); + else if (bucket_empty) + empty_bucket(res); } static inline isc_boolean_t bad_server(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + isc_sockaddr_t *sa; - for (sa = ISC_LIST_HEAD(fctx->bad); - sa != NULL; - sa = ISC_LIST_NEXT(sa, link)) { - if (isc_sockaddr_equal(sa, address)) - return (ISC_TRUE); - } + for (sa = ISC_LIST_HEAD(fctx->bad); + sa != NULL; + sa = ISC_LIST_NEXT(sa, link)) { + if (isc_sockaddr_equal(sa, address)) + return (ISC_TRUE); + } - return (ISC_FALSE); + return (ISC_FALSE); } static inline isc_boolean_t mark_bad(fetchctx_t *fctx) { - dns_adbfind_t *curr; - dns_adbaddrinfo_t *addrinfo; - isc_boolean_t all_bad = ISC_TRUE; - - /* - * Mark all known bad servers, so we don't try to talk to them - * again. - */ - - /* - * Mark any bad nameservers. - */ - for (curr = ISC_LIST_HEAD(fctx->finds); - curr != NULL; - curr = ISC_LIST_NEXT(curr, publink)) { - for (addrinfo = ISC_LIST_HEAD(curr->list); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (bad_server(fctx, &addrinfo->sockaddr)) - addrinfo->flags |= FCTX_ADDRINFO_MARK; - else - all_bad = ISC_FALSE; - } - } - - /* - * Mark any bad forwarders. - */ - for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (bad_server(fctx, &addrinfo->sockaddr)) - addrinfo->flags |= FCTX_ADDRINFO_MARK; - else - all_bad = ISC_FALSE; - } - - /* - * Mark any bad alternates. - */ - for (curr = ISC_LIST_HEAD(fctx->altfinds); - curr != NULL; - curr = ISC_LIST_NEXT(curr, publink)) { - for (addrinfo = ISC_LIST_HEAD(curr->list); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (bad_server(fctx, &addrinfo->sockaddr)) - addrinfo->flags |= FCTX_ADDRINFO_MARK; - else - all_bad = ISC_FALSE; - } - } - - for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (bad_server(fctx, &addrinfo->sockaddr)) - addrinfo->flags |= FCTX_ADDRINFO_MARK; - else - all_bad = ISC_FALSE; - } - - return (all_bad); + dns_adbfind_t *curr; + dns_adbaddrinfo_t *addrinfo; + isc_boolean_t all_bad = ISC_TRUE; + + /* + * Mark all known bad servers, so we don't try to talk to them + * again. + */ + + /* + * Mark any bad nameservers. + */ + for (curr = ISC_LIST_HEAD(fctx->finds); + curr != NULL; + curr = ISC_LIST_NEXT(curr, publink)) { + for (addrinfo = ISC_LIST_HEAD(curr->list); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (bad_server(fctx, &addrinfo->sockaddr)) + addrinfo->flags |= FCTX_ADDRINFO_MARK; + else + all_bad = ISC_FALSE; + } + } + + /* + * Mark any bad forwarders. + */ + for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (bad_server(fctx, &addrinfo->sockaddr)) + addrinfo->flags |= FCTX_ADDRINFO_MARK; + else + all_bad = ISC_FALSE; + } + + /* + * Mark any bad alternates. + */ + for (curr = ISC_LIST_HEAD(fctx->altfinds); + curr != NULL; + curr = ISC_LIST_NEXT(curr, publink)) { + for (addrinfo = ISC_LIST_HEAD(curr->list); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (bad_server(fctx, &addrinfo->sockaddr)) + addrinfo->flags |= FCTX_ADDRINFO_MARK; + else + all_bad = ISC_FALSE; + } + } + + for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (bad_server(fctx, &addrinfo->sockaddr)) + addrinfo->flags |= FCTX_ADDRINFO_MARK; + else + all_bad = ISC_FALSE; + } + + return (all_bad); } static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason) { - char namebuf[DNS_NAME_FORMATSIZE]; - char addrbuf[ISC_SOCKADDR_FORMATSIZE]; - char classbuf[64]; - char typebuf[64]; - char code[64]; - isc_buffer_t b; - isc_sockaddr_t *sa; - const char *sep1, *sep2; - isc_sockaddr_t *address = &addrinfo->sockaddr; - - if (bad_server(fctx, address)) { - /* - * We already know this server is bad. - */ - return; - } - - FCTXTRACE("add_bad"); - - sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, - sizeof(*sa)); - if (sa == NULL) - return; - *sa = *address; - ISC_LIST_INITANDAPPEND(fctx->bad, sa, link); - - if (reason == DNS_R_LAME) /* already logged */ - return; - - if (reason == DNS_R_UNEXPECTEDRCODE && - fctx->rmessage->opcode == dns_rcode_servfail && - ISFORWARDER(addrinfo)) - return; - - if (reason == DNS_R_UNEXPECTEDRCODE) { - isc_buffer_init(&b, code, sizeof(code) - 1); - dns_rcode_totext(fctx->rmessage->rcode, &b); - code[isc_buffer_usedlength(&b)] = '\0'; - sep1 = "("; - sep2 = ") "; - } else if (reason == DNS_R_UNEXPECTEDOPCODE) { - isc_buffer_init(&b, code, sizeof(code) - 1); - dns_opcode_totext((dns_opcode_t)fctx->rmessage->opcode, &b); - code[isc_buffer_usedlength(&b)] = '\0'; - sep1 = "("; - sep2 = ") "; - } else { - code[0] = '\0'; - sep1 = ""; - sep2 = ""; - } - dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); - dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); - dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf)); - isc_sockaddr_format(address, addrbuf, sizeof(addrbuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, - DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, - "%s %s%s%sresolving '%s/%s/%s': %s", - dns_result_totext(reason), sep1, code, sep2, - namebuf, typebuf, classbuf, addrbuf); + char namebuf[DNS_NAME_FORMATSIZE]; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + char classbuf[64]; + char typebuf[64]; + char code[64]; + isc_buffer_t b; + isc_sockaddr_t *sa; + const char *sep1, *sep2; + isc_sockaddr_t *address = &addrinfo->sockaddr; + + if (bad_server(fctx, address)) { + /* + * We already know this server is bad. + */ + return; + } + + FCTXTRACE("add_bad"); + + sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx, + sizeof(*sa)); + if (sa == NULL) + return; + *sa = *address; + ISC_LIST_INITANDAPPEND(fctx->bad, sa, link); + + if (reason == DNS_R_LAME) /* already logged */ + return; + + if (reason == DNS_R_UNEXPECTEDRCODE && + fctx->rmessage->opcode == dns_rcode_servfail && + ISFORWARDER(addrinfo)) + return; + + if (reason == DNS_R_UNEXPECTEDRCODE) { + isc_buffer_init(&b, code, sizeof(code) - 1); + dns_rcode_totext(fctx->rmessage->rcode, &b); + code[isc_buffer_usedlength(&b)] = '\0'; + sep1 = "("; + sep2 = ") "; + } else if (reason == DNS_R_UNEXPECTEDOPCODE) { + isc_buffer_init(&b, code, sizeof(code) - 1); + dns_opcode_totext((dns_opcode_t)fctx->rmessage->opcode, &b); + code[isc_buffer_usedlength(&b)] = '\0'; + sep1 = "("; + sep2 = ") "; + } else { + code[0] = '\0'; + sep1 = ""; + sep2 = ""; + } + dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); + dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); + dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf)); + isc_sockaddr_format(address, addrbuf, sizeof(addrbuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, + DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, + "%s %s%s%sresolving '%s/%s/%s': %s", + dns_result_totext(reason), sep1, code, sep2, + namebuf, typebuf, classbuf, addrbuf); } static void sort_adbfind(dns_adbfind_t *find) { - dns_adbaddrinfo_t *best, *curr; - dns_adbaddrinfolist_t sorted; - - /* - * Lame N^2 bubble sort. - */ - - ISC_LIST_INIT(sorted); - while (!ISC_LIST_EMPTY(find->list)) { - best = ISC_LIST_HEAD(find->list); - curr = ISC_LIST_NEXT(best, publink); - while (curr != NULL) { - if (curr->srtt < best->srtt) - best = curr; - curr = ISC_LIST_NEXT(curr, publink); - } - ISC_LIST_UNLINK(find->list, best, publink); - ISC_LIST_APPEND(sorted, best, publink); - } - find->list = sorted; + dns_adbaddrinfo_t *best, *curr; + dns_adbaddrinfolist_t sorted; + + /* + * Lame N^2 bubble sort. + */ + + ISC_LIST_INIT(sorted); + while (!ISC_LIST_EMPTY(find->list)) { + best = ISC_LIST_HEAD(find->list); + curr = ISC_LIST_NEXT(best, publink); + while (curr != NULL) { + if (curr->srtt < best->srtt) + best = curr; + curr = ISC_LIST_NEXT(curr, publink); + } + ISC_LIST_UNLINK(find->list, best, publink); + ISC_LIST_APPEND(sorted, best, publink); + } + find->list = sorted; } static void sort_finds(fetchctx_t *fctx) { - dns_adbfind_t *best, *curr; - dns_adbfindlist_t sorted; - dns_adbaddrinfo_t *addrinfo, *bestaddrinfo; - - /* - * Lame N^2 bubble sort. - */ - - ISC_LIST_INIT(sorted); - while (!ISC_LIST_EMPTY(fctx->finds)) { - best = ISC_LIST_HEAD(fctx->finds); - bestaddrinfo = ISC_LIST_HEAD(best->list); - INSIST(bestaddrinfo != NULL); - curr = ISC_LIST_NEXT(best, publink); - while (curr != NULL) { - addrinfo = ISC_LIST_HEAD(curr->list); - INSIST(addrinfo != NULL); - if (addrinfo->srtt < bestaddrinfo->srtt) { - best = curr; - bestaddrinfo = addrinfo; - } - curr = ISC_LIST_NEXT(curr, publink); - } - ISC_LIST_UNLINK(fctx->finds, best, publink); - ISC_LIST_APPEND(sorted, best, publink); - } - fctx->finds = sorted; - - ISC_LIST_INIT(sorted); - while (!ISC_LIST_EMPTY(fctx->altfinds)) { - best = ISC_LIST_HEAD(fctx->altfinds); - bestaddrinfo = ISC_LIST_HEAD(best->list); - INSIST(bestaddrinfo != NULL); - curr = ISC_LIST_NEXT(best, publink); - while (curr != NULL) { - addrinfo = ISC_LIST_HEAD(curr->list); - INSIST(addrinfo != NULL); - if (addrinfo->srtt < bestaddrinfo->srtt) { - best = curr; - bestaddrinfo = addrinfo; - } - curr = ISC_LIST_NEXT(curr, publink); - } - ISC_LIST_UNLINK(fctx->altfinds, best, publink); - ISC_LIST_APPEND(sorted, best, publink); - } - fctx->altfinds = sorted; + dns_adbfind_t *best, *curr; + dns_adbfindlist_t sorted; + dns_adbaddrinfo_t *addrinfo, *bestaddrinfo; + + /* + * Lame N^2 bubble sort. + */ + + ISC_LIST_INIT(sorted); + while (!ISC_LIST_EMPTY(fctx->finds)) { + best = ISC_LIST_HEAD(fctx->finds); + bestaddrinfo = ISC_LIST_HEAD(best->list); + INSIST(bestaddrinfo != NULL); + curr = ISC_LIST_NEXT(best, publink); + while (curr != NULL) { + addrinfo = ISC_LIST_HEAD(curr->list); + INSIST(addrinfo != NULL); + if (addrinfo->srtt < bestaddrinfo->srtt) { + best = curr; + bestaddrinfo = addrinfo; + } + curr = ISC_LIST_NEXT(curr, publink); + } + ISC_LIST_UNLINK(fctx->finds, best, publink); + ISC_LIST_APPEND(sorted, best, publink); + } + fctx->finds = sorted; + + ISC_LIST_INIT(sorted); + while (!ISC_LIST_EMPTY(fctx->altfinds)) { + best = ISC_LIST_HEAD(fctx->altfinds); + bestaddrinfo = ISC_LIST_HEAD(best->list); + INSIST(bestaddrinfo != NULL); + curr = ISC_LIST_NEXT(best, publink); + while (curr != NULL) { + addrinfo = ISC_LIST_HEAD(curr->list); + INSIST(addrinfo != NULL); + if (addrinfo->srtt < bestaddrinfo->srtt) { + best = curr; + bestaddrinfo = addrinfo; + } + curr = ISC_LIST_NEXT(curr, publink); + } + ISC_LIST_UNLINK(fctx->altfinds, best, publink); + ISC_LIST_APPEND(sorted, best, publink); + } + fctx->altfinds = sorted; } static void findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, - unsigned int options, unsigned int flags, isc_stdtime_t now, - isc_boolean_t *need_alternate) + unsigned int options, unsigned int flags, isc_stdtime_t now, + isc_boolean_t *need_alternate) { - dns_adbaddrinfo_t *ai; - dns_adbfind_t *find; - dns_resolver_t *res; - isc_boolean_t unshared; - isc_result_t result; - - res = fctx->res; - unshared = ISC_TF((fctx->options | DNS_FETCHOPT_UNSHARED) != 0); - /* - * If this name is a subdomain of the query domain, tell - * the ADB to start looking using zone/hint data. This keeps us - * from getting stuck if the nameserver is beneath the zone cut - * and we don't know its address (e.g. because the A record has - * expired). - */ - if (dns_name_issubdomain(name, &fctx->domain)) - options |= DNS_ADBFIND_STARTATZONE; - options |= DNS_ADBFIND_GLUEOK; - options |= DNS_ADBFIND_HINTOK; - - /* - * See what we know about this address. - */ - find = NULL; - result = dns_adb_createfind(fctx->adb, - res->buckets[fctx->bucketnum].task, - fctx_finddone, fctx, name, - &fctx->name, fctx->type, - options, now, NULL, - res->view->dstport, &find); - if (result != ISC_R_SUCCESS) { - if (result == DNS_R_ALIAS) { - /* - * XXXRTH Follow the CNAME/DNAME chain? - */ - dns_adb_destroyfind(&find); - } - } else if (!ISC_LIST_EMPTY(find->list)) { - /* - * We have at least some of the addresses for the - * name. - */ - INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0); - sort_adbfind(find); - if (flags != 0 || port != 0) { - for (ai = ISC_LIST_HEAD(find->list); - ai != NULL; - ai = ISC_LIST_NEXT(ai, publink)) { - ai->flags |= flags; - if (port != 0) - isc_sockaddr_setport(&ai->sockaddr, - port); - } - } - if ((flags & FCTX_ADDRINFO_FORWARDER) != 0) - ISC_LIST_APPEND(fctx->altfinds, find, publink); - else - ISC_LIST_APPEND(fctx->finds, find, publink); - } else { - /* - * We don't know any of the addresses for this - * name. - */ - if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) { - /* - * We're looking for them and will get an - * event about it later. - */ - fctx->pending++; - /* - * Bootstrap. - */ - if (need_alternate != NULL && - !*need_alternate && unshared && - ((res->dispatchv4 == NULL && - find->result_v6 != DNS_R_NXDOMAIN) || - (res->dispatchv6 == NULL && - find->result_v4 != DNS_R_NXDOMAIN))) - *need_alternate = ISC_TRUE; - } else { - /* - * If we know there are no addresses for - * the family we are using then try to add - * an alternative server. - */ - if (need_alternate != NULL && !*need_alternate && - ((res->dispatchv4 == NULL && - find->result_v6 == DNS_R_NXRRSET) || - (res->dispatchv6 == NULL && - find->result_v4 == DNS_R_NXRRSET))) - *need_alternate = ISC_TRUE; - dns_adb_destroyfind(&find); - } - } + dns_adbaddrinfo_t *ai; + dns_adbfind_t *find; + dns_resolver_t *res; + isc_boolean_t unshared; + isc_result_t result; + + res = fctx->res; + unshared = ISC_TF((fctx->options | DNS_FETCHOPT_UNSHARED) != 0); + /* + * If this name is a subdomain of the query domain, tell + * the ADB to start looking using zone/hint data. This keeps us + * from getting stuck if the nameserver is beneath the zone cut + * and we don't know its address (e.g. because the A record has + * expired). + */ + if (dns_name_issubdomain(name, &fctx->domain)) + options |= DNS_ADBFIND_STARTATZONE; + options |= DNS_ADBFIND_GLUEOK; + options |= DNS_ADBFIND_HINTOK; + + /* + * See what we know about this address. + */ + find = NULL; + result = dns_adb_createfind(fctx->adb, + res->buckets[fctx->bucketnum].task, + fctx_finddone, fctx, name, + &fctx->name, fctx->type, + options, now, NULL, + res->view->dstport, &find); + if (result != ISC_R_SUCCESS) { + if (result == DNS_R_ALIAS) { + /* + * XXXRTH Follow the CNAME/DNAME chain? + */ + dns_adb_destroyfind(&find); + } + } else if (!ISC_LIST_EMPTY(find->list)) { + /* + * We have at least some of the addresses for the + * name. + */ + INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0); + sort_adbfind(find); + if (flags != 0 || port != 0) { + for (ai = ISC_LIST_HEAD(find->list); + ai != NULL; + ai = ISC_LIST_NEXT(ai, publink)) { + ai->flags |= flags; + if (port != 0) + isc_sockaddr_setport(&ai->sockaddr, + port); + } + } + if ((flags & FCTX_ADDRINFO_FORWARDER) != 0) + ISC_LIST_APPEND(fctx->altfinds, find, publink); + else + ISC_LIST_APPEND(fctx->finds, find, publink); + } else { + /* + * We don't know any of the addresses for this + * name. + */ + if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) { + /* + * We're looking for them and will get an + * event about it later. + */ + fctx->pending++; + /* + * Bootstrap. + */ + if (need_alternate != NULL && + !*need_alternate && unshared && + ((res->dispatchv4 == NULL && + find->result_v6 != DNS_R_NXDOMAIN) || + (res->dispatchv6 == NULL && + find->result_v4 != DNS_R_NXDOMAIN))) + *need_alternate = ISC_TRUE; + } else { + /* + * If we know there are no addresses for + * the family we are using then try to add + * an alternative server. + */ + if (need_alternate != NULL && !*need_alternate && + ((res->dispatchv4 == NULL && + find->result_v6 == DNS_R_NXRRSET) || + (res->dispatchv6 == NULL && + find->result_v4 == DNS_R_NXRRSET))) + *need_alternate = ISC_TRUE; + dns_adb_destroyfind(&find); + } + } } static isc_result_t fctx_getaddresses(fetchctx_t *fctx) { - dns_rdata_t rdata = DNS_RDATA_INIT; - isc_result_t result; - dns_resolver_t *res; - isc_stdtime_t now; - unsigned int stdoptions; - isc_sockaddr_t *sa; - dns_adbaddrinfo_t *ai; - isc_boolean_t all_bad; - dns_rdata_ns_t ns; - isc_boolean_t need_alternate = ISC_FALSE; - - FCTXTRACE("getaddresses"); - - /* - * Don't pound on remote servers. (Failsafe!) - */ - fctx->restarts++; - if (fctx->restarts > 10) { - FCTXTRACE("too many restarts"); - return (DNS_R_SERVFAIL); - } - - res = fctx->res; - stdoptions = 0; /* Keep compiler happy. */ - - /* - * Forwarders. - */ - - INSIST(ISC_LIST_EMPTY(fctx->forwaddrs)); - INSIST(ISC_LIST_EMPTY(fctx->altaddrs)); - - /* - * If this fctx has forwarders, use them; otherwise use any - * selective forwarders specified in the view; otherwise use the - * resolver's forwarders (if any). - */ - sa = ISC_LIST_HEAD(fctx->forwarders); - if (sa == NULL) { - dns_forwarders_t *forwarders = NULL; - dns_name_t *name = &fctx->name; - dns_name_t suffix; - unsigned int labels; - - /* - * DS records are found in the parent server. - * Strip label to get the correct forwarder (if any). - */ - if (fctx->type == dns_rdatatype_ds && - dns_name_countlabels(name) > 1) { - dns_name_init(&suffix, NULL); - labels = dns_name_countlabels(name); - dns_name_getlabelsequence(name, 1, labels - 1, &suffix); - name = &suffix; - } - result = dns_fwdtable_find(fctx->res->view->fwdtable, name, - &forwarders); - if (result == ISC_R_SUCCESS) { - sa = ISC_LIST_HEAD(forwarders->addrs); - fctx->fwdpolicy = forwarders->fwdpolicy; - } - } - - while (sa != NULL) { - ai = NULL; - result = dns_adb_findaddrinfo(fctx->adb, - sa, &ai, 0); /* XXXMLG */ - if (result == ISC_R_SUCCESS) { - dns_adbaddrinfo_t *cur; - ai->flags |= FCTX_ADDRINFO_FORWARDER; - cur = ISC_LIST_HEAD(fctx->forwaddrs); - while (cur != NULL && cur->srtt < ai->srtt) - cur = ISC_LIST_NEXT(cur, publink); - if (cur != NULL) - ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur, - ai, publink); - else - ISC_LIST_APPEND(fctx->forwaddrs, ai, publink); - } - sa = ISC_LIST_NEXT(sa, link); - } - - /* - * If the forwarding policy is "only", we don't need the addresses - * of the nameservers. - */ - if (fctx->fwdpolicy == dns_fwdpolicy_only) - goto out; - - /* - * Normal nameservers. - */ - - stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT; - if (fctx->restarts == 1) { - /* - * To avoid sending out a flood of queries likely to - * result in NXRRSET, we suppress fetches for address - * families we don't have the first time through, - * provided that we have addresses in some family we - * can use. - * - * We don't want to set this option all the time, since - * if fctx->restarts > 1, we've clearly been having trouble - * with the addresses we had, so getting more could help. - */ - stdoptions |= DNS_ADBFIND_AVOIDFETCHES; - } - if (res->dispatchv4 != NULL) - stdoptions |= DNS_ADBFIND_INET; - if (res->dispatchv6 != NULL) - stdoptions |= DNS_ADBFIND_INET6; - isc_stdtime_get(&now); - - INSIST(ISC_LIST_EMPTY(fctx->finds)); - INSIST(ISC_LIST_EMPTY(fctx->altfinds)); - - for (result = dns_rdataset_first(&fctx->nameservers); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&fctx->nameservers)) - { - dns_rdataset_current(&fctx->nameservers, &rdata); - /* - * Extract the name from the NS record. - */ - result = dns_rdata_tostruct(&rdata, &ns, NULL); - if (result != ISC_R_SUCCESS) - continue; - - findname(fctx, &ns.name, 0, stdoptions, 0, now, - &need_alternate); - dns_rdata_reset(&rdata); - dns_rdata_freestruct(&ns); - } - if (result != ISC_R_NOMORE) - return (result); - - /* - * Do we need to use 6 to 4? - */ - if (need_alternate) { - int family; - alternate_t *a; - family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET; - for (a = ISC_LIST_HEAD(fctx->res->alternates); - a != NULL; - a = ISC_LIST_NEXT(a, link)) { - if (!a->isaddress) { - findname(fctx, &a->_u._n.name, a->_u._n.port, - stdoptions, FCTX_ADDRINFO_FORWARDER, - now, NULL); - continue; - } - if (isc_sockaddr_pf(&a->_u.addr) != family) - continue; - ai = NULL; - result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr, - &ai, 0); - if (result == ISC_R_SUCCESS) { - dns_adbaddrinfo_t *cur; - ai->flags |= FCTX_ADDRINFO_FORWARDER; - cur = ISC_LIST_HEAD(fctx->altaddrs); - while (cur != NULL && cur->srtt < ai->srtt) - cur = ISC_LIST_NEXT(cur, publink); - if (cur != NULL) - ISC_LIST_INSERTBEFORE(fctx->altaddrs, - cur, ai, publink); - else - ISC_LIST_APPEND(fctx->altaddrs, ai, - publink); - } - } - } + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; + dns_resolver_t *res; + isc_stdtime_t now; + unsigned int stdoptions; + isc_sockaddr_t *sa; + dns_adbaddrinfo_t *ai; + isc_boolean_t all_bad; + dns_rdata_ns_t ns; + isc_boolean_t need_alternate = ISC_FALSE; + + FCTXTRACE("getaddresses"); + + /* + * Don't pound on remote servers. (Failsafe!) + */ + fctx->restarts++; + if (fctx->restarts > 10) { + FCTXTRACE("too many restarts"); + return (DNS_R_SERVFAIL); + } + + res = fctx->res; + stdoptions = 0; /* Keep compiler happy. */ + + /* + * Forwarders. + */ + + INSIST(ISC_LIST_EMPTY(fctx->forwaddrs)); + INSIST(ISC_LIST_EMPTY(fctx->altaddrs)); + + /* + * If this fctx has forwarders, use them; otherwise use any + * selective forwarders specified in the view; otherwise use the + * resolver's forwarders (if any). + */ + sa = ISC_LIST_HEAD(fctx->forwarders); + if (sa == NULL) { + dns_forwarders_t *forwarders = NULL; + dns_name_t *name = &fctx->name; + dns_name_t suffix; + unsigned int labels; + + /* + * DS records are found in the parent server. + * Strip label to get the correct forwarder (if any). + */ + if (fctx->type == dns_rdatatype_ds && + dns_name_countlabels(name) > 1) { + dns_name_init(&suffix, NULL); + labels = dns_name_countlabels(name); + dns_name_getlabelsequence(name, 1, labels - 1, &suffix); + name = &suffix; + } + result = dns_fwdtable_find(fctx->res->view->fwdtable, name, + &forwarders); + if (result == ISC_R_SUCCESS) { + sa = ISC_LIST_HEAD(forwarders->addrs); + fctx->fwdpolicy = forwarders->fwdpolicy; + } + } + + while (sa != NULL) { + ai = NULL; + result = dns_adb_findaddrinfo(fctx->adb, + sa, &ai, 0); /* XXXMLG */ + if (result == ISC_R_SUCCESS) { + dns_adbaddrinfo_t *cur; + ai->flags |= FCTX_ADDRINFO_FORWARDER; + cur = ISC_LIST_HEAD(fctx->forwaddrs); + while (cur != NULL && cur->srtt < ai->srtt) + cur = ISC_LIST_NEXT(cur, publink); + if (cur != NULL) + ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur, + ai, publink); + else + ISC_LIST_APPEND(fctx->forwaddrs, ai, publink); + } + sa = ISC_LIST_NEXT(sa, link); + } + + /* + * If the forwarding policy is "only", we don't need the addresses + * of the nameservers. + */ + if (fctx->fwdpolicy == dns_fwdpolicy_only) + goto out; + + /* + * Normal nameservers. + */ + + stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT; + if (fctx->restarts == 1) { + /* + * To avoid sending out a flood of queries likely to + * result in NXRRSET, we suppress fetches for address + * families we don't have the first time through, + * provided that we have addresses in some family we + * can use. + * + * We don't want to set this option all the time, since + * if fctx->restarts > 1, we've clearly been having trouble + * with the addresses we had, so getting more could help. + */ + stdoptions |= DNS_ADBFIND_AVOIDFETCHES; + } + if (res->dispatchv4 != NULL) + stdoptions |= DNS_ADBFIND_INET; + if (res->dispatchv6 != NULL) + stdoptions |= DNS_ADBFIND_INET6; + isc_stdtime_get(&now); + + INSIST(ISC_LIST_EMPTY(fctx->finds)); + INSIST(ISC_LIST_EMPTY(fctx->altfinds)); + + for (result = dns_rdataset_first(&fctx->nameservers); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&fctx->nameservers)) + { + dns_rdataset_current(&fctx->nameservers, &rdata); + /* + * Extract the name from the NS record. + */ + result = dns_rdata_tostruct(&rdata, &ns, NULL); + if (result != ISC_R_SUCCESS) + continue; + + findname(fctx, &ns.name, 0, stdoptions, 0, now, + &need_alternate); + dns_rdata_reset(&rdata); + dns_rdata_freestruct(&ns); + } + if (result != ISC_R_NOMORE) + return (result); + + /* + * Do we need to use 6 to 4? + */ + if (need_alternate) { + int family; + alternate_t *a; + family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET; + for (a = ISC_LIST_HEAD(fctx->res->alternates); + a != NULL; + a = ISC_LIST_NEXT(a, link)) { + if (!a->isaddress) { + findname(fctx, &a->_u._n.name, a->_u._n.port, + stdoptions, FCTX_ADDRINFO_FORWARDER, + now, NULL); + continue; + } + if (isc_sockaddr_pf(&a->_u.addr) != family) + continue; + ai = NULL; + result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr, + &ai, 0); + if (result == ISC_R_SUCCESS) { + dns_adbaddrinfo_t *cur; + ai->flags |= FCTX_ADDRINFO_FORWARDER; + cur = ISC_LIST_HEAD(fctx->altaddrs); + while (cur != NULL && cur->srtt < ai->srtt) + cur = ISC_LIST_NEXT(cur, publink); + if (cur != NULL) + ISC_LIST_INSERTBEFORE(fctx->altaddrs, + cur, ai, publink); + else + ISC_LIST_APPEND(fctx->altaddrs, ai, + publink); + } + } + } out: - /* - * Mark all known bad servers. - */ - all_bad = mark_bad(fctx); - - /* - * How are we doing? - */ - if (all_bad) { - /* - * We've got no addresses. - */ - if (fctx->pending > 0) { - /* - * We're fetching the addresses, but don't have any - * yet. Tell the caller to wait for an answer. - */ - result = DNS_R_WAIT; - } else { - /* - * We've lost completely. We don't know any - * addresses, and the ADB has told us it can't get - * them. - */ - FCTXTRACE("no addresses"); - result = ISC_R_FAILURE; - } - } else { - /* - * We've found some addresses. We might still be looking - * for more addresses. - */ - sort_finds(fctx); - result = ISC_R_SUCCESS; - } - - return (result); + /* + * Mark all known bad servers. + */ + all_bad = mark_bad(fctx); + + /* + * How are we doing? + */ + if (all_bad) { + /* + * We've got no addresses. + */ + if (fctx->pending > 0) { + /* + * We're fetching the addresses, but don't have any + * yet. Tell the caller to wait for an answer. + */ + result = DNS_R_WAIT; + } else { + /* + * We've lost completely. We don't know any + * addresses, and the ADB has told us it can't get + * them. + */ + FCTXTRACE("no addresses"); + result = ISC_R_FAILURE; + } + } else { + /* + * We've found some addresses. We might still be looking + * for more addresses. + */ + sort_finds(fctx); + result = ISC_R_SUCCESS; + } + + return (result); } static inline void possibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr) { - isc_netaddr_t na; - char buf[ISC_NETADDR_FORMATSIZE]; - isc_sockaddr_t *sa; - isc_boolean_t aborted = ISC_FALSE; - isc_boolean_t bogus; - dns_acl_t *blackhole; - isc_netaddr_t ipaddr; - dns_peer_t *peer = NULL; - dns_resolver_t *res; - const char *msg = NULL; - - sa = &addr->sockaddr; - - res = fctx->res; - isc_netaddr_fromsockaddr(&ipaddr, sa); - blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr); - (void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer); - - if (blackhole != NULL) { - int match; - - if (dns_acl_match(&ipaddr, NULL, blackhole, - &res->view->aclenv, - &match, NULL) == ISC_R_SUCCESS && - match > 0) - aborted = ISC_TRUE; - } - - if (peer != NULL && - dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS && - bogus) - aborted = ISC_TRUE; - - if (aborted) { - addr->flags |= FCTX_ADDRINFO_MARK; - msg = "ignoring blackholed / bogus server: "; - } else if (isc_sockaddr_ismulticast(sa)) { - addr->flags |= FCTX_ADDRINFO_MARK; - msg = "ignoring multicast address: "; - } else if (isc_sockaddr_isexperimental(sa)) { - addr->flags |= FCTX_ADDRINFO_MARK; - msg = "ignoring experimental address: "; - } else if (sa->type.sa.sa_family != AF_INET6) { - return; - } else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) { - addr->flags |= FCTX_ADDRINFO_MARK; - msg = "ignoring IPv6 mapped IPV4 address: "; - } else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) { - addr->flags |= FCTX_ADDRINFO_MARK; - msg = "ignoring IPv6 compatibility IPV4 address: "; - } else - return; - - if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) - return; - - isc_netaddr_fromsockaddr(&na, sa); - isc_netaddr_format(&na, buf, sizeof(buf)); - FCTXTRACE2(msg, buf); + isc_netaddr_t na; + char buf[ISC_NETADDR_FORMATSIZE]; + isc_sockaddr_t *sa; + isc_boolean_t aborted = ISC_FALSE; + isc_boolean_t bogus; + dns_acl_t *blackhole; + isc_netaddr_t ipaddr; + dns_peer_t *peer = NULL; + dns_resolver_t *res; + const char *msg = NULL; + + sa = &addr->sockaddr; + + res = fctx->res; + isc_netaddr_fromsockaddr(&ipaddr, sa); + blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr); + (void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer); + + if (blackhole != NULL) { + int match; + + if (dns_acl_match(&ipaddr, NULL, blackhole, + &res->view->aclenv, + &match, NULL) == ISC_R_SUCCESS && + match > 0) + aborted = ISC_TRUE; + } + + if (peer != NULL && + dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS && + bogus) + aborted = ISC_TRUE; + + if (aborted) { + addr->flags |= FCTX_ADDRINFO_MARK; + msg = "ignoring blackholed / bogus server: "; + } else if (isc_sockaddr_ismulticast(sa)) { + addr->flags |= FCTX_ADDRINFO_MARK; + msg = "ignoring multicast address: "; + } else if (isc_sockaddr_isexperimental(sa)) { + addr->flags |= FCTX_ADDRINFO_MARK; + msg = "ignoring experimental address: "; + } else if (sa->type.sa.sa_family != AF_INET6) { + return; + } else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) { + addr->flags |= FCTX_ADDRINFO_MARK; + msg = "ignoring IPv6 mapped IPV4 address: "; + } else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) { + addr->flags |= FCTX_ADDRINFO_MARK; + msg = "ignoring IPv6 compatibility IPV4 address: "; + } else + return; + + if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) + return; + + isc_netaddr_fromsockaddr(&na, sa); + isc_netaddr_format(&na, buf, sizeof(buf)); + FCTXTRACE2(msg, buf); } static inline dns_adbaddrinfo_t * fctx_nextaddress(fetchctx_t *fctx) { - dns_adbfind_t *find, *start; - dns_adbaddrinfo_t *addrinfo; - dns_adbaddrinfo_t *faddrinfo; - - /* - * Return the next untried address, if any. - */ - - /* - * Find the first unmarked forwarder (if any). - */ - for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (!UNMARKED(addrinfo)) - continue; - possibly_mark(fctx, addrinfo); - if (UNMARKED(addrinfo)) { - addrinfo->flags |= FCTX_ADDRINFO_MARK; - fctx->find = NULL; - return (addrinfo); - } - } - - /* - * No forwarders. Move to the next find. - */ - - fctx->attributes |= FCTX_ATTR_TRIEDFIND; - - find = fctx->find; - if (find == NULL) - find = ISC_LIST_HEAD(fctx->finds); - else { - find = ISC_LIST_NEXT(find, publink); - if (find == NULL) - find = ISC_LIST_HEAD(fctx->finds); - } - - /* - * Find the first unmarked addrinfo. - */ - addrinfo = NULL; - if (find != NULL) { - start = find; - do { - for (addrinfo = ISC_LIST_HEAD(find->list); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (!UNMARKED(addrinfo)) - continue; - possibly_mark(fctx, addrinfo); - if (UNMARKED(addrinfo)) { - addrinfo->flags |= FCTX_ADDRINFO_MARK; - break; - } - } - if (addrinfo != NULL) - break; - find = ISC_LIST_NEXT(find, publink); - if (find == NULL) - find = ISC_LIST_HEAD(fctx->finds); - } while (find != start); - } - - fctx->find = find; - if (addrinfo != NULL) - return (addrinfo); - - /* - * No nameservers left. Try alternates. - */ - - fctx->attributes |= FCTX_ATTR_TRIEDALT; - - find = fctx->altfind; - if (find == NULL) - find = ISC_LIST_HEAD(fctx->altfinds); - else { - find = ISC_LIST_NEXT(find, publink); - if (find == NULL) - find = ISC_LIST_HEAD(fctx->altfinds); - } - - /* - * Find the first unmarked addrinfo. - */ - addrinfo = NULL; - if (find != NULL) { - start = find; - do { - for (addrinfo = ISC_LIST_HEAD(find->list); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (!UNMARKED(addrinfo)) - continue; - possibly_mark(fctx, addrinfo); - if (UNMARKED(addrinfo)) { - addrinfo->flags |= FCTX_ADDRINFO_MARK; - break; - } - } - if (addrinfo != NULL) - break; - find = ISC_LIST_NEXT(find, publink); - if (find == NULL) - find = ISC_LIST_HEAD(fctx->altfinds); - } while (find != start); - } - - faddrinfo = addrinfo; - - /* - * See if we have a better alternate server by address. - */ - - for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { - if (!UNMARKED(addrinfo)) - continue; - possibly_mark(fctx, addrinfo); - if (UNMARKED(addrinfo) && - (faddrinfo == NULL || - addrinfo->srtt < faddrinfo->srtt)) { - if (faddrinfo != NULL) - faddrinfo->flags &= ~FCTX_ADDRINFO_MARK; - addrinfo->flags |= FCTX_ADDRINFO_MARK; - break; - } - } - - if (addrinfo == NULL) { - addrinfo = faddrinfo; - fctx->altfind = find; - } - - return (addrinfo); + dns_adbfind_t *find, *start; + dns_adbaddrinfo_t *addrinfo; + dns_adbaddrinfo_t *faddrinfo; + + /* + * Return the next untried address, if any. + */ + + /* + * Find the first unmarked forwarder (if any). + */ + for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (!UNMARKED(addrinfo)) + continue; + possibly_mark(fctx, addrinfo); + if (UNMARKED(addrinfo)) { + addrinfo->flags |= FCTX_ADDRINFO_MARK; + fctx->find = NULL; + return (addrinfo); + } + } + + /* + * No forwarders. Move to the next find. + */ + + fctx->attributes |= FCTX_ATTR_TRIEDFIND; + + find = fctx->find; + if (find == NULL) + find = ISC_LIST_HEAD(fctx->finds); + else { + find = ISC_LIST_NEXT(find, publink); + if (find == NULL) + find = ISC_LIST_HEAD(fctx->finds); + } + + /* + * Find the first unmarked addrinfo. + */ + addrinfo = NULL; + if (find != NULL) { + start = find; + do { + for (addrinfo = ISC_LIST_HEAD(find->list); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (!UNMARKED(addrinfo)) + continue; + possibly_mark(fctx, addrinfo); + if (UNMARKED(addrinfo)) { + addrinfo->flags |= FCTX_ADDRINFO_MARK; + break; + } + } + if (addrinfo != NULL) + break; + find = ISC_LIST_NEXT(find, publink); + if (find == NULL) + find = ISC_LIST_HEAD(fctx->finds); + } while (find != start); + } + + fctx->find = find; + if (addrinfo != NULL) + return (addrinfo); + + /* + * No nameservers left. Try alternates. + */ + + fctx->attributes |= FCTX_ATTR_TRIEDALT; + + find = fctx->altfind; + if (find == NULL) + find = ISC_LIST_HEAD(fctx->altfinds); + else { + find = ISC_LIST_NEXT(find, publink); + if (find == NULL) + find = ISC_LIST_HEAD(fctx->altfinds); + } + + /* + * Find the first unmarked addrinfo. + */ + addrinfo = NULL; + if (find != NULL) { + start = find; + do { + for (addrinfo = ISC_LIST_HEAD(find->list); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (!UNMARKED(addrinfo)) + continue; + possibly_mark(fctx, addrinfo); + if (UNMARKED(addrinfo)) { + addrinfo->flags |= FCTX_ADDRINFO_MARK; + break; + } + } + if (addrinfo != NULL) + break; + find = ISC_LIST_NEXT(find, publink); + if (find == NULL) + find = ISC_LIST_HEAD(fctx->altfinds); + } while (find != start); + } + + faddrinfo = addrinfo; + + /* + * See if we have a better alternate server by address. + */ + + for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); + addrinfo != NULL; + addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { + if (!UNMARKED(addrinfo)) + continue; + possibly_mark(fctx, addrinfo); + if (UNMARKED(addrinfo) && + (faddrinfo == NULL || + addrinfo->srtt < faddrinfo->srtt)) { + if (faddrinfo != NULL) + faddrinfo->flags &= ~FCTX_ADDRINFO_MARK; + addrinfo->flags |= FCTX_ADDRINFO_MARK; + break; + } + } + + if (addrinfo == NULL) { + addrinfo = faddrinfo; + fctx->altfind = find; + } + + return (addrinfo); } static void fctx_try(fetchctx_t *fctx) { - isc_result_t result; - dns_adbaddrinfo_t *addrinfo; - - FCTXTRACE("try"); - - REQUIRE(!ADDRWAIT(fctx)); - - addrinfo = fctx_nextaddress(fctx); - if (addrinfo == NULL) { - /* - * We have no more addresses. Start over. - */ - fctx_cancelqueries(fctx, ISC_TRUE); - fctx_cleanupfinds(fctx); - fctx_cleanupaltfinds(fctx); - fctx_cleanupforwaddrs(fctx); - fctx_cleanupaltaddrs(fctx); - result = fctx_getaddresses(fctx); - if (result == DNS_R_WAIT) { - /* - * Sleep waiting for addresses. - */ - FCTXTRACE("addrwait"); - fctx->attributes |= FCTX_ATTR_ADDRWAIT; - return; - } else if (result != ISC_R_SUCCESS) { - /* - * Something bad happened. - */ - fctx_done(fctx, result); - return; - } - - addrinfo = fctx_nextaddress(fctx); - /* - * While we may have addresses from the ADB, they - * might be bad ones. In this case, return SERVFAIL. - */ - if (addrinfo == NULL) { - fctx_done(fctx, DNS_R_SERVFAIL); - return; - } - } - - result = fctx_query(fctx, addrinfo, fctx->options); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); + isc_result_t result; + dns_adbaddrinfo_t *addrinfo; + + FCTXTRACE("try"); + + REQUIRE(!ADDRWAIT(fctx)); + + addrinfo = fctx_nextaddress(fctx); + if (addrinfo == NULL) { + /* + * We have no more addresses. Start over. + */ + fctx_cancelqueries(fctx, ISC_TRUE); + fctx_cleanupfinds(fctx); + fctx_cleanupaltfinds(fctx); + fctx_cleanupforwaddrs(fctx); + fctx_cleanupaltaddrs(fctx); + result = fctx_getaddresses(fctx); + if (result == DNS_R_WAIT) { + /* + * Sleep waiting for addresses. + */ + FCTXTRACE("addrwait"); + fctx->attributes |= FCTX_ATTR_ADDRWAIT; + return; + } else if (result != ISC_R_SUCCESS) { + /* + * Something bad happened. + */ + fctx_done(fctx, result); + return; + } + + addrinfo = fctx_nextaddress(fctx); + /* + * While we may have addresses from the ADB, they + * might be bad ones. In this case, return SERVFAIL. + */ + if (addrinfo == NULL) { + fctx_done(fctx, DNS_R_SERVFAIL); + return; + } + } + + result = fctx_query(fctx, addrinfo, fctx->options); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); } static isc_boolean_t fctx_destroy(fetchctx_t *fctx) { - dns_resolver_t *res; - unsigned int bucketnum; - isc_sockaddr_t *sa, *next_sa; - - /* - * Caller must be holding the bucket lock. - */ - - REQUIRE(VALID_FCTX(fctx)); - REQUIRE(fctx->state == fetchstate_done || - fctx->state == fetchstate_init); - REQUIRE(ISC_LIST_EMPTY(fctx->events)); - REQUIRE(ISC_LIST_EMPTY(fctx->queries)); - REQUIRE(ISC_LIST_EMPTY(fctx->finds)); - REQUIRE(ISC_LIST_EMPTY(fctx->altfinds)); - REQUIRE(fctx->pending == 0); - REQUIRE(fctx->references == 0); - REQUIRE(ISC_LIST_EMPTY(fctx->validators)); - - FCTXTRACE("destroy"); - - res = fctx->res; - bucketnum = fctx->bucketnum; - - ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link); - - /* - * Free bad. - */ - for (sa = ISC_LIST_HEAD(fctx->bad); - sa != NULL; - sa = next_sa) { - next_sa = ISC_LIST_NEXT(sa, link); - ISC_LIST_UNLINK(fctx->bad, sa, link); - isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa)); - } - - for (sa = ISC_LIST_HEAD(fctx->edns); - sa != NULL; - sa = next_sa) { - next_sa = ISC_LIST_NEXT(sa, link); - ISC_LIST_UNLINK(fctx->edns, sa, link); - isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa)); - } - - for (sa = ISC_LIST_HEAD(fctx->edns512); - sa != NULL; - sa = next_sa) { - next_sa = ISC_LIST_NEXT(sa, link); - ISC_LIST_UNLINK(fctx->edns512, sa, link); - isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa)); - } - - isc_timer_detach(&fctx->timer); - dns_message_destroy(&fctx->rmessage); - dns_message_destroy(&fctx->qmessage); - if (dns_name_countlabels(&fctx->domain) > 0) - dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx); - if (dns_rdataset_isassociated(&fctx->nameservers)) - dns_rdataset_disassociate(&fctx->nameservers); - dns_name_free(&fctx->name, res->buckets[bucketnum].mctx); - dns_db_detach(&fctx->cache); - dns_adb_detach(&fctx->adb); - isc_mem_free(res->buckets[bucketnum].mctx, fctx->info); - isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx)); - - LOCK(&res->nlock); - res->nfctx--; - UNLOCK(&res->nlock); - - if (res->buckets[bucketnum].exiting && - ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs)) - return (ISC_TRUE); - - return (ISC_FALSE); + dns_resolver_t *res; + unsigned int bucketnum; + isc_sockaddr_t *sa, *next_sa; + + /* + * Caller must be holding the bucket lock. + */ + + REQUIRE(VALID_FCTX(fctx)); + REQUIRE(fctx->state == fetchstate_done || + fctx->state == fetchstate_init); + REQUIRE(ISC_LIST_EMPTY(fctx->events)); + REQUIRE(ISC_LIST_EMPTY(fctx->queries)); + REQUIRE(ISC_LIST_EMPTY(fctx->finds)); + REQUIRE(ISC_LIST_EMPTY(fctx->altfinds)); + REQUIRE(fctx->pending == 0); + REQUIRE(fctx->references == 0); + REQUIRE(ISC_LIST_EMPTY(fctx->validators)); + + FCTXTRACE("destroy"); + + res = fctx->res; + bucketnum = fctx->bucketnum; + + ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link); + + /* + * Free bad. + */ + for (sa = ISC_LIST_HEAD(fctx->bad); + sa != NULL; + sa = next_sa) { + next_sa = ISC_LIST_NEXT(sa, link); + ISC_LIST_UNLINK(fctx->bad, sa, link); + isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa)); + } + + for (sa = ISC_LIST_HEAD(fctx->edns); + sa != NULL; + sa = next_sa) { + next_sa = ISC_LIST_NEXT(sa, link); + ISC_LIST_UNLINK(fctx->edns, sa, link); + isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa)); + } + + for (sa = ISC_LIST_HEAD(fctx->edns512); + sa != NULL; + sa = next_sa) { + next_sa = ISC_LIST_NEXT(sa, link); + ISC_LIST_UNLINK(fctx->edns512, sa, link); + isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa)); + } + + isc_timer_detach(&fctx->timer); + dns_message_destroy(&fctx->rmessage); + dns_message_destroy(&fctx->qmessage); + if (dns_name_countlabels(&fctx->domain) > 0) + dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx); + if (dns_rdataset_isassociated(&fctx->nameservers)) + dns_rdataset_disassociate(&fctx->nameservers); + dns_name_free(&fctx->name, res->buckets[bucketnum].mctx); + dns_db_detach(&fctx->cache); + dns_adb_detach(&fctx->adb); + isc_mem_free(res->buckets[bucketnum].mctx, fctx->info); + isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx)); + + LOCK(&res->nlock); + res->nfctx--; + UNLOCK(&res->nlock); + + if (res->buckets[bucketnum].exiting && + ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs)) + return (ISC_TRUE); + + return (ISC_FALSE); } /* @@ -2713,209 +2757,209 @@ fctx_destroy(fetchctx_t *fctx) { static void fctx_timeout(isc_task_t *task, isc_event_t *event) { - fetchctx_t *fctx = event->ev_arg; - - REQUIRE(VALID_FCTX(fctx)); - - UNUSED(task); - - FCTXTRACE("timeout"); - - if (event->ev_type == ISC_TIMEREVENT_LIFE) { - fctx_done(fctx, ISC_R_TIMEDOUT); - } else { - isc_result_t result; - - fctx->timeouts++; - /* - * We could cancel the running queries here, or we could let - * them keep going. Right now we choose the latter... - */ - fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - /* - * Our timer has triggered. Reestablish the fctx lifetime - * timer. - */ - result = fctx_starttimer(fctx); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - else - /* - * Keep trying. - */ - fctx_try(fctx); - } - - isc_event_free(&event); + fetchctx_t *fctx = event->ev_arg; + + REQUIRE(VALID_FCTX(fctx)); + + UNUSED(task); + + FCTXTRACE("timeout"); + + if (event->ev_type == ISC_TIMEREVENT_LIFE) { + fctx_done(fctx, ISC_R_TIMEDOUT); + } else { + isc_result_t result; + + fctx->timeouts++; + /* + * We could cancel the running queries here, or we could let + * them keep going. Right now we choose the latter... + */ + fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; + /* + * Our timer has triggered. Reestablish the fctx lifetime + * timer. + */ + result = fctx_starttimer(fctx); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + else + /* + * Keep trying. + */ + fctx_try(fctx); + } + + isc_event_free(&event); } static void fctx_shutdown(fetchctx_t *fctx) { - isc_event_t *cevent; - - /* - * Start the shutdown process for fctx, if it isn't already underway. - */ - - FCTXTRACE("shutdown"); - - /* - * The caller must be holding the appropriate bucket lock. - */ - - if (fctx->want_shutdown) - return; - - fctx->want_shutdown = ISC_TRUE; - - /* - * Unless we're still initializing (in which case the - * control event is still outstanding), we need to post - * the control event to tell the fetch we want it to - * exit. - */ - if (fctx->state != fetchstate_init) { - cevent = &fctx->control_event; - isc_task_send(fctx->res->buckets[fctx->bucketnum].task, - &cevent); - } + isc_event_t *cevent; + + /* + * Start the shutdown process for fctx, if it isn't already underway. + */ + + FCTXTRACE("shutdown"); + + /* + * The caller must be holding the appropriate bucket lock. + */ + + if (fctx->want_shutdown) + return; + + fctx->want_shutdown = ISC_TRUE; + + /* + * Unless we're still initializing (in which case the + * control event is still outstanding), we need to post + * the control event to tell the fetch we want it to + * exit. + */ + if (fctx->state != fetchstate_init) { + cevent = &fctx->control_event; + isc_task_send(fctx->res->buckets[fctx->bucketnum].task, + &cevent); + } } static void fctx_doshutdown(isc_task_t *task, isc_event_t *event) { - fetchctx_t *fctx = event->ev_arg; - isc_boolean_t bucket_empty = ISC_FALSE; - dns_resolver_t *res; - unsigned int bucketnum; - dns_validator_t *validator; - - REQUIRE(VALID_FCTX(fctx)); - - UNUSED(task); - - res = fctx->res; - bucketnum = fctx->bucketnum; - - FCTXTRACE("doshutdown"); - - /* - * An fctx that is shutting down is no longer in ADDRWAIT mode. - */ - fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - - /* - * Cancel all pending validators. Note that this must be done - * without the bucket lock held, since that could cause deadlock. - */ - validator = ISC_LIST_HEAD(fctx->validators); - while (validator != NULL) { - dns_validator_cancel(validator); - validator = ISC_LIST_NEXT(validator, link); - } - - if (fctx->nsfetch != NULL) - dns_resolver_cancelfetch(fctx->nsfetch); - - /* - * Shut down anything that is still running on behalf of this - * fetch. To avoid deadlock with the ADB, we must do this - * before we lock the bucket lock. - */ - fctx_stopeverything(fctx, ISC_FALSE); - - LOCK(&res->buckets[bucketnum].lock); - - fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; - - INSIST(fctx->state == fetchstate_active || - fctx->state == fetchstate_done); - INSIST(fctx->want_shutdown); - - if (fctx->state != fetchstate_done) { - fctx->state = fetchstate_done; - fctx_sendevents(fctx, ISC_R_CANCELED); - } - - if (fctx->references == 0 && fctx->pending == 0 && - fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) - bucket_empty = fctx_destroy(fctx); - - UNLOCK(&res->buckets[bucketnum].lock); - - if (bucket_empty) - empty_bucket(res); + fetchctx_t *fctx = event->ev_arg; + isc_boolean_t bucket_empty = ISC_FALSE; + dns_resolver_t *res; + unsigned int bucketnum; + dns_validator_t *validator; + + REQUIRE(VALID_FCTX(fctx)); + + UNUSED(task); + + res = fctx->res; + bucketnum = fctx->bucketnum; + + FCTXTRACE("doshutdown"); + + /* + * An fctx that is shutting down is no longer in ADDRWAIT mode. + */ + fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; + + /* + * Cancel all pending validators. Note that this must be done + * without the bucket lock held, since that could cause deadlock. + */ + validator = ISC_LIST_HEAD(fctx->validators); + while (validator != NULL) { + dns_validator_cancel(validator); + validator = ISC_LIST_NEXT(validator, link); + } + + if (fctx->nsfetch != NULL) + dns_resolver_cancelfetch(fctx->nsfetch); + + /* + * Shut down anything that is still running on behalf of this + * fetch. To avoid deadlock with the ADB, we must do this + * before we lock the bucket lock. + */ + fctx_stopeverything(fctx, ISC_FALSE); + + LOCK(&res->buckets[bucketnum].lock); + + fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; + + INSIST(fctx->state == fetchstate_active || + fctx->state == fetchstate_done); + INSIST(fctx->want_shutdown); + + if (fctx->state != fetchstate_done) { + fctx->state = fetchstate_done; + fctx_sendevents(fctx, ISC_R_CANCELED); + } + + if (fctx->references == 0 && fctx->pending == 0 && + fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) + bucket_empty = fctx_destroy(fctx); + + UNLOCK(&res->buckets[bucketnum].lock); + + if (bucket_empty) + empty_bucket(res); } static void fctx_start(isc_task_t *task, isc_event_t *event) { - fetchctx_t *fctx = event->ev_arg; - isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE; - dns_resolver_t *res; - unsigned int bucketnum; - - REQUIRE(VALID_FCTX(fctx)); - - UNUSED(task); - - res = fctx->res; - bucketnum = fctx->bucketnum; - - FCTXTRACE("start"); - - LOCK(&res->buckets[bucketnum].lock); - - INSIST(fctx->state == fetchstate_init); - if (fctx->want_shutdown) { - /* - * We haven't started this fctx yet, and we've been requested - * to shut it down. - */ - fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; - fctx->state = fetchstate_done; - fctx_sendevents(fctx, ISC_R_CANCELED); - /* - * Since we haven't started, we INSIST that we have no - * pending ADB finds and no pending validations. - */ - INSIST(fctx->pending == 0); - INSIST(fctx->nqueries == 0); - INSIST(ISC_LIST_EMPTY(fctx->validators)); - if (fctx->references == 0) { - /* - * It's now safe to destroy this fctx. - */ - bucket_empty = fctx_destroy(fctx); - } - done = ISC_TRUE; - } else { - /* - * Normal fctx startup. - */ - fctx->state = fetchstate_active; - /* - * Reset the control event for later use in shutting down - * the fctx. - */ - ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, - DNS_EVENT_FETCHCONTROL, fctx_doshutdown, fctx, - NULL, NULL, NULL); - } - - UNLOCK(&res->buckets[bucketnum].lock); - - if (!done) { - isc_result_t result; - - /* - * All is well. Start working on the fetch. - */ - result = fctx_starttimer(fctx); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - else - fctx_try(fctx); - } else if (bucket_empty) - empty_bucket(res); + fetchctx_t *fctx = event->ev_arg; + isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE; + dns_resolver_t *res; + unsigned int bucketnum; + + REQUIRE(VALID_FCTX(fctx)); + + UNUSED(task); + + res = fctx->res; + bucketnum = fctx->bucketnum; + + FCTXTRACE("start"); + + LOCK(&res->buckets[bucketnum].lock); + + INSIST(fctx->state == fetchstate_init); + if (fctx->want_shutdown) { + /* + * We haven't started this fctx yet, and we've been requested + * to shut it down. + */ + fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; + fctx->state = fetchstate_done; + fctx_sendevents(fctx, ISC_R_CANCELED); + /* + * Since we haven't started, we INSIST that we have no + * pending ADB finds and no pending validations. + */ + INSIST(fctx->pending == 0); + INSIST(fctx->nqueries == 0); + INSIST(ISC_LIST_EMPTY(fctx->validators)); + if (fctx->references == 0) { + /* + * It's now safe to destroy this fctx. + */ + bucket_empty = fctx_destroy(fctx); + } + done = ISC_TRUE; + } else { + /* + * Normal fctx startup. + */ + fctx->state = fetchstate_active; + /* + * Reset the control event for later use in shutting down + * the fctx. + */ + ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, + DNS_EVENT_FETCHCONTROL, fctx_doshutdown, fctx, + NULL, NULL, NULL); + } + + UNLOCK(&res->buckets[bucketnum].lock); + + if (!done) { + isc_result_t result; + + /* + * All is well. Start working on the fetch. + */ + result = fctx_starttimer(fctx); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + else + fctx_try(fctx); + } else if (bucket_empty) + empty_bucket(res); } /* @@ -2924,294 +2968,295 @@ fctx_start(isc_task_t *task, isc_event_t *event) { static inline isc_result_t fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client, - dns_messageid_t id, isc_taskaction_t action, void *arg, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, - dns_fetch_t *fetch) + dns_messageid_t id, isc_taskaction_t action, void *arg, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, + dns_fetch_t *fetch) { - isc_task_t *clone; - dns_fetchevent_t *event; - - FCTXTRACE("join"); - - /* - * We store the task we're going to send this event to in the - * sender field. We'll make the fetch the sender when we actually - * send the event. - */ - clone = NULL; - isc_task_attach(task, &clone); - event = (dns_fetchevent_t *) - isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE, - action, arg, sizeof(*event)); - if (event == NULL) { - isc_task_detach(&clone); - return (ISC_R_NOMEMORY); - } - event->result = DNS_R_SERVFAIL; - event->qtype = fctx->type; - event->db = NULL; - event->node = NULL; - event->rdataset = rdataset; - event->sigrdataset = sigrdataset; - event->fetch = fetch; - event->client = client; - event->id = id; - dns_fixedname_init(&event->foundname); - - /* - * Make sure that we can store the sigrdataset in the - * first event if it is needed by any of the events. - */ - if (event->sigrdataset != NULL) - ISC_LIST_PREPEND(fctx->events, event, ev_link); - else - ISC_LIST_APPEND(fctx->events, event, ev_link); - fctx->references++; - - fetch->magic = DNS_FETCH_MAGIC; - fetch->private = fctx; - - return (ISC_R_SUCCESS); + isc_task_t *clone; + dns_fetchevent_t *event; + + FCTXTRACE("join"); + + /* + * We store the task we're going to send this event to in the + * sender field. We'll make the fetch the sender when we actually + * send the event. + */ + clone = NULL; + isc_task_attach(task, &clone); + event = (dns_fetchevent_t *) + isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE, + action, arg, sizeof(*event)); + if (event == NULL) { + isc_task_detach(&clone); + return (ISC_R_NOMEMORY); + } + event->result = DNS_R_SERVFAIL; + event->qtype = fctx->type; + event->db = NULL; + event->node = NULL; + event->rdataset = rdataset; + event->sigrdataset = sigrdataset; + event->fetch = fetch; + event->client = client; + event->id = id; + dns_fixedname_init(&event->foundname); + + /* + * Make sure that we can store the sigrdataset in the + * first event if it is needed by any of the events. + */ + if (event->sigrdataset != NULL) + ISC_LIST_PREPEND(fctx->events, event, ev_link); + else + ISC_LIST_APPEND(fctx->events, event, ev_link); + fctx->references++; + + fetch->magic = DNS_FETCH_MAGIC; + fetch->private = fctx; + + return (ISC_R_SUCCESS); } static isc_result_t fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, - dns_name_t *domain, dns_rdataset_t *nameservers, - unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp) + dns_name_t *domain, dns_rdataset_t *nameservers, + unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp) { - fetchctx_t *fctx; - isc_result_t result; - isc_result_t iresult; - isc_interval_t interval; - dns_fixedname_t fixed; - unsigned int findoptions = 0; - char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE]; - char typebuf[DNS_RDATATYPE_FORMATSIZE]; - dns_name_t suffix; - - /* - * Caller must be holding the lock for bucket number 'bucketnum'. - */ - REQUIRE(fctxp != NULL && *fctxp == NULL); - - fctx = isc_mem_get(res->buckets[bucketnum].mctx, sizeof(*fctx)); - if (fctx == NULL) - return (ISC_R_NOMEMORY); - dns_name_format(name, buf, sizeof(buf)); - dns_rdatatype_format(type, typebuf, sizeof(typebuf)); - strcat(buf, "/"); /* checked */ - strcat(buf, typebuf); /* checked */ - fctx->info = isc_mem_strdup(res->buckets[bucketnum].mctx, buf); - if (fctx->info == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup_fetch; - } - FCTXTRACE("create"); - dns_name_init(&fctx->name, NULL); - result = dns_name_dup(name, res->buckets[bucketnum].mctx, &fctx->name); - if (result != ISC_R_SUCCESS) - goto cleanup_info; - dns_name_init(&fctx->domain, NULL); - dns_rdataset_init(&fctx->nameservers); - - fctx->type = type; - fctx->options = options; - /* - * Note! We do not attach to the task. We are relying on the - * resolver to ensure that this task doesn't go away while we are - * using it. - */ - fctx->res = res; - fctx->references = 0; - fctx->bucketnum = bucketnum; - fctx->state = fetchstate_init; - fctx->want_shutdown = ISC_FALSE; - fctx->cloned = ISC_FALSE; - ISC_LIST_INIT(fctx->queries); - ISC_LIST_INIT(fctx->finds); - ISC_LIST_INIT(fctx->altfinds); - ISC_LIST_INIT(fctx->forwaddrs); - ISC_LIST_INIT(fctx->altaddrs); - ISC_LIST_INIT(fctx->forwarders); - fctx->fwdpolicy = dns_fwdpolicy_none; - ISC_LIST_INIT(fctx->bad); - ISC_LIST_INIT(fctx->edns); - ISC_LIST_INIT(fctx->edns512); - ISC_LIST_INIT(fctx->validators); - fctx->find = NULL; - fctx->altfind = NULL; - fctx->pending = 0; - fctx->restarts = 0; - fctx->timeouts = 0; - fctx->attributes = 0; - fctx->spilled = ISC_FALSE; - fctx->nqueries = 0; - - dns_name_init(&fctx->nsname, NULL); - fctx->nsfetch = NULL; - dns_rdataset_init(&fctx->nsrrset); - - if (domain == NULL) { - dns_forwarders_t *forwarders = NULL; - unsigned int labels; - - /* - * DS records are found in the parent server. - * Strip label to get the correct forwarder (if any). - */ - if (fctx->type == dns_rdatatype_ds && - dns_name_countlabels(name) > 1) { - dns_name_init(&suffix, NULL); - labels = dns_name_countlabels(name); - dns_name_getlabelsequence(name, 1, labels - 1, &suffix); - name = &suffix; - } - dns_fixedname_init(&fixed); - domain = dns_fixedname_name(&fixed); - result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, - domain, &forwarders); - if (result == ISC_R_SUCCESS) - fctx->fwdpolicy = forwarders->fwdpolicy; - - if (fctx->fwdpolicy != dns_fwdpolicy_only) { - /* - * The caller didn't supply a query domain and - * nameservers, and we're not in forward-only mode, - * so find the best nameservers to use. - */ - if (dns_rdatatype_atparent(type)) - findoptions |= DNS_DBFIND_NOEXACT; - result = dns_view_findzonecut(res->view, name, domain, - 0, findoptions, ISC_TRUE, - &fctx->nameservers, - NULL); - if (result != ISC_R_SUCCESS) - goto cleanup_name; - result = dns_name_dup(domain, - res->buckets[bucketnum].mctx, - &fctx->domain); - if (result != ISC_R_SUCCESS) { - dns_rdataset_disassociate(&fctx->nameservers); - goto cleanup_name; - } - } else { - /* - * We're in forward-only mode. Set the query domain. - */ - result = dns_name_dup(domain, - res->buckets[bucketnum].mctx, - &fctx->domain); - if (result != ISC_R_SUCCESS) - goto cleanup_name; - } - } else { - result = dns_name_dup(domain, - res->buckets[bucketnum].mctx, - &fctx->domain); - if (result != ISC_R_SUCCESS) - goto cleanup_name; - dns_rdataset_clone(nameservers, &fctx->nameservers); - } - - INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain)); - - fctx->qmessage = NULL; - result = dns_message_create(res->buckets[bucketnum].mctx, - DNS_MESSAGE_INTENTRENDER, - &fctx->qmessage); - - if (result != ISC_R_SUCCESS) - goto cleanup_domain; - - fctx->rmessage = NULL; - result = dns_message_create(res->buckets[bucketnum].mctx, - DNS_MESSAGE_INTENTPARSE, - &fctx->rmessage); - - if (result != ISC_R_SUCCESS) - goto cleanup_qmessage; - - /* - * Compute an expiration time for the entire fetch. - */ - isc_interval_set(&interval, 30, 0); /* XXXRTH constant */ - iresult = isc_time_nowplusinterval(&fctx->expires, &interval); - if (iresult != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_time_nowplusinterval: %s", - isc_result_totext(iresult)); - result = ISC_R_UNEXPECTED; - goto cleanup_rmessage; - } - - /* - * Default retry interval initialization. We set the interval now - * mostly so it won't be uninitialized. It will be set to the - * correct value before a query is issued. - */ - isc_interval_set(&fctx->interval, 2, 0); - - /* - * Create an inactive timer. It will be made active when the fetch - * is actually started. - */ - fctx->timer = NULL; - iresult = isc_timer_create(res->timermgr, isc_timertype_inactive, - NULL, NULL, - res->buckets[bucketnum].task, fctx_timeout, - fctx, &fctx->timer); - if (iresult != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_timer_create: %s", - isc_result_totext(iresult)); - result = ISC_R_UNEXPECTED; - goto cleanup_rmessage; - } - - /* - * Attach to the view's cache and adb. - */ - fctx->cache = NULL; - dns_db_attach(res->view->cachedb, &fctx->cache); - fctx->adb = NULL; - dns_adb_attach(res->view->adb, &fctx->adb); - - ISC_LIST_INIT(fctx->events); - ISC_LINK_INIT(fctx, link); - fctx->magic = FCTX_MAGIC; - - ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link); - - LOCK(&res->nlock); - res->nfctx++; - UNLOCK(&res->nlock); - - *fctxp = fctx; - - return (ISC_R_SUCCESS); + fetchctx_t *fctx; + isc_result_t result; + isc_result_t iresult; + isc_interval_t interval; + dns_fixedname_t fixed; + unsigned int findoptions = 0; + char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + dns_name_t suffix; + + /* + * Caller must be holding the lock for bucket number 'bucketnum'. + */ + REQUIRE(fctxp != NULL && *fctxp == NULL); + + fctx = isc_mem_get(res->buckets[bucketnum].mctx, sizeof(*fctx)); + if (fctx == NULL) + return (ISC_R_NOMEMORY); + dns_name_format(name, buf, sizeof(buf)); + dns_rdatatype_format(type, typebuf, sizeof(typebuf)); + strcat(buf, "/"); /* checked */ + strcat(buf, typebuf); /* checked */ + fctx->info = isc_mem_strdup(res->buckets[bucketnum].mctx, buf); + if (fctx->info == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_fetch; + } + FCTXTRACE("create"); + dns_name_init(&fctx->name, NULL); + result = dns_name_dup(name, res->buckets[bucketnum].mctx, &fctx->name); + if (result != ISC_R_SUCCESS) + goto cleanup_info; + dns_name_init(&fctx->domain, NULL); + dns_rdataset_init(&fctx->nameservers); + + fctx->type = type; + fctx->options = options; + /* + * Note! We do not attach to the task. We are relying on the + * resolver to ensure that this task doesn't go away while we are + * using it. + */ + fctx->res = res; + fctx->references = 0; + fctx->bucketnum = bucketnum; + fctx->state = fetchstate_init; + fctx->want_shutdown = ISC_FALSE; + fctx->cloned = ISC_FALSE; + ISC_LIST_INIT(fctx->queries); + ISC_LIST_INIT(fctx->finds); + ISC_LIST_INIT(fctx->altfinds); + ISC_LIST_INIT(fctx->forwaddrs); + ISC_LIST_INIT(fctx->altaddrs); + ISC_LIST_INIT(fctx->forwarders); + fctx->fwdpolicy = dns_fwdpolicy_none; + ISC_LIST_INIT(fctx->bad); + ISC_LIST_INIT(fctx->edns); + ISC_LIST_INIT(fctx->edns512); + ISC_LIST_INIT(fctx->validators); + fctx->validator = NULL; + fctx->find = NULL; + fctx->altfind = NULL; + fctx->pending = 0; + fctx->restarts = 0; + fctx->timeouts = 0; + fctx->attributes = 0; + fctx->spilled = ISC_FALSE; + fctx->nqueries = 0; + + dns_name_init(&fctx->nsname, NULL); + fctx->nsfetch = NULL; + dns_rdataset_init(&fctx->nsrrset); + + if (domain == NULL) { + dns_forwarders_t *forwarders = NULL; + unsigned int labels; + + /* + * DS records are found in the parent server. + * Strip label to get the correct forwarder (if any). + */ + if (fctx->type == dns_rdatatype_ds && + dns_name_countlabels(name) > 1) { + dns_name_init(&suffix, NULL); + labels = dns_name_countlabels(name); + dns_name_getlabelsequence(name, 1, labels - 1, &suffix); + name = &suffix; + } + dns_fixedname_init(&fixed); + domain = dns_fixedname_name(&fixed); + result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, + domain, &forwarders); + if (result == ISC_R_SUCCESS) + fctx->fwdpolicy = forwarders->fwdpolicy; + + if (fctx->fwdpolicy != dns_fwdpolicy_only) { + /* + * The caller didn't supply a query domain and + * nameservers, and we're not in forward-only mode, + * so find the best nameservers to use. + */ + if (dns_rdatatype_atparent(type)) + findoptions |= DNS_DBFIND_NOEXACT; + result = dns_view_findzonecut(res->view, name, domain, + 0, findoptions, ISC_TRUE, + &fctx->nameservers, + NULL); + if (result != ISC_R_SUCCESS) + goto cleanup_name; + result = dns_name_dup(domain, + res->buckets[bucketnum].mctx, + &fctx->domain); + if (result != ISC_R_SUCCESS) { + dns_rdataset_disassociate(&fctx->nameservers); + goto cleanup_name; + } + } else { + /* + * We're in forward-only mode. Set the query domain. + */ + result = dns_name_dup(domain, + res->buckets[bucketnum].mctx, + &fctx->domain); + if (result != ISC_R_SUCCESS) + goto cleanup_name; + } + } else { + result = dns_name_dup(domain, + res->buckets[bucketnum].mctx, + &fctx->domain); + if (result != ISC_R_SUCCESS) + goto cleanup_name; + dns_rdataset_clone(nameservers, &fctx->nameservers); + } + + INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain)); + + fctx->qmessage = NULL; + result = dns_message_create(res->buckets[bucketnum].mctx, + DNS_MESSAGE_INTENTRENDER, + &fctx->qmessage); + + if (result != ISC_R_SUCCESS) + goto cleanup_domain; + + fctx->rmessage = NULL; + result = dns_message_create(res->buckets[bucketnum].mctx, + DNS_MESSAGE_INTENTPARSE, + &fctx->rmessage); + + if (result != ISC_R_SUCCESS) + goto cleanup_qmessage; + + /* + * Compute an expiration time for the entire fetch. + */ + isc_interval_set(&interval, 30, 0); /* XXXRTH constant */ + iresult = isc_time_nowplusinterval(&fctx->expires, &interval); + if (iresult != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_time_nowplusinterval: %s", + isc_result_totext(iresult)); + result = ISC_R_UNEXPECTED; + goto cleanup_rmessage; + } + + /* + * Default retry interval initialization. We set the interval now + * mostly so it won't be uninitialized. It will be set to the + * correct value before a query is issued. + */ + isc_interval_set(&fctx->interval, 2, 0); + + /* + * Create an inactive timer. It will be made active when the fetch + * is actually started. + */ + fctx->timer = NULL; + iresult = isc_timer_create(res->timermgr, isc_timertype_inactive, + NULL, NULL, + res->buckets[bucketnum].task, fctx_timeout, + fctx, &fctx->timer); + if (iresult != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_timer_create: %s", + isc_result_totext(iresult)); + result = ISC_R_UNEXPECTED; + goto cleanup_rmessage; + } + + /* + * Attach to the view's cache and adb. + */ + fctx->cache = NULL; + dns_db_attach(res->view->cachedb, &fctx->cache); + fctx->adb = NULL; + dns_adb_attach(res->view->adb, &fctx->adb); + + ISC_LIST_INIT(fctx->events); + ISC_LINK_INIT(fctx, link); + fctx->magic = FCTX_MAGIC; + + ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link); + + LOCK(&res->nlock); + res->nfctx++; + UNLOCK(&res->nlock); + + *fctxp = fctx; + + return (ISC_R_SUCCESS); cleanup_rmessage: - dns_message_destroy(&fctx->rmessage); + dns_message_destroy(&fctx->rmessage); cleanup_qmessage: - dns_message_destroy(&fctx->qmessage); + dns_message_destroy(&fctx->qmessage); cleanup_domain: - if (dns_name_countlabels(&fctx->domain) > 0) - dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx); - if (dns_rdataset_isassociated(&fctx->nameservers)) - dns_rdataset_disassociate(&fctx->nameservers); + if (dns_name_countlabels(&fctx->domain) > 0) + dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx); + if (dns_rdataset_isassociated(&fctx->nameservers)) + dns_rdataset_disassociate(&fctx->nameservers); cleanup_name: - dns_name_free(&fctx->name, res->buckets[bucketnum].mctx); + dns_name_free(&fctx->name, res->buckets[bucketnum].mctx); cleanup_info: - isc_mem_free(res->buckets[bucketnum].mctx, fctx->info); + isc_mem_free(res->buckets[bucketnum].mctx, fctx->info); cleanup_fetch: - isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx)); + isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx)); - return (result); + return (result); } /* @@ -3219,148 +3264,148 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, */ static inline isc_boolean_t is_lame(fetchctx_t *fctx) { - dns_message_t *message = fctx->rmessage; - dns_name_t *name; - dns_rdataset_t *rdataset; - isc_result_t result; - - if (message->rcode != dns_rcode_noerror && - message->rcode != dns_rcode_nxdomain) - return (ISC_FALSE); - - if (message->counts[DNS_SECTION_ANSWER] != 0) - return (ISC_FALSE); - - if (message->counts[DNS_SECTION_AUTHORITY] == 0) - return (ISC_FALSE); - - result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); - while (result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - dns_namereln_t namereln; - int order; - unsigned int labels; - if (rdataset->type != dns_rdatatype_ns) - continue; - namereln = dns_name_fullcompare(name, &fctx->domain, - &order, &labels); - if (namereln == dns_namereln_equal && - (message->flags & DNS_MESSAGEFLAG_AA) != 0) - return (ISC_FALSE); - if (namereln == dns_namereln_subdomain) - return (ISC_FALSE); - return (ISC_TRUE); - } - result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); - } - - return (ISC_FALSE); + dns_message_t *message = fctx->rmessage; + dns_name_t *name; + dns_rdataset_t *rdataset; + isc_result_t result; + + if (message->rcode != dns_rcode_noerror && + message->rcode != dns_rcode_nxdomain) + return (ISC_FALSE); + + if (message->counts[DNS_SECTION_ANSWER] != 0) + return (ISC_FALSE); + + if (message->counts[DNS_SECTION_AUTHORITY] == 0) + return (ISC_FALSE); + + result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + dns_namereln_t namereln; + int order; + unsigned int labels; + if (rdataset->type != dns_rdatatype_ns) + continue; + namereln = dns_name_fullcompare(name, &fctx->domain, + &order, &labels); + if (namereln == dns_namereln_equal && + (message->flags & DNS_MESSAGEFLAG_AA) != 0) + return (ISC_FALSE); + if (namereln == dns_namereln_subdomain) + return (ISC_FALSE); + return (ISC_TRUE); + } + result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); + } + + return (ISC_FALSE); } static inline void log_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) { - char namebuf[DNS_NAME_FORMATSIZE]; - char domainbuf[DNS_NAME_FORMATSIZE]; - char addrbuf[ISC_SOCKADDR_FORMATSIZE]; - - dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); - dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); - isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, - DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, - "lame server resolving '%s' (in '%s'?): %s", - namebuf, domainbuf, addrbuf); + char namebuf[DNS_NAME_FORMATSIZE]; + char domainbuf[DNS_NAME_FORMATSIZE]; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); + dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); + isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, + DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, + "lame server resolving '%s' (in '%s'?): %s", + namebuf, domainbuf, addrbuf); } static inline isc_result_t same_question(fetchctx_t *fctx) { - isc_result_t result; - dns_message_t *message = fctx->rmessage; - dns_name_t *name; - dns_rdataset_t *rdataset; - - /* - * Caller must be holding the fctx lock. - */ - - /* - * XXXRTH Currently we support only one question. - */ - if (message->counts[DNS_SECTION_QUESTION] != 1) - return (DNS_R_FORMERR); - - result = dns_message_firstname(message, DNS_SECTION_QUESTION); - if (result != ISC_R_SUCCESS) - return (result); - name = NULL; - dns_message_currentname(message, DNS_SECTION_QUESTION, &name); - rdataset = ISC_LIST_HEAD(name->list); - INSIST(rdataset != NULL); - INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); - if (fctx->type != rdataset->type || - fctx->res->rdclass != rdataset->rdclass || - !dns_name_equal(&fctx->name, name)) - return (DNS_R_FORMERR); - - return (ISC_R_SUCCESS); + isc_result_t result; + dns_message_t *message = fctx->rmessage; + dns_name_t *name; + dns_rdataset_t *rdataset; + + /* + * Caller must be holding the fctx lock. + */ + + /* + * XXXRTH Currently we support only one question. + */ + if (message->counts[DNS_SECTION_QUESTION] != 1) + return (DNS_R_FORMERR); + + result = dns_message_firstname(message, DNS_SECTION_QUESTION); + if (result != ISC_R_SUCCESS) + return (result); + name = NULL; + dns_message_currentname(message, DNS_SECTION_QUESTION, &name); + rdataset = ISC_LIST_HEAD(name->list); + INSIST(rdataset != NULL); + INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); + if (fctx->type != rdataset->type || + fctx->res->rdclass != rdataset->rdclass || + !dns_name_equal(&fctx->name, name)) + return (DNS_R_FORMERR); + + return (ISC_R_SUCCESS); } static void clone_results(fetchctx_t *fctx) { - dns_fetchevent_t *event, *hevent; - isc_result_t result; - dns_name_t *name, *hname; - - FCTXTRACE("clone_results"); - - /* - * Set up any other events to have the same data as the first - * event. - * - * Caller must be holding the appropriate lock. - */ - - fctx->cloned = ISC_TRUE; - hevent = ISC_LIST_HEAD(fctx->events); - if (hevent == NULL) - return; - hname = dns_fixedname_name(&hevent->foundname); - for (event = ISC_LIST_NEXT(hevent, ev_link); - event != NULL; - event = ISC_LIST_NEXT(event, ev_link)) { - name = dns_fixedname_name(&event->foundname); - result = dns_name_copy(hname, name, NULL); - if (result != ISC_R_SUCCESS) - event->result = result; - else - event->result = hevent->result; - dns_db_attach(hevent->db, &event->db); - dns_db_attachnode(hevent->db, hevent->node, &event->node); - INSIST(hevent->rdataset != NULL); - INSIST(event->rdataset != NULL); - if (dns_rdataset_isassociated(hevent->rdataset)) - dns_rdataset_clone(hevent->rdataset, event->rdataset); - INSIST(! (hevent->sigrdataset == NULL && - event->sigrdataset != NULL)); - if (hevent->sigrdataset != NULL && - dns_rdataset_isassociated(hevent->sigrdataset) && - event->sigrdataset != NULL) - dns_rdataset_clone(hevent->sigrdataset, - event->sigrdataset); - } + dns_fetchevent_t *event, *hevent; + isc_result_t result; + dns_name_t *name, *hname; + + FCTXTRACE("clone_results"); + + /* + * Set up any other events to have the same data as the first + * event. + * + * Caller must be holding the appropriate lock. + */ + + fctx->cloned = ISC_TRUE; + hevent = ISC_LIST_HEAD(fctx->events); + if (hevent == NULL) + return; + hname = dns_fixedname_name(&hevent->foundname); + for (event = ISC_LIST_NEXT(hevent, ev_link); + event != NULL; + event = ISC_LIST_NEXT(event, ev_link)) { + name = dns_fixedname_name(&event->foundname); + result = dns_name_copy(hname, name, NULL); + if (result != ISC_R_SUCCESS) + event->result = result; + else + event->result = hevent->result; + dns_db_attach(hevent->db, &event->db); + dns_db_attachnode(hevent->db, hevent->node, &event->node); + INSIST(hevent->rdataset != NULL); + INSIST(event->rdataset != NULL); + if (dns_rdataset_isassociated(hevent->rdataset)) + dns_rdataset_clone(hevent->rdataset, event->rdataset); + INSIST(! (hevent->sigrdataset == NULL && + event->sigrdataset != NULL)); + if (hevent->sigrdataset != NULL && + dns_rdataset_isassociated(hevent->sigrdataset) && + event->sigrdataset != NULL) + dns_rdataset_clone(hevent->sigrdataset, + event->sigrdataset); + } } -#define CACHE(r) (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0) -#define ANSWER(r) (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0) -#define ANSWERSIG(r) (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0) -#define EXTERNAL(r) (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0) -#define CHAINING(r) (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0) -#define CHASE(r) (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0) -#define CHECKNAMES(r) (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0) +#define CACHE(r) (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0) +#define ANSWER(r) (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0) +#define ANSWERSIG(r) (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0) +#define EXTERNAL(r) (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0) +#define CHAINING(r) (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0) +#define CHASE(r) (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0) +#define CHECKNAMES(r) (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0) /* @@ -3369,36 +3414,42 @@ clone_results(fetchctx_t *fctx) { * was the last fctx in the resolver, destroy the resolver. * * Requires: - * '*fctx' is shutting down. + * '*fctx' is shutting down. */ static void maybe_destroy(fetchctx_t *fctx) { - unsigned int bucketnum; - isc_boolean_t bucket_empty = ISC_FALSE; - dns_resolver_t *res = fctx->res; - dns_validator_t *validator; - - REQUIRE(SHUTTINGDOWN(fctx)); - - if (fctx->pending != 0 || fctx->nqueries != 0) - return; - - for (validator = ISC_LIST_HEAD(fctx->validators); - validator != NULL; - validator = ISC_LIST_HEAD(fctx->validators)) { - ISC_LIST_UNLINK(fctx->validators, validator, link); - dns_validator_cancel(validator); - dns_validator_destroy(&validator); - } - - bucketnum = fctx->bucketnum; - LOCK(&res->buckets[bucketnum].lock); - if (fctx->references == 0) - bucket_empty = fctx_destroy(fctx); - UNLOCK(&res->buckets[bucketnum].lock); - - if (bucket_empty) - empty_bucket(res); + unsigned int bucketnum; + isc_boolean_t bucket_empty = ISC_FALSE; + dns_resolver_t *res = fctx->res; + dns_validator_t *validator, *next_validator; + + REQUIRE(SHUTTINGDOWN(fctx)); + + if (fctx->pending != 0 || fctx->nqueries != 0) + return; + + for (validator = ISC_LIST_HEAD(fctx->validators); + validator != NULL; validator = next_validator) { + next_validator = ISC_LIST_NEXT(validator, link); + dns_validator_cancel(validator); + /* + * If this is a active validator wait for the cancel + * to complete before calling dns_validator_destroy(). + */ + if (validator == fctx->validator) + continue; + ISC_LIST_UNLINK(fctx->validators, validator, link); + dns_validator_destroy(&validator); + } + + bucketnum = fctx->bucketnum; + LOCK(&res->buckets[bucketnum].lock); + if (fctx->references == 0 && ISC_LIST_EMPTY(fctx->validators)) + bucket_empty = fctx_destroy(fctx); + UNLOCK(&res->buckets[bucketnum].lock); + + if (bucket_empty) + empty_bucket(res); } /* @@ -3406,682 +3457,718 @@ maybe_destroy(fetchctx_t *fctx) { */ static void validated(isc_task_t *task, isc_event_t *event) { - isc_result_t result = ISC_R_SUCCESS; - isc_result_t eresult = ISC_R_SUCCESS; - isc_stdtime_t now; - fetchctx_t *fctx; - dns_validatorevent_t *vevent; - dns_fetchevent_t *hevent; - dns_rdataset_t *ardataset = NULL; - dns_rdataset_t *asigrdataset = NULL; - dns_dbnode_t *node = NULL; - isc_boolean_t negative; - isc_boolean_t chaining; - isc_boolean_t sentresponse; - isc_uint32_t ttl; - dns_dbnode_t *nsnode = NULL; - dns_name_t *name; - dns_rdataset_t *rdataset; - dns_rdataset_t *sigrdataset; - dns_valarg_t *valarg; - dns_adbaddrinfo_t *addrinfo; - - UNUSED(task); /* for now */ - - REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE); - valarg = event->ev_arg; - fctx = valarg->fctx; - addrinfo = valarg->addrinfo; - REQUIRE(VALID_FCTX(fctx)); - REQUIRE(!ISC_LIST_EMPTY(fctx->validators)); - - vevent = (dns_validatorevent_t *)event; - - FCTXTRACE("received validation completion event"); - - ISC_LIST_UNLINK(fctx->validators, vevent->validator, link); - - /* - * Destroy the validator early so that we can - * destroy the fctx if necessary. - */ - dns_validator_destroy(&vevent->validator); - isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx, - valarg, sizeof(*valarg)); - - negative = ISC_TF(vevent->rdataset == NULL); - - sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0); - - /* - * If shutting down, ignore the results. Check to see if we're - * done waiting for validator completions and ADB pending events; if - * so, destroy the fctx. - */ - if (SHUTTINGDOWN(fctx) && !sentresponse) { - maybe_destroy(fctx); /* Locks bucket. */ - goto cleanup_event; - } - - LOCK(&fctx->res->buckets[fctx->bucketnum].lock); - - /* - * If chaining, we need to make sure that the right result code is - * returned, and that the rdatasets are bound. - */ - if (vevent->result == ISC_R_SUCCESS && - !negative && - vevent->rdataset != NULL && - CHAINING(vevent->rdataset)) - { - if (vevent->rdataset->type == dns_rdatatype_cname) - eresult = DNS_R_CNAME; - else { - INSIST(vevent->rdataset->type == dns_rdatatype_dname); - eresult = DNS_R_DNAME; - } - chaining = ISC_TRUE; - } else - chaining = ISC_FALSE; - - /* - * Either we're not shutting down, or we are shutting down but want - * to cache the result anyway (if this was a validation started by - * a query with cd set) - */ - - hevent = ISC_LIST_HEAD(fctx->events); - if (hevent != NULL) { - if (!negative && !chaining && - (fctx->type == dns_rdatatype_any || - fctx->type == dns_rdatatype_rrsig || - fctx->type == dns_rdatatype_sig)) { - /* - * Don't bind rdatasets; the caller - * will iterate the node. - */ - } else { - ardataset = hevent->rdataset; - asigrdataset = hevent->sigrdataset; - } - } - - if (vevent->result != ISC_R_SUCCESS) { - FCTXTRACE("validation failed"); - result = ISC_R_NOTFOUND; - if (vevent->rdataset != NULL) - result = dns_db_findnode(fctx->cache, vevent->name, - ISC_TRUE, &node); - if (result == ISC_R_SUCCESS) - (void)dns_db_deleterdataset(fctx->cache, node, NULL, - vevent->type, 0); - if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL) - (void)dns_db_deleterdataset(fctx->cache, node, NULL, - dns_rdatatype_rrsig, - vevent->type); - if (result == ISC_R_SUCCESS) - dns_db_detachnode(fctx->cache, &node); - result = vevent->result; - add_bad(fctx, addrinfo, result); - isc_event_free(&event); - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - if (!ISC_LIST_EMPTY(fctx->validators)) - dns_validator_send(ISC_LIST_HEAD(fctx->validators)); - else if (sentresponse) - fctx_done(fctx, result); /* Locks bucket. */ - else - fctx_try(fctx); /* Locks bucket. */ - return; - } - - isc_stdtime_get(&now); - - if (negative) { - dns_rdatatype_t covers; - FCTXTRACE("nonexistence validation OK"); - - if (fctx->rmessage->rcode == dns_rcode_nxdomain) - covers = dns_rdatatype_any; - else - covers = fctx->type; - - result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, - &node); - if (result != ISC_R_SUCCESS) - goto noanswer_response; - - /* - * If we are asking for a SOA record set the cache time - * to zero to facilitate locating the containing zone of - * a arbitary zone. - */ - ttl = fctx->res->view->maxncachettl; - if (fctx->type == dns_rdatatype_soa && - covers == dns_rdatatype_any && - fctx->res->zero_no_soa_ttl) - ttl = 0; - - result = ncache_adderesult(fctx->rmessage, fctx->cache, node, - covers, now, ttl, - ardataset, &eresult); - if (result != ISC_R_SUCCESS) - goto noanswer_response; - goto answer_response; - } - - FCTXTRACE("validation OK"); - - if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) { - - result = dns_rdataset_addnoqname(vevent->rdataset, - vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - INSIST(vevent->sigrdataset != NULL); - vevent->sigrdataset->ttl = vevent->rdataset->ttl; - } - - /* - * The data was already cached as pending data. - * Re-cache it as secure and bind the cached - * rdatasets to the first event on the fetch - * event list. - */ - result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node); - if (result != ISC_R_SUCCESS) - goto noanswer_response; - - result = dns_db_addrdataset(fctx->cache, node, NULL, now, - vevent->rdataset, 0, ardataset); - if (result != ISC_R_SUCCESS && - result != DNS_R_UNCHANGED) - goto noanswer_response; - if (vevent->sigrdataset != NULL) { - result = dns_db_addrdataset(fctx->cache, node, NULL, now, - vevent->sigrdataset, 0, - asigrdataset); - if (result != ISC_R_SUCCESS && - result != DNS_R_UNCHANGED) - goto noanswer_response; - } - - if (sentresponse) { - /* - * If we only deferred the destroy because we wanted to cache - * the data, destroy now. - */ - dns_db_detachnode(fctx->cache, &node); - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - if (SHUTTINGDOWN(fctx)) - maybe_destroy(fctx); /* Locks bucket. */ - goto cleanup_event; - } - - if (!ISC_LIST_EMPTY(fctx->validators)) { - INSIST(!negative); - INSIST(fctx->type == dns_rdatatype_any || - fctx->type == dns_rdatatype_rrsig || - fctx->type == dns_rdatatype_sig); - /* - * Don't send a response yet - we have - * more rdatasets that still need to - * be validated. - */ - dns_db_detachnode(fctx->cache, &node); - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - dns_validator_send(ISC_LIST_HEAD(fctx->validators)); - goto cleanup_event; - } + isc_result_t result = ISC_R_SUCCESS; + isc_result_t eresult = ISC_R_SUCCESS; + isc_stdtime_t now; + fetchctx_t *fctx; + dns_validatorevent_t *vevent; + dns_fetchevent_t *hevent; + dns_rdataset_t *ardataset = NULL; + dns_rdataset_t *asigrdataset = NULL; + dns_dbnode_t *node = NULL; + isc_boolean_t negative; + isc_boolean_t chaining; + isc_boolean_t sentresponse; + isc_uint32_t ttl; + dns_dbnode_t *nsnode = NULL; + dns_name_t *name; + dns_rdataset_t *rdataset; + dns_rdataset_t *sigrdataset; + dns_valarg_t *valarg; + dns_adbaddrinfo_t *addrinfo; + + UNUSED(task); /* for now */ + + REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE); + valarg = event->ev_arg; + fctx = valarg->fctx; + addrinfo = valarg->addrinfo; + REQUIRE(VALID_FCTX(fctx)); + REQUIRE(!ISC_LIST_EMPTY(fctx->validators)); + + vevent = (dns_validatorevent_t *)event; + + FCTXTRACE("received validation completion event"); + + ISC_LIST_UNLINK(fctx->validators, vevent->validator, link); + fctx->validator = NULL; + + /* + * Destroy the validator early so that we can + * destroy the fctx if necessary. + */ + dns_validator_destroy(&vevent->validator); + isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx, + valarg, sizeof(*valarg)); + + negative = ISC_TF(vevent->rdataset == NULL); + + sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0); + + /* + * If shutting down, ignore the results. Check to see if we're + * done waiting for validator completions and ADB pending events; if + * so, destroy the fctx. + */ + if (SHUTTINGDOWN(fctx) && !sentresponse) { + maybe_destroy(fctx); /* Locks bucket. */ + goto cleanup_event; + } + + LOCK(&fctx->res->buckets[fctx->bucketnum].lock); + + /* + * If chaining, we need to make sure that the right result code is + * returned, and that the rdatasets are bound. + */ + if (vevent->result == ISC_R_SUCCESS && + !negative && + vevent->rdataset != NULL && + CHAINING(vevent->rdataset)) + { + if (vevent->rdataset->type == dns_rdatatype_cname) + eresult = DNS_R_CNAME; + else { + INSIST(vevent->rdataset->type == dns_rdatatype_dname); + eresult = DNS_R_DNAME; + } + chaining = ISC_TRUE; + } else + chaining = ISC_FALSE; + + /* + * Either we're not shutting down, or we are shutting down but want + * to cache the result anyway (if this was a validation started by + * a query with cd set) + */ + + hevent = ISC_LIST_HEAD(fctx->events); + if (hevent != NULL) { + if (!negative && !chaining && + (fctx->type == dns_rdatatype_any || + fctx->type == dns_rdatatype_rrsig || + fctx->type == dns_rdatatype_sig)) { + /* + * Don't bind rdatasets; the caller + * will iterate the node. + */ + } else { + ardataset = hevent->rdataset; + asigrdataset = hevent->sigrdataset; + } + } + + if (vevent->result != ISC_R_SUCCESS) { + FCTXTRACE("validation failed"); + result = ISC_R_NOTFOUND; + if (vevent->rdataset != NULL) + result = dns_db_findnode(fctx->cache, vevent->name, + ISC_TRUE, &node); + if (result == ISC_R_SUCCESS) + (void)dns_db_deleterdataset(fctx->cache, node, NULL, + vevent->type, 0); + if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL) + (void)dns_db_deleterdataset(fctx->cache, node, NULL, + dns_rdatatype_rrsig, + vevent->type); + if (result == ISC_R_SUCCESS) + dns_db_detachnode(fctx->cache, &node); + result = vevent->result; + add_bad(fctx, addrinfo, result); + isc_event_free(&event); + UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + INSIST(fctx->validator == NULL); + fctx->validator = ISC_LIST_HEAD(fctx->validators); + if (fctx->validator != NULL) { + dns_validator_send(fctx->validator); + } else if (sentresponse) + fctx_done(fctx, result); /* Locks bucket. */ + else + fctx_try(fctx); /* Locks bucket. */ + return; + } + + isc_stdtime_get(&now); + + if (negative) { + dns_rdatatype_t covers; + FCTXTRACE("nonexistence validation OK"); + + if (fctx->rmessage->rcode == dns_rcode_nxdomain) + covers = dns_rdatatype_any; + else + covers = fctx->type; + + result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, + &node); + if (result != ISC_R_SUCCESS) + goto noanswer_response; + + /* + * If we are asking for a SOA record set the cache time + * to zero to facilitate locating the containing zone of + * a arbitary zone. + */ + ttl = fctx->res->view->maxncachettl; + if (fctx->type == dns_rdatatype_soa && + covers == dns_rdatatype_any && + fctx->res->zero_no_soa_ttl) + ttl = 0; + + result = ncache_adderesult(fctx->rmessage, fctx->cache, node, + covers, now, ttl, + ardataset, &eresult); + if (result != ISC_R_SUCCESS) + goto noanswer_response; + goto answer_response; + } + + FCTXTRACE("validation OK"); + + if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) { + + result = dns_rdataset_addnoqname(vevent->rdataset, + vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + INSIST(vevent->sigrdataset != NULL); + vevent->sigrdataset->ttl = vevent->rdataset->ttl; + } + + /* + * The data was already cached as pending data. + * Re-cache it as secure and bind the cached + * rdatasets to the first event on the fetch + * event list. + */ + result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node); + if (result != ISC_R_SUCCESS) + goto noanswer_response; + + result = dns_db_addrdataset(fctx->cache, node, NULL, now, + vevent->rdataset, 0, ardataset); + if (result != ISC_R_SUCCESS && + result != DNS_R_UNCHANGED) + goto noanswer_response; + if (ardataset != NULL && ardataset->type == 0) { + if (NXDOMAIN(ardataset)) + eresult = DNS_R_NCACHENXDOMAIN; + else + eresult = DNS_R_NCACHENXRRSET; + } else if (vevent->sigrdataset != NULL) { + result = dns_db_addrdataset(fctx->cache, node, NULL, now, + vevent->sigrdataset, 0, + asigrdataset); + if (result != ISC_R_SUCCESS && + result != DNS_R_UNCHANGED) + goto noanswer_response; + } + + if (sentresponse) { + /* + * If we only deferred the destroy because we wanted to cache + * the data, destroy now. + */ + dns_db_detachnode(fctx->cache, &node); + UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + if (SHUTTINGDOWN(fctx)) + maybe_destroy(fctx); /* Locks bucket. */ + goto cleanup_event; + } + + if (!ISC_LIST_EMPTY(fctx->validators)) { + INSIST(!negative); + INSIST(fctx->type == dns_rdatatype_any || + fctx->type == dns_rdatatype_rrsig || + fctx->type == dns_rdatatype_sig); + /* + * Don't send a response yet - we have + * more rdatasets that still need to + * be validated. + */ + dns_db_detachnode(fctx->cache, &node); + UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + dns_validator_send(ISC_LIST_HEAD(fctx->validators)); + goto cleanup_event; + } answer_response: - /* - * Cache any NS/NSEC records that happened to be validated. - */ - result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); - while (result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY, - &name); - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - if ((rdataset->type != dns_rdatatype_ns && - rdataset->type != dns_rdatatype_nsec) || - rdataset->trust != dns_trust_secure) - continue; - for (sigrdataset = ISC_LIST_HEAD(name->list); - sigrdataset != NULL; - sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { - if (sigrdataset->type != dns_rdatatype_rrsig || - sigrdataset->covers != rdataset->type) - continue; - break; - } - if (sigrdataset == NULL || - sigrdataset->trust != dns_trust_secure) - continue; - result = dns_db_findnode(fctx->cache, name, ISC_TRUE, - &nsnode); - if (result != ISC_R_SUCCESS) - continue; - - result = dns_db_addrdataset(fctx->cache, nsnode, NULL, - now, rdataset, 0, NULL); - if (result == ISC_R_SUCCESS) - result = dns_db_addrdataset(fctx->cache, nsnode, - NULL, now, - sigrdataset, 0, - NULL); - dns_db_detachnode(fctx->cache, &nsnode); - } - result = dns_message_nextname(fctx->rmessage, - DNS_SECTION_AUTHORITY); - } - - result = ISC_R_SUCCESS; - - /* - * Respond with an answer, positive or negative, - * as opposed to an error. 'node' must be non-NULL. - */ - - fctx->attributes |= FCTX_ATTR_HAVEANSWER; - - if (hevent != NULL) { - hevent->result = eresult; - RUNTIME_CHECK(dns_name_copy(vevent->name, - dns_fixedname_name(&hevent->foundname), NULL) - == ISC_R_SUCCESS); - dns_db_attach(fctx->cache, &hevent->db); - dns_db_transfernode(fctx->cache, &node, &hevent->node); - clone_results(fctx); - } + /* + * Cache any NS/NSEC records that happened to be validated. + */ + result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY, + &name); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + if ((rdataset->type != dns_rdatatype_ns && + rdataset->type != dns_rdatatype_nsec) || + rdataset->trust != dns_trust_secure) + continue; + for (sigrdataset = ISC_LIST_HEAD(name->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { + if (sigrdataset->type != dns_rdatatype_rrsig || + sigrdataset->covers != rdataset->type) + continue; + break; + } + if (sigrdataset == NULL || + sigrdataset->trust != dns_trust_secure) + continue; + result = dns_db_findnode(fctx->cache, name, ISC_TRUE, + &nsnode); + if (result != ISC_R_SUCCESS) + continue; + + result = dns_db_addrdataset(fctx->cache, nsnode, NULL, + now, rdataset, 0, NULL); + if (result == ISC_R_SUCCESS) + result = dns_db_addrdataset(fctx->cache, nsnode, + NULL, now, + sigrdataset, 0, + NULL); + dns_db_detachnode(fctx->cache, &nsnode); + } + result = dns_message_nextname(fctx->rmessage, + DNS_SECTION_AUTHORITY); + } + + result = ISC_R_SUCCESS; + + /* + * Respond with an answer, positive or negative, + * as opposed to an error. 'node' must be non-NULL. + */ + + fctx->attributes |= FCTX_ATTR_HAVEANSWER; + + if (hevent != NULL) { + hevent->result = eresult; + RUNTIME_CHECK(dns_name_copy(vevent->name, + dns_fixedname_name(&hevent->foundname), NULL) + == ISC_R_SUCCESS); + dns_db_attach(fctx->cache, &hevent->db); + dns_db_transfernode(fctx->cache, &node, &hevent->node); + clone_results(fctx); + } noanswer_response: - if (node != NULL) - dns_db_detachnode(fctx->cache, &node); + if (node != NULL) + dns_db_detachnode(fctx->cache, &node); - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - fctx_done(fctx, result); /* Locks bucket. */ + fctx_done(fctx, result); /* Locks bucket. */ cleanup_event: - INSIST(node == NULL); - isc_event_free(&event); + INSIST(node == NULL); + isc_event_free(&event); } static inline isc_result_t cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, - isc_stdtime_t now) { - dns_rdataset_t *rdataset, *sigrdataset; - dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset; - dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL; - dns_dbnode_t *node, **anodep; - dns_db_t **adbp; - dns_name_t *aname; - dns_resolver_t *res; - isc_boolean_t need_validation, secure_domain, have_answer; - isc_result_t result, eresult; - dns_fetchevent_t *event; - unsigned int options; - isc_task_t *task; - isc_boolean_t fail; - unsigned int valoptions = 0; - - /* - * The appropriate bucket lock must be held. - */ - - res = fctx->res; - need_validation = ISC_FALSE; - secure_domain = ISC_FALSE; - have_answer = ISC_FALSE; - eresult = ISC_R_SUCCESS; - task = res->buckets[fctx->bucketnum].task; - - /* - * Is DNSSEC validation required for this name? - */ - if (res->view->enablevalidation) { - result = dns_keytable_issecuredomain(res->view->secroots, name, - &secure_domain); - if (result != ISC_R_SUCCESS) - return (result); - - if (!secure_domain && res->view->dlv != NULL) { - valoptions = DNS_VALIDATOR_DLV; - secure_domain = ISC_TRUE; - } - } - - if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) - need_validation = ISC_FALSE; - else - need_validation = secure_domain; - - adbp = NULL; - aname = NULL; - anodep = NULL; - ardataset = NULL; - asigrdataset = NULL; - event = NULL; - if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 && - !need_validation) { - have_answer = ISC_TRUE; - event = ISC_LIST_HEAD(fctx->events); - if (event != NULL) { - adbp = &event->db; - aname = dns_fixedname_name(&event->foundname); - result = dns_name_copy(name, aname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - anodep = &event->node; - /* - * If this is an ANY, SIG or RRSIG query, we're not - * going to return any rdatasets, unless we encountered - * a CNAME or DNAME as "the answer". In this case, - * we're going to return DNS_R_CNAME or DNS_R_DNAME - * and we must set up the rdatasets. - */ - if ((fctx->type != dns_rdatatype_any && - fctx->type != dns_rdatatype_rrsig && - fctx->type != dns_rdatatype_sig) || - (name->attributes & DNS_NAMEATTR_CHAINING) != 0) { - ardataset = event->rdataset; - asigrdataset = event->sigrdataset; - } - } - } - - /* - * Find or create the cache node. - */ - node = NULL; - result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); - if (result != ISC_R_SUCCESS) - return (result); - - /* - * Cache or validate each cacheable rdataset. - */ - fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0); - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - if (!CACHE(rdataset)) - continue; - if (CHECKNAMES(rdataset)) { - char namebuf[DNS_NAME_FORMATSIZE]; - char typebuf[DNS_RDATATYPE_FORMATSIZE]; - char classbuf[DNS_RDATATYPE_FORMATSIZE]; - - dns_name_format(name, namebuf, sizeof(namebuf)); - dns_rdatatype_format(rdataset->type, typebuf, - sizeof(typebuf)); - dns_rdataclass_format(rdataset->rdclass, classbuf, - sizeof(classbuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, - "check-names %s %s/%s/%s", - fail ? "failure" : "warning", - namebuf, typebuf, classbuf); - if (fail) { - if (ANSWER(rdataset)) { - dns_db_detachnode(fctx->cache, &node); - return (DNS_R_BADNAME); - } - continue; - } - } - - /* - * Enforce the configure maximum cache TTL. - */ - if (rdataset->ttl > res->view->maxcachettl) - rdataset->ttl = res->view->maxcachettl; - - /* - * If this rrset is in a secure domain, do DNSSEC validation - * for it, unless it is glue. - */ - if (secure_domain && rdataset->trust != dns_trust_glue) { - /* - * RRSIGs are validated as part of validating the - * type they cover. - */ - if (rdataset->type == dns_rdatatype_rrsig) - continue; - /* - * Find the SIG for this rdataset, if we have it. - */ - for (sigrdataset = ISC_LIST_HEAD(name->list); - sigrdataset != NULL; - sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { - if (sigrdataset->type == dns_rdatatype_rrsig && - sigrdataset->covers == rdataset->type) - break; - } - if (sigrdataset == NULL) { - if (!ANSWER(rdataset) && need_validation) { - /* - * Ignore non-answer rdatasets that - * are missing signatures. - */ - continue; - } - } - - /* - * Normalize the rdataset and sigrdataset TTLs. - */ - if (sigrdataset != NULL) { - rdataset->ttl = ISC_MIN(rdataset->ttl, - sigrdataset->ttl); - sigrdataset->ttl = rdataset->ttl; - } - - /* - * Cache this rdataset/sigrdataset pair as - * pending data. - */ - rdataset->trust = dns_trust_pending; - if (sigrdataset != NULL) - sigrdataset->trust = dns_trust_pending; - if (!need_validation) - addedrdataset = ardataset; - else - addedrdataset = NULL; - result = dns_db_addrdataset(fctx->cache, node, NULL, - now, rdataset, 0, - addedrdataset); - if (result == DNS_R_UNCHANGED) - result = ISC_R_SUCCESS; - if (result != ISC_R_SUCCESS) - break; - if (sigrdataset != NULL) { - if (!need_validation) - addedrdataset = asigrdataset; - else - addedrdataset = NULL; - result = dns_db_addrdataset(fctx->cache, - node, NULL, now, - sigrdataset, 0, - addedrdataset); - if (result == DNS_R_UNCHANGED) - result = ISC_R_SUCCESS; - if (result != ISC_R_SUCCESS) - break; - } else if (!ANSWER(rdataset)) - continue; - - if (ANSWER(rdataset) && need_validation) { - if (fctx->type != dns_rdatatype_any && - fctx->type != dns_rdatatype_rrsig && - fctx->type != dns_rdatatype_sig) { - /* - * This is The Answer. We will - * validate it, but first we cache - * the rest of the response - it may - * contain useful keys. - */ - INSIST(valrdataset == NULL && - valsigrdataset == NULL); - valrdataset = rdataset; - valsigrdataset = sigrdataset; - } else { - /* - * This is one of (potentially) - * multiple answers to an ANY - * or SIG query. To keep things - * simple, we just start the - * validator right away rather - * than caching first and - * having to remember which - * rdatasets needed validation. - */ - result = valcreate(fctx, addrinfo, - name, rdataset->type, - rdataset, - sigrdataset, - valoptions, task); - /* - * Defer any further validations. - * This prevents multiple validators - * from manipulating fctx->rmessage - * simultaniously. - */ - valoptions |= DNS_VALIDATOR_DEFER; - } - } else if (CHAINING(rdataset)) { - if (rdataset->type == dns_rdatatype_cname) - eresult = DNS_R_CNAME; - else { - INSIST(rdataset->type == - dns_rdatatype_dname); - eresult = DNS_R_DNAME; - } - } - } else if (!EXTERNAL(rdataset)) { - /* - * It's OK to cache this rdataset now. - */ - if (ANSWER(rdataset)) - addedrdataset = ardataset; - else if (ANSWERSIG(rdataset)) - addedrdataset = asigrdataset; - else - addedrdataset = NULL; - if (CHAINING(rdataset)) { - if (rdataset->type == dns_rdatatype_cname) - eresult = DNS_R_CNAME; - else { - INSIST(rdataset->type == - dns_rdatatype_dname); - eresult = DNS_R_DNAME; - } - } - if (rdataset->trust == dns_trust_glue && - (rdataset->type == dns_rdatatype_ns || - (rdataset->type == dns_rdatatype_rrsig && - rdataset->covers == dns_rdatatype_ns))) { - /* - * If the trust level is 'dns_trust_glue' - * then we are adding data from a referral - * we got while executing the search algorithm. - * New referral data always takes precedence - * over the existing cache contents. - */ - options = DNS_DBADD_FORCE; - } else - options = 0; - /* - * Now we can add the rdataset. - */ - result = dns_db_addrdataset(fctx->cache, - node, NULL, now, - rdataset, - options, - addedrdataset); - if (result == DNS_R_UNCHANGED) { - if (ANSWER(rdataset) && - ardataset != NULL && - ardataset->type == 0) { - /* - * The answer in the cache is better - * than the answer we found, and is - * a negative cache entry, so we - * must set eresult appropriately. - */ - if (NXDOMAIN(ardataset)) - eresult = - DNS_R_NCACHENXDOMAIN; - else - eresult = - DNS_R_NCACHENXRRSET; - } - result = ISC_R_SUCCESS; - } else if (result != ISC_R_SUCCESS) - break; - } - } - - if (valrdataset != NULL) - result = valcreate(fctx, addrinfo, name, fctx->type, - valrdataset, valsigrdataset, valoptions, - task); - - if (result == ISC_R_SUCCESS && have_answer) { - fctx->attributes |= FCTX_ATTR_HAVEANSWER; - if (event != NULL) { - event->result = eresult; - dns_db_attach(fctx->cache, adbp); - dns_db_transfernode(fctx->cache, &node, anodep); - clone_results(fctx); - } - } - - if (node != NULL) - dns_db_detachnode(fctx->cache, &node); - - return (result); + isc_stdtime_t now) +{ + dns_rdataset_t *rdataset, *sigrdataset; + dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset; + dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL; + dns_dbnode_t *node, **anodep; + dns_db_t **adbp; + dns_name_t *aname; + dns_resolver_t *res; + isc_boolean_t need_validation, secure_domain, have_answer; + isc_result_t result, eresult; + dns_fetchevent_t *event; + unsigned int options; + isc_task_t *task; + isc_boolean_t fail; + unsigned int valoptions = 0; + + /* + * The appropriate bucket lock must be held. + */ + + res = fctx->res; + need_validation = ISC_FALSE; + secure_domain = ISC_FALSE; + have_answer = ISC_FALSE; + eresult = ISC_R_SUCCESS; + task = res->buckets[fctx->bucketnum].task; + + /* + * Is DNSSEC validation required for this name? + */ + if (res->view->enablevalidation) { + result = dns_keytable_issecuredomain(res->view->secroots, name, + &secure_domain); + if (result != ISC_R_SUCCESS) + return (result); + + if (!secure_domain && res->view->dlv != NULL) { + valoptions = DNS_VALIDATOR_DLV; + secure_domain = ISC_TRUE; + } + } + + if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) + need_validation = ISC_FALSE; + else + need_validation = secure_domain; + + adbp = NULL; + aname = NULL; + anodep = NULL; + ardataset = NULL; + asigrdataset = NULL; + event = NULL; + if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 && + !need_validation) { + have_answer = ISC_TRUE; + event = ISC_LIST_HEAD(fctx->events); + if (event != NULL) { + adbp = &event->db; + aname = dns_fixedname_name(&event->foundname); + result = dns_name_copy(name, aname, NULL); + if (result != ISC_R_SUCCESS) + return (result); + anodep = &event->node; + /* + * If this is an ANY, SIG or RRSIG query, we're not + * going to return any rdatasets, unless we encountered + * a CNAME or DNAME as "the answer". In this case, + * we're going to return DNS_R_CNAME or DNS_R_DNAME + * and we must set up the rdatasets. + */ + if ((fctx->type != dns_rdatatype_any && + fctx->type != dns_rdatatype_rrsig && + fctx->type != dns_rdatatype_sig) || + (name->attributes & DNS_NAMEATTR_CHAINING) != 0) { + ardataset = event->rdataset; + asigrdataset = event->sigrdataset; + } + } + } + + /* + * Find or create the cache node. + */ + node = NULL; + result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); + if (result != ISC_R_SUCCESS) + return (result); + + /* + * Cache or validate each cacheable rdataset. + */ + fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + if (!CACHE(rdataset)) + continue; + if (CHECKNAMES(rdataset)) { + char namebuf[DNS_NAME_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + char classbuf[DNS_RDATATYPE_FORMATSIZE]; + + dns_name_format(name, namebuf, sizeof(namebuf)); + dns_rdatatype_format(rdataset->type, typebuf, + sizeof(typebuf)); + dns_rdataclass_format(rdataset->rdclass, classbuf, + sizeof(classbuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, + "check-names %s %s/%s/%s", + fail ? "failure" : "warning", + namebuf, typebuf, classbuf); + if (fail) { + if (ANSWER(rdataset)) { + dns_db_detachnode(fctx->cache, &node); + return (DNS_R_BADNAME); + } + continue; + } + } + + /* + * Enforce the configure maximum cache TTL. + */ + if (rdataset->ttl > res->view->maxcachettl) + rdataset->ttl = res->view->maxcachettl; + + /* + * If this rrset is in a secure domain, do DNSSEC validation + * for it, unless it is glue. + */ + if (secure_domain && rdataset->trust != dns_trust_glue) { + /* + * RRSIGs are validated as part of validating the + * type they cover. + */ + if (rdataset->type == dns_rdatatype_rrsig) + continue; + /* + * Find the SIG for this rdataset, if we have it. + */ + for (sigrdataset = ISC_LIST_HEAD(name->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { + if (sigrdataset->type == dns_rdatatype_rrsig && + sigrdataset->covers == rdataset->type) + break; + } + if (sigrdataset == NULL) { + if (!ANSWER(rdataset) && need_validation) { + /* + * Ignore non-answer rdatasets that + * are missing signatures. + */ + continue; + } + } + + /* + * Normalize the rdataset and sigrdataset TTLs. + */ + if (sigrdataset != NULL) { + rdataset->ttl = ISC_MIN(rdataset->ttl, + sigrdataset->ttl); + sigrdataset->ttl = rdataset->ttl; + } + + /* + * Cache this rdataset/sigrdataset pair as + * pending data. + */ + rdataset->trust = dns_trust_pending; + if (sigrdataset != NULL) + sigrdataset->trust = dns_trust_pending; + if (!need_validation) + addedrdataset = ardataset; + else + addedrdataset = NULL; + result = dns_db_addrdataset(fctx->cache, node, NULL, + now, rdataset, 0, + addedrdataset); + if (result == DNS_R_UNCHANGED) { + result = ISC_R_SUCCESS; + if (!need_validation && + ardataset != NULL && + ardataset->type == 0) { + /* + * The answer in the cache is better + * than the answer we found, and is + * a negative cache entry, so we + * must set eresult appropriately. + */ + if (NXDOMAIN(ardataset)) + eresult = DNS_R_NCACHENXDOMAIN; + else + eresult = DNS_R_NCACHENXRRSET; + /* + * We have a negative response from + * the cache so don't attempt to + * add the RRSIG rrset. + */ + continue; + } + } + if (result != ISC_R_SUCCESS) + break; + if (sigrdataset != NULL) { + if (!need_validation) + addedrdataset = asigrdataset; + else + addedrdataset = NULL; + result = dns_db_addrdataset(fctx->cache, + node, NULL, now, + sigrdataset, 0, + addedrdataset); + if (result == DNS_R_UNCHANGED) + result = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS) + break; + } else if (!ANSWER(rdataset)) + continue; + + if (ANSWER(rdataset) && need_validation) { + if (fctx->type != dns_rdatatype_any && + fctx->type != dns_rdatatype_rrsig && + fctx->type != dns_rdatatype_sig) { + /* + * This is The Answer. We will + * validate it, but first we cache + * the rest of the response - it may + * contain useful keys. + */ + INSIST(valrdataset == NULL && + valsigrdataset == NULL); + valrdataset = rdataset; + valsigrdataset = sigrdataset; + } else { + /* + * This is one of (potentially) + * multiple answers to an ANY + * or SIG query. To keep things + * simple, we just start the + * validator right away rather + * than caching first and + * having to remember which + * rdatasets needed validation. + */ + result = valcreate(fctx, addrinfo, + name, rdataset->type, + rdataset, + sigrdataset, + valoptions, task); + /* + * Defer any further validations. + * This prevents multiple validators + * from manipulating fctx->rmessage + * simultaniously. + */ + valoptions |= DNS_VALIDATOR_DEFER; + } + } else if (CHAINING(rdataset)) { + if (rdataset->type == dns_rdatatype_cname) + eresult = DNS_R_CNAME; + else { + INSIST(rdataset->type == + dns_rdatatype_dname); + eresult = DNS_R_DNAME; + } + } + } else if (!EXTERNAL(rdataset)) { + /* + * It's OK to cache this rdataset now. + */ + if (ANSWER(rdataset)) + addedrdataset = ardataset; + else if (ANSWERSIG(rdataset)) + addedrdataset = asigrdataset; + else + addedrdataset = NULL; + if (CHAINING(rdataset)) { + if (rdataset->type == dns_rdatatype_cname) + eresult = DNS_R_CNAME; + else { + INSIST(rdataset->type == + dns_rdatatype_dname); + eresult = DNS_R_DNAME; + } + } + if (rdataset->trust == dns_trust_glue && + (rdataset->type == dns_rdatatype_ns || + (rdataset->type == dns_rdatatype_rrsig && + rdataset->covers == dns_rdatatype_ns))) { + /* + * If the trust level is 'dns_trust_glue' + * then we are adding data from a referral + * we got while executing the search algorithm. + * New referral data always takes precedence + * over the existing cache contents. + */ + options = DNS_DBADD_FORCE; + } else + options = 0; + /* + * Now we can add the rdataset. + */ + result = dns_db_addrdataset(fctx->cache, + node, NULL, now, + rdataset, + options, + addedrdataset); + if (result == DNS_R_UNCHANGED) { + if (ANSWER(rdataset) && + ardataset != NULL && + ardataset->type == 0) { + /* + * The answer in the cache is better + * than the answer we found, and is + * a negative cache entry, so we + * must set eresult appropriately. + */ + if (NXDOMAIN(ardataset)) + eresult = DNS_R_NCACHENXDOMAIN; + else + eresult = DNS_R_NCACHENXRRSET; + } + result = ISC_R_SUCCESS; + } else if (result != ISC_R_SUCCESS) + break; + } + } + + if (valrdataset != NULL) + result = valcreate(fctx, addrinfo, name, fctx->type, + valrdataset, valsigrdataset, valoptions, + task); + + if (result == ISC_R_SUCCESS && have_answer) { + fctx->attributes |= FCTX_ATTR_HAVEANSWER; + if (event != NULL) { + /* + * Negative results must be indicated in event->result. + */ + if (dns_rdataset_isassociated(event->rdataset) && + event->rdataset->type == dns_rdatatype_none) { + INSIST(eresult == DNS_R_NCACHENXDOMAIN || + eresult == DNS_R_NCACHENXRRSET); + } + event->result = eresult; + dns_db_attach(fctx->cache, adbp); + dns_db_transfernode(fctx->cache, &node, anodep); + clone_results(fctx); + } + } + + if (node != NULL) + dns_db_detachnode(fctx->cache, &node); + + return (result); } static inline isc_result_t cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) { - isc_result_t result; - dns_section_t section; - dns_name_t *name; - - FCTXTRACE("cache_message"); - - fctx->attributes &= ~FCTX_ATTR_WANTCACHE; - - LOCK(&fctx->res->buckets[fctx->bucketnum].lock); - - for (section = DNS_SECTION_ANSWER; - section <= DNS_SECTION_ADDITIONAL; - section++) { - result = dns_message_firstname(fctx->rmessage, section); - while (result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(fctx->rmessage, section, - &name); - if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) { - result = cache_name(fctx, name, addrinfo, now); - if (result != ISC_R_SUCCESS) - break; - } - result = dns_message_nextname(fctx->rmessage, section); - } - if (result != ISC_R_NOMORE) - break; - } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - - return (result); + isc_result_t result; + dns_section_t section; + dns_name_t *name; + + FCTXTRACE("cache_message"); + + fctx->attributes &= ~FCTX_ATTR_WANTCACHE; + + LOCK(&fctx->res->buckets[fctx->bucketnum].lock); + + for (section = DNS_SECTION_ANSWER; + section <= DNS_SECTION_ADDITIONAL; + section++) { + result = dns_message_firstname(fctx->rmessage, section); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(fctx->rmessage, section, + &name); + if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) { + result = cache_name(fctx, name, addrinfo, now); + if (result != ISC_R_SUCCESS) + break; + } + result = dns_message_nextname(fctx->rmessage, section); + } + if (result != ISC_R_NOMORE) + break; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + + return (result); } /* @@ -4089,385 +4176,385 @@ cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) */ static isc_result_t ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, - dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, - dns_rdataset_t *ardataset, - isc_result_t *eresultp) + dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, + dns_rdataset_t *ardataset, + isc_result_t *eresultp) { - isc_result_t result; - dns_rdataset_t rdataset; - - if (ardataset == NULL) { - dns_rdataset_init(&rdataset); - ardataset = &rdataset; - } - result = dns_ncache_add(message, cache, node, covers, now, - maxttl, ardataset); - if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) { - /* - * If the cache now contains a negative entry and we - * care about whether it is DNS_R_NCACHENXDOMAIN or - * DNS_R_NCACHENXRRSET then extract it. - */ - if (ardataset->type == 0) { - /* - * The cache data is a negative cache entry. - */ - if (NXDOMAIN(ardataset)) - *eresultp = DNS_R_NCACHENXDOMAIN; - else - *eresultp = DNS_R_NCACHENXRRSET; - } else { - /* - * Either we don't care about the nature of the - * cache rdataset (because no fetch is interested - * in the outcome), or the cache rdataset is not - * a negative cache entry. Whichever case it is, - * we can return success. - * - * XXXRTH There's a CNAME/DNAME problem here. - */ - *eresultp = ISC_R_SUCCESS; - } - result = ISC_R_SUCCESS; - } - if (ardataset == &rdataset && dns_rdataset_isassociated(ardataset)) - dns_rdataset_disassociate(ardataset); - - return (result); + isc_result_t result; + dns_rdataset_t rdataset; + + if (ardataset == NULL) { + dns_rdataset_init(&rdataset); + ardataset = &rdataset; + } + result = dns_ncache_add(message, cache, node, covers, now, + maxttl, ardataset); + if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) { + /* + * If the cache now contains a negative entry and we + * care about whether it is DNS_R_NCACHENXDOMAIN or + * DNS_R_NCACHENXRRSET then extract it. + */ + if (ardataset->type == 0) { + /* + * The cache data is a negative cache entry. + */ + if (NXDOMAIN(ardataset)) + *eresultp = DNS_R_NCACHENXDOMAIN; + else + *eresultp = DNS_R_NCACHENXRRSET; + } else { + /* + * Either we don't care about the nature of the + * cache rdataset (because no fetch is interested + * in the outcome), or the cache rdataset is not + * a negative cache entry. Whichever case it is, + * we can return success. + * + * XXXRTH There's a CNAME/DNAME problem here. + */ + *eresultp = ISC_R_SUCCESS; + } + result = ISC_R_SUCCESS; + } + if (ardataset == &rdataset && dns_rdataset_isassociated(ardataset)) + dns_rdataset_disassociate(ardataset); + + return (result); } static inline isc_result_t ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, - dns_rdatatype_t covers, isc_stdtime_t now) + dns_rdatatype_t covers, isc_stdtime_t now) { - isc_result_t result, eresult; - dns_name_t *name; - dns_resolver_t *res; - dns_db_t **adbp; - dns_dbnode_t *node, **anodep; - dns_rdataset_t *ardataset; - isc_boolean_t need_validation, secure_domain; - dns_name_t *aname; - dns_fetchevent_t *event; - isc_uint32_t ttl; - unsigned int valoptions = 0; - - FCTXTRACE("ncache_message"); - - fctx->attributes &= ~FCTX_ATTR_WANTNCACHE; - - res = fctx->res; - need_validation = ISC_FALSE; - secure_domain = ISC_FALSE; - eresult = ISC_R_SUCCESS; - name = &fctx->name; - node = NULL; - - /* - * XXXMPA remove when we follow cnames and adjust the setting - * of FCTX_ATTR_WANTNCACHE in noanswer_response(). - */ - INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0); - - /* - * Is DNSSEC validation required for this name? - */ - if (fctx->res->view->enablevalidation) { - result = dns_keytable_issecuredomain(res->view->secroots, name, - &secure_domain); - if (result != ISC_R_SUCCESS) - return (result); - - if (!secure_domain && res->view->dlv != NULL) { - valoptions = DNS_VALIDATOR_DLV; - secure_domain = ISC_TRUE; - } - } - - if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) - need_validation = ISC_FALSE; - else - need_validation = secure_domain; - - if (secure_domain) { - /* - * Mark all rdatasets as pending. - */ - dns_rdataset_t *trdataset; - dns_name_t *tname; - - result = dns_message_firstname(fctx->rmessage, - DNS_SECTION_AUTHORITY); - while (result == ISC_R_SUCCESS) { - tname = NULL; - dns_message_currentname(fctx->rmessage, - DNS_SECTION_AUTHORITY, - &tname); - for (trdataset = ISC_LIST_HEAD(tname->list); - trdataset != NULL; - trdataset = ISC_LIST_NEXT(trdataset, link)) - trdataset->trust = dns_trust_pending; - result = dns_message_nextname(fctx->rmessage, - DNS_SECTION_AUTHORITY); - } - if (result != ISC_R_NOMORE) - return (result); - - } - - if (need_validation) { - /* - * Do negative response validation. - */ - result = valcreate(fctx, addrinfo, name, fctx->type, - NULL, NULL, valoptions, - res->buckets[fctx->bucketnum].task); - /* - * If validation is necessary, return now. Otherwise continue - * to process the message, letting the validation complete - * in its own good time. - */ - return (result); - } - - LOCK(&res->buckets[fctx->bucketnum].lock); - - adbp = NULL; - aname = NULL; - anodep = NULL; - ardataset = NULL; - if (!HAVE_ANSWER(fctx)) { - event = ISC_LIST_HEAD(fctx->events); - if (event != NULL) { - adbp = &event->db; - aname = dns_fixedname_name(&event->foundname); - result = dns_name_copy(name, aname, NULL); - if (result != ISC_R_SUCCESS) - goto unlock; - anodep = &event->node; - ardataset = event->rdataset; - } - } else - event = NULL; - - result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); - if (result != ISC_R_SUCCESS) - goto unlock; - - /* - * If we are asking for a SOA record set the cache time - * to zero to facilitate locating the containing zone of - * a arbitary zone. - */ - ttl = fctx->res->view->maxncachettl; - if (fctx->type == dns_rdatatype_soa && - covers == dns_rdatatype_any) - ttl = 0; - - result = ncache_adderesult(fctx->rmessage, fctx->cache, node, - covers, now, ttl, ardataset, &eresult); - if (result != ISC_R_SUCCESS) - goto unlock; - - if (!HAVE_ANSWER(fctx)) { - fctx->attributes |= FCTX_ATTR_HAVEANSWER; - if (event != NULL) { - event->result = eresult; - dns_db_attach(fctx->cache, adbp); - dns_db_transfernode(fctx->cache, &node, anodep); - clone_results(fctx); - } - } + isc_result_t result, eresult; + dns_name_t *name; + dns_resolver_t *res; + dns_db_t **adbp; + dns_dbnode_t *node, **anodep; + dns_rdataset_t *ardataset; + isc_boolean_t need_validation, secure_domain; + dns_name_t *aname; + dns_fetchevent_t *event; + isc_uint32_t ttl; + unsigned int valoptions = 0; + + FCTXTRACE("ncache_message"); + + fctx->attributes &= ~FCTX_ATTR_WANTNCACHE; + + res = fctx->res; + need_validation = ISC_FALSE; + secure_domain = ISC_FALSE; + eresult = ISC_R_SUCCESS; + name = &fctx->name; + node = NULL; + + /* + * XXXMPA remove when we follow cnames and adjust the setting + * of FCTX_ATTR_WANTNCACHE in noanswer_response(). + */ + INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0); + + /* + * Is DNSSEC validation required for this name? + */ + if (fctx->res->view->enablevalidation) { + result = dns_keytable_issecuredomain(res->view->secroots, name, + &secure_domain); + if (result != ISC_R_SUCCESS) + return (result); + + if (!secure_domain && res->view->dlv != NULL) { + valoptions = DNS_VALIDATOR_DLV; + secure_domain = ISC_TRUE; + } + } + + if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) + need_validation = ISC_FALSE; + else + need_validation = secure_domain; + + if (secure_domain) { + /* + * Mark all rdatasets as pending. + */ + dns_rdataset_t *trdataset; + dns_name_t *tname; + + result = dns_message_firstname(fctx->rmessage, + DNS_SECTION_AUTHORITY); + while (result == ISC_R_SUCCESS) { + tname = NULL; + dns_message_currentname(fctx->rmessage, + DNS_SECTION_AUTHORITY, + &tname); + for (trdataset = ISC_LIST_HEAD(tname->list); + trdataset != NULL; + trdataset = ISC_LIST_NEXT(trdataset, link)) + trdataset->trust = dns_trust_pending; + result = dns_message_nextname(fctx->rmessage, + DNS_SECTION_AUTHORITY); + } + if (result != ISC_R_NOMORE) + return (result); + + } + + if (need_validation) { + /* + * Do negative response validation. + */ + result = valcreate(fctx, addrinfo, name, fctx->type, + NULL, NULL, valoptions, + res->buckets[fctx->bucketnum].task); + /* + * If validation is necessary, return now. Otherwise continue + * to process the message, letting the validation complete + * in its own good time. + */ + return (result); + } + + LOCK(&res->buckets[fctx->bucketnum].lock); + + adbp = NULL; + aname = NULL; + anodep = NULL; + ardataset = NULL; + if (!HAVE_ANSWER(fctx)) { + event = ISC_LIST_HEAD(fctx->events); + if (event != NULL) { + adbp = &event->db; + aname = dns_fixedname_name(&event->foundname); + result = dns_name_copy(name, aname, NULL); + if (result != ISC_R_SUCCESS) + goto unlock; + anodep = &event->node; + ardataset = event->rdataset; + } + } else + event = NULL; + + result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); + if (result != ISC_R_SUCCESS) + goto unlock; + + /* + * If we are asking for a SOA record set the cache time + * to zero to facilitate locating the containing zone of + * a arbitary zone. + */ + ttl = fctx->res->view->maxncachettl; + if (fctx->type == dns_rdatatype_soa && + covers == dns_rdatatype_any) + ttl = 0; + + result = ncache_adderesult(fctx->rmessage, fctx->cache, node, + covers, now, ttl, ardataset, &eresult); + if (result != ISC_R_SUCCESS) + goto unlock; + + if (!HAVE_ANSWER(fctx)) { + fctx->attributes |= FCTX_ATTR_HAVEANSWER; + if (event != NULL) { + event->result = eresult; + dns_db_attach(fctx->cache, adbp); + dns_db_transfernode(fctx->cache, &node, anodep); + clone_results(fctx); + } + } unlock: - UNLOCK(&res->buckets[fctx->bucketnum].lock); + UNLOCK(&res->buckets[fctx->bucketnum].lock); - if (node != NULL) - dns_db_detachnode(fctx->cache, &node); + if (node != NULL) + dns_db_detachnode(fctx->cache, &node); - return (result); + return (result); } static inline void mark_related(dns_name_t *name, dns_rdataset_t *rdataset, - isc_boolean_t external, isc_boolean_t gluing) + isc_boolean_t external, isc_boolean_t gluing) { - name->attributes |= DNS_NAMEATTR_CACHE; - if (gluing) { - rdataset->trust = dns_trust_glue; - /* - * Glue with 0 TTL causes problems. We force the TTL to - * 1 second to prevent this. - */ - if (rdataset->ttl == 0) - rdataset->ttl = 1; - } else - rdataset->trust = dns_trust_additional; - /* - * Avoid infinite loops by only marking new rdatasets. - */ - if (!CACHE(rdataset)) { - name->attributes |= DNS_NAMEATTR_CHASE; - rdataset->attributes |= DNS_RDATASETATTR_CHASE; - } - rdataset->attributes |= DNS_RDATASETATTR_CACHE; - if (external) - rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; + name->attributes |= DNS_NAMEATTR_CACHE; + if (gluing) { + rdataset->trust = dns_trust_glue; + /* + * Glue with 0 TTL causes problems. We force the TTL to + * 1 second to prevent this. + */ + if (rdataset->ttl == 0) + rdataset->ttl = 1; + } else + rdataset->trust = dns_trust_additional; + /* + * Avoid infinite loops by only marking new rdatasets. + */ + if (!CACHE(rdataset)) { + name->attributes |= DNS_NAMEATTR_CHASE; + rdataset->attributes |= DNS_RDATASETATTR_CHASE; + } + rdataset->attributes |= DNS_RDATASETATTR_CACHE; + if (external) + rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; } static isc_result_t check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) { - fetchctx_t *fctx = arg; - isc_result_t result; - dns_name_t *name; - dns_rdataset_t *rdataset; - isc_boolean_t external; - dns_rdatatype_t rtype; - isc_boolean_t gluing; - - REQUIRE(VALID_FCTX(fctx)); - - if (GLUING(fctx)) - gluing = ISC_TRUE; - else - gluing = ISC_FALSE; - name = NULL; - rdataset = NULL; - result = dns_message_findname(fctx->rmessage, DNS_SECTION_ADDITIONAL, - addname, dns_rdatatype_any, 0, &name, - NULL); - if (result == ISC_R_SUCCESS) { - external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); - if (type == dns_rdatatype_a) { - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - if (rdataset->type == dns_rdatatype_rrsig) - rtype = rdataset->covers; - else - rtype = rdataset->type; - if (rtype == dns_rdatatype_a || - rtype == dns_rdatatype_aaaa) - mark_related(name, rdataset, external, - gluing); - } - } else { - result = dns_message_findtype(name, type, 0, - &rdataset); - if (result == ISC_R_SUCCESS) { - mark_related(name, rdataset, external, gluing); - /* - * Do we have its SIG too? - */ - rdataset = NULL; - result = dns_message_findtype(name, - dns_rdatatype_rrsig, - type, &rdataset); - if (result == ISC_R_SUCCESS) - mark_related(name, rdataset, external, - gluing); - } - } - } - - return (ISC_R_SUCCESS); + fetchctx_t *fctx = arg; + isc_result_t result; + dns_name_t *name; + dns_rdataset_t *rdataset; + isc_boolean_t external; + dns_rdatatype_t rtype; + isc_boolean_t gluing; + + REQUIRE(VALID_FCTX(fctx)); + + if (GLUING(fctx)) + gluing = ISC_TRUE; + else + gluing = ISC_FALSE; + name = NULL; + rdataset = NULL; + result = dns_message_findname(fctx->rmessage, DNS_SECTION_ADDITIONAL, + addname, dns_rdatatype_any, 0, &name, + NULL); + if (result == ISC_R_SUCCESS) { + external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); + if (type == dns_rdatatype_a) { + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + if (rdataset->type == dns_rdatatype_rrsig) + rtype = rdataset->covers; + else + rtype = rdataset->type; + if (rtype == dns_rdatatype_a || + rtype == dns_rdatatype_aaaa) + mark_related(name, rdataset, external, + gluing); + } + } else { + result = dns_message_findtype(name, type, 0, + &rdataset); + if (result == ISC_R_SUCCESS) { + mark_related(name, rdataset, external, gluing); + /* + * Do we have its SIG too? + */ + rdataset = NULL; + result = dns_message_findtype(name, + dns_rdatatype_rrsig, + type, &rdataset); + if (result == ISC_R_SUCCESS) + mark_related(name, rdataset, external, + gluing); + } + } + } + + return (ISC_R_SUCCESS); } static void chase_additional(fetchctx_t *fctx) { - isc_boolean_t rescan; - dns_section_t section = DNS_SECTION_ADDITIONAL; - isc_result_t result; + isc_boolean_t rescan; + dns_section_t section = DNS_SECTION_ADDITIONAL; + isc_result_t result; again: - rescan = ISC_FALSE; - - for (result = dns_message_firstname(fctx->rmessage, section); - result == ISC_R_SUCCESS; - result = dns_message_nextname(fctx->rmessage, section)) { - dns_name_t *name = NULL; - dns_rdataset_t *rdataset; - dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL, - &name); - if ((name->attributes & DNS_NAMEATTR_CHASE) == 0) - continue; - name->attributes &= ~DNS_NAMEATTR_CHASE; - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - if (CHASE(rdataset)) { - rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; - (void)dns_rdataset_additionaldata(rdataset, - check_related, - fctx); - rescan = ISC_TRUE; - } - } - } - if (rescan) - goto again; + rescan = ISC_FALSE; + + for (result = dns_message_firstname(fctx->rmessage, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(fctx->rmessage, section)) { + dns_name_t *name = NULL; + dns_rdataset_t *rdataset; + dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL, + &name); + if ((name->attributes & DNS_NAMEATTR_CHASE) == 0) + continue; + name->attributes &= ~DNS_NAMEATTR_CHASE; + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + if (CHASE(rdataset)) { + rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; + (void)dns_rdataset_additionaldata(rdataset, + check_related, + fctx); + rescan = ISC_TRUE; + } + } + } + if (rescan) + goto again; } static inline isc_result_t cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) { - isc_result_t result; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdata_cname_t cname; - - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &rdata); - result = dns_rdata_tostruct(&rdata, &cname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - dns_name_init(tname, NULL); - dns_name_clone(&cname.cname, tname); - dns_rdata_freestruct(&cname); - - return (ISC_R_SUCCESS); + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_cname_t cname; + + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) + return (result); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, NULL); + if (result != ISC_R_SUCCESS) + return (result); + dns_name_init(tname, NULL); + dns_name_clone(&cname.cname, tname); + dns_rdata_freestruct(&cname); + + return (ISC_R_SUCCESS); } static inline isc_result_t dname_target(dns_rdataset_t *rdataset, dns_name_t *qname, dns_name_t *oname, - dns_fixedname_t *fixeddname) + dns_fixedname_t *fixeddname) { - isc_result_t result; - dns_rdata_t rdata = DNS_RDATA_INIT; - unsigned int nlabels; - int order; - dns_namereln_t namereln; - dns_rdata_dname_t dname; - dns_fixedname_t prefix; - - /* - * Get the target name of the DNAME. - */ - - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &rdata); - result = dns_rdata_tostruct(&rdata, &dname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - - /* - * Get the prefix of qname. - */ - namereln = dns_name_fullcompare(qname, oname, &order, &nlabels); - if (namereln != dns_namereln_subdomain) { - dns_rdata_freestruct(&dname); - return (DNS_R_FORMERR); - } - dns_fixedname_init(&prefix); - dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); - dns_fixedname_init(fixeddname); - result = dns_name_concatenate(dns_fixedname_name(&prefix), - &dname.dname, - dns_fixedname_name(fixeddname), NULL); - dns_rdata_freestruct(&dname); - return (result); + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int nlabels; + int order; + dns_namereln_t namereln; + dns_rdata_dname_t dname; + dns_fixedname_t prefix; + + /* + * Get the target name of the DNAME. + */ + + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) + return (result); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &dname, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + /* + * Get the prefix of qname. + */ + namereln = dns_name_fullcompare(qname, oname, &order, &nlabels); + if (namereln != dns_namereln_subdomain) { + dns_rdata_freestruct(&dname); + return (DNS_R_FORMERR); + } + dns_fixedname_init(&prefix); + dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); + dns_fixedname_init(fixeddname); + result = dns_name_concatenate(dns_fixedname_name(&prefix), + &dname.dname, + dns_fixedname_name(fixeddname), NULL); + dns_rdata_freestruct(&dname); + return (result); } /* @@ -4479,1552 +4566,1552 @@ dname_target(dns_rdataset_t *rdataset, dns_name_t *qname, dns_name_t *oname, */ static isc_result_t noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, - isc_boolean_t bind8_ns_resp) + isc_boolean_t bind8_ns_resp) { - isc_result_t result; - dns_message_t *message; - dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name; - dns_rdataset_t *rdataset, *ns_rdataset; - isc_boolean_t aa, negative_response; - dns_rdatatype_t type; - dns_section_t section = - bind8_ns_resp ? DNS_SECTION_ANSWER : DNS_SECTION_AUTHORITY; - - FCTXTRACE("noanswer_response"); - - message = fctx->rmessage; - - /* - * Setup qname. - */ - if (oqname == NULL) { - /* - * We have a normal, non-chained negative response or - * referral. - */ - if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) - aa = ISC_TRUE; - else - aa = ISC_FALSE; - qname = &fctx->name; - } else { - /* - * We're being invoked by answer_response() after it has - * followed a CNAME/DNAME chain. - */ - qname = oqname; - aa = ISC_FALSE; - /* - * If the current qname is not a subdomain of the query - * domain, there's no point in looking at the authority - * section without doing DNSSEC validation. - * - * Until we do that validation, we'll just return success - * in this case. - */ - if (!dns_name_issubdomain(qname, &fctx->domain)) - return (ISC_R_SUCCESS); - } - - /* - * We have to figure out if this is a negative response, or a - * referral. - */ - - /* - * Sometimes we can tell if its a negative response by looking at - * the message header. - */ - negative_response = ISC_FALSE; - if (message->rcode == dns_rcode_nxdomain || - (message->counts[DNS_SECTION_ANSWER] == 0 && - message->counts[DNS_SECTION_AUTHORITY] == 0)) - negative_response = ISC_TRUE; - - /* - * Process the authority section. - */ - ns_name = NULL; - ns_rdataset = NULL; - soa_name = NULL; - ds_name = NULL; - result = dns_message_firstname(message, section); - while (result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(message, section, &name); - if (dns_name_issubdomain(name, &fctx->domain)) { - /* - * Look for NS/SOA RRsets first. - */ - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - type = rdataset->type; - if (type == dns_rdatatype_rrsig) - type = rdataset->covers; - if (((type == dns_rdatatype_ns || - type == dns_rdatatype_soa) && - !dns_name_issubdomain(qname, name))) - return (DNS_R_FORMERR); - if (type == dns_rdatatype_ns) { - /* - * NS or RRSIG NS. - * - * Only one set of NS RRs is allowed. - */ - if (rdataset->type == - dns_rdatatype_ns) { - if (ns_name != NULL && - name != ns_name) - return (DNS_R_FORMERR); - ns_name = name; - ns_rdataset = rdataset; - } - name->attributes |= - DNS_NAMEATTR_CACHE; - rdataset->attributes |= - DNS_RDATASETATTR_CACHE; - rdataset->trust = dns_trust_glue; - } - if (type == dns_rdatatype_soa) { - /* - * SOA, or RRSIG SOA. - * - * Only one SOA is allowed. - */ - if (rdataset->type == - dns_rdatatype_soa) { - if (soa_name != NULL && - name != soa_name) - return (DNS_R_FORMERR); - soa_name = name; - } - name->attributes |= - DNS_NAMEATTR_NCACHE; - rdataset->attributes |= - DNS_RDATASETATTR_NCACHE; - if (aa) - rdataset->trust = - dns_trust_authauthority; - else - rdataset->trust = - dns_trust_additional; - } - } - } - result = dns_message_nextname(message, section); - if (result == ISC_R_NOMORE) - break; - else if (result != ISC_R_SUCCESS) - return (result); - } - - /* - * A negative response has a SOA record (Type 2) - * and a optional NS RRset (Type 1) or it has neither - * a SOA or a NS RRset (Type 3, handled above) or - * rcode is NXDOMAIN (handled above) in which case - * the NS RRset is allowed (Type 4). - */ - if (soa_name != NULL) - negative_response = ISC_TRUE; - - result = dns_message_firstname(message, section); - while (result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(message, section, &name); - if (dns_name_issubdomain(name, &fctx->domain)) { - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - type = rdataset->type; - if (type == dns_rdatatype_rrsig) - type = rdataset->covers; - if (type == dns_rdatatype_nsec) { - /* - * NSEC or RRSIG NSEC. - */ - if (negative_response) { - name->attributes |= - DNS_NAMEATTR_NCACHE; - rdataset->attributes |= - DNS_RDATASETATTR_NCACHE; - } else { - name->attributes |= - DNS_NAMEATTR_CACHE; - rdataset->attributes |= - DNS_RDATASETATTR_CACHE; - } - if (aa) - rdataset->trust = - dns_trust_authauthority; - else - rdataset->trust = - dns_trust_additional; - /* - * No additional data needs to be - * marked. - */ - } else if (type == dns_rdatatype_ds) { - /* - * DS or SIG DS. - * - * These should only be here if - * this is a referral, and there - * should only be one DS. - */ - if (ns_name == NULL) - return (DNS_R_FORMERR); - if (rdataset->type == - dns_rdatatype_ds) { - if (ds_name != NULL && - name != ds_name) - return (DNS_R_FORMERR); - ds_name = name; - } - name->attributes |= - DNS_NAMEATTR_CACHE; - rdataset->attributes |= - DNS_RDATASETATTR_CACHE; - if (aa) - rdataset->trust = - dns_trust_authauthority; - else - rdataset->trust = - dns_trust_additional; - } - } - } - result = dns_message_nextname(message, section); - if (result == ISC_R_NOMORE) - break; - else if (result != ISC_R_SUCCESS) - return (result); - } - - /* - * Trigger lookups for DNS nameservers. - */ - if (negative_response && message->rcode == dns_rcode_noerror && - fctx->type == dns_rdatatype_ds && soa_name != NULL && - dns_name_equal(soa_name, qname) && - !dns_name_equal(qname, dns_rootname)) - return (DNS_R_CHASEDSSERVERS); - - /* - * Did we find anything? - */ - if (!negative_response && ns_name == NULL) { - /* - * Nope. - */ - if (oqname != NULL) { - /* - * We've already got a partial CNAME/DNAME chain, - * and haven't found else anything useful here, but - * no error has occurred since we have an answer. - */ - return (ISC_R_SUCCESS); - } else { - /* - * The responder is insane. - */ - return (DNS_R_FORMERR); - } - } - - /* - * If we found both NS and SOA, they should be the same name. - */ - if (ns_name != NULL && soa_name != NULL && ns_name != soa_name) - return (DNS_R_FORMERR); - - /* - * Do we have a referral? (We only want to follow a referral if - * we're not following a chain.) - */ - if (!negative_response && ns_name != NULL && oqname == NULL) { - /* - * We already know ns_name is a subdomain of fctx->domain. - * If ns_name is equal to fctx->domain, we're not making - * progress. We return DNS_R_FORMERR so that we'll keep - * trying other servers. - */ - if (dns_name_equal(ns_name, &fctx->domain)) - return (DNS_R_FORMERR); - - /* - * If the referral name is not a parent of the query - * name, consider the responder insane. - */ - if (! dns_name_issubdomain(&fctx->name, ns_name)) { - FCTXTRACE("referral to non-parent"); - return (DNS_R_FORMERR); - } - - /* - * Mark any additional data related to this rdataset. - * It's important that we do this before we change the - * query domain. - */ - INSIST(ns_rdataset != NULL); - fctx->attributes |= FCTX_ATTR_GLUING; - (void)dns_rdataset_additionaldata(ns_rdataset, check_related, - fctx); - fctx->attributes &= ~FCTX_ATTR_GLUING; - /* - * NS rdatasets with 0 TTL cause problems. - * dns_view_findzonecut() will not find them when we - * try to follow the referral, and we'll SERVFAIL - * because the best nameservers are now above QDOMAIN. - * We force the TTL to 1 second to prevent this. - */ - if (ns_rdataset->ttl == 0) - ns_rdataset->ttl = 1; - /* - * Set the current query domain to the referral name. - * - * XXXRTH We should check if we're in forward-only mode, and - * if so we should bail out. - */ - INSIST(dns_name_countlabels(&fctx->domain) > 0); - dns_name_free(&fctx->domain, - fctx->res->buckets[fctx->bucketnum].mctx); - if (dns_rdataset_isassociated(&fctx->nameservers)) - dns_rdataset_disassociate(&fctx->nameservers); - dns_name_init(&fctx->domain, NULL); - result = dns_name_dup(ns_name, - fctx->res->buckets[fctx->bucketnum].mctx, - &fctx->domain); - if (result != ISC_R_SUCCESS) - return (result); - fctx->attributes |= FCTX_ATTR_WANTCACHE; - return (DNS_R_DELEGATION); - } - - /* - * Since we're not doing a referral, we don't want to cache any - * NS RRs we may have found. - */ - if (ns_name != NULL) - ns_name->attributes &= ~DNS_NAMEATTR_CACHE; - - if (negative_response && oqname == NULL) - fctx->attributes |= FCTX_ATTR_WANTNCACHE; - - return (ISC_R_SUCCESS); + isc_result_t result; + dns_message_t *message; + dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name; + dns_rdataset_t *rdataset, *ns_rdataset; + isc_boolean_t aa, negative_response; + dns_rdatatype_t type; + dns_section_t section = + bind8_ns_resp ? DNS_SECTION_ANSWER : DNS_SECTION_AUTHORITY; + + FCTXTRACE("noanswer_response"); + + message = fctx->rmessage; + + /* + * Setup qname. + */ + if (oqname == NULL) { + /* + * We have a normal, non-chained negative response or + * referral. + */ + if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) + aa = ISC_TRUE; + else + aa = ISC_FALSE; + qname = &fctx->name; + } else { + /* + * We're being invoked by answer_response() after it has + * followed a CNAME/DNAME chain. + */ + qname = oqname; + aa = ISC_FALSE; + /* + * If the current qname is not a subdomain of the query + * domain, there's no point in looking at the authority + * section without doing DNSSEC validation. + * + * Until we do that validation, we'll just return success + * in this case. + */ + if (!dns_name_issubdomain(qname, &fctx->domain)) + return (ISC_R_SUCCESS); + } + + /* + * We have to figure out if this is a negative response, or a + * referral. + */ + + /* + * Sometimes we can tell if its a negative response by looking at + * the message header. + */ + negative_response = ISC_FALSE; + if (message->rcode == dns_rcode_nxdomain || + (message->counts[DNS_SECTION_ANSWER] == 0 && + message->counts[DNS_SECTION_AUTHORITY] == 0)) + negative_response = ISC_TRUE; + + /* + * Process the authority section. + */ + ns_name = NULL; + ns_rdataset = NULL; + soa_name = NULL; + ds_name = NULL; + result = dns_message_firstname(message, section); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, section, &name); + if (dns_name_issubdomain(name, &fctx->domain)) { + /* + * Look for NS/SOA RRsets first. + */ + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + type = rdataset->type; + if (type == dns_rdatatype_rrsig) + type = rdataset->covers; + if (((type == dns_rdatatype_ns || + type == dns_rdatatype_soa) && + !dns_name_issubdomain(qname, name))) + return (DNS_R_FORMERR); + if (type == dns_rdatatype_ns) { + /* + * NS or RRSIG NS. + * + * Only one set of NS RRs is allowed. + */ + if (rdataset->type == + dns_rdatatype_ns) { + if (ns_name != NULL && + name != ns_name) + return (DNS_R_FORMERR); + ns_name = name; + ns_rdataset = rdataset; + } + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + rdataset->trust = dns_trust_glue; + } + if (type == dns_rdatatype_soa) { + /* + * SOA, or RRSIG SOA. + * + * Only one SOA is allowed. + */ + if (rdataset->type == + dns_rdatatype_soa) { + if (soa_name != NULL && + name != soa_name) + return (DNS_R_FORMERR); + soa_name = name; + } + name->attributes |= + DNS_NAMEATTR_NCACHE; + rdataset->attributes |= + DNS_RDATASETATTR_NCACHE; + if (aa) + rdataset->trust = + dns_trust_authauthority; + else + rdataset->trust = + dns_trust_additional; + } + } + } + result = dns_message_nextname(message, section); + if (result == ISC_R_NOMORE) + break; + else if (result != ISC_R_SUCCESS) + return (result); + } + + /* + * A negative response has a SOA record (Type 2) + * and a optional NS RRset (Type 1) or it has neither + * a SOA or a NS RRset (Type 3, handled above) or + * rcode is NXDOMAIN (handled above) in which case + * the NS RRset is allowed (Type 4). + */ + if (soa_name != NULL) + negative_response = ISC_TRUE; + + result = dns_message_firstname(message, section); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, section, &name); + if (dns_name_issubdomain(name, &fctx->domain)) { + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + type = rdataset->type; + if (type == dns_rdatatype_rrsig) + type = rdataset->covers; + if (type == dns_rdatatype_nsec) { + /* + * NSEC or RRSIG NSEC. + */ + if (negative_response) { + name->attributes |= + DNS_NAMEATTR_NCACHE; + rdataset->attributes |= + DNS_RDATASETATTR_NCACHE; + } else { + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + } + if (aa) + rdataset->trust = + dns_trust_authauthority; + else + rdataset->trust = + dns_trust_additional; + /* + * No additional data needs to be + * marked. + */ + } else if (type == dns_rdatatype_ds) { + /* + * DS or SIG DS. + * + * These should only be here if + * this is a referral, and there + * should only be one DS. + */ + if (ns_name == NULL) + return (DNS_R_FORMERR); + if (rdataset->type == + dns_rdatatype_ds) { + if (ds_name != NULL && + name != ds_name) + return (DNS_R_FORMERR); + ds_name = name; + } + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + if (aa) + rdataset->trust = + dns_trust_authauthority; + else + rdataset->trust = + dns_trust_additional; + } + } + } + result = dns_message_nextname(message, section); + if (result == ISC_R_NOMORE) + break; + else if (result != ISC_R_SUCCESS) + return (result); + } + + /* + * Trigger lookups for DNS nameservers. + */ + if (negative_response && message->rcode == dns_rcode_noerror && + fctx->type == dns_rdatatype_ds && soa_name != NULL && + dns_name_equal(soa_name, qname) && + !dns_name_equal(qname, dns_rootname)) + return (DNS_R_CHASEDSSERVERS); + + /* + * Did we find anything? + */ + if (!negative_response && ns_name == NULL) { + /* + * Nope. + */ + if (oqname != NULL) { + /* + * We've already got a partial CNAME/DNAME chain, + * and haven't found else anything useful here, but + * no error has occurred since we have an answer. + */ + return (ISC_R_SUCCESS); + } else { + /* + * The responder is insane. + */ + return (DNS_R_FORMERR); + } + } + + /* + * If we found both NS and SOA, they should be the same name. + */ + if (ns_name != NULL && soa_name != NULL && ns_name != soa_name) + return (DNS_R_FORMERR); + + /* + * Do we have a referral? (We only want to follow a referral if + * we're not following a chain.) + */ + if (!negative_response && ns_name != NULL && oqname == NULL) { + /* + * We already know ns_name is a subdomain of fctx->domain. + * If ns_name is equal to fctx->domain, we're not making + * progress. We return DNS_R_FORMERR so that we'll keep + * trying other servers. + */ + if (dns_name_equal(ns_name, &fctx->domain)) + return (DNS_R_FORMERR); + + /* + * If the referral name is not a parent of the query + * name, consider the responder insane. + */ + if (! dns_name_issubdomain(&fctx->name, ns_name)) { + FCTXTRACE("referral to non-parent"); + return (DNS_R_FORMERR); + } + + /* + * Mark any additional data related to this rdataset. + * It's important that we do this before we change the + * query domain. + */ + INSIST(ns_rdataset != NULL); + fctx->attributes |= FCTX_ATTR_GLUING; + (void)dns_rdataset_additionaldata(ns_rdataset, check_related, + fctx); + fctx->attributes &= ~FCTX_ATTR_GLUING; + /* + * NS rdatasets with 0 TTL cause problems. + * dns_view_findzonecut() will not find them when we + * try to follow the referral, and we'll SERVFAIL + * because the best nameservers are now above QDOMAIN. + * We force the TTL to 1 second to prevent this. + */ + if (ns_rdataset->ttl == 0) + ns_rdataset->ttl = 1; + /* + * Set the current query domain to the referral name. + * + * XXXRTH We should check if we're in forward-only mode, and + * if so we should bail out. + */ + INSIST(dns_name_countlabels(&fctx->domain) > 0); + dns_name_free(&fctx->domain, + fctx->res->buckets[fctx->bucketnum].mctx); + if (dns_rdataset_isassociated(&fctx->nameservers)) + dns_rdataset_disassociate(&fctx->nameservers); + dns_name_init(&fctx->domain, NULL); + result = dns_name_dup(ns_name, + fctx->res->buckets[fctx->bucketnum].mctx, + &fctx->domain); + if (result != ISC_R_SUCCESS) + return (result); + fctx->attributes |= FCTX_ATTR_WANTCACHE; + return (DNS_R_DELEGATION); + } + + /* + * Since we're not doing a referral, we don't want to cache any + * NS RRs we may have found. + */ + if (ns_name != NULL) + ns_name->attributes &= ~DNS_NAMEATTR_CACHE; + + if (negative_response && oqname == NULL) + fctx->attributes |= FCTX_ATTR_WANTNCACHE; + + return (ISC_R_SUCCESS); } static isc_result_t answer_response(fetchctx_t *fctx) { - isc_result_t result; - dns_message_t *message; - dns_name_t *name, *qname, tname; - dns_rdataset_t *rdataset; - isc_boolean_t done, external, chaining, aa, found, want_chaining; - isc_boolean_t have_answer, found_cname, found_type, wanted_chaining; - unsigned int aflag; - dns_rdatatype_t type; - dns_fixedname_t dname, fqname; - - FCTXTRACE("answer_response"); - - message = fctx->rmessage; - - /* - * Examine the answer section, marking those rdatasets which are - * part of the answer and should be cached. - */ - - done = ISC_FALSE; - found_cname = ISC_FALSE; - found_type = ISC_FALSE; - chaining = ISC_FALSE; - have_answer = ISC_FALSE; - want_chaining = ISC_FALSE; - if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) - aa = ISC_TRUE; - else - aa = ISC_FALSE; - qname = &fctx->name; - type = fctx->type; - result = dns_message_firstname(message, DNS_SECTION_ANSWER); - while (!done && result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(message, DNS_SECTION_ANSWER, &name); - external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); - if (dns_name_equal(name, qname)) { - wanted_chaining = ISC_FALSE; - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - found = ISC_FALSE; - want_chaining = ISC_FALSE; - aflag = 0; - if (rdataset->type == type && !found_cname) { - /* - * We've found an ordinary answer. - */ - found = ISC_TRUE; - found_type = ISC_TRUE; - done = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWER; - } else if (type == dns_rdatatype_any) { - /* - * We've found an answer matching - * an ANY query. There may be - * more. - */ - found = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWER; - } else if (rdataset->type == dns_rdatatype_rrsig - && rdataset->covers == type - && !found_cname) { - /* - * We've found a signature that - * covers the type we're looking for. - */ - found = ISC_TRUE; - found_type = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWERSIG; - } else if (rdataset->type == - dns_rdatatype_cname - && !found_type) { - /* - * We're looking for something else, - * but we found a CNAME. - * - * Getting a CNAME response for some - * query types is an error. - */ - if (type == dns_rdatatype_rrsig || - type == dns_rdatatype_dnskey || - type == dns_rdatatype_nsec) - return (DNS_R_FORMERR); - found = ISC_TRUE; - found_cname = ISC_TRUE; - want_chaining = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWER; - result = cname_target(rdataset, - &tname); - if (result != ISC_R_SUCCESS) - return (result); - } else if (rdataset->type == dns_rdatatype_rrsig - && rdataset->covers == - dns_rdatatype_cname - && !found_type) { - /* - * We're looking for something else, - * but we found a SIG CNAME. - */ - found = ISC_TRUE; - found_cname = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWERSIG; - } - - if (found) { - /* - * We've found an answer to our - * question. - */ - name->attributes |= - DNS_NAMEATTR_CACHE; - rdataset->attributes |= - DNS_RDATASETATTR_CACHE; - rdataset->trust = dns_trust_answer; - if (!chaining) { - /* - * This data is "the" answer - * to our question only if - * we're not chaining (i.e. - * if we haven't followed - * a CNAME or DNAME). - */ - INSIST(!external); - if (aflag == - DNS_RDATASETATTR_ANSWER) - have_answer = ISC_TRUE; - name->attributes |= - DNS_NAMEATTR_ANSWER; - rdataset->attributes |= aflag; - if (aa) - rdataset->trust = - dns_trust_authanswer; - } else if (external) { - /* - * This data is outside of - * our query domain, and - * may only be cached if it - * comes from a secure zone - * and validates. - */ - rdataset->attributes |= - DNS_RDATASETATTR_EXTERNAL; - } - - /* - * Mark any additional data related - * to this rdataset. - */ - (void)dns_rdataset_additionaldata( - rdataset, - check_related, - fctx); - - /* - * CNAME chaining. - */ - if (want_chaining) { - wanted_chaining = ISC_TRUE; - name->attributes |= - DNS_NAMEATTR_CHAINING; - rdataset->attributes |= - DNS_RDATASETATTR_CHAINING; - qname = &tname; - } - } - /* - * We could add an "else" clause here and - * log that we're ignoring this rdataset. - */ - } - /* - * If wanted_chaining is true, we've done - * some chaining as the result of processing - * this node, and thus we need to set - * chaining to true. - * - * We don't set chaining inside of the - * rdataset loop because doing that would - * cause us to ignore the signatures of - * CNAMEs. - */ - if (wanted_chaining) - chaining = ISC_TRUE; - } else { - /* - * Look for a DNAME (or its SIG). Anything else is - * ignored. - */ - wanted_chaining = ISC_FALSE; - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - isc_boolean_t found_dname = ISC_FALSE; - found = ISC_FALSE; - aflag = 0; - if (rdataset->type == dns_rdatatype_dname) { - /* - * We're looking for something else, - * but we found a DNAME. - * - * If we're not chaining, then the - * DNAME should not be external. - */ - if (!chaining && external) - return (DNS_R_FORMERR); - found = ISC_TRUE; - want_chaining = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWER; - result = dname_target(rdataset, - qname, name, - &dname); - if (result == ISC_R_NOSPACE) { - /* - * We can't construct the - * DNAME target. Do not - * try to continue. - */ - want_chaining = ISC_FALSE; - } else if (result != ISC_R_SUCCESS) - return (result); - else - found_dname = ISC_TRUE; - } else if (rdataset->type == dns_rdatatype_rrsig - && rdataset->covers == - dns_rdatatype_dname) { - /* - * We've found a signature that - * covers the DNAME. - */ - found = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWERSIG; - } - - if (found) { - /* - * We've found an answer to our - * question. - */ - name->attributes |= - DNS_NAMEATTR_CACHE; - rdataset->attributes |= - DNS_RDATASETATTR_CACHE; - rdataset->trust = dns_trust_answer; - if (!chaining) { - /* - * This data is "the" answer - * to our question only if - * we're not chaining. - */ - INSIST(!external); - if (aflag == - DNS_RDATASETATTR_ANSWER) - have_answer = ISC_TRUE; - name->attributes |= - DNS_NAMEATTR_ANSWER; - rdataset->attributes |= aflag; - if (aa) - rdataset->trust = - dns_trust_authanswer; - } else if (external) { - rdataset->attributes |= - DNS_RDATASETATTR_EXTERNAL; - } - - /* - * DNAME chaining. - */ - if (found_dname) { - /* - * Copy the the dname into the - * qname fixed name. - * - * Although we check for - * failure of the copy - * operation, in practice it - * should never fail since - * we already know that the - * result fits in a fixedname. - */ - dns_fixedname_init(&fqname); - result = dns_name_copy( - dns_fixedname_name(&dname), - dns_fixedname_name(&fqname), - NULL); - if (result != ISC_R_SUCCESS) - return (result); - wanted_chaining = ISC_TRUE; - name->attributes |= - DNS_NAMEATTR_CHAINING; - rdataset->attributes |= - DNS_RDATASETATTR_CHAINING; - qname = dns_fixedname_name( - &fqname); - } - } - } - if (wanted_chaining) - chaining = ISC_TRUE; - } - result = dns_message_nextname(message, DNS_SECTION_ANSWER); - } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - if (result != ISC_R_SUCCESS) - return (result); - - /* - * We should have found an answer. - */ - if (!have_answer) - return (DNS_R_FORMERR); - - /* - * This response is now potentially cacheable. - */ - fctx->attributes |= FCTX_ATTR_WANTCACHE; - - /* - * Did chaining end before we got the final answer? - */ - if (chaining) { - /* - * Yes. This may be a negative reply, so hand off - * authority section processing to the noanswer code. - * If it isn't a noanswer response, no harm will be - * done. - */ - return (noanswer_response(fctx, qname, ISC_FALSE)); - } - - /* - * We didn't end with an incomplete chain, so the rcode should be - * "no error". - */ - if (message->rcode != dns_rcode_noerror) - return (DNS_R_FORMERR); - - /* - * Examine the authority section (if there is one). - * - * We expect there to be only one owner name for all the rdatasets - * in this section, and we expect that it is not external. - */ - done = ISC_FALSE; - result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); - while (!done && result == ISC_R_SUCCESS) { - name = NULL; - dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); - external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); - if (!external) { - /* - * We expect to find NS or SIG NS rdatasets, and - * nothing else. - */ - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - if (rdataset->type == dns_rdatatype_ns || - (rdataset->type == dns_rdatatype_rrsig && - rdataset->covers == dns_rdatatype_ns)) { - name->attributes |= - DNS_NAMEATTR_CACHE; - rdataset->attributes |= - DNS_RDATASETATTR_CACHE; - if (aa && !chaining) - rdataset->trust = - dns_trust_authauthority; - else - rdataset->trust = - dns_trust_additional; - - /* - * Mark any additional data related - * to this rdataset. - */ - (void)dns_rdataset_additionaldata( - rdataset, - check_related, - fctx); - done = ISC_TRUE; - } - } - } - result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); - } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - - return (result); + isc_result_t result; + dns_message_t *message; + dns_name_t *name, *qname, tname; + dns_rdataset_t *rdataset; + isc_boolean_t done, external, chaining, aa, found, want_chaining; + isc_boolean_t have_answer, found_cname, found_type, wanted_chaining; + unsigned int aflag; + dns_rdatatype_t type; + dns_fixedname_t dname, fqname; + + FCTXTRACE("answer_response"); + + message = fctx->rmessage; + + /* + * Examine the answer section, marking those rdatasets which are + * part of the answer and should be cached. + */ + + done = ISC_FALSE; + found_cname = ISC_FALSE; + found_type = ISC_FALSE; + chaining = ISC_FALSE; + have_answer = ISC_FALSE; + want_chaining = ISC_FALSE; + if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) + aa = ISC_TRUE; + else + aa = ISC_FALSE; + qname = &fctx->name; + type = fctx->type; + result = dns_message_firstname(message, DNS_SECTION_ANSWER); + while (!done && result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, DNS_SECTION_ANSWER, &name); + external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); + if (dns_name_equal(name, qname)) { + wanted_chaining = ISC_FALSE; + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + found = ISC_FALSE; + want_chaining = ISC_FALSE; + aflag = 0; + if (rdataset->type == type && !found_cname) { + /* + * We've found an ordinary answer. + */ + found = ISC_TRUE; + found_type = ISC_TRUE; + done = ISC_TRUE; + aflag = DNS_RDATASETATTR_ANSWER; + } else if (type == dns_rdatatype_any) { + /* + * We've found an answer matching + * an ANY query. There may be + * more. + */ + found = ISC_TRUE; + aflag = DNS_RDATASETATTR_ANSWER; + } else if (rdataset->type == dns_rdatatype_rrsig + && rdataset->covers == type + && !found_cname) { + /* + * We've found a signature that + * covers the type we're looking for. + */ + found = ISC_TRUE; + found_type = ISC_TRUE; + aflag = DNS_RDATASETATTR_ANSWERSIG; + } else if (rdataset->type == + dns_rdatatype_cname + && !found_type) { + /* + * We're looking for something else, + * but we found a CNAME. + * + * Getting a CNAME response for some + * query types is an error. + */ + if (type == dns_rdatatype_rrsig || + type == dns_rdatatype_dnskey || + type == dns_rdatatype_nsec) + return (DNS_R_FORMERR); + found = ISC_TRUE; + found_cname = ISC_TRUE; + want_chaining = ISC_TRUE; + aflag = DNS_RDATASETATTR_ANSWER; + result = cname_target(rdataset, + &tname); + if (result != ISC_R_SUCCESS) + return (result); + } else if (rdataset->type == dns_rdatatype_rrsig + && rdataset->covers == + dns_rdatatype_cname + && !found_type) { + /* + * We're looking for something else, + * but we found a SIG CNAME. + */ + found = ISC_TRUE; + found_cname = ISC_TRUE; + aflag = DNS_RDATASETATTR_ANSWERSIG; + } + + if (found) { + /* + * We've found an answer to our + * question. + */ + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + rdataset->trust = dns_trust_answer; + if (!chaining) { + /* + * This data is "the" answer + * to our question only if + * we're not chaining (i.e. + * if we haven't followed + * a CNAME or DNAME). + */ + INSIST(!external); + if (aflag == + DNS_RDATASETATTR_ANSWER) + have_answer = ISC_TRUE; + name->attributes |= + DNS_NAMEATTR_ANSWER; + rdataset->attributes |= aflag; + if (aa) + rdataset->trust = + dns_trust_authanswer; + } else if (external) { + /* + * This data is outside of + * our query domain, and + * may only be cached if it + * comes from a secure zone + * and validates. + */ + rdataset->attributes |= + DNS_RDATASETATTR_EXTERNAL; + } + + /* + * Mark any additional data related + * to this rdataset. + */ + (void)dns_rdataset_additionaldata( + rdataset, + check_related, + fctx); + + /* + * CNAME chaining. + */ + if (want_chaining) { + wanted_chaining = ISC_TRUE; + name->attributes |= + DNS_NAMEATTR_CHAINING; + rdataset->attributes |= + DNS_RDATASETATTR_CHAINING; + qname = &tname; + } + } + /* + * We could add an "else" clause here and + * log that we're ignoring this rdataset. + */ + } + /* + * If wanted_chaining is true, we've done + * some chaining as the result of processing + * this node, and thus we need to set + * chaining to true. + * + * We don't set chaining inside of the + * rdataset loop because doing that would + * cause us to ignore the signatures of + * CNAMEs. + */ + if (wanted_chaining) + chaining = ISC_TRUE; + } else { + /* + * Look for a DNAME (or its SIG). Anything else is + * ignored. + */ + wanted_chaining = ISC_FALSE; + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + isc_boolean_t found_dname = ISC_FALSE; + found = ISC_FALSE; + aflag = 0; + if (rdataset->type == dns_rdatatype_dname) { + /* + * We're looking for something else, + * but we found a DNAME. + * + * If we're not chaining, then the + * DNAME should not be external. + */ + if (!chaining && external) + return (DNS_R_FORMERR); + found = ISC_TRUE; + want_chaining = ISC_TRUE; + aflag = DNS_RDATASETATTR_ANSWER; + result = dname_target(rdataset, + qname, name, + &dname); + if (result == ISC_R_NOSPACE) { + /* + * We can't construct the + * DNAME target. Do not + * try to continue. + */ + want_chaining = ISC_FALSE; + } else if (result != ISC_R_SUCCESS) + return (result); + else + found_dname = ISC_TRUE; + } else if (rdataset->type == dns_rdatatype_rrsig + && rdataset->covers == + dns_rdatatype_dname) { + /* + * We've found a signature that + * covers the DNAME. + */ + found = ISC_TRUE; + aflag = DNS_RDATASETATTR_ANSWERSIG; + } + + if (found) { + /* + * We've found an answer to our + * question. + */ + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + rdataset->trust = dns_trust_answer; + if (!chaining) { + /* + * This data is "the" answer + * to our question only if + * we're not chaining. + */ + INSIST(!external); + if (aflag == + DNS_RDATASETATTR_ANSWER) + have_answer = ISC_TRUE; + name->attributes |= + DNS_NAMEATTR_ANSWER; + rdataset->attributes |= aflag; + if (aa) + rdataset->trust = + dns_trust_authanswer; + } else if (external) { + rdataset->attributes |= + DNS_RDATASETATTR_EXTERNAL; + } + + /* + * DNAME chaining. + */ + if (found_dname) { + /* + * Copy the the dname into the + * qname fixed name. + * + * Although we check for + * failure of the copy + * operation, in practice it + * should never fail since + * we already know that the + * result fits in a fixedname. + */ + dns_fixedname_init(&fqname); + result = dns_name_copy( + dns_fixedname_name(&dname), + dns_fixedname_name(&fqname), + NULL); + if (result != ISC_R_SUCCESS) + return (result); + wanted_chaining = ISC_TRUE; + name->attributes |= + DNS_NAMEATTR_CHAINING; + rdataset->attributes |= + DNS_RDATASETATTR_CHAINING; + qname = dns_fixedname_name( + &fqname); + } + } + } + if (wanted_chaining) + chaining = ISC_TRUE; + } + result = dns_message_nextname(message, DNS_SECTION_ANSWER); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS) + return (result); + + /* + * We should have found an answer. + */ + if (!have_answer) + return (DNS_R_FORMERR); + + /* + * This response is now potentially cacheable. + */ + fctx->attributes |= FCTX_ATTR_WANTCACHE; + + /* + * Did chaining end before we got the final answer? + */ + if (chaining) { + /* + * Yes. This may be a negative reply, so hand off + * authority section processing to the noanswer code. + * If it isn't a noanswer response, no harm will be + * done. + */ + return (noanswer_response(fctx, qname, ISC_FALSE)); + } + + /* + * We didn't end with an incomplete chain, so the rcode should be + * "no error". + */ + if (message->rcode != dns_rcode_noerror) + return (DNS_R_FORMERR); + + /* + * Examine the authority section (if there is one). + * + * We expect there to be only one owner name for all the rdatasets + * in this section, and we expect that it is not external. + */ + done = ISC_FALSE; + result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); + while (!done && result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); + external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); + if (!external) { + /* + * We expect to find NS or SIG NS rdatasets, and + * nothing else. + */ + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + if (rdataset->type == dns_rdatatype_ns || + (rdataset->type == dns_rdatatype_rrsig && + rdataset->covers == dns_rdatatype_ns)) { + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + if (aa && !chaining) + rdataset->trust = + dns_trust_authauthority; + else + rdataset->trust = + dns_trust_additional; + + /* + * Mark any additional data related + * to this rdataset. + */ + (void)dns_rdataset_additionaldata( + rdataset, + check_related, + fctx); + done = ISC_TRUE; + } + } + } + result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + return (result); } static void resume_dslookup(isc_task_t *task, isc_event_t *event) { - dns_fetchevent_t *fevent; - dns_resolver_t *res; - fetchctx_t *fctx; - isc_result_t result; - isc_boolean_t bucket_empty = ISC_FALSE; - isc_boolean_t locked = ISC_FALSE; - unsigned int bucketnum; - dns_rdataset_t nameservers; - dns_fixedname_t fixed; - dns_name_t *domain; - - REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); - fevent = (dns_fetchevent_t *)event; - fctx = event->ev_arg; - REQUIRE(VALID_FCTX(fctx)); - res = fctx->res; - - UNUSED(task); - FCTXTRACE("resume_dslookup"); - - if (fevent->node != NULL) - dns_db_detachnode(fevent->db, &fevent->node); - if (fevent->db != NULL) - dns_db_detach(&fevent->db); - - dns_rdataset_init(&nameservers); - - bucketnum = fctx->bucketnum; - if (fevent->result == ISC_R_CANCELED) { - dns_resolver_destroyfetch(&fctx->nsfetch); - fctx_done(fctx, ISC_R_CANCELED); - } else if (fevent->result == ISC_R_SUCCESS) { - - FCTXTRACE("resuming DS lookup"); - - dns_resolver_destroyfetch(&fctx->nsfetch); - if (dns_rdataset_isassociated(&fctx->nameservers)) - dns_rdataset_disassociate(&fctx->nameservers); - dns_rdataset_clone(fevent->rdataset, &fctx->nameservers); - dns_name_free(&fctx->domain, - fctx->res->buckets[bucketnum].mctx); - dns_name_init(&fctx->domain, NULL); - result = dns_name_dup(&fctx->nsname, - fctx->res->buckets[bucketnum].mctx, - &fctx->domain); - if (result != ISC_R_SUCCESS) { - fctx_done(fctx, DNS_R_SERVFAIL); - goto cleanup; - } - /* - * Try again. - */ - fctx_try(fctx); - } else { - unsigned int n; - dns_rdataset_t *nsrdataset = NULL; - - /* - * Retrieve state from fctx->nsfetch before we destroy it. - */ - dns_fixedname_init(&fixed); - domain = dns_fixedname_name(&fixed); - dns_name_copy(&fctx->nsfetch->private->domain, domain, NULL); - if (dns_name_equal(&fctx->nsname, domain)) { - fctx_done(fctx, DNS_R_SERVFAIL); - dns_resolver_destroyfetch(&fctx->nsfetch); - goto cleanup; - } - if (dns_rdataset_isassociated( - &fctx->nsfetch->private->nameservers)) { - dns_rdataset_clone( - &fctx->nsfetch->private->nameservers, - &nameservers); - nsrdataset = &nameservers; - } else - domain = NULL; - dns_resolver_destroyfetch(&fctx->nsfetch); - n = dns_name_countlabels(&fctx->nsname); - dns_name_getlabelsequence(&fctx->nsname, 1, n - 1, - &fctx->nsname); - - if (dns_rdataset_isassociated(fevent->rdataset)) - dns_rdataset_disassociate(fevent->rdataset); - FCTXTRACE("continuing to look for parent's NS records"); - result = dns_resolver_createfetch(fctx->res, &fctx->nsname, - dns_rdatatype_ns, domain, - nsrdataset, NULL, 0, task, - resume_dslookup, fctx, - &fctx->nsrrset, NULL, - &fctx->nsfetch); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - else { - LOCK(&res->buckets[bucketnum].lock); - locked = ISC_TRUE; - fctx->references++; - } - } + dns_fetchevent_t *fevent; + dns_resolver_t *res; + fetchctx_t *fctx; + isc_result_t result; + isc_boolean_t bucket_empty = ISC_FALSE; + isc_boolean_t locked = ISC_FALSE; + unsigned int bucketnum; + dns_rdataset_t nameservers; + dns_fixedname_t fixed; + dns_name_t *domain; + + REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); + fevent = (dns_fetchevent_t *)event; + fctx = event->ev_arg; + REQUIRE(VALID_FCTX(fctx)); + res = fctx->res; + + UNUSED(task); + FCTXTRACE("resume_dslookup"); + + if (fevent->node != NULL) + dns_db_detachnode(fevent->db, &fevent->node); + if (fevent->db != NULL) + dns_db_detach(&fevent->db); + + dns_rdataset_init(&nameservers); + + bucketnum = fctx->bucketnum; + if (fevent->result == ISC_R_CANCELED) { + dns_resolver_destroyfetch(&fctx->nsfetch); + fctx_done(fctx, ISC_R_CANCELED); + } else if (fevent->result == ISC_R_SUCCESS) { + + FCTXTRACE("resuming DS lookup"); + + dns_resolver_destroyfetch(&fctx->nsfetch); + if (dns_rdataset_isassociated(&fctx->nameservers)) + dns_rdataset_disassociate(&fctx->nameservers); + dns_rdataset_clone(fevent->rdataset, &fctx->nameservers); + dns_name_free(&fctx->domain, + fctx->res->buckets[bucketnum].mctx); + dns_name_init(&fctx->domain, NULL); + result = dns_name_dup(&fctx->nsname, + fctx->res->buckets[bucketnum].mctx, + &fctx->domain); + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, DNS_R_SERVFAIL); + goto cleanup; + } + /* + * Try again. + */ + fctx_try(fctx); + } else { + unsigned int n; + dns_rdataset_t *nsrdataset = NULL; + + /* + * Retrieve state from fctx->nsfetch before we destroy it. + */ + dns_fixedname_init(&fixed); + domain = dns_fixedname_name(&fixed); + dns_name_copy(&fctx->nsfetch->private->domain, domain, NULL); + if (dns_name_equal(&fctx->nsname, domain)) { + fctx_done(fctx, DNS_R_SERVFAIL); + dns_resolver_destroyfetch(&fctx->nsfetch); + goto cleanup; + } + if (dns_rdataset_isassociated( + &fctx->nsfetch->private->nameservers)) { + dns_rdataset_clone( + &fctx->nsfetch->private->nameservers, + &nameservers); + nsrdataset = &nameservers; + } else + domain = NULL; + dns_resolver_destroyfetch(&fctx->nsfetch); + n = dns_name_countlabels(&fctx->nsname); + dns_name_getlabelsequence(&fctx->nsname, 1, n - 1, + &fctx->nsname); + + if (dns_rdataset_isassociated(fevent->rdataset)) + dns_rdataset_disassociate(fevent->rdataset); + FCTXTRACE("continuing to look for parent's NS records"); + result = dns_resolver_createfetch(fctx->res, &fctx->nsname, + dns_rdatatype_ns, domain, + nsrdataset, NULL, 0, task, + resume_dslookup, fctx, + &fctx->nsrrset, NULL, + &fctx->nsfetch); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + else { + LOCK(&res->buckets[bucketnum].lock); + locked = ISC_TRUE; + fctx->references++; + } + } cleanup: - if (dns_rdataset_isassociated(&nameservers)) - dns_rdataset_disassociate(&nameservers); - if (dns_rdataset_isassociated(fevent->rdataset)) - dns_rdataset_disassociate(fevent->rdataset); - INSIST(fevent->sigrdataset == NULL); - isc_event_free(&event); - if (!locked) - LOCK(&res->buckets[bucketnum].lock); - fctx->references--; - if (fctx->references == 0) - bucket_empty = fctx_destroy(fctx); - UNLOCK(&res->buckets[bucketnum].lock); - if (bucket_empty) - empty_bucket(res); + if (dns_rdataset_isassociated(&nameservers)) + dns_rdataset_disassociate(&nameservers); + if (dns_rdataset_isassociated(fevent->rdataset)) + dns_rdataset_disassociate(fevent->rdataset); + INSIST(fevent->sigrdataset == NULL); + isc_event_free(&event); + if (!locked) + LOCK(&res->buckets[bucketnum].lock); + fctx->references--; + if (fctx->references == 0) + bucket_empty = fctx_destroy(fctx); + UNLOCK(&res->buckets[bucketnum].lock); + if (bucket_empty) + empty_bucket(res); } static inline void checknamessection(dns_message_t *message, dns_section_t section) { - isc_result_t result; - dns_name_t *name; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdataset_t *rdataset; - - for (result = dns_message_firstname(message, section); - result == ISC_R_SUCCESS; - result = dns_message_nextname(message, section)) - { - name = NULL; - dns_message_currentname(message, section, &name); - for (rdataset = ISC_LIST_HEAD(name->list); - rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - for (result = dns_rdataset_first(rdataset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(rdataset)) { - dns_rdataset_current(rdataset, &rdata); - if (!dns_rdata_checkowner(name, rdata.rdclass, - rdata.type, - ISC_FALSE) || - !dns_rdata_checknames(&rdata, name, NULL)) - { - rdataset->attributes |= - DNS_RDATASETATTR_CHECKNAMES; - } - dns_rdata_reset(&rdata); - } - } - } + isc_result_t result; + dns_name_t *name; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t *rdataset; + + for (result = dns_message_firstname(message, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(message, section)) + { + name = NULL; + dns_message_currentname(message, section, &name); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_rdataset_current(rdataset, &rdata); + if (!dns_rdata_checkowner(name, rdata.rdclass, + rdata.type, + ISC_FALSE) || + !dns_rdata_checknames(&rdata, name, NULL)) + { + rdataset->attributes |= + DNS_RDATASETATTR_CHECKNAMES; + } + dns_rdata_reset(&rdata); + } + } + } } static void checknames(dns_message_t *message) { - checknamessection(message, DNS_SECTION_ANSWER); - checknamessection(message, DNS_SECTION_AUTHORITY); - checknamessection(message, DNS_SECTION_ADDITIONAL); + checknamessection(message, DNS_SECTION_ANSWER); + checknamessection(message, DNS_SECTION_AUTHORITY); + checknamessection(message, DNS_SECTION_ADDITIONAL); } static void log_packet(dns_message_t *message, int level, isc_mem_t *mctx) { - isc_buffer_t buffer; - char *buf = NULL; - int len = 1024; - isc_result_t result; - - if (! isc_log_wouldlog(dns_lctx, level)) - return; - - /* - * Note that these are multiline debug messages. We want a newline - * to appear in the log after each message. - */ - - do { - buf = isc_mem_get(mctx, len); - if (buf == NULL) - break; - isc_buffer_init(&buffer, buf, len); - result = dns_message_totext(message, &dns_master_style_debug, - 0, &buffer); - if (result == ISC_R_NOSPACE) { - isc_mem_put(mctx, buf, len); - len += 1024; - } else if (result == ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, level, - "received packet:\n%.*s", - (int)isc_buffer_usedlength(&buffer), - buf); - } while (result == ISC_R_NOSPACE); - - if (buf != NULL) - isc_mem_put(mctx, buf, len); + isc_buffer_t buffer; + char *buf = NULL; + int len = 1024; + isc_result_t result; + + if (! isc_log_wouldlog(dns_lctx, level)) + return; + + /* + * Note that these are multiline debug messages. We want a newline + * to appear in the log after each message. + */ + + do { + buf = isc_mem_get(mctx, len); + if (buf == NULL) + break; + isc_buffer_init(&buffer, buf, len); + result = dns_message_totext(message, &dns_master_style_debug, + 0, &buffer); + if (result == ISC_R_NOSPACE) { + isc_mem_put(mctx, buf, len); + len += 1024; + } else if (result == ISC_R_SUCCESS) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, level, + "received packet:\n%.*s", + (int)isc_buffer_usedlength(&buffer), + buf); + } while (result == ISC_R_NOSPACE); + + if (buf != NULL) + isc_mem_put(mctx, buf, len); } static void resquery_response(isc_task_t *task, isc_event_t *event) { - isc_result_t result = ISC_R_SUCCESS; - resquery_t *query = event->ev_arg; - dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event; - isc_boolean_t keep_trying, get_nameservers, resend; - isc_boolean_t truncated; - dns_message_t *message; - fetchctx_t *fctx; - dns_name_t *fname; - dns_fixedname_t foundname; - isc_stdtime_t now; - isc_time_t tnow, *finish; - dns_adbaddrinfo_t *addrinfo; - unsigned int options; - unsigned int findoptions; - isc_result_t broken_server; - - REQUIRE(VALID_QUERY(query)); - fctx = query->fctx; - options = query->options; - REQUIRE(VALID_FCTX(fctx)); - REQUIRE(event->ev_type == DNS_EVENT_DISPATCH); - - QTRACE("response"); - - (void)isc_timer_touch(fctx->timer); - - keep_trying = ISC_FALSE; - broken_server = ISC_R_SUCCESS; - get_nameservers = ISC_FALSE; - resend = ISC_FALSE; - truncated = ISC_FALSE; - finish = NULL; - - if (fctx->res->exiting) { - result = ISC_R_SHUTTINGDOWN; - goto done; - } - - fctx->timeouts = 0; - - /* - * XXXRTH We should really get the current time just once. We - * need a routine to convert from an isc_time_t to an - * isc_stdtime_t. - */ - TIME_NOW(&tnow); - finish = &tnow; - isc_stdtime_get(&now); - - /* - * Did the dispatcher have a problem? - */ - if (devent->result != ISC_R_SUCCESS) { - if (devent->result == ISC_R_EOF && - (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { - /* - * The problem might be that they - * don't understand EDNS0. Turn it - * off and try again. - */ - options |= DNS_FETCHOPT_NOEDNS0; - resend = ISC_TRUE; - /* - * Remember that they don't like EDNS0. - */ - dns_adb_changeflags(fctx->adb, - query->addrinfo, - DNS_FETCHOPT_NOEDNS0, - DNS_FETCHOPT_NOEDNS0); - } else { - /* - * There's no hope for this query. - */ - keep_trying = ISC_TRUE; - } - goto done; - } - - message = fctx->rmessage; - - if (query->tsig != NULL) { - result = dns_message_setquerytsig(message, query->tsig); - if (result != ISC_R_SUCCESS) - goto done; - } - - if (query->tsigkey) { - result = dns_message_settsigkey(message, query->tsigkey); - if (result != ISC_R_SUCCESS) - goto done; - } - - result = dns_message_parse(message, &devent->buffer, 0); - if (result != ISC_R_SUCCESS) { - switch (result) { - case ISC_R_UNEXPECTEDEND: - if (!message->question_ok || - (message->flags & DNS_MESSAGEFLAG_TC) == 0 || - (options & DNS_FETCHOPT_TCP) != 0) { - /* - * Either the message ended prematurely, - * and/or wasn't marked as being truncated, - * and/or this is a response to a query we - * sent over TCP. In all of these cases, - * something is wrong with the remote - * server and we don't want to retry using - * TCP. - */ - if ((query->options & DNS_FETCHOPT_NOEDNS0) - == 0) { - /* - * The problem might be that they - * don't understand EDNS0. Turn it - * off and try again. - */ - options |= DNS_FETCHOPT_NOEDNS0; - resend = ISC_TRUE; - /* - * Remember that they don't like EDNS0. - */ - dns_adb_changeflags( - fctx->adb, - query->addrinfo, - DNS_FETCHOPT_NOEDNS0, - DNS_FETCHOPT_NOEDNS0); - } else { - broken_server = result; - keep_trying = ISC_TRUE; - } - goto done; - } - /* - * We defer retrying via TCP for a bit so we can - * check out this message further. - */ - truncated = ISC_TRUE; - break; - case DNS_R_FORMERR: - if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { - /* - * The problem might be that they - * don't understand EDNS0. Turn it - * off and try again. - */ - options |= DNS_FETCHOPT_NOEDNS0; - resend = ISC_TRUE; - /* - * Remember that they don't like EDNS0. - */ - dns_adb_changeflags(fctx->adb, - query->addrinfo, - DNS_FETCHOPT_NOEDNS0, - DNS_FETCHOPT_NOEDNS0); - } else { - broken_server = DNS_R_UNEXPECTEDRCODE; - keep_trying = ISC_TRUE; - } - goto done; - default: - /* - * Something bad has happened. - */ - goto done; - } - } - - /* - * Log the incoming packet. - */ - log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx); - - /* - * If the message is signed, check the signature. If not, this - * returns success anyway. - */ - result = dns_message_checksig(message, fctx->res->view); - if (result != ISC_R_SUCCESS) - goto done; - - /* - * The dispatcher should ensure we only get responses with QR set. - */ - INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0); - /* - * INSIST() that the message comes from the place we sent it to, - * since the dispatch code should ensure this. - * - * INSIST() that the message id is correct (this should also be - * ensured by the dispatch code). - */ - - - /* - * Deal with truncated responses by retrying using TCP. - */ - if ((message->flags & DNS_MESSAGEFLAG_TC) != 0) - truncated = ISC_TRUE; - - if (truncated) { - if ((options & DNS_FETCHOPT_TCP) != 0) { - broken_server = DNS_R_TRUNCATEDTCP; - keep_trying = ISC_TRUE; - } else { - options |= DNS_FETCHOPT_TCP; - resend = ISC_TRUE; - } - goto done; - } - - /* - * Is it a query response? - */ - if (message->opcode != dns_opcode_query) { - /* XXXRTH Log */ - broken_server = DNS_R_UNEXPECTEDOPCODE; - keep_trying = ISC_TRUE; - goto done; - } - - /* - * Is the remote server broken, or does it dislike us? - */ - if (message->rcode != dns_rcode_noerror && - message->rcode != dns_rcode_nxdomain) { - if ((message->rcode == dns_rcode_formerr || - message->rcode == dns_rcode_notimp || - message->rcode == dns_rcode_servfail) && - (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { - /* - * It's very likely they don't like EDNS0. - * - * XXXRTH We should check if the question - * we're asking requires EDNS0, and - * if so, we should bail out. - */ - options |= DNS_FETCHOPT_NOEDNS0; - resend = ISC_TRUE; - /* - * Remember that they don't like EDNS0. - */ - if (message->rcode != dns_rcode_servfail) - dns_adb_changeflags(fctx->adb, query->addrinfo, - DNS_FETCHOPT_NOEDNS0, - DNS_FETCHOPT_NOEDNS0); - } else if (message->rcode == dns_rcode_formerr) { - if (ISFORWARDER(query->addrinfo)) { - /* - * This forwarder doesn't understand us, - * but other forwarders might. Keep trying. - */ - broken_server = DNS_R_REMOTEFORMERR; - keep_trying = ISC_TRUE; - } else { - /* - * The server doesn't understand us. Since - * all servers for a zone need similar - * capabilities, we assume that we will get - * FORMERR from all servers, and thus we - * cannot make any more progress with this - * fetch. - */ - result = DNS_R_FORMERR; - } - } else if (message->rcode == dns_rcode_yxdomain) { - /* - * DNAME mapping failed because the new name - * was too long. There's no chance of success - * for this fetch. - */ - result = DNS_R_YXDOMAIN; - } else if (message->rcode == dns_rcode_badvers) { - dns_rdataset_t *opt; - unsigned int flags, mask; - unsigned int version; - - resend = ISC_TRUE; - opt = dns_message_getopt(message); - version = (opt->ttl >> 16) & 0xff; - flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) | - DNS_FETCHOPT_EDNSVERSIONSET; - mask = DNS_FETCHOPT_EDNSVERSIONMASK | - DNS_FETCHOPT_EDNSVERSIONSET; - switch (version) { - case 0: - dns_adb_changeflags(fctx->adb, query->addrinfo, - flags, mask); - break; - default: - broken_server = DNS_R_BADVERS; - keep_trying = ISC_TRUE; - break; - } - } else { - /* - * XXXRTH log. - */ - broken_server = DNS_R_UNEXPECTEDRCODE; - INSIST(broken_server != ISC_R_SUCCESS); - keep_trying = ISC_TRUE; - } - goto done; - } - - /* - * Is the question the same as the one we asked? - */ - result = same_question(fctx); - if (result != ISC_R_SUCCESS) { - /* XXXRTH Log */ - if (result == DNS_R_FORMERR) - keep_trying = ISC_TRUE; - goto done; - } - - /* - * Is the server lame? - */ - if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) && - is_lame(fctx)) { - log_lame(fctx, query->addrinfo); - result = dns_adb_marklame(fctx->adb, query->addrinfo, - &fctx->name, fctx->type, - now + fctx->res->lame_ttl); - if (result != ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, - "could not mark server as lame: %s", - isc_result_totext(result)); - broken_server = DNS_R_LAME; - keep_trying = ISC_TRUE; - goto done; - } - - /* - * Enforce delegations only zones like NET and COM. - */ - if (!ISFORWARDER(query->addrinfo) && - dns_view_isdelegationonly(fctx->res->view, &fctx->domain) && - !dns_name_equal(&fctx->domain, &fctx->name) && - fix_mustbedelegationornxdomain(message, fctx)) { - char namebuf[DNS_NAME_FORMATSIZE]; - char domainbuf[DNS_NAME_FORMATSIZE]; - char addrbuf[ISC_SOCKADDR_FORMATSIZE]; - char classbuf[64]; - char typebuf[64]; - - dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); - dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); - dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); - dns_rdataclass_format(fctx->res->rdclass, classbuf, - sizeof(classbuf)); - isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, - sizeof(addrbuf)); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY, - DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, - "enforced delegation-only for '%s' (%s/%s/%s) " - "from %s", - domainbuf, namebuf, typebuf, classbuf, addrbuf); - } - - if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0) - checknames(message); - - /* - * Clear cache bits. - */ - fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE); - - /* - * Did we get any answers? - */ - if (message->counts[DNS_SECTION_ANSWER] > 0 && - (message->rcode == dns_rcode_noerror || - message->rcode == dns_rcode_nxdomain)) { - /* - * We've got answers. However, if we sent - * a BIND 8 server an NS query, it may have - * incorrectly responded with a non-authoritative - * answer instead of a referral. Since this - * answer lacks the SIGs necessary to do DNSSEC - * validation, we must invoke the following special - * kludge to treat it as a referral. - */ - if (fctx->type == dns_rdatatype_ns && - (message->flags & DNS_MESSAGEFLAG_AA) == 0 && - !ISFORWARDER(query->addrinfo)) - { - result = noanswer_response(fctx, NULL, ISC_TRUE); - if (result != DNS_R_DELEGATION) { - /* - * The answer section must have contained - * something other than the NS records - * we asked for. Since AA is not set - * and the server is not a forwarder, - * it is technically lame and it's easier - * to treat it as such than to figure out - * some more elaborate course of action. - */ - broken_server = DNS_R_LAME; - keep_trying = ISC_TRUE; - goto done; - } - goto force_referral; - } - result = answer_response(fctx); - if (result != ISC_R_SUCCESS) { - if (result == DNS_R_FORMERR) - keep_trying = ISC_TRUE; - goto done; - } - } else if (message->counts[DNS_SECTION_AUTHORITY] > 0 || - message->rcode == dns_rcode_noerror || - message->rcode == dns_rcode_nxdomain) { - /* - * NXDOMAIN, NXRDATASET, or referral. - */ - result = noanswer_response(fctx, NULL, ISC_FALSE); - if (result == DNS_R_CHASEDSSERVERS) { - } else if (result == DNS_R_DELEGATION) { - force_referral: - /* - * We don't have the answer, but we know a better - * place to look. - */ - get_nameservers = ISC_TRUE; - keep_trying = ISC_TRUE; - /* - * We have a new set of name servers, and it - * has not experienced any restarts yet. - */ - fctx->restarts = 0; - result = ISC_R_SUCCESS; - } else if (result != ISC_R_SUCCESS) { - /* - * Something has gone wrong. - */ - if (result == DNS_R_FORMERR) - keep_trying = ISC_TRUE; - goto done; - } - } else { - /* - * The server is insane. - */ - /* XXXRTH Log */ - broken_server = DNS_R_UNEXPECTEDRCODE; - keep_trying = ISC_TRUE; - goto done; - } - - /* - * Follow additional section data chains. - */ - chase_additional(fctx); - - /* - * Cache the cacheable parts of the message. This may also cause - * work to be queued to the DNSSEC validator. - */ - if (WANTCACHE(fctx)) { - result = cache_message(fctx, query->addrinfo, now); - if (result != ISC_R_SUCCESS) - goto done; - } - - /* - * Ncache the negatively cacheable parts of the message. This may - * also cause work to be queued to the DNSSEC validator. - */ - if (WANTNCACHE(fctx)) { - dns_rdatatype_t covers; - if (message->rcode == dns_rcode_nxdomain) - covers = dns_rdatatype_any; - else - covers = fctx->type; - - /* - * Cache any negative cache entries in the message. - */ - result = ncache_message(fctx, query->addrinfo, covers, now); - } + isc_result_t result = ISC_R_SUCCESS; + resquery_t *query = event->ev_arg; + dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event; + isc_boolean_t keep_trying, get_nameservers, resend; + isc_boolean_t truncated; + dns_message_t *message; + fetchctx_t *fctx; + dns_name_t *fname; + dns_fixedname_t foundname; + isc_stdtime_t now; + isc_time_t tnow, *finish; + dns_adbaddrinfo_t *addrinfo; + unsigned int options; + unsigned int findoptions; + isc_result_t broken_server; + + REQUIRE(VALID_QUERY(query)); + fctx = query->fctx; + options = query->options; + REQUIRE(VALID_FCTX(fctx)); + REQUIRE(event->ev_type == DNS_EVENT_DISPATCH); + + QTRACE("response"); + + (void)isc_timer_touch(fctx->timer); + + keep_trying = ISC_FALSE; + broken_server = ISC_R_SUCCESS; + get_nameservers = ISC_FALSE; + resend = ISC_FALSE; + truncated = ISC_FALSE; + finish = NULL; + + if (fctx->res->exiting) { + result = ISC_R_SHUTTINGDOWN; + goto done; + } + + fctx->timeouts = 0; + + /* + * XXXRTH We should really get the current time just once. We + * need a routine to convert from an isc_time_t to an + * isc_stdtime_t. + */ + TIME_NOW(&tnow); + finish = &tnow; + isc_stdtime_get(&now); + + /* + * Did the dispatcher have a problem? + */ + if (devent->result != ISC_R_SUCCESS) { + if (devent->result == ISC_R_EOF && + (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + /* + * The problem might be that they + * don't understand EDNS0. Turn it + * off and try again. + */ + options |= DNS_FETCHOPT_NOEDNS0; + resend = ISC_TRUE; + /* + * Remember that they don't like EDNS0. + */ + dns_adb_changeflags(fctx->adb, + query->addrinfo, + DNS_FETCHOPT_NOEDNS0, + DNS_FETCHOPT_NOEDNS0); + } else { + /* + * There's no hope for this query. + */ + keep_trying = ISC_TRUE; + } + goto done; + } + + message = fctx->rmessage; + + if (query->tsig != NULL) { + result = dns_message_setquerytsig(message, query->tsig); + if (result != ISC_R_SUCCESS) + goto done; + } + + if (query->tsigkey) { + result = dns_message_settsigkey(message, query->tsigkey); + if (result != ISC_R_SUCCESS) + goto done; + } + + result = dns_message_parse(message, &devent->buffer, 0); + if (result != ISC_R_SUCCESS) { + switch (result) { + case ISC_R_UNEXPECTEDEND: + if (!message->question_ok || + (message->flags & DNS_MESSAGEFLAG_TC) == 0 || + (options & DNS_FETCHOPT_TCP) != 0) { + /* + * Either the message ended prematurely, + * and/or wasn't marked as being truncated, + * and/or this is a response to a query we + * sent over TCP. In all of these cases, + * something is wrong with the remote + * server and we don't want to retry using + * TCP. + */ + if ((query->options & DNS_FETCHOPT_NOEDNS0) + == 0) { + /* + * The problem might be that they + * don't understand EDNS0. Turn it + * off and try again. + */ + options |= DNS_FETCHOPT_NOEDNS0; + resend = ISC_TRUE; + /* + * Remember that they don't like EDNS0. + */ + dns_adb_changeflags( + fctx->adb, + query->addrinfo, + DNS_FETCHOPT_NOEDNS0, + DNS_FETCHOPT_NOEDNS0); + } else { + broken_server = result; + keep_trying = ISC_TRUE; + } + goto done; + } + /* + * We defer retrying via TCP for a bit so we can + * check out this message further. + */ + truncated = ISC_TRUE; + break; + case DNS_R_FORMERR: + if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + /* + * The problem might be that they + * don't understand EDNS0. Turn it + * off and try again. + */ + options |= DNS_FETCHOPT_NOEDNS0; + resend = ISC_TRUE; + /* + * Remember that they don't like EDNS0. + */ + dns_adb_changeflags(fctx->adb, + query->addrinfo, + DNS_FETCHOPT_NOEDNS0, + DNS_FETCHOPT_NOEDNS0); + } else { + broken_server = DNS_R_UNEXPECTEDRCODE; + keep_trying = ISC_TRUE; + } + goto done; + default: + /* + * Something bad has happened. + */ + goto done; + } + } + + /* + * Log the incoming packet. + */ + log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx); + + /* + * If the message is signed, check the signature. If not, this + * returns success anyway. + */ + result = dns_message_checksig(message, fctx->res->view); + if (result != ISC_R_SUCCESS) + goto done; + + /* + * The dispatcher should ensure we only get responses with QR set. + */ + INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0); + /* + * INSIST() that the message comes from the place we sent it to, + * since the dispatch code should ensure this. + * + * INSIST() that the message id is correct (this should also be + * ensured by the dispatch code). + */ + + + /* + * Deal with truncated responses by retrying using TCP. + */ + if ((message->flags & DNS_MESSAGEFLAG_TC) != 0) + truncated = ISC_TRUE; + + if (truncated) { + if ((options & DNS_FETCHOPT_TCP) != 0) { + broken_server = DNS_R_TRUNCATEDTCP; + keep_trying = ISC_TRUE; + } else { + options |= DNS_FETCHOPT_TCP; + resend = ISC_TRUE; + } + goto done; + } + + /* + * Is it a query response? + */ + if (message->opcode != dns_opcode_query) { + /* XXXRTH Log */ + broken_server = DNS_R_UNEXPECTEDOPCODE; + keep_trying = ISC_TRUE; + goto done; + } + + /* + * Is the remote server broken, or does it dislike us? + */ + if (message->rcode != dns_rcode_noerror && + message->rcode != dns_rcode_nxdomain) { + if ((message->rcode == dns_rcode_formerr || + message->rcode == dns_rcode_notimp || + message->rcode == dns_rcode_servfail) && + (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + /* + * It's very likely they don't like EDNS0. + * + * XXXRTH We should check if the question + * we're asking requires EDNS0, and + * if so, we should bail out. + */ + options |= DNS_FETCHOPT_NOEDNS0; + resend = ISC_TRUE; + /* + * Remember that they don't like EDNS0. + */ + if (message->rcode != dns_rcode_servfail) + dns_adb_changeflags(fctx->adb, query->addrinfo, + DNS_FETCHOPT_NOEDNS0, + DNS_FETCHOPT_NOEDNS0); + } else if (message->rcode == dns_rcode_formerr) { + if (ISFORWARDER(query->addrinfo)) { + /* + * This forwarder doesn't understand us, + * but other forwarders might. Keep trying. + */ + broken_server = DNS_R_REMOTEFORMERR; + keep_trying = ISC_TRUE; + } else { + /* + * The server doesn't understand us. Since + * all servers for a zone need similar + * capabilities, we assume that we will get + * FORMERR from all servers, and thus we + * cannot make any more progress with this + * fetch. + */ + result = DNS_R_FORMERR; + } + } else if (message->rcode == dns_rcode_yxdomain) { + /* + * DNAME mapping failed because the new name + * was too long. There's no chance of success + * for this fetch. + */ + result = DNS_R_YXDOMAIN; + } else if (message->rcode == dns_rcode_badvers) { + dns_rdataset_t *opt; + unsigned int flags, mask; + unsigned int version; + + resend = ISC_TRUE; + opt = dns_message_getopt(message); + version = (opt->ttl >> 16) & 0xff; + flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) | + DNS_FETCHOPT_EDNSVERSIONSET; + mask = DNS_FETCHOPT_EDNSVERSIONMASK | + DNS_FETCHOPT_EDNSVERSIONSET; + switch (version) { + case 0: + dns_adb_changeflags(fctx->adb, query->addrinfo, + flags, mask); + break; + default: + broken_server = DNS_R_BADVERS; + keep_trying = ISC_TRUE; + break; + } + } else { + /* + * XXXRTH log. + */ + broken_server = DNS_R_UNEXPECTEDRCODE; + INSIST(broken_server != ISC_R_SUCCESS); + keep_trying = ISC_TRUE; + } + goto done; + } + + /* + * Is the question the same as the one we asked? + */ + result = same_question(fctx); + if (result != ISC_R_SUCCESS) { + /* XXXRTH Log */ + if (result == DNS_R_FORMERR) + keep_trying = ISC_TRUE; + goto done; + } + + /* + * Is the server lame? + */ + if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) && + is_lame(fctx)) { + log_lame(fctx, query->addrinfo); + result = dns_adb_marklame(fctx->adb, query->addrinfo, + &fctx->name, fctx->type, + now + fctx->res->lame_ttl); + if (result != ISC_R_SUCCESS) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, + "could not mark server as lame: %s", + isc_result_totext(result)); + broken_server = DNS_R_LAME; + keep_trying = ISC_TRUE; + goto done; + } + + /* + * Enforce delegations only zones like NET and COM. + */ + if (!ISFORWARDER(query->addrinfo) && + dns_view_isdelegationonly(fctx->res->view, &fctx->domain) && + !dns_name_equal(&fctx->domain, &fctx->name) && + fix_mustbedelegationornxdomain(message, fctx)) { + char namebuf[DNS_NAME_FORMATSIZE]; + char domainbuf[DNS_NAME_FORMATSIZE]; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + char classbuf[64]; + char typebuf[64]; + + dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); + dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); + dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); + dns_rdataclass_format(fctx->res->rdclass, classbuf, + sizeof(classbuf)); + isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, + sizeof(addrbuf)); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY, + DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, + "enforced delegation-only for '%s' (%s/%s/%s) " + "from %s", + domainbuf, namebuf, typebuf, classbuf, addrbuf); + } + + if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0) + checknames(message); + + /* + * Clear cache bits. + */ + fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE); + + /* + * Did we get any answers? + */ + if (message->counts[DNS_SECTION_ANSWER] > 0 && + (message->rcode == dns_rcode_noerror || + message->rcode == dns_rcode_nxdomain)) { + /* + * We've got answers. However, if we sent + * a BIND 8 server an NS query, it may have + * incorrectly responded with a non-authoritative + * answer instead of a referral. Since this + * answer lacks the SIGs necessary to do DNSSEC + * validation, we must invoke the following special + * kludge to treat it as a referral. + */ + if (fctx->type == dns_rdatatype_ns && + (message->flags & DNS_MESSAGEFLAG_AA) == 0 && + !ISFORWARDER(query->addrinfo)) + { + result = noanswer_response(fctx, NULL, ISC_TRUE); + if (result != DNS_R_DELEGATION) { + /* + * The answer section must have contained + * something other than the NS records + * we asked for. Since AA is not set + * and the server is not a forwarder, + * it is technically lame and it's easier + * to treat it as such than to figure out + * some more elaborate course of action. + */ + broken_server = DNS_R_LAME; + keep_trying = ISC_TRUE; + goto done; + } + goto force_referral; + } + result = answer_response(fctx); + if (result != ISC_R_SUCCESS) { + if (result == DNS_R_FORMERR) + keep_trying = ISC_TRUE; + goto done; + } + } else if (message->counts[DNS_SECTION_AUTHORITY] > 0 || + message->rcode == dns_rcode_noerror || + message->rcode == dns_rcode_nxdomain) { + /* + * NXDOMAIN, NXRDATASET, or referral. + */ + result = noanswer_response(fctx, NULL, ISC_FALSE); + if (result == DNS_R_CHASEDSSERVERS) { + } else if (result == DNS_R_DELEGATION) { + force_referral: + /* + * We don't have the answer, but we know a better + * place to look. + */ + get_nameservers = ISC_TRUE; + keep_trying = ISC_TRUE; + /* + * We have a new set of name servers, and it + * has not experienced any restarts yet. + */ + fctx->restarts = 0; + result = ISC_R_SUCCESS; + } else if (result != ISC_R_SUCCESS) { + /* + * Something has gone wrong. + */ + if (result == DNS_R_FORMERR) + keep_trying = ISC_TRUE; + goto done; + } + } else { + /* + * The server is insane. + */ + /* XXXRTH Log */ + broken_server = DNS_R_UNEXPECTEDRCODE; + keep_trying = ISC_TRUE; + goto done; + } + + /* + * Follow additional section data chains. + */ + chase_additional(fctx); + + /* + * Cache the cacheable parts of the message. This may also cause + * work to be queued to the DNSSEC validator. + */ + if (WANTCACHE(fctx)) { + result = cache_message(fctx, query->addrinfo, now); + if (result != ISC_R_SUCCESS) + goto done; + } + + /* + * Ncache the negatively cacheable parts of the message. This may + * also cause work to be queued to the DNSSEC validator. + */ + if (WANTNCACHE(fctx)) { + dns_rdatatype_t covers; + if (message->rcode == dns_rcode_nxdomain) + covers = dns_rdatatype_any; + else + covers = fctx->type; + + /* + * Cache any negative cache entries in the message. + */ + result = ncache_message(fctx, query->addrinfo, covers, now); + } done: - /* - * Remember the query's addrinfo, in case we need to mark the - * server as broken. - */ - addrinfo = query->addrinfo; - - /* - * Cancel the query. - * - * XXXRTH Don't cancel the query if waiting for validation? - */ - fctx_cancelquery(&query, &devent, finish, ISC_FALSE); - - if (keep_trying) { - if (result == DNS_R_FORMERR) - broken_server = DNS_R_FORMERR; - if (broken_server != ISC_R_SUCCESS) { - /* - * Add this server to the list of bad servers for - * this fctx. - */ - add_bad(fctx, addrinfo, broken_server); - } - - if (get_nameservers) { - dns_name_t *name; - dns_fixedname_init(&foundname); - fname = dns_fixedname_name(&foundname); - if (result != ISC_R_SUCCESS) { - fctx_done(fctx, DNS_R_SERVFAIL); - return; - } - findoptions = 0; - if (dns_rdatatype_atparent(fctx->type)) - findoptions |= DNS_DBFIND_NOEXACT; - if ((options & DNS_FETCHOPT_UNSHARED) == 0) - name = &fctx->name; - else - name = &fctx->domain; - result = dns_view_findzonecut(fctx->res->view, - name, fname, - now, findoptions, - ISC_TRUE, - &fctx->nameservers, - NULL); - if (result != ISC_R_SUCCESS) { - FCTXTRACE("couldn't find a zonecut"); - fctx_done(fctx, DNS_R_SERVFAIL); - return; - } - if (!dns_name_issubdomain(fname, &fctx->domain)) { - /* - * The best nameservers are now above our - * QDOMAIN. - */ - FCTXTRACE("nameservers now above QDOMAIN"); - fctx_done(fctx, DNS_R_SERVFAIL); - return; - } - dns_name_free(&fctx->domain, - fctx->res->buckets[fctx->bucketnum].mctx); - dns_name_init(&fctx->domain, NULL); - result = dns_name_dup(fname, - fctx->res->buckets[fctx->bucketnum].mctx, - &fctx->domain); - if (result != ISC_R_SUCCESS) { - fctx_done(fctx, DNS_R_SERVFAIL); - return; - } - fctx_cancelqueries(fctx, ISC_TRUE); - fctx_cleanupfinds(fctx); - fctx_cleanupaltfinds(fctx); - fctx_cleanupforwaddrs(fctx); - fctx_cleanupaltaddrs(fctx); - } - /* - * Try again. - */ - fctx_try(fctx); - } else if (resend) { - /* - * Resend (probably with changed options). - */ - FCTXTRACE("resend"); - result = fctx_query(fctx, addrinfo, options); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - } else if (result == ISC_R_SUCCESS && !HAVE_ANSWER(fctx)) { - /* - * All has gone well so far, but we are waiting for the - * DNSSEC validator to validate the answer. - */ - FCTXTRACE("wait for validator"); - fctx_cancelqueries(fctx, ISC_TRUE); - /* - * We must not retransmit while the validator is working; - * it has references to the current rmessage. - */ - result = fctx_stopidletimer(fctx); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - } else if (result == DNS_R_CHASEDSSERVERS) { - unsigned int n; - add_bad(fctx, addrinfo, result); - fctx_cancelqueries(fctx, ISC_TRUE); - fctx_cleanupfinds(fctx); - fctx_cleanupforwaddrs(fctx); - - n = dns_name_countlabels(&fctx->name); - dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname); - - FCTXTRACE("suspending DS lookup to find parent's NS records"); - - result = dns_resolver_createfetch(fctx->res, &fctx->nsname, - dns_rdatatype_ns, - NULL, NULL, NULL, 0, task, - resume_dslookup, fctx, - &fctx->nsrrset, NULL, - &fctx->nsfetch); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - LOCK(&fctx->res->buckets[fctx->bucketnum].lock); - fctx->references++; - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - result = fctx_stopidletimer(fctx); - if (result != ISC_R_SUCCESS) - fctx_done(fctx, result); - } else { - /* - * We're done. - */ - fctx_done(fctx, result); - } + /* + * Remember the query's addrinfo, in case we need to mark the + * server as broken. + */ + addrinfo = query->addrinfo; + + /* + * Cancel the query. + * + * XXXRTH Don't cancel the query if waiting for validation? + */ + fctx_cancelquery(&query, &devent, finish, ISC_FALSE); + + if (keep_trying) { + if (result == DNS_R_FORMERR) + broken_server = DNS_R_FORMERR; + if (broken_server != ISC_R_SUCCESS) { + /* + * Add this server to the list of bad servers for + * this fctx. + */ + add_bad(fctx, addrinfo, broken_server); + } + + if (get_nameservers) { + dns_name_t *name; + dns_fixedname_init(&foundname); + fname = dns_fixedname_name(&foundname); + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, DNS_R_SERVFAIL); + return; + } + findoptions = 0; + if (dns_rdatatype_atparent(fctx->type)) + findoptions |= DNS_DBFIND_NOEXACT; + if ((options & DNS_FETCHOPT_UNSHARED) == 0) + name = &fctx->name; + else + name = &fctx->domain; + result = dns_view_findzonecut(fctx->res->view, + name, fname, + now, findoptions, + ISC_TRUE, + &fctx->nameservers, + NULL); + if (result != ISC_R_SUCCESS) { + FCTXTRACE("couldn't find a zonecut"); + fctx_done(fctx, DNS_R_SERVFAIL); + return; + } + if (!dns_name_issubdomain(fname, &fctx->domain)) { + /* + * The best nameservers are now above our + * QDOMAIN. + */ + FCTXTRACE("nameservers now above QDOMAIN"); + fctx_done(fctx, DNS_R_SERVFAIL); + return; + } + dns_name_free(&fctx->domain, + fctx->res->buckets[fctx->bucketnum].mctx); + dns_name_init(&fctx->domain, NULL); + result = dns_name_dup(fname, + fctx->res->buckets[fctx->bucketnum].mctx, + &fctx->domain); + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, DNS_R_SERVFAIL); + return; + } + fctx_cancelqueries(fctx, ISC_TRUE); + fctx_cleanupfinds(fctx); + fctx_cleanupaltfinds(fctx); + fctx_cleanupforwaddrs(fctx); + fctx_cleanupaltaddrs(fctx); + } + /* + * Try again. + */ + fctx_try(fctx); + } else if (resend) { + /* + * Resend (probably with changed options). + */ + FCTXTRACE("resend"); + result = fctx_query(fctx, addrinfo, options); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + } else if (result == ISC_R_SUCCESS && !HAVE_ANSWER(fctx)) { + /* + * All has gone well so far, but we are waiting for the + * DNSSEC validator to validate the answer. + */ + FCTXTRACE("wait for validator"); + fctx_cancelqueries(fctx, ISC_TRUE); + /* + * We must not retransmit while the validator is working; + * it has references to the current rmessage. + */ + result = fctx_stopidletimer(fctx); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + } else if (result == DNS_R_CHASEDSSERVERS) { + unsigned int n; + add_bad(fctx, addrinfo, result); + fctx_cancelqueries(fctx, ISC_TRUE); + fctx_cleanupfinds(fctx); + fctx_cleanupforwaddrs(fctx); + + n = dns_name_countlabels(&fctx->name); + dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname); + + FCTXTRACE("suspending DS lookup to find parent's NS records"); + + result = dns_resolver_createfetch(fctx->res, &fctx->nsname, + dns_rdatatype_ns, + NULL, NULL, NULL, 0, task, + resume_dslookup, fctx, + &fctx->nsrrset, NULL, + &fctx->nsfetch); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + LOCK(&fctx->res->buckets[fctx->bucketnum].lock); + fctx->references++; + UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + result = fctx_stopidletimer(fctx); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + } else { + /* + * We're done. + */ + fctx_done(fctx, result); + } } @@ -6034,1441 +6121,1514 @@ resquery_response(isc_task_t *task, isc_event_t *event) { static void destroy(dns_resolver_t *res) { - unsigned int i; - alternate_t *a; - - REQUIRE(res->references == 0); - REQUIRE(!res->priming); - REQUIRE(res->primefetch == NULL); - - RTRACE("destroy"); - - INSIST(res->nfctx == 0); - - RES_DESTROYLOCK(&res->poollock); - DESTROYLOCK(&res->primelock); - DESTROYLOCK(&res->nlock); - DESTROYLOCK(&res->lock); - for (i = 0; i < res->nbuckets; i++) { - INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs)); - isc_task_shutdown(res->buckets[i].task); - isc_task_detach(&res->buckets[i].task); - DESTROYLOCK(&res->buckets[i].lock); - isc_mem_detach(&res->buckets[i].mctx); - } - isc_mem_put(res->mctx, res->buckets, - res->nbuckets * sizeof(fctxbucket_t)); - if (res->dispatchv4 != NULL) - dns_dispatch_detach(&res->dispatchv4); - if (res->dispatchv6 != NULL) - dns_dispatch_detach(&res->dispatchv6); - if (res->dispatchv4pool != NULL) { - for (i = 0; i < res->ndisps; i++) - dns_dispatch_detach(&res->dispatchv4pool[i]); - isc_mem_put(res->mctx, res->dispatchv4pool, - res->ndisps * sizeof(dns_dispatch_t *)); - } - if (res->dispatchv6pool != NULL) { - for (i = 0; i < res->ndisps; i++) - dns_dispatch_detach(&res->dispatchv6pool[i]); - isc_mem_put(res->mctx, res->dispatchv6pool, - res->ndisps * sizeof(dns_dispatch_t *)); - } - while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) { - ISC_LIST_UNLINK(res->alternates, a, link); - if (!a->isaddress) - dns_name_free(&a->_u._n.name, res->mctx); - isc_mem_put(res->mctx, a, sizeof(*a)); - } - if (res->disppooltimer != NULL) - isc_timer_detach(&res->disppooltimer); - dns_resolver_reset_algorithms(res); - dns_resolver_resetmustbesecure(res); + unsigned int i; + alternate_t *a; + + REQUIRE(res->references == 0); + REQUIRE(!res->priming); + REQUIRE(res->primefetch == NULL); + + RTRACE("destroy"); + + INSIST(res->nfctx == 0); + +#ifdef LRU_DEBUG + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, + "destroying resolver %p: external queries " + "total/NS/SOA/A/AAAA=%u/%u/%u/%u/%u", + res, res->extqueries, res->extqueries_ns, + res->extqueries_soa, res->extqueries_a, + res->extqueries_aaaa); +#endif + + RES_DESTROYLOCK(&res->poollock); + DESTROYLOCK(&res->primelock); + DESTROYLOCK(&res->nlock); + DESTROYLOCK(&res->lock); + for (i = 0; i < res->nbuckets; i++) { + INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs)); + isc_task_shutdown(res->buckets[i].task); + isc_task_detach(&res->buckets[i].task); + DESTROYLOCK(&res->buckets[i].lock); + isc_mem_detach(&res->buckets[i].mctx); + } + isc_mem_put(res->mctx, res->buckets, + res->nbuckets * sizeof(fctxbucket_t)); + if (res->dispatchv4 != NULL) + dns_dispatch_detach(&res->dispatchv4); + if (res->dispatchv6 != NULL) + dns_dispatch_detach(&res->dispatchv6); + if (res->dispatchv4pool != NULL) { + for (i = 0; i < res->ndisps; i++) + dns_dispatch_detach(&res->dispatchv4pool[i]); + isc_mem_put(res->mctx, res->dispatchv4pool, + res->ndisps * sizeof(dns_dispatch_t *)); + } + if (res->dispatchv6pool != NULL) { + for (i = 0; i < res->ndisps; i++) + dns_dispatch_detach(&res->dispatchv6pool[i]); + isc_mem_put(res->mctx, res->dispatchv6pool, + res->ndisps * sizeof(dns_dispatch_t *)); + } + while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) { + ISC_LIST_UNLINK(res->alternates, a, link); + if (!a->isaddress) + dns_name_free(&a->_u._n.name, res->mctx); + isc_mem_put(res->mctx, a, sizeof(*a)); + } + if (res->disppooltimer != NULL) + isc_timer_detach(&res->disppooltimer); +#ifdef LRU_DEBUG + if (res->dumptimer != NULL) + isc_timer_detach(&res->dumptimer); +#endif + dns_resolver_reset_algorithms(res); + dns_resolver_resetmustbesecure(res); #if USE_ALGLOCK - isc_rwlock_destroy(&res->alglock); + isc_rwlock_destroy(&res->alglock); #endif #if USE_MBSLOCK - isc_rwlock_destroy(&res->mbslock); + isc_rwlock_destroy(&res->mbslock); #endif - isc_timer_detach(&res->spillattimer); - res->magic = 0; - isc_mem_put(res->mctx, res, sizeof(*res)); + isc_timer_detach(&res->spillattimer); + res->magic = 0; + isc_mem_put(res->mctx, res, sizeof(*res)); } static void send_shutdown_events(dns_resolver_t *res) { - isc_event_t *event, *next_event; - isc_task_t *etask; - - /* - * Caller must be holding the resolver lock. - */ - - for (event = ISC_LIST_HEAD(res->whenshutdown); - event != NULL; - event = next_event) { - next_event = ISC_LIST_NEXT(event, ev_link); - ISC_LIST_UNLINK(res->whenshutdown, event, ev_link); - etask = event->ev_sender; - event->ev_sender = res; - isc_task_sendanddetach(&etask, &event); - } + isc_event_t *event, *next_event; + isc_task_t *etask; + + /* + * Caller must be holding the resolver lock. + */ + + for (event = ISC_LIST_HEAD(res->whenshutdown); + event != NULL; + event = next_event) { + next_event = ISC_LIST_NEXT(event, ev_link); + ISC_LIST_UNLINK(res->whenshutdown, event, ev_link); + etask = event->ev_sender; + event->ev_sender = res; + isc_task_sendanddetach(&etask, &event); + } } static void empty_bucket(dns_resolver_t *res) { - RTRACE("empty_bucket"); + RTRACE("empty_bucket"); - LOCK(&res->lock); + LOCK(&res->lock); - INSIST(res->activebuckets > 0); - res->activebuckets--; - if (res->activebuckets == 0) - send_shutdown_events(res); + INSIST(res->activebuckets > 0); + res->activebuckets--; + if (res->activebuckets == 0) + send_shutdown_events(res); - UNLOCK(&res->lock); + UNLOCK(&res->lock); } static void spillattimer_countdown(isc_task_t *task, isc_event_t *event) { - dns_resolver_t *res = event->ev_arg; - isc_result_t result; - unsigned int count; - isc_boolean_t logit = ISC_FALSE; - - REQUIRE(VALID_RESOLVER(res)); - - UNUSED(task); - - LOCK(&res->lock); - INSIST(!res->exiting); - if (res->spillat > res->spillatmin) { - res->spillat--; - logit = ISC_TRUE; - } - if (res->spillat <= res->spillatmin) { - result = isc_timer_reset(res->spillattimer, - isc_timertype_inactive, NULL, - NULL, ISC_TRUE); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - } - count = res->spillat; - UNLOCK(&res->lock); - if (logit) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, - "clients-per-query decreased to %u", count); - - isc_event_free(&event); + dns_resolver_t *res = event->ev_arg; + isc_result_t result; + unsigned int count; + isc_boolean_t logit = ISC_FALSE; + + REQUIRE(VALID_RESOLVER(res)); + + UNUSED(task); + + LOCK(&res->lock); + INSIST(!res->exiting); + if (res->spillat > res->spillatmin) { + res->spillat--; + logit = ISC_TRUE; + } + if (res->spillat <= res->spillatmin) { + result = isc_timer_reset(res->spillattimer, + isc_timertype_inactive, NULL, + NULL, ISC_TRUE); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + count = res->spillat; + UNLOCK(&res->lock); + if (logit) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, + "clients-per-query decreased to %u", count); + + isc_event_free(&event); } isc_result_t dns_resolver_create(dns_view_t *view, - isc_taskmgr_t *taskmgr, unsigned int ntasks, - isc_socketmgr_t *socketmgr, - isc_timermgr_t *timermgr, - unsigned int options, - dns_dispatchmgr_t *dispatchmgr, - dns_dispatch_t *dispatchv4, - dns_dispatch_t *dispatchv6, - dns_resolver_t **resp) + isc_taskmgr_t *taskmgr, unsigned int ntasks, + isc_socketmgr_t *socketmgr, + isc_timermgr_t *timermgr, + unsigned int options, + dns_dispatchmgr_t *dispatchmgr, + dns_dispatch_t *dispatchv4, + dns_dispatch_t *dispatchv6, + dns_resolver_t **resp) { - dns_resolver_t *res; - isc_result_t result = ISC_R_SUCCESS; - unsigned int i, buckets_created = 0; - isc_task_t *task = NULL; - char name[16]; - - /* - * Create a resolver. - */ - - REQUIRE(DNS_VIEW_VALID(view)); - REQUIRE(ntasks > 0); - REQUIRE(resp != NULL && *resp == NULL); - REQUIRE(dispatchmgr != NULL); - REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL); - - res = isc_mem_get(view->mctx, sizeof(*res)); - if (res == NULL) - return (ISC_R_NOMEMORY); - RTRACE("create"); - res->mctx = view->mctx; - res->rdclass = view->rdclass; - res->socketmgr = socketmgr; - res->timermgr = timermgr; - res->taskmgr = taskmgr; - res->dispatchmgr = dispatchmgr; - res->view = view; - res->options = options; - res->lame_ttl = 0; - ISC_LIST_INIT(res->alternates); - res->udpsize = RECV_BUFFER_SIZE; - res->algorithms = NULL; - res->mustbesecure = NULL; - res->spillatmin = res->spillat = 10; - res->spillatmax = 100; - res->spillattimer = NULL; - res->zero_no_soa_ttl = ISC_FALSE; - res->ndisps = 0; - res->nextdisp = 0; /* meaningless at this point, but init it */ - res->dispatchv4pool = NULL; - res->dispatchv6pool = NULL; - res->disppooltimer = NULL; - - res->nbuckets = ntasks; - res->activebuckets = ntasks; - res->buckets = isc_mem_get(view->mctx, - ntasks * sizeof(fctxbucket_t)); - if (res->buckets == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup_res; - } - for (i = 0; i < ntasks; i++) { - result = isc_mutex_init(&res->buckets[i].lock); - if (result != ISC_R_SUCCESS) - goto cleanup_buckets; - res->buckets[i].task = NULL; - result = isc_task_create(taskmgr, 0, &res->buckets[i].task); - if (result != ISC_R_SUCCESS) { - DESTROYLOCK(&res->buckets[i].lock); - goto cleanup_buckets; - } - res->buckets[i].mctx = NULL; - result = isc_mem_create(0, 0, &res->buckets[i].mctx); - if (result != ISC_R_SUCCESS) { - isc_task_detach(&res->buckets[i].task); - DESTROYLOCK(&res->buckets[i].lock); - goto cleanup_buckets; - } - snprintf(name, sizeof(name), "res%u", i); - isc_task_setname(res->buckets[i].task, name, res); - ISC_LIST_INIT(res->buckets[i].fctxs); - res->buckets[i].exiting = ISC_FALSE; - buckets_created++; - } - - res->dispatchv4 = NULL; - if (dispatchv4 != NULL) - dns_dispatch_attach(dispatchv4, &res->dispatchv4); - - res->dispatchv6 = NULL; - if (dispatchv6 != NULL) - dns_dispatch_attach(dispatchv6, &res->dispatchv6); - - res->references = 1; - res->exiting = ISC_FALSE; - res->frozen = ISC_FALSE; - ISC_LIST_INIT(res->whenshutdown); - res->priming = ISC_FALSE; - res->primefetch = NULL; - res->nfctx = 0; - - result = isc_mutex_init(&res->lock); - if (result != ISC_R_SUCCESS) - goto cleanup_dispatches; - - result = isc_mutex_init(&res->nlock); - if (result != ISC_R_SUCCESS) - goto cleanup_lock; - - result = isc_mutex_init(&res->primelock); - if (result != ISC_R_SUCCESS) - goto cleanup_nlock; - - result = RES_INITLOCK(&res->poollock); - if (result != ISC_R_SUCCESS) - goto cleanup_primelock; - - task = NULL; - result = isc_task_create(taskmgr, 0, &task); - if (result != ISC_R_SUCCESS) - goto cleanup_poollock; - - result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL, - task, spillattimer_countdown, res, - &res->spillattimer); - isc_task_detach(&task); - if (result != ISC_R_SUCCESS) - goto cleanup_poollock; + dns_resolver_t *res; + isc_result_t result = ISC_R_SUCCESS; + unsigned int i, buckets_created = 0; + isc_task_t *task = NULL; + char name[16]; + + /* + * Create a resolver. + */ + + REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(ntasks > 0); + REQUIRE(resp != NULL && *resp == NULL); + REQUIRE(dispatchmgr != NULL); + REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL); + + res = isc_mem_get(view->mctx, sizeof(*res)); + if (res == NULL) + return (ISC_R_NOMEMORY); + RTRACE("create"); + res->mctx = view->mctx; + res->rdclass = view->rdclass; + res->socketmgr = socketmgr; + res->timermgr = timermgr; + res->taskmgr = taskmgr; + res->dispatchmgr = dispatchmgr; + res->view = view; + res->options = options; + res->lame_ttl = 0; + ISC_LIST_INIT(res->alternates); + res->udpsize = RECV_BUFFER_SIZE; + res->algorithms = NULL; + res->mustbesecure = NULL; + res->spillatmin = res->spillat = 10; + res->spillatmax = 100; + res->spillattimer = NULL; + res->zero_no_soa_ttl = ISC_FALSE; + res->ndisps = 0; + res->nextdisp = 0; /* meaningless at this point, but init it */ + res->dispatchv4pool = NULL; + res->dispatchv6pool = NULL; + res->disppooltimer = NULL; +#ifdef LRU_DEBUG + res->dumptimer = NULL; + res->extqueries = 0; + res->extqueries_ns = 0; + res->extqueries_soa = 0; + res->extqueries_a = 0; + res->extqueries_aaaa = 0; +#endif + + res->nbuckets = ntasks; + res->activebuckets = ntasks; + res->buckets = isc_mem_get(view->mctx, + ntasks * sizeof(fctxbucket_t)); + if (res->buckets == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_res; + } + for (i = 0; i < ntasks; i++) { + result = isc_mutex_init(&res->buckets[i].lock); + if (result != ISC_R_SUCCESS) + goto cleanup_buckets; + res->buckets[i].task = NULL; + result = isc_task_create(taskmgr, 0, &res->buckets[i].task); + if (result != ISC_R_SUCCESS) { + DESTROYLOCK(&res->buckets[i].lock); + goto cleanup_buckets; + } + res->buckets[i].mctx = NULL; + result = isc_mem_create(0, 0, &res->buckets[i].mctx); + if (result != ISC_R_SUCCESS) { + isc_task_detach(&res->buckets[i].task); + DESTROYLOCK(&res->buckets[i].lock); + goto cleanup_buckets; + } + snprintf(name, sizeof(name), "res%u", i); + isc_task_setname(res->buckets[i].task, name, res); + ISC_LIST_INIT(res->buckets[i].fctxs); + res->buckets[i].exiting = ISC_FALSE; + buckets_created++; + } + + res->dispatchv4 = NULL; + if (dispatchv4 != NULL) + dns_dispatch_attach(dispatchv4, &res->dispatchv4); + + res->dispatchv6 = NULL; + if (dispatchv6 != NULL) + dns_dispatch_attach(dispatchv6, &res->dispatchv6); + + res->references = 1; + res->exiting = ISC_FALSE; + res->frozen = ISC_FALSE; + ISC_LIST_INIT(res->whenshutdown); + res->priming = ISC_FALSE; + res->primefetch = NULL; + res->nfctx = 0; + + result = isc_mutex_init(&res->lock); + if (result != ISC_R_SUCCESS) + goto cleanup_dispatches; + + result = isc_mutex_init(&res->nlock); + if (result != ISC_R_SUCCESS) + goto cleanup_lock; + + result = isc_mutex_init(&res->primelock); + if (result != ISC_R_SUCCESS) + goto cleanup_nlock; + + result = RES_INITLOCK(&res->poollock); + if (result != ISC_R_SUCCESS) + goto cleanup_primelock; + + task = NULL; + result = isc_task_create(taskmgr, 0, &task); + if (result != ISC_R_SUCCESS) + goto cleanup_poollock; + + result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL, + task, spillattimer_countdown, res, + &res->spillattimer); + +#ifdef LRU_DEBUG + { + isc_interval_t interval; + + interval.seconds = DUMP_INTERVAL; + interval.nanoseconds = 0; + RUNTIME_CHECK(isc_time_nowplusinterval(&res->dump_time, + &interval) == + ISC_R_SUCCESS); + + result = isc_timer_create(timermgr, isc_timertype_once, + &res->dump_time, NULL, task, + timer_dump, res, &res->dumptimer); + } +#endif + isc_task_detach(&task); + if (result != ISC_R_SUCCESS) + goto cleanup_poollock; #if USE_ALGLOCK - result = isc_rwlock_init(&res->alglock, 0, 0); - if (result != ISC_R_SUCCESS) - goto cleanup_spillattimer; + result = isc_rwlock_init(&res->alglock, 0, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_spillattimer; #endif #if USE_MBSLOCK - result = isc_rwlock_init(&res->mbslock, 0, 0); - if (result != ISC_R_SUCCESS) - goto cleanup_alglock; + result = isc_rwlock_init(&res->mbslock, 0, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_alglock; #endif - res->magic = RES_MAGIC; + res->magic = RES_MAGIC; - *resp = res; + *resp = res; - return (ISC_R_SUCCESS); + return (ISC_R_SUCCESS); #if USE_MBSLOCK cleanup_alglock: #if USE_ALGLOCK - isc_rwlock_destroy(&res->alglock); + isc_rwlock_destroy(&res->alglock); #endif #endif #if USE_ALGLOCK || USE_MBSLOCK cleanup_spillattimer: - isc_timer_detach(&res->spillattimer); + isc_timer_detach(&res->spillattimer); #endif cleanup_poollock: - RES_DESTROYLOCK(&res->poollock); + RES_DESTROYLOCK(&res->poollock); cleanup_primelock: - DESTROYLOCK(&res->primelock); + DESTROYLOCK(&res->primelock); cleanup_nlock: - DESTROYLOCK(&res->nlock); + DESTROYLOCK(&res->nlock); cleanup_lock: - DESTROYLOCK(&res->lock); + DESTROYLOCK(&res->lock); cleanup_dispatches: - if (res->dispatchv6 != NULL) - dns_dispatch_detach(&res->dispatchv6); - if (res->dispatchv4 != NULL) - dns_dispatch_detach(&res->dispatchv4); + if (res->dispatchv6 != NULL) + dns_dispatch_detach(&res->dispatchv6); + if (res->dispatchv4 != NULL) + dns_dispatch_detach(&res->dispatchv4); cleanup_buckets: - for (i = 0; i < buckets_created; i++) { - isc_mem_detach(&res->buckets[i].mctx); - DESTROYLOCK(&res->buckets[i].lock); - isc_task_shutdown(res->buckets[i].task); - isc_task_detach(&res->buckets[i].task); - } - isc_mem_put(view->mctx, res->buckets, - res->nbuckets * sizeof(fctxbucket_t)); + for (i = 0; i < buckets_created; i++) { + isc_mem_detach(&res->buckets[i].mctx); + DESTROYLOCK(&res->buckets[i].lock); + isc_task_shutdown(res->buckets[i].task); + isc_task_detach(&res->buckets[i].task); + } + isc_mem_put(view->mctx, res->buckets, + res->nbuckets * sizeof(fctxbucket_t)); cleanup_res: - isc_mem_put(view->mctx, res, sizeof(*res)); + isc_mem_put(view->mctx, res, sizeof(*res)); - return (result); + return (result); } static void prime_done(isc_task_t *task, isc_event_t *event) { - dns_resolver_t *res; - dns_fetchevent_t *fevent; - dns_fetch_t *fetch; - dns_db_t *db = NULL; - - REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); - fevent = (dns_fetchevent_t *)event; - res = event->ev_arg; - REQUIRE(VALID_RESOLVER(res)); - - UNUSED(task); - - LOCK(&res->lock); - - INSIST(res->priming); - res->priming = ISC_FALSE; - LOCK(&res->primelock); - fetch = res->primefetch; - res->primefetch = NULL; - UNLOCK(&res->primelock); - - UNLOCK(&res->lock); - - if (fevent->result == ISC_R_SUCCESS && - res->view->cache != NULL && res->view->hints != NULL) { - dns_cache_attachdb(res->view->cache, &db); - dns_root_checkhints(res->view, res->view->hints, db); - dns_db_detach(&db); - } - - if (fevent->node != NULL) - dns_db_detachnode(fevent->db, &fevent->node); - if (fevent->db != NULL) - dns_db_detach(&fevent->db); - if (dns_rdataset_isassociated(fevent->rdataset)) - dns_rdataset_disassociate(fevent->rdataset); - INSIST(fevent->sigrdataset == NULL); - - isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset)); - - isc_event_free(&event); - dns_resolver_destroyfetch(&fetch); + dns_resolver_t *res; + dns_fetchevent_t *fevent; + dns_fetch_t *fetch; + dns_db_t *db = NULL; + + REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); + fevent = (dns_fetchevent_t *)event; + res = event->ev_arg; + REQUIRE(VALID_RESOLVER(res)); + + UNUSED(task); + + LOCK(&res->lock); + + INSIST(res->priming); + res->priming = ISC_FALSE; + LOCK(&res->primelock); + fetch = res->primefetch; + res->primefetch = NULL; + UNLOCK(&res->primelock); + + UNLOCK(&res->lock); + + if (fevent->result == ISC_R_SUCCESS && + res->view->cache != NULL && res->view->hints != NULL) { + dns_cache_attachdb(res->view->cache, &db); + dns_root_checkhints(res->view, res->view->hints, db); + dns_db_detach(&db); + } + + if (fevent->node != NULL) + dns_db_detachnode(fevent->db, &fevent->node); + if (fevent->db != NULL) + dns_db_detach(&fevent->db); + if (dns_rdataset_isassociated(fevent->rdataset)) + dns_rdataset_disassociate(fevent->rdataset); + INSIST(fevent->sigrdataset == NULL); + + isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset)); + + isc_event_free(&event); + dns_resolver_destroyfetch(&fetch); } void dns_resolver_prime(dns_resolver_t *res) { - isc_boolean_t want_priming = ISC_FALSE; - dns_rdataset_t *rdataset; - isc_result_t result; - - REQUIRE(VALID_RESOLVER(res)); - REQUIRE(res->frozen); - - RTRACE("dns_resolver_prime"); - - LOCK(&res->lock); - - if (!res->exiting && !res->priming) { - INSIST(res->primefetch == NULL); - res->priming = ISC_TRUE; - want_priming = ISC_TRUE; - } - - UNLOCK(&res->lock); - - if (want_priming) { - /* - * To avoid any possible recursive locking problems, we - * start the priming fetch like any other fetch, and holding - * no resolver locks. No one else will try to start it - * because we're the ones who set res->priming to true. - * Any other callers of dns_resolver_prime() while we're - * running will see that res->priming is already true and - * do nothing. - */ - RTRACE("priming"); - rdataset = isc_mem_get(res->mctx, sizeof(*rdataset)); - if (rdataset == NULL) { - LOCK(&res->lock); - INSIST(res->priming); - INSIST(res->primefetch == NULL); - res->priming = ISC_FALSE; - UNLOCK(&res->lock); - return; - } - dns_rdataset_init(rdataset); - LOCK(&res->primelock); - result = dns_resolver_createfetch(res, dns_rootname, - dns_rdatatype_ns, - NULL, NULL, NULL, 0, - res->buckets[0].task, - prime_done, - res, rdataset, NULL, - &res->primefetch); - UNLOCK(&res->primelock); - if (result != ISC_R_SUCCESS) { - LOCK(&res->lock); - INSIST(res->priming); - res->priming = ISC_FALSE; - UNLOCK(&res->lock); - } - } + isc_boolean_t want_priming = ISC_FALSE; + dns_rdataset_t *rdataset; + isc_result_t result; + + REQUIRE(VALID_RESOLVER(res)); + REQUIRE(res->frozen); + + RTRACE("dns_resolver_prime"); + + LOCK(&res->lock); + + if (!res->exiting && !res->priming) { + INSIST(res->primefetch == NULL); + res->priming = ISC_TRUE; + want_priming = ISC_TRUE; + } + + UNLOCK(&res->lock); + + if (want_priming) { + /* + * To avoid any possible recursive locking problems, we + * start the priming fetch like any other fetch, and holding + * no resolver locks. No one else will try to start it + * because we're the ones who set res->priming to true. + * Any other callers of dns_resolver_prime() while we're + * running will see that res->priming is already true and + * do nothing. + */ + RTRACE("priming"); + rdataset = isc_mem_get(res->mctx, sizeof(*rdataset)); + if (rdataset == NULL) { + LOCK(&res->lock); + INSIST(res->priming); + INSIST(res->primefetch == NULL); + res->priming = ISC_FALSE; + UNLOCK(&res->lock); + return; + } + dns_rdataset_init(rdataset); + LOCK(&res->primelock); + result = dns_resolver_createfetch(res, dns_rootname, + dns_rdatatype_ns, + NULL, NULL, NULL, 0, + res->buckets[0].task, + prime_done, + res, rdataset, NULL, + &res->primefetch); + UNLOCK(&res->primelock); + if (result != ISC_R_SUCCESS) { + LOCK(&res->lock); + INSIST(res->priming); + res->priming = ISC_FALSE; + UNLOCK(&res->lock); + } + } } void dns_resolver_freeze(dns_resolver_t *res) { - /* - * Freeze resolver. - */ + /* + * Freeze resolver. + */ - REQUIRE(VALID_RESOLVER(res)); - REQUIRE(!res->frozen); + REQUIRE(VALID_RESOLVER(res)); + REQUIRE(!res->frozen); - res->frozen = ISC_TRUE; + res->frozen = ISC_TRUE; } void dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) { - REQUIRE(VALID_RESOLVER(source)); - REQUIRE(targetp != NULL && *targetp == NULL); + REQUIRE(VALID_RESOLVER(source)); + REQUIRE(targetp != NULL && *targetp == NULL); - RRTRACE(source, "attach"); - LOCK(&source->lock); - REQUIRE(!source->exiting); + RRTRACE(source, "attach"); + LOCK(&source->lock); + REQUIRE(!source->exiting); - INSIST(source->references > 0); - source->references++; - INSIST(source->references != 0); - UNLOCK(&source->lock); + INSIST(source->references > 0); + source->references++; + INSIST(source->references != 0); + UNLOCK(&source->lock); - *targetp = source; + *targetp = source; } void dns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task, - isc_event_t **eventp) + isc_event_t **eventp) { - isc_task_t *clone; - isc_event_t *event; - - REQUIRE(VALID_RESOLVER(res)); - REQUIRE(eventp != NULL); - - event = *eventp; - *eventp = NULL; - - LOCK(&res->lock); - - if (res->exiting && res->activebuckets == 0) { - /* - * We're already shutdown. Send the event. - */ - event->ev_sender = res; - isc_task_send(task, &event); - } else { - clone = NULL; - isc_task_attach(task, &clone); - event->ev_sender = clone; - ISC_LIST_APPEND(res->whenshutdown, event, ev_link); - } - - UNLOCK(&res->lock); + isc_task_t *clone; + isc_event_t *event; + + REQUIRE(VALID_RESOLVER(res)); + REQUIRE(eventp != NULL); + + event = *eventp; + *eventp = NULL; + + LOCK(&res->lock); + + if (res->exiting && res->activebuckets == 0) { + /* + * We're already shutdown. Send the event. + */ + event->ev_sender = res; + isc_task_send(task, &event); + } else { + clone = NULL; + isc_task_attach(task, &clone); + event->ev_sender = clone; + ISC_LIST_APPEND(res->whenshutdown, event, ev_link); + } + + UNLOCK(&res->lock); } void dns_resolver_shutdown(dns_resolver_t *res) { - unsigned int i; - fetchctx_t *fctx; - isc_socket_t *sock; - isc_result_t result; - - REQUIRE(VALID_RESOLVER(res)); - - RTRACE("shutdown"); - - LOCK(&res->lock); - - if (!res->exiting) { - RTRACE("exiting"); - res->exiting = ISC_TRUE; - - for (i = 0; i < res->nbuckets; i++) { - LOCK(&res->buckets[i].lock); - for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs); - fctx != NULL; - fctx = ISC_LIST_NEXT(fctx, link)) - fctx_shutdown(fctx); - if (res->dispatchv4 != NULL) { - sock = dns_dispatch_getsocket(res->dispatchv4); - isc_socket_cancel(sock, res->buckets[i].task, - ISC_SOCKCANCEL_ALL); - } - if (res->dispatchv6 != NULL) { - sock = dns_dispatch_getsocket(res->dispatchv6); - isc_socket_cancel(sock, res->buckets[i].task, - ISC_SOCKCANCEL_ALL); - } - res->buckets[i].exiting = ISC_TRUE; - if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) { - INSIST(res->activebuckets > 0); - res->activebuckets--; - } - UNLOCK(&res->buckets[i].lock); - } - if (res->activebuckets == 0) - send_shutdown_events(res); - result = isc_timer_reset(res->spillattimer, - isc_timertype_inactive, NULL, - NULL, ISC_TRUE); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - } - - UNLOCK(&res->lock); + unsigned int i; + fetchctx_t *fctx; + isc_socket_t *sock; + isc_result_t result; + + REQUIRE(VALID_RESOLVER(res)); + + RTRACE("shutdown"); + + LOCK(&res->lock); + + if (!res->exiting) { + RTRACE("exiting"); + res->exiting = ISC_TRUE; + + for (i = 0; i < res->nbuckets; i++) { + LOCK(&res->buckets[i].lock); + for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs); + fctx != NULL; + fctx = ISC_LIST_NEXT(fctx, link)) + fctx_shutdown(fctx); + if (res->dispatchv4 != NULL) { + sock = dns_dispatch_getsocket(res->dispatchv4); + isc_socket_cancel(sock, res->buckets[i].task, + ISC_SOCKCANCEL_ALL); + } + if (res->dispatchv6 != NULL) { + sock = dns_dispatch_getsocket(res->dispatchv6); + isc_socket_cancel(sock, res->buckets[i].task, + ISC_SOCKCANCEL_ALL); + } + res->buckets[i].exiting = ISC_TRUE; + if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) { + INSIST(res->activebuckets > 0); + res->activebuckets--; + } + UNLOCK(&res->buckets[i].lock); + } + if (res->activebuckets == 0) + send_shutdown_events(res); + result = isc_timer_reset(res->spillattimer, + isc_timertype_inactive, NULL, + NULL, ISC_TRUE); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + + UNLOCK(&res->lock); } void dns_resolver_detach(dns_resolver_t **resp) { - dns_resolver_t *res; - isc_boolean_t need_destroy = ISC_FALSE; + dns_resolver_t *res; + isc_boolean_t need_destroy = ISC_FALSE; - REQUIRE(resp != NULL); - res = *resp; - REQUIRE(VALID_RESOLVER(res)); + REQUIRE(resp != NULL); + res = *resp; + REQUIRE(VALID_RESOLVER(res)); - RTRACE("detach"); + RTRACE("detach"); - LOCK(&res->lock); + LOCK(&res->lock); - INSIST(res->references > 0); - res->references--; - if (res->references == 0) { - INSIST(res->exiting && res->activebuckets == 0); - need_destroy = ISC_TRUE; - } + INSIST(res->references > 0); + res->references--; + if (res->references == 0) { + INSIST(res->exiting && res->activebuckets == 0); + need_destroy = ISC_TRUE; + } - UNLOCK(&res->lock); + UNLOCK(&res->lock); - if (need_destroy) - destroy(res); + if (need_destroy) + destroy(res); - *resp = NULL; + *resp = NULL; } static inline isc_boolean_t fctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, - unsigned int options) + unsigned int options) { - if (fctx->type != type || fctx->options != options) - return (ISC_FALSE); - return (dns_name_equal(&fctx->name, name)); + if (fctx->type != type || fctx->options != options) + return (ISC_FALSE); + return (dns_name_equal(&fctx->name, name)); } static inline void log_fetch(dns_name_t *name, dns_rdatatype_t type) { - char namebuf[DNS_NAME_FORMATSIZE]; - char typebuf[DNS_RDATATYPE_FORMATSIZE]; - int level = ISC_LOG_DEBUG(1); + char namebuf[DNS_NAME_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + int level = ISC_LOG_DEBUG(1); - if (! isc_log_wouldlog(dns_lctx, level)) - return; + if (! isc_log_wouldlog(dns_lctx, level)) + return; - dns_name_format(name, namebuf, sizeof(namebuf)); - dns_rdatatype_format(type, typebuf, sizeof(typebuf)); + dns_name_format(name, namebuf, sizeof(namebuf)); + dns_rdatatype_format(type, typebuf, sizeof(typebuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, level, - "createfetch: %s %s", namebuf, typebuf); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, level, + "createfetch: %s %s", namebuf, typebuf); } isc_result_t dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, - dns_rdatatype_t type, - dns_name_t *domain, dns_rdataset_t *nameservers, - dns_forwarders_t *forwarders, - unsigned int options, isc_task_t *task, - isc_taskaction_t action, void *arg, - dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset, - dns_fetch_t **fetchp) + dns_rdatatype_t type, + dns_name_t *domain, dns_rdataset_t *nameservers, + dns_forwarders_t *forwarders, + unsigned int options, isc_task_t *task, + isc_taskaction_t action, void *arg, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, + dns_fetch_t **fetchp) { - return (dns_resolver_createfetch2(res, name, type, domain, - nameservers, forwarders, NULL, 0, - options, task, action, arg, - rdataset, sigrdataset, fetchp)); + return (dns_resolver_createfetch2(res, name, type, domain, + nameservers, forwarders, NULL, 0, + options, task, action, arg, + rdataset, sigrdataset, fetchp)); } isc_result_t dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name, - dns_rdatatype_t type, - dns_name_t *domain, dns_rdataset_t *nameservers, - dns_forwarders_t *forwarders, - isc_sockaddr_t *client, dns_messageid_t id, - unsigned int options, isc_task_t *task, - isc_taskaction_t action, void *arg, - dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset, - dns_fetch_t **fetchp) + dns_rdatatype_t type, + dns_name_t *domain, dns_rdataset_t *nameservers, + dns_forwarders_t *forwarders, + isc_sockaddr_t *client, dns_messageid_t id, + unsigned int options, isc_task_t *task, + isc_taskaction_t action, void *arg, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, + dns_fetch_t **fetchp) { - dns_fetch_t *fetch; - fetchctx_t *fctx = NULL; - isc_result_t result = ISC_R_SUCCESS; - unsigned int bucketnum; - isc_boolean_t new_fctx = ISC_FALSE; - isc_event_t *event; - unsigned int count = 0; - unsigned int spillat; - - UNUSED(forwarders); - - REQUIRE(VALID_RESOLVER(res)); - REQUIRE(res->frozen); - /* XXXRTH Check for meta type */ - if (domain != NULL) { - REQUIRE(DNS_RDATASET_VALID(nameservers)); - REQUIRE(nameservers->type == dns_rdatatype_ns); - } else - REQUIRE(nameservers == NULL); - REQUIRE(forwarders == NULL); - REQUIRE(!dns_rdataset_isassociated(rdataset)); - REQUIRE(sigrdataset == NULL || - !dns_rdataset_isassociated(sigrdataset)); - REQUIRE(fetchp != NULL && *fetchp == NULL); - - log_fetch(name, type); - - /* - * XXXRTH use a mempool? - */ - fetch = isc_mem_get(res->mctx, sizeof(*fetch)); - if (fetch == NULL) - return (ISC_R_NOMEMORY); - - bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets; - - LOCK(&res->lock); - spillat = res->spillat; - UNLOCK(&res->lock); - LOCK(&res->buckets[bucketnum].lock); - - if (res->buckets[bucketnum].exiting) { - result = ISC_R_SHUTTINGDOWN; - goto unlock; - } - - if ((options & DNS_FETCHOPT_UNSHARED) == 0) { - for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs); - fctx != NULL; - fctx = ISC_LIST_NEXT(fctx, link)) { - if (fctx_match(fctx, name, type, options)) - break; - } - } - - /* - * Is this a duplicate? - */ - if (fctx != NULL && client != NULL) { - dns_fetchevent_t *fevent; - for (fevent = ISC_LIST_HEAD(fctx->events); - fevent != NULL; - fevent = ISC_LIST_NEXT(fevent, ev_link)) { - if (fevent->client != NULL && fevent->id == id && - isc_sockaddr_equal(fevent->client, client)) { - result = DNS_R_DUPLICATE; - goto unlock; - } - count++; - } - } - if (count >= res->spillatmin && res->spillatmin != 0) { - if (count >= spillat) - fctx->spilled = ISC_TRUE; - if (fctx->spilled) { - result = DNS_R_DROP; - goto unlock; - } - } - - /* - * If we didn't have a fetch, would attach to a done fetch, this - * fetch has already cloned its results, or if the fetch has gone - * "idle" (no one was interested in it), we need to start a new - * fetch instead of joining with the existing one. - */ - if (fctx == NULL || - fctx->state == fetchstate_done || - fctx->cloned || - ISC_LIST_EMPTY(fctx->events)) { - fctx = NULL; - result = fctx_create(res, name, type, domain, nameservers, - options, bucketnum, &fctx); - if (result != ISC_R_SUCCESS) - goto unlock; - new_fctx = ISC_TRUE; - } - - result = fctx_join(fctx, task, client, id, action, arg, - rdataset, sigrdataset, fetch); - if (new_fctx) { - if (result == ISC_R_SUCCESS) { - /* - * Launch this fctx. - */ - event = &fctx->control_event; - ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, - DNS_EVENT_FETCHCONTROL, - fctx_start, fctx, NULL, - NULL, NULL); - isc_task_send(res->buckets[bucketnum].task, &event); - } else { - /* - * We don't care about the result of fctx_destroy() - * since we know we're not exiting. - */ - (void)fctx_destroy(fctx); - } - } + dns_fetch_t *fetch; + fetchctx_t *fctx = NULL; + isc_result_t result = ISC_R_SUCCESS; + unsigned int bucketnum; + isc_boolean_t new_fctx = ISC_FALSE; + isc_event_t *event; + unsigned int count = 0; + unsigned int spillat; + + UNUSED(forwarders); + + REQUIRE(VALID_RESOLVER(res)); + REQUIRE(res->frozen); + /* XXXRTH Check for meta type */ + if (domain != NULL) { + REQUIRE(DNS_RDATASET_VALID(nameservers)); + REQUIRE(nameservers->type == dns_rdatatype_ns); + } else + REQUIRE(nameservers == NULL); + REQUIRE(forwarders == NULL); + REQUIRE(!dns_rdataset_isassociated(rdataset)); + REQUIRE(sigrdataset == NULL || + !dns_rdataset_isassociated(sigrdataset)); + REQUIRE(fetchp != NULL && *fetchp == NULL); + + log_fetch(name, type); + + /* + * XXXRTH use a mempool? + */ + fetch = isc_mem_get(res->mctx, sizeof(*fetch)); + if (fetch == NULL) + return (ISC_R_NOMEMORY); + + bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets; + + LOCK(&res->lock); + spillat = res->spillat; + UNLOCK(&res->lock); + LOCK(&res->buckets[bucketnum].lock); + + if (res->buckets[bucketnum].exiting) { + result = ISC_R_SHUTTINGDOWN; + goto unlock; + } + + if ((options & DNS_FETCHOPT_UNSHARED) == 0) { + for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs); + fctx != NULL; + fctx = ISC_LIST_NEXT(fctx, link)) { + if (fctx_match(fctx, name, type, options)) + break; + } + } + + /* + * Is this a duplicate? + */ + if (fctx != NULL && client != NULL) { + dns_fetchevent_t *fevent; + for (fevent = ISC_LIST_HEAD(fctx->events); + fevent != NULL; + fevent = ISC_LIST_NEXT(fevent, ev_link)) { + if (fevent->client != NULL && fevent->id == id && + isc_sockaddr_equal(fevent->client, client)) { + result = DNS_R_DUPLICATE; + goto unlock; + } + count++; + } + } + if (count >= res->spillatmin && res->spillatmin != 0) { + if (count >= spillat) + fctx->spilled = ISC_TRUE; + if (fctx->spilled) { + result = DNS_R_DROP; + goto unlock; + } + } + + /* + * If we didn't have a fetch, would attach to a done fetch, this + * fetch has already cloned its results, or if the fetch has gone + * "idle" (no one was interested in it), we need to start a new + * fetch instead of joining with the existing one. + */ + if (fctx == NULL || + fctx->state == fetchstate_done || + fctx->cloned || + ISC_LIST_EMPTY(fctx->events)) { + fctx = NULL; + result = fctx_create(res, name, type, domain, nameservers, + options, bucketnum, &fctx); + if (result != ISC_R_SUCCESS) + goto unlock; + new_fctx = ISC_TRUE; + } + + result = fctx_join(fctx, task, client, id, action, arg, + rdataset, sigrdataset, fetch); + if (new_fctx) { + if (result == ISC_R_SUCCESS) { + /* + * Launch this fctx. + */ + event = &fctx->control_event; + ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, + DNS_EVENT_FETCHCONTROL, + fctx_start, fctx, NULL, + NULL, NULL); + isc_task_send(res->buckets[bucketnum].task, &event); + } else { + /* + * We don't care about the result of fctx_destroy() + * since we know we're not exiting. + */ + (void)fctx_destroy(fctx); + } + } unlock: - UNLOCK(&res->buckets[bucketnum].lock); + UNLOCK(&res->buckets[bucketnum].lock); - if (result == ISC_R_SUCCESS) { - FTRACE("created"); - *fetchp = fetch; - } else - isc_mem_put(res->mctx, fetch, sizeof(*fetch)); + if (result == ISC_R_SUCCESS) { + FTRACE("created"); + *fetchp = fetch; + } else + isc_mem_put(res->mctx, fetch, sizeof(*fetch)); - return (result); + return (result); } void dns_resolver_cancelfetch(dns_fetch_t *fetch) { - fetchctx_t *fctx; - dns_resolver_t *res; - dns_fetchevent_t *event, *next_event; - isc_task_t *etask; - - REQUIRE(DNS_FETCH_VALID(fetch)); - fctx = fetch->private; - REQUIRE(VALID_FCTX(fctx)); - res = fctx->res; - - FTRACE("cancelfetch"); - - LOCK(&res->buckets[fctx->bucketnum].lock); - - /* - * Find the completion event for this fetch (as opposed - * to those for other fetches that have joined the same - * fctx) and send it with result = ISC_R_CANCELED. - */ - event = NULL; - if (fctx->state != fetchstate_done) { - for (event = ISC_LIST_HEAD(fctx->events); - event != NULL; - event = next_event) { - next_event = ISC_LIST_NEXT(event, ev_link); - if (event->fetch == fetch) { - ISC_LIST_UNLINK(fctx->events, event, ev_link); - break; - } - } - } - if (event != NULL) { - etask = event->ev_sender; - event->ev_sender = fctx; - event->result = ISC_R_CANCELED; - isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event)); - } - /* - * The fctx continues running even if no fetches remain; - * the answer is still cached. - */ - - UNLOCK(&res->buckets[fctx->bucketnum].lock); + fetchctx_t *fctx; + dns_resolver_t *res; + dns_fetchevent_t *event, *next_event; + isc_task_t *etask; + + REQUIRE(DNS_FETCH_VALID(fetch)); + fctx = fetch->private; + REQUIRE(VALID_FCTX(fctx)); + res = fctx->res; + + FTRACE("cancelfetch"); + + LOCK(&res->buckets[fctx->bucketnum].lock); + + /* + * Find the completion event for this fetch (as opposed + * to those for other fetches that have joined the same + * fctx) and send it with result = ISC_R_CANCELED. + */ + event = NULL; + if (fctx->state != fetchstate_done) { + for (event = ISC_LIST_HEAD(fctx->events); + event != NULL; + event = next_event) { + next_event = ISC_LIST_NEXT(event, ev_link); + if (event->fetch == fetch) { + ISC_LIST_UNLINK(fctx->events, event, ev_link); + break; + } + } + } + if (event != NULL) { + etask = event->ev_sender; + event->ev_sender = fctx; + event->result = ISC_R_CANCELED; + isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event)); + } + /* + * The fctx continues running even if no fetches remain; + * the answer is still cached. + */ + + UNLOCK(&res->buckets[fctx->bucketnum].lock); } void dns_resolver_destroyfetch(dns_fetch_t **fetchp) { - dns_fetch_t *fetch; - dns_resolver_t *res; - dns_fetchevent_t *event, *next_event; - fetchctx_t *fctx; - unsigned int bucketnum; - isc_boolean_t bucket_empty = ISC_FALSE; - - REQUIRE(fetchp != NULL); - fetch = *fetchp; - REQUIRE(DNS_FETCH_VALID(fetch)); - fctx = fetch->private; - REQUIRE(VALID_FCTX(fctx)); - res = fctx->res; - - FTRACE("destroyfetch"); - - bucketnum = fctx->bucketnum; - LOCK(&res->buckets[bucketnum].lock); - - /* - * Sanity check: the caller should have gotten its event before - * trying to destroy the fetch. - */ - event = NULL; - if (fctx->state != fetchstate_done) { - for (event = ISC_LIST_HEAD(fctx->events); - event != NULL; - event = next_event) { - next_event = ISC_LIST_NEXT(event, ev_link); - RUNTIME_CHECK(event->fetch != fetch); - } - } - - INSIST(fctx->references > 0); - fctx->references--; - if (fctx->references == 0) { - /* - * No one cares about the result of this fetch anymore. - */ - if (fctx->pending == 0 && fctx->nqueries == 0 && - ISC_LIST_EMPTY(fctx->validators) && - SHUTTINGDOWN(fctx)) { - /* - * This fctx is already shutdown; we were just - * waiting for the last reference to go away. - */ - bucket_empty = fctx_destroy(fctx); - } else { - /* - * Initiate shutdown. - */ - fctx_shutdown(fctx); - } - } - - UNLOCK(&res->buckets[bucketnum].lock); - - isc_mem_put(res->mctx, fetch, sizeof(*fetch)); - *fetchp = NULL; - - if (bucket_empty) - empty_bucket(res); + dns_fetch_t *fetch; + dns_resolver_t *res; + dns_fetchevent_t *event, *next_event; + fetchctx_t *fctx; + unsigned int bucketnum; + isc_boolean_t bucket_empty = ISC_FALSE; + + REQUIRE(fetchp != NULL); + fetch = *fetchp; + REQUIRE(DNS_FETCH_VALID(fetch)); + fctx = fetch->private; + REQUIRE(VALID_FCTX(fctx)); + res = fctx->res; + + FTRACE("destroyfetch"); + + bucketnum = fctx->bucketnum; + LOCK(&res->buckets[bucketnum].lock); + + /* + * Sanity check: the caller should have gotten its event before + * trying to destroy the fetch. + */ + event = NULL; + if (fctx->state != fetchstate_done) { + for (event = ISC_LIST_HEAD(fctx->events); + event != NULL; + event = next_event) { + next_event = ISC_LIST_NEXT(event, ev_link); + RUNTIME_CHECK(event->fetch != fetch); + } + } + + INSIST(fctx->references > 0); + fctx->references--; + if (fctx->references == 0) { + /* + * No one cares about the result of this fetch anymore. + */ + if (fctx->pending == 0 && fctx->nqueries == 0 && + ISC_LIST_EMPTY(fctx->validators) && + SHUTTINGDOWN(fctx)) { + /* + * This fctx is already shutdown; we were just + * waiting for the last reference to go away. + */ + bucket_empty = fctx_destroy(fctx); + } else { + /* + * Initiate shutdown. + */ + fctx_shutdown(fctx); + } + } + + UNLOCK(&res->buckets[bucketnum].lock); + + isc_mem_put(res->mctx, fetch, sizeof(*fetch)); + *fetchp = NULL; + + if (bucket_empty) + empty_bucket(res); } dns_dispatchmgr_t * dns_resolver_dispatchmgr(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->dispatchmgr); + REQUIRE(VALID_RESOLVER(resolver)); + return (resolver->dispatchmgr); } dns_dispatch_t * dns_resolver_dispatchv4(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->dispatchv4); + REQUIRE(VALID_RESOLVER(resolver)); + return (resolver->dispatchv4); } dns_dispatch_t * dns_resolver_dispatchv6(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->dispatchv6); + REQUIRE(VALID_RESOLVER(resolver)); + return (resolver->dispatchv6); } isc_socketmgr_t * dns_resolver_socketmgr(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->socketmgr); + REQUIRE(VALID_RESOLVER(resolver)); + return (resolver->socketmgr); } isc_taskmgr_t * dns_resolver_taskmgr(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->taskmgr); + REQUIRE(VALID_RESOLVER(resolver)); + return (resolver->taskmgr); } isc_uint32_t dns_resolver_getlamettl(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->lame_ttl); + REQUIRE(VALID_RESOLVER(resolver)); + return (resolver->lame_ttl); } void dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl) { - REQUIRE(VALID_RESOLVER(resolver)); - resolver->lame_ttl = lame_ttl; + REQUIRE(VALID_RESOLVER(resolver)); + resolver->lame_ttl = lame_ttl; } unsigned int dns_resolver_nrunning(dns_resolver_t *resolver) { - unsigned int n; - LOCK(&resolver->nlock); - n = resolver->nfctx; - UNLOCK(&resolver->nlock); - return (n); + unsigned int n; + LOCK(&resolver->nlock); + n = resolver->nfctx; + UNLOCK(&resolver->nlock); + return (n); } isc_result_t dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt, - dns_name_t *name, in_port_t port) { - alternate_t *a; - isc_result_t result; - - REQUIRE(VALID_RESOLVER(resolver)); - REQUIRE(!resolver->frozen); - REQUIRE((alt == NULL) ^ (name == NULL)); - - a = isc_mem_get(resolver->mctx, sizeof(*a)); - if (a == NULL) - return (ISC_R_NOMEMORY); - if (alt != NULL) { - a->isaddress = ISC_TRUE; - a->_u.addr = *alt; - } else { - a->isaddress = ISC_FALSE; - a->_u._n.port = port; - dns_name_init(&a->_u._n.name, NULL); - result = dns_name_dup(name, resolver->mctx, &a->_u._n.name); - if (result != ISC_R_SUCCESS) { - isc_mem_put(resolver->mctx, a, sizeof(*a)); - return (result); - } - } - ISC_LINK_INIT(a, link); - ISC_LIST_APPEND(resolver->alternates, a, link); - - return (ISC_R_SUCCESS); + dns_name_t *name, in_port_t port) { + alternate_t *a; + isc_result_t result; + + REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(!resolver->frozen); + REQUIRE((alt == NULL) ^ (name == NULL)); + + a = isc_mem_get(resolver->mctx, sizeof(*a)); + if (a == NULL) + return (ISC_R_NOMEMORY); + if (alt != NULL) { + a->isaddress = ISC_TRUE; + a->_u.addr = *alt; + } else { + a->isaddress = ISC_FALSE; + a->_u._n.port = port; + dns_name_init(&a->_u._n.name, NULL); + result = dns_name_dup(name, resolver->mctx, &a->_u._n.name); + if (result != ISC_R_SUCCESS) { + isc_mem_put(resolver->mctx, a, sizeof(*a)); + return (result); + } + } + ISC_LINK_INIT(a, link); + ISC_LIST_APPEND(resolver->alternates, a, link); + + return (ISC_R_SUCCESS); } void dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize) { - REQUIRE(VALID_RESOLVER(resolver)); - resolver->udpsize = udpsize; + REQUIRE(VALID_RESOLVER(resolver)); + resolver->udpsize = udpsize; } isc_uint16_t dns_resolver_getudpsize(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->udpsize); + REQUIRE(VALID_RESOLVER(resolver)); + return (resolver->udpsize); } static void free_algorithm(void *node, void *arg) { - unsigned char *algorithms = node; - isc_mem_t *mctx = arg; + unsigned char *algorithms = node; + isc_mem_t *mctx = arg; - isc_mem_put(mctx, algorithms, *algorithms); + isc_mem_put(mctx, algorithms, *algorithms); } - + void dns_resolver_reset_algorithms(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); #if USE_ALGLOCK - RWLOCK(&resolver->alglock, isc_rwlocktype_write); + RWLOCK(&resolver->alglock, isc_rwlocktype_write); #endif - if (resolver->algorithms != NULL) - dns_rbt_destroy(&resolver->algorithms); + if (resolver->algorithms != NULL) + dns_rbt_destroy(&resolver->algorithms); #if USE_ALGLOCK - RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); + RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); #endif } isc_result_t dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, - unsigned int alg) + unsigned int alg) { - unsigned int len, mask; - unsigned char *new; - unsigned char *algorithms; - isc_result_t result; - dns_rbtnode_t *node = NULL; + unsigned int len, mask; + unsigned char *new; + unsigned char *algorithms; + isc_result_t result; + dns_rbtnode_t *node = NULL; - REQUIRE(VALID_RESOLVER(resolver)); - if (alg > 255) - return (ISC_R_RANGE); + REQUIRE(VALID_RESOLVER(resolver)); + if (alg > 255) + return (ISC_R_RANGE); #if USE_ALGLOCK - RWLOCK(&resolver->alglock, isc_rwlocktype_write); + RWLOCK(&resolver->alglock, isc_rwlocktype_write); #endif - if (resolver->algorithms == NULL) { - result = dns_rbt_create(resolver->mctx, free_algorithm, - resolver->mctx, &resolver->algorithms); - if (result != ISC_R_SUCCESS) - goto cleanup; - } - - len = alg/8 + 2; - mask = 1 << (alg%8); - - result = dns_rbt_addnode(resolver->algorithms, name, &node); - - if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) { - algorithms = node->data; - if (algorithms == NULL || len > *algorithms) { - new = isc_mem_get(resolver->mctx, len); - if (new == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; - } - memset(new, 0, len); - if (algorithms != NULL) - memcpy(new, algorithms, *algorithms); - new[len-1] |= mask; - *new = len; - node->data = new; - if (algorithms != NULL) - isc_mem_put(resolver->mctx, algorithms, - *algorithms); - } else - algorithms[len-1] |= mask; - } - result = ISC_R_SUCCESS; + if (resolver->algorithms == NULL) { + result = dns_rbt_create(resolver->mctx, free_algorithm, + resolver->mctx, &resolver->algorithms); + if (result != ISC_R_SUCCESS) + goto cleanup; + } + + len = alg/8 + 2; + mask = 1 << (alg%8); + + result = dns_rbt_addnode(resolver->algorithms, name, &node); + + if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) { + algorithms = node->data; + if (algorithms == NULL || len > *algorithms) { + new = isc_mem_get(resolver->mctx, len); + if (new == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + memset(new, 0, len); + if (algorithms != NULL) + memcpy(new, algorithms, *algorithms); + new[len-1] |= mask; + *new = len; + node->data = new; + if (algorithms != NULL) + isc_mem_put(resolver->mctx, algorithms, + *algorithms); + } else + algorithms[len-1] |= mask; + } + result = ISC_R_SUCCESS; cleanup: #if USE_ALGLOCK - RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); + RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); #endif - return (result); + return (result); } isc_boolean_t dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name, - unsigned int alg) + unsigned int alg) { - unsigned int len, mask; - unsigned char *algorithms; - void *data = NULL; - isc_result_t result; - isc_boolean_t found = ISC_FALSE; + unsigned int len, mask; + unsigned char *algorithms; + void *data = NULL; + isc_result_t result; + isc_boolean_t found = ISC_FALSE; - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); #if USE_ALGLOCK - RWLOCK(&resolver->alglock, isc_rwlocktype_read); + RWLOCK(&resolver->alglock, isc_rwlocktype_read); #endif - if (resolver->algorithms == NULL) - goto unlock; - result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data); - if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { - len = alg/8 + 2; - mask = 1 << (alg%8); - algorithms = data; - if (len <= *algorithms && (algorithms[len-1] & mask) != 0) - found = ISC_TRUE; - } + if (resolver->algorithms == NULL) + goto unlock; + result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data); + if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { + len = alg/8 + 2; + mask = 1 << (alg%8); + algorithms = data; + if (len <= *algorithms && (algorithms[len-1] & mask) != 0) + found = ISC_TRUE; + } unlock: #if USE_ALGLOCK - RWUNLOCK(&resolver->alglock, isc_rwlocktype_read); + RWUNLOCK(&resolver->alglock, isc_rwlocktype_read); #endif - if (found) - return (ISC_FALSE); - return (dst_algorithm_supported(alg)); + if (found) + return (ISC_FALSE); + return (dst_algorithm_supported(alg)); } isc_boolean_t dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) { - UNUSED(resolver); - return (dns_ds_digest_supported(digest)); + UNUSED(resolver); + return (dns_ds_digest_supported(digest)); } void dns_resolver_resetmustbesecure(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK - RWLOCK(&resolver->mbslock, isc_rwlocktype_write); + RWLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif - if (resolver->mustbesecure != NULL) - dns_rbt_destroy(&resolver->mustbesecure); + if (resolver->mustbesecure != NULL) + dns_rbt_destroy(&resolver->mustbesecure); #if USE_MBSLOCK - RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); + RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif } - + static isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE; isc_result_t dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, isc_boolean_t value) { - isc_result_t result; + isc_result_t result; - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK - RWLOCK(&resolver->mbslock, isc_rwlocktype_write); + RWLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif - if (resolver->mustbesecure == NULL) { - result = dns_rbt_create(resolver->mctx, NULL, NULL, - &resolver->mustbesecure); - if (result != ISC_R_SUCCESS) - goto cleanup; - } - result = dns_rbt_addname(resolver->mustbesecure, name, - value ? &yes : &no); + if (resolver->mustbesecure == NULL) { + result = dns_rbt_create(resolver->mctx, NULL, NULL, + &resolver->mustbesecure); + if (result != ISC_R_SUCCESS) + goto cleanup; + } + result = dns_rbt_addname(resolver->mustbesecure, name, + value ? &yes : &no); cleanup: #if USE_MBSLOCK - RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); + RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif - return (result); + return (result); } isc_boolean_t dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) { - void *data = NULL; - isc_boolean_t value = ISC_FALSE; - isc_result_t result; + void *data = NULL; + isc_boolean_t value = ISC_FALSE; + isc_result_t result; - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK - RWLOCK(&resolver->mbslock, isc_rwlocktype_read); + RWLOCK(&resolver->mbslock, isc_rwlocktype_read); #endif - if (resolver->mustbesecure == NULL) - goto unlock; - result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data); - if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) - value = *(isc_boolean_t*)data; + if (resolver->mustbesecure == NULL) + goto unlock; + result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data); + if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) + value = *(isc_boolean_t*)data; unlock: #if USE_MBSLOCK - RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read); + RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read); #endif - return (value); + return (value); } void dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur, - isc_uint32_t *min, isc_uint32_t *max) + isc_uint32_t *min, isc_uint32_t *max) { - REQUIRE(VALID_RESOLVER(resolver)); - - LOCK(&resolver->lock); - if (cur != NULL) - *cur = resolver->spillat; - if (min != NULL) - *min = resolver->spillatmin; - if (max != NULL) - *max = resolver->spillatmax; - UNLOCK(&resolver->lock); + REQUIRE(VALID_RESOLVER(resolver)); + + LOCK(&resolver->lock); + if (cur != NULL) + *cur = resolver->spillat; + if (min != NULL) + *min = resolver->spillatmin; + if (max != NULL) + *max = resolver->spillatmax; + UNLOCK(&resolver->lock); } void dns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min, - isc_uint32_t max) + isc_uint32_t max) { - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); - LOCK(&resolver->lock); - resolver->spillatmin = resolver->spillat = min; - resolver->spillatmax = max; - UNLOCK(&resolver->lock); + LOCK(&resolver->lock); + resolver->spillatmin = resolver->spillat = min; + resolver->spillatmax = max; + UNLOCK(&resolver->lock); } isc_boolean_t dns_resolver_getzeronosoattl(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->zero_no_soa_ttl); + return (resolver->zero_no_soa_ttl); } void dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) { - REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(VALID_RESOLVER(resolver)); - resolver->zero_no_soa_ttl = state; + resolver->zero_no_soa_ttl = state; } unsigned int dns_resolver_getoptions(dns_resolver_t *resolver) { - REQUIRE(VALID_RESOLVER(resolver)); - - return (resolver->options); + REQUIRE(VALID_RESOLVER(resolver)); + + return (resolver->options); } static void disppooltimer_update(isc_task_t *task, isc_event_t *event) { - dns_resolver_t *res = event->ev_arg; - isc_sockaddr_t addr4, addr6; - dns_dispatch_t *disp4 = NULL, *disp6 = NULL; - isc_result_t result; - unsigned int nxt; - unsigned int attrs_base, attrs, attrmask; - - REQUIRE(VALID_RESOLVER(res)); - REQUIRE((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0 || - (res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0); - - UNUSED(task); - isc_event_free(&event); - - LOCK(&res->lock); - nxt = res->nextdisp++; - if (res->nextdisp == res->ndisps) - res->nextdisp = 0; - UNLOCK(&res->lock); - - attrs_base = 0; - attrs_base |= DNS_DISPATCHATTR_UDP; - attrs_base |= DNS_DISPATCHATTR_RANDOMPORT; - - attrmask = 0; - attrmask |= DNS_DISPATCHATTR_UDP; - attrmask |= DNS_DISPATCHATTR_TCP; - attrmask |= DNS_DISPATCHATTR_IPV4; - attrmask |= DNS_DISPATCHATTR_IPV6; - - RES_LOCK(&res->poollock, isc_rwlocktype_read); - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { - result = dns_dispatch_getlocaladdress(res->dispatchv4pool[nxt], - &addr4); - INSIST(result == ISC_R_SUCCESS); - } - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { - result = dns_dispatch_getlocaladdress(res->dispatchv6pool[nxt], - &addr6); - INSIST(result == ISC_R_SUCCESS); - } - RES_UNLOCK(&res->poollock, isc_rwlocktype_read); - - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { - attrs = attrs_base; - attrs |= DNS_DISPATCHATTR_IPV4; - - result = dns_dispatch_getudp(res->dispatchmgr, - res->socketmgr, - res->taskmgr, &addr4, - 4096, 1000, 32768, 16411, - 16433, attrs, attrmask, - &disp4); - if (result != ISC_R_SUCCESS) { - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, - "could not update an IPv4 random query " - "port: %s", - isc_result_totext(result)); - /* keep the old one */ - } - - /* - * We don't try to ensure the new dispatch is unique (see the - * comments in dns_resolver_createdispatchpool()). - */ - } - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { - attrs = attrs_base; - attrs |= DNS_DISPATCHATTR_IPV6; - - result = dns_dispatch_getudp(res->dispatchmgr, - res->socketmgr, - res->taskmgr, &addr6, - 4096, 1000, 32768, 16411, - 16433, attrs, attrmask, - &disp6); - if (result != ISC_R_SUCCESS) { - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, - DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, - "could not update an IPv6 random query " - "port: %s", - isc_result_totext(result)); - } - } - - RES_LOCK(&res->poollock, isc_rwlocktype_write); - if (disp4 != NULL) { - dns_dispatch_detach(&res->dispatchv4pool[nxt]); - res->dispatchv4pool[nxt] = disp4; - } - if (disp6 != NULL) { - dns_dispatch_detach(&res->dispatchv6pool[nxt]); - res->dispatchv6pool[nxt] = disp6; - } - RES_UNLOCK(&res->poollock, isc_rwlocktype_write); - - return; + dns_resolver_t *res = event->ev_arg; + isc_sockaddr_t addr4, addr6; + dns_dispatch_t *disp4 = NULL, *disp6 = NULL; + isc_result_t result; + unsigned int nxt; + unsigned int attrs_base, attrs, attrmask; + + REQUIRE(VALID_RESOLVER(res)); + REQUIRE((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0 || + (res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0); + + UNUSED(task); + isc_event_free(&event); + + LOCK(&res->lock); + nxt = res->nextdisp++; + if (res->nextdisp == res->ndisps) + res->nextdisp = 0; + UNLOCK(&res->lock); + + attrs_base = 0; + attrs_base |= DNS_DISPATCHATTR_UDP; + attrs_base |= DNS_DISPATCHATTR_RANDOMPORT; + + attrmask = 0; + attrmask |= DNS_DISPATCHATTR_UDP; + attrmask |= DNS_DISPATCHATTR_TCP; + attrmask |= DNS_DISPATCHATTR_IPV4; + attrmask |= DNS_DISPATCHATTR_IPV6; + + RES_LOCK(&res->poollock, isc_rwlocktype_read); + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { + result = dns_dispatch_getlocaladdress(res->dispatchv4pool[nxt], + &addr4); + INSIST(result == ISC_R_SUCCESS); + } + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { + result = dns_dispatch_getlocaladdress(res->dispatchv6pool[nxt], + &addr6); + INSIST(result == ISC_R_SUCCESS); + } + RES_UNLOCK(&res->poollock, isc_rwlocktype_read); + + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { + attrs = attrs_base; + attrs |= DNS_DISPATCHATTR_IPV4; + + result = dns_dispatch_getudp(res->dispatchmgr, + res->socketmgr, + res->taskmgr, &addr4, + 4096, 1000, 32768, 16411, + 16433, attrs, attrmask, + &disp4); + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, + "could not update an IPv4 random query " + "port: %s", + isc_result_totext(result)); + /* keep the old one */ + } + + /* + * We don't try to ensure the new dispatch is unique (see the + * comments in dns_resolver_createdispatchpool()). + */ + } + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { + attrs = attrs_base; + attrs |= DNS_DISPATCHATTR_IPV6; + + result = dns_dispatch_getudp(res->dispatchmgr, + res->socketmgr, + res->taskmgr, &addr6, + 4096, 1000, 32768, 16411, + 16433, attrs, attrmask, + &disp6); + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, + "could not update an IPv6 random query " + "port: %s", + isc_result_totext(result)); + } + } + + RES_LOCK(&res->poollock, isc_rwlocktype_write); + if (disp4 != NULL) { + dns_dispatch_detach(&res->dispatchv4pool[nxt]); + res->dispatchv4pool[nxt] = disp4; + } + if (disp6 != NULL) { + dns_dispatch_detach(&res->dispatchv6pool[nxt]); + res->dispatchv6pool[nxt] = disp6; + } + RES_UNLOCK(&res->poollock, isc_rwlocktype_write); + + return; } isc_result_t dns_resolver_createdispatchpool(dns_resolver_t *res, unsigned int ndisps, - unsigned int tick) + unsigned int tick) { - unsigned int i; - isc_result_t result = ISC_R_SUCCESS; - unsigned int attrs_base, attrs, attrmask; - isc_sockaddr_t addr4, addr6; - dns_dispatch_t *disp; - isc_task_t *task; - isc_interval_t interval; - - REQUIRE(VALID_RESOLVER(res)); - REQUIRE(!res->frozen); /* meaning we don't have to lock res */ - REQUIRE(ndisps > 0); - REQUIRE((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0 || - (res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0); - - attrs_base = 0; - attrs_base |= DNS_DISPATCHATTR_UDP; - attrs_base |= DNS_DISPATCHATTR_RANDOMPORT; - - attrmask = 0; - attrmask |= DNS_DISPATCHATTR_UDP; - attrmask |= DNS_DISPATCHATTR_TCP; - attrmask |= DNS_DISPATCHATTR_IPV4; - attrmask |= DNS_DISPATCHATTR_IPV6; - - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { - INSIST(res->dispatchv4 != NULL); - result = dns_dispatch_getlocaladdress(res->dispatchv4, &addr4); - INSIST(result == ISC_R_SUCCESS && - isc_sockaddr_getport(&addr4) == 0); - res->dispatchv4pool = isc_mem_get(res->mctx, - sizeof(dns_dispatch_t *) * - ndisps); - if (res->dispatchv4pool == NULL) - return (ISC_R_NOMEMORY); - for (i = 0; i < ndisps; i++) - res->dispatchv4pool[i] = NULL; - } - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { - INSIST(res->dispatchv6 != NULL); - result = dns_dispatch_getlocaladdress(res->dispatchv6, &addr6); - INSIST(result == ISC_R_SUCCESS && - isc_sockaddr_getport(&addr6) == 0); - res->dispatchv6pool = isc_mem_get(res->mctx, - sizeof(dns_dispatch_t *) * - ndisps); - if (res->dispatchv6pool == NULL) { - isc_mem_put(res->mctx, res->dispatchv4pool, - sizeof(dns_dispatch_t *) * ndisps); - res->dispatchv4pool = NULL; - return (ISC_R_NOMEMORY); - } - for (i = 0; i < ndisps; i++) - res->dispatchv6pool[i] = NULL; - } - - for (i = 0; i < ndisps; i++) { - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { - attrs = attrs_base; - attrs |= DNS_DISPATCHATTR_IPV4; - - disp = NULL; - result = dns_dispatch_getudp(res->dispatchmgr, - res->socketmgr, - res->taskmgr, &addr4, - 4096, 1000, 32768, 16411, - 16433, attrs, attrmask, - &disp); - if (result != ISC_R_SUCCESS) - goto cleanup; - res->dispatchv4pool[i] = disp; - - /* - * It might be better to ensure all ports are - * different, but in practice it's probably okay to - * assume dns_dispatch_getudp() made reasonable - * choices. - */ - } - if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { - attrs = attrs_base; - attrs |= DNS_DISPATCHATTR_IPV6; - - disp = NULL; - result = dns_dispatch_getudp(res->dispatchmgr, - res->socketmgr, - res->taskmgr, &addr6, - 4096, 1000, 32768, 16411, - 16433, attrs, attrmask, - &disp); - if (result != ISC_R_SUCCESS) - goto cleanup; - - res->dispatchv6pool[i] = disp; - } - } - - /* start update timer */ - if (tick != 0) { - task = NULL; - result = isc_task_create(res->taskmgr, 0, &task); - if (result != ISC_R_SUCCESS) - goto cleanup; - isc_interval_set(&interval, tick, 0); - result = isc_timer_create(res->timermgr, isc_timertype_ticker, - NULL, &interval, task, - disppooltimer_update, - res, &res->disppooltimer); - isc_task_detach(&task); - if (result != ISC_R_SUCCESS) - goto cleanup; - } - - res->ndisps = ndisps; - res->nextdisp = 0; - - return (result); + unsigned int i; + isc_result_t result = ISC_R_SUCCESS; + unsigned int attrs_base, attrs, attrmask; + isc_sockaddr_t addr4, addr6; + dns_dispatch_t *disp; + isc_task_t *task; + isc_interval_t interval; + + REQUIRE(VALID_RESOLVER(res)); + REQUIRE(!res->frozen); /* meaning we don't have to lock res */ + REQUIRE(ndisps > 0); + REQUIRE((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0 || + (res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0); + + attrs_base = 0; + attrs_base |= DNS_DISPATCHATTR_UDP; + attrs_base |= DNS_DISPATCHATTR_RANDOMPORT; + + attrmask = 0; + attrmask |= DNS_DISPATCHATTR_UDP; + attrmask |= DNS_DISPATCHATTR_TCP; + attrmask |= DNS_DISPATCHATTR_IPV4; + attrmask |= DNS_DISPATCHATTR_IPV6; + + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { + INSIST(res->dispatchv4 != NULL); + result = dns_dispatch_getlocaladdress(res->dispatchv4, &addr4); + INSIST(result == ISC_R_SUCCESS && + isc_sockaddr_getport(&addr4) == 0); + res->dispatchv4pool = isc_mem_get(res->mctx, + sizeof(dns_dispatch_t *) * + ndisps); + if (res->dispatchv4pool == NULL) + return (ISC_R_NOMEMORY); + for (i = 0; i < ndisps; i++) + res->dispatchv4pool[i] = NULL; + } + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { + INSIST(res->dispatchv6 != NULL); + result = dns_dispatch_getlocaladdress(res->dispatchv6, &addr6); + INSIST(result == ISC_R_SUCCESS && + isc_sockaddr_getport(&addr6) == 0); + res->dispatchv6pool = isc_mem_get(res->mctx, + sizeof(dns_dispatch_t *) * + ndisps); + if (res->dispatchv6pool == NULL) { + isc_mem_put(res->mctx, res->dispatchv4pool, + sizeof(dns_dispatch_t *) * ndisps); + res->dispatchv4pool = NULL; + return (ISC_R_NOMEMORY); + } + for (i = 0; i < ndisps; i++) + res->dispatchv6pool[i] = NULL; + } + + for (i = 0; i < ndisps; i++) { + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) { + attrs = attrs_base; + attrs |= DNS_DISPATCHATTR_IPV4; + + disp = NULL; + result = dns_dispatch_getudp(res->dispatchmgr, + res->socketmgr, + res->taskmgr, &addr4, + 4096, 1000, 32768, 16411, + 16433, attrs, attrmask, + &disp); + if (result != ISC_R_SUCCESS) + goto cleanup; + res->dispatchv4pool[i] = disp; + + /* + * It might be better to ensure all ports are + * different, but in practice it's probably okay to + * assume dns_dispatch_getudp() made reasonable + * choices. + */ + } + if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) { + attrs = attrs_base; + attrs |= DNS_DISPATCHATTR_IPV6; + + disp = NULL; + result = dns_dispatch_getudp(res->dispatchmgr, + res->socketmgr, + res->taskmgr, &addr6, + 4096, 1000, 32768, 16411, + 16433, attrs, attrmask, + &disp); + if (result != ISC_R_SUCCESS) + goto cleanup; + + res->dispatchv6pool[i] = disp; + } + } + + /* start update timer */ + if (tick != 0) { + task = NULL; + result = isc_task_create(res->taskmgr, 0, &task); + if (result != ISC_R_SUCCESS) + goto cleanup; + isc_interval_set(&interval, tick, 0); + result = isc_timer_create(res->timermgr, isc_timertype_ticker, + NULL, &interval, task, + disppooltimer_update, + res, &res->disppooltimer); + isc_task_detach(&task); + if (result != ISC_R_SUCCESS) + goto cleanup; + } + + res->ndisps = ndisps; + res->nextdisp = 0; + + return (result); cleanup: - for (i = 0; i < ndisps; i++) { - if (res->dispatchv4pool[i] != NULL) - dns_dispatch_detach(&res->dispatchv4pool[i]); - if (res->dispatchv6pool[i] != NULL) - dns_dispatch_detach(&res->dispatchv6pool[i]); - } - if (res->dispatchv4pool != NULL) { - isc_mem_put(res->mctx, res->dispatchv4pool, - sizeof(dns_dispatch_t *) * ndisps); - } - if (res->dispatchv6pool != NULL) { - isc_mem_put(res->mctx, res->dispatchv6pool, - sizeof(dns_dispatch_t *) * ndisps); - } - - return (result); + if (res->dispatchv4pool != NULL) { + for (i = 0; i < ndisps; i++) + if (res->dispatchv4pool[i] != NULL) + dns_dispatch_detach(&res->dispatchv4pool[i]); + isc_mem_put(res->mctx, res->dispatchv4pool, + sizeof(dns_dispatch_t *) * ndisps); + } + if (res->dispatchv6pool != NULL) { + for (i = 0; i < ndisps; i++) + if (res->dispatchv6pool[i] != NULL) + dns_dispatch_detach(&res->dispatchv6pool[i]); + isc_mem_put(res->mctx, res->dispatchv6pool, + sizeof(dns_dispatch_t *) * ndisps); + } + + return (result); } + +#ifdef LRU_DEBUG +static void +timer_dump(isc_task_t *task, isc_event_t *ev) { + dns_resolver_t *res; + isc_interval_t interval; + isc_time_t nexttime; + + UNUSED(task); + + res = ev->ev_arg; + INSIST(VALID_RESOLVER(res)); + + if (res->extqueries > 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, + "resolver dump %p: external queries " + "total/NS/SOA/A/AAAA=%u/%u/%u/%u/%u", + res, res->extqueries, res->extqueries_ns, + res->extqueries_soa, res->extqueries_a, + res->extqueries_aaaa); + } + + interval.seconds = DUMP_INTERVAL; + interval.nanoseconds = 0; + + RUNTIME_CHECK(isc_time_add(&res->dump_time, &interval, &nexttime) == + ISC_R_SUCCESS); /* XXX: this is not always true */ + res->dump_time = nexttime; + (void)isc_timer_reset(res->dumptimer, isc_timertype_once, + &res->dump_time, NULL, ISC_FALSE); + + isc_event_free(&ev); +} +#endif diff --git a/lib/dns/rootns.c b/lib/dns/rootns.c index 61ff55dc..b3c302bd 100644 --- a/lib/dns/rootns.c +++ b/lib/dns/rootns.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rootns.c,v 1.31 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: rootns.c,v 1.32 2007/10/30 23:44:01 marka Exp $ */ /*! \file */ @@ -72,7 +72,7 @@ static char root_ns[] = "I.ROOT-SERVERS.NET. 3600000 IN A 192.36.148.17\n" "J.ROOT-SERVERS.NET. 3600000 IN A 192.58.128.30\n" "K.ROOT-SERVERS.NET. 3600000 IN A 193.0.14.129\n" -"L.ROOT-SERVERS.NET. 3600000 IN A 198.32.64.12\n" +"L.ROOT-SERVERS.NET. 3600000 IN A 199.7.83.42\n" "M.ROOT-SERVERS.NET. 3600000 IN A 202.12.27.33\n"; static isc_result_t diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c index 3a1c0ee8..24aff3fa 100644 --- a/lib/dns/sdb.c +++ b/lib/dns/sdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: sdb.c,v 1.58 2007/06/18 23:47:41 tbox Exp $ */ +/* $Id: sdb.c,v 1.59 2007/08/27 03:32:27 marka Exp $ */ /*! \file */ @@ -121,6 +121,10 @@ typedef struct sdb_rdatasetiter { /* This is a reasonable value */ #define SDB_DEFAULT_TTL (60 * 60 * 24) +#ifdef __COVERITY__ +#define MAYBE_LOCK(sdb) LOCK(&sdb->implementation->driverlock) +#define MAYBE_UNLOCK(sdb) UNLOCK(&sdb->implementation->driverlock) +#else #define MAYBE_LOCK(sdb) \ do { \ unsigned int flags = sdb->implementation->flags; \ @@ -134,6 +138,7 @@ typedef struct sdb_rdatasetiter { if ((flags & DNS_SDBFLAG_THREADSAFE) == 0) \ UNLOCK(&sdb->implementation->driverlock); \ } while (0) +#endif static int dummy; diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c index 314d0e16..627ea2e1 100644 --- a/lib/dns/sdlz.c +++ b/lib/dns/sdlz.c @@ -50,7 +50,7 @@ * USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: sdlz.c,v 1.13 2007/06/18 23:47:41 tbox Exp $ */ +/* $Id: sdlz.c,v 1.14 2007/08/27 03:32:27 marka Exp $ */ /*! \file */ @@ -166,6 +166,10 @@ typedef struct sdlz_rdatasetiter { static int dummy; +#ifdef __COVERITY__ +#define MAYBE_LOCK(imp) LOCK(&imp->driverlock) +#define MAYBE_UNLOCK(imp) UNLOCK(&imp->driverlock) +#else #define MAYBE_LOCK(imp) \ do { \ unsigned int flags = imp->flags; \ @@ -179,6 +183,7 @@ static int dummy; if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \ UNLOCK(&imp->driverlock); \ } while (0) +#endif /* * Forward references. Try to keep these to a minimum. diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c index f1391e52..5ab20a34 100644 --- a/lib/dns/tsig.c +++ b/lib/dns/tsig.c @@ -16,7 +16,7 @@ */ /* - * $Id: tsig.c,v 1.129 2007/06/19 23:47:16 tbox Exp $ + * $Id: tsig.c,v 1.130 2007/09/24 17:18:25 each Exp $ */ /*! \file */ #include <config.h> @@ -1512,8 +1512,10 @@ dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) { return (ISC_R_NOMEMORY); result = isc_rwlock_init(&ring->lock, 0, 0); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t)); return (result); + } ring->keys = NULL; result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys); diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 56035586..00743261 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: validator.c,v 1.152 2007/06/18 23:47:41 tbox Exp $ */ +/* $Id: validator.c,v 1.155 2007/09/19 03:38:55 marka Exp $ */ #include <config.h> @@ -87,6 +87,7 @@ #define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC) #define VALATTR_SHUTDOWN 0x0001 /*%< Shutting down. */ +#define VALATTR_CANCELED 0x0002 /*%< Cancelled. */ #define VALATTR_TRIEDVERIFY 0x0004 /*%< We have found a key and * have attempted a verify. */ #define VALATTR_INSECURITY 0x0010 /*%< Attempting proveunsecure. */ @@ -112,6 +113,7 @@ #define DLVTRIED(val) ((val->attributes & VALATTR_DLVTRIED) != 0) #define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0) +#define CANCELED(v) (((v)->attributes & VALATTR_CANCELED) != 0) static void destroy(dns_validator_t *val); @@ -278,7 +280,9 @@ fetch_callback_validator(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_validator"); LOCK(&val->lock); - if (eresult == ISC_R_SUCCESS) { + if (CANCELED(val)) { + validator_done(val, ISC_R_CANCELED); + } else if (eresult == ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "keyset with trust %d", rdataset->trust); /* @@ -342,7 +346,9 @@ dsfetched(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched"); LOCK(&val->lock); - if (eresult == ISC_R_SUCCESS) { + if (CANCELED(val)) { + validator_done(val, ISC_R_CANCELED); + } else if (eresult == ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "dsset with trust %d", rdataset->trust); val->dsset = &val->frdataset; @@ -415,7 +421,9 @@ dsfetched2(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2: %s", dns_result_totext(eresult)); LOCK(&val->lock); - if (eresult == DNS_R_NXRRSET || eresult == DNS_R_NCACHENXRRSET) { + if (CANCELED(val)) { + validator_done(val, ISC_R_CANCELED); + } else if (eresult == DNS_R_NXRRSET || eresult == DNS_R_NCACHENXRRSET) { /* * There is no DS. If this is a delegation, we're done. */ @@ -490,7 +498,9 @@ keyvalidated(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in keyvalidated"); LOCK(&val->lock); - if (eresult == ISC_R_SUCCESS) { + if (CANCELED(val)) { + validator_done(val, ISC_R_CANCELED); + } else if (eresult == ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "keyset with trust %d", val->frdataset.trust); /* @@ -540,7 +550,9 @@ dsvalidated(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in dsvalidated"); LOCK(&val->lock); - if (eresult == ISC_R_SUCCESS) { + if (CANCELED(val)) { + validator_done(val, ISC_R_CANCELED); + } else if (eresult == ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "dsset with trust %d", val->frdataset.trust); if ((val->attributes & VALATTR_INSECURITY) != 0) @@ -749,7 +761,9 @@ authvalidated(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in authvalidated"); LOCK(&val->lock); - if (result != ISC_R_SUCCESS) { + if (CANCELED(val)) { + validator_done(val, ISC_R_CANCELED); + } else if (result != ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "authvalidated: got %s", isc_result_totext(result)); @@ -2376,6 +2390,10 @@ finddlvsep(dns_validator_t *val, isc_boolean_t resume) { dns_fixedname_init(&val->dlvsep); dlvsep = dns_fixedname_name(&val->dlvsep); dns_name_copy(val->event->name, dlvsep, NULL); + /* + * If this is a response to a DS query, we need to look in + * the parent zone for the trust anchor. + */ if (val->event->type == dns_rdatatype_ds) { labels = dns_name_countlabels(dlvsep); if (labels == 0) @@ -2478,9 +2496,16 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { if (val->havedlvsep) dns_name_copy(dns_fixedname_name(&val->dlvsep), secroot, NULL); else { + dns_name_copy(val->event->name, secroot, NULL); + /* + * If this is a response to a DS query, we need to look in + * the parent zone for the trust anchor. + */ + if (val->event->type == dns_rdatatype_ds && + dns_name_countlabels(secroot) > 1U) + dns_name_split(secroot, 1, NULL, secroot); result = dns_keytable_finddeepestmatch(val->keytable, - val->event->name, - secroot); + secroot, secroot); if (result == ISC_R_NOTFOUND) { validator_log(val, ISC_LOG_DEBUG(3), @@ -2817,7 +2842,6 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, dns_validatorevent_t *event; REQUIRE(name != NULL); - REQUIRE(type != 0); REQUIRE(rdataset != NULL || (rdataset == NULL && sigrdataset == NULL && message != NULL)); REQUIRE(validatorp != NULL && *validatorp == NULL); @@ -2933,6 +2957,7 @@ dns_validator_cancel(dns_validator_t *validator) { isc_event_free((isc_event_t **)&validator->event); isc_task_detach(&task); } + validator->attributes |= VALATTR_CANCELED; } UNLOCK(&validator->lock); } diff --git a/lib/dns/win32/libdns.def b/lib/dns/win32/libdns.def index 59505b95..97ba42e2 100644 --- a/lib/dns/win32/libdns.def +++ b/lib/dns/win32/libdns.def @@ -18,16 +18,15 @@ dns_acache_setdb dns_acache_setentry dns_acache_shutdown dns_acl_any -dns_acl_appendelement dns_acl_attach dns_acl_create dns_acl_detach -dns_acl_elementmatch -dns_acl_equal +dns_acl_isany dns_acl_isinsecure +dns_acl_isnone dns_acl_match +dns_acl_merge dns_acl_none -dns_aclelement_equal dns_aclelement_match dns_aclenv_copy dns_aclenv_destroy @@ -195,6 +194,11 @@ dns_fwdtable_add dns_fwdtable_create dns_fwdtable_destroy dns_fwdtable_find +dns_iptable_addprefix +dns_iptable_attach +dns_iptable_create +dns_iptable_detach +dns_iptable_merge dns_journal_begin_transaction dns_journal_commit dns_journal_current_rr diff --git a/lib/dns/win32/libdns.dsp b/lib/dns/win32/libdns.dsp index 2242362e..a8c01f85 100644 --- a/lib/dns/win32/libdns.dsp +++ b/lib/dns/win32/libdns.dsp @@ -190,6 +190,10 @@ SOURCE=..\include\dns\forward.h # End Source File
# Begin Source File
+SOURCE=..\include\dns\iptable.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\dns\journal.h
# End Source File
# Begin Source File
@@ -478,6 +482,10 @@ SOURCE=..\forward.c # End Source File
# Begin Source File
+SOURCE=..\iptable.c
+# End Source File
+# Begin Source File
+
SOURCE=..\journal.c
# End Source File
# Begin Source File
diff --git a/lib/dns/win32/libdns.mak b/lib/dns/win32/libdns.mak index 4e92261b..7e2af76f 100644 --- a/lib/dns/win32/libdns.mak +++ b/lib/dns/win32/libdns.mak @@ -1048,6 +1048,24 @@ SOURCE=..\forward.c !ENDIF
+SOURCE=..\iptable.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\iptable.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\iptable.obj" "$(INTDIR)\iptable.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
SOURCE=..\journal.c
!IF "$(CFG)" == "libdns - Win32 Release"
diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 18a3cc8e..0c0993bb 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: xfrin.c,v 1.153 2007/06/18 23:47:42 tbox Exp $ */ +/* $Id: xfrin.c,v 1.154 2007/10/31 01:56:47 marka Exp $ */ /*! \file */ @@ -1059,6 +1059,7 @@ xfrin_send_request(dns_xfrin_ctx_t *xfr) { xfr->checkid = ISC_TRUE; xfr->id++; + xfr->nmsg = 0; msg->id = xfr->id; CHECK(render(msg, xfr->mctx, &xfr->qbuffer)); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index d12895ed..209b26a8 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.465 2007/06/18 23:47:42 tbox Exp $ */ +/* $Id: zone.c,v 1.469 2007/09/18 00:22:31 marka Exp $ */ /*! \file */ @@ -260,6 +260,11 @@ struct dns_zone { char * strname; char * strrdclass; char * strviewname; + + /*% + * Serial number for deferred journal compaction. + */ + isc_uint32_t compact_serial; }; #define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0)) @@ -305,6 +310,7 @@ struct dns_zone { #define DNS_ZONEFLG_NOEDNS 0x00400000U #define DNS_ZONEFLG_USEALTXFRSRC 0x00800000U #define DNS_ZONEFLG_SOABEFOREAXFR 0x01000000U +#define DNS_ZONEFLG_NEEDCOMPACT 0x02000000U #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0) @@ -1138,11 +1144,7 @@ zone_isdynamic(dns_zone_t *zone) { zone->type == dns_zone_stub || (!zone->update_disabled && zone->ssutable != NULL) || (!zone->update_disabled && zone->update_acl != NULL && - ! (zone->update_acl->length == 1 && - zone->update_acl->elements[0].negative == ISC_TRUE - && - zone->update_acl->elements[0].type == - dns_aclelementtype_any)))); + !dns_acl_isnone(zone->update_acl)))); } @@ -3274,6 +3276,9 @@ dump_done(void *arg, isc_result_t result) { dns_db_t *db; dns_dbversion_t *version; isc_boolean_t again = ISC_FALSE; + isc_boolean_t compact = ISC_FALSE; + isc_uint32_t serial; + isc_result_t tresult; REQUIRE(DNS_ZONE_VALID(zone)); @@ -3281,8 +3286,6 @@ dump_done(void *arg, isc_result_t result) { if (result == ISC_R_SUCCESS && zone->journal != NULL && zone->journalsize != -1) { - isc_uint32_t serial; - isc_result_t tresult; /* * We don't own these, zone->dctx must stay valid. @@ -3291,7 +3294,11 @@ dump_done(void *arg, isc_result_t result) { version = dns_dumpctx_version(zone->dctx); tresult = dns_db_getsoaserial(db, version, &serial); - if (tresult == ISC_R_SUCCESS) { + /* + * Note: we are task locked here so we can test + * zone->xfr safely. + */ + if (tresult == ISC_R_SUCCESS && zone->xfr == NULL) { tresult = dns_journal_compact(zone->mctx, zone->journal, serial, @@ -3310,11 +3317,16 @@ dump_done(void *arg, isc_result_t result) { dns_result_totext(tresult)); break; } + } else if (tresult == ISC_R_SUCCESS) { + compact = ISC_TRUE; + zone->compact_serial = serial; } } LOCK_ZONE(zone); DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_DUMPING); + if (compact) + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDCOMPACT); if (result != ISC_R_SUCCESS && result != ISC_R_CANCELED) { /* * Try again in a short while. @@ -4037,9 +4049,11 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdata_reset(&rdata); /* - * don't notify the master server. + * Don't notify the master server unless explictly + * configured to do so. */ - if (dns_name_compare(&master, &ns.name) == 0) { + if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOTIFYTOSOA) && + dns_name_compare(&master, &ns.name) == 0) { result = dns_rdataset_next(&nsrdset); continue; } @@ -5565,7 +5579,6 @@ notify_createmessage(dns_zone_t *zone, unsigned int flags, REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(messagep != NULL && *messagep == NULL); - message = NULL; result = dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER, &message); if (result != ISC_R_SUCCESS) @@ -5692,8 +5705,7 @@ notify_createmessage(dns_zone_t *zone, unsigned int flags, dns_message_puttempname(message, &tempname); if (temprdataset != NULL) dns_message_puttemprdataset(message, &temprdataset); - if (message != NULL) - dns_message_destroy(&message); + dns_message_destroy(&message); return (result); } @@ -6812,6 +6824,30 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { dns_tsigkey_detach(&zone->tsigkey); /* + * Handle any deferred journal compaction. + */ + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDCOMPACT)) { + result = dns_journal_compact(zone->mctx, zone->journal, + zone->compact_serial, + zone->journalsize); + switch (result) { + case ISC_R_SUCCESS: + case ISC_R_NOSPACE: + case ISC_R_NOTFOUND: + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "dns_journal_compact: %s", + dns_result_totext(result)); + break; + default: + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_journal_compact failed: %s", + dns_result_totext(result)); + break; + } + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDCOMPACT); + } + + /* * This transfer finishing freed up a transfer quota slot. * Let any other zones waiting for quota have it. */ |