]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
2704. [bug] Serial of dynamic and stub zones could be inconsistent
authorEvan Hunt <each@isc.org>
Mon, 5 Oct 2009 19:39:20 +0000 (19:39 +0000)
committerEvan Hunt <each@isc.org>
Mon, 5 Oct 2009 19:39:20 +0000 (19:39 +0000)
with their SOA serial.  [RT #19387]

CHANGES
bin/named/statschannel.c
lib/dns/include/dns/zone.h
lib/dns/win32/libdns.def
lib/dns/zone.c

diff --git a/CHANGES b/CHANGES
index 72125d4eb770c4fe4603ee131a846f4200d8d88a..5082e2b1e6fc363c0555123f834e5b7c83a7fa68 100644 (file)
--- 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]
index 0540e4e84f3e5aa02d204b842d26f9dc19d9d76b..1547c41613188fc8119d06810d11dba3f99df60b 100644 (file)
@@ -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;
index 5b781d5e33c4823eca46e53eadd3c99fd25f7dbf..9be1aabb0f87ed19be07c5e973c86bd4ea3af7c5 100644 (file)
@@ -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
index 23d4f56285c6f722cd4968c1b77e3a8a9337d59b..723a15a32fd9e929bfadcda4f587545394973b97 100644 (file)
@@ -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
index 3f5fbdd217a6f42a37555013fe3ee77e167c57c7..c4580e32b4b97c4391920d33c319924c595255f5 100644 (file)
@@ -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);
                }
 
                /*