📄 adb.c
字号:
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); return (ai);}static inline voidfree_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { dns_adbaddrinfo_t *ai; INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo)); ai = *ainfo; *ainfo = NULL; INSIST(ai->entry == NULL); INSIST(!ISC_LINK_LINKED(ai, publink)); ai->magic = 0; isc_mempool_put(adb->aimp, ai);}/* * Search for the name. NOTE: The bucket is kept locked on both * success and failure, so it must always be unlocked by the caller! * * On the first call to this function, *bucketp must be set to * DNS_ADB_INVALIDBUCKET. */static inline dns_adbname_t *find_name_and_lock(dns_adb_t *adb, dns_name_t *name, 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);}/* * Search for the address. NOTE: The bucket is kept locked on both * success and failure, so it must always be unlocked by the caller. * * On the first call to this function, *bucketp must be set to * DNS_ADB_INVALIDBUCKET. This will cause a lock to occur. On * later calls (within the same "lock path") it can be left alone, so * if this function is called multiple times locking is only done if * the bucket changes. */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; 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; } 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);}/* * Entry bucket MUST be locked! */static isc_boolean_tentry_is_bad_for_zone(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *zone, isc_stdtime_t now){ dns_adbzoneinfo_t *zi, *next_zi; isc_boolean_t is_bad; is_bad = ISC_FALSE; zi = ISC_LIST_HEAD(entry->zoneinfo); if (zi == NULL) return (ISC_FALSE); while (zi != NULL) { next_zi = ISC_LIST_NEXT(zi, plink); /* * Has the entry expired? */ if (zi->lame_timer < now) { ISC_LIST_UNLINK(entry->zoneinfo, zi, plink); free_adbzoneinfo(adb, &zi); } /* * Order tests from least to most expensive. */ if (zi != NULL && !is_bad) { if (dns_name_equal(zone, &zi->zone)) is_bad = ISC_TRUE; } zi = next_zi; } return (is_bad);}static voidcopy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *zone, 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_bad_for_zone(adb, entry, zone, 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_bad_for_zone(adb, entry, zone, 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]);}static voidshutdown_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);}/* * Name bucket must be locked; adb may be locked; no other locks held. */static isc_boolean_tcheck_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { dns_adbname_t *name; isc_result_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);}/* * Entry bucket must be locked; adb may be locked; no other locks held. */static isc_boolean_tcheck_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); if (adb->overmem) { isc_uint32_t val; isc_random_get(&val); expire = ISC_TF((val % 4) == 0); } else expire = ISC_FALSE; 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);}/* * ADB must be locked, and no other locks held. */static isc_boolean_tcleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { dns_adbname_t *name; dns_adbname_t *next_name; isc_result_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);}/* * ADB must be locked, and no other locks held. */static isc_boolean_tcleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) { dns_adbentry_t *entry, *next_entry; isc_boolean_t result = ISC_FALSE; 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);}static voidtimer_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;#ifdef DUMP_ADB_AFTER_CLEANING dump_adb(adb, stdout, ISC_TRUE);#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); UNLOCK(&adb->lock); isc_event_free(&ev);}static voiddestroy(dns_adb_t *adb) { adb->magic = 0; /* * 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->zimp); 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); DESTROYLOCK(&adb->reflock); DESTROYLOCK(&adb->lock); DESTROYLOCK(&adb->mplock); isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));}/* * Public functions. */isc_result_tdns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, 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->zimp = 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -