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