2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A *
2N/A * This file defines and implements the re-entrant getipnodebyname(),
2N/A * getipnodebyaddr(), and freehostent() routines for IPv6. These routines
2N/A * follow use the netdir_getbyYY() (see netdir_inet.c).
2N/A *
2N/A * lib/libnsl/nss/getipnodeby.c
2N/A */
2N/A
2N/A#include "mt.h"
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <stropts.h>
2N/A#include <ctype.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <netdb.h>
2N/A#include <stdio.h>
2N/A#include <arpa/inet.h>
2N/A#include <nss_dbdefs.h>
2N/A#include <netinet/in.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/sockio.h>
2N/A#include <nss_netdir.h>
2N/A#include <net/if.h>
2N/A#include <netinet/in.h>
2N/A#include <netdir.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A#include <fcntl.h>
2N/A#include <sys/time.h>
2N/A#include "nss.h"
2N/A
2N/A#define IPV6_LITERAL_CHAR ':'
2N/A
2N/A/*
2N/A * The number of nanoseconds getipnodebyname() waits before getting
2N/A * fresh interface count information with SIOCGLIFNUM. The default is
2N/A * five minutes.
2N/A */
2N/A#define IFNUM_TIMEOUT ((hrtime_t)300 * NANOSEC)
2N/A
2N/A/*
2N/A * Bits in the bitfield returned by getipnodebyname_processflags().
2N/A *
2N/A * IPNODE_WANTIPV6 The user wants IPv6 addresses returned.
2N/A * IPNODE_WANTIPV4 The user wants IPv4 addresses returned.
2N/A * IPNODE_IPV4IFNOIPV6 The user only wants IPv4 addresses returned if no IPv6
2N/A * addresses are returned.
2N/A * IPNODE_LOOKUPIPNODES getipnodebyname() needs to lookup the name in ipnodes.
2N/A * IPNODE_LOOKUPHOSTS getipnodebyname() needs to lookup the name in hosts.
2N/A * IPNODE_ISLITERAL The name supplied is a literal address string.
2N/A */
2N/A#define IPNODE_WANTIPV6 0x00000001u
2N/A#define IPNODE_WANTIPV4 0x00000002u
2N/A#define IPNODE_IPV4IFNOIPV6 0x00000004u
2N/A#define IPNODE_LOOKUPIPNODES 0x00000008u
2N/A#define IPNODE_LOOKUPHOSTS 0x00000010u
2N/A#define IPNODE_LITERAL 0x00000020u
2N/A#define IPNODE_IPV4 (IPNODE_WANTIPV4 | IPNODE_IPV4IFNOIPV6)
2N/A
2N/A/*
2N/A * The default set of bits corresponding to a getipnodebyname() flags
2N/A * argument of AI_DEFAULT.
2N/A */
2N/A#define IPNODE_DEFAULT (IPNODE_WANTIPV6 | IPNODE_IPV4 | \
2N/A IPNODE_LOOKUPIPNODES | IPNODE_LOOKUPHOSTS)
2N/A
2N/Aextern struct netconfig *__rpc_getconfip(char *);
2N/A
2N/Astatic struct hostent *__mapv4tov6(struct hostent *, struct hostent *,
2N/A nss_XbyY_buf_t *, int);
2N/Astruct hostent *__mappedtov4(struct hostent *, int *);
2N/Astatic struct hostent *__filter_addresses(int, struct hostent *);
2N/Astatic int __find_mapped(struct hostent *, int);
2N/Astatic nss_XbyY_buf_t *__IPv6_alloc(int);
2N/Astatic void __IPv6_cleanup(nss_XbyY_buf_t *);
2N/Astatic int __ai_addrconfig(int, boolean_t *);
2N/A
2N/A
2N/A#ifdef PIC
2N/Astruct hostent *
2N/A_uncached_getipnodebyname(const char *nam, struct hostent *result,
2N/A char *buffer, int buflen, int af_family, int flags, int *h_errnop)
2N/A{
2N/A return (_switch_getipnodebyname_r(nam, result, buffer, buflen,
2N/A af_family, flags, h_errnop));
2N/A}
2N/A
2N/Astruct hostent *
2N/A_uncached_getipnodebyaddr(const char *addr, int length, int type,
2N/A struct hostent *result, char *buffer, int buflen, int *h_errnop)
2N/A{
2N/A if (type == AF_INET)
2N/A return (_switch_gethostbyaddr_r(addr, length, type,
2N/A result, buffer, buflen, h_errnop));
2N/A else if (type == AF_INET6)
2N/A return (_switch_getipnodebyaddr_r(addr, length, type,
2N/A result, buffer, buflen, h_errnop));
2N/A return (NULL);
2N/A}
2N/A#endif
2N/A
2N/A/*
2N/A * Given a name, an address family, and a set of flags, return a
2N/A * bitfield that getipnodebyname() will use.
2N/A */
2N/Astatic uint_t
2N/Agetipnodebyname_processflags(const char *name, int af, int flags)
2N/A{
2N/A uint_t ipnode_bits = IPNODE_DEFAULT;
2N/A boolean_t ipv6configured = B_FALSE;
2N/A boolean_t ipv4configured = B_FALSE;
2N/A boolean_t ipv6loconfigured = B_FALSE;
2N/A boolean_t ipv4loconfigured = B_FALSE;
2N/A
2N/A /*
2N/A * If AI_ADDRCONFIG is specified, we need to determine the number
2N/A * of addresses of each address family configured on the system as
2N/A * appropriate.
2N/A */
2N/A if (flags & AI_ADDRCONFIG) {
2N/A ipv6configured = (af == AF_INET6 &&
2N/A __ai_addrconfig(AF_INET6, &ipv6loconfigured) > 0);
2N/A ipv4configured = ((af == AF_INET || (flags & AI_V4MAPPED)) &&
2N/A __ai_addrconfig(AF_INET, &ipv4loconfigured) > 0);
2N/A }
2N/A
2N/A /*
2N/A * Determine what kinds of addresses the user is interested
2N/A * in getting back.
2N/A *
2N/A * If flag AI_ADDRCONFIG is set, an address is only returned if
2N/A * an address of the same address family is configured on the local
2N/A * system. The handling is separate for loopback vs. non-loopback
2N/A * addresses, in that only the existence of configured non-loopback
2N/A * addresses affects the the handling of non-loopback results, and
2N/A * vice versa.
2N/A */
2N/A switch (af) {
2N/A case AF_INET6:
2N/A if ((flags & AI_ADDRCONFIG) && !ipv6configured) {
2N/A /*
2N/A * No external v6 interface is configured,
2N/A * check if v6 loopback address is configured.
2N/A * If yes, allow string representation of "localhost"
2N/A * to be resolved.
2N/A */
2N/A if (!ipv6loconfigured || strcmp(name, "localhost") != 0)
2N/A ipnode_bits &= ~IPNODE_WANTIPV6;
2N/A }
2N/A
2N/A if (flags & AI_V4MAPPED) {
2N/A if ((flags & AI_ADDRCONFIG) && !ipv4configured) {
2N/A /*
2N/A * No external v4 interface is configured,
2N/A * check if v4 loopback address is configured.
2N/A * If yes, allow string representation of
2N/A * "localhost" to be resolved.
2N/A */
2N/A if (!ipv4loconfigured ||
2N/A strcmp(name, "localhost") != 0)
2N/A ipnode_bits &= ~IPNODE_IPV4;
2N/A } else if (flags & AI_ALL) {
2N/A ipnode_bits &= ~IPNODE_IPV4IFNOIPV6;
2N/A }
2N/A } else {
2N/A ipnode_bits &= ~IPNODE_IPV4;
2N/A }
2N/A break;
2N/A case AF_INET:
2N/A if ((flags & AI_ADDRCONFIG) && !ipv4configured) {
2N/A /*
2N/A * No external v4 interface is configured,
2N/A * check if v4 loopback address is configured.
2N/A * If yes, allow sting representation of "localhost"
2N/A * to be resolved.
2N/A */
2N/A if (!ipv4loconfigured || strcmp(name, "localhost") != 0)
2N/A ipnode_bits &= ~IPNODE_IPV4;
2N/A }
2N/A ipnode_bits &= ~IPNODE_WANTIPV6;
2N/A ipnode_bits &= ~IPNODE_IPV4IFNOIPV6;
2N/A break;
2N/A default:
2N/A ipnode_bits = 0;
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * If we're not looking for IPv4 addresses, don't bother looking
2N/A * in hosts.
2N/A */
2N/A if (!(ipnode_bits & IPNODE_WANTIPV4))
2N/A ipnode_bits &= ~IPNODE_LOOKUPHOSTS;
2N/A
2N/A /*
2N/A * Determine if name is a literal IP address. This will
2N/A * further narrow down what type of lookup we're going to do.
2N/A */
2N/A if (strchr(name, IPV6_LITERAL_CHAR) != NULL) {
2N/A /* Literal IPv6 address */
2N/A ipnode_bits |= IPNODE_LITERAL;
2N/A /*
2N/A * In s9 we accepted the literal without filtering independent
2N/A * of what family was passed in hints. We continue to do
2N/A * this.
2N/A */
2N/A ipnode_bits |= (IPNODE_WANTIPV6 | IPNODE_WANTIPV4);
2N/A ipnode_bits &= ~IPNODE_LOOKUPHOSTS;
2N/A } else if (inet_addr(name) != 0xffffffffU) {
2N/A /* Literal IPv4 address */
2N/A ipnode_bits |= (IPNODE_LITERAL | IPNODE_WANTIPV4);
2N/A ipnode_bits &= ~IPNODE_WANTIPV6;
2N/A ipnode_bits &= ~IPNODE_LOOKUPIPNODES;
2N/A }
2N/A return (ipnode_bits);
2N/A}
2N/A
2N/Astruct hostent *
2N/Agetipnodebyname(const char *name, int af, int flags, int *error_num)
2N/A{
2N/A struct hostent *hp = NULL;
2N/A nss_XbyY_buf_t *buf4 = NULL;
2N/A nss_XbyY_buf_t *buf6 = NULL;
2N/A struct netconfig *nconf;
2N/A struct nss_netdirbyname_in nssin;
2N/A union nss_netdirbyname_out nssout;
2N/A int ret;
2N/A uint_t ipnode_bits;
2N/A
2N/A if ((nconf = __rpc_getconfip("udp")) == NULL &&
2N/A (nconf = __rpc_getconfip("tcp")) == NULL) {
2N/A *error_num = NO_RECOVERY;
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Check to see if AI_ADDRCONFIG is specified.
2N/A * Unset AI_ADDRCONFIG if name points to a literal address.
2N/A * This diverges from RFC 3493 in that even with no external interfaces
2N/A * configured, we allow literal address strings to be returned. This
2N/A * allows certain applications that are specifically using literal
2N/A * addresses for robustness to continue when the interfaces are being
2N/A * re-configured.
2N/A */
2N/A if (flags & AI_ADDRCONFIG) {
2N/A boolean_t isv4literal;
2N/A boolean_t isv6literal;
2N/A struct in6_addr v6addr;
2N/A
2N/A /* Check to see if name points to a literal address */
2N/A isv4literal = (inet_addr(name) != ((in_addr_t)-1)) ||
2N/A (strcmp(name, HOST_BROADCAST) == 0);
2N/A isv6literal = inet_pton(AF_INET6, name, &v6addr) > 0;
2N/A
2N/A /* Unset AI_ADDRCONFIG if name points to a literal address. */
2N/A if (isv4literal || isv6literal)
2N/A flags &= ~AI_ADDRCONFIG;
2N/A }
2N/A ipnode_bits = getipnodebyname_processflags(name, af, flags);
2N/A
2N/A /* Make sure we have something to look up. */
2N/A if (!(ipnode_bits & (IPNODE_WANTIPV6 | IPNODE_WANTIPV4))) {
2N/A *error_num = HOST_NOT_FOUND;
2N/A goto cleanup;
2N/A }
2N/A *error_num = NETDB_SUCCESS; /* Think positively. */
2N/A
2N/A /*
2N/A * Perform the requested lookups. We always look through
2N/A * ipnodes first for both IPv4 and IPv6 addresses. Depending
2N/A * on what was returned and what was needed, we either filter
2N/A * out the garbage, or ask for more using hosts.
2N/A */
2N/A if (ipnode_bits & IPNODE_LOOKUPIPNODES) {
2N/A if ((buf6 = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == NULL) {
2N/A *error_num = NO_RECOVERY;
2N/A goto cleanup;
2N/A }
2N/A nssin.op_t = NSS_HOST6;
2N/A nssin.arg.nss.host6.name = name;
2N/A nssin.arg.nss.host6.buf = buf6->buffer;
2N/A nssin.arg.nss.host6.buflen = buf6->buflen;
2N/A nssin.arg.nss.host6.af_family = af;
2N/A nssin.arg.nss.host6.flags = flags;
2N/A nssout.nss.host.hent = buf6->result;
2N/A nssout.nss.host.herrno_p = error_num;
2N/A ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout);
2N/A if (ret != ND_OK) {
2N/A __IPv6_cleanup(buf6);
2N/A buf6 = NULL;
2N/A } else if (ipnode_bits & IPNODE_WANTIPV4) {
2N/A /*
2N/A * buf6 may have all that we need if we either
2N/A * only wanted IPv4 addresses if there were no
2N/A * IPv6 addresses returned, or if there are
2N/A * IPv4-mapped addresses in buf6. If either
2N/A * of these are true, then there's no need to
2N/A * look in hosts.
2N/A */
2N/A if (ipnode_bits & IPNODE_IPV4IFNOIPV6 ||
2N/A __find_mapped(buf6->result, 0) != 0) {
2N/A ipnode_bits &= ~IPNODE_LOOKUPHOSTS;
2N/A } else if (!(ipnode_bits & IPNODE_WANTIPV6)) {
2N/A /*
2N/A * If all we're looking for are IPv4
2N/A * addresses and there are none in
2N/A * buf6 then buf6 is now useless.
2N/A */
2N/A __IPv6_cleanup(buf6);
2N/A buf6 = NULL;
2N/A }
2N/A }
2N/A }
2N/A if (ipnode_bits & IPNODE_LOOKUPHOSTS) {
2N/A if ((buf4 = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == NULL) {
2N/A *error_num = NO_RECOVERY;
2N/A goto cleanup;
2N/A }
2N/A nssin.op_t = NSS_HOST;
2N/A nssin.arg.nss.host.name = name;
2N/A nssin.arg.nss.host.buf = buf4->buffer;
2N/A nssin.arg.nss.host.buflen = buf4->buflen;
2N/A nssout.nss.host.hent = buf4->result;
2N/A nssout.nss.host.herrno_p = error_num;
2N/A ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout);
2N/A if (ret != ND_OK) {
2N/A __IPv6_cleanup(buf4);
2N/A buf4 = NULL;
2N/A }
2N/A }
2N/A
2N/A if (buf6 == NULL && buf4 == NULL) {
2N/A if (*error_num == NETDB_SUCCESS) {
2N/A /* Should not happen; lets be clear if it does. */
2N/A *error_num = HOST_NOT_FOUND;
2N/A }
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* Extract the appropriate addresses from the returned buffer(s). */
2N/A switch (af) {
2N/A case AF_INET6: {
2N/A if (buf4 != NULL) {
2N/A nss_XbyY_buf_t *mergebuf;
2N/A
2N/A /*
2N/A * The IPv4 results we have need to be
2N/A * converted to IPv4-mapped addresses,
2N/A * conditionally merged with the IPv6
2N/A * results, and the end result needs to be
2N/A * re-ordered.
2N/A */
2N/A mergebuf = __IPv6_alloc(NSS_BUFLEN_IPNODES);
2N/A if (mergebuf == NULL) {
2N/A *error_num = NO_RECOVERY;
2N/A goto cleanup;
2N/A }
2N/A hp = __mapv4tov6(buf4->result,
2N/A ((buf6 != NULL) ? buf6->result : NULL),
2N/A mergebuf, 1);
2N/A if (hp != NULL)
2N/A order_haddrlist_af(AF_INET6, hp->h_addr_list);
2N/A else
2N/A *error_num = NO_RECOVERY;
2N/A free(mergebuf);
2N/A }
2N/A
2N/A if (buf4 == NULL && buf6 != NULL) {
2N/A hp = buf6->result;
2N/A
2N/A /*
2N/A * We have what we need in buf6, but we may need
2N/A * to filter out some addresses depending on what
2N/A * is being asked for.
2N/A */
2N/A if (!(ipnode_bits & IPNODE_WANTIPV4))
2N/A hp = __filter_addresses(AF_INET, buf6->result);
2N/A else if (!(ipnode_bits & IPNODE_WANTIPV6))
2N/A hp = __filter_addresses(AF_INET6, buf6->result);
2N/A
2N/A if (hp == NULL)
2N/A *error_num = NO_ADDRESS;
2N/A }
2N/A
2N/A break;
2N/A }
2N/A
2N/A case AF_INET:
2N/A /* We could have results in buf6 or buf4, not both */
2N/A if (buf6 != NULL) {
2N/A /*
2N/A * Extract the IPv4-mapped addresses from buf6
2N/A * into hp.
2N/A */
2N/A hp = __mappedtov4(buf6->result, error_num);
2N/A } else {
2N/A /* We have what we need in buf4. */
2N/A hp = buf4->result;
2N/A if (ipnode_bits & IPNODE_LITERAL) {
2N/A /*
2N/A * There is a special case here for literal
2N/A * IPv4 address strings. The hosts
2N/A * front-end sets h_aliases to a one
2N/A * element array containing a single NULL
2N/A * pointer (in ndaddr2hent()), while
2N/A * getipnodebyname() requires h_aliases to
2N/A * be a NULL pointer itself. We're not
2N/A * going to change the front-end since it
2N/A * needs to remain backward compatible for
2N/A * gethostbyname() and friends. Just set
2N/A * h_aliases to NULL here instead.
2N/A */
2N/A hp->h_aliases = NULL;
2N/A }
2N/A }
2N/A
2N/A break;
2N/A
2N/A default:
2N/A break;
2N/A }
2N/A
2N/Acleanup:
2N/A /*
2N/A * Free the memory we allocated, but make sure we don't free
2N/A * the memory we're returning to the caller.
2N/A */
2N/A if (buf6 != NULL) {
2N/A if (buf6->result == hp)
2N/A buf6->result = NULL;
2N/A __IPv6_cleanup(buf6);
2N/A }
2N/A if (buf4 != NULL) {
2N/A if (buf4->result == hp)
2N/A buf4->result = NULL;
2N/A __IPv6_cleanup(buf4);
2N/A }
2N/A (void) freenetconfigent(nconf);
2N/A
2N/A return (hp);
2N/A}
2N/A
2N/A/*
2N/A * This is the IPv6 interface for "gethostbyaddr".
2N/A */
2N/Astruct hostent *
2N/Agetipnodebyaddr(const void *src, size_t len, int type, int *error_num)
2N/A{
2N/A struct in6_addr *addr6 = 0;
2N/A struct in_addr *addr4 = 0;
2N/A nss_XbyY_buf_t *buf = 0;
2N/A nss_XbyY_buf_t *res = 0;
2N/A struct netconfig *nconf;
2N/A struct hostent *hp = 0;
2N/A struct nss_netdirbyaddr_in nssin;
2N/A union nss_netdirbyaddr_out nssout;
2N/A int neterr;
2N/A char tmpbuf[64];
2N/A
2N/A if (type == AF_INET6) {
2N/A if ((addr6 = (struct in6_addr *)src) == NULL) {
2N/A *error_num = HOST_NOT_FOUND;
2N/A return (NULL);
2N/A }
2N/A } else if (type == AF_INET) {
2N/A if ((addr4 = (struct in_addr *)src) == NULL) {
2N/A *error_num = HOST_NOT_FOUND;
2N/A return (NULL);
2N/A }
2N/A } else {
2N/A *error_num = HOST_NOT_FOUND;
2N/A return (NULL);
2N/A }
2N/A /*
2N/A * Specific case: query for "::"
2N/A */
2N/A if (type == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(addr6)) {
2N/A *error_num = HOST_NOT_FOUND;
2N/A return (NULL);
2N/A }
2N/A /*
2N/A * Step 1: IPv4-mapped address or IPv4 Compat
2N/A */
2N/A if ((type == AF_INET6 && len == 16) &&
2N/A ((IN6_IS_ADDR_V4MAPPED(addr6)) ||
2N/A (IN6_IS_ADDR_V4COMPAT(addr6)))) {
2N/A if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
2N/A *error_num = NO_RECOVERY;
2N/A return (NULL);
2N/A }
2N/A if ((nconf = __rpc_getconfip("udp")) == NULL &&
2N/A (nconf = __rpc_getconfip("tcp")) == NULL) {
2N/A *error_num = NO_RECOVERY;
2N/A __IPv6_cleanup(buf);
2N/A return (NULL);
2N/A }
2N/A nssin.op_t = NSS_HOST6;
2N/A if (IN6_IS_ADDR_V4COMPAT(addr6)) {
2N/A (void) memcpy(tmpbuf, addr6, sizeof (*addr6));
2N/A tmpbuf[10] = 0xffU;
2N/A tmpbuf[11] = 0xffU;
2N/A nssin.arg.nss.host.addr = (const char *)tmpbuf;
2N/A } else {
2N/A nssin.arg.nss.host.addr = (const char *)addr6;
2N/A }
2N/A nssin.arg.nss.host.len = sizeof (struct in6_addr);
2N/A nssin.arg.nss.host.type = AF_INET6;
2N/A nssin.arg.nss.host.buf = buf->buffer;
2N/A nssin.arg.nss.host.buflen = buf->buflen;
2N/A
2N/A nssout.nss.host.hent = buf->result;
2N/A nssout.nss.host.herrno_p = error_num;
2N/A /*
2N/A * We pass in nconf and let the implementation of the
2N/A * long-named func decide whether to use the switch based on
2N/A * nc_nlookups.
2N/A */
2N/A neterr =
2N/A _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
2N/A
2N/A (void) freenetconfigent(nconf);
2N/A if (neterr != ND_OK) {
2N/A /* Failover case, try hosts db for v4 address */
2N/A if (!gethostbyaddr_r(((char *)addr6) + 12,
2N/A sizeof (in_addr_t), AF_INET, buf->result,
2N/A buf->buffer, buf->buflen, error_num)) {
2N/A __IPv6_cleanup(buf);
2N/A return (NULL);
2N/A }
2N/A /* Found one, now format it into mapped/compat addr */
2N/A if ((res = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
2N/A __IPv6_cleanup(buf);
2N/A *error_num = NO_RECOVERY;
2N/A return (NULL);
2N/A }
2N/A /* Convert IPv4 to mapped/compat address w/name */
2N/A hp = res->result;
2N/A (void) __mapv4tov6(buf->result, 0, res,
2N/A IN6_IS_ADDR_V4MAPPED(addr6));
2N/A __IPv6_cleanup(buf);
2N/A free(res);
2N/A return (hp);
2N/A }
2N/A /*
2N/A * At this point, we'll have a v4mapped hostent. If that's
2N/A * what was passed in, just return. If the request was a compat,
2N/A * twiggle the two bytes to make the mapped address a compat.
2N/A */
2N/A hp = buf->result;
2N/A if (IN6_IS_ADDR_V4COMPAT(addr6)) {
2N/A /* LINTED pointer cast */
2N/A addr6 = (struct in6_addr *)hp->h_addr_list[0];
2N/A addr6->s6_addr[10] = 0;
2N/A addr6->s6_addr[11] = 0;
2N/A }
2N/A free(buf);
2N/A return (hp);
2N/A }
2N/A /*
2N/A * Step 2: AF_INET, v4 lookup. Since we're going to search the
2N/A * ipnodes (v6) path first, we need to treat this as a v4mapped
2N/A * address. nscd(1m) caches v4 from ipnodes as mapped v6's. The
2N/A * switch backend knows to lookup v4's (not v4mapped) from the
2N/A * name services.
2N/A */
2N/A if (type == AF_INET) {
2N/A struct in6_addr v4mapbuf;
2N/A addr6 = &v4mapbuf;
2N/A
2N/A IN6_INADDR_TO_V4MAPPED(addr4, addr6);
2N/A if ((nconf = __rpc_getconfip("udp")) == NULL &&
2N/A (nconf = __rpc_getconfip("tcp")) == NULL) {
2N/A *error_num = NO_RECOVERY;
2N/A return (NULL);
2N/A }
2N/A if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
2N/A *error_num = NO_RECOVERY;
2N/A freenetconfigent(nconf);
2N/A return (NULL);
2N/A }
2N/A nssin.op_t = NSS_HOST6;
2N/A nssin.arg.nss.host.addr = (const char *)addr6;
2N/A nssin.arg.nss.host.len = sizeof (struct in6_addr);
2N/A nssin.arg.nss.host.type = AF_INET6;
2N/A nssin.arg.nss.host.buf = buf->buffer;
2N/A nssin.arg.nss.host.buflen = buf->buflen;
2N/A
2N/A nssout.nss.host.hent = buf->result;
2N/A nssout.nss.host.herrno_p = error_num;
2N/A /*
2N/A * We pass in nconf and let the implementation of the
2N/A * long-named func decide whether to use the switch based on
2N/A * nc_nlookups.
2N/A */
2N/A neterr =
2N/A _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
2N/A
2N/A (void) freenetconfigent(nconf);
2N/A if (neterr != ND_OK) {
2N/A /* Failover case, try hosts db for v4 address */
2N/A hp = buf->result;
2N/A if (!gethostbyaddr_r(src, len, type, buf->result,
2N/A buf->buffer, buf->buflen, error_num)) {
2N/A __IPv6_cleanup(buf);
2N/A return (NULL);
2N/A }
2N/A free(buf);
2N/A return (hp);
2N/A }
2N/A if ((hp = __mappedtov4(buf->result, error_num)) == NULL) {
2N/A __IPv6_cleanup(buf);
2N/A return (NULL);
2N/A }
2N/A __IPv6_cleanup(buf);
2N/A return (hp);
2N/A }
2N/A /*
2N/A * Step 3: AF_INET6, plain vanilla v6 getipnodebyaddr() call.
2N/A */
2N/A if (type == AF_INET6) {
2N/A if ((nconf = __rpc_getconfip("udp")) == NULL &&
2N/A (nconf = __rpc_getconfip("tcp")) == NULL) {
2N/A *error_num = NO_RECOVERY;
2N/A return (NULL);
2N/A }
2N/A if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
2N/A *error_num = NO_RECOVERY;
2N/A freenetconfigent(nconf);
2N/A return (NULL);
2N/A }
2N/A nssin.op_t = NSS_HOST6;
2N/A nssin.arg.nss.host.addr = (const char *)addr6;
2N/A nssin.arg.nss.host.len = len;
2N/A nssin.arg.nss.host.type = type;
2N/A nssin.arg.nss.host.buf = buf->buffer;
2N/A nssin.arg.nss.host.buflen = buf->buflen;
2N/A
2N/A nssout.nss.host.hent = buf->result;
2N/A nssout.nss.host.herrno_p = error_num;
2N/A /*
2N/A * We pass in nconf and let the implementation of the
2N/A * long-named func decide whether to use the switch based on
2N/A * nc_nlookups.
2N/A */
2N/A neterr =
2N/A _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
2N/A
2N/A (void) freenetconfigent(nconf);
2N/A if (neterr != ND_OK) {
2N/A __IPv6_cleanup(buf);
2N/A return (NULL);
2N/A }
2N/A free(buf);
2N/A return (nssout.nss.host.hent);
2N/A }
2N/A /*
2N/A * If we got here, unknown type.
2N/A */
2N/A *error_num = HOST_NOT_FOUND;
2N/A return (NULL);
2N/A}
2N/A
2N/Avoid
2N/Afreehostent(struct hostent *hent)
2N/A{
2N/A free(hent);
2N/A}
2N/A
2N/Astatic int
2N/A__ai_addrconfig(int af, boolean_t *loconfigured)
2N/A{
2N/A struct lifnum lifn;
2N/A struct lifconf lifc;
2N/A struct lifreq *lifp, *buf = NULL;
2N/A size_t bufsize;
2N/A hrtime_t now, *then;
2N/A static hrtime_t then4, then6; /* the last time we updated ifnum# */
2N/A static int ifnum4 = -1, ifnum6 = -1;
2N/A int *num;
2N/A int nlifr, count = 0;
2N/A
2N/A *loconfigured = B_FALSE;
2N/A
2N/A switch (af) {
2N/A case AF_INET:
2N/A num = &ifnum4;
2N/A then = &then4;
2N/A break;
2N/A case AF_INET6:
2N/A num = &ifnum6;
2N/A then = &then6;
2N/A break;
2N/A default:
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * We don't need to check this every time someone does a name
2N/A * lookup. Do it every IFNUM_TIMEOUT for each address family.
2N/A *
2N/A * There's no need to protect all of this with a lock. The
2N/A * worst that can happen is that we update the interface count
2N/A * twice instead of once. That's no big deal.
2N/A */
2N/A now = gethrtime();
2N/A if (*num == -1 || ((now - *then) >= IFNUM_TIMEOUT)) {
2N/A lifn.lifn_family = af;
2N/A /*
2N/A * We want to determine if this machine knows anything
2N/A * at all about the address family; the status of the
2N/A * interface is less important. Hence, set
2N/A * 'lifn_flags' to zero.
2N/A */
2N/A lifn.lifn_flags = 0;
2N/Aagain:
2N/A if (nss_ioctl(af, SIOCGLIFNUM, &lifn) < 0)
2N/A goto fail;
2N/A
2N/A if (lifn.lifn_count == 0) {
2N/A *num = 0;
2N/A *then = now;
2N/A return (*num);
2N/A }
2N/A
2N/A /*
2N/A * Pad the interface count to detect when additional
2N/A * interfaces have been configured between SIOCGLIFNUM
2N/A * and SIOCGLIFCONF.
2N/A */
2N/A lifn.lifn_count += 4;
2N/A
2N/A bufsize = lifn.lifn_count * sizeof (struct lifreq);
2N/A if ((buf = realloc(buf, bufsize)) == NULL)
2N/A goto fail;
2N/A
2N/A lifc.lifc_family = af;
2N/A lifc.lifc_flags = 0;
2N/A lifc.lifc_len = bufsize;
2N/A lifc.lifc_buf = (caddr_t)buf;
2N/A if (nss_ioctl(af, SIOCGLIFCONF, &lifc) < 0)
2N/A goto fail;
2N/A
2N/A nlifr = lifc.lifc_len / sizeof (struct lifreq);
2N/A if (nlifr >= lifn.lifn_count)
2N/A goto again;
2N/A /*
2N/A * Do not include any loopback addresses, 127.0.0.1 for AF_INET
2N/A * and ::1 for AF_INET6, while counting the number of available
2N/A * IPv4 or IPv6 addresses. (RFC 3493 requires this, whenever
2N/A * AI_ADDRCONFIG flag is set)
2N/A */
2N/A for (lifp = buf; lifp < buf + nlifr; lifp++) {
2N/A switch (af) {
2N/A case AF_INET: {
2N/A struct sockaddr_in *in;
2N/A
2N/A in = (struct sockaddr_in *)&lifp->lifr_addr;
2N/A if (ntohl(in->sin_addr.s_addr) ==
2N/A INADDR_LOOPBACK) {
2N/A *loconfigured = B_TRUE;
2N/A count++;
2N/A }
2N/A break;
2N/A }
2N/A case AF_INET6: {
2N/A struct sockaddr_in6 *in6;
2N/A
2N/A in6 = (struct sockaddr_in6 *)&lifp->lifr_addr;
2N/A if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr)) {
2N/A *loconfigured = B_TRUE;
2N/A count++;
2N/A }
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A *num = nlifr - count;
2N/A *then = now;
2N/A free(buf);
2N/A }
2N/A return (*num);
2N/Afail:
2N/A free(buf);
2N/A /*
2N/A * If the process is running without the NET_ACCESS basic privilege,
2N/A * pretend we still have inet/inet6 interfaces.
2N/A */
2N/A if (errno == EACCES)
2N/A return (1);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * This routine will either convert an IPv4 address to a mapped or compat
2N/A * IPv6 (if he6 == NULL) or merge IPv6 (he6) addresses with mapped
2N/A * v4 (he4) addresses. In either case, the results are returned in res.
2N/A * Caller must provide all buffers.
2N/A * Inputs:
2N/A * he4 pointer to IPv4 buffer
2N/A * he6 pointer to IPv6 buffer (NULL if not merging v4/v6
2N/A * res pointer to results buffer
2N/A * mapped mapped == 1, map IPv4 : mapped == 0, compat IPv4
2N/A * mapped flag is ignored if he6 != NULL
2N/A *
2N/A * The results are packed into the res->buffer as follows:
2N/A * <--------------- buffer + buflen -------------------------------------->
2N/A * |-----------------|-----------------|----------------|----------------|
2N/A * | pointers vector | pointers vector | aliases grow | addresses grow |
2N/A * | for addresses | for aliases | | |
2N/A * | this way -> | this way -> | <- this way |<- this way |
2N/A * |-----------------|-----------------|----------------|----------------|
2N/A * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1|
2N/A */
2N/Astatic struct hostent *
2N/A__mapv4tov6(struct hostent *he4, struct hostent *he6, nss_XbyY_buf_t *res,
2N/A int mapped)
2N/A{
2N/A char *buffer, *limit;
2N/A int buflen = res->buflen;
2N/A struct in6_addr *addr6p;
2N/A char *buff_locp;
2N/A struct hostent *host;
2N/A int count = 0, len, i;
2N/A char *h_namep;
2N/A
2N/A if (he4 == NULL || res == NULL) {
2N/A return (NULL);
2N/A }
2N/A limit = res->buffer + buflen;
2N/A host = (struct hostent *)res->result;
2N/A buffer = res->buffer;
2N/A
2N/A buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in6_addr));
2N/A host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
2N/A if ((char *)host->h_addr_list >= limit ||
2N/A buff_locp <= (char *)host->h_addr_list) {
2N/A return (NULL);
2N/A }
2N/A if (he6 == NULL) {
2N/A /*
2N/A * If he6==NULL, map the v4 address into the v6 address format.
2N/A * This is used for getipnodebyaddr() (single address, mapped or
2N/A * compatible) or for v4 mapped for getipnodebyname(), which
2N/A * could be multiple addresses. This could also be a literal
2N/A * address string, which is why there is a inet_addr() call.
2N/A */
2N/A for (i = 0; he4->h_addr_list[i] != NULL; i++) {
2N/A buff_locp -= sizeof (struct in6_addr);
2N/A if (buff_locp <=
2N/A (char *)&(host->h_addr_list[count + 1])) {
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A return (NULL);
2N/A }
2N/A /* LINTED pointer cast */
2N/A addr6p = (struct in6_addr *)buff_locp;
2N/A host->h_addr_list[count] = (char *)addr6p;
2N/A bzero(addr6p->s6_addr, sizeof (struct in6_addr));
2N/A if (mapped) {
2N/A addr6p->s6_addr[10] = 0xff;
2N/A addr6p->s6_addr[11] = 0xff;
2N/A }
2N/A bcopy((char *)he4->h_addr_list[i],
2N/A &addr6p->s6_addr[12], sizeof (struct in_addr));
2N/A ++count;
2N/A }
2N/A /*
2N/A * Set last array element to NULL and add cname as first alias
2N/A */
2N/A host->h_addr_list[count] = NULL;
2N/A host->h_aliases = host->h_addr_list + count + 1;
2N/A count = 0;
2N/A if ((int)(inet_addr(he4->h_name)) != -1) {
2N/A /*
2N/A * Literal address string, since we're mapping, we need the IPv6
2N/A * V4 mapped literal address string for h_name.
2N/A */
2N/A char tmpstr[128];
2N/A (void) inet_ntop(AF_INET6, host->h_addr_list[0], tmpstr,
2N/A sizeof (tmpstr));
2N/A buff_locp -= (len = strlen(tmpstr) + 1);
2N/A h_namep = tmpstr;
2N/A if (buff_locp <= (char *)(host->h_aliases))
2N/A return (NULL);
2N/A bcopy(h_namep, buff_locp, len);
2N/A host->h_name = buff_locp;
2N/A host->h_aliases = NULL; /* no aliases for literal */
2N/A host->h_length = sizeof (struct in6_addr);
2N/A host->h_addrtype = AF_INET6;
2N/A return (host); /* we're done, return result */
2N/A }
2N/A /*
2N/A * Not a literal address string, so just copy h_name.
2N/A */
2N/A buff_locp -= (len = strlen(he4->h_name) + 1);
2N/A h_namep = he4->h_name;
2N/A if (buff_locp <= (char *)(host->h_aliases))
2N/A return (NULL);
2N/A bcopy(h_namep, buff_locp, len);
2N/A host->h_name = buff_locp;
2N/A /*
2N/A * Pass 2 (IPv4 aliases):
2N/A */
2N/A for (i = 0; he4->h_aliases[i] != NULL; i++) {
2N/A buff_locp -= (len = strlen(he4->h_aliases[i]) + 1);
2N/A if (buff_locp <=
2N/A (char *)&(host->h_aliases[count + 1])) {
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A return (NULL);
2N/A }
2N/A host->h_aliases[count] = buff_locp;
2N/A bcopy((char *)he4->h_aliases[i], buff_locp, len);
2N/A ++count;
2N/A }
2N/A host->h_aliases[count] = NULL;
2N/A host->h_length = sizeof (struct in6_addr);
2N/A host->h_addrtype = AF_INET6;
2N/A return (host);
2N/A } else {
2N/A /*
2N/A * Merge IPv4 mapped addresses with IPv6 addresses. The
2N/A * IPv6 address will go in first, followed by the v4 mapped.
2N/A *
2N/A * Pass 1 (IPv6 addresses):
2N/A */
2N/A for (i = 0; he6->h_addr_list[i] != NULL; i++) {
2N/A buff_locp -= sizeof (struct in6_addr);
2N/A if (buff_locp <=
2N/A (char *)&(host->h_addr_list[count + 1])) {
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A return (NULL);
2N/A }
2N/A host->h_addr_list[count] = buff_locp;
2N/A bcopy((char *)he6->h_addr_list[i], buff_locp,
2N/A sizeof (struct in6_addr));
2N/A ++count;
2N/A }
2N/A /*
2N/A * Pass 1 (IPv4 mapped addresses):
2N/A */
2N/A for (i = 0; he4->h_addr_list[i] != NULL; i++) {
2N/A buff_locp -= sizeof (struct in6_addr);
2N/A if (buff_locp <=
2N/A (char *)&(host->h_addr_list[count + 1])) {
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A return (NULL);
2N/A }
2N/A /* LINTED pointer cast */
2N/A addr6p = (struct in6_addr *)buff_locp;
2N/A host->h_addr_list[count] = (char *)addr6p;
2N/A bzero(addr6p->s6_addr, sizeof (struct in6_addr));
2N/A addr6p->s6_addr[10] = 0xff;
2N/A addr6p->s6_addr[11] = 0xff;
2N/A bcopy(he4->h_addr_list[i], &addr6p->s6_addr[12],
2N/A sizeof (struct in_addr));
2N/A ++count;
2N/A }
2N/A /*
2N/A * Pass 2 (IPv6 aliases, host name first). We start h_aliases
2N/A * one after where h_addr_list array ended. This is where cname
2N/A * is put, followed by all aliases. Reset count to 0, for index
2N/A * in the h_aliases array.
2N/A */
2N/A host->h_addr_list[count] = NULL;
2N/A host->h_aliases = host->h_addr_list + count + 1;
2N/A count = 0;
2N/A buff_locp -= (len = strlen(he6->h_name) + 1);
2N/A if (buff_locp <= (char *)(host->h_aliases))
2N/A return (NULL);
2N/A bcopy(he6->h_name, buff_locp, len);
2N/A host->h_name = buff_locp;
2N/A for (i = 0; he6->h_aliases[i] != NULL; i++) {
2N/A buff_locp -= (len = strlen(he6->h_aliases[i]) + 1);
2N/A if (buff_locp <=
2N/A (char *)&(host->h_aliases[count + 1])) {
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A return (NULL);
2N/A }
2N/A host->h_aliases[count] = buff_locp;
2N/A bcopy((char *)he6->h_aliases[i], buff_locp, len);
2N/A ++count;
2N/A }
2N/A /*
2N/A * Pass 2 (IPv4 aliases):
2N/A */
2N/A for (i = 0; he4->h_aliases[i] != NULL; i++) {
2N/A buff_locp -= (len = strlen(he4->h_aliases[i]) + 1);
2N/A if (buff_locp <=
2N/A (char *)&(host->h_aliases[count + 1])) {
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A return (NULL);
2N/A }
2N/A host->h_aliases[count] = buff_locp;
2N/A bcopy((char *)he4->h_aliases[i], buff_locp, len);
2N/A ++count;
2N/A }
2N/A host->h_aliases[count] = NULL;
2N/A host->h_length = sizeof (struct in6_addr);
2N/A host->h_addrtype = AF_INET6;
2N/A return (host);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * This routine will convert a mapped v4 hostent (AF_INET6) to a
2N/A * AF_INET hostent. If no mapped addrs found, then a NULL is returned.
2N/A * If mapped addrs found, then a new buffer is alloc'd and all the v4 mapped
2N/A * addresses are extracted and copied to it. On sucess, a pointer to a new
2N/A * hostent is returned.
2N/A * There are two possible errors in which case a NULL is returned.
2N/A * One of two error codes are returned:
2N/A *
2N/A * NO_RECOVERY - a malloc failed or the like for which there's no recovery.
2N/A * NO_ADDRESS - after filtering all the v4, there was nothing left!
2N/A *
2N/A * Inputs:
2N/A * he pointer to hostent with mapped v4 addresses
2N/A * filter_error pointer to return error code
2N/A * Return:
2N/A * pointer to a malloc'd hostent with v4 addresses.
2N/A *
2N/A * The results are packed into the res->buffer as follows:
2N/A * <--------------- buffer + buflen -------------------------------------->
2N/A * |-----------------|-----------------|----------------|----------------|
2N/A * | pointers vector | pointers vector | aliases grow | addresses grow |
2N/A * | for addresses | for aliases | | |
2N/A * | this way -> | this way -> | <- this way |<- this way |
2N/A * |-----------------|-----------------|----------------|----------------|
2N/A * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1|
2N/A */
2N/Astruct hostent *
2N/A__mappedtov4(struct hostent *he, int *extract_error)
2N/A{
2N/A char *buffer, *limit;
2N/A nss_XbyY_buf_t *res;
2N/A int buflen = NSS_BUFLEN_HOSTS;
2N/A struct in_addr *addr4p;
2N/A char *buff_locp;
2N/A struct hostent *host;
2N/A int count = 0, len, i;
2N/A char *h_namep;
2N/A
2N/A if (he == NULL) {
2N/A *extract_error = NO_ADDRESS;
2N/A return (NULL);
2N/A }
2N/A if ((__find_mapped(he, 0)) == 0) {
2N/A *extract_error = NO_ADDRESS;
2N/A return (NULL);
2N/A }
2N/A if ((res = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == 0) {
2N/A *extract_error = NO_RECOVERY;
2N/A return (NULL);
2N/A }
2N/A limit = res->buffer + buflen;
2N/A host = (struct hostent *)res->result;
2N/A buffer = res->buffer;
2N/A
2N/A buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in_addr));
2N/A host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
2N/A if ((char *)host->h_addr_list >= limit ||
2N/A buff_locp <= (char *)host->h_addr_list)
2N/A goto cleanup;
2N/A /*
2N/A * "Unmap" the v4 mapped address(es) into a v4 hostent format.
2N/A * This is used for getipnodebyaddr() (single address) or for
2N/A * v4 mapped for getipnodebyname(), which could be multiple
2N/A * addresses. This could also be a literal address string,
2N/A * which is why there is a inet_addr() call.
2N/A */
2N/A for (i = 0; he->h_addr_list[i] != NULL; i++) {
2N/A /* LINTED pointer cast */
2N/A if (!IN6_IS_ADDR_V4MAPPED((struct in6_addr *)
2N/A he->h_addr_list[i]))
2N/A continue;
2N/A buff_locp -= sizeof (struct in6_addr);
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A if (buff_locp <=
2N/A (char *)&(host->h_addr_list[count + 1]))
2N/A goto cleanup;
2N/A /* LINTED pointer cast */
2N/A addr4p = (struct in_addr *)buff_locp;
2N/A host->h_addr_list[count] = (char *)addr4p;
2N/A bzero((char *)&addr4p->s_addr,
2N/A sizeof (struct in_addr));
2N/A /* LINTED pointer cast */
2N/A IN6_V4MAPPED_TO_INADDR(
2N/A (struct in6_addr *)he->h_addr_list[i], addr4p);
2N/A ++count;
2N/A }
2N/A /*
2N/A * Set last array element to NULL and add cname as first alias
2N/A */
2N/A host->h_addr_list[count] = NULL;
2N/A host->h_aliases = host->h_addr_list + count + 1;
2N/A count = 0;
2N/A /* Copy official host name */
2N/A buff_locp -= (len = strlen(he->h_name) + 1);
2N/A h_namep = he->h_name;
2N/A if (buff_locp <= (char *)(host->h_aliases))
2N/A goto cleanup;
2N/A bcopy(h_namep, buff_locp, len);
2N/A host->h_name = buff_locp;
2N/A /*
2N/A * Pass 2 (IPv4 aliases):
2N/A */
2N/A if (he->h_aliases != NULL) {
2N/A for (i = 0; he->h_aliases[i] != NULL; i++) {
2N/A buff_locp -= (len = strlen(he->h_aliases[i]) + 1);
2N/A /*
2N/A * Has to be room for the pointer to the address we're
2N/A * about to add, as well as the final NULL ptr.
2N/A */
2N/A if (buff_locp <=
2N/A (char *)&(host->h_aliases[count + 1]))
2N/A goto cleanup;
2N/A host->h_aliases[count] = buff_locp;
2N/A bcopy((char *)he->h_aliases[i], buff_locp, len);
2N/A ++count;
2N/A }
2N/A }
2N/A host->h_aliases[count] = NULL;
2N/A host->h_length = sizeof (struct in_addr);
2N/A host->h_addrtype = AF_INET;
2N/A free(res);
2N/A return (host);
2N/Acleanup:
2N/A *extract_error = NO_RECOVERY;
2N/A (void) __IPv6_cleanup(res);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * This routine takes as input a pointer to a hostent and filters out
2N/A * the type of addresses specified by the af argument. AF_INET
2N/A * indicates that the caller wishes to filter out IPv4-mapped
2N/A * addresses, and AF_INET6 indicates that the caller wishes to filter
2N/A * out IPv6 addresses which aren't IPv4-mapped. If filtering would
2N/A * result in all addresses being filtered out, a NULL pointer is returned.
2N/A * Otherwise, the he pointer passed in is returned, even if no addresses
2N/A * were filtered out.
2N/A */
2N/Astatic struct hostent *
2N/A__filter_addresses(int af, struct hostent *he)
2N/A{
2N/A struct in6_addr **in6addrlist, **in6addr;
2N/A boolean_t isipv4mapped;
2N/A int i = 0;
2N/A
2N/A if (he == NULL)
2N/A return (NULL);
2N/A
2N/A in6addrlist = (struct in6_addr **)he->h_addr_list;
2N/A for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
2N/A isipv4mapped = IN6_IS_ADDR_V4MAPPED(*in6addr);
2N/A
2N/A if ((af == AF_INET && !isipv4mapped) ||
2N/A (af == AF_INET6 && isipv4mapped)) {
2N/A if (in6addrlist[i] != *in6addr)
2N/A in6addrlist[i] = *in6addr;
2N/A i++;
2N/A }
2N/A }
2N/A
2N/A if (i == 0) {
2N/A /* We filtered everything out. */
2N/A return (NULL);
2N/A } else {
2N/A /* NULL terminate the list and return the hostent */
2N/A in6addrlist[i] = NULL;
2N/A return (he);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * This routine searches a hostent for v4 mapped IPv6 addresses.
2N/A * he hostent structure to seach
2N/A * find_both flag indicating if only want mapped or both map'd and v6
2N/A * return values:
2N/A * 0 = No mapped addresses
2N/A * 1 = Mapped v4 address found (returns on first one found)
2N/A * 2 = Both v6 and v4 mapped are present
2N/A *
2N/A * If hostent passed in with no addresses, zero will be returned.
2N/A */
2N/A
2N/Astatic int
2N/A__find_mapped(struct hostent *he, int find_both)
2N/A{
2N/A int i;
2N/A int mapd_found = 0;
2N/A int v6_found = 0;
2N/A
2N/A for (i = 0; he->h_addr_list[i] != NULL; i++) {
2N/A /* LINTED pointer cast */
2N/A if (IN6_IS_ADDR_V4MAPPED(
2N/A (struct in6_addr *)he->h_addr_list[i])) {
2N/A if (find_both)
2N/A mapd_found = 1;
2N/A else
2N/A return (1);
2N/A } else {
2N/A v6_found = 1;
2N/A }
2N/A /* save some iterations once both found */
2N/A if (mapd_found && v6_found)
2N/A return (2);
2N/A }
2N/A return (mapd_found);
2N/A}
2N/A
2N/A/*
2N/A * This routine was added specifically for the IPv6 getipnodeby*() APIs. This
2N/A * separates the result pointer (ptr to hostent+data buf) from the
2N/A * nss_XbyY_buf_t ptr (required for nsswitch API). The returned hostent ptr
2N/A * can be passed to freehostent() and freed independently.
2N/A *
2N/A * bufp->result bufp->buffer
2N/A * | |
2N/A * V V
2N/A * ------------------------------------------------...--
2N/A * |struct hostent |addresses aliases |
2N/A * ------------------------------------------------...--
2N/A * | |<--------bufp->buflen-------------->|
2N/A */
2N/A
2N/A#define ALIGN(x) ((((long)(x)) + sizeof (long) - 1) & ~(sizeof (long) - 1))
2N/A
2N/Astatic nss_XbyY_buf_t *
2N/A__IPv6_alloc(int bufsz)
2N/A{
2N/A nss_XbyY_buf_t *bufp;
2N/A
2N/A if ((bufp = malloc(sizeof (nss_XbyY_buf_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A if ((bufp->result = malloc(ALIGN(sizeof (struct hostent)) + bufsz)) ==
2N/A NULL) {
2N/A free(bufp);
2N/A return (NULL);
2N/A }
2N/A bufp->buffer = (char *)(bufp->result) + sizeof (struct hostent);
2N/A bufp->buflen = bufsz;
2N/A return (bufp);
2N/A}
2N/A
2N/A/*
2N/A * This routine is use only for error return cleanup. This will free the
2N/A * hostent pointer, so don't use for successful returns.
2N/A */
2N/Astatic void
2N/A__IPv6_cleanup(nss_XbyY_buf_t *bufp)
2N/A{
2N/A if (bufp == NULL)
2N/A return;
2N/A if (bufp->result != NULL)
2N/A free(bufp->result);
2N/A free(bufp);
2N/A}