]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
3987. [func] Allow the zone serial of a dynamically updatable
authorMark Andrews <marka@isc.org>
Tue, 21 Oct 2014 07:15:42 +0000 (18:15 +1100)
committerMark Andrews <marka@isc.org>
Tue, 21 Oct 2014 07:15:42 +0000 (18:15 +1100)
                        zone to be updated via rndc. [RT #37404]

bin/named/server.c
bin/rndc/rndc.docbook
bin/tests/system/inline/tests.sh
doc/arm/notes.xml
lib/dns/include/dns/events.h
lib/dns/include/dns/zone.h
lib/dns/zone.c

index 11ba8efcce112765bbc079f49d9b65d8df40826b..4310e33412b97e1355614e21c56b023b4bf698eb 100644 (file)
@@ -9449,6 +9449,8 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) {
        isc_boolean_t first = ISC_TRUE;
        isc_boolean_t list = ISC_FALSE, clear = ISC_FALSE;
        isc_boolean_t chain = ISC_FALSE;
+       isc_boolean_t setserial = ISC_FALSE;
+       isc_uint32_t serial = 0;
        char keystr[DNS_SECALG_FORMATSIZE + 7];
        unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0;
        unsigned char salt[255];
@@ -9524,6 +9526,12 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) {
                                saltlen = isc_buffer_usedlength(&buf);
                        }
                }
+       } else if (strcasecmp(ptr, "-serial") == 0) {
+               ptr = next_token(&args, " \t");
+               if (ptr == NULL)
+                       return (ISC_R_UNEXPECTEDEND);
+               CHECK(isc_parse_uint32(&serial, ptr, 10));
+               setserial = ISC_TRUE;
        } else
                CHECK(DNS_R_SYNTAX);
 
@@ -9541,7 +9549,11 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) {
                                             (isc_uint8_t)flags, iter,
                                             (isc_uint8_t)saltlen, salt,
                                             ISC_TRUE));
-               putstr(text, "request queued");
+               putstr(text, "nsec3param request queued");
+               putnull(text);
+       } else if (setserial) {
+               CHECK(dns_zone_setserial(zone, serial));
+               putstr(text, "serial request queued");
                putnull(text);
        } else if (list) {
                privatetype = dns_zone_getprivatetype(zone);
index ace6f11651741c2d58ad1a09b64fcbc7358d962f..b394411b9ae42e0bb0c1c43f62b52cc8caeb39f9 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><userinput>signing <optional>( -list | -clear <replaceable>keyid/algorithm</replaceable> | -clear <literal>all</literal> | -nsec3param ( <replaceable>parameters</replaceable> | <literal>none</literal> ) ) </optional> <replaceable>zone</replaceable> <optional><replaceable>class</replaceable> <optional><replaceable>view</replaceable></optional></optional> </userinput></term>
+        <term><userinput>signing <optional>( -list | -clear <replaceable>keyid/algorithm</replaceable> | -clear <literal>all</literal> | -nsec3param ( <replaceable>parameters</replaceable> | <literal>none</literal> ) | -serial <replaceable>value</serial> ) </optional> <replaceable>zone</replaceable> <optional><replaceable>class</replaceable> <optional><replaceable>view</replaceable></optional></optional> </userinput></term>
         <listitem>
           <para>
             List, edit, or remove the DNSSEC signing state records
             removes an existing NSEC3 chain and replaces it
             with NSEC.
           </para>
+         <para>
+            <command>rndc signing -serial value</command> sets
+           the serial number of the zone to value.  If the value
+           would cause the serial number to go backwards it will
+           be rejected.  The primary use is to set the serial on
+           inline signed zones.
+         </para>
         </listitem>
       </varlistentry>
     </variablelist>
index 97e92fefd0630cc07408b20a48c020871c631303..49646f562240e2574bd491015ce70b9cac71573d 100755 (executable)
@@ -890,4 +890,93 @@ $IMPORTKEY -f import.key import.example > /dev/null 2>&1 || ret=1
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
+n=`expr $n + 1`
+echo "I:testing updating inline secure serial via 'rndc signing -serial' ($n)"
+ret=0
+$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.n3.pre.test$n
+newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.n3.pre.test$n`
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -serial ${newserial:-0} nsec3 > /dev/null 2>&1
+sleep 1
+$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.ns3.post.test$n
+serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns3.post.test$n`
+[ ${newserial:-0} -eq ${serial:-1} ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:testing updating inline secure serial via 'rndc signing -serial' with negative change ($n)"
+ret=0
+$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.n3.pre.test$n
+oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.n3.pre.test$n`
+newserial=`awk '$4 == "SOA" { print $7 - 10}' dig.out.n3.pre.test$n`
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -serial ${newserial:-0} nsec3 > /dev/null 2>&1
+sleep 1
+$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.ns3.post.test$n
+serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns3.post.test$n`
+[ ${oldserial:-0} -eq ${serial:-1} ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+#
+# Freezing only operates on the raw zone.
+#
+n=`expr $n + 1`
+echo "I:testing updating inline secure serial via 'rndc signing -serial' when frozen ($n)"
+ret=0
+$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.n3.pre.test$n
+oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.n3.pre.test$n`
+newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.n3.pre.test$n`
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 freeze nsec3 > /dev/null 2>&1
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -serial ${newserial:-0} nsec3 > /dev/null 2>&1
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 thaw nsec3 > /dev/null 2>&1
+sleep 1
+$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.ns3.post.test$n
+serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns3.post.test$n`
+[ ${newserial:-0} -eq ${serial:-1} ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+echo "I:testing updating dynamic serial via 'rndc signing -serial' ($n)"
+ret=0
+$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.pre.test$n
+newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.ns2.pre.test$n`
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 signing -serial ${newserial:-0} bits > /dev/null 2>&1
+sleep 1
+$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.post.test$n
+serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.post.test$n`
+[ ${newserial:-0} -eq ${serial:-1} ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:testing updating dynamic serial via 'rndc signing -serial' with negative change ($n)"
+ret=0
+$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.pre.test$n
+oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.pre.test$n`
+newserial=`awk '$4 == "SOA" { print $7 - 10}' dig.out.ns2.pre.test$n`
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 signing -serial ${newserial:-0} bits > /dev/null 2>&1
+sleep 1
+$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.post.test$n
+serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.post.test$n`
+[ ${oldserial:-0} -eq ${serial:-1} ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:testing updating dynamic serial via 'rndc signing -serial' when frozen ($n)"
+ret=0
+$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.pre.test$n
+oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.pre.test$n`
+newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.ns2.pre.test$n`
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 freeze bits > /dev/null 2>&1
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 signing -serial ${newserial:-0} bits > /dev/null 2>&1
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 thaw bits > /dev/null 2>&1
+sleep 1
+$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.post.test$n
+serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.post.test$n`
+[ ${oldserial:-0} -eq ${serial:-1} ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
 exit $status
index 0bd6a7975f8cc88aa7b302bbf565fc92d7489556..3bd26e174b4c99654dee1b0b68709794103485e7 100644 (file)
     <title>New Features</title>
     <itemizedlist>
       <listitem>
-       <para>None</para>
+       <para>
+         rndc can be used to set the serial of dynamically updatable
+         zones using "rndc signing -serial # zone".  This is
+         particularly useful with inline zones which have been
+         reset.  The serial should be set to a value larger than
+         that on the slaves.  This will trigger a AXFR style
+         transfer.
+       </para>
       </listitem>
     </itemizedlist>
   </sect2>
index fd2144f64937850a40c6c0ad88be359efd60e91e..72ea306b28e73143a982724e40d32a4fb801874d 100644 (file)
@@ -79,6 +79,7 @@
 #define DNS_EVENT_ZONELOAD                     (ISC_EVENTCLASS_DNS + 49)
 #define DNS_EVENT_KEYDONE                      (ISC_EVENTCLASS_DNS + 50)
 #define DNS_EVENT_SETNSEC3PARAM                        (ISC_EVENTCLASS_DNS + 51)
+#define DNS_EVENT_SETSERIAL                    (ISC_EVENTCLASS_DNS + 52)
 
 #define DNS_EVENT_FIRSTEVENT                   (ISC_EVENTCLASS_DNS + 0)
 #define DNS_EVENT_LASTEVENT                    (ISC_EVENTCLASS_DNS + 65535)
index d6ab7cac7f1635dfac774c02caa34f9ce402c601..0a789ab0361dc1b3b84ae285c401b5789cc3f871 100644 (file)
@@ -2378,6 +2378,11 @@ dns_zone_getstatlevel(dns_zone_t *zone);
  * full, terse, or none.
  */
 
+isc_result_t
+dns_zone_setserial(dns_zone_t *zone, isc_uint32_t serial);
+/*%
+ * Set the zone's serial to 'serial'.
+ */
 ISC_LANG_ENDDECLS
 
 
index af3681ac60f5b3e74400dd0a8851244e29ce7e46..4fd84f79297cc61b7ebe94ed679ae5ef89c8d047 100644 (file)
@@ -847,6 +847,11 @@ struct np3event {
        nsec3param_t params;
 };
 
+struct ssevent {
+       isc_event_t event;
+       isc_uint32_t serial;
+};
+
 /*%
  * Increment resolver-related statistics counters.  Zone must be locked.
  */
@@ -17705,21 +17710,21 @@ keydone(isc_task_t *task, isc_event_t *event) {
        dns_diff_init(zone->mctx, &diff);
 
        ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
-       if (zone->db != NULL) {
+       if (zone->db != NULL)
                dns_db_attach(zone->db, &db);
-               dns_db_currentversion(db, &oldver);
-               result = dns_db_newversion(db, &newver);
-               if (result != ISC_R_SUCCESS) {
-                       dns_zone_log(zone, ISC_LOG_ERROR,
-                                    "keydone:dns_db_newversion -> %s",
-                                    dns_result_totext(result));
-                       goto failure;
-               }
-       }
        ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
        if (db == NULL)
                goto failure;
 
+       dns_db_currentversion(db, &oldver);
+       result = dns_db_newversion(db, &newver);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "keydone:dns_db_newversion -> %s",
+                            dns_result_totext(result));
+               goto failure;
+       }
+
        result = dns_db_getoriginnode(db, &node);
        if (result != ISC_R_SUCCESS)
                goto failure;
@@ -17901,21 +17906,22 @@ setnsec3param(isc_task_t *task, isc_event_t *event) {
        dns_diff_init(zone->mctx, &diff);
 
        ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
-       if (zone->db != NULL) {
+       if (zone->db != NULL)
                dns_db_attach(zone->db, &db);
-               dns_db_currentversion(db, &oldver);
-               result = dns_db_newversion(db, &newver);
-               if (result != ISC_R_SUCCESS) {
-                       dns_zone_log(zone, ISC_LOG_ERROR,
-                                    "setnsec3param:dns_db_newversion -> %s",
-                                    dns_result_totext(result));
-                       goto failure;
-               }
-       }
        ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
        if (db == NULL)
                goto failure;
 
+       dns_db_currentversion(db, &oldver);
+       result = dns_db_newversion(db, &newver);
+       if (result != ISC_R_SUCCESS) {
+               ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "setnsec3param:dns_db_newversion -> %s",
+                            dns_result_totext(result));
+               goto failure;
+       }
+
        CHECK(dns_db_getoriginnode(db, &node));
 
        /*
@@ -18191,3 +18197,143 @@ dns_zone_getstatlevel(dns_zone_t *zone) {
 
        return (zone->statlevel);
 }
+
+static void
+setserial(isc_task_t *task, isc_event_t *event) {
+       isc_uint32_t oldserial, desired;
+       const char *me = "setserial";
+       isc_boolean_t commit = ISC_FALSE;
+       isc_result_t result;
+       dns_dbversion_t *oldver = NULL, *newver = NULL;
+       dns_zone_t *zone;
+       dns_db_t *db = NULL;
+       dns_diff_t diff;
+       struct ssevent *sse = (struct ssevent *)event;
+       dns_update_log_t log = { update_log_cb, NULL };
+       dns_difftuple_t *oldtuple = NULL, *newtuple = NULL;
+
+       UNUSED(task);
+
+       zone = event->ev_arg;
+       INSIST(DNS_ZONE_VALID(zone));
+
+       ENTER;
+
+       if (zone->update_disabled)
+               goto failure;
+
+       desired = sse->serial;
+
+       dns_diff_init(zone->mctx, &diff);
+
+       ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+       if (zone->db != NULL)
+               dns_db_attach(zone->db, &db);
+       ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+       if (db == NULL)
+               goto failure;
+
+       dns_db_currentversion(db, &oldver);
+       result = dns_db_newversion(db, &newver);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "setserial:dns_db_newversion -> %s",
+                            dns_result_totext(result));
+               goto failure;
+       }
+
+       CHECK(dns_db_createsoatuple(db, oldver, diff.mctx,
+                                   DNS_DIFFOP_DEL, &oldtuple));
+       CHECK(dns_difftuple_copy(oldtuple, &newtuple));
+       newtuple->op = DNS_DIFFOP_ADD;
+
+       oldserial = dns_soa_getserial(&oldtuple->rdata);
+       if (desired == 0U)
+               desired = 1;
+       if (!isc_serial_gt(desired, oldserial)) {
+               if (desired != oldserial)
+                       dns_zone_log(zone, ISC_LOG_INFO,
+                                    "setserial: desired serial (%u) "
+                                    "out of range (%u-%u)", desired,
+                                    oldserial + 1, (oldserial + 0x7fffffff));
+               goto failure;
+       }
+
+       dns_soa_setserial(desired, &newtuple->rdata);
+       CHECK(do_one_tuple(&oldtuple, db, newver, &diff));
+       CHECK(do_one_tuple(&newtuple, db, newver, &diff));
+       result = dns_update_signatures(&log, zone, db,
+                                      oldver, newver, &diff,
+                                      zone->sigvalidityinterval);
+       if (result != ISC_R_NOTFOUND)
+               CHECK(result);
+
+       /* Write changes to journal file. */
+       CHECK(zone_journal(zone, &diff, NULL, "setserial"));
+       commit = ISC_TRUE;
+
+       LOCK_ZONE(zone);
+       zone_needdump(zone, 30);
+       UNLOCK_ZONE(zone);
+
+ failure:
+       if (oldtuple != NULL)
+               dns_difftuple_free(&oldtuple);
+       if (newtuple != NULL)
+               dns_difftuple_free(&newtuple);
+       if (oldver != NULL)
+               dns_db_closeversion(db, &oldver, ISC_FALSE);
+       if (newver != NULL)
+               dns_db_closeversion(db, &newver, commit);
+       if (db != NULL)
+               dns_db_detach(&db);
+       dns_diff_clear(&diff);
+       isc_event_free(&event);
+       dns_zone_idetach(&zone);
+
+       INSIST(oldver == NULL);
+       INSIST(newver == NULL);
+}
+
+isc_result_t
+dns_zone_setserial(dns_zone_t *zone, isc_uint32_t serial) {
+       isc_result_t result = ISC_R_SUCCESS;
+       dns_zone_t *dummy = NULL;
+       isc_event_t *e = NULL;
+       struct ssevent *sse;
+       
+       REQUIRE(DNS_ZONE_VALID(zone));
+
+       LOCK_ZONE(zone);
+
+       if (!inline_secure(zone)) {
+               if (!dns_zone_isdynamic(zone, ISC_TRUE)) {
+                       result = DNS_R_NOTDYNAMIC;
+                       goto failure;
+               }
+       }
+
+       if (zone->update_disabled) {
+               result = DNS_R_FROZEN;
+               goto failure;
+       }
+
+       e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETSERIAL,
+                              setserial, zone, sizeof(struct ssevent));
+       if (e == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto failure;
+       }
+
+       sse = (struct ssevent *)e;
+       sse->serial = serial;
+
+       zone_iattach(zone, &dummy);
+       isc_task_send(zone->task, &e);
+
+ failure:
+       if (e != NULL)
+               isc_event_free(&e);
+       UNLOCK_ZONE(zone);
+       return (result);
+}