xref: /inferno-os/appl/cmd/ndb/dns.b (revision 7e479951464b9bb71e87aeef19d908e9fb9be494)
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