From 3ff75c89eb7b8c4f8c7dd375beec2981d147c791 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Mon, 5 Oct 2009 19:39:20 +0000 Subject: [PATCH] 2704. [bug] Serial of dynamic and stub zones could be inconsistent with their SOA serial. [RT #19387] --- CHANGES | 3 ++ bin/named/statschannel.c | 10 ++-- lib/dns/include/dns/zone.h | 15 +++++- lib/dns/win32/libdns.def | 1 + lib/dns/zone.c | 102 ++++++++++++++++++++++++------------- 5 files changed, 91 insertions(+), 40 deletions(-) diff --git a/CHANGES b/CHANGES index 72125d4eb7..5082e2b1e6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2704. [bug] Serial of dynamic and stub zones could be inconsistent + with their SOA serial. [RT #19387] + 2703. [func] Introduce an OpenSSL "engine" argument with -E for all binaries which can take benefit of crypto hardware. [RT #20230] diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c index 0540e4e84f..1547c41613 100644 --- a/bin/named/statschannel.c +++ b/bin/named/statschannel.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: statschannel.c,v 1.22 2009/02/17 03:40:28 marka Exp $ */ +/* $Id: statschannel.c,v 1.23 2009/10/05 19:39:20 each Exp $ */ /*! \file */ @@ -678,9 +678,11 @@ zone_xmlrender(dns_zone_t *zone, void *arg) { xmlTextWriterWriteString(writer, ISC_XMLCHAR buf); xmlTextWriterEndElement(writer); - serial = dns_zone_getserial(zone); xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"); - xmlTextWriterWriteFormatString(writer, "%u", serial); + if (dns_zone_getserial2(zone, &serial) == ISC_R_SUCCESS) + xmlTextWriterWriteFormatString(writer, "%u", serial); + else + xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"); xmlTextWriterEndElement(writer); zonestats = dns_zone_getrequeststats(zone); @@ -729,7 +731,7 @@ generatexml(ns_server_t *server, int *buflen, xmlChar **buf) { TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "bind")); TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics")); TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version", - ISC_XMLCHAR "2.1")); + ISC_XMLCHAR "2.2")); /* Set common fields for statistics dump */ dumparg.type = statsformat_xml; diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 5b781d5e33..9be1aabb0f 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.166 2009/07/02 07:39:03 marka Exp $ */ +/* $Id: zone.h,v 1.167 2009/10/05 19:39:20 each Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -150,13 +150,24 @@ dns_zone_getclass(dns_zone_t *zone); *\li 'zone' to be a valid zone. */ +isc_result_t +dns_zone_getserial2(dns_zone_t *zone, isc_uint32_t *serialp); + isc_uint32_t dns_zone_getserial(dns_zone_t *zone); /*%< - * Returns the current serial number of the zone. + * Returns the current serial number of the zone. On success, the SOA + * serial of the zone will be copied into '*serialp'. + * dns_zone_getserial() cannot catch failure cases and is deprecated by + * dns_zone_getserial2(). * * Requires: *\li 'zone' to be a valid zone. + *\li 'serialp' to be non NULL + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #DNS_R_NOTLOADED zone DB is not loaded */ void diff --git a/lib/dns/win32/libdns.def b/lib/dns/win32/libdns.def index 23d4f56285..723a15a32f 100644 --- a/lib/dns/win32/libdns.def +++ b/lib/dns/win32/libdns.def @@ -731,6 +731,7 @@ dns_zone_getprivatetype dns_zone_getqueryacl dns_zone_getrequeststats dns_zone_getserial +dns_zone_getserial2 dns_zone_getsigresigninginterval dns_zone_getsigvalidityinterval dns_zone_getssutable diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 3f5fbdd217..c4580e32b4 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.507 2009/09/22 08:38:14 fdupont Exp $ */ +/* $Id: zone.c,v 1.508 2009/10/05 19:39:20 each Exp $ */ /*! \file */ @@ -207,7 +207,6 @@ struct dns_zone { isc_time_t nsec3chaintime; isc_time_t refreshkeytime; /* Used by key zones */ isc_uint32_t refreshkeycount; - isc_uint32_t serial; isc_uint32_t refresh; isc_uint32_t retry; isc_uint32_t expire; @@ -748,7 +747,6 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { isc_time_settoepoch(&zone->nsec3chaintime); isc_time_settoepoch(&zone->refreshkeytime); zone->refreshkeycount = 0; - zone->serial = 0; zone->refresh = DNS_ZONE_DEFAULTREFRESH; zone->retry = DNS_ZONE_DEFAULTRETRY; zone->expire = 0; @@ -997,16 +995,35 @@ dns_zone_setnotifytype(dns_zone_t *zone, dns_notifytype_t notifytype) { UNLOCK_ZONE(zone); } -isc_uint32_t -dns_zone_getserial(dns_zone_t *zone) { - isc_uint32_t serial; +isc_result_t +dns_zone_getserial2(dns_zone_t *zone, isc_uint32_t *serialp) { + isc_result_t result; REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(serialp != NULL); LOCK_ZONE(zone); - serial = zone->serial; + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + result = zone_get_from_db(zone, zone->db, NULL, NULL, serialp, + NULL, NULL, NULL, NULL, NULL); + } else + result = DNS_R_NOTLOADED; + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); UNLOCK_ZONE(zone); + return (result); +} + +isc_uint32_t +dns_zone_getserial(dns_zone_t *zone) { + isc_result_t result; + isc_uint32_t serial; + + result = dns_zone_getserial2(zone, &serial); + if (result != ISC_R_SUCCESS) + serial = 0; /* XXX: not really correct, but no other choice */ + return (serial); } @@ -3086,7 +3103,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, unsigned int soacount = 0; unsigned int nscount = 0; unsigned int errors = 0; - isc_uint32_t serial, refresh, retry, expire, minimum; + isc_uint32_t serial, oldserial, refresh, retry, expire, minimum; isc_time_t now; isc_boolean_t needdump = ISC_FALSE; isc_boolean_t hasinclude = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE); @@ -3238,14 +3255,18 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, * This is checked in zone_replacedb() for slave zones * as they don't reload from disk. */ + result = zone_get_from_db(zone, zone->db, NULL, NULL, + &oldserial, NULL, NULL, NULL, + NULL, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) && - !isc_serial_gt(serial, zone->serial)) { + !isc_serial_gt(serial, oldserial)) { isc_uint32_t serialmin, serialmax; INSIST(zone->type == dns_zone_master); - serialmin = (zone->serial + 1) & 0xffffffffU; - serialmax = (zone->serial + 0x7fffffffU) & + serialmin = (oldserial + 1) & 0xffffffffU; + serialmax = (oldserial + 0x7fffffffU) & 0xffffffffU; dns_zone_log(zone, ISC_LOG_ERROR, "ixfr-from-differences: " @@ -3254,11 +3275,11 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, serialmax); result = DNS_R_BADZONE; goto cleanup; - } else if (!isc_serial_ge(serial, zone->serial)) + } else if (!isc_serial_ge(serial, oldserial)) dns_zone_log(zone, ISC_LOG_ERROR, "zone serial (%u/%u) has gone " - "backwards", serial, zone->serial); - else if (serial == zone->serial && !hasinclude) + "backwards", serial, oldserial); + else if (serial == oldserial && !hasinclude) dns_zone_log(zone, ISC_LOG_ERROR, "zone serial (%u) unchanged. " "zone may fail to transfer " @@ -3275,7 +3296,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "3 * refresh."); } - zone->serial = serial; zone->refresh = RANGE(refresh, zone->minrefresh, zone->maxrefresh); zone->retry = RANGE(retry, @@ -3311,7 +3331,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, break; case dns_zone_key: - zone->serial = serial; result = sync_keyzone(zone, db); if (result != ISC_R_SUCCESS) goto cleanup; @@ -3374,9 +3393,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } if (! dns_db_ispersistent(db)) - dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s", - zone->serial, - dns_db_issecure(db) ? " (signed)" : ""); + dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s", serial, + dns_db_issecure(db) ? " (DNSSEC signed)" : ""); return (result); @@ -8766,7 +8784,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; + isc_uint32_t serial, oldserial; unsigned int j; zone = revent->ev_arg; @@ -8989,12 +9007,17 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { RUNTIME_CHECK(result == ISC_R_SUCCESS); serial = soa.serial; - - zone_debuglog(zone, me, 1, "serial: new %u, old %u", - serial, zone->serial); + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) { + result = dns_zone_getserial2(zone, &oldserial); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + zone_debuglog(zone, me, 1, "serial: new %u, old %u", + serial, oldserial); + } else + zone_debuglog(zone, me, 1, "serial: new %u, old not loaded", + serial); if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) || DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) || - isc_serial_gt(serial, zone->serial)) { + isc_serial_gt(serial, oldserial)) { if (dns_zonemgr_unreachable(zone->zmgr, &zone->masteraddr, &zone->sourceaddr, &now)) { dns_zone_log(zone, ISC_LOG_INFO, @@ -9018,7 +9041,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { } if (msg != NULL) dns_message_destroy(&msg); - } else if (isc_serial_eq(soa.serial, zone->serial)) { + } else if (isc_serial_eq(soa.serial, oldserial)) { if (zone->masterfile != NULL) { result = ISC_R_FAILURE; if (zone->journal != NULL) @@ -9051,7 +9074,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MULTIMASTER)) dns_zone_log(zone, ISC_LOG_INFO, "serial number (%u) " "received from master %s < ours (%u)", - soa.serial, master, zone->serial); + soa.serial, master, oldserial); else zone_debuglog(zone, me, 1, "ahead"); zone->mastersok[zone->curmaster] = ISC_TRUE; @@ -10186,13 +10209,21 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, if (result == ISC_R_SUCCESS) result = dns_rdataset_first(rdataset); if (result == ISC_R_SUCCESS) { - isc_uint32_t serial = 0; + isc_uint32_t serial = 0, oldserial; dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &soa, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); serial = soa.serial; - if (isc_serial_le(serial, zone->serial)) { + /* + * The following should safely be performed without DB + * lock and succeed in this context. + */ + result = zone_get_from_db(zone, zone->db, NULL, NULL, + &oldserial, NULL, NULL, NULL, + NULL, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (isc_serial_le(serial, oldserial)) { dns_zone_log(zone, ISC_LOG_INFO, "notify from %s: " "zone is up to date", @@ -10870,7 +10901,7 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { if (zone->db != NULL && zone->journal != NULL && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) && !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { - isc_uint32_t serial; + isc_uint32_t serial, oldserial; dns_zone_log(zone, ISC_LOG_DEBUG(3), "generating diffs"); @@ -10885,11 +10916,15 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { /* * This is checked in zone_postload() for master zones. */ + result = zone_get_from_db(zone, zone->db, NULL, NULL, + &oldserial, NULL, NULL, NULL, NULL, + NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (zone->type == dns_zone_slave && - !isc_serial_gt(serial, zone->serial)) { + !isc_serial_gt(serial, oldserial)) { isc_uint32_t serialmin, serialmax; - serialmin = (zone->serial + 1) & 0xffffffffU; - serialmax = (zone->serial + 0x7fffffffU) & 0xffffffffU; + serialmin = (oldserial + 1) & 0xffffffffU; + serialmax = (oldserial + 0x7fffffffU) & 0xffffffffU; dns_zone_log(zone, ISC_LOG_ERROR, "ixfr-from-differences: failed: " "new serial (%u) out of range [%u - %u]", @@ -11082,7 +11117,6 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { zone_unload(zone); goto next_master; } - zone->serial = serial; zone->refresh = RANGE(refresh, zone->minrefresh, zone->maxrefresh); zone->retry = RANGE(retry, zone->minretry, @@ -11120,7 +11154,7 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { buf[0] = '\0'; dns_zone_log(zone, ISC_LOG_INFO, "transferred serial %u%s", - zone->serial, buf); + serial, buf); } /*