#include "dns.h"
typedef struct Dest Dest;
-typedef struct Ipaddr Ipaddr;
typedef struct Query Query;
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 */
-
- Destmagic= 0xcafebabe,
- Querymagic= 0xdeadbeef,
+ Maxretries= 10, /* cname+actual resends: was 32; have pity on user */
};
enum { Hurry, Patient, };
enum { Outns, Inns, };
-struct Ipaddr {
- Ipaddr *next;
- uchar ip[IPaddrlen];
-};
-
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 */
-
- ulong magic;
};
-/*
- * Query has a QLock in it, thus it can't be an automatic
- * variable, since each process would see a separate copy
- * of the lock on its stack.
- */
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 */
int udpfd;
- QLock tcplock; /* only one tcp call at a time per query */
int tcpset;
int tcpfd; /* if Tcp, read replies from here */
int tcpctlfd;
uchar tcpip[IPaddrlen];
-
- ulong magic;
-};
-
-/* 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);
nrp->ptr->name);
rp = dnresolve(nname, class, type, req, cn, depth+1,
recurse, rooted, status);
- lock(&dnlock);
rrfreelist(rrremneg(&rp));
- unlock(&dnlock);
}
if(drp != nil)
rrfreelist(drp);
* try the name directly
*/
rp = dnresolve1(name, class, type, req, depth, recurse);
- if(rp == 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
*/
- dp = dnlookup(name, class, 0);
if(type != Tptr && dp->respcode != Rname)
for(loops = 0; rp == nil && loops < Maxretries; loops++){
/* retry cname, then the actual type */
}
name = rp->host->name;
- lock(&dnlock);
if(cn)
rrcat(cn, rp);
else
rrfreelist(rp);
- unlock(&dnlock);
rp = dnresolve1(name, class, type, req,
depth, recurse);
static void
queryinit(Query *qp, DN *dp, int type, Request *req)
{
+ assert(dp && dp->magic == DNmagic);
+
memset(qp, 0, sizeof *qp);
qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
qp->dp = dp;
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->magic = Querymagic;
-}
-
-static void
-queryck(Query *qp)
-{
- assert(qp);
- assert(qp->magic == Querymagic);
+ qp->prev = req->aux;
+ qp->req = req;
+ req->aux = qp;
}
static void
querydestroy(Query *qp)
{
- queryck(qp);
+ if(qp->req->aux == qp)
+ qp->req->aux = qp->prev;
/* leave udpfd open */
- if (qp->tcpfd > 0)
+ if (qp->tcpfd >= 0)
close(qp->tcpfd);
- if (qp->tcpctlfd > 0) {
+ if (qp->tcpctlfd >= 0) {
hangup(qp->tcpctlfd);
close(qp->tcpctlfd);
}
- free(qp->dest);
memset(qp, 0, sizeof *qp); /* prevent accidents */
qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
}
-static void
-destinit(Dest *p)
-{
- memset(p, 0, sizeof *p);
- p->magic = Destmagic;
-}
-
-static void
-destck(Dest *p)
-{
- assert(p);
- assert(p->magic == Destmagic);
-}
-
/*
* if the response to a query hasn't arrived within 100 ms.,
* it's unlikely to arrive at all. after 1 s., it's really unlikely.
{
int rv;
+ if(nsrp == nil)
+ return Answnone;
qp->nsrp = nsrp;
rv = netquery(qp, depth);
- lock(&dnlock);
+ qp->nsrp = nil; /* prevent accidents */
rrfreelist(nsrp);
- unlock(&dnlock);
return rv;
}
dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
if(dbnsrp && dbnsrp->local){
rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
- lock(&dnlock);
rrfreelist(dbnsrp);
- unlock(&dnlock);
return rp;
}
* entries
*/
if(recurse == Dontrecurse){
- if(dbnsrp) {
- lock(&dnlock);
+ if(dbnsrp)
rrfreelist(dbnsrp);
- unlock(&dnlock);
- }
continue;
}
/* 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){
- lock(&dnlock);
+ if(nsrp && !nsrp->db && (long)(nsrp->expire - now) <= 0)
rrfreelistptr(&nsrp);
- unlock(&dnlock);
- }
if(nsrp){
- lock(&dnlock);
rrfreelistptr(&dbnsrp);
- unlock(&dnlock);
/* query the name servers found in cache */
if(netqueryns(qp, depth+1, nsrp) > Answnone)
Area *area;
DN *dp;
RR *rp;
- Query *qp;
+ Query q;
if(debug)
dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
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();
getpid(), name, type, class);
return rp;
}
- lock(&dnlock);
rrfreelist(rp);
- unlock(&dnlock);
rp = nil; /* accident prevention */
USED(rp);
*/
if(type != Tcname){
rp = rrlookup(dp, Tcname, NOneg);
- lock(&dnlock);
rrfreelist(rp);
- unlock(&dnlock);
if(rp){
if(debug)
dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname",
* we should have found its data in memory by now.
*/
area = inmyarea(dp->name);
- if (area || strncmp(dp->name, "local#", 6) == 0) {
-// char buf[32];
-
-// dnslog("%s %s: no data in area %s", dp->name,
-// rrname(type, buf, sizeof buf), area->soarr->owner->name);
+ if (area || strncmp(dp->name, "local#", 6) == 0)
return nil;
- }
- qp = emalloc(sizeof *qp);
- queryinit(qp, dp, type, req);
- rp = issuequery(qp, name, class, depth, recurse);
- querydestroy(qp);
- free(qp);
+ queryinit(&q, dp, type, req);
+ rp = issuequery(&q, name, class, depth, recurse);
+ querydestroy(&q);
+
if(rp){
if(debug)
dnslog("[%d] dnresolve1 %s %d %d: rr from query",
char ds[64], adir[64];
/* get a udp port */
- snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
+ snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt && *mtpt) ? mtpt : "/net");
ctl = announce(ds, adir);
if(ctl < 0){
/* warning("can't get udp port"); */
void
initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno)
{
+ memset(mp, 0, sizeof *mp);
mp->flags = flags;
mp->id = reqno;
mp->qd = rp;
mp->qdcount = 1;
}
-DNSmsg *
-newdnsmsg(RR *rp, int flags, ushort reqno)
-{
- DNSmsg *mp;
-
- mp = emalloc(sizeof *mp);
- initdnsmsg(mp, rp, flags, reqno);
- return mp;
-}
-
/* generate a DNS UDP query packet */
int
mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
hnputs(uh->rport, 53);
/* make request and convert it to output format */
- memset(&m, 0, sizeof m);
rp = rralloc(type);
rp->owner = dp;
initdnsmsg(&m, rp, flags, reqno);
len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
- rrfreelistptr(&m.qd);
- memset(&m, 0, sizeof m); /* cause trouble */
+ rrfreelist(rp);
return len;
}
void
freeanswers(DNSmsg *mp)
{
- lock(&dnlock);
rrfreelistptr(&mp->qd);
rrfreelistptr(&mp->an);
rrfreelistptr(&mp->ns);
rrfreelistptr(&mp->ar);
- unlock(&dnlock);
mp->qdcount = mp->ancount = mp->nscount = mp->arcount = 0;
}
uchar lenbuf[2];
len = -1; /* pessimism */
+ *replyp = nil;
+ memset(srcip, 0, IPaddrlen);
ms = endms - NS2MS(startns);
if (ms <= 0)
return -1; /* taking too long */
reply = ibuf;
- memset(srcip, 0, IPaddrlen);
alarm(ms);
if (medium == Udp)
- if (qp->udpfd <= 0)
+ if (qp->udpfd < 0)
dnslog("readnet: qp->udpfd closed");
else {
len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
if (!qp->tcpset)
dnslog("readnet: tcp params not set");
fd = qp->tcpfd;
- if (fd <= 0)
+ if (fd < 0)
dnslog("readnet: %s: tcp fd unset for dest %I",
qp->dp->name, qp->tcpip);
else if (readn(fd, lenbuf, 2) != 2) {
uchar srcip[IPaddrlen];
RR *rp;
- queryck(qp);
- memset(mp, 0, sizeof *mp);
- memset(srcip, 0, sizeof srcip);
- if (0)
- len = -1;
for (; timems() < endms &&
(len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0;
freeanswers(mp)){
"ns %s", qp->dp->name,
rp->host->name);
}
+ memset(mp, 0, sizeof *mp);
return -1;
}
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;
- Dest *cur;
+ ulong mark;
+ Dest *p;
if(nd >= Maxdest) /* dest array is full? */
- return Maxdest - 1;
+ return Maxdest;
/*
* look for a server whose address we already know.
* if we find one, mark it so we ignore this on
* subsequent passes.
*/
- arp = 0;
+ mark = 1UL<<type;
+ arp = nil;
for(rp = qp->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;
}
}
* 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);
- lock(&dnlock);
rrfreelist(rrremneg(&arp));
- unlock(&dnlock);
if(arp)
break;
}
+ }
/* use any addresses that we found */
for(trp = arp; trp && nd < Maxdest; trp = trp->next){
- cur = &qp->dest[nd];
- parseip(cur->a, trp->ip->name);
+ p = &qp->dest[nd];
+ memset(p, 0, sizeof *p);
+ 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
* the end of the ns list in /lib/ndb for `dom='.
*/
- if (ipisbm(cur->a) ||
- cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
+ if (ipisbm(p->a) ||
+ cfg.straddle && !insideaddr(qp->dp->name) && insidens(p->a))
continue;
- cur->nx = 0;
- cur->s = trp->owner;
- cur->code = Rtimeout;
+ 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++;
}
- lock(&dnlock);
rrfreelist(arp);
- unlock(&dnlock);
return nd;
}
DN *soaowner;
ulong ttl;
+ qlock(&stats);
stats.negcached++;
+ qunlock(&stats);
/* no cache time specified, don't make anything up */
if(soarr != nil){
- lock(&dnlock);
if(soarr->next != nil)
rrfreelistptr(&soarr->next);
- unlock(&dnlock);
soaowner = soarr->owner;
} else
soaowner = nil;
/* 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;
static int
setdestoutns(Dest *p, int n)
{
- uchar *outns = outsidens(n);
-
- destck(p);
- destinit(p);
- if (outns == nil) {
+ memset(p, 0, sizeof *p);
+ if (outsidensip(n, p->a) < 0){
if (n == 0)
dnslog("[%d] no outside-ns in ndb", getpid());
return -1;
}
- memmove(p->a, outns, sizeof p->a);
p->s = dnlookup("outside-ns-ips", Cin, 1);
return 0;
}
static int
mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
{
- int rv = -1, nfd;
- char *domain;
- char conndir[40], net[40];
+ int rv, nfd;
+ char conndir[40], addr[128];
uchar belen[2];
NetConnInfo *nci;
- queryck(qp);
- domain = smprint("%I", udppkt);
- if (myaddr(domain)) {
- dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
- domain);
- free(domain);
+ rv = -1;
+ if (myip(udppkt))
return rv;
- }
-
switch (medium) {
case Udp:
- free(domain);
nfd = dup(qp->udpfd, -1);
if (nfd < 0) {
warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
close(qp->udpfd); /* ensure it's closed */
qp->udpfd = -1; /* poison it */
- return rv;
+ break;
}
close(nfd);
- if (qp->udpfd <= 0)
+ if (qp->udpfd < 0)
dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
else {
if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
len+Udphdrsize)
warning("sending udp msg: %r");
else {
+ qlock(&stats);
stats.qsent++;
+ qunlock(&stats);
rv = 0;
}
}
break;
case Tcp:
/* send via TCP & keep fd around for reply */
- snprint(net, sizeof net, "%s/tcp",
- (mntpt[0] != '\0'? mntpt: "/net"));
+ memmove(qp->tcpip, udppkt, sizeof qp->tcpip);
+ snprint(addr, sizeof addr, "%s/tcp!%I!dns",
+ (mntpt && *mntpt) ? mntpt : "/net", udppkt);
alarm(10*1000);
- qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil,
- conndir, &qp->tcpctlfd);
+ qp->tcpfd = dial(addr, nil, conndir, &qp->tcpctlfd);
alarm(0);
if (qp->tcpfd < 0) {
- dnslog("can't dial tcp!%s!dns: %r", domain);
- free(domain);
+ dnslog("can't dial %s: %r", addr);
break;
}
- free(domain);
nci = getnetconninfo(conndir, qp->tcpfd);
if (nci) {
parseip(qp->tcpip, nci->rsys);
if (write(qp->tcpfd, belen, 2) != 2 ||
write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
warning("sending tcp msg: %r");
+ else
+ rv = 0;
break;
- default:
- sysfatal("mydnsquery: bad medium");
}
return rv;
}
static int
xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
{
- int j, n;
+ int n;
char buf[32];
Dest *p;
- queryck(qp);
if(timems() >= qp->req->aborttime)
return -1;
/*
- * get a nameserver address if we need one.
- * serveraddrs populates qp->dest.
+ * if we send tcp query, we just take the dest ip address from
+ * the udp header placed there by tcpquery().
*/
- p = qp->dest;
- destck(p);
- if (qp->ndest < 0 || qp->ndest > Maxdest) {
- dnslog("qp->ndest %d out of range", qp->ndest);
- abort();
+ if (medium == Tcp) {
+ procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
+ qp->dp->name, rrname(qp->type, buf, sizeof buf));
+ if(mydnsquery(qp, medium, obuf, len) < 0) /* sets qp->tcpip from obuf */
+ return -1;
+ if(debug)
+ logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
+ qp->type);
+ return 0;
}
+
/*
+ * get a nameserver address if we need one.
* we're to transmit to more destinations than we currently have,
* so get another.
*/
- if (qp->ndest > qp->curdest - p) {
- j = serveraddrs(qp, qp->curdest - p, depth);
- if (j < 0 || j >= Maxdest) {
- dnslog("serveraddrs() result %d out of range", j);
- abort();
- }
- qp->curdest = &qp->dest[j];
- }
- destck(qp->curdest);
-
- /* no servers, punt */
- if (qp->ndest == 0)
- if (cfg.straddle && cfg.inside) {
+ p = qp->dest;
+ n = qp->curdest - p;
+ if (qp->ndest > n) {
+ /* 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" */
- qp->curdest = qp->dest;
- for(n = 0; n < Maxdest; n++, qp->curdest++)
- if (setdestoutns(qp->curdest, n) < 0)
+ while(n < Maxdest){
+ if (setdestoutns(&qp->dest[n], n) < 0)
break;
+ n++;
+ }
if(n == 0)
dnslog("xmitquery: %s: no outside-ns nameservers",
qp->dp->name);
- } else
- /* it's probably just a bogus domain, don't log it */
- return -1;
+ }
+ qp->curdest = &qp->dest[n];
+ }
- /* send to first 'qp->ndest' destinations */
- j = 0;
- if (medium == Tcp) {
- j++;
- queryck(qp);
- assert(qp->dp);
- procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
+ for(n = 0; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
+ /* skip destinations we've finished with */
+ if(p->nx >= Maxtrans)
+ continue;
+ /* exponential backoff of requests */
+ if((1<<p->nx) > qp->ndest)
+ continue;
+
+ if(ipcmp(p->a, IPnoaddr) == 0)
+ continue; /* mistake */
+
+ procsetname("udp %sside query to %I/%s %s %s",
+ (inns? "in": "out"), p->a, p->s->name,
qp->dp->name, rrname(qp->type, buf, sizeof buf));
- mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
if(debug)
- logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
- qp->type);
- } else
- for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
- /* skip destinations we've finished with */
- if(p->nx >= Maxtrans)
- continue;
+ logsend(qp->req->id, depth, p->a, p->s->name,
+ qp->dp->name, qp->type);
+
+ /* fill in UDP destination addr & send it */
+ memmove(obuf, p->a, sizeof p->a);
+ if(mydnsquery(qp, medium, obuf, len) == 0)
+ n++;
+ p->nx++;
+ }
- j++;
+ return n == 0 ? -1 : 0;
+}
- /* exponential backoff of requests */
- if((1<<p->nx) > qp->ndest)
- continue;
+/* is mp a cachable negative response (with Rname set)? */
+static int
+isnegrname(DNSmsg *mp)
+{
+ /* TODO: could add || cfg.justforw to RHS of && */
+ return mp->an == nil && (mp->flags & Rmask) == Rname;
+}
- if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0)
- continue; /* mistake */
+static int
+filterhints(RR *rp, void *arg)
+{
+ RR *nsrp;
- procsetname("udp %sside query to %I/%s %s %s",
- (inns? "in": "out"), p->a, p->s->name,
- qp->dp->name, rrname(qp->type, buf, sizeof buf));
- if(debug)
- logsend(qp->req->id, depth, p->a, p->s->name,
- qp->dp->name, qp->type);
+ 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;
- /* fill in UDP destination addr & send it */
- memmove(obuf, p->a, sizeof p->a);
- mydnsquery(qp, medium, obuf, len);
- p->nx++;
- }
- if(j == 0) {
- return -1;
- }
return 0;
}
-static int lckindex[Maxlcks] = {
- 0, /* all others map here */
- Ta,
- Tns,
- Tcname,
- Tsoa,
- Tptr,
- Tmx,
- Ttxt,
- Taaaa,
-};
-
static int
-qtype2lck(int qtype) /* map query type to querylck index */
+filterauth(RR *rp, void *arg)
{
- int i;
+ Dest *dest;
+ RR *nsrp;
- for (i = 1; i < nelem(lckindex); i++)
- if (lckindex[i] == qtype)
- return i;
- return 0;
+ 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);
}
-/* is mp a cachable negative response (with Rname set)? */
-static int
-isnegrname(DNSmsg *mp)
+static void
+reportandfree(RR *l, char *note, Dest *p)
{
- /* TODO: could add || cfg.justforw to RHS of && */
- return mp->an == nil && (mp->flags & Rmask) == Rname;
+ 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;
-// int lcktype;
char buf[32];
DN *ndp;
- Query *nqp;
+ 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 != qp->curdest)
- 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 != qp->curdest)
- p->code = Rserver;
- dnslog(" and no answers");
- return Answerr;
- }
- dnslog(" but has answers; ignoring ns");
- lock(&dnlock);
- rrfreelistptr(&mp->ns);
- unlock(&dnlock);
- mp->nscount = 0;
- }
+ if((tp = rrremfilter(&mp->ns, filterauth, p)) != 0)
+ reportandfree(tp, "bad delegation", p);
/* remove any soa's from the authority section */
- lock(&dnlock);
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);
- unlock(&dnlock);
- 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);
} else {
ndp = nil;
- lock(&dnlock);
rrfreelistptr(&mp->ns);
- unlock(&dnlock);
mp->nscount = 0;
}
/* free the question */
if(mp->qd) {
- lock(&dnlock);
rrfreelistptr(&mp->qd);
- unlock(&dnlock);
mp->qdcount = 0;
}
/*
- * 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( /* (mp->flags & Fauth) && */ mp->an == nil)
cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
- else {
- lock(&dnlock);
+ else
rrfreelist(soarr);
- unlock(&dnlock);
- }
return 1;
} else if (isnegrname(mp)) {
qp->dp->respcode = Rname;
return 1;
}
stats.negnorname++;
- lock(&dnlock);
rrfreelist(soarr);
- unlock(&dnlock);
/*
* if we've been given better name servers, recurse.
* 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)){
- lock(&dnlock);
rrfreelist(tp);
- unlock(&dnlock);
return Answnone;
}
procsetname("recursive query for %s %s", qp->dp->name,
rrname(qp->type, buf, sizeof buf));
- /*
- * we're called from udpquery, called from
- * netquery, which current holds qp->dp->querylck,
- * so release it now and acquire it upon return.
- */
-// lcktype = qtype2lck(qp->type); /* someday try this again */
-// qunlock(&qp->dp->querylck[lcktype]);
-
- nqp = emalloc(sizeof *nqp);
- queryinit(nqp, qp->dp, qp->type, qp->req);
- nqp->nsrp = tp;
- rv = netquery(nqp, depth+1);
-
-// qlock(&qp->dp->querylck[lcktype]);
- rrfreelist(nqp->nsrp);
- querydestroy(nqp);
- free(nqp);
+
+ queryinit(&nq, qp->dp, qp->type, qp->req);
+ rv = netqueryns(&nq, depth+1, tp);
+ querydestroy(&nq);
+
return rv;
}
dnslog("%s: udp reply truncated; retrying query via tcp to %I",
qp->dp->name, qp->tcpip);
- qlock(&qp->tcplock);
memmove(obuf, ibuf, IPaddrlen); /* send back to respondent */
- /* sets qp->tcpip from obuf's udp header */
+ memset(mp, 0, sizeof *mp);
if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
readreply(qp, Tcp, req, ibuf, mp, endms) < 0)
rv = -1;
- if (qp->tcpfd > 0) {
+ if (qp->tcpfd >= 0) {
hangup(qp->tcpctlfd);
close(qp->tcpctlfd);
close(qp->tcpfd);
}
qp->tcpfd = qp->tcpctlfd = -1;
- qunlock(&qp->tcplock);
+
return rv;
}
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[12];
+ char buf[32];
uchar srcip[IPaddrlen];
- Dest *p, *np, *dest;
+ 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 */
- queryck(qp);
- dest = emalloc(Maxdest * sizeof *dest); /* dest can't be on stack */
- for (p = dest; p < dest + Maxdest; p++)
- destinit(p);
- /* this dest array is local to this call of queryns() */
- free(qp->dest);
+ memset(dest, 0, sizeof dest);
qp->curdest = qp->dest = dest;
/*
* 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)
}
/* 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;
+ }
/* remove all addrs of responding server from list */
for(np = qp->dest; np < qp->curdest; np++)
if(np->s == p->s)
- p->nx = Maxtrans;
+ 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) {
- free(qp->dest);
qp->dest = qp->curdest = nil; /* prevent accidents */
return rv;
}
/* if all servers returned failure, propagate it */
qp->dp->respcode = Rserver;
- for(p = dest; p < qp->curdest; p++) {
- destck(p);
+ for(p = dest; p < qp->curdest; p++)
if(p->code != Rserver)
qp->dp->respcode = Rok;
- p->magic = 0; /* prevent accidents */
- }
// if (qp->dp->respcode)
// dnslog("queryns setting Rserver for %s", qp->dp->name);
- free(qp->dest);
qp->dest = qp->curdest = nil; /* prevent accidents */
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,
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;
/* 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);
- sysfatal("out of udp conversations"); /* we're buggered */
+ 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->req->aborttime = timems() + 3*wait; /* for all udp queries */
-
qp->udpfd = fd;
- rv = queryns(qp, depth, ibuf, obuf, wait, inns);
- close(fd);
+ rv = queryns(qp, depth, ibuf, obuf, 500UL<<(patient != 0), inns);
qp->udpfd = -1;
+ close(fd);
+Out:
free(obuf);
free(ibuf);
return rv;
static int
netquery(Query *qp, int depth)
{
- int lock, rv, triedin, inname;
- char buf[32];
+ int rv, triedin, inname;
RR *rp;
- DN *dp;
- Querylck *qlp;
- static int whined;
rv = Answnone; /* pessimism */
if(depth > 12) /* in a recursive loop? */
return Answnone;
slave(qp->req);
+
/*
* slave might have forked. if so, the parent process longjmped to
* req->mret; we're usually the child slave, but if there are too
- * many children already, we're still the same process.
+ * many children already, we're still the same process. under no
+ * circumstances block the 9p loop.
*/
+ if(!qp->req->isslave && strcmp(qp->req->from, "9p") == 0)
+ return Answnone;
- /*
- * don't lock before call to slave so only children can block.
- * just lock at top-level invocation.
- */
- lock = depth <= 1 && qp->req->isslave;
- dp = qp->dp; /* ensure that it doesn't change underfoot */
- qlp = nil;
- if(lock) {
- procsetname("query lock wait: %s %s from %s", dp->name,
- rrname(qp->type, buf, sizeof buf), qp->req->from);
- /*
- * don't make concurrent queries for this name.
- * dozens of processes blocking here probably indicates
- * an error in our dns data that causes us to not
- * recognise a zone (area) as one of our own, thus
- * causing us to query other nameservers.
- */
- qlp = &dp->querylck[qtype2lck(qp->type)];
- qlock(qlp);
- if (qlp->Ref.ref > Maxoutstanding) {
- qunlock(qlp);
- if (!whined) {
- whined = 1;
- dnslog("too many outstanding queries for %s;"
- " dropping this one; no further logging"
- " of drops", dp->name);
- }
- return 0;
- }
- ++qlp->Ref.ref;
- qunlock(qlp);
- }
- procsetname("netquery: %s", dp->name);
+ procsetname("netquery: %s", qp->dp->name);
/* prepare server RR's for incremental lookup */
for(rp = qp->nsrp; rp; rp = rp->next)
* for inside addresses and /net.alt for outside addresses,
* thus bypassing other inside nameservers.
*/
- inname = insideaddr(dp->name);
+ inname = insideaddr(qp->dp->name);
if (!cfg.straddle || inname) {
rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
triedin = 1;
if (triedin)
dnslog(
"[%d] netquery: internal nameservers failed for %s; trying external",
- getpid(), dp->name);
+ getpid(), qp->dp->name);
/* prepare server RR's for incremental lookup */
for(rp = qp->nsrp; rp; rp = rp->next)
rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
}
-// if (rv == Answnone) /* could ask /net.alt/dns directly */
-// askoutdns(dp, qp->type);
-
- if(lock && qlp) {
- qlock(qlp);
- assert(qlp->Ref.ref > 0);
- qunlock(qlp);
- decref(qlp);
- }
+
return rv;
}
int rv;
char root[] = "";
Request req;
- RR *rr;
- Query *qp;
+ RR *rr, *nsrp;
+ Query q;
memset(&req, 0, sizeof req);
req.isslave = 1;
req.aborttime = timems() + Maxreqtm;
req.from = "internal";
-
- qp = emalloc(sizeof *qp);
- queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
- qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
- for (rr = qp->nsrp; rr != nil; rr = rr->next) /* DEBUG */
+ queryinit(&q, dnlookup(root, Cin, 1), Tns, &req);
+ nsrp = randomize(dblookup(root, Cin, Tns, 0, 0));
+ for (rr = nsrp; rr != nil; rr = rr->next)
dnslog("seerootns query nsrp: %R", rr);
-
- rv = netquery(qp, 0); /* lookup ". ns" using qp->nsrp */
-
- rrfreelist(qp->nsrp);
- querydestroy(qp);
- free(qp);
+ rv = netqueryns(&q, 0, nsrp); /* lookup ". ns" using nsrp */
+ querydestroy(&q);
return rv;
}