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 /* 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 if(rp->marker) 465 continue; 466 arp = rrlookup(rp->host, Ta, NOneg); 467 if(arp){ 468 rp->marker = 1; 469 break; 470 } 471 arp = dblookup(rp->host->name, Cin, Ta, 0, 0); 472 if(arp){ 473 rp->marker = 1; 474 break; 475 } 476 } 477 478 /* 479 * if the cache and database lookup didn't find any new 480 * server addresses, try resolving one via the network. 481 * Mark any we try to resolve so we don't try a second time. 482 */ 483 if(arp == 0){ 484 for(rp = nsrp; rp; rp = rp->next){ 485 if(rp->marker) 486 continue; 487 rp->marker = 1; 488 489 /* 490 * avoid loops looking up a server under itself 491 */ 492 if(subsume(rp->owner->name, rp->host->name)) 493 continue; 494 495 arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0, depth+1, Recurse, 1, 0); 496 rrfreelist(rrremneg(&arp)); 497 if(arp) 498 break; 499 } 500 } 501 502 /* use any addresses that we found */ 503 for(trp = arp; trp; trp = trp->next){ 504 if(nd >= Maxdest) 505 break; 506 cur = &dest[nd]; 507 parseip(cur->a, trp->ip->name); 508 if(ipisbm(cur->a)) 509 continue; 510 cur->nx = 0; 511 cur->s = trp->owner; 512 cur->code = Rtimeout; 513 nd++; 514 } 515 rrfreelist(arp); 516 return nd; 517 } 518 519 /* 520 * cache negative responses 521 */ 522 static void 523 cacheneg(DN *dp, int type, int rcode, RR *soarr) 524 { 525 RR *rp; 526 DN *soaowner; 527 ulong ttl; 528 529 /* no cache time specified, don' make anything up */ 530 if(soarr != nil){ 531 if(soarr->next != nil){ 532 rrfreelist(soarr->next); 533 soarr->next = nil; 534 } 535 soaowner = soarr->owner; 536 } else 537 soaowner = nil; 538 539 /* the attach can cause soarr to be freed so mine it now */ 540 if(soarr != nil && soarr->soa != nil) 541 ttl = soarr->soa->minttl+now; 542 else 543 ttl = 5*Min; 544 545 /* add soa and negative RR to the database */ 546 rrattach(soarr, 1); 547 548 rp = rralloc(type); 549 rp->owner = dp; 550 rp->negative = 1; 551 rp->negsoaowner = soaowner; 552 rp->negrcode = rcode; 553 rp->ttl = ttl; 554 rrattach(rp, 1); 555 } 556 557 /* 558 * query name servers. If the name server returns a pointer to another 559 * name server, recurse. 560 */ 561 static int 562 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth, uchar *ibuf, uchar *obuf) 563 { 564 int ndest, j, len, replywaits, rv; 565 ushort req; 566 RR *tp, *soarr; 567 Dest *p, *l, *np; 568 DN *ndp; 569 Dest dest[Maxdest]; 570 DNSmsg m; 571 ulong endtime; 572 573 /* pack request into a message */ 574 req = rand(); 575 len = mkreq(dp, type, obuf, req); 576 577 /* no server addresses yet */ 578 l = dest; 579 580 /* 581 * transmit requests and wait for answers. 582 * at most Maxtrans attempts to each address. 583 * each cycle send one more message than the previous. 584 */ 585 for(ndest = 1; ndest < Maxdest; ndest++){ 586 p = dest; 587 588 endtime = time(0); 589 if(endtime >= reqp->aborttime) 590 break; 591 592 /* get a server address if we need one */ 593 if(ndest > l - p){ 594 j = serveraddrs(nsrp, dest, l - p, depth, reqp); 595 l = &dest[j]; 596 } 597 598 /* no servers, punt */ 599 if(l == dest) 600 break; 601 602 /* send to first 'ndest' destinations */ 603 j = 0; 604 for(; p < &dest[ndest] && p < l; p++){ 605 /* skip destinations we've finished with */ 606 if(p->nx >= Maxtrans) 607 continue; 608 609 j++; 610 611 /* exponential backoff of requests */ 612 if((1<<p->nx) > ndest) 613 continue; 614 615 memmove(obuf, p->a, sizeof(p->a)); 616 if(debug) 617 logsend(reqp->id, depth, obuf, p->s->name, 618 dp->name, type); 619 if(write(fd, obuf, len + Udphdrsize) < 0) 620 warning("sending udp msg %r"); 621 p->nx++; 622 } 623 if(j == 0) 624 break; /* no destinations left */ 625 626 /* wait up to 5 seconds for replies */ 627 endtime = time(0) + 5; 628 if(endtime > reqp->aborttime) 629 endtime = reqp->aborttime; 630 631 for(replywaits = 0; replywaits < ndest; replywaits++){ 632 if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0) 633 break; /* timed out */ 634 635 /* find responder */ 636 for(p = dest; p < l; p++) 637 if(memcmp(p->a, ibuf, sizeof(p->a)) == 0) 638 break; 639 640 /* remove all addrs of responding server from list */ 641 for(np = dest; np < l; np++) 642 if(np->s == p->s) 643 p->nx = Maxtrans; 644 645 /* ignore any error replies */ 646 if((m.flags & Rmask) == Rserver){ 647 rrfreelist(m.qd); 648 rrfreelist(m.an); 649 rrfreelist(m.ar); 650 rrfreelist(m.ns); 651 if(p != l) 652 p->code = Rserver; 653 continue; 654 } 655 656 /* ignore any bad delegations */ 657 if(m.ns && baddelegation(m.ns, nsrp, ibuf)){ 658 rrfreelist(m.ns); 659 m.ns = nil; 660 if(m.an == nil){ 661 rrfreelist(m.qd); 662 rrfreelist(m.ar); 663 if(p != l) 664 p->code = Rserver; 665 continue; 666 } 667 } 668 669 670 /* remove any soa's from the authority section */ 671 soarr = rrremtype(&m.ns, Tsoa); 672 673 /* incorporate answers */ 674 if(m.an) 675 rrattach(m.an, (m.flags & Fauth) ? 1 : 0); 676 if(m.ar) 677 rrattach(m.ar, 0); 678 if(m.ns){ 679 ndp = m.ns->owner; 680 rrattach(m.ns, 0); 681 } else 682 ndp = 0; 683 684 /* free the question */ 685 if(m.qd) 686 rrfreelist(m.qd); 687 688 /* 689 * Any reply from an authoritative server, 690 * or a positive reply terminates the search 691 */ 692 if(m.an != nil || (m.flags & Fauth)){ 693 if(m.an == nil && (m.flags & Rmask) == Rname) 694 dp->nonexistent = Rname; 695 else 696 dp->nonexistent = 0; 697 698 /* 699 * cache any negative responses, free soarr 700 */ 701 if((m.flags & Fauth) && m.an == nil) 702 cacheneg(dp, type, (m.flags & Rmask), soarr); 703 else 704 rrfreelist(soarr); 705 return 1; 706 } 707 rrfreelist(soarr); 708 709 /* 710 * if we've been given better name servers 711 * recurse 712 */ 713 if(m.ns){ 714 tp = rrlookup(ndp, Tns, NOneg); 715 if(!contains(nsrp, tp)){ 716 rv = netquery(dp, type, tp, reqp, depth+1); 717 rrfreelist(tp); 718 return rv; 719 } else 720 rrfreelist(tp); 721 } 722 } 723 } 724 725 /* if all servers returned failure, propogate it */ 726 dp->nonexistent = Rserver; 727 for(p = dest; p < l; p++) 728 if(p->code != Rserver) 729 dp->nonexistent = 0; 730 731 return 0; 732 } 733 734 static int 735 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth) 736 { 737 uchar *obuf; 738 uchar *ibuf; 739 RR *rp; 740 int fd, rv; 741 742 if(depth > 12) 743 return 0; 744 745 /* use alloced buffers rather than ones from the stack */ 746 ibuf = emalloc(Maxudpin+Udphdrsize); 747 obuf = emalloc(Maxudp+Udphdrsize); 748 749 slave(reqp); 750 751 /* prepare server RR's for incremental lookup */ 752 for(rp = nsrp; rp; rp = rp->next) 753 rp->marker = 0; 754 755 fd = udpport(); 756 if(fd < 0) 757 return 0; 758 rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf); 759 close(fd); 760 free(ibuf); 761 free(obuf); 762 763 return rv; 764 } 765