/* 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 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 */ }