Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: jb
Fetching contributors…

Cannot retrieve contributors at this time

1568 lines (1298 sloc) 35.813 kB
/* 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;
#ifdef EAI_NODATA
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;
}
Jump to Line
Something went wrong with that request. Please try again.