1implement DNS; 2 3# 4# domain name service 5# 6# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. 7# 8# RFCs: 1034, 1035, 2181, 2308 9# 10# TO DO: 11# server side: 12# database; inmyzone; ptr generation; separate zone transfer 13# currently doesn't implement loony rules on case 14# limit work 15# check data 16# Call 17# ipv6 18# 19 20include "sys.m"; 21 sys: Sys; 22 stderr: ref Sys->FD; 23 24include "draw.m"; 25 26include "bufio.m"; 27 28include "srv.m"; 29 srv: Srv; 30 31include "ip.m"; 32 ip: IP; 33 IPaddrlen, IPaddr, IPv4off, Udphdrlen, Udpraddr, Udpladdr, Udprport, Udplport: import ip; 34 35include "arg.m"; 36 37include "attrdb.m"; 38 attrdb: Attrdb; 39 Db, Dbentry, Tuples: import attrdb; 40 41include "ipattr.m"; 42 ipattr: IPattr; 43 dbattr: import ipattr; 44 45include "keyring.m"; 46include "security.m"; 47 random: Random; 48 49include "dial.m"; 50 dial: Dial; 51 52DNS: module 53{ 54 init: fn(nil: ref Draw->Context, nil: list of string); 55}; 56 57Reply: adt 58{ 59 fid: int; 60 pid: int; 61 query: string; 62 attr: string; 63 addrs: list of string; 64 err: string; 65}; 66 67rlist: list of ref Reply; 68 69dnsfile := "/lib/ndb/local"; 70myname: string; 71mntpt := "/net"; 72DNSport: con 53; 73debug := 0; 74referdns := 0; 75usehost := 1; 76now: int; 77 78servers: list of string; 79 80# domain name from dns/db 81domain: string; 82dnsdomains: list of string; 83 84init(nil: ref Draw->Context, args: list of string) 85{ 86 sys = load Sys Sys->PATH; 87 stderr = sys->fildes(2); 88 arg := load Arg Arg->PATH; 89 if(arg == nil) 90 cantload(Arg->PATH); 91 dial = load Dial Dial->PATH; 92 if(dial == nil) 93 cantload(Dial->PATH); 94 arg->init(args); 95 arg->setusage("dns [-Drh] [-f dnsfile] [-x mntpt]"); 96 svcname := "#sdns"; 97 while((c := arg->opt()) != 0) 98 case c { 99 'D' => 100 debug = 1; 101 'f' => 102 dnsfile = arg->earg(); 103 'h' => 104 usehost = 0; 105 'r' => 106 referdns = 1; 107 'x' => 108 mntpt = arg->earg(); 109 svcname = "#sdns"+svcpt(mntpt); 110 * => 111 arg->usage(); 112 } 113 args = arg->argv(); 114 if(args != nil) 115 arg->usage(); 116 arg = nil; 117 118 if(usehost){ 119 srv = load Srv Srv->PATH; # hosted Inferno only 120 if(srv != nil) 121 srv->init(); 122 } 123 ip = load IP IP->PATH; 124 if(ip == nil) 125 cantload(IP->PATH); 126 ip->init(); 127 attrdb = load Attrdb Attrdb->PATH; 128 if(attrdb == nil) 129 cantload(Attrdb->PATH); 130 attrdb->init(); 131 ipattr = load IPattr IPattr->PATH; 132 if(ipattr == nil) 133 cantload(IPattr->PATH); 134 ipattr->init(attrdb, ip); 135 136 sys->pctl(Sys->NEWPGRP | Sys->FORKFD, nil); 137 138 random = load Random Random->PATH; 139 if(random == nil) 140 cantload(Random->PATH); 141 dnsid = random->randomint(Random->ReallyRandom); # avoid clashes 142 random = nil; 143 myname = sysname(); 144 stderr = sys->fildes(2); 145 readservers(); 146 now = time(); 147 sys->remove(svcname+"/dns"); 148 sys->unmount(svcname, mntpt); 149 publish(svcname); 150 if(sys->bind(svcname, mntpt, Sys->MBEFORE) < 0) 151 error(sys->sprint("can't bind #s on %s: %r", mntpt)); 152 file := sys->file2chan(mntpt, "dns"); 153 if(file == nil) 154 error(sys->sprint("can't make %s/dns: %r", mntpt)); 155 sync := chan of int; 156 spawn dnscache(sync); 157 <-sync; 158 spawn dns(file); 159} 160 161publish(dir: string) 162{ 163 d := Sys->nulldir; 164 d.mode = 8r777; 165 if(sys->wstat(dir, d) < 0) 166 sys->fprint(sys->fildes(2), "cs: can't publish %s: %r\n", dir); 167} 168 169svcpt(s: string): string 170{ 171 for(i:=0; i<len s; i++) 172 if(s[i] == '/') 173 s[i] = '_'; 174 return s; 175} 176 177cantload(s: string) 178{ 179 error(sys->sprint("can't load %s: %r", s)); 180} 181 182error(s: string) 183{ 184 sys->fprint(stderr, "dns: %s\n", s); 185 raise "fail:error"; 186} 187 188dns(file: ref Sys->FileIO) 189{ 190 pidc := chan of int; 191 donec := chan of ref Reply; 192 for(;;){ 193 alt { 194 (nil, buf, fid, wc) := <-file.write => 195 now = time(); 196 cleanfid(fid); # each write cancels previous requests 197 if(wc != nil){ 198 r := ref Reply; 199 r.fid = fid; 200 spawn request(r, buf, wc, pidc, donec); 201 r.pid = <-pidc; 202 rlist = r :: rlist; 203 } 204 205 (off, nbytes, fid, rc) := <-file.read => 206 now = time(); 207 if(rc != nil){ 208 r := findfid(fid); 209 if(r != nil) 210 reply(r, off, nbytes, rc); 211 else 212 rc <-= (nil, "unknown request"); 213 } 214 215 r := <-donec => 216 now = time(); 217 r.pid = 0; 218 if(r.err != nil) 219 cleanfid(r.fid); 220 } 221 } 222} 223 224findfid(fid: int): ref Reply 225{ 226 for(rl := rlist; rl != nil; rl = tl rl){ 227 r := hd rl; 228 if(r.fid == fid) 229 return r; 230 } 231 return nil; 232} 233 234cleanfid(fid: int) 235{ 236 rl := rlist; 237 rlist = nil; 238 for(; rl != nil; rl = tl rl){ 239 r := hd rl; 240 if(r.fid != fid) 241 rlist = r :: rlist; 242 else 243 killgrp(r.pid); 244 } 245} 246 247killgrp(pid: int) 248{ 249 if(pid != 0){ 250 fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); 251 if(fd == nil || sys->fprint(fd, "killgrp") < 0) 252 sys->fprint(stderr, "dns: can't killgrp %d: %r\n", pid); 253 } 254} 255 256request(r: ref Reply, data: array of byte, wc: chan of (int, string), pidc: chan of int, donec: chan of ref Reply) 257{ 258 pidc <-= sys->pctl(Sys->NEWPGRP, nil); 259 query := string data; 260 for(i := 0; i < len query; i++) 261 if(query[i] == ' ') 262 break; 263 r.query = query[0:i]; 264 for(; i < len query && query[i] == ' '; i++) 265 ; 266 r.attr = query[i:]; 267 attr := rrtype(r.attr); 268 if(attr < 0) 269 r.err = "unknown type"; 270 else 271 (r.addrs, r.err) = dnslookup(r.query, attr); 272 if(r.addrs == nil && r.err == nil) 273 r.err = "not found"; 274 if(r.err != nil){ 275 if(debug) 276 sys->fprint(stderr, "dns: %s: %s\n", query, r.err); 277 wc <-= (0, "dns: "+r.err); 278 } else 279 wc <-= (len data, nil); 280 donec <-= r; 281} 282 283reply(r: ref Reply, off: int, nbytes: int, rc: chan of (array of byte, string)) 284{ 285 if(r.err != nil || r.addrs == nil){ 286 rc <-= (nil, r.err); 287 return; 288 } 289 addr: string; 290 if(r.addrs != nil){ 291 addr = hd r.addrs; 292 r.addrs = tl r.addrs; 293 } 294 off = 0; # this version ignores offsets 295# rc <-= reads(r.query+" "+r.attr+" "+addr, off, nbytes); 296 rc <-= reads(addr, off, nbytes); 297} 298 299# 300# return the file2chan reply for a read of the given string 301# 302reads(str: string, off, nbytes: int): (array of byte, string) 303{ 304 bstr := array of byte str; 305 slen := len bstr; 306 if(off < 0 || off >= slen) 307 return (nil, nil); 308 if(off + nbytes > slen) 309 nbytes = slen - off; 310 if(nbytes <= 0) 311 return (nil, nil); 312 return (bstr[off:off+nbytes], nil); 313} 314 315sysname(): string 316{ 317 t := rf("/dev/sysname"); 318 if(t != nil) 319 return t; 320 t = rf("#e/sysname"); 321 if(t == nil){ 322 s := rf(mntpt+"/ndb"); 323 if(s != nil){ 324 db := Db.sopen(t); 325 if(db != nil){ 326 (e, nil) := db.find(nil, "sys"); 327 if(e != nil) 328 t = e.findfirst("sys"); 329 } 330 } 331 } 332 if(t != nil){ 333 fd := sys->open("/dev/sysname", Sys->OWRITE); 334 if(fd != nil) 335 sys->fprint(fd, "%s", t); 336 } 337 return t; 338} 339 340rf(name: string): string 341{ 342 fd := sys->open(name, Sys->OREAD); 343 buf := array[Sys->NAMEMAX] of byte; 344 n := sys->read(fd, buf, len buf); 345 if(n <= 0) 346 return nil; 347 return string buf[0:n]; 348} 349 350samefile(d1, d2: Sys->Dir): int 351{ 352 # ``it was black ... it was white! it was dark ... it was light! ah yes, i remember it well...'' 353 return d1.dev==d2.dev && d1.dtype==d2.dtype && 354 d1.qid.path==d2.qid.path && d1.qid.vers==d2.qid.vers && 355 d1.mtime==d2.mtime; 356} 357 358# 359# database 360# dnsdomain= suffix to add to unqualified unrooted names 361# dns= dns server to try 362# dom= domain name 363# ip= IP address 364# ns= name server 365# soa= 366# soa=delegated 367# infernosite= set of site-wide parameters 368# 369 370# 371# basic Domain Name Service resolver 372# 373 374laststat := 0; # time last stat'd (to reduce churn) 375dnsdb: ref Db; 376 377readservers(): list of string 378{ 379 if(laststat != 0 && now < laststat+2*60) 380 return servers; 381 laststat = now; 382 if(dnsdb == nil){ 383 db := Db.open(dnsfile); 384 if(db == nil){ 385 sys->fprint(stderr, "dns: can't open %s: %r\n", dnsfile); 386 return nil; 387 } 388 dyndb := Db.open(mntpt+"/ndb"); 389 if(dyndb != nil) 390 dnsdb = dyndb.append(db); 391 else 392 dnsdb = db; 393 }else{ 394 if(!dnsdb.changed()) 395 return servers; 396 dnsdb.reopen(); 397 } 398 if((l := dblooknet("sys", myname, "dnsdomain")) == nil) 399 l = dblook("infernosite", "", "dnsdomain"); 400 dnsdomains = "" :: l; 401 if((l = dblooknet("sys", myname, "dns")) == nil) 402 l = dblook("infernosite", "", "dns"); 403 servers = l; 404# zones := dblook("soa", "", "dom"); 405#printlist("zones", zones); 406 if(debug) 407 printlist("dnsdomains", dnsdomains); 408 if(debug) 409 printlist("servers", servers); 410 return servers; 411} 412 413printlist(w: string, l: list of string) 414{ 415 sys->print("%s:", w); 416 for(; l != nil; l = tl l) 417 sys->print(" %q", hd l); 418 sys->print("\n"); 419} 420 421dblookns(dom: string): list of ref RR 422{ 423 domns := dblook("dom", dom, "ns"); 424 hosts: list of ref RR; 425 for(; domns != nil; domns = tl domns){ 426 s := hd domns; 427 if(debug) 428 sys->print("dns db: dom=%s ns=%s\n", dom, s); 429 ipl: list of ref RR = nil; 430 addrs := dblook("dom", s, "ip"); 431 for(; addrs != nil; addrs = tl addrs){ 432 a := parseip(hd addrs); 433 if(a != nil){ 434 ipl = ref RR.A(s, Ta, Cin, now+60, 0, a) :: ipl; 435 if(debug) 436 sys->print("dom=%s ip=%s\n", s, hd addrs); 437 } 438 } 439 if(ipl != nil){ 440 # only use ones for which we've got addresses 441 cachec <-= (ipl, 0); 442 hosts = ref RR.Host(dom, Tns, Cin, now+60, 0, s) :: hosts; 443 } 444 } 445 if(hosts == nil){ 446 if(debug) 447 sys->print("dns: no ns for dom=%s in db\n", dom); 448 return nil; 449 } 450 cachec <-= (hosts, 0); 451 cachec <-= Sync; 452 return hosts; 453} 454 455defaultresolvers(): list of ref NS 456{ 457 resolvers := readservers(); 458 al: list of ref RR; 459 for(; resolvers != nil; resolvers = tl resolvers){ 460 nm := hd resolvers; 461 a := parseip(nm); 462 if(a == nil){ 463 # try looking it up as a domain name with an ip address 464 for(addrs := dblook("dom", nm, "ip"); addrs != nil; addrs = tl addrs){ 465 a = parseip(hd addrs); 466 if(a != nil) 467 al = ref RR.A("defaultns", Ta, Cin, now+60, 0, a) :: al; 468 } 469 }else 470 al = ref RR.A("defaultns", Ta, Cin, now+60, 0, a) :: al; 471 } 472 if(al == nil){ 473 if(debug) 474 sys->print("dns: no default resolvers\n"); 475 return nil; 476 } 477 return ref NS("defaultns", al, 1, now+60) :: nil; 478} 479 480dblook(attr: string, val: string, rattr: string): list of string 481{ 482 rl: list of string; 483 ptr: ref Attrdb->Dbptr; 484 for(;;){ 485 e: ref Dbentry; 486 (e, ptr) = dnsdb.findbyattr(ptr, attr, val, rattr); 487 if(e == nil) 488 break; 489 for(l := e.findbyattr(attr, val, rattr); l != nil; l = tl l){ 490 (nil, al) := hd l; 491 for(; al != nil; al = tl al) 492 if(!inlist((hd al).val, rl)) 493 rl = (hd al).val :: rl; 494 } 495 } 496 return reverse(rl); 497} 498 499# 500# starting from the ip= associated with attr=val, search over all 501# containing networks for the nearest values of rattr 502# 503dblooknet(attr: string, val: string, rattr: string): list of string 504{ 505#sys->print("dblooknet: %s=%s -> %s\n", attr, val, rattr); 506 (results, nil) := ipattr->findnetattrs(dnsdb, attr, val, rattr::nil); 507 rl: list of string; 508 for(; results != nil; results = tl results){ 509 (nil, nattrs) := hd results; 510 for(; nattrs != nil; nattrs = tl nattrs){ 511 na := hd nattrs; 512 if(na.name == rattr){ 513 for(pairs := na.pairs; pairs != nil; pairs = tl pairs) 514 if((s := (hd pairs).val) != nil && !inlist(s, rl)) 515 rl = s :: rl; 516 } 517 } 518 } 519 if(rl == nil) 520 return dblook(attr, val, rattr); 521 return reverse(rl); 522} 523 524inlist(s: string, l: list of string): int 525{ 526 for(; l != nil; l = tl l) 527 if(hd l == s) 528 return 1; 529 return 0; 530} 531 532reverse[T](l: list of T): list of T 533{ 534 r: list of T; 535 for(; l != nil; l = tl l) 536 r = hd l :: r; 537 return r; 538} 539 540append(h: list of string, s: string): list of string 541{ 542 if(h == nil) 543 return s :: nil; 544 return hd h :: append(tl h, s); 545} 546 547# 548# subset of RR types 549# 550Ta: con 1; 551Tns: con 2; 552Tcname: con 5; 553Tsoa: con 6; 554Tmb: con 7; 555Tptr: con 12; 556Thinfo: con 13; 557Tmx: con 15; 558Tall: con 255; 559 560# 561# classes 562# 563Cin: con 1; 564Call: con 255; 565 566# 567# opcodes 568# 569Oquery: con 0<<11; # normal query 570Oinverse: con 1<<11; # inverse query 571Ostatus: con 2<<11; # status request 572Omask: con 16rF<<11; # mask for opcode 573 574# 575# response codes 576# 577Rok: con 0; 578Rformat: con 1; # format error 579Rserver: con 2; # server failure 580Rname: con 3; # bad name 581Runimplemented: con 4; # unimplemented operation 582Rrefused: con 5; # permission denied, not supported 583Rmask: con 16rF; # mask for response 584 585# 586# other flags in opcode 587# 588Fresp: con 1<<15; # message is a response 589Fauth: con 1<<10; # true if an authoritative response 590Ftrunc: con 1<<9; # truncated message 591Frecurse: con 1<<8; # request recursion 592Fcanrecurse: con 1<<7; # server can recurse 593 594QR: adt { 595 name: string; 596 rtype: int; 597 class: int; 598 599 text: fn(q: self ref QR): string; 600}; 601 602RR: adt { 603 name: string; 604 rtype: int; 605 class: int; 606 ttl: int; 607 flags: int; 608 pick { 609 Error => 610 reason: string; # cached negative 611 Host => 612 host: string; 613 Hinfo => 614 cpu: string; 615 os: string; 616 Mx => 617 pref: int; 618 host: string; 619 Soa => 620 soa: ref SOA; 621 A or 622 Other => 623 rdata: array of byte; 624 } 625 626 islive: fn(r: self ref RR): int; 627 outlives: fn(a: self ref RR, b: ref RR): int; 628 match: fn(a: self ref RR, b: ref RR): int; 629 text: fn(a: self ref RR): string; 630}; 631 632SOA: adt { 633 mname: string; 634 rname: string; 635 serial: int; 636 refresh: int; 637 retry: int; 638 expire: int; 639 minttl: int; 640 641 text: fn(nil: self ref SOA): string; 642}; 643 644DNSmsg: adt { 645 id: int; 646 flags: int; 647 qd: list of ref QR; 648 an: list of ref RR; 649 ns: list of ref RR; 650 ar: list of ref RR; 651 err: string; 652 653 pack: fn(m: self ref DNSmsg, hdrlen: int): array of byte; 654 unpack: fn(a: array of byte): ref DNSmsg; 655 text: fn(m: self ref DNSmsg): string; 656}; 657 658NM: adt { 659 name: string; 660 rr: list of ref RR; 661 stats: ref Stats; 662}; 663 664Stats: adt { 665 rtt: int; 666}; 667 668cachec: chan of (list of ref RR, int); 669cache: array of list of ref NM; 670Sync: con (nil, 0); # empty list sent to ensure that last cache update done 671 672hash(s: string): array of list of ref NM 673{ 674 h := 0; 675 for(i:=0; i<len s; i++){ # hashpjw 676 c := s[i]; 677 if(c >= 'A' && c <= 'Z') 678 c += 'a'-'A'; 679 h = (h<<4) + c; 680 if((g := h & int 16rF0000000) != 0) 681 h ^= ((g>>24) & 16rFF) | g; 682 } 683 return cache[(h&~(1<<31))%len cache:]; 684} 685 686lower(s: string): string 687{ 688 for(i := 0; i < len s; i++){ 689 c := s[i]; 690 if(c >= 'A' && c <= 'Z'){ 691 n := s; 692 for(; i < len n; i++){ 693 c = n[i]; 694 if(c >= 'A' && c <= 'Z') 695 n[i] = c+('a'-'A'); 696 } 697 return n; 698 } 699 } 700 return s; 701} 702 703# 704# split rrl into a list of those RRs that match rr and a list of those that don't 705# 706partrrl(rr: ref RR, rrl: list of ref RR): (list of ref RR, list of ref RR) 707{ 708 m: list of ref RR; 709 nm: list of ref RR; 710 name := lower(rr.name); 711 for(; rrl != nil; rrl = tl rrl){ 712 t := hd rrl; 713 if(t.rtype == rr.rtype && t.class == rr.class && 714 (t.name == name || lower(t.name) == name)) 715 m = t :: m; 716 else 717 nm = t :: nm; 718 } 719 return (m, nm); 720} 721 722copyrrl(rrl: list of ref RR): list of ref RR 723{ 724 nl: list of ref RR; 725 for(; rrl != nil; rrl = tl rrl) 726 nl = ref *hd rrl :: nl; 727# return revrrl(rrl); 728 return rrl; # probably don't care about order 729} 730 731dnscache(sync: chan of int) 732{ 733 cache = array[32] of list of ref NM; 734 cachec = chan of (list of ref RR, int); 735 sync <-= sys->pctl(0, nil); 736 for(;;){ 737 (rrl, flags) := <-cachec; 738 #now = time(); 739 List: 740 while(rrl != nil){ 741 rrset: list of ref RR; 742 (rrset, rrl) = partrrl(hd rrl, rrl); 743 rr := hd rrset; 744 rr.flags = flags; 745 name := lower(rr.name); 746 hb := hash(name); 747 for(ces := hb[0]; ces != nil; ces = tl ces){ 748 ce := hd ces; 749 if(ce.name == name){ 750 rr.name = ce.name; # share string 751 x := ce.rr; 752 ce.rr = insertrrset(ce.rr, rr, rrset); 753 if(x != ce.rr && debug) 754 sys->print("insertrr %s:%s\n", name, rrsettext(rrset)); 755 continue List; 756 } 757 } 758 if(debug) 759 sys->print("newrr %s:%s\n", name, rrsettext(rrset)); 760 hb[0] = ref NM(name, rrset, nil) :: hb[0]; 761 } 762 } 763} 764 765lookcache(name: string, rtype: int, rclass: int): (list of ref RR, string) 766{ 767 results: list of ref RR; 768 name = lower(name); 769 for(ces := hash(name)[0]; ces != nil; ces = tl ces){ 770 ce := hd ces; 771 if(ce.name == name){ 772 for(zl := ce.rr; zl != nil; zl = tl zl){ 773 r := hd zl; 774 if((r.rtype == rtype || r.rtype == Tall || rtype == Tall) && r.class == rclass && r.name == name && r.islive()){ 775 pick ar := r { 776 Error => 777 if(rtype != Tall || ar.reason != "resource does not exist"){ 778 if(debug) 779 sys->print("lookcache: %s[%s]: !%s\n", name, rrtypename(rtype), ar.reason); 780 return (nil, ar.reason); 781 } 782 * => 783 results = ref *r :: results; 784 } 785 } 786 } 787 } 788 } 789 if(debug) 790 sys->print("lookcache: %s[%s]: %s\n", name, rrtypename(rtype), rrsettext(results)); 791 return (results, nil); 792} 793 794# 795# insert RRset new in existing list of RRsets rrl 796# if that's desirable (it's the whole RRset or nothing, see rfc2181) 797# 798insertrrset(rrl: list of ref RR, rr: ref RR, new: list of ref RR): list of ref RR 799{ 800 # TO DO: expire entries 801 match := 0; 802 for(l := rrl; l != nil; l = tl l){ 803 orr := hd l; 804 if(orr.rtype == rr.rtype && orr.class == rr.class){ # name already known to match 805 match = 1; 806 if(!orr.islive()) 807 break; # prefer new, unexpired data 808 if(tagof rr == tagof RR.Error && tagof orr != tagof RR.Error) 809 return rrl; # prefer unexpired positive 810 if(rr.flags & Fauth) 811 break; # prefer newly-arrived authoritative data 812 if(orr.flags & Fauth) 813 return rrl; # prefer authoritative data 814 if(orr.outlives(rr)) 815 return rrl; # prefer longer-lived data 816 } 817 } 818 if(match){ 819 # strip out existing RR set 820 l = rrl; 821 rrl = nil; 822 for(; l != nil; l = tl l){ 823 orr := hd l; 824 if((orr.rtype != rr.rtype || orr.class != rr.class) && orr.islive()){ 825 rrl = orr :: rrl;} 826 } 827 } 828 # add new RR set 829 for(; new != nil; new = tl new){ 830 nrr := hd new; 831 nrr.name = rr.name; 832 rrl = nrr :: rrl; 833 } 834 return rrl; 835} 836 837rrsettext(rrl: list of ref RR): string 838{ 839 s := ""; 840 for(; rrl != nil; rrl = tl rrl) 841 s += " ["+(hd rrl).text()+"]"; 842 return s; 843} 844 845QR.text(qr: self ref QR): string 846{ 847 s := sys->sprint("%s %s", qr.name, rrtypename(qr.rtype)); 848 if(qr.class != Cin) 849 s += sys->sprint(" [c=%d]", qr.class); 850 return s; 851} 852 853RR.islive(rr: self ref RR): int 854{ 855 return rr.ttl >= now; 856} 857 858RR.outlives(a: self ref RR, b: ref RR): int 859{ 860 return a.ttl > b.ttl; 861} 862 863RR.match(a: self ref RR, b: ref RR): int 864{ 865 # compare content, not ttl 866 return a.rtype == b.rtype && a.class == b.class && a.name == b.name; 867} 868 869RR.text(rr: self ref RR): string 870{ 871 s := sys->sprint("%s %s", rr.name, rrtypename(rr.rtype)); 872 pick ar := rr { 873 Host => 874 s += sys->sprint("\t%s", ar.host); 875 Hinfo => 876 s += sys->sprint("\t%s %s", ar.cpu, ar.os); 877 Mx => 878 s += sys->sprint("\t%ud %s", ar.pref, ar.host); 879 Soa => 880 s += sys->sprint("\t%s", ar.soa.text()); 881 A => 882 if(len ar.rdata == 4){ 883 a := ar.rdata; 884 s += sys->sprint("\t%d.%d.%d.%d", int a[0], int a[1], int a[2], int a[3]); 885 } 886 Error => 887 s += sys->sprint("\t!%s", ar.reason); 888 } 889 return s; 890} 891 892SOA.text(soa: self ref SOA): string 893{ 894 return sys->sprint("%s %s %ud %ud %ud %ud %ud", soa.mname, soa.rname, 895 soa.serial, soa.refresh, soa.retry, soa.expire, soa.minttl); 896} 897 898NS: adt { 899 name: string; 900 addr: list of ref RR; 901 canrecur: int; 902 ttl: int; 903}; 904 905dnslookup(name: string, attr: int): (list of string, string) 906{ 907 case attr { 908 Ta => 909 case dbattr(name) { 910 "sys" => 911 # could apply domains 912 ; 913 "dom" => 914 ; 915 * => 916 return (nil, "invalid host name"); 917 } 918 if(srv != nil){ # try the host's map first 919 l := srv->iph2a(name); 920 if(l != nil) 921 return (fullresult(name, "ip", l), nil); 922 } 923 Tptr => 924 if(srv != nil){ # try host's map first 925 l := srv->ipa2h(arpa2addr(name)); 926 if(l != nil) 927 return (fullresult(name, "ptr", l), nil); 928 } 929 } 930 return dnslookup1(name, attr); 931} 932 933fullresult(name: string, attr: string, l: list of string): list of string 934{ 935 rl: list of string; 936 for(; l != nil; l = tl l) 937 rl = sys->sprint("%s %s\t%s", name, attr, hd l) :: rl; 938 return reverse(rl); 939} 940 941arpa2addr(a: string): string 942{ 943 (nil, flds) := sys->tokenize(a, "."); 944 rl: list of string; 945 for(; flds != nil && lower(s := hd flds) != "in-addr"; flds = tl flds) 946 rl = s :: rl; 947 dom: string; 948 for(; rl != nil; rl = tl rl){ 949 if(dom != nil) 950 dom[len dom] = '.'; 951 dom += hd rl; 952 } 953 return dom; 954} 955 956dnslookup1(label: string, attr: int): (list of string, string) 957{ 958 (rrl, err) := fulldnsquery(label, attr, 0); 959 if(err != nil || rrl == nil) 960 return (nil, err); 961 r: list of string; 962 for(; rrl != nil; rrl = tl rrl) 963 r = (hd rrl).text() :: r; 964 return (reverse(r), nil); 965} 966 967trimdot(s: string): string 968{ 969 while(s != nil && s[len s - 1] == '.') 970 s = s[0:len s -1]; 971 return s; 972} 973 974parent(s: string): string 975{ 976 if(s == "") 977 return "."; 978 for(i := 0; i < len s; i++) 979 if(s[i] == '.') 980 return s[i+1:]; 981 return ""; 982} 983 984rootservers(): list of ref NS 985{ 986 slist := ref NS("a.root-servers.net", 987 ref RR.A("a.root-servers.net", Ta, Cin, 1<<31, 0, 988 array[] of {byte 198, byte 41, byte 0, byte 4})::nil, 0, 1<<31) :: nil; 989 return slist; 990} 991 992# 993# this broadly follows the algorithm given in RFC 1034 994# as adjusted and qualified by several other RFCs. 995# `label' is 1034's SNAME, `attr' is `STYPE' 996# 997# TO DO: 998# keep statistics for name servers 999 1000fulldnsquery(label: string, attr: int, depth: int): (list of ref RR, string) 1001{ 1002 slist: list of ref NS; 1003 fd: ref Sys->FD; 1004 if(depth > 10) 1005 return (nil, "dns loop"); 1006 ncname := 0; 1007Step1: 1008 for(tries:=0; tries<10; tries++){ 1009 1010 # 1. see if in local information, and if so, return it 1011 (x, err) := lookcache(label, attr, Cin); 1012 if(x != nil) 1013 return (x, nil); 1014 if(err != nil) 1015 return (nil, err); 1016 if(attr != Tcname){ 1017 if(++ncname > 10) 1018 return (nil, "cname alias loop"); 1019 (x, err) = lookcache(label, Tcname, Cin); 1020 if(x != nil){ 1021 pick rx := hd x { 1022 Host => 1023 label = rx.host; 1024 continue; 1025 } 1026 } 1027 } 1028 1029 # 2. find the best servers to ask 1030 slist = nil; 1031 for(d := trimdot(label); d != "."; d = parent(d)){ 1032 nsl: list of ref RR; 1033 (nsl, err) = lookcache(d, Tns, Cin); 1034 if(nsl == nil) 1035 nsl = dblookns(d); 1036 # add each to slist; put ones with known addresses first 1037 known: list of ref NS = nil; 1038 for(; nsl != nil; nsl = tl nsl){ 1039 pick ns := hd nsl { 1040 Host => 1041 (addrs, err2) := lookcache(ns.host, Ta, Cin); 1042 if(addrs != nil) 1043 known = ref NS(ns.host, addrs, 0, 1<<31) :: known; 1044 else if(err2 == nil) 1045 slist = ref NS(ns.host, nil, 0, 1<<31) :: slist; 1046 } 1047 1048 } 1049 for(; known != nil; known = tl known) 1050 slist = hd known :: slist; 1051 if(slist != nil) 1052 break; 1053 } 1054 # if no servers, resort to safety belt 1055 if(slist == nil){ 1056 slist = defaultresolvers(); 1057 if(slist == nil){ 1058 slist = rootservers(); 1059 if(slist == nil) 1060 return (nil, "no dns servers configured"); 1061 } 1062 } 1063 (id, query, err1) := mkquery(attr, Cin, label); 1064 if(err1 != nil){ 1065 sys->fprint(stderr, "dns: %s\n", err1); 1066 return (nil, err1); 1067 } 1068 1069 if(debug) 1070 printnslist(sys->sprint("ns for %s: ", d), slist); 1071 1072 # 3. send them queries until one returns a response 1073 for(qset := slist; qset != nil; qset = tl qset){ 1074 ns := hd qset; 1075 if(ns.addr == nil){ 1076 if(debug) 1077 sys->print("recursive[%d] query for %s address\n", depth+1, ns.name); 1078 (ns.addr, nil) = fulldnsquery(ns.name, Ta, depth+1); 1079 if(ns.addr == nil) 1080 continue; 1081 } 1082 if(fd == nil){ 1083 fd = udpport(); 1084 if(fd == nil) 1085 return (nil, sys->sprint("%r")); 1086 } 1087 (dm, err2) := udpquery(fd, id, query, ns.name, hd ns.addr); 1088 if(dm == nil){ 1089 sys->fprint(stderr, "dns: %s: %s\n", ns.name, err2); 1090 # TO DO: remove from slist 1091 continue; 1092 } 1093 # 4. analyse the response 1094 # a. answers the question or has Rname, cache it and return to client 1095 # b. delegation to other NS? cache and goto step 2. 1096 # c. if response is CNAME and QTYPE!=CNAME change SNAME to the 1097 # canonical name (data) of the CNAME RR and goto step 1. 1098 # d. if response is server failure or otherwise odd, delete server from SLIST 1099 # and goto step 3. 1100 auth := (dm.flags & Fauth) != 0; 1101 soa: ref RR.Soa; 1102 (soa, dm.ns) = soaof(dm.ns); 1103 if((dm.flags & Rmask) != Rok){ 1104 # don't repeat the request on an error 1105 # TO DO: should return `best error' 1106 if(tl qset != nil && ((dm.flags & Rmask) != Rname || !auth)) 1107 continue; 1108 cause := reason(dm.flags & Rmask); 1109 if(auth && soa != nil){ 1110 # rfc2038 says to cache soa with cached negatives, and the 1111 # negative to be retrieved for all attributes if name does not exist 1112 if((ttl := soa.soa.minttl) > 0) 1113 ttl += now; 1114 else 1115 ttl = now+10*60; 1116 a := attr; 1117 if((dm.flags & Rmask) == Rname) 1118 a = Tall; 1119 cachec <-= (ref RR.Error(label, a, Cin, ttl, auth, cause)::soa::nil, auth); 1120 } 1121 return (nil, cause); 1122 } 1123 if(dm.an != nil){ 1124 if(1 && dm.ns != nil) 1125 cachec <-= (dm.ns, 0); 1126 if(1 && dm.ar != nil) 1127 cachec <-= (dm.ar, 0); 1128 cachec <-= (dm.an, auth); 1129 cachec <-= Sync; 1130 if(isresponse(dm, attr)) 1131 return (dm.an, nil); 1132 if(attr != Tcname && (cn := cnameof(dm)) != nil){ 1133 if(++ncname > 10) 1134 return (nil, "cname alias loop"); 1135 label = cn; 1136 continue Step1; 1137 } 1138 } 1139 if(auth){ 1140 if(soa != nil && (ttl := soa.soa.minttl) > 0) 1141 ttl += now; 1142 else 1143 ttl = now+10*60; 1144 if(soa != nil) 1145 l := soa :: nil; 1146 cachec <-= (ref RR.Error(label, attr, Cin, ttl, auth, "resource does not exist")::l, auth); 1147 return (nil, "resource does not exist"); 1148 } 1149 if(isdelegation(dm)){ 1150 # cache valid name servers and hints 1151 cachec <-= (dm.ns, 0); 1152 if(dm.ar != nil) 1153 cachec <-= (dm.ar, 0); 1154 cachec <-= Sync; 1155 continue Step1; 1156 } 1157 } 1158 } 1159 return (nil, "server failed"); 1160} 1161 1162isresponse(dn: ref DNSmsg, attr: int): int 1163{ 1164 if(dn == nil || dn.an == nil) 1165 return 0; 1166 return (hd dn.an).rtype == attr; 1167} 1168 1169cnameof(dn: ref DNSmsg): string 1170{ 1171 if(dn != nil && dn.an != nil && (rr := hd dn.an).rtype == Tcname) 1172 pick ar := rr { 1173 Host => 1174 return ar.host; 1175 } 1176 return nil; 1177} 1178 1179soaof(rrl: list of ref RR): (ref RR.Soa, list of ref RR) 1180{ 1181 for(l := rrl; l != nil; l = tl l) 1182 pick rr := hd l { 1183 Soa => 1184 rest := tl l; 1185 for(; rrl != l; rrl = tl rrl) 1186 if(tagof hd rrl != tagof RR.Soa) # (just in case) 1187 rest = hd rrl :: rest; 1188 return (rr, rest); 1189 } 1190 return (nil, rrl); 1191} 1192 1193isdelegation(dn: ref DNSmsg): int 1194{ 1195 if(dn.an != nil) 1196 return 0; 1197 for(al := dn.ns; al != nil; al = tl al) 1198 if((hd al).rtype == Tns) 1199 return 1; 1200 return 0; 1201} 1202 1203printnslist(prefix: string, nsl: list of ref NS) 1204{ 1205 s := prefix; 1206 for(; nsl != nil; nsl = tl nsl){ 1207 ns := hd nsl; 1208 s += sys->sprint(" [%s %s]", ns.name, rrsettext(ns.addr)); 1209 } 1210 sys->print("%s\n", s); 1211} 1212 1213# 1214# DNS message format 1215# 1216 1217Udpdnslim: con 512; 1218 1219Labels: adt { 1220 names: list of (string, int); 1221 1222 new: fn(): ref Labels; 1223 look: fn(labs: self ref Labels, s: string): int; 1224 install: fn(labs: self ref Labels, s: string, o: int); 1225}; 1226 1227Labels.new(): ref Labels 1228{ 1229 return ref Labels; 1230} 1231 1232Labels.look(labs: self ref Labels, s: string): int 1233{ 1234 for(nl := labs.names; nl != nil; nl = tl nl){ 1235 (t, o) := hd nl; 1236 if(s == t) 1237 return 16rC000 | o; 1238 } 1239 return 0; 1240} 1241 1242Labels.install(labs: self ref Labels, s: string, off: int) 1243{ 1244 labs.names = (s, off) :: labs.names; 1245} 1246 1247put2(a: array of byte, o: int, val: int): int 1248{ 1249 if(o < 0) 1250 return o; 1251 if(o + 2 > len a) 1252 return -o; 1253 a[o] = byte (val>>8); 1254 a[o+1] = byte val; 1255 return o+2; 1256} 1257 1258put4(a: array of byte, o: int, val: int): int 1259{ 1260 if(o < 0) 1261 return o; 1262 if(o + 4 > len a) 1263 return -o; 1264 a[o] = byte (val>>24); 1265 a[o+1] = byte (val>>16); 1266 a[o+2] = byte (val>>8); 1267 a[o+3] = byte val; 1268 return o+4; 1269} 1270 1271puta(a: array of byte, o: int, b: array of byte): int 1272{ 1273 if(o < 0) 1274 return o; 1275 l := len b; 1276 if(l > 255 || o+l+1 > len a) 1277 return -(o+l+1); 1278 a[o++] = byte l; 1279 a[o:] = b; 1280 return o+len b; 1281} 1282 1283puts(a: array of byte, o: int, s: string): int 1284{ 1285 return puta(a, o, array of byte s); 1286} 1287 1288get2(a: array of byte, o: int): (int, int) 1289{ 1290 if(o < 0) 1291 return (0, o); 1292 if(o + 2 > len a) 1293 return (0, -o); 1294 val := (int a[o] << 8) | int a[o+1]; 1295 return (val, o+2); 1296} 1297 1298get4(a: array of byte, o: int): (int, int) 1299{ 1300 if(o < 0) 1301 return (0, o); 1302 if(o + 4 > len a) 1303 return (0, -o); 1304 val := (((((int a[o] << 8)| int a[o+1]) << 8) | int a[o+2]) << 8) | int a[o+3]; 1305 return (val, o+4); 1306} 1307 1308gets(a: array of byte, o: int): (string, int) 1309{ 1310 if(o < 0) 1311 return (nil, o); 1312 if(o+1 > len a) 1313 return (nil, -o); 1314 l := int a[o++]; 1315 if(o+l > len a) 1316 return (nil, -o); 1317 return (string a[o:o+l], o+l); 1318} 1319 1320putdn(a: array of byte, o: int, name: string, labs: ref Labels): int 1321{ 1322 if(o < 0) 1323 return o; 1324 o0 := o; 1325 while(name != "") { 1326 n := labs.look(name); 1327 if(n != 0){ 1328 o = put2(a, o, n); 1329 if(o < 0) 1330 return -o0; 1331 return o; 1332 } 1333 for(l := 0; l < len name && name[l] != '.'; l++) 1334 ; 1335 if(o+l+1 > len a) 1336 return -o0; 1337 labs.install(name, o); 1338 a[o++] = byte l; 1339 for(i := 0; i < l; i++) 1340 a[o++] = byte name[i]; 1341 for(; l < len name && name[l] == '.'; l++) 1342 ; 1343 name = name[l:]; 1344 } 1345 if(o >= len a) 1346 return -o0; 1347 a[o++] = byte 0; 1348 return o; 1349} 1350 1351getdn(a: array of byte, o: int, depth: int): (string, int) 1352{ 1353 if(depth > 30) 1354 return (nil, -o); 1355 if(o < 0) 1356 return (nil, o); 1357 name := ""; 1358 while(o < len a && (l := int a[o++]) != 0) { 1359 if((l & 16rC0) == 16rC0) { # pointer 1360 if(o >= len a) 1361 return (nil, -o); 1362 po := ((l & 16r3F)<<8) | int a[o]; 1363 if(po >= len a) 1364 return ("", -o); 1365 o++; 1366 pname: string; 1367 (pname, po) = getdn(a, po, depth+1); 1368 if(po < 1) 1369 return (nil, -o); 1370 name += pname; 1371 break; 1372 } 1373 if((l & 16rC0) != 0) 1374 return (nil, -o); # format error 1375 if(o + l > len a) 1376 return (nil, -o); 1377 name += string a[o:o+l]; 1378 o += l; 1379 if(o < len a && a[o] != byte 0) 1380 name += "."; 1381 } 1382 return (lower(name), o); 1383} 1384 1385putqrl(a: array of byte, o: int, qrl: list of ref QR, labs: ref Labels): int 1386{ 1387 for(; qrl != nil && o >= 0; qrl = tl qrl){ 1388 q := hd qrl; 1389 o = putdn(a, o, q.name, labs); 1390 o = put2(a, o, q.rtype); 1391 o = put2(a, o, q.class); 1392 } 1393 return o; 1394} 1395 1396getqrl(nq: int, a: array of byte, o: int): (list of ref QR, int) 1397{ 1398 if(o < 0) 1399 return (nil, o); 1400 qrl: list of ref QR; 1401 for(i := 0; i < nq; i++) { 1402 qd := ref QR; 1403 (qd.name, o) = getdn(a, o, 0); 1404 (qd.rtype, o) = get2(a, o); 1405 (qd.class, o) = get2(a, o); 1406 if(o < 1) 1407 break; 1408 qrl = qd :: qrl; 1409 } 1410 q: list of ref QR; 1411 for(; qrl != nil; qrl = tl qrl) 1412 q = hd qrl :: q; 1413 return (q, o); 1414} 1415 1416putrrl(a: array of byte, o: int, rrl: list of ref RR, labs: ref Labels): int 1417{ 1418 if(o < 0) 1419 return o; 1420 for(; rrl != nil; rrl = tl rrl){ 1421 rr := hd rrl; 1422 o0 := o; 1423 o = putdn(a, o, rr.name, labs); 1424 o = put2(a, o, rr.rtype); 1425 o = put2(a, o, rr.class); 1426 o = put4(a, o, rr.ttl); 1427 pick ar := rr { 1428 Host => 1429 o = putdn(a, o, ar.host, labs); 1430 Hinfo => 1431 o = puts(a, o, ar.cpu); 1432 o = puts(a, o, ar.os); 1433 Mx => 1434 o = put2(a, o, ar.pref); 1435 o = putdn(a, o, ar.host, labs); 1436 Soa => 1437 soa := ar.soa; 1438 o = putdn(a, o, soa.mname, labs); 1439 o = putdn(a, o, soa.rname, labs); 1440 o = put4(a, o, soa.serial); 1441 o = put4(a, o, soa.refresh); 1442 o = put4(a, o, soa.retry); 1443 o = put4(a, o, soa.expire); 1444 o = put4(a, o, soa.minttl); 1445 A or 1446 Other => 1447 dlen := len ar.rdata; 1448 o = put2(a, o, dlen); 1449 if(o < 1) 1450 return -o0; 1451 if(o + dlen > len a) 1452 return -o0; 1453 a[o:] = ar.rdata; 1454 o += dlen; 1455 } 1456 } 1457 return o; 1458} 1459 1460getrrl(nr: int, a: array of byte, o: int): (list of ref RR, int) 1461{ 1462 if(o < 0) 1463 return (nil, o); 1464 rrl: list of ref RR; 1465 for(i := 0; i < nr; i++) { 1466 name: string; 1467 rtype, rclass, ttl: int; 1468 (name, o) = getdn(a, o, 0); 1469 (rtype, o) = get2(a, o); 1470 (rclass, o) = get2(a, o); 1471 (ttl, o) = get4(a, o); 1472 if(ttl <= 0) 1473 ttl = 0; 1474 #ttl = 1*60; 1475 ttl += now; 1476 dlen: int; 1477 (dlen, o) = get2(a, o); 1478 if(o < 1) 1479 return (rrl, o); 1480 if(o+dlen > len a) 1481 return (rrl, -(o+dlen)); 1482 rr: ref RR; 1483 dname: string; 1484 case rtype { 1485 Tsoa => 1486 soa := ref SOA; 1487 (soa.mname, o) = getdn(a, o, 0); 1488 (soa.rname, o) = getdn(a, o, 0); 1489 (soa.serial, o) = get4(a, o); 1490 (soa.refresh, o) = get4(a, o); 1491 (soa.retry, o) = get4(a, o); 1492 (soa.expire, o) = get4(a, o); 1493 (soa.minttl, o) = get4(a, o); 1494 rr = ref RR.Soa(name, rtype, rclass, ttl, 0, soa); 1495 Thinfo => 1496 cpu, os: string; 1497 (cpu, o) = gets(a, o); 1498 (os, o) = gets(a, o); 1499 rr = ref RR.Hinfo(name, rtype, rclass, ttl, 0, cpu, os); 1500 Tmx => 1501 pref: int; 1502 host: string; 1503 (pref, o) = get2(a, o); 1504 (host, o) = getdn(a, o, 0); 1505 rr = ref RR.Mx(name, rtype, rclass, ttl, 0, pref, host); 1506 Tcname or 1507 Tns or 1508 Tptr => 1509 (dname, o) = getdn(a, o, 0); 1510 rr = ref RR.Host(name, rtype, rclass, ttl, 0, dname); 1511 Ta => 1512 rdata := array[dlen] of byte; 1513 rdata[0:] = a[o:o+dlen]; 1514 rr = ref RR.A(name, rtype, rclass, ttl, 0, rdata); 1515 o += dlen; 1516 * => 1517 rdata := array[dlen] of byte; 1518 rdata[0:] = a[o:o+dlen]; 1519 rr = ref RR.Other(name, rtype, rclass, ttl, 0, rdata); 1520 o += dlen; 1521 } 1522 rrl = rr :: rrl; 1523 } 1524 r: list of ref RR; 1525 for(; rrl != nil; rrl = tl rrl) 1526 r = (hd rrl) :: r; 1527 return (r, o); 1528} 1529 1530DNSmsg.pack(msg: self ref DNSmsg, hdrlen: int): array of byte 1531{ 1532 a := array[Udpdnslim+hdrlen] of byte; 1533 1534 l := hdrlen; 1535 l = put2(a, l, msg.id); 1536 l = put2(a, l, msg.flags); 1537 l = put2(a, l, len msg.qd); 1538 l = put2(a, l, len msg.an); 1539 l = put2(a, l, len msg.ns); 1540 l = put2(a, l, len msg.ar); 1541 labs := Labels.new(); 1542 l = putqrl(a, l, msg.qd, labs); 1543 l = putrrl(a, l, msg.an, labs); 1544 l = putrrl(a, l, msg.ns, labs); 1545 l = putrrl(a, l, msg.ar, labs); 1546 if(l < 1) 1547 return nil; 1548 return a[0:l]; 1549} 1550 1551DNSmsg.unpack(a: array of byte): ref DNSmsg 1552{ 1553 msg := ref DNSmsg; 1554 msg.flags = Rformat; 1555 l := 0; 1556 (msg.id, l) = get2(a, l); 1557 (msg.flags, l) = get2(a, l); 1558 if(l < 0 || l > len a){ 1559 msg.err = "length error"; 1560 return msg; 1561 } 1562 if(l >= len a) 1563 return msg; 1564 1565 nqd, nan, nns, nar: int; 1566 (nqd, l) = get2(a, l); 1567 (nan, l) = get2(a, l); 1568 (nns, l) = get2(a, l); 1569 (nar, l) = get2(a, l); 1570 if(l >= len a) 1571 return msg; 1572 (msg.qd, l) = getqrl(nqd, a, l); 1573 (msg.an, l) = getrrl(nan, a, l); 1574 (msg.ns, l) = getrrl(nns, a, l); 1575 (msg.ar, l) = getrrl(nar, a, l); 1576 if(l < 1){ 1577 sys->fprint(stderr, "l=%d format error\n", l); 1578 msg.err = "format error"; 1579 return msg; 1580 } 1581 return msg; 1582} 1583 1584DNSmsg.text(msg: self ref DNSmsg): string 1585{ 1586 s := sys->sprint("id=%ud flags=#%ux[%s]\n", msg.id, msg.flags, flagtext(msg.flags)); 1587 s += " QR:\n"; 1588 for(x := msg.qd; x != nil; x = tl x) 1589 s += "\t"+(hd x).text()+"\n"; 1590 s += " AN:\n"; 1591 for(l := msg.an; l != nil; l = tl l) 1592 s += "\t"+(hd l).text()+"\n"; 1593 s += " NS:\n"; 1594 for(l = msg.ns; l != nil; l = tl l) 1595 s += "\t"+(hd l).text()+"\n"; 1596 s += " AR:\n"; 1597 for(l = msg.ar; l != nil; l = tl l) 1598 s += "\t"+(hd l).text()+"\n"; 1599 return s; 1600} 1601 1602flagtext(f: int): string 1603{ 1604 s := ""; 1605 if(f & Fresp) 1606 s += "R"; 1607 if(f & Fauth) 1608 s += "A"; 1609 if(f & Ftrunc) 1610 s += "T"; 1611 if(f & Frecurse) 1612 s += "r"; 1613 if(f & Fcanrecurse) 1614 s += "c"; 1615 if((f & Fresp) == 0) 1616 return s; 1617 if(s != "") 1618 s += ","; 1619 return s+reason(f & Rmask); 1620} 1621 1622rcodes := array[] of { 1623 Rok => "no error", 1624 Rformat => "format error", 1625 Rserver => "server failure", 1626 Rname => "name does not exist", 1627 Runimplemented => "unimplemented", 1628 Rrefused => "refused", 1629}; 1630 1631reason(n: int): string 1632{ 1633 if(n < 0 || n > len rcodes) 1634 return sys->sprint("error %d", n); 1635 return rcodes[n]; 1636} 1637 1638rrtype(s: string): int 1639{ 1640 case s { 1641 "ip" => return Ta; 1642 "ns" => return Tns; 1643 "cname" => return Tcname; 1644 "soa" => return Tsoa; 1645 "ptr" => return Tptr; 1646 "mx" => return Tmx; 1647 "hinfo" => return Thinfo; 1648 "all" or "any" => return Tall; 1649 * => return -1; 1650 } 1651} 1652 1653rrtypename(t: int): string 1654{ 1655 case t { 1656 Ta => return "ip"; 1657 Tns => return "ns"; 1658 Tcname => return "cname"; 1659 Tsoa => return "soa"; 1660 Tptr => return "ptr"; 1661 Tmx => return "mx"; 1662 Tall => return "all"; 1663 Thinfo => return "hinfo"; 1664 * => return string t; 1665 } 1666} 1667 1668# 1669# format of UDP head read and written in `headers' mode 1670# 1671Udphdrsize: con Udphdrlen; 1672dnsid := 1; 1673 1674mkquery(qtype: int, qclass: int, name: string): (int, array of byte, string) 1675{ 1676 qd := ref QR(name, qtype, qclass); 1677 dm := ref DNSmsg; 1678 dm.id = dnsid++; # doesn't matter if two different procs use it (different fds) 1679 dm.flags = Oquery; 1680 if(referdns || !debug) 1681 dm.flags |= Frecurse; 1682 dm.qd = qd :: nil; 1683 a: array of byte; 1684 a = dm.pack(Udphdrsize); 1685 if(a == nil) 1686 return (0, nil, "dns: bad query message"); # should only happen if a name is ridiculous 1687 for(i:=0; i<Udphdrsize; i++) 1688 a[i] = byte 0; 1689 a[Udprport] = byte (DNSport>>8); 1690 a[Udprport+1] = byte DNSport; 1691 return (dm.id&16rFFFF, a, nil); 1692} 1693 1694udpquery(fd: ref Sys->FD, id: int, query: array of byte, sname: string, addr: ref RR): (ref DNSmsg, string) 1695{ 1696 # TO DO: check address and ports? 1697 1698 if(debug) 1699 sys->print("udp query %s\n", sname); 1700 pick ar := addr { 1701 A => 1702 query[Udpraddr:] = ip->v4prefix[0:IPv4off]; 1703 query[Udpraddr+IPv4off:] = ar.rdata[0:4]; 1704 * => 1705 return (nil, "not A resource"); 1706 } 1707 dm: ref DNSmsg; 1708 pidc := chan of int; 1709 c := chan of array of byte; 1710 spawn reader(fd, c, pidc); 1711 rpid := <-pidc; 1712 spawn timer(c, pidc); 1713 tpid := <-pidc; 1714 for(ntries := 0; ntries < 8; ntries++){ 1715 if(debug){ 1716 ipa := query[Udpraddr+IPv4off:]; 1717 sys->print("send udp!%d.%d.%d.%d!%d [%d] %d\n", int ipa[0], int ipa[1], 1718 int ipa[2], int ipa[3], get2(query, Udprport).t0, ntries, len query); 1719 } 1720 n := sys->write(fd, query, len query); 1721 if(n != len query) 1722 return (nil, sys->sprint("udp write err: %r")); 1723 buf := <-c; 1724 if(buf != nil){ 1725 buf = buf[Udphdrsize:]; 1726 dm = DNSmsg.unpack(buf); 1727 if(dm == nil){ 1728 kill(tpid); 1729 kill(rpid); 1730 return (nil, "bad udp reply message"); 1731 } 1732 if(dm.flags & Fresp && dm.id == id){ 1733 if(dm.flags & Ftrunc && dm.ns == nil){ 1734 if(debug) 1735 sys->print("id=%d was truncated\n", dm.id); 1736 }else 1737 break; 1738 }else if(debug) 1739 sys->print("id=%d got flags #%ux id %d\n", id, dm.flags, dm.id); 1740 }else if(debug) 1741 sys->print("timeout\n"); 1742 } 1743 kill(tpid); 1744 kill(rpid); 1745 if(dm == nil) 1746 return (nil, "no reply"); 1747 if(dm.err != nil){ 1748 sys->fprint(stderr, "bad reply: %s\n", dm.err); 1749 return (nil, dm.err); 1750 } 1751 if(debug) 1752 sys->print("reply: %s\n", dm.text()); 1753 return (dm, nil); 1754} 1755 1756reader(fd: ref Sys->FD, c: chan of array of byte, pidc: chan of int) 1757{ 1758 pidc <-= sys->pctl(0, nil); 1759 for(;;){ 1760 buf := array[4096+Udphdrsize] of byte; 1761 n := sys->read(fd, buf, len buf); 1762 if(n > 0){ 1763 if(debug) 1764 sys->print("rcvd %d\n", n); 1765 c <-= buf[0:n]; 1766 }else 1767 c <-= nil; 1768 } 1769} 1770 1771timer(c: chan of array of byte, pidc: chan of int) 1772{ 1773 pidc <-= sys->pctl(0, nil); 1774 for(;;){ 1775 sys->sleep(5*1000); 1776 c <-= nil; 1777 } 1778} 1779 1780kill(pid: int) 1781{ 1782 fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); 1783 if(fd != nil) 1784 sys->fprint(fd, "kill"); 1785} 1786 1787udpport(): ref Sys->FD 1788{ 1789 conn := dial->announce(mntpt+"/udp!*!0"); 1790 if(conn == nil) 1791 return nil; 1792 if(sys->fprint(conn.cfd, "headers") < 0){ 1793 sys->fprint(stderr, "dns: can't set headers mode: %r\n"); 1794 return nil; 1795 } 1796 conn.dfd = sys->open(conn.dir+"/data", Sys->ORDWR); 1797 if(conn.dfd == nil){ 1798 sys->fprint(stderr, "dns: can't open %s/data: %r\n", conn.dir); 1799 return nil; 1800 } 1801 return conn.dfd; 1802} 1803 1804# 1805# TCP/IP can be used to get the whole of a truncated message 1806# 1807tcpquery(query: array of byte): (ref DNSmsg, string) 1808{ 1809 # TO DO: check request id, ports etc. 1810 1811 ipa := query[Udpraddr+IPv4off:]; 1812 addr := sys->sprint("tcp!%d.%d.%d.%d!%d", int ipa[0], int ipa[1], int ipa[2], int ipa[3], DNSport); 1813 conn := dial->dial(addr, nil); 1814 if(conn == nil) 1815 return (nil, sys->sprint("can't dial %s: %r", addr)); 1816 query = query[Udphdrsize-2:]; 1817 put2(query, 0, len query-2); # replace UDP header by message length 1818 n := sys->write(conn.dfd, query[Udphdrsize:], len query); 1819 if(n != len query) 1820 return (nil, sys->sprint("dns: %s: write err: %r", addr)); 1821 buf := readn(conn.dfd, 2); # TCP/DNS record header 1822 (mlen, nil) := get2(buf, 0); 1823 if(mlen < 2 || mlen > 16384) 1824 return (nil, sys->sprint("dns: %s: bad reply msg length=%d", addr, mlen)); 1825 buf = readn(conn.dfd, mlen); 1826 if(buf == nil) 1827 return (nil, sys->sprint("dns: %s: read err: %r", addr)); 1828 dm := DNSmsg.unpack(buf); 1829 if(dm == nil) 1830 return (nil, "dns: bad reply message"); 1831 if(dm.err != nil){ 1832 sys->fprint(stderr, "dns: %s: bad reply: %s\n", addr, dm.err); 1833 return (nil, dm.err); 1834 } 1835 return (dm, nil); 1836} 1837 1838readn(fd: ref Sys->FD, nb: int): array of byte 1839{ 1840 buf:= array[nb] of byte; 1841 for(n:=0; n<nb;){ 1842 m := sys->read(fd, buf[n:], nb-n); 1843 if(m <= 0) 1844 return nil; 1845 n += m; 1846 } 1847 return buf; 1848} 1849 1850timefd: ref Sys->FD; 1851 1852time(): int 1853{ 1854 if(timefd == nil){ 1855 timefd = sys->open("/dev/time", Sys->OREAD); 1856 if(timefd == nil) 1857 return 0; 1858 } 1859 buf := array[128] of byte; 1860 sys->seek(timefd, big 0, 0); 1861 n := sys->read(timefd, buf, len buf); 1862 if(n < 0) 1863 return 0; 1864 return int ((big string buf[0:n]) / big 1000000); 1865} 1866 1867parseip(s: string): array of byte 1868{ 1869 (ok, a) := IPaddr.parse(s); 1870 if(ok < 0 || !a.isv4()) 1871 return nil; 1872 return a.v4(); 1873} 1874