13cbadd90SDavid du Colombier /* 23cbadd90SDavid du Colombier * domain name resolvers, see rfcs 1035 and 1123 33cbadd90SDavid du Colombier */ 43e12c5d1SDavid du Colombier #include <u.h> 53e12c5d1SDavid du Colombier #include <libc.h> 64f8f669cSDavid du Colombier #include <ip.h> 74f8f669cSDavid du Colombier #include <bio.h> 84f8f669cSDavid du Colombier #include <ndb.h> 93e12c5d1SDavid du Colombier #include "dns.h" 103e12c5d1SDavid du Colombier 113cbadd90SDavid du Colombier typedef struct Dest Dest; 123cbadd90SDavid du Colombier typedef struct Ipaddr Ipaddr; 133cbadd90SDavid du Colombier typedef struct Query Query; 143cbadd90SDavid du Colombier 153e12c5d1SDavid du Colombier enum 163e12c5d1SDavid du Colombier { 173cbadd90SDavid du Colombier Udp, Tcp, 18b8b25780SDavid du Colombier 19c3617180SDavid du Colombier Answerr= -1, 20c3617180SDavid du Colombier Answnone, 21c3617180SDavid du Colombier 227dd7cddfSDavid du Colombier Maxdest= 24, /* maximum destinations for a request message */ 23b8b25780SDavid du Colombier Maxoutstanding= 15, /* max. outstanding queries per domain name */ 24e6d9d902SDavid du Colombier Remntretry= 15, /* min. sec.s between /net.alt remount tries */ 25e6d9d902SDavid du Colombier 26e6d9d902SDavid du Colombier /* 27e6d9d902SDavid du Colombier * these are the old values; we're trying longer timeouts now 28e6d9d902SDavid du Colombier * primarily for the benefit of remote nameservers querying us 29e6d9d902SDavid du Colombier * during times of bad connectivity. 30e6d9d902SDavid du Colombier */ 31e6d9d902SDavid du Colombier // Maxtrans= 3, /* maximum transmissions to a server */ 32e6d9d902SDavid du Colombier // Maxretries= 3, /* cname+actual resends: was 32; have pity on user */ 33e6d9d902SDavid du Colombier // Maxwaitms= 1000, /* wait no longer for a remote dns query */ 34e6d9d902SDavid du Colombier // Minwaitms= 100, /* willing to wait for a remote dns query */ 35e6d9d902SDavid du Colombier 36e6d9d902SDavid du Colombier Maxtrans= 5, /* maximum transmissions to a server */ 37e6d9d902SDavid du Colombier Maxretries= 5, /* cname+actual resends: was 32; have pity on user */ 38e6d9d902SDavid du Colombier Maxwaitms= 5000, /* wait no longer for a remote dns query */ 39e6d9d902SDavid du Colombier Minwaitms= 500, /* willing to wait for a remote dns query */ 40b8b25780SDavid du Colombier 413cbadd90SDavid du Colombier Destmagic= 0xcafebabe, 423cbadd90SDavid du Colombier Querymagic= 0xdeadbeef, 433e12c5d1SDavid du Colombier }; 44a41547ffSDavid du Colombier enum { Hurry, Patient, }; 45a41547ffSDavid du Colombier enum { Outns, Inns, }; 463e12c5d1SDavid du Colombier 473cbadd90SDavid du Colombier struct Ipaddr { 483cbadd90SDavid du Colombier Ipaddr *next; 493cbadd90SDavid du Colombier uchar ip[IPaddrlen]; 503cbadd90SDavid du Colombier }; 513cbadd90SDavid du Colombier 523cbadd90SDavid du Colombier struct Dest 533cbadd90SDavid du Colombier { 543cbadd90SDavid du Colombier uchar a[IPaddrlen]; /* ip address */ 553cbadd90SDavid du Colombier DN *s; /* name server */ 563cbadd90SDavid du Colombier int nx; /* number of transmissions */ 573cbadd90SDavid du Colombier int code; /* response code; used to clear dp->respcode */ 583cbadd90SDavid du Colombier 593cbadd90SDavid du Colombier ulong magic; 603cbadd90SDavid du Colombier }; 613cbadd90SDavid du Colombier 62c73252aeSDavid du Colombier /* 63c73252aeSDavid du Colombier * Query has a QLock in it, thus it can't be an automatic 64c73252aeSDavid du Colombier * variable, since each process would see a separate copy 65c73252aeSDavid du Colombier * of the lock on its stack. 66c73252aeSDavid du Colombier */ 673cbadd90SDavid du Colombier struct Query { 683cbadd90SDavid du Colombier DN *dp; /* domain */ 69adb31a62SDavid du Colombier ushort type; /* and type to look up */ 703cbadd90SDavid du Colombier Request *req; 713cbadd90SDavid du Colombier RR *nsrp; /* name servers to consult */ 723cbadd90SDavid du Colombier 73a41547ffSDavid du Colombier /* dest must not be on the stack due to forking in slave() */ 743cbadd90SDavid du Colombier Dest *dest; /* array of destinations */ 7559f7772cSDavid du Colombier Dest *curdest; /* pointer to next to fill */ 7659f7772cSDavid du Colombier int ndest; /* transmit to this many on this round */ 773cbadd90SDavid du Colombier 78f46c709fSDavid du Colombier int udpfd; 793cbadd90SDavid du Colombier 803cbadd90SDavid du Colombier QLock tcplock; /* only one tcp call at a time per query */ 813cbadd90SDavid du Colombier int tcpset; 823cbadd90SDavid du Colombier int tcpfd; /* if Tcp, read replies from here */ 833cbadd90SDavid du Colombier int tcpctlfd; 843cbadd90SDavid du Colombier uchar tcpip[IPaddrlen]; 853cbadd90SDavid du Colombier 863cbadd90SDavid du Colombier ulong magic; 873cbadd90SDavid du Colombier }; 883cbadd90SDavid du Colombier 89a41547ffSDavid du Colombier /* estimated % probability of such a record existing at all */ 90a41547ffSDavid du Colombier int likely[] = { 91a41547ffSDavid du Colombier [Ta] 95, 92a41547ffSDavid du Colombier [Taaaa] 10, 93a41547ffSDavid du Colombier [Tcname] 15, 94a41547ffSDavid du Colombier [Tmx] 60, 95a41547ffSDavid du Colombier [Tns] 90, 96a41547ffSDavid du Colombier [Tnull] 5, 97a41547ffSDavid du Colombier [Tptr] 35, 98a41547ffSDavid du Colombier [Tsoa] 90, 99a41547ffSDavid du Colombier [Tsrv] 60, 100a41547ffSDavid du Colombier [Ttxt] 15, 101a41547ffSDavid du Colombier [Tall] 95, 1023cbadd90SDavid du Colombier }; 1033cbadd90SDavid du Colombier 1047dd7cddfSDavid du Colombier static RR* dnresolve1(char*, int, int, Request*, int, int); 1053cbadd90SDavid du Colombier static int netquery(Query *, int); 1063cbadd90SDavid du Colombier 1074f8f669cSDavid du Colombier /* 108530fef66SDavid du Colombier * reading /proc/pid/args yields either "name args" or "name [display args]", 1094f8f669cSDavid du Colombier * so return only display args, if any. 1104f8f669cSDavid du Colombier */ 1114f8f669cSDavid du Colombier static char * 1124f8f669cSDavid du Colombier procgetname(void) 1134f8f669cSDavid du Colombier { 1144f8f669cSDavid du Colombier int fd, n; 1154f8f669cSDavid du Colombier char *lp, *rp; 1164f8f669cSDavid du Colombier char buf[256]; 1174f8f669cSDavid du Colombier 1184f8f669cSDavid du Colombier snprint(buf, sizeof buf, "#p/%d/args", getpid()); 1194f8f669cSDavid du Colombier if((fd = open(buf, OREAD)) < 0) 1204f8f669cSDavid du Colombier return strdup(""); 1214f8f669cSDavid du Colombier *buf = '\0'; 1224f8f669cSDavid du Colombier n = read(fd, buf, sizeof buf-1); 1234f8f669cSDavid du Colombier close(fd); 1244f8f669cSDavid du Colombier if (n >= 0) 1254f8f669cSDavid du Colombier buf[n] = '\0'; 1264f8f669cSDavid du Colombier if ((lp = strchr(buf, '[')) == nil || 1274f8f669cSDavid du Colombier (rp = strrchr(buf, ']')) == nil) 1284f8f669cSDavid du Colombier return strdup(""); 1294f8f669cSDavid du Colombier *rp = '\0'; 1304f8f669cSDavid du Colombier return strdup(lp+1); 1314f8f669cSDavid du Colombier } 1323e12c5d1SDavid du Colombier 133e6d9d902SDavid du Colombier void 134e6d9d902SDavid du Colombier rrfreelistptr(RR **rpp) 135e6d9d902SDavid du Colombier { 136e6d9d902SDavid du Colombier RR *rp; 137e6d9d902SDavid du Colombier 138e6d9d902SDavid du Colombier if (rpp == nil || *rpp == nil) 139e6d9d902SDavid du Colombier return; 140e6d9d902SDavid du Colombier rp = *rpp; 141c3617180SDavid du Colombier *rpp = nil; /* update pointer in memory before freeing list */ 142e6d9d902SDavid du Colombier rrfreelist(rp); 143e6d9d902SDavid du Colombier } 144e6d9d902SDavid du Colombier 1453e12c5d1SDavid du Colombier /* 1463e12c5d1SDavid du Colombier * lookup 'type' info for domain name 'name'. If it doesn't exist, try 1473e12c5d1SDavid du Colombier * looking it up as a canonical name. 148b8b25780SDavid du Colombier * 149b8b25780SDavid du Colombier * this process can be quite slow if time-outs are set too high when querying 150b8b25780SDavid du Colombier * nameservers that just don't respond to certain query types. in that case, 151b8b25780SDavid du Colombier * there will be multiple udp retries, multiple nameservers will be queried, 152b8b25780SDavid du Colombier * and this will be repeated for a cname query. the whole thing will be 153b8b25780SDavid du Colombier * retried several times until we get an answer or a time-out. 1543e12c5d1SDavid du Colombier */ 1553e12c5d1SDavid du Colombier RR* 1564f8f669cSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, 1574f8f669cSDavid du Colombier int recurse, int rooted, int *status) 1583e12c5d1SDavid du Colombier { 1597dd7cddfSDavid du Colombier RR *rp, *nrp, *drp; 1603e12c5d1SDavid du Colombier DN *dp; 1617dd7cddfSDavid du Colombier int loops; 1624f8f669cSDavid du Colombier char *procname; 1637dd7cddfSDavid du Colombier char nname[Domlen]; 1643e12c5d1SDavid du Colombier 1657dd7cddfSDavid du Colombier if(status) 1667dd7cddfSDavid du Colombier *status = 0; 1677dd7cddfSDavid du Colombier 168a41547ffSDavid du Colombier if(depth > 12) /* in a recursive loop? */ 169a41547ffSDavid du Colombier return nil; 170a41547ffSDavid du Colombier 1714f8f669cSDavid du Colombier procname = procgetname(); 1727dd7cddfSDavid du Colombier /* 1737dd7cddfSDavid du Colombier * hack for systems that don't have resolve search 1747dd7cddfSDavid du Colombier * lists. Just look up the simple name in the database. 1757dd7cddfSDavid du Colombier */ 1766dc4800dSDavid du Colombier if(!rooted && strchr(name, '.') == nil){ 1777dd7cddfSDavid du Colombier rp = nil; 1787dd7cddfSDavid du Colombier drp = domainlist(class); 179a41547ffSDavid du Colombier for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){ 1804f8f669cSDavid du Colombier snprint(nname, sizeof nname, "%s.%s", name, 1814f8f669cSDavid du Colombier nrp->ptr->name); 182a41547ffSDavid du Colombier rp = dnresolve(nname, class, type, req, cn, depth+1, 1834f8f669cSDavid du Colombier recurse, rooted, status); 184530fef66SDavid du Colombier lock(&dnlock); 185271b8d73SDavid du Colombier rrfreelist(rrremneg(&rp)); 186530fef66SDavid du Colombier unlock(&dnlock); 1877dd7cddfSDavid du Colombier } 1887dd7cddfSDavid du Colombier if(drp != nil) 1896aaebd7dSDavid du Colombier rrfreelist(drp); 1904f8f669cSDavid du Colombier procsetname(procname); 1914f8f669cSDavid du Colombier free(procname); 1923e12c5d1SDavid du Colombier return rp; 1937dd7cddfSDavid du Colombier } 1943e12c5d1SDavid du Colombier 1957dd7cddfSDavid du Colombier /* 1967dd7cddfSDavid du Colombier * try the name directly 1977dd7cddfSDavid du Colombier */ 1987dd7cddfSDavid du Colombier rp = dnresolve1(name, class, type, req, depth, recurse); 199225077b0SDavid du Colombier if(rp == nil) { 200225077b0SDavid du Colombier /* 201225077b0SDavid du Colombier * try it as a canonical name if we weren't told 202225077b0SDavid du Colombier * that the name didn't exist 203225077b0SDavid du Colombier */ 2047dd7cddfSDavid du Colombier dp = dnlookup(name, class, 0); 2054f8f669cSDavid du Colombier if(type != Tptr && dp->respcode != Rname) 206b8b25780SDavid du Colombier for(loops = 0; rp == nil && loops < Maxretries; loops++){ 207b8b25780SDavid du Colombier /* retry cname, then the actual type */ 208225077b0SDavid du Colombier rp = dnresolve1(name, class, Tcname, req, 209225077b0SDavid du Colombier depth, recurse); 2107dd7cddfSDavid du Colombier if(rp == nil) 2117dd7cddfSDavid du Colombier break; 21280ee5cbfSDavid du Colombier 213c73252aeSDavid du Colombier /* rp->host == nil shouldn't happen, but does */ 214c73252aeSDavid du Colombier if(rp->negative || rp->host == nil){ 21580ee5cbfSDavid du Colombier rrfreelist(rp); 21680ee5cbfSDavid du Colombier rp = nil; 21780ee5cbfSDavid du Colombier break; 21880ee5cbfSDavid du Colombier } 2197dd7cddfSDavid du Colombier 2207dd7cddfSDavid du Colombier name = rp->host->name; 221530fef66SDavid du Colombier lock(&dnlock); 2227dd7cddfSDavid du Colombier if(cn) 2237dd7cddfSDavid du Colombier rrcat(cn, rp); 2247dd7cddfSDavid du Colombier else 2257dd7cddfSDavid du Colombier rrfreelist(rp); 226530fef66SDavid du Colombier unlock(&dnlock); 2277dd7cddfSDavid du Colombier 228225077b0SDavid du Colombier rp = dnresolve1(name, class, type, req, 229225077b0SDavid du Colombier depth, recurse); 2307dd7cddfSDavid du Colombier } 2317dd7cddfSDavid du Colombier 2327dd7cddfSDavid du Colombier /* distinction between not found and not good */ 233d9924332SDavid du Colombier if(rp == nil && status != nil && dp->respcode != Rok) 2344f8f669cSDavid du Colombier *status = dp->respcode; 235225077b0SDavid du Colombier } 2364f8f669cSDavid du Colombier procsetname(procname); 2374f8f669cSDavid du Colombier free(procname); 2387dd7cddfSDavid du Colombier return randomize(rp); 2393e12c5d1SDavid du Colombier } 2403e12c5d1SDavid du Colombier 2413cbadd90SDavid du Colombier static void 2423cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req) 2433cbadd90SDavid du Colombier { 2443cbadd90SDavid du Colombier memset(qp, 0, sizeof *qp); 2454e7b9544SDavid du Colombier qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1; 2463cbadd90SDavid du Colombier qp->dp = dp; 2473cbadd90SDavid du Colombier qp->type = type; 248adb31a62SDavid du Colombier if (qp->type != type) 249adb31a62SDavid du Colombier dnslog("queryinit: bogus type %d", type); 2503cbadd90SDavid du Colombier qp->req = req; 2513cbadd90SDavid du Colombier qp->nsrp = nil; 2523cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; 2533cbadd90SDavid du Colombier qp->magic = Querymagic; 2543cbadd90SDavid du Colombier } 2553cbadd90SDavid du Colombier 2563cbadd90SDavid du Colombier static void 2573cbadd90SDavid du Colombier queryck(Query *qp) 2583cbadd90SDavid du Colombier { 2593cbadd90SDavid du Colombier assert(qp); 2603cbadd90SDavid du Colombier assert(qp->magic == Querymagic); 2613cbadd90SDavid du Colombier } 2623cbadd90SDavid du Colombier 2633cbadd90SDavid du Colombier static void 2644e7b9544SDavid du Colombier querydestroy(Query *qp) 2654e7b9544SDavid du Colombier { 2664e7b9544SDavid du Colombier queryck(qp); 267530fef66SDavid du Colombier /* leave udpfd open */ 2684e7b9544SDavid du Colombier if (qp->tcpfd > 0) 2694e7b9544SDavid du Colombier close(qp->tcpfd); 2704e7b9544SDavid du Colombier if (qp->tcpctlfd > 0) { 2714e7b9544SDavid du Colombier hangup(qp->tcpctlfd); 2724e7b9544SDavid du Colombier close(qp->tcpctlfd); 2734e7b9544SDavid du Colombier } 274a41547ffSDavid du Colombier free(qp->dest); 2754e7b9544SDavid du Colombier memset(qp, 0, sizeof *qp); /* prevent accidents */ 2764e7b9544SDavid du Colombier qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1; 2774e7b9544SDavid du Colombier } 2784e7b9544SDavid du Colombier 2794e7b9544SDavid du Colombier static void 2803cbadd90SDavid du Colombier destinit(Dest *p) 2813cbadd90SDavid du Colombier { 2823cbadd90SDavid du Colombier memset(p, 0, sizeof *p); 2833cbadd90SDavid du Colombier p->magic = Destmagic; 2843cbadd90SDavid du Colombier } 2853cbadd90SDavid du Colombier 2863cbadd90SDavid du Colombier static void 2873cbadd90SDavid du Colombier destck(Dest *p) 2883cbadd90SDavid du Colombier { 2893cbadd90SDavid du Colombier assert(p); 2903cbadd90SDavid du Colombier assert(p->magic == Destmagic); 2913cbadd90SDavid du Colombier } 2923cbadd90SDavid du Colombier 293a41547ffSDavid du Colombier /* 294a41547ffSDavid du Colombier * if the response to a query hasn't arrived within 100 ms., 295a41547ffSDavid du Colombier * it's unlikely to arrive at all. after 1 s., it's really unlikely. 296a41547ffSDavid du Colombier * queries for missing RRs are likely to produce time-outs rather than 297a41547ffSDavid du Colombier * negative responses, so cname and aaaa queries are likely to time out, 298a41547ffSDavid du Colombier * thus we don't wait very long for them. 299a41547ffSDavid du Colombier */ 300a41547ffSDavid du Colombier static void 301a41547ffSDavid du Colombier notestats(vlong start, int tmout, int type) 302a41547ffSDavid du Colombier { 303a41547ffSDavid du Colombier qlock(&stats); 304a41547ffSDavid du Colombier if (tmout) { 305a41547ffSDavid du Colombier stats.tmout++; 306a41547ffSDavid du Colombier if (type == Taaaa) 307a41547ffSDavid du Colombier stats.tmoutv6++; 308a41547ffSDavid du Colombier else if (type == Tcname) 309a41547ffSDavid du Colombier stats.tmoutcname++; 310a41547ffSDavid du Colombier } else { 311a41547ffSDavid du Colombier long wait10ths = NS2MS(nsec() - start) / 100; 312a41547ffSDavid du Colombier 31381730632SDavid du Colombier if (wait10ths <= 0) 314a41547ffSDavid du Colombier stats.under10ths[0]++; 315a41547ffSDavid du Colombier else if (wait10ths >= nelem(stats.under10ths)) 316a41547ffSDavid du Colombier stats.under10ths[nelem(stats.under10ths) - 1]++; 317a41547ffSDavid du Colombier else 318a41547ffSDavid du Colombier stats.under10ths[wait10ths]++; 319a41547ffSDavid du Colombier } 320a41547ffSDavid du Colombier qunlock(&stats); 321a41547ffSDavid du Colombier } 322a41547ffSDavid du Colombier 323a41547ffSDavid du Colombier static void 324a41547ffSDavid du Colombier noteinmem(void) 325a41547ffSDavid du Colombier { 326a41547ffSDavid du Colombier qlock(&stats); 327a41547ffSDavid du Colombier stats.answinmem++; 328a41547ffSDavid du Colombier qunlock(&stats); 329a41547ffSDavid du Colombier } 330a41547ffSDavid du Colombier 331c3617180SDavid du Colombier /* netquery with given name servers, free ns rrs when done */ 332c3617180SDavid du Colombier static int 333c3617180SDavid du Colombier netqueryns(Query *qp, int depth, RR *nsrp) 334c3617180SDavid du Colombier { 335c3617180SDavid du Colombier int rv; 336c3617180SDavid du Colombier 337c3617180SDavid du Colombier qp->nsrp = nsrp; 338c3617180SDavid du Colombier rv = netquery(qp, depth); 339c3617180SDavid du Colombier lock(&dnlock); 340c3617180SDavid du Colombier rrfreelist(nsrp); 341c3617180SDavid du Colombier unlock(&dnlock); 342c3617180SDavid du Colombier return rv; 343c3617180SDavid du Colombier } 344c3617180SDavid du Colombier 3453e12c5d1SDavid du Colombier static RR* 34646595261SDavid du Colombier issuequery(Query *qp, char *name, int class, int depth, int recurse) 3473e12c5d1SDavid du Colombier { 348f46c709fSDavid du Colombier char *cp; 349c73252aeSDavid du Colombier DN *nsdp; 35046595261SDavid du Colombier RR *rp, *nsrp, *dbnsrp; 3513cbadd90SDavid du Colombier 3523e12c5d1SDavid du Colombier /* 3534f8f669cSDavid du Colombier * if we're running as just a resolver, query our 3547dd7cddfSDavid du Colombier * designated name servers 355219b2ee8SDavid du Colombier */ 3564f8f669cSDavid du Colombier if(cfg.resolver){ 3577dd7cddfSDavid du Colombier nsrp = randomize(getdnsservers(class)); 358c3617180SDavid du Colombier if(nsrp != nil) 359c3617180SDavid du Colombier if(netqueryns(qp, depth+1, nsrp) > Answnone) 360c73252aeSDavid du Colombier return rrlookup(qp->dp, qp->type, OKneg); 3617dd7cddfSDavid du Colombier } 362219b2ee8SDavid du Colombier 363219b2ee8SDavid du Colombier /* 3643e12c5d1SDavid du Colombier * walk up the domain name looking for 3653e12c5d1SDavid du Colombier * a name server for the domain. 3663e12c5d1SDavid du Colombier */ 3673e12c5d1SDavid du Colombier for(cp = name; cp; cp = walkup(cp)){ 3687dd7cddfSDavid du Colombier /* 3697dd7cddfSDavid du Colombier * if this is a local (served by us) domain, 3707dd7cddfSDavid du Colombier * return answer 3717dd7cddfSDavid du Colombier */ 3727dd7cddfSDavid du Colombier dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0)); 3737dd7cddfSDavid du Colombier if(dbnsrp && dbnsrp->local){ 374c73252aeSDavid du Colombier rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl); 375c3617180SDavid du Colombier lock(&dnlock); 3767dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 377c3617180SDavid du Colombier unlock(&dnlock); 3787dd7cddfSDavid du Colombier return rp; 3797dd7cddfSDavid du Colombier } 3807dd7cddfSDavid du Colombier 3817dd7cddfSDavid du Colombier /* 3827dd7cddfSDavid du Colombier * if recursion isn't set, just accept local 3837dd7cddfSDavid du Colombier * entries 3847dd7cddfSDavid du Colombier */ 3857dd7cddfSDavid du Colombier if(recurse == Dontrecurse){ 386c3617180SDavid du Colombier if(dbnsrp) { 387c3617180SDavid du Colombier lock(&dnlock); 3887dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 389c3617180SDavid du Colombier unlock(&dnlock); 390c3617180SDavid du Colombier } 3917dd7cddfSDavid du Colombier continue; 3927dd7cddfSDavid du Colombier } 3937dd7cddfSDavid du Colombier 3947dd7cddfSDavid du Colombier /* look for ns in cache */ 3953e12c5d1SDavid du Colombier nsdp = dnlookup(cp, class, 0); 3967dd7cddfSDavid du Colombier nsrp = nil; 3973e12c5d1SDavid du Colombier if(nsdp) 3987dd7cddfSDavid du Colombier nsrp = randomize(rrlookup(nsdp, Tns, NOneg)); 3997dd7cddfSDavid du Colombier 4007dd7cddfSDavid du Colombier /* if the entry timed out, ignore it */ 4017dd7cddfSDavid du Colombier if(nsrp && nsrp->ttl < now){ 402c3617180SDavid du Colombier lock(&dnlock); 403c3617180SDavid du Colombier rrfreelistptr(&nsrp); 404c3617180SDavid du Colombier unlock(&dnlock); 4057dd7cddfSDavid du Colombier } 4063e12c5d1SDavid du Colombier 4073e12c5d1SDavid du Colombier if(nsrp){ 408c3617180SDavid du Colombier lock(&dnlock); 409c3617180SDavid du Colombier rrfreelistptr(&dbnsrp); 410c3617180SDavid du Colombier unlock(&dnlock); 4117dd7cddfSDavid du Colombier 4124f8f669cSDavid du Colombier /* query the name servers found in cache */ 413c3617180SDavid du Colombier if(netqueryns(qp, depth+1, nsrp) > Answnone) 414c73252aeSDavid du Colombier return rrlookup(qp->dp, qp->type, OKneg); 415c3617180SDavid du Colombier } else if(dbnsrp) 4167dd7cddfSDavid du Colombier /* try the name servers found in db */ 417c3617180SDavid du Colombier if(netqueryns(qp, depth+1, dbnsrp) > Answnone) 418c73252aeSDavid du Colombier return rrlookup(qp->dp, qp->type, NOneg); 4193e12c5d1SDavid du Colombier } 42046595261SDavid du Colombier return nil; 421c73252aeSDavid du Colombier } 422c73252aeSDavid du Colombier 423c73252aeSDavid du Colombier static RR* 424c73252aeSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth, 425c73252aeSDavid du Colombier int recurse) 426c73252aeSDavid du Colombier { 427c73252aeSDavid du Colombier Area *area; 428c73252aeSDavid du Colombier DN *dp; 429c73252aeSDavid du Colombier RR *rp; 430c73252aeSDavid du Colombier Query *qp; 431c73252aeSDavid du Colombier 432c73252aeSDavid du Colombier if(debug) 433c73252aeSDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class); 434c73252aeSDavid du Colombier 435c73252aeSDavid du Colombier /* only class Cin implemented so far */ 436c73252aeSDavid du Colombier if(class != Cin) 437c73252aeSDavid du Colombier return nil; 438c73252aeSDavid du Colombier 439c73252aeSDavid du Colombier dp = dnlookup(name, class, 1); 440c73252aeSDavid du Colombier 441c73252aeSDavid du Colombier /* 442c73252aeSDavid du Colombier * Try the cache first 443c73252aeSDavid du Colombier */ 444c73252aeSDavid du Colombier rp = rrlookup(dp, type, OKneg); 445c73252aeSDavid du Colombier if(rp) 446c73252aeSDavid du Colombier if(rp->db){ 447c73252aeSDavid du Colombier /* unauthoritative db entries are hints */ 448c73252aeSDavid du Colombier if(rp->auth) { 449c73252aeSDavid du Colombier noteinmem(); 450d9924332SDavid du Colombier if(debug) 451d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: auth rr in db", 452d9924332SDavid du Colombier getpid(), name, type, class); 453c73252aeSDavid du Colombier return rp; 454c73252aeSDavid du Colombier } 455c73252aeSDavid du Colombier } else 456c73252aeSDavid du Colombier /* cached entry must still be valid */ 457c73252aeSDavid du Colombier if(rp->ttl > now) 458c73252aeSDavid du Colombier /* but Tall entries are special */ 459c73252aeSDavid du Colombier if(type != Tall || rp->query == Tall) { 460c73252aeSDavid du Colombier noteinmem(); 461d9924332SDavid du Colombier if(debug) 462d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr not in db", 463d9924332SDavid du Colombier getpid(), name, type, class); 464c73252aeSDavid du Colombier return rp; 465c73252aeSDavid du Colombier } 466c3617180SDavid du Colombier lock(&dnlock); 467c73252aeSDavid du Colombier rrfreelist(rp); 468c3617180SDavid du Colombier unlock(&dnlock); 46946595261SDavid du Colombier rp = nil; /* accident prevention */ 47046595261SDavid du Colombier USED(rp); 471c73252aeSDavid du Colombier 472c73252aeSDavid du Colombier /* 473c73252aeSDavid du Colombier * try the cache for a canonical name. if found punt 474c73252aeSDavid du Colombier * since we'll find it during the canonical name search 475c73252aeSDavid du Colombier * in dnresolve(). 476c73252aeSDavid du Colombier */ 477c73252aeSDavid du Colombier if(type != Tcname){ 478c73252aeSDavid du Colombier rp = rrlookup(dp, Tcname, NOneg); 479c3617180SDavid du Colombier lock(&dnlock); 480c73252aeSDavid du Colombier rrfreelist(rp); 481c3617180SDavid du Colombier unlock(&dnlock); 482d9924332SDavid du Colombier if(rp){ 483d9924332SDavid du Colombier if(debug) 484d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname", 485d9924332SDavid du Colombier getpid(), name, type, class); 486c73252aeSDavid du Colombier return nil; 487c73252aeSDavid du Colombier } 488d9924332SDavid du Colombier } 489c73252aeSDavid du Colombier 490c73252aeSDavid du Colombier /* 491c73252aeSDavid du Colombier * if the domain name is within an area of ours, 492c73252aeSDavid du Colombier * we should have found its data in memory by now. 493c73252aeSDavid du Colombier */ 494c73252aeSDavid du Colombier area = inmyarea(dp->name); 495c73252aeSDavid du Colombier if (area || strncmp(dp->name, "local#", 6) == 0) { 496c73252aeSDavid du Colombier // char buf[32]; 497c73252aeSDavid du Colombier 498c73252aeSDavid du Colombier // dnslog("%s %s: no data in area %s", dp->name, 499c73252aeSDavid du Colombier // rrname(type, buf, sizeof buf), area->soarr->owner->name); 500c73252aeSDavid du Colombier return nil; 501c73252aeSDavid du Colombier } 502c73252aeSDavid du Colombier 503c73252aeSDavid du Colombier qp = emalloc(sizeof *qp); 504c73252aeSDavid du Colombier queryinit(qp, dp, type, req); 50546595261SDavid du Colombier rp = issuequery(qp, name, class, depth, recurse); 506c73252aeSDavid du Colombier querydestroy(qp); 507c73252aeSDavid du Colombier free(qp); 508d9924332SDavid du Colombier if(rp){ 509d9924332SDavid du Colombier if(debug) 510d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from query", 511d9924332SDavid du Colombier getpid(), name, type, class); 512c73252aeSDavid du Colombier return rp; 513d9924332SDavid du Colombier } 5143e12c5d1SDavid du Colombier 5157dd7cddfSDavid du Colombier /* settle for a non-authoritative answer */ 5167dd7cddfSDavid du Colombier rp = rrlookup(dp, type, OKneg); 517d9924332SDavid du Colombier if(rp){ 518d9924332SDavid du Colombier if(debug) 519d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup", 520d9924332SDavid du Colombier getpid(), name, type, class); 5217dd7cddfSDavid du Colombier return rp; 522d9924332SDavid du Colombier } 5237dd7cddfSDavid du Colombier 5247dd7cddfSDavid du Colombier /* noone answered. try the database, we might have a chance. */ 525d9924332SDavid du Colombier rp = dblookup(name, class, type, 0, 0); 526d9924332SDavid du Colombier if (rp) { 527d9924332SDavid du Colombier if(debug) 528d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from dblookup", 529d9924332SDavid du Colombier getpid(), name, type, class); 530d9924332SDavid du Colombier }else{ 531d9924332SDavid du Colombier if(debug) 532d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: no rr from dblookup; crapped out", 533d9924332SDavid du Colombier getpid(), name, type, class); 534d9924332SDavid du Colombier } 535d9924332SDavid du Colombier return rp; 5363e12c5d1SDavid du Colombier } 5373e12c5d1SDavid du Colombier 5383e12c5d1SDavid du Colombier /* 5394f8f669cSDavid du Colombier * walk a domain name one element to the right. 5404f8f669cSDavid du Colombier * return a pointer to that element. 5413e12c5d1SDavid du Colombier * in other words, return a pointer to the parent domain name. 5423e12c5d1SDavid du Colombier */ 5433e12c5d1SDavid du Colombier char* 5443e12c5d1SDavid du Colombier walkup(char *name) 5453e12c5d1SDavid du Colombier { 5463e12c5d1SDavid du Colombier char *cp; 5473e12c5d1SDavid du Colombier 5483e12c5d1SDavid du Colombier cp = strchr(name, '.'); 5493e12c5d1SDavid du Colombier if(cp) 5503e12c5d1SDavid du Colombier return cp+1; 5513e12c5d1SDavid du Colombier else if(*name) 5523e12c5d1SDavid du Colombier return ""; 5533e12c5d1SDavid du Colombier else 5543e12c5d1SDavid du Colombier return 0; 5553e12c5d1SDavid du Colombier } 5563e12c5d1SDavid du Colombier 5573e12c5d1SDavid du Colombier /* 558a41547ffSDavid du Colombier * Get a udp port for sending requests and reading replies. Put the port 5593e12c5d1SDavid du Colombier * into "headers" mode. 5603e12c5d1SDavid du Colombier */ 5613e12c5d1SDavid du Colombier static char *hmsg = "headers"; 5623e12c5d1SDavid du Colombier 563dc5a79c1SDavid du Colombier int 5644f8f669cSDavid du Colombier udpport(char *mtpt) 5653e12c5d1SDavid du Colombier { 5663e12c5d1SDavid du Colombier int fd, ctl; 5674f8f669cSDavid du Colombier char ds[64], adir[64]; 5683e12c5d1SDavid du Colombier 5693e12c5d1SDavid du Colombier /* get a udp port */ 5704f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net")); 5717dd7cddfSDavid du Colombier ctl = announce(ds, adir); 5727dd7cddfSDavid du Colombier if(ctl < 0){ 5737dd7cddfSDavid du Colombier /* warning("can't get udp port"); */ 574bd389b36SDavid du Colombier return -1; 575bd389b36SDavid du Colombier } 5763e12c5d1SDavid du Colombier 5773e12c5d1SDavid du Colombier /* turn on header style interface */ 578410ea80bSDavid du Colombier if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){ 5797dd7cddfSDavid du Colombier close(ctl); 580bd389b36SDavid du Colombier warning(hmsg); 581bd389b36SDavid du Colombier return -1; 582bd389b36SDavid du Colombier } 5833e12c5d1SDavid du Colombier 5847dd7cddfSDavid du Colombier /* grab the data file */ 5854f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/data", adir); 5867dd7cddfSDavid du Colombier fd = open(ds, ORDWR); 5873e12c5d1SDavid du Colombier close(ctl); 5884f8f669cSDavid du Colombier if(fd < 0) 5894f8f669cSDavid du Colombier warning("can't open udp port %s: %r", ds); 5903e12c5d1SDavid du Colombier return fd; 5913e12c5d1SDavid du Colombier } 5923e12c5d1SDavid du Colombier 593d9924332SDavid du Colombier void 594d9924332SDavid du Colombier initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno) 595d9924332SDavid du Colombier { 596d9924332SDavid du Colombier mp->flags = flags; 597d9924332SDavid du Colombier mp->id = reqno; 598d9924332SDavid du Colombier mp->qd = rp; 59959f7772cSDavid du Colombier if(rp != nil) 60059f7772cSDavid du Colombier mp->qdcount = 1; 601d9924332SDavid du Colombier } 602d9924332SDavid du Colombier 603d9924332SDavid du Colombier DNSmsg * 604d9924332SDavid du Colombier newdnsmsg(RR *rp, int flags, ushort reqno) 605d9924332SDavid du Colombier { 606d9924332SDavid du Colombier DNSmsg *mp; 607d9924332SDavid du Colombier 608d9924332SDavid du Colombier mp = emalloc(sizeof *mp); 609d9924332SDavid du Colombier initdnsmsg(mp, rp, flags, reqno); 610d9924332SDavid du Colombier return mp; 611d9924332SDavid du Colombier } 612d9924332SDavid du Colombier 613d6d99297SDavid du Colombier /* generate a DNS UDP query packet */ 614dc5a79c1SDavid du Colombier int 615dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno) 6163e12c5d1SDavid du Colombier { 6173e12c5d1SDavid du Colombier DNSmsg m; 6183e12c5d1SDavid du Colombier int len; 619f27a9a5aSDavid du Colombier Udphdr *uh = (Udphdr*)buf; 620d9924332SDavid du Colombier RR *rp; 6213e12c5d1SDavid du Colombier 6223e12c5d1SDavid du Colombier /* stuff port number into output buffer */ 6233cbadd90SDavid du Colombier memset(uh, 0, sizeof *uh); 6247dd7cddfSDavid du Colombier hnputs(uh->rport, 53); 6253e12c5d1SDavid du Colombier 6263e12c5d1SDavid du Colombier /* make request and convert it to output format */ 6273cbadd90SDavid du Colombier memset(&m, 0, sizeof m); 628d9924332SDavid du Colombier rp = rralloc(type); 629d9924332SDavid du Colombier rp->owner = dp; 630d9924332SDavid du Colombier initdnsmsg(&m, rp, flags, reqno); 631f27a9a5aSDavid du Colombier len = convDNS2M(&m, &buf[Udphdrsize], Maxudp); 632c3617180SDavid du Colombier rrfreelistptr(&m.qd); 6336aaebd7dSDavid du Colombier memset(&m, 0, sizeof m); /* cause trouble */ 6343e12c5d1SDavid du Colombier return len; 6353e12c5d1SDavid du Colombier } 6363e12c5d1SDavid du Colombier 637a41547ffSDavid du Colombier void 6387dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp) 6393e12c5d1SDavid du Colombier { 640c3617180SDavid du Colombier lock(&dnlock); 641e6d9d902SDavid du Colombier rrfreelistptr(&mp->qd); 642e6d9d902SDavid du Colombier rrfreelistptr(&mp->an); 643e6d9d902SDavid du Colombier rrfreelistptr(&mp->ns); 644e6d9d902SDavid du Colombier rrfreelistptr(&mp->ar); 645c3617180SDavid du Colombier unlock(&dnlock); 646c3617180SDavid du Colombier mp->qdcount = mp->ancount = mp->nscount = mp->arcount = 0; 6477dd7cddfSDavid du Colombier } 6487dd7cddfSDavid du Colombier 649*82f6abeeSDavid du Colombier /* timed read of reply. sets srcip. ibuf must be 64K to handle tcp answers. */ 6503cbadd90SDavid du Colombier static int 651b8b25780SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp, 6523cbadd90SDavid du Colombier uchar *srcip) 6533cbadd90SDavid du Colombier { 6543cbadd90SDavid du Colombier int len, fd; 655a41547ffSDavid du Colombier long ms; 656a41547ffSDavid du Colombier vlong startns = nsec(); 6573cbadd90SDavid du Colombier uchar *reply; 6583cbadd90SDavid du Colombier uchar lenbuf[2]; 6593cbadd90SDavid du Colombier 6603cbadd90SDavid du Colombier len = -1; /* pessimism */ 661b8b25780SDavid du Colombier ms = endms - NS2MS(startns); 662b8b25780SDavid du Colombier if (ms <= 0) 663b8b25780SDavid du Colombier return -1; /* taking too long */ 664b8b25780SDavid du Colombier 665b8b25780SDavid du Colombier reply = ibuf; 6664e7b9544SDavid du Colombier memset(srcip, 0, IPaddrlen); 667b8b25780SDavid du Colombier alarm(ms); 668a41547ffSDavid du Colombier if (medium == Udp) 6694e7b9544SDavid du Colombier if (qp->udpfd <= 0) 6704e7b9544SDavid du Colombier dnslog("readnet: qp->udpfd closed"); 6714e7b9544SDavid du Colombier else { 672f27a9a5aSDavid du Colombier len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin); 673a41547ffSDavid du Colombier alarm(0); 674a41547ffSDavid du Colombier notestats(startns, len < 0, qp->type); 6754e7b9544SDavid du Colombier if (len >= IPaddrlen) 6764e7b9544SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 677f27a9a5aSDavid du Colombier if (len >= Udphdrsize) { 678f27a9a5aSDavid du Colombier len -= Udphdrsize; 679f27a9a5aSDavid du Colombier reply += Udphdrsize; 6804e7b9544SDavid du Colombier } 6814e7b9544SDavid du Colombier } 682a41547ffSDavid du Colombier else { 6833cbadd90SDavid du Colombier if (!qp->tcpset) 6843cbadd90SDavid du Colombier dnslog("readnet: tcp params not set"); 6853cbadd90SDavid du Colombier fd = qp->tcpfd; 6863cbadd90SDavid du Colombier if (fd <= 0) 6873cbadd90SDavid du Colombier dnslog("readnet: %s: tcp fd unset for dest %I", 6883cbadd90SDavid du Colombier qp->dp->name, qp->tcpip); 6893cbadd90SDavid du Colombier else if (readn(fd, lenbuf, 2) != 2) { 690e6d9d902SDavid du Colombier dnslog("readnet: short read of 2-byte tcp msg size from %I", 6913cbadd90SDavid du Colombier qp->tcpip); 692a41547ffSDavid du Colombier /* probably a time-out */ 693a41547ffSDavid du Colombier notestats(startns, 1, qp->type); 6943cbadd90SDavid du Colombier } else { 6953cbadd90SDavid du Colombier len = lenbuf[0]<<8 | lenbuf[1]; 6963cbadd90SDavid du Colombier if (readn(fd, ibuf, len) != len) { 6973cbadd90SDavid du Colombier dnslog("readnet: short read of tcp data from %I", 6983cbadd90SDavid du Colombier qp->tcpip); 699a41547ffSDavid du Colombier /* probably a time-out */ 700a41547ffSDavid du Colombier notestats(startns, 1, qp->type); 7013cbadd90SDavid du Colombier len = -1; 7023cbadd90SDavid du Colombier } 7033cbadd90SDavid du Colombier } 7043cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 7053cbadd90SDavid du Colombier } 706b8b25780SDavid du Colombier alarm(0); 7073cbadd90SDavid du Colombier *replyp = reply; 7083cbadd90SDavid du Colombier return len; 7093cbadd90SDavid du Colombier } 7103cbadd90SDavid du Colombier 7117dd7cddfSDavid du Colombier /* 7123cbadd90SDavid du Colombier * read replies to a request and remember the rrs in the answer(s). 7133cbadd90SDavid du Colombier * ignore any of the wrong type. 714b8b25780SDavid du Colombier * wait at most until endms. 7157dd7cddfSDavid du Colombier */ 7167dd7cddfSDavid du Colombier static int 7173cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp, 718b8b25780SDavid du Colombier uvlong endms) 7197dd7cddfSDavid du Colombier { 720b8b25780SDavid du Colombier int len; 7217dd7cddfSDavid du Colombier char *err; 722a41547ffSDavid du Colombier char tbuf[32]; 7233cbadd90SDavid du Colombier uchar *reply; 7243cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 7253e12c5d1SDavid du Colombier RR *rp; 7267dd7cddfSDavid du Colombier 7273cbadd90SDavid du Colombier queryck(qp); 7283cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 729a41547ffSDavid du Colombier memset(srcip, 0, sizeof srcip); 730a41547ffSDavid du Colombier if (0) 731a41547ffSDavid du Colombier len = -1; 732b8b25780SDavid du Colombier for (; timems() < endms && 733b8b25780SDavid du Colombier (len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0; 7343cbadd90SDavid du Colombier freeanswers(mp)){ 7357dd7cddfSDavid du Colombier /* convert into internal format */ 7363cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 7373cbadd90SDavid du Colombier err = convM2DNS(reply, len, mp, nil); 738d6d99297SDavid du Colombier if (mp->flags & Ftrunc) { 739a41547ffSDavid du Colombier free(err); 740a41547ffSDavid du Colombier freeanswers(mp); 741a41547ffSDavid du Colombier /* notify our caller to retry the query via tcp. */ 7423cbadd90SDavid du Colombier return -1; 7433cbadd90SDavid du Colombier } else if(err){ 7443cbadd90SDavid du Colombier dnslog("readreply: %s: input err, len %d: %s: %I", 7453cbadd90SDavid du Colombier qp->dp->name, len, err, srcip); 7463cbadd90SDavid du Colombier free(err); 7477dd7cddfSDavid du Colombier continue; 7487dd7cddfSDavid du Colombier } 7497dd7cddfSDavid du Colombier if(debug) 7503cbadd90SDavid du Colombier logreply(qp->req->id, srcip, mp); 7517dd7cddfSDavid du Colombier 7527dd7cddfSDavid du Colombier /* answering the right question? */ 7533cbadd90SDavid du Colombier if(mp->id != req) 7543cbadd90SDavid du Colombier dnslog("%d: id %d instead of %d: %I", qp->req->id, 7553cbadd90SDavid du Colombier mp->id, req, srcip); 7563cbadd90SDavid du Colombier else if(mp->qd == 0) 7573cbadd90SDavid du Colombier dnslog("%d: no question RR: %I", qp->req->id, srcip); 7583cbadd90SDavid du Colombier else if(mp->qd->owner != qp->dp) 7593cbadd90SDavid du Colombier dnslog("%d: owner %s instead of %s: %I", qp->req->id, 7603cbadd90SDavid du Colombier mp->qd->owner->name, qp->dp->name, srcip); 7613cbadd90SDavid du Colombier else if(mp->qd->type != qp->type) 7623cbadd90SDavid du Colombier dnslog("%d: qp->type %d instead of %d: %I", 7633cbadd90SDavid du Colombier qp->req->id, mp->qd->type, qp->type, srcip); 7643cbadd90SDavid du Colombier else { 7657dd7cddfSDavid du Colombier /* remember what request this is in answer to */ 7667dd7cddfSDavid du Colombier for(rp = mp->an; rp; rp = rp->next) 7673cbadd90SDavid du Colombier rp->query = qp->type; 768b8b25780SDavid du Colombier return 0; 7697dd7cddfSDavid du Colombier } 7707dd7cddfSDavid du Colombier } 771b8b25780SDavid du Colombier if (timems() >= endms) { 772a41547ffSDavid du Colombier ; /* query expired */ 773a41547ffSDavid du Colombier } else if (0) { 774a41547ffSDavid du Colombier /* this happens routinely when a read times out */ 775a41547ffSDavid du Colombier dnslog("readreply: %s type %s: ns %I read error or eof " 776a41547ffSDavid du Colombier "(returned %d): %r", qp->dp->name, rrname(qp->type, 777a41547ffSDavid du Colombier tbuf, sizeof tbuf), srcip, len); 778a41547ffSDavid du Colombier if (medium == Udp) 779a41547ffSDavid du Colombier for (rp = qp->nsrp; rp != nil; rp = rp->next) 780a41547ffSDavid du Colombier if (rp->type == Tns) 781a41547ffSDavid du Colombier dnslog("readreply: %s: query sent to " 782a41547ffSDavid du Colombier "ns %s", qp->dp->name, 783a41547ffSDavid du Colombier rp->host->name); 784a41547ffSDavid du Colombier } 7853cbadd90SDavid du Colombier return -1; 7863cbadd90SDavid du Colombier } 7877dd7cddfSDavid du Colombier 7887dd7cddfSDavid du Colombier /* 7897dd7cddfSDavid du Colombier * return non-0 if first list includes second list 7907dd7cddfSDavid du Colombier */ 7917dd7cddfSDavid du Colombier int 7927dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2) 7937dd7cddfSDavid du Colombier { 7947dd7cddfSDavid du Colombier RR *trp1, *trp2; 7957dd7cddfSDavid du Colombier 7967dd7cddfSDavid du Colombier for(trp2 = rp2; trp2; trp2 = trp2->next){ 7973cbadd90SDavid du Colombier for(trp1 = rp1; trp1; trp1 = trp1->next) 7987dd7cddfSDavid du Colombier if(trp1->type == trp2->type) 7997dd7cddfSDavid du Colombier if(trp1->host == trp2->host) 8007dd7cddfSDavid du Colombier if(trp1->owner == trp2->owner) 8017dd7cddfSDavid du Colombier break; 8024f8f669cSDavid du Colombier if(trp1 == nil) 8037dd7cddfSDavid du Colombier return 0; 8047dd7cddfSDavid du Colombier } 8057dd7cddfSDavid du Colombier return 1; 8067dd7cddfSDavid du Colombier } 8077dd7cddfSDavid du Colombier 8087dd7cddfSDavid du Colombier 8096b6b9ac8SDavid du Colombier /* 8106b6b9ac8SDavid du Colombier * return multicast version if any 8116b6b9ac8SDavid du Colombier */ 8126b6b9ac8SDavid du Colombier int 8136b6b9ac8SDavid du Colombier ipisbm(uchar *ip) 8146b6b9ac8SDavid du Colombier { 8156b6b9ac8SDavid du Colombier if(isv4(ip)){ 8164f8f669cSDavid du Colombier if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 || 8174f8f669cSDavid du Colombier ipcmp(ip, IPv4bcast) == 0) 8186b6b9ac8SDavid du Colombier return 4; 8194f8f669cSDavid du Colombier } else 8206b6b9ac8SDavid du Colombier if(ip[0] == 0xff) 8216b6b9ac8SDavid du Colombier return 6; 82228a8a86bSDavid du Colombier return 0; 82328a8a86bSDavid du Colombier } 82428a8a86bSDavid du Colombier 8257dd7cddfSDavid du Colombier /* 82659f7772cSDavid du Colombier * Get next server address(es) into qp->dest[nd] and beyond 8277dd7cddfSDavid du Colombier */ 8287dd7cddfSDavid du Colombier static int 8293cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth) 8307dd7cddfSDavid du Colombier { 8317dd7cddfSDavid du Colombier RR *rp, *arp, *trp; 8327dd7cddfSDavid du Colombier Dest *cur; 8337dd7cddfSDavid du Colombier 834c3617180SDavid du Colombier if(nd >= Maxdest) /* dest array is full? */ 835c3617180SDavid du Colombier return Maxdest - 1; 8367dd7cddfSDavid du Colombier 8377dd7cddfSDavid du Colombier /* 8387dd7cddfSDavid du Colombier * look for a server whose address we already know. 8397dd7cddfSDavid du Colombier * if we find one, mark it so we ignore this on 8407dd7cddfSDavid du Colombier * subsequent passes. 8417dd7cddfSDavid du Colombier */ 8427dd7cddfSDavid du Colombier arp = 0; 8433cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 84434f77ae3SDavid du Colombier assert(rp->magic == RRmagic); 8457dd7cddfSDavid du Colombier if(rp->marker) 8467dd7cddfSDavid du Colombier continue; 8477dd7cddfSDavid du Colombier arp = rrlookup(rp->host, Ta, NOneg); 848d9924332SDavid du Colombier if(arp == nil) 849d9924332SDavid du Colombier arp = rrlookup(rp->host, Taaaa, NOneg); 8507dd7cddfSDavid du Colombier if(arp){ 8517dd7cddfSDavid du Colombier rp->marker = 1; 8527dd7cddfSDavid du Colombier break; 8537dd7cddfSDavid du Colombier } 8547dd7cddfSDavid du Colombier arp = dblookup(rp->host->name, Cin, Ta, 0, 0); 855d9924332SDavid du Colombier if(arp == nil) 856d9924332SDavid du Colombier arp = dblookup(rp->host->name, Cin, Taaaa, 0, 0); 8577dd7cddfSDavid du Colombier if(arp){ 8587dd7cddfSDavid du Colombier rp->marker = 1; 8597dd7cddfSDavid du Colombier break; 8607dd7cddfSDavid du Colombier } 8617dd7cddfSDavid du Colombier } 8627dd7cddfSDavid du Colombier 8637dd7cddfSDavid du Colombier /* 8647dd7cddfSDavid du Colombier * if the cache and database lookup didn't find any new 8657dd7cddfSDavid du Colombier * server addresses, try resolving one via the network. 8667dd7cddfSDavid du Colombier * Mark any we try to resolve so we don't try a second time. 8677dd7cddfSDavid du Colombier */ 8684f8f669cSDavid du Colombier if(arp == 0) 8693cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 8707dd7cddfSDavid du Colombier if(rp->marker) 8717dd7cddfSDavid du Colombier continue; 8727dd7cddfSDavid du Colombier rp->marker = 1; 8737dd7cddfSDavid du Colombier 8747dd7cddfSDavid du Colombier /* 8757dd7cddfSDavid du Colombier * avoid loops looking up a server under itself 8767dd7cddfSDavid du Colombier */ 8777dd7cddfSDavid du Colombier if(subsume(rp->owner->name, rp->host->name)) 8787dd7cddfSDavid du Colombier continue; 8797dd7cddfSDavid du Colombier 8803cbadd90SDavid du Colombier arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0, 8814f8f669cSDavid du Colombier depth+1, Recurse, 1, 0); 882d9924332SDavid du Colombier if(arp == nil) 883d9924332SDavid du Colombier arp = dnresolve(rp->host->name, Cin, Taaaa, 884d9924332SDavid du Colombier qp->req, 0, depth+1, Recurse, 1, 0); 885530fef66SDavid du Colombier lock(&dnlock); 8867dd7cddfSDavid du Colombier rrfreelist(rrremneg(&arp)); 887530fef66SDavid du Colombier unlock(&dnlock); 8887dd7cddfSDavid du Colombier if(arp) 8897dd7cddfSDavid du Colombier break; 8907dd7cddfSDavid du Colombier } 8917dd7cddfSDavid du Colombier 8927dd7cddfSDavid du Colombier /* use any addresses that we found */ 8933cbadd90SDavid du Colombier for(trp = arp; trp && nd < Maxdest; trp = trp->next){ 8943cbadd90SDavid du Colombier cur = &qp->dest[nd]; 8957dd7cddfSDavid du Colombier parseip(cur->a, trp->ip->name); 8964f8f669cSDavid du Colombier /* 8974f8f669cSDavid du Colombier * straddling servers can reject all nameservers if they are all 8984f8f669cSDavid du Colombier * inside, so be sure to list at least one outside ns at 8994f8f669cSDavid du Colombier * the end of the ns list in /lib/ndb for `dom='. 9004f8f669cSDavid du Colombier */ 9014f8f669cSDavid du Colombier if (ipisbm(cur->a) || 9023cbadd90SDavid du Colombier cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a)) 9036b6b9ac8SDavid du Colombier continue; 9047dd7cddfSDavid du Colombier cur->nx = 0; 9057dd7cddfSDavid du Colombier cur->s = trp->owner; 9067dd7cddfSDavid du Colombier cur->code = Rtimeout; 9076b6b9ac8SDavid du Colombier nd++; 9087dd7cddfSDavid du Colombier } 909c3617180SDavid du Colombier lock(&dnlock); 9107dd7cddfSDavid du Colombier rrfreelist(arp); 911c3617180SDavid du Colombier unlock(&dnlock); 9127dd7cddfSDavid du Colombier return nd; 9137dd7cddfSDavid du Colombier } 9147dd7cddfSDavid du Colombier 9157dd7cddfSDavid du Colombier /* 9167dd7cddfSDavid du Colombier * cache negative responses 9177dd7cddfSDavid du Colombier */ 9187dd7cddfSDavid du Colombier static void 9197dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr) 9207dd7cddfSDavid du Colombier { 9217dd7cddfSDavid du Colombier RR *rp; 9227dd7cddfSDavid du Colombier DN *soaowner; 9239a747e4fSDavid du Colombier ulong ttl; 9247dd7cddfSDavid du Colombier 925f46c709fSDavid du Colombier stats.negcached++; 926f46c709fSDavid du Colombier 9274f8f669cSDavid du Colombier /* no cache time specified, don't make anything up */ 9287dd7cddfSDavid du Colombier if(soarr != nil){ 929c3617180SDavid du Colombier lock(&dnlock); 930e6d9d902SDavid du Colombier if(soarr->next != nil) 931e6d9d902SDavid du Colombier rrfreelistptr(&soarr->next); 932c3617180SDavid du Colombier unlock(&dnlock); 9337dd7cddfSDavid du Colombier soaowner = soarr->owner; 9347dd7cddfSDavid du Colombier } else 9357dd7cddfSDavid du Colombier soaowner = nil; 9367dd7cddfSDavid du Colombier 9379a747e4fSDavid du Colombier /* the attach can cause soarr to be freed so mine it now */ 9389a747e4fSDavid du Colombier if(soarr != nil && soarr->soa != nil) 9399a747e4fSDavid du Colombier ttl = soarr->soa->minttl+now; 9409a747e4fSDavid du Colombier else 9419a747e4fSDavid du Colombier ttl = 5*Min; 9429a747e4fSDavid du Colombier 9437dd7cddfSDavid du Colombier /* add soa and negative RR to the database */ 9446dc4800dSDavid du Colombier rrattach(soarr, Authoritative); 9457dd7cddfSDavid du Colombier 9467dd7cddfSDavid du Colombier rp = rralloc(type); 9477dd7cddfSDavid du Colombier rp->owner = dp; 9487dd7cddfSDavid du Colombier rp->negative = 1; 9497dd7cddfSDavid du Colombier rp->negsoaowner = soaowner; 9507dd7cddfSDavid du Colombier rp->negrcode = rcode; 9519a747e4fSDavid du Colombier rp->ttl = ttl; 9526dc4800dSDavid du Colombier rrattach(rp, Authoritative); 9537dd7cddfSDavid du Colombier } 9547dd7cddfSDavid du Colombier 9554f8f669cSDavid du Colombier static int 9564f8f669cSDavid du Colombier setdestoutns(Dest *p, int n) 9574f8f669cSDavid du Colombier { 9584f8f669cSDavid du Colombier uchar *outns = outsidens(n); 9594f8f669cSDavid du Colombier 9603cbadd90SDavid du Colombier destck(p); 9613cbadd90SDavid du Colombier destinit(p); 9624f8f669cSDavid du Colombier if (outns == nil) { 9634f8f669cSDavid du Colombier if (n == 0) 9644f8f669cSDavid du Colombier dnslog("[%d] no outside-ns in ndb", getpid()); 9654f8f669cSDavid du Colombier return -1; 9664f8f669cSDavid du Colombier } 9674f8f669cSDavid du Colombier memmove(p->a, outns, sizeof p->a); 9684f8f669cSDavid du Colombier p->s = dnlookup("outside-ns-ips", Cin, 1); 9694f8f669cSDavid du Colombier return 0; 9704f8f669cSDavid du Colombier } 9714f8f669cSDavid du Colombier 9727dd7cddfSDavid du Colombier /* 9733cbadd90SDavid du Colombier * issue query via UDP or TCP as appropriate. 9743cbadd90SDavid du Colombier * for TCP, returns with qp->tcpip set from udppkt header. 9757dd7cddfSDavid du Colombier */ 9767dd7cddfSDavid du Colombier static int 9773cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len) 9787dd7cddfSDavid du Colombier { 979a41547ffSDavid du Colombier int rv = -1, nfd; 9803cbadd90SDavid du Colombier char *domain; 981cc499a30SDavid du Colombier char conndir[40], net[40]; 982a41547ffSDavid du Colombier uchar belen[2]; 9833cbadd90SDavid du Colombier NetConnInfo *nci; 9844f8f669cSDavid du Colombier 9853cbadd90SDavid du Colombier queryck(qp); 9864e153993SDavid du Colombier domain = smprint("%I", udppkt); 9874e153993SDavid du Colombier if (myaddr(domain)) { 9884e153993SDavid du Colombier dnslog("mydnsquery: trying to send to myself (%s); bzzzt", 9894e153993SDavid du Colombier domain); 9904e153993SDavid du Colombier free(domain); 9914e153993SDavid du Colombier return rv; 9924e153993SDavid du Colombier } 9934e153993SDavid du Colombier 9943cbadd90SDavid du Colombier switch (medium) { 9953cbadd90SDavid du Colombier case Udp: 9964e153993SDavid du Colombier free(domain); 997a41547ffSDavid du Colombier nfd = dup(qp->udpfd, -1); 998a41547ffSDavid du Colombier if (nfd < 0) { 999a41547ffSDavid du Colombier warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd); 1000a41547ffSDavid du Colombier close(qp->udpfd); /* ensure it's closed */ 1001a41547ffSDavid du Colombier qp->udpfd = -1; /* poison it */ 1002a41547ffSDavid du Colombier return rv; 1003a41547ffSDavid du Colombier } 1004a41547ffSDavid du Colombier close(nfd); 1005a41547ffSDavid du Colombier 10064e7b9544SDavid du Colombier if (qp->udpfd <= 0) 1007a41547ffSDavid du Colombier dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd); 10084e7b9544SDavid du Colombier else { 1009f27a9a5aSDavid du Colombier if (write(qp->udpfd, udppkt, len+Udphdrsize) != 1010f27a9a5aSDavid du Colombier len+Udphdrsize) 1011a41547ffSDavid du Colombier warning("sending udp msg: %r"); 1012a41547ffSDavid du Colombier else { 1013a41547ffSDavid du Colombier stats.qsent++; 10143cbadd90SDavid du Colombier rv = 0; 10154e7b9544SDavid du Colombier } 1016a41547ffSDavid du Colombier } 10173cbadd90SDavid du Colombier break; 10183cbadd90SDavid du Colombier case Tcp: 10193cbadd90SDavid du Colombier /* send via TCP & keep fd around for reply */ 1020cc499a30SDavid du Colombier snprint(net, sizeof net, "%s/tcp", 1021cc499a30SDavid du Colombier (mntpt[0] != '\0'? mntpt: "/net")); 10223cbadd90SDavid du Colombier alarm(10*1000); 1023cc499a30SDavid du Colombier qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil, 10243cbadd90SDavid du Colombier conndir, &qp->tcpctlfd); 10253cbadd90SDavid du Colombier alarm(0); 10263cbadd90SDavid du Colombier if (qp->tcpfd < 0) { 10273cbadd90SDavid du Colombier dnslog("can't dial tcp!%s!dns: %r", domain); 1028a41547ffSDavid du Colombier free(domain); 1029a41547ffSDavid du Colombier break; 1030a41547ffSDavid du Colombier } 1031a41547ffSDavid du Colombier free(domain); 10323cbadd90SDavid du Colombier nci = getnetconninfo(conndir, qp->tcpfd); 10333cbadd90SDavid du Colombier if (nci) { 10343cbadd90SDavid du Colombier parseip(qp->tcpip, nci->rsys); 10353cbadd90SDavid du Colombier freenetconninfo(nci); 10363cbadd90SDavid du Colombier } else 10373cbadd90SDavid du Colombier dnslog("mydnsquery: getnetconninfo failed"); 10383cbadd90SDavid du Colombier qp->tcpset = 1; 10393e12c5d1SDavid du Colombier 10403cbadd90SDavid du Colombier belen[0] = len >> 8; 10413cbadd90SDavid du Colombier belen[1] = len; 10423cbadd90SDavid du Colombier if (write(qp->tcpfd, belen, 2) != 2 || 1043f27a9a5aSDavid du Colombier write(qp->tcpfd, udppkt + Udphdrsize, len) != len) 1044a41547ffSDavid du Colombier warning("sending tcp msg: %r"); 10453cbadd90SDavid du Colombier break; 10463cbadd90SDavid du Colombier default: 10473cbadd90SDavid du Colombier sysfatal("mydnsquery: bad medium"); 10483cbadd90SDavid du Colombier } 10493cbadd90SDavid du Colombier return rv; 10503cbadd90SDavid du Colombier } 10517dd7cddfSDavid du Colombier 10523e12c5d1SDavid du Colombier /* 10533cbadd90SDavid du Colombier * send query to all UDP destinations or one TCP destination, 10543cbadd90SDavid du Colombier * taken from obuf (udp packet) header 10553e12c5d1SDavid du Colombier */ 10563cbadd90SDavid du Colombier static int 10573cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len) 10583cbadd90SDavid du Colombier { 10593cbadd90SDavid du Colombier int j, n; 10603cbadd90SDavid du Colombier char buf[32]; 10613cbadd90SDavid du Colombier Dest *p; 10623e12c5d1SDavid du Colombier 10633cbadd90SDavid du Colombier queryck(qp); 1064b8b25780SDavid du Colombier if(timems() >= qp->req->aborttime) 10653cbadd90SDavid du Colombier return -1; 10663cbadd90SDavid du Colombier 10673cbadd90SDavid du Colombier /* 10683cbadd90SDavid du Colombier * get a nameserver address if we need one. 10693cbadd90SDavid du Colombier * serveraddrs populates qp->dest. 10703cbadd90SDavid du Colombier */ 10713cbadd90SDavid du Colombier p = qp->dest; 10723cbadd90SDavid du Colombier destck(p); 1073c3617180SDavid du Colombier if (qp->ndest < 0 || qp->ndest > Maxdest) { 10743cbadd90SDavid du Colombier dnslog("qp->ndest %d out of range", qp->ndest); 1075c3617180SDavid du Colombier abort(); 1076c3617180SDavid du Colombier } 1077c3617180SDavid du Colombier /* 1078c3617180SDavid du Colombier * we're to transmit to more destinations than we currently have, 1079c3617180SDavid du Colombier * so get another. 1080c3617180SDavid du Colombier */ 10813468a491SDavid du Colombier if (qp->ndest > qp->curdest - p) { 10823468a491SDavid du Colombier j = serveraddrs(qp, qp->curdest - p, depth); 10833468a491SDavid du Colombier if (j < 0 || j >= Maxdest) { 10843468a491SDavid du Colombier dnslog("serveraddrs() result %d out of range", j); 10853468a491SDavid du Colombier abort(); 10863468a491SDavid du Colombier } 10873468a491SDavid du Colombier qp->curdest = &qp->dest[j]; 10883468a491SDavid du Colombier } 10893cbadd90SDavid du Colombier destck(qp->curdest); 10907dd7cddfSDavid du Colombier 10917dd7cddfSDavid du Colombier /* no servers, punt */ 10923468a491SDavid du Colombier if (qp->ndest == 0) 10934f8f669cSDavid du Colombier if (cfg.straddle && cfg.inside) { 10943cbadd90SDavid du Colombier /* get ips of "outside-ns-ips" */ 1095c3617180SDavid du Colombier qp->curdest = qp->dest; 10963cbadd90SDavid du Colombier for(n = 0; n < Maxdest; n++, qp->curdest++) 10973cbadd90SDavid du Colombier if (setdestoutns(qp->curdest, n) < 0) 10987dd7cddfSDavid du Colombier break; 1099c3617180SDavid du Colombier if(n == 0) 1100c3617180SDavid du Colombier dnslog("xmitquery: %s: no outside-ns nameservers", 1101c3617180SDavid du Colombier qp->dp->name); 1102c3617180SDavid du Colombier } else 11034e7b9544SDavid du Colombier /* it's probably just a bogus domain, don't log it */ 11043cbadd90SDavid du Colombier return -1; 11057dd7cddfSDavid du Colombier 11063cbadd90SDavid du Colombier /* send to first 'qp->ndest' destinations */ 11077dd7cddfSDavid du Colombier j = 0; 11083cbadd90SDavid du Colombier if (medium == Tcp) { 11093cbadd90SDavid du Colombier j++; 11103cbadd90SDavid du Colombier queryck(qp); 11113cbadd90SDavid du Colombier assert(qp->dp); 11123cbadd90SDavid du Colombier procsetname("tcp %sside query for %s %s", (inns? "in": "out"), 11133cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 11143cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */ 11153cbadd90SDavid du Colombier if(debug) 11163cbadd90SDavid du Colombier logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name, 11173cbadd90SDavid du Colombier qp->type); 11183cbadd90SDavid du Colombier } else 11193cbadd90SDavid du Colombier for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){ 11207dd7cddfSDavid du Colombier /* skip destinations we've finished with */ 11217dd7cddfSDavid du Colombier if(p->nx >= Maxtrans) 11227dd7cddfSDavid du Colombier continue; 11237dd7cddfSDavid du Colombier 11247dd7cddfSDavid du Colombier j++; 11257dd7cddfSDavid du Colombier 11267dd7cddfSDavid du Colombier /* exponential backoff of requests */ 11273cbadd90SDavid du Colombier if((1<<p->nx) > qp->ndest) 11287dd7cddfSDavid du Colombier continue; 11297dd7cddfSDavid du Colombier 113059f7772cSDavid du Colombier if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0) 113159f7772cSDavid du Colombier continue; /* mistake */ 113259f7772cSDavid du Colombier 11333cbadd90SDavid du Colombier procsetname("udp %sside query to %I/%s %s %s", 11343cbadd90SDavid du Colombier (inns? "in": "out"), p->a, p->s->name, 11353cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 1136219b2ee8SDavid du Colombier if(debug) 11373cbadd90SDavid du Colombier logsend(qp->req->id, depth, p->a, p->s->name, 11383cbadd90SDavid du Colombier qp->dp->name, qp->type); 11394f8f669cSDavid du Colombier 11403cbadd90SDavid du Colombier /* fill in UDP destination addr & send it */ 11413cbadd90SDavid du Colombier memmove(obuf, p->a, sizeof p->a); 11423cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); 11437dd7cddfSDavid du Colombier p->nx++; 11443e12c5d1SDavid du Colombier } 11453cbadd90SDavid du Colombier if(j == 0) { 11463cbadd90SDavid du Colombier return -1; 11473cbadd90SDavid du Colombier } 11483cbadd90SDavid du Colombier return 0; 11493cbadd90SDavid du Colombier } 11503e12c5d1SDavid du Colombier 1151f46c709fSDavid du Colombier static int lckindex[Maxlcks] = { 1152f46c709fSDavid du Colombier 0, /* all others map here */ 1153f46c709fSDavid du Colombier Ta, 1154f46c709fSDavid du Colombier Tns, 1155f46c709fSDavid du Colombier Tcname, 1156f46c709fSDavid du Colombier Tsoa, 1157f46c709fSDavid du Colombier Tptr, 1158f46c709fSDavid du Colombier Tmx, 1159f46c709fSDavid du Colombier Ttxt, 1160f46c709fSDavid du Colombier Taaaa, 1161f46c709fSDavid du Colombier }; 1162f46c709fSDavid du Colombier 1163f46c709fSDavid du Colombier static int 1164f46c709fSDavid du Colombier qtype2lck(int qtype) /* map query type to querylck index */ 1165f46c709fSDavid du Colombier { 1166f46c709fSDavid du Colombier int i; 1167f46c709fSDavid du Colombier 1168f46c709fSDavid du Colombier for (i = 1; i < nelem(lckindex); i++) 1169f46c709fSDavid du Colombier if (lckindex[i] == qtype) 1170f46c709fSDavid du Colombier return i; 1171f46c709fSDavid du Colombier return 0; 1172f46c709fSDavid du Colombier } 1173f46c709fSDavid du Colombier 11740319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */ 11750319257bSDavid du Colombier static int 11760319257bSDavid du Colombier isnegrname(DNSmsg *mp) 11770319257bSDavid du Colombier { 11780319257bSDavid du Colombier /* TODO: could add || cfg.justforw to RHS of && */ 11790319257bSDavid du Colombier return mp->an == nil && (mp->flags & Rmask) == Rname; 11800319257bSDavid du Colombier } 11810319257bSDavid du Colombier 1182c3617180SDavid du Colombier /* returns Answerr (-1) on errors, else number of answers, which can be zero. */ 11833cbadd90SDavid du Colombier static int 11843cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) 11853cbadd90SDavid du Colombier { 11863cbadd90SDavid du Colombier int rv; 11874e5f5f32SDavid du Colombier // int lcktype; 11883cbadd90SDavid du Colombier char buf[32]; 11893cbadd90SDavid du Colombier DN *ndp; 1190c73252aeSDavid du Colombier Query *nqp; 11913cbadd90SDavid du Colombier RR *tp, *soarr; 11927dd7cddfSDavid du Colombier 1193f46c709fSDavid du Colombier if (mp->an == nil) 1194f46c709fSDavid du Colombier stats.negans++; 1195f46c709fSDavid du Colombier 119659cc4ca5SDavid du Colombier /* ignore any error replies */ 11973cbadd90SDavid du Colombier if((mp->flags & Rmask) == Rserver){ 11980319257bSDavid du Colombier stats.negserver++; 1199a41547ffSDavid du Colombier freeanswers(mp); 12003cbadd90SDavid du Colombier if(p != qp->curdest) 12017dd7cddfSDavid du Colombier p->code = Rserver; 1202c3617180SDavid du Colombier return Answerr; 12033e12c5d1SDavid du Colombier } 12043e12c5d1SDavid du Colombier 120559cc4ca5SDavid du Colombier /* ignore any bad delegations */ 12063cbadd90SDavid du Colombier if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){ 12070319257bSDavid du Colombier stats.negbaddeleg++; 12083cbadd90SDavid du Colombier if(mp->an == nil){ 12090319257bSDavid du Colombier stats.negbdnoans++; 1210a41547ffSDavid du Colombier freeanswers(mp); 12113cbadd90SDavid du Colombier if(p != qp->curdest) 121259cc4ca5SDavid du Colombier p->code = Rserver; 1213c3617180SDavid du Colombier dnslog(" and no answers"); 1214c3617180SDavid du Colombier return Answerr; 121559cc4ca5SDavid du Colombier } 1216c3617180SDavid du Colombier dnslog(" but has answers; ignoring ns"); 1217c3617180SDavid du Colombier lock(&dnlock); 1218e6d9d902SDavid du Colombier rrfreelistptr(&mp->ns); 1219c3617180SDavid du Colombier unlock(&dnlock); 1220c3617180SDavid du Colombier mp->nscount = 0; 122159cc4ca5SDavid du Colombier } 122259cc4ca5SDavid du Colombier 12237dd7cddfSDavid du Colombier /* remove any soa's from the authority section */ 1224530fef66SDavid du Colombier lock(&dnlock); 12253cbadd90SDavid du Colombier soarr = rrremtype(&mp->ns, Tsoa); 12267dd7cddfSDavid du Colombier 12273e12c5d1SDavid du Colombier /* incorporate answers */ 12284e153993SDavid du Colombier unique(mp->an); 12294e153993SDavid du Colombier unique(mp->ns); 12304e153993SDavid du Colombier unique(mp->ar); 1231530fef66SDavid du Colombier unlock(&dnlock); 1232e6d9d902SDavid du Colombier 12333cbadd90SDavid du Colombier if(mp->an) 12343cbadd90SDavid du Colombier rrattach(mp->an, (mp->flags & Fauth) != 0); 12353cbadd90SDavid du Colombier if(mp->ar) 12366dc4800dSDavid du Colombier rrattach(mp->ar, Notauthoritative); 1237a41547ffSDavid du Colombier if(mp->ns && !cfg.justforw){ 12383cbadd90SDavid du Colombier ndp = mp->ns->owner; 12396dc4800dSDavid du Colombier rrattach(mp->ns, Notauthoritative); 12400319257bSDavid du Colombier } else { 12414f8f669cSDavid du Colombier ndp = nil; 1242c3617180SDavid du Colombier lock(&dnlock); 1243e6d9d902SDavid du Colombier rrfreelistptr(&mp->ns); 1244c3617180SDavid du Colombier unlock(&dnlock); 1245c3617180SDavid du Colombier mp->nscount = 0; 12460319257bSDavid du Colombier } 12477dd7cddfSDavid du Colombier 12487dd7cddfSDavid du Colombier /* free the question */ 1249c3617180SDavid du Colombier if(mp->qd) { 1250c3617180SDavid du Colombier lock(&dnlock); 1251e6d9d902SDavid du Colombier rrfreelistptr(&mp->qd); 1252c3617180SDavid du Colombier unlock(&dnlock); 1253c3617180SDavid du Colombier mp->qdcount = 0; 1254c3617180SDavid du Colombier } 12553e12c5d1SDavid du Colombier 12563e12c5d1SDavid du Colombier /* 12573e12c5d1SDavid du Colombier * Any reply from an authoritative server, 1258a41547ffSDavid du Colombier * or a positive reply terminates the search. 1259a41547ffSDavid du Colombier * A negative response now also terminates the search. 12603e12c5d1SDavid du Colombier */ 12613cbadd90SDavid du Colombier if(mp->an != nil || (mp->flags & Fauth)){ 12620319257bSDavid du Colombier if(isnegrname(mp)) 12633cbadd90SDavid du Colombier qp->dp->respcode = Rname; 12647dd7cddfSDavid du Colombier else 1265d9924332SDavid du Colombier qp->dp->respcode = Rok; 12667dd7cddfSDavid du Colombier 12677dd7cddfSDavid du Colombier /* 1268a41547ffSDavid du Colombier * cache any negative responses, free soarr. 1269a41547ffSDavid du Colombier * negative responses need not be authoritative: 1270a41547ffSDavid du Colombier * they can legitimately come from a cache. 12717dd7cddfSDavid du Colombier */ 1272a41547ffSDavid du Colombier if( /* (mp->flags & Fauth) && */ mp->an == nil) 12733cbadd90SDavid du Colombier cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr); 1274c3617180SDavid du Colombier else { 1275c3617180SDavid du Colombier lock(&dnlock); 12767dd7cddfSDavid du Colombier rrfreelist(soarr); 1277c3617180SDavid du Colombier unlock(&dnlock); 1278c3617180SDavid du Colombier } 12793e12c5d1SDavid du Colombier return 1; 12800319257bSDavid du Colombier } else if (isnegrname(mp)) { 1281a41547ffSDavid du Colombier qp->dp->respcode = Rname; 1282a41547ffSDavid du Colombier /* 1283a41547ffSDavid du Colombier * cache negative response. 1284a41547ffSDavid du Colombier * negative responses need not be authoritative: 1285a41547ffSDavid du Colombier * they can legitimately come from a cache. 1286a41547ffSDavid du Colombier */ 1287a41547ffSDavid du Colombier cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr); 1288a41547ffSDavid du Colombier return 1; 12893e12c5d1SDavid du Colombier } 12900319257bSDavid du Colombier stats.negnorname++; 1291c3617180SDavid du Colombier lock(&dnlock); 12927dd7cddfSDavid du Colombier rrfreelist(soarr); 1293c3617180SDavid du Colombier unlock(&dnlock); 12943e12c5d1SDavid du Colombier 12953e12c5d1SDavid du Colombier /* 1296a41547ffSDavid du Colombier * if we've been given better name servers, recurse. 1297a41547ffSDavid du Colombier * if we're a pure resolver, don't recurse, we have 1298a41547ffSDavid du Colombier * to forward to a fixed set of named servers. 12993e12c5d1SDavid du Colombier */ 1300a41547ffSDavid du Colombier if(!mp->ns || cfg.resolver && cfg.justforw) 1301c3617180SDavid du Colombier return Answnone; 13027dd7cddfSDavid du Colombier tp = rrlookup(ndp, Tns, NOneg); 13033cbadd90SDavid du Colombier if(contains(qp->nsrp, tp)){ 1304c3617180SDavid du Colombier lock(&dnlock); 13057dd7cddfSDavid du Colombier rrfreelist(tp); 1306c3617180SDavid du Colombier unlock(&dnlock); 1307c3617180SDavid du Colombier return Answnone; 13083e12c5d1SDavid du Colombier } 13093cbadd90SDavid du Colombier procsetname("recursive query for %s %s", qp->dp->name, 13103cbadd90SDavid du Colombier rrname(qp->type, buf, sizeof buf)); 13114e5f5f32SDavid du Colombier /* 13124e5f5f32SDavid du Colombier * we're called from udpquery, called from 13134e5f5f32SDavid du Colombier * netquery, which current holds qp->dp->querylck, 13144e5f5f32SDavid du Colombier * so release it now and acquire it upon return. 13154e5f5f32SDavid du Colombier */ 1316e6d9d902SDavid du Colombier // lcktype = qtype2lck(qp->type); /* someday try this again */ 13174e5f5f32SDavid du Colombier // qunlock(&qp->dp->querylck[lcktype]); 13183cbadd90SDavid du Colombier 1319c73252aeSDavid du Colombier nqp = emalloc(sizeof *nqp); 1320c73252aeSDavid du Colombier queryinit(nqp, qp->dp, qp->type, qp->req); 1321c73252aeSDavid du Colombier nqp->nsrp = tp; 1322c73252aeSDavid du Colombier rv = netquery(nqp, depth+1); 13233cbadd90SDavid du Colombier 13244e5f5f32SDavid du Colombier // qlock(&qp->dp->querylck[lcktype]); 1325adb31a62SDavid du Colombier rrfreelist(nqp->nsrp); 1326c73252aeSDavid du Colombier querydestroy(nqp); 1327c73252aeSDavid du Colombier free(nqp); 13283cbadd90SDavid du Colombier return rv; 13293cbadd90SDavid du Colombier } 13303cbadd90SDavid du Colombier 13313cbadd90SDavid du Colombier /* 13323cbadd90SDavid du Colombier * send a query via tcp to a single address (from ibuf's udp header) 13333cbadd90SDavid du Colombier * and read the answer(s) into mp->an. 13343cbadd90SDavid du Colombier */ 13353cbadd90SDavid du Colombier static int 13363cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len, 1337b8b25780SDavid du Colombier ulong waitms, int inns, ushort req) 13383cbadd90SDavid du Colombier { 13394e7b9544SDavid du Colombier int rv = 0; 1340b8b25780SDavid du Colombier uvlong endms; 13413cbadd90SDavid du Colombier 1342b8b25780SDavid du Colombier endms = timems() + waitms; 1343b8b25780SDavid du Colombier if(endms > qp->req->aborttime) 1344b8b25780SDavid du Colombier endms = qp->req->aborttime; 13453cbadd90SDavid du Colombier 1346a41547ffSDavid du Colombier if (0) 13474e7b9544SDavid du Colombier dnslog("%s: udp reply truncated; retrying query via tcp to %I", 13484e7b9544SDavid du Colombier qp->dp->name, qp->tcpip); 13494e7b9544SDavid du Colombier 13503cbadd90SDavid du Colombier qlock(&qp->tcplock); 13513cbadd90SDavid du Colombier memmove(obuf, ibuf, IPaddrlen); /* send back to respondent */ 13523cbadd90SDavid du Colombier /* sets qp->tcpip from obuf's udp header */ 13534e7b9544SDavid du Colombier if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 || 1354b8b25780SDavid du Colombier readreply(qp, Tcp, req, ibuf, mp, endms) < 0) 13554e7b9544SDavid du Colombier rv = -1; 13563cbadd90SDavid du Colombier if (qp->tcpfd > 0) { 13573cbadd90SDavid du Colombier hangup(qp->tcpctlfd); 13583cbadd90SDavid du Colombier close(qp->tcpctlfd); 13593cbadd90SDavid du Colombier close(qp->tcpfd); 13603cbadd90SDavid du Colombier } 13613cbadd90SDavid du Colombier qp->tcpfd = qp->tcpctlfd = -1; 13623cbadd90SDavid du Colombier qunlock(&qp->tcplock); 13634e7b9544SDavid du Colombier return rv; 13643cbadd90SDavid du Colombier } 13653cbadd90SDavid du Colombier 13663cbadd90SDavid du Colombier /* 136759f7772cSDavid du Colombier * query name servers. fill in obuf with on-the-wire representation of a 136859f7772cSDavid du Colombier * DNSmsg derived from qp. if the name server returns a pointer to another 13693cbadd90SDavid du Colombier * name server, recurse. 13703cbadd90SDavid du Colombier */ 13713cbadd90SDavid du Colombier static int 1372b8b25780SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns) 13733cbadd90SDavid du Colombier { 13743cbadd90SDavid du Colombier int ndest, len, replywaits, rv; 13753cbadd90SDavid du Colombier ushort req; 1376b8b25780SDavid du Colombier uvlong endms; 13773cbadd90SDavid du Colombier char buf[12]; 13783cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 1379a41547ffSDavid du Colombier Dest *p, *np, *dest; 13803cbadd90SDavid du Colombier 13813cbadd90SDavid du Colombier /* pack request into a udp message */ 13823cbadd90SDavid du Colombier req = rand(); 13833cbadd90SDavid du Colombier len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req); 13843cbadd90SDavid du Colombier 13853cbadd90SDavid du Colombier /* no server addresses yet */ 13863cbadd90SDavid du Colombier queryck(qp); 1387a41547ffSDavid du Colombier dest = emalloc(Maxdest * sizeof *dest); /* dest can't be on stack */ 1388a41547ffSDavid du Colombier for (p = dest; p < dest + Maxdest; p++) 13893cbadd90SDavid du Colombier destinit(p); 13901eb0187fSDavid du Colombier /* this dest array is local to this call of queryns() */ 13911eb0187fSDavid du Colombier free(qp->dest); 13923cbadd90SDavid du Colombier qp->curdest = qp->dest = dest; 13933cbadd90SDavid du Colombier 13943cbadd90SDavid du Colombier /* 13953cbadd90SDavid du Colombier * transmit udp requests and wait for answers. 13963cbadd90SDavid du Colombier * at most Maxtrans attempts to each address. 13973cbadd90SDavid du Colombier * each cycle send one more message than the previous. 13983cbadd90SDavid du Colombier * retry a query via tcp if its response is truncated. 13993cbadd90SDavid du Colombier */ 14003cbadd90SDavid du Colombier for(ndest = 1; ndest < Maxdest; ndest++){ 14013cbadd90SDavid du Colombier qp->ndest = ndest; 14023cbadd90SDavid du Colombier qp->tcpset = 0; 14033cbadd90SDavid du Colombier if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0) 14043cbadd90SDavid du Colombier break; 14053cbadd90SDavid du Colombier 1406b8b25780SDavid du Colombier endms = timems() + waitms; 1407b8b25780SDavid du Colombier if(endms > qp->req->aborttime) 1408b8b25780SDavid du Colombier endms = qp->req->aborttime; 14093cbadd90SDavid du Colombier 14103cbadd90SDavid du Colombier for(replywaits = 0; replywaits < ndest; replywaits++){ 141181730632SDavid du Colombier DNSmsg m; 141281730632SDavid du Colombier 1413f46c709fSDavid du Colombier procsetname("reading %sside reply from %I: %s %s from %s", 1414a41547ffSDavid du Colombier (inns? "in": "out"), obuf, qp->dp->name, 1415f46c709fSDavid du Colombier rrname(qp->type, buf, sizeof buf), qp->req->from); 14163cbadd90SDavid du Colombier 141781730632SDavid du Colombier /* read udp answer into m */ 1418b8b25780SDavid du Colombier if (readreply(qp, Udp, req, ibuf, &m, endms) >= 0) 14193cbadd90SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 142081730632SDavid du Colombier else if (!(m.flags & Ftrunc)) { 142181730632SDavid du Colombier freeanswers(&m); 14223cbadd90SDavid du Colombier break; /* timed out on this dest */ 142381730632SDavid du Colombier } else { 14243cbadd90SDavid du Colombier /* whoops, it was truncated! ask again via tcp */ 14256aaebd7dSDavid du Colombier freeanswers(&m); 14263cbadd90SDavid du Colombier rv = tcpquery(qp, &m, depth, ibuf, obuf, len, 1427b8b25780SDavid du Colombier waitms, inns, req); /* answer in m */ 142881730632SDavid du Colombier if (rv < 0) { 142981730632SDavid du Colombier freeanswers(&m); 14303cbadd90SDavid du Colombier break; /* failed via tcp too */ 143181730632SDavid du Colombier } 14323cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 14333cbadd90SDavid du Colombier } 14343cbadd90SDavid du Colombier 14353cbadd90SDavid du Colombier /* find responder */ 14364e5f5f32SDavid du Colombier // dnslog("queryns got reply from %I", srcip); 14373cbadd90SDavid du Colombier for(p = qp->dest; p < qp->curdest; p++) 14383cbadd90SDavid du Colombier if(memcmp(p->a, srcip, sizeof p->a) == 0) 14393cbadd90SDavid du Colombier break; 14403cbadd90SDavid du Colombier 14413cbadd90SDavid du Colombier /* remove all addrs of responding server from list */ 14423cbadd90SDavid du Colombier for(np = qp->dest; np < qp->curdest; np++) 14433cbadd90SDavid du Colombier if(np->s == p->s) 14443cbadd90SDavid du Colombier p->nx = Maxtrans; 14453cbadd90SDavid du Colombier 144681730632SDavid du Colombier /* free or incorporate RRs in m */ 14473cbadd90SDavid du Colombier rv = procansw(qp, &m, srcip, depth, p); 1448c3617180SDavid du Colombier if (rv > Answnone) { 14491eb0187fSDavid du Colombier free(qp->dest); 14501eb0187fSDavid du Colombier qp->dest = qp->curdest = nil; /* prevent accidents */ 14513cbadd90SDavid du Colombier return rv; 14523e12c5d1SDavid du Colombier } 14533e12c5d1SDavid du Colombier } 14541eb0187fSDavid du Colombier } 14557dd7cddfSDavid du Colombier 14564f8f669cSDavid du Colombier /* if all servers returned failure, propagate it */ 14573cbadd90SDavid du Colombier qp->dp->respcode = Rserver; 14583cbadd90SDavid du Colombier for(p = dest; p < qp->curdest; p++) { 14593cbadd90SDavid du Colombier destck(p); 14607dd7cddfSDavid du Colombier if(p->code != Rserver) 1461d9924332SDavid du Colombier qp->dp->respcode = Rok; 14623cbadd90SDavid du Colombier p->magic = 0; /* prevent accidents */ 14633cbadd90SDavid du Colombier } 14644f8f669cSDavid du Colombier 14653cbadd90SDavid du Colombier // if (qp->dp->respcode) 14664e5f5f32SDavid du Colombier // dnslog("queryns setting Rserver for %s", qp->dp->name); 14677dd7cddfSDavid du Colombier 1468a41547ffSDavid du Colombier free(qp->dest); 14693cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; /* prevent accidents */ 1470c3617180SDavid du Colombier return Answnone; 14713e12c5d1SDavid du Colombier } 14727dd7cddfSDavid du Colombier 14734f8f669cSDavid du Colombier /* 14744f8f669cSDavid du Colombier * run a command with a supplied fd as standard input 14754f8f669cSDavid du Colombier */ 14764f8f669cSDavid du Colombier char * 14774f8f669cSDavid du Colombier system(int fd, char *cmd) 147828a8a86bSDavid du Colombier { 14794f8f669cSDavid du Colombier int pid, p, i; 14804f8f669cSDavid du Colombier static Waitmsg msg; 14816b0d5c8bSDavid du Colombier 14824f8f669cSDavid du Colombier if((pid = fork()) == -1) 14834f8f669cSDavid du Colombier sysfatal("fork failed: %r"); 14844f8f669cSDavid du Colombier else if(pid == 0){ 14854f8f669cSDavid du Colombier dup(fd, 0); 14864f8f669cSDavid du Colombier close(fd); 14874f8f669cSDavid du Colombier for (i = 3; i < 200; i++) 14884f8f669cSDavid du Colombier close(i); /* don't leak fds */ 14894f8f669cSDavid du Colombier execl("/bin/rc", "rc", "-c", cmd, nil); 14904f8f669cSDavid du Colombier sysfatal("exec rc: %r"); 14914f8f669cSDavid du Colombier } 14924f8f669cSDavid du Colombier for(p = waitpid(); p >= 0; p = waitpid()) 14934f8f669cSDavid du Colombier if(p == pid) 14944f8f669cSDavid du Colombier return msg.msg; 14954f8f669cSDavid du Colombier return "lost child"; 14964f8f669cSDavid du Colombier } 14974f8f669cSDavid du Colombier 1498b8b25780SDavid du Colombier /* compute wait, weighted by probability of success, with bounds */ 14990319257bSDavid du Colombier static ulong 15000319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob) 15010319257bSDavid du Colombier { 15020319257bSDavid du Colombier ulong wait; 15030319257bSDavid du Colombier 15040319257bSDavid du Colombier wait = (ms * pcntprob) / 100; 1505b8b25780SDavid du Colombier if (wait < Minwaitms) 1506b8b25780SDavid du Colombier wait = Minwaitms; 1507b8b25780SDavid du Colombier if (wait > Maxwaitms) 1508b8b25780SDavid du Colombier wait = Maxwaitms; 15090319257bSDavid du Colombier return wait; 15100319257bSDavid du Colombier } 15110319257bSDavid du Colombier 1512a41547ffSDavid du Colombier /* 1513a41547ffSDavid du Colombier * in principle we could use a single descriptor for a udp port 1514a41547ffSDavid du Colombier * to send all queries and receive all the answers to them, 1515a41547ffSDavid du Colombier * but we'd have to sort out the answers by dns-query id. 1516a41547ffSDavid du Colombier */ 15174f8f669cSDavid du Colombier static int 15183cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns) 15194f8f669cSDavid du Colombier { 15200319257bSDavid du Colombier int fd, rv; 15214f8f669cSDavid du Colombier long now; 1522b8b25780SDavid du Colombier ulong pcntprob; 1523b8b25780SDavid du Colombier uvlong wait, reqtm; 15244f8f669cSDavid du Colombier char *msg; 15254f8f669cSDavid du Colombier uchar *obuf, *ibuf; 15264f8f669cSDavid du Colombier static QLock mntlck; 15274f8f669cSDavid du Colombier static ulong lastmount; 15287dd7cddfSDavid du Colombier 15297dd7cddfSDavid du Colombier /* use alloced buffers rather than ones from the stack */ 15303cbadd90SDavid du Colombier ibuf = emalloc(64*1024); /* max. tcp reply size */ 1531f27a9a5aSDavid du Colombier obuf = emalloc(Maxudp+Udphdrsize); 15327dd7cddfSDavid du Colombier 15334f8f669cSDavid du Colombier fd = udpport(mntpt); 15344f8f669cSDavid du Colombier while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) { 15354f8f669cSDavid du Colombier /* HACK: remount /net.alt */ 15364f8f669cSDavid du Colombier now = time(nil); 15374f8f669cSDavid du Colombier if (now < lastmount + Remntretry) 1538b8b25780SDavid du Colombier sleep(S2MS(lastmount + Remntretry - now)); 15394f8f669cSDavid du Colombier qlock(&mntlck); 15404f8f669cSDavid du Colombier fd = udpport(mntpt); /* try again under lock */ 15414f8f669cSDavid du Colombier if (fd < 0) { 15424f8f669cSDavid du Colombier dnslog("[%d] remounting /net.alt", getpid()); 15434f8f669cSDavid du Colombier unmount(nil, "/net.alt"); 15444f8f669cSDavid du Colombier 15454f8f669cSDavid du Colombier msg = system(open("/dev/null", ORDWR), "outside"); 15464f8f669cSDavid du Colombier 15474f8f669cSDavid du Colombier lastmount = time(nil); 15484f8f669cSDavid du Colombier if (msg && *msg) { 15494f8f669cSDavid du Colombier dnslog("[%d] can't remount /net.alt: %s", 15504f8f669cSDavid du Colombier getpid(), msg); 1551b8b25780SDavid du Colombier sleep(10*1000); /* don't spin remounting */ 15524f8f669cSDavid du Colombier } else 15534f8f669cSDavid du Colombier fd = udpport(mntpt); 15544f8f669cSDavid du Colombier } 15554f8f669cSDavid du Colombier qunlock(&mntlck); 15564f8f669cSDavid du Colombier } 1557a41547ffSDavid du Colombier if (fd < 0) { 15584f8f669cSDavid du Colombier dnslog("can't get udpport for %s query of name %s: %r", 15593cbadd90SDavid du Colombier mntpt, qp->dp->name); 1560a41547ffSDavid du Colombier sysfatal("out of udp conversations"); /* we're buggered */ 1561a41547ffSDavid du Colombier } 1562a41547ffSDavid du Colombier 1563a41547ffSDavid du Colombier /* 1564b8b25780SDavid du Colombier * Our QIP servers are busted and respond to AAAA and CNAME queries 1565b8b25780SDavid du Colombier * with (sometimes malformed [too short] packets and) no answers and 1566b8b25780SDavid du Colombier * just NS RRs but not Rname errors. so make time-to-wait 1567b8b25780SDavid du Colombier * proportional to estimated probability of an RR of that type existing. 1568a41547ffSDavid du Colombier */ 1569adb31a62SDavid du Colombier if (qp->type >= nelem(likely)) 15700319257bSDavid du Colombier pcntprob = 35; /* unpopular query type */ 15710319257bSDavid du Colombier else 15720319257bSDavid du Colombier pcntprob = likely[qp->type]; 15730319257bSDavid du Colombier reqtm = (patient? 2 * Maxreqtm: Maxreqtm); 1574b8b25780SDavid du Colombier wait = weight(reqtm / 3, pcntprob); /* time for one udp query */ 1575b8b25780SDavid du Colombier qp->req->aborttime = timems() + 3*wait; /* for all udp queries */ 1576a41547ffSDavid du Colombier 15770319257bSDavid du Colombier qp->udpfd = fd; 1578b8b25780SDavid du Colombier rv = queryns(qp, depth, ibuf, obuf, wait, inns); 1579a41547ffSDavid du Colombier close(fd); 1580a41547ffSDavid du Colombier qp->udpfd = -1; 15814f8f669cSDavid du Colombier 15824f8f669cSDavid du Colombier free(obuf); 15834f8f669cSDavid du Colombier free(ibuf); 15844f8f669cSDavid du Colombier return rv; 15854f8f669cSDavid du Colombier } 15864f8f669cSDavid du Colombier 1587b8b25780SDavid du Colombier /* 1588b8b25780SDavid du Colombier * look up (qp->dp->name, qp->type) rr in dns, 1589b8b25780SDavid du Colombier * using nameservers in qp->nsrp. 1590b8b25780SDavid du Colombier */ 15914f8f669cSDavid du Colombier static int 15923cbadd90SDavid du Colombier netquery(Query *qp, int depth) 15934f8f669cSDavid du Colombier { 1594410ea80bSDavid du Colombier int lock, rv, triedin, inname; 1595b8b25780SDavid du Colombier char buf[32]; 15964f8f669cSDavid du Colombier RR *rp; 15974e153993SDavid du Colombier DN *dp; 1598225077b0SDavid du Colombier Querylck *qlp; 1599225077b0SDavid du Colombier static int whined; 16004f8f669cSDavid du Colombier 1601c3617180SDavid du Colombier rv = Answnone; /* pessimism */ 16024f8f669cSDavid du Colombier if(depth > 12) /* in a recursive loop? */ 1603c3617180SDavid du Colombier return Answnone; 16044f8f669cSDavid du Colombier 16053cbadd90SDavid du Colombier slave(qp->req); 1606d3907fe5SDavid du Colombier /* 1607d3907fe5SDavid du Colombier * slave might have forked. if so, the parent process longjmped to 1608d3907fe5SDavid du Colombier * req->mret; we're usually the child slave, but if there are too 1609d3907fe5SDavid du Colombier * many children already, we're still the same process. 1610d3907fe5SDavid du Colombier */ 16114f8f669cSDavid du Colombier 16124e5f5f32SDavid du Colombier /* 16134e5f5f32SDavid du Colombier * don't lock before call to slave so only children can block. 16144e5f5f32SDavid du Colombier * just lock at top-level invocation. 16154e5f5f32SDavid du Colombier */ 1616225077b0SDavid du Colombier lock = depth <= 1 && qp->req->isslave; 16174e153993SDavid du Colombier dp = qp->dp; /* ensure that it doesn't change underfoot */ 1618225077b0SDavid du Colombier qlp = nil; 16194e5f5f32SDavid du Colombier if(lock) { 1620b8b25780SDavid du Colombier procsetname("query lock wait: %s %s from %s", dp->name, 1621b8b25780SDavid du Colombier rrname(qp->type, buf, sizeof buf), qp->req->from); 1622ccf6439bSDavid du Colombier /* 1623ccf6439bSDavid du Colombier * don't make concurrent queries for this name. 162481730632SDavid du Colombier * dozens of processes blocking here probably indicates 162581730632SDavid du Colombier * an error in our dns data that causes us to not 162681730632SDavid du Colombier * recognise a zone (area) as one of our own, thus 162781730632SDavid du Colombier * causing us to query other nameservers. 1628ccf6439bSDavid du Colombier */ 1629225077b0SDavid du Colombier qlp = &dp->querylck[qtype2lck(qp->type)]; 1630225077b0SDavid du Colombier qlock(qlp); 1631b8b25780SDavid du Colombier if (qlp->Ref.ref > Maxoutstanding) { 1632225077b0SDavid du Colombier qunlock(qlp); 1633225077b0SDavid du Colombier if (!whined) { 1634225077b0SDavid du Colombier whined = 1; 1635225077b0SDavid du Colombier dnslog("too many outstanding queries for %s;" 163613fec586SDavid du Colombier " dropping this one; no further logging" 163713fec586SDavid du Colombier " of drops", dp->name); 1638225077b0SDavid du Colombier } 1639225077b0SDavid du Colombier return 0; 1640225077b0SDavid du Colombier } 1641410ea80bSDavid du Colombier ++qlp->Ref.ref; 1642410ea80bSDavid du Colombier qunlock(qlp); 1643225077b0SDavid du Colombier } 16444e153993SDavid du Colombier procsetname("netquery: %s", dp->name); 16456b0d5c8bSDavid du Colombier 16466b0d5c8bSDavid du Colombier /* prepare server RR's for incremental lookup */ 16473cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 16486b0d5c8bSDavid du Colombier rp->marker = 0; 16496b0d5c8bSDavid du Colombier 16504f8f669cSDavid du Colombier triedin = 0; 1651a41547ffSDavid du Colombier 16524f8f669cSDavid du Colombier /* 16534f8f669cSDavid du Colombier * normal resolvers and servers will just use mntpt for all addresses, 16544f8f669cSDavid du Colombier * even on the outside. straddling servers will use mntpt (/net) 16554f8f669cSDavid du Colombier * for inside addresses and /net.alt for outside addresses, 16564f8f669cSDavid du Colombier * thus bypassing other inside nameservers. 16574f8f669cSDavid du Colombier */ 16584e153993SDavid du Colombier inname = insideaddr(dp->name); 16594f8f669cSDavid du Colombier if (!cfg.straddle || inname) { 16603cbadd90SDavid du Colombier rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns)); 16614f8f669cSDavid du Colombier triedin = 1; 16624f8f669cSDavid du Colombier } 16634f8f669cSDavid du Colombier 16644f8f669cSDavid du Colombier /* 16654f8f669cSDavid du Colombier * if we're still looking, are inside, and have an outside domain, 16664f8f669cSDavid du Colombier * try it on our outside interface, if any. 16674f8f669cSDavid du Colombier */ 1668c3617180SDavid du Colombier if (rv == Answnone && cfg.inside && !inname) { 16694f8f669cSDavid du Colombier if (triedin) 16704f8f669cSDavid du Colombier dnslog( 16714f8f669cSDavid du Colombier "[%d] netquery: internal nameservers failed for %s; trying external", 16724e153993SDavid du Colombier getpid(), dp->name); 16734f8f669cSDavid du Colombier 16744f8f669cSDavid du Colombier /* prepare server RR's for incremental lookup */ 16753cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 16764f8f669cSDavid du Colombier rp->marker = 0; 16774f8f669cSDavid du Colombier 16783cbadd90SDavid du Colombier rv = udpquery(qp, "/net.alt", depth, Patient, Outns); 16794f8f669cSDavid du Colombier } 1680c3617180SDavid du Colombier // if (rv == Answnone) /* could ask /net.alt/dns directly */ 16814e153993SDavid du Colombier // askoutdns(dp, qp->type); 16824f8f669cSDavid du Colombier 1683410ea80bSDavid du Colombier if(lock && qlp) { 1684410ea80bSDavid du Colombier qlock(qlp); 1685410ea80bSDavid du Colombier assert(qlp->Ref.ref > 0); 1686410ea80bSDavid du Colombier qunlock(qlp); 1687225077b0SDavid du Colombier decref(qlp); 1688410ea80bSDavid du Colombier } 16897dd7cddfSDavid du Colombier return rv; 16907dd7cddfSDavid du Colombier } 16914f8f669cSDavid du Colombier 16924f8f669cSDavid du Colombier int 16934f8f669cSDavid du Colombier seerootns(void) 16944f8f669cSDavid du Colombier { 16953cbadd90SDavid du Colombier int rv; 16964f8f669cSDavid du Colombier char root[] = ""; 16974f8f669cSDavid du Colombier Request req; 1698c3617180SDavid du Colombier RR *rr; 1699c73252aeSDavid du Colombier Query *qp; 17004f8f669cSDavid du Colombier 17014f8f669cSDavid du Colombier memset(&req, 0, sizeof req); 17024f8f669cSDavid du Colombier req.isslave = 1; 1703b8b25780SDavid du Colombier req.aborttime = timems() + Maxreqtm; 1704f46c709fSDavid du Colombier req.from = "internal"; 1705c3617180SDavid du Colombier 1706c73252aeSDavid du Colombier qp = emalloc(sizeof *qp); 1707c73252aeSDavid du Colombier queryinit(qp, dnlookup(root, Cin, 1), Tns, &req); 1708c73252aeSDavid du Colombier qp->nsrp = dblookup(root, Cin, Tns, 0, 0); 1709c3617180SDavid du Colombier for (rr = qp->nsrp; rr != nil; rr = rr->next) /* DEBUG */ 1710c3617180SDavid du Colombier dnslog("seerootns query nsrp: %R", rr); 1711c3617180SDavid du Colombier 1712c3617180SDavid du Colombier rv = netquery(qp, 0); /* lookup ". ns" using qp->nsrp */ 1713c73252aeSDavid du Colombier 1714c73252aeSDavid du Colombier rrfreelist(qp->nsrp); 1715c73252aeSDavid du Colombier querydestroy(qp); 1716c73252aeSDavid du Colombier free(qp); 17173cbadd90SDavid du Colombier return rv; 17184f8f669cSDavid du Colombier } 1719