]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dnresolve.c
bc2614c69b30a67c72ddea598692d88e7b139880
[plan9front.git] / sys / src / cmd / ndb / dnresolve.c
1 /*
2  * domain name resolvers, see rfcs 1035 and 1123
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <ip.h>
7 #include <bio.h>
8 #include <ndb.h>
9 #include "dns.h"
10
11 typedef struct Dest Dest;
12 typedef struct Query Query;
13
14 enum
15 {
16         Udp, Tcp,
17
18         Answerr=        -1,
19         Answnone,
20
21         Maxdest=        32,     /* maximum destinations for a request message */
22         Maxoutstanding= 15,     /* max. outstanding queries per domain name */
23
24         /*
25          * these are the old values; we're trying longer timeouts now
26          * primarily for the benefit of remote nameservers querying us
27          * during times of bad connectivity.
28          */
29         Maxtrans=       5,      /* maximum transmissions to a server */
30         Maxretries=     10,     /* cname+actual resends: was 32; have pity on user */
31 };
32 enum { Hurry, Patient, };
33 enum { Outns, Inns, };
34
35 struct Dest
36 {
37         uchar   a[IPaddrlen];   /* ip address */
38         DN      *s;             /* name server name */
39         RR      *n;             /* name server rr */
40         int     nx;             /* number of transmissions */
41         int     code;           /* response code; used to clear dp->respcode */
42 };
43
44 struct Query {
45         DN      *dp;            /* domain */
46         ushort  type;           /* and type to look up */
47         Request *req;
48         Query   *prev;          /* previous query */
49
50         RR      *nsrp;          /* name servers to consult */
51
52         Dest    *dest;          /* array of destinations */
53         Dest    *curdest;       /* pointer to next to fill */
54         int     ndest;          /* transmit to this many on this round */
55
56         int     udpfd;
57
58         int     tcpset;
59         int     tcpfd;          /* if Tcp, read replies from here */
60         int     tcpctlfd;
61         uchar   tcpip[IPaddrlen];
62 };
63
64 static RR*      dnresolve1(char*, int, int, Request*, int, int);
65 static int      netquery(Query *, int);
66
67 /*
68  * reading /proc/pid/args yields either "name args" or "name [display args]",
69  * so return only display args, if any.
70  */
71 static char *
72 procgetname(void)
73 {
74         int fd, n;
75         char *lp, *rp;
76         char buf[256];
77
78         snprint(buf, sizeof buf, "#p/%d/args", getpid());
79         if((fd = open(buf, OREAD)) < 0)
80                 return strdup("");
81         *buf = '\0';
82         n = read(fd, buf, sizeof buf-1);
83         close(fd);
84         if (n >= 0)
85                 buf[n] = '\0';
86         if ((lp = strchr(buf, '[')) == nil ||
87             (rp = strrchr(buf, ']')) == nil)
88                 return strdup("");
89         *rp = '\0';
90         return strdup(lp+1);
91 }
92
93 void
94 rrfreelistptr(RR **rpp)
95 {
96         RR *rp;
97
98         if (rpp == nil || *rpp == nil)
99                 return;
100         rp = *rpp;
101         *rpp = nil;     /* update pointer in memory before freeing list */
102         rrfreelist(rp);
103 }
104
105 /*
106  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
107  *  looking it up as a canonical name.
108  *
109  *  this process can be quite slow if time-outs are set too high when querying
110  *  nameservers that just don't respond to certain query types.  in that case,
111  *  there will be multiple udp retries, multiple nameservers will be queried,
112  *  and this will be repeated for a cname query.  the whole thing will be
113  *  retried several times until we get an answer or a time-out.
114  */
115 RR*
116 dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
117         int recurse, int rooted, int *status)
118 {
119         RR *rp, *nrp, *drp;
120         DN *dp;
121         int loops;
122         char *procname;
123         char nname[Domlen];
124
125         if(status)
126                 *status = 0;
127
128         if(depth > 12)                  /* in a recursive loop? */
129                 return nil;
130
131         procname = procgetname();
132         /*
133          *  hack for systems that don't have resolve search
134          *  lists.  Just look up the simple name in the database.
135          */
136         if(!rooted && strchr(name, '.') == nil){
137                 rp = nil;
138                 drp = domainlist(class);
139                 for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){
140                         snprint(nname, sizeof nname, "%s.%s", name,
141                                 nrp->ptr->name);
142                         rp = dnresolve(nname, class, type, req, cn, depth+1,
143                                 recurse, rooted, status);
144                         rrfreelist(rrremneg(&rp));
145                 }
146                 if(drp != nil)
147                         rrfreelist(drp);
148                 procsetname(procname);
149                 free(procname);
150                 return rp;
151         }
152
153         /*
154          *  try the name directly
155          */
156         rp = dnresolve1(name, class, type, req, depth, recurse);
157         if(rp == nil && (dp = idnlookup(name, class, 0)) != nil) {
158                 /*
159                  * try it as a canonical name if we weren't told
160                  * that the name didn't exist
161                  */
162                 if(type != Tptr && dp->respcode != Rname)
163                         for(loops = 0; rp == nil && loops < Maxretries; loops++){
164                                 /* retry cname, then the actual type */
165                                 rp = dnresolve1(name, class, Tcname, req,
166                                         depth, recurse);
167                                 if(rp == nil)
168                                         break;
169
170                                 /* rp->host == nil shouldn't happen, but does */
171                                 if(rp->negative || rp->host == nil){
172                                         rrfreelist(rp);
173                                         rp = nil;
174                                         break;
175                                 }
176
177                                 name = rp->host->name;
178                                 if(cn)
179                                         rrcat(cn, rp);
180                                 else
181                                         rrfreelist(rp);
182
183                                 rp = dnresolve1(name, class, type, req,
184                                         depth, recurse);
185                         }
186
187                 /* distinction between not found and not good */
188                 if(rp == nil && status != nil && dp->respcode != Rok)
189                         *status = dp->respcode;
190         }
191         procsetname(procname);
192         free(procname);
193         return randomize(rp);
194 }
195
196 static void
197 queryinit(Query *qp, DN *dp, int type, Request *req)
198 {
199         assert(dp && dp->magic == DNmagic);
200
201         memset(qp, 0, sizeof *qp);
202         qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
203         qp->dp = dp;
204         qp->type = type;
205         if (qp->type != type)
206                 dnslog("queryinit: bogus type %d", type);
207         qp->nsrp = nil;
208         qp->dest = qp->curdest = nil;
209         qp->prev = req->aux;
210         qp->req = req;
211         req->aux = qp;
212 }
213
214 static void
215 querydestroy(Query *qp)
216 {
217         if(qp->req->aux == qp)
218                 qp->req->aux = qp->prev;
219         /* leave udpfd open */
220         if (qp->tcpfd >= 0)
221                 close(qp->tcpfd);
222         if (qp->tcpctlfd >= 0) {
223                 hangup(qp->tcpctlfd);
224                 close(qp->tcpctlfd);
225         }
226         memset(qp, 0, sizeof *qp);      /* prevent accidents */
227         qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
228 }
229
230 /*
231  * if the response to a query hasn't arrived within 100 ms.,
232  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
233  * queries for missing RRs are likely to produce time-outs rather than
234  * negative responses, so cname and aaaa queries are likely to time out,
235  * thus we don't wait very long for them.
236  */
237 static void
238 notestats(vlong start, int tmout, int type)
239 {
240         qlock(&stats);
241         if (tmout) {
242                 stats.tmout++;
243                 if (type == Taaaa)
244                         stats.tmoutv6++;
245                 else if (type == Tcname)
246                         stats.tmoutcname++;
247         } else {
248                 long wait10ths = NS2MS(nsec() - start) / 100;
249
250                 if (wait10ths <= 0)
251                         stats.under10ths[0]++;
252                 else if (wait10ths >= nelem(stats.under10ths))
253                         stats.under10ths[nelem(stats.under10ths) - 1]++;
254                 else
255                         stats.under10ths[wait10ths]++;
256         }
257         qunlock(&stats);
258 }
259
260 static void
261 noteinmem(void)
262 {
263         qlock(&stats);
264         stats.answinmem++;
265         qunlock(&stats);
266 }
267
268 /* netquery with given name servers, free ns rrs when done */
269 static int
270 netqueryns(Query *qp, int depth, RR *nsrp)
271 {
272         int rv;
273
274         if(nsrp == nil)
275                 return Answnone;
276         qp->nsrp = nsrp;
277         rv = netquery(qp, depth);
278         qp->nsrp = nil;         /* prevent accidents */
279         rrfreelist(nsrp);
280         return rv;
281 }
282
283 static RR*
284 issuequery(Query *qp, char *name, int class, int depth, int recurse)
285 {
286         char *cp;
287         DN *nsdp;
288         RR *rp, *nsrp, *dbnsrp;
289
290         /*
291          *  if we're running as just a resolver, query our
292          *  designated name servers
293          */
294         if(cfg.resolver){
295                 nsrp = randomize(getdnsservers(class));
296                 if(nsrp != nil)
297                         if(netqueryns(qp, depth+1, nsrp) > Answnone)
298                                 return rrlookup(qp->dp, qp->type, OKneg);
299         }
300
301         /*
302          *  walk up the domain name looking for
303          *  a name server for the domain.
304          */
305         for(cp = name; cp; cp = walkup(cp)){
306                 /*
307                  *  if this is a local (served by us) domain,
308                  *  return answer
309                  */
310                 dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
311                 if(dbnsrp && dbnsrp->local){
312                         rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
313                         rrfreelist(dbnsrp);
314                         return rp;
315                 }
316
317                 /*
318                  *  if recursion isn't set, just accept local
319                  *  entries
320                  */
321                 if(recurse == Dontrecurse){
322                         if(dbnsrp)
323                                 rrfreelist(dbnsrp);
324                         continue;
325                 }
326
327                 /* look for ns in cache */
328                 nsdp = idnlookup(cp, class, 0);
329                 nsrp = nil;
330                 if(nsdp)
331                         nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
332
333                 /* if the entry timed out, ignore it */
334                 if(nsrp && !nsrp->db && (long)(nsrp->expire - now) <= 0)
335                         rrfreelistptr(&nsrp);
336
337                 if(nsrp){
338                         rrfreelistptr(&dbnsrp);
339
340                         /* query the name servers found in cache */
341                         if(netqueryns(qp, depth+1, nsrp) > Answnone)
342                                 return rrlookup(qp->dp, qp->type, OKneg);
343                 } else if(dbnsrp)
344                         /* try the name servers found in db */
345                         if(netqueryns(qp, depth+1, dbnsrp) > Answnone)
346                                 return rrlookup(qp->dp, qp->type, NOneg);
347         }
348         return nil;
349 }
350
351 static RR*
352 dnresolve1(char *name, int class, int type, Request *req, int depth,
353         int recurse)
354 {
355         Area *area;
356         DN *dp;
357         RR *rp;
358         Query q;
359
360         if(debug)
361                 dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
362
363         /* only class Cin implemented so far */
364         if(class != Cin)
365                 return nil;
366
367         dp = idnlookup(name, class, 1);
368
369         /*
370          *  Try the cache first
371          */
372         rp = rrlookup(dp, type, OKneg);
373         if(rp)
374                 if(rp->db){
375                         /* unauthoritative db entries are hints */
376                         if(rp->auth) {
377                                 noteinmem();
378                                 if(debug)
379                                         dnslog("[%d] dnresolve1 %s %d %d: auth rr in db",
380                                                 getpid(), name, type, class);
381                                 return rp;
382                         }
383                 } else
384                         /* cached entry must still be valid */
385                         if((long)(rp->expire - now) > 0)
386                                 /* but Tall entries are special */
387                                 if(type != Tall || rp->query == Tall) {
388                                         noteinmem();
389                                         if(debug)
390                                                 dnslog("[%d] dnresolve1 %s %d %d: rr not in db",
391                                                         getpid(), name, type, class);
392                                         return rp;
393                                 }
394         rrfreelist(rp);
395         rp = nil;               /* accident prevention */
396         USED(rp);
397
398         /*
399          * try the cache for a canonical name. if found punt
400          * since we'll find it during the canonical name search
401          * in dnresolve().
402          */
403         if(type != Tcname){
404                 rp = rrlookup(dp, Tcname, NOneg);
405                 rrfreelist(rp);
406                 if(rp){
407                         if(debug)
408                                 dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname",
409                                         getpid(), name, type, class);
410                         return nil;
411                 }
412         }
413
414         /*
415          * if the domain name is within an area of ours,
416          * we should have found its data in memory by now.
417          */
418         area = inmyarea(dp->name);
419         if (area || strncmp(dp->name, "local#", 6) == 0)
420                 return nil;
421
422         queryinit(&q, dp, type, req);
423         rp = issuequery(&q, name, class, depth, recurse);
424         querydestroy(&q);
425
426         if(rp){
427                 if(debug)
428                         dnslog("[%d] dnresolve1 %s %d %d: rr from query",
429                                 getpid(), name, type, class);
430                 return rp;
431         }
432
433         /* settle for a non-authoritative answer */
434         rp = rrlookup(dp, type, OKneg);
435         if(rp){
436                 if(debug)
437                         dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup",
438                                 getpid(), name, type, class);
439                 return rp;
440         }
441
442         /* noone answered.  try the database, we might have a chance. */
443         rp = dblookup(name, class, type, 0, 0);
444         if (rp) {
445                 if(debug)
446                         dnslog("[%d] dnresolve1 %s %d %d: rr from dblookup",
447                                 getpid(), name, type, class);
448         }else{
449                 if(debug)
450                         dnslog("[%d] dnresolve1 %s %d %d: no rr from dblookup; crapped out",
451                                 getpid(), name, type, class);
452         }
453         return rp;
454 }
455
456 /*
457  *  walk a domain name one element to the right.
458  *  return a pointer to that element.
459  *  in other words, return a pointer to the parent domain name.
460  */
461 char*
462 walkup(char *name)
463 {
464         char *cp;
465
466         cp = strchr(name, '.');
467         if(cp)
468                 return cp+1;
469         else if(*name)
470                 return "";
471         else
472                 return 0;
473 }
474
475 /*
476  *  Get a udp port for sending requests and reading replies.  Put the port
477  *  into "headers" mode.
478  */
479 static char *hmsg = "headers";
480
481 int
482 udpport(char *mtpt)
483 {
484         int fd, ctl;
485         char ds[64], adir[64];
486
487         /* get a udp port */
488         snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt && *mtpt) ? mtpt : "/net");
489         ctl = announce(ds, adir);
490         if(ctl < 0){
491                 /* warning("can't get udp port"); */
492                 return -1;
493         }
494
495         /* turn on header style interface */
496         if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){
497                 close(ctl);
498                 warning(hmsg);
499                 return -1;
500         }
501
502         /* grab the data file */
503         snprint(ds, sizeof ds, "%s/data", adir);
504         fd = open(ds, ORDWR);
505         close(ctl);
506         if(fd < 0)
507                 warning("can't open udp port %s: %r", ds);
508         return fd;
509 }
510
511 void
512 initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno)
513 {
514         memset(mp, 0, sizeof *mp);
515         mp->flags = flags;
516         mp->id = reqno;
517         mp->qd = rp;
518         if(rp != nil)
519                 mp->qdcount = 1;
520 }
521
522 /* generate a DNS UDP query packet */
523 int
524 mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
525 {
526         DNSmsg m;
527         int len;
528         Udphdr *uh = (Udphdr*)buf;
529         RR *rp;
530
531         /* stuff port number into output buffer */
532         memset(uh, 0, sizeof *uh);
533         hnputs(uh->rport, 53);
534
535         /* make request and convert it to output format */
536         rp = rralloc(type);
537         rp->owner = dp;
538         initdnsmsg(&m, rp, flags, reqno);
539         len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
540         rrfreelist(rp);
541         return len;
542 }
543
544 void
545 freeanswers(DNSmsg *mp)
546 {
547         rrfreelistptr(&mp->qd);
548         rrfreelistptr(&mp->an);
549         rrfreelistptr(&mp->ns);
550         rrfreelistptr(&mp->ar);
551         mp->qdcount = mp->ancount = mp->nscount = mp->arcount = 0;
552 }
553
554 /* timed read of reply.  sets srcip.  ibuf must be 64K to handle tcp answers. */
555 static int
556 readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp,
557         uchar *srcip)
558 {
559         int len, fd;
560         long ms;
561         vlong startns = nsec();
562         uchar *reply;
563         uchar lenbuf[2];
564
565         len = -1;                       /* pessimism */
566         *replyp = nil;
567         memset(srcip, 0, IPaddrlen);
568         ms = endms - NS2MS(startns);
569         if (ms <= 0)
570                 return -1;              /* taking too long */
571
572         reply = ibuf;
573         alarm(ms);
574         if (medium == Udp)
575                 if (qp->udpfd < 0)
576                         dnslog("readnet: qp->udpfd closed");
577                 else {
578                         len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
579                         alarm(0);
580                         notestats(startns, len < 0, qp->type);
581                         if (len >= IPaddrlen)
582                                 memmove(srcip, ibuf, IPaddrlen);
583                         if (len >= Udphdrsize) {
584                                 len   -= Udphdrsize;
585                                 reply += Udphdrsize;
586                         }
587                 }
588         else {
589                 if (!qp->tcpset)
590                         dnslog("readnet: tcp params not set");
591                 fd = qp->tcpfd;
592                 if (fd < 0)
593                         dnslog("readnet: %s: tcp fd unset for dest %I",
594                                 qp->dp->name, qp->tcpip);
595                 else if (readn(fd, lenbuf, 2) != 2) {
596                         dnslog("readnet: short read of 2-byte tcp msg size from %I",
597                                 qp->tcpip);
598                         /* probably a time-out */
599                         notestats(startns, 1, qp->type);
600                 } else {
601                         len = lenbuf[0]<<8 | lenbuf[1];
602                         if (readn(fd, ibuf, len) != len) {
603                                 dnslog("readnet: short read of tcp data from %I",
604                                         qp->tcpip);
605                                 /* probably a time-out */
606                                 notestats(startns, 1, qp->type);
607                                 len = -1;
608                         }
609                 }
610                 memmove(srcip, qp->tcpip, IPaddrlen);
611         }
612         alarm(0);
613         *replyp = reply;
614         return len;
615 }
616
617 /*
618  *  read replies to a request and remember the rrs in the answer(s).
619  *  ignore any of the wrong type.
620  *  wait at most until endms.
621  */
622 static int
623 readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
624         uvlong endms)
625 {
626         int len;
627         char *err;
628         char tbuf[32];
629         uchar *reply;
630         uchar srcip[IPaddrlen];
631         RR *rp;
632
633         for (; timems() < endms &&
634             (len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0;
635             freeanswers(mp)){
636                 /* convert into internal format  */
637                 memset(mp, 0, sizeof *mp);
638                 err = convM2DNS(reply, len, mp, nil);
639                 if (mp->flags & Ftrunc) {
640                         free(err);
641                         freeanswers(mp);
642                         /* notify our caller to retry the query via tcp. */
643                         return -1;
644                 } else if(err){
645                         dnslog("readreply: %s: input err, len %d: %s: %I",
646                                 qp->dp->name, len, err, srcip);
647                         free(err);
648                         continue;
649                 }
650                 if(debug)
651                         logreply(qp->req->id, srcip, mp);
652
653                 /* answering the right question? */
654                 if(mp->id != req)
655                         dnslog("%d: id %d instead of %d: %I", qp->req->id,
656                                 mp->id, req, srcip);
657                 else if(mp->qd == 0)
658                         dnslog("%d: no question RR: %I", qp->req->id, srcip);
659                 else if(mp->qd->owner != qp->dp)
660                         dnslog("%d: owner %s instead of %s: %I", qp->req->id,
661                                 mp->qd->owner->name, qp->dp->name, srcip);
662                 else if(mp->qd->type != qp->type)
663                         dnslog("%d: qp->type %d instead of %d: %I",
664                                 qp->req->id, mp->qd->type, qp->type, srcip);
665                 else {
666                         /* remember what request this is in answer to */
667                         for(rp = mp->an; rp; rp = rp->next)
668                                 rp->query = qp->type;
669                         return 0;
670                 }
671         }
672         if (timems() >= endms) {
673                 ;                               /* query expired */
674         } else if (0) {
675                 /* this happens routinely when a read times out */
676                 dnslog("readreply: %s type %s: ns %I read error or eof "
677                         "(returned %d): %r", qp->dp->name, rrname(qp->type,
678                         tbuf, sizeof tbuf), srcip, len);
679                 if (medium == Udp)
680                         for (rp = qp->nsrp; rp != nil; rp = rp->next)
681                                 if (rp->type == Tns)
682                                         dnslog("readreply: %s: query sent to "
683                                                 "ns %s", qp->dp->name,
684                                                 rp->host->name);
685         }
686         memset(mp, 0, sizeof *mp);
687         return -1;
688 }
689
690 /*
691  *      return non-0 if first list includes second list
692  */
693 int
694 contains(RR *rp1, RR *rp2)
695 {
696         RR *trp1, *trp2;
697
698         for(trp2 = rp2; trp2; trp2 = trp2->next){
699                 for(trp1 = rp1; trp1; trp1 = trp1->next)
700                         if(trp1->type == trp2->type)
701                         if(trp1->host == trp2->host)
702                         if(trp1->owner == trp2->owner)
703                                 break;
704                 if(trp1 == nil)
705                         return 0;
706         }
707         return 1;
708 }
709
710
711 /*
712  *  return multicast version if any
713  */
714 int
715 ipisbm(uchar *ip)
716 {
717         if(isv4(ip)){
718                 if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
719                     ipcmp(ip, IPv4bcast) == 0)
720                         return 4;
721         } else
722                 if(ip[0] == 0xff)
723                         return 6;
724         return 0;
725 }
726
727 static int
728 queryloops(Query *qp, RR *rp)
729 {
730         DN *ns = rp->host;
731
732         /*
733          *  looking up a server under itself
734          */
735         if(subsume(rp->owner->name, ns->name))
736                 return 1;
737
738         /*
739          *  cycle on name servers refering
740          *  to each another.
741          */
742         for(; qp; qp = qp->prev)
743                 if(qp->dp == ns)
744                         return 1;
745
746         return 0;
747 }
748
749 /*
750  *  Get next server type address(es) into qp->dest[nd] and beyond
751  */
752 static int
753 serveraddrs(Query *qp, int nd, int depth, int type)
754 {
755         RR *rp, *arp, *trp;
756         ulong mark;
757         Dest *p;
758
759         if(nd >= Maxdest)               /* dest array is full? */
760                 return Maxdest;
761
762         /*
763          *  look for a server whose address we already know.
764          *  if we find one, mark it so we ignore this on
765          *  subsequent passes.
766          */
767         mark = 1UL<<type;
768         arp = nil;
769         for(rp = qp->nsrp; rp; rp = rp->next){
770                 assert(rp->magic == RRmagic);
771                 if(rp->marker & mark)
772                         continue;
773                 arp = rrlookup(rp->host, type, NOneg);
774                 if(arp){
775                         rp->marker |= mark;
776                         break;
777                 }
778                 arp = dblookup(rp->host->name, Cin, type, 0, 0);
779                 if(arp){
780                         rp->marker |= mark;
781                         break;
782                 }
783         }
784
785         /*
786          *  if the cache and database lookup didn't find any new
787          *  server addresses, try resolving one via the network.
788          *  Mark any we try to resolve so we don't try a second time.
789          */
790         if(arp == nil){
791                 for(rp = qp->nsrp; rp; rp = rp->next)
792                         if((rp->marker & mark) == 0)
793                         if(queryloops(qp, rp))
794                                 /*
795                                  * give up as we should have got the address
796                                  * by higher up nameserver when recursing
797                                  * down, or will be queried when recursing up.
798                                  */
799                                 return nd;
800
801                 for(rp = qp->nsrp; rp; rp = rp->next){
802                         if(rp->marker & mark)
803                                 continue;
804                         rp->marker |= mark;
805                         arp = dnresolve(rp->host->name, Cin, type, qp->req, 0,
806                                 depth+1, Recurse, 1, 0);
807                         rrfreelist(rrremneg(&arp));
808                         if(arp)
809                                 break;
810                 }
811         }
812
813         /* use any addresses that we found */
814         for(trp = arp; trp && nd < Maxdest; trp = trp->next){
815                 p = &qp->dest[nd];
816                 memset(p, 0, sizeof *p);
817                 if(parseip(p->a, trp->ip->name) == -1)
818                         continue;
819
820                 /*
821                  * straddling servers can reject all nameservers if they are all
822                  * inside, so be sure to list at least one outside ns at
823                  * the end of the ns list in /lib/ndb for `dom='.
824                  */
825                 if (ipisbm(p->a) ||
826                     cfg.straddle && !insideaddr(qp->dp->name) && insidens(p->a))
827                         continue;
828                 p->nx = 0;
829                 p->n = nil;
830                 p->s = trp->owner;
831                 for(rp = qp->nsrp; rp; rp = rp->next){
832                         if(rp->host == p->s){
833                                 p->n = rp;
834                                 break;
835                         }
836                 }
837                 p->code = Rtimeout;
838                 nd++;
839         }
840         rrfreelist(arp);
841         return nd;
842 }
843
844 /*
845  *  cache negative responses
846  */
847 static void
848 cacheneg(DN *dp, int type, int rcode, RR *soarr)
849 {
850         RR *rp;
851         DN *soaowner;
852         ulong ttl;
853
854         qlock(&stats);
855         stats.negcached++;
856         qunlock(&stats);
857
858         /* no cache time specified, don't make anything up */
859         if(soarr != nil){
860                 if(soarr->next != nil)
861                         rrfreelistptr(&soarr->next);
862                 soaowner = soarr->owner;
863         } else
864                 soaowner = nil;
865
866         /* the attach can cause soarr to be freed so mine it now */
867         if(soarr != nil && soarr->soa != nil)
868                 ttl = soarr->soa->minttl;
869         else
870                 ttl = 5*Min;
871
872         /* add soa and negative RR to the database */
873         rrattach(soarr, Authoritative);
874
875         rp = rralloc(type);
876         rp->owner = dp;
877         rp->negative = 1;
878         rp->negsoaowner = soaowner;
879         rp->negrcode = rcode;
880         rp->ttl = ttl;
881         rrattach(rp, Authoritative);
882 }
883
884 static int
885 setdestoutns(Dest *p, int n)
886 {
887         memset(p, 0, sizeof *p);
888         if (outsidensip(n, p->a) < 0){
889                 if (n == 0)
890                         dnslog("[%d] no outside-ns in ndb", getpid());
891                 return -1;
892         }
893         p->s = dnlookup("outside-ns-ips", Cin, 1);
894         return 0;
895 }
896
897 /*
898  * issue query via UDP or TCP as appropriate.
899  * for TCP, returns with qp->tcpip set from udppkt header.
900  */
901 static int
902 mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
903 {
904         int rv, nfd;
905         char conndir[40], addr[128];
906         uchar belen[2];
907         NetConnInfo *nci;
908
909         rv = -1;
910         if (myip(udppkt))
911                 return rv;
912         switch (medium) {
913         case Udp:
914                 nfd = dup(qp->udpfd, -1);
915                 if (nfd < 0) {
916                         warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
917                         close(qp->udpfd);       /* ensure it's closed */
918                         qp->udpfd = -1;         /* poison it */
919                         break;
920                 }
921                 close(nfd);
922
923                 if (qp->udpfd < 0)
924                         dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
925                 else {
926                         if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
927                             len+Udphdrsize)
928                                 warning("sending udp msg: %r");
929                         else {
930                                 qlock(&stats);
931                                 stats.qsent++;
932                                 qunlock(&stats);
933                                 rv = 0;
934                         }
935                 }
936                 break;
937         case Tcp:
938                 /* send via TCP & keep fd around for reply */
939                 memmove(qp->tcpip, udppkt, sizeof qp->tcpip);
940                 snprint(addr, sizeof addr, "%s/tcp!%I!dns",
941                         (mntpt && *mntpt) ? mntpt : "/net", udppkt);
942                 alarm(10*1000);
943                 qp->tcpfd = dial(addr, nil, conndir, &qp->tcpctlfd);
944                 alarm(0);
945                 if (qp->tcpfd < 0) {
946                         dnslog("can't dial %s: %r", addr);
947                         break;
948                 }
949                 nci = getnetconninfo(conndir, qp->tcpfd);
950                 if (nci) {
951                         parseip(qp->tcpip, nci->rsys);
952                         freenetconninfo(nci);
953                 } else
954                         dnslog("mydnsquery: getnetconninfo failed");
955                 qp->tcpset = 1;
956
957                 belen[0] = len >> 8;
958                 belen[1] = len;
959                 if (write(qp->tcpfd, belen, 2) != 2 ||
960                     write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
961                         warning("sending tcp msg: %r");
962                 else
963                         rv = 0;
964                 break;
965         }
966         return rv;
967 }
968
969 /*
970  * send query to all UDP destinations or one TCP destination,
971  * taken from obuf (udp packet) header
972  */
973 static int
974 xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
975 {
976         int n;
977         char buf[32];
978         Dest *p;
979
980         if(timems() >= qp->req->aborttime)
981                 return -1;
982
983         /*
984          * if we send tcp query, we just take the dest ip address from
985          * the udp header placed there by tcpquery().
986          */
987         if (medium == Tcp) {
988                 procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
989                         qp->dp->name, rrname(qp->type, buf, sizeof buf));
990                 if(mydnsquery(qp, medium, obuf, len) < 0) /* sets qp->tcpip from obuf */
991                         return -1;
992                 if(debug)
993                         logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
994                                 qp->type);
995                 return 0;
996         }
997
998         /*
999          * get a nameserver address if we need one.
1000          * we're to transmit to more destinations than we currently have,
1001          * so get another.
1002          */
1003         p = qp->dest;
1004         n = qp->curdest - p;
1005         if (qp->ndest > n) {
1006                 /* populates qp->dest with v4 and v6 addresses. */
1007                 n = serveraddrs(qp, n, depth, Ta);
1008                 n = serveraddrs(qp, n, depth, Taaaa);
1009                 if (n == 0 && cfg.straddle && cfg.inside) {
1010                         /* get ips of "outside-ns-ips" */
1011                         while(n < Maxdest){
1012                                 if (setdestoutns(&qp->dest[n], n) < 0)
1013                                         break;
1014                                 n++;
1015                         }
1016                         if(n == 0)
1017                                 dnslog("xmitquery: %s: no outside-ns nameservers",
1018                                         qp->dp->name);
1019                 }
1020                 qp->curdest = &qp->dest[n];
1021         }
1022
1023         for(n = 0; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
1024                 /* skip destinations we've finished with */
1025                 if(p->nx >= Maxtrans)
1026                         continue;
1027                 /* exponential backoff of requests */
1028                 if((1<<p->nx) > qp->ndest)
1029                         continue;
1030
1031                 if(ipcmp(p->a, IPnoaddr) == 0)
1032                         continue;               /* mistake */
1033
1034                 procsetname("udp %sside query to %I/%s %s %s",
1035                         (inns? "in": "out"), p->a, p->s->name,
1036                         qp->dp->name, rrname(qp->type, buf, sizeof buf));
1037                 if(debug)
1038                         logsend(qp->req->id, depth, p->a, p->s->name,
1039                                 qp->dp->name, qp->type);
1040
1041                 /* fill in UDP destination addr & send it */
1042                 memmove(obuf, p->a, sizeof p->a);
1043                 if(mydnsquery(qp, medium, obuf, len) == 0)
1044                         n++;
1045                 p->nx++;
1046         }
1047
1048         return n == 0 ? -1 : 0;
1049 }
1050
1051 /* is mp a cachable negative response (with Rname set)? */
1052 static int
1053 isnegrname(DNSmsg *mp)
1054 {
1055         /* TODO: could add || cfg.justforw to RHS of && */
1056         return mp->an == nil && (mp->flags & Rmask) == Rname;
1057 }
1058
1059 static int
1060 filterhints(RR *rp, void *arg)
1061 {
1062         RR *nsrp;
1063
1064         if(rp->type != Ta && rp->type != Taaaa)
1065                 return 0;
1066
1067         for(nsrp = arg; nsrp; nsrp = nsrp->next)
1068                 if(nsrp->type == Tns && rp->owner == nsrp->host)
1069                         return 1;
1070
1071         return 0;
1072 }
1073
1074 static int
1075 filterauth(RR *rp, void *arg)
1076 {
1077         Dest *dest;
1078         RR *nsrp;
1079
1080         dest = arg;
1081         nsrp = dest->n;
1082         if(nsrp == nil)
1083                 return 0;
1084
1085         if(rp->type == Tsoa && rp->owner != nsrp->owner
1086         && !subsume(nsrp->owner->name, rp->owner->name)
1087         && strncmp(nsrp->owner->name, "local#", 6) != 0)
1088                 return 1;
1089
1090         if(rp->type != Tns)
1091                 return 0;
1092
1093         if(rp->owner != nsrp->owner
1094         && !subsume(nsrp->owner->name, rp->owner->name)
1095         && strncmp(nsrp->owner->name, "local#", 6) != 0)
1096                 return 1;
1097
1098         return baddelegation(rp, nsrp, dest->a);
1099 }
1100
1101 static void
1102 reportandfree(RR *l, char *note, Dest *p)
1103 {
1104         RR *rp;
1105
1106         while(rp = l){
1107                 l = l->next;
1108                 rp->next = nil;
1109                 if(debug)
1110                         dnslog("ignoring %s from %I/%s: %R",
1111                                 note, p->a, p->s->name, rp);
1112                 rrfree(rp);
1113         }
1114 }
1115
1116 /* returns Answerr (-1) on errors, else number of answers, which can be zero. */
1117 static int
1118 procansw(Query *qp, DNSmsg *mp, int depth, Dest *p)
1119 {
1120         int rv;
1121         char buf[32];
1122         DN *ndp;
1123         Query nq;
1124         RR *tp, *soarr;
1125
1126         if(mp->an == nil)
1127                 stats.negans++;
1128
1129         /* ignore any error replies */
1130         switch(mp->flags & Rmask){
1131         case Rrefused:
1132         case Rserver:
1133                 stats.negserver++;
1134                 freeanswers(mp);
1135                 p->code = Rserver;
1136                 return Answerr;
1137         }
1138
1139         /* ignore any bad delegations */
1140         if((tp = rrremfilter(&mp->ns, filterauth, p)) != 0)
1141                 reportandfree(tp, "bad delegation", p);
1142
1143         /* remove any soa's from the authority section */
1144         soarr = rrremtype(&mp->ns, Tsoa);
1145
1146         /* only nameservers remaining */
1147         if((tp = rrremtype(&mp->ns, Tns)) != 0){
1148                 reportandfree(mp->ns, "non-nameserver", p);
1149                 mp->ns = tp;
1150         }
1151
1152         /* remove answers not related to the question. */
1153         if((tp = rrremowner(&mp->an, qp->dp)) != 0){
1154                 reportandfree(mp->an, "wrong subject answer", p);
1155                 mp->an = tp;
1156         }
1157         if(qp->type != Tall){
1158                 if((tp = rrremtype(&mp->an, qp->type)) != 0){
1159                         reportandfree(mp->an, "wrong type answer", p);
1160                         mp->an = tp;
1161                 }
1162         }
1163
1164         /* incorporate answers */
1165         unique(mp->an);
1166         unique(mp->ns);
1167         unique(mp->ar);
1168
1169         if(mp->an){
1170                 /*
1171                  * only use cname answer when returned. some dns servers
1172                  * attach (potential) spam hint address records which poisons
1173                  * the cache.
1174                  */
1175                 if((tp = rrremtype(&mp->an, Tcname)) != 0){
1176                         reportandfree(mp->an, "ip in cname answer", p);
1177                         mp->an = tp;
1178                 }
1179                 rrattach(mp->an, (mp->flags & Fauth) != 0);
1180         }
1181         if(mp->ar){
1182                 /* restrict hints to address rr's for nameservers only */
1183                 if((tp = rrremfilter(&mp->ar, filterhints, mp->ns)) != 0){
1184                         reportandfree(mp->ar, "hint", p);
1185                         mp->ar = tp;
1186                 }
1187                 rrattach(mp->ar, Notauthoritative);
1188         }
1189         if(mp->ns && !cfg.justforw){
1190                 ndp = mp->ns->owner;
1191                 rrattach(mp->ns, Notauthoritative);
1192         } else {
1193                 ndp = nil;
1194                 rrfreelistptr(&mp->ns);
1195                 mp->nscount = 0;
1196         }
1197
1198         /* free the question */
1199         if(mp->qd) {
1200                 rrfreelistptr(&mp->qd);
1201                 mp->qdcount = 0;
1202         }
1203
1204         /*
1205          *  Any reply from an authoritative server
1206          *  that does not provide more nameservers,
1207          *  or a positive reply terminates the search.
1208          *  A negative response now also terminates the search.
1209          */
1210         if(mp->an || (mp->flags & Fauth) && mp->ns == nil){
1211                 if(isnegrname(mp))
1212                         qp->dp->respcode = Rname;
1213                 else
1214                         qp->dp->respcode = Rok;
1215
1216                 /*
1217                  *  cache any negative responses, free soarr.
1218                  *  negative responses need not be authoritative:
1219                  *  they can legitimately come from a cache.
1220                  */
1221                 if( /* (mp->flags & Fauth) && */ mp->an == nil)
1222                         cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1223                 else
1224                         rrfreelist(soarr);
1225                 return 1;
1226         } else if (isnegrname(mp)) {
1227                 qp->dp->respcode = Rname;
1228                 /*
1229                  *  cache negative response.
1230                  *  negative responses need not be authoritative:
1231                  *  they can legitimately come from a cache.
1232                  */
1233                 cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1234                 return 1;
1235         }
1236         stats.negnorname++;
1237         rrfreelist(soarr);
1238
1239         /*
1240          *  if we've been given better name servers, recurse.
1241          *  if we're a pure resolver, don't recurse, we have
1242          *  to forward to a fixed set of named servers.
1243          */
1244         if(mp->ns == nil || cfg.resolver && cfg.justforw)
1245                 return Answnone;
1246         tp = rrlookup(ndp, Tns, NOneg);
1247         if(contains(qp->nsrp, tp)){
1248                 rrfreelist(tp);
1249                 return Answnone;
1250         }
1251         procsetname("recursive query for %s %s", qp->dp->name,
1252                 rrname(qp->type, buf, sizeof buf));
1253
1254         queryinit(&nq, qp->dp, qp->type, qp->req);
1255         rv = netqueryns(&nq, depth+1, tp);
1256         querydestroy(&nq);
1257
1258         return rv;
1259 }
1260
1261 /*
1262  * send a query via tcp to a single address (from ibuf's udp header)
1263  * and read the answer(s) into mp->an.
1264  */
1265 static int
1266 tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1267         ulong waitms, int inns, ushort req)
1268 {
1269         int rv = 0;
1270         uvlong endms;
1271
1272         endms = timems() + waitms;
1273         if(endms > qp->req->aborttime)
1274                 endms = qp->req->aborttime;
1275
1276         if (0)
1277                 dnslog("%s: udp reply truncated; retrying query via tcp to %I",
1278                         qp->dp->name, qp->tcpip);
1279
1280         memmove(obuf, ibuf, IPaddrlen);         /* send back to respondent */
1281         memset(mp, 0, sizeof *mp);
1282         if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1283             readreply(qp, Tcp, req, ibuf, mp, endms) < 0)
1284                 rv = -1;
1285         if (qp->tcpfd >= 0) {
1286                 hangup(qp->tcpctlfd);
1287                 close(qp->tcpctlfd);
1288                 close(qp->tcpfd);
1289         }
1290         qp->tcpfd = qp->tcpctlfd = -1;
1291
1292         return rv;
1293 }
1294
1295 /*
1296  *  query name servers.  fill in obuf with on-the-wire representation of a
1297  *  DNSmsg derived from qp.  if the name server returns a pointer to another
1298  *  name server, recurse.
1299  */
1300 static int
1301 queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
1302 {
1303         int ndest, len, replywaits, rv, flag;
1304         ushort req;
1305         uvlong endms;
1306         char buf[32];
1307         uchar srcip[IPaddrlen];
1308         Dest *p, *np, dest[Maxdest];
1309
1310         req = rand();
1311
1312         /* request recursion only for local dns servers */
1313         flag = Oquery;
1314         if(strncmp(qp->nsrp->owner->name, "local#", 6) == 0)
1315                 flag |= Frecurse;
1316
1317         /* pack request into a udp message */
1318         len = mkreq(qp->dp, qp->type, obuf, flag, req);
1319
1320         /* no server addresses yet */
1321         memset(dest, 0, sizeof dest);
1322         qp->curdest = qp->dest = dest;
1323
1324         /*
1325          *  transmit udp requests and wait for answers.
1326          *  at most Maxtrans attempts to each address.
1327          *  each cycle send one more message than the previous.
1328          *  retry a query via tcp if its response is truncated.
1329          */
1330         for(ndest = 2; ndest < Maxdest; ndest += 2){
1331                 qp->ndest = ndest;
1332                 qp->tcpset = 0;
1333                 if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
1334                         break;
1335
1336                 endms = timems() + waitms;
1337                 if(endms > qp->req->aborttime)
1338                         endms = qp->req->aborttime;
1339
1340                 for(replywaits = 0; replywaits < ndest; replywaits++){
1341                         DNSmsg m;
1342
1343                         procsetname("reading %sside reply from %I: %s %s from %s",
1344                                 (inns? "in": "out"), obuf, qp->dp->name,
1345                                 rrname(qp->type, buf, sizeof buf), qp->req->from);
1346
1347                         /* read udp answer into m */
1348                         if (readreply(qp, Udp, req, ibuf, &m, endms) >= 0)
1349                                 memmove(srcip, ibuf, IPaddrlen);
1350                         else if (!(m.flags & Ftrunc)) {
1351                                 freeanswers(&m);
1352                                 break;          /* timed out on this dest */
1353                         } else {
1354                                 /* whoops, it was truncated! ask again via tcp */
1355                                 freeanswers(&m);
1356                                 rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1357                                         waitms, inns, req);  /* answer in m */
1358                                 if (rv < 0) {
1359                                         freeanswers(&m);
1360                                         break;          /* failed via tcp too */
1361                                 }
1362                                 memmove(srcip, qp->tcpip, IPaddrlen);
1363                         }
1364
1365                         /* find responder */
1366                         if(debug)
1367                                 dnslog("queryns got reply from %I", srcip);
1368                         for(p = qp->dest; p < qp->curdest; p++)
1369                                 if(ipcmp(p->a, srcip) == 0)
1370                                         break;
1371                         if(p >= qp->curdest){
1372                                 dnslog("response from %I but no destination", srcip);
1373                                 continue;
1374                         }
1375
1376                         /* remove all addrs of responding server from list */
1377                         for(np = qp->dest; np < qp->curdest; np++)
1378                                 if(np->s == p->s)
1379                                         np->nx = Maxtrans;
1380
1381                         /* free or incorporate RRs in m */
1382                         rv = procansw(qp, &m, depth, p);
1383                         if (rv > Answnone) {
1384                                 qp->dest = qp->curdest = nil; /* prevent accidents */
1385                                 return rv;
1386                         }
1387                 }
1388         }
1389
1390         /* if all servers returned failure, propagate it */
1391         qp->dp->respcode = Rserver;
1392         for(p = dest; p < qp->curdest; p++)
1393                 if(p->code != Rserver)
1394                         qp->dp->respcode = Rok;
1395
1396 //      if (qp->dp->respcode)
1397 //              dnslog("queryns setting Rserver for %s", qp->dp->name);
1398
1399         qp->dest = qp->curdest = nil;           /* prevent accidents */
1400         return Answnone;
1401 }
1402
1403 /*
1404  * in principle we could use a single descriptor for a udp port
1405  * to send all queries and receive all the answers to them,
1406  * but we'd have to sort out the answers by dns-query id.
1407  */
1408 static int
1409 udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
1410 {
1411         int fd, rv;
1412         uchar *obuf, *ibuf;
1413
1414         /* use alloced buffers rather than ones from the stack */
1415         ibuf = emalloc(64*1024);                /* max. tcp reply size */
1416         obuf = emalloc(Maxudp+Udphdrsize);
1417
1418         fd = udpport(mntpt);
1419         if (fd < 0) {
1420                 dnslog("can't get udpport for %s query of name %s: %r",
1421                         mntpt, qp->dp->name);
1422                 rv = -1;
1423                 goto Out;
1424         }
1425         qp->udpfd = fd;
1426         rv = queryns(qp, depth, ibuf, obuf, 500UL<<(patient != 0), inns);
1427         qp->udpfd = -1;
1428         close(fd);
1429
1430 Out:
1431         free(obuf);
1432         free(ibuf);
1433         return rv;
1434 }
1435
1436 /*
1437  * look up (qp->dp->name, qp->type) rr in dns,
1438  * using nameservers in qp->nsrp.
1439  */
1440 static int
1441 netquery(Query *qp, int depth)
1442 {
1443         int rv, triedin, inname;
1444         RR *rp;
1445
1446         rv = Answnone;                  /* pessimism */
1447         if(depth > 12)                  /* in a recursive loop? */
1448                 return Answnone;
1449
1450         slave(qp->req);
1451
1452         /*
1453          * slave might have forked.  if so, the parent process longjmped to
1454          * req->mret; we're usually the child slave, but if there are too
1455          * many children already, we're still the same process. under no
1456          * circumstances block the 9p loop.
1457          */
1458         if(!qp->req->isslave && strcmp(qp->req->from, "9p") == 0)
1459                 return Answnone;
1460
1461         procsetname("netquery: %s", qp->dp->name);
1462
1463         /* prepare server RR's for incremental lookup */
1464         for(rp = qp->nsrp; rp; rp = rp->next)
1465                 rp->marker = 0;
1466
1467         triedin = 0;
1468
1469         /*
1470          * normal resolvers and servers will just use mntpt for all addresses,
1471          * even on the outside.  straddling servers will use mntpt (/net)
1472          * for inside addresses and /net.alt for outside addresses,
1473          * thus bypassing other inside nameservers.
1474          */
1475         inname = insideaddr(qp->dp->name);
1476         if (!cfg.straddle || inname) {
1477                 rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
1478                 triedin = 1;
1479         }
1480
1481         /*
1482          * if we're still looking, are inside, and have an outside domain,
1483          * try it on our outside interface, if any.
1484          */
1485         if (rv == Answnone && cfg.inside && !inname) {
1486                 if (triedin)
1487                         dnslog(
1488            "[%d] netquery: internal nameservers failed for %s; trying external",
1489                                 getpid(), qp->dp->name);
1490
1491                 /* prepare server RR's for incremental lookup */
1492                 for(rp = qp->nsrp; rp; rp = rp->next)
1493                         rp->marker = 0;
1494
1495                 rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
1496         }
1497
1498         return rv;
1499 }
1500
1501 int
1502 seerootns(void)
1503 {
1504         int rv;
1505         char root[] = "";
1506         Request req;
1507         RR *rr, *nsrp;
1508         Query q;
1509
1510         memset(&req, 0, sizeof req);
1511         req.isslave = 1;
1512         req.aborttime = timems() + Maxreqtm;
1513         req.from = "internal";
1514         queryinit(&q, dnlookup(root, Cin, 1), Tns, &req);
1515         nsrp = randomize(dblookup(root, Cin, Tns, 0, 0));
1516         for (rr = nsrp; rr != nil; rr = rr->next)
1517                 dnslog("seerootns query nsrp: %R", rr);
1518         rv = netqueryns(&q, 0, nsrp);           /* lookup ". ns" using nsrp */
1519         querydestroy(&q);
1520         return rv;
1521 }