/*
* 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
*/
/*
*/
#include <netdb.h>
#include <nss_dbdefs.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <libintl.h>
/*
* getaddrinfo() returns EAI_NONAME in some cases, however
* since EAI_NONAME is not part of SUSv3 it needed to be
* masked in the standards compliant environment.
* GAIV_DEFAULT and GAIV_XPG6 accomplish this.
*/
#define GAIV_DEFAULT 0
/*
* Storage allocation for global variables in6addr_any and
* in6addr_loopback. The extern declarations for these
* variables could have been defined in any of the "C" files
* in libsocket. They are defined here with other IPv6
* related interfaces.
*/
/* AI_MASK: all valid flags for addrinfo */
#define ANY 0
/* function prototypes for used by getaddrinfo() routine */
static boolean_t str_isnumber(const char *p);
/*
* getaddrinfo:
*
* Purpose:
* Routine for performing Address-to-nodename in a
* protocol-independent fashion.
* Description and history of the routine:
* Nodename-to-address translation is done in a protocol-
* independent fashion using the getaddrinfo() function
* that is taken from the IEEE POSIX 1003.1g.
*
* The official specification for this function will be the
* final POSIX standard, with the following additional
* requirements:
*
* - getaddrinfo() must be thread safe
* - The AI_NUMERICHOST is new.
* - All fields in socket address structures returned by
*
* getaddrinfo() that are not filled in through an explicit
* argument (e.g., sin6_flowinfo and sin_zero) must be set to 0.
* (This makes it easier to compare socket address structures).
*
* Input Parameters:
* nodename - pointer to null-terminated strings that represents
* pointer can be NULL.
* servname - pointer to null-terminated strings that represents
* a servicename or literal port number or this
* pointer can be NULL.
* hints - optional argument that points to an addrinfo structure
* to provide hints on the type of socket that the caller
* supports.
* Possible setting of the ai_flags member of the hints structure:
* AI_PASSIVE - If set, the caller plans to use the returned socket
* address in a call to bind(). In this case, it the
* nodename argument is NULL, then the IP address portion
* of the socket address structure will be set to
* INADDR_ANY for IPv4 or IN6ADDR_ANY_INIT for IPv6.
* AI_PASSIVE - If not set, then the returned socket address will be
* ready for a call to connect() (for conn-oriented) or
* connect(), sendto(), or sendmsg() (for connectionless).
* In this case, if nodename is NULL, then the IP address
* portion of the socket address structure will be set to
* the loopback address.
* AI_CANONNAME - If set, then upon successful return the ai_canonname
* field of the first addrinfo structure in the linked
* list will point to a NULL-terminated string
* containing the canonical name of the specified nodename.
* AI_NUMERICHOST - If set, then a non-NULL nodename string must be a numeric
* host address string. Otherwise an error of EAI_NONAME
* is returned. This flag prevents any type of name
* resolution service from being called.
* AI_NUMERICSERV - If set, then a non-null servname string supplied shall
* be a numeric port string. Otherwise, an [EAI_NONAME]
* error shall be returned. This flag shall prevent any
* type of name resolution service from being invoked.
* AI_V4MAPPED - If set, along with an ai_family of AF_INET6, then
* getaddrinfo() shall return IPv4-mapped IPv6 addresses
* on finding no matching IPv6 addresses ( ai_addrlen shall
* be 16). The AI_V4MAPPED flag shall be ignored unless
* ai_family equals AF_INET6.
* AI_ALL - If the AI_ALL flag is used with the AI_V4MAPPED flag,
* then getaddrinfo() shall return all matching IPv6 and
* IPv4 addresses. The AI_ALL flag without the AI_V4MAPPED
* flag is ignored.
* Output Parameters:
* res - upon successful return a pointer to a linked list of one
* or more addrinfo structures is returned through this
* argument. The caller can process each addrinfo structures
* in this list by following the ai_next pointer, until a
* NULL pointer is encountered. In each returned addrinfo
* structure the three members ai_family, ai_socktype, and
* ai_protocol are corresponding arguments for a call to the
* socket() function. In each addrinfo structure the ai_addr
* field points to filled-in socket address structure whose
* length is specified by the ai_addrlen member.
*
* Return Value:
* This function returns 0 upon success or a nonzero error code. The
* following names are nonzero error codes from getaddrinfo(), and are
* defined in <netdb.h>.
* EAI_ADDRFAMILY - address family not supported
* EAI_AGAIN - DNS temporary failure
* EAI_BADFLAGS - invalid ai_flags
* EAI_FAIL - DNS non-recoverable failure
* EAI_FAMILY - ai_family not supported
* EAI_MEMORY - memory allocation failure
* EAI_NODATA - no address associated with nodename
* EAI_SERVICE - servname not supported for ai_socktype
* EAI_SOCKTYPE - ai_socktype not supported
* EAI_SYSTEM - system error in errno
*
* Memory Allocation:
* All of the information returned by getaddrinfo() is dynamically
* allocated: the addrinfo structures, and the socket address
* structures and canonical node name strings pointed to by the
* addrinfo structures.
*/
static int
{
int error;
aip->ai_socktype = 0;
aip->ai_protocol = 0;
#ifdef __sparcv9
/*
* We need to clear _ai_pad to preserve binary
* compatibility with previously compiled 64-bit
* applications by guaranteeing the upper 32-bits
* are empty.
*/
#endif /* __sparcv9 */
aip->ai_addrlen = 0;
port = 0;
/* if nodename nor servname provided */
return (EAI_NONAME);
}
/* check for bad flags in hints */
return (EAI_BADFLAGS);
}
return (EAI_BADFLAGS);
}
return (EAI_FAMILY);
}
#ifdef __sparcv9
/*
* We need to clear _ai_pad to preserve binary
* compatibility. See prior comment.
*/
#endif /* __sparcv9 */
switch (aip->ai_socktype) {
case ANY:
switch (aip->ai_protocol) {
case ANY:
break;
case IPPROTO_UDP:
break;
case IPPROTO_TCP:
case IPPROTO_SCTP:
break;
default:
break;
}
break;
case SOCK_RAW:
break;
case SOCK_SEQPACKET:
/*
* If the hint does not have a preference on the
* protocol, use SCTP as the default for
* SOCK_SEQPACKET.
*/
break;
case SOCK_DGRAM:
break;
case SOCK_STREAM:
/*
* If the hint does not have a preference on the
* protocol, use TCP as the default for SOCK_STREAM.
*/
break;
default:
return (EAI_SOCKTYPE);
}
}
/*
* Get the service.
*/
switch (aip->ai_socktype) {
case ANY:
break;
case SOCK_DGRAM:
proto = "udp";
break;
case SOCK_STREAM:
/*
* If there is no hint given, use TCP as the default
* protocol.
*/
switch (aip->ai_protocol) {
case ANY:
case IPPROTO_TCP:
default:
proto = "tcp";
break;
case IPPROTO_SCTP:
proto = "sctp";
break;
}
break;
case SOCK_SEQPACKET:
/* Default to SCTP if no hint given. */
switch (aip->ai_protocol) {
case ANY:
default:
proto = "sctp";
break;
}
break;
}
/*
* Servname string can be a decimal port number.
* If we already know the socket type there is no need
* to call getservbyport.
*/
if (!str_isnumber(servname)) {
return (EAI_NONAME);
}
} else if (str_isnumber(servname)) {
do {
bufsize *= 2;
return (EAI_MEMORY);
}
return (EAI_SERVICE);
}
/*
* errno == ERANGE so our scratch buffer space
* wasn't big enough. Double it and try
* again.
*/
}
} else {
do {
bufsize *= 2;
return (EAI_MEMORY);
}
return (EAI_SERVICE);
}
/*
* errno == ERANGE so our scratch buffer space wasn't
* big enough. Double it and try again.
*/
}
/*
* RFC 2553bis doesn't allow us to use the
* any resolver to find out if there is a
* match. We could walk the service file
* with *servent(). Given the commonality of
* calling getaddrinfo() with a number and
* ANY protocol we won't add that at this time.
*/
return (EAI_NONAME);
}
} else {
return (EAI_SYSTEM);
}
}
}
/*
* hostname is NULL
* case 1: AI_PASSIVE bit set : anyaddr 0.0.0.0 or ::
* case 2: AI_PASSIVE bit not set : localhost 127.0.0.1 or ::1
*/
char *canonname;
goto v4only;
/* create IPv6 addrinfo */
goto nomem;
addrlen = sizeof (struct sockaddr_in6);
goto nomem;
}
/* LINTED alignment */
} else {
/* LINTED alignment */
goto nomem;
}
}
}
/* LINTED alignment */
/* LINTED alignment */
goto success;
}
/* If address family is PF_UNSPEC or PF_INET */
/* create IPv4 addrinfo */
goto nomem;
addrlen = sizeof (struct sockaddr_in);
goto nomem;
}
/* LINTED alignment */
} else {
/* LINTED alignment */
goto nomem;
}
}
}
/* LINTED alignment */
/* LINTED alignment */
goto success;
}
/* hostname string is a literal address or an alphabetical name */
if (error) {
return (error);
}
return (0);
return (EAI_MEMORY);
}
int
{
}
int
{
}
static int
{
int i, errnum;
int addrlen;
char *canonname;
/*
* Check for existence of address-zoneid delimiter '%'
* If the delimiter exists, parse the zoneid portion of
* <addr>%<zone_id>
*/
/* make sure we have room for <addr> portion of hostname */
return (EAI_MEMORY);
}
/* chop off and save <zone_id> portion */
++zonestr; /* make zonestr point at start of <zone-id> */
/* ensure zone is valid */
return (EAI_NONAME);
}
} else {
return (EAI_MEMORY);
}
}
/* Check to see if AI_NUMERICHOST bit is set */
/* check to see if _hostname points to a literal IP address */
return (EAI_NONAME);
}
}
/* if hostname argument is literal, name service doesn't get called */
} else {
}
switch (errnum) {
case HOST_NOT_FOUND:
return (EAI_NONAME);
case TRY_AGAIN:
return (EAI_AGAIN);
case NO_RECOVERY:
return (EAI_FAIL);
case NO_ADDRESS:
return (EAI_NONAME);
return (EAI_NODATA);
default:
return (EAI_SYSTEM);
}
}
for (i = 0; hp->h_addr_list[i]; i++) {
/* Determine if an IPv6 addrinfo structure should be created */
/* LINTED alignment */
}
sizeof (struct in_addr));
} else {
return (EAI_SYSTEM);
}
if (create_v6_addrinfo) {
/* create IPv6 addrinfo */
goto nomem;
addrlen = sizeof (struct sockaddr_in6);
goto nomem;
}
/* LINTED alignment */
goto nomem;
}
}
/* LINTED alignment */
/* LINTED alignment */
/* set sin6_scope_id */
/*
* Translate 'zonestr' into a valid
* sin6_scope_id.
*/
if ((errnum =
/* LINTED alignment */
/* LINTED alignment */
return (errnum);
}
} else {
/* LINTED alignment */
}
} else {
/* create IPv4 addrinfo */
goto nomem;
addrlen = sizeof (struct sockaddr_in);
goto nomem;
}
/* LINTED alignment */
goto nomem;
}
}
/* LINTED alignment */
/* LINTED alignment */
}
}
return (0);
return (EAI_MEMORY);
}
/*
* getscopeidfromzone(sa, zone, sin6_scope_id)
*
* Converts the string pointed to by 'zone' into a sin6_scope_id.
* 'zone' will either be a pointer to an interface name or will
* be a literal sin6_scope_id.
*
* 0 is returned on success and the output parameter 'sin6_scope_id' will
* be set to a valid sin6_scope_id.
* EAI_NONAME is returned for either of two reasons:
* 1. The IPv6 address pointed to by sa->sin6_addr is not
* part of the 'link scope' (ie link local, nodelocal multicast or
* linklocal multicast address)
* 2. The string pointed to by 'zone' can not be translate to a valid
* sin6_scope_id.
*/
static uint_t
char *endp;
if (IN6_IS_ADDR_LINKSCOPE(addr)) {
/*
* Look up interface index associated with interface name
* pointed to by 'zone'. Since the address is part of the link
* scope, there is a one-to-one relationship between interface
* index and sin6_scope_id.
* If an interface index can not be found for 'zone', then
* treat 'zone' as a literal sin6_scope_id value.
*/
return (0);
} else {
/* check that entire string was read */
if (*endp != '\0') {
return (EAI_NONAME);
}
} else {
return (EAI_NONAME);
}
}
} else {
return (EAI_NONAME);
}
return (0);
}
void
{
do {
if (ai->ai_canonname)
}
static boolean_t
str_isnumber(const char *p)
{
char *q = (char *)p;
while (*q) {
if (!isdigit(*q))
return (B_FALSE);
q++;
}
return (B_TRUE);
}
static const char *gai_errlist[] = {
"name translation error 0 (no error)", /* 0 */
"specified address family not supported", /* 1 EAI_ADDRFAMILY */
"temporary name resolution failure", /* 2 EAI_AGAIN */
"invalid flags", /* 3 EAI_BADFLAGS */
"non-recoverable name resolution failure", /* 4 EAI_FAIL */
"specified address family not supported", /* 5 EAI_FAMILY */
"memory allocation failure", /* 6 EAI_MEMORY */
"no address for the specified node name", /* 7 EAI_NODATA */
"node name or service name not known", /* 8 EAI_NONAME */
"service name not available for the specified socket type",
/* 9 EAI_SERVICE */
"specified socket type not supported", /* 10 EAI_SOCKTYPE */
"system error", /* 11 EAI_SYSTEM */
};
const char *
{
if (ecode < 0)
return (dgettext(TEXT_DOMAIN,
"name translation internal error"));
}