xref: /netbsd-src/lib/libc/net/hesiod.c (revision 4644559ea4f43b5dbe0f8cc1aa0388457c1603e2)
1*4644559eSmaya /*	$NetBSD: hesiod.c,v 1.30 2017/03/10 18:02:32 maya Exp $	*/
2de3b78d7Slukem 
37a51f6dfSlukem /* Copyright (c) 1996 by Internet Software Consortium.
4de3b78d7Slukem  *
57a51f6dfSlukem  * Permission to use, copy, modify, and distribute this software for any
67a51f6dfSlukem  * purpose with or without fee is hereby granted, provided that the above
77a51f6dfSlukem  * copyright notice and this permission notice appear in all copies.
8de3b78d7Slukem  *
97a51f6dfSlukem  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
107a51f6dfSlukem  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
117a51f6dfSlukem  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
127a51f6dfSlukem  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
137a51f6dfSlukem  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
147a51f6dfSlukem  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
157a51f6dfSlukem  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
167a51f6dfSlukem  * SOFTWARE.
177a51f6dfSlukem  */
187a51f6dfSlukem 
197a51f6dfSlukem /* Copyright 1996 by the Massachusetts Institute of Technology.
20de3b78d7Slukem  *
217a51f6dfSlukem  * Permission to use, copy, modify, and distribute this
227a51f6dfSlukem  * software and its documentation for any purpose and without
237a51f6dfSlukem  * fee is hereby granted, provided that the above copyright
247a51f6dfSlukem  * notice appear in all copies and that both that copyright
257a51f6dfSlukem  * notice and this permission notice appear in supporting
267a51f6dfSlukem  * documentation, and that the name of M.I.T. not be used in
277a51f6dfSlukem  * advertising or publicity pertaining to distribution of the
287a51f6dfSlukem  * software without specific, written prior permission.
297a51f6dfSlukem  * M.I.T. makes no representations about the suitability of
307a51f6dfSlukem  * this software for any purpose.  It is provided "as is"
317a51f6dfSlukem  * without express or implied warranty.
327a51f6dfSlukem  */
337a51f6dfSlukem 
347a51f6dfSlukem /* This file is part of the hesiod library.  It implements the core
357a51f6dfSlukem  * portion of the hesiod resolver.
367a51f6dfSlukem  *
377a51f6dfSlukem  * This file is loosely based on an interim version of hesiod.c from
387a51f6dfSlukem  * the BIND IRS library, which was in turn based on an earlier version
397a51f6dfSlukem  * of this file.  Extensive changes have been made on each step of the
407a51f6dfSlukem  * path.
417a51f6dfSlukem  *
42d14c1915Schristos  * This implementation is thread-safe because it uses res_nsend().
43de3b78d7Slukem  */
44de3b78d7Slukem 
45de3b78d7Slukem #include <sys/cdefs.h>
46de3b78d7Slukem 
477a51f6dfSlukem #if defined(LIBC_SCCS) && !defined(lint)
487a51f6dfSlukem __IDSTRING(rcsid_hesiod_c,
497a51f6dfSlukem     "#Id: hesiod.c,v 1.18.2.1 1997/01/03 20:48:20 ghudson Exp #");
507a51f6dfSlukem __IDSTRING(rcsid_hesiod_p_h,
517a51f6dfSlukem     "#Id: hesiod_p.h,v 1.1 1996/12/08 21:39:37 ghudson Exp #");
527a51f6dfSlukem __IDSTRING(rcsid_hescompat_c,
537a51f6dfSlukem     "#Id: hescompat.c,v 1.1.2.1 1996/12/16 08:37:45 ghudson Exp #");
54*4644559eSmaya __RCSID("$NetBSD: hesiod.c,v 1.30 2017/03/10 18:02:32 maya Exp $");
557a51f6dfSlukem #endif /* LIBC_SCCS and not lint */
567a51f6dfSlukem 
577a51f6dfSlukem #include "namespace.h"
58de3b78d7Slukem 
59de3b78d7Slukem #include <sys/types.h>
60de3b78d7Slukem #include <sys/param.h>
61de3b78d7Slukem #include <netinet/in.h>
62de3b78d7Slukem #include <arpa/nameser.h>
63de3b78d7Slukem 
64b48252f3Slukem #include <assert.h>
657a51f6dfSlukem #include <ctype.h>
66de3b78d7Slukem #include <errno.h>
67de3b78d7Slukem #include <hesiod.h>
68de3b78d7Slukem #include <resolv.h>
69de3b78d7Slukem #include <stdio.h>
70de3b78d7Slukem #include <stdlib.h>
71de3b78d7Slukem #include <string.h>
72afc75b9eSlukem #include <unistd.h>
73de3b78d7Slukem 
747a51f6dfSlukem #ifdef __weak_alias
7560549036Smycroft __weak_alias(hesiod_init,_hesiod_init)
7660549036Smycroft __weak_alias(hesiod_end,_hesiod_end)
7760549036Smycroft __weak_alias(hesiod_to_bind,_hesiod_to_bind)
7860549036Smycroft __weak_alias(hesiod_resolve,_hesiod_resolve)
7960549036Smycroft __weak_alias(hesiod_free_list,_hesiod_free_list)
8060549036Smycroft __weak_alias(hes_init,_hes_init)
8160549036Smycroft __weak_alias(hes_to_bind,_hes_to_bind)
8260549036Smycroft __weak_alias(hes_resolve,_hes_resolve)
8360549036Smycroft __weak_alias(hes_error,_hes_error)
8460549036Smycroft __weak_alias(hes_free,_hes_free)
857a51f6dfSlukem #endif
86de3b78d7Slukem 
877a51f6dfSlukem struct hesiod_p {
887a51f6dfSlukem 	char	*lhs;			/* normally ".ns" */
897a51f6dfSlukem 	char	*rhs;			/* AKA the default hesiod domain */
907a51f6dfSlukem 	int	 classes[2];		/* The class search order. */
917a51f6dfSlukem };
92de3b78d7Slukem 
937a51f6dfSlukem #define	MAX_HESRESP	1024
94de3b78d7Slukem 
95504f8671Smatt static int	  read_config_file(struct hesiod_p *, const char *);
96504f8671Smatt static char	**get_txt_records(int, const char *);
97504f8671Smatt static int	  init_context(void);
98504f8671Smatt static void	  translate_errors(void);
99de3b78d7Slukem 
100de3b78d7Slukem 
1017a51f6dfSlukem /*
1027a51f6dfSlukem  * hesiod_init --
1037a51f6dfSlukem  *	initialize a hesiod_p.
1047a51f6dfSlukem  */
1057a51f6dfSlukem int
hesiod_init(void ** context)106504f8671Smatt hesiod_init(void **context)
107de3b78d7Slukem {
1087a51f6dfSlukem 	struct hesiod_p	*ctx;
1097a51f6dfSlukem 	const char	*p, *configname;
110b48252f3Slukem 	int serrno;
111b48252f3Slukem 
112b48252f3Slukem 	_DIAGASSERT(context != NULL);
113de3b78d7Slukem 
114efd08c7bSlukem 	ctx = calloc(1, sizeof(struct hesiod_p));
1157a51f6dfSlukem 	if (ctx) {
1167a51f6dfSlukem 		*context = ctx;
117afc75b9eSlukem 		/*
118afc75b9eSlukem 		 * don't permit overrides from environment
119afc75b9eSlukem 		 * for set.id programs
120afc75b9eSlukem 		 */
121afc75b9eSlukem 		if (issetugid())
122afc75b9eSlukem 			configname = NULL;
123afc75b9eSlukem 		else
1247a51f6dfSlukem 			configname = getenv("HESIOD_CONFIG");
1257a51f6dfSlukem 		if (!configname)
1267a51f6dfSlukem 			configname = _PATH_HESIOD_CONF;
1277a51f6dfSlukem 		if (read_config_file(ctx, configname) >= 0) {
1287a51f6dfSlukem 			/*
1297a51f6dfSlukem 			 * The default rhs can be overridden by an
130afc75b9eSlukem 			 * environment variable, unless set.id.
1317a51f6dfSlukem 			 */
132afc75b9eSlukem 			if (issetugid())
133afc75b9eSlukem 				p = NULL;
134afc75b9eSlukem 			else
1357a51f6dfSlukem 				p = getenv("HES_DOMAIN");
1367a51f6dfSlukem 			if (p) {
1377a51f6dfSlukem 				if (ctx->rhs)
1387a51f6dfSlukem 					free(ctx->rhs);
1397a51f6dfSlukem 				ctx->rhs = malloc(strlen(p) + 2);
1407a51f6dfSlukem 				if (ctx->rhs) {
1417a51f6dfSlukem 					*ctx->rhs = '.';
1427a51f6dfSlukem 					strcpy(ctx->rhs + 1,
1437a51f6dfSlukem 					    (*p == '.') ? p + 1 : p);
1447a51f6dfSlukem 					return 0;
1457a51f6dfSlukem 				} else
1467a51f6dfSlukem 					errno = ENOMEM;
1477a51f6dfSlukem 			} else
1487a51f6dfSlukem 				return 0;
149de3b78d7Slukem 		}
1507a51f6dfSlukem 	} else
1517a51f6dfSlukem 		errno = ENOMEM;
152de3b78d7Slukem 
153b48252f3Slukem 	serrno = errno;
154efd08c7bSlukem 	if (ctx) {
1557a51f6dfSlukem 		free(ctx->lhs);
1567a51f6dfSlukem 		free(ctx->rhs);
1577a51f6dfSlukem 		free(ctx);
158efd08c7bSlukem 	}
159b48252f3Slukem 	errno = serrno;
1607a51f6dfSlukem 	return -1;
161de3b78d7Slukem }
162de3b78d7Slukem 
163de3b78d7Slukem /*
1647a51f6dfSlukem  * hesiod_end --
1657a51f6dfSlukem  *	Deallocates the hesiod_p.
166de3b78d7Slukem  */
1677a51f6dfSlukem void
hesiod_end(void * context)168504f8671Smatt hesiod_end(void *context)
169de3b78d7Slukem {
1707a51f6dfSlukem 	struct hesiod_p *ctx = (struct hesiod_p *) context;
171de3b78d7Slukem 
172b48252f3Slukem 	_DIAGASSERT(context != NULL);
173b48252f3Slukem 
1747a51f6dfSlukem 	free(ctx->rhs);
1757a51f6dfSlukem 	if (ctx->lhs)
1767a51f6dfSlukem 		free(ctx->lhs);
1777a51f6dfSlukem 	free(ctx);
178de3b78d7Slukem }
179de3b78d7Slukem 
1807a51f6dfSlukem /*
1817a51f6dfSlukem  * hesiod_to_bind --
1827a51f6dfSlukem  * 	takes a hesiod (name, type) and returns a DNS
1837a51f6dfSlukem  *	name which is to be resolved.
1847a51f6dfSlukem  */
1857a51f6dfSlukem char *
hesiod_to_bind(void * context,const char * name,const char * type)1867a51f6dfSlukem hesiod_to_bind(void *context, const char *name, const char *type)
1877a51f6dfSlukem {
1887a51f6dfSlukem 	struct hesiod_p *ctx = (struct hesiod_p *) context;
1897a51f6dfSlukem 	char		 bindname[MAXDNAME], *p, *ret, **rhs_list = NULL;
1907a51f6dfSlukem 	const char	*rhs;
1911289029fSthorpej 	size_t		 len;
192de3b78d7Slukem 
193b48252f3Slukem 	_DIAGASSERT(context != NULL);
194b48252f3Slukem 	_DIAGASSERT(name != NULL);
195b48252f3Slukem 	_DIAGASSERT(type != NULL);
196b48252f3Slukem 
19737a3c0e7Ssommerfeld         if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) {
19837a3c0e7Ssommerfeld                 errno = EMSGSIZE;
19937a3c0e7Ssommerfeld                 return NULL;
20037a3c0e7Ssommerfeld         }
2017a51f6dfSlukem 
2027a51f6dfSlukem 	/*
2037a51f6dfSlukem 	 * Find the right right hand side to use, possibly
2047a51f6dfSlukem 	 * truncating bindname.
2057a51f6dfSlukem 	 */
2067a51f6dfSlukem 	p = strchr(bindname, '@');
2077a51f6dfSlukem 	if (p) {
2087a51f6dfSlukem 		*p++ = 0;
2097a51f6dfSlukem 		if (strchr(p, '.'))
2107a51f6dfSlukem 			rhs = name + (p - bindname);
2117a51f6dfSlukem 		else {
2127a51f6dfSlukem 			rhs_list = hesiod_resolve(context, p, "rhs-extension");
2137a51f6dfSlukem 			if (rhs_list)
2147a51f6dfSlukem 				rhs = *rhs_list;
2157a51f6dfSlukem 			else {
2167a51f6dfSlukem 				errno = ENOENT;
2177a51f6dfSlukem 				return NULL;
2187a51f6dfSlukem 			}
2197a51f6dfSlukem 		}
2207a51f6dfSlukem 	} else
2217a51f6dfSlukem 		rhs = ctx->rhs;
2227a51f6dfSlukem 
2237a51f6dfSlukem 	/* See if we have enough room. */
2247a51f6dfSlukem 	len = strlen(bindname) + 1 + strlen(type);
2257a51f6dfSlukem 	if (ctx->lhs)
2267a51f6dfSlukem 		len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
2277a51f6dfSlukem 	len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0);
2287a51f6dfSlukem 	if (len > sizeof(bindname) - 1) {
2297a51f6dfSlukem 		if (rhs_list)
2307a51f6dfSlukem 			hesiod_free_list(context, rhs_list);
231de3b78d7Slukem 		errno = EMSGSIZE;
2327a51f6dfSlukem 		return NULL;
2337a51f6dfSlukem 	}
2347a51f6dfSlukem 	/* Put together the rest of the domain. */
23590a8853fSitojun 	strlcat(bindname, ".", sizeof(bindname));
23690a8853fSitojun 	strlcat(bindname, type, sizeof(bindname));
23736362e2aSsimonb 	/* Only append lhs if it isn't empty. */
23836362e2aSsimonb 	if (ctx->lhs && ctx->lhs[0] != '\0' ) {
2397a51f6dfSlukem 		if (ctx->lhs[0] != '.')
24090a8853fSitojun 			strlcat(bindname, ".", sizeof(bindname));
24190a8853fSitojun 		strlcat(bindname, ctx->lhs, sizeof(bindname));
2427a51f6dfSlukem 	}
2437a51f6dfSlukem 	if (rhs[0] != '.')
24490a8853fSitojun 		strlcat(bindname, ".", sizeof(bindname));
24590a8853fSitojun 	strlcat(bindname, rhs, sizeof(bindname));
2467a51f6dfSlukem 
2477a51f6dfSlukem 	/* rhs_list is no longer needed, since we're done with rhs. */
2487a51f6dfSlukem 	if (rhs_list)
2497a51f6dfSlukem 		hesiod_free_list(context, rhs_list);
2507a51f6dfSlukem 
2517a51f6dfSlukem 	/* Make a copy of the result and return it to the caller. */
2527a51f6dfSlukem 	ret = strdup(bindname);
2536bf409aaSgroo 	if (ret == NULL)
2547a51f6dfSlukem 		errno = ENOMEM;
2557a51f6dfSlukem 	return ret;
256de3b78d7Slukem }
257de3b78d7Slukem 
2587a51f6dfSlukem /*
2597a51f6dfSlukem  * hesiod_resolve --
2607a51f6dfSlukem  *	Given a hesiod name and type, return an array of strings returned
2617a51f6dfSlukem  *	by the resolver.
2627a51f6dfSlukem  */
2637a51f6dfSlukem char **
hesiod_resolve(void * context,const char * name,const char * type)264504f8671Smatt hesiod_resolve(void *context, const char *name, const char *type)
2657a51f6dfSlukem {
2667a51f6dfSlukem 	struct hesiod_p	*ctx = (struct hesiod_p *) context;
2677a51f6dfSlukem 	char		*bindname, **retvec;
268de3b78d7Slukem 
269b48252f3Slukem 	_DIAGASSERT(context != NULL);
270b48252f3Slukem 	_DIAGASSERT(name != NULL);
271b48252f3Slukem 	_DIAGASSERT(type != NULL);
272b48252f3Slukem 
2737a51f6dfSlukem 	bindname = hesiod_to_bind(context, name, type);
2747a51f6dfSlukem 	if (!bindname)
2757a51f6dfSlukem 		return NULL;
276de3b78d7Slukem 
2777a51f6dfSlukem 	retvec = get_txt_records(ctx->classes[0], bindname);
2787a51f6dfSlukem 	if (retvec == NULL && errno == ENOENT && ctx->classes[1])
2797a51f6dfSlukem 		retvec = get_txt_records(ctx->classes[1], bindname);
280de3b78d7Slukem 
2817a51f6dfSlukem 	free(bindname);
2827a51f6dfSlukem 	return retvec;
2837a51f6dfSlukem }
2847a51f6dfSlukem 
2857a51f6dfSlukem /*ARGSUSED*/
2867a51f6dfSlukem void
hesiod_free_list(void * context,char ** list)287504f8671Smatt hesiod_free_list(void *context, char **list)
2887a51f6dfSlukem {
2897a51f6dfSlukem 	char  **p;
2907a51f6dfSlukem 
291b48252f3Slukem 	_DIAGASSERT(context != NULL);
292b48252f3Slukem 
2933ca17e24Slukem 	if (list == NULL)
2943ca17e24Slukem 		return;
2957a51f6dfSlukem 	for (p = list; *p; p++)
2967a51f6dfSlukem 		free(*p);
2977a51f6dfSlukem 	free(list);
2987a51f6dfSlukem }
2997a51f6dfSlukem 
3007a51f6dfSlukem 
3017a51f6dfSlukem /* read_config_file --
3027a51f6dfSlukem  *	Parse the /etc/hesiod.conf file.  Returns 0 on success,
3037a51f6dfSlukem  *	-1 on failure.  On failure, it might leave values in ctx->lhs
3047a51f6dfSlukem  *	or ctx->rhs which need to be freed by the caller.
3057a51f6dfSlukem  */
3067a51f6dfSlukem static int
read_config_file(struct hesiod_p * ctx,const char * filename)307504f8671Smatt read_config_file(struct hesiod_p *ctx, const char *filename)
3087a51f6dfSlukem {
3092204ab6bSchristos 	char	*buf, *key, *data, *p, **which;
3107a51f6dfSlukem 	int	 n;
3117a51f6dfSlukem 	FILE	*fp;
3127a51f6dfSlukem 
313b48252f3Slukem 	_DIAGASSERT(ctx != NULL);
314b48252f3Slukem 	_DIAGASSERT(filename != NULL);
315b48252f3Slukem 
3167a51f6dfSlukem 	/* Set default query classes. */
317712ba4aeSlukem 	ctx->classes[0] = C_IN;
318712ba4aeSlukem 	ctx->classes[1] = C_HS;
3197a51f6dfSlukem 
3207a51f6dfSlukem 	/* Try to open the configuration file. */
3219a513d96Schristos 	fp = fopen(filename, "re");
3227a51f6dfSlukem 	if (!fp) {
3237a51f6dfSlukem 		/* Use compiled in default domain names. */
3247a51f6dfSlukem 		ctx->lhs = strdup(DEF_LHS);
3257a51f6dfSlukem 		ctx->rhs = strdup(DEF_RHS);
3267a51f6dfSlukem 		if (ctx->lhs && ctx->rhs)
3277a51f6dfSlukem 			return 0;
3287a51f6dfSlukem 		else {
3297a51f6dfSlukem 			errno = ENOMEM;
3307a51f6dfSlukem 			return -1;
3317a51f6dfSlukem 		}
3327a51f6dfSlukem 	}
3337a51f6dfSlukem 	ctx->lhs = NULL;
3347a51f6dfSlukem 	ctx->rhs = NULL;
3352204ab6bSchristos 	for (; (buf = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCALL))
3362204ab6bSchristos 	    != NULL; free(buf)) {
3377a51f6dfSlukem 		p = buf;
3387a51f6dfSlukem 		while (*p == ' ' || *p == '\t')
3397a51f6dfSlukem 			p++;
3407a51f6dfSlukem 		key = p;
3416bf409aaSgroo 		while (*p != ' ' && *p != '\t' && *p != '=' && *p)
3427a51f6dfSlukem 			p++;
3436bf409aaSgroo 
3446bf409aaSgroo 		if (*p == '\0')
3456bf409aaSgroo 			continue;
3466bf409aaSgroo 
3477a51f6dfSlukem 		*p++ = 0;
3487a51f6dfSlukem 
3497daefc5aSitohy 		while (isspace((u_char) *p) || *p == '=')
3507a51f6dfSlukem 			p++;
3516bf409aaSgroo 
3526bf409aaSgroo 		if (*p == '\0')
3536bf409aaSgroo 			continue;
3546bf409aaSgroo 
3557a51f6dfSlukem 		data = p;
3566bf409aaSgroo 		while (!isspace((u_char) *p) && *p)
3577a51f6dfSlukem 			p++;
3586bf409aaSgroo 
3597a51f6dfSlukem 		*p = 0;
3607a51f6dfSlukem 
3617a51f6dfSlukem 		if (strcasecmp(key, "lhs") == 0 ||
3627a51f6dfSlukem 		    strcasecmp(key, "rhs") == 0) {
3637a51f6dfSlukem 			which = (strcasecmp(key, "lhs") == 0)
3647a51f6dfSlukem 			    ? &ctx->lhs : &ctx->rhs;
3657a51f6dfSlukem 			*which = strdup(data);
3667a51f6dfSlukem 			if (!*which) {
3677a51f6dfSlukem 				errno = ENOMEM;
3682204ab6bSchristos 				free(buf);
369aff1729aSwiz 				(void)fclose(fp);
3707a51f6dfSlukem 				return -1;
3717a51f6dfSlukem 			}
3727a51f6dfSlukem 		} else {
3737a51f6dfSlukem 			if (strcasecmp(key, "classes") == 0) {
3747a51f6dfSlukem 				n = 0;
3757a51f6dfSlukem 				while (*data && n < 2) {
3767a51f6dfSlukem 					p = data;
3777a51f6dfSlukem 					while (*p && *p != ',')
3787a51f6dfSlukem 						p++;
3797a51f6dfSlukem 					if (*p)
3807a51f6dfSlukem 						*p++ = 0;
3817a51f6dfSlukem 					if (strcasecmp(data, "IN") == 0)
3827a51f6dfSlukem 						ctx->classes[n++] = C_IN;
3837a51f6dfSlukem 					else
3847a51f6dfSlukem 						if (strcasecmp(data, "HS") == 0)
3857a51f6dfSlukem 							ctx->classes[n++] =
3867a51f6dfSlukem 							    C_HS;
3877a51f6dfSlukem 					data = p;
3887a51f6dfSlukem 				}
3897a51f6dfSlukem 				while (n < 2)
3907a51f6dfSlukem 					ctx->classes[n++] = 0;
3917a51f6dfSlukem 			}
3927a51f6dfSlukem 		}
3937a51f6dfSlukem 	}
3947a51f6dfSlukem 	fclose(fp);
3957a51f6dfSlukem 
3967a51f6dfSlukem 	if (!ctx->rhs || ctx->classes[0] == 0 ||
3977a51f6dfSlukem 	    ctx->classes[0] == ctx->classes[1]) {
3987a51f6dfSlukem 		errno = ENOEXEC;
3997a51f6dfSlukem 		return -1;
4007a51f6dfSlukem 	}
4017a51f6dfSlukem 	return 0;
4027a51f6dfSlukem }
4037a51f6dfSlukem 
4047a51f6dfSlukem /*
4057a51f6dfSlukem  * get_txt_records --
4067a51f6dfSlukem  *	Given a DNS class and a DNS name, do a lookup for TXT records, and
4077a51f6dfSlukem  *	return a list of them.
4087a51f6dfSlukem  */
4097a51f6dfSlukem static char **
get_txt_records(int qclass,const char * name)410504f8671Smatt get_txt_records(int qclass, const char *name)
4117a51f6dfSlukem {
4127a51f6dfSlukem 	HEADER		*hp;
4137a51f6dfSlukem 	unsigned char	 qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
4147a51f6dfSlukem 	char		*dst, **list;
4157a51f6dfSlukem 	int		 ancount, qdcount, i, j, n, skip, type, class, len;
416d14c1915Schristos 	res_state	 res = __res_get_state();
4177a51f6dfSlukem 
41813cc3543Schristos 	if (res == NULL)
4197a51f6dfSlukem 		return NULL;
42013cc3543Schristos 
42113cc3543Schristos 	_DIAGASSERT(name != NULL);
4227a51f6dfSlukem 
4237a51f6dfSlukem 	/* Construct the query. */
424d14c1915Schristos 	n = res_nmkquery(res, QUERY, name, qclass, T_TXT, NULL, 0,
4257a51f6dfSlukem 	    NULL, qbuf, PACKETSZ);
426dcb9078cSghudson 	if (n < 0) {
427dcb9078cSghudson 		errno = EMSGSIZE;
428d14c1915Schristos 		__res_put_state(res);
4297a51f6dfSlukem 		return NULL;
430dcb9078cSghudson 	}
4317a51f6dfSlukem 
4327a51f6dfSlukem 	/* Send the query. */
433d14c1915Schristos 	n = res_nsend(res, qbuf, n, abuf, MAX_HESRESP);
434d14c1915Schristos 	__res_put_state(res);
435de3b78d7Slukem 	if (n < 0) {
436de3b78d7Slukem 		errno = ECONNREFUSED;
4377a51f6dfSlukem 		return NULL;
438de3b78d7Slukem 	}
4397a51f6dfSlukem 	/* Parse the header of the result. */
4407a51f6dfSlukem 	hp = (HEADER *) (void *) abuf;
4417a51f6dfSlukem 	ancount = ntohs(hp->ancount);
4427a51f6dfSlukem 	qdcount = ntohs(hp->qdcount);
4437a51f6dfSlukem 	p = abuf + sizeof(HEADER);
4447a51f6dfSlukem 	eom = abuf + n;
445de3b78d7Slukem 
4467a51f6dfSlukem 	/*
4477a51f6dfSlukem 	 * Skip questions, trying to get to the answer section
4487a51f6dfSlukem 	 * which follows.
4497a51f6dfSlukem 	 */
4507a51f6dfSlukem 	for (i = 0; i < qdcount; i++) {
4517a51f6dfSlukem 		skip = dn_skipname(p, eom);
4527a51f6dfSlukem 		if (skip < 0 || p + skip + QFIXEDSZ > eom) {
4537a51f6dfSlukem 			errno = EMSGSIZE;
4547a51f6dfSlukem 			return NULL;
455de3b78d7Slukem 		}
4567a51f6dfSlukem 		p += skip + QFIXEDSZ;
4577a51f6dfSlukem 	}
4587a51f6dfSlukem 
4597a51f6dfSlukem 	/* Allocate space for the text record answers. */
4607a51f6dfSlukem 	list = malloc((ancount + 1) * sizeof(char *));
4617a51f6dfSlukem 	if (!list) {
4627a51f6dfSlukem 		errno = ENOMEM;
4637a51f6dfSlukem 		return NULL;
4647a51f6dfSlukem 	}
4657a51f6dfSlukem 	/* Parse the answers. */
4667a51f6dfSlukem 	j = 0;
4677a51f6dfSlukem 	for (i = 0; i < ancount; i++) {
4687a51f6dfSlukem 		/* Parse the header of this answer. */
4697a51f6dfSlukem 		skip = dn_skipname(p, eom);
4707a51f6dfSlukem 		if (skip < 0 || p + skip + 10 > eom)
4717a51f6dfSlukem 			break;
4727a51f6dfSlukem 		type = p[skip + 0] << 8 | p[skip + 1];
4737a51f6dfSlukem 		class = p[skip + 2] << 8 | p[skip + 3];
4747a51f6dfSlukem 		len = p[skip + 8] << 8 | p[skip + 9];
4757a51f6dfSlukem 		p += skip + 10;
4767a51f6dfSlukem 		if (p + len > eom) {
4777a51f6dfSlukem 			errno = EMSGSIZE;
4787a51f6dfSlukem 			break;
4797a51f6dfSlukem 		}
4807a51f6dfSlukem 		/* Skip entries of the wrong class and type. */
4817a51f6dfSlukem 		if (class != qclass || type != T_TXT) {
4827a51f6dfSlukem 			p += len;
4837a51f6dfSlukem 			continue;
4847a51f6dfSlukem 		}
4857a51f6dfSlukem 		/* Allocate space for this answer. */
4867a51f6dfSlukem 		list[j] = malloc((size_t)len);
4877a51f6dfSlukem 		if (!list[j]) {
4887a51f6dfSlukem 			errno = ENOMEM;
4897a51f6dfSlukem 			break;
4907a51f6dfSlukem 		}
4917a51f6dfSlukem 		dst = list[j++];
4927a51f6dfSlukem 
4937a51f6dfSlukem 		/* Copy answer data into the allocated area. */
4947a51f6dfSlukem 		eor = p + len;
4957a51f6dfSlukem 		while (p < eor) {
4967a51f6dfSlukem 			n = (unsigned char) *p++;
4977a51f6dfSlukem 			if (p + n > eor) {
4987a51f6dfSlukem 				errno = EMSGSIZE;
4997a51f6dfSlukem 				break;
5007a51f6dfSlukem 			}
5017a51f6dfSlukem 			memcpy(dst, p, (size_t)n);
5027a51f6dfSlukem 			p += n;
5037a51f6dfSlukem 			dst += n;
5047a51f6dfSlukem 		}
5057a51f6dfSlukem 		if (p < eor) {
5067a51f6dfSlukem 			errno = EMSGSIZE;
5077a51f6dfSlukem 			break;
5087a51f6dfSlukem 		}
5097a51f6dfSlukem 		*dst = 0;
5107a51f6dfSlukem 	}
5117a51f6dfSlukem 
5127a51f6dfSlukem 	/*
5137a51f6dfSlukem 	 * If we didn't terminate the loop normally, something
5147a51f6dfSlukem 	 * went wrong.
5157a51f6dfSlukem 	 */
5167a51f6dfSlukem 	if (i < ancount) {
5177a51f6dfSlukem 		for (i = 0; i < j; i++)
5187a51f6dfSlukem 			free(list[i]);
5197a51f6dfSlukem 		free(list);
5207a51f6dfSlukem 		return NULL;
5217a51f6dfSlukem 	}
5227a51f6dfSlukem 	if (j == 0) {
5237a51f6dfSlukem 		errno = ENOENT;
5247a51f6dfSlukem 		free(list);
5257a51f6dfSlukem 		return NULL;
5267a51f6dfSlukem 	}
5277a51f6dfSlukem 	list[j] = NULL;
5287a51f6dfSlukem 	return list;
5297a51f6dfSlukem }
5307a51f6dfSlukem 
5317a51f6dfSlukem /*
5327a51f6dfSlukem  * COMPATIBILITY FUNCTIONS
5337a51f6dfSlukem  */
5347a51f6dfSlukem 
5357a51f6dfSlukem static int	  inited = 0;
5367a51f6dfSlukem static void	 *context;
5377a51f6dfSlukem static int	  errval = HES_ER_UNINIT;
538de3b78d7Slukem 
539de3b78d7Slukem int
hes_init(void)540504f8671Smatt hes_init(void)
541de3b78d7Slukem {
5427a51f6dfSlukem 	init_context();
5437a51f6dfSlukem 	return errval;
544de3b78d7Slukem }
545de3b78d7Slukem 
546de3b78d7Slukem char *
hes_to_bind(const char * name,const char * type)547504f8671Smatt hes_to_bind(const char *name, const char *type)
548de3b78d7Slukem {
5497a51f6dfSlukem 	static	char	*bindname;
550b48252f3Slukem 
551b48252f3Slukem 	_DIAGASSERT(name != NULL);
552b48252f3Slukem 	_DIAGASSERT(type != NULL);
553b48252f3Slukem 
5547a51f6dfSlukem 	if (init_context() < 0)
555de3b78d7Slukem 		return NULL;
556*4644559eSmaya 
5577a51f6dfSlukem 	bindname = hesiod_to_bind(context, name, type);
5587a51f6dfSlukem 	if (!bindname)
5597a51f6dfSlukem 		translate_errors();
5607a51f6dfSlukem 	return bindname;
561de3b78d7Slukem }
5627a51f6dfSlukem 
5637a51f6dfSlukem char **
hes_resolve(const char * name,const char * type)564504f8671Smatt hes_resolve(const char *name, const char *type)
5657a51f6dfSlukem {
5667a51f6dfSlukem 	static char	**list;
5677a51f6dfSlukem 
568b48252f3Slukem 	_DIAGASSERT(name != NULL);
569b48252f3Slukem 	_DIAGASSERT(type != NULL);
570b48252f3Slukem 
5717a51f6dfSlukem 	if (init_context() < 0)
5727a51f6dfSlukem 		return NULL;
5737a51f6dfSlukem 
5747a51f6dfSlukem 	/*
5757a51f6dfSlukem 	 * In the old Hesiod interface, the caller was responsible for
5767a51f6dfSlukem 	 * freeing the returned strings but not the vector of strings itself.
5777a51f6dfSlukem 	 */
5787a51f6dfSlukem 	if (list)
5797a51f6dfSlukem 		free(list);
5807a51f6dfSlukem 
5817a51f6dfSlukem 	list = hesiod_resolve(context, name, type);
5827a51f6dfSlukem 	if (!list)
5837a51f6dfSlukem 		translate_errors();
5847a51f6dfSlukem 	return list;
585de3b78d7Slukem }
586de3b78d7Slukem 
587de3b78d7Slukem int
hes_error(void)588504f8671Smatt hes_error(void)
589de3b78d7Slukem {
5907a51f6dfSlukem 	return errval;
591de3b78d7Slukem }
592de3b78d7Slukem 
593de3b78d7Slukem void
hes_free(char ** hp)594504f8671Smatt hes_free(char **hp)
595de3b78d7Slukem {
5963ca17e24Slukem 	hesiod_free_list(context, hp);
597de3b78d7Slukem }
5987a51f6dfSlukem 
5997a51f6dfSlukem static int
init_context(void)600504f8671Smatt init_context(void)
6017a51f6dfSlukem {
6027a51f6dfSlukem 	if (!inited) {
6037a51f6dfSlukem 		inited = 1;
6047a51f6dfSlukem 		if (hesiod_init(&context) < 0) {
6057a51f6dfSlukem 			errval = HES_ER_CONFIG;
6067a51f6dfSlukem 			return -1;
6077a51f6dfSlukem 		}
6087a51f6dfSlukem 		errval = HES_ER_OK;
6097a51f6dfSlukem 	}
6107a51f6dfSlukem 	return 0;
6117a51f6dfSlukem }
6127a51f6dfSlukem 
6137a51f6dfSlukem static void
translate_errors(void)614504f8671Smatt translate_errors(void)
6157a51f6dfSlukem {
6167a51f6dfSlukem 	switch (errno) {
6177a51f6dfSlukem 	case ENOENT:
6187a51f6dfSlukem 		errval = HES_ER_NOTFOUND;
6197a51f6dfSlukem 		break;
6207a51f6dfSlukem 	case ECONNREFUSED:
6217a51f6dfSlukem 	case EMSGSIZE:
6227a51f6dfSlukem 		errval = HES_ER_NET;
6237a51f6dfSlukem 		break;
6247a51f6dfSlukem 	default:
6257a51f6dfSlukem 		/* Not a good match, but the best we can do. */
6267a51f6dfSlukem 		errval = HES_ER_CONFIG;
6277a51f6dfSlukem 		break;
6287a51f6dfSlukem 	}
6297a51f6dfSlukem }
630