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 */
};
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 */
};
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 */
* 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
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);
{
int rv;
+ if(nsrp == nil)
+ return Answnone;
qp->nsrp = nsrp;
rv = netquery(qp, depth);
qp->nsrp = nil; /* prevent accidents */
}
/* 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){
if(class != Cin)
return nil;
- dp = dnlookup(name, class, 1);
+ dp = idnlookup(name, class, 1);
/*
* Try the cache first
}
} 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();
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
*/
* 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)
if(arp)
break;
}
+ }
/* use any addresses that we found */
for(trp = arp; trp && nd < Maxdest; trp = trp->next){
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++;
}
/* 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;
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);
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];
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);
}
/*
- * 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
* 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)){
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);
}
/* 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;
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)
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;
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);
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);