xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 1e8349eb642b57c778df068f20e57f4c16ccee23)
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, "dnresolve1 %s %d %d", 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 static 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 static int
284 mkreq(DN *dp, int type, uchar *buf, 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 = Frecurse;
297 //	m.flags = resolver ? Frecurse : 0;
298 	m.id = reqno;
299 	m.qd = rralloc(type);
300 	m.qd->owner = dp;
301 	m.qd->type = type;
302 	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
303 	if(len < 0)
304 		abort(); /* "can't convert" */;
305 	rrfree(m.qd);
306 	return len;
307 }
308 
309 /* for alarms in readreply */
310 static void
311 ding(void *x, char *msg)
312 {
313 	USED(x);
314 	if(strcmp(msg, "alarm") == 0)
315 		noted(NCONT);
316 	else
317 		noted(NDFLT);
318 }
319 
320 static void
321 freeanswers(DNSmsg *mp)
322 {
323 	rrfreelist(mp->qd);
324 	rrfreelist(mp->an);
325 	rrfreelist(mp->ns);
326 	rrfreelist(mp->ar);
327 }
328 
329 /*
330  *  read replies to a request.  ignore any of the wrong type.  wait at most 5 seconds.
331  */
332 static int
333 readreply(int fd, DN *dp, int type, ushort req,
334 	  uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp)
335 {
336 	char *err;
337 	int len;
338 	ulong now;
339 	RR *rp;
340 
341 	notify(ding);
342 
343 	for(; ; freeanswers(mp)){
344 		now = time(0);
345 		if(now >= endtime)
346 			return -1;	/* timed out */
347 
348 		/* timed read */
349 		alarm((endtime - now) * 1000);
350 		len = read(fd, ibuf, OUdphdrsize+Maxudpin);
351 		alarm(0);
352 		len -= OUdphdrsize;
353 		if(len < 0)
354 			return -1;	/* timed out */
355 
356 		/* convert into internal format  */
357 		memset(mp, 0, sizeof(*mp));
358 		err = convM2DNS(&ibuf[OUdphdrsize], len, mp);
359 		if(err){
360 			syslog(0, LOG, "input err %s: %I", err, ibuf);
361 			continue;
362 		}
363 		if(debug)
364 			logreply(reqp->id, ibuf, mp);
365 
366 		/* answering the right question? */
367 		if(mp->id != req){
368 			syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id,
369 					mp->id, req, ibuf);
370 			continue;
371 		}
372 		if(mp->qd == 0){
373 			syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf);
374 			continue;
375 		}
376 		if(mp->qd->owner != dp){
377 			syslog(0, LOG, "%d: owner %s instead of %s: %I", reqp->id,
378 				mp->qd->owner->name, dp->name, ibuf);
379 			continue;
380 		}
381 		if(mp->qd->type != type){
382 			syslog(0, LOG, "%d: type %d instead of %d: %I", reqp->id,
383 				mp->qd->type, type, ibuf);
384 			continue;
385 		}
386 
387 		/* remember what request this is in answer to */
388 		for(rp = mp->an; rp; rp = rp->next)
389 			rp->query = type;
390 
391 		return 0;
392 	}
393 
394 	return 0;	/* never reached */
395 }
396 
397 /*
398  *	return non-0 if first list includes second list
399  */
400 int
401 contains(RR *rp1, RR *rp2)
402 {
403 	RR *trp1, *trp2;
404 
405 	for(trp2 = rp2; trp2; trp2 = trp2->next){
406 		for(trp1 = rp1; trp1; trp1 = trp1->next){
407 			if(trp1->type == trp2->type)
408 			if(trp1->host == trp2->host)
409 			if(trp1->owner == trp2->owner)
410 				break;
411 		}
412 		if(trp1 == 0)
413 			return 0;
414 	}
415 
416 	return 1;
417 }
418 
419 
420 typedef struct Dest	Dest;
421 struct Dest
422 {
423 	uchar	a[IPaddrlen];	/* ip address */
424 	DN	*s;		/* name server */
425 	int	nx;		/* number of transmissions */
426 	int	code;
427 };
428 
429 
430 /*
431  *  return multicast version if any
432  */
433 int
434 ipisbm(uchar *ip)
435 {
436 	if(isv4(ip)){
437 		if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
438 			return 4;
439 		if(ipcmp(ip, IPv4bcast) == 0)
440 			return 4;
441 	} else {
442 		if(ip[0] == 0xff)
443 			return 6;
444 	}
445 	return 0;
446 }
447 
448 /*
449  *  Get next server address
450  */
451 static int
452 serveraddrs(RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
453 {
454 	RR *rp, *arp, *trp;
455 	Dest *cur;
456 
457 	if(nd >= Maxdest)
458 		return 0;
459 
460 	/*
461 	 *  look for a server whose address we already know.
462 	 *  if we find one, mark it so we ignore this on
463 	 *  subsequent passes.
464 	 */
465 	arp = 0;
466 	for(rp = nsrp; rp; rp = rp->next){
467 		if(rp->marker)
468 			continue;
469 		arp = rrlookup(rp->host, Ta, NOneg);
470 		if(arp){
471 			rp->marker = 1;
472 			break;
473 		}
474 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
475 		if(arp){
476 			rp->marker = 1;
477 			break;
478 		}
479 	}
480 
481 	/*
482 	 *  if the cache and database lookup didn't find any new
483 	 *  server addresses, try resolving one via the network.
484 	 *  Mark any we try to resolve so we don't try a second time.
485 	 */
486 	if(arp == 0){
487 		for(rp = nsrp; rp; rp = rp->next){
488 			if(rp->marker)
489 				continue;
490 			rp->marker = 1;
491 
492 			/*
493 			 *  avoid loops looking up a server under itself
494 			 */
495 			if(subsume(rp->owner->name, rp->host->name))
496 				continue;
497 
498 			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0, depth+1, Recurse, 1, 0);
499 			rrfreelist(rrremneg(&arp));
500 			if(arp)
501 				break;
502 		}
503 	}
504 
505 	/* use any addresses that we found */
506 	for(trp = arp; trp; trp = trp->next){
507 		if(nd >= Maxdest)
508 			break;
509 		cur = &dest[nd];
510 		parseip(cur->a, trp->ip->name);
511 		if(ipisbm(cur->a))
512 			continue;
513 		cur->nx = 0;
514 		cur->s = trp->owner;
515 		cur->code = Rtimeout;
516 		nd++;
517 	}
518 	rrfreelist(arp);
519 	return nd;
520 }
521 
522 /*
523  *  cache negative responses
524  */
525 static void
526 cacheneg(DN *dp, int type, int rcode, RR *soarr)
527 {
528 	RR *rp;
529 	DN *soaowner;
530 	ulong ttl;
531 
532 	/* no cache time specified, don' make anything up */
533 	if(soarr != nil){
534 		if(soarr->next != nil){
535 			rrfreelist(soarr->next);
536 			soarr->next = nil;
537 		}
538 		soaowner = soarr->owner;
539 	} else
540 		soaowner = nil;
541 
542 	/* the attach can cause soarr to be freed so mine it now */
543 	if(soarr != nil && soarr->soa != nil)
544 		ttl = soarr->soa->minttl+now;
545 	else
546 		ttl = 5*Min;
547 
548 	/* add soa and negative RR to the database */
549 	rrattach(soarr, 1);
550 
551 	rp = rralloc(type);
552 	rp->owner = dp;
553 	rp->negative = 1;
554 	rp->negsoaowner = soaowner;
555 	rp->negrcode = rcode;
556 	rp->ttl = ttl;
557 	rrattach(rp, 1);
558 }
559 
560 /*
561  *  query name servers.  If the name server returns a pointer to another
562  *  name server, recurse.
563  */
564 static int
565 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth, uchar *ibuf, uchar *obuf)
566 {
567 	int ndest, j, len, replywaits, rv;
568 	ushort req;
569 	RR *tp, *soarr;
570 	Dest *p, *l, *np;
571 	DN *ndp;
572 	Dest dest[Maxdest];
573 	DNSmsg m;
574 	ulong endtime;
575 
576 	/* pack request into a message */
577 	req = rand();
578 	len = mkreq(dp, type, obuf, req);
579 
580 	/* no server addresses yet */
581 	l = dest;
582 
583 	/*
584 	 *  transmit requests and wait for answers.
585 	 *  at most Maxtrans attempts to each address.
586 	 *  each cycle send one more message than the previous.
587 	 */
588 	for(ndest = 1; ndest < Maxdest; ndest++){
589 		p = dest;
590 
591 		endtime = time(0);
592 		if(endtime >= reqp->aborttime)
593 			break;
594 
595 		/* get a server address if we need one */
596 		if(ndest > l - p){
597 			j = serveraddrs(nsrp, dest, l - p, depth, reqp);
598 			l = &dest[j];
599 		}
600 
601 		/* no servers, punt */
602 		if(l == dest)
603 			break;
604 
605 		/* send to first 'ndest' destinations */
606 		j = 0;
607 		for(; p < &dest[ndest] && p < l; p++){
608 			/* skip destinations we've finished with */
609 			if(p->nx >= Maxtrans)
610 				continue;
611 
612 			j++;
613 
614 			/* exponential backoff of requests */
615 			if((1<<p->nx) > ndest)
616 				continue;
617 
618 			memmove(obuf, p->a, sizeof(p->a));
619 			if(debug)
620 				logsend(reqp->id, depth, obuf, p->s->name,
621 					dp->name, type);
622 			if(write(fd, obuf, len + OUdphdrsize) < 0)
623 				warning("sending udp msg %r");
624 			p->nx++;
625 		}
626 		if(j == 0)
627 			break;		/* no destinations left */
628 
629 		/* wait up to 5 seconds for replies */
630 		endtime = time(0) + 5;
631 		if(endtime > reqp->aborttime)
632 			endtime = reqp->aborttime;
633 
634 		for(replywaits = 0; replywaits < ndest; replywaits++){
635 			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)
636 				break;		/* timed out */
637 
638 			/* find responder */
639 			for(p = dest; p < l; p++)
640 				if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)
641 					break;
642 
643 			/* remove all addrs of responding server from list */
644 			for(np = dest; np < l; np++)
645 				if(np->s == p->s)
646 					p->nx = Maxtrans;
647 
648 			/* ignore any error replies */
649 			if((m.flags & Rmask) == Rserver){
650 				rrfreelist(m.qd);
651 				rrfreelist(m.an);
652 				rrfreelist(m.ar);
653 				rrfreelist(m.ns);
654 				if(p != l)
655 					p->code = Rserver;
656 				continue;
657 			}
658 
659 			/* ignore any bad delegations */
660 			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
661 				rrfreelist(m.ns);
662 				m.ns = nil;
663 				if(m.an == nil){
664 					rrfreelist(m.qd);
665 					rrfreelist(m.ar);
666 					if(p != l)
667 						p->code = Rserver;
668 					continue;
669 				}
670 			}
671 
672 
673 			/* remove any soa's from the authority section */
674 			soarr = rrremtype(&m.ns, Tsoa);
675 
676 			/* incorporate answers */
677 			if(m.an)
678 				rrattach(m.an, (m.flags & Fauth) ? 1 : 0);
679 			if(m.ar)
680 				rrattach(m.ar, 0);
681 			if(m.ns){
682 				ndp = m.ns->owner;
683 				rrattach(m.ns, 0);
684 			} else
685 				ndp = 0;
686 
687 			/* free the question */
688 			if(m.qd)
689 				rrfreelist(m.qd);
690 
691 			/*
692 			 *  Any reply from an authoritative server,
693 			 *  or a positive reply terminates the search
694 			 */
695 			if(m.an != nil || (m.flags & Fauth)){
696 				if(m.an == nil && (m.flags & Rmask) == Rname)
697 					dp->nonexistent = Rname;
698 				else
699 					dp->nonexistent = 0;
700 
701 				/*
702 				 *  cache any negative responses, free soarr
703 				 */
704 				if((m.flags & Fauth) && m.an == nil)
705 					cacheneg(dp, type, (m.flags & Rmask), soarr);
706 				else
707 					rrfreelist(soarr);
708 				return 1;
709 			}
710 			rrfreelist(soarr);
711 
712 			/*
713 			 *  if we've been given better name servers
714 			 *  recurse
715 			 */
716 			if(m.ns){
717 				tp = rrlookup(ndp, Tns, NOneg);
718 				if(!contains(nsrp, tp)){
719 					rv = netquery(dp, type, tp, reqp, depth+1);
720 					rrfreelist(tp);
721 					return rv;
722 				} else
723 					rrfreelist(tp);
724 			}
725 		}
726 	}
727 
728 	/* if all servers returned failure, propogate it */
729 	dp->nonexistent = Rserver;
730 	for(p = dest; p < l; p++)
731 		if(p->code != Rserver)
732 			dp->nonexistent = 0;
733 
734 	return 0;
735 }
736 
737 static int
738 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
739 {
740 	uchar *obuf;
741 	uchar *ibuf;
742 	RR *rp;
743 	int fd, rv;
744 
745 	if(depth > 12)
746 		return 0;
747 
748 	/* use alloced buffers rather than ones from the stack */
749 	ibuf = emalloc(Maxudpin+OUdphdrsize);
750 	obuf = emalloc(Maxudp+OUdphdrsize);
751 
752 	slave(reqp);
753 
754 	/* prepare server RR's for incremental lookup */
755 	for(rp = nsrp; rp; rp = rp->next)
756 		rp->marker = 0;
757 
758 	fd = udpport();
759 	if(fd < 0)
760 		return 0;
761 	rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf);
762 	close(fd);
763 	free(ibuf);
764 	free(obuf);
765 
766 	return rv;
767 }
768