/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include #include #include #include #include #include #include #include #include "zone_p.h" static void zonemgr_free(dns_zonemgr_t *zmgr); /*** *** Zone manager. ***/ static void setrl(isc_ratelimiter_t *rl, unsigned int *rate, unsigned int value) { isc_interval_t interval; uint32_t s, ns; uint32_t pertic; if (value == 0) { value = 1; } if (value == 1) { s = 1; ns = 0; pertic = 1; } else if (value <= 10) { s = 0; ns = 1000000000 / value; pertic = 1; } else { s = 0; ns = (1000000000 / value) * 10; pertic = 10; } isc_interval_set(&interval, s, ns); isc_ratelimiter_setinterval(rl, &interval); isc_ratelimiter_setpertic(rl, pertic); *rate = value; } void dns_zonemgr_create(isc_mem_t *mctx, dns_zonemgr_t **zmgrp) { dns_zonemgr_t *zmgr = NULL; isc_loop_t *loop = isc_loop(); REQUIRE(mctx != NULL); REQUIRE(zmgrp != NULL && *zmgrp == NULL); zmgr = isc_mem_get(mctx, sizeof(*zmgr)); *zmgr = (dns_zonemgr_t){ .workers = isc_loopmgr_nloops(), .transfersin = 10, .transfersperns = 2, }; isc_refcount_init(&zmgr->refs, 1); isc_mem_attach(mctx, &zmgr->mctx); ISC_LIST_INIT(zmgr->zones); ISC_LIST_INIT(zmgr->waiting_for_xfrin); ISC_LIST_INIT(zmgr->xfrin_in_progress); isc_rwlock_init(&zmgr->rwlock); isc_ratelimiter_create(loop, &zmgr->checkdsrl); isc_ratelimiter_create(loop, &zmgr->notifyrl); isc_ratelimiter_create(loop, &zmgr->refreshrl); isc_ratelimiter_create(loop, &zmgr->startupnotifyrl); isc_ratelimiter_create(loop, &zmgr->startuprefreshrl); zmgr->mctxpool = isc_mem_cget(zmgr->mctx, zmgr->workers, sizeof(zmgr->mctxpool[0])); for (size_t i = 0; i < zmgr->workers; i++) { isc_mem_create("zonemgr-mctxpool", &zmgr->mctxpool[i]); } /* Default to 20 refresh queries / notifies / checkds per second. */ setrl(zmgr->checkdsrl, &zmgr->checkdsrate, 20); setrl(zmgr->notifyrl, &zmgr->notifyrate, 20); setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, 20); setrl(zmgr->refreshrl, &zmgr->serialqueryrate, 20); setrl(zmgr->startuprefreshrl, &zmgr->startupserialqueryrate, 20); isc_ratelimiter_setpushpop(zmgr->startupnotifyrl, true); isc_ratelimiter_setpushpop(zmgr->startuprefreshrl, true); zmgr->tlsctx_cache = NULL; isc_rwlock_init(&zmgr->tlsctx_cache_rwlock); zmgr->magic = ZONEMGR_MAGIC; *zmgrp = zmgr; } isc_result_t dns_zonemgr_createzone(dns_zonemgr_t *zmgr, dns_zone_t **zonep) { isc_mem_t *mctx = NULL; dns_zone_t *zone = NULL; isc_tid_t tid; REQUIRE(DNS_ZONEMGR_VALID(zmgr)); REQUIRE(zonep != NULL && *zonep == NULL); if (zmgr->mctxpool == NULL) { return ISC_R_FAILURE; } tid = isc_random_uniform(zmgr->workers); mctx = zmgr->mctxpool[tid]; if (mctx == NULL) { return ISC_R_FAILURE; } dns_zone_create(&zone, mctx, tid); *zonep = zone; return ISC_R_SUCCESS; } isc_result_t dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(DNS_ZONEMGR_VALID(zmgr)); RWLOCK(&zmgr->rwlock, isc_rwlocktype_write); LOCK_ZONE(zone); REQUIRE(zone->timer == NULL); REQUIRE(zone->zmgr == NULL); isc_loop_t *loop = isc_loop_get(zone->tid); isc_loop_attach(loop, &zone->loop); ISC_LIST_APPEND(zmgr->zones, zone, link); zone->zmgr = zmgr; isc_refcount_increment(&zmgr->refs); UNLOCK_ZONE(zone); RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write); return ISC_R_SUCCESS; } void dns_zonemgr_releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(DNS_ZONEMGR_VALID(zmgr)); REQUIRE(zone->zmgr == zmgr); RWLOCK(&zmgr->rwlock, isc_rwlocktype_write); LOCK_ZONE(zone); ISC_LIST_UNLINK(zmgr->zones, zone, link); if (zone->timer != NULL) { isc_refcount_decrement(&zone->irefs); isc_timer_destroy(&zone->timer); } isc_loop_detach(&zone->loop); /* Detach below, outside of the write lock. */ zone->zmgr = NULL; UNLOCK_ZONE(zone); RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write); dns_zonemgr_detach(&zmgr); } void dns_zonemgr_attach(dns_zonemgr_t *source, dns_zonemgr_t **target) { REQUIRE(DNS_ZONEMGR_VALID(source)); REQUIRE(target != NULL && *target == NULL); isc_refcount_increment(&source->refs); *target = source; } void dns_zonemgr_detach(dns_zonemgr_t **zmgrp) { dns_zonemgr_t *zmgr; REQUIRE(zmgrp != NULL); zmgr = *zmgrp; *zmgrp = NULL; REQUIRE(DNS_ZONEMGR_VALID(zmgr)); if (isc_refcount_decrement(&zmgr->refs) == 1) { zonemgr_free(zmgr); } } isc_result_t dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); RWLOCK(&zmgr->rwlock, isc_rwlocktype_read); ISC_LIST_FOREACH(zmgr->zones, zone, link) { isc_time_t now; LOCK_ZONE(zone); now = isc_time_now(); dns__zone_settimer(zone, &now); UNLOCK_ZONE(zone); } RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read); /* * Recent configuration changes may have increased the * amount of available transfers quota. Make sure any * transfers currently blocked on quota get started if * possible. */ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write); dns__zonemgr_resume_xfrs(zmgr, true); RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write); return ISC_R_SUCCESS; } void dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); isc_ratelimiter_shutdown(zmgr->checkdsrl); isc_ratelimiter_shutdown(zmgr->notifyrl); isc_ratelimiter_shutdown(zmgr->refreshrl); isc_ratelimiter_shutdown(zmgr->startupnotifyrl); isc_ratelimiter_shutdown(zmgr->startuprefreshrl); for (size_t i = 0; i < zmgr->workers; i++) { isc_mem_detach(&zmgr->mctxpool[i]); } RWLOCK(&zmgr->rwlock, isc_rwlocktype_read); ISC_LIST_FOREACH(zmgr->zones, zone, link) { LOCK_ZONE(zone); dns__zone_forward_cancel(zone); UNLOCK_ZONE(zone); } RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read); } static void zonemgr_free(dns_zonemgr_t *zmgr) { REQUIRE(ISC_LIST_EMPTY(zmgr->zones)); zmgr->magic = 0; isc_refcount_destroy(&zmgr->refs); isc_ratelimiter_detach(&zmgr->checkdsrl); isc_ratelimiter_detach(&zmgr->notifyrl); isc_ratelimiter_detach(&zmgr->refreshrl); isc_ratelimiter_detach(&zmgr->startupnotifyrl); isc_ratelimiter_detach(&zmgr->startuprefreshrl); isc_mem_cput(zmgr->mctx, zmgr->mctxpool, zmgr->workers, sizeof(zmgr->mctxpool[0])); isc_rwlock_destroy(&zmgr->rwlock); isc_rwlock_destroy(&zmgr->tlsctx_cache_rwlock); if (zmgr->tlsctx_cache != NULL) { isc_tlsctx_cache_detach(&zmgr->tlsctx_cache); } isc_mem_putanddetach(&zmgr->mctx, zmgr, sizeof(*zmgr)); } void dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, uint32_t value) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); zmgr->transfersin = value; } uint32_t dns_zonemgr_gettransfersin(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); return zmgr->transfersin; } void dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, uint32_t value) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); zmgr->transfersperns = value; } uint32_t dns_zonemgr_gettransfersperns(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); return zmgr->transfersperns; } /* * Try to start a new incoming zone transfer to fill a quota * slot that was just vacated. * * Requires: * The zone manager is locked by the caller. */ void dns__zonemgr_resume_xfrs(dns_zonemgr_t *zmgr, bool multi) { ISC_LIST_FOREACH(zmgr->waiting_for_xfrin, zone, statelink) { isc_result_t result; result = dns__zonemgr_start_xfrin_ifquota(zmgr, zone); if (result == ISC_R_SUCCESS) { if (multi) { continue; } /* * We successfully filled the slot. We're done. */ break; } else if (result == ISC_R_QUOTA) { /* * Not enough quota. This is probably the per-server * quota, because we usually get called when a unit of * global quota has just been freed. Try the next * zone, it may succeed if it uses another primary. */ continue; } else { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), "starting zone transfer: %s", isc_result_totext(result)); break; } } } /* * This event callback is called when a zone has received * any necessary zone transfer quota. This is the time * to go ahead and start the transfer. */ static void got_transfer_quota(void *arg) { dns_zone_t *zone = (dns_zone_t *)arg; isc_result_t result = ISC_R_SUCCESS; dns_peer_t *peer = NULL; char primary[ISC_SOCKADDR_FORMATSIZE]; char source[ISC_SOCKADDR_FORMATSIZE]; dns_rdatatype_t xfrtype; uint32_t ixfr_maxdiffs = 0; isc_netaddr_t primaryip; isc_sockaddr_t primaryaddr; isc_sockaddr_t sourceaddr; dns_transport_type_t soa_transport_type = DNS_TRANSPORT_NONE; const char *soa_before = ""; bool loaded; isc_tlsctx_cache_t *zmgr_tlsctx_cache = NULL; dns_xfrin_t *xfr = NULL; if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) { dns__zone_xfrdone(zone, NULL, ISC_R_CANCELED); return; } primaryaddr = dns_remote_curraddr(&zone->primaries); isc_sockaddr_format(&primaryaddr, primary, sizeof(primary)); if (dns_unreachcache_find(zone->view->unreachcache, &primaryaddr, &zone->sourceaddr) == ISC_R_SUCCESS) { isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source)); dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_INFO, "got_transfer_quota: skipping zone transfer as " "primary %s (source %s) is unreachable (cached)", primary, source); dns__zone_xfrdone(zone, NULL, ISC_R_CANCELED); return; } isc_netaddr_fromsockaddr(&primaryip, &primaryaddr); (void)dns_peerlist_peerbyaddr(zone->view->peers, &primaryip, &peer); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR)) { soa_before = "SOA before "; } /* * Decide whether we should request IXFR or AXFR. */ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); loaded = (zone->db != NULL); ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (!loaded) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), "no database exists yet, requesting AXFR of " "initial version from %s", primary); xfrtype = dns_rdatatype_axfr; } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), "forced reload, requesting AXFR of " "initial version from %s", primary); xfrtype = dns_rdatatype_axfr; } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOIXFR)) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), "retrying with AXFR from %s due to " "previous IXFR failure", primary); xfrtype = dns_rdatatype_axfr; LOCK_ZONE(zone); DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOIXFR); UNLOCK_ZONE(zone); } else { bool use_ixfr = true; if (peer != NULL) { result = dns_peer_getrequestixfr(peer, &use_ixfr); } if (peer == NULL || result != ISC_R_SUCCESS) { use_ixfr = zone->requestixfr; } if (peer != NULL) { result = dns_peer_getrequestixfrmaxdiffs( peer, &ixfr_maxdiffs); } if (peer == NULL || result != ISC_R_SUCCESS) { ixfr_maxdiffs = zone->requestixfr_maxdiffs; } if (!use_ixfr) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), "IXFR disabled, " "requesting %sAXFR from %s", soa_before, primary); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR)) { xfrtype = dns_rdatatype_soa; } else { xfrtype = dns_rdatatype_axfr; } } else { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), "requesting IXFR from %s", primary); xfrtype = dns_rdatatype_ixfr; } } /* * Determine if we should attempt to sign the request with TSIG. */ result = ISC_R_NOTFOUND; /* * First, look for a tsig key in the primaries statement, then * try for a server key. */ if (dns_remote_keyname(&zone->primaries) != NULL) { dns_view_t *view = dns_zone_getview(zone); dns_name_t *keyname = dns_remote_keyname(&zone->primaries); result = dns_view_gettsig(view, keyname, &zone->tsigkey); } if (result != ISC_R_SUCCESS) { INSIST(zone->tsigkey == NULL); result = dns_view_getpeertsig(zone->view, &primaryip, &zone->tsigkey); } if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_ERROR, "could not get TSIG key for zone transfer: %s", isc_result_totext(result)); } /* * Get the TLS transport for the primary, if configured. */ if (dns_remote_tlsname(&zone->primaries) != NULL) { dns_view_t *view = dns_zone_getview(zone); dns_name_t *tlsname = dns_remote_tlsname(&zone->primaries); result = dns_view_gettransport(view, DNS_TRANSPORT_TLS, tlsname, &zone->transport); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_ERROR, "could not get TLS configuration for " "zone transfer: %s", isc_result_totext(result)); } } if (xfrtype != dns_rdatatype_soa) { /* * If 'xfrtype' is dns_rdatatype_soa, then the SOA query will be * performed by xfrin, otherwise, the SOA request performed by * soa_query() was successful and we should inform the xfrin * about the transport type used for that query, so that the * information can be presented in the statistics channel. */ soa_transport_type = dns_zone_getrequesttransporttype(zone); } LOCK_ZONE(zone); sourceaddr = zone->sourceaddr; UNLOCK_ZONE(zone); INSIST(isc_sockaddr_pf(&primaryaddr) == isc_sockaddr_pf(&sourceaddr)); dns__zonemgr_tlsctx_attach(zone->zmgr, &zmgr_tlsctx_cache); dns_xfrin_create(zone, xfrtype, ixfr_maxdiffs, &primaryaddr, &sourceaddr, zone->tsigkey, soa_transport_type, zone->transport, zmgr_tlsctx_cache, zone->mctx, &xfr); INSIST(xfr != NULL); isc_tlsctx_cache_detach(&zmgr_tlsctx_cache); LOCK_ZONE(zone); if (zone->xfr != NULL) { dns_xfrin_detach(&zone->xfr); } dns_xfrin_attach(xfr, &zone->xfr); UNLOCK_ZONE(zone); dns_xfrin_detach(&xfr); /* * Any failure in this function is handled like a failed * zone transfer. This ensures that we get removed from * zmgr->xfrin_in_progress. */ result = dns_xfrin_start(zone->xfr, dns__zone_xfrdone); if (result != ISC_R_SUCCESS) { dns__zone_xfrdone(zone, NULL, result); return; } LOCK_ZONE(zone); if (xfrtype == dns_rdatatype_axfr) { if (isc_sockaddr_pf(&primaryaddr) == PF_INET) { dns__zone_stats_increment( zone, dns_zonestatscounter_axfrreqv4); } else { dns__zone_stats_increment( zone, dns_zonestatscounter_axfrreqv6); } } else if (xfrtype == dns_rdatatype_ixfr) { if (isc_sockaddr_pf(&primaryaddr) == PF_INET) { dns__zone_stats_increment( zone, dns_zonestatscounter_ixfrreqv4); } else { dns__zone_stats_increment( zone, dns_zonestatscounter_ixfrreqv6); } } UNLOCK_ZONE(zone); } /* * Try to start an incoming zone transfer for 'zone', quota permitting. * * Requires: * The zone manager is locked by the caller. * * Returns: * ISC_R_SUCCESS There was enough quota and we attempted to * start a transfer. dns__zone_xfrdone() has been or will * be called. * ISC_R_QUOTA Not enough quota. * Others Failure. */ isc_result_t dns__zonemgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone) { dns_peer_t *peer = NULL; isc_netaddr_t primaryip; isc_sockaddr_t curraddr; uint32_t nxfrsin, nxfrsperns; uint32_t maxtransfersin, maxtransfersperns; /* * If we are exiting just pretend we got quota so the zone will * be cleaned up in the zone's loop context. */ LOCK_ZONE(zone); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) { UNLOCK_ZONE(zone); goto gotquota; } /* * Find any configured information about the server we'd * like to transfer this zone from. */ curraddr = dns_remote_curraddr(&zone->primaries); isc_netaddr_fromsockaddr(&primaryip, &curraddr); (void)dns_peerlist_peerbyaddr(zone->view->peers, &primaryip, &peer); UNLOCK_ZONE(zone); /* * Determine the total maximum number of simultaneous * transfers allowed, and the maximum for this specific * primary. */ maxtransfersin = zmgr->transfersin; maxtransfersperns = zmgr->transfersperns; if (peer != NULL) { (void)dns_peer_gettransfers(peer, &maxtransfersperns); } /* * Count the total number of transfers that are in progress, * and the number of transfers in progress from this primary. * We linearly scan a list of all transfers; if this turns * out to be too slow, we could hash on the primary address. */ nxfrsin = nxfrsperns = 0; ISC_LIST_FOREACH(zmgr->xfrin_in_progress, x, statelink) { isc_netaddr_t xip; isc_sockaddr_t xaddr; LOCK_ZONE(x); xaddr = dns_remote_curraddr(&x->primaries); isc_netaddr_fromsockaddr(&xip, &xaddr); UNLOCK_ZONE(x); nxfrsin++; if (isc_netaddr_equal(&xip, &primaryip)) { nxfrsperns++; } } /* Enforce quota. */ if (nxfrsin >= maxtransfersin) { return ISC_R_QUOTA; } if (nxfrsperns >= maxtransfersperns) { return ISC_R_QUOTA; } gotquota: /* * We have sufficient quota. Move the zone to the "xfrin_in_progress" * list and start the actual transfer asynchronously. */ LOCK_ZONE(zone); INSIST(zone->statelist == &zmgr->waiting_for_xfrin); ISC_LIST_UNLINK(zmgr->waiting_for_xfrin, zone, statelink); ISC_LIST_APPEND(zmgr->xfrin_in_progress, zone, statelink); zone->statelist = &zmgr->xfrin_in_progress; isc_async_run(zone->loop, got_transfer_quota, zone); dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_INFO, "Transfer started."); UNLOCK_ZONE(zone); return ISC_R_SUCCESS; } void dns_zonemgr_setcheckdsrate(dns_zonemgr_t *zmgr, unsigned int value) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); setrl(zmgr->checkdsrl, &zmgr->checkdsrate, value); } void dns_zonemgr_setnotifyrate(dns_zonemgr_t *zmgr, unsigned int value) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); setrl(zmgr->notifyrl, &zmgr->notifyrate, value); } void dns_zonemgr_setstartupnotifyrate(dns_zonemgr_t *zmgr, unsigned int value) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, value); } void dns_zonemgr_setkeystores(dns_zonemgr_t *zmgr, dns_keystorelist_t *keystores) { zmgr->keystores = keystores; } void dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); setrl(zmgr->refreshrl, &zmgr->serialqueryrate, value); /* XXXMPA separate out once we have the code to support this. */ setrl(zmgr->startuprefreshrl, &zmgr->startupserialqueryrate, value); } unsigned int dns_zonemgr_getnotifyrate(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); return zmgr->notifyrate; } void dns__zonemgr_getnotifyrl(dns_zonemgr_t *zmgr, isc_ratelimiter_t **prl) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); *prl = zmgr->notifyrl; } unsigned int dns_zonemgr_getstartupnotifyrate(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); return zmgr->startupnotifyrate; } void dns__zonemgr_getstartupnotifyrl(dns_zonemgr_t *zmgr, isc_ratelimiter_t **prl) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); *prl = zmgr->startupnotifyrl; } unsigned int dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); return zmgr->serialqueryrate; } unsigned int dns_zonemgr_getcount(dns_zonemgr_t *zmgr, dns_zonestate_t state) { unsigned int count = 0; REQUIRE(DNS_ZONEMGR_VALID(zmgr)); RWLOCK(&zmgr->rwlock, isc_rwlocktype_read); switch (state) { case DNS_ZONESTATE_XFERRUNNING: ISC_LIST_FOREACH(zmgr->xfrin_in_progress, zone, statelink) { count++; } break; case DNS_ZONESTATE_XFERDEFERRED: ISC_LIST_FOREACH(zmgr->waiting_for_xfrin, zone, statelink) { count++; } break; case DNS_ZONESTATE_XFERFIRSTREFRESH: ISC_LIST_FOREACH(zmgr->zones, zone, link) { if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FIRSTREFRESH)) { count++; } } break; case DNS_ZONESTATE_SOAQUERY: ISC_LIST_FOREACH(zmgr->zones, zone, link) { if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH)) { count++; } } break; case DNS_ZONESTATE_ANY: ISC_LIST_FOREACH(zmgr->zones, zone, link) { dns_view_t *view = zone->view; if (view != NULL && strcmp(view->name, "_bind") == 0) { continue; } count++; } break; case DNS_ZONESTATE_AUTOMATIC: ISC_LIST_FOREACH(zmgr->zones, zone, link) { dns_view_t *view = zone->view; if (view != NULL && strcmp(view->name, "_bind") == 0) { continue; } if (zone->automatic) { count++; } } break; default: UNREACHABLE(); } RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read); return count; } isc_result_t dns_zonemgr_next_zone(dns_zone_t *zone, dns_zone_t **next) { REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(next != NULL && *next == NULL); *next = ISC_LIST_NEXT(zone, link); if (*next == NULL) { return ISC_R_NOMORE; } else { return ISC_R_SUCCESS; } } isc_result_t dns_zonemgr_first_zone(dns_zonemgr_t *zmgr, dns_zone_t **first) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); REQUIRE(first != NULL && *first == NULL); *first = ISC_LIST_HEAD(zmgr->zones); if (*first == NULL) { return ISC_R_NOMORE; } else { return ISC_R_SUCCESS; } }