// networking.cpp
// Copyright (C) 2006-2009 MicroNeil Research Corporation.
//
// This program is part of the MicroNeil Research Open Library Project. For
// more information go to http://www.microneil.com/OpenLibrary/index.html
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// 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.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA
//==============================================================================

// See networking.hpp for notes.
// See networking.inline.hpp for inlined methods & functions.

#include "networking.hpp"

Networking Network;                                                             // Finally creating the Network instance.

//// Platform Specific Stuff ///////////////////////////////////////////////////

#if defined(WIN32) || defined(WIN64)

////////////////////////////////////////////////////////////////////////////////
//// Being Windows specific code

WSADATA WSSTartData;                                                            // Socket library data structure.

// Error description handling for humans.

string Networking::DescriptiveError(string Msg, int Errno) {                    // Form a descriptive error w/ errno.
        char* s = 0;

        switch(Errno) {
            case WSA_INVALID_HANDLE: s = "WSA_INVALID_HANDLE"; break;
            case WSA_NOT_ENOUGH_MEMORY: s = "WSA_NOT_ENOUGH_MEMORY"; break;
            case WSA_INVALID_PARAMETER: s = "WSA_INVALID_PARAMETER"; break;
            case WSA_OPERATION_ABORTED: s = "WSA_OPERATION_ABORTED"; break;
            case WSA_IO_INCOMPLETE: s = "WSA_IO_INCOMPLETE"; break;
            case WSA_IO_PENDING: s = "WSA_IO_PENDING"; break;
            case WSAEINTR: s = "WSAEINTR"; break;
            case WSAEBADF: s = "WSAEBADF"; break;
            case WSAEACCES: s = "WSAEACCES"; break;
            case WSAEFAULT: s = "WSAEFAULT"; break;
            case WSAEINVAL: s = "WSAEINVAL"; break;
            case WSAEMFILE: s = "WSAEMFILE"; break;
            case WSAEWOULDBLOCK: s = "WSAEWOULDBLOCK"; break;
            case WSAEINPROGRESS: s = "WSAEINPROGRESS"; break;
            case WSAEALREADY: s = "WSAEALREADY"; break;
            case WSAENOTSOCK: s = "WSAENOTSOCK"; break;
            case WSAEDESTADDRREQ: s = "WSAEDESTADDRREQ"; break;
            case WSAEMSGSIZE: s = "WSAEMSGSIZE"; break;
            case WSAEPROTOTYPE: s = "WSAEPROTOTYPE"; break;
            case WSAENOPROTOOPT: s = "WSAENOPROTOOPT"; break;
            case WSAEPROTONOSUPPORT: s = "WSAEPROTONOSUPPORT"; break;
            case WSAESOCKTNOSUPPORT: s = "WSAESOCKTNOSUPPORT"; break;
            case WSAEOPNOTSUPP: s = "WSAEOPNOTSUPP"; break;
            case WSAEPFNOSUPPORT: s = "WSAEPFNOSUPPORT"; break;
            case WSAEAFNOSUPPORT: s = "WSAEAFNOSUPPORT"; break;
            case WSAEADDRINUSE: s = "WSAEADDRINUSE"; break;
            case WSAEADDRNOTAVAIL: s = "WSAEADDRNOTAVAIL"; break;
            case WSAENETDOWN: s = "WSAENETDOWN"; break;
            case WSAENETUNREACH: s = "WSAENETUNREACH"; break;
            case WSAENETRESET: s = "WSAENETRESET"; break;
            case WSAECONNABORTED: s = "WSAECONNABORTED"; break;
            case WSAECONNRESET: s = "WSAECONNRESET"; break;
            case WSAENOBUFS: s = "WSAENOBUFS"; break;
            case WSAEISCONN: s = "WSAEISCONN"; break;
            case WSAENOTCONN: s = "WSAENOTCONN"; break;
            case WSAESHUTDOWN: s = "WSAESHUTDOWN"; break;
            case WSAETOOMANYREFS: s = "WSAETOOMANYREFS"; break;
            case WSAETIMEDOUT: s = "WSAETIMEDOUT"; break;
            case WSAECONNREFUSED: s = "WSAECONNREFUSED"; break;
            case WSAELOOP: s = "WSAELOOP"; break;
            case WSAENAMETOOLONG: s = "WSAENAMETOOLONG"; break;
            case WSAEHOSTDOWN: s = "WSAEHOSTDOWN"; break;
            case WSAEHOSTUNREACH: s = "WSAEHOSTUNREACH"; break;
            case WSAENOTEMPTY: s = "WSAENOTEMPTY"; break;
            case WSAEPROCLIM: s = "WSAEPROCLIM"; break;
            case WSAEUSERS: s = "WSAEUSERS"; break;
            case WSAEDQUOT: s = "WSAEDQUOT"; break;
            case WSAESTALE: s = "WSAESTALE"; break;
            case WSAEREMOTE: s = "WSAEREMOTE"; break;
            case WSASYSNOTREADY: s = "WSASYSNOTREADY"; break;
            case WSAVERNOTSUPPORTED: s = "WSAVERNOTSUPPORTED"; break;
            case WSANOTINITIALISED: s = "WSANOTINITIALISED"; break;
            case WSAEDISCON: s = "WSAEDISCON"; break;
            case WSAENOMORE: s = "WSAENOMORE"; break;
            case WSAECANCELLED: s = "WSAECANCELLED"; break;
            case WSAEINVALIDPROCTABLE: s = "WSAEINVALIDPROCTABLE"; break;
            case WSAEINVALIDPROVIDER: s = "WSAEINVALIDPROVIDER"; break;
            case WSAEPROVIDERFAILEDINIT: s = "WSAEPROVIDERFAILEDINIT"; break;
            case WSASYSCALLFAILURE: s = "WSASYSCALLFAILURE"; break;
            case WSASERVICE_NOT_FOUND: s = "WSASERVICE_NOT_FOUND"; break;
            case WSATYPE_NOT_FOUND: s = "WSATYPE_NOT_FOUND"; break;
            case WSA_E_NO_MORE: s = "WSA_E_NO_MORE"; break;
            case WSA_E_CANCELLED: s = "WSA_E_CANCELLED"; break;
            case WSAEREFUSED: s = "WSAEREFUSED"; break;
            case WSAHOST_NOT_FOUND: s = "WSAHOST_NOT_FOUND"; break;
            case WSATRY_AGAIN: s = "WSATRY_AGAIN"; break;
            case WSANO_RECOVERY: s = "WSANO_RECOVERY"; break;
            case WSANO_DATA: s = "WSANO_DATA"; break;
            case WSA_QOS_RECEIVERS: s = "WSA_QOS_RECEIVERS"; break;
            case WSA_QOS_SENDERS: s = "WSA_QOS_SENDERS"; break;
            case WSA_QOS_NO_SENDERS: s = "WSA_QOS_NO_SENDERS"; break;
            case WSA_QOS_NO_RECEIVERS: s = "WSA_QOS_NO_RECEIVERS"; break;
            case WSA_QOS_REQUEST_CONFIRMED: s = "WSA_QOS_REQUEST_CONFIRMED"; break;
            case WSA_QOS_ADMISSION_FAILURE: s = "WSA_QOS_ADMISSION_FAILURE"; break;
            case WSA_QOS_POLICY_FAILURE: s = "WSA_QOS_POLICY_FAILURE"; break;
            case WSA_QOS_BAD_STYLE: s = "WSA_QOS_BAD_STYLE"; break;
            case WSA_QOS_BAD_OBJECT: s = "WSA_QOS_BAD_OBJECT"; break;
            case WSA_QOS_TRAFFIC_CTRL_ERROR: s = "WSA_QOS_TRAFFIC_CTRL_ERROR"; break;
            case WSA_QOS_GENERIC_ERROR: s = "WSA_QOS_GENERIC_ERROR"; break;
            case WSA_QOS_ESERVICETYPE: s = "WSA_QOS_ESERVICETYPE"; break;
            case WSA_QOS_EFLOWSPEC: s = "WSA_QOS_EFLOWSPEC"; break;
            case WSA_QOS_EPROVSPECBUF: s = "WSA_QOS_EPROVSPECBUF"; break;
            case WSA_QOS_EFILTERSTYLE: s = "WSA_QOS_EFILTERSTYLE"; break;
            case WSA_QOS_EFILTERTYPE: s = "WSA_QOS_EFILTERTYPE"; break;
            case WSA_QOS_EFILTERCOUNT: s = "WSA_QOS_EFILTERCOUNT"; break;
            case WSA_QOS_EOBJLENGTH: s = "WSA_QOS_EOBJLENGTH"; break;
            case WSA_QOS_EFLOWCOUNT: s = "WSA_QOS_EFLOWCOUNT"; break;
            case WSA_QOS_EPOLICYOBJ: s = "WSA_QOS_EPOLICYOBJ"; break;
            case WSA_QOS_EFLOWDESC: s = "WSA_QOS_EFLOWDESC"; break;
            case WSA_QOS_EPSFLOWSPEC: s = "WSA_QOS_EPSFLOWSPEC"; break;
            case WSA_QOS_EPSFILTERSPEC: s = "WSA_QOS_EPSFILTERSPEC"; break;
            case WSA_QOS_ESDMODEOBJ: s = "WSA_QOS_ESDMODEOBJ"; break;
            case WSA_QOS_ESHAPERATEOBJ: s = "WSA_QOS_ESHAPERATEOBJ"; break;
            case WSA_QOS_RESERVED_PETYPE: s = "WSA_QOS_RESERVED_PETYPE"; break;

#ifdef WSA_QOS_EUNKOWNPSOBJ
            case WSA_QOS_EUNKOWNPSOBJ: s = "WSA_QOS_EUNKOWNPSOBJ"; break;
#endif
        }

        Msg.append(" ");
        if(s) {
            Msg.append(s);
        }
        else  {
            ostringstream ErrNoMsg;
            ErrNoMsg << " UNKNOWN ErrorNumber = " << Errno;
            Msg.append(ErrNoMsg.str());
        }
        return Msg;
};

// Networking Constructor //////////////////////////////////////////////////////
// Handles any necessary setup of Network Module resources.

Networking::Networking() {                                                      // Upon initialization,
    if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData)) {                         // startup the Winsock2.0 DLL.
        throw InitializationError(                                              // If that fails then throw!
            "Networking::Networking() if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData))"
        );
    }
}

// Networking Destructor ///////////////////////////////////////////////////////
// Handles any necessary cleanup of Network Module resources.

Networking::~Networking() {                                                     // Upon shutdown,
    WSACleanup();                                                               // shutdown the Winsock DLL.
}

//// Emd Windows specific code
////////////////////////////////////////////////////////////////////////////////

#else

////////////////////////////////////////////////////////////////////////////////
//// Begin GNU specific code

// Error description handling for humans.

string Networking::DescriptiveError(string Msg, int Errno) {                    // Form a descriptive error w/ errno.
        Msg.append(" "); Msg.append(strerror(Errno));
        return Msg;
};

// Networking Constructor //////////////////////////////////////////////////////
// Handles any necessary setup of Network Module resources.

Networking::Networking() {                                                      // Upon initialization,
    // Nothing So Far...                                                        // nothing special required.
}

// Networking Destructor ///////////////////////////////////////////////////////
// Handles any necessary cleanup of Network Module resources.

Networking::~Networking() {                                                     // GNU sockets cleanup,
    // Nothing So Far...                                                        // nothing specail to required.
}

//// End GNU specific code
////////////////////////////////////////////////////////////////////////////////

#endif

////////////////////////////////////////////////////////////////////////////////
//// Platform Agnostic Stuff

//// Useful Internal Bits & Pieces /////////////////////////////////////////////

const int LowestOctetMask = 0x000000FF;                                         // The bits to look at.
const int OneOctetInBits = 8;                                                   // The bits to shift.

void splitIP(                                                                   // Split an IP into octets.
  unsigned long A,                                                              // The address in host format.
  int& a0,                                                                      // Reference to the first octet.
  int& a1,                                                                      // Reference to the second octet.
  int& a2,                                                                      // Reference to the third octet.
  int& a3                                                                       // Reference to the forth octet.
  ){
    a3 = A & LowestOctetMask; A >>= OneOctetInBits;                             // Get the lowest order octet & move.
    a2 = A & LowestOctetMask; A >>= OneOctetInBits;                             // Get the next lowest octet & move.
    a1 = A & LowestOctetMask; A >>= OneOctetInBits;                             // Get the next lowest octet & move.
    a0 = A & LowestOctetMask;                                                   // Get the highest octet. That's IT!
}

//// IP4Address methods ////////////////////////////////////////////////////////

IP4Address::operator unsigned long int() const {                                // Assign to unsigned long int.
    return IP;                                                                  // Return it.
}

IP4Address::operator string() const {                                           // Assign to a string.
    char stringbfr[IPStringBufferSize];                                         // Grab a temporary buffer.
    memset(stringbfr, 0, sizeof(stringbfr));                                    // Null out it's space.
    int a0, a1, a2, a3;                                                         // Grab some integers.
    splitIP(IP, a0, a1, a2, a3);                                                // Split the IP in the IP4Address.
    sprintf(stringbfr, "%d.%d.%d.%d", a0, a1, a2, a3);                          // Format the octets.
    return string(stringbfr);                                                   // Return a string.
}

//// SocketAddress methods /////////////////////////////////////////////////////

// getAddress(str, len)

const char* SocketAddress::getAddress(char* str) {                              // Get the IP address into a cstring.
    if(NULL == str) {                                                           // If the caller did not provide a
        str = IPStringBuffer;                                                   // buffer to use then we will use ours.
    }
    int a0, a1, a2, a3, i=0;                                                    // Grab a bunch of handy integers.
    getAddress(a0, a1, a2, a3);                                                 // Get the address as octets.
    sprintf(str, "%d.%d.%d.%d", a0, a1, a2, a3);                                // Format as dotted decimal notation.
    return str;                                                                 // Return the output buffer.
}

// getAddress(int& a0, int& a1, int& a2, int& a3)

void SocketAddress::getAddress(int& a0, int& a1, int& a2, int& a3) {            // Get the IP address into 4 ints
    unsigned long A = getAddress();                                             // Get the address.
    splitIP(A, a0, a1, a2, a3);                                                 // Split it into octets.
}

//// TCPListener methods ///////////////////////////////////////////////////////

TCPListener::TCPListener(unsigned short Port) {                                 // Set up localhost on this Port.
    LocalAddress.setPort(Port);                                                 // Establish the port.
    LocalAddress.setAddress(LOCALHOST);                                         // Set the address to LOCALHOST.
    MaxPending = DefaultMaxPending;                                             // Use the default inbound queue size.
    ReuseAddress = true;                                                        // ReuseAddress on by default.
    OpenStage1Complete = false;                                                 // This stage of open() not yet done.
    OpenStage2Complete = false;                                                 // This stage of open() not yet done.

    // Create a socket...

    LastError = 0;

    Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);                         // Create the socket.
    if(INVALID_SOCKET == Handle) {                                              // If that operation failed then
        LastError = Network.getLastError();                                     // grab the error code and
        throw Networking::SocketCreationError(                                  // throw.
          Network.DescriptiveError(
            "TCPListener::open().socket()", LastError));
    }
}

TCPListener::TCPListener(SocketAddress& WhereToBind) {                          // Set up specific "name" for listening.
    LocalAddress = WhereToBind;                                                 // Make my Local address as provided.
    MaxPending = DefaultMaxPending;                                             // Use the default inbound queue size.
    ReuseAddress = true;                                                        // ReuseAddress on by default.
    OpenStage1Complete = false;                                                 // This stage of open() not yet done.
    OpenStage2Complete = false;                                                 // This stage of open() not yet done.

    // Create a socket...

    LastError = 0;

    Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);                         // Create the socket.
    if(INVALID_SOCKET == Handle) {                                              // If that operation failed then
        LastError = Network.getLastError();                                     // grab the error code and
        throw Networking::SocketCreationError(                                  // throw.
          Network.DescriptiveError(
            "TCPListener::open().socket()", LastError));
    }
}

// open()

void TCPListener::open() {                                                      // Open when ready.

    if(OpenSucceeded) return;                                                   // If open already, we're done.

    LastError = 0;                                                              // Clear the last error.
    bool SuccessFlag = true;                                                    // Start optimistically.

    // Set SO_REUSEADDR if turned on

    if(!OpenStage1Complete) {                                                   // Do this stage only once.
        int ReuseAddress_Flag = (ReuseAddress? 1:0);                            // Setup an appropriate integer flag.
        int result =                                                            // Set SO_REUSEADDR before bind().
          setsockopt(
            Handle,
            SOL_SOCKET,
            SO_REUSEADDR,
            (char*) &ReuseAddress_Flag,
            sizeof(ReuseAddress_Flag));

        if(0 > result) {                                                        // If there was an error then
            SuccessFlag = false;                                                // we did not succeed.
            LastError = Network.getLastError();                                 // Capture the error information and
            throw Networking::SocketSetSockOptError(                            // throw.
                Network.DescriptiveError(
                "TCPListener::open().setsockopt()", LastError));
        }
        OpenStage1Complete = true;                                              // Stage 1 complete now.
    }                                                                           // End of open() stage 1

    // Next we bind it...

    if(!OpenStage2Complete) {                                                   // Do this stage only once.
        int result =                                                            // Bind our socket to the LocalAddress.
          bind(
            Handle,
            LocalAddress.getPtr_sockaddr(),
            LocalAddress.getAddressSize());

        if(0 > result) {                                                        // If there was an error then
            SuccessFlag = false;                                                // we did not succeed.
            LastError = Network.getLastError();                                 // Capture the error information and
            throw Networking::SocketBindError(                                  // throw.
                Network.DescriptiveError(
                "TCPListener::open().bind()", LastError));
        }
        OpenStage2Complete = true;                                              // Stage 2 complete now.
    }                                                                           // End of open() stage 2

    // Then we put it in a listening state...

    int result = listen(Handle, MaxPending);                                    // Listen for up to MaxPending at once.

    if(0 > result) {                                                            // If an error occurred then
        SuccessFlag = false;                                                    // we did not succeed.
        LastError = Network.getLastError();                                     // Capture the error information and
        throw Networking::SocketListenError(                                    // throw.
          Network.DescriptiveError(
            "TCPListener::open().listen()", LastError));
    }

    OpenSucceeded = SuccessFlag;                                                // So, did we succeed?
}

// acceptClient()

TCPClient* TCPListener::acceptClient() {                                        // Accept a client connection.

    LastError = 0;                                                              // Clear the last error.
    socklen_t rsize = RemoteAddress.getAddressSize();                           // Size as an int for accept().

    hSocket NewHandle =                                                         // Accept a new connection if available.
      accept(
        Handle,                                                                 // use our handle, of course,...
        RemoteAddress.getPtr_sockaddr(),                                        // and store the remote hosts
        &rsize);                                                                // address for us.

    if(INVALID_SOCKET == NewHandle) {                                           // If there was an error then
        LastError = Network.getLastError();                                     // capture the error value.
        if(!Network.WouldBlock(LastError)) {                                    // If it's not a EWOULDBLOCK error
            throw Networking::SocketAcceptError(                                // then we need to throw.
              Network.DescriptiveError(
                "TCPListener::acceptClient()", LastError));
        } else {                                                                // EWOULDBLOCK errors are normal in
            return NULL;                                                        // non blocking mode so we return
        }                                                                       // NULL when we see them.
    }

    // If things have gone well we can do what we came for.

    return new TCPClient(*this, NewHandle, RemoteAddress);                      // Create the new TCPClient object.

}

//// TCPClient methods /////////////////////////////////////////////////////////

int TCPClient::transmit(const char* bfr, int size) {                            // How to send a buffer of data.
    LastError = 0;                                                              // No errors yet.
    if(0 == size) return 0;                                                     // Nothing to send, send nothing.
    if(0 == bfr)                                                                // Watch out for null buffers.
      throw Networking::SocketWriteError("TCPClient::transmit() NULL Bfr!");
    if(0 > size)                                                                // Watch out for bad sizes.
      throw Networking::SocketWriteError("TCPClient::transmit() 0 > size!");

    int ByteCount = send(Handle, bfr, size, NOFLAGS);                           // Try to send and capture the count.
    if(0 > ByteCount) ByteCount = 0;                                            // Mask error results as 0 bytes sent.

    if(size > ByteCount) {                                                      // If we didn't send it all check it out.
        LastError = Network.getLastError();                                     // Grab the error code.
        if(Network.WouldBlock(LastError)) {                                     // If the error was WouldBlock then
            return ByteCount;                                                   // it was a partial send - return.
        } else {                                                                // If this was a different kind of error
            throw Networking::SocketWriteError(                                 // then throw!
              Network.DescriptiveError(
                "TCPClient::transmit()", LastError));
        }
    }
    return ByteCount;                                                           // Ultimately return the byte count.
}

int TCPClient::receive(char* bfr, int size) {                                   // How to receive a buffer of data.
    if(ReadBufferIsEmpty()) {                                                   // If the read buffer is empty then
        fillReadBuffer();                                                       // fill it first.
    }                                                                           // Optimize our transfer to the smaller
    if(DataLength < size) {                                                     // of what we have or the size of the
        size = DataLength;                                                      // provided buffer. This way we ony check
    }                                                                           // one value in our copy loop ;-)
    int RemainingDataLength = size;                                             // Capture the length of data to xfer.
    while(0 < RemainingDataLength) {                                            // While we have work to do
        *bfr = *ReadPointer;                                                    // copy each byte from our ReadBuffer,
        bfr++; ReadPointer++;                                                   // move the pointers to the next byte,
        DataLength--;                                                           // update our ReadBuffers's DataLength,
        RemainingDataLength--;                                                  // and count down the bytes left to xfer.
    }
    return size;                                                                // When done, say how much we moved.
}

int TCPClient::delimited_receive(char* bfr, int size, char delimiter) {         // How to receive delimited data.
    if(ReadBufferIsEmpty()) {                                                   // If the read buffer is empty then
        fillReadBuffer();                                                       // fill it first.
    }                                                                           // Optimize our transfer to the smaller
    if(DataLength < size) {                                                     // of what we have or the size of the
        size = DataLength;                                                      // provided buffer. This way we ony check
    }                                                                           // one value in our copy loop ;-)
    int Count = 0;                                                              // Keep our byte count in scope.
    bool DelimiterNotReached = true;                                            // Watching for our deliimiter.
    while((Count < size) && DelimiterNotReached) {                              // While there is work to do...
        *bfr = *ReadPointer;                                                    // copy each byte from our ReadBuffer,
        DelimiterNotReached = (delimiter != (*bfr));                            // check for the delimiter character,
        bfr++; ReadPointer++;                                                   // move the pointers to the next byte,
        DataLength--;                                                           // update our ReadBuffers's DataLength,
        Count++;                                                                // and count up the bytes we have moved.
    }
    return Count;                                                               // When done, say how much we moved.
}

//// TCPHost methods ///////////////////////////////////////////////////////////

// Constructors...

TCPHost::TCPHost(unsigned short Port) {                                         // Will connect to localhost on Port.
    RemoteAddress.setPort(Port);                                                // Connect to Port on
    RemoteAddress.setAddress(LOCALHOST);                                        // Localhost.
    ReadPointer = ReadBuffer;                                                   // Set the read position to zero.
    DataLength = 0;                                                             // There is no data yet.
    ReuseAddress = false;                                                       // ReuseAddress off by default.
    OpenStage1Complete = false;                                                 // Stage 1 of open() not done yet.

    // Create a socket to use.

    LastError = 0;                                                              // Clear our last error value.

    Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);                         // Create the socket.
    if(0 > Handle) {                                                            // If that operation failed then
        LastError = Network.getLastError();                                     // grab the error code and
        throw Networking::SocketCreationError(                                  // throw.
          Network.DescriptiveError(
            "TCPHost::open().socket()", LastError));
    }
}

TCPHost::TCPHost(SocketAddress& Remote) {                                       // Will connect to Remote address/port.
    RemoteAddress = Remote;                                                     // Capture the provided address.
    ReadPointer = ReadBuffer;                                                   // Set the read position to zero.
    DataLength = 0;                                                             // There is no data yet.
    ReuseAddress = false;                                                       // ReuseAddress off by default.
    OpenStage1Complete = false;                                                 // Stage 1 of open() not done yet.

    // Create a socket to use.

    LastError = 0;                                                              // Clear our last error value.

    Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);                         // Create the socket.
    if(0 > Handle) {                                                            // If that operation failed then
        LastError = Network.getLastError();                                     // grab the error code and
        throw Networking::SocketCreationError(                                  // throw.
          Network.DescriptiveError(
            "TCPHost::open().socket()", LastError));
    }
}

// Methods...

void TCPHost::open() {                                                          // We provide open().

    if(OpenSucceeded) return;                                                   // If open already, we're done.

    LastError = 0;                                                              // Clear our LastError value.
    bool SuccessFlag = true;                                                    // Begin optimistically.

    // Set SO_REUSEADDR if turned on

    if(!OpenStage1Complete) {                                                   // If we haven't done this yet:
        int ReuseAddress_Flag = (ReuseAddress? 1:0);                            // Setup an appropriate integer flag.
        int result =                                                            // Set SO_REUSEADDR before bind().
          setsockopt(
            Handle,
            SOL_SOCKET,
            SO_REUSEADDR,
            (char *)&ReuseAddress_Flag,
            sizeof(ReuseAddress_Flag));

        if(0 > result) {                                                        // If there was an error then
            SuccessFlag = false;                                                // we did not succeed.
            LastError = Network.getLastError();                                 // Capture the error information and
            throw Networking::SocketSetSockOptError(                            // throw.
              Network.DescriptiveError(
                "TCPListener::open().setsockopt()", LastError));
        }
        OpenStage1Complete = true;                                              // Skip this section from now on.
    }                                                                           // Done with stage 1.

    // Connect the socekt to the Host.

    int result =                                                                // Connect to the remote host
      connect(                                                                  // using the socket we just
        Handle,                                                                 // stored in Handle and
        RemoteAddress.getPtr_sockaddr(),                                        // the Remote address.
        RemoteAddress.getAddressSize());

    if(0 > result) {                                                            // If there was an error then
        SuccessFlag = false;                                                    // we did not succeed.
        LastError = Network.getLastError();                                     // Record the error data.
        if(Network.IsConnected(LastError)) {                                    // If we actually did succeed then
            SuccessFlag = true;                                                 // say so. (Silly Winsock!)
        } else                                                                  // But if that's not the case check
        if(                                                                     // to see if something bad happened -
          !Network.WouldBlock(LastError) &&                                     // not just would-block, or
          !Network.InProgress(LastError)                                        // in progress...
          ) {                                                                   // If it was something other than
            throw Networking::SocketConnectError(                               // WouldBlock or InProgress then
              Network.DescriptiveError(                                         // throw!
                "TCPHost::open().connect()", LastError));
        }                                                                       // If it was WouldBlock then it's
    }                                                                           // considered to be ok.

    OpenSucceeded = SuccessFlag;                                                // So, are we open now?
}

int TCPHost::transmit(const char* bfr, int size) {                              // How to send a buffer of data.
    LastError = 0;                                                              // No errors yet.
    if(0 == size) return 0;                                                     // Nothing to send, send nothing.
    if(0 == bfr)                                                                // Watch out for null buffers.
      throw Networking::SocketWriteError("TCPHost::transmit() NULL Bfr!");
    if(0 > size)                                                                // Watch out for bad sizes.
      throw Networking::SocketWriteError("TCPHost::transmit() 0 > size!");

    int ByteCount = send(Handle, bfr, size, NOFLAGS);                           // Try to send and capture the count.
    if(0 > ByteCount) ByteCount = 0;                                            // Mask error results as 0 bytes sent.

    if(size > ByteCount) {                                                      // If we didn't send it all check it out.
        LastError = Network.getLastError();                                     // Grab the error code.
        if(Network.WouldBlock(LastError)) {                                     // If the error was WouldBlock then
            return ByteCount;                                                   // it was a partial snd - return.
        } else {                                                                // If this was a different kind of error
            throw Networking::SocketWriteError(                                 // then throw!
              Network.DescriptiveError(
                "TCPHost::transmit().send()", LastError));
        }
    }
    return ByteCount;                                                           // Ultimately return the byte count.
}

int TCPHost::receive(char* bfr, int size) {                                     // How to receive a buffer of data.
    if(ReadBufferIsEmpty()) {                                                   // If the read buffer is empty then
        fillReadBuffer();                                                       // fill it first.
    }                                                                           // Optimize our transfer to the smaller
    if(DataLength < size) {                                                     // of what we have or the size of the
        size = DataLength;                                                      // provided buffer. This way we ony check
    }                                                                           // one value in our copy loop ;-)
    int RemainingDataLength = size;                                             // Capture the length of data to xfer.
    while(0 < RemainingDataLength) {                                            // While we have work to do
        *bfr = *ReadPointer;                                                    // copy each byte from our ReadBuffer,
        bfr++; ReadPointer++;                                                   // move the pointers to the next byte,
        DataLength--;                                                           // update our ReadBuffers's DataLength,
        RemainingDataLength--;                                                  // and count down the bytes left to xfer.
    }
    return size;                                                                // When done, say how much we moved.
}

int TCPHost::delimited_receive(char* bfr, int size, char delimiter) {           // How to receive delimited data.
    if(ReadBufferIsEmpty()) {                                                   // If the read buffer is empty then
        fillReadBuffer();                                                       // fill it first.
    }                                                                           // Optimize our transfer to the smaller
    if(DataLength < size) {                                                     // of what we have or the size of the
        size = DataLength;                                                      // provided buffer. This way we ony check
    }                                                                           // one value in our copy loop ;-)
    int Count = 0;                                                              // Keep our byte count in scope.
    bool DelimiterNotReached = true;                                            // Watching for our deliimiter.
    while((Count < size) && DelimiterNotReached) {                              // While there is work to do...
        *bfr = *ReadPointer;                                                    // copy each byte from our ReadBuffer,
        DelimiterNotReached = (delimiter != (*bfr));                            // check for the delimiter character,
        bfr++; ReadPointer++;                                                   // move the pointers to the next byte,
        DataLength--;                                                           // update our ReadBuffers's DataLength,
        Count++;                                                                // and count up the bytes we have moved.
    }
    return Count;                                                               // When done, say how much we moved.
}


// End Platform Agnostic Stuff
////////////////////////////////////////////////////////////////////////////////
