return (ISC_R_SUCCESS);
}
+static bool
+issecure(dns_db_t *db) {
+ qpzonedb_t *qpdb = NULL;
+ bool secure;
+
+ qpdb = (qpzonedb_t *)db;
+
+ REQUIRE(VALID_QPZONE(qpdb));
+
+ RWLOCK(&qpdb->lock, isc_rwlocktype_read);
+ secure = qpdb->current_version->secure;
+ RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
+
+ return (secure);
+}
+
+static isc_result_t
+getnsec3parameters(dns_db_t *db, dns_dbversion_t *dbversion, dns_hash_t *hash,
+ uint8_t *flags, uint16_t *iterations, unsigned char *salt,
+ size_t *salt_length) {
+ qpzonedb_t *qpdb = NULL;
+ isc_result_t result = ISC_R_NOTFOUND;
+ qpdb_version_t *version = dbversion;
+
+ qpdb = (qpzonedb_t *)db;
+
+ REQUIRE(VALID_QPZONE(qpdb));
+ INSIST(version == NULL || version->qpdb == qpdb);
+
+ RWLOCK(&qpdb->lock, isc_rwlocktype_read);
+ if (version == NULL) {
+ version = qpdb->current_version;
+ }
+
+ if (version->havensec3) {
+ if (hash != NULL) {
+ *hash = version->hash;
+ }
+ if (salt != NULL && salt_length != NULL) {
+ REQUIRE(*salt_length >= version->salt_length);
+ memmove(salt, version->salt, version->salt_length);
+ }
+ if (salt_length != NULL) {
+ *salt_length = version->salt_length;
+ }
+ if (iterations != NULL) {
+ *iterations = version->iterations;
+ }
+ if (flags != NULL) {
+ *flags = version->flags;
+ }
+ result = ISC_R_SUCCESS;
+ }
+ RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+static isc_result_t
+getsize(dns_db_t *db, dns_dbversion_t *dbversion, uint64_t *records,
+ uint64_t *xfrsize) {
+ qpzonedb_t *qpdb = NULL;
+ qpdb_version_t *version = dbversion;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ qpdb = (qpzonedb_t *)db;
+
+ REQUIRE(VALID_QPZONE(qpdb));
+ INSIST(version == NULL || version->qpdb == qpdb);
+
+ RWLOCK(&qpdb->lock, isc_rwlocktype_read);
+ if (version == NULL) {
+ version = qpdb->current_version;
+ }
+
+ RWLOCK(&version->rwlock, isc_rwlocktype_read);
+ SET_IF_NOT_NULL(records, version->records);
+
+ SET_IF_NOT_NULL(xfrsize, version->xfrsize);
+ RWUNLOCK(&version->rwlock, isc_rwlocktype_read);
+ RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+static isc_result_t
+setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
+ qpzonedb_t *qpdb = (qpzonedb_t *)db;
+ dns_slabheader_t *header = NULL, oldheader;
+ isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+
+ REQUIRE(VALID_QPZONE(qpdb));
+ REQUIRE(rdataset != NULL);
+ REQUIRE(rdataset->methods == &dns_rdataslab_rdatasetmethods);
+
+ header = dns_slabheader_fromrdataset(rdataset);
+
+ NODE_WRLOCK(&qpdb->node_locks[HEADERNODE(header)->locknum].lock,
+ &nlocktype);
+
+ oldheader = *header;
+
+ /*
+ * Only break the heap invariant (by adjusting resign and resign_lsb)
+ * if we are going to be restoring it by calling isc_heap_increased
+ * or isc_heap_decreased.
+ */
+ if (resign != 0) {
+ header->resign = (isc_stdtime_t)(dns_time64_from32(resign) >>
+ 1);
+ header->resign_lsb = resign & 0x1;
+ }
+ if (header->heap_index != 0) {
+ INSIST(RESIGN(header));
+ if (resign == 0) {
+ isc_heap_delete(
+ qpdb->heaps[HEADERNODE(header)->locknum],
+ header->heap_index);
+ header->heap_index = 0;
+ header->heap = NULL;
+ } else if (resign_sooner(header, &oldheader)) {
+ isc_heap_increased(
+ qpdb->heaps[HEADERNODE(header)->locknum],
+ header->heap_index);
+ } else if (resign_sooner(&oldheader, header)) {
+ isc_heap_decreased(
+ qpdb->heaps[HEADERNODE(header)->locknum],
+ header->heap_index);
+ }
+ } else if (resign != 0) {
+ DNS_SLABHEADER_SETATTR(header, DNS_SLABHEADERATTR_RESIGN);
+ resigninsert(qpdb, HEADERNODE(header)->locknum, header);
+ }
+ NODE_UNLOCK(&qpdb->node_locks[HEADERNODE(header)->locknum].lock,
+ &nlocktype);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
+ dns_name_t *foundname DNS__DB_FLARG) {
+ qpzonedb_t *qpdb = (qpzonedb_t *)db;
+ dns_slabheader_t *header = NULL, *this = NULL;
+ isc_result_t result = ISC_R_NOTFOUND;
+ unsigned int locknum = 0;
+ isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+
+ REQUIRE(VALID_QPZONE(qpdb));
+
+ for (int i = 0; i < qpdb->node_lock_count; i++) {
+ NODE_RDLOCK(&qpdb->node_locks[i].lock, &nlocktype);
+
+ /*
+ * Find for the earliest signing time among all of the
+ * heaps, each of which is covered by a different bucket
+ * lock.
+ */
+ this = isc_heap_element(qpdb->heaps[i], 1);
+ if (this == NULL) {
+ /* Nothing found; unlock and try the next heap. */
+ NODE_UNLOCK(&qpdb->node_locks[i].lock, &nlocktype);
+ continue;
+ }
+
+ if (header == NULL) {
+ /*
+ * Found a signing time: retain the bucket lock and
+ * preserve the lock number so we can unlock it
+ * later.
+ */
+ header = this;
+ locknum = i;
+ nlocktype = isc_rwlocktype_none;
+ } else if (resign_sooner(this, header)) {
+ /*
+ * Found an earlier signing time; release the
+ * previous bucket lock and retain this one instead.
+ */
+ NODE_UNLOCK(&qpdb->node_locks[locknum].lock,
+ &nlocktype);
+ header = this;
+ locknum = i;
+ } else {
+ /*
+ * Earliest signing time in this heap isn't
+ * an improvement; unlock and try the next heap.
+ */
+ NODE_UNLOCK(&qpdb->node_locks[i].lock, &nlocktype);
+ }
+ }
+
+ if (header != NULL) {
+ nlocktype = isc_rwlocktype_read;
+ /*
+ * Found something; pass back the answer and unlock
+ * the bucket.
+ */
+ bindrdataset(qpdb, HEADERNODE(header), header, 0,
+ rdataset DNS__DB_FLARG_PASS);
+
+ if (foundname != NULL) {
+ dns_name_copy(HEADERNODE(header)->name, foundname);
+ }
+
+ NODE_UNLOCK(&qpdb->node_locks[locknum].lock, &nlocktype);
+
+ result = ISC_R_SUCCESS;
+ }
+
+ return (result);
+}
+
static isc_result_t
-findnodeintree(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
- bool create, bool nsec3, dns_dbnode_t **nodep DNS__DB_FLARG) {
+setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
+ qpzonedb_t *qpdb = (qpzonedb_t *)db;
+
+ REQUIRE(VALID_QPZONE(qpdb));
+ REQUIRE(!IS_STUB(qpdb));
+ REQUIRE(stats != NULL);
+
+ isc_stats_attach(stats, &qpdb->gluecachestats);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findnodeintree(qpzonedb_t *qpdb, const dns_name_t *name, bool create,
+ bool nsec3, dns_dbnode_t **nodep DNS__DB_FLARG) {
isc_result_t result;
qpdata_t *node = NULL;
+ dns_qpmulti_t *dbtree = nsec3 ? qpdb->nsec3 : qpdb->tree;
+ dns_qpread_t qpr = { 0 };
+ dns_qp_t *qp = NULL;
+
+ if (create) {
+ dns_qpmulti_write(dbtree, &qp);
+ } else {
+ dns_qpmulti_query(dbtree, &qpr);
+ qp = (dns_qp_t *)&qpr;
+ }
result = dns_qp_getname(qp, name, (void **)&node, NULL);
if (result != ISC_R_SUCCESS) {
if (!create) {
+ dns_qpread_destroy(dbtree, &qpr);
return (result);
}
wildcardmagic(qpdb, qp, name, true);
}
}
- } else if (result == ISC_R_EXISTS) {
- result = ISC_R_SUCCESS;
}
- }
- if (nsec3) {
- INSIST(node->nsec == DNS_DB_NSEC_NSEC3);
+ INSIST(node->nsec == DNS_DB_NSEC_NSEC3 || !nsec3);
}
newref(qpdb, node DNS__DB_FLARG_PASS);
+ if (create) {
+ dns_qp_compact(qp, DNS_QPGC_MAYBE);
+ dns_qpmulti_commit(dbtree, &qp);
+ } else {
+ dns_qpread_destroy(dbtree, &qpr);
+ }
+
*nodep = (dns_dbnode_t *)node;
- return (result);
+
+ return (ISC_R_SUCCESS);
}
static isc_result_t
findnode(dns_db_t *db, const dns_name_t *name, bool create,
dns_dbnode_t **nodep DNS__DB_FLARG) {
qpzonedb_t *qpdb = (qpzonedb_t *)db;
- isc_result_t result;
- dns_qpread_t qpr = { 0 };
- dns_qp_t *qp = NULL;
REQUIRE(VALID_QPZONE(qpdb));
- if (create) {
- dns_qpmulti_write(qpdb->tree, &qp);
- } else {
- dns_qpmulti_query(qpdb->tree, &qpr);
- qp = (dns_qp_t *)&qpr;
- }
+ return (findnodeintree(qpdb, name, create, false,
+ nodep DNS__DB_FLARG_PASS));
+}
- result = findnodeintree(qpdb, qp, name, create, false,
- nodep DNS__DB_FLARG_PASS);
+static isc_result_t
+findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
+ dns_dbnode_t **nodep DNS__DB_FLARG) {
+ qpzonedb_t *qpdb = (qpzonedb_t *)db;
- if (create) {
- dns_qp_compact(qp, DNS_QPGC_MAYBE);
- dns_qpmulti_commit(qpdb->tree, &qp);
- } else {
- dns_qpread_destroy(qpdb->tree, &qpr);
- }
+ REQUIRE(VALID_QPZONE(qpdb));
- return (result);
+ return (findnodeintree(qpdb, name, create, true,
+ nodep DNS__DB_FLARG_PASS));
}
static bool
}
}
+static unsigned int
+nodecount(dns_db_t *db, dns_dbtree_t tree) {
+ qpzonedb_t *qpdb = NULL;
+ dns_qp_memusage_t mu;
+
+ qpdb = (qpzonedb_t *)db;
+
+ REQUIRE(VALID_QPZONE(qpdb));
+
+ switch (tree) {
+ case dns_dbtree_main:
+ mu = dns_qpmulti_memusage(qpdb->tree);
+ break;
+ case dns_dbtree_nsec:
+ mu = dns_qpmulti_memusage(qpdb->nsec);
+ break;
+ case dns_dbtree_nsec3:
+ mu = dns_qpmulti_memusage(qpdb->nsec3);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return (mu.leaves);
+}
+
static void
setloop(dns_db_t *db, isc_loop_t *loop) {
qpzonedb_t *qpdb = NULL;
return (ISC_R_SUCCESS);
}
+static void
+locknode(dns_db_t *db, dns_dbnode_t *dbnode, isc_rwlocktype_t type) {
+ qpzonedb_t *qpdb = (qpzonedb_t *)db;
+ qpdata_t *node = (qpdata_t *)dbnode;
+
+ RWLOCK(&qpdb->node_locks[node->locknum].lock, type);
+}
+
+static void
+unlocknode(dns_db_t *db, dns_dbnode_t *dbnode, isc_rwlocktype_t type) {
+ qpzonedb_t *qpdb = (qpzonedb_t *)db;
+ qpdata_t *node = (qpdata_t *)dbnode;
+
+ RWUNLOCK(&qpdb->node_locks[node->locknum].lock, type);
+}
+
static void
deletedata(dns_db_t *db ISC_ATTR_UNUSED, dns_dbnode_t *node ISC_ATTR_UNUSED,
void *data) {
return (result);
}
+static dns_glue_t *
+new_gluelist(isc_mem_t *mctx, dns_name_t *name) {
+ dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue));
+ *glue = (dns_glue_t){ 0 };
+ dns_name_t *gluename = dns_fixedname_initname(&glue->fixedname);
+
+ isc_mem_attach(mctx, &glue->mctx);
+ dns_name_copy(name, gluename);
+
+ return (glue);
+}
+
+static isc_result_t
+glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
+ dns_rdataset_t *unused DNS__DB_FLARG) {
+ dns_glue_additionaldata_ctx_t *ctx = NULL;
+ isc_result_t result;
+ dns_fixedname_t fixedname_a;
+ dns_name_t *name_a = NULL;
+ dns_rdataset_t rdataset_a, sigrdataset_a;
+ qpdata_t *node_a = NULL;
+ dns_fixedname_t fixedname_aaaa;
+ dns_name_t *name_aaaa = NULL;
+ dns_rdataset_t rdataset_aaaa, sigrdataset_aaaa;
+ qpdata_t *node_aaaa = NULL;
+ dns_glue_t *glue = NULL;
+
+ UNUSED(unused);
+
+ /*
+ * NS records want addresses in additional records.
+ */
+ INSIST(qtype == dns_rdatatype_a);
+
+ ctx = (dns_glue_additionaldata_ctx_t *)arg;
+
+ name_a = dns_fixedname_initname(&fixedname_a);
+ dns_rdataset_init(&rdataset_a);
+ dns_rdataset_init(&sigrdataset_a);
+
+ name_aaaa = dns_fixedname_initname(&fixedname_aaaa);
+ dns_rdataset_init(&rdataset_aaaa);
+ dns_rdataset_init(&sigrdataset_aaaa);
+
+ result = find(ctx->db, name, ctx->version, dns_rdatatype_a,
+ DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_a, name_a,
+ &rdataset_a, &sigrdataset_a DNS__DB_FLARG_PASS);
+ if (result == DNS_R_GLUE) {
+ glue = new_gluelist(ctx->db->mctx, name_a);
+
+ dns_rdataset_init(&glue->rdataset_a);
+ dns_rdataset_init(&glue->sigrdataset_a);
+ dns_rdataset_init(&glue->rdataset_aaaa);
+ dns_rdataset_init(&glue->sigrdataset_aaaa);
+
+ dns_rdataset_clone(&rdataset_a, &glue->rdataset_a);
+ if (dns_rdataset_isassociated(&sigrdataset_a)) {
+ dns_rdataset_clone(&sigrdataset_a,
+ &glue->sigrdataset_a);
+ }
+ }
+
+ result = find(ctx->db, name, ctx->version, dns_rdatatype_aaaa,
+ DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_aaaa,
+ name_aaaa, &rdataset_aaaa,
+ &sigrdataset_aaaa DNS__DB_FLARG_PASS);
+ if (result == DNS_R_GLUE) {
+ if (glue == NULL) {
+ glue = new_gluelist(ctx->db->mctx, name_aaaa);
+
+ dns_rdataset_init(&glue->rdataset_a);
+ dns_rdataset_init(&glue->sigrdataset_a);
+ dns_rdataset_init(&glue->rdataset_aaaa);
+ dns_rdataset_init(&glue->sigrdataset_aaaa);
+ } else {
+ INSIST(node_a == node_aaaa);
+ INSIST(dns_name_equal(name_a, name_aaaa));
+ }
+
+ dns_rdataset_clone(&rdataset_aaaa, &glue->rdataset_aaaa);
+ if (dns_rdataset_isassociated(&sigrdataset_aaaa)) {
+ dns_rdataset_clone(&sigrdataset_aaaa,
+ &glue->sigrdataset_aaaa);
+ }
+ }
+
+ /*
+ * If the currently processed NS record is in-bailiwick, mark any glue
+ * RRsets found for it with DNS_RDATASETATTR_REQUIRED. Note that for
+ * simplicity, glue RRsets for all in-bailiwick NS records are marked
+ * this way, even though dns_message_rendersection() only checks the
+ * attributes for the first rdataset associated with the first name
+ * added to the ADDITIONAL section.
+ */
+ if (glue != NULL && dns_name_issubdomain(name, ctx->nodename)) {
+ if (dns_rdataset_isassociated(&glue->rdataset_a)) {
+ glue->rdataset_a.attributes |=
+ DNS_RDATASETATTR_REQUIRED;
+ }
+ if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) {
+ glue->rdataset_aaaa.attributes |=
+ DNS_RDATASETATTR_REQUIRED;
+ }
+ }
+
+ if (glue != NULL) {
+ glue->next = ctx->glue_list;
+ ctx->glue_list = glue;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ if (dns_rdataset_isassociated(&rdataset_a)) {
+ dns_rdataset_disassociate(&rdataset_a);
+ }
+ if (dns_rdataset_isassociated(&sigrdataset_a)) {
+ dns_rdataset_disassociate(&sigrdataset_a);
+ }
+
+ if (dns_rdataset_isassociated(&rdataset_aaaa)) {
+ dns_rdataset_disassociate(&rdataset_aaaa);
+ }
+ if (dns_rdataset_isassociated(&sigrdataset_aaaa)) {
+ dns_rdataset_disassociate(&sigrdataset_aaaa);
+ }
+
+ if (node_a != NULL) {
+ dns__db_detachnode(ctx->db,
+ (dns_dbnode_t *)&node_a DNS__DB_FLARG_PASS);
+ }
+ if (node_aaaa != NULL) {
+ dns__db_detachnode(
+ ctx->db, (dns_dbnode_t *)&node_aaaa DNS__DB_FLARG_PASS);
+ }
+
+ return (result);
+}
+
+#define IS_REQUIRED_GLUE(r) (((r)->attributes & DNS_RDATASETATTR_REQUIRED) != 0)
+
+static void
+addglue_to_message(dns_glue_t *ge, dns_message_t *msg) {
+ for (; ge != NULL; ge = ge->next) {
+ dns_name_t *name = NULL;
+ dns_rdataset_t *rdataset_a = NULL;
+ dns_rdataset_t *sigrdataset_a = NULL;
+ dns_rdataset_t *rdataset_aaaa = NULL;
+ dns_rdataset_t *sigrdataset_aaaa = NULL;
+ dns_name_t *gluename = dns_fixedname_name(&ge->fixedname);
+ bool prepend_name = false;
+
+ dns_message_gettempname(msg, &name);
+
+ dns_name_copy(gluename, name);
+
+ if (dns_rdataset_isassociated(&ge->rdataset_a)) {
+ dns_message_gettemprdataset(msg, &rdataset_a);
+ }
+
+ if (dns_rdataset_isassociated(&ge->sigrdataset_a)) {
+ dns_message_gettemprdataset(msg, &sigrdataset_a);
+ }
+
+ if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
+ dns_message_gettemprdataset(msg, &rdataset_aaaa);
+ }
+
+ if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
+ dns_message_gettemprdataset(msg, &sigrdataset_aaaa);
+ }
+
+ if (rdataset_a != NULL) {
+ dns_rdataset_clone(&ge->rdataset_a, rdataset_a);
+ ISC_LIST_APPEND(name->list, rdataset_a, link);
+ if (IS_REQUIRED_GLUE(rdataset_a)) {
+ prepend_name = true;
+ }
+ }
+
+ if (sigrdataset_a != NULL) {
+ dns_rdataset_clone(&ge->sigrdataset_a, sigrdataset_a);
+ ISC_LIST_APPEND(name->list, sigrdataset_a, link);
+ }
+
+ if (rdataset_aaaa != NULL) {
+ dns_rdataset_clone(&ge->rdataset_aaaa, rdataset_aaaa);
+ ISC_LIST_APPEND(name->list, rdataset_aaaa, link);
+ if (IS_REQUIRED_GLUE(rdataset_aaaa)) {
+ prepend_name = true;
+ }
+ }
+ if (sigrdataset_aaaa != NULL) {
+ dns_rdataset_clone(&ge->sigrdataset_aaaa,
+ sigrdataset_aaaa);
+ ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link);
+ }
+
+ dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
+
+ /*
+ * When looking for required glue, dns_message_rendersection()
+ * only processes the first rdataset associated with the first
+ * name added to the ADDITIONAL section. dns_message_addname()
+ * performs an append on the list of names in a given section,
+ * so if any glue record was marked as required, we need to
+ * move the name it is associated with to the beginning of the
+ * list for the ADDITIONAL section or else required glue might
+ * not be rendered.
+ */
+ if (prepend_name) {
+ ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL],
+ name, link);
+ ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL],
+ name, link);
+ }
+ }
+}
+
+static dns_glue_t *
+newglue(qpzonedb_t *qpdb, qpdb_version_t *version, qpdata_t *node,
+ dns_rdataset_t *rdataset) {
+ dns_fixedname_t nodename;
+ dns_glue_additionaldata_ctx_t ctx = {
+ .db = (dns_db_t *)qpdb,
+ .version = (dns_dbversion_t *)version,
+ .nodename = dns_fixedname_initname(&nodename),
+ };
+
+ /*
+ * Get the owner name of the NS RRset - it will be necessary for
+ * identifying required glue in glue_nsdname_cb() (by
+ * determining which NS records in the delegation are
+ * in-bailiwick).
+ */
+ dns_name_copy(node->name, ctx.nodename);
+
+ (void)dns_rdataset_additionaldata(rdataset, dns_rootname,
+ glue_nsdname_cb, &ctx);
+
+ return (ctx.glue_list);
+}
+
+static isc_result_t
+addglue(dns_db_t *db, dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
+ dns_message_t *msg) {
+ qpzonedb_t *qpdb = (qpzonedb_t *)db;
+ qpdb_version_t *version = dbversion;
+ qpdata_t *node = (qpdata_t *)rdataset->slab.node;
+ dns_slabheader_t *header = dns_slabheader_fromrdataset(rdataset);
+
+ REQUIRE(rdataset->type == dns_rdatatype_ns);
+ REQUIRE(qpdb == (qpzonedb_t *)rdataset->slab.db);
+ REQUIRE(qpdb == version->qpdb);
+ REQUIRE(!IS_STUB(qpdb));
+
+ rcu_read_lock();
+
+ dns_glue_t *glue = rcu_dereference(header->glue_list);
+ if (glue == NULL) {
+ /* No cached glue was found in the table. Get new glue. */
+ glue = newglue(qpdb, version, node, rdataset);
+
+ /* Cache the glue or (void *)-1 if no glue was found. */
+ dns_glue_t *old_glue = rcu_cmpxchg_pointer(
+ &header->glue_list, NULL, (glue) ? glue : (void *)-1);
+ if (old_glue != NULL) {
+ /* Somebody else was faster */
+ freeglue(glue);
+ glue = old_glue;
+ } else if (glue != NULL) {
+ cds_wfs_push(&version->glue_stack, &header->wfs_node);
+ }
+ }
+
+ /* We have a cached result. Add it to the message and return. */
+
+ if (qpdb->gluecachestats != NULL) {
+ isc_stats_increment(
+ qpdb->gluecachestats,
+ (glue == (void *)-1)
+ ? dns_gluecachestatscounter_hits_absent
+ : dns_gluecachestatscounter_hits_present);
+ }
+
+ /*
+ * (void *)-1 is a special value that means no glue is present in the
+ * zone.
+ */
+ if (glue != (void *)-1) {
+ addglue_to_message(glue, msg);
+ }
+
+ rcu_read_unlock();
+
+ return (ISC_R_SUCCESS);
+}
+
static dns_dbmethods_t qpdb_zonemethods = {
.destroy = qpdb_destroy,
.beginload = beginload,
.addrdataset = addrdataset,
.subtractrdataset = subtractrdataset,
.deleterdataset = deleterdataset,
- .issecure = NULL,
- .nodecount = NULL,
+ .issecure = issecure,
+ .nodecount = nodecount,
.setloop = setloop,
.getoriginnode = getoriginnode,
- .getnsec3parameters = NULL,
- .findnsec3node = NULL,
- .setsigningtime = NULL,
- .getsigningtime = NULL,
- .getsize = NULL,
- .setgluecachestats = NULL,
- .locknode = NULL,
- .unlocknode = NULL,
- .addglue = NULL,
+ .getnsec3parameters = getnsec3parameters,
+ .findnsec3node = findnsec3node,
+ .setsigningtime = setsigningtime,
+ .getsigningtime = getsigningtime,
+ .getsize = getsize,
+ .setgluecachestats = setgluecachestats,
+ .locknode = locknode,
+ .unlocknode = unlocknode,
+ .addglue = addglue,
.deletedata = deletedata,
};