1*3e12c5d1SDavid du Colombier #include <u.h> 2*3e12c5d1SDavid du Colombier #include <libc.h> 3*3e12c5d1SDavid du Colombier #include "dns.h" 4*3e12c5d1SDavid du Colombier #include "ip.h" 5*3e12c5d1SDavid du Colombier 6*3e12c5d1SDavid du Colombier enum 7*3e12c5d1SDavid du Colombier { 8*3e12c5d1SDavid du Colombier Maxdest= 32, /* maximum destinations for a request message */ 9*3e12c5d1SDavid du Colombier Maxtrans= 3, /* maximum transmissions to a server */ 10*3e12c5d1SDavid du Colombier }; 11*3e12c5d1SDavid du Colombier 12*3e12c5d1SDavid du Colombier typedef struct Dest Dest; 13*3e12c5d1SDavid du Colombier struct Dest 14*3e12c5d1SDavid du Colombier { 15*3e12c5d1SDavid du Colombier uchar a[4]; /* ip address */ 16*3e12c5d1SDavid du Colombier DN *s; /* name server */ 17*3e12c5d1SDavid du Colombier int nx; /* number of transmissions */ 18*3e12c5d1SDavid du Colombier }; 19*3e12c5d1SDavid du Colombier 20*3e12c5d1SDavid du Colombier static ulong reqno; /* request id */ 21*3e12c5d1SDavid du Colombier 22*3e12c5d1SDavid du Colombier static int netquery(DN*, int, RR*, Request*); 23*3e12c5d1SDavid du Colombier static RR* dnresolve1(char*, int, int, Request*); 24*3e12c5d1SDavid du Colombier 25*3e12c5d1SDavid du Colombier /* 26*3e12c5d1SDavid du Colombier * lookup 'type' info for domain name 'name'. If it doesn't exist, try 27*3e12c5d1SDavid du Colombier * looking it up as a canonical name. 28*3e12c5d1SDavid du Colombier */ 29*3e12c5d1SDavid du Colombier RR* 30*3e12c5d1SDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn) 31*3e12c5d1SDavid du Colombier { 32*3e12c5d1SDavid du Colombier RR *rp; 33*3e12c5d1SDavid du Colombier DN *dp; 34*3e12c5d1SDavid du Colombier 35*3e12c5d1SDavid du Colombier /* try the name directly */ 36*3e12c5d1SDavid du Colombier rp = dnresolve1(name, class, type, req); 37*3e12c5d1SDavid du Colombier if(rp) 38*3e12c5d1SDavid du Colombier return rp; 39*3e12c5d1SDavid du Colombier 40*3e12c5d1SDavid du Colombier /* try it as a canonical name */ 41*3e12c5d1SDavid du Colombier rp = dnresolve1(name, class, Tcname, req); 42*3e12c5d1SDavid du Colombier if(rp == 0) 43*3e12c5d1SDavid du Colombier return 0; 44*3e12c5d1SDavid du Colombier if(rp && cn) 45*3e12c5d1SDavid du Colombier *cn = rp; 46*3e12c5d1SDavid du Colombier dp = rp->host; 47*3e12c5d1SDavid du Colombier return dnresolve1(dp->name, class, type, req); 48*3e12c5d1SDavid du Colombier } 49*3e12c5d1SDavid du Colombier 50*3e12c5d1SDavid du Colombier static RR* 51*3e12c5d1SDavid du Colombier dnresolve1(char *name, int class, int type, Request *req) 52*3e12c5d1SDavid du Colombier { 53*3e12c5d1SDavid du Colombier DN *dp, *nsdp; 54*3e12c5d1SDavid du Colombier RR *rp, *nsrp; 55*3e12c5d1SDavid du Colombier char *cp; 56*3e12c5d1SDavid du Colombier 57*3e12c5d1SDavid du Colombier /* only class Cin implemented so far */ 58*3e12c5d1SDavid du Colombier if(class != Cin) 59*3e12c5d1SDavid du Colombier return 0; 60*3e12c5d1SDavid du Colombier 61*3e12c5d1SDavid du Colombier dp = dnlookup(name, class, 1); 62*3e12c5d1SDavid du Colombier 63*3e12c5d1SDavid du Colombier /* first try the cache */ 64*3e12c5d1SDavid du Colombier rp = rrlookup(dp, type); 65*3e12c5d1SDavid du Colombier if(rp) 66*3e12c5d1SDavid du Colombier return rp; 67*3e12c5d1SDavid du Colombier 68*3e12c5d1SDavid du Colombier /* in-addr.arpa queries are special */ 69*3e12c5d1SDavid du Colombier if(type == Tptr){ 70*3e12c5d1SDavid du Colombier rp = dbinaddr(dp); 71*3e12c5d1SDavid du Colombier if(rp) 72*3e12c5d1SDavid du Colombier return rp; 73*3e12c5d1SDavid du Colombier } 74*3e12c5d1SDavid du Colombier 75*3e12c5d1SDavid du Colombier /* 76*3e12c5d1SDavid du Colombier * walk up the domain name looking for 77*3e12c5d1SDavid du Colombier * a name server for the domain. 78*3e12c5d1SDavid du Colombier */ 79*3e12c5d1SDavid du Colombier for(cp = name; cp; cp = walkup(cp)){ 80*3e12c5d1SDavid du Colombier /* look for ns in cache and database */ 81*3e12c5d1SDavid du Colombier nsdp = dnlookup(cp, class, 0); 82*3e12c5d1SDavid du Colombier nsrp = 0; 83*3e12c5d1SDavid du Colombier if(nsdp) 84*3e12c5d1SDavid du Colombier nsrp = rrlookup(nsdp, Tns); 85*3e12c5d1SDavid du Colombier if(nsrp == 0) 86*3e12c5d1SDavid du Colombier nsrp = dblookup(cp, class, Tns, 0); 87*3e12c5d1SDavid du Colombier 88*3e12c5d1SDavid du Colombier if(nsrp){ 89*3e12c5d1SDavid du Colombier /* local domains resolved from this db */ 90*3e12c5d1SDavid du Colombier if(nsrp->local){ 91*3e12c5d1SDavid du Colombier if(nsrp->db) /* free database rr's */ 92*3e12c5d1SDavid du Colombier rrfreelist(nsrp); 93*3e12c5d1SDavid du Colombier return dblookup(name, class, type, 1); 94*3e12c5d1SDavid du Colombier } 95*3e12c5d1SDavid du Colombier 96*3e12c5d1SDavid du Colombier /* try the name servers */ 97*3e12c5d1SDavid du Colombier if(netquery(dp, type, nsrp, req)){ 98*3e12c5d1SDavid du Colombier /* we got an answer */ 99*3e12c5d1SDavid du Colombier if(nsrp->db) /* free database rr's */ 100*3e12c5d1SDavid du Colombier rrfreelist(nsrp); 101*3e12c5d1SDavid du Colombier return rrlookup(dp, type); 102*3e12c5d1SDavid du Colombier } 103*3e12c5d1SDavid du Colombier } 104*3e12c5d1SDavid du Colombier } 105*3e12c5d1SDavid du Colombier 106*3e12c5d1SDavid du Colombier /* noone answered */ 107*3e12c5d1SDavid du Colombier return 0; 108*3e12c5d1SDavid du Colombier } 109*3e12c5d1SDavid du Colombier 110*3e12c5d1SDavid du Colombier /* 111*3e12c5d1SDavid du Colombier * walk a domain name one element to the right. return a pointer to that element. 112*3e12c5d1SDavid du Colombier * in other words, return a pointer to the parent domain name. 113*3e12c5d1SDavid du Colombier */ 114*3e12c5d1SDavid du Colombier char* 115*3e12c5d1SDavid du Colombier walkup(char *name) 116*3e12c5d1SDavid du Colombier { 117*3e12c5d1SDavid du Colombier char *cp; 118*3e12c5d1SDavid du Colombier 119*3e12c5d1SDavid du Colombier cp = strchr(name, '.'); 120*3e12c5d1SDavid du Colombier if(cp) 121*3e12c5d1SDavid du Colombier return cp+1; 122*3e12c5d1SDavid du Colombier else if(*name) 123*3e12c5d1SDavid du Colombier return ""; 124*3e12c5d1SDavid du Colombier else 125*3e12c5d1SDavid du Colombier return 0; 126*3e12c5d1SDavid du Colombier } 127*3e12c5d1SDavid du Colombier 128*3e12c5d1SDavid du Colombier /* 129*3e12c5d1SDavid du Colombier * Get a udpport for requests and replies. Put the port 130*3e12c5d1SDavid du Colombier * into "headers" mode. 131*3e12c5d1SDavid du Colombier */ 132*3e12c5d1SDavid du Colombier static char *hmsg = "headers"; 133*3e12c5d1SDavid du Colombier 134*3e12c5d1SDavid du Colombier static int 135*3e12c5d1SDavid du Colombier udpport(void) 136*3e12c5d1SDavid du Colombier { 137*3e12c5d1SDavid du Colombier int fd, ctl; 138*3e12c5d1SDavid du Colombier 139*3e12c5d1SDavid du Colombier /* get a udp port */ 140*3e12c5d1SDavid du Colombier fd = dial("udp!0.0.0.0!0", 0, 0, &ctl); 141*3e12c5d1SDavid du Colombier if(fd < 0) 142*3e12c5d1SDavid du Colombier fatal("can't get udp port"); 143*3e12c5d1SDavid du Colombier 144*3e12c5d1SDavid du Colombier /* turn on header style interface */ 145*3e12c5d1SDavid du Colombier if(write(ctl, hmsg, strlen(hmsg)) , 0) 146*3e12c5d1SDavid du Colombier fatal(hmsg); 147*3e12c5d1SDavid du Colombier 148*3e12c5d1SDavid du Colombier close(ctl); 149*3e12c5d1SDavid du Colombier return fd; 150*3e12c5d1SDavid du Colombier } 151*3e12c5d1SDavid du Colombier 152*3e12c5d1SDavid du Colombier static int 153*3e12c5d1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int reqno) 154*3e12c5d1SDavid du Colombier { 155*3e12c5d1SDavid du Colombier DNSmsg m; 156*3e12c5d1SDavid du Colombier int len; 157*3e12c5d1SDavid du Colombier 158*3e12c5d1SDavid du Colombier /* stuff port number into output buffer */ 159*3e12c5d1SDavid du Colombier buf[4] = 0; 160*3e12c5d1SDavid du Colombier buf[5] = 53; 161*3e12c5d1SDavid du Colombier 162*3e12c5d1SDavid du Colombier /* make request and convert it to output format */ 163*3e12c5d1SDavid du Colombier memset(&m, 0, sizeof(m)); 164*3e12c5d1SDavid du Colombier m.flags = 0; 165*3e12c5d1SDavid du Colombier m.id = reqno; 166*3e12c5d1SDavid du Colombier m.qd = rralloc(type); 167*3e12c5d1SDavid du Colombier m.qd->owner = dp; 168*3e12c5d1SDavid du Colombier m.qd->type = type; 169*3e12c5d1SDavid du Colombier len = convDNS2M(&m, &buf[Udphdrlen], Maxudp); 170*3e12c5d1SDavid du Colombier if(len < 0) 171*3e12c5d1SDavid du Colombier fatal("can't convert"); 172*3e12c5d1SDavid du Colombier return len; 173*3e12c5d1SDavid du Colombier } 174*3e12c5d1SDavid du Colombier 175*3e12c5d1SDavid du Colombier /* 176*3e12c5d1SDavid du Colombier * read replies to a request. ignore any of the wrong type. 177*3e12c5d1SDavid du Colombier */ 178*3e12c5d1SDavid du Colombier static int 179*3e12c5d1SDavid du Colombier readreq(int fd, DN *dp, int type, int req, uchar *ibuf, DNSmsg *mp) 180*3e12c5d1SDavid du Colombier { 181*3e12c5d1SDavid du Colombier char *err; 182*3e12c5d1SDavid du Colombier int len; 183*3e12c5d1SDavid du Colombier 184*3e12c5d1SDavid du Colombier for(;;){ 185*3e12c5d1SDavid du Colombier len = read(fd, ibuf, Udphdrlen+Maxudp); 186*3e12c5d1SDavid du Colombier len -= Udphdrlen; 187*3e12c5d1SDavid du Colombier if(len < 0) 188*3e12c5d1SDavid du Colombier return -1; /* timed out */ 189*3e12c5d1SDavid du Colombier 190*3e12c5d1SDavid du Colombier /* convert into internal format */ 191*3e12c5d1SDavid du Colombier err = convM2DNS(&ibuf[Udphdrlen], len, mp); 192*3e12c5d1SDavid du Colombier if(err){ 193*3e12c5d1SDavid du Colombier syslog(0, "dns", "input err %s", err); 194*3e12c5d1SDavid du Colombier continue; 195*3e12c5d1SDavid du Colombier } 196*3e12c5d1SDavid du Colombier 197*3e12c5d1SDavid du Colombier /* answering the right question? */ 198*3e12c5d1SDavid du Colombier if(mp->id != req){ 199*3e12c5d1SDavid du Colombier syslog(0, "dns", "id %d instead of %d", mp->id, req); 200*3e12c5d1SDavid du Colombier continue; 201*3e12c5d1SDavid du Colombier } 202*3e12c5d1SDavid du Colombier if(mp->qd == 0){ 203*3e12c5d1SDavid du Colombier syslog(0, "dns", "no question RR"); 204*3e12c5d1SDavid du Colombier continue; 205*3e12c5d1SDavid du Colombier } 206*3e12c5d1SDavid du Colombier if(mp->qd->owner != dp){ 207*3e12c5d1SDavid du Colombier syslog(0, "dns", "owner %s instead of %s", mp->qd->owner->name, 208*3e12c5d1SDavid du Colombier dp->name); 209*3e12c5d1SDavid du Colombier continue; 210*3e12c5d1SDavid du Colombier } 211*3e12c5d1SDavid du Colombier if(mp->qd->type != type){ 212*3e12c5d1SDavid du Colombier syslog(0, "dns", "type %d instead of %d", mp->qd->type, type); 213*3e12c5d1SDavid du Colombier continue; 214*3e12c5d1SDavid du Colombier } 215*3e12c5d1SDavid du Colombier return 0; 216*3e12c5d1SDavid du Colombier } 217*3e12c5d1SDavid du Colombier 218*3e12c5d1SDavid du Colombier return 0; /* never reached */ 219*3e12c5d1SDavid du Colombier } 220*3e12c5d1SDavid du Colombier 221*3e12c5d1SDavid du Colombier /* 222*3e12c5d1SDavid du Colombier * query name servers. If the name server returns a pointer to another 223*3e12c5d1SDavid du Colombier * name server, recurse. 224*3e12c5d1SDavid du Colombier */ 225*3e12c5d1SDavid du Colombier static void 226*3e12c5d1SDavid du Colombier ding(void *x, char *msg) 227*3e12c5d1SDavid du Colombier { 228*3e12c5d1SDavid du Colombier USED(x); 229*3e12c5d1SDavid du Colombier if(strcmp(msg, "alarm") == 0) 230*3e12c5d1SDavid du Colombier noted(NCONT); 231*3e12c5d1SDavid du Colombier else 232*3e12c5d1SDavid du Colombier noted(NDFLT); 233*3e12c5d1SDavid du Colombier } 234*3e12c5d1SDavid du Colombier static int 235*3e12c5d1SDavid du Colombier netquery(DN *dp, int type, RR *nsrp, Request *reqp) 236*3e12c5d1SDavid du Colombier { 237*3e12c5d1SDavid du Colombier int fd, i, j, len; 238*3e12c5d1SDavid du Colombier ulong req; 239*3e12c5d1SDavid du Colombier RR *rp; 240*3e12c5d1SDavid du Colombier Dest *p, *l; 241*3e12c5d1SDavid du Colombier DN *ndp; 242*3e12c5d1SDavid du Colombier Dest dest[Maxdest]; 243*3e12c5d1SDavid du Colombier DNSmsg m; 244*3e12c5d1SDavid du Colombier uchar obuf[Maxudp+Udphdrlen]; 245*3e12c5d1SDavid du Colombier uchar ibuf[Maxudp+Udphdrlen]; 246*3e12c5d1SDavid du Colombier 247*3e12c5d1SDavid du Colombier slave(reqp); 248*3e12c5d1SDavid du Colombier 249*3e12c5d1SDavid du Colombier /* get the addresses */ 250*3e12c5d1SDavid du Colombier l = dest; 251*3e12c5d1SDavid du Colombier for(; nsrp && nsrp->type == Tns; nsrp = nsrp->next){ 252*3e12c5d1SDavid du Colombier rp = rrlookup(nsrp->host, Ta); 253*3e12c5d1SDavid du Colombier if(rp == 0) 254*3e12c5d1SDavid du Colombier rp = dblookup(nsrp->host->name, Cin, Ta, 0); 255*3e12c5d1SDavid du Colombier for(; rp && rp->type == Ta; rp = rp->next){ 256*3e12c5d1SDavid du Colombier if(l >= &dest[Maxdest]) 257*3e12c5d1SDavid du Colombier break; 258*3e12c5d1SDavid du Colombier parseip(l->a, rp->ip->name); 259*3e12c5d1SDavid du Colombier l->nx = 0; 260*3e12c5d1SDavid du Colombier l->s = nsrp->host; 261*3e12c5d1SDavid du Colombier l++; 262*3e12c5d1SDavid du Colombier } 263*3e12c5d1SDavid du Colombier } 264*3e12c5d1SDavid du Colombier 265*3e12c5d1SDavid du Colombier /* pack request into a message */ 266*3e12c5d1SDavid du Colombier req = reqno++; 267*3e12c5d1SDavid du Colombier len = mkreq(dp, type, obuf, req); 268*3e12c5d1SDavid du Colombier 269*3e12c5d1SDavid du Colombier /* 270*3e12c5d1SDavid du Colombier * transmit requests and wait for answers. 271*3e12c5d1SDavid du Colombier * at most 3 attempts to each address. 272*3e12c5d1SDavid du Colombier * each cycle send one more message than the previous. 273*3e12c5d1SDavid du Colombier */ 274*3e12c5d1SDavid du Colombier fd = udpport(); 275*3e12c5d1SDavid du Colombier notify(ding); 276*3e12c5d1SDavid du Colombier for(i = 1;; i++){ 277*3e12c5d1SDavid du Colombier /* send to i destinations */ 278*3e12c5d1SDavid du Colombier p = dest; 279*3e12c5d1SDavid du Colombier for(j = 0; j < i; j++){ 280*3e12c5d1SDavid du Colombier /* skip destinations we've finished with */ 281*3e12c5d1SDavid du Colombier for(; p < l; p++) 282*3e12c5d1SDavid du Colombier if(p->nx < Maxtrans) 283*3e12c5d1SDavid du Colombier break; 284*3e12c5d1SDavid du Colombier if(p >= l) 285*3e12c5d1SDavid du Colombier break; 286*3e12c5d1SDavid du Colombier 287*3e12c5d1SDavid du Colombier p->nx++; 288*3e12c5d1SDavid du Colombier memmove(obuf, p->a, sizeof(p->a)); 289*3e12c5d1SDavid du Colombier if(write(fd, obuf, len + Udphdrlen) < 0) 290*3e12c5d1SDavid du Colombier fatal("sending udp msg %r"); 291*3e12c5d1SDavid du Colombier p++; 292*3e12c5d1SDavid du Colombier } 293*3e12c5d1SDavid du Colombier if(j == 0) 294*3e12c5d1SDavid du Colombier break; /* no destinations left */ 295*3e12c5d1SDavid du Colombier 296*3e12c5d1SDavid du Colombier /* wait a fixed time for replies */ 297*3e12c5d1SDavid du Colombier alarm(1000); 298*3e12c5d1SDavid du Colombier for(;;){ 299*3e12c5d1SDavid du Colombier if(readreq(fd, dp, type, req, ibuf, &m) < 0) 300*3e12c5d1SDavid du Colombier break; /* timed out */ 301*3e12c5d1SDavid du Colombier 302*3e12c5d1SDavid du Colombier /* remove all addrs of responding server from list */ 303*3e12c5d1SDavid du Colombier for(p = dest; p < l; p++) 304*3e12c5d1SDavid du Colombier if(memcmp(p->a, ibuf, sizeof(p->a)) == 0){ 305*3e12c5d1SDavid du Colombier ndp = p->s; 306*3e12c5d1SDavid du Colombier for(p = dest; p < l; p++) 307*3e12c5d1SDavid du Colombier if(p->s == ndp) 308*3e12c5d1SDavid du Colombier p->nx = Maxtrans; 309*3e12c5d1SDavid du Colombier break; 310*3e12c5d1SDavid du Colombier } 311*3e12c5d1SDavid du Colombier 312*3e12c5d1SDavid du Colombier /* incorporate answers */ 313*3e12c5d1SDavid du Colombier if(m.an) 314*3e12c5d1SDavid du Colombier rrattach(m.an, m.flags & Fauth); 315*3e12c5d1SDavid du Colombier if(m.ar) 316*3e12c5d1SDavid du Colombier rrattach(m.ar, 0); 317*3e12c5d1SDavid du Colombier 318*3e12c5d1SDavid du Colombier /* 319*3e12c5d1SDavid du Colombier * Any reply from an authoritative server, 320*3e12c5d1SDavid du Colombier * or a positive reply terminates the search 321*3e12c5d1SDavid du Colombier */ 322*3e12c5d1SDavid du Colombier if(m.an || (m.flags & Fauth)){ 323*3e12c5d1SDavid du Colombier alarm(0); 324*3e12c5d1SDavid du Colombier close(fd); 325*3e12c5d1SDavid du Colombier return 1; 326*3e12c5d1SDavid du Colombier } 327*3e12c5d1SDavid du Colombier 328*3e12c5d1SDavid du Colombier /* 329*3e12c5d1SDavid du Colombier * if we've been given better name servers 330*3e12c5d1SDavid du Colombier * recurse 331*3e12c5d1SDavid du Colombier */ 332*3e12c5d1SDavid du Colombier if(m.ns){ 333*3e12c5d1SDavid du Colombier alarm(0); 334*3e12c5d1SDavid du Colombier close(fd); 335*3e12c5d1SDavid du Colombier ndp = m.ns->owner; 336*3e12c5d1SDavid du Colombier rrattach(m.ns, 0); 337*3e12c5d1SDavid du Colombier return netquery(dp, type, rrlookup(ndp, Tns), reqp); 338*3e12c5d1SDavid du Colombier } 339*3e12c5d1SDavid du Colombier } 340*3e12c5d1SDavid du Colombier alarm(0); 341*3e12c5d1SDavid du Colombier } 342*3e12c5d1SDavid du Colombier alarm(0); 343*3e12c5d1SDavid du Colombier close(fd); 344*3e12c5d1SDavid du Colombier return 0; 345*3e12c5d1SDavid du Colombier } 346