xref: /original-bsd/usr.sbin/amd/amd/info_hes.c (revision c3e32dec)
1 /*
2  * Copyright (c) 1989 Jan-Simon Pendry
3  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Jan-Simon Pendry at Imperial College, London.
9  *
10  * %sccs.include.redist.c%
11  *
12  *	@(#)info_hes.c	8.1 (Berkeley) 06/06/93
13  *
14  * $Id: info_hes.c,v 5.2.2.1 1992/02/09 15:08:29 jsp beta $
15  *
16  */
17 
18 /*
19  * Get info from Hesiod
20  *
21  * Zone transfer code from Bruce Cole <cole@cs.wisc.edu>
22  */
23 
24 #include "am.h"
25 
26 #ifdef HAS_HESIOD_MAPS
27 #include <hesiod.h>
28 
29 #define	HES_PREFIX	"hesiod."
30 #define	HES_PREFLEN	7
31 
32 #ifdef HAS_HESIOD_RELOAD
33 #include <arpa/nameser.h>
34 #include <resolv.h>
35 #include <sys/uio.h>
36 #include <netdb.h>
37 
38 /*
39  * Patch up broken system include files
40  */
41 #ifndef C_HS
42 #define C_HS	4
43 #endif
44 #ifndef T_TXT
45 #define	T_TXT	16
46 #endif
47 
48 static int soacnt;
49 static struct timeval hs_timeout;
50 static int servernum;
51 #endif /* HAS_HESIOD_RELOAD */
52 
53 /*
54  * No easy way to probe the server - check the map name begins with "hesiod."
55  */
56 int hesiod_init P((char *map, time_t *tp));
57 int hesiod_init(map, tp)
58 char *map;
59 time_t *tp;
60 {
61 #ifdef DEBUG
62 	dlog("hesiod_init(%s)", map);
63 #endif
64 	*tp = 0;
65 	return strncmp(map, HES_PREFIX, HES_PREFLEN) == 0 ? 0 : ENOENT;
66 }
67 
68 
69 /*
70  * Make Hesiod name.  Skip past the "hesiod."
71  * at the start of the map name and append
72  * ".automount".  The net effect is that a lookup
73  * of /defaults in hesiod.home will result in a
74  * call to hes_resolve("/defaults", "home.automount");
75  */
76 #ifdef notdef
77 #define MAKE_HES_NAME(dest, src) sprintf(dest, "%s%s", src + HES_PREFLEN, ".automount")
78 #endif
79 
80 /*
81  * Do a Hesiod nameserver call.
82  * Modify time is ignored by Hesiod - XXX
83  */
84 int hesiod_search P((mnt_map *m, char *map, char **pval, time_t *tp));
85 int hesiod_search(m, map, key, pval, tp)
86 mnt_map *m;
87 char *map;
88 char *key;
89 char **pval;
90 time_t *tp;
91 {
92 	int error;
93 	char hes_key[MAXPATHLEN];
94 	char **rvec;
95 #ifdef DEBUG
96 	dlog("hesiod_search(m=%x, map=%s, key=%s, pval=%x tp=%x)", m, map, key, pval, tp);
97 #endif
98 	/*MAKE_HES_NAME(hes_map, map);*/
99 	sprintf(hes_key, "%s.%s", key, map+HES_PREFLEN);
100 
101 	/*
102 	 * Call the resolver
103 	 */
104 #ifdef DEBUG
105 	dlog("hesiod_search: hes_resolve(%s, %s)", hes_key, "automount");
106 #ifdef HAS_HESIOD_RELOAD
107 	if (debug_flags & D_FULL)
108 		_res.options |= RES_DEBUG;
109 #endif
110 #endif
111 	rvec = hes_resolve(hes_key, "automount");
112 	/*
113 	 * If a reply was forthcoming then return
114 	 * it (and free subsequent replies)
115 	 */
116 	if (rvec && *rvec) {
117 		*pval = *rvec;
118 		while (*++rvec)
119 			free(*rvec);
120 		return 0;
121 	}
122 
123 	/*
124 	 * Otherwise reflect the hesiod error into a Un*x error
125 	 */
126 #ifdef DEBUG
127 	dlog("hesiod_search: Error: %d", hes_error());
128 #endif
129 	switch (hes_error()) {
130 	case HES_ER_NOTFOUND:	error = ENOENT; break;
131 	case HES_ER_CONFIG:	error = EIO; break;
132 	case HES_ER_NET:	error = ETIMEDOUT; break;
133 	default:		error = EINVAL; break;
134 	}
135 #ifdef DEBUG
136 	dlog("hesiod_search: Returning: %d", error);
137 #endif
138 	return error;
139 }
140 
141 #ifdef HAS_HESIOD_RELOAD
142 /*
143  * Zone transfer...
144  */
145 
146 #define MAXHSNS 8
147 #define MAX_NSADDR 16
148 
149 static char *hs_domain;
150 static mnt_map *hs_map;
151 static int hs_nscount;
152 static char nsaddr_list[MAX_NSADDR][sizeof(struct in_addr)];
153 
154 int hesiod_reload P((mnt_map *m, char *map, void (*fn)()));
155 int hesiod_reload(m, map, fn)
156 mnt_map *m;
157 char *map;
158 void (*fn)();
159 {
160 	char *zone_name, *cp;
161 	short domainlen;
162 	int status;
163 
164 #ifdef DEBUG
165 	dlog("hesiod_reload (%x %s %x)", m, map, fn);
166 #endif DEBUG
167 	if (status = res_init()) {
168 #ifdef DEBUG
169 		dlog("hesiod_reload: res_init failed with %d", status);
170 #endif
171 		return(status);
172 	}
173 	_res.retrans = 90;
174 	hs_map = m;
175 	domainlen = strlen(hostdomain);
176 	zone_name = hes_to_bind(map+HES_PREFLEN, "automount");
177 	if (*zone_name == '.')
178 		zone_name++;
179 	hs_domain = zone_name;
180 	/* Traverse the DNS tree until we find an SOA we can transfer from.
181 	 (Our initial zone_name is likely to just be a subtree of a
182 	 real zone). */
183 	do {
184 		/* If we can't find any NS records, go up a level in the
185 		   DNS tree */
186 		if (hs_get_ns_list(zone_name) == 0 &&
187 		    hs_zone_transfer(zone_name) == 0)
188 			return(0);
189 		/* Move up DNS tree by one component */
190 		if (cp = strchr(zone_name, '.'))
191 			zone_name = ++cp;
192 		else
193 			break;
194 	} while (strlen(zone_name) >= domainlen);
195 #ifdef DEBUG
196 	dlog("hesiod_reload: Giving up on %s", hs_domain);
197 #endif
198 	return(-1);
199 }
200 
201 hs_zone_transfer(domain)
202 char *domain;
203 {
204 	int status, len;
205 	char buf[PACKETSZ];
206 	/* Want to make sure ansbuf is well alligned */
207 	long ansbuf[PACKETSZ/sizeof(long)];
208 
209 #ifdef DEBUG
210 	dlog("hs_zone_transfer (%s)", domain);
211 #endif
212 	if ((len = res_mkquery(QUERY, domain, C_HS, T_AXFR,
213 			       (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1) {
214 #ifdef DEBUG
215 		dlog("hs_zone_transfer: res_mkquery failed");
216 #endif
217 		errno = 0;
218 		return(-1);
219 	}
220 	if ((status = hs_res_send(buf, len, (char *)ansbuf, PACKETSZ)) == -1) {
221 #ifdef DEBUG
222 	    dlog("hs_zone_transfer: hs_res_send failed.  status %d errno %d",
223 		 status, errno);
224 #endif
225 		errno = 0;
226 		return(-1);
227 	}
228 	return(0);
229 }
230 
231 #define hs_server_addr(ns) ((struct in_addr *) nsaddr_list[ns])
232 
233 hs_res_send(buf, buflen, answer, anslen)
234 char *buf;
235 int buflen;
236 char *answer;
237 int anslen;
238 {
239 	int retry, ns;
240 	u_short id, len;
241 	HEADER *hp = (HEADER *) buf;
242 	struct iovec iov[2];
243 	static int s = -1;
244 	int status;
245 	struct sockaddr_in server;
246 
247 	soacnt = 0;
248 	id = hp->id;
249 	/*
250 	 * Send request, RETRY times, or until successful
251 	 */
252 	for (retry = _res.retry; retry > 0; retry--) {
253 		for (ns = 0; ns < hs_nscount; ns++) {
254 			hs_timeout.tv_sec =
255 				(_res.retrans << (_res.retry - retry))
256 				/ hs_nscount;
257 			if (hs_timeout.tv_sec <= 0)
258 				hs_timeout.tv_sec = 1;
259 			hs_timeout.tv_usec = 0;
260 			if (s < 0) {
261 				s = socket(AF_INET, SOCK_STREAM, 0);
262 				if (s < 0) {
263 					continue;
264 				}
265 				servernum = ns;
266 				bcopy(hs_server_addr(ns), &server.sin_addr,
267 				      sizeof(struct in_addr));
268 				server.sin_family = AF_INET;
269 				server.sin_port = htons(NAMESERVER_PORT);
270 
271 				if (connect(s, &server,
272 					    sizeof(struct sockaddr)) < 0) {
273 					(void) close(s);
274 					s = -1;
275 					continue;
276 				}
277 			}
278 			/*
279 			 * Send length & message
280 			 */
281 			len = htons((u_short)buflen);
282 			iov[0].iov_base = (caddr_t)&len;
283 			iov[0].iov_len = sizeof(len);
284 			iov[1].iov_base = buf;
285 			iov[1].iov_len = buflen;
286 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
287 				(void) close(s);
288 				s = -1;
289 				continue;
290 			}
291 			status = 0;
292 			while (s != -1 && soacnt < 2 && status != -2) {
293 				if ((status =
294 				     hs_readresp(s, answer, anslen)) == -1) {
295 					(void) close(s);
296 					s = -1;
297 					continue;
298 				}
299 			}
300 			if (status == -2) {
301 				/* There was a permanent error transfering this
302 				   zone.  Give up. */
303 				if (s != -1) {
304 					(void) close(s);
305 					s = -1;
306 				}
307 				return(-1);
308 			}
309 			if (s == -1)
310 				continue;
311 			return (0);
312 		}
313 	}
314 	if (errno == 0)
315 		errno = ETIMEDOUT;
316 	return (-1);
317 }
318 
319 /* Returns:
320    0: Success
321    -1: Error
322    -2: Permanent failure
323 */
324 hs_readresp(s, answer, anslen)
325 int s;
326 char *answer;
327 int anslen;
328 {
329 	register int len, n;
330 	char *cp;
331 
332 	cp = answer;
333 	len = sizeof(short);
334 	while (len != 0 &&
335 	       (n = hs_res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) {
336 		cp += n;
337 		len -= n;
338 	}
339 	if (n <= 0)
340 		return(-1);
341 	cp = answer;
342 	if ((len = _getshort(cp)) > anslen) {
343 #ifdef DEBUG
344 		dlog("hs_readresp: response too long: %d", len);
345 #endif
346 		return(-1);
347 	}
348 	while (len != 0 &&
349 	       (n = hs_res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) {
350 		cp += n;
351 		len -= n;
352 	}
353 	if (n <= 0)
354 		return(-1);
355 	return(hs_parse(answer, answer+PACKETSZ));
356 }
357 
358 hs_res_vcread(sock, buf, buflen, timeout)
359 int sock, buflen;
360 char *buf;
361 struct timeval *timeout;
362 {
363 	register int n;
364 
365 	if ((n = hs_res_selwait(sock, timeout)) > 0)
366 		return(read(sock, buf, buflen));
367 	else
368 		return(n);
369 }
370 
371 hs_res_selwait(sock, timeout)
372 int sock;
373 struct timeval *timeout;
374 {
375 	fd_set dsmask;
376 	register int n;
377 
378 	/*
379 	 * Wait for reply
380 	 */
381 	FD_ZERO(&dsmask);
382 	FD_SET(sock, &dsmask);
383 	n = select(sock+1, &dsmask, (fd_set *)NULL,
384 		   (fd_set *)NULL, timeout);
385 	return(n);
386 }
387 
388 /* Returns:
389    0: Success
390    -1: Error
391    -2: Permanent failure
392 */
393 hs_parse(msg, eom)
394 char *msg, *eom;
395 {
396 	register char *cp;
397 	register HEADER *hp;
398 	register int n, len;
399 	int qdcount, ancount;
400 	char key[PACKETSZ];
401 	char *key_cpy, *value, *hs_make_value();
402 	short type;
403 
404 	hp = (HEADER *)msg;
405 	if (hp->rcode != NOERROR || hp->opcode != QUERY) {
406 		char dq[20];
407 #ifdef DEBUG
408 		dlog("Bad response (%d) from nameserver %s", hp->rcode, inet_dquad(dq, hs_server_addr(servernum)->s_addr));
409 #endif DEBUG
410 		return(-1);
411 	}
412 	cp = msg + sizeof(HEADER);
413 	ancount = ntohs(hp->ancount);
414 	qdcount = ntohs(hp->qdcount);
415 	while (qdcount-- > 0)
416 		cp += dn_skipname(cp, eom) + QFIXEDSZ;
417 	if (soacnt == 0 && ancount == 0) {
418 		/* XXX We should look for NS records to find SOA */
419 #ifdef DEBUG
420 		dlog("No SOA found");
421 #endif
422 		return(-2);
423 	}
424 	while (ancount-- > 0 && cp < eom) {
425 		if ((n = dn_expand(msg, eom, cp, key, PACKETSZ)) < 0)
426 			break;
427 		cp += n;
428 		if ((type = _getshort(cp)) == T_SOA) {
429 			soacnt++;
430 		}
431 		cp += 2*sizeof(u_short) + sizeof(u_long);
432 		len = _getshort(cp);
433 		cp += sizeof(u_short);
434 		/* Check to see if key is in our domain */
435 		if (type == T_TXT && hs_strip_our_domain(key)) {
436 			value = hs_make_value(cp, len);
437 			if (value == NULL)
438 				return(-1);
439 			key_cpy = strdup(key);
440 #ifdef DEBUG
441 			dlog("hs_parse: Parsed key: %s, value: %s", key,
442 			     value);
443 #endif
444 			mapc_add_kv(hs_map, key_cpy, value);
445 		}
446 		cp += len;
447 		errno = 0;
448 	}
449 	return(0);
450 }
451 
452 /* Check to see if the domain name in the supplied argument matches
453    hs_domain.  Strip hs_domain from supplied argument if so. */
454 hs_strip_our_domain(name)
455 char *name;
456 {
457 	char *end_pos;
458 	short targ_len, cur_len;
459 
460 	targ_len = strlen(hs_domain);
461 	cur_len = strlen(name);
462 	if (cur_len <= targ_len)
463 		return(0);
464 	end_pos = &name[cur_len - targ_len];
465 	if (strcmp(end_pos, hs_domain) != 0)
466 		return(0);
467 	if (*--end_pos != '.')
468 		return(0);
469 	*end_pos = '\0';
470 	return(1);
471 }
472 
473 #define MAXDATA 8*1024
474 
475 char *
476 hs_make_value(cp, len)
477 char *cp;
478 int len;
479 {
480 	char *value, *cpcpy, *valuep;
481 	int cnt, nextcnt, totalcnt, lencpy;
482 #ifdef DEBUG
483 	char *dbgname;
484 
485 	dbgname = &cp[1];
486 #endif DEBUG
487 
488 	lencpy = len;
489 	cpcpy = cp;
490 	totalcnt = 0;
491 	cnt = *cpcpy++;
492 	while (cnt) {
493 		totalcnt += cnt;
494 		lencpy -= cnt+1;
495 		if (lencpy == 0)
496 			break;
497 		nextcnt = cpcpy[cnt];
498 		cpcpy = &cpcpy[cnt+1];
499 		cnt = nextcnt;
500 	}
501 	if (totalcnt < 1 || totalcnt > MAXDATA || totalcnt > len) {
502 #ifdef DEBUG
503 		dlog("TXT RR not of expected length (%d %d): %s", totalcnt,
504 		     len, dbgname);
505 #endif DEBUG
506 		return(NULL);
507 	}
508 	/* Allocate null terminated string */
509 	value = (char *) xmalloc(totalcnt+1);
510 	value[totalcnt] = '\0';
511 	cnt = *cp++;
512 	valuep = value;
513 	while (cnt) {
514 		bcopy(cp, valuep, cnt);
515 		len -= cnt+1;
516 		if (len == 0)
517 			break;
518 		valuep = &valuep[cnt];
519 		nextcnt = cp[cnt];
520 		cp = &cp[cnt+1];
521 		cnt = nextcnt;
522 	}
523 	return(value);
524 }
525 
526 hs_make_ns_query(domain, ansbuf)
527 char *domain;
528 char *ansbuf;
529 {
530 	int status, len;
531 	char buf[PACKETSZ];
532 
533 	if ((len = res_mkquery(QUERY, domain, C_HS, T_NS,
534 			       (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1) {
535 #ifdef DEBUG
536 		dlog("hs_get_ns_list: res_mkquery failed");
537 #endif
538 		errno = 0;
539 		return(-1);
540 	}
541 	if ((status = res_send(buf, len, (char *)ansbuf, PACKETSZ)) == -1) {
542 #ifdef DEBUG
543 	    dlog("hs_get_ns_list: res_send failed.  status %d errno %d",
544 		 status, errno);
545 #endif
546 		errno = 0;
547 		return(-1);
548 	}
549 	return(0);
550 }
551 
552 static void
553 add_address(addr)
554 struct in_addr *addr;
555 {
556 	char dq[20];
557 	bcopy((char *)addr, nsaddr_list[hs_nscount++], sizeof(struct in_addr));
558 #ifdef DEBUG
559 	dlog("Adding NS address %s", inet_dquad(dq, addr->s_addr));
560 #endif DEBUG
561 }
562 
563 hs_get_ns_list(domain)
564 char *domain;
565 {
566 	register HEADER *hp;
567 	int qdcount, nscount;
568 	register char *cp;
569 	register int n, len;
570 	char key[PACKETSZ], name[PACKETSZ], msg[PACKETSZ], *eom;
571 	register long **hptr;
572 	struct hostent *ghp;
573 	int numns;
574 	char nsname[MAXHSNS][MAXDATA];
575 	int nshaveaddr[MAXHSNS], i;
576 	short type;
577 
578 	if (hs_make_ns_query(domain, msg) == -1)
579 		return(-1);
580 	numns = hs_nscount = 0;
581 	eom = &msg[PACKETSZ];
582 	bzero(nsname, sizeof(nsname));
583 	hp = (HEADER *)msg;
584 	if (hp->rcode != NOERROR || hp->opcode != QUERY) {
585 #ifdef DEBUG
586 		dlog("Bad response (%d) from nameserver %#x", hp->rcode,
587 		      hs_server_addr(servernum)->s_addr);
588 #endif DEBUG
589 		return(-1);
590 	}
591 	cp = msg + sizeof(HEADER);
592 	qdcount = ntohs(hp->qdcount);
593 	while (qdcount-- > 0)
594 		cp += dn_skipname(cp, eom) + QFIXEDSZ;
595 	nscount = ntohs(hp->ancount) + ntohs(hp->nscount) + ntohs(hp->arcount);
596 #ifdef DEBUG
597 	dlog("hs_get_ns_list: Processing %d response records", nscount);
598 #endif
599 	for (;nscount; nscount--) {
600 		if ((n = dn_expand(msg, eom, cp, key, PACKETSZ)) < 0)
601 			break;
602 		cp += n;
603 		type = _getshort(cp);
604 		cp += 2*sizeof(u_short) + sizeof(u_long);
605 		len = _getshort(cp);
606 		cp += sizeof(u_short);
607 #ifdef DEBUG
608 		dlog("hs_get_ns_list: Record type: %d", type);
609 #endif
610 		switch (type) {
611 		case T_NS:
612 			if (numns >= MAXHSNS || strcasecmp(domain, key) != 0)
613 				break;
614 			if ((n = dn_expand(msg, eom, cp, name, PACKETSZ)) < 0)
615 				break;
616 #ifdef DEBUG
617 			dlog("hs_get_ns_list: NS name: %s", name);
618 #endif
619 			for (i = 0; i < numns; i++)
620 				if (strcasecmp(nsname[i], name) == 0)
621 					break;
622 			if (i == numns) {
623 #ifdef DEBUG
624 				dlog("hs_get_ns_list: Saving name %s", name);
625 #endif
626 				strncpy(nsname[numns], name, MAXDATA);
627 				nshaveaddr[numns] = 0;
628 				numns++;
629 			}
630 			break;
631 		case T_A:
632 			if (hs_nscount == MAX_NSADDR)
633 				break;
634 			for (i = 0; i < numns; i++) {
635 				if (strcasecmp(nsname[i], domain) == 0) {
636 					nshaveaddr[i]++;
637 					add_address((struct in_addr *) cp);
638 					break;
639 				}
640 			}
641 			break;
642 		default:
643 			break;
644 		}
645 		if (hs_nscount == MAX_NSADDR)
646 			break;
647 		cp += len;
648 		errno = 0;
649 	}
650 #ifdef  DEBUG
651 	dlog("hs_get_ns_list: Found %d NS records", numns);
652 #endif
653 	for (i = 0; i < numns; i++) {
654 		if (nshaveaddr[i])
655 			continue;
656 		if ((ghp = gethostbyname(nsname[i])) == 0)
657 			continue;
658 		for (hptr = (long **)ghp->h_addr_list;
659 		     *hptr && hs_nscount < MAX_NSADDR; hptr++) {
660 			add_address((struct in_addr *) *hptr);
661 		}
662 	}
663 	if (hs_nscount)
664 		return(0);
665 #ifdef DEBUG
666 	dlog("No NS records found for %s", domain);
667 	return(-1);
668 #endif DEBUG
669 }
670 #endif /* HAS_HESIOD_RELOAD */
671 #endif /* HAS_HESIOD_MAPS */
672