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