minix/lib/libc/net/gethnamaddr.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

1441 lines
31 KiB
C

/* $NetBSD: gethnamaddr.c,v 1.76 2010/08/29 15:40:35 christos Exp $ */
/*
* ++Copyright++ 1985, 1988, 1993
* -
* Copyright (c) 1985, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* 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, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION 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--
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)gethostnamadr.c 8.1 (Berkeley) 6/4/93";
static char rcsid[] = "Id: gethnamaddr.c,v 8.21 1997/06/01 20:34:37 vixie Exp ";
#else
__RCSID("$NetBSD: gethnamaddr.c,v 1.76 2010/08/29 15:40:35 christos Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
#if defined(_LIBC)
#include "namespace.h"
#endif
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <stdarg.h>
#include <stdio.h>
#include <syslog.h>
#ifndef LOG_AUTH
# define LOG_AUTH 0
#endif
#define MULTI_PTRS_ARE_ALIASES 1 /* XXX - experimental */
#include <nsswitch.h>
#include <stdlib.h>
#include <string.h>
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#endif
#if defined(_LIBC) && defined(__weak_alias)
__weak_alias(gethostbyaddr,_gethostbyaddr)
__weak_alias(gethostbyname,_gethostbyname)
__weak_alias(gethostent,_gethostent)
#endif
#define MAXALIASES 35
#define MAXADDRS 35
static const char AskedForGot[] =
"gethostby*.getanswer: asked for \"%s\", got \"%s\"";
static char *h_addr_ptrs[MAXADDRS + 1];
#ifdef YP
static char *__ypdomain;
#endif
static struct hostent host;
static char *host_aliases[MAXALIASES];
static char hostbuf[8*1024];
static u_int32_t host_addr[16 / sizeof(u_int32_t)]; /* IPv4 or IPv6 */
static FILE *hostf = NULL;
static int stayopen = 0;
#define MAXPACKET (64*1024)
typedef union {
HEADER hdr;
u_char buf[MAXPACKET];
} querybuf;
typedef union {
int32_t al;
char ac;
} align;
#ifdef DEBUG
static void debugprintf(const char *, res_state, ...)
__attribute__((__format__(__printf__, 1, 3)));
#endif
static struct hostent *getanswer(const querybuf *, int, const char *, int,
res_state);
static void map_v4v6_address(const char *, char *);
static void map_v4v6_hostent(struct hostent *, char **, char *);
static void addrsort(char **, int, res_state);
void _sethtent(int);
void _endhtent(void);
struct hostent *_gethtent(void);
void ht_sethostent(int);
void ht_endhostent(void);
struct hostent *ht_gethostbyname(char *);
struct hostent *ht_gethostbyaddr(const char *, int, int);
void dns_service(void);
#undef dn_skipname
int dn_skipname(const u_char *, const u_char *);
int _gethtbyaddr(void *, void *, va_list);
int _gethtbyname(void *, void *, va_list);
struct hostent *_gethtbyname2(const char *, int);
int _dns_gethtbyaddr(void *, void *, va_list);
int _dns_gethtbyname(void *, void *, va_list);
#ifdef YP
struct hostent *_yphostent(char *, int);
int _yp_gethtbyaddr(void *, void *, va_list);
int _yp_gethtbyname(void *, void *, va_list);
#endif
static struct hostent *gethostbyname_internal(const char *, int, res_state);
static const ns_src default_dns_files[] = {
{ NSSRC_FILES, NS_SUCCESS },
{ NSSRC_DNS, NS_SUCCESS },
{ 0, 0 }
};
#ifdef DEBUG
static void
debugprintf(const char *msg, res_state res, ...)
{
_DIAGASSERT(msg != NULL);
if (res->options & RES_DEBUG) {
int save = errno;
va_list ap;
va_start (ap, res);
vprintf(msg, ap);
va_end (ap);
errno = save;
}
}
#else
# define debugprintf(msg, res, num) /*nada*/
#endif
#define BOUNDED_INCR(x) \
do { \
cp += (x); \
if (cp > eom) { \
h_errno = NO_RECOVERY; \
return NULL; \
} \
} while (/*CONSTCOND*/0)
#define BOUNDS_CHECK(ptr, count) \
do { \
if ((ptr) + (count) > eom) { \
h_errno = NO_RECOVERY; \
return NULL; \
} \
} while (/*CONSTCOND*/0)
static struct hostent *
getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
res_state res)
{
const HEADER *hp;
const u_char *cp;
int n;
const u_char *eom, *erdata;
char *bp, **ap, **hap, *ep;
int type, class, ancount, qdcount;
int haveanswer, had_error;
int toobig = 0;
char tbuf[MAXDNAME];
const char *tname;
int (*name_ok)(const char *);
_DIAGASSERT(answer != NULL);
_DIAGASSERT(qname != NULL);
tname = qname;
host.h_name = NULL;
eom = answer->buf + anslen;
switch (qtype) {
case T_A:
case T_AAAA:
name_ok = res_hnok;
break;
case T_PTR:
name_ok = res_dnok;
break;
default:
return NULL; /* XXX should be abort(); */
}
/*
* find first satisfactory answer
*/
hp = &answer->hdr;
ancount = ntohs(hp->ancount);
qdcount = ntohs(hp->qdcount);
bp = hostbuf;
ep = hostbuf + sizeof hostbuf;
cp = answer->buf;
BOUNDED_INCR(HFIXEDSZ);
if (qdcount != 1) {
h_errno = NO_RECOVERY;
return NULL;
}
n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
if ((n < 0) || !(*name_ok)(bp)) {
h_errno = NO_RECOVERY;
return NULL;
}
BOUNDED_INCR(n + QFIXEDSZ);
if (qtype == T_A || qtype == T_AAAA) {
/* res_send() has already verified that the query name is the
* same as the one we sent; this just gets the expanded name
* (i.e., with the succeeding search-domain tacked on).
*/
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
h_errno = NO_RECOVERY;
return NULL;
}
host.h_name = bp;
bp += n;
/* The qname can be abbreviated, but h_name is now absolute. */
qname = host.h_name;
}
ap = host_aliases;
*ap = NULL;
host.h_aliases = host_aliases;
hap = h_addr_ptrs;
*hap = NULL;
host.h_addr_list = h_addr_ptrs;
haveanswer = 0;
had_error = 0;
while (ancount-- > 0 && cp < eom && !had_error) {
n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
if ((n < 0) || !(*name_ok)(bp)) {
had_error++;
continue;
}
cp += n; /* name */
BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
type = _getshort(cp);
cp += INT16SZ; /* type */
class = _getshort(cp);
cp += INT16SZ + INT32SZ; /* class, TTL */
n = _getshort(cp);
cp += INT16SZ; /* len */
BOUNDS_CHECK(cp, n);
erdata = cp + n;
if (class != C_IN) {
/* XXX - debug? syslog? */
cp += n;
continue; /* XXX - had_error++ ? */
}
if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
if (ap >= &host_aliases[MAXALIASES-1])
continue;
n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
if ((n < 0) || !(*name_ok)(tbuf)) {
had_error++;
continue;
}
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
/* Store alias. */
*ap++ = bp;
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
had_error++;
continue;
}
bp += n;
/* Get canonical name. */
n = strlen(tbuf) + 1; /* for the \0 */
if (n > ep - bp || n >= MAXHOSTNAMELEN) {
had_error++;
continue;
}
strlcpy(bp, tbuf, (size_t)(ep - bp));
host.h_name = bp;
bp += n;
continue;
}
if (qtype == T_PTR && type == T_CNAME) {
n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
if (n < 0 || !res_dnok(tbuf)) {
had_error++;
continue;
}
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
/* Get canonical name. */
n = strlen(tbuf) + 1; /* for the \0 */
if (n > ep - bp || n >= MAXHOSTNAMELEN) {
had_error++;
continue;
}
strlcpy(bp, tbuf, (size_t)(ep - bp));
tname = bp;
bp += n;
continue;
}
if (type != qtype) {
if (type != T_KEY && type != T_SIG)
syslog(LOG_NOTICE|LOG_AUTH,
"gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
qname, p_class(C_IN), p_type(qtype),
p_type(type));
cp += n;
continue; /* XXX - had_error++ ? */
}
switch (type) {
case T_PTR:
if (strcasecmp(tname, bp) != 0) {
syslog(LOG_NOTICE|LOG_AUTH,
AskedForGot, qname, bp);
cp += n;
continue; /* XXX - had_error++ ? */
}
n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
if ((n < 0) || !res_hnok(bp)) {
had_error++;
break;
}
#if MULTI_PTRS_ARE_ALIASES
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
if (!haveanswer)
host.h_name = bp;
else if (ap < &host_aliases[MAXALIASES-1])
*ap++ = bp;
else
n = -1;
if (n != -1) {
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
had_error++;
break;
}
bp += n;
}
break;
#else
host.h_name = bp;
if (res->options & RES_USE_INET6) {
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
had_error++;
break;
}
bp += n;
map_v4v6_hostent(&host, &bp, ep);
}
h_errno = NETDB_SUCCESS;
return &host;
#endif
case T_A:
case T_AAAA:
if (strcasecmp(host.h_name, bp) != 0) {
syslog(LOG_NOTICE|LOG_AUTH,
AskedForGot, host.h_name, bp);
cp += n;
continue; /* XXX - had_error++ ? */
}
if (n != host.h_length) {
cp += n;
continue;
}
if (type == T_AAAA) {
struct in6_addr in6;
memcpy(&in6, cp, IN6ADDRSZ);
if (IN6_IS_ADDR_V4MAPPED(&in6)) {
cp += n;
continue;
}
}
if (!haveanswer) {
int nn;
host.h_name = bp;
nn = strlen(bp) + 1; /* for the \0 */
bp += nn;
}
bp += sizeof(align) -
(size_t)((u_long)bp % sizeof(align));
if (bp + n >= &hostbuf[sizeof hostbuf]) {
debugprintf("size (%d) too big\n", res, n);
had_error++;
continue;
}
if (hap >= &h_addr_ptrs[MAXADDRS-1]) {
if (!toobig++) {
debugprintf("Too many addresses (%d)\n",
res, MAXADDRS);
}
cp += n;
continue;
}
(void)memcpy(*hap++ = bp, cp, (size_t)n);
bp += n;
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
break;
default:
abort();
}
if (!had_error)
haveanswer++;
}
if (haveanswer) {
*ap = NULL;
*hap = NULL;
/*
* Note: we sort even if host can take only one address
* in its return structures - should give it the "best"
* address in that case, not some random one
*/
if (res->nsort && haveanswer > 1 && qtype == T_A)
addrsort(h_addr_ptrs, haveanswer, res);
if (!host.h_name) {
n = strlen(qname) + 1; /* for the \0 */
if (n > ep - bp || n >= MAXHOSTNAMELEN)
goto no_recovery;
strlcpy(bp, qname, (size_t)(ep - bp));
host.h_name = bp;
bp += n;
}
if (res->options & RES_USE_INET6)
map_v4v6_hostent(&host, &bp, ep);
h_errno = NETDB_SUCCESS;
return &host;
}
no_recovery:
h_errno = NO_RECOVERY;
return NULL;
}
struct hostent *
gethostbyname(const char *name)
{
struct hostent *hp;
res_state res = __res_get_state();
if (res == NULL)
return NULL;
_DIAGASSERT(name != NULL);
if (res->options & RES_USE_INET6) {
hp = gethostbyname_internal(name, AF_INET6, res);
if (hp) {
__res_put_state(res);
return hp;
}
}
hp = gethostbyname_internal(name, AF_INET, res);
__res_put_state(res);
return hp;
}
struct hostent *
gethostbyname2(const char *name, int af)
{
struct hostent *hp;
res_state res = __res_get_state();
if (res == NULL)
return NULL;
hp = gethostbyname_internal(name, af, res);
__res_put_state(res);
return hp;
}
static struct hostent *
gethostbyname_internal(const char *name, int af, res_state res)
{
const char *cp;
char *bp, *ep;
int size;
struct hostent *hp;
static const ns_dtab dtab[] = {
NS_FILES_CB(_gethtbyname, NULL)
{ NSSRC_DNS, _dns_gethtbyname, NULL }, /* force -DHESIOD */
NS_NIS_CB(_yp_gethtbyname, NULL)
NS_NULL_CB
};
_DIAGASSERT(name != NULL);
switch (af) {
case AF_INET:
size = INADDRSZ;
break;
case AF_INET6:
size = IN6ADDRSZ;
break;
default:
h_errno = NETDB_INTERNAL;
errno = EAFNOSUPPORT;
return NULL;
}
host.h_addrtype = af;
host.h_length = size;
/*
* if there aren't any dots, it could be a user-level alias.
* this is also done in res_nquery() since we are not the only
* function that looks up host names.
*/
if (!strchr(name, '.') && (cp = __hostalias(name)))
name = cp;
/*
* disallow names consisting only of digits/dots, unless
* they end in a dot.
*/
if (isdigit((u_char) name[0]))
for (cp = name;; ++cp) {
if (!*cp) {
if (*--cp == '.')
break;
/*
* All-numeric, no dot at the end.
* Fake up a hostent as if we'd actually
* done a lookup.
*/
if (inet_pton(af, name,
(char *)(void *)host_addr) <= 0) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
strncpy(hostbuf, name, MAXDNAME);
hostbuf[MAXDNAME] = '\0';
bp = hostbuf + MAXDNAME;
ep = hostbuf + sizeof hostbuf;
host.h_name = hostbuf;
host.h_aliases = host_aliases;
host_aliases[0] = NULL;
h_addr_ptrs[0] = (char *)(void *)host_addr;
h_addr_ptrs[1] = NULL;
host.h_addr_list = h_addr_ptrs;
if (res->options & RES_USE_INET6)
map_v4v6_hostent(&host, &bp, ep);
h_errno = NETDB_SUCCESS;
return &host;
}
if (!isdigit((u_char) *cp) && *cp != '.')
break;
}
if ((isxdigit((u_char) name[0]) && strchr(name, ':') != NULL) ||
name[0] == ':')
for (cp = name;; ++cp) {
if (!*cp) {
if (*--cp == '.')
break;
/*
* All-IPv6-legal, no dot at the end.
* Fake up a hostent as if we'd actually
* done a lookup.
*/
if (inet_pton(af, name,
(char *)(void *)host_addr) <= 0) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
strncpy(hostbuf, name, MAXDNAME);
hostbuf[MAXDNAME] = '\0';
bp = hostbuf + MAXDNAME;
ep = hostbuf + sizeof hostbuf;
host.h_name = hostbuf;
host.h_aliases = host_aliases;
host_aliases[0] = NULL;
h_addr_ptrs[0] = (char *)(void *)host_addr;
h_addr_ptrs[1] = NULL;
host.h_addr_list = h_addr_ptrs;
h_errno = NETDB_SUCCESS;
return &host;
}
if (!isxdigit((u_char) *cp) && *cp != ':' && *cp != '.')
break;
}
hp = NULL;
h_errno = NETDB_INTERNAL;
if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyname",
default_dns_files, name, strlen(name), af) != NS_SUCCESS)
return NULL;
h_errno = NETDB_SUCCESS;
return hp;
}
struct hostent *
gethostbyaddr(const char *addr, /* XXX should have been def'd as u_char! */
socklen_t len, int af)
{
const u_char *uaddr = (const u_char *)addr;
socklen_t size;
struct hostent *hp;
static const ns_dtab dtab[] = {
NS_FILES_CB(_gethtbyaddr, NULL)
{ NSSRC_DNS, _dns_gethtbyaddr, NULL }, /* force -DHESIOD */
NS_NIS_CB(_yp_gethtbyaddr, NULL)
NS_NULL_CB
};
_DIAGASSERT(addr != NULL);
if (af == AF_INET6 && len == IN6ADDRSZ &&
(IN6_IS_ADDR_LINKLOCAL((const struct in6_addr *)(const void *)uaddr) ||
IN6_IS_ADDR_SITELOCAL((const struct in6_addr *)(const void *)uaddr))) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
if (af == AF_INET6 && len == IN6ADDRSZ &&
(IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)(const void *)uaddr) ||
IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)(const void *)uaddr))) {
/* Unmap. */
addr += IN6ADDRSZ - INADDRSZ;
uaddr += IN6ADDRSZ - INADDRSZ;
af = AF_INET;
len = INADDRSZ;
}
switch (af) {
case AF_INET:
size = INADDRSZ;
break;
case AF_INET6:
size = IN6ADDRSZ;
break;
default:
errno = EAFNOSUPPORT;
h_errno = NETDB_INTERNAL;
return NULL;
}
if (size != len) {
errno = EINVAL;
h_errno = NETDB_INTERNAL;
return NULL;
}
hp = NULL;
h_errno = NETDB_INTERNAL;
if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyaddr",
default_dns_files, uaddr, len, af) != NS_SUCCESS)
return NULL;
h_errno = NETDB_SUCCESS;
return hp;
}
void
_sethtent(int f)
{
if (!hostf)
hostf = fopen(_PATH_HOSTS, "r" );
else
rewind(hostf);
stayopen = f;
}
void
_endhtent(void)
{
if (hostf && !stayopen) {
(void) fclose(hostf);
hostf = NULL;
}
}
struct hostent *
_gethtent(void)
{
char *p;
char *cp, **q;
int af, len;
if (!hostf && !(hostf = fopen(_PATH_HOSTS, "r" ))) {
h_errno = NETDB_INTERNAL;
return NULL;
}
again:
if (!(p = fgets(hostbuf, sizeof hostbuf, hostf))) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
if (*p == '#')
goto again;
if (!(cp = strpbrk(p, "#\n")))
goto again;
*cp = '\0';
if (!(cp = strpbrk(p, " \t")))
goto again;
*cp++ = '\0';
if (inet_pton(AF_INET6, p, (char *)(void *)host_addr) > 0) {
af = AF_INET6;
len = IN6ADDRSZ;
} else if (inet_pton(AF_INET, p, (char *)(void *)host_addr) > 0) {
res_state res = __res_get_state();
if (res == NULL)
return NULL;
if (res->options & RES_USE_INET6) {
map_v4v6_address((char *)(void *)host_addr,
(char *)(void *)host_addr);
af = AF_INET6;
len = IN6ADDRSZ;
} else {
af = AF_INET;
len = INADDRSZ;
}
__res_put_state(res);
} else {
goto again;
}
/* if this is not something we're looking for, skip it. */
if (host.h_addrtype != 0 && host.h_addrtype != af)
goto again;
if (host.h_length != 0 && host.h_length != len)
goto again;
h_addr_ptrs[0] = (char *)(void *)host_addr;
h_addr_ptrs[1] = NULL;
host.h_addr_list = h_addr_ptrs;
host.h_length = len;
host.h_addrtype = af;
while (*cp == ' ' || *cp == '\t')
cp++;
host.h_name = cp;
q = host.h_aliases = host_aliases;
if ((cp = strpbrk(cp, " \t")) != NULL)
*cp++ = '\0';
while (cp && *cp) {
if (*cp == ' ' || *cp == '\t') {
cp++;
continue;
}
if (q < &host_aliases[MAXALIASES - 1])
*q++ = cp;
if ((cp = strpbrk(cp, " \t")) != NULL)
*cp++ = '\0';
}
*q = NULL;
h_errno = NETDB_SUCCESS;
return &host;
}
/*ARGSUSED*/
int
_gethtbyname(void *rv, void *cb_data, va_list ap)
{
struct hostent *hp;
const char *name;
int af;
_DIAGASSERT(rv != NULL);
name = va_arg(ap, char *);
/* NOSTRICT skip len */(void)va_arg(ap, int);
af = va_arg(ap, int);
hp = NULL;
#if 0
{
res_state res = __res_get_state();
if (res == NULL)
return NS_NOTFOUND;
if (res->options & RES_USE_INET6)
hp = _gethtbyname2(name, AF_INET6);
if (hp==NULL)
hp = _gethtbyname2(name, AF_INET);
__res_put_state(res);
}
#else
hp = _gethtbyname2(name, af);
#endif
*((struct hostent **)rv) = hp;
if (hp == NULL) {
h_errno = HOST_NOT_FOUND;
return NS_NOTFOUND;
}
return NS_SUCCESS;
}
struct hostent *
_gethtbyname2(const char *name, int af)
{
struct hostent *p;
char *tmpbuf, *ptr, **cp;
int num;
size_t len;
_DIAGASSERT(name != NULL);
_sethtent(stayopen);
ptr = tmpbuf = NULL;
num = 0;
while ((p = _gethtent()) != NULL && num < MAXADDRS) {
if (p->h_addrtype != af)
continue;
if (strcasecmp(p->h_name, name) != 0) {
for (cp = p->h_aliases; *cp != NULL; cp++)
if (strcasecmp(*cp, name) == 0)
break;
if (*cp == NULL) continue;
}
if (num == 0) {
size_t bufsize;
char *src;
bufsize = strlen(p->h_name) + 2 +
MAXADDRS * p->h_length +
ALIGNBYTES;
for (cp = p->h_aliases; *cp != NULL; cp++)
bufsize += strlen(*cp) + 1;
if ((tmpbuf = malloc(bufsize)) == NULL) {
h_errno = NETDB_INTERNAL;
return NULL;
}
ptr = tmpbuf;
src = p->h_name;
while ((*ptr++ = *src++) != '\0');
for (cp = p->h_aliases; *cp != NULL; cp++) {
src = *cp;
while ((*ptr++ = *src++) != '\0');
}
*ptr++ = '\0';
ptr = (char *)(void *)ALIGN(ptr);
}
(void)memcpy(ptr, p->h_addr_list[0], (size_t)p->h_length);
ptr += p->h_length;
num++;
}
_endhtent();
if (num == 0) return NULL;
len = ptr - tmpbuf;
if (len > (sizeof(hostbuf) - ALIGNBYTES)) {
free(tmpbuf);
errno = ENOSPC;
h_errno = NETDB_INTERNAL;
return NULL;
}
ptr = memcpy((void *)ALIGN(hostbuf), tmpbuf, len);
free(tmpbuf);
host.h_name = ptr;
while (*ptr++);
cp = host_aliases;
while (*ptr) {
*cp++ = ptr;
while (*ptr++);
}
ptr++;
*cp = NULL;
ptr = (char *)(void *)ALIGN(ptr);
cp = h_addr_ptrs;
while (num--) {
*cp++ = ptr;
ptr += host.h_length;
}
*cp = NULL;
return &host;
}
/*ARGSUSED*/
int
_gethtbyaddr(void *rv, void *cb_data, va_list ap)
{
struct hostent *p;
const unsigned char *addr;
int len, af;
_DIAGASSERT(rv != NULL);
addr = va_arg(ap, unsigned char *);
len = va_arg(ap, int);
af = va_arg(ap, int);
host.h_length = len;
host.h_addrtype = af;
_sethtent(stayopen);
while ((p = _gethtent()) != NULL)
if (p->h_addrtype == af && !memcmp(p->h_addr, addr,
(size_t)len))
break;
_endhtent();
*((struct hostent **)rv) = p;
if (p==NULL) {
h_errno = HOST_NOT_FOUND;
return NS_NOTFOUND;
}
return NS_SUCCESS;
}
static void
map_v4v6_address(const char *src, char *dst)
{
u_char *p = (u_char *)dst;
char tmp[INADDRSZ];
int i;
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
/* Stash a temporary copy so our caller can update in place. */
(void)memcpy(tmp, src, INADDRSZ);
/* Mark this ipv6 addr as a mapped ipv4. */
for (i = 0; i < 10; i++)
*p++ = 0x00;
*p++ = 0xff;
*p++ = 0xff;
/* Retrieve the saved copy and we're done. */
(void)memcpy((void *)p, tmp, INADDRSZ);
}
static void
map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep)
{
char **ap;
_DIAGASSERT(hp != NULL);
_DIAGASSERT(bpp != NULL);
_DIAGASSERT(ep != NULL);
if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ)
return;
hp->h_addrtype = AF_INET6;
hp->h_length = IN6ADDRSZ;
for (ap = hp->h_addr_list; *ap; ap++) {
int i = sizeof(align) - (size_t)((u_long)*bpp % sizeof(align));
if (ep - *bpp < (i + IN6ADDRSZ)) {
/* Out of memory. Truncate address list here. XXX */
*ap = NULL;
return;
}
*bpp += i;
map_v4v6_address(*ap, *bpp);
*ap = *bpp;
*bpp += IN6ADDRSZ;
}
}
static void
addrsort(char **ap, int num, res_state res)
{
int i, j;
char **p;
short aval[MAXADDRS];
int needsort = 0;
_DIAGASSERT(ap != NULL);
p = ap;
for (i = 0; i < num; i++, p++) {
for (j = 0 ; (unsigned)j < res->nsort; j++)
if (res->sort_list[j].addr.s_addr ==
(((struct in_addr *)(void *)(*p))->s_addr &
res->sort_list[j].mask))
break;
aval[i] = j;
if (needsort == 0 && i > 0 && j < aval[i-1])
needsort = i;
}
if (!needsort)
return;
while (needsort < num) {
for (j = needsort - 1; j >= 0; j--) {
if (aval[j] > aval[j+1]) {
char *hp;
i = aval[j];
aval[j] = aval[j+1];
aval[j+1] = i;
hp = ap[j];
ap[j] = ap[j+1];
ap[j+1] = hp;
} else
break;
}
needsort++;
}
}
struct hostent *
gethostent(void)
{
host.h_addrtype = 0;
host.h_length = 0;
return _gethtent();
}
/*ARGSUSED*/
int
_dns_gethtbyname(void *rv, void *cb_data, va_list ap)
{
querybuf *buf;
int n, type;
struct hostent *hp;
const char *name;
int af;
res_state res;
_DIAGASSERT(rv != NULL);
name = va_arg(ap, char *);
/* NOSTRICT skip len */(void)va_arg(ap, int);
af = va_arg(ap, int);
switch (af) {
case AF_INET:
type = T_A;
break;
case AF_INET6:
type = T_AAAA;
break;
default:
return NS_UNAVAIL;
}
buf = malloc(sizeof(*buf));
if (buf == NULL) {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
res = __res_get_state();
if (res == NULL) {
free(buf);
return NS_NOTFOUND;
}
n = res_nsearch(res, name, C_IN, type, buf->buf, sizeof(buf->buf));
if (n < 0) {
free(buf);
debugprintf("res_nsearch failed (%d)\n", res, n);
__res_put_state(res);
return NS_NOTFOUND;
}
hp = getanswer(buf, n, name, type, res);
free(buf);
__res_put_state(res);
if (hp == NULL)
switch (h_errno) {
case HOST_NOT_FOUND:
return NS_NOTFOUND;
case TRY_AGAIN:
return NS_TRYAGAIN;
default:
return NS_UNAVAIL;
}
*((struct hostent **)rv) = hp;
return NS_SUCCESS;
}
/*ARGSUSED*/
int
_dns_gethtbyaddr(void *rv, void *cb_data, va_list ap)
{
char qbuf[MAXDNAME + 1], *qp, *ep;
int n;
querybuf *buf;
struct hostent *hp;
const unsigned char *uaddr;
int len, af, advance;
res_state res;
_DIAGASSERT(rv != NULL);
uaddr = va_arg(ap, unsigned char *);
len = va_arg(ap, int);
af = va_arg(ap, int);
switch (af) {
case AF_INET:
(void)snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa",
(uaddr[3] & 0xff), (uaddr[2] & 0xff),
(uaddr[1] & 0xff), (uaddr[0] & 0xff));
break;
case AF_INET6:
qp = qbuf;
ep = qbuf + sizeof(qbuf) - 1;
for (n = IN6ADDRSZ - 1; n >= 0; n--) {
advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.",
uaddr[n] & 0xf,
((unsigned int)uaddr[n] >> 4) & 0xf);
if (advance > 0 && qp + advance < ep)
qp += advance;
else {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
}
if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
break;
default:
abort();
}
buf = malloc(sizeof(*buf));
if (buf == NULL) {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
res = __res_get_state();
if (res == NULL) {
free(buf);
return NS_NOTFOUND;
}
n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, sizeof(buf->buf));
if (n < 0) {
free(buf);
debugprintf("res_nquery failed (%d)\n", res, n);
__res_put_state(res);
return NS_NOTFOUND;
}
hp = getanswer(buf, n, qbuf, T_PTR, res);
free(buf);
if (hp == NULL) {
__res_put_state(res);
switch (h_errno) {
case HOST_NOT_FOUND:
return NS_NOTFOUND;
case TRY_AGAIN:
return NS_TRYAGAIN;
default:
return NS_UNAVAIL;
}
}
hp->h_addrtype = af;
hp->h_length = len;
(void)memcpy(host_addr, uaddr, (size_t)len);
h_addr_ptrs[0] = (char *)(void *)host_addr;
h_addr_ptrs[1] = NULL;
if (af == AF_INET && (res->options & RES_USE_INET6)) {
map_v4v6_address((char *)(void *)host_addr,
(char *)(void *)host_addr);
hp->h_addrtype = AF_INET6;
hp->h_length = IN6ADDRSZ;
}
__res_put_state(res);
*((struct hostent **)rv) = hp;
h_errno = NETDB_SUCCESS;
return NS_SUCCESS;
}
#ifdef YP
/*ARGSUSED*/
struct hostent *
_yphostent(char *line, int af)
{
static struct in_addr host_addrs[MAXADDRS];
static struct in6_addr host6_addrs[MAXADDRS];
char *p = line;
char *cp, **q;
char **hap;
int addrok;
int more;
size_t naddrs;
_DIAGASSERT(line != NULL);
host.h_name = NULL;
host.h_addr_list = h_addr_ptrs;
host.h_addrtype = af;
switch (af) {
case AF_INET:
host.h_length = INADDRSZ;
break;
case AF_INET6:
host.h_length = IN6ADDRSZ;
break;
default:
return NULL;
}
hap = h_addr_ptrs;
q = host.h_aliases = host_aliases;
naddrs = 0;
nextline:
/* check for host_addrs overflow */
if (naddrs >= sizeof(host_addrs) / sizeof(host_addrs[0]))
goto done;
if (naddrs >= sizeof(host6_addrs) / sizeof(host6_addrs[0]))
goto done;
more = 0;
cp = strpbrk(p, " \t");
if (cp == NULL)
goto done;
*cp++ = '\0';
/* p has should have an address */
addrok = 0;
switch (af) {
case AF_INET:
addrok = inet_aton(p, &host_addrs[naddrs]);
break;
case AF_INET6:
addrok = inet_pton(af, p, &host6_addrs[naddrs]);
break;
}
if (addrok != 1) {
/* skip to the next line */
while (cp && *cp) {
if (*cp == '\n') {
cp++;
goto nextline;
}
cp++;
}
goto done;
}
switch (af) {
case AF_INET:
*hap++ = (char *)(void *)&host_addrs[naddrs++];
break;
case AF_INET6:
*hap++ = (char *)(void *)&host6_addrs[naddrs++];
break;
}
while (*cp == ' ' || *cp == '\t')
cp++;
p = cp;
cp = strpbrk(p, " \t\n");
if (cp != NULL) {
if (*cp == '\n')
more = 1;
*cp++ = '\0';
}
if (!host.h_name)
host.h_name = p;
else if (strcmp(host.h_name, p)==0)
;
else if (q < &host_aliases[MAXALIASES - 1])
*q++ = p;
p = cp;
if (more)
goto nextline;
while (cp && *cp) {
if (*cp == ' ' || *cp == '\t') {
cp++;
continue;
}
if (*cp == '\n') {
cp++;
goto nextline;
}
if (q < &host_aliases[MAXALIASES - 1])
*q++ = cp;
cp = strpbrk(cp, " \t");
if (cp != NULL)
*cp++ = '\0';
}
done:
if (host.h_name == NULL)
return NULL;
*q = NULL;
*hap = NULL;
return &host;
}
/*ARGSUSED*/
int
_yp_gethtbyaddr(void *rv, void *cb_data, va_list ap)
{
struct hostent *hp = NULL;
static char *__ypcurrent;
int __ypcurrentlen, r;
char name[INET6_ADDRSTRLEN]; /* XXX enough? */
const unsigned char *uaddr;
int af;
const char *map;
_DIAGASSERT(rv != NULL);
uaddr = va_arg(ap, unsigned char *);
/* NOSTRICT skip len */(void)va_arg(ap, int);
af = va_arg(ap, int);
if (!__ypdomain) {
if (_yp_check(&__ypdomain) == 0)
return NS_UNAVAIL;
}
/*
* XXX unfortunately, we cannot support IPv6 extended scoped address
* notation here. gethostbyaddr() is not scope-aware. too bad.
*/
if (inet_ntop(af, uaddr, name, sizeof(name)) == NULL)
return NS_UNAVAIL;
if (__ypcurrent)
free(__ypcurrent);
__ypcurrent = NULL;
switch (af) {
case AF_INET:
map = "hosts.byaddr";
break;
default:
map = "ipnodes.byaddr";
break;
}
r = yp_match(__ypdomain, map, name,
(int)strlen(name), &__ypcurrent, &__ypcurrentlen);
if (r == 0)
hp = _yphostent(__ypcurrent, af);
if (hp == NULL) {
h_errno = HOST_NOT_FOUND;
return NS_NOTFOUND;
}
*((struct hostent **)rv) = hp;
return NS_SUCCESS;
}
/*ARGSUSED*/
int
_yp_gethtbyname(void *rv, void *cb_data, va_list ap)
{
struct hostent *hp = NULL;
static char *__ypcurrent;
int __ypcurrentlen, r;
const char *name;
int af;
const char *map;
_DIAGASSERT(rv != NULL);
name = va_arg(ap, char *);
/* NOSTRICT skip len */(void)va_arg(ap, int);
af = va_arg(ap, int);
if (!__ypdomain) {
if (_yp_check(&__ypdomain) == 0)
return NS_UNAVAIL;
}
if (__ypcurrent)
free(__ypcurrent);
__ypcurrent = NULL;
switch (af) {
case AF_INET:
map = "hosts.byname";
break;
default:
map = "ipnodes.byname";
break;
}
r = yp_match(__ypdomain, map, name,
(int)strlen(name), &__ypcurrent, &__ypcurrentlen);
if (r == 0)
hp = _yphostent(__ypcurrent, af);
if (hp == NULL) {
h_errno = HOST_NOT_FOUND;
return NS_NOTFOUND;
}
*((struct hostent **)rv) = hp;
return NS_SUCCESS;
}
#endif