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

/* NOTE that in x86_64-w64-mingw32 both EAI_NODATA and EAI_NONAME are the same */
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
        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;
}