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 197dd7cddfSDavid du Colombier Maxdest= 24, /* maximum destinations for a request message */ 20b8b25780SDavid du Colombier Maxoutstanding= 15, /* max. outstanding queries per domain name */ 21e6d9d902SDavid du Colombier Remntretry= 15, /* min. sec.s between /net.alt remount tries */ 22e6d9d902SDavid du Colombier 23e6d9d902SDavid du Colombier /* 24e6d9d902SDavid du Colombier * these are the old values; we're trying longer timeouts now 25e6d9d902SDavid du Colombier * primarily for the benefit of remote nameservers querying us 26e6d9d902SDavid du Colombier * during times of bad connectivity. 27e6d9d902SDavid du Colombier */ 28e6d9d902SDavid du Colombier // Maxtrans= 3, /* maximum transmissions to a server */ 29e6d9d902SDavid du Colombier // Maxretries= 3, /* cname+actual resends: was 32; have pity on user */ 30e6d9d902SDavid du Colombier // Maxwaitms= 1000, /* wait no longer for a remote dns query */ 31e6d9d902SDavid du Colombier // Minwaitms= 100, /* willing to wait for a remote dns query */ 32e6d9d902SDavid du Colombier 33e6d9d902SDavid du Colombier Maxtrans= 5, /* maximum transmissions to a server */ 34e6d9d902SDavid du Colombier Maxretries= 5, /* cname+actual resends: was 32; have pity on user */ 35e6d9d902SDavid du Colombier Maxwaitms= 5000, /* wait no longer for a remote dns query */ 36e6d9d902SDavid du Colombier Minwaitms= 500, /* willing to wait for a remote dns query */ 37b8b25780SDavid du Colombier 383cbadd90SDavid du Colombier Destmagic= 0xcafebabe, 393cbadd90SDavid du Colombier Querymagic= 0xdeadbeef, 403e12c5d1SDavid du Colombier }; 41a41547ffSDavid du Colombier enum { Hurry, Patient, }; 42a41547ffSDavid du Colombier enum { Outns, Inns, }; 433e12c5d1SDavid du Colombier 443cbadd90SDavid du Colombier struct Ipaddr { 453cbadd90SDavid du Colombier Ipaddr *next; 463cbadd90SDavid du Colombier uchar ip[IPaddrlen]; 473cbadd90SDavid du Colombier }; 483cbadd90SDavid du Colombier 493cbadd90SDavid du Colombier struct Dest 503cbadd90SDavid du Colombier { 513cbadd90SDavid du Colombier uchar a[IPaddrlen]; /* ip address */ 523cbadd90SDavid du Colombier DN *s; /* name server */ 533cbadd90SDavid du Colombier int nx; /* number of transmissions */ 543cbadd90SDavid du Colombier int code; /* response code; used to clear dp->respcode */ 553cbadd90SDavid du Colombier 563cbadd90SDavid du Colombier ulong magic; 573cbadd90SDavid du Colombier }; 583cbadd90SDavid du Colombier 59c73252aeSDavid du Colombier /* 60c73252aeSDavid du Colombier * Query has a QLock in it, thus it can't be an automatic 61c73252aeSDavid du Colombier * variable, since each process would see a separate copy 62c73252aeSDavid du Colombier * of the lock on its stack. 63c73252aeSDavid du Colombier */ 643cbadd90SDavid du Colombier struct Query { 653cbadd90SDavid du Colombier DN *dp; /* domain */ 66adb31a62SDavid du Colombier ushort type; /* and type to look up */ 673cbadd90SDavid du Colombier Request *req; 683cbadd90SDavid du Colombier RR *nsrp; /* name servers to consult */ 693cbadd90SDavid du Colombier 70a41547ffSDavid du Colombier /* dest must not be on the stack due to forking in slave() */ 713cbadd90SDavid du Colombier Dest *dest; /* array of destinations */ 72*59f7772cSDavid du Colombier Dest *curdest; /* pointer to next to fill */ 73*59f7772cSDavid du Colombier int ndest; /* transmit to this many on this round */ 743cbadd90SDavid du Colombier 75f46c709fSDavid du Colombier int udpfd; 763cbadd90SDavid du Colombier 773cbadd90SDavid du Colombier QLock tcplock; /* only one tcp call at a time per query */ 783cbadd90SDavid du Colombier int tcpset; 793cbadd90SDavid du Colombier int tcpfd; /* if Tcp, read replies from here */ 803cbadd90SDavid du Colombier int tcpctlfd; 813cbadd90SDavid du Colombier uchar tcpip[IPaddrlen]; 823cbadd90SDavid du Colombier 833cbadd90SDavid du Colombier ulong magic; 843cbadd90SDavid du Colombier }; 853cbadd90SDavid du Colombier 86a41547ffSDavid du Colombier /* estimated % probability of such a record existing at all */ 87a41547ffSDavid du Colombier int likely[] = { 88a41547ffSDavid du Colombier [Ta] 95, 89a41547ffSDavid du Colombier [Taaaa] 10, 90a41547ffSDavid du Colombier [Tcname] 15, 91a41547ffSDavid du Colombier [Tmx] 60, 92a41547ffSDavid du Colombier [Tns] 90, 93a41547ffSDavid du Colombier [Tnull] 5, 94a41547ffSDavid du Colombier [Tptr] 35, 95a41547ffSDavid du Colombier [Tsoa] 90, 96a41547ffSDavid du Colombier [Tsrv] 60, 97a41547ffSDavid du Colombier [Ttxt] 15, 98a41547ffSDavid du Colombier [Tall] 95, 993cbadd90SDavid du Colombier }; 1003cbadd90SDavid du Colombier 1017dd7cddfSDavid du Colombier static RR* dnresolve1(char*, int, int, Request*, int, int); 1023cbadd90SDavid du Colombier static int netquery(Query *, int); 1033cbadd90SDavid du Colombier 1044f8f669cSDavid du Colombier /* 105530fef66SDavid du Colombier * reading /proc/pid/args yields either "name args" or "name [display args]", 1064f8f669cSDavid du Colombier * so return only display args, if any. 1074f8f669cSDavid du Colombier */ 1084f8f669cSDavid du Colombier static char * 1094f8f669cSDavid du Colombier procgetname(void) 1104f8f669cSDavid du Colombier { 1114f8f669cSDavid du Colombier int fd, n; 1124f8f669cSDavid du Colombier char *lp, *rp; 1134f8f669cSDavid du Colombier char buf[256]; 1144f8f669cSDavid du Colombier 1154f8f669cSDavid du Colombier snprint(buf, sizeof buf, "#p/%d/args", getpid()); 1164f8f669cSDavid du Colombier if((fd = open(buf, OREAD)) < 0) 1174f8f669cSDavid du Colombier return strdup(""); 1184f8f669cSDavid du Colombier *buf = '\0'; 1194f8f669cSDavid du Colombier n = read(fd, buf, sizeof buf-1); 1204f8f669cSDavid du Colombier close(fd); 1214f8f669cSDavid du Colombier if (n >= 0) 1224f8f669cSDavid du Colombier buf[n] = '\0'; 1234f8f669cSDavid du Colombier if ((lp = strchr(buf, '[')) == nil || 1244f8f669cSDavid du Colombier (rp = strrchr(buf, ']')) == nil) 1254f8f669cSDavid du Colombier return strdup(""); 1264f8f669cSDavid du Colombier *rp = '\0'; 1274f8f669cSDavid du Colombier return strdup(lp+1); 1284f8f669cSDavid du Colombier } 1293e12c5d1SDavid du Colombier 130e6d9d902SDavid du Colombier void 131e6d9d902SDavid du Colombier rrfreelistptr(RR **rpp) 132e6d9d902SDavid du Colombier { 133e6d9d902SDavid du Colombier RR *rp; 134e6d9d902SDavid du Colombier 135e6d9d902SDavid du Colombier if (rpp == nil || *rpp == nil) 136e6d9d902SDavid du Colombier return; 137e6d9d902SDavid du Colombier rp = *rpp; 138e6d9d902SDavid du Colombier *rpp = nil; /* update pointer in memory before freeing */ 139e6d9d902SDavid du Colombier rrfreelist(rp); 140e6d9d902SDavid du Colombier } 141e6d9d902SDavid du Colombier 1423e12c5d1SDavid du Colombier /* 1433e12c5d1SDavid du Colombier * lookup 'type' info for domain name 'name'. If it doesn't exist, try 1443e12c5d1SDavid du Colombier * looking it up as a canonical name. 145b8b25780SDavid du Colombier * 146b8b25780SDavid du Colombier * this process can be quite slow if time-outs are set too high when querying 147b8b25780SDavid du Colombier * nameservers that just don't respond to certain query types. in that case, 148b8b25780SDavid du Colombier * there will be multiple udp retries, multiple nameservers will be queried, 149b8b25780SDavid du Colombier * and this will be repeated for a cname query. the whole thing will be 150b8b25780SDavid du Colombier * retried several times until we get an answer or a time-out. 1513e12c5d1SDavid du Colombier */ 1523e12c5d1SDavid du Colombier RR* 1534f8f669cSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, 1544f8f669cSDavid du Colombier int recurse, int rooted, int *status) 1553e12c5d1SDavid du Colombier { 1567dd7cddfSDavid du Colombier RR *rp, *nrp, *drp; 1573e12c5d1SDavid du Colombier DN *dp; 1587dd7cddfSDavid du Colombier int loops; 1594f8f669cSDavid du Colombier char *procname; 1607dd7cddfSDavid du Colombier char nname[Domlen]; 1613e12c5d1SDavid du Colombier 1627dd7cddfSDavid du Colombier if(status) 1637dd7cddfSDavid du Colombier *status = 0; 1647dd7cddfSDavid du Colombier 165a41547ffSDavid du Colombier if(depth > 12) /* in a recursive loop? */ 166a41547ffSDavid du Colombier return nil; 167a41547ffSDavid du Colombier 1684f8f669cSDavid du Colombier procname = procgetname(); 1697dd7cddfSDavid du Colombier /* 1707dd7cddfSDavid du Colombier * hack for systems that don't have resolve search 1717dd7cddfSDavid du Colombier * lists. Just look up the simple name in the database. 1727dd7cddfSDavid du Colombier */ 1736dc4800dSDavid du Colombier if(!rooted && strchr(name, '.') == nil){ 1747dd7cddfSDavid du Colombier rp = nil; 1757dd7cddfSDavid du Colombier drp = domainlist(class); 176a41547ffSDavid du Colombier for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){ 1774f8f669cSDavid du Colombier snprint(nname, sizeof nname, "%s.%s", name, 1784f8f669cSDavid du Colombier nrp->ptr->name); 179a41547ffSDavid du Colombier rp = dnresolve(nname, class, type, req, cn, depth+1, 1804f8f669cSDavid du Colombier recurse, rooted, status); 181530fef66SDavid du Colombier lock(&dnlock); 182271b8d73SDavid du Colombier rrfreelist(rrremneg(&rp)); 183530fef66SDavid du Colombier unlock(&dnlock); 1847dd7cddfSDavid du Colombier } 1857dd7cddfSDavid du Colombier if(drp != nil) 1866aaebd7dSDavid du Colombier rrfreelist(drp); 1874f8f669cSDavid du Colombier procsetname(procname); 1884f8f669cSDavid du Colombier free(procname); 1893e12c5d1SDavid du Colombier return rp; 1907dd7cddfSDavid du Colombier } 1913e12c5d1SDavid du Colombier 1927dd7cddfSDavid du Colombier /* 1937dd7cddfSDavid du Colombier * try the name directly 1947dd7cddfSDavid du Colombier */ 1957dd7cddfSDavid du Colombier rp = dnresolve1(name, class, type, req, depth, recurse); 196225077b0SDavid du Colombier if(rp == nil) { 197225077b0SDavid du Colombier /* 198225077b0SDavid du Colombier * try it as a canonical name if we weren't told 199225077b0SDavid du Colombier * that the name didn't exist 200225077b0SDavid du Colombier */ 2017dd7cddfSDavid du Colombier dp = dnlookup(name, class, 0); 2024f8f669cSDavid du Colombier if(type != Tptr && dp->respcode != Rname) 203b8b25780SDavid du Colombier for(loops = 0; rp == nil && loops < Maxretries; loops++){ 204b8b25780SDavid du Colombier /* retry cname, then the actual type */ 205225077b0SDavid du Colombier rp = dnresolve1(name, class, Tcname, req, 206225077b0SDavid du Colombier depth, recurse); 2077dd7cddfSDavid du Colombier if(rp == nil) 2087dd7cddfSDavid du Colombier break; 20980ee5cbfSDavid du Colombier 210c73252aeSDavid du Colombier /* rp->host == nil shouldn't happen, but does */ 211c73252aeSDavid du Colombier if(rp->negative || rp->host == nil){ 21280ee5cbfSDavid du Colombier rrfreelist(rp); 21380ee5cbfSDavid du Colombier rp = nil; 21480ee5cbfSDavid du Colombier break; 21580ee5cbfSDavid du Colombier } 2167dd7cddfSDavid du Colombier 2177dd7cddfSDavid du Colombier name = rp->host->name; 218530fef66SDavid du Colombier lock(&dnlock); 2197dd7cddfSDavid du Colombier if(cn) 2207dd7cddfSDavid du Colombier rrcat(cn, rp); 2217dd7cddfSDavid du Colombier else 2227dd7cddfSDavid du Colombier rrfreelist(rp); 223530fef66SDavid du Colombier unlock(&dnlock); 2247dd7cddfSDavid du Colombier 225225077b0SDavid du Colombier rp = dnresolve1(name, class, type, req, 226225077b0SDavid du Colombier depth, recurse); 2277dd7cddfSDavid du Colombier } 2287dd7cddfSDavid du Colombier 2297dd7cddfSDavid du Colombier /* distinction between not found and not good */ 230d9924332SDavid du Colombier if(rp == nil && status != nil && dp->respcode != Rok) 2314f8f669cSDavid du Colombier *status = dp->respcode; 232225077b0SDavid du Colombier } 2334f8f669cSDavid du Colombier procsetname(procname); 2344f8f669cSDavid du Colombier free(procname); 2357dd7cddfSDavid du Colombier return randomize(rp); 2363e12c5d1SDavid du Colombier } 2373e12c5d1SDavid du Colombier 2383cbadd90SDavid du Colombier static void 2393cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req) 2403cbadd90SDavid du Colombier { 2413cbadd90SDavid du Colombier memset(qp, 0, sizeof *qp); 2424e7b9544SDavid du Colombier qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1; 2433cbadd90SDavid du Colombier qp->dp = dp; 2443cbadd90SDavid du Colombier qp->type = type; 245adb31a62SDavid du Colombier if (qp->type != type) 246adb31a62SDavid du Colombier dnslog("queryinit: bogus type %d", type); 2473cbadd90SDavid du Colombier qp->req = req; 2483cbadd90SDavid du Colombier qp->nsrp = nil; 2493cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; 2503cbadd90SDavid du Colombier qp->magic = Querymagic; 2513cbadd90SDavid du Colombier } 2523cbadd90SDavid du Colombier 2533cbadd90SDavid du Colombier static void 2543cbadd90SDavid du Colombier queryck(Query *qp) 2553cbadd90SDavid du Colombier { 2563cbadd90SDavid du Colombier assert(qp); 2573cbadd90SDavid du Colombier assert(qp->magic == Querymagic); 2583cbadd90SDavid du Colombier } 2593cbadd90SDavid du Colombier 2603cbadd90SDavid du Colombier static void 2614e7b9544SDavid du Colombier querydestroy(Query *qp) 2624e7b9544SDavid du Colombier { 2634e7b9544SDavid du Colombier queryck(qp); 264530fef66SDavid du Colombier /* leave udpfd open */ 2654e7b9544SDavid du Colombier if (qp->tcpfd > 0) 2664e7b9544SDavid du Colombier close(qp->tcpfd); 2674e7b9544SDavid du Colombier if (qp->tcpctlfd > 0) { 2684e7b9544SDavid du Colombier hangup(qp->tcpctlfd); 2694e7b9544SDavid du Colombier close(qp->tcpctlfd); 2704e7b9544SDavid du Colombier } 271a41547ffSDavid du Colombier free(qp->dest); 2724e7b9544SDavid du Colombier memset(qp, 0, sizeof *qp); /* prevent accidents */ 2734e7b9544SDavid du Colombier qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1; 2744e7b9544SDavid du Colombier } 2754e7b9544SDavid du Colombier 2764e7b9544SDavid du Colombier static void 2773cbadd90SDavid du Colombier destinit(Dest *p) 2783cbadd90SDavid du Colombier { 2793cbadd90SDavid du Colombier memset(p, 0, sizeof *p); 2803cbadd90SDavid du Colombier p->magic = Destmagic; 2813cbadd90SDavid du Colombier } 2823cbadd90SDavid du Colombier 2833cbadd90SDavid du Colombier static void 2843cbadd90SDavid du Colombier destck(Dest *p) 2853cbadd90SDavid du Colombier { 2863cbadd90SDavid du Colombier assert(p); 2873cbadd90SDavid du Colombier assert(p->magic == Destmagic); 2883cbadd90SDavid du Colombier } 2893cbadd90SDavid du Colombier 290a41547ffSDavid du Colombier /* 291a41547ffSDavid du Colombier * if the response to a query hasn't arrived within 100 ms., 292a41547ffSDavid du Colombier * it's unlikely to arrive at all. after 1 s., it's really unlikely. 293a41547ffSDavid du Colombier * queries for missing RRs are likely to produce time-outs rather than 294a41547ffSDavid du Colombier * negative responses, so cname and aaaa queries are likely to time out, 295a41547ffSDavid du Colombier * thus we don't wait very long for them. 296a41547ffSDavid du Colombier */ 297a41547ffSDavid du Colombier static void 298a41547ffSDavid du Colombier notestats(vlong start, int tmout, int type) 299a41547ffSDavid du Colombier { 300a41547ffSDavid du Colombier qlock(&stats); 301a41547ffSDavid du Colombier if (tmout) { 302a41547ffSDavid du Colombier stats.tmout++; 303a41547ffSDavid du Colombier if (type == Taaaa) 304a41547ffSDavid du Colombier stats.tmoutv6++; 305a41547ffSDavid du Colombier else if (type == Tcname) 306a41547ffSDavid du Colombier stats.tmoutcname++; 307a41547ffSDavid du Colombier } else { 308a41547ffSDavid du Colombier long wait10ths = NS2MS(nsec() - start) / 100; 309a41547ffSDavid du Colombier 31081730632SDavid du Colombier if (wait10ths <= 0) 311a41547ffSDavid du Colombier stats.under10ths[0]++; 312a41547ffSDavid du Colombier else if (wait10ths >= nelem(stats.under10ths)) 313a41547ffSDavid du Colombier stats.under10ths[nelem(stats.under10ths) - 1]++; 314a41547ffSDavid du Colombier else 315a41547ffSDavid du Colombier stats.under10ths[wait10ths]++; 316a41547ffSDavid du Colombier } 317a41547ffSDavid du Colombier qunlock(&stats); 318a41547ffSDavid du Colombier } 319a41547ffSDavid du Colombier 320a41547ffSDavid du Colombier static void 321a41547ffSDavid du Colombier noteinmem(void) 322a41547ffSDavid du Colombier { 323a41547ffSDavid du Colombier qlock(&stats); 324a41547ffSDavid du Colombier stats.answinmem++; 325a41547ffSDavid du Colombier qunlock(&stats); 326a41547ffSDavid du Colombier } 327a41547ffSDavid du Colombier 3283e12c5d1SDavid du Colombier static RR* 32946595261SDavid du Colombier issuequery(Query *qp, char *name, int class, int depth, int recurse) 3303e12c5d1SDavid du Colombier { 331f46c709fSDavid du Colombier char *cp; 332c73252aeSDavid du Colombier DN *nsdp; 33346595261SDavid du Colombier RR *rp, *nsrp, *dbnsrp; 3343cbadd90SDavid du Colombier 3353e12c5d1SDavid du Colombier /* 3364f8f669cSDavid du Colombier * if we're running as just a resolver, query our 3377dd7cddfSDavid du Colombier * designated name servers 338219b2ee8SDavid du Colombier */ 3394f8f669cSDavid du Colombier if(cfg.resolver){ 3407dd7cddfSDavid du Colombier nsrp = randomize(getdnsservers(class)); 3417dd7cddfSDavid du Colombier if(nsrp != nil) { 342c73252aeSDavid du Colombier qp->nsrp = nsrp; 343c73252aeSDavid du Colombier if(netquery(qp, depth+1)){ 3447dd7cddfSDavid du Colombier rrfreelist(nsrp); 345c73252aeSDavid du Colombier return rrlookup(qp->dp, qp->type, OKneg); 3467dd7cddfSDavid du Colombier } 3477dd7cddfSDavid du Colombier rrfreelist(nsrp); 3487dd7cddfSDavid du Colombier } 349219b2ee8SDavid du Colombier } 350219b2ee8SDavid du Colombier 351219b2ee8SDavid du Colombier /* 3523e12c5d1SDavid du Colombier * walk up the domain name looking for 3533e12c5d1SDavid du Colombier * a name server for the domain. 3543e12c5d1SDavid du Colombier */ 3553e12c5d1SDavid du Colombier for(cp = name; cp; cp = walkup(cp)){ 3567dd7cddfSDavid du Colombier /* 3577dd7cddfSDavid du Colombier * if this is a local (served by us) domain, 3587dd7cddfSDavid du Colombier * return answer 3597dd7cddfSDavid du Colombier */ 3607dd7cddfSDavid du Colombier dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0)); 3617dd7cddfSDavid du Colombier if(dbnsrp && dbnsrp->local){ 362c73252aeSDavid du Colombier rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl); 3637dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 3647dd7cddfSDavid du Colombier return rp; 3657dd7cddfSDavid du Colombier } 3667dd7cddfSDavid du Colombier 3677dd7cddfSDavid du Colombier /* 3687dd7cddfSDavid du Colombier * if recursion isn't set, just accept local 3697dd7cddfSDavid du Colombier * entries 3707dd7cddfSDavid du Colombier */ 3717dd7cddfSDavid du Colombier if(recurse == Dontrecurse){ 3727dd7cddfSDavid du Colombier if(dbnsrp) 3737dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 3747dd7cddfSDavid du Colombier continue; 3757dd7cddfSDavid du Colombier } 3767dd7cddfSDavid du Colombier 3777dd7cddfSDavid du Colombier /* look for ns in cache */ 3783e12c5d1SDavid du Colombier nsdp = dnlookup(cp, class, 0); 3797dd7cddfSDavid du Colombier nsrp = nil; 3803e12c5d1SDavid du Colombier if(nsdp) 3817dd7cddfSDavid du Colombier nsrp = randomize(rrlookup(nsdp, Tns, NOneg)); 3827dd7cddfSDavid du Colombier 3837dd7cddfSDavid du Colombier /* if the entry timed out, ignore it */ 3847dd7cddfSDavid du Colombier if(nsrp && nsrp->ttl < now){ 3857dd7cddfSDavid du Colombier rrfreelist(nsrp); 3867dd7cddfSDavid du Colombier nsrp = nil; 3877dd7cddfSDavid du Colombier } 3883e12c5d1SDavid du Colombier 3893e12c5d1SDavid du Colombier if(nsrp){ 3907dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 3917dd7cddfSDavid du Colombier 3924f8f669cSDavid du Colombier /* query the name servers found in cache */ 393c73252aeSDavid du Colombier qp->nsrp = nsrp; 394c73252aeSDavid du Colombier if(netquery(qp, depth+1)){ 3953e12c5d1SDavid du Colombier rrfreelist(nsrp); 396c73252aeSDavid du Colombier return rrlookup(qp->dp, qp->type, OKneg); 3977dd7cddfSDavid du Colombier } 3987dd7cddfSDavid du Colombier rrfreelist(nsrp); 3997dd7cddfSDavid du Colombier continue; 4003e12c5d1SDavid du Colombier } 4013e12c5d1SDavid du Colombier 4027dd7cddfSDavid du Colombier /* use ns from db */ 4037dd7cddfSDavid du Colombier if(dbnsrp){ 4047dd7cddfSDavid du Colombier /* try the name servers found in db */ 405c73252aeSDavid du Colombier qp->nsrp = dbnsrp; 406c73252aeSDavid du Colombier if(netquery(qp, depth+1)){ 4073e12c5d1SDavid du Colombier /* we got an answer */ 4087dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 409c73252aeSDavid du Colombier return rrlookup(qp->dp, qp->type, NOneg); 4103e12c5d1SDavid du Colombier } 4117dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 4123e12c5d1SDavid du Colombier } 4133e12c5d1SDavid du Colombier } 41446595261SDavid du Colombier return nil; 415c73252aeSDavid du Colombier } 416c73252aeSDavid du Colombier 417c73252aeSDavid du Colombier static RR* 418c73252aeSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth, 419c73252aeSDavid du Colombier int recurse) 420c73252aeSDavid du Colombier { 421c73252aeSDavid du Colombier Area *area; 422c73252aeSDavid du Colombier DN *dp; 423c73252aeSDavid du Colombier RR *rp; 424c73252aeSDavid du Colombier Query *qp; 425c73252aeSDavid du Colombier 426c73252aeSDavid du Colombier if(debug) 427c73252aeSDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class); 428c73252aeSDavid du Colombier 429c73252aeSDavid du Colombier /* only class Cin implemented so far */ 430c73252aeSDavid du Colombier if(class != Cin) 431c73252aeSDavid du Colombier return nil; 432c73252aeSDavid du Colombier 433c73252aeSDavid du Colombier dp = dnlookup(name, class, 1); 434c73252aeSDavid du Colombier 435c73252aeSDavid du Colombier /* 436c73252aeSDavid du Colombier * Try the cache first 437c73252aeSDavid du Colombier */ 438c73252aeSDavid du Colombier rp = rrlookup(dp, type, OKneg); 439c73252aeSDavid du Colombier if(rp) 440c73252aeSDavid du Colombier if(rp->db){ 441c73252aeSDavid du Colombier /* unauthoritative db entries are hints */ 442c73252aeSDavid du Colombier if(rp->auth) { 443c73252aeSDavid du Colombier noteinmem(); 444d9924332SDavid du Colombier if(debug) 445d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: auth rr in db", 446d9924332SDavid du Colombier getpid(), name, type, class); 447c73252aeSDavid du Colombier return rp; 448c73252aeSDavid du Colombier } 449c73252aeSDavid du Colombier } else 450c73252aeSDavid du Colombier /* cached entry must still be valid */ 451c73252aeSDavid du Colombier if(rp->ttl > now) 452c73252aeSDavid du Colombier /* but Tall entries are special */ 453c73252aeSDavid du Colombier if(type != Tall || rp->query == Tall) { 454c73252aeSDavid du Colombier noteinmem(); 455d9924332SDavid du Colombier if(debug) 456d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr not in db", 457d9924332SDavid du Colombier getpid(), name, type, class); 458c73252aeSDavid du Colombier return rp; 459c73252aeSDavid du Colombier } 460c73252aeSDavid du Colombier rrfreelist(rp); 46146595261SDavid du Colombier rp = nil; /* accident prevention */ 46246595261SDavid du Colombier USED(rp); 463c73252aeSDavid du Colombier 464c73252aeSDavid du Colombier /* 465c73252aeSDavid du Colombier * try the cache for a canonical name. if found punt 466c73252aeSDavid du Colombier * since we'll find it during the canonical name search 467c73252aeSDavid du Colombier * in dnresolve(). 468c73252aeSDavid du Colombier */ 469c73252aeSDavid du Colombier if(type != Tcname){ 470c73252aeSDavid du Colombier rp = rrlookup(dp, Tcname, NOneg); 471c73252aeSDavid du Colombier rrfreelist(rp); 472d9924332SDavid du Colombier if(rp){ 473d9924332SDavid du Colombier if(debug) 474d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname", 475d9924332SDavid du Colombier getpid(), name, type, class); 476c73252aeSDavid du Colombier return nil; 477c73252aeSDavid du Colombier } 478d9924332SDavid du Colombier } 479c73252aeSDavid du Colombier 480c73252aeSDavid du Colombier /* 481c73252aeSDavid du Colombier * if the domain name is within an area of ours, 482c73252aeSDavid du Colombier * we should have found its data in memory by now. 483c73252aeSDavid du Colombier */ 484c73252aeSDavid du Colombier area = inmyarea(dp->name); 485c73252aeSDavid du Colombier if (area || strncmp(dp->name, "local#", 6) == 0) { 486c73252aeSDavid du Colombier // char buf[32]; 487c73252aeSDavid du Colombier 488c73252aeSDavid du Colombier // dnslog("%s %s: no data in area %s", dp->name, 489c73252aeSDavid du Colombier // rrname(type, buf, sizeof buf), area->soarr->owner->name); 490c73252aeSDavid du Colombier return nil; 491c73252aeSDavid du Colombier } 492c73252aeSDavid du Colombier 493c73252aeSDavid du Colombier qp = emalloc(sizeof *qp); 494c73252aeSDavid du Colombier queryinit(qp, dp, type, req); 49546595261SDavid du Colombier rp = issuequery(qp, name, class, depth, recurse); 496c73252aeSDavid du Colombier querydestroy(qp); 497c73252aeSDavid du Colombier free(qp); 498d9924332SDavid du Colombier if(rp){ 499d9924332SDavid du Colombier if(debug) 500d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from query", 501d9924332SDavid du Colombier getpid(), name, type, class); 502c73252aeSDavid du Colombier return rp; 503d9924332SDavid du Colombier } 5043e12c5d1SDavid du Colombier 5057dd7cddfSDavid du Colombier /* settle for a non-authoritative answer */ 5067dd7cddfSDavid du Colombier rp = rrlookup(dp, type, OKneg); 507d9924332SDavid du Colombier if(rp){ 508d9924332SDavid du Colombier if(debug) 509d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup", 510d9924332SDavid du Colombier getpid(), name, type, class); 5117dd7cddfSDavid du Colombier return rp; 512d9924332SDavid du Colombier } 5137dd7cddfSDavid du Colombier 5147dd7cddfSDavid du Colombier /* noone answered. try the database, we might have a chance. */ 515d9924332SDavid du Colombier rp = dblookup(name, class, type, 0, 0); 516d9924332SDavid du Colombier if (rp) { 517d9924332SDavid du Colombier if(debug) 518d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: rr from dblookup", 519d9924332SDavid du Colombier getpid(), name, type, class); 520d9924332SDavid du Colombier }else{ 521d9924332SDavid du Colombier if(debug) 522d9924332SDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d: no rr from dblookup; crapped out", 523d9924332SDavid du Colombier getpid(), name, type, class); 524d9924332SDavid du Colombier } 525d9924332SDavid du Colombier return rp; 5263e12c5d1SDavid du Colombier } 5273e12c5d1SDavid du Colombier 5283e12c5d1SDavid du Colombier /* 5294f8f669cSDavid du Colombier * walk a domain name one element to the right. 5304f8f669cSDavid du Colombier * return a pointer to that element. 5313e12c5d1SDavid du Colombier * in other words, return a pointer to the parent domain name. 5323e12c5d1SDavid du Colombier */ 5333e12c5d1SDavid du Colombier char* 5343e12c5d1SDavid du Colombier walkup(char *name) 5353e12c5d1SDavid du Colombier { 5363e12c5d1SDavid du Colombier char *cp; 5373e12c5d1SDavid du Colombier 5383e12c5d1SDavid du Colombier cp = strchr(name, '.'); 5393e12c5d1SDavid du Colombier if(cp) 5403e12c5d1SDavid du Colombier return cp+1; 5413e12c5d1SDavid du Colombier else if(*name) 5423e12c5d1SDavid du Colombier return ""; 5433e12c5d1SDavid du Colombier else 5443e12c5d1SDavid du Colombier return 0; 5453e12c5d1SDavid du Colombier } 5463e12c5d1SDavid du Colombier 5473e12c5d1SDavid du Colombier /* 548a41547ffSDavid du Colombier * Get a udp port for sending requests and reading replies. Put the port 5493e12c5d1SDavid du Colombier * into "headers" mode. 5503e12c5d1SDavid du Colombier */ 5513e12c5d1SDavid du Colombier static char *hmsg = "headers"; 5523e12c5d1SDavid du Colombier 553dc5a79c1SDavid du Colombier int 5544f8f669cSDavid du Colombier udpport(char *mtpt) 5553e12c5d1SDavid du Colombier { 5563e12c5d1SDavid du Colombier int fd, ctl; 5574f8f669cSDavid du Colombier char ds[64], adir[64]; 5583e12c5d1SDavid du Colombier 5593e12c5d1SDavid du Colombier /* get a udp port */ 5604f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net")); 5617dd7cddfSDavid du Colombier ctl = announce(ds, adir); 5627dd7cddfSDavid du Colombier if(ctl < 0){ 5637dd7cddfSDavid du Colombier /* warning("can't get udp port"); */ 564bd389b36SDavid du Colombier return -1; 565bd389b36SDavid du Colombier } 5663e12c5d1SDavid du Colombier 5673e12c5d1SDavid du Colombier /* turn on header style interface */ 568410ea80bSDavid du Colombier if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){ 5697dd7cddfSDavid du Colombier close(ctl); 570bd389b36SDavid du Colombier warning(hmsg); 571bd389b36SDavid du Colombier return -1; 572bd389b36SDavid du Colombier } 5733e12c5d1SDavid du Colombier 5747dd7cddfSDavid du Colombier /* grab the data file */ 5754f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/data", adir); 5767dd7cddfSDavid du Colombier fd = open(ds, ORDWR); 5773e12c5d1SDavid du Colombier close(ctl); 5784f8f669cSDavid du Colombier if(fd < 0) 5794f8f669cSDavid du Colombier warning("can't open udp port %s: %r", ds); 5803e12c5d1SDavid du Colombier return fd; 5813e12c5d1SDavid du Colombier } 5823e12c5d1SDavid du Colombier 583d9924332SDavid du Colombier void 584d9924332SDavid du Colombier initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno) 585d9924332SDavid du Colombier { 586d9924332SDavid du Colombier mp->flags = flags; 587d9924332SDavid du Colombier mp->id = reqno; 588d9924332SDavid du Colombier mp->qd = rp; 589*59f7772cSDavid du Colombier if(rp != nil) 590*59f7772cSDavid du Colombier mp->qdcount = 1; 591d9924332SDavid du Colombier } 592d9924332SDavid du Colombier 593d9924332SDavid du Colombier DNSmsg * 594d9924332SDavid du Colombier newdnsmsg(RR *rp, int flags, ushort reqno) 595d9924332SDavid du Colombier { 596d9924332SDavid du Colombier DNSmsg *mp; 597d9924332SDavid du Colombier 598d9924332SDavid du Colombier mp = emalloc(sizeof *mp); 599d9924332SDavid du Colombier initdnsmsg(mp, rp, flags, reqno); 600d9924332SDavid du Colombier return mp; 601d9924332SDavid du Colombier } 602d9924332SDavid du Colombier 603d6d99297SDavid du Colombier /* generate a DNS UDP query packet */ 604dc5a79c1SDavid du Colombier int 605dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno) 6063e12c5d1SDavid du Colombier { 6073e12c5d1SDavid du Colombier DNSmsg m; 6083e12c5d1SDavid du Colombier int len; 609f27a9a5aSDavid du Colombier Udphdr *uh = (Udphdr*)buf; 610d9924332SDavid du Colombier RR *rp; 6113e12c5d1SDavid du Colombier 6123e12c5d1SDavid du Colombier /* stuff port number into output buffer */ 6133cbadd90SDavid du Colombier memset(uh, 0, sizeof *uh); 6147dd7cddfSDavid du Colombier hnputs(uh->rport, 53); 6153e12c5d1SDavid du Colombier 6163e12c5d1SDavid du Colombier /* make request and convert it to output format */ 6173cbadd90SDavid du Colombier memset(&m, 0, sizeof m); 618d9924332SDavid du Colombier rp = rralloc(type); 619d9924332SDavid du Colombier rp->owner = dp; 620d9924332SDavid du Colombier initdnsmsg(&m, rp, flags, reqno); 621f27a9a5aSDavid du Colombier len = convDNS2M(&m, &buf[Udphdrsize], Maxudp); 6227dd7cddfSDavid du Colombier rrfree(m.qd); 6236aaebd7dSDavid du Colombier memset(&m, 0, sizeof m); /* cause trouble */ 6243e12c5d1SDavid du Colombier return len; 6253e12c5d1SDavid du Colombier } 6263e12c5d1SDavid du Colombier 627a41547ffSDavid du Colombier void 6287dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp) 6293e12c5d1SDavid du Colombier { 630e6d9d902SDavid du Colombier rrfreelistptr(&mp->qd); 631e6d9d902SDavid du Colombier rrfreelistptr(&mp->an); 632e6d9d902SDavid du Colombier rrfreelistptr(&mp->ns); 633e6d9d902SDavid du Colombier rrfreelistptr(&mp->ar); 6347dd7cddfSDavid du Colombier } 6357dd7cddfSDavid du Colombier 636b8b25780SDavid du Colombier /* timed read of reply. sets srcip */ 6373cbadd90SDavid du Colombier static int 638b8b25780SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp, 6393cbadd90SDavid du Colombier uchar *srcip) 6403cbadd90SDavid du Colombier { 6413cbadd90SDavid du Colombier int len, fd; 642a41547ffSDavid du Colombier long ms; 643a41547ffSDavid du Colombier vlong startns = nsec(); 6443cbadd90SDavid du Colombier uchar *reply; 6453cbadd90SDavid du Colombier uchar lenbuf[2]; 6463cbadd90SDavid du Colombier 6473cbadd90SDavid du Colombier len = -1; /* pessimism */ 648b8b25780SDavid du Colombier ms = endms - NS2MS(startns); 649b8b25780SDavid du Colombier if (ms <= 0) 650b8b25780SDavid du Colombier return -1; /* taking too long */ 651b8b25780SDavid du Colombier 652b8b25780SDavid du Colombier reply = ibuf; 6534e7b9544SDavid du Colombier memset(srcip, 0, IPaddrlen); 654b8b25780SDavid du Colombier alarm(ms); 655a41547ffSDavid du Colombier if (medium == Udp) 6564e7b9544SDavid du Colombier if (qp->udpfd <= 0) 6574e7b9544SDavid du Colombier dnslog("readnet: qp->udpfd closed"); 6584e7b9544SDavid du Colombier else { 659f27a9a5aSDavid du Colombier len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin); 660a41547ffSDavid du Colombier alarm(0); 661a41547ffSDavid du Colombier notestats(startns, len < 0, qp->type); 6624e7b9544SDavid du Colombier if (len >= IPaddrlen) 6634e7b9544SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 664f27a9a5aSDavid du Colombier if (len >= Udphdrsize) { 665f27a9a5aSDavid du Colombier len -= Udphdrsize; 666f27a9a5aSDavid du Colombier reply += Udphdrsize; 6674e7b9544SDavid du Colombier } 6684e7b9544SDavid du Colombier } 669a41547ffSDavid du Colombier else { 6703cbadd90SDavid du Colombier if (!qp->tcpset) 6713cbadd90SDavid du Colombier dnslog("readnet: tcp params not set"); 6723cbadd90SDavid du Colombier fd = qp->tcpfd; 6733cbadd90SDavid du Colombier if (fd <= 0) 6743cbadd90SDavid du Colombier dnslog("readnet: %s: tcp fd unset for dest %I", 6753cbadd90SDavid du Colombier qp->dp->name, qp->tcpip); 6763cbadd90SDavid du Colombier else if (readn(fd, lenbuf, 2) != 2) { 677e6d9d902SDavid du Colombier dnslog("readnet: short read of 2-byte tcp msg size from %I", 6783cbadd90SDavid du Colombier qp->tcpip); 679a41547ffSDavid du Colombier /* probably a time-out */ 680a41547ffSDavid du Colombier notestats(startns, 1, qp->type); 6813cbadd90SDavid du Colombier } else { 6823cbadd90SDavid du Colombier len = lenbuf[0]<<8 | lenbuf[1]; 6833cbadd90SDavid du Colombier if (readn(fd, ibuf, len) != len) { 6843cbadd90SDavid du Colombier dnslog("readnet: short read of tcp data from %I", 6853cbadd90SDavid du Colombier qp->tcpip); 686a41547ffSDavid du Colombier /* probably a time-out */ 687a41547ffSDavid du Colombier notestats(startns, 1, qp->type); 6883cbadd90SDavid du Colombier len = -1; 6893cbadd90SDavid du Colombier } 6903cbadd90SDavid du Colombier } 6913cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 6923cbadd90SDavid du Colombier } 693b8b25780SDavid du Colombier alarm(0); 6943cbadd90SDavid du Colombier *replyp = reply; 6953cbadd90SDavid du Colombier return len; 6963cbadd90SDavid du Colombier } 6973cbadd90SDavid du Colombier 6987dd7cddfSDavid du Colombier /* 6993cbadd90SDavid du Colombier * read replies to a request and remember the rrs in the answer(s). 7003cbadd90SDavid du Colombier * ignore any of the wrong type. 701b8b25780SDavid du Colombier * wait at most until endms. 7027dd7cddfSDavid du Colombier */ 7037dd7cddfSDavid du Colombier static int 7043cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp, 705b8b25780SDavid du Colombier uvlong endms) 7067dd7cddfSDavid du Colombier { 707b8b25780SDavid du Colombier int len; 7087dd7cddfSDavid du Colombier char *err; 709a41547ffSDavid du Colombier char tbuf[32]; 7103cbadd90SDavid du Colombier uchar *reply; 7113cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 7123e12c5d1SDavid du Colombier RR *rp; 7137dd7cddfSDavid du Colombier 7143cbadd90SDavid du Colombier queryck(qp); 7153cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 716a41547ffSDavid du Colombier memset(srcip, 0, sizeof srcip); 717a41547ffSDavid du Colombier if (0) 718a41547ffSDavid du Colombier len = -1; 719b8b25780SDavid du Colombier for (; timems() < endms && 720b8b25780SDavid du Colombier (len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0; 7213cbadd90SDavid du Colombier freeanswers(mp)){ 7227dd7cddfSDavid du Colombier /* convert into internal format */ 7233cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 7243cbadd90SDavid du Colombier err = convM2DNS(reply, len, mp, nil); 725d6d99297SDavid du Colombier if (mp->flags & Ftrunc) { 726a41547ffSDavid du Colombier free(err); 727a41547ffSDavid du Colombier freeanswers(mp); 728a41547ffSDavid du Colombier /* notify our caller to retry the query via tcp. */ 7293cbadd90SDavid du Colombier return -1; 7303cbadd90SDavid du Colombier } else if(err){ 7313cbadd90SDavid du Colombier dnslog("readreply: %s: input err, len %d: %s: %I", 7323cbadd90SDavid du Colombier qp->dp->name, len, err, srcip); 7333cbadd90SDavid du Colombier free(err); 7347dd7cddfSDavid du Colombier continue; 7357dd7cddfSDavid du Colombier } 7367dd7cddfSDavid du Colombier if(debug) 7373cbadd90SDavid du Colombier logreply(qp->req->id, srcip, mp); 7387dd7cddfSDavid du Colombier 7397dd7cddfSDavid du Colombier /* answering the right question? */ 7403cbadd90SDavid du Colombier if(mp->id != req) 7413cbadd90SDavid du Colombier dnslog("%d: id %d instead of %d: %I", qp->req->id, 7423cbadd90SDavid du Colombier mp->id, req, srcip); 7433cbadd90SDavid du Colombier else if(mp->qd == 0) 7443cbadd90SDavid du Colombier dnslog("%d: no question RR: %I", qp->req->id, srcip); 7453cbadd90SDavid du Colombier else if(mp->qd->owner != qp->dp) 7463cbadd90SDavid du Colombier dnslog("%d: owner %s instead of %s: %I", qp->req->id, 7473cbadd90SDavid du Colombier mp->qd->owner->name, qp->dp->name, srcip); 7483cbadd90SDavid du Colombier else if(mp->qd->type != qp->type) 7493cbadd90SDavid du Colombier dnslog("%d: qp->type %d instead of %d: %I", 7503cbadd90SDavid du Colombier qp->req->id, mp->qd->type, qp->type, srcip); 7513cbadd90SDavid du Colombier else { 7527dd7cddfSDavid du Colombier /* remember what request this is in answer to */ 7537dd7cddfSDavid du Colombier for(rp = mp->an; rp; rp = rp->next) 7543cbadd90SDavid du Colombier rp->query = qp->type; 755b8b25780SDavid du Colombier return 0; 7567dd7cddfSDavid du Colombier } 7577dd7cddfSDavid du Colombier } 758b8b25780SDavid du Colombier if (timems() >= endms) { 759a41547ffSDavid du Colombier ; /* query expired */ 760a41547ffSDavid du Colombier } else if (0) { 761a41547ffSDavid du Colombier /* this happens routinely when a read times out */ 762a41547ffSDavid du Colombier dnslog("readreply: %s type %s: ns %I read error or eof " 763a41547ffSDavid du Colombier "(returned %d): %r", qp->dp->name, rrname(qp->type, 764a41547ffSDavid du Colombier tbuf, sizeof tbuf), srcip, len); 765a41547ffSDavid du Colombier if (medium == Udp) 766a41547ffSDavid du Colombier for (rp = qp->nsrp; rp != nil; rp = rp->next) 767a41547ffSDavid du Colombier if (rp->type == Tns) 768a41547ffSDavid du Colombier dnslog("readreply: %s: query sent to " 769a41547ffSDavid du Colombier "ns %s", qp->dp->name, 770a41547ffSDavid du Colombier rp->host->name); 771a41547ffSDavid du Colombier } 7723cbadd90SDavid du Colombier return -1; 7733cbadd90SDavid du Colombier } 7747dd7cddfSDavid du Colombier 7757dd7cddfSDavid du Colombier /* 7767dd7cddfSDavid du Colombier * return non-0 if first list includes second list 7777dd7cddfSDavid du Colombier */ 7787dd7cddfSDavid du Colombier int 7797dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2) 7807dd7cddfSDavid du Colombier { 7817dd7cddfSDavid du Colombier RR *trp1, *trp2; 7827dd7cddfSDavid du Colombier 7837dd7cddfSDavid du Colombier for(trp2 = rp2; trp2; trp2 = trp2->next){ 7843cbadd90SDavid du Colombier for(trp1 = rp1; trp1; trp1 = trp1->next) 7857dd7cddfSDavid du Colombier if(trp1->type == trp2->type) 7867dd7cddfSDavid du Colombier if(trp1->host == trp2->host) 7877dd7cddfSDavid du Colombier if(trp1->owner == trp2->owner) 7887dd7cddfSDavid du Colombier break; 7894f8f669cSDavid du Colombier if(trp1 == nil) 7907dd7cddfSDavid du Colombier return 0; 7917dd7cddfSDavid du Colombier } 7927dd7cddfSDavid du Colombier return 1; 7937dd7cddfSDavid du Colombier } 7947dd7cddfSDavid du Colombier 7957dd7cddfSDavid du Colombier 7966b6b9ac8SDavid du Colombier /* 7976b6b9ac8SDavid du Colombier * return multicast version if any 7986b6b9ac8SDavid du Colombier */ 7996b6b9ac8SDavid du Colombier int 8006b6b9ac8SDavid du Colombier ipisbm(uchar *ip) 8016b6b9ac8SDavid du Colombier { 8026b6b9ac8SDavid du Colombier if(isv4(ip)){ 8034f8f669cSDavid du Colombier if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 || 8044f8f669cSDavid du Colombier ipcmp(ip, IPv4bcast) == 0) 8056b6b9ac8SDavid du Colombier return 4; 8064f8f669cSDavid du Colombier } else 8076b6b9ac8SDavid du Colombier if(ip[0] == 0xff) 8086b6b9ac8SDavid du Colombier return 6; 80928a8a86bSDavid du Colombier return 0; 81028a8a86bSDavid du Colombier } 81128a8a86bSDavid du Colombier 8127dd7cddfSDavid du Colombier /* 813*59f7772cSDavid du Colombier * Get next server address(es) into qp->dest[nd] and beyond 8147dd7cddfSDavid du Colombier */ 8157dd7cddfSDavid du Colombier static int 8163cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth) 8177dd7cddfSDavid du Colombier { 8187dd7cddfSDavid du Colombier RR *rp, *arp, *trp; 8197dd7cddfSDavid du Colombier Dest *cur; 8207dd7cddfSDavid du Colombier 8217dd7cddfSDavid du Colombier if(nd >= Maxdest) 8227dd7cddfSDavid du Colombier return 0; 8237dd7cddfSDavid du Colombier 8247dd7cddfSDavid du Colombier /* 8257dd7cddfSDavid du Colombier * look for a server whose address we already know. 8267dd7cddfSDavid du Colombier * if we find one, mark it so we ignore this on 8277dd7cddfSDavid du Colombier * subsequent passes. 8287dd7cddfSDavid du Colombier */ 8297dd7cddfSDavid du Colombier arp = 0; 8303cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 83134f77ae3SDavid du Colombier assert(rp->magic == RRmagic); 8327dd7cddfSDavid du Colombier if(rp->marker) 8337dd7cddfSDavid du Colombier continue; 8347dd7cddfSDavid du Colombier arp = rrlookup(rp->host, Ta, NOneg); 835d9924332SDavid du Colombier if(arp == nil) 836d9924332SDavid du Colombier arp = rrlookup(rp->host, Taaaa, NOneg); 8377dd7cddfSDavid du Colombier if(arp){ 8387dd7cddfSDavid du Colombier rp->marker = 1; 8397dd7cddfSDavid du Colombier break; 8407dd7cddfSDavid du Colombier } 8417dd7cddfSDavid du Colombier arp = dblookup(rp->host->name, Cin, Ta, 0, 0); 842d9924332SDavid du Colombier if(arp == nil) 843d9924332SDavid du Colombier arp = dblookup(rp->host->name, Cin, Taaaa, 0, 0); 8447dd7cddfSDavid du Colombier if(arp){ 8457dd7cddfSDavid du Colombier rp->marker = 1; 8467dd7cddfSDavid du Colombier break; 8477dd7cddfSDavid du Colombier } 8487dd7cddfSDavid du Colombier } 8497dd7cddfSDavid du Colombier 8507dd7cddfSDavid du Colombier /* 8517dd7cddfSDavid du Colombier * if the cache and database lookup didn't find any new 8527dd7cddfSDavid du Colombier * server addresses, try resolving one via the network. 8537dd7cddfSDavid du Colombier * Mark any we try to resolve so we don't try a second time. 8547dd7cddfSDavid du Colombier */ 8554f8f669cSDavid du Colombier if(arp == 0) 8563cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 8577dd7cddfSDavid du Colombier if(rp->marker) 8587dd7cddfSDavid du Colombier continue; 8597dd7cddfSDavid du Colombier rp->marker = 1; 8607dd7cddfSDavid du Colombier 8617dd7cddfSDavid du Colombier /* 8627dd7cddfSDavid du Colombier * avoid loops looking up a server under itself 8637dd7cddfSDavid du Colombier */ 8647dd7cddfSDavid du Colombier if(subsume(rp->owner->name, rp->host->name)) 8657dd7cddfSDavid du Colombier continue; 8667dd7cddfSDavid du Colombier 8673cbadd90SDavid du Colombier arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0, 8684f8f669cSDavid du Colombier depth+1, Recurse, 1, 0); 869d9924332SDavid du Colombier if(arp == nil) 870d9924332SDavid du Colombier arp = dnresolve(rp->host->name, Cin, Taaaa, 871d9924332SDavid du Colombier qp->req, 0, depth+1, Recurse, 1, 0); 872530fef66SDavid du Colombier lock(&dnlock); 8737dd7cddfSDavid du Colombier rrfreelist(rrremneg(&arp)); 874530fef66SDavid du Colombier unlock(&dnlock); 8757dd7cddfSDavid du Colombier if(arp) 8767dd7cddfSDavid du Colombier break; 8777dd7cddfSDavid du Colombier } 8787dd7cddfSDavid du Colombier 8797dd7cddfSDavid du Colombier /* use any addresses that we found */ 8803cbadd90SDavid du Colombier for(trp = arp; trp && nd < Maxdest; trp = trp->next){ 8813cbadd90SDavid du Colombier cur = &qp->dest[nd]; 8827dd7cddfSDavid du Colombier parseip(cur->a, trp->ip->name); 8834f8f669cSDavid du Colombier /* 8844f8f669cSDavid du Colombier * straddling servers can reject all nameservers if they are all 8854f8f669cSDavid du Colombier * inside, so be sure to list at least one outside ns at 8864f8f669cSDavid du Colombier * the end of the ns list in /lib/ndb for `dom='. 8874f8f669cSDavid du Colombier */ 8884f8f669cSDavid du Colombier if (ipisbm(cur->a) || 8893cbadd90SDavid du Colombier cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a)) 8906b6b9ac8SDavid du Colombier continue; 8917dd7cddfSDavid du Colombier cur->nx = 0; 8927dd7cddfSDavid du Colombier cur->s = trp->owner; 8937dd7cddfSDavid du Colombier cur->code = Rtimeout; 8946b6b9ac8SDavid du Colombier nd++; 8957dd7cddfSDavid du Colombier } 8967dd7cddfSDavid du Colombier rrfreelist(arp); 8977dd7cddfSDavid du Colombier return nd; 8987dd7cddfSDavid du Colombier } 8997dd7cddfSDavid du Colombier 9007dd7cddfSDavid du Colombier /* 9017dd7cddfSDavid du Colombier * cache negative responses 9027dd7cddfSDavid du Colombier */ 9037dd7cddfSDavid du Colombier static void 9047dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr) 9057dd7cddfSDavid du Colombier { 9067dd7cddfSDavid du Colombier RR *rp; 9077dd7cddfSDavid du Colombier DN *soaowner; 9089a747e4fSDavid du Colombier ulong ttl; 9097dd7cddfSDavid du Colombier 910f46c709fSDavid du Colombier stats.negcached++; 911f46c709fSDavid du Colombier 9124f8f669cSDavid du Colombier /* no cache time specified, don't make anything up */ 9137dd7cddfSDavid du Colombier if(soarr != nil){ 914e6d9d902SDavid du Colombier if(soarr->next != nil) 915e6d9d902SDavid du Colombier rrfreelistptr(&soarr->next); 9167dd7cddfSDavid du Colombier soaowner = soarr->owner; 9177dd7cddfSDavid du Colombier } else 9187dd7cddfSDavid du Colombier soaowner = nil; 9197dd7cddfSDavid du Colombier 9209a747e4fSDavid du Colombier /* the attach can cause soarr to be freed so mine it now */ 9219a747e4fSDavid du Colombier if(soarr != nil && soarr->soa != nil) 9229a747e4fSDavid du Colombier ttl = soarr->soa->minttl+now; 9239a747e4fSDavid du Colombier else 9249a747e4fSDavid du Colombier ttl = 5*Min; 9259a747e4fSDavid du Colombier 9267dd7cddfSDavid du Colombier /* add soa and negative RR to the database */ 9276dc4800dSDavid du Colombier rrattach(soarr, Authoritative); 9287dd7cddfSDavid du Colombier 9297dd7cddfSDavid du Colombier rp = rralloc(type); 9307dd7cddfSDavid du Colombier rp->owner = dp; 9317dd7cddfSDavid du Colombier rp->negative = 1; 9327dd7cddfSDavid du Colombier rp->negsoaowner = soaowner; 9337dd7cddfSDavid du Colombier rp->negrcode = rcode; 9349a747e4fSDavid du Colombier rp->ttl = ttl; 9356dc4800dSDavid du Colombier rrattach(rp, Authoritative); 9367dd7cddfSDavid du Colombier } 9377dd7cddfSDavid du Colombier 9384f8f669cSDavid du Colombier static int 9394f8f669cSDavid du Colombier setdestoutns(Dest *p, int n) 9404f8f669cSDavid du Colombier { 9414f8f669cSDavid du Colombier uchar *outns = outsidens(n); 9424f8f669cSDavid du Colombier 9433cbadd90SDavid du Colombier destck(p); 9443cbadd90SDavid du Colombier destinit(p); 9454f8f669cSDavid du Colombier if (outns == nil) { 9464f8f669cSDavid du Colombier if (n == 0) 9474f8f669cSDavid du Colombier dnslog("[%d] no outside-ns in ndb", getpid()); 9484f8f669cSDavid du Colombier return -1; 9494f8f669cSDavid du Colombier } 9504f8f669cSDavid du Colombier memmove(p->a, outns, sizeof p->a); 9514f8f669cSDavid du Colombier p->s = dnlookup("outside-ns-ips", Cin, 1); 9524f8f669cSDavid du Colombier return 0; 9534f8f669cSDavid du Colombier } 9544f8f669cSDavid du Colombier 9557dd7cddfSDavid du Colombier /* 9563cbadd90SDavid du Colombier * issue query via UDP or TCP as appropriate. 9573cbadd90SDavid du Colombier * for TCP, returns with qp->tcpip set from udppkt header. 9587dd7cddfSDavid du Colombier */ 9597dd7cddfSDavid du Colombier static int 9603cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len) 9617dd7cddfSDavid du Colombier { 962a41547ffSDavid du Colombier int rv = -1, nfd; 9633cbadd90SDavid du Colombier char *domain; 964cc499a30SDavid du Colombier char conndir[40], net[40]; 965a41547ffSDavid du Colombier uchar belen[2]; 9663cbadd90SDavid du Colombier NetConnInfo *nci; 9674f8f669cSDavid du Colombier 9683cbadd90SDavid du Colombier queryck(qp); 9694e153993SDavid du Colombier domain = smprint("%I", udppkt); 9704e153993SDavid du Colombier if (myaddr(domain)) { 9714e153993SDavid du Colombier dnslog("mydnsquery: trying to send to myself (%s); bzzzt", 9724e153993SDavid du Colombier domain); 9734e153993SDavid du Colombier free(domain); 9744e153993SDavid du Colombier return rv; 9754e153993SDavid du Colombier } 9764e153993SDavid du Colombier 9773cbadd90SDavid du Colombier switch (medium) { 9783cbadd90SDavid du Colombier case Udp: 9794e153993SDavid du Colombier free(domain); 980a41547ffSDavid du Colombier nfd = dup(qp->udpfd, -1); 981a41547ffSDavid du Colombier if (nfd < 0) { 982a41547ffSDavid du Colombier warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd); 983a41547ffSDavid du Colombier close(qp->udpfd); /* ensure it's closed */ 984a41547ffSDavid du Colombier qp->udpfd = -1; /* poison it */ 985a41547ffSDavid du Colombier return rv; 986a41547ffSDavid du Colombier } 987a41547ffSDavid du Colombier close(nfd); 988a41547ffSDavid du Colombier 9894e7b9544SDavid du Colombier if (qp->udpfd <= 0) 990a41547ffSDavid du Colombier dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd); 9914e7b9544SDavid du Colombier else { 992f27a9a5aSDavid du Colombier if (write(qp->udpfd, udppkt, len+Udphdrsize) != 993f27a9a5aSDavid du Colombier len+Udphdrsize) 994a41547ffSDavid du Colombier warning("sending udp msg: %r"); 995a41547ffSDavid du Colombier else { 996a41547ffSDavid du Colombier stats.qsent++; 9973cbadd90SDavid du Colombier rv = 0; 9984e7b9544SDavid du Colombier } 999a41547ffSDavid du Colombier } 10003cbadd90SDavid du Colombier break; 10013cbadd90SDavid du Colombier case Tcp: 10023cbadd90SDavid du Colombier /* send via TCP & keep fd around for reply */ 1003cc499a30SDavid du Colombier snprint(net, sizeof net, "%s/tcp", 1004cc499a30SDavid du Colombier (mntpt[0] != '\0'? mntpt: "/net")); 10053cbadd90SDavid du Colombier alarm(10*1000); 1006cc499a30SDavid du Colombier qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil, 10073cbadd90SDavid du Colombier conndir, &qp->tcpctlfd); 10083cbadd90SDavid du Colombier alarm(0); 10093cbadd90SDavid du Colombier if (qp->tcpfd < 0) { 10103cbadd90SDavid du Colombier dnslog("can't dial tcp!%s!dns: %r", domain); 1011a41547ffSDavid du Colombier free(domain); 1012a41547ffSDavid du Colombier break; 1013a41547ffSDavid du Colombier } 1014a41547ffSDavid du Colombier free(domain); 10153cbadd90SDavid du Colombier nci = getnetconninfo(conndir, qp->tcpfd); 10163cbadd90SDavid du Colombier if (nci) { 10173cbadd90SDavid du Colombier parseip(qp->tcpip, nci->rsys); 10183cbadd90SDavid du Colombier freenetconninfo(nci); 10193cbadd90SDavid du Colombier } else 10203cbadd90SDavid du Colombier dnslog("mydnsquery: getnetconninfo failed"); 10213cbadd90SDavid du Colombier qp->tcpset = 1; 10223e12c5d1SDavid du Colombier 10233cbadd90SDavid du Colombier belen[0] = len >> 8; 10243cbadd90SDavid du Colombier belen[1] = len; 10253cbadd90SDavid du Colombier if (write(qp->tcpfd, belen, 2) != 2 || 1026f27a9a5aSDavid du Colombier write(qp->tcpfd, udppkt + Udphdrsize, len) != len) 1027a41547ffSDavid du Colombier warning("sending tcp msg: %r"); 10283cbadd90SDavid du Colombier break; 10293cbadd90SDavid du Colombier default: 10303cbadd90SDavid du Colombier sysfatal("mydnsquery: bad medium"); 10313cbadd90SDavid du Colombier } 10323cbadd90SDavid du Colombier return rv; 10333cbadd90SDavid du Colombier } 10347dd7cddfSDavid du Colombier 10353e12c5d1SDavid du Colombier /* 10363cbadd90SDavid du Colombier * send query to all UDP destinations or one TCP destination, 10373cbadd90SDavid du Colombier * taken from obuf (udp packet) header 10383e12c5d1SDavid du Colombier */ 10393cbadd90SDavid du Colombier static int 10403cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len) 10413cbadd90SDavid du Colombier { 10423cbadd90SDavid du Colombier int j, n; 10433cbadd90SDavid du Colombier char buf[32]; 10443cbadd90SDavid du Colombier Dest *p; 10453e12c5d1SDavid du Colombier 10463cbadd90SDavid du Colombier queryck(qp); 1047b8b25780SDavid du Colombier if(timems() >= qp->req->aborttime) 10483cbadd90SDavid du Colombier return -1; 10493cbadd90SDavid du Colombier 10503cbadd90SDavid du Colombier /* 10513cbadd90SDavid du Colombier * get a nameserver address if we need one. 10523cbadd90SDavid du Colombier * serveraddrs populates qp->dest. 10533cbadd90SDavid du Colombier */ 10543cbadd90SDavid du Colombier p = qp->dest; 10553cbadd90SDavid du Colombier destck(p); 10563cbadd90SDavid du Colombier if (qp->ndest < 0 || qp->ndest > Maxdest) 10573cbadd90SDavid du Colombier dnslog("qp->ndest %d out of range", qp->ndest); 10583468a491SDavid du Colombier if (qp->ndest > qp->curdest - p) { 10593468a491SDavid du Colombier j = serveraddrs(qp, qp->curdest - p, depth); 10603468a491SDavid du Colombier if (j < 0 || j >= Maxdest) { 10613468a491SDavid du Colombier dnslog("serveraddrs() result %d out of range", j); 10623468a491SDavid du Colombier abort(); 10633468a491SDavid du Colombier } 10643468a491SDavid du Colombier qp->curdest = &qp->dest[j]; 10653468a491SDavid du Colombier } 10663cbadd90SDavid du Colombier destck(qp->curdest); 10677dd7cddfSDavid du Colombier 10687dd7cddfSDavid du Colombier /* no servers, punt */ 10693468a491SDavid du Colombier if (qp->ndest == 0) 10704f8f669cSDavid du Colombier if (cfg.straddle && cfg.inside) { 10713cbadd90SDavid du Colombier /* get ips of "outside-ns-ips" */ 10723cbadd90SDavid du Colombier p = qp->curdest = qp->dest; 10733cbadd90SDavid du Colombier for(n = 0; n < Maxdest; n++, qp->curdest++) 10743cbadd90SDavid du Colombier if (setdestoutns(qp->curdest, n) < 0) 10757dd7cddfSDavid du Colombier break; 10764f8f669cSDavid du Colombier } else { 10774e7b9544SDavid du Colombier /* it's probably just a bogus domain, don't log it */ 10784e7b9544SDavid du Colombier // dnslog("xmitquery: %s: no nameservers", qp->dp->name); 10793cbadd90SDavid du Colombier return -1; 10804f8f669cSDavid du Colombier } 10817dd7cddfSDavid du Colombier 10823cbadd90SDavid du Colombier /* send to first 'qp->ndest' destinations */ 10837dd7cddfSDavid du Colombier j = 0; 10843cbadd90SDavid du Colombier if (medium == Tcp) { 10853cbadd90SDavid du Colombier j++; 10863cbadd90SDavid du Colombier queryck(qp); 10873cbadd90SDavid du Colombier assert(qp->dp); 10883cbadd90SDavid du Colombier procsetname("tcp %sside query for %s %s", (inns? "in": "out"), 10893cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 10903cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */ 10913cbadd90SDavid du Colombier if(debug) 10923cbadd90SDavid du Colombier logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name, 10933cbadd90SDavid du Colombier qp->type); 10943cbadd90SDavid du Colombier } else 10953cbadd90SDavid du Colombier for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){ 10967dd7cddfSDavid du Colombier /* skip destinations we've finished with */ 10977dd7cddfSDavid du Colombier if(p->nx >= Maxtrans) 10987dd7cddfSDavid du Colombier continue; 10997dd7cddfSDavid du Colombier 11007dd7cddfSDavid du Colombier j++; 11017dd7cddfSDavid du Colombier 11027dd7cddfSDavid du Colombier /* exponential backoff of requests */ 11033cbadd90SDavid du Colombier if((1<<p->nx) > qp->ndest) 11047dd7cddfSDavid du Colombier continue; 11057dd7cddfSDavid du Colombier 1106*59f7772cSDavid du Colombier if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0) 1107*59f7772cSDavid du Colombier continue; /* mistake */ 1108*59f7772cSDavid du Colombier 11093cbadd90SDavid du Colombier procsetname("udp %sside query to %I/%s %s %s", 11103cbadd90SDavid du Colombier (inns? "in": "out"), p->a, p->s->name, 11113cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 1112219b2ee8SDavid du Colombier if(debug) 11133cbadd90SDavid du Colombier logsend(qp->req->id, depth, p->a, p->s->name, 11143cbadd90SDavid du Colombier qp->dp->name, qp->type); 11154f8f669cSDavid du Colombier 11163cbadd90SDavid du Colombier /* fill in UDP destination addr & send it */ 11173cbadd90SDavid du Colombier memmove(obuf, p->a, sizeof p->a); 11183cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); 11197dd7cddfSDavid du Colombier p->nx++; 11203e12c5d1SDavid du Colombier } 11213cbadd90SDavid du Colombier if(j == 0) { 11223cbadd90SDavid du Colombier // dnslog("xmitquery: %s: no destinations left", qp->dp->name); 11233cbadd90SDavid du Colombier return -1; 11243cbadd90SDavid du Colombier } 11253cbadd90SDavid du Colombier return 0; 11263cbadd90SDavid du Colombier } 11273e12c5d1SDavid du Colombier 1128f46c709fSDavid du Colombier static int lckindex[Maxlcks] = { 1129f46c709fSDavid du Colombier 0, /* all others map here */ 1130f46c709fSDavid du Colombier Ta, 1131f46c709fSDavid du Colombier Tns, 1132f46c709fSDavid du Colombier Tcname, 1133f46c709fSDavid du Colombier Tsoa, 1134f46c709fSDavid du Colombier Tptr, 1135f46c709fSDavid du Colombier Tmx, 1136f46c709fSDavid du Colombier Ttxt, 1137f46c709fSDavid du Colombier Taaaa, 1138f46c709fSDavid du Colombier }; 1139f46c709fSDavid du Colombier 1140f46c709fSDavid du Colombier static int 1141f46c709fSDavid du Colombier qtype2lck(int qtype) /* map query type to querylck index */ 1142f46c709fSDavid du Colombier { 1143f46c709fSDavid du Colombier int i; 1144f46c709fSDavid du Colombier 1145f46c709fSDavid du Colombier for (i = 1; i < nelem(lckindex); i++) 1146f46c709fSDavid du Colombier if (lckindex[i] == qtype) 1147f46c709fSDavid du Colombier return i; 1148f46c709fSDavid du Colombier return 0; 1149f46c709fSDavid du Colombier } 1150f46c709fSDavid du Colombier 11510319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */ 11520319257bSDavid du Colombier static int 11530319257bSDavid du Colombier isnegrname(DNSmsg *mp) 11540319257bSDavid du Colombier { 11550319257bSDavid du Colombier /* TODO: could add || cfg.justforw to RHS of && */ 11560319257bSDavid du Colombier return mp->an == nil && (mp->flags & Rmask) == Rname; 11570319257bSDavid du Colombier } 11580319257bSDavid du Colombier 11593cbadd90SDavid du Colombier static int 11603cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) 11613cbadd90SDavid du Colombier { 11623cbadd90SDavid du Colombier int rv; 11634e5f5f32SDavid du Colombier // int lcktype; 11643cbadd90SDavid du Colombier char buf[32]; 11653cbadd90SDavid du Colombier DN *ndp; 1166c73252aeSDavid du Colombier Query *nqp; 11673cbadd90SDavid du Colombier RR *tp, *soarr; 11687dd7cddfSDavid du Colombier 1169f46c709fSDavid du Colombier if (mp->an == nil) 1170f46c709fSDavid du Colombier stats.negans++; 1171f46c709fSDavid du Colombier 117259cc4ca5SDavid du Colombier /* ignore any error replies */ 11733cbadd90SDavid du Colombier if((mp->flags & Rmask) == Rserver){ 11740319257bSDavid du Colombier stats.negserver++; 1175a41547ffSDavid du Colombier freeanswers(mp); 11763cbadd90SDavid du Colombier if(p != qp->curdest) 11777dd7cddfSDavid du Colombier p->code = Rserver; 11783cbadd90SDavid du Colombier return -1; 11793e12c5d1SDavid du Colombier } 11803e12c5d1SDavid du Colombier 118159cc4ca5SDavid du Colombier /* ignore any bad delegations */ 11823cbadd90SDavid du Colombier if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){ 11830319257bSDavid du Colombier stats.negbaddeleg++; 11843cbadd90SDavid du Colombier if(mp->an == nil){ 11850319257bSDavid du Colombier stats.negbdnoans++; 1186a41547ffSDavid du Colombier freeanswers(mp); 11873cbadd90SDavid du Colombier if(p != qp->curdest) 118859cc4ca5SDavid du Colombier p->code = Rserver; 11893cbadd90SDavid du Colombier return -1; 119059cc4ca5SDavid du Colombier } 1191e6d9d902SDavid du Colombier rrfreelistptr(&mp->ns); 119259cc4ca5SDavid du Colombier } 119359cc4ca5SDavid du Colombier 11947dd7cddfSDavid du Colombier /* remove any soa's from the authority section */ 1195530fef66SDavid du Colombier lock(&dnlock); 11963cbadd90SDavid du Colombier soarr = rrremtype(&mp->ns, Tsoa); 11977dd7cddfSDavid du Colombier 11983e12c5d1SDavid du Colombier /* incorporate answers */ 11994e153993SDavid du Colombier unique(mp->an); 12004e153993SDavid du Colombier unique(mp->ns); 12014e153993SDavid du Colombier unique(mp->ar); 1202530fef66SDavid du Colombier unlock(&dnlock); 1203e6d9d902SDavid du Colombier 12043cbadd90SDavid du Colombier if(mp->an) 12053cbadd90SDavid du Colombier rrattach(mp->an, (mp->flags & Fauth) != 0); 12063cbadd90SDavid du Colombier if(mp->ar) 12076dc4800dSDavid du Colombier rrattach(mp->ar, Notauthoritative); 1208a41547ffSDavid du Colombier if(mp->ns && !cfg.justforw){ 12093cbadd90SDavid du Colombier ndp = mp->ns->owner; 12106dc4800dSDavid du Colombier rrattach(mp->ns, Notauthoritative); 12110319257bSDavid du Colombier } else { 12124f8f669cSDavid du Colombier ndp = nil; 1213e6d9d902SDavid du Colombier rrfreelistptr(&mp->ns); 12140319257bSDavid du Colombier } 12157dd7cddfSDavid du Colombier 12167dd7cddfSDavid du Colombier /* free the question */ 1217e6d9d902SDavid du Colombier if(mp->qd) 1218e6d9d902SDavid du Colombier rrfreelistptr(&mp->qd); 12193e12c5d1SDavid du Colombier 12203e12c5d1SDavid du Colombier /* 12213e12c5d1SDavid du Colombier * Any reply from an authoritative server, 1222a41547ffSDavid du Colombier * or a positive reply terminates the search. 1223a41547ffSDavid du Colombier * A negative response now also terminates the search. 12243e12c5d1SDavid du Colombier */ 12253cbadd90SDavid du Colombier if(mp->an != nil || (mp->flags & Fauth)){ 12260319257bSDavid du Colombier if(isnegrname(mp)) 12273cbadd90SDavid du Colombier qp->dp->respcode = Rname; 12287dd7cddfSDavid du Colombier else 1229d9924332SDavid du Colombier qp->dp->respcode = Rok; 12307dd7cddfSDavid du Colombier 12317dd7cddfSDavid du Colombier /* 1232a41547ffSDavid du Colombier * cache any negative responses, free soarr. 1233a41547ffSDavid du Colombier * negative responses need not be authoritative: 1234a41547ffSDavid du Colombier * they can legitimately come from a cache. 12357dd7cddfSDavid du Colombier */ 1236a41547ffSDavid du Colombier if( /* (mp->flags & Fauth) && */ mp->an == nil) 12373cbadd90SDavid du Colombier cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr); 12387dd7cddfSDavid du Colombier else 12397dd7cddfSDavid du Colombier rrfreelist(soarr); 12403e12c5d1SDavid du Colombier return 1; 12410319257bSDavid du Colombier } else if (isnegrname(mp)) { 1242a41547ffSDavid du Colombier qp->dp->respcode = Rname; 1243a41547ffSDavid du Colombier /* 1244a41547ffSDavid du Colombier * cache negative response. 1245a41547ffSDavid du Colombier * negative responses need not be authoritative: 1246a41547ffSDavid du Colombier * they can legitimately come from a cache. 1247a41547ffSDavid du Colombier */ 1248a41547ffSDavid du Colombier cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr); 1249a41547ffSDavid du Colombier return 1; 12503e12c5d1SDavid du Colombier } 12510319257bSDavid du Colombier stats.negnorname++; 12527dd7cddfSDavid du Colombier rrfreelist(soarr); 12533e12c5d1SDavid du Colombier 12543e12c5d1SDavid du Colombier /* 1255a41547ffSDavid du Colombier * if we've been given better name servers, recurse. 1256a41547ffSDavid du Colombier * if we're a pure resolver, don't recurse, we have 1257a41547ffSDavid du Colombier * to forward to a fixed set of named servers. 12583e12c5d1SDavid du Colombier */ 1259a41547ffSDavid du Colombier if(!mp->ns || cfg.resolver && cfg.justforw) 12603cbadd90SDavid du Colombier return 0; 12617dd7cddfSDavid du Colombier tp = rrlookup(ndp, Tns, NOneg); 12623cbadd90SDavid du Colombier if(contains(qp->nsrp, tp)){ 12637dd7cddfSDavid du Colombier rrfreelist(tp); 12643cbadd90SDavid du Colombier return 0; 12653e12c5d1SDavid du Colombier } 12663cbadd90SDavid du Colombier procsetname("recursive query for %s %s", qp->dp->name, 12673cbadd90SDavid du Colombier rrname(qp->type, buf, sizeof buf)); 12684e5f5f32SDavid du Colombier /* 12694e5f5f32SDavid du Colombier * we're called from udpquery, called from 12704e5f5f32SDavid du Colombier * netquery, which current holds qp->dp->querylck, 12714e5f5f32SDavid du Colombier * so release it now and acquire it upon return. 12724e5f5f32SDavid du Colombier */ 1273e6d9d902SDavid du Colombier // lcktype = qtype2lck(qp->type); /* someday try this again */ 12744e5f5f32SDavid du Colombier // qunlock(&qp->dp->querylck[lcktype]); 12753cbadd90SDavid du Colombier 1276c73252aeSDavid du Colombier nqp = emalloc(sizeof *nqp); 1277c73252aeSDavid du Colombier queryinit(nqp, qp->dp, qp->type, qp->req); 1278c73252aeSDavid du Colombier nqp->nsrp = tp; 1279c73252aeSDavid du Colombier rv = netquery(nqp, depth+1); 12803cbadd90SDavid du Colombier 12814e5f5f32SDavid du Colombier // qlock(&qp->dp->querylck[lcktype]); 1282adb31a62SDavid du Colombier rrfreelist(nqp->nsrp); 1283c73252aeSDavid du Colombier querydestroy(nqp); 1284c73252aeSDavid du Colombier free(nqp); 12853cbadd90SDavid du Colombier return rv; 12863cbadd90SDavid du Colombier } 12873cbadd90SDavid du Colombier 12883cbadd90SDavid du Colombier /* 12893cbadd90SDavid du Colombier * send a query via tcp to a single address (from ibuf's udp header) 12903cbadd90SDavid du Colombier * and read the answer(s) into mp->an. 12913cbadd90SDavid du Colombier */ 12923cbadd90SDavid du Colombier static int 12933cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len, 1294b8b25780SDavid du Colombier ulong waitms, int inns, ushort req) 12953cbadd90SDavid du Colombier { 12964e7b9544SDavid du Colombier int rv = 0; 1297b8b25780SDavid du Colombier uvlong endms; 12983cbadd90SDavid du Colombier 1299b8b25780SDavid du Colombier endms = timems() + waitms; 1300b8b25780SDavid du Colombier if(endms > qp->req->aborttime) 1301b8b25780SDavid du Colombier endms = qp->req->aborttime; 13023cbadd90SDavid du Colombier 1303a41547ffSDavid du Colombier if (0) 13044e7b9544SDavid du Colombier dnslog("%s: udp reply truncated; retrying query via tcp to %I", 13054e7b9544SDavid du Colombier qp->dp->name, qp->tcpip); 13064e7b9544SDavid du Colombier 13073cbadd90SDavid du Colombier qlock(&qp->tcplock); 13083cbadd90SDavid du Colombier memmove(obuf, ibuf, IPaddrlen); /* send back to respondent */ 13093cbadd90SDavid du Colombier /* sets qp->tcpip from obuf's udp header */ 13104e7b9544SDavid du Colombier if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 || 1311b8b25780SDavid du Colombier readreply(qp, Tcp, req, ibuf, mp, endms) < 0) 13124e7b9544SDavid du Colombier rv = -1; 13133cbadd90SDavid du Colombier if (qp->tcpfd > 0) { 13143cbadd90SDavid du Colombier hangup(qp->tcpctlfd); 13153cbadd90SDavid du Colombier close(qp->tcpctlfd); 13163cbadd90SDavid du Colombier close(qp->tcpfd); 13173cbadd90SDavid du Colombier } 13183cbadd90SDavid du Colombier qp->tcpfd = qp->tcpctlfd = -1; 13193cbadd90SDavid du Colombier qunlock(&qp->tcplock); 13204e7b9544SDavid du Colombier return rv; 13213cbadd90SDavid du Colombier } 13223cbadd90SDavid du Colombier 13233cbadd90SDavid du Colombier /* 1324*59f7772cSDavid du Colombier * query name servers. fill in obuf with on-the-wire representation of a 1325*59f7772cSDavid du Colombier * DNSmsg derived from qp. if the name server returns a pointer to another 13263cbadd90SDavid du Colombier * name server, recurse. 13273cbadd90SDavid du Colombier */ 13283cbadd90SDavid du Colombier static int 1329b8b25780SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns) 13303cbadd90SDavid du Colombier { 13313cbadd90SDavid du Colombier int ndest, len, replywaits, rv; 13323cbadd90SDavid du Colombier ushort req; 1333b8b25780SDavid du Colombier uvlong endms; 13343cbadd90SDavid du Colombier char buf[12]; 13353cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 1336a41547ffSDavid du Colombier Dest *p, *np, *dest; 13373cbadd90SDavid du Colombier 13383cbadd90SDavid du Colombier /* pack request into a udp message */ 13393cbadd90SDavid du Colombier req = rand(); 13403cbadd90SDavid du Colombier len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req); 13413cbadd90SDavid du Colombier 13423cbadd90SDavid du Colombier /* no server addresses yet */ 13433cbadd90SDavid du Colombier queryck(qp); 1344a41547ffSDavid du Colombier dest = emalloc(Maxdest * sizeof *dest); /* dest can't be on stack */ 1345a41547ffSDavid du Colombier for (p = dest; p < dest + Maxdest; p++) 13463cbadd90SDavid du Colombier destinit(p); 13471eb0187fSDavid du Colombier /* this dest array is local to this call of queryns() */ 13481eb0187fSDavid du Colombier free(qp->dest); 13493cbadd90SDavid du Colombier qp->curdest = qp->dest = dest; 13503cbadd90SDavid du Colombier 13513cbadd90SDavid du Colombier /* 13523cbadd90SDavid du Colombier * transmit udp requests and wait for answers. 13533cbadd90SDavid du Colombier * at most Maxtrans attempts to each address. 13543cbadd90SDavid du Colombier * each cycle send one more message than the previous. 13553cbadd90SDavid du Colombier * retry a query via tcp if its response is truncated. 13563cbadd90SDavid du Colombier */ 13573cbadd90SDavid du Colombier for(ndest = 1; ndest < Maxdest; ndest++){ 13583cbadd90SDavid du Colombier qp->ndest = ndest; 13593cbadd90SDavid du Colombier qp->tcpset = 0; 13603cbadd90SDavid du Colombier if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0) 13613cbadd90SDavid du Colombier break; 13623cbadd90SDavid du Colombier 1363b8b25780SDavid du Colombier endms = timems() + waitms; 1364b8b25780SDavid du Colombier if(endms > qp->req->aborttime) 1365b8b25780SDavid du Colombier endms = qp->req->aborttime; 13663cbadd90SDavid du Colombier 13673cbadd90SDavid du Colombier for(replywaits = 0; replywaits < ndest; replywaits++){ 136881730632SDavid du Colombier DNSmsg m; 136981730632SDavid du Colombier 1370f46c709fSDavid du Colombier procsetname("reading %sside reply from %I: %s %s from %s", 1371a41547ffSDavid du Colombier (inns? "in": "out"), obuf, qp->dp->name, 1372f46c709fSDavid du Colombier rrname(qp->type, buf, sizeof buf), qp->req->from); 13733cbadd90SDavid du Colombier 137481730632SDavid du Colombier /* read udp answer into m */ 1375b8b25780SDavid du Colombier if (readreply(qp, Udp, req, ibuf, &m, endms) >= 0) 13763cbadd90SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 137781730632SDavid du Colombier else if (!(m.flags & Ftrunc)) { 137881730632SDavid du Colombier freeanswers(&m); 13793cbadd90SDavid du Colombier break; /* timed out on this dest */ 138081730632SDavid du Colombier } else { 13813cbadd90SDavid du Colombier /* whoops, it was truncated! ask again via tcp */ 13826aaebd7dSDavid du Colombier freeanswers(&m); 13833cbadd90SDavid du Colombier rv = tcpquery(qp, &m, depth, ibuf, obuf, len, 1384b8b25780SDavid du Colombier waitms, inns, req); /* answer in m */ 138581730632SDavid du Colombier if (rv < 0) { 138681730632SDavid du Colombier freeanswers(&m); 13873cbadd90SDavid du Colombier break; /* failed via tcp too */ 138881730632SDavid du Colombier } 13893cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 13903cbadd90SDavid du Colombier } 13913cbadd90SDavid du Colombier 13923cbadd90SDavid du Colombier /* find responder */ 13934e5f5f32SDavid du Colombier // dnslog("queryns got reply from %I", srcip); 13943cbadd90SDavid du Colombier for(p = qp->dest; p < qp->curdest; p++) 13953cbadd90SDavid du Colombier if(memcmp(p->a, srcip, sizeof p->a) == 0) 13963cbadd90SDavid du Colombier break; 13973cbadd90SDavid du Colombier 13983cbadd90SDavid du Colombier /* remove all addrs of responding server from list */ 13993cbadd90SDavid du Colombier for(np = qp->dest; np < qp->curdest; np++) 14003cbadd90SDavid du Colombier if(np->s == p->s) 14013cbadd90SDavid du Colombier p->nx = Maxtrans; 14023cbadd90SDavid du Colombier 140381730632SDavid du Colombier /* free or incorporate RRs in m */ 14043cbadd90SDavid du Colombier rv = procansw(qp, &m, srcip, depth, p); 14051eb0187fSDavid du Colombier if (rv > 0) { 14061eb0187fSDavid du Colombier free(qp->dest); 14071eb0187fSDavid du Colombier qp->dest = qp->curdest = nil; /* prevent accidents */ 14083cbadd90SDavid du Colombier return rv; 14093e12c5d1SDavid du Colombier } 14103e12c5d1SDavid du Colombier } 14111eb0187fSDavid du Colombier } 14127dd7cddfSDavid du Colombier 14134f8f669cSDavid du Colombier /* if all servers returned failure, propagate it */ 14143cbadd90SDavid du Colombier qp->dp->respcode = Rserver; 14153cbadd90SDavid du Colombier for(p = dest; p < qp->curdest; p++) { 14163cbadd90SDavid du Colombier destck(p); 14177dd7cddfSDavid du Colombier if(p->code != Rserver) 1418d9924332SDavid du Colombier qp->dp->respcode = Rok; 14193cbadd90SDavid du Colombier p->magic = 0; /* prevent accidents */ 14203cbadd90SDavid du Colombier } 14214f8f669cSDavid du Colombier 14223cbadd90SDavid du Colombier // if (qp->dp->respcode) 14234e5f5f32SDavid du Colombier // dnslog("queryns setting Rserver for %s", qp->dp->name); 14247dd7cddfSDavid du Colombier 1425a41547ffSDavid du Colombier free(qp->dest); 14263cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; /* prevent accidents */ 14273e12c5d1SDavid du Colombier return 0; 14283e12c5d1SDavid du Colombier } 14297dd7cddfSDavid du Colombier 14304f8f669cSDavid du Colombier /* 14314f8f669cSDavid du Colombier * run a command with a supplied fd as standard input 14324f8f669cSDavid du Colombier */ 14334f8f669cSDavid du Colombier char * 14344f8f669cSDavid du Colombier system(int fd, char *cmd) 143528a8a86bSDavid du Colombier { 14364f8f669cSDavid du Colombier int pid, p, i; 14374f8f669cSDavid du Colombier static Waitmsg msg; 14386b0d5c8bSDavid du Colombier 14394f8f669cSDavid du Colombier if((pid = fork()) == -1) 14404f8f669cSDavid du Colombier sysfatal("fork failed: %r"); 14414f8f669cSDavid du Colombier else if(pid == 0){ 14424f8f669cSDavid du Colombier dup(fd, 0); 14434f8f669cSDavid du Colombier close(fd); 14444f8f669cSDavid du Colombier for (i = 3; i < 200; i++) 14454f8f669cSDavid du Colombier close(i); /* don't leak fds */ 14464f8f669cSDavid du Colombier execl("/bin/rc", "rc", "-c", cmd, nil); 14474f8f669cSDavid du Colombier sysfatal("exec rc: %r"); 14484f8f669cSDavid du Colombier } 14494f8f669cSDavid du Colombier for(p = waitpid(); p >= 0; p = waitpid()) 14504f8f669cSDavid du Colombier if(p == pid) 14514f8f669cSDavid du Colombier return msg.msg; 14524f8f669cSDavid du Colombier return "lost child"; 14534f8f669cSDavid du Colombier } 14544f8f669cSDavid du Colombier 1455b8b25780SDavid du Colombier /* compute wait, weighted by probability of success, with bounds */ 14560319257bSDavid du Colombier static ulong 14570319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob) 14580319257bSDavid du Colombier { 14590319257bSDavid du Colombier ulong wait; 14600319257bSDavid du Colombier 14610319257bSDavid du Colombier wait = (ms * pcntprob) / 100; 1462b8b25780SDavid du Colombier if (wait < Minwaitms) 1463b8b25780SDavid du Colombier wait = Minwaitms; 1464b8b25780SDavid du Colombier if (wait > Maxwaitms) 1465b8b25780SDavid du Colombier wait = Maxwaitms; 14660319257bSDavid du Colombier return wait; 14670319257bSDavid du Colombier } 14680319257bSDavid du Colombier 1469a41547ffSDavid du Colombier /* 1470a41547ffSDavid du Colombier * in principle we could use a single descriptor for a udp port 1471a41547ffSDavid du Colombier * to send all queries and receive all the answers to them, 1472a41547ffSDavid du Colombier * but we'd have to sort out the answers by dns-query id. 1473a41547ffSDavid du Colombier */ 14744f8f669cSDavid du Colombier static int 14753cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns) 14764f8f669cSDavid du Colombier { 14770319257bSDavid du Colombier int fd, rv; 14784f8f669cSDavid du Colombier long now; 1479b8b25780SDavid du Colombier ulong pcntprob; 1480b8b25780SDavid du Colombier uvlong wait, reqtm; 14814f8f669cSDavid du Colombier char *msg; 14824f8f669cSDavid du Colombier uchar *obuf, *ibuf; 14834f8f669cSDavid du Colombier static QLock mntlck; 14844f8f669cSDavid du Colombier static ulong lastmount; 14857dd7cddfSDavid du Colombier 14867dd7cddfSDavid du Colombier /* use alloced buffers rather than ones from the stack */ 1487f27a9a5aSDavid du Colombier // ibuf = emalloc(Maxudpin+Udphdrsize); 14883cbadd90SDavid du Colombier ibuf = emalloc(64*1024); /* max. tcp reply size */ 1489f27a9a5aSDavid du Colombier obuf = emalloc(Maxudp+Udphdrsize); 14907dd7cddfSDavid du Colombier 14914f8f669cSDavid du Colombier fd = udpport(mntpt); 14924f8f669cSDavid du Colombier while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) { 14934f8f669cSDavid du Colombier /* HACK: remount /net.alt */ 14944f8f669cSDavid du Colombier now = time(nil); 14954f8f669cSDavid du Colombier if (now < lastmount + Remntretry) 1496b8b25780SDavid du Colombier sleep(S2MS(lastmount + Remntretry - now)); 14974f8f669cSDavid du Colombier qlock(&mntlck); 14984f8f669cSDavid du Colombier fd = udpport(mntpt); /* try again under lock */ 14994f8f669cSDavid du Colombier if (fd < 0) { 15004f8f669cSDavid du Colombier dnslog("[%d] remounting /net.alt", getpid()); 15014f8f669cSDavid du Colombier unmount(nil, "/net.alt"); 15024f8f669cSDavid du Colombier 15034f8f669cSDavid du Colombier msg = system(open("/dev/null", ORDWR), "outside"); 15044f8f669cSDavid du Colombier 15054f8f669cSDavid du Colombier lastmount = time(nil); 15064f8f669cSDavid du Colombier if (msg && *msg) { 15074f8f669cSDavid du Colombier dnslog("[%d] can't remount /net.alt: %s", 15084f8f669cSDavid du Colombier getpid(), msg); 1509b8b25780SDavid du Colombier sleep(10*1000); /* don't spin remounting */ 15104f8f669cSDavid du Colombier } else 15114f8f669cSDavid du Colombier fd = udpport(mntpt); 15124f8f669cSDavid du Colombier } 15134f8f669cSDavid du Colombier qunlock(&mntlck); 15144f8f669cSDavid du Colombier } 1515a41547ffSDavid du Colombier if (fd < 0) { 15164f8f669cSDavid du Colombier dnslog("can't get udpport for %s query of name %s: %r", 15173cbadd90SDavid du Colombier mntpt, qp->dp->name); 1518a41547ffSDavid du Colombier sysfatal("out of udp conversations"); /* we're buggered */ 1519a41547ffSDavid du Colombier } 1520a41547ffSDavid du Colombier 1521a41547ffSDavid du Colombier /* 1522b8b25780SDavid du Colombier * Our QIP servers are busted and respond to AAAA and CNAME queries 1523b8b25780SDavid du Colombier * with (sometimes malformed [too short] packets and) no answers and 1524b8b25780SDavid du Colombier * just NS RRs but not Rname errors. so make time-to-wait 1525b8b25780SDavid du Colombier * proportional to estimated probability of an RR of that type existing. 1526a41547ffSDavid du Colombier */ 1527adb31a62SDavid du Colombier if (qp->type >= nelem(likely)) 15280319257bSDavid du Colombier pcntprob = 35; /* unpopular query type */ 15290319257bSDavid du Colombier else 15300319257bSDavid du Colombier pcntprob = likely[qp->type]; 15310319257bSDavid du Colombier reqtm = (patient? 2 * Maxreqtm: Maxreqtm); 1532b8b25780SDavid du Colombier wait = weight(reqtm / 3, pcntprob); /* time for one udp query */ 1533b8b25780SDavid du Colombier qp->req->aborttime = timems() + 3*wait; /* for all udp queries */ 1534a41547ffSDavid du Colombier 15350319257bSDavid du Colombier qp->udpfd = fd; 1536b8b25780SDavid du Colombier rv = queryns(qp, depth, ibuf, obuf, wait, inns); 1537a41547ffSDavid du Colombier close(fd); 1538a41547ffSDavid du Colombier qp->udpfd = -1; 15394f8f669cSDavid du Colombier 15404f8f669cSDavid du Colombier free(obuf); 15414f8f669cSDavid du Colombier free(ibuf); 15424f8f669cSDavid du Colombier return rv; 15434f8f669cSDavid du Colombier } 15444f8f669cSDavid du Colombier 1545b8b25780SDavid du Colombier /* 1546b8b25780SDavid du Colombier * look up (qp->dp->name, qp->type) rr in dns, 1547b8b25780SDavid du Colombier * using nameservers in qp->nsrp. 1548b8b25780SDavid du Colombier */ 15494f8f669cSDavid du Colombier static int 15503cbadd90SDavid du Colombier netquery(Query *qp, int depth) 15514f8f669cSDavid du Colombier { 1552410ea80bSDavid du Colombier int lock, rv, triedin, inname; 1553b8b25780SDavid du Colombier char buf[32]; 15544f8f669cSDavid du Colombier RR *rp; 15554e153993SDavid du Colombier DN *dp; 1556225077b0SDavid du Colombier Querylck *qlp; 1557225077b0SDavid du Colombier static int whined; 15584f8f669cSDavid du Colombier 1559225077b0SDavid du Colombier rv = 0; /* pessimism */ 15604f8f669cSDavid du Colombier if(depth > 12) /* in a recursive loop? */ 15614f8f669cSDavid du Colombier return 0; 15624f8f669cSDavid du Colombier 15633cbadd90SDavid du Colombier slave(qp->req); 1564d3907fe5SDavid du Colombier /* 1565d3907fe5SDavid du Colombier * slave might have forked. if so, the parent process longjmped to 1566d3907fe5SDavid du Colombier * req->mret; we're usually the child slave, but if there are too 1567d3907fe5SDavid du Colombier * many children already, we're still the same process. 1568d3907fe5SDavid du Colombier */ 15694f8f669cSDavid du Colombier 15704e5f5f32SDavid du Colombier /* 15714e5f5f32SDavid du Colombier * don't lock before call to slave so only children can block. 15724e5f5f32SDavid du Colombier * just lock at top-level invocation. 15734e5f5f32SDavid du Colombier */ 1574225077b0SDavid du Colombier lock = depth <= 1 && qp->req->isslave; 15754e153993SDavid du Colombier dp = qp->dp; /* ensure that it doesn't change underfoot */ 1576225077b0SDavid du Colombier qlp = nil; 15774e5f5f32SDavid du Colombier if(lock) { 1578b8b25780SDavid du Colombier procsetname("query lock wait: %s %s from %s", dp->name, 1579b8b25780SDavid du Colombier rrname(qp->type, buf, sizeof buf), qp->req->from); 1580ccf6439bSDavid du Colombier /* 1581ccf6439bSDavid du Colombier * don't make concurrent queries for this name. 158281730632SDavid du Colombier * dozens of processes blocking here probably indicates 158381730632SDavid du Colombier * an error in our dns data that causes us to not 158481730632SDavid du Colombier * recognise a zone (area) as one of our own, thus 158581730632SDavid du Colombier * causing us to query other nameservers. 1586ccf6439bSDavid du Colombier */ 1587225077b0SDavid du Colombier qlp = &dp->querylck[qtype2lck(qp->type)]; 1588225077b0SDavid du Colombier qlock(qlp); 1589b8b25780SDavid du Colombier if (qlp->Ref.ref > Maxoutstanding) { 1590225077b0SDavid du Colombier qunlock(qlp); 1591225077b0SDavid du Colombier if (!whined) { 1592225077b0SDavid du Colombier whined = 1; 1593225077b0SDavid du Colombier dnslog("too many outstanding queries for %s;" 159413fec586SDavid du Colombier " dropping this one; no further logging" 159513fec586SDavid du Colombier " of drops", dp->name); 1596225077b0SDavid du Colombier } 1597225077b0SDavid du Colombier return 0; 1598225077b0SDavid du Colombier } 1599410ea80bSDavid du Colombier ++qlp->Ref.ref; 1600410ea80bSDavid du Colombier qunlock(qlp); 1601225077b0SDavid du Colombier } 16024e153993SDavid du Colombier procsetname("netquery: %s", dp->name); 16036b0d5c8bSDavid du Colombier 16046b0d5c8bSDavid du Colombier /* prepare server RR's for incremental lookup */ 16053cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 16066b0d5c8bSDavid du Colombier rp->marker = 0; 16076b0d5c8bSDavid du Colombier 16084f8f669cSDavid du Colombier triedin = 0; 1609a41547ffSDavid du Colombier 16104f8f669cSDavid du Colombier /* 16114f8f669cSDavid du Colombier * normal resolvers and servers will just use mntpt for all addresses, 16124f8f669cSDavid du Colombier * even on the outside. straddling servers will use mntpt (/net) 16134f8f669cSDavid du Colombier * for inside addresses and /net.alt for outside addresses, 16144f8f669cSDavid du Colombier * thus bypassing other inside nameservers. 16154f8f669cSDavid du Colombier */ 16164e153993SDavid du Colombier inname = insideaddr(dp->name); 16174f8f669cSDavid du Colombier if (!cfg.straddle || inname) { 16183cbadd90SDavid du Colombier rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns)); 16194f8f669cSDavid du Colombier triedin = 1; 16204f8f669cSDavid du Colombier } 16214f8f669cSDavid du Colombier 16224f8f669cSDavid du Colombier /* 16234f8f669cSDavid du Colombier * if we're still looking, are inside, and have an outside domain, 16244f8f669cSDavid du Colombier * try it on our outside interface, if any. 16254f8f669cSDavid du Colombier */ 16264f8f669cSDavid du Colombier if (rv == 0 && cfg.inside && !inname) { 16274f8f669cSDavid du Colombier if (triedin) 16284f8f669cSDavid du Colombier dnslog( 16294f8f669cSDavid du Colombier "[%d] netquery: internal nameservers failed for %s; trying external", 16304e153993SDavid du Colombier getpid(), dp->name); 16314f8f669cSDavid du Colombier 16324f8f669cSDavid du Colombier /* prepare server RR's for incremental lookup */ 16333cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 16344f8f669cSDavid du Colombier rp->marker = 0; 16354f8f669cSDavid du Colombier 16363cbadd90SDavid du Colombier rv = udpquery(qp, "/net.alt", depth, Patient, Outns); 16374f8f669cSDavid du Colombier } 16383cbadd90SDavid du Colombier // if (rv == 0) /* could ask /net.alt/dns directly */ 16394e153993SDavid du Colombier // askoutdns(dp, qp->type); 16404f8f669cSDavid du Colombier 1641410ea80bSDavid du Colombier if(lock && qlp) { 1642410ea80bSDavid du Colombier qlock(qlp); 1643410ea80bSDavid du Colombier assert(qlp->Ref.ref > 0); 1644410ea80bSDavid du Colombier qunlock(qlp); 1645225077b0SDavid du Colombier decref(qlp); 1646410ea80bSDavid du Colombier } 16477dd7cddfSDavid du Colombier return rv; 16487dd7cddfSDavid du Colombier } 16494f8f669cSDavid du Colombier 16504f8f669cSDavid du Colombier int 16514f8f669cSDavid du Colombier seerootns(void) 16524f8f669cSDavid du Colombier { 16533cbadd90SDavid du Colombier int rv; 16544f8f669cSDavid du Colombier char root[] = ""; 16554f8f669cSDavid du Colombier Request req; 1656c73252aeSDavid du Colombier Query *qp; 16574f8f669cSDavid du Colombier 16584f8f669cSDavid du Colombier memset(&req, 0, sizeof req); 16594f8f669cSDavid du Colombier req.isslave = 1; 1660b8b25780SDavid du Colombier req.aborttime = timems() + Maxreqtm; 1661f46c709fSDavid du Colombier req.from = "internal"; 1662c73252aeSDavid du Colombier qp = emalloc(sizeof *qp); 1663c73252aeSDavid du Colombier queryinit(qp, dnlookup(root, Cin, 1), Tns, &req); 1664c73252aeSDavid du Colombier 1665c73252aeSDavid du Colombier qp->nsrp = dblookup(root, Cin, Tns, 0, 0); 1666c73252aeSDavid du Colombier rv = netquery(qp, 0); 1667c73252aeSDavid du Colombier 1668c73252aeSDavid du Colombier rrfreelist(qp->nsrp); 1669c73252aeSDavid du Colombier querydestroy(qp); 1670c73252aeSDavid du Colombier free(qp); 16713cbadd90SDavid du Colombier return rv; 16724f8f669cSDavid du Colombier } 1673