📄 rbtdb.c
字号:
if (!changed->dirty) { UNLINK(version->changed_list, changed, link); APPEND(*cleanup_list, changed, link); } }}static voidcloseversion(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; isc_mutex_t *lock; REQUIRE(VALID_RBTDB(rbtdb)); version = (rbtdb_version_t *)*versionp; cleanup_version = NULL; ISC_LIST_INIT(cleanup_list); LOCK(&rbtdb->lock); INSIST(version->references > 0); INSIST(!version->writer || !(commit && version->references > 1)); version->references--; serial = version->serial; if (version->references == 0) { if (version->writer) { if (commit) { INSIST(version->commit_ok); INSIST(version == rbtdb->future_version); 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 (rbtdb->current_version->references == 0) { cleanup_version = rbtdb->current_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; } 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; UNLOCK(&rbtdb->lock); 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) { next_changed = NEXT(changed, link); rbtnode = changed->node; lock = &rbtdb->node_locks[rbtnode->locknum].lock; LOCK(lock); INSIST(rbtnode->references > 0); rbtnode->references--; if (rollback) rollback_node(rbtnode, serial); if (rbtnode->references == 0) no_references(rbtdb, rbtnode, least_serial, isc_rwlocktype_none); UNLOCK(lock); isc_mem_put(rbtdb->common.mctx, changed, sizeof(*changed)); } } *versionp = NULL;}/* * Add the necessary magic for the wildcard name 'name' * to be found in 'rbtdb'. * * In order for wildcard matching to work correctly in * zone_find(), we must ensure that a node for the wildcarding * level exists in the database, and has its 'find_callback' * and 'wild' bits set. * * E.g. if the wildcard name is "*.sub.example." then we * must ensure that "sub.example." exists and is marked as * a wildcard level. */static isc_result_tadd_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);}static isc_result_tadd_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);}static isc_result_tfindnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, dns_dbnode_t **nodep){ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; dns_rbtnode_t *node = NULL; dns_name_t nodename; unsigned int locknum; 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;#else 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); } } locknum = node->locknum; LOCK(&rbtdb->node_locks[locknum].lock); new_reference(rbtdb, node); UNLOCK(&rbtdb->node_locks[locknum].lock); RWUNLOCK(&rbtdb->tree_lock, locktype); *nodep = (dns_dbnode_t *)node; return (ISC_R_SUCCESS);}static isc_result_tzone_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; LOCK(&(search->rbtdb->node_locks[node->locknum].lock)); /* * 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; } UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock)); return (result);}static inline voidbind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header, isc_stdtime_t now, dns_rdataset_t *rdataset){ unsigned char *raw; /* * Caller must be holding the node lock. */ 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 (header->count == ISC_UINT32_MAX) header->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_tsetup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep, 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; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -