import unbound 1.13.2, ok florian@
authorsthen <sthen@openbsd.org>
Fri, 13 Aug 2021 19:55:24 +0000 (19:55 +0000)
committersthen <sthen@openbsd.org>
Fri, 13 Aug 2021 19:55:24 +0000 (19:55 +0000)
20 files changed:
usr.sbin/unbound/dnstap/dnstap.h
usr.sbin/unbound/dnstap/unbound-dnstap-socket.c
usr.sbin/unbound/services/rpz.c
usr.sbin/unbound/testcode/delayer.c
usr.sbin/unbound/testcode/do-tests.sh
usr.sbin/unbound/testcode/dohclient.c
usr.sbin/unbound/testcode/fake_event.c
usr.sbin/unbound/testcode/mini_tdir.sh
usr.sbin/unbound/testcode/petal.c
usr.sbin/unbound/testcode/readzone.c [new file with mode: 0644]
usr.sbin/unbound/testcode/replay.c
usr.sbin/unbound/testcode/replay.h
usr.sbin/unbound/testcode/streamtcp.c
usr.sbin/unbound/testcode/testbound.c
usr.sbin/unbound/testcode/unitauth.c
usr.sbin/unbound/testcode/unitldns.c
usr.sbin/unbound/testcode/unitmain.c
usr.sbin/unbound/testcode/unitmain.h
usr.sbin/unbound/testcode/unittcpreuse.c [new file with mode: 0644]
usr.sbin/unbound/testcode/unitzonemd.c [new file with mode: 0644]

index 783b8c5..449fae7 100644 (file)
@@ -123,12 +123,14 @@ dt_delete(struct dt_env *env);
  * Create and send a new dnstap "Message" event of type CLIENT_QUERY.
  * @param env: dnstap environment object.
  * @param qsock: address/port of client.
+ * @param rsock: local (service) address/port.
  * @param cptype: comm_udp or comm_tcp.
  * @param qmsg: query message.
  */
 void
 dt_msg_send_client_query(struct dt_env *env,
                         struct sockaddr_storage *qsock,
+                        struct sockaddr_storage *rsock,
                         enum comm_point_type cptype,
                         struct sldns_buffer *qmsg);
 
@@ -136,12 +138,14 @@ dt_msg_send_client_query(struct dt_env *env,
  * Create and send a new dnstap "Message" event of type CLIENT_RESPONSE.
  * @param env: dnstap environment object.
  * @param qsock: address/port of client.
+ * @param rsock: local (service) address/port.
  * @param cptype: comm_udp or comm_tcp.
  * @param rmsg: response message.
  */
 void
 dt_msg_send_client_response(struct dt_env *env,
                            struct sockaddr_storage *qsock,
+                           struct sockaddr_storage *rsock,
                            enum comm_point_type cptype,
                            struct sldns_buffer *rmsg);
 
@@ -150,7 +154,8 @@ dt_msg_send_client_response(struct dt_env *env,
  * FORWARDER_QUERY. The type used is dependent on the value of the RD bit
  * in the query header.
  * @param env: dnstap environment object.
- * @param rsock: address/port of server the query is being sent to.
+ * @param rsock: address/port of server (upstream) the query is being sent to.
+ * @param qsock: address/port of server (local) the query is being sent from.
  * @param cptype: comm_udp or comm_tcp.
  * @param zone: query zone.
  * @param zone_len: length of zone.
@@ -159,6 +164,7 @@ dt_msg_send_client_response(struct dt_env *env,
 void
 dt_msg_send_outside_query(struct dt_env *env,
                          struct sockaddr_storage *rsock,
+                         struct sockaddr_storage *qsock,
                          enum comm_point_type cptype,
                          uint8_t *zone, size_t zone_len,
                          struct sldns_buffer *qmsg);
@@ -168,7 +174,8 @@ dt_msg_send_outside_query(struct dt_env *env,
  * FORWARDER_RESPONSE. The type used is dependent on the value of the RD bit
  * in the query header.
  * @param env: dnstap environment object.
- * @param rsock: address/port of server the response was received from.
+ * @param rsock: address/port of server (upstream) the response was received from.
+ * @param qsock: address/port of server (local) the response was received to.
  * @param cptype: comm_udp or comm_tcp.
  * @param zone: query zone.
  * @param zone_len: length of zone.
@@ -181,6 +188,7 @@ dt_msg_send_outside_query(struct dt_env *env,
 void
 dt_msg_send_outside_response(struct dt_env *env,
                             struct sockaddr_storage *rsock,
+                            struct sockaddr_storage *qsock,
                             enum comm_point_type cptype,
                             uint8_t *zone, size_t zone_len,
                             uint8_t *qbuf, size_t qbuf_len,
index 8e28be4..3de8ab3 100644 (file)
@@ -1012,6 +1012,7 @@ void dtio_tap_callback(int fd, short ATTR_UNUSED(bits), void* arg)
                if(verbosity) log_info("bidirectional stream");
                if(!reply_with_accept(data)) {
                        tap_data_free(data);
+                       return;
                }
        } else if(data->len >= 4 && sldns_read_uint32(data->frame) ==
                FSTRM_CONTROL_FRAME_STOP && data->is_bidirectional) {
@@ -1166,8 +1167,13 @@ int sig_quit = 0;
 /** signal handler for user quit */
 static RETSIGTYPE main_sigh(int sig)
 {
-       if(!sig_quit)
-               fprintf(stderr, "exit on signal %d\n", sig);
+       if(!sig_quit) {
+               char str[] = "exit on signal   \n";
+               str[15] = '0' + (sig/10)%10;
+               str[16] = '0' + sig%10;
+               /* simple cast to void will not silence Wunused-result */
+               (void)!write(STDERR_FILENO, str, strlen(str));
+       }
        if(sig_base) {
                ub_event_base_loopexit(sig_base);
                sig_base = NULL;
@@ -1375,14 +1381,6 @@ int worker_handle_request(struct comm_point* ATTR_UNUSED(c),
        return 0;
 }
 
-int worker_handle_reply(struct comm_point* ATTR_UNUSED(c), 
-       void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
-        struct comm_reply* ATTR_UNUSED(reply_info))
-{
-       log_assert(0);
-       return 0;
-}
-
 int worker_handle_service_reply(struct comm_point* ATTR_UNUSED(c), 
        void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
         struct comm_reply* ATTR_UNUSED(reply_info))
@@ -1456,14 +1454,6 @@ struct outbound_entry* libworker_send_query(
        return 0;
 }
 
-int libworker_handle_reply(struct comm_point* ATTR_UNUSED(c), 
-       void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
-        struct comm_reply* ATTR_UNUSED(reply_info))
-{
-       log_assert(0);
-       return 0;
-}
-
 int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c), 
        void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
         struct comm_reply* ATTR_UNUSED(reply_info))
index 2b6b0ac..3a1ec00 100644 (file)
@@ -162,6 +162,7 @@ rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
                case LDNS_RR_TYPE_RRSIG:
                case LDNS_RR_TYPE_NSEC:
                case LDNS_RR_TYPE_NSEC3:
+               case LDNS_RR_TYPE_NSEC3PARAM:
                        return RPZ_INVALID_ACTION;
                case LDNS_RR_TYPE_CNAME:
                        break;
@@ -479,8 +480,21 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
        int newzone = 0;
 
        if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) {
-               verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s",
-                       rpz_action_to_string(a));
+               char str[255+1];
+               if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS ||
+                       rrtype == LDNS_RR_TYPE_DNAME ||
+                       rrtype == LDNS_RR_TYPE_DNSKEY ||
+                       rrtype == LDNS_RR_TYPE_RRSIG ||
+                       rrtype == LDNS_RR_TYPE_NSEC ||
+                       rrtype == LDNS_RR_TYPE_NSEC3PARAM ||
+                       rrtype == LDNS_RR_TYPE_NSEC3 ||
+                       rrtype == LDNS_RR_TYPE_DS) {
+                       free(dname);
+                       return; /* no need to log these types as unsupported */
+               }
+               dname_str(dname, str);
+               verbose(VERB_ALGO, "RPZ: qname trigger, %s skipping unsupported action: %s",
+                       str, rpz_action_to_string(a));
                free(dname);
                return;
        }
@@ -552,8 +566,10 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
 
        if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION ||
                respa == respip_invalid) {
-               verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s",
-                       rpz_action_to_string(a));
+               char str[255+1];
+               dname_str(dname, str);
+               verbose(VERB_ALGO, "RPZ: respip trigger, %s skipping unsupported action: %s",
+                       str, rpz_action_to_string(a));
                return 0;
        }
 
@@ -702,7 +718,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
         * zone match, append '*' to that and do another lookup. */
 
        ce = dname_get_shared_topdomain(z->name, qname);
-       if(!ce /* should not happen */ || !*ce /* root */) {
+       if(!ce /* should not happen */) {
                lock_rw_unlock(&z->lock);
                if(zones_keep_lock) {
                        lock_rw_unlock(&r->local_zones->lock);
index 54175db..e915961 100644 (file)
@@ -347,7 +347,11 @@ static volatile int do_quit = 0;
 /** signal handler for user quit */
 static RETSIGTYPE delayer_sigh(int sig)
 {
-       printf("exit on signal %d\n", sig);
+       char str[] = "exit on signal   \n";
+       str[15] = '0' + (sig/10)%10;
+       str[16] = '0' + sig%10;
+       /* simple cast to void will not silence Wunused-result */
+       (void)!write(STDOUT_FILENO, str, strlen(str));
        do_quit = 1;
 }
 
index effb7c1..2a1cfc4 100755 (executable)
@@ -1,5 +1,11 @@
 #!/usr/bin/env bash
 . testdata/common.sh
+quiet=0
+if test "$1" = "-q"; then
+       quiet=1
+       tdirarg="-q"
+       shift
+fi
 
 NEED_SPLINT='00-lint.tdir'
 NEED_DOXYGEN='01-doc.tdir'
@@ -33,7 +39,7 @@ fi
 export -n NOTIFY_SOCKET
 
 cd testdata;
-sh ../testcode/mini_tdir.sh clean
+sh ../testcode/mini_tdir.sh $tdirarg clean
 rm -f .perfstats.txt
 for test in `ls -d *.tdir`; do
        SKIP=0
@@ -57,10 +63,10 @@ for test in `ls -d *.tdir`; do
        fi
        if test $SKIP -eq 0; then
                echo $test
-               sh ../testcode/mini_tdir.sh -a ../.. exe $test
+               sh ../testcode/mini_tdir.sh -a ../.. $tdirarg exe $test
        else
                echo "skip $test"
        fi
 done
-sh ../testcode/mini_tdir.sh report
+sh ../testcode/mini_tdir.sh $tdirarg report
 cat .perfstats.txt
index 2634180..93d84a8 100644 (file)
@@ -423,6 +423,7 @@ http2_session_create()
 
        if(nghttp2_session_callbacks_new(&callbacks) == NGHTTP2_ERR_NOMEM) {
                log_err("failed to initialize nghttp2 callback");
+               free(h2_session);
                return NULL;
        }
        nghttp2_session_callbacks_set_recv_callback(callbacks, http2_recv_cb);
@@ -501,7 +502,9 @@ run(struct http2_session* h2_session, int port, int no_tls, int count, char** q)
        if(!no_tls) {
                ctx = connect_sslctx_create(NULL, NULL, NULL, 0);
                if(!ctx) fatal_exit("cannot create ssl ctx");
+#ifdef HAVE_SSL_CTX_SET_ALPN_PROTOS
                SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)"\x02h2", 3);
+#endif
                ssl = outgoing_ssl_fd(ctx, fd);
                if(!ssl) {
                        printf("cannot create ssl\n");
@@ -620,7 +623,25 @@ int main(int argc, char** argv)
                return 1;
        }
 
-
+       if(!no_tls) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
+               ERR_load_SSL_strings();
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
+#  ifndef S_SPLINT_S
+               OpenSSL_add_all_algorithms();
+#  endif
+#else
+               OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
+                       | OPENSSL_INIT_ADD_ALL_DIGESTS
+                       | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
+               (void)SSL_library_init();
+#else
+               (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
+#endif
+       }
        run(h2_session, port, no_tls, argc, argv);
 
        checklock_stop();
index b8166c4..5f81b9e 100644 (file)
@@ -451,6 +451,8 @@ fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo)
        struct comm_reply repinfo;
        memset(&repinfo, 0, sizeof(repinfo));
        repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point));
+       if(!repinfo.c)
+               fatal_exit("out of memory in fake_front_query");
        repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in);
        if(todo->addrlen != 0) {
                repinfo.addrlen = todo->addrlen;
@@ -597,7 +599,7 @@ autotrust_check(struct replay_runtime* runtime, struct replay_moment* mom)
                        log_err("should be: %s", p->str);
                        fatal_exit("autotrust_check failed");
                }
-               if(line[0]) line[strlen(line)-1] = 0; /* remove newline */
+               strip_end_white(line);
                expanded = macro_process(runtime->vars, runtime, p->str);
                if(!expanded) 
                        fatal_exit("could not expand macro line %d", lineno);
@@ -650,7 +652,7 @@ tempfile_check(struct replay_runtime* runtime, struct replay_moment* mom)
                        log_err("should be: %s", p->str);
                        fatal_exit("tempfile_check failed");
                }
-               if(line[0]) line[strlen(line)-1] = 0; /* remove newline */
+               strip_end_white(line);
                expanded = macro_process(runtime->vars, runtime, p->str);
                if(!expanded) 
                        fatal_exit("could not expand macro line %d", lineno);
@@ -909,6 +911,8 @@ comm_base_create(int ATTR_UNUSED(sigs))
        /* we return the runtime structure instead. */
        struct replay_runtime* runtime = (struct replay_runtime*)
                calloc(1, sizeof(struct replay_runtime));
+       if(!runtime)
+               fatal_exit("out of memory in fake_event.c:comm_base_create");
        runtime->scenario = saved_scenario;
        runtime->vars = macro_store_create();
        if(!runtime->vars) fatal_exit("out of memory");
@@ -1046,7 +1050,9 @@ outside_network_create(struct comm_base* base, size_t bufsize,
        void (*unwanted_action)(void*), void* ATTR_UNUSED(unwanted_param),
        int ATTR_UNUSED(do_udp), void* ATTR_UNUSED(sslctx),
        int ATTR_UNUSED(delayclose), int ATTR_UNUSED(tls_use_sni),
-       struct dt_env* ATTR_UNUSED(dtenv), int ATTR_UNUSED(udp_connect))
+       struct dt_env* ATTR_UNUSED(dtenv), int ATTR_UNUSED(udp_connect),
+       int ATTR_UNUSED(max_reuse_tcp_queries), int ATTR_UNUSED(tcp_reuse_timeout),
+       int ATTR_UNUSED(tcp_auth_query_timeout))
 {
        struct replay_runtime* runtime = (struct replay_runtime*)base;
        struct outside_network* outnet =  calloc(1, 
@@ -1306,8 +1312,9 @@ void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
        log_info("double delete of pending serviced query");
 }
 
-int resolve_interface_names(struct config_file* ATTR_UNUSED(cfg),
-       char*** ATTR_UNUSED(resif), int* ATTR_UNUSED(num_resif))
+int resolve_interface_names(char** ATTR_UNUSED(ifs), int ATTR_UNUSED(num_ifs),
+       struct config_strlist* ATTR_UNUSED(list), char*** ATTR_UNUSED(resif),
+       int* ATTR_UNUSED(num_resif))
 {
        return 1;
 }
@@ -1531,6 +1538,8 @@ struct comm_timer* comm_timer_create(struct comm_base* base,
 {
        struct replay_runtime* runtime = (struct replay_runtime*)base;
        struct fake_timer* t = (struct fake_timer*)calloc(1, sizeof(*t));
+       if(!t)
+               fatal_exit("out of memory in fake_event.c:comm_timer_create");
        t->cb = cb;
        t->cb_arg = cb_arg;
        fptr_ok(fptr_whitelist_comm_timer(t->cb)); /* check in advance */
@@ -1629,7 +1638,8 @@ int create_udp_sock(int ATTR_UNUSED(family), int ATTR_UNUSED(socktype),
 struct comm_point* comm_point_create_udp(struct comm_base *ATTR_UNUSED(base),
        int ATTR_UNUSED(fd), sldns_buffer* ATTR_UNUSED(buffer),
        comm_point_callback_type* ATTR_UNUSED(callback),
-       void* ATTR_UNUSED(callback_arg))
+       void* ATTR_UNUSED(callback_arg),
+       struct unbound_socket* ATTR_UNUSED(socket))
 {
        log_assert(0);
        return NULL;
@@ -1707,7 +1717,7 @@ struct comm_point* outnet_comm_point_for_tcp(struct outside_network* outnet,
                addr_to_str((struct sockaddr_storage*)to_addr, to_addrlen,
                        addrbuf, sizeof(addrbuf));
                if(verbosity >= VERB_ALGO) {
-                       if(buf[0] != 0) buf[strlen(buf)-1] = 0; /* del newline*/
+                       strip_end_white(buf);
                        log_info("tcp to %s: %s", addrbuf, buf);
                }
                log_assert(sldns_buffer_limit(query)-LDNS_HEADER_SIZE >= 2);
@@ -1739,7 +1749,7 @@ struct comm_point* outnet_comm_point_for_tcp(struct outside_network* outnet,
 struct comm_point* outnet_comm_point_for_http(struct outside_network* outnet,
        comm_point_callback_type* cb, void* cb_arg,
        struct sockaddr_storage* to_addr, socklen_t to_addrlen, int timeout,
-       int ssl, char* host, char* path)
+       int ssl, char* host, char* path, struct config_file* cfg)
 {
        struct replay_runtime* runtime = (struct replay_runtime*)
                outnet->base;
@@ -1761,6 +1771,7 @@ struct comm_point* outnet_comm_point_for_http(struct outside_network* outnet,
        (void)ssl;
        (void)host;
        (void)path;
+       (void)cfg;
 
        /* handle http comm point and return contents from test script */
        return (struct comm_point*)fc;
@@ -1797,7 +1808,7 @@ int comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet,
                addr_to_str((struct sockaddr_storage*)addr, addrlen,
                        addrbuf, sizeof(addrbuf));
                if(verbosity >= VERB_ALGO) {
-                       if(buf[0] != 0) buf[strlen(buf)-1] = 0; /* del newline*/
+                       strip_end_white(buf);
                        log_info("udp to %s: %s", addrbuf, buf);
                }
                log_assert(sldns_buffer_limit(packet)-LDNS_HEADER_SIZE >= 2);
index 5f02b08..0457a95 100755 (executable)
@@ -5,14 +5,23 @@ if test "$1" = "-a"; then
        shift
        shift
 fi
-       
+quiet=0
+if test "$1" = "-q"; then
+       quiet=1
+       shift
+fi
+
 if test "$1" = "clean"; then
-       echo "rm -f result.* .done* .tdir.var.master .tdir.var.test"
+       if test $quiet = 0; then
+               echo "rm -f result.* .done* .tdir.var.master .tdir.var.test"
+       fi
        rm -f result.* .done* .tdir.var.master .tdir.var.test
        exit 0
 fi
 if test "$1" = "fake"; then
-       echo "minitdir fake $2"
+       if test $quiet = 0; then
+               echo "minitdir fake $2"
+       fi
        echo "fake" > .done-`basename $2 .tdir`
        exit 0
 fi
@@ -37,7 +46,7 @@ if test "$1" = "-f" && test "$2" = "report"; then
                        desc=`grep ^Description: "result.$name" | sed -e 's/Description: //'`
                fi
                if test -f ".done-$name"; then
-                       if test "$1" != "-q"; then
+                       if test $quiet = 0; then
                                echo "** PASSED ** $timelen $name: $desc"
                                pass=`expr $pass + 1`
                        fi
@@ -65,7 +74,7 @@ if test "$1" = "report" || test "$2" = "report"; then
        for result in *.tdir; do
                name=`basename $result .tdir`
                if test -f ".done-$name"; then
-                       if test "$1" != "-q"; then
+                       if test $quiet = 0; then
                                echo "** PASSED ** : $name"
                        fi
                else
@@ -82,9 +91,9 @@ fi
 if test "$1" != 'exe'; then
        # usage
        echo "mini tdir. Reduced functionality for old shells."
-       echo "  tdir exe <file>"
-       echo "  tdir fake <file>"
-       echo "  tdir clean"
+       echo "  tdir [-q] exe <file>"
+       echo "  tdir [-q] fake <file>"
+       echo "  tdir [-q] clean"
        echo "  tdir [-q|-f] report"
        exit 1
 fi
@@ -117,7 +126,9 @@ if test -f .done-$name; then
 fi
 
 # Copy
-echo "minitdir copy $1 to $dir"
+if test $quiet = 0; then
+       echo "minitdir copy $1 to $dir"
+fi
 mkdir $dir
 if cp --help 2>&1 | grep -- "-a" >/dev/null; then
 cp -a $name.tdir/* $dir/
@@ -131,7 +142,9 @@ echo "minitdir exe $name" > $result
 grep "Description:" $name.dsc >> $result 2>&1
 echo "DateRunStart: "`date "+%s" 2>/dev/null` >> $result
 if test -f $name.pre; then
-       echo "minitdir exe $name.pre"
+       if test $quiet = 0; then
+               echo "minitdir exe $name.pre"
+       fi
        echo "minitdir exe $name.pre" >> $result
        $shell $name.pre $args >> $result
        if test $? -ne 0; then
@@ -139,7 +152,9 @@ if test -f $name.pre; then
        fi
 fi
 if test -f $name.test; then
-       echo "minitdir exe $name.test"
+       if test $quiet = 0; then
+               echo "minitdir exe $name.test"
+       fi
        echo "minitdir exe $name.test" >> $result
        $shell $name.test $args >>$result 2>&1
        if test $? -ne 0; then
@@ -149,12 +164,16 @@ if test -f $name.test; then
        else
                echo "$name: PASSED" >> $result
                echo "$name: PASSED" > ../.done-$name
-               echo "$name: PASSED"
+               if test $quiet = 0; then
+                       echo "$name: PASSED"
+               fi
                success="yes"
        fi
 fi
 if test -f $name.post; then
-       echo "minitdir exe $name.post"
+       if test $quiet = 0; then
+               echo "minitdir exe $name.post"
+       fi
        echo "minitdir exe $name.post" >> $result
        $shell $name.post $args >> $result
        if test $? -ne 0; then
index 123684a..a1a3761 100644 (file)
@@ -238,6 +238,9 @@ setup_ctx(char* key, char* cert)
        (void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
 #endif
        (void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
+       SSL_CTX_set_security_level(ctx, 0); /* for keys in tests */
+#endif
        if(!SSL_CTX_use_certificate_chain_file(ctx, cert))
                print_exit("cannot read cert");
        if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM))
diff --git a/usr.sbin/unbound/testcode/readzone.c b/usr.sbin/unbound/testcode/readzone.c
new file mode 100644 (file)
index 0000000..94511e5
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * testcode/readzone.c - readzone tool reads zonefiles
+ *
+ * Copyright (c) 2021, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/**
+ * \file
+ * Command to read and echo a zonefile.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <stdint.h>
+#include "sldns/str2wire.h"
+#include "sldns/wire2str.h"
+
+int print_usage(FILE *out, const char *progname)
+{
+       fprintf(out, "usage: %s [ -u ] <zonefile> [<origin>]\n", progname);
+       fprintf(out, "\t-u\tprint in unknown type (RFC3597) format\n");
+       return out == stdout ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int main(int argc, char *const *argv)
+{
+       char *progname = argv[0];
+       uint8_t rr[LDNS_RR_BUF_SIZE];
+       char *str = malloc(1024 * 1024);
+       size_t str_len = sizeof(str);
+       struct sldns_file_parse_state state;
+       FILE *in = NULL;
+       int s = -1;
+       int opt;
+       int print_in_unknown_type_format = 0;
+
+       while ((opt = getopt(argc, argv, "hu")) != -1) {
+               switch (opt) {
+               case 'h':
+                       free(str);
+                       return print_usage(stdout, progname);
+               case 'u':
+                       print_in_unknown_type_format = 1;
+                       break;
+               default:
+                       free(str);
+                       return print_usage(stderr, progname);
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       memset(&state, 0, sizeof(state));
+       state.default_ttl = 3600;
+       state.lineno = 1;
+       if (argc == 2) {
+               state.origin_len = sizeof(state.origin);
+               s = sldns_str2wire_dname_buf(argv[1], state.origin
+                                                   , &state.origin_len);
+               if (s) {
+                       fprintf(stderr, "Error parsing origin: %s\n"
+                                     , sldns_get_errorstr_parse(s));
+                       free(str);
+                       return EXIT_FAILURE;
+               }
+               s = -1;
+       }
+       if (!str)
+               fprintf(stderr, "Memory allocation error: %s\n"
+                             , strerror(errno));
+
+       else if (argc != 1 && argc != 2) {
+               free(str);
+               return print_usage(stderr, progname);
+       }
+
+       else if (!(in = fopen(argv[0], "r")))
+               fprintf(stderr, "Error opening \"%s\": %s\n"
+                             , argv[0], strerror(errno));
+       else while (!feof(in)) {
+               size_t rr_len = sizeof(rr), dname_len = 0;
+               size_t written;
+
+               s = sldns_fp2wire_rr_buf(in, rr, &rr_len, &dname_len, &state);
+               if (s) {
+                       fprintf( stderr, "parse error %d:%d: %s\n"
+                                      , state.lineno, LDNS_WIREPARSE_OFFSET(s)
+                                      , sldns_get_errorstr_parse(s));
+                       break;
+               }
+               if (rr_len == 0)
+                       continue;
+
+               if (print_in_unknown_type_format)
+                       written = sldns_wire2str_rr_unknown_buf(
+                               rr, rr_len, str, str_len);
+               else
+                       written = sldns_wire2str_rr_buf(
+                               rr, rr_len, str, str_len);
+
+               if (written > str_len) {
+                       while (written > str_len)
+                               str_len *= 2;
+                       free(str);
+                       if (!(str = malloc(str_len))) {
+                               fprintf(stderr, "Memory allocation error: %s\n"
+                                             , strerror(errno));
+                               s = -1;
+                               break;
+                       }
+                       if (print_in_unknown_type_format)
+                               (void) sldns_wire2str_rr_unknown_buf(
+                                       rr, rr_len, str, str_len);
+                       else
+                               (void) sldns_wire2str_rr_buf(
+                                       rr, rr_len, str, str_len);
+               }
+               fprintf(stdout, "%s", str);
+       }
+       if (in)
+               fclose(in);
+       free(str);
+       return !in || s ? EXIT_FAILURE : EXIT_SUCCESS;
+}
index 84ce504..2487c14 100644 (file)
@@ -124,8 +124,7 @@ replay_range_delete(struct replay_range* rng)
        free(rng);
 }
 
-/** strip whitespace from end of string */
-static void
+void
 strip_end_white(char* p)
 {
        size_t i;
@@ -227,7 +226,7 @@ read_file_content(FILE* in, int* lineno, struct replay_moment* mom)
                if(strncmp(line, "FILE_END", 8) == 0) {
                        return;
                }
-               if(line[0]) line[strlen(line)-1] = 0; /* remove newline */
+               strip_end_white(line);
                if(!cfg_strlist_insert(last, strdup(line)))
                        fatal_exit("malloc failure");
                last = &( (*last)->next );
@@ -249,7 +248,7 @@ read_assign_step(char* remain, struct replay_moment* mom)
        if(eq != '=')
                fatal_exit("no '=' in assign: %s", remain);
        remain += skip;
-       if(remain[0]) remain[strlen(remain)-1]=0; /* remove newline */
+       strip_end_white(remain);
        mom->string = strdup(remain);
        if(!mom->variable || !mom->string)
                fatal_exit("out of memory");
@@ -318,8 +317,7 @@ replay_moment_read(char* remain, FILE* in, const char* name,
                mom->evt_type = repevt_autotrust_check;
                while(isspace((unsigned char)*remain))
                        remain++;
-               if(strlen(remain)>0 && remain[strlen(remain)-1]=='\n')
-                       remain[strlen(remain)-1] = 0;
+               strip_end_white(remain);
                mom->autotrust_id = strdup(remain);
                if(!mom->autotrust_id) fatal_exit("out of memory");
                read_file_content(in, &pstate->lineno, mom);
@@ -327,8 +325,7 @@ replay_moment_read(char* remain, FILE* in, const char* name,
                mom->evt_type = repevt_tempfile_check;
                while(isspace((unsigned char)*remain))
                        remain++;
-               if(strlen(remain)>0 && remain[strlen(remain)-1]=='\n')
-                       remain[strlen(remain)-1] = 0;
+               strip_end_white(remain);
                mom->autotrust_id = strdup(remain);
                if(!mom->autotrust_id) fatal_exit("out of memory");
                read_file_content(in, &pstate->lineno, mom);
@@ -359,8 +356,7 @@ replay_moment_read(char* remain, FILE* in, const char* name,
                        m++;
                if(!extstrtoaddr(s, &mom->addr, &mom->addrlen))
                        fatal_exit("bad infra_rtt address %s", s);
-               if(strlen(m)>0 && m[strlen(m)-1]=='\n')
-                       m[strlen(m)-1] = 0;
+               strip_end_white(m);
                mom->variable = strdup(remain);
                mom->string = strdup(m);
                if(!mom->string) fatal_exit("out of memory");
@@ -375,8 +371,7 @@ replay_moment_read(char* remain, FILE* in, const char* name,
        if(parse_keyword(&remain, "ADDRESS")) {
                while(isspace((unsigned char)*remain))
                        remain++;
-               if(strlen(remain) > 0) /* remove \n */
-                       remain[strlen(remain)-1] = 0;
+               strip_end_white(remain);
                if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen)) {
                        log_err("line %d: could not parse ADDRESS: %s", 
                                pstate->lineno, remain);
@@ -693,7 +688,11 @@ do_macro_ctime(char* arg)
                return NULL;
        }
        ctime_r(&tt, buf);
-       if(buf[0]) buf[strlen(buf)-1]=0; /* remove trailing newline */
+#ifdef USE_WINSOCK
+       if(strlen(buf) > 10 && buf[7]==' ' && buf[8]=='0')
+               buf[8]=' '; /* fix error in windows ctime */
+#endif
+       strip_end_white(buf);
        return strdup(buf);
 }
 
index 5132cda..0271dff 100644 (file)
@@ -425,6 +425,9 @@ int replay_var_compare(const void* a, const void* b);
 /** get oldest enabled fake timer */
 struct fake_timer* replay_get_oldest_timer(struct replay_runtime* runtime);
 
+/** strip whitespace from end of string */
+void strip_end_white(char* p);
+
 /**
  * Create variable storage
  * @return new or NULL on failure.
index ffdddbe..2bd076e 100644 (file)
@@ -397,11 +397,17 @@ send_em(const char* svr, int udp, int usessl, int noanswer, int onarrival,
 /** SIGPIPE handler */
 static RETSIGTYPE sigh(int sig)
 {
+       char str[] = "Got unhandled signal   \n";
        if(sig == SIGPIPE) {
-               printf("got SIGPIPE, remote connection gone\n");
+               char* strpipe = "got SIGPIPE, remote connection gone\n";
+               /* simple cast to void will not silence Wunused-result */
+               (void)!write(STDOUT_FILENO, strpipe, strlen(strpipe));
                exit(1);
        }
-       printf("Got unhandled signal %d\n", sig);
+       str[21] = '0' + (sig/10)%10;
+       str[22] = '0' + sig%10;
+       /* simple cast to void will not silence Wunused-result */
+       (void)!write(STDOUT_FILENO, str, strlen(str));
        exit(1);
 }
 #endif /* SIGPIPE */
index 5e10779..c929001 100644 (file)
@@ -168,7 +168,7 @@ spool_temp_file_name(int* lineno, FILE* cfg, char* id)
                id++;
        if(*id == '\0') 
                fatal_exit("TEMPFILE_NAME must have id, line %d", *lineno);
-       id[strlen(id)-1]=0; /* remove newline */
+       strip_end_white(id);
        fake_temp_file("_temp_", id, line, sizeof(line));
        fprintf(cfg, "\"%s\"\n", line);
 }
@@ -185,7 +185,7 @@ spool_temp_file(FILE* in, int* lineno, char* id)
                id++;
        if(*id == '\0') 
                fatal_exit("TEMPFILE_CONTENTS must have id, line %d", *lineno);
-       id[strlen(id)-1]=0; /* remove newline */
+       strip_end_white(id);
        fake_temp_file("_temp_", id, line, sizeof(line));
        /* open file and spool to it */
        spool = fopen(line, "w");
@@ -205,7 +205,7 @@ spool_temp_file(FILE* in, int* lineno, char* id)
                        char* tid = parse+17;
                        while(isspace((unsigned char)*tid))
                                tid++;
-                       tid[strlen(tid)-1]=0; /* remove newline */
+                       strip_end_white(tid);
                        fake_temp_file("_temp_", tid, l2, sizeof(l2));
                        snprintf(line, sizeof(line), "$INCLUDE %s\n", l2);
                }
@@ -230,7 +230,7 @@ spool_auto_file(FILE* in, int* lineno, FILE* cfg, char* id)
                id++;
        if(*id == '\0') 
                fatal_exit("AUTROTRUST_FILE must have id, line %d", *lineno);
-       id[strlen(id)-1]=0; /* remove newline */
+       strip_end_white(id);
        fake_temp_file("_auto_", id, line, sizeof(line));
        /* add option for the file */
        fprintf(cfg, "server:   auto-trust-anchor-file: \"%s\"\n", line);
@@ -279,6 +279,7 @@ setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[])
        fprintf(cfg, "          username: \"\"\n");
        fprintf(cfg, "          pidfile: \"\"\n");
        fprintf(cfg, "          val-log-level: 2\n");
+       fprintf(cfg, "          log-servfail: yes\n");
        fprintf(cfg, "remote-control:   control-enable: no\n");
        while(fgets(line, MAX_LINE_LEN-1, in)) {
                parse = line;
index 4b3410c..d193526 100644 (file)
@@ -468,8 +468,13 @@ tmpfilecleanup(void)
        int i;
        char buf[256];
        for(i=0; i<tempno; i++) {
+#ifdef USE_WINSOCK
+               snprintf(buf, sizeof(buf), "unbound.unittest.%u.%d",
+                       (unsigned)getpid(), i);
+#else
                snprintf(buf, sizeof(buf), "/tmp/unbound.unittest.%u.%d",
                        (unsigned)getpid(), i);
+#endif
                if(vbmp) printf("cleanup: unlink %s\n", buf);
                unlink(buf);
        }
@@ -483,8 +488,13 @@ create_tmp_file(const char* s)
        char *fname;
        FILE *out;
        size_t r;
+#ifdef USE_WINSOCK
+       snprintf(buf, sizeof(buf), "unbound.unittest.%u.%d",
+               (unsigned)getpid(), tempno++);
+#else
        snprintf(buf, sizeof(buf), "/tmp/unbound.unittest.%u.%d",
                (unsigned)getpid(), tempno++);
+#endif
        fname = strdup(buf);
        if(!fname) fatal_exit("out of memory");
        /* if no string, just make the name */
@@ -517,8 +527,8 @@ del_tmp_file(char* fname)
 }
 
 /** Add zone from file for testing */
-static struct auth_zone*
-addzone(struct auth_zones* az, const char* name, char* fname)
+struct auth_zone*
+authtest_addzone(struct auth_zones* az, const char* name, char* fname)
 {
        struct auth_zone* z;
        size_t nmlen;
@@ -593,7 +603,7 @@ check_read_exact(const char* name, const char* zone)
 
        az = auth_zones_create();
        unit_assert(az);
-       z = addzone(az, name, fname);
+       z = authtest_addzone(az, name, fname);
        unit_assert(z);
        outf = create_tmp_file(NULL);
        if(!auth_zone_write_file(z, outf)) {
@@ -844,7 +854,7 @@ check_queries(const char* name, const char* zone, struct q_ans* queries)
        fname = create_tmp_file(zone);
        az = auth_zones_create();
        if(!az) fatal_exit("out of memory");
-       z = addzone(az, name, fname);
+       z = authtest_addzone(az, name, fname);
        if(!z) fatal_exit("could not read zone for queries test");
        del_tmp_file(fname);
 
index 22c9ed9..6397f29 100644 (file)
@@ -47,7 +47,7 @@
 #include "sldns/parseutil.h"
 
 /** verbose this unit test */
-static int vbmp = 0; 
+static int vbmp = 0;
 
 /** print buffer to hex into string */
 static void
index c61026f..c18be7b 100644 (file)
@@ -839,52 +839,6 @@ static void respip_test(void)
        respip_conf_actions_test();
 }
 
-#include "services/outside_network.h"
-/** add number of new IDs to the reuse tree, randomly chosen */
-static void tcpid_addmore(struct reuse_tcp* reuse,
-       struct outside_network* outnet, unsigned int addnum)
-{
-       unsigned int i;
-       struct waiting_tcp* w;
-       for(i=0; i<addnum; i++) {
-               uint16_t id = reuse_tcp_select_id(reuse, outnet);
-               unit_assert(!reuse_tcp_by_id_find(reuse, id));
-               w = calloc(1, sizeof(*w));
-               unit_assert(w);
-               w->id = id;
-               w->outnet = outnet;
-               w->next_waiting = (void*)reuse->pending;
-               reuse_tree_by_id_insert(reuse, w);
-       }
-}
-
-/** fill up the reuse ID tree and test assertions */
-static void tcpid_fillup(struct reuse_tcp* reuse,
-       struct outside_network* outnet)
-{
-       int t, numtest=3;
-       for(t=0; t<numtest; t++) {
-               rbtree_init(&reuse->tree_by_id, reuse_id_cmp);
-               tcpid_addmore(reuse, outnet, 65535);
-               reuse_del_readwait(&reuse->tree_by_id);
-       }
-}
-
-/** test TCP ID selection */
-static void tcpid_test(void)
-{
-       struct pending_tcp pend;
-       struct outside_network outnet;
-       unit_show_func("services/outside_network.c", "reuse_tcp_select_id");
-       memset(&pend, 0, sizeof(pend));
-       pend.reuse.pending = &pend;
-       memset(&outnet, 0, sizeof(outnet));
-       outnet.rnd = ub_initstate(NULL);
-       rbtree_init(&pend.reuse.tree_by_id, reuse_id_cmp);
-       tcpid_fillup(&pend.reuse, &outnet);
-       ub_randfree(outnet.rnd);
-}
-
 void unit_show_func(const char* file, const char* func)
 {
        printf("test %s:%s\n", file, func);
@@ -952,8 +906,9 @@ main(int argc, char* argv[])
        slabhash_test();
        infra_test();
        ldns_test();
+       zonemd_test();
+       tcpreuse_test();
        msgparse_test();
-       tcpid_test();
 #ifdef CLIENT_SUBNET
        ecs_test();
 #endif /* CLIENT_SUBNET */
index e5c6109..adcd74f 100644 (file)
@@ -80,5 +80,9 @@ void ecs_test(void);
 void ldns_test(void);
 /** unit test for auth zone functions */
 void authzone_test(void);
+/** unit test for zonemd functions */
+void zonemd_test(void);
+/** unit test for tcp_reuse functions */
+void tcpreuse_test(void);
 
 #endif /* TESTCODE_UNITMAIN_H */
diff --git a/usr.sbin/unbound/testcode/unittcpreuse.c b/usr.sbin/unbound/testcode/unittcpreuse.c
new file mode 100644 (file)
index 0000000..087c6c1
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * testcode/unittcpreuse.c - unit test for tcp_reuse.
+ *
+ * Copyright (c) 2021, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/**
+ * \file
+ * Tests the tcp_reuse functionality.
+ */
+
+#include "config.h"
+#include "testcode/unitmain.h"
+#include "util/log.h"
+#include "util/random.h"
+#include "services/outside_network.h"
+
+/** add number of new IDs to the reuse tree, randomly chosen */
+static void tcpid_addmore(struct reuse_tcp* reuse,
+       struct outside_network* outnet, unsigned int addnum)
+{
+       unsigned int i;
+       struct waiting_tcp* w;
+       for(i=0; i<addnum; i++) {
+               uint16_t id = reuse_tcp_select_id(reuse, outnet);
+               unit_assert(!reuse_tcp_by_id_find(reuse, id));
+               w = calloc(1, sizeof(*w));
+               unit_assert(w);
+               w->id = id;
+               w->outnet = outnet;
+               w->next_waiting = (void*)reuse->pending;
+               reuse_tree_by_id_insert(reuse, w);
+       }
+}
+
+/** fill up the reuse ID tree and test assertions */
+static void tcpid_fillup(struct reuse_tcp* reuse,
+       struct outside_network* outnet)
+{
+       int t, numtest=3;
+       for(t=0; t<numtest; t++) {
+               rbtree_init(&reuse->tree_by_id, reuse_id_cmp);
+               tcpid_addmore(reuse, outnet, 65535);
+               reuse_del_readwait(&reuse->tree_by_id);
+       }
+}
+
+/** test TCP ID selection */
+static void tcpid_test(void)
+{
+       struct pending_tcp pend;
+       struct outside_network outnet;
+       unit_show_func("services/outside_network.c", "reuse_tcp_select_id");
+       memset(&pend, 0, sizeof(pend));
+       pend.reuse.pending = &pend;
+       memset(&outnet, 0, sizeof(outnet));
+       outnet.rnd = ub_initstate(NULL);
+       rbtree_init(&pend.reuse.tree_by_id, reuse_id_cmp);
+       tcpid_fillup(&pend.reuse, &outnet);
+       ub_randfree(outnet.rnd);
+}
+
+/** check that the tree has present number of nodes and the LRU is linked
+ * properly. */
+static void check_tree_and_list(struct outside_network* outnet, int present)
+{
+       int i;
+       struct reuse_tcp *reuse, *next_reuse;
+       unit_assert(present == (int)outnet->tcp_reuse.count);
+       if(present < 1) {
+               unit_assert(outnet->tcp_reuse_first == NULL);
+               unit_assert(outnet->tcp_reuse_last == NULL);
+               return;
+       }
+       unit_assert(outnet->tcp_reuse_first->item_on_lru_list);
+       unit_assert(!outnet->tcp_reuse_first->lru_prev);
+       reuse = outnet->tcp_reuse_first;
+       for(i=0; i<present-1; i++) {
+               unit_assert(reuse->item_on_lru_list);
+               unit_assert(reuse->lru_next);
+               unit_assert(reuse->lru_next != reuse);
+               next_reuse = reuse->lru_next;
+               unit_assert(next_reuse->lru_prev == reuse);
+               reuse = next_reuse;
+       }
+       unit_assert(!reuse->lru_next);
+       unit_assert(outnet->tcp_reuse_last->item_on_lru_list);
+       unit_assert(outnet->tcp_reuse_last == reuse);
+}
+
+/** creates pending_tcp. Copy of outside_network.c:create_pending_tcp without
+ *  the comm_point creation */
+static int create_pending_tcp(struct outside_network* outnet)
+{
+       size_t i;
+       if(outnet->num_tcp == 0)
+               return 1; /* no tcp needed, nothing to do */
+       if(!(outnet->tcp_conns = (struct pending_tcp **)calloc(
+                       outnet->num_tcp, sizeof(struct pending_tcp*))))
+               return 0;
+       for(i=0; i<outnet->num_tcp; i++) {
+               if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1,
+                       sizeof(struct pending_tcp))))
+                       return 0;
+               outnet->tcp_conns[i]->next_free = outnet->tcp_free;
+               outnet->tcp_free = outnet->tcp_conns[i];
+       }
+       return 1;
+}
+
+/** empty the tcp_reuse tree and LRU list */
+static void empty_tree(struct outside_network* outnet)
+{
+       size_t i;
+       struct reuse_tcp* reuse;
+       reuse = outnet->tcp_reuse_first;
+       i = outnet->tcp_reuse.count;
+       while(reuse) {
+               reuse_tcp_remove_tree_list(outnet, reuse);
+               check_tree_and_list(outnet, --i);
+               reuse = outnet->tcp_reuse_first;
+       }
+}
+
+/** check removal of the LRU element on the given position of total elements */
+static void check_removal(struct outside_network* outnet, int position, int total)
+{
+       int i;
+       struct reuse_tcp* reuse;
+       empty_tree(outnet);
+       for(i=0; i<total; i++) {
+               reuse_tcp_insert(outnet, outnet->tcp_conns[i]);
+       }
+       check_tree_and_list(outnet, total);
+       reuse = outnet->tcp_reuse_first;
+       for(i=0; i<position; i++) reuse = reuse->lru_next;
+       reuse_tcp_remove_tree_list(outnet, reuse);
+       check_tree_and_list(outnet, total-1);
+}
+
+/** check snipping off the last element of the LRU with total elements */
+static void check_snip(struct outside_network* outnet, int total)
+{
+       int i;
+       struct reuse_tcp* reuse;
+       empty_tree(outnet);
+       for(i=0; i<total; i++) {
+               reuse_tcp_insert(outnet, outnet->tcp_conns[i]);
+       }
+       check_tree_and_list(outnet, total);
+       reuse = reuse_tcp_lru_snip(outnet);
+       while(reuse) {
+               reuse_tcp_remove_tree_list(outnet, reuse);
+               check_tree_and_list(outnet, --total);
+               reuse = reuse_tcp_lru_snip(outnet);
+       }
+       unit_assert(outnet->tcp_reuse_first == NULL);
+       unit_assert(outnet->tcp_reuse_last == NULL);
+       unit_assert(outnet->tcp_reuse.count == 0);
+}
+
+/** test tcp_reuse tree and LRU list functions */
+static void tcp_reuse_tree_list_test(void)
+{
+       size_t i;
+       struct outside_network outnet;
+       struct reuse_tcp* reuse;
+       memset(&outnet, 0, sizeof(outnet));
+       rbtree_init(&outnet.tcp_reuse, reuse_cmp);
+       outnet.num_tcp = 5;
+       outnet.tcp_reuse_max = outnet.num_tcp;
+       if(!create_pending_tcp(&outnet)) fatal_exit("out of memory");
+       /* add all to the tree */
+       unit_show_func("services/outside_network.c", "reuse_tcp_insert");
+       for(i=0; i<outnet.num_tcp; i++) {
+               reuse_tcp_insert(&outnet, outnet.tcp_conns[i]);
+               check_tree_and_list(&outnet, i+1);
+       }
+       /* check touching */
+       unit_show_func("services/outside_network.c", "reuse_tcp_lru_touch");
+       for(i=0; i<outnet.tcp_reuse.count; i++) {
+               for(reuse = outnet.tcp_reuse_first; reuse->lru_next; reuse = reuse->lru_next);
+               reuse_tcp_lru_touch(&outnet, reuse);
+               check_tree_and_list(&outnet, outnet.num_tcp);
+       }
+       /* check removal */
+       unit_show_func("services/outside_network.c", "reuse_tcp_remove_tree_list");
+       check_removal(&outnet, 2, 5);
+       check_removal(&outnet, 1, 3);
+       check_removal(&outnet, 1, 2);
+       /* check snip */
+       unit_show_func("services/outside_network.c", "reuse_tcp_lru_snip");
+       check_snip(&outnet, 4);
+
+       for(i=0; i<outnet.num_tcp; i++)
+               if(outnet.tcp_conns[i]) {
+                       free(outnet.tcp_conns[i]);
+               }
+       free(outnet.tcp_conns);
+}
+
+void tcpreuse_test(void)
+{
+    unit_show_feature("tcp_reuse");
+    tcpid_test();
+    tcp_reuse_tree_list_test();
+}
diff --git a/usr.sbin/unbound/testcode/unitzonemd.c b/usr.sbin/unbound/testcode/unitzonemd.c
new file mode 100644 (file)
index 0000000..5caa68a
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ * testcode/unitzonemd.c - unit test for zonemd.
+ *
+ * Copyright (c) 2020, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/**
+ * \file
+ * Unit tests for ZONEMD functionality.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include "util/log.h"
+#include "testcode/unitmain.h"
+#include "sldns/str2wire.h"
+#include "services/authzone.h"
+#include "util/data/dname.h"
+#include "util/regional.h"
+#include "validator/val_anchor.h"
+
+#define xstr(s) str(s)
+#define str(s) #s
+#define SRCDIRSTR xstr(SRCDIR)
+
+/** Add zone from file for testing */
+struct auth_zone* authtest_addzone(struct auth_zones* az, const char* name,
+       char* fname);
+
+/** zonemd unit test, generate a zonemd digest and check if correct */
+static void zonemd_generate_test(const char* zname, char* zfile,
+       int scheme, int hashalgo, const char* digest)
+{
+       uint8_t zonemd_hash[512];
+       size_t hashlen = 0;
+       char output[1024+1];
+       size_t i;
+       struct auth_zones* az;
+       struct auth_zone* z;
+       int result;
+       struct regional* region = NULL;
+       struct sldns_buffer* buf = NULL;
+       char* reason = NULL;
+       char* digestdup;
+
+       if(!zonemd_hashalgo_supported(hashalgo))
+               return; /* cannot test unsupported algo */
+
+       /* setup environment */
+       az = auth_zones_create();
+       unit_assert(az);
+       region = regional_create();
+       unit_assert(region);
+       buf = sldns_buffer_new(65535);
+       unit_assert(buf);
+
+       /* read file */
+       z = authtest_addzone(az, zname, zfile);
+       unit_assert(z);
+       lock_rw_wrlock(&z->lock);
+       z->zonemd_check = 1;
+       lock_rw_unlock(&z->lock);
+
+       /* create zonemd digest */
+       result = auth_zone_generate_zonemd_hash(z, scheme, hashalgo,
+               zonemd_hash, sizeof(zonemd_hash), &hashlen, region, buf,
+               &reason);
+       if(reason) printf("zonemd failure reason: %s\n", reason);
+       unit_assert(result);
+
+       /* check digest */
+       unit_assert(hashlen*2+1 <= sizeof(output));
+       for(i=0; i<hashlen; i++) {
+               const char* hexl = "0123456789ABCDEF";
+               output[i*2] = hexl[(zonemd_hash[i]&0xf0)>>4];
+               output[i*2+1] = hexl[zonemd_hash[i]&0xf];
+       }
+       output[hashlen*2] = 0;
+       digestdup = strdup(digest);
+       unit_assert(digestdup);
+       for(i=0; i<strlen(digestdup); i++) {
+               digestdup[i] = toupper(digestdup[i]);
+       }
+       if(verbosity >= VERB_ALGO) {
+               char zname[255+1];
+               dname_str(z->name, zname);
+               printf("zonemd generated for %s in %s with "
+                       "scheme=%d hashalgo=%d\n", zname, z->zonefile,
+                       scheme, hashalgo);
+               printf("digest %s\n", output);
+               printf("wanted %s\n", digestdup);
+       }
+       unit_assert(strcmp(output, digestdup) == 0);
+
+       /* delete environment */
+       free(digestdup);
+       auth_zones_delete(az);
+       regional_destroy(region);
+       sldns_buffer_free(buf);
+
+       if(verbosity >= VERB_ALGO) {
+               printf("\n");
+       }
+}
+
+/** loop over files and test generated zonemd digest */
+static void zonemd_generate_tests(void)
+{
+       unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_hash");
+       zonemd_generate_test("example.org", SRCDIRSTR "/testdata/zonemd.example1.zone",
+               1, 2, "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.1 */
+       zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a1.zone",
+               1, 1, "c68090d90a7aed716bc459f9340e3d7c1370d4d24b7e2fc3a1ddc0b9a87153b9a9713b3c9ae5cc27777f98b8e730044c");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.2 */
+       zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a2.zone",
+               1, 1, "31cefb03814f5062ad12fa951ba0ef5f8da6ae354a415767246f7dc932ceb1e742a2108f529db6a33a11c01493de358d");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.3 SHA384 digest */
+       zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a3.zone",
+               1, 1, "62e6cf51b02e54b9b5f967d547ce43136792901f9f88e637493daaf401c92c279dd10f0edb1c56f8080211f8480ee306");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.3 SHA512 digest*/
+       zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a3.zone",
+               1, 2, "08cfa1115c7b948c4163a901270395ea226a930cd2cbcf2fa9a5e6eb85f37c8a4e114d884e66f176eab121cb02db7d652e0cc4827e7a3204f166b47e5613fd27");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.4 */
+       zonemd_generate_test("uri.arpa", SRCDIRSTR "/testdata/zonemd.example_a4.zone",
+               1, 1, "1291b78ddf7669b1a39d014d87626b709b55774c5d7d58fadc556439889a10eaf6f11d615900a4f996bd46279514e473");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.5 */
+       zonemd_generate_test("root-servers.net", SRCDIRSTR "/testdata/zonemd.example_a5.zone",
+               1, 1, "f1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a978a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79");
+}
+
+/** test the zonemd check routine */
+static void zonemd_check_test(void)
+{
+       const char* zname = "example.org";
+       char* zfile = SRCDIRSTR "/testdata/zonemd.example1.zone";
+       int scheme = 1;
+       int hashalgo = 2;
+       const char* digest = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7";
+       const char* digestwrong = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D48100";
+       uint8_t hash[512], hashwrong[512];
+       size_t hashlen = 0, hashwronglen = 0;
+       struct auth_zones* az;
+       struct auth_zone* z;
+       int result;
+       struct regional* region = NULL;
+       struct sldns_buffer* buf = NULL;
+       char* reason = NULL;
+
+       if(!zonemd_hashalgo_supported(hashalgo))
+               return; /* cannot test unsupported algo */
+       unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_check");
+
+       /* setup environment */
+       az = auth_zones_create();
+       unit_assert(az);
+       region = regional_create();
+       unit_assert(region);
+       buf = sldns_buffer_new(65535);
+       unit_assert(buf);
+
+       /* read file */
+       z = authtest_addzone(az, zname, zfile);
+       unit_assert(z);
+       lock_rw_wrlock(&z->lock);
+       z->zonemd_check = 1;
+       lock_rw_unlock(&z->lock);
+       hashlen = sizeof(hash);
+       if(sldns_str2wire_hex_buf(digest, hash, &hashlen) != 0) {
+               unit_assert(0); /* parse failure */
+       }
+       hashwronglen = sizeof(hashwrong);
+       if(sldns_str2wire_hex_buf(digestwrong, hashwrong, &hashwronglen) != 0) {
+               unit_assert(0); /* parse failure */
+       }
+
+       /* check return values of the check routine */
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hash, hashlen, region, buf, &reason);
+       unit_assert(result && reason == NULL);
+       result = auth_zone_generate_zonemd_check(z, 241, hashalgo,
+               hash, hashlen, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "unsupported scheme")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, 242,
+               hash, hashlen, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "unsupported algorithm")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hash, 2, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "digest length too small, less than 12")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hashwrong, hashwronglen, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "incorrect digest")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hashwrong, hashwronglen-3, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "incorrect digest length")==0);
+
+       /* delete environment */
+       auth_zones_delete(az);
+       regional_destroy(region);
+       sldns_buffer_free(buf);
+
+       if(verbosity >= VERB_ALGO) {
+               printf("\n");
+       }
+}
+
+/** zonemd test verify */
+static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
+       char* date_override, char* result_wanted)
+{
+       time_t now = 0;
+       struct module_stack mods;
+       struct module_env env;
+       char* result = NULL;
+       struct auth_zone* z;
+
+       /* setup test harness */
+       memset(&mods, 0, sizeof(mods));
+       memset(&env, 0, sizeof(env));
+       env.scratch = regional_create();
+       if(!env.scratch)
+               fatal_exit("out of memory");
+       env.scratch_buffer = sldns_buffer_new(65553);
+       if(!env.scratch_buffer)
+               fatal_exit("out of memory");
+       env.cfg = config_create();
+       if(!env.cfg)
+               fatal_exit("out of memory");
+       env.now = &now;
+       env.cfg->val_date_override = cfg_convert_timeval(date_override);
+       if(!env.cfg->val_date_override)
+               fatal_exit("could not parse datetime %s", date_override);
+       if(env.cfg->module_conf)
+               free(env.cfg->module_conf);
+       env.cfg->module_conf = strdup("validator iterator");
+       if(!env.cfg->module_conf)
+               fatal_exit("out of memory");
+       if(tastr) {
+               if(!cfg_strlist_insert(&env.cfg->trust_anchor_list,
+                       strdup(tastr)))
+                       fatal_exit("out of memory");
+       }
+       env.anchors = anchors_create();
+       if(!env.anchors)
+               fatal_exit("out of memory");
+       env.auth_zones = auth_zones_create();
+       if(!env.auth_zones)
+               fatal_exit("out of memory");
+       modstack_init(&mods);
+       if(!modstack_setup(&mods, env.cfg->module_conf, &env))
+               fatal_exit("could not modstack_setup");
+       env.mesh = mesh_create(&mods, &env);
+       if(!env.mesh)
+               fatal_exit("out of memory");
+
+       /* load data */
+       z = authtest_addzone(env.auth_zones, zname, zfile);
+       if(!z)
+               fatal_exit("could not addzone %s %s", zname, zfile);
+
+       /* test */
+       lock_rw_wrlock(&z->lock);
+       z->zonemd_check = 1;
+       auth_zone_verify_zonemd(z, &env, &mods, &result, 1, 0);
+       lock_rw_unlock(&z->lock);
+       if(verbosity >= VERB_ALGO) {
+               printf("auth zone %s: ZONEMD verification %s: %s\n", zname,
+                       (strcmp(result, "ZONEMD verification successful")==0?"successful":"failed"),
+                       result);
+       }
+       if(!result)
+               fatal_exit("out of memory");
+       unit_assert(strcmp(result, result_wanted) == 0);
+       if(strcmp(result, "ZONEMD verification successful") == 0 ||
+               strcmp(result, "DNSSEC verified nonexistence of ZONEMD") == 0 ||
+               strcmp(result, "no ZONEMD present") == 0) {
+               lock_rw_rdlock(&z->lock);
+               unit_assert(!z->zone_expired);
+               lock_rw_unlock(&z->lock);
+       } else {
+               lock_rw_rdlock(&z->lock);
+               unit_assert(z->zone_expired);
+               lock_rw_unlock(&z->lock);
+       }
+       free(result);
+
+       /* desetup test harness */
+       mesh_delete(env.mesh);
+       modstack_desetup(&mods, &env);
+       auth_zones_delete(env.auth_zones);
+       anchors_delete(env.anchors);
+       config_delete(env.cfg);
+       regional_destroy(env.scratch);
+       sldns_buffer_free(env.scratch_buffer);
+
+       if(verbosity >= VERB_ALGO) {
+               printf("\n");
+       }
+}
+
+/** zonemd test verify suite */
+static void zonemd_verify_tests(void)
+{
+       unit_show_func("services/authzone.c", "auth_zone_verify_zonemd");
+       /* give trustanchor for unsigned zone, should fail */
+       zonemd_verify_test("example.org",
+               SRCDIRSTR "/testdata/zonemd.example1.zone",
+               "example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20180302005009",
+               "verify DNSKEY RRset with trust anchor failed: have trust anchor, but zone has no DNSKEY");
+       /* unsigned zone without ZONEMD in it */
+       zonemd_verify_test("example.org",
+               SRCDIRSTR "/testdata/zonemd.example1.zone",
+               NULL,
+               "20180302005009",
+               "no ZONEMD present");
+       /* no trust anchor, so it succeeds for zone with a correct ZONEMD */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example2.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD verification successful");
+       /* trust anchor for another zone, so it is indeterminate */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example2.zone",
+               "example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20180302005009",
+               "ZONEMD verification successful");
+
+       /* load a DNSSEC signed zone, but no trust anchor */
+       /* this zonefile has an incorrect ZONEMD digest, with correct
+        * DNSSEC signature. */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example3.zone",
+               NULL,
+               "20180302005009",
+               "incorrect digest");
+       /* load a DNSSEC zone with NSEC3, but no trust anchor */
+       /* this zonefile has an incorrect ZONEMD digest, with correct
+        * DNSSEC signature. */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example4.zone",
+               NULL,
+               "20180302005009",
+               "incorrect digest");
+       /* valid zonemd, in dnssec signed zone, no trust anchor*/
+       /* this zonefile has a correct ZONEMD digest and
+        * correct DNSSEC signature */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example5.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD verification successful");
+       /* valid zonemd, in dnssec NSEC3 zone, no trust anchor*/
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example6.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD verification successful");
+
+       /* load a DNSSEC signed zone with a trust anchor, valid ZONEMD */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example5.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "ZONEMD verification successful");
+       /* load a DNSSEC NSEC3 signed zone with a trust anchor, valid ZONEMD */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example6.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "ZONEMD verification successful");
+
+       /* load a DNSSEC NSEC zone without ZONEMD */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example7.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "DNSSEC verified nonexistence of ZONEMD");
+       /* load a DNSSEC NSEC3 zone without ZONEMD */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example8.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "DNSSEC verified nonexistence of ZONEMD");
+
+       /* load DNSSEC zone but RRSIG on ZONEMD is wrong */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example9.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+#ifdef HAVE_SSL
+               "DNSSEC verify failed for ZONEMD RRset: signature crypto failed"
+#else /* HAVE_NETTLE */
+               "DNSSEC verify failed for ZONEMD RRset: RSA signature verification failed"
+#endif
+               );
+       /* load DNSSEC zone but RRSIG on SOA is wrong */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example10.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+#ifdef HAVE_SSL
+               "DNSSEC verify failed for SOA RRset: signature crypto failed"
+#else /* HAVE_NETTLE */
+               "DNSSEC verify failed for SOA RRset: RSA signature verification failed"
+#endif
+               );
+
+       /* load DNSSEC zone without ZONEMD, but NSEC bitmap says it exists */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example11.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "DNSSEC NSEC bitmap says type ZONEMD exists");
+       /* load DNSSEC zone without ZONEMD, but NSEC3 bitmap says it exists */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example12.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "DNSSEC NSEC3 bitmap says type ZONEMD exists");
+
+       /* load DNSSEC zone without ZONEMD, but RRSIG on NSEC not okay */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example13.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+#ifdef HAVE_SSL
+               "DNSSEC verify failed for NSEC RRset: signature crypto failed"
+#else /* HAVE_NETTLE */
+               "DNSSEC verify failed for NSEC RRset: RSA signature verification failed"
+#endif
+               );
+       /* load DNSSEC zone without ZONEMD, but RRSIG on NSEC3 not okay */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example14.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+#ifdef HAVE_SSL
+               "DNSSEC verify failed for NSEC3 RRset: signature crypto failed"
+#else /* HAVE_NETTLE */
+               "DNSSEC verify failed for NSEC3 RRset: RSA signature verification failed"
+#endif
+               );
+
+       /* load DNSSEC zone, with ZONEMD, but DNSKEY RRSIG is not okay. */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example15.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+#ifdef HAVE_SSL
+               "verify DNSKEY RRset with trust anchor failed: signature crypto failed"
+#else /* HAVE_NETTLE */
+               "verify DNSKEY RRset with trust anchor failed: RSA signature verification failed"
+#endif
+               );
+       /* load DNSSEC zone, but trust anchor mismatches DNSKEY */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example5.zone",
+               /* okay anchor is
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af", */
+               "example.com. IN DS 55566 8 2 0000000000111111222223333444444dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "verify DNSKEY RRset with trust anchor failed: DS hash mismatches key");
+       /* load DNSSEC zone, but trust anchor fails because the zone
+        * has expired signatures.  We set the date for it */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example5.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               /* okay date: "20201020135527", */
+               "20221020135527",
+               "verify DNSKEY RRset with trust anchor failed: signature expired");
+
+       /* duplicate zonemd with same scheme and algorithm */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example16.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD RRSet contains more than one RR with the same scheme and hash algorithm");
+       /* different capitalisation of ns name and owner names, should
+        * be canonicalized. */
+       zonemd_verify_test("example.com",
+               SRCDIRSTR "/testdata/zonemd.example17.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD verification successful");
+}
+
+/** zonemd unit tests */
+void zonemd_test(void)
+{
+       unit_show_feature("zonemd");
+       zonemd_generate_tests();
+       zonemd_check_test();
+       zonemd_verify_tests();
+}