From 55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 21 Oct 2008 07:00:00 -0700 Subject: Initial Contribution --- sockets.c | 449 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 449 insertions(+) create mode 100644 sockets.c (limited to 'sockets.c') diff --git a/sockets.c b/sockets.c new file mode 100644 index 0000000..3a5e96d --- /dev/null +++ b/sockets.c @@ -0,0 +1,449 @@ +/* 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. +*/ +#include "sockets.h" +#include "vl.h" +#include +#include "android_debug.h" + +/* 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 { _ret = (_cmd); } while ( _ret < 0 && errno == EINTR ) +#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"; + } +} +#endif + +int socket_get_type(int fd) +{ + int opt = -1; + int optlen = sizeof(opt); + getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&opt, (void*)&optlen ); + return opt; +} + +int socket_set_nonblock(int fd) +{ +#ifdef _WIN32 + unsigned long opt = 1; + return ioctlsocket(fd, FIONBIO, &opt); +#else + return fcntl(fd, F_SETFL, O_NONBLOCK); +#endif +} + +int socket_set_blocking(int fd) +{ +#ifdef _WIN32 + unsigned long opt = 0; + return ioctlsocket(fd, FIONBIO, &opt); +#else + return fcntl(fd, F_SETFL, O_NONBLOCK); +#endif +} + + +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. + */ + BOOL flag = 1; + return setsockopt( fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&flag, sizeof(flag) ); +#else + int flag = 1; + return setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&flag, sizeof(flag) ); +#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) ); +} + + +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) ); +} + + +#ifdef _WIN32 +#include + +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 && socket_errno == EINTR); + + if (ret < 0 && socket_errno == EWOULDBLOCK) + return; + + qemu_set_fd_handler( fd, NULL, NULL, NULL ); + closesocket( fd ); +} + +void +socket_close( int fd ) +{ + shutdown( fd, SD_BOTH ); + /* we want to drain the socket before closing it */ + qemu_set_fd_handler( fd, socket_close_handler, NULL, (void*)fd ); +} + +#else /* !_WIN32 */ + +#include + +void +socket_close( int fd ) +{ + shutdown( fd, SHUT_RDWR ); + close( fd ); +} + +#endif /* !_WIN32 */ + + +static int +socket_bind_server( int s, const struct sockaddr* addr, socklen_t addrlen, int 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 (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; + } + } + return s; +} + + +static int +socket_connect_client( int s, const struct sockaddr* addr, socklen_t addrlen ) +{ + int ret; + + QSOCKET_CALL(ret, connect(s, addr, addrlen)); + if ( ret < 0 ) { + dprint( "could not connect client socket: %s\n", socket_errstr() ); + socket_close(s); + return -1; + } + + socket_set_nonblock( s ); + return s; +} + + +static int +socket_in_server( int address, int port, int type ) +{ + struct sockaddr_in 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; + + return socket_bind_server( s, (struct sockaddr*) &addr, sizeof(addr), type ); +} + + +static int +socket_in_client( struct sockaddr_in* addr, int type ) +{ + int s; + + s = socket(addr->sin_family, type, 0); + if (s < 0) return -1; + + return socket_connect_client( s, (struct sockaddr*) addr, sizeof(*addr) ); +} + + +int +socket_loopback_server( int port, int type ) +{ + return socket_in_server( INADDR_LOOPBACK, port, type ); +} + +int +socket_loopback_client( int port, int 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); + + return socket_in_client( &addr, type ); +} + + +int +socket_network_client( const char* host, int port, int type ) +{ + struct hostent* hp; + struct sockaddr_in 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 ); + + return socket_in_client( &addr, type ); +} + + +int +socket_anyaddr_server( int port, int type ) +{ + return socket_in_server( INADDR_ANY, port, type ); +} + +int +socket_accept_any( int server_fd ) +{ + int 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() ); + return -1; + } + + /* set to non-blocking */ + socket_set_nonblock( fd ); + return fd; +} + + +#ifndef _WIN32 + +#include + +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; +} + +int +socket_unix_server( const char* name, int 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; + + addrlen = socket_unix_prepare_address( &addr, name ); + if (addrlen < 0) { + socket_close(s); + return -1; + } + + do { + ret = unlink( addr.sun_path ); + } while (ret < 0 && errno == EINTR); + + return socket_bind_server( s, (struct sockaddr*) &addr, (socklen_t)addrlen, type ); +} + +int +socket_unix_client( const char* name, int type ) +{ + struct sockaddr_un addr; + int addrlen; + int s; + + 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); + return -1; + } + + return socket_connect_client( s, (struct sockaddr*) &addr, (socklen_t)addrlen ); +} +#endif + -- cgit v1.1