minix/lib/libc/net/hesiod.c
Ben Gras 2fe8fb192f Full switch to clang/ELF. Drop ack. Simplify.
There is important information about booting non-ack images in
docs/UPDATING. ack/aout-format images can't be built any more, and
booting clang/ELF-format ones is a little different. Updating to the
new boot monitor is recommended.

Changes in this commit:

	. drop boot monitor -> allowing dropping ack support
	. facility to copy ELF boot files to /boot so that old boot monitor
	  can still boot fairly easily, see UPDATING
	. no more ack-format libraries -> single-case libraries
	. some cleanup of OBJECT_FMT, COMPILER_TYPE, etc cases
	. drop several ack toolchain commands, but not all support
	  commands (e.g. aal is gone but acksize is not yet).
	. a few libc files moved to netbsd libc dir
	. new /bin/date as minix date used code in libc/
	. test compile fix
	. harmonize includes
	. /usr/lib is no longer special: without ack, /usr/lib plays no
	  kind of special bootstrapping role any more and bootstrapping
	  is done exclusively through packages, so releases depend even
	  less on the state of the machine making them now.
	. rename nbsd_lib* to lib*
	. reduce mtree
2012-02-14 14:52:02 +01:00

649 lines
14 KiB
C

/* $NetBSD: hesiod.c,v 1.25 2011/01/05 00:09:43 wiz Exp $ */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/* Copyright 1996 by the Massachusetts Institute of Technology.
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
/* This file is part of the hesiod library. It implements the core
* portion of the hesiod resolver.
*
* This file is loosely based on an interim version of hesiod.c from
* the BIND IRS library, which was in turn based on an earlier version
* of this file. Extensive changes have been made on each step of the
* path.
*
* This implementation is thread-safe because it uses res_nsend().
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__IDSTRING(rcsid_hesiod_c,
"#Id: hesiod.c,v 1.18.2.1 1997/01/03 20:48:20 ghudson Exp #");
__IDSTRING(rcsid_hesiod_p_h,
"#Id: hesiod_p.h,v 1.1 1996/12/08 21:39:37 ghudson Exp #");
__IDSTRING(rcsid_hescompat_c,
"#Id: hescompat.c,v 1.1.2.1 1996/12/16 08:37:45 ghudson Exp #");
__RCSID("$NetBSD: hesiod.c,v 1.25 2011/01/05 00:09:43 wiz Exp $");
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <hesiod.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef __weak_alias
__weak_alias(hesiod_init,_hesiod_init)
__weak_alias(hesiod_end,_hesiod_end)
__weak_alias(hesiod_to_bind,_hesiod_to_bind)
__weak_alias(hesiod_resolve,_hesiod_resolve)
__weak_alias(hesiod_free_list,_hesiod_free_list)
__weak_alias(hes_init,_hes_init)
__weak_alias(hes_to_bind,_hes_to_bind)
__weak_alias(hes_resolve,_hes_resolve)
__weak_alias(hes_error,_hes_error)
__weak_alias(hes_free,_hes_free)
#endif
struct hesiod_p {
char *lhs; /* normally ".ns" */
char *rhs; /* AKA the default hesiod domain */
int classes[2]; /* The class search order. */
};
#define MAX_HESRESP 1024
static int read_config_file __P((struct hesiod_p *, const char *));
static char **get_txt_records __P((int, const char *));
static int init_context __P((void));
static void translate_errors __P((void));
/*
* hesiod_init --
* initialize a hesiod_p.
*/
int
hesiod_init(context)
void **context;
{
struct hesiod_p *ctx;
const char *p, *configname;
int serrno;
_DIAGASSERT(context != NULL);
ctx = calloc(1, sizeof(struct hesiod_p));
if (ctx) {
*context = ctx;
/*
* don't permit overrides from environment
* for set.id programs
*/
if (issetugid())
configname = NULL;
else
configname = getenv("HESIOD_CONFIG");
if (!configname)
configname = _PATH_HESIOD_CONF;
if (read_config_file(ctx, configname) >= 0) {
/*
* The default rhs can be overridden by an
* environment variable, unless set.id.
*/
if (issetugid())
p = NULL;
else
p = getenv("HES_DOMAIN");
if (p) {
if (ctx->rhs)
free(ctx->rhs);
ctx->rhs = malloc(strlen(p) + 2);
if (ctx->rhs) {
*ctx->rhs = '.';
strcpy(ctx->rhs + 1,
(*p == '.') ? p + 1 : p);
return 0;
} else
errno = ENOMEM;
} else
return 0;
}
} else
errno = ENOMEM;
serrno = errno;
if (ctx) {
if (ctx->lhs)
free(ctx->lhs);
if (ctx->rhs)
free(ctx->rhs);
free(ctx);
}
errno = serrno;
return -1;
}
/*
* hesiod_end --
* Deallocates the hesiod_p.
*/
void
hesiod_end(context)
void *context;
{
struct hesiod_p *ctx = (struct hesiod_p *) context;
_DIAGASSERT(context != NULL);
free(ctx->rhs);
if (ctx->lhs)
free(ctx->lhs);
free(ctx);
}
/*
* hesiod_to_bind --
* takes a hesiod (name, type) and returns a DNS
* name which is to be resolved.
*/
char *
hesiod_to_bind(void *context, const char *name, const char *type)
{
struct hesiod_p *ctx = (struct hesiod_p *) context;
char bindname[MAXDNAME], *p, *ret, **rhs_list = NULL;
const char *rhs;
size_t len;
_DIAGASSERT(context != NULL);
_DIAGASSERT(name != NULL);
_DIAGASSERT(type != NULL);
if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) {
errno = EMSGSIZE;
return NULL;
}
/*
* Find the right right hand side to use, possibly
* truncating bindname.
*/
p = strchr(bindname, '@');
if (p) {
*p++ = 0;
if (strchr(p, '.'))
rhs = name + (p - bindname);
else {
rhs_list = hesiod_resolve(context, p, "rhs-extension");
if (rhs_list)
rhs = *rhs_list;
else {
errno = ENOENT;
return NULL;
}
}
} else
rhs = ctx->rhs;
/* See if we have enough room. */
len = strlen(bindname) + 1 + strlen(type);
if (ctx->lhs)
len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0);
if (len > sizeof(bindname) - 1) {
if (rhs_list)
hesiod_free_list(context, rhs_list);
errno = EMSGSIZE;
return NULL;
}
/* Put together the rest of the domain. */
strlcat(bindname, ".", sizeof(bindname));
strlcat(bindname, type, sizeof(bindname));
/* Only append lhs if it isn't empty. */
if (ctx->lhs && ctx->lhs[0] != '\0' ) {
if (ctx->lhs[0] != '.')
strlcat(bindname, ".", sizeof(bindname));
strlcat(bindname, ctx->lhs, sizeof(bindname));
}
if (rhs[0] != '.')
strlcat(bindname, ".", sizeof(bindname));
strlcat(bindname, rhs, sizeof(bindname));
/* rhs_list is no longer needed, since we're done with rhs. */
if (rhs_list)
hesiod_free_list(context, rhs_list);
/* Make a copy of the result and return it to the caller. */
ret = strdup(bindname);
if (ret == NULL)
errno = ENOMEM;
return ret;
}
/*
* hesiod_resolve --
* Given a hesiod name and type, return an array of strings returned
* by the resolver.
*/
char **
hesiod_resolve(context, name, type)
void *context;
const char *name;
const char *type;
{
struct hesiod_p *ctx = (struct hesiod_p *) context;
char *bindname, **retvec;
_DIAGASSERT(context != NULL);
_DIAGASSERT(name != NULL);
_DIAGASSERT(type != NULL);
bindname = hesiod_to_bind(context, name, type);
if (!bindname)
return NULL;
retvec = get_txt_records(ctx->classes[0], bindname);
if (retvec == NULL && errno == ENOENT && ctx->classes[1])
retvec = get_txt_records(ctx->classes[1], bindname);
free(bindname);
return retvec;
}
/*ARGSUSED*/
void
hesiod_free_list(context, list)
void *context;
char **list;
{
char **p;
_DIAGASSERT(context != NULL);
if (list == NULL)
return;
for (p = list; *p; p++)
free(*p);
free(list);
}
/* read_config_file --
* Parse the /etc/hesiod.conf file. Returns 0 on success,
* -1 on failure. On failure, it might leave values in ctx->lhs
* or ctx->rhs which need to be freed by the caller.
*/
static int
read_config_file(ctx, filename)
struct hesiod_p *ctx;
const char *filename;
{
char *key, *data, *p, **which;
char buf[MAXDNAME + 7];
int n;
FILE *fp;
_DIAGASSERT(ctx != NULL);
_DIAGASSERT(filename != NULL);
/* Set default query classes. */
ctx->classes[0] = C_IN;
ctx->classes[1] = C_HS;
/* Try to open the configuration file. */
fp = fopen(filename, "r");
if (!fp) {
/* Use compiled in default domain names. */
ctx->lhs = strdup(DEF_LHS);
ctx->rhs = strdup(DEF_RHS);
if (ctx->lhs && ctx->rhs)
return 0;
else {
errno = ENOMEM;
return -1;
}
}
ctx->lhs = NULL;
ctx->rhs = NULL;
while (fgets(buf, sizeof(buf), fp) != NULL) {
p = buf;
if (*p == '#' || *p == '\n' || *p == '\r')
continue;
while (*p == ' ' || *p == '\t')
p++;
key = p;
while (*p != ' ' && *p != '\t' && *p != '=' && *p)
p++;
if (*p == '\0')
continue;
*p++ = 0;
while (isspace((u_char) *p) || *p == '=')
p++;
if (*p == '\0')
continue;
data = p;
while (!isspace((u_char) *p) && *p)
p++;
*p = 0;
if (strcasecmp(key, "lhs") == 0 ||
strcasecmp(key, "rhs") == 0) {
which = (strcasecmp(key, "lhs") == 0)
? &ctx->lhs : &ctx->rhs;
*which = strdup(data);
if (!*which) {
errno = ENOMEM;
(void)fclose(fp);
return -1;
}
} else {
if (strcasecmp(key, "classes") == 0) {
n = 0;
while (*data && n < 2) {
p = data;
while (*p && *p != ',')
p++;
if (*p)
*p++ = 0;
if (strcasecmp(data, "IN") == 0)
ctx->classes[n++] = C_IN;
else
if (strcasecmp(data, "HS") == 0)
ctx->classes[n++] =
C_HS;
data = p;
}
while (n < 2)
ctx->classes[n++] = 0;
}
}
}
fclose(fp);
if (!ctx->rhs || ctx->classes[0] == 0 ||
ctx->classes[0] == ctx->classes[1]) {
errno = ENOEXEC;
return -1;
}
return 0;
}
/*
* get_txt_records --
* Given a DNS class and a DNS name, do a lookup for TXT records, and
* return a list of them.
*/
static char **
get_txt_records(qclass, name)
int qclass;
const char *name;
{
HEADER *hp;
unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
char *dst, **list;
int ancount, qdcount, i, j, n, skip, type, class, len;
res_state res = __res_get_state();
if (res == NULL)
return NULL;
_DIAGASSERT(name != NULL);
/* Construct the query. */
n = res_nmkquery(res, QUERY, name, qclass, T_TXT, NULL, 0,
NULL, qbuf, PACKETSZ);
if (n < 0) {
errno = EMSGSIZE;
__res_put_state(res);
return NULL;
}
/* Send the query. */
n = res_nsend(res, qbuf, n, abuf, MAX_HESRESP);
__res_put_state(res);
if (n < 0) {
errno = ECONNREFUSED;
return NULL;
}
/* Parse the header of the result. */
hp = (HEADER *) (void *) abuf;
ancount = ntohs(hp->ancount);
qdcount = ntohs(hp->qdcount);
p = abuf + sizeof(HEADER);
eom = abuf + n;
/*
* Skip questions, trying to get to the answer section
* which follows.
*/
for (i = 0; i < qdcount; i++) {
skip = dn_skipname(p, eom);
if (skip < 0 || p + skip + QFIXEDSZ > eom) {
errno = EMSGSIZE;
return NULL;
}
p += skip + QFIXEDSZ;
}
/* Allocate space for the text record answers. */
list = malloc((ancount + 1) * sizeof(char *));
if (!list) {
errno = ENOMEM;
return NULL;
}
/* Parse the answers. */
j = 0;
for (i = 0; i < ancount; i++) {
/* Parse the header of this answer. */
skip = dn_skipname(p, eom);
if (skip < 0 || p + skip + 10 > eom)
break;
type = p[skip + 0] << 8 | p[skip + 1];
class = p[skip + 2] << 8 | p[skip + 3];
len = p[skip + 8] << 8 | p[skip + 9];
p += skip + 10;
if (p + len > eom) {
errno = EMSGSIZE;
break;
}
/* Skip entries of the wrong class and type. */
if (class != qclass || type != T_TXT) {
p += len;
continue;
}
/* Allocate space for this answer. */
list[j] = malloc((size_t)len);
if (!list[j]) {
errno = ENOMEM;
break;
}
dst = list[j++];
/* Copy answer data into the allocated area. */
eor = p + len;
while (p < eor) {
n = (unsigned char) *p++;
if (p + n > eor) {
errno = EMSGSIZE;
break;
}
memcpy(dst, p, (size_t)n);
p += n;
dst += n;
}
if (p < eor) {
errno = EMSGSIZE;
break;
}
*dst = 0;
}
/*
* If we didn't terminate the loop normally, something
* went wrong.
*/
if (i < ancount) {
for (i = 0; i < j; i++)
free(list[i]);
free(list);
return NULL;
}
if (j == 0) {
errno = ENOENT;
free(list);
return NULL;
}
list[j] = NULL;
return list;
}
/*
* COMPATIBILITY FUNCTIONS
*/
static int inited = 0;
static void *context;
static int errval = HES_ER_UNINIT;
int
hes_init()
{
init_context();
return errval;
}
char *
hes_to_bind(name, type)
const char *name;
const char *type;
{
static char *bindname;
_DIAGASSERT(name != NULL);
_DIAGASSERT(type != NULL);
if (init_context() < 0)
return NULL;
if (bindname)
free(bindname);
bindname = hesiod_to_bind(context, name, type);
if (!bindname)
translate_errors();
return bindname;
}
char **
hes_resolve(name, type)
const char *name;
const char *type;
{
static char **list;
_DIAGASSERT(name != NULL);
_DIAGASSERT(type != NULL);
if (init_context() < 0)
return NULL;
/*
* In the old Hesiod interface, the caller was responsible for
* freeing the returned strings but not the vector of strings itself.
*/
if (list)
free(list);
list = hesiod_resolve(context, name, type);
if (!list)
translate_errors();
return list;
}
int
hes_error()
{
return errval;
}
void
hes_free(hp)
char **hp;
{
hesiod_free_list(context, hp);
}
static int
init_context()
{
if (!inited) {
inited = 1;
if (hesiod_init(&context) < 0) {
errval = HES_ER_CONFIG;
return -1;
}
errval = HES_ER_OK;
}
return 0;
}
static void
translate_errors()
{
switch (errno) {
case ENOENT:
errval = HES_ER_NOTFOUND;
break;
case ECONNREFUSED:
case EMSGSIZE:
errval = HES_ER_NET;
break;
default:
/* Not a good match, but the best we can do. */
errval = HES_ER_CONFIG;
break;
}
}