| /* Copyright (C) 2007-2008 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| */ |
| #ifdef __linux__ /* Recent versions of glibc only define EAI_NODATA, which is an |
| extension to the POSIX standard, if _GNU_SOURCE is defined. */ |
| # define _GNU_SOURCE 1 |
| #endif |
| |
| #include "sockets.h" |
| #include <fcntl.h> |
| #include <stddef.h> |
| #include "qemu_debug.h" |
| #include "qemu-char.h" |
| #include <stdlib.h> |
| #include <string.h> |
| #include "android/utils/path.h" |
| #include "android/utils/debug.h" |
| #include "android/utils/misc.h" |
| #include "android/utils/system.h" |
| |
| #define D(...) VERBOSE_PRINT(socket,__VA_ARGS__) |
| |
| #ifdef _WIN32 |
| # define xxWIN32_LEAN_AND_MEAN |
| # include <windows.h> |
| # include <winsock2.h> |
| # include <ws2tcpip.h> |
| #else /* !_WIN32 */ |
| # include <sys/ioctl.h> |
| # include <sys/socket.h> |
| # include <netinet/in.h> |
| # include <netinet/tcp.h> |
| # include <netdb.h> |
| # if HAVE_UNIX_SOCKETS |
| # include <sys/un.h> |
| # ifndef UNIX_PATH_MAX |
| # define UNIX_PATH_MAX (sizeof(((struct sockaddr_un*)0)->sun_path)-1) |
| # endif |
| # endif |
| #endif /* !_WIN32 */ |
| |
| |
| |
| /* QSOCKET_CALL is used to deal with the fact that EINTR happens pretty |
| * easily in QEMU since we use SIGALRM to implement periodic timers |
| */ |
| #ifdef _WIN32 |
| # define QSOCKET_CALL(_ret,_cmd) \ |
| do { _ret = (_cmd); } while ( _ret < 0 && WSAGetLastError() == WSAEINTR ) |
| #else |
| # define QSOCKET_CALL(_ret,_cmd) \ |
| do { \ |
| errno = 0; \ |
| do { _ret = (_cmd); } while ( _ret < 0 && errno == EINTR ); \ |
| } while (0); |
| #endif |
| |
| #ifdef _WIN32 |
| |
| #include <errno.h> |
| |
| static int winsock_error; |
| |
| #define WINSOCK_ERRORS_LIST \ |
| EE(WSA_INVALID_HANDLE,EINVAL,"invalid handle") \ |
| EE(WSA_NOT_ENOUGH_MEMORY,ENOMEM,"not enough memory") \ |
| EE(WSA_INVALID_PARAMETER,EINVAL,"invalid parameter") \ |
| EE(WSAEINTR,EINTR,"interrupted function call") \ |
| EE(WSAEALREADY,EALREADY,"operation already in progress") \ |
| EE(WSAEBADF,EBADF,"bad file descriptor") \ |
| EE(WSAEACCES,EACCES,"permission denied") \ |
| EE(WSAEFAULT,EFAULT,"bad address") \ |
| EE(WSAEINVAL,EINVAL,"invalid argument") \ |
| EE(WSAEMFILE,EMFILE,"too many opened files") \ |
| EE(WSAEWOULDBLOCK,EWOULDBLOCK,"resource temporarily unavailable") \ |
| EE(WSAEINPROGRESS,EINPROGRESS,"operation now in progress") \ |
| EE(WSAEALREADY,EAGAIN,"operation already in progress") \ |
| EE(WSAENOTSOCK,EBADF,"socket operation not on socket") \ |
| EE(WSAEDESTADDRREQ,EDESTADDRREQ,"destination address required") \ |
| EE(WSAEMSGSIZE,EMSGSIZE,"message too long") \ |
| EE(WSAEPROTOTYPE,EPROTOTYPE,"wrong protocol type for socket") \ |
| EE(WSAENOPROTOOPT,ENOPROTOOPT,"bad protocol option") \ |
| EE(WSAEADDRINUSE,EADDRINUSE,"address already in use") \ |
| EE(WSAEADDRNOTAVAIL,EADDRNOTAVAIL,"cannot assign requested address") \ |
| EE(WSAENETDOWN,ENETDOWN,"network is down") \ |
| EE(WSAENETUNREACH,ENETUNREACH,"network unreachable") \ |
| EE(WSAENETRESET,ENETRESET,"network dropped connection on reset") \ |
| EE(WSAECONNABORTED,ECONNABORTED,"software caused connection abort") \ |
| EE(WSAECONNRESET,ECONNRESET,"connection reset by peer") \ |
| EE(WSAENOBUFS,ENOBUFS,"no buffer space available") \ |
| EE(WSAEISCONN,EISCONN,"socket is already connected") \ |
| EE(WSAENOTCONN,ENOTCONN,"socket is not connected") \ |
| EE(WSAESHUTDOWN,ESHUTDOWN,"cannot send after socket shutdown") \ |
| EE(WSAETOOMANYREFS,ETOOMANYREFS,"too many references") \ |
| EE(WSAETIMEDOUT,ETIMEDOUT,"connection timed out") \ |
| EE(WSAECONNREFUSED,ECONNREFUSED,"connection refused") \ |
| EE(WSAELOOP,ELOOP,"cannot translate name") \ |
| EE(WSAENAMETOOLONG,ENAMETOOLONG,"name too long") \ |
| EE(WSAEHOSTDOWN,EHOSTDOWN,"host is down") \ |
| EE(WSAEHOSTUNREACH,EHOSTUNREACH,"no route to host") \ |
| |
| typedef struct { |
| int winsock; |
| int unix; |
| const char* string; |
| } WinsockError; |
| |
| static const WinsockError _winsock_errors[] = { |
| #define EE(w,u,s) { w, u, s }, |
| WINSOCK_ERRORS_LIST |
| #undef EE |
| { -1, -1, NULL } |
| }; |
| |
| /* this function reads the latest winsock error code and updates |
| * errno to a matching value. It also returns the new value of |
| * errno. |
| */ |
| static int |
| fix_errno( void ) |
| { |
| const WinsockError* werr = _winsock_errors; |
| int unix = EINVAL; /* generic error code */ |
| |
| winsock_error = WSAGetLastError(); |
| |
| for ( ; werr->string != NULL; werr++ ) { |
| if (werr->winsock == winsock_error) { |
| unix = werr->unix; |
| break; |
| } |
| } |
| errno = unix; |
| return -1; |
| } |
| |
| static int |
| set_errno( int code ) |
| { |
| winsock_error = -1; |
| errno = code; |
| return -1; |
| } |
| |
| /* this function returns a string describing the latest Winsock error */ |
| const char* |
| _errno_str(void) |
| { |
| const WinsockError* werr = _winsock_errors; |
| const char* result = NULL; |
| |
| for ( ; werr->string; werr++ ) { |
| if (werr->winsock == winsock_error) { |
| result = werr->string; |
| break; |
| } |
| } |
| |
| if (result == NULL) { |
| result = tempstr_format( |
| "Unkown socket error (Winsock=0x%08x) errno=%d: %s", |
| winsock_error, errno, strerror(errno)); |
| } |
| return result; |
| } |
| #else |
| static int |
| fix_errno( void ) |
| { |
| return -1; |
| } |
| |
| static int |
| set_errno( int code ) |
| { |
| errno = code; |
| return -1; |
| } |
| #endif |
| |
| /* socket types */ |
| |
| static int |
| socket_family_to_bsd( SocketFamily family ) |
| { |
| switch (family) { |
| case SOCKET_INET: return AF_INET; |
| case SOCKET_IN6: return AF_INET6; |
| #if HAVE_UNIX_SOCKETS |
| case SOCKET_UNIX: return AF_LOCAL; |
| #endif |
| default: return -1; |
| } |
| } |
| |
| static int |
| socket_type_to_bsd( SocketType type ) |
| { |
| switch (type) { |
| case SOCKET_DGRAM: return SOCK_DGRAM; |
| case SOCKET_STREAM: return SOCK_STREAM; |
| default: return 0; |
| } |
| } |
| |
| static SocketType |
| socket_type_from_bsd( int type ) |
| { |
| switch (type) { |
| case SOCK_DGRAM: return SOCKET_DGRAM; |
| case SOCK_STREAM: return SOCKET_STREAM; |
| default: return (SocketType) SOCKET_UNSPEC; |
| } |
| } |
| |
| #if 0 |
| static int |
| socket_type_check( SocketType type ) |
| { |
| return (type == SOCKET_DGRAM || type == SOCKET_STREAM); |
| } |
| #endif |
| |
| typedef union { |
| struct sockaddr sa[1]; |
| struct sockaddr_in in[1]; |
| #if HAVE_IN6_SOCKETS |
| struct sockaddr_in6 in6[1]; |
| #endif |
| #if HAVE_UNIX_SOCKETS |
| struct sockaddr_un un[1]; |
| #endif |
| } sockaddr_storage; |
| |
| /* socket addresses */ |
| |
| void |
| sock_address_init_inet( SockAddress* a, uint32_t ip, uint16_t port ) |
| { |
| a->family = SOCKET_INET; |
| a->u.inet.port = port; |
| a->u.inet.address = ip; |
| } |
| |
| void |
| sock_address_init_in6 ( SockAddress* a, const uint8_t* ip6[16], uint16_t port ) |
| { |
| a->family = SOCKET_IN6; |
| a->u.in6.port = port; |
| memcpy( a->u.in6.address, ip6, sizeof(a->u.in6.address) ); |
| } |
| |
| void |
| sock_address_init_unix( SockAddress* a, const char* path ) |
| { |
| a->family = SOCKET_UNIX; |
| a->u._unix.path = strdup(path ? path : ""); |
| a->u._unix.owner = 1; |
| } |
| |
| void sock_address_done( SockAddress* a ) |
| { |
| if (a->family == SOCKET_UNIX && a->u._unix.owner) { |
| a->u._unix.owner = 0; |
| free((char*)a->u._unix.path); |
| } |
| } |
| |
| static char* |
| format_char( char* buf, char* end, int c ) |
| { |
| if (buf < end) { |
| if (buf+1 == end) { |
| *buf++ = 0; |
| } else { |
| *buf++ = (char) c; |
| *buf = 0; |
| } |
| } |
| return buf; |
| } |
| |
| static char* |
| format_str( char* buf, char* end, const char* str ) |
| { |
| int len = strlen(str); |
| int avail = end - buf; |
| |
| if (len > avail) |
| len = avail; |
| |
| memcpy( buf, str, len ); |
| buf += len; |
| |
| if (buf == end) |
| buf[-1] = 0; |
| else |
| buf[0] = 0; |
| |
| return buf; |
| } |
| |
| static char* |
| format_unsigned( char* buf, char* end, unsigned val ) |
| { |
| char temp[16]; |
| int nn; |
| |
| for ( nn = 0; val != 0; nn++ ) { |
| int rem = val % 10; |
| temp[nn] = '0'+rem; |
| val /= 10; |
| } |
| |
| if (nn == 0) |
| temp[nn++] = '0'; |
| |
| while (nn > 0) |
| buf = format_char(buf, end, temp[--nn]); |
| |
| return buf; |
| } |
| |
| static char* |
| format_hex( char* buf, char* end, unsigned val, int ndigits ) |
| { |
| int shift = 4*ndigits; |
| static const char hex[16] = "0123456789abcdef"; |
| |
| while (shift >= 0) { |
| buf = format_char(buf, end, hex[(val >> shift) & 15]); |
| shift -= 4; |
| } |
| return buf; |
| } |
| |
| static char* |
| format_ip4( char* buf, char* end, uint32_t ip ) |
| { |
| buf = format_unsigned( buf, end, (unsigned)(ip >> 24) ); |
| buf = format_char( buf, end, '.'); |
| buf = format_unsigned( buf, end, (unsigned)((ip >> 16) & 255)); |
| buf = format_char( buf, end, '.'); |
| buf = format_unsigned( buf, end, (unsigned)((ip >> 8) & 255)); |
| buf = format_char( buf, end, '.'); |
| buf = format_unsigned( buf, end, (unsigned)(ip & 255)); |
| return buf; |
| } |
| |
| static char* |
| format_ip6( char* buf, char* end, const uint8_t* ip6 ) |
| { |
| int nn; |
| for (nn = 0; nn < 8; nn++) { |
| int val = (ip6[0] << 16) | ip6[1]; |
| ip6 += 2; |
| if (nn > 0) |
| buf = format_char(buf, end, ':'); |
| if (val == 0) |
| continue; |
| buf = format_hex(buf, end, val, 4); |
| } |
| return buf; |
| } |
| |
| const char* |
| sock_address_to_string( const SockAddress* a ) |
| { |
| static char buf0[MAX_PATH]; |
| char *buf = buf0, *end = buf + sizeof(buf0); |
| |
| switch (a->family) { |
| case SOCKET_INET: |
| buf = format_ip4( buf, end, a->u.inet.address ); |
| buf = format_char( buf, end, ':' ); |
| buf = format_unsigned( buf, end, (unsigned) a->u.inet.port ); |
| break; |
| |
| case SOCKET_IN6: |
| buf = format_ip6( buf, end, a->u.in6.address ); |
| buf = format_char( buf, end, ':' ); |
| buf = format_unsigned( buf, end, (unsigned) a->u.in6.port ); |
| break; |
| |
| case SOCKET_UNIX: |
| buf = format_str( buf, end, a->u._unix.path ); |
| break; |
| |
| default: |
| return NULL; |
| } |
| |
| return buf0; |
| } |
| |
| int |
| sock_address_equal( const SockAddress* a, const SockAddress* b ) |
| { |
| if (a->family != b->family) |
| return 0; |
| |
| switch (a->family) { |
| case SOCKET_INET: |
| return (a->u.inet.address == b->u.inet.address && |
| a->u.inet.port == b->u.inet.port); |
| |
| case SOCKET_IN6: |
| return (!memcmp(a->u.in6.address, b->u.in6.address, 16) && |
| a->u.in6.port == b->u.in6.port); |
| |
| case SOCKET_UNIX: |
| return (!strcmp(a->u._unix.path, b->u._unix.path)); |
| |
| default: |
| return 0; |
| } |
| } |
| |
| int |
| sock_address_get_port( const SockAddress* a ) |
| { |
| switch (a->family) { |
| case SOCKET_INET: |
| return a->u.inet.port; |
| case SOCKET_IN6: |
| return a->u.in6.port; |
| default: |
| return -1; |
| } |
| } |
| |
| void |
| sock_address_set_port( SockAddress* a, uint16_t port ) |
| { |
| switch (a->family) { |
| case SOCKET_INET: |
| a->u.inet.port = port; |
| break; |
| case SOCKET_IN6: |
| a->u.in6.port = port; |
| break; |
| default: |
| ; |
| } |
| } |
| |
| const char* |
| sock_address_get_path( const SockAddress* a ) |
| { |
| if (a->family == SOCKET_UNIX) |
| return a->u._unix.path; |
| else |
| return NULL; |
| } |
| |
| int |
| sock_address_get_ip( const SockAddress* a ) |
| { |
| if (a->family == SOCKET_INET) |
| return a->u.inet.address; |
| |
| return -1; |
| } |
| |
| #if 0 |
| char* |
| bufprint_sock_address( char* p, char* end, const SockAddress* a ) |
| { |
| switch (a->family) { |
| case SOCKET_INET: |
| { |
| uint32_t ip = a->u.inet.address; |
| |
| return bufprint( p, end, "%d.%d.%d.%d:%d", |
| (ip >> 24) & 255, (ip >> 16) & 255, |
| (ip >> 8) & 255, ip & 255, |
| a->u.inet.port ); |
| } |
| case SOCKET_IN6: |
| { |
| int nn = 0; |
| const char* column = ""; |
| const uint8_t* tab = a->u.in6.address; |
| for (nn = 0; nn < 16; nn += 2) { |
| p = bufprint(p, end, "%s%04x", column, (tab[n] << 8) | tab[n+1]); |
| column = ":"; |
| } |
| return bufprint(p, end, ":%d", a->u.in6.port); |
| } |
| case SOCKET_UNIX: |
| { |
| return bufprint(p, end, "%s", a->u._unix.path); |
| } |
| default: |
| return p; |
| } |
| } |
| #endif |
| |
| static int |
| sock_address_to_bsd( const SockAddress* a, sockaddr_storage* paddress, socklen_t *psize ) |
| { |
| switch (a->family) { |
| case SOCKET_INET: |
| { |
| struct sockaddr_in* dst = paddress->in; |
| |
| *psize = sizeof(*dst); |
| |
| memset( paddress, 0, *psize ); |
| |
| dst->sin_family = AF_INET; |
| dst->sin_port = htons(a->u.inet.port); |
| dst->sin_addr.s_addr = htonl(a->u.inet.address); |
| } |
| break; |
| |
| #if HAVE_IN6_SOCKETS |
| case SOCKET_IN6: |
| { |
| struct sockaddr_in6* dst = paddress->in6; |
| |
| *psize = sizeof(*dst); |
| |
| memset( paddress, 0, *psize ); |
| |
| dst->sin6_family = AF_INET6; |
| dst->sin6_port = htons(a->u.in6.port); |
| memcpy( dst->sin6_addr.s6_addr, a->u.in6.address, 16 ); |
| } |
| break; |
| #endif /* HAVE_IN6_SOCKETS */ |
| |
| #if HAVE_UNIX_SOCKETS |
| case SOCKET_UNIX: |
| { |
| int slen = strlen(a->u._unix.path); |
| struct sockaddr_un* dst = paddress->un; |
| |
| if (slen >= UNIX_PATH_MAX) |
| return -1; |
| |
| memset( dst, 0, sizeof(*dst) ); |
| |
| dst->sun_family = AF_LOCAL; |
| memcpy( dst->sun_path, a->u._unix.path, slen ); |
| dst->sun_path[slen] = 0; |
| |
| *psize = (char*)&dst->sun_path[slen+1] - (char*)dst; |
| } |
| break; |
| #endif /* HAVE_UNIX_SOCKETS */ |
| |
| default: |
| return set_errno(EINVAL); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| sock_address_from_bsd( SockAddress* a, const void* from, size_t fromlen ) |
| { |
| switch (((struct sockaddr *)from)->sa_family) { |
| case AF_INET: |
| { |
| const struct sockaddr_in* src = from; |
| |
| if (fromlen < sizeof(*src)) |
| return set_errno(EINVAL); |
| |
| a->family = SOCKET_INET; |
| a->u.inet.port = ntohs(src->sin_port); |
| a->u.inet.address = ntohl(src->sin_addr.s_addr); |
| } |
| break; |
| |
| #ifdef HAVE_IN6_SOCKETS |
| case AF_INET6: |
| { |
| const struct sockaddr_in6* src = from; |
| |
| if (fromlen < sizeof(*src)) |
| return set_errno(EINVAL); |
| |
| a->family = SOCKET_IN6; |
| a->u.in6.port = ntohs(src->sin6_port); |
| memcpy(a->u.in6.address, src->sin6_addr.s6_addr, 16); |
| } |
| break; |
| #endif |
| |
| #ifdef HAVE_UNIX_SOCKETS |
| case AF_LOCAL: |
| { |
| const struct sockaddr_un* src = from; |
| char* end; |
| |
| if (fromlen < sizeof(*src)) |
| return set_errno(EINVAL); |
| |
| /* check that the path is zero-terminated */ |
| end = memchr(src->sun_path, 0, UNIX_PATH_MAX); |
| if (end == NULL) |
| return set_errno(EINVAL); |
| |
| a->family = SOCKET_UNIX; |
| a->u._unix.owner = 1; |
| a->u._unix.path = strdup(src->sun_path); |
| } |
| break; |
| #endif |
| |
| default: |
| return set_errno(EINVAL); |
| } |
| return 0; |
| } |
| |
| |
| int |
| sock_address_init_resolve( SockAddress* a, const char* hostname, uint16_t port, int preferIn6 ) |
| { |
| struct addrinfo hints[1]; |
| struct addrinfo* res; |
| int ret; |
| |
| memset(hints, 0, sizeof(hints)); |
| hints->ai_family = preferIn6 ? AF_INET6 : AF_UNSPEC; |
| |
| ret = getaddrinfo(hostname, NULL, hints, &res); |
| if (ret != 0) { |
| int err; |
| |
| switch (ret) { |
| case EAI_AGAIN: /* server is down */ |
| case EAI_FAIL: /* server is sick */ |
| err = EHOSTDOWN; |
| break; |
| |
| /* NOTE that in x86_64-w64-mingw32 both EAI_NODATA and EAI_NONAME are the same */ |
| #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) |
| case EAI_NODATA: |
| #endif |
| case EAI_NONAME: |
| err = ENOENT; |
| break; |
| |
| case EAI_MEMORY: |
| err = ENOMEM; |
| break; |
| |
| default: |
| err = EINVAL; |
| } |
| return set_errno(err); |
| } |
| |
| /* Parse the returned list of addresses. */ |
| { |
| struct addrinfo* res_ipv4 = NULL; |
| struct addrinfo* res_ipv6 = NULL; |
| struct addrinfo* r; |
| |
| /* If preferIn6 is false, we stop on the first IPv4 address, |
| * otherwise, we stop on the first IPv6 one |
| */ |
| for (r = res; r != NULL; r = r->ai_next) { |
| if (r->ai_family == AF_INET && res_ipv4 == NULL) { |
| res_ipv4 = r; |
| if (!preferIn6) |
| break; |
| } |
| else if (r->ai_family == AF_INET6 && res_ipv6 == NULL) { |
| res_ipv6 = r; |
| if (preferIn6) |
| break; |
| } |
| } |
| |
| /* Select the best address in 'r', which will be NULL |
| * if there is no corresponding address. |
| */ |
| if (preferIn6) { |
| r = res_ipv6; |
| if (r == NULL) |
| r = res_ipv4; |
| } else { |
| r = res_ipv4; |
| if (r == NULL) |
| r = res_ipv6; |
| } |
| |
| if (r == NULL) { |
| ret = set_errno(ENOENT); |
| goto Exit; |
| } |
| |
| /* Convert to a SockAddress */ |
| ret = sock_address_from_bsd( a, r->ai_addr, r->ai_addrlen ); |
| if (ret < 0) |
| goto Exit; |
| } |
| |
| /* need to set the port */ |
| switch (a->family) { |
| case SOCKET_INET: a->u.inet.port = port; break; |
| case SOCKET_IN6: a->u.in6.port = port; break; |
| default: ; |
| } |
| |
| Exit: |
| freeaddrinfo(res); |
| return ret; |
| } |
| |
| /* The Winsock headers for mingw lack some definitions */ |
| #ifndef AI_ADDRCONFIG |
| # define AI_ADDRCONFIG 0 |
| #endif |
| |
| SockAddress** |
| sock_address_list_create( const char* hostname, |
| const char* port, |
| unsigned flags ) |
| { |
| SockAddress** list = NULL; |
| SockAddress* addr; |
| int nn, count, ret; |
| struct addrinfo ai, *res, *e; |
| |
| memset(&ai, 0, sizeof(ai)); |
| ai.ai_flags |= AI_ADDRCONFIG; |
| ai.ai_family = PF_UNSPEC; |
| |
| if (flags & SOCKET_LIST_FORCE_INET) |
| ai.ai_family = PF_INET; |
| else if (flags & SOCKET_LIST_FORCE_IN6) |
| ai.ai_family = PF_INET6; |
| |
| if (flags & SOCKET_LIST_PASSIVE) |
| ai.ai_flags |= AI_PASSIVE; |
| else |
| ai.ai_flags |= AI_CANONNAME; |
| |
| if (flags & SOCKET_LIST_DGRAM) |
| ai.ai_socktype = SOCK_DGRAM; |
| |
| while (1) { |
| struct addrinfo hints = ai; |
| |
| ret = getaddrinfo(hostname, port, &hints, &res); |
| if (ret == 0) |
| break; |
| |
| switch (ret) { |
| #ifdef EAI_ADDRFAMILY |
| case EAI_ADDRFAMILY: |
| #endif |
| case EAI_NODATA: |
| set_errno(ENOENT); |
| break; |
| case EAI_FAMILY: |
| set_errno(EAFNOSUPPORT); |
| break; |
| case EAI_AGAIN: |
| set_errno(EAGAIN); |
| break; |
| #ifdef EAI_SYSTEM |
| case EAI_SYSTEM: |
| if (errno == EINTR) |
| continue; |
| break; |
| #endif |
| default: |
| set_errno(EINVAL); |
| } |
| return NULL; |
| } |
| |
| /* allocate result list */ |
| for (count = 0, e = res; e != NULL; e = e->ai_next) |
| count += 1; |
| |
| AARRAY_NEW(list, count+1); |
| AARRAY_NEW(addr, count); |
| |
| for (nn = 0, e = res; e != NULL; e = e->ai_next) { |
| |
| ret = sock_address_from_bsd(addr, e->ai_addr, e->ai_addrlen); |
| if (ret < 0) |
| continue; |
| |
| list[nn++] = addr++; |
| } |
| list[nn] = NULL; |
| freeaddrinfo(res); |
| return list; |
| } |
| |
| SockAddress** |
| sock_address_list_create2(const char* host_and_port, unsigned flags ) |
| { |
| char host_name[512]; |
| const char* actual_host_name = "localhost"; |
| // Parse host and port name. |
| const char* port_name = strchr(host_and_port, ':'); |
| if (port_name != NULL) { |
| int to_copy = MIN(sizeof(host_name)-1, port_name - host_and_port); |
| if (to_copy != 0) { |
| memcpy(host_name, host_and_port, to_copy); |
| host_name[to_copy] = '\0'; |
| actual_host_name = host_name; |
| port_name++; |
| } else { |
| return NULL; |
| } |
| } else { |
| port_name = host_and_port; |
| } |
| // Make sure that port_name is not empty. |
| if (port_name[0] == '\0') { |
| return NULL; |
| } |
| return sock_address_list_create(actual_host_name, port_name, flags); |
| } |
| |
| void |
| sock_address_list_free( SockAddress** list ) |
| { |
| int nn; |
| SockAddress* addr; |
| |
| if (list == NULL) |
| return; |
| |
| addr = list[0]; |
| for (nn = 0; list[nn] != NULL; nn++) { |
| sock_address_done(list[nn]); |
| list[nn] = NULL; |
| } |
| AFREE(addr); |
| AFREE(list); |
| } |
| |
| int |
| sock_address_get_numeric_info( SockAddress* a, |
| char* host, |
| size_t hostlen, |
| char* serv, |
| size_t servlen ) |
| { |
| struct sockaddr* saddr; |
| socklen_t slen; |
| int ret; |
| |
| switch (a->family) { |
| case SOCKET_INET: |
| saddr = (struct sockaddr*) &a->u.inet.address; |
| slen = sizeof(a->u.inet.address); |
| break; |
| |
| #if HAVE_IN6_SOCKET |
| case SOCKET_IN6: |
| saddr = (struct sockaddr*) &a->u.in6.address; |
| slen = sizeof(a->u.in6.address); |
| break; |
| #endif |
| default: |
| return set_errno(EINVAL); |
| } |
| |
| ret = getnameinfo( saddr, slen, host, hostlen, serv, servlen, |
| NI_NUMERICHOST | NI_NUMERICSERV ); |
| |
| switch (ret) { |
| case 0: |
| break; |
| case EAI_AGAIN: |
| ret = EAGAIN; |
| break; |
| default: |
| ret = EINVAL; |
| } |
| return ret; |
| } |
| |
| int |
| socket_create( SocketFamily family, SocketType type ) |
| { |
| int ret; |
| int sfamily = socket_family_to_bsd(family); |
| int stype = socket_type_to_bsd(type); |
| |
| if (sfamily < 0 || stype < 0) { |
| return set_errno(EINVAL); |
| } |
| |
| QSOCKET_CALL(ret, socket(sfamily, stype, 0)); |
| if (ret < 0) |
| return fix_errno(); |
| |
| return ret; |
| } |
| |
| |
| int |
| socket_create_inet( SocketType type ) |
| { |
| return socket_create( SOCKET_INET, type ); |
| } |
| |
| #if HAVE_IN6_SOCKETS |
| int |
| socket_create_in6 ( SocketType type ) |
| { |
| return socket_create( SOCKET_IN6, type ); |
| } |
| #endif |
| |
| #if HAVE_UNIX_SOCKETS |
| int |
| socket_create_unix( SocketType type ) |
| { |
| return socket_create( SOCKET_UNIX, type ); |
| } |
| #endif |
| |
| int socket_can_read(int fd) |
| { |
| #ifdef _WIN32 |
| unsigned long opt; |
| |
| if (ioctlsocket(fd, FIONREAD, &opt) < 0) |
| return 0; |
| |
| return opt; |
| #else |
| int opt; |
| |
| if (ioctl(fd, FIONREAD, &opt) < 0) |
| return 0; |
| |
| return opt; |
| #endif |
| } |
| |
| #define SOCKET_CALL(cmd) \ |
| int ret; \ |
| QSOCKET_CALL(ret, (cmd)); \ |
| if (ret < 0) \ |
| return fix_errno(); \ |
| return ret; \ |
| |
| int |
| socket_send(int fd, const void* buf, int buflen) |
| { |
| SOCKET_CALL(send(fd, buf, buflen, 0)) |
| } |
| |
| int |
| socket_send_oob( int fd, const void* buf, int buflen ) |
| { |
| SOCKET_CALL(send(fd, buf, buflen, MSG_OOB)); |
| } |
| |
| int |
| socket_sendto(int fd, const void* buf, int buflen, const SockAddress* to) |
| { |
| sockaddr_storage sa; |
| socklen_t salen; |
| |
| if (sock_address_to_bsd(to, &sa, &salen) < 0) |
| return -1; |
| |
| SOCKET_CALL(sendto(fd, buf, buflen, 0, sa.sa, salen)); |
| } |
| |
| int |
| socket_recv(int fd, void* buf, int len) |
| { |
| SOCKET_CALL(recv(fd, buf, len, 0)); |
| } |
| |
| int |
| socket_recvfrom(int fd, void* buf, int len, SockAddress* from) |
| { |
| sockaddr_storage sa; |
| socklen_t salen = sizeof(sa); |
| int ret; |
| |
| QSOCKET_CALL(ret,recvfrom(fd,buf,len,0,sa.sa,&salen)); |
| if (ret < 0) |
| return fix_errno(); |
| |
| if (sock_address_from_bsd(from, &sa, salen) < 0) |
| return -1; |
| |
| return ret; |
| } |
| |
| int |
| socket_connect( int fd, const SockAddress* address ) |
| { |
| sockaddr_storage addr; |
| socklen_t addrlen; |
| |
| if (sock_address_to_bsd(address, &addr, &addrlen) < 0) |
| return -1; |
| |
| SOCKET_CALL(connect(fd,addr.sa,addrlen)); |
| } |
| |
| int |
| socket_bind( int fd, const SockAddress* address ) |
| { |
| sockaddr_storage addr; |
| socklen_t addrlen; |
| |
| if (sock_address_to_bsd(address, &addr, &addrlen) < 0) |
| return -1; |
| |
| SOCKET_CALL(bind(fd, addr.sa, addrlen)); |
| } |
| |
| int |
| socket_get_address( int fd, SockAddress* address ) |
| { |
| sockaddr_storage addr; |
| socklen_t addrlen = sizeof(addr); |
| int ret; |
| |
| QSOCKET_CALL(ret, getsockname(fd, addr.sa, &addrlen)); |
| if (ret < 0) |
| return fix_errno(); |
| |
| return sock_address_from_bsd(address, &addr, addrlen); |
| } |
| |
| int |
| socket_get_peer_address( int fd, SockAddress* address ) |
| { |
| sockaddr_storage addr; |
| socklen_t addrlen = sizeof(addr); |
| int ret; |
| |
| QSOCKET_CALL(ret, getpeername(fd, addr.sa, &addrlen)); |
| if (ret < 0) |
| return fix_errno(); |
| |
| return sock_address_from_bsd(address, &addr, addrlen); |
| } |
| |
| int |
| socket_listen( int fd, int backlog ) |
| { |
| SOCKET_CALL(listen(fd, backlog)); |
| } |
| |
| int |
| socket_accept( int fd, SockAddress* address ) |
| { |
| sockaddr_storage addr; |
| socklen_t addrlen = sizeof(addr); |
| int ret; |
| |
| QSOCKET_CALL(ret, accept(fd, addr.sa, &addrlen)); |
| if (ret < 0) |
| return fix_errno(); |
| |
| if (address) { |
| if (sock_address_from_bsd(address, &addr, addrlen) < 0) { |
| socket_close(ret); |
| return -1; |
| } |
| } |
| return ret; |
| } |
| |
| static int |
| socket_getoption(int fd, int domain, int option, int defaut) |
| { |
| int ret; |
| while (1) { |
| #ifdef _WIN32 |
| DWORD opt = (DWORD)-1; |
| #else |
| int opt = -1; |
| #endif |
| socklen_t optlen = sizeof(opt); |
| ret = getsockopt(fd, domain, option, (char*)&opt, &optlen); |
| if (ret == 0) |
| return (int)opt; |
| if (errno != EINTR) |
| return defaut; |
| } |
| #undef OPT_CAST |
| } |
| |
| |
| SocketType socket_get_type(int fd) |
| { |
| int so_type = socket_getoption(fd, SOL_SOCKET, SO_TYPE, -1); |
| return socket_type_from_bsd(so_type); |
| } |
| |
| int socket_set_nonblock(int fd) |
| { |
| #ifdef _WIN32 |
| unsigned long opt = 1; |
| return ioctlsocket(fd, FIONBIO, &opt); |
| #else |
| int flags = fcntl(fd, F_GETFL); |
| return fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
| #endif |
| } |
| |
| int socket_set_blocking(int fd) |
| { |
| #ifdef _WIN32 |
| unsigned long opt = 0; |
| return ioctlsocket(fd, FIONBIO, &opt); |
| #else |
| int flags = fcntl(fd, F_GETFL); |
| return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); |
| #endif |
| } |
| |
| static int |
| socket_setoption(int fd, int domain, int option, int _flag) |
| { |
| #ifdef _WIN32 |
| DWORD flag = (DWORD) _flag; |
| #else |
| int flag = _flag; |
| #endif |
| return setsockopt( fd, domain, option, (const char*)&flag, sizeof(flag) ); |
| } |
| |
| int socket_set_xreuseaddr(int fd) |
| { |
| #ifdef _WIN32 |
| /* on Windows, SO_REUSEADDR is used to indicate that several programs can |
| * bind to the same port. this is completely different from the Unix |
| * semantics. instead of SO_EXCLUSIVEADDR to ensure that explicitely prevent |
| * this. |
| */ |
| return socket_setoption(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1); |
| #else |
| return socket_setoption(fd, SOL_SOCKET, SO_REUSEADDR, 1); |
| #endif |
| } |
| |
| |
| int socket_set_oobinline(int fd) |
| { |
| return socket_setoption(fd, SOL_SOCKET, SO_OOBINLINE, 1); |
| } |
| |
| |
| int socket_set_nodelay(int fd) |
| { |
| return socket_setoption(fd, IPPROTO_TCP, TCP_NODELAY, 1); |
| } |
| |
| int socket_set_ipv6only(int fd) |
| { |
| /* IPV6_ONLY is only supported since Vista on Windows, |
| * and the Mingw headers lack its definition anyway. |
| */ |
| #if defined(_WIN32) && !defined(IPV6_V6ONLY) |
| return 0; |
| #else |
| return socket_setoption(fd, IPPROTO_IPV6, IPV6_V6ONLY, 1); |
| #endif |
| } |
| |
| |
| int socket_get_error(int fd) |
| { |
| return socket_getoption(fd, SOL_SOCKET, SO_ERROR, -1); |
| } |
| |
| #ifdef _WIN32 |
| #include <stdlib.h> |
| |
| static void socket_cleanup(void) |
| { |
| WSACleanup(); |
| } |
| |
| int socket_init(void) |
| { |
| WSADATA Data; |
| int ret, err; |
| |
| ret = WSAStartup(MAKEWORD(2,2), &Data); |
| if (ret != 0) { |
| err = WSAGetLastError(); |
| return -1; |
| } |
| atexit(socket_cleanup); |
| return 0; |
| } |
| |
| #else /* !_WIN32 */ |
| |
| int socket_init(void) |
| { |
| return 0; /* nothing to do on Unix */ |
| } |
| |
| #endif /* !_WIN32 */ |
| |
| #ifdef _WIN32 |
| |
| static void |
| socket_close_handler( void* _fd ) |
| { |
| int fd = (int)_fd; |
| int ret; |
| char buff[64]; |
| |
| /* we want to drain the read side of the socket before closing it */ |
| do { |
| ret = recv( fd, buff, sizeof(buff), 0 ); |
| } while (ret < 0 && WSAGetLastError() == WSAEINTR); |
| |
| if (ret < 0 && WSAGetLastError() == EWOULDBLOCK) |
| return; |
| |
| qemu_set_fd_handler( fd, NULL, NULL, NULL ); |
| closesocket( fd ); |
| } |
| |
| void |
| socket_close( int fd ) |
| { |
| int old_errno = errno; |
| |
| shutdown( fd, SD_BOTH ); |
| /* we want to drain the socket before closing it */ |
| qemu_set_fd_handler( fd, socket_close_handler, NULL, (void*)fd ); |
| |
| errno = old_errno; |
| } |
| |
| #else /* !_WIN32 */ |
| |
| #include <unistd.h> |
| |
| void |
| socket_close( int fd ) |
| { |
| int old_errno = errno; |
| |
| shutdown( fd, SHUT_RDWR ); |
| close( fd ); |
| |
| errno = old_errno; |
| } |
| |
| #endif /* !_WIN32 */ |
| |
| |
| static int |
| socket_bind_server( int s, const SockAddress* to, SocketType type ) |
| { |
| socket_set_xreuseaddr(s); |
| |
| if (socket_bind(s, to) < 0) { |
| D("could not bind server socket address %s: %s", |
| sock_address_to_string(to), errno_str); |
| goto FAIL; |
| } |
| |
| if (type == SOCKET_STREAM) { |
| if (socket_listen(s, 4) < 0) { |
| D("could not listen server socket %s: %s", |
| sock_address_to_string(to), errno_str); |
| goto FAIL; |
| } |
| } |
| return s; |
| |
| FAIL: |
| socket_close(s); |
| return -1; |
| } |
| |
| |
| static int |
| socket_connect_client( int s, const SockAddress* to ) |
| { |
| if (socket_connect(s, to) < 0) { |
| D( "could not connect client socket to %s: %s\n", |
| sock_address_to_string(to), errno_str ); |
| socket_close(s); |
| return -1; |
| } |
| |
| socket_set_nonblock( s ); |
| return s; |
| } |
| |
| |
| static int |
| socket_in_server( int address, int port, SocketType type ) |
| { |
| SockAddress addr; |
| int s; |
| |
| sock_address_init_inet( &addr, address, port ); |
| s = socket_create_inet( type ); |
| if (s < 0) |
| return -1; |
| |
| return socket_bind_server( s, &addr, type ); |
| } |
| |
| |
| static int |
| socket_in_client( SockAddress* to, SocketType type ) |
| { |
| int s; |
| |
| s = socket_create_inet( type ); |
| if (s < 0) return -1; |
| |
| return socket_connect_client( s, to ); |
| } |
| |
| |
| int |
| socket_loopback_server( int port, SocketType type ) |
| { |
| return socket_in_server( SOCK_ADDRESS_INET_LOOPBACK, port, type ); |
| } |
| |
| int |
| socket_loopback_client( int port, SocketType type ) |
| { |
| SockAddress addr; |
| |
| sock_address_init_inet( &addr, SOCK_ADDRESS_INET_LOOPBACK, port ); |
| return socket_in_client( &addr, type ); |
| } |
| |
| |
| int |
| socket_network_client( const char* host, int port, SocketType type ) |
| { |
| SockAddress addr; |
| |
| if (sock_address_init_resolve( &addr, host, port, 0) < 0) |
| return -1; |
| |
| return socket_in_client( &addr, type ); |
| } |
| |
| |
| int |
| socket_anyaddr_server( int port, SocketType type ) |
| { |
| return socket_in_server( SOCK_ADDRESS_INET_ANY, port, type ); |
| } |
| |
| int |
| socket_accept_any( int server_fd ) |
| { |
| int fd; |
| |
| QSOCKET_CALL(fd, accept( server_fd, NULL, 0 )); |
| if (fd < 0) { |
| D( "could not accept client connection from fd %d: %s", |
| server_fd, errno_str ); |
| return -1; |
| } |
| |
| /* set to non-blocking */ |
| socket_set_nonblock( fd ); |
| return fd; |
| } |
| |
| |
| #if HAVE_UNIX_SOCKETS |
| |
| int |
| socket_unix_server( const char* name, SocketType type ) |
| { |
| SockAddress addr; |
| int s, ret; |
| |
| s = socket_create_unix( type ); |
| if (s < 0) |
| return -1; |
| |
| sock_address_init_unix( &addr, name ); |
| |
| do { |
| ret = unlink( name ); |
| } while (ret < 0 && errno == EINTR); |
| |
| ret = socket_bind_server( s, &addr, type ); |
| |
| sock_address_done( &addr ); |
| return ret; |
| } |
| |
| int |
| socket_unix_client( const char* name, SocketType type ) |
| { |
| SockAddress addr; |
| int s, ret; |
| |
| s = socket_create_unix(type); |
| if (s < 0) |
| return -1; |
| |
| sock_address_init_unix( &addr, name ); |
| |
| ret = socket_connect_client( s, &addr ); |
| |
| sock_address_done( &addr ); |
| return ret; |
| } |
| |
| #endif /* HAVE_UNIX_SOCKETS */ |
| |
| |
| |
| int |
| socket_pair(int *fd1, int *fd2) |
| { |
| #ifndef _WIN32 |
| int fds[2]; |
| int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); |
| |
| if (!ret) { |
| socket_set_nonblock(fds[0]); |
| socket_set_nonblock(fds[1]); |
| *fd1 = fds[0]; |
| *fd2 = fds[1]; |
| } |
| return ret; |
| #else /* _WIN32 */ |
| /* on Windows, select() only works with network sockets, which |
| * means we absolutely cannot use Win32 PIPEs to implement |
| * socket pairs with the current event loop implementation. |
| * We're going to do like Cygwin: create a random pair |
| * of localhost TCP sockets and connect them together |
| */ |
| int s0, s1, s2, port; |
| struct sockaddr_in sockin; |
| socklen_t len; |
| |
| /* first, create the 'server' socket. |
| * a port number of 0 means 'any port between 1024 and 5000. |
| * see Winsock bind() documentation for details */ |
| s0 = socket_loopback_server( 0, SOCK_STREAM ); |
| if (s0 < 0) |
| return -1; |
| |
| /* now connect a client socket to it, we first need to |
| * extract the server socket's port number */ |
| len = sizeof sockin; |
| if (getsockname(s0, (struct sockaddr*) &sockin, &len) < 0) { |
| closesocket (s0); |
| return -1; |
| } |
| |
| port = ntohs(sockin.sin_port); |
| s2 = socket_loopback_client( port, SOCK_STREAM ); |
| if (s2 < 0) { |
| closesocket(s0); |
| return -1; |
| } |
| |
| /* we need to accept the connection on the server socket |
| * this will create the second socket for the pair |
| */ |
| len = sizeof sockin; |
| s1 = accept(s0, (struct sockaddr*) &sockin, &len); |
| if (s1 == INVALID_SOCKET) { |
| closesocket (s0); |
| closesocket (s2); |
| return -1; |
| } |
| socket_set_nonblock(s1); |
| |
| /* close server socket */ |
| closesocket(s0); |
| *fd1 = s1; |
| *fd2 = s2; |
| return 0; |
| #endif /* _WIN32 */ |
| } |
| |
| |
| |
| int |
| socket_mcast_inet_add_membership( int s, uint32_t ip ) |
| { |
| struct ip_mreq imr; |
| |
| imr.imr_multiaddr.s_addr = htonl(ip); |
| imr.imr_interface.s_addr = htonl(INADDR_ANY); |
| |
| if ( setsockopt( s, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
| (const char *)&imr, |
| sizeof(struct ip_mreq)) < 0 ) |
| { |
| return fix_errno(); |
| } |
| return 0; |
| } |
| |
| int |
| socket_mcast_inet_drop_membership( int s, uint32_t ip ) |
| { |
| struct ip_mreq imr; |
| |
| imr.imr_multiaddr.s_addr = htonl(ip); |
| imr.imr_interface.s_addr = htonl(INADDR_ANY); |
| |
| if ( setsockopt( s, IPPROTO_IP, IP_DROP_MEMBERSHIP, |
| (const char *)&imr, |
| sizeof(struct ip_mreq)) < 0 ) |
| { |
| return fix_errno(); |
| } |
| return 0; |
| } |
| |
| int |
| socket_mcast_inet_set_loop( int s, int enabled ) |
| { |
| return socket_setoption( s, IPPROTO_IP, IP_MULTICAST_LOOP, !!enabled ); |
| } |
| |
| int |
| socket_mcast_inet_set_ttl( int s, int ttl ) |
| { |
| return socket_setoption( s, IPPROTO_IP, IP_MULTICAST_TTL, ttl ); |
| } |
| |
| |
| char* |
| host_name( void ) |
| { |
| static char buf[256]; /* 255 is the max host name length supported by DNS */ |
| int ret; |
| |
| QSOCKET_CALL(ret, gethostname(buf, sizeof(buf))); |
| |
| if (ret < 0) |
| return "localhost"; |
| else |
| return buf; |
| } |