diff options
Diffstat (limited to 'usr/src/lib/libsocket/inet/getaddrinfo.c')
| -rw-r--r-- | usr/src/lib/libsocket/inet/getaddrinfo.c | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/usr/src/lib/libsocket/inet/getaddrinfo.c b/usr/src/lib/libsocket/inet/getaddrinfo.c new file mode 100644 index 0000000000..9d978a7496 --- /dev/null +++ b/usr/src/lib/libsocket/inet/getaddrinfo.c @@ -0,0 +1,806 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + * or http://www.opensolaris.org/os/licensing. + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +#include <netdb.h> +#include <arpa/inet.h> +#include <nss_dbdefs.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <stdlib.h> +#include <net/if.h> + +extern char *_dgettext(const char *, const char *); + +#define ai2sin(x) ((struct sockaddr_in *)((x)->ai_addr)) +#define ai2sin6(x) ((struct sockaddr_in6 *)((x)->ai_addr)) + +#define HOST_BROADCAST "255.255.255.255" + +/* + * 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 +#define GAIV_XPG6 1 + +/* + * Storage allocation for global variables in6addr_any and + * in6addr_loopback. The extern declarations for these + * variables are defined in <netinet/in.h>. These two + * variables could have been defined in any of the "C" files + * in libsocket. They are defined here with other IPv6 + * related interfaces. + */ +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; + +/* AI_MASK: all valid flags for addrinfo */ +#define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST \ + | AI_ADDRCONFIG | AI_NUMERICSERV | AI_V4MAPPED | AI_ALL) +#define ANY 0 +/* function prototypes for used by getaddrinfo() routine */ +static int get_addr(int family, const char *hostname, struct addrinfo *aip, + struct addrinfo *cur, ushort_t port, int version); +static uint_t getscopeidfromzone(const struct sockaddr_in6 *sa, + const char *zone, uint32_t *sin6_scope_id); +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 + * a hostname or literal ip address (IPv4/IPv6) or this + * 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_NONAME - host/servname not known + * 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 +_getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res, int version) +{ + struct addrinfo *cur; + struct addrinfo *aip; + struct addrinfo ai; + int error; + ushort_t port; + + cur = &ai; + aip = &ai; + + aip->ai_flags = 0; + aip->ai_family = PF_UNSPEC; + 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. + */ + aip->_ai_pad = 0; +#endif /* __sparcv9 */ + aip->ai_addrlen = 0; + aip->ai_canonname = NULL; + aip->ai_addr = NULL; + aip->ai_next = NULL; + port = 0; + + /* if nodename nor servname provided */ + if (hostname == NULL && servname == NULL) { + *res = NULL; + return (EAI_NONAME); + } + if (hints != NULL) { + /* check for bad flags in hints */ + if ((hints->ai_flags != 0) && (hints->ai_flags & ~AI_MASK)) { + *res = NULL; + return (EAI_BADFLAGS); + } + if ((hostname == NULL || *hostname == '\0') && + (hints->ai_flags & AI_CANONNAME)) { + *res = NULL; + return (EAI_BADFLAGS); + } + if (hints->ai_family != PF_UNSPEC && + hints->ai_family != PF_INET && + hints->ai_family != PF_INET6) { + *res = NULL; + return (EAI_FAMILY); + } + + (void) memcpy(aip, hints, sizeof (*aip)); +#ifdef __sparcv9 + /* + * We need to clear _ai_pad to preserve binary + * compatibility. See prior comment. + */ + aip->_ai_pad = 0; +#endif /* __sparcv9 */ + switch (aip->ai_socktype) { + case ANY: + switch (aip->ai_protocol) { + case ANY: + break; + case IPPROTO_UDP: + aip->ai_socktype = SOCK_DGRAM; + break; + case IPPROTO_TCP: + aip->ai_socktype = SOCK_STREAM; + break; + default: + aip->ai_socktype = SOCK_RAW; + break; + } + break; + case SOCK_RAW: + break; + case SOCK_DGRAM: + aip->ai_protocol = IPPROTO_UDP; + break; + case SOCK_STREAM: + aip->ai_protocol = IPPROTO_TCP; + break; + default: + *res = NULL; + return (EAI_SOCKTYPE); + } + } + + /* + * Get the service. + */ + + if (servname != NULL) { + struct servent result; + int bufsize = 128; + char *buf = NULL; + struct servent *sp; + char *proto = NULL; + + switch (aip->ai_socktype) { + case ANY: + proto = NULL; + break; + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_STREAM: + proto = "tcp"; + break; + } + /* + * Servname string can be a decimal port number. + * If we already know the socket type there is no need + * to call getservbyport. + */ + if (aip->ai_flags & AI_NUMERICSERV) { + if (!str_isnumber(servname)) { + return (EAI_NONAME); + } + port = htons(atoi(servname)); + } else if (str_isnumber(servname)) { + port = htons(atoi(servname)); + if (aip->ai_socktype == ANY) { + do { + if (buf != NULL) + free(buf); + bufsize *= 2; + buf = malloc(bufsize); + if (buf == NULL) { + *res = NULL; + return (EAI_MEMORY); + } + + sp = getservbyport_r(port, proto, + &result, buf, bufsize); + if (sp == NULL && errno != ERANGE) { + free(buf); + *res = NULL; + return (EAI_SERVICE); + } + /* + * errno == ERANGE so our scratch buffer space + * wasn't big enough. Double it and try + * again. + */ + } while (sp == NULL); + } + } else { + do { + if (buf != NULL) + free(buf); + bufsize *= 2; + buf = malloc(bufsize); + if (buf == NULL) { + *res = NULL; + return (EAI_MEMORY); + } + + sp = getservbyname_r(servname, proto, &result, + buf, bufsize); + if (sp == NULL && errno != ERANGE) { + free(buf); + *res = NULL; + return (EAI_SERVICE); + } + /* + * errno == ERANGE so our scratch buffer space wasn't + * big enough. Double it and try again. + */ + } while (sp == NULL); + + port = sp->s_port; + } + if (aip->ai_socktype == ANY) { + if (aip->ai_flags & AI_NUMERICSERV) { + /* + * 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); + } + + if (strcmp(sp->s_proto, "udp") == 0) { + aip->ai_socktype = SOCK_DGRAM; + aip->ai_protocol = IPPROTO_UDP; + } else if (strcmp(sp->s_proto, "tcp") == 0) { + aip->ai_socktype = SOCK_STREAM; + aip->ai_protocol = IPPROTO_TCP; + } else { + if (buf != NULL) + free(buf); + + *res = NULL; + errno = EPROTONOSUPPORT; + return (EAI_SYSTEM); + } + } + + if (buf != NULL) + free(buf); + } + + /* + * 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 + */ + + if (hostname == NULL) { + struct addrinfo *nai; + socklen_t addrlen; + char *canonname; + + if (aip->ai_family == PF_INET) + goto v4only; + /* create IPv6 addrinfo */ + nai = malloc(sizeof (struct addrinfo)); + if (nai == NULL) + goto nomem; + *nai = *aip; + addrlen = sizeof (struct sockaddr_in6); + nai->ai_addr = malloc(addrlen); + if (nai->ai_addr == NULL) { + freeaddrinfo(nai); + goto nomem; + } + bzero(nai->ai_addr, addrlen); + nai->ai_addrlen = addrlen; + nai->ai_family = PF_INET6; + nai->ai_protocol = 0; + nai->ai_canonname = NULL; + if (nai->ai_flags & AI_PASSIVE) { + ai2sin6(nai)->sin6_addr = in6addr_any; + } else { + ai2sin6(nai)->sin6_addr = in6addr_loopback; + if (nai->ai_flags & AI_CANONNAME) { + canonname = strdup("loopback"); + if (canonname == NULL) { + freeaddrinfo(nai); + goto nomem; + } + nai->ai_canonname = canonname; + } + } + ai2sin6(nai)->sin6_family = PF_INET6; + ai2sin6(nai)->sin6_port = port; + cur->ai_next = nai; + cur = nai; + if (aip->ai_family == PF_INET6) { + cur->ai_next = NULL; + goto success; + } + /* If address family is PF_UNSPEC or PF_INET */ +v4only: + /* create IPv4 addrinfo */ + nai = malloc(sizeof (struct addrinfo)); + if (nai == NULL) + goto nomem; + *nai = *aip; + addrlen = sizeof (struct sockaddr_in); + nai->ai_addr = malloc(addrlen); + if (nai->ai_addr == NULL) { + freeaddrinfo(nai); + goto nomem; + } + bzero(nai->ai_addr, addrlen); + nai->ai_addrlen = addrlen; + nai->ai_family = PF_INET; + nai->ai_protocol = 0; + nai->ai_canonname = NULL; + if (nai->ai_flags & AI_PASSIVE) { + ai2sin(nai)->sin_addr.s_addr = INADDR_ANY; + } else { + ai2sin(nai)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (nai->ai_flags & AI_CANONNAME && + nai->ai_family != PF_UNSPEC) { + canonname = strdup("loopback"); + if (canonname == NULL) { + freeaddrinfo(nai); + goto nomem; + } + nai->ai_canonname = canonname; + } + } + ai2sin(nai)->sin_family = PF_INET; + ai2sin(nai)->sin_port = port; + cur->ai_next = nai; + cur = nai; + cur->ai_next = NULL; + goto success; + } + + /* hostname string is a literal address or an alphabetical name */ + error = get_addr(aip->ai_family, hostname, aip, cur, port, version); + if (error) { + *res = NULL; + return (error); + } + +success: + *res = aip->ai_next; + return (0); + +nomem: + return (EAI_MEMORY); +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + return (_getaddrinfo(hostname, servname, hints, res, GAIV_DEFAULT)); +} + +int +__xnet_getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + return (_getaddrinfo(hostname, servname, hints, res, GAIV_XPG6)); +} + +static int +get_addr(int family, const char *hostname, struct addrinfo *aip, struct + addrinfo *cur, ushort_t port, int version) +{ + struct hostent *hp; + char _hostname[MAXHOSTNAMELEN]; + int i, errnum; + struct addrinfo *nai; + int addrlen; + char *canonname; + boolean_t firsttime = B_TRUE; + boolean_t create_v6_addrinfo; + struct in_addr v4addr; + struct in6_addr v6addr; + struct in6_addr *v6addrp; + char *zonestr = NULL; + + /* + * Check for existence of address-zoneid delimiter '%' + * If the delimiter exists, parse the zoneid portion of + * <addr>%<zone_id> + */ + if ((zonestr = strchr(hostname, '%')) != NULL) { + /* make sure we have room for <addr> portion of hostname */ + if (((zonestr - hostname) + 1) > sizeof (_hostname)) { + return (EAI_MEMORY); + } + + /* chop off and save <zone_id> portion */ + (void) strlcpy(_hostname, hostname, (zonestr - hostname) + 1); + ++zonestr; /* make zonestr point at start of <zone-id> */ + /* ensure zone is valid */ + if ((*zonestr == '\0') || (strlen(zonestr) > LIFNAMSIZ)) { + return (EAI_NONAME); + } + } else { + size_t hlen = sizeof (_hostname); + + if (strlcpy(_hostname, hostname, hlen) >= hlen) { + return (EAI_MEMORY); + } + } + + /* Check to see if AI_NUMERICHOST bit is set */ + if (aip->ai_flags & AI_NUMERICHOST) { + /* check to see if _hostname points to a literal IP address */ + if (!((inet_addr(_hostname) != ((in_addr_t)-1)) || + (strcmp(_hostname, HOST_BROADCAST) == 0) || + (inet_pton(AF_INET6, _hostname, &v6addr) > 0))) { + return (EAI_NONAME); + } + } + + /* if hostname argument is literal, name service doesn't get called */ + if (family == PF_UNSPEC) { + hp = getipnodebyname(_hostname, AF_INET6, AI_ALL | + aip->ai_flags | AI_V4MAPPED, &errnum); + } else { + hp = getipnodebyname(_hostname, family, aip->ai_flags, &errnum); + } + + if (hp == NULL) { + switch (errnum) { + case HOST_NOT_FOUND: + return (EAI_NONAME); + case TRY_AGAIN: + return (EAI_AGAIN); + case NO_RECOVERY: + return (EAI_FAIL); + case NO_ADDRESS: + if (version == GAIV_XPG6) + 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 */ + create_v6_addrinfo = B_TRUE; + if (hp->h_addrtype == AF_INET6) { + v6addrp = (struct in6_addr *)hp->h_addr_list[i]; + if (!(aip->ai_flags & AI_V4MAPPED) && + IN6_IS_ADDR_V4MAPPED(v6addrp)) { + create_v6_addrinfo = B_FALSE; + IN6_V4MAPPED_TO_INADDR(v6addrp, &v4addr); + } + } else if (hp->h_addrtype == AF_INET) { + create_v6_addrinfo = B_FALSE; + (void) memcpy(&v4addr, hp->h_addr_list[i], + sizeof (struct in_addr)); + } else { + return (EAI_SYSTEM); + } + + if (create_v6_addrinfo) { + /* create IPv6 addrinfo */ + nai = malloc(sizeof (struct addrinfo)); + if (nai == NULL) + goto nomem; + *nai = *aip; + addrlen = sizeof (struct sockaddr_in6); + nai->ai_addr = malloc(addrlen); + if (nai->ai_addr == NULL) { + freeaddrinfo(nai); + goto nomem; + } + bzero(nai->ai_addr, addrlen); + nai->ai_addrlen = addrlen; + nai->ai_family = PF_INET6; + nai->ai_protocol = 0; + + (void) memcpy(ai2sin6(nai)->sin6_addr.s6_addr, + hp->h_addr_list[i], sizeof (struct in6_addr)); + nai->ai_canonname = NULL; + if ((nai->ai_flags & AI_CANONNAME) && firsttime) { + canonname = strdup(hp->h_name); + if (canonname == NULL) { + freeaddrinfo(nai); + goto nomem; + } + nai->ai_canonname = canonname; + firsttime = B_FALSE; + } + ai2sin6(nai)->sin6_family = PF_INET6; + ai2sin6(nai)->sin6_port = port; + /* set sin6_scope_id */ + if (zonestr != NULL) { + /* + * Translate 'zonestr' into a valid + * sin6_scope_id. + */ + if ((errnum = + getscopeidfromzone(ai2sin6(nai), zonestr, + &ai2sin6(nai)->sin6_scope_id)) != 0) { + return (errnum); + } + } else { + ai2sin6(nai)->sin6_scope_id = 0; + } + } else { + /* create IPv4 addrinfo */ + nai = malloc(sizeof (struct addrinfo)); + if (nai == NULL) + goto nomem; + *nai = *aip; + addrlen = sizeof (struct sockaddr_in); + nai->ai_addr = malloc(addrlen); + if (nai->ai_addr == NULL) { + freeaddrinfo(nai); + goto nomem; + } + bzero(nai->ai_addr, addrlen); + nai->ai_addrlen = addrlen; + nai->ai_family = PF_INET; + nai->ai_protocol = 0; + (void) memcpy(&(ai2sin(nai)->sin_addr.s_addr), + &v4addr, sizeof (struct in_addr)); + nai->ai_canonname = NULL; + if (nai->ai_flags & AI_CANONNAME && firsttime) { + canonname = strdup(hp->h_name); + if (canonname == NULL) { + freeaddrinfo(nai); + goto nomem; + } + nai->ai_canonname = canonname; + firsttime = B_FALSE; + } + ai2sin(nai)->sin_family = PF_INET; + ai2sin(nai)->sin_port = port; + } + + cur->ai_next = nai; + cur = nai; + } + cur->ai_next = NULL; + freehostent(hp); + return (0); + +nomem: + freehostent(hp); + 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 +getscopeidfromzone(const struct sockaddr_in6 *sa, const char *zone, + uint32_t *sin6_scope_id) { + const in6_addr_t *addr = &sa->sin6_addr; + ulong_t ul_scope_id; + 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. + */ + if ((*sin6_scope_id = if_nametoindex(zone)) != 0) { + return (0); + } else { + if ((ul_scope_id = strtoul(zone, &endp, 10)) != 0) { + /* check that entire string was read */ + if (*endp != '\0') { + return (EAI_NONAME); + } + *sin6_scope_id = + (uint32_t)(ul_scope_id & 0xffffffffUL); + } else { + return (EAI_NONAME); + } + } + } else { + return (EAI_NONAME); + } + return (0); +} + + +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + do { + next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + if (ai->ai_addr) + free(ai->ai_addr); + free(ai); + ai = next; + } while (ai != NULL); +} + +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 */ +}; +static int gai_nerr = { sizeof (gai_errlist)/sizeof (gai_errlist[0]) }; + +const char * +gai_strerror(int ecode) +{ + if (ecode < 0) + return (_dgettext(TEXT_DOMAIN, + "name translation internal error")); + else if (ecode < gai_nerr) + return (_dgettext(TEXT_DOMAIN, gai_errlist[ecode])); + return (_dgettext(TEXT_DOMAIN, "unknown name translation error")); +} |
