/* Copyright (c) 2001, Stanford University
 * All rights reserved
 *
 * See the file LICENSE.txt for information on redistributing this software.
 */

#ifdef WINDOWS
#define WIN32_LEAN_AND_MEAN
#pragma warning( push, 3 )
#include <winsock2.h>
#pragma warning( pop )
#pragma warning( disable : 4514 )
#pragma warning( disable : 4127 )
# ifndef VBOX
typedef int ssize_t;
# endif
#else
#include <sys/types.h>
#include <sys/wait.h>
#ifdef OSF1
typedef int socklen_t;
#endif
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#endif

#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#ifdef AIX
#include <strings.h>
#endif

#ifdef LINUX
#include <sys/ioctl.h>
#include <unistd.h>
#endif

#include "cr_error.h"
#include "cr_mem.h"
#include "cr_string.h"
#include "cr_bufpool.h"
#include "cr_net.h"
#include "cr_endian.h"
#include "cr_threads.h"
#include "cr_environment.h"
#include "net_internals.h"

#ifdef ADDRINFO
#define PF PF_UNSPEC
#endif

#ifdef WINDOWS
#define EADDRINUSE   WSAEADDRINUSE
#define ECONNREFUSED WSAECONNREFUSED
#endif

#ifdef WINDOWS

#undef  ECONNRESET
#define ECONNRESET  WSAECONNRESET
#undef  EINTR
#define EINTR       WSAEINTR

int crTCPIPErrno( void )
{
    return WSAGetLastError( );
}

char *crTCPIPErrorString( int err )
{
    static char buf[512], *temp;
    
    sprintf( buf, "err=%d", err );
    
#define X(x)    crStrcpy(buf,x); break

    switch ( err )
    {
        case WSAECONNREFUSED: X( "connection refused" );
        case WSAECONNRESET:   X( "connection reset" );
        default:
            FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
                           FORMAT_MESSAGE_FROM_SYSTEM |
                           FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, err,
                           MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
                           (LPTSTR) &temp, 0, NULL );
            if ( temp )
            {
                crStrncpy( buf, temp, sizeof(buf)-1 );
                buf[sizeof(buf)-1] = 0;
            }
    }

#undef X

    temp = buf + crStrlen(buf) - 1;
    while ( temp > buf && isspace( *temp ) )
    {
        *temp = '\0';
        temp--;
    }

    return buf;
}

#else /* WINDOWS */

int crTCPIPErrno( void )
{
    int err = errno;
    errno = 0;
    return err;
}

char *crTCPIPErrorString( int err )
{
    static char buf[512], *temp;
    
    temp = strerror( err );
    if ( temp )
    {
        crStrncpy( buf, temp, sizeof(buf)-1 );
        buf[sizeof(buf)-1] = 0;
    }
    else
    {
        sprintf( buf, "err=%d", err );
    }

    return buf;
}

#endif /* WINDOWS */


/*
 * Socket callbacks.  When a socket is created or destroyed we will
 * call these callback functions.
 * XXX Currently only implemented for TCP/IP.
 * XXX Maybe have lists of callbacks?
 */
static CRSocketCallbackProc SocketCreateCallback = NULL;
static CRSocketCallbackProc SocketDestroyCallback = NULL;

void
crRegisterSocketCallback(int mode, CRSocketCallbackProc proc)
{
    if (mode == CR_SOCKET_CREATE) {
        SocketCreateCallback = proc;
    }
    else if (mode == CR_SOCKET_DESTROY) {
        SocketDestroyCallback = proc;
    }
    else {
        crError("Invalid crRegisterSocketCallbac mode=%d", mode);
    }
}



void crCloseSocket( CRSocket sock )
{
    int fail;

    if (sock <= 0)
        return;

    if (SocketDestroyCallback) {
        SocketDestroyCallback(CR_SOCKET_DESTROY, sock);
    }

#ifdef WINDOWS
    fail = ( closesocket( sock ) != 0 );
#else
    shutdown( sock, 2 /* RDWR */ );
    fail = ( close( sock ) != 0 );
#endif
    if ( fail )
    {
        int err = crTCPIPErrno( );
        crWarning( "crCloseSocket( sock=%d ): %s",
                             sock, crTCPIPErrorString( err ) );
    }
}

cr_tcpip_data cr_tcpip;

/**
 * Read len bytes from socket, and store in buffer.
 * \return 1 if success, -1 if error, 0 if sender exited.
 */
int
__tcpip_read_exact( CRSocket sock, void *buf, unsigned int len )
{
    char *dst = (char *) buf;
    /* 
     * Shouldn't write to a non-existent socket, ie when 
     * crTCPIPDoDisconnect has removed it from the pool
     */
    if ( sock <= 0 )
        return 1;

    while ( len > 0 )
    {
        const int num_read = recv( sock, dst, (int) len, 0 );

#ifdef WINDOWS_XXXX
        /* MWE: why is this necessary for windows???  Does it return a
             "good" value for num_bytes despite having a reset
             connection? */
        if ( crTCPIPErrno( ) == ECONNRESET )
            return -1;
#endif

        if ( num_read < 0 )
        {
            int error = crTCPIPErrno();
            switch( error )
            {
                case EINTR:
                    crWarning( "__tcpip_read_exact(TCPIP): "
                            "caught an EINTR, looping for more data" );
                    continue;
                case EFAULT:
                    crWarning( "EFAULT" );
                    break;
                case EINVAL:
                    crWarning( "EINVAL" );
                    break;
                default:
                    break;
            }
            crWarning( "Bad bad bad socket error: %s", crTCPIPErrorString( error ) );
            return -1;
        }

        if ( num_read == 0 ) 
        {
            /* client exited gracefully */
            return 0;
        }

        dst += num_read;
        len -= num_read;
    }

    return 1;
}

void
crTCPIPReadExact( CRConnection *conn, void *buf, unsigned int len )
{
    if ( __tcpip_read_exact( conn->tcp_socket, buf, len ) <= 0 )
    {
        __tcpip_dead_connection( conn );
    }
}

/**
 * Write the given buffer of len bytes on the socket.
 * \return 1 if OK, negative value if error.
 */
int
__tcpip_write_exact( CRSocket sock, const void *buf, unsigned int len )
{
    const char *src = (const char *) buf;

    /* 
     * Shouldn't write to a non-existent socket, ie when 
     * crTCPIPDoDisconnect has removed it from the pool
     */
    if ( sock <= 0 )
        return 1;

    while ( len > 0 )
    {
        const int num_written = send( sock, src, len, 0 );
        if ( num_written <= 0 )
        {
            int err;
          if ( (err = crTCPIPErrno( )) == EINTR )
          {
                crWarning("__tcpip_write_exact(TCPIP): caught an EINTR, continuing");
                continue;
          }
          
          return -err;
        }
         
        len -= num_written;
        src += num_written;
    }
         
    return 1;
}

void
crTCPIPWriteExact( CRConnection *conn, const void *buf, unsigned int len )
{
    if ( __tcpip_write_exact( conn->tcp_socket, buf, len) <= 0 )
    {
        __tcpip_dead_connection( conn );
    }
}


/**
 * Make sockets do what we want: 
 * 
 * 1) Change the size of the send/receive buffers to 64K 
 * 2) Turn off Nagle's algorithm
 */
static void
spankSocket( CRSocket sock )
{
    /* why do we do 1) ? things work much better for me to push the
     * the buffer size way up -- karl
     */
#ifdef LINUX
    int sndbuf = 1*1024*1024;
#else
    int sndbuf = 64*1024;
#endif  

    int rcvbuf = sndbuf;
    int so_reuseaddr = 1;
    int tcp_nodelay = 1;

    if ( setsockopt( sock, SOL_SOCKET, SO_SNDBUF, 
             (char *) &sndbuf, sizeof(sndbuf) ) )
    {
        int err = crTCPIPErrno( );
        crWarning( "setsockopt( SO_SNDBUF=%d ) : %s",
               sndbuf, crTCPIPErrorString( err ) );
    }
    
    if ( setsockopt( sock, SOL_SOCKET, SO_RCVBUF,
             (char *) &rcvbuf, sizeof(rcvbuf) ) )
    {
        int err = crTCPIPErrno( );
        crWarning( "setsockopt( SO_RCVBUF=%d ) : %s",
               rcvbuf, crTCPIPErrorString( err ) );
    }
    
    
    if ( setsockopt( sock, SOL_SOCKET, SO_REUSEADDR,
             (char *) &so_reuseaddr, sizeof(so_reuseaddr) ) )
    {
        int err = crTCPIPErrno( );
        crWarning( "setsockopt( SO_REUSEADDR=%d ) : %s",
               so_reuseaddr, crTCPIPErrorString( err ) );
    }
    
    if ( setsockopt( sock, IPPROTO_TCP, TCP_NODELAY,
             (char *) &tcp_nodelay, sizeof(tcp_nodelay) ) )
    {
        int err = crTCPIPErrno( );
        crWarning( "setsockopt( TCP_NODELAY=%d )"
               " : %s", tcp_nodelay, crTCPIPErrorString( err ) );
    }
}


#if defined( WINDOWS ) || defined( IRIX ) || defined( IRIX64 )
typedef int socklen_t;
#endif


/**
 * Create a listening socket using the given port.
 * Caller can then pass the socket to accept().
 * If the port is one that's been seen before, we'll reuse/return the
 * previously create socket.
 */
static int
CreateListeningSocket(int port)
{
    /* XXX should use an unbounded list here instead of parallel arrays... */
#define MAX_PORTS 100
    static int ports[MAX_PORTS];
    static int sockets[MAX_PORTS];
    static int count = 0;
    int i, sock = -1;

    /* search to see if we've seen this port before */
    for (i = 0; i < count; i++) {
        if (ports[i] == port) {
            return sockets[i];
        }
    }

    /* new port so create new socket */
    {
        int err;
#ifndef ADDRINFO
        struct sockaddr_in  servaddr;
#endif

        /* with the new OOB stuff, we can have multiple ports being 
         * accepted on, so we need to redo the server socket every time.
         */
#ifndef ADDRINFO
        sock = socket( AF_INET, SOCK_STREAM, 0 );
        if ( sock == -1 )
        {
            err = crTCPIPErrno( );
            crError( "Couldn't create socket: %s", crTCPIPErrorString( err ) );
        }
        spankSocket( sock );

        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = INADDR_ANY;
        servaddr.sin_port = htons( (short) port );

        if ( bind( sock, (struct sockaddr *) &servaddr, sizeof(servaddr) ) )
        {
            err = crTCPIPErrno( );
            crError( "Couldn't bind to socket (port=%d): %s",
                             port, crTCPIPErrorString( err ) );
        }

        if ( listen( sock, 100 /* max pending connections */ ) )
        {
            err = crTCPIPErrno( );
            crError( "Couldn't listen on socket: %s", crTCPIPErrorString( err ) );
        }
#else
        char port_s[NI_MAXSERV];
        struct addrinfo *res,*cur;
        struct addrinfo hints;

        sprintf(port_s, "%u", (short unsigned) port);

        crMemset(&hints, 0, sizeof(hints));
        hints.ai_flags = AI_PASSIVE;
        hints.ai_family = PF;
        hints.ai_socktype = SOCK_STREAM;

        err = getaddrinfo( NULL, port_s, &hints, &res );
        if ( err )
            crError( "Couldn't find local TCP port %s: %s",
                             port_s, gai_strerror(err) );

        for (cur=res;cur;cur=cur->ai_next)
        {
            sock = socket( cur->ai_family, cur->ai_socktype, cur->ai_protocol );
            if ( sock == -1 )
            {
                err = crTCPIPErrno( );
                if (err != EAFNOSUPPORT)
                    crWarning("Couldn't create socket of family %i: %s, trying another", 
                                        cur->ai_family, crTCPIPErrorString( err ) );
                continue;
            }
            spankSocket( sock );

            if ( bind( sock, cur->ai_addr, cur->ai_addrlen ) )
            {
                err = crTCPIPErrno( );
                crWarning( "Couldn't bind to socket (port=%d): %s", 
                       port, crTCPIPErrorString( err ) );
                crCloseSocket( sock );
                continue;
            }

            if ( listen( sock, 100 /* max pending connections */ ) )
            {
                err = crTCPIPErrno( );
                crWarning("Couldn't listen on socket: %s", crTCPIPErrorString(err));
                crCloseSocket( sock );
                continue;
            }
            break;
        }
        freeaddrinfo(res);
        if (!cur)
            crError( "Couldn't find/bind local TCP port %s", port_s);
#endif
    }
    
    /* save the new port/socket */
    if (count == MAX_PORTS) {
        crError("Fatal error in tcpip layer: too many listening ports/sockets");
    }
    ports[count] = port;
    sockets[count] = sock;
    count++;

    return sock;
}




void
crTCPIPAccept( CRConnection *conn, const char *hostname, unsigned short port )
{
    int err;
    socklen_t       addr_length;
#ifndef ADDRINFO
    struct hostent      *host;
    struct in_addr      sin_addr;
    struct sockaddr     addr;
#else
    struct sockaddr_storage addr;
    char            host[NI_MAXHOST];
#endif

    cr_tcpip.server_sock = CreateListeningSocket(port);

    /* If brokered, we'll contact the mothership to broker the network
     * connection.  We'll send the mothership our hostname, the port and
     * our endianness and will get in return a connection ID number.
     */
    if (conn->broker) {
        crError("There shouldn't be any brokered connections in VirtualBox");
    }
    
    addr_length =   sizeof( addr );
    conn->tcp_socket = accept( cr_tcpip.server_sock, (struct sockaddr *) &addr, &addr_length );
    if (conn->tcp_socket == -1)
    {
        err = crTCPIPErrno( );
        crError( "Couldn't accept client: %s", crTCPIPErrorString( err ) );
    }
    
    if (SocketCreateCallback) {
        SocketCreateCallback(CR_SOCKET_CREATE, conn->tcp_socket);
    }

#ifndef ADDRINFO
    sin_addr = ((struct sockaddr_in *) &addr)->sin_addr;
    host = gethostbyaddr( (char *) &sin_addr, sizeof( sin_addr), AF_INET );
    if (host == NULL )
    {
        char *temp = inet_ntoa( sin_addr );
        conn->hostname = crStrdup( temp );
    }
#else
    err = getnameinfo ( (struct sockaddr *) &addr, addr_length,
                host, sizeof( host),
                NULL, 0, NI_NAMEREQD);
    if ( err )
    {
        err = getnameinfo ( (struct sockaddr *) &addr, addr_length,
                    host, sizeof( host),
                    NULL, 0, NI_NUMERICHOST);
        if ( err )  /* shouldn't ever happen */
            conn->hostname = crStrdup("unknown ?!");
        else
            conn->hostname = crStrdup( host );
    }
#endif
    else
    {
        char *temp;
#ifndef ADDRINFO
        conn->hostname = crStrdup( host->h_name );
#else
        conn->hostname = crStrdup( host );
#endif

        temp = conn->hostname;
        while (*temp && *temp != '.' )
            temp++;
        *temp = '\0';
    }

#ifdef RECV_BAIL_OUT 
    err = sizeof(unsigned int);
    if ( getsockopt( conn->tcp_socket, SOL_SOCKET, SO_RCVBUF,
            (char *) &conn->krecv_buf_size, &err ) )
    {
        conn->krecv_buf_size = 0;   
    }
#endif

    crDebug( "Accepted connection from \"%s\".", conn->hostname );
}


void *
crTCPIPAlloc( CRConnection *conn )
{
    CRTCPIPBuffer *buf;

#ifdef CHROMIUM_THREADSAFE
    crLockMutex(&cr_tcpip.mutex);
#endif

    buf = (CRTCPIPBuffer *) crBufferPoolPop( cr_tcpip.bufpool, conn->buffer_size );

    if ( buf == NULL )
    {
        crDebug("Buffer pool %p was empty; allocated new %d byte buffer.", 
                        cr_tcpip.bufpool,
                        (unsigned int)sizeof(CRTCPIPBuffer) + conn->buffer_size);
        buf = (CRTCPIPBuffer *) 
            crAlloc( sizeof(CRTCPIPBuffer) + conn->buffer_size );
        buf->magic = CR_TCPIP_BUFFER_MAGIC;
        buf->kind  = CRTCPIPMemory;
        buf->pad   = 0;
        buf->allocated = conn->buffer_size;
    }
    
#ifdef CHROMIUM_THREADSAFE
    crUnlockMutex(&cr_tcpip.mutex);
#endif

    return (void *)( buf + 1 );
}


static void
crTCPIPSingleRecv( CRConnection *conn, void *buf, unsigned int len )
{
    crTCPIPReadExact( conn, buf, len );
}


static void
crTCPIPSend( CRConnection *conn, void **bufp,
                         const void *start, unsigned int len )
{
    if ( !conn || conn->type == CR_NO_CONNECTION )
        return;

    if (!bufp) {
        /* We're sending a user-allocated buffer.
         * Simply write the length & the payload and return.
         */
        const int sendable_len = conn->swap ? SWAP32(len) : len;
        crTCPIPWriteExact( conn, &sendable_len, sizeof(len) );
        if (!conn || conn->type == CR_NO_CONNECTION)
            return;
        crTCPIPWriteExact( conn, start, len );
    }
    else {
        /* The region [start .. start + len + 1] lies within a buffer that
         * was allocated with crTCPIPAlloc() and can be put into the free
         * buffer pool when we're done sending it.
         */
        CRTCPIPBuffer *tcpip_buffer;
        unsigned int *lenp;

        tcpip_buffer = (CRTCPIPBuffer *)(*bufp) - 1;

        CRASSERT( tcpip_buffer->magic == CR_TCPIP_BUFFER_MAGIC );

        /* All of the buffers passed to the send function were allocated
         * with crTCPIPAlloc(), which includes a header with a 4 byte
         * pad field, to insure that we always have a place to write
         * the length field, even when start == *bufp.
         */
        lenp = (unsigned int *) start - 1;
        *lenp = conn->swap ? SWAP32(len) : len;

        crTCPIPWriteExact(conn, lenp, len + sizeof(unsigned int));

        /* Reclaim this pointer for reuse */
#ifdef CHROMIUM_THREADSAFE
        crLockMutex(&cr_tcpip.mutex);
#endif
        crBufferPoolPush(cr_tcpip.bufpool, tcpip_buffer, tcpip_buffer->allocated);
#ifdef CHROMIUM_THREADSAFE
        crUnlockMutex(&cr_tcpip.mutex);
#endif
        /* Since the buffer's now in the 'free' buffer pool, the caller can't
         * use it any more.  Setting bufp to NULL will make sure the caller
         * doesn't try to re-use the buffer.
         */
        *bufp = NULL;
    }
}


void
__tcpip_dead_connection( CRConnection *conn )
{
    crDebug( "Dead connection (sock=%d, host=%s), removing from pool",
                   conn->tcp_socket, conn->hostname );
    /* remove from connection pool */
    crTCPIPDoDisconnect( conn );
}


int
__crSelect( int n, fd_set *readfds, int sec, int usec )
{
    for ( ; ; ) 
    { 
        int err, num_ready;

        if (sec || usec)
        {
            /* We re-init everytime for Linux, as it corrupts
             * the timeout structure, but other OS's
             * don't have a problem with it.
             */
            struct timeval timeout;
            timeout.tv_sec = sec;
            timeout.tv_usec = usec;
            num_ready = select( n, readfds, NULL, NULL, &timeout );
        } 
        else
            num_ready = select( n, readfds, NULL, NULL, NULL );

        if ( num_ready >= 0 )
        {
            return num_ready;
        }

        err = crTCPIPErrno( );
        if ( err == EINTR )
        {
            crWarning( "select interrupted by an unblocked signal, trying again" );
        }
        else
        {
            crError( "select failed: %s", crTCPIPErrorString( err ) );
        }
    }
}


void
crTCPIPFree( CRConnection *conn, void *buf )
{
    CRTCPIPBuffer *tcpip_buffer = (CRTCPIPBuffer *) buf - 1;

    CRASSERT( tcpip_buffer->magic == CR_TCPIP_BUFFER_MAGIC );
    conn->recv_credits += tcpip_buffer->len;

    switch ( tcpip_buffer->kind )
    {
        case CRTCPIPMemory:
#ifdef CHROMIUM_THREADSAFE
            crLockMutex(&cr_tcpip.mutex);
#endif
            if (cr_tcpip.bufpool) {
                /* pool may have been deallocated just a bit earlier in response
                 * to a SIGPIPE (Broken Pipe) signal.
                 */
                crBufferPoolPush( cr_tcpip.bufpool, tcpip_buffer, tcpip_buffer->allocated );
            }
#ifdef CHROMIUM_THREADSAFE
            crUnlockMutex(&cr_tcpip.mutex);
#endif
            break;

        case CRTCPIPMemoryBig:
            crFree( tcpip_buffer );
            break;

        default:
            crError( "Weird buffer kind trying to free in crTCPIPFree: %d", tcpip_buffer->kind );
    }
}


/**
 * Check if message type is GATHER.  If so, process it specially.
 * \return number of bytes which were consumed
 */ 
static int
crTCPIPUserbufRecv(CRConnection *conn, CRMessage *msg)
{
    if (msg->header.type == CR_MESSAGE_GATHER) {
        /* grab the offset and the length */
        const int len = 2 * sizeof(unsigned int); /* was unsigned long!!!! */
        unsigned int buf[2];

        if (__tcpip_read_exact(conn->tcp_socket, buf, len) <= 0)
        {
            __tcpip_dead_connection( conn );
        }
        msg->gather.offset = buf[0];
        msg->gather.len = buf[1];

        /* read the rest into the userbuf */
        if (buf[0] + buf[1] > (unsigned int) conn->userbuf_len)
        {
            crDebug("userbuf for Gather Message is too small!");
            return len;
        }

        if (__tcpip_read_exact(conn->tcp_socket,
                                                     conn->userbuf + buf[0], buf[1]) <= 0)
        {
            __tcpip_dead_connection( conn );
        }
        return len + buf[1];
    }
    else {
        return 0;
    }
}


/**
 * Receive the next message on the given connection.
 * If we're being called by crTCPIPRecv(), we already know there's
 * something to receive.
 */
static void
crTCPIPReceiveMessage(CRConnection *conn)
{
    CRMessage *msg;
    CRMessageType cached_type;
    CRTCPIPBuffer *tcpip_buffer;
    unsigned int len, total, leftover;
    const int sock = conn->tcp_socket;

    /* Our gigE board is acting odd. If we recv() an amount
     * less than what is already in the RECVBUF, performance
     * goes into the toilet (somewhere around a factor of 3).
     * This is an ugly hack, but seems to get around whatever
     * funk is being produced  
     *
     * Remember to set your kernel recv buffers to be bigger
     * than the framebuffer 'chunk' you are sending (see
     * sysctl -a | grep rmem) , or this will really have no
     * effect.   --karl 
     */      
#ifdef RECV_BAIL_OUT 
    {
        int inbuf;
        (void) recv(sock, &len, sizeof(len), MSG_PEEK);
        ioctl(conn->tcp_socket, FIONREAD, &inbuf);

        if ((conn->krecv_buf_size > len) && (inbuf < len))
            return;
    }
#endif

    /* this reads the length of the message */
    if ( __tcpip_read_exact( sock, &len, sizeof(len)) <= 0 )
    {
        __tcpip_dead_connection( conn );
        return;
    }

    if (conn->swap)
        len = SWAP32(len);

    CRASSERT( len > 0 );

    if ( len <= conn->buffer_size )
    {
        /* put in pre-allocated buffer */
        tcpip_buffer = (CRTCPIPBuffer *) crTCPIPAlloc( conn ) - 1;
    }
    else
    {
        /* allocate new buffer */
        tcpip_buffer = (CRTCPIPBuffer *) crAlloc( sizeof(*tcpip_buffer) + len );
        tcpip_buffer->magic = CR_TCPIP_BUFFER_MAGIC;
        tcpip_buffer->kind  = CRTCPIPMemoryBig;
        tcpip_buffer->pad   = 0;
    }

    tcpip_buffer->len = len;

    /* if we have set a userbuf, and there is room in it, we probably 
     * want to stick the message into that, instead of our allocated
     * buffer.
     */
    leftover = 0;
    total = len;
    if ((conn->userbuf != NULL)
            && (conn->userbuf_len >= (int) sizeof(CRMessageHeader)))
    {
        leftover = len - sizeof(CRMessageHeader);
        total = sizeof(CRMessageHeader);
    }

    if ( __tcpip_read_exact( sock, tcpip_buffer + 1, total) <= 0 )
    {
        crWarning( "Bad juju: %d %d on socket 0x%x", tcpip_buffer->allocated,
                             total, sock );
        crFree( tcpip_buffer );
        __tcpip_dead_connection( conn );
        return;
    }

    conn->recv_credits -= total;
    conn->total_bytes_recv +=  total;

    msg = (CRMessage *) (tcpip_buffer + 1);
    cached_type = msg->header.type;
    if (conn->swap)
    {
        msg->header.type = (CRMessageType) SWAP32( msg->header.type );
        msg->header.conn_id = (CRMessageType) SWAP32( msg->header.conn_id );
    }
    
    /* if there is still data pending, it should go into the user buffer */
    if (leftover)
    {
        const unsigned int handled = crTCPIPUserbufRecv(conn, msg);

        /* if there is anything left, plop it into the recv_buffer */
        if (leftover - handled)
        {
            if ( __tcpip_read_exact( sock, tcpip_buffer + 1 + total, leftover-handled) <= 0 )
            {
                crWarning( "Bad juju: %d %d", tcpip_buffer->allocated, leftover-handled);
                crFree( tcpip_buffer );
                __tcpip_dead_connection( conn );
                return;
            }
        }

        conn->recv_credits -= handled;
        conn->total_bytes_recv +=  handled;
    }

    crNetDispatchMessage( cr_tcpip.recv_list, conn, msg, len );
#if 0
    crLogRead( len );
#endif

    /* CR_MESSAGE_OPCODES is freed in crserverlib/server_stream.c with crNetFree.
     * OOB messages are the programmer's problem.  -- Humper 12/17/01
     */
    if (cached_type != CR_MESSAGE_OPCODES
            && cached_type != CR_MESSAGE_OOB
            && cached_type != CR_MESSAGE_GATHER) 
    {
        crTCPIPFree( conn, tcpip_buffer + 1 );
    }
}


/**
 * Loop over all TCP/IP connections, reading incoming data on those
 * that are ready.
 */
int
crTCPIPRecv( void )
{
    /* ensure we don't get caught with a new thread connecting */
    const int num_conns = cr_tcpip.num_conns;
    int num_ready, max_fd, i;
    fd_set read_fds;
    int msock = -1; /* assumed mothership socket */
#if CRAPPFAKER_SHOULD_DIE
    int none_left = 1;
#endif

#ifdef CHROMIUM_THREADSAFE
    crLockMutex(&cr_tcpip.recvmutex);
#endif

    /*
     * Loop over all connections and determine which are TCP/IP connections
     * that are ready to be read.
     */
    max_fd = 0;
    FD_ZERO( &read_fds );
    for ( i = 0; i < num_conns; i++ )
    {
        CRConnection *conn = cr_tcpip.conns[i];
        if ( !conn || conn->type == CR_NO_CONNECTION )
            continue;

#if CRAPPFAKER_SHOULD_DIE
        none_left = 0;
#endif

        if ( conn->recv_credits > 0 || conn->type != CR_TCPIP )
        {
            /* 
             * NOTE: may want to always put the FD in the descriptor
             * set so we'll notice broken connections.  Down in the
             * loop that iterates over the ready sockets only peek
             * (MSG_PEEK flag to recv()?) if the connection isn't
             * enabled. 
             */
#if 0 /* not used - see below */
#ifndef ADDRINFO
            struct sockaddr s;
#else
            struct sockaddr_storage s;
#endif
            socklen_t slen;
#endif
            fd_set only_fd; /* testing single fd */
            CRSocket sock = conn->tcp_socket;

            if ( (int) sock + 1 > max_fd )
                max_fd = (int) sock + 1;
            FD_SET( sock, &read_fds );

            /* KLUDGE CITY......
             *
             * With threads there's a race condition between
             * TCPIPRecv and TCPIPSingleRecv when new
             * clients are connecting, thus new mothership
             * connections are also being established.
             * This code below is to check that we're not
             * in a state of accepting the socket without
             * connecting to it otherwise we fail with
             * ENOTCONN later. But, this is really a side
             * effect of this routine catching a motherships
             * socket connection and reading data that wasn't
             * really meant for us. It was really meant for
             * TCPIPSingleRecv. So, if we detect an
             * in-progress connection we set the msock id
             * so that we can assume the motherships socket
             * and skip over them.
             */
            
            FD_ZERO(&only_fd);
            FD_SET( sock, &only_fd );

#if 0 /* Disabled on Dec 13 2005 by BrianP - seems to cause trouble */
            slen = sizeof( s );
            /* Check that the socket is REALLY connected */
            /* Doesn't this call introduce some inefficiency??? (BP) */
            if (getpeername(sock, (struct sockaddr *) &s, &slen) < 0) {
                /* Another kludge.....
                 * If we disconnect a socket without writing
                 * anything to it, we end up here. Detect
                 * the disconnected socket by checking if
                 * we've ever sent something and then
                 * disconnect it.
                 * 
                 * I think the networking layer needs
                 * a bit of a re-write.... Alan.
                 */
                if (conn->total_bytes_sent > 0) {
                    crTCPIPDoDisconnect( conn );
                }
                FD_CLR(sock, &read_fds);
                msock = sock;
            }
#endif
            /* 
             * Nope, that last socket we've just caught in
             * the connecting phase. We've probably found
             * a mothership connection here, and we shouldn't
             * process it 
             */
            if ((int)sock == msock+1)
                FD_CLR(sock, &read_fds);
        }
    }

#if CRAPPFAKER_SHOULD_DIE
    if (none_left) {
        /*
         * Caught no more connections.
         * Review this if we want to try 
         * restarting crserver's dynamically.
         */
#ifdef CHROMIUM_THREADSAFE
        crUnlockMutex(&cr_tcpip.recvmutex);
#endif
        crError("No more connections to process, terminating...\n");
        exit(0); /* shouldn't get here */
    }
#endif

    if (!max_fd) {
#ifdef CHROMIUM_THREADSAFE
        crUnlockMutex(&cr_tcpip.recvmutex);
#endif
        return 0;
    }

    if ( num_conns ) {
        num_ready = __crSelect( max_fd, &read_fds, 0, 500 );
    }
    else {
        crWarning( "Waiting for first connection..." );
        num_ready = __crSelect( max_fd, &read_fds, 0, 0 );
    }

    if ( num_ready == 0 ) {
#ifdef CHROMIUM_THREADSAFE
        crUnlockMutex(&cr_tcpip.recvmutex);
#endif
        return 0;
    }

    /*
     * Loop over connections, receive data on the TCP/IP connections that
     * we determined are ready above.
     */
    for ( i = 0; i < num_conns; i++ )
    {
        CRConnection *conn = cr_tcpip.conns[i];
        CRSocket sock;

        if ( !conn || conn->type == CR_NO_CONNECTION )
            continue;

        /* Added by Samuel Thibault during TCP/IP / UDP code factorization */
        if ( conn->type != CR_TCPIP )
            continue;

        sock = conn->tcp_socket;
        if ( !FD_ISSET( sock, &read_fds ) )
            continue;

        if (conn->threaded)
            continue;

        crTCPIPReceiveMessage(conn);
    }

#ifdef CHROMIUM_THREADSAFE
    crUnlockMutex(&cr_tcpip.recvmutex);
#endif

    return 1;
}


static void
crTCPIPHandleNewMessage( CRConnection *conn, CRMessage *msg, unsigned int len )
{
    CRTCPIPBuffer *buf = ((CRTCPIPBuffer *) msg) - 1;

    /* build a header so we can delete the message later */
    buf->magic = CR_TCPIP_BUFFER_MAGIC;
    buf->kind  = CRTCPIPMemory;
    buf->len   = len;
    buf->pad   = 0;

    crNetDispatchMessage( cr_tcpip.recv_list, conn, msg, len );
}


static void
crTCPIPInstantReclaim( CRConnection *conn, CRMessage *mess )
{
    crTCPIPFree( conn, mess );
}


void
crTCPIPInit( CRNetReceiveFuncList *rfl, CRNetCloseFuncList *cfl,
                         unsigned int mtu )
{
    (void) mtu;

    cr_tcpip.recv_list = rfl;
    cr_tcpip.close_list = cfl;
    if ( cr_tcpip.initialized )
    {
        return;
    }

    cr_tcpip.initialized = 1;

    cr_tcpip.num_conns = 0;
    cr_tcpip.conns     = NULL;
    
    cr_tcpip.server_sock    = -1;

#ifdef CHROMIUM_THREADSAFE
    crInitMutex(&cr_tcpip.mutex);
    crInitMutex(&cr_tcpip.recvmutex);
#endif
    cr_tcpip.bufpool = crBufferPoolInit(16);
}


/**
 * The function that actually connects.  This should only be called by clients 
 * Servers have another way to set up the socket.
 */
int
crTCPIPDoConnect( CRConnection *conn )
{
    int err;
#ifndef ADDRINFO
    struct sockaddr_in servaddr;
    struct hostent *hp;
    int i;

    conn->tcp_socket = socket( AF_INET, SOCK_STREAM, 0 );
    if ( conn->tcp_socket < 0 )
    {
        int err = crTCPIPErrno( );
        crWarning( "socket error: %s", crTCPIPErrorString( err ) );
        cr_tcpip.conns[conn->index] = NULL; /* remove from table */
        return 0;
    }

    if (SocketCreateCallback) {
        SocketCreateCallback(CR_SOCKET_CREATE, conn->tcp_socket);
    }

    /* Set up the socket the way *we* want. */
    spankSocket( conn->tcp_socket );

    /* Standard Berkeley sockets mumbo jumbo */
    hp = gethostbyname( conn->hostname );
    if ( !hp )
    {
        crWarning( "Unknown host: \"%s\"", conn->hostname );
        cr_tcpip.conns[conn->index] = NULL; /* remove from table */
        return 0;
    }

    crMemset( &servaddr, 0, sizeof(servaddr) );
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons( (short) conn->port );

    crMemcpy((char *) &servaddr.sin_addr, hp->h_addr, sizeof(servaddr.sin_addr));
#else
    char port_s[NI_MAXSERV];
    struct addrinfo *res,*cur;
    struct addrinfo hints;

    sprintf(port_s, "%u", (short unsigned) conn->port);

    crMemset(&hints, 0, sizeof(hints));
    hints.ai_family = PF;
    hints.ai_socktype = SOCK_STREAM;

    err = getaddrinfo( conn->hostname, port_s, &hints, &res);
    if ( err )
    {
        crWarning( "Unknown host: \"%s\": %s", conn->hostname, gai_strerror(err) );
        cr_tcpip.conns[conn->index] = NULL; /* remove from table */
        return 0;
    }
#endif

    /* If brokered, we'll contact the mothership to broker the network
     * connection.  We'll send the mothership our hostname, the port and
     * our endianness and will get in return a connection ID number.
     */
    if (conn->broker)
    {
        crError("There shouldn't be any brokered connections in VirtualBox");
    }

#ifndef ADDRINFO
    for (i=1;i;)
#else
    for (cur=res;cur;)
#endif
    {
#ifndef ADDRINFO

#ifdef RECV_BAIL_OUT        
        err = sizeof(unsigned int);
        if ( getsockopt( conn->tcp_socket, SOL_SOCKET, SO_RCVBUF,
                (char *) &conn->krecv_buf_size, &err ) )
        {
            conn->krecv_buf_size = 0;   
        }
#endif
        if ( !connect( conn->tcp_socket, (struct sockaddr *) &servaddr,
                    sizeof(servaddr) ) )
            return 1;
#else

        conn->tcp_socket = socket( cur->ai_family, cur->ai_socktype, cur->ai_protocol );
        if ( conn->tcp_socket < 0 )
        {
            int err = crTCPIPErrno( );
            if (err != EAFNOSUPPORT)
                crWarning( "socket error: %s, trying another way", crTCPIPErrorString( err ) );
            cur=cur->ai_next;
            continue;
        }

        if (SocketCreateCallback) {
            SocketCreateCallback(CR_SOCKET_CREATE, conn->tcp_socket);
        }

        err = 1;
        setsockopt(conn->tcp_socket, SOL_SOCKET, SO_REUSEADDR,  &err, sizeof(int));

        /* Set up the socket the way *we* want. */
        spankSocket( conn->tcp_socket );

#if RECV_BAIL_OUT       
        err = sizeof(unsigned int);
        if ( getsockopt( conn->tcp_socket, SOL_SOCKET, SO_RCVBUF,
                (char *) &conn->krecv_buf_size, &err ) )
        {
            conn->krecv_buf_size = 0;   
        }
#endif

        if ( !connect( conn->tcp_socket, cur->ai_addr, cur->ai_addrlen ) ) {
            freeaddrinfo(res);
            return 1;
        }
#endif

        err = crTCPIPErrno( );
        if ( err == EADDRINUSE || err == ECONNREFUSED )
            crWarning( "Connection refused to %s:%d, %s",
                    conn->hostname, conn->port, crTCPIPErrorString( err ) );

        else if ( err == EINTR )
        {
            crWarning( "connection to %s:%d "
                    "interrupted, trying again", conn->hostname, conn->port );
            continue;
        }
        else
            crWarning( "Couldn't connect to %s:%d, %s",
                    conn->hostname, conn->port, crTCPIPErrorString( err ) );
        crCloseSocket( conn->tcp_socket );
#ifndef ADDRINFO
        i=0;
#else
        cur=cur->ai_next;
#endif
    }
#ifdef ADDRINFO
    freeaddrinfo(res);
    crWarning( "Couldn't find any suitable way to connect to %s", conn->hostname );
#endif
    cr_tcpip.conns[conn->index] = NULL; /* remove from table */
    return 0;
}


/**
 * Disconnect this connection, but don't free(conn).
 */
void
crTCPIPDoDisconnect( CRConnection *conn )
{
    int num_conns = cr_tcpip.num_conns;
    int none_left = 1;
    int i;

    /* If this connection has already been disconnected (e.g.
     * if the connection has been lost and disabled through
     * a call to __tcpip_dead_connection(), which will then
     * call this routine), don't disconnect it again; if we
     * do, and if a new valid connection appears in the same
     * slot (conn->index), we'll effectively disable the
     * valid connection by mistake, leaving us unable to
     * receive inbound data on that connection.
     */
    if (conn->type == CR_NO_CONNECTION) return;

    crCloseSocket( conn->tcp_socket );
    if (conn->hostname) {
        crFree(conn->hostname);
        conn->hostname = NULL;
    }
    conn->tcp_socket = 0;
    conn->type = CR_NO_CONNECTION;
    cr_tcpip.conns[conn->index] = NULL;

    /* see if any connections remain */
    for (i = 0; i < num_conns; i++) 
    {
        if ( cr_tcpip.conns[i] && cr_tcpip.conns[i]->type != CR_NO_CONNECTION )
            none_left = 0; /* found a live connection */
    }

#if 0 /* disabled on 13 Dec 2005 by BrianP - this prevents future client
             * connections after the last one goes away.
             */
    if (none_left && cr_tcpip.server_sock != -1)
    {
        crDebug("Closing master socket (probably quitting).");
        crCloseSocket( cr_tcpip.server_sock );
        cr_tcpip.server_sock = -1;
#ifdef CHROMIUM_THREADSAFE
        crFreeMutex(&cr_tcpip.mutex);
        crFreeMutex(&cr_tcpip.recvmutex);
#endif
        crBufferPoolFree( cr_tcpip.bufpool );
        cr_tcpip.bufpool = NULL;
        last_port = 0;
        cr_tcpip.initialized = 0;
    }
#endif
}


/**
 * Initialize a CRConnection for tcp/ip.  This is called via the
 * InitConnection() function (and from the UDP module).
 */
void
crTCPIPConnection( CRConnection *conn )
{
    int i, found = 0;
    int n_bytes;

    CRASSERT( cr_tcpip.initialized );

    conn->type = CR_TCPIP;
    conn->Alloc = crTCPIPAlloc;
    conn->Send = crTCPIPSend;
    conn->SendExact = crTCPIPWriteExact;
    conn->Recv = crTCPIPSingleRecv;
    conn->RecvMsg = crTCPIPReceiveMessage;
    conn->Free = crTCPIPFree;
    conn->Accept = crTCPIPAccept;
    conn->Connect = crTCPIPDoConnect;
    conn->Disconnect = crTCPIPDoDisconnect;
    conn->InstantReclaim = crTCPIPInstantReclaim;
    conn->HandleNewMessage = crTCPIPHandleNewMessage;
    conn->index = cr_tcpip.num_conns;
    conn->sizeof_buffer_header = sizeof( CRTCPIPBuffer );
    conn->actual_network = 1;

    conn->krecv_buf_size = 0;

    /* Find a free slot */
    for (i = 0; i < cr_tcpip.num_conns; i++) {
        if (cr_tcpip.conns[i] == NULL) {
            conn->index = i;
            cr_tcpip.conns[i] = conn;
            found = 1;
            break;
        }
    }
    
    /* Realloc connection stack if we couldn't find a free slot */
    if (found == 0) {
        n_bytes = ( cr_tcpip.num_conns + 1 ) * sizeof(*cr_tcpip.conns);
        crRealloc( (void **) &cr_tcpip.conns, n_bytes );
        cr_tcpip.conns[cr_tcpip.num_conns++] = conn;
    }
}


int crGetHostname( char *buf, unsigned int len )
{
    const char *override;
    int ret;

    override = crGetenv("CR_HOSTNAME");
    if (override)
    {
        crStrncpy(buf, override, len);
        ret = 0;    
    }
    else
        ret = gethostname( buf, len );
    return ret;
}


CRConnection** crTCPIPDump( int *num )
{
    *num = cr_tcpip.num_conns;

    return cr_tcpip.conns;
}
