diff options
Diffstat (limited to 'contrib/bind9/lib/dns/zone.c')
-rw-r--r-- | contrib/bind9/lib/dns/zone.c | 2582 |
1 files changed, 2262 insertions, 320 deletions
diff --git a/contrib/bind9/lib/dns/zone.c b/contrib/bind9/lib/dns/zone.c index c212bf6..10ba807 100644 --- a/contrib/bind9/lib/dns/zone.c +++ b/contrib/bind9/lib/dns/zone.c @@ -23,16 +23,18 @@ #include <errno.h> #include <isc/file.h> +#include <isc/hex.h> #include <isc/mutex.h> +#include <isc/pool.h> #include <isc/print.h> #include <isc/random.h> #include <isc/ratelimiter.h> #include <isc/refcount.h> #include <isc/rwlock.h> #include <isc/serial.h> -#include <isc/strerror.h> #include <isc/stats.h> #include <isc/stdtime.h> +#include <isc/strerror.h> #include <isc/string.h> #include <isc/taskpool.h> #include <isc/timer.h> @@ -61,6 +63,7 @@ #include <dns/private.h> #include <dns/rbt.h> #include <dns/rcode.h> +#include <dns/rdata.h> #include <dns/rdataclass.h> #include <dns/rdatalist.h> #include <dns/rdataset.h> @@ -76,8 +79,10 @@ #include <dns/stats.h> #include <dns/time.h> #include <dns/tsig.h> +#include <dns/update.h> #include <dns/xfrin.h> #include <dns/zone.h> +#include <dns/zt.h> #include <dst/dst.h> @@ -145,6 +150,7 @@ typedef ISC_LIST(dns_signing_t) dns_signinglist_t; typedef struct dns_nsec3chain dns_nsec3chain_t; typedef ISC_LIST(dns_nsec3chain_t) dns_nsec3chainlist_t; typedef struct dns_keyfetch dns_keyfetch_t; +typedef struct dns_asyncload dns_asyncload_t; #define DNS_ZONE_CHECKLOCK #ifdef DNS_ZONE_CHECKLOCK @@ -217,6 +223,7 @@ struct dns_zone { isc_time_t signingtime; isc_time_t nsec3chaintime; isc_time_t refreshkeytime; + isc_uint32_t refreshkeyinterval; isc_uint32_t refreshkeycount; isc_uint32_t refresh; isc_uint32_t retry; @@ -239,9 +246,11 @@ struct dns_zone { isc_sockaddr_t masteraddr; dns_notifytype_t notifytype; isc_sockaddr_t *notify; + dns_name_t **notifykeynames; unsigned int notifycnt; isc_sockaddr_t notifyfrom; isc_task_t *task; + isc_task_t *loadtask; isc_sockaddr_t notifysrc4; isc_sockaddr_t notifysrc6; isc_sockaddr_t xfrsource4; @@ -297,8 +306,10 @@ struct dns_zone { * Optional per-zone statistics counters. Counted outside of this * module. */ + dns_zonestat_level_t statlevel; isc_boolean_t requeststats_on; isc_stats_t *requeststats; + dns_stats_t *rcvquerystats; isc_uint32_t notifydelay; dns_isselffunc_t isself; void *isselfarg; @@ -340,9 +351,25 @@ struct dns_zone { isc_boolean_t is_rpz; /*% + * Serial number update method. + */ + dns_updatemethod_t updatemethod; + + /*% + * whether ixfr is requested + */ + isc_boolean_t requestixfr; + + /*% * Outstanding forwarded UPDATE requests. */ dns_forwardlist_t forwards; + + dns_zone_t *raw; + dns_zone_t *secure; + + isc_boolean_t sourceserialset; + isc_uint32_t sourceserial; }; typedef struct { @@ -403,8 +430,9 @@ typedef struct { #define DNS_ZONEFLG_NEEDCOMPACT 0x02000000U #define DNS_ZONEFLG_REFRESHING 0x04000000U /*%< Refreshing keydata */ #define DNS_ZONEFLG_THAW 0x08000000U -/* #define DNS_ZONEFLG_XXXXX 0x10000000U XXXMPA unused. */ +#define DNS_ZONEFLG_LOADPENDING 0x10000000U /*%< Loading scheduled */ #define DNS_ZONEFLG_NODELAY 0x20000000U +#define DNS_ZONEFLG_SENDSECURE 0x40000000U #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0) #define DNS_ZONEKEY_OPTION(z,o) (((z)->keyopts & (o)) != 0) @@ -437,7 +465,9 @@ struct dns_zonemgr { isc_timermgr_t * timermgr; isc_socketmgr_t * socketmgr; isc_taskpool_t * zonetasks; + isc_taskpool_t * loadtasks; isc_task_t * task; + isc_pool_t * mctxpool; isc_ratelimiter_t * rl; isc_rwlock_t rwlock; isc_mutex_t iolock; @@ -476,6 +506,7 @@ struct dns_notify { dns_request_t *request; dns_name_t ns; isc_sockaddr_t dst; + dns_tsigkey_t *key; ISC_LINK(dns_notify_t) link; }; @@ -594,6 +625,15 @@ struct dns_keyfetch { dns_fetch_t *fetch; }; +/*% + * Hold state for an asynchronous load + */ +struct dns_asyncload { + dns_zone_t *zone; + dns_zt_zoneloaded_t loaded; + void *loaded_arg; +}; + #define HOUR 3600 #define DAY (24*HOUR) #define MONTH (30*DAY) @@ -632,6 +672,9 @@ static void zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length); static void zone_name_tostr(dns_zone_t *zone, char *buf, size_t length); static void zone_rdclass_tostr(dns_zone_t *zone, char *buf, size_t length); static void zone_viewname_tostr(dns_zone_t *zone, char *buf, size_t length); +static isc_result_t zone_send_secureserial(dns_zone_t *zone, + isc_boolean_t secure_locked, + isc_uint32_t serial); #if 0 /* ondestroy example */ @@ -688,6 +731,8 @@ static isc_result_t delete_nsec(dns_db_t *db, dns_dbversion_t *ver, static void zone_rekey(dns_zone_t *zone); static isc_boolean_t delsig_ok(dns_rdata_rrsig_t *rrsig_ptr, dst_key_t **keys, unsigned int nkeys); +static isc_result_t zone_send_securedb(dns_zone_t *zone, isc_boolean_t locked, + dns_db_t *db); #define ENTER zone_debuglog(zone, me, 1, "enter") @@ -798,6 +843,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { isc_time_settoepoch(&zone->signingtime); isc_time_settoepoch(&zone->nsec3chaintime); isc_time_settoepoch(&zone->refreshkeytime); + zone->refreshkeyinterval = 0; zone->refreshkeycount = 0; zone->refresh = DNS_ZONE_DEFAULTREFRESH; zone->retry = DNS_ZONE_DEFAULTRETRY; @@ -813,9 +859,11 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->masterscnt = 0; zone->curmaster = 0; zone->notify = NULL; + zone->notifykeynames = NULL; zone->notifytype = dns_notifytype_yes; zone->notifycnt = 0; zone->task = NULL; + zone->loadtask = NULL; zone->update_acl = NULL; zone->forward_acl = NULL; zone->notify_acl = NULL; @@ -857,7 +905,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->statelist = NULL; zone->stats = NULL; zone->requeststats_on = ISC_FALSE; + zone->statlevel = dns_zonestat_none; zone->requeststats = NULL; + zone->rcvquerystats = NULL; zone->notifydelay = 5; zone->isself = NULL; zone->isselfarg = NULL; @@ -869,6 +919,10 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->added = ISC_FALSE; zone->is_rpz = ISC_FALSE; ISC_LIST_INIT(zone->forwards); + zone->raw = NULL; + zone->secure = NULL; + zone->sourceserial = 0; + zone->sourceserialset = ISC_FALSE; zone->magic = ZONE_MAGIC; @@ -925,6 +979,8 @@ zone_free(dns_zone_t *zone) { if (zone->task != NULL) isc_task_detach(&zone->task); + if (zone->loadtask != NULL) + isc_task_detach(&zone->loadtask); if (zone->zmgr != NULL) dns_zonemgr_releasezone(zone->zmgr, zone); @@ -959,6 +1015,8 @@ zone_free(dns_zone_t *zone) { isc_stats_detach(&zone->stats); if (zone->requeststats != NULL) isc_stats_detach(&zone->requeststats); + if(zone->rcvquerystats != NULL ) + dns_stats_detach(&zone->rcvquerystats); if (zone->db != NULL) zone_detachdb(zone); if (zone->acache != NULL) @@ -1005,6 +1063,30 @@ zone_free(dns_zone_t *zone) { } /* + * Returns ISC_TRUE iff this the signed side of an inline-signing zone. + * Caller should hold zone lock. + */ +static inline isc_boolean_t +inline_secure(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + if (zone->raw != NULL) + return (ISC_TRUE); + return (ISC_FALSE); +} + +/* + * Returns ISC_TRUE iff this the unsigned side of an inline-signing zone + * Caller should hold zone lock. + */ +static inline isc_boolean_t +inline_raw(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + if (zone->secure != NULL) + return (ISC_TRUE); + return (ISC_FALSE); +} + +/* * Single shot. */ void @@ -1032,6 +1114,8 @@ dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass) { zone_rdclass_tostr(zone, namebuf, sizeof namebuf); zone->strrdclass = isc_mem_strdup(zone->mctx, namebuf); + if (inline_secure(zone)) + dns_zone_setclass(zone->raw, rdclass); UNLOCK_ZONE(zone); } @@ -1092,6 +1176,7 @@ dns_zone_getserial(dns_zone_t *zone) { */ void dns_zone_settype(dns_zone_t *zone, dns_zonetype_t type) { + char namebuf[1024]; REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(type != dns_zone_none); @@ -1102,6 +1187,12 @@ dns_zone_settype(dns_zone_t *zone, dns_zonetype_t type) { LOCK_ZONE(zone); REQUIRE(zone->type == dns_zone_none || zone->type == type); zone->type = type; + + if (zone->strnamerd != NULL) + isc_mem_free(zone->mctx, zone->strnamerd); + + zone_namerd_tostr(zone, namebuf, sizeof namebuf); + zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf); UNLOCK_ZONE(zone); } @@ -1220,10 +1311,12 @@ dns_zone_setview(dns_zone_t *zone, dns_view_t *view) { zone_viewname_tostr(zone, namebuf, sizeof namebuf); zone->strviewname = isc_mem_strdup(zone->mctx, namebuf); + if (inline_secure(zone)) + dns_zone_setview(zone->raw, view); + UNLOCK_ZONE(zone); } - dns_view_t * dns_zone_getview(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); @@ -1257,6 +1350,8 @@ dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) { zone_name_tostr(zone, namebuf, sizeof namebuf); zone->strname = isc_mem_strdup(zone->mctx, namebuf); + if (result == ISC_R_SUCCESS && inline_secure(zone)) + result = dns_zone_setorigin(zone->raw, origin); UNLOCK_ZONE(zone); return (result); } @@ -1394,16 +1489,24 @@ dns_zone_getjournal(dns_zone_t *zone) { * allow dynamic updates either by having an update policy ("ssutable") * or an "allow-update" ACL with a value other than exactly "{ none; }". */ -static isc_boolean_t -zone_isdynamic(dns_zone_t *zone) { +isc_boolean_t +dns_zone_isdynamic(dns_zone_t *zone, isc_boolean_t ignore_freeze) { REQUIRE(DNS_ZONE_VALID(zone)); - return (ISC_TF(zone->type == dns_zone_slave || - zone->type == dns_zone_stub || - zone->type == dns_zone_key || - (!zone->update_disabled && zone->ssutable != NULL) || - (!zone->update_disabled && zone->update_acl != NULL && - !dns_acl_isnone(zone->update_acl)))); + if (zone->type == dns_zone_slave || zone->type == dns_zone_stub || + zone->type == dns_zone_key || + (zone->type == dns_zone_redirect && zone->masters != NULL)) + return (ISC_TRUE); + + /* If !ignore_freeze, we need check whether updates are disabled. */ + if (zone->type == dns_zone_master && + (!zone->update_disabled || ignore_freeze) && + ((zone->ssutable != NULL) || + (zone->update_acl != NULL && !dns_acl_isnone(zone->update_acl)))) + return (ISC_TRUE); + + return (ISC_FALSE); + } /* @@ -1437,11 +1540,21 @@ zone_load(dns_zone_t *zone, unsigned int flags) { isc_time_t now; isc_time_t loadtime, filetime; dns_db_t *db = NULL; - isc_boolean_t rbt; + isc_boolean_t rbt, hasraw; REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); + hasraw = inline_secure(zone); + if (hasraw) { + result = zone_load(zone->raw, flags); + if (result != ISC_R_SUCCESS) { + UNLOCK_ZONE(zone); + return(result); + } + LOCK_ZONE(zone->raw); + } + TIME_NOW(&now); INSIST(zone->type != dns_zone_none); @@ -1453,7 +1566,6 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } - INSIST(zone->db_argc >= 1); rbt = strcmp(zone->db_argv[0], "rbt") == 0 || @@ -1467,7 +1579,7 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } - if (zone->db != NULL && zone_isdynamic(zone)) { + if (zone->db != NULL && dns_zone_isdynamic(zone, ISC_FALSE)) { /* * This is a slave, stub, or dynamically updated * zone being reloaded. Do nothing - the database @@ -1531,7 +1643,8 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } - if ((zone->type == dns_zone_slave || zone->type == dns_zone_stub) && + if ((zone->type == dns_zone_slave || zone->type == dns_zone_stub || + (zone->type == dns_zone_redirect && zone->masters != NULL)) && rbt) { if (zone->masterfile == NULL || !isc_file_exists(zone->masterfile)) { @@ -1569,7 +1682,9 @@ zone_load(dns_zone_t *zone, unsigned int flags) { result = zone_startload(db, zone, loadtime); } else { result = DNS_R_NOMASTERFILE; - if (zone->type == dns_zone_master) { + if (zone->type == dns_zone_master || + (zone->type == dns_zone_redirect && + zone->masters == NULL)) { dns_zone_log(zone, ISC_LOG_ERROR, "loading zone: " "no master file configured"); @@ -1590,6 +1705,8 @@ zone_load(dns_zone_t *zone, unsigned int flags) { result = zone_postload(zone, db, loadtime, result); cleanup: + if (hasraw) + UNLOCK_ZONE(zone->raw); UNLOCK_ZONE(zone); if (db != NULL) dns_db_detach(&db); @@ -1606,17 +1723,100 @@ dns_zone_loadnew(dns_zone_t *zone) { return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT)); } +static void +zone_asyncload(isc_task_t *task, isc_event_t *event) { + dns_asyncload_t *asl = event->ev_arg; + dns_zone_t *zone = asl->zone; + isc_result_t result = ISC_R_SUCCESS; + + UNUSED(task); + + REQUIRE(DNS_ZONE_VALID(zone)); + + if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) + result = ISC_R_CANCELED; + isc_event_free(&event); + if (result == ISC_R_CANCELED || + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) + goto cleanup; + + zone_load(zone, 0); + + LOCK_ZONE(zone); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING); + UNLOCK_ZONE(zone); + + /* Inform the zone table we've finished loading */ + if (asl->loaded != NULL) + (asl->loaded)(asl->loaded_arg, zone, task); + + cleanup: + isc_mem_put(zone->mctx, asl, sizeof (*asl)); + dns_zone_idetach(&zone); +} + +isc_result_t +dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg) { + isc_event_t *e; + dns_asyncload_t *asl = NULL; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(DNS_ZONE_VALID(zone)); + + if (zone->zmgr == NULL) + return (ISC_R_FAILURE); + + asl = isc_mem_get(zone->mctx, sizeof (*asl)); + if (asl == NULL) + CHECK(ISC_R_NOMEMORY); + + asl->zone = NULL; + asl->loaded = done; + asl->loaded_arg = arg; + + e = isc_event_allocate(zone->zmgr->mctx, zone->zmgr, + DNS_EVENT_ZONELOAD, + zone_asyncload, asl, + sizeof(isc_event_t)); + if (e == NULL) + CHECK(ISC_R_NOMEMORY); + + LOCK_ZONE(zone); + zone_iattach(zone, &asl->zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADPENDING); + isc_task_send(zone->loadtask, &e); + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); + + failure: + if (asl != NULL) + isc_mem_put(zone->mctx, asl, sizeof (*asl)); + return (result); +} + +isc_boolean_t +dns__zone_loadpending(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (ISC_TF(DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING))); +} + isc_result_t dns_zone_loadandthaw(dns_zone_t *zone) { isc_result_t result; - result = zone_load(zone, DNS_ZONELOADFLAG_THAW); + if (inline_raw(zone)) + result = zone_load(zone->secure, DNS_ZONELOADFLAG_THAW); + else + result = zone_load(zone, DNS_ZONELOADFLAG_THAW); + switch (result) { case DNS_R_CONTINUE: /* Deferred thaw. */ break; - case ISC_R_SUCCESS: case DNS_R_UPTODATE: + case ISC_R_SUCCESS: case DNS_R_SEENINCLUDE: zone->update_disabled = ISC_FALSE; break; @@ -1635,7 +1835,8 @@ get_master_options(dns_zone_t *zone) { unsigned int options; options = DNS_MASTER_ZONE; - if (zone->type == dns_zone_slave) + if (zone->type == dns_zone_slave || + (zone->type == dns_zone_redirect && zone->masters == NULL)) options |= DNS_MASTER_SLAVE; if (zone->type == dns_zone_key) options |= DNS_MASTER_KEY; @@ -1653,9 +1854,9 @@ get_master_options(dns_zone_t *zone) { options |= DNS_MASTER_CHECKMXFAIL; if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKWILDCARD)) options |= DNS_MASTER_CHECKWILDCARD; - if (zone->type == dns_zone_master && + if (inline_secure(zone) || (zone->type == dns_zone_master && ((zone->update_acl != NULL && !dns_acl_isnone(zone->update_acl)) || - zone->ssutable != NULL)) + zone->ssutable != NULL))) options |= DNS_MASTER_RESIGN; return (options); } @@ -1679,8 +1880,7 @@ zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { result = dns_master_loadfileinc3(load->zone->masterfile, dns_db_origin(load->db), dns_db_origin(load->db), - load->zone->rdclass, - options, + load->zone->rdclass, options, load->zone->sigresigninginterval, &load->callbacks, task, zone_loaddone, load, @@ -1696,11 +1896,29 @@ zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { } static void +get_raw_serial(dns_zone_t *raw, dns_masterrawheader_t *rawdata) { + isc_result_t result; + unsigned int soacount; + + LOCK(&raw->lock); + if (raw->db != NULL) { + result = zone_get_from_db(raw, raw->db, NULL, &soacount, + &rawdata->sourceserial, + NULL, NULL, NULL, NULL, + NULL); + if (result == ISC_R_SUCCESS && soacount > 0U) + rawdata->flags |= DNS_MASTERRAW_SOURCESERIALSET; + } + UNLOCK(&raw->lock); +} + +static void zone_gotwritehandle(isc_task_t *task, isc_event_t *event) { const char me[] = "zone_gotwritehandle"; dns_zone_t *zone = event->ev_arg; isc_result_t result = ISC_R_SUCCESS; dns_dbversion_t *version = NULL; + dns_masterrawheader_t rawdata; REQUIRE(DNS_ZONE_VALID(zone)); INSIST(task == zone->task); @@ -1716,11 +1934,14 @@ zone_gotwritehandle(isc_task_t *task, isc_event_t *event) { ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); if (zone->db != NULL) { dns_db_currentversion(zone->db, &version); - result = dns_master_dumpinc2(zone->mctx, zone->db, version, + dns_master_initrawheader(&rawdata); + if (inline_secure(zone)) + get_raw_serial(zone->raw, &rawdata); + result = dns_master_dumpinc3(zone->mctx, zone->db, version, &dns_master_style_default, zone->masterfile, zone->task, dump_done, zone, &zone->dctx, - zone->masterformat); + zone->masterformat, &rawdata); dns_db_closeversion(zone->db, &version, ISC_FALSE); } else result = ISC_R_CANCELED; @@ -1734,6 +1955,31 @@ zone_gotwritehandle(isc_task_t *task, isc_event_t *event) { dump_done(zone, result); } +/* + * Save the raw serial number for inline-signing zones. + * (XXX: Other information from the header will be used + * for other purposes in the future, but for now this is + * all we're interested in.) + */ +static void +zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header) { + if ((header->flags & DNS_MASTERRAW_SOURCESERIALSET) == 0) + return; + + zone->sourceserial = header->sourceserial; + zone->sourceserialset = ISC_TRUE; +} + +void +dns_zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header) { + if (zone == NULL) + return; + + LOCK_ZONE(zone); + zone_setrawdata(zone, header); + UNLOCK_ZONE(zone); +} + static isc_result_t zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { dns_load_t *load; @@ -1753,7 +1999,7 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS)) options |= DNS_MASTER_MANYERRORS; - if (zone->zmgr != NULL && zone->db != NULL && zone->task != NULL) { + if (zone->zmgr != NULL && zone->db != NULL && zone->loadtask != NULL) { load = isc_mem_get(zone->mctx, sizeof(*load)); if (load == NULL) return (ISC_R_NOMEMORY); @@ -1768,11 +2014,13 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { zone_iattach(zone, &load->zone); dns_db_attach(db, &load->db); dns_rdatacallbacks_init(&load->callbacks); + load->callbacks.rawdata = zone_setrawdata; + zone_iattach(zone, &load->callbacks.zone); result = dns_db_beginload(db, &load->callbacks.add, &load->callbacks.add_private); if (result != ISC_R_SUCCESS) goto cleanup; - result = zonemgr_getio(zone->zmgr, ISC_TRUE, zone->task, + result = zonemgr_getio(zone->zmgr, ISC_TRUE, zone->loadtask, zone_gotreadhandle, load, &zone->readio); if (result != ISC_R_SUCCESS) { @@ -1789,18 +2037,24 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { dns_rdatacallbacks_t callbacks; dns_rdatacallbacks_init(&callbacks); + callbacks.rawdata = zone_setrawdata; + zone_iattach(zone, &callbacks.zone); result = dns_db_beginload(db, &callbacks.add, &callbacks.add_private); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + zone_idetach(&callbacks.zone); return (result); - result = dns_master_loadfile3(zone->masterfile, &zone->origin, - &zone->origin, zone->rdclass, - options, zone->sigresigninginterval, + } + result = dns_master_loadfile3(zone->masterfile, + &zone->origin, &zone->origin, + zone->rdclass, options, + zone->sigresigninginterval, &callbacks, zone->mctx, zone->masterformat); tresult = dns_db_endload(db, &callbacks.add_private); if (result == ISC_R_SUCCESS) result = tresult; + zone_idetach(&callbacks.zone); } return (result); @@ -1809,6 +2063,7 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { load->magic = 0; dns_db_detach(&load->db); zone_idetach(&load->zone); + zone_idetach(&load->callbacks.zone); isc_mem_detach(&load->mctx); isc_mem_put(zone->mctx, load, sizeof(*load)); return (result); @@ -2531,13 +2786,22 @@ resume_signingwithkey(dns_zone_t *zone) { static isc_result_t zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { dns_nsec3chain_t *nsec3chain, *current; + dns_dbversion_t *version = NULL; + isc_boolean_t nseconly = ISC_FALSE, nsec3ok = ISC_FALSE; isc_result_t result; isc_time_t now; unsigned int options = 0; char saltbuf[255*2+1]; - char flags[sizeof("REMOVE|CREATE|NONSEC|OPTOUT")]; + char flags[sizeof("INITIAL|REMOVE|CREATE|NONSEC|OPTOUT")]; int i; + dns_db_currentversion(zone->db, &version); + result = dns_nsec_nseconly(zone->db, version, &nseconly); + nsec3ok = (result == ISC_R_SUCCESS && !nseconly); + dns_db_closeversion(zone->db, &version, ISC_FALSE); + if (!nsec3ok && (nsec3param->flags & DNS_NSEC3FLAG_REMOVE) == 0) + return (ISC_R_SUCCESS); + nsec3chain = isc_mem_get(zone->mctx, sizeof *nsec3chain); if (nsec3chain == NULL) return (ISC_R_NOMEMORY); @@ -2564,6 +2828,12 @@ zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { flags[0] = '\0'; if (nsec3param->flags & DNS_NSEC3FLAG_REMOVE) strlcat(flags, "REMOVE", sizeof(flags)); + if (nsec3param->flags & DNS_NSEC3FLAG_INITIAL) { + if (flags[0] == '\0') + strlcpy(flags, "INITIAL", sizeof(flags)); + else + strlcat(flags, "|INITIAL", sizeof(flags)); + } if (nsec3param->flags & DNS_NSEC3FLAG_CREATE) { if (flags[0] == '\0') strlcpy(flags, "CREATE", sizeof(flags)); @@ -2592,6 +2862,7 @@ zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { "zone_addnsec3chain(%u,%s,%u,%s)", nsec3param->hash, flags, nsec3param->iterations, saltbuf); + for (current = ISC_LIST_HEAD(zone->nsec3chain); current != NULL; current = ISC_LIST_NEXT(current, link)) { @@ -2644,6 +2915,7 @@ resume_addnsec3chain(dns_zone_t *zone) { dns_rdataset_t rdataset; isc_result_t result; dns_rdata_nsec3param_t nsec3param; + isc_boolean_t nseconly = ISC_FALSE, nsec3ok = ISC_FALSE; if (zone->privatetype == 0) return; @@ -2653,6 +2925,10 @@ resume_addnsec3chain(dns_zone_t *zone) { goto cleanup; dns_db_currentversion(zone->db, &version); + + result = dns_nsec_nseconly(zone->db, version, &nseconly); + nsec3ok = (result == ISC_R_SUCCESS && !nseconly); + dns_rdataset_init(&rdataset); result = dns_db_findrdataset(zone->db, node, version, zone->privatetype, dns_rdatatype_none, @@ -2676,8 +2952,9 @@ resume_addnsec3chain(dns_zone_t *zone) { continue; result = dns_rdata_tostruct(&rdata, &nsec3param, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 || - (nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) { + if (((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) || + ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 && nsec3ok)) + { result = zone_addnsec3chain(zone, &nsec3param); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -2727,7 +3004,7 @@ check_nsec3param(dns_zone_t *zone, dns_db_t *db) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t dynamic = (zone->type == dns_zone_master) ? - zone_isdynamic(zone) : ISC_FALSE; + dns_zone_isdynamic(zone, ISC_FALSE) : ISC_FALSE; dns_rdataset_init(&rdataset); result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node); @@ -3149,8 +3426,8 @@ update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, } static isc_result_t -increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver, - dns_diff_t *diff, isc_mem_t *mctx) { +update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, + isc_mem_t *mctx, dns_updatemethod_t method) { dns_difftuple_t *deltuple = NULL; dns_difftuple_t *addtuple = NULL; isc_uint32_t serial; @@ -3161,12 +3438,7 @@ increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver, addtuple->op = DNS_DIFFOP_ADD; serial = dns_soa_getserial(&addtuple->rdata); - - /* RFC1982 */ - serial = (serial + 1) & 0xFFFFFFFF; - if (serial == 0) - serial = 1; - + serial = dns_update_soaserial(serial, method); dns_soa_setserial(serial, &addtuple->rdata); CHECK(do_one_tuple(&deltuple, db, ver, diff)); CHECK(do_one_tuple(&addtuple, db, ver, diff)); @@ -3184,17 +3456,20 @@ increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver, * Write all transactions in 'diff' to the zone journal file. */ static isc_result_t -zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { +zone_journal(dns_zone_t *zone, dns_diff_t *diff, isc_uint32_t *sourceserial, + const char *caller) +{ const char me[] = "zone_journal"; const char *journalfile; isc_result_t result = ISC_R_SUCCESS; dns_journal_t *journal = NULL; + unsigned int mode = DNS_JOURNAL_CREATE|DNS_JOURNAL_WRITE; ENTER; journalfile = dns_zone_getjournal(zone); if (journalfile != NULL) { - result = dns_journal_open(zone->mctx, journalfile, - ISC_TRUE, &journal); + result = dns_journal_open(zone->mctx, journalfile, mode, + &journal); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "%s:dns_journal_open -> %s", @@ -3202,15 +3477,18 @@ zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { return (result); } + if (sourceserial != NULL) + dns_journal_set_sourceserial(journal, *sourceserial); + result = dns_journal_write_transaction(journal, diff); - dns_journal_destroy(&journal); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "%s:dns_journal_write_transaction -> %s", caller, dns_result_totext(result)); - return (result); } + dns_journal_destroy(&journal); } + return (result); } @@ -3391,8 +3669,9 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { if (changed) { /* Write changes to journal file. */ - CHECK(increment_soa_serial(db, ver, &diff, zone->mctx)); - CHECK(zone_journal(zone, &diff, "sync_keyzone")); + CHECK(update_soa_serial(db, ver, &diff, zone->mctx, + zone->updatemethod)); + CHECK(zone_journal(zone, &diff, NULL, "sync_keyzone")); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -3418,6 +3697,73 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { return (result); } +isc_result_t +dns_zone_synckeyzone(dns_zone_t *zone) { + isc_result_t result; + dns_db_t *db = NULL; + + if (zone->type != dns_zone_key) + return (DNS_R_BADZONE); + + CHECK(dns_zone_getdb(zone, &db)); + + LOCK_ZONE(zone); + result = sync_keyzone(zone, db); + UNLOCK_ZONE(zone); + + failure: + if (db != NULL) + dns_db_detach(&db); + return (result); +} + +static void +maybe_send_secure(dns_zone_t *zone) { + isc_result_t result; + + /* + * We've finished loading, or else failed to load, an inline-signing + * 'secure' zone. We now need information about the status of the + * 'raw' zone. If we failed to load, then we need it to send a + * copy of its database; if we succeeded, we need it to send its + * serial number so that we can sync with it. If it has not yet + * loaded, we set a flag so that it will send the necessary + * information when it has finished loading. + */ + if (zone->raw->db != NULL) { + if (zone->db != NULL) { + isc_uint32_t serial; + unsigned int soacount; + + result = zone_get_from_db(zone->raw, zone->raw->db, + NULL, &soacount, &serial, NULL, + NULL, NULL, NULL, NULL); + if (result == ISC_R_SUCCESS && soacount > 0U) + zone_send_secureserial(zone->raw, ISC_TRUE, serial); + } else + zone_send_securedb(zone->raw, ISC_TRUE, zone->raw->db); + + } else + DNS_ZONE_SETFLAG(zone->raw, DNS_ZONEFLG_SENDSECURE); +} + +static isc_boolean_t +zone_unchanged(dns_db_t *db1, dns_db_t *db2, isc_mem_t *mctx) { + isc_result_t result; + isc_boolean_t answer = ISC_FALSE; + dns_diff_t diff; + + dns_diff_init(mctx, &diff); + result = dns_db_diffx(&diff, db1, NULL, db2, NULL, NULL); + if (result == ISC_R_SUCCESS && ISC_LIST_EMPTY(diff.tuples)) + answer = ISC_TRUE; + dns_diff_clear(&diff); + return (answer); +} + +/* + * The zone is presumed to be locked. + */ static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, isc_result_t result) @@ -3441,7 +3787,9 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, */ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { if (zone->type == dns_zone_slave || - zone->type == dns_zone_stub) { + zone->type == dns_zone_stub || + (zone->type == dns_zone_redirect && + zone->masters == NULL)) { if (result == ISC_R_FILENOTFOUND) dns_zone_log(zone, ISC_LOG_DEBUG(1), "no master file"); @@ -3451,6 +3799,12 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "failed: %s", zone->masterfile, dns_result_totext(result)); + } else if (zone->type == dns_zone_master && + inline_secure(zone) && result == ISC_R_FILENOTFOUND) + { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "no master file, requesting db"); + maybe_send_secure(zone); } else { int level = ISC_LOG_ERROR; if (zone->type == dns_zone_key && @@ -3510,6 +3864,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "journal rollforward failed: %s", dns_result_totext(result)); goto cleanup; + + } if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -3525,7 +3881,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, needdump = ISC_TRUE; } - dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded; checking validity"); /* * Obtain ns, soa and cname counts for top of zone. */ @@ -3539,6 +3894,46 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } /* + * Check to make sure the journal is up to date, and remove the + * journal file if it isn't, as we wouldn't be able to apply + * updates otherwise. + */ + if (zone->journal != NULL && dns_zone_isdynamic(zone, ISC_TRUE) && + ! DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) { + isc_uint32_t jserial; + dns_journal_t *journal = NULL; + + result = dns_journal_open(zone->mctx, zone->journal, + DNS_JOURNAL_READ, &journal); + if (result == ISC_R_SUCCESS) { + jserial = dns_journal_last_serial(journal); + dns_journal_destroy(&journal); + } else { + jserial = serial; + result = ISC_R_SUCCESS; + } + + if (jserial != serial) { + dns_zone_log(zone, ISC_LOG_INFO, + "journal file is out of date: " + "removing journal file"); + if (remove(zone->journal) < 0 && errno != ENOENT) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_ZONE, + ISC_LOG_WARNING, + "unable to remove journal " + "'%s': '%s'", + zone->journal, strbuf); + } + } + } + + dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded; checking validity"); + + /* * Master / Slave / Stub zones require both NS and SOA records at * the top of the zone. */ @@ -3548,6 +3943,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, case dns_zone_master: case dns_zone_slave: case dns_zone_stub: + case dns_zone_redirect: if (soacount != 1) { dns_zone_log(zone, ISC_LOG_ERROR, "has %d SOA records", soacount); @@ -3564,7 +3960,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, result = DNS_R_BADZONE; goto cleanup; } - if (zone->type != dns_zone_stub) { + if (zone->type != dns_zone_stub && + zone->type != dns_zone_redirect) { result = check_nsec3param(zone, db); if (result != ISC_R_SUCCESS) goto cleanup; @@ -3575,7 +3972,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, result = DNS_R_BADZONE; goto cleanup; } - if (zone->type == dns_zone_master && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRR) && !zone_check_dup(zone, db)) { @@ -3602,6 +3998,14 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, INSIST(zone->type == dns_zone_master); + if (serial == oldserial && + zone_unchanged(zone->db, db, zone->mctx)) { + dns_zone_log(zone, ISC_LOG_INFO, + "ixfr-from-differences: " + "unchanged"); + return(ISC_R_SUCCESS); + } + serialmin = (oldserial + 1) & 0xffffffffU; serialmax = (oldserial + 0x7fffffffU) & 0xffffffffU; @@ -3644,7 +4048,9 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS); if (zone->type == dns_zone_slave || - zone->type == dns_zone_stub) { + zone->type == dns_zone_stub || + (zone->type == dns_zone_redirect && + zone->masters != NULL)) { isc_time_t t; isc_uint32_t delay; @@ -3666,6 +4072,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, &zone->expiretime) >= 0) zone->refreshtime = now; } + break; case dns_zone_key: @@ -3717,8 +4124,24 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY); + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SENDSECURE) && + inline_raw(zone)) + { + if (zone->secure->db == NULL) + zone_send_securedb(zone, ISC_FALSE, db); + else + zone_send_secureserial(zone, ISC_FALSE, serial); + } } + /* + * Finished loading inline-signing zone; need to get status + * from the raw side now. + */ + if (zone->type == dns_zone_master && inline_secure(zone)) + maybe_send_secure(zone); + + result = ISC_R_SUCCESS; if (needdump) { @@ -3736,7 +4159,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } if (zone->type == dns_zone_master && - zone_isdynamic(zone) && + !DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN) && + dns_zone_isdynamic(zone, ISC_FALSE) && dns_db_issecure(db)) { dns_name_t *name; dns_fixedname_t fixed; @@ -3775,12 +4199,14 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, dns_db_issecure(db) ? " (DNSSEC signed)" : ""); zone->loadtime = loadtime; + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING); return (result); cleanup: if (zone->type == dns_zone_slave || zone->type == dns_zone_stub || - zone->type == dns_zone_key) { + zone->type == dns_zone_key || + (zone->type == dns_zone_redirect && zone->masters != NULL)) { if (zone->journal != NULL) zone_saveunique(zone, zone->journal, "jn-XXXXXXXX"); if (zone->masterfile != NULL) @@ -3791,8 +4217,15 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, if (zone->task != NULL) zone_settimer(zone, &now); result = ISC_R_SUCCESS; - } else if (zone->type == dns_zone_master) - dns_zone_log(zone, ISC_LOG_ERROR, "not loaded due to errors."); + } else if (zone->type == dns_zone_master || + zone->type == dns_zone_redirect) { + if (!(inline_secure(zone) && result == ISC_R_FILENOTFOUND)) + dns_zone_log(zone, ISC_LOG_ERROR, + "not loaded due to errors."); + else if (zone->type == dns_zone_master) + result = ISC_R_SUCCESS; + } + return (result); } @@ -4087,6 +4520,8 @@ dns_zone_attach(dns_zone_t *source, dns_zone_t **target) { void dns_zone_detach(dns_zone_t **zonep) { dns_zone_t *zone; + dns_zone_t *raw = NULL; + dns_zone_t *secure = NULL; unsigned int refs; isc_boolean_t free_now = ISC_FALSE; @@ -4124,12 +4559,21 @@ dns_zone_detach(dns_zone_t **zonep) { */ INSIST(zone->view == NULL); free_now = ISC_TRUE; + raw = zone->raw; + zone->raw = NULL; + secure = zone->secure; + zone->secure = NULL; } UNLOCK_ZONE(zone); } *zonep = NULL; - if (free_now) + if (free_now) { + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); zone_free(zone); + } } void @@ -4141,26 +4585,6 @@ dns_zone_iattach(dns_zone_t *source, dns_zone_t **target) { UNLOCK_ZONE(source); } -isc_result_t -dns_zone_synckeyzone(dns_zone_t *zone) { - isc_result_t result; - dns_db_t *db = NULL; - - if (zone->type != dns_zone_key) - return (DNS_R_BADZONE); - - CHECK(dns_zone_getdb(zone, &db)); - - LOCK_ZONE(zone); - result = sync_keyzone(zone, db); - UNLOCK_ZONE(zone); - - failure: - if (db != NULL) - dns_db_detach(&db); - return (result); -} - static void zone_iattach(dns_zone_t *source, dns_zone_t **target) { @@ -4385,48 +4809,8 @@ dns_zone_getnotifysrc6(dns_zone_t *zone) { return (&zone->notifysrc6); } -isc_result_t -dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, - isc_uint32_t count) -{ - isc_sockaddr_t *new; - - REQUIRE(DNS_ZONE_VALID(zone)); - REQUIRE(count == 0 || notify != NULL); - - LOCK_ZONE(zone); - if (zone->notify != NULL) { - isc_mem_put(zone->mctx, zone->notify, - zone->notifycnt * sizeof(*new)); - zone->notify = NULL; - zone->notifycnt = 0; - } - if (count != 0) { - new = isc_mem_get(zone->mctx, count * sizeof(*new)); - if (new == NULL) { - UNLOCK_ZONE(zone); - return (ISC_R_NOMEMORY); - } - memcpy(new, notify, count * sizeof(*new)); - zone->notify = new; - zone->notifycnt = count; - } - UNLOCK_ZONE(zone); - return (ISC_R_SUCCESS); -} - -isc_result_t -dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters, - isc_uint32_t count) -{ - isc_result_t result; - - result = dns_zone_setmasterswithkeys(zone, masters, NULL, count); - return (result); -} - static isc_boolean_t -same_masters(const isc_sockaddr_t *old, const isc_sockaddr_t *new, +same_addrs(const isc_sockaddr_t *old, const isc_sockaddr_t *new, isc_uint32_t count) { unsigned int i; @@ -4456,15 +4840,170 @@ same_keynames(dns_name_t **old, dns_name_t **new, isc_uint32_t count) { return (ISC_TRUE); } +static void +clear_addresskeylist(isc_sockaddr_t **addrsp, dns_name_t ***keynamesp, + unsigned int *countp, isc_mem_t *mctx) +{ + unsigned int count; + isc_sockaddr_t *addrs; + dns_name_t **keynames; + + REQUIRE(countp != NULL && addrsp != NULL && keynamesp != NULL); + + count = *countp; + *countp = 0; + addrs = *addrsp; + *addrsp = NULL; + keynames = *keynamesp; + *keynamesp = NULL; + + if (addrs != NULL) + isc_mem_put(mctx, addrs, count * sizeof(isc_sockaddr_t)); + + if (keynames != NULL) { + unsigned int i; + for (i = 0; i < count; i++) { + if (keynames[i] != NULL) { + dns_name_free(keynames[i], mctx); + isc_mem_put(mctx, keynames[i], + sizeof(dns_name_t)); + keynames[i] = NULL; + } + } + isc_mem_put(mctx, keynames, count * sizeof(dns_name_t *)); + } +} + +static isc_result_t +set_addrkeylist(unsigned int count, + const isc_sockaddr_t *addrs, isc_sockaddr_t **newaddrsp, + dns_name_t **names, dns_name_t ***newnamesp, + isc_mem_t *mctx) +{ + isc_result_t result; + isc_sockaddr_t *newaddrs = NULL; + dns_name_t **newnames = NULL; + unsigned int i; + + REQUIRE(newaddrsp != NULL && *newaddrsp == NULL); + REQUIRE(newnamesp != NULL && *newnamesp == NULL); + + newaddrs = isc_mem_get(mctx, count * sizeof(*newaddrs)); + if (newaddrs == NULL) + return (ISC_R_NOMEMORY); + memcpy(newaddrs, addrs, count * sizeof(*newaddrs)); + + newnames = NULL; + if (names != NULL) { + newnames = isc_mem_get(mctx, count * sizeof(*newnames)); + if (newnames == NULL) { + isc_mem_put(mctx, newaddrs, count * sizeof(*newaddrs)); + return (ISC_R_NOMEMORY); + } + for (i = 0; i < count; i++) + newnames[i] = NULL; + for (i = 0; i < count; i++) { + if (names[i] != NULL) { + newnames[i] = isc_mem_get(mctx, + sizeof(dns_name_t)); + if (newnames[i] == NULL) + goto allocfail; + dns_name_init(newnames[i], NULL); + result = dns_name_dup(names[i], mctx, + newnames[i]); + if (result != ISC_R_SUCCESS) { + allocfail: + for (i = 0; i < count; i++) + if (newnames[i] != NULL) + dns_name_free( + newnames[i], + mctx); + isc_mem_put(mctx, newaddrs, + count * sizeof(*newaddrs)); + isc_mem_put(mctx, newnames, + count * sizeof(*newnames)); + return (ISC_R_NOMEMORY); + } + } + } + } + + *newaddrsp = newaddrs; + *newnamesp = newnames; + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, + isc_uint32_t count) +{ + return (dns_zone_setalsonotifywithkeys(zone, notify, NULL, count)); +} + +isc_result_t +dns_zone_setalsonotifywithkeys(dns_zone_t *zone, const isc_sockaddr_t *notify, + dns_name_t **keynames, isc_uint32_t count) +{ + isc_result_t result; + isc_sockaddr_t *newaddrs = NULL; + dns_name_t **newnames = NULL; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(count == 0 || notify != NULL); + if (keynames != NULL) + REQUIRE(count != 0); + + LOCK_ZONE(zone); + + if (count == zone->notifycnt && + same_addrs(zone->notify, notify, count) && + same_keynames(zone->notifykeynames, keynames, count)) + goto unlock; + + clear_addresskeylist(&zone->notify, &zone->notifykeynames, + &zone->notifycnt, zone->mctx); + + if (count == 0) + goto unlock; + + /* + * Set up the notify and notifykey lists + */ + result = set_addrkeylist(count, notify, &newaddrs, + keynames, &newnames, zone->mctx); + if (result != ISC_R_SUCCESS) + goto unlock; + + /* + * Everything is ok so attach to the zone. + */ + zone->notify = newaddrs; + zone->notifykeynames = newnames; + zone->notifycnt = count; + unlock: + UNLOCK_ZONE(zone); + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters, + isc_uint32_t count) +{ + isc_result_t result; + + result = dns_zone_setmasterswithkeys(zone, masters, NULL, count); + return (result); +} + isc_result_t dns_zone_setmasterswithkeys(dns_zone_t *zone, const isc_sockaddr_t *masters, dns_name_t **keynames, isc_uint32_t count) { - isc_sockaddr_t *new; isc_result_t result = ISC_R_SUCCESS; - dns_name_t **newname; + isc_sockaddr_t *newaddrs = NULL; + dns_name_t **newnames = NULL; isc_boolean_t *newok; unsigned int i; @@ -4482,38 +5021,24 @@ dns_zone_setmasterswithkeys(dns_zone_t *zone, * unlock and exit. */ if (count != zone->masterscnt || - !same_masters(zone->masters, masters, count) || + !same_addrs(zone->masters, masters, count) || !same_keynames(zone->masterkeynames, keynames, count)) { if (zone->request != NULL) dns_request_cancel(zone->request); } else goto unlock; - if (zone->masters != NULL) { - isc_mem_put(zone->mctx, zone->masters, - zone->masterscnt * sizeof(*new)); - zone->masters = NULL; - } - if (zone->masterkeynames != NULL) { - for (i = 0; i < zone->masterscnt; i++) { - if (zone->masterkeynames[i] != NULL) { - dns_name_free(zone->masterkeynames[i], - zone->mctx); - isc_mem_put(zone->mctx, - zone->masterkeynames[i], - sizeof(dns_name_t)); - zone->masterkeynames[i] = NULL; - } - } - isc_mem_put(zone->mctx, zone->masterkeynames, - zone->masterscnt * sizeof(dns_name_t *)); - zone->masterkeynames = NULL; - } + + /* + * This needs to happen before clear_addresskeylist() sets + * zone->masterscnt to 0: + */ if (zone->mastersok != NULL) { isc_mem_put(zone->mctx, zone->mastersok, zone->masterscnt * sizeof(isc_boolean_t)); zone->mastersok = NULL; } - zone->masterscnt = 0; + clear_addresskeylist(&zone->masters, &zone->masterkeynames, + &zone->masterscnt, zone->mctx); /* * If count == 0, don't allocate any space for masters, mastersok or * keynames so internally, those pointers are NULL if count == 0 @@ -4522,76 +5047,34 @@ dns_zone_setmasterswithkeys(dns_zone_t *zone, goto unlock; /* - * masters must contain count elements! - */ - new = isc_mem_get(zone->mctx, count * sizeof(*new)); - if (new == NULL) { - result = ISC_R_NOMEMORY; - goto unlock; - } - memcpy(new, masters, count * sizeof(*new)); - - /* - * Similarly for mastersok. + * mastersok must contain count elements */ newok = isc_mem_get(zone->mctx, count * sizeof(*newok)); if (newok == NULL) { result = ISC_R_NOMEMORY; - isc_mem_put(zone->mctx, new, count * sizeof(*new)); + isc_mem_put(zone->mctx, newaddrs, count * sizeof(*newaddrs)); goto unlock; }; for (i = 0; i < count; i++) newok[i] = ISC_FALSE; /* - * if keynames is non-NULL, it must contain count elements! + * Now set up the masters and masterkey lists */ - newname = NULL; - if (keynames != NULL) { - newname = isc_mem_get(zone->mctx, count * sizeof(*newname)); - if (newname == NULL) { - result = ISC_R_NOMEMORY; - isc_mem_put(zone->mctx, new, count * sizeof(*new)); - isc_mem_put(zone->mctx, newok, count * sizeof(*newok)); - goto unlock; - } - for (i = 0; i < count; i++) - newname[i] = NULL; - for (i = 0; i < count; i++) { - if (keynames[i] != NULL) { - newname[i] = isc_mem_get(zone->mctx, - sizeof(dns_name_t)); - if (newname[i] == NULL) - goto allocfail; - dns_name_init(newname[i], NULL); - result = dns_name_dup(keynames[i], zone->mctx, - newname[i]); - if (result != ISC_R_SUCCESS) { - allocfail: - for (i = 0; i < count; i++) - if (newname[i] != NULL) - dns_name_free( - newname[i], - zone->mctx); - isc_mem_put(zone->mctx, new, - count * sizeof(*new)); - isc_mem_put(zone->mctx, newok, - count * sizeof(*newok)); - isc_mem_put(zone->mctx, newname, - count * sizeof(*newname)); - goto unlock; - } - } - } + result = set_addrkeylist(count, masters, &newaddrs, + keynames, &newnames, zone->mctx); + if (result != ISC_R_SUCCESS) { + isc_mem_put(zone->mctx, newok, count * sizeof(*newok)); + goto unlock; } /* * Everything is ok so attach to the zone. */ zone->curmaster = 0; - zone->masters = new; zone->mastersok = newok; - zone->masterkeynames = newname; + zone->masters = newaddrs; + zone->masterkeynames = newnames; zone->masterscnt = count; DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOMASTERS); @@ -5072,9 +5555,12 @@ zone_resigninc(dns_zone_t *zone) { zonediff_init(&zonediff, &_sig_diff); /* - * Updates are disabled. Pause for 5 minutes. + * Zone is frozen or automatic resigning is disabled. + * Pause for 5 minutes. */ - if (zone->update_disabled) { + if (zone->update_disabled || + DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN)) + { result = ISC_R_FAILURE; goto failure; } @@ -5186,18 +5672,18 @@ zone_resigninc(dns_zone_t *zone) { */ if (ISC_LIST_EMPTY(zonediff.diff->tuples)) { /* - * Commit the changes if any key has been marked as offline. - */ + * Commit the changes if any key has been marked as offline. */ if (zonediff.offline) dns_db_closeversion(db, &version, ISC_TRUE); goto failure; } /* Increment SOA serial if we have made changes */ - result = increment_soa_serial(db, version, zonediff.diff, zone->mctx); + result = update_soa_serial(db, version, zonediff.diff, zone->mctx, + zone->updatemethod); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, - "zone_resigninc:increment_soa_serial -> %s", + "zone_resigninc:update_soa_serial -> %s", dns_result_totext(result)); goto failure; } @@ -5217,7 +5703,7 @@ zone_resigninc(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, zonediff.diff, "zone_resigninc")); + CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_resigninc")); /* Everything has succeeded. Commit the changes. */ dns_db_closeversion(db, &version, ISC_TRUE); @@ -5625,6 +6111,7 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, isc_buffer_t buffer; unsigned char parambuf[DNS_NSEC3PARAM_BUFFERSIZE]; dns_ttl_t ttl = 0; + isc_boolean_t nseconly = ISC_FALSE, nsec3ok = ISC_FALSE; dns_rdataset_init(&rdataset); @@ -5675,6 +6162,10 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, if (active) goto add; + + result = dns_nsec_nseconly(db, ver, &nseconly); + nsec3ok = (result == ISC_R_SUCCESS && !nseconly); + /* * Delete all private records which match that in nsec3chain. */ @@ -5697,7 +6188,9 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, continue; CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); - if (nsec3param.hash != chain->nsec3param.hash || + if ((!nsec3ok && + (nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0) || + nsec3param.hash != chain->nsec3param.hash || nsec3param.iterations != chain->nsec3param.iterations || nsec3param.salt_length != chain->nsec3param.salt_length || memcmp(nsec3param.salt, chain->nsec3param.salt, @@ -6271,7 +6764,8 @@ zone_nsec3chain(dns_zone_t *zone) { * of removing this NSEC3 chain. */ if (first && !updatensec && - (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0) { + (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0) + { result = need_nsec_chain(db, version, &nsec3chain->nsec3param, &buildnsecchain); @@ -6585,10 +7079,11 @@ zone_nsec3chain(dns_zone_t *zone) { goto failure; } - result = increment_soa_serial(db, version, zonediff.diff, zone->mctx); + result = update_soa_serial(db, version, zonediff.diff, zone->mctx, + zone->updatemethod); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" - "increment_soa_serial -> %s", + "update_soa_serial -> %s", dns_result_totext(result)); goto failure; } @@ -6603,7 +7098,7 @@ zone_nsec3chain(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, zonediff.diff, "zone_nsec3chain")); + CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_nsec3chain")); LOCK_ZONE(zone); zone_needdump(zone, DNS_DUMP_DELAY); @@ -7154,10 +7649,11 @@ zone_sign(dns_zone_t *zone) { goto failure; } - result = increment_soa_serial(db, version, zonediff.diff, zone->mctx); + result = update_soa_serial(db, version, zonediff.diff, zone->mctx, + zone->updatemethod); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, - "zone_sign:increment_soa_serial -> %s", + "zone_sign:update_soa_serial -> %s", dns_result_totext(result)); goto failure; } @@ -7179,7 +7675,7 @@ zone_sign(dns_zone_t *zone) { /* * Write changes to journal file. */ - CHECK(zone_journal(zone, zonediff.diff, "zone_sign")); + CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_sign")); pauseall: /* @@ -7941,8 +8437,9 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { if (!ISC_LIST_EMPTY(diff.tuples)) { /* Write changes to journal file. */ - CHECK(increment_soa_serial(kfetch->db, ver, &diff, mctx)); - CHECK(zone_journal(zone, &diff, "keyfetch_done")); + CHECK(update_soa_serial(kfetch->db, ver, &diff, mctx, + zone->updatemethod)); + CHECK(zone_journal(zone, &diff, NULL, "keyfetch_done")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); @@ -8115,8 +8612,9 @@ zone_refreshkeys(dns_zone_t *zone) { } } if (!ISC_LIST_EMPTY(diff.tuples)) { - CHECK(increment_soa_serial(db, ver, &diff, zone->mctx)); - CHECK(zone_journal(zone, &diff, "zone_refreshkeys")); + CHECK(update_soa_serial(db, ver, &diff, zone->mctx, + zone->updatemethod)); + CHECK(zone_journal(zone, &diff, NULL, "zone_refreshkeys")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -8164,11 +8662,17 @@ zone_maintenance(dns_zone_t *zone) { ENTER; /* + * Are we pending load/reload? + */ + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) + return; + + /* * Configuring the view of this zone may have * failed, for example because the config file * had a syntax error. In that case, the view - * db or resolver will be NULL, and we had better not try - * to do maintenance on it. + * adb or resolver will be NULL, and we had better not try + * to do further maintenance on it. */ if (zone->view == NULL || zone->view->adb == NULL) return; @@ -8179,6 +8683,9 @@ zone_maintenance(dns_zone_t *zone) { * Expire check. */ switch (zone->type) { + case dns_zone_redirect: + if (zone->masters == NULL) + break; case dns_zone_slave: case dns_zone_stub: LOCK_ZONE(zone); @@ -8197,6 +8704,9 @@ zone_maintenance(dns_zone_t *zone) { * Up to date check. */ switch (zone->type) { + case dns_zone_redirect: + if (zone->masters == NULL) + break; case dns_zone_slave: case dns_zone_stub: if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH) && @@ -8222,6 +8732,7 @@ zone_maintenance(dns_zone_t *zone) { case dns_zone_master: case dns_zone_slave: case dns_zone_key: + case dns_zone_redirect: case dns_zone_stub: LOCK_ZONE(zone); if (zone->masterfile != NULL && @@ -8249,6 +8760,7 @@ zone_maintenance(dns_zone_t *zone) { */ switch (zone->type) { case dns_zone_master: + case dns_zone_redirect: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) && isc_time_compare(&now, &zone->notifytime) >= 0) zone_notify(zone, &now); @@ -8278,6 +8790,7 @@ zone_maintenance(dns_zone_t *zone) { switch (zone->type) { case dns_zone_master: + case dns_zone_redirect: case dns_zone_slave: /* * Do we need to sign/resign some RRsets? @@ -8299,6 +8812,7 @@ zone_maintenance(dns_zone_t *zone) { set_key_expiry_warning(zone, zone->key_expiry, isc_time_seconds(&now)); break; + default: break; } @@ -8307,10 +8821,31 @@ zone_maintenance(dns_zone_t *zone) { void dns_zone_markdirty(dns_zone_t *zone) { + isc_uint32_t serial; + isc_result_t result = ISC_R_SUCCESS; LOCK_ZONE(zone); - if (zone->type == dns_zone_master) - set_resigntime(zone); /* XXXMPA make separate call back */ + if (zone->type == dns_zone_master) { + if (inline_raw(zone)) { + unsigned int soacount; + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + result = zone_get_from_db(zone, zone->db, NULL, + &soacount, &serial, + NULL, NULL, NULL, + NULL, NULL); + } else + result = DNS_R_NOTLOADED; + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (result == ISC_R_SUCCESS && soacount > 0U) + zone_send_secureserial(zone, ISC_FALSE, serial); + } + + /* XXXMPA make separate call back */ + if (result == ISC_R_SUCCESS) + set_resigntime(zone); + } zone_needdump(zone, DNS_DUMP_DELAY); UNLOCK_ZONE(zone); } @@ -8498,6 +9033,22 @@ dump_done(void *arg, isc_result_t result) { tresult = dns_db_getsoaserial(db, version, &serial); /* + * If there is a secure version of this zone + * use its serial if it is less than ours. + */ + if (tresult == ISC_R_SUCCESS && inline_raw(zone) && + zone->secure->db != NULL) + { + isc_uint32_t sserial; + isc_result_t mresult; + + mresult = dns_db_getsoaserial(zone->secure->db, + NULL, &sserial); + if (mresult == ISC_R_SUCCESS && + isc_serial_lt(sserial, serial)) + serial = sserial; + } + /* * Note: we are task locked here so we can test * zone->xfr safely. */ @@ -8605,10 +9156,15 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { result = DNS_R_CONTINUE; UNLOCK_ZONE(zone); } else { + dns_masterrawheader_t rawdata; dns_db_currentversion(db, &version); - result = dns_master_dump2(zone->mctx, db, version, + dns_master_initrawheader(&rawdata); + if (inline_secure(zone)) + get_raw_serial(zone->raw, &rawdata); + result = dns_master_dump3(zone->mctx, db, version, &dns_master_style_default, - masterfile, masterformat); + masterfile, masterformat, + &rawdata); dns_db_closeversion(db, &version, ISC_FALSE); } fail: @@ -8647,11 +9203,12 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { static isc_result_t dumptostream(dns_zone_t *zone, FILE *fd, const dns_master_style_t *style, - dns_masterformat_t format) + dns_masterformat_t format, const isc_uint32_t rawversion) { isc_result_t result; dns_dbversion_t *version = NULL; dns_db_t *db = NULL; + dns_masterrawheader_t rawdata; REQUIRE(DNS_ZONE_VALID(zone)); @@ -8663,29 +9220,46 @@ dumptostream(dns_zone_t *zone, FILE *fd, const dns_master_style_t *style, return (DNS_R_NOTLOADED); dns_db_currentversion(db, &version); - result = dns_master_dumptostream2(zone->mctx, db, version, style, - format, fd); + dns_master_initrawheader(&rawdata); + if (rawversion == 0) + rawdata.flags |= DNS_MASTERRAW_COMPAT; + else if (inline_secure(zone)) + get_raw_serial(zone->raw, &rawdata); + else if (zone->sourceserialset) { + rawdata.flags = DNS_MASTERRAW_SOURCESERIALSET; + rawdata.sourceserial = zone->sourceserial; + } + result = dns_master_dumptostream3(zone->mctx, db, version, style, + format, &rawdata, fd); dns_db_closeversion(db, &version, ISC_FALSE); dns_db_detach(&db); return (result); } isc_result_t +dns_zone_dumptostream3(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, + const dns_master_style_t *style, + const isc_uint32_t rawversion) +{ + return (dumptostream(zone, fd, style, format, rawversion)); +} + +isc_result_t dns_zone_dumptostream2(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, const dns_master_style_t *style) { - return dumptostream(zone, fd, style, format); + return (dumptostream(zone, fd, style, format, DNS_RAWFORMAT_VERSION)); } isc_result_t dns_zone_dumptostream(dns_zone_t *zone, FILE *fd) { - return dumptostream(zone, fd, &dns_master_style_default, - dns_masterformat_text); + return (dumptostream(zone, fd, &dns_master_style_default, + dns_masterformat_text, 0)); } isc_result_t dns_zone_fulldumptostream(dns_zone_t *zone, FILE *fd) { - return dumptostream(zone, fd, &dns_master_style_full, - dns_masterformat_text); + return (dumptostream(zone, fd, &dns_master_style_full, + dns_masterformat_text, 0)); } void @@ -8880,6 +9454,8 @@ notify_destroy(dns_notify_t *notify, isc_boolean_t locked) { dns_request_destroy(¬ify->request); if (dns_name_dynamic(¬ify->ns)) dns_name_free(¬ify->ns, notify->mctx); + if (notify->key != NULL) + dns_tsigkey_detach(¬ify->key); mctx = notify->mctx; isc_mem_put(notify->mctx, notify, sizeof(*notify)); isc_mem_detach(&mctx); @@ -8901,6 +9477,7 @@ notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) { notify->zone = NULL; notify->find = NULL; notify->request = NULL; + notify->key = NULL; isc_sockaddr_any(¬ify->dst); dns_name_init(¬ify->ns, NULL); ISC_LINK_INIT(notify, link); @@ -9045,15 +9622,23 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) { if (result != ISC_R_SUCCESS) goto cleanup; - isc_netaddr_fromsockaddr(&dstip, ¬ify->dst); - isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); - result = dns_view_getpeertsig(notify->zone->view, &dstip, &key); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - notify_log(notify->zone, ISC_LOG_ERROR, "NOTIFY to %s not " - "sent. Peer TSIG key lookup failure.", addrbuf); - goto cleanup_message; + if (notify->key != NULL) { + /* Transfer ownership of key */ + key = notify->key; + notify->key = NULL; + } else { + isc_netaddr_fromsockaddr(&dstip, ¬ify->dst); + isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); + result = dns_view_getpeertsig(notify->zone->view, &dstip, &key); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { + notify_log(notify->zone, ISC_LOG_ERROR, + "NOTIFY to %s not sent. " + "Peer TSIG key lookup failure.", addrbuf); + goto cleanup_message; + } } + /* XXX: should we log the tsig key too? */ notify_log(notify->zone, ISC_LOG_DEBUG(3), "sending notify to %s", addrbuf); if (notify->zone->view->peers != NULL) { @@ -9255,14 +9840,30 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { */ LOCK_ZONE(zone); for (i = 0; i < zone->notifycnt; i++) { + dns_tsigkey_t *key = NULL; + dst = zone->notify[i]; if (notify_isqueued(zone, NULL, &dst)) continue; + result = notify_create(zone->mctx, flags, ¬ify); if (result != ISC_R_SUCCESS) continue; + zone_iattach(zone, ¬ify->zone); notify->dst = dst; + + if ((zone->notifykeynames != NULL) && + (zone->notifykeynames[i] != NULL)) { + dns_view_t *view = dns_zone_getview(zone); + dns_name_t *keyname = zone->notifykeynames[i]; + result = dns_view_gettsig(view, keyname, &key); + if (result == ISC_R_SUCCESS) { + notify->key = key; + key = NULL; + } + } + ISC_LIST_APPEND(zone->notifies, notify, link); result = notify_send_queue(notify); if (result != ISC_R_SUCCESS) @@ -9753,7 +10354,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { "master %s exceeded (source %s)", master, source); /* Try with slave with TCP. */ - if (zone->type == dns_zone_slave && + if ((zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH)) { if (!dns_zonemgr_unreachable(zone->zmgr, &zone->masteraddr, @@ -9819,7 +10421,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { * Perhaps AXFR/IXFR is allowed even if SOA queries aren't. */ if (msg->rcode == dns_rcode_refused && - zone->type == dns_zone_slave) + (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect)) goto tcp_transfer; goto next_master; } @@ -9828,7 +10431,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { * If truncated punt to zone transfer which will query again. */ if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { - if (zone->type == dns_zone_slave) { + if (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) { dns_zone_log(zone, ISC_LOG_INFO, "refresh: truncated UDP answer, " "initiating TCP zone xfer " @@ -9955,7 +10559,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { dns_zone_log(zone, ISC_LOG_INFO, "refresh: skipping %s as master %s " "(source %s) is unreachable (cached)", - zone->type == dns_zone_slave ? + (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) ? "zone transfer" : "NS query", master, source); goto next_master; @@ -9963,7 +10568,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { tcp_transfer: isc_event_free(&event); dns_request_destroy(&zone->request); - if (zone->type == dns_zone_slave) { + if (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) { do_queue_xfrin = ISC_TRUE; } else { INSIST(zone->type == dns_zone_stub); @@ -10643,6 +11249,7 @@ static void zone_shutdown(isc_task_t *task, isc_event_t *event) { dns_zone_t *zone = (dns_zone_t *) event->ev_arg; isc_boolean_t free_needed, linked = ISC_FALSE; + dns_zone_t *raw = NULL, *secure = NULL; UNUSED(task); REQUIRE(DNS_ZONE_VALID(zone)); @@ -10725,7 +11332,19 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { */ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SHUTDOWN); free_needed = exit_check(zone); + if (inline_secure(zone)) { + raw = zone->raw; + zone->raw = NULL; + } + if (inline_raw(zone)) { + secure = zone->secure; + zone->secure = NULL; + } UNLOCK_ZONE(zone); + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); if (free_needed) zone_free(zone); } @@ -10759,6 +11378,11 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { isc_time_settoepoch(&next); switch (zone->type) { + case dns_zone_redirect: + if (zone->masters != NULL) + goto treat_as_slave; + /* FALLTHROUGH */ + case dns_zone_master: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) next = zone->notifytime; @@ -10769,6 +11393,8 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { isc_time_compare(&zone->dumptime, &next) < 0) next = zone->dumptime; } + if (zone->type == dns_zone_redirect) + break; if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING) && !isc_time_isepoch(&zone->refreshkeytime)) { if (isc_time_isepoch(&next) || @@ -10798,9 +11424,10 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { break; case dns_zone_slave: + treat_as_slave: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) next = zone->notifytime; - /*FALLTHROUGH*/ + /* FALLTHROUGH */ case dns_zone_stub: if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH) && @@ -11078,9 +11705,17 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, isc_sockaddr_format(from, fromtext, sizeof(fromtext)); /* - * We only handle NOTIFY (SOA) at the present. + * Notify messages are processed by the raw zone. */ LOCK_ZONE(zone); + if (inline_secure(zone)) { + result = dns_zone_notifyreceive(zone->raw, from, msg); + UNLOCK_ZONE(zone); + return (result); + } + /* + * We only handle NOTIFY (SOA) at the present. + */ if (isc_sockaddr_pf(from) == PF_INET) inc_stats(zone, dns_zonestatscounter_notifyinv4); else @@ -11468,15 +12103,17 @@ zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) { * Leave space for terminating '\0'. */ isc_buffer_init(&buffer, buf, length - 1); - if (dns_name_dynamic(&zone->origin)) - result = dns_name_totext(&zone->origin, ISC_TRUE, &buffer); - if (result != ISC_R_SUCCESS && - isc_buffer_availablelength(&buffer) >= (sizeof("<UNKNOWN>") - 1)) - isc_buffer_putstr(&buffer, "<UNKNOWN>"); + if (zone->type != dns_zone_redirect && zone->type != dns_zone_key) { + if (dns_name_dynamic(&zone->origin)) + result = dns_name_totext(&zone->origin, ISC_TRUE, &buffer); + if (result != ISC_R_SUCCESS && + isc_buffer_availablelength(&buffer) >= (sizeof("<UNKNOWN>") - 1)) + isc_buffer_putstr(&buffer, "<UNKNOWN>"); - if (isc_buffer_availablelength(&buffer) > 0) - isc_buffer_putstr(&buffer, "/"); - (void)dns_rdataclass_totext(zone->rdclass, &buffer); + if (isc_buffer_availablelength(&buffer) > 0) + isc_buffer_putstr(&buffer, "/"); + (void)dns_rdataclass_totext(zone->rdclass, &buffer); + } if (zone->view != NULL && strcmp(zone->view->name, "_bind") != 0 && strcmp(zone->view->name, "_default") != 0 && @@ -11484,6 +12121,10 @@ zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) { isc_buffer_putstr(&buffer, "/"); isc_buffer_putstr(&buffer, zone->view->name); } + if (inline_secure(zone) && 9U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (signed)"); + if (inline_raw(zone) && 11U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (unsigned)"); buf[isc_buffer_usedlength(&buffer)] = '\0'; } @@ -11585,8 +12226,9 @@ dns_zone_logc(dns_zone_t *zone, isc_logcategory_t *category, vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); isc_log_write(dns_lctx, category, DNS_LOGMODULE_ZONE, - level, "%s %s: %s", (zone->type == dns_zone_key) ? - "managed-keys-zone" : "zone", zone->strnamerd, message); + level, "%s%s: %s", (zone->type == dns_zone_key) ? + "managed-keys-zone" : (zone->type == dns_zone_redirect) ? + "redirect-zone" : "zone ", zone->strnamerd, message); } void @@ -11601,8 +12243,9 @@ dns_zone_log(dns_zone_t *zone, int level, const char *fmt, ...) { vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, - level, "%s %s: %s", (zone->type == dns_zone_key) ? - "managed-keys-zone" : "zone", zone->strnamerd, message); + level, "%s%s: %s", (zone->type == dns_zone_key) ? + "managed-keys-zone" : (zone->type == dns_zone_redirect) ? + "redirect-zone" : "zone ", zone->strnamerd, message); } static void @@ -11612,6 +12255,7 @@ zone_debuglog(dns_zone_t *zone, const char *me, int debuglevel, va_list ap; char message[4096]; int level = ISC_LOG_DEBUG(debuglevel); + const char *zstr; if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) return; @@ -11619,9 +12263,21 @@ zone_debuglog(dns_zone_t *zone, const char *me, int debuglevel, va_start(ap, fmt); vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); + + switch (zone->type) { + case dns_zone_key: + zstr = "managed-keys-zone"; + break; + case dns_zone_redirect: + zstr = "redirect-zone"; + break; + default: + zstr = "zone"; + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, - level, "%s: %s %s: %s", me, zone->type != dns_zone_key ? - "zone" : "managed-keys-zone", zone->strnamerd, message); + level, "%s: %s %s: %s", me, zstr, zone->strnamerd, + message); } static int @@ -11799,6 +12455,572 @@ notify_done(isc_task_t *task, isc_event_t *event) { dns_message_destroy(&message); } +struct secure_event { + isc_event_t e; + dns_db_t *db; + isc_uint32_t serial; +}; + +static void +update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { + UNUSED(arg); + dns_zone_log(zone, level, "%s", message); +} + +static isc_result_t +sync_secure_journal(dns_zone_t *zone, dns_journal_t *journal, + isc_uint32_t start, isc_uint32_t end, + dns_difftuple_t **soatuplep, dns_diff_t *diff) +{ + isc_result_t result; + dns_difftuple_t *tuple = NULL; + dns_diffop_t op = DNS_DIFFOP_ADD; + int n_soa = 0; + + REQUIRE(soatuplep != NULL); + + if (start == end) + return (DNS_R_UNCHANGED); + + CHECK(dns_journal_iter_init(journal, start, end)); + for (result = dns_journal_first_rr(journal); + result == ISC_R_SUCCESS; + result = dns_journal_next_rr(journal)) + { + dns_name_t *name = NULL; + isc_uint32_t ttl; + dns_rdata_t *rdata = NULL; + dns_journal_current_rr(journal, &name, &ttl, &rdata); + + if (rdata->type == dns_rdatatype_soa) { + n_soa++; + if (n_soa == 2) { + /* + * Save the latest raw SOA record. + */ + if (*soatuplep != NULL) + dns_difftuple_free(soatuplep); + CHECK(dns_difftuple_create(diff->mctx, + DNS_DIFFOP_ADD, + name, ttl, rdata, + soatuplep)); + } + if (n_soa == 3) + n_soa = 1; + continue; + } + + /* Sanity. */ + if (n_soa == 0) { + dns_zone_log(zone->raw, ISC_LOG_ERROR, + "corrupt journal file: '%s'\n", + zone->raw->journal); + return (ISC_R_FAILURE); + } + + if (zone->privatetype != 0 && + rdata->type == zone->privatetype) + continue; + + if (rdata->type == dns_rdatatype_nsec || + rdata->type == dns_rdatatype_rrsig || + rdata->type == dns_rdatatype_nsec3 || + rdata->type == dns_rdatatype_dnskey || + rdata->type == dns_rdatatype_nsec3param) + continue; + + op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; + + CHECK(dns_difftuple_create(diff->mctx, op, name, ttl, rdata, + &tuple)); + dns_diff_appendminimal(diff, &tuple); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + failure: + return(result); +} + +static isc_result_t +sync_secure_db(dns_zone_t *seczone, dns_db_t *secdb, + dns_dbversion_t *secver, dns_difftuple_t **soatuple, + dns_diff_t *diff) +{ + isc_result_t result; + dns_db_t *rawdb = NULL; + dns_dbversion_t *rawver = NULL; + dns_difftuple_t *tuple = NULL, *next; + dns_difftuple_t *oldtuple = NULL, *newtuple = NULL; + dns_rdata_soa_t oldsoa, newsoa; + + REQUIRE(DNS_ZONE_VALID(seczone)); + REQUIRE(inline_secure(seczone)); + REQUIRE(soatuple != NULL && *soatuple == NULL); + + if (!seczone->sourceserialset) + return (DNS_R_UNCHANGED); + + dns_db_attach(seczone->raw->db, &rawdb); + dns_db_currentversion(rawdb, &rawver); + result = dns_db_diffx(diff, rawdb, rawver, secdb, secver, NULL); + dns_db_closeversion(rawdb, &rawver, ISC_FALSE); + dns_db_detach(&rawdb); + + if (result != ISC_R_SUCCESS) + return (result); + + for (tuple = ISC_LIST_HEAD(diff->tuples); + tuple != NULL; + tuple = next) + { + next = ISC_LIST_NEXT(tuple, link); + if (tuple->rdata.type == dns_rdatatype_nsec || + tuple->rdata.type == dns_rdatatype_rrsig || + tuple->rdata.type == dns_rdatatype_dnskey || + tuple->rdata.type == dns_rdatatype_nsec3 || + tuple->rdata.type == dns_rdatatype_nsec3param) + { + ISC_LIST_UNLINK(diff->tuples, tuple, link); + dns_difftuple_free(&tuple); + continue; + } + if (tuple->rdata.type == dns_rdatatype_soa) { + if (tuple->op == DNS_DIFFOP_DEL) { + INSIST(oldtuple == NULL); + oldtuple = tuple; + } + if (tuple->op == DNS_DIFFOP_ADD) { + INSIST(newtuple == NULL); + newtuple = tuple; + } + } + } + + if (oldtuple != NULL && newtuple != NULL) { + + result = dns_rdata_tostruct(&oldtuple->rdata, &oldsoa, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + result = dns_rdata_tostruct(&newtuple->rdata, &newsoa, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + /* + * If the SOA records are the same except for the serial + * remove them from the diff. + */ + if (oldsoa.refresh == newsoa.refresh && + oldsoa.retry == newsoa.retry && + oldsoa.minimum == newsoa.minimum && + oldsoa.expire == newsoa.expire && + dns_name_equal(&oldsoa.origin, &newsoa.origin) && + dns_name_equal(&oldsoa.contact, &newsoa.contact)) { + ISC_LIST_UNLINK(diff->tuples, oldtuple, link); + dns_difftuple_free(&oldtuple); + ISC_LIST_UNLINK(diff->tuples, newtuple, link); + dns_difftuple_free(&newtuple); + } + } + + if (ISC_LIST_EMPTY(diff->tuples)) + return (DNS_R_UNCHANGED); + + /* + * If there are still SOA records in the diff they can now be removed + * saving the new SOA record. + */ + if (oldtuple != NULL) { + ISC_LIST_UNLINK(diff->tuples, oldtuple, link); + dns_difftuple_free(&oldtuple); + } + + if (newtuple != NULL) { + ISC_LIST_UNLINK(diff->tuples, newtuple, link); + *soatuple = newtuple; + } + + return (ISC_R_SUCCESS); +} + +static void +receive_secure_serial(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_journal_t *rjournal = NULL; + isc_uint32_t start, end; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbversion_t *newver = NULL, *oldver = NULL; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL, *soatuple = NULL; + dns_update_log_t log = { update_log_cb, NULL }; + isc_time_t timenow; + + zone = event->ev_arg; + end = ((struct secure_event *)event)->serial; + isc_event_free(&event); + + LOCK_ZONE(zone); + + dns_diff_init(zone->mctx, &diff); + + UNUSED(task); + + /* + * zone->db may be NULL if the load from disk failed. + */ + if (zone->db == NULL || !inline_secure(zone)) { + result = ISC_R_FAILURE; + goto failure; + } + + /* + * We first attempt to sync the raw zone to the secure zone + * by using the raw zone's journal, applying all the deltas + * from the latest source-serial of the secure zone up to + * the current serial number of the raw zone. + * + * If that fails, then we'll fall back to a direct comparison + * between raw and secure zones. + */ + result = dns_journal_open(zone->raw->mctx, zone->raw->journal, + DNS_JOURNAL_WRITE, &rjournal); + if (result != ISC_R_SUCCESS) + goto failure; + else { + dns_journal_t *sjournal = NULL; + + result = dns_journal_open(zone->mctx, zone->journal, + DNS_JOURNAL_READ, &sjournal); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto failure; + + if (!dns_journal_get_sourceserial(rjournal, &start)) { + start = dns_journal_first_serial(rjournal); + dns_journal_set_sourceserial(rjournal, start); + } + if (sjournal != NULL) { + isc_uint32_t serial; + /* + * We read the secure journal first, if that exists + * use its value provided it is greater that from the + * raw journal. + */ + if (dns_journal_get_sourceserial(sjournal, &serial)) { + if (isc_serial_gt(serial, start)) + start = serial; + } + dns_journal_destroy(&sjournal); + } + } + + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + CHECK(dns_db_newversion(db, &newver)); + + /* + * Try to apply diffs from the raw zone's journal to the secure + * zone. If that fails, we recover by syncing up the databases + * directly. + */ + result = sync_secure_journal(zone, rjournal, start, end, + &soatuple, &diff); + if (result == DNS_R_UNCHANGED) + goto failure; + else if (result != ISC_R_SUCCESS) + CHECK(sync_secure_db(zone, db, oldver, &soatuple, &diff)); + + CHECK(dns_diff_apply(&diff, db, newver)); + + if (soatuple != NULL) { + isc_uint32_t oldserial, newserial, desired; + + CHECK(dns_db_createsoatuple(db, oldver, diff.mctx, + DNS_DIFFOP_DEL, &tuple)); + oldserial = dns_soa_getserial(&tuple->rdata); + newserial = desired = dns_soa_getserial(&soatuple->rdata); + if (!isc_serial_gt(newserial, oldserial)) { + newserial = oldserial + 1; + if (newserial == 0) + newserial++; + dns_soa_setserial(newserial, &soatuple->rdata); + } + CHECK(do_one_tuple(&tuple, db, newver, &diff)); + CHECK(do_one_tuple(&soatuple, db, newver, &diff)); + dns_zone_log(zone, ISC_LOG_INFO, "serial %u (unsigned %u)", + newserial, desired); + } else + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + + CHECK(dns_update_signatures(&log, zone, db, oldver, newver, + &diff, zone->sigvalidityinterval)); + + CHECK(zone_journal(zone, &diff, &end, "receive_secure_serial")); + + dns_journal_set_sourceserial(rjournal, end); + dns_journal_commit(rjournal); + + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + + zone->sourceserial = end; + zone->sourceserialset = ISC_TRUE; + zone_needdump(zone, DNS_DUMP_DELAY); + + TIME_NOW(&timenow); + zone_settimer(zone, &timenow); + + dns_db_closeversion(db, &oldver, ISC_FALSE); + dns_db_closeversion(db, &newver, ISC_TRUE); + + failure: + UNLOCK_ZONE(zone); + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_serial: %s", + dns_result_totext(result)); + if (tuple != NULL) + dns_difftuple_free(&tuple); + if (soatuple != NULL) + dns_difftuple_free(&soatuple); + if (db != NULL) { + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, ISC_FALSE); + dns_db_detach(&db); + } + if (rjournal != NULL) + dns_journal_destroy(&rjournal); + dns_diff_clear(&diff); + dns_zone_idetach(&zone); +} + +static isc_result_t +zone_send_secureserial(dns_zone_t *zone, isc_boolean_t locked, + isc_uint32_t serial) +{ + isc_event_t *e; + dns_zone_t *dummy = NULL; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECURESERIAL, + receive_secure_serial, zone->secure, + sizeof(struct secure_event)); + if (e == NULL) + return (ISC_R_NOMEMORY); + ((struct secure_event *)e)->serial = serial; + if (locked) + zone_iattach(zone->secure, &dummy); + else + dns_zone_iattach(zone->secure, &dummy); + isc_task_send(zone->secure->task, &e); + + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SENDSECURE); + return (ISC_R_SUCCESS); +} + +static isc_result_t +checkandaddsoa(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdataset_t *rdataset, isc_uint32_t oldserial) +{ + dns_rdata_soa_t soa; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatalist_t temprdatalist; + dns_rdataset_t temprdataset; + isc_buffer_t b; + isc_result_t result; + unsigned char buf[DNS_SOA_BUFFERSIZE]; + + result = dns_rdataset_first(rdataset); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &soa, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + if (isc_serial_gt(soa.serial, oldserial)) + return (dns_db_addrdataset(db, node, version, 0, rdataset, 0, + NULL)); + /* + * Always bump the serial. + */ + oldserial++; + if (oldserial == 0) + oldserial++; + soa.serial = oldserial; + + /* + * Construct a replacement rdataset. + */ + dns_rdata_reset(&rdata); + isc_buffer_init(&b, buf, sizeof(buf)); + result = dns_rdata_fromstruct(&rdata, rdataset->rdclass, + dns_rdatatype_soa, &soa, &b); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + temprdatalist.rdclass = rdata.rdclass; + temprdatalist.type = rdata.type; + temprdatalist.covers = 0; + temprdatalist.ttl = rdataset->ttl; + ISC_LIST_INIT(temprdatalist.rdata); + ISC_LIST_APPEND(temprdatalist.rdata, &rdata, link); + + dns_rdataset_init(&temprdataset); + result = dns_rdatalist_tordataset(&temprdatalist, &temprdataset); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + return (dns_db_addrdataset(db, node, version, 0, &temprdataset, + 0, NULL)); +} + +static void +receive_secure_db(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_zone_t *zone; + dns_db_t *rawdb, *db = NULL; + dns_dbnode_t *rawnode = NULL, *node = NULL; + dns_fixedname_t fname; + dns_name_t *name; + dns_dbiterator_t *dbiterator = NULL; + dns_rdatasetiter_t *rdsit = NULL; + dns_rdataset_t rdataset; + dns_dbversion_t *version = NULL; + isc_time_t loadtime; + unsigned int oldserial = 0; + isc_boolean_t have_oldserial = ISC_FALSE; + + UNUSED(task); + + zone = event->ev_arg; + rawdb = ((struct secure_event *)event)->db; + isc_event_free(&event); + + REQUIRE(inline_secure(zone)); + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_rdataset_init(&rdataset); + + TIME_NOW(&loadtime); + if (zone->db != NULL) { + result = dns_db_getsoaserial(zone->db, NULL, &oldserial); + if (result == ISC_R_SUCCESS) + have_oldserial = ISC_TRUE; + } + + result = dns_db_create(zone->mctx, zone->db_argv[0], + &zone->origin, dns_dbtype_zone, zone->rdclass, + zone->db_argc - 1, zone->db_argv + 1, &db); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_newversion(db, &version); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_createiterator(rawdb, 0, &dbiterator); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_dbiterator_first(dbiterator); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiterator)) { + result = dns_dbiterator_current(dbiterator, &rawnode, name); + if (result != ISC_R_SUCCESS) + continue; + + result = dns_db_findnode(db, name, ISC_TRUE, &node); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_allrdatasets(rawdb, rawnode, NULL, 0, &rdsit); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdatasetiter_first(rdsit); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsit)) { + dns_rdatasetiter_current(rdsit, &rdataset); + if (rdataset.type == dns_rdatatype_nsec || + rdataset.type == dns_rdatatype_rrsig || + rdataset.type == dns_rdatatype_nsec3 || + rdataset.type == dns_rdatatype_dnskey || + rdataset.type == dns_rdatatype_nsec3param) { + dns_rdataset_disassociate(&rdataset); + continue; + } + if (rdataset.type == dns_rdatatype_soa && + have_oldserial) { + result = checkandaddsoa(db, node, version, + &rdataset, oldserial); + } else + result = dns_db_addrdataset(db, node, version, + 0, &rdataset, 0, + NULL); + if (result != ISC_R_SUCCESS) + goto failure; + + dns_rdataset_disassociate(&rdataset); + } + dns_rdatasetiter_destroy(&rdsit); + dns_db_detachnode(rawdb, &rawnode); + dns_db_detachnode(db, &node); + } + + dns_db_closeversion(db, &version, ISC_TRUE); + /* + * Lock hierarchy: zmgr, zone, raw. + */ + LOCK_ZONE(zone); + if (inline_secure(zone)) + LOCK_ZONE(zone->raw); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS); + zone_needdump(zone, 0); /* XXXMPA */ + if (inline_secure(zone)) + UNLOCK_ZONE(zone->raw); + UNLOCK_ZONE(zone); + + failure: + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_db: %s", + dns_result_totext(result)); + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (db != NULL) { + if (node != NULL) + dns_db_detachnode(db, &node); + dns_db_detach(&db); + } + if (rawnode != NULL) + dns_db_detachnode(rawdb, &rawnode); + dns_db_detach(&rawdb); + if (dbiterator != NULL) + dns_dbiterator_destroy(&dbiterator); + dns_zone_idetach(&zone); +} + +static isc_result_t +zone_send_securedb(dns_zone_t *zone, isc_boolean_t locked, dns_db_t *db) { + isc_event_t *e; + dns_db_t *dummy = NULL; + dns_zone_t *secure = NULL; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECUREDB, + receive_secure_db, zone->secure, + sizeof(struct secure_event)); + if (e == NULL) + return (ISC_R_NOMEMORY); + dns_db_attach(db, &dummy); + ((struct secure_event *)e)->db = dummy; + if (locked) + zone_iattach(zone->secure, &secure); + else + dns_zone_iattach(zone->secure, &secure); + + isc_task_send(zone->secure->task, &e); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SENDSECURE); + return (ISC_R_SUCCESS); +} + isc_result_t dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { isc_result_t result; @@ -11860,7 +13082,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { */ if (zone->db != NULL && zone->journal != NULL && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) && - !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) + { isc_uint32_t serial, oldserial; unsigned int soacount; @@ -11882,8 +13105,10 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); RUNTIME_CHECK(soacount > 0U); - if (zone->type == dns_zone_slave && - !isc_serial_gt(serial, oldserial)) { + if ((zone->type == dns_zone_slave || + (zone->type == dns_zone_redirect && + zone->masters != NULL)) + && !isc_serial_gt(serial, oldserial)) { isc_uint32_t serialmin, serialmax; serialmin = (oldserial + 1) & 0xffffffffU; serialmax = (oldserial + 0x7fffffffU) & 0xffffffffU; @@ -11919,6 +13144,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { break; } } + if (zone->type == dns_zone_master && inline_raw(zone)) + zone_send_secureserial(zone, ISC_FALSE, serial); } else { if (dump && zone->masterfile != NULL) { /* @@ -11969,13 +13196,14 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { zone->journal, strbuf); } } + + if (inline_raw(zone)) + zone_send_securedb(zone, ISC_FALSE, db); } dns_db_closeversion(db, &ver, ISC_FALSE); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3), - "replacing zone database"); + dns_zone_log(zone, ISC_LOG_DEBUG(3), "replacing zone database"); if (zone->db != NULL) zone_detachdb(zone); @@ -12120,6 +13348,8 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { dns_zone_log(zone, ISC_LOG_INFO, "transferred serial %u%s", serial, buf); + if (inline_raw(zone)) + zone_send_secureserial(zone, ISC_FALSE, serial); } /* @@ -12274,18 +13504,26 @@ zone_loaddone(void *arg, isc_result_t result) { (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE)) result = tresult; - LOCK_ZONE(load->zone); - (void)zone_postload(load->zone, load->db, load->loadtime, result); - zonemgr_putio(&load->zone->readio); - DNS_ZONE_CLRFLAG(load->zone, DNS_ZONEFLG_LOADING); + /* + * Lock hierarchy: zmgr, zone, raw. + */ + LOCK_ZONE(zone); + if (inline_secure(zone)) + LOCK_ZONE(zone->raw); + (void)zone_postload(zone, load->db, load->loadtime, result); + zonemgr_putio(&zone->readio); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADING); + zone_idetach(&load->callbacks.zone); /* * Leave the zone frozen if the reload fails. */ if ((result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE) && - DNS_ZONE_FLAG(load->zone, DNS_ZONEFLG_THAW)) + DNS_ZONE_FLAG(zone, DNS_ZONEFLG_THAW)) zone->update_disabled = ISC_FALSE; - DNS_ZONE_CLRFLAG(load->zone, DNS_ZONEFLG_THAW); - UNLOCK_ZONE(load->zone); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_THAW); + if (inline_secure(zone)) + UNLOCK_ZONE(zone->raw); + UNLOCK_ZONE(zone); load->magic = 0; dns_db_detach(&load->db); @@ -12383,7 +13621,7 @@ queue_xfrin(dns_zone_t *zone) { */ static void got_transfer_quota(isc_task_t *task, isc_event_t *event) { - isc_result_t result; + isc_result_t result = ISC_R_SUCCESS; dns_peer_t *peer = NULL; char master[ISC_SOCKADDR_FORMATSIZE]; char source[ISC_SOCKADDR_FORMATSIZE]; @@ -12432,14 +13670,6 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { "no database exists yet, requesting AXFR of " "initial version from %s", master); xfrtype = dns_rdatatype_axfr; - } else if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) { - dns_zone_log(zone, ISC_LOG_DEBUG(1), "ixfr-from-differences " - "set, requesting %sAXFR from %s", soa_before, - master); - if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR)) - xfrtype = dns_rdatatype_soa; - else - xfrtype = dns_rdatatype_axfr; } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "forced reload, requesting AXFR of " @@ -12455,13 +13685,10 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { UNLOCK_ZONE(zone); } else { isc_boolean_t use_ixfr = ISC_TRUE; - if (peer != NULL && - dns_peer_getrequestixfr(peer, &use_ixfr) == - ISC_R_SUCCESS) { - ; /* Using peer setting */ - } else { - use_ixfr = zone->view->requestixfr; - } + if (peer != NULL) + result = dns_peer_getrequestixfr(peer, &use_ixfr); + if (peer == NULL || result != ISC_R_SUCCESS) + use_ixfr = zone->requestixfr; if (use_ixfr == ISC_FALSE) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "IXFR disabled, requesting %sAXFR from %s", @@ -12806,6 +14033,8 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, zmgr->timermgr = timermgr; zmgr->socketmgr = socketmgr; zmgr->zonetasks = NULL; + zmgr->loadtasks = NULL; + zmgr->mctxpool = NULL; zmgr->task = NULL; zmgr->rl = NULL; ISC_LIST_INIT(zmgr->zones); @@ -12874,6 +14103,33 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, } isc_result_t +dns_zonemgr_createzone(dns_zonemgr_t *zmgr, dns_zone_t **zonep) { + isc_result_t result; + isc_mem_t *mctx = NULL; + dns_zone_t *zone = NULL; + void *item; + + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + REQUIRE(zonep != NULL && *zonep == NULL); + + if (zmgr->mctxpool == NULL) + return (ISC_R_FAILURE); + + item = isc_pool_get(zmgr->mctxpool); + if (item == NULL) + return (ISC_R_FAILURE); + + isc_mem_attach((isc_mem_t *) item, &mctx); + result = dns_zone_create(&zone, mctx); + isc_mem_detach(&mctx); + + if (result == ISC_R_SUCCESS) + *zonep = zone; + + return (result); +} + +isc_result_t dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { isc_result_t result; @@ -12890,6 +14146,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { REQUIRE(zone->zmgr == NULL); isc_taskpool_gettask(zmgr->zonetasks, &zone->task); + isc_taskpool_gettask(zmgr->loadtasks, &zone->loadtask); /* * Set the task name. The tag will arbitrarily point to one @@ -12897,6 +14154,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { * to be managed last). */ isc_task_setname(zone->task, "zone", zone); + isc_task_setname(zone->loadtask, "loadzone", zone); result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive, NULL, NULL, @@ -12904,7 +14162,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { &zone->timer); if (result != ISC_R_SUCCESS) - goto cleanup_task; + goto cleanup_tasks; /* * The timer "holds" a iref. @@ -12918,7 +14176,8 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { goto unlock; - cleanup_task: + cleanup_tasks: + isc_task_detach(&zone->loadtask); isc_task_detach(&zone->task); unlock: @@ -13034,6 +14293,10 @@ dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { isc_task_destroy(&zmgr->task); if (zmgr->zonetasks != NULL) isc_taskpool_destroy(&zmgr->zonetasks); + if (zmgr->loadtasks != NULL) + isc_taskpool_destroy(&zmgr->loadtasks); + if (zmgr->mctxpool != NULL) + isc_pool_destroy(&zmgr->mctxpool); RWLOCK(&zmgr->rwlock, isc_rwlocktype_read); for (zone = ISC_LIST_HEAD(zmgr->zones); @@ -13047,23 +14310,56 @@ dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read); } +static isc_result_t +mctxinit(void **target, void *arg) { + isc_result_t result; + isc_mem_t *mctx = NULL; + + UNUSED(arg); + + REQUIRE(target != NULL && *target == NULL); + + result = isc_mem_create(0, 0, &mctx); + if (result != ISC_R_SUCCESS) + return (result); + isc_mem_setname(mctx, "zonemgr-pool", NULL); + + *target = mctx; + return (ISC_R_SUCCESS); +} + +static void +mctxfree(void **target) { + isc_mem_t *mctx = *(isc_mem_t **) target; + isc_mem_detach(&mctx); + *target = NULL; +} + +#define ZONES_PER_TASK 100 +#define ZONES_PER_MCTX 1000 + isc_result_t dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones) { isc_result_t result; - int ntasks = num_zones / 100; + int ntasks = num_zones / ZONES_PER_TASK; + int nmctx = num_zones / ZONES_PER_MCTX; isc_taskpool_t *pool = NULL; + isc_pool_t *mctxpool = NULL; REQUIRE(DNS_ZONEMGR_VALID(zmgr)); /* * For anything fewer than 1000 zones we use 10 tasks in - * the task pool. More than that, and we'll scale at one - * task per 100 zones. + * the task pools. More than that, and we'll scale at one + * task per 100 zones. Similarly, for anything smaller than + * 2000 zones we use 2 memory contexts, then scale at 1:1000. */ if (ntasks < 10) ntasks = 10; + if (nmctx < 2) + nmctx = 2; - /* Create or resize the zone task pool. */ + /* Create or resize the zone task pools. */ if (zmgr->zonetasks == NULL) result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx, ntasks, 2, &pool); @@ -13073,6 +14369,43 @@ dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones) { if (result == ISC_R_SUCCESS) zmgr->zonetasks = pool; + pool = NULL; + if (zmgr->loadtasks == NULL) + result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx, + ntasks, 2, &pool); + else + result = isc_taskpool_expand(&zmgr->loadtasks, ntasks, &pool); + + if (result == ISC_R_SUCCESS) + zmgr->loadtasks = pool; + +#ifdef BIND9 + /* + * We always set all tasks in the zone-load task pool to + * privileged. This prevents other tasks in the system from + * running while the server task manager is in privileged + * mode. + * + * NOTE: If we start using task privileges for any other + * part of the system than zone tasks, then this will need to be + * revisted. In that case we'd want to turn on privileges for + * zone tasks only when we were loading, and turn them off the + * rest of the time. For now, however, it's okay to just + * set it and forget it. + */ + isc_taskpool_setprivilege(zmgr->loadtasks, ISC_TRUE); +#endif + + /* Create or resize the zone memory context pool. */ + if (zmgr->mctxpool == NULL) + result = isc_pool_create(zmgr->mctx, nmctx, mctxfree, + mctxinit, NULL, &mctxpool); + else + result = isc_pool_expand(&zmgr->mctxpool, nmctx, &mctxpool); + + if (result == ISC_R_SUCCESS) + zmgr->mctxpool = mctxpool; + return (result); } @@ -13309,12 +14642,14 @@ zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high, io = isc_mem_get(zmgr->mctx, sizeof(*io)); if (io == NULL) return (ISC_R_NOMEMORY); + io->event = isc_event_allocate(zmgr->mctx, task, DNS_EVENT_IOREADY, action, arg, sizeof(*io->event)); if (io->event == NULL) { isc_mem_put(zmgr->mctx, io, sizeof(*io)); return (ISC_R_NOMEMORY); } + io->zmgr = zmgr; io->high = high; io->task = NULL; @@ -13334,9 +14669,8 @@ zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high, UNLOCK(&zmgr->iolock); *iop = io; - if (!queue) { + if (!queue) isc_task_send(io->task, &io->event); - } return (ISC_R_SUCCESS); } @@ -13613,7 +14947,8 @@ void dns_zone_forcereload(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); - if (zone->type == dns_zone_master) + if (zone->type == dns_zone_master || + (zone->type == dns_zone_redirect && zone->masters == NULL)) return; LOCK_ZONE(zone); @@ -13661,6 +14996,7 @@ dns_zone_setstats(dns_zone_t *zone, isc_stats_t *stats) { void dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) { + REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); @@ -13673,9 +15009,24 @@ dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) { } } UNLOCK_ZONE(zone); +} - return; +#ifdef NEWSTATS +void +dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats) { + + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + if (zone->requeststats_on && stats != NULL) { + if (zone->rcvquerystats == NULL) { + dns_stats_attach(stats, &zone->rcvquerystats); + zone->requeststats_on = ISC_TRUE; + } + } + UNLOCK_ZONE(zone); } +#endif isc_stats_t * dns_zone_getrequeststats(dns_zone_t *zone) { @@ -13693,6 +15044,20 @@ dns_zone_getrequeststats(dns_zone_t *zone) { return (NULL); } +#ifdef NEWSTATS +/* + * Return the received query stats bucket + * see note from dns_zone_getrequeststats() + */ +dns_stats_t * +dns_zone_getrcvquerystats(dns_zone_t *zone) { + if (zone->requeststats_on) + return (zone->rcvquerystats); + else + return (NULL); +} +#endif + void dns_zone_dialup(dns_zone_t *zone) { @@ -13705,7 +15070,7 @@ dns_zone_dialup(dns_zone_t *zone) { if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY)) dns_zone_notify(zone); - if (zone->type != dns_zone_master && + if (zone->type != dns_zone_master && zone->masters != NULL && DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH)) dns_zone_refresh(zone); } @@ -14321,8 +15686,12 @@ dnskey_sane(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, } /* Check existing DB for NSEC-only DNSKEY */ - if (!nseconly) - CHECK(dns_nsec_nseconly(db, ver, &nseconly)); + if (!nseconly) { + result = dns_nsec_nseconly(db, ver, &nseconly); + if (result == ISC_R_NOTFOUND) + result = ISC_R_SUCCESS; + CHECK(result); + } /* Check existing DB for NSEC3 */ if (!nsec3) @@ -14360,7 +15729,7 @@ clean_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, if (result != ISC_R_NOTFOUND) goto failure; - result = dns_nsec3param_deletechains(db, ver, zone, diff); + result = dns_nsec3param_deletechains(db, ver, zone, ISC_TRUE, diff); failure: if (node != NULL) @@ -14476,14 +15845,11 @@ zone_rekey(dns_zone_t *zone) { dns_rdatatype_none, 0, &keyset, &keysigs); if (result == ISC_R_SUCCESS) { ttl = keyset.ttl; - result = dns_dnssec_keylistfromrdataset(&zone->origin, dir, - mctx, &keyset, - &keysigs, &soasigs, - ISC_FALSE, ISC_FALSE, - &dnskeys); - /* Can't get keys for some reason; try again later. */ - if (result != ISC_R_SUCCESS) - goto trylater; + CHECK(dns_dnssec_keylistfromrdataset(&zone->origin, dir, + mctx, &keyset, + &keysigs, &soasigs, + ISC_FALSE, ISC_FALSE, + &dnskeys)); } else if (result != ISC_R_NOTFOUND) goto failure; @@ -14509,7 +15875,7 @@ zone_rekey(dns_zone_t *zone) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_rekey:" "couldn't update zone keys: %s", isc_result_totext(result)); - goto trylater; + goto failure; } /* @@ -14552,15 +15918,17 @@ zone_rekey(dns_zone_t *zone) { CHECK(add_signing_records(db, zone->privatetype, ver, &diff, ISC_TF(newalg || fullsign))); - CHECK(increment_soa_serial(db, ver, &diff, mctx)); + CHECK(update_soa_serial(db, ver, &diff, mctx, + zone->updatemethod)); CHECK(add_chains(zone, db, ver, &diff)); CHECK(sign_apex(zone, db, ver, &diff, &zonediff)); - CHECK(zone_journal(zone, zonediff.diff, "zone_rekey")); + CHECK(zone_journal(zone, zonediff.diff, NULL, + "zone_rekey")); commit = ISC_TRUE; } } - dns_db_closeversion(db, &ver, commit); + dns_db_closeversion(db, &ver, ISC_TRUE); if (commit) { dns_difftuple_t *tuple; @@ -14673,6 +16041,13 @@ zone_rekey(dns_zone_t *zone) { } /* + * Activate any NSEC3 chain updates that may have + * been scheduled before this rekey. + */ + if (fullsign || newalg) + resume_addnsec3chain(zone); + + /* * Schedule the next resigning event */ set_resigntime(zone); @@ -14683,15 +16058,16 @@ zone_rekey(dns_zone_t *zone) { /* * If we're doing key maintenance, set the key refresh timer to - * the next scheduled key event or to one hour in the future, - * whichever is sooner. + * the next scheduled key event or to 'dnssec-loadkeys-interval' + * seconds in the future, whichever is sooner. */ if (DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_MAINTAIN)) { isc_time_t timethen; isc_stdtime_t then; LOCK_ZONE(zone); - DNS_ZONE_TIME_ADD(&timenow, HOUR, &timethen); + DNS_ZONE_TIME_ADD(&timenow, zone->refreshkeyinterval, + &timethen); zone->refreshkeytime = timethen; UNLOCK_ZONE(zone); @@ -14718,7 +16094,7 @@ zone_rekey(dns_zone_t *zone) { dns_zone_log(zone, ISC_LOG_INFO, "next key event: %s", timebuf); } - failure: + done: dns_diff_clear(&diff); dns_diff_clear(&_sig_diff); @@ -14740,10 +16116,14 @@ zone_rekey(dns_zone_t *zone) { dns_db_detach(&db); return; - trylater: - isc_interval_set(&ival, HOUR, 0); + failure: + /* + * Something went wrong; try again in ten minutes or + * after a key refresh interval, whichever is shorter. + */ + isc_interval_set(&ival, ISC_MIN(zone->refreshkeyinterval, 600), 0); isc_time_nowplusinterval(&zone->refreshkeytime, &ival); - goto failure; + goto done; } void @@ -14802,10 +16182,572 @@ dns_zone_dlzpostload(dns_zone_t *zone, dns_db_t *db) { isc_time_t loadtime; isc_result_t result; + TIME_NOW(&loadtime); + /* + * Lock hierarchy: zmgr, zone, raw. + */ LOCK_ZONE(zone); + if (inline_secure(zone)) + LOCK_ZONE(zone->raw); result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS); + if (inline_secure(zone)) + UNLOCK_ZONE(zone->raw); UNLOCK_ZONE(zone); return result; } + +isc_result_t +dns_zone_setrefreshkeyinterval(dns_zone_t *zone, isc_uint32_t interval) { + REQUIRE(DNS_ZONE_VALID(zone)); + if (interval == 0) + return (ISC_R_RANGE); + /* Maximum value: 24 hours (3600 minutes) */ + if (interval > (24 * 60)) + interval = (24 * 60); + /* Multiply by 60 for seconds */ + zone->refreshkeyinterval = interval * 60; + return (ISC_R_SUCCESS); +} + +void +dns_zone_setrequestixfr(dns_zone_t *zone, isc_boolean_t flag) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->requestixfr = flag; +} + +isc_boolean_t +dns_zone_getrequestixfr(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->requestixfr); +} + +void +dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->updatemethod = method; +} + +dns_updatemethod_t +dns_zone_getserialupdatemethod(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return(zone->updatemethod); +} + +/* + * Lock hierarchy: zmgr, zone, raw. + */ +isc_result_t +dns_zone_link(dns_zone_t *zone, dns_zone_t *raw) { + isc_result_t result; + dns_zonemgr_t *zmgr; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(zone->zmgr != NULL); + REQUIRE(zone->task != NULL); + REQUIRE(zone->loadtask != NULL); + REQUIRE(zone->raw == NULL); + + REQUIRE(DNS_ZONE_VALID(raw)); + REQUIRE(raw->zmgr == NULL); + REQUIRE(raw->task == NULL); + REQUIRE(raw->loadtask == NULL); + REQUIRE(raw->secure == NULL); + + /* + * Lock hierarchy: zmgr, zone, raw. + */ + zmgr = zone->zmgr; + RWLOCK(&zmgr->rwlock, isc_rwlocktype_write); + LOCK_ZONE(zone); + LOCK_ZONE(raw); + + result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive, + NULL, NULL, zone->task, zone_timer, raw, + &raw->timer); + if (result != ISC_R_SUCCESS) + goto unlock; + + /* + * The timer "holds" a iref. + */ + raw->irefs++; + INSIST(raw->irefs != 0); + + + /* dns_zone_attach(raw, &zone->raw); */ + isc_refcount_increment(&raw->erefs, NULL); + zone->raw = raw; + + /* dns_zone_iattach(zone, &raw->secure); */ + zone_iattach(zone, &raw->secure); + + isc_task_attach(zone->task, &raw->task); + isc_task_attach(zone->loadtask, &raw->loadtask); + + ISC_LIST_APPEND(zmgr->zones, raw, link); + raw->zmgr = zmgr; + zmgr->refs++; + + unlock: + UNLOCK_ZONE(raw); + UNLOCK_ZONE(zone); + RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write); + return (result); +} + +void +dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(raw != NULL && *raw == NULL); + + LOCK(&zone->lock); + if (zone->raw != NULL) + dns_zone_attach(zone->raw, raw); + UNLOCK(&zone->lock); +} + +struct keydone { + isc_event_t event; + isc_boolean_t all; + unsigned char data[5]; +}; + +#define PENDINGFLAGS (DNS_NSEC3FLAG_CREATE|DNS_NSEC3FLAG_INITIAL) + +static void +keydone(isc_task_t *task, isc_event_t *event) { + const char *me = "keydone"; + isc_boolean_t commit = ISC_FALSE; + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_dbversion_t *oldver = NULL, *newver = NULL; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_diff_t diff; + struct keydone *keydone = (struct keydone *)event; + dns_update_log_t log = { update_log_cb, NULL }; + isc_boolean_t clear_pending = ISC_FALSE; + + UNUSED(task); + + zone = event->ev_arg; + INSIST(DNS_ZONE_VALID(zone)); + + ENTER; + + dns_rdataset_init(&rdataset); + dns_diff_init(zone->mctx, &diff); + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "keydone:dns_db_newversion -> %s", + dns_result_totext(result)); + goto failure; + } + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (db == NULL) + goto failure; + + result = dns_db_getoriginnode(db, &node); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_findrdataset(db, node, newver, zone->privatetype, + dns_rdatatype_none, 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); + goto failure; + } + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); + goto failure; + } + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + isc_boolean_t found = ISC_FALSE; + + dns_rdataset_current(&rdataset, &rdata); + + if (keydone->all) { + if (rdata.length == 5 && rdata.data[0] != 0 && + rdata.data[3] == 0 && rdata.data[4] == 1) + found = ISC_TRUE; + else if (rdata.data[0] == 0 && + (rdata.data[2] & PENDINGFLAGS) != 0) { + found = ISC_TRUE; + clear_pending = ISC_TRUE; + } + } else if (rdata.length == 5 && + memcmp(rdata.data, keydone->data, 5) == 0) + found = ISC_TRUE; + + if (found) + CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_DEL, + &zone->origin, rdataset.ttl, + &rdata)); + dns_rdata_reset(&rdata); + } + + if (!ISC_LIST_EMPTY(diff.tuples)) { + /* Write changes to journal file. */ + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + + result = dns_update_signatures(&log, zone, db, + oldver, newver, &diff, + zone->sigvalidityinterval); + if (!clear_pending) + CHECK(result); + + CHECK(zone_journal(zone, &diff, NULL, "keydone")); + commit = ISC_TRUE; + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); + zone_needdump(zone, 30); + UNLOCK_ZONE(zone); + } + + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (db != NULL) { + if (node != NULL) + dns_db_detachnode(db, &node); + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, commit); + dns_db_detach(&db); + } + dns_diff_clear(&diff); + isc_event_free(&event); + dns_zone_idetach(&zone); +} + +isc_result_t +dns_zone_keydone(dns_zone_t *zone, const char *keystr) { + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *e; + isc_buffer_t b; + dns_zone_t *dummy = NULL; + struct keydone *kd; + + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + + e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_KEYDONE, keydone, + zone, sizeof(struct keydone)); + if (e == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + kd = (struct keydone *) e; + if (strcasecmp(keystr, "all") == 0) + kd->all = ISC_TRUE; + else { + isc_textregion_t r; + char *algstr; + dns_keytag_t keyid; + dns_secalg_t alg; + size_t n; + + kd->all = ISC_FALSE; + + n = sscanf(keystr, "%hd/", &keyid); + if (n == 0U) + CHECK(ISC_R_FAILURE); + + algstr = strchr(keystr, '/'); + if (algstr != NULL) + algstr++; + else + CHECK(ISC_R_FAILURE); + + n = sscanf(algstr, "%hhd", &alg); + if (n == 0U) { + DE_CONST(algstr, r.base); + r.length = strlen(algstr); + CHECK(dns_secalg_fromtext(&alg, &r)); + } + + /* construct a private-type rdata */ + isc_buffer_init(&b, kd->data, sizeof(kd->data)); + isc_buffer_putuint8(&b, alg); + isc_buffer_putuint8(&b, (keyid & 0xff00) >> 8); + isc_buffer_putuint8(&b, (keyid & 0xff)); + isc_buffer_putuint8(&b, 0); + isc_buffer_putuint8(&b, 1); + } + + zone_iattach(zone, &dummy); + isc_task_send(zone->task, &e); + + failure: + if (e != NULL) + isc_event_free(&e); + UNLOCK_ZONE(zone); + return (result); +} + +struct nsec3param { + isc_event_t event; + unsigned char data[DNS_NSEC3PARAM_BUFFERSIZE + 1]; + unsigned int length; + isc_boolean_t nsec; + isc_boolean_t replace; +}; + +static void +setnsec3param(isc_task_t *task, isc_event_t *event) { + const char *me = "setnsec3param"; + isc_boolean_t commit = ISC_FALSE; + isc_result_t result; + dns_dbversion_t *oldver = NULL, *newver = NULL; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t prdataset, nrdataset; + dns_diff_t diff; + struct nsec3param *np = (struct nsec3param *)event; + dns_update_log_t log = { update_log_cb, NULL }; + dns_rdata_t rdata; + isc_boolean_t nseconly; + isc_boolean_t exists = ISC_FALSE; + + UNUSED(task); + + zone = event->ev_arg; + INSIST(DNS_ZONE_VALID(zone)); + + ENTER; + + dns_rdataset_init(&prdataset); + dns_rdataset_init(&nrdataset); + dns_diff_init(zone->mctx, &diff); + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "setnsec3param:dns_db_newversion -> %s", + dns_result_totext(result)); + goto failure; + } + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (db == NULL) + goto failure; + + CHECK(dns_db_getoriginnode(db, &node)); + + /* + * Does a private-type record already exist for this chain? + */ + result = dns_db_findrdataset(db, node, newver, zone->privatetype, + dns_rdatatype_none, 0, &prdataset, NULL); + if (result == ISC_R_SUCCESS) { + for (result = dns_rdataset_first(&prdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&prdataset)) { + dns_rdata_init(&rdata); + dns_rdataset_current(&prdataset, &rdata); + + if (np->length == rdata.length && + memcmp(rdata.data, np->data, np->length) == 0) { + exists = ISC_TRUE; + break; + } + } + } else if (result != ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&prdataset)); + goto failure; + } + + /* + * Does the chain already exist? + */ + result = dns_db_findrdataset(db, node, newver, + dns_rdatatype_nsec3param, + dns_rdatatype_none, 0, &nrdataset, NULL); + if (result == ISC_R_SUCCESS) { + for (result = dns_rdataset_first(&nrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&nrdataset)) { + dns_rdata_init(&rdata); + dns_rdataset_current(&nrdataset, &rdata); + + if (np->length == (rdata.length + 1) && + memcmp(rdata.data, np->data + 1, + np->length - 1) == 0) + { + exists = ISC_TRUE; + break; + } + } + } else if (result != ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&nrdataset)); + goto failure; + } + + + /* + * We need to remove any existing NSEC3 chains. + */ + if (!exists && np->replace && (np->length != 0 || np->nsec)) + CHECK(dns_nsec3param_deletechains(db, newver, zone, + !np->nsec, &diff)); + + if (!exists && np->length != 0) { + /* + * We're creating an NSEC3 chain. + * + * If the zone is not currently capable of supporting + * an NSEC3 chain, add the INITIAL flag, so these + * parameters can be used later when NSEC3 becomes + * available. + */ + dns_rdata_init(&rdata); + + np->data[2] |= DNS_NSEC3FLAG_CREATE; + result = dns_nsec_nseconly(db, newver, &nseconly); + if (result == ISC_R_NOTFOUND || nseconly) + np->data[2] |= DNS_NSEC3FLAG_INITIAL; + + rdata.length = np->length; + rdata.data = np->data; + rdata.type = zone->privatetype; + rdata.rdclass = zone->rdclass; + CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_ADD, + &zone->origin, 0, &rdata)); + } + + if (!ISC_LIST_EMPTY(diff.tuples)) { + /* Write changes to journal file. */ + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + result = dns_update_signatures(&log, zone, db, + oldver, newver, &diff, + zone->sigvalidityinterval); + if (result != ISC_R_NOTFOUND) + CHECK(result); + CHECK(zone_journal(zone, &diff, NULL, "setnsec3param")); + commit = ISC_TRUE; + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); + zone_needdump(zone, 30); + UNLOCK_ZONE(zone); + } + + failure: + if (dns_rdataset_isassociated(&prdataset)) + dns_rdataset_disassociate(&prdataset); + if (dns_rdataset_isassociated(&nrdataset)) + dns_rdataset_disassociate(&nrdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, commit); + if (db != NULL) + dns_db_detach(&db); + if (commit) + resume_addnsec3chain(zone); + dns_diff_clear(&diff); + isc_event_free(&event); + dns_zone_idetach(&zone); +} + +isc_result_t +dns_zone_setnsec3param(dns_zone_t *zone, isc_uint8_t hash, isc_uint8_t flags, + isc_uint16_t iter, isc_uint8_t saltlen, + unsigned char *salt, isc_boolean_t replace) +{ + isc_result_t result = ISC_R_SUCCESS; + dns_rdata_nsec3param_t param; + dns_rdata_t nrdata = DNS_RDATA_INIT; + dns_rdata_t prdata = DNS_RDATA_INIT; + unsigned char nbuf[DNS_NSEC3PARAM_BUFFERSIZE]; + struct nsec3param *np; + dns_zone_t *dummy = NULL; + isc_buffer_t b; + isc_event_t *e; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(salt != NULL); + + LOCK_ZONE(zone); + + e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETNSEC3PARAM, + setnsec3param, zone, sizeof(struct nsec3param)); + if (e == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + np = (struct nsec3param *) e; + np->replace = replace; + if (hash == 0) { + np->length = 0; + np->nsec = ISC_TRUE; + } else { + param.common.rdclass = zone->rdclass; + param.common.rdtype = dns_rdatatype_nsec3param; + ISC_LINK_INIT(¶m.common, link); + param.mctx = NULL; + param.hash = hash; + param.flags = flags; + param.iterations = iter; + param.salt_length = saltlen; + param.salt = salt; + isc_buffer_init(&b, nbuf, sizeof(nbuf)); + CHECK(dns_rdata_fromstruct(&nrdata, zone->rdclass, + dns_rdatatype_nsec3param, + ¶m, &b)); + dns_nsec3param_toprivate(&nrdata, &prdata, zone->privatetype, + np->data, sizeof(np->data)); + np->length = prdata.length; + } + + zone_iattach(zone, &dummy); + isc_task_send(zone->task, &e); + + failure: + if (e != NULL) + isc_event_free(&e); + UNLOCK_ZONE(zone); + return (result); +} + +void +dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level) { + REQUIRE(DNS_ZONE_VALID(zone)); + + zone->statlevel = level; +} + +dns_zonestat_level_t +dns_zone_getstatlevel(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (zone->statlevel); +} |