13e12c5d1SDavid du Colombier #include <u.h> 23e12c5d1SDavid du Colombier #include <libc.h> 3*219b2ee8SDavid du Colombier #include <lock.h> 43e12c5d1SDavid du Colombier #include "dns.h" 53e12c5d1SDavid du Colombier #include "ip.h" 63e12c5d1SDavid du Colombier 73e12c5d1SDavid du Colombier enum 83e12c5d1SDavid du Colombier { 93e12c5d1SDavid du Colombier Maxdest= 32, /* maximum destinations for a request message */ 103e12c5d1SDavid du Colombier Maxtrans= 3, /* maximum transmissions to a server */ 113e12c5d1SDavid du Colombier }; 123e12c5d1SDavid du Colombier 133e12c5d1SDavid du Colombier typedef struct Dest Dest; 143e12c5d1SDavid du Colombier struct Dest 153e12c5d1SDavid du Colombier { 163e12c5d1SDavid du Colombier uchar a[4]; /* ip address */ 173e12c5d1SDavid du Colombier DN *s; /* name server */ 183e12c5d1SDavid du Colombier int nx; /* number of transmissions */ 193e12c5d1SDavid du Colombier }; 203e12c5d1SDavid du Colombier 213e12c5d1SDavid du Colombier static ulong reqno; /* request id */ 223e12c5d1SDavid du Colombier 233e12c5d1SDavid du Colombier static int netquery(DN*, int, RR*, Request*); 243e12c5d1SDavid du Colombier static RR* dnresolve1(char*, int, int, Request*); 253e12c5d1SDavid du Colombier 263e12c5d1SDavid du Colombier /* 273e12c5d1SDavid du Colombier * lookup 'type' info for domain name 'name'. If it doesn't exist, try 283e12c5d1SDavid du Colombier * looking it up as a canonical name. 293e12c5d1SDavid du Colombier */ 303e12c5d1SDavid du Colombier RR* 313e12c5d1SDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn) 323e12c5d1SDavid du Colombier { 333e12c5d1SDavid du Colombier RR *rp; 343e12c5d1SDavid du Colombier DN *dp; 353e12c5d1SDavid du Colombier 363e12c5d1SDavid du Colombier /* try the name directly */ 373e12c5d1SDavid du Colombier rp = dnresolve1(name, class, type, req); 383e12c5d1SDavid du Colombier if(rp) 393e12c5d1SDavid du Colombier return rp; 403e12c5d1SDavid du Colombier 413e12c5d1SDavid du Colombier /* try it as a canonical name */ 423e12c5d1SDavid du Colombier rp = dnresolve1(name, class, Tcname, req); 433e12c5d1SDavid du Colombier if(rp == 0) 443e12c5d1SDavid du Colombier return 0; 453e12c5d1SDavid du Colombier if(rp && cn) 463e12c5d1SDavid du Colombier *cn = rp; 473e12c5d1SDavid du Colombier dp = rp->host; 483e12c5d1SDavid du Colombier return dnresolve1(dp->name, class, type, req); 493e12c5d1SDavid du Colombier } 503e12c5d1SDavid du Colombier 513e12c5d1SDavid du Colombier static RR* 523e12c5d1SDavid du Colombier dnresolve1(char *name, int class, int type, Request *req) 533e12c5d1SDavid du Colombier { 543e12c5d1SDavid du Colombier DN *dp, *nsdp; 553e12c5d1SDavid du Colombier RR *rp, *nsrp; 563e12c5d1SDavid du Colombier char *cp; 573e12c5d1SDavid du Colombier 583e12c5d1SDavid du Colombier /* only class Cin implemented so far */ 593e12c5d1SDavid du Colombier if(class != Cin) 603e12c5d1SDavid du Colombier return 0; 613e12c5d1SDavid du Colombier 623e12c5d1SDavid du Colombier dp = dnlookup(name, class, 1); 633e12c5d1SDavid du Colombier 643e12c5d1SDavid du Colombier /* first try the cache */ 653e12c5d1SDavid du Colombier rp = rrlookup(dp, type); 663e12c5d1SDavid du Colombier if(rp) 673e12c5d1SDavid du Colombier return rp; 683e12c5d1SDavid du Colombier 69*219b2ee8SDavid du Colombier /* in-addr.arpa queries (and all) are special */ 70*219b2ee8SDavid du Colombier if(tsame(type, Tptr)){ 713e12c5d1SDavid du Colombier rp = dbinaddr(dp); 723e12c5d1SDavid du Colombier if(rp) 733e12c5d1SDavid du Colombier return rp; 743e12c5d1SDavid du Colombier } 753e12c5d1SDavid du Colombier 763e12c5d1SDavid du Colombier /* 77*219b2ee8SDavid du Colombier * Quick grab, see if it's a 'relative to my domain' request. 78*219b2ee8SDavid du Colombier * I'm not sure this is a good idea but our x-terminals want it. 79*219b2ee8SDavid du Colombier */ 80*219b2ee8SDavid du Colombier if(strchr(name, '.') == 0){ 81*219b2ee8SDavid du Colombier rp = dblookup(name, class, type, 1); 82*219b2ee8SDavid du Colombier if(rp) 83*219b2ee8SDavid du Colombier return rp; 84*219b2ee8SDavid du Colombier } 85*219b2ee8SDavid du Colombier 86*219b2ee8SDavid du Colombier /* 873e12c5d1SDavid du Colombier * walk up the domain name looking for 883e12c5d1SDavid du Colombier * a name server for the domain. 893e12c5d1SDavid du Colombier */ 903e12c5d1SDavid du Colombier for(cp = name; cp; cp = walkup(cp)){ 913e12c5d1SDavid du Colombier /* look for ns in cache and database */ 923e12c5d1SDavid du Colombier nsdp = dnlookup(cp, class, 0); 933e12c5d1SDavid du Colombier nsrp = 0; 943e12c5d1SDavid du Colombier if(nsdp) 953e12c5d1SDavid du Colombier nsrp = rrlookup(nsdp, Tns); 963e12c5d1SDavid du Colombier if(nsrp == 0) 973e12c5d1SDavid du Colombier nsrp = dblookup(cp, class, Tns, 0); 983e12c5d1SDavid du Colombier 993e12c5d1SDavid du Colombier if(nsrp){ 1003e12c5d1SDavid du Colombier /* local domains resolved from this db */ 1013e12c5d1SDavid du Colombier if(nsrp->local){ 1023e12c5d1SDavid du Colombier if(nsrp->db) /* free database rr's */ 1033e12c5d1SDavid du Colombier rrfreelist(nsrp); 1043e12c5d1SDavid du Colombier return dblookup(name, class, type, 1); 1053e12c5d1SDavid du Colombier } 1063e12c5d1SDavid du Colombier 1073e12c5d1SDavid du Colombier /* try the name servers */ 1083e12c5d1SDavid du Colombier if(netquery(dp, type, nsrp, req)){ 1093e12c5d1SDavid du Colombier /* we got an answer */ 1103e12c5d1SDavid du Colombier if(nsrp->db) /* free database rr's */ 1113e12c5d1SDavid du Colombier rrfreelist(nsrp); 1123e12c5d1SDavid du Colombier return rrlookup(dp, type); 1133e12c5d1SDavid du Colombier } 1143e12c5d1SDavid du Colombier } 1153e12c5d1SDavid du Colombier } 1163e12c5d1SDavid du Colombier 1173e12c5d1SDavid du Colombier /* noone answered */ 1183e12c5d1SDavid du Colombier return 0; 1193e12c5d1SDavid du Colombier } 1203e12c5d1SDavid du Colombier 1213e12c5d1SDavid du Colombier /* 1223e12c5d1SDavid du Colombier * walk a domain name one element to the right. return a pointer to that element. 1233e12c5d1SDavid du Colombier * in other words, return a pointer to the parent domain name. 1243e12c5d1SDavid du Colombier */ 1253e12c5d1SDavid du Colombier char* 1263e12c5d1SDavid du Colombier walkup(char *name) 1273e12c5d1SDavid du Colombier { 1283e12c5d1SDavid du Colombier char *cp; 1293e12c5d1SDavid du Colombier 1303e12c5d1SDavid du Colombier cp = strchr(name, '.'); 1313e12c5d1SDavid du Colombier if(cp) 1323e12c5d1SDavid du Colombier return cp+1; 1333e12c5d1SDavid du Colombier else if(*name) 1343e12c5d1SDavid du Colombier return ""; 1353e12c5d1SDavid du Colombier else 1363e12c5d1SDavid du Colombier return 0; 1373e12c5d1SDavid du Colombier } 1383e12c5d1SDavid du Colombier 1393e12c5d1SDavid du Colombier /* 1403e12c5d1SDavid du Colombier * Get a udpport for requests and replies. Put the port 1413e12c5d1SDavid du Colombier * into "headers" mode. 1423e12c5d1SDavid du Colombier */ 1433e12c5d1SDavid du Colombier static char *hmsg = "headers"; 1443e12c5d1SDavid du Colombier 1453e12c5d1SDavid du Colombier static int 1463e12c5d1SDavid du Colombier udpport(void) 1473e12c5d1SDavid du Colombier { 1483e12c5d1SDavid du Colombier int fd, ctl; 1493e12c5d1SDavid du Colombier 1503e12c5d1SDavid du Colombier /* get a udp port */ 1513e12c5d1SDavid du Colombier fd = dial("udp!0.0.0.0!0", 0, 0, &ctl); 152bd389b36SDavid du Colombier if(fd < 0){ 153bd389b36SDavid du Colombier warning("can't get udp port"); 154bd389b36SDavid du Colombier return -1; 155bd389b36SDavid du Colombier } 1563e12c5d1SDavid du Colombier 1573e12c5d1SDavid du Colombier /* turn on header style interface */ 158bd389b36SDavid du Colombier if(write(ctl, hmsg, strlen(hmsg)) , 0){ 159bd389b36SDavid du Colombier warning(hmsg); 160bd389b36SDavid du Colombier return -1; 161bd389b36SDavid du Colombier } 1623e12c5d1SDavid du Colombier 1633e12c5d1SDavid du Colombier close(ctl); 1643e12c5d1SDavid du Colombier return fd; 1653e12c5d1SDavid du Colombier } 1663e12c5d1SDavid du Colombier 1673e12c5d1SDavid du Colombier static int 1683e12c5d1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int reqno) 1693e12c5d1SDavid du Colombier { 1703e12c5d1SDavid du Colombier DNSmsg m; 1713e12c5d1SDavid du Colombier int len; 1723e12c5d1SDavid du Colombier 1733e12c5d1SDavid du Colombier /* stuff port number into output buffer */ 1743e12c5d1SDavid du Colombier buf[4] = 0; 1753e12c5d1SDavid du Colombier buf[5] = 53; 1763e12c5d1SDavid du Colombier 1773e12c5d1SDavid du Colombier /* make request and convert it to output format */ 1783e12c5d1SDavid du Colombier memset(&m, 0, sizeof(m)); 179*219b2ee8SDavid du Colombier m.flags = Frecurse; 1803e12c5d1SDavid du Colombier m.id = reqno; 1813e12c5d1SDavid du Colombier m.qd = rralloc(type); 1823e12c5d1SDavid du Colombier m.qd->owner = dp; 1833e12c5d1SDavid du Colombier m.qd->type = type; 184*219b2ee8SDavid du Colombier len = convDNS2M(&m, &buf[Udphdrsize], Maxudp); 1853e12c5d1SDavid du Colombier if(len < 0) 1863e12c5d1SDavid du Colombier fatal("can't convert"); 1873e12c5d1SDavid du Colombier return len; 1883e12c5d1SDavid du Colombier } 1893e12c5d1SDavid du Colombier 1903e12c5d1SDavid du Colombier /* 1913e12c5d1SDavid du Colombier * read replies to a request. ignore any of the wrong type. 1923e12c5d1SDavid du Colombier */ 1933e12c5d1SDavid du Colombier static int 1943e12c5d1SDavid du Colombier readreq(int fd, DN *dp, int type, int req, uchar *ibuf, DNSmsg *mp) 1953e12c5d1SDavid du Colombier { 1963e12c5d1SDavid du Colombier char *err; 1973e12c5d1SDavid du Colombier int len; 1983e12c5d1SDavid du Colombier 1993e12c5d1SDavid du Colombier for(;;){ 200*219b2ee8SDavid du Colombier len = read(fd, ibuf, Udphdrsize+Maxudp); 201*219b2ee8SDavid du Colombier len -= Udphdrsize; 2023e12c5d1SDavid du Colombier if(len < 0) 2033e12c5d1SDavid du Colombier return -1; /* timed out */ 2043e12c5d1SDavid du Colombier 2053e12c5d1SDavid du Colombier /* convert into internal format */ 206*219b2ee8SDavid du Colombier err = convM2DNS(&ibuf[Udphdrsize], len, mp); 2073e12c5d1SDavid du Colombier if(err){ 2083e12c5d1SDavid du Colombier syslog(0, "dns", "input err %s", err); 2093e12c5d1SDavid du Colombier continue; 2103e12c5d1SDavid du Colombier } 211*219b2ee8SDavid du Colombier if(debug){ 212*219b2ee8SDavid du Colombier if(mp->qd) 213*219b2ee8SDavid du Colombier syslog(0, "dns", "rcvd %I qd %s", ibuf, mp->qd->owner->name); 214*219b2ee8SDavid du Colombier if(mp->an) 215*219b2ee8SDavid du Colombier syslog(0, "dns", "rcvd %I an %R", ibuf, mp->an); 216*219b2ee8SDavid du Colombier if(mp->ns) 217*219b2ee8SDavid du Colombier syslog(0, "dns", "rcvd %I ns %R", ibuf, mp->ns); 218*219b2ee8SDavid du Colombier if(mp->ar) 219*219b2ee8SDavid du Colombier syslog(0, "dns", "rcvd %I ar %R", ibuf, mp->ar); 220*219b2ee8SDavid du Colombier } 2213e12c5d1SDavid du Colombier 2223e12c5d1SDavid du Colombier /* answering the right question? */ 2233e12c5d1SDavid du Colombier if(mp->id != req){ 2243e12c5d1SDavid du Colombier syslog(0, "dns", "id %d instead of %d", mp->id, req); 2253e12c5d1SDavid du Colombier continue; 2263e12c5d1SDavid du Colombier } 2273e12c5d1SDavid du Colombier if(mp->qd == 0){ 2283e12c5d1SDavid du Colombier syslog(0, "dns", "no question RR"); 2293e12c5d1SDavid du Colombier continue; 2303e12c5d1SDavid du Colombier } 2313e12c5d1SDavid du Colombier if(mp->qd->owner != dp){ 2323e12c5d1SDavid du Colombier syslog(0, "dns", "owner %s instead of %s", mp->qd->owner->name, 2333e12c5d1SDavid du Colombier dp->name); 2343e12c5d1SDavid du Colombier continue; 2353e12c5d1SDavid du Colombier } 2363e12c5d1SDavid du Colombier if(mp->qd->type != type){ 2373e12c5d1SDavid du Colombier syslog(0, "dns", "type %d instead of %d", mp->qd->type, type); 2383e12c5d1SDavid du Colombier continue; 2393e12c5d1SDavid du Colombier } 2403e12c5d1SDavid du Colombier return 0; 2413e12c5d1SDavid du Colombier } 2423e12c5d1SDavid du Colombier 2433e12c5d1SDavid du Colombier return 0; /* never reached */ 2443e12c5d1SDavid du Colombier } 2453e12c5d1SDavid du Colombier 2463e12c5d1SDavid du Colombier /* 2473e12c5d1SDavid du Colombier * query name servers. If the name server returns a pointer to another 2483e12c5d1SDavid du Colombier * name server, recurse. 2493e12c5d1SDavid du Colombier */ 2503e12c5d1SDavid du Colombier static void 2513e12c5d1SDavid du Colombier ding(void *x, char *msg) 2523e12c5d1SDavid du Colombier { 2533e12c5d1SDavid du Colombier USED(x); 2543e12c5d1SDavid du Colombier if(strcmp(msg, "alarm") == 0) 2553e12c5d1SDavid du Colombier noted(NCONT); 2563e12c5d1SDavid du Colombier else 2573e12c5d1SDavid du Colombier noted(NDFLT); 2583e12c5d1SDavid du Colombier } 2593e12c5d1SDavid du Colombier static int 2603e12c5d1SDavid du Colombier netquery(DN *dp, int type, RR *nsrp, Request *reqp) 2613e12c5d1SDavid du Colombier { 2623e12c5d1SDavid du Colombier int fd, i, j, len; 2633e12c5d1SDavid du Colombier ulong req; 2643e12c5d1SDavid du Colombier RR *rp; 2653e12c5d1SDavid du Colombier Dest *p, *l; 2663e12c5d1SDavid du Colombier DN *ndp; 2673e12c5d1SDavid du Colombier Dest dest[Maxdest]; 2683e12c5d1SDavid du Colombier DNSmsg m; 269*219b2ee8SDavid du Colombier uchar obuf[Maxudp+Udphdrsize]; 270*219b2ee8SDavid du Colombier uchar ibuf[Maxudp+Udphdrsize]; 2713e12c5d1SDavid du Colombier 2723e12c5d1SDavid du Colombier slave(reqp); 2733e12c5d1SDavid du Colombier 2743e12c5d1SDavid du Colombier /* get the addresses */ 2753e12c5d1SDavid du Colombier l = dest; 2763e12c5d1SDavid du Colombier for(; nsrp && nsrp->type == Tns; nsrp = nsrp->next){ 2773e12c5d1SDavid du Colombier rp = rrlookup(nsrp->host, Ta); 2783e12c5d1SDavid du Colombier if(rp == 0) 2793e12c5d1SDavid du Colombier rp = dblookup(nsrp->host->name, Cin, Ta, 0); 2803e12c5d1SDavid du Colombier for(; rp && rp->type == Ta; rp = rp->next){ 2813e12c5d1SDavid du Colombier if(l >= &dest[Maxdest]) 2823e12c5d1SDavid du Colombier break; 2833e12c5d1SDavid du Colombier parseip(l->a, rp->ip->name); 2843e12c5d1SDavid du Colombier l->nx = 0; 2853e12c5d1SDavid du Colombier l->s = nsrp->host; 2863e12c5d1SDavid du Colombier l++; 2873e12c5d1SDavid du Colombier } 2883e12c5d1SDavid du Colombier } 2893e12c5d1SDavid du Colombier 2903e12c5d1SDavid du Colombier /* pack request into a message */ 2913e12c5d1SDavid du Colombier req = reqno++; 2923e12c5d1SDavid du Colombier len = mkreq(dp, type, obuf, req); 2933e12c5d1SDavid du Colombier 2943e12c5d1SDavid du Colombier /* 2953e12c5d1SDavid du Colombier * transmit requests and wait for answers. 296*219b2ee8SDavid du Colombier * at most Maxtrans attempts to each address. 2973e12c5d1SDavid du Colombier * each cycle send one more message than the previous. 2983e12c5d1SDavid du Colombier */ 2993e12c5d1SDavid du Colombier fd = udpport(); 300bd389b36SDavid du Colombier if(fd < 0) 301bd389b36SDavid du Colombier return 0; 3023e12c5d1SDavid du Colombier notify(ding); 3033e12c5d1SDavid du Colombier for(i = 1;; i++){ 3043e12c5d1SDavid du Colombier /* send to i destinations */ 3053e12c5d1SDavid du Colombier p = dest; 3063e12c5d1SDavid du Colombier for(j = 0; j < i; j++){ 3073e12c5d1SDavid du Colombier /* skip destinations we've finished with */ 3083e12c5d1SDavid du Colombier for(; p < l; p++) 3093e12c5d1SDavid du Colombier if(p->nx < Maxtrans) 3103e12c5d1SDavid du Colombier break; 3113e12c5d1SDavid du Colombier if(p >= l) 3123e12c5d1SDavid du Colombier break; 3133e12c5d1SDavid du Colombier 3143e12c5d1SDavid du Colombier p->nx++; 3153e12c5d1SDavid du Colombier memmove(obuf, p->a, sizeof(p->a)); 316*219b2ee8SDavid du Colombier if(debug) 317*219b2ee8SDavid du Colombier syslog(0, "dns", "sending to %I", obuf); 318*219b2ee8SDavid du Colombier if(write(fd, obuf, len + Udphdrsize) < 0) 319bd389b36SDavid du Colombier warning("sending udp msg %r"); 3203e12c5d1SDavid du Colombier p++; 3213e12c5d1SDavid du Colombier } 3223e12c5d1SDavid du Colombier if(j == 0) 3233e12c5d1SDavid du Colombier break; /* no destinations left */ 3243e12c5d1SDavid du Colombier 3253e12c5d1SDavid du Colombier /* wait a fixed time for replies */ 3263e12c5d1SDavid du Colombier alarm(1000); 3273e12c5d1SDavid du Colombier for(;;){ 3283e12c5d1SDavid du Colombier if(readreq(fd, dp, type, req, ibuf, &m) < 0) 3293e12c5d1SDavid du Colombier break; /* timed out */ 3303e12c5d1SDavid du Colombier 3313e12c5d1SDavid du Colombier /* remove all addrs of responding server from list */ 3323e12c5d1SDavid du Colombier for(p = dest; p < l; p++) 3333e12c5d1SDavid du Colombier if(memcmp(p->a, ibuf, sizeof(p->a)) == 0){ 3343e12c5d1SDavid du Colombier ndp = p->s; 3353e12c5d1SDavid du Colombier for(p = dest; p < l; p++) 3363e12c5d1SDavid du Colombier if(p->s == ndp) 3373e12c5d1SDavid du Colombier p->nx = Maxtrans; 3383e12c5d1SDavid du Colombier break; 3393e12c5d1SDavid du Colombier } 3403e12c5d1SDavid du Colombier 3413e12c5d1SDavid du Colombier /* incorporate answers */ 3423e12c5d1SDavid du Colombier if(m.an) 3433e12c5d1SDavid du Colombier rrattach(m.an, m.flags & Fauth); 3443e12c5d1SDavid du Colombier if(m.ar) 3453e12c5d1SDavid du Colombier rrattach(m.ar, 0); 3463e12c5d1SDavid du Colombier 3473e12c5d1SDavid du Colombier /* 3483e12c5d1SDavid du Colombier * Any reply from an authoritative server, 3493e12c5d1SDavid du Colombier * or a positive reply terminates the search 3503e12c5d1SDavid du Colombier */ 3513e12c5d1SDavid du Colombier if(m.an || (m.flags & Fauth)){ 3523e12c5d1SDavid du Colombier alarm(0); 3533e12c5d1SDavid du Colombier close(fd); 3543e12c5d1SDavid du Colombier return 1; 3553e12c5d1SDavid du Colombier } 3563e12c5d1SDavid du Colombier 3573e12c5d1SDavid du Colombier /* 3583e12c5d1SDavid du Colombier * if we've been given better name servers 3593e12c5d1SDavid du Colombier * recurse 3603e12c5d1SDavid du Colombier */ 3613e12c5d1SDavid du Colombier if(m.ns){ 3623e12c5d1SDavid du Colombier alarm(0); 3633e12c5d1SDavid du Colombier close(fd); 3643e12c5d1SDavid du Colombier ndp = m.ns->owner; 3653e12c5d1SDavid du Colombier rrattach(m.ns, 0); 3663e12c5d1SDavid du Colombier return netquery(dp, type, rrlookup(ndp, Tns), reqp); 3673e12c5d1SDavid du Colombier } 3683e12c5d1SDavid du Colombier } 3693e12c5d1SDavid du Colombier alarm(0); 3703e12c5d1SDavid du Colombier } 3713e12c5d1SDavid du Colombier alarm(0); 3723e12c5d1SDavid du Colombier close(fd); 3733e12c5d1SDavid du Colombier return 0; 3743e12c5d1SDavid du Colombier } 375