X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=sys%2Fsrc%2Fcmd%2Fndb%2Fdnresolve.c;h=bc2614c69b30a67c72ddea598692d88e7b139880;hb=2728e065895e7af2493ed7af3b8897caa416adf6;hp=6e8709e943aefcf6e4b75eaecb7cdd37daa2e05a;hpb=5f87d8dcc814700f10f40c10a0225400e4828ef9;p=plan9front.git diff --git a/sys/src/cmd/ndb/dnresolve.c b/sys/src/cmd/ndb/dnresolve.c index 6e8709e94..bc2614c69 100644 --- a/sys/src/cmd/ndb/dnresolve.c +++ b/sys/src/cmd/ndb/dnresolve.c @@ -18,24 +18,16 @@ enum Answerr= -1, Answnone, - Maxdest= 24, /* maximum destinations for a request message */ + Maxdest= 32, /* maximum destinations for a request message */ Maxoutstanding= 15, /* max. outstanding queries per domain name */ - Remntretry= 15, /* min. sec.s between /net.alt remount tries */ /* * these are the old values; we're trying longer timeouts now * primarily for the benefit of remote nameservers querying us * during times of bad connectivity. */ -// Maxtrans= 3, /* maximum transmissions to a server */ -// Maxretries= 3, /* cname+actual resends: was 32; have pity on user */ -// Maxwaitms= 1000, /* wait no longer for a remote dns query */ -// Minwaitms= 100, /* willing to wait for a remote dns query */ - Maxtrans= 5, /* maximum transmissions to a server */ - Maxretries= 5, /* cname+actual resends: was 32; have pity on user */ - Maxwaitms= 5000, /* wait no longer for a remote dns query */ - Minwaitms= 500, /* willing to wait for a remote dns query */ + Maxretries= 10, /* cname+actual resends: was 32; have pity on user */ }; enum { Hurry, Patient, }; enum { Outns, Inns, }; @@ -43,7 +35,8 @@ enum { Outns, Inns, }; struct Dest { uchar a[IPaddrlen]; /* ip address */ - DN *s; /* name server */ + DN *s; /* name server name */ + RR *n; /* name server rr */ int nx; /* number of transmissions */ int code; /* response code; used to clear dp->respcode */ }; @@ -52,9 +45,10 @@ struct Query { DN *dp; /* domain */ ushort type; /* and type to look up */ Request *req; + Query *prev; /* previous query */ + RR *nsrp; /* name servers to consult */ - /* dest must not be on the stack due to forking in slave() */ Dest *dest; /* array of destinations */ Dest *curdest; /* pointer to next to fill */ int ndest; /* transmit to this many on this round */ @@ -67,21 +61,6 @@ struct Query { uchar tcpip[IPaddrlen]; }; -/* estimated % probability of such a record existing at all */ -int likely[] = { - [Ta] 95, - [Taaaa] 10, - [Tcname] 15, - [Tmx] 60, - [Tns] 90, - [Tnull] 5, - [Tptr] 35, - [Tsoa] 90, - [Tsrv] 60, - [Ttxt] 15, - [Tall] 95, -}; - static RR* dnresolve1(char*, int, int, Request*, int, int); static int netquery(Query *, int); @@ -175,7 +154,7 @@ dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, * try the name directly */ rp = dnresolve1(name, class, type, req, depth, recurse); - if(rp == nil && (dp = dnlookup(name, class, 0)) != nil) { + if(rp == nil && (dp = idnlookup(name, class, 0)) != nil) { /* * try it as a canonical name if we weren't told * that the name didn't exist @@ -225,14 +204,18 @@ queryinit(Query *qp, DN *dp, int type, Request *req) qp->type = type; if (qp->type != type) dnslog("queryinit: bogus type %d", type); - qp->req = req; qp->nsrp = nil; qp->dest = qp->curdest = nil; + qp->prev = req->aux; + qp->req = req; + req->aux = qp; } static void querydestroy(Query *qp) { + if(qp->req->aux == qp) + qp->req->aux = qp->prev; /* leave udpfd open */ if (qp->tcpfd >= 0) close(qp->tcpfd); @@ -288,6 +271,8 @@ netqueryns(Query *qp, int depth, RR *nsrp) { int rv; + if(nsrp == nil) + return Answnone; qp->nsrp = nsrp; rv = netquery(qp, depth); qp->nsrp = nil; /* prevent accidents */ @@ -340,7 +325,7 @@ issuequery(Query *qp, char *name, int class, int depth, int recurse) } /* look for ns in cache */ - nsdp = dnlookup(cp, class, 0); + nsdp = idnlookup(cp, class, 0); nsrp = nil; if(nsdp) nsrp = randomize(rrlookup(nsdp, Tns, NOneg)); @@ -379,7 +364,7 @@ dnresolve1(char *name, int class, int type, Request *req, int depth, if(class != Cin) return nil; - dp = dnlookup(name, class, 1); + dp = idnlookup(name, class, 1); /* * Try the cache first @@ -739,13 +724,36 @@ ipisbm(uchar *ip) return 0; } +static int +queryloops(Query *qp, RR *rp) +{ + DN *ns = rp->host; + + /* + * looking up a server under itself + */ + if(subsume(rp->owner->name, ns->name)) + return 1; + + /* + * cycle on name servers refering + * to each another. + */ + for(; qp; qp = qp->prev) + if(qp->dp == ns) + return 1; + + return 0; +} + /* - * Get next server address(es) into qp->dest[nd] and beyond + * Get next server type address(es) into qp->dest[nd] and beyond */ static int -serveraddrs(Query *qp, int nd, int depth) +serveraddrs(Query *qp, int nd, int depth, int type) { RR *rp, *arp, *trp; + ulong mark; Dest *p; if(nd >= Maxdest) /* dest array is full? */ @@ -756,23 +764,20 @@ serveraddrs(Query *qp, int nd, int depth) * if we find one, mark it so we ignore this on * subsequent passes. */ - arp = 0; + mark = 1UL<nsrp; rp; rp = rp->next){ assert(rp->magic == RRmagic); - if(rp->marker) + if(rp->marker & mark) continue; - arp = rrlookup(rp->host, Ta, NOneg); - if(arp == nil) - arp = rrlookup(rp->host, Taaaa, NOneg); + arp = rrlookup(rp->host, type, NOneg); if(arp){ - rp->marker = 1; + rp->marker |= mark; break; } - arp = dblookup(rp->host->name, Cin, Ta, 0, 0); - if(arp == nil) - arp = dblookup(rp->host->name, Cin, Taaaa, 0, 0); + arp = dblookup(rp->host->name, Cin, type, 0, 0); if(arp){ - rp->marker = 1; + rp->marker |= mark; break; } } @@ -782,33 +787,36 @@ serveraddrs(Query *qp, int nd, int depth) * server addresses, try resolving one via the network. * Mark any we try to resolve so we don't try a second time. */ - if(arp == 0) - for(rp = qp->nsrp; rp; rp = rp->next){ - if(rp->marker) - continue; - rp->marker = 1; + if(arp == nil){ + for(rp = qp->nsrp; rp; rp = rp->next) + if((rp->marker & mark) == 0) + if(queryloops(qp, rp)) + /* + * give up as we should have got the address + * by higher up nameserver when recursing + * down, or will be queried when recursing up. + */ + return nd; - /* - * avoid loops looking up a server under itself - */ - if(subsume(rp->owner->name, rp->host->name)) + for(rp = qp->nsrp; rp; rp = rp->next){ + if(rp->marker & mark) continue; - - arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0, + rp->marker |= mark; + arp = dnresolve(rp->host->name, Cin, type, qp->req, 0, depth+1, Recurse, 1, 0); - if(arp == nil) - arp = dnresolve(rp->host->name, Cin, Taaaa, - qp->req, 0, depth+1, Recurse, 1, 0); rrfreelist(rrremneg(&arp)); if(arp) break; } + } /* use any addresses that we found */ for(trp = arp; trp && nd < Maxdest; trp = trp->next){ p = &qp->dest[nd]; memset(p, 0, sizeof *p); - parseip(p->a, trp->ip->name); + if(parseip(p->a, trp->ip->name) == -1) + continue; + /* * straddling servers can reject all nameservers if they are all * inside, so be sure to list at least one outside ns at @@ -818,7 +826,14 @@ serveraddrs(Query *qp, int nd, int depth) cfg.straddle && !insideaddr(qp->dp->name) && insidens(p->a)) continue; p->nx = 0; + p->n = nil; p->s = trp->owner; + for(rp = qp->nsrp; rp; rp = rp->next){ + if(rp->host == p->s){ + p->n = rp; + break; + } + } p->code = Rtimeout; nd++; } @@ -887,13 +902,12 @@ static int mydnsquery(Query *qp, int medium, uchar *udppkt, int len) { int rv, nfd; - char conndir[40], addr[128], domain[64]; + char conndir[40], addr[128]; uchar belen[2]; NetConnInfo *nci; rv = -1; - snprint(domain, sizeof(domain), "%I", udppkt); - if (myaddr(domain)) + if (myip(udppkt)) return rv; switch (medium) { case Udp: @@ -923,9 +937,8 @@ mydnsquery(Query *qp, int medium, uchar *udppkt, int len) case Tcp: /* send via TCP & keep fd around for reply */ memmove(qp->tcpip, udppkt, sizeof qp->tcpip); - snprint(addr, sizeof addr, "%s/tcp!%s!dns", - (mntpt && *mntpt) ? mntpt : "/net", - domain); + snprint(addr, sizeof addr, "%s/tcp!%I!dns", + (mntpt && *mntpt) ? mntpt : "/net", udppkt); alarm(10*1000); qp->tcpfd = dial(addr, nil, conndir, &qp->tcpctlfd); alarm(0); @@ -990,8 +1003,9 @@ xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len) p = qp->dest; n = qp->curdest - p; if (qp->ndest > n) { - n = serveraddrs(qp, n, depth); /* populates qp->dest. */ - assert(n >= 0 && n <= Maxdest); + /* populates qp->dest with v4 and v6 addresses. */ + n = serveraddrs(qp, n, depth, Ta); + n = serveraddrs(qp, n, depth, Taaaa); if (n == 0 && cfg.straddle && cfg.inside) { /* get ips of "outside-ns-ips" */ while(n < Maxdest){ @@ -1014,7 +1028,7 @@ xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len) if((1<nx) > qp->ndest) continue; - if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0) + if(ipcmp(p->a, IPnoaddr) == 0) continue; /* mistake */ procsetname("udp %sside query to %I/%s %s %s", @@ -1042,9 +1056,66 @@ isnegrname(DNSmsg *mp) return mp->an == nil && (mp->flags & Rmask) == Rname; } +static int +filterhints(RR *rp, void *arg) +{ + RR *nsrp; + + if(rp->type != Ta && rp->type != Taaaa) + return 0; + + for(nsrp = arg; nsrp; nsrp = nsrp->next) + if(nsrp->type == Tns && rp->owner == nsrp->host) + return 1; + + return 0; +} + +static int +filterauth(RR *rp, void *arg) +{ + Dest *dest; + RR *nsrp; + + dest = arg; + nsrp = dest->n; + if(nsrp == nil) + return 0; + + if(rp->type == Tsoa && rp->owner != nsrp->owner + && !subsume(nsrp->owner->name, rp->owner->name) + && strncmp(nsrp->owner->name, "local#", 6) != 0) + return 1; + + if(rp->type != Tns) + return 0; + + if(rp->owner != nsrp->owner + && !subsume(nsrp->owner->name, rp->owner->name) + && strncmp(nsrp->owner->name, "local#", 6) != 0) + return 1; + + return baddelegation(rp, nsrp, dest->a); +} + +static void +reportandfree(RR *l, char *note, Dest *p) +{ + RR *rp; + + while(rp = l){ + l = l->next; + rp->next = nil; + if(debug) + dnslog("ignoring %s from %I/%s: %R", + note, p->a, p->s->name, rp); + rrfree(rp); + } +} + /* returns Answerr (-1) on errors, else number of answers, which can be zero. */ static int -procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) +procansw(Query *qp, DNSmsg *mp, int depth, Dest *p) { int rv; char buf[32]; @@ -1052,37 +1123,44 @@ procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) Query nq; RR *tp, *soarr; - if (mp->an == nil) + if(mp->an == nil) stats.negans++; /* ignore any error replies */ - if((mp->flags & Rmask) == Rserver){ + switch(mp->flags & Rmask){ + case Rrefused: + case Rserver: stats.negserver++; freeanswers(mp); - if(p != nil) - p->code = Rserver; + p->code = Rserver; return Answerr; } /* ignore any bad delegations */ - if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){ - stats.negbaddeleg++; - if(mp->an == nil){ - stats.negbdnoans++; - freeanswers(mp); - if(p != nil) - p->code = Rserver; - dnslog(" and no answers"); - return Answerr; - } - dnslog(" but has answers; ignoring ns"); - rrfreelistptr(&mp->ns); - mp->nscount = 0; - } + if((tp = rrremfilter(&mp->ns, filterauth, p)) != 0) + reportandfree(tp, "bad delegation", p); /* remove any soa's from the authority section */ soarr = rrremtype(&mp->ns, Tsoa); + /* only nameservers remaining */ + if((tp = rrremtype(&mp->ns, Tns)) != 0){ + reportandfree(mp->ns, "non-nameserver", p); + mp->ns = tp; + } + + /* remove answers not related to the question. */ + if((tp = rrremowner(&mp->an, qp->dp)) != 0){ + reportandfree(mp->an, "wrong subject answer", p); + mp->an = tp; + } + if(qp->type != Tall){ + if((tp = rrremtype(&mp->an, qp->type)) != 0){ + reportandfree(mp->an, "wrong type answer", p); + mp->an = tp; + } + } + /* incorporate answers */ unique(mp->an); unique(mp->ns); @@ -1091,17 +1169,23 @@ procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) if(mp->an){ /* * only use cname answer when returned. some dns servers - * attach (potential) spam hint address records which poisons the cache. + * attach (potential) spam hint address records which poisons + * the cache. */ if((tp = rrremtype(&mp->an, Tcname)) != 0){ - if(mp->an) - rrfreelist(mp->an); + reportandfree(mp->an, "ip in cname answer", p); mp->an = tp; } rrattach(mp->an, (mp->flags & Fauth) != 0); } - if(mp->ar) + if(mp->ar){ + /* restrict hints to address rr's for nameservers only */ + if((tp = rrremfilter(&mp->ar, filterhints, mp->ns)) != 0){ + reportandfree(mp->ar, "hint", p); + mp->ar = tp; + } rrattach(mp->ar, Notauthoritative); + } if(mp->ns && !cfg.justforw){ ndp = mp->ns->owner; rrattach(mp->ns, Notauthoritative); @@ -1118,11 +1202,12 @@ procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) } /* - * Any reply from an authoritative server, + * Any reply from an authoritative server + * that does not provide more nameservers, * or a positive reply terminates the search. * A negative response now also terminates the search. */ - if(mp->an != nil || (mp->flags & Fauth)){ + if(mp->an || (mp->flags & Fauth) && mp->ns == nil){ if(isnegrname(mp)) qp->dp->respcode = Rname; else @@ -1156,7 +1241,7 @@ procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) * if we're a pure resolver, don't recurse, we have * to forward to a fixed set of named servers. */ - if(!mp->ns || cfg.resolver && cfg.justforw) + if(mp->ns == nil || cfg.resolver && cfg.justforw) return Answnone; tp = rrlookup(ndp, Tns, NOneg); if(contains(qp->nsrp, tp)){ @@ -1215,16 +1300,22 @@ tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len, static int queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns) { - int ndest, len, replywaits, rv; + int ndest, len, replywaits, rv, flag; ushort req; uvlong endms; char buf[32]; uchar srcip[IPaddrlen]; Dest *p, *np, dest[Maxdest]; - /* pack request into a udp message */ req = rand(); - len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req); + + /* request recursion only for local dns servers */ + flag = Oquery; + if(strncmp(qp->nsrp->owner->name, "local#", 6) == 0) + flag |= Frecurse; + + /* pack request into a udp message */ + len = mkreq(qp->dp, qp->type, obuf, flag, req); /* no server addresses yet */ memset(dest, 0, sizeof dest); @@ -1236,7 +1327,7 @@ queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns) * each cycle send one more message than the previous. * retry a query via tcp if its response is truncated. */ - for(ndest = 1; ndest < Maxdest; ndest++){ + for(ndest = 2; ndest < Maxdest; ndest += 2){ qp->ndest = ndest; qp->tcpset = 0; if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0) @@ -1272,21 +1363,23 @@ queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns) } /* find responder */ - // dnslog("queryns got reply from %I", srcip); + if(debug) + dnslog("queryns got reply from %I", srcip); for(p = qp->dest; p < qp->curdest; p++) - if(memcmp(p->a, srcip, sizeof p->a) == 0) + if(ipcmp(p->a, srcip) == 0) break; + if(p >= qp->curdest){ + dnslog("response from %I but no destination", srcip); + continue; + } - if(p != qp->curdest) { - /* remove all addrs of responding server from list */ - for(np = qp->dest; np < qp->curdest; np++) - if(np->s == p->s) - np->nx = Maxtrans; - } else - p = nil; + /* remove all addrs of responding server from list */ + for(np = qp->dest; np < qp->curdest; np++) + if(np->s == p->s) + np->nx = Maxtrans; /* free or incorporate RRs in m */ - rv = procansw(qp, &m, srcip, depth, p); + rv = procansw(qp, &m, depth, p); if (rv > Answnone) { qp->dest = qp->curdest = nil; /* prevent accidents */ return rv; @@ -1307,45 +1400,6 @@ queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns) return Answnone; } -/* - * run a command with a supplied fd as standard input - */ -char * -system(int fd, char *cmd) -{ - int pid, p, i; - static Waitmsg msg; - - if((pid = fork()) == -1) - sysfatal("fork failed: %r"); - else if(pid == 0){ - dup(fd, 0); - close(fd); - for (i = 3; i < 200; i++) - close(i); /* don't leak fds */ - execl("/bin/rc", "rc", "-c", cmd, nil); - sysfatal("exec rc: %r"); - } - for(p = waitpid(); p >= 0; p = waitpid()) - if(p == pid) - return msg.msg; - return "lost child"; -} - -/* compute wait, weighted by probability of success, with bounds */ -static ulong -weight(ulong ms, unsigned pcntprob) -{ - ulong wait; - - wait = (ms * pcntprob) / 100; - if (wait < Minwaitms) - wait = Minwaitms; - if (wait > Maxwaitms) - wait = Maxwaitms; - return wait; -} - /* * in principle we could use a single descriptor for a udp port * to send all queries and receive all the answers to them, @@ -1355,65 +1409,21 @@ static int udpquery(Query *qp, char *mntpt, int depth, int patient, int inns) { int fd, rv; - long now; - ulong pcntprob; - uvlong wait, reqtm; - char *msg; uchar *obuf, *ibuf; - static QLock mntlck; - static ulong lastmount; - - rv = -1; /* use alloced buffers rather than ones from the stack */ ibuf = emalloc(64*1024); /* max. tcp reply size */ obuf = emalloc(Maxudp+Udphdrsize); fd = udpport(mntpt); - while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) { - /* HACK: remount /net.alt */ - now = time(nil); - if (now < lastmount + Remntretry) - sleep(S2MS(lastmount + Remntretry - now)); - qlock(&mntlck); - fd = udpport(mntpt); /* try again under lock */ - if (fd < 0) { - dnslog("[%d] remounting /net.alt", getpid()); - unmount(nil, "/net.alt"); - - msg = system(open("/dev/null", ORDWR), "outside"); - - lastmount = time(nil); - if (msg && *msg) { - dnslog("[%d] can't remount /net.alt: %s", - getpid(), msg); - sleep(10*1000); /* don't spin remounting */ - } else - fd = udpport(mntpt); - } - qunlock(&mntlck); - } if (fd < 0) { dnslog("can't get udpport for %s query of name %s: %r", mntpt, qp->dp->name); + rv = -1; goto Out; } - - /* - * Our QIP servers are busted and respond to AAAA and CNAME queries - * with (sometimes malformed [too short] packets and) no answers and - * just NS RRs but not Rname errors. so make time-to-wait - * proportional to estimated probability of an RR of that type existing. - */ - if (qp->type >= nelem(likely)) - pcntprob = 35; /* unpopular query type */ - else - pcntprob = likely[qp->type]; - reqtm = (patient? 2 * Maxreqtm: Maxreqtm); - wait = weight(reqtm / 3, pcntprob); /* time for one udp query */ - qp->udpfd = fd; - rv = queryns(qp, depth, ibuf, obuf, wait, inns); + rv = queryns(qp, depth, ibuf, obuf, 500UL<<(patient != 0), inns); qp->udpfd = -1; close(fd); @@ -1502,7 +1512,7 @@ seerootns(void) req.aborttime = timems() + Maxreqtm; req.from = "internal"; queryinit(&q, dnlookup(root, Cin, 1), Tns, &req); - nsrp = dblookup(root, Cin, Tns, 0, 0); + nsrp = randomize(dblookup(root, Cin, Tns, 0, 0)); for (rr = nsrp; rr != nil; rr = rr->next) dnslog("seerootns query nsrp: %R", rr); rv = netqueryns(&q, 0, nsrp); /* lookup ". ns" using nsrp */