]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/ndb/dnresolve.c
ndb/dns: do recursive lookup for dnsslave=
[plan9front.git] / sys / src / cmd / ndb / dnresolve.c
index e99177d7d35231f9fb44bf38be8e2c40087afc38..f595ce39d5fda9e32cd948edbfdf9f3760e5e3e6 100644 (file)
@@ -9,7 +9,6 @@
 #include "dns.h"
 
 typedef struct Dest Dest;
-typedef struct Ipaddr Ipaddr;
 typedef struct Query Query;
 
 enum
@@ -19,86 +18,47 @@ enum
        Answerr=        -1,
        Answnone,
 
-       Maxdest=        24,     /* maximum destinations for a request message */
+       Maxdest=        32,     /* maximum destinations for a request message */
        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,
+       Maxretries=     10,     /* cname+actual resends: was 32; have pity on user */
 };
 enum { Hurry, Patient, };
 enum { Outns, Inns, };
 
-struct Ipaddr {
-       Ipaddr *next;
-       uchar   ip[IPaddrlen];
-};
-
 struct Dest
 {
        uchar   a[IPaddrlen];   /* ip address */
-       DN      *s;             /* name server */
+       DN      *s;             /* name server name */
+       RR      *n;             /* name server rr */
        int     nx;             /* number of transmissions */
        int     code;           /* response code; used to clear dp->respcode */
-
-       ulong   magic;
 };
 
-/*
- * Query has a QLock in it, thus it can't be an automatic
- * variable, since each process would see a separate copy
- * of the lock on its stack.
- */
 struct Query {
        DN      *dp;            /* domain */
        ushort  type;           /* and type to look up */
        Request *req;
+       Query   *prev;          /* previous query */
+
        RR      *nsrp;          /* name servers to consult */
 
-       /* dest must not be on the stack due to forking in slave() */
        Dest    *dest;          /* array of destinations */
        Dest    *curdest;       /* pointer to next to fill */
        int     ndest;          /* transmit to this many on this round */
 
        int     udpfd;
 
-       QLock   tcplock;        /* only one tcp call at a time per query */
        int     tcpset;
        int     tcpfd;          /* if Tcp, read replies from here */
        int     tcpctlfd;
        uchar   tcpip[IPaddrlen];
-
-       ulong   magic;
-};
-
-/* estimated % probability of such a record existing at all */
-int likely[] = {
-       [Ta]            95,
-       [Taaaa]         10,
-       [Tcname]        15,
-       [Tmx]           60,
-       [Tns]           90,
-       [Tnull]         5,
-       [Tptr]          35,
-       [Tsoa]          90,
-       [Tsrv]          60,
-       [Ttxt]          15,
-       [Tall]          95,
 };
 
 static RR*     dnresolve1(char*, int, int, Request*, int, int);
@@ -181,13 +141,11 @@ dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
                                nrp->ptr->name);
                        rp = dnresolve(nname, class, type, req, cn, depth+1,
                                recurse, rooted, status);
-                       lock(&dnlock);
                        rrfreelist(rrremneg(&rp));
-                       unlock(&dnlock);
                }
                if(drp != nil)
                        rrfreelist(drp);
-               procsetname(procname);
+               procsetname("%s", procname);
                free(procname);
                return rp;
        }
@@ -196,12 +154,11 @@ dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
         *  try the name directly
         */
        rp = dnresolve1(name, class, type, req, depth, recurse);
-       if(rp == nil) {
+       if(rp == nil && (dp = idnlookup(name, class, 0)) != nil) {
                /*
                 * try it as a canonical name if we weren't told
                 * that the name didn't exist
                 */
-               dp = dnlookup(name, class, 0);
                if(type != Tptr && dp->respcode != Rname)
                        for(loops = 0; rp == nil && loops < Maxretries; loops++){
                                /* retry cname, then the actual type */
@@ -218,12 +175,10 @@ dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
                                }
 
                                name = rp->host->name;
-                               lock(&dnlock);
                                if(cn)
                                        rrcat(cn, rp);
                                else
                                        rrfreelist(rp);
-                               unlock(&dnlock);
 
                                rp = dnresolve1(name, class, type, req,
                                        depth, recurse);
@@ -233,7 +188,7 @@ dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
                if(rp == nil && status != nil && dp->respcode != Rok)
                        *status = dp->respcode;
        }
-       procsetname(procname);
+       procsetname("%s", procname);
        free(procname);
        return randomize(rp);
 }
@@ -241,29 +196,26 @@ dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
 static void
 queryinit(Query *qp, DN *dp, int type, Request *req)
 {
+       assert(dp && dp->magic == DNmagic);
+
        memset(qp, 0, sizeof *qp);
        qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
        qp->dp = dp;
        qp->type = type;
        if (qp->type != type)
                dnslog("queryinit: bogus type %d", type);
-       qp->req = req;
        qp->nsrp = nil;
        qp->dest = qp->curdest = nil;
-       qp->magic = Querymagic;
-}
-
-static void
-queryck(Query *qp)
-{
-       assert(qp);
-       assert(qp->magic == Querymagic);
+       qp->prev = req->aux;
+       qp->req = req;
+       req->aux = qp;
 }
 
 static void
 querydestroy(Query *qp)
 {
-       queryck(qp);
+       if(qp->req->aux == qp)
+               qp->req->aux = qp->prev;
        /* leave udpfd open */
        if (qp->tcpfd >= 0)
                close(qp->tcpfd);
@@ -271,25 +223,10 @@ querydestroy(Query *qp)
                hangup(qp->tcpctlfd);
                close(qp->tcpctlfd);
        }
-       free(qp->dest);
        memset(qp, 0, sizeof *qp);      /* prevent accidents */
        qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
 }
 
-static void
-destinit(Dest *p)
-{
-       memset(p, 0, sizeof *p);
-       p->magic = Destmagic;
-}
-
-static void
-destck(Dest *p)
-{
-       assert(p);
-       assert(p->magic == Destmagic);
-}
-
 /*
  * 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.
@@ -334,11 +271,12 @@ netqueryns(Query *qp, int depth, RR *nsrp)
 {
        int rv;
 
+       if(nsrp == nil)
+               return Answnone;
        qp->nsrp = nsrp;
        rv = netquery(qp, depth);
-       lock(&dnlock);
+       qp->nsrp = nil;         /* prevent accidents */
        rrfreelist(nsrp);
-       unlock(&dnlock);
        return rv;
 }
 
@@ -372,9 +310,7 @@ issuequery(Query *qp, char *name, int class, int depth, int recurse)
                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;
                }
 
@@ -383,31 +319,23 @@ issuequery(Query *qp, char *name, int class, int depth, int recurse)
                 *  entries
                 */
                if(recurse == Dontrecurse){
-                       if(dbnsrp) {
-                               lock(&dnlock);
+                       if(dbnsrp)
                                rrfreelist(dbnsrp);
-                               unlock(&dnlock);
-                       }
                        continue;
                }
 
                /* look for ns in cache */
-               nsdp = dnlookup(cp, class, 0);
+               nsdp = idnlookup(cp, class, 0);
                nsrp = nil;
                if(nsdp)
                        nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
 
                /* if the entry timed out, ignore it */
-               if(nsrp && nsrp->ttl < now){
-                       lock(&dnlock);
+               if(nsrp && !nsrp->db && (long)(nsrp->expire - now) <= 0)
                        rrfreelistptr(&nsrp);
-                       unlock(&dnlock);
-               }
 
                if(nsrp){
-                       lock(&dnlock);
                        rrfreelistptr(&dbnsrp);
-                       unlock(&dnlock);
 
                        /* query the name servers found in cache */
                        if(netqueryns(qp, depth+1, nsrp) > Answnone)
@@ -427,7 +355,7 @@ dnresolve1(char *name, int class, int type, Request *req, int depth,
        Area *area;
        DN *dp;
        RR *rp;
-       Query *qp;
+       Query q;
 
        if(debug)
                dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
@@ -436,7 +364,7 @@ dnresolve1(char *name, int class, int type, Request *req, int depth,
        if(class != Cin)
                return nil;
 
-       dp = dnlookup(name, class, 1);
+       dp = idnlookup(name, class, 1);
 
        /*
         *  Try the cache first
@@ -454,7 +382,7 @@ dnresolve1(char *name, int class, int type, Request *req, int depth,
                        }
                } else
                        /* cached entry must still be valid */
-                       if(rp->ttl > now)
+                       if((long)(rp->expire - now) > 0)
                                /* but Tall entries are special */
                                if(type != Tall || rp->query == Tall) {
                                        noteinmem();
@@ -463,9 +391,7 @@ dnresolve1(char *name, int class, int type, Request *req, int depth,
                                                        getpid(), name, type, class);
                                        return rp;
                                }
-       lock(&dnlock);
        rrfreelist(rp);
-       unlock(&dnlock);
        rp = nil;               /* accident prevention */
        USED(rp);
 
@@ -476,9 +402,7 @@ dnresolve1(char *name, int class, int type, Request *req, int depth,
         */
        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",
@@ -492,19 +416,13 @@ dnresolve1(char *name, int class, int type, Request *req, int depth,
         * we should have found its data in memory by now.
         */
        area = inmyarea(dp->name);
-       if (area || strncmp(dp->name, "local#", 6) == 0) {
-//             char buf[32];
-
-//             dnslog("%s %s: no data in area %s", dp->name,
-//                     rrname(type, buf, sizeof buf), area->soarr->owner->name);
+       if (area || strncmp(dp->name, "local#", 6) == 0)
                return nil;
-       }
 
-       qp = emalloc(sizeof *qp);
-       queryinit(qp, dp, type, req);
-       rp = issuequery(qp, name, class, depth, recurse);
-       querydestroy(qp);
-       free(qp);
+       queryinit(&q, dp, type, req);
+       rp = issuequery(&q, name, class, depth, recurse);
+       querydestroy(&q);
+
        if(rp){
                if(debug)
                        dnslog("[%d] dnresolve1 %s %d %d: rr from query",
@@ -567,7 +485,7 @@ udpport(char *mtpt)
        char ds[64], adir[64];
 
        /* get a udp port */
-       snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
+       snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt && *mtpt) ? mtpt : "/net");
        ctl = announce(ds, adir);
        if(ctl < 0){
                /* warning("can't get udp port"); */
@@ -593,6 +511,7 @@ udpport(char *mtpt)
 void
 initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno)
 {
+       memset(mp, 0, sizeof *mp);
        mp->flags = flags;
        mp->id = reqno;
        mp->qd = rp;
@@ -600,16 +519,6 @@ initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno)
                mp->qdcount = 1;
 }
 
-DNSmsg *
-newdnsmsg(RR *rp, int flags, ushort reqno)
-{
-       DNSmsg *mp;
-
-       mp = emalloc(sizeof *mp);
-       initdnsmsg(mp, rp, flags, reqno);
-       return mp;
-}
-
 /* generate a DNS UDP query packet */
 int
 mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
@@ -624,25 +533,21 @@ mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
        hnputs(uh->rport, 53);
 
        /* make request and convert it to output format */
-       memset(&m, 0, sizeof m);
        rp = rralloc(type);
        rp->owner = dp;
        initdnsmsg(&m, rp, flags, reqno);
        len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
-       rrfreelistptr(&m.qd);
-       memset(&m, 0, sizeof m);                /* cause trouble */
+       rrfreelist(rp);
        return len;
 }
 
 void
 freeanswers(DNSmsg *mp)
 {
-       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;
 }
 
@@ -658,12 +563,13 @@ readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp,
        uchar lenbuf[2];
 
        len = -1;                       /* pessimism */
+       *replyp = nil;
+       memset(srcip, 0, IPaddrlen);
        ms = endms - NS2MS(startns);
        if (ms <= 0)
                return -1;              /* taking too long */
 
        reply = ibuf;
-       memset(srcip, 0, IPaddrlen);
        alarm(ms);
        if (medium == Udp)
                if (qp->udpfd < 0)
@@ -724,11 +630,6 @@ readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
        uchar srcip[IPaddrlen];
        RR *rp;
 
-       queryck(qp);
-       memset(mp, 0, sizeof *mp);
-       memset(srcip, 0, sizeof srcip);
-       if (0)
-               len = -1;
        for (; timems() < endms &&
            (len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0;
            freeanswers(mp)){
@@ -782,6 +683,7 @@ readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
                                                "ns %s", qp->dp->name,
                                                rp->host->name);
        }
+       memset(mp, 0, sizeof *mp);
        return -1;
 }
 
@@ -822,40 +724,60 @@ ipisbm(uchar *ip)
        return 0;
 }
 
+static int
+queryloops(Query *qp, RR *rp)
+{
+       DN *ns = rp->host;
+
+       /*
+        *  looking up a server under itself
+        */
+       if(subsume(rp->owner->name, ns->name))
+               return 1;
+
+       /*
+        *  cycle on name servers refering
+        *  to each another.
+        */
+       for(; qp; qp = qp->prev)
+               if(qp->dp == ns)
+                       return 1;
+
+       return 0;
+}
+
 /*
- *  Get next server address(es) into qp->dest[nd] and beyond
+ *  Get next server type address(es) into qp->dest[nd] and beyond
  */
 static int
-serveraddrs(Query *qp, int nd, int depth)
+serveraddrs(Query *qp, int nd, int depth, int type)
 {
        RR *rp, *arp, *trp;
-       Dest *cur;
+       ulong mark;
+       Dest *p;
 
        if(nd >= Maxdest)               /* dest array is full? */
-               return Maxdest - 1;
+               return Maxdest;
 
        /*
         *  look for a server whose address we already know.
         *  if we find one, mark it so we ignore this on
         *  subsequent passes.
         */
-       arp = 0;
+       mark = 1UL<<type;
+       arp = nil;
        for(rp = qp->nsrp; rp; rp = rp->next){
                assert(rp->magic == RRmagic);
-               if(rp->marker)
+               if(rp->marker & mark)
                        continue;
-               arp = rrlookup(rp->host, Ta, NOneg);
-               if(arp == nil)
-                       arp = rrlookup(rp->host, Taaaa, NOneg);
+               arp = rrlookup(rp->host, type, NOneg);
                if(arp){
-                       rp->marker = 1;
+                       rp->marker |= mark;
                        break;
                }
-               arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
-               if(arp == nil)
-                       arp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
+               arp = dblookup(rp->host->name, Cin, type, 0, 0);
                if(arp){
-                       rp->marker = 1;
+                       rp->marker |= mark;
                        break;
                }
        }
@@ -865,50 +787,57 @@ serveraddrs(Query *qp, int nd, int depth)
         *  server addresses, try resolving one via the network.
         *  Mark any we try to resolve so we don't try a second time.
         */
-       if(arp == 0)
-               for(rp = qp->nsrp; rp; rp = rp->next){
-                       if(rp->marker)
-                               continue;
-                       rp->marker = 1;
+       if(arp == nil){
+               for(rp = qp->nsrp; rp; rp = rp->next)
+                       if((rp->marker & mark) == 0)
+                       if(queryloops(qp, rp))
+                               /*
+                                * give up as we should have got the address
+                                * by higher up nameserver when recursing
+                                * down, or will be queried when recursing up.
+                                */
+                               return nd;
 
-                       /*
-                        *  avoid loops looking up a server under itself
-                        */
-                       if(subsume(rp->owner->name, rp->host->name))
+               for(rp = qp->nsrp; rp; rp = rp->next){
+                       if(rp->marker & mark)
                                continue;
-
-                       arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
+                       rp->marker |= mark;
+                       arp = dnresolve(rp->host->name, Cin, type, qp->req, 0,
                                depth+1, Recurse, 1, 0);
-                       if(arp == nil)
-                               arp = dnresolve(rp->host->name, Cin, Taaaa,
-                                       qp->req, 0, depth+1, Recurse, 1, 0);
-                       lock(&dnlock);
                        rrfreelist(rrremneg(&arp));
-                       unlock(&dnlock);
                        if(arp)
                                break;
                }
+       }
 
        /* use any addresses that we found */
        for(trp = arp; trp && nd < Maxdest; trp = trp->next){
-               cur = &qp->dest[nd];
-               parseip(cur->a, trp->ip->name);
+               p = &qp->dest[nd];
+               memset(p, 0, sizeof *p);
+               if(parseip(p->a, trp->ip->name) == -1)
+                       continue;
+
                /*
                 * straddling servers can reject all nameservers if they are all
                 * inside, so be sure to list at least one outside ns at
                 * the end of the ns list in /lib/ndb for `dom='.
                 */
-               if (ipisbm(cur->a) ||
-                   cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
+               if (ipisbm(p->a) ||
+                   cfg.straddle && !insideaddr(qp->dp->name) && insidens(p->a))
                        continue;
-               cur->nx = 0;
-               cur->s = trp->owner;
-               cur->code = Rtimeout;
+               p->nx = 0;
+               p->n = nil;
+               p->s = trp->owner;
+               for(rp = qp->nsrp; rp; rp = rp->next){
+                       if(rp->host == p->s){
+                               p->n = rp;
+                               break;
+                       }
+               }
+               p->code = Rtimeout;
                nd++;
        }
-       lock(&dnlock);
        rrfreelist(arp);
-       unlock(&dnlock);
        return nd;
 }
 
@@ -922,21 +851,21 @@ cacheneg(DN *dp, int type, int rcode, RR *soarr)
        DN *soaowner;
        ulong ttl;
 
+       qlock(&stats);
        stats.negcached++;
+       qunlock(&stats);
 
        /* no cache time specified, don't make anything up */
        if(soarr != nil){
-               lock(&dnlock);
                if(soarr->next != nil)
                        rrfreelistptr(&soarr->next);
-               unlock(&dnlock);
                soaowner = soarr->owner;
        } else
                soaowner = nil;
 
        /* the attach can cause soarr to be freed so mine it now */
        if(soarr != nil && soarr->soa != nil)
-               ttl = soarr->soa->minttl+now;
+               ttl = soarr->soa->minttl;
        else
                ttl = 5*Min;
 
@@ -955,16 +884,12 @@ cacheneg(DN *dp, int type, int rcode, RR *soarr)
 static int
 setdestoutns(Dest *p, int n)
 {
-       uchar *outns = outsidens(n);
-
-       destck(p);
-       destinit(p);
-       if (outns == nil) {
+       memset(p, 0, sizeof *p);
+       if (outsidensip(n, p->a) < 0){
                if (n == 0)
                        dnslog("[%d] no outside-ns in ndb", getpid());
                return -1;
        }
-       memmove(p->a, outns, sizeof p->a);
        p->s = dnlookup("outside-ns-ips", Cin, 1);
        return 0;
 }
@@ -976,30 +901,22 @@ setdestoutns(Dest *p, int n)
 static int
 mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
 {
-       int rv = -1, nfd;
-       char *domain;
-       char conndir[40], net[40];
+       int rv, nfd;
+       char conndir[40], addr[128];
        uchar belen[2];
        NetConnInfo *nci;
 
-       queryck(qp);
-       domain = smprint("%I", udppkt);
-       if (myaddr(domain)) {
-               dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
-                       domain);
-               free(domain);
+       rv = -1;
+       if (myip(udppkt))
                return rv;
-       }
-
        switch (medium) {
        case Udp:
-               free(domain);
                nfd = dup(qp->udpfd, -1);
                if (nfd < 0) {
                        warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
                        close(qp->udpfd);       /* ensure it's closed */
                        qp->udpfd = -1;         /* poison it */
-                       return rv;
+                       break;
                }
                close(nfd);
 
@@ -1010,25 +927,25 @@ mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
                            len+Udphdrsize)
                                warning("sending udp msg: %r");
                        else {
+                               qlock(&stats);
                                stats.qsent++;
+                               qunlock(&stats);
                                rv = 0;
                        }
                }
                break;
        case Tcp:
                /* send via TCP & keep fd around for reply */
-               snprint(net, sizeof net, "%s/tcp",
-                       (mntpt[0] != '\0'? mntpt: "/net"));
+               memmove(qp->tcpip, udppkt, sizeof qp->tcpip);
+               snprint(addr, sizeof addr, "%s/tcp!%I!dns",
+                       (mntpt && *mntpt) ? mntpt : "/net", udppkt);
                alarm(10*1000);
-               qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil,
-                       conndir, &qp->tcpctlfd);
+               qp->tcpfd = dial(addr, nil, conndir, &qp->tcpctlfd);
                alarm(0);
                if (qp->tcpfd < 0) {
-                       dnslog("can't dial tcp!%s!dns: %r", domain);
-                       free(domain);
+                       dnslog("can't dial %s: %r", addr);
                        break;
                }
-               free(domain);
                nci = getnetconninfo(conndir, qp->tcpfd);
                if (nci) {
                        parseip(qp->tcpip, nci->rsys);
@@ -1042,9 +959,9 @@ mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
                if (write(qp->tcpfd, belen, 2) != 2 ||
                    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
                        warning("sending tcp msg: %r");
+               else
+                       rv = 0;
                break;
-       default:
-               sysfatal("mydnsquery: bad medium");
        }
        return rv;
 }
@@ -1056,209 +973,241 @@ mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
 static int
 xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
 {
-       int j, n;
+       int n;
        char buf[32];
        Dest *p;
 
-       queryck(qp);
        if(timems() >= qp->req->aborttime)
                return -1;
 
        /*
-        * get a nameserver address if we need one.
-        * serveraddrs populates qp->dest.
+        * if we send tcp query, we just take the dest ip address from
+        * the udp header placed there by tcpquery().
         */
-       p = qp->dest;
-       destck(p);
-       if (qp->ndest < 0 || qp->ndest > Maxdest) {
-               dnslog("qp->ndest %d out of range", qp->ndest);
-               abort();
+       if (medium == Tcp) {
+               procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
+                       qp->dp->name, rrname(qp->type, buf, sizeof buf));
+               if(mydnsquery(qp, medium, obuf, len) < 0) /* sets qp->tcpip from obuf */
+                       return -1;
+               if(debug)
+                       logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
+                               qp->type);
+               return 0;
        }
+
        /*
+        * get a nameserver address if we need one.
         * 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) {
-                       dnslog("serveraddrs() result %d out of range", j);
-                       abort();
-               }
-               qp->curdest = &qp->dest[j];
-       }
-       destck(qp->curdest);
-
-       /* no servers, punt */
-       if (qp->ndest == 0)
-               if (cfg.straddle && cfg.inside) {
+       p = qp->dest;
+       n = qp->curdest - p;
+       if (qp->ndest > n) {
+               /* populates qp->dest with v4 and v6 addresses. */
+               n = serveraddrs(qp, n, depth, Ta);
+               n = serveraddrs(qp, n, depth, Taaaa);
+               if (n == 0 && cfg.straddle && cfg.inside) {
                        /* get ips of "outside-ns-ips" */
-                       qp->curdest = qp->dest;
-                       for(n = 0; n < Maxdest; n++, qp->curdest++)
-                               if (setdestoutns(qp->curdest, n) < 0)
+                       while(n < Maxdest){
+                               if (setdestoutns(&qp->dest[n], n) < 0)
                                        break;
+                               n++;
+                       }
                        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 */
-                       return -1;
+               }
+               qp->curdest = &qp->dest[n];
+       }
 
-       /* send to first 'qp->ndest' destinations */
-       j = 0;
-       if (medium == Tcp) {
-               j++;
-               queryck(qp);
-               assert(qp->dp);
-               procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
+       for(n = 0; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
+               /* skip destinations we've finished with */
+               if(p->nx >= Maxtrans)
+                       continue;
+               /* exponential backoff of requests */
+               if((1<<p->nx) > qp->ndest)
+                       continue;
+
+               if(ipcmp(p->a, 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));
-               mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
                if(debug)
-                       logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
-                               qp->type);
-       } else
-               for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
-                       /* skip destinations we've finished with */
-                       if(p->nx >= Maxtrans)
-                               continue;
+                       logsend(qp->req->id, depth, p->a, p->s->name,
+                               qp->dp->name, qp->type);
+
+               /* fill in UDP destination addr & send it */
+               memmove(obuf, p->a, sizeof p->a);
+               if(mydnsquery(qp, medium, obuf, len) == 0)
+                       n++;
+               p->nx++;
+       }
 
-                       j++;
+       return n == 0 ? -1 : 0;
+}
 
-                       /* exponential backoff of requests */
-                       if((1<<p->nx) > qp->ndest)
-                               continue;
+/* is mp a cachable negative response (with Rname set)? */
+static int
+isnegrname(DNSmsg *mp)
+{
+       /* TODO: could add || cfg.justforw to RHS of && */
+       return mp->an == nil && (mp->flags & Rmask) == Rname;
+}
 
-                       if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0)
-                               continue;               /* mistake */
+static int
+filterhints(RR *rp, void *arg)
+{
+       RR *nsrp;
 
-                       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));
-                       if(debug)
-                               logsend(qp->req->id, depth, p->a, p->s->name,
-                                       qp->dp->name, qp->type);
+       if(rp->type != Ta && rp->type != Taaaa)
+               return 0;
+
+       for(nsrp = arg; nsrp; nsrp = nsrp->next)
+               if(nsrp->type == Tns && rp->owner == nsrp->host)
+                       return 1;
 
-                       /* fill in UDP destination addr & send it */
-                       memmove(obuf, p->a, sizeof p->a);
-                       mydnsquery(qp, medium, obuf, len);
-                       p->nx++;
-               }
-       if(j == 0) {
-               return -1;
-       }
        return 0;
 }
 
-static int lckindex[Maxlcks] = {
-       0,                      /* all others map here */
-       Ta,
-       Tns,
-       Tcname,
-       Tsoa,
-       Tptr,
-       Tmx,
-       Ttxt,
-       Taaaa,
-};
-
 static int
-qtype2lck(int qtype)           /* map query type to querylck index */
+filterauth(RR *rp, void *arg)
 {
-       int i;
+       Dest *dest;
+       RR *nsrp;
 
-       for (i = 1; i < nelem(lckindex); i++)
-               if (lckindex[i] == qtype)
-                       return i;
-       return 0;
+       dest = arg;
+       nsrp = dest->n;
+       if(nsrp == nil)
+               return 0;
+
+       if(rp->type == Tsoa && rp->owner != nsrp->owner
+       && !subsume(nsrp->owner->name, rp->owner->name)
+       && strncmp(nsrp->owner->name, "local#", 6) != 0)
+               return 1;
+
+       if(rp->type != Tns)
+               return 0;
+
+       if(rp->owner != nsrp->owner
+       && !subsume(nsrp->owner->name, rp->owner->name)
+       && strncmp(nsrp->owner->name, "local#", 6) != 0)
+               return 1;
+
+       return baddelegation(rp, nsrp, dest->a);
 }
 
-/* is mp a cachable negative response (with Rname set)? */
-static int
-isnegrname(DNSmsg *mp)
+static void
+reportandfree(RR *l, char *note, Dest *p)
 {
-       /* TODO: could add || cfg.justforw to RHS of && */
-       return mp->an == nil && (mp->flags & Rmask) == Rname;
+       RR *rp;
+
+       while(rp = l){
+               l = l->next;
+               rp->next = nil;
+               if(debug)
+                       dnslog("ignoring %s from %I/%s: %R",
+                               note, p->a, p->s->name, rp);
+               rrfree(rp);
+       }
 }
 
 /* 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)
+procansw(Query *qp, DNSmsg *mp, int depth, Dest *p)
 {
        int rv;
-//     int lcktype;
        char buf[32];
        DN *ndp;
-       Query *nqp;
+       Query nq;
        RR *tp, *soarr;
 
-       if (mp->an == nil)
+       if(mp->an == nil)
                stats.negans++;
 
        /* ignore any error replies */
-       if((mp->flags & Rmask) == Rserver){
+       switch(mp->flags & Rmask){
+       case Rrefused:
+       case Rserver:
                stats.negserver++;
                freeanswers(mp);
-               if(p != qp->curdest)
-                       p->code = Rserver;
+               p->code = Rserver;
                return Answerr;
        }
 
        /* ignore any bad delegations */
-       if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
-               stats.negbaddeleg++;
-               if(mp->an == nil){
-                       stats.negbdnoans++;
-                       freeanswers(mp);
-                       if(p != qp->curdest)
-                               p->code = Rserver;
-                       dnslog(" and no answers");
-                       return Answerr;
-               }
-               dnslog(" but has answers; ignoring ns");
-               lock(&dnlock);
-               rrfreelistptr(&mp->ns);
-               unlock(&dnlock);
-               mp->nscount = 0;
-       }
+       if((tp = rrremfilter(&mp->ns, filterauth, p)) != 0)
+               reportandfree(tp, "bad delegation", p);
 
        /* remove any soa's from the authority section */
-       lock(&dnlock);
        soarr = rrremtype(&mp->ns, Tsoa);
 
+       /* only nameservers remaining */
+       if((tp = rrremtype(&mp->ns, Tns)) != 0){
+               reportandfree(mp->ns, "non-nameserver", p);
+               mp->ns = tp;
+       }
+
+       /* remove answers not related to the question. */
+       if((tp = rrremowner(&mp->an, qp->dp)) != 0){
+               reportandfree(mp->an, "wrong subject answer", p);
+               mp->an = tp;
+       }
+       if(qp->type != Tall){
+               if((tp = rrremtype(&mp->an, qp->type)) != 0){
+                       reportandfree(mp->an, "wrong type answer", p);
+                       mp->an = tp;
+               }
+       }
+
        /* incorporate answers */
        unique(mp->an);
        unique(mp->ns);
        unique(mp->ar);
-       unlock(&dnlock);
 
-       if(mp->an)
+       if(mp->an){
+               /*
+                * only use cname answer when returned. some dns servers
+                * attach (potential) spam hint address records which poisons
+                * the cache.
+                */
+               if((tp = rrremtype(&mp->an, Tcname)) != 0){
+                       reportandfree(mp->an, "ip in cname answer", p);
+                       mp->an = tp;
+               }
                rrattach(mp->an, (mp->flags & Fauth) != 0);
-       if(mp->ar)
+       }
+       if(mp->ar){
+               /* restrict hints to address rr's for nameservers only */
+               if((tp = rrremfilter(&mp->ar, filterhints, mp->ns)) != 0){
+                       reportandfree(mp->ar, "hint", p);
+                       mp->ar = tp;
+               }
                rrattach(mp->ar, Notauthoritative);
+       }
        if(mp->ns && !cfg.justforw){
                ndp = mp->ns->owner;
                rrattach(mp->ns, Notauthoritative);
        } else {
                ndp = nil;
-               lock(&dnlock);
                rrfreelistptr(&mp->ns);
-               unlock(&dnlock);
                mp->nscount = 0;
        }
 
        /* free the question */
        if(mp->qd) {
-               lock(&dnlock);
                rrfreelistptr(&mp->qd);
-               unlock(&dnlock);
                mp->qdcount = 0;
        }
 
        /*
-        *  Any reply from an authoritative server,
+        *  Any reply from an authoritative server
+        *  that does not provide more nameservers,
         *  or a positive reply terminates the search.
         *  A negative response now also terminates the search.
         */
-       if(mp->an != nil || (mp->flags & Fauth)){
+       if(mp->an || (mp->flags & Fauth) && mp->ns == nil){
                if(isnegrname(mp))
                        qp->dp->respcode = Rname;
                else
@@ -1271,11 +1220,8 @@ procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
                 */
                if( /* (mp->flags & Fauth) && */ mp->an == nil)
                        cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
-               else {
-                       lock(&dnlock);
+               else
                        rrfreelist(soarr);
-                       unlock(&dnlock);
-               }
                return 1;
        } else if (isnegrname(mp)) {
                qp->dp->respcode = Rname;
@@ -1288,43 +1234,27 @@ procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
                return 1;
        }
        stats.negnorname++;
-       lock(&dnlock);
        rrfreelist(soarr);
-       unlock(&dnlock);
 
        /*
         *  if we've been given better name servers, recurse.
         *  if we're a pure resolver, don't recurse, we have
         *  to forward to a fixed set of named servers.
         */
-       if(!mp->ns || cfg.resolver && cfg.justforw)
+       if(mp->ns == nil || cfg.resolver && cfg.justforw)
                return Answnone;
        tp = rrlookup(ndp, Tns, NOneg);
        if(contains(qp->nsrp, tp)){
-               lock(&dnlock);
                rrfreelist(tp);
-               unlock(&dnlock);
                return Answnone;
        }
        procsetname("recursive query for %s %s", qp->dp->name,
                rrname(qp->type, buf, sizeof buf));
-       /*
-        *  we're called from udpquery, called from
-        *  netquery, which current holds qp->dp->querylck,
-        *  so release it now and acquire it upon return.
-        */
-//     lcktype = qtype2lck(qp->type);          /* someday try this again */
-//     qunlock(&qp->dp->querylck[lcktype]);
-
-       nqp = emalloc(sizeof *nqp);
-       queryinit(nqp, qp->dp, qp->type, qp->req);
-       nqp->nsrp = tp;
-       rv = netquery(nqp, depth+1);
-
-//     qlock(&qp->dp->querylck[lcktype]);
-       rrfreelist(nqp->nsrp);
-       querydestroy(nqp);
-       free(nqp);
+
+       queryinit(&nq, qp->dp, qp->type, qp->req);
+       rv = netqueryns(&nq, depth+1, tp);
+       querydestroy(&nq);
+
        return rv;
 }
 
@@ -1347,9 +1277,8 @@ tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
                dnslog("%s: udp reply truncated; retrying query via tcp to %I",
                        qp->dp->name, qp->tcpip);
 
-       qlock(&qp->tcplock);
        memmove(obuf, ibuf, IPaddrlen);         /* send back to respondent */
-       /* sets qp->tcpip from obuf's udp header */
+       memset(mp, 0, sizeof *mp);
        if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
            readreply(qp, Tcp, req, ibuf, mp, endms) < 0)
                rv = -1;
@@ -1359,7 +1288,7 @@ tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
                close(qp->tcpfd);
        }
        qp->tcpfd = qp->tcpctlfd = -1;
-       qunlock(&qp->tcplock);
+
        return rv;
 }
 
@@ -1371,24 +1300,25 @@ tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
 static int
 queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
 {
-       int ndest, len, replywaits, rv;
+       int ndest, len, replywaits, rv, flag;
        ushort req;
        uvlong endms;
-       char buf[12];
+       char buf[32];
        uchar srcip[IPaddrlen];
-       Dest *p, *np, *dest;
+       Dest *p, *np, dest[Maxdest];
 
-       /* pack request into a udp message */
        req = rand();
-       len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
+
+       /* request recursion only for local dns servers */
+       flag = Oquery;
+       if(strncmp(qp->nsrp->owner->name, "local#", 6) == 0)
+               flag |= Frecurse;
+
+       /* pack request into a udp message */
+       len = mkreq(qp->dp, qp->type, obuf, flag, req);
 
        /* no server addresses yet */
-       queryck(qp);
-       dest = emalloc(Maxdest * sizeof *dest); /* dest can't be on stack */
-       for (p = dest; p < dest + Maxdest; p++)
-               destinit(p);
-       /* this dest array is local to this call of queryns() */
-       free(qp->dest);
+       memset(dest, 0, sizeof dest);
        qp->curdest = qp->dest = dest;
 
        /*
@@ -1397,7 +1327,7 @@ queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
         *  each cycle send one more message than the previous.
         *  retry a query via tcp if its response is truncated.
         */
-       for(ndest = 1; ndest < Maxdest; ndest++){
+       for(ndest = 2; ndest < Maxdest; ndest += 2){
                qp->ndest = ndest;
                qp->tcpset = 0;
                if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
@@ -1433,20 +1363,24 @@ queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
                        }
 
                        /* find responder */
-                       // dnslog("queryns got reply from %I", srcip);
+                       if(debug)
+                               dnslog("queryns got reply from %I", srcip);
                        for(p = qp->dest; p < qp->curdest; p++)
-                               if(memcmp(p->a, srcip, sizeof p->a) == 0)
+                               if(ipcmp(p->a, srcip) == 0)
                                        break;
+                       if(p >= qp->curdest){
+                               dnslog("response from %I but no destination", srcip);
+                               continue;
+                       }
 
                        /* remove all addrs of responding server from list */
                        for(np = qp->dest; np < qp->curdest; np++)
                                if(np->s == p->s)
-                                       p->nx = Maxtrans;
+                                       np->nx = Maxtrans;
 
                        /* free or incorporate RRs in m */
-                       rv = procansw(qp, &m, srcip, depth, p);
+                       rv = procansw(qp, &m, depth, p);
                        if (rv > Answnone) {
-                               free(qp->dest);
                                qp->dest = qp->curdest = nil; /* prevent accidents */
                                return rv;
                        }
@@ -1455,60 +1389,17 @@ queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
 
        /* if all servers returned failure, propagate it */
        qp->dp->respcode = Rserver;
-       for(p = dest; p < qp->curdest; p++) {
-               destck(p);
+       for(p = dest; p < qp->curdest; p++)
                if(p->code != Rserver)
                        qp->dp->respcode = Rok;
-               p->magic = 0;                   /* prevent accidents */
-       }
 
 //     if (qp->dp->respcode)
 //             dnslog("queryns setting Rserver for %s", qp->dp->name);
 
-       free(qp->dest);
        qp->dest = qp->curdest = nil;           /* prevent accidents */
        return Answnone;
 }
 
-/*
- *  run a command with a supplied fd as standard input
- */
-char *
-system(int fd, char *cmd)
-{
-       int pid, p, i;
-       static Waitmsg msg;
-
-       if((pid = fork()) == -1)
-               sysfatal("fork failed: %r");
-       else if(pid == 0){
-               dup(fd, 0);
-               close(fd);
-               for (i = 3; i < 200; i++)
-                       close(i);               /* don't leak fds */
-               execl("/bin/rc", "rc", "-c", cmd, nil);
-               sysfatal("exec rc: %r");
-       }
-       for(p = waitpid(); p >= 0; p = waitpid())
-               if(p == pid)
-                       return msg.msg;
-       return "lost child";
-}
-
-/* compute wait, weighted by probability of success, with bounds */
-static ulong
-weight(ulong ms, unsigned pcntprob)
-{
-       ulong wait;
-
-       wait = (ms * pcntprob) / 100;
-       if (wait < Minwaitms)
-               wait = Minwaitms;
-       if (wait > Maxwaitms)
-               wait = Maxwaitms;
-       return wait;
-}
-
 /*
  * in principle we could use a single descriptor for a udp port
  * to send all queries and receive all the answers to them,
@@ -1518,67 +1409,25 @@ static int
 udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
 {
        int fd, rv;
-       long now;
-       ulong pcntprob;
-       uvlong wait, reqtm;
-       char *msg;
        uchar *obuf, *ibuf;
-       static QLock mntlck;
-       static ulong lastmount;
 
        /* use alloced buffers rather than ones from the stack */
        ibuf = emalloc(64*1024);                /* max. tcp reply size */
        obuf = emalloc(Maxudp+Udphdrsize);
 
        fd = udpport(mntpt);
-       while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
-               /* HACK: remount /net.alt */
-               now = time(nil);
-               if (now < lastmount + Remntretry)
-                       sleep(S2MS(lastmount + Remntretry - now));
-               qlock(&mntlck);
-               fd = udpport(mntpt);    /* try again under lock */
-               if (fd < 0) {
-                       dnslog("[%d] remounting /net.alt", getpid());
-                       unmount(nil, "/net.alt");
-
-                       msg = system(open("/dev/null", ORDWR), "outside");
-
-                       lastmount = time(nil);
-                       if (msg && *msg) {
-                               dnslog("[%d] can't remount /net.alt: %s",
-                                       getpid(), msg);
-                               sleep(10*1000); /* don't spin remounting */
-                       } else
-                               fd = udpport(mntpt);
-               }
-               qunlock(&mntlck);
-       }
        if (fd < 0) {
                dnslog("can't get udpport for %s query of name %s: %r",
                        mntpt, qp->dp->name);
-               sysfatal("out of udp conversations");   /* we're buggered */
+               rv = -1;
+               goto Out;
        }
-
-       /*
-        * Our QIP servers are busted and respond to AAAA and CNAME queries
-        * with (sometimes malformed [too short] packets and) no answers and
-        * just NS RRs but not Rname errors.  so make time-to-wait
-        * proportional to estimated probability of an RR of that type existing.
-        */
-       if (qp->type >= nelem(likely))
-               pcntprob = 35;                  /* unpopular query type */
-       else
-               pcntprob = likely[qp->type];
-       reqtm = (patient? 2 * Maxreqtm: Maxreqtm);
-       wait = weight(reqtm / 3, pcntprob);     /* time for one udp query */
-       qp->req->aborttime = timems() + 3*wait; /* for all udp queries */
-
        qp->udpfd = fd;
-       rv = queryns(qp, depth, ibuf, obuf, wait, inns);
-       close(fd);
+       rv = queryns(qp, depth, ibuf, obuf, 500UL<<(patient != 0), inns);
        qp->udpfd = -1;
+       close(fd);
 
+Out:
        free(obuf);
        free(ibuf);
        return rv;
@@ -1591,12 +1440,8 @@ udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
 static int
 netquery(Query *qp, int depth)
 {
-       int lock, rv, triedin, inname;
-       char buf[32];
+       int rv, triedin, inname;
        RR *rp;
-       DN *dp;
-       Querylck *qlp;
-       static int whined;
 
        rv = Answnone;                  /* pessimism */
        if(depth > 12)                  /* in a recursive loop? */
@@ -1613,39 +1458,7 @@ netquery(Query *qp, int depth)
        if(!qp->req->isslave && strcmp(qp->req->from, "9p") == 0)
                return Answnone;
 
-       /*
-        * don't lock before call to slave so only children can block.
-        * just lock at top-level invocation.
-        */
-       lock = depth <= 1 && qp->req->isslave;
-       dp = qp->dp;            /* ensure that it doesn't change underfoot */
-       qlp = nil;
-       if(lock) {
-               procsetname("query lock wait: %s %s from %s", dp->name,
-                       rrname(qp->type, buf, sizeof buf), qp->req->from);
-               /*
-                * don't make concurrent queries for this name.
-                * dozens of processes blocking here probably indicates
-                * an error in our dns data that causes us to not
-                * recognise a zone (area) as one of our own, thus
-                * causing us to query other nameservers.
-                */
-               qlp = &dp->querylck[qtype2lck(qp->type)];
-               qlock(qlp);
-               if (qlp->Ref.ref > Maxoutstanding) {
-                       qunlock(qlp);
-                       if (!whined) {
-                               whined = 1;
-                               dnslog("too many outstanding queries for %s;"
-                                       " dropping this one; no further logging"
-                                       " of drops", dp->name);
-                       }
-                       return Answnone;
-               }
-               ++qlp->Ref.ref;
-               qunlock(qlp);
-       }
-       procsetname("netquery: %s", dp->name);
+       procsetname("netquery: %s", qp->dp->name);
 
        /* prepare server RR's for incremental lookup */
        for(rp = qp->nsrp; rp; rp = rp->next)
@@ -1659,7 +1472,7 @@ netquery(Query *qp, int depth)
         * for inside addresses and /net.alt for outside addresses,
         * thus bypassing other inside nameservers.
         */
-       inname = insideaddr(dp->name);
+       inname = insideaddr(qp->dp->name);
        if (!cfg.straddle || inname) {
                rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
                triedin = 1;
@@ -1673,7 +1486,7 @@ netquery(Query *qp, int depth)
                if (triedin)
                        dnslog(
           "[%d] netquery: internal nameservers failed for %s; trying external",
-                               getpid(), dp->name);
+                               getpid(), qp->dp->name);
 
                /* prepare server RR's for incremental lookup */
                for(rp = qp->nsrp; rp; rp = rp->next)
@@ -1681,15 +1494,7 @@ netquery(Query *qp, int depth)
 
                rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
        }
-//     if (rv == Answnone)             /* could ask /net.alt/dns directly */
-//             askoutdns(dp, qp->type);
-
-       if(lock && qlp) {
-               qlock(qlp);
-               assert(qlp->Ref.ref > 0);
-               qunlock(qlp);
-               decref(qlp);
-       }
+
        return rv;
 }
 
@@ -1699,24 +1504,18 @@ seerootns(void)
        int rv;
        char root[] = "";
        Request req;
-       RR *rr;
-       Query *qp;
+       RR *rr, *nsrp;
+       Query q;
 
        memset(&req, 0, sizeof req);
        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);
-       for (rr = qp->nsrp; rr != nil; rr = rr->next)   /* DEBUG */
+       queryinit(&q, dnlookup(root, Cin, 1), Tns, &req);
+       nsrp = randomize(dblookup(root, Cin, Tns, 0, 0));
+       for (rr = nsrp; rr != nil; rr = rr->next)
                dnslog("seerootns query nsrp: %R", rr);
-
-       rv = netquery(qp, 0);           /* lookup ". ns" using qp->nsrp */
-
-       rrfreelist(qp->nsrp);
-       querydestroy(qp);
-       free(qp);
+       rv = netqueryns(&q, 0, nsrp);           /* lookup ". ns" using nsrp */
+       querydestroy(&q);
        return rv;
 }