165532Snectar/*	$NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $	*/
265532Snectar
365532Snectar/* Copyright (c) 1996 by Internet Software Consortium.
465532Snectar *
565532Snectar * Permission to use, copy, modify, and distribute this software for any
665532Snectar * purpose with or without fee is hereby granted, provided that the above
765532Snectar * copyright notice and this permission notice appear in all copies.
865532Snectar *
965532Snectar * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
1065532Snectar * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
1165532Snectar * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
1265532Snectar * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
1365532Snectar * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
1465532Snectar * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
1565532Snectar * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
1665532Snectar * SOFTWARE.
1765532Snectar */
1865532Snectar
1965532Snectar/* Copyright 1996 by the Massachusetts Institute of Technology.
2065532Snectar *
2165532Snectar * Permission to use, copy, modify, and distribute this
2265532Snectar * software and its documentation for any purpose and without
2365532Snectar * fee is hereby granted, provided that the above copyright
2465532Snectar * notice appear in all copies and that both that copyright
2565532Snectar * notice and this permission notice appear in supporting
2665532Snectar * documentation, and that the name of M.I.T. not be used in
2765532Snectar * advertising or publicity pertaining to distribution of the
2865532Snectar * software without specific, written prior permission.
2965532Snectar * M.I.T. makes no representations about the suitability of
3065532Snectar * this software for any purpose.  It is provided "as is"
3165532Snectar * without express or implied warranty.
3265532Snectar */
3365532Snectar
3465532Snectar/* This file is part of the hesiod library.  It implements the core
3565532Snectar * portion of the hesiod resolver.
3665532Snectar *
3765532Snectar * This file is loosely based on an interim version of hesiod.c from
3865532Snectar * the BIND IRS library, which was in turn based on an earlier version
3965532Snectar * of this file.  Extensive changes have been made on each step of the
4065532Snectar * path.
4165532Snectar *
4265532Snectar * This implementation is not truly thread-safe at the moment because
4365532Snectar * it uses res_send() and accesses _res.
4465532Snectar */
4565532Snectar
4665532Snectar#include <sys/cdefs.h>
4765532Snectar
4892986Sobrien#if 0
4965532Snectarstatic char *orig_rcsid = "$NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $";
5092986Sobrien#endif
5192986Sobrien#include <sys/cdefs.h>
5292986Sobrien__FBSDID("$FreeBSD$");
5365532Snectar
5465532Snectar#include <sys/types.h>
5565532Snectar#include <sys/param.h>
5665532Snectar#include <netinet/in.h>
5765532Snectar#include <arpa/nameser.h>
5865532Snectar
5965532Snectar#include <ctype.h>
6065532Snectar#include <errno.h>
6165532Snectar#include <hesiod.h>
6265532Snectar#include <resolv.h>
6365532Snectar#include <stdio.h>
6465532Snectar#include <stdlib.h>
6565532Snectar#include <string.h>
6666451Snectar#include <unistd.h>
6765532Snectar
6865532Snectarstruct hesiod_p {
6965532Snectar	char	*lhs;			/* normally ".ns" */
7065532Snectar	char	*rhs;			/* AKA the default hesiod domain */
7165532Snectar	int	 classes[2];		/* The class search order. */
7265532Snectar};
7365532Snectar
7465532Snectar#define	MAX_HESRESP	1024
7565532Snectar
7692905Sobrienstatic int	  read_config_file(struct hesiod_p *, const char *);
7792905Sobrienstatic char	**get_txt_records(int, const char *);
7892905Sobrienstatic int	  init_context(void);
7992905Sobrienstatic void	  translate_errors(void);
8065532Snectar
8165532Snectar
8265532Snectar/*
8365532Snectar * hesiod_init --
8465532Snectar *	initialize a hesiod_p.
8565532Snectar */
8665532Snectarint
8765532Snectarhesiod_init(context)
8865532Snectar	void	**context;
8965532Snectar{
9065532Snectar	struct hesiod_p	*ctx;
9165532Snectar	const char	*p, *configname;
9265532Snectar
9365532Snectar	ctx = malloc(sizeof(struct hesiod_p));
9465532Snectar	if (ctx) {
9565532Snectar		*context = ctx;
9666485Snectar		if (!issetugid())
9766451Snectar			configname = getenv("HESIOD_CONFIG");
9866451Snectar		else
9966451Snectar			configname = NULL;
10065532Snectar		if (!configname)
10165532Snectar			configname = _PATH_HESIOD_CONF;
10265532Snectar		if (read_config_file(ctx, configname) >= 0) {
10365532Snectar			/*
10465532Snectar			 * The default rhs can be overridden by an
10565532Snectar			 * environment variable.
10665532Snectar			 */
10766485Snectar			if (!issetugid())
10866451Snectar				p = getenv("HES_DOMAIN");
10966451Snectar			else
11066451Snectar				p = NULL;
11165532Snectar			if (p) {
11265532Snectar				if (ctx->rhs)
11365532Snectar					free(ctx->rhs);
11465532Snectar				ctx->rhs = malloc(strlen(p) + 2);
11565532Snectar				if (ctx->rhs) {
11665532Snectar					*ctx->rhs = '.';
11765532Snectar					strcpy(ctx->rhs + 1,
11865532Snectar					    (*p == '.') ? p + 1 : p);
11965532Snectar					return 0;
12065532Snectar				} else
12165532Snectar					errno = ENOMEM;
12265532Snectar			} else
12365532Snectar				return 0;
12465532Snectar		}
12565532Snectar	} else
12665532Snectar		errno = ENOMEM;
12765532Snectar
12865532Snectar	if (ctx->lhs)
12965532Snectar		free(ctx->lhs);
13065532Snectar	if (ctx->rhs)
13165532Snectar		free(ctx->rhs);
13265532Snectar	if (ctx)
13365532Snectar		free(ctx);
13465532Snectar	return -1;
13565532Snectar}
13665532Snectar
13765532Snectar/*
13865532Snectar * hesiod_end --
13965532Snectar *	Deallocates the hesiod_p.
14065532Snectar */
14165532Snectarvoid
14265532Snectarhesiod_end(context)
14365532Snectar	void	*context;
14465532Snectar{
14565532Snectar	struct hesiod_p *ctx = (struct hesiod_p *) context;
14665532Snectar
14765532Snectar	free(ctx->rhs);
14865532Snectar	if (ctx->lhs)
14965532Snectar		free(ctx->lhs);
15065532Snectar	free(ctx);
15165532Snectar}
15265532Snectar
15365532Snectar/*
15465532Snectar * hesiod_to_bind --
15565532Snectar * 	takes a hesiod (name, type) and returns a DNS
15665532Snectar *	name which is to be resolved.
15765532Snectar */
15865532Snectarchar *
15965532Snectarhesiod_to_bind(void *context, const char *name, const char *type)
16065532Snectar{
16165532Snectar	struct hesiod_p *ctx = (struct hesiod_p *) context;
16265532Snectar	char		 bindname[MAXDNAME], *p, *ret, **rhs_list = NULL;
16365532Snectar	const char	*rhs;
16465532Snectar	int		 len;
16565532Snectar
166114443Snectar	if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) {
16771929Snectar		errno = EMSGSIZE;
16871929Snectar		return NULL;
16971929Snectar	}
17065532Snectar
17165532Snectar		/*
17265532Snectar		 * Find the right right hand side to use, possibly
17365532Snectar		 * truncating bindname.
17465532Snectar		 */
17565532Snectar	p = strchr(bindname, '@');
17665532Snectar	if (p) {
17765532Snectar		*p++ = 0;
17865532Snectar		if (strchr(p, '.'))
17965532Snectar			rhs = name + (p - bindname);
18065532Snectar		else {
18165532Snectar			rhs_list = hesiod_resolve(context, p, "rhs-extension");
18265532Snectar			if (rhs_list)
18365532Snectar				rhs = *rhs_list;
18465532Snectar			else {
18565532Snectar				errno = ENOENT;
18665532Snectar				return NULL;
18765532Snectar			}
18865532Snectar		}
18965532Snectar	} else
19065532Snectar		rhs = ctx->rhs;
19165532Snectar
19265532Snectar		/* See if we have enough room. */
19365532Snectar	len = strlen(bindname) + 1 + strlen(type);
19465532Snectar	if (ctx->lhs)
19565532Snectar		len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
19665532Snectar	len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0);
19765532Snectar	if (len > sizeof(bindname) - 1) {
19865532Snectar		if (rhs_list)
19965532Snectar			hesiod_free_list(context, rhs_list);
20065532Snectar		errno = EMSGSIZE;
20165532Snectar		return NULL;
20265532Snectar	}
20365532Snectar		/* Put together the rest of the domain. */
20465532Snectar	strcat(bindname, ".");
20565532Snectar	strcat(bindname, type);
20665532Snectar		/* Only append lhs if it isn't empty. */
20765532Snectar	if (ctx->lhs && ctx->lhs[0] != '\0' ) {
20865532Snectar		if (ctx->lhs[0] != '.')
20965532Snectar			strcat(bindname, ".");
21065532Snectar		strcat(bindname, ctx->lhs);
21165532Snectar	}
21265532Snectar	if (rhs[0] != '.')
21365532Snectar		strcat(bindname, ".");
21465532Snectar	strcat(bindname, rhs);
21565532Snectar
21665532Snectar		/* rhs_list is no longer needed, since we're done with rhs. */
21765532Snectar	if (rhs_list)
21865532Snectar		hesiod_free_list(context, rhs_list);
21965532Snectar
22065532Snectar		/* Make a copy of the result and return it to the caller. */
22165532Snectar	ret = strdup(bindname);
22265532Snectar	if (!ret)
22365532Snectar		errno = ENOMEM;
22465532Snectar	return ret;
22565532Snectar}
22665532Snectar
22765532Snectar/*
22865532Snectar * hesiod_resolve --
22965532Snectar *	Given a hesiod name and type, return an array of strings returned
23065532Snectar *	by the resolver.
23165532Snectar */
23265532Snectarchar **
23365532Snectarhesiod_resolve(context, name, type)
23465532Snectar	void		*context;
23565532Snectar	const char	*name;
23665532Snectar	const char	*type;
23765532Snectar{
23865532Snectar	struct hesiod_p	*ctx = (struct hesiod_p *) context;
23965532Snectar	char		*bindname, **retvec;
24065532Snectar
24165532Snectar	bindname = hesiod_to_bind(context, name, type);
24265532Snectar	if (!bindname)
24365532Snectar		return NULL;
24465532Snectar
24565532Snectar	retvec = get_txt_records(ctx->classes[0], bindname);
24665532Snectar	if (retvec == NULL && errno == ENOENT && ctx->classes[1])
24765532Snectar		retvec = get_txt_records(ctx->classes[1], bindname);
24865532Snectar
24965532Snectar	free(bindname);
25065532Snectar	return retvec;
25165532Snectar}
25265532Snectar
25365532Snectar/*ARGSUSED*/
25465532Snectarvoid
25565532Snectarhesiod_free_list(context, list)
25665532Snectar	void	 *context;
25765532Snectar	char	**list;
25865532Snectar{
25965532Snectar	char  **p;
26065532Snectar
26165532Snectar	if (list == NULL)
26265532Snectar		return;
26365532Snectar	for (p = list; *p; p++)
26465532Snectar		free(*p);
26565532Snectar	free(list);
26665532Snectar}
26765532Snectar
26865532Snectar
26965532Snectar/* read_config_file --
27065532Snectar *	Parse the /etc/hesiod.conf file.  Returns 0 on success,
27165532Snectar *	-1 on failure.  On failure, it might leave values in ctx->lhs
27265532Snectar *	or ctx->rhs which need to be freed by the caller.
27365532Snectar */
27465532Snectarstatic int
27565532Snectarread_config_file(ctx, filename)
27665532Snectar	struct hesiod_p	*ctx;
27765532Snectar	const char	*filename;
27865532Snectar{
27965532Snectar	char	*key, *data, *p, **which;
28065532Snectar	char	 buf[MAXDNAME + 7];
28165532Snectar	int	 n;
28265532Snectar	FILE	*fp;
28365532Snectar
28465532Snectar		/* Set default query classes. */
28565532Snectar	ctx->classes[0] = C_IN;
28665532Snectar	ctx->classes[1] = C_HS;
28765532Snectar
28865532Snectar		/* Try to open the configuration file. */
289254700Sjilles	fp = fopen(filename, "re");
29065532Snectar	if (!fp) {
29165532Snectar		/* Use compiled in default domain names. */
29265532Snectar		ctx->lhs = strdup(DEF_LHS);
29365532Snectar		ctx->rhs = strdup(DEF_RHS);
29465532Snectar		if (ctx->lhs && ctx->rhs)
29565532Snectar			return 0;
29665532Snectar		else {
29765532Snectar			errno = ENOMEM;
29865532Snectar			return -1;
29965532Snectar		}
30065532Snectar	}
30165532Snectar	ctx->lhs = NULL;
30265532Snectar	ctx->rhs = NULL;
30365532Snectar	while (fgets(buf, sizeof(buf), fp) != NULL) {
30465532Snectar		p = buf;
30565532Snectar		if (*p == '#' || *p == '\n' || *p == '\r')
30665532Snectar			continue;
30765532Snectar		while (*p == ' ' || *p == '\t')
30865532Snectar			p++;
30965532Snectar		key = p;
31065532Snectar		while (*p != ' ' && *p != '\t' && *p != '=')
31165532Snectar			p++;
31265532Snectar		*p++ = 0;
31365532Snectar
31465532Snectar		while (isspace(*p) || *p == '=')
31565532Snectar			p++;
31665532Snectar		data = p;
31765532Snectar		while (!isspace(*p))
31865532Snectar			p++;
31965532Snectar		*p = 0;
32065532Snectar
32165532Snectar		if (strcasecmp(key, "lhs") == 0 ||
32265532Snectar		    strcasecmp(key, "rhs") == 0) {
32365532Snectar			which = (strcasecmp(key, "lhs") == 0)
32465532Snectar			    ? &ctx->lhs : &ctx->rhs;
32565532Snectar			*which = strdup(data);
32665532Snectar			if (!*which) {
327217143Skib				fclose(fp);
32865532Snectar				errno = ENOMEM;
32965532Snectar				return -1;
33065532Snectar			}
33165532Snectar		} else {
33265532Snectar			if (strcasecmp(key, "classes") == 0) {
33365532Snectar				n = 0;
33465532Snectar				while (*data && n < 2) {
33565532Snectar					p = data;
33665532Snectar					while (*p && *p != ',')
33765532Snectar						p++;
33865532Snectar					if (*p)
33965532Snectar						*p++ = 0;
34065532Snectar					if (strcasecmp(data, "IN") == 0)
34165532Snectar						ctx->classes[n++] = C_IN;
34265532Snectar					else
34365532Snectar						if (strcasecmp(data, "HS") == 0)
34465532Snectar							ctx->classes[n++] =
34565532Snectar							    C_HS;
34665532Snectar					data = p;
34765532Snectar				}
34865532Snectar				while (n < 2)
34965532Snectar					ctx->classes[n++] = 0;
35065532Snectar			}
35165532Snectar		}
35265532Snectar	}
35365532Snectar	fclose(fp);
35465532Snectar
35565532Snectar	if (!ctx->rhs || ctx->classes[0] == 0 ||
35665532Snectar	    ctx->classes[0] == ctx->classes[1]) {
35765532Snectar		errno = ENOEXEC;
35865532Snectar		return -1;
35965532Snectar	}
36065532Snectar	return 0;
36165532Snectar}
36265532Snectar
36365532Snectar/*
36465532Snectar * get_txt_records --
36565532Snectar *	Given a DNS class and a DNS name, do a lookup for TXT records, and
36665532Snectar *	return a list of them.
36765532Snectar */
36865532Snectarstatic char **
36965532Snectarget_txt_records(qclass, name)
37065532Snectar	int		 qclass;
37165532Snectar	const char	*name;
37265532Snectar{
37365532Snectar	HEADER		*hp;
37465532Snectar	unsigned char	 qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
37565532Snectar	char		*dst, **list;
37665532Snectar	int		 ancount, qdcount, i, j, n, skip, type, class, len;
37765532Snectar
37865532Snectar		/* Make sure the resolver is initialized. */
37965532Snectar	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
38065532Snectar		return NULL;
38165532Snectar
38265532Snectar		/* Construct the query. */
38365532Snectar	n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0,
38465532Snectar	    NULL, qbuf, PACKETSZ);
38565532Snectar	if (n < 0)
38665532Snectar		return NULL;
38765532Snectar
38865532Snectar		/* Send the query. */
38965532Snectar	n = res_send(qbuf, n, abuf, MAX_HESRESP);
390103350Snectar	if (n < 0 || n > MAX_HESRESP) {
391103350Snectar		errno = ECONNREFUSED; /* XXX */
39265532Snectar		return NULL;
39365532Snectar	}
39465532Snectar		/* Parse the header of the result. */
39565532Snectar	hp = (HEADER *) (void *) abuf;
39665532Snectar	ancount = ntohs(hp->ancount);
39765532Snectar	qdcount = ntohs(hp->qdcount);
39865532Snectar	p = abuf + sizeof(HEADER);
39965532Snectar	eom = abuf + n;
40065532Snectar
40165532Snectar		/*
40265532Snectar		 * Skip questions, trying to get to the answer section
40365532Snectar		 * which follows.
40465532Snectar		 */
40565532Snectar	for (i = 0; i < qdcount; i++) {
40665532Snectar		skip = dn_skipname(p, eom);
40765532Snectar		if (skip < 0 || p + skip + QFIXEDSZ > eom) {
40865532Snectar			errno = EMSGSIZE;
40965532Snectar			return NULL;
41065532Snectar		}
41165532Snectar		p += skip + QFIXEDSZ;
41265532Snectar	}
41365532Snectar
41465532Snectar		/* Allocate space for the text record answers. */
41565532Snectar	list = malloc((ancount + 1) * sizeof(char *));
41665532Snectar	if (!list) {
41765532Snectar		errno = ENOMEM;
41865532Snectar		return NULL;
41965532Snectar	}
42065532Snectar		/* Parse the answers. */
42165532Snectar	j = 0;
42265532Snectar	for (i = 0; i < ancount; i++) {
42365532Snectar		/* Parse the header of this answer. */
42465532Snectar		skip = dn_skipname(p, eom);
42565532Snectar		if (skip < 0 || p + skip + 10 > eom)
42665532Snectar			break;
42765532Snectar		type = p[skip + 0] << 8 | p[skip + 1];
42865532Snectar		class = p[skip + 2] << 8 | p[skip + 3];
42965532Snectar		len = p[skip + 8] << 8 | p[skip + 9];
43065532Snectar		p += skip + 10;
43165532Snectar		if (p + len > eom) {
43265532Snectar			errno = EMSGSIZE;
43365532Snectar			break;
43465532Snectar		}
43565532Snectar		/* Skip entries of the wrong class and type. */
43665532Snectar		if (class != qclass || type != T_TXT) {
43765532Snectar			p += len;
43865532Snectar			continue;
43965532Snectar		}
44065532Snectar		/* Allocate space for this answer. */
44165532Snectar		list[j] = malloc((size_t)len);
44265532Snectar		if (!list[j]) {
44365532Snectar			errno = ENOMEM;
44465532Snectar			break;
44565532Snectar		}
44665532Snectar		dst = list[j++];
44765532Snectar
44865532Snectar		/* Copy answer data into the allocated area. */
44965532Snectar		eor = p + len;
45065532Snectar		while (p < eor) {
45165532Snectar			n = (unsigned char) *p++;
45265532Snectar			if (p + n > eor) {
45365532Snectar				errno = EMSGSIZE;
45465532Snectar				break;
45565532Snectar			}
45665532Snectar			memcpy(dst, p, (size_t)n);
45765532Snectar			p += n;
45865532Snectar			dst += n;
45965532Snectar		}
46065532Snectar		if (p < eor) {
46165532Snectar			errno = EMSGSIZE;
46265532Snectar			break;
46365532Snectar		}
46465532Snectar		*dst = 0;
46565532Snectar	}
46665532Snectar
46765532Snectar		/*
46865532Snectar		 * If we didn't terminate the loop normally, something
46965532Snectar		 * went wrong.
47065532Snectar		 */
47165532Snectar	if (i < ancount) {
47265532Snectar		for (i = 0; i < j; i++)
47365532Snectar			free(list[i]);
47465532Snectar		free(list);
47565532Snectar		return NULL;
47665532Snectar	}
47765532Snectar	if (j == 0) {
47865532Snectar		errno = ENOENT;
47965532Snectar		free(list);
48065532Snectar		return NULL;
48165532Snectar	}
48265532Snectar	list[j] = NULL;
48365532Snectar	return list;
48465532Snectar}
48565532Snectar
48665532Snectar		/*
48765532Snectar		 *	COMPATIBILITY FUNCTIONS
48865532Snectar		 */
48965532Snectar
49065532Snectarstatic int	  inited = 0;
49165532Snectarstatic void	 *context;
49265532Snectarstatic int	  errval = HES_ER_UNINIT;
49365532Snectar
49465532Snectarint
49565532Snectarhes_init()
49665532Snectar{
49765532Snectar	init_context();
49865532Snectar	return errval;
49965532Snectar}
50065532Snectar
50165532Snectarchar *
50265532Snectarhes_to_bind(name, type)
50365532Snectar	const char	*name;
50465532Snectar	const char	*type;
50565532Snectar{
50665532Snectar	static	char	*bindname;
50765532Snectar	if (init_context() < 0)
50865532Snectar		return NULL;
50965532Snectar	if (bindname)
51065532Snectar		free(bindname);
51165532Snectar	bindname = hesiod_to_bind(context, name, type);
51265532Snectar	if (!bindname)
51365532Snectar		translate_errors();
51465532Snectar	return bindname;
51565532Snectar}
51665532Snectar
51765532Snectarchar **
51865532Snectarhes_resolve(name, type)
51965532Snectar	const char	*name;
52065532Snectar	const char	*type;
52165532Snectar{
52265532Snectar	static char	**list;
52365532Snectar
52465532Snectar	if (init_context() < 0)
52565532Snectar		return NULL;
52665532Snectar
52765532Snectar	/*
52865532Snectar	 * In the old Hesiod interface, the caller was responsible for
52965532Snectar	 * freeing the returned strings but not the vector of strings itself.
53065532Snectar	 */
53165532Snectar	if (list)
53265532Snectar		free(list);
53365532Snectar
53465532Snectar	list = hesiod_resolve(context, name, type);
53565532Snectar	if (!list)
53665532Snectar		translate_errors();
53765532Snectar	return list;
53865532Snectar}
53965532Snectar
54065532Snectarint
54165532Snectarhes_error()
54265532Snectar{
54365532Snectar	return errval;
54465532Snectar}
54565532Snectar
54665532Snectarvoid
54765532Snectarhes_free(hp)
54865532Snectar	char **hp;
54965532Snectar{
55065532Snectar	hesiod_free_list(context, hp);
55165532Snectar}
55265532Snectar
55365532Snectarstatic int
55465532Snectarinit_context()
55565532Snectar{
55665532Snectar	if (!inited) {
55765532Snectar		inited = 1;
55865532Snectar		if (hesiod_init(&context) < 0) {
55965532Snectar			errval = HES_ER_CONFIG;
56065532Snectar			return -1;
56165532Snectar		}
56265532Snectar		errval = HES_ER_OK;
56365532Snectar	}
56465532Snectar	return 0;
56565532Snectar}
56665532Snectar
56765532Snectarstatic void
56865532Snectartranslate_errors()
56965532Snectar{
57065532Snectar	switch (errno) {
57165532Snectar	case ENOENT:
57265532Snectar		errval = HES_ER_NOTFOUND;
57365532Snectar		break;
57465532Snectar	case ECONNREFUSED:
57565532Snectar	case EMSGSIZE:
57665532Snectar		errval = HES_ER_NET;
57765532Snectar		break;
57865532Snectar	case ENOMEM:
57965532Snectar	default:
58065532Snectar		/* Not a good match, but the best we can do. */
58165532Snectar		errval = HES_ER_CONFIG;
58265532Snectar		break;
58365532Snectar	}
58465532Snectar}
585