1 #include <u.h> 2 #include <libc.h> 3 #include "dns.h" 4 #include "ip.h" 5 6 enum 7 { 8 Maxdest= 24, /* maximum destinations for a request message */ 9 Maxtrans= 3, /* maximum transmissions to a server */ 10 }; 11 12 static int netquery(DN*, int, RR*, Request*, int); 13 static RR* dnresolve1(char*, int, int, Request*, int, int); 14 15 char *LOG = "dns"; 16 17 /* 18 * lookup 'type' info for domain name 'name'. If it doesn't exist, try 19 * looking it up as a canonical name. 20 */ 21 RR* 22 dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, int recurse, int rooted, int *status) 23 { 24 RR *rp, *nrp, *drp; 25 DN *dp; 26 int loops; 27 char nname[Domlen]; 28 29 if(status) 30 *status = 0; 31 32 /* 33 * hack for systems that don't have resolve search 34 * lists. Just look up the simple name in the database. 35 */ 36 if(!rooted && strchr(name, '.') == 0){ 37 rp = nil; 38 drp = domainlist(class); 39 for(nrp = drp; nrp != nil; nrp = nrp->next){ 40 snprint(nname, sizeof(nname), "%s.%s", name, nrp->ptr->name); 41 rp = dnresolve(nname, class, type, req,cn, depth, recurse, rooted, status); 42 if(rp != nil) 43 break; 44 } 45 if(drp != nil) 46 rrfree(drp); 47 return rp; 48 } 49 50 /* 51 * try the name directly 52 */ 53 rp = dnresolve1(name, class, type, req, depth, recurse); 54 if(rp) 55 return randomize(rp); 56 57 /* try it as a canonical name if we weren't told the name didn't exist */ 58 dp = dnlookup(name, class, 0); 59 if(type != Tptr && dp->nonexistent != Rname){ 60 for(loops=0; rp == nil && loops < 32; loops++){ 61 rp = dnresolve1(name, class, Tcname, req, depth, recurse); 62 if(rp == nil) 63 break; 64 65 if(rp->negative){ 66 rrfreelist(rp); 67 rp = nil; 68 break; 69 } 70 71 name = rp->host->name; 72 if(cn) 73 rrcat(cn, rp); 74 else 75 rrfreelist(rp); 76 77 rp = dnresolve1(name, class, type, req, depth, recurse); 78 } 79 } 80 81 /* distinction between not found and not good */ 82 if(rp == 0 && status != 0 && dp->nonexistent != 0) 83 *status = dp->nonexistent; 84 85 return randomize(rp); 86 } 87 88 static RR* 89 dnresolve1(char *name, int class, int type, Request *req, int depth, int recurse) 90 { 91 DN *dp, *nsdp; 92 RR *rp, *nsrp, *dbnsrp; 93 char *cp; 94 95 if(debug) 96 syslog(0, LOG, "dnresolve1 %s %d %d", name, type, class); 97 98 /* only class Cin implemented so far */ 99 if(class != Cin) 100 return 0; 101 102 dp = dnlookup(name, class, 1); 103 104 /* 105 * Try the cache first 106 */ 107 rp = rrlookup(dp, type, OKneg); 108 if(rp){ 109 if(rp->db){ 110 /* unauthenticated db entries are hints */ 111 if(rp->auth) 112 return rp; 113 } else { 114 /* cached entry must still be valid */ 115 if(rp->ttl > now){ 116 /* but Tall entries are special */ 117 if(type != Tall || rp->query == Tall) 118 return rp; 119 } 120 } 121 } 122 rrfreelist(rp); 123 124 /* 125 * try the cache for a canonical name. if found punt 126 * since we'll find it during the canonical name search 127 * in dnresolve(). 128 */ 129 if(type != Tcname){ 130 rp = rrlookup(dp, Tcname, NOneg); 131 rrfreelist(rp); 132 if(rp) 133 return 0; 134 } 135 136 /* 137 * if we're running as just a resolver, go to our 138 * designated name servers 139 */ 140 if(resolver){ 141 nsrp = randomize(getdnsservers(class)); 142 if(nsrp != nil) { 143 if(netquery(dp, type, nsrp, req, depth+1)){ 144 rrfreelist(nsrp); 145 return rrlookup(dp, type, OKneg); 146 } 147 rrfreelist(nsrp); 148 } 149 } 150 151 /* 152 * walk up the domain name looking for 153 * a name server for the domain. 154 */ 155 for(cp = name; cp; cp = walkup(cp)){ 156 /* 157 * if this is a local (served by us) domain, 158 * return answer 159 */ 160 dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0)); 161 if(dbnsrp && dbnsrp->local){ 162 rp = dblookup(name, class, type, 1, dbnsrp->ttl); 163 rrfreelist(dbnsrp); 164 return rp; 165 } 166 167 /* 168 * if recursion isn't set, just accept local 169 * entries 170 */ 171 if(recurse == Dontrecurse){ 172 if(dbnsrp) 173 rrfreelist(dbnsrp); 174 continue; 175 } 176 177 /* look for ns in cache */ 178 nsdp = dnlookup(cp, class, 0); 179 nsrp = nil; 180 if(nsdp) 181 nsrp = randomize(rrlookup(nsdp, Tns, NOneg)); 182 183 /* if the entry timed out, ignore it */ 184 if(nsrp && nsrp->ttl < now){ 185 rrfreelist(nsrp); 186 nsrp = nil; 187 } 188 189 if(nsrp){ 190 rrfreelist(dbnsrp); 191 192 /* try the name servers found in cache */ 193 if(netquery(dp, type, nsrp, req, depth+1)){ 194 rrfreelist(nsrp); 195 return rrlookup(dp, type, OKneg); 196 } 197 rrfreelist(nsrp); 198 continue; 199 } 200 201 /* use ns from db */ 202 if(dbnsrp){ 203 /* try the name servers found in db */ 204 if(netquery(dp, type, dbnsrp, req, depth+1)){ 205 /* we got an answer */ 206 rrfreelist(dbnsrp); 207 return rrlookup(dp, type, NOneg); 208 } 209 rrfreelist(dbnsrp); 210 } 211 } 212 213 /* settle for a non-authoritative answer */ 214 rp = rrlookup(dp, type, OKneg); 215 if(rp) 216 return rp; 217 218 /* noone answered. try the database, we might have a chance. */ 219 return dblookup(name, class, type, 0, 0); 220 } 221 222 /* 223 * walk a domain name one element to the right. return a pointer to that element. 224 * in other words, return a pointer to the parent domain name. 225 */ 226 char* 227 walkup(char *name) 228 { 229 char *cp; 230 231 cp = strchr(name, '.'); 232 if(cp) 233 return cp+1; 234 else if(*name) 235 return ""; 236 else 237 return 0; 238 } 239 240 /* 241 * Get a udpport for requests and replies. Put the port 242 * into "headers" mode. 243 */ 244 static char *hmsg = "headers"; 245 246 static int 247 udpport(void) 248 { 249 int fd, ctl; 250 char ds[64]; 251 char adir[64]; 252 253 /* get a udp port */ 254 snprint(ds, sizeof(ds), "%s/udp!*!0", mntpt); 255 ctl = announce(ds, adir); 256 if(ctl < 0){ 257 /* warning("can't get udp port"); */ 258 return -1; 259 } 260 261 /* turn on header style interface */ 262 if(write(ctl, hmsg, strlen(hmsg)) , 0){ 263 close(ctl); 264 warning(hmsg); 265 return -1; 266 } 267 268 /* grab the data file */ 269 snprint(ds, sizeof(ds), "%s/data", adir); 270 fd = open(ds, ORDWR); 271 close(ctl); 272 if(fd < 0){ 273 warning("can't open udp port: %r"); 274 return -1; 275 } 276 277 return fd; 278 } 279 280 static int 281 mkreq(DN *dp, int type, uchar *buf, ushort reqno) 282 { 283 DNSmsg m; 284 int len; 285 Udphdr *uh = (Udphdr*)buf; 286 287 /* stuff port number into output buffer */ 288 memset(uh, 0, sizeof(*uh)); 289 hnputs(uh->rport, 53); 290 291 /* make request and convert it to output format */ 292 memset(&m, 0, sizeof(m)); 293 m.flags = Frecurse; 294 // m.flags = resolver ? Frecurse : 0; 295 m.id = reqno; 296 m.qd = rralloc(type); 297 m.qd->owner = dp; 298 m.qd->type = type; 299 len = convDNS2M(&m, &buf[Udphdrsize], Maxudp); 300 if(len < 0) 301 abort(); /* "can't convert" */; 302 rrfree(m.qd); 303 return len; 304 } 305 306 /* for alarms in readreply */ 307 static void 308 ding(void *x, char *msg) 309 { 310 USED(x); 311 if(strcmp(msg, "alarm") == 0) 312 noted(NCONT); 313 else 314 noted(NDFLT); 315 } 316 317 static void 318 freeanswers(DNSmsg *mp) 319 { 320 rrfreelist(mp->qd); 321 rrfreelist(mp->an); 322 rrfreelist(mp->ns); 323 rrfreelist(mp->ar); 324 } 325 326 /* 327 * read replies to a request. ignore any of the wrong type. wait at most 5 seconds. 328 */ 329 static int 330 readreply(int fd, DN *dp, int type, ushort req, 331 uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp) 332 { 333 char *err; 334 int len; 335 ulong now; 336 RR *rp; 337 338 notify(ding); 339 340 for(; ; freeanswers(mp)){ 341 now = time(0); 342 if(now >= endtime) 343 return -1; /* timed out */ 344 345 /* timed read */ 346 alarm((endtime - now) * 1000); 347 len = read(fd, ibuf, Udphdrsize+Maxudpin); 348 alarm(0); 349 len -= Udphdrsize; 350 if(len < 0) 351 return -1; /* timed out */ 352 353 /* convert into internal format */ 354 memset(mp, 0, sizeof(*mp)); 355 err = convM2DNS(&ibuf[Udphdrsize], len, mp); 356 if(err){ 357 syslog(0, LOG, "input err %s: %I", err, ibuf); 358 continue; 359 } 360 if(debug) 361 logreply(reqp->id, ibuf, mp); 362 363 /* answering the right question? */ 364 if(mp->id != req){ 365 syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id, 366 mp->id, req, ibuf); 367 continue; 368 } 369 if(mp->qd == 0){ 370 syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf); 371 continue; 372 } 373 if(mp->qd->owner != dp){ 374 syslog(0, LOG, "%d: owner %s instead of %s: %I", reqp->id, 375 mp->qd->owner->name, dp->name, ibuf); 376 continue; 377 } 378 if(mp->qd->type != type){ 379 syslog(0, LOG, "%d: type %d instead of %d: %I", reqp->id, 380 mp->qd->type, type, ibuf); 381 continue; 382 } 383 384 /* remember what request this is in answer to */ 385 for(rp = mp->an; rp; rp = rp->next) 386 rp->query = type; 387 388 return 0; 389 } 390 391 return 0; /* never reached */ 392 } 393 394 /* 395 * return non-0 if first list includes second list 396 */ 397 int 398 contains(RR *rp1, RR *rp2) 399 { 400 RR *trp1, *trp2; 401 402 for(trp2 = rp2; trp2; trp2 = trp2->next){ 403 for(trp1 = rp1; trp1; trp1 = trp1->next){ 404 if(trp1->type == trp2->type) 405 if(trp1->host == trp2->host) 406 if(trp1->owner == trp2->owner) 407 break; 408 } 409 if(trp1 == 0) 410 return 0; 411 } 412 413 return 1; 414 } 415 416 417 typedef struct Dest Dest; 418 struct Dest 419 { 420 uchar a[IPaddrlen]; /* ip address */ 421 DN *s; /* name server */ 422 int nx; /* number of transmissions */ 423 int code; 424 }; 425 426 /* 427 * Get next server address 428 */ 429 static int 430 serveraddrs(RR *nsrp, Dest *dest, int nd, int depth, Request *reqp) 431 { 432 RR *rp, *arp, *trp; 433 Dest *cur; 434 435 if(nd >= Maxdest) 436 return 0; 437 438 /* 439 * look for a server whose address we already know. 440 * if we find one, mark it so we ignore this on 441 * subsequent passes. 442 */ 443 arp = 0; 444 for(rp = nsrp; rp; rp = rp->next){ 445 if(rp->marker) 446 continue; 447 arp = rrlookup(rp->host, Ta, NOneg); 448 if(arp){ 449 rp->marker = 1; 450 break; 451 } 452 arp = dblookup(rp->host->name, Cin, Ta, 0, 0); 453 if(arp){ 454 rp->marker = 1; 455 break; 456 } 457 } 458 459 /* 460 * if the cache and database lookup didn't find any new 461 * server addresses, try resolving one via the network. 462 * Mark any we try to resolve so we don't try a second time. 463 */ 464 if(arp == 0){ 465 for(rp = nsrp; rp; rp = rp->next){ 466 if(rp->marker) 467 continue; 468 rp->marker = 1; 469 470 /* 471 * avoid loops looking up a server under itself 472 */ 473 if(subsume(rp->owner->name, rp->host->name)) 474 continue; 475 476 arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0, depth+1, Recurse, 1, 0); 477 rrfreelist(rrremneg(&arp)); 478 if(arp) 479 break; 480 } 481 } 482 483 /* use any addresses that we found */ 484 for(trp = arp; trp; trp = trp->next){ 485 if(nd >= Maxdest) 486 break; 487 cur = &dest[nd++]; 488 parseip(cur->a, trp->ip->name); 489 cur->nx = 0; 490 cur->s = trp->owner; 491 cur->code = Rtimeout; 492 } 493 rrfreelist(arp); 494 return nd; 495 } 496 497 /* 498 * cache negative responses 499 */ 500 static void 501 cacheneg(DN *dp, int type, int rcode, RR *soarr) 502 { 503 RR *rp; 504 DN *soaowner; 505 ulong ttl; 506 507 /* no cache time specified, don' make anything up */ 508 if(soarr != nil){ 509 if(soarr->next != nil){ 510 rrfreelist(soarr->next); 511 soarr->next = nil; 512 } 513 soaowner = soarr->owner; 514 } else 515 soaowner = nil; 516 517 /* the attach can cause soarr to be freed so mine it now */ 518 if(soarr != nil && soarr->soa != nil) 519 ttl = soarr->soa->minttl+now; 520 else 521 ttl = 5*Min; 522 523 /* add soa and negative RR to the database */ 524 rrattach(soarr, 1); 525 526 rp = rralloc(type); 527 rp->owner = dp; 528 rp->negative = 1; 529 rp->negsoaowner = soaowner; 530 rp->negrcode = rcode; 531 rp->ttl = ttl; 532 rrattach(rp, 1); 533 } 534 535 /* 536 * query name servers. If the name server returns a pointer to another 537 * name server, recurse. 538 */ 539 static int 540 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth, uchar *ibuf, uchar *obuf) 541 { 542 int ndest, j, len, replywaits, rv; 543 ushort req; 544 RR *tp, *soarr; 545 Dest *p, *l, *np; 546 DN *ndp; 547 Dest dest[Maxdest]; 548 DNSmsg m; 549 ulong endtime; 550 551 /* pack request into a message */ 552 req = rand(); 553 len = mkreq(dp, type, obuf, req); 554 555 /* no server addresses yet */ 556 l = dest; 557 558 /* 559 * transmit requests and wait for answers. 560 * at most Maxtrans attempts to each address. 561 * each cycle send one more message than the previous. 562 */ 563 for(ndest = 1; ndest < Maxdest; ndest++){ 564 p = dest; 565 566 endtime = time(0); 567 if(endtime >= reqp->aborttime) 568 break; 569 570 /* get a server address if we need one */ 571 if(ndest > l - p){ 572 j = serveraddrs(nsrp, dest, l - p, depth, reqp); 573 l = &dest[j]; 574 } 575 576 /* no servers, punt */ 577 if(l == dest) 578 break; 579 580 /* send to first 'ndest' destinations */ 581 j = 0; 582 for(; p < &dest[ndest] && p < l; p++){ 583 /* skip destinations we've finished with */ 584 if(p->nx >= Maxtrans) 585 continue; 586 587 j++; 588 589 /* exponential backoff of requests */ 590 if((1<<p->nx) > ndest) 591 continue; 592 593 memmove(obuf, p->a, sizeof(p->a)); 594 if(debug) 595 logsend(reqp->id, depth, obuf, p->s->name, 596 dp->name, type); 597 if(write(fd, obuf, len + Udphdrsize) < 0) 598 warning("sending udp msg %r"); 599 p->nx++; 600 } 601 if(j == 0) 602 break; /* no destinations left */ 603 604 /* wait up to 5 seconds for replies */ 605 endtime = time(0) + 5; 606 if(endtime > reqp->aborttime) 607 endtime = reqp->aborttime; 608 609 for(replywaits = 0; replywaits < ndest; replywaits++){ 610 if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0) 611 break; /* timed out */ 612 613 /* find responder */ 614 for(p = dest; p < l; p++) 615 if(memcmp(p->a, ibuf, sizeof(p->a)) == 0) 616 break; 617 618 /* remove all addrs of responding server from list */ 619 for(np = dest; np < l; np++) 620 if(np->s == p->s) 621 p->nx = Maxtrans; 622 623 /* ignore any error replies */ 624 if((m.flags & Rmask) == Rserver){ 625 rrfreelist(m.qd); 626 rrfreelist(m.an); 627 rrfreelist(m.ar); 628 rrfreelist(m.ns); 629 if(p != l) 630 p->code = Rserver; 631 continue; 632 } 633 634 /* ignore any bad delegations */ 635 if(m.ns && baddelegation(m.ns, nsrp, ibuf)){ 636 rrfreelist(m.ns); 637 m.ns = nil; 638 if(m.an == nil){ 639 rrfreelist(m.qd); 640 rrfreelist(m.ar); 641 if(p != l) 642 p->code = Rserver; 643 continue; 644 } 645 } 646 647 648 /* remove any soa's from the authority section */ 649 soarr = rrremtype(&m.ns, Tsoa); 650 651 /* incorporate answers */ 652 if(m.an) 653 rrattach(m.an, (m.flags & Fauth) ? 1 : 0); 654 if(m.ar) 655 rrattach(m.ar, 0); 656 if(m.ns){ 657 ndp = m.ns->owner; 658 rrattach(m.ns, 0); 659 } else 660 ndp = 0; 661 662 /* free the question */ 663 if(m.qd) 664 rrfreelist(m.qd); 665 666 /* 667 * Any reply from an authoritative server, 668 * or a positive reply terminates the search 669 */ 670 if(m.an != nil || (m.flags & Fauth)){ 671 if(m.an == nil && (m.flags & Rmask) == Rname) 672 dp->nonexistent = Rname; 673 else 674 dp->nonexistent = 0; 675 676 /* 677 * cache any negative responses, free soarr 678 */ 679 if((m.flags & Fauth) && m.an == nil) 680 cacheneg(dp, type, (m.flags & Rmask), soarr); 681 else 682 rrfreelist(soarr); 683 return 1; 684 } 685 rrfreelist(soarr); 686 687 /* 688 * if we've been given better name servers 689 * recurse 690 */ 691 if(m.ns){ 692 tp = rrlookup(ndp, Tns, NOneg); 693 if(!contains(nsrp, tp)){ 694 rv = netquery(dp, type, tp, reqp, depth+1); 695 rrfreelist(tp); 696 return rv; 697 } else 698 rrfreelist(tp); 699 } 700 } 701 } 702 703 /* if all servers returned failure, propogate it */ 704 dp->nonexistent = Rserver; 705 for(p = dest; p < l; p++) 706 if(p->code != Rserver) 707 dp->nonexistent = 0; 708 709 return 0; 710 } 711 712 static int 713 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth) 714 { 715 uchar *obuf; 716 uchar *ibuf; 717 RR *rp; 718 int fd, rv; 719 720 if(depth > 12) 721 return 0; 722 723 /* use alloced buffers rather than ones from the stack */ 724 ibuf = emalloc(Maxudpin+Udphdrsize); 725 obuf = emalloc(Maxudp+Udphdrsize); 726 727 slave(reqp); 728 729 /* prepare server RR's for incremental lookup */ 730 for(rp = nsrp; rp; rp = rp->next) 731 rp->marker = 0; 732 733 fd = udpport(); 734 if(fd < 0) 735 return 0; 736 rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf); 737 close(fd); 738 free(ibuf); 739 free(obuf); 740 741 return rv; 742 } 743