]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/ndb/dnresolve.c
ndb/dns: double Maxretries for long cname redirection chains
[plan9front.git] / sys / src / cmd / ndb / dnresolve.c
index 0f2767176e65d99068e6bdedad1c7ed56b93846c..3e700e5b60e422146b8b6701d1a8d60178eba536 100644 (file)
@@ -20,20 +20,14 @@ enum
 
        Maxdest=        24,     /* 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 */
+       Maxretries=     10,     /* 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 */
 };
@@ -43,7 +37,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 +47,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 */
@@ -175,7 +171,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 +221,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 +288,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,13 +342,13 @@ 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));
 
                /* if the entry timed out, ignore it */
-               if(nsrp && nsrp->ttl < now)
+               if(nsrp && !nsrp->db && (long)(nsrp->expire - now) <= 0)
                        rrfreelistptr(&nsrp);
 
                if(nsrp){
@@ -379,7 +381,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
@@ -397,7 +399,7 @@ dnresolve1(char *name, int class, int type, Request *req, int depth,
                        }
                } else
                        /* cached entry must still be valid */
-                       if(rp->ttl > now)
+                       if((long)(rp->expire - now) > 0)
                                /* but Tall entries are special */
                                if(type != Tall || rp->query == Tall) {
                                        noteinmem();
@@ -739,6 +741,28 @@ 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
  */
@@ -782,18 +806,21 @@ 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)
+       if(arp == 0){
+               for(rp = qp->nsrp; rp; rp = rp->next)
+                       if(rp->marker == 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;
+
                for(rp = qp->nsrp; rp; rp = rp->next){
                        if(rp->marker)
                                continue;
                        rp->marker = 1;
-
-                       /*
-                        *  avoid loops looking up a server under itself
-                        */
-                       if(subsume(rp->owner->name, rp->host->name))
-                               continue;
-
                        arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
                                depth+1, Recurse, 1, 0);
                        if(arp == nil)
@@ -803,6 +830,7 @@ serveraddrs(Query *qp, int nd, int depth)
                        if(arp)
                                break;
                }
+       }
 
        /* use any addresses that we found */
        for(trp = arp; trp && nd < Maxdest; trp = trp->next){
@@ -818,7 +846,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++;
        }
@@ -850,7 +885,7 @@ cacheneg(DN *dp, int type, int rcode, RR *soarr)
 
        /* the attach can cause soarr to be freed so mine it now */
        if(soarr != nil && soarr->soa != nil)
-               ttl = soarr->soa->minttl+now;
+               ttl = soarr->soa->minttl;
        else
                ttl = 5*Min;
 
@@ -893,11 +928,8 @@ mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
 
        rv = -1;
        snprint(domain, sizeof(domain), "%I", udppkt);
-       if (myaddr(domain)) {
-               dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
-                       domain);
+       if (myaddr(domain))
                return rv;
-       }
        switch (medium) {
        case Udp:
                nfd = dup(qp->udpfd, -1);
@@ -1045,9 +1077,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];
@@ -1055,46 +1144,69 @@ 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);
        unique(mp->ar);
 
-       if(mp->an)
+       if(mp->an){
+               /*
+                * only use cname answer when returned. some dns servers
+                * attach (potential) spam hint address records which poisons
+                * the cache.
+                */
+               if((tp = rrremtype(&mp->an, Tcname)) != 0){
+                       reportandfree(mp->an, "ip in cname answer", p);
+                       mp->an = tp;
+               }
                rrattach(mp->an, (mp->flags & Fauth) != 0);
-       if(mp->ar)
+       }
+       if(mp->ar){
+               /* restrict hints to address rr's for nameservers only */
+               if((tp = rrremfilter(&mp->ar, filterhints, mp->ns)) != 0){
+                       reportandfree(mp->ar, "hint", p);
+                       mp->ar = tp;
+               }
                rrattach(mp->ar, Notauthoritative);
+       }
        if(mp->ns && !cfg.justforw){
                ndp = mp->ns->owner;
                rrattach(mp->ns, Notauthoritative);
@@ -1111,11 +1223,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
@@ -1149,7 +1262,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)){
@@ -1208,16 +1321,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);
@@ -1265,21 +1384,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)
                                        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;
@@ -1300,31 +1421,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)
@@ -1348,13 +1444,9 @@ 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;
 
@@ -1363,29 +1455,6 @@ udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
        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);
@@ -1404,7 +1473,6 @@ udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
                pcntprob = likely[qp->type];
        reqtm = (patient? 2 * Maxreqtm: Maxreqtm);
        wait = weight(reqtm / 3, pcntprob);     /* time for one udp query */
-       qp->req->aborttime = timems() + 3*wait; /* for all udp queries */
 
        qp->udpfd = fd;
        rv = queryns(qp, depth, ibuf, obuf, wait, inns);