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