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 typedef struct Sluggards Sluggards; 153cbadd90SDavid du Colombier 163e12c5d1SDavid du Colombier enum 173e12c5d1SDavid du Colombier { 183cbadd90SDavid du Colombier Udp, Tcp, 197dd7cddfSDavid du Colombier Maxdest= 24, /* maximum destinations for a request message */ 203e12c5d1SDavid du Colombier Maxtrans= 3, /* maximum transmissions to a server */ 213cbadd90SDavid du Colombier Destmagic= 0xcafebabe, 223cbadd90SDavid du Colombier Querymagic= 0xdeadbeef, 233e12c5d1SDavid du Colombier }; 243e12c5d1SDavid du Colombier 253cbadd90SDavid du Colombier struct Ipaddr { 263cbadd90SDavid du Colombier Ipaddr *next; 273cbadd90SDavid du Colombier uchar ip[IPaddrlen]; 283cbadd90SDavid du Colombier }; 293cbadd90SDavid du Colombier 303cbadd90SDavid du Colombier struct Dest 313cbadd90SDavid du Colombier { 323cbadd90SDavid du Colombier uchar a[IPaddrlen]; /* ip address */ 333cbadd90SDavid du Colombier DN *s; /* name server */ 343cbadd90SDavid du Colombier int nx; /* number of transmissions */ 353cbadd90SDavid du Colombier int code; /* response code; used to clear dp->respcode */ 363cbadd90SDavid du Colombier 373cbadd90SDavid du Colombier ulong magic; 383cbadd90SDavid du Colombier }; 393cbadd90SDavid du Colombier 403cbadd90SDavid du Colombier struct Query { 413cbadd90SDavid du Colombier DN *dp; /* domain */ 423cbadd90SDavid du Colombier int type; /* and type to look up */ 433cbadd90SDavid du Colombier Request *req; 443cbadd90SDavid du Colombier RR *nsrp; /* name servers to consult */ 453cbadd90SDavid du Colombier 463cbadd90SDavid du Colombier Dest *dest; /* array of destinations */ 473cbadd90SDavid du Colombier Dest *curdest; /* pointer to one of them */ 483cbadd90SDavid du Colombier int ndest; 493cbadd90SDavid du Colombier 503cbadd90SDavid du Colombier int udpfd; /* can be shared by all udp users */ 513cbadd90SDavid du Colombier 523cbadd90SDavid du Colombier QLock tcplock; /* only one tcp call at a time per query */ 533cbadd90SDavid du Colombier int tcpset; 543cbadd90SDavid du Colombier int tcpfd; /* if Tcp, read replies from here */ 553cbadd90SDavid du Colombier int tcpctlfd; 563cbadd90SDavid du Colombier uchar tcpip[IPaddrlen]; 573cbadd90SDavid du Colombier 583cbadd90SDavid du Colombier ulong magic; 593cbadd90SDavid du Colombier }; 603cbadd90SDavid du Colombier 613cbadd90SDavid du Colombier /* a list of sluggardly name servers */ 623cbadd90SDavid du Colombier struct Sluggards { 633cbadd90SDavid du Colombier QLock; 643cbadd90SDavid du Colombier Ipaddr *head; 653cbadd90SDavid du Colombier Ipaddr *tail; 663cbadd90SDavid du Colombier }; 673cbadd90SDavid du Colombier 683cbadd90SDavid du Colombier static Sluggards slugs; 693cbadd90SDavid du Colombier 707dd7cddfSDavid du Colombier static RR* dnresolve1(char*, int, int, Request*, int, int); 713cbadd90SDavid du Colombier static int netquery(Query *, int); 723cbadd90SDavid du Colombier 733cbadd90SDavid du Colombier static Ipaddr * 743cbadd90SDavid du Colombier newslug(void) 753cbadd90SDavid du Colombier { 763cbadd90SDavid du Colombier return emalloc(sizeof(Ipaddr)); 773cbadd90SDavid du Colombier } 783cbadd90SDavid du Colombier 793cbadd90SDavid du Colombier static void 803cbadd90SDavid du Colombier addslug(uchar nsip[]) 813cbadd90SDavid du Colombier { 823cbadd90SDavid du Colombier Ipaddr *sp; 833cbadd90SDavid du Colombier static uchar zip[IPaddrlen]; 843cbadd90SDavid du Colombier 853cbadd90SDavid du Colombier if (memcmp(nsip, zip, IPaddrlen) == 0) 863cbadd90SDavid du Colombier return; 873cbadd90SDavid du Colombier 883cbadd90SDavid du Colombier qlock(&slugs); 893cbadd90SDavid du Colombier for (sp = slugs.head; sp != nil; sp = sp->next) 903cbadd90SDavid du Colombier if (memcmp(sp->ip, nsip, IPaddrlen) == 0) { 913cbadd90SDavid du Colombier qunlock(&slugs); /* already know it */ 923cbadd90SDavid du Colombier return; 933cbadd90SDavid du Colombier } 943cbadd90SDavid du Colombier 953cbadd90SDavid du Colombier if (slugs.head == nil) 963cbadd90SDavid du Colombier slugs.head = slugs.tail = newslug(); 973cbadd90SDavid du Colombier else { 983cbadd90SDavid du Colombier slugs.tail->next = newslug(); 993cbadd90SDavid du Colombier slugs.tail = slugs.tail->next; 1003cbadd90SDavid du Colombier } 1013cbadd90SDavid du Colombier memmove(slugs.tail->ip, nsip, IPaddrlen); 1023cbadd90SDavid du Colombier qunlock(&slugs); 1033cbadd90SDavid du Colombier 1043cbadd90SDavid du Colombier dnslog("%I is a slug", nsip); 1053cbadd90SDavid du Colombier } 1063cbadd90SDavid du Colombier 1073cbadd90SDavid du Colombier int 1083cbadd90SDavid du Colombier isaslug(uchar nsip[]) 1093cbadd90SDavid du Colombier { 1103cbadd90SDavid du Colombier Ipaddr *sp; 1113cbadd90SDavid du Colombier 1123cbadd90SDavid du Colombier qlock(&slugs); 1133cbadd90SDavid du Colombier for (sp = slugs.head; sp != nil; sp = sp->next) 1143cbadd90SDavid du Colombier if (memcmp(sp->ip, nsip, IPaddrlen) == 0) { 1153cbadd90SDavid du Colombier qunlock(&slugs); 1163cbadd90SDavid du Colombier return 1; 1173cbadd90SDavid du Colombier } 1183cbadd90SDavid du Colombier qunlock(&slugs); 1193cbadd90SDavid du Colombier return 0; 1203cbadd90SDavid du Colombier } 1213e12c5d1SDavid du Colombier 1224f8f669cSDavid du Colombier /* 1234f8f669cSDavid du Colombier * reading /proc/pid/args yields either "name" or "name [display args]", 1244f8f669cSDavid du Colombier * so return only display args, if any. 1254f8f669cSDavid du Colombier */ 1264f8f669cSDavid du Colombier static char * 1274f8f669cSDavid du Colombier procgetname(void) 1284f8f669cSDavid du Colombier { 1294f8f669cSDavid du Colombier int fd, n; 1304f8f669cSDavid du Colombier char *lp, *rp; 1314f8f669cSDavid du Colombier char buf[256]; 1324f8f669cSDavid du Colombier 1334f8f669cSDavid du Colombier snprint(buf, sizeof buf, "#p/%d/args", getpid()); 1344f8f669cSDavid du Colombier if((fd = open(buf, OREAD)) < 0) 1354f8f669cSDavid du Colombier return strdup(""); 1364f8f669cSDavid du Colombier *buf = '\0'; 1374f8f669cSDavid du Colombier n = read(fd, buf, sizeof buf-1); 1384f8f669cSDavid du Colombier close(fd); 1394f8f669cSDavid du Colombier if (n >= 0) 1404f8f669cSDavid du Colombier buf[n] = '\0'; 1414f8f669cSDavid du Colombier if ((lp = strchr(buf, '[')) == nil || 1424f8f669cSDavid du Colombier (rp = strrchr(buf, ']')) == nil) 1434f8f669cSDavid du Colombier return strdup(""); 1444f8f669cSDavid du Colombier *rp = '\0'; 1454f8f669cSDavid du Colombier return strdup(lp+1); 1464f8f669cSDavid du Colombier } 1473e12c5d1SDavid du Colombier 1483e12c5d1SDavid du Colombier /* 1493e12c5d1SDavid du Colombier * lookup 'type' info for domain name 'name'. If it doesn't exist, try 1503e12c5d1SDavid du Colombier * looking it up as a canonical name. 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 1654f8f669cSDavid du Colombier procname = procgetname(); 1667dd7cddfSDavid du Colombier /* 1677dd7cddfSDavid du Colombier * hack for systems that don't have resolve search 1687dd7cddfSDavid du Colombier * lists. Just look up the simple name in the database. 1697dd7cddfSDavid du Colombier */ 1707dd7cddfSDavid du Colombier if(!rooted && strchr(name, '.') == 0){ 1717dd7cddfSDavid du Colombier rp = nil; 1727dd7cddfSDavid du Colombier drp = domainlist(class); 1737dd7cddfSDavid du Colombier for(nrp = drp; nrp != nil; nrp = nrp->next){ 1744f8f669cSDavid du Colombier snprint(nname, sizeof nname, "%s.%s", name, 1754f8f669cSDavid du Colombier nrp->ptr->name); 1764f8f669cSDavid du Colombier rp = dnresolve(nname, class, type, req, cn, depth, 1774f8f669cSDavid du Colombier recurse, rooted, status); 178271b8d73SDavid du Colombier rrfreelist(rrremneg(&rp)); 1797dd7cddfSDavid du Colombier if(rp != nil) 1807dd7cddfSDavid du Colombier break; 1817dd7cddfSDavid du Colombier } 1827dd7cddfSDavid du Colombier if(drp != nil) 1837dd7cddfSDavid du Colombier rrfree(drp); 1844f8f669cSDavid du Colombier procsetname(procname); 1854f8f669cSDavid du Colombier free(procname); 1863e12c5d1SDavid du Colombier return rp; 1877dd7cddfSDavid du Colombier } 1883e12c5d1SDavid du Colombier 1897dd7cddfSDavid du Colombier /* 1907dd7cddfSDavid du Colombier * try the name directly 1917dd7cddfSDavid du Colombier */ 1927dd7cddfSDavid du Colombier rp = dnresolve1(name, class, type, req, depth, recurse); 1934f8f669cSDavid du Colombier if(rp) { 1944f8f669cSDavid du Colombier procsetname(procname); 1954f8f669cSDavid du Colombier free(procname); 1967dd7cddfSDavid du Colombier return randomize(rp); 1974f8f669cSDavid du Colombier } 1987dd7cddfSDavid du Colombier 1997dd7cddfSDavid du Colombier /* try it as a canonical name if we weren't told the name didn't exist */ 2007dd7cddfSDavid du Colombier dp = dnlookup(name, class, 0); 2014f8f669cSDavid du Colombier if(type != Tptr && dp->respcode != Rname) 2027dd7cddfSDavid du Colombier for(loops = 0; rp == nil && loops < 32; loops++){ 2037dd7cddfSDavid du Colombier rp = dnresolve1(name, class, Tcname, req, depth, recurse); 2047dd7cddfSDavid du Colombier if(rp == nil) 2057dd7cddfSDavid du Colombier break; 20680ee5cbfSDavid du Colombier 20780ee5cbfSDavid du Colombier if(rp->negative){ 20880ee5cbfSDavid du Colombier rrfreelist(rp); 20980ee5cbfSDavid du Colombier rp = nil; 21080ee5cbfSDavid du Colombier break; 21180ee5cbfSDavid du Colombier } 2127dd7cddfSDavid du Colombier 2137dd7cddfSDavid du Colombier name = rp->host->name; 2147dd7cddfSDavid du Colombier if(cn) 2157dd7cddfSDavid du Colombier rrcat(cn, rp); 2167dd7cddfSDavid du Colombier else 2177dd7cddfSDavid du Colombier rrfreelist(rp); 2187dd7cddfSDavid du Colombier 2197dd7cddfSDavid du Colombier rp = dnresolve1(name, class, type, req, depth, recurse); 2207dd7cddfSDavid du Colombier } 2217dd7cddfSDavid du Colombier 2227dd7cddfSDavid du Colombier /* distinction between not found and not good */ 2234f8f669cSDavid du Colombier if(rp == nil && status != nil && dp->respcode != 0) 2244f8f669cSDavid du Colombier *status = dp->respcode; 2257dd7cddfSDavid du Colombier 2264f8f669cSDavid du Colombier procsetname(procname); 2274f8f669cSDavid du Colombier free(procname); 2287dd7cddfSDavid du Colombier return randomize(rp); 2293e12c5d1SDavid du Colombier } 2303e12c5d1SDavid du Colombier 2313cbadd90SDavid du Colombier static void 2323cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req) 2333cbadd90SDavid du Colombier { 2343cbadd90SDavid du Colombier memset(qp, 0, sizeof *qp); 235*4e7b9544SDavid du Colombier qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1; 2363cbadd90SDavid du Colombier qp->dp = dp; 2373cbadd90SDavid du Colombier qp->type = type; 2383cbadd90SDavid du Colombier qp->req = req; 2393cbadd90SDavid du Colombier qp->nsrp = nil; 2403cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; 2413cbadd90SDavid du Colombier qp->magic = Querymagic; 2423cbadd90SDavid du Colombier } 2433cbadd90SDavid du Colombier 2443cbadd90SDavid du Colombier static void 2453cbadd90SDavid du Colombier queryck(Query *qp) 2463cbadd90SDavid du Colombier { 2473cbadd90SDavid du Colombier assert(qp); 2483cbadd90SDavid du Colombier assert(qp->magic == Querymagic); 2493cbadd90SDavid du Colombier } 2503cbadd90SDavid du Colombier 2513cbadd90SDavid du Colombier static void 252*4e7b9544SDavid du Colombier querydestroy(Query *qp) 253*4e7b9544SDavid du Colombier { 254*4e7b9544SDavid du Colombier queryck(qp); 255*4e7b9544SDavid du Colombier if (qp->udpfd > 0) 256*4e7b9544SDavid du Colombier close(qp->udpfd); 257*4e7b9544SDavid du Colombier if (qp->tcpfd > 0) 258*4e7b9544SDavid du Colombier close(qp->tcpfd); 259*4e7b9544SDavid du Colombier if (qp->tcpctlfd > 0) { 260*4e7b9544SDavid du Colombier hangup(qp->tcpctlfd); 261*4e7b9544SDavid du Colombier close(qp->tcpctlfd); 262*4e7b9544SDavid du Colombier } 263*4e7b9544SDavid du Colombier memset(qp, 0, sizeof *qp); /* prevent accidents */ 264*4e7b9544SDavid du Colombier qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1; 265*4e7b9544SDavid du Colombier } 266*4e7b9544SDavid du Colombier 267*4e7b9544SDavid du Colombier static void 2683cbadd90SDavid du Colombier destinit(Dest *p) 2693cbadd90SDavid du Colombier { 2703cbadd90SDavid du Colombier memset(p, 0, sizeof *p); 2713cbadd90SDavid du Colombier p->magic = Destmagic; 2723cbadd90SDavid du Colombier } 2733cbadd90SDavid du Colombier 2743cbadd90SDavid du Colombier static void 2753cbadd90SDavid du Colombier destck(Dest *p) 2763cbadd90SDavid du Colombier { 2773cbadd90SDavid du Colombier assert(p); 2783cbadd90SDavid du Colombier assert(p->magic == Destmagic); 2793cbadd90SDavid du Colombier } 2803cbadd90SDavid du Colombier 2813e12c5d1SDavid du Colombier static RR* 2824f8f669cSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth, 2834f8f669cSDavid du Colombier int recurse) 2843e12c5d1SDavid du Colombier { 2853e12c5d1SDavid du Colombier DN *dp, *nsdp; 2867dd7cddfSDavid du Colombier RR *rp, *nsrp, *dbnsrp; 2873e12c5d1SDavid du Colombier char *cp; 2883cbadd90SDavid du Colombier Query query; 2893e12c5d1SDavid du Colombier 2907dd7cddfSDavid du Colombier if(debug) 2914f8f669cSDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class); 2927dd7cddfSDavid du Colombier 2933e12c5d1SDavid du Colombier /* only class Cin implemented so far */ 2943e12c5d1SDavid du Colombier if(class != Cin) 2954f8f669cSDavid du Colombier return nil; 2963e12c5d1SDavid du Colombier 2973e12c5d1SDavid du Colombier dp = dnlookup(name, class, 1); 2983e12c5d1SDavid du Colombier 2997dd7cddfSDavid du Colombier /* 3007dd7cddfSDavid du Colombier * Try the cache first 3017dd7cddfSDavid du Colombier */ 3027dd7cddfSDavid du Colombier rp = rrlookup(dp, type, OKneg); 3034f8f669cSDavid du Colombier if(rp) 3047dd7cddfSDavid du Colombier if(rp->db){ 3054f8f669cSDavid du Colombier /* unauthoritative db entries are hints */ 3067dd7cddfSDavid du Colombier if(rp->auth) 3073e12c5d1SDavid du Colombier return rp; 3084f8f669cSDavid du Colombier } else 3097dd7cddfSDavid du Colombier /* cached entry must still be valid */ 3104f8f669cSDavid du Colombier if(rp->ttl > now) 3117dd7cddfSDavid du Colombier /* but Tall entries are special */ 3127dd7cddfSDavid du Colombier if(type != Tall || rp->query == Tall) 3137dd7cddfSDavid du Colombier return rp; 3144f8f669cSDavid du Colombier 3157dd7cddfSDavid du Colombier rrfreelist(rp); 3163e12c5d1SDavid du Colombier 3177dd7cddfSDavid du Colombier /* 3187dd7cddfSDavid du Colombier * try the cache for a canonical name. if found punt 3197dd7cddfSDavid du Colombier * since we'll find it during the canonical name search 3207dd7cddfSDavid du Colombier * in dnresolve(). 3217dd7cddfSDavid du Colombier */ 3227dd7cddfSDavid du Colombier if(type != Tcname){ 3237dd7cddfSDavid du Colombier rp = rrlookup(dp, Tcname, NOneg); 3247dd7cddfSDavid du Colombier rrfreelist(rp); 3253e12c5d1SDavid du Colombier if(rp) 3264f8f669cSDavid du Colombier return nil; 3273e12c5d1SDavid du Colombier } 3283e12c5d1SDavid du Colombier 3293cbadd90SDavid du Colombier queryinit(&query, dp, type, req); 3303cbadd90SDavid du Colombier 3313e12c5d1SDavid du Colombier /* 3324f8f669cSDavid du Colombier * if we're running as just a resolver, query our 3337dd7cddfSDavid du Colombier * designated name servers 334219b2ee8SDavid du Colombier */ 3354f8f669cSDavid du Colombier if(cfg.resolver){ 3367dd7cddfSDavid du Colombier nsrp = randomize(getdnsservers(class)); 3377dd7cddfSDavid du Colombier if(nsrp != nil) { 3383cbadd90SDavid du Colombier query.nsrp = nsrp; 3393cbadd90SDavid du Colombier if(netquery(&query, depth+1)){ 3407dd7cddfSDavid du Colombier rrfreelist(nsrp); 341*4e7b9544SDavid du Colombier querydestroy(&query); 3427dd7cddfSDavid du Colombier return rrlookup(dp, type, OKneg); 3437dd7cddfSDavid du Colombier } 3447dd7cddfSDavid du Colombier rrfreelist(nsrp); 3457dd7cddfSDavid du Colombier } 346219b2ee8SDavid du Colombier } 347219b2ee8SDavid du Colombier 348219b2ee8SDavid du Colombier /* 3493e12c5d1SDavid du Colombier * walk up the domain name looking for 3503e12c5d1SDavid du Colombier * a name server for the domain. 3513e12c5d1SDavid du Colombier */ 3523e12c5d1SDavid du Colombier for(cp = name; cp; cp = walkup(cp)){ 3537dd7cddfSDavid du Colombier /* 3547dd7cddfSDavid du Colombier * if this is a local (served by us) domain, 3557dd7cddfSDavid du Colombier * return answer 3567dd7cddfSDavid du Colombier */ 3577dd7cddfSDavid du Colombier dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0)); 3587dd7cddfSDavid du Colombier if(dbnsrp && dbnsrp->local){ 3597dd7cddfSDavid du Colombier rp = dblookup(name, class, type, 1, dbnsrp->ttl); 3607dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 361*4e7b9544SDavid du Colombier querydestroy(&query); 3627dd7cddfSDavid du Colombier return rp; 3637dd7cddfSDavid du Colombier } 3647dd7cddfSDavid du Colombier 3657dd7cddfSDavid du Colombier /* 3667dd7cddfSDavid du Colombier * if recursion isn't set, just accept local 3677dd7cddfSDavid du Colombier * entries 3687dd7cddfSDavid du Colombier */ 3697dd7cddfSDavid du Colombier if(recurse == Dontrecurse){ 3707dd7cddfSDavid du Colombier if(dbnsrp) 3717dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 3727dd7cddfSDavid du Colombier continue; 3737dd7cddfSDavid du Colombier } 3747dd7cddfSDavid du Colombier 3757dd7cddfSDavid du Colombier /* look for ns in cache */ 3763e12c5d1SDavid du Colombier nsdp = dnlookup(cp, class, 0); 3777dd7cddfSDavid du Colombier nsrp = nil; 3783e12c5d1SDavid du Colombier if(nsdp) 3797dd7cddfSDavid du Colombier nsrp = randomize(rrlookup(nsdp, Tns, NOneg)); 3807dd7cddfSDavid du Colombier 3817dd7cddfSDavid du Colombier /* if the entry timed out, ignore it */ 3827dd7cddfSDavid du Colombier if(nsrp && nsrp->ttl < now){ 3837dd7cddfSDavid du Colombier rrfreelist(nsrp); 3847dd7cddfSDavid du Colombier nsrp = nil; 3857dd7cddfSDavid du Colombier } 3863e12c5d1SDavid du Colombier 3873e12c5d1SDavid du Colombier if(nsrp){ 3887dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 3897dd7cddfSDavid du Colombier 3904f8f669cSDavid du Colombier /* query the name servers found in cache */ 3913cbadd90SDavid du Colombier query.nsrp = nsrp; 3923cbadd90SDavid du Colombier if(netquery(&query, depth+1)){ 3933e12c5d1SDavid du Colombier rrfreelist(nsrp); 394*4e7b9544SDavid du Colombier querydestroy(&query); 3957dd7cddfSDavid du Colombier return rrlookup(dp, type, OKneg); 3967dd7cddfSDavid du Colombier } 3977dd7cddfSDavid du Colombier rrfreelist(nsrp); 3987dd7cddfSDavid du Colombier continue; 3993e12c5d1SDavid du Colombier } 4003e12c5d1SDavid du Colombier 4017dd7cddfSDavid du Colombier /* use ns from db */ 4027dd7cddfSDavid du Colombier if(dbnsrp){ 4037dd7cddfSDavid du Colombier /* try the name servers found in db */ 4043cbadd90SDavid du Colombier query.nsrp = dbnsrp; 4053cbadd90SDavid du Colombier if(netquery(&query, depth+1)){ 4063e12c5d1SDavid du Colombier /* we got an answer */ 4077dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 408*4e7b9544SDavid du Colombier querydestroy(&query); 4097dd7cddfSDavid du Colombier return rrlookup(dp, type, NOneg); 4103e12c5d1SDavid du Colombier } 4117dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 4123e12c5d1SDavid du Colombier } 4133e12c5d1SDavid du Colombier } 414*4e7b9544SDavid du Colombier querydestroy(&query); 4153e12c5d1SDavid du Colombier 4167dd7cddfSDavid du Colombier /* settle for a non-authoritative answer */ 4177dd7cddfSDavid du Colombier rp = rrlookup(dp, type, OKneg); 4187dd7cddfSDavid du Colombier if(rp) 4197dd7cddfSDavid du Colombier return rp; 4207dd7cddfSDavid du Colombier 4217dd7cddfSDavid du Colombier /* noone answered. try the database, we might have a chance. */ 4227dd7cddfSDavid du Colombier return dblookup(name, class, type, 0, 0); 4233e12c5d1SDavid du Colombier } 4243e12c5d1SDavid du Colombier 4253e12c5d1SDavid du Colombier /* 4264f8f669cSDavid du Colombier * walk a domain name one element to the right. 4274f8f669cSDavid du Colombier * return a pointer to that element. 4283e12c5d1SDavid du Colombier * in other words, return a pointer to the parent domain name. 4293e12c5d1SDavid du Colombier */ 4303e12c5d1SDavid du Colombier char* 4313e12c5d1SDavid du Colombier walkup(char *name) 4323e12c5d1SDavid du Colombier { 4333e12c5d1SDavid du Colombier char *cp; 4343e12c5d1SDavid du Colombier 4353e12c5d1SDavid du Colombier cp = strchr(name, '.'); 4363e12c5d1SDavid du Colombier if(cp) 4373e12c5d1SDavid du Colombier return cp+1; 4383e12c5d1SDavid du Colombier else if(*name) 4393e12c5d1SDavid du Colombier return ""; 4403e12c5d1SDavid du Colombier else 4413e12c5d1SDavid du Colombier return 0; 4423e12c5d1SDavid du Colombier } 4433e12c5d1SDavid du Colombier 4443e12c5d1SDavid du Colombier /* 4453e12c5d1SDavid du Colombier * Get a udpport for requests and replies. Put the port 4463e12c5d1SDavid du Colombier * into "headers" mode. 4473e12c5d1SDavid du Colombier */ 4483e12c5d1SDavid du Colombier static char *hmsg = "headers"; 4491e8349ebSDavid du Colombier static char *ohmsg = "oldheaders"; 4503e12c5d1SDavid du Colombier 451dc5a79c1SDavid du Colombier int 4524f8f669cSDavid du Colombier udpport(char *mtpt) 4533e12c5d1SDavid du Colombier { 4543e12c5d1SDavid du Colombier int fd, ctl; 4554f8f669cSDavid du Colombier char ds[64], adir[64]; 4563e12c5d1SDavid du Colombier 4573e12c5d1SDavid du Colombier /* get a udp port */ 4584f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net")); 4597dd7cddfSDavid du Colombier ctl = announce(ds, adir); 4607dd7cddfSDavid du Colombier if(ctl < 0){ 4617dd7cddfSDavid du Colombier /* warning("can't get udp port"); */ 462bd389b36SDavid du Colombier return -1; 463bd389b36SDavid du Colombier } 4643e12c5d1SDavid du Colombier 4653e12c5d1SDavid du Colombier /* turn on header style interface */ 466bd389b36SDavid du Colombier if(write(ctl, hmsg, strlen(hmsg)) , 0){ 4677dd7cddfSDavid du Colombier close(ctl); 468bd389b36SDavid du Colombier warning(hmsg); 469bd389b36SDavid du Colombier return -1; 470bd389b36SDavid du Colombier } 4711e8349ebSDavid du Colombier write(ctl, ohmsg, strlen(ohmsg)); 4723e12c5d1SDavid du Colombier 4737dd7cddfSDavid du Colombier /* grab the data file */ 4744f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/data", adir); 4757dd7cddfSDavid du Colombier fd = open(ds, ORDWR); 4763e12c5d1SDavid du Colombier close(ctl); 4774f8f669cSDavid du Colombier if(fd < 0) 4784f8f669cSDavid du Colombier warning("can't open udp port %s: %r", ds); 4793e12c5d1SDavid du Colombier return fd; 4803e12c5d1SDavid du Colombier } 4813e12c5d1SDavid du Colombier 482d6d99297SDavid du Colombier /* generate a DNS UDP query packet */ 483dc5a79c1SDavid du Colombier int 484dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno) 4853e12c5d1SDavid du Colombier { 4863e12c5d1SDavid du Colombier DNSmsg m; 4873e12c5d1SDavid du Colombier int len; 488d9dc5dd1SDavid du Colombier OUdphdr *uh = (OUdphdr*)buf; 4893e12c5d1SDavid du Colombier 4903e12c5d1SDavid du Colombier /* stuff port number into output buffer */ 4913cbadd90SDavid du Colombier memset(uh, 0, sizeof *uh); 4927dd7cddfSDavid du Colombier hnputs(uh->rport, 53); 4933e12c5d1SDavid du Colombier 4943e12c5d1SDavid du Colombier /* make request and convert it to output format */ 4953cbadd90SDavid du Colombier memset(&m, 0, sizeof m); 496dc5a79c1SDavid du Colombier m.flags = flags; 4973e12c5d1SDavid du Colombier m.id = reqno; 4983e12c5d1SDavid du Colombier m.qd = rralloc(type); 4993e12c5d1SDavid du Colombier m.qd->owner = dp; 5003e12c5d1SDavid du Colombier m.qd->type = type; 501d9dc5dd1SDavid du Colombier len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp); 5027dd7cddfSDavid du Colombier rrfree(m.qd); 5033e12c5d1SDavid du Colombier return len; 5043e12c5d1SDavid du Colombier } 5053e12c5d1SDavid du Colombier 5067dd7cddfSDavid du Colombier /* for alarms in readreply */ 5073e12c5d1SDavid du Colombier static void 5083e12c5d1SDavid du Colombier ding(void *x, char *msg) 5093e12c5d1SDavid du Colombier { 5103e12c5d1SDavid du Colombier USED(x); 5113e12c5d1SDavid du Colombier if(strcmp(msg, "alarm") == 0) 5123e12c5d1SDavid du Colombier noted(NCONT); 5133e12c5d1SDavid du Colombier else 5143e12c5d1SDavid du Colombier noted(NDFLT); 5153e12c5d1SDavid du Colombier } 5167dd7cddfSDavid du Colombier 5177dd7cddfSDavid du Colombier static void 5187dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp) 5193e12c5d1SDavid du Colombier { 5207dd7cddfSDavid du Colombier rrfreelist(mp->qd); 5217dd7cddfSDavid du Colombier rrfreelist(mp->an); 5227dd7cddfSDavid du Colombier rrfreelist(mp->ns); 5237dd7cddfSDavid du Colombier rrfreelist(mp->ar); 5244f8f669cSDavid du Colombier mp->qd = mp->an = mp->ns = mp->ar = nil; 5257dd7cddfSDavid du Colombier } 5267dd7cddfSDavid du Colombier 5273cbadd90SDavid du Colombier /* sets srcip */ 5283cbadd90SDavid du Colombier static int 5293cbadd90SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp, 5303cbadd90SDavid du Colombier uchar *srcip) 5313cbadd90SDavid du Colombier { 5323cbadd90SDavid du Colombier int len, fd; 5333cbadd90SDavid du Colombier uchar *reply; 5343cbadd90SDavid du Colombier uchar lenbuf[2]; 5353cbadd90SDavid du Colombier 5363cbadd90SDavid du Colombier /* timed read of reply */ 5373cbadd90SDavid du Colombier alarm((endtime - time(nil)) * 1000); 5383cbadd90SDavid du Colombier reply = ibuf; 5393cbadd90SDavid du Colombier len = -1; /* pessimism */ 540*4e7b9544SDavid du Colombier memset(srcip, 0, IPaddrlen); 541*4e7b9544SDavid du Colombier if (medium == Udp) { 542*4e7b9544SDavid du Colombier if (qp->udpfd <= 0) 543*4e7b9544SDavid du Colombier dnslog("readnet: qp->udpfd closed"); 544*4e7b9544SDavid du Colombier else { 545*4e7b9544SDavid du Colombier len = read(qp->udpfd, ibuf, OUdphdrsize+Maxudpin); 546*4e7b9544SDavid du Colombier if (len >= IPaddrlen) 547*4e7b9544SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 548*4e7b9544SDavid du Colombier if (len >= OUdphdrsize) { 549*4e7b9544SDavid du Colombier len -= OUdphdrsize; 550*4e7b9544SDavid du Colombier reply += OUdphdrsize; 551*4e7b9544SDavid du Colombier } 552*4e7b9544SDavid du Colombier } 553*4e7b9544SDavid du Colombier } else { 5543cbadd90SDavid du Colombier if (!qp->tcpset) 5553cbadd90SDavid du Colombier dnslog("readnet: tcp params not set"); 5563cbadd90SDavid du Colombier fd = qp->tcpfd; 5573cbadd90SDavid du Colombier if (fd <= 0) 5583cbadd90SDavid du Colombier dnslog("readnet: %s: tcp fd unset for dest %I", 5593cbadd90SDavid du Colombier qp->dp->name, qp->tcpip); 5603cbadd90SDavid du Colombier else if (readn(fd, lenbuf, 2) != 2) { 5613cbadd90SDavid du Colombier dnslog("readnet: short read of tcp size from %I", 5623cbadd90SDavid du Colombier qp->tcpip); 563ccf6439bSDavid du Colombier /* 564ccf6439bSDavid du Colombier * probably a time-out; demote the ns. 565ccf6439bSDavid du Colombier * actually, the problem may be the query, not the ns. 566ccf6439bSDavid du Colombier */ 5673cbadd90SDavid du Colombier addslug(qp->tcpip); 5683cbadd90SDavid du Colombier } else { 5693cbadd90SDavid du Colombier len = lenbuf[0]<<8 | lenbuf[1]; 5703cbadd90SDavid du Colombier if (readn(fd, ibuf, len) != len) { 5713cbadd90SDavid du Colombier dnslog("readnet: short read of tcp data from %I", 5723cbadd90SDavid du Colombier qp->tcpip); 5733cbadd90SDavid du Colombier /* probably a time-out; demote the ns */ 5743cbadd90SDavid du Colombier addslug(qp->tcpip); 5753cbadd90SDavid du Colombier len = -1; 5763cbadd90SDavid du Colombier } 5773cbadd90SDavid du Colombier } 5783cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 5793cbadd90SDavid du Colombier } 5803cbadd90SDavid du Colombier alarm(0); 5813cbadd90SDavid du Colombier *replyp = reply; 5823cbadd90SDavid du Colombier return len; 5833cbadd90SDavid du Colombier } 5843cbadd90SDavid du Colombier 5857dd7cddfSDavid du Colombier /* 5863cbadd90SDavid du Colombier * read replies to a request and remember the rrs in the answer(s). 5873cbadd90SDavid du Colombier * ignore any of the wrong type. 5884f8f669cSDavid du Colombier * wait at most until endtime. 5897dd7cddfSDavid du Colombier */ 5907dd7cddfSDavid du Colombier static int 5913cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp, 5923cbadd90SDavid du Colombier ulong endtime) 5937dd7cddfSDavid du Colombier { 5943cbadd90SDavid du Colombier int len = -1, rv; 5957dd7cddfSDavid du Colombier char *err; 5963cbadd90SDavid du Colombier uchar *reply; 5973cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 5983e12c5d1SDavid du Colombier RR *rp; 5997dd7cddfSDavid du Colombier 6007dd7cddfSDavid du Colombier notify(ding); 6017dd7cddfSDavid du Colombier 6023cbadd90SDavid du Colombier queryck(qp); 6033cbadd90SDavid du Colombier rv = 0; 6043cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 6053cbadd90SDavid du Colombier if (time(nil) >= endtime) 6063cbadd90SDavid du Colombier return -1; /* timed out before we started */ 6077dd7cddfSDavid du Colombier 6083cbadd90SDavid du Colombier for (; time(nil) < endtime && 6093cbadd90SDavid du Colombier (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0; 6103cbadd90SDavid du Colombier freeanswers(mp)){ 6117dd7cddfSDavid du Colombier /* convert into internal format */ 6123cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 6133cbadd90SDavid du Colombier err = convM2DNS(reply, len, mp, nil); 614d6d99297SDavid du Colombier if (mp->flags & Ftrunc) { 6153cbadd90SDavid du Colombier // dnslog("readreply: %s: truncated reply, len %d from %I", 6163cbadd90SDavid du Colombier // qp->dp->name, len, srcip); 6173cbadd90SDavid du Colombier /* notify the caller to retry the query via tcp. */ 6183cbadd90SDavid du Colombier return -1; 6193cbadd90SDavid du Colombier } else if(err){ 6203cbadd90SDavid du Colombier dnslog("readreply: %s: input err, len %d: %s: %I", 6213cbadd90SDavid du Colombier qp->dp->name, len, err, srcip); 6223cbadd90SDavid du Colombier free(err); 6237dd7cddfSDavid du Colombier continue; 6247dd7cddfSDavid du Colombier } 6253cbadd90SDavid du Colombier if (err) 6263cbadd90SDavid du Colombier free(err); 6277dd7cddfSDavid du Colombier if(debug) 6283cbadd90SDavid du Colombier logreply(qp->req->id, srcip, mp); 6297dd7cddfSDavid du Colombier 6307dd7cddfSDavid du Colombier /* answering the right question? */ 6313cbadd90SDavid du Colombier if(mp->id != req) 6323cbadd90SDavid du Colombier dnslog("%d: id %d instead of %d: %I", qp->req->id, 6333cbadd90SDavid du Colombier mp->id, req, srcip); 6343cbadd90SDavid du Colombier else if(mp->qd == 0) 6353cbadd90SDavid du Colombier dnslog("%d: no question RR: %I", qp->req->id, srcip); 6363cbadd90SDavid du Colombier else if(mp->qd->owner != qp->dp) 6373cbadd90SDavid du Colombier dnslog("%d: owner %s instead of %s: %I", qp->req->id, 6383cbadd90SDavid du Colombier mp->qd->owner->name, qp->dp->name, srcip); 6393cbadd90SDavid du Colombier else if(mp->qd->type != qp->type) 6403cbadd90SDavid du Colombier dnslog("%d: qp->type %d instead of %d: %I", 6413cbadd90SDavid du Colombier qp->req->id, mp->qd->type, qp->type, srcip); 6423cbadd90SDavid du Colombier else { 6437dd7cddfSDavid du Colombier /* remember what request this is in answer to */ 6447dd7cddfSDavid du Colombier for(rp = mp->an; rp; rp = rp->next) 6453cbadd90SDavid du Colombier rp->query = qp->type; 6463cbadd90SDavid du Colombier return rv; 6477dd7cddfSDavid du Colombier } 6487dd7cddfSDavid du Colombier } 6493cbadd90SDavid du Colombier if (time(nil) >= endtime) 6503cbadd90SDavid du Colombier addslug(srcip); 6513cbadd90SDavid du Colombier else 6523cbadd90SDavid du Colombier dnslog("readreply: %s: %I read error or eof (returned %d)", 6533cbadd90SDavid du Colombier qp->dp->name, srcip, len); 6543cbadd90SDavid du Colombier return -1; 6553cbadd90SDavid du Colombier } 6567dd7cddfSDavid du Colombier 6577dd7cddfSDavid du Colombier /* 6587dd7cddfSDavid du Colombier * return non-0 if first list includes second list 6597dd7cddfSDavid du Colombier */ 6607dd7cddfSDavid du Colombier int 6617dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2) 6627dd7cddfSDavid du Colombier { 6637dd7cddfSDavid du Colombier RR *trp1, *trp2; 6647dd7cddfSDavid du Colombier 6657dd7cddfSDavid du Colombier for(trp2 = rp2; trp2; trp2 = trp2->next){ 6663cbadd90SDavid du Colombier for(trp1 = rp1; trp1; trp1 = trp1->next) 6677dd7cddfSDavid du Colombier if(trp1->type == trp2->type) 6687dd7cddfSDavid du Colombier if(trp1->host == trp2->host) 6697dd7cddfSDavid du Colombier if(trp1->owner == trp2->owner) 6707dd7cddfSDavid du Colombier break; 6714f8f669cSDavid du Colombier if(trp1 == nil) 6727dd7cddfSDavid du Colombier return 0; 6737dd7cddfSDavid du Colombier } 6747dd7cddfSDavid du Colombier return 1; 6757dd7cddfSDavid du Colombier } 6767dd7cddfSDavid du Colombier 6777dd7cddfSDavid du Colombier 6786b6b9ac8SDavid du Colombier /* 6796b6b9ac8SDavid du Colombier * return multicast version if any 6806b6b9ac8SDavid du Colombier */ 6816b6b9ac8SDavid du Colombier int 6826b6b9ac8SDavid du Colombier ipisbm(uchar *ip) 6836b6b9ac8SDavid du Colombier { 6846b6b9ac8SDavid du Colombier if(isv4(ip)){ 6854f8f669cSDavid du Colombier if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 || 6864f8f669cSDavid du Colombier ipcmp(ip, IPv4bcast) == 0) 6876b6b9ac8SDavid du Colombier return 4; 6884f8f669cSDavid du Colombier } else 6896b6b9ac8SDavid du Colombier if(ip[0] == 0xff) 6906b6b9ac8SDavid du Colombier return 6; 69128a8a86bSDavid du Colombier return 0; 69228a8a86bSDavid du Colombier } 69328a8a86bSDavid du Colombier 6947dd7cddfSDavid du Colombier /* 6957dd7cddfSDavid du Colombier * Get next server address 6967dd7cddfSDavid du Colombier */ 6977dd7cddfSDavid du Colombier static int 6983cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth) 6997dd7cddfSDavid du Colombier { 7007dd7cddfSDavid du Colombier RR *rp, *arp, *trp; 7017dd7cddfSDavid du Colombier Dest *cur; 7027dd7cddfSDavid du Colombier 7037dd7cddfSDavid du Colombier if(nd >= Maxdest) 7047dd7cddfSDavid du Colombier return 0; 7057dd7cddfSDavid du Colombier 7067dd7cddfSDavid du Colombier /* 7077dd7cddfSDavid du Colombier * look for a server whose address we already know. 7087dd7cddfSDavid du Colombier * if we find one, mark it so we ignore this on 7097dd7cddfSDavid du Colombier * subsequent passes. 7107dd7cddfSDavid du Colombier */ 7117dd7cddfSDavid du Colombier arp = 0; 7123cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 71334f77ae3SDavid du Colombier assert(rp->magic == RRmagic); 7147dd7cddfSDavid du Colombier if(rp->marker) 7157dd7cddfSDavid du Colombier continue; 7167dd7cddfSDavid du Colombier arp = rrlookup(rp->host, Ta, NOneg); 7177dd7cddfSDavid du Colombier if(arp){ 7187dd7cddfSDavid du Colombier rp->marker = 1; 7197dd7cddfSDavid du Colombier break; 7207dd7cddfSDavid du Colombier } 7217dd7cddfSDavid du Colombier arp = dblookup(rp->host->name, Cin, Ta, 0, 0); 7227dd7cddfSDavid du Colombier if(arp){ 7237dd7cddfSDavid du Colombier rp->marker = 1; 7247dd7cddfSDavid du Colombier break; 7257dd7cddfSDavid du Colombier } 7267dd7cddfSDavid du Colombier } 7277dd7cddfSDavid du Colombier 7287dd7cddfSDavid du Colombier /* 7297dd7cddfSDavid du Colombier * if the cache and database lookup didn't find any new 7307dd7cddfSDavid du Colombier * server addresses, try resolving one via the network. 7317dd7cddfSDavid du Colombier * Mark any we try to resolve so we don't try a second time. 7327dd7cddfSDavid du Colombier */ 7334f8f669cSDavid du Colombier if(arp == 0) 7343cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 7357dd7cddfSDavid du Colombier if(rp->marker) 7367dd7cddfSDavid du Colombier continue; 7377dd7cddfSDavid du Colombier rp->marker = 1; 7387dd7cddfSDavid du Colombier 7397dd7cddfSDavid du Colombier /* 7407dd7cddfSDavid du Colombier * avoid loops looking up a server under itself 7417dd7cddfSDavid du Colombier */ 7427dd7cddfSDavid du Colombier if(subsume(rp->owner->name, rp->host->name)) 7437dd7cddfSDavid du Colombier continue; 7447dd7cddfSDavid du Colombier 7453cbadd90SDavid du Colombier arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0, 7464f8f669cSDavid du Colombier depth+1, Recurse, 1, 0); 7477dd7cddfSDavid du Colombier rrfreelist(rrremneg(&arp)); 7487dd7cddfSDavid du Colombier if(arp) 7497dd7cddfSDavid du Colombier break; 7507dd7cddfSDavid du Colombier } 7517dd7cddfSDavid du Colombier 7527dd7cddfSDavid du Colombier /* use any addresses that we found */ 7533cbadd90SDavid du Colombier for(trp = arp; trp && nd < Maxdest; trp = trp->next){ 7543cbadd90SDavid du Colombier cur = &qp->dest[nd]; 7557dd7cddfSDavid du Colombier parseip(cur->a, trp->ip->name); 7564f8f669cSDavid du Colombier /* 7574f8f669cSDavid du Colombier * straddling servers can reject all nameservers if they are all 7584f8f669cSDavid du Colombier * inside, so be sure to list at least one outside ns at 7594f8f669cSDavid du Colombier * the end of the ns list in /lib/ndb for `dom='. 7604f8f669cSDavid du Colombier */ 7614f8f669cSDavid du Colombier if (ipisbm(cur->a) || 7623cbadd90SDavid du Colombier cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a)) 7636b6b9ac8SDavid du Colombier continue; 7647dd7cddfSDavid du Colombier cur->nx = 0; 7657dd7cddfSDavid du Colombier cur->s = trp->owner; 7667dd7cddfSDavid du Colombier cur->code = Rtimeout; 7676b6b9ac8SDavid du Colombier nd++; 7687dd7cddfSDavid du Colombier } 7697dd7cddfSDavid du Colombier rrfreelist(arp); 7707dd7cddfSDavid du Colombier return nd; 7717dd7cddfSDavid du Colombier } 7727dd7cddfSDavid du Colombier 7737dd7cddfSDavid du Colombier /* 7747dd7cddfSDavid du Colombier * cache negative responses 7757dd7cddfSDavid du Colombier */ 7767dd7cddfSDavid du Colombier static void 7777dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr) 7787dd7cddfSDavid du Colombier { 7797dd7cddfSDavid du Colombier RR *rp; 7807dd7cddfSDavid du Colombier DN *soaowner; 7819a747e4fSDavid du Colombier ulong ttl; 7827dd7cddfSDavid du Colombier 7834f8f669cSDavid du Colombier /* no cache time specified, don't make anything up */ 7847dd7cddfSDavid du Colombier if(soarr != nil){ 7857dd7cddfSDavid du Colombier if(soarr->next != nil){ 7867dd7cddfSDavid du Colombier rrfreelist(soarr->next); 7877dd7cddfSDavid du Colombier soarr->next = nil; 7887dd7cddfSDavid du Colombier } 7897dd7cddfSDavid du Colombier soaowner = soarr->owner; 7907dd7cddfSDavid du Colombier } else 7917dd7cddfSDavid du Colombier soaowner = nil; 7927dd7cddfSDavid du Colombier 7939a747e4fSDavid du Colombier /* the attach can cause soarr to be freed so mine it now */ 7949a747e4fSDavid du Colombier if(soarr != nil && soarr->soa != nil) 7959a747e4fSDavid du Colombier ttl = soarr->soa->minttl+now; 7969a747e4fSDavid du Colombier else 7979a747e4fSDavid du Colombier ttl = 5*Min; 7989a747e4fSDavid du Colombier 7997dd7cddfSDavid du Colombier /* add soa and negative RR to the database */ 8007dd7cddfSDavid du Colombier rrattach(soarr, 1); 8017dd7cddfSDavid du Colombier 8027dd7cddfSDavid du Colombier rp = rralloc(type); 8037dd7cddfSDavid du Colombier rp->owner = dp; 8047dd7cddfSDavid du Colombier rp->negative = 1; 8057dd7cddfSDavid du Colombier rp->negsoaowner = soaowner; 8067dd7cddfSDavid du Colombier rp->negrcode = rcode; 8079a747e4fSDavid du Colombier rp->ttl = ttl; 8087dd7cddfSDavid du Colombier rrattach(rp, 1); 8097dd7cddfSDavid du Colombier } 8107dd7cddfSDavid du Colombier 8114f8f669cSDavid du Colombier static int 8124f8f669cSDavid du Colombier setdestoutns(Dest *p, int n) 8134f8f669cSDavid du Colombier { 8144f8f669cSDavid du Colombier uchar *outns = outsidens(n); 8154f8f669cSDavid du Colombier 8163cbadd90SDavid du Colombier destck(p); 8173cbadd90SDavid du Colombier destinit(p); 8184f8f669cSDavid du Colombier if (outns == nil) { 8194f8f669cSDavid du Colombier if (n == 0) 8204f8f669cSDavid du Colombier dnslog("[%d] no outside-ns in ndb", getpid()); 8214f8f669cSDavid du Colombier return -1; 8224f8f669cSDavid du Colombier } 8234f8f669cSDavid du Colombier memmove(p->a, outns, sizeof p->a); 8244f8f669cSDavid du Colombier p->s = dnlookup("outside-ns-ips", Cin, 1); 8254f8f669cSDavid du Colombier return 0; 8264f8f669cSDavid du Colombier } 8274f8f669cSDavid du Colombier 8287dd7cddfSDavid du Colombier /* 8293cbadd90SDavid du Colombier * issue query via UDP or TCP as appropriate. 8303cbadd90SDavid du Colombier * for TCP, returns with qp->tcpip set from udppkt header. 8317dd7cddfSDavid du Colombier */ 8327dd7cddfSDavid du Colombier static int 8333cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len) 8347dd7cddfSDavid du Colombier { 8353cbadd90SDavid du Colombier int rv = -1; 8363cbadd90SDavid du Colombier char *domain; 8373cbadd90SDavid du Colombier char conndir[40]; 8383cbadd90SDavid du Colombier NetConnInfo *nci; 8394f8f669cSDavid du Colombier 8403cbadd90SDavid du Colombier queryck(qp); 8413cbadd90SDavid du Colombier switch (medium) { 8423cbadd90SDavid du Colombier case Udp: 843*4e7b9544SDavid du Colombier if (qp->udpfd <= 0) 844*4e7b9544SDavid du Colombier dnslog("mydnsquery: qp->udpfd closed"); 845*4e7b9544SDavid du Colombier else { 846*4e7b9544SDavid du Colombier if (write(qp->udpfd, udppkt, len+OUdphdrsize) != 847*4e7b9544SDavid du Colombier len+OUdphdrsize) 8483cbadd90SDavid du Colombier warning("sending udp msg %r"); 8493cbadd90SDavid du Colombier rv = 0; 850*4e7b9544SDavid du Colombier } 8513cbadd90SDavid du Colombier break; 8523cbadd90SDavid du Colombier case Tcp: 8533cbadd90SDavid du Colombier /* send via TCP & keep fd around for reply */ 8543cbadd90SDavid du Colombier domain = smprint("%I", udppkt); 8553cbadd90SDavid du Colombier alarm(10*1000); 8563cbadd90SDavid du Colombier qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil, 8573cbadd90SDavid du Colombier conndir, &qp->tcpctlfd); 8583cbadd90SDavid du Colombier alarm(0); 8593cbadd90SDavid du Colombier if (qp->tcpfd < 0) { 8603cbadd90SDavid du Colombier dnslog("can't dial tcp!%s!dns: %r", domain); 8613cbadd90SDavid du Colombier addslug(udppkt); 8623cbadd90SDavid du Colombier } else { 8633cbadd90SDavid du Colombier uchar belen[2]; 8643e12c5d1SDavid du Colombier 8653cbadd90SDavid du Colombier nci = getnetconninfo(conndir, qp->tcpfd); 8663cbadd90SDavid du Colombier if (nci) { 8673cbadd90SDavid du Colombier parseip(qp->tcpip, nci->rsys); 8683cbadd90SDavid du Colombier freenetconninfo(nci); 8693cbadd90SDavid du Colombier } else 8703cbadd90SDavid du Colombier dnslog("mydnsquery: getnetconninfo failed"); 8713cbadd90SDavid du Colombier qp->tcpset = 1; 8723e12c5d1SDavid du Colombier 8733cbadd90SDavid du Colombier belen[0] = len >> 8; 8743cbadd90SDavid du Colombier belen[1] = len; 8753cbadd90SDavid du Colombier if (write(qp->tcpfd, belen, 2) != 2 || 8763cbadd90SDavid du Colombier write(qp->tcpfd, udppkt + OUdphdrsize, len) != len) 8773cbadd90SDavid du Colombier warning("sending tcp msg %r"); 8783cbadd90SDavid du Colombier } 8793cbadd90SDavid du Colombier free(domain); 8803cbadd90SDavid du Colombier break; 8813cbadd90SDavid du Colombier default: 8823cbadd90SDavid du Colombier sysfatal("mydnsquery: bad medium"); 8833cbadd90SDavid du Colombier } 8843cbadd90SDavid du Colombier return rv; 8853cbadd90SDavid du Colombier } 8867dd7cddfSDavid du Colombier 8873e12c5d1SDavid du Colombier /* 8883cbadd90SDavid du Colombier * send query to all UDP destinations or one TCP destination, 8893cbadd90SDavid du Colombier * taken from obuf (udp packet) header 8903e12c5d1SDavid du Colombier */ 8913cbadd90SDavid du Colombier static int 8923cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len) 8933cbadd90SDavid du Colombier { 8943cbadd90SDavid du Colombier int j, n; 8953cbadd90SDavid du Colombier char buf[32]; 8963cbadd90SDavid du Colombier Dest *p; 8973e12c5d1SDavid du Colombier 8983cbadd90SDavid du Colombier queryck(qp); 8993cbadd90SDavid du Colombier if(time(nil) >= qp->req->aborttime) 9003cbadd90SDavid du Colombier return -1; 9013cbadd90SDavid du Colombier 9023cbadd90SDavid du Colombier /* 9033cbadd90SDavid du Colombier * get a nameserver address if we need one. 9043cbadd90SDavid du Colombier * serveraddrs populates qp->dest. 9053cbadd90SDavid du Colombier */ 9063cbadd90SDavid du Colombier p = qp->dest; 9073cbadd90SDavid du Colombier destck(p); 9083cbadd90SDavid du Colombier if (qp->ndest < 0 || qp->ndest > Maxdest) 9093cbadd90SDavid du Colombier dnslog("qp->ndest %d out of range", qp->ndest); 9103cbadd90SDavid du Colombier if (qp->ndest > qp->curdest - p) 9113cbadd90SDavid du Colombier qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)]; 9123cbadd90SDavid du Colombier destck(qp->curdest); 9137dd7cddfSDavid du Colombier 9147dd7cddfSDavid du Colombier /* no servers, punt */ 9153cbadd90SDavid du Colombier if (qp->curdest == qp->dest) 9164f8f669cSDavid du Colombier if (cfg.straddle && cfg.inside) { 9173cbadd90SDavid du Colombier /* get ips of "outside-ns-ips" */ 9183cbadd90SDavid du Colombier p = qp->curdest = qp->dest; 9193cbadd90SDavid du Colombier for(n = 0; n < Maxdest; n++, qp->curdest++) 9203cbadd90SDavid du Colombier if (setdestoutns(qp->curdest, n) < 0) 9217dd7cddfSDavid du Colombier break; 9224f8f669cSDavid du Colombier } else { 923*4e7b9544SDavid du Colombier /* it's probably just a bogus domain, don't log it */ 924*4e7b9544SDavid du Colombier // dnslog("xmitquery: %s: no nameservers", qp->dp->name); 9253cbadd90SDavid du Colombier return -1; 9264f8f669cSDavid du Colombier } 9277dd7cddfSDavid du Colombier 9283cbadd90SDavid du Colombier /* send to first 'qp->ndest' destinations */ 9297dd7cddfSDavid du Colombier j = 0; 9303cbadd90SDavid du Colombier if (medium == Tcp) { 9313cbadd90SDavid du Colombier j++; 9323cbadd90SDavid du Colombier queryck(qp); 9333cbadd90SDavid du Colombier assert(qp->dp); 9343cbadd90SDavid du Colombier procsetname("tcp %sside query for %s %s", (inns? "in": "out"), 9353cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 9363cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */ 9373cbadd90SDavid du Colombier if(debug) 9383cbadd90SDavid du Colombier logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name, 9393cbadd90SDavid du Colombier qp->type); 9403cbadd90SDavid du Colombier } else 9413cbadd90SDavid du Colombier for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){ 9427dd7cddfSDavid du Colombier /* skip destinations we've finished with */ 9437dd7cddfSDavid du Colombier if(p->nx >= Maxtrans) 9447dd7cddfSDavid du Colombier continue; 9457dd7cddfSDavid du Colombier 9467dd7cddfSDavid du Colombier j++; 9477dd7cddfSDavid du Colombier 9487dd7cddfSDavid du Colombier /* exponential backoff of requests */ 9493cbadd90SDavid du Colombier if((1<<p->nx) > qp->ndest) 9507dd7cddfSDavid du Colombier continue; 9517dd7cddfSDavid du Colombier 9523cbadd90SDavid du Colombier procsetname("udp %sside query to %I/%s %s %s", 9533cbadd90SDavid du Colombier (inns? "in": "out"), p->a, p->s->name, 9543cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 955219b2ee8SDavid du Colombier if(debug) 9563cbadd90SDavid du Colombier logsend(qp->req->id, depth, p->a, p->s->name, 9573cbadd90SDavid du Colombier qp->dp->name, qp->type); 9584f8f669cSDavid du Colombier 9593cbadd90SDavid du Colombier /* fill in UDP destination addr & send it */ 9603cbadd90SDavid du Colombier memmove(obuf, p->a, sizeof p->a); 9613cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); 9627dd7cddfSDavid du Colombier p->nx++; 9633e12c5d1SDavid du Colombier } 9643cbadd90SDavid du Colombier if(j == 0) { 9653cbadd90SDavid du Colombier // dnslog("xmitquery: %s: no destinations left", qp->dp->name); 9663cbadd90SDavid du Colombier return -1; 9673cbadd90SDavid du Colombier } 9683cbadd90SDavid du Colombier return 0; 9693cbadd90SDavid du Colombier } 9703e12c5d1SDavid du Colombier 9713cbadd90SDavid du Colombier static int 9723cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) 9733cbadd90SDavid du Colombier { 9743cbadd90SDavid du Colombier int rv; 9753cbadd90SDavid du Colombier char buf[32]; 9763cbadd90SDavid du Colombier DN *ndp; 9773cbadd90SDavid du Colombier Query nquery; 9783cbadd90SDavid du Colombier RR *tp, *soarr; 9797dd7cddfSDavid du Colombier 98059cc4ca5SDavid du Colombier /* ignore any error replies */ 9813cbadd90SDavid du Colombier if((mp->flags & Rmask) == Rserver){ 9823cbadd90SDavid du Colombier rrfreelist(mp->qd); 9833cbadd90SDavid du Colombier rrfreelist(mp->an); 9843cbadd90SDavid du Colombier rrfreelist(mp->ar); 9853cbadd90SDavid du Colombier rrfreelist(mp->ns); 9863cbadd90SDavid du Colombier if(p != qp->curdest) 9877dd7cddfSDavid du Colombier p->code = Rserver; 9883cbadd90SDavid du Colombier return -1; 9893e12c5d1SDavid du Colombier } 9903e12c5d1SDavid du Colombier 99159cc4ca5SDavid du Colombier /* ignore any bad delegations */ 9923cbadd90SDavid du Colombier if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){ 9933cbadd90SDavid du Colombier rrfreelist(mp->ns); 9943cbadd90SDavid du Colombier mp->ns = nil; 9953cbadd90SDavid du Colombier if(mp->an == nil){ 9963cbadd90SDavid du Colombier rrfreelist(mp->qd); 9973cbadd90SDavid du Colombier rrfreelist(mp->ar); 9983cbadd90SDavid du Colombier if(p != qp->curdest) 99959cc4ca5SDavid du Colombier p->code = Rserver; 10003cbadd90SDavid du Colombier return -1; 100159cc4ca5SDavid du Colombier } 100259cc4ca5SDavid du Colombier } 100359cc4ca5SDavid du Colombier 10047dd7cddfSDavid du Colombier /* remove any soa's from the authority section */ 10053cbadd90SDavid du Colombier soarr = rrremtype(&mp->ns, Tsoa); 10067dd7cddfSDavid du Colombier 10073e12c5d1SDavid du Colombier /* incorporate answers */ 10083cbadd90SDavid du Colombier if(mp->an) 10093cbadd90SDavid du Colombier rrattach(mp->an, (mp->flags & Fauth) != 0); 10103cbadd90SDavid du Colombier if(mp->ar) 10113cbadd90SDavid du Colombier rrattach(mp->ar, 0); 10123cbadd90SDavid du Colombier if(mp->ns){ 10133cbadd90SDavid du Colombier ndp = mp->ns->owner; 10143cbadd90SDavid du Colombier rrattach(mp->ns, 0); 10157dd7cddfSDavid du Colombier } else 10164f8f669cSDavid du Colombier ndp = nil; 10177dd7cddfSDavid du Colombier 10187dd7cddfSDavid du Colombier /* free the question */ 10193cbadd90SDavid du Colombier if(mp->qd) 10203cbadd90SDavid du Colombier rrfreelist(mp->qd); 10213e12c5d1SDavid du Colombier 10223e12c5d1SDavid du Colombier /* 10233e12c5d1SDavid du Colombier * Any reply from an authoritative server, 10243e12c5d1SDavid du Colombier * or a positive reply terminates the search 10253e12c5d1SDavid du Colombier */ 10263cbadd90SDavid du Colombier if(mp->an != nil || (mp->flags & Fauth)){ 10273cbadd90SDavid du Colombier if(mp->an == nil && (mp->flags & Rmask) == Rname) 10283cbadd90SDavid du Colombier qp->dp->respcode = Rname; 10297dd7cddfSDavid du Colombier else 10303cbadd90SDavid du Colombier qp->dp->respcode = 0; 10317dd7cddfSDavid du Colombier 10327dd7cddfSDavid du Colombier /* 10337dd7cddfSDavid du Colombier * cache any negative responses, free soarr 10347dd7cddfSDavid du Colombier */ 10353cbadd90SDavid du Colombier if((mp->flags & Fauth) && mp->an == nil) 10363cbadd90SDavid du Colombier cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr); 10377dd7cddfSDavid du Colombier else 10387dd7cddfSDavid du Colombier rrfreelist(soarr); 10393e12c5d1SDavid du Colombier return 1; 10403e12c5d1SDavid du Colombier } 10417dd7cddfSDavid du Colombier rrfreelist(soarr); 10423e12c5d1SDavid du Colombier 10433e12c5d1SDavid du Colombier /* 10444f8f669cSDavid du Colombier * if we've been given better name servers, 10454f8f669cSDavid du Colombier * recurse. we're called from udpquery, called from 10463cbadd90SDavid du Colombier * netquery, which current holds qp->dp->querylck, 10474f8f669cSDavid du Colombier * so release it now and acquire it upon return. 10483e12c5d1SDavid du Colombier */ 10493cbadd90SDavid du Colombier if(!mp->ns) 10503cbadd90SDavid du Colombier return 0; 10517dd7cddfSDavid du Colombier tp = rrlookup(ndp, Tns, NOneg); 10523cbadd90SDavid du Colombier if(contains(qp->nsrp, tp)){ 10537dd7cddfSDavid du Colombier rrfreelist(tp); 10543cbadd90SDavid du Colombier return 0; 10553e12c5d1SDavid du Colombier } 10563cbadd90SDavid du Colombier procsetname("recursive query for %s %s", qp->dp->name, 10573cbadd90SDavid du Colombier rrname(qp->type, buf, sizeof buf)); 1058ccf6439bSDavid du Colombier // qunlock(&qp->dp->querylck); 10593cbadd90SDavid du Colombier 10603cbadd90SDavid du Colombier queryinit(&nquery, qp->dp, qp->type, qp->req); 10613cbadd90SDavid du Colombier nquery.nsrp = tp; 10623cbadd90SDavid du Colombier rv = netquery(&nquery, depth+1); 10633cbadd90SDavid du Colombier 1064ccf6439bSDavid du Colombier // qlock(&qp->dp->querylck); 10653cbadd90SDavid du Colombier rrfreelist(tp); 1066*4e7b9544SDavid du Colombier querydestroy(&nquery); 10673cbadd90SDavid du Colombier return rv; 10683cbadd90SDavid du Colombier } 10693cbadd90SDavid du Colombier 10703cbadd90SDavid du Colombier /* 10713cbadd90SDavid du Colombier * send a query via tcp to a single address (from ibuf's udp header) 10723cbadd90SDavid du Colombier * and read the answer(s) into mp->an. 10733cbadd90SDavid du Colombier */ 10743cbadd90SDavid du Colombier static int 10753cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len, 10763cbadd90SDavid du Colombier int waitsecs, int inns, ushort req) 10773cbadd90SDavid du Colombier { 1078*4e7b9544SDavid du Colombier int rv = 0; 10793cbadd90SDavid du Colombier ulong endtime; 10803cbadd90SDavid du Colombier 10813cbadd90SDavid du Colombier endtime = time(nil) + waitsecs; 10823cbadd90SDavid du Colombier if(endtime > qp->req->aborttime) 10833cbadd90SDavid du Colombier endtime = qp->req->aborttime; 10843cbadd90SDavid du Colombier 1085*4e7b9544SDavid du Colombier dnslog("%s: udp reply truncated; retrying query via tcp to %I", 1086*4e7b9544SDavid du Colombier qp->dp->name, qp->tcpip); 1087*4e7b9544SDavid du Colombier 10883cbadd90SDavid du Colombier qlock(&qp->tcplock); 10893cbadd90SDavid du Colombier memmove(obuf, ibuf, IPaddrlen); /* send back to respondent */ 10903cbadd90SDavid du Colombier /* sets qp->tcpip from obuf's udp header */ 1091*4e7b9544SDavid du Colombier if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 || 1092*4e7b9544SDavid du Colombier readreply(qp, Tcp, req, ibuf, mp, endtime) < 0) 1093*4e7b9544SDavid du Colombier rv = -1; 10943cbadd90SDavid du Colombier if (qp->tcpfd > 0) { 10953cbadd90SDavid du Colombier hangup(qp->tcpctlfd); 10963cbadd90SDavid du Colombier close(qp->tcpctlfd); 10973cbadd90SDavid du Colombier close(qp->tcpfd); 10983cbadd90SDavid du Colombier } 10993cbadd90SDavid du Colombier qp->tcpfd = qp->tcpctlfd = -1; 11003cbadd90SDavid du Colombier qunlock(&qp->tcplock); 1101*4e7b9544SDavid du Colombier return rv; 11023cbadd90SDavid du Colombier } 11033cbadd90SDavid du Colombier 11043cbadd90SDavid du Colombier /* 11053cbadd90SDavid du Colombier * query name servers. If the name server returns a pointer to another 11063cbadd90SDavid du Colombier * name server, recurse. 11073cbadd90SDavid du Colombier */ 11083cbadd90SDavid du Colombier static int 11093cbadd90SDavid du Colombier netquery1(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns) 11103cbadd90SDavid du Colombier { 11113cbadd90SDavid du Colombier int ndest, len, replywaits, rv; 11123cbadd90SDavid du Colombier ushort req; 11133cbadd90SDavid du Colombier ulong endtime; 11143cbadd90SDavid du Colombier char buf[12]; 11153cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 11163cbadd90SDavid du Colombier DNSmsg m; 11173cbadd90SDavid du Colombier Dest *p, *np; 11183cbadd90SDavid du Colombier Dest dest[Maxdest]; 11193cbadd90SDavid du Colombier 11203cbadd90SDavid du Colombier /* pack request into a udp message */ 11213cbadd90SDavid du Colombier req = rand(); 11223cbadd90SDavid du Colombier len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req); 11233cbadd90SDavid du Colombier 11243cbadd90SDavid du Colombier /* no server addresses yet */ 11253cbadd90SDavid du Colombier queryck(qp); 11263cbadd90SDavid du Colombier for (p = dest; p < dest + nelem(dest); p++) 11273cbadd90SDavid du Colombier destinit(p); 11283cbadd90SDavid du Colombier qp->curdest = qp->dest = dest; 11293cbadd90SDavid du Colombier 11303cbadd90SDavid du Colombier /* 11313cbadd90SDavid du Colombier * transmit udp requests and wait for answers. 11323cbadd90SDavid du Colombier * at most Maxtrans attempts to each address. 11333cbadd90SDavid du Colombier * each cycle send one more message than the previous. 11343cbadd90SDavid du Colombier * retry a query via tcp if its response is truncated. 11353cbadd90SDavid du Colombier */ 11363cbadd90SDavid du Colombier for(ndest = 1; ndest < Maxdest; ndest++){ 11373cbadd90SDavid du Colombier qp->ndest = ndest; 11383cbadd90SDavid du Colombier qp->tcpset = 0; 11393cbadd90SDavid du Colombier if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0) 11403cbadd90SDavid du Colombier break; 11413cbadd90SDavid du Colombier 11423cbadd90SDavid du Colombier endtime = time(nil) + waitsecs; 11433cbadd90SDavid du Colombier if(endtime > qp->req->aborttime) 11443cbadd90SDavid du Colombier endtime = qp->req->aborttime; 11453cbadd90SDavid du Colombier 11463cbadd90SDavid du Colombier for(replywaits = 0; replywaits < ndest; replywaits++){ 11473cbadd90SDavid du Colombier procsetname("reading %sside reply from %s%I for %s %s", 11483cbadd90SDavid du Colombier (inns? "in": "out"), 11493cbadd90SDavid du Colombier (isaslug(qp->tcpip)? "sluggard ": ""), obuf, 11503cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 11513cbadd90SDavid du Colombier 11523cbadd90SDavid du Colombier /* read udp answer */ 11533cbadd90SDavid du Colombier if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0) 11543cbadd90SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 11553cbadd90SDavid du Colombier else if (!(m.flags & Ftrunc)) { 11563cbadd90SDavid du Colombier addslug(ibuf); 11573cbadd90SDavid du Colombier break; /* timed out on this dest */ 11583cbadd90SDavid du Colombier } else { 11593cbadd90SDavid du Colombier /* whoops, it was truncated! ask again via tcp */ 11603cbadd90SDavid du Colombier rv = tcpquery(qp, &m, depth, ibuf, obuf, len, 11613cbadd90SDavid du Colombier waitsecs, inns, req); 11623cbadd90SDavid du Colombier if (rv < 0) 11633cbadd90SDavid du Colombier break; /* failed via tcp too */ 11643cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 11653cbadd90SDavid du Colombier } 11663cbadd90SDavid du Colombier 11673cbadd90SDavid du Colombier /* find responder */ 11683cbadd90SDavid du Colombier // dnslog("netquery1 got reply from %I", srcip); 11693cbadd90SDavid du Colombier for(p = qp->dest; p < qp->curdest; p++) 11703cbadd90SDavid du Colombier if(memcmp(p->a, srcip, sizeof p->a) == 0) 11713cbadd90SDavid du Colombier break; 11723cbadd90SDavid du Colombier 11733cbadd90SDavid du Colombier /* remove all addrs of responding server from list */ 11743cbadd90SDavid du Colombier for(np = qp->dest; np < qp->curdest; np++) 11753cbadd90SDavid du Colombier if(np->s == p->s) 11763cbadd90SDavid du Colombier p->nx = Maxtrans; 11773cbadd90SDavid du Colombier 11783cbadd90SDavid du Colombier rv = procansw(qp, &m, srcip, depth, p); 11793cbadd90SDavid du Colombier if (rv > 0) 11803cbadd90SDavid du Colombier return rv; 11813e12c5d1SDavid du Colombier } 11823e12c5d1SDavid du Colombier } 11837dd7cddfSDavid du Colombier 11844f8f669cSDavid du Colombier /* if all servers returned failure, propagate it */ 11853cbadd90SDavid du Colombier qp->dp->respcode = Rserver; 11863cbadd90SDavid du Colombier for(p = dest; p < qp->curdest; p++) { 11873cbadd90SDavid du Colombier destck(p); 11887dd7cddfSDavid du Colombier if(p->code != Rserver) 11893cbadd90SDavid du Colombier qp->dp->respcode = 0; 11903cbadd90SDavid du Colombier p->magic = 0; /* prevent accidents */ 11913cbadd90SDavid du Colombier } 11924f8f669cSDavid du Colombier 11933cbadd90SDavid du Colombier // if (qp->dp->respcode) 11943cbadd90SDavid du Colombier // dnslog("netquery1 setting Rserver for %s", qp->dp->name); 11957dd7cddfSDavid du Colombier 11963cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; /* prevent accidents */ 11973e12c5d1SDavid du Colombier return 0; 11983e12c5d1SDavid du Colombier } 11997dd7cddfSDavid du Colombier 12004f8f669cSDavid du Colombier /* 12014f8f669cSDavid du Colombier * run a command with a supplied fd as standard input 12024f8f669cSDavid du Colombier */ 12034f8f669cSDavid du Colombier char * 12044f8f669cSDavid du Colombier system(int fd, char *cmd) 120528a8a86bSDavid du Colombier { 12064f8f669cSDavid du Colombier int pid, p, i; 12074f8f669cSDavid du Colombier static Waitmsg msg; 12086b0d5c8bSDavid du Colombier 12094f8f669cSDavid du Colombier if((pid = fork()) == -1) 12104f8f669cSDavid du Colombier sysfatal("fork failed: %r"); 12114f8f669cSDavid du Colombier else if(pid == 0){ 12124f8f669cSDavid du Colombier dup(fd, 0); 12134f8f669cSDavid du Colombier close(fd); 12144f8f669cSDavid du Colombier for (i = 3; i < 200; i++) 12154f8f669cSDavid du Colombier close(i); /* don't leak fds */ 12164f8f669cSDavid du Colombier execl("/bin/rc", "rc", "-c", cmd, nil); 12174f8f669cSDavid du Colombier sysfatal("exec rc: %r"); 12184f8f669cSDavid du Colombier } 12194f8f669cSDavid du Colombier for(p = waitpid(); p >= 0; p = waitpid()) 12204f8f669cSDavid du Colombier if(p == pid) 12214f8f669cSDavid du Colombier return msg.msg; 12224f8f669cSDavid du Colombier return "lost child"; 12234f8f669cSDavid du Colombier } 12244f8f669cSDavid du Colombier 12254f8f669cSDavid du Colombier enum { Hurry, Patient, }; 12264f8f669cSDavid du Colombier enum { Outns, Inns, }; 12274f8f669cSDavid du Colombier enum { Remntretry = 15, }; /* min. sec.s between remount attempts */ 12284f8f669cSDavid du Colombier 12294f8f669cSDavid du Colombier static int 12303cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns) 12314f8f669cSDavid du Colombier { 12324f8f669cSDavid du Colombier int fd, rv = 0; 12334f8f669cSDavid du Colombier long now; 12344f8f669cSDavid du Colombier char *msg; 12354f8f669cSDavid du Colombier uchar *obuf, *ibuf; 12364f8f669cSDavid du Colombier static QLock mntlck; 12374f8f669cSDavid du Colombier static ulong lastmount; 12387dd7cddfSDavid du Colombier 12397dd7cddfSDavid du Colombier /* use alloced buffers rather than ones from the stack */ 12403cbadd90SDavid du Colombier // ibuf = emalloc(Maxudpin+OUdphdrsize); 12413cbadd90SDavid du Colombier ibuf = emalloc(64*1024); /* max. tcp reply size */ 1242d9dc5dd1SDavid du Colombier obuf = emalloc(Maxudp+OUdphdrsize); 12437dd7cddfSDavid du Colombier 12444f8f669cSDavid du Colombier fd = udpport(mntpt); 12454f8f669cSDavid du Colombier while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) { 12464f8f669cSDavid du Colombier /* HACK: remount /net.alt */ 12474f8f669cSDavid du Colombier now = time(nil); 12484f8f669cSDavid du Colombier if (now < lastmount + Remntretry) 12494f8f669cSDavid du Colombier sleep((lastmount + Remntretry - now)*1000); 12504f8f669cSDavid du Colombier qlock(&mntlck); 12514f8f669cSDavid du Colombier fd = udpport(mntpt); /* try again under lock */ 12524f8f669cSDavid du Colombier if (fd < 0) { 12534f8f669cSDavid du Colombier dnslog("[%d] remounting /net.alt", getpid()); 12544f8f669cSDavid du Colombier unmount(nil, "/net.alt"); 12554f8f669cSDavid du Colombier 12564f8f669cSDavid du Colombier msg = system(open("/dev/null", ORDWR), "outside"); 12574f8f669cSDavid du Colombier 12584f8f669cSDavid du Colombier lastmount = time(nil); 12594f8f669cSDavid du Colombier if (msg && *msg) { 12604f8f669cSDavid du Colombier dnslog("[%d] can't remount /net.alt: %s", 12614f8f669cSDavid du Colombier getpid(), msg); 12624f8f669cSDavid du Colombier sleep(10*1000); /* don't spin wildly */ 12634f8f669cSDavid du Colombier } else 12644f8f669cSDavid du Colombier fd = udpport(mntpt); 12654f8f669cSDavid du Colombier } 12664f8f669cSDavid du Colombier qunlock(&mntlck); 12674f8f669cSDavid du Colombier } 12684f8f669cSDavid du Colombier if(fd >= 0) { 12693cbadd90SDavid du Colombier qp->req->aborttime = time(nil) + (patient? Maxreqtm: Maxreqtm/2); 12703cbadd90SDavid du Colombier qp->udpfd = fd; 1271ccf6439bSDavid du Colombier /* tune; was (patient? 15: 10) */ 12723cbadd90SDavid du Colombier rv = netquery1(qp, depth, ibuf, obuf, (patient? 10: 5), inns); 12734f8f669cSDavid du Colombier close(fd); 12744f8f669cSDavid du Colombier } else 12754f8f669cSDavid du Colombier dnslog("can't get udpport for %s query of name %s: %r", 12763cbadd90SDavid du Colombier mntpt, qp->dp->name); 12774f8f669cSDavid du Colombier 12784f8f669cSDavid du Colombier free(obuf); 12794f8f669cSDavid du Colombier free(ibuf); 12804f8f669cSDavid du Colombier return rv; 12814f8f669cSDavid du Colombier } 12824f8f669cSDavid du Colombier 12834f8f669cSDavid du Colombier /* look up (dp->name,type) via *nsrp with results in *reqp */ 12844f8f669cSDavid du Colombier static int 12853cbadd90SDavid du Colombier netquery(Query *qp, int depth) 12864f8f669cSDavid du Colombier { 12874f8f669cSDavid du Colombier int lock, rv, triedin, inname; 12884f8f669cSDavid du Colombier RR *rp; 12894f8f669cSDavid du Colombier 12904f8f669cSDavid du Colombier if(depth > 12) /* in a recursive loop? */ 12914f8f669cSDavid du Colombier return 0; 12924f8f669cSDavid du Colombier 12933cbadd90SDavid du Colombier slave(qp->req); 1294d3907fe5SDavid du Colombier /* 1295d3907fe5SDavid du Colombier * slave might have forked. if so, the parent process longjmped to 1296d3907fe5SDavid du Colombier * req->mret; we're usually the child slave, but if there are too 1297d3907fe5SDavid du Colombier * many children already, we're still the same process. 1298d3907fe5SDavid du Colombier */ 12994f8f669cSDavid du Colombier 13004f8f669cSDavid du Colombier /* don't lock before call to slave so only children can block */ 1301ccf6439bSDavid du Colombier if (0) 13023cbadd90SDavid du Colombier lock = qp->req->isslave != 0; 1303ccf6439bSDavid du Colombier if(0 && lock) { 13043cbadd90SDavid du Colombier procsetname("query lock wait for %s", qp->dp->name); 1305ccf6439bSDavid du Colombier /* 1306ccf6439bSDavid du Colombier * don't make concurrent queries for this name. 1307ccf6439bSDavid du Colombier * 1308ccf6439bSDavid du Colombier * this seemed like a good idea, to avoid swamping 1309ccf6439bSDavid du Colombier * an overloaded ns, but in practice, dns processes 1310ccf6439bSDavid du Colombier * pile up quickly and dns becomes unresponsive for a while. 1311ccf6439bSDavid du Colombier */ 13123cbadd90SDavid du Colombier qlock(&qp->dp->querylck); 13134f8f669cSDavid du Colombier } 1314ccf6439bSDavid du Colombier procsetname("netquery: %s", qp->dp->name); 13156b0d5c8bSDavid du Colombier 13166b0d5c8bSDavid du Colombier /* prepare server RR's for incremental lookup */ 13173cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 13186b0d5c8bSDavid du Colombier rp->marker = 0; 13196b0d5c8bSDavid du Colombier 13204f8f669cSDavid du Colombier rv = 0; /* pessimism */ 13214f8f669cSDavid du Colombier triedin = 0; 13223cbadd90SDavid du Colombier qp->nsrp = qp->nsrp; 13234f8f669cSDavid du Colombier /* 13244f8f669cSDavid du Colombier * normal resolvers and servers will just use mntpt for all addresses, 13254f8f669cSDavid du Colombier * even on the outside. straddling servers will use mntpt (/net) 13264f8f669cSDavid du Colombier * for inside addresses and /net.alt for outside addresses, 13274f8f669cSDavid du Colombier * thus bypassing other inside nameservers. 13284f8f669cSDavid du Colombier */ 13293cbadd90SDavid du Colombier inname = insideaddr(qp->dp->name); 13304f8f669cSDavid du Colombier if (!cfg.straddle || inname) { 13313cbadd90SDavid du Colombier rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns)); 13324f8f669cSDavid du Colombier triedin = 1; 13334f8f669cSDavid du Colombier } 13344f8f669cSDavid du Colombier 13354f8f669cSDavid du Colombier /* 13364f8f669cSDavid du Colombier * if we're still looking, are inside, and have an outside domain, 13374f8f669cSDavid du Colombier * try it on our outside interface, if any. 13384f8f669cSDavid du Colombier */ 13394f8f669cSDavid du Colombier if (rv == 0 && cfg.inside && !inname) { 13404f8f669cSDavid du Colombier if (triedin) 13414f8f669cSDavid du Colombier dnslog( 13424f8f669cSDavid du Colombier "[%d] netquery: internal nameservers failed for %s; trying external", 13433cbadd90SDavid du Colombier getpid(), qp->dp->name); 13444f8f669cSDavid du Colombier 13454f8f669cSDavid du Colombier /* prepare server RR's for incremental lookup */ 13463cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 13474f8f669cSDavid du Colombier rp->marker = 0; 13484f8f669cSDavid du Colombier 13493cbadd90SDavid du Colombier rv = udpquery(qp, "/net.alt", depth, Patient, Outns); 13504f8f669cSDavid du Colombier } 13513cbadd90SDavid du Colombier // if (rv == 0) /* could ask /net.alt/dns directly */ 13523cbadd90SDavid du Colombier // askoutdns(qp->dp, qp->type); 13534f8f669cSDavid du Colombier 1354ccf6439bSDavid du Colombier if(0 && lock) 13553cbadd90SDavid du Colombier qunlock(&qp->dp->querylck); 13567dd7cddfSDavid du Colombier return rv; 13577dd7cddfSDavid du Colombier } 13584f8f669cSDavid du Colombier 13594f8f669cSDavid du Colombier int 13604f8f669cSDavid du Colombier seerootns(void) 13614f8f669cSDavid du Colombier { 13623cbadd90SDavid du Colombier int rv; 13634f8f669cSDavid du Colombier char root[] = ""; 13644f8f669cSDavid du Colombier Request req; 13653cbadd90SDavid du Colombier Query query; 13664f8f669cSDavid du Colombier 13674f8f669cSDavid du Colombier memset(&req, 0, sizeof req); 13684f8f669cSDavid du Colombier req.isslave = 1; 1369ccf6439bSDavid du Colombier req.aborttime = now + Maxreqtm; 13703cbadd90SDavid du Colombier queryinit(&query, dnlookup(root, Cin, 1), Tns, &req); 13713cbadd90SDavid du Colombier query.nsrp = dblookup(root, Cin, Tns, 0, 0); 13723cbadd90SDavid du Colombier rv = netquery(&query, 0); 1373*4e7b9544SDavid du Colombier querydestroy(&query); 13743cbadd90SDavid du Colombier return rv; 13754f8f669cSDavid du Colombier } 1376