diff options
Diffstat (limited to 'sockets.c')
-rw-r--r-- | sockets.c | 1126 |
1 files changed, 926 insertions, 200 deletions
@@ -13,6 +13,30 @@ #include "vl.h" #include <fcntl.h> #include "android_debug.h" +#include <stdlib.h> +#include <string.h> +#include "android_utils.h" + +#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 @@ -26,88 +50,757 @@ #endif #ifdef _WIN32 -const char* socket_strerr(void) -{ - int err = WSAGetLastError(); - switch (err) { - case WSA_INVALID_HANDLE: - return "invalid handle"; - case WSA_NOT_ENOUGH_MEMORY: - return "not enough memory"; - case WSA_INVALID_PARAMETER: - return "invalid parameter"; - case WSA_OPERATION_ABORTED: - return "operation aborted"; - case WSA_IO_INCOMPLETE: - return "incomplete i/o"; - case WSA_IO_PENDING: - return "pending i/o"; - case WSAEINTR: - return "interrupted"; - case WSAEBADF: - return "bad file descriptor"; - case WSAEACCES: - return "permission denied"; - case WSAEFAULT: - return "bad address"; - case WSAEINVAL: - return "invalid argument"; - case WSAEMFILE: - return "too many opened files"; - case WSAEWOULDBLOCK: - return "resource temporarily unavailable"; - case WSAEINPROGRESS: - return "operation in progress"; - case WSAEALREADY: - return "operation already in progress"; - case WSAENOTSOCK: - return "socket operation not on socket"; - case WSAEDESTADDRREQ: - return "destination address required"; - case WSAEMSGSIZE: - return "message too long"; - case WSAEPROTOTYPE: - return "wrong protocol for socket type"; - case WSAENOPROTOOPT: - return "bad option for protocol"; - case WSAEPROTONOSUPPORT: - return "protocol not supported"; - case WSAEADDRINUSE: - return "address already in use"; - case WSAEADDRNOTAVAIL: - return "address not available"; - case WSAENETDOWN: - return "network is down"; - case WSAENETUNREACH: - return "network unreachable"; - case WSAENETRESET: - return "network dropped connection on reset"; - case WSAECONNABORTED: - return "connection aborted"; - case WSAECONNRESET: - return "connection reset by peer"; - case WSAENOBUFS: - return "no buffer space available"; - case WSAETIMEDOUT: - return "connection timed out"; - case WSAECONNREFUSED: - return "connection refused"; - case WSAEHOSTDOWN: - return "host is down"; - case WSAEHOSTUNREACH: - return "no route to host"; - default: - return "unknown/TODO"; + +#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(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,EAGAIN,"resource temporarily unavailable") \ + EE(WSAEINPROGRESS,EAGAIN,"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 */ + + 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 = "<unknown error>"; + + for ( ; werr->string; werr++ ) { + if (werr->winsock == winsock_error) { + result = werr->string; + break; + } + } + + if (result == NULL) + result = strerror(errno); + + return result; +} +#else +static int +_fix_errno( void ) +{ + return -1; +} + +static int +_set_errno( int code ) +{ + errno = code; + return -1; } #endif -int socket_get_type(int fd) +/* 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 -1; + } +} + +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) -1; + } +} + +#if 0 +static int +socket_type_check( SocketType type ) +{ + return (type == SOCKET_DGRAM || type == SOCKET_STREAM); +} +#endif + +/* 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) + return buf; + if (buf+1 == end) + c = 0; + *buf++ = (char) c; + 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; + } +} + +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 + +int +sock_address_to_bsd( const SockAddress* a, void* paddress, size_t *psize ) +{ + switch (a->family) { + case SOCKET_INET: + { + struct sockaddr_in* dst = (struct sockaddr_in*) paddress; + + *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 = (struct sockaddr_in6*) paddress; + + *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 = (struct sockaddr_un*) paddress; + + if (slen >= UNIX_PATH_MAX) + return -1; + + memset( paddress, 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; +} + +int +sock_address_to_inet( SockAddress* a, int *paddr_ip, int *paddr_port ) +{ + struct sockaddr addr; + socklen_t addrlen; + + if (a->family != SOCKET_INET) { + return _set_errno(EINVAL); + } + + if (sock_address_to_bsd(a, &addr, &addrlen) < 0) + return -1; + + *paddr_ip = ntohl(((struct sockaddr_in*)&addr)->sin_addr.s_addr); + *paddr_port = ntohs(((struct sockaddr_in*)&addr)->sin_port); + + return 0; +} + +int +sock_address_from_bsd( SockAddress* a, const void* from, size_t fromlen ) +{ + switch (((struct sockaddr*)from)->sa_family) { + case AF_INET: + { + struct sockaddr_in* src = (struct sockaddr_in*) 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: + { + struct sockaddr_in6* src = (struct sockaddr_in6*) 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: + { + struct sockaddr_un* src = (struct sockaddr_un*) 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; + + if (getaddrinfo(hostname, NULL, hints, &res) < 0) { + return _fix_errno(); + } + + ret = sock_address_from_bsd( a, res->ai_addr, res->ai_addrlen ); + freeaddrinfo(res); + + /* 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: ; + } + + 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) +{ + struct sockaddr sa; + socklen_t salen; + + if (sock_address_to_bsd(to, &sa, &salen) < 0) + return -1; + + SOCKET_CALL(sendto(fd, buf, buflen, 0, &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) +{ + struct sockaddr sa; + socklen_t salen = sizeof(sa); + int ret; + + QSOCKET_CALL(ret,recvfrom(fd,buf,len,0,&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 ) +{ + struct sockaddr addr; + socklen_t addrlen; + + if (sock_address_to_bsd(address, &addr, &addrlen) < 0) + return -1; + + SOCKET_CALL(connect(fd,&addr,addrlen)); +} + +int +socket_bind( int fd, const SockAddress* address ) +{ + struct sockaddr addr; + socklen_t addrlen; + + if (sock_address_to_bsd(address, &addr, &addrlen) < 0) + return -1; + + SOCKET_CALL(bind(fd, &addr, addrlen)); +} + +int +socket_get_address( int fd, SockAddress* address ) +{ + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int ret; + + QSOCKET_CALL(ret, getsockname(fd, &addr, &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 ) +{ + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int ret; + + QSOCKET_CALL(ret, accept(fd, &addr, &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; +} + +SocketType socket_get_type(int fd) { int opt = -1; int optlen = sizeof(opt); getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&opt, (void*)&optlen ); - return opt; + + return socket_type_from_bsd(opt); } int socket_set_nonblock(int fd) @@ -116,7 +809,8 @@ int socket_set_nonblock(int fd) unsigned long opt = 1; return ioctlsocket(fd, FIONBIO, &opt); #else - return fcntl(fd, F_SETFL, O_NONBLOCK); + int flags = fcntl(fd, F_GETFL); + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); #endif } @@ -126,10 +820,22 @@ int socket_set_blocking(int fd) unsigned long opt = 0; return ioctlsocket(fd, FIONBIO, &opt); #else - return fcntl(fd, F_SETFL, O_NONBLOCK); + 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) { @@ -139,36 +845,22 @@ int socket_set_xreuseaddr(int fd) * semantics. instead of SO_EXCLUSIVEADDR to ensure that explicitely prevent * this. */ - BOOL flag = 1; - return setsockopt( fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&flag, sizeof(flag) ); + return socket_setoption(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1); #else - int flag = 1; - return setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&flag, sizeof(flag) ); + return socket_setoption(fd, SOL_SOCKET, SO_REUSEADDR, 1); #endif } int socket_set_oobinline(int fd) { -#ifdef _WIN32 - BOOL flag = 1; -#else - int flag = 1; -#endif - /* enable low-latency */ - return setsockopt( fd, SOL_SOCKET, SO_OOBINLINE, (const char*)&flag, sizeof(flag) ); + return socket_setoption(fd, SOL_SOCKET, SO_OOBINLINE, 1); } int socket_set_lowlatency(int fd) { -#ifdef _WIN32 - BOOL flag = 1; -#else - int flag = 1; -#endif - /* enable low-latency */ - return setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(flag) ); + return socket_setoption(fd, IPPROTO_TCP, TCP_NODELAY, 1); } @@ -215,9 +907,9 @@ socket_close_handler( void* _fd ) /* we want to drain the read side of the socket before closing it */ do { ret = recv( fd, buff, sizeof(buff), 0 ); - } while (ret < 0 && socket_errno == EINTR); + } while (ret < 0 && WSAGetLastError() == WSAEINTR); - if (ret < 0 && socket_errno == EWOULDBLOCK) + if (ret < 0 && WSAGetLastError() == EWOULDBLOCK) return; qemu_set_fd_handler( fd, NULL, NULL, NULL ); @@ -227,9 +919,13 @@ socket_close_handler( void* _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 */ @@ -239,47 +935,49 @@ socket_close( int fd ) 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 struct sockaddr* addr, socklen_t addrlen, int type ) +socket_bind_server( int s, const SockAddress* to, SocketType type ) { - int ret; - socket_set_xreuseaddr(s); - QSOCKET_CALL(ret, bind(s, addr, addrlen)); - if ( ret < 0 ) { - dprint("could not bind server socket: %s", socket_errstr()); - socket_close(s); - return -1; + if (socket_bind(s, to) < 0) { + dprint("could not bind server socket address %s: %s", + sock_address_to_string(to), errno_str); + goto FAIL; } - if (type == SOCK_STREAM) { - QSOCKET_CALL( ret, listen(s, 4) ); - if ( ret < 0 ) { - dprint("could not listen server socket: %s", socket_errstr()); - socket_close(s); - return -1; + if (type == SOCKET_STREAM) { + if (socket_listen(s, 4) < 0) { + dprint("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 struct sockaddr* addr, socklen_t addrlen ) +socket_connect_client( int s, const SockAddress* to ) { - int ret; - - QSOCKET_CALL(ret, connect(s, addr, addrlen)); - if ( ret < 0 ) { - dprint( "could not connect client socket: %s\n", socket_errstr() ); + if (socket_connect(s, to) < 0) { + dprint( "could not connect client socket to %s: %s\n", + sock_address_to_string(to), errno_str ); socket_close(s); return -1; } @@ -290,76 +988,64 @@ socket_connect_client( int s, const struct sockaddr* addr, socklen_t addrlen static int -socket_in_server( int address, int port, int type ) +socket_in_server( int address, int port, SocketType type ) { - struct sockaddr_in addr; - int s; + SockAddress addr; + int s; - memset( &addr, 0, sizeof(addr) ); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(address); - - s = socket(PF_INET, type, 0); - if (s < 0) return -1; + sock_address_init_inet( &addr, address, port ); + s = socket_create_inet( type ); + if (s < 0) + return -1; - return socket_bind_server( s, (struct sockaddr*) &addr, sizeof(addr), type ); + return socket_bind_server( s, &addr, type ); } static int -socket_in_client( struct sockaddr_in* addr, int type ) +socket_in_client( SockAddress* to, SocketType type ) { int s; - s = socket(addr->sin_family, type, 0); + s = socket_create_inet( type ); if (s < 0) return -1; - return socket_connect_client( s, (struct sockaddr*) addr, sizeof(*addr) ); + return socket_connect_client( s, to ); } int -socket_loopback_server( int port, int type ) +socket_loopback_server( int port, SocketType type ) { - return socket_in_server( INADDR_LOOPBACK, port, type ); + return socket_in_server( SOCK_ADDRESS_INET_LOOPBACK, port, type ); } int -socket_loopback_client( int port, int type ) +socket_loopback_client( int port, SocketType type ) { - struct sockaddr_in addr; - memset( &addr, 0, sizeof(addr) ); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + 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, int type ) +socket_network_client( const char* host, int port, SocketType type ) { - struct hostent* hp; - struct sockaddr_in addr; + SockAddress addr; - hp = gethostbyname(host); - if (hp == 0) return -1; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = hp->h_addrtype; - addr.sin_port = htons(port); - memcpy( &addr.sin_addr, hp->h_addr, hp->h_length ); + if (sock_address_init_resolve( &addr, host, port, 0) < 0) + return -1; return socket_in_client( &addr, type ); } int -socket_anyaddr_server( int port, int type ) +socket_anyaddr_server( int port, SocketType type ) { - return socket_in_server( INADDR_ANY, port, type ); + return socket_in_server( SOCK_ADDRESS_INET_ANY, port, type ); } int @@ -370,7 +1056,7 @@ socket_accept_any( int server_fd ) QSOCKET_CALL(fd, accept( server_fd, NULL, 0 )); if (fd < 0) { dprint( "could not accept client connection from fd %d: %s", - server_fd, socket_errstr() ); + server_fd, errno_str ); return -1; } @@ -380,72 +1066,49 @@ socket_accept_any( int server_fd ) } -#ifndef _WIN32 - -#include <sys/un.h> - -static int -socket_unix_prepare_address( struct sockaddr_un* addr, const char* name ) -{ - size_t namelen = strlen(name); - size_t offset = offsetof(struct sockaddr_un, sun_path); - - if (offset + namelen + 1 > sizeof(*addr)) { - fprintf(stderr, "unix socket path too long\n"); - return -1; - } - memset( addr, 0, sizeof(*addr) ); - addr->sun_family = AF_LOCAL; - memcpy( addr->sun_path, name, namelen+1 ); - return offset + namelen + 1; -} +#if HAVE_UNIX_SOCKETS int -socket_unix_server( const char* name, int type ) +socket_unix_server( const char* name, SocketType type ) { - struct sockaddr_un addr; - int addrlen; - int s, ret; - - do { - s = socket(AF_LOCAL, type, 0); - } while (s < 0 && socket_errno == EINTR); - if (s < 0) return -1; + SockAddress addr; + int s, ret; - addrlen = socket_unix_prepare_address( &addr, name ); - if (addrlen < 0) { - socket_close(s); + s = socket_create_unix( type ); + if (s < 0) return -1; - } + + sock_address_init_unix( &addr, name ); do { - ret = unlink( addr.sun_path ); + ret = unlink( name ); } while (ret < 0 && errno == EINTR); - return socket_bind_server( s, (struct sockaddr*) &addr, (socklen_t)addrlen, type ); + ret = socket_bind_server( s, &addr, type ); + + sock_address_done( &addr ); + return ret; } int -socket_unix_client( const char* name, int type ) +socket_unix_client( const char* name, SocketType type ) { - struct sockaddr_un addr; - int addrlen; - int s; + SockAddress addr; + int s, ret; - do { - s = socket(AF_LOCAL, type, 0); - } while (s < 0 && socket_errno == EINTR); - if (s < 0) return -1; - - addrlen = socket_unix_prepare_address( &addr, name ); - if (addrlen < 0) { - socket_close(s); + s = socket_create_unix(type); + if (s < 0) return -1; - } - return socket_connect_client( s, (struct sockaddr*) &addr, (socklen_t)addrlen ); + sock_address_init_unix( &addr, name ); + + ret = socket_connect_client( s, &addr ); + + sock_address_done( &addr ); + return ret; } -#endif + +#endif /* HAVE_UNIX_SOCKETS */ @@ -515,3 +1178,66 @@ socket_pair(int *fd1, int *fd2) 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; +} |