xref: /plan9-contrib/sys/src/cmd/ndb/dnresolve.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
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  *  Get next server address
428  */
429 static int
430 serveraddrs(RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
431 {
432 	RR *rp, *arp, *trp;
433 	Dest *cur;
434 
435 	if(nd >= Maxdest)
436 		return 0;
437 
438 	/*
439 	 *  look for a server whose address we already know.
440 	 *  if we find one, mark it so we ignore this on
441 	 *  subsequent passes.
442 	 */
443 	arp = 0;
444 	for(rp = nsrp; rp; rp = rp->next){
445 		if(rp->marker)
446 			continue;
447 		arp = rrlookup(rp->host, Ta, NOneg);
448 		if(arp){
449 			rp->marker = 1;
450 			break;
451 		}
452 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
453 		if(arp){
454 			rp->marker = 1;
455 			break;
456 		}
457 	}
458 
459 	/*
460 	 *  if the cache and database lookup didn't find any new
461 	 *  server addresses, try resolving one via the network.
462 	 *  Mark any we try to resolve so we don't try a second time.
463 	 */
464 	if(arp == 0){
465 		for(rp = nsrp; rp; rp = rp->next){
466 			if(rp->marker)
467 				continue;
468 			rp->marker = 1;
469 
470 			/*
471 			 *  avoid loops looking up a server under itself
472 			 */
473 			if(subsume(rp->owner->name, rp->host->name))
474 				continue;
475 
476 			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0, depth+1, Recurse, 1, 0);
477 			rrfreelist(rrremneg(&arp));
478 			if(arp)
479 				break;
480 		}
481 	}
482 
483 	/* use any addresses that we found */
484 	for(trp = arp; trp; trp = trp->next){
485 		if(nd >= Maxdest)
486 			break;
487 		cur = &dest[nd++];
488 		parseip(cur->a, trp->ip->name);
489 		cur->nx = 0;
490 		cur->s = trp->owner;
491 		cur->code = Rtimeout;
492 	}
493 	rrfreelist(arp);
494 	return nd;
495 }
496 
497 /*
498  *  cache negative responses
499  */
500 static void
501 cacheneg(DN *dp, int type, int rcode, RR *soarr)
502 {
503 	RR *rp;
504 	DN *soaowner;
505 	ulong ttl;
506 
507 	/* no cache time specified, don' make anything up */
508 	if(soarr != nil){
509 		if(soarr->next != nil){
510 			rrfreelist(soarr->next);
511 			soarr->next = nil;
512 		}
513 		soaowner = soarr->owner;
514 	} else
515 		soaowner = nil;
516 
517 	/* the attach can cause soarr to be freed so mine it now */
518 	if(soarr != nil && soarr->soa != nil)
519 		ttl = soarr->soa->minttl+now;
520 	else
521 		ttl = 5*Min;
522 
523 	/* add soa and negative RR to the database */
524 	rrattach(soarr, 1);
525 
526 	rp = rralloc(type);
527 	rp->owner = dp;
528 	rp->negative = 1;
529 	rp->negsoaowner = soaowner;
530 	rp->negrcode = rcode;
531 	rp->ttl = ttl;
532 	rrattach(rp, 1);
533 }
534 
535 /*
536  *  query name servers.  If the name server returns a pointer to another
537  *  name server, recurse.
538  */
539 static int
540 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth, uchar *ibuf, uchar *obuf)
541 {
542 	int ndest, j, len, replywaits, rv;
543 	ushort req;
544 	RR *tp, *soarr;
545 	Dest *p, *l, *np;
546 	DN *ndp;
547 	Dest dest[Maxdest];
548 	DNSmsg m;
549 	ulong endtime;
550 
551 	/* pack request into a message */
552 	req = rand();
553 	len = mkreq(dp, type, obuf, req);
554 
555 	/* no server addresses yet */
556 	l = dest;
557 
558 	/*
559 	 *  transmit requests and wait for answers.
560 	 *  at most Maxtrans attempts to each address.
561 	 *  each cycle send one more message than the previous.
562 	 */
563 	for(ndest = 1; ndest < Maxdest; ndest++){
564 		p = dest;
565 
566 		endtime = time(0);
567 		if(endtime >= reqp->aborttime)
568 			break;
569 
570 		/* get a server address if we need one */
571 		if(ndest > l - p){
572 			j = serveraddrs(nsrp, dest, l - p, depth, reqp);
573 			l = &dest[j];
574 		}
575 
576 		/* no servers, punt */
577 		if(l == dest)
578 			break;
579 
580 		/* send to first 'ndest' destinations */
581 		j = 0;
582 		for(; p < &dest[ndest] && p < l; p++){
583 			/* skip destinations we've finished with */
584 			if(p->nx >= Maxtrans)
585 				continue;
586 
587 			j++;
588 
589 			/* exponential backoff of requests */
590 			if((1<<p->nx) > ndest)
591 				continue;
592 
593 			memmove(obuf, p->a, sizeof(p->a));
594 			if(debug)
595 				logsend(reqp->id, depth, obuf, p->s->name,
596 					dp->name, type);
597 			if(write(fd, obuf, len + Udphdrsize) < 0)
598 				warning("sending udp msg %r");
599 			p->nx++;
600 		}
601 		if(j == 0)
602 			break;		/* no destinations left */
603 
604 		/* wait up to 5 seconds for replies */
605 		endtime = time(0) + 5;
606 		if(endtime > reqp->aborttime)
607 			endtime = reqp->aborttime;
608 
609 		for(replywaits = 0; replywaits < ndest; replywaits++){
610 			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)
611 				break;		/* timed out */
612 
613 			/* find responder */
614 			for(p = dest; p < l; p++)
615 				if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)
616 					break;
617 
618 			/* remove all addrs of responding server from list */
619 			for(np = dest; np < l; np++)
620 				if(np->s == p->s)
621 					p->nx = Maxtrans;
622 
623 			/* ignore any error replies */
624 			if((m.flags & Rmask) == Rserver){
625 				rrfreelist(m.qd);
626 				rrfreelist(m.an);
627 				rrfreelist(m.ar);
628 				rrfreelist(m.ns);
629 				if(p != l)
630 					p->code = Rserver;
631 				continue;
632 			}
633 
634 			/* ignore any bad delegations */
635 			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
636 				rrfreelist(m.ns);
637 				m.ns = nil;
638 				if(m.an == nil){
639 					rrfreelist(m.qd);
640 					rrfreelist(m.ar);
641 					if(p != l)
642 						p->code = Rserver;
643 					continue;
644 				}
645 			}
646 
647 
648 			/* remove any soa's from the authority section */
649 			soarr = rrremtype(&m.ns, Tsoa);
650 
651 			/* incorporate answers */
652 			if(m.an)
653 				rrattach(m.an, (m.flags & Fauth) ? 1 : 0);
654 			if(m.ar)
655 				rrattach(m.ar, 0);
656 			if(m.ns){
657 				ndp = m.ns->owner;
658 				rrattach(m.ns, 0);
659 			} else
660 				ndp = 0;
661 
662 			/* free the question */
663 			if(m.qd)
664 				rrfreelist(m.qd);
665 
666 			/*
667 			 *  Any reply from an authoritative server,
668 			 *  or a positive reply terminates the search
669 			 */
670 			if(m.an != nil || (m.flags & Fauth)){
671 				if(m.an == nil && (m.flags & Rmask) == Rname)
672 					dp->nonexistent = Rname;
673 				else
674 					dp->nonexistent = 0;
675 
676 				/*
677 				 *  cache any negative responses, free soarr
678 				 */
679 				if((m.flags & Fauth) && m.an == nil)
680 					cacheneg(dp, type, (m.flags & Rmask), soarr);
681 				else
682 					rrfreelist(soarr);
683 				return 1;
684 			}
685 			rrfreelist(soarr);
686 
687 			/*
688 			 *  if we've been given better name servers
689 			 *  recurse
690 			 */
691 			if(m.ns){
692 				tp = rrlookup(ndp, Tns, NOneg);
693 				if(!contains(nsrp, tp)){
694 					rv = netquery(dp, type, tp, reqp, depth+1);
695 					rrfreelist(tp);
696 					return rv;
697 				} else
698 					rrfreelist(tp);
699 			}
700 		}
701 	}
702 
703 	/* if all servers returned failure, propogate it */
704 	dp->nonexistent = Rserver;
705 	for(p = dest; p < l; p++)
706 		if(p->code != Rserver)
707 			dp->nonexistent = 0;
708 
709 	return 0;
710 }
711 
712 static int
713 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
714 {
715 	uchar *obuf;
716 	uchar *ibuf;
717 	RR *rp;
718 	int fd, rv;
719 
720 	if(depth > 12)
721 		return 0;
722 
723 	/* use alloced buffers rather than ones from the stack */
724 	ibuf = emalloc(Maxudpin+Udphdrsize);
725 	obuf = emalloc(Maxudp+Udphdrsize);
726 
727 	slave(reqp);
728 
729 	/* prepare server RR's for incremental lookup */
730 	for(rp = nsrp; rp; rp = rp->next)
731 		rp->marker = 0;
732 
733 	fd = udpport();
734 	if(fd < 0)
735 		return 0;
736 	rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf);
737 	close(fd);
738 	free(ibuf);
739 	free(obuf);
740 
741 	return rv;
742 }
743