]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
4533. [bug] dns_client_update should terminate on prerequiste
authorMark Andrews <marka@isc.org>
Tue, 13 Dec 2016 04:47:03 +0000 (15:47 +1100)
committerMark Andrews <marka@isc.org>
Tue, 13 Dec 2016 04:47:03 +0000 (15:47 +1100)
                        failures (NXDOMAIN, YXDOMAIN, NXRRSET, YXRRSET)
                        and also on BADZONE.  [RT #43865]

CHANGES
bin/tests/system/conf.sh.in
bin/tests/system/nsupdate/clean.sh
bin/tests/system/nsupdate/ns1/named.conf
bin/tests/system/nsupdate/ns1/sample.db.in [new file with mode: 0644]
bin/tests/system/nsupdate/ns2/named.conf
bin/tests/system/nsupdate/ns2/sample.db.in [new file with mode: 0644]
bin/tests/system/nsupdate/setup.sh
bin/tests/system/nsupdate/tests.sh
lib/dns/client.c
lib/samples/sample-update.c

diff --git a/CHANGES b/CHANGES
index a7d76743043b1bbc6dd9b72443b11d187d68c8f2..f6b8d7a16643765c0f8245d7c116d8f3565707bf 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+4533.  [bug]           dns_client_update should terminate on prerequiste
+                       failures (NXDOMAIN, YXDOMAIN, NXRRSET, YXRRSET)
+                       and also on BADZONE.  [RT #43865]
+
 4532.  [contrib]       Make gen-data-queryperf.py python 3 compatible.
                        [RT #43836]
 
index 78440ee47688530b265c0a358cec1fbb0ccfedd7..ff7c46b8fb8337f0c7b90357ba997ed4a52d3393 100644 (file)
@@ -65,6 +65,7 @@ KEYDELETE=$TOP/bin/tests/system/tkey/keydelete
 LWTEST=$TOP/bin/tests/system/lwresd/lwtest
 MAKEJOURNAL=$TOP/bin/tests/makejournal
 PIPEQUERIES=$TOP/bin/tests/system/pipelined/pipequeries
+SAMPLEUPDATE=$TOP/lib/samples/sample-update
 
 # The "stress" test is not run by default since it creates enough
 # load on the machine to make it unusable to other users.
@@ -166,6 +167,7 @@ export RANDFILE
 export RESOLVE
 export RNDC
 export RRCHECKER
+export SAMPLEUPDATE
 export SIGNER
 export SUBDIRS
 export TESTSOCK6
index 5c9d0405b7840b83ca92401088e558bbeb3bb394..a72ef61bbee268645eec422e3a774c0772f415eb 100644 (file)
@@ -34,3 +34,8 @@ rm -f ns3/nsec3param.test.db
 rm -f ns3/too-big.test.db
 rm -f nsupdate.out*
 rm -f typelist.out.*
+rm -f ns1/sample.db
+rm -f ns2/sample.db
+rm -f update.out.*
+rm -f check.out.*
+rm -f update.out.*
index 5d89e91889131b64b13dd7f92c0038b6bfb42d76..350792837e0ab2fedd41ef535c8580105d938064 100644 (file)
@@ -124,3 +124,9 @@ zone "many.test" {
        allow-update { any; };
        file "many.test.db";
 };
+
+zone "sample" {
+       type master;
+       allow-update { any; };
+       file "sample.db";
+};
diff --git a/bin/tests/system/nsupdate/ns1/sample.db.in b/bin/tests/system/nsupdate/ns1/sample.db.in
new file mode 100644 (file)
index 0000000..6881eb0
--- /dev/null
@@ -0,0 +1,8 @@
+@ 0 SOA ns1 . 0 0 0 0 0
+@ 0 NS ns1
+ns1 0 A 10.53.0.1
+; a RRset that exists
+exists 0 TXT This RRset exists.
+; nxdomain
+; A named without a TXT RRset
+no-txt 0 A 1.2.3.4
index 9de2e80fa7cd4c3a1a3265e7401e819fdae52871..a14af0e2d9ea6afde9958afde36a21e1ff0f90f8 100644 (file)
@@ -55,4 +55,10 @@ view primary {
                file "update.bk";
                allow-transfer { any; };
        };
+
+       zone "sample" {
+               type master;
+               allow-update { any; };
+               file "sample.db";
+       };
 };
diff --git a/bin/tests/system/nsupdate/ns2/sample.db.in b/bin/tests/system/nsupdate/ns2/sample.db.in
new file mode 100644 (file)
index 0000000..fde9dae
--- /dev/null
@@ -0,0 +1,10 @@
+@ 0 SOA ns2 . 0 0 0 0 0
+@ 0 NS ns2
+ns2 0 A 10.53.0.2
+;
+; These prerequistes are reversed, relative to ns1/sample.db.in:
+; 'exists' does not exist.
+nxdomain 0 TXT This RRset exists.
+
+; a name with a TXT RRset
+no-txt 0 TXT This RRset exists
index c5f0e26fce8af921243b5c743b14db8b436b14ab..3ca59fcea1b40aa6c3d346483c5c81e85597a8db 100644 (file)
@@ -58,3 +58,6 @@ $DDNSCONFGEN -q -r $RANDFILE -a hmac-sha512 -k sha512-key -z keytests.nil > ns1/
 
 cp -f ns1/many.test.db.in ns1/many.test.db
 rm -f ns1/many.test.db.jnl
+
+cp ns1/sample.db.in ns1/sample.db
+cp ns2/sample.db.in ns2/sample.db
index 8a1e4797e03f4296ea91f03fb83fc716c6ef36b0..c771bd8e4f4341f1b4a3dc6a2d4053c2cdc8cebe 100755 (executable)
@@ -701,5 +701,76 @@ grep "status: NXDOMAIN" dig.out.ns3.test$n > /dev/null || ret=1
 grep "records in zone (4) exceeds max-records (3)" ns3/named.run > /dev/null || ret=1
 [ $ret = 0 ] || { echo I:failed; status=1; }
 
+#
+#  Add client library tests here
+#
+n=`expr $n + 1`
+echo "I:check that dns_client_update handles prerequisite NXDOMAIN failure ($n)"
+$SAMPLEUPDATE -P 5300 -a 10.53.0.1 -a 10.53.0.2 -p "nxdomain exists.sample" \
+       add "nxdomain-exists.sample 0 in a 1.2.3.4" > update.out.test$n 2>&1
+$SAMPLEUPDATE -P 5300 -a 10.53.0.2 -p "nxdomain exists.sample" \
+       add "check-nxdomain-exists.sample 0 in a 1.2.3.4" > update.out.check$n 2>&1
+$DIG +tcp @10.53.0.1 -p 5300 a nxdomain-exists.sample > dig.out.ns1.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a nxdomain-exists.sample > dig.out.ns2.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a check-nxdomain-exists.sample > check.out.ns2.test$n
+grep "update failed: YXDOMAIN" update.out.test$n > /dev/null || ret=1
+grep "update succeeded" update.out.check$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns1.test$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns2.test$n > /dev/null || ret=1
+grep "status: NOERROR" check.out.ns2.test$n > /dev/null || ret=1
+[ $ret = 0 ] || { echo I:failed; status=1; }
+
+n=`expr $n + 1`
+echo "I:check that dns_client_update handles prerequisite YXDOMAIN failure ($n)"
+$SAMPLEUPDATE -P 5300 -a 10.53.0.1 -a 10.53.0.2 -p "yxdomain nxdomain.sample" \
+       add "yxdomain-nxdomain.sample 0 in a 1.2.3.4" > update.out.test$n 2>&1
+$SAMPLEUPDATE -P 5300 -a 10.53.0.2 -p "yxdomain nxdomain.sample" \
+       add "check-yxdomain-nxdomain.sample 0 in a 1.2.3.4" > update.out.check$n 2>&1
+$DIG +tcp @10.53.0.1 -p 5300 a nxdomain-exists.sample > dig.out.ns1.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a nxdomain-exists.sample > dig.out.ns2.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a check-nxdomain-exists.sample > check.out.ns2.test$n
+grep "update failed: NXDOMAIN" update.out.test$n > /dev/null || ret=1
+grep "update succeeded" update.out.check$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns1.test$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns2.test$n > /dev/null || ret=1
+grep "status: NOERROR" check.out.ns2.test$n > /dev/null || ret=1
+[ $ret = 0 ] || { echo I:failed; status=1; }
+
+n=`expr $n + 1`
+echo "I:check that dns_client_update handles prerequisite NXRRSET failure ($n)"
+$SAMPLEUPDATE -P 5300 -a 10.53.0.1 -a 10.53.0.2 -p "nxrrset exists.sample TXT This RRset exists." \
+       add "nxrrset-exists.sample 0 in a 1.2.3.4" > update.out.test$n 2>&1
+$SAMPLEUPDATE -P 5300 -a 10.53.0.2 -p "nxrrset exists.sample TXT This RRset exists." \
+       add "check-nxrrset-exists.sample 0 in a 1.2.3.4" > update.out.check$n 2>&1
+$DIG +tcp @10.53.0.1 -p 5300 a nxrrset-exists.sample > dig.out.ns1.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a nxrrset-exists.sample > dig.out.ns2.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a check-nxrrset-exists.sample > check.out.ns2.test$n
+grep "update failed: YXRRSET" update.out.test$n > /dev/null || ret=1
+grep "update succeeded" update.out.check$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns1.test$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns2.test$n > /dev/null || ret=1
+grep "status: NOERROR" check.out.ns2.test$n > /dev/null || ret=1
+[ $ret = 0 ] || { echo I:failed; status=1; }
+
+n=`expr $n + 1`
+echo "I:check that dns_client_update handles prerequisite YXRRSET failure ($n)"
+$SAMPLEUPDATE -P 5300 -a 10.53.0.1 -a 10.53.0.2 -p "yxrrset no-txt.sample TXT" \
+       add "yxrrset-nxrrset.sample 0 in a 1.2.3.4" > update.out.test$n 2>&1
+$SAMPLEUPDATE -P 5300 -a 10.53.0.2 -p "yxrrset no-txt.sample TXT" \
+       add "check-yxrrset-nxrrset.sample 0 in a 1.2.3.4" > update.out.check$n 2>&1
+$DIG +tcp @10.53.0.1 -p 5300 a yxrrset-nxrrset.sample > dig.out.ns1.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a yxrrset-nxrrset.sample > dig.out.ns2.test$n
+$DIG +tcp @10.53.0.2 -p 5300 a check-yxrrset-nxrrset.sample > check.out.ns2.test$n
+grep "update failed: NXRRSET" update.out.test$n > /dev/null || ret=1
+grep "update succeeded" update.out.check$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns1.test$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns2.test$n > /dev/null || ret=1
+grep "status: NOERROR" check.out.ns2.test$n > /dev/null || ret=1
+[ $ret = 0 ] || { echo I:failed; status=1; }
+
+#
+# End client library tests here
+#
+
 echo "I:exit status: $status"
 [ $status -eq 0 ] || exit 1
index 93294501fe7a28cafe7f04f112c6c4434e140bbc..0ec1953eb13f847a0f84c2074d30044eb09964da 100644 (file)
@@ -1978,8 +1978,21 @@ update_done(isc_task_t *task, isc_event_t *event) {
        LOCK(&uctx->lock);
        uctx->currentserver = ISC_LIST_NEXT(uctx->currentserver, link);
        dns_request_destroy(&uctx->updatereq);
-       if (result != ISC_R_SUCCESS && !uctx->canceled &&
-           uctx->currentserver != NULL) {
+       /*
+        * Moving on to the next server shouldn't change the result
+        * for NXDOMAIN, YXDOMAIN, NXRRSET and YXRRSET as they
+        * indicate a prerequisite failure.  REFUSED should also
+        * be consistent across all servers but often isn't as that
+        * is policy rather that zone content driven (slaves that
+        * aren't willing to forward should return NOTIMPL).  NOTZONE
+        * indicates that we stuffed up the request construction so
+        * don't retry.
+        */
+       if (result != ISC_R_SUCCESS && result != DNS_R_NXDOMAIN &&
+           result != DNS_R_YXDOMAIN && result != DNS_R_YXRRSET &&
+           result != DNS_R_NXRRSET && result != DNS_R_NOTZONE &&
+           !uctx->canceled && uctx->currentserver != NULL)
+       {
                dns_message_renderreset(uctx->updatemsg);
                dns_message_settsigkey(uctx->updatemsg, NULL);
 
index 2a3020467af90ab1484dec866d16c00751019986..06d52dd4232f35a03f05b9d125b6b25d80f767cf 100644 (file)
@@ -60,6 +60,8 @@ static const dns_rdataclass_t default_rdataclass = dns_rdataclass_in;
 static isc_bufferlist_t usedbuffers;
 static ISC_LIST(dns_rdatalist_t) usedrdatalists;
 
+static const char *port = "53";
+
 static void setup_tsec(char *keyfile, isc_mem_t *mctx);
 static void update_addordelete(isc_mem_t *mctx, char *cmdline,
                               isc_boolean_t isdelete, dns_name_t *name);
@@ -80,20 +82,49 @@ usage(void) {
        exit(1);
 }
 
+static isc_boolean_t
+addserver(const char *server, isc_sockaddrlist_t *list,
+          isc_sockaddr_t *sockaddr)
+{
+       struct addrinfo hints, *res;
+       int gaierror;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_protocol = IPPROTO_UDP;
+#ifdef AI_NUMERICHOST
+       hints.ai_flags |= AI_NUMERICHOST;
+#endif
+#ifdef AI_NUMERICSERV
+       hints.ai_flags |= AI_NUMERICSERV;
+#endif
+       gaierror = getaddrinfo(server, port, &hints, &res);
+       if (gaierror != 0) {
+               fprintf(stderr, "getaddrinfo(%s) failed: %s\n",
+                       server, gai_strerror(gaierror));
+               return (ISC_FALSE);
+       }
+       INSIST(res->ai_addrlen <= sizeof(sockaddr->type));
+       memmove(&sockaddr->type, res->ai_addr, res->ai_addrlen);
+       freeaddrinfo(res);
+       sockaddr->length = (unsigned int)res->ai_addrlen;
+       ISC_LINK_INIT(sockaddr, link);
+       ISC_LIST_APPEND(*list, sockaddr, link);
+       return (ISC_TRUE);
+}
+
 int
 main(int argc, char *argv[]) {
        int ch;
-       struct addrinfo hints, *res;
-       int gaierror;
        dns_client_t *client = NULL;
        char *zonenamestr = NULL;
        char *keyfilename = NULL;
        char *prereqstr = NULL;
-       isc_sockaddrlist_t auth_servers;
-       char *auth_server = NULL;
-       char *recursive_server = NULL;
-       isc_sockaddr_t sa_auth, sa_recursive;
+       isc_sockaddr_t sa_auth[10], sa_recursive[10];
+       unsigned int nsa_auth = 0, nsa_recursive = 0;
        isc_sockaddrlist_t rec_servers;
+       isc_sockaddrlist_t auth_servers;
        isc_result_t result;
        isc_boolean_t isdelete;
        isc_buffer_t b, *buf;
@@ -106,19 +137,32 @@ main(int argc, char *argv[]) {
        dns_namelist_t updatelist, prereqlist, *prereqlistp = NULL;
        isc_mem_t *umctx = NULL;
 
-       while ((ch = isc_commandline_parse(argc, argv, "a:k:p:r:z:")) != EOF) {
+       ISC_LIST_INIT(auth_servers);
+       ISC_LIST_INIT(rec_servers);
+
+       while ((ch = isc_commandline_parse(argc, argv, "a:k:p:P:r:z:")) != EOF) {
                switch (ch) {
                case 'k':
                        keyfilename = isc_commandline_argument;
                        break;
                case 'a':
-                       auth_server = isc_commandline_argument;
+                       if (nsa_auth < sizeof(sa_auth)/sizeof(*sa_auth) &&
+                           addserver(isc_commandline_argument, &auth_servers,
+                                     &sa_auth[nsa_auth]))
+                               nsa_auth++;
                        break;
                case 'p':
                        prereqstr = isc_commandline_argument;
                        break;
+               case 'P':
+                       port = isc_commandline_argument;
+                       break;
                case 'r':
-                       recursive_server = isc_commandline_argument;
+                       if (nsa_recursive < 
+                               sizeof(sa_recursive)/sizeof(*sa_recursive) &&
+                           addserver(isc_commandline_argument, &rec_servers,
+                                     &sa_recursive[nsa_recursive]))
+                               nsa_recursive++;
                        break;
                case 'z':
                        zonenamestr = isc_commandline_argument;
@@ -143,8 +187,9 @@ main(int argc, char *argv[]) {
                exit(1);
        }
 
-       if (auth_server == NULL && recursive_server == NULL) {
-               fprintf(stderr, "authoritative or recursive server "
+       if (ISC_LIST_HEAD(auth_servers) == NULL &&
+           ISC_LIST_HEAD(rec_servers) == NULL) {
+               fprintf(stderr, "authoritative or recursive servers "
                        "must be specified\n");
                usage();
        }
@@ -153,7 +198,6 @@ main(int argc, char *argv[]) {
        ISC_LIST_INIT(usedbuffers);
        ISC_LIST_INIT(usedrdatalists);
        ISC_LIST_INIT(prereqlist);
-       ISC_LIST_INIT(auth_servers);
        isc_lib_register();
        result = dns_lib_init();
        if (result != ISC_R_SUCCESS) {
@@ -172,60 +216,6 @@ main(int argc, char *argv[]) {
                exit(1);
        }
 
-       /* Set the authoritative server */
-       if (auth_server != NULL) {
-               memset(&hints, 0, sizeof(hints));
-               hints.ai_family = AF_UNSPEC;
-               hints.ai_socktype = SOCK_DGRAM;
-               hints.ai_protocol = IPPROTO_UDP;
-#ifdef AI_NUMERICHOST
-               hints.ai_flags = AI_NUMERICHOST;
-#endif
-               gaierror = getaddrinfo(auth_server, "53", &hints, &res);
-               if (gaierror != 0) {
-                       fprintf(stderr, "getaddrinfo failed: %s\n",
-                               gai_strerror(gaierror));
-                       exit(1);
-               }
-               INSIST(res->ai_addrlen <= sizeof(sa_auth.type));
-               memmove(&sa_auth.type, res->ai_addr, res->ai_addrlen);
-               freeaddrinfo(res);
-               sa_auth.length = (unsigned int)res->ai_addrlen;
-               ISC_LINK_INIT(&sa_auth, link);
-
-               ISC_LIST_APPEND(auth_servers, &sa_auth, link);
-       }
-
-       /* Set the recursive server */
-       if (recursive_server != NULL) {
-               memset(&hints, 0, sizeof(hints));
-               hints.ai_family = AF_UNSPEC;
-               hints.ai_socktype = SOCK_DGRAM;
-               hints.ai_protocol = IPPROTO_UDP;
-#ifdef AI_NUMERICHOST
-               hints.ai_flags = AI_NUMERICHOST;
-#endif
-               gaierror = getaddrinfo(recursive_server, "53", &hints, &res);
-               if (gaierror != 0) {
-                       fprintf(stderr, "getaddrinfo failed: %s\n",
-                               gai_strerror(gaierror));
-                       exit(1);
-               }
-               INSIST(res->ai_addrlen <= sizeof(sa_recursive.type));
-               memmove(&sa_recursive.type, res->ai_addr, res->ai_addrlen);
-               freeaddrinfo(res);
-               sa_recursive.length = (unsigned int)res->ai_addrlen;
-               ISC_LINK_INIT(&sa_recursive, link);
-               ISC_LIST_INIT(rec_servers);
-               ISC_LIST_APPEND(rec_servers, &sa_recursive, link);
-               result = dns_client_setservers(client, dns_rdataclass_in,
-                                              NULL, &rec_servers);
-               if (result != ISC_R_SUCCESS) {
-                       fprintf(stderr, "set server failed: %d\n", result);
-                       exit(1);
-               }
-       }
-
        /* Construct zone name */
        zname = NULL;
        if (zonenamestr != NULL) {
@@ -264,8 +254,8 @@ main(int argc, char *argv[]) {
        result = dns_client_update(client,
                                   default_rdataclass, /* XXX: fixed */
                                   zname, prereqlistp, &updatelist,
-                                  (auth_server == NULL) ? NULL :
-                                  &auth_servers, tsec, 0);
+                                  (ISC_LIST_HEAD(auth_servers) == NULL) ?
+                                   NULL : &auth_servers, tsec, 0);
        if (result != ISC_R_SUCCESS) {
                fprintf(stderr,
                        "update failed: %s\n", dns_result_totext(result));