1*3cbadd90SDavid du Colombier /* 2*3cbadd90SDavid du Colombier * domain name resolvers, see rfcs 1035 and 1123 3*3cbadd90SDavid 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 11*3cbadd90SDavid du Colombier typedef struct Dest Dest; 12*3cbadd90SDavid du Colombier typedef struct Ipaddr Ipaddr; 13*3cbadd90SDavid du Colombier typedef struct Query Query; 14*3cbadd90SDavid du Colombier typedef struct Sluggards Sluggards; 15*3cbadd90SDavid du Colombier 163e12c5d1SDavid du Colombier enum 173e12c5d1SDavid du Colombier { 18*3cbadd90SDavid 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 */ 21*3cbadd90SDavid du Colombier Destmagic= 0xcafebabe, 22*3cbadd90SDavid du Colombier Querymagic= 0xdeadbeef, 233e12c5d1SDavid du Colombier }; 243e12c5d1SDavid du Colombier 25*3cbadd90SDavid du Colombier struct Ipaddr { 26*3cbadd90SDavid du Colombier Ipaddr *next; 27*3cbadd90SDavid du Colombier uchar ip[IPaddrlen]; 28*3cbadd90SDavid du Colombier }; 29*3cbadd90SDavid du Colombier 30*3cbadd90SDavid du Colombier struct Dest 31*3cbadd90SDavid du Colombier { 32*3cbadd90SDavid du Colombier uchar a[IPaddrlen]; /* ip address */ 33*3cbadd90SDavid du Colombier DN *s; /* name server */ 34*3cbadd90SDavid du Colombier int nx; /* number of transmissions */ 35*3cbadd90SDavid du Colombier int code; /* response code; used to clear dp->respcode */ 36*3cbadd90SDavid du Colombier 37*3cbadd90SDavid du Colombier ulong magic; 38*3cbadd90SDavid du Colombier }; 39*3cbadd90SDavid du Colombier 40*3cbadd90SDavid du Colombier struct Query { 41*3cbadd90SDavid du Colombier DN *dp; /* domain */ 42*3cbadd90SDavid du Colombier int type; /* and type to look up */ 43*3cbadd90SDavid du Colombier Request *req; 44*3cbadd90SDavid du Colombier RR *nsrp; /* name servers to consult */ 45*3cbadd90SDavid du Colombier 46*3cbadd90SDavid du Colombier Dest *dest; /* array of destinations */ 47*3cbadd90SDavid du Colombier Dest *curdest; /* pointer to one of them */ 48*3cbadd90SDavid du Colombier int ndest; 49*3cbadd90SDavid du Colombier 50*3cbadd90SDavid du Colombier int udpfd; /* can be shared by all udp users */ 51*3cbadd90SDavid du Colombier 52*3cbadd90SDavid du Colombier QLock tcplock; /* only one tcp call at a time per query */ 53*3cbadd90SDavid du Colombier int tcpset; 54*3cbadd90SDavid du Colombier int tcpfd; /* if Tcp, read replies from here */ 55*3cbadd90SDavid du Colombier int tcpctlfd; 56*3cbadd90SDavid du Colombier uchar tcpip[IPaddrlen]; 57*3cbadd90SDavid du Colombier 58*3cbadd90SDavid du Colombier ulong magic; 59*3cbadd90SDavid du Colombier }; 60*3cbadd90SDavid du Colombier 61*3cbadd90SDavid du Colombier /* a list of sluggardly name servers */ 62*3cbadd90SDavid du Colombier struct Sluggards { 63*3cbadd90SDavid du Colombier QLock; 64*3cbadd90SDavid du Colombier Ipaddr *head; 65*3cbadd90SDavid du Colombier Ipaddr *tail; 66*3cbadd90SDavid du Colombier }; 67*3cbadd90SDavid du Colombier 68*3cbadd90SDavid du Colombier static Sluggards slugs; 69*3cbadd90SDavid du Colombier 707dd7cddfSDavid du Colombier static RR* dnresolve1(char*, int, int, Request*, int, int); 71*3cbadd90SDavid du Colombier static int netquery(Query *, int); 72*3cbadd90SDavid du Colombier 73*3cbadd90SDavid du Colombier static Ipaddr * 74*3cbadd90SDavid du Colombier newslug(void) 75*3cbadd90SDavid du Colombier { 76*3cbadd90SDavid du Colombier return emalloc(sizeof(Ipaddr)); 77*3cbadd90SDavid du Colombier } 78*3cbadd90SDavid du Colombier 79*3cbadd90SDavid du Colombier static void 80*3cbadd90SDavid du Colombier addslug(uchar nsip[]) 81*3cbadd90SDavid du Colombier { 82*3cbadd90SDavid du Colombier Ipaddr *sp; 83*3cbadd90SDavid du Colombier static uchar zip[IPaddrlen]; 84*3cbadd90SDavid du Colombier 85*3cbadd90SDavid du Colombier if (memcmp(nsip, zip, IPaddrlen) == 0) 86*3cbadd90SDavid du Colombier return; 87*3cbadd90SDavid du Colombier 88*3cbadd90SDavid du Colombier qlock(&slugs); 89*3cbadd90SDavid du Colombier for (sp = slugs.head; sp != nil; sp = sp->next) 90*3cbadd90SDavid du Colombier if (memcmp(sp->ip, nsip, IPaddrlen) == 0) { 91*3cbadd90SDavid du Colombier qunlock(&slugs); /* already know it */ 92*3cbadd90SDavid du Colombier return; 93*3cbadd90SDavid du Colombier } 94*3cbadd90SDavid du Colombier 95*3cbadd90SDavid du Colombier if (slugs.head == nil) 96*3cbadd90SDavid du Colombier slugs.head = slugs.tail = newslug(); 97*3cbadd90SDavid du Colombier else { 98*3cbadd90SDavid du Colombier slugs.tail->next = newslug(); 99*3cbadd90SDavid du Colombier slugs.tail = slugs.tail->next; 100*3cbadd90SDavid du Colombier } 101*3cbadd90SDavid du Colombier memmove(slugs.tail->ip, nsip, IPaddrlen); 102*3cbadd90SDavid du Colombier qunlock(&slugs); 103*3cbadd90SDavid du Colombier 104*3cbadd90SDavid du Colombier dnslog("%I is a slug", nsip); 105*3cbadd90SDavid du Colombier } 106*3cbadd90SDavid du Colombier 107*3cbadd90SDavid du Colombier int 108*3cbadd90SDavid du Colombier isaslug(uchar nsip[]) 109*3cbadd90SDavid du Colombier { 110*3cbadd90SDavid du Colombier Ipaddr *sp; 111*3cbadd90SDavid du Colombier 112*3cbadd90SDavid du Colombier qlock(&slugs); 113*3cbadd90SDavid du Colombier for (sp = slugs.head; sp != nil; sp = sp->next) 114*3cbadd90SDavid du Colombier if (memcmp(sp->ip, nsip, IPaddrlen) == 0) { 115*3cbadd90SDavid du Colombier qunlock(&slugs); 116*3cbadd90SDavid du Colombier return 1; 117*3cbadd90SDavid du Colombier } 118*3cbadd90SDavid du Colombier qunlock(&slugs); 119*3cbadd90SDavid du Colombier return 0; 120*3cbadd90SDavid 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 231*3cbadd90SDavid du Colombier static void 232*3cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req) 233*3cbadd90SDavid du Colombier { 234*3cbadd90SDavid du Colombier memset(qp, 0, sizeof *qp); 235*3cbadd90SDavid du Colombier qp->udpfd = -1; 236*3cbadd90SDavid du Colombier qp->dp = dp; 237*3cbadd90SDavid du Colombier qp->type = type; 238*3cbadd90SDavid du Colombier qp->req = req; 239*3cbadd90SDavid du Colombier qp->nsrp = nil; 240*3cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; 241*3cbadd90SDavid du Colombier qp->magic = Querymagic; 242*3cbadd90SDavid du Colombier } 243*3cbadd90SDavid du Colombier 244*3cbadd90SDavid du Colombier static void 245*3cbadd90SDavid du Colombier queryck(Query *qp) 246*3cbadd90SDavid du Colombier { 247*3cbadd90SDavid du Colombier assert(qp); 248*3cbadd90SDavid du Colombier assert(qp->magic == Querymagic); 249*3cbadd90SDavid du Colombier } 250*3cbadd90SDavid du Colombier 251*3cbadd90SDavid du Colombier static void 252*3cbadd90SDavid du Colombier destinit(Dest *p) 253*3cbadd90SDavid du Colombier { 254*3cbadd90SDavid du Colombier memset(p, 0, sizeof *p); 255*3cbadd90SDavid du Colombier p->magic = Destmagic; 256*3cbadd90SDavid du Colombier } 257*3cbadd90SDavid du Colombier 258*3cbadd90SDavid du Colombier static void 259*3cbadd90SDavid du Colombier destck(Dest *p) 260*3cbadd90SDavid du Colombier { 261*3cbadd90SDavid du Colombier assert(p); 262*3cbadd90SDavid du Colombier assert(p->magic == Destmagic); 263*3cbadd90SDavid du Colombier } 264*3cbadd90SDavid du Colombier 2653e12c5d1SDavid du Colombier static RR* 2664f8f669cSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth, 2674f8f669cSDavid du Colombier int recurse) 2683e12c5d1SDavid du Colombier { 2693e12c5d1SDavid du Colombier DN *dp, *nsdp; 2707dd7cddfSDavid du Colombier RR *rp, *nsrp, *dbnsrp; 2713e12c5d1SDavid du Colombier char *cp; 272*3cbadd90SDavid du Colombier Query query; 2733e12c5d1SDavid du Colombier 2747dd7cddfSDavid du Colombier if(debug) 2754f8f669cSDavid du Colombier dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class); 2767dd7cddfSDavid du Colombier 2773e12c5d1SDavid du Colombier /* only class Cin implemented so far */ 2783e12c5d1SDavid du Colombier if(class != Cin) 2794f8f669cSDavid du Colombier return nil; 2803e12c5d1SDavid du Colombier 2813e12c5d1SDavid du Colombier dp = dnlookup(name, class, 1); 2823e12c5d1SDavid du Colombier 2837dd7cddfSDavid du Colombier /* 2847dd7cddfSDavid du Colombier * Try the cache first 2857dd7cddfSDavid du Colombier */ 2867dd7cddfSDavid du Colombier rp = rrlookup(dp, type, OKneg); 2874f8f669cSDavid du Colombier if(rp) 2887dd7cddfSDavid du Colombier if(rp->db){ 2894f8f669cSDavid du Colombier /* unauthoritative db entries are hints */ 2907dd7cddfSDavid du Colombier if(rp->auth) 2913e12c5d1SDavid du Colombier return rp; 2924f8f669cSDavid du Colombier } else 2937dd7cddfSDavid du Colombier /* cached entry must still be valid */ 2944f8f669cSDavid du Colombier if(rp->ttl > now) 2957dd7cddfSDavid du Colombier /* but Tall entries are special */ 2967dd7cddfSDavid du Colombier if(type != Tall || rp->query == Tall) 2977dd7cddfSDavid du Colombier return rp; 2984f8f669cSDavid du Colombier 2997dd7cddfSDavid du Colombier rrfreelist(rp); 3003e12c5d1SDavid du Colombier 3017dd7cddfSDavid du Colombier /* 3027dd7cddfSDavid du Colombier * try the cache for a canonical name. if found punt 3037dd7cddfSDavid du Colombier * since we'll find it during the canonical name search 3047dd7cddfSDavid du Colombier * in dnresolve(). 3057dd7cddfSDavid du Colombier */ 3067dd7cddfSDavid du Colombier if(type != Tcname){ 3077dd7cddfSDavid du Colombier rp = rrlookup(dp, Tcname, NOneg); 3087dd7cddfSDavid du Colombier rrfreelist(rp); 3093e12c5d1SDavid du Colombier if(rp) 3104f8f669cSDavid du Colombier return nil; 3113e12c5d1SDavid du Colombier } 3123e12c5d1SDavid du Colombier 313*3cbadd90SDavid du Colombier queryinit(&query, dp, type, req); 314*3cbadd90SDavid du Colombier 3153e12c5d1SDavid du Colombier /* 3164f8f669cSDavid du Colombier * if we're running as just a resolver, query our 3177dd7cddfSDavid du Colombier * designated name servers 318219b2ee8SDavid du Colombier */ 3194f8f669cSDavid du Colombier if(cfg.resolver){ 3207dd7cddfSDavid du Colombier nsrp = randomize(getdnsservers(class)); 3217dd7cddfSDavid du Colombier if(nsrp != nil) { 322*3cbadd90SDavid du Colombier query.nsrp = nsrp; 323*3cbadd90SDavid du Colombier if(netquery(&query, depth+1)){ 3247dd7cddfSDavid du Colombier rrfreelist(nsrp); 325*3cbadd90SDavid du Colombier /* prevent accidents */ 326*3cbadd90SDavid du Colombier memset(&query, 0, sizeof query); 3277dd7cddfSDavid du Colombier return rrlookup(dp, type, OKneg); 3287dd7cddfSDavid du Colombier } 3297dd7cddfSDavid du Colombier rrfreelist(nsrp); 3307dd7cddfSDavid du Colombier } 331219b2ee8SDavid du Colombier } 332219b2ee8SDavid du Colombier 333219b2ee8SDavid du Colombier /* 3343e12c5d1SDavid du Colombier * walk up the domain name looking for 3353e12c5d1SDavid du Colombier * a name server for the domain. 3363e12c5d1SDavid du Colombier */ 3373e12c5d1SDavid du Colombier for(cp = name; cp; cp = walkup(cp)){ 3387dd7cddfSDavid du Colombier /* 3397dd7cddfSDavid du Colombier * if this is a local (served by us) domain, 3407dd7cddfSDavid du Colombier * return answer 3417dd7cddfSDavid du Colombier */ 3427dd7cddfSDavid du Colombier dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0)); 3437dd7cddfSDavid du Colombier if(dbnsrp && dbnsrp->local){ 3447dd7cddfSDavid du Colombier rp = dblookup(name, class, type, 1, dbnsrp->ttl); 3457dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 346*3cbadd90SDavid du Colombier /* prevent accidents */ 347*3cbadd90SDavid du Colombier memset(&query, 0, sizeof query); 3487dd7cddfSDavid du Colombier return rp; 3497dd7cddfSDavid du Colombier } 3507dd7cddfSDavid du Colombier 3517dd7cddfSDavid du Colombier /* 3527dd7cddfSDavid du Colombier * if recursion isn't set, just accept local 3537dd7cddfSDavid du Colombier * entries 3547dd7cddfSDavid du Colombier */ 3557dd7cddfSDavid du Colombier if(recurse == Dontrecurse){ 3567dd7cddfSDavid du Colombier if(dbnsrp) 3577dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 3587dd7cddfSDavid du Colombier continue; 3597dd7cddfSDavid du Colombier } 3607dd7cddfSDavid du Colombier 3617dd7cddfSDavid du Colombier /* look for ns in cache */ 3623e12c5d1SDavid du Colombier nsdp = dnlookup(cp, class, 0); 3637dd7cddfSDavid du Colombier nsrp = nil; 3643e12c5d1SDavid du Colombier if(nsdp) 3657dd7cddfSDavid du Colombier nsrp = randomize(rrlookup(nsdp, Tns, NOneg)); 3667dd7cddfSDavid du Colombier 3677dd7cddfSDavid du Colombier /* if the entry timed out, ignore it */ 3687dd7cddfSDavid du Colombier if(nsrp && nsrp->ttl < now){ 3697dd7cddfSDavid du Colombier rrfreelist(nsrp); 3707dd7cddfSDavid du Colombier nsrp = nil; 3717dd7cddfSDavid du Colombier } 3723e12c5d1SDavid du Colombier 3733e12c5d1SDavid du Colombier if(nsrp){ 3747dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 3757dd7cddfSDavid du Colombier 3764f8f669cSDavid du Colombier /* query the name servers found in cache */ 377*3cbadd90SDavid du Colombier query.nsrp = nsrp; 378*3cbadd90SDavid du Colombier if(netquery(&query, depth+1)){ 3793e12c5d1SDavid du Colombier rrfreelist(nsrp); 380*3cbadd90SDavid du Colombier /* prevent accidents */ 381*3cbadd90SDavid du Colombier memset(&query, 0, sizeof query); 3827dd7cddfSDavid du Colombier return rrlookup(dp, type, OKneg); 3837dd7cddfSDavid du Colombier } 3847dd7cddfSDavid du Colombier rrfreelist(nsrp); 3857dd7cddfSDavid du Colombier continue; 3863e12c5d1SDavid du Colombier } 3873e12c5d1SDavid du Colombier 3887dd7cddfSDavid du Colombier /* use ns from db */ 3897dd7cddfSDavid du Colombier if(dbnsrp){ 3907dd7cddfSDavid du Colombier /* try the name servers found in db */ 391*3cbadd90SDavid du Colombier query.nsrp = dbnsrp; 392*3cbadd90SDavid du Colombier if(netquery(&query, depth+1)){ 3933e12c5d1SDavid du Colombier /* we got an answer */ 3947dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 395*3cbadd90SDavid du Colombier /* prevent accidents */ 396*3cbadd90SDavid du Colombier memset(&query, 0, sizeof query); 3977dd7cddfSDavid du Colombier return rrlookup(dp, type, NOneg); 3983e12c5d1SDavid du Colombier } 3997dd7cddfSDavid du Colombier rrfreelist(dbnsrp); 4003e12c5d1SDavid du Colombier } 4013e12c5d1SDavid du Colombier } 402*3cbadd90SDavid du Colombier memset(&query, 0, sizeof query); /* prevent accidents */ 4033e12c5d1SDavid du Colombier 4047dd7cddfSDavid du Colombier /* settle for a non-authoritative answer */ 4057dd7cddfSDavid du Colombier rp = rrlookup(dp, type, OKneg); 4067dd7cddfSDavid du Colombier if(rp) 4077dd7cddfSDavid du Colombier return rp; 4087dd7cddfSDavid du Colombier 4097dd7cddfSDavid du Colombier /* noone answered. try the database, we might have a chance. */ 4107dd7cddfSDavid du Colombier return dblookup(name, class, type, 0, 0); 4113e12c5d1SDavid du Colombier } 4123e12c5d1SDavid du Colombier 4133e12c5d1SDavid du Colombier /* 4144f8f669cSDavid du Colombier * walk a domain name one element to the right. 4154f8f669cSDavid du Colombier * return a pointer to that element. 4163e12c5d1SDavid du Colombier * in other words, return a pointer to the parent domain name. 4173e12c5d1SDavid du Colombier */ 4183e12c5d1SDavid du Colombier char* 4193e12c5d1SDavid du Colombier walkup(char *name) 4203e12c5d1SDavid du Colombier { 4213e12c5d1SDavid du Colombier char *cp; 4223e12c5d1SDavid du Colombier 4233e12c5d1SDavid du Colombier cp = strchr(name, '.'); 4243e12c5d1SDavid du Colombier if(cp) 4253e12c5d1SDavid du Colombier return cp+1; 4263e12c5d1SDavid du Colombier else if(*name) 4273e12c5d1SDavid du Colombier return ""; 4283e12c5d1SDavid du Colombier else 4293e12c5d1SDavid du Colombier return 0; 4303e12c5d1SDavid du Colombier } 4313e12c5d1SDavid du Colombier 4323e12c5d1SDavid du Colombier /* 4333e12c5d1SDavid du Colombier * Get a udpport for requests and replies. Put the port 4343e12c5d1SDavid du Colombier * into "headers" mode. 4353e12c5d1SDavid du Colombier */ 4363e12c5d1SDavid du Colombier static char *hmsg = "headers"; 4371e8349ebSDavid du Colombier static char *ohmsg = "oldheaders"; 4383e12c5d1SDavid du Colombier 439dc5a79c1SDavid du Colombier int 4404f8f669cSDavid du Colombier udpport(char *mtpt) 4413e12c5d1SDavid du Colombier { 4423e12c5d1SDavid du Colombier int fd, ctl; 4434f8f669cSDavid du Colombier char ds[64], adir[64]; 4443e12c5d1SDavid du Colombier 4453e12c5d1SDavid du Colombier /* get a udp port */ 4464f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net")); 4477dd7cddfSDavid du Colombier ctl = announce(ds, adir); 4487dd7cddfSDavid du Colombier if(ctl < 0){ 4497dd7cddfSDavid du Colombier /* warning("can't get udp port"); */ 450bd389b36SDavid du Colombier return -1; 451bd389b36SDavid du Colombier } 4523e12c5d1SDavid du Colombier 4533e12c5d1SDavid du Colombier /* turn on header style interface */ 454bd389b36SDavid du Colombier if(write(ctl, hmsg, strlen(hmsg)) , 0){ 4557dd7cddfSDavid du Colombier close(ctl); 456bd389b36SDavid du Colombier warning(hmsg); 457bd389b36SDavid du Colombier return -1; 458bd389b36SDavid du Colombier } 4591e8349ebSDavid du Colombier write(ctl, ohmsg, strlen(ohmsg)); 4603e12c5d1SDavid du Colombier 4617dd7cddfSDavid du Colombier /* grab the data file */ 4624f8f669cSDavid du Colombier snprint(ds, sizeof ds, "%s/data", adir); 4637dd7cddfSDavid du Colombier fd = open(ds, ORDWR); 4643e12c5d1SDavid du Colombier close(ctl); 4654f8f669cSDavid du Colombier if(fd < 0) 4664f8f669cSDavid du Colombier warning("can't open udp port %s: %r", ds); 4673e12c5d1SDavid du Colombier return fd; 4683e12c5d1SDavid du Colombier } 4693e12c5d1SDavid du Colombier 470d6d99297SDavid du Colombier /* generate a DNS UDP query packet */ 471dc5a79c1SDavid du Colombier int 472dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno) 4733e12c5d1SDavid du Colombier { 4743e12c5d1SDavid du Colombier DNSmsg m; 4753e12c5d1SDavid du Colombier int len; 476d9dc5dd1SDavid du Colombier OUdphdr *uh = (OUdphdr*)buf; 4773e12c5d1SDavid du Colombier 4783e12c5d1SDavid du Colombier /* stuff port number into output buffer */ 479*3cbadd90SDavid du Colombier memset(uh, 0, sizeof *uh); 4807dd7cddfSDavid du Colombier hnputs(uh->rport, 53); 4813e12c5d1SDavid du Colombier 4823e12c5d1SDavid du Colombier /* make request and convert it to output format */ 483*3cbadd90SDavid du Colombier memset(&m, 0, sizeof m); 484dc5a79c1SDavid du Colombier m.flags = flags; 4853e12c5d1SDavid du Colombier m.id = reqno; 4863e12c5d1SDavid du Colombier m.qd = rralloc(type); 4873e12c5d1SDavid du Colombier m.qd->owner = dp; 4883e12c5d1SDavid du Colombier m.qd->type = type; 489d9dc5dd1SDavid du Colombier len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp); 4907dd7cddfSDavid du Colombier rrfree(m.qd); 4913e12c5d1SDavid du Colombier return len; 4923e12c5d1SDavid du Colombier } 4933e12c5d1SDavid du Colombier 4947dd7cddfSDavid du Colombier /* for alarms in readreply */ 4953e12c5d1SDavid du Colombier static void 4963e12c5d1SDavid du Colombier ding(void *x, char *msg) 4973e12c5d1SDavid du Colombier { 4983e12c5d1SDavid du Colombier USED(x); 4993e12c5d1SDavid du Colombier if(strcmp(msg, "alarm") == 0) 5003e12c5d1SDavid du Colombier noted(NCONT); 5013e12c5d1SDavid du Colombier else 5023e12c5d1SDavid du Colombier noted(NDFLT); 5033e12c5d1SDavid du Colombier } 5047dd7cddfSDavid du Colombier 5057dd7cddfSDavid du Colombier static void 5067dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp) 5073e12c5d1SDavid du Colombier { 5087dd7cddfSDavid du Colombier rrfreelist(mp->qd); 5097dd7cddfSDavid du Colombier rrfreelist(mp->an); 5107dd7cddfSDavid du Colombier rrfreelist(mp->ns); 5117dd7cddfSDavid du Colombier rrfreelist(mp->ar); 5124f8f669cSDavid du Colombier mp->qd = mp->an = mp->ns = mp->ar = nil; 5137dd7cddfSDavid du Colombier } 5147dd7cddfSDavid du Colombier 515*3cbadd90SDavid du Colombier /* sets srcip */ 516*3cbadd90SDavid du Colombier static int 517*3cbadd90SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp, 518*3cbadd90SDavid du Colombier uchar *srcip) 519*3cbadd90SDavid du Colombier { 520*3cbadd90SDavid du Colombier int len, fd; 521*3cbadd90SDavid du Colombier uchar *reply; 522*3cbadd90SDavid du Colombier uchar lenbuf[2]; 523*3cbadd90SDavid du Colombier 524*3cbadd90SDavid du Colombier /* timed read of reply */ 525*3cbadd90SDavid du Colombier alarm((endtime - time(nil)) * 1000); 526*3cbadd90SDavid du Colombier reply = ibuf; 527*3cbadd90SDavid du Colombier if (medium == Udp) { 528*3cbadd90SDavid du Colombier len = read(qp->udpfd, ibuf, OUdphdrsize+Maxudpin); 529*3cbadd90SDavid du Colombier len -= OUdphdrsize; 530*3cbadd90SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 531*3cbadd90SDavid du Colombier reply += OUdphdrsize; 532*3cbadd90SDavid du Colombier } else { 533*3cbadd90SDavid du Colombier len = -1; /* pessimism */ 534*3cbadd90SDavid du Colombier if (!qp->tcpset) 535*3cbadd90SDavid du Colombier dnslog("readnet: tcp params not set"); 536*3cbadd90SDavid du Colombier fd = qp->tcpfd; 537*3cbadd90SDavid du Colombier if (fd <= 0) 538*3cbadd90SDavid du Colombier dnslog("readnet: %s: tcp fd unset for dest %I", 539*3cbadd90SDavid du Colombier qp->dp->name, qp->tcpip); 540*3cbadd90SDavid du Colombier else if (readn(fd, lenbuf, 2) != 2) { 541*3cbadd90SDavid du Colombier dnslog("readnet: short read of tcp size from %I", 542*3cbadd90SDavid du Colombier qp->tcpip); 543*3cbadd90SDavid du Colombier /* probably a time-out; demote the ns */ 544*3cbadd90SDavid du Colombier addslug(qp->tcpip); 545*3cbadd90SDavid du Colombier } else { 546*3cbadd90SDavid du Colombier len = lenbuf[0]<<8 | lenbuf[1]; 547*3cbadd90SDavid du Colombier if (readn(fd, ibuf, len) != len) { 548*3cbadd90SDavid du Colombier dnslog("readnet: short read of tcp data from %I", 549*3cbadd90SDavid du Colombier qp->tcpip); 550*3cbadd90SDavid du Colombier /* probably a time-out; demote the ns */ 551*3cbadd90SDavid du Colombier addslug(qp->tcpip); 552*3cbadd90SDavid du Colombier len = -1; 553*3cbadd90SDavid du Colombier } 554*3cbadd90SDavid du Colombier } 555*3cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 556*3cbadd90SDavid du Colombier } 557*3cbadd90SDavid du Colombier alarm(0); 558*3cbadd90SDavid du Colombier *replyp = reply; 559*3cbadd90SDavid du Colombier return len; 560*3cbadd90SDavid du Colombier } 561*3cbadd90SDavid du Colombier 5627dd7cddfSDavid du Colombier /* 563*3cbadd90SDavid du Colombier * read replies to a request and remember the rrs in the answer(s). 564*3cbadd90SDavid du Colombier * ignore any of the wrong type. 5654f8f669cSDavid du Colombier * wait at most until endtime. 5667dd7cddfSDavid du Colombier */ 5677dd7cddfSDavid du Colombier static int 568*3cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp, 569*3cbadd90SDavid du Colombier ulong endtime) 5707dd7cddfSDavid du Colombier { 571*3cbadd90SDavid du Colombier int len = -1, rv; 5727dd7cddfSDavid du Colombier char *err; 573*3cbadd90SDavid du Colombier uchar *reply; 574*3cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 5753e12c5d1SDavid du Colombier RR *rp; 5767dd7cddfSDavid du Colombier 5777dd7cddfSDavid du Colombier notify(ding); 5787dd7cddfSDavid du Colombier 579*3cbadd90SDavid du Colombier queryck(qp); 580*3cbadd90SDavid du Colombier rv = 0; 581*3cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 582*3cbadd90SDavid du Colombier if (time(nil) >= endtime) 583*3cbadd90SDavid du Colombier return -1; /* timed out before we started */ 5847dd7cddfSDavid du Colombier 585*3cbadd90SDavid du Colombier for (; time(nil) < endtime && 586*3cbadd90SDavid du Colombier (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0; 587*3cbadd90SDavid du Colombier freeanswers(mp)){ 5887dd7cddfSDavid du Colombier /* convert into internal format */ 589*3cbadd90SDavid du Colombier memset(mp, 0, sizeof *mp); 590*3cbadd90SDavid du Colombier err = convM2DNS(reply, len, mp, nil); 591d6d99297SDavid du Colombier if (mp->flags & Ftrunc) { 592*3cbadd90SDavid du Colombier // dnslog("readreply: %s: truncated reply, len %d from %I", 593*3cbadd90SDavid du Colombier // qp->dp->name, len, srcip); 594*3cbadd90SDavid du Colombier /* notify the caller to retry the query via tcp. */ 595*3cbadd90SDavid du Colombier return -1; 596*3cbadd90SDavid du Colombier } else if(err){ 597*3cbadd90SDavid du Colombier dnslog("readreply: %s: input err, len %d: %s: %I", 598*3cbadd90SDavid du Colombier qp->dp->name, len, err, srcip); 599*3cbadd90SDavid du Colombier free(err); 6007dd7cddfSDavid du Colombier continue; 6017dd7cddfSDavid du Colombier } 602*3cbadd90SDavid du Colombier if (err) 603*3cbadd90SDavid du Colombier free(err); 6047dd7cddfSDavid du Colombier if(debug) 605*3cbadd90SDavid du Colombier logreply(qp->req->id, srcip, mp); 6067dd7cddfSDavid du Colombier 6077dd7cddfSDavid du Colombier /* answering the right question? */ 608*3cbadd90SDavid du Colombier if(mp->id != req) 609*3cbadd90SDavid du Colombier dnslog("%d: id %d instead of %d: %I", qp->req->id, 610*3cbadd90SDavid du Colombier mp->id, req, srcip); 611*3cbadd90SDavid du Colombier else if(mp->qd == 0) 612*3cbadd90SDavid du Colombier dnslog("%d: no question RR: %I", qp->req->id, srcip); 613*3cbadd90SDavid du Colombier else if(mp->qd->owner != qp->dp) 614*3cbadd90SDavid du Colombier dnslog("%d: owner %s instead of %s: %I", qp->req->id, 615*3cbadd90SDavid du Colombier mp->qd->owner->name, qp->dp->name, srcip); 616*3cbadd90SDavid du Colombier else if(mp->qd->type != qp->type) 617*3cbadd90SDavid du Colombier dnslog("%d: qp->type %d instead of %d: %I", 618*3cbadd90SDavid du Colombier qp->req->id, mp->qd->type, qp->type, srcip); 619*3cbadd90SDavid du Colombier else { 6207dd7cddfSDavid du Colombier /* remember what request this is in answer to */ 6217dd7cddfSDavid du Colombier for(rp = mp->an; rp; rp = rp->next) 622*3cbadd90SDavid du Colombier rp->query = qp->type; 623*3cbadd90SDavid du Colombier return rv; 6247dd7cddfSDavid du Colombier } 6257dd7cddfSDavid du Colombier } 626*3cbadd90SDavid du Colombier if (time(nil) >= endtime) 627*3cbadd90SDavid du Colombier addslug(srcip); 628*3cbadd90SDavid du Colombier else 629*3cbadd90SDavid du Colombier dnslog("readreply: %s: %I read error or eof (returned %d)", 630*3cbadd90SDavid du Colombier qp->dp->name, srcip, len); 631*3cbadd90SDavid du Colombier return -1; 632*3cbadd90SDavid du Colombier } 6337dd7cddfSDavid du Colombier 6347dd7cddfSDavid du Colombier /* 6357dd7cddfSDavid du Colombier * return non-0 if first list includes second list 6367dd7cddfSDavid du Colombier */ 6377dd7cddfSDavid du Colombier int 6387dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2) 6397dd7cddfSDavid du Colombier { 6407dd7cddfSDavid du Colombier RR *trp1, *trp2; 6417dd7cddfSDavid du Colombier 6427dd7cddfSDavid du Colombier for(trp2 = rp2; trp2; trp2 = trp2->next){ 643*3cbadd90SDavid du Colombier for(trp1 = rp1; trp1; trp1 = trp1->next) 6447dd7cddfSDavid du Colombier if(trp1->type == trp2->type) 6457dd7cddfSDavid du Colombier if(trp1->host == trp2->host) 6467dd7cddfSDavid du Colombier if(trp1->owner == trp2->owner) 6477dd7cddfSDavid du Colombier break; 6484f8f669cSDavid du Colombier if(trp1 == nil) 6497dd7cddfSDavid du Colombier return 0; 6507dd7cddfSDavid du Colombier } 6517dd7cddfSDavid du Colombier return 1; 6527dd7cddfSDavid du Colombier } 6537dd7cddfSDavid du Colombier 6547dd7cddfSDavid du Colombier 6556b6b9ac8SDavid du Colombier /* 6566b6b9ac8SDavid du Colombier * return multicast version if any 6576b6b9ac8SDavid du Colombier */ 6586b6b9ac8SDavid du Colombier int 6596b6b9ac8SDavid du Colombier ipisbm(uchar *ip) 6606b6b9ac8SDavid du Colombier { 6616b6b9ac8SDavid du Colombier if(isv4(ip)){ 6624f8f669cSDavid du Colombier if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 || 6634f8f669cSDavid du Colombier ipcmp(ip, IPv4bcast) == 0) 6646b6b9ac8SDavid du Colombier return 4; 6654f8f669cSDavid du Colombier } else 6666b6b9ac8SDavid du Colombier if(ip[0] == 0xff) 6676b6b9ac8SDavid du Colombier return 6; 66828a8a86bSDavid du Colombier return 0; 66928a8a86bSDavid du Colombier } 67028a8a86bSDavid du Colombier 6717dd7cddfSDavid du Colombier /* 6727dd7cddfSDavid du Colombier * Get next server address 6737dd7cddfSDavid du Colombier */ 6747dd7cddfSDavid du Colombier static int 675*3cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth) 6767dd7cddfSDavid du Colombier { 6777dd7cddfSDavid du Colombier RR *rp, *arp, *trp; 6787dd7cddfSDavid du Colombier Dest *cur; 6797dd7cddfSDavid du Colombier 6807dd7cddfSDavid du Colombier if(nd >= Maxdest) 6817dd7cddfSDavid du Colombier return 0; 6827dd7cddfSDavid du Colombier 6837dd7cddfSDavid du Colombier /* 6847dd7cddfSDavid du Colombier * look for a server whose address we already know. 6857dd7cddfSDavid du Colombier * if we find one, mark it so we ignore this on 6867dd7cddfSDavid du Colombier * subsequent passes. 6877dd7cddfSDavid du Colombier */ 6887dd7cddfSDavid du Colombier arp = 0; 689*3cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 69034f77ae3SDavid du Colombier assert(rp->magic == RRmagic); 6917dd7cddfSDavid du Colombier if(rp->marker) 6927dd7cddfSDavid du Colombier continue; 6937dd7cddfSDavid du Colombier arp = rrlookup(rp->host, Ta, NOneg); 6947dd7cddfSDavid du Colombier if(arp){ 6957dd7cddfSDavid du Colombier rp->marker = 1; 6967dd7cddfSDavid du Colombier break; 6977dd7cddfSDavid du Colombier } 6987dd7cddfSDavid du Colombier arp = dblookup(rp->host->name, Cin, Ta, 0, 0); 6997dd7cddfSDavid du Colombier if(arp){ 7007dd7cddfSDavid du Colombier rp->marker = 1; 7017dd7cddfSDavid du Colombier break; 7027dd7cddfSDavid du Colombier } 7037dd7cddfSDavid du Colombier } 7047dd7cddfSDavid du Colombier 7057dd7cddfSDavid du Colombier /* 7067dd7cddfSDavid du Colombier * if the cache and database lookup didn't find any new 7077dd7cddfSDavid du Colombier * server addresses, try resolving one via the network. 7087dd7cddfSDavid du Colombier * Mark any we try to resolve so we don't try a second time. 7097dd7cddfSDavid du Colombier */ 7104f8f669cSDavid du Colombier if(arp == 0) 711*3cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next){ 7127dd7cddfSDavid du Colombier if(rp->marker) 7137dd7cddfSDavid du Colombier continue; 7147dd7cddfSDavid du Colombier rp->marker = 1; 7157dd7cddfSDavid du Colombier 7167dd7cddfSDavid du Colombier /* 7177dd7cddfSDavid du Colombier * avoid loops looking up a server under itself 7187dd7cddfSDavid du Colombier */ 7197dd7cddfSDavid du Colombier if(subsume(rp->owner->name, rp->host->name)) 7207dd7cddfSDavid du Colombier continue; 7217dd7cddfSDavid du Colombier 722*3cbadd90SDavid du Colombier arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0, 7234f8f669cSDavid du Colombier depth+1, Recurse, 1, 0); 7247dd7cddfSDavid du Colombier rrfreelist(rrremneg(&arp)); 7257dd7cddfSDavid du Colombier if(arp) 7267dd7cddfSDavid du Colombier break; 7277dd7cddfSDavid du Colombier } 7287dd7cddfSDavid du Colombier 7297dd7cddfSDavid du Colombier /* use any addresses that we found */ 730*3cbadd90SDavid du Colombier for(trp = arp; trp && nd < Maxdest; trp = trp->next){ 731*3cbadd90SDavid du Colombier cur = &qp->dest[nd]; 7327dd7cddfSDavid du Colombier parseip(cur->a, trp->ip->name); 7334f8f669cSDavid du Colombier /* 7344f8f669cSDavid du Colombier * straddling servers can reject all nameservers if they are all 7354f8f669cSDavid du Colombier * inside, so be sure to list at least one outside ns at 7364f8f669cSDavid du Colombier * the end of the ns list in /lib/ndb for `dom='. 7374f8f669cSDavid du Colombier */ 7384f8f669cSDavid du Colombier if (ipisbm(cur->a) || 739*3cbadd90SDavid du Colombier cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a)) 7406b6b9ac8SDavid du Colombier continue; 7417dd7cddfSDavid du Colombier cur->nx = 0; 7427dd7cddfSDavid du Colombier cur->s = trp->owner; 7437dd7cddfSDavid du Colombier cur->code = Rtimeout; 7446b6b9ac8SDavid du Colombier nd++; 7457dd7cddfSDavid du Colombier } 7467dd7cddfSDavid du Colombier rrfreelist(arp); 7477dd7cddfSDavid du Colombier return nd; 7487dd7cddfSDavid du Colombier } 7497dd7cddfSDavid du Colombier 7507dd7cddfSDavid du Colombier /* 7517dd7cddfSDavid du Colombier * cache negative responses 7527dd7cddfSDavid du Colombier */ 7537dd7cddfSDavid du Colombier static void 7547dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr) 7557dd7cddfSDavid du Colombier { 7567dd7cddfSDavid du Colombier RR *rp; 7577dd7cddfSDavid du Colombier DN *soaowner; 7589a747e4fSDavid du Colombier ulong ttl; 7597dd7cddfSDavid du Colombier 7604f8f669cSDavid du Colombier /* no cache time specified, don't make anything up */ 7617dd7cddfSDavid du Colombier if(soarr != nil){ 7627dd7cddfSDavid du Colombier if(soarr->next != nil){ 7637dd7cddfSDavid du Colombier rrfreelist(soarr->next); 7647dd7cddfSDavid du Colombier soarr->next = nil; 7657dd7cddfSDavid du Colombier } 7667dd7cddfSDavid du Colombier soaowner = soarr->owner; 7677dd7cddfSDavid du Colombier } else 7687dd7cddfSDavid du Colombier soaowner = nil; 7697dd7cddfSDavid du Colombier 7709a747e4fSDavid du Colombier /* the attach can cause soarr to be freed so mine it now */ 7719a747e4fSDavid du Colombier if(soarr != nil && soarr->soa != nil) 7729a747e4fSDavid du Colombier ttl = soarr->soa->minttl+now; 7739a747e4fSDavid du Colombier else 7749a747e4fSDavid du Colombier ttl = 5*Min; 7759a747e4fSDavid du Colombier 7767dd7cddfSDavid du Colombier /* add soa and negative RR to the database */ 7777dd7cddfSDavid du Colombier rrattach(soarr, 1); 7787dd7cddfSDavid du Colombier 7797dd7cddfSDavid du Colombier rp = rralloc(type); 7807dd7cddfSDavid du Colombier rp->owner = dp; 7817dd7cddfSDavid du Colombier rp->negative = 1; 7827dd7cddfSDavid du Colombier rp->negsoaowner = soaowner; 7837dd7cddfSDavid du Colombier rp->negrcode = rcode; 7849a747e4fSDavid du Colombier rp->ttl = ttl; 7857dd7cddfSDavid du Colombier rrattach(rp, 1); 7867dd7cddfSDavid du Colombier } 7877dd7cddfSDavid du Colombier 7884f8f669cSDavid du Colombier static int 7894f8f669cSDavid du Colombier setdestoutns(Dest *p, int n) 7904f8f669cSDavid du Colombier { 7914f8f669cSDavid du Colombier uchar *outns = outsidens(n); 7924f8f669cSDavid du Colombier 793*3cbadd90SDavid du Colombier destck(p); 794*3cbadd90SDavid du Colombier destinit(p); 7954f8f669cSDavid du Colombier if (outns == nil) { 7964f8f669cSDavid du Colombier if (n == 0) 7974f8f669cSDavid du Colombier dnslog("[%d] no outside-ns in ndb", getpid()); 7984f8f669cSDavid du Colombier return -1; 7994f8f669cSDavid du Colombier } 8004f8f669cSDavid du Colombier memmove(p->a, outns, sizeof p->a); 8014f8f669cSDavid du Colombier p->s = dnlookup("outside-ns-ips", Cin, 1); 8024f8f669cSDavid du Colombier return 0; 8034f8f669cSDavid du Colombier } 8044f8f669cSDavid du Colombier 8057dd7cddfSDavid du Colombier /* 806*3cbadd90SDavid du Colombier * issue query via UDP or TCP as appropriate. 807*3cbadd90SDavid du Colombier * for TCP, returns with qp->tcpip set from udppkt header. 8087dd7cddfSDavid du Colombier */ 8097dd7cddfSDavid du Colombier static int 810*3cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len) 8117dd7cddfSDavid du Colombier { 812*3cbadd90SDavid du Colombier int rv = -1; 813*3cbadd90SDavid du Colombier char *domain; 814*3cbadd90SDavid du Colombier char conndir[40]; 815*3cbadd90SDavid du Colombier NetConnInfo *nci; 8164f8f669cSDavid du Colombier 817*3cbadd90SDavid du Colombier queryck(qp); 818*3cbadd90SDavid du Colombier switch (medium) { 819*3cbadd90SDavid du Colombier case Udp: 820*3cbadd90SDavid du Colombier if(write(qp->udpfd, udppkt, len+OUdphdrsize) != len+OUdphdrsize) 821*3cbadd90SDavid du Colombier warning("sending udp msg %r"); 822*3cbadd90SDavid du Colombier rv = 0; 823*3cbadd90SDavid du Colombier break; 824*3cbadd90SDavid du Colombier case Tcp: 825*3cbadd90SDavid du Colombier /* send via TCP & keep fd around for reply */ 826*3cbadd90SDavid du Colombier domain = smprint("%I", udppkt); 827*3cbadd90SDavid du Colombier alarm(10*1000); 828*3cbadd90SDavid du Colombier qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil, 829*3cbadd90SDavid du Colombier conndir, &qp->tcpctlfd); 830*3cbadd90SDavid du Colombier alarm(0); 831*3cbadd90SDavid du Colombier if (qp->tcpfd < 0) { 832*3cbadd90SDavid du Colombier dnslog("can't dial tcp!%s!dns: %r", domain); 833*3cbadd90SDavid du Colombier addslug(udppkt); 834*3cbadd90SDavid du Colombier } else { 835*3cbadd90SDavid du Colombier uchar belen[2]; 8363e12c5d1SDavid du Colombier 837*3cbadd90SDavid du Colombier nci = getnetconninfo(conndir, qp->tcpfd); 838*3cbadd90SDavid du Colombier if (nci) { 839*3cbadd90SDavid du Colombier parseip(qp->tcpip, nci->rsys); 840*3cbadd90SDavid du Colombier freenetconninfo(nci); 841*3cbadd90SDavid du Colombier } else 842*3cbadd90SDavid du Colombier dnslog("mydnsquery: getnetconninfo failed"); 843*3cbadd90SDavid du Colombier qp->tcpset = 1; 8443e12c5d1SDavid du Colombier 845*3cbadd90SDavid du Colombier belen[0] = len >> 8; 846*3cbadd90SDavid du Colombier belen[1] = len; 847*3cbadd90SDavid du Colombier if (write(qp->tcpfd, belen, 2) != 2 || 848*3cbadd90SDavid du Colombier write(qp->tcpfd, udppkt + OUdphdrsize, len) != len) 849*3cbadd90SDavid du Colombier warning("sending tcp msg %r"); 850*3cbadd90SDavid du Colombier } 851*3cbadd90SDavid du Colombier free(domain); 852*3cbadd90SDavid du Colombier break; 853*3cbadd90SDavid du Colombier default: 854*3cbadd90SDavid du Colombier sysfatal("mydnsquery: bad medium"); 855*3cbadd90SDavid du Colombier } 856*3cbadd90SDavid du Colombier return rv; 857*3cbadd90SDavid du Colombier } 8587dd7cddfSDavid du Colombier 8593e12c5d1SDavid du Colombier /* 860*3cbadd90SDavid du Colombier * send query to all UDP destinations or one TCP destination, 861*3cbadd90SDavid du Colombier * taken from obuf (udp packet) header 8623e12c5d1SDavid du Colombier */ 863*3cbadd90SDavid du Colombier static int 864*3cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len) 865*3cbadd90SDavid du Colombier { 866*3cbadd90SDavid du Colombier int j, n; 867*3cbadd90SDavid du Colombier char buf[32]; 868*3cbadd90SDavid du Colombier Dest *p; 8693e12c5d1SDavid du Colombier 870*3cbadd90SDavid du Colombier queryck(qp); 871*3cbadd90SDavid du Colombier // dnslog("xmitquery xmit loop: now %ld aborttime %ld", time(nil), 872*3cbadd90SDavid du Colombier // qp->req->aborttime); 873*3cbadd90SDavid du Colombier if(time(nil) >= qp->req->aborttime) 874*3cbadd90SDavid du Colombier return -1; 875*3cbadd90SDavid du Colombier 876*3cbadd90SDavid du Colombier /* 877*3cbadd90SDavid du Colombier * get a nameserver address if we need one. 878*3cbadd90SDavid du Colombier * serveraddrs populates qp->dest. 879*3cbadd90SDavid du Colombier */ 880*3cbadd90SDavid du Colombier p = qp->dest; 881*3cbadd90SDavid du Colombier destck(p); 882*3cbadd90SDavid du Colombier if (qp->ndest < 0 || qp->ndest > Maxdest) 883*3cbadd90SDavid du Colombier dnslog("qp->ndest %d out of range", qp->ndest); 884*3cbadd90SDavid du Colombier if (qp->ndest > qp->curdest - p) 885*3cbadd90SDavid du Colombier qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)]; 886*3cbadd90SDavid du Colombier destck(qp->curdest); 8877dd7cddfSDavid du Colombier 8887dd7cddfSDavid du Colombier /* no servers, punt */ 889*3cbadd90SDavid du Colombier if (qp->curdest == qp->dest) 8904f8f669cSDavid du Colombier if (cfg.straddle && cfg.inside) { 891*3cbadd90SDavid du Colombier /* get ips of "outside-ns-ips" */ 892*3cbadd90SDavid du Colombier p = qp->curdest = qp->dest; 893*3cbadd90SDavid du Colombier for(n = 0; n < Maxdest; n++, qp->curdest++) 894*3cbadd90SDavid du Colombier if (setdestoutns(qp->curdest, n) < 0) 8957dd7cddfSDavid du Colombier break; 8964f8f669cSDavid du Colombier } else { 897*3cbadd90SDavid du Colombier dnslog("xmitquery: %s: no nameservers", qp->dp->name); 898*3cbadd90SDavid du Colombier return -1; 8994f8f669cSDavid du Colombier } 9007dd7cddfSDavid du Colombier 901*3cbadd90SDavid du Colombier /* send to first 'qp->ndest' destinations */ 9027dd7cddfSDavid du Colombier j = 0; 903*3cbadd90SDavid du Colombier if (medium == Tcp) { 904*3cbadd90SDavid du Colombier j++; 905*3cbadd90SDavid du Colombier queryck(qp); 906*3cbadd90SDavid du Colombier assert(qp->dp); 907*3cbadd90SDavid du Colombier procsetname("tcp %sside query for %s %s", (inns? "in": "out"), 908*3cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 909*3cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */ 910*3cbadd90SDavid du Colombier if(debug) 911*3cbadd90SDavid du Colombier logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name, 912*3cbadd90SDavid du Colombier qp->type); 913*3cbadd90SDavid du Colombier } else 914*3cbadd90SDavid du Colombier for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){ 9157dd7cddfSDavid du Colombier /* skip destinations we've finished with */ 9167dd7cddfSDavid du Colombier if(p->nx >= Maxtrans) 9177dd7cddfSDavid du Colombier continue; 9187dd7cddfSDavid du Colombier 9197dd7cddfSDavid du Colombier j++; 9207dd7cddfSDavid du Colombier 9217dd7cddfSDavid du Colombier /* exponential backoff of requests */ 922*3cbadd90SDavid du Colombier if((1<<p->nx) > qp->ndest) 9237dd7cddfSDavid du Colombier continue; 9247dd7cddfSDavid du Colombier 925*3cbadd90SDavid du Colombier procsetname("udp %sside query to %I/%s %s %s", 926*3cbadd90SDavid du Colombier (inns? "in": "out"), p->a, p->s->name, 927*3cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 928219b2ee8SDavid du Colombier if(debug) 929*3cbadd90SDavid du Colombier logsend(qp->req->id, depth, p->a, p->s->name, 930*3cbadd90SDavid du Colombier qp->dp->name, qp->type); 9314f8f669cSDavid du Colombier 932*3cbadd90SDavid du Colombier /* fill in UDP destination addr & send it */ 933*3cbadd90SDavid du Colombier memmove(obuf, p->a, sizeof p->a); 934*3cbadd90SDavid du Colombier mydnsquery(qp, medium, obuf, len); 9357dd7cddfSDavid du Colombier p->nx++; 9363e12c5d1SDavid du Colombier } 937*3cbadd90SDavid du Colombier if(j == 0) { 938*3cbadd90SDavid du Colombier // dnslog("xmitquery: %s: no destinations left", qp->dp->name); 939*3cbadd90SDavid du Colombier return -1; 940*3cbadd90SDavid du Colombier } 941*3cbadd90SDavid du Colombier return 0; 942*3cbadd90SDavid du Colombier } 9433e12c5d1SDavid du Colombier 944*3cbadd90SDavid du Colombier static int 945*3cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p) 946*3cbadd90SDavid du Colombier { 947*3cbadd90SDavid du Colombier int rv; 948*3cbadd90SDavid du Colombier char buf[32]; 949*3cbadd90SDavid du Colombier DN *ndp; 950*3cbadd90SDavid du Colombier Query nquery; 951*3cbadd90SDavid du Colombier RR *tp, *soarr; 9527dd7cddfSDavid du Colombier 95359cc4ca5SDavid du Colombier /* ignore any error replies */ 954*3cbadd90SDavid du Colombier if((mp->flags & Rmask) == Rserver){ 955*3cbadd90SDavid du Colombier rrfreelist(mp->qd); 956*3cbadd90SDavid du Colombier rrfreelist(mp->an); 957*3cbadd90SDavid du Colombier rrfreelist(mp->ar); 958*3cbadd90SDavid du Colombier rrfreelist(mp->ns); 959*3cbadd90SDavid du Colombier if(p != qp->curdest) 9607dd7cddfSDavid du Colombier p->code = Rserver; 961*3cbadd90SDavid du Colombier return -1; 9623e12c5d1SDavid du Colombier } 9633e12c5d1SDavid du Colombier 96459cc4ca5SDavid du Colombier /* ignore any bad delegations */ 965*3cbadd90SDavid du Colombier if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){ 966*3cbadd90SDavid du Colombier rrfreelist(mp->ns); 967*3cbadd90SDavid du Colombier mp->ns = nil; 968*3cbadd90SDavid du Colombier if(mp->an == nil){ 969*3cbadd90SDavid du Colombier rrfreelist(mp->qd); 970*3cbadd90SDavid du Colombier rrfreelist(mp->ar); 971*3cbadd90SDavid du Colombier if(p != qp->curdest) 97259cc4ca5SDavid du Colombier p->code = Rserver; 973*3cbadd90SDavid du Colombier return -1; 97459cc4ca5SDavid du Colombier } 97559cc4ca5SDavid du Colombier } 97659cc4ca5SDavid du Colombier 9777dd7cddfSDavid du Colombier /* remove any soa's from the authority section */ 978*3cbadd90SDavid du Colombier soarr = rrremtype(&mp->ns, Tsoa); 9797dd7cddfSDavid du Colombier 9803e12c5d1SDavid du Colombier /* incorporate answers */ 981*3cbadd90SDavid du Colombier if(mp->an) 982*3cbadd90SDavid du Colombier rrattach(mp->an, (mp->flags & Fauth) != 0); 983*3cbadd90SDavid du Colombier if(mp->ar) 984*3cbadd90SDavid du Colombier rrattach(mp->ar, 0); 985*3cbadd90SDavid du Colombier if(mp->ns){ 986*3cbadd90SDavid du Colombier ndp = mp->ns->owner; 987*3cbadd90SDavid du Colombier rrattach(mp->ns, 0); 9887dd7cddfSDavid du Colombier } else 9894f8f669cSDavid du Colombier ndp = nil; 9907dd7cddfSDavid du Colombier 9917dd7cddfSDavid du Colombier /* free the question */ 992*3cbadd90SDavid du Colombier if(mp->qd) 993*3cbadd90SDavid du Colombier rrfreelist(mp->qd); 9943e12c5d1SDavid du Colombier 9953e12c5d1SDavid du Colombier /* 9963e12c5d1SDavid du Colombier * Any reply from an authoritative server, 9973e12c5d1SDavid du Colombier * or a positive reply terminates the search 9983e12c5d1SDavid du Colombier */ 999*3cbadd90SDavid du Colombier if(mp->an != nil || (mp->flags & Fauth)){ 1000*3cbadd90SDavid du Colombier if(mp->an == nil && (mp->flags & Rmask) == Rname) 1001*3cbadd90SDavid du Colombier qp->dp->respcode = Rname; 10027dd7cddfSDavid du Colombier else 1003*3cbadd90SDavid du Colombier qp->dp->respcode = 0; 10047dd7cddfSDavid du Colombier 10057dd7cddfSDavid du Colombier /* 10067dd7cddfSDavid du Colombier * cache any negative responses, free soarr 10077dd7cddfSDavid du Colombier */ 1008*3cbadd90SDavid du Colombier if((mp->flags & Fauth) && mp->an == nil) 1009*3cbadd90SDavid du Colombier cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr); 10107dd7cddfSDavid du Colombier else 10117dd7cddfSDavid du Colombier rrfreelist(soarr); 10123e12c5d1SDavid du Colombier return 1; 10133e12c5d1SDavid du Colombier } 10147dd7cddfSDavid du Colombier rrfreelist(soarr); 10153e12c5d1SDavid du Colombier 10163e12c5d1SDavid du Colombier /* 10174f8f669cSDavid du Colombier * if we've been given better name servers, 10184f8f669cSDavid du Colombier * recurse. we're called from udpquery, called from 1019*3cbadd90SDavid du Colombier * netquery, which current holds qp->dp->querylck, 10204f8f669cSDavid du Colombier * so release it now and acquire it upon return. 10213e12c5d1SDavid du Colombier */ 1022*3cbadd90SDavid du Colombier if(!mp->ns) 1023*3cbadd90SDavid du Colombier return 0; 10247dd7cddfSDavid du Colombier tp = rrlookup(ndp, Tns, NOneg); 1025*3cbadd90SDavid du Colombier if(contains(qp->nsrp, tp)){ 10267dd7cddfSDavid du Colombier rrfreelist(tp); 1027*3cbadd90SDavid du Colombier return 0; 10283e12c5d1SDavid du Colombier } 1029*3cbadd90SDavid du Colombier procsetname("recursive query for %s %s", qp->dp->name, 1030*3cbadd90SDavid du Colombier rrname(qp->type, buf, sizeof buf)); 1031*3cbadd90SDavid du Colombier qunlock(&qp->dp->querylck); 1032*3cbadd90SDavid du Colombier 1033*3cbadd90SDavid du Colombier queryinit(&nquery, qp->dp, qp->type, qp->req); 1034*3cbadd90SDavid du Colombier nquery.nsrp = tp; 1035*3cbadd90SDavid du Colombier rv = netquery(&nquery, depth+1); 1036*3cbadd90SDavid du Colombier 1037*3cbadd90SDavid du Colombier qlock(&qp->dp->querylck); 1038*3cbadd90SDavid du Colombier rrfreelist(tp); 1039*3cbadd90SDavid du Colombier memset(&nquery, 0, sizeof nquery); /* prevent accidents */ 1040*3cbadd90SDavid du Colombier return rv; 1041*3cbadd90SDavid du Colombier } 1042*3cbadd90SDavid du Colombier 1043*3cbadd90SDavid du Colombier /* 1044*3cbadd90SDavid du Colombier * send a query via tcp to a single address (from ibuf's udp header) 1045*3cbadd90SDavid du Colombier * and read the answer(s) into mp->an. 1046*3cbadd90SDavid du Colombier */ 1047*3cbadd90SDavid du Colombier static int 1048*3cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len, 1049*3cbadd90SDavid du Colombier int waitsecs, int inns, ushort req) 1050*3cbadd90SDavid du Colombier { 1051*3cbadd90SDavid du Colombier ulong endtime; 1052*3cbadd90SDavid du Colombier 1053*3cbadd90SDavid du Colombier endtime = time(nil) + waitsecs; 1054*3cbadd90SDavid du Colombier if(endtime > qp->req->aborttime) 1055*3cbadd90SDavid du Colombier endtime = qp->req->aborttime; 1056*3cbadd90SDavid du Colombier 1057*3cbadd90SDavid du Colombier qlock(&qp->tcplock); 1058*3cbadd90SDavid du Colombier memmove(obuf, ibuf, IPaddrlen); /* send back to respondent */ 1059*3cbadd90SDavid du Colombier /* sets qp->tcpip from obuf's udp header */ 1060*3cbadd90SDavid du Colombier if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0) { 1061*3cbadd90SDavid du Colombier qunlock(&qp->tcplock); 1062*3cbadd90SDavid du Colombier return -1; 1063*3cbadd90SDavid du Colombier } 1064*3cbadd90SDavid du Colombier dnslog("%s: udp reply truncated; retrying query via tcp to %I", 1065*3cbadd90SDavid du Colombier qp->dp->name, qp->tcpip); 1066*3cbadd90SDavid du Colombier if (readreply(qp, Tcp, req, ibuf, mp, endtime) < 0) { 1067*3cbadd90SDavid du Colombier qunlock(&qp->tcplock); 1068*3cbadd90SDavid du Colombier return -1; 1069*3cbadd90SDavid du Colombier } 1070*3cbadd90SDavid du Colombier if (qp->tcpfd > 0) { 1071*3cbadd90SDavid du Colombier hangup(qp->tcpctlfd); 1072*3cbadd90SDavid du Colombier close(qp->tcpctlfd); 1073*3cbadd90SDavid du Colombier close(qp->tcpfd); 1074*3cbadd90SDavid du Colombier } 1075*3cbadd90SDavid du Colombier qp->tcpfd = qp->tcpctlfd = -1; 1076*3cbadd90SDavid du Colombier qunlock(&qp->tcplock); 1077*3cbadd90SDavid du Colombier 1078*3cbadd90SDavid du Colombier // dnslog("%s: %s answer by tcp", qp->dp->name, 1079*3cbadd90SDavid du Colombier // (mp->an? "got": "didn't get")); 1080*3cbadd90SDavid du Colombier return 0; 1081*3cbadd90SDavid du Colombier } 1082*3cbadd90SDavid du Colombier 1083*3cbadd90SDavid du Colombier /* 1084*3cbadd90SDavid du Colombier * query name servers. If the name server returns a pointer to another 1085*3cbadd90SDavid du Colombier * name server, recurse. 1086*3cbadd90SDavid du Colombier */ 1087*3cbadd90SDavid du Colombier static int 1088*3cbadd90SDavid du Colombier netquery1(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns) 1089*3cbadd90SDavid du Colombier { 1090*3cbadd90SDavid du Colombier int ndest, len, replywaits, rv; 1091*3cbadd90SDavid du Colombier ushort req; 1092*3cbadd90SDavid du Colombier ulong endtime; 1093*3cbadd90SDavid du Colombier char buf[12]; 1094*3cbadd90SDavid du Colombier uchar srcip[IPaddrlen]; 1095*3cbadd90SDavid du Colombier DNSmsg m; 1096*3cbadd90SDavid du Colombier Dest *p, *np; 1097*3cbadd90SDavid du Colombier Dest dest[Maxdest]; 1098*3cbadd90SDavid du Colombier 1099*3cbadd90SDavid du Colombier /* pack request into a udp message */ 1100*3cbadd90SDavid du Colombier req = rand(); 1101*3cbadd90SDavid du Colombier len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req); 1102*3cbadd90SDavid du Colombier 1103*3cbadd90SDavid du Colombier /* no server addresses yet */ 1104*3cbadd90SDavid du Colombier queryck(qp); 1105*3cbadd90SDavid du Colombier for (p = dest; p < dest + nelem(dest); p++) 1106*3cbadd90SDavid du Colombier destinit(p); 1107*3cbadd90SDavid du Colombier qp->curdest = qp->dest = dest; 1108*3cbadd90SDavid du Colombier 1109*3cbadd90SDavid du Colombier /* 1110*3cbadd90SDavid du Colombier * transmit udp requests and wait for answers. 1111*3cbadd90SDavid du Colombier * at most Maxtrans attempts to each address. 1112*3cbadd90SDavid du Colombier * each cycle send one more message than the previous. 1113*3cbadd90SDavid du Colombier * retry a query via tcp if its response is truncated. 1114*3cbadd90SDavid du Colombier */ 1115*3cbadd90SDavid du Colombier for(ndest = 1; ndest < Maxdest; ndest++){ 1116*3cbadd90SDavid du Colombier qp->ndest = ndest; 1117*3cbadd90SDavid du Colombier qp->tcpset = 0; 1118*3cbadd90SDavid du Colombier if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0) 1119*3cbadd90SDavid du Colombier break; 1120*3cbadd90SDavid du Colombier 1121*3cbadd90SDavid du Colombier endtime = time(nil) + waitsecs; 1122*3cbadd90SDavid du Colombier if(endtime > qp->req->aborttime) 1123*3cbadd90SDavid du Colombier endtime = qp->req->aborttime; 1124*3cbadd90SDavid du Colombier 1125*3cbadd90SDavid du Colombier for(replywaits = 0; replywaits < ndest; replywaits++){ 1126*3cbadd90SDavid du Colombier procsetname("reading %sside reply from %s%I for %s %s", 1127*3cbadd90SDavid du Colombier (inns? "in": "out"), 1128*3cbadd90SDavid du Colombier (isaslug(qp->tcpip)? "sluggard ": ""), obuf, 1129*3cbadd90SDavid du Colombier qp->dp->name, rrname(qp->type, buf, sizeof buf)); 1130*3cbadd90SDavid du Colombier 1131*3cbadd90SDavid du Colombier /* read udp answer */ 1132*3cbadd90SDavid du Colombier if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0) 1133*3cbadd90SDavid du Colombier memmove(srcip, ibuf, IPaddrlen); 1134*3cbadd90SDavid du Colombier else if (!(m.flags & Ftrunc)) { 1135*3cbadd90SDavid du Colombier addslug(ibuf); 1136*3cbadd90SDavid du Colombier break; /* timed out on this dest */ 1137*3cbadd90SDavid du Colombier } else { 1138*3cbadd90SDavid du Colombier /* whoops, it was truncated! ask again via tcp */ 1139*3cbadd90SDavid du Colombier rv = tcpquery(qp, &m, depth, ibuf, obuf, len, 1140*3cbadd90SDavid du Colombier waitsecs, inns, req); 1141*3cbadd90SDavid du Colombier if (rv < 0) 1142*3cbadd90SDavid du Colombier break; /* failed via tcp too */ 1143*3cbadd90SDavid du Colombier memmove(srcip, qp->tcpip, IPaddrlen); 1144*3cbadd90SDavid du Colombier } 1145*3cbadd90SDavid du Colombier 1146*3cbadd90SDavid du Colombier /* find responder */ 1147*3cbadd90SDavid du Colombier // dnslog("netquery1 got reply from %I", srcip); 1148*3cbadd90SDavid du Colombier for(p = qp->dest; p < qp->curdest; p++) 1149*3cbadd90SDavid du Colombier if(memcmp(p->a, srcip, sizeof p->a) == 0) 1150*3cbadd90SDavid du Colombier break; 1151*3cbadd90SDavid du Colombier 1152*3cbadd90SDavid du Colombier /* remove all addrs of responding server from list */ 1153*3cbadd90SDavid du Colombier for(np = qp->dest; np < qp->curdest; np++) 1154*3cbadd90SDavid du Colombier if(np->s == p->s) 1155*3cbadd90SDavid du Colombier p->nx = Maxtrans; 1156*3cbadd90SDavid du Colombier 1157*3cbadd90SDavid du Colombier rv = procansw(qp, &m, srcip, depth, p); 1158*3cbadd90SDavid du Colombier if (rv > 0) 1159*3cbadd90SDavid du Colombier return rv; 11603e12c5d1SDavid du Colombier } 11613e12c5d1SDavid du Colombier } 11627dd7cddfSDavid du Colombier 11634f8f669cSDavid du Colombier /* if all servers returned failure, propagate it */ 1164*3cbadd90SDavid du Colombier qp->dp->respcode = Rserver; 1165*3cbadd90SDavid du Colombier for(p = dest; p < qp->curdest; p++) { 1166*3cbadd90SDavid du Colombier destck(p); 11677dd7cddfSDavid du Colombier if(p->code != Rserver) 1168*3cbadd90SDavid du Colombier qp->dp->respcode = 0; 1169*3cbadd90SDavid du Colombier p->magic = 0; /* prevent accidents */ 1170*3cbadd90SDavid du Colombier } 11714f8f669cSDavid du Colombier 1172*3cbadd90SDavid du Colombier // if (qp->dp->respcode) 1173*3cbadd90SDavid du Colombier // dnslog("netquery1 setting Rserver for %s", qp->dp->name); 11747dd7cddfSDavid du Colombier 1175*3cbadd90SDavid du Colombier qp->dest = qp->curdest = nil; /* prevent accidents */ 11763e12c5d1SDavid du Colombier return 0; 11773e12c5d1SDavid du Colombier } 11787dd7cddfSDavid du Colombier 11794f8f669cSDavid du Colombier /* 11804f8f669cSDavid du Colombier * run a command with a supplied fd as standard input 11814f8f669cSDavid du Colombier */ 11824f8f669cSDavid du Colombier char * 11834f8f669cSDavid du Colombier system(int fd, char *cmd) 118428a8a86bSDavid du Colombier { 11854f8f669cSDavid du Colombier int pid, p, i; 11864f8f669cSDavid du Colombier static Waitmsg msg; 11876b0d5c8bSDavid du Colombier 11884f8f669cSDavid du Colombier if((pid = fork()) == -1) 11894f8f669cSDavid du Colombier sysfatal("fork failed: %r"); 11904f8f669cSDavid du Colombier else if(pid == 0){ 11914f8f669cSDavid du Colombier dup(fd, 0); 11924f8f669cSDavid du Colombier close(fd); 11934f8f669cSDavid du Colombier for (i = 3; i < 200; i++) 11944f8f669cSDavid du Colombier close(i); /* don't leak fds */ 11954f8f669cSDavid du Colombier execl("/bin/rc", "rc", "-c", cmd, nil); 11964f8f669cSDavid du Colombier sysfatal("exec rc: %r"); 11974f8f669cSDavid du Colombier } 11984f8f669cSDavid du Colombier for(p = waitpid(); p >= 0; p = waitpid()) 11994f8f669cSDavid du Colombier if(p == pid) 12004f8f669cSDavid du Colombier return msg.msg; 12014f8f669cSDavid du Colombier return "lost child"; 12024f8f669cSDavid du Colombier } 12034f8f669cSDavid du Colombier 12044f8f669cSDavid du Colombier enum { Hurry, Patient, }; 12054f8f669cSDavid du Colombier enum { Outns, Inns, }; 12064f8f669cSDavid du Colombier enum { Remntretry = 15, }; /* min. sec.s between remount attempts */ 12074f8f669cSDavid du Colombier 12084f8f669cSDavid du Colombier static int 1209*3cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns) 12104f8f669cSDavid du Colombier { 12114f8f669cSDavid du Colombier int fd, rv = 0; 12124f8f669cSDavid du Colombier long now; 12134f8f669cSDavid du Colombier char *msg; 12144f8f669cSDavid du Colombier uchar *obuf, *ibuf; 12154f8f669cSDavid du Colombier static QLock mntlck; 12164f8f669cSDavid du Colombier static ulong lastmount; 12177dd7cddfSDavid du Colombier 12187dd7cddfSDavid du Colombier /* use alloced buffers rather than ones from the stack */ 1219*3cbadd90SDavid du Colombier // ibuf = emalloc(Maxudpin+OUdphdrsize); 1220*3cbadd90SDavid du Colombier ibuf = emalloc(64*1024); /* max. tcp reply size */ 1221d9dc5dd1SDavid du Colombier obuf = emalloc(Maxudp+OUdphdrsize); 12227dd7cddfSDavid du Colombier 12234f8f669cSDavid du Colombier fd = udpport(mntpt); 12244f8f669cSDavid du Colombier while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) { 12254f8f669cSDavid du Colombier /* HACK: remount /net.alt */ 12264f8f669cSDavid du Colombier now = time(nil); 12274f8f669cSDavid du Colombier if (now < lastmount + Remntretry) 12284f8f669cSDavid du Colombier sleep((lastmount + Remntretry - now)*1000); 12294f8f669cSDavid du Colombier qlock(&mntlck); 12304f8f669cSDavid du Colombier fd = udpport(mntpt); /* try again under lock */ 12314f8f669cSDavid du Colombier if (fd < 0) { 12324f8f669cSDavid du Colombier dnslog("[%d] remounting /net.alt", getpid()); 12334f8f669cSDavid du Colombier unmount(nil, "/net.alt"); 12344f8f669cSDavid du Colombier 12354f8f669cSDavid du Colombier msg = system(open("/dev/null", ORDWR), "outside"); 12364f8f669cSDavid du Colombier 12374f8f669cSDavid du Colombier lastmount = time(nil); 12384f8f669cSDavid du Colombier if (msg && *msg) { 12394f8f669cSDavid du Colombier dnslog("[%d] can't remount /net.alt: %s", 12404f8f669cSDavid du Colombier getpid(), msg); 12414f8f669cSDavid du Colombier sleep(10*1000); /* don't spin wildly */ 12424f8f669cSDavid du Colombier } else 12434f8f669cSDavid du Colombier fd = udpport(mntpt); 12444f8f669cSDavid du Colombier } 12454f8f669cSDavid du Colombier qunlock(&mntlck); 12464f8f669cSDavid du Colombier } 12474f8f669cSDavid du Colombier if(fd >= 0) { 1248*3cbadd90SDavid du Colombier qp->req->aborttime = time(nil) + (patient? Maxreqtm: Maxreqtm/2); 1249d6d99297SDavid du Colombier /* tune; was (patient? 15: 10) */ 1250*3cbadd90SDavid du Colombier qp->udpfd = fd; 1251*3cbadd90SDavid du Colombier rv = netquery1(qp, depth, ibuf, obuf, (patient? 10: 5), inns); 12524f8f669cSDavid du Colombier close(fd); 12534f8f669cSDavid du Colombier } else 12544f8f669cSDavid du Colombier dnslog("can't get udpport for %s query of name %s: %r", 1255*3cbadd90SDavid du Colombier mntpt, qp->dp->name); 12564f8f669cSDavid du Colombier 12574f8f669cSDavid du Colombier free(obuf); 12584f8f669cSDavid du Colombier free(ibuf); 12594f8f669cSDavid du Colombier return rv; 12604f8f669cSDavid du Colombier } 12614f8f669cSDavid du Colombier 12624f8f669cSDavid du Colombier /* look up (dp->name,type) via *nsrp with results in *reqp */ 12634f8f669cSDavid du Colombier static int 1264*3cbadd90SDavid du Colombier netquery(Query *qp, int depth) 12654f8f669cSDavid du Colombier { 12664f8f669cSDavid du Colombier int lock, rv, triedin, inname; 12674f8f669cSDavid du Colombier RR *rp; 12684f8f669cSDavid du Colombier 12694f8f669cSDavid du Colombier if(depth > 12) /* in a recursive loop? */ 12704f8f669cSDavid du Colombier return 0; 12714f8f669cSDavid du Colombier 1272*3cbadd90SDavid du Colombier slave(qp->req); 1273d3907fe5SDavid du Colombier /* 1274d3907fe5SDavid du Colombier * slave might have forked. if so, the parent process longjmped to 1275d3907fe5SDavid du Colombier * req->mret; we're usually the child slave, but if there are too 1276d3907fe5SDavid du Colombier * many children already, we're still the same process. 1277d3907fe5SDavid du Colombier */ 12784f8f669cSDavid du Colombier 12794f8f669cSDavid du Colombier /* don't lock before call to slave so only children can block */ 1280*3cbadd90SDavid du Colombier lock = qp->req->isslave != 0; 12814f8f669cSDavid du Colombier if(lock) { 1282*3cbadd90SDavid du Colombier procsetname("query lock wait for %s", qp->dp->name); 12834f8f669cSDavid du Colombier /* don't make concurrent queries for this name */ 1284*3cbadd90SDavid du Colombier qlock(&qp->dp->querylck); 1285*3cbadd90SDavid du Colombier procsetname("netquery: %s", qp->dp->name); 12864f8f669cSDavid du Colombier } 12876b0d5c8bSDavid du Colombier 12886b0d5c8bSDavid du Colombier /* prepare server RR's for incremental lookup */ 1289*3cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 12906b0d5c8bSDavid du Colombier rp->marker = 0; 12916b0d5c8bSDavid du Colombier 12924f8f669cSDavid du Colombier rv = 0; /* pessimism */ 12934f8f669cSDavid du Colombier triedin = 0; 1294*3cbadd90SDavid du Colombier qp->nsrp = qp->nsrp; 12954f8f669cSDavid du Colombier /* 12964f8f669cSDavid du Colombier * normal resolvers and servers will just use mntpt for all addresses, 12974f8f669cSDavid du Colombier * even on the outside. straddling servers will use mntpt (/net) 12984f8f669cSDavid du Colombier * for inside addresses and /net.alt for outside addresses, 12994f8f669cSDavid du Colombier * thus bypassing other inside nameservers. 13004f8f669cSDavid du Colombier */ 1301*3cbadd90SDavid du Colombier inname = insideaddr(qp->dp->name); 13024f8f669cSDavid du Colombier if (!cfg.straddle || inname) { 1303*3cbadd90SDavid du Colombier rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns)); 13044f8f669cSDavid du Colombier triedin = 1; 13054f8f669cSDavid du Colombier } 13064f8f669cSDavid du Colombier 13074f8f669cSDavid du Colombier /* 13084f8f669cSDavid du Colombier * if we're still looking, are inside, and have an outside domain, 13094f8f669cSDavid du Colombier * try it on our outside interface, if any. 13104f8f669cSDavid du Colombier */ 13114f8f669cSDavid du Colombier if (rv == 0 && cfg.inside && !inname) { 13124f8f669cSDavid du Colombier if (triedin) 13134f8f669cSDavid du Colombier dnslog( 13144f8f669cSDavid du Colombier "[%d] netquery: internal nameservers failed for %s; trying external", 1315*3cbadd90SDavid du Colombier getpid(), qp->dp->name); 13164f8f669cSDavid du Colombier 13174f8f669cSDavid du Colombier /* prepare server RR's for incremental lookup */ 1318*3cbadd90SDavid du Colombier for(rp = qp->nsrp; rp; rp = rp->next) 13194f8f669cSDavid du Colombier rp->marker = 0; 13204f8f669cSDavid du Colombier 1321*3cbadd90SDavid du Colombier rv = udpquery(qp, "/net.alt", depth, Patient, Outns); 13224f8f669cSDavid du Colombier } 1323*3cbadd90SDavid du Colombier // if (rv == 0) /* could ask /net.alt/dns directly */ 1324*3cbadd90SDavid du Colombier // askoutdns(qp->dp, qp->type); 13254f8f669cSDavid du Colombier 1326*3cbadd90SDavid du Colombier if(lock) { 1327*3cbadd90SDavid du Colombier qunlock(&qp->dp->querylck); 1328*3cbadd90SDavid du Colombier } 13297dd7cddfSDavid du Colombier return rv; 13307dd7cddfSDavid du Colombier } 13314f8f669cSDavid du Colombier 13324f8f669cSDavid du Colombier int 13334f8f669cSDavid du Colombier seerootns(void) 13344f8f669cSDavid du Colombier { 1335*3cbadd90SDavid du Colombier int rv; 13364f8f669cSDavid du Colombier char root[] = ""; 13374f8f669cSDavid du Colombier Request req; 1338*3cbadd90SDavid du Colombier Query query; 13394f8f669cSDavid du Colombier 13404f8f669cSDavid du Colombier memset(&req, 0, sizeof req); 13414f8f669cSDavid du Colombier req.isslave = 1; 13424f8f669cSDavid du Colombier req.aborttime = now + Maxreqtm*2; /* be patient */ 1343*3cbadd90SDavid du Colombier queryinit(&query, dnlookup(root, Cin, 1), Tns, &req); 1344*3cbadd90SDavid du Colombier query.nsrp = dblookup(root, Cin, Tns, 0, 0); 1345*3cbadd90SDavid du Colombier rv = netquery(&query, 0); 1346*3cbadd90SDavid du Colombier memset(&query, 0, sizeof query); /* prevent accidents */ 1347*3cbadd90SDavid du Colombier return rv; 13484f8f669cSDavid du Colombier } 1349