diff options
author | Internet Software Consortium, Inc <@isc.org> | 2011-11-01 14:44:50 -0600 |
---|---|---|
committer | Internet Software Consortium, Inc <@isc.org> | 2011-11-01 14:44:50 -0600 |
commit | 15c17fb71db9b8f876da1be5e6ddbba25ce61aba (patch) | |
tree | 8f808bcda25300ee1b2bbb66f7fb49ab3a01027a /lib | |
parent | 0985d8a79623e77e4d2c801a661d1b1180f41285 (diff) | |
download | bind9-15c17fb71db9b8f876da1be5e6ddbba25ce61aba.tar.gz |
9.9.0a2
Diffstat (limited to 'lib')
52 files changed, 4389 insertions, 299 deletions
diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 2b7b894d..1df7989f 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check.c,v 1.133 2011-06-17 07:05:02 each Exp $ */ +/* $Id: check.c,v 1.134 2011-08-30 05:16:14 marka Exp $ */ /*! \file */ @@ -1225,7 +1225,7 @@ check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) { #define STATICSTUBZONE 64 #define REDIRECTZONE 128 #define STREDIRECTZONE 0 /* Set to REDIRECTZONE to allow xfr-in. */ -#define CHECKACL 256 +#define CHECKACL 512 typedef struct { const char *name; @@ -1255,7 +1255,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, static optionstable options[] = { { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE | - CHECKACL | STATICSTUBZONE }, + CHECKACL | STATICSTUBZONE }, { "allow-notify", SLAVEZONE | CHECKACL }, { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL }, { "notify", MASTERZONE | SLAVEZONE }, @@ -1279,13 +1279,14 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { "max-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "min-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "dnssec-secure-to-insecure", MASTERZONE }, - { "sig-validity-interval", MASTERZONE }, - { "sig-re-signing-interval", MASTERZONE }, - { "sig-signing-nodes", MASTERZONE }, - { "sig-signing-type", MASTERZONE }, - { "sig-signing-signatures", MASTERZONE }, + { "sig-re-signing-interval", MASTERZONE | SLAVEZONE }, + { "sig-signing-nodes", MASTERZONE | SLAVEZONE }, + { "sig-signing-signatures", MASTERZONE | SLAVEZONE }, + { "sig-signing-type", MASTERZONE | SLAVEZONE }, + { "sig-validity-interval", MASTERZONE | SLAVEZONE }, + { "signing", MASTERZONE | SLAVEZONE }, { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE | - STATICSTUBZONE| REDIRECTZONE }, + STATICSTUBZONE | REDIRECTZONE }, { "allow-update", MASTERZONE | CHECKACL }, { "allow-update-forwarding", SLAVEZONE | CHECKACL }, { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE | REDIRECTZONE }, @@ -1296,7 +1297,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE }, { "update-policy", MASTERZONE }, { "database", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE }, - { "key-directory", MASTERZONE }, + { "key-directory", MASTERZONE | SLAVEZONE }, { "check-wildcard", MASTERZONE }, { "check-mx", MASTERZONE }, { "check-dup-records", MASTERZONE }, @@ -1308,7 +1309,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { "update-check-ksk", MASTERZONE }, { "dnssec-dnskey-kskonly", MASTERZONE }, { "dnssec-loadkeys-interval", MASTERZONE }, - { "auto-dnssec", MASTERZONE }, + { "auto-dnssec", MASTERZONE | SLAVEZONE }, { "try-tcp-refresh", SLAVEZONE | STREDIRECTZONE }, { "server-addresses", STATICSTUBZONE }, { "server-names", STATICSTUBZONE }, diff --git a/lib/dns/cache.c b/lib/dns/cache.c index 38821c43..232a752e 100644 --- a/lib/dns/cache.c +++ b/lib/dns/cache.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: cache.c,v 1.90 2011-08-02 20:36:12 each Exp $ */ +/* $Id: cache.c,v 1.91 2011-08-26 05:12:56 marka Exp $ */ /*! \file */ @@ -1186,7 +1186,7 @@ clearnode(dns_db_t *db, dns_dbnode_t *node) { static isc_result_t cleartree(dns_db_t *db, dns_name_t *name) { - isc_result_t result; + isc_result_t result, answer = ISC_R_SUCCESS; dns_dbiterator_t *iter = NULL; dns_dbnode_t *node = NULL; dns_fixedname_t fnodename; @@ -1205,12 +1205,22 @@ cleartree(dns_db_t *db, dns_name_t *name) { while (result == ISC_R_SUCCESS) { result = dns_dbiterator_current(iter, &node, nodename); - if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) + if (result == DNS_R_NEWORIGIN) + result = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS) goto cleanup; + /* + * Are we done? + */ if (! dns_name_issubdomain(nodename, name)) goto cleanup; + /* + * If clearnode fails record and move onto the next node. + */ result = clearnode(db, node); + if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) + answer = result; dns_db_detachnode(db, &node); result = dns_dbiterator_next(iter); } @@ -1218,12 +1228,14 @@ cleartree(dns_db_t *db, dns_name_t *name) { cleanup: if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) result = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) + answer = result; if (node != NULL) dns_db_detachnode(db, &node); if (iter != NULL) dns_dbiterator_destroy(&iter); - return (result); + return (answer); } isc_result_t diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 33cfd361..3c568c73 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -16,7 +16,7 @@ */ /* - * $Id: dnssec.c,v 1.124 2011-05-06 21:08:33 each Exp $ + * $Id: dnssec.c,v 1.125 2011-08-26 05:29:48 marka Exp $ */ /*! \file */ @@ -1705,10 +1705,8 @@ dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, shortest = thisttl; } - if (shortest != 0) { - found_ttl = ISC_TRUE; + if (shortest != 0) ttl = shortest; - } } /* diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index c8e4e897..5a490c05 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -31,7 +31,7 @@ /* * Principal Author: Brian Wellington - * $Id: dst_api.c,v 1.63 2011-08-18 23:46:34 tbox Exp $ + * $Id: dst_api.c,v 1.64 2011-09-05 18:00:22 each Exp $ */ /*! \file */ @@ -1579,7 +1579,7 @@ write_public_key(const dst_key_t *key, int type, const char *directory) { fprintf(fp, "%d ", key->key_ttl); isc_buffer_usedregion(&classb, &r); - if ((unsigned)isc_util_fwrite(r.base, 1, r.length, fp) != r.length) + if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length) ret = DST_R_WRITEERROR; if ((type & DST_TYPE_KEY) != 0) @@ -1588,7 +1588,7 @@ write_public_key(const dst_key_t *key, int type, const char *directory) { fprintf(fp, " DNSKEY "); isc_buffer_usedregion(&textb, &r); - if ((unsigned)isc_util_fwrite(r.base, 1, r.length, fp) != r.length) + if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length) ret = DST_R_WRITEERROR; fputc('\n', fp); diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c index a6c5f450..938671c1 100644 --- a/lib/dns/gssapictx.c +++ b/lib/dns/gssapictx.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: gssapictx.c,v 1.28 2011-04-07 23:03:22 marka Exp $ */ +/* $Id: gssapictx.c,v 1.29 2011-08-29 06:33:25 marka Exp $ */ #include <config.h> @@ -135,6 +135,7 @@ name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer, } result = dns_name_toprincipal(namep, buffer); + RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_buffer_putuint8(buffer, 0); isc_buffer_usedregion(buffer, &r); REGION_TO_GBUFFER(r, *gbuffer); @@ -309,7 +310,7 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, if (gret != GSS_S_COMPLETE) { gss_log(3, "failed to acquire %s credentials for %s: %s", initiate ? "initiate" : "accept", - (char *)gnamebuf.value, + (gname != NULL) ? (char *)gnamebuf.value : "?", gss_error_tostring(gret, minor, buf, sizeof(buf))); check_config((char *)array); return (ISC_R_FAILURE); @@ -317,12 +318,14 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, gss_log(4, "acquired %s credentials for %s", initiate ? "initiate" : "accept", - (char *)gnamebuf.value); + (gname != NULL) ? (char *)gnamebuf.value : "?"); log_cred(*cred); return (ISC_R_SUCCESS); #else + REQUIRE(cred != NULL && *cred == NULL); + UNUSED(name); UNUSED(initiate); UNUSED(cred); @@ -342,13 +345,15 @@ dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name, char *sname; char *rname; isc_buffer_t buffer; + isc_result_t result; /* * It is far, far easier to write the names we are looking at into * a string, and do string operations on them. */ isc_buffer_init(&buffer, sbuf, sizeof(sbuf)); - dns_name_toprincipal(signer, &buffer); + result = dns_name_toprincipal(signer, &buffer); + RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_buffer_putuint8(&buffer, 0); if (name != NULL) dns_name_format(name, nbuf, sizeof(nbuf)); @@ -414,13 +419,15 @@ dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name, char *nname; char *rname; isc_buffer_t buffer; + isc_result_t result; /* * It is far, far easier to write the names we are looking at into * a string, and do string operations on them. */ isc_buffer_init(&buffer, sbuf, sizeof(sbuf)); - dns_name_toprincipal(signer, &buffer); + result = dns_name_toprincipal(signer, &buffer); + RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_buffer_putuint8(&buffer, 0); if (name != NULL) dns_name_format(name, nbuf, sizeof(nbuf)); @@ -664,8 +671,7 @@ dst_gssapi_acceptctx(gss_cred_id_t cred, gss_log(3, "failed " "gsskrb5_register_acceptor_identity(%s): %s", gssapi_keytab, - gss_error_tostring(gret, minor, - buf, sizeof(buf))); + gss_error_tostring(gret, 0, buf, sizeof(buf))); return (DNS_R_INVALIDTKEY); } #else diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index d9858336..88c2719c 100644 --- a/lib/dns/include/dns/events.h +++ b/lib/dns/include/dns/events.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009-2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: events.h,v 1.56 2010-12-21 03:11:42 marka Exp $ */ +/* $Id: events.h,v 1.59 2011-09-02 21:15:36 each Exp $ */ #ifndef DNS_EVENTS_H #define DNS_EVENTS_H 1 @@ -74,6 +74,9 @@ #define DNS_EVENT_CLIENTREQDONE (ISC_EVENTCLASS_DNS + 44) #define DNS_EVENT_ADBGROWENTRIES (ISC_EVENTCLASS_DNS + 45) #define DNS_EVENT_ADBGROWNAMES (ISC_EVENTCLASS_DNS + 46) +#define DNS_EVENT_ZONESECURESERIAL (ISC_EVENTCLASS_DNS + 47) +#define DNS_EVENT_ZONESECUREDB (ISC_EVENTCLASS_DNS + 48) +#define DNS_EVENT_ZONELOAD (ISC_EVENTCLASS_DNS + 49) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/lib/dns/include/dns/journal.h b/lib/dns/include/dns/journal.h index 28a7dbe3..c5024a37 100644 --- a/lib/dns/include/dns/journal.h +++ b/lib/dns/include/dns/journal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.h,v 1.37 2009-11-04 23:48:18 tbox Exp $ */ +/* $Id: journal.h,v 1.39 2011-08-30 23:46:53 tbox Exp $ */ #ifndef DNS_JOURNAL_H #define DNS_JOURNAL_H 1 @@ -46,6 +46,10 @@ ***/ #define DNS_JOURNALOPT_RESIGN 0x00000001 +#define DNS_JOURNAL_READ 0x00000000 /* ISC_FALSE */ +#define DNS_JOURNAL_CREATE 0x00000001 /* ISC_TRUE */ +#define DNS_JOURNAL_WRITE 0x00000002 + /*** *** Types ***/ @@ -95,16 +99,15 @@ dns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, */ isc_result_t -dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, +dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, dns_journal_t **journalp); /*%< * Open the journal file 'filename' and create a dns_journal_t object for it. * - * If 'write' is ISC_TRUE, the journal is open for writing. If it does - * not exist, it is created. - * - * If 'write' is ISC_FALSE, the journal is open for reading. If it does - * not exist, ISC_R_NOTFOUND is returned. + * DNS_JOURNAL_CREATE open the journal for reading and writing and create + * the journal if it does not exist. + * DNS_JOURNAL_WRITE open the journal for readinge and writing. + * DNS_JOURNAL_READ open the journal for reading only. */ void @@ -284,6 +287,14 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, * exists and is non-empty 'serial' must exist in the journal. */ +isc_uint32_t +dns_journal_get_bitws(dns_journal_t *j); +void +dns_journal_set_bitws(dns_journal_t *j, isc_uint32_t bitws); +/*%< + * Get and set bump in the wire serial. + */ + ISC_LANG_ENDDECLS #endif /* DNS_JOURNAL_H */ diff --git a/lib/dns/include/dns/update.h b/lib/dns/include/dns/update.h index cf5d0146..31239999 100644 --- a/lib/dns/include/dns/update.h +++ b/lib/dns/include/dns/update.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.h,v 1.2 2011-07-01 02:25:48 marka Exp $ */ +/* $Id: update.h,v 1.5 2011-08-30 23:46:53 tbox Exp $ */ #ifndef DNS_UPDATE_H #define DNS_UPDATE_H 1 @@ -28,6 +28,13 @@ #include <isc/lang.h> #include <dns/types.h> +#include <dns/diff.h> + +typedef struct { + void (*func)(void *arg, dns_zone_t *zone, int level, + const char *message); + void *arg; +} dns_update_log_t; ISC_LANG_BEGINDECLS @@ -47,6 +54,11 @@ dns_update_soaserial(isc_uint32_t serial, dns_updatemethod_t method); * if not. */ +isc_result_t +dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *oldver, dns_dbversion_t *newver, + dns_diff_t *diff, isc_uint32_t sigvalidityinterval); + ISC_LANG_ENDDECLS #endif /* DNS_UPDATE_H */ diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 9b6e2e66..690ca7a5 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.h,v 1.134 2011-08-02 20:36:13 each Exp $ */ +/* $Id: view.h,v 1.136 2011-09-06 22:29:33 smann Exp $ */ #ifndef DNS_VIEW_H #define DNS_VIEW_H 1 @@ -76,6 +76,7 @@ #include <dns/rdatastruct.h> #include <dns/rpz.h> #include <dns/types.h> +#include <dns/zt.h> ISC_LANG_BEGINDECLS @@ -141,7 +142,6 @@ struct dns_view { dns_rbt_t * answeracl_exclude; dns_rbt_t * denyanswernames; dns_rbt_t * answernames_exclude; - isc_boolean_t requestixfr; isc_boolean_t provideixfr; isc_boolean_t requestnsid; dns_ttl_t maxcachettl; @@ -728,14 +728,21 @@ dns_view_load(dns_view_t *view, isc_boolean_t stop); isc_result_t dns_view_loadnew(dns_view_t *view, isc_boolean_t stop); + +isc_result_t +dns_view_asyncload(dns_view_t *view, dns_zt_allloaded_t callback, void *arg); /*%< * Load zones attached to this view. dns_view_load() loads * all zones whose master file has changed since the last * load; dns_view_loadnew() loads only zones that have never * been loaded. * + * dns_view_asyncload() loads zones asynchronously. When all zones + * in the view have finished loading, 'callback' is called with argument + * 'arg' to inform the caller. + * * If 'stop' is ISC_TRUE, stop on the first error and return it. - * If 'stop' is ISC_FALSE, ignore errors. + * If 'stop' is ISC_FALSE (or we are loading asynchronously), ignore errors. * * Requires: * diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 6e4e2e3f..1d8e434c 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.h,v 1.191 2011-07-06 01:36:32 each Exp $ */ +/* $Id: zone.h,v 1.194 2011-09-06 22:29:33 smann Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -35,6 +35,7 @@ #include <dns/masterdump.h> #include <dns/rdatastruct.h> #include <dns/types.h> +#include <dns/zt.h> typedef enum { dns_zone_none, @@ -44,7 +45,7 @@ typedef enum { dns_zone_staticstub, dns_zone_key, dns_zone_dlz, - dns_zone_redirect, + dns_zone_redirect } dns_zonetype_t; #define DNS_ZONEOPT_SERVERS 0x00000001U /*%< perform server checks */ @@ -287,6 +288,7 @@ dns_zone_loadnew(dns_zone_t *zone); isc_result_t dns_zone_loadandthaw(dns_zone_t *zone); + /*%< * Cause the database to be loaded from its backing store. * Confirm that the minimum requirements for the zone type are @@ -311,6 +313,25 @@ dns_zone_loadandthaw(dns_zone_t *zone); *\li Any result value from dns_db_load(). */ +isc_result_t +dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg); +/*%< + * Cause the database to be loaded from its backing store asynchronously. + * Other zone maintenance functions are suspended until this is complete. + * When finished, 'done' is called to inform the caller, with 'arg' as + * its first argument and 'zone' as its second. (Normally, 'arg' is + * expected to point to the zone table but is left undefined for testing + * purposes.) + */ + +isc_boolean_t +dns__zone_loadpending(dns_zone_t *zone); +/*%< + * Indicates whether the zone is waiting to be loaded asynchronously. + * (Not currently intended for use outside of this module and associated + * tests.) + */ + void dns_zone_attach(dns_zone_t *source, dns_zone_t **target); /*%< @@ -1428,6 +1449,14 @@ dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr); */ void +dns__zonemgr_run(isc_task_t *task, isc_event_t *event); +/*%< + * Event handler to call dns_zonemgr_forcemaint(); used to start + * zone operations from a unit test. Not intended for use outside + * libdns or related tests. + */ + +void dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr); /*%< * Attempt to start any stalled zone transfers. @@ -1903,6 +1932,25 @@ dns_zone_setrefreshkeyinterval(dns_zone_t *zone, isc_uint32_t interval); * \li 'zone' to be valid. */ +isc_boolean_t +dns_zone_getrequestixfr(dns_zone_t *zone); +/*% + * Returns the true/false value of the request-ixfr option in the zone. + * + * Requires: + * \li 'zone' to be valid. + */ + +void +dns_zone_setrequestixfr(dns_zone_t *zone, isc_boolean_t flag); +/*% + * Sets the request-ixfr option for the zone. Either true or false. The + * default value is determined by the setting of this option in the view. + * + * Requires: + * \li 'zone' to be valid. + */ + void dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method); /*% @@ -1923,6 +1971,13 @@ dns_zone_getserialupdatemethod(dns_zone_t *zone); * Requires: * \li 'zone' to be valid. */ + +void +dns_zone_link(dns_zone_t *zone, dns_zone_t *raw); + +void +dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw); + ISC_LANG_ENDDECLS #endif /* DNS_ZONE_H */ diff --git a/lib/dns/include/dns/zt.h b/lib/dns/include/dns/zt.h index 6e5ef5c6..e636fc68 100644 --- a/lib/dns/include/dns/zt.h +++ b/lib/dns/include/dns/zt.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zt.h,v 1.38 2007-06-19 23:47:17 tbox Exp $ */ +/* $Id: zt.h,v 1.40 2011-09-02 23:46:32 tbox Exp $ */ #ifndef DNS_ZT_H #define DNS_ZT_H 1 @@ -30,6 +30,21 @@ ISC_LANG_BEGINDECLS +typedef isc_result_t +(*dns_zt_allloaded_t)(void *arg); +/*%< + * Method prototype: when all pending zone loads are complete, + * the zone table can inform the caller via a callback function with + * this signature. + */ + +typedef isc_result_t +(*dns_zt_zoneloaded_t)(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task); +/*%< + * Method prototype: when a zone finishes loading, the zt object + * can be informed via a callback function with this signature. + */ + isc_result_t dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **zt); /*%< @@ -134,6 +149,9 @@ dns_zt_load(dns_zt_t *zt, isc_boolean_t stop); isc_result_t dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop); + +isc_result_t +dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg); /*%< * Load all zones in the table. If 'stop' is ISC_TRUE, * stop on the first error and return it. If 'stop' @@ -142,6 +160,10 @@ dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop); * dns_zt_loadnew() only loads zones that are not yet loaded. * dns_zt_load() also loads zones that are already loaded and * and whose master file has changed since the last load. + * dns_zt_asyncload() loads zones asynchronously; when all + * zones in the zone table have finished loaded (or failed due + * to errors), the caller is informed by calling 'alldone' + * with an argument of 'arg'. * * Requires: * \li 'zt' to be valid @@ -178,6 +200,16 @@ dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub, * any error code from 'action'. */ +isc_boolean_t +dns_zt_loadspending(dns_zt_t *zt); +/*%< + * Returns ISC_TRUE if and only if there are zones still waiting to + * be loaded in zone table 'zt'. + * + * Requires: + * \li 'zt' to be valid. + */ + ISC_LANG_ENDDECLS #endif /* DNS_ZT_H */ diff --git a/lib/dns/journal.c b/lib/dns/journal.c index c6350d63..16fe541b 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.c,v 1.114 2011-03-12 04:59:48 tbox Exp $ */ +/* $Id: journal.c,v 1.116 2011-08-30 23:46:52 tbox Exp $ */ #include <config.h> @@ -213,6 +213,8 @@ typedef union { journal_rawpos_t end; /*% Number of index entries following the header. */ unsigned char index_size[4]; + /*% Bump in the wire serial. */ + unsigned char bitws[4]; } h; /* Pad the header to a fixed size. */ unsigned char pad[JOURNAL_HEADER_SIZE]; @@ -252,6 +254,7 @@ typedef struct { journal_pos_t begin; journal_pos_t end; isc_uint32_t index_size; + isc_uint32_t bitws; } journal_header_t; /*% @@ -284,7 +287,7 @@ typedef struct { */ static journal_header_t -initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0 }; +initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0, 0 }; #define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset) @@ -292,7 +295,8 @@ typedef enum { JOURNAL_STATE_INVALID, JOURNAL_STATE_READ, JOURNAL_STATE_WRITE, - JOURNAL_STATE_TRANSACTION + JOURNAL_STATE_TRANSACTION, + JOURNAL_STATE_BITWS } journal_state_t; struct dns_journal { @@ -353,6 +357,7 @@ journal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) { journal_pos_decode(&raw->h.begin, &cooked->begin); journal_pos_decode(&raw->h.end, &cooked->end); cooked->index_size = decode_uint32(raw->h.index_size); + cooked->bitws = decode_uint32(raw->h.bitws); } static void @@ -363,6 +368,7 @@ journal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) { journal_pos_encode(&raw->h.begin, &cooked->begin); journal_pos_encode(&raw->h.end, &cooked->end); encode_uint32(cooked->index_size, raw->h.index_size); + encode_uint32(cooked->bitws, raw->h.bitws); } /* @@ -667,13 +673,17 @@ journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, } isc_result_t -dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, +dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, dns_journal_t **journalp) { isc_result_t result; int namelen; char backup[1024]; + isc_boolean_t write, create; - result = journal_open(mctx, filename, write, write, journalp); + create = ISC_TF(mode & DNS_JOURNAL_CREATE); + write = ISC_TF(mode & (DNS_JOURNAL_WRITE|DNS_JOURNAL_CREATE)); + + result = journal_open(mctx, filename, write, create, journalp); if (result == ISC_R_NOTFOUND) { namelen = strlen(filename); if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) @@ -944,7 +954,8 @@ dns_journal_begin_transaction(dns_journal_t *j) { journal_rawxhdr_t hdr; REQUIRE(DNS_JOURNAL_VALID(j)); - REQUIRE(j->state == JOURNAL_STATE_WRITE); + REQUIRE(j->state == JOURNAL_STATE_WRITE || + j->state == JOURNAL_STATE_BITWS); /* * Find the file offset where the new transaction should @@ -1067,7 +1078,21 @@ dns_journal_commit(dns_journal_t *j) { journal_rawheader_t rawheader; REQUIRE(DNS_JOURNAL_VALID(j)); - REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); + REQUIRE(j->state == JOURNAL_STATE_TRANSACTION || + j->state == JOURNAL_STATE_BITWS); + + /* + * Just write out a updated header. + */ + if (j->state == JOURNAL_STATE_BITWS) { + CHECK(journal_fsync(j)); + journal_header_encode(&j->header, &rawheader); + CHECK(journal_seek(j, 0)); + CHECK(journal_write(j, &rawheader, sizeof(rawheader))); + CHECK(journal_fsync(j)); + j->state = JOURNAL_STATE_WRITE; + return (ISC_R_SUCCESS); + } /* * Perform some basic consistency checks. @@ -1124,19 +1149,24 @@ dns_journal_commit(dns_journal_t *j) { */ CHECK(journal_fsync(j)); - /* - * Update the transaction header. - */ - CHECK(journal_seek(j, j->x.pos[0].offset)); - CHECK(journal_write_xhdr(j, (j->x.pos[1].offset - j->x.pos[0].offset) - - sizeof(journal_rawxhdr_t), - j->x.pos[0].serial, j->x.pos[1].serial)); + if (j->state == JOURNAL_STATE_TRANSACTION) { + isc_offset_t offset; + offset = (j->x.pos[1].offset - j->x.pos[0].offset) - + sizeof(journal_rawxhdr_t); + /* + * Update the transaction header. + */ + CHECK(journal_seek(j, j->x.pos[0].offset)); + CHECK(journal_write_xhdr(j, offset, j->x.pos[0].serial, + j->x.pos[1].serial)); + } /* * Update the journal header. */ if (JOURNAL_EMPTY(&j->header)) { j->header.begin = j->x.pos[0]; + j->header.bitws = j->header.begin.serial; } j->header.end = j->x.pos[1]; journal_header_encode(&j->header, &rawheader); @@ -1415,6 +1445,7 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { return (result); } + fprintf(file, "BITWS = %u\n", j->header.bitws); dns_diff_init(j->mctx, &diff); /* @@ -1497,14 +1528,33 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { /* * Miscellaneous accessors. */ -isc_uint32_t dns_journal_first_serial(dns_journal_t *j) { +isc_uint32_t +dns_journal_first_serial(dns_journal_t *j) { return (j->header.begin.serial); } -isc_uint32_t dns_journal_last_serial(dns_journal_t *j) { +isc_uint32_t +dns_journal_last_serial(dns_journal_t *j) { return (j->header.end.serial); } +void +dns_journal_set_bitws(dns_journal_t *j, isc_uint32_t bitws) { + + REQUIRE(j->state == JOURNAL_STATE_WRITE || + j->state == JOURNAL_STATE_BITWS || + j->state == JOURNAL_STATE_TRANSACTION); + + j->header.bitws = bitws; + if (j->state == JOURNAL_STATE_WRITE) + j->state = JOURNAL_STATE_BITWS; +} + +isc_uint32_t +dns_journal_get_bitws(dns_journal_t *j) { + return (j->header.bitws); +} + /**************************************************************************/ /* * Iteration support. @@ -2145,6 +2195,7 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, new->header.begin.offset = indexend; new->header.end.serial = j->header.end.serial; new->header.end.offset = indexend + copy_length; + new->header.bitws = j->header.bitws; /* * Update the journal header. diff --git a/lib/dns/masterdump.c b/lib/dns/masterdump.c index 8d911c10..61a9cfc1 100644 --- a/lib/dns/masterdump.c +++ b/lib/dns/masterdump.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: masterdump.c,v 1.108 2011-06-08 22:13:50 each Exp $ */ +/* $Id: masterdump.c,v 1.109 2011-09-07 19:11:13 each Exp $ */ /*! \file */ @@ -419,12 +419,11 @@ rdataset_totext(dns_rdataset_t *rdataset, rdataset->attributes |= DNS_RDATASETATTR_LOADORDER; result = dns_rdataset_first(rdataset); - REQUIRE(result == ISC_R_SUCCESS); current_ttl = ctx->current_ttl; current_ttl_valid = ctx->current_ttl_valid; - do { + while (result == ISC_R_SUCCESS) { column = 0; /* @@ -550,7 +549,7 @@ rdataset_totext(dns_rdataset_t *rdataset, first = ISC_FALSE; result = dns_rdataset_next(rdataset); - } while (result == ISC_R_SUCCESS); + } if (result != ISC_R_NOMORE) return (result); diff --git a/lib/dns/rbt.c b/lib/dns/rbt.c index 1b4ff3ac..38f3fc15 100644 --- a/lib/dns/rbt.c +++ b/lib/dns/rbt.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbt.c,v 1.148 2011-03-12 04:59:48 tbox Exp $ */ +/* $Id: rbt.c,v 1.149 2011-08-25 05:56:50 marka Exp $ */ /*! \file */ @@ -1929,6 +1929,8 @@ dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) { sibling = RIGHT(parent); } + INSIST(sibling != NULL); + if (IS_BLACK(LEFT(sibling)) && IS_BLACK(RIGHT(sibling))) { MAKE_RED(sibling); @@ -1965,6 +1967,8 @@ dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) { sibling = LEFT(parent); } + INSIST(sibling != NULL); + if (IS_BLACK(LEFT(sibling)) && IS_BLACK(RIGHT(sibling))) { MAKE_RED(sibling); diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index 93f67f9c..979abb56 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdataset.c,v 1.90 2011-06-08 22:13:50 each Exp $ */ +/* $Id: rdataset.c,v 1.91 2011-08-25 06:28:11 marka Exp $ */ /*! \file */ @@ -442,11 +442,11 @@ towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, j = val % count; for (i = 0; i < count; i++) { if (order != NULL) - sorted[j].key = (*order)(&shuffled[i], + sorted[i].key = (*order)(&shuffled[j], order_arg); else - sorted[j].key = 0; /* Unused */ - sorted[j].rdata = &shuffled[i]; + sorted[i].key = 0; /* Unused */ + sorted[i].rdata = &shuffled[j]; j++; if (j == count) j = 0; /* Wrap around. */ diff --git a/lib/dns/spnego.c b/lib/dns/spnego.c index f71d6cc6..8f1b067c 100644 --- a/lib/dns/spnego.c +++ b/lib/dns/spnego.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: spnego.c,v 1.18 2011-04-04 11:09:11 marka Exp $ */ +/* $Id: spnego.c,v 1.20 2011-08-29 04:15:50 marka Exp $ */ /*! \file * \brief @@ -948,8 +948,9 @@ der_match_tag_and_length(const unsigned char *p, size_t len, e = der_get_length(p, len, length_ret, &l); if (e) return (e); - p += l; + /* p += l; */ len -= l; + POST(len); ret += l; if (size) *size = ret; @@ -980,6 +981,7 @@ decode_enumerated(const unsigned char *p, size_t len, void *num, size_t *size) return (e); p += l; len -= l; + POST(p); POST(len); ret += l; if (size) *size = ret; @@ -1016,6 +1018,7 @@ decode_octet_string(const unsigned char *p, size_t len, return (e); p += l; len -= l; + POST(p); POST(len); ret += l; if (size) *size = ret; @@ -1052,6 +1055,7 @@ decode_oid(const unsigned char *p, size_t len, return (e); p += l; len -= l; + POST(p); POST(len); ret += l; if (size) *size = ret; @@ -1198,6 +1202,7 @@ der_put_octet_string(unsigned char *p, size_t len, return (ASN1_OVERFLOW); p -= data->length; len -= data->length; + POST(len); memcpy(p + 1, data->data, data->length); *size = data->length; return (0); @@ -1263,6 +1268,7 @@ der_put_length_and_tag(unsigned char *p, size_t len, size_t len_val, return (e); p -= l; len -= l; + POST(p); POST(len); ret += l; *size = ret; return (0); @@ -1287,6 +1293,7 @@ encode_enumerated(unsigned char *p, size_t len, const void *data, size_t *size) return (e); p -= l; len -= l; + POST(p); POST(len); ret += l; *size = ret; return (0); @@ -1311,6 +1318,7 @@ encode_octet_string(unsigned char *p, size_t len, return (e); p -= l; len -= l; + POST(p); POST(len); ret += l; *size = ret; return (0); @@ -1335,6 +1343,7 @@ encode_oid(unsigned char *p, size_t len, return (e); p -= l; len -= l; + POST(p); POST(len); ret += l; *size = ret; return (0); diff --git a/lib/dns/tests/Makefile.in b/lib/dns/tests/Makefile.in index 0056d9d4..bf18e285 100644 --- a/lib/dns/tests/Makefile.in +++ b/lib/dns/tests/Makefile.in @@ -12,7 +12,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.7 2011-08-23 01:29:38 each Exp $ +# $Id: Makefile.in,v 1.8 2011-09-02 21:15:37 each Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -37,12 +37,12 @@ DNSDEPLIBS = ../libdns.@A@ LIBS = @LIBS@ @ATFLIBS@ OBJS = dnstest.@O@ -SRCS = dnstest.c master_test.c time_test.c update_test.c \ - zonemgr_test.c dbiterator_test.c +SRCS = dnstest.c master_test.c dbiterator_test.c time_test.c \ + update_test.c zonemgr_test.c zt_test.c SUBDIRS = -TARGETS = master_test@EXEEXT@ time_test@EXEEXT@ update_test@EXEEXT@ \ - zonemgr_test@EXEEXT@ dbiterator_test@EXEEXT@ +TARGETS = master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \ + update_test@EXEEXT@ zonemgr_test@EXEEXT@ zt_test@EXEEXT@ @BIND9_MAKE_RULES@ @@ -71,6 +71,11 @@ dbiterator_test@EXEEXT@: dbiterator_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPL dbiterator_test.@O@ dnstest.@O@ ${DNSLIBS} \ ${ISCLIBS} ${LIBS} +zt_test@EXEEXT@: zt_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + zt_test.@O@ dnstest.@O@ ${DNSLIBS} \ + ${ISCLIBS} ${LIBS} + unit:: sh ${top_srcdir}/unit/unittest.sh diff --git a/lib/dns/tests/dbiterator_test.c b/lib/dns/tests/dbiterator_test.c index b8787a9e..12af6908 100644 --- a/lib/dns/tests/dbiterator_test.c +++ b/lib/dns/tests/dbiterator_test.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dbiterator_test.c,v 1.4 2011-08-23 23:54:00 tbox Exp $ */ +/* $Id: dbiterator_test.c,v 1.5 2011-08-29 23:44:07 marka Exp $ */ /*! \file */ @@ -161,6 +161,9 @@ test_walk(const atf_tc_t *tc) { result == ISC_R_SUCCESS; result = dns_dbiterator_next(iter)) { result = dns_dbiterator_current(iter, &node, name); + if (result == DNS_R_NEWORIGIN) + result = ISC_R_SUCCESS; + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); dns_db_detachnode(db, &node); i++; } @@ -221,6 +224,9 @@ static void test_reverse(const atf_tc_t *tc) { result == ISC_R_SUCCESS; result = dns_dbiterator_prev(iter)) { result = dns_dbiterator_current(iter, &node, name); + if (result == DNS_R_NEWORIGIN) + result = ISC_R_SUCCESS; + ATF_CHECK_EQ(result, ISC_R_SUCCESS); dns_db_detachnode(db, &node); i++; } @@ -285,6 +291,9 @@ static void test_seek(const atf_tc_t *tc) { while (result == ISC_R_SUCCESS) { result = dns_dbiterator_current(iter, &node, name); + if (result == DNS_R_NEWORIGIN) + result = ISC_R_SUCCESS; + ATF_CHECK_EQ(result, ISC_R_SUCCESS); dns_db_detachnode(db, &node); result = dns_dbiterator_next(iter); i++; diff --git a/lib/dns/tests/dnstest.c b/lib/dns/tests/dnstest.c index 300a429b..019154ab 100644 --- a/lib/dns/tests/dnstest.c +++ b/lib/dns/tests/dnstest.c @@ -14,12 +14,15 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnstest.c,v 1.4 2011-07-06 01:36:32 each Exp $ */ +/* $Id: dnstest.c,v 1.7 2011-09-03 19:22:43 each Exp $ */ /*! \file */ #include <config.h> +#include <time.h> +#include <unistd.h> + #include <isc/app.h> #include <isc/buffer.h> #include <isc/entropy.h> @@ -32,9 +35,12 @@ #include <isc/timer.h> #include <isc/util.h> +#include <dns/fixedname.h> #include <dns/log.h> #include <dns/name.h> #include <dns/result.h> +#include <dns/view.h> +#include <dns/zone.h> #include <dst/dst.h> @@ -44,8 +50,11 @@ isc_mem_t *mctx = NULL; isc_entropy_t *ectx = NULL; isc_log_t *lctx = NULL; isc_taskmgr_t *taskmgr = NULL; +isc_task_t *maintask = NULL; isc_timermgr_t *timermgr = NULL; isc_socketmgr_t *socketmgr = NULL; +dns_zonemgr_t *zonemgr = NULL; +isc_boolean_t app_running = ISC_FALSE; int ncpus; static isc_boolean_t hash_active = ISC_FALSE, dst_active = ISC_FALSE; @@ -67,8 +76,12 @@ static isc_logcategory_t categories[] = { static void cleanup_managers() { + if (app_running) + isc_app_finish(); if (socketmgr != NULL) isc_socketmgr_destroy(&socketmgr); + if (maintask != NULL) + isc_task_destroy(&maintask); if (taskmgr != NULL) isc_taskmgr_destroy(&taskmgr); if (timermgr != NULL) @@ -87,6 +100,8 @@ create_managers() { CHECK(isc_taskmgr_create(mctx, ncpus, 0, &taskmgr)); CHECK(isc_timermgr_create(mctx, &timermgr)); CHECK(isc_socketmgr_create(mctx, &socketmgr)); + CHECK(isc_task_create(taskmgr, 0, &maintask)); + CHECK(isc_app_start()); return (ISC_R_SUCCESS); cleanup: @@ -134,6 +149,14 @@ dns_test_begin(FILE *logfile, isc_boolean_t start_managers) { if (start_managers) CHECK(create_managers()); + /* + * atf-run changes us to a /tmp directory, so tests + * that access test data files must first chdir to the proper + * location. + */ + if (chdir(TESTS) == -1) + CHECK(ISC_R_FAILURE); + return (ISC_R_SUCCESS); cleanup: @@ -162,3 +185,114 @@ dns_test_end() { isc_mem_destroy(&mctx); } +/* + * Create a zone with origin 'name', return a pointer to the zone object in + * 'zonep'. If 'view' is set, add the zone to that view; otherwise, create + * a new view for the purpose. + * + * If the created view is going to be needed by the caller subsequently, + * then 'keepview' should be set to true; this will prevent the view + * from being detached. In this case, the caller is responsible for + * detaching the view. + */ +isc_result_t +dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view, + isc_boolean_t keepview) +{ + isc_result_t result; + dns_zone_t *zone = NULL; + isc_buffer_t buffer; + dns_fixedname_t fixorigin; + dns_name_t *origin; + + if (view == NULL) + CHECK(dns_view_create(mctx, dns_rdataclass_in, "view", &view)); + else if (!keepview) + keepview = ISC_TRUE; + + CHECK(dns_zone_create(&zone, mctx)); + + isc_buffer_init(&buffer, name, strlen(name)); + isc_buffer_add(&buffer, strlen(name)); + dns_fixedname_init(&fixorigin); + origin = dns_fixedname_name(&fixorigin); + CHECK(dns_name_fromtext(origin, &buffer, dns_rootname, 0, NULL)); + CHECK(dns_zone_setorigin(zone, origin)); + dns_zone_setview(zone, view); + dns_zone_settype(zone, dns_zone_master); + dns_zone_setclass(zone, view->rdclass); + dns_view_addzone(view, zone); + + if (!keepview) + dns_view_detach(&view); + + *zonep = zone; + + return (ISC_R_SUCCESS); + + cleanup: + if (zone != NULL) + dns_zone_detach(&zone); + if (view != NULL) + dns_view_detach(&view); + return (result); +} + +isc_result_t +dns_test_setupzonemgr() { + isc_result_t result; + REQUIRE(zonemgr == NULL); + + result = dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr, + &zonemgr); + return (result); +} + +isc_result_t +dns_test_managezone(dns_zone_t *zone) { + isc_result_t result; + REQUIRE(zonemgr != NULL); + + result = dns_zonemgr_setsize(zonemgr, 1); + if (result != ISC_R_SUCCESS) + return (result); + + result = dns_zonemgr_managezone(zonemgr, zone); + return (result); +} + +void +dns_test_releasezone(dns_zone_t *zone) { + REQUIRE(zonemgr != NULL); + dns_zonemgr_releasezone(zonemgr, zone); +} + +void +dns_test_closezonemgr() { + REQUIRE(zonemgr != NULL); + + dns_zonemgr_shutdown(zonemgr); + dns_zonemgr_detach(&zonemgr); +} + +/* + * Sleep for 'usec' microseconds. + */ +void +dns_test_nap(isc_uint32_t usec) { +#ifdef HAVE_NANOSLEEP + struct timespec ts; + + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + nanosleep(&ts, NULL); +#elif HAVE_USLEEP + usleep(usec); +#else + /* + * No fractional-second sleep function is available, so we + * round up to the nearest second and sleep instead + */ + sleep((usec / 1000000) + 1); +#endif +} diff --git a/lib/dns/tests/dnstest.h b/lib/dns/tests/dnstest.h index 00a2c376..8bddd410 100644 --- a/lib/dns/tests/dnstest.h +++ b/lib/dns/tests/dnstest.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnstest.h,v 1.3 2011-07-06 01:36:32 each Exp $ */ +/* $Id: dnstest.h,v 1.4 2011-09-02 21:15:37 each Exp $ */ /*! \file */ @@ -31,6 +31,7 @@ #include <isc/util.h> #include <dns/result.h> +#include <dns/zone.h> #define CHECK(r) \ do { \ @@ -42,11 +43,13 @@ extern isc_mem_t *mctx; extern isc_entropy_t *ectx; extern isc_log_t *lctx; -isc_taskmgr_t *taskmgr; -isc_timermgr_t *timermgr; -isc_socketmgr_t *socketmgr; -int ncpus; - +extern isc_taskmgr_t *taskmgr; +extern isc_task_t *maintask; +extern isc_timermgr_t *timermgr; +extern isc_socketmgr_t *socketmgr; +extern dns_zonemgr_t *zonemgr; +extern isc_boolean_t app_running; +extern int ncpus; isc_result_t dns_test_begin(FILE *logfile, isc_boolean_t create_managers); @@ -54,3 +57,21 @@ dns_test_begin(FILE *logfile, isc_boolean_t create_managers); void dns_test_end(void); +isc_result_t +dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view, + isc_boolean_t keepview); + +isc_result_t +dns_test_setupzonemgr(void); + +isc_result_t +dns_test_managezone(dns_zone_t *zone); + +void +dns_test_releasezone(dns_zone_t *zone); + +void +dns_test_closezonemgr(void); + +void +dns_test_nap(isc_uint32_t usec); diff --git a/lib/dns/tests/master_test.c b/lib/dns/tests/master_test.c index 2920b8cf..afe6eaa4 100644 --- a/lib/dns/tests/master_test.c +++ b/lib/dns/tests/master_test.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master_test.c,v 1.4 2011-07-06 01:36:32 each Exp $ */ +/* $Id: master_test.c,v 1.6 2011-09-07 19:11:14 each Exp $ */ /*! \file */ @@ -27,8 +27,10 @@ #include <dns/cache.h> #include <dns/callbacks.h> #include <dns/master.h> +#include <dns/masterdump.h> #include <dns/name.h> #include <dns/rdata.h> +#include <dns/rdatalist.h> #include <dns/rdataset.h> #include "dnstest.h" @@ -85,14 +87,6 @@ test_master(const char *testfile) { dns_rdatacallbacks_init_stdio(&callbacks); callbacks.add = add_callback; - /* - * atf-run changes us to a /tmp directory, so tests - * that access test data files must first chdir to the proper - * location. - */ - if (chdir(TESTS) == -1) - return (ISC_R_FAILURE); - result = dns_master_loadfile(testfile, &dns_origin, &dns_origin, dns_rdataclass_in, ISC_TRUE, &callbacks, mctx); @@ -334,6 +328,50 @@ ATF_TC_BODY(master_leadingzero, tc) { dns_test_end(); } +ATF_TC(master_totext); +ATF_TC_HEAD(master_totext, tc) { + atf_tc_set_md_var(tc, "descr", "masterfile totext tests"); +} +ATF_TC_BODY(master_totext, tc) { + isc_result_t result; + dns_rdataset_t rdataset; + dns_rdatalist_t rdatalist; + isc_buffer_t target; + unsigned char buf[BIGBUFLEN]; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_FALSE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* First, test with an empty rdataset */ + rdatalist.rdclass = dns_rdataclass_in; + rdatalist.type = dns_rdatatype_none; + rdatalist.covers = dns_rdatatype_none; + rdatalist.ttl = 0; + ISC_LIST_INIT(rdatalist.rdata); + ISC_LINK_INIT(&rdatalist, link); + + dns_rdataset_init(&rdataset); + result = dns_rdatalist_tordataset(&rdatalist, &rdataset); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + isc_buffer_init(&target, buf, BIGBUFLEN); + result = dns_master_rdatasettotext(dns_rootname, + &rdataset, &dns_master_style_debug, + &target); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_EQ(isc_buffer_usedlength(&target), 0); + + /* + * XXX: We will also need to add tests for dumping various + * rdata types, classes, etc, and comparing the results against + * known-good output. + */ + + dns_test_end(); +} + /* * Main */ @@ -349,6 +387,7 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, master_includefail); ATF_TP_ADD_TC(tp, master_blanklines); ATF_TP_ADD_TC(tp, master_leadingzero); + ATF_TP_ADD_TC(tp, master_totext); return (atf_no_error()); } diff --git a/lib/dns/tests/testdata/zt/zone1.db b/lib/dns/tests/testdata/zt/zone1.db new file mode 100644 index 00000000..4744a4d3 --- /dev/null +++ b/lib/dns/tests/testdata/zt/zone1.db @@ -0,0 +1,27 @@ +; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +; $Id: zone1.db,v 1.3 2011-09-02 23:46:32 tbox Exp $ + +$TTL 1000 +@ in soa localhost. postmaster.localhost. ( + 1993050801 ;serial + 3600 ;refresh + 1800 ;retry + 604800 ;expiration + 3600 ) ;minimum + in ns ns.vix.com. + in ns ns2.vix.com. + in ns ns3.vix.com. +a in a 1.2.3.4 diff --git a/lib/dns/tests/zonemgr_test.c b/lib/dns/tests/zonemgr_test.c index 502e7ec7..b00165cb 100644 --- a/lib/dns/tests/zonemgr_test.c +++ b/lib/dns/tests/zonemgr_test.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zonemgr_test.c,v 1.2 2011-07-06 05:05:51 each Exp $ */ +/* $Id: zonemgr_test.c,v 1.3 2011-09-02 21:15:37 each Exp $ */ /*! \file */ @@ -34,41 +34,6 @@ #include "dnstest.h" -static isc_result_t -make_zone(const char *name, dns_zone_t **zonep) { - isc_result_t result; - dns_view_t *view = NULL; - dns_zone_t *zone = NULL; - isc_buffer_t buffer; - dns_fixedname_t fixorigin; - dns_name_t *origin; - - CHECK(dns_view_create(mctx, dns_rdataclass_in, "view", &view)); - CHECK(dns_zone_create(&zone, mctx)); - - isc_buffer_init(&buffer, name, strlen(name)); - isc_buffer_add(&buffer, strlen(name)); - dns_fixedname_init(&fixorigin); - origin = dns_fixedname_name(&fixorigin); - CHECK(dns_name_fromtext(origin, &buffer, dns_rootname, 0, NULL)); - CHECK(dns_zone_setorigin(zone, origin)); - dns_zone_setview(zone, view); - dns_zone_settype(zone, dns_zone_master); - dns_zone_setclass(zone, view->rdclass); - - dns_view_detach(&view); - *zonep = zone; - - return (ISC_R_SUCCESS); - - cleanup: - if (zone != NULL) - dns_zone_detach(&zone); - if (view != NULL) - dns_view_detach(&view); - return (result); -} - /* * Individual unit tests */ @@ -115,7 +80,7 @@ ATF_TC_BODY(zonemgr_managezone, tc) { &zonemgr); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); - result = make_zone("foo", &zone); + result = dns_test_makezone("foo", &zone, NULL, ISC_FALSE); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); /* This should not succeed until the dns_zonemgr_setsize() is run */ diff --git a/lib/dns/tests/zt_test.c b/lib/dns/tests/zt_test.c new file mode 100644 index 00000000..1239c39a --- /dev/null +++ b/lib/dns/tests/zt_test.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: zt_test.c,v 1.5 2011-09-03 16:15:08 each Exp $ */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <unistd.h> + +#include <isc/app.h> +#include <isc/buffer.h> +#include <isc/task.h> +#include <isc/timer.h> + +#include <dns/db.h> +#include <dns/name.h> +#include <dns/view.h> +#include <dns/zone.h> +#include <dns/zt.h> + +#include "dnstest.h" + +/* + * Helper functions + */ +static isc_result_t +count_zone(dns_zone_t *zone, void *uap) { + int *nzones = (int *)uap; + + UNUSED(zone); + + *nzones += 1; + return (ISC_R_SUCCESS); +} + +static isc_result_t +load_done(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { + /* We treat zt as a pointer to a boolean for testing purposes */ + isc_boolean_t *done = (isc_boolean_t *) zt; + + UNUSED(zone); + UNUSED(task); + + *done = ISC_TRUE; + isc_app_shutdown(); + return (ISC_R_SUCCESS); +} + +static isc_result_t +all_done(void *arg) { + isc_boolean_t *done = (isc_boolean_t *) arg; + + *done = ISC_TRUE; + isc_app_shutdown(); + return (ISC_R_SUCCESS); +} + +/* + * Individual unit tests + */ +ATF_TC(apply); +ATF_TC_HEAD(apply, tc) { + atf_tc_set_md_var(tc, "descr", "apply a function to a zone table"); +} +ATF_TC_BODY(apply, tc) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_view_t *view = NULL; + int nzones = 0; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = dns_test_makezone("foo", &zone, NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + view = dns_zone_getview(zone); + ATF_REQUIRE(view->zonetable != NULL); + + ATF_CHECK_EQ(0, nzones); + result = dns_zt_apply(view->zonetable, ISC_FALSE, count_zone, &nzones); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_EQ(1, nzones); + + /* These steps are necessary so the zone can be detached properly */ + result = dns_test_setupzonemgr(); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = dns_test_managezone(zone); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + dns_test_releasezone(zone); + dns_test_closezonemgr(); + + /* The view was left attached in dns_test_makezone() */ + dns_view_detach(&view); + dns_zone_detach(&zone); + + dns_test_end(); +} + +ATF_TC(asyncload_zone); +ATF_TC_HEAD(asyncload_zone, tc) { + atf_tc_set_md_var(tc, "descr", "asynchronous zone load"); +} +ATF_TC_BODY(asyncload_zone, tc) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_view_t *view = NULL; + dns_db_t *db = NULL; + isc_boolean_t done = ISC_FALSE; + int i = 0; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = dns_test_makezone("foo", &zone, NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = dns_test_setupzonemgr(); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = dns_test_managezone(zone); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + view = dns_zone_getview(zone); + ATF_REQUIRE(view->zonetable != NULL); + + ATF_CHECK(!dns__zone_loadpending(zone)); + ATF_CHECK(!done); + dns_zone_setfile(zone, "testdata/zt/zone1.db"); + dns_zone_asyncload(zone, load_done, (void *) &done); + ATF_CHECK(dns__zone_loadpending(zone)); + + isc_app_run(); + while (dns__zone_loadpending(zone) && i++ < 5000) + dns_test_nap(1000); + ATF_CHECK(done); + + /* The zone should now be loaded; test it */ + result = dns_zone_getdb(zone, &db); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(db != NULL); + if (db != NULL) + dns_db_detach(&db); + + dns_test_releasezone(zone); + dns_test_closezonemgr(); + + dns_zone_detach(&zone); + dns_view_detach(&view); + + dns_test_end(); +} + +ATF_TC(asyncload_zt); +ATF_TC_HEAD(asyncload_zt, tc) { + atf_tc_set_md_var(tc, "descr", "asynchronous zone table load"); +} +ATF_TC_BODY(asyncload_zt, tc) { + isc_result_t result; + dns_zone_t *zone1 = NULL, *zone2 = NULL, *zone3 = NULL; + dns_view_t *view; + dns_zt_t *zt; + dns_db_t *db = NULL; + isc_boolean_t done = ISC_FALSE; + int i = 0; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = dns_test_makezone("foo", &zone1, NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + dns_zone_setfile(zone1, "testdata/zt/zone1.db"); + view = dns_zone_getview(zone1); + + result = dns_test_makezone("bar", &zone2, view, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + dns_zone_setfile(zone2, "testdata/zt/zone1.db"); + + /* This one will fail to load */ + result = dns_test_makezone("fake", &zone3, view, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + dns_zone_setfile(zone3, "testdata/zt/nonexistent.db"); + + zt = view->zonetable; + ATF_REQUIRE(zt != NULL); + + result = dns_test_setupzonemgr(); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = dns_test_managezone(zone1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = dns_test_managezone(zone2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = dns_test_managezone(zone3); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(!dns__zone_loadpending(zone1)); + ATF_CHECK(!dns__zone_loadpending(zone2)); + ATF_CHECK(!done); + dns_zt_asyncload(zt, all_done, (void *) &done); + + isc_app_run(); + while (!done && i++ < 5000) + dns_test_nap(1000); + ATF_CHECK(done); + + /* Both zones should now be loaded; test them */ + result = dns_zone_getdb(zone1, &db); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(db != NULL); + if (db != NULL) + dns_db_detach(&db); + + result = dns_zone_getdb(zone2, &db); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(db != NULL); + if (db != NULL) + dns_db_detach(&db); + + dns_test_releasezone(zone3); + dns_test_releasezone(zone2); + dns_test_releasezone(zone1); + dns_test_closezonemgr(); + + dns_zone_detach(&zone1); + dns_zone_detach(&zone2); + dns_zone_detach(&zone3); + dns_view_detach(&view); + + dns_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, apply); + ATF_TP_ADD_TC(tp, asyncload_zone); + ATF_TP_ADD_TC(tp, asyncload_zt); + return (atf_no_error()); +} diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c index 06fe182e..498ca0e3 100644 --- a/lib/dns/tsig.c +++ b/lib/dns/tsig.c @@ -16,7 +16,7 @@ */ /* - * $Id: tsig.c,v 1.148 2011-03-21 19:54:03 each Exp $ + * $Id: tsig.c,v 1.150 2011-08-29 04:02:54 marka Exp $ */ /*! \file */ #include <config.h> @@ -889,6 +889,7 @@ dns_tsig_sign(dns_message_t *msg) { isc_result_t ret; unsigned char badtimedata[BADTIMELEN]; unsigned int sigsize = 0; + isc_boolean_t response = is_response(msg); REQUIRE(msg != NULL); REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg))); @@ -896,7 +897,7 @@ dns_tsig_sign(dns_message_t *msg) { /* * If this is a response, there should be a query tsig. */ - if (is_response(msg) && msg->querytsig == NULL) + if (response && msg->querytsig == NULL) return (DNS_R_EXPECTEDTSIG); dynbuf = NULL; @@ -919,7 +920,7 @@ dns_tsig_sign(dns_message_t *msg) { isc_buffer_init(&databuf, data, sizeof(data)); - if (is_response(msg)) + if (response) tsig.error = msg->querytsigstatus; else tsig.error = dns_rcode_noerror; @@ -948,7 +949,7 @@ dns_tsig_sign(dns_message_t *msg) { /* * If this is a response, digest the query signature. */ - if (is_response(msg)) { + if (response) { dns_rdata_t querytsigrdata = DNS_RDATA_INIT; ret = dns_rdataset_first(msg->querytsig); @@ -1017,8 +1018,10 @@ dns_tsig_sign(dns_message_t *msg) { } /* Digest the timesigned and fudge */ isc_buffer_clear(&databuf); - if (tsig.error == dns_tsigerror_badtime) + if (tsig.error == dns_tsigerror_badtime) { + INSIST(response); tsig.timesigned = querytsig.timesigned; + } isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_usedregion(&databuf, &r); @@ -1068,7 +1071,7 @@ dns_tsig_sign(dns_message_t *msg) { digestbits = dst_key_getbits(key->key); if (digestbits != 0) { unsigned int bytes = (digestbits + 1) / 8; - if (is_response(msg) && bytes < querytsig.siglen) + if (response && bytes < querytsig.siglen) bytes = querytsig.siglen; if (bytes > isc_buffer_usedlength(&sigbuf)) bytes = isc_buffer_usedlength(&sigbuf); @@ -1170,10 +1173,12 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, isc_uint16_t addcount, id; unsigned int siglen; unsigned int alg; + isc_boolean_t response; REQUIRE(source != NULL); REQUIRE(DNS_MESSAGE_VALID(msg)); tsigkey = dns_message_gettsigkey(msg); + response = is_response(msg); REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey)); @@ -1195,8 +1200,7 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, * If this is a response and there's no key or query TSIG, there * shouldn't be one on the response. */ - if (is_response(msg) && - (tsigkey == NULL || msg->querytsig == NULL)) + if (response && (tsigkey == NULL || msg->querytsig == NULL)) return (DNS_R_UNEXPECTEDTSIG); mctx = msg->mctx; @@ -1215,7 +1219,7 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, if (ret != ISC_R_SUCCESS) return (ret); dns_rdata_reset(&rdata); - if (is_response(msg)) { + if (response) { ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) return (ret); @@ -1228,7 +1232,7 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, /* * Do the key name and algorithm match that of the query? */ - if (is_response(msg) && + if (response && (!dns_name_equal(keyname, &tsigkey->name) || !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) { msg->tsigstatus = dns_tsigerror_badkey; @@ -1326,7 +1330,7 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, if (ret != ISC_R_SUCCESS) return (ret); - if (is_response(msg)) { + if (response) { isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); diff --git a/lib/dns/update.c b/lib/dns/update.c index a9b1dfad..9099994d 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -14,14 +14,1839 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.3 2011-07-01 23:47:44 tbox Exp $ */ +/* $Id: update.c,v 1.5 2011-08-30 23:46:52 tbox Exp $ */ -#include "config.h" +#include <config.h> -#include <isc/stdtime.h> +#include <isc/log.h> +#include <isc/netaddr.h> +#include <isc/print.h> #include <isc/serial.h> +#include <isc/stats.h> +#include <isc/stdtime.h> +#include <isc/string.h> +#include <isc/taskpool.h> +#include <isc/util.h> +#include <dns/db.h> +#include <dns/dbiterator.h> +#include <dns/diff.h> +#include <dns/dnssec.h> +#include <dns/events.h> +#include <dns/fixedname.h> +#include <dns/journal.h> +#include <dns/keyvalues.h> +#include <dns/log.h> +#include <dns/message.h> +#include <dns/nsec.h> +#include <dns/nsec3.h> +#include <dns/private.h> +#include <dns/rdataclass.h> +#include <dns/rdataset.h> +#include <dns/rdatasetiter.h> +#include <dns/rdatastruct.h> +#include <dns/rdatatype.h> +#include <dns/result.h> +#include <dns/soa.h> +#include <dns/ssu.h> +#include <dns/tsig.h> #include <dns/update.h> +#include <dns/view.h> +#include <dns/zone.h> +#include <dns/zt.h> + + +/**************************************************************************/ + +/*% + * Log level for tracing dynamic update protocol requests. + */ +#define LOGLEVEL_PROTOCOL ISC_LOG_INFO + +/*% + * Log level for low-level debug tracing. + */ +#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) + +/*% + * Check an operation for failure. These macros all assume that + * the function using them has a 'result' variable and a 'failure' + * label. + */ +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally with result 'code', which must not + * be ISC_R_SUCCESS. The reason for failure presumably has + * been logged already. + * + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ + +#define FAIL(code) \ + do { \ + result = (code); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally and log as a client error. + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ +#define FAILC(code, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s (%s)", _what, \ + msg, isc_result_totext(result)); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +#define FAILN(code, name, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ + char _nbuf[DNS_NAME_FORMATSIZE]; \ + dns_name_format(name, _nbuf, sizeof(_nbuf)); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s: %s (%s)", _what, _nbuf, \ + msg, isc_result_totext(result)); \ + } \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +#define FAILNT(code, name, type, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ + char _nbuf[DNS_NAME_FORMATSIZE]; \ + char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ + dns_name_format(name, _nbuf, sizeof(_nbuf)); \ + dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s/%s: %s (%s)", \ + _what, _nbuf, _tbuf, msg, \ + isc_result_totext(result)); \ + } \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally and log as a server error. + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ +#define FAILS(code, msg) \ + do { \ + result = (code); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "error: %s: %s", \ + msg, isc_result_totext(result)); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/**************************************************************************/ + +typedef struct rr rr_t; + +struct rr { + /* dns_name_t name; */ + isc_uint32_t ttl; + dns_rdata_t rdata; +}; + +typedef struct update_event update_event_t; + +/**************************************************************************/ + +static void +update_log(dns_update_log_t *callback, dns_zone_t *zone, + int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5); + +static void +update_log(dns_update_log_t *callback, dns_zone_t *zone, + int level, const char *fmt, ...) +{ + va_list ap; + char message[4096]; + + if (callback == NULL) + return; + + if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) + return; + + + va_start(ap, fmt); + vsnprintf(message, sizeof(message), fmt, ap); + va_end(ap); + + (callback->func)(callback->arg, zone, level, message); +} + +/*% + * Update a single RR in version 'ver' of 'db' and log the + * update in 'diff'. + * + * Ensures: + * \li '*tuple' == NULL. Either the tuple is freed, or its + * ownership has been transferred to the diff. + */ +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 +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)); +} + +/**************************************************************************/ +/* + * Callback-style iteration over rdatasets and rdatas. + * + * foreach_rrset() can be used to iterate over the RRsets + * of a name and call a callback function with each + * one. Similarly, foreach_rr() can be used to iterate + * over the individual RRs at name, optionally restricted + * to RRs of a given type. + * + * The callback functions are called "actions" and take + * two arguments: a void pointer for passing arbitrary + * context information, and a pointer to the current RRset + * or RR. By convention, their names end in "_action". + */ + +/* + * XXXRTH We might want to make this public somewhere in libdns. + */ + +/*% + * Function type for foreach_rrset() iterator actions. + */ +typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset); + +/*% + * Function type for foreach_rr() iterator actions. + */ +typedef isc_result_t rr_func(void *data, rr_t *rr); + +/*% + * Internal context struct for foreach_node_rr(). + */ +typedef struct { + rr_func * rr_action; + void * rr_action_data; +} foreach_node_rr_ctx_t; + +/*% + * Internal helper function for foreach_node_rr(). + */ +static isc_result_t +foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) { + isc_result_t result; + foreach_node_rr_ctx_t *ctx = data; + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + rr_t rr = { 0, DNS_RDATA_INIT }; + + dns_rdataset_current(rdataset, &rr.rdata); + rr.ttl = rdataset->ttl; + result = (*ctx->rr_action)(ctx->rr_action_data, &rr); + if (result != ISC_R_SUCCESS) + return (result); + } + if (result != ISC_R_NOMORE) + return (result); + return (ISC_R_SUCCESS); +} + +/*% + * For each rdataset of 'name' in 'ver' of 'db', call 'action' + * with the rdataset and 'action_data' as arguments. If the name + * does not exist, do nothing. + * + * If 'action' returns an error, abort iteration and return the error. + */ +static isc_result_t +foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + rrset_func *action, void *action_data) +{ + isc_result_t result; + dns_dbnode_t *node; + dns_rdatasetiter_t *iter; + + node = NULL; + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + iter = NULL; + result = dns_db_allrdatasets(db, node, ver, + (isc_stdtime_t) 0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdataset_t rdataset; + + dns_rdataset_init(&rdataset); + dns_rdatasetiter_current(iter, &rdataset); + + result = (*action)(action_data, &rdataset); + + dns_rdataset_disassociate(&rdataset); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + cleanup_iterator: + dns_rdatasetiter_destroy(&iter); + + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/*% + * For each RR of 'name' in 'ver' of 'db', call 'action' + * with the RR and 'action_data' as arguments. If the name + * does not exist, do nothing. + * + * If 'action' returns an error, abort iteration + * and return the error. + */ +static isc_result_t +foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + rr_func *rr_action, void *rr_action_data) +{ + foreach_node_rr_ctx_t ctx; + ctx.rr_action = rr_action; + ctx.rr_action_data = rr_action_data; + return (foreach_rrset(db, ver, name, + foreach_node_rr_action, &ctx)); +} + + +/*% + * For each of the RRs specified by 'db', 'ver', 'name', 'type', + * (which can be dns_rdatatype_any to match any type), and 'covers', call + * 'action' with the RR and 'action_data' as arguments. If the name + * does not exist, or if no RRset of the given type exists at the name, + * do nothing. + * + * If 'action' returns an error, abort iteration and return the error. + */ +static isc_result_t +foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action, + void *rr_action_data) +{ + + isc_result_t result; + dns_dbnode_t *node; + dns_rdataset_t rdataset; + + if (type == dns_rdatatype_any) + return (foreach_node_rr(db, ver, name, + rr_action, rr_action_data)); + + node = NULL; + if (type == dns_rdatatype_nsec3 || + (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3)) + result = dns_db_findnsec3node(db, name, ISC_FALSE, &node); + else + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, type, covers, + (isc_stdtime_t) 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + goto cleanup_node; + } + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + rr_t rr = { 0, DNS_RDATA_INIT }; + dns_rdataset_current(&rdataset, &rr.rdata); + rr.ttl = rdataset.ttl; + result = (*rr_action)(rr_action_data, &rr); + if (result != ISC_R_SUCCESS) + goto cleanup_rdataset; + } + if (result != ISC_R_NOMORE) + goto cleanup_rdataset; + result = ISC_R_SUCCESS; + + cleanup_rdataset: + dns_rdataset_disassociate(&rdataset); + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/**************************************************************************/ +/* + * Various tests on the database contents (for prerequisites, etc). + */ + +/*% + * Function type for predicate functions that compare a database RR 'db_rr' + * against an update RR 'update_rr'. + */ +typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); + +/*% + * Helper function for rrset_exists(). + */ +static isc_result_t +rrset_exists_action(void *data, rr_t *rr) { + UNUSED(data); + UNUSED(rr); + return (ISC_R_EXISTS); +} + +/*% + * Utility macro for RR existence checking functions. + * + * If the variable 'result' has the value ISC_R_EXISTS or + * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE, + * respectively, and return success. + * + * If 'result' has any other value, there was a failure. + * Return the failure result code and do not set *exists. + * + * This would be more readable as "do { if ... } while(0)", + * but that form generates tons of warnings on Solaris 2.6. + */ +#define RETURN_EXISTENCE_FLAG \ + return ((result == ISC_R_EXISTS) ? \ + (*exists = ISC_TRUE, ISC_R_SUCCESS) : \ + ((result == ISC_R_SUCCESS) ? \ + (*exists = ISC_FALSE, ISC_R_SUCCESS) : \ + result)) + +/*% + * Set '*exists' to true iff an rrset of the given type exists, + * to false otherwise. + */ +static isc_result_t +rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rr(db, ver, name, type, covers, + rrset_exists_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/*% + * Set '*visible' to true if the RRset exists and is part of the + * visible zone. Otherwise '*visible' is set to false unless a + * error occurs. + */ +static isc_result_t +rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, isc_boolean_t *visible) +{ + isc_result_t result; + dns_fixedname_t fixed; + + dns_fixedname_init(&fixed); + result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&fixed), NULL, NULL); + switch (result) { + case ISC_R_SUCCESS: + *visible = ISC_TRUE; + break; + /* + * Glue, obscured, deleted or replaced records. + */ + case DNS_R_DELEGATION: + case DNS_R_DNAME: + case DNS_R_CNAME: + case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: + case DNS_R_EMPTYNAME: + case DNS_R_COVERINGNSEC: + *visible = ISC_FALSE; + result = ISC_R_SUCCESS; + break; + default: + break; + } + return (result); +} + +/*% + * Context struct and helper function for name_exists(). + */ + +static isc_result_t +name_exists_action(void *data, dns_rdataset_t *rrset) { + UNUSED(data); + UNUSED(rrset); + return (ISC_R_EXISTS); +} + +/*% + * Set '*exists' to true iff the given name exists, to false otherwise. + */ +static isc_result_t +name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rrset(db, ver, name, + name_exists_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/**************************************************************************/ +/* + * Checking of "RRset exists (value dependent)" prerequisites. + * + * In the RFC2136 section 3.2.5, this is the pseudocode involving + * a variable called "temp", a mapping of <name, type> tuples to rrsets. + * + * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t" + * where each tuple has op==DNS_DIFFOP_EXISTS. + */ + +/*% + * A comparison function defining the sorting order for the entries + * in the "temp" data structure. The major sort key is the owner name, + * followed by the type and rdata. + */ +static int +temp_order(const void *av, const void *bv) { + dns_difftuple_t const * const *ap = av; + dns_difftuple_t const * const *bp = bv; + dns_difftuple_t const *a = *ap; + dns_difftuple_t const *b = *bp; + int r; + r = dns_name_compare(&a->name, &b->name); + if (r != 0) + return (r); + r = (b->rdata.type - a->rdata.type); + if (r != 0) + return (r); + r = dns_rdata_casecompare(&a->rdata, &b->rdata); + return (r); +} + +/**************************************************************************/ +/* + * Conditional deletion of RRs. + */ + +/*% + * Context structure for delete_if(). + */ + +typedef struct { + rr_predicate *predicate; + dns_db_t *db; + dns_dbversion_t *ver; + dns_diff_t *diff; + dns_name_t *name; + dns_rdata_t *update_rr; +} conditional_delete_ctx_t; + +/*% + * Predicate functions for delete_if(). + */ + +/*% + * Return true always. + */ +static isc_boolean_t +true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + UNUSED(update_rr); + UNUSED(db_rr); + return (ISC_TRUE); +} + +/*% + * Return true if the record is a RRSIG. + */ +static isc_boolean_t +rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + UNUSED(update_rr); + return ((db_rr->type == dns_rdatatype_rrsig) ? + ISC_TRUE : ISC_FALSE); +} + +/*% + * Internal helper function for delete_if(). + */ +static isc_result_t +delete_if_action(void *data, rr_t *rr) { + conditional_delete_ctx_t *ctx = data; + if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) { + isc_result_t result; + result = update_one_rr(ctx->db, ctx->ver, ctx->diff, + DNS_DIFFOP_DEL, ctx->name, + rr->ttl, &rr->rdata); + return (result); + } else { + return (ISC_R_SUCCESS); + } +} + +/*% + * Conditionally delete RRs. Apply 'predicate' to the RRs + * specified by 'db', 'ver', 'name', and 'type' (which can + * be dns_rdatatype_any to match any type). Delete those + * RRs for which the predicate returns true, and log the + * deletions in 'diff'. + */ +static isc_result_t +delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, + dns_rdata_t *update_rr, dns_diff_t *diff) +{ + conditional_delete_ctx_t ctx; + ctx.predicate = predicate; + ctx.db = db; + ctx.ver = ver; + ctx.diff = diff; + ctx.name = name; + ctx.update_rr = update_rr; + return (foreach_rr(db, ver, name, type, covers, + delete_if_action, &ctx)); +} + +/**************************************************************************/ +/* + * Incremental updating of NSECs and RRSIGs. + */ + +#define MAXZONEKEYS 32 /*%< Maximum number of zone keys supported. */ + +/*% + * We abuse the dns_diff_t type to represent a set of domain names + * affected by the update. + */ +static isc_result_t +namelist_append_name(dns_diff_t *list, dns_name_t *name) { + isc_result_t result; + dns_difftuple_t *tuple = NULL; + static dns_rdata_t dummy_rdata = DNS_RDATA_INIT; + + CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, + &dummy_rdata, &tuple)); + dns_diff_append(list, &tuple); + failure: + return (result); +} + +static isc_result_t +namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected) +{ + isc_result_t result; + dns_fixedname_t fixedname; + dns_name_t *child; + dns_dbiterator_t *dbit = NULL; + + dns_fixedname_init(&fixedname); + child = dns_fixedname_name(&fixedname); + + CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit)); + + for (result = dns_dbiterator_seek(dbit, name); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbit)) + { + dns_dbnode_t *node = NULL; + CHECK(dns_dbiterator_current(dbit, &node, child)); + dns_db_detachnode(db, &node); + if (! dns_name_issubdomain(child, name)) + break; + CHECK(namelist_append_name(affected, child)); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + failure: + if (dbit != NULL) + dns_dbiterator_destroy(&dbit); + return (result); +} + + + +/*% + * Helper function for non_nsec_rrset_exists(). + */ +static isc_result_t +is_non_nsec_action(void *data, dns_rdataset_t *rrset) { + UNUSED(data); + if (!(rrset->type == dns_rdatatype_nsec || + rrset->type == dns_rdatatype_nsec3 || + (rrset->type == dns_rdatatype_rrsig && + (rrset->covers == dns_rdatatype_nsec || + rrset->covers == dns_rdatatype_nsec3)))) + return (ISC_R_EXISTS); + return (ISC_R_SUCCESS); +} + +/*% + * Check whether there is an rrset other than a NSEC or RRSIG NSEC, + * i.e., anything that justifies the continued existence of a name + * after a secure update. + * + * If such an rrset exists, set '*exists' to ISC_TRUE. + * Otherwise, set it to ISC_FALSE. + */ +static isc_result_t +non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *name, isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/*% + * A comparison function for sorting dns_diff_t:s by name. + */ +static int +name_order(const void *av, const void *bv) { + dns_difftuple_t const * const *ap = av; + dns_difftuple_t const * const *bp = bv; + dns_difftuple_t const *a = *ap; + dns_difftuple_t const *b = *bp; + return (dns_name_compare(&a->name, &b->name)); +} + +static isc_result_t +uniqify_name_list(dns_diff_t *list) { + isc_result_t result; + dns_difftuple_t *p, *q; + + CHECK(dns_diff_sort(list, name_order)); + + p = ISC_LIST_HEAD(list->tuples); + while (p != NULL) { + do { + q = ISC_LIST_NEXT(p, link); + if (q == NULL || ! dns_name_equal(&p->name, &q->name)) + break; + ISC_LIST_UNLINK(list->tuples, q, link); + dns_difftuple_free(&q); + } while (1); + p = ISC_LIST_NEXT(p, link); + } + failure: + return (result); +} + +static isc_result_t +is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + isc_boolean_t *flag, isc_boolean_t *cut, isc_boolean_t *unsecure) +{ + isc_result_t result; + dns_fixedname_t foundname; + dns_fixedname_init(&foundname); + result = dns_db_find(db, name, ver, dns_rdatatype_any, + DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&foundname), + NULL, NULL); + if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) { + *flag = ISC_TRUE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (ISC_R_SUCCESS); + } else if (result == DNS_R_ZONECUT) { + *flag = ISC_TRUE; + *cut = ISC_TRUE; + if (unsecure != NULL) { + /* + * We are at the zonecut. Check to see if there + * is a DS RRset. + */ + if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&foundname), + NULL, NULL) == DNS_R_NXRRSET) + *unsecure = ISC_TRUE; + else + *unsecure = ISC_FALSE; + } + return (ISC_R_SUCCESS); + } else if (result == DNS_R_GLUE || result == DNS_R_DNAME || + result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) { + *flag = ISC_FALSE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (ISC_R_SUCCESS); + } else { + /* + * Silence compiler. + */ + *flag = ISC_FALSE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (result); + } +} + +/*% + * Find the next/previous name that has a NSEC record. + * In other words, skip empty database nodes and names that + * have had their NSECs removed because they are obscured by + * a zone cut. + */ +static isc_result_t +next_active(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname, + isc_boolean_t forward) +{ + isc_result_t result; + dns_dbiterator_t *dbit = NULL; + isc_boolean_t has_nsec = ISC_FALSE; + unsigned int wraps = 0; + isc_boolean_t secure = dns_db_issecure(db); + + CHECK(dns_db_createiterator(db, 0, &dbit)); + + CHECK(dns_dbiterator_seek(dbit, oldname)); + do { + dns_dbnode_t *node = NULL; + + if (forward) + result = dns_dbiterator_next(dbit); + else + result = dns_dbiterator_prev(dbit); + if (result == ISC_R_NOMORE) { + /* + * Wrap around. + */ + if (forward) + CHECK(dns_dbiterator_first(dbit)); + else + CHECK(dns_dbiterator_last(dbit)); + wraps++; + if (wraps == 2) { + update_log(log, zone, ISC_LOG_ERROR, + "secure zone with no NSECs"); + result = DNS_R_BADZONE; + goto failure; + } + } + CHECK(dns_dbiterator_current(dbit, &node, newname)); + dns_db_detachnode(db, &node); + + /* + * The iterator may hold the tree lock, and + * rrset_exists() calls dns_db_findnode() which + * may try to reacquire it. To avoid deadlock + * we must pause the iterator first. + */ + CHECK(dns_dbiterator_pause(dbit)); + if (secure) { + CHECK(rrset_exists(db, ver, newname, + dns_rdatatype_nsec, 0, &has_nsec)); + } else { + dns_fixedname_t ffound; + dns_name_t *found; + dns_fixedname_init(&ffound); + found = dns_fixedname_name(&ffound); + result = dns_db_find(db, newname, ver, + dns_rdatatype_soa, + DNS_DBFIND_NOWILD, 0, NULL, found, + NULL, NULL); + if (result == ISC_R_SUCCESS || + result == DNS_R_EMPTYNAME || + result == DNS_R_NXRRSET || + result == DNS_R_CNAME || + (result == DNS_R_DELEGATION && + dns_name_equal(newname, found))) { + has_nsec = ISC_TRUE; + result = ISC_R_SUCCESS; + } else if (result != DNS_R_NXDOMAIN) + break; + } + } while (! has_nsec); + failure: + if (dbit != NULL) + dns_dbiterator_destroy(&dbit); + + return (result); +} + +/*% + * Add a NSEC record for "name", recording the change in "diff". + * The existing NSEC is removed. + */ +static isc_result_t +add_nsec(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl, + dns_diff_t *diff) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + unsigned char buffer[DNS_NSEC_BUFFERSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_difftuple_t *tuple = NULL; + dns_fixedname_t fixedname; + dns_name_t *target; + + dns_fixedname_init(&fixedname); + target = dns_fixedname_name(&fixedname); + + /* + * Find the successor name, aka NSEC target. + */ + CHECK(next_active(log, zone, db, ver, name, target, ISC_TRUE)); + + /* + * Create the NSEC RDATA. + */ + CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); + dns_rdata_init(&rdata); + CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); + dns_db_detachnode(db, &node); + + /* + * Delete the old NSEC and record the change. + */ + CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0, + NULL, diff)); + /* + * Add the new NSEC and record the change. + */ + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, + nsecttl, &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + INSIST(tuple == NULL); + + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/*% + * Add a placeholder NSEC record for "name", recording the change in "diff". + */ +static isc_result_t +add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_diff_t *diff) +{ + isc_result_t result; + dns_difftuple_t *tuple = NULL; + isc_region_t r; + unsigned char data[1] = { 0 }; /* The root domain, no bits. */ + dns_rdata_t rdata = DNS_RDATA_INIT; + + r.base = data; + r.length = sizeof(data); + dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r); + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, + &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + failure: + return (result); +} + +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, + dst_key_t **keys, unsigned int *nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + const char *directory = dns_zone_getkeydirectory(zone); + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), + directory, mctx, maxkeys, keys, nkeys)); + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/*% + * Add RRSIG records for an RRset, recording the change in "diff". + */ +static isc_result_t +add_sigs(dns_update_log_t *log, dns_zone_t *zone, 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_stdtime_t inception, isc_stdtime_t expire, + isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t sig_rdata = DNS_RDATA_INIT; + isc_buffer_t buffer; + unsigned char data[1024]; /* XXX */ + unsigned int i, j; + isc_boolean_t added_sig = ISC_FALSE; + isc_mem_t *mctx = diff->mctx; + + dns_rdataset_init(&rdataset); + isc_buffer_init(&buffer, data, sizeof(data)); + + /* Get the rdataset to sign. */ + if (type == dns_rdatatype_nsec3) + CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node)); + else + CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); + CHECK(dns_db_findrdataset(db, node, ver, type, 0, + (isc_stdtime_t) 0, &rdataset, NULL)); + dns_db_detachnode(db, &node); + +#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) + + /* + * If we are honoring KSK flags then we need to check that we + * have both KSK and non-KSK keys that are not revoked per + * algorithm. + */ + for (i = 0; i < nkeys; i++) { + 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. */ + CHECK(dns_dnssec_sign(name, &rdataset, keys[i], + &inception, &expire, + mctx, &buffer, &sig_rdata)); + + /* Update the database and journal with the RRSIG. */ + /* XXX inefficient - will cause dataset merging */ + 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)); + added_sig = ISC_TRUE; + } + if (!added_sig) { + update_log(log, zone, ISC_LOG_ERROR, + "found no active private keys, " + "unable to generate any signatures"); + result = ISC_R_NOTFOUND; + } + + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/* + * Delete expired RRsigs and any RRsigs we are about to re-sign. + * See also zone.c:del_sigs(). + */ +static isc_result_t +del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i; + dns_rdata_rrsig_t rrsig; + isc_boolean_t found; + + dns_rdataset_init(&rdataset); + + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, + dns_rdatatype_dnskey, (isc_stdtime_t) 0, + &rdataset, NULL); + dns_db_detachnode(db, &node); + + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + found = ISC_FALSE; + for (i = 0; i < nkeys; i++) { + if (rrsig.keyid == dst_key_id(keys[i])) { + found = ISC_TRUE; + if (!dst_key_isprivate(keys[i])) { + /* + * The re-signing code in zone.c + * will mark this as offline. + * Just skip the record for now. + */ + break; + } + result = update_one_rr(db, ver, diff, + DNS_DIFFOP_DEL, name, + rdataset.ttl, &rdata); + break; + } + } + /* + * If there is not a matching DNSKEY then delete the RRSIG. + */ + if (!found) + result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, + name, rdataset.ttl, &rdata); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) + break; + } + dns_rdataset_disassociate(&rdataset); + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; +failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +static isc_result_t +add_exposed_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, isc_boolean_t cut, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, + isc_stdtime_t inception, isc_stdtime_t expire, + isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) +{ + isc_result_t result; + dns_dbnode_t *node; + dns_rdatasetiter_t *iter; + + node = NULL; + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + iter = NULL; + result = dns_db_allrdatasets(db, node, ver, + (isc_stdtime_t) 0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdataset_t rdataset; + dns_rdatatype_t type; + isc_boolean_t flag; + + dns_rdataset_init(&rdataset); + dns_rdatasetiter_current(iter, &rdataset); + type = rdataset.type; + dns_rdataset_disassociate(&rdataset); + + /* + * We don't need to sign unsigned NSEC records at the cut + * as they are handled elsewhere. + */ + if ((type == dns_rdatatype_rrsig) || + (cut && type != dns_rdatatype_ds)) + continue; + result = rrset_exists(db, ver, name, dns_rdatatype_rrsig, + type, &flag); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + if (flag) + continue;; + result = add_sigs(log, zone, db, ver, name, type, diff, + keys, nkeys, inception, expire, + check_ksk, keyset_kskonly); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + cleanup_iterator: + dns_rdatasetiter_destroy(&iter); + + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/*% + * Update RRSIG, NSEC and NSEC3 records affected by an update. The original + * update, including the SOA serial update but excluding the RRSIG & NSEC + * changes, is in "diff" and has already been applied to "newver" of "db". + * The database version prior to the update is "oldver". + * + * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver" + * and added (as a minimal diff) to "diff". + * + * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds. + */ +isc_result_t +dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *oldver, dns_dbversion_t *newver, + dns_diff_t *diff, isc_uint32_t sigvalidityinterval) +{ + isc_result_t result; + dns_difftuple_t *t; + dns_diff_t diffnames; + dns_diff_t affected; + dns_diff_t sig_diff; + dns_diff_t nsec_diff; + dns_diff_t nsec_mindiff; + isc_boolean_t flag, build_nsec, build_nsec3; + dst_key_t *zone_keys[MAXZONEKEYS]; + unsigned int nkeys = 0; + unsigned int i; + isc_stdtime_t now, inception, expire; + dns_ttl_t nsecttl; + dns_rdata_soa_t soa; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t rdataset; + dns_dbnode_t *node = NULL; + isc_boolean_t check_ksk, keyset_kskonly; + isc_boolean_t unsecure; + isc_boolean_t cut; + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); + + dns_diff_init(diff->mctx, &diffnames); + dns_diff_init(diff->mctx, &affected); + + dns_diff_init(diff->mctx, &sig_diff); + sig_diff.resign = dns_zone_getsigresigninginterval(zone); + dns_diff_init(diff->mctx, &nsec_diff); + dns_diff_init(diff->mctx, &nsec_mindiff); + + result = find_zone_keys(zone, db, newver, diff->mctx, + MAXZONEKEYS, zone_keys, &nkeys); + if (result != ISC_R_SUCCESS) { + update_log(log, zone, ISC_LOG_ERROR, + "could not get zone keys for secure dynamic update"); + goto failure; + } + + isc_stdtime_get(&now); + inception = now - 3600; /* Allow for some clock skew. */ + expire = now + sigvalidityinterval; + + /* + * Do we look at the KSK flag on the DNSKEY to determining which + * keys sign which RRsets? First check the zone option then + * check the keys flags to make sure at least one has a ksk set + * and one doesn't. + */ + check_ksk = ISC_TF((dns_zone_getoptions(zone) & + DNS_ZONEOPT_UPDATECHECKKSK) != 0); + keyset_kskonly = ISC_TF((dns_zone_getoptions(zone) & + DNS_ZONEOPT_DNSKEYKSKONLY) != 0); + + /* + * Get the NSEC/NSEC3 TTL from the SOA MINIMUM field. + */ + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + dns_rdataset_init(&rdataset); + CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0, + (isc_stdtime_t) 0, &rdataset, NULL)); + CHECK(dns_rdataset_first(&rdataset)); + dns_rdataset_current(&rdataset, &rdata); + CHECK(dns_rdata_tostruct(&rdata, &soa, NULL)); + nsecttl = soa.minimum; + dns_rdataset_disassociate(&rdataset); + dns_db_detachnode(db, &node); + + /* + * Find all RRsets directly affected by the update, and + * update their RRSIGs. Also build a list of names affected + * by the update in "diffnames". + */ + CHECK(dns_diff_sort(diff, temp_order)); + + t = ISC_LIST_HEAD(diff->tuples); + while (t != NULL) { + dns_name_t *name = &t->name; + /* Now "name" is a new, unique name affected by the update. */ + + CHECK(namelist_append_name(&diffnames, name)); + + while (t != NULL && dns_name_equal(&t->name, name)) { + dns_rdatatype_t type; + type = t->rdata.type; + + /* + * Now "name" and "type" denote a new unique RRset + * affected by the update. + */ + + /* Don't sign RRSIGs. */ + if (type == dns_rdatatype_rrsig) + goto skip; + + /* + * Delete all old RRSIGs covering this type, since they + * are all invalid when the signed RRset has changed. + * We may not be able to recreate all of them - tough. + * Special case changes to the zone's DNSKEY records + * to support offline KSKs. + */ + if (type == dns_rdatatype_dnskey) + del_keysigs(db, newver, name, &sig_diff, + zone_keys, nkeys); + else + CHECK(delete_if(true_p, db, newver, name, + dns_rdatatype_rrsig, type, + NULL, &sig_diff)); + + /* + * If this RRset is still visible after the update, + * add a new signature for it. + */ + CHECK(rrset_visible(db, newver, name, type, &flag)); + if (flag) { + CHECK(add_sigs(log, zone, db, newver, name, + type, &sig_diff, zone_keys, + nkeys, inception, expire, + check_ksk, keyset_kskonly)); + } + skip: + /* Skip any other updates to the same RRset. */ + while (t != NULL && + dns_name_equal(&t->name, name) && + t->rdata.type == type) + { + t = ISC_LIST_NEXT(t, link); + } + } + } + update_log(log, zone, ISC_LOG_DEBUG(3), "updated data signatures"); + + /* Remove orphaned NSECs and RRSIG NSECs. */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag)); + if (! flag) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_any, 0, + NULL, &sig_diff)); + } + } + update_log(log, zone, ISC_LOG_DEBUG(3), + "removed any orphaned NSEC records"); + + /* + * See if we need to build NSEC or NSEC3 chains. + */ + CHECK(dns_private_chains(db, newver, privatetype, &build_nsec, + &build_nsec3)); + if (!build_nsec) + goto update_nsec3; + + update_log(log, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC chain"); + + /* + * When a name is created or deleted, its predecessor needs to + * have its NSEC updated. + */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t existed, exists; + dns_fixedname_t fixedname; + dns_name_t *prevname; + + dns_fixedname_init(&fixedname); + prevname = dns_fixedname_name(&fixedname); + + if (oldver != NULL) + CHECK(name_exists(db, oldver, &t->name, &existed)); + else + existed = ISC_FALSE; + CHECK(name_exists(db, newver, &t->name, &exists)); + if (exists == existed) + continue; + + /* + * Find the predecessor. + * When names become obscured or unobscured in this update + * transaction, we may find the wrong predecessor because + * the NSECs have not yet been updated to reflect the delegation + * change. This should not matter because in this case, + * the correct predecessor is either the delegation node or + * a newly unobscured node, and those nodes are on the + * "affected" list in any case. + */ + CHECK(next_active(log, zone, db, newver, + &t->name, prevname, ISC_FALSE)); + CHECK(namelist_append_name(&affected, prevname)); + } + + /* + * Find names potentially affected by delegation changes + * (obscured by adding an NS or DNAME, or unobscured by + * removing one). + */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t ns_existed, dname_existed; + isc_boolean_t ns_exists, dname_exists; + + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, &t->name, + dns_rdatatype_ns, 0, &ns_existed)); + else + ns_existed = ISC_FALSE; + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, &t->name, + dns_rdatatype_dname, 0, + &dname_existed)); + else + dname_existed = ISC_FALSE; + CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, + &ns_exists)); + CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0, + &dname_exists)); + if ((ns_exists || dname_exists) == (ns_existed || dname_existed)) + continue; + /* + * There was a delegation change. Mark all subdomains + * of t->name as potentially needing a NSEC update. + */ + CHECK(namelist_append_subdomain(db, &t->name, &affected)); + } + + ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link); + INSIST(ISC_LIST_EMPTY(diffnames.tuples)); + + CHECK(uniqify_name_list(&affected)); + + /* + * Determine which names should have NSECs, and delete/create + * NSECs to make it so. We don't know the final NSEC targets yet, + * so we just create placeholder NSECs with arbitrary contents + * to indicate that their respective owner names should be part of + * the NSEC chain. + */ + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t exists; + dns_name_t *name = &t->name; + + CHECK(name_exists(db, newver, name, &exists)); + if (! exists) + continue; + CHECK(is_active(db, newver, name, &flag, &cut, NULL)); + if (!flag) { + /* + * This name is obscured. Delete any + * existing NSEC record. + */ + CHECK(delete_if(true_p, db, newver, name, + dns_rdatatype_nsec, 0, + NULL, &nsec_diff)); + CHECK(delete_if(rrsig_p, db, newver, name, + dns_rdatatype_any, 0, NULL, diff)); + } else { + /* + * This name is not obscured. It needs to have a + * NSEC unless it is the at the origin, in which + * case it should already exist if there is a complete + * NSEC chain and if there isn't a complete NSEC chain + * we don't want to add one as that would signal that + * there is a complete NSEC chain. + */ + if (!dns_name_equal(name, dns_db_origin(db))) { + CHECK(rrset_exists(db, newver, name, + dns_rdatatype_nsec, 0, + &flag)); + if (!flag) + CHECK(add_placeholder_nsec(db, newver, + name, diff)); + } + CHECK(add_exposed_sigs(log, zone, db, newver, name, + cut, &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + } + } + + /* + * Now we know which names are part of the NSEC chain. + * Make them all point at their correct targets. + */ + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + CHECK(rrset_exists(db, newver, &t->name, + dns_rdatatype_nsec, 0, &flag)); + if (flag) { + /* + * There is a NSEC, but we don't know if it is correct. + * Delete it and create a correct one to be sure. + * If the update was unnecessary, the diff minimization + * will take care of eliminating it from the journal, + * IXFRs, etc. + * + * The RRSIG bit should always be set in the NSECs + * we generate, because they will all get RRSIG NSECs. + * (XXX what if the zone keys are missing?). + * Because the RRSIG NSECs have not necessarily been + * created yet, the correctness of the bit mask relies + * on the assumption that NSECs are only created if + * there is other data, and if there is other data, + * there are other RRSIGs. + */ + CHECK(add_nsec(log, zone, db, newver, &t->name, + nsecttl, &nsec_diff)); + } + } + + /* + * Minimize the set of NSEC updates so that we don't + * have to regenerate the RRSIG NSECs for NSECs that were + * replaced with identical ones. + */ + while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_diff.tuples, t, link); + dns_diff_appendminimal(&nsec_mindiff, &t); + } + + update_log(log, zone, ISC_LOG_DEBUG(3), "signing rebuilt NSEC chain"); + + /* Update RRSIG NSECs. */ + for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + if (t->op == DNS_DIFFOP_DEL) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_rrsig, dns_rdatatype_nsec, + NULL, &sig_diff)); + } else if (t->op == DNS_DIFFOP_ADD) { + CHECK(add_sigs(log, zone, db, newver, &t->name, + dns_rdatatype_nsec, &sig_diff, + zone_keys, nkeys, inception, expire, + check_ksk, keyset_kskonly)); + } else { + INSIST(0); + } + } + + update_nsec3: + + /* Record our changes for the journal. */ + while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(sig_diff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + + INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); + + if (!build_nsec3) { + update_log(log, zone, ISC_LOG_DEBUG(3), + "no NSEC3 chains to rebuild"); + goto failure; + } + + update_log(log, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC3 chains"); + + dns_diff_clear(&diffnames); + dns_diff_clear(&affected); + + CHECK(dns_diff_sort(diff, temp_order)); + + /* + * Find names potentially affected by delegation changes + * (obscured by adding an NS or DNAME, or unobscured by + * removing one). + */ + t = ISC_LIST_HEAD(diff->tuples); + while (t != NULL) { + dns_name_t *name = &t->name; + + isc_boolean_t ns_existed, dname_existed; + isc_boolean_t ns_exists, dname_exists; + isc_boolean_t exists, existed; + + if (t->rdata.type == dns_rdatatype_nsec || + t->rdata.type == dns_rdatatype_rrsig) { + t = ISC_LIST_NEXT(t, link); + continue; + } + + CHECK(namelist_append_name(&affected, name)); + + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, name, dns_rdatatype_ns, + 0, &ns_existed)); + else + ns_existed = ISC_FALSE; + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, name, + dns_rdatatype_dname, 0, + &dname_existed)); + else + dname_existed = ISC_FALSE; + CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns, 0, + &ns_exists)); + CHECK(rrset_exists(db, newver, name, dns_rdatatype_dname, 0, + &dname_exists)); + + exists = ns_exists || dname_exists; + existed = ns_existed || dname_existed; + if (exists == existed) + goto nextname; + /* + * There was a delegation change. Mark all subdomains + * of t->name as potentially needing a NSEC3 update. + */ + CHECK(namelist_append_subdomain(db, name, &affected)); + + nextname: + while (t != NULL && dns_name_equal(&t->name, name)) + t = ISC_LIST_NEXT(t, link); + } + + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) { + dns_name_t *name = &t->name; + + unsecure = ISC_FALSE; /* Silence compiler warning. */ + CHECK(is_active(db, newver, name, &flag, &cut, &unsecure)); + + if (!flag) { + CHECK(delete_if(rrsig_p, db, newver, name, + dns_rdatatype_any, 0, NULL, diff)); + CHECK(dns_nsec3_delnsec3sx(db, newver, name, + privatetype, &nsec_diff)); + } else { + CHECK(add_exposed_sigs(log, zone, db, newver, name, + cut, &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + CHECK(dns_nsec3_addnsec3sx(db, newver, name, nsecttl, + unsecure, privatetype, + &nsec_diff)); + } + } + + /* + * Minimize the set of NSEC3 updates so that we don't + * have to regenerate the RRSIG NSEC3s for NSEC3s that were + * replaced with identical ones. + */ + while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_diff.tuples, t, link); + dns_diff_appendminimal(&nsec_mindiff, &t); + } + + update_log(log, zone, ISC_LOG_DEBUG(3), + "signing rebuilt NSEC3 chain"); + + /* Update RRSIG NSEC3s. */ + for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + if (t->op == DNS_DIFFOP_DEL) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_rrsig, + dns_rdatatype_nsec3, + NULL, &sig_diff)); + } else if (t->op == DNS_DIFFOP_ADD) { + CHECK(add_sigs(log, zone, db, newver, &t->name, + dns_rdatatype_nsec3, + &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + } else { + INSIST(0); + } + } + + /* Record our changes for the journal. */ + while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(sig_diff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + + INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); + + failure: + dns_diff_clear(&sig_diff); + dns_diff_clear(&nsec_diff); + dns_diff_clear(&nsec_mindiff); + + dns_diff_clear(&affected); + dns_diff_clear(&diffnames); + + for (i = 0; i < nkeys; i++) + dst_key_free(&zone_keys[i]); + + return (result); +} isc_uint32_t dns_update_soaserial(isc_uint32_t serial, dns_updatemethod_t method) { diff --git a/lib/dns/view.c b/lib/dns/view.c index fafbae06..cd7a2be4 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.c,v 1.181 2011-08-02 20:36:12 each Exp $ */ +/* $Id: view.c,v 1.184 2011-09-06 22:29:33 smann Exp $ */ /*! \file */ @@ -181,7 +181,6 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->answeracl_exclude = NULL; view->denyanswernames = NULL; view->answernames_exclude = NULL; - view->requestixfr = ISC_TRUE; view->provideixfr = ISC_TRUE; view->maxcachettl = 7 * 24 * 3600; view->maxncachettl = 3 * 3600; @@ -1418,6 +1417,7 @@ isc_result_t dns_view_load(dns_view_t *view, isc_boolean_t stop) { REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); return (dns_zt_load(view->zonetable, stop)); } @@ -1426,9 +1426,20 @@ isc_result_t dns_view_loadnew(dns_view_t *view, isc_boolean_t stop) { REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); return (dns_zt_loadnew(view->zonetable, stop)); } + +isc_result_t +dns_view_asyncload(dns_view_t *view, dns_zt_allloaded_t callback, void *arg) { + REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); + + return (dns_zt_asyncload(view->zonetable, callback, arg)); +} + + #endif /* BIND9 */ isc_result_t @@ -1728,6 +1739,9 @@ isc_result_t dns_view_issecuredomain(dns_view_t *view, dns_name_t *name, isc_boolean_t *secure_domain) { REQUIRE(DNS_VIEW_VALID(view)); + + if (view->secroots_priv == NULL) + return (ISC_R_NOTFOUND); return (dns_keytable_issecuredomain(view->secroots_priv, name, secure_domain)); } diff --git a/lib/dns/win32/libdns.def b/lib/dns/win32/libdns.def index 493ed6b2..6ced2e95 100644 --- a/lib/dns/win32/libdns.def +++ b/lib/dns/win32/libdns.def @@ -699,6 +699,7 @@ dns_tsigrcode_fromtext dns_tsigrcode_totext dns_ttl_fromtext dns_ttl_totext +dns_update_signatures dns_update_soaserial dns_validator_cancel dns_validator_create @@ -706,6 +707,7 @@ dns_validator_destroy dns_validator_send dns_view_adddelegationonly dns_view_addzone +dns_view_asyncload dns_view_attach dns_view_checksig dns_view_create @@ -801,6 +803,7 @@ dns_zone_getoptions dns_zone_getorigin dns_zone_getprivatetype dns_zone_getqueryacl +dns_zone_getraw dns_zone_getrequeststats dns_zone_getserial dns_zone_getserial2 @@ -822,6 +825,7 @@ dns_zone_iattach dns_zone_idetach dns_zone_isdynamic dns_zone_isforced +dns_zone_link dns_zone_load dns_zone_loadandthaw dns_zone_loadnew @@ -881,6 +885,7 @@ dns_zone_setprivatetype dns_zone_setqueryacl dns_zone_setqueryonacl dns_zone_setrefreshkeyinterval +dns_zone_setrequestixfr dns_zone_setrequeststats dns_zone_setserialupdatemethod dns_zone_setsignatures diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 31d21891..e0a23ecc 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: xfrin.c,v 1.170 2011-03-11 06:11:25 marka Exp $ */ +/* $Id: xfrin.c,v 1.171 2011-08-30 05:16:14 marka Exp $ */ /*! \file */ @@ -630,7 +630,8 @@ dns_xfrin_create2(dns_zone_t *zone, dns_rdatatype_t xfrtype, isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, isc_mem_t *mctx, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, - isc_task_t *task, dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp) + isc_task_t *task, dns_xfrindone_t done, + dns_xfrin_ctx_t **xfrp) { dns_name_t *zonename = dns_zone_getorigin(zone); dns_xfrin_ctx_t *xfr = NULL; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 930bbb21..e2ffeb9f 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.623 2011-08-09 02:24:28 marka Exp $ */ +/* $Id: zone.c,v 1.632 2011-09-06 22:29:33 smann Exp $ */ /*! \file */ @@ -79,6 +79,7 @@ #include <dns/update.h> #include <dns/xfrin.h> #include <dns/zone.h> +#include <dns/zt.h> #include <dst/dst.h> @@ -138,6 +139,7 @@ typedef struct dns_notify dns_notify_t; typedef struct dns_stub dns_stub_t; typedef struct dns_load dns_load_t; typedef struct dns_forward dns_forward_t; +typedef ISC_LIST(dns_forward_t) dns_forwardlist_t; typedef struct dns_io dns_io_t; typedef ISC_LIST(dns_io_t) dns_iolist_t; typedef struct dns_signing dns_signing_t; @@ -145,6 +147,7 @@ typedef ISC_LIST(dns_signing_t) dns_signinglist_t; typedef struct dns_nsec3chain dns_nsec3chain_t; typedef ISC_LIST(dns_nsec3chain_t) dns_nsec3chainlist_t; typedef struct dns_keyfetch dns_keyfetch_t; +typedef struct dns_asyncload dns_asyncload_t; #define DNS_ZONE_CHECKLOCK #ifdef DNS_ZONE_CHECKLOCK @@ -244,6 +247,7 @@ struct dns_zone { unsigned int notifycnt; isc_sockaddr_t notifyfrom; isc_task_t *task; + isc_task_t *loadtask; isc_sockaddr_t notifysrc4; isc_sockaddr_t notifysrc6; isc_sockaddr_t xfrsource4; @@ -345,6 +349,19 @@ struct dns_zone { * Serial number update method. */ dns_updatemethod_t updatemethod; + + /*% + * whether ixfr is requested + */ + isc_boolean_t requestixfr; + + /*% + * Outstanding forwarded UPDATE requests. + */ + dns_forwardlist_t forwards; + + dns_zone_t *raw; + dns_zone_t *secure; }; #define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0)) @@ -393,7 +410,7 @@ struct dns_zone { #define DNS_ZONEFLG_NEEDCOMPACT 0x02000000U #define DNS_ZONEFLG_REFRESHING 0x04000000U /*%< Refreshing keydata */ #define DNS_ZONEFLG_THAW 0x08000000U -/* #define DNS_ZONEFLG_XXXXX 0x10000000U XXXMPA unused. */ +#define DNS_ZONEFLG_LOADPENDING 0x10000000U /*%< Loading scheduled */ #define DNS_ZONEFLG_NODELAY 0x20000000U #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0) @@ -427,6 +444,7 @@ struct dns_zonemgr { isc_timermgr_t * timermgr; isc_socketmgr_t * socketmgr; isc_taskpool_t * zonetasks; + isc_taskpool_t * loadtasks; isc_task_t * task; isc_ratelimiter_t * rl; isc_rwlock_t rwlock; @@ -510,6 +528,7 @@ struct dns_forward { isc_sockaddr_t addr; dns_updatecallback_t callback; void *callback_arg; + ISC_LINK(dns_forward_t) link; }; /*% @@ -583,6 +602,15 @@ struct dns_keyfetch { dns_fetch_t *fetch; }; +/*% + * Hold state for an asynchronous load + */ +struct dns_asyncload { + dns_zone_t *zone; + dns_zt_zoneloaded_t loaded; + void *loaded_arg; +}; + #define HOUR 3600 #define DAY (24*HOUR) #define MONTH (30*DAY) @@ -811,6 +839,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->notifytype = dns_notifytype_yes; zone->notifycnt = 0; zone->task = NULL; + zone->loadtask = NULL; zone->update_acl = NULL; zone->forward_acl = NULL; zone->notify_acl = NULL; @@ -863,6 +892,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->privatetype = (dns_rdatatype_t)0xffffU; zone->added = ISC_FALSE; zone->rpz_zone = ISC_FALSE; + ISC_LIST_INIT(zone->forwards); + zone->raw = NULL; + zone->secure = NULL; zone->magic = ZONE_MAGIC; @@ -919,6 +951,8 @@ zone_free(dns_zone_t *zone) { if (zone->task != NULL) isc_task_detach(&zone->task); + if (zone->loadtask != NULL) + isc_task_detach(&zone->loadtask); if (zone->zmgr != NULL) dns_zonemgr_releasezone(zone->zmgr, zone); @@ -1026,6 +1060,8 @@ dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass) { zone_rdclass_tostr(zone, namebuf, sizeof namebuf); zone->strrdclass = isc_mem_strdup(zone->mctx, namebuf); + if (zone->raw != NULL) + dns_zone_setclass(zone->raw, rdclass); UNLOCK_ZONE(zone); } @@ -1217,10 +1253,12 @@ dns_zone_setview(dns_zone_t *zone, dns_view_t *view) { zone_viewname_tostr(zone, namebuf, sizeof namebuf); zone->strviewname = isc_mem_strdup(zone->mctx, namebuf); + if (zone->raw != NULL) + dns_zone_setview(zone->raw, view); + UNLOCK_ZONE(zone); } - dns_view_t * dns_zone_getview(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); @@ -1254,6 +1292,8 @@ dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) { zone_name_tostr(zone, namebuf, sizeof namebuf); zone->strname = isc_mem_strdup(zone->mctx, namebuf); + if (result == ISC_R_SUCCESS && zone->raw != NULL) + result = dns_zone_setorigin(zone->raw, origin); UNLOCK_ZONE(zone); return (result); } @@ -1424,6 +1464,12 @@ zone_load(dns_zone_t *zone, unsigned int flags) { LOCK_ZONE(zone); TIME_NOW(&now); + if (zone->raw != NULL) { + result = zone_load(zone->raw, flags); + if (result != ISC_R_SUCCESS) + goto cleanup; + } + INSIST(zone->type != dns_zone_none); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADING)) { @@ -1433,7 +1479,6 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } - INSIST(zone->db_argc >= 1); rbt = strcmp(zone->db_argv[0], "rbt") == 0 || @@ -1592,6 +1637,82 @@ dns_zone_loadnew(dns_zone_t *zone) { return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT)); } +static void +zone_asyncload(isc_task_t *task, isc_event_t *event) { + dns_asyncload_t *asl = event->ev_arg; + dns_zone_t *zone = asl->zone; + isc_result_t result = ISC_R_SUCCESS; + + UNUSED(task); + + REQUIRE(DNS_ZONE_VALID(zone)); + + if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) + result = ISC_R_CANCELED; + isc_event_free(&event); + if (result == ISC_R_CANCELED || + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) + return; + + zone_load(zone, 0); + + LOCK_ZONE(zone); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING); + UNLOCK_ZONE(zone); + + /* Inform the zone table we've finished loading */ + if (asl->loaded != NULL) + (asl->loaded)(asl->loaded_arg, zone, task); + + isc_mem_put(zone->mctx, asl, sizeof (*asl)); +} + +isc_result_t +dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg) { + isc_event_t *e; + dns_asyncload_t *asl = NULL; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(DNS_ZONE_VALID(zone)); + + if (zone->zmgr == NULL) + return (ISC_R_FAILURE); + + asl = isc_mem_get(zone->mctx, sizeof (*asl)); + if (asl == NULL) + CHECK(ISC_R_NOMEMORY); + + asl->zone = zone; + asl->loaded = done; + asl->loaded_arg = arg; + + e = isc_event_allocate(zone->zmgr->mctx, zone->zmgr, + DNS_EVENT_ZONELOAD, + zone_asyncload, asl, + sizeof(isc_event_t)); + if (e == NULL) + CHECK(ISC_R_NOMEMORY); + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADPENDING); + isc_task_send(zone->loadtask, &e); + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); + + failure: + if (asl != NULL) + isc_mem_put(zone->mctx, asl, sizeof (*asl)); + return (result); +} + +isc_boolean_t +dns__zone_loadpending(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (ISC_TF(DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING))); +} + isc_result_t dns_zone_loadandthaw(dns_zone_t *zone) { isc_result_t result; @@ -1729,7 +1850,7 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS)) options |= DNS_MASTER_MANYERRORS; - if (zone->zmgr != NULL && zone->db != NULL && zone->task != NULL) { + if (zone->zmgr != NULL && zone->db != NULL && zone->loadtask != NULL) { load = isc_mem_get(zone->mctx, sizeof(*load)); if (load == NULL) return (ISC_R_NOMEMORY); @@ -1748,7 +1869,7 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { &load->callbacks.add_private); if (result != ISC_R_SUCCESS) goto cleanup; - result = zonemgr_getio(zone->zmgr, ISC_TRUE, zone->task, + result = zonemgr_getio(zone->zmgr, ISC_TRUE, zone->loadtask, zone_gotreadhandle, load, &zone->readio); if (result != ISC_R_SUCCESS) { @@ -3105,7 +3226,9 @@ update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, * Write all transactions in 'diff' to the zone journal file. */ static isc_result_t -zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { +zone_journal(dns_zone_t *zone, dns_diff_t *diff, isc_uint32_t *bitws, + const char *caller) +{ const char me[] = "zone_journal"; const char *journalfile; isc_result_t result = ISC_R_SUCCESS; @@ -3115,13 +3238,15 @@ zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { journalfile = dns_zone_getjournal(zone); if (journalfile != NULL) { result = dns_journal_open(zone->mctx, journalfile, - ISC_TRUE, &journal); + DNS_JOURNAL_CREATE, &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); } + if (bitws != NULL) + dns_journal_set_bitws(journal, *bitws); result = dns_journal_write_transaction(journal, diff); dns_journal_destroy(&journal); @@ -3314,7 +3439,7 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { /* Write changes to journal file. */ CHECK(update_soa_serial(db, ver, &diff, zone->mctx, zone->updatemethod)); - CHECK(zone_journal(zone, &diff, "sync_keyzone")); + CHECK(zone_journal(zone, &diff, NULL, "sync_keyzone")); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -3423,6 +3548,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "journal rollforward failed: %s", dns_result_totext(result)); goto cleanup; + + } if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -3531,7 +3658,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, result = DNS_R_BADZONE; goto cleanup; } - if (zone->type == dns_zone_master && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRR) && !zone_check_dup(zone, db)) { @@ -3729,6 +3855,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s", serial, dns_db_issecure(db) ? " (DNSSEC signed)" : ""); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING); return (result); cleanup: @@ -3749,6 +3876,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } else if (zone->type == dns_zone_master || zone->type == dns_zone_redirect) dns_zone_log(zone, ISC_LOG_ERROR, "not loaded due to errors."); + return (result); } @@ -4030,6 +4158,8 @@ dns_zone_attach(dns_zone_t *source, dns_zone_t **target) { void dns_zone_detach(dns_zone_t **zonep) { dns_zone_t *zone; + dns_zone_t *raw = NULL; + dns_zone_t *secure = NULL; unsigned int refs; isc_boolean_t free_now = ISC_FALSE; @@ -4067,12 +4197,21 @@ dns_zone_detach(dns_zone_t **zonep) { */ INSIST(zone->view == NULL); free_now = ISC_TRUE; + raw = zone->raw; + zone->raw = NULL; + secure = zone->secure; + zone->secure = NULL; } UNLOCK_ZONE(zone); } *zonep = NULL; - if (free_now) + if (free_now) { + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); zone_free(zone); + } } void @@ -4350,13 +4489,14 @@ clear_addresskeylist(isc_sockaddr_t **addrsp, dns_name_t ***keynamesp, REQUIRE(countp != NULL && addrsp != NULL && keynamesp != NULL); count = *countp; + *countp = 0; addrs = *addrsp; + *addrsp = NULL; keynames = *keynamesp; + *keynamesp = NULL; - if (addrs != NULL) { + if (addrs != NULL) isc_mem_put(mctx, addrs, count * sizeof(isc_sockaddr_t)); - addrs = *addrsp = NULL; - } if (keynames != NULL) { unsigned int i; @@ -4369,10 +4509,7 @@ clear_addresskeylist(isc_sockaddr_t **addrsp, dns_name_t ***keynamesp, } } isc_mem_put(mctx, keynames, count * sizeof(dns_name_t *)); - keynames = *keynamesp = NULL; } - - count = *countp = 0; } static isc_result_t @@ -5185,7 +5322,7 @@ zone_resigninc(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, &sig_diff, "zone_resigninc")); + CHECK(zone_journal(zone, &sig_diff, NULL, "zone_resigninc")); /* Everything has succeeded. Commit the changes. */ dns_db_closeversion(db, &version, ISC_TRUE); @@ -6561,7 +6698,7 @@ zone_nsec3chain(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, &sig_diff, "zone_nsec3chain")); + CHECK(zone_journal(zone, &sig_diff, NULL, "zone_nsec3chain")); LOCK_ZONE(zone); zone_needdump(zone, DNS_DUMP_DELAY); @@ -7131,7 +7268,7 @@ zone_sign(dns_zone_t *zone) { /* * Write changes to journal file. */ - CHECK(zone_journal(zone, &sig_diff, "zone_sign")); + CHECK(zone_journal(zone, &sig_diff, NULL, "zone_sign")); pauseall: /* @@ -7888,7 +8025,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { /* Write changes to journal file. */ CHECK(update_soa_serial(kfetch->db, ver, &diff, mctx, zone->updatemethod)); - CHECK(zone_journal(zone, &diff, "keyfetch_done")); + CHECK(zone_journal(zone, &diff, NULL, "keyfetch_done")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); @@ -8046,7 +8183,7 @@ zone_refreshkeys(dns_zone_t *zone) { if (!ISC_LIST_EMPTY(diff.tuples)) { CHECK(update_soa_serial(db, ver, &diff, zone->mctx, zone->updatemethod)); - CHECK(zone_journal(zone, &diff, "sync_keyzone")); + CHECK(zone_journal(zone, &diff, NULL, "sync_keyzone")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -8074,11 +8211,17 @@ zone_maintenance(dns_zone_t *zone) { ENTER; /* + * Are we pending load/reload? + */ + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) + return; + + /* * Configuring the view of this zone may have * failed, for example because the config file * had a syntax error. In that case, the view - * adb or resolver, and we had better not try - * to do maintenance on it. + * adb or resolver will be NULL, and we had better not try + * to do further maintenance on it. */ if (zone->view == NULL || zone->view->adb == NULL) return; @@ -8199,6 +8342,7 @@ zone_maintenance(dns_zone_t *zone) { set_key_expiry_warning(zone, zone->key_expiry, isc_time_seconds(&now)); break; + default: break; } @@ -8396,6 +8540,21 @@ dump_done(void *arg, isc_result_t result) { tresult = dns_db_getsoaserial(db, version, &serial); /* + * If there is a secure version of this zone + * use its serial if it is less than ours. + */ + if (tresult == ISC_R_SUCCESS && + zone->secure != NULL && zone->secure->db != NULL) { + isc_uint32_t sserial; + isc_result_t mresult; + + mresult = dns_db_getsoaserial(zone->secure->db, + NULL, &sserial); + if (mresult == ISC_R_SUCCESS && + isc_serial_lt(sserial, serial)) + serial = sserial; + } + /* * Note: we are task locked here so we can test * zone->xfr safely. */ @@ -8616,6 +8775,24 @@ notify_cancel(dns_zone_t *zone) { } static void +forward_cancel(dns_zone_t *zone) { + dns_forward_t *forward; + + /* + * 'zone' locked by caller. + */ + + REQUIRE(LOCKED_ZONE(zone)); + + for (forward = ISC_LIST_HEAD(zone->forwards); + forward != NULL; + forward = ISC_LIST_NEXT(forward, link)) { + if (forward->request != NULL) + dns_request_cancel(forward->request); + } +} + +static void zone_unload(dns_zone_t *zone) { /* @@ -9612,7 +9789,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_soa_t soa; isc_result_t result; - isc_uint32_t serial, oldserial; + isc_uint32_t serial, oldserial = 0; unsigned int j; zone = revent->ev_arg; @@ -10552,11 +10729,13 @@ static void zone_shutdown(isc_task_t *task, isc_event_t *event) { dns_zone_t *zone = (dns_zone_t *) event->ev_arg; isc_boolean_t free_needed, linked = ISC_FALSE; + dns_zone_t *raw = NULL, *secure = NULL; UNUSED(task); REQUIRE(DNS_ZONE_VALID(zone)); INSIST(event->ev_type == DNS_EVENT_ZONECONTROL); INSIST(isc_refcount_current(&zone->erefs) == 0); + zone_debuglog(zone, "zone_shutdown", 3, "shutting down"); /* @@ -10615,6 +10794,8 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { notify_cancel(zone); + forward_cancel(zone); + if (zone->timer != NULL) { isc_timer_detach(&zone->timer); INSIST(zone->irefs > 0); @@ -10631,7 +10812,19 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { */ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SHUTDOWN); free_needed = exit_check(zone); + if (zone->raw != NULL) { + raw = zone->raw; + zone->raw = NULL; + } + if (zone->secure != NULL) { + secure = zone->secure; + zone->secure = NULL; + } UNLOCK_ZONE(zone); + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); if (free_needed) zone_free(zone); } @@ -10991,9 +11184,17 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, isc_sockaddr_format(from, fromtext, sizeof(fromtext)); /* - * We only handle NOTIFY (SOA) at the present. + * Notify messages are processed by the raw zone. */ LOCK_ZONE(zone); + if (zone->raw != NULL) { + result = dns_zone_notifyreceive(zone->raw, from, msg); + UNLOCK_ZONE(zone); + return (result); + } + /* + * We only handle NOTIFY (SOA) at the present. + */ if (isc_sockaddr_pf(from) == PF_INET) inc_stats(zone, dns_zonestatscounter_notifyinv4); else @@ -11394,6 +11595,10 @@ zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) { isc_buffer_putstr(&buffer, "/"); isc_buffer_putstr(&buffer, zone->view->name); } + if (zone->raw != NULL && 9U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (signed)"); + if (zone->secure != NULL && 11U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (unsigned)"); buf[isc_buffer_usedlength(&buffer)] = '\0'; } @@ -11711,6 +11916,333 @@ notify_done(isc_task_t *task, isc_event_t *event) { dns_message_destroy(&message); } +struct secure_serial { + isc_event_t e; + isc_uint32_t serial; +}; + +static void +update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { + UNUSED(arg); + dns_zone_log(zone, level, "%s", message); +} + +static void +receive_secure_serial(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_journal_t *rjournal = NULL, *sjournal = NULL; + isc_uint32_t start, end; + dns_zone_t *zone; + int n_soa = 0; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_dbversion_t *newver = NULL, *oldver = NULL; + isc_uint32_t oldserial, newserial; + dns_diffop_t op = DNS_DIFFOP_ADD; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL, *soatuple = NULL; + dns_update_log_t log = { update_log_cb, NULL }; + isc_time_t timenow; + + zone = event->ev_arg; + end = ((struct secure_serial *)event)->serial; + + dns_diff_init(zone->mctx, &diff); + + UNUSED(task); + CHECK(dns_journal_open(zone->raw->mctx, zone->raw->journal, + DNS_JOURNAL_WRITE, &rjournal)); + result = dns_journal_open(zone->raw->mctx, zone->journal, + DNS_JOURNAL_READ, &sjournal); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto failure; + + start = dns_journal_get_bitws(rjournal); + if (sjournal != NULL) { + isc_uint32_t serial = dns_journal_get_bitws(sjournal); + /* + * We write the secure journal first so if that exists + * use its value provided it is greater that from the + * raw journal. + */ + if (isc_serial_gt(serial, start)) + start = serial; + dns_journal_destroy(&sjournal); + } + + if (start == end) + goto failure; + CHECK(dns_journal_iter_init(rjournal, start, end)); + + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + CHECK(dns_db_newversion(db, &newver)); + + for (result = dns_journal_first_rr(rjournal); + result == ISC_R_SUCCESS; + result = dns_journal_next_rr(rjournal)) { + dns_name_t *name = NULL; + isc_uint32_t ttl; + dns_rdata_t *rdata = NULL; + dns_journal_current_rr(rjournal, &name, &ttl, &rdata); + + if (rdata->type == dns_rdatatype_soa) { + n_soa++; + if (n_soa == 2) { + /* + * Save the lastest raw SOA record. + */ + if (soatuple != NULL) + dns_difftuple_free(&soatuple); + CHECK(dns_difftuple_create(diff.mctx, + DNS_DIFFOP_ADD, + name, ttl, rdata, + &soatuple)); + } + if (n_soa == 3) + n_soa = 1; + continue; + } + + /* Sanity. */ + if (n_soa == 0) { + dns_zone_log(zone->raw, ISC_LOG_ERROR, + "corrupt journal file: '%s'\n", + zone->raw->journal); + goto failure; + } + + if (zone->privatetype != 0 && + rdata->type == zone->privatetype) + continue; + + if (rdata->type == dns_rdatatype_nsec || + rdata->type == dns_rdatatype_rrsig || + rdata->type == dns_rdatatype_nsec3 || + rdata->type == dns_rdatatype_dnskey || + rdata->type == dns_rdatatype_nsec3param) + continue; + + op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; + + CHECK(dns_difftuple_create(diff.mctx, op, name, ttl, rdata, + &tuple)); + dns_diff_appendminimal(&diff, &tuple); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + CHECK(result); + + CHECK(dns_diff_apply(&diff, db, newver)); + + if (soatuple != NULL) { + isc_uint32_t desired; + + CHECK(dns_db_createsoatuple(db, oldver, diff.mctx, + DNS_DIFFOP_DEL, &tuple)); + oldserial = dns_soa_getserial(&tuple->rdata); + newserial = desired = dns_soa_getserial(&soatuple->rdata); + if (!isc_serial_gt(newserial, oldserial)) { + newserial = oldserial + 1; + if (newserial == 0) + newserial++; + dns_soa_setserial(newserial, &soatuple->rdata); + } + CHECK(do_one_tuple(&tuple, db, newver, &diff)); + CHECK(do_one_tuple(&soatuple, db, newver, &diff)); + dns_zone_log(zone, ISC_LOG_INFO, "serial %u (unsigned %u)", + newserial, desired); + } else + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + + CHECK(dns_update_signatures(&log, zone, db, oldver, newver, + &diff, zone->sigvalidityinterval)); + + CHECK(zone_journal(zone, &diff, &end, "receive_secure_serial")); + + dns_journal_set_bitws(rjournal, end); + dns_journal_commit(rjournal); + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + + zone_needdump(zone, DNS_DUMP_DELAY); + + TIME_NOW(&timenow); + zone_settimer(zone, &timenow); + + UNLOCK_ZONE(zone); + + dns_db_closeversion(db, &oldver, ISC_FALSE); + dns_db_closeversion(db, &newver, ISC_TRUE); + + failure: + if (tuple != NULL) + dns_difftuple_free(&tuple); + if (soatuple != NULL) + dns_difftuple_free(&soatuple); + if (db != NULL) { + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, ISC_FALSE); + if (node != NULL) + dns_db_detachnode(db, &node); + dns_db_detach(&db); + } + if (rjournal != NULL) + dns_journal_destroy(&rjournal); + if (sjournal != NULL) + dns_journal_destroy(&sjournal); + dns_diff_clear(&diff); + isc_event_free(&event); +} + +static isc_result_t +zone_send_secureserial(dns_zone_t *zone, isc_uint32_t serial) { + isc_event_t *e; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECURESERIAL, + receive_secure_serial, zone->secure, + sizeof(struct secure_serial)); + if (e == NULL) + return (ISC_R_NOMEMORY); + ((struct secure_serial *)e)->serial = serial; + + isc_task_send(zone->secure->task, &e); + return (ISC_R_SUCCESS); +} + +struct secure_db { + isc_event_t e; + dns_db_t *db; +}; + +static void +receive_secure_db(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_zone_t *zone; + dns_db_t *rawdb, *db = NULL; + dns_dbnode_t *rawnode = NULL, *node = NULL; + dns_fixedname_t fname; + dns_name_t *name; + dns_dbiterator_t *dbiterator = NULL; + dns_rdatasetiter_t *rdsit = NULL; + dns_rdataset_t rdataset; + dns_dbversion_t *version = NULL; + isc_time_t loadtime; + + UNUSED(task); + + zone = event->ev_arg; + rawdb = ((struct secure_db *)event)->db; + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_rdataset_init(&rdataset); + + TIME_NOW(&loadtime); + + result = dns_db_create(zone->mctx, zone->db_argv[0], + &zone->origin, dns_dbtype_zone, zone->rdclass, + zone->db_argc - 1, zone->db_argv + 1, &db); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_newversion(db, &version); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_createiterator(rawdb, 0, &dbiterator); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_dbiterator_first(dbiterator); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiterator)) { + result = dns_dbiterator_current(dbiterator, &rawnode, name); + if (result != ISC_R_SUCCESS) + continue; + + result = dns_db_findnode(db, name, ISC_TRUE, &node); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_allrdatasets(rawdb, rawnode, NULL, 0, &rdsit); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdatasetiter_first(rdsit); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsit)) { + dns_rdatasetiter_current(rdsit, &rdataset); + if (rdataset.type == dns_rdatatype_nsec || + rdataset.type == dns_rdatatype_rrsig || + rdataset.type == dns_rdatatype_nsec3 || + rdataset.type == dns_rdatatype_dnskey || + rdataset.type == dns_rdatatype_nsec3param) { + dns_rdataset_disassociate(&rdataset); + continue; + } + + result = dns_db_addrdataset(db, node, version, 0, + &rdataset, 0, NULL); + if (result != ISC_R_SUCCESS) + goto failure; + + dns_rdataset_disassociate(&rdataset); + } + dns_rdatasetiter_destroy(&rdsit); + dns_db_detachnode(rawdb, &rawnode); + dns_db_detachnode(db, &node); + } + + dns_db_closeversion(db, &version, ISC_TRUE); + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS); + zone_needdump(zone, 0); /* XXXMPA */ + UNLOCK_ZONE(zone); + + failure: + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_db: %s", + dns_result_totext(result)); + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (db != NULL) { + if (node != NULL) + dns_db_detachnode(db, &node); + dns_db_detach(&db); + } + if (rawnode != NULL) + dns_db_detachnode(rawdb, &rawnode); + dns_db_detach(&rawdb); + if (dbiterator != NULL) + dns_dbiterator_destroy(&dbiterator); + isc_event_free(&event); +} + +static isc_result_t +zone_send_securedb(dns_zone_t *zone, dns_db_t *db) { + isc_event_t *e; + dns_db_t *dummy = NULL; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECUREDB, + receive_secure_db, zone->secure, + sizeof(struct secure_db)); + if (e == NULL) + return (ISC_R_NOMEMORY); + dns_db_attach(db, &dummy); + ((struct secure_db *)e)->db = dummy; + + isc_task_send(zone->secure->task, &e); + return (ISC_R_SUCCESS); +} + isc_result_t dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { isc_result_t result; @@ -11831,6 +12363,10 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { break; } } +#if 0 + if (zone->secure != NULL) + zone_send_secureserial(zone, serial); +#endif } else { if (dump && zone->masterfile != NULL) { /* @@ -11881,6 +12417,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { zone->journal, strbuf); } } + if (zone->secure != NULL) + zone_send_securedb(zone, db); } dns_db_closeversion(db, &ver, ISC_FALSE); @@ -12032,6 +12570,8 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { dns_zone_log(zone, ISC_LOG_INFO, "transferred serial %u%s", serial, buf); + if (zone->secure != NULL) + zone_send_secureserial(zone, serial); } /* @@ -12289,7 +12829,7 @@ queue_xfrin(dns_zone_t *zone) { */ static void got_transfer_quota(isc_task_t *task, isc_event_t *event) { - isc_result_t result; + isc_result_t result = ISC_R_SUCCESS; dns_peer_t *peer = NULL; char master[ISC_SOCKADDR_FORMATSIZE]; char source[ISC_SOCKADDR_FORMATSIZE]; @@ -12337,14 +12877,6 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { "no database exists yet, requesting AXFR of " "initial version from %s", master); xfrtype = dns_rdatatype_axfr; - } else if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) { - dns_zone_log(zone, ISC_LOG_DEBUG(1), "ixfr-from-differences " - "set, requesting %sAXFR from %s", soa_before, - master); - if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR)) - xfrtype = dns_rdatatype_soa; - else - xfrtype = dns_rdatatype_axfr; } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "forced reload, requesting AXFR of " @@ -12360,13 +12892,10 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { UNLOCK_ZONE(zone); } else { isc_boolean_t use_ixfr = ISC_TRUE; - if (peer != NULL && - dns_peer_getrequestixfr(peer, &use_ixfr) == - ISC_R_SUCCESS) { - ; /* Using peer setting */ - } else { - use_ixfr = zone->view->requestixfr; - } + if (peer != NULL) + result = dns_peer_getrequestixfr(peer, &use_ixfr); + if (peer == NULL || result != ISC_R_SUCCESS) + use_ixfr = zone->requestixfr; if (use_ixfr == ISC_FALSE) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "IXFR disabled, requesting %sAXFR from %s", @@ -12454,8 +12983,13 @@ forward_destroy(dns_forward_t *forward) { dns_request_destroy(&forward->request); if (forward->msgbuf != NULL) isc_buffer_free(&forward->msgbuf); - if (forward->zone != NULL) + if (forward->zone != NULL) { + LOCK(&forward->zone->lock); + if (ISC_LINK_LINKED(forward, link)) + ISC_LIST_UNLINK(forward->zone->forwards, forward, link); + UNLOCK(&forward->zone->lock); dns_zone_idetach(&forward->zone); + } isc_mem_putanddetach(&forward->mctx, forward, sizeof(*forward)); } @@ -12465,6 +12999,12 @@ sendtomaster(dns_forward_t *forward) { isc_sockaddr_t src; LOCK_ZONE(forward->zone); + + if (DNS_ZONE_FLAG(forward->zone, DNS_ZONEFLG_EXITING)) { + UNLOCK_ZONE(forward->zone); + return (ISC_R_CANCELED); + } + if (forward->which >= forward->zone->masterscnt) { UNLOCK_ZONE(forward->zone); return (ISC_R_NOMORE); @@ -12495,6 +13035,11 @@ sendtomaster(dns_forward_t *forward) { forward->zone->task, forward_callback, forward, &forward->request); + if (result == ISC_R_SUCCESS) { + if (!ISC_LINK_LINKED(forward, link)) + ISC_LIST_APPEND(forward->zone->forwards, forward, link); + } + unlock: UNLOCK_ZONE(forward->zone); return (result); @@ -12621,6 +13166,7 @@ dns_zone_forwardupdate(dns_zone_t *zone, dns_message_t *msg, forward->mctx = 0; forward->callback = callback; forward->callback_arg = callback_arg; + ISC_LINK_INIT(forward, link); forward->magic = FORWARD_MAGIC; mr = dns_message_getrawmessage(msg); @@ -12694,6 +13240,7 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, zmgr->timermgr = timermgr; zmgr->socketmgr = socketmgr; zmgr->zonetasks = NULL; + zmgr->loadtasks = NULL; zmgr->task = NULL; zmgr->rl = NULL; ISC_LIST_INIT(zmgr->zones); @@ -12769,6 +13316,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { REQUIRE(zone->zmgr == NULL); isc_taskpool_gettask(zmgr->zonetasks, &zone->task); + isc_taskpool_gettask(zmgr->loadtasks, &zone->loadtask); /* * Set the task name. The tag will arbitrarily point to one @@ -12776,6 +13324,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { * to be managed last). */ isc_task_setname(zone->task, "zone", zone); + isc_task_setname(zone->loadtask, "loadzone", zone); result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive, NULL, NULL, @@ -12783,7 +13332,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { &zone->timer); if (result != ISC_R_SUCCESS) - goto cleanup_task; + goto cleanup_tasks; /* * The timer "holds" a iref. @@ -12797,7 +13346,8 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { goto unlock; - cleanup_task: + cleanup_tasks: + isc_task_detach(&zone->loadtask); isc_task_detach(&zone->task); unlock: @@ -12903,6 +13453,8 @@ dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr) { void dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { + dns_zone_t *zone; + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); isc_ratelimiter_shutdown(zmgr->rl); @@ -12911,6 +13463,20 @@ dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { isc_task_destroy(&zmgr->task); if (zmgr->zonetasks != NULL) isc_taskpool_destroy(&zmgr->zonetasks); + if (zmgr->loadtasks != NULL) + isc_taskpool_destroy(&zmgr->loadtasks); + + RWLOCK(&zmgr->rwlock, isc_rwlocktype_read); + for (zone = ISC_LIST_HEAD(zmgr->zones); + zone != NULL; + zone = ISC_LIST_NEXT(zone, link)) + { + LOCK_ZONE(zone); + forward_cancel(zone); + UNLOCK_ZONE(zone); + } + RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read); + } isc_result_t @@ -12923,13 +13489,13 @@ dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones) { /* * For anything fewer than 1000 zones we use 10 tasks in - * the task pool. More than that, and we'll scale at one + * the task pools. More than that, and we'll scale at one * task per 100 zones. */ if (ntasks < 10) ntasks = 10; - /* Create or resize the zone task pool. */ + /* Create or resize the zone task pools. */ if (zmgr->zonetasks == NULL) result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx, ntasks, 2, &pool); @@ -12939,6 +13505,33 @@ dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones) { if (result == ISC_R_SUCCESS) zmgr->zonetasks = pool; + pool = NULL; + if (zmgr->loadtasks == NULL) + result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx, + ntasks, 2, &pool); + else + result = isc_taskpool_expand(&zmgr->loadtasks, ntasks, &pool); + + if (result == ISC_R_SUCCESS) + zmgr->loadtasks = pool; + +#ifdef BIND9 + /* + * We always set all tasks in the zone-load task pool to + * privileged. This prevents other tasks in the system from + * running while the server task manager is in privileged + * mode. + * + * NOTE: If we start using task privileges for any other + * part of the system than zone tasks, then this will need to be + * revisted. In that case we'd want to turn on privileges for + * zone tasks only when we were loading, and turn them off the + * rest of the time. For now, however, it's okay to just + * set it and forget it. + */ + isc_taskpool_setprivilege(zmgr->loadtasks, ISC_TRUE); +#endif + return (result); } @@ -13161,12 +13754,14 @@ zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high, io = isc_mem_get(zmgr->mctx, sizeof(*io)); if (io == NULL) return (ISC_R_NOMEMORY); + io->event = isc_event_allocate(zmgr->mctx, task, DNS_EVENT_IOREADY, action, arg, sizeof(*io->event)); if (io->event == NULL) { isc_mem_put(zmgr->mctx, io, sizeof(*io)); return (ISC_R_NOMEMORY); } + io->zmgr = zmgr; io->high = high; io->task = NULL; @@ -13186,9 +13781,8 @@ zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high, UNLOCK(&zmgr->iolock); *iop = io; - if (!queue) { + if (!queue) isc_task_send(io->task, &io->event); - } return (ISC_R_SUCCESS); } @@ -14364,7 +14958,8 @@ zone_rekey(dns_zone_t *zone) { zone->updatemethod)); CHECK(add_chains(zone, db, ver, &diff)); CHECK(sign_apex(zone, db, ver, &diff, &sig_diff)); - CHECK(zone_journal(zone, &sig_diff, "zone_rekey")); + CHECK(zone_journal(zone, &sig_diff, NULL, + "zone_rekey")); commit = ISC_TRUE; } } @@ -14654,6 +15249,18 @@ dns_zone_setrefreshkeyinterval(dns_zone_t *zone, isc_uint32_t interval) { } void +dns_zone_setrequestixfr(dns_zone_t *zone, isc_boolean_t bool) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->requestixfr = bool; +} + +isc_boolean_t +dns_zone_getrequestixfr(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->requestixfr); +} + +void dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method) { REQUIRE(DNS_ZONE_VALID(zone)); zone->updatemethod = method; @@ -14664,3 +15271,32 @@ dns_zone_getserialupdatemethod(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); return(zone->updatemethod); } + +void +dns_zone_link(dns_zone_t *zone, dns_zone_t *raw) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(DNS_ZONE_VALID(raw)); + + LOCK(&zone->lock); + if (zone->raw != NULL) + dns_zone_detach(&zone->raw); + dns_zone_attach(raw, &zone->raw); + UNLOCK(&zone->lock); + + LOCK(&raw->lock); + if (raw->secure != NULL) + dns_zone_idetach(&raw->secure); + dns_zone_iattach(zone, &raw->secure); + UNLOCK(&raw->lock); +} + +void +dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(raw != NULL && *raw == NULL); + + LOCK(&zone->lock); + if (zone->raw != NULL) + dns_zone_attach(zone->raw, raw); + UNLOCK(&zone->lock); +} diff --git a/lib/dns/zt.c b/lib/dns/zt.c index 49f6c487..0995377a 100644 --- a/lib/dns/zt.c +++ b/lib/dns/zt.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zt.c,v 1.50 2011-03-21 23:47:21 tbox Exp $ */ +/* $Id: zt.c,v 1.56 2011-09-07 00:50:06 marka Exp $ */ /*! \file */ @@ -25,6 +25,7 @@ #include <isc/magic.h> #include <isc/mem.h> #include <isc/string.h> +#include <isc/task.h> #include <isc/util.h> #include <dns/log.h> @@ -42,8 +43,11 @@ struct dns_zt { isc_mem_t *mctx; dns_rdataclass_t rdclass; isc_rwlock_t rwlock; + dns_zt_allloaded_t loaddone; + void * loaddone_arg; /* Locked by lock. */ isc_uint32_t references; + unsigned int loads_pending; dns_rbt_t *table; }; @@ -57,11 +61,17 @@ static isc_result_t load(dns_zone_t *zone, void *uap); static isc_result_t +asyncload(dns_zone_t *zone, void *callback); + +static isc_result_t loadnew(dns_zone_t *zone, void *uap); static isc_result_t freezezones(dns_zone_t *zone, void *uap); +static isc_result_t +doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task); + isc_result_t dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) { @@ -87,6 +97,9 @@ dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) zt->references = 1; zt->rdclass = rdclass; zt->magic = ZTMAGIC; + zt->loaddone = NULL; + zt->loaddone_arg = NULL; + zt->loads_pending = 0; *ztp = zt; return (ISC_R_SUCCESS); @@ -243,13 +256,63 @@ static isc_result_t load(dns_zone_t *zone, void *uap) { isc_result_t result; UNUSED(uap); + result = dns_zone_load(zone); if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE) result = ISC_R_SUCCESS; + return (result); } isc_result_t +dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg) { + isc_result_t result, tresult; + static dns_zt_zoneloaded_t dl = doneloading; + int pending; + + REQUIRE(VALID_ZT(zt)); + + RWLOCK(&zt->rwlock, isc_rwlocktype_read); + + INSIST(zt->loads_pending == 0); + + result = dns_zt_apply2(zt, ISC_FALSE, &tresult, asyncload, &dl); + + pending = zt->loads_pending; + if (pending != 0) { + zt->loaddone = alldone; + zt->loaddone_arg = arg; + } + + RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); + + if (pending == 0) + alldone(arg); + + return (result); +} + +/* + * Initiates asynchronous loading of zone 'zone'. 'callback' is a + * pointer to a function which will be used to inform the caller when + * the zone loading is complete. + */ +static isc_result_t +asyncload(dns_zone_t *zone, void *callback) { + isc_result_t result; + dns_zt_zoneloaded_t *loaded = callback; + dns_zt_t *zt; + + REQUIRE(zone != NULL); + zt = dns_zone_getview(zone)->zonetable; + + result = dns_zone_asyncload(zone, *loaded, zt); + if (result == ISC_R_SUCCESS) + zt->loads_pending++; + return (ISC_R_SUCCESS); +} + +isc_result_t dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop) { isc_result_t result; @@ -265,6 +328,7 @@ static isc_result_t loadnew(dns_zone_t *zone, void *uap) { isc_result_t result; UNUSED(uap); + result = dns_zone_loadnew(zone); if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE || result == DNS_R_DYNAMIC) @@ -281,6 +345,8 @@ dns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze) { RWLOCK(&zt->rwlock, isc_rwlocktype_read); result = dns_zt_apply2(zt, ISC_FALSE, &tresult, freezezones, &freeze); RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); + if (tresult == ISC_R_NOTFOUND) + tresult = ISC_R_SUCCESS; return ((result == ISC_R_SUCCESS) ? tresult : result); } @@ -364,6 +430,7 @@ dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub, /* * The tree is empty. */ + tresult = result; result = ISC_R_NOMORE; } while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { @@ -393,6 +460,38 @@ dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub, return (result); } +/* + * Decrement the loads_pending counter; when counter reaches + * zero, call the loaddone callback that was initially set by + * dns_zt_asyncload(). + */ +static isc_result_t +doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { + dns_zt_allloaded_t alldone = NULL; + void *arg = NULL; + + UNUSED(zone); + UNUSED(task); + + REQUIRE(VALID_ZT(zt)); + + RWLOCK(&zt->rwlock, isc_rwlocktype_write); + INSIST(zt->loads_pending != 0); + zt->loads_pending--; + if (zt->loads_pending == 0) { + alldone = zt->loaddone; + arg = zt->loaddone_arg; + zt->loaddone = NULL; + zt->loaddone_arg = NULL; + } + RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); + + if (alldone != NULL) + alldone(arg); + + return (ISC_R_SUCCESS); +} + /*** *** Private ***/ diff --git a/lib/isc/include/isc/list.h b/lib/isc/include/isc/list.h index 69ba1c63..64dcc599 100644 --- a/lib/isc/include/isc/list.h +++ b/lib/isc/include/isc/list.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2006, 2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: list.h,v 1.24 2007-06-19 23:47:18 tbox Exp $ */ +/* $Id: list.h,v 1.26 2011-09-02 23:46:33 tbox Exp $ */ #ifndef ISC_LIST_H #define ISC_LIST_H 1 @@ -169,6 +169,19 @@ (list2).tail = NULL; \ } while (0) +#define ISC_LIST_PREPENDLIST(list1, list2, link) \ + do { \ + if (ISC_LIST_EMPTY(list1)) \ + (list1) = (list2); \ + else if (!ISC_LIST_EMPTY(list2)) { \ + (list2).tail->link.next = (list1).head; \ + (list1).head->link.prev = (list2).tail; \ + (list1).head = (list2).head; \ + } \ + (list2).head = NULL; \ + (list2).tail = NULL; \ + } while (0) + #define ISC_LIST_ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link) #define __ISC_LIST_ENQUEUEUNSAFE(list, elt, link) \ __ISC_LIST_APPENDUNSAFE(list, elt, link) diff --git a/lib/isc/include/isc/namespace.h b/lib/isc/include/isc/namespace.h index 11f25285..6822443b 100644 --- a/lib/isc/include/isc/namespace.h +++ b/lib/isc/include/isc/namespace.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namespace.h,v 1.12 2011-08-23 17:02:53 each Exp $ */ +/* $Id: namespace.h,v 1.13 2011-09-02 21:15:38 each Exp $ */ #ifndef ISCAPI_NAMESPACE_H #define ISCAPI_NAMESPACE_H 1 @@ -31,6 +31,7 @@ #define isc_app_run isc__app_run #define isc_app_ctxrun isc__app_ctxrun #define isc_app_shutdown isc__app_shutdown +#define isc_app_ctxfinish isc__app_ctxfinish #define isc_app_ctxshutdown isc__app_ctxshutdown #define isc_app_ctxsuspend isc__app_ctxsuspend #define isc_app_reload isc__app_reload @@ -147,9 +148,13 @@ #define isc_task_gettag isc__task_gettag #define isc_task_getcurrenttime isc__task_getcurrenttime #define isc_taskmgr_create isc__taskmgr_create +#define isc_taskmgr_setmode isc__taskmgr_setmode +#define isc_taskmgr_mode isc__taskmgr_mode #define isc_taskmgr_destroy isc__taskmgr_destroy #define isc_task_beginexclusive isc__task_beginexclusive #define isc_task_endexclusive isc__task_endexclusive +#define isc_task_setprivilege isc__task_setprivilege +#define isc_task_privilege isc__task_privilege #define isc_timer_create isc__timer_create #define isc_timer_reset isc__timer_reset diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h index 64e531c0..c5ac5bf3 100644 --- a/lib/isc/include/isc/task.h +++ b/lib/isc/include/isc/task.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: task.h,v 1.71 2011-02-03 12:18:12 tbox Exp $ */ +/* $Id: task.h,v 1.73 2011-09-02 23:46:33 tbox Exp $ */ #ifndef ISC_TASK_H #define ISC_TASK_H 1 @@ -88,6 +88,7 @@ #define ISC_TASKEVENT_FIRSTEVENT (ISC_EVENTCLASS_TASK + 0) #define ISC_TASKEVENT_SHUTDOWN (ISC_EVENTCLASS_TASK + 1) +#define ISC_TASKEVENT_TEST (ISC_EVENTCLASS_TASK + 1) #define ISC_TASKEVENT_LASTEVENT (ISC_EVENTCLASS_TASK + 65535) /***** @@ -100,9 +101,17 @@ ISC_LANG_BEGINDECLS *** Types ***/ +typedef enum { + isc_taskmgrmode_normal = 0, + isc_taskmgrmode_privileged +} isc_taskmgrmode_t; + /*% Task and task manager methods */ typedef struct isc_taskmgrmethods { void (*destroy)(isc_taskmgr_t **managerp); + void (*setmode)(isc_taskmgr_t *manager, + isc_taskmgrmode_t mode); + isc_taskmgrmode_t (*mode)(isc_taskmgr_t *manager); isc_result_t (*taskcreate)(isc_taskmgr_t *manager, unsigned int quantum, isc_task_t **taskp); @@ -127,6 +136,8 @@ typedef struct isc_taskmethods { void *tag); isc_result_t (*beginexclusive)(isc_task_t *task); void (*endexclusive)(isc_task_t *task); + void (*setprivilege)(isc_task_t *task, isc_boolean_t priv); + isc_boolean_t (*privilege)(isc_task_t *task); } isc_taskmethods_t; /*% @@ -611,6 +622,32 @@ isc_task_exiting(isc_task_t *t); *\li 'task' is a valid task. */ +void +isc_task_setprivilege(isc_task_t *task, isc_boolean_t priv); +/*%< + * Set or unset the task's "privileged" flag depending on the value of + * 'priv'. + * + * Under normal circumstances this flag has no effect on the task behavior, + * but when the task manager has been set to privileged exeuction mode via + * isc_taskmgr_setmode(), only tasks with the flag set will be executed, + * and all other tasks will wait until they're done. Once all privileged + * tasks have finished executing, the task manager will automatically + * return to normal execution mode and nonprivileged task can resume. + * + * Requires: + *\li 'task' is a valid task. + */ + +isc_boolean_t +isc_task_privilege(isc_task_t *task); +/*%< + * Returns the current value of the task's privilege flag. + * + * Requires: + *\li 'task' is a valid task. + */ + /***** ***** Task Manager. *****/ @@ -664,6 +701,31 @@ isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, */ void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode); + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager); +/*%< + * Set/get the current operating mode of the task manager. Valid modes are: + * + *\li isc_taskmgrmode_normal + *\li isc_taskmgrmode_privileged + * + * In privileged execution mode, only tasks that have had the "privilege" + * flag set via isc_task_setprivilege() can be executed. When all such + * tasks are complete, the manager automatically returns to normal mode + * and proceeds with running non-privileged ready tasks. This means it is + * necessary to have at least one privileged task waiting on the ready + * queue *before* setting the manager into privileged execution mode, + * which in turn means the task which calls this function should be in + * task-exclusive mode when it does so. + * + * Requires: + * + *\li 'manager' is a valid task manager. + */ + +void isc_taskmgr_destroy(isc_taskmgr_t **managerp); /*%< * Destroy '*managerp'. diff --git a/lib/isc/include/isc/taskpool.h b/lib/isc/include/isc/taskpool.h index ec3ebbcb..fcdddc53 100644 --- a/lib/isc/include/isc/taskpool.h +++ b/lib/isc/include/isc/taskpool.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: taskpool.h,v 1.17 2011-07-07 23:47:50 tbox Exp $ */ +/* $Id: taskpool.h,v 1.18 2011-09-02 21:15:38 each Exp $ */ #ifndef ISC_TASKPOOL_H #define ISC_TASKPOOL_H 1 @@ -139,6 +139,19 @@ isc_taskpool_destroy(isc_taskpool_t **poolp); * \li '*poolp' is a valid task pool. */ +void +isc_taskpool_setprivilege(isc_taskpool_t *pool, isc_boolean_t priv); +/*%< + * Set the privilege flag on all tasks in 'pool' to 'priv'. If 'priv' is + * true, then when the task manager is set into privileged mode, only + * tasks wihin this pool will be able to execute. (Note: It is important + * to turn the pool tasks' privilege back off before the last task finishes + * executing.) + * + * Requires: + * \li 'pool' is a valid task pool. + */ + ISC_LANG_ENDDECLS #endif /* ISC_TASKPOOL_H */ diff --git a/lib/isc/include/isc/util.h b/lib/isc/include/isc/util.h index 8bb334ed..e7ef3dfa 100644 --- a/lib/isc/include/isc/util.h +++ b/lib/isc/include/isc/util.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: util.h,v 1.34 2011-03-12 04:59:49 tbox Exp $ */ +/* $Id: util.h,v 1.35 2011-09-05 18:00:22 each Exp $ */ #ifndef ISC_UTIL_H #define ISC_UTIL_H 1 @@ -235,14 +235,4 @@ */ #define TIME_NOW(tp) RUNTIME_CHECK(isc_time_now((tp)) == ISC_R_SUCCESS) -/*% - * Prevent Linux spurious warnings - */ -#if defined(__GNUC__) && (__GNUC__ > 3) -#define isc_util_fwrite(a, b, c, d) \ - __builtin_expect(fwrite((a), (b), (c), (d)), (c)) -#else -#define isc_util_fwrite(a, b, c, d) fwrite((a), (b), (c), (d)) -#endif - #endif /* ISC_UTIL_H */ diff --git a/lib/isc/task.c b/lib/isc/task.c index ffe57afb..dedaf09d 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: task.c,v 1.117 2011-02-03 12:18:12 tbox Exp $ */ +/* $Id: task.c,v 1.122 2011-09-03 16:27:51 each Exp $ */ /*! \file * \author Principal Author: Bob Halley @@ -64,9 +64,7 @@ #endif /* ISC_PLATFORM_USETHREADS */ #endif /* BIND9 */ -#ifndef USE_WORKER_THREADS #include "task_p.h" -#endif /* USE_WORKER_THREADS */ #ifdef ISC_TASK_TRACE #define XTRACE(m) fprintf(stderr, "task %p thread %lu: %s\n", \ @@ -120,9 +118,11 @@ struct isc__task { /* Locked by task manager lock. */ LINK(isc__task_t) link; LINK(isc__task_t) ready_link; + LINK(isc__task_t) ready_priority_link; }; #define TASK_F_SHUTTINGDOWN 0x01 +#define TASK_F_PRIVILEGED 0x02 #define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \ != 0) @@ -145,11 +145,15 @@ struct isc__taskmgr { unsigned int default_quantum; LIST(isc__task_t) tasks; isc__tasklist_t ready_tasks; + isc__tasklist_t ready_priority_tasks; + isc_taskmgrmode_t mode; #ifdef ISC_PLATFORM_USETHREADS isc_condition_t work_available; isc_condition_t exclusive_granted; + isc_condition_t paused; #endif /* ISC_PLATFORM_USETHREADS */ unsigned int tasks_running; + isc_boolean_t pause_requested; isc_boolean_t exclusive_requested; isc_boolean_t exiting; #ifdef USE_SHARED_MANAGER @@ -225,6 +229,23 @@ ISC_TASKFUNC_SCOPE isc_result_t isc__task_beginexclusive(isc_task_t *task); ISC_TASKFUNC_SCOPE void isc__task_endexclusive(isc_task_t *task0); +ISC_TASKFUNC_SCOPE void +isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv); +ISC_TASKFUNC_SCOPE isc_boolean_t +isc__task_privilege(isc_task_t *task0); +ISC_TASKFUNC_SCOPE void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode); +ISC_TASKFUNC_SCOPE isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0); + +static inline isc_boolean_t +empty_readyq(isc__taskmgr_t *manager); + +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager); + +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task); static struct isc__taskmethods { isc_taskmethods_t methods; @@ -249,7 +270,9 @@ static struct isc__taskmethods { isc__task_purge, isc__task_purgerange, isc__task_beginexclusive, - isc__task_endexclusive + isc__task_endexclusive, + isc__task_setprivilege, + isc__task_privilege } #ifndef BIND9 , @@ -261,6 +284,8 @@ static struct isc__taskmethods { static isc_taskmgrmethods_t taskmgrmethods = { isc__taskmgr_destroy, + isc__taskmgr_setmode, + isc__taskmgr_mode, isc__task_create }; @@ -333,6 +358,7 @@ isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, task->tag = NULL; INIT_LINK(task, link); INIT_LINK(task, ready_link); + INIT_LINK(task, ready_priority_link); exiting = ISC_FALSE; LOCK(&manager->lock); @@ -400,6 +426,7 @@ task_shutdown(isc__task_t *task) { } INSIST(task->state == task_state_ready || task->state == task_state_running); + /* * Note that we post shutdown events LIFO. */ @@ -415,9 +442,17 @@ task_shutdown(isc__task_t *task) { return (was_idle); } +/* + * Moves a task onto the appropriate run queue. + * + * Caller must NOT hold manager lock. + */ static inline void task_ready(isc__task_t *task) { isc__taskmgr_t *manager = task->manager; +#ifdef USE_WORKER_THREADS + isc_boolean_t has_privilege = isc__task_privilege((isc_task_t *) task); +#endif /* USE_WORKER_THREADS */ REQUIRE(VALID_MANAGER(manager)); REQUIRE(task->state == task_state_ready); @@ -425,12 +460,11 @@ task_ready(isc__task_t *task) { XTRACE("task_ready"); LOCK(&manager->lock); - - ENQUEUE(manager->ready_tasks, task, ready_link); + push_readyq(manager, task); #ifdef USE_WORKER_THREADS - SIGNAL(&manager->work_available); + if (manager->mode == isc_taskmgrmode_normal || has_privilege) + SIGNAL(&manager->work_available); #endif /* USE_WORKER_THREADS */ - UNLOCK(&manager->lock); } @@ -868,21 +902,81 @@ isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) { REQUIRE(t != NULL); LOCK(&task->lock); - *t = task->now; - UNLOCK(&task->lock); } /*** *** Task Manager. ***/ + +/* + * Return ISC_TRUE if the current ready list for the manager, which is + * either ready_tasks or the ready_priority_tasks, depending on whether + * the manager is currently in normal or privileged execution mode. + * + * Caller must hold the task manager lock. + */ +static inline isc_boolean_t +empty_readyq(isc__taskmgr_t *manager) { + isc__tasklist_t queue; + + if (manager->mode == isc_taskmgrmode_normal) + queue = manager->ready_tasks; + else + queue = manager->ready_priority_tasks; + + return (ISC_TF(EMPTY(queue))); +} + +/* + * Dequeue and return a pointer to the first task on the current ready + * list for the manager. + * If the task is privileged, dequeue it from the other ready list + * as well. + * + * Caller must hold the task manager lock. + */ +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager) { + isc__task_t *task; + + if (manager->mode == isc_taskmgrmode_normal) + task = HEAD(manager->ready_tasks); + else + task = HEAD(manager->ready_priority_tasks); + + if (task != NULL) { + DEQUEUE(manager->ready_tasks, task, ready_link); + if (ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + } + + return (task); +} + +/* + * Push 'task' onto the ready_tasks queue. If 'task' has the privilege + * flag set, then also push it onto the ready_priority_tasks queue. + * + * Caller must hold the task manager lock. + */ +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task) { + ENQUEUE(manager->ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); +} + static void dispatch(isc__taskmgr_t *manager) { isc__task_t *task; #ifndef USE_WORKER_THREADS unsigned int total_dispatch_count = 0; - isc__tasklist_t ready_tasks; + isc__tasklist_t new_ready_tasks; + isc__tasklist_t new_priority_tasks; #endif /* USE_WORKER_THREADS */ REQUIRE(VALID_MANAGER(manager)); @@ -938,9 +1032,11 @@ dispatch(isc__taskmgr_t *manager) { */ #ifndef USE_WORKER_THREADS - ISC_LIST_INIT(ready_tasks); + ISC_LIST_INIT(new_ready_tasks); + ISC_LIST_INIT(new_priority_tasks); #endif LOCK(&manager->lock); + while (!FINISHED(manager)) { #ifdef USE_WORKER_THREADS /* @@ -949,10 +1045,12 @@ dispatch(isc__taskmgr_t *manager) { * the task while only holding the manager lock, and then * change the task to running state while only holding the * task lock. + * + * If a pause has been requested, don't do any work + * until it's been released. */ - while ((EMPTY(manager->ready_tasks) || - manager->exclusive_requested) && - !FINISHED(manager)) + while ((empty_readyq(manager) || manager->pause_requested || + manager->exclusive_requested) && !FINISHED(manager)) { XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, @@ -964,13 +1062,13 @@ dispatch(isc__taskmgr_t *manager) { } #else /* USE_WORKER_THREADS */ if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || - EMPTY(manager->ready_tasks)) + empty_readyq(manager)) break; #endif /* USE_WORKER_THREADS */ XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_WORKING, "working")); - task = HEAD(manager->ready_tasks); + task = pop_readyq(manager); if (task != NULL) { unsigned int dispatch_count = 0; isc_boolean_t done = ISC_FALSE; @@ -985,7 +1083,6 @@ dispatch(isc__taskmgr_t *manager) { * have a task to do. We must reacquire the manager * lock before exiting the 'if (task != NULL)' block. */ - DEQUEUE(manager->ready_tasks, task, ready_link); manager->tasks_running++; UNLOCK(&manager->lock); @@ -1106,6 +1203,9 @@ dispatch(isc__taskmgr_t *manager) { if (manager->exclusive_requested && manager->tasks_running == 1) { SIGNAL(&manager->exclusive_granted); + } else if (manager->pause_requested && + manager->tasks_running == 0) { + SIGNAL(&manager->paused); } #endif /* USE_WORKER_THREADS */ if (requeue) { @@ -1129,17 +1229,39 @@ dispatch(isc__taskmgr_t *manager) { * might even hurt rather than help. */ #ifdef USE_WORKER_THREADS - ENQUEUE(manager->ready_tasks, task, - ready_link); + push_readyq(manager, task); #else - ENQUEUE(ready_tasks, task, ready_link); + ENQUEUE(new_ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(new_priority_tasks, task, + ready_priority_link); #endif } } + +#ifdef USE_WORKER_THREADS + /* + * If we are in privileged execution mode and there are no + * tasks remaining on the current ready queue, then + * we're stuck. Automatically drop privileges at that + * point and continue with the regular ready queue. + */ + if (manager->tasks_running == 0 && empty_readyq(manager)) { + manager->mode = isc_taskmgrmode_normal; + if (!empty_readyq(manager)) + BROADCAST(&manager->work_available); + } +#endif } + #ifndef USE_WORKER_THREADS - ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link); + ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link); + ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks, + ready_priority_link); + if (empty_readyq(manager)) + manager->mode = isc_taskmgrmode_normal; #endif + UNLOCK(&manager->lock); } @@ -1174,6 +1296,7 @@ manager_free(isc__taskmgr_t *manager) { #ifdef USE_WORKER_THREADS (void)isc_condition_destroy(&manager->exclusive_granted); (void)isc_condition_destroy(&manager->work_available); + (void)isc_condition_destroy(&manager->paused); isc_mem_free(manager->mctx, manager->threads); #endif /* USE_WORKER_THREADS */ DESTROYLOCK(&manager->lock); @@ -1224,6 +1347,7 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, manager->common.methods = &taskmgrmethods; manager->common.impmagic = TASK_MANAGER_MAGIC; manager->common.magic = ISCAPI_TASKMGR_MAGIC; + manager->mode = isc_taskmgrmode_normal; manager->mctx = NULL; result = isc_mutex_init(&manager->lock); if (result != ISC_R_SUCCESS) @@ -1253,14 +1377,24 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, result = ISC_R_UNEXPECTED; goto cleanup_workavailable; } + if (isc_condition_init(&manager->paused) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + result = ISC_R_UNEXPECTED; + goto cleanup_exclusivegranted; + } #endif /* USE_WORKER_THREADS */ if (default_quantum == 0) default_quantum = DEFAULT_DEFAULT_QUANTUM; manager->default_quantum = default_quantum; INIT_LIST(manager->tasks); INIT_LIST(manager->ready_tasks); + INIT_LIST(manager->ready_priority_tasks); manager->tasks_running = 0; manager->exclusive_requested = ISC_FALSE; + manager->pause_requested = ISC_FALSE; manager->exiting = ISC_FALSE; isc_mem_attach(mctx, &manager->mctx); @@ -1296,6 +1430,8 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, return (ISC_R_SUCCESS); #ifdef USE_WORKER_THREADS + cleanup_exclusivegranted: + (void)isc_condition_destroy(&manager->exclusive_granted); cleanup_workavailable: (void)isc_condition_destroy(&manager->work_available); cleanup_threads: @@ -1361,6 +1497,11 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { manager->exiting = ISC_TRUE; /* + * If privileged mode was on, turn it off. + */ + manager->mode = isc_taskmgrmode_normal; + + /* * Post shutdown event(s) to every task (if they haven't already been * posted). */ @@ -1369,7 +1510,7 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { task = NEXT(task, link)) { LOCK(&task->lock); if (task_shutdown(task)) - ENQUEUE(manager->ready_tasks, task, ready_link); + push_readyq(manager, task); UNLOCK(&task->lock); } #ifdef USE_WORKER_THREADS @@ -1408,10 +1549,30 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { *managerp = NULL; } +ISC_TASKFUNC_SCOPE void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + manager->mode = mode; + UNLOCK(&manager->lock); +} + +ISC_TASKFUNC_SCOPE isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc_taskmgrmode_t mode; + LOCK(&manager->lock); + mode = manager->mode; + UNLOCK(&manager->lock); + return (mode); +} + #ifndef USE_WORKER_THREADS isc_boolean_t isc__taskmgr_ready(isc_taskmgr_t *manager0) { isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc_boolean_t is_ready; #ifdef USE_SHARED_MANAGER if (manager == NULL) @@ -1419,7 +1580,12 @@ isc__taskmgr_ready(isc_taskmgr_t *manager0) { #endif if (manager == NULL) return (ISC_FALSE); - return (ISC_TF(!ISC_LIST_EMPTY(manager->ready_tasks))); + + LOCK(&manager->lock); + is_ready = !empty_readyq(manager); + UNLOCK(&manager->lock); + + return (is_ready); } isc_result_t @@ -1438,6 +1604,29 @@ isc__taskmgr_dispatch(isc_taskmgr_t *manager0) { return (ISC_R_SUCCESS); } +#else +ISC_TASKFUNC_SCOPE void +isc__taskmgr_pause(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + LOCK(&manager->lock); + while (manager->tasks_running > 0) { + WAIT(&manager->paused, &manager->lock); + } + manager->pause_requested = ISC_TRUE; + UNLOCK(&manager->lock); +} + +ISC_TASKFUNC_SCOPE void +isc__taskmgr_resume(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + if (manager->pause_requested) { + manager->pause_requested = ISC_FALSE; + BROADCAST(&manager->work_available); + } + UNLOCK(&manager->lock); +} #endif /* USE_WORKER_THREADS */ ISC_TASKFUNC_SCOPE isc_result_t @@ -1479,6 +1668,44 @@ isc__task_endexclusive(isc_task_t *task0) { #endif } +ISC_TASKFUNC_SCOPE void +isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv) { + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + isc_boolean_t oldpriv; + + LOCK(&task->lock); + oldpriv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); + if (priv) + task->flags |= TASK_F_PRIVILEGED; + else + task->flags &= ~TASK_F_PRIVILEGED; + UNLOCK(&task->lock); + + if (priv == oldpriv) + return; + + LOCK(&manager->lock); + if (priv && ISC_LINK_LINKED(task, ready_link)) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + else if (!priv && ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + UNLOCK(&manager->lock); +} + +ISC_TASKFUNC_SCOPE isc_boolean_t +isc__task_privilege(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + isc_boolean_t priv; + + LOCK(&task->lock); + priv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); + UNLOCK(&task->lock); + return (priv); +} + #ifdef USE_SOCKETIMPREGISTER isc_result_t isc__task_register() { diff --git a/lib/isc/task_api.c b/lib/isc/task_api.c index 4e03db24..c1907207 100644 --- a/lib/isc/task_api.c +++ b/lib/isc/task_api.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: task_api.c,v 1.7 2010-12-22 23:46:59 tbox Exp $ */ +/* $Id: task_api.c,v 1.9 2011-09-02 23:46:32 tbox Exp $ */ #include <config.h> @@ -99,6 +99,20 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) { ENSURE(*managerp == NULL); } +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + manager->methods->setmode(manager, mode); +} + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + return (manager->methods->mode(manager)); +} + isc_result_t isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, isc_task_t **taskp) @@ -201,6 +215,20 @@ isc_task_endexclusive(isc_task_t *task) { task->methods->endexclusive(task); } +void +isc_task_setprivilege(isc_task_t *task, isc_boolean_t priv) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + task->methods->setprivilege(task, priv); +} + +isc_boolean_t +isc_task_privilege(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + return (task->methods->privilege(task)); +} + /*% * This is necessary for libisc's internal timer implementation. Other diff --git a/lib/isc/task_p.h b/lib/isc/task_p.h index 1bcdca65..ded69ddc 100644 --- a/lib/isc/task_p.h +++ b/lib/isc/task_p.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,17 +15,25 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: task_p.h,v 1.13 2009-09-02 23:48:02 tbox Exp $ */ +/* $Id: task_p.h,v 1.15 2011-09-02 23:46:32 tbox Exp $ */ #ifndef ISC_TASK_P_H #define ISC_TASK_P_H /*! \file */ +#if defined(BIND9) && defined(ISC_PLATFORM_USETHREADS) +void +isc__taskmgr_pause(isc_taskmgr_t *taskmgr); + +void +isc__taskmgr_resume(isc_taskmgr_t *taskmgr); +#else isc_boolean_t isc__taskmgr_ready(isc_taskmgr_t *taskmgr); isc_result_t isc__taskmgr_dispatch(isc_taskmgr_t *taskmgr); +#endif /* !BIND9 || !ISC_PLATFORM_USETHREADS */ #endif /* ISC_TASK_P_H */ diff --git a/lib/isc/taskpool.c b/lib/isc/taskpool.c index f8991a97..69514d6d 100644 --- a/lib/isc/taskpool.c +++ b/lib/isc/taskpool.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: taskpool.c,v 1.20 2011-07-07 23:47:49 tbox Exp $ */ +/* $Id: taskpool.c,v 1.21 2011-09-02 21:15:38 each Exp $ */ /*! \file */ @@ -163,9 +163,8 @@ isc_taskpool_destroy(isc_taskpool_t **poolp) { unsigned int i; isc_taskpool_t *pool = *poolp; for (i = 0; i < pool->ntasks; i++) { - if (pool->tasks[i] != NULL) { + if (pool->tasks[i] != NULL) isc_task_detach(&pool->tasks[i]); - } } isc_mem_put(pool->mctx, pool->tasks, pool->ntasks * sizeof(isc_task_t *)); @@ -173,4 +172,14 @@ isc_taskpool_destroy(isc_taskpool_t **poolp) { *poolp = NULL; } +void +isc_taskpool_setprivilege(isc_taskpool_t *pool, isc_boolean_t priv) { + unsigned int i; + + REQUIRE(pool != NULL); + for (i = 0; i < pool->ntasks; i++) { + if (pool->tasks[i] != NULL) + isc_task_setprivilege(pool->tasks[i], priv); + } +} diff --git a/lib/isc/tests/Makefile.in b/lib/isc/tests/Makefile.in index 5db6e437..8c0a088b 100644 --- a/lib/isc/tests/Makefile.in +++ b/lib/isc/tests/Makefile.in @@ -12,7 +12,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.6 2011-08-23 01:29:38 each Exp $ +# $Id: Makefile.in,v 1.7 2011-09-02 21:15:38 each Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -35,10 +35,12 @@ ISCDEPLIBS = ../libisc.@A@ LIBS = @LIBS@ @ATFLIBS@ OBJS = isctest.@O@ -SRCS = isctest.c taskpool_test.c socket_test.c hash_test.c +SRCS = isctest.c taskpool_test.c socket_test.c hash_test.c \ + task_test.c SUBDIRS = -TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ +TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \ + task_test@EXEEXT@ @BIND9_MAKE_RULES@ @@ -46,6 +48,10 @@ taskpool_test@EXEEXT@: taskpool_test.@O@ isctest.@O@ ${ISCDEPLIBS} ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ taskpool_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} +task_test@EXEEXT@: task_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + task_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + socket_test@EXEEXT@: socket_test.@O@ isctest.@O@ ${ISCDEPLIBS} ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ socket_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} diff --git a/lib/isc/tests/isctest.c b/lib/isc/tests/isctest.c index b512492b..e01f7f35 100644 --- a/lib/isc/tests/isctest.c +++ b/lib/isc/tests/isctest.c @@ -14,12 +14,14 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: isctest.c,v 1.3 2011-07-28 04:04:37 each Exp $ */ +/* $Id: isctest.c,v 1.4 2011-09-02 21:15:38 each Exp $ */ /*! \file */ #include <config.h> +#include <time.h> + #include <isc/app.h> #include <isc/buffer.h> #include <isc/entropy.h> @@ -153,3 +155,24 @@ isc_test_end() { isc_mem_destroy(&mctx); } +/* + * Sleep for 'usec' microseconds. + */ +void +isc_test_nap(isc_uint32_t usec) { +#ifdef HAVE_NANOSLEEP + struct timespec ts; + + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + nanosleep(&ts, NULL); +#elif HAVE_USLEEP + usleep(usec); +#else + /* + * No fractional-second sleep function is available, so we + * round up to the nearest second and sleep instead + */ + sleep((usec / 1000000) + 1); +#endif +} diff --git a/lib/isc/tests/isctest.h b/lib/isc/tests/isctest.h index 1d6d9645..2d5914f8 100644 --- a/lib/isc/tests/isctest.h +++ b/lib/isc/tests/isctest.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: isctest.h,v 1.3 2011-07-28 04:04:37 each Exp $ */ +/* $Id: isctest.h,v 1.4 2011-09-02 21:15:38 each Exp $ */ /*! \file */ @@ -52,3 +52,5 @@ isc_test_begin(FILE *logfile, isc_boolean_t start_managers); void isc_test_end(void); +void +isc_test_nap(isc_uint32_t usec); diff --git a/lib/isc/tests/socket_test.c b/lib/isc/tests/socket_test.c index c6f8d4b1..266ea2fc 100644 --- a/lib/isc/tests/socket_test.c +++ b/lib/isc/tests/socket_test.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: socket_test.c,v 1.3 2011-07-28 23:47:59 tbox Exp $ */ +/* $Id: socket_test.c,v 1.4 2011-09-02 21:15:38 each Exp $ */ /*! \file */ @@ -56,22 +56,6 @@ event_done(isc_task_t *task, isc_event_t *event) { isc_event_free(&event); } -static void -nap(isc_uint32_t usec) { -#ifdef HAVE_NANOSLEEP - struct timespec ts; - - ts.tv_sec = usec / 1000000; - ts.tv_nsec = (usec % 1000000) * 1000; - nanosleep(&ts, NULL); -#elif HAVE_USLEEP - usleep(usec); -#else - /* Round up to the nearest second and sleep, instead */ - sleep((usec / 1000000) + 1); -#endif -} - static isc_result_t waitfor(completion_t *completion) { int i = 0; @@ -80,7 +64,7 @@ waitfor(completion_t *completion) { while (isc__taskmgr_ready(taskmgr)) isc__taskmgr_dispatch(taskmgr); #endif - nap(1000); + isc_test_nap(1000); } if (completion->done) return (ISC_R_SUCCESS); diff --git a/lib/isc/tests/task_test.c b/lib/isc/tests/task_test.c new file mode 100644 index 00000000..a8dfd89d --- /dev/null +++ b/lib/isc/tests/task_test.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: task_test.c,v 1.3 2011-09-02 23:46:33 tbox Exp $ */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <unistd.h> + +#include <isc/task.h> +#include <isc/util.h> + +#include "../task_p.h" +#include "isctest.h" + +/* + * Helper functions + */ + +/* task event handler, sets a boolean to true */ +int counter = 0; +isc_mutex_t set_lock; + +static void +set(isc_task_t *task, isc_event_t *event) { + int *value = (int *) event->ev_arg; + + UNUSED(task); + + isc_event_free(&event); + LOCK(&set_lock); + *value = counter++; + UNLOCK(&set_lock); +} + +static void +set_and_drop(isc_task_t *task, isc_event_t *event) { + int *value = (int *) event->ev_arg; + + UNUSED(task); + + isc_event_free(&event); + LOCK(&set_lock); + *value = (int) isc_taskmgr_mode(taskmgr); + counter++; + UNLOCK(&set_lock); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_normal); +} + +/* + * Individual unit tests + */ + +/* Create a task */ +ATF_TC(create_task); +ATF_TC_HEAD(create_task, tc) { + atf_tc_set_md_var(tc, "descr", "create and destroy a task"); +} +ATF_TC_BODY(create_task, tc) { + isc_result_t result; + isc_task_t *task = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_task_destroy(&task); + ATF_REQUIRE_EQ(task, NULL); + + isc_test_end(); +} + +/* Process events */ +ATF_TC(all_events); +ATF_TC_HEAD(all_events, tc) { + atf_tc_set_md_var(tc, "descr", "process task events"); +} +ATF_TC_BODY(all_events, tc) { + isc_result_t result; + isc_task_t *task = NULL; + isc_event_t *event; + int a = 0, b = 0; + int i = 0; + + UNUSED(tc); + + counter = 1; + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* First event */ + event = isc_event_allocate(mctx, task, ISC_TASKEVENT_TEST, + set, &a, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(a, 0); + isc_task_send(task, &event); + + event = isc_event_allocate(mctx, task, ISC_TASKEVENT_TEST, + set, &b, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(b, 0); + isc_task_send(task, &event); + + while (a == 0 && b == 0 && i++ < 5000) { +#ifndef ISC_PLATFORM_USETHREADS + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); +#endif + isc_test_nap(1000); + } + + ATF_CHECK(a != 0); + ATF_CHECK(b != 0); + + isc_task_destroy(&task); + ATF_REQUIRE_EQ(task, NULL); + + isc_test_end(); +} + +/* Privileged events */ +ATF_TC(privileged_events); +ATF_TC_HEAD(privileged_events, tc) { + atf_tc_set_md_var(tc, "descr", "process privileged events"); +} +ATF_TC_BODY(privileged_events, tc) { + isc_result_t result; + isc_task_t *task1 = NULL, *task2 = NULL; + isc_event_t *event; + int a = 0, b = 0, c = 0, d = 0, e = 0; + int i = 0; + + UNUSED(tc); + + counter = 1; + result = isc_mutex_init(&set_lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + +#ifdef ISC_PLATFORM_USETHREADS + /* + * Pause the task manager so we can fill up the work queue + * without things happening while we do it. + */ + isc__taskmgr_pause(taskmgr); +#endif + + result = isc_task_create(taskmgr, 0, &task1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task1, "privileged", NULL); + ATF_CHECK(!isc_task_privilege(task1)); + isc_task_setprivilege(task1, ISC_TRUE); + ATF_CHECK(isc_task_privilege(task1)); + + result = isc_task_create(taskmgr, 0, &task2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task2, "normal", NULL); + ATF_CHECK(!isc_task_privilege(task2)); + + /* First event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set, &a, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(a, 0); + isc_task_send(task1, &event); + + /* Second event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set, &b, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(b, 0); + isc_task_send(task2, &event); + + /* Third event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set, &c, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(c, 0); + isc_task_send(task1, &event); + + /* Fourth event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set, &d, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(d, 0); + isc_task_send(task1, &event); + + /* Fifth event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set, &e, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(e, 0); + isc_task_send(task2, &event); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged); + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_privileged); + +#ifdef ISC_PLATFORM_USETHREADS + isc__taskmgr_resume(taskmgr); +#endif + + /* We're waiting for *all* variables to be set */ + while ((a == 0 || b == 0 || c == 0 || d == 0 || e == 0) && i++ < 5000) { +#ifndef ISC_PLATFORM_USETHREADS + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); +#endif + isc_test_nap(1000); + } + + /* + * We can't guarantee what order the events fire, but + * we do know the privileged tasks that set a, c, and d + * would have fired first. + */ + ATF_CHECK(a <= 3); + ATF_CHECK(c <= 3); + ATF_CHECK(d <= 3); + + /* ...and the non-privileged tasks that set b and e, last */ + ATF_CHECK(b >= 4); + ATF_CHECK(e >= 4); + + ATF_CHECK_EQ(counter, 6); + + isc_task_setprivilege(task1, ISC_FALSE); + ATF_CHECK(!isc_task_privilege(task1)); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + + isc_task_destroy(&task1); + ATF_REQUIRE_EQ(task1, NULL); + isc_task_destroy(&task2); + ATF_REQUIRE_EQ(task2, NULL); + + isc_test_end(); +} + +/* + * Edge case: this tests that the task manager behaves as expected when + * we explicitly set it into normal mode *while* running privileged. + */ +ATF_TC(privilege_drop); +ATF_TC_HEAD(privilege_drop, tc) { + atf_tc_set_md_var(tc, "descr", "process privileged events"); +} +ATF_TC_BODY(privilege_drop, tc) { + isc_result_t result; + isc_task_t *task1 = NULL, *task2 = NULL; + isc_event_t *event; + int a = 0, b = 0, c = 0, d = 0, e = 0; + int i = 0; + + UNUSED(tc); + + counter = 1; + result = isc_mutex_init(&set_lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + +#ifdef ISC_PLATFORM_USETHREADS + /* + * Pause the task manager so we can fill up the work queue + * without things happening while we do it. + */ + isc__taskmgr_pause(taskmgr); +#endif + + result = isc_task_create(taskmgr, 0, &task1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task1, "privileged", NULL); + ATF_CHECK(!isc_task_privilege(task1)); + isc_task_setprivilege(task1, ISC_TRUE); + ATF_CHECK(isc_task_privilege(task1)); + + result = isc_task_create(taskmgr, 0, &task2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task2, "normal", NULL); + ATF_CHECK(!isc_task_privilege(task2)); + + /* First event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set_and_drop, &a, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(a, 0); + isc_task_send(task1, &event); + + /* Second event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set_and_drop, &b, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(b, 0); + isc_task_send(task2, &event); + + /* Third event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set_and_drop, &c, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(c, 0); + isc_task_send(task1, &event); + + /* Fourth event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set_and_drop, &d, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(d, 0); + isc_task_send(task1, &event); + + /* Fifth event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set_and_drop, &e, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(e, 0); + isc_task_send(task2, &event); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged); + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_privileged); + +#ifdef ISC_PLATFORM_USETHREADS + isc__taskmgr_resume(taskmgr); +#endif + + /* We're waiting for *any* variable to be set */ + while ((a == 0 && b == 0 && c == 0 && d == 0 && e == 0) && i++ < 5000) { +#ifndef ISC_PLATFORM_USETHREADS + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); +#endif + isc_test_nap(1000); + } + + /* + * We can't guarantee what order the events fire, but + * we do know *exactly one* of the privileged tasks will + * have run in privileged mode... + */ + ATF_CHECK(a == isc_taskmgrmode_privileged || + c == isc_taskmgrmode_privileged || + d == isc_taskmgrmode_privileged); + ATF_CHECK(a + c + d == isc_taskmgrmode_privileged); + + /* ...and neither of the non-privileged tasks did... */ + ATF_CHECK(b == isc_taskmgrmode_normal || e == isc_taskmgrmode_normal); + + /* ...but all five of them did run. */ + ATF_CHECK_EQ(counter, 6); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + + isc_task_destroy(&task1); + ATF_REQUIRE_EQ(task1, NULL); + isc_task_destroy(&task2); + ATF_REQUIRE_EQ(task2, NULL); + + isc_test_end(); +} +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, create_task); + ATF_TP_ADD_TC(tp, all_events); + ATF_TP_ADD_TC(tp, privileged_events); + ATF_TP_ADD_TC(tp, privilege_drop); + + return (atf_no_error()); +} + diff --git a/lib/isc/tests/taskpool_test.c b/lib/isc/tests/taskpool_test.c index a582afdf..3cb9ad47 100644 --- a/lib/isc/tests/taskpool_test.c +++ b/lib/isc/tests/taskpool_test.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: taskpool_test.c,v 1.3 2011-07-28 04:04:37 each Exp $ */ +/* $Id: taskpool_test.c,v 1.4 2011-09-02 21:15:38 each Exp $ */ /*! \file */ @@ -148,6 +148,55 @@ ATF_TC_BODY(get_tasks, tc) { isc_test_end(); } +/* Get tasks */ +ATF_TC(set_privilege); +ATF_TC_HEAD(set_privilege, tc) { + atf_tc_set_md_var(tc, "descr", "create a taskpool"); +} +ATF_TC_BODY(set_privilege, tc) { + isc_result_t result; + isc_taskpool_t *pool = NULL; + isc_task_t *task1 = NULL, *task2 = NULL, *task3 = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_taskpool_create(taskmgr, mctx, 2, 2, &pool); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool), 2); + + isc_taskpool_setprivilege(pool, ISC_TRUE); + + isc_taskpool_gettask(pool, &task1); + isc_taskpool_gettask(pool, &task2); + isc_taskpool_gettask(pool, &task3); + + ATF_CHECK(ISCAPI_TASK_VALID(task1)); + ATF_CHECK(ISCAPI_TASK_VALID(task2)); + ATF_CHECK(ISCAPI_TASK_VALID(task3)); + + ATF_CHECK(isc_task_privilege(task1)); + ATF_CHECK(isc_task_privilege(task2)); + ATF_CHECK(isc_task_privilege(task3)); + + isc_taskpool_setprivilege(pool, ISC_FALSE); + + ATF_CHECK(!isc_task_privilege(task1)); + ATF_CHECK(!isc_task_privilege(task2)); + ATF_CHECK(!isc_task_privilege(task3)); + + isc_task_destroy(&task1); + isc_task_destroy(&task2); + isc_task_destroy(&task3); + + isc_taskpool_destroy(&pool); + ATF_REQUIRE_EQ(pool, NULL); + + isc_test_end(); +} + /* * Main */ @@ -155,6 +204,7 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, create_pool); ATF_TP_ADD_TC(tp, expand_pool); ATF_TP_ADD_TC(tp, get_tasks); + ATF_TP_ADD_TC(tp, set_privilege); return (atf_no_error()); } diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c index 54a6d256..5e97b902 100644 --- a/lib/isc/unix/socket.c +++ b/lib/isc/unix/socket.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: socket.c,v 1.346 2011-08-24 23:17:52 marka Exp $ */ +/* $Id: socket.c,v 1.348 2011-08-25 11:37:13 marka Exp $ */ /*! \file */ @@ -3784,7 +3784,6 @@ static isc_threadresult_t watcher(void *uap) { isc__socketmgr_t *manager = uap; isc_boolean_t done; - int ctlfd; int cc; #ifdef USE_KQUEUE const char *fnname = "kevent()"; @@ -3796,16 +3795,19 @@ watcher(void *uap) { #elif defined (USE_SELECT) const char *fnname = "select()"; int maxfd; + int ctlfd; #endif char strbuf[ISC_STRERRORSIZE]; #ifdef ISC_SOCKET_USE_POLLWATCH pollstate_t pollstate = poll_idle; #endif +#if defined (USE_SELECT) /* * Get the control fd here. This will never change. */ ctlfd = manager->pipe_fds[0]; +#endif done = ISC_FALSE; while (!done) { do { diff --git a/lib/isc/win32/libisc.def b/lib/isc/win32/libisc.def index 226e0142..fb084103 100644 --- a/lib/isc/win32/libisc.def +++ b/lib/isc/win32/libisc.def @@ -127,17 +127,21 @@ isc__task_getcurrenttime isc__task_getname isc__task_gettag isc__task_onshutdown +isc__task_privilege isc__task_purge isc__task_purgeevent isc__task_purgerange isc__task_send isc__task_sendanddetach isc__task_setname +isc__task_setprivilege isc__task_shutdown isc__task_unsend isc__task_unsendrange isc__taskmgr_create isc__taskmgr_destroy +isc__taskmgr_mode +isc__taskmgr_setmode isc__timer_attach isc__timer_create isc__timer_detach @@ -535,6 +539,7 @@ isc_taskpool_create isc_taskpool_destroy isc_taskpool_expand isc_taskpool_gettask +isc_taskpool_setprivilege isc_taskpool_size isc_thread_create isc_thread_join diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c index 33090c77..59d309c2 100644 --- a/lib/isccfg/aclconf.c +++ b/lib/isccfg/aclconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: aclconf.c,v 1.31 2011-06-17 23:47:49 tbox Exp $ */ +/* $Id: aclconf.c,v 1.32 2011-08-26 04:49:14 marka Exp $ */ #include <config.h> @@ -74,13 +74,11 @@ void cfg_aclconfctx_detach(cfg_aclconfctx_t **actxp) { cfg_aclconfctx_t *actx; dns_acl_t *dacl, *next; - isc_mem_t *mctx; unsigned int refs; REQUIRE(actxp != NULL && *actxp != NULL); actx = *actxp; - mctx = actx->mctx; isc_refcount_decrement(&actx->references, &refs); if (refs == 0) { diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 52f1d8a1..b66b943b 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namedconf.c,v 1.139 2011-07-01 02:25:48 marka Exp $ */ +/* $Id: namedconf.c,v 1.141 2011-09-06 22:29:33 smann Exp $ */ /*! \file */ @@ -1395,10 +1395,12 @@ zone_clauses[] = { { "notify-to-soa", &cfg_type_boolean, 0 }, { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY }, { "serial-update-method", &cfg_type_updatemethod, 0 }, + { "request-ixfr", &cfg_type_boolean, 0 }, { "sig-signing-nodes", &cfg_type_uint32, 0 }, { "sig-signing-signatures", &cfg_type_uint32, 0 }, { "sig-signing-type", &cfg_type_uint32, 0 }, { "sig-validity-interval", &cfg_type_validityinterval, 0 }, + { "inline-signing", &cfg_type_boolean, 0 }, { "transfer-source", &cfg_type_sockaddr4wild, 0 }, { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "try-tcp-refresh", &cfg_type_boolean, 0 }, diff --git a/lib/lwres/getnameinfo.c b/lib/lwres/getnameinfo.c index b27ac407..ab72b4a2 100644 --- a/lib/lwres/getnameinfo.c +++ b/lib/lwres/getnameinfo.c @@ -1,5 +1,5 @@ /* - * Portions Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 2004, 2005, 2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Portions Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: getnameinfo.c,v 1.39 2007-06-19 23:47:22 tbox Exp $ */ +/* $Id: getnameinfo.c,v 1.41 2011-08-30 23:46:53 tbox Exp $ */ /*! \file */ @@ -62,51 +62,51 @@ * sockaddr sa which is salen bytes long. The hostname is of length * hostlen and is returned via *host. The maximum length of the hostname * is 1025 bytes: #NI_MAXHOST. - * + * * The name of the service associated with the port number in sa is * returned in *serv. It is servlen bytes long. The maximum length of the * service name is #NI_MAXSERV - 32 bytes. - * + * * The flags argument sets the following bits: - * + * * \li #NI_NOFQDN: * A fully qualified domain name is not required for local hosts. * The local part of the fully qualified domain name is returned * instead. - * + * * \li #NI_NUMERICHOST * Return the address in numeric form, as if calling inet_ntop(), * instead of a host name. - * + * * \li #NI_NAMEREQD * A name is required. If the hostname cannot be found in the DNS * and this flag is set, a non-zero error code is returned. If the * hostname is not found and the flag is not set, the address is * returned in numeric form. - * + * * \li #NI_NUMERICSERV * The service name is returned as a digit string representing the * port number. - * + * * \li #NI_DGRAM * Specifies that the service being looked up is a datagram * service, and causes getservbyport() to be called with a second * argument of "udp" instead of its default of "tcp". This is * required for the few ports (512-514) that have different * services for UDP and TCP. - * + * * \section getnameinfo_return Return Values - * + * * lwres_getnameinfo() returns 0 on success or a non-zero error code if * an error occurs. - * + * * \section getname_see See Also - * - * RFC2133, getservbyport(), + * + * RFC2133, getservbyport(), * lwres_getnamebyaddr(). lwres_net_ntop(). - * + * * \section getnameinfo_bugs Bugs - * + * * RFC2133 fails to define what the nonzero return values of * getnameinfo() are. */ @@ -219,6 +219,7 @@ lwres_getnameinfo(const struct sockaddr *sa, size_t salen, char *host, default: port = 0; addr = NULL; + POST(port); POST(addr); INSIST(0); } proto = (flags & NI_DGRAM) ? "udp" : "tcp"; diff --git a/lib/lwres/lwinetpton.c b/lib/lwres/lwinetpton.c index 55c732c3..0710f1d1 100644 --- a/lib/lwres/lwinetpton.c +++ b/lib/lwres/lwinetpton.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1996-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -19,7 +19,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char rcsid[] = "$Id: lwinetpton.c,v 1.12 2007-06-19 23:47:22 tbox Exp $"; +static char rcsid[] = "$Id: lwinetpton.c,v 1.14 2011-08-29 23:46:44 tbox Exp $"; #endif /* LIBC_SCCS and not lint */ #include <config.h> @@ -41,7 +41,7 @@ static char rcsid[] = "$Id: lwinetpton.c,v 1.12 2007-06-19 23:47:22 tbox Exp $"; static int inet_pton4(const char *src, unsigned char *dst); static int inet_pton6(const char *src, unsigned char *dst); -/*! +/*! * int * lwres_net_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) @@ -103,7 +103,12 @@ inet_pton4(const char *src, unsigned char *dst) { } else if (ch == '.' && saw_digit) { if (octets == 4) return (0); - *++tp = 0; + /* + * "clang --analyse" generates warnings using: + * *++tp = 0; + */ + tp++; + *tp = 0; saw_digit = 0; } else return (0); |