/*
- * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004-2010 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2003 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or distribute this software for any
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: zone.c,v 1.483.36.6 2009/03/26 22:57:07 marka Exp $ */
+/* $Id: zone.c,v 1.540.2.26 2010/06/02 01:00:28 marka Exp $ */
/*! \file */
#include <dns/dnssec.h>
#include <dns/events.h>
#include <dns/journal.h>
+#include <dns/keydata.h>
+#include <dns/keytable.h>
#include <dns/keyvalues.h>
#include <dns/log.h>
#include <dns/master.h>
#include <dns/nsec.h>
#include <dns/nsec3.h>
#include <dns/peer.h>
+#include <dns/private.h>
+#include <dns/rbt.h>
#include <dns/rcode.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/request.h>
#include <dns/resolver.h>
#include <dns/result.h>
+#include <dns/rriterator.h>
#include <dns/soa.h>
#include <dns/ssu.h>
#include <dns/stats.h>
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;
#define DNS_ZONE_CHECKLOCK
#ifdef DNS_ZONE_CHECKLOCK
isc_time_t keywarntime;
isc_time_t signingtime;
isc_time_t nsec3chaintime;
- isc_uint32_t serial;
+ isc_time_t refreshkeytime;
+ isc_uint32_t refreshkeycount;
isc_uint32_t refresh;
isc_uint32_t retry;
isc_uint32_t expire;
/*%
* Statistics counters about zone management.
*/
- isc_stats_t *stats;
+ isc_stats_t *stats;
/*%
* Optional per-zone statistics counters. Counted outside of this
* module.
*/
- isc_boolean_t requeststats_on;
- isc_stats_t *requeststats;
+ isc_boolean_t requeststats_on;
+ isc_stats_t *requeststats;
isc_uint32_t notifydelay;
dns_isselffunc_t isself;
void *isselfarg;
isc_uint32_t signatures;
isc_uint32_t nodes;
dns_rdatatype_t privatetype;
+
+ /*%
+ * Autosigning/key-maintenance options
+ */
+ isc_uint32_t keyopts;
};
#define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0))
* from SOA (if not set, we
* are still using
* default timer values) */
-#define DNS_ZONEFLG_FORCEXFER 0x00008000U /*%< Force a zone xfer */
+#define DNS_ZONEFLG_FORCEXFER 0x00008000U /*%< Force a zone xfer */
#define DNS_ZONEFLG_NOREFRESH 0x00010000U
#define DNS_ZONEFLG_DIALNOTIFY 0x00020000U
#define DNS_ZONEFLG_DIALREFRESH 0x00040000U
#define DNS_ZONEFLG_USEALTXFRSRC 0x00800000U
#define DNS_ZONEFLG_SOABEFOREAXFR 0x01000000U
#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_NODELAY 0x20000000U
#define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0)
+#define DNS_ZONEKEY_OPTION(z,o) (((z)->keyopts & (o)) != 0)
/* Flags for zone_load() */
#define DNS_ZONELOADFLAG_NOSTAT 0x00000001U /* Do not stat() master files */
+#define DNS_ZONELOADFLAG_THAW 0x00000002U /* Thaw the zone on successful
+ load. */
#define UNREACH_CHACHE_SIZE 10U
#define UNREACH_HOLD_TIME 600 /* 10 minutes */
* DNSKEY as result of an update.
*/
struct dns_signing {
- unsigned int magic;
+ unsigned int magic;
dns_db_t *db;
dns_dbiterator_t *dbiterator;
dns_secalg_t algorithm;
};
struct dns_nsec3chain {
- unsigned int magic;
+ unsigned int magic;
dns_db_t *db;
dns_dbiterator_t *dbiterator;
dns_rdata_nsec3param_t nsec3param;
unsigned char salt[255];
isc_boolean_t done;
- isc_boolean_t seen_nsec;
- isc_boolean_t delete_nsec;
- isc_boolean_t save_delete_nsec;
+ isc_boolean_t seen_nsec;
+ isc_boolean_t delete_nsec;
+ isc_boolean_t save_delete_nsec;
ISC_LINK(dns_nsec3chain_t) link;
};
/*%<
* so it can be recovered in the event of a error.
*/
+struct dns_keyfetch {
+ dns_fixedname_t name;
+ dns_rdataset_t keydataset;
+ dns_rdataset_t dnskeyset;
+ dns_rdataset_t dnskeysigset;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ dns_fetch_t *fetch;
+};
+
+#define HOUR 3600
+#define DAY (24*HOUR)
+#define MONTH (30*DAY)
#define SEND_BUFFER_SIZE 2048
static void notify_log(dns_zone_t *zone, int level, const char *fmt, ...)
ISC_FORMAT_PRINTF(3, 4);
static void queue_xfrin(dns_zone_t *zone);
+static isc_result_t update_one_rr(dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff, dns_diffop_t op,
+ dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata);
static void zone_unload(dns_zone_t *zone);
static void zone_expire(dns_zone_t *zone);
static void zone_iattach(dns_zone_t *source, dns_zone_t **target);
isc_time_t *now);
static isc_result_t zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
isc_uint16_t keyid, isc_boolean_t delete);
+static isc_result_t delete_nsec(dns_db_t *db, dns_dbversion_t *ver,
+ dns_dbnode_t *node, dns_name_t *name,
+ dns_diff_t *diff);
+static void zone_rekey(dns_zone_t *zone);
#define ENTER zone_debuglog(zone, me, 1, "enter")
zone->type = dns_zone_none;
zone->flags = 0;
zone->options = 0;
+ zone->keyopts = 0;
zone->db_argc = 0;
zone->db_argv = NULL;
isc_time_settoepoch(&zone->expiretime);
isc_time_settoepoch(&zone->keywarntime);
isc_time_settoepoch(&zone->signingtime);
isc_time_settoepoch(&zone->nsec3chaintime);
- zone->serial = 0;
+ isc_time_settoepoch(&zone->refreshkeytime);
+ zone->refreshkeycount = 0;
zone->refresh = DNS_ZONE_DEFAULTREFRESH;
zone->retry = DNS_ZONE_DEFAULTRETRY;
zone->expire = 0;
UNLOCK_ZONE(zone);
}
-isc_uint32_t
-dns_zone_getserial(dns_zone_t *zone) {
- isc_uint32_t serial;
+isc_result_t
+dns_zone_getserial2(dns_zone_t *zone, isc_uint32_t *serialp) {
+ isc_result_t result;
REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(serialp != NULL);
LOCK_ZONE(zone);
- serial = zone->serial;
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ result = zone_get_from_db(zone, zone->db, NULL, NULL, serialp,
+ NULL, NULL, NULL, NULL, NULL);
+ } else
+ result = DNS_R_NOTLOADED;
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
UNLOCK_ZONE(zone);
+ return (result);
+}
+
+isc_uint32_t
+dns_zone_getserial(dns_zone_t *zone) {
+ isc_result_t result;
+ isc_uint32_t serial;
+
+ result = dns_zone_getserial2(zone, &serial);
+ if (result != ISC_R_SUCCESS)
+ serial = 0; /* XXX: not really correct, but no other choice */
+
return (serial);
}
* master file (if any) is written by the server, rather than being
* updated manually and read by the server.
*
- * This is true for slave zones, stub zones, and zones that allow
- * dynamic updates either by having an update policy ("ssutable")
+ * This is true for slave zones, stub zones, key zones, and zones that
+ * 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
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))));
INSIST(zone->type != dns_zone_none);
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADING)) {
- result = ISC_R_SUCCESS;
+ if ((flags & DNS_ZONELOADFLAG_THAW) != 0)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_THAW);
+ result = DNS_R_CONTINUE;
goto cleanup;
}
*/
if (zone->masterfile != NULL) {
/*
- * The file is already loaded. If we are just doing a
+ * The file is already loaded. If we are just doing a
* "rndc reconfig", we are done.
*/
if (!isc_time_isepoch(&zone->loadtime) &&
if (result == DNS_R_CONTINUE) {
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADING);
+ if ((flags & DNS_ZONELOADFLAG_THAW) != 0)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_THAW);
goto cleanup;
}
return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT));
}
+isc_result_t
+dns_zone_loadandthaw(dns_zone_t *zone) {
+ isc_result_t result;
+
+ 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 DNS_R_SEENINCLUDE:
+ zone->update_disabled = ISC_FALSE;
+ break;
+ case DNS_R_NOMASTERFILE:
+ zone->update_disabled = ISC_FALSE;
+ break;
+ default:
+ /* Error, remain in disabled state. */
+ break;
+ }
+ return (result);
+}
+
static unsigned int
get_master_options(dns_zone_t *zone) {
unsigned int options;
options = DNS_MASTER_ZONE;
if (zone->type == dns_zone_slave)
options |= DNS_MASTER_SLAVE;
+ if (zone->type == dns_zone_key)
+ options |= DNS_MASTER_KEY;
if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNS))
options |= DNS_MASTER_CHECKNS;
if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FATALNS))
dns_name_format(name, namebuf, sizeof namebuf);
if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
result == DNS_R_EMPTYNAME) {
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL))
+ level = ISC_LOG_WARNING;
dns_zone_log(zone, level,
"%s/MX '%s' has no address records (A or AAAA)",
ownerbuf, namebuf);
- /* XXX950 make fatal for 9.5.0. */
- return (ISC_TRUE);
+ return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE);
}
if (result == DNS_R_CNAME) {
return (answer);
}
+static isc_boolean_t
+zone_rrset_check_dup(dns_zone_t *zone, dns_name_t *owner,
+ dns_rdataset_t *rdataset)
+{
+ dns_rdataset_t tmprdataset;
+ isc_result_t result;
+ isc_boolean_t answer = ISC_TRUE;
+ isc_boolean_t format = ISC_TRUE;
+ int level = ISC_LOG_WARNING;
+ char ownerbuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ unsigned int count1 = 0;
+
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRRFAIL))
+ level = ISC_LOG_ERROR;
+
+ dns_rdataset_init(&tmprdataset);
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_t rdata1 = DNS_RDATA_INIT;
+ unsigned int count2 = 0;
+
+ count1++;
+ dns_rdataset_current(rdataset, &rdata1);
+ dns_rdataset_clone(rdataset, &tmprdataset);
+ for (result = dns_rdataset_first(&tmprdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&tmprdataset)) {
+ dns_rdata_t rdata2 = DNS_RDATA_INIT;
+ count2++;
+ if (count1 >= count2)
+ continue;
+ dns_rdataset_current(&tmprdataset, &rdata2);
+ if (dns_rdata_casecompare(&rdata1, &rdata2) == 0) {
+ if (format) {
+ dns_name_format(owner, ownerbuf,
+ sizeof ownerbuf);
+ dns_rdatatype_format(rdata1.type,
+ typebuf,
+ sizeof(typebuf));
+ format = ISC_FALSE;
+ }
+ dns_zone_log(zone, level, "%s/%s has "
+ "semantically identical records",
+ ownerbuf, typebuf);
+ if (level == ISC_LOG_ERROR)
+ answer = ISC_FALSE;
+ break;
+ }
+ }
+ dns_rdataset_disassociate(&tmprdataset);
+ if (!format)
+ break;
+ }
+ return (answer);
+}
+
+static isc_boolean_t
+zone_check_dup(dns_zone_t *zone, dns_db_t *db) {
+ dns_dbiterator_t *dbiterator = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ dns_rdataset_t rdataset;
+ dns_rdatasetiter_t *rdsit = NULL;
+ isc_boolean_t ok = ISC_TRUE;
+ isc_result_t result;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_createiterator(db, 0, &dbiterator);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_TRUE);
+
+ for (result = dns_dbiterator_first(dbiterator);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbiterator)) {
+ result = dns_dbiterator_current(dbiterator, &node, name);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ result = dns_db_allrdatasets(db, node, NULL, 0, &rdsit);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ for (result = dns_rdatasetiter_first(rdsit);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsit)) {
+ dns_rdatasetiter_current(rdsit, &rdataset);
+ if (!zone_rrset_check_dup(zone, name, &rdataset))
+ ok = ISC_FALSE;
+ dns_rdataset_disassociate(&rdataset);
+ }
+ dns_rdatasetiter_destroy(&rdsit);
+ dns_db_detachnode(db, &node);
+ }
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_dbiterator_destroy(&dbiterator);
+
+ return (ok);
+}
+
static isc_boolean_t
integrity_checks(dns_zone_t *zone, dns_db_t *db) {
dns_dbiterator_t *dbiterator = NULL;
result = dns_rdataset_next(&rdataset);
}
dns_rdataset_disassociate(&rdataset);
+ goto next;
checkmx:
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_mx,
/*
* OpenSSL verification of RSA keys with exponent 3 is known to be
- * broken prior OpenSSL 0.9.8c/0.9.7k. Look for such keys and warn
+ * broken prior OpenSSL 0.9.8c/0.9.7k. Look for such keys and warn
* if they are in use.
*/
static void
dns_db_detachnode(db, &node);
if (version != NULL)
dns_db_closeversion(db, &version, ISC_FALSE);
-
}
static void
zone->privatetype,
dns_rdatatype_none, 0,
&rdataset, NULL);
- if (result != ISC_R_SUCCESS)
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
goto cleanup;
+ }
for (result = dns_rdataset_first(&rdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&rdataset))
{
dns_rdataset_current(&rdataset, &rdata);
- if (rdata.length != 5 || rdata.data[4] != 0) {
+ if (rdata.length != 5 ||
+ rdata.data[0] == 0 || rdata.data[4] != 0) {
dns_rdata_reset(&rdata);
continue;
}
result = zone_signwithkey(zone, rdata.data[0],
- (rdata.data[1] << 8) | rdata.data[2], ISC_TF(rdata.data[3]));
+ (rdata.data[1] << 8) | rdata.data[2],
+ ISC_TF(rdata.data[3]));
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"zone_signwithkey failed: %s",
dns_db_detachnode(zone->db, &node);
if (version != NULL)
dns_db_closeversion(zone->db, &version, ISC_FALSE);
-
}
static isc_result_t
isc_result_t result;
isc_time_t now;
unsigned int options = 0;
+ char saltbuf[255*2+1];
+ char flags[sizeof("REMOVE|CREATE|NONSEC|OPTOUT")];
+ int i;
nsec3chain = isc_mem_get(zone->mctx, sizeof *nsec3chain);
if (nsec3chain == NULL)
nsec3chain->delete_nsec = ISC_FALSE;
nsec3chain->save_delete_nsec = ISC_FALSE;
+ if (nsec3param->flags == 0)
+ strlcpy(flags, "NONE", sizeof(flags));
+ else {
+ flags[0] = '\0';
+ if (nsec3param->flags & DNS_NSEC3FLAG_REMOVE)
+ strlcat(flags, "REMOVE", sizeof(flags));
+ if (nsec3param->flags & DNS_NSEC3FLAG_CREATE) {
+ if (flags[0] == '\0')
+ strlcpy(flags, "CREATE", sizeof(flags));
+ else
+ strlcat(flags, "|CREATE", sizeof(flags));
+ }
+ if (nsec3param->flags & DNS_NSEC3FLAG_NONSEC) {
+ if (flags[0] == '\0')
+ strlcpy(flags, "NONSEC", sizeof(flags));
+ else
+ strlcat(flags, "|NONSEC", sizeof(flags));
+ }
+ if (nsec3param->flags & DNS_NSEC3FLAG_OPTOUT) {
+ if (flags[0] == '\0')
+ strlcpy(flags, "OPTOUT", sizeof(flags));
+ else
+ strlcat(flags, "|OPTOUT", sizeof(flags));
+ }
+ }
+ if (nsec3param->salt_length == 0)
+ strlcpy(saltbuf, "-", sizeof(saltbuf));
+ else
+ for (i = 0; i < nsec3param->salt_length; i++)
+ sprintf(&saltbuf[i*2], "%02X", nsec3chain->salt[i]);
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "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)) {
resume_addnsec3chain(dns_zone_t *zone) {
dns_dbnode_t *node = NULL;
dns_dbversion_t *version = NULL;
- dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdataset_t rdataset;
isc_result_t result;
dns_rdata_nsec3param_t nsec3param;
+ if (zone->privatetype == 0)
+ return;
+
result = dns_db_findnode(zone->db, &zone->origin, ISC_FALSE, &node);
if (result != ISC_R_SUCCESS)
goto cleanup;
dns_db_currentversion(zone->db, &version);
dns_rdataset_init(&rdataset);
result = dns_db_findrdataset(zone->db, node, version,
- dns_rdatatype_nsec3param,
- dns_rdatatype_none, 0,
- &rdataset, NULL);
- if (result != ISC_R_SUCCESS)
+ zone->privatetype, dns_rdatatype_none,
+ 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
goto cleanup;
+ }
for (result = dns_rdataset_first(&rdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&rdataset))
{
- dns_rdataset_current(&rdataset, &rdata);
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_t private = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &private);
+ if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
+ sizeof(buf)))
+ continue;
result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 ||
dns_result_totext(result));
}
}
- dns_rdata_reset(&rdata);
}
dns_rdataset_disassociate(&rdataset);
-
cleanup:
if (node != NULL)
dns_db_detachnode(zone->db, &node);
set_resigntime(dns_zone_t *zone) {
dns_rdataset_t rdataset;
dns_fixedname_t fixed;
- char namebuf[DNS_NAME_FORMATSIZE];
unsigned int resign;
isc_result_t result;
isc_uint32_t nanosecs;
dns_rdataset_init(&rdataset);
dns_fixedname_init(&fixed);
- result = dns_db_getsigningtime(zone->db, &rdataset,
- dns_fixedname_name(&fixed));
+ result = dns_db_getsigningtime(zone->db, &rdataset,
+ dns_fixedname_name(&fixed));
if (result != ISC_R_SUCCESS) {
isc_time_settoepoch(&zone->resigntime);
return;
}
resign = rdataset.resign;
- dns_name_format(dns_fixedname_name(&fixed), namebuf, sizeof(namebuf));
dns_rdataset_disassociate(&rdataset);
isc_random_get(&nanosecs);
nanosecs %= 1000000000;
dns_rdatatype_nsec3param,
dns_rdatatype_none, 0, &rdataset, NULL);
if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
result = ISC_R_SUCCESS;
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
dns_zone_log(zone, ISC_LOG_ERROR,
"nsec3param lookup failure: %s",
dns_result_totext(result));
return (result);
}
+/*
+ * Set the timer for refreshing the key zone to the soonest future time
+ * of the set (current timer, keydata->refresh, keydata->addhd,
+ * keydata->removehd).
+ */
+static void
+set_refreshkeytimer(dns_zone_t *zone, dns_rdata_keydata_t *key,
+ isc_stdtime_t now) {
+ const char me[] = "set_refreshkeytimer";
+ isc_stdtime_t then;
+ isc_time_t timenow, timethen;
+
+ ENTER;
+ then = key->refresh;
+ if (key->addhd > now && key->addhd < then)
+ then = key->addhd;
+ if (key->removehd > now && key->removehd < then)
+ then = key->removehd;
+
+ TIME_NOW(&timenow);
+ if (then > now)
+ DNS_ZONE_TIME_ADD(&timenow, then - now, &timethen);
+ else
+ timethen = timenow;
+ if (isc_time_compare(&zone->refreshkeytime, &timenow) < 0 ||
+ isc_time_compare(&timethen, &zone->refreshkeytime) < 0)
+ zone->refreshkeytime = timethen;
+ zone_settimer(zone, &timenow);
+}
+
+/*
+ * Convert key(s) linked from 'keynode' to KEYDATA and add to the key zone.
+ * If the key zone is changed, set '*changed' to ISC_TRUE.
+ */
static isc_result_t
-zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
- isc_result_t result)
+create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff, dns_keytable_t *keytable,
+ dns_keynode_t **keynodep, isc_boolean_t *changed)
{
- unsigned int soacount = 0;
- unsigned int nscount = 0;
- unsigned int errors = 0;
- isc_uint32_t serial, refresh, retry, expire, minimum;
- isc_time_t now;
- isc_boolean_t needdump = ISC_FALSE;
- isc_boolean_t hasinclude = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE);
- unsigned int options;
+ const char me[] = "create_keydata";
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_buffer_t keyb, dstb;
+ unsigned char key_buf[4096], dst_buf[DST_KEY_MAXSIZE];
+ dns_rdata_keydata_t keydata;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_keynode_t *keynode;
+ isc_stdtime_t now;
+ isc_region_t r;
+ dst_key_t *key;
- TIME_NOW(&now);
+ REQUIRE(keynodep != NULL);
+ keynode = *keynodep;
- /*
- * Initiate zone transfer? We may need a error code that
- * indicates that the "permanent" form does not exist.
- * XXX better error feedback to log.
- */
- if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
- if (zone->type == dns_zone_slave ||
- zone->type == dns_zone_stub) {
- if (result == ISC_R_FILENOTFOUND)
- dns_zone_log(zone, ISC_LOG_DEBUG(1),
- "no master file");
- else if (result != DNS_R_NOMASTERFILE)
- dns_zone_log(zone, ISC_LOG_ERROR,
- "loading from master file %s "
- "failed: %s",
- zone->masterfile,
- dns_result_totext(result));
- } else
- dns_zone_log(zone, ISC_LOG_ERROR,
- "loading from master file %s failed: %s",
- zone->masterfile,
- dns_result_totext(result));
- goto cleanup;
- }
+ ENTER;
+ isc_stdtime_get(&now);
- dns_zone_log(zone, ISC_LOG_DEBUG(2),
- "number of nodes in database: %u",
- dns_db_nodecount(db));
+ /* Loop in case there's more than one key. */
+ while (result == ISC_R_SUCCESS) {
+ dns_keynode_t *nextnode = NULL;
- if (result == DNS_R_SEENINCLUDE)
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HASINCLUDE);
- else
- DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_HASINCLUDE);
+ key = dns_keynode_key(keynode);
+ if (key == NULL)
+ goto skip;
- /*
- * Apply update log, if any, on initial load.
- */
- if (zone->journal != NULL &&
- ! DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOMERGE) &&
- ! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED))
- {
- if (zone->type == dns_zone_master &&
- (zone->update_acl != NULL || zone->ssutable != NULL))
- options = DNS_JOURNALOPT_RESIGN;
- else
- options = 0;
- result = dns_journal_rollforward(zone->mctx, db, options,
- zone->journal);
- if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND &&
- result != DNS_R_UPTODATE && result != DNS_R_NOJOURNAL &&
- result != ISC_R_RANGE) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "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,
- "journal rollforward failed: "
- "journal out of sync with zone");
- goto cleanup;
- }
- dns_zone_log(zone, ISC_LOG_DEBUG(1),
- "journal rollforward completed "
- "successfully: %s",
- dns_result_totext(result));
- if (result == ISC_R_SUCCESS)
- needdump = ISC_TRUE;
- }
+ isc_buffer_init(&dstb, dst_buf, sizeof(dst_buf));
+ CHECK(dst_key_todns(key, &dstb));
- zone->loadtime = loadtime;
+ /* Convert DST key to DNSKEY. */
+ dns_rdata_reset(&rdata);
+ isc_buffer_usedregion(&dstb, &r);
+ dns_rdata_fromregion(&rdata, dst_key_class(key),
+ dns_rdatatype_dnskey, &r);
- dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded");
- /*
- * Obtain ns, soa and cname counts for top of zone.
- */
- INSIST(db != NULL);
- result = zone_get_from_db(zone, db, &nscount, &soacount, &serial,
- &refresh, &retry, &expire, &minimum,
- &errors);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "could not find NS and/or SOA records");
+ /* DSTKEY to KEYDATA. */
+ CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL));
+ CHECK(dns_keydata_fromdnskey(&keydata, &dnskey, now, 0, 0,
+ NULL));
+
+ /* KEYDATA to rdata. */
+ dns_rdata_reset(&rdata);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ CHECK(dns_rdata_fromstruct(&rdata,
+ zone->rdclass, dns_rdatatype_keydata,
+ &keydata, &keyb));
+
+ /* Add rdata to zone. */
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD,
+ dst_key_name(key), 0, &rdata));
+ *changed = ISC_TRUE;
+
+ skip:
+ result = dns_keytable_nextkeynode(keytable, keynode, &nextnode);
+ if (result != ISC_R_NOTFOUND) {
+ dns_keytable_detachkeynode(keytable, &keynode);
+ keynode = nextnode;
+ }
}
- /*
- * Master / Slave / Stub zones require both NS and SOA records at
- * the top of the zone.
- */
+ /* Refresh new keys from the zone apex as soon as possible. */
+ if (*changed)
+ set_refreshkeytimer(zone, &keydata, now);
- switch (zone->type) {
- case dns_zone_master:
- case dns_zone_slave:
- case dns_zone_stub:
- if (soacount != 1) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "has %d SOA records", soacount);
- result = DNS_R_BADZONE;
- }
- if (nscount == 0) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "has no NS records");
- result = DNS_R_BADZONE;
- }
- if (result != ISC_R_SUCCESS)
- goto cleanup;
- if (zone->type == dns_zone_master && errors != 0) {
- result = DNS_R_BADZONE;
- goto cleanup;
- }
- if (zone->type != dns_zone_stub) {
- result = check_nsec3param(zone, db);
- if (result != ISC_R_SUCCESS)
- goto cleanup;
- }
- if (zone->type == dns_zone_master &&
- DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKINTEGRITY) &&
- !integrity_checks(zone, db)) {
- result = DNS_R_BADZONE;
- goto cleanup;
- }
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(keytable, &keynode);
+ *keynodep = NULL;
- if (zone->db != NULL) {
- /*
- * This is checked in zone_replacedb() for slave zones
- * as they don't reload from disk.
- */
- if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) &&
- !isc_serial_gt(serial, zone->serial)) {
- isc_uint32_t serialmin, serialmax;
+ return (ISC_R_SUCCESS);
- INSIST(zone->type == dns_zone_master);
+ failure:
+ return (result);
+}
- serialmin = (zone->serial + 1) & 0xffffffffU;
- serialmax = (zone->serial + 0x7fffffffU) &
- 0xffffffffU;
- dns_zone_log(zone, ISC_LOG_ERROR,
- "ixfr-from-differences: "
- "new serial (%u) out of range "
- "[%u - %u]", serial, serialmin,
- serialmax);
- result = DNS_R_BADZONE;
- goto cleanup;
- } else if (!isc_serial_ge(serial, zone->serial))
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone serial has gone backwards");
- else if (serial == zone->serial && !hasinclude)
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone serial unchanged. "
- "zone may fail to transfer "
- "to slaves.");
- }
+/*
+ * Remove from the key zone all the KEYDATA records found in rdataset.
+ */
+static isc_result_t
+delete_keydata(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ dns_name_t *name, dns_rdataset_t *rdataset)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result, uresult;
- if (zone->type == dns_zone_master &&
- (zone->update_acl != NULL || zone->ssutable != NULL) &&
- zone->sigresigninginterval < (3 * refresh) &&
- dns_db_issecure(db))
- {
- dns_zone_log(zone, ISC_LOG_WARNING,
- "sig-re-signing-interval less than "
- "3 * refresh.");
- }
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+ uresult = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
+ name, 0, &rdata);
+ if (uresult != ISC_R_SUCCESS)
+ return (uresult);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
- zone->serial = serial;
- zone->refresh = RANGE(refresh,
- zone->minrefresh, zone->maxrefresh);
- zone->retry = RANGE(retry,
- zone->minretry, zone->maxretry);
- zone->expire = RANGE(expire, zone->refresh + zone->retry,
- DNS_MAX_EXPIRE);
- zone->minimum = minimum;
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
+/*
+ * Compute the DNSSEC key ID for a DNSKEY record.
+ */
+static isc_result_t
+compute_tag(dns_name_t *name, dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx,
+ dns_keytag_t *tag)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char data[4096];
+ isc_buffer_t buffer;
+ dst_key_t *dstkey = NULL;
- if (zone->type == dns_zone_slave ||
- zone->type == dns_zone_stub) {
- isc_time_t t;
- isc_uint32_t delay;
+ isc_buffer_init(&buffer, data, sizeof(data));
+ dns_rdata_fromstruct(&rdata, dnskey->common.rdclass,
+ dns_rdatatype_dnskey, dnskey, &buffer);
- result = isc_file_getmodtime(zone->journal, &t);
- if (result != ISC_R_SUCCESS)
- result = isc_file_getmodtime(zone->masterfile,
- &t);
- if (result == ISC_R_SUCCESS)
- DNS_ZONE_TIME_ADD(&t, zone->expire,
- &zone->expiretime);
- else
- DNS_ZONE_TIME_ADD(&now, zone->retry,
- &zone->expiretime);
+ result = dns_dnssec_keyfromrdata(name, &rdata, mctx, &dstkey);
+ if (result == ISC_R_SUCCESS)
+ *tag = dst_key_id(dstkey);
+ dst_key_free(&dstkey);
- delay = isc_random_jitter(zone->retry,
- (zone->retry * 3) / 4);
- DNS_ZONE_TIME_ADD(&now, delay, &zone->refreshtime);
- if (isc_time_compare(&zone->refreshtime,
- &zone->expiretime) >= 0)
- zone->refreshtime = now;
- }
- break;
- default:
- UNEXPECTED_ERROR(__FILE__, __LINE__,
- "unexpected zone type %d", zone->type);
- result = ISC_R_UNEXPECTED;
- goto cleanup;
- }
+ return (result);
+}
- /*
- * Check for weak DNSKEY's.
- */
- if (zone->type == dns_zone_master)
- zone_check_dnskeys(zone, db);
+/*
+ * Add key to the security roots for all views.
+ */
+static void
+trust_key(dns_viewlist_t *viewlist, dns_name_t *keyname,
+ dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char data[4096];
+ isc_buffer_t buffer;
+ dns_view_t *view;
+ dns_keytable_t *sr = NULL;
-#if 0
- /* destroy notification example. */
- {
- isc_event_t *e = isc_event_allocate(zone->mctx, NULL,
- DNS_EVENT_DBDESTROYED,
- dns_zonemgr_dbdestroyed,
- zone,
- sizeof(isc_event_t));
- dns_db_ondestroy(db, zone->task, &e);
- }
-#endif
+ /* Convert dnskey to DST key. */
+ isc_buffer_init(&buffer, data, sizeof(data));
+ dns_rdata_fromstruct(&rdata, dnskey->common.rdclass,
+ dns_rdatatype_dnskey, dnskey, &buffer);
- ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
- if (zone->db != NULL) {
- result = zone_replacedb(zone, db, ISC_FALSE);
- ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ for (view = ISC_LIST_HEAD(*viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ dst_key_t *key = NULL;
+
+ result = dns_view_getsecroots(view, &sr);
if (result != ISC_R_SUCCESS)
- goto cleanup;
- } else {
- zone_attachdb(zone, db);
- ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
- DNS_ZONE_SETFLAG(zone,
- DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY);
- }
- result = ISC_R_SUCCESS;
- if (needdump)
- zone_needdump(zone, DNS_DUMP_DELAY);
- if (zone->task != NULL) {
- if (zone->type == dns_zone_master) {
- set_resigntime(zone);
- resume_signingwithkey(zone);
- resume_addnsec3chain(zone);
- }
- zone_settimer(zone, &now);
- }
+ continue;
- if (! dns_db_ispersistent(db))
- dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s",
- zone->serial,
- dns_db_issecure(db) ? " (signed)" : "");
+ CHECK(dns_dnssec_keyfromrdata(keyname, &rdata, mctx, &key));
+ CHECK(dns_keytable_add(sr, ISC_TRUE, &key));
+ dns_keytable_detach(&sr);
+ }
- return (result);
+ failure:
+ if (sr != NULL)
+ dns_keytable_detach(&sr);
+ return;
+}
- cleanup:
- if (zone->type == dns_zone_slave ||
- zone->type == dns_zone_stub) {
- if (zone->journal != NULL)
- zone_saveunique(zone, zone->journal, "jn-XXXXXXXX");
- if (zone->masterfile != NULL)
- zone_saveunique(zone, zone->masterfile, "db-XXXXXXXX");
+/*
+ * Remove key from the security roots for all views.
+ */
+static void
+untrust_key(dns_viewlist_t *viewlist, dns_name_t *keyname, isc_mem_t *mctx,
+ dns_rdata_dnskey_t *dnskey)
+{
+ dns_view_t *view;
- /* Mark the zone for immediate refresh. */
- zone->refreshtime = now;
- if (zone->task != NULL)
- zone_settimer(zone, &now);
- result = ISC_R_SUCCESS;
- }
- return (result);
+ for (view = ISC_LIST_HEAD(*viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ dns_view_untrust(view, keyname, dnskey, mctx);
}
-static isc_boolean_t
-exit_check(dns_zone_t *zone) {
+/*
+ * Add a null key to the security roots for all views, so that all queries
+ * to the zone will fail.
+ */
+static void
+fail_secure(dns_viewlist_t *viewlist, dns_name_t *keyname) {
+ isc_result_t result;
+ dns_view_t *view;
- REQUIRE(LOCKED_ZONE(zone));
+ for (view = ISC_LIST_HEAD(*viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ dns_keytable_t *sr = NULL;
- if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SHUTDOWN) &&
- zone->irefs == 0)
- {
- /*
- * DNS_ZONEFLG_SHUTDOWN can only be set if erefs == 0.
- */
- INSIST(isc_refcount_current(&zone->erefs) == 0);
- return (ISC_TRUE);
+ result = dns_view_getsecroots(view, &sr);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ dns_keytable_marksecure(sr, keyname);
+ dns_keytable_detach(&sr);
}
- return (ISC_FALSE);
}
-static isc_boolean_t
-zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name) {
+/*
+ * Scan a set of KEYDATA records from the key zone. The ones that are
+ * valid (i.e., the add holddown timer has expired) become trusted keys for
+ * all views.
+ */
+static void
+load_secroots(dns_zone_t *zone, dns_name_t *name, dns_rdataset_t *rdataset) {
isc_result_t result;
- char namebuf[DNS_NAME_FORMATSIZE];
- char altbuf[DNS_NAME_FORMATSIZE];
- dns_fixedname_t fixed;
- dns_name_t *foundname;
- int level;
-
- if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOCHECKNS))
- return (ISC_TRUE);
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t keydata;
+ dns_rdata_dnskey_t dnskey;
+ isc_mem_t *mctx = zone->mctx;
+ dns_view_t *view = zone->view;
+ dns_viewlist_t *viewlist = view->viewlist;
+ int trusted = 0, revoked = 0, pending = 0;
+ isc_stdtime_t now;
- if (zone->type == dns_zone_master)
- level = ISC_LOG_ERROR;
- else
- level = ISC_LOG_WARNING;
+ isc_stdtime_get(&now);
- dns_fixedname_init(&fixed);
- foundname = dns_fixedname_name(&fixed);
+ /* For each view, delete references to this key from secroots. */
+ for (view = ISC_LIST_HEAD(*viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ dns_keytable_t *sr = NULL;
- result = dns_db_find(db, name, NULL, dns_rdatatype_a,
- 0, 0, NULL, foundname, NULL, NULL);
- if (result == ISC_R_SUCCESS)
- return (ISC_TRUE);
+ result = dns_view_getsecroots(view, &sr);
+ if (result != ISC_R_SUCCESS)
+ continue;
- if (result == DNS_R_NXRRSET) {
- result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
- 0, 0, NULL, foundname, NULL, NULL);
- if (result == ISC_R_SUCCESS)
- return (ISC_TRUE);
+ dns_keytable_delete(sr, name);
+ dns_keytable_detach(&sr);
}
- dns_name_format(name, namebuf, sizeof namebuf);
- if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
- result == DNS_R_EMPTYNAME) {
- dns_zone_log(zone, level,
- "NS '%s' has no address records (A or AAAA)",
- namebuf);
- /* XXX950 Make fatal ISC_FALSE for 9.5.0. */
- return (ISC_TRUE);
- }
+ /* Now insert all the accepted trust anchors from this keydata set. */
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
- if (result == DNS_R_CNAME) {
- dns_zone_log(zone, level, "NS '%s' is a CNAME (illegal)",
- namebuf);
- /* XXX950 Make fatal ISC_FALSE for 9.5.0. */
- return (ISC_TRUE);
- }
+ /* Convert rdata to keydata. */
+ dns_rdata_tostruct(&rdata, &keydata, NULL);
- if (result == DNS_R_DNAME) {
- dns_name_format(foundname, altbuf, sizeof altbuf);
- dns_zone_log(zone, level,
- "NS '%s' is below a DNAME '%s' (illegal)",
- namebuf, altbuf);
- /* XXX950 Make fatal ISC_FALSE for 9.5.0. */
- return (ISC_TRUE);
+ /* Set the key refresh timer. */
+ set_refreshkeytimer(zone, &keydata, now);
+
+ /* If the removal timer is nonzero, this key was revoked. */
+ if (keydata.removehd != 0) {
+ revoked++;
+ continue;
+ }
+
+ /*
+ * If the add timer is still pending, this key is not
+ * trusted yet.
+ */
+ if (now < keydata.addhd) {
+ pending++;
+ continue;
+ }
+
+ /* Convert keydata to dnskey. */
+ dns_keydata_todnskey(&keydata, &dnskey, NULL);
+
+ /* Add to keytables. */
+ trusted++;
+ trust_key(viewlist, name, &dnskey, mctx);
}
- return (ISC_TRUE);
+ if (trusted == 0 && pending != 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "No valid trust anchors for '%s'!", namebuf);
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "%d key(s) revoked, %d still pending",
+ revoked, pending);
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "All queries to '%s' will fail", namebuf);
+ fail_secure(viewlist, name);
+ }
}
static isc_result_t
-zone_count_ns_rr(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node,
- dns_dbversion_t *version, unsigned int *nscount,
- unsigned int *errors)
+do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
{
+ dns_diff_t temp_diff;
isc_result_t result;
- unsigned int count = 0;
- unsigned int ecount = 0;
- dns_rdataset_t rdataset;
- dns_rdata_t rdata;
- dns_rdata_ns_t ns;
- dns_rdataset_init(&rdataset);
- result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns,
- dns_rdatatype_none, 0, &rdataset, NULL);
- if (result == ISC_R_NOTFOUND)
- goto success;
- if (result != ISC_R_SUCCESS)
- goto invalidate_rdataset;
+ /*
+ * Create a singleton diff.
+ */
+ dns_diff_init(diff->mctx, &temp_diff);
+ temp_diff.resign = diff->resign;
+ ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
- result = dns_rdataset_first(&rdataset);
- while (result == ISC_R_SUCCESS) {
- if (errors != NULL && zone->rdclass == dns_rdataclass_in &&
- (zone->type == dns_zone_master ||
- zone->type == dns_zone_slave)) {
- dns_rdata_init(&rdata);
- dns_rdataset_current(&rdataset, &rdata);
- result = dns_rdata_tostruct(&rdata, &ns, NULL);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- if (dns_name_issubdomain(&ns.name, &zone->origin) &&
- !zone_check_ns(zone, db, &ns.name))
- ecount++;
- }
- count++;
- result = dns_rdataset_next(&rdataset);
+ /*
+ * Apply it to the database.
+ */
+ result = dns_diff_apply(&temp_diff, db, ver);
+ ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
+ if (result != ISC_R_SUCCESS) {
+ dns_difftuple_free(tuple);
+ return (result);
}
- dns_rdataset_disassociate(&rdataset);
- success:
- if (nscount != NULL)
- *nscount = count;
- if (errors != NULL)
- *errors = ecount;
-
- result = ISC_R_SUCCESS;
-
- invalidate_rdataset:
- dns_rdataset_invalidate(&rdataset);
+ /*
+ * Merge it into the current pending journal entry.
+ */
+ dns_diff_appendminimal(diff, tuple);
- return (result);
+ /*
+ * Do not clear temp_diff.
+ */
+ return (ISC_R_SUCCESS);
}
static isc_result_t
-zone_load_soa_rr(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
- unsigned int *soacount,
- isc_uint32_t *serial, isc_uint32_t *refresh,
- isc_uint32_t *retry, isc_uint32_t *expire,
- isc_uint32_t *minimum)
+update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata)
{
+ dns_difftuple_t *tuple = NULL;
isc_result_t result;
- unsigned int count;
- dns_rdataset_t rdataset;
- dns_rdata_t rdata = DNS_RDATA_INIT;
- dns_rdata_soa_t soa;
-
- dns_rdataset_init(&rdataset);
- result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa,
- dns_rdatatype_none, 0, &rdataset, NULL);
- if (result == ISC_R_NOTFOUND) {
- if (soacount != NULL)
- *soacount = 0;
- if (serial != NULL)
- *serial = 0;
- if (refresh != NULL)
- *refresh = 0;
- if (retry != NULL)
- *retry = 0;
- if (expire != NULL)
- *expire = 0;
- if (minimum != NULL)
- *minimum = 0;
- result = ISC_R_SUCCESS;
- goto invalidate_rdataset;
- }
+ result = dns_difftuple_create(diff->mctx, op,
+ name, ttl, rdata, &tuple);
if (result != ISC_R_SUCCESS)
- goto invalidate_rdataset;
+ return (result);
+ return (do_one_tuple(&tuple, db, ver, diff));
+}
- count = 0;
- result = dns_rdataset_first(&rdataset);
- while (result == ISC_R_SUCCESS) {
- dns_rdata_init(&rdata);
- dns_rdataset_current(&rdataset, &rdata);
- count++;
- if (count == 1) {
- result = dns_rdata_tostruct(&rdata, &soa, NULL);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- }
+static isc_result_t
+increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff, isc_mem_t *mctx) {
+ dns_difftuple_t *deltuple = NULL;
+ dns_difftuple_t *addtuple = NULL;
+ isc_uint32_t serial;
+ isc_result_t result;
- result = dns_rdataset_next(&rdataset);
- dns_rdata_reset(&rdata);
- }
- dns_rdataset_disassociate(&rdataset);
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
+ CHECK(dns_difftuple_copy(deltuple, &addtuple));
+ addtuple->op = DNS_DIFFOP_ADD;
- if (soacount != NULL)
- *soacount = count;
+ serial = dns_soa_getserial(&addtuple->rdata);
- if (count > 0) {
- if (serial != NULL)
- *serial = soa.serial;
- if (refresh != NULL)
- *refresh = soa.refresh;
- if (retry != NULL)
- *retry = soa.retry;
- if (expire != NULL)
- *expire = soa.expire;
- if (minimum != NULL)
- *minimum = soa.minimum;
- }
+ /* RFC1982 */
+ serial = (serial + 1) & 0xFFFFFFFF;
+ if (serial == 0)
+ serial = 1;
+ dns_soa_setserial(serial, &addtuple->rdata);
+ CHECK(do_one_tuple(&deltuple, db, ver, diff));
+ CHECK(do_one_tuple(&addtuple, db, ver, diff));
result = ISC_R_SUCCESS;
- invalidate_rdataset:
- dns_rdataset_invalidate(&rdataset);
+ failure:
+ if (addtuple != NULL)
+ dns_difftuple_free(&addtuple);
+ if (deltuple != NULL)
+ dns_difftuple_free(&deltuple);
+ return (result);
+}
+
+/*
+ * 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) {
+ const char me[] = "zone_journal";
+ const char *journalfile;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_journal_t *journal = NULL;
+
+ ENTER;
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL) {
+ result = dns_journal_open(zone->mctx, journalfile,
+ ISC_TRUE, &journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "%s:dns_journal_open -> %s\n",
+ caller, dns_result_totext(result));
+ return (result);
+ }
+ 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\n",
+ caller, dns_result_totext(result));
+ return (result);
+ }
+ }
return (result);
}
/*
- * zone must be locked.
+ * Create an SOA record for a newly-created zone
*/
static isc_result_t
-zone_get_from_db(dns_zone_t *zone, dns_db_t *db, unsigned int *nscount,
- unsigned int *soacount, isc_uint32_t *serial,
- isc_uint32_t *refresh, isc_uint32_t *retry,
- isc_uint32_t *expire, isc_uint32_t *minimum,
- unsigned int *errors)
-{
- dns_dbversion_t *version;
+add_soa(dns_zone_t *zone, dns_db_t *db) {
isc_result_t result;
- isc_result_t answer = ISC_R_SUCCESS;
- dns_dbnode_t *node;
-
- REQUIRE(db != NULL);
- REQUIRE(zone != NULL);
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char buf[DNS_SOA_BUFFERSIZE];
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
- version = NULL;
- dns_db_currentversion(db, &version);
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "creating SOA");
- node = NULL;
- result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node);
+ dns_diff_init(zone->mctx, &diff);
+ result = dns_db_newversion(db, &ver);
if (result != ISC_R_SUCCESS) {
- answer = result;
- goto closeversion;
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "add_soa:dns_db_newversion -> %s\n",
+ dns_result_totext(result));
+ goto failure;
}
- if (nscount != NULL || errors != NULL) {
- result = zone_count_ns_rr(zone, db, node, version,
- nscount, errors);
- if (result != ISC_R_SUCCESS)
- answer = result;
+ /* Build SOA record */
+ result = dns_soa_buildrdata(&zone->origin, dns_rootname, zone->rdclass,
+ 0, 0, 0, 0, 0, buf, &rdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "add_soa:dns_soa_buildrdata -> %s\n",
+ dns_result_totext(result));
+ goto failure;
}
- if (soacount != NULL || serial != NULL || refresh != NULL
- || retry != NULL || expire != NULL || minimum != NULL) {
- result = zone_load_soa_rr(db, node, version, soacount,
- serial, refresh, retry, expire,
- minimum);
- if (result != ISC_R_SUCCESS)
- answer = result;
- }
+ result = update_one_rr(db, ver, &diff, DNS_DIFFOP_ADD,
+ &zone->origin, 0, &rdata);
- dns_db_detachnode(db, &node);
- closeversion:
- dns_db_closeversion(db, &version, ISC_FALSE);
+failure:
+ dns_diff_clear(&diff);
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, ISC_TF(result == ISC_R_SUCCESS));
- return (answer);
+ return (result);
}
-void
-dns_zone_attach(dns_zone_t *source, dns_zone_t **target) {
- REQUIRE(DNS_ZONE_VALID(source));
- REQUIRE(target != NULL && *target == NULL);
- isc_refcount_increment(&source->erefs, NULL);
- *target = source;
-}
+/*
+ * Synchronize the set of initializing keys found in managed-keys {}
+ * statements with the set of trust anchors found in the managed-keys.bind
+ * zone. If a domain is no longer named in managed-keys, delete all keys
+ * from that domain from the key zone. If a domain is mentioned in in
+ * managed-keys but there are no references to it in the key zone, load
+ * the key zone with the initializing key(s) for that domain.
+ */
+static isc_result_t
+sync_keyzone(dns_zone_t *zone, dns_db_t *db) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_boolean_t changed = ISC_FALSE;
+ dns_rbtnodechain_t chain;
+ dns_fixedname_t fn;
+ dns_name_t foundname, *origin;
+ dns_keynode_t *keynode = NULL;
+ dns_view_t *view = zone->view;
+ dns_keytable_t *sr = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+ dns_rriterator_t rrit;
-void
-dns_zone_detach(dns_zone_t **zonep) {
- dns_zone_t *zone;
- unsigned int refs;
- isc_boolean_t free_now = ISC_FALSE;
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "synchronizing trusted keys");
- REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+ dns_name_init(&foundname, NULL);
+ dns_fixedname_init(&fn);
+ origin = dns_fixedname_name(&fn);
- zone = *zonep;
+ dns_diff_init(zone->mctx, &diff);
- isc_refcount_decrement(&zone->erefs, &refs);
+ CHECK(dns_view_getsecroots(view, &sr));
- if (refs == 0) {
- LOCK_ZONE(zone);
- /*
- * We just detached the last external reference.
- */
- if (zone->task != NULL) {
- /*
- * This zone is being managed. Post
- * its control event and let it clean
- * up synchronously in the context of
- * its task.
- */
- isc_event_t *ev = &zone->ctlevent;
- isc_task_send(zone->task, &ev);
- } else {
- /*
- * This zone is not being managed; it has
- * no task and can have no outstanding
- * events. Free it immediately.
- */
- /*
- * Unmanaged zones should not have non-null views;
- * we have no way of detaching from the view here
- * without causing deadlock because this code is called
- * with the view already locked.
- */
- INSIST(zone->view == NULL);
- free_now = ISC_TRUE;
- }
- UNLOCK_ZONE(zone);
+ result = dns_db_newversion(db, &ver);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sync_keyzone:dns_db_newversion -> %s\n",
+ dns_result_totext(result));
+ goto failure;
}
- *zonep = NULL;
- if (free_now)
- zone_free(zone);
-}
-
-void
-dns_zone_iattach(dns_zone_t *source, dns_zone_t **target) {
- REQUIRE(DNS_ZONE_VALID(source));
- REQUIRE(target != NULL && *target == NULL);
- LOCK_ZONE(source);
- zone_iattach(source, target);
- UNLOCK_ZONE(source);
-}
-
-static void
-zone_iattach(dns_zone_t *source, dns_zone_t **target) {
/*
- * 'source' locked by caller.
+ * Walk the zone DB. If we find any keys whose names are no longer
+ * in managed-keys (or *are* in trusted-keys, meaning they are
+ * permanent and not RFC5011-maintained), delete them from the
+ * zone. Otherwise call load_secroots(), which loads keys into
+ * secroots as appropriate.
*/
- REQUIRE(LOCKED_ZONE(source));
- REQUIRE(DNS_ZONE_VALID(source));
- REQUIRE(target != NULL && *target == NULL);
- INSIST(source->irefs + isc_refcount_current(&source->erefs) > 0);
- source->irefs++;
- INSIST(source->irefs != 0);
- *target = source;
-}
+ dns_rriterator_init(&rrit, db, ver, 0);
+ for (result = dns_rriterator_first(&rrit);
+ result == ISC_R_SUCCESS;
+ result = dns_rriterator_nextrrset(&rrit)) {
+ dns_rdataset_t *rdataset;
+ dns_name_t *rrname = NULL;
+ isc_uint32_t ttl;
+
+ dns_rriterator_current(&rrit, &rrname, &ttl,
+ &rdataset, NULL);
+ if (!dns_rdataset_isassociated(rdataset)) {
+ dns_rriterator_destroy(&rrit);
+ goto failure;
+ }
-static void
-zone_idetach(dns_zone_t **zonep) {
- dns_zone_t *zone;
+ if (rdataset->type != dns_rdatatype_keydata)
+ continue;
+
+ result = dns_keytable_find(sr, rrname, &keynode);
+ if ((result != ISC_R_SUCCESS &&
+ result != DNS_R_PARTIALMATCH) ||
+ dns_keynode_managed(keynode) == ISC_FALSE) {
+ CHECK(delete_keydata(db, ver, &diff,
+ rrname, rdataset));
+ changed = ISC_TRUE;
+ } else {
+ load_secroots(zone, rrname, rdataset);
+ }
+
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(sr, &keynode);
+ }
+ dns_rriterator_destroy(&rrit);
/*
- * 'zone' locked by caller.
+ * Now walk secroots to find any managed keys that aren't
+ * in the zone. If we find any, we add them to the zone.
*/
- REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
- zone = *zonep;
- REQUIRE(LOCKED_ZONE(*zonep));
- *zonep = NULL;
-
- INSIST(zone->irefs > 0);
- zone->irefs--;
- INSIST(zone->irefs + isc_refcount_current(&zone->erefs) > 0);
-}
+ RWLOCK(&sr->rwlock, isc_rwlocktype_write);
+ dns_rbtnodechain_init(&chain, zone->mctx);
+ result = dns_rbtnodechain_first(&chain, sr->table, &foundname, origin);
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_NOMORE;
+ while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ dns_rbtnode_t *rbtnode = NULL;
+
+ dns_rbtnodechain_current(&chain, &foundname, origin, &rbtnode);
+ if (rbtnode->data == NULL)
+ goto skip;
+
+ dns_keytable_attachkeynode(sr, rbtnode->data, &keynode);
+ if (dns_keynode_managed(keynode)) {
+ dns_fixedname_t fname;
+ dns_name_t *keyname;
+ dst_key_t *key;
+ key = dns_keynode_key(keynode);
+ dns_fixedname_init(&fname);
+
+ if (key == NULL) /* fail_secure() was called. */
+ goto skip;
+
+ keyname = dst_key_name(key);
+ result = dns_db_find(db, keyname, ver,
+ dns_rdatatype_keydata,
+ DNS_DBFIND_NOWILD, 0, NULL,
+ dns_fixedname_name(&fname),
+ NULL, NULL);
+ if (result != ISC_R_SUCCESS)
+ result = create_keydata(zone, db, ver, &diff,
+ sr, &keynode, &changed);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ skip:
+ result = dns_rbtnodechain_next(&chain, &foundname, origin);
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(sr, &keynode);
+ }
+ RWUNLOCK(&sr->rwlock, isc_rwlocktype_write);
-void
-dns_zone_idetach(dns_zone_t **zonep) {
- dns_zone_t *zone;
- isc_boolean_t free_needed;
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
- REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
- zone = *zonep;
- *zonep = NULL;
+ if (changed) {
+ /* Write changes to journal file. */
+ result = increment_soa_serial(db, ver, &diff, zone->mctx);
+ if (result == ISC_R_SUCCESS)
+ zone_journal(zone, &diff, "sync_keyzone");
- LOCK_ZONE(zone);
- INSIST(zone->irefs > 0);
- zone->irefs--;
- free_needed = exit_check(zone);
- UNLOCK_ZONE(zone);
- if (free_needed)
- zone_free(zone);
-}
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ }
-isc_mem_t *
-dns_zone_getmctx(dns_zone_t *zone) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ failure:
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(sr, &keynode);
+ if (sr != NULL)
+ dns_keytable_detach(&sr);
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, changed);
+ dns_diff_clear(&diff);
- return (zone->mctx);
+ return (result);
}
-dns_zonemgr_t *
-dns_zone_getmgr(dns_zone_t *zone) {
- REQUIRE(DNS_ZONE_VALID(zone));
+static isc_result_t
+zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
+ isc_result_t result)
+{
+ unsigned int soacount = 0;
+ unsigned int nscount = 0;
+ unsigned int errors = 0;
+ isc_uint32_t serial, oldserial, refresh, retry, expire, minimum;
+ isc_time_t now;
+ isc_boolean_t needdump = ISC_FALSE;
+ isc_boolean_t hasinclude = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE);
+ isc_boolean_t nomaster = ISC_FALSE;
+ unsigned int options;
- return (zone->zmgr);
-}
+ TIME_NOW(&now);
-void
-dns_zone_setflag(dns_zone_t *zone, unsigned int flags, isc_boolean_t value) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ /*
+ * Initiate zone transfer? We may need a error code that
+ * indicates that the "permanent" form does not exist.
+ * XXX better error feedback to log.
+ */
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub) {
+ if (result == ISC_R_FILENOTFOUND)
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "no master file");
+ else if (result != DNS_R_NOMASTERFILE)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "loading from master file %s "
+ "failed: %s",
+ zone->masterfile,
+ dns_result_totext(result));
+ } else {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "loading from master file %s failed: %s",
+ zone->masterfile,
+ dns_result_totext(result));
+ nomaster = ISC_TRUE;
+ }
- LOCK_ZONE(zone);
- if (value)
- DNS_ZONE_SETFLAG(zone, flags);
- else
- DNS_ZONE_CLRFLAG(zone, flags);
- UNLOCK_ZONE(zone);
-}
+ if (zone->type != dns_zone_key)
+ goto cleanup;
+ }
-void
-dns_zone_setoption(dns_zone_t *zone, unsigned int option, isc_boolean_t value)
-{
- REQUIRE(DNS_ZONE_VALID(zone));
+ dns_zone_log(zone, ISC_LOG_DEBUG(2),
+ "number of nodes in database: %u",
+ dns_db_nodecount(db));
- LOCK_ZONE(zone);
- if (value)
- zone->options |= option;
+ if (result == DNS_R_SEENINCLUDE)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HASINCLUDE);
else
- zone->options &= ~option;
- UNLOCK_ZONE(zone);
-}
-
-unsigned int
-dns_zone_getoptions(dns_zone_t *zone) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_HASINCLUDE);
- REQUIRE(DNS_ZONE_VALID(zone));
+ /*
+ * If there's no master file for a key zone, then the zone is new:
+ * create an SOA record. (We do this now, instead of later, so that
+ * if there happens to be a journal file, we can roll forward from
+ * a sane starting point.)
+ */
+ if (nomaster && zone->type == dns_zone_key) {
+ result = add_soa(zone, db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
- return (zone->options);
-}
+ /*
+ * Apply update log, if any, on initial load.
+ */
+ if (zone->journal != NULL &&
+ ! DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOMERGE) &&
+ ! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED))
+ {
+ if (zone->type == dns_zone_master &&
+ (zone->update_acl != NULL || zone->ssutable != NULL))
+ options = DNS_JOURNALOPT_RESIGN;
+ else
+ options = 0;
+ result = dns_journal_rollforward2(zone->mctx, db, options,
+ zone->sigresigninginterval,
+ zone->journal);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND &&
+ result != DNS_R_UPTODATE && result != DNS_R_NOJOURNAL &&
+ result != ISC_R_RANGE) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "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,
+ "journal rollforward failed: "
+ "journal out of sync with zone");
+ goto cleanup;
+ }
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "journal rollforward completed "
+ "successfully: %s",
+ dns_result_totext(result));
+ if (result == ISC_R_SUCCESS)
+ needdump = ISC_TRUE;
+ }
-isc_result_t
-dns_zone_setxfrsource4(dns_zone_t *zone, const isc_sockaddr_t *xfrsource) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ zone->loadtime = loadtime;
- LOCK_ZONE(zone);
- zone->xfrsource4 = *xfrsource;
- UNLOCK_ZONE(zone);
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded");
+ /*
+ * Obtain ns, soa and cname counts for top of zone.
+ */
+ INSIST(db != NULL);
+ result = zone_get_from_db(zone, db, &nscount, &soacount, &serial,
+ &refresh, &retry, &expire, &minimum,
+ &errors);
+ if (result != ISC_R_SUCCESS && zone->type != dns_zone_key) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "could not find NS and/or SOA records");
+ }
- return (ISC_R_SUCCESS);
-}
+ /*
+ * Master / Slave / Stub zones require both NS and SOA records at
+ * the top of the zone.
+ */
-isc_sockaddr_t *
-dns_zone_getxfrsource4(dns_zone_t *zone) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ switch (zone->type) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ case dns_zone_stub:
+ if (soacount != 1) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "has %d SOA records", soacount);
+ result = DNS_R_BADZONE;
+ }
+ if (nscount == 0) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "has no NS records");
+ result = DNS_R_BADZONE;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (zone->type == dns_zone_master && errors != 0) {
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ }
+ if (zone->type != dns_zone_stub) {
+ result = check_nsec3param(zone, db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ if (zone->type == dns_zone_master &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKINTEGRITY) &&
+ !integrity_checks(zone, db)) {
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ }
+
+ if (zone->type == dns_zone_master &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRR) &&
+ !zone_check_dup(zone, db)) {
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ }
+
+ if (zone->db != NULL) {
+ /*
+ * This is checked in zone_replacedb() for slave zones
+ * as they don't reload from disk.
+ */
+ result = zone_get_from_db(zone, zone->db, NULL, NULL,
+ &oldserial, NULL, NULL, NULL,
+ NULL, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) &&
+ !isc_serial_gt(serial, oldserial)) {
+ isc_uint32_t serialmin, serialmax;
+
+ INSIST(zone->type == dns_zone_master);
+
+ serialmin = (oldserial + 1) & 0xffffffffU;
+ serialmax = (oldserial + 0x7fffffffU) &
+ 0xffffffffU;
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "ixfr-from-differences: "
+ "new serial (%u) out of range "
+ "[%u - %u]", serial, serialmin,
+ serialmax);
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ } else if (!isc_serial_ge(serial, oldserial))
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone serial (%u/%u) has gone "
+ "backwards", serial, oldserial);
+ else if (serial == oldserial && !hasinclude)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone serial (%u) unchanged. "
+ "zone may fail to transfer "
+ "to slaves.", serial);
+ }
+
+ if (zone->type == dns_zone_master &&
+ (zone->update_acl != NULL || zone->ssutable != NULL) &&
+ zone->sigresigninginterval < (3 * refresh) &&
+ dns_db_issecure(db))
+ {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "sig-re-signing-interval less than "
+ "3 * refresh.");
+ }
+
+ zone->refresh = RANGE(refresh,
+ zone->minrefresh, zone->maxrefresh);
+ zone->retry = RANGE(retry,
+ zone->minretry, zone->maxretry);
+ zone->expire = RANGE(expire, zone->refresh + zone->retry,
+ DNS_MAX_EXPIRE);
+ zone->minimum = minimum;
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
+
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub) {
+ isc_time_t t;
+ isc_uint32_t delay;
+
+ result = isc_file_getmodtime(zone->journal, &t);
+ if (result != ISC_R_SUCCESS)
+ result = isc_file_getmodtime(zone->masterfile,
+ &t);
+ if (result == ISC_R_SUCCESS)
+ DNS_ZONE_TIME_ADD(&t, zone->expire,
+ &zone->expiretime);
+ else
+ DNS_ZONE_TIME_ADD(&now, zone->retry,
+ &zone->expiretime);
+
+ delay = isc_random_jitter(zone->retry,
+ (zone->retry * 3) / 4);
+ DNS_ZONE_TIME_ADD(&now, delay, &zone->refreshtime);
+ if (isc_time_compare(&zone->refreshtime,
+ &zone->expiretime) >= 0)
+ zone->refreshtime = now;
+ }
+ break;
+
+ case dns_zone_key:
+ result = sync_keyzone(zone, db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ break;
+
+ default:
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "unexpected zone type %d", zone->type);
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+
+ /*
+ * Check for weak DNSKEY's.
+ */
+ if (zone->type == dns_zone_master)
+ zone_check_dnskeys(zone, db);
+
+ /*
+ * Schedule DNSSEC key refresh.
+ */
+ if (zone->type == dns_zone_master &&
+ DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_MAINTAIN))
+ zone->refreshkeytime = now;
+
+#if 0
+ /* destroy notification example. */
+ {
+ isc_event_t *e = isc_event_allocate(zone->mctx, NULL,
+ DNS_EVENT_DBDESTROYED,
+ dns_zonemgr_dbdestroyed,
+ zone,
+ sizeof(isc_event_t));
+ dns_db_ondestroy(db, zone->task, &e);
+ }
+#endif
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ if (zone->db != NULL) {
+ result = zone_replacedb(zone, db, ISC_FALSE);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else {
+ zone_attachdb(zone, db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ DNS_ZONE_SETFLAG(zone,
+ DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY);
+ }
+
+ result = ISC_R_SUCCESS;
+
+ if (needdump) {
+ if (zone->type == dns_zone_key)
+ zone_needdump(zone, 30);
+ else
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ }
+
+ if (zone->task != NULL) {
+ if (zone->type == dns_zone_master) {
+ set_resigntime(zone);
+ resume_signingwithkey(zone);
+ resume_addnsec3chain(zone);
+ }
+ zone_settimer(zone, &now);
+ }
+
+ if (! dns_db_ispersistent(db))
+ dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s", serial,
+ dns_db_issecure(db) ? " (DNSSEC signed)" : "");
+
+ return (result);
+
+ cleanup:
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub ||
+ zone->type == dns_zone_key) {
+ if (zone->journal != NULL)
+ zone_saveunique(zone, zone->journal, "jn-XXXXXXXX");
+ if (zone->masterfile != NULL)
+ zone_saveunique(zone, zone->masterfile, "db-XXXXXXXX");
+
+ /* Mark the zone for immediate refresh. */
+ zone->refreshtime = now;
+ 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.");
+ return (result);
+}
+
+static isc_boolean_t
+exit_check(dns_zone_t *zone) {
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SHUTDOWN) &&
+ zone->irefs == 0)
+ {
+ /*
+ * DNS_ZONEFLG_SHUTDOWN can only be set if erefs == 0.
+ */
+ INSIST(isc_refcount_current(&zone->erefs) == 0);
+ return (ISC_TRUE);
+ }
+ return (ISC_FALSE);
+}
+
+static isc_boolean_t
+zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
+ isc_boolean_t logit)
+{
+ isc_result_t result;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char altbuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fixed;
+ dns_name_t *foundname;
+ int level;
+
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOCHECKNS))
+ return (ISC_TRUE);
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+
+ if (result == DNS_R_NXRRSET) {
+ result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+ }
+
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
+ result == DNS_R_EMPTYNAME) {
+ if (logit) {
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_zone_log(zone, level, "NS '%s' has no address "
+ "records (A or AAAA)", namebuf);
+ }
+ return (ISC_FALSE);
+ }
+
+ if (result == DNS_R_CNAME) {
+ if (logit) {
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_zone_log(zone, level, "NS '%s' is a CNAME "
+ "(illegal)", namebuf);
+ }
+ return (ISC_FALSE);
+ }
+
+ if (result == DNS_R_DNAME) {
+ if (logit) {
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ dns_zone_log(zone, level, "NS '%s' is below a DNAME "
+ "'%s' (illegal)", namebuf, altbuf);
+ }
+ return (ISC_FALSE);
+ }
+
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+zone_count_ns_rr(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, unsigned int *nscount,
+ unsigned int *errors, isc_boolean_t logit)
+{
+ isc_result_t result;
+ unsigned int count = 0;
+ unsigned int ecount = 0;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata;
+ dns_rdata_ns_t ns;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto success;
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto invalidate_rdataset;
+ }
+
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ if (errors != NULL && zone->rdclass == dns_rdataclass_in &&
+ (zone->type == dns_zone_master ||
+ zone->type == dns_zone_slave)) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dns_name_issubdomain(&ns.name, &zone->origin) &&
+ !zone_check_ns(zone, db, &ns.name, logit))
+ ecount++;
+ }
+ count++;
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ success:
+ if (nscount != NULL)
+ *nscount = count;
+ if (errors != NULL)
+ *errors = ecount;
+
+ result = ISC_R_SUCCESS;
+
+ invalidate_rdataset:
+ dns_rdataset_invalidate(&rdataset);
+
+ return (result);
+}
+
+static isc_result_t
+zone_load_soa_rr(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ unsigned int *soacount,
+ isc_uint32_t *serial, isc_uint32_t *refresh,
+ isc_uint32_t *retry, isc_uint32_t *expire,
+ isc_uint32_t *minimum)
+{
+ isc_result_t result;
+ unsigned int count;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_soa_t soa;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ if (soacount != NULL)
+ *soacount = 0;
+ if (serial != NULL)
+ *serial = 0;
+ if (refresh != NULL)
+ *refresh = 0;
+ if (retry != NULL)
+ *retry = 0;
+ if (expire != NULL)
+ *expire = 0;
+ if (minimum != NULL)
+ *minimum = 0;
+ result = ISC_R_SUCCESS;
+ goto invalidate_rdataset;
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto invalidate_rdataset;
+ }
+
+ count = 0;
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ count++;
+ if (count == 1) {
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ result = dns_rdataset_next(&rdataset);
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ if (soacount != NULL)
+ *soacount = count;
+
+ if (count > 0) {
+ if (serial != NULL)
+ *serial = soa.serial;
+ if (refresh != NULL)
+ *refresh = soa.refresh;
+ if (retry != NULL)
+ *retry = soa.retry;
+ if (expire != NULL)
+ *expire = soa.expire;
+ if (minimum != NULL)
+ *minimum = soa.minimum;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ invalidate_rdataset:
+ dns_rdataset_invalidate(&rdataset);
+
+ return (result);
+}
+
+/*
+ * zone must be locked.
+ */
+static isc_result_t
+zone_get_from_db(dns_zone_t *zone, dns_db_t *db, unsigned int *nscount,
+ unsigned int *soacount, isc_uint32_t *serial,
+ isc_uint32_t *refresh, isc_uint32_t *retry,
+ isc_uint32_t *expire, isc_uint32_t *minimum,
+ unsigned int *errors)
+{
+ isc_result_t result;
+ isc_result_t answer = ISC_R_SUCCESS;
+ dns_dbversion_t *version = NULL;
+ dns_dbnode_t *node;
+
+ REQUIRE(db != NULL);
+ REQUIRE(zone != NULL);
+
+ dns_db_currentversion(db, &version);
+
+ node = NULL;
+ result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS) {
+ answer = result;
+ goto closeversion;
+ }
+
+ if (nscount != NULL || errors != NULL) {
+ result = zone_count_ns_rr(zone, db, node, version,
+ nscount, errors, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ answer = result;
+ }
+
+ if (soacount != NULL || serial != NULL || refresh != NULL
+ || retry != NULL || expire != NULL || minimum != NULL) {
+ result = zone_load_soa_rr(db, node, version, soacount,
+ serial, refresh, retry, expire,
+ minimum);
+ if (result != ISC_R_SUCCESS)
+ answer = result;
+ }
+
+ dns_db_detachnode(db, &node);
+ closeversion:
+ dns_db_closeversion(db, &version, ISC_FALSE);
+
+ return (answer);
+}
+
+void
+dns_zone_attach(dns_zone_t *source, dns_zone_t **target) {
+ REQUIRE(DNS_ZONE_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+ isc_refcount_increment(&source->erefs, NULL);
+ *target = source;
+}
+
+void
+dns_zone_detach(dns_zone_t **zonep) {
+ dns_zone_t *zone;
+ unsigned int refs;
+ isc_boolean_t free_now = ISC_FALSE;
+
+ REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+
+ zone = *zonep;
+
+ isc_refcount_decrement(&zone->erefs, &refs);
+
+ if (refs == 0) {
+ LOCK_ZONE(zone);
+ /*
+ * We just detached the last external reference.
+ */
+ if (zone->task != NULL) {
+ /*
+ * This zone is being managed. Post
+ * its control event and let it clean
+ * up synchronously in the context of
+ * its task.
+ */
+ isc_event_t *ev = &zone->ctlevent;
+ isc_task_send(zone->task, &ev);
+ } else {
+ /*
+ * This zone is not being managed; it has
+ * no task and can have no outstanding
+ * events. Free it immediately.
+ */
+ /*
+ * Unmanaged zones should not have non-null views;
+ * we have no way of detaching from the view here
+ * without causing deadlock because this code is called
+ * with the view already locked.
+ */
+ INSIST(zone->view == NULL);
+ free_now = ISC_TRUE;
+ }
+ UNLOCK_ZONE(zone);
+ }
+ *zonep = NULL;
+ if (free_now)
+ zone_free(zone);
+}
+
+void
+dns_zone_iattach(dns_zone_t *source, dns_zone_t **target) {
+ REQUIRE(DNS_ZONE_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+ LOCK_ZONE(source);
+ zone_iattach(source, target);
+ UNLOCK_ZONE(source);
+}
+
+static void
+zone_iattach(dns_zone_t *source, dns_zone_t **target) {
+
+ /*
+ * 'source' locked by caller.
+ */
+ REQUIRE(LOCKED_ZONE(source));
+ REQUIRE(DNS_ZONE_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+ INSIST(source->irefs + isc_refcount_current(&source->erefs) > 0);
+ source->irefs++;
+ INSIST(source->irefs != 0);
+ *target = source;
+}
+
+static void
+zone_idetach(dns_zone_t **zonep) {
+ dns_zone_t *zone;
+
+ /*
+ * 'zone' locked by caller.
+ */
+ REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+ zone = *zonep;
+ REQUIRE(LOCKED_ZONE(*zonep));
+ *zonep = NULL;
+
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ INSIST(zone->irefs + isc_refcount_current(&zone->erefs) > 0);
+}
+
+void
+dns_zone_idetach(dns_zone_t **zonep) {
+ dns_zone_t *zone;
+ isc_boolean_t free_needed;
+
+ REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+ zone = *zonep;
+ *zonep = NULL;
+
+ LOCK_ZONE(zone);
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ free_needed = exit_check(zone);
+ UNLOCK_ZONE(zone);
+ if (free_needed)
+ zone_free(zone);
+}
+
+isc_mem_t *
+dns_zone_getmctx(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->mctx);
+}
+
+dns_zonemgr_t *
+dns_zone_getmgr(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->zmgr);
+}
+
+void
+dns_zone_setflag(dns_zone_t *zone, unsigned int flags, isc_boolean_t value) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ DNS_ZONE_SETFLAG(zone, flags);
+ else
+ DNS_ZONE_CLRFLAG(zone, flags);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setoption(dns_zone_t *zone, unsigned int option, isc_boolean_t value)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ zone->options |= option;
+ else
+ zone->options &= ~option;
+ UNLOCK_ZONE(zone);
+}
+
+unsigned int
+dns_zone_getoptions(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->options);
+}
+
+void
+dns_zone_setkeyopt(dns_zone_t *zone, unsigned int keyopt, isc_boolean_t value)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ zone->keyopts |= keyopt;
+ else
+ zone->keyopts &= ~keyopt;
+ UNLOCK_ZONE(zone);
+}
+
+unsigned int
+dns_zone_getkeyopts(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->keyopts);
+}
+
+isc_result_t
+dns_zone_setxfrsource4(dns_zone_t *zone, const isc_sockaddr_t *xfrsource) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->xfrsource4 = *xfrsource;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_sockaddr_t *
+dns_zone_getxfrsource4(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
return (&zone->xfrsource4);
}
zone->mastersok = newok;
zone->masterkeynames = newname;
zone->masterscnt = count;
- DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOMASTERS);
-
- unlock:
- UNLOCK_ZONE(zone);
- return (result);
-}
-
-isc_result_t
-dns_zone_getdb(dns_zone_t *zone, dns_db_t **dpb) {
- isc_result_t result = ISC_R_SUCCESS;
-
- REQUIRE(DNS_ZONE_VALID(zone));
-
- ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
- if (zone->db == NULL)
- result = DNS_R_NOTLOADED;
- else
- dns_db_attach(zone->db, dpb);
- ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
-
- return (result);
-}
-
-/*
- * Co-ordinates the starting of routine jobs.
- */
-
-void
-dns_zone_maintenance(dns_zone_t *zone) {
- const char me[] = "dns_zone_maintenance";
- isc_time_t now;
-
- REQUIRE(DNS_ZONE_VALID(zone));
- ENTER;
-
- LOCK_ZONE(zone);
- TIME_NOW(&now);
- zone_settimer(zone, &now);
- UNLOCK_ZONE(zone);
-}
-
-static inline isc_boolean_t
-was_dumping(dns_zone_t *zone) {
- isc_boolean_t dumping;
-
- REQUIRE(LOCKED_ZONE(zone));
-
- dumping = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING);
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DUMPING);
- if (!dumping) {
- DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
- isc_time_settoepoch(&zone->dumptime);
- }
- return (dumping);
-}
-
-#define MAXZONEKEYS 10
-
-static isc_result_t
-do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
- dns_diff_t *diff)
-{
- dns_diff_t temp_diff;
- isc_result_t result;
-
- /*
- * Create a singleton diff.
- */
- dns_diff_init(diff->mctx, &temp_diff);
- temp_diff.resign = diff->resign;
- ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
-
- /*
- * Apply it to the database.
- */
- result = dns_diff_apply(&temp_diff, db, ver);
- ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
- if (result != ISC_R_SUCCESS) {
- dns_difftuple_free(tuple);
- return (result);
- }
-
- /*
- * Merge it into the current pending journal entry.
- */
- dns_diff_appendminimal(diff, tuple);
-
- /*
- * Do not clear temp_diff.
- */
- return (ISC_R_SUCCESS);
-}
-
-static isc_result_t
-increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver,
- dns_diff_t *diff, isc_mem_t *mctx)
-{
- dns_difftuple_t *deltuple = NULL;
- dns_difftuple_t *addtuple = NULL;
- isc_uint32_t serial;
- isc_result_t result;
-
- CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
- CHECK(dns_difftuple_copy(deltuple, &addtuple));
- addtuple->op = DNS_DIFFOP_ADD;
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOMASTERS);
- serial = dns_soa_getserial(&addtuple->rdata);
+ unlock:
+ UNLOCK_ZONE(zone);
+ return (result);
+}
- /* RFC1982 */
- serial = (serial + 1) & 0xFFFFFFFF;
- if (serial == 0)
- serial = 1;
+isc_result_t
+dns_zone_getdb(dns_zone_t *zone, dns_db_t **dpb) {
+ isc_result_t result = ISC_R_SUCCESS;
- dns_soa_setserial(serial, &addtuple->rdata);
- CHECK(do_one_tuple(&deltuple, db, ver, diff));
- CHECK(do_one_tuple(&addtuple, db, ver, diff));
- result = ISC_R_SUCCESS;
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db == NULL)
+ result = DNS_R_NOTLOADED;
+ else
+ dns_db_attach(zone->db, dpb);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
- failure:
- if (addtuple != NULL)
- dns_difftuple_free(&addtuple);
- if (deltuple != NULL)
- dns_difftuple_free(&deltuple);
return (result);
}
-static isc_result_t
-update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
- dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
- dns_rdata_t *rdata)
-{
- dns_difftuple_t *tuple = NULL;
- isc_result_t result;
- result = dns_difftuple_create(diff->mctx, op,
- name, ttl, rdata, &tuple);
- if (result != ISC_R_SUCCESS)
- return (result);
- return (do_one_tuple(&tuple, db, ver, diff));
+/*
+ * Co-ordinates the starting of routine jobs.
+ */
+
+void
+dns_zone_maintenance(dns_zone_t *zone) {
+ const char me[] = "dns_zone_maintenance";
+ isc_time_t now;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ ENTER;
+
+ LOCK_ZONE(zone);
+ TIME_NOW(&now);
+ zone_settimer(zone, &now);
+ UNLOCK_ZONE(zone);
}
-static isc_boolean_t
-ksk_sanity(dns_db_t *db, dns_dbversion_t *ver) {
- isc_boolean_t ret = ISC_FALSE;
- isc_boolean_t have_ksk = ISC_FALSE, have_nonksk = ISC_FALSE;
- isc_result_t result;
- dns_dbnode_t *node = NULL;
- dns_rdataset_t rdataset;
- dns_rdata_t rdata = DNS_RDATA_INIT;
- dns_rdata_dnskey_t dnskey;
+static inline isc_boolean_t
+was_dumping(dns_zone_t *zone) {
+ isc_boolean_t dumping;
- dns_rdataset_init(&rdataset);
- CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
- CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0,
- &rdataset, NULL));
- CHECK(dns_rdataset_first(&rdataset));
- while (result == ISC_R_SUCCESS && (!have_ksk || !have_nonksk)) {
- dns_rdataset_current(&rdataset, &rdata);
- CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL));
- if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
- == DNS_KEYOWNER_ZONE) {
- if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0)
- have_ksk = ISC_TRUE;
- else
- have_nonksk = ISC_TRUE;
- }
- dns_rdata_reset(&rdata);
- result = dns_rdataset_next(&rdataset);
+ REQUIRE(LOCKED_ZONE(zone));
+
+ dumping = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DUMPING);
+ if (!dumping) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
+ isc_time_settoepoch(&zone->dumptime);
}
- if (have_ksk && have_nonksk)
- ret = ISC_TRUE;
- failure:
- if (dns_rdataset_isassociated(&rdataset))
- dns_rdataset_disassociate(&rdataset);
- if (node != NULL)
- dns_db_detachnode(db, &node);
- return (ret);
+ return (dumping);
}
+#define MAXZONEKEYS 10
+
static isc_result_t
find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
isc_mem_t *mctx, unsigned int maxkeys,
(isc_stdtime_t) 0, &rdataset, NULL);
dns_db_detachnode(db, &node);
- if (result == ISC_R_NOTFOUND)
+ if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
return (ISC_R_SUCCESS);
- if (result != ISC_R_SUCCESS)
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
goto failure;
+ }
for (result = dns_rdataset_first(&rdataset);
result == ISC_R_SUCCESS;
if (type != dns_rdatatype_dnskey) {
result = update_one_rr(db, ver, diff,
- DNS_DIFFOP_DEL, name,
+ DNS_DIFFOP_DELRESIGN, name,
rdataset.ttl, &rdata);
dns_rdata_reset(&rdata);
if (result != ISC_R_SUCCESS)
break;
}
result = update_one_rr(db, ver, diff,
- DNS_DIFFOP_DEL,
+ DNS_DIFFOP_DELRESIGN,
name, rdataset.ttl,
&rdata);
break;
* delete the RRSIG.
*/
if (!found)
- result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
- name, rdataset.ttl, &rdata);
+ result = update_one_rr(db, ver, diff,
+ DNS_DIFFOP_DELRESIGN, name,
+ rdataset.ttl, &rdata);
dns_rdata_reset(&rdata);
if (result != ISC_R_SUCCESS)
break;
add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys,
unsigned int nkeys, isc_mem_t *mctx, isc_stdtime_t inception,
- isc_stdtime_t expire, isc_boolean_t check_ksk)
+ isc_stdtime_t expire, isc_boolean_t check_ksk,
+ isc_boolean_t keyset_kskonly)
{
isc_result_t result;
dns_dbnode_t *node = NULL;
dns_rdata_t sig_rdata = DNS_RDATA_INIT;
unsigned char data[1024]; /* XXX */
isc_buffer_t buffer;
- unsigned int i;
+ unsigned int i, j;
dns_rdataset_init(&rdataset);
isc_buffer_init(&buffer, data, sizeof(data));
result = dns_db_findrdataset(db, node, ver, type, 0,
(isc_stdtime_t) 0, &rdataset, NULL);
dns_db_detachnode(db, &node);
- if (result == ISC_R_NOTFOUND)
+ if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
return (ISC_R_SUCCESS);
- if (result != ISC_R_SUCCESS)
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
goto failure;
+ }
+
+#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)
+#define KSK(x) ((dst_key_flags(x) & DNS_KEYFLAG_KSK) != 0)
+#define ALG(x) dst_key_alg(x)
for (i = 0; i < nkeys; i++) {
- if (check_ksk && type != dns_rdatatype_dnskey &&
- (dst_key_flags(keys[i]) & DNS_KEYFLAG_KSK) != 0)
- continue;
+ isc_boolean_t both = ISC_FALSE;
+
if (!dst_key_isprivate(keys[i]))
continue;
+
+ if (check_ksk && !REVOKE(keys[i])) {
+ isc_boolean_t have_ksk, have_nonksk;
+ if (KSK(keys[i])) {
+ have_ksk = ISC_TRUE;
+ have_nonksk = ISC_FALSE;
+ } else {
+ have_ksk = ISC_FALSE;
+ have_nonksk = ISC_TRUE;
+ }
+ for (j = 0; j < nkeys; j++) {
+ if (j == i || ALG(keys[i]) != ALG(keys[j]))
+ continue;
+ if (REVOKE(keys[j]))
+ continue;
+ if (KSK(keys[j]))
+ have_ksk = ISC_TRUE;
+ else
+ have_nonksk = ISC_TRUE;
+ both = have_ksk && have_nonksk;
+ if (both)
+ break;
+ }
+ }
+ if (both) {
+ if (type == dns_rdatatype_dnskey) {
+ if (!KSK(keys[i]) && keyset_kskonly)
+ continue;
+ } else if (KSK(keys[i]))
+ continue;
+ } else if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey)
+ continue;
+
/* Calculate the signature, creating a RRSIG RDATA. */
+ isc_buffer_clear(&buffer);
CHECK(dns_dnssec_sign(name, &rdataset, keys[i],
&inception, &expire,
mctx, &buffer, &sig_rdata));
CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN,
name, rdataset.ttl, &sig_rdata));
dns_rdata_reset(&sig_rdata);
+ isc_buffer_init(&buffer, data, sizeof(data));
}
failure:
static void
zone_resigninc(dns_zone_t *zone) {
- const char *journalfile;
dns_db_t *db = NULL;
dns_dbversion_t *version = NULL;
dns_diff_t sig_diff;
dns_rdataset_t rdataset;
dns_rdatatype_t covers;
dst_key_t *zone_keys[MAXZONEKEYS];
- isc_boolean_t check_ksk;
+ isc_boolean_t check_ksk, keyset_kskonly = ISC_FALSE;
isc_result_t result;
isc_stdtime_t now, inception, soaexpire, expire, stop;
isc_uint32_t jitter;
stop = now + 5;
check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
- if (check_ksk)
- check_ksk = ksk_sanity(db, version);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
name = dns_fixedname_name(&fixed);
result = dns_db_getsigningtime(db, &rdataset, name);
}
result = add_sigs(db, version, name, covers, &sig_diff,
zone_keys, nkeys, zone->mctx, inception,
- expire, check_ksk);
+ expire, check_ksk, keyset_kskonly);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"zone_resigninc:add_sigs -> %s\n",
dns_result_totext(result));
break;
}
- result = dns_db_getsigningtime(db, &rdataset,
+ result = dns_db_getsigningtime(db, &rdataset,
dns_fixedname_name(&fixed));
if (nkeys == 0 && result == ISC_R_NOTFOUND) {
result = ISC_R_SUCCESS;
*/
result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
&sig_diff, zone_keys, nkeys, zone->mctx, inception,
- soaexpire, check_ksk);
+ soaexpire, check_ksk, keyset_kskonly);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"zone_resigninc:add_sigs -> %s\n",
goto failure;
}
- journalfile = dns_zone_getjournal(zone);
- if (journalfile != NULL) {
- dns_journal_t *journal = NULL;
- result = dns_journal_open(zone->mctx, journalfile,
- ISC_TRUE, &journal);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_resigninc:dns_journal_open -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
-
- result = dns_journal_write_transaction(journal, &sig_diff);
- dns_journal_destroy(&journal);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_resigninc:dns_journal_write_transaction -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
- }
+ /* Write changes to journal file. */
+ zone_journal(zone, &sig_diff, "zone_resigninc");
- /*
- * Everything has succeeded. Commit the changes.
- */
+ /* Everything has succeeded. Commit the changes. */
dns_db_closeversion(db, &version, ISC_TRUE);
failure:
return (result);
}
-static void
-set_bit(unsigned char *array, unsigned int index) {
- unsigned int shift, mask;
-
- shift = 7 - (index % 8);
- mask = 1 << shift;
-
- array[index / 8] |= mask;
-}
-
static isc_boolean_t
signed_with_key(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
dns_rdatatype_t type, dst_key_t *key)
dns_rdataset_init(&rdataset);
result = dns_db_findrdataset(db, node, version, dns_rdatatype_rrsig,
type, 0, &rdataset, NULL);
- if (result != ISC_R_SUCCESS)
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
return (ISC_FALSE);
+ }
for (result = dns_rdataset_first(&rdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&rdataset)) {
CHECK(next_active(db, version, name, next, bottom));
CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer,
&rdata));
- if (dns_name_equal(dns_db_origin(db), name)) {
- /*
- * Set the OPT bit to indicate that this is a
- * partially secure zone.
- */
- isc_region_t region;
-
- dns_rdata_toregion(&rdata, ®ion);
- dns_name_fromregion(next, ®ion);
- isc_region_consume(®ion, next->length);
- INSIST(region.length > (2 + dns_rdatatype_opt / 8) &&
- region.base[0] == 0 &&
- region.base[1] > dns_rdatatype_opt / 8);
- set_bit(region.base + 2, dns_rdatatype_opt);
- }
CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, ttl,
&rdata));
failure:
isc_boolean_t build_nsec, dst_key_t *key,
isc_stdtime_t inception, isc_stdtime_t expire,
unsigned int minimum, isc_boolean_t is_ksk,
- isc_boolean_t *delegation, dns_diff_t *diff,
- isc_int32_t *signatures, isc_mem_t *mctx)
+ isc_boolean_t keyset_kskonly, isc_boolean_t *delegation,
+ dns_diff_t *diff, isc_int32_t *signatures, isc_mem_t *mctx)
{
isc_result_t result;
dns_rdatasetiter_t *iterator = NULL;
result = ISC_R_SUCCESS;
return (result);
}
+
dns_rdataset_init(&rdataset);
isc_buffer_init(&buffer, data, sizeof(data));
seen_rr = seen_soa = seen_ns = seen_dname = seen_nsec =
seen_nsec = ISC_TRUE;
else if (rdataset.type == dns_rdatatype_nsec3)
seen_nsec3 = ISC_TRUE;
- seen_rr = ISC_TRUE;
+ if (rdataset.type != dns_rdatatype_rrsig)
+ seen_rr = ISC_TRUE;
dns_rdataset_disassociate(&rdataset);
}
if (result != ISC_R_NOMORE)
if (build_nsec && !seen_nsec3 && !seen_nsec && seen_rr) {
/* Build and add NSEC. */
bottom = (seen_ns && !seen_soa) || seen_dname;
- CHECK(add_nsec(db, version, name, node, minimum, bottom, diff));
- /* Count a NSEC generation as a signature generation. */
- (*signatures)--;
+ /*
+ * Build a NSEC record except at the origin.
+ */
+ if (!dns_name_equal(name, dns_db_origin(db))) {
+ CHECK(add_nsec(db, version, name, node, minimum,
+ bottom, diff));
+ /* Count a NSEC generation as a signature generation. */
+ (*signatures)--;
+ }
}
result = dns_rdatasetiter_first(iterator);
while (result == ISC_R_SUCCESS) {
if (rdataset.type == dns_rdatatype_soa ||
rdataset.type == dns_rdatatype_rrsig)
goto next_rdataset;
- if (is_ksk && rdataset.type != dns_rdatatype_dnskey)
+ if (rdataset.type == dns_rdatatype_dnskey) {
+ if (!is_ksk && keyset_kskonly)
+ goto next_rdataset;
+ } else if (is_ksk)
goto next_rdataset;
if (*delegation &&
rdataset.type != dns_rdatatype_ds &&
if (signed_with_key(db, node, version, rdataset.type, key))
goto next_rdataset;
/* Calculate the signature, creating a RRSIG RDATA. */
+ isc_buffer_clear(&buffer);
CHECK(dns_dnssec_sign(name, &rdataset, key, &inception,
&expire, mctx, &buffer, &rdata));
/* Update the database and journal with the RRSIG. */
result = ISC_R_SUCCESS;
if (seen_dname)
*delegation = ISC_TRUE;
-failure:
+ failure:
if (dns_rdataset_isassociated(&rdataset))
dns_rdataset_disassociate(&rdataset);
if (iterator != NULL)
return (result);
}
+/*
+ * If 'update_only' is set then don't create a NSEC RRset if it doesn't exist.
+ */
static isc_result_t
updatesecure(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
- dns_ttl_t minimum, isc_boolean_t *secureupdated, dns_diff_t *diff)
+ dns_ttl_t minimum, isc_boolean_t update_only, dns_diff_t *diff)
{
isc_result_t result;
- dns_rdata_t rdata = DNS_RDATA_INIT;
- unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE];
dns_rdataset_t rdataset;
- dns_rdata_nsec_t nsec;
dns_dbnode_t *node = NULL;
- /*
- * Check to see if the OPT bit has already been cleared.
- */
CHECK(dns_db_getoriginnode(db, &node));
- dns_rdataset_init(&rdataset);
- CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
- dns_rdatatype_none, 0, &rdataset, NULL));
- CHECK(dns_rdataset_first(&rdataset));
- dns_rdataset_current(&rdataset, &rdata);
-
- /*
- * Find the NEXT name for building the new record.
- */
- CHECK(dns_rdata_tostruct(&rdata, &nsec, NULL));
-
- /*
- * Delete the old NSEC record.
- */
- CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_DEL, name, minimum,
- &rdata));
- dns_rdata_reset(&rdata);
-
- /*
- * Add the new NSEC record.
- */
- CHECK(dns_nsec_buildrdata(db, version, node, &nsec.next, nsecbuffer,
- &rdata));
- CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, minimum,
- &rdata));
- dns_rdata_reset(&rdata);
-
- if (secureupdated != NULL)
- *secureupdated = ISC_TRUE;
-
+ if (update_only) {
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec,
+ dns_rdatatype_none,
+ 0, &rdataset, NULL);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+ CHECK(delete_nsec(db, version, node, name, diff));
+ CHECK(add_nsec(db, version, name, node, minimum, ISC_FALSE, diff));
+ success:
+ result = ISC_R_SUCCESS;
failure:
if (node != NULL)
dns_db_detachnode(db, &node);
- if (dns_rdataset_isassociated(&rdataset))
- dns_rdataset_disassociate(&rdataset);
return (result);
}
static isc_result_t
-updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version,
- dns_name_t *name, dns_rdatatype_t privatetype,
- dns_diff_t *diff)
+updatesignwithkey(dns_zone_t *zone, dns_signing_t *signing,
+ dns_dbversion_t *version, isc_boolean_t build_nsec3,
+ dns_ttl_t minimum, dns_diff_t *diff)
{
isc_result_t result;
dns_dbnode_t *node = NULL;
dns_rdata_t rdata = DNS_RDATA_INIT;
unsigned char data[5];
isc_boolean_t seen_done = ISC_FALSE;
+ isc_boolean_t have_rr = ISC_FALSE;
dns_rdataset_init(&rdataset);
result = dns_db_getoriginnode(signing->db, &node);
if (result != ISC_R_SUCCESS)
goto failure;
- result = dns_db_findrdataset(signing->db, node, version, privatetype,
- dns_rdatatype_none, 0, &rdataset, NULL);
+ result = dns_db_findrdataset(signing->db, node, version,
+ zone->privatetype, dns_rdatatype_none,
+ 0, &rdataset, NULL);
if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
result = ISC_R_SUCCESS;
goto failure;
}
- if (result != ISC_R_SUCCESS)
+ 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)) {
dns_rdataset_current(&rdataset, &rdata);
+ /*
+ * If we don't match the algorithm or keyid skip the record.
+ */
if (rdata.length != 5 ||
rdata.data[0] != signing->algorithm ||
rdata.data[1] != ((signing->keyid >> 8) & 0xff) ||
rdata.data[2] != (signing->keyid & 0xff)) {
+ have_rr = ISC_TRUE;
dns_rdata_reset(&rdata);
continue;
}
- if (!signing->delete && rdata.data[4] != 0)
+ /*
+ * We have a match. If we were signing (!signing->delete)
+ * and we already have a record indicating that we have
+ * finished signing (rdata.data[4] != 0) then keep it.
+ * Otherwise it needs to be deleted as we have removed all
+ * the signatures (signing->delete), so any record indicating
+ * completion is now out of date, or we have finished signing
+ * with the new record so we no longer need to remember that
+ * we need to sign the zone with the matching key across a
+ * nameserver re-start.
+ */
+ if (!signing->delete && rdata.data[4] != 0) {
seen_done = ISC_TRUE;
- else
+ have_rr = ISC_TRUE;
+ } else
CHECK(update_one_rr(signing->db, version, diff,
- DNS_DIFFOP_DEL, name, rdataset.ttl, &rdata));
+ DNS_DIFFOP_DEL, &zone->origin,
+ rdataset.ttl, &rdata));
dns_rdata_reset(&rdata);
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
if (!signing->delete && !seen_done) {
-
+ /*
+ * If we were signing then we need to indicate that we have
+ * finished signing the zone with this key. If it is already
+ * there we don't need to add it a second time.
+ */
data[0] = signing->algorithm;
data[1] = (signing->keyid >> 8) & 0xff;
data[2] = signing->keyid & 0xff;
data[4] = 1;
rdata.length = sizeof(data);
rdata.data = data;
- rdata.type = privatetype;
+ rdata.type = zone->privatetype;
rdata.rdclass = dns_db_class(signing->db);
CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD,
- name, rdataset.ttl, &rdata));
+ &zone->origin, rdataset.ttl, &rdata));
+ } else if (!have_rr) {
+ dns_name_t *origin = dns_db_origin(signing->db);
+ /*
+ * Rebuild the NSEC/NSEC3 record for the origin as we no
+ * longer have any private records.
+ */
+ if (build_nsec3)
+ CHECK(dns_nsec3_addnsec3s(signing->db, version, origin,
+ minimum, ISC_FALSE, diff));
+ CHECK(updatesecure(signing->db, version, origin, minimum,
+ ISC_TRUE, diff));
}
+
failure:
if (dns_rdataset_isassociated(&rdataset))
dns_rdataset_disassociate(&rdataset);
return (result);
}
+/*
+ * If 'active' is set then we are not done with the chain yet so only
+ * delete the nsec3param record which indicates a full chain exists
+ * (flags == 0).
+ */
static isc_result_t
fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain,
- isc_boolean_t active, dns_diff_t *diff)
+ isc_boolean_t active, dns_rdatatype_t privatetype,
+ dns_diff_t *diff)
{
dns_dbnode_t *node = NULL;
dns_name_t *name = dns_db_origin(db);
unsigned char parambuf[DNS_NSEC3PARAM_BUFFERSIZE];
dns_ttl_t ttl = 0;
- dns_rdataset_init(&rdataset);
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto try_private;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Preserve the existing ttl.
+ */
+ ttl = rdataset.ttl;
+
+ /*
+ * Delete all NSEC3PARAM records which match that in nsec3chain.
+ */
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+ if (nsec3param.hash != chain->nsec3param.hash ||
+ (active && nsec3param.flags != 0) ||
+ nsec3param.iterations != chain->nsec3param.iterations ||
+ nsec3param.salt_length != chain->nsec3param.salt_length ||
+ memcmp(nsec3param.salt, chain->nsec3param.salt,
+ nsec3param.salt_length)) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
+ name, rdataset.ttl, &rdata));
+ dns_rdata_reset(&rdata);
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+
+ dns_rdataset_disassociate(&rdataset);
- result = dns_db_getoriginnode(db, &node);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ try_private:
+
+ if (active)
+ goto add;
+ /*
+ * Delete all private records which match that in nsec3chain.
+ */
+ result = dns_db_findrdataset(db, node, ver, privatetype,
0, 0, &rdataset, NULL);
if (result == ISC_R_NOTFOUND)
goto add;
if (result != ISC_R_SUCCESS)
goto failure;
- /*
- * Preserve the existing ttl.
- */
- ttl = rdataset.ttl;
-
- /*
- * Delete all NSEC3PARAM records which match that in nsec3chain.
- */
for (result = dns_rdataset_first(&rdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t private = DNS_RDATA_INIT;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
- dns_rdataset_current(&rdataset, &rdata);
+ dns_rdataset_current(&rdataset, &private);
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf)))
+ continue;
CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
if (nsec3param.hash != chain->nsec3param.hash ||
- (active && nsec3param.flags != 0) ||
nsec3param.iterations != chain->nsec3param.iterations ||
nsec3param.salt_length != chain->nsec3param.salt_length ||
memcmp(nsec3param.salt, chain->nsec3param.salt,
}
CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
- name, rdataset.ttl, &rdata));
+ name, rdataset.ttl, &private));
dns_rdata_reset(&rdata);
}
if (result != ISC_R_NOMORE)
static isc_result_t
need_nsec_chain(dns_db_t *db, dns_dbversion_t *ver,
const dns_rdata_nsec3param_t *param,
- isc_boolean_t *answer, isc_boolean_t *updatensec)
+ isc_boolean_t *answer)
{
dns_dbnode_t *node = NULL;
dns_rdata_t rdata = DNS_RDATA_INIT;
RUNTIME_CHECK(result == ISC_R_SUCCESS);
dns_rdataset_init(&rdataset);
+
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
0, 0, &rdataset, NULL);
- if (result == ISC_R_NOTFOUND)
- goto check_nsec3param;
-
- if (result != ISC_R_SUCCESS)
- goto failure;
-
- CHECK(dns_rdataset_first(&rdataset));
- dns_rdataset_current(&rdataset, &rdata);
-
- if (!dns_nsec_typepresent(&rdata, dns_rdatatype_opt)) {
- /*
- * We have a complete NSEC chain. Signal to update
- * the apex NSEC record.
- */
- *updatensec = ISC_TRUE;
- goto failure;
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+ }
+ if (result != ISC_R_NOTFOUND) {
+ dns_db_detachnode(db, &node);
+ return (result);
}
- dns_rdataset_disassociate(&rdataset);
- dns_rdata_reset(&rdata);
- check_nsec3param:
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
0, 0, &rdataset, NULL);
if (result == ISC_R_NOTFOUND) {
return (result);
}
+static isc_result_t
+update_sigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version,
+ dst_key_t *zone_keys[], unsigned int nkeys, dns_zone_t *zone,
+ isc_stdtime_t inception, isc_stdtime_t expire, isc_stdtime_t now,
+ isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly,
+ dns_diff_t *sig_diff)
+{
+ dns_difftuple_t *tuple;
+ isc_result_t result;
+
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_HEAD(diff->tuples)) {
+ result = del_sigs(zone, db, version, &tuple->name,
+ tuple->rdata.type, sig_diff,
+ zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "update_sigs:del_sigs -> %s\n",
+ dns_result_totext(result));
+ return (result);
+ }
+ result = add_sigs(db, version, &tuple->name,
+ tuple->rdata.type, sig_diff,
+ zone_keys, nkeys, zone->mctx, inception,
+ expire, check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "update_sigs:add_sigs -> %s\n",
+ dns_result_totext(result));
+ return (result);
+ }
+
+ do {
+ dns_difftuple_t *next = ISC_LIST_NEXT(tuple, link);
+ while (next != NULL &&
+ (tuple->rdata.type != next->rdata.type ||
+ !dns_name_equal(&tuple->name, &next->name)))
+ next = ISC_LIST_NEXT(next, link);
+ ISC_LIST_UNLINK(diff->tuples, tuple, link);
+ dns_diff_appendminimal(sig_diff, &tuple);
+ INSIST(tuple == NULL);
+ tuple = next;
+ } while (tuple != NULL);
+ }
+ return (ISC_R_SUCCESS);
+}
+
/*
* Incrementally build and sign a new NSEC3 chain using the parameters
* requested.
*/
static void
zone_nsec3chain(dns_zone_t *zone) {
- const char *journalfile;
dns_db_t *db = NULL;
dns_dbnode_t *node = NULL;
dns_dbversion_t *version = NULL;
dns_nsec3chainlist_t cleanup;
dst_key_t *zone_keys[MAXZONEKEYS];
isc_int32_t signatures;
- isc_boolean_t check_ksk, is_ksk;
+ isc_boolean_t check_ksk, keyset_kskonly, is_ksk;
isc_boolean_t delegation;
isc_boolean_t first;
isc_result_t result;
isc_boolean_t seen_soa, seen_ns, seen_dname, seen_ds;
isc_boolean_t seen_nsec, seen_nsec3, seen_rr;
dns_rdatasetiter_t *iterator = NULL;
- dns_difftuple_t *tuple;
isc_boolean_t buildnsecchain;
isc_boolean_t updatensec = ISC_FALSE;
+ dns_rdatatype_t privatetype = zone->privatetype;
dns_rdataset_init(&rdataset);
dns_fixedname_init(&fixed);
stop = now + 5;
check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
- if (check_ksk)
- check_ksk = ksk_sanity(db, version);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
/*
* We keep pulling nodes off each iterator in turn until
* Process one node.
*/
dns_dbiterator_pause(nsec3chain->dbiterator);
- CHECK(dns_nsec3_addnsec3(db, version, name,
- &nsec3chain->nsec3param,
- zone->minimum, unsecure, &nsec3_diff));
+ result = dns_nsec3_addnsec3(db, version, name,
+ &nsec3chain->nsec3param,
+ zone->minimum, unsecure,
+ &nsec3_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns_nsec3_addnsec3 -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
/*
* Treat each call to dns_nsec3_addnsec3() as if it's cost is
* two signatures. Additionally there will, in general, be
if (result == ISC_R_NOMORE && nsec3chain->delete_nsec) {
CHECK(fixup_nsec3param(db, version, nsec3chain,
- ISC_FALSE, ¶m_diff));
+ ISC_FALSE, privatetype,
+ ¶m_diff));
LOCK_ZONE(zone);
ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
link);
CHECK(fixup_nsec3param(db, version,
nsec3chain,
ISC_TRUE,
+ privatetype,
¶m_diff));
nsec3chain->delete_nsec = ISC_TRUE;
goto same_addchain;
}
CHECK(fixup_nsec3param(db, version, nsec3chain,
- ISC_FALSE, ¶m_diff));
+ ISC_FALSE, privatetype,
+ ¶m_diff));
LOCK_ZONE(zone);
ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
link);
* of removing this NSEC3 chain.
*/
if (first && !updatensec &&
- (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0)
- CHECK(need_nsec_chain(db, version,
- &nsec3chain->nsec3param,
- &buildnsecchain, &updatensec));
+ (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0) {
+ result = need_nsec_chain(db, version,
+ &nsec3chain->nsec3param,
+ &buildnsecchain);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "need_nsec_chain -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ if (first)
+ dns_zone_log(zone, ISC_LOG_DEBUG(3), "zone_nsec3chain:"
+ "buildnsecchain = %u\n", buildnsecchain);
dns_dbiterator_current(nsec3chain->dbiterator, &node, name);
delegation = ISC_FALSE;
/*
* Delete the NSECPARAM record that matches this chain.
*/
- if (first)
- CHECK(fixup_nsec3param(db, version, nsec3chain,
- ISC_TRUE, ¶m_diff));
+ if (first) {
+ result = fixup_nsec3param(db, version,
+ nsec3chain,
+ ISC_TRUE, privatetype,
+ ¶m_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "fixup_nsec3param -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
/*
* Delete the NSEC3 records.
*/
- CHECK(deletematchingnsec3(db, version, node, name,
- &nsec3chain->nsec3param,
- &nsec3_diff));
+ result = deletematchingnsec3(db, version, node, name,
+ &nsec3chain->nsec3param,
+ &nsec3_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "deletematchingnsec3 -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
goto next_removenode;
}
seen_nsec = ISC_TRUE;
else if (rdataset.type == dns_rdatatype_nsec3)
seen_nsec3 = ISC_TRUE;
- seen_rr = ISC_TRUE;
+ if (rdataset.type != dns_rdatatype_rrsig)
+ seen_rr = ISC_TRUE;
dns_rdataset_disassociate(&rdataset);
}
dns_rdatasetiter_destroy(&iterator);
if ((seen_ns && !seen_soa) || seen_dname)
delegation = ISC_TRUE;
- CHECK(add_nsec(db, version, name, node, zone->minimum,
- delegation, &nsec_diff));
+ /*
+ * Add a NSEC record except at the origin.
+ */
+ if (!dns_name_equal(name, dns_db_origin(db))) {
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ CHECK(add_nsec(db, version, name, node, zone->minimum,
+ delegation, &nsec_diff));
+ }
next_removenode:
first = ISC_FALSE;
UNLOCK_ZONE(zone);
ISC_LIST_APPEND(cleanup, nsec3chain, link);
dns_dbiterator_pause(nsec3chain->dbiterator);
- CHECK(fixup_nsec3param(db, version, nsec3chain,
- ISC_FALSE, ¶m_diff));
+ result = fixup_nsec3param(db, version,
+ nsec3chain, ISC_FALSE,
+ privatetype,
+ ¶m_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "fixup_nsec3param -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
goto next_removechain;
} else if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
}
/*
- * Add / update signatures for the NSEC3 records.
+ * We may need to update the NSEC/NSEC3 records for the zone apex.
*/
- for (tuple = ISC_LIST_HEAD(nsec3_diff.tuples);
- tuple != NULL;
- tuple = ISC_LIST_HEAD(nsec3_diff.tuples)) {
- /*
- * We have changed the NSEC3 RRset above so we need to update
- * the signatures.
- */
- result = del_sigs(zone, db, version, &tuple->name,
- dns_rdatatype_nsec3, &sig_diff,
- zone_keys, nkeys, now);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_nsec3chain:del_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
- result = add_sigs(db, version, &tuple->name,
- dns_rdatatype_nsec3, &sig_diff, zone_keys,
- nkeys, zone->mctx, inception, expire,
- check_ksk);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_nsec3chain:add_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
+ if (!ISC_LIST_EMPTY(param_diff.tuples)) {
+ isc_boolean_t rebuild_nsec = ISC_FALSE,
+ rebuild_nsec3 = ISC_FALSE;
+ result = dns_db_getoriginnode(db, &node);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+ for (result = dns_rdatasetiter_first(iterator);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iterator)) {
+ dns_rdatasetiter_current(iterator, &rdataset);
+ if (rdataset.type == dns_rdatatype_nsec)
+ rebuild_nsec = ISC_TRUE;
+ if (rdataset.type == dns_rdatatype_nsec3param)
+ rebuild_nsec3 = ISC_TRUE;
+ dns_rdataset_disassociate(&rdataset);
}
+ dns_rdatasetiter_destroy(&iterator);
+ dns_db_detachnode(db, &node);
- do {
- dns_difftuple_t *next = ISC_LIST_NEXT(tuple, link);
- while (next != NULL &&
- !dns_name_equal(&tuple->name, &next->name))
- next = ISC_LIST_NEXT(next, link);
- ISC_LIST_UNLINK(nsec3_diff.tuples, tuple, link);
- dns_diff_appendminimal(&sig_diff, &tuple);
- INSIST(tuple == NULL);
- tuple = next;
- } while (tuple != NULL);
- }
-
- for (tuple = ISC_LIST_HEAD(param_diff.tuples);
- tuple != NULL;
- tuple = ISC_LIST_HEAD(param_diff.tuples)) {
- /*
- * We have changed the NSEC3PARAM RRset above so we need to
- * update the signatures.
- */
- result = del_sigs(zone, db, version, &tuple->name,
- dns_rdatatype_nsec3param, &sig_diff,
- zone_keys, nkeys, now);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_nsec3chain:del_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
+ if (rebuild_nsec) {
+ if (nsec3chain != NULL)
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ result = updatesecure(db, version, &zone->origin,
+ zone->minimum, ISC_TRUE,
+ &nsec_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "updatesecure -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
}
- result = add_sigs(db, version, &tuple->name,
- dns_rdatatype_nsec3param, &sig_diff,
- zone_keys, nkeys, zone->mctx, inception,
- expire, check_ksk);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_nsec3chain:add_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
+ if (rebuild_nsec3) {
+ result = dns_nsec3_addnsec3s(db, version,
+ dns_db_origin(db),
+ zone->minimum, ISC_FALSE,
+ &nsec3_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "dns_nsec3_addnsec3s -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
}
- ISC_LIST_UNLINK(param_diff.tuples, tuple, link);
- dns_diff_appendminimal(&sig_diff, &tuple);
- INSIST(tuple == NULL);
}
- if (updatensec)
- CHECK(updatesecure(db, version, &zone->origin, zone->minimum,
- NULL, &nsec_diff));
+ /*
+ * Add / update signatures for the NSEC3 records.
+ */
+ result = update_sigs(&nsec3_diff, db, version, zone_keys,
+ nkeys, zone, inception, expire, now,
+ check_ksk, keyset_kskonly, &sig_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "update_sigs -> %s\n", dns_result_totext(result));
+ goto failure;
+ }
- for (tuple = ISC_LIST_HEAD(nsec_diff.tuples);
- tuple != NULL;
- tuple = ISC_LIST_HEAD(nsec_diff.tuples)) {
- result = del_sigs(zone, db, version, &tuple->name,
- dns_rdatatype_nsec, &sig_diff,
- zone_keys, nkeys, now);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_nsec3chain:del_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
- result = add_sigs(db, version, &tuple->name,
- dns_rdatatype_nsec, &sig_diff,
- zone_keys, nkeys, zone->mctx, inception,
- expire, check_ksk);
+ /*
+ * We have changed the NSEC3PARAM or private RRsets
+ * above so we need to update the signatures.
+ */
+ result = update_sigs(¶m_diff, db, version, zone_keys,
+ nkeys, zone, inception, expire, now,
+ check_ksk, keyset_kskonly, &sig_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "update_sigs -> %s\n", dns_result_totext(result));
+ goto failure;
+ }
+
+ if (updatensec) {
+ if (nsec3chain != NULL)
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ result = updatesecure(db, version, &zone->origin,
+ zone->minimum, ISC_FALSE, &nsec_diff);
if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_nsec3chain:add_sigs -> %s\n",
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "updatesecure -> %s\n",
dns_result_totext(result));
goto failure;
}
- ISC_LIST_UNLINK(nsec_diff.tuples, tuple, link);
- dns_diff_appendminimal(&sig_diff, &tuple);
- INSIST(tuple == NULL);
+ }
+
+ result = update_sigs(&nsec_diff, db, version, zone_keys,
+ nkeys, zone, inception, expire, now,
+ check_ksk, keyset_kskonly, &sig_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "update_sigs -> %s\n", dns_result_totext(result));
+ goto failure;
}
/*
result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
&sig_diff, zone_keys, nkeys, zone->mctx, inception,
- soaexpire, check_ksk);
+ soaexpire, check_ksk, keyset_kskonly);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
"add_sigs -> %s\n", dns_result_totext(result));
goto failure;
}
- journalfile = dns_zone_getjournal(zone);
- if (journalfile != NULL) {
- dns_journal_t *journal = NULL;
- result = dns_journal_open(zone->mctx, journalfile,
- ISC_TRUE, &journal);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
- "dns_journal_open -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
-
- result = dns_journal_write_transaction(journal, &sig_diff);
- dns_journal_destroy(&journal);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
- "dns_journal_write_transaction -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
- }
+ /* Write changes to journal file. */
+ zone_journal(zone, &sig_diff, "zone_nsec3chain");
LOCK_ZONE(zone);
zone_needdump(zone, DNS_DUMP_DELAY);
set_resigntime(zone);
failure:
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain: %s\n",
+ dns_result_totext(result));
/*
* On error roll back the current nsec3chain.
*/
for (i = 0; i < nkeys; i++)
dst_key_free(&zone_keys[i]);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
if (version != NULL) {
dns_db_closeversion(db, &version, ISC_FALSE);
dns_db_detach(&db);
rrsig.keyid != keyid)
continue;
CHECK(update_one_rr(db, version, diff,
- DNS_DIFFOP_DEL, name,
+ DNS_DIFFOP_DELRESIGN, name,
rdataset.ttl, &rdata));
}
dns_rdataset_disassociate(&rdataset);
*/
static void
zone_sign(dns_zone_t *zone) {
- const char *journalfile;
dns_db_t *db = NULL;
dns_dbnode_t *node = NULL;
dns_dbversion_t *version = NULL;
dns_diff_t sig_diff;
+ dns_diff_t post_diff;
dns_fixedname_t fixed;
dns_fixedname_t nextfixed;
dns_name_t *name, *nextname;
dns_signinglist_t cleanup;
dst_key_t *zone_keys[MAXZONEKEYS];
isc_int32_t signatures;
- isc_boolean_t check_ksk, is_ksk;
+ isc_boolean_t check_ksk, keyset_kskonly, is_ksk;
+ isc_boolean_t commit = ISC_FALSE;
isc_boolean_t delegation;
- isc_boolean_t finishedakey = ISC_FALSE;
- isc_boolean_t secureupdated = ISC_FALSE;
- isc_boolean_t build_nsec3 = ISC_FALSE, build_nsec = ISC_FALSE;
+ isc_boolean_t build_nsec = ISC_FALSE;
+ isc_boolean_t build_nsec3 = ISC_FALSE;
isc_boolean_t first;
isc_result_t result;
isc_stdtime_t now, inception, soaexpire, expire, stop;
isc_uint32_t jitter;
- unsigned int i;
+ unsigned int i, j;
unsigned int nkeys = 0;
isc_uint32_t nodes;
+ isc_boolean_t was_ksk;
dns_rdataset_init(&rdataset);
dns_fixedname_init(&fixed);
nextname = dns_fixedname_name(&nextfixed);
dns_diff_init(zone->mctx, &sig_diff);
sig_diff.resign = zone->sigresigninginterval;
+ dns_diff_init(zone->mctx, &post_diff);
ISC_LIST_INIT(cleanup);
/*
expire = soaexpire - jitter % 3600;
stop = now + 5;
- check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
- if (check_ksk)
- check_ksk = ksk_sanity(db, version);
-
/*
* We keep pulling nodes off each iterator in turn until
* we have no more nodes to pull off or we reach the limits
signatures = zone->signatures;
signing = ISC_LIST_HEAD(zone->signing);
first = ISC_TRUE;
- /*
- * See if we have a NSEC chain.
- */
- result = dns_db_getoriginnode(db, &node);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
- dns_rdatatype_none, 0, &rdataset, NULL);
- dns_db_detachnode(db, &node);
- if (result == ISC_R_SUCCESS) {
- build_nsec = ISC_TRUE;
- dns_rdataset_disassociate(&rdataset);
- } else if (result != ISC_R_NOTFOUND) {
- goto failure;
- } else {
- /*
- * No NSEC chain present.
- * See if we need to build a NSEC3 chain?
- */
- result = dns_nsec3_active(db, version, ISC_TRUE, &build_nsec3);
- if (result == ISC_R_SUCCESS) {
- if (build_nsec3)
- build_nsec3 = ISC_FALSE;
- else {
- result = dns_nsec3_active(db, version,
- ISC_FALSE,
- &build_nsec3);
- if (build_nsec3)
- secureupdated = ISC_TRUE;
- else
- build_nsec = ISC_TRUE;
- }
- }
- }
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
+
+ /* Determine which type of chain to build */
+ CHECK(dns_private_chains(db, version, zone->privatetype,
+ &build_nsec, &build_nsec3));
+
+ /* If neither chain is found, default to NSEC */
+ if (!build_nsec && !build_nsec3)
+ build_nsec = ISC_TRUE;
while (signing != NULL && nodes-- > 0 && signatures > 0) {
nextsigning = ISC_LIST_NEXT(signing, link);
ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
if (signing->done || signing->db != zone->db) {
/*
- * The zone has been reloaded. We will have
+ * The zone has been reloaded. We will have
* created new signings as part of the reload
* process so we can destroy this one.
*/
if (signing->db != db)
goto next_signing;
- is_ksk = ISC_FALSE;
delegation = ISC_FALSE;
+ was_ksk = ISC_FALSE;
+
+ if (first && signing->delete) {
+ /*
+ * Remove the key we are deleting from consideration.
+ */
+ for (i = 0, j = 0; i < nkeys; i++) {
+ /*
+ * Find the key we want to remove.
+ */
+ if (ALG(zone_keys[i]) == signing->algorithm &&
+ dst_key_id(zone_keys[i]) == signing->keyid)
+ {
+ if (KSK(zone_keys[i]))
+ dst_key_free(&zone_keys[i]);
+ continue;
+ }
+ zone_keys[j] = zone_keys[i];
+ j++;
+ }
+ nkeys = j;
+ }
+
dns_dbiterator_current(signing->dbiterator, &node, name);
if (signing->delete) {
CHECK(del_sig(db, version, name, node, nkeys,
signing->algorithm, signing->keyid,
&sig_diff));
- goto next_node;
}
+
/*
* On the first pass we need to check if the current node
* has not been obscured.
*/
dns_dbiterator_pause(signing->dbiterator);
for (i = 0; i < nkeys; i++) {
+ isc_boolean_t both = ISC_FALSE;
+
+ /*
+ * Find the keys we want to sign with.
+ */
+ if (!dst_key_isprivate(zone_keys[i]))
+ continue;
+
+ /*
+ * When adding look for the specific key.
+ */
+ if (!signing->delete &&
+ (dst_key_alg(zone_keys[i]) != signing->algorithm ||
+ dst_key_id(zone_keys[i]) != signing->keyid))
+ continue;
+
/*
- * Find the key we want to sign with.
+ * When deleting make sure we are properly signed
+ * with the algorithm that was being removed.
*/
- if (dst_key_alg(zone_keys[i]) != signing->algorithm ||
- dst_key_id(zone_keys[i]) != signing->keyid ||
- !dst_key_isprivate(zone_keys[i]))
+ if (signing->delete &&
+ ALG(zone_keys[i]) != signing->algorithm)
continue;
+
/*
* Do we do KSK processing?
*/
- if (check_ksk &&
- (dst_key_flags(zone_keys[i]) & DNS_KEYFLAG_KSK) != 0)
- is_ksk = ISC_TRUE;
+ if (check_ksk && !REVOKE(zone_keys[i])) {
+ isc_boolean_t have_ksk, have_nonksk;
+ if (KSK(zone_keys[i])) {
+ have_ksk = ISC_TRUE;
+ have_nonksk = ISC_FALSE;
+ } else {
+ have_ksk = ISC_FALSE;
+ have_nonksk = ISC_TRUE;
+ }
+ for (j = 0; j < nkeys; j++) {
+ if (j == i ||
+ ALG(zone_keys[i]) !=
+ ALG(zone_keys[j]))
+ continue;
+ if (REVOKE(zone_keys[j]))
+ continue;
+ if (KSK(zone_keys[j]))
+ have_ksk = ISC_TRUE;
+ else
+ have_nonksk = ISC_TRUE;
+ both = have_ksk && have_nonksk;
+ if (both)
+ break;
+ }
+ }
+ if (both || REVOKE(zone_keys[i]))
+ is_ksk = KSK(zone_keys[i]);
+ else
+ is_ksk = ISC_FALSE;
+
CHECK(sign_a_node(db, name, node, version, build_nsec3,
build_nsec, zone_keys[i], inception,
expire, zone->minimum, is_ksk,
- &delegation, &sig_diff, &signatures,
- zone->mctx));
- break;
+ ISC_TF(both && keyset_kskonly),
+ &delegation, &sig_diff,
+ &signatures, zone->mctx));
+ /*
+ * If we are adding we are done. Look for other keys
+ * of the same algorithm if deleting.
+ */
+ if (!signing->delete)
+ break;
}
+
/*
* Go onto next node.
*/
ISC_LIST_UNLINK(zone->signing, signing, link);
ISC_LIST_APPEND(cleanup, signing, link);
dns_dbiterator_pause(signing->dbiterator);
- finishedakey = ISC_TRUE;
- if (!is_ksk && !secureupdated && nkeys != 0 &&
- build_nsec) {
+ if (nkeys != 0 && build_nsec) {
/*
* We have finished regenerating the
* zone with a zone signing key.
result = updatesecure(db, version,
&zone->origin,
zone->minimum,
- &secureupdated,
- &sig_diff);
+ ISC_FALSE,
+ &post_diff);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone,
ISC_LOG_ERROR,
goto failure;
}
}
- result = updatesignwithkey(signing, version,
- &zone->origin,
- zone->privatetype,
- &sig_diff);
+ result = updatesignwithkey(zone, signing,
+ version,
+ build_nsec3,
+ zone->minimum,
+ &post_diff);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
- "updatesignwithkey -> %s\n",
+ "updatesignwithkey "
+ "-> %s\n",
dns_result_totext(result));
goto failure;
}
+ build_nsec = ISC_FALSE;
goto next_signing;
} else if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
} while (1);
continue;
- next_signing:
- dns_dbiterator_pause(signing->dbiterator);
- signing = nextsigning;
- first = ISC_TRUE;
- }
+ next_signing:
+ dns_dbiterator_pause(signing->dbiterator);
+ signing = nextsigning;
+ first = ISC_TRUE;
+ }
+
+ if (ISC_LIST_HEAD(post_diff.tuples) != NULL) {
+ result = update_sigs(&post_diff, db, version, zone_keys,
+ nkeys, zone, inception, expire, now,
+ check_ksk, keyset_kskonly, &sig_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_sign:"
+ "update_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ /*
+ * Have we changed anything?
+ */
+ if (ISC_LIST_HEAD(sig_diff.tuples) == NULL) {
+ result = ISC_R_SUCCESS;
+ goto pauseall;
+ }
+
+ commit = ISC_TRUE;
+
+ result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = increment_soa_serial(db, version, &sig_diff, zone->mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:increment_soa_serial -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Generate maximum life time signatures so that the above loop
+ * termination is sensible.
+ */
+ result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, zone->mctx, inception,
+ soaexpire, check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Write changes to journal file.
+ */
+ result = zone_journal(zone, &sig_diff, "zone_sign");
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ pauseall:
+ /*
+ * Pause all iterators so that dns_db_closeversion() can succeed.
+ */
+ for (signing = ISC_LIST_HEAD(zone->signing);
+ signing != NULL;
+ signing = ISC_LIST_NEXT(signing, link))
+ dns_dbiterator_pause(signing->dbiterator);
+
+ for (signing = ISC_LIST_HEAD(cleanup);
+ signing != NULL;
+ signing = ISC_LIST_NEXT(signing, link))
+ dns_dbiterator_pause(signing->dbiterator);
+
+ /*
+ * Everything has succeeded. Commit the changes.
+ */
+ dns_db_closeversion(db, &version, commit);
+
+ /*
+ * Everything succeeded so we can clean these up now.
+ */
+ signing = ISC_LIST_HEAD(cleanup);
+ while (signing != NULL) {
+ ISC_LIST_UNLINK(cleanup, signing, link);
+ dns_db_detach(&signing->db);
+ dns_dbiterator_destroy(&signing->dbiterator);
+ isc_mem_put(zone->mctx, signing, sizeof *signing);
+ signing = ISC_LIST_HEAD(cleanup);
+ }
+
+ set_resigntime(zone);
+
+ if (commit) {
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ UNLOCK_ZONE(zone);
+ }
+
+ failure:
+ /*
+ * Rollback the cleanup list.
+ */
+ signing = ISC_LIST_HEAD(cleanup);
+ while (signing != NULL) {
+ ISC_LIST_UNLINK(cleanup, signing, link);
+ ISC_LIST_PREPEND(zone->signing, signing, link);
+ dns_dbiterator_first(signing->dbiterator);
+ dns_dbiterator_pause(signing->dbiterator);
+ signing = ISC_LIST_HEAD(cleanup);
+ }
+
+ for (signing = ISC_LIST_HEAD(zone->signing);
+ signing != NULL;
+ signing = ISC_LIST_NEXT(signing, link))
+ dns_dbiterator_pause(signing->dbiterator);
+
+ dns_diff_clear(&sig_diff);
+
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, ISC_FALSE);
+ dns_db_detach(&db);
+ } else if (db != NULL)
+ dns_db_detach(&db);
+
+ if (ISC_LIST_HEAD(zone->signing) != NULL) {
+ isc_interval_t i;
+ if (zone->update_disabled || result != ISC_R_SUCCESS)
+ isc_interval_set(&i, 60, 0); /* 1 minute */
+ else
+ isc_interval_set(&i, 0, 10000000); /* 10 ms */
+ isc_time_nowplusinterval(&zone->signingtime, &i);
+ } else
+ isc_time_settoepoch(&zone->signingtime);
+}
+
+static void
+normalize_key(dns_rdata_t *rr, dns_rdata_t *target,
+ unsigned char *data, int size) {
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_keydata_t keydata;
+ isc_buffer_t buf;
+
+ dns_rdata_reset(target);
+ isc_buffer_init(&buf, data, size);
+
+ switch (rr->type) {
+ case dns_rdatatype_dnskey:
+ dns_rdata_tostruct(rr, &dnskey, NULL);
+ dnskey.flags &= ~DNS_KEYFLAG_REVOKE;
+ dns_rdata_fromstruct(target, rr->rdclass, dns_rdatatype_dnskey,
+ &dnskey, &buf);
+ break;
+ case dns_rdatatype_keydata:
+ dns_rdata_tostruct(rr, &keydata, NULL);
+ dns_keydata_todnskey(&keydata, &dnskey, NULL);
+ dns_rdata_fromstruct(target, rr->rdclass, dns_rdatatype_dnskey,
+ &dnskey, &buf);
+ break;
+ default:
+ INSIST(0);
+ }
+}
+
+/*
+ * 'rdset' contains either a DNSKEY rdataset from the zone apex, or
+ * a KEYDATA rdataset from the key zone.
+ *
+ * 'rr' contains either a DNSKEY record, or a KEYDATA record
+ *
+ * After normalizing keys to the same format (DNSKEY, with revoke bit
+ * cleared), return ISC_TRUE if a key that matches 'rr' is found in
+ * 'rdset', or ISC_FALSE if not.
+ */
+
+static isc_boolean_t
+matchkey(dns_rdataset_t *rdset, dns_rdata_t *rr) {
+ unsigned char data1[4096], data2[4096];
+ dns_rdata_t rdata, rdata1, rdata2;
+ isc_result_t result;
+
+ dns_rdata_init(&rdata);
+ dns_rdata_init(&rdata1);
+ dns_rdata_init(&rdata2);
+
+ normalize_key(rr, &rdata1, data1, sizeof(data1));
+
+ for (result = dns_rdataset_first(rdset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdset, &rdata);
+ normalize_key(&rdata, &rdata2, data2, sizeof(data2));
+ if (dns_rdata_compare(&rdata1, &rdata2) == 0)
+ return (ISC_TRUE);
+ }
+
+ return (ISC_FALSE);
+}
+
+/*
+ * Calculate the refresh interval for a keydata zone, per
+ * RFC5011: MAX(1 hr,
+ * MIN(15 days,
+ * 1/2 * OrigTTL,
+ * 1/2 * RRSigExpirationInterval))
+ * or for retries: MAX(1 hr,
+ * MIN(1 day,
+ * 1/10 * OrigTTL,
+ * 1/10 * RRSigExpirationInterval))
+ */
+static inline isc_stdtime_t
+refresh_time(dns_keyfetch_t *kfetch, isc_boolean_t retry) {
+ isc_result_t result;
+ isc_uint32_t t;
+ dns_rdataset_t *rdset;
+ dns_rdata_t sigrr = DNS_RDATA_INIT;
+ dns_rdata_sig_t sig;
+ isc_stdtime_t now;
+
+ isc_stdtime_get(&now);
+
+ if (dns_rdataset_isassociated(&kfetch->dnskeysigset))
+ rdset = &kfetch->dnskeysigset;
+ else
+ return (now + HOUR);
+
+ result = dns_rdataset_first(rdset);
+ if (result != ISC_R_SUCCESS)
+ return (now + HOUR);
+
+ dns_rdataset_current(rdset, &sigrr);
+ result = dns_rdata_tostruct(&sigrr, &sig, NULL);
+
+ if (!retry) {
+ t = sig.originalttl / 2;
+
+ if (isc_serial_gt(sig.timeexpire, now)) {
+ isc_uint32_t exp = (sig.timeexpire - now) / 2;
+ if (t > exp)
+ t = exp;
+ }
+
+ if (t > (15*DAY))
+ t = (15*DAY);
+
+ if (t < HOUR)
+ t = HOUR;
+ } else {
+ t = sig.originalttl / 10;
+
+ if (isc_serial_gt(sig.timeexpire, now)) {
+ isc_uint32_t exp = (sig.timeexpire - now) / 10;
+ if (t > exp)
+ t = exp;
+ }
+
+ if (t > DAY)
+ t = DAY;
+
+ if (t < HOUR)
+ t = HOUR;
+ }
+
+ return (now + t);
+}
+
+/*
+ * This routine is called when no changes are needed in a KEYDATA
+ * record except to simply update the refresh timer. Caller should
+ * hold zone lock.
+ */
+static isc_result_t
+minimal_update(dns_keyfetch_t *kfetch, dns_dbversion_t *ver, dns_diff_t *diff)
+{
+ isc_result_t result;
+ isc_buffer_t keyb;
+ unsigned char key_buf[4096];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t keydata;
+ dns_name_t *name;
+ dns_zone_t *zone = kfetch->zone;
+ isc_stdtime_t now;
+
+ name = dns_fixedname_name(&kfetch->name);
+ isc_stdtime_get(&now);
+
+ for (result = dns_rdataset_first(&kfetch->keydataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->keydataset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&kfetch->keydataset, &rdata);
+
+ /* Delete old version */
+ CHECK(update_one_rr(kfetch->db, ver, diff, DNS_DIFFOP_DEL,
+ name, 0, &rdata));
+
+ /* Update refresh timer */
+ CHECK(dns_rdata_tostruct(&rdata, &keydata, NULL));
+ keydata.refresh = refresh_time(kfetch, ISC_TRUE);
+ set_refreshkeytimer(zone, &keydata, now);
+
+ dns_rdata_reset(&rdata);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ CHECK(dns_rdata_fromstruct(&rdata,
+ zone->rdclass, dns_rdatatype_keydata,
+ &keydata, &keyb));
+
+ /* Insert updated version */
+ CHECK(update_one_rr(kfetch->db, ver, diff, DNS_DIFFOP_ADD,
+ name, 0, &rdata));
+ }
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/*
+ * Verify that DNSKEY set is signed by the key specified in 'keydata'.
+ */
+static isc_boolean_t
+revocable(dns_keyfetch_t *kfetch, dns_rdata_keydata_t *keydata) {
+ isc_result_t result;
+ dns_name_t *keyname;
+ isc_mem_t *mctx;
+ dns_rdata_t sigrr = DNS_RDATA_INIT;
+ dns_rdata_t rr = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t sig;
+ dns_rdata_dnskey_t dnskey;
+ dst_key_t *dstkey = NULL;
+ unsigned char key_buf[4096];
+ isc_buffer_t keyb;
+ isc_boolean_t answer = ISC_FALSE;
+
+ REQUIRE(kfetch != NULL && keydata != NULL);
+ REQUIRE(dns_rdataset_isassociated(&kfetch->dnskeysigset));
+
+ keyname = dns_fixedname_name(&kfetch->name);
+ mctx = kfetch->zone->view->mctx;
+
+ /* Generate a key from keydata */
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_keydata_todnskey(keydata, &dnskey, NULL);
+ dns_rdata_fromstruct(&rr, keydata->common.rdclass, dns_rdatatype_dnskey,
+ &dnskey, &keyb);
+ result = dns_dnssec_keyfromrdata(keyname, &rr, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+
+ /* See if that key generated any of the signatures */
+ for (result = dns_rdataset_first(&kfetch->dnskeysigset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->dnskeysigset)) {
+ dns_fixedname_t fixed;
+ dns_fixedname_init(&fixed);
+
+ dns_rdata_reset(&sigrr);
+ dns_rdataset_current(&kfetch->dnskeysigset, &sigrr);
+ result = dns_rdata_tostruct(&sigrr, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
- if (secureupdated) {
- /*
- * We have changed the NSEC RRset above so we need to update
- * the signatures.
- */
- result = del_sigs(zone, db, version, &zone->origin,
- dns_rdatatype_nsec, &sig_diff, zone_keys,
- nkeys, now);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:del_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
- result = add_sigs(db, version, &zone->origin,
- dns_rdatatype_nsec, &sig_diff, zone_keys,
- nkeys, zone->mctx, inception, soaexpire,
- check_ksk);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:add_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
- }
- if (finishedakey) {
- /*
- * We have changed the RRset above so we need to update
- * the signatures.
- */
- result = del_sigs(zone, db, version, &zone->origin,
- zone->privatetype, &sig_diff,
- zone_keys, nkeys, now);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:del_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
- result = add_sigs(db, version, &zone->origin,
- zone->privatetype, &sig_diff,
- zone_keys, nkeys, zone->mctx, inception,
- soaexpire, check_ksk);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:add_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
+ if (dst_key_alg(dstkey) == sig.algorithm &&
+ (dst_key_id(dstkey) == sig.keyid ||
+ (sig.algorithm != 1 && sig.keyid ==
+ ((dst_key_id(dstkey) + 128) & 0xffff)))) {
+ result = dns_dnssec_verify2(keyname,
+ &kfetch->dnskeyset,
+ dstkey, ISC_FALSE, mctx, &sigrr,
+ dns_fixedname_name(&fixed));
+
+ dns_zone_log(kfetch->zone, ISC_LOG_DEBUG(3),
+ "Confirm revoked DNSKEY is self-signed: "
+ "%s", dns_result_totext(result));
+
+ if (result == ISC_R_SUCCESS) {
+ answer = ISC_TRUE;
+ break;
+ }
}
}
- result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
- &sig_diff, zone_keys, nkeys, now);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:del_sigs -> %s\n",
- dns_result_totext(result));
+
+ dst_key_free(&dstkey);
+ return (answer);
+}
+
+/*
+ * A DNSKEY set has been fetched from the zone apex of a zone whose trust
+ * anchors are being managed; scan the keyset, and update the key zone and the
+ * local trust anchors according to RFC5011.
+ */
+static void
+keyfetch_done(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result, eresult;
+ dns_fetchevent_t *devent;
+ dns_keyfetch_t *kfetch;
+ dns_zone_t *zone;
+ isc_mem_t *mctx = NULL;
+ dns_keytable_t *secroots = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+ isc_boolean_t changed = ISC_FALSE;
+ isc_boolean_t alldone = ISC_FALSE;
+ dns_name_t *keyname;
+ dns_rdata_t sigrr = DNS_RDATA_INIT;
+ dns_rdata_t dnskeyrr = DNS_RDATA_INIT;
+ dns_rdata_t keydatarr = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t sig;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_keydata_t keydata;
+ isc_boolean_t initializing;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ unsigned char key_buf[4096];
+ isc_buffer_t keyb;
+ dst_key_t *dstkey;
+ isc_stdtime_t now;
+ int pending = 0;
+ isc_boolean_t secure;
+
+ UNUSED(task);
+ INSIST(event != NULL && event->ev_type == DNS_EVENT_FETCHDONE);
+ INSIST(event->ev_arg != NULL);
+
+ kfetch = event->ev_arg;
+ zone = kfetch->zone;
+ isc_mem_attach(zone->mctx, &mctx);
+ keyname = dns_fixedname_name(&kfetch->name);
+
+ devent = (dns_fetchevent_t *) event;
+ eresult = devent->result;
+
+ /* Free resources which are not of interest */
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&kfetch->fetch);
+
+ isc_stdtime_get(&now);
+ dns_name_format(keyname, namebuf, sizeof(namebuf));
+
+ result = dns_view_getsecroots(zone->view, &secroots);
+ INSIST(result == ISC_R_SUCCESS);
+
+ LOCK_ZONE(zone);
+ dns_db_newversion(kfetch->db, &ver);
+ dns_diff_init(mctx, &diff);
+
+ zone->refreshkeycount--;
+ alldone = ISC_TF(zone->refreshkeycount == 0);
+
+ if (alldone)
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESHING);
+
+ /* Fetch failed */
+ if (eresult != ISC_R_SUCCESS ||
+ !dns_rdataset_isassociated(&kfetch->dnskeyset)) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Unable to fetch DNSKEY set "
+ "'%s': %s", namebuf, dns_result_totext(eresult));
+ CHECK(minimal_update(kfetch, ver, &diff));
+ changed = ISC_TRUE;
goto failure;
}
- result = increment_soa_serial(db, version, &sig_diff, zone->mctx);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:increment_soa_serial -> %s\n",
- dns_result_totext(result));
+ /* No RRSIGs found */
+ if (!dns_rdataset_isassociated(&kfetch->dnskeysigset)) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "No DNSKEY RRSIGs found for "
+ "'%s': %s", namebuf, dns_result_totext(eresult));
+ CHECK(minimal_update(kfetch, ver, &diff));
+ changed = ISC_TRUE;
goto failure;
}
/*
- * Generate maximum life time signatures so that the above loop
- * termination is sensible.
+ * Validate the dnskeyset against the current trusted keys.
*/
- result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
- &sig_diff, zone_keys, nkeys, zone->mctx, inception,
- soaexpire, check_ksk);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:add_sigs -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
+ for (result = dns_rdataset_first(&kfetch->dnskeysigset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->dnskeysigset)) {
+ dns_keynode_t *keynode = NULL;
- journalfile = dns_zone_getjournal(zone);
- if (journalfile != NULL) {
- dns_journal_t *journal = NULL;
- result = dns_journal_open(zone->mctx, journalfile,
- ISC_TRUE, &journal);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:dns_journal_open -> %s\n",
- dns_result_totext(result));
- goto failure;
- }
+ dns_rdata_reset(&sigrr);
+ dns_rdataset_current(&kfetch->dnskeysigset, &sigrr);
+ result = dns_rdata_tostruct(&sigrr, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
- result = dns_journal_write_transaction(journal, &sig_diff);
- dns_journal_destroy(&journal);
- if (result != ISC_R_SUCCESS) {
- dns_zone_log(zone, ISC_LOG_ERROR,
- "zone_sign:dns_journal_write_transaction -> %s\n",
- dns_result_totext(result));
- goto failure;
+ result = dns_keytable_find(secroots, keyname, &keynode);
+ while (result == ISC_R_SUCCESS) {
+ dns_keynode_t *nextnode = NULL;
+ dns_fixedname_t fixed;
+ dns_fixedname_init(&fixed);
+
+ dstkey = dns_keynode_key(keynode);
+ if (dstkey == NULL) /* fail_secure() was called */
+ break;
+
+ if (dst_key_alg(dstkey) == sig.algorithm &&
+ dst_key_id(dstkey) == sig.keyid) {
+ result = dns_dnssec_verify2(keyname,
+ &kfetch->dnskeyset,
+ dstkey, ISC_FALSE,
+ zone->view->mctx, &sigrr,
+ dns_fixedname_name(&fixed));
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Verifying DNSKEY set for zone "
+ "'%s': %s", namebuf,
+ dns_result_totext(result));
+
+ if (result == ISC_R_SUCCESS) {
+ kfetch->dnskeyset.trust =
+ dns_trust_secure;
+ kfetch->dnskeysigset.trust =
+ dns_trust_secure;
+ dns_keytable_detachkeynode(secroots,
+ &keynode);
+ break;
+ }
+ }
+
+ result = dns_keytable_nextkeynode(secroots,
+ keynode, &nextnode);
+ dns_keytable_detachkeynode(secroots, &keynode);
+ keynode = nextnode;
}
- }
+ if (kfetch->dnskeyset.trust == dns_trust_secure)
+ break;
+ }
/*
- * Pause all iterators so that dns_db_closeversion() can succeed.
+ * If we were not able to verify the answer using the current
+ * trusted keys then all we can do is look at any revoked keys.
*/
- for (signing = ISC_LIST_HEAD(zone->signing);
- signing != NULL;
- signing = ISC_LIST_NEXT(signing, link))
- dns_dbiterator_pause(signing->dbiterator);
-
- for (signing = ISC_LIST_HEAD(cleanup);
- signing != NULL;
- signing = ISC_LIST_NEXT(signing, link))
- dns_dbiterator_pause(signing->dbiterator);
+ secure = ISC_TF(kfetch->dnskeyset.trust == dns_trust_secure);
/*
- * Everything has succeeded. Commit the changes.
+ * First scan keydataset to find keys that are not in dnskeyset
+ * - Missing keys which are not scheduled for removal,
+ * log a warning
+ * - Missing keys which are scheduled for removal and
+ * the remove hold-down timer has completed should
+ * be removed from the key zone
+ * - Missing keys whose acceptance timers have not yet
+ * completed, log a warning and reset the acceptance
+ * timer to 30 days in the future
+ * - All keys not being removed have their refresh timers
+ * updated
*/
- dns_db_closeversion(db, &version, ISC_TRUE);
+ initializing = ISC_TRUE;
+ for (result = dns_rdataset_first(&kfetch->keydataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->keydataset)) {
+ dns_rdata_reset(&keydatarr);
+ dns_rdataset_current(&kfetch->keydataset, &keydatarr);
+ dns_rdata_tostruct(&keydatarr, &keydata, NULL);
+
+ /*
+ * If any keydata record has a nonzero add holddown, then
+ * there was a pre-existing trust anchor for this domain;
+ * that means we are *not* initializing it and shouldn't
+ * automatically trust all the keys we find at the zone apex.
+ */
+ initializing = initializing && ISC_TF(keydata.addhd == 0);
+
+ if (! matchkey(&kfetch->dnskeyset, &keydatarr)) {
+ isc_boolean_t deletekey = ISC_FALSE;
+
+ if (!secure) {
+ if (now > keydata.removehd)
+ deletekey = ISC_TRUE;
+ } else if (now < keydata.addhd) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Pending key unexpectedly missing "
+ "from %s; restarting acceptance "
+ "timer", namebuf);
+ keydata.addhd = now + MONTH;
+ keydata.refresh = refresh_time(kfetch,
+ ISC_FALSE);
+ } else if (keydata.addhd == 0) {
+ keydata.addhd = now;
+ } else if (keydata.removehd == 0) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Active key unexpectedly missing "
+ "from %s", namebuf);
+ keydata.refresh = now + HOUR;
+ } else if (now > keydata.removehd) {
+ deletekey = ISC_TRUE;
+ } else {
+ keydata.refresh = refresh_time(kfetch,
+ ISC_FALSE);
+ }
+
+ if (secure || deletekey) {
+ /* Delete old version */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_DEL, keyname, 0,
+ &keydatarr));
+ changed = ISC_TRUE;
+ }
+
+ if (!secure || deletekey)
+ continue;
+
+ dns_rdata_reset(&keydatarr);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_rdata_fromstruct(&keydatarr, zone->rdclass,
+ dns_rdatatype_keydata,
+ &keydata, &keyb);
+
+ /* Insert updated version */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_ADD, keyname, 0,
+ &keydatarr));
+ changed = ISC_TRUE;
+
+ set_refreshkeytimer(zone, &keydata, now);
+ }
+ }
/*
- * Everything succeeded so we can clean these up now.
+ * Next scan dnskeyset:
+ * - If new keys are found (i.e., lacking a match in keydataset)
+ * add them to the key zone and set the acceptance timer
+ * to 30 days in the future (or to immediately if we've
+ * determined that we're initializing the zone for the
+ * first time)
+ * - Previously-known keys that have been revoked
+ * must be scheduled for removal from the key zone (or,
+ * if they hadn't been accepted as trust anchors yet
+ * anyway, removed at once)
+ * - Previously-known unrevoked keys whose acceptance timers
+ * have completed are promoted to trust anchors
+ * - All keys not being removed have their refresh
+ * timers updated
*/
- signing = ISC_LIST_HEAD(cleanup);
- while (signing != NULL) {
- ISC_LIST_UNLINK(cleanup, signing, link);
- dns_db_detach(&signing->db);
- dns_dbiterator_destroy(&signing->dbiterator);
- isc_mem_put(zone->mctx, signing, sizeof *signing);
- signing = ISC_LIST_HEAD(cleanup);
- }
+ for (result = dns_rdataset_first(&kfetch->dnskeyset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->dnskeyset)) {
+ isc_boolean_t revoked = ISC_FALSE;
+ isc_boolean_t newkey = ISC_FALSE;
+ isc_boolean_t updatekey = ISC_FALSE;
+ isc_boolean_t deletekey = ISC_FALSE;
+ isc_boolean_t trustkey = ISC_FALSE;
+
+ dns_rdata_reset(&dnskeyrr);
+ dns_rdataset_current(&kfetch->dnskeyset, &dnskeyrr);
+ dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL);
+
+ /* Skip ZSK's */
+ if (!ISC_TF(dnskey.flags & DNS_KEYFLAG_KSK))
+ continue;
- set_resigntime(zone);
+ revoked = ISC_TF(dnskey.flags & DNS_KEYFLAG_REVOKE);
+
+ if (matchkey(&kfetch->keydataset, &dnskeyrr)) {
+ dns_rdata_reset(&keydatarr);
+ dns_rdataset_current(&kfetch->keydataset, &keydatarr);
+ dns_rdata_tostruct(&keydatarr, &keydata, NULL);
+
+ if (revoked && revocable(kfetch, &keydata)) {
+ if (keydata.addhd > now) {
+ /*
+ * Key wasn't trusted yet, and now
+ * it's been revoked? Just remove it
+ */
+ deletekey = ISC_TRUE;
+ } else if (keydata.removehd == 0) {
+ /* Remove from secroots */
+ untrust_key(zone->view->viewlist,
+ keyname, mctx, &dnskey);
+
+ /* If initializing, delete now */
+ if (keydata.addhd == 0)
+ deletekey = ISC_TRUE;
+ else
+ keydata.removehd = now + MONTH;
+ } else if (keydata.removehd < now) {
+ /* Scheduled for removal */
+ deletekey = ISC_TRUE;
+ }
+ } else if (revoked) {
+ if (secure && keydata.removehd == 0) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Active key for zone "
+ "'%s' is revoked but "
+ "did not self-sign; "
+ "ignoring.", namebuf);
+ continue;
+ }
+ } else if (secure) {
+ if (keydata.removehd != 0) {
+ /*
+ * Key isn't revoked--but it
+ * seems it used to be.
+ * Remove it now and add it
+ * back as if it were a fresh key.
+ */
+ deletekey = ISC_TRUE;
+ newkey = ISC_TRUE;
+ } else if (keydata.addhd > now)
+ pending++;
+ else if (keydata.addhd == 0)
+ keydata.addhd = now;
+
+ if (keydata.addhd <= now)
+ trustkey = ISC_TRUE;
+ }
+
+ if (!deletekey && !newkey)
+ updatekey = ISC_TRUE;
+ } else if (secure) {
+ /*
+ * Key wasn't in the key zone but it's
+ * revoked now anyway, so just skip it
+ */
+ if (revoked)
+ continue;
+
+ /* Key wasn't in the key zone: add it */
+ newkey = ISC_TRUE;
+
+ if (initializing) {
+ dns_keytag_t tag = 0;
+ CHECK(compute_tag(keyname, &dnskey,
+ mctx, &tag));
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Initializing automatic trust "
+ "anchor management for zone '%s'; "
+ "DNSKEY ID %d is now trusted, "
+ "waiving the normal 30-day "
+ "waiting period.",
+ namebuf, tag);
+ trustkey = ISC_TRUE;
+ }
+ }
+
+ /* Delete old version */
+ if (deletekey || !newkey) {
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_DEL, keyname, 0,
+ &keydatarr));
+ changed = ISC_TRUE;
+ }
+
+ if (updatekey) {
+ /* Set refresh timer */
+ keydata.refresh = refresh_time(kfetch, ISC_FALSE);
+ dns_rdata_reset(&keydatarr);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_rdata_fromstruct(&keydatarr, zone->rdclass,
+ dns_rdatatype_keydata,
+ &keydata, &keyb);
+
+ /* Insert updated version */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_ADD, keyname, 0,
+ &keydatarr));
+ changed = ISC_TRUE;
+ } else if (newkey) {
+ /* Convert DNSKEY to KEYDATA */
+ dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL);
+ dns_keydata_fromdnskey(&keydata, &dnskey, 0, 0, 0,
+ NULL);
+ keydata.addhd = initializing ? now : now + MONTH;
+ keydata.refresh = refresh_time(kfetch, ISC_FALSE);
+ dns_rdata_reset(&keydatarr);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_rdata_fromstruct(&keydatarr, zone->rdclass,
+ dns_rdatatype_keydata,
+ &keydata, &keyb);
+
+ /* Insert into key zone */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_ADD, keyname, 0,
+ &keydatarr));
+ changed = ISC_TRUE;
+ }
+
+ if (trustkey) {
+ /* Trust this key in all views */
+ dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL);
+ trust_key(zone->view->viewlist, keyname, &dnskey,
+ mctx);
+ }
+
+ if (!deletekey)
+ set_refreshkeytimer(zone, &keydata, now);
+ }
+
+ /*
+ * RFC5011 says, "A trust point that has all of its trust anchors
+ * revoked is considered deleted and is treated as if the trust
+ * point was never configured." But if someone revoked their
+ * active key before the standby was trusted, that would mean the
+ * zone would suddenly be nonsecured. We avoid this by checking to
+ * see if there's pending keydata. If so, we put a null key in
+ * the security roots; then all queries to the zone will fail.
+ */
+ if (pending != 0)
+ fail_secure(zone->view->viewlist, keyname);
+
+ failure:
+ if (changed) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ }
- LOCK_ZONE(zone);
- zone_needdump(zone, DNS_DUMP_DELAY);
UNLOCK_ZONE(zone);
- failure:
- /*
- * Rollback the cleanup list.
- */
- signing = ISC_LIST_HEAD(cleanup);
- while (signing != NULL) {
- ISC_LIST_UNLINK(cleanup, signing, link);
- ISC_LIST_APPEND(zone->signing, signing, link);
- dns_dbiterator_first(signing->dbiterator);
- dns_dbiterator_pause(signing->dbiterator);
- signing = ISC_LIST_HEAD(cleanup);
+ /* Write changes to journal file. */
+ if (alldone) {
+ result = increment_soa_serial(kfetch->db, ver, &diff, mctx);
+ if (result == ISC_R_SUCCESS)
+ result = zone_journal(zone, &diff, "keyfetch_done");
}
- for (signing = ISC_LIST_HEAD(zone->signing);
- signing != NULL;
- signing = ISC_LIST_NEXT(signing, link))
- dns_dbiterator_pause(signing->dbiterator);
+ dns_diff_clear(&diff);
+ dns_db_closeversion(kfetch->db, &ver, changed);
+ dns_db_detach(&kfetch->db);
+ dns_zone_detach(&kfetch->zone);
- dns_diff_clear(&sig_diff);
+ if (dns_rdataset_isassociated(&kfetch->keydataset))
+ dns_rdataset_disassociate(&kfetch->keydataset);
+ if (dns_rdataset_isassociated(&kfetch->dnskeyset))
+ dns_rdataset_disassociate(&kfetch->dnskeyset);
+ if (dns_rdataset_isassociated(&kfetch->dnskeysigset))
+ dns_rdataset_disassociate(&kfetch->dnskeysigset);
- for (i = 0; i < nkeys; i++)
- dst_key_free(&zone_keys[i]);
+ dns_name_free(keyname, mctx);
+ isc_mem_put(mctx, kfetch, sizeof(dns_keyfetch_t));
+ isc_mem_detach(&mctx);
- if (version != NULL) {
- dns_db_closeversion(db, &version, ISC_FALSE);
- dns_db_detach(&db);
- } else if (db != NULL)
- dns_db_detach(&db);
+ if (secroots != NULL)
+ dns_keytable_detach(&secroots);
+}
- if (ISC_LIST_HEAD(zone->signing) != NULL) {
- isc_interval_t i;
- if (zone->update_disabled || result != ISC_R_SUCCESS)
- isc_interval_set(&i, 60, 0); /* 1 minute */
- else
- isc_interval_set(&i, 0, 10000000); /* 10 ms */
- isc_time_nowplusinterval(&zone->signingtime, &i);
- } else
- isc_time_settoepoch(&zone->signingtime);
+/*
+ * Refresh the data in the key zone. Initiate a fetch to get new DNSKEY
+ * records from the zone apex.
+ */
+static void
+zone_refreshkeys(dns_zone_t *zone) {
+ const char me[] = "zone_refreshkeys";
+ isc_result_t result;
+ dns_rriterator_t rrit;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t kd;
+ isc_stdtime_t now;
+ isc_boolean_t commit = ISC_FALSE;
+
+ ENTER;
+ REQUIRE(zone->db != NULL);
+
+ isc_stdtime_get(&now);
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ LOCK_ZONE(zone);
+ dns_db_newversion(db, &ver);
+ dns_diff_init(zone->mctx, &diff);
+
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_REFRESHING);
+
+ dns_rriterator_init(&rrit, db, ver, 0);
+ for (result = dns_rriterator_first(&rrit);
+ result == ISC_R_SUCCESS;
+ result = dns_rriterator_nextrrset(&rrit)) {
+ isc_stdtime_t timer = 0xffffffff;
+ dns_keyfetch_t *kfetch;
+ dns_rdataset_t *kdset;
+ dns_name_t *name = NULL;
+ isc_uint32_t ttl;
+
+ dns_rriterator_current(&rrit, &name, &ttl, &kdset, NULL);
+ if (!dns_rdataset_isassociated(kdset))
+ continue;
+
+ if (kdset->type != dns_rdatatype_keydata)
+ continue;
+
+ /*
+ * Scan the stored keys looking for ones that need
+ * removal or refreshing
+ */
+ for (result = dns_rdataset_first(kdset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(kdset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(kdset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &kd, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /* Removal timer expired? */
+ if (kd.removehd != 0 && kd.removehd < now) {
+ CHECK(update_one_rr(db, ver, &diff,
+ DNS_DIFFOP_DEL, name, ttl,
+ &rdata));
+ continue;
+ }
+
+ /* Acceptance timer expired? */
+ if (kd.addhd != 0 && kd.addhd < now)
+ timer = kd.addhd;
+
+ /* Or do we just need to refresh the keyset? */
+ if (timer > kd.refresh)
+ timer = kd.refresh;
+ }
+
+ if (timer > now)
+ continue;
+
+ zone->refreshkeycount++;
+
+ kfetch = isc_mem_get(zone->mctx, sizeof(dns_keyfetch_t));
+ kfetch->zone = NULL;
+ dns_zone_attach(zone, &kfetch->zone);
+ dns_fixedname_init(&kfetch->name);
+ dns_name_dup(name, zone->mctx,
+ dns_fixedname_name(&kfetch->name));
+ dns_rdataset_init(&kfetch->dnskeyset);
+ dns_rdataset_init(&kfetch->dnskeysigset);
+ dns_rdataset_init(&kfetch->keydataset);
+ dns_rdataset_clone(kdset, &kfetch->keydataset);
+ kfetch->db = NULL;
+ dns_db_attach(db, &kfetch->db);
+ kfetch->fetch = NULL;
+
+ dns_resolver_createfetch(zone->view->resolver,
+ dns_fixedname_name(&kfetch->name),
+ dns_rdatatype_dnskey,
+ NULL, NULL, NULL,
+ DNS_FETCHOPT_NOVALIDATE,
+ zone->task, keyfetch_done, kfetch,
+ &kfetch->dnskeyset,
+ &kfetch->dnskeysigset,
+ &kfetch->fetch);
+ }
+ if (!ISC_LIST_EMPTY(diff.tuples)) {
+ CHECK(increment_soa_serial(db, ver, &diff, zone->mctx));
+ commit = ISC_TRUE;
+ zone_journal(zone, &diff, "sync_keyzone");
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ }
+ failure:
+ UNLOCK_ZONE(zone);
+
+ dns_rriterator_destroy(&rrit);
+ dns_diff_clear(&diff);
+ dns_db_closeversion(db, &ver, commit);
+ dns_db_detach(&db);
}
static void
/*
* Configuring the view of this zone may have
* failed, for example because the config file
- * had a syntax error. In that case, the view
+ * had a syntax error. In that case, the view
* adb or resolver, and we had better not try
* to do maintenance on it.
*/
switch (zone->type) {
case dns_zone_master:
case dns_zone_slave:
+ case dns_zone_key:
LOCK_ZONE(zone);
if (zone->masterfile != NULL &&
isc_time_compare(&now, &zone->dumptime) >= 0 &&
break;
}
+ /*
+ * Do we need to refresh keys?
+ */
+ switch (zone->type) {
+ case dns_zone_key:
+ if (isc_time_compare(&now, &zone->refreshkeytime) >= 0 &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING))
+ zone_refreshkeys(zone);
+ break;
+ case dns_zone_master:
+ if (!isc_time_isepoch(&zone->refreshkeytime) &&
+ isc_time_compare(&now, &zone->refreshkeytime) >= 0)
+ zone_rekey(zone);
+ default:
+ break;
+ }
+
switch (zone->type) {
case dns_zone_master:
case dns_zone_slave:
isc_sockaddr_t any;
isc_boolean_t isself;
isc_netaddr_t dstaddr;
+ isc_result_t result;
if (zone->view == NULL || zone->isself == NULL)
return (ISC_FALSE);
src = *dst;
isc_netaddr_fromsockaddr(&dstaddr, dst);
- (void)dns_view_getpeertsig(zone->view, &dstaddr, &key);
+ result = dns_view_getpeertsig(zone->view, &dstaddr, &key);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ return (ISC_FALSE);
isself = (zone->isself)(zone->view, key, &src, dst, zone->rdclass,
zone->isselfarg);
if (key != NULL)
goto cleanup;
isc_netaddr_fromsockaddr(&dstip, ¬ify->dst);
- (void)dns_view_getpeertsig(notify->zone->view, &dstip, &key);
-
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;
+ }
+
notify_log(notify->zone, ISC_LOG_DEBUG(3), "sending notify to %s",
addrbuf);
if (notify->zone->view->peers != NULL) {
cleanup_key:
if (key != NULL)
dns_tsigkey_detach(&key);
+ cleanup_message:
dns_message_destroy(&message);
cleanup:
UNLOCK_ZONE(notify->zone);
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_soa_t soa;
isc_result_t result;
- isc_uint32_t serial;
+ isc_uint32_t serial, oldserial;
unsigned int j;
zone = revent->ev_arg;
RUNTIME_CHECK(result == ISC_R_SUCCESS);
serial = soa.serial;
-
- zone_debuglog(zone, me, 1, "serial: new %u, old %u",
- serial, zone->serial);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ result = dns_zone_getserial2(zone, &oldserial);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ zone_debuglog(zone, me, 1, "serial: new %u, old %u",
+ serial, oldserial);
+ } else
+ zone_debuglog(zone, me, 1, "serial: new %u, old not loaded",
+ serial);
if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) ||
DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) ||
- isc_serial_gt(serial, zone->serial)) {
+ isc_serial_gt(serial, oldserial)) {
if (dns_zonemgr_unreachable(zone->zmgr, &zone->masteraddr,
&zone->sourceaddr, &now)) {
dns_zone_log(zone, ISC_LOG_INFO,
}
if (msg != NULL)
dns_message_destroy(&msg);
- } else if (isc_serial_eq(soa.serial, zone->serial)) {
+ } else if (isc_serial_eq(soa.serial, oldserial)) {
if (zone->masterfile != NULL) {
result = ISC_R_FAILURE;
if (zone->journal != NULL)
if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MULTIMASTER))
dns_zone_log(zone, ISC_LOG_INFO, "serial number (%u) "
"received from master %s < ours (%u)",
- soa.serial, master, zone->serial);
+ soa.serial, master, oldserial);
else
zone_debuglog(zone, me, 1, "ahead");
zone->mastersok[zone->curmaster] = ISC_TRUE;
dns_name_format(keyname, namebuf, sizeof(namebuf));
dns_zone_log(zone, ISC_LOG_ERROR,
"unable to find key: %s", namebuf);
+ goto skip_master;
+ }
+ }
+ if (key == NULL) {
+ result = dns_view_getpeertsig(zone->view, &masterip, &key);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+ char addrbuf[ISC_NETADDR_FORMATSIZE];
+ isc_netaddr_format(&masterip, addrbuf, sizeof(addrbuf));
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "unable to find TSIG key for %s", addrbuf);
+ goto skip_master;
}
}
- if (key == NULL)
- (void)dns_view_getpeertsig(zone->view, &masterip, &key);
have_xfrsource = ISC_FALSE;
reqnsid = zone->view->requestnsid;
/*
* We have now canceled everything set the flag to allow exit_check()
- * to succeed. We must not unlock between setting this flag and
+ * to succeed. We must not unlock between setting this flag and
* calling exit_check().
*/
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SHUTDOWN);
isc_time_t next;
isc_result_t result;
+ ENTER;
REQUIRE(DNS_ZONE_VALID(zone));
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING))
return;
isc_time_compare(&zone->dumptime, &next) < 0)
next = zone->dumptime;
}
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING) &&
+ !isc_time_isepoch(&zone->refreshkeytime)) {
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->refreshkeytime, &next) < 0)
+ next = zone->refreshkeytime;
+ }
if (!isc_time_isepoch(&zone->resigntime)) {
if (isc_time_isepoch(&next) ||
isc_time_compare(&zone->resigntime, &next) < 0)
}
break;
+ case dns_zone_key:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) {
+ INSIST(!isc_time_isepoch(&zone->dumptime));
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->dumptime, &next) < 0)
+ next = zone->dumptime;
+ }
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING)) {
+ if (isc_time_isepoch(&next) ||
+ (!isc_time_isepoch(&zone->refreshkeytime) &&
+ isc_time_compare(&zone->refreshkeytime, &next) < 0))
+ next = zone->refreshkeytime;
+ }
+ break;
+
default:
break;
}
REQUIRE(DNS_ZONE_VALID(zone));
/*
- * If type != T_SOA return DNS_R_REFUSED. We don't yet support
+ * If type != T_SOA return DNS_R_NOTIMP. We don't yet support
* ROLLOVER.
*
* SOA: RFC1996
if (result == ISC_R_SUCCESS)
result = dns_rdataset_first(rdataset);
if (result == ISC_R_SUCCESS) {
- isc_uint32_t serial = 0;
+ isc_uint32_t serial = 0, oldserial;
dns_rdataset_current(rdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &soa, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
serial = soa.serial;
- if (isc_serial_le(serial, zone->serial)) {
+ /*
+ * The following should safely be performed without DB
+ * lock and succeed in this context.
+ */
+ result = zone_get_from_db(zone, zone->db, NULL, NULL,
+ &oldserial, NULL, NULL, NULL,
+ NULL, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (isc_serial_le(serial, oldserial)) {
dns_zone_log(zone, ISC_LOG_INFO,
"notify from %s: "
"zone is up to date",
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
isc_log_write(dns_lctx, category, DNS_LOGMODULE_ZONE,
- level, "zone %s: %s", zone->strnamerd, message);
+ level, "%s %s: %s", (zone->type == dns_zone_key) ?
+ "managed-keys-zone" : "zone", zone->strnamerd, message);
}
void
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
- level, "zone %s: %s", zone->strnamerd, message);
+ level, "%s %s: %s", (zone->type == dns_zone_key) ?
+ "managed-keys-zone" : "zone", zone->strnamerd, message);
}
static void
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
- level, "%s: zone %s: %s", me, zone->strnamerd, message);
+ level, "%s: %s %s: %s", me, zone->type != dns_zone_key ?
+ "zone" : "managed-keys-zone", zone->strnamerd, message);
}
static int
dns_result_totext(result));
/*
- * Old bind's return formerr if they see a soa record. Retry w/o
+ * Old bind's return formerr if they see a soa record. Retry w/o
* the soa if we see a formerr and had sent a SOA.
*/
isc_event_free(&event);
"has %d SOA records", soacount);
result = DNS_R_BADZONE;
}
- if (nscount == 0) {
+ if (nscount == 0 && zone->type != dns_zone_key) {
dns_zone_log(zone, ISC_LOG_ERROR, "has no NS records");
result = DNS_R_BADZONE;
}
if (zone->db != NULL && zone->journal != NULL &&
DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) &&
!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) {
- isc_uint32_t serial;
+ isc_uint32_t serial, oldserial;
dns_zone_log(zone, ISC_LOG_DEBUG(3), "generating diffs");
/*
* This is checked in zone_postload() for master zones.
*/
+ result = zone_get_from_db(zone, zone->db, NULL, NULL,
+ &oldserial, NULL, NULL, NULL, NULL,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (zone->type == dns_zone_slave &&
- !isc_serial_gt(serial, zone->serial)) {
+ !isc_serial_gt(serial, oldserial)) {
isc_uint32_t serialmin, serialmax;
- serialmin = (zone->serial + 1) & 0xffffffffU;
- serialmax = (zone->serial + 0x7fffffffU) & 0xffffffffU;
+ serialmin = (oldserial + 1) & 0xffffffffU;
+ serialmax = (oldserial + 0x7fffffffU) & 0xffffffffU;
dns_zone_log(zone, ISC_LOG_ERROR,
"ixfr-from-differences: failed: "
"new serial (%u) out of range [%u - %u]",
}
} else {
if (dump && zone->masterfile != NULL) {
- isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
- DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
- "dumping new zone version");
- result = dns_db_dump2(db, ver, zone->masterfile,
- zone->masterformat);
- if (result != ISC_R_SUCCESS)
- goto fail;
-
/*
- * Update the time the zone was updated, so
- * dns_zone_load can avoid loading it when
- * the server is reloaded. If isc_time_now
- * fails for some reason, all that happens is
- * the timestamp is not updated.
+ * If DNS_ZONEFLG_FORCEXFER was set we don't want
+ * to keep the old masterfile.
*/
- TIME_NOW(&zone->loadtime);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) &&
+ remove(zone->masterfile) < 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 masterfile "
+ "'%s': '%s'",
+ zone->masterfile, strbuf);
+ }
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) == 0)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NODELAY);
+ else
+ zone_needdump(zone, 0);
}
-
if (dump && zone->journal != NULL) {
/*
* The in-memory database just changed, and
* being loaded from disk. Also, we have not
* journaled diffs for this change.
* Therefore, the on-disk journal is missing
- * the deltas for this change. Since it can
+ * the deltas for this change. Since it can
* no longer be used to bring the zone
* up-to-date, it is useless and should be
* removed.
zone_unload(zone);
goto next_master;
}
- zone->serial = serial;
zone->refresh = RANGE(refresh, zone->minrefresh,
zone->maxrefresh);
zone->retry = RANGE(retry, zone->minretry,
buf[0] = '\0';
dns_zone_log(zone, ISC_LOG_INFO,
"transferred serial %u%s",
- zone->serial, buf);
+ serial, buf);
}
/*
&now);
/* Someone removed the file from underneath us! */
if (result == ISC_R_FILENOTFOUND &&
- zone->masterfile != NULL)
- zone_needdump(zone, DNS_DUMP_DELAY);
- else if (result != ISC_R_SUCCESS)
+ zone->masterfile != NULL) {
+ unsigned int delay = DNS_DUMP_DELAY;
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NODELAY))
+ delay = 0;
+ zone_needdump(zone, delay);
+ } else if (result != ISC_R_SUCCESS)
dns_zone_log(zone, ISC_LOG_ERROR,
"transfer: could not set file "
"modification time of '%s': %s",
zone->masterfile,
dns_result_totext(result));
}
-
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NODELAY);
inc_stats(zone, dns_zonestatscounter_xfrsuccess);
break;
(void)zone_postload(load->zone, load->db, load->loadtime, result);
zonemgr_putio(&load->zone->readio);
DNS_ZONE_CLRFLAG(load->zone, DNS_ZONEFLG_LOADING);
+ /*
+ * 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))
+ zone->update_disabled = ISC_FALSE;
+ DNS_ZONE_CLRFLAG(load->zone, DNS_ZONEFLG_THAW);
UNLOCK_ZONE(load->zone);
load->magic = 0;
zmgr->magic = ZONEMGR_MAGIC;
- *zmgrp = zmgr;
- return (ISC_R_SUCCESS);
+ *zmgrp = zmgr;
+ return (ISC_R_SUCCESS);
+
+#if 0
+ free_iolock:
+ DESTROYLOCK(&zmgr->iolock);
+#endif
+ free_rl:
+ isc_ratelimiter_detach(&zmgr->rl);
+ free_task:
+ isc_task_detach(&zmgr->task);
+ free_taskpool:
+ isc_taskpool_destroy(&zmgr->zonetasks);
+ free_rwlock:
+ isc_rwlock_destroy(&zmgr->rwlock);
+ free_mem:
+ isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr));
+ isc_mem_detach(&mctx);
+ return (result);
+}
+
+isc_result_t
+dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
+ isc_result_t result;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ LOCK_ZONE(zone);
+ REQUIRE(zone->task == NULL);
+ REQUIRE(zone->timer == NULL);
+ REQUIRE(zone->zmgr == NULL);
+
+ isc_taskpool_gettask(zmgr->zonetasks,
+ dns_name_hash(dns_zone_getorigin(zone),
+ ISC_FALSE),
+ &zone->task);
+
+ /*
+ * Set the task name. The tag will arbitrarily point to one
+ * of the zones sharing the task (in practice, the one
+ * to be managed last).
+ */
+ isc_task_setname(zone->task, "zone", zone);
+
+ result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive,
+ NULL, NULL,
+ zone->task, zone_timer, zone,
+ &zone->timer);
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_task;
+
+ /*
+ * The timer "holds" a iref.
+ */
+ zone->irefs++;
+ INSIST(zone->irefs != 0);
+
+ ISC_LIST_APPEND(zmgr->zones, zone, link);
+ zone->zmgr = zmgr;
+ zmgr->refs++;
+
+ goto unlock;
+
+ cleanup_task:
+ isc_task_detach(&zone->task);
+
+ unlock:
+ UNLOCK_ZONE(zone);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ return (result);
+}
+
+void
+dns_zonemgr_releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
+ isc_boolean_t free_now = ISC_FALSE;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(zone->zmgr == zmgr);
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ LOCK_ZONE(zone);
+
+ ISC_LIST_UNLINK(zmgr->zones, zone, link);
+ zone->zmgr = NULL;
+ zmgr->refs--;
+ if (zmgr->refs == 0)
+ free_now = ISC_TRUE;
+
+ UNLOCK_ZONE(zone);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+
+ if (free_now)
+ zonemgr_free(zmgr);
+ ENSURE(zone->zmgr == NULL);
+}
+
+void
+dns_zonemgr_attach(dns_zonemgr_t *source, dns_zonemgr_t **target) {
+ REQUIRE(DNS_ZONEMGR_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+
+ RWLOCK(&source->rwlock, isc_rwlocktype_write);
+ REQUIRE(source->refs > 0);
+ source->refs++;
+ INSIST(source->refs > 0);
+ RWUNLOCK(&source->rwlock, isc_rwlocktype_write);
+ *target = source;
+}
+
+void
+dns_zonemgr_detach(dns_zonemgr_t **zmgrp) {
+ dns_zonemgr_t *zmgr;
+ isc_boolean_t free_now = ISC_FALSE;
+
+ REQUIRE(zmgrp != NULL);
+ zmgr = *zmgrp;
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ zmgr->refs--;
+ if (zmgr->refs == 0)
+ free_now = ISC_TRUE;
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+
+ if (free_now)
+ zonemgr_free(zmgr);
+}
+
+isc_result_t
+dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr) {
+ dns_zone_t *p;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+ for (p = ISC_LIST_HEAD(zmgr->zones);
+ p != NULL;
+ p = ISC_LIST_NEXT(p, link))
+ {
+ dns_zone_maintenance(p);
+ }
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+
+ /*
+ * Recent configuration changes may have increased the
+ * amount of available transfers quota. Make sure any
+ * transfers currently blocked on quota get started if
+ * possible.
+ */
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ zmgr_resume_xfrs(zmgr, ISC_TRUE);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ zmgr_resume_xfrs(zmgr, ISC_TRUE);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+}
+
+void
+dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ isc_ratelimiter_shutdown(zmgr->rl);
+
+ if (zmgr->task != NULL)
+ isc_task_destroy(&zmgr->task);
+ if (zmgr->zonetasks != NULL)
+ isc_taskpool_destroy(&zmgr->zonetasks);
+}
+
+static void
+zonemgr_free(dns_zonemgr_t *zmgr) {
+ isc_mem_t *mctx;
+
+ INSIST(zmgr->refs == 0);
+ INSIST(ISC_LIST_EMPTY(zmgr->zones));
+
+ zmgr->magic = 0;
-#if 0
- free_iolock:
DESTROYLOCK(&zmgr->iolock);
-#endif
- free_rl:
isc_ratelimiter_detach(&zmgr->rl);
- free_task:
- isc_task_detach(&zmgr->task);
- free_taskpool:
- isc_taskpool_destroy(&zmgr->zonetasks);
- free_rwlock:
+
isc_rwlock_destroy(&zmgr->rwlock);
- free_mem:
+ mctx = zmgr->mctx;
isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr));
isc_mem_detach(&mctx);
- return (result);
}
-isc_result_t
-dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
- isc_result_t result;
+void
+dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, isc_uint32_t value) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- REQUIRE(DNS_ZONE_VALID(zone));
+ zmgr->transfersin = value;
+}
+
+isc_uint32_t
+dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr) {
REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- LOCK_ZONE(zone);
- REQUIRE(zone->task == NULL);
- REQUIRE(zone->timer == NULL);
- REQUIRE(zone->zmgr == NULL);
+ return (zmgr->transfersin);
+}
- isc_taskpool_gettask(zmgr->zonetasks,
- dns_name_hash(dns_zone_getorigin(zone),
- ISC_FALSE),
- &zone->task);
+void
+dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, isc_uint32_t value) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ zmgr->transfersperns = value;
+}
+
+isc_uint32_t
+dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->transfersperns);
+}
+
+/*
+ * Try to start a new incoming zone transfer to fill a quota
+ * slot that was just vacated.
+ *
+ * Requires:
+ * The zone manager is locked by the caller.
+ */
+static void
+zmgr_resume_xfrs(dns_zonemgr_t *zmgr, isc_boolean_t multi) {
+ dns_zone_t *zone;
+ dns_zone_t *next;
+
+ for (zone = ISC_LIST_HEAD(zmgr->waiting_for_xfrin);
+ zone != NULL;
+ zone = next)
+ {
+ isc_result_t result;
+ next = ISC_LIST_NEXT(zone, statelink);
+ result = zmgr_start_xfrin_ifquota(zmgr, zone);
+ if (result == ISC_R_SUCCESS) {
+ if (multi)
+ continue;
+ /*
+ * We successfully filled the slot. We're done.
+ */
+ break;
+ } else if (result == ISC_R_QUOTA) {
+ /*
+ * Not enough quota. This is probably the per-server
+ * quota, because we usually get called when a unit of
+ * global quota has just been freed. Try the next
+ * zone, it may succeed if it uses another master.
+ */
+ continue;
+ } else {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "starting zone transfer: %s",
+ isc_result_totext(result));
+ break;
+ }
+ }
+}
+
+/*
+ * Try to start an incoming zone transfer for 'zone', quota permitting.
+ *
+ * Requires:
+ * The zone manager is locked by the caller.
+ *
+ * Returns:
+ * ISC_R_SUCCESS There was enough quota and we attempted to
+ * start a transfer. zone_xfrdone() has been or will
+ * be called.
+ * ISC_R_QUOTA Not enough quota.
+ * Others Failure.
+ */
+static isc_result_t
+zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
+ dns_peer_t *peer = NULL;
+ isc_netaddr_t masterip;
+ isc_uint32_t nxfrsin, nxfrsperns;
+ dns_zone_t *x;
+ isc_uint32_t maxtransfersin, maxtransfersperns;
+ isc_event_t *e;
/*
- * Set the task name. The tag will arbitrarily point to one
- * of the zones sharing the task (in practice, the one
- * to be managed last).
+ * Find any configured information about the server we'd
+ * like to transfer this zone from.
*/
- isc_task_setname(zone->task, "zone", zone);
-
- result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive,
- NULL, NULL,
- zone->task, zone_timer, zone,
- &zone->timer);
+ isc_netaddr_fromsockaddr(&masterip, &zone->masteraddr);
+ (void)dns_peerlist_peerbyaddr(zone->view->peers,
+ &masterip, &peer);
- if (result != ISC_R_SUCCESS)
- goto cleanup_task;
+ /*
+ * Determine the total maximum number of simultaneous
+ * transfers allowed, and the maximum for this specific
+ * master.
+ */
+ maxtransfersin = zmgr->transfersin;
+ maxtransfersperns = zmgr->transfersperns;
+ if (peer != NULL)
+ (void)dns_peer_gettransfers(peer, &maxtransfersperns);
/*
- * The timer "holds" a iref.
+ * Count the total number of transfers that are in progress,
+ * and the number of transfers in progress from this master.
+ * We linearly scan a list of all transfers; if this turns
+ * out to be too slow, we could hash on the master address.
*/
- zone->irefs++;
- INSIST(zone->irefs != 0);
+ nxfrsin = nxfrsperns = 0;
+ for (x = ISC_LIST_HEAD(zmgr->xfrin_in_progress);
+ x != NULL;
+ x = ISC_LIST_NEXT(x, statelink))
+ {
+ isc_netaddr_t xip;
+ isc_netaddr_fromsockaddr(&xip, &x->masteraddr);
+ nxfrsin++;
+ if (isc_netaddr_equal(&xip, &masterip))
+ nxfrsperns++;
+ }
- ISC_LIST_APPEND(zmgr->zones, zone, link);
- zone->zmgr = zmgr;
- zmgr->refs++;
+ /* Enforce quota. */
+ if (nxfrsin >= maxtransfersin)
+ return (ISC_R_QUOTA);
- goto unlock;
+ if (nxfrsperns >= maxtransfersperns)
+ return (ISC_R_QUOTA);
- cleanup_task:
- isc_task_detach(&zone->task);
+ /*
+ * We have sufficient quota. Move the zone to the "xfrin_in_progress"
+ * list and send it an event to let it start the actual transfer in the
+ * context of its own task.
+ */
+ e = isc_event_allocate(zmgr->mctx, zmgr,
+ DNS_EVENT_ZONESTARTXFRIN,
+ got_transfer_quota, zone,
+ sizeof(isc_event_t));
+ if (e == NULL)
+ return (ISC_R_NOMEMORY);
- unlock:
+ LOCK_ZONE(zone);
+ INSIST(zone->statelist == &zmgr->waiting_for_xfrin);
+ ISC_LIST_UNLINK(zmgr->waiting_for_xfrin, zone, statelink);
+ ISC_LIST_APPEND(zmgr->xfrin_in_progress, zone, statelink);
+ zone->statelist = &zmgr->xfrin_in_progress;
+ isc_task_send(zone->task, &e);
+ dns_zone_log(zone, ISC_LOG_INFO, "Transfer started.");
UNLOCK_ZONE(zone);
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- return (result);
+
+ return (ISC_R_SUCCESS);
}
void
-dns_zonemgr_releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
- isc_boolean_t free_now = ISC_FALSE;
+dns_zonemgr_setiolimit(dns_zonemgr_t *zmgr, isc_uint32_t iolimit) {
- REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- REQUIRE(zone->zmgr == zmgr);
+ REQUIRE(iolimit > 0);
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- LOCK_ZONE(zone);
+ zmgr->iolimit = iolimit;
+}
- ISC_LIST_UNLINK(zmgr->zones, zone, link);
- zone->zmgr = NULL;
- zmgr->refs--;
- if (zmgr->refs == 0)
- free_now = ISC_TRUE;
+isc_uint32_t
+dns_zonemgr_getiolimit(dns_zonemgr_t *zmgr) {
- UNLOCK_ZONE(zone);
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- if (free_now)
- zonemgr_free(zmgr);
- ENSURE(zone->zmgr == NULL);
+ return (zmgr->iolimit);
}
-void
-dns_zonemgr_attach(dns_zonemgr_t *source, dns_zonemgr_t **target) {
- REQUIRE(DNS_ZONEMGR_VALID(source));
- REQUIRE(target != NULL && *target == NULL);
+/*
+ * Get permission to request a file handle from the OS.
+ * An event will be sent to action when one is available.
+ * There are two queues available (high and low), the high
+ * queue will be serviced before the low one.
+ *
+ * zonemgr_putio() must be called after the event is delivered to
+ * 'action'.
+ */
+
+static isc_result_t
+zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_io_t **iop)
+{
+ dns_io_t *io;
+ isc_boolean_t queue;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(iop != NULL && *iop == NULL);
+
+ 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;
+ isc_task_attach(task, &io->task);
+ ISC_LINK_INIT(io, link);
+ io->magic = IO_MAGIC;
+
+ LOCK(&zmgr->iolock);
+ zmgr->ioactive++;
+ queue = ISC_TF(zmgr->ioactive > zmgr->iolimit);
+ if (queue) {
+ if (io->high)
+ ISC_LIST_APPEND(zmgr->high, io, link);
+ else
+ ISC_LIST_APPEND(zmgr->low, io, link);
+ }
+ UNLOCK(&zmgr->iolock);
+ *iop = io;
- RWLOCK(&source->rwlock, isc_rwlocktype_write);
- REQUIRE(source->refs > 0);
- source->refs++;
- INSIST(source->refs > 0);
- RWUNLOCK(&source->rwlock, isc_rwlocktype_write);
- *target = source;
+ if (!queue) {
+ isc_task_send(io->task, &io->event);
+ }
+ return (ISC_R_SUCCESS);
}
-void
-dns_zonemgr_detach(dns_zonemgr_t **zmgrp) {
+static void
+zonemgr_putio(dns_io_t **iop) {
+ dns_io_t *io;
+ dns_io_t *next;
dns_zonemgr_t *zmgr;
- isc_boolean_t free_now = ISC_FALSE;
-
- REQUIRE(zmgrp != NULL);
- zmgr = *zmgrp;
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- zmgr->refs--;
- if (zmgr->refs == 0)
- free_now = ISC_TRUE;
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ REQUIRE(iop != NULL);
+ io = *iop;
+ REQUIRE(DNS_IO_VALID(io));
- if (free_now)
- zonemgr_free(zmgr);
-}
+ *iop = NULL;
-isc_result_t
-dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr) {
- dns_zone_t *p;
+ INSIST(!ISC_LINK_LINKED(io, link));
+ INSIST(io->event == NULL);
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ zmgr = io->zmgr;
+ isc_task_detach(&io->task);
+ io->magic = 0;
+ isc_mem_put(zmgr->mctx, io, sizeof(*io));
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_read);
- for (p = ISC_LIST_HEAD(zmgr->zones);
- p != NULL;
- p = ISC_LIST_NEXT(p, link))
- {
- dns_zone_maintenance(p);
+ LOCK(&zmgr->iolock);
+ INSIST(zmgr->ioactive > 0);
+ zmgr->ioactive--;
+ next = HEAD(zmgr->high);
+ if (next == NULL)
+ next = HEAD(zmgr->low);
+ if (next != NULL) {
+ if (next->high)
+ ISC_LIST_UNLINK(zmgr->high, next, link);
+ else
+ ISC_LIST_UNLINK(zmgr->low, next, link);
+ INSIST(next->event != NULL);
}
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read);
-
- /*
- * Recent configuration changes may have increased the
- * amount of available transfers quota. Make sure any
- * transfers currently blocked on quota get started if
- * possible.
- */
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- zmgr_resume_xfrs(zmgr, ISC_TRUE);
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- return (ISC_R_SUCCESS);
+ UNLOCK(&zmgr->iolock);
+ if (next != NULL)
+ isc_task_send(next->task, &next->event);
}
-void
-dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr) {
-
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
-
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- zmgr_resume_xfrs(zmgr, ISC_TRUE);
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
-}
+static void
+zonemgr_cancelio(dns_io_t *io) {
+ isc_boolean_t send_event = ISC_FALSE;
-void
-dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) {
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(DNS_IO_VALID(io));
- isc_ratelimiter_shutdown(zmgr->rl);
+ /*
+ * If we are queued to be run then dequeue.
+ */
+ LOCK(&io->zmgr->iolock);
+ if (ISC_LINK_LINKED(io, link)) {
+ if (io->high)
+ ISC_LIST_UNLINK(io->zmgr->high, io, link);
+ else
+ ISC_LIST_UNLINK(io->zmgr->low, io, link);
- if (zmgr->task != NULL)
- isc_task_destroy(&zmgr->task);
- if (zmgr->zonetasks != NULL)
- isc_taskpool_destroy(&zmgr->zonetasks);
+ send_event = ISC_TRUE;
+ INSIST(io->event != NULL);
+ }
+ UNLOCK(&io->zmgr->iolock);
+ if (send_event) {
+ io->event->ev_attributes |= ISC_EVENTATTR_CANCELED;
+ isc_task_send(io->task, &io->event);
+ }
}
static void
-zonemgr_free(dns_zonemgr_t *zmgr) {
- isc_mem_t *mctx;
+zone_saveunique(dns_zone_t *zone, const char *path, const char *templat) {
+ char *buf;
+ int buflen;
+ isc_result_t result;
- INSIST(zmgr->refs == 0);
- INSIST(ISC_LIST_EMPTY(zmgr->zones));
+ buflen = strlen(path) + strlen(templat) + 2;
- zmgr->magic = 0;
+ buf = isc_mem_get(zone->mctx, buflen);
+ if (buf == NULL)
+ return;
- DESTROYLOCK(&zmgr->iolock);
- isc_ratelimiter_detach(&zmgr->rl);
+ result = isc_file_template(path, templat, buf, buflen);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
- isc_rwlock_destroy(&zmgr->rwlock);
- mctx = zmgr->mctx;
- isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr));
- isc_mem_detach(&mctx);
-}
+ result = isc_file_renameunique(path, buf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
-void
-dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, isc_uint32_t value) {
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ dns_zone_log(zone, ISC_LOG_WARNING, "saved '%s' as '%s'",
+ path, buf);
- zmgr->transfersin = value;
+ cleanup:
+ isc_mem_put(zone->mctx, buf, buflen);
}
-isc_uint32_t
-dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr) {
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+#if 0
+/* Hook for ondestroy notification from a database. */
- return (zmgr->transfersin);
+static void
+dns_zonemgr_dbdestroyed(isc_task_t *task, isc_event_t *event) {
+ dns_db_t *db = event->sender;
+ UNUSED(task);
+
+ isc_event_free(&event);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
+ "database (%p) destroyed", (void*) db);
}
+#endif
void
-dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, isc_uint32_t value) {
+dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) {
+ isc_interval_t interval;
+ isc_uint32_t s, ns;
+ isc_uint32_t pertic;
+ isc_result_t result;
+
REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- zmgr->transfersperns = value;
-}
+ if (value == 0)
+ value = 1;
-isc_uint32_t
-dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr) {
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ if (value == 1) {
+ s = 1;
+ ns = 0;
+ pertic = 1;
+ } else if (value <= 10) {
+ s = 0;
+ ns = 1000000000 / value;
+ pertic = 1;
+ } else {
+ s = 0;
+ ns = (1000000000 / value) * 10;
+ pertic = 10;
+ }
- return (zmgr->transfersperns);
+ isc_interval_set(&interval, s, ns);
+ result = isc_ratelimiter_setinterval(zmgr->rl, &interval);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_ratelimiter_setpertic(zmgr->rl, pertic);
+
+ zmgr->serialqueryrate = value;
}
-/*
- * Try to start a new incoming zone transfer to fill a quota
- * slot that was just vacated.
- *
- * Requires:
- * The zone manager is locked by the caller.
- */
-static void
-zmgr_resume_xfrs(dns_zonemgr_t *zmgr, isc_boolean_t multi) {
- dns_zone_t *zone;
- dns_zone_t *next;
+unsigned int
+dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- for (zone = ISC_LIST_HEAD(zmgr->waiting_for_xfrin);
- zone != NULL;
- zone = next)
- {
- isc_result_t result;
- next = ISC_LIST_NEXT(zone, statelink);
- result = zmgr_start_xfrin_ifquota(zmgr, zone);
- if (result == ISC_R_SUCCESS) {
- if (multi)
- continue;
- /*
- * We successfully filled the slot. We're done.
- */
- break;
- } else if (result == ISC_R_QUOTA) {
- /*
- * Not enough quota. This is probably the per-server
- * quota, because we usually get called when a unit of
- * global quota has just been freed. Try the next
- * zone, it may succeed if it uses another master.
- */
- continue;
- } else {
- dns_zone_log(zone, ISC_LOG_DEBUG(1),
- "starting zone transfer: %s",
- isc_result_totext(result));
+ return (zmgr->serialqueryrate);
+}
+
+static isc_boolean_t
+dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local, isc_time_t *now)
+{
+ unsigned int i;
+ isc_rwlocktype_t locktype;
+ isc_result_t result;
+ isc_uint32_t seconds = isc_time_seconds(now);
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ locktype = isc_rwlocktype_read;
+ RWLOCK(&zmgr->rwlock, locktype);
+ for (i = 0; i < UNREACH_CHACHE_SIZE; i++) {
+ if (zmgr->unreachable[i].expire >= seconds &&
+ isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
+ isc_sockaddr_equal(&zmgr->unreachable[i].local, local)) {
+ result = isc_rwlock_tryupgrade(&zmgr->rwlock);
+ if (result == ISC_R_SUCCESS) {
+ locktype = isc_rwlocktype_write;
+ zmgr->unreachable[i].last = seconds;
+ }
break;
}
}
+ RWUNLOCK(&zmgr->rwlock, locktype);
+ return (ISC_TF(i < UNREACH_CHACHE_SIZE));
}
-/*
- * Try to start an incoming zone transfer for 'zone', quota permitting.
- *
- * Requires:
- * The zone manager is locked by the caller.
- *
- * Returns:
- * ISC_R_SUCCESS There was enough quota and we attempted to
- * start a transfer. zone_xfrdone() has been or will
- * be called.
- * ISC_R_QUOTA Not enough quota.
- * Others Failure.
- */
-static isc_result_t
-zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
- dns_peer_t *peer = NULL;
- isc_netaddr_t masterip;
- isc_uint32_t nxfrsin, nxfrsperns;
- dns_zone_t *x;
- isc_uint32_t maxtransfersin, maxtransfersperns;
- isc_event_t *e;
-
- /*
- * Find any configured information about the server we'd
- * like to transfer this zone from.
- */
- isc_netaddr_fromsockaddr(&masterip, &zone->masteraddr);
- (void)dns_peerlist_peerbyaddr(zone->view->peers,
- &masterip, &peer);
+void
+dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local, isc_time_t *now)
+{
+ isc_uint32_t seconds = isc_time_seconds(now);
+ isc_uint32_t last = seconds;
+ unsigned int i, slot = UNREACH_CHACHE_SIZE, oldest = 0;
- /*
- * Determine the total maximum number of simultaneous
- * transfers allowed, and the maximum for this specific
- * master.
- */
- maxtransfersin = zmgr->transfersin;
- maxtransfersperns = zmgr->transfersperns;
- if (peer != NULL)
- (void)dns_peer_gettransfers(peer, &maxtransfersperns);
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- /*
- * Count the total number of transfers that are in progress,
- * and the number of transfers in progress from this master.
- * We linearly scan a list of all transfers; if this turns
- * out to be too slow, we could hash on the master address.
- */
- nxfrsin = nxfrsperns = 0;
- for (x = ISC_LIST_HEAD(zmgr->xfrin_in_progress);
- x != NULL;
- x = ISC_LIST_NEXT(x, statelink))
- {
- isc_netaddr_t xip;
- isc_netaddr_fromsockaddr(&xip, &x->masteraddr);
- nxfrsin++;
- if (isc_netaddr_equal(&xip, &masterip))
- nxfrsperns++;
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ for (i = 0; i < UNREACH_CHACHE_SIZE; i++) {
+ /* Existing entry? */
+ if (isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
+ isc_sockaddr_equal(&zmgr->unreachable[i].local, local))
+ break;
+ /* Empty slot? */
+ if (zmgr->unreachable[i].expire < seconds)
+ slot = i;
+ /* Least recently used slot? */
+ if (zmgr->unreachable[i].last < last) {
+ last = zmgr->unreachable[i].last;
+ oldest = i;
+ }
}
+ if (i < UNREACH_CHACHE_SIZE) {
+ /*
+ * Found a existing entry. Update the expire timer and
+ * last usage timestamps.
+ */
+ zmgr->unreachable[i].expire = seconds + UNREACH_HOLD_TIME;
+ zmgr->unreachable[i].last = seconds;
+ } else if (slot != UNREACH_CHACHE_SIZE) {
+ /*
+ * Found a empty slot. Add a new entry to the cache.
+ */
+ zmgr->unreachable[slot].expire = seconds + UNREACH_HOLD_TIME;
+ zmgr->unreachable[slot].last = seconds;
+ zmgr->unreachable[slot].remote = *remote;
+ zmgr->unreachable[slot].local = *local;
+ } else {
+ /*
+ * Replace the least recently used entry in the cache.
+ */
+ zmgr->unreachable[oldest].expire = seconds + UNREACH_HOLD_TIME;
+ zmgr->unreachable[oldest].last = seconds;
+ zmgr->unreachable[oldest].remote = *remote;
+ zmgr->unreachable[oldest].local = *local;
+ }
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+}
- /* Enforce quota. */
- if (nxfrsin >= maxtransfersin)
- return (ISC_R_QUOTA);
-
- if (nxfrsperns >= maxtransfersperns)
- return (ISC_R_QUOTA);
+void
+dns_zone_forcereload(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- /*
- * We have sufficient quota. Move the zone to the "xfrin_in_progress"
- * list and send it an event to let it start the actual transfer in the
- * context of its own task.
- */
- e = isc_event_allocate(zmgr->mctx, zmgr,
- DNS_EVENT_ZONESTARTXFRIN,
- got_transfer_quota, zone,
- sizeof(isc_event_t));
- if (e == NULL)
- return (ISC_R_NOMEMORY);
+ if (zone->type == dns_zone_master)
+ return;
LOCK_ZONE(zone);
- INSIST(zone->statelist == &zmgr->waiting_for_xfrin);
- ISC_LIST_UNLINK(zmgr->waiting_for_xfrin, zone, statelink);
- ISC_LIST_APPEND(zmgr->xfrin_in_progress, zone, statelink);
- zone->statelist = &zmgr->xfrin_in_progress;
- isc_task_send(zone->task, &e);
- dns_zone_log(zone, ISC_LOG_INFO, "Transfer started.");
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_FORCEXFER);
UNLOCK_ZONE(zone);
-
- return (ISC_R_SUCCESS);
+ dns_zone_refresh(zone);
}
-void
-dns_zonemgr_setiolimit(dns_zonemgr_t *zmgr, isc_uint32_t iolimit) {
-
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- REQUIRE(iolimit > 0);
+isc_boolean_t
+dns_zone_isforced(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- zmgr->iolimit = iolimit;
+ return (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER));
}
-isc_uint32_t
-dns_zonemgr_getiolimit(dns_zonemgr_t *zmgr) {
-
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+isc_result_t
+dns_zone_setstatistics(dns_zone_t *zone, isc_boolean_t on) {
+ /*
+ * This function is obsoleted.
+ */
+ UNUSED(zone);
+ UNUSED(on);
+ return (ISC_R_NOTIMPLEMENTED);
+}
- return (zmgr->iolimit);
+isc_uint64_t *
+dns_zone_getstatscounters(dns_zone_t *zone) {
+ /*
+ * This function is obsoleted.
+ */
+ UNUSED(zone);
+ return (NULL);
}
-/*
- * Get permission to request a file handle from the OS.
- * An event will be sent to action when one is available.
- * There are two queues available (high and low), the high
- * queue will be serviced before the low one.
- *
- * zonemgr_putio() must be called after the event is delivered to
- * 'action'.
- */
+void
+dns_zone_setstats(dns_zone_t *zone, isc_stats_t *stats) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(zone->stats == NULL);
-static isc_result_t
-zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high,
- isc_task_t *task, isc_taskaction_t action, void *arg,
- dns_io_t **iop)
-{
- dns_io_t *io;
- isc_boolean_t queue;
+ LOCK_ZONE(zone);
+ zone->stats = NULL;
+ isc_stats_attach(stats, &zone->stats);
+ UNLOCK_ZONE(zone);
+}
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- REQUIRE(iop != NULL && *iop == NULL);
+void
+dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- 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);
+ LOCK_ZONE(zone);
+ if (zone->requeststats_on && stats == NULL)
+ zone->requeststats_on = ISC_FALSE;
+ else if (!zone->requeststats_on && stats != NULL) {
+ if (zone->requeststats == NULL) {
+ isc_stats_attach(stats, &zone->requeststats);
+ zone->requeststats_on = ISC_TRUE;
+ }
}
- io->zmgr = zmgr;
- io->high = high;
- io->task = NULL;
- isc_task_attach(task, &io->task);
- ISC_LINK_INIT(io, link);
- io->magic = IO_MAGIC;
+ UNLOCK_ZONE(zone);
- LOCK(&zmgr->iolock);
- zmgr->ioactive++;
- queue = ISC_TF(zmgr->ioactive > zmgr->iolimit);
- if (queue) {
- if (io->high)
- ISC_LIST_APPEND(zmgr->high, io, link);
- else
- ISC_LIST_APPEND(zmgr->low, io, link);
- }
- UNLOCK(&zmgr->iolock);
- *iop = io;
+ return;
+}
- if (!queue) {
- isc_task_send(io->task, &io->event);
- }
- return (ISC_R_SUCCESS);
+isc_stats_t *
+dns_zone_getrequeststats(dns_zone_t *zone) {
+ /*
+ * We don't lock zone for efficiency reason. This is not catastrophic
+ * because requeststats must always be valid when requeststats_on is
+ * true.
+ * Some counters may be incremented while requeststats_on is becoming
+ * false, or some cannot be incremented just after the statistics are
+ * installed, but it shouldn't matter much in practice.
+ */
+ if (zone->requeststats_on)
+ return (zone->requeststats);
+ else
+ return (NULL);
}
-static void
-zonemgr_putio(dns_io_t **iop) {
- dns_io_t *io;
- dns_io_t *next;
- dns_zonemgr_t *zmgr;
+void
+dns_zone_dialup(dns_zone_t *zone) {
- REQUIRE(iop != NULL);
- io = *iop;
- REQUIRE(DNS_IO_VALID(io));
+ REQUIRE(DNS_ZONE_VALID(zone));
- *iop = NULL;
+ zone_debuglog(zone, "dns_zone_dialup", 3,
+ "notify = %d, refresh = %d",
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY),
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH));
- INSIST(!ISC_LINK_LINKED(io, link));
- INSIST(io->event == NULL);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY))
+ dns_zone_notify(zone);
+ if (zone->type != dns_zone_master &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH))
+ dns_zone_refresh(zone);
+}
- zmgr = io->zmgr;
- isc_task_detach(&io->task);
- io->magic = 0;
- isc_mem_put(zmgr->mctx, io, sizeof(*io));
+void
+dns_zone_setdialup(dns_zone_t *zone, dns_dialuptype_t dialup) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- LOCK(&zmgr->iolock);
- INSIST(zmgr->ioactive > 0);
- zmgr->ioactive--;
- next = HEAD(zmgr->high);
- if (next == NULL)
- next = HEAD(zmgr->low);
- if (next != NULL) {
- if (next->high)
- ISC_LIST_UNLINK(zmgr->high, next, link);
- else
- ISC_LIST_UNLINK(zmgr->low, next, link);
- INSIST(next->event != NULL);
+ LOCK_ZONE(zone);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_DIALNOTIFY |
+ DNS_ZONEFLG_DIALREFRESH |
+ DNS_ZONEFLG_NOREFRESH);
+ switch (dialup) {
+ case dns_dialuptype_no:
+ break;
+ case dns_dialuptype_yes:
+ DNS_ZONE_SETFLAG(zone, (DNS_ZONEFLG_DIALNOTIFY |
+ DNS_ZONEFLG_DIALREFRESH |
+ DNS_ZONEFLG_NOREFRESH));
+ break;
+ case dns_dialuptype_notify:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALNOTIFY);
+ break;
+ case dns_dialuptype_notifypassive:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALNOTIFY);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
+ break;
+ case dns_dialuptype_refresh:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALREFRESH);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
+ break;
+ case dns_dialuptype_passive:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
+ break;
+ default:
+ INSIST(0);
}
- UNLOCK(&zmgr->iolock);
- if (next != NULL)
- isc_task_send(next->task, &next->event);
+ UNLOCK_ZONE(zone);
}
-static void
-zonemgr_cancelio(dns_io_t *io) {
- isc_boolean_t send_event = ISC_FALSE;
+isc_result_t
+dns_zone_setkeydirectory(dns_zone_t *zone, const char *directory) {
+ isc_result_t result = ISC_R_SUCCESS;
- REQUIRE(DNS_IO_VALID(io));
+ REQUIRE(DNS_ZONE_VALID(zone));
- /*
- * If we are queued to be run then dequeue.
- */
- LOCK(&io->zmgr->iolock);
- if (ISC_LINK_LINKED(io, link)) {
- if (io->high)
- ISC_LIST_UNLINK(io->zmgr->high, io, link);
- else
- ISC_LIST_UNLINK(io->zmgr->low, io, link);
+ LOCK_ZONE(zone);
+ result = dns_zone_setstring(zone, &zone->keydirectory, directory);
+ UNLOCK_ZONE(zone);
- send_event = ISC_TRUE;
- INSIST(io->event != NULL);
- }
- UNLOCK(&io->zmgr->iolock);
- if (send_event) {
- io->event->ev_attributes |= ISC_EVENTATTR_CANCELED;
- isc_task_send(io->task, &io->event);
- }
+ return (result);
}
-static void
-zone_saveunique(dns_zone_t *zone, const char *path, const char *templat) {
- char *buf;
- int buflen;
- isc_result_t result;
+const char *
+dns_zone_getkeydirectory(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- buflen = strlen(path) + strlen(templat) + 2;
+ return (zone->keydirectory);
+}
- buf = isc_mem_get(zone->mctx, buflen);
- if (buf == NULL)
- return;
+unsigned int
+dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state) {
+ dns_zone_t *zone;
+ unsigned int count = 0;
- result = isc_file_template(path, templat, buf, buflen);
- if (result != ISC_R_SUCCESS)
- goto cleanup;
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
- result = isc_file_renameunique(path, buf);
- if (result != ISC_R_SUCCESS)
- goto cleanup;
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+ switch (state) {
+ case DNS_ZONESTATE_XFERRUNNING:
+ for (zone = ISC_LIST_HEAD(zmgr->xfrin_in_progress);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, statelink))
+ count++;
+ break;
+ case DNS_ZONESTATE_XFERDEFERRED:
+ for (zone = ISC_LIST_HEAD(zmgr->waiting_for_xfrin);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, statelink))
+ count++;
+ break;
+ case DNS_ZONESTATE_SOAQUERY:
+ for (zone = ISC_LIST_HEAD(zmgr->zones);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, link))
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH))
+ count++;
+ break;
+ case DNS_ZONESTATE_ANY:
+ for (zone = ISC_LIST_HEAD(zmgr->zones);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, link)) {
+ dns_view_t *view = zone->view;
+ if (view != NULL && strcmp(view->name, "_bind") == 0)
+ continue;
+ count++;
+ }
+ break;
+ default:
+ INSIST(0);
+ }
- dns_zone_log(zone, ISC_LOG_WARNING, "saved '%s' as '%s'",
- path, buf);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read);
- cleanup:
- isc_mem_put(zone->mctx, buf, buflen);
+ return (count);
}
-#if 0
-/* Hook for ondestroy notification from a database. */
+isc_result_t
+dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata) {
+ isc_boolean_t ok = ISC_TRUE;
+ isc_boolean_t fail = ISC_FALSE;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char namebuf2[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ int level = ISC_LOG_WARNING;
+ dns_name_t bad;
-static void
-dns_zonemgr_dbdestroyed(isc_task_t *task, isc_event_t *event) {
- dns_db_t *db = event->sender;
- UNUSED(task);
+ REQUIRE(DNS_ZONE_VALID(zone));
- isc_event_free(&event);
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMES))
+ return (ISC_R_SUCCESS);
- isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
- DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
- "database (%p) destroyed", (void*) db);
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL)) {
+ level = ISC_LOG_ERROR;
+ fail = ISC_TRUE;
+ }
+
+ ok = dns_rdata_checkowner(name, rdata->rdclass, rdata->type, ISC_TRUE);
+ if (!ok) {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
+ dns_zone_log(zone, level, "%s/%s: %s", namebuf, typebuf,
+ dns_result_totext(DNS_R_BADOWNERNAME));
+ if (fail)
+ return (DNS_R_BADOWNERNAME);
+ }
+
+ dns_name_init(&bad, NULL);
+ ok = dns_rdata_checknames(rdata, name, &bad);
+ if (!ok) {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_name_format(&bad, namebuf2, sizeof(namebuf2));
+ dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
+ dns_zone_log(zone, level, "%s/%s: %s: %s ", namebuf, typebuf,
+ namebuf2, dns_result_totext(DNS_R_BADNAME));
+ if (fail)
+ return (DNS_R_BADNAME);
+ }
+
+ return (ISC_R_SUCCESS);
}
-#endif
void
-dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) {
- isc_interval_t interval;
- isc_uint32_t s, ns;
- isc_uint32_t pertic;
- isc_result_t result;
+dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->checkmx = checkmx;
+}
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+void
+dns_zone_setchecksrv(dns_zone_t *zone, dns_checksrvfunc_t checksrv) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->checksrv = checksrv;
+}
+
+void
+dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->checkns = checkns;
+}
- if (value == 0)
- value = 1;
+void
+dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- if (value == 1) {
- s = 1;
- ns = 0;
- pertic = 1;
- } else if (value <= 10) {
- s = 0;
- ns = 1000000000 / value;
- pertic = 1;
- } else {
- s = 0;
- ns = (1000000000 / value) * 10;
- pertic = 10;
- }
+ LOCK_ZONE(zone);
+ zone->isself = isself;
+ zone->isselfarg = arg;
+ UNLOCK_ZONE(zone);
+}
- isc_interval_set(&interval, s, ns);
- result = isc_ratelimiter_setinterval(zmgr->rl, &interval);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- isc_ratelimiter_setpertic(zmgr->rl, pertic);
+void
+dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- zmgr->serialqueryrate = value;
+ LOCK_ZONE(zone);
+ zone->notifydelay = delay;
+ UNLOCK_ZONE(zone);
}
-unsigned int
-dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr) {
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+isc_uint32_t
+dns_zone_getnotifydelay(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
- return (zmgr->serialqueryrate);
+ return (zone->notifydelay);
}
-static isc_boolean_t
-dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
- isc_sockaddr_t *local, isc_time_t *now)
+isc_result_t
+dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
+ isc_uint16_t keyid, isc_boolean_t delete)
{
- unsigned int i;
- isc_rwlocktype_t locktype;
isc_result_t result;
- isc_uint32_t seconds = isc_time_seconds(now);
+ REQUIRE(DNS_ZONE_VALID(zone));
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ dns_zone_log(zone, ISC_LOG_NOTICE,
+ "dns_zone_signwithkey(algorithm=%u, keyid=%u)",
+ algorithm, keyid);
+ LOCK_ZONE(zone);
+ result = zone_signwithkey(zone, algorithm, keyid, delete);
+ UNLOCK_ZONE(zone);
- locktype = isc_rwlocktype_read;
- RWLOCK(&zmgr->rwlock, locktype);
- for (i = 0; i < UNREACH_CHACHE_SIZE; i++) {
- if (zmgr->unreachable[i].expire >= seconds &&
- isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
- isc_sockaddr_equal(&zmgr->unreachable[i].local, local)) {
- result = isc_rwlock_tryupgrade(&zmgr->rwlock);
- if (result == ISC_R_SUCCESS) {
- locktype = isc_rwlocktype_write;
- zmgr->unreachable[i].last = seconds;
- }
- break;
- }
- }
- RWUNLOCK(&zmgr->rwlock, locktype);
- return (ISC_TF(i < UNREACH_CHACHE_SIZE));
+ return (result);
}
-void
-dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
- isc_sockaddr_t *local, isc_time_t *now)
-{
- isc_uint32_t seconds = isc_time_seconds(now);
- isc_uint32_t last = seconds;
- unsigned int i, slot = UNREACH_CHACHE_SIZE, oldest = 0;
-
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+static const char *hex = "0123456789ABCDEF";
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
- for (i = 0; i < UNREACH_CHACHE_SIZE; i++) {
- /* Existing entry? */
- if (isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
- isc_sockaddr_equal(&zmgr->unreachable[i].local, local))
- break;
- /* Empty slot? */
- if (zmgr->unreachable[i].expire < seconds)
- slot = i;
- /* Least recently used slot? */
- if (zmgr->unreachable[i].last < last) {
- last = zmgr->unreachable[i].last;
- oldest = i;
- }
- }
- if (i < UNREACH_CHACHE_SIZE) {
- /*
- * Found a existing entry. Update the expire timer and
- * last usage timestamps.
- */
- zmgr->unreachable[i].expire = seconds + UNREACH_HOLD_TIME;
- zmgr->unreachable[i].last = seconds;
- } else if (slot != UNREACH_CHACHE_SIZE) {
- /*
- * Found a empty slot. Add a new entry to the cache.
- */
- zmgr->unreachable[slot].expire = seconds + UNREACH_HOLD_TIME;
- zmgr->unreachable[slot].last = seconds;
- zmgr->unreachable[slot].remote = *remote;
- zmgr->unreachable[slot].local = *local;
- } else {
- /*
- * Replace the least recently used entry in the cache.
- */
- zmgr->unreachable[oldest].expire = seconds + UNREACH_HOLD_TIME;
- zmgr->unreachable[oldest].last = seconds;
- zmgr->unreachable[oldest].remote = *remote;
- zmgr->unreachable[oldest].local = *local;
- }
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
-}
+isc_result_t
+dns_zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
+ isc_result_t result;
+ char salt[255*2+1];
+ unsigned int i, j;
-void
-dns_zone_forcereload(dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));
- if (zone->type == dns_zone_master)
- return;
-
+ if (nsec3param->salt_length != 0) {
+ INSIST((nsec3param->salt_length * 2U) < sizeof(salt));
+ for (i = 0, j = 0; i < nsec3param->salt_length; i++) {
+ salt[j++] = hex[(nsec3param->salt[i] >> 4) & 0xf];
+ salt[j++] = hex[nsec3param->salt[i] & 0xf];
+ }
+ salt[j] = '\0';
+ } else
+ strcpy(salt, "-");
+ dns_zone_log(zone, ISC_LOG_NOTICE,
+ "dns_zone_addnsec3chain(hash=%u, iterations=%u, salt=%s)",
+ nsec3param->hash, nsec3param->iterations,
+ salt);
LOCK_ZONE(zone);
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_FORCEXFER);
+ result = zone_addnsec3chain(zone, nsec3param);
UNLOCK_ZONE(zone);
- dns_zone_refresh(zone);
+
+ return (result);
}
-isc_boolean_t
-dns_zone_isforced(dns_zone_t *zone) {
+void
+dns_zone_setnodes(dns_zone_t *zone, isc_uint32_t nodes) {
REQUIRE(DNS_ZONE_VALID(zone));
- return (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER));
+ if (nodes == 0)
+ nodes = 1;
+ zone->nodes = nodes;
}
-isc_result_t
-dns_zone_setstatistics(dns_zone_t *zone, isc_boolean_t on) {
- /*
- * This function is obsoleted.
- */
- UNUSED(zone);
- UNUSED(on);
- return (ISC_R_NOTIMPLEMENTED);
-}
+void
+dns_zone_setsignatures(dns_zone_t *zone, isc_uint32_t signatures) {
+ REQUIRE(DNS_ZONE_VALID(zone));
-isc_uint64_t *
-dns_zone_getstatscounters(dns_zone_t *zone) {
/*
- * This function is obsoleted.
+ * We treat signatures as a signed value so explicitly
+ * limit its range here.
*/
- UNUSED(zone);
- return (NULL);
+ if (signatures > ISC_INT32_MAX)
+ signatures = ISC_INT32_MAX;
+ else if (signatures == 0)
+ signatures = 1;
+ zone->signatures = signatures;
}
void
-dns_zone_setstats(dns_zone_t *zone, isc_stats_t *stats) {
+dns_zone_setprivatetype(dns_zone_t *zone, dns_rdatatype_t type) {
REQUIRE(DNS_ZONE_VALID(zone));
- REQUIRE(zone->stats == NULL);
-
- LOCK_ZONE(zone);
- zone->stats = NULL;
- isc_stats_attach(stats, &zone->stats);
- UNLOCK_ZONE(zone);
+ zone->privatetype = type;
}
-void
-dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) {
+dns_rdatatype_t
+dns_zone_getprivatetype(dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->privatetype);
+}
- LOCK_ZONE(zone);
- if (zone->requeststats_on && stats == NULL)
- zone->requeststats_on = ISC_FALSE;
- else if (!zone->requeststats_on && stats != NULL) {
- if (zone->requeststats == NULL) {
- isc_stats_attach(stats, &zone->requeststats);
- zone->requeststats_on = ISC_TRUE;
+static isc_result_t
+zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid,
+ isc_boolean_t delete)
+{
+ dns_signing_t *signing;
+ dns_signing_t *current;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_time_t now;
+
+ signing = isc_mem_get(zone->mctx, sizeof *signing);
+ if (signing == NULL)
+ return (ISC_R_NOMEMORY);
+
+ signing->magic = 0;
+ signing->db = NULL;
+ signing->dbiterator = NULL;
+ signing->algorithm = algorithm;
+ signing->keyid = keyid;
+ signing->delete = delete;
+ signing->done = ISC_FALSE;
+
+ TIME_NOW(&now);
+
+ for (current = ISC_LIST_HEAD(zone->signing);
+ current != NULL;
+ current = ISC_LIST_NEXT(current, link)) {
+ if (current->db == zone->db &&
+ current->algorithm == signing->algorithm &&
+ current->keyid == signing->keyid) {
+ if (current->delete != signing->delete)
+ current->done = ISC_TRUE;
+ else
+ goto cleanup;
+ }
+ }
+
+ if (zone->db != NULL) {
+ dns_db_attach(zone->db, &signing->db);
+ result = dns_db_createiterator(signing->db, 0,
+ &signing->dbiterator);
+
+ if (result == ISC_R_SUCCESS)
+ result = dns_dbiterator_first(signing->dbiterator);
+ if (result == ISC_R_SUCCESS) {
+ dns_dbiterator_pause(signing->dbiterator);
+ ISC_LIST_INITANDAPPEND(zone->signing, signing, link);
+ signing = NULL;
+ if (isc_time_isepoch(&zone->signingtime)) {
+ zone->signingtime = now;
+ if (zone->task != NULL)
+ zone_settimer(zone, &now);
+ }
}
+ } else
+ result = ISC_R_NOTFOUND;
+
+ cleanup:
+ if (signing != NULL) {
+ if (signing->db != NULL)
+ dns_db_detach(&signing->db);
+ if (signing->dbiterator != NULL)
+ dns_dbiterator_destroy(&signing->dbiterator);
+ isc_mem_put(zone->mctx, signing, sizeof *signing);
}
- UNLOCK_ZONE(zone);
+ return (result);
+}
- return;
+static void
+logmsg(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
+ ISC_LOG_DEBUG(1), format, args);
+ va_end(args);
}
-isc_stats_t *
-dns_zone_getrequeststats(dns_zone_t *zone) {
- /*
- * We don't lock zone for efficiency reason. This is not catastrophic
- * because requeststats must always be valid when requeststats_on is
- * true.
- * Some counters may be incremented while requeststats_on is becoming
- * false, or some cannot be incremented just after the statistics are
- * installed, but it shouldn't matter much in practice.
- */
- if (zone->requeststats_on)
- return (zone->requeststats);
- else
- return (NULL);
+static void
+clear_keylist(dns_dnsseckeylist_t *list, isc_mem_t *mctx) {
+ dns_dnsseckey_t *key;
+ while (!ISC_LIST_EMPTY(*list)) {
+ key = ISC_LIST_HEAD(*list);
+ ISC_LIST_UNLINK(*list, key, link);
+ dns_dnsseckey_destroy(mctx, &key);
+ }
}
-void
-dns_zone_dialup(dns_zone_t *zone) {
+/* Called once; *timep should be set to the current time. */
+static isc_result_t
+next_keyevent(dst_key_t *key, isc_stdtime_t *timep) {
+ isc_result_t result;
+ isc_stdtime_t now, then = 0, event;
+ int i;
- REQUIRE(DNS_ZONE_VALID(zone));
+ now = *timep;
- zone_debuglog(zone, "dns_zone_dialup", 3,
- "notify = %d, refresh = %d",
- DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY),
- DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH));
+ for (i = 0; i <= DST_MAX_TIMES; i++) {
+ result = dst_key_gettime(key, i, &event);
+ if (result == ISC_R_SUCCESS && event > now &&
+ (then == 0 || event < then))
+ then = event;
+ }
- if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY))
- dns_zone_notify(zone);
- if (zone->type != dns_zone_master &&
- DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH))
- dns_zone_refresh(zone);
+ if (then != 0) {
+ *timep = then;
+ return (ISC_R_SUCCESS);
+ }
+
+ return (ISC_R_NOTFOUND);
}
-void
-dns_zone_setdialup(dns_zone_t *zone, dns_dialuptype_t dialup) {
- REQUIRE(DNS_ZONE_VALID(zone));
+static isc_result_t
+rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ const dns_rdata_t *rdata, isc_boolean_t *flag)
+{
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+ isc_result_t result;
- LOCK_ZONE(zone);
- DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_DIALNOTIFY |
- DNS_ZONEFLG_DIALREFRESH |
- DNS_ZONEFLG_NOREFRESH);
- switch (dialup) {
- case dns_dialuptype_no:
- break;
- case dns_dialuptype_yes:
- DNS_ZONE_SETFLAG(zone, (DNS_ZONEFLG_DIALNOTIFY |
- DNS_ZONEFLG_DIALREFRESH |
- DNS_ZONEFLG_NOREFRESH));
- break;
- case dns_dialuptype_notify:
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALNOTIFY);
- break;
- case dns_dialuptype_notifypassive:
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALNOTIFY);
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
- break;
- case dns_dialuptype_refresh:
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALREFRESH);
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
- break;
- case dns_dialuptype_passive:
- DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
- break;
- default:
- INSIST(0);
+ dns_rdataset_init(&rdataset);
+ if (rdata->type == dns_rdatatype_nsec3)
+ CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node));
+ else
+ CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+ result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ *flag = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ goto failure;
}
- UNLOCK_ZONE(zone);
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t myrdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &myrdata);
+ if (!dns_rdata_compare(&myrdata, rdata))
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ *flag = ISC_TRUE;
+ } else if (result == ISC_R_NOMORE) {
+ *flag = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ }
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
}
-isc_result_t
-dns_zone_setkeydirectory(dns_zone_t *zone, const char *directory) {
+/*
+ * Add records to signal the state of signing or of key removal.
+ */
+static isc_result_t
+add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype,
+ dns_dbversion_t *ver, dns_diff_t *diff)
+{
+ dns_difftuple_t *tuple, *newtuple = NULL;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_boolean_t flag;
+ isc_region_t r;
isc_result_t result = ISC_R_SUCCESS;
+ isc_uint16_t keyid;
+ unsigned char buf[5];
+ dns_name_t *name = dns_db_origin(db);
- REQUIRE(DNS_ZONE_VALID(zone));
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ if (tuple->rdata.type != dns_rdatatype_dnskey)
+ continue;
- LOCK_ZONE(zone);
- result = dns_zone_setstring(zone, &zone->keydirectory, directory);
- UNLOCK_ZONE(zone);
+ dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL);
+ if ((dnskey.flags &
+ (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
+ != DNS_KEYOWNER_ZONE)
+ continue;
+
+ dns_rdata_toregion(&tuple->rdata, &r);
+
+ keyid = dst_region_computeid(&r, dnskey.algorithm);
+
+ buf[0] = dnskey.algorithm;
+ buf[1] = (keyid & 0xff00) >> 8;
+ buf[2] = (keyid & 0xff);
+ buf[3] = (tuple->op == DNS_DIFFOP_ADD) ? 0 : 1;
+ buf[4] = 0;
+ rdata.data = buf;
+ rdata.length = sizeof(buf);
+ rdata.type = privatetype;
+ rdata.rdclass = tuple->rdata.rdclass;
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ if (flag)
+ continue;
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ name, 0, &rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ INSIST(newtuple == NULL);
+ /*
+ * Remove any record which says this operation has already
+ * completed.
+ */
+ buf[4] = 1;
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ if (flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+ name, 0, &rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ INSIST(newtuple == NULL);
+ }
+ }
+ failure:
return (result);
}
-const char *
-dns_zone_getkeydirectory(dns_zone_t *zone) {
- REQUIRE(DNS_ZONE_VALID(zone));
+static isc_result_t
+sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_rdatatype_t type, dns_diff_t *diff)
+{
+ isc_result_t result;
+ isc_stdtime_t now, inception, soaexpire;
+ isc_boolean_t check_ksk, keyset_kskonly;
+ dst_key_t *zone_keys[MAXZONEKEYS];
+ unsigned int nkeys = 0, i;
- return (zone->keydirectory);
-}
+ result = find_zone_keys(zone, db, ver, zone->mctx, MAXZONEKEYS,
+ zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sign_apex:find_zone_keys -> %s\n",
+ dns_result_totext(result));
+ return (result);
+ }
-unsigned int
-dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state) {
- dns_zone_t *zone;
- unsigned int count = 0;
+ isc_stdtime_get(&now);
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + dns_zone_getsigvalidityinterval(zone);
- REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
- RWLOCK(&zmgr->rwlock, isc_rwlocktype_read);
- switch (state) {
- case DNS_ZONESTATE_XFERRUNNING:
- for (zone = ISC_LIST_HEAD(zmgr->xfrin_in_progress);
- zone != NULL;
- zone = ISC_LIST_NEXT(zone, statelink))
- count++;
- break;
- case DNS_ZONESTATE_XFERDEFERRED:
- for (zone = ISC_LIST_HEAD(zmgr->waiting_for_xfrin);
- zone != NULL;
- zone = ISC_LIST_NEXT(zone, statelink))
- count++;
- break;
- case DNS_ZONESTATE_SOAQUERY:
- for (zone = ISC_LIST_HEAD(zmgr->zones);
- zone != NULL;
- zone = ISC_LIST_NEXT(zone, link))
- if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH))
- count++;
- break;
- case DNS_ZONESTATE_ANY:
- for (zone = ISC_LIST_HEAD(zmgr->zones);
- zone != NULL;
- zone = ISC_LIST_NEXT(zone, link)) {
- dns_view_t *view = zone->view;
- if (view != NULL && strcmp(view->name, "_bind") == 0)
- continue;
- count++;
- }
- break;
- default:
- INSIST(0);
+ result = del_sigs(zone, db, ver, &zone->origin, type, diff,
+ zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sign_apex:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
}
- RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+ result = add_sigs(db, ver, &zone->origin, type, diff, zone_keys,
+ nkeys, zone->mctx, inception, soaexpire,
+ check_ksk, keyset_kskonly);
- return (count);
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR, "sign_apex:add_sigs -> %s\n",
+ dns_result_totext(result));
+ failure:
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+ return (result);
}
-isc_result_t
-dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata) {
- isc_boolean_t ok = ISC_TRUE;
- isc_boolean_t fail = ISC_FALSE;
- char namebuf[DNS_NAME_FORMATSIZE];
- char namebuf2[DNS_NAME_FORMATSIZE];
- char typebuf[DNS_RDATATYPE_FORMATSIZE];
- int level = ISC_LOG_WARNING;
- dns_name_t bad;
-
- REQUIRE(DNS_ZONE_VALID(zone));
+/*
+ * Prevent the zone entering a inconsistent state where
+ * NSEC only DNSKEYs are present with NSEC3 chains.
+ * See update.c:check_dnssec()
+ */
+static isc_boolean_t
+dnskey_sane(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple;
+ isc_boolean_t nseconly = ISC_FALSE, nsec3 = ISC_FALSE;
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
- if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMES))
- return (ISC_R_SUCCESS);
+ /* Scan the tuples for an NSEC-only DNSKEY */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ isc_uint8_t alg;
+ if (tuple->rdata.type != dns_rdatatype_dnskey ||
+ tuple->op != DNS_DIFFOP_ADD)
+ continue;
- if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL)) {
- level = ISC_LOG_ERROR;
- fail = ISC_TRUE;
+ alg = tuple->rdata.data[3];
+ if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
+ alg == DST_ALG_DSA || alg == DST_ALG_ECC) {
+ nseconly = ISC_TRUE;
+ break;
+ }
}
- ok = dns_rdata_checkowner(name, rdata->rdclass, rdata->type, ISC_TRUE);
- if (!ok) {
- dns_name_format(name, namebuf, sizeof(namebuf));
- dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
- dns_zone_log(zone, level, "%s/%s: %s", namebuf, typebuf,
- dns_result_totext(DNS_R_BADOWNERNAME));
- if (fail)
- return (DNS_R_BADOWNERNAME);
- }
+ /* Check existing DB for NSEC-only DNSKEY */
+ if (!nseconly)
+ CHECK(dns_nsec_nseconly(db, ver, &nseconly));
- dns_name_init(&bad, NULL);
- ok = dns_rdata_checknames(rdata, name, &bad);
- if (!ok) {
- dns_name_format(name, namebuf, sizeof(namebuf));
- dns_name_format(&bad, namebuf2, sizeof(namebuf2));
- dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
- dns_zone_log(zone, level, "%s/%s: %s: %s ", namebuf, typebuf,
- namebuf2, dns_result_totext(DNS_R_BADNAME));
- if (fail)
- return (DNS_R_BADNAME);
+ /* Check existing DB for NSEC3 */
+ if (!nsec3)
+ CHECK(dns_nsec3_activex(db, ver, ISC_FALSE,
+ privatetype, &nsec3));
+
+ /* Refuse to allow NSEC3 with NSEC-only keys */
+ if (nseconly && nsec3) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "NSEC only DNSKEYs and NSEC3 chains not allowed");
+ goto failure;
}
- return (ISC_R_SUCCESS);
-}
+ return (ISC_TRUE);
-void
-dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx) {
- REQUIRE(DNS_ZONE_VALID(zone));
- zone->checkmx = checkmx;
+ failure:
+ return (ISC_FALSE);
}
-void
-dns_zone_setchecksrv(dns_zone_t *zone, dns_checksrvfunc_t checksrv) {
- REQUIRE(DNS_ZONE_VALID(zone));
- zone->checksrv = checksrv;
-}
+static isc_result_t
+clean_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
-void
-dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns) {
- REQUIRE(DNS_ZONE_VALID(zone));
- zone->checkns = checkns;
-}
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_getoriginnode(db, &node));
-void
-dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOTFOUND)
+ goto failure;
- LOCK_ZONE(zone);
- zone->isself = isself;
- zone->isselfarg = arg;
- UNLOCK_ZONE(zone);
+ result = dns_nsec3param_deletechains(db, ver, zone, diff);
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
}
-void
-dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay) {
+static void
+zone_rekey(dns_zone_t *zone) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_rdataset_t soaset, soasigs, keyset, keysigs;
+ dns_dnsseckeylist_t dnskeys, keys, rmkeys;
+ dns_dnsseckey_t *key;
+ dns_diff_t diff;
+ isc_boolean_t commit = ISC_FALSE, newactive = ISC_FALSE;
+ dns_ttl_t ttl = 3600;
+ const char *dir;
+ isc_mem_t *mctx;
+ isc_stdtime_t now;
+ isc_time_t timenow;
+ isc_interval_t ival;
+
REQUIRE(DNS_ZONE_VALID(zone));
- LOCK_ZONE(zone);
- zone->notifydelay = delay;
- UNLOCK_ZONE(zone);
-}
+ ISC_LIST_INIT(dnskeys);
+ ISC_LIST_INIT(keys);
+ ISC_LIST_INIT(rmkeys);
+ dns_rdataset_init(&soaset);
+ dns_rdataset_init(&soasigs);
+ dns_rdataset_init(&keyset);
+ dns_rdataset_init(&keysigs);
+ dir = dns_zone_getkeydirectory(zone);
+ mctx = zone->mctx;
+ dns_diff_init(mctx, &diff);
-isc_uint32_t
-dns_zone_getnotifydelay(dns_zone_t *zone) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ CHECK(dns_zone_getdb(zone, &db));
+ CHECK(dns_db_newversion(db, &ver));
+ CHECK(dns_db_getoriginnode(db, &node));
- return (zone->notifydelay);
-}
+ dns_zone_log(zone, ISC_LOG_INFO, "reconfiguring zone keys");
-isc_result_t
-dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
- isc_uint16_t keyid, isc_boolean_t delete)
-{
- isc_result_t result;
- REQUIRE(DNS_ZONE_VALID(zone));
+ /* Get the SOA record's TTL */
+ CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_soa,
+ dns_rdatatype_none, 0, &soaset, &soasigs));
+ ttl = soaset.ttl;
+ dns_rdataset_disassociate(&soaset);
- dns_zone_log(zone, ISC_LOG_NOTICE,
- "dns_zone_signwithkey(algorithm=%u, keyid=%u)",
- algorithm, keyid);
- LOCK_ZONE(zone);
- result = zone_signwithkey(zone, algorithm, keyid, delete);
- UNLOCK_ZONE(zone);
+ /* Get the DNSKEY rdataset */
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey,
+ 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;
+ } else if (result != ISC_R_NOTFOUND)
+ goto failure;
- return (result);
-}
+ result = dns_dnssec_findmatchingkeys(&zone->origin, dir, mctx, &keys);
+ if (result == ISC_R_SUCCESS) {
+ isc_boolean_t check_ksk;
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
-static const char *hex = "0123456789ABCDEF";
+ result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys,
+ &zone->origin, ttl, &diff,
+ ISC_TF(!check_ksk),
+ mctx, logmsg);
-isc_result_t
-dns_zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
- isc_result_t result;
- char salt[255*2+1];
- unsigned int i, j;
+ /* Keys couldn't be updated for some reason;
+ * try again later. */
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_rekey:"
+ "couldn't update zone keys: %s",
+ isc_result_totext(result));
+ goto trylater;
+ }
- REQUIRE(DNS_ZONE_VALID(zone));
+ /* See if any pre-existing keys have newly become active */
+ for (key = ISC_LIST_HEAD(dnskeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ if (key->first_sign) {
+ newactive = ISC_TRUE;
+ break;
+ }
+ }
- if (nsec3param->salt_length != 0) {
- INSIST((nsec3param->salt_length * 2U) < sizeof(salt));
- for (i = 0, j = 0; i < nsec3param->salt_length; i++) {
- salt[j++] = hex[(nsec3param->salt[i] >> 4) & 0xf];
- salt[j++] = hex[nsec3param->salt[i] & 0xf];
+ if ((newactive || !ISC_LIST_EMPTY(diff.tuples)) &&
+ dnskey_sane(zone, db, ver, &diff)) {
+ CHECK(dns_diff_apply(&diff, db, ver));
+ CHECK(clean_nsec3param(zone, db, ver, &diff));
+ CHECK(sign_apex(zone, db, ver, dns_rdatatype_dnskey,
+ &diff));
+ CHECK(add_signing_records(db, zone->privatetype, ver,
+ &diff));
+ CHECK(increment_soa_serial(db, ver, &diff, mctx));
+ CHECK(sign_apex(zone, db, ver, dns_rdatatype_soa,
+ &diff));
+ CHECK(zone_journal(zone, &diff, "zone_rekey"));
+ commit = ISC_TRUE;
}
- salt[j] = '\0';
- } else
- strcpy(salt, "-");
- dns_zone_log(zone, ISC_LOG_NOTICE,
- "dns_zone_addnsec3chain(hash=%u, iterations=%u, salt=%s)",
- nsec3param->hash, nsec3param->iterations,
- salt);
- LOCK_ZONE(zone);
- result = zone_addnsec3chain(zone, nsec3param);
- UNLOCK_ZONE(zone);
+ }
- return (result);
-}
+ dns_db_closeversion(db, &ver, commit);
-void
-dns_zone_setnodes(dns_zone_t *zone, isc_uint32_t nodes) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ if (commit) {
+ isc_time_t timenow;
+ dns_difftuple_t *tuple;
- if (nodes == 0)
- nodes = 1;
- zone->nodes = nodes;
-}
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
-void
-dns_zone_setsignatures(dns_zone_t *zone, isc_uint32_t signatures) {
- REQUIRE(DNS_ZONE_VALID(zone));
+ zone_needdump(zone, DNS_DUMP_DELAY);
- /*
- * We treat signatures as a signed value so explicitly
- * limit its range here.
- */
- if (signatures > ISC_INT32_MAX)
- signatures = ISC_INT32_MAX;
- else if (signatures == 0)
- signatures = 1;
- zone->signatures = signatures;
-}
+ TIME_NOW(&timenow);
+ zone_settimer(zone, &timenow);
-void
-dns_zone_setprivatetype(dns_zone_t *zone, dns_rdatatype_t type) {
- REQUIRE(DNS_ZONE_VALID(zone));
- zone->privatetype = type;
-}
+ for (tuple = ISC_LIST_HEAD(diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ dns_rdata_dnskey_t dnskey;
+ dns_secalg_t algorithm;
+ isc_region_t r;
+ isc_uint16_t keyid;
-dns_rdatatype_t
-dns_zone_getprivatetype(dns_zone_t *zone) {
- REQUIRE(DNS_ZONE_VALID(zone));
- return (zone->privatetype);
-}
+ if (tuple->rdata.type != dns_rdatatype_dnskey)
+ continue;
-static isc_result_t
-zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid,
- isc_boolean_t delete)
-{
- dns_signing_t *signing;
- dns_signing_t *current;
- isc_result_t result = ISC_R_SUCCESS;
- isc_time_t now;
+ result = dns_rdata_tostruct(&tuple->rdata, &dnskey,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_toregion(&tuple->rdata, &r);
+ algorithm = dnskey.algorithm;
+ keyid = dst_region_computeid(&r, algorithm);
- signing = isc_mem_get(zone->mctx, sizeof *signing);
- if (signing == NULL)
- return (ISC_R_NOMEMORY);
+ result = zone_signwithkey(zone, algorithm, keyid,
+ ISC_TF(tuple->op == DNS_DIFFOP_DEL));
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_signwithkey failed: %s",
+ dns_result_totext(result));
+ }
+ }
- signing->magic = 0;
- signing->db = NULL;
- signing->dbiterator = NULL;
- signing->algorithm = algorithm;
- signing->keyid = keyid;
- signing->delete = delete;
- signing->done = ISC_FALSE;
+ /*
+ * Cause the zone to add/delete NSEC3 chains for the
+ * deferred NSEC3PARAM changes.
+ */
+ for (tuple = ISC_LIST_HEAD(diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3param_t nsec3param;
- TIME_NOW(&now);
+ if (tuple->rdata.type != zone->privatetype ||
+ tuple->op != DNS_DIFFOP_ADD)
+ continue;
- for (current = ISC_LIST_HEAD(zone->signing);
- current != NULL;
- current = ISC_LIST_NEXT(current, link)) {
- if (current->db == zone->db &&
- current->algorithm == signing->algorithm &&
- current->keyid == signing->keyid) {
- if (current->delete != signing->delete)
- current->done = ISC_TRUE;
- else
- goto cleanup;
+ if (!dns_nsec3param_fromprivate(&tuple->rdata, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+ if (nsec3param.flags == 0)
+ continue;
+
+ result = zone_addnsec3chain(zone, &nsec3param);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_addnsec3chain failed: %s",
+ dns_result_totext(result));
+ }
}
+ UNLOCK_ZONE(zone);
}
- if (zone->db != NULL) {
- dns_db_attach(zone->db, &signing->db);
- result = dns_db_createiterator(signing->db, 0,
- &signing->dbiterator);
+ isc_stdtime_get(&now);
+ TIME_NOW(&timenow);
+ isc_time_settoepoch(&zone->refreshkeytime);
+ for (key = ISC_LIST_HEAD(dnskeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ isc_stdtime_t then;
+ isc_time_t timethen;
- if (result == ISC_R_SUCCESS)
- result = dns_dbiterator_first(signing->dbiterator);
- if (result == ISC_R_SUCCESS) {
- dns_dbiterator_pause(signing->dbiterator);
- ISC_LIST_INITANDAPPEND(zone->signing, signing, link);
- signing = NULL;
- if (isc_time_isepoch(&zone->signingtime)) {
- zone->signingtime = now;
- if (zone->task != NULL)
- zone_settimer(zone, &now);
- }
+ /*
+ * If we are doing automatic key maintenance and the
+ * key metadata indicates there is a key change event
+ * scheduled in the future, set the key refresh timer.
+ */
+ if (!DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_MAINTAIN))
+ break;
+
+ then = now;
+ result = next_keyevent(key->key, &then);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ DNS_ZONE_TIME_ADD(&timenow, then - now, &timethen);
+ LOCK_ZONE(zone);
+ if (isc_time_isepoch(&zone->refreshkeytime) ||
+ isc_time_compare(&timethen, &zone->refreshkeytime) < 0) {
+ zone->refreshkeytime = timethen;
+ zone_settimer(zone, &timenow);
}
- } else
- result = ISC_R_NOTFOUND;
+ UNLOCK_ZONE(zone);
+ }
- cleanup:
- if (signing != NULL) {
- dns_db_detach(&signing->db);
- if (signing->dbiterator != NULL)
- dns_dbiterator_destroy(&signing->dbiterator);
- isc_mem_put(zone->mctx, signing, sizeof *signing);
+ failure:
+ dns_diff_clear(&diff);
+
+ clear_keylist(&dnskeys, mctx);
+ clear_keylist(&keys, mctx);
+ clear_keylist(&rmkeys, mctx);
+
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, ISC_FALSE);
+ if (dns_rdataset_isassociated(&keyset))
+ dns_rdataset_disassociate(&keyset);
+ if (dns_rdataset_isassociated(&keysigs))
+ dns_rdataset_disassociate(&keysigs);
+ if (dns_rdataset_isassociated(&soasigs))
+ dns_rdataset_disassociate(&soasigs);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ return;
+
+ trylater:
+ isc_interval_set(&ival, HOUR, 0);
+ isc_time_nowplusinterval(&zone->refreshkeytime, &ival);
+ goto failure;
+}
+
+void
+dns_zone_rekey(dns_zone_t *zone) {
+ isc_time_t now;
+
+ if (zone->type == dns_zone_master && zone->task != NULL) {
+ LOCK_ZONE(zone);
+
+ TIME_NOW(&now);
+ zone->refreshkeytime = now;
+ zone_settimer(zone, &now);
+ UNLOCK_ZONE(zone);
}
+}
+
+isc_result_t
+dns_zone_nscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+ unsigned int *errors)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(errors != NULL);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = zone_count_ns_rr(zone, db, node, version, NULL, errors,
+ ISC_FALSE);
+ dns_db_detachnode(db, &node);
return (result);
}