Blob Blame History Raw
diff -Naur unbound-1.4.16/compat/memcmp.h unbound-trunk/compat/memcmp.h
--- unbound-1.4.16/compat/memcmp.h	1969-12-31 19:00:00.000000000 -0500
+++ unbound-trunk/compat/memcmp.h	2012-02-22 20:37:14.182368184 -0500
@@ -0,0 +1,16 @@
+/*
+ *	memcmp.h: undef memcmp for compat.
+ *
+ *	Copyright (c) 2012, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+*/
+#ifndef COMPAT_MEMCMP_H
+#define COMPAT_MEMCMP_H
+
+#ifdef memcmp
+/* undef here otherwise autoheader messes it up in config.h */
+#  undef memcmp
+#endif
+
+#endif /* COMPAT_MEMCMP_H */
diff -Naur unbound-1.4.16/daemon/cachedump.c unbound-trunk/daemon/cachedump.c
--- unbound-1.4.16/daemon/cachedump.c	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/daemon/cachedump.c	2012-02-22 20:37:15.803368186 -0500
@@ -765,7 +765,7 @@
 	if(!go_on) 
 		return 1; /* skip this one, not all references satisfied */
 
-	if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, NULL)) {
+	if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL)) {
 		log_warn("error out of memory");
 		return 0;
 	}
@@ -802,7 +802,8 @@
 {
 	char buf[257];
 	struct delegpt_addr* a;
-	int lame, dlame, rlame, rto, edns_vs, to, delay, entry_ttl;
+	int lame, dlame, rlame, rto, edns_vs, to, delay, entry_ttl,
+		tA = 0, tAAAA = 0, tother = 0;
 	struct rtt_info ri;
 	uint8_t edns_lame_known;
 	for(a = dp->target_list; a; a = a->next_target) {
@@ -817,9 +818,11 @@
 		delay=0;
 		entry_ttl = infra_get_host_rto(worker->env.infra_cache,
 			&a->addr, a->addrlen, dp->name, dp->namelen,
-			&ri, &delay, *worker->env.now);
+			&ri, &delay, *worker->env.now, &tA, &tAAAA, &tother);
 		if(entry_ttl == -2 && ri.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
-			if(!ssl_printf(ssl, "expired, rto %d msec.\n", ri.rto))
+			if(!ssl_printf(ssl, "expired, rto %d msec, tA %d "
+				"tAAAA %d tother %d.\n", ri.rto, tA, tAAAA,
+				tother))
 				return;
 			continue;
 		}
@@ -840,11 +843,12 @@
 			continue; /* skip stuff not in infra cache */
 		}
 		if(!ssl_printf(ssl, "%s%s%s%srto %d msec, ttl %d, ping %d "
-			"var %d rtt %d",
+			"var %d rtt %d, tA %d, tAAAA %d, tother %d",
 			lame?"LAME ":"", dlame?"NoDNSSEC ":"",
 			a->lame?"AddrWasParentSide ":"",
 			rlame?"NoAuthButRecursive ":"", rto, entry_ttl,
-			ri.srtt, ri.rttvar, rtt_notimeout(&ri)))
+			ri.srtt, ri.rttvar, rtt_notimeout(&ri),
+			tA, tAAAA, tother))
 			return;
 		if(delay)
 			if(!ssl_printf(ssl, ", probedelay %d", delay))
@@ -907,19 +911,12 @@
 	char b[260];
 	struct query_info qinfo;
 	struct iter_hints_stub* stub;
-	struct iter_env* ie;
 	regional_free_all(region);
 	qinfo.qname = nm;
 	qinfo.qname_len = nmlen;
 	qinfo.qtype = LDNS_RR_TYPE_A;
 	qinfo.qclass = LDNS_RR_CLASS_IN;
 
-	if(modstack_find(&worker->daemon->mods, "iterator") == -1) {
-		return ssl_printf(ssl, "error: no iterator module\n");
-	}
-	ie = (struct iter_env*)worker->env.modinfo[modstack_find(&worker->
-		daemon->mods, "iterator")];
-
 	dname_str(nm, b);
 	if(!ssl_printf(ssl, "The following name servers are used for lookup "
 		"of %s\n", b)) 
@@ -964,7 +961,8 @@
 				continue;
 			}
 		} 
-		stub = hints_lookup_stub(ie->hints, nm, qinfo.qclass, dp);
+		stub = hints_lookup_stub(worker->env.hints, nm, qinfo.qclass,
+			dp);
 		if(stub) {
 			if(stub->noprime) {
 				if(!ssl_printf(ssl, "The noprime stub servers "
diff -Naur unbound-1.4.16/daemon/remote.c unbound-trunk/daemon/remote.c
--- unbound-1.4.16/daemon/remote.c	2011-11-10 13:44:06.000000000 -0500
+++ unbound-trunk/daemon/remote.c	2012-02-22 20:37:15.804368186 -0500
@@ -68,6 +68,7 @@
 #include "validator/validator.h"
 #include "validator/val_kcache.h"
 #include "validator/val_kentry.h"
+#include "validator/val_anchor.h"
 #include "iterator/iterator.h"
 #include "iterator/iter_fwd.h"
 #include "iterator/iter_hints.h"
@@ -1337,15 +1338,15 @@
 
 /** parse args into delegpt */
 static struct delegpt*
-parse_delegpt(SSL* ssl, struct regional* region, char* args, uint8_t* root)
+parse_delegpt(SSL* ssl, char* args, uint8_t* nm, int allow_names)
 {
 	/* parse args and add in */
 	char* p = args;
 	char* todo;
-	struct delegpt* dp = delegpt_create(region);
+	struct delegpt* dp = delegpt_create_mlc(nm);
 	struct sockaddr_storage addr;
 	socklen_t addrlen;
-	if(!dp || !delegpt_set_name(dp, region, root)) {
+	if(!dp) {
 		(void)ssl_printf(ssl, "error out of memory\n");
 		return NULL;
 	}
@@ -1358,14 +1359,37 @@
 		}
 		/* parse address */
 		if(!extstrtoaddr(todo, &addr, &addrlen)) {
-			(void)ssl_printf(ssl, "error cannot parse"
-				" IP address '%s'\n", todo);
-			return NULL;
-		}
-		/* add address */
-		if(!delegpt_add_addr(dp, region, &addr, addrlen, 0, 0)) {
-			(void)ssl_printf(ssl, "error out of memory\n");
-			return NULL;
+			if(allow_names) {
+				uint8_t* n = NULL;
+				size_t ln;
+				int lb;
+				if(!parse_arg_name(ssl, todo, &n, &ln, &lb)) {
+					(void)ssl_printf(ssl, "error cannot "
+						"parse IP address or name "
+						"'%s'\n", todo);
+					delegpt_free_mlc(dp);
+					return NULL;
+				}
+				if(!delegpt_add_ns_mlc(dp, n, 0)) {
+					(void)ssl_printf(ssl, "error out of memory\n");
+					delegpt_free_mlc(dp);
+					return NULL;
+				}
+				free(n);
+
+			} else {
+				(void)ssl_printf(ssl, "error cannot parse"
+					" IP address '%s'\n", todo);
+				delegpt_free_mlc(dp);
+				return NULL;
+			}
+		} else {
+			/* add address */
+			if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0)) {
+				(void)ssl_printf(ssl, "error out of memory\n");
+				delegpt_free_mlc(dp);
+				return NULL;
+			}
 		}
 	}
 	return dp;
@@ -1389,20 +1413,166 @@
 	 * the actual mesh is not running, so we can freely edit it. */
 	/* delete all the existing queries first */
 	mesh_delete_all(worker->env.mesh);
-	/* reset the fwd structure ; the cfg is unchanged (shared by threads)*/
-	/* this reset frees up memory */
-	forwards_apply_cfg(fwd, worker->env.cfg);
 	if(strcmp(args, "off") == 0) {
 		forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root);
 	} else {
 		struct delegpt* dp;
-		if(!(dp = parse_delegpt(ssl, fwd->region, args, root)))
+		if(!(dp = parse_delegpt(ssl, args, root, 0)))
 			return;
 		if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) {
 			(void)ssl_printf(ssl, "error out of memory\n");
+			delegpt_free_mlc(dp);
+			return;
+		}
+	}
+	send_ok(ssl);
+}
+
+static int
+parse_fs_args(SSL* ssl, char* args, uint8_t** nm, struct delegpt** dp,
+	int* insecure, int* prime)
+{
+	char* zonename;
+	char* rest;
+	size_t nmlen;
+	int nmlabs;
+	/* parse all -x args */
+	while(args[0] == '+') {
+		if(!find_arg2(ssl, args, &rest))
+			return 0;
+		while(*(++args) != 0) {
+			if(*args == 'i' && insecure)
+				*insecure = 1;
+			else if(*args == 'p' && prime)
+				*prime = 1;
+			else {
+				(void)ssl_printf(ssl, "error: unknown option %s\n", args);
+				return 0;
+			}
+		}
+		args = rest;
+	}
+	/* parse name */
+	if(dp) {
+		if(!find_arg2(ssl, args, &rest))
+			return 0;
+		zonename = args;
+		args = rest;
+	} else	zonename = args;
+	if(!parse_arg_name(ssl, zonename, nm, &nmlen, &nmlabs))
+		return 0;
+
+	/* parse dp */
+	if(dp) {
+		if(!(*dp = parse_delegpt(ssl, args, *nm, 1))) {
+			free(*nm);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/** do the forward_add command */
+static void
+do_forward_add(SSL* ssl, struct worker* worker, char* args)
+{
+	struct iter_forwards* fwd = worker->env.fwds;
+	int insecure = 0;
+	uint8_t* nm = NULL;
+	struct delegpt* dp = NULL;
+	if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL))
+		return;
+	if(insecure) {
+		if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
+			nm)) {
+			(void)ssl_printf(ssl, "error out of memory\n");
+			delegpt_free_mlc(dp);
+			free(nm);
+			return;
+		}
+	}
+	if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) {
+		(void)ssl_printf(ssl, "error out of memory\n");
+		delegpt_free_mlc(dp);
+		free(nm);
+		return;
+	}
+	free(nm);
+	send_ok(ssl);
+}
+
+/** do the forward_remove command */
+static void
+do_forward_remove(SSL* ssl, struct worker* worker, char* args)
+{
+	struct iter_forwards* fwd = worker->env.fwds;
+	int insecure = 0;
+	uint8_t* nm = NULL;
+	if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL))
+		return;
+	if(insecure)
+		anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
+			nm);
+	forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, nm);
+	free(nm);
+	send_ok(ssl);
+}
+
+/** do the stub_add command */
+static void
+do_stub_add(SSL* ssl, struct worker* worker, char* args)
+{
+	struct iter_forwards* fwd = worker->env.fwds;
+	int insecure = 0, prime = 0;
+	uint8_t* nm = NULL;
+	struct delegpt* dp = NULL;
+	if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime))
+		return;
+	if(insecure) {
+		if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
+			nm)) {
+			(void)ssl_printf(ssl, "error out of memory\n");
+			delegpt_free_mlc(dp);
+			free(nm);
 			return;
 		}
 	}
+	if(!forwards_add_stub_hole(fwd, LDNS_RR_CLASS_IN, nm)) {
+		if(insecure) anchors_delete_insecure(worker->env.anchors,
+			LDNS_RR_CLASS_IN, nm);
+		(void)ssl_printf(ssl, "error out of memory\n");
+		delegpt_free_mlc(dp);
+		free(nm);
+		return;
+	}
+	if(!hints_add_stub(worker->env.hints, LDNS_RR_CLASS_IN, dp, !prime)) {
+		(void)ssl_printf(ssl, "error out of memory\n");
+		forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm);
+		if(insecure) anchors_delete_insecure(worker->env.anchors,
+			LDNS_RR_CLASS_IN, nm);
+		delegpt_free_mlc(dp);
+		free(nm);
+		return;
+	}
+	free(nm);
+	send_ok(ssl);
+}
+
+/** do the stub_remove command */
+static void
+do_stub_remove(SSL* ssl, struct worker* worker, char* args)
+{
+	struct iter_forwards* fwd = worker->env.fwds;
+	int insecure = 0;
+	uint8_t* nm = NULL;
+	if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL))
+		return;
+	if(insecure)
+		anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
+			nm);
+	forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm);
+	hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm);
+	free(nm);
 	send_ok(ssl);
 }
 
@@ -1570,9 +1740,11 @@
 		return;
 	}
 	if(!ssl_printf(a->ssl, "%s %s ttl %d ping %d var %d rtt %d rto %d "
+		"tA %d tAAAA %d tother %d "
 		"ednsknown %d edns %d delay %d lame dnssec %d rec %d A %d "
 		"other %d\n", ip_str, name, (int)(d->ttl - a->now),
 		d->rtt.srtt, d->rtt.rttvar, rtt_notimeout(&d->rtt), d->rtt.rto,
+		d->timeout_A, d->timeout_AAAA, d->timeout_other,
 		(int)d->edns_lame_known, (int)d->edns_version,
 		(int)(a->now<d->probedelay?d->probedelay-a->now:0),
 		(int)d->isdnsseclame, (int)d->rec_lame, (int)d->lame_type_A,
@@ -1652,17 +1824,8 @@
 static void
 do_list_stubs(SSL* ssl, struct worker* worker)
 {
-	/* readonly structure */
-	int m;
 	struct iter_hints_stub* z;
-	struct iter_env* ie;
-	m = modstack_find(&worker->env.mesh->mods, "iterator");
-	if(m == -1) {
-		(void)ssl_printf(ssl, "error no iterator module\n");
-		return;
-	}
-	ie = (struct iter_env*)worker->env.modinfo[m];
-	RBTREE_FOR(z, struct iter_hints_stub*, &ie->hints->tree) {
+	RBTREE_FOR(z, struct iter_hints_stub*, &worker->env.hints->tree) {
 		if(!ssl_print_name_dp(ssl, 
 			z->noprime?"stub noprime":"stub prime", z->node.name,
 			z->node.dclass, z->dp))
@@ -1780,6 +1943,26 @@
 	} else if(cmdcmp(p, "list_local_data", 15)) {
 		do_list_local_data(ssl, worker);
 		return;
+	} else if(cmdcmp(p, "stub_add", 8)) {
+		/* must always distribute this cmd */
+		if(rc) distribute_cmd(rc, ssl, cmd);
+		do_stub_add(ssl, worker, skipwhite(p+8));
+		return;
+	} else if(cmdcmp(p, "stub_remove", 11)) {
+		/* must always distribute this cmd */
+		if(rc) distribute_cmd(rc, ssl, cmd);
+		do_stub_remove(ssl, worker, skipwhite(p+11));
+		return;
+	} else if(cmdcmp(p, "forward_add", 11)) {
+		/* must always distribute this cmd */
+		if(rc) distribute_cmd(rc, ssl, cmd);
+		do_forward_add(ssl, worker, skipwhite(p+11));
+		return;
+	} else if(cmdcmp(p, "forward_remove", 14)) {
+		/* must always distribute this cmd */
+		if(rc) distribute_cmd(rc, ssl, cmd);
+		do_forward_remove(ssl, worker, skipwhite(p+14));
+		return;
 	} else if(cmdcmp(p, "forward", 7)) {
 		/* must always distribute this cmd */
 		if(rc) distribute_cmd(rc, ssl, cmd);
diff -Naur unbound-1.4.16/daemon/worker.c unbound-trunk/daemon/worker.c
--- unbound-1.4.16/daemon/worker.c	2011-11-10 13:44:06.000000000 -0500
+++ unbound-trunk/daemon/worker.c	2012-02-22 20:37:15.803368186 -0500
@@ -67,7 +67,9 @@
 #include "util/fptr_wlist.h"
 #include "util/tube.h"
 #include "iterator/iter_fwd.h"
+#include "iterator/iter_hints.h"
 #include "validator/autotrust.h"
+#include "validator/val_anchor.h"
 
 #ifdef HAVE_SYS_TYPES_H
 #  include <sys/types.h>
@@ -148,7 +150,7 @@
 #ifdef UNBOUND_ALLOC_STATS
 	/* debug func in validator module */
 	size_t total, front, back, mesh, msg, rrset, infra, ac, superac;
-	size_t me, iter, val;
+	size_t me, iter, val, anch;
 	int i;
 	if(verbosity < VERB_ALGO) 
 		return;
@@ -160,6 +162,7 @@
 	mesh = mesh_get_mem(worker->env.mesh);
 	ac = alloc_get_mem(&worker->alloc);
 	superac = alloc_get_mem(&worker->daemon->superalloc);
+	anch = anchors_get_mem(worker->env.anchors);
 	iter = 0;
 	val = 0;
 	for(i=0; i<worker->env.mesh->mods.num; i++) {
@@ -177,7 +180,8 @@
 		+ regional_get_mem(worker->scratchpad) 
 		+ sizeof(*worker->env.scratch_buffer) 
 		+ ldns_buffer_capacity(worker->env.scratch_buffer)
-		+ forwards_get_mem(worker->env.fwds);
+		+ forwards_get_mem(worker->env.fwds)
+		+ hints_get_mem(worker->env.hints;
 	if(worker->thread_num == 0)
 		me += acl_list_get_mem(worker->daemon->acl);
 	if(cur_serv) {
@@ -185,12 +189,12 @@
 	}
 	total = front+back+mesh+msg+rrset+infra+iter+val+ac+superac+me;
 	log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
-		"rrset=%u infra=%u iter=%u val=%u "
+		"rrset=%u infra=%u iter=%u val=%u anchors=%u "
 		"alloccache=%u globalalloccache=%u me=%u",
 		(unsigned)total, (unsigned)front, (unsigned)back, 
 		(unsigned)mesh, (unsigned)msg, (unsigned)rrset, 
-		(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)ac, 
-		(unsigned)superac, (unsigned)me);
+		(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)anch,
+		(unsigned)ac, (unsigned)superac, (unsigned)me);
 	debug_total_mem(total);
 #else /* no UNBOUND_ALLOC_STATS */
 	size_t val = 0;
@@ -1158,6 +1162,12 @@
 		worker_delete(worker);
 		return 0;
 	}
+	if(!(worker->env.hints = hints_create()) ||
+		!hints_apply_cfg(worker->env.hints, cfg)) {
+		log_err("Could not set root or stub hints");
+		worker_delete(worker);
+		return 0;
+	}
 	/* one probe timer per process -- if we have 5011 anchors */
 	if(autr_get_num_anchors(worker->env.anchors) > 0
 #ifndef THREADS_DISABLED
@@ -1210,6 +1220,7 @@
 	mesh_delete(worker->env.mesh);
 	ldns_buffer_free(worker->env.scratch_buffer);
 	forwards_delete(worker->env.fwds);
+	hints_delete(worker->env.hints);
 	listen_delete(worker->front);
 	outside_network_delete(worker->back);
 	comm_signal_delete(worker->comsig);
diff -Naur unbound-1.4.16/iterator/iterator.c unbound-trunk/iterator/iterator.c
--- unbound-1.4.16/iterator/iterator.c	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/iterator/iterator.c	2012-02-22 20:37:15.042368186 -0500
@@ -89,7 +89,6 @@
 	iter_env = (struct iter_env*)env->modinfo[id];
 	free(iter_env->target_fetch_policy);
 	priv_delete(iter_env->priv);
-	hints_delete(iter_env->hints);
 	donotq_delete(iter_env->donotq);
 	free(iter_env);
 	env->modinfo[id] = NULL;
@@ -260,7 +259,7 @@
 	/* do not waste time trying to validate this servfail */
 	err.security = sec_status_indeterminate;
 	verbose(VERB_ALGO, "store error response in message cache");
-	if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, NULL)) {
+	if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL)) {
 		log_err("error_response_cache: could not store error (nomem)");
 	}
 	return error_response(qstate, id, rcode);
@@ -532,21 +531,20 @@
  * Generate and send a root priming request.
  * @param qstate: the qtstate that triggered the need to prime.
  * @param iq: iterator query state.
- * @param ie: iterator global state.
  * @param id: module id.
  * @param qclass: the class to prime.
  * @return 0 on failure
  */
 static int
-prime_root(struct module_qstate* qstate, struct iter_qstate* iq, 
-	struct iter_env* ie, int id, uint16_t qclass)
+prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id,
+	uint16_t qclass)
 {
 	struct delegpt* dp;
 	struct module_qstate* subq;
 	verbose(VERB_DETAIL, "priming . %s NS", 
 		ldns_lookup_by_id(ldns_rr_classes, (int)qclass)?
 		ldns_lookup_by_id(ldns_rr_classes, (int)qclass)->name:"??");
-	dp = hints_lookup_root(ie->hints, qclass);
+	dp = hints_lookup_root(qstate->env->hints, qclass);
 	if(!dp) {
 		verbose(VERB_ALGO, "Cannot prime due to lack of hints");
 		return 0;
@@ -590,7 +588,6 @@
  *
  * @param qstate: the qtstate that triggered the need to prime.
  * @param iq: iterator query state.
- * @param ie: iterator global state.
  * @param id: module id.
  * @param q: request name.
  * @return true if a priming subrequest was made, false if not. The will only
@@ -599,8 +596,8 @@
  *         that a noprime-stub is available and resolution can continue.
  */
 static int
-prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, 
-	struct iter_env* ie, int id, struct query_info* q)
+prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
+	struct query_info* q)
 {
 	/* Lookup the stub hint. This will return null if the stub doesn't 
 	 * need to be re-primed. */
@@ -614,7 +611,8 @@
 		/* remove first label, but not for root */
 		dname_remove_label(&delname, &delnamelen);
 
-	stub = hints_lookup_stub(ie->hints, delname, q->qclass, iq->dp);
+	stub = hints_lookup_stub(qstate->env->hints, delname, q->qclass,
+		iq->dp);
 	/* The stub (if there is one) does not need priming. */
 	if(!stub)
 		return 0;
@@ -1042,14 +1040,15 @@
 		if(delname)
 		     iq->dp = dns_cache_find_delegation(qstate->env, delname, 
 			delnamelen, iq->qchase.qtype, iq->qchase.qclass, 
-			qstate->region, &iq->deleg_msg, *qstate->env->now);
+			qstate->region, &iq->deleg_msg,
+			*qstate->env->now+qstate->prefetch_leeway);
 		else iq->dp = NULL;
 
 		/* If the cache has returned nothing, then we have a 
 		 * root priming situation. */
 		if(iq->dp == NULL) {
 			/* if there is a stub, then no root prime needed */
-			int r = prime_stub(qstate, iq, ie, id, &iq->qchase);
+			int r = prime_stub(qstate, iq, id, &iq->qchase);
 			if(r == 2)
 				break; /* got noprime-stub-zone, continue */
 			else if(r)
@@ -1058,7 +1057,7 @@
 				iq->qchase.qclass)) {
 				/* forward zone root, no root prime needed */
 				/* fill in some dp - safety belt */
-				iq->dp = hints_lookup_root(ie->hints, 
+				iq->dp = hints_lookup_root(qstate->env->hints, 
 					iq->qchase.qclass);
 				if(!iq->dp) {
 					log_err("internal error: no hints dp");
@@ -1075,7 +1074,7 @@
 			}
 			/* Note that the result of this will set a new
 			 * DelegationPoint based on the result of priming. */
-			if(!prime_root(qstate, iq, ie, id, iq->qchase.qclass))
+			if(!prime_root(qstate, iq, id, iq->qchase.qclass))
 				return error_response(qstate, id, 
 					LDNS_RCODE_REFUSED);
 
@@ -1104,7 +1103,7 @@
 				/* use safety belt */
 				verbose(VERB_QUERY, "Cache has root NS but "
 				"no addresses. Fallback to the safety belt.");
-				iq->dp = hints_lookup_root(ie->hints, 
+				iq->dp = hints_lookup_root(qstate->env->hints, 
 					iq->qchase.qclass);
 				/* note deleg_msg is from previous lookup,
 				 * but RD is on, so it is not used */
@@ -1151,20 +1150,19 @@
  *
  * @param qstate: query state.
  * @param iq: iterator query state.
- * @param ie: iterator shared global environment.
  * @param id: module id.
  * @return true if the event needs more request processing immediately,
  *         false if not.
  */
 static int
 processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
-	struct iter_env* ie, int id)
+	int id)
 {
 	log_query_info(VERB_QUERY, "resolving (init part 2): ", 
 		&qstate->qinfo);
 
 	/* Check to see if we need to prime a stub zone. */
-	if(prime_stub(qstate, iq, ie, id, &iq->qchase)) {
+	if(prime_stub(qstate, iq, id, &iq->qchase)) {
 		/* A priming sub request was made */
 		return 0;
 	}
@@ -1261,7 +1259,8 @@
 		} else {
 			subiq->dp = dns_cache_find_delegation(qstate->env, 
 				name, namelen, qtype, qclass, subq->region,
-				&subiq->deleg_msg, *qstate->env->now); 
+				&subiq->deleg_msg,
+				*qstate->env->now+subq->prefetch_leeway); 
 			/* if no dp, then it's from root, refetch unneeded */
 			if(subiq->dp) { 
 				subiq->dnssec_expected = iter_indicates_dnssec(
@@ -1833,6 +1832,7 @@
 		}
 		if(!iter_dns_store(qstate->env, &iq->response->qinfo,
 			iq->response->rep, 0, qstate->prefetch_leeway,
+			iq->dp&&iq->dp->has_parent_side_NS,
 			qstate->region))
 			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
 		/* close down outstanding requests to be discarded */
@@ -1872,7 +1872,7 @@
 			/* Store the referral under the current query */
 			/* no prefetch-leeway, since its not the answer */
 			if(!iter_dns_store(qstate->env, &iq->response->qinfo,
-				iq->response->rep, 1, 0, NULL))
+				iq->response->rep, 1, 0, 0, NULL))
 				return error_response(qstate, id, 
 					LDNS_RCODE_SERVFAIL);
 			if(iq->store_parent_NS)
@@ -1958,7 +1958,9 @@
 		 * the partial query answer (CNAME only). */
 		/* prefetchleeway applied because this updates answer parts */
 		if(!iter_dns_store(qstate->env, &iq->response->qinfo,
-			iq->response->rep, 1, qstate->prefetch_leeway, NULL))
+			iq->response->rep, 1, qstate->prefetch_leeway,
+			iq->dp&&iq->dp->has_parent_side_NS,
+			NULL))
 			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
 		/* set the current request's qname to the new value. */
 		iq->qchase.qname = sname;
@@ -2336,7 +2338,6 @@
 processCollectClass(struct module_qstate* qstate, int id)
 {
 	struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
-	struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
 	struct module_qstate* subq;
 	/* If qchase.qclass == 0 then send out queries for all classes.
 	 * Otherwise, do nothing (wait for all answers to arrive and the
@@ -2345,7 +2346,8 @@
 	if(iq->qchase.qclass == 0) {
 		uint16_t c = 0;
 		iq->qchase.qclass = LDNS_RR_CLASS_ANY;
-		while(iter_get_next_root(ie->hints, qstate->env->fwds, &c)) {
+		while(iter_get_next_root(qstate->env->hints,
+			qstate->env->fwds, &c)) {
 			/* generate query for this class */
 			log_nametypeclass(VERB_ALGO, "spawn collect query",
 				qstate->qinfo.qname, qstate->qinfo.qtype, c);
@@ -2435,6 +2437,7 @@
 		if(qstate->query_flags&BIT_RD) {
 			if(!iter_dns_store(qstate->env, &qstate->qinfo, 
 				iq->response->rep, 0, qstate->prefetch_leeway,
+				iq->dp&&iq->dp->has_parent_side_NS,
 				qstate->region))
 				return error_response(qstate, id, 
 					LDNS_RCODE_SERVFAIL);
@@ -2493,7 +2496,7 @@
 				cont = processInitRequest(qstate, iq, ie, id);
 				break;
 			case INIT_REQUEST_2_STATE:
-				cont = processInitRequest2(qstate, iq, ie, id);
+				cont = processInitRequest2(qstate, iq, id);
 				break;
 			case INIT_REQUEST_3_STATE:
 				cont = processInitRequest3(qstate, iq, id);
@@ -2705,8 +2708,7 @@
 	if(!ie)
 		return 0;
 	return sizeof(*ie) + sizeof(int)*((size_t)ie->max_dependency_depth+1)
-		+ hints_get_mem(ie->hints) + donotq_get_mem(ie->donotq)
-		+ priv_get_mem(ie->priv);
+		+ donotq_get_mem(ie->donotq) + priv_get_mem(ie->priv);
 }
 
 /**
diff -Naur unbound-1.4.16/iterator/iterator.h unbound-trunk/iterator/iterator.h
--- unbound-1.4.16/iterator/iterator.h	2011-09-16 10:11:12.000000000 -0400
+++ unbound-trunk/iterator/iterator.h	2012-02-22 20:37:15.038368186 -0500
@@ -86,14 +86,6 @@
  * Global state for the iterator. 
  */
 struct iter_env {
-	/** 
-	 * The hints -- these aren't stored in the cache because they don't 
-	 * expire. The hints are always used to "prime" the cache. Note 
-	 * that both root hints and stub zone "hints" are stored in this 
-	 * data structure.
-	 */
-	struct iter_hints* hints;
-
 	/** A flag to indicate whether or not we have an IPv6 route */
 	int supports_ipv6;
 
diff -Naur unbound-1.4.16/iterator/iter_delegpt.c unbound-trunk/iterator/iter_delegpt.c
--- unbound-1.4.16/iterator/iter_delegpt.c	2011-09-07 10:34:10.000000000 -0400
+++ unbound-trunk/iterator/iter_delegpt.c	2012-02-22 20:37:15.040368186 -0500
@@ -90,6 +90,7 @@
 int 
 delegpt_set_name(struct delegpt* dp, struct regional* region, uint8_t* name)
 {
+	log_assert(!dp->dp_type_mlc);
 	dp->namelabs = dname_count_size_labels(name, &dp->namelen);
 	dp->name = regional_alloc_init(region, name, dp->namelen);
 	return dp->name != 0;
@@ -102,6 +103,7 @@
 	struct delegpt_ns* ns;
 	size_t len;
 	(void)dname_count_size_labels(name, &len);
+	log_assert(!dp->dp_type_mlc);
 	/* slow check for duplicates to avoid counting failures when
 	 * adding the same server as a dependency twice */
 	if(delegpt_find_ns(dp, name, len))
@@ -120,7 +122,7 @@
 	ns->lame = (uint8_t)lame;
 	ns->done_pside4 = 0;
 	ns->done_pside6 = 0;
-	return 1;
+	return ns->name != 0;
 }
 
 struct delegpt_ns*
@@ -157,6 +159,7 @@
 	socklen_t addrlen, int bogus, int lame)
 {
 	struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
+	log_assert(!dp->dp_type_mlc);
 	if(!ns) {
 		/* ignore it */
 		return 1;
@@ -177,6 +180,7 @@
 	int lame)
 {
 	struct delegpt_addr* a;
+	log_assert(!dp->dp_type_mlc);
 	/* check for duplicates */
 	if((a = delegpt_find_addr(dp, addr, addrlen))) {
 		if(bogus)
@@ -377,6 +381,7 @@
 	struct packed_rrset_data* nsdata = (struct packed_rrset_data*)
 		ns_rrset->entry.data;
 	size_t i;
+	log_assert(!dp->dp_type_mlc);
 	if(nsdata->security == sec_status_bogus)
 		dp->bogus = 1;
 	for(i=0; i<nsdata->count; i++) {
@@ -399,6 +404,7 @@
         size_t i;
         struct sockaddr_in sa;
         socklen_t len = (socklen_t)sizeof(sa);
+	log_assert(!dp->dp_type_mlc);
         memset(&sa, 0, len);
         sa.sin_family = AF_INET;
         sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
@@ -422,6 +428,7 @@
         size_t i;
         struct sockaddr_in6 sa;
         socklen_t len = (socklen_t)sizeof(sa);
+	log_assert(!dp->dp_type_mlc);
         memset(&sa, 0, len);
         sa.sin6_family = AF_INET6;
         sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
@@ -492,3 +499,141 @@
 			ns->resolved = 1;
 	}
 }
+
+struct delegpt* delegpt_create_mlc(uint8_t* name)
+{
+	struct delegpt* dp=(struct delegpt*)calloc(1, sizeof(*dp));
+	if(!dp)
+		return NULL;
+	memset(dp, 0, sizeof(*dp));
+	dp->dp_type_mlc = 1;
+	if(name) {
+		dp->namelabs = dname_count_size_labels(name, &dp->namelen);
+		dp->name = memdup(name, dp->namelen);
+		if(!dp->name) {
+			free(dp);
+			return NULL;
+		}
+	}
+	return dp;
+}
+
+void delegpt_free_mlc(struct delegpt* dp)
+{
+	struct delegpt_ns* n, *nn;
+	struct delegpt_addr* a, *na;
+	if(!dp) return;
+	log_assert(dp->dp_type_mlc);
+	n = dp->nslist;
+	while(n) {
+		nn = n->next;
+		free(n->name);
+		free(n);
+		n = nn;
+	}
+	a = dp->target_list;
+	while(a) {
+		na = a->next_target;
+		free(a);
+		a = na;
+	}
+	free(dp->name);
+	free(dp);
+}
+
+int delegpt_set_name_mlc(struct delegpt* dp, uint8_t* name)
+{
+	log_assert(dp->dp_type_mlc);
+	dp->namelabs = dname_count_size_labels(name, &dp->namelen);
+	dp->name = memdup(name, dp->namelen);
+	return (dp->name != NULL);
+}
+
+int delegpt_add_ns_mlc(struct delegpt* dp, uint8_t* name, int lame)
+{
+	struct delegpt_ns* ns;
+	size_t len;
+	(void)dname_count_size_labels(name, &len);
+	log_assert(dp->dp_type_mlc);
+	/* slow check for duplicates to avoid counting failures when
+	 * adding the same server as a dependency twice */
+	if(delegpt_find_ns(dp, name, len))
+		return 1;
+	ns = (struct delegpt_ns*)malloc(sizeof(struct delegpt_ns));
+	if(!ns)
+		return 0;
+	ns->namelen = len;
+	ns->name = memdup(name, ns->namelen);
+	if(!ns->name) {
+		free(ns);
+		return 0;
+	}
+	ns->next = dp->nslist;
+	dp->nslist = ns;
+	ns->resolved = 0;
+	ns->got4 = 0;
+	ns->got6 = 0;
+	ns->lame = (uint8_t)lame;
+	ns->done_pside4 = 0;
+	ns->done_pside6 = 0;
+	return 1;
+}
+
+int delegpt_add_addr_mlc(struct delegpt* dp, struct sockaddr_storage* addr,
+	socklen_t addrlen, int bogus, int lame)
+{
+	struct delegpt_addr* a;
+	log_assert(dp->dp_type_mlc);
+	/* check for duplicates */
+	if((a = delegpt_find_addr(dp, addr, addrlen))) {
+		if(bogus)
+			a->bogus = bogus;
+		if(!lame)
+			a->lame = 0;
+		return 1;
+	}
+
+	a = (struct delegpt_addr*)malloc(sizeof(struct delegpt_addr));
+	if(!a)
+		return 0;
+	a->next_target = dp->target_list;
+	dp->target_list = a;
+	a->next_result = 0;
+	a->next_usable = dp->usable_list;
+	dp->usable_list = a;
+	memcpy(&a->addr, addr, addrlen);
+	a->addrlen = addrlen;
+	a->attempts = 0;
+	a->bogus = bogus;
+	a->lame = lame;
+	return 1;
+}
+
+int delegpt_add_target_mlc(struct delegpt* dp, uint8_t* name, size_t namelen,
+	struct sockaddr_storage* addr, socklen_t addrlen, int bogus, int lame)
+{
+	struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
+	log_assert(dp->dp_type_mlc);
+	if(!ns) {
+		/* ignore it */
+		return 1;
+	}
+	if(!lame) {
+		if(addr_is_ip6(addr, addrlen))
+			ns->got6 = 1;
+		else	ns->got4 = 1;
+		if(ns->got4 && ns->got6)
+			ns->resolved = 1;
+	}
+	return delegpt_add_addr_mlc(dp, addr, addrlen, bogus, lame);
+}
+
+size_t delegpt_get_mem(struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	size_t s = sizeof(*dp) + dp->namelen +
+		delegpt_count_targets(dp)*sizeof(struct delegpt_addr);
+	for(ns=dp->nslist; ns; ns=ns->next)
+		s += sizeof(*ns)+ns->namelen;
+	return s;
+}
diff -Naur unbound-1.4.16/iterator/iter_delegpt.h unbound-trunk/iterator/iter_delegpt.h
--- unbound-1.4.16/iterator/iter_delegpt.h	2011-09-07 10:34:10.000000000 -0400
+++ unbound-trunk/iterator/iter_delegpt.h	2012-02-22 20:37:15.041368186 -0500
@@ -79,6 +79,8 @@
 	 * Also true if the delegationpoint was created from a delegation
 	 * message and thus contains the parent-side-info already. */
 	uint8_t has_parent_side_NS;
+	/** for assertions on type of delegpt */
+	uint8_t dp_type_mlc;
 };
 
 /**
@@ -346,4 +348,64 @@
  */
 void delegpt_no_ipv4(struct delegpt* dp);
 
+/** 
+ * create malloced delegation point, with the given name 
+ * @param name: uncompressed wireformat of degegpt name.
+ * @return NULL on alloc failure
+ */
+struct delegpt* delegpt_create_mlc(uint8_t* name);
+
+/** 
+ * free malloced delegation point.
+ * @param dp: must have been created with delegpt_create_mlc, free'd. 
+ */
+void delegpt_free_mlc(struct delegpt* dp);
+
+/**
+ * Set name of delegation point.
+ * @param dp: delegation point. malloced.
+ * @param name: name to use.
+ * @return false on error.
+ */
+int delegpt_set_name_mlc(struct delegpt* dp, uint8_t* name);
+
+/**
+ * add a name to malloced delegation point.
+ * @param dp: must have been created with delegpt_create_mlc. 
+ * @param name: the name to add.
+ * @param lame: the name is lame, disprefer.
+ * @return false on error.
+ */
+int delegpt_add_ns_mlc(struct delegpt* dp, uint8_t* name, int lame);
+
+/**
+ * add an address to a malloced delegation point.
+ * @param dp: must have been created with delegpt_create_mlc. 
+ * @param addr: the address.
+ * @param addrlen: the length of addr.
+ * @param bogus: if address is bogus.
+ * @param lame: if address is lame.
+ * @return false on error.
+ */
+int delegpt_add_addr_mlc(struct delegpt* dp, struct sockaddr_storage* addr,
+	socklen_t addrlen, int bogus, int lame);
+
+/**
+ * Add target address to the delegation point.
+ * @param dp: must have been created with delegpt_create_mlc. 
+ * @param name: name for which target was found (must be in nslist).
+ *	This name is marked resolved.
+ * @param namelen: length of name.
+ * @param addr: the address.
+ * @param addrlen: the length of addr.
+ * @param bogus: security status for the address, pass true if bogus.
+ * @param lame: address is lame.
+ * @return false on error.
+ */
+int delegpt_add_target_mlc(struct delegpt* dp, uint8_t* name, size_t namelen,
+	struct sockaddr_storage* addr, socklen_t addrlen, int bogus, int lame);
+
+/** get memory in use by dp */
+size_t delegpt_get_mem(struct delegpt* dp);
+
 #endif /* ITERATOR_ITER_DELEGPT_H */
diff -Naur unbound-1.4.16/iterator/iter_fwd.c unbound-trunk/iterator/iter_fwd.c
--- unbound-1.4.16/iterator/iter_fwd.c	2011-11-10 13:44:06.000000000 -0500
+++ unbound-trunk/iterator/iter_fwd.c	2012-02-22 20:37:15.039368186 -0500
@@ -45,7 +45,6 @@
 #include <ldns/rr.h>
 #include "iterator/iter_fwd.h"
 #include "iterator/iter_delegpt.h"
-#include "util/regional.h"
 #include "util/log.h"
 #include "util/config_file.h"
 #include "util/net_help.h"
@@ -73,21 +72,36 @@
 		sizeof(struct iter_forwards));
 	if(!fwd)
 		return NULL;
-	fwd->region = regional_create();
-	if(!fwd->region) {
-		forwards_delete(fwd);
-		return NULL;
-	}
 	return fwd;
 }
 
+static void fwd_zone_free(struct iter_forward_zone* n)
+{
+	if(!n) return;
+	delegpt_free_mlc(n->dp);
+	free(n->name);
+	free(n);
+}
+
+static void delfwdnode(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+	struct iter_forward_zone* node = (struct iter_forward_zone*)n;
+	fwd_zone_free(node);
+}
+
+static void fwd_del_tree(struct iter_forwards* fwd)
+{
+	if(fwd->tree)
+		traverse_postorder(fwd->tree, &delfwdnode, NULL);
+	free(fwd->tree);
+}
+
 void 
 forwards_delete(struct iter_forwards* fwd)
 {
 	if(!fwd) 
 		return;
-	regional_destroy(fwd->region);
-	free(fwd->tree);
+	fwd_del_tree(fwd);
 	free(fwd);
 }
 
@@ -96,20 +110,28 @@
 forwards_insert_data(struct iter_forwards* fwd, uint16_t c, uint8_t* nm, 
 	size_t nmlen, int nmlabs, struct delegpt* dp)
 {
-	struct iter_forward_zone* node = regional_alloc(fwd->region,
+	struct iter_forward_zone* node = (struct iter_forward_zone*)malloc(
 		sizeof(struct iter_forward_zone));
-	if(!node)
+	if(!node) {
+		delegpt_free_mlc(dp);
 		return 0;
+	}
 	node->node.key = node;
 	node->dclass = c;
-	node->name = regional_alloc_init(fwd->region, nm, nmlen);
-	if(!node->name)
+	node->name = memdup(nm, nmlen);
+	if(!node->name) {
+		delegpt_free_mlc(dp);
+		free(node);
 		return 0;
+	}
 	node->namelen = nmlen;
 	node->namelabs = nmlabs;
 	node->dp = dp;
 	if(!rbtree_insert(fwd->tree, &node->node)) {
 		log_err("duplicate forward zone ignored.");
+		delegpt_free_mlc(dp);
+		free(node->name);
+		free(node);
 	}
 	return 1;
 }
@@ -152,33 +174,32 @@
 }
 
 /** set zone name */
-static int 
-read_fwds_name(struct iter_forwards* fwd, struct config_stub* s, 
-	struct delegpt* dp)
+static struct delegpt* 
+read_fwds_name(struct config_stub* s)
 {
+	struct delegpt* dp;
 	ldns_rdf* rdf;
 	if(!s->name) {
 		log_err("forward zone without a name (use name \".\" to forward everything)");
-		return 0;
+		return NULL;
 	}
 	rdf = ldns_dname_new_frm_str(s->name);
 	if(!rdf) {
 		log_err("cannot parse forward zone name %s", s->name);
-		return 0;
+		return NULL;
 	}
-	if(!delegpt_set_name(dp, fwd->region, ldns_rdf_data(rdf))) {
+	if(!(dp=delegpt_create_mlc(ldns_rdf_data(rdf)))) {
 		ldns_rdf_deep_free(rdf);
 		log_err("out of memory");
-		return 0;
+		return NULL;
 	}
 	ldns_rdf_deep_free(rdf);
-	return 1;
+	return dp;
 }
 
 /** set fwd host names */
 static int 
-read_fwds_host(struct iter_forwards* fwd, struct config_stub* s, 
-	struct delegpt* dp)
+read_fwds_host(struct config_stub* s, struct delegpt* dp)
 {
 	struct config_strlist* p;
 	ldns_rdf* rdf;
@@ -190,7 +211,7 @@
 				s->name, p->str);
 			return 0;
 		}
-		if(!delegpt_add_ns(dp, fwd->region, ldns_rdf_data(rdf), 0)) {
+		if(!delegpt_add_ns_mlc(dp, ldns_rdf_data(rdf), 0)) {
 			ldns_rdf_deep_free(rdf);
 			log_err("out of memory");
 			return 0;
@@ -202,8 +223,7 @@
 
 /** set fwd server addresses */
 static int 
-read_fwds_addr(struct iter_forwards* fwd, struct config_stub* s, 
-	struct delegpt* dp)
+read_fwds_addr(struct config_stub* s, struct delegpt* dp)
 {
 	struct config_strlist* p;
 	struct sockaddr_storage addr;
@@ -215,7 +235,7 @@
 				s->name, p->str);
 			return 0;
 		}
-		if(!delegpt_add_addr(dp, fwd->region, &addr, addrlen, 0, 0)) {
+		if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0)) {
 			log_err("out of memory");
 			return 0;
 		}
@@ -229,18 +249,14 @@
 {
 	struct config_stub* s;
 	for(s = cfg->forwards; s; s = s->next) {
-		struct delegpt* dp = delegpt_create(fwd->region);
-		if(!dp) {
-			log_err("out of memory");
+		struct delegpt* dp;
+		if(!(dp=read_fwds_name(s)) ||
+			!read_fwds_host(s, dp) ||
+			!read_fwds_addr(s, dp))
 			return 0;
-		}
 		/* set flag that parent side NS information is included.
 		 * Asking a (higher up) server on the internet is not useful */
 		dp->has_parent_side_NS = 1;
-		if(!read_fwds_name(fwd, s, dp) ||
-			!read_fwds_host(fwd, s, dp) ||
-			!read_fwds_addr(fwd, s, dp))
-			return 0;
 		if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp))
 			return 0;
 		verbose(VERB_QUERY, "Forward zone server list:");
@@ -268,28 +284,35 @@
 	return 0; /* no forwards above, no holes needed */
 }
 
+/** insert a stub hole (if necessary) for stub name */
+static int
+fwd_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
+{
+	struct iter_forward_zone key;
+	key.node.key = &key;
+	key.dclass = c;
+	key.name = nm;
+	key.namelabs = dname_count_size_labels(key.name, &key.namelen);
+	if(need_hole_insert(fwd->tree, &key)) {
+		return forwards_insert_data(fwd, key.dclass, key.name,
+			key.namelen, key.namelabs, NULL);
+	}
+	return 1;
+}
+
 /** make NULL entries for stubs */
 static int
 make_stub_holes(struct iter_forwards* fwd, struct config_file* cfg)
 {
 	struct config_stub* s;
-	struct iter_forward_zone key;
-	key.node.key = &key;
-	key.dclass = LDNS_RR_CLASS_IN;
 	for(s = cfg->stubs; s; s = s->next) {
 		ldns_rdf* rdf = ldns_dname_new_frm_str(s->name);
 		if(!rdf) {
 			log_err("cannot parse stub name '%s'", s->name);
 			return 0;
 		}
-		key.name = ldns_rdf_data(rdf);
-		key.namelabs = dname_count_size_labels(key.name, &key.namelen);
-		if(!need_hole_insert(fwd->tree, &key)) {
-			ldns_rdf_deep_free(rdf);
-			continue;
-		}
-		if(!forwards_insert_data(fwd, key.dclass, key.name, 
-			key.namelen, key.namelabs, NULL)) {
+		if(!fwd_add_stub_hole(fwd, LDNS_RR_CLASS_IN,
+				ldns_rdf_data(rdf))) {
 			ldns_rdf_deep_free(rdf);
 			log_err("out of memory");
 			return 0;
@@ -302,8 +325,7 @@
 int 
 forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
 {
-	free(fwd->tree);
-	regional_free_all(fwd->region);
+	fwd_del_tree(fwd);
 	fwd->tree = rbtree_create(fwd_cmp);
 	if(!fwd->tree)
 		return 0;
@@ -411,15 +433,36 @@
 size_t 
 forwards_get_mem(struct iter_forwards* fwd)
 {
+	struct iter_forward_zone* p;
+	size_t s;
 	if(!fwd)
 		return 0;
-	return sizeof(*fwd) + sizeof(*fwd->tree) + 
-		regional_get_mem(fwd->region);
+	s = sizeof(*fwd) + sizeof(*fwd->tree);
+	RBTREE_FOR(p, struct iter_forward_zone*, fwd->tree) {
+		s += sizeof(*p) + p->namelen + delegpt_get_mem(p->dp);
+	}
+	return s;
+}
+
+static struct iter_forward_zone*
+fwd_zone_find(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
+{
+	struct iter_forward_zone key;
+	key.node.key = &key;
+	key.dclass = c;
+	key.name = nm;
+	key.namelabs = dname_count_size_labels(nm, &key.namelen);
+	return (struct iter_forward_zone*)rbtree_search(fwd->tree, &key);
 }
 
 int 
 forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
 {
+	struct iter_forward_zone *z;
+	if((z=fwd_zone_find(fwd, c, dp->name)) != NULL) {
+		(void)rbtree_delete(fwd->tree, &z->node);
+		fwd_zone_free(z);
+	}
 	if(!forwards_insert(fwd, c, dp))
 		return 0;
 	fwd_init_parents(fwd);
@@ -429,14 +472,34 @@
 void 
 forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
 {
-	struct iter_forward_zone key;
-	key.node.key = &key;
-	key.dclass = c;
-	key.name = nm;
-	key.namelabs = dname_count_size_labels(nm, &key.namelen);
-	if(!rbtree_search(fwd->tree, &key))
+	struct iter_forward_zone *z;
+	if(!(z=fwd_zone_find(fwd, c, nm)))
+		return; /* nothing to do */
+	(void)rbtree_delete(fwd->tree, &z->node);
+	fwd_zone_free(z);
+	fwd_init_parents(fwd);
+}
+
+int
+forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
+{
+	if(!fwd_add_stub_hole(fwd, c, nm)) {
+		return 0;
+	}
+	fwd_init_parents(fwd);
+	return 1;
+}
+
+void
+forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
+{
+	struct iter_forward_zone *z;
+	if(!(z=fwd_zone_find(fwd, c, nm)))
 		return; /* nothing to do */
-	(void)rbtree_delete(fwd->tree, &key);
+	if(z->dp != NULL)
+		return; /* not a stub hole */
+	(void)rbtree_delete(fwd->tree, &z->node);
+	fwd_zone_free(z);
 	fwd_init_parents(fwd);
 }
 
diff -Naur unbound-1.4.16/iterator/iter_fwd.h unbound-trunk/iterator/iter_fwd.h
--- unbound-1.4.16/iterator/iter_fwd.h	2010-07-07 09:13:36.000000000 -0400
+++ unbound-trunk/iterator/iter_fwd.h	2012-02-22 20:37:15.041368186 -0500
@@ -45,14 +45,11 @@
 #include "util/rbtree.h"
 struct config_file;
 struct delegpt;
-struct regional;
 
 /**
  * Iterator forward zones structure
  */
 struct iter_forwards {
-	/** regional where forward zone server addresses are allocated */
-	struct regional* region;
 	/** 
 	 * Zones are stored in this tree. Sort order is specially chosen.
 	 * first sorted on qclass. Then on dname in nsec-like order, so that
@@ -77,7 +74,9 @@
 	int namelabs;
 	/** delegation point with forward server information for this zone. 
 	 * If NULL then this forward entry is used to indicate that a
-	 * stub-zone with the same name exists, and should be used. */
+	 * stub-zone with the same name exists, and should be used. 
+	 * This delegation point is malloced.
+	 */
 	struct delegpt* dp;
 	/** pointer to parent in tree (or NULL if none) */
 	struct iter_forward_zone* parent;
@@ -152,9 +151,7 @@
  * @param fwd: the forward data structure
  * @param c: class of zone
  * @param dp: delegation point with name and target nameservers for new
- *	forward zone. This delegation point and all its data must be
- *	malloced in the fwd->region. (then it is freed when the fwd is
- *	deleted).
+ *	forward zone. malloced.
  * @return false on failure (out of memory);
  */
 int forwards_add_zone(struct iter_forwards* fwd, uint16_t c, 
@@ -162,12 +159,31 @@
 
 /**
  * Remove zone from forward structure. For external use since it 
- * recalcs the tree parents. Does not actually release any memory, the region 
- * is unchanged.
+ * recalcs the tree parents.
  * @param fwd: the forward data structure
  * @param c: class of zone
  * @param nm: name of zone (in uncompressed wireformat).
  */
 void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm);
 
+/**
+ * Add stub hole (empty entry in forward table, that makes resolution skip
+ * a forward-zone because the stub zone should override the forward zone).
+ * Does not add one if not necessary.
+ * @param fwd: the forward data structure
+ * @param c: class of zone
+ * @param nm: name of zone (in uncompressed wireformat).
+ * @return false on failure (out of memory);
+ */
+int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm);
+
+/**
+ * Remove stub hole, if one exists.
+ * @param fwd: the forward data structure
+ * @param c: class of zone
+ * @param nm: name of zone (in uncompressed wireformat).
+ */
+void forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
+	uint8_t* nm);
+
 #endif /* ITERATOR_ITER_FWD_H */
diff -Naur unbound-1.4.16/iterator/iter_hints.c unbound-trunk/iterator/iter_hints.c
--- unbound-1.4.16/iterator/iter_hints.c	2011-11-10 13:44:06.000000000 -0500
+++ unbound-trunk/iterator/iter_hints.c	2012-02-22 20:37:15.041368186 -0500
@@ -44,7 +44,6 @@
 #include <ldns/rr.h>
 #include "iterator/iter_hints.h"
 #include "iterator/iter_delegpt.h"
-#include "util/regional.h"
 #include "util/log.h"
 #include "util/config_file.h"
 #include "util/net_help.h"
@@ -57,26 +56,39 @@
 		sizeof(struct iter_hints));
 	if(!hints)
 		return NULL;
-	hints->region = regional_create();
-	if(!hints->region) {
-		hints_delete(hints);
-		return NULL;
-	}
 	return hints;
 }
 
+static void hints_stub_free(struct iter_hints_stub* s)
+{
+	if(!s) return;
+	delegpt_free_mlc(s->dp);
+	free(s);
+}
+
+static void delhintnode(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+	struct iter_hints_stub* node = (struct iter_hints_stub*)n;
+	hints_stub_free(node);
+}
+
+static void hints_del_tree(struct iter_hints* hints)
+{
+	traverse_postorder(&hints->tree, &delhintnode, NULL);
+}
+
 void 
 hints_delete(struct iter_hints* hints)
 {
 	if(!hints) 
 		return;
-	regional_destroy(hints->region);
+	hints_del_tree(hints);
 	free(hints);
 }
 
 /** add hint to delegation hints */
 static int
-ah(struct delegpt* dp, struct regional* r, const char* sv, const char* ip)
+ah(struct delegpt* dp, const char* sv, const char* ip)
 {
 	struct sockaddr_storage addr;
 	socklen_t addrlen;
@@ -85,9 +97,9 @@
 		log_err("could not parse %s", sv);
 		return 0;
 	}
-	if(!delegpt_add_ns(dp, r, ldns_rdf_data(rdf), 0) ||
+	if(!delegpt_add_ns_mlc(dp, ldns_rdf_data(rdf), 0) ||
 	   !extstrtoaddr(ip, &addr, &addrlen) ||
-	   !delegpt_add_target(dp, r, ldns_rdf_data(rdf), ldns_rdf_size(rdf),
+	   !delegpt_add_target_mlc(dp, ldns_rdf_data(rdf), ldns_rdf_size(rdf),
 		&addr, addrlen, 0, 0)) {
 		ldns_rdf_deep_free(rdf);
 		return 0;
@@ -98,7 +110,7 @@
 
 /** obtain compiletime provided root hints */
 static struct delegpt* 
-compile_time_root_prime(struct regional* r, int do_ip4, int do_ip6)
+compile_time_root_prime(int do_ip4, int do_ip6)
 {
 	/* from:
 	 ;       This file is made available by InterNIC
@@ -109,37 +121,35 @@
 	 ;
 	 ;       related version of root zone:   2010061700
 	 */
-	struct delegpt* dp = delegpt_create(r);
+	struct delegpt* dp = delegpt_create_mlc((uint8_t*)"\000");
 	if(!dp)
 		return NULL;
 	dp->has_parent_side_NS = 1;
-	if(!delegpt_set_name(dp, r, (uint8_t*)"\000"))
-		return NULL;
       if(do_ip4) {
-	if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "198.41.0.4"))	return 0;
-	if(!ah(dp, r, "B.ROOT-SERVERS.NET.", "192.228.79.201")) return 0;
-	if(!ah(dp, r, "C.ROOT-SERVERS.NET.", "192.33.4.12"))	return 0;
-	if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "128.8.10.90"))	return 0;
-	if(!ah(dp, r, "E.ROOT-SERVERS.NET.", "192.203.230.10")) return 0;
-	if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "192.5.5.241"))	return 0;
-	if(!ah(dp, r, "G.ROOT-SERVERS.NET.", "192.112.36.4"))	return 0;
-	if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "128.63.2.53"))	return 0;
-	if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "192.36.148.17"))	return 0;
-	if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "192.58.128.30"))	return 0;
-	if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "193.0.14.129"))	return 0;
-	if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "199.7.83.42"))	return 0;
-	if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "202.12.27.33"))	return 0;
+	if(!ah(dp, "A.ROOT-SERVERS.NET.", "198.41.0.4"))	return 0;
+	if(!ah(dp, "B.ROOT-SERVERS.NET.", "192.228.79.201")) return 0;
+	if(!ah(dp, "C.ROOT-SERVERS.NET.", "192.33.4.12"))	return 0;
+	if(!ah(dp, "D.ROOT-SERVERS.NET.", "128.8.10.90"))	return 0;
+	if(!ah(dp, "E.ROOT-SERVERS.NET.", "192.203.230.10")) return 0;
+	if(!ah(dp, "F.ROOT-SERVERS.NET.", "192.5.5.241"))	return 0;
+	if(!ah(dp, "G.ROOT-SERVERS.NET.", "192.112.36.4"))	return 0;
+	if(!ah(dp, "H.ROOT-SERVERS.NET.", "128.63.2.53"))	return 0;
+	if(!ah(dp, "I.ROOT-SERVERS.NET.", "192.36.148.17"))	return 0;
+	if(!ah(dp, "J.ROOT-SERVERS.NET.", "192.58.128.30"))	return 0;
+	if(!ah(dp, "K.ROOT-SERVERS.NET.", "193.0.14.129"))	return 0;
+	if(!ah(dp, "L.ROOT-SERVERS.NET.", "199.7.83.42"))	return 0;
+	if(!ah(dp, "M.ROOT-SERVERS.NET.", "202.12.27.33"))	return 0;
       }
       if(do_ip6) {
-	if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) return 0;
-	if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) return 0;
-	if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) return 0;
-	if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) return 0;
-	if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) return 0;
-	if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) return 0;
-	if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) return 0;
-	if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) return 0;
-	if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) return 0;
+	if(!ah(dp, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) return 0;
+	if(!ah(dp, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) return 0;
+	if(!ah(dp, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) return 0;
+	if(!ah(dp, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) return 0;
+	if(!ah(dp, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) return 0;
+	if(!ah(dp, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) return 0;
+	if(!ah(dp, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) return 0;
+	if(!ah(dp, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) return 0;
+	if(!ah(dp, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) return 0;
       }
 	return dp;
 }
@@ -149,51 +159,50 @@
 hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
 	int noprime)
 {
-	struct iter_hints_stub* node = regional_alloc(hints->region,
+	struct iter_hints_stub* node = (struct iter_hints_stub*)malloc(
 		sizeof(struct iter_hints_stub));
-	uint8_t* nm;
-	if(!node)
-		return 0;
-	nm = regional_alloc_init(hints->region, dp->name, dp->namelen);
-	if(!nm)
+	if(!node) {
+		delegpt_free_mlc(dp);
 		return 0;
+	}
 	node->dp = dp;
 	node->noprime = (uint8_t)noprime;
-	if(!name_tree_insert(&hints->tree, &node->node, nm, dp->namelen,
+	if(!name_tree_insert(&hints->tree, &node->node, dp->name, dp->namelen,
 		dp->namelabs, c)) {
 		log_err("second hints ignored.");
+		delegpt_free_mlc(dp);
+		free(node);
 	}
 	return 1;
 }
 
 /** set stub name */
-static int 
-read_stubs_name(struct iter_hints* hints, struct config_stub* s, 
-	struct delegpt* dp)
+static struct delegpt* 
+read_stubs_name(struct config_stub* s)
 {
+	struct delegpt* dp;
 	ldns_rdf* rdf;
 	if(!s->name) {
 		log_err("stub zone without a name");
-		return 0;
+		return NULL;
 	}
 	rdf = ldns_dname_new_frm_str(s->name);
 	if(!rdf) {
 		log_err("cannot parse stub zone name %s", s->name);
-		return 0;
+		return NULL;
 	}
-	if(!delegpt_set_name(dp, hints->region, ldns_rdf_data(rdf))) {
+	if(!(dp=delegpt_create_mlc(ldns_rdf_data(rdf)))) {
 		ldns_rdf_deep_free(rdf);
 		log_err("out of memory");
-		return 0;
+		return NULL;
 	}
 	ldns_rdf_deep_free(rdf);
-	return 1;
+	return dp;
 }
 
 /** set stub host names */
 static int 
-read_stubs_host(struct iter_hints* hints, struct config_stub* s, 
-	struct delegpt* dp)
+read_stubs_host(struct config_stub* s, struct delegpt* dp)
 {
 	struct config_strlist* p;
 	ldns_rdf* rdf;
@@ -205,7 +214,7 @@
 				s->name, p->str);
 			return 0;
 		}
-		if(!delegpt_add_ns(dp, hints->region, ldns_rdf_data(rdf), 0)) {
+		if(!delegpt_add_ns_mlc(dp, ldns_rdf_data(rdf), 0)) {
 			ldns_rdf_deep_free(rdf);
 			log_err("out of memory");
 			return 0;
@@ -217,8 +226,7 @@
 
 /** set stub server addresses */
 static int 
-read_stubs_addr(struct iter_hints* hints, struct config_stub* s, 
-	struct delegpt* dp)
+read_stubs_addr(struct config_stub* s, struct delegpt* dp)
 {
 	struct config_strlist* p;
 	struct sockaddr_storage addr;
@@ -230,7 +238,7 @@
 				s->name, p->str);
 			return 0;
 		}
-		if(!delegpt_add_addr(dp, hints->region, &addr, addrlen, 0, 0)) {
+		if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0)) {
 			log_err("out of memory");
 			return 0;
 		}
@@ -243,17 +251,13 @@
 read_stubs(struct iter_hints* hints, struct config_file* cfg)
 {
 	struct config_stub* s;
+	struct delegpt* dp;
 	for(s = cfg->stubs; s; s = s->next) {
-		struct delegpt* dp = delegpt_create(hints->region);
-		if(!dp) {
-			log_err("out of memory");
+		if(!(dp=read_stubs_name(s)) ||
+			!read_stubs_host(s, dp) ||
+			!read_stubs_addr(s, dp))
 			return 0;
-		}
 		dp->has_parent_side_NS = 1;
-		if(!read_stubs_name(hints, s, dp) ||
-			!read_stubs_host(hints, s, dp) ||
-			!read_stubs_addr(hints, s, dp))
-			return 0;
 		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime))
 			return 0;
 		delegpt_log(VERB_QUERY, dp);
@@ -279,7 +283,7 @@
 			fname, strerror(errno));
 		return 0;
 	}
-	dp = delegpt_create(hints->region);
+	dp = delegpt_create_mlc(NULL);
 	if(!dp) {
 		log_err("out of memory reading root hints");
 		fclose(f);
@@ -300,14 +304,14 @@
 			goto stop_read;
 		}
 		if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NS) {
-			if(!delegpt_add_ns(dp, hints->region,
+			if(!delegpt_add_ns_mlc(dp,
 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), 0)) {
 				log_err("out of memory reading root hints");
 				goto stop_read;
 			}
 			c = ldns_rr_get_class(rr);
 			if(!dp->name) {
-				if(!delegpt_set_name(dp, hints->region, 
+				if(!delegpt_set_name_mlc(dp,
 					ldns_rdf_data(ldns_rr_owner(rr)))){
 					log_err("out of memory.");
 					goto stop_read;
@@ -321,7 +325,7 @@
 			sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
 			memmove(&sa.sin_addr, 
 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET_SIZE);
-			if(!delegpt_add_target(dp, hints->region,
+			if(!delegpt_add_target_mlc(dp,
 					ldns_rdf_data(ldns_rr_owner(rr)),
 					ldns_rdf_size(ldns_rr_owner(rr)),
 					(struct sockaddr_storage*)&sa, len, 
@@ -337,7 +341,7 @@
 			sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
 			memmove(&sa.sin6_addr, 
 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET6_SIZE);
-			if(!delegpt_add_target(dp, hints->region,
+			if(!delegpt_add_target_mlc(dp,
 					ldns_rdf_data(ldns_rr_owner(rr)),
 					ldns_rdf_size(ldns_rr_owner(rr)),
 					(struct sockaddr_storage*)&sa, len,
@@ -360,6 +364,7 @@
 	fclose(f);
 	if(!dp->name) {
 		log_warn("root hints %s: no NS content", fname);
+		delegpt_free_mlc(dp);
 		return 1;
 	}
 	if(!hints_insert(hints, c, dp, 0)) {
@@ -373,6 +378,7 @@
 		ldns_rdf_deep_free(origin);
 	if (prev_rr)
 		ldns_rdf_deep_free(prev_rr);
+	delegpt_free_mlc(dp);
 	fclose(f);
 	return 0;
 }
@@ -400,7 +406,7 @@
 int 
 hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
 {
-	regional_free_all(hints->region);
+	hints_del_tree(hints);
 	name_tree_init(&hints->tree);
 	
 	/* read root hints */
@@ -413,8 +419,8 @@
 
 	/* use fallback compiletime root hints */
 	if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) {
-		struct delegpt* dp = compile_time_root_prime(hints->region,
-			cfg->do_ip4, cfg->do_ip6);
+		struct delegpt* dp = compile_time_root_prime(cfg->do_ip4,
+			cfg->do_ip6);
 		verbose(VERB_ALGO, "no config, using builtin root hints.");
 		if(!dp) 
 			return 0;
@@ -483,6 +489,43 @@
 size_t 
 hints_get_mem(struct iter_hints* hints)
 {
+	size_t s;
+	struct iter_hints_stub* p;
 	if(!hints) return 0;
-	return sizeof(*hints) + regional_get_mem(hints->region);
+	s = sizeof(*hints);
+	RBTREE_FOR(p, struct iter_hints_stub*, &hints->tree) {
+		s += sizeof(*p) + delegpt_get_mem(p->dp);
+	}
+	return s;
 }
+
+int 
+hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
+	int noprime)
+{
+	struct iter_hints_stub *z;
+	if((z=(struct iter_hints_stub*)name_tree_find(&hints->tree,
+		dp->name, dp->namelen, dp->namelabs, c)) != NULL) {
+		(void)rbtree_delete(&hints->tree, &z->node);
+		hints_stub_free(z);
+	}
+	if(!hints_insert(hints, c, dp, noprime))
+		return 0;
+	name_tree_init_parents(&hints->tree);
+	return 1;
+}
+
+void 
+hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm)
+{
+	struct iter_hints_stub *z;
+	size_t len;
+	int labs = dname_count_size_labels(nm, &len);
+	if(!(z=(struct iter_hints_stub*)name_tree_find(&hints->tree,
+		nm, len, labs, c)))
+		return; /* nothing to do */
+	(void)rbtree_delete(&hints->tree, &z->node);
+	hints_stub_free(z);
+	name_tree_init_parents(&hints->tree);
+}
+
diff -Naur unbound-1.4.16/iterator/iter_hints.h unbound-trunk/iterator/iter_hints.h
--- unbound-1.4.16/iterator/iter_hints.h	2010-07-07 09:13:36.000000000 -0400
+++ unbound-trunk/iterator/iter_hints.h	2012-02-22 20:37:15.042368186 -0500
@@ -46,14 +46,11 @@
 struct iter_env;
 struct config_file;
 struct delegpt;
-struct regional;
 
 /**
  * Iterator hints structure
  */
 struct iter_hints {
-	/** regional where hints are allocated */
-	struct regional* region;
 	/** 
 	 * Hints are stored in this tree. Sort order is specially chosen.
 	 * first sorted on qclass. Then on dname in nsec-like order, so that
@@ -71,7 +68,7 @@
 struct iter_hints_stub {
 	/** tree sorted by name, class */
 	struct name_tree_node node;
-	/** delegation point with hint information for this stub. */
+	/** delegation point with hint information for this stub. malloced. */
 	struct delegpt* dp;
 	/** does the stub need to forego priming (like on other ports) */
 	uint8_t noprime;
@@ -139,4 +136,26 @@
  */
 size_t hints_get_mem(struct iter_hints* hints);
 
+/**
+ * Add stub to hints structure. For external use since it recalcs 
+ * the tree parents.
+ * @param hints: the hints data structure
+ * @param c: class of zone
+ * @param dp: delegation point with name and target nameservers for new
+ *	hints stub. malloced.
+ * @param noprime: set noprime option to true or false on new hint stub.
+ * @return false on failure (out of memory);
+ */
+int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
+	int noprime);
+
+/**
+ * Remove stub from hints structure. For external use since it 
+ * recalcs the tree parents.
+ * @param hints: the hints data structure
+ * @param c: class of stub zone
+ * @param nm: name of stub zone (in uncompressed wireformat).
+ */
+void hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm);
+
 #endif /* ITERATOR_ITER_HINTS_H */
diff -Naur unbound-1.4.16/iterator/iter_utils.c unbound-trunk/iterator/iter_utils.c
--- unbound-1.4.16/iterator/iter_utils.c	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/iterator/iter_utils.c	2012-02-22 20:37:15.042368186 -0500
@@ -113,12 +113,6 @@
 		verbose(VERB_QUERY, "target fetch policy for level %d is %d",
 			i, iter_env->target_fetch_policy[i]);
 	
-	if(!iter_env->hints)
-		iter_env->hints = hints_create();
-	if(!iter_env->hints || !hints_apply_cfg(iter_env->hints, cfg)) {
-		log_err("Could not set root or stub hints");
-		return 0;
-	}
 	if(!iter_env->donotq)
 		iter_env->donotq = donotq_create();
 	if(!iter_env->donotq || !donotq_apply_cfg(iter_env->donotq, cfg)) {
@@ -425,11 +419,11 @@
 
 int 
 iter_dns_store(struct module_env* env, struct query_info* msgqinf,
-	struct reply_info* msgrep, int is_referral, uint32_t leeway,
+	struct reply_info* msgrep, int is_referral, uint32_t leeway, int pside,
 	struct regional* region)
 {
 	return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
-		region);
+		pside, region);
 }
 
 int 
diff -Naur unbound-1.4.16/iterator/iter_utils.h unbound-trunk/iterator/iter_utils.h
--- unbound-1.4.16/iterator/iter_utils.h	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/iterator/iter_utils.h	2012-02-22 20:37:15.042368186 -0500
@@ -121,11 +121,13 @@
  * @param is_referral: If true, then the given message to be stored is a
  *	referral. The cache implementation may use this as a hint.
  * @param leeway: prefetch TTL leeway to expire old rrsets quicker.
+ * @param pside: true if dp is parentside, thus message is 'fresh' and NS
+ * 	can be prefetch-updates.
  * @param region: to copy modified (cache is better) rrs back to.
  * @return 0 on alloc error (out of memory).
  */
 int iter_dns_store(struct module_env* env, struct query_info* qinf,
-	struct reply_info* rep, int is_referral, uint32_t leeway,
+	struct reply_info* rep, int is_referral, uint32_t leeway, int pside,
 	struct regional* region);
 
 /**
diff -Naur unbound-1.4.16/libunbound/libworker.c unbound-trunk/libunbound/libworker.c
--- unbound-1.4.16/libunbound/libworker.c	2012-01-10 10:07:16.000000000 -0500
+++ unbound-trunk/libunbound/libworker.c	2012-02-22 20:37:15.600368186 -0500
@@ -66,6 +66,7 @@
 #include "util/data/msgencode.h"
 #include "util/tube.h"
 #include "iterator/iter_fwd.h"
+#include "iterator/iter_hints.h"
 
 /** handle new query command for bg worker */
 static void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len);
@@ -83,6 +84,7 @@
 		ldns_buffer_free(w->env->scratch_buffer);
 		regional_destroy(w->env->scratch);
 		forwards_delete(w->env->fwds);
+		hints_delete(w->env->hints);
 		ub_randfree(w->env->rnd);
 		free(w->env);
 	}
@@ -127,17 +129,24 @@
 		forwards_delete(w->env->fwds);
 		w->env->fwds = NULL;
 	}
+	w->env->hints = hints_create();
+	if(w->env->hints && !hints_apply_cfg(w->env->hints, cfg)) { 
+		hints_delete(w->env->hints);
+		w->env->hints = NULL;
+	}
 	if(cfg->ssl_upstream) {
 		w->sslctx = connect_sslctx_create(NULL, NULL, NULL);
 		if(!w->sslctx) {
-			libworker_delete(w);
-			return NULL;
+			/* to make the setup fail after unlock */
+			hints_delete(w->env->hints);
+			w->env->hints = NULL;
 		}
 	}
 	if(!w->is_bg || w->is_bg_thread) {
 		lock_basic_unlock(&ctx->cfglock);
 	}
-	if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds) {
+	if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds ||
+		!w->env->hints) {
 		libworker_delete(w);
 		return NULL;
 	}
diff -Naur unbound-1.4.16/pythonmod/pythonmod_utils.c unbound-trunk/pythonmod/pythonmod_utils.c
--- unbound-1.4.16/pythonmod/pythonmod_utils.c	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/pythonmod/pythonmod_utils.c	2012-02-22 20:37:12.805368182 -0500
@@ -66,7 +66,7 @@
     }
 
     return dns_cache_store(qstate->env, qinfo, msgrep, is_referral, 
-	qstate->prefetch_leeway, NULL);
+	qstate->prefetch_leeway, 0, NULL);
 }
 
 /*  Invalidate the message associated with query_info stored in message cache */
diff -Naur unbound-1.4.16/services/cache/dns.c unbound-trunk/services/cache/dns.c
--- unbound-1.4.16/services/cache/dns.c	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/services/cache/dns.c	2012-02-22 20:37:14.065368184 -0500
@@ -55,12 +55,20 @@
  * @param env: module environment with caches.
  * @param rep: contains list of rrsets to store.
  * @param now: current time.
+ * @param leeway: during prefetch how much leeway to update TTLs.
+ * 	This makes rrsets (other than type NS) timeout sooner so they get
+ * 	updated with a new full TTL.
+ * 	Type NS does not get this, because it must not be refreshed from the
+ * 	child domain, but keep counting down properly.
+ * @param pside: if from parentside discovered NS, so that its NS is okay
+ * 	in a prefetch situation to be updated (without becoming sticky).
  * @param qrep: update rrsets here if cache is better
  * @param region: for qrep allocs.
  */
 static void
 store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now,
-	struct reply_info* qrep, struct regional* region)
+	uint32_t leeway, int pside, struct reply_info* qrep,
+	struct regional* region)
 {
         size_t i;
         /* see if rrset already exists in cache, if not insert it. */
@@ -69,7 +77,8 @@
                 rep->ref[i].id = rep->rrsets[i]->id;
 		/* update ref if it was in the cache */ 
 		switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
-                        env->alloc, now)) {
+                        env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)==
+			LDNS_RR_TYPE_NS && !pside)?0:leeway))) {
 		case 0: /* ref unchanged, item inserted */
 			break;
 		case 2: /* ref updated, cache is superior */
@@ -96,7 +105,7 @@
 
 void 
 dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
-	hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
+	hashvalue_t hash, struct reply_info* rep, uint32_t leeway, int pside,
 	struct reply_info* qrep, struct regional* region)
 {
 	struct msgreply_entry* e;
@@ -112,7 +121,7 @@
 	/* there was a reply_info_sortref(rep) here but it seems to be
 	 * unnecessary, because the cache gets locked per rrset. */
 	reply_info_set_ttls(rep, *env->now);
-	store_rrsets(env, rep, *env->now+leeway, qrep, region);
+	store_rrsets(env, rep, *env->now, leeway, pside, qrep, region);
 	if(ttl == 0) {
 		/* we do not store the message, but we did store the RRs,
 		 * which could be useful for delegation information */
@@ -730,7 +739,7 @@
 
 int 
 dns_cache_store(struct module_env* env, struct query_info* msgqinf,
-        struct reply_info* msgrep, int is_referral, uint32_t leeway,
+        struct reply_info* msgrep, int is_referral, uint32_t leeway, int pside,
 	struct regional* region)
 {
 	struct reply_info* rep = NULL;
@@ -752,8 +761,11 @@
 			ref.key = rep->rrsets[i];
 			ref.id = rep->rrsets[i]->id;
 			/*ignore ret: it was in the cache, ref updated */
+			/* no leeway for typeNS */
 			(void)rrset_cache_update(env->rrset_cache, &ref, 
-				env->alloc, *env->now + leeway);
+				env->alloc, *env->now + 
+				((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS
+				 && !pside) ? 0:leeway));
 		}
 		free(rep);
 		return 1;
@@ -774,7 +786,8 @@
 		rep->flags |= (BIT_RA | BIT_QR);
 		rep->flags &= ~(BIT_AA | BIT_CD);
 		h = query_info_hash(&qinf);
-		dns_cache_store_msg(env, &qinf, h, rep, leeway, msgrep, region);
+		dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep,
+			region);
 		/* qname is used inside query_info_entrysetup, and set to 
 		 * NULL. If it has not been used, free it. free(0) is safe. */
 		free(qinf.qname);
diff -Naur unbound-1.4.16/services/cache/dns.h unbound-trunk/services/cache/dns.h
--- unbound-1.4.16/services/cache/dns.h	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/services/cache/dns.h	2012-02-22 20:37:14.066368184 -0500
@@ -74,12 +74,15 @@
  *      It will store only the RRsets, not the message.
  * @param leeway: TTL value, if not 0, other rrsets are considered expired
  *	that many seconds before actual TTL expiry.
+ * @param pside: if true, information came from a server which was fetched
+ * 	from the parentside of the zonecut.  This means that the type NS
+ * 	can be updated to full TTL even in prefetch situations.
  * @param region: region to allocate better entries from cache into.
  *   (used when is_referral is false).
  * @return 0 on alloc error (out of memory).
  */
 int dns_cache_store(struct module_env* env, struct query_info* qinf,
-        struct reply_info* rep, int is_referral, uint32_t leeway,
+        struct reply_info* rep, int is_referral, uint32_t leeway, int pside,
 	struct regional* region); 
 
 /**
@@ -95,11 +98,14 @@
  *	Adjusts the reply info TTLs to absolute time.
  * @param leeway: TTL value, if not 0, other rrsets are considered expired
  *	that many seconds before actual TTL expiry.
+ * @param pside: if true, information came from a server which was fetched
+ * 	from the parentside of the zonecut.  This means that the type NS
+ * 	can be updated to full TTL even in prefetch situations.
  * @param qrep: message that can be altered with better rrs from cache.
  * @param region: to allocate into for qmsg.
  */
 void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
-	hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
+	hashvalue_t hash, struct reply_info* rep, uint32_t leeway, int pside,
 	struct reply_info* qrep, struct regional* region);
 
 /**
diff -Naur unbound-1.4.16/services/cache/infra.c unbound-trunk/services/cache/infra.c
--- unbound-1.4.16/services/cache/infra.c	2012-01-10 10:07:16.000000000 -0500
+++ unbound-trunk/services/cache/infra.c	2012-02-22 20:37:14.063368184 -0500
@@ -52,6 +52,11 @@
 /** Timeout when only a single probe query per IP is allowed. */
 #define PROBE_MAXRTO 12000 /* in msec */
 
+/** number of timeouts for a type when the domain can be blocked ;
+ * even if another type has completely rtt maxed it, the different type
+ * can do this number of packets (until those all timeout too) */
+#define TIMEOUT_COUNT_MAX 3
+
 size_t 
 infra_sizefunc(void* k, void* ATTR_UNUSED(d))
 {
@@ -196,6 +201,9 @@
 	data->rec_lame = 0;
 	data->lame_type_A = 0;
 	data->lame_other = 0;
+	data->timeout_A = 0;
+	data->timeout_AAAA = 0;
+	data->timeout_other = 0;
 }
 
 /** 
@@ -250,6 +258,9 @@
 	if(e && ((struct infra_data*)e->data)->ttl < timenow) {
 		/* it expired, try to reuse existing entry */
 		int old = ((struct infra_data*)e->data)->rtt.rto;
+		uint8_t tA = ((struct infra_data*)e->data)->timeout_A;
+		uint8_t tAAAA = ((struct infra_data*)e->data)->timeout_AAAA;
+		uint8_t tother = ((struct infra_data*)e->data)->timeout_other;
 		lock_rw_unlock(&e->lock);
 		e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1);
 		if(e) {
@@ -259,9 +270,13 @@
 			data_entry_init(infra, e, timenow);
 			wr = 1;
 			/* TOP_TIMEOUT remains on reuse */
-			if(old >= USEFUL_SERVER_TOP_TIMEOUT)
+			if(old >= USEFUL_SERVER_TOP_TIMEOUT) {
 				((struct infra_data*)e->data)->rtt.rto
 					= USEFUL_SERVER_TOP_TIMEOUT;
+				((struct infra_data*)e->data)->timeout_A = tA;
+				((struct infra_data*)e->data)->timeout_AAAA = tAAAA;
+				((struct infra_data*)e->data)->timeout_other = tother;
+			}
 		}
 	}
 	if(!e) {
@@ -358,8 +373,8 @@
 
 int 
 infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr,
-	socklen_t addrlen, uint8_t* nm, size_t nmlen, int roundtrip,
-	int orig_rtt, uint32_t timenow)
+	socklen_t addrlen, uint8_t* nm, size_t nmlen, int qtype,
+	int roundtrip, int orig_rtt, uint32_t timenow)
 {
 	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
 		nm, nmlen, 1);
@@ -377,9 +392,24 @@
 	data = (struct infra_data*)e->data;
 	if(roundtrip == -1) {
 		rtt_lost(&data->rtt, orig_rtt);
+		if(qtype == LDNS_RR_TYPE_A) {
+			if(data->timeout_A < TIMEOUT_COUNT_MAX)
+				data->timeout_A++;
+		} else if(qtype == LDNS_RR_TYPE_AAAA) {
+			if(data->timeout_AAAA < TIMEOUT_COUNT_MAX)
+				data->timeout_AAAA++;
+		} else {
+			if(data->timeout_other < TIMEOUT_COUNT_MAX)
+				data->timeout_other++;
+		}
 	} else {
 		rtt_update(&data->rtt, roundtrip);
 		data->probedelay = 0;
+		if(qtype == LDNS_RR_TYPE_A)
+			data->timeout_A = 0;
+		else if(qtype == LDNS_RR_TYPE_AAAA)
+			data->timeout_AAAA = 0;
+		else	data->timeout_other = 0;
 	}
 	if(data->rtt.rto > 0)
 		rto = data->rtt.rto;
@@ -392,7 +422,8 @@
 
 int infra_get_host_rto(struct infra_cache* infra,
         struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm,
-	size_t nmlen, struct rtt_info* rtt, int* delay, uint32_t timenow)
+	size_t nmlen, struct rtt_info* rtt, int* delay, uint32_t timenow,
+	int* tA, int* tAAAA, int* tother)
 {
 	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
 		nm, nmlen, 0);
@@ -407,6 +438,9 @@
 			*delay = (int)(data->probedelay - timenow);
 		else	*delay = 0;
 	}
+	*tA = (int)data->timeout_A;
+	*tAAAA = (int)data->timeout_AAAA;
+	*tother = (int)data->timeout_other;
 	lock_rw_unlock(&e->lock);
 	return ttl;
 }
@@ -456,20 +490,34 @@
 	host = (struct infra_data*)e->data;
 	*rtt = rtt_unclamped(&host->rtt);
 	if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay
-		&& rtt_notimeout(&host->rtt)*4 <= host->rtt.rto)
+		&& rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) {
 		/* single probe for this domain, and we are not probing */
-		*rtt = USEFUL_SERVER_TOP_TIMEOUT;
+		/* unless the query type allows a probe to happen */
+		if(qtype == LDNS_RR_TYPE_A) {
+			if(host->timeout_A >= TIMEOUT_COUNT_MAX)
+				*rtt = USEFUL_SERVER_TOP_TIMEOUT;
+			else	*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+		} else if(qtype == LDNS_RR_TYPE_AAAA) {
+			if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX)
+				*rtt = USEFUL_SERVER_TOP_TIMEOUT;
+			else	*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+		} else {
+			if(host->timeout_other >= TIMEOUT_COUNT_MAX)
+				*rtt = USEFUL_SERVER_TOP_TIMEOUT;
+			else	*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+		}
+	}
 	if(timenow > host->ttl) {
 		/* expired entry */
 		/* see if this can be a re-probe of an unresponsive server */
 		/* minus 1000 because that is outside of the RTTBAND, so
 		 * blacklisted servers stay blacklisted if this is chosen */
 		if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
+			lock_rw_unlock(&e->lock);
 			*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
 			*lame = 0;
 			*dnsseclame = 0;
 			*reclame = 0;
-			lock_rw_unlock(&e->lock);
 			return 1;
 		}
 		lock_rw_unlock(&e->lock);
diff -Naur unbound-1.4.16/services/cache/infra.h unbound-trunk/services/cache/infra.h
--- unbound-1.4.16/services/cache/infra.h	2011-10-26 11:46:23.000000000 -0400
+++ unbound-trunk/services/cache/infra.h	2012-02-22 20:37:14.065368184 -0500
@@ -91,6 +91,13 @@
 	uint8_t lame_type_A;
 	/** the host is lame (not authoritative) for other query types */
 	uint8_t lame_other;
+
+	/** timeouts counter for type A */
+	uint8_t timeout_A;
+	/** timeouts counter for type AAAA */
+	uint8_t timeout_AAAA;
+	/** timeouts counter for others */
+	uint8_t timeout_other;
 };
 
 /**
@@ -195,6 +202,7 @@
  * @param addrlen: length of addr.
  * @param name: zone name
  * @param namelen: zone name length
+ * @param qtype: query type.
  * @param roundtrip: estimate of roundtrip time in milliseconds or -1 for 
  * 	timeout.
  * @param orig_rtt: original rtt for the query that timed out (roundtrip==-1).
@@ -203,7 +211,7 @@
  * @return: 0 on error. new rto otherwise.
  */
 int infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr,
-	socklen_t addrlen, uint8_t* name, size_t namelen,
+	socklen_t addrlen, uint8_t* name, size_t namelen, int qtype,
 	int roundtrip, int orig_rtt, uint32_t timenow);
 
 /**
@@ -267,12 +275,16 @@
  * @param rtt: the rtt_info is copied into here (caller alloced return struct).
  * @param delay: probe delay (if any).
  * @param timenow: what time it is now.
+ * @param tA: timeout counter on type A.
+ * @param tAAAA: timeout counter on type AAAA.
+ * @param tother: timeout counter on type other.
  * @return TTL the infra host element is valid for. If -1: not found in cache.
  *	TTL -2: found but expired.
  */
 int infra_get_host_rto(struct infra_cache* infra,
         struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name,
-	size_t namelen, struct rtt_info* rtt, int* delay, uint32_t timenow);
+	size_t namelen, struct rtt_info* rtt, int* delay, uint32_t timenow,
+	int* tA, int* tAAAA, int* tother);
 
 /**
  * Get memory used by the infra cache.
diff -Naur unbound-1.4.16/services/outside_network.c unbound-trunk/services/outside_network.c
--- unbound-1.4.16/services/outside_network.c	2011-12-24 06:27:53.000000000 -0500
+++ unbound-trunk/services/outside_network.c	2012-02-22 20:37:14.106368184 -0500
@@ -1166,7 +1166,7 @@
 serviced_create(struct outside_network* outnet, ldns_buffer* buff, int dnssec,
 	int want_dnssec, int tcp_upstream, int ssl_upstream,
 	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
-	size_t zonelen)
+	size_t zonelen, int qtype)
 {
 	struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
 #ifdef UNBOUND_DEBUG
@@ -1188,6 +1188,7 @@
 		return NULL;
 	}
 	sq->zonelen = zonelen;
+	sq->qtype = qtype;
 	sq->dnssec = dnssec;
 	sq->want_dnssec = want_dnssec;
 	sq->tcp_upstream = tcp_upstream;
@@ -1566,8 +1567,8 @@
 		 * huge due to system-hibernated and we woke up */
 		if(roundtime < TCP_AUTH_QUERY_TIMEOUT*1000) {
 		    if(!infra_rtt_update(sq->outnet->infra, &sq->addr,
-			sq->addrlen, sq->zone, sq->zonelen, roundtime,
-			sq->last_rtt, (uint32_t)now.tv_sec))
+			sq->addrlen, sq->zone, sq->zonelen, sq->qtype,
+			roundtime, sq->last_rtt, (uint32_t)now.tv_sec))
 			log_err("out of memory noting rtt.");
 		}
 	    }
@@ -1658,7 +1659,7 @@
 		}
 		sq->retry++;
 		if(!(rto=infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
-			sq->zone, sq->zonelen, -1, sq->last_rtt,
+			sq->zone, sq->zonelen, sq->qtype, -1, sq->last_rtt,
 			(uint32_t)now.tv_sec)))
 			log_err("out of memory in UDP exponential backoff");
 		if(sq->retry < OUTBOUND_UDP_RETRY) {
@@ -1752,8 +1753,8 @@
 		 * above this value gives trouble with server selection */
 		if(roundtime < 60000) {
 		    if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen, 
-			sq->zone, sq->zonelen, roundtime, sq->last_rtt,
-			(uint32_t)now.tv_sec))
+			sq->zone, sq->zonelen, sq->qtype, roundtime,
+			sq->last_rtt, (uint32_t)now.tv_sec))
 			log_err("out of memory noting rtt.");
 		}
 	    }
@@ -1814,7 +1815,7 @@
 		/* make new serviced query entry */
 		sq = serviced_create(outnet, buff, dnssec, want_dnssec,
 			tcp_upstream, ssl_upstream, addr, addrlen, zone,
-			zonelen);
+			zonelen, (int)qtype);
 		if(!sq) {
 			free(cb);
 			return NULL;
diff -Naur unbound-1.4.16/services/outside_network.h unbound-trunk/services/outside_network.h
--- unbound-1.4.16/services/outside_network.h	2011-11-01 06:18:56.000000000 -0400
+++ unbound-trunk/services/outside_network.h	2012-02-22 20:37:14.107368184 -0500
@@ -312,6 +312,8 @@
 	uint8_t* zone;
 	/** length of zone name */
 	size_t zonelen;
+	/** qtype */
+	int qtype;
 	/** current status */
 	enum serviced_query_status {
 		/** initial status */
diff -Naur unbound-1.4.16/smallapp/unbound-checkconf.c unbound-trunk/smallapp/unbound-checkconf.c
--- unbound-1.4.16/smallapp/unbound-checkconf.c	2011-08-26 02:50:23.000000000 -0400
+++ unbound-trunk/smallapp/unbound-checkconf.c	2012-02-22 20:37:16.189368186 -0500
@@ -50,6 +50,7 @@
 #include "util/regional.h"
 #include "iterator/iterator.h"
 #include "iterator/iter_fwd.h"
+#include "iterator/iter_hints.h"
 #include "validator/validator.h"
 #include "services/localzone.h"
 #ifdef HAVE_GETOPT_H
@@ -434,6 +435,17 @@
 	forwards_delete(fwd);
 }
 
+/** check hints */
+static void
+check_hints(struct config_file* cfg)
+{
+	struct iter_hints* hints = hints_create();
+	if(!hints || !hints_apply_cfg(hints, cfg)) {
+		fatal_exit("Could not set root or stub hints");
+	}
+	hints_delete(hints);
+}
+
 /** check config file */
 static void
 checkconf(const char* cfgfile, const char* opt)
@@ -454,6 +466,7 @@
 		check_mod(cfg, pythonmod_get_funcblock());
 #endif
 	check_fwd(cfg);
+	check_hints(cfg);
 	if(opt) print_option(cfg, opt);
 	else	printf("unbound-checkconf: no errors in %s\n", cfgfile);
 	config_delete(cfg);
diff -Naur unbound-1.4.16/smallapp/unbound-control.c unbound-trunk/smallapp/unbound-control.c
--- unbound-1.4.16/smallapp/unbound-control.c	2011-06-10 06:11:38.000000000 -0400
+++ unbound-trunk/smallapp/unbound-control.c	2012-02-22 20:37:16.188368186 -0500
@@ -104,6 +104,12 @@
 	printf("  list_forwards			list forward-zones in use\n");
 	printf("  list_local_zones		list local-zones in use\n");
 	printf("  list_local_data		list local-data RRs in use\n");
+	printf("  forward_add [+i] zone addr..	add forward-zone with servers\n");
+	printf("  forward_remove [+i] zone	remove forward zone\n");
+	printf("  stub_add [+ip] zone addr..	add stub-zone with servers\n");
+	printf("  stub_remove [+i] zone		remove stub zone\n");
+	printf("		+i		also do dnssec insecure point\n");
+	printf("		+p		set stub to use priming\n");
 	printf("  forward [off | addr ...]	without arg show forward setup\n");
 	printf("				or off to turn off root forwarding\n");
 	printf("				or give list of ip addresses\n");
diff -Naur unbound-1.4.16/testcode/fake_event.c unbound-trunk/testcode/fake_event.c
--- unbound-1.4.16/testcode/fake_event.c	2011-11-01 06:18:56.000000000 -0400
+++ unbound-trunk/testcode/fake_event.c	2012-02-22 20:37:22.995368196 -0500
@@ -147,6 +147,7 @@
 {
 	if(!pend)
 		return;
+	free(pend->zone);
 	ldns_buffer_free(pend->buffer);
 	ldns_pkt_free(pend->pkt);
 	free(pend);
@@ -554,7 +555,7 @@
 	if(!dp) fatal_exit("cannot parse %s", now->variable);
 	rto = infra_rtt_update(runtime->infra, &now->addr,
 		now->addrlen, ldns_rdf_data(dp), ldns_rdf_size(dp),
-		atoi(now->string), -1, runtime->now_secs);
+		LDNS_RR_TYPE_A, atoi(now->string), -1, runtime->now_secs);
 	log_addr(0, "INFRA_RTT for", &now->addr, now->addrlen);
 	log_info("INFRA_RTT(%s roundtrip %d): rto of %d", now->variable,
 		atoi(now->string), rto);
@@ -562,6 +563,24 @@
 	ldns_rdf_deep_free(dp);
 }
 
+/** perform exponential backoff on the timout */
+static void
+expon_timeout_backoff(struct replay_runtime* runtime)
+{
+	struct fake_pending* p = runtime->pending_list;
+	int rtt, vs;
+	uint8_t edns_lame_known;
+	int last_rtt, rto;
+	if(!p) return; /* no pending packet to backoff */
+	if(!infra_host(runtime->infra, &p->addr, p->addrlen, p->zone,
+		p->zonelen, runtime->now_secs, &vs, &edns_lame_known, &rtt))
+		return;
+	last_rtt = rtt;
+	rto = infra_rtt_update(runtime->infra, &p->addr, p->addrlen, p->zone,
+		p->zonelen, p->qtype, -1, last_rtt, runtime->now_secs);
+	log_info("infra_rtt_update returned rto %d", rto);
+}
+
 /**
  * Advance to the next moment.
  */
@@ -608,6 +627,7 @@
 	case repevt_timeout:
 		mom = runtime->now;
 		advance_moment(runtime);
+		expon_timeout_backoff(runtime);
 		fake_pending_callback(runtime, mom, NETEVENT_TIMEOUT);
 		break;
 	case repevt_back_reply:
@@ -929,6 +949,7 @@
 	pend->timeout = timeout/1000;
 	pend->transport = transport_udp;
 	pend->pkt = NULL;
+	pend->zone = NULL;
 	pend->serviced = 0;
 	pend->runtime = runtime;
 	status = ldns_buffer2pkt_wire(&pend->pkt, packet);
@@ -982,6 +1003,7 @@
 	pend->timeout = timeout;
 	pend->transport = transport_tcp;
 	pend->pkt = NULL;
+	pend->zone = NULL;
 	pend->runtime = runtime;
 	pend->serviced = 0;
 	status = ldns_buffer2pkt_wire(&pend->pkt, packet);
@@ -1017,9 +1039,8 @@
 	uint16_t flags, int dnssec, int ATTR_UNUSED(want_dnssec),
 	int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream),
 	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
-	size_t ATTR_UNUSED(zonelen), comm_point_callback_t* callback,
-	void* callback_arg, ldns_buffer* ATTR_UNUSED(buff),
-	int (*arg_compare)(void*,void*))
+	size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
+	ldns_buffer* ATTR_UNUSED(buff), int (*arg_compare)(void*,void*))
 {
 	struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
 	struct fake_pending* pend = (struct fake_pending*)calloc(1,
@@ -1062,6 +1083,10 @@
 	}
 	memcpy(&pend->addr, addr, addrlen);
 	pend->addrlen = addrlen;
+	pend->zone = memdup(zone, zonelen);
+	pend->zonelen = zonelen;
+	pend->qtype = (int)qtype;
+	log_assert(pend->zone);
 	pend->callback = callback;
 	pend->cb_arg = callback_arg;
 	pend->timeout = UDP_AUTH_QUERY_TIMEOUT;
@@ -1112,6 +1137,7 @@
 			else 	runtime->pending_list = p->next;
 			ldns_buffer_free(p->buffer);
 			ldns_pkt_free(p->pkt);
+			free(p->zone);
 			free(p);
 			return;
 		}
diff -Naur unbound-1.4.16/testcode/replay.h unbound-trunk/testcode/replay.h
--- unbound-1.4.16/testcode/replay.h	2011-10-26 11:46:23.000000000 -0400
+++ unbound-trunk/testcode/replay.h	2012-02-22 20:37:22.950368196 -0500
@@ -323,6 +323,12 @@
 	struct sockaddr_storage addr;
 	/** len of addr */
 	socklen_t addrlen;
+	/** zone name, uncompressed wire format (as used when sent) */
+	uint8_t* zone;
+	/** length of zone name */
+	size_t zonelen;
+	/** qtype */
+	int qtype;
 	/** The callback function to call when answer arrives (or timeout) */
 	comm_point_callback_t* callback;
 	/** callback user argument */
diff -Naur unbound-1.4.16/testcode/testbound.c unbound-trunk/testcode/testbound.c
--- unbound-1.4.16/testcode/testbound.c	2010-09-15 03:15:30.000000000 -0400
+++ unbound-trunk/testcode/testbound.c	2012-02-22 20:37:22.995368196 -0500
@@ -70,6 +70,7 @@
 	printf("-p file	playback text file\n");
 	printf("-2 	detect SHA256 support (exit code 0 or 1)\n");
 	printf("-g 	detect GOST support (exit code 0 or 1)\n");
+	printf("-e 	detect ECDSA support (exit code 0 or 1)\n");
 	printf("-s 	testbound self-test - unit test of testbound parts.\n");
 	printf("-o str  unbound commandline options separated by spaces.\n");
 	printf("Version %s\n", PACKAGE_VERSION);
@@ -272,7 +273,7 @@
 	pass_argc = 1;
 	pass_argv[0] = "unbound";
 	add_opts("-d", &pass_argc, pass_argv);
-	while( (c=getopt(argc, argv, "2gho:p:s")) != -1) {
+	while( (c=getopt(argc, argv, "2egho:p:s")) != -1) {
 		switch(c) {
 		case 's':
 			free(pass_argv[1]);
@@ -288,6 +289,15 @@
 			exit(1);
 #endif
 			break;
+		case 'e':
+#if defined(USE_ECDSA)
+			printf("ECDSA supported\n");
+			exit(0);
+#else
+			printf("ECDSA not supported\n");
+			exit(1);
+#endif
+			break;
 		case 'g':
 #ifdef USE_GOST
 			if(ldns_key_EVP_load_gost_id()) {
diff -Naur unbound-1.4.16/testcode/unitmain.c unbound-trunk/testcode/unitmain.c
--- unbound-1.4.16/testcode/unitmain.c	2011-11-10 13:44:06.000000000 -0500
+++ unbound-trunk/testcode/unitmain.c	2012-02-22 20:37:22.996368196 -0500
@@ -445,7 +445,7 @@
 		&vs, &edns_lame, &to) );
 	unit_assert( vs == 0 && to == init && edns_lame == 0 );
 
-	unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen, -1, init, now) );
+	unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen, LDNS_RR_TYPE_A, -1, init, now) );
 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen, 
 			now, &vs, &edns_lame, &to) );
 	unit_assert( vs == 0 && to == init*2 && edns_lame == 0 );
diff -Naur unbound-1.4.16/testcode/unitverify.c unbound-trunk/testcode/unitverify.c
--- unbound-1.4.16/testcode/unitverify.c	2010-12-20 11:08:52.000000000 -0500
+++ unbound-trunk/testcode/unitverify.c	2012-02-22 20:37:22.994368196 -0500
@@ -520,6 +520,11 @@
 	  verifytest_file("testdata/test_sigs.gost", "20090807060504");
 	else printf("Warning: skipped GOST, openssl does not provide gost.\n");
 #endif
+#ifdef USE_ECDSA
+	verifytest_file("testdata/test_sigs.ecdsa_p256", "20100908100439");
+	verifytest_file("testdata/test_sigs.ecdsa_p384", "20100908100439");
+	dstest_file("testdata/test_ds.sha384");
+#endif
 	dstest_file("testdata/test_ds.sha1");
 	nsectest();
 	nsec3_hash_test("testdata/test_nsec3_hash.1");
diff -Naur unbound-1.4.16/util/iana_ports.inc unbound-trunk/util/iana_ports.inc
--- unbound-1.4.16/util/iana_ports.inc	2012-01-10 04:42:55.000000000 -0500
+++ unbound-trunk/util/iana_ports.inc	2012-02-22 20:37:23.866368198 -0500
@@ -5026,6 +5026,7 @@
 12006,
 12007,
 12008,
+12009,
 12012,
 12013,
 12109,
@@ -5272,6 +5273,7 @@
 32896,
 33123,
 33331,
+33334,
 33434,
 33656,
 34249,
@@ -5304,6 +5306,7 @@
 43188,
 43189,
 43190,
+43439,
 43440,
 43441,
 44321,
diff -Naur unbound-1.4.16/util/module.h unbound-trunk/util/module.h
--- unbound-1.4.16/util/module.h	2011-10-26 11:46:23.000000000 -0400
+++ unbound-trunk/util/module.h	2012-02-22 20:37:23.867368198 -0500
@@ -60,6 +60,7 @@
 struct val_anchors;
 struct val_neg_cache;
 struct iter_forwards;
+struct iter_hints;
 
 /** Maximum number of modules in operation */
 #define MAX_MODULE 5
@@ -204,6 +205,14 @@
 	/** Mapping of forwarding zones to targets.
 	 * iterator forwarder information. per-thread, created by worker */
 	struct iter_forwards* fwds;
+	/** 
+	 * iterator forwarder information. per-thread, created by worker.
+	 * The hints -- these aren't stored in the cache because they don't 
+	 * expire. The hints are always used to "prime" the cache. Note 
+	 * that both root hints and stub zone "hints" are stored in this 
+	 * data structure. 
+	 */
+	struct iter_hints* hints;
 	/** module specific data. indexed by module id. */
 	void* modinfo[MAX_MODULE];
 };
diff -Naur unbound-1.4.16/util/netevent.c unbound-trunk/util/netevent.c
--- unbound-1.4.16/util/netevent.c	2011-11-10 13:44:06.000000000 -0500
+++ unbound-trunk/util/netevent.c	2012-02-22 20:37:23.867368198 -0500
@@ -367,11 +367,15 @@
 			strncpy(buf1, "(inet_ntop error)", sizeof(buf1));
 		}
 		buf1[sizeof(buf1)-1]=0;
+#ifdef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST
 		if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_spec_dst, 
 			buf2, (socklen_t)sizeof(buf2)) == 0) {
 			strncpy(buf2, "(inet_ntop error)", sizeof(buf2));
 		}
 		buf2[sizeof(buf2)-1]=0;
+#else
+		buf2[0]=0;
+#endif
 		log_info("%s: %d %s %s", str, r->pktinfo.v4info.ipi_ifindex,
 			buf1, buf2);
 #elif defined(IP_RECVDSTADDR)
diff -Naur unbound-1.4.16/validator/autotrust.c unbound-trunk/validator/autotrust.c
--- unbound-1.4.16/validator/autotrust.c	2012-01-19 09:17:22.000000000 -0500
+++ unbound-trunk/validator/autotrust.c	2012-02-22 20:37:14.741368184 -0500
@@ -996,6 +996,9 @@
 	/* success; overwrite actual file */
 	fclose(out);
 	verbose(VERB_ALGO, "autotrust: replaced %s", fname);
+#ifdef UB_ON_WINDOWS
+	(void)unlink(fname); /* windows does not replace file with rename() */
+#endif
 	if(rename(tempf, fname) < 0) {
 		log_err("rename(%s to %s): %s", tempf, fname, strerror(errno));
 	}
diff -Naur unbound-1.4.16/validator/val_anchor.c unbound-trunk/validator/val_anchor.c
--- unbound-1.4.16/validator/val_anchor.c	2011-11-10 13:44:06.000000000 -0500
+++ unbound-trunk/validator/val_anchor.c	2012-02-22 20:37:14.741368184 -0500
@@ -49,7 +49,6 @@
 #include "util/data/dname.h"
 #include "util/log.h"
 #include "util/net_help.h"
-#include "util/regional.h"
 #include "util/config_file.h"
 #ifdef HAVE_GLOB_H
 #include <glob.h>
@@ -77,11 +76,6 @@
 	struct val_anchors* a = (struct val_anchors*)calloc(1, sizeof(*a));
 	if(!a)
 		return NULL;
-	a->region = regional_create();
-	if(!a->region) {
-		free(a);
-		return NULL;
-	}
 	a->tree = rbtree_create(anchor_cmp);
 	if(!a->tree) {
 		anchors_delete(a);
@@ -98,15 +92,45 @@
 	return a;
 }
 
+/** delete assembled rrset */
+static void
+assembled_rrset_delete(struct ub_packed_rrset_key* pkey)
+{
+	if(!pkey) return;
+	if(pkey->entry.data) {
+		struct packed_rrset_data* pd = (struct packed_rrset_data*)
+			pkey->entry.data;
+		free(pd->rr_data);
+		free(pd->rr_ttl);
+		free(pd->rr_len);
+		free(pd);
+	}
+	free(pkey->rk.dname);
+	free(pkey);
+}
+
 /** destroy locks in tree and delete autotrust anchors */
 static void
 anchors_delfunc(rbnode_t* elem, void* ATTR_UNUSED(arg))
 {
 	struct trust_anchor* ta = (struct trust_anchor*)elem;
+	if(!ta) return;
 	if(ta->autr) {
 		autr_point_delete(ta);
 	} else {
+		struct ta_key* p, *np;
 		lock_basic_destroy(&ta->lock);
+		free(ta->name);
+		p = ta->keylist;
+		while(p) {
+			np = p->next;
+			free(p->data);
+			free(p);
+			p = np;
+		}
+		assembled_rrset_delete(ta->ds_rrset);
+		assembled_rrset_delete(ta->dnskey_rrset);
+		free(ta);
 	}
 }
 
@@ -118,9 +142,9 @@
 	lock_unprotect(&anchors->lock, anchors->autr);
 	lock_unprotect(&anchors->lock, anchors);
 	lock_basic_destroy(&anchors->lock);
-	traverse_postorder(anchors->tree, anchors_delfunc, NULL);
+	if(anchors->tree)
+		traverse_postorder(anchors->tree, anchors_delfunc, NULL);
 	free(anchors->tree);
-	regional_destroy(anchors->region);
 	autr_global_delete(anchors->autr);
 	free(anchors);
 }
@@ -193,30 +217,34 @@
 /** create new trust anchor object */
 static struct trust_anchor*
 anchor_new_ta(struct val_anchors* anchors, uint8_t* name, int namelabs,
-	size_t namelen, uint16_t dclass)
+	size_t namelen, uint16_t dclass, int lockit)
 {
 #ifdef UNBOUND_DEBUG
 	rbnode_t* r;
 #endif
-	struct trust_anchor* ta = (struct trust_anchor*)regional_alloc(
-		anchors->region, sizeof(struct trust_anchor));
+	struct trust_anchor* ta = (struct trust_anchor*)malloc(
+		sizeof(struct trust_anchor));
 	if(!ta)
 		return NULL;
 	memset(ta, 0, sizeof(*ta));
 	ta->node.key = ta;
-	ta->name = regional_alloc_init(anchors->region, name, namelen);
-	if(!ta->name)
+	ta->name = memdup(name, namelen);
+	if(!ta->name) {
+		free(ta);
 		return NULL;
+	}
 	ta->namelabs = namelabs;
 	ta->namelen = namelen;
 	ta->dclass = dclass;
 	lock_basic_init(&ta->lock);
-	lock_basic_lock(&anchors->lock);
+	if(lockit)
+		lock_basic_lock(&anchors->lock);
 #ifdef UNBOUND_DEBUG
 	r =
 #endif
 	rbtree_insert(anchors->tree, &ta->node);
-	lock_basic_unlock(&anchors->lock);
+	if(lockit)
+		lock_basic_unlock(&anchors->lock);
 	log_assert(r != NULL);
 	return ta;
 }
@@ -237,17 +265,17 @@
 	
 /** create new trustanchor key */
 static struct ta_key*
-anchor_new_ta_key(struct val_anchors* anchors, uint8_t* rdata, size_t rdata_len,
-	uint16_t type)
+anchor_new_ta_key(uint8_t* rdata, size_t rdata_len, uint16_t type)
 {
-	struct ta_key* k = (struct ta_key*)regional_alloc(anchors->region,
-		sizeof(*k));
+	struct ta_key* k = (struct ta_key*)malloc(sizeof(*k));
 	if(!k)
 		return NULL;
 	memset(k, 0, sizeof(*k));
-	k->data = regional_alloc_init(anchors->region, rdata, rdata_len);
-	if(!k->data)
+	k->data = memdup(rdata, rdata_len);
+	if(!k->data) {
+		free(k);
 		return NULL;
+	}
 	k->len = rdata_len;
 	k->type = type;
 	return k;
@@ -282,7 +310,7 @@
 	/* lookup or create trustanchor */
 	ta = anchor_find(anchors, name, namelabs, namelen, dclass);
 	if(!ta) {
-		ta = anchor_new_ta(anchors, name, namelabs, namelen, dclass);
+		ta = anchor_new_ta(anchors, name, namelabs, namelen, dclass, 1);
 		if(!ta)
 			return NULL;
 		lock_basic_lock(&ta->lock);
@@ -296,7 +324,7 @@
 		lock_basic_unlock(&ta->lock);
 		return ta;
 	}
-	k = anchor_new_ta_key(anchors, rdata, rdata_len, type);
+	k = anchor_new_ta_key(rdata, rdata_len, type);
 	if(!k) {
 		lock_basic_unlock(&ta->lock);
 		return NULL;
@@ -826,55 +854,73 @@
 
 /** 
  * Assemble an rrset structure for the type 
- * @param region: allocated in this region.
  * @param ta: trust anchor.
  * @param num: number of items to fetch from list.
  * @param type: fetch only items of this type.
  * @return rrset or NULL on error.
  */
 static struct ub_packed_rrset_key*
-assemble_it(struct regional* region, struct trust_anchor* ta, size_t num, 
-	uint16_t type)
+assemble_it(struct trust_anchor* ta, size_t num, uint16_t type)
 {
 	struct ub_packed_rrset_key* pkey = (struct ub_packed_rrset_key*)
-		regional_alloc(region, sizeof(*pkey));
+		malloc(sizeof(*pkey));
 	struct packed_rrset_data* pd;
 	struct ta_key* tk;
 	size_t i;
 	if(!pkey)
 		return NULL;
 	memset(pkey, 0, sizeof(*pkey));
-	pkey->rk.dname = regional_alloc_init(region, ta->name, ta->namelen);
-	if(!pkey->rk.dname)
+	pkey->rk.dname = memdup(ta->name, ta->namelen);
+	if(!pkey->rk.dname) {
+		free(pkey);
 		return NULL;
-	
+	}
+
 	pkey->rk.dname_len = ta->namelen;
 	pkey->rk.type = htons(type);
 	pkey->rk.rrset_class = htons(ta->dclass);
 	/* The rrset is build in an uncompressed way. This means it
 	 * cannot be copied in the normal way. */
-	pd = (struct packed_rrset_data*)regional_alloc(region, sizeof(*pd));
-	if(!pd)
+	pd = (struct packed_rrset_data*)malloc(sizeof(*pd));
+	if(!pd) {
+		free(pkey->rk.dname);
+		free(pkey);
 		return NULL;
+	}
 	memset(pd, 0, sizeof(*pd));
 	pd->count = num;
 	pd->trust = rrset_trust_ultimate;
-	pd->rr_len = (size_t*)regional_alloc(region, num*sizeof(size_t));
-	if(!pd->rr_len)
-		return NULL;
-	pd->rr_ttl = (uint32_t*)regional_alloc(region, num*sizeof(uint32_t));
-	if(!pd->rr_ttl)
-		return NULL;
-	pd->rr_data = (uint8_t**)regional_alloc(region, num*sizeof(uint8_t*));
-	if(!pd->rr_data)
+	pd->rr_len = (size_t*)malloc(num*sizeof(size_t));
+	if(!pd->rr_len) {
+		free(pd);
+		free(pkey->rk.dname);
+		free(pkey);
+		return NULL;
+	}
+	pd->rr_ttl = (uint32_t*)malloc(num*sizeof(uint32_t));
+	if(!pd->rr_ttl) {
+		free(pd->rr_len);
+		free(pd);
+		free(pkey->rk.dname);
+		free(pkey);
+		return NULL;
+	}
+	pd->rr_data = (uint8_t**)malloc(num*sizeof(uint8_t*));
+	if(!pd->rr_data) {
+		free(pd->rr_ttl);
+		free(pd->rr_len);
+		free(pd);
+		free(pkey->rk.dname);
+		free(pkey);
 		return NULL;
+	}
 	/* fill in rrs */
 	i=0;
 	for(tk = ta->keylist; tk; tk = tk->next) {
 		if(tk->type != type)
 			continue;
 		pd->rr_len[i] = tk->len;
-		/* reuse data ptr to allocation in region */
+		/* reuse data ptr to allocation in talist */
 		pd->rr_data[i] = tk->data;
 		pd->rr_ttl[i] = 0;
 		i++;
@@ -885,22 +931,20 @@
 
 /**
  * Assemble structures for the trust DS and DNSKEY rrsets.
- * @param anchors: trust anchor storage.
  * @param ta: trust anchor
  * @return: false on error.
  */
 static int
-anchors_assemble(struct val_anchors* anchors, struct trust_anchor* ta)
+anchors_assemble(struct trust_anchor* ta)
 {
 	if(ta->numDS > 0) {
-		ta->ds_rrset = assemble_it(anchors->region, ta,
-			ta->numDS, LDNS_RR_TYPE_DS);
+		ta->ds_rrset = assemble_it(ta, ta->numDS, LDNS_RR_TYPE_DS);
 		if(!ta->ds_rrset)
 			return 0;
 	}
 	if(ta->numDNSKEY > 0) {
-		ta->dnskey_rrset = assemble_it(anchors->region, ta,
-			ta->numDNSKEY, LDNS_RR_TYPE_DNSKEY);
+		ta->dnskey_rrset = assemble_it(ta, ta->numDNSKEY,
+			LDNS_RR_TYPE_DNSKEY);
 		if(!ta->dnskey_rrset)
 			return 0;
 	}
@@ -961,7 +1005,7 @@
 			ta = next; /* skip */
 			continue;
 		}
-		if(!anchors_assemble(anchors, ta)) {
+		if(!anchors_assemble(ta)) {
 			log_err("out of memory");
 			lock_basic_unlock(&ta->lock);
 			lock_basic_unlock(&anchors->lock);
@@ -987,7 +1031,7 @@
 				" upgrade unbound and openssl)", b);
 			(void)rbtree_delete(anchors->tree, &ta->node);
 			lock_basic_unlock(&ta->lock);
-			lock_basic_destroy(&ta->lock);
+			anchors_delfunc(&ta->node, NULL);
 			ta = next;
 			continue;
 		}
@@ -1146,5 +1190,72 @@
 size_t 
 anchors_get_mem(struct val_anchors* anchors)
 {
-	return sizeof(*anchors) + regional_get_mem(anchors->region);
+	struct trust_anchor *ta;
+	size_t s = sizeof(*anchors);
+	RBTREE_FOR(ta, struct trust_anchor*, anchors->tree) {
+		s += sizeof(*ta) + ta->namelen;
+		/* keys and so on */
+	}
+	return s;
+}
+
+int
+anchors_add_insecure(struct val_anchors* anchors, uint16_t c, uint8_t* nm)
+{
+	struct trust_anchor key;
+	key.node.key = &key;
+	key.name = nm;
+	key.namelabs = dname_count_size_labels(nm, &key.namelen);
+	key.dclass = c;
+	lock_basic_lock(&anchors->lock);
+	if(rbtree_search(anchors->tree, &key)) {
+		lock_basic_unlock(&anchors->lock);
+		/* nothing to do, already an anchor or insecure point */
+		return 1;
+	}
+	if(!anchor_new_ta(anchors, nm, key.namelabs, key.namelen, c, 0)) {
+		log_err("out of memory");
+		lock_basic_unlock(&anchors->lock);
+		return 0;
+	}
+	/* no other contents in new ta, because it is insecure point */
+	anchors_init_parents_locked(anchors);
+	lock_basic_unlock(&anchors->lock);
+	return 1;
+}
+
+void
+anchors_delete_insecure(struct val_anchors* anchors, uint16_t c,
+        uint8_t* nm)
+{
+	struct trust_anchor key;
+	struct trust_anchor* ta;
+	key.node.key = &key;
+	key.name = nm;
+	key.namelabs = dname_count_size_labels(nm, &key.namelen);
+	key.dclass = c;
+	lock_basic_lock(&anchors->lock);
+	if(!(ta=(struct trust_anchor*)rbtree_search(anchors->tree, &key))) {
+		lock_basic_unlock(&anchors->lock);
+		/* nothing there */
+		return;
+	}
+	/* lock it to drive away other threads that use it */
+	lock_basic_lock(&ta->lock);
+	/* see if its really an insecure point */
+	if(ta->keylist || ta->autr || ta->numDS || ta->numDNSKEY) {
+		lock_basic_unlock(&ta->lock);
+		/* its not an insecure point, do not remove it */
+		return;
+	}
+
+	/* remove from tree */
+	(void)rbtree_delete(anchors->tree, &ta->node);
+	anchors_init_parents_locked(anchors);
+	lock_basic_unlock(&anchors->lock);
+
+	/* actual free of data */
+	lock_basic_unlock(&ta->lock);
+	anchors_delfunc(&ta->node, NULL);
 }
+
diff -Naur unbound-1.4.16/validator/val_anchor.h unbound-trunk/validator/val_anchor.h
--- unbound-1.4.16/validator/val_anchor.h	2010-07-07 09:13:36.000000000 -0400
+++ unbound-trunk/validator/val_anchor.h	2012-02-22 20:37:14.743368184 -0500
@@ -43,7 +43,6 @@
 #define VALIDATOR_VAL_ANCHOR_H
 #include "util/rbtree.h"
 #include "util/locks.h"
-struct regional;
 struct trust_anchor;
 struct config_file;
 struct ub_packed_rrset_key;
@@ -60,11 +59,6 @@
 struct val_anchors {
 	/** lock on trees */
 	lock_basic_t lock;
-	/** 
-	 * region where trust anchors are allocated.
-	 * Autotrust anchors are malloced so they can be updated. 
-	 */
-	struct regional* region;
 	/**
 	 * Anchors are store in this tree. Sort order is chosen, so that
 	 * dnames are in nsec-like order. A lookup on class, name will return
@@ -111,7 +105,6 @@
 	struct trust_anchor* parent;
 	/** 
 	 * List of DS or DNSKEY rrs that form the trust anchor.
-	 * It is allocated in the region.
 	 */
 	struct ta_key* keylist;
 	/** Autotrust anchor point data, or NULL */
@@ -203,4 +196,23 @@
 /** compare two trust anchors */
 int anchor_cmp(const void* k1, const void* k2);
 
+/**
+ * Add insecure point trust anchor.  For external use (locks and init_parents)
+ * @param anchors: anchor storage.
+ * @param c: class.
+ * @param nm: name of insecure trust point.
+ * @return false on alloc failure.
+ */
+int anchors_add_insecure(struct val_anchors* anchors, uint16_t c, uint8_t* nm);
+
+/**
+ * Delete insecure point trust anchor.  Does not remove if no such point.
+ * For external use (locks and init_parents)
+ * @param anchors: anchor storage.
+ * @param c: class.
+ * @param nm: name of insecure trust point.
+ */
+void anchors_delete_insecure(struct val_anchors* anchors, uint16_t c,
+	uint8_t* nm);
+
 #endif /* VALIDATOR_VAL_ANCHOR_H */
diff -Naur unbound-1.4.16/validator/validator.c unbound-trunk/validator/validator.c
--- unbound-1.4.16/validator/validator.c	2012-01-10 04:42:32.000000000 -0500
+++ unbound-trunk/validator/validator.c	2012-02-22 20:37:14.743368184 -0500
@@ -1977,15 +1977,17 @@
 
 	/* store results in cache */
 	if(qstate->query_flags&BIT_RD) {
+		/* if secure, this will override cache anyway, no need
+		 * to check if from parentNS */
 		if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 
-			vq->orig_msg->rep, 0, qstate->prefetch_leeway, NULL)) {
+			vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL)) {
 			log_err("out of memory caching validator results");
 		}
 	} else {
 		/* for a referral, store the verified RRsets */
 		/* and this does not get prefetched, so no leeway */
 		if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 
-			vq->orig_msg->rep, 1, 0, NULL)) {
+			vq->orig_msg->rep, 1, 0, 0, NULL)) {
 			log_err("out of memory caching validator results");
 		}
 	}
@@ -2923,7 +2925,6 @@
 		return 0;
 	return sizeof(*ve) + key_cache_get_mem(ve->kcache) + 
 		val_neg_get_mem(ve->neg_cache) +
-		anchors_get_mem(env->anchors) + 
 		sizeof(size_t)*2*ve->nsec3_keyiter_count;
 }
 
diff -Naur unbound-1.4.16/validator/val_sigcrypt.c unbound-trunk/validator/val_sigcrypt.c
--- unbound-1.4.16/validator/val_sigcrypt.c	2012-01-17 04:06:18.000000000 -0500
+++ unbound-trunk/validator/val_sigcrypt.c	2012-02-22 20:37:14.739368184 -0500
@@ -280,6 +280,10 @@
 				return 32;
 			else	return 0;
 #endif
+#ifdef USE_ECDSA
+		case LDNS_SHA384:
+			return SHA384_DIGEST_LENGTH;
+#endif
 		default: break;
 	}
 	return 0;
@@ -348,6 +352,12 @@
 				ldns_buffer_limit(b), (unsigned char*)digest))
 				return 1;
 #endif
+#ifdef USE_ECDSA
+		case LDNS_SHA384:
+			(void)SHA384((unsigned char*)ldns_buffer_begin(b),
+				ldns_buffer_limit(b), (unsigned char*)digest);
+			return 1;
+#endif
 		default: 
 			verbose(VERB_QUERY, "unknown DS digest algorithm %d", 
 				(int) ds_get_digest_algo(ds_rrset, ds_idx));
@@ -418,6 +428,10 @@
 #if defined(HAVE_EVP_SHA512) && defined(USE_SHA2)
 	case LDNS_RSASHA512:
 #endif
+#ifdef USE_ECDSA
+	case LDNS_ECDSAP256SHA256:
+	case LDNS_ECDSAP384SHA384:
+#endif
 		return 1;
 #ifdef USE_GOST
 	case LDNS_ECC_GOST:
@@ -1321,7 +1335,7 @@
  * Setup DSA key digest in DER encoding ... 
  * @param sig: input is signature output alloced ptr (unless failure).
  * 	caller must free alloced ptr if this routine returns true.
- * @param len: intput is initial siglen, output is output len.
+ * @param len: input is initial siglen, output is output len.
  * @return false on failure.
  */
 static int
@@ -1350,6 +1364,7 @@
 	*sig = NULL;
 	newlen = i2d_DSA_SIG(dsasig, sig);
 	if(newlen < 0) {
+		DSA_SIG_free(dsasig);
 		free(*sig);
 		return 0;
 	}
@@ -1358,6 +1373,48 @@
 	return 1;
 }
 
+#ifdef USE_ECDSA
+/**
+ * Setup the ECDSA signature in its encoding that the library wants.
+ * Converts from plain numbers to ASN formatted.
+ * @param sig: input is signature, output alloced ptr (unless failure).
+ * 	caller must free alloced ptr if this routine returns true.
+ * @param len: input is initial siglen, output is output len.
+ * @return false on failure.
+ */
+static int
+setup_ecdsa_sig(unsigned char** sig, unsigned int* len)
+{
+	ECDSA_SIG* ecdsa_sig;
+	int newlen;
+	int bnsize = (int)((*len)/2);
+	/* if too short or not even length, fails */
+	if(*len < 16 || bnsize*2 != (int)*len)
+		return 0;
+	/* use the raw data to parse two evenly long BIGNUMs, "r | s". */
+	ecdsa_sig = ECDSA_SIG_new();
+	if(!ecdsa_sig) return 0;
+	ecdsa_sig->r = BN_bin2bn(*sig, bnsize, ecdsa_sig->r);
+	ecdsa_sig->s = BN_bin2bn(*sig+bnsize, bnsize, ecdsa_sig->s);
+	if(!ecdsa_sig->r || !ecdsa_sig->s) {
+		ECDSA_SIG_free(ecdsa_sig);
+		return 0;
+	}
+
+	/* spool it into ASN format */
+	*sig = NULL;
+	newlen = i2d_ECDSA_SIG(ecdsa_sig, sig);
+	if(newlen <= 0) {
+		ECDSA_SIG_free(ecdsa_sig);
+		free(*sig);
+		return 0;
+	}
+	*len = (unsigned int)newlen;
+	ECDSA_SIG_free(ecdsa_sig);
+	return 1;
+}
+#endif /* USE_ECDSA */
+
 /**
  * Setup key and digest for verification. Adjust sig if necessary.
  *
@@ -1472,6 +1529,62 @@
 			}
 			break;
 #endif
+#ifdef USE_ECDSA
+		case LDNS_ECDSAP256SHA256:
+			*evp_key = ldns_ecdsa2pkey_raw(key, keylen,
+				LDNS_ECDSAP256SHA256);
+			if(!*evp_key) {
+				verbose(VERB_QUERY, "verify: "
+					"ldns_ecdsa2pkey_raw failed");
+				return 0;
+			}
+#ifdef USE_ECDSA_EVP_WORKAROUND
+			/* openssl before 1.0.0 fixes RSA with the SHA256
+			 * hash in EVP.  We create one for ecdsa_sha256 */
+			{
+				static int md_ecdsa_256_done = 0;
+				static EVP_MD md;
+				if(!md_ecdsa_256_done) {
+					EVP_MD m = *EVP_sha256();
+					md_ecdsa_256_done = 1;
+					m.required_pkey_type[0] = (*evp_key)->type;
+					m.verify = (void*)ECDSA_verify;
+					md = m;
+				}
+				*digest_type = &md;
+			}
+#else
+			*digest_type = EVP_sha256();
+#endif
+			break;
+		case LDNS_ECDSAP384SHA384:
+			*evp_key = ldns_ecdsa2pkey_raw(key, keylen,
+				LDNS_ECDSAP384SHA384);
+			if(!*evp_key) {
+				verbose(VERB_QUERY, "verify: "
+					"ldns_ecdsa2pkey_raw failed");
+				return 0;
+			}
+#ifdef USE_ECDSA_EVP_WORKAROUND
+			/* openssl before 1.0.0 fixes RSA with the SHA384
+			 * hash in EVP.  We create one for ecdsa_sha384 */
+			{
+				static int md_ecdsa_384_done = 0;
+				static EVP_MD md;
+				if(!md_ecdsa_384_done) {
+					EVP_MD m = *EVP_sha384();
+					md_ecdsa_384_done = 1;
+					m.required_pkey_type[0] = (*evp_key)->type;
+					m.verify = (void*)ECDSA_verify;
+					md = m;
+				}
+				*digest_type = &md;
+			}
+#else
+			*digest_type = EVP_sha384();
+#endif
+			break;
+#endif /* USE_ECDSA */
 		default:
 			verbose(VERB_QUERY, "verify: unknown algorithm %d", 
 				algo);
@@ -1519,7 +1632,19 @@
 			return sec_status_bogus;
 		}
 		dofree = 1;
-	} 
+	}
+#ifdef USE_ECDSA
+	else if(algo == LDNS_ECDSAP256SHA256 || algo == LDNS_ECDSAP384SHA384) {
+		/* EVP uses ASN prefix on sig, which is not in the wire data */
+		if(!setup_ecdsa_sig(&sigblock, &sigblock_len)) {
+			verbose(VERB_QUERY, "verify: failed to setup ECDSA sig");
+			*reason = "use of signature for ECDSA crypto failed";
+			EVP_PKEY_free(evp_key);
+			return sec_status_bogus;
+		}
+		dofree = 1;
+	}
+#endif /* USE_ECDSA */
 
 	/* do the signature cryptography work */
 	EVP_MD_CTX_init(&ctx);
@@ -1536,7 +1661,7 @@
 		if(dofree) free(sigblock);
 		return sec_status_unchecked;
 	}
-		
+
 	res = EVP_VerifyFinal(&ctx, sigblock, sigblock_len, evp_key);
 	if(EVP_MD_CTX_cleanup(&ctx) == 0) {
 		verbose(VERB_QUERY, "verify: EVP_MD_CTX_cleanup failed");