//
// The contents of this file are subject to the Mozilla Public
// License Version 1.1 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of
// the License at http://www.mozilla.org/MPL/
// 
// Software distributed under the License is distributed on an "AS
// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
// implied. See the License for the specific language governing
// rights and limitations under the License.
// 
// The Original Code is State Machine Compiler (SMC).
// 
// The Initial Developer of the Original Code is Charles W. Rapp.
// Portions created by Charles W. Rapp are
// Copyright (C) 2000 - 2003 Charles W. Rapp.
// All Rights Reserved.
// 
// Contributor(s): 
//
// Name
//  TcpConnection.cpp
//
// Description
//  TCP connection class implementation.
//
// RCS ID
// $Id: TcpConnection.cpp,v 1.6 2005/06/08 11:09:12 cwrapp Exp $
//
// CHANGE LOG
// $Log: TcpConnection.cpp,v $
// Revision 1.6  2005/06/08 11:09:12  cwrapp
// + Updated Python code generator to place "pass" in methods with empty
//   bodies.
// + Corrected FSM errors in Python example 7.
// + Removed unnecessary includes from C++ examples.
// + Corrected errors in top-level makefile's distribution build.
//
// Revision 1.5  2005/05/28 13:31:18  cwrapp
// Updated C++ examples.
//
// Revision 1.0  2003/12/14 19:40:23  charlesr
// Initial revision
//

#ifdef WIN32
#pragma warning(disable: 4355)
#endif

#include "TcpConnection.h"
#include "TcpSegment.h"
#include "TcpClient.h"
#include "TcpServer.h"
#include "Eventloop.h"
#include <errno.h>
#include <string.h>
#include <memory.h>
#include <fcntl.h>
#if !defined(WIN32)
#include <unistd.h>
#include <netdb.h>
#  if defined(__sun)
#include <sys/systeminfo.h>
#  endif
#endif

#if defined(WIN32)
// Get rid of an annoying build warning.
#pragma warning(disable: 4355)
#endif

using namespace std;

#if defined(WIN32)
typedef int socklen_t;
#endif

// External variable declarations.
extern Eventloop *Gevent_loop;

// Const member declarations.
const unsigned long TcpConnection::ISN = 1415531521;
const int TcpConnection::MIN_TIMEOUT = 1;
const int TcpConnection::ACK_TIMEOUT = 2000;
const int TcpConnection::CLOSE_TIMEOUT = 5000;
const int TcpConnection::BUFFER_BLOCK_SIZE = 4096;

// Const declarations.
const static int MSG_READ = 0;
const static int NO_SEND_FLAGS = 0;
const static long MAX_HOSTNAME_LEN = 257;
const static unsigned long LOCAL_HOST_ADDRESS = 0x7f000001;
#if defined(WIN32)
const static unsigned short MIN_PORT = 1024;
const static unsigned short MAX_PORT = 5000;
#endif

// External routine declarations.
char* winsock_strerror(int);

//---------------------------------------------------------------
// getFarAddress() const (Public)
// Return the far-end's 4-byte IP address.
//
unsigned long TcpConnection::getFarAddress() const
{
    return (_farAddress.sin_addr.s_addr);
} // end of TcpConnection::getFarAddress() const

//---------------------------------------------------------------
// getFarPort() const (Public)
// Return the far-end's TCP port.
//
unsigned short TcpConnection::getFarPort() const
{
    return (_farAddress.sin_port);
} // end of TcpConnection::getFarPort() const

//---------------------------------------------------------------
// getSequenceNumber() const (Public)
// Return the current sequence number.
//
unsigned long TcpConnection::getSequenceNumber() const
{
    return (_sequence_number);
} // end of TcpConnection::getSequenceNumber() const

//---------------------------------------------------------------
// transmit(const unsigned char*, int, int) (Public)
// Transmit data to the far side.
//
void TcpConnection::transmit(const char *data,
                             int offset,
                             int size)
{
    _fsm.Transmit(data, offset, size);
    return;
} // end of TcpConnection::transmit(const unsigned char*, int, int)

//---------------------------------------------------------------
// doClose() (Public)
// Start closing this connection.
//
void TcpConnection::doClose()
{
    _fsm.Close();
    return;
} // end of TcpConnection::doClose()

//---------------------------------------------------------------
// setListener(TcpConnectionListener&) (Public)
// Set the connection's listener to a new object.
//
void TcpConnection::setListener(TcpConnectionListener& listener)
{
    _listener = &listener;
    return;
} // end of TcpConnection::setListener(TcpConnectionListener&)

//---------------------------------------------------------------
// handleReceive(int) (Public)
// Process incoming data.
//
void TcpConnection::handleReceive(int)
{
    sockaddr sourceAddress;
    TcpSegment *segment;
    socklen_t addressSize;
    int flags,
        done,
        errorFlag,
        bytesRead,
        totalBytesRead;

    // Read in the segment. Since the data may exceed the buffer
    // size, keep expanding the buffer until all the data is
    // read.
    for (done = 0, bytesRead = 0, totalBytesRead = 0, errorFlag = 0;
         done == 0;
         totalBytesRead += bytesRead)
    {
        // Be certain to offset properly into the input buffer.
        addressSize = sizeof(sourceAddress);
        bytesRead = recvfrom(_udp_socket,
                             (_buffer + totalBytesRead),
                             _bufferSize,
                             MSG_READ,
                             &sourceAddress,
                             &addressSize);

#if defined(WIN32)
        if (bytesRead == SOCKET_ERROR)
#else
        if (bytesRead < 0)
#endif
        {
            done = 1;

            if (errno != EAGAIN)
            {
                cerr << "Receive failure - "
                     << strerror(errno)
                     << "."
                     << endl;

                errorFlag = 1;
            }
            else
            {
                bytesRead = 0;
            }
        }
        else if (bytesRead >= 0 && bytesRead < _bufferSize)
        {
            done = 1;
        }
        else
        {
            // The entire buffer was filled with the data.
            // Expand the buffer and read some more.
            expandBuffer();
        }
    }

    if (errorFlag == 0 && bytesRead > 0)
    {
        segment = new TcpSegment(reinterpret_cast<sockaddr_in&>(sourceAddress),
                                 _nearAddress,
                                 _buffer,
                                 totalBytesRead);

#if defined(SMC_DEBUG)
        cout << "**** Received segment:\n" << *segment << endl;
#endif

        // Generate the appropriate transition based on the header
        // flags.
        flags = segment->getFlags();
        switch(flags)
        {
            case TcpSegment::FIN:
                _fsm.FIN(*segment);
                break;

            case TcpSegment::SYN:
                _fsm.SYN(*segment);
                break;

            case TcpSegment::RST:
                _fsm.RST(*segment);
                break;

            case TcpSegment::PSH:
                _fsm.PSH(*segment);
                break;

            case TcpSegment::ACK:
                _fsm.ACK(*segment);
                break;

            case TcpSegment::URG:
                _fsm.URG(*segment);
                break;

            case TcpSegment::FIN_ACK:
                _fsm.FIN_ACK(*segment);
                break;

            case TcpSegment::SYN_ACK:
                _fsm.SYN_ACK(*segment);
                break;

            case TcpSegment::RST_ACK:
                _fsm.RST_ACK(*segment);
                break;

            case TcpSegment::PSH_ACK:
                _fsm.PSH_ACK(*segment);
                break;

            default:
                _fsm.UNDEF(*segment);
                break;
        }
    }

    return;
} // end of TcpConnection::handleReceive(int)

//---------------------------------------------------------------
// handleTimeout(const char*) (Public)
// Generate the appropriate transition based on the timer's name.
//
void TcpConnection::handleTimeout(const char *name)
{
#if defined(SMC_DEBUG)
    cout << "**** Timer " << name << " has expired." << endl;
#endif

    if (strcmp(name, "ACK_TIMER") == 0)
    {
        _fsm.AckTimeout();
    }
    else if (strcmp(name, "CONN_ACK_TIMER") == 0)
    {
        _fsm.ConnAckTimeout();
    }
    else if (strcmp(name, "TRANS_ACK_TIMER") == 0)
    {
        _fsm.TransAckTimeout();
    }
    else if (strcmp(name, "CLOSE_ACK_TIMER") == 0)
    {
        _fsm.CloseAckTimeout();
    }
    else if (strcmp(name, "CLOSE_TIMER") == 0)
    {
        _fsm.CloseTimeout();
    }
    else if (strcmp(name, "SERVER_OPENED") == 0)
    {
        _fsm.ServerOpened();
    }
    else if (strcmp(name, "CLIENT_OPENED") == 0)
    {
        _fsm.ClientOpened(&_farAddress);
    }
    else if (strcmp(name, "OPEN_FAILED") == 0)
    {
        _fsm.OpenFailed(_errorMessage);
        if (_errorMessage != NULL)
        {
            delete[] _errorMessage;
            _errorMessage = NULL;
        }
    }

    return;
} // end of TcpConnection::handleTimeout(const char*)

//---------------------------------------------------------------
// openServerSocket(unsigned short) (Public)
// Open a server socket on the specified port.
//
void TcpConnection::openServerSocket(unsigned short port)
{
#if defined(WIN32)
    unsigned long blockingFlag = 1;
    HANDLE newHandle;
#endif

    // Set up this server's address.
    (void) memset(&_nearAddress, 0, sizeof(_nearAddress));
    _nearAddress.sin_family = AF_INET;
    _nearAddress.sin_port = port;
    _nearAddress.sin_addr.s_addr = INADDR_ANY;

    // Tell the UDP socket to route data bound for port to this
    // object.
#if defined(WIN32)
    if ((_udp_win_socket = socket(AF_INET, SOCK_DGRAM, 0))
            == INVALID_SOCKET ||
        DuplicateHandle(GetCurrentProcess(),
                        (HANDLE) _udp_win_socket,
                        GetCurrentProcess(),
                        &newHandle,
                        0,
                        FALSE,
                        DUPLICATE_SAME_ACCESS) == FALSE ||
        bind((unsigned int) newHandle,
             reinterpret_cast<sockaddr*>(&_nearAddress),
             sizeof(_nearAddress))
            == SOCKET_ERROR)
#else
    if ((_udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
        bind(_udp_socket,
             reinterpret_cast<sockaddr*>(&_nearAddress),
             sizeof(_nearAddress))
            < 0)
#endif
    {
        char *error;

        // Use timer to report this event asynchronously.
        // Store away error message.
        if (_errorMessage != NULL)
        {
            delete[] _errorMessage;
            _errorMessage = NULL;
        }
#if defined(WIN32)
        error = winsock_strerror(WSAGetLastError());
#else
        error = strerror(errno);
#endif
        _errorMessage = new char[strlen(error) + 1];
        (void) strcpy(_errorMessage, error);
        Gevent_loop->startTimer("OPEN_FAILED",
                                MIN_TIMEOUT,
                                *this);
    }
    else
    {
        // Get the UDP socket's address.
        _nearAddress.sin_family = AF_INET;
        _nearAddress.sin_addr.s_addr = getLocalAddress();

        // Set the socket to non-blocking and register it with
        // the event loop.
#if defined(WIN32)
        _udp_socket = (unsigned int) newHandle;
        (void) ioctlsocket(_udp_socket, FIONBIO, &blockingFlag);
#else
        (void) fcntl(_udp_socket, F_SETFL, O_NDELAY);
#endif
        Gevent_loop->addFD(_udp_socket, *this);

        Gevent_loop->startTimer("SERVER_OPENED",
                                MIN_TIMEOUT,
                                *this);
    }

    return;
} // end of TcpConnection::openServerSocket(int)

//---------------------------------------------------------------
// openClientSocket(const sockaddr_in*) (Public)
// Connect to the specified address.
//
void TcpConnection::openClientSocket(const sockaddr_in *address)
{
#if defined(WIN32)
    unsigned long blockingFlag = 1;
    HANDLE newHandle;
    int new_port;
#endif

    // Set up this client's address.
    (void) memset(&_nearAddress, 0, sizeof(_nearAddress));
    _nearAddress.sin_family = AF_INET;
    _nearAddress.sin_port = 0;
    _nearAddress.sin_addr.s_addr = INADDR_ANY;

#if defined(WIN32)
    if ((_udp_win_socket = socket(AF_INET, SOCK_DGRAM, 0))
            == INVALID_SOCKET ||
        DuplicateHandle(GetCurrentProcess(),
                        (HANDLE) _udp_win_socket,
                        GetCurrentProcess(),
                        &newHandle,
                        0,
                        FALSE,
                        DUPLICATE_SAME_ACCESS) == FALSE ||
        (new_port = doBind((int) newHandle)) < 0)
#else
    if ((_udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
        bind(_udp_socket,
             reinterpret_cast<sockaddr*>(&_nearAddress),
             sizeof(_nearAddress))
            < 0)
#endif
    {
        char *error;

        // Use timer to report this event asynchronously.
        // Store away error message.
        if (_errorMessage != NULL)
        {
            delete[] _errorMessage;
            _errorMessage = NULL;
        }
#if defined(WIN32)
        error = winsock_strerror(WSAGetLastError());
#else
        error = strerror(errno);
#endif
        _errorMessage = new char[strlen(error) + 1];
        (void) strcpy(_errorMessage, error);
        Gevent_loop->startTimer("OPEN_FAILED",
                                MIN_TIMEOUT,
                                *this);
    }
    else
    {
        // Get the UDP socket's address.
        _nearAddress.sin_family = AF_INET;
#if defined(WIN32)
        _nearAddress.sin_port = (unsigned short) new_port;
#else
        _nearAddress.sin_port = getLocalPort(_udp_socket);
#endif
        _nearAddress.sin_addr.s_addr = getLocalAddress();

        // Set the socket to non-blocking and register it with
        // the event loop.
#if defined(WIN32)
        _udp_socket = (unsigned int) newHandle;
        (void) ioctlsocket(_udp_socket, FIONBIO, &blockingFlag);
#else
        (void) fcntl(_udp_socket, F_SETFL, O_NDELAY);
#endif
        Gevent_loop->addFD(_udp_socket, *this);

        // Save the far-end address for later use.
        (void) memcpy(&_farAddress, address, sizeof(_farAddress));

        _sequence_number = ISN;

        Gevent_loop->startTimer("CLIENT_OPENED",
                                MIN_TIMEOUT,
                                *this);
    }

    return;
} // end of TcpConnection::openClientSocket(const sockaddr_in*)

//---------------------------------------------------------------
// openSuccess() (Public)
// The socket has been successfully opened.
//
void TcpConnection::openSuccess()
{
    _listener->opened(*this);
    return;
} // end of TcpConnection::openSuccess()

//---------------------------------------------------------------
// openFailed(const char*) (Public)
// The socket could not be opened.
//
void TcpConnection::openFailed(const char *reason)
{
    _listener->openFailed(reason, *this);
    return;
} // end of TcpConnection::openFailed(const char*)

//---------------------------------------------------------------
// closeSocket() (Public)
// Finish closing the socket.
//
void TcpConnection::closeSocket()
{
    if (_udp_socket >= 0)
    {
        // Remove this UDP socket from the event loop.
        Gevent_loop->removeFD(_udp_socket);

        // Now close the UDP socket.
#if defined(WIN32)
        CloseHandle((HANDLE) _udp_socket);
        closesocket(_udp_win_socket);
        _udp_win_socket = NULL;
#else
        close(_udp_socket);
#endif
        _udp_socket = -1;
    }
    
    return;
} // end of TcpConnection::closeSocket()

//---------------------------------------------------------------
// halfClosed() (Public)
// The far end client socket has closed its half of the
// connection.
//
void TcpConnection::halfClosed()
{
    _listener->halfClosed(*this);
    return;
} // end of TcpConnection::halfClosed()

//---------------------------------------------------------------
// closed(const char*) (Public)
// The connection is completely closed.
//
void TcpConnection::closed(const char *reason)
{
    TcpConnectionListener *listener;

    if (_listener != NULL)
    {
        listener = _listener;
        _listener = NULL;
        listener->closed(reason, *this);
    }

    return;
} // end of TcpConnection::closed(const char*)

//---------------------------------------------------------------
// clearListener() (Public)
// Clear this connection's current listener.
//
void TcpConnection::clearListener()
{
    _listener = NULL;
    return;
} // end of TcpConnection::clearListener()

//---------------------------------------------------------------
// transmitted() (Public)
// Data transmission successfully completed.
//
void TcpConnection::transmitted()
{
    _listener->transmitted(*this);
    return;
} // end of TcpConnection::transmitted()

//---------------------------------------------------------------
// transmitFailed(const char*) (Public)
// Data transmission failed.
//
void TcpConnection::transmitFailed(const char *reason)
{
    _listener->transmitFailed(reason, *this);
    return;
} // end of TcpConnection::transmitFailed(const char*)

//---------------------------------------------------------------
// receive(const TcpSegment&) (Public)
// Send received data to the listener.
//
void TcpConnection::receive(const TcpSegment& segment)
{
    if (_listener != NULL)
    {
        _listener->receive(segment.getData(),
                           segment.getDataSize(),
                           *this);
    }

    return;
} // end of TcpConnection::receive(const TcpSegment&)

//---------------------------------------------------------------
// sendOpenSyn(const sockaddr_in*) (Public)
// Send the opening SYN message which requests connection to a
// service.
//
void TcpConnection::sendOpenSyn(const sockaddr_in *address)
{
    TcpSegment segment(*address,
                       _nearAddress,
                       0,
                       0,
                       TcpSegment::SYN,
                       NULL,
                       0,
                       0);

    doSend(TcpSegment::SYN, NULL, 0, 0, &segment);
    return;
} // end of TcpConnection::sendOpenSyn(const sockaddr_in*)

//---------------------------------------------------------------
// accept(const TcpSegment&) (Public)
// Create a client socket to handle the new connection.
//
void TcpConnection::accept(const TcpSegment& segment)
{
    TcpClient *accept_client;
    int new_socket;
#if defined(WIN32)
    unsigned long blockingFlag = 1;
    HANDLE newHandle;
    int new_port;
#else
    sockaddr_in clientAddress;
#endif

    // Create a new UDP socket to handle this connection.
    // Let it be assigned a random UDP port.
#if defined(WIN32)
    newHandle = NULL;
    if ((new_socket = socket(AF_INET, SOCK_DGRAM, 0))
            == INVALID_SOCKET ||
        DuplicateHandle(GetCurrentProcess(),
                        (HANDLE) new_socket,
                        GetCurrentProcess(),
                        &newHandle,
                        0,
                        FALSE,
                        DUPLICATE_SAME_ACCESS) == FALSE ||
        (new_port = doBind((int) newHandle)) < 0)
    {
        // If an error occurs now, ignore it.
        if (new_socket != INVALID_SOCKET)
        {
            closesocket(new_socket);
        }
        if (newHandle != NULL)
        {
            CloseHandle((HANDLE) newHandle);
        }
    }
    else
    {
        // Set the socket to non-blocking.
        (void) ioctlsocket((int) newHandle, FIONBIO, &blockingFlag);

        // Have the new client socket use this server
        // socket's near address for now.
        accept_client = new TcpClient(segment.getSource(),
                                      _nearAddress,
                                      (unsigned short) new_port,
                                      new_socket,
                                      newHandle,
                                      _sequence_number,
                                      (TcpServer&) *this,
                                      *_listener);
#else
    // Set up this client's address.
    (void) memset(&clientAddress, 0, sizeof(clientAddress));
    clientAddress.sin_family = AF_INET;
    clientAddress.sin_port = 0;
    clientAddress.sin_addr.s_addr = INADDR_ANY;

    if ((new_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
        bind(new_socket,
             reinterpret_cast<sockaddr*>(&clientAddress),
             sizeof(clientAddress))
            < 0)
    {
        // If an error occurs now, ignore it.
        if (new_socket >= 0)
        {
            close(new_socket);
        }
    }
    else
    {
        // Set the socket to non-blocking.
        (void) fcntl(new_socket, F_SETFL, O_NDELAY);

        // Have the new client socket use this server
        // socket's near address for now.
        accept_client = new TcpClient(segment.getSource(),
                                      _nearAddress,
                                      new_socket,
                                      _sequence_number,
                                      (TcpServer&) *this,
                                      *_listener);

#endif

        accept_client->acceptOpen(segment);
    }

    return;
} // end of TcpConnection::accept(const TcpSegment&)

//---------------------------------------------------------------
// accepted() (Public)
// Connection completely accepted.
//
void TcpConnection::accepted()
{
    TcpServer *server = _server;
    TcpConnectionListener *listener = _listener;

    // Tell the server listener that a new connection has
    // been accepted. Then clear the server listener because
    // this socket is now truly a client socket. Clear the
    // listener member data now because the callback method
    // will be resetting it and the reset will fail if we
    // don't do it.
    _server = NULL;
    _listener = NULL;
    listener->accepted((TcpClient&) *this, *server);

    return;
} // end of TcpConnection::accepted()

//---------------------------------------------------------------
// sendSynAck(const TcpSegment&) (Public)
// Acknowledge a SYN message.
//
void TcpConnection::sendSynAck(const TcpSegment& segment)
{
    unsigned short port;
    char port_bytes[2];

    // Tell the far end client socket with what port it should
    // now communicate.
#if defined(WIN32)
    port = htons(_actualPort);
#else
    port = getLocalPort(_udp_socket);
#endif

    port_bytes[0] = (char) ((port & 0xff00) >> 8);
    port_bytes[1] = (char) (port & 0x00ff);

    doSend(TcpSegment::SYN_ACK, port_bytes, 0, 2, &segment);

    return;
} // end of TcpConnection::sendSynAck(const TcpSegment&)

//---------------------------------------------------------------
// sendSynAckAck(const TcpSegment&) (Public)
// Acknowledge the service's acknowledge. Need to do this so
// doSend() will use the correct address.
//
void TcpConnection::sendSynAckAck(const TcpSegment& segment)
{
    TcpSegment faux_segment(_farAddress,
                            _nearAddress,
                            segment.getSequenceNumber(),
                            segment.getAcknowledgeNumber(),
                            segment.getFlags(),
                            NULL,
                            0,
                            0);

    doSend(TcpSegment::ACK, NULL, 0, 0, &faux_segment);
    return;
} // end of TcpConnection::sendSynAckAck(const TcpSegment&)

//---------------------------------------------------------------
// doSend(...) (Public)
// Send data to the specified address.
//
void TcpConnection::doSend(unsigned short flags,
                           const char *data,
                           int offset,
                           int size,
                           const TcpSegment *recv_segment)
{
    sockaddr_in to_address;
    unsigned long ack_number;
    TcpSegment *send_segment;
    char *packet;
    int packet_size;

    (void) memset(&to_address, 0, sizeof(to_address));

    // First, use the received segment.
    // Else, use the connection's far end.
    if (recv_segment != NULL)
    {
        (void) memcpy(&to_address,
                      &(recv_segment->getSource()),
                      sizeof(to_address));
    }
    // If the address was not specified, then send this segment
    // to whatever client socket we are currently speaking.
    else
    {
        (void) memcpy(&to_address, &_farAddress, sizeof(to_address));
    }

    // Send the ack number only if the ack flag is set.
    if ((flags & TcpSegment::ACK) == 0 || recv_segment == NULL)
    {
        ack_number = 0;
    }
    else
    {
        // Figure out the ack number based on the received
        // segment's sequence number and data size.
        ack_number = getAck(*recv_segment);
    }

    send_segment =
        new TcpSegment(_nearAddress,
                       to_address,
                       _sequence_number,
                       ack_number,
                       flags,
                       data,
                       offset,
                       size);

    // Advance the sequence number depending on the message sent.
    // Don't do this if the message came from an interloper.
    if (recv_segment == NULL ||
        (recv_segment->getSource()).sin_port == _farAddress.sin_port &&
        (recv_segment->getSource()).sin_addr.s_addr == _farAddress.sin_addr.s_addr)
    {
        _sequence_number = getAck(*send_segment);
    }

    // Now send the data. First convert the TCP segment into a
    // sequence of raw bytes.
    send_segment->packetize(packet, packet_size);

    if (packet != NULL && packet_size > 0)
    {
#if defined(SMC_DEBUG)
        cout << "**** Transmit segment:\n" << *send_segment << endl;
#endif

#if defined(WIN32)
        if (sendto(_udp_socket,
                   packet,
                   packet_size,
                   NO_SEND_FLAGS,
                   reinterpret_cast<sockaddr *>(&to_address),
                   sizeof(to_address)) == SOCKET_ERROR)
#else
        if (sendto(_udp_socket,
                   packet,
                   packet_size,
                   NO_SEND_FLAGS,
                   reinterpret_cast<sockaddr *>(&to_address),
                   sizeof(to_address)) < 0)
#endif
            {
                cerr << "Transmit failed - "
                     << strerror(errno)
                     << "."
                     << endl;
            }

        // Free up the allocated packet.
        delete[] packet;
    }

    delete send_segment;

    return;
} // end of TcpConnection::doSend(...)

//---------------------------------------------------------------
// startTimer(const char*, long) (Public)
// Start the named timer running.
//
void TcpConnection::startTimer(const char *name, long duration)
{
#if defined(SMC_DEBUG)
    cout << "**** Starting timer "
         << name
         << ", duration: "
         << duration
         << " milliseconds."
         << endl;
#endif

    Gevent_loop->startTimer(name, duration, *this);
    return;
} // end of TcpConnection::startTimer(const char*, long)

//---------------------------------------------------------------
// stopTimer(const char*) (Public)
// Stop the named timer.
//
void TcpConnection::stopTimer(const char* name)
{
#if defined(SMC_DEBUG)
    cout << "**** Stopping timer " << name << "." << endl;
#endif

    Gevent_loop->stopTimer(name, *this);
    return;
} // end of TcpConnection::stopTimer(const char*)

//---------------------------------------------------------------
// setNearAddress() (Public)
// Figure out this socket's port and address based on the socket.
//
void TcpConnection::setNearAddress()
{
    // Get the UDP socket's address.
    _nearAddress.sin_port = AF_INET;
#if defined(WIN32)
    _nearAddress.sin_port = _actualPort;
#else
    _nearAddress.sin_port = getLocalPort(_udp_socket);
#endif
    _nearAddress.sin_addr.s_addr = getLocalAddress();

    return;
} // end of TcpConnection::setNearAddress()

//---------------------------------------------------------------
// setFarAddress(const TcpSegment&) (Public)
// Send data to a new client port instead of the server port.
//
void TcpConnection::setFarAddress(const TcpSegment& segment)
{
    const char *data = segment.getData();
    const int data_size = segment.getDataSize();
    unsigned short port;

    _farAddress.sin_family = AF_INET;
    port = ((((unsigned short) data[0]) & 0x00ff) << 8) |
            (((unsigned short) data[1]) & 0x00ff);
    _farAddress.sin_port = ntohs(port);
    _farAddress.sin_addr.s_addr = segment.getSource().sin_addr.s_addr;

    return;
} // end of TcpConnection::setFarAddress(const TcpSegment&)

//---------------------------------------------------------------
// deleteSegment(const TcpSegment&) (Public)
// Delete a segment object.
//
void TcpConnection::deleteSegment(const TcpSegment& segment)
{
    delete const_cast<TcpSegment*>(&segment);
    return;
} // end of TcpConnection::deleteSegment(const TcpSegment&)

//---------------------------------------------------------------
// TcpConnection(TcpConnectionListener&) (Protected)
// Create a server socket.
//
TcpConnection::TcpConnection(TcpConnectionListener& listener)
: _listener(&listener),
  _sequence_number(ISN),
  _udp_socket(-1),
  _server(NULL),
  _buffer(NULL),
  _bufferSize(0),
  _errorMessage(NULL),
  _fsm(*this)
{
    (void) memset(&_nearAddress, 0, sizeof(_nearAddress));
    (void) memset(&_farAddress, 0, sizeof(_farAddress));
    expandBuffer();

#if defined(SMC_DEBUG)
    // Turn on state map debug printout.
    _fsm.setDebugFlag(true);
#endif

    return;
} // end of TcpConnection::TcpConnection(TcpConnectionListener&)

//---------------------------------------------------------------
// TcpConnection(const sockaddr_in&, ...) (Protected)
// "Accepted" client connection.
//
TcpConnection::TcpConnection(const sockaddr_in& far_address,
                             const sockaddr_in& near_address,
#if defined(WIN32)
                             unsigned short actual_port,
                             SOCKET udp_socket,
                             HANDLE udp_handle,
#else
                             int udp_socket,
#endif
                             int sequence_number,
                             TcpServer& server,
                             TcpConnectionListener& listener)
: _listener(&listener),
#if defined(WIN32)
  _actualPort(actual_port),
  _udp_win_socket(udp_socket),
  _udp_socket((int) udp_handle),
#else
  _udp_socket(udp_socket),
#endif
  _sequence_number(sequence_number),
  _server(&server),
  _buffer(NULL),
  _bufferSize(0),
  _errorMessage(NULL),
  _fsm(*this)
{
    // Register the UDP socket with the event loop.
    Gevent_loop->addFD(_udp_socket, *this);

    (void) memcpy(&_nearAddress, &near_address, sizeof(_nearAddress));
    (void) memcpy(&_farAddress, &far_address, sizeof(_farAddress));

    // Set up the input buffer.
    expandBuffer();

#if defined(SMC_DEBUG)
    // Uncomment to turn on state map debug printout.
    _fsm.setDebugFlag(true);
#endif

    return;
} // end of TcpConnection::TcpConnection(...)

//---------------------------------------------------------------
// ~TcpConnection() (Protected)
// Destructor.
//
TcpConnection::~TcpConnection()
{
    if (_buffer != NULL)
    {
        delete[] _buffer;
        _buffer = NULL;
        _bufferSize = 0;
    }

    if (_errorMessage != NULL)
    {
        delete[] _errorMessage;
        _errorMessage = NULL;
    }

    // Just in case ...
    closeSocket();

    return;
} // end of TcpConnection::~TcpConnection()

//---------------------------------------------------------------
// passiveOpen(unsigned short) (Protected)
// Open a server socket.
//
void TcpConnection::passiveOpen(unsigned short port)
{
    _fsm.PassiveOpen(port);
    return;
} // end of TcpConnection::passiveOpen(unsigned short)

//---------------------------------------------------------------
// activeOpen(const sockaddr_in&) (Protected)
// Open a client socket.
//
void TcpConnection::activeOpen(const sockaddr_in& address)
{
    _fsm.ActiveOpen(&address);
    return;
} // end of TcpConnection::activeOpen(const sockaddr_in&)

//---------------------------------------------------------------
// acceptOpen(const TcpSegment&) (Protected)
// "Open" an accepted client connection.
//
void TcpConnection::acceptOpen(const TcpSegment& segment)
{
    _fsm.AcceptOpen(segment);
    return;
} // end of TcpConnection::acceptOpen(const TcpSegment&)

//---------------------------------------------------------------
// expandBuffer() (Private)
// Expand the buffer's current size by one block.
//
void TcpConnection::expandBuffer()
{
    char *newBuffer;
    int newSize,
        currBlocks;

    currBlocks = _bufferSize / BUFFER_BLOCK_SIZE;
    newSize = (currBlocks + 1) * BUFFER_BLOCK_SIZE;
    newBuffer = new char[newSize];

    // If the current buffer has data in it, copy it over to the
    // new buffer before deleting the current buffer.
    if (_buffer != NULL)
    {
        (void) memcpy(newBuffer, _buffer, _bufferSize);
        delete[] _buffer;
    }

    _buffer = newBuffer;
    _bufferSize = newSize;

    return;
} // end of TcpConnection::expandBuffer()

#if defined(WIN32)
//---------------------------------------------------------------
// doBind(int) const (Private)
// Bind this UDP socket to a random, available port number.
//
int TcpConnection::doBind(int handle) const
{
    sockaddr_in address;
    unsigned short portIt;
    int socket_error,
        retval;

    // Set up this client's address.
    (void) memset(&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = 0;
    address.sin_addr.s_addr = INADDR_ANY;

    // Start at the largest number and work down.
    for (portIt = MIN_PORT; portIt <= MAX_PORT; ++portIt)
    {
        address.sin_port = htons(portIt);
        if (bind(handle, (sockaddr *) &address, sizeof(sockaddr_in))
                == SOCKET_ERROR)
        {
            socket_error = WSAGetLastError();
            if (socket_error != WSAEADDRINUSE &&
                socket_error != WSAEADDRNOTAVAIL)
            {
                retval = -1;
                break;
            }
        }
        else
        {
            // The bind worked. Return the port number.
            retval = address.sin_port;
            break;
        }
    }

    return(retval);
} // end of TcpConnection::doBind(int) const
#else
//---------------------------------------------------------------
// getLocalPort(int) const (Private)
// Figure out the UDP socket's port number.
//
unsigned short TcpConnection::getLocalPort(int fd) const
{
    socklen_t size;
    sockaddr_in address;
    unsigned short retval;

    size = sizeof(address);
    if (getsockname(fd,
                    reinterpret_cast<sockaddr *>(&address),
                    &size) < 0)
    {
        retval = 0xffff;
    }
    else
    {
        retval = address.sin_port;
    }

    return(retval);
} // end of TcpConnection::getLocalPort(int) const
#endif

//---------------------------------------------------------------
// getLocalAddress() const (Private)
// Return this machine's local internet address. Do this by
// first retrieving the hostname and then doing gethostbyname()
// on itself.
//
unsigned long TcpConnection::getLocalAddress() const
{
    char hostname[MAX_HOSTNAME_LEN];
    hostent *hostEntry;
    unsigned long retval;

    // 1. Get this machine's network host name.
#if defined(__sun)
    (void) sysinfo(SI_HOSTNAME, hostname, MAX_HOSTNAME_LEN);
#else
    (void) gethostname(hostname, MAX_HOSTNAME_LEN);
#endif

    // 2. Get this host name's address.
    if ((hostEntry = gethostbyname(hostname)) != NULL)
    {
        (void) memcpy(&retval,
                      hostEntry->h_addr,
                      hostEntry->h_length);
    }
    else
    {
        // If there is no known address for this host name,
        // default to 127.0.0.1.
        retval = LOCAL_HOST_ADDRESS;
    }

    return(retval);
} // end of TcpConnection::getLocalAddress() const

//---------------------------------------------------------------
// getAck(const TcpSegment&) (Private)
// Figure out what the acknowledgement number should be based on
// the TCP segment's type and size.
//
int TcpConnection::getAck(const TcpSegment& segment)
{
    int retval;

    // The ack # depends on the segment's flags.
    switch (segment.getFlags())
    {
        case TcpSegment::FIN:
        case TcpSegment::SYN:
        case TcpSegment::FIN_ACK:
        case TcpSegment::SYN_ACK:
            retval = segment.getSequenceNumber() + 1;
            break;

        case TcpSegment::PSH:
        case TcpSegment::PSH_ACK:
            retval = segment.getSequenceNumber() +
                     segment.getDataSize();
            break;

        case TcpSegment::ACK:
        default:
            retval = segment.getSequenceNumber();
            break;
    }

    return(retval);
} // end of TcpConnection::getAck(const TcpSegment&)
