shithub: riscv

Download patch

ref: 0d926a251269277214ab0ce5e84f5c4674576d6f
parent: f7bc3526b3acfe70263167e0a78dbb4a4050c6e4
author: cinap_lenrek <[email protected]>
date: Fri Mar 9 01:02:17 EST 2012

dns: import changes from sources

--- a/sys/src/cmd/ndb/convM2DNS.c
+++ b/sys/src/cmd/ndb/convM2DNS.c
@@ -6,9 +6,9 @@
 typedef struct Scan	Scan;
 struct Scan
 {
-	uchar	*base;
-	uchar	*p;
-	uchar	*ep;
+	uchar	*base;		/* input buffer */
+	uchar	*p;		/* current position */
+	uchar	*ep;		/* byte after the end */
 
 	char	*err;
 	char	errbuf[256];	/* hold a formatted error sometimes */
@@ -17,16 +17,6 @@
 	int	trunc;		/* flag: input truncated */
 };
 
-#define NAME(x)		gname(x, rp, sp)
-#define SYMBOL(x)	(x = gsym(rp, sp))
-#define STRING(x)	(x = gstr(rp, sp))
-#define USHORT(x)	(x = gshort(rp, sp))
-#define ULONG(x)	(x = glong(rp, sp))
-#define UCHAR(x)	(x = gchar(rp, sp))
-#define V4ADDR(x)	(x = gv4addr(rp, sp))
-#define V6ADDR(x)	(x = gv6addr(rp, sp))
-#define BYTES(x, y)	(y = gbytes(rp, sp, &x, len - (sp->p - data)))
-
 static int
 errneg(RR *rp, Scan *sp, int actual)
 {
@@ -56,7 +46,7 @@
 	/* hack to cope with servers that don't set Ftrunc when they should */
 	if (remain < Maxudp && need > Maxudp)
 		sp->trunc = 1;
-	if (debug)
+	if (debug && rp)
 		dnslog("malformed rr: %R", rp);
 	return 0;
 }
@@ -236,6 +226,10 @@
 		goto err;
 	pointer = 0;
 	p = sp->p;
+	if (p == nil) {
+		dnslog("gname: %R: nil sp->p", rp);
+		goto err;
+	}
 	toend = to + Domlen;
 	for(len = 0; *p && p < sp->ep; len += (pointer? 0: n+1)) {
 		n = 0;
@@ -312,11 +306,21 @@
 //			where);
 		if (sp->rcode == Rok)
 			sp->rcode = Rformat;
-		return (uchar)type << 8 | type >> 8;
+		type >>= 8;
 	}
 	return type;
 }
 
+#define NAME(x)		gname(x, rp, sp)
+#define SYMBOL(x)	((x) = gsym(rp, sp))
+#define STRING(x)	((x) = gstr(rp, sp))
+#define USHORT(x)	((x) = gshort(rp, sp))
+#define ULONG(x)	((x) = glong(rp, sp))
+#define UCHAR(x)	((x) = gchar(rp, sp))
+#define V4ADDR(x)	((x) = gv4addr(rp, sp))
+#define V6ADDR(x)	((x) = gv6addr(rp, sp))
+#define BYTES(x, y)	((y) = gbytes(rp, sp, &(x), len - (sp->p - data)))
+
 /*
  *  convert the next RR from a message
  */
@@ -323,13 +327,14 @@
 static RR*
 convM2RR(Scan *sp, char *what)
 {
-	int type, class, len;
+	int type, class, len, left;
 	char dname[Domlen+1];
 	uchar *data;
-	RR *rp = nil;
+	RR *rp;
 	Txt *t, **l;
 
 retry:
+	rp = nil;
 	NAME(dname);
 	USHORT(type);
 	USHORT(class);
@@ -341,8 +346,10 @@
 
 	ULONG(rp->ttl);
 	rp->ttl += now;
-	USHORT(len);
+	USHORT(len);			/* length of data following */
 	data = sp->p;
+	assert(data != nil);
+	left = sp->ep - sp->p;
 
 	/*
 	 * ms windows generates a lot of badly-formatted hints.
@@ -350,14 +357,17 @@
 	 * it also generates answers in which p overshoots ep by exactly
 	 * one byte; this seems to be harmless, so don't log them either.
 	 */
-	if (sp->ep - sp->p < len &&
+	if (len > left &&
 	   !(strcmp(what, "hints") == 0 ||
 	     sp->p == sp->ep + 1 && strcmp(what, "answers") == 0))
-		errtoolong(rp, sp, sp->ep - sp->p, len, "convM2RR");
+		errtoolong(rp, sp, left, len, "convM2RR");
 	if(sp->err || sp->rcode || sp->stop){
 		rrfree(rp);
 		return nil;
 	}
+	/* even if we don't log an error message, truncate length to fit data */
+	if (len > left)
+		len = left;
 
 	switch(type){
 	default:
@@ -364,7 +374,6 @@
 		/* unknown type, just ignore it */
 		sp->p = data + len;
 		rrfree(rp);
-		rp = nil;
 		goto retry;
 	case Thinfo:
 		SYMBOL(rp->cpu);
@@ -493,8 +502,9 @@
 {
 	char dname[Domlen+1];
 	int type, class;
-	RR *rp = nil;
+	RR *rp;
 
+	rp = nil;
 	NAME(dname);
 	USHORT(type);
 	USHORT(class);
@@ -535,7 +545,8 @@
 		*l = rp;
 		l = &rp->next;
 	}
-//	setmalloctag(first, getcallerpc(&sp));
+//	if(first)
+//		setmalloctag(first, getcallerpc(&sp));
 	return first;
 }
 
@@ -553,9 +564,8 @@
 	Scan scan;
 	Scan *sp;
 
-	if (codep)
-		*codep = Rok;
 	assert(len >= 0);
+	assert(buf != nil);
 	sp = &scan;
 	memset(sp, 0, sizeof *sp);
 	sp->base = sp->p = buf;
@@ -562,6 +572,7 @@
 	sp->ep = buf + len;
 	sp->err = nil;
 	sp->errbuf[0] = '\0';
+	sp->rcode = Rok;
 
 	memset(m, 0, sizeof *m);
 	USHORT(m->id);
--- a/sys/src/cmd/ndb/dblookup.c
+++ b/sys/src/cmd/ndb/dblookup.c
@@ -100,9 +100,6 @@
 	char buf[256];
 	RR *rp, *tp;
 	DN *dp, *ndp;
-	static int parallel;
-	static int parfd[2];
-	static char token[1];
 
 	/* so far only internet lookups are implemented */
 	if(class != Cin)
@@ -109,19 +106,19 @@
 		return 0;
 
 	err = Rname;
+	rp = nil;
 
 	if(type == Tall){
-		lock(&dnlock);
-		rp = nil;
 		for (type = Ta; type < Tall; type++)
-			if(implemented[type])
-				rrcat(&rp, dblookup(name, class, type, auth, ttl));
-		unlock(&dnlock);
+			if(implemented[type]) {
+				tp = dblookup(name, class, type, auth, ttl);
+				lock(&dnlock);
+				rrcat(&rp, tp);
+				unlock(&dnlock);
+			}
 		return rp;
 	}
 
-	rp = nil;
-
 	lock(&dblock);
 	dp = dnlookup(name, class, 1);
 
@@ -1199,6 +1196,8 @@
 
 	if (!cfg.inside || !cfg.straddle || !cfg.serve)
 		return 1;
+	if (dom[0] == '\0' || strcmp(dom, ".") == 0)	/* dns root? */
+		return 1;			/* hack for initialisation */
 
 	lock(&dblock);
 	if (indoms == nil)
@@ -1205,7 +1204,7 @@
 		loaddomsrvs();
 	if (indoms == nil) {
 		unlock(&dblock);
-		return 1;	/* no "inside" sys, try inside nameservers */
+		return 1;  /* no "inside-dom" sys, try inside nameservers */
 	}
 
 	rv = 0;
--- a/sys/src/cmd/ndb/dn.c
+++ b/sys/src/cmd/ndb/dn.c
@@ -14,12 +14,15 @@
  * figure it out.
  */
 enum {
-	Deftarget = 8000,
+	Deftarget	= 1<<30,	/* effectively disable aging */
+	Minage		= 1<<30,
+	Defagefreq	= 1<<30,	/* age names this often (seconds) */
+
+	/* these settings will trigger frequent aging */
+//	Deftarget	= 4000,
+//	Minage		=  5*60,
+//	Defagefreq	= 15*60,	/* age names this often (seconds) */
 };
-enum {
-	Minage		= 10*60,
-	Defagefreq	= 30*60,	/* age names this often (seconds) */
-};
 
 /*
  *  Hash table for domain names.  The hash is based only on the
@@ -339,13 +342,20 @@
 }
 
 /*
- *  delete rp from *l, free rp.
+ *  delete head of *l and free the old head.
  *  call with dnlock held.
  */
 static void
-rrdelete(RR **l, RR *rp)
+rrdelhead(RR **l)
 {
-	*l = rp->next;
+	RR *rp;
+
+	if (canlock(&dnlock))
+		abort();	/* rrdelhead called with dnlock not held */
+	rp = *l;
+	if(rp == nil)
+		return;
+	*l = rp->next;		/* unlink head */
 	rp->cached = 0;		/* avoid blowing an assertion in rrfree */
 	rrfree(rp);
 }
@@ -361,6 +371,8 @@
 	RR *rp, *next;
 	ulong diff;
 
+	if (canlock(&dnlock))
+		abort();	/* dnage called with dnlock not held */
 	diff = now - dp->referenced;
 	if(diff < Reserved || dp->keep)
 		return;
@@ -370,7 +382,7 @@
 		assert(rp->magic == RRmagic && rp->cached);
 		next = rp->next;
 		if(!rp->db && (rp->expire < now || diff > dnvars.oldest))
-			rrdelete(l, rp);
+			rrdelhead(l); /* rp == *l before; *l == rp->next after */
 		else
 			l = &rp->next;
 	}
@@ -783,8 +795,9 @@
 		if(rp->db == new->db && rp->auth == new->auth){
 			/* negative drives out positive and vice versa */
 			if(rp->negative != new->negative) {
-				rrdelete(l, rp);
-				continue;		/* *l == rp->next */
+				/* rp == *l before; *l == rp->next after */
+				rrdelhead(l);
+				continue;	
 			}
 			/* all things equal, pick the newer one */
 			else if(rp->arg0 == new->arg0 && rp->arg1 == new->arg1){
@@ -794,8 +807,9 @@
 					rrfree(new);
 					return;
 				}
-				rrdelete(l, rp);
-				continue;		/* *l == rp->next */
+				/* rp == *l before; *l == rp->next after */
+				rrdelhead(l);
+				continue;
 			}
 			/*
 			 *  Hack for pointer records.  This makes sure
@@ -833,7 +847,7 @@
 void
 rrattach(RR *rp, int auth)
 {
-	RR *next;
+	RR *next, *tp;
 	DN *dp;
 
 	lock(&dnlock);
@@ -842,17 +856,20 @@
 		rp->next = nil;
 		dp = rp->owner;
 
-		/* avoid any outside spoofing */
 //		dnslog("rrattach: %s", rp->owner->name);
-		if(cfg.cachedb && !rp->db && inmyarea(rp->owner->name))
+		/* avoid any outside spoofing; leave keepers alone */
+		if(cfg.cachedb && !rp->db && inmyarea(rp->owner->name)
+//		    || dp->keep			/* TODO: make this work */
+		    )
 			rrfree(rp);
 		else {
-			/* ameliorate the memory leak */
+			/* ameliorate the memory leak (someday delete this) */
 			if (0 && rrlistlen(dp->rr) > 50 && !dp->keep) {
 				dnslog("rrattach(%s): rr list too long; "
 					"freeing it", dp->name);
-				rrfreelist(dp->rr);
+				tp = dp->rr;
 				dp->rr = nil;
+				rrfreelist(tp);
 			} else
 				USED(dp);
 			rrattach1(rp, auth);
@@ -873,6 +890,8 @@
 	Sig *sig;
 	Txt *t, *nt, **l;
 
+	if (canlock(&dnlock))
+		abort();	/* rrcopy called with dnlock not held */
 	nrp = rralloc(rp->type);
 	setmalloctag(nrp, getcallerpc(&rp));
 	switch(rp->type){
@@ -1024,8 +1043,8 @@
 		}
 
 out:
-	unlock(&dnlock);
 	unique(first);
+	unlock(&dnlock);
 //	dnslog("rrlookup(%s) -> %#p\t# in-core only", dp->name, first);
 //	if (first)
 //		setmalloctag(first, getcallerpc(&dp));
@@ -1084,6 +1103,8 @@
 	RR *olp, *nlp;
 	RR **last;
 
+	if (canlock(&dnlock))
+		abort();	/* rrcat called with dnlock not held */
 	/* check for duplicates */
 	for (olp = *start; 0 && olp; olp = olp->next)
 		for (nlp = rp; nlp; nlp = nlp->next)
@@ -1108,6 +1129,8 @@
 	RR **nl, *rp;
 	RR *first;
 
+	if (canlock(&dnlock))
+		abort();	/* rrremneg called with dnlock not held */
 	first = nil;
 	nl = &first;
 	while(*l != nil){
@@ -1573,6 +1596,7 @@
 		&& r1->arg1 == r2->arg1;
 }
 
+/* called with dnlock held */
 void
 unique(RR *rp)
 {
--- a/sys/src/cmd/ndb/dnarea.c
+++ b/sys/src/cmd/ndb/dnarea.c
@@ -91,8 +91,10 @@
 
 	while(s = *l){
 		*l = s->next;
+		lock(&dnlock);
 		rrfree(s->soarr);
 		memset(s, 0, sizeof *s);	/* cause trouble */
+		unlock(&dnlock);
 		free(s);
 	}
 }
--- a/sys/src/cmd/ndb/dnresolve.c
+++ b/sys/src/cmd/ndb/dnresolve.c
@@ -16,14 +16,28 @@
 {
 	Udp, Tcp,
 
+	Answerr=	-1,
+	Answnone,
+
 	Maxdest=	24,	/* maximum destinations for a request message */
-	Maxtrans=	3,	/* maximum transmissions to a server */
-	Maxretries=	3, /* cname+actual resends: was 32; have pity on user */
-	Maxwaitms=	1000,	/* wait no longer for a remote dns query */
-	Minwaitms=	100,	/* willing to wait for a remote dns query */
-	Remntretry=	15,	/* min. sec.s between /net.alt remount tries */
 	Maxoutstanding=	15,	/* max. outstanding queries per domain name */
+	Remntretry=	15,	/* min. sec.s between /net.alt remount tries */
 
+	/*
+	 * these are the old values; we're trying longer timeouts now
+	 * primarily for the benefit of remote nameservers querying us
+	 * during times of bad connectivity.
+	 */
+//	Maxtrans=	3,	/* maximum transmissions to a server */
+//	Maxretries=	3, /* cname+actual resends: was 32; have pity on user */
+//	Maxwaitms=	1000,	/* wait no longer for a remote dns query */
+//	Minwaitms=	100,	/* willing to wait for a remote dns query */
+
+	Maxtrans=	5,	/* maximum transmissions to a server */
+	Maxretries=	5, /* cname+actual resends: was 32; have pity on user */
+	Maxwaitms=	5000,	/* wait no longer for a remote dns query */
+	Minwaitms=	500,	/* willing to wait for a remote dns query */
+
 	Destmagic=	0xcafebabe,
 	Querymagic=	0xdeadbeef,
 };
@@ -58,8 +72,8 @@
 
 	/* dest must not be on the stack due to forking in slave() */
 	Dest	*dest;		/* array of destinations */
-	Dest	*curdest;	/* pointer to one of them */
-	int	ndest;
+	Dest	*curdest;	/* pointer to next to fill */
+	int	ndest;		/* transmit to this many on this round */
 
 	int	udpfd;
 
@@ -116,6 +130,18 @@
 	return strdup(lp+1);
 }
 
+void
+rrfreelistptr(RR **rpp)
+{
+	RR *rp;
+
+	if (rpp == nil || *rpp == nil)
+		return;
+	rp = *rpp;
+	*rpp = nil;	/* update pointer in memory before freeing list */
+	rrfreelist(rp);
+}
+
 /*
  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
  *  looking it up as a canonical name.
@@ -264,12 +290,6 @@
 	assert(p->magic == Destmagic);
 }
 
-static void
-destdestroy(Dest *p)
-{
-	USED(p);
-}
-
 /*
  * if the response to a query hasn't arrived within 100 ms.,
  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
@@ -308,6 +328,20 @@
 	qunlock(&stats);
 }
 
+/* netquery with given name servers, free ns rrs when done */
+static int
+netqueryns(Query *qp, int depth, RR *nsrp)
+{
+	int rv;
+
+	qp->nsrp = nsrp;
+	rv = netquery(qp, depth);
+	lock(&dnlock);
+	rrfreelist(nsrp);
+	unlock(&dnlock);
+	return rv;
+}
+
 static RR*
 issuequery(Query *qp, char *name, int class, int depth, int recurse)
 {
@@ -321,14 +355,9 @@
 	 */
 	if(cfg.resolver){
 		nsrp = randomize(getdnsservers(class));
-		if(nsrp != nil) {
-			qp->nsrp = nsrp;
-			if(netquery(qp, depth+1)){
-				rrfreelist(nsrp);
+		if(nsrp != nil)
+			if(netqueryns(qp, depth+1, nsrp) > Answnone)
 				return rrlookup(qp->dp, qp->type, OKneg);
-			}
-			rrfreelist(nsrp);
-		}
 	}
 
 	/*
@@ -343,7 +372,9 @@
 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
 		if(dbnsrp && dbnsrp->local){
 			rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
+			lock(&dnlock);
 			rrfreelist(dbnsrp);
+			unlock(&dnlock);
 			return rp;
 		}
 
@@ -352,8 +383,11 @@
 		 *  entries
 		 */
 		if(recurse == Dontrecurse){
-			if(dbnsrp)
+			if(dbnsrp) {
+				lock(&dnlock);
 				rrfreelist(dbnsrp);
+				unlock(&dnlock);
+			}
 			continue;
 		}
 
@@ -365,34 +399,23 @@
 
 		/* if the entry timed out, ignore it */
 		if(nsrp && nsrp->ttl < now){
-			rrfreelist(nsrp);
-			nsrp = nil;
+			lock(&dnlock);
+			rrfreelistptr(&nsrp);
+			unlock(&dnlock);
 		}
 
 		if(nsrp){
-			rrfreelist(dbnsrp);
+			lock(&dnlock);
+			rrfreelistptr(&dbnsrp);
+			unlock(&dnlock);
 
 			/* query the name servers found in cache */
-			qp->nsrp = nsrp;
-			if(netquery(qp, depth+1)){
-				rrfreelist(nsrp);
+			if(netqueryns(qp, depth+1, nsrp) > Answnone)
 				return rrlookup(qp->dp, qp->type, OKneg);
-			}
-			rrfreelist(nsrp);
-			continue;
-		}
-
-		/* use ns from db */
-		if(dbnsrp){
+		} else if(dbnsrp)
 			/* try the name servers found in db */
-			qp->nsrp = dbnsrp;
-			if(netquery(qp, depth+1)){
-				/* we got an answer */
-				rrfreelist(dbnsrp);
+			if(netqueryns(qp, depth+1, dbnsrp) > Answnone)
 				return rrlookup(qp->dp, qp->type, NOneg);
-			}
-			rrfreelist(dbnsrp);
-		}
 	}
 	return nil;
 }
@@ -440,7 +463,9 @@
 							getpid(), name, type, class);
 					return rp;
 				}
+	lock(&dnlock);
 	rrfreelist(rp);
+	unlock(&dnlock);
 	rp = nil;		/* accident prevention */
 	USED(rp);
 
@@ -451,7 +476,9 @@
 	 */
 	if(type != Tcname){
 		rp = rrlookup(dp, Tcname, NOneg);
+		lock(&dnlock);
 		rrfreelist(rp);
+		unlock(&dnlock);
 		if(rp){
 			if(debug)
 				dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname",
@@ -569,6 +596,8 @@
 	mp->flags = flags;
 	mp->id = reqno;
 	mp->qd = rp;
+	if(rp != nil)
+		mp->qdcount = 1;
 }
 
 DNSmsg *
@@ -600,7 +629,7 @@
 	rp->owner = dp;
 	initdnsmsg(&m, rp, flags, reqno);
 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
-	rrfree(m.qd);
+	rrfreelistptr(&m.qd);
 	memset(&m, 0, sizeof m);		/* cause trouble */
 	return len;
 }
@@ -608,14 +637,16 @@
 void
 freeanswers(DNSmsg *mp)
 {
-	rrfreelist(mp->qd);
-	rrfreelist(mp->an);
-	rrfreelist(mp->ns);
-	rrfreelist(mp->ar);
-	mp->qd = mp->an = mp->ns = mp->ar = nil;
+	lock(&dnlock);
+	rrfreelistptr(&mp->qd);
+	rrfreelistptr(&mp->an);
+	rrfreelistptr(&mp->ns);
+	rrfreelistptr(&mp->ar);
+	unlock(&dnlock);
+	mp->qdcount = mp->ancount = mp->nscount = mp->arcount = 0;
 }
 
-/* timed read of reply.  sets srcip */
+/* timed read of reply.  sets srcip.  ibuf must be 64K to handle tcp answers. */
 static int
 readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp,
 	uchar *srcip)
@@ -656,7 +687,7 @@
 			dnslog("readnet: %s: tcp fd unset for dest %I",
 				qp->dp->name, qp->tcpip);
 		else if (readn(fd, lenbuf, 2) != 2) {
-			dnslog("readnet: short read of tcp size from %I",
+			dnslog("readnet: short read of 2-byte tcp msg size from %I",
 				qp->tcpip);
 			/* probably a time-out */
 			notestats(startns, 1, qp->type);
@@ -792,7 +823,7 @@
 }
 
 /*
- *  Get next server address
+ *  Get next server address(es) into qp->dest[nd] and beyond
  */
 static int
 serveraddrs(Query *qp, int nd, int depth)
@@ -800,8 +831,8 @@
 	RR *rp, *arp, *trp;
 	Dest *cur;
 
-	if(nd >= Maxdest)
-		return 0;
+	if(nd >= Maxdest)		/* dest array is full? */
+		return Maxdest - 1;
 
 	/*
 	 *  look for a server whose address we already know.
@@ -875,7 +906,9 @@
 		cur->code = Rtimeout;
 		nd++;
 	}
+	lock(&dnlock);
 	rrfreelist(arp);
+	unlock(&dnlock);
 	return nd;
 }
 
@@ -893,10 +926,10 @@
 
 	/* no cache time specified, don't make anything up */
 	if(soarr != nil){
-		if(soarr->next != nil){
-			rrfreelist(soarr->next);
-			soarr->next = nil;
-		}
+		lock(&dnlock);
+		if(soarr->next != nil)
+			rrfreelistptr(&soarr->next);
+		unlock(&dnlock);
 		soaowner = soarr->owner;
 	} else
 		soaowner = nil;
@@ -945,7 +978,7 @@
 {
 	int rv = -1, nfd;
 	char *domain;
-	char conndir[40];
+	char conndir[40], net[40];
 	uchar belen[2];
 	NetConnInfo *nci;
 
@@ -984,8 +1017,10 @@
 		break;
 	case Tcp:
 		/* send via TCP & keep fd around for reply */
+		snprint(net, sizeof net, "%s/tcp",
+			(mntpt[0] != '\0'? mntpt: "/net"));
 		alarm(10*1000);
-		qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil,
+		qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil,
 			conndir, &qp->tcpctlfd);
 		alarm(0);
 		if (qp->tcpfd < 0) {
@@ -1035,8 +1070,14 @@
 	 */
 	p = qp->dest;
 	destck(p);
-	if (qp->ndest < 0 || qp->ndest > Maxdest)
+	if (qp->ndest < 0 || qp->ndest > Maxdest) {
 		dnslog("qp->ndest %d out of range", qp->ndest);
+		abort();
+	}
+	/*
+	 * we're to transmit to more destinations than we currently have,
+	 * so get another.
+	 */
 	if (qp->ndest > qp->curdest - p) {
 		j = serveraddrs(qp, qp->curdest - p, depth);
 		if (j < 0 || j >= Maxdest) {
@@ -1051,15 +1092,16 @@
 	if (qp->ndest == 0)
 		if (cfg.straddle && cfg.inside) {
 			/* get ips of "outside-ns-ips" */
-			p = qp->curdest = qp->dest;
+			qp->curdest = qp->dest;
 			for(n = 0; n < Maxdest; n++, qp->curdest++)
 				if (setdestoutns(qp->curdest, n) < 0)
 					break;
-		} else {
+			if(n == 0)
+				dnslog("xmitquery: %s: no outside-ns nameservers",
+					qp->dp->name);
+		} else
 			/* it's probably just a bogus domain, don't log it */
-			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
 			return -1;
-		}
 
 	/* send to first 'qp->ndest' destinations */
 	j = 0;
@@ -1085,6 +1127,9 @@
 			if((1<<p->nx) > qp->ndest)
 				continue;
 
+			if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0)
+				continue;		/* mistake */
+
 			procsetname("udp %sside query to %I/%s %s %s",
 				(inns? "in": "out"), p->a, p->s->name,
 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
@@ -1098,7 +1143,6 @@
 			p->nx++;
 		}
 	if(j == 0) {
-		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
 		return -1;
 	}
 	return 0;
@@ -1135,6 +1179,7 @@
 	return mp->an == nil && (mp->flags & Rmask) == Rname;
 }
 
+/* returns Answerr (-1) on errors, else number of answers, which can be zero. */
 static int
 procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
 {
@@ -1154,7 +1199,7 @@
 		freeanswers(mp);
 		if(p != qp->curdest)
 			p->code = Rserver;
-		return -1;
+		return Answerr;
 	}
 
 	/* ignore any bad delegations */
@@ -1165,10 +1210,14 @@
 			freeanswers(mp);
 			if(p != qp->curdest)
 				p->code = Rserver;
-			return -1;
+			dnslog(" and no answers");
+			return Answerr;
 		}
-		rrfreelist(mp->ns);
-		mp->ns = nil;
+		dnslog(" but has answers; ignoring ns");
+		lock(&dnlock);
+		rrfreelistptr(&mp->ns);
+		unlock(&dnlock);
+		mp->nscount = 0;
 	}
 
 	/* remove any soa's from the authority section */
@@ -1180,6 +1229,7 @@
 	unique(mp->ns);
 	unique(mp->ar);
 	unlock(&dnlock);
+
 	if(mp->an)
 		rrattach(mp->an, (mp->flags & Fauth) != 0);
 	if(mp->ar)
@@ -1189,14 +1239,18 @@
 		rrattach(mp->ns, Notauthoritative);
 	} else {
 		ndp = nil;
-		rrfreelist(mp->ns);
-		mp->ns = nil;
+		lock(&dnlock);
+		rrfreelistptr(&mp->ns);
+		unlock(&dnlock);
+		mp->nscount = 0;
 	}
 
 	/* free the question */
 	if(mp->qd) {
-		rrfreelist(mp->qd);
-		mp->qd = nil;
+		lock(&dnlock);
+		rrfreelistptr(&mp->qd);
+		unlock(&dnlock);
+		mp->qdcount = 0;
 	}
 
 	/*
@@ -1217,8 +1271,11 @@
 		 */
 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
-		else
+		else {
+			lock(&dnlock);
 			rrfreelist(soarr);
+			unlock(&dnlock);
+		}
 		return 1;
 	} else if (isnegrname(mp)) {
 		qp->dp->respcode = Rname;
@@ -1231,7 +1288,9 @@
 		return 1;
 	}
 	stats.negnorname++;
+	lock(&dnlock);
 	rrfreelist(soarr);
+	unlock(&dnlock);
 
 	/*
 	 *  if we've been given better name servers, recurse.
@@ -1239,11 +1298,13 @@
 	 *  to forward to a fixed set of named servers.
 	 */
 	if(!mp->ns || cfg.resolver && cfg.justforw)
-		return 0;
+		return Answnone;
 	tp = rrlookup(ndp, Tns, NOneg);
 	if(contains(qp->nsrp, tp)){
+		lock(&dnlock);
 		rrfreelist(tp);
-		return 0;
+		unlock(&dnlock);
+		return Answnone;
 	}
 	procsetname("recursive query for %s %s", qp->dp->name,
 		rrname(qp->type, buf, sizeof buf));
@@ -1252,7 +1313,7 @@
 	 *  netquery, which current holds qp->dp->querylck,
 	 *  so release it now and acquire it upon return.
 	 */
-//	lcktype = qtype2lck(qp->type);
+//	lcktype = qtype2lck(qp->type);		/* someday try this again */
 //	qunlock(&qp->dp->querylck[lcktype]);
 
 	nqp = emalloc(sizeof *nqp);
@@ -1303,7 +1364,8 @@
 }
 
 /*
- *  query name servers.  If the name server returns a pointer to another
+ *  query name servers.  fill in obuf with on-the-wire representation of a
+ *  DNSmsg derived from qp.  if the name server returns a pointer to another
  *  name server, recurse.
  */
 static int
@@ -1383,7 +1445,7 @@
 
 			/* free or incorporate RRs in m */
 			rv = procansw(qp, &m, srcip, depth, p);
-			if (rv > 0) {
+			if (rv > Answnone) {
 				free(qp->dest);
 				qp->dest = qp->curdest = nil; /* prevent accidents */
 				return rv;
@@ -1405,7 +1467,7 @@
 
 	free(qp->dest);
 	qp->dest = qp->curdest = nil;		/* prevent accidents */
-	return 0;
+	return Answnone;
 }
 
 /*
@@ -1465,7 +1527,6 @@
 	static ulong lastmount;
 
 	/* use alloced buffers rather than ones from the stack */
-	// ibuf = emalloc(Maxudpin+Udphdrsize);
 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
 	obuf = emalloc(Maxudp+Udphdrsize);
 
@@ -1537,9 +1598,9 @@
 	Querylck *qlp;
 	static int whined;
 
-	rv = 0;				/* pessimism */
+	rv = Answnone;			/* pessimism */
 	if(depth > 12)			/* in a recursive loop? */
-		return 0;
+		return Answnone;
 
 	slave(qp->req);
 	/*
@@ -1604,7 +1665,7 @@
 	 * if we're still looking, are inside, and have an outside domain,
 	 * try it on our outside interface, if any.
 	 */
-	if (rv == 0 && cfg.inside && !inname) {
+	if (rv == Answnone && cfg.inside && !inname) {
 		if (triedin)
 			dnslog(
 	   "[%d] netquery: internal nameservers failed for %s; trying external",
@@ -1616,7 +1677,7 @@
 
 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
 	}
-//	if (rv == 0)		/* could ask /net.alt/dns directly */
+//	if (rv == Answnone)		/* could ask /net.alt/dns directly */
 //		askoutdns(dp, qp->type);
 
 	if(lock && qlp) {
@@ -1634,6 +1695,7 @@
 	int rv;
 	char root[] = "";
 	Request req;
+	RR *rr;
 	Query *qp;
 
 	memset(&req, 0, sizeof req);
@@ -1640,11 +1702,14 @@
 	req.isslave = 1;
 	req.aborttime = timems() + Maxreqtm;
 	req.from = "internal";
+
 	qp = emalloc(sizeof *qp);
 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
-
 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
-	rv = netquery(qp, 0);
+	for (rr = qp->nsrp; rr != nil; rr = rr->next)	/* DEBUG */
+		dnslog("seerootns query nsrp: %R", rr);
+
+	rv = netquery(qp, 0);		/* lookup ". ns" using qp->nsrp */
 
 	rrfreelist(qp->nsrp);
 	querydestroy(qp);
--- a/sys/src/cmd/ndb/dnsdebug.c
+++ b/sys/src/cmd/ndb/dnsdebug.c
@@ -343,7 +343,9 @@
 
 	l = &first;
 	for(rp = serveraddrs; rp != nil; rp = rp->next){
+		lock(&dnlock);
 		rrcopy(rp, l);
+		unlock(&dnlock);
 		rrattach(first, Authoritative);
 	}
 }
--- a/sys/src/cmd/ndb/dnserver.c
+++ b/sys/src/cmd/ndb/dnserver.c
@@ -20,7 +20,7 @@
 	char tname[32];
 	DN *nsdp, *dp;
 	Area *myarea;
-	RR *tp, *neg;
+	RR *tp, *neg, *rp;
 
 	dncheck(nil, 1);
 
@@ -109,8 +109,11 @@
 			if(repp->ns){
 				/* don't pass on anything we know is wrong */
 				if(repp->ns->negative){
-					rrfreelist(repp->ns);
+					lock(&dnlock);
+					rp = repp->ns;
 					repp->ns = nil;
+					rrfreelist(rp);
+					unlock(&dnlock);
 				}
 				break;
 			}
@@ -216,8 +219,9 @@
 			hp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
 		if (hp && strncmp(hp->owner->name, "local#", 6) == 0)
 			dnslog("returning %s as hint", hp->owner->name);
+		lock(&dnlock);
 		rrcat(last, hp);
+		unlock(&dnlock);
 		break;
 	}
 }
- 
\ No newline at end of file
--- a/sys/src/cmd/ndb/dnudpserver.c
+++ b/sys/src/cmd/ndb/dnudpserver.c
@@ -283,19 +283,21 @@
 	ctl = announce(datafile, dir);
 	if(ctl < 0){
 		if(!whined++)
-			warning("can't announce on dns udp port");
+			warning("can't announce on %s", datafile);
 		return -1;
 	}
-	snprint(datafile, sizeof(datafile), "%s/data", dir);
 
 	/* turn on header style interface */
 	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg))
 		abort();			/* hmsg */
+
+	snprint(datafile, sizeof(datafile), "%s/data", dir);
 	data = open(datafile, ORDWR);
 	if(data < 0){
 		close(ctl);
 		if(!whined++)
-			warning("can't announce on dns udp port");
+			warning("can't open %s to announce on dns udp port",
+				datafile);
 		return -1;
 	}
 
--- a/sys/src/cmd/ndb/inform.c
+++ b/sys/src/cmd/ndb/inform.c
@@ -24,11 +24,6 @@
 	[10] "domain name not in zone",
 };
 
-char *dnsrch[] = {
-	"dnsdomain",
-	"dom",
-};
-
 void
 usage(void)
 {
@@ -98,12 +93,12 @@
 {
 	int debug, len, fd;
 	uint err;
-	char *sysname, *dnsdomain, *dom, *ns, net[32];
+	char *sysname, *dnsdomain, *dom, *inform, *ns, net[32];
 	uchar *p, buf[4096], addr[IPv4addrlen], v6addr[IPaddrlen];
 	ushort txid;
 	Ndb *db;
 	Ndbtuple *t, *tt;
-	static char *query[] = { "dom", "dnsdomain", "ns", };
+	static char *query[] = { "dom", "dnsdomain", "ns", "inform" };
 
 	fmtinstall('I', eipfmt);
 	fmtinstall('V', eipfmt);
@@ -112,6 +107,7 @@
 	debug = 0;
 	ns = nil;
 	dom = nil;
+	inform = nil;
 	dnsdomain = nil;
 	ARGBEGIN{
 	case 'd':
@@ -133,7 +129,7 @@
 	if((db = ndbopen(nil)) == nil)
 		sysfatal("can't open ndb: %r");
 	tt = ndbipinfo(db, "sys", sysname, query, nelem(query));
-	for(t = tt; t; t = t->entry)
+	for(t = tt; t; t = t->entry){
 		if(strcmp(t->attr, "ns") == 0)
 			ns = t->val;
 		else if(strcmp(t->attr, "dom") == 0)
@@ -140,9 +136,15 @@
 			dom = t->val;
 		else if(strcmp(t->attr, "dnsdomain") == 0)
 			dnsdomain = t->val;
+		else if(strcmp(t->attr, "inform") == 0)
+			inform = t->val;
+	}
+
 	ndbfree(tt);
 	ndbclose(db);
 
+	if(inform)
+		dom = inform;
 	if(!ns)
 		sysfatal("no relevant ns=");
 	if(!dom)
--- a/sys/src/cmd/ndb/mkhash.c
+++ b/sys/src/cmd/ndb/mkhash.c
@@ -76,7 +76,7 @@
 	uchar *p;
 
 	if(argc != 3){
-		fprint(2, "mkhash: usage file attribute\n");
+		fprint(2, "usage: mkhash file attribute\n");
 		exits("usage");
 	}
 	db = ndbopen(argv[1]);