Home | History | Annotate | Line # | Download | only in net
hesiod.c revision 1.9.2.1
      1 /*	$NetBSD: hesiod.c,v 1.9.2.1 2000/10/31 14:59:45 he Exp $	*/
      2 
      3 /* Copyright (c) 1996 by Internet Software Consortium.
      4  *
      5  * Permission to use, copy, modify, and distribute this software for any
      6  * purpose with or without fee is hereby granted, provided that the above
      7  * copyright notice and this permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
     10  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
     11  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
     12  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
     14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
     15  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     16  * SOFTWARE.
     17  */
     18 
     19 /* Copyright 1996 by the Massachusetts Institute of Technology.
     20  *
     21  * Permission to use, copy, modify, and distribute this
     22  * software and its documentation for any purpose and without
     23  * fee is hereby granted, provided that the above copyright
     24  * notice appear in all copies and that both that copyright
     25  * notice and this permission notice appear in supporting
     26  * documentation, and that the name of M.I.T. not be used in
     27  * advertising or publicity pertaining to distribution of the
     28  * software without specific, written prior permission.
     29  * M.I.T. makes no representations about the suitability of
     30  * this software for any purpose.  It is provided "as is"
     31  * without express or implied warranty.
     32  */
     33 
     34 /* This file is part of the hesiod library.  It implements the core
     35  * portion of the hesiod resolver.
     36  *
     37  * This file is loosely based on an interim version of hesiod.c from
     38  * the BIND IRS library, which was in turn based on an earlier version
     39  * of this file.  Extensive changes have been made on each step of the
     40  * path.
     41  *
     42  * This implementation is not truly thread-safe at the moment because
     43  * it uses res_send() and accesses _res.
     44  */
     45 
     46 #include <sys/cdefs.h>
     47 
     48 #if defined(LIBC_SCCS) && !defined(lint)
     49 __IDSTRING(rcsid_hesiod_c,
     50     "#Id: hesiod.c,v 1.18.2.1 1997/01/03 20:48:20 ghudson Exp #");
     51 __IDSTRING(rcsid_hesiod_p_h,
     52     "#Id: hesiod_p.h,v 1.1 1996/12/08 21:39:37 ghudson Exp #");
     53 __IDSTRING(rcsid_hescompat_c,
     54     "#Id: hescompat.c,v 1.1.2.1 1996/12/16 08:37:45 ghudson Exp #");
     55 __RCSID("$NetBSD: hesiod.c,v 1.9.2.1 2000/10/31 14:59:45 he Exp $");
     56 #endif /* LIBC_SCCS and not lint */
     57 
     58 #include "namespace.h"
     59 
     60 #include <sys/types.h>
     61 #include <sys/param.h>
     62 #include <netinet/in.h>
     63 #include <arpa/nameser.h>
     64 
     65 #include <ctype.h>
     66 #include <errno.h>
     67 #include <hesiod.h>
     68 #include <resolv.h>
     69 #include <stdio.h>
     70 #include <stdlib.h>
     71 #include <string.h>
     72 #include <unistd.h>
     73 
     74 #ifdef __weak_alias
     75 __weak_alias(hesiod_init,_hesiod_init);
     76 __weak_alias(hesiod_end,_hesiod_end);
     77 __weak_alias(hesiod_to_bind,_hesiod_to_bind);
     78 __weak_alias(hesiod_resolve,_hesiod_resolve);
     79 __weak_alias(hesiod_free_list,_hesiod_free_list);
     80 __weak_alias(hes_init,_hes_init);
     81 __weak_alias(hes_to_bind,_hes_to_bind);
     82 __weak_alias(hes_resolve,_hes_resolve);
     83 __weak_alias(hes_error,_hes_error);
     84 __weak_alias(hes_free,_hes_free);
     85 #endif
     86 
     87 struct hesiod_p {
     88 	char	*lhs;			/* normally ".ns" */
     89 	char	*rhs;			/* AKA the default hesiod domain */
     90 	int	 classes[2];		/* The class search order. */
     91 };
     92 
     93 #define	MAX_HESRESP	1024
     94 
     95 static int	  read_config_file __P((struct hesiod_p *, const char *));
     96 static char	**get_txt_records __P((int, const char *));
     97 static int	  init_context __P((void));
     98 static void	  translate_errors __P((void));
     99 
    100 
    101 /*
    102  * hesiod_init --
    103  *	initialize a hesiod_p.
    104  */
    105 int
    106 hesiod_init(context)
    107 	void	**context;
    108 {
    109 	struct hesiod_p	*ctx;
    110 	const char	*p, *configname;
    111 
    112 	ctx = malloc(sizeof(struct hesiod_p));
    113 	if (ctx) {
    114 		*context = ctx;
    115 			/*
    116 			 * don't permit overrides from environment
    117 			 * for set.id programs
    118 			 */
    119 		if (issetugid())
    120 			configname = NULL;
    121 		else
    122 			configname = getenv("HESIOD_CONFIG");
    123 		if (!configname)
    124 			configname = _PATH_HESIOD_CONF;
    125 		if (read_config_file(ctx, configname) >= 0) {
    126 			/*
    127 			 * The default rhs can be overridden by an
    128 			 * environment variable, unless set.id.
    129 			 */
    130 			if (issetugid())
    131 				p = NULL;
    132 			else
    133 				p = getenv("HES_DOMAIN");
    134 			if (p) {
    135 				if (ctx->rhs)
    136 					free(ctx->rhs);
    137 				ctx->rhs = malloc(strlen(p) + 2);
    138 				if (ctx->rhs) {
    139 					*ctx->rhs = '.';
    140 					strcpy(ctx->rhs + 1,
    141 					    (*p == '.') ? p + 1 : p);
    142 					return 0;
    143 				} else
    144 					errno = ENOMEM;
    145 			} else
    146 				return 0;
    147 		}
    148 	} else
    149 		errno = ENOMEM;
    150 
    151 	if (ctx->lhs)
    152 		free(ctx->lhs);
    153 	if (ctx->rhs)
    154 		free(ctx->rhs);
    155 	if (ctx)
    156 		free(ctx);
    157 	return -1;
    158 }
    159 
    160 /*
    161  * hesiod_end --
    162  *	Deallocates the hesiod_p.
    163  */
    164 void
    165 hesiod_end(context)
    166 	void	*context;
    167 {
    168 	struct hesiod_p *ctx = (struct hesiod_p *) context;
    169 
    170 	free(ctx->rhs);
    171 	if (ctx->lhs)
    172 		free(ctx->lhs);
    173 	free(ctx);
    174 }
    175 
    176 /*
    177  * hesiod_to_bind --
    178  * 	takes a hesiod (name, type) and returns a DNS
    179  *	name which is to be resolved.
    180  */
    181 char *
    182 hesiod_to_bind(void *context, const char *name, const char *type)
    183 {
    184 	struct hesiod_p *ctx = (struct hesiod_p *) context;
    185 	char		 bindname[MAXDNAME], *p, *ret, **rhs_list = NULL;
    186 	const char	*rhs;
    187 	int		 len;
    188 
    189 	strcpy(bindname, name);
    190 
    191 		/*
    192 		 * Find the right right hand side to use, possibly
    193 		 * truncating bindname.
    194 		 */
    195 	p = strchr(bindname, '@');
    196 	if (p) {
    197 		*p++ = 0;
    198 		if (strchr(p, '.'))
    199 			rhs = name + (p - bindname);
    200 		else {
    201 			rhs_list = hesiod_resolve(context, p, "rhs-extension");
    202 			if (rhs_list)
    203 				rhs = *rhs_list;
    204 			else {
    205 				errno = ENOENT;
    206 				return NULL;
    207 			}
    208 		}
    209 	} else
    210 		rhs = ctx->rhs;
    211 
    212 		/* See if we have enough room. */
    213 	len = strlen(bindname) + 1 + strlen(type);
    214 	if (ctx->lhs)
    215 		len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
    216 	len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0);
    217 	if (len > sizeof(bindname) - 1) {
    218 		if (rhs_list)
    219 			hesiod_free_list(context, rhs_list);
    220 		errno = EMSGSIZE;
    221 		return NULL;
    222 	}
    223 		/* Put together the rest of the domain. */
    224 	strcat(bindname, ".");
    225 	strcat(bindname, type);
    226 		/* Only append lhs if it isn't empty. */
    227 	if (ctx->lhs && ctx->lhs[0] != '\0' ) {
    228 		if (ctx->lhs[0] != '.')
    229 			strcat(bindname, ".");
    230 		strcat(bindname, ctx->lhs);
    231 	}
    232 	if (rhs[0] != '.')
    233 		strcat(bindname, ".");
    234 	strcat(bindname, rhs);
    235 
    236 		/* rhs_list is no longer needed, since we're done with rhs. */
    237 	if (rhs_list)
    238 		hesiod_free_list(context, rhs_list);
    239 
    240 		/* Make a copy of the result and return it to the caller. */
    241 	ret = strdup(bindname);
    242 	if (!ret)
    243 		errno = ENOMEM;
    244 	return ret;
    245 }
    246 
    247 /*
    248  * hesiod_resolve --
    249  *	Given a hesiod name and type, return an array of strings returned
    250  *	by the resolver.
    251  */
    252 char **
    253 hesiod_resolve(context, name, type)
    254 	void		*context;
    255 	const char	*name;
    256 	const char	*type;
    257 {
    258 	struct hesiod_p	*ctx = (struct hesiod_p *) context;
    259 	char		*bindname, **retvec;
    260 
    261 	bindname = hesiod_to_bind(context, name, type);
    262 	if (!bindname)
    263 		return NULL;
    264 
    265 	retvec = get_txt_records(ctx->classes[0], bindname);
    266 	if (retvec == NULL && errno == ENOENT && ctx->classes[1])
    267 		retvec = get_txt_records(ctx->classes[1], bindname);
    268 
    269 	free(bindname);
    270 	return retvec;
    271 }
    272 
    273 /*ARGSUSED*/
    274 void
    275 hesiod_free_list(context, list)
    276 	void	 *context;
    277 	char	**list;
    278 {
    279 	char  **p;
    280 
    281 	if (list == NULL)
    282 		return;
    283 	for (p = list; *p; p++)
    284 		free(*p);
    285 	free(list);
    286 }
    287 
    288 
    289 /* read_config_file --
    290  *	Parse the /etc/hesiod.conf file.  Returns 0 on success,
    291  *	-1 on failure.  On failure, it might leave values in ctx->lhs
    292  *	or ctx->rhs which need to be freed by the caller.
    293  */
    294 static int
    295 read_config_file(ctx, filename)
    296 	struct hesiod_p	*ctx;
    297 	const char	*filename;
    298 {
    299 	char	*key, *data, *p, **which;
    300 	char	 buf[MAXDNAME + 7];
    301 	int	 n;
    302 	FILE	*fp;
    303 
    304 		/* Set default query classes. */
    305 	ctx->classes[0] = C_IN;
    306 	ctx->classes[1] = C_HS;
    307 
    308 		/* Try to open the configuration file. */
    309 	fp = fopen(filename, "r");
    310 	if (!fp) {
    311 		/* Use compiled in default domain names. */
    312 		ctx->lhs = strdup(DEF_LHS);
    313 		ctx->rhs = strdup(DEF_RHS);
    314 		if (ctx->lhs && ctx->rhs)
    315 			return 0;
    316 		else {
    317 			errno = ENOMEM;
    318 			return -1;
    319 		}
    320 	}
    321 	ctx->lhs = NULL;
    322 	ctx->rhs = NULL;
    323 	while (fgets(buf, sizeof(buf), fp) != NULL) {
    324 		p = buf;
    325 		if (*p == '#' || *p == '\n' || *p == '\r')
    326 			continue;
    327 		while (*p == ' ' || *p == '\t')
    328 			p++;
    329 		key = p;
    330 		while (*p != ' ' && *p != '\t' && *p != '=')
    331 			p++;
    332 		*p++ = 0;
    333 
    334 		while (isspace(*p) || *p == '=')
    335 			p++;
    336 		data = p;
    337 		while (!isspace(*p))
    338 			p++;
    339 		*p = 0;
    340 
    341 		if (strcasecmp(key, "lhs") == 0 ||
    342 		    strcasecmp(key, "rhs") == 0) {
    343 			which = (strcasecmp(key, "lhs") == 0)
    344 			    ? &ctx->lhs : &ctx->rhs;
    345 			*which = strdup(data);
    346 			if (!*which) {
    347 				errno = ENOMEM;
    348 				return -1;
    349 			}
    350 		} else {
    351 			if (strcasecmp(key, "classes") == 0) {
    352 				n = 0;
    353 				while (*data && n < 2) {
    354 					p = data;
    355 					while (*p && *p != ',')
    356 						p++;
    357 					if (*p)
    358 						*p++ = 0;
    359 					if (strcasecmp(data, "IN") == 0)
    360 						ctx->classes[n++] = C_IN;
    361 					else
    362 						if (strcasecmp(data, "HS") == 0)
    363 							ctx->classes[n++] =
    364 							    C_HS;
    365 					data = p;
    366 				}
    367 				while (n < 2)
    368 					ctx->classes[n++] = 0;
    369 			}
    370 		}
    371 	}
    372 	fclose(fp);
    373 
    374 	if (!ctx->rhs || ctx->classes[0] == 0 ||
    375 	    ctx->classes[0] == ctx->classes[1]) {
    376 		errno = ENOEXEC;
    377 		return -1;
    378 	}
    379 	return 0;
    380 }
    381 
    382 /*
    383  * get_txt_records --
    384  *	Given a DNS class and a DNS name, do a lookup for TXT records, and
    385  *	return a list of them.
    386  */
    387 static char **
    388 get_txt_records(qclass, name)
    389 	int		 qclass;
    390 	const char	*name;
    391 {
    392 	HEADER		*hp;
    393 	unsigned char	 qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
    394 	char		*dst, **list;
    395 	int		 ancount, qdcount, i, j, n, skip, type, class, len;
    396 
    397 		/* Make sure the resolver is initialized. */
    398 	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
    399 		return NULL;
    400 
    401 		/* Construct the query. */
    402 	n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0,
    403 	    NULL, qbuf, PACKETSZ);
    404 	if (n < 0)
    405 		return NULL;
    406 
    407 		/* Send the query. */
    408 	n = res_send(qbuf, n, abuf, MAX_HESRESP);
    409 	if (n < 0) {
    410 		errno = ECONNREFUSED;
    411 		return NULL;
    412 	}
    413 		/* Parse the header of the result. */
    414 	hp = (HEADER *) (void *) abuf;
    415 	ancount = ntohs(hp->ancount);
    416 	qdcount = ntohs(hp->qdcount);
    417 	p = abuf + sizeof(HEADER);
    418 	eom = abuf + n;
    419 
    420 		/*
    421 		 * Skip questions, trying to get to the answer section
    422 		 * which follows.
    423 		 */
    424 	for (i = 0; i < qdcount; i++) {
    425 		skip = dn_skipname(p, eom);
    426 		if (skip < 0 || p + skip + QFIXEDSZ > eom) {
    427 			errno = EMSGSIZE;
    428 			return NULL;
    429 		}
    430 		p += skip + QFIXEDSZ;
    431 	}
    432 
    433 		/* Allocate space for the text record answers. */
    434 	list = malloc((ancount + 1) * sizeof(char *));
    435 	if (!list) {
    436 		errno = ENOMEM;
    437 		return NULL;
    438 	}
    439 		/* Parse the answers. */
    440 	j = 0;
    441 	for (i = 0; i < ancount; i++) {
    442 		/* Parse the header of this answer. */
    443 		skip = dn_skipname(p, eom);
    444 		if (skip < 0 || p + skip + 10 > eom)
    445 			break;
    446 		type = p[skip + 0] << 8 | p[skip + 1];
    447 		class = p[skip + 2] << 8 | p[skip + 3];
    448 		len = p[skip + 8] << 8 | p[skip + 9];
    449 		p += skip + 10;
    450 		if (p + len > eom) {
    451 			errno = EMSGSIZE;
    452 			break;
    453 		}
    454 		/* Skip entries of the wrong class and type. */
    455 		if (class != qclass || type != T_TXT) {
    456 			p += len;
    457 			continue;
    458 		}
    459 		/* Allocate space for this answer. */
    460 		list[j] = malloc((size_t)len);
    461 		if (!list[j]) {
    462 			errno = ENOMEM;
    463 			break;
    464 		}
    465 		dst = list[j++];
    466 
    467 		/* Copy answer data into the allocated area. */
    468 		eor = p + len;
    469 		while (p < eor) {
    470 			n = (unsigned char) *p++;
    471 			if (p + n > eor) {
    472 				errno = EMSGSIZE;
    473 				break;
    474 			}
    475 			memcpy(dst, p, (size_t)n);
    476 			p += n;
    477 			dst += n;
    478 		}
    479 		if (p < eor) {
    480 			errno = EMSGSIZE;
    481 			break;
    482 		}
    483 		*dst = 0;
    484 	}
    485 
    486 		/*
    487 		 * If we didn't terminate the loop normally, something
    488 		 * went wrong.
    489 		 */
    490 	if (i < ancount) {
    491 		for (i = 0; i < j; i++)
    492 			free(list[i]);
    493 		free(list);
    494 		return NULL;
    495 	}
    496 	if (j == 0) {
    497 		errno = ENOENT;
    498 		free(list);
    499 		return NULL;
    500 	}
    501 	list[j] = NULL;
    502 	return list;
    503 }
    504 
    505 		/*
    506 		 *	COMPATIBILITY FUNCTIONS
    507 		 */
    508 
    509 static int	  inited = 0;
    510 static void	 *context;
    511 static int	  errval = HES_ER_UNINIT;
    512 
    513 int
    514 hes_init()
    515 {
    516 	init_context();
    517 	return errval;
    518 }
    519 
    520 char *
    521 hes_to_bind(name, type)
    522 	const char	*name;
    523 	const char	*type;
    524 {
    525 	static	char	*bindname;
    526 	if (init_context() < 0)
    527 		return NULL;
    528 	if (bindname)
    529 		free(bindname);
    530 	bindname = hesiod_to_bind(context, name, type);
    531 	if (!bindname)
    532 		translate_errors();
    533 	return bindname;
    534 }
    535 
    536 char **
    537 hes_resolve(name, type)
    538 	const char	*name;
    539 	const char	*type;
    540 {
    541 	static char	**list;
    542 
    543 	if (init_context() < 0)
    544 		return NULL;
    545 
    546 	/*
    547 	 * In the old Hesiod interface, the caller was responsible for
    548 	 * freeing the returned strings but not the vector of strings itself.
    549 	 */
    550 	if (list)
    551 		free(list);
    552 
    553 	list = hesiod_resolve(context, name, type);
    554 	if (!list)
    555 		translate_errors();
    556 	return list;
    557 }
    558 
    559 int
    560 hes_error()
    561 {
    562 	return errval;
    563 }
    564 
    565 void
    566 hes_free(hp)
    567 	char **hp;
    568 {
    569 	hesiod_free_list(context, hp);
    570 }
    571 
    572 static int
    573 init_context()
    574 {
    575 	if (!inited) {
    576 		inited = 1;
    577 		if (hesiod_init(&context) < 0) {
    578 			errval = HES_ER_CONFIG;
    579 			return -1;
    580 		}
    581 		errval = HES_ER_OK;
    582 	}
    583 	return 0;
    584 }
    585 
    586 static void
    587 translate_errors()
    588 {
    589 	switch (errno) {
    590 	case ENOENT:
    591 		errval = HES_ER_NOTFOUND;
    592 		break;
    593 	case ECONNREFUSED:
    594 	case EMSGSIZE:
    595 		errval = HES_ER_NET;
    596 		break;
    597 	case ENOMEM:
    598 	default:
    599 		/* Not a good match, but the best we can do. */
    600 		errval = HES_ER_CONFIG;
    601 		break;
    602 	}
    603 }
    604