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