diff --git a/lib/dns/include/dns/rpz.h b/lib/dns/include/dns/rpz.h index bde983182f..ff46c4005b 100644 --- a/lib/dns/include/dns/rpz.h +++ b/lib/dns/include/dns/rpz.h @@ -144,27 +144,20 @@ struct dns_rpz_zone { dns_ttl_t max_policy_ttl; dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */ - uint32_t min_update_interval; /* minimal interval between - * updates */ - isc_ht_t *nodes; /* entries in zone */ - dns_rpz_zones_t *rpzs; /* owner */ - isc_time_t lastupdated; /* last time the zone was processed - * */ - bool updatepending; /* there is an update - * pending/waiting */ - bool updaterunning; /* there is an update running */ - dns_db_t *db; /* zones database */ - dns_dbversion_t *dbversion; /* version we will be updating to */ - dns_db_t *updb; /* zones database we're working on */ - dns_dbversion_t *updbversion; /* version we're currently working - * on */ - dns_dbiterator_t *updbit; /* iterator to use when updating */ - isc_ht_t *newnodes; /* entries in zone being updated */ - bool db_registered; /* is the notify event - * registered? */ - bool addsoa; /* add soa to the additional section */ - isc_timer_t *updatetimer; - isc_event_t updateevent; + uint32_t min_update_interval; /* minimal interval between updates */ + isc_ht_t *nodes; /* entries in zone */ + dns_rpz_zones_t *rpzs; /* owner */ + isc_time_t lastupdated; /* last time the zone was processed */ + bool updatepending; /* there is an update pending */ + bool updaterunning; /* there is an update running */ + isc_result_t updateresult; /* result from the offloaded work */ + dns_db_t *db; /* zones database */ + dns_dbversion_t *dbversion; /* version we will be updating to */ + dns_db_t *updb; /* zones database we're working on */ + dns_dbversion_t *updbversion; /* version we're working on */ + bool addsoa; /* add soa to the additional section */ + isc_timer_t *updatetimer; + isc_event_t updateevent; }; /* diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index f16a907e77..18a38a4f81 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -88,11 +88,6 @@ #define DNS_RPZ_HTSIZE_MAX 24 #define DNS_RPZ_HTSIZE_DIV 3 -/* - * Maximum number of nodes to process per quantum - */ -#define DNS_RPZ_QUANTUM 1024 - static void update_from_db(dns_rpz_zone_t *rpz); @@ -172,6 +167,9 @@ struct dns_rpz_nm_data { dns_rpz_nm_zbits_t wild; }; +static isc_result_t +rpz_shuttingdown(dns_rpz_zone_t *rpz); + static isc_result_t rpz_add(dns_rpz_zone_t *rpz, const dns_name_t *src_name); static void @@ -1535,7 +1533,7 @@ dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp) { /* * This will never be used, but costs us nothing and - * simplifies update_from_db + * simplifies update_from_db(). */ isc_ht_init(&rpz->nodes, rpzs->mctx, 1, ISC_HT_CASE_SENSITIVE); @@ -1574,7 +1572,6 @@ isc_result_t dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg) { dns_rpz_zone_t *rpz = (dns_rpz_zone_t *)fn_arg; isc_time_t now; - uint64_t tdiff; isc_result_t result = ISC_R_SUCCESS; char dname[DNS_NAME_FORMATSIZE]; @@ -1599,15 +1596,18 @@ dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg) { dns_db_attach(db, &rpz->db); } + dns_name_format(&rpz->origin, dname, DNS_NAME_FORMATSIZE); + if (!rpz->updatepending && !rpz->updaterunning) { + uint64_t tdiff; + rpz->updatepending = true; + isc_time_now(&now); tdiff = isc_time_microdiff(&now, &rpz->lastupdated) / 1000000; if (tdiff < rpz->min_update_interval) { uint64_t defer = rpz->min_update_interval - tdiff; isc_interval_t interval; - dns_name_format(&rpz->origin, dname, - DNS_NAME_FORMATSIZE); isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, ISC_LOG_INFO, "rpz: %s: new zone version came " @@ -1616,12 +1616,9 @@ dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg) { dname, defer); isc_interval_set(&interval, (unsigned int)defer, 0); dns_db_currentversion(rpz->db, &rpz->dbversion); - result = isc_timer_reset(rpz->updatetimer, - isc_timertype_once, NULL, - &interval, true); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } + (void)isc_timer_reset(rpz->updatetimer, + isc_timertype_once, NULL, + &interval, true); } else { isc_event_t *event = NULL; @@ -1637,7 +1634,6 @@ dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg) { } } else { rpz->updatepending = true; - dns_name_format(&rpz->origin, dname, DNS_NAME_FORMATSIZE); isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3), "rpz: %s: update already queued or running", @@ -1647,8 +1643,6 @@ dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg) { } dns_db_currentversion(rpz->db, &rpz->dbversion); } - -cleanup: UNLOCK(&rpz->rpzs->maint_lock); return (result); @@ -1680,38 +1674,75 @@ dns_rpz_update_taskaction(isc_task_t *task, isc_event_t *event) { UNLOCK(&rpz->rpzs->maint_lock); } +static void +update_rpz_done_cb(void *data, isc_result_t result) { + dns_rpz_zone_t *rpz = (dns_rpz_zone_t *)data; + char dname[DNS_NAME_FORMATSIZE]; + + if (result == ISC_R_SUCCESS && rpz->updateresult != ISC_R_SUCCESS) { + result = rpz->updateresult; + } + + LOCK(&rpz->rpzs->maint_lock); + rpz->updaterunning = false; + + dns_name_format(&rpz->origin, dname, DNS_NAME_FORMATSIZE); + + /* If there's no update pending, finish. */ + if (!rpz->updatepending) { + goto done; + } + + /* If there's an update pending, schedule it */ + if (rpz->min_update_interval > 0) { + uint64_t defer = rpz->min_update_interval; + isc_interval_t interval; + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_INFO, + "rpz: %s: new zone version came " + "too soon, deferring update for " + "%" PRIu64 " seconds", + dname, defer); + isc_interval_set(&interval, (unsigned int)defer, 0); + (void)isc_timer_reset(rpz->updatetimer, isc_timertype_once, + NULL, &interval, true); + } else { + isc_event_t *event = NULL; + INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link)); + ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0, + NULL, DNS_EVENT_RPZUPDATED, + dns_rpz_update_taskaction, rpz, rpz, NULL, NULL); + event = &rpz->updateevent; + isc_task_send(rpz->rpzs->updater, &event); + } + +done: + dns_db_closeversion(rpz->updb, &rpz->updbversion, false); + dns_db_detach(&rpz->updb); + + UNLOCK(&rpz->rpzs->maint_lock); + + rpz_detach(&rpz); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, + ISC_LOG_INFO, "rpz: %s: reload done: %s", dname, + isc_result_totext(result)); +} + static isc_result_t -setup_update(dns_rpz_zone_t *rpz) { +update_nodes(dns_rpz_zone_t *rpz, isc_ht_t *newnodes) { isc_result_t result; + dns_dbiterator_t *updbit = NULL; + dns_name_t *name = NULL; + dns_fixedname_t fixname; char domain[DNS_NAME_FORMATSIZE]; - unsigned int nodecount; - uint32_t hashsize; dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, - ISC_LOG_INFO, "rpz: %s: reload start", domain); - nodecount = dns_db_nodecount(rpz->updb, dns_dbtree_main); - hashsize = 1; - while (nodecount != 0 && - hashsize <= (DNS_RPZ_HTSIZE_MAX + DNS_RPZ_HTSIZE_DIV)) - { - hashsize++; - nodecount >>= 1; - } + name = dns_fixedname_initname(&fixname); - if (hashsize > DNS_RPZ_HTSIZE_DIV) { - hashsize -= DNS_RPZ_HTSIZE_DIV; - } - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, - ISC_LOG_DEBUG(1), "rpz: %s: using hashtable size %d", - domain, hashsize); - - isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize, - ISC_HT_CASE_SENSITIVE); - - result = dns_db_createiterator(rpz->updb, DNS_DB_NONSEC3, &rpz->updbit); + result = dns_db_createiterator(rpz->updb, DNS_DB_NONSEC3, &updbit); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, @@ -1720,8 +1751,8 @@ setup_update(dns_rpz_zone_t *rpz) { goto cleanup; } - result = dns_dbiterator_first(rpz->updbit); - if (result != ISC_R_SUCCESS) { + result = dns_dbiterator_first(updbit); + if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, "rpz: %s: failed to get db iterator - %s", domain, @@ -1729,220 +1760,30 @@ setup_update(dns_rpz_zone_t *rpz) { goto cleanup; } - result = dns_dbiterator_pause(rpz->updbit); - if (result != ISC_R_SUCCESS) { - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, - "rpz: %s: failed to pause db iterator - %s", - domain, isc_result_totext(result)); - goto cleanup; - } - -cleanup: - if (result != ISC_R_SUCCESS) { - if (rpz->updbit != NULL) { - dns_dbiterator_destroy(&rpz->updbit); - } - if (rpz->newnodes != NULL) { - isc_ht_destroy(&rpz->newnodes); - } - dns_db_closeversion(rpz->updb, &rpz->updbversion, false); - } - - return (result); -} - -static void -finish_update(dns_rpz_zone_t *rpz) { - LOCK(&rpz->rpzs->maint_lock); - rpz->updaterunning = false; - - /* - * If there's an update pending, schedule it. - */ - if (rpz->updatepending) { - if (rpz->min_update_interval > 0) { - uint64_t defer = rpz->min_update_interval; - char dname[DNS_NAME_FORMATSIZE]; - isc_interval_t interval; - - dns_name_format(&rpz->origin, dname, - DNS_NAME_FORMATSIZE); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_MASTER, ISC_LOG_INFO, - "rpz: %s: new zone version came " - "too soon, deferring update for " - "%" PRIu64 " seconds", - dname, defer); - isc_interval_set(&interval, (unsigned int)defer, 0); - isc_timer_reset(rpz->updatetimer, isc_timertype_once, - NULL, &interval, true); - } else { - isc_event_t *event = NULL; - INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link)); - ISC_EVENT_INIT(&rpz->updateevent, - sizeof(rpz->updateevent), 0, NULL, - DNS_EVENT_RPZUPDATED, - dns_rpz_update_taskaction, rpz, rpz, - NULL, NULL); - event = &rpz->updateevent; - isc_task_send(rpz->rpzs->updater, &event); - } - } - UNLOCK(&rpz->rpzs->maint_lock); -} - -static void -cleanup_quantum(isc_task_t *task, isc_event_t *event) { - isc_result_t result = ISC_R_SUCCESS; - char domain[DNS_NAME_FORMATSIZE]; - dns_rpz_zone_t *rpz = NULL; - isc_ht_iter_t *iter = NULL; - dns_fixedname_t fname; - dns_name_t *name = NULL; - int count = 0; - - UNUSED(task); - - REQUIRE(event != NULL); - REQUIRE(event->ev_sender != NULL); - - rpz = (dns_rpz_zone_t *)event->ev_sender; - iter = (isc_ht_iter_t *)event->ev_arg; - isc_event_free(&event); - - if (iter == NULL) { - /* - * Iterate over old ht with existing nodes deleted to - * delete deleted nodes from RPZ - */ - isc_ht_iter_create(rpz->nodes, &iter); - } - - name = dns_fixedname_initname(&fname); - - LOCK(&rpz->rpzs->maint_lock); - - /* Check that we aren't shutting down. */ - if (rpz->rpzs->zones[rpz->num] == NULL) { - UNLOCK(&rpz->rpzs->maint_lock); - goto cleanup; - } - - for (result = isc_ht_iter_first(iter); - result == ISC_R_SUCCESS && count++ < DNS_RPZ_QUANTUM; - result = isc_ht_iter_delcurrent_next(iter)) - { - isc_region_t region; - unsigned char *key = NULL; - size_t keysize; - - isc_ht_iter_currentkey(iter, &key, &keysize); - region.base = key; - region.length = (unsigned int)keysize; - dns_name_fromregion(name, ®ion); - rpz_del(rpz, name); - } - - if (result == ISC_R_SUCCESS) { - isc_event_t *nevent = NULL; - - /* - * We finished a quantum; trigger the next one and return. - */ - - INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link)); - ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0, - NULL, DNS_EVENT_RPZUPDATED, cleanup_quantum, - iter, rpz, NULL, NULL); - nevent = &rpz->updateevent; - isc_task_send(rpz->rpzs->updater, &nevent); - UNLOCK(&rpz->rpzs->maint_lock); - return; - } else if (result == ISC_R_NOMORE) { - isc_ht_t *tmpht = NULL; - - /* - * Done with cleanup of deleted nodes; finalize - * the update. - */ - tmpht = rpz->nodes; - rpz->nodes = rpz->newnodes; - rpz->newnodes = tmpht; - - UNLOCK(&rpz->rpzs->maint_lock); - finish_update(rpz); - dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_MASTER, ISC_LOG_INFO, - "rpz: %s: reload done", domain); - } else { - UNLOCK(&rpz->rpzs->maint_lock); - } - - /* - * If we're here, we're finished or something went wrong. - */ -cleanup: - if (iter != NULL) { - isc_ht_iter_destroy(&iter); - } - if (rpz->newnodes != NULL) { - isc_ht_destroy(&rpz->newnodes); - } - dns_db_closeversion(rpz->updb, &rpz->updbversion, false); - dns_db_detach(&rpz->updb); - rpz_detach(&rpz); -} - -static void -update_quantum(isc_task_t *task, isc_event_t *event) { - isc_result_t result = ISC_R_SUCCESS; - dns_dbnode_t *node = NULL; - dns_rpz_zone_t *rpz = NULL; - char domain[DNS_NAME_FORMATSIZE]; - dns_fixedname_t fixname; - dns_name_t *name = NULL; - isc_event_t *nevent = NULL; - int count = 0; - - UNUSED(task); - - REQUIRE(event != NULL); - REQUIRE(event->ev_arg != NULL); - - rpz = (dns_rpz_zone_t *)event->ev_arg; - isc_event_free(&event); - - REQUIRE(rpz->updbit != NULL); - REQUIRE(rpz->newnodes != NULL); - - name = dns_fixedname_initname(&fixname); - - dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE); - - LOCK(&rpz->rpzs->maint_lock); - - /* Check that we aren't shutting down. */ - if (rpz->rpzs->zones[rpz->num] == NULL) { - UNLOCK(&rpz->rpzs->maint_lock); - goto cleanup; - } - - while (result == ISC_R_SUCCESS && count++ < DNS_RPZ_QUANTUM) { + while (result == ISC_R_SUCCESS) { char namebuf[DNS_NAME_FORMATSIZE]; dns_rdatasetiter_t *rdsiter = NULL; + dns_dbnode_t *node = NULL; - result = dns_dbiterator_current(rpz->updbit, &node, name); + result = rpz_shuttingdown(rpz); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(rpz->updb, &node); + goto cleanup; + } + + result = dns_dbiterator_current(updbit, &node, name); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, "rpz: %s: failed to get dbiterator - %s", domain, isc_result_totext(result)); dns_db_detachnode(rpz->updb, &node); - break; + goto cleanup; } + result = dns_dbiterator_pause(updbit); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + result = dns_db_allrdatasets(rpz->updb, node, rpz->updbversion, 0, 0, &rdsiter); if (result != ISC_R_SUCCESS) { @@ -1952,12 +1793,15 @@ update_quantum(isc_task_t *task, isc_event_t *event) { "rrdatasets - %s", domain, isc_result_totext(result)); dns_db_detachnode(rpz->updb, &node); - break; + goto cleanup; } result = dns_rdatasetiter_first(rdsiter); + dns_rdatasetiter_destroy(&rdsiter); - if (result != ISC_R_SUCCESS) { /* empty non-terminal */ + dns_db_detachnode(rpz->updb, &node); + + if (result != ISC_R_SUCCESS) { /* skip empty non-terminal */ if (result != ISC_R_NOMORE) { isc_log_write( dns_lctx, DNS_LOGCATEGORY_GENERAL, @@ -1966,14 +1810,13 @@ update_quantum(isc_task_t *task, isc_event_t *event) { "rdatasetiter", domain, isc_result_totext(result)); } - dns_db_detachnode(rpz->updb, &node); - result = dns_dbiterator_next(rpz->updbit); - continue; + goto next; } dns_name_downcase(name, name, NULL); - result = isc_ht_add(rpz->newnodes, name->ndata, name->length, - rpz); + + /* Add entry to the new nodes table */ + result = isc_ht_add(newnodes, name->ndata, name->length, rpz); if (result != ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, @@ -1981,131 +1824,170 @@ update_quantum(isc_task_t *task, isc_event_t *event) { "rpz: %s, adding node %s to HT error %s", domain, namebuf, isc_result_totext(result)); - dns_db_detachnode(rpz->updb, &node); - result = dns_dbiterator_next(rpz->updbit); - continue; + goto next; } + /* Does the entry exist in the old nodes table? */ result = isc_ht_find(rpz->nodes, name->ndata, name->length, NULL); - if (result == ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { /* found */ isc_ht_delete(rpz->nodes, name->ndata, name->length); - } else { /* not found */ - result = rpz_add(rpz, name); - if (result != ISC_R_SUCCESS) { - dns_name_format(name, namebuf, sizeof(namebuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_MASTER, - ISC_LOG_ERROR, - "rpz: %s: adding node %s " - "to RPZ error %s", - domain, namebuf, - isc_result_totext(result)); - } else { - dns_name_format(name, namebuf, sizeof(namebuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_MASTER, - ISC_LOG_DEBUG(3), - "rpz: %s: adding node %s", domain, - namebuf); - } + goto next; } - dns_db_detachnode(rpz->updb, &node); - result = dns_dbiterator_next(rpz->updbit); - } - - if (result == ISC_R_SUCCESS) { /* - * Pause the iterator so that the DB is not locked. + * Only the single rpz updates are serialized, so we need to + * lock here because we can be processing more updates to + * different rpz zones at the same time */ - dns_dbiterator_pause(rpz->updbit); - - /* - * We finished a quantum; trigger the next one and return. - */ - INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link)); - ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0, - NULL, DNS_EVENT_RPZUPDATED, update_quantum, rpz, - rpz, NULL, NULL); - nevent = &rpz->updateevent; - isc_task_send(rpz->rpzs->updater, &nevent); + LOCK(&rpz->rpzs->maint_lock); + result = rpz_add(rpz, name); UNLOCK(&rpz->rpzs->maint_lock); - return; - } else if (result == ISC_R_NOMORE) { - /* - * Done with the new database; now we just need to - * clean up the old. - */ - dns_dbiterator_destroy(&rpz->updbit); - INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link)); - ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0, - NULL, DNS_EVENT_RPZUPDATED, cleanup_quantum, - NULL, rpz, NULL, NULL); - nevent = &rpz->updateevent; - isc_task_send(rpz->rpzs->updater, &nevent); - UNLOCK(&rpz->rpzs->maint_lock); - return; + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, + "rpz: %s: adding node %s " + "to RPZ error %s", + domain, namebuf, + isc_result_totext(result)); + } else if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) { + dns_name_format(name, namebuf, sizeof(namebuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3), + "rpz: %s: adding node %s", domain, + namebuf); + } + + next: + result = dns_dbiterator_next(updbit); + } + INSIST(result != ISC_R_SUCCESS); + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; } - - /* - * If we're here, something went wrong, so clean up. - */ - UNLOCK(&rpz->rpzs->maint_lock); cleanup: - if (rpz->updbit != NULL) { - dns_dbiterator_destroy(&rpz->updbit); + dns_dbiterator_destroy(&updbit); + + return (result); +} + +static isc_result_t +cleanup_nodes(dns_rpz_zone_t *rpz) { + isc_result_t result; + isc_ht_iter_t *iter = NULL; + dns_name_t *name = NULL; + dns_fixedname_t fixname; + + name = dns_fixedname_initname(&fixname); + + isc_ht_iter_create(rpz->nodes, &iter); + + for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(iter)) + { + isc_region_t region; + unsigned char *key = NULL; + size_t keysize; + + result = rpz_shuttingdown(rpz); + if (result != ISC_R_SUCCESS) { + break; + } + + isc_ht_iter_currentkey(iter, &key, &keysize); + region.base = key; + region.length = (unsigned int)keysize; + dns_name_fromregion(name, ®ion); + + LOCK(&rpz->rpzs->maint_lock); + rpz_del(rpz, name); + UNLOCK(&rpz->rpzs->maint_lock); } - if (rpz->newnodes != NULL) { - isc_ht_destroy(&rpz->newnodes); + INSIST(result != ISC_R_SUCCESS); + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; } - dns_db_closeversion(rpz->updb, &rpz->updbversion, false); - dns_db_detach(&rpz->updb); - rpz_detach(&rpz); + + isc_ht_iter_destroy(&iter); + + return (result); +} + +static isc_result_t +rpz_shuttingdown(dns_rpz_zone_t *rpz) { + bool shuttingdown = false; + + LOCK(&rpz->rpzs->maint_lock); + /* Check that we aren't shutting down. */ + shuttingdown = (rpz->rpzs->zones[rpz->num] == NULL); + UNLOCK(&rpz->rpzs->maint_lock); + + if (shuttingdown) { + return (ISC_R_SHUTTINGDOWN); + } + + return (ISC_R_SUCCESS); } static void -update_from_db(dns_rpz_zone_t *rpz) { - dns_rpz_zone_t *rpz_zone = NULL; +update_rpz_cb(void *data) { + dns_rpz_zone_t *rpz = (dns_rpz_zone_t *)data; + isc_result_t result = ISC_R_SUCCESS; + isc_ht_t *newnodes = NULL; - REQUIRE(rpz != NULL); - REQUIRE(DNS_DB_VALID(rpz->db)); - REQUIRE(rpz->updb == NULL); - REQUIRE(rpz->updbversion == NULL); - REQUIRE(rpz->updbit == NULL); - REQUIRE(rpz->newnodes == NULL); + REQUIRE(rpz->nodes != NULL); - rpz_attach(rpz, &(dns_rpz_zone_t *){ NULL }); - - dns_db_attach(rpz->db, &rpz->updb); - rpz->updbversion = rpz->dbversion; - rpz->dbversion = NULL; - - result = setup_update(rpz); + result = rpz_shuttingdown(rpz); if (result != ISC_R_SUCCESS) { goto cleanup; } - event = &rpz->updateevent; - INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link)); - ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0, NULL, - DNS_EVENT_RPZUPDATED, update_quantum, rpz, rpz, NULL, - NULL); - isc_task_send(rpz->rpzs->updater, &event); - return; + isc_ht_init(&newnodes, rpz->rpzs->mctx, 1, ISC_HT_CASE_SENSITIVE); + + result = update_nodes(rpz, newnodes); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cleanup_nodes(rpz); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* Finalize the update */ + ISC_SWAP(rpz->nodes, newnodes); cleanup: - if (rpz->updbit != NULL) { - dns_dbiterator_destroy(&rpz->updbit); - } - if (rpz->newnodes != NULL) { - isc_ht_destroy(&rpz->newnodes); - } - dns_db_closeversion(rpz->updb, &rpz->updbversion, false); - dns_db_detach(&rpz->updb); - rpz_detach(&rpz); + isc_ht_destroy(&newnodes); + + rpz->updateresult = result; +} + +static void +update_from_db(dns_rpz_zone_t *rpz) { + char domain[DNS_NAME_FORMATSIZE]; + dns_rpz_zone_t *rpz_zone = NULL; + + REQUIRE(isc_nm_tid() >= 0); + REQUIRE(rpz != NULL); + REQUIRE(DNS_DB_VALID(rpz->db)); + REQUIRE(rpz->updb == NULL); + REQUIRE(rpz->updbversion == NULL); + + rpz_attach(rpz, &rpz_zone); + dns_db_attach(rpz->db, &rpz->updb); + rpz->updbversion = rpz->dbversion; + rpz->dbversion = NULL; + + dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, + ISC_LOG_INFO, "rpz: %s: reload start", domain); + + isc_nm_work_offload(isc_task_getnetmgr(rpz->rpzs->updater), + update_rpz_cb, update_rpz_done_cb, rpz_zone); } /* @@ -2192,22 +2074,8 @@ rpz_destroy(dns_rpz_zone_t *rpz) { dns_rpz_dbupdate_callback, rpz); dns_db_detach(&rpz->db); } - if (rpz->updaterunning) { - isc_task_purgeevent(rpzs->updater, &rpz->updateevent); - if (rpz->updbit != NULL) { - dns_dbiterator_destroy(&rpz->updbit); - } - if (rpz->newnodes != NULL) { - isc_ht_destroy(&rpz->newnodes); - } - if (rpz->updb != NULL) { - if (rpz->updbversion != NULL) { - dns_db_closeversion(rpz->updb, - &rpz->updbversion, false); - } - dns_db_detach(&rpz->updb); - } - } + + INSIST(!rpz->updaterunning); isc_timer_reset(rpz->updatetimer, isc_timertype_inactive, NULL, NULL, true); @@ -2254,21 +2122,20 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) { *rpzsp = NULL; if (isc_refcount_decrement(&rpzs->refs) == 1) { - LOCK(&rpzs->maint_lock); /* - * Forget the last of view's rpz machinery after + * Forget the last of the view's rpz machinery after * the last reference. */ + LOCK(&rpzs->maint_lock); for (dns_rpz_num_t rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) { - dns_rpz_zone_t *rpz = rpzs->zones[rpz_num]; - rpzs->zones[rpz_num] = NULL; - if (rpz != NULL) { - rpz_detach(&rpz); + if (rpzs->zones[rpz_num] != NULL) { + rpz_detach(&rpzs->zones[rpz_num]); } } UNLOCK(&rpzs->maint_lock); + rpz_detach_rpzs(&rpzs); } } diff --git a/lib/isc/include/isc/util.h b/lib/isc/include/isc/util.h index 6b8e581450..922661beaa 100644 --- a/lib/isc/include/isc/util.h +++ b/lib/isc/include/isc/util.h @@ -365,3 +365,13 @@ mock_assert(const int result, const char *const expression, * Misc */ #include + +/*% + * Swap + */ +#define ISC_SWAP(a, b) \ + { \ + typeof(a) __tmp_swap = a; \ + a = b; \ + b = __tmp_swap; \ + }