| /* |
| * Copyright (c) 2001, 02 Motoyuki Kasahara |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the project nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| /* |
| * This program provides getaddrinfo() and getnameinfo() described in |
| * RFC2133, 2553 and 3493. These functions are mainly used for IPv6 |
| * application to resolve hostname or address. |
| * |
| * This program is designed to be working on traditional IPv4 systems |
| * which don't have those functions. Therefore, this implementation |
| * supports IPv4 only. |
| * |
| * This program is useful for application which should support both IPv6 |
| * and traditional IPv4 systems. Use genuine getaddrinfo() and getnameinfo() |
| * provided by system if the system supports IPv6. Otherwise, use this |
| * implementation. |
| * |
| * This program is intended to be used in combination with GNU Autoconf. |
| * |
| * This program also provides freeaddrinfo() and gai_strerror(). |
| * |
| * To use this program in your application, insert the following lines to |
| * C source files after including `sys/types.h', `sys/socket.h' and |
| * `netdb.h'. `getaddrinfo.h' defines `struct addrinfo' and AI_, NI_, |
| * EAI_ macros. |
| * |
| * #ifndef HAVE_GETADDRINFO |
| * #include "getaddrinfo.h" |
| * #endif |
| * |
| * Restriction: |
| * getaddrinfo() and getnameinfo() of this program are NOT thread |
| * safe, unless the cpp macro ENABLE_PTHREAD is defined. |
| */ |
| |
| /* |
| * Add the following code to your configure.ac (or configure.in). |
| * AC_C_CONST |
| * AC_HEADER_STDC |
| * AC_CHECK_HEADERS(string.h memory.h stdlib.h) |
| * AC_CHECK_FUNCS(memcpy) |
| * AC_REPLACE_FUNCS(memset) |
| * AC_TYPE_SOCKLEN_T |
| * AC_TYPE_IN_PORT_T |
| * AC_DECL_H_ERRNO |
| * |
| * AC_CHECK_FUNCS(getaddrinfo getnameinfo) |
| * if test "$ac_cv_func_getaddrinfo$ac_cv_func_getnameinfo" != yesyes ; then |
| * LIBOBJS="$LIBOBJS getaddrinfo.$ac_objext" |
| * fi |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <sys/types.h> |
| #include <stdio.h> |
| |
| #ifdef WIN32 |
| #include <time.h> |
| #include <winsock2.h> |
| #ifdef DO_IPV6 |
| #include <ws2tcpip.h> |
| #endif /* DO_IPV6 */ |
| #include <windows.h> |
| #else |
| #include <sys/socket.h> |
| #endif |
| |
| |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| |
| #if defined(STDC_HEADERS) || defined(HAVE_STRING_H) |
| #include <string.h> |
| #if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) |
| #include <memory.h> |
| #endif /* not STDC_HEADERS and HAVE_MEMORY_H */ |
| #else /* not STDC_HEADERS and not HAVE_STRING_H */ |
| #include <strings.h> |
| #endif /* not STDC_HEADERS and not HAVE_STRING_H */ |
| |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| |
| #ifdef ENABLE_PTHREAD |
| #include <pthread.h> |
| #endif |
| |
| #ifdef ENABLE_NLS |
| #include <libintl.h> |
| #endif |
| |
| #ifndef HAVE_MEMCPY |
| #define memcpy(d, s, n) bcopy((s), (d), (n)) |
| #ifdef __STDC__ |
| void *memchr(const void *, int, size_t); |
| int memcmp(const void *, const void *, size_t); |
| void *memmove(void *, const void *, size_t); |
| void *memset(void *, int, size_t); |
| #else /* not __STDC__ */ |
| char *memchr(); |
| int memcmp(); |
| char *memmove(); |
| char *memset(); |
| #endif /* not __STDC__ */ |
| #endif /* not HAVE_MEMCPY */ |
| |
| #ifndef H_ERRNO_DECLARED |
| extern int h_errno; |
| #endif |
| |
| #include "getaddrinfo.h" |
| |
| #ifdef ENABLE_NLS |
| #define _(string) gettext(string) |
| #ifdef gettext_noop |
| #define N_(string) gettext_noop(string) |
| #else |
| #define N_(string) (string) |
| #endif |
| #else |
| #define gettext(string) (string) |
| #define _(string) (string) |
| #define N_(string) (string) |
| #endif |
| |
| /* |
| * Error messages for gai_strerror(). |
| */ |
| static char *eai_errlist[] = { |
| N_("Success"), |
| |
| /* EAI_ADDRFAMILY */ |
| N_("Address family for hostname not supported"), |
| |
| /* EAI_AGAIN */ |
| N_("Temporary failure in name resolution"), |
| |
| /* EAI_BADFLAGS */ |
| N_("Invalid value for ai_flags"), |
| |
| /* EAI_FAIL */ |
| N_("Non-recoverable failure in name resolution"), |
| |
| /* EAI_FAMILY */ |
| N_("ai_family not supported"), |
| |
| /* EAI_MEMORY */ |
| N_("Memory allocation failure"), |
| |
| /* EAI_NONAME */ |
| N_("hostname nor servname provided, or not known"), |
| |
| /* EAI_OVERFLOW */ |
| N_("An argument buffer overflowed"), |
| |
| /* EAI_SERVICE */ |
| N_("servname not supported for ai_socktype"), |
| |
| /* EAI_SOCKTYPE */ |
| N_("ai_socktype not supported"), |
| |
| /* EAI_SYSTEM */ |
| N_("System error returned in errno") |
| }; |
| |
| /* |
| * Default hints for getaddrinfo(). |
| */ |
| static struct addrinfo default_hints = { |
| 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL |
| }; |
| |
| /* |
| * Mutex. |
| */ |
| #ifdef ENABLE_PTHREAD |
| static pthread_mutex_t gai_mutex = PTHREAD_MUTEX_INITIALIZER; |
| #endif |
| |
| /* |
| * Declaration of static functions. |
| */ |
| #ifdef __STDC__ |
| static int is_integer(const char *); |
| static int is_address(const char *); |
| static int itoa_length(int); |
| #else |
| static int is_integer(); |
| static int is_address(); |
| static int itoa_length(); |
| #endif |
| |
| /* |
| * gai_strerror(). |
| */ |
| const char * |
| gai_strerror(ecode) |
| int ecode; |
| { |
| if (ecode < 0 || ecode > EAI_SYSTEM) |
| return _("Unknown error"); |
| |
| return gettext(eai_errlist[ecode]); |
| } |
| |
| /* |
| * freeaddrinfo(). |
| */ |
| void |
| freeaddrinfo(ai) |
| struct addrinfo *ai; |
| { |
| struct addrinfo *next_ai; |
| |
| while (ai != NULL) { |
| if (ai->ai_canonname != NULL) |
| free(ai->ai_canonname); |
| if (ai->ai_addr != NULL) |
| free(ai->ai_addr); |
| next_ai = ai->ai_next; |
| free(ai); |
| ai = next_ai; |
| } |
| } |
| |
| /* |
| * Return 1 if the string `s' represents an integer. |
| */ |
| static int |
| is_integer(s) |
| const char *s; |
| { |
| if (*s == '-' || *s == '+') |
| s++; |
| if (*s < '0' || '9' < *s) |
| return 0; |
| |
| s++; |
| while ('0' <= *s && *s <= '9') |
| s++; |
| |
| return (*s == '\0'); |
| } |
| |
| /* |
| * Return 1 if the string `s' represents an IPv4 address. |
| * Unlike inet_addr(), it doesn't permit malformed nortation such |
| * as "192.168". |
| */ |
| static int |
| is_address(s) |
| const char *s; |
| { |
| const static char delimiters[] = {'.', '.', '.', '\0'}; |
| int i, j; |
| int octet; |
| |
| for (i = 0; i < 4; i++) { |
| if (*s == '0' && *(s + 1) != delimiters[i]) |
| return 0; |
| for (j = 0, octet = 0; '0' <= *s && *s <= '9' && j < 3; s++, j++) |
| octet = octet * 10 + (*s - '0'); |
| if (j == 0 || octet > 255 || *s != delimiters[i]) |
| return 0; |
| s++; |
| } |
| |
| return 1; |
| } |
| |
| /* |
| * Calcurate length of the string `s', where `s' is set by |
| * sprintf(s, "%d", n). |
| */ |
| static int |
| itoa_length(n) |
| int n; |
| { |
| int result = 1; |
| |
| if (n < 0) { |
| n = -n; |
| result++; |
| } |
| |
| while (n >= 10) { |
| result++; |
| n /= 10; |
| } |
| |
| return result; |
| } |
| |
| /* |
| * getaddrinfo(). |
| */ |
| int |
| getaddrinfo(nodename, servname, hints, res) |
| const char *nodename; |
| const char *servname; |
| const struct addrinfo *hints; |
| struct addrinfo **res; |
| { |
| struct addrinfo *head_res = NULL; |
| struct addrinfo *tail_res = NULL; |
| struct addrinfo *new_res; |
| struct sockaddr_in *sa_in; |
| struct in_addr **addr_list; |
| struct in_addr *addr_list_buf[2]; |
| struct in_addr addr_buf; |
| struct in_addr **ap; |
| struct servent *servent; |
| struct hostent *hostent; |
| const char *canonname = NULL; |
| in_port_t port; |
| int saved_h_errno; |
| int result = 0; |
| |
| #ifdef ENABLE_PTHREAD |
| pthread_mutex_lock(&gai_mutex); |
| #endif |
| |
| saved_h_errno = h_errno; |
| |
| if (nodename == NULL && servname == NULL) { |
| result = EAI_NONAME; |
| goto end; |
| } |
| |
| if (hints != NULL) { |
| if (hints->ai_family != PF_INET && hints->ai_family != PF_UNSPEC) { |
| result = EAI_FAMILY; |
| goto end; |
| } |
| if (hints->ai_socktype != SOCK_DGRAM |
| && hints->ai_socktype != SOCK_STREAM |
| && hints->ai_socktype != 0) { |
| result = EAI_SOCKTYPE; |
| goto end; |
| } |
| } else { |
| hints = &default_hints; |
| } |
| |
| if (servname != NULL) { |
| if (is_integer(servname)) |
| port = htons(atoi(servname)); |
| else { |
| if (hints->ai_flags & AI_NUMERICSERV) { |
| result = EAI_NONAME; |
| goto end; |
| } |
| |
| if (hints->ai_socktype == SOCK_DGRAM) |
| servent = getservbyname(servname, "udp"); |
| else if (hints->ai_socktype == SOCK_STREAM) |
| servent = getservbyname(servname, "tcp"); |
| else if (hints->ai_socktype == 0) |
| servent = getservbyname(servname, "tcp"); |
| else { |
| result = EAI_SOCKTYPE; |
| goto end; |
| } |
| |
| if (servent == NULL) { |
| result = EAI_SERVICE; |
| goto end; |
| } |
| port = servent->s_port; |
| } |
| } else { |
| port = htons(0); |
| } |
| |
| if (nodename != NULL) { |
| if (is_address(nodename)) { |
| addr_buf.s_addr = inet_addr(nodename); |
| addr_list_buf[0] = &addr_buf; |
| addr_list_buf[1] = NULL; |
| addr_list = addr_list_buf; |
| |
| if (hints->ai_flags & AI_CANONNAME |
| && !(hints->ai_flags & AI_NUMERICHOST)) { |
| hostent = gethostbyaddr((char *)&addr_buf, |
| sizeof(struct in_addr), AF_INET); |
| if (hostent != NULL) |
| canonname = hostent->h_name; |
| else |
| canonname = nodename; |
| } |
| } else { |
| if (hints->ai_flags & AI_NUMERICHOST) { |
| result = EAI_NONAME; |
| goto end; |
| } |
| |
| hostent = gethostbyname(nodename); |
| if (hostent == NULL) { |
| switch (h_errno) { |
| case HOST_NOT_FOUND: |
| case NO_DATA: |
| result = EAI_NONAME; |
| goto end; |
| case TRY_AGAIN: |
| result = EAI_AGAIN; |
| goto end; |
| default: |
| result = EAI_FAIL; |
| goto end; |
| } |
| } |
| addr_list = (struct in_addr **)hostent->h_addr_list; |
| |
| if (hints->ai_flags & AI_CANONNAME) |
| canonname = hostent->h_name; |
| } |
| } else { |
| if (hints->ai_flags & AI_PASSIVE) |
| addr_buf.s_addr = htonl(INADDR_ANY); |
| else |
| addr_buf.s_addr = htonl(0x7F000001); |
| addr_list_buf[0] = &addr_buf; |
| addr_list_buf[1] = NULL; |
| addr_list = addr_list_buf; |
| } |
| |
| for (ap = addr_list; *ap != NULL; ap++) { |
| new_res = (struct addrinfo *)malloc(sizeof(struct addrinfo)); |
| if (new_res == NULL) { |
| if (head_res != NULL) |
| freeaddrinfo(head_res); |
| result = EAI_MEMORY; |
| goto end; |
| } |
| |
| new_res->ai_family = PF_INET; |
| new_res->ai_socktype = hints->ai_socktype; |
| new_res->ai_protocol = hints->ai_protocol; |
| new_res->ai_addr = NULL; |
| new_res->ai_addrlen = sizeof(struct sockaddr_in); |
| new_res->ai_canonname = NULL; |
| new_res->ai_next = NULL; |
| |
| new_res->ai_addr = (struct sockaddr *) |
| malloc(sizeof(struct sockaddr_in)); |
| if (new_res->ai_addr == NULL) { |
| free(new_res); |
| if (head_res != NULL) |
| freeaddrinfo(head_res); |
| result = EAI_MEMORY; |
| goto end; |
| } |
| |
| sa_in = (struct sockaddr_in *)new_res->ai_addr; |
| memset(sa_in, 0, sizeof(struct sockaddr_in)); |
| sa_in->sin_family = PF_INET; |
| sa_in->sin_port = port; |
| memcpy(&sa_in->sin_addr, *ap, sizeof(struct in_addr)); |
| |
| if (head_res == NULL) |
| head_res = new_res; |
| else |
| tail_res->ai_next = new_res; |
| tail_res = new_res; |
| } |
| |
| if (canonname != NULL && head_res != NULL) { |
| head_res->ai_canonname = (char *)malloc(strlen(canonname) + 1); |
| if (head_res->ai_canonname != NULL) |
| strcpy(head_res->ai_canonname, canonname); |
| } |
| |
| *res = head_res; |
| |
| end: |
| h_errno = saved_h_errno; |
| #ifdef ENABLE_PTHREAD |
| pthread_mutex_unlock(&gai_mutex); |
| #endif |
| return result; |
| } |
| |
| /* |
| * getnameinfo(). |
| */ |
| int |
| getnameinfo(sa, salen, node, nodelen, serv, servlen, flags) |
| const struct sockaddr *sa; |
| socklen_t salen; |
| char *node; |
| socklen_t nodelen; |
| char *serv; |
| socklen_t servlen; |
| int flags; |
| { |
| const struct sockaddr_in *sa_in = (const struct sockaddr_in *)sa; |
| struct hostent *hostent; |
| struct servent *servent; |
| char *ntoa_address; |
| int saved_h_errno; |
| int result = 0; |
| |
| #ifdef ENABLE_PTHREAD |
| pthread_mutex_lock(&gai_mutex); |
| #endif |
| |
| saved_h_errno = h_errno; |
| |
| if (sa_in->sin_family != PF_INET) { |
| result = EAI_FAMILY; |
| goto end; |
| } else if (node == NULL && serv == NULL) { |
| result = EAI_NONAME; |
| goto end; |
| } |
| |
| if (serv != NULL && servlen > 0) { |
| if (flags & NI_NUMERICSERV) |
| servent = NULL; |
| else if (flags & NI_DGRAM) |
| servent = getservbyport(sa_in->sin_port, "udp"); |
| else |
| servent = getservbyport(sa_in->sin_port, "tcp"); |
| |
| if (servent != NULL) { |
| if (servlen <= strlen(servent->s_name)) { |
| result = EAI_OVERFLOW; |
| goto end; |
| } |
| strcpy(serv, servent->s_name); |
| } else { |
| if (servlen <= itoa_length(ntohs(sa_in->sin_port))) { |
| result = EAI_OVERFLOW; |
| goto end; |
| } |
| sprintf(serv, "%d", ntohs(sa_in->sin_port)); |
| } |
| } |
| |
| if (node != NULL && nodelen > 0) { |
| if (flags & NI_NUMERICHOST) |
| hostent = NULL; |
| else { |
| hostent = gethostbyaddr((char *)&sa_in->sin_addr, |
| sizeof(struct in_addr), AF_INET); |
| } |
| if (hostent != NULL) { |
| if (nodelen <= strlen(hostent->h_name)) { |
| result = EAI_OVERFLOW; |
| goto end; |
| } |
| strcpy(node, hostent->h_name); |
| } else { |
| if (flags & NI_NAMEREQD) { |
| result = EAI_NONAME; |
| goto end; |
| } |
| ntoa_address = inet_ntoa(sa_in->sin_addr); |
| if (nodelen <= strlen(ntoa_address)) { |
| result = EAI_OVERFLOW; |
| goto end; |
| } |
| strcpy(node, ntoa_address); |
| } |
| |
| } |
| |
| end: |
| h_errno = saved_h_errno; |
| #ifdef ENABLE_PTHREAD |
| pthread_mutex_unlock(&gai_mutex); |
| #endif |
| return result; |
| } |
| |