/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#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 <stdlib.h>
#include <string.h>
#include <limits.h>
//#include "android/utils/path.h"
//#include "android/utils/debug.h"
//#include "android/utils/misc.h"
//#include "android/utils/system.h"
#define D(...) ((void)0)
#ifdef _WIN32
# define xxWIN32_LEAN_AND_MEAN
# define _WIN32_WINNT 0x501
# 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 */
#define MIN(x,y) ({ typeof(x) _x = (x); typeof(y) _y = (y); _x <= _y ? _x : _y; })
#define AFREE(p) free(p)
#define AARRAY_NEW(p,count) (p) = malloc(sizeof(*(p))*(count))
#define AARRAY_NEW0(p,count) (p) = calloc(sizeof(*(p)),(count))
/* 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 = "Unknown socket error";
}
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