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