xref: /plan9-contrib/sys/src/cmd/ndb/dnresolve.c (revision 6b6b9ac8b0b103b1e30e4d019522a78c950fce74)
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