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;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rbtdb_version_t *rbtversion = NULL;
+ dns_rbtnode_t *node = NULL;
rbtdb_search_t search;
bool cname_ok = true;
bool close_version = false;
bool maybe_zonecut = false;
bool at_zonecut = false;
- bool wild;
- bool empty_node;
- rdatasetheader_t *header, *header_next, *found, *nsecheader;
- rdatasetheader_t *foundsig, *cnamesig, *nsecsig;
+ bool wild = false;
+ bool empty_node = true;
+ bool active = false;
+ rdatasetheader_t *header = NULL, *header_next = NULL;
+ rdatasetheader_t *found = NULL, *nsecheader = NULL;
+ rdatasetheader_t *foundsig = NULL, *cnamesig = NULL, *nsecsig = NULL;
rbtdb_rdatatype_t sigtype;
- bool active;
- nodelock_t *lock;
- dns_rbt_t *tree;
-
- search.rbtdb = (dns_rbtdb_t *)db;
+ nodelock_t *lock = NULL;
+ dns_rbt_t *tree = NULL;
- REQUIRE(VALID_RBTDB(search.rbtdb));
- INSIST(version == NULL ||
- ((rbtdb_version_t *)version)->rbtdb == (dns_rbtdb_t *)db);
+ REQUIRE(VALID_RBTDB(rbtdb));
+ INSIST(version == NULL || ((rbtdb_version_t *)version)->rbtdb == rbtdb);
/*
* We don't care about 'now'.
*/
UNUSED(now);
+ if ((options & DNS_DBFIND_NONBLOCK) != 0) {
+ result = isc_rwlock_trylock(&rbtdb->tree_lock,
+ isc_rwlocktype_read);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ } else {
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ }
+
/*
* If the caller didn't supply a version, attach to the current
* version.
close_version = true;
}
- search.rbtversion = version;
- search.serial = search.rbtversion->serial;
- search.options = options;
- search.copy_name = false;
- search.need_cleanup = false;
- search.wild = false;
- search.zonecut = NULL;
- dns_fixedname_init(&search.zonecut_name);
+ rbtversion = (rbtdb_version_t *)version;
+ search = (rbtdb_search_t){ .rbtdb = rbtdb,
+ .options = options,
+ .rbtversion = rbtversion,
+ .serial = rbtversion->serial };
dns_rbtnodechain_init(&search.chain);
- search.now = 0;
-
- /*
- * 'wild' will be true iff. we've matched a wildcard.
- */
- wild = false;
-
- RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+ dns_fixedname_init(&search.zonecut_name);
/*
* 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.
*/
- tree = (options & DNS_DBFIND_FORCENSEC3) != 0 ? search.rbtdb->nsec3
- : search.rbtdb->tree;
+ tree = (options & DNS_DBFIND_FORCENSEC3) != 0 ? rbtdb->nsec3
+ : rbtdb->tree;
result = dns_rbt_findnode(tree, name, foundname, &node, &search.chain,
DNS_RBTFIND_EMPTYDATA, zone_zonecut_callback,
&search);
}
}
- active = false;
if ((options & DNS_DBFIND_FORCENSEC3) == 0) {
/*
* The NSEC3 tree won't have empty nodes,
* If we're here, then the name does not exist, is not
* beneath a zonecut, and there's no matching wildcard.
*/
- if ((search.rbtversion->secure == dns_db_secure &&
- !search.rbtversion->havensec3) ||
+ if ((rbtversion->secure == dns_db_secure &&
+ !rbtversion->havensec3) ||
(search.options & DNS_DBFIND_FORCENSEC) != 0 ||
(search.options & DNS_DBFIND_FORCENSEC3) != 0)
{
result = find_closest_nsec(&search, nodep, foundname,
rdataset, sigrdataset, tree,
- search.rbtversion->secure);
+ rbtversion->secure);
if (result == ISC_R_SUCCESS) {
result = active ? DNS_R_EMPTYNAME
: DNS_R_NXDOMAIN;
* we always return a referral.
*/
if (node->find_callback &&
- ((node != search.rbtdb->origin_node &&
- !dns_rdatatype_atparent(type)) ||
- IS_STUB(search.rbtdb)))
+ ((node != rbtdb->origin_node &&
+ !dns_rdatatype_atparent(type)) || IS_STUB(rbtdb)))
{
maybe_zonecut = true;
}
* We now go looking for rdata...
*/
- lock = &search.rbtdb->node_locks[node->locknum].lock;
+ lock = &rbtdb->node_locks[node->locknum].lock;
NODE_LOCK(lock, isc_rwlocktype_read);
- found = NULL;
- foundsig = NULL;
sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
- nsecheader = NULL;
- nsecsig = NULL;
- cnamesig = NULL;
- empty_node = true;
for (header = node->data; header != NULL; header = header_next) {
header_next = header->next;
/*
* ensure that search->zonecut_rdataset will
* still be valid later.
*/
- new_reference(search.rbtdb, node,
- isc_rwlocktype_read);
+ new_reference(rbtdb, node, isc_rwlocktype_read);
search.zonecut = node;
search.zonecut_rdataset = header;
search.zonecut_sigrdataset = NULL;
break;
}
} else if (header->type == dns_rdatatype_nsec &&
- !search.rbtversion->havensec3) {
+ !rbtversion->havensec3) {
/*
* Remember a NSEC rdataset even if we're
* not specifically looking for it, because
*/
nsecheader = header;
} else if (header->type == RBTDB_RDATATYPE_SIGNSEC &&
- !search.rbtversion->havensec3)
+ !rbtversion->havensec3)
{
/*
* If we need the NSEC rdataset, we'll also
* The desired type doesn't exist.
*/
result = DNS_R_NXRRSET;
- if (search.rbtversion->secure == dns_db_secure &&
- !search.rbtversion->havensec3 &&
+ if (rbtversion->secure == dns_db_secure &&
+ !rbtversion->havensec3 &&
(nsecheader == NULL || nsecsig == NULL))
{
/*
NODE_UNLOCK(lock, isc_rwlocktype_read);
result = find_closest_nsec(&search, nodep, foundname,
rdataset, sigrdataset,
- search.rbtdb->tree,
- search.rbtversion->secure);
+ rbtdb->tree,
+ rbtversion->secure);
if (result == ISC_R_SUCCESS) {
result = DNS_R_EMPTYWILD;
}
goto node_exit;
}
if (nodep != NULL) {
- new_reference(search.rbtdb, node, isc_rwlocktype_read);
+ new_reference(rbtdb, node, isc_rwlocktype_read);
*nodep = node;
}
- if ((search.rbtversion->secure == dns_db_secure &&
- !search.rbtversion->havensec3) ||
+ if ((rbtversion->secure == dns_db_secure &&
+ !rbtversion->havensec3) ||
(search.options & DNS_DBFIND_FORCENSEC) != 0)
{
- bind_rdataset(search.rbtdb, node, nsecheader, 0,
+ bind_rdataset(rbtdb, node, nsecheader, 0,
isc_rwlocktype_read, rdataset);
if (nsecsig != NULL) {
- bind_rdataset(search.rbtdb, node, nsecsig, 0,
+ bind_rdataset(rbtdb, node, nsecsig, 0,
isc_rwlocktype_read, sigrdataset);
}
}
if (nodep != NULL) {
if (!at_zonecut) {
- new_reference(search.rbtdb, node, isc_rwlocktype_read);
+ new_reference(rbtdb, node, isc_rwlocktype_read);
} else {
search.need_cleanup = false;
}
}
if (type != dns_rdatatype_any) {
- bind_rdataset(search.rbtdb, node, found, 0, isc_rwlocktype_read,
+ bind_rdataset(rbtdb, node, found, 0, isc_rwlocktype_read,
rdataset);
if (foundsig != NULL) {
- bind_rdataset(search.rbtdb, node, foundsig, 0,
+ bind_rdataset(rbtdb, node, foundsig, 0,
isc_rwlocktype_read, sigrdataset);
}
}
NODE_UNLOCK(lock, isc_rwlocktype_read);
tree_exit:
- RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
/*
* If we found a zonecut but aren't going to use it, we have to
if (search.need_cleanup) {
node = search.zonecut;
INSIST(node != NULL);
- lock = &(search.rbtdb->node_locks[node->locknum].lock);
+ lock = &(rbtdb->node_locks[node->locknum].lock);
NODE_LOCK(lock, isc_rwlocktype_read);
- decrement_reference(search.rbtdb, node, 0, isc_rwlocktype_read,
+ decrement_reference(rbtdb, node, 0, isc_rwlocktype_read,
isc_rwlocktype_none, false);
NODE_UNLOCK(lock, isc_rwlocktype_read);
}
* Free any allocated memory associated with qctx.
*/
static void
-qctx_freedata(query_ctx_t *qctx) {
+qctx_free_buffers(query_ctx_t *qctx) {
if (qctx->rdataset != NULL) {
ns_client_putrdataset(qctx->client, &qctx->rdataset);
}
if (qctx->fname != NULL) {
ns_client_releasename(qctx->client, &qctx->fname);
}
+}
+
+static void
+qctx_freedata(query_ctx_t *qctx) {
+ qctx_free_buffers(qctx);
if (qctx->db != NULL) {
INSIST(qctx->node == NULL);
qctx_destroy(&qctx);
}
+/*
+ * Use the asynchronous hook mechanism to set up a callback in order to
+ * delay an authoritative query until later if the database lock is busy.
+ */
+static void
+cancelhook(ns_hookasync_t *hctx) {
+ UNUSED(hctx);
+}
+
+static void
+destroyhook(ns_hookasync_t **ctxp) {
+ ns_hookasync_t *ctx = NULL;
+
+ REQUIRE(ctxp != NULL);
+
+ ctx = *ctxp;
+ isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx));
+}
+
+static isc_result_t
+delayquery(query_ctx_t *qctx, isc_mem_t *mctx, void *arg, isc_task_t *task,
+ isc_taskaction_t action, void *evarg, ns_hookasync_t **ctxp) {
+ ns_hook_resevent_t *rev = (ns_hook_resevent_t *)isc_event_allocate(
+ mctx, task, NS_EVENT_HOOKASYNCDONE, action, evarg,
+ sizeof(*rev));
+ ns_hookasync_t *ctx = isc_mem_get(mctx, sizeof(*ctx));
+
+ UNUSED(arg);
+
+ *ctx = (ns_hookasync_t){ .cancel = cancelhook,
+ .destroy = destroyhook };
+ isc_mem_attach(mctx, &ctx->mctx);
+
+ rev->hookpoint = NS_QUERY_LOOKUP_BEGIN;
+ rev->saved_qctx = qctx;
+ rev->ctx = ctx;
+
+ isc_task_send(task, (isc_event_t **)&rev);
+
+ *ctxp = ctx;
+ return (ISC_R_SUCCESS);
+}
+
/*%
* Perform a local database lookup, in either an authoritative or
* cache database. If unable to answer, call ns_query_done(); otherwise
dns_clientinfo_t ci;
dns_name_t *rpzqname = NULL;
char namebuf[DNS_NAME_FORMATSIZE];
- unsigned int dboptions;
+ unsigned int dboptions = 0;
dns_ttl_t stale_refresh = 0;
bool dbfind_stale = false;
bool stale_timeout = false;
}
dboptions = qctx->client->query.dboptions;
+ dboptions |= DNS_DBFIND_NONBLOCK;
+
if (!qctx->is_zone && qctx->findcoveringnsec &&
(qctx->type != dns_rdatatype_null || !dns_name_istat(rpzqname)))
{
dboptions, qctx->client->now, &qctx->node,
qctx->fname, &cm, &ci, qctx->rdataset,
qctx->sigrdataset);
+ if (result == ISC_R_LOCKBUSY) {
+ qctx_free_buffers(qctx);
+ ns_query_hookasync(qctx, delayquery, NULL);
+ return (ISC_R_COMPLETE);
+ }
/*
* Fixup fname and sigrdataset.
isc_event_free(ISC_EVENT_PTR(&event));
return;
}
+
/*
* We are resuming from recursion. Reset any attributes, options
* that a lookup due to stale-answer-client-timeout may have set.
REQUIRE(client->query.hookactx == NULL);
REQUIRE(client->query.fetch == NULL);
- result = check_recursionquota(client);
- if (result != ISC_R_SUCCESS) {
- goto cleanup;
+ if (!qctx->is_zone) {
+ result = check_recursionquota(client);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
}
saved_qctx = isc_mem_get(client->mctx, sizeof(*saved_qctx));