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