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