diff options
Diffstat (limited to 'contrib/bind9/lib/dns/zone.c')
-rw-r--r-- | contrib/bind9/lib/dns/zone.c | 1338 |
1 files changed, 1147 insertions, 191 deletions
diff --git a/contrib/bind9/lib/dns/zone.c b/contrib/bind9/lib/dns/zone.c index d2a47b0..5a73796 100644 --- a/contrib/bind9/lib/dns/zone.c +++ b/contrib/bind9/lib/dns/zone.c @@ -15,7 +15,9 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.333.2.23.2.65 2006/07/19 01:04:24 marka Exp $ */ +/* $Id: zone.c,v 1.410.18.47 2006/12/07 06:21:16 marka Exp $ */ + +/*! \file */ #include <config.h> @@ -25,16 +27,19 @@ #include <isc/random.h> #include <isc/ratelimiter.h> #include <isc/refcount.h> +#include <isc/rwlock.h> #include <isc/serial.h> #include <isc/string.h> #include <isc/taskpool.h> #include <isc/timer.h> #include <isc/util.h> +#include <dns/acache.h> #include <dns/acl.h> #include <dns/adb.h> #include <dns/callbacks.h> #include <dns/db.h> +#include <dns/dbiterator.h> #include <dns/events.h> #include <dns/journal.h> #include <dns/log.h> @@ -79,7 +84,7 @@ #define IO_MAGIC ISC_MAGIC('Z', 'm', 'I', 'O') #define DNS_IO_VALID(load) ISC_MAGIC_VALID(load, IO_MAGIC) -/* +/*% * Ensure 'a' is at least 'min' but not more than 'max'. */ #define RANGE(a, min, max) \ @@ -88,16 +93,16 @@ /* * Default values. */ -#define DNS_DEFAULT_IDLEIN 3600 /* 1 hour */ -#define DNS_DEFAULT_IDLEOUT 3600 /* 1 hour */ -#define MAX_XFER_TIME (2*3600) /* Documented default is 2 hours */ +#define DNS_DEFAULT_IDLEIN 3600 /*%< 1 hour */ +#define DNS_DEFAULT_IDLEOUT 3600 /*%< 1 hour */ +#define MAX_XFER_TIME (2*3600) /*%< Documented default is 2 hours */ #ifndef DNS_MAX_EXPIRE -#define DNS_MAX_EXPIRE 14515200 /* 24 weeks */ +#define DNS_MAX_EXPIRE 14515200 /*%< 24 weeks */ #endif #ifndef DNS_DUMP_DELAY -#define DNS_DUMP_DELAY 900 /* 15 minutes */ +#define DNS_DUMP_DELAY 900 /*%< 15 minutes */ #endif typedef struct dns_notify dns_notify_t; @@ -123,6 +128,18 @@ typedef ISC_LIST(dns_io_t) dns_iolist_t; #define LOCKED_ZONE(z) ISC_TRUE #endif +#ifdef ISC_RWLOCK_USEATOMIC +#define ZONEDB_INITLOCK(l) isc_rwlock_init((l), 0, 0) +#define ZONEDB_DESTROYLOCK(l) isc_rwlock_destroy(l) +#define ZONEDB_LOCK(l, t) RWLOCK((l), (t)) +#define ZONEDB_UNLOCK(l, t) RWUNLOCK((l), (t)) +#else +#define ZONEDB_INITLOCK(l) isc_mutex_init(l) +#define ZONEDB_DESTROYLOCK(l) DESTROYLOCK(l) +#define ZONEDB_LOCK(l, t) LOCK(l) +#define ZONEDB_UNLOCK(l, t) UNLOCK(l) +#endif + struct dns_zone { /* Unlocked */ unsigned int magic; @@ -133,14 +150,21 @@ struct dns_zone { isc_mem_t *mctx; isc_refcount_t erefs; +#ifdef ISC_RWLOCK_USEATOMIC + isc_rwlock_t dblock; +#else + isc_mutex_t dblock; +#endif + dns_db_t *db; /* Locked by dblock */ + /* Locked */ - dns_db_t *db; dns_zonemgr_t *zmgr; ISC_LINK(dns_zone_t) link; /* Used by zmgr. */ isc_timer_t *timer; unsigned int irefs; dns_name_t origin; char *masterfile; + dns_masterformat_t masterformat; char *journal; isc_int32_t journalsize; dns_rdataclass_t rdclass; @@ -153,6 +177,7 @@ struct dns_zone { isc_time_t refreshtime; isc_time_t dumptime; isc_time_t loadtime; + isc_time_t notifytime; isc_uint32_t serial; isc_uint32_t refresh; isc_uint32_t retry; @@ -176,13 +201,13 @@ struct dns_zone { unsigned int notifycnt; isc_sockaddr_t notifyfrom; isc_task_t *task; - isc_sockaddr_t notifysrc4; - isc_sockaddr_t notifysrc6; - isc_sockaddr_t xfrsource4; - isc_sockaddr_t xfrsource6; - isc_sockaddr_t altxfrsource4; - isc_sockaddr_t altxfrsource6; - isc_sockaddr_t sourceaddr; + isc_sockaddr_t notifysrc4; + isc_sockaddr_t notifysrc6; + isc_sockaddr_t xfrsource4; + isc_sockaddr_t xfrsource6; + isc_sockaddr_t altxfrsource4; + isc_sockaddr_t altxfrsource6; + isc_sockaddr_t sourceaddr; dns_xfrin_ctx_t *xfr; /* task locked */ dns_tsigkey_t *tsigkey; /* key used for xfr */ /* Access Control Lists */ @@ -192,6 +217,7 @@ struct dns_zone { dns_acl_t *query_acl; dns_acl_t *xfr_acl; isc_boolean_t update_disabled; + isc_boolean_t zero_no_soa_ttl; dns_severity_t check_names; ISC_LIST(dns_notify_t) notifies; dns_request_t *request; @@ -207,7 +233,11 @@ struct dns_zone { dns_ssutable_t *ssutable; isc_uint32_t sigvalidityinterval; dns_view_t *view; - /* + dns_acache_t *acache; + dns_checkmxfunc_t checkmx; + dns_checksrvfunc_t checksrv; + dns_checknsfunc_t checkns; + /*% * Zones in certain states such as "waiting for zone transfer" * or "zone transfer in progress" are kept on per-state linked lists * in the zone manager using the 'statelink' field. The 'statelist' @@ -216,10 +246,13 @@ struct dns_zone { */ ISC_LINK(dns_zone_t) statelink; dns_zonelist_t *statelist; - /* + /*% * Optional per-zone statistics counters (NULL if not present). */ - isc_uint64_t *counters; + isc_uint64_t *counters; + isc_uint32_t notifydelay; + dns_isselffunc_t isself; + void *isselfarg; }; #define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0)) @@ -232,35 +265,35 @@ struct dns_zone { (z)->flags &= ~(f); \ } while (0) /* XXX MPA these may need to go back into zone.h */ -#define DNS_ZONEFLG_REFRESH 0x00000001U /* refresh check in progress */ -#define DNS_ZONEFLG_NEEDDUMP 0x00000002U /* zone need consolidation */ -#define DNS_ZONEFLG_USEVC 0x00000004U /* use tcp for refresh query */ -#define DNS_ZONEFLG_DUMPING 0x00000008U /* a dump is in progress */ -#define DNS_ZONEFLG_HASINCLUDE 0x00000010U /* $INCLUDE in zone file */ -#define DNS_ZONEFLG_LOADED 0x00000020U /* database has loaded */ -#define DNS_ZONEFLG_EXITING 0x00000040U /* zone is being destroyed */ -#define DNS_ZONEFLG_EXPIRED 0x00000080U /* zone has expired */ -#define DNS_ZONEFLG_NEEDREFRESH 0x00000100U /* refresh check needed */ -#define DNS_ZONEFLG_UPTODATE 0x00000200U /* zone contents are +#define DNS_ZONEFLG_REFRESH 0x00000001U /*%< refresh check in progress */ +#define DNS_ZONEFLG_NEEDDUMP 0x00000002U /*%< zone need consolidation */ +#define DNS_ZONEFLG_USEVC 0x00000004U /*%< use tcp for refresh query */ +#define DNS_ZONEFLG_DUMPING 0x00000008U /*%< a dump is in progress */ +#define DNS_ZONEFLG_HASINCLUDE 0x00000010U /*%< $INCLUDE in zone file */ +#define DNS_ZONEFLG_LOADED 0x00000020U /*%< database has loaded */ +#define DNS_ZONEFLG_EXITING 0x00000040U /*%< zone is being destroyed */ +#define DNS_ZONEFLG_EXPIRED 0x00000080U /*%< zone has expired */ +#define DNS_ZONEFLG_NEEDREFRESH 0x00000100U /*%< refresh check needed */ +#define DNS_ZONEFLG_UPTODATE 0x00000200U /*%< zone contents are * uptodate */ -#define DNS_ZONEFLG_NEEDNOTIFY 0x00000400U /* need to send out notify +#define DNS_ZONEFLG_NEEDNOTIFY 0x00000400U /*%< need to send out notify * messages */ -#define DNS_ZONEFLG_DIFFONRELOAD 0x00000800U /* generate a journal diff on +#define DNS_ZONEFLG_DIFFONRELOAD 0x00000800U /*%< generate a journal diff on * reload */ -#define DNS_ZONEFLG_NOMASTERS 0x00001000U /* an attempt to refresh a +#define DNS_ZONEFLG_NOMASTERS 0x00001000U /*%< an attempt to refresh a * zone with no masters * occured */ -#define DNS_ZONEFLG_LOADING 0x00002000U /* load from disk in progress*/ -#define DNS_ZONEFLG_HAVETIMERS 0x00004000U /* timer values have been set +#define DNS_ZONEFLG_LOADING 0x00002000U /*%< load from disk in progress*/ +#define DNS_ZONEFLG_HAVETIMERS 0x00004000U /*%< timer values have been set * from SOA (if not set, we * are still using * default timer values) */ -#define DNS_ZONEFLG_FORCEXFER 0x00008000U /* Force a zone xfer */ +#define DNS_ZONEFLG_FORCEXFER 0x00008000U /*%< Force a zone xfer */ #define DNS_ZONEFLG_NOREFRESH 0x00010000U #define DNS_ZONEFLG_DIALNOTIFY 0x00020000U #define DNS_ZONEFLG_DIALREFRESH 0x00040000U #define DNS_ZONEFLG_SHUTDOWN 0x00080000U -#define DNS_ZONEFLAG_NOIXFR 0x00100000U /* IXFR failed, force AXFR */ +#define DNS_ZONEFLAG_NOIXFR 0x00100000U /*%< IXFR failed, force AXFR */ #define DNS_ZONEFLG_FLUSH 0x00200000U #define DNS_ZONEFLG_NOEDNS 0x00400000U #define DNS_ZONEFLG_USEALTXFRSRC 0x00800000U @@ -301,7 +334,7 @@ struct dns_zonemgr { dns_iolist_t low; }; -/* +/*% * Hold notify state. */ struct dns_notify { @@ -318,7 +351,7 @@ struct dns_notify { #define DNS_NOTIFY_NOSOA 0x0001U -/* +/*% * dns_stub holds state while performing a 'stub' transfer. * 'db' is the zone's 'db' or a new one if this is the initial * transfer. @@ -332,7 +365,7 @@ struct dns_stub { dns_dbversion_t *version; }; -/* +/*% * Hold load state. */ struct dns_load { @@ -344,7 +377,7 @@ struct dns_load { dns_rdatacallbacks_t callbacks; }; -/* +/*% * Hold forward state. */ struct dns_forward { @@ -359,7 +392,7 @@ struct dns_forward { void *callback_arg; }; -/* +/*% * Hold IO request state. */ struct dns_io { @@ -386,6 +419,8 @@ static void zone_iattach(dns_zone_t *source, dns_zone_t **target); static void zone_idetach(dns_zone_t **zonep); static isc_result_t zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump); +static inline void zone_attachdb(dns_zone_t *zone, dns_db_t *db); +static inline void zone_detachdb(dns_zone_t *zone); static isc_result_t default_journal(dns_zone_t *zone); static void zone_xfrdone(dns_zone_t *zone, isc_result_t result); static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, @@ -430,17 +465,18 @@ static void zonemgr_putio(dns_io_t **iop); static void zonemgr_cancelio(dns_io_t *io); static isc_result_t -zone_get_from_db(dns_db_t *db, dns_name_t *origin, unsigned int *nscount, +zone_get_from_db(dns_zone_t *zone, dns_db_t *db, unsigned int *nscount, unsigned int *soacount, isc_uint32_t *serial, isc_uint32_t *refresh, isc_uint32_t *retry, - isc_uint32_t *expire, isc_uint32_t *minimum); + isc_uint32_t *expire, isc_uint32_t *minimum, + unsigned int *errors); static void zone_freedbargs(dns_zone_t *zone); static void forward_callback(isc_task_t *task, isc_event_t *event); static void zone_saveunique(dns_zone_t *zone, const char *path, const char *templat); static void zone_maintenance(dns_zone_t *zone); -static void zone_notify(dns_zone_t *zone); +static void zone_notify(dns_zone_t *zone, isc_time_t *now); static void dump_done(void *arg, isc_result_t result); #define ENTER zone_debuglog(zone, me, 1, "enter") @@ -484,36 +520,41 @@ isc_result_t dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { isc_result_t result; dns_zone_t *zone; + isc_time_t now; REQUIRE(zonep != NULL && *zonep == NULL); REQUIRE(mctx != NULL); + TIME_NOW(&now); zone = isc_mem_get(mctx, sizeof(*zone)); if (zone == NULL) return (ISC_R_NOMEMORY); + zone->mctx = NULL; + isc_mem_attach(mctx, &zone->mctx); + result = isc_mutex_init(&zone->lock); - if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, zone, sizeof(*zone)); - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_mutex_init() failed: %s", - isc_result_totext(result)); - return (ISC_R_UNEXPECTED); - } + if (result != ISC_R_SUCCESS) + goto free_zone; + + result = ZONEDB_INITLOCK(&zone->dblock); + if (result != ISC_R_SUCCESS) + goto free_mutex; /* XXX MPA check that all elements are initialised */ - zone->mctx = NULL; #ifdef DNS_ZONE_CHECKLOCK zone->locked = ISC_FALSE; #endif - isc_mem_attach(mctx, &zone->mctx); zone->db = NULL; zone->zmgr = NULL; ISC_LINK_INIT(zone, link); - isc_refcount_init(&zone->erefs, 1); /* Implicit attach. */ + result = isc_refcount_init(&zone->erefs, 1); /* Implicit attach. */ + if (result != ISC_R_SUCCESS) + goto free_dblock; zone->irefs = 0; dns_name_init(&zone->origin, NULL); zone->masterfile = NULL; + zone->masterformat = dns_masterformat_none; zone->keydirectory = NULL; zone->journalsize = -1; zone->journal = NULL; @@ -527,6 +568,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { isc_time_settoepoch(&zone->refreshtime); isc_time_settoepoch(&zone->dumptime); isc_time_settoepoch(&zone->loadtime); + zone->notifytime = now; zone->serial = 0; zone->refresh = DNS_ZONE_DEFAULTREFRESH; zone->retry = DNS_ZONE_DEFAULTRETRY; @@ -551,6 +593,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->query_acl = NULL; zone->xfr_acl = NULL; zone->update_disabled = ISC_FALSE; + zone->zero_no_soa_ttl = ISC_TRUE; zone->check_names = dns_severity_ignore; zone->request = NULL; zone->lctx = NULL; @@ -574,16 +617,23 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->ssutable = NULL; zone->sigvalidityinterval = 30 * 24 * 3600; zone->view = NULL; + zone->acache = NULL; + zone->checkmx = NULL; + zone->checksrv = NULL; + zone->checkns = NULL; ISC_LINK_INIT(zone, statelink); zone->statelist = NULL; zone->counters = NULL; + zone->notifydelay = 5; + zone->isself = NULL; + zone->isselfarg = NULL; zone->magic = ZONE_MAGIC; /* Must be after magic is set. */ result = dns_zone_setdbtype(zone, dbargc_default, dbargv_default); if (result != ISC_R_SUCCESS) - goto free_mutex; + goto free_erefs; ISC_EVENT_INIT(&zone->ctlevent, sizeof(zone->ctlevent), 0, NULL, DNS_EVENT_ZONECONTROL, zone_shutdown, zone, zone, @@ -591,8 +641,17 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { *zonep = zone; return (ISC_R_SUCCESS); + free_erefs: + isc_refcount_decrement(&zone->erefs, NULL); + isc_refcount_destroy(&zone->erefs); + + free_dblock: + ZONEDB_DESTROYLOCK(&zone->dblock); + free_mutex: DESTROYLOCK(&zone->lock); + + free_zone: isc_mem_putanddetach(&zone->mctx, zone, sizeof(*zone)); return (result); } @@ -639,7 +698,9 @@ zone_free(dns_zone_t *zone) { if (zone->counters != NULL) dns_stats_freecounters(zone->mctx, &zone->counters); if (zone->db != NULL) - dns_db_detach(&zone->db); + zone_detachdb(zone); + if (zone->acache != NULL) + dns_acache_detach(&zone->acache); zone_freedbargs(zone); RUNTIME_CHECK(dns_zone_setmasterswithkeys(zone, NULL, NULL, 0) == ISC_R_SUCCESS); @@ -662,6 +723,7 @@ zone_free(dns_zone_t *zone) { dns_ssutable_detach(&zone->ssutable); /* last stuff */ + ZONEDB_DESTROYLOCK(&zone->dblock); DESTROYLOCK(&zone->lock); isc_refcount_destroy(&zone->erefs); zone->magic = 0; @@ -739,6 +801,39 @@ zone_freedbargs(dns_zone_t *zone) { } isc_result_t +dns_zone_getdbtype(dns_zone_t *zone, char ***argv, isc_mem_t *mctx) { + size_t size = 0; + unsigned int i; + isc_result_t result = ISC_R_SUCCESS; + void *mem; + char **tmp, *tmp2; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(argv != NULL && *argv == NULL); + + LOCK_ZONE(zone); + size = (zone->db_argc + 1) * sizeof(char *); + for (i = 0; i < zone->db_argc; i++) + size += strlen(zone->db_argv[i]) + 1; + mem = isc_mem_allocate(mctx, size); + if (mem != NULL) { + tmp = mem; + tmp2 = mem; + tmp2 += (zone->db_argc + 1) * sizeof(char *); + for (i = 0; i < zone->db_argc; i++) { + *tmp++ = tmp2; + strcpy(tmp2, zone->db_argv[i]); + tmp2 += strlen(tmp2) + 1; + } + *tmp = NULL; + } else + result = ISC_R_NOMEMORY; + UNLOCK_ZONE(zone); + *argv = mem; + return (result); +} + +isc_result_t dns_zone_setdbtype(dns_zone_t *zone, unsigned int dbargc, const char * const *dbargv) { isc_result_t result = ISC_R_SUCCESS; @@ -822,6 +917,35 @@ dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) { return (result); } +void +dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(acache != NULL); + + LOCK_ZONE(zone); + if (zone->acache != NULL) + dns_acache_detach(&zone->acache); + dns_acache_attach(acache, &zone->acache); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + isc_result_t result; + + /* + * If the zone reuses an existing DB, the DB needs to be + * set in the acache explicitly. We can safely ignore the + * case where the DB is already set. If other error happens, + * the acache will not work effectively. + */ + result = dns_acache_setdb(acache, zone->db); + if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_acache_setdb() failed: %s", + isc_result_totext(result)); + } + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + UNLOCK_ZONE(zone); +} static isc_result_t dns_zone_setstring(dns_zone_t *zone, char **field, const char *value) { @@ -844,14 +968,22 @@ dns_zone_setstring(dns_zone_t *zone, char **field, const char *value) { isc_result_t dns_zone_setfile(dns_zone_t *zone, const char *file) { + return (dns_zone_setfile2(zone, file, dns_masterformat_text)); +} + +isc_result_t +dns_zone_setfile2(dns_zone_t *zone, const char *file, + dns_masterformat_t format) { isc_result_t result = ISC_R_SUCCESS; REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); result = dns_zone_setstring(zone, &zone->masterfile, file); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { + zone->masterformat = format; result = default_journal(zone); + } UNLOCK_ZONE(zone); return (result); @@ -979,31 +1111,42 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } + + /* + * Store the current time before the zone is loaded, so that if the + * file changes between the time of the load and the time that + * zone->loadtime is set, then the file will still be reloaded + * the next time dns_zone_load is called. + */ + TIME_NOW(&loadtime); + /* * Don't do the load if the file that stores the zone is older * than the last time the zone was loaded. If the zone has not * been loaded yet, zone->loadtime will be the epoch. */ - if (zone->masterfile != NULL && ! isc_time_isepoch(&zone->loadtime)) { + if (zone->masterfile != NULL) { /* * The file is already loaded. If we are just doing a * "rndc reconfig", we are done. */ - if ((flags & DNS_ZONELOADFLAG_NOSTAT) != 0) { + if (!isc_time_isepoch(&zone->loadtime) && + (flags & DNS_ZONELOADFLAG_NOSTAT) != 0) { result = ISC_R_SUCCESS; goto cleanup; } - if (! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE)) { - result = isc_file_getmodtime(zone->masterfile, - &filetime); - if (result == ISC_R_SUCCESS && + + result = isc_file_getmodtime(zone->masterfile, &filetime); + if (result == ISC_R_SUCCESS) { + if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE) && isc_time_compare(&filetime, &zone->loadtime) <= 0) { dns_zone_log(zone, ISC_LOG_DEBUG(1), - "skipping load: master file older " - "than last load"); + "skipping load: master file " + "older than last load"); result = DNS_R_UPTODATE; goto cleanup; } + loadtime = filetime; } } @@ -1024,9 +1167,10 @@ zone_load(dns_zone_t *zone, unsigned int flags) { strcmp(zone->db_argv[0], "rbt64") == 0)) { if (zone->masterfile == NULL || !isc_file_exists(zone->masterfile)) { - if (zone->masterfile != NULL) + if (zone->masterfile != NULL) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "no master file"); + } zone->refreshtime = now; if (zone->task != NULL) zone_settimer(zone, &now); @@ -1037,14 +1181,6 @@ zone_load(dns_zone_t *zone, unsigned int flags) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "starting load"); - /* - * Store the current time before the zone is loaded, so that if the - * file changes between the time of the load and the time that - * zone->loadtime is set, then the file will still be reloaded - * the next time dns_zone_load is called. - */ - TIME_NOW(&loadtime); - result = dns_db_create(zone->mctx, zone->db_argv[0], &zone->origin, (zone->type == dns_zone_stub) ? dns_dbtype_stub : dns_dbtype_zone, @@ -1125,14 +1261,21 @@ zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { options |= DNS_MASTER_CHECKNAMES; if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKNAMESFAIL)) options |= DNS_MASTER_CHECKNAMESFAIL; - result = dns_master_loadfileinc(load->zone->masterfile, - dns_db_origin(load->db), - dns_db_origin(load->db), - load->zone->rdclass, - options, - &load->callbacks, task, - zone_loaddone, load, - &load->zone->lctx, load->zone->mctx); + if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKMX)) + options |= DNS_MASTER_CHECKMX; + if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKMXFAIL)) + options |= DNS_MASTER_CHECKMXFAIL; + if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKWILDCARD)) + options |= DNS_MASTER_CHECKWILDCARD; + result = dns_master_loadfileinc2(load->zone->masterfile, + dns_db_origin(load->db), + dns_db_origin(load->db), + load->zone->rdclass, + options, + &load->callbacks, task, + zone_loaddone, load, + &load->zone->lctx, load->zone->mctx, + load->zone->masterformat); if (result != ISC_R_SUCCESS && result != DNS_R_CONTINUE && result != DNS_R_SEENINCLUDE) goto fail; @@ -1160,12 +1303,14 @@ zone_gotwritehandle(isc_task_t *task, isc_event_t *event) { goto fail; LOCK_ZONE(zone); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); dns_db_currentversion(zone->db, &version); - result = dns_master_dumpinc(zone->mctx, zone->db, version, - &dns_master_style_default, - zone->masterfile, zone->task, - dump_done, zone, &zone->dctx); + result = dns_master_dumpinc2(zone->mctx, zone->db, version, + &dns_master_style_default, + zone->masterfile, zone->task, dump_done, + zone, &zone->dctx, zone->masterformat); dns_db_closeversion(zone->db, &version, ISC_FALSE); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); UNLOCK_ZONE(zone); if (result != DNS_R_CONTINUE) goto fail; @@ -1195,6 +1340,12 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { options |= DNS_MASTER_CHECKNAMES; if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL)) options |= DNS_MASTER_CHECKNAMESFAIL; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMX)) + options |= DNS_MASTER_CHECKMX; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL)) + options |= DNS_MASTER_CHECKMXFAIL; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKWILDCARD)) + options |= DNS_MASTER_CHECKWILDCARD; if (zone->zmgr != NULL && zone->db != NULL && zone->task != NULL) { load = isc_mem_get(zone->mctx, sizeof(*load)); @@ -1236,9 +1387,10 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { &callbacks.add_private); if (result != ISC_R_SUCCESS) return (result); - result = dns_master_loadfile(zone->masterfile, &zone->origin, - &zone->origin, zone->rdclass, - options, &callbacks, zone->mctx); + result = dns_master_loadfile2(zone->masterfile, &zone->origin, + &zone->origin, zone->rdclass, + options, &callbacks, zone->mctx, + zone->masterformat); tresult = dns_db_endload(db, &callbacks.add_private); if (result == ISC_R_SUCCESS) result = tresult; @@ -1255,12 +1407,487 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { return (result); } +static isc_boolean_t +zone_check_mx(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, + dns_name_t *owner) +{ + isc_result_t result; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixed; + dns_name_t *foundname; + int level; + + /* + * Outside of zone. + */ + if (!dns_name_issubdomain(name, &zone->origin)) { + if (zone->checkmx != NULL) + return ((zone->checkmx)(zone, name, owner)); + return (ISC_TRUE); + } + + if (zone->type == dns_zone_master) + level = ISC_LOG_ERROR; + else + level = ISC_LOG_WARNING; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + + result = dns_db_find(db, name, NULL, dns_rdatatype_a, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + + if (result == DNS_R_NXRRSET) { + result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + } + + dns_name_format(owner, ownerbuf, sizeof ownerbuf); + dns_name_format(name, namebuf, sizeof namebuf); + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME) { + dns_zone_log(zone, level, + "%s/MX '%s' has no address records (A or AAAA)", + ownerbuf, namebuf); + /* XXX950 make fatal for 9.5.0. */ + return (ISC_TRUE); + } + + if (result == DNS_R_CNAME) { + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNMXCNAME) || + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME)) + level = ISC_LOG_WARNING; + if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME)) + dns_zone_log(zone, level, + "%s/MX '%s' is a CNAME (illegal)", + ownerbuf, namebuf); + return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE); + } + + if (result == DNS_R_DNAME) { + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNMXCNAME) || + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME)) + level = ISC_LOG_WARNING; + if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME)) { + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, "%s/MX '%s' is below a DNAME" + " '%s' (illegal)", ownerbuf, namebuf, + altbuf); + } + return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE); + } + + if (zone->checkmx != NULL && result == DNS_R_DELEGATION) + return ((zone->checkmx)(zone, name, owner)); + + return (ISC_TRUE); +} + +static isc_boolean_t +zone_check_srv(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, + dns_name_t *owner) +{ + isc_result_t result; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixed; + dns_name_t *foundname; + int level; + + /* + * "." means the services does not exist. + */ + if (dns_name_equal(name, dns_rootname)) + return (ISC_TRUE); + + /* + * Outside of zone. + */ + if (!dns_name_issubdomain(name, &zone->origin)) { + if (zone->checksrv != NULL) + return ((zone->checksrv)(zone, name, owner)); + return (ISC_TRUE); + } + + if (zone->type == dns_zone_master) + level = ISC_LOG_ERROR; + else + level = ISC_LOG_WARNING; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + + result = dns_db_find(db, name, NULL, dns_rdatatype_a, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + + if (result == DNS_R_NXRRSET) { + result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + } + + dns_name_format(owner, ownerbuf, sizeof ownerbuf); + dns_name_format(name, namebuf, sizeof namebuf); + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME) { + dns_zone_log(zone, level, + "%s/SRV '%s' has no address records (A or AAAA)", + ownerbuf, namebuf); + /* XXX950 make fatal for 9.5.0. */ + return (ISC_TRUE); + } + + if (result == DNS_R_CNAME) { + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNSRVCNAME) || + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME)) + level = ISC_LOG_WARNING; + if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME)) + dns_zone_log(zone, level, + "%s/SRV '%s' is a CNAME (illegal)", + ownerbuf, namebuf); + return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE); + } + + if (result == DNS_R_DNAME) { + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNSRVCNAME) || + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME)) + level = ISC_LOG_WARNING; + if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME)) { + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, "%s/SRV '%s' is below a " + "DNAME '%s' (illegal)", ownerbuf, namebuf, + altbuf); + } + return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE); + } + + if (zone->checksrv != NULL && result == DNS_R_DELEGATION) + return ((zone->checksrv)(zone, name, owner)); + + return (ISC_TRUE); +} + +static isc_boolean_t +zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, + dns_name_t *owner) +{ + isc_boolean_t answer = ISC_TRUE; + isc_result_t result, tresult; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixed; + dns_name_t *foundname; + dns_rdataset_t a; + dns_rdataset_t aaaa; + int level; + + /* + * Outside of zone. + */ + if (!dns_name_issubdomain(name, &zone->origin)) { + if (zone->checkns != NULL) + return ((zone->checkns)(zone, name, owner, NULL, NULL)); + return (ISC_TRUE); + } + + if (zone->type == dns_zone_master) + level = ISC_LOG_ERROR; + else + level = ISC_LOG_WARNING; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + dns_rdataset_init(&a); + dns_rdataset_init(&aaaa); + + result = dns_db_find(db, name, NULL, dns_rdatatype_a, + DNS_DBFIND_GLUEOK, 0, NULL, + foundname, &a, NULL); + + if (result == ISC_R_SUCCESS) { + dns_rdataset_disassociate(&a); + return (ISC_TRUE); + } else if (result == DNS_R_DELEGATION) + dns_rdataset_disassociate(&a); + + if (result == DNS_R_NXRRSET || result == DNS_R_DELEGATION || + result == DNS_R_GLUE) { + tresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa, + DNS_DBFIND_GLUEOK, 0, NULL, + foundname, &aaaa, NULL); + if (tresult == ISC_R_SUCCESS) { + dns_rdataset_disassociate(&aaaa); + return (ISC_TRUE); + } + if (tresult == DNS_R_DELEGATION) + dns_rdataset_disassociate(&aaaa); + if (result == DNS_R_GLUE || tresult == DNS_R_GLUE) { + /* + * Check glue against child zone. + */ + if (zone->checkns != NULL) + answer = (zone->checkns)(zone, name, owner, + &a, &aaaa); + if (dns_rdataset_isassociated(&a)) + dns_rdataset_disassociate(&a); + if (dns_rdataset_isassociated(&aaaa)) + dns_rdataset_disassociate(&aaaa); + return (answer); + } + } else + tresult = result; + + dns_name_format(owner, ownerbuf, sizeof ownerbuf); + dns_name_format(name, namebuf, sizeof namebuf); + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME || result == DNS_R_DELEGATION) { + const char *what; + if (dns_name_issubdomain(name, owner)) + what = "REQUIRED GLUE "; + else if (result == DNS_R_DELEGATION) + what = "SIBLING GLUE "; + else + what = ""; + + if (result != DNS_R_DELEGATION || + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKSIBLING)) { + dns_zone_log(zone, level, "%s/NS '%s' has no %s" + "address records (A or AAAA)", + ownerbuf, namebuf, what); + /* + * Log missing address record. + */ + if (result == DNS_R_DELEGATION && zone->checkns != NULL) + (void)(zone->checkns)(zone, name, owner, + &a, &aaaa); + /* XXX950 make fatal for 9.5.0. */ + /* answer = ISC_FALSE; */ + } + } else if (result == DNS_R_CNAME) { + dns_zone_log(zone, level, "%s/NS '%s' is a CNAME (illegal)", + ownerbuf, namebuf); + /* XXX950 make fatal for 9.5.0. */ + /* answer = ISC_FALSE; */ + } else if (result == DNS_R_DNAME) { + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, + "%s/NS '%s' is below a DNAME '%s' (illegal)", + ownerbuf, namebuf, altbuf); + /* XXX950 make fatal for 9.5.0. */ + /* answer = ISC_FALSE; */ + } + + if (dns_rdataset_isassociated(&a)) + dns_rdataset_disassociate(&a); + if (dns_rdataset_isassociated(&aaaa)) + dns_rdataset_disassociate(&aaaa); + return (answer); +} + +static isc_boolean_t +integrity_checks(dns_zone_t *zone, dns_db_t *db) { + dns_dbiterator_t *dbiterator = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_fixedname_t fixed; + dns_fixedname_t fixedbottom; + dns_rdata_mx_t mx; + dns_rdata_ns_t ns; + dns_rdata_in_srv_t srv; + dns_rdata_t rdata; + dns_name_t *name; + dns_name_t *bottom; + isc_result_t result; + isc_boolean_t ok = ISC_TRUE; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + dns_fixedname_init(&fixedbottom); + bottom = dns_fixedname_name(&fixedbottom); + dns_rdataset_init(&rdataset); + dns_rdata_init(&rdata); + + result = dns_db_createiterator(db, ISC_FALSE, &dbiterator); + if (result != ISC_R_SUCCESS) + return (ISC_TRUE); + + result = dns_dbiterator_first(dbiterator); + while (result == ISC_R_SUCCESS) { + result = dns_dbiterator_current(dbiterator, &node, name); + if (result != ISC_R_SUCCESS) + goto cleanup; + + /* + * Is this name visible in the zone? + */ + if (!dns_name_issubdomain(name, &zone->origin) || + (dns_name_countlabels(bottom) > 0 && + dns_name_issubdomain(name, bottom))) + goto next; + + /* + * Don't check the NS records at the origin. + */ + if (dns_name_equal(name, &zone->origin)) + goto checkmx; + + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ns, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto checkmx; + /* + * Remember bottom of zone. + */ + dns_name_copy(name, bottom, NULL); + + result = dns_rdataset_first(&rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &ns, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (!zone_check_glue(zone, db, &ns.name, name)) + ok = ISC_FALSE; + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + dns_rdataset_disassociate(&rdataset); + + checkmx: + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_mx, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto checksrv; + result = dns_rdataset_first(&rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &mx, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (!zone_check_mx(zone, db, &mx.mx, name)) + ok = ISC_FALSE; + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + dns_rdataset_disassociate(&rdataset); + + checksrv: + if (zone->rdclass != dns_rdataclass_in) + goto next; + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_srv, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto next; + result = dns_rdataset_first(&rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &srv, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (!zone_check_srv(zone, db, &srv.target, name)) + ok = ISC_FALSE; + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + dns_rdataset_disassociate(&rdataset); + + next: + dns_db_detachnode(db, &node); + result = dns_dbiterator_next(dbiterator); + } + + cleanup: + if (node != NULL) + dns_db_detachnode(db, &node); + dns_dbiterator_destroy(&dbiterator); + + return (ok); +} + +/* + * OpenSSL verification of RSA keys with exponent 3 is known to be + * broken prior OpenSSL 0.9.8c/0.9.7k. Look for such keys and warn + * if they are in use. + */ +static void +zone_check_dnskeys(dns_zone_t *zone, dns_db_t *db) { + dns_dbnode_t *node = NULL; + dns_dbversion_t *version = NULL; + dns_rdata_dnskey_t dnskey; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t rdataset; + isc_result_t result; + isc_boolean_t logit, foundrsa = ISC_FALSE, foundmd5 = ISC_FALSE; + const char *algorithm; + + result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) + goto cleanup; + + dns_db_currentversion(db, &version); + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey, + dns_rdatatype_none, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto cleanup; + + 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, &dnskey, NULL); + INSIST(result == ISC_R_SUCCESS); + + if ((dnskey.algorithm == DST_ALG_RSASHA1 || + dnskey.algorithm == DST_ALG_RSAMD5) && + dnskey.datalen > 1 && dnskey.data[0] == 1 && + dnskey.data[1] == 3) + { + if (dnskey.algorithm == DST_ALG_RSASHA1) { + logit = !foundrsa; + foundrsa = ISC_TRUE; + algorithm = "RSASHA1"; + } else { + logit = !foundmd5; + foundmd5 = ISC_TRUE; + algorithm = "RSAMD5"; + } + if (logit) + dns_zone_log(zone, ISC_LOG_WARNING, + "weak %s (%u) key found " + "(exponent=3)", algorithm, + dnskey.algorithm); + if (foundrsa && foundmd5) + break; + } + dns_rdata_reset(&rdata); + } + dns_rdataset_disassociate(&rdataset); + + cleanup: + if (node != NULL) + dns_db_detachnode(db, &node); + if (version != NULL) + dns_db_closeversion(db, &version, ISC_FALSE); + +} + static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, isc_result_t result) { unsigned int soacount = 0; unsigned int nscount = 0; + unsigned int errors = 0; isc_uint32_t serial, refresh, retry, expire, minimum; isc_time_t now; isc_boolean_t needdump = ISC_FALSE; @@ -1281,12 +1908,13 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "no master file"); else if (result != DNS_R_NOMASTERFILE) dns_zone_log(zone, ISC_LOG_ERROR, - "loading master file %s: %s", + "loading from master file %s " + "failed: %s", zone->masterfile, dns_result_totext(result)); } else dns_zone_log(zone, ISC_LOG_ERROR, - "loading master file %s: %s", + "loading from master file %s failed: %s", zone->masterfile, dns_result_totext(result)); goto cleanup; @@ -1337,14 +1965,12 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded"); /* - * Obtain ns and soa counts for top of zone. + * Obtain ns, soa and cname counts for top of zone. */ - nscount = 0; - soacount = 0; INSIST(db != NULL); - result = zone_get_from_db(db, &zone->origin, &nscount, - &soacount, &serial, &refresh, &retry, - &expire, &minimum); + result = zone_get_from_db(zone, db, &nscount, &soacount, &serial, + &refresh, &retry, &expire, &minimum, + &errors); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "could not find NS and/or SOA records"); @@ -1371,6 +1997,17 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } if (result != ISC_R_SUCCESS) goto cleanup; + if (zone->type == dns_zone_master && errors != 0) { + result = DNS_R_BADZONE; + goto cleanup; + } + if (zone->type == dns_zone_master && + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKINTEGRITY) && + !integrity_checks(zone, db)) { + result = DNS_R_BADZONE; + goto cleanup; + } + if (zone->db != NULL) { /* * This is checked in zone_replacedb() for slave zones @@ -1397,7 +2034,9 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "zone serial has gone backwards"); else if (serial == zone->serial && !hasinclude) dns_zone_log(zone, ISC_LOG_ERROR, - "zone serial unchanged"); + "zone serial unchanged. " + "zone may fail to transfer " + "to slaves."); } zone->serial = serial; zone->refresh = RANGE(refresh, @@ -1440,6 +2079,11 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, goto cleanup; } + /* + * Check for weak DNSKEY's. + */ + if (zone->type == dns_zone_master) + zone_check_dnskeys(zone, db); #if 0 /* destroy notification example. */ @@ -1453,12 +2097,15 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } #endif + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write); if (zone->db != NULL) { result = zone_replacedb(zone, db, ISC_FALSE); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); if (result != ISC_R_SUCCESS) goto cleanup; } else { - dns_db_attach(db, &zone->db); + zone_attachdb(zone, db); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY); } @@ -1509,36 +2156,111 @@ exit_check(dns_zone_t *zone) { return (ISC_FALSE); } +static isc_boolean_t +zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name) { + isc_result_t result; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixed; + dns_name_t *foundname; + int level; + + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOCHECKNS)) + return (ISC_TRUE); + + if (zone->type == dns_zone_master) + level = ISC_LOG_ERROR; + else + level = ISC_LOG_WARNING; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + + result = dns_db_find(db, name, NULL, dns_rdatatype_a, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + + if (result == DNS_R_NXRRSET) { + result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + } + + dns_name_format(name, namebuf, sizeof namebuf); + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME) { + dns_zone_log(zone, level, + "NS '%s' has no address records (A or AAAA)", + namebuf); + /* XXX950 Make fatal ISC_FALSE for 9.5.0. */ + return (ISC_TRUE); + } + + if (result == DNS_R_CNAME) { + dns_zone_log(zone, level, "NS '%s' is a CNAME (illegal)", + namebuf); + /* XXX950 Make fatal ISC_FALSE for 9.5.0. */ + return (ISC_TRUE); + } + + if (result == DNS_R_DNAME) { + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, + "NS '%s' is below a DNAME '%s' (illegal)", + namebuf, altbuf); + /* XXX950 Make fatal ISC_FALSE for 9.5.0. */ + return (ISC_TRUE); + } + + return (ISC_TRUE); +} + static isc_result_t -zone_count_ns_rr(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - unsigned int *nscount) +zone_count_ns_rr(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, + dns_dbversion_t *version, unsigned int *nscount, + unsigned int *errors) { isc_result_t result; - unsigned int count; + unsigned int count = 0; + unsigned int ecount = 0; dns_rdataset_t rdataset; - - REQUIRE(nscount != NULL); + dns_rdata_t rdata; + dns_rdata_ns_t ns; dns_rdataset_init(&rdataset); result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns, dns_rdatatype_none, 0, &rdataset, NULL); - if (result == ISC_R_NOTFOUND) { - *nscount = 0; - result = ISC_R_SUCCESS; - goto invalidate_rdataset; - } + if (result == ISC_R_NOTFOUND) + goto success; if (result != ISC_R_SUCCESS) goto invalidate_rdataset; - count = 0; result = dns_rdataset_first(&rdataset); while (result == ISC_R_SUCCESS) { + if (errors != NULL && zone->rdclass == dns_rdataclass_in && + (zone->type == dns_zone_master || + zone->type == dns_zone_slave)) { + dns_rdata_init(&rdata); + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &ns, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (dns_name_issubdomain(&ns.name, &zone->origin) && + !zone_check_ns(zone, db, &ns.name)) + ecount++; + } count++; result = dns_rdataset_next(&rdataset); } dns_rdataset_disassociate(&rdataset); - *nscount = count; + success: + if (nscount != NULL) + *nscount = count; + if (errors != NULL) + *errors = ecount; + result = ISC_R_SUCCESS; invalidate_rdataset: @@ -1626,10 +2348,11 @@ zone_load_soa_rr(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * zone must be locked. */ static isc_result_t -zone_get_from_db(dns_db_t *db, dns_name_t *origin, unsigned int *nscount, +zone_get_from_db(dns_zone_t *zone, dns_db_t *db, unsigned int *nscount, unsigned int *soacount, isc_uint32_t *serial, isc_uint32_t *refresh, isc_uint32_t *retry, - isc_uint32_t *expire, isc_uint32_t *minimum) + isc_uint32_t *expire, isc_uint32_t *minimum, + unsigned int *errors) { dns_dbversion_t *version; isc_result_t result; @@ -1637,20 +2360,21 @@ zone_get_from_db(dns_db_t *db, dns_name_t *origin, unsigned int *nscount, dns_dbnode_t *node; REQUIRE(db != NULL); - REQUIRE(origin != NULL); + REQUIRE(zone != NULL); version = NULL; dns_db_currentversion(db, &version); node = NULL; - result = dns_db_findnode(db, origin, ISC_FALSE, &node); + result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node); if (result != ISC_R_SUCCESS) { answer = result; goto closeversion; } - if (nscount != NULL) { - result = zone_count_ns_rr(db, node, version, nscount); + if (nscount != NULL || errors != NULL) { + result = zone_count_ns_rr(zone, db, node, version, + nscount, errors); if (result != ISC_R_SUCCESS) answer = result; } @@ -1979,6 +2703,37 @@ dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters, return (result); } +static isc_boolean_t +same_masters(const isc_sockaddr_t *old, const isc_sockaddr_t *new, + isc_uint32_t count) +{ + unsigned int i; + + for (i = 0; i < count; i++) + if (!isc_sockaddr_equal(&old[i], &new[i])) + return (ISC_FALSE); + return (ISC_TRUE); +} + +static isc_boolean_t +same_keynames(dns_name_t **old, dns_name_t **new, isc_uint32_t count) { + unsigned int i; + + if (old == NULL && new == NULL) + return (ISC_TRUE); + if (old == NULL || new == NULL) + return (ISC_FALSE); + + for (i = 0; i < count; i++) { + if (old[i] == NULL && new[i] == NULL) + continue; + if (old[i] == NULL || new[i] == NULL || + !dns_name_equal(old[i], new[i])) + return (ISC_FALSE); + } + return (ISC_TRUE); +} + isc_result_t dns_zone_setmasterswithkeys(dns_zone_t *zone, const isc_sockaddr_t *masters, @@ -1998,6 +2753,19 @@ dns_zone_setmasterswithkeys(dns_zone_t *zone, } LOCK_ZONE(zone); + /* + * The refresh code assumes that 'masters' wouldn't change under it. + * If it will change then kill off any current refresh in progress + * and update the masters info. If it won't change then we can just + * unlock and exit. + */ + if (count != zone->masterscnt || + !same_masters(zone->masters, masters, count) || + !same_keynames(zone->masterkeynames, keynames, count)) { + if (zone->request != NULL) + dns_request_cancel(zone->request); + } else + goto unlock; if (zone->masters != NULL) { isc_mem_put(zone->mctx, zone->masters, zone->masterscnt * sizeof(*new)); @@ -2115,12 +2883,12 @@ dns_zone_getdb(dns_zone_t *zone, dns_db_t **dpb) { REQUIRE(DNS_ZONE_VALID(zone)); - LOCK_ZONE(zone); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); if (zone->db == NULL) result = DNS_R_NOTLOADED; else dns_db_attach(zone->db, dpb); - UNLOCK_ZONE(zone); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); return (result); } @@ -2245,8 +3013,9 @@ zone_maintenance(dns_zone_t *zone) { switch (zone->type) { case dns_zone_master: case dns_zone_slave: - if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) - zone_notify(zone); + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) && + isc_time_compare(&now, &zone->notifytime) >= 0) + zone_notify(zone, &now); break; default: break; @@ -2498,6 +3267,7 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { isc_boolean_t again; dns_db_t *db = NULL; char *masterfile = NULL; + dns_masterformat_t masterformat = dns_masterformat_none; /* * 'compact' MUST only be set if we are task locked. @@ -2507,11 +3277,15 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { ENTER; redo: - LOCK_ZONE(zone); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); if (zone->db != NULL) dns_db_attach(zone->db, &db); - if (zone->masterfile != NULL) + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + LOCK_ZONE(zone); + if (zone->masterfile != NULL) { masterfile = isc_mem_strdup(zone->mctx, zone->masterfile); + masterformat = zone->masterformat; + } UNLOCK_ZONE(zone); if (db == NULL) { result = DNS_R_NOTLOADED; @@ -2536,9 +3310,9 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { UNLOCK_ZONE(zone); } else { dns_db_currentversion(db, &version); - result = dns_master_dump(zone->mctx, db, version, - &dns_master_style_default, - masterfile); + result = dns_master_dump2(zone->mctx, db, version, + &dns_master_style_default, + masterfile, masterformat); dns_db_closeversion(db, &version, ISC_FALSE); } fail: @@ -2576,35 +3350,46 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { } static isc_result_t -dumptostream(dns_zone_t *zone, FILE *fd, const dns_master_style_t *style) { +dumptostream(dns_zone_t *zone, FILE *fd, const dns_master_style_t *style, + dns_masterformat_t format) +{ isc_result_t result; dns_dbversion_t *version = NULL; dns_db_t *db = NULL; REQUIRE(DNS_ZONE_VALID(zone)); - LOCK_ZONE(zone); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); if (zone->db != NULL) dns_db_attach(zone->db, &db); - UNLOCK_ZONE(zone); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) return (DNS_R_NOTLOADED); dns_db_currentversion(db, &version); - result = dns_master_dumptostream(zone->mctx, db, version, style, fd); + result = dns_master_dumptostream2(zone->mctx, db, version, style, + format, fd); dns_db_closeversion(db, &version, ISC_FALSE); dns_db_detach(&db); return (result); } isc_result_t +dns_zone_dumptostream2(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, + const dns_master_style_t *style) { + return dumptostream(zone, fd, style, format); +} + +isc_result_t dns_zone_dumptostream(dns_zone_t *zone, FILE *fd) { - return dumptostream(zone, fd, &dns_master_style_default); + return dumptostream(zone, fd, &dns_master_style_default, + dns_masterformat_text); } isc_result_t dns_zone_fulldumptostream(dns_zone_t *zone, FILE *fd) { - return dumptostream(zone, fd, &dns_master_style_full); + return dumptostream(zone, fd, &dns_master_style_full, + dns_masterformat_text); } void @@ -2645,7 +3430,9 @@ zone_unload(dns_zone_t *zone) { REQUIRE(LOCKED_ZONE(zone)); - dns_db_detach(&zone->db); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write); + zone_detachdb(zone); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADED); DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP); } @@ -2700,6 +3487,46 @@ notify_isqueued(dns_zone_t *zone, dns_name_t *name, isc_sockaddr_t *addr) { return (ISC_FALSE); } +static isc_boolean_t +notify_isself(dns_zone_t *zone, isc_sockaddr_t *dst) { + dns_tsigkey_t *key = NULL; + isc_sockaddr_t src; + isc_sockaddr_t any; + isc_boolean_t isself; + isc_netaddr_t dstaddr; + + if (zone->view == NULL || zone->isself == NULL) + return (ISC_FALSE); + + switch (isc_sockaddr_pf(dst)) { + case PF_INET: + src = zone->notifysrc4; + isc_sockaddr_any(&any); + break; + case PF_INET6: + src = zone->notifysrc6; + isc_sockaddr_any6(&any); + break; + default: + return (ISC_FALSE); + } + + /* + * When sending from any the kernel will assign a source address + * that matches the destination address. + */ + if (isc_sockaddr_eqaddr(&any, &src)) + src = *dst; + + isc_netaddr_fromsockaddr(&dstaddr, dst); + (void)dns_view_getpeertsig(zone->view, &dstaddr, &key); + isself = (zone->isself)(zone->view, key, &src, dst, zone->rdclass, + zone->isselfarg); + if (key != NULL) + dns_tsigkey_detach(&key); + return (isself); +} + static void notify_destroy(dns_notify_t *notify, isc_boolean_t locked) { isc_mem_t *mctx; @@ -2800,7 +3627,7 @@ notify_find_address(dns_notify_t *notify) { result = dns_adb_createfind(notify->zone->view->adb, notify->zone->task, process_adb_event, notify, - ¬ify->ns, dns_rootname, + ¬ify->ns, dns_rootname, 0, options, 0, NULL, notify->zone->view->dstport, ¬ify->find); @@ -2853,6 +3680,7 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) { char addrbuf[ISC_SOCKADDR_FORMATSIZE]; isc_sockaddr_t src; int timeout; + isc_boolean_t have_notifysource = ISC_FALSE; notify = event->ev_arg; REQUIRE(DNS_NOTIFY_VALID(notify)); @@ -2880,7 +3708,7 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) { */ if (isc_sockaddr_pf(¬ify->dst) == PF_INET6 && IN6_IS_ADDR_V4MAPPED(¬ify->dst.type.sin6.sin6_addr)) { - isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); + isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); notify_log(notify->zone, ISC_LOG_DEBUG(3), "notify: ignoring IPv6 mapped IPV4 address: %s", addrbuf); @@ -2898,12 +3726,24 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) { isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); notify_log(notify->zone, ISC_LOG_DEBUG(3), "sending notify to %s", addrbuf); + if (notify->zone->view->peers != NULL) { + dns_peer_t *peer = NULL; + result = dns_peerlist_peerbyaddr(notify->zone->view->peers, + &dstip, &peer); + if (result == ISC_R_SUCCESS) { + result = dns_peer_getnotifysource(peer, &src); + if (result == ISC_R_SUCCESS) + have_notifysource = ISC_TRUE; + } + } switch (isc_sockaddr_pf(¬ify->dst)) { case PF_INET: - src = notify->zone->notifysrc4; + if (!have_notifysource) + src = notify->zone->notifysrc4; break; case PF_INET6: - src = notify->zone->notifysrc6; + if (!have_notifysource) + src = notify->zone->notifysrc6; break; default: result = ISC_R_NOTIMPLEMENTED; @@ -2947,6 +3787,8 @@ notify_send(dns_notify_t *notify) { dst = ai->sockaddr; if (notify_isqueued(notify->zone, NULL, &dst)) continue; + if (notify_isself(notify->zone, &dst)) + continue; new = NULL; result = notify_create(notify->mctx, (notify->flags & DNS_NOTIFY_NOSOA), @@ -2982,8 +3824,9 @@ dns_zone_notify(dns_zone_t *zone) { } static void -zone_notify(dns_zone_t *zone) { +zone_notify(dns_zone_t *zone, isc_time_t *now) { dns_dbnode_t *node = NULL; + dns_db_t *zonedb = NULL; dns_dbversion_t *version = NULL; dns_name_t *origin = NULL; dns_name_t master; @@ -3001,13 +3844,13 @@ zone_notify(dns_zone_t *zone) { dns_notifytype_t notifytype; unsigned int flags = 0; isc_boolean_t loggednotify = ISC_FALSE; - dns_db_t *db = NULL; REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); notifytype = zone->notifytype; + DNS_ZONE_TIME_ADD(now, zone->notifydelay, &zone->notifytime); UNLOCK_ZONE(zone); if (! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) @@ -3016,11 +3859,8 @@ zone_notify(dns_zone_t *zone) { if (notifytype == dns_notifytype_no) return; - LOCK_ZONE(zone); - if (zone->db != NULL) - dns_db_attach(zone->db, &db); - UNLOCK_ZONE(zone); - if (db == NULL) + if (notifytype == dns_notifytype_masteronly && + zone->type != dns_zone_master) return; origin = &zone->origin; @@ -3035,13 +3875,19 @@ zone_notify(dns_zone_t *zone) { /* * Get SOA RRset. */ - dns_db_currentversion(db, &version); - result = dns_db_findnode(db, origin, ISC_FALSE, &node); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) + dns_db_attach(zone->db, &zonedb); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (zonedb == NULL) + return; + dns_db_currentversion(zonedb, &version); + result = dns_db_findnode(zonedb, origin, ISC_FALSE, &node); if (result != ISC_R_SUCCESS) goto cleanup1; dns_rdataset_init(&soardset); - result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa, + result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_soa, dns_rdatatype_none, 0, &soardset, NULL); if (result != ISC_R_SUCCESS) goto cleanup2; @@ -3098,7 +3944,7 @@ zone_notify(dns_zone_t *zone) { */ dns_rdataset_init(&nsrdset); - result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns, + result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_ns, dns_rdatatype_none, 0, &nsrdset, NULL); if (result != ISC_R_SUCCESS) goto cleanup3; @@ -3155,10 +4001,10 @@ zone_notify(dns_zone_t *zone) { if (dns_name_dynamic(&master)) dns_name_free(&master, zone->mctx); cleanup2: - dns_db_detachnode(db, &node); + dns_db_detachnode(zonedb, &node); cleanup1: - dns_db_closeversion(db, &version, ISC_FALSE); - dns_db_detach(&db); + dns_db_closeversion(zonedb, &version, ISC_FALSE); + dns_db_detach(&zonedb); } /*** @@ -3406,10 +4252,10 @@ stub_callback(isc_task_t *task, isc_event_t *event) { * Tidy up. */ dns_db_closeversion(stub->db, &stub->version, ISC_TRUE); - LOCK_ZONE(zone); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write); if (zone->db == NULL) - dns_db_attach(stub->db, &zone->db); - UNLOCK_ZONE(zone); + zone_attachdb(zone, stub->db); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); dns_db_detach(&stub->db); if (zone->masterfile != NULL) { @@ -3955,7 +4801,7 @@ create_query(dns_zone_t *zone, dns_rdatatype_t rdtype, } static isc_result_t -add_opt(dns_message_t *message) { +add_opt(dns_message_t *message, isc_uint16_t udpsize) { dns_rdataset_t *rdataset = NULL; dns_rdatalist_t *rdatalist = NULL; dns_rdata_t *rdata = NULL; @@ -3978,7 +4824,7 @@ add_opt(dns_message_t *message) { /* * Set Maximum UDP buffer size. */ - rdatalist->rdclass = SEND_BUFFER_SIZE; + rdatalist->rdclass = udpsize; /* * Set EXTENDED-RCODE, VERSION, DO and Z to 0. @@ -4025,6 +4871,7 @@ soa_query(isc_task_t *task, isc_event_t *event) { isc_boolean_t cancel = ISC_TRUE; int timeout; isc_boolean_t have_xfrsource; + isc_uint16_t udpsize = SEND_BUFFER_SIZE; REQUIRE(DNS_ZONE_VALID(zone)); @@ -4068,7 +4915,7 @@ soa_query(isc_task_t *task, isc_event_t *event) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(keyname, namebuf, sizeof(namebuf)); dns_zone_log(zone, ISC_LOG_ERROR, - "unable to find key: %s", namebuf); + "unable to find key: %s", namebuf); } } if (key == NULL) @@ -4088,6 +4935,10 @@ soa_query(isc_task_t *task, isc_event_t *event) { &zone->sourceaddr); if (result == ISC_R_SUCCESS) have_xfrsource = ISC_TRUE; + if (zone->view->resolver != NULL) + udpsize = + dns_resolver_getudpsize(zone->view->resolver); + (void)dns_peer_getudpsize(peer, &udpsize); } } @@ -4119,7 +4970,7 @@ soa_query(isc_task_t *task, isc_event_t *event) { DNS_REQUESTOPT_TCP : 0; if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) { - result = add_opt(message); + result = add_opt(message, udpsize); if (result != ISC_R_SUCCESS) zone_debuglog(zone, me, 1, "unable to add opt record: %s", @@ -4184,6 +5035,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { dns_dbnode_t *node = NULL; int timeout; isc_boolean_t have_xfrsource = ISC_FALSE; + isc_uint16_t udpsize = SEND_BUFFER_SIZE; REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE((soardataset != NULL && stub == NULL) || @@ -4213,9 +5065,13 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { * new one and attach it to the zone once we have the NS * RRset and glue. */ - if (zone->db != NULL) + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { dns_db_attach(zone->db, &stub->db); - else { + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + } else { + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + INSIST(zone->db_argc >= 1); result = dns_db_create(zone->mctx, zone->db_argv[0], &zone->origin, dns_dbtype_stub, @@ -4284,7 +5140,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(keyname, namebuf, sizeof(namebuf)); dns_zone_log(zone, ISC_LOG_ERROR, - "unable to find key: %s", namebuf); + "unable to find key: %s", namebuf); } } if (key == NULL) @@ -4303,11 +5159,15 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { &zone->sourceaddr); if (result == ISC_R_SUCCESS) have_xfrsource = ISC_TRUE; + if (zone->view->resolver != NULL) + udpsize = + dns_resolver_getudpsize(zone->view->resolver); + (void)dns_peer_getudpsize(peer, &udpsize); } } if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) { - result = add_opt(message); + result = add_opt(message, udpsize); if (result != ISC_R_SUCCESS) zone_debuglog(zone, me, 1, "unable to add opt record: %s", @@ -4367,7 +5227,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { if (message != NULL) dns_message_destroy(&message); unlock: - if (key != NULL) + if (key != NULL) dns_tsigkey_detach(&key); UNLOCK_ZONE(zone); return; @@ -4495,7 +5355,7 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { switch (zone->type) { case dns_zone_master: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) - next = *now; + next = zone->notifytime; if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) && !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) { INSIST(!isc_time_isepoch(&zone->dumptime)); @@ -4507,7 +5367,7 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { case dns_zone_slave: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) - next = *now; + next = zone->notifytime; /*FALLTHROUGH*/ case dns_zone_stub: @@ -4582,6 +5442,7 @@ static isc_result_t notify_createmessage(dns_zone_t *zone, unsigned int flags, dns_message_t **messagep) { + dns_db_t *zonedb = NULL; dns_dbnode_t *node = NULL; dns_dbversion_t *version = NULL; dns_message_t *message = NULL; @@ -4647,15 +5508,20 @@ notify_createmessage(dns_zone_t *zone, unsigned int flags, if (result != ISC_R_SUCCESS) goto soa_cleanup; + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + INSIST(zone->db != NULL); /* XXXJT: is this assumption correct? */ + dns_db_attach(zone->db, &zonedb); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + dns_name_init(tempname, NULL); dns_name_clone(&zone->origin, tempname); - dns_db_currentversion(zone->db, &version); - result = dns_db_findnode(zone->db, tempname, ISC_FALSE, &node); + dns_db_currentversion(zonedb, &version); + result = dns_db_findnode(zonedb, tempname, ISC_FALSE, &node); if (result != ISC_R_SUCCESS) goto soa_cleanup; dns_rdataset_init(&rdataset); - result = dns_db_findrdataset(zone->db, node, version, + result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_soa, dns_rdatatype_none, 0, &rdataset, NULL); @@ -4699,9 +5565,11 @@ notify_createmessage(dns_zone_t *zone, unsigned int flags, soa_cleanup: if (node != NULL) - dns_db_detachnode(zone->db, &node); + dns_db_detachnode(zonedb, &node); if (version != NULL) - dns_db_closeversion(zone->db, &version, ISC_FALSE); + dns_db_closeversion(zonedb, &version, ISC_FALSE); + if (zonedb != NULL) + dns_db_detach(&zonedb); if (tempname != NULL) dns_message_puttempname(message, &tempname); if (temprdata != NULL) @@ -4744,7 +5612,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, * If type != T_SOA return DNS_R_REFUSED. We don't yet support * ROLLOVER. * - * SOA: RFC 1996 + * SOA: RFC1996 * Check that 'from' is a valid notify source, (zone->masters). * Return DNS_R_REFUSED if not. * @@ -5046,6 +5914,19 @@ dns_zone_setupdatedisabled(dns_zone_t *zone, isc_boolean_t state) { zone->update_disabled = state; } +isc_boolean_t +dns_zone_getzeronosoattl(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->zero_no_soa_ttl); + +} + +void +dns_zone_setzeronosoattl(dns_zone_t *zone, isc_boolean_t state) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->zero_no_soa_ttl = state; +} + void dns_zone_setchecknames(dns_zone_t *zone, dns_severity_t severity) { @@ -5244,7 +6125,8 @@ dns_zone_getmaxxfrout(dns_zone_t *zone) { return (zone->maxxfrout); } -dns_zonetype_t dns_zone_gettype(dns_zone_t *zone) { +dns_zonetype_t +dns_zone_gettype(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); return (zone->type); @@ -5265,8 +6147,10 @@ dns_zone_settask(dns_zone_t *zone, isc_task_t *task) { if (zone->task != NULL) isc_task_detach(&zone->task); isc_task_attach(task, &zone->task); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); if (zone->db != NULL) dns_db_settask(zone->db, zone->task); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); UNLOCK_ZONE(zone); } @@ -5371,7 +6255,9 @@ dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write); result = zone_replacedb(zone, db, dump); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); UNLOCK_ZONE(zone); return (result); } @@ -5384,13 +6270,13 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { unsigned int nscount = 0; /* - * 'zone' locked by caller. + * 'zone' and 'zonedb' locked by caller. */ REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(LOCKED_ZONE(zone)); - result = zone_get_from_db(db, &zone->origin, &nscount, &soacount, - NULL, NULL, NULL, NULL, NULL); + result = zone_get_from_db(zone, db, &nscount, &soacount, + NULL, NULL, NULL, NULL, NULL, NULL); if (result == ISC_R_SUCCESS) { if (soacount != 1) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -5478,7 +6364,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3), "dumping new zone version"); - result = dns_db_dump(db, ver, zone->masterfile); + result = dns_db_dump2(db, ver, zone->masterfile, + zone->masterformat); if (result != ISC_R_SUCCESS) goto fail; @@ -5518,8 +6405,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { "replacing zone database"); if (zone->db != NULL) - dns_db_detach(&zone->db); - dns_db_attach(db, &zone->db); + zone_detachdb(zone); + zone_attachdb(zone, db); dns_db_settask(zone->db, zone->task); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY); return (ISC_R_SUCCESS); @@ -5529,6 +6416,33 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { return (result); } +/* The caller must hold the dblock as a writer. */ +static inline void +zone_attachdb(dns_zone_t *zone, dns_db_t *db) { + REQUIRE(zone->db == NULL && db != NULL); + + dns_db_attach(db, &zone->db); + if (zone->acache != NULL) { + isc_result_t result; + result = dns_acache_setdb(zone->acache, db); + if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_acache_setdb() failed: %s", + isc_result_totext(result)); + } + } +} + +/* The caller must hold the dblock as a writer. */ +static inline void +zone_detachdb(dns_zone_t *zone) { + REQUIRE(zone->db != NULL); + + if (zone->acache != NULL) + (void)dns_acache_putdb(zone->acache, zone->db); + dns_db_detach(&zone->db); +} + static void zone_xfrdone(dns_zone_t *zone, isc_result_t result) { isc_time_t now; @@ -5559,8 +6473,11 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { /* * Has the zone expired underneath us? */ - if (zone->db == NULL) + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db == NULL) { + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); goto same_master; + } /* * Update the zone structure's data from the actual @@ -5569,9 +6486,10 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { nscount = 0; soacount = 0; INSIST(zone->db != NULL); - result = zone_get_from_db(zone->db, &zone->origin, &nscount, + result = zone_get_from_db(zone, zone->db, &nscount, &soacount, &serial, &refresh, - &retry, &expire, &minimum); + &retry, &expire, &minimum, NULL); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (result == ISC_R_SUCCESS) { if (soacount != 1) dns_zone_log(zone, ISC_LOG_ERROR, @@ -6217,13 +7135,9 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, ISC_LIST_INIT(zmgr->waiting_for_xfrin); ISC_LIST_INIT(zmgr->xfrin_in_progress); result = isc_rwlock_init(&zmgr->rwlock, 0, 0); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_rwlock_init() failed: %s", - isc_result_totext(result)); - result = ISC_R_UNEXPECTED; + if (result != ISC_R_SUCCESS) goto free_mem; - } + zmgr->transfersin = 10; zmgr->transfersperns = 2; @@ -6254,12 +7168,9 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, ISC_LIST_INIT(zmgr->low); result = isc_mutex_init(&zmgr->iolock); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_mutex_init() failed: %s", - isc_result_totext(result)); + if (result != ISC_R_SUCCESS) goto free_rl; - } + zmgr->magic = ZONEMGR_MAGIC; *zmgrp = zmgr; @@ -6955,6 +7866,7 @@ dns_zone_getkeydirectory(dns_zone_t *zone) { return (zone->keydirectory); } + unsigned int dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state) { dns_zone_t *zone; @@ -7046,3 +7958,47 @@ dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata) { return (ISC_R_SUCCESS); } + +void +dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->checkmx = checkmx; +} + +void +dns_zone_setchecksrv(dns_zone_t *zone, dns_checksrvfunc_t checksrv) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->checksrv = checksrv; +} + +void +dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->checkns = checkns; +} + +void +dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->isself = isself; + zone->isselfarg = arg; + UNLOCK_ZONE(zone); +} + +void +dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->notifydelay = delay; + UNLOCK_ZONE(zone); +} + +isc_uint32_t +dns_zone_getnotifydelay(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (zone->notifydelay); +} |