/* * 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 * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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 . 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 /* * This is a private, undocumented, flag that getaddrinfo() uses for * getipnodebyname(). In the case of AI_ADDRCONFIG && AI_V4MAPPED, if there are * no IPv6 addresses, getaddrinfo() should return non-IPv4 mapped addresses. On * the flip side, getipnodebyname() is defined by RFC 2553 to explicitly do so. * Therefore this private flag indicates to getaddrinfo that we shouldn't do * this. */ #define AI_ADDRINFO 0x8000 /* 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 . * 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: case IPPROTO_SCTP: aip->ai_socktype = SOCK_STREAM; break; default: aip->ai_socktype = SOCK_RAW; 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. */ if (aip->ai_protocol == ANY) aip->ai_protocol = IPPROTO_SCTP; break; case SOCK_DGRAM: aip->ai_protocol = IPPROTO_UDP; break; case SOCK_STREAM: /* * If the hint does not have a preference on the * protocol, use TCP as the default for SOCK_STREAM. */ if (aip->ai_protocol == ANY) 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: /* * 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 (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 (strcmp(sp->s_proto, "sctp") == 0) { aip->ai_socktype = SOCK_STREAM; aip->ai_protocol = IPPROTO_SCTP; } 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_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_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 * % */ if ((zonestr = strchr(hostname, '%')) != NULL) { /* make sure we have room for portion of hostname */ if (((zonestr - hostname) + 1) > sizeof (_hostname)) { return (EAI_MEMORY); } /* chop off and save portion */ (void) strlcpy(_hostname, hostname, (zonestr - hostname) + 1); ++zonestr; /* make zonestr point at start of */ /* 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 | AI_ADDRINFO, &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; (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; (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")); }