getipnodeby.c revision 20d217c8569fadc52e6956aa7fcc78efd8d1f1b5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* This file defines and implements the re-entrant getipnodebyname(),
* getipnodebyaddr(), and freehostent() routines for IPv6. These routines
* follow use the netdir_getbyYY() (see netdir_inet.c).
*
*/
#include "mt.h"
#include <stdlib.h>
#include <unistd.h>
#include <stropts.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <netdb.h>
#include <stdio.h>
#include <nss_dbdefs.h>
#include <nss_netdir.h>
#include <netdir.h>
#include <thread.h>
#include <synch.h>
#include <fcntl.h>
#include "nss.h"
#define IPV6_LITERAL_CHAR ':'
/*
* The number of nanoseconds getipnodebyname() waits before getting
* fresh interface count information with SIOCGLIFNUM. The default is
* five minutes.
*/
/*
* Bits in the bitfield returned by getipnodebyname_processflags().
*
* IPNODE_WANTIPV6 The user wants IPv6 addresses returned.
* IPNODE_WANTIPV4 The user wants IPv4 addresses returned.
* IPNODE_IPV4IFNOIPV6 The user only wants IPv4 addresses returned if no IPv6
* addresses are returned.
* IPNODE_LOOKUPIPNODES getipnodebyname() needs to lookup the name in ipnodes.
* IPNODE_LOOKUPHOSTS getipnodebyname() needs to lookup the name in hosts.
* IPNODE_ISLITERAL The name supplied is a literal address string.
*/
#define IPNODE_WANTIPV6 0x00000001u
#define IPNODE_WANTIPV4 0x00000002u
#define IPNODE_IPV4IFNOIPV6 0x00000004u
#define IPNODE_LOOKUPIPNODES 0x00000008u
#define IPNODE_LOOKUPHOSTS 0x00000010u
#define IPNODE_LITERAL 0x00000020u
/*
* The default set of bits corresponding to a getipnodebyname() flags
* argument of AI_DEFAULT.
*/
extern struct netconfig *__rpc_getconfip(char *);
nss_XbyY_buf_t *, int);
static int __find_mapped(struct hostent *, int);
static nss_XbyY_buf_t *__IPv6_alloc(int);
static void __IPv6_cleanup(nss_XbyY_buf_t *);
static int __ai_addrconfig(int);
#ifdef PIC
struct hostent *
{
}
struct hostent *
{
return (NULL);
}
#endif
/*
* Given a name, an address family, and a set of flags, return a
* bitfield that getipnodebyname() will use.
*/
static uint_t
{
/*
* If AI_ADDRCONFIG is specified, we need to determine the number
* of addresses of each address family configured on the system as
* appropriate.
*/
if (flags & AI_ADDRCONFIG) {
__ai_addrconfig(AF_INET6) > 0);
__ai_addrconfig(AF_INET) > 0);
}
/*
* Determine what kinds of addresses the user is interested
* in getting back.
*/
switch (af) {
case AF_INET6:
if (flags & AI_V4MAPPED) {
ipnode_bits &= ~IPNODE_IPV4;
}
} else {
ipnode_bits &= ~IPNODE_IPV4;
}
break;
case AF_INET:
ipnode_bits &= ~IPNODE_IPV4;
break;
default:
ipnode_bits = 0;
break;
}
/*
* If we're not looking for IPv4 addresses, don't bother looking
* in hosts.
*/
if (!(ipnode_bits & IPNODE_WANTIPV4))
/*
* Determine if name is a literal IP address. This will
* further narrow down what type of lookup we're going to do.
*/
/* Literal IPv6 address */
/*
* In s9 we accepted the literal without filtering independent
* of what family was passed in hints. We continue to do
* this.
*/
/* Literal IPv4 address */
}
return (ipnode_bits);
}
struct hostent *
{
struct nss_netdirbyname_in nssin;
union nss_netdirbyname_out nssout;
int ret;
*error_num = NO_RECOVERY;
return (NULL);
}
/* Make sure we have something to look up. */
goto cleanup;
}
/*
* Perform the requested lookups. We always look through
* ipnodes first for both IPv4 and IPv6 addresses. Depending
* on what was returned and what was needed, we either filter
* out the garbage, or ask for more using hosts.
*/
if (ipnode_bits & IPNODE_LOOKUPIPNODES) {
*error_num = NO_RECOVERY;
goto cleanup;
}
} else if (ipnode_bits & IPNODE_WANTIPV4) {
/*
* buf6 may have all that we need if we either
* only wanted IPv4 addresses if there were no
* IPv6 addresses returned, or if there are
* IPv4-mapped addresses in buf6. If either
* of these are true, then there's no need to
* look in hosts.
*/
if (ipnode_bits & IPNODE_IPV4IFNOIPV6 ||
} else if (!(ipnode_bits & IPNODE_WANTIPV6)) {
/*
* If all we're looking for are IPv4
* addresses and there are none in
* buf6 then buf6 is now useless.
*/
}
}
}
if (ipnode_bits & IPNODE_LOOKUPHOSTS) {
*error_num = NO_RECOVERY;
goto cleanup;
}
}
}
goto cleanup;
}
/* Extract the appropriate addresses from the returned buffer(s). */
switch (af) {
case AF_INET6: {
/*
* The IPv4 results we have need to be
* converted to IPv4-mapped addresses,
* conditionally merged with the IPv6
* results, and the end result needs to be
* re-ordered.
*/
*error_num = NO_RECOVERY;
goto cleanup;
}
mergebuf, 1);
else
*error_num = NO_RECOVERY;
}
/*
* We have what we need in buf6, but we may need
* to filter out some addresses depending on what
* is being asked for.
*/
if (!(ipnode_bits & IPNODE_WANTIPV4))
else if (!(ipnode_bits & IPNODE_WANTIPV6))
*error_num = NO_ADDRESS;
}
break;
}
case AF_INET:
/* We could have results in buf6 or buf4, not both */
/*
* Extract the IPv4-mapped addresses from buf6
* into hp.
*/
} else {
/* We have what we need in buf4. */
if (ipnode_bits & IPNODE_LITERAL) {
/*
* There is a special case here for literal
* IPv4 address strings. The hosts
* front-end sets h_aliases to a one
* element array containing a single NULL
* pointer (in ndaddr2hent()), while
* getipnodebyname() requires h_aliases to
* be a NULL pointer itself. We're not
* going to change the front-end since it
* needs to remain backward compatible for
* gethostbyname() and friends. Just set
* h_aliases to NULL here instead.
*/
}
}
break;
default:
break;
}
/*
* Free the memory we allocated, but make sure we don't free
* the memory we're returning to the caller.
*/
}
}
(void) freenetconfigent(nconf);
return (hp);
}
/*
* This is the IPv6 interface for "gethostbyaddr".
*/
struct hostent *
{
nss_XbyY_buf_t *buf = 0;
nss_XbyY_buf_t *res = 0;
struct nss_netdirbyaddr_in nssin;
union nss_netdirbyaddr_out nssout;
int neterr;
char tmpbuf[64];
return (NULL);
}
return (NULL);
}
} else {
return (NULL);
}
/*
* Specific case: query for "::"
*/
return (NULL);
}
/*
* Step 1: IPv4-mapped address or IPv4 Compat
*/
((IN6_IS_ADDR_V4MAPPED(addr6)) ||
(IN6_IS_ADDR_V4COMPAT(addr6)))) {
*error_num = NO_RECOVERY;
return (NULL);
}
*error_num = NO_RECOVERY;
return (NULL);
}
if (IN6_IS_ADDR_V4COMPAT(addr6)) {
} else {
}
/*
* We pass in nconf and let the implementation of the
* long-named func decide whether to use the switch based on
* nc_nlookups.
*/
neterr =
(void) freenetconfigent(nconf);
/* Failover case, try hosts db for v4 address */
return (NULL);
}
*error_num = NO_RECOVERY;
return (NULL);
}
return (hp);
}
/*
* At this point, we'll have a v4mapped hostent. If that's
* what was passed in, just return. If the request was a compat,
* twiggle the two bytes to make the mapped address a compat.
*/
if (IN6_IS_ADDR_V4COMPAT(addr6)) {
/* LINTED pointer cast */
}
return (hp);
}
/*
* Step 2: AF_INET, v4 lookup. Since we're going to search the
* ipnodes (v6) path first, we need to treat this as a v4mapped
* address. nscd(1m) caches v4 from ipnodes as mapped v6's. The
* switch backend knows to lookup v4's (not v4mapped) from the
* name services.
*/
*error_num = NO_RECOVERY;
return (NULL);
}
*error_num = NO_RECOVERY;
return (NULL);
}
/*
* We pass in nconf and let the implementation of the
* long-named func decide whether to use the switch based on
* nc_nlookups.
*/
neterr =
(void) freenetconfigent(nconf);
/* Failover case, try hosts db for v4 address */
return (NULL);
}
return (hp);
}
return (NULL);
}
return (hp);
}
/*
* Step 3: AF_INET6, plain vanilla v6 getipnodebyaddr() call.
*/
*error_num = NO_RECOVERY;
return (NULL);
}
*error_num = NO_RECOVERY;
return (NULL);
}
/*
* We pass in nconf and let the implementation of the
* long-named func decide whether to use the switch based on
* nc_nlookups.
*/
neterr =
(void) freenetconfigent(nconf);
return (NULL);
}
}
/*
* If we got here, unknown type.
*/
return (NULL);
}
void
{
}
static int
__ai_addrconfig(int af)
{
int *num;
switch (af) {
case AF_INET:
break;
case AF_INET6:
break;
default:
return (0);
}
/*
* We don't need to check this every time someone does a name
* lookup. Do it every IFNUM_TIMEOUT for each address family.
*
* There's no need to protect all of this with a lock. The
* worst that can happen is that we update the interface count
* twice instead of once. That's no big deal.
*/
/*
* We want to determine if this machine knows anything
* at all about the address family; the status of the
* interface is less important. Hence, set
* 'lifn_flags' to zero.
*/
lifn.lifn_flags = 0;
goto fail;
if (lifn.lifn_count == 0) {
*num = 0;
return (*num);
}
/*
* Pad the interface count to detect when additional
* interfaces have been configured between SIOCGLIFNUM
* and SIOCGLIFCONF.
*/
goto fail;
lifc.lifc_flags = 0;
goto fail;
goto again;
/*
* Do not include any loopback addresses, 127.0.0.1 for AF_INET
* and ::1 for AF_INET6, while counting the number of available
* IPv4 or IPv6 addresses. (RFC 3493 requires this, whenever
* AI_ADDRCONFIG flag is set)
*/
switch (af) {
case AF_INET: {
struct sockaddr_in *in;
count++;
}
break;
}
case AF_INET6: {
struct sockaddr_in6 *in6;
count++;
break;
}
}
}
}
return (*num);
fail:
return (-1);
}
/*
* This routine will either convert an IPv4 address to a mapped or compat
* IPv6 (if he6 == NULL) or merge IPv6 (he6) addresses with mapped
* v4 (he4) addresses. In either case, the results are returned in res.
* Caller must provide all buffers.
* Inputs:
* he4 pointer to IPv4 buffer
* res pointer to results buffer
* mapped mapped == 1, map IPv4 : mapped == 0, compat IPv4
* mapped flag is ignored if he6 != NULL
*
* The results are packed into the res->buffer as follows:
* <--------------- buffer + buflen -------------------------------------->
* |-----------------|-----------------|----------------|----------------|
* | pointers vector | pointers vector | aliases grow | addresses grow |
* | for addresses | for aliases | | |
* | this way -> | this way -> | <- this way |<- this way |
* |-----------------|-----------------|----------------|----------------|
* | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1|
*/
static struct hostent *
int mapped)
{
char *buff_locp;
char *h_namep;
return (NULL);
}
return (NULL);
}
/*
* If he6==NULL, map the v4 address into the v6 address format.
* This is used for getipnodebyaddr() (single address, mapped or
* compatible) or for v4 mapped for getipnodebyname(), which
* could be multiple addresses. This could also be a literal
* address string, which is why there is a inet_addr() call.
*/
if (buff_locp <=
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
return (NULL);
}
/* LINTED pointer cast */
if (mapped) {
}
++count;
}
/*
* Set last array element to NULL and add cname as first alias
*/
count = 0;
/*
* Literal address string, since we're mapping, we need the IPv6
* V4 mapped literal address string for h_name.
*/
char tmpstr[128];
sizeof (tmpstr));
return (NULL);
return (host); /* we're done, return result */
}
/*
* Not a literal address string, so just copy h_name.
*/
return (NULL);
/*
* Pass 2 (IPv4 aliases):
*/
if (buff_locp <=
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
return (NULL);
}
++count;
}
return (host);
} else {
/*
* Merge IPv4 mapped addresses with IPv6 addresses. The
* IPv6 address will go in first, followed by the v4 mapped.
*
* Pass 1 (IPv6 addresses):
*/
if (buff_locp <=
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
return (NULL);
}
sizeof (struct in6_addr));
++count;
}
/*
* Pass 1 (IPv4 mapped addresses):
*/
if (buff_locp <=
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
return (NULL);
}
/* LINTED pointer cast */
sizeof (struct in_addr));
++count;
}
/*
* Pass 2 (IPv6 aliases, host name first). We start h_aliases
* one after where h_addr_list array ended. This is where cname
* is put, followed by all aliases. Reset count to 0, for index
* in the h_aliases array.
*/
count = 0;
return (NULL);
if (buff_locp <=
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
return (NULL);
}
++count;
}
/*
* Pass 2 (IPv4 aliases):
*/
if (buff_locp <=
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
return (NULL);
}
++count;
}
return (host);
}
}
/*
* This routine will convert a mapped v4 hostent (AF_INET6) to a
* AF_INET hostent. If no mapped addrs found, then a NULL is returned.
* If mapped addrs found, then a new buffer is alloc'd and all the v4 mapped
* addresses are extracted and copied to it. On sucess, a pointer to a new
* hostent is returned.
* There are two possible errors in which case a NULL is returned.
* One of two error codes are returned:
*
* NO_RECOVERY - a malloc failed or the like for which there's no recovery.
* NO_ADDRESS - after filtering all the v4, there was nothing left!
*
* Inputs:
* he pointer to hostent with mapped v4 addresses
* filter_error pointer to return error code
* Return:
* pointer to a malloc'd hostent with v4 addresses.
*
* The results are packed into the res->buffer as follows:
* <--------------- buffer + buflen -------------------------------------->
* |-----------------|-----------------|----------------|----------------|
* | pointers vector | pointers vector | aliases grow | addresses grow |
* | for addresses | for aliases | | |
* | this way -> | this way -> | <- this way |<- this way |
* |-----------------|-----------------|----------------|----------------|
* | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1|
*/
struct hostent *
{
int buflen = NSS_BUFLEN_HOSTS;
char *buff_locp;
char *h_namep;
return (NULL);
}
if ((__find_mapped(he, 0)) == 0) {
return (NULL);
}
return (NULL);
}
goto cleanup;
/*
* "Unmap" the v4 mapped address(es) into a v4 hostent format.
* This is used for getipnodebyaddr() (single address) or for
* v4 mapped for getipnodebyname(), which could be multiple
* addresses. This could also be a literal address string,
* which is why there is a inet_addr() call.
*/
/* LINTED pointer cast */
if (!IN6_IS_ADDR_V4MAPPED((struct in6_addr *)
he->h_addr_list[i]))
continue;
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
if (buff_locp <=
goto cleanup;
/* LINTED pointer cast */
sizeof (struct in_addr));
/* LINTED pointer cast */
++count;
}
/*
* Set last array element to NULL and add cname as first alias
*/
count = 0;
/* Copy official host name */
goto cleanup;
/*
* Pass 2 (IPv4 aliases):
*/
/*
* Has to be room for the pointer to the address we're
* about to add, as well as the final NULL ptr.
*/
if (buff_locp <=
goto cleanup;
++count;
}
}
return (host);
(void) __IPv6_cleanup(res);
return (NULL);
}
/*
* This routine takes as input a pointer to a hostent and filters out
* the type of addresses specified by the af argument. AF_INET
* indicates that the caller wishes to filter out IPv4-mapped
* addresses, and AF_INET6 indicates that the caller wishes to filter
* out IPv6 addresses which aren't IPv4-mapped. If filtering would
* result in all addresses being filtered out, a NULL pointer is returned.
* Otherwise, the he pointer passed in is returned, even if no addresses
* were filtered out.
*/
static struct hostent *
{
int i = 0;
return (NULL);
if (in6addrlist[i] != *in6addr)
in6addrlist[i] = *in6addr;
i++;
}
}
if (i == 0) {
/* We filtered everything out. */
return (NULL);
} else {
/* NULL terminate the list and return the hostent */
in6addrlist[i] = NULL;
return (he);
}
}
/*
* This routine searches a hostent for v4 mapped IPv6 addresses.
* he hostent structure to seach
* find_both flag indicating if only want mapped or both map'd and v6
* return values:
* 0 = No mapped addresses
* 1 = Mapped v4 address found (returns on first one found)
* 2 = Both v6 and v4 mapped are present
*
* If hostent passed in with no addresses, zero will be returned.
*/
static int
{
int i;
int mapd_found = 0;
int v6_found = 0;
/* LINTED pointer cast */
if (IN6_IS_ADDR_V4MAPPED(
if (find_both)
mapd_found = 1;
else
return (1);
} else {
v6_found = 1;
}
/* save some iterations once both found */
if (mapd_found && v6_found)
return (2);
}
return (mapd_found);
}
/*
* This routine was added specifically for the IPv6 getipnodeby*() APIs. This
* separates the result pointer (ptr to hostent+data buf) from the
* nss_XbyY_buf_t ptr (required for nsswitch API). The returned hostent ptr
* can be passed to freehostent() and freed independently.
*
* bufp->result bufp->buffer
* | |
* V V
* ------------------------------------------------...--
* |struct hostent |addresses aliases |
* ------------------------------------------------...--
* | |<--------bufp->buflen-------------->|
*/
static nss_XbyY_buf_t *
__IPv6_alloc(int bufsz)
{
return (NULL);
NULL) {
return (NULL);
}
return (bufp);
}
/*
* This routine is use only for error return cleanup. This will free the
* hostent pointer, so don't use for successful returns.
*/
static void
{
return;
}