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