/*---------------------------------------------------------------------------*/
/*                                                                           */
/* CSimpleSocket.cpp - CSimpleSocket Implementation                          */
/*                                                                           */
/* Author : Mark Carrier (mark@carrierlabs.com)                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/
/* Copyright (c) 2007-2009 CarrierLabs, LLC.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 4. The name "CarrierLabs" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    mark@carrierlabs.com.
 *
 * THIS SOFTWARE IS PROVIDED BY MARK CARRIER ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL MARK CARRIER OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *----------------------------------------------------------------------------*/
#include "SimpleSocket.h"

CSimpleSocket::CSimpleSocket(CSocketType nType) :
    m_socket(INVALID_SOCKET),
    m_socketErrno(CSimpleSocket::SocketInvalidSocket),
    m_pBuffer(NULL), m_nBufferSize(0), m_nSocketDomain(AF_INET),
    m_nSocketType(SocketTypeInvalid), m_nBytesReceived(-1),
    m_nBytesSent(-1), m_nFlags(0),
    m_bIsBlocking(true)
{
    SetConnectTimeout(1, 0);
    memset(&m_stRecvTimeout, 0, sizeof(struct timeval));
    memset(&m_stSendTimeout, 0, sizeof(struct timeval));
    memset(&m_stLinger, 0, sizeof(struct linger));

    switch(nType)
    {
        //----------------------------------------------------------------------
        // Declare socket type stream - TCP
        //----------------------------------------------------------------------
    case CSimpleSocket::SocketTypeTcp:
    {
        m_nSocketDomain = AF_INET;
        m_nSocketType = CSimpleSocket::SocketTypeTcp;
        break;
    }
    case CSimpleSocket::SocketTypeTcp6:
    {
        m_nSocketDomain = AF_INET6;
        m_nSocketType = CSimpleSocket::SocketTypeTcp6;
        break;
    }
    //----------------------------------------------------------------------
    // Declare socket type datagram - UDP
    //----------------------------------------------------------------------
    case CSimpleSocket::SocketTypeUdp:
    {
        m_nSocketDomain = AF_INET;
        m_nSocketType = CSimpleSocket::SocketTypeUdp;
        break;
    }
    case CSimpleSocket::SocketTypeUdp6:
    {
        m_nSocketDomain = AF_INET6;
        m_nSocketType = CSimpleSocket::SocketTypeUdp6;
        break;
    }
    //----------------------------------------------------------------------
    // Declare socket type raw Ethernet - Ethernet
    //----------------------------------------------------------------------
    case CSimpleSocket::SocketTypeRaw:
    {
#if defined(_LINUX) && !defined(_DARWIN)
        m_nSocketDomain = AF_PACKET;
        m_nSocketType = CSimpleSocket::SocketTypeRaw;
#endif
#ifdef _WIN32
        m_nSocketType = CSimpleSocket::SocketTypeInvalid;
#endif
        break;
    }
    default:
        m_nSocketType = CSimpleSocket::SocketTypeInvalid;
        break;
    }
}

CSimpleSocket::CSimpleSocket(CSimpleSocket &socket)
{
    m_pBuffer = new uint8_t[socket.m_nBufferSize];
    m_nBufferSize = socket.m_nBufferSize;
    memcpy(m_pBuffer, socket.m_pBuffer, socket.m_nBufferSize);
}

CSimpleSocket *CSimpleSocket::operator=(CSimpleSocket &socket)
{
    if (m_nBufferSize != socket.m_nBufferSize)
    {
        delete m_pBuffer;
        m_pBuffer = new uint8_t[socket.m_nBufferSize];
        m_nBufferSize = socket.m_nBufferSize;
        memcpy(m_pBuffer, socket.m_pBuffer, socket.m_nBufferSize);
    }

    return this;
}


//------------------------------------------------------------------------------
//
// Initialize() - Initialize socket class
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Initialize()
{
    errno = CSimpleSocket::SocketSuccess;

#ifdef _WIN32
    //-------------------------------------------------------------------------
    // Data structure containing general Windows Sockets Info
    //-------------------------------------------------------------------------
    memset(&m_hWSAData, 0, sizeof(m_hWSAData));
    WSAStartup(MAKEWORD(2, 0), &m_hWSAData);
#endif

    //-------------------------------------------------------------------------
    // Create the basic Socket Handle
    //-------------------------------------------------------------------------
    m_timer.Initialize();
    m_timer.SetStartTime();
    m_socket = socket(m_nSocketDomain, m_nSocketType, 0);
    m_timer.SetEndTime();

    TranslateSocketError();

    return (IsSocketValid());
}


//------------------------------------------------------------------------------
//
// BindInterface()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::BindInterface(const char *pInterface)
{
    bool           bRetVal = false;
    struct in_addr stInterfaceAddr;

    if (GetMulticast() == true)
    {
        if (pInterface)
        {
            stInterfaceAddr.s_addr= inet_addr(pInterface);
            if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_IF, &stInterfaceAddr, sizeof(stInterfaceAddr)) == SocketSuccess)
            {
                bRetVal = true;
            }
        }
    }
    else
    {
        SetSocketError(CSimpleSocket::SocketProtocolError);
    }

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// SetMulticast()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetMulticast(bool bEnable, uint8_t multicastTTL)
{
    bool bRetVal = false;

    if (GetSocketType() == CSimpleSocket::SocketTypeUdp)
    {
        m_bIsMulticast = bEnable;
        if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&multicastTTL, sizeof(multicastTTL)) == SocketError)
        {
            TranslateSocketError();
            bRetVal = false;
        }
        else
        {
            bRetVal = true;
        }
    }
    else
    {
        m_socketErrno = CSimpleSocket::SocketProtocolError;
    }

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// SetSocketDscp()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetSocketDscp(int32_t nDscp)
{
    bool  bRetVal = true;
    int32_t nTempVal = nDscp;

    nTempVal <<= 4;
    nTempVal /= 4;

    if (IsSocketValid())
    {
        if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_TOS, &nTempVal, sizeof(nTempVal)) == SocketError)
        {
            TranslateSocketError();
            bRetVal = false;
        }
    }

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// GetSocketDscp()
//
//------------------------------------------------------------------------------
int32_t CSimpleSocket::GetSocketDscp(void)
{
    int32_t      nTempVal = 0;
    socklen_t  nLen = 0;

    if (IsSocketValid())
    {
        if (GETSOCKOPT(m_socket, IPPROTO_IP, IP_TOS, &nTempVal, &nLen) == SocketError)
        {
            TranslateSocketError();
        }

        nTempVal *= 4;
        nTempVal >>= 4;
    }

    return nTempVal;
}


//------------------------------------------------------------------------------
//
// GetWindowSize()
//
//------------------------------------------------------------------------------
uint32_t CSimpleSocket::GetWindowSize(uint32_t nOptionName)
{
    uint32_t nTcpWinSize = 0;

    //-------------------------------------------------------------------------
    // no socket given, return system default allocate our own new socket
    //-------------------------------------------------------------------------
    if (m_socket != CSimpleSocket::SocketError)
    {
        socklen_t nLen = sizeof(nTcpWinSize);

        //---------------------------------------------------------------------
        // query for buffer size
        //---------------------------------------------------------------------
        GETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nTcpWinSize, &nLen);
        TranslateSocketError();
    }
    else
    {
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
    }

    return nTcpWinSize;
}


//------------------------------------------------------------------------------
//
// SetWindowSize()
//
//------------------------------------------------------------------------------
uint32_t CSimpleSocket::SetWindowSize(uint32_t nOptionName, uint32_t nWindowSize)
{
    //-------------------------------------------------------------------------
    // no socket given, return system default allocate our own new socket
    //-------------------------------------------------------------------------
    if (m_socket != CSimpleSocket::SocketError)
    {
        SETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nWindowSize, sizeof(nWindowSize));
        TranslateSocketError();
    }
    else
    {
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
    }

    return nWindowSize;
}


//------------------------------------------------------------------------------
//
// DisableNagleAlgorithm()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::DisableNagleAlgoritm()
{
    bool  bRetVal = false;
    int32_t nTcpNoDelay = 1;

    //----------------------------------------------------------------------
    // Set TCP NoDelay flag to true
    //----------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(int32_t)) == 0)
    {
        bRetVal = true;
    }

    TranslateSocketError();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// EnableNagleAlgorithm()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::EnableNagleAlgoritm()
{
    bool  bRetVal = false;
    int32_t nTcpNoDelay = 0;

    //----------------------------------------------------------------------
    // Set TCP NoDelay flag to false
    //----------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(int32_t)) == 0)
    {
        bRetVal = true;
    }

    TranslateSocketError();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// Send() - Send data on a valid socket
//
//------------------------------------------------------------------------------
int32_t CSimpleSocket::Send(const uint8_t *pBuf, size_t bytesToSend)
{
    SetSocketError(SocketSuccess);
    m_nBytesSent = 0;

    switch(m_nSocketType)
    {
    case CSimpleSocket::SocketTypeTcp:
    {
        if (IsSocketValid())
        {
            if ((bytesToSend > 0) && (pBuf != NULL))
            {
                m_timer.Initialize();
                m_timer.SetStartTime();

                //---------------------------------------------------------
                // Check error condition and attempt to resend if call
                // was interrupted by a signal.
                //---------------------------------------------------------
                do
                {
                    m_nBytesSent = SEND(m_socket, pBuf, bytesToSend, 0);
                    TranslateSocketError();
                } while (GetSocketError() == CSimpleSocket::SocketInterrupted);

                m_timer.SetEndTime();
            }
        }
        break;
    }
    case CSimpleSocket::SocketTypeUdp:
    {
        if (IsSocketValid())
        {
            if ((bytesToSend > 0) && (pBuf != NULL))
            {
                m_timer.Initialize();
                m_timer.SetStartTime();

                //---------------------------------------------------------
                // Check error condition and attempt to resend if call
                // was interrupted by a signal.
                //---------------------------------------------------------
                //                    if (GetMulticast())
                //                    {
                //                        do
                //                        {
                //                            m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stMulticastGroup,
                //                                                  sizeof(m_stMulticastGroup));
                //                            TranslateSocketError();
                //                        } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
                //                    }
                //                    else
                {
                    do
                    {
                        m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stServerSockaddr, sizeof(m_stServerSockaddr));
                        TranslateSocketError();
                    } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
                }

                m_timer.SetEndTime();
            }
        }
        break;
    }
    default:
        break;
    }

    return m_nBytesSent;
}


//------------------------------------------------------------------------------
//
// Close() - Close socket and free up any memory allocated for the socket
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Close(void)
{
    bool bRetVal = false;

    //--------------------------------------------------------------------------
    // delete internal buffer
    //--------------------------------------------------------------------------
    if (m_pBuffer != NULL)
    {
        delete [] m_pBuffer;
        m_pBuffer = NULL;
    }

    //--------------------------------------------------------------------------
    // if socket handle is currently valid, close and then invalidate
    //--------------------------------------------------------------------------
    if (IsSocketValid())
    {
        if (CLOSE(m_socket) != CSimpleSocket::SocketError)
        {
            m_socket = INVALID_SOCKET;
            bRetVal = true;
        }
    }

    TranslateSocketError();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// Shtudown()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Shutdown(CShutdownMode nShutdown)
{
    CSocketError nRetVal = SocketEunknown;

    nRetVal = (CSocketError)shutdown(m_socket, nShutdown);
    TranslateSocketError();

    return (nRetVal == CSimpleSocket::SocketSuccess) ? true: false;
}


//------------------------------------------------------------------------------
//
// Flush()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Flush()
{
    int32_t nTcpNoDelay = 1;
    int32_t nCurFlags = 0;
    uint8_t tmpbuf = 0;
    bool  bRetVal = false;

    //--------------------------------------------------------------------------
    // Get the current setting of the TCP_NODELAY flag.
    //--------------------------------------------------------------------------
    if (GETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nCurFlags, sizeof(int32_t)) == 0)
    {
        //----------------------------------------------------------------------
        // Set TCP NoDelay flag
        //----------------------------------------------------------------------
        if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(int32_t)) == 0)
        {
            //------------------------------------------------------------------
            // Send empty byte stream to flush the TCP send buffer
            //------------------------------------------------------------------
            if (Send(&tmpbuf, 0) != CSimpleSocket::SocketError)
            {
                bRetVal = true;
            }

            TranslateSocketError();
        }

        //----------------------------------------------------------------------
        // Reset the TCP_NODELAY flag to original state.
        //----------------------------------------------------------------------
        SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nCurFlags, sizeof(int32_t));
    }

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// Writev -
//
//------------------------------------------------------------------------------
int32_t CSimpleSocket::Writev(const struct iovec *pVector, size_t nCount)
{
    int32_t nBytes     = 0;
    int32_t nBytesSent = 0;
    int32_t i          = 0;

    //--------------------------------------------------------------------------
    // Send each buffer as a separate send, windows does not support this
    // function call.
    //--------------------------------------------------------------------------
    for (i = 0; i < (int32_t)nCount; i++)
    {
        if ((nBytes = Send((uint8_t *)pVector[i].iov_base, pVector[i].iov_len)) == CSimpleSocket::SocketError)
        {
            break;
        }

        nBytesSent += nBytes;
    }

    if (i > 0)
    {
        Flush();
    }

    return nBytesSent;
}


//------------------------------------------------------------------------------
//
// Send() - Send data on a valid socket via a vector of buffers.
//
//------------------------------------------------------------------------------
int32_t CSimpleSocket::Send(const struct iovec *sendVector, int32_t nNumItems)
{
    SetSocketError(SocketSuccess);
    m_nBytesSent = 0;

    if ((m_nBytesSent = WRITEV(m_socket, sendVector, nNumItems)) == CSimpleSocket::SocketError)
    {
        TranslateSocketError();
    }

    return m_nBytesSent;
}


//------------------------------------------------------------------------------
//
// SetReceiveTimeout()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetReceiveTimeout(int32_t nRecvTimeoutSec, int32_t nRecvTimeoutUsec)
{
    bool bRetVal = true;

    memset(&m_stRecvTimeout, 0, sizeof(struct timeval));

    m_stRecvTimeout.tv_sec = nRecvTimeoutSec;
    m_stRecvTimeout.tv_usec = nRecvTimeoutUsec;

    //--------------------------------------------------------------------------
    // Sanity check to make sure the options are supported!
    //--------------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_RCVTIMEO, &m_stRecvTimeout,
                   sizeof(struct timeval)) == CSimpleSocket::SocketError)
    {
        bRetVal = false;
        TranslateSocketError();
    }

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// SetSendTimeout()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetSendTimeout(int32_t nSendTimeoutSec, int32_t nSendTimeoutUsec)
{
    bool bRetVal = true;

    memset(&m_stSendTimeout, 0, sizeof(struct timeval));
    m_stSendTimeout.tv_sec = nSendTimeoutSec;
    m_stSendTimeout.tv_usec = nSendTimeoutUsec;

    //--------------------------------------------------------------------------
    // Sanity check to make sure the options are supported!
    //--------------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_SNDTIMEO, &m_stSendTimeout,
                   sizeof(struct timeval)) == CSimpleSocket::SocketError)
    {
        bRetVal = false;
        TranslateSocketError();
    }

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// SetOptionReuseAddr()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetOptionReuseAddr()
{
    bool  bRetVal = false;
    int32_t nReuse  = IPTOS_LOWDELAY;

    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&nReuse, sizeof(int32_t)) == 0)
    {
        bRetVal = true;
    }

    TranslateSocketError();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// SetOptionLinger()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetOptionLinger(bool bEnable, uint16_t nTime)
{
    bool bRetVal = false;

    m_stLinger.l_onoff = (bEnable == true) ? 1: 0;
    m_stLinger.l_linger = nTime;

    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_LINGER, &m_stLinger, sizeof(m_stLinger)) == 0)
    {
        bRetVal = true;
    }

    TranslateSocketError();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// Receive() - Attempts to receive a block of data on an established
//             connection.    Data is received in an internal buffer managed
//             by the class.  This buffer is only valid until the next call
//             to Receive(), a call to Close(), or until the object goes out
//             of scope.
//
//------------------------------------------------------------------------------
int32_t CSimpleSocket::Receive(int32_t nMaxBytes, uint8_t * pBuffer )
{
    m_nBytesReceived = 0;

    //--------------------------------------------------------------------------
    // If the socket is invalid then return false.
    //--------------------------------------------------------------------------
    if (IsSocketValid() == false)
    {
        return m_nBytesReceived;
    }

    uint8_t * pWorkBuffer = pBuffer;
    if ( pBuffer == NULL )
    {
        //--------------------------------------------------------------------------
        // Free existing buffer and allocate a new buffer the size of
        // nMaxBytes.
        //--------------------------------------------------------------------------
        if ((m_pBuffer != NULL) && (nMaxBytes != m_nBufferSize))
        {
            delete [] m_pBuffer;
            m_pBuffer = NULL;
        }

        //--------------------------------------------------------------------------
        // Allocate a new internal buffer to receive data.
        //--------------------------------------------------------------------------
        if (m_pBuffer == NULL)
        {
            m_nBufferSize = nMaxBytes;
            m_pBuffer = new uint8_t[nMaxBytes];
        }

        pWorkBuffer = m_pBuffer;
    }

    SetSocketError(SocketSuccess);

    m_timer.Initialize();
    m_timer.SetStartTime();

    switch (m_nSocketType)
    {
        //----------------------------------------------------------------------
        // If zero bytes are received, then return.  If SocketERROR is
        // received, free buffer and return CSocket::SocketError (-1) to caller.
        //----------------------------------------------------------------------
    case CSimpleSocket::SocketTypeTcp:
    {
        do
        {
            m_nBytesReceived = RECV(m_socket, (pWorkBuffer + m_nBytesReceived),
                                    nMaxBytes, m_nFlags);
            TranslateSocketError();
        } while ((GetSocketError() == CSimpleSocket::SocketInterrupted));

        break;
    }
    case CSimpleSocket::SocketTypeUdp:
    {
        uint32_t srcSize;

        srcSize = sizeof(struct sockaddr_in);

        if (GetMulticast() == true)
        {
            do
            {
                m_nBytesReceived = RECVFROM(m_socket, pWorkBuffer, nMaxBytes, 0,
                                            &m_stMulticastGroup, &srcSize);
                TranslateSocketError();
            } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
        }
        else
        {
            do
            {
                m_nBytesReceived = RECVFROM(m_socket, pWorkBuffer, nMaxBytes, 0,
                                            &m_stClientSockaddr, &srcSize);
                TranslateSocketError();
            } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
        }

        break;
    }
    default:
        break;
    }

    m_timer.SetEndTime();
    TranslateSocketError();

    //--------------------------------------------------------------------------
    // If we encounter an error translate the error code and return.  One
    // possible error code could be EAGAIN (EWOULDBLOCK) if the socket is
    // non-blocking.  This does not mean there is an error, but no data is
    // yet available on the socket.
    //--------------------------------------------------------------------------
    if (m_nBytesReceived == CSimpleSocket::SocketError)
    {
        if (m_pBuffer != NULL)
        {
            delete [] m_pBuffer;
            m_pBuffer = NULL;
        }
    }

    return m_nBytesReceived;
}


//------------------------------------------------------------------------------
//
// SetNonblocking()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetNonblocking(void)
{
    int32_t  nCurFlags;

#if _WIN32
    nCurFlags = 1;

    if (ioctlsocket(m_socket, FIONBIO, (ULONG *)&nCurFlags) != 0)
    {
        TranslateSocketError();
        return false;
    }
#else
    if ((nCurFlags = fcntl(m_socket, F_GETFL)) < 0)
    {
        TranslateSocketError();
        return false;
    }

    nCurFlags |= O_NONBLOCK;

    if (fcntl(m_socket, F_SETFL, nCurFlags) != 0)
    {
        TranslateSocketError();
        return false;
    }
#endif

    m_bIsBlocking = false;

    return true;
}


//------------------------------------------------------------------------------
//
// SetBlocking()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetBlocking(void)
{
    int32_t nCurFlags;

#if _WIN32
    nCurFlags = 0;

    if (ioctlsocket(m_socket, FIONBIO, (ULONG *)&nCurFlags) != 0)
    {
        return false;
    }
#else
    if ((nCurFlags = fcntl(m_socket, F_GETFL)) < 0)
    {
        TranslateSocketError();
        return false;
    }

    nCurFlags &= (~O_NONBLOCK);

    if (fcntl(m_socket, F_SETFL, nCurFlags) != 0)
    {
        TranslateSocketError();
        return false;
    }
#endif
    m_bIsBlocking = true;

    return true;
}


//------------------------------------------------------------------------------
//
// SendFile() - stands-in for system provided sendfile
//
//------------------------------------------------------------------------------
int32_t CSimpleSocket::SendFile(int32_t nOutFd, int32_t nInFd, off_t *pOffset, int32_t nCount)
{
    int32_t  nOutCount = CSimpleSocket::SocketError;

    static char szData[SOCKET_SENDFILE_BLOCKSIZE];
    int32_t       nInCount = 0;

    if (lseek(nInFd, *pOffset, SEEK_SET) == -1)
    {
        return -1;
    }

    while (nOutCount < nCount)
    {
        nInCount = (nCount - nOutCount) < SOCKET_SENDFILE_BLOCKSIZE ? (nCount - nOutCount) : SOCKET_SENDFILE_BLOCKSIZE;

        if ((read(nInFd, szData, nInCount)) != (int32_t)nInCount)
        {
            return -1;
        }

        if ((SEND(nOutFd, szData, nInCount, 0)) != (int32_t)nInCount)
        {
            return -1;
        }

        nOutCount += nInCount;
    }

    *pOffset += nOutCount;

    TranslateSocketError();

    return nOutCount;
}


//------------------------------------------------------------------------------
//
// TranslateSocketError() -
//
//------------------------------------------------------------------------------
void CSimpleSocket::TranslateSocketError(void)
{
#if defined(_LINUX) || defined(_DARWIN)
    switch (errno)
    {
    case EXIT_SUCCESS:
        SetSocketError(CSimpleSocket::SocketSuccess);
        break;
    case ENOTCONN:
        SetSocketError(CSimpleSocket::SocketNotconnected);
        break;
    case ENOTSOCK:
    case EBADF:
    case EACCES:
    case EAFNOSUPPORT:
    case EMFILE:
    case ENFILE:
    case ENOBUFS:
    case ENOMEM:
    case EPROTONOSUPPORT:
    case EPIPE:
    case EOPNOTSUPP:
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
        break;
    case ECONNREFUSED :
        SetSocketError(CSimpleSocket::SocketConnectionRefused);
        break;
    case ETIMEDOUT:
        SetSocketError(CSimpleSocket::SocketTimedout);
        break;
    case EINPROGRESS:
        SetSocketError(CSimpleSocket::SocketEinprogress);
        break;
    case EWOULDBLOCK:
        //        case EAGAIN:
        SetSocketError(CSimpleSocket::SocketEwouldblock);
        break;
    case EINTR:
        SetSocketError(CSimpleSocket::SocketInterrupted);
        break;
    case ECONNABORTED:
        SetSocketError(CSimpleSocket::SocketConnectionAborted);
        break;
    case EINVAL:
    case EPROTO:
        SetSocketError(CSimpleSocket::SocketProtocolError);
        break;
    case EPERM:
        SetSocketError(CSimpleSocket::SocketFirewallError);
        break;
    case EFAULT:
        SetSocketError(CSimpleSocket::SocketInvalidSocketBuffer);
        break;
    case ECONNRESET:
    case ENOPROTOOPT:
        SetSocketError(CSimpleSocket::SocketConnectionReset);
        break;
    case EADDRINUSE:
        SetSocketError(CSimpleSocket::SocketAddressInUse);
        break;
    default:
        SetSocketError(CSimpleSocket::SocketEunknown);
        break;
    }
#endif
#ifdef _WIN32
    int32_t nError = WSAGetLastError();
    switch (nError)
    {
    case EXIT_SUCCESS:
        SetSocketError(CSimpleSocket::SocketSuccess);
        break;
    case WSAEBADF:
    case WSAENOTCONN:
        SetSocketError(CSimpleSocket::SocketNotconnected);
        break;
    case WSAEINTR:
        SetSocketError(CSimpleSocket::SocketInterrupted);
        break;
    case WSAEACCES:
    case WSAEAFNOSUPPORT:
    case WSAEINVAL:
    case WSAEMFILE:
    case WSAENOBUFS:
    case WSAEPROTONOSUPPORT:
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
        break;
    case WSAECONNREFUSED :
        SetSocketError(CSimpleSocket::SocketConnectionRefused);
        break;
    case WSAETIMEDOUT:
        SetSocketError(CSimpleSocket::SocketTimedout);
        break;
    case WSAEINPROGRESS:
        SetSocketError(CSimpleSocket::SocketEinprogress);
        break;
    case WSAECONNABORTED:
        SetSocketError(CSimpleSocket::SocketConnectionAborted);
        break;
    case WSAEWOULDBLOCK:
        SetSocketError(CSimpleSocket::SocketEwouldblock);
        break;
    case WSAENOTSOCK:
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
        break;
    case WSAECONNRESET:
        SetSocketError(CSimpleSocket::SocketConnectionReset);
        break;
    case WSANO_DATA:
        SetSocketError(CSimpleSocket::SocketInvalidAddress);
        break;
    case WSAEADDRINUSE:
        SetSocketError(CSimpleSocket::SocketAddressInUse);
        break;
    case WSAEFAULT:
        SetSocketError(CSimpleSocket::SocketInvalidPointer);
        break;
    default:
        SetSocketError(CSimpleSocket::SocketEunknown);
        break;
    }
#endif
}

//------------------------------------------------------------------------------
//
// DescribeError()
//
//------------------------------------------------------------------------------

const char *CSimpleSocket::DescribeError(CSocketError err)
{
    switch (err) {
        case CSimpleSocket::SocketError:
            return "Generic socket error translates to error below.";
        case CSimpleSocket::SocketSuccess:
            return "No socket error.";
        case CSimpleSocket::SocketInvalidSocket:
            return "Invalid socket handle.";
        case CSimpleSocket::SocketInvalidAddress:
            return "Invalid destination address specified.";
        case CSimpleSocket::SocketInvalidPort:
            return "Invalid destination port specified.";
        case CSimpleSocket::SocketConnectionRefused:
            return "No server is listening at remote address.";
        case CSimpleSocket::SocketTimedout:
            return "Timed out while attempting operation.";
        case CSimpleSocket::SocketEwouldblock:
            return "Operation would block if socket were blocking.";
        case CSimpleSocket::SocketNotconnected:
            return "Currently not connected.";
        case CSimpleSocket::SocketEinprogress:
            return "Socket is non-blocking and the connection cannot be completed immediately";
        case CSimpleSocket::SocketInterrupted:
            return "Call was interrupted by a signal that was caught before a valid connection arrived.";
        case CSimpleSocket::SocketConnectionAborted:
            return "The connection has been aborted.";
        case CSimpleSocket::SocketProtocolError:
            return "Invalid protocol for operation.";
        case CSimpleSocket::SocketFirewallError:
            return "Firewall rules forbid connection.";
        case CSimpleSocket::SocketInvalidSocketBuffer:
            return "The receive buffer point outside the process's address space.";
        case CSimpleSocket::SocketConnectionReset:
            return "Connection was forcibly closed by the remote host.";
        case CSimpleSocket::SocketAddressInUse:
            return "Address already in use.";
        case CSimpleSocket::SocketInvalidPointer:
            return "Pointer type supplied as argument is invalid.";
        case CSimpleSocket::SocketEunknown:
            return "Unknown error";
        default:
            return "No such CSimpleSocket error";
    }
}

//------------------------------------------------------------------------------
//
// Select()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Select(int32_t nTimeoutSec, int32_t nTimeoutUSec)
{
    bool            bRetVal = false;
    struct timeval *pTimeout = NULL;
    struct timeval  timeout;
    int32_t           nNumDescriptors = -1;
    int32_t           nError = 0;

    FD_ZERO(&m_errorFds);
    FD_ZERO(&m_readFds);
    FD_ZERO(&m_writeFds);
    FD_SET(m_socket, &m_errorFds);
    FD_SET(m_socket, &m_readFds);
    FD_SET(m_socket, &m_writeFds);

    //---------------------------------------------------------------------
    // If timeout has been specified then set value, otherwise set timeout
    // to NULL which will block until a descriptor is ready for read/write
    // or an error has occurred.
    //---------------------------------------------------------------------
    if ((nTimeoutSec > 0) || (nTimeoutUSec > 0))
    {
        timeout.tv_sec = nTimeoutSec;
        timeout.tv_usec = nTimeoutUSec;
        pTimeout = &timeout;
    }

    nNumDescriptors = SELECT(m_socket+1, &m_readFds, &m_writeFds, &m_errorFds, pTimeout);
//    nNumDescriptors = SELECT(m_socket+1, &m_readFds, NULL, NULL, pTimeout);

    //----------------------------------------------------------------------
    // Handle timeout
    //----------------------------------------------------------------------
    if (nNumDescriptors == 0)
    {
        SetSocketError(CSimpleSocket::SocketTimedout);
    }
    //----------------------------------------------------------------------
    // If a file descriptor (read/write) is set then check the
    // socket error (SO_ERROR) to see if there is a pending error.
    //----------------------------------------------------------------------
    else if ((FD_ISSET(m_socket, &m_readFds)) || (FD_ISSET(m_socket, &m_writeFds)))
    {
        int32_t nLen = sizeof(nError);

        if (GETSOCKOPT(m_socket, SOL_SOCKET, SO_ERROR, &nError, &nLen) == 0)
        {
            errno = nError;

            if (nError == 0)
            {
                bRetVal = true;
            }
        }

        TranslateSocketError();
    }

    return bRetVal;
}