2fe8fb192f
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
649 lines
14 KiB
C
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;
|
|
}
|
|
}
|