All the heavy lifting was done by sthen while updating unbound,
thanks!

I'll put this in sometimes during the weekend, but tests
would still be welcome.

diff --git libunbound/config.h libunbound/config.h
index 7b0ba84679b..8c7028b6ab9 100644
--- libunbound/config.h
+++ libunbound/config.h
@@ -87,6 +87,10 @@
    if you don't. */
 #define HAVE_DECL_ARC4RANDOM_UNIFORM 1
 
+/* Define to 1 if you have the declaration of `evsignal_assign', and to 0 if
+   you don't. */
+#define HAVE_DECL_EVSIGNAL_ASSIGN 0
+
 /* Define to 1 if you have the declaration of `inet_ntop', and to 0 if you
    don't. */
 #define HAVE_DECL_INET_NTOP 1
@@ -167,6 +171,9 @@
 /* Define to 1 if you have the `ERR_load_crypto_strings' function. */
 #define HAVE_ERR_LOAD_CRYPTO_STRINGS 1
 
+/* Define to 1 if you have the `event_assign' function. */
+/* #undef HAVE_EVENT_ASSIGN */
+
 /* Define to 1 if you have the `event_base_free' function. */
 #define HAVE_EVENT_BASE_FREE 1
 
@@ -407,7 +414,7 @@
 /* Define to 1 if you have the `RAND_cleanup' function. */
 #define HAVE_RAND_CLEANUP 1
 
-/* Define to 1 if you have the `reallocarray' function. */
+/* If we have reallocarray(3) */
 #define HAVE_REALLOCARRAY 1
 
 /* Define to 1 if you have the `recvmsg' function. */
@@ -664,7 +671,7 @@
 #define PACKAGE_NAME "unbound"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "unbound 1.9.1"
+#define PACKAGE_STRING "unbound 1.9.2"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "unbound"
@@ -673,7 +680,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.9.1"
+#define PACKAGE_VERSION "1.9.2"
 
 /* default pidfile location */
 #define PIDFILE ""
@@ -695,7 +702,7 @@
 #define ROOT_CERT_FILE "/var/unbound/etc/icannbundle.pem"
 
 /* version number for resource files */
-#define RSRC_PACKAGE_VERSION 1,9,1,0
+#define RSRC_PACKAGE_VERSION 1,9,2,0
 
 /* Directory to chdir to */
 #define RUN_DIR "/var/unbound/etc"
@@ -966,8 +973,14 @@
 
 
 
+#ifndef _OPENBSD_SOURCE
+#define _OPENBSD_SOURCE 1
+#endif
+
 #ifndef UNBOUND_DEBUG
+# ifndef NDEBUG
 #  define NDEBUG
+# endif
 #endif
 
 /** Use small-ldns codebase */
diff --git libunbound/iterator/iter_utils.c libunbound/iterator/iter_utils.c
index be7965a60e3..2ab55ceb497 100644
--- libunbound/iterator/iter_utils.c
+++ libunbound/iterator/iter_utils.c
@@ -1211,6 +1211,19 @@ iter_scrub_ds(struct dns_msg* msg, struct 
ub_packed_rrset_key* ns, uint8_t* z)
        }
 }
 
+void
+iter_scrub_nxdomain(struct dns_msg* msg)
+{
+       if(msg->rep->an_numrrsets == 0)
+               return;
+
+       memmove(msg->rep->rrsets, msg->rep->rrsets+msg->rep->an_numrrsets,
+               sizeof(struct ub_packed_rrset_key*) *
+               (msg->rep->rrset_count-msg->rep->an_numrrsets));
+       msg->rep->rrset_count -= msg->rep->an_numrrsets;
+       msg->rep->an_numrrsets = 0;
+}
+
 void iter_dec_attempts(struct delegpt* dp, int d)
 {
        struct delegpt_addr* a;
diff --git libunbound/iterator/iter_utils.h libunbound/iterator/iter_utils.h
index ccfb280224b..f771930bba2 100644
--- libunbound/iterator/iter_utils.h
+++ libunbound/iterator/iter_utils.h
@@ -334,6 +334,13 @@ int iter_get_next_root(struct iter_hints* hints, struct 
iter_forwards* fwd,
 void iter_scrub_ds(struct dns_msg* msg, struct ub_packed_rrset_key* ns,
        uint8_t* z);
 
+/**
+ * Prepare an NXDOMAIN message to be used for a subdomain answer by removing 
all
+ * RRs from the ANSWER section.
+ * @param msg: the response to scrub.
+ */
+void iter_scrub_nxdomain(struct dns_msg* msg);
+
 /**
  * Remove query attempts from all available ips. For 0x20.
  * @param dp: delegpt.
diff --git libunbound/iterator/iterator.c libunbound/iterator/iterator.c
index c73fb517748..c906c271448 100644
--- libunbound/iterator/iterator.c
+++ libunbound/iterator/iterator.c
@@ -2718,8 +2718,15 @@ processQueryResponse(struct module_qstate* qstate, 
struct iter_qstate* iq,
                        && !(iq->chase_flags & BIT_RD)) {
                        if(FLAGS_GET_RCODE(iq->response->rep->flags) != 
                                LDNS_RCODE_NOERROR) {
-                               if(qstate->env->cfg->qname_minimisation_strict)
-                                       return final_state(iq);
+                               if(qstate->env->cfg->qname_minimisation_strict) 
{
+                                       
if(FLAGS_GET_RCODE(iq->response->rep->flags) ==
+                                               LDNS_RCODE_NXDOMAIN) {
+                                               
iter_scrub_nxdomain(iq->response);
+                                               return final_state(iq);
+                                       }
+                                       return error_response(qstate, id,
+                                               LDNS_RCODE_SERVFAIL);
+                               }
                                /* Best effort qname-minimisation. 
                                 * Stop minimising and send full query when
                                 * RCODE is not NOERROR. */
diff --git libunbound/services/authzone.c libunbound/services/authzone.c
index a87c2274fb9..1426f423a1b 100644
--- libunbound/services/authzone.c
+++ libunbound/services/authzone.c
@@ -2042,11 +2042,13 @@ auth_xfer_delete(struct auth_xfer* xfr)
        if(xfr->task_probe) {
                auth_free_masters(xfr->task_probe->masters);
                comm_point_delete(xfr->task_probe->cp);
+               comm_timer_delete(xfr->task_probe->timer);
                free(xfr->task_probe);
        }
        if(xfr->task_transfer) {
                auth_free_masters(xfr->task_transfer->masters);
                comm_point_delete(xfr->task_transfer->cp);
+               comm_timer_delete(xfr->task_transfer->timer);
                if(xfr->task_transfer->chunks_first) {
                        auth_chunks_delete(xfr->task_transfer);
                }
@@ -2746,6 +2748,7 @@ az_nsec3_insert(struct auth_zone* z, struct regional* 
region,
  *     that is an exact match that should exist for it.
  *     If that does not exist, a higher exact match + nxproof is enabled
  *     (for some sort of opt-out empty nonterminal cases).
+ * nodataproof: search for exact match and include that instead.
  * ceproof: include ce proof NSEC3 (omitted for wildcard replies).
  * nxproof: include denial of the qname.
  * wcproof: include denial of wildcard (wildcard.ce).
@@ -2753,7 +2756,8 @@ az_nsec3_insert(struct auth_zone* z, struct regional* 
region,
 static int
 az_add_nsec3_proof(struct auth_zone* z, struct regional* region,
        struct dns_msg* msg, uint8_t* cenm, size_t cenmlen, uint8_t* qname,
-       size_t qname_len, int ceproof, int nxproof, int wcproof)
+       size_t qname_len, int nodataproof, int ceproof, int nxproof,
+       int wcproof)
 {
        int algo;
        size_t iter, saltlen;
@@ -2764,6 +2768,19 @@ az_add_nsec3_proof(struct auth_zone* z, struct regional* 
region,
        /* find parameters of nsec3 proof */
        if(!az_nsec3_param(z, &algo, &iter, &salt, &saltlen))
                return 1; /* no nsec3 */
+       if(nodataproof) {
+               /* see if the node has a hash of itself for the nodata
+                * proof nsec3, this has to be an exact match nsec3. */
+               struct auth_data* match;
+               match = az_nsec3_find_exact(z, qname, qname_len, algo,
+                       iter, salt, saltlen);
+               if(match) {
+                       if(!az_nsec3_insert(z, region, msg, match))
+                               return 0;
+                       /* only nodata NSEC3 needed, no CE or others. */
+                       return 1;
+               }
+       }
        /* find ce that has an NSEC3 */
        if(ceproof) {
                node = az_nsec3_find_ce(z, &cenm, &cenmlen, &no_exact_ce,
@@ -2916,7 +2933,7 @@ az_generate_notype_answer(struct auth_zone* z, struct 
regional* region,
                /* DNSSEC denial NSEC3 */
                if(!az_add_nsec3_proof(z, region, msg, node->name,
                        node->namelen, msg->qinfo.qname,
-                       msg->qinfo.qname_len, 1, 0, 0))
+                       msg->qinfo.qname_len, 1, 1, 0, 0))
                        return 0;
        }
        return 1;
@@ -2943,7 +2960,7 @@ az_generate_referral_answer(struct auth_zone* z, struct 
regional* region,
                } else {
                        if(!az_add_nsec3_proof(z, region, msg, ce->name,
                                ce->namelen, msg->qinfo.qname,
-                               msg->qinfo.qname_len, 1, 0, 0))
+                               msg->qinfo.qname_len, 1, 1, 0, 0))
                                return 0;
                }
        }
@@ -2982,6 +2999,7 @@ az_generate_wildcard_answer(struct auth_zone* z, struct 
query_info* qinfo,
        struct auth_data* wildcard, struct auth_data* node)
 {
        struct auth_rrset* rrset, *nsec;
+       int insert_ce = 0;
        if((rrset=az_domain_rrset(wildcard, qinfo->qtype)) != NULL) {
                /* wildcard has type, add it */
                if(!msg_add_rrset_an(z, region, msg, wildcard, rrset))
@@ -3008,6 +3026,10 @@ az_generate_wildcard_answer(struct auth_zone* z, struct 
query_info* qinfo,
                /* call other notype routine for dnssec notype denials */
                if(!az_generate_notype_answer(z, region, msg, wildcard))
                        return 0;
+               /* because the notype, there is no positive data with an
+                * RRSIG that indicates the wildcard position.  Thus the
+                * wildcard qname denial needs to have a CE nsec3. */
+               insert_ce = 1;
        }
 
        /* ce and node for dnssec denial of wildcard original name */
@@ -3019,7 +3041,7 @@ az_generate_wildcard_answer(struct auth_zone* z, struct 
query_info* qinfo,
                dname_remove_label(&wildup, &wilduplen);
                if(!az_add_nsec3_proof(z, region, msg, wildup,
                        wilduplen, msg->qinfo.qname,
-                       msg->qinfo.qname_len, 0, 1, 0))
+                       msg->qinfo.qname_len, 0, insert_ce, 1, 0))
                        return 0;
        }
 
@@ -3045,7 +3067,7 @@ az_generate_nxdomain_answer(struct auth_zone* z, struct 
regional* region,
        } else if(ce) {
                if(!az_add_nsec3_proof(z, region, msg, ce->name,
                        ce->namelen, msg->qinfo.qname,
-                       msg->qinfo.qname_len, 1, 1, 1))
+                       msg->qinfo.qname_len, 0, 1, 1, 1))
                        return 0;
        }
        return 1;
@@ -4953,6 +4975,9 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct 
module_env* env,
 static void
 xfr_transfer_disown(struct auth_xfer* xfr)
 {
+       /* remove timer (from this worker's event base) */
+       comm_timer_delete(xfr->task_transfer->timer);
+       xfr->task_transfer->timer = NULL;
        /* remove the commpoint */
        comm_point_delete(xfr->task_transfer->cp);
        xfr->task_transfer->cp = NULL;
@@ -5034,6 +5059,9 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct 
module_env* env)
        struct sockaddr_storage addr;
        socklen_t addrlen = 0;
        struct auth_master* master = xfr->task_transfer->master;
+       char *auth_name = NULL;
+       struct timeval t;
+       int timeout;
        if(!master) return 0;
        if(master->allow_notify) return 0; /* only for notify */
 
@@ -5042,7 +5070,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct 
module_env* env)
                addrlen = xfr->task_transfer->scan_addr->addrlen;
                memmove(&addr, &xfr->task_transfer->scan_addr->addr, addrlen);
        } else {
-               if(!extstrtoaddr(master->host, &addr, &addrlen)) {
+               if(!authextstrtoaddr(master->host, &addr, &addrlen, 
&auth_name)) {
                        /* the ones that are not in addr format are supposed
                         * to be looked up.  The lookup has failed however,
                         * so skip them */
@@ -5059,25 +5087,46 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct 
module_env* env)
                comm_point_delete(xfr->task_transfer->cp);
                xfr->task_transfer->cp = NULL;
        }
+       if(!xfr->task_transfer->timer) {
+               xfr->task_transfer->timer = comm_timer_create(env->worker_base,
+                       auth_xfer_transfer_timer_callback, xfr);
+               if(!xfr->task_transfer->timer) {
+                       log_err("malloc failure");
+                       return 0;
+               }
+       }
+       timeout = AUTH_TRANSFER_TIMEOUT;
+#ifndef S_SPLINT_S
+        t.tv_sec = timeout/1000;
+        t.tv_usec = (timeout%1000)*1000;
+#endif
 
        if(master->http) {
                /* perform http fetch */
                /* store http port number into sockaddr,
                 * unless someone used unbound's host@port notation */
+               xfr->task_transfer->on_ixfr = 0;
                if(strchr(master->host, '@') == NULL)
                        sockaddr_store_port(&addr, addrlen, master->port);
                xfr->task_transfer->cp = outnet_comm_point_for_http(
                        env->outnet, auth_xfer_transfer_http_callback, xfr,
-                       &addr, addrlen, AUTH_TRANSFER_TIMEOUT, master->ssl,
-                       master->host, master->file);
+                       &addr, addrlen, -1, master->ssl, master->host,
+                       master->file);
                if(!xfr->task_transfer->cp) {
-                       char zname[255+1];
+                       char zname[255+1], as[256];
                        dname_str(xfr->name, zname);
+                       addr_to_str(&addr, addrlen, as, sizeof(as));
                        verbose(VERB_ALGO, "cannot create http cp "
-                               "connection for %s to %s", zname,
-                               master->host);
+                               "connection for %s to %s", zname, as);
                        return 0;
                }
+               comm_timer_set(xfr->task_transfer->timer, &t);
+               if(verbosity >= VERB_ALGO) {
+                       char zname[255+1], as[256];
+                       dname_str(xfr->name, zname);
+                       addr_to_str(&addr, addrlen, as, sizeof(as));
+                       verbose(VERB_ALGO, "auth zone %s transfer next HTTP 
fetch from %s started", zname, as);
+               }
                return 1;
        }
 
@@ -5091,14 +5140,24 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct 
module_env* env)
        /* connect on fd */
        xfr->task_transfer->cp = outnet_comm_point_for_tcp(env->outnet,
                auth_xfer_transfer_tcp_callback, xfr, &addr, addrlen,
-               env->scratch_buffer, AUTH_TRANSFER_TIMEOUT);
+               env->scratch_buffer, -1,
+               auth_name != NULL, auth_name);
        if(!xfr->task_transfer->cp) {
-               char zname[255+1];
-               dname_str(xfr->name, zname);
+               char zname[255+1], as[256];
+               dname_str(xfr->name, zname);
+               addr_to_str(&addr, addrlen, as, sizeof(as));
                verbose(VERB_ALGO, "cannot create tcp cp connection for "
-                       "xfr %s to %s", zname, master->host);
+                       "xfr %s to %s", zname, as);
                return 0;
        }
+       comm_timer_set(xfr->task_transfer->timer, &t);
+       if(verbosity >= VERB_ALGO) {
+               char zname[255+1], as[256];
+               dname_str(xfr->name, zname);
+               addr_to_str(&addr, addrlen, as, sizeof(as));
+               verbose(VERB_ALGO, "auth zone %s transfer next %s fetch from %s 
started", zname, 
+                       (xfr->task_transfer->on_ixfr?"IXFR":"AXFR"), as);
+       }
        return 1;
 }
 
@@ -5116,6 +5175,11 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, 
struct module_env* env)
                         * and we may then get an instant cache response,
                         * and that calls the callback just like a full
                         * lookup and lookup failures also call callback */
+                       if(verbosity >= VERB_ALGO) {
+                               char zname[255+1];
+                               dname_str(xfr->name, zname);
+                               verbose(VERB_ALGO, "auth zone %s transfer next 
target lookup", zname);
+                       }
                        lock_basic_unlock(&xfr->lock);
                        return;
                }
@@ -5134,6 +5198,11 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, 
struct module_env* env)
                /* failed to fetch, next master */
                xfr_transfer_nextmaster(xfr);
        }
+       if(verbosity >= VERB_ALGO) {
+               char zname[255+1];
+               dname_str(xfr->name, zname);
+               verbose(VERB_ALGO, "auth zone %s transfer failed, wait", zname);
+       }
 
        /* we failed to fetch the zone, move to wait task
         * use the shorter retry timeout */
@@ -5231,8 +5300,26 @@ void auth_xfer_transfer_lookup_callback(void* arg, int 
rcode, sldns_buffer* buf,
                        if(answer) {
                                xfr_master_add_addrs(xfr->task_transfer->
                                        lookup_target, answer, wanted_qtype);
+                       } else {
+                               if(verbosity >= VERB_ALGO) {
+                                       char zname[255+1];
+                                       dname_str(xfr->name, zname);
+                                       verbose(VERB_ALGO, "auth zone %s host 
%s type %s transfer lookup has nodata", zname, 
xfr->task_transfer->lookup_target->host, 
(xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
+                               }
+                       }
+               } else {
+                       if(verbosity >= VERB_ALGO) {
+                               char zname[255+1];
+                               dname_str(xfr->name, zname);
+                               verbose(VERB_ALGO, "auth zone %s host %s type 
%s transfer lookup has no answer", zname, 
xfr->task_transfer->lookup_target->host, 
(xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
                        }
                }
+       } else {
+               if(verbosity >= VERB_ALGO) {
+                       char zname[255+1];
+                       dname_str(xfr->name, zname);
+                       verbose(VERB_ALGO, "auth zone %s host %s type %s 
transfer lookup failed", zname, xfr->task_transfer->lookup_target->host, 
(xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
+               }
        }
        if(xfr->task_transfer->lookup_target->list &&
                xfr->task_transfer->lookup_target == 
xfr_transfer_current_master(xfr))
@@ -5616,6 +5703,46 @@ process_list_end_transfer(struct auth_xfer* xfr, struct 
module_env* env)
        xfr_transfer_nexttarget_or_end(xfr, env);
 }
 
+/** callback for the task_transfer timer */
+void
+auth_xfer_transfer_timer_callback(void* arg)
+{
+       struct auth_xfer* xfr = (struct auth_xfer*)arg;
+       struct module_env* env;
+       int gonextonfail = 1;
+       log_assert(xfr->task_transfer);
+       lock_basic_lock(&xfr->lock);
+       env = xfr->task_transfer->env;
+       if(env->outnet->want_to_quit) {
+               lock_basic_unlock(&xfr->lock);
+               return; /* stop on quit */
+       }
+
+       verbose(VERB_ALGO, "xfr stopped, connection timeout to %s",
+               xfr->task_transfer->master->host);
+
+       /* see if IXFR caused the failure, if so, try AXFR */
+       if(xfr->task_transfer->on_ixfr) {
+               xfr->task_transfer->ixfr_possible_timeout_count++;
+               if(xfr->task_transfer->ixfr_possible_timeout_count >=
+                       NUM_TIMEOUTS_FALLBACK_IXFR) {
+                       verbose(VERB_ALGO, "xfr to %s, fallback "
+                               "from IXFR to AXFR (because of timeouts)",
+                               xfr->task_transfer->master->host);
+                       xfr->task_transfer->ixfr_fail = 1;
+                       gonextonfail = 0;
+               }
+       }
+
+       /* delete transferred data from list */
+       auth_chunks_delete(xfr->task_transfer);
+       comm_point_delete(xfr->task_transfer->cp);
+       xfr->task_transfer->cp = NULL;
+       if(gonextonfail)
+               xfr_transfer_nextmaster(xfr);
+       xfr_transfer_nexttarget_or_end(xfr, env);
+}
+
 /** callback for task_transfer tcp connections */
 int
 auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err,
@@ -5632,6 +5759,8 @@ auth_xfer_transfer_tcp_callback(struct comm_point* c, 
void* arg, int err,
                lock_basic_unlock(&xfr->lock);
                return 0; /* stop on quit */
        }
+       /* stop the timer */
+       comm_timer_disable(xfr->task_transfer->timer);
 
        if(err != NETEVENT_NOERROR) {
                /* connection failed, closed, or timeout */
@@ -5712,6 +5841,8 @@ auth_xfer_transfer_http_callback(struct comm_point* c, 
void* arg, int err,
                return 0; /* stop on quit */
        }
        verbose(VERB_ALGO, "auth zone transfer http callback");
+       /* stop the timer */
+       comm_timer_disable(xfr->task_transfer->timer);
 
        if(err != NETEVENT_NOERROR && err != NETEVENT_DONE) {
                /* connection failed, closed, or timeout */
@@ -5809,6 +5940,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct 
module_env* env,
        struct timeval t;
        /* pick master */
        struct auth_master* master = xfr_probe_current_master(xfr);
+       char *auth_name = NULL;
        if(!master) return 0;
        if(master->allow_notify) return 0; /* only for notify */
        if(master->http) return 0; /* only masters get SOA UDP probe,
@@ -5819,7 +5951,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct 
module_env* env,
                addrlen = xfr->task_probe->scan_addr->addrlen;
                memmove(&addr, &xfr->task_probe->scan_addr->addr, addrlen);
        } else {
-               if(!extstrtoaddr(master->host, &addr, &addrlen)) {
+               if(!authextstrtoaddr(master->host, &addr, &addrlen, 
&auth_name)) {
                        /* the ones that are not in addr format are supposed
                         * to be looked up.  The lookup has failed however,
                         * so skip them */
@@ -5829,6 +5961,18 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct 
module_env* env,
                                zname, master->host);
                        return 0;
                }
+               if (auth_name != NULL) {
+                       if (addr.ss_family == AF_INET
+                       &&  ntohs(((struct sockaddr_in *)&addr)->sin_port)
+                           == env->cfg->ssl_port)
+                               ((struct sockaddr_in *)&addr)->sin_port
+                                       = htons(env->cfg->port);
+                       else if (addr.ss_family == AF_INET6
+                       &&  ntohs(((struct sockaddr_in6 *)&addr)->sin6_port)
+                           == env->cfg->ssl_port)
+                               ((struct sockaddr_in6 *)&addr)->sin6_port
+                                       = htons(env->cfg->port);
+               }
        }
 
        /* create packet */
@@ -5838,14 +5982,26 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct 
module_env* env,
                xfr->task_probe->id = (uint16_t)(ub_random(env->rnd)&0xffff);
        xfr_create_soa_probe_packet(xfr, env->scratch_buffer, 
                xfr->task_probe->id);
+       /* we need to remove the cp if we have a different ip4/ip6 type now */
+       if(xfr->task_probe->cp &&
+               ((xfr->task_probe->cp_is_ip6 && !addr_is_ip6(&addr, addrlen)) ||
+               (!xfr->task_probe->cp_is_ip6 && addr_is_ip6(&addr, addrlen)))
+               ) {
+               comm_point_delete(xfr->task_probe->cp);
+               xfr->task_probe->cp = NULL;
+       }
        if(!xfr->task_probe->cp) {
+               if(addr_is_ip6(&addr, addrlen))
+                       xfr->task_probe->cp_is_ip6 = 1;
+               else    xfr->task_probe->cp_is_ip6 = 0;
                xfr->task_probe->cp = outnet_comm_point_for_udp(env->outnet,
                        auth_xfer_probe_udp_callback, xfr, &addr, addrlen);
                if(!xfr->task_probe->cp) {
-                       char zname[255+1];
+                       char zname[255+1], as[256];
                        dname_str(xfr->name, zname);
+                       addr_to_str(&addr, addrlen, as, sizeof(as));
                        verbose(VERB_ALGO, "cannot create udp cp for "
-                               "probe %s to %s", zname, master->host);
+                               "probe %s to %s", zname, as);
                        return 0;
                }
        }
@@ -5861,12 +6017,20 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct 
module_env* env,
        /* send udp packet */
        if(!comm_point_send_udp_msg(xfr->task_probe->cp, env->scratch_buffer,
                (struct sockaddr*)&addr, addrlen)) {
-               char zname[255+1];
+               char zname[255+1], as[256];
                dname_str(xfr->name, zname);
+               addr_to_str(&addr, addrlen, as, sizeof(as));
                verbose(VERB_ALGO, "failed to send soa probe for %s to %s",
-                       zname, master->host);
+                       zname, as);
                return 0;
        }
+       if(verbosity >= VERB_ALGO) {
+               char zname[255+1], as[256];
+               dname_str(xfr->name, zname);
+               addr_to_str(&addr, addrlen, as, sizeof(as));
+               verbose(VERB_ALGO, "auth zone %s soa probe sent to %s", zname,
+                       as);
+       }
        xfr->task_probe->timeout = timeout;
 #ifndef S_SPLINT_S
        t.tv_sec = timeout/1000;
@@ -5891,6 +6055,11 @@ auth_xfer_probe_timer_callback(void* arg)
                return; /* stop on quit */
        }
 
+       if(verbosity >= VERB_ALGO) {
+               char zname[255+1];
+               dname_str(xfr->name, zname);
+               verbose(VERB_ALGO, "auth zone %s soa probe timeout", zname);
+       }
        if(xfr->task_probe->timeout <= AUTH_PROBE_TIMEOUT_STOP) {
                /* try again with bigger timeout */
                if(xfr_probe_send_probe(xfr, env, xfr->task_probe->timeout*2)) {
@@ -6078,6 +6247,11 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct 
module_env* env)
                         * and we may then get an instant cache response,
                         * and that calls the callback just like a full
                         * lookup and lookup failures also call callback */
+                       if(verbosity >= VERB_ALGO) {
+                               char zname[255+1];
+                               dname_str(xfr->name, zname);
+                               verbose(VERB_ALGO, "auth zone %s probe next 
target lookup", zname);
+                       }
                        lock_basic_unlock(&xfr->lock);
                        return;
                }
@@ -6086,9 +6260,19 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct 
module_env* env)
        /* probe of list has ended.  Create or refresh the list of of
         * allow_notify addrs */
        probe_copy_masters_for_allow_notify(xfr);
+       if(verbosity >= VERB_ALGO) {
+               char zname[255+1];
+               dname_str(xfr->name, zname);
+               verbose(VERB_ALGO, "auth zone %s probe: notify addrs updated", 
zname);
+       }
        if(xfr->task_probe->only_lookup) {
                /* only wanted lookups for copy, stop probe and start wait */
                xfr->task_probe->only_lookup = 0;
+               if(verbosity >= VERB_ALGO) {
+                       char zname[255+1];
+                       dname_str(xfr->name, zname);
+                       verbose(VERB_ALGO, "auth zone %s probe: finished 
only_lookup", zname);
+               }
                xfr_probe_disown(xfr);
                if(xfr->task_nextprobe->worker == NULL)
                        xfr_set_timeout(xfr, env, 0, 0);
@@ -6110,13 +6294,22 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct 
module_env* env)
        /* done with probe sequence, wait */
        if(xfr->task_probe->have_new_lease) {
                /* if zone not updated, start the wait timer again */
-               verbose(VERB_ALGO, "auth_zone unchanged, new lease, wait");
+               if(verbosity >= VERB_ALGO) {
+                       char zname[255+1];
+                       dname_str(xfr->name, zname);
+                       verbose(VERB_ALGO, "auth_zone %s unchanged, new lease, 
wait", zname);
+               }
                xfr_probe_disown(xfr);
                if(xfr->have_zone)
                        xfr->lease_time = *env->now;
                if(xfr->task_nextprobe->worker == NULL)
                        xfr_set_timeout(xfr, env, 0, 0);
        } else {
+               if(verbosity >= VERB_ALGO) {
+                       char zname[255+1];
+                       dname_str(xfr->name, zname);
+                       verbose(VERB_ALGO, "auth zone %s soa probe failed, wait 
to retry", zname);
+               }
                /* we failed to send this as well, move to the wait task,
                 * use the shorter retry timeout */
                xfr_probe_disown(xfr);
@@ -6161,7 +6354,25 @@ void auth_xfer_probe_lookup_callback(void* arg, int 
rcode, sldns_buffer* buf,
                        if(answer) {
                                xfr_master_add_addrs(xfr->task_probe->
                                        lookup_target, answer, wanted_qtype);
+                       } else {
+                               if(verbosity >= VERB_ALGO) {
+                                       char zname[255+1];
+                                       dname_str(xfr->name, zname);
+                                       verbose(VERB_ALGO, "auth zone %s host 
%s type %s probe lookup has nodata", zname, 
xfr->task_probe->lookup_target->host, 
(xfr->task_probe->lookup_aaaa?"AAAA":"A"));
+                               }
                        }
+               } else {
+                       if(verbosity >= VERB_ALGO) {
+                               char zname[255+1];
+                               dname_str(xfr->name, zname);
+                               verbose(VERB_ALGO, "auth zone %s host %s type 
%s probe lookup has no address", zname, xfr->task_probe->lookup_target->host, 
(xfr->task_probe->lookup_aaaa?"AAAA":"A"));
+                       }
+               }
+       } else {
+               if(verbosity >= VERB_ALGO) {
+                       char zname[255+1];
+                       dname_str(xfr->name, zname);
+                       verbose(VERB_ALGO, "auth zone %s host %s type %s probe 
lookup failed", zname, xfr->task_probe->lookup_target->host, 
(xfr->task_probe->lookup_aaaa?"AAAA":"A"));
                }
        }
        if(xfr->task_probe->lookup_target->list &&
diff --git libunbound/services/authzone.h libunbound/services/authzone.h
index 4706803a86b..a695bd029b5 100644
--- libunbound/services/authzone.h
+++ libunbound/services/authzone.h
@@ -327,6 +327,8 @@ struct auth_probe {
        /** the SOA probe udp event.
         * on the workers event base. */
        struct comm_point* cp;
+       /** is the cp for ip6 or ip4 */
+       int cp_is_ip6;
        /** timeout for packets.
         * on the workers event base. */
        struct comm_timer* timer;
@@ -398,6 +400,9 @@ struct auth_transfer {
        /** the transfer (TCP) to the master.
         * on the workers event base. */
        struct comm_point* cp;
+       /** timeout for the transfer.
+        * on the workers event base. */
+       struct comm_timer* timer;
 };
 
 /** list of addresses */
@@ -647,6 +652,8 @@ int auth_xfer_transfer_http_callback(struct comm_point* c, 
void* arg, int err,
         struct comm_reply* repinfo);
 /** xfer probe timeout callback, part of task_probe */
 void auth_xfer_probe_timer_callback(void* arg);
+/** xfer transfer timeout callback, part of task_transfer */
+void auth_xfer_transfer_timer_callback(void* arg);
 /** mesh callback for task_probe on lookup of host names */
 void auth_xfer_probe_lookup_callback(void* arg, int rcode,
        struct sldns_buffer* buf, enum sec_status sec, char* why_bogus,
diff --git libunbound/services/cache/dns.c libunbound/services/cache/dns.c
index 47611ac5aef..aa4efec73f4 100644
--- libunbound/services/cache/dns.c
+++ libunbound/services/cache/dns.c
@@ -40,6 +40,7 @@
  */
 #include "config.h"
 #include "iterator/iter_delegpt.h"
+#include "iterator/iter_utils.h"
 #include "validator/val_nsec.h"
 #include "validator/val_utils.h"
 #include "services/cache/dns.h"
@@ -728,6 +729,8 @@ fill_any(struct module_env* env,
                if(!msg) {
                        return NULL;
                }
+               /* set NOTIMPL for RFC 8482 */
+               msg->rep->flags |= LDNS_RCODE_NOTIMPL;
                msg->rep->security = sec_status_indeterminate;
                return msg;
        }
@@ -912,12 +915,15 @@ dns_cache_lookup(struct module_env* env,
                        struct dns_msg* msg;
                        if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN
                          && data->security == sec_status_secure
+                         && (data->an_numrrsets == 0 ||
+                               ntohs(data->rrsets[0]->rk.type) != 
LDNS_RR_TYPE_CNAME)
                          && (msg=tomsg(env, &k, data, region, now, scratch))){
                                lock_rw_unlock(&e->lock);
                                msg->qinfo.qname=qname;
                                msg->qinfo.qname_len=qnamelen;
                                /* check that DNSSEC really works out */
                                msg->rep->security = sec_status_unchecked;
+                               iter_scrub_nxdomain(msg);
                                return msg;
                        }
                        lock_rw_unlock(&e->lock);
diff --git libunbound/services/listen_dnsport.c 
libunbound/services/listen_dnsport.c
index e74d1abcffc..7e2afd843be 100644
--- libunbound/services/listen_dnsport.c
+++ libunbound/services/listen_dnsport.c
@@ -851,13 +851,16 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, 
int* noproto,
 #ifdef ENOPROTOOPT
                /* squelch ENOPROTOOPT: freebsd server mode with kernel support
                   disabled, except when verbosity enabled for debugging */
-               if(errno != ENOPROTOOPT || verbosity >= 3)
+               if(errno != ENOPROTOOPT || verbosity >= 3) {
 #endif
                  if(errno == EPERM) {
                        log_warn("Setting TCP Fast Open as server failed: %s ; 
this could likely be because sysctl net.inet.tcp.fastopen.enabled, 
net.inet.tcp.fastopen.server_enable, or net.ipv4.tcp_fastopen is disabled", 
strerror(errno));
                  } else {
                        log_err("Setting TCP Fast Open as server failed: %s", 
strerror(errno));
                  }
+#ifdef ENOPROTOOPT
+               }
+#endif
        }
 #endif
        return s;
@@ -1749,6 +1752,7 @@ tcp_req_info_handle_readdone(struct tcp_req_info* req)
        req->is_drop = 0;
        req->is_reply = 0;
        req->in_worker_handle = 1;
+       sldns_buffer_set_limit(req->spool_buffer, 0);
        /* handle the current request */
        /* this calls the worker handle request routine that could give
         * a cache response, or localdata response, or drop the reply,
@@ -1771,24 +1775,12 @@ tcp_req_info_handle_readdone(struct tcp_req_info* req)
         * If mesh failed to add a new entry and called commpoint_drop_reply. 
         * Then the mesh state has been cleared. */
        if(req->is_drop) {
-               /* we can now call drop_reply without recursing into ourselves
-                * whilst in the callback */
-               /* we have to close the stream because there is no reply,
-                * no servfail to send, but the query needs an action, for
-                * a stream that is close the connection */
-               sldns_buffer_clear(c->buffer);
-               comm_point_drop_reply(&c->repinfo);
+               /* the reply has been dropped, stream has been closed. */
                return;
        }
        /* If mesh failed(mallocfail) and called commpoint_send_reply with
         * something like servfail then we pick up that reply below. */
        if(req->is_reply) {
-               /* reply from mesh is in the spool_buffer */
-               sldns_buffer_clear(c->buffer);
-               sldns_buffer_write(c->buffer,
-                       sldns_buffer_begin(req->spool_buffer),
-                       sldns_buffer_limit(req->spool_buffer));
-               sldns_buffer_flip(c->buffer);
                goto send_it;
        }
 
@@ -1867,7 +1859,14 @@ void
 tcp_req_info_send_reply(struct tcp_req_info* req)
 {
        if(req->in_worker_handle) {
-               /* It is in the right buffer to answer straight away */
+               /* reply from mesh is in the spool_buffer */
+               /* copy now, so that the spool buffer is free for other tasks
+                * before the callback is done */
+               sldns_buffer_clear(req->cp->buffer);
+               sldns_buffer_write(req->cp->buffer,
+                       sldns_buffer_begin(req->spool_buffer),
+                       sldns_buffer_limit(req->spool_buffer));
+               sldns_buffer_flip(req->cp->buffer);
                req->is_reply = 1;
                return;
        }
diff --git libunbound/services/mesh.c libunbound/services/mesh.c
index bee0f76a4ad..59ee9a08ec0 100644
--- libunbound/services/mesh.c
+++ libunbound/services/mesh.c
@@ -354,6 +354,10 @@ void mesh_new_client(struct mesh_area* mesh, struct 
query_info* qinfo,
        int was_detached = 0;
        int was_noreply = 0;
        int added = 0;
+       struct sldns_buffer* r_buffer = rep->c->buffer;
+       if(rep->c->tcp_req_info) {
+               r_buffer = rep->c->tcp_req_info->spool_buffer;
+       }
        if(!unique)
                s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 
0, 0);
        /* does this create a new reply state? */
@@ -389,7 +393,7 @@ void mesh_new_client(struct mesh_area* mesh, struct 
query_info* qinfo,
                        if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, 
NULL, NULL,
                                LDNS_RCODE_SERVFAIL, edns, rep, 
mesh->env->scratch))
                                        edns->opt_list = NULL;
-                       error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
+                       error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
                                qinfo, qid, qflags, edns);
                        comm_point_send_reply(rep);
                        return;
@@ -405,7 +409,7 @@ void mesh_new_client(struct mesh_area* mesh, struct 
query_info* qinfo,
                                if(!inplace_cb_reply_servfail_call(mesh->env, 
qinfo, NULL,
                                        NULL, LDNS_RCODE_SERVFAIL, edns, rep, 
mesh->env->scratch))
                                                edns->opt_list = NULL;
-                               error_encode(rep->c->buffer, 
LDNS_RCODE_SERVFAIL,
+                               error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
                                        qinfo, qid, qflags, edns);
                                comm_point_send_reply(rep);
                                return;
@@ -434,7 +438,7 @@ void mesh_new_client(struct mesh_area* mesh, struct 
query_info* qinfo,
                        if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, 
&s->s,
                                NULL, LDNS_RCODE_SERVFAIL, edns, rep, 
mesh->env->scratch))
                                        edns->opt_list = NULL;
-                       error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
+                       error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
                                qinfo, qid, qflags, edns);
                        comm_point_send_reply(rep);
                        if(added)
@@ -1192,12 +1196,16 @@ void mesh_query_done(struct mesh_state* mstate)
                        comm_point_drop_reply(&r->query_reply);
                else {
                        struct sldns_buffer* r_buffer = 
r->query_reply.c->buffer;
-                       if(r->query_reply.c->tcp_req_info)
+                       if(r->query_reply.c->tcp_req_info) {
                                r_buffer = 
r->query_reply.c->tcp_req_info->spool_buffer;
+                               prev_buffer = NULL;
+                       }
                        mesh_send_reply(mstate, mstate->s.return_rcode, rep,
                                r, r_buffer, prev, prev_buffer);
-                       if(r->query_reply.c->tcp_req_info)
+                       if(r->query_reply.c->tcp_req_info) {
                                
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
+                               r_buffer = NULL;
+                       }
                        prev = r;
                        prev_buffer = r_buffer;
                }
@@ -1341,21 +1349,14 @@ int mesh_state_add_reply(struct mesh_state* s, struct 
edns_data* edns,
                log_assert(qinfo->local_alias->rrset->rk.dname ==
                        sldns_buffer_at(rep->c->buffer, LDNS_HEADER_SIZE));
 
-               d = regional_alloc_init(s->s.region, dsrc,
-                       sizeof(struct packed_rrset_data)
-                       + sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t));
+               /* the rrset is not packed, like in the cache, but it is
+                * individualy allocated with an allocator from localzone. */
+               d = regional_alloc_zero(s->s.region, sizeof(*d));
                if(!d)
                        return 0;
                r->local_alias->rrset->entry.data = d;
-               d->rr_len = (size_t*)((uint8_t*)d +
-                       sizeof(struct packed_rrset_data));
-               d->rr_data = (uint8_t**)&(d->rr_len[1]);
-               d->rr_ttl = (time_t*)&(d->rr_data[1]);
-               d->rr_len[0] = dsrc->rr_len[0];
-               d->rr_ttl[0] = dsrc->rr_ttl[0];
-               d->rr_data[0] = regional_alloc_init(s->s.region,
-                       dsrc->rr_data[0], d->rr_len[0]);
-               if(!d->rr_data[0])
+               if(!rrset_insert_rr(s->s.region, d, dsrc->rr_data[0],
+                       dsrc->rr_len[0], dsrc->rr_ttl[0], "CNAME local alias"))
                        return 0;
        } else
                r->local_alias = NULL;
diff --git libunbound/services/outside_network.c 
libunbound/services/outside_network.c
index 16d63df4395..0323f1b30c9 100644
--- libunbound/services/outside_network.c
+++ libunbound/services/outside_network.c
@@ -364,6 +364,8 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* 
pkt, size_t pkt_len)
                        comm_point_close(pend->c);
                        return 0;
                }
+               verbose(VERB_ALGO, "the query is using TLS encryption, for %s",
+                       (w->tls_auth_name?w->tls_auth_name:"an unauthenticated 
connection"));
 #ifdef USE_WINSOCK
                comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl);
 #endif
@@ -404,6 +406,8 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* 
pkt, size_t pkt_len)
                        }
                        SSL_set_verify(pend->c->ssl, SSL_VERIFY_PEER, NULL);
                }
+#else
+               verbose(VERB_ALGO, "the query has an auth_name, but libssl has 
no call to perform TLS authentication");
 #endif /* HAVE_SSL_SET1_HOST */
        }
        w->pkt = NULL;
@@ -2277,11 +2281,60 @@ outnet_comm_point_for_udp(struct outside_network* 
outnet,
        return cp;
 }
 
+/** setup SSL for comm point */
+static int
+setup_comm_ssl(struct comm_point* cp, struct outside_network* outnet,
+       int fd, char* host)
+{
+       cp->ssl = outgoing_ssl_fd(outnet->sslctx, fd);
+       if(!cp->ssl) {
+               log_err("cannot create SSL object");
+               return 0;
+       }
+#ifdef USE_WINSOCK
+       comm_point_tcp_win_bio_cb(cp, cp->ssl);
+#endif
+       cp->ssl_shake_state = comm_ssl_shake_write;
+       /* https verification */
+#ifdef HAVE_SSL_SET1_HOST
+       if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) {
+               /* because we set SSL_VERIFY_PEER, in netevent in
+                * ssl_handshake, it'll check if the certificate
+                * verification has succeeded */
+               /* SSL_VERIFY_PEER is set on the sslctx */
+               /* and the certificates to verify with are loaded into
+                * it with SSL_load_verify_locations or
+                * SSL_CTX_set_default_verify_paths */
+               /* setting the hostname makes openssl verify the
+                * host name in the x509 certificate in the
+                * SSL connection*/
+               if(!SSL_set1_host(cp->ssl, host)) {
+                       log_err("SSL_set1_host failed");
+                       return 0;
+               }
+       }
+#elif defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
+       /* openssl 1.0.2 has this function that can be used for
+        * set1_host like verification */
+       if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) {
+               X509_VERIFY_PARAM* param = SSL_get0_param(cp->ssl);
+               X509_VERIFY_PARAM_set_hostflags(param, 
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+               if(!X509_VERIFY_PARAM_set1_host(param, host, strlen(host))) {
+                       log_err("X509_VERIFY_PARAM_set1_host failed");
+                       return 0;
+               }
+       }
+#else
+       (void)host;
+#endif /* HAVE_SSL_SET1_HOST */
+       return 1;
+}
+
 struct comm_point*
 outnet_comm_point_for_tcp(struct outside_network* outnet,
        comm_point_callback_type* cb, void* cb_arg,
        struct sockaddr_storage* to_addr, socklen_t to_addrlen,
-       sldns_buffer* query, int timeout)
+       sldns_buffer* query, int timeout, int ssl, char* host)
 {
        struct comm_point* cp;
        int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss);
@@ -2301,6 +2354,16 @@ outnet_comm_point_for_tcp(struct outside_network* outnet,
        }
        cp->repinfo.addrlen = to_addrlen;
        memcpy(&cp->repinfo.addr, to_addr, to_addrlen);
+
+       /* setup for SSL (if needed) */
+       if(ssl) {
+               if(!setup_comm_ssl(cp, outnet, fd, host)) {
+                       log_err("cannot setup XoT");
+                       comm_point_delete(cp);
+                       return NULL;
+               }
+       }
+
        /* set timeout on TCP connection */
        comm_point_start_listening(cp, fd, timeout);
        /* copy scratch buffer to cp->buffer */
@@ -2357,48 +2420,11 @@ outnet_comm_point_for_http(struct outside_network* 
outnet,
 
        /* setup for SSL (if needed) */
        if(ssl) {
-               cp->ssl = outgoing_ssl_fd(outnet->sslctx, fd);
-               if(!cp->ssl) {
+               if(!setup_comm_ssl(cp, outnet, fd, host)) {
                        log_err("cannot setup https");
                        comm_point_delete(cp);
                        return NULL;
                }
-#ifdef USE_WINSOCK
-               comm_point_tcp_win_bio_cb(cp, cp->ssl);
-#endif
-               cp->ssl_shake_state = comm_ssl_shake_write;
-               /* https verification */
-#ifdef HAVE_SSL_SET1_HOST
-               if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) {
-                       /* because we set SSL_VERIFY_PEER, in netevent in
-                        * ssl_handshake, it'll check if the certificate
-                        * verification has succeeded */
-                       /* SSL_VERIFY_PEER is set on the sslctx */
-                       /* and the certificates to verify with are loaded into
-                        * it with SSL_load_verify_locations or
-                        * SSL_CTX_set_default_verify_paths */
-                       /* setting the hostname makes openssl verify the
-                        * host name in the x509 certificate in the
-                        * SSL connection*/
-                       if(!SSL_set1_host(cp->ssl, host)) {
-                               log_err("SSL_set1_host failed");
-                               comm_point_delete(cp);
-                               return NULL;
-                       }
-               }
-#elif defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
-               /* openssl 1.0.2 has this function that can be used for
-                * set1_host like verification */
-               if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) {
-                       X509_VERIFY_PARAM* param = SSL_get0_param(cp->ssl);
-                       X509_VERIFY_PARAM_set_hostflags(param, 
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
-                       if(!X509_VERIFY_PARAM_set1_host(param, host, 
strlen(host))) {
-                               log_err("X509_VERIFY_PARAM_set1_host failed");
-                               comm_point_delete(cp);
-                               return NULL;
-                       }
-               }
-#endif /* HAVE_SSL_SET1_HOST */
        }
 
        /* set timeout on TCP connection */
diff --git libunbound/services/outside_network.h 
libunbound/services/outside_network.h
index 48ef03edba7..3456a3da38b 100644
--- libunbound/services/outside_network.h
+++ libunbound/services/outside_network.h
@@ -570,12 +570,14 @@ struct comm_point* outnet_comm_point_for_udp(struct 
outside_network* outnet,
  * @param timeout: timeout for the TCP connection.
  *     timeout in milliseconds, or -1 for no (change to the) timeout.
  *     So seconds*1000.
+ * @param ssl: set to true for TLS.
+ * @param host: hostname for host name verification of TLS (or NULL if no TLS).
  * @return tcp_out commpoint, or NULL.
  */
 struct comm_point* outnet_comm_point_for_tcp(struct outside_network* outnet,
        comm_point_callback_type* cb, void* cb_arg,
        struct sockaddr_storage* to_addr, socklen_t to_addrlen,
-       struct sldns_buffer* query, int timeout);
+       struct sldns_buffer* query, int timeout, int ssl, char* host);
 
 /**
  * Create http commpoint suitable for communication to the destination.
diff --git libunbound/util/alloc.c libunbound/util/alloc.c
index 908b1f42361..7e9618931ca 100644
--- libunbound/util/alloc.c
+++ libunbound/util/alloc.c
@@ -376,6 +376,7 @@ void *unbound_stat_malloc(size_t size)
 {
        void* res;
        if(size == 0) size = 1;
+       log_assert(size <= SIZE_MAX-16);
        res = malloc(size+16);
        if(!res) return NULL;
        unbound_mem_alloc += size;
@@ -398,6 +399,7 @@ void *unbound_stat_calloc(size_t nmemb, size_t size)
        if(nmemb != 0 && INT_MAX/nmemb < size)
                return NULL; /* integer overflow check */
        s = (nmemb*size==0)?(size_t)1:nmemb*size;
+       log_assert(s <= SIZE_MAX-16);
        res = calloc(1, s+16);
        if(!res) return NULL;
        log_info("stat %p=calloc(%u, %u)", res+16, (unsigned)nmemb, 
(unsigned)size);
@@ -447,6 +449,7 @@ void *unbound_stat_realloc(void *ptr, size_t size)
                /* nothing changes */
                return ptr;
        }
+       log_assert(size <= SIZE_MAX-16);
        res = malloc(size+16);
        if(!res) return NULL;
        unbound_mem_alloc += size;
@@ -521,7 +524,9 @@ void *unbound_stat_malloc_lite(size_t size, const char* 
file, int line,
         const char* func)
 {
        /*  [prefix .. len .. actual data .. suffix] */
-       void* res = malloc(size+lite_pad*2+sizeof(size_t));
+       void* res;
+       log_assert(size <= SIZE_MAX-(lite_pad*2+sizeof(size_t)));
+       res = malloc(size+lite_pad*2+sizeof(size_t));
        if(!res) return NULL;
        memmove(res, lite_pre, lite_pad);
        memmove(res+lite_pad, &size, sizeof(size_t));
@@ -538,6 +543,7 @@ void *unbound_stat_calloc_lite(size_t nmemb, size_t size, 
const char* file,
        if(nmemb != 0 && INT_MAX/nmemb < size)
                return NULL; /* integer overflow check */
        req = nmemb * size;
+       log_assert(req <= SIZE_MAX-(lite_pad*2+sizeof(size_t)));
        res = malloc(req+lite_pad*2+sizeof(size_t));
        if(!res) return NULL;
        memmove(res, lite_pre, lite_pad);
diff --git libunbound/util/data/msgreply.h libunbound/util/data/msgreply.h
index a455c4d2b37..8d75f9b12f3 100644
--- libunbound/util/data/msgreply.h
+++ libunbound/util/data/msgreply.h
@@ -157,7 +157,7 @@ struct reply_info {
        time_t prefetch_ttl;
 
        /** 
-        * Reply TTL extended with serve exipred TTL, to limit time to serve
+        * Reply TTL extended with serve expired TTL, to limit time to serve
         * expired message.
         */
        time_t serve_expired_ttl;
diff --git libunbound/util/fptr_wlist.c libunbound/util/fptr_wlist.c
index 02f85e8dc4a..94d23fa3a32 100644
--- libunbound/util/fptr_wlist.c
+++ libunbound/util/fptr_wlist.c
@@ -127,6 +127,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
 #endif
        else if(fptr == &auth_xfer_timer) return 1;
        else if(fptr == &auth_xfer_probe_timer_callback) return 1;
+       else if(fptr == &auth_xfer_transfer_timer_callback) return 1;
        return 0;
 }
 
diff --git libunbound/util/iana_ports.inc libunbound/util/iana_ports.inc
index 5ead47f0f66..aa972a67bd5 100644
--- libunbound/util/iana_ports.inc
+++ libunbound/util/iana_ports.inc
@@ -4768,6 +4768,7 @@
 8088,
 8097,
 8100,
+8111,
 8115,
 8116,
 8118,
@@ -4864,6 +4865,7 @@
 8805,
 8807,
 8808,
+8809,
 8873,
 8880,
 8883,
diff --git libunbound/util/net_help.c libunbound/util/net_help.c
index 2b1be92460b..13bcdf8085b 100644
--- libunbound/util/net_help.c
+++ libunbound/util/net_help.c
@@ -802,6 +802,16 @@ void* listen_sslctx_create(char* key, char* pem, char* 
verifypem)
                log_crypto_err("could not SSL_CTX_new");
                return NULL;
        }
+       if(!key || key[0] == 0) {
+               log_err("error: no tls-service-key file specified");
+               SSL_CTX_free(ctx);
+               return NULL;
+       }
+       if(!pem || pem[0] == 0) {
+               log_err("error: no tls-service-pem file specified");
+               SSL_CTX_free(ctx);
+               return NULL;
+       }
        if(!listen_sslctx_setup(ctx)) {
                SSL_CTX_free(ctx);
                return NULL;
@@ -1235,7 +1245,12 @@ listen_sslctx_delete_ticket_keys(void)
        struct tls_session_ticket_key *key;
        if(!ticket_keys) return;
        for(key = ticket_keys; key->key_name != NULL; key++) {
-               memset(key->key_name, 0xdd, 80); /* wipe key data from memory*/
+               /* wipe key data from memory*/
+#ifdef HAVE_EXPLICIT_BZERO
+               explicit_bzero(key->key_name, 80);
+#else
+               memset(key->key_name, 0xdd, 80);
+#endif
                free(key->key_name);
        }
        free(ticket_keys);
diff --git libunbound/util/netevent.c libunbound/util/netevent.c
index f33e44058b1..9e2ba92b5fd 100644
--- libunbound/util/netevent.c
+++ libunbound/util/netevent.c
@@ -178,7 +178,7 @@ comm_base_create(int sigs)
        }
        ub_comm_base_now(b);
        ub_get_event_sys(b->eb->base, &evnm, &evsys, &evmethod);
-       verbose(VERB_ALGO, "%s %s user %s method.", evnm, evsys, evmethod);
+       verbose(VERB_ALGO, "%s %s uses %s method.", evnm, evsys, evmethod);
        return b;
 }
 
@@ -926,6 +926,14 @@ comm_point_tcp_accept_callback(int fd, short event, void* 
arg)
        }
        /* accept incoming connection. */
        c_hdl = c->tcp_free;
+       /* clear leftover flags from previous use, and then set the
+        * correct event base for the event structure for libevent */
+       ub_event_free(c_hdl->ev->ev);
+       c_hdl->ev->ev = ub_event_new(c_hdl->ev->base->eb->base, -1, 
UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT, comm_point_tcp_handle_callback, 
c_hdl);
+       if(!c_hdl->ev->ev) {
+               log_warn("could not ub_event_new, dropped tcp");
+               return;
+       }
        log_assert(fd != -1);
        (void)fd;
        new_fd = comm_point_perform_accept(c, &c_hdl->repinfo.addr,
@@ -1184,6 +1192,10 @@ ssl_handle_read(struct comm_point* c)
                                comm_point_listen_for_rw(c, 0, 1);
                                return 1;
                        } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef ECONNRESET
+                               if(errno == ECONNRESET && verbosity < 2)
+                                       return 0; /* silence reset by peer */
+#endif
                                if(errno != 0)
                                        log_err("SSL_read syscall: %s",
                                                strerror(errno));
@@ -1228,6 +1240,10 @@ ssl_handle_read(struct comm_point* c)
                                comm_point_listen_for_rw(c, 0, 1);
                                return 1;
                        } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef ECONNRESET
+                               if(errno == ECONNRESET && verbosity < 2)
+                                       return 0; /* silence reset by peer */
+#endif
                                if(errno != 0)
                                        log_err("SSL_read syscall: %s",
                                                strerror(errno));
@@ -1288,13 +1304,17 @@ ssl_handle_write(struct comm_point* c)
                        if(want == SSL_ERROR_ZERO_RETURN) {
                                return 0; /* closed */
                        } else if(want == SSL_ERROR_WANT_READ) {
-                               c->ssl_shake_state = comm_ssl_shake_read;
+                               c->ssl_shake_state = comm_ssl_shake_hs_read;
                                comm_point_listen_for_rw(c, 1, 0);
                                return 1; /* wait for read condition */
                        } else if(want == SSL_ERROR_WANT_WRITE) {
                                ub_winsock_tcp_wouldblock(c->ev->ev, 
UB_EV_WRITE);
                                return 1; /* write more later */
                        } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef EPIPE
+                               if(errno == EPIPE && verbosity < 2)
+                                       return 0; /* silence 'broken pipe' */
+#endif
                                if(errno != 0)
                                        log_err("SSL_write syscall: %s",
                                                strerror(errno));
@@ -1322,13 +1342,17 @@ ssl_handle_write(struct comm_point* c)
                if(want == SSL_ERROR_ZERO_RETURN) {
                        return 0; /* closed */
                } else if(want == SSL_ERROR_WANT_READ) {
-                       c->ssl_shake_state = comm_ssl_shake_read;
+                       c->ssl_shake_state = comm_ssl_shake_hs_read;
                        comm_point_listen_for_rw(c, 1, 0);
                        return 1; /* wait for read condition */
                } else if(want == SSL_ERROR_WANT_WRITE) {
                        ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
                        return 1; /* write more later */
                } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef EPIPE
+                       if(errno == EPIPE && verbosity < 2)
+                               return 0; /* silence 'broken pipe' */
+#endif
                        if(errno != 0)
                                log_err("SSL_write syscall: %s",
                                        strerror(errno));
@@ -1543,7 +1567,6 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                iov[1].iov_base = sldns_buffer_begin(buffer);
                iov[1].iov_len = sldns_buffer_limit(buffer);
                log_assert(iov[0].iov_len > 0);
-               log_assert(iov[1].iov_len > 0);
                msg.msg_name = &c->repinfo.addr;
                msg.msg_namelen = c->repinfo.addrlen;
                msg.msg_iov = iov;
@@ -1610,7 +1633,6 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                iov[1].iov_base = sldns_buffer_begin(buffer);
                iov[1].iov_len = sldns_buffer_limit(buffer);
                log_assert(iov[0].iov_len > 0);
-               log_assert(iov[1].iov_len > 0);
                r = writev(fd, iov, 2);
 #else /* HAVE_WRITEV */
                r = send(fd, (void*)(((uint8_t*)&len)+c->tcp_byte_count),
@@ -1624,6 +1646,10 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
   #endif
                        if(errno == EINTR || errno == EAGAIN)
                                return 1;
+#ifdef ECONNRESET
+                       if(errno == ECONNRESET && verbosity < 2)
+                               return 0; /* silence reset by peer */
+#endif
 #  ifdef HAVE_WRITEV
                        log_err_addr("tcp writev", strerror(errno),
                                &c->repinfo.addr, c->repinfo.addrlen);
@@ -1641,6 +1667,8 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                                        UB_EV_WRITE);
                                return 1; 
                        }
+                       if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
+                               return 0; /* silence reset by peer */
                        log_err_addr("tcp send s",
                                wsa_strerror(WSAGetLastError()),
                                &c->repinfo.addr, c->repinfo.addrlen);
@@ -1664,6 +1692,10 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
 #ifndef USE_WINSOCK
                if(errno == EINTR || errno == EAGAIN)
                        return 1;
+#ifdef ECONNRESET
+               if(errno == ECONNRESET && verbosity < 2)
+                       return 0; /* silence reset by peer */
+#endif
                log_err_addr("tcp send r", strerror(errno),
                        &c->repinfo.addr, c->repinfo.addrlen);
 #else
@@ -1673,6 +1705,8 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                        ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
                        return 1; 
                }
+               if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
+                       return 0; /* silence reset by peer */
                log_err_addr("tcp send r", wsa_strerror(WSAGetLastError()),
                        &c->repinfo.addr, c->repinfo.addrlen);
 #endif
@@ -1738,6 +1772,16 @@ comm_point_tcp_handle_callback(int fd, short event, 
void* arg)
        }
 #endif
 
+       if(event&UB_EV_TIMEOUT) {
+               verbose(VERB_QUERY, "tcp took too long, dropped");
+               reclaim_tcp_handler(c);
+               if(!c->tcp_do_close) {
+                       fptr_ok(fptr_whitelist_comm_point(c->callback));
+                       (void)(*c->callback)(c, c->cb_arg,
+                               NETEVENT_TIMEOUT, NULL);
+               }
+               return;
+       }
        if(event&UB_EV_READ) {
                int has_tcpq = (c->tcp_req_info != NULL);
                if(!comm_point_tcp_handle_read(fd, c, 0)) {
@@ -1768,16 +1812,6 @@ comm_point_tcp_handle_callback(int fd, short event, 
void* arg)
                        tcp_req_info_read_again(fd, c);
                return;
        }
-       if(event&UB_EV_TIMEOUT) {
-               verbose(VERB_QUERY, "tcp took too long, dropped");
-               reclaim_tcp_handler(c);
-               if(!c->tcp_do_close) {
-                       fptr_ok(fptr_whitelist_comm_point(c->callback));
-                       (void)(*c->callback)(c, c->cb_arg,
-                               NETEVENT_TIMEOUT, NULL);
-               }
-               return;
-       }
        log_err("Ignored event %d for tcphdl.", event);
 }
 
@@ -1826,6 +1860,10 @@ ssl_http_read_more(struct comm_point* c)
                        comm_point_listen_for_rw(c, 0, 1);
                        return 1;
                } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef ECONNRESET
+                       if(errno == ECONNRESET && verbosity < 2)
+                               return 0; /* silence reset by peer */
+#endif
                        if(errno != 0)
                                log_err("SSL_read syscall: %s",
                                        strerror(errno));
@@ -2268,12 +2306,16 @@ ssl_http_write_more(struct comm_point* c)
                if(want == SSL_ERROR_ZERO_RETURN) {
                        return 0; /* closed */
                } else if(want == SSL_ERROR_WANT_READ) {
-                       c->ssl_shake_state = comm_ssl_shake_read;
+                       c->ssl_shake_state = comm_ssl_shake_hs_read;
                        comm_point_listen_for_rw(c, 1, 0);
                        return 1; /* wait for read condition */
                } else if(want == SSL_ERROR_WANT_WRITE) {
                        return 1; /* write more later */
                } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef EPIPE
+                       if(errno == EPIPE && verbosity < 2)
+                               return 0; /* silence 'broken pipe' */
+#endif
                        if(errno != 0)
                                log_err("SSL_write syscall: %s",
                                        strerror(errno));
@@ -2382,6 +2424,16 @@ comm_point_http_handle_callback(int fd, short event, 
void* arg)
        log_assert(c->type == comm_http);
        ub_comm_base_now(c->ev->base);
 
+       if(event&UB_EV_TIMEOUT) {
+               verbose(VERB_QUERY, "http took too long, dropped");
+               reclaim_http_handler(c);
+               if(!c->tcp_do_close) {
+                       fptr_ok(fptr_whitelist_comm_point(c->callback));
+                       (void)(*c->callback)(c, c->cb_arg,
+                               NETEVENT_TIMEOUT, NULL);
+               }
+               return;
+       }
        if(event&UB_EV_READ) {
                if(!comm_point_http_handle_read(fd, c)) {
                        reclaim_http_handler(c);
@@ -2406,16 +2458,6 @@ comm_point_http_handle_callback(int fd, short event, 
void* arg)
                }
                return;
        }
-       if(event&UB_EV_TIMEOUT) {
-               verbose(VERB_QUERY, "http took too long, dropped");
-               reclaim_http_handler(c);
-               if(!c->tcp_do_close) {
-                       fptr_ok(fptr_whitelist_comm_point(c->callback));
-                       (void)(*c->callback)(c, c->cb_arg,
-                               NETEVENT_TIMEOUT, NULL);
-               }
-               return;
-       }
        log_err("Ignored event %d for httphdl.", event);
 }
 
@@ -3138,8 +3180,8 @@ comm_point_stop_listening(struct comm_point* c)
 void 
 comm_point_start_listening(struct comm_point* c, int newfd, int msec)
 {
-       verbose(VERB_ALGO, "comm point start listening %d", 
-               c->fd==-1?newfd:c->fd);
+       verbose(VERB_ALGO, "comm point start listening %d (%d msec)", 
+               c->fd==-1?newfd:c->fd, msec);
        if(c->type == comm_tcp_accept && !c->tcp_free) {
                /* no use to start listening no free slots. */
                return;
diff --git libunbound/util/storage/lookup3.c libunbound/util/storage/lookup3.c
index cc110748156..bb25eb433c9 100644
--- libunbound/util/storage/lookup3.c
+++ libunbound/util/storage/lookup3.c
@@ -1,4 +1,7 @@
 /*
+  May 2019(Wouter) patch to enable the valgrind clean implementation all the
+     time.  This enables better security audit and checks, which is better
+     than the speedup.  Git issue #30.  Renamed the define ARRAY_CLEAN_ACCESS.
   February 2013(Wouter) patch defines for BSD endianness, from Brad Smith.
   January 2012(Wouter) added randomised initial value, fallout from 28c3.
   March 2007(Wouter) adapted from lookup3.c original, add config.h include.
@@ -44,6 +47,7 @@ on 1 byte), but shoehorning those bytes into integers 
efficiently is messy.
 -------------------------------------------------------------------------------
 */
 /*#define SELF_TEST 1*/
+#define ARRAY_CLEAN_ACCESS 1
 
 #include "config.h"
 #include "util/storage/lookup3.h"
@@ -336,7 +340,7 @@ uint32_t hashlittle( const void *key, size_t length, 
uint32_t initval)
   u.ptr = key;
   if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
     const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
-#ifdef VALGRIND
+#ifdef ARRAY_CLEAN_ACCESS
     const uint8_t  *k8;
 #endif
 
@@ -361,7 +365,7 @@ uint32_t hashlittle( const void *key, size_t length, 
uint32_t initval)
      * still catch it and complain.  The masking trick does make the hash
      * noticeably faster for short strings (like English words).
      */
-#ifndef VALGRIND
+#ifndef ARRAY_CLEAN_ACCESS
 
     switch(length)
     {
diff --git libunbound/util/ub_event.c libunbound/util/ub_event.c
index 78481a98205..e097fbc4015 100644
--- libunbound/util/ub_event.c
+++ libunbound/util/ub_event.c
@@ -295,11 +295,18 @@ ub_event_new(struct ub_event_base* base, int fd, short 
bits,
        if (!ev)
                return NULL;
 
+#ifndef HAVE_EVENT_ASSIGN
        event_set(ev, fd, NATIVE_BITS(bits), NATIVE_BITS_CB(cb), arg);
        if (event_base_set(AS_EVENT_BASE(base), ev) != 0) {
                free(ev);
                return NULL;
        }
+#else
+       if (event_assign(ev, AS_EVENT_BASE(base), fd, bits, cb, arg) != 0) {
+               free(ev);
+               return NULL;
+       }
+#endif
        return AS_UB_EVENT(ev);
 }
 
@@ -312,11 +319,18 @@ ub_signal_new(struct ub_event_base* base, int fd,
        if (!ev)
                return NULL;
 
+#if !HAVE_DECL_EVSIGNAL_ASSIGN
        signal_set(ev, fd, NATIVE_BITS_CB(cb), arg);
        if (event_base_set(AS_EVENT_BASE(base), ev) != 0) {
                free(ev);
                return NULL;
        }
+#else
+       if (evsignal_assign(ev, AS_EVENT_BASE(base), fd, cb, arg) != 0) {
+               free(ev);
+               return NULL;
+       }
+#endif
        return AS_UB_EVENT(ev);
 }
 


-- 
I'm not entirely sure you are real.

Reply via email to