?lang_form? ?lang_select? ?lang_submit? ?lang_endform?
{HEADER END}
{FILE START}

library

?curdirlinks? - Rev 32

?prevdifflink? - Blame - ?getfile?

/*********************************************************************
 *
 *      Transmission Control Protocol (TCP) Communications Layer
 *  Module for Microchip TCP/IP Stack
 *       -Provides reliable, handshaked transport of application stream 
 *    oriented data with flow control
 *       -Reference: RFC 793
 *
 *********************************************************************
 * FileName:        TCP.c
 * Dependencies:    IP, Tick, Ethernet/WiFi (ENC28J60.c, ETH97J60.c, 
 *                                      ENCX24J600.c, or WFMac.c), ARP (optional), 
 *                                      DNS (optional)
 * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
 * Compiler:        Microchip C32 v1.05 or higher
 *                                      Microchip C30 v3.12 or higher
 *                                      Microchip C18 v3.30 or higher
 *                                      HI-TECH PICC-18 PRO 9.63PL2 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright (C) 2002-2009 Microchip Technology Inc.  All rights
 * reserved.
 *
 * Microchip licenses to you the right to use, modify, copy, and
 * distribute:
 * (i)  the Software when embedded on a Microchip microcontroller or
 *      digital signal controller product ("Device") which is
 *      integrated into Licensee's product; or
 * (ii) ONLY the Software driver source files ENC28J60.c, ENC28J60.h,
 *              ENCX24J600.c and ENCX24J600.h ported to a non-Microchip device
 *              used in conjunction with a Microchip ethernet controller for
 *              the sole purpose of interfacing with the ethernet controller.
 *
 * You should refer to the license agreement accompanying this
 * Software for additional information regarding your rights and
 * obligations.
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author               Date            Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Nilesh Rajbharti     5/8/01          Original        (Rev 1.0)
 * Howard Schlunder             12/11/06        Changed almost everything to 
 *                                                                      better meet RFC 793.
 ********************************************************************/
#define __TCP_C

#include "TCPIP Stack/TCPIP.h"

#if defined(STACK_USE_TCP)

/****************************************************************************
  Section:
        Configuration Parameters
  ***************************************************************************/

// Starting port for client sockets
#define LOCAL_PORT_START_NUMBER (1024u)
// End port for client sockets
#define LOCAL_PORT_END_NUMBER   (5000u)

// For debugging only.  Normal applications should never enable these
//#define DEBUG_GENERATE_TX_LOSS                31129
//#define DEBUG_GENERATE_RX_LOSS                32113

// A lot of pointer dereference code can be removed if you 
// locally copy TCBStubs to an absolute memory location.
// If you define TCP_OPTIMIZE_FOR_SIZE, local caching will 
// occur and will substantially decrease the entire TCP ROM 
// footprint (up to 35%).  If you leave TCP_OPTIMIZE_FOR_SIZE 
// undefined, the local caching will be disabled.  On PIC18 
// products, this will improve TCP performance/throughput by 
// approximately 15%.
#define TCP_OPTIMIZE_FOR_SIZE

// For smallest size and best throughput, TCP_OPTIMIZE_FOR_SIZE 
// should always be enabled on PIC24/dsPIC products.  On PIC32 
// products there is very little difference and depnds on compiler 
// optimization level
#if defined(__C30__) && !defined(TCP_OPTIMIZE_FOR_SIZE)
        #define TCP_OPTIMIZE_FOR_SIZE
#elif defined(__C32__) && defined(TCP_OPTIMIZE_FOR_SIZE)
        #undef TCP_OPTIMIZE_FOR_SIZE
#endif

// TCP Maximum Segment Size for TX.  The TX maximum segment size is actually 
// govered by the remote node's MSS option advirtised during connection 
// establishment.  However, if the remote node specifies an unhandlably large 
// MSS (ex: > Ethernet MTU), this define sets a hard limit so that we don't 
// cause any TX buffer overflows.  If the remote node does not advirtise a MSS 
// option, all TX segments are fixed at 536 bytes maximum.
#define TCP_MAX_SEG_SIZE_TX                     (1460u)

// TCP Maximum Segment Size for RX.  This value is advirtised during connection 
// establishment and the remote node should obey it.  This should be set to 536 
// to avoid IP layer fragmentation from causing packet loss.  However, raising 
// its value can enhance performance at the (small) risk of introducing 
// incompatibility with certain special remote nodes (ex: ones connected via a 
// slow dial up modem).
#define TCP_MAX_SEG_SIZE_RX                     (536u)

// TCP Timeout and retransmit numbers
#define TCP_START_TIMEOUT_VAL           ((DWORD)TICK_SECOND*1)  // Timeout to retransmit unacked data
#define TCP_DELAYED_ACK_TIMEOUT         ((DWORD)TICK_SECOND/10) // Timeout for delayed-acknowledgement algorithm
#define TCP_FIN_WAIT_2_TIMEOUT          ((DWORD)TICK_SECOND*5)  // Timeout for FIN WAIT 2 state
#define TCP_KEEP_ALIVE_TIMEOUT          ((DWORD)TICK_SECOND*10) // Timeout for keep-alive messages when no traffic is sent
#define TCP_CLOSE_WAIT_TIMEOUT          ((DWORD)TICK_SECOND/5)  // Timeout for the CLOSE_WAIT state
#define TCP_MAX_RETRIES                     (5u)                                        // Maximum number of retransmission attempts
#define TCP_MAX_UNACKED_KEEP_ALIVES     (6u)                                    // Maximum number of keep-alive messages that can be sent without receiving a response before automatically closing the connection
#define TCP_MAX_SYN_RETRIES                     (2u)    // Smaller than all other retries to reduce SYN flood DoS duration

#define TCP_AUTO_TRANSMIT_TIMEOUT_VAL   (TICK_SECOND/25ull)     // Timeout before automatically transmitting unflushed data
#define TCP_WINDOW_UPDATE_TIMEOUT_VAL   (TICK_SECOND/5ull)      // Timeout before automatically transmitting a window update due to a TCPGet() or TCPGetArray() function call

#define TCP_SYN_QUEUE_MAX_ENTRIES       (3u)                                    // Number of TCP RX SYN packets to save if they cannot be serviced immediately
#define TCP_SYN_QUEUE_TIMEOUT           ((DWORD)TICK_SECOND*3)  // Timeout for when SYN queue entries are deleted if unserviceable

/****************************************************************************
  Section:
        TCP Header Data Types
  ***************************************************************************/

#define FIN     (0x01)          // FIN Flag as defined in RFC
#define SYN     (0x02)          // SYN Flag as defined in RFC
#define RST     (0x04)          // Reset Flag as defined in RFC
#define PSH     (0x08)          // Push Flag as defined in RFC
#define ACK     (0x10)          // Acknowledge Flag as defined in RFC
#define URG     (0x20)          // Urgent Flag as defined in RFC

// TCP Header Data Structure
typedef struct
{
        WORD    SourcePort;             // Local port number
        WORD    DestPort;               // Remote port number
        DWORD   SeqNumber;              // Local sequence number
        DWORD   AckNumber;              // Acknowledging remote sequence number

        struct
        {
                unsigned char Reserved3      : 4;
                unsigned char Val            : 4;
        } DataOffset;                   // Data offset flags nibble

        union
        {
                struct
                {
                        unsigned char flagFIN    : 1;
                        unsigned char flagSYN    : 1;
                        unsigned char flagRST    : 1;
                        unsigned char flagPSH    : 1;
                        unsigned char flagACK    : 1;
                        unsigned char flagURG    : 1;
                        unsigned char Reserved2  : 2;
                } bits;
                BYTE byte;
        } Flags;                                // TCP Flags as defined in RFC

        WORD    Window;                 // Local free RX buffer window
        WORD    Checksum;               // Data payload checksum
        WORD    UrgentPointer;  // Urgent pointer
} TCP_HEADER;

#define TCP_OPTIONS_END_OF_LIST     (0x00u)             // End of List TCP Option Flag
#define TCP_OPTIONS_NO_OP           (0x01u)             // No Op TCP Option
#define TCP_OPTIONS_MAX_SEG_SIZE    (0x02u)             // Maximum segment size TCP flag
typedef struct
{
        BYTE        Kind;                                                       // Type of option
        BYTE        Length;                                                     // Length
        WORD_VAL    MaxSegSize;                                         // Maximum segment size
} TCP_OPTIONS;                                                                  // TCP Options data structure                                                   

// Structure containing all the important elements of an incomming 
// SYN packet in order to establish a connection at a future time 
// if all sockets on the listening port are already connected to 
// someone
typedef struct 
{
        NODE_INFO       niSourceAddress;// Remote IP address and MAC address
        WORD            wSourcePort;    // Remote TCP port number that the response SYN needs to be sent to
        DWORD           dwSourceSEQ;    // Remote TCP SEQuence number that must be ACKnowledged when we send our response SYN
        WORD            wDestPort;              // Local TCP port which the original SYN was destined for
        WORD            wTimestamp;             // Timer to expire old SYN packets that can't be serviced at all
} TCP_SYN_QUEUE;


#if defined(STACK_CLIENT_MODE)
static WORD NextPort __attribute__((persistent));       // Tracking variable for next local client port number
#endif

/****************************************************************************
  Section:
        TCB Definitions
  ***************************************************************************/

// Determines the number of defined TCP sockets
#define TCP_SOCKET_COUNT        (sizeof(TCPSocketInitializer)/sizeof(TCPSocketInitializer[0]))


#if defined(HI_TECH_C)
        // The initializer forces this large array out of the bss section 
        // so we can link correctly.
        #pragma psect bigdata=TCB_uRAM_BIG
        #pragma psect data=TCB_uRAM
        static TCB_STUB TCBStubs[TCP_SOCKET_COUNT] = {'\0'};    
        #pragma psect data=ordinary_data_sect
        #pragma psect bigdata=ordinary_data_sect_big
#else
        // The TCB array is very large.  With the C18 compiler, one must 
        // modify the linker script to make an array that spans more than 
        // one memory bank.  To do this, make the necessary changes to your 
        // processor's linker script (.lkr).  Here is an example showing 
        // gpr11 and 128 bytes of gpr12 being combined into one 384 byte 
        // block used exclusively by the TCB_uRAM data section:
        // ...
        // //DATABANK   NAME=gpr11      START=0xB00          END=0xBFF
        // //DATABANK   NAME=gpr12      START=0xC00          END=0xCFF
        // DATABANK   NAME=gpr11b     START=0xB00          END=0xC7F           PROTECTED
        // DATABANK   NAME=gpr12      START=0xC80          END=0xCFF
        // ...
        // SECTION    NAME=TCB_uRAM    RAM=gpr11b
        // ...
        #if defined(__18CXX) && !defined(HI_TECH_C)     
                #pragma udata TCB_uRAM
        #endif
        static TCB_STUB TCBStubs[TCP_SOCKET_COUNT];
        #if defined(__18CXX) && !defined(HI_TECH_C)     
                #pragma udata                                   // Return to any other RAM section
        #endif
#endif

static TCB MyTCB;                                                                       // Currently loaded TCB
static TCP_SOCKET hCurrentTCP = INVALID_SOCKET;         // Current TCP socket
#if TCP_SYN_QUEUE_MAX_ENTRIES
        #if defined(__18CXX) && !defined(HI_TECH_C)     
                #pragma udata SYN_QUEUE_RAM_SECT
        #endif
        static TCP_SYN_QUEUE SYNQueue[TCP_SYN_QUEUE_MAX_ENTRIES];       // Array of saved incoming SYN requests that need to be serviced later
        #if defined(__18CXX) && !defined(HI_TECH_C)     
                #pragma udata
        #endif
#endif

/****************************************************************************
  Section:
        Function Prototypes
  ***************************************************************************/

static void TCPRAMCopy(PTR_BASE wDest, BYTE vDestType, PTR_BASE wSource, BYTE vSourceType, WORD wLength);

#if defined(__18CXX)
        static void TCPRAMCopyROM(PTR_BASE wDest, BYTE wDestType, ROM BYTE* wSource, WORD wLength);
#else
        #define TCPRAMCopyROM(a,b,c,d)  TCPRAMCopy(a,b,c,TCP_PIC_RAM,d)
#endif

static void SendTCP(BYTE vTCPFlags, BYTE vSendFlags);
static void HandleTCPSeg(TCP_HEADER* h, WORD len);
static BOOL FindMatchingSocket(TCP_HEADER* h, NODE_INFO* remote);
static void SwapTCPHeader(TCP_HEADER* header);
static void CloseSocket(void);
static void SyncTCB(void);

// Indicates if this packet is a retransmission (no reset) or a new packet (reset required)
#define SENDTCP_RESET_TIMERS    0x01
// Instead of transmitting normal data, a garbage octet is transmitted according to RFC 1122 section 4.2.3.6
#define SENDTCP_KEEP_ALIVE              0x02


/****************************************************************************
  Section:
        TCB Optimization Configuration
  ***************************************************************************/

#if defined(TCP_OPTIMIZE_FOR_SIZE)
        static TCB_STUB MyTCBStub;
        
        // Flushes MyTCBStub cache and loads up the specified TCB_STUB.
        // Does nothing on cache hit.
        static void SyncTCBStub(TCP_SOCKET hTCP)
        {
                if(hCurrentTCP == hTCP)
                        return;
        
                if(hCurrentTCP != INVALID_SOCKET)
                {
                        // Save the current TCB stub
                        memcpy((void*)&TCBStubs[hCurrentTCP], (void*)&MyTCBStub, sizeof(MyTCBStub));
                }
        
                hCurrentTCP = hTCP;
        
                if(hTCP == INVALID_SOCKET)
                        return;
        
                // Load up the new TCB stub
                memcpy((void*)&MyTCBStub, (void*)&TCBStubs[hTCP], sizeof(MyTCBStub));
        }
#else
        // Flushes MyTCBStub cache and loads up the specified TCB_STUB.
        // Does nothing on cache hit.
        #define SyncTCBStub(a)  hCurrentTCP = (a)
        // Alias to current TCP stub.
        #define MyTCBStub               TCBStubs[hCurrentTCP]
#endif



// Flushes MyTCB cache and loads up the specified TCB.
// Does nothing on cache hit.
static void SyncTCB(void)
{
        static TCP_SOCKET hLastTCB = INVALID_SOCKET;
        
        if(hLastTCB == hCurrentTCP)
                return;

        if(hLastTCB != INVALID_SOCKET)
        {
                // Save the current TCB
                TCPRAMCopy(TCBStubs[hLastTCB].bufferTxStart - sizeof(MyTCB), TCBStubs[hLastTCB].vMemoryMedium, (PTR_BASE)&MyTCB, TCP_PIC_RAM, sizeof(MyTCB));
        }

        // Load up the new TCB
        hLastTCB = hCurrentTCP;
        TCPRAMCopy((PTR_BASE)&MyTCB, TCP_PIC_RAM, MyTCBStub.bufferTxStart - sizeof(MyTCB), MyTCBStub.vMemoryMedium, sizeof(MyTCB));
}


/*****************************************************************************
  Function:
        void TCPInit(void)

  Summary:
        Initializes the TCP module.

  Description:
        Initializes the TCP module.  This function sets up the TCP buffers
        in memory and initializes each socket to the CLOSED state.  If
        insufficient memory was allocated for the TCP sockets, the function
        will hang here to be captured by the debugger.

  Precondition:
        None

  Parameters:
        None

  Returns:
        None
        
  Remarks:
        This function is called only one during lifetime of the application.
  ***************************************************************************/
void TCPInit(void)
{
        BYTE i;
        BYTE vSocketsAllocated;
        WORD wTXSize, wRXSize;
        PTR_BASE ptrBaseAddress;
        BYTE vMedium;
        #if TCP_ETH_RAM_SIZE > 0
        WORD wCurrentETHAddress = TCP_ETH_RAM_BASE_ADDRESS;
        #endif
        #if TCP_PIC_RAM_SIZE > 0
        PTR_BASE ptrCurrentPICAddress = TCP_PIC_RAM_BASE_ADDRESS;
        #endif
        #if TCP_SPI_RAM_SIZE > 0
        WORD wCurrentSPIAddress = TCP_SPI_RAM_BASE_ADDRESS;
        #endif

        #if defined(STACK_CLIENT_MODE)
                // Initialize NextPort to a random value if it is zero (such as after 
                // reset on a PIC32 or PIC18 when the static memory initializer is 
                // used).  By starting with a random number, we decrease the risk of 
                // reusing a port number that was previously used if the user power 
                // cycles the device.
                if(NextPort == 0u)
                        NextPort = (((WORD)GenerateRandomDWORD()) & 0x07FFu) + LOCAL_PORT_START_NUMBER;
        #endif


        // Mark all SYN Queue entries as invalid by zeroing the memory
        #if TCP_SYN_QUEUE_MAX_ENTRIES
                memset((void*)SYNQueue, 0x00, sizeof(SYNQueue));
        #endif
        
        // Allocate all socket FIFO addresses
        vSocketsAllocated = 0;
        for(i = 0; i < TCP_SOCKET_COUNT; i++)
        {
                // Generate all needed sockets of each type (TCP_PURPOSE_*)
                SyncTCBStub(i);
        
                vMedium = TCPSocketInitializer[i].vMemoryMedium;
                wTXSize = TCPSocketInitializer[i].wTXBufferSize;
                wRXSize = TCPSocketInitializer[i].wRXBufferSize;
        
                switch(vMedium)
                {
                        #if TCP_ETH_RAM_SIZE > 0
                        case TCP_ETH_RAM:
                                ptrBaseAddress = wCurrentETHAddress;
                                wCurrentETHAddress += sizeof(TCB) + wTXSize+1 + wRXSize+1;
                                // Do a sanity check to ensure that we aren't going to use memory that hasn't been allocated to us.
                                // If your code locks up right here, it means you've incorrectly allocated your TCP socket buffers in TCPIPConfig.h.  See the TCP memory allocation section.  More RAM needs to be allocated to the base memory mediums, or the individual sockets TX and RX FIFOS and socket quantiy needs to be shrunken.
                                while(wCurrentETHAddress > TCP_ETH_RAM_BASE_ADDRESS + TCP_ETH_RAM_SIZE);
                                break;
                        #endif
                                
                        #if TCP_PIC_RAM_SIZE > 0
                        case TCP_PIC_RAM:
                                ptrBaseAddress = ptrCurrentPICAddress;
                                ptrCurrentPICAddress += sizeof(TCB) + wTXSize+1 + wRXSize+1;
                                // Do a sanity check to ensure that we aren't going to use memory that hasn't been allocated to us.
                                // If your code locks up right here, it means you've incorrectly allocated your TCP socket buffers in TCPIPConfig.h.  See the TCP memory allocation section.  More RAM needs to be allocated to the base memory mediums, or the individual sockets TX and RX FIFOS and socket quantiy needs to be shrunken.
                                while(ptrCurrentPICAddress > TCP_PIC_RAM_BASE_ADDRESS + TCP_PIC_RAM_SIZE);
                                break;
                        #endif
                                
                        #if TCP_SPI_RAM_SIZE > 0
                        case TCP_SPI_RAM:
                                ptrBaseAddress = wCurrentSPIAddress;
                                wCurrentSPIAddress += sizeof(TCB) + wTXSize+1 + wRXSize+1;
                                // Do a sanity check to ensure that we aren't going to use memory that hasn't been allocated to us.
                                // If your code locks up right here, it means you've incorrectly allocated your TCP socket buffers in TCPIPConfig.h.  See the TCP memory allocation section.  More RAM needs to be allocated to the base memory mediums, or the individual sockets TX and RX FIFOS and socket quantiy needs to be shrunken.
                                while(wCurrentSPIAddress > TCP_SPI_RAM_BASE_ADDRESS + TCP_SPI_RAM_SIZE);
                                break;
                        #endif
                        
                        default:
                                while(1); // Undefined allocation medium.  Go fix your TCPIPConfig.h TCP memory allocations.
                }
        
                MyTCBStub.vMemoryMedium = vMedium;
                MyTCBStub.bufferTxStart = ptrBaseAddress + sizeof(TCB);
                MyTCBStub.bufferRxStart = MyTCBStub.bufferTxStart + wTXSize + 1;
                MyTCBStub.bufferEnd             = MyTCBStub.bufferRxStart + wRXSize;
                MyTCBStub.smState               = TCP_CLOSED;
                MyTCBStub.Flags.bServer = FALSE;
                #if defined(STACK_USE_SSL)
                MyTCBStub.sslStubID = SSL_INVALID_ID;
                #endif          

                SyncTCB();
                MyTCB.vSocketPurpose = TCPSocketInitializer[i].vSocketPurpose;
                CloseSocket();
        }
}

/****************************************************************************
  Section:
        Connection Management Functions
  ***************************************************************************/


/*****************************************************************************
  Function:
        TCP_SOCKET TCPOpen(DWORD dwRemoteHost, BYTE vRemoteHostType, WORD wPort, BYTE vSocketPurpose)
    
  Summary:
    Opens a TCP socket for listening or as a client.

  Description:
    Provides a unified method for opening TCP sockets. This function can
    open both client and server sockets. For client sockets, it can accept
    a host name string to query in DNS, an IP address as a string, an IP
    address in binary form, or a previously resolved NODE_INFO structure
    containing the remote IP address and associated MAC address. When a
    host name or IP address only is provided, the TCP module will
    internally perform the necessary DNS and/or ARP resolution steps before
    reporting that the TCP socket is connected (via a call to
    TCPISConnected returning TRUE). Server sockets ignore this destination
    parameter and listen only on the indicated port.
    
    The vSocketPurpose field allows sockets to be opened with varying
    buffer size parameters and memory storage mediums. This field
    corresponds to pre-defined sockets allocated in the
    TCPSocketInitializer[] array in TCPIPConfig.h. The TCPIPConfig.h file
    can be edited using the TCP/IP Configuration Wizard.
    
    Sockets are statically allocated on boot, but can be claimed with this
    \function and freed using TCPDisconnect or TCPClose (for client
    sockets). Server sockets can be freed using TCPClose only (calls to
    TCPDisconnect will return server sockets to the listening state,
    allowing reuse).

  Conditions:
    TCP is initialized.

  Input:
    dwRemoteHost -     For client sockets only. Provide a pointer to a
                       null\-terminated string of the remote host name (ex\:
                       "www.microchip.com" or "192.168.1.123"), a literal
                       destination IP address (ex\: 0x7B01A8C0 or an IP_ADDR
                       data type), or a pointer to a NODE_INFO structure
                       with the remote IP address and remote node or gateway
                       MAC address specified. If a string is provided, note
                       that it must be statically allocated in memory and
                       cannot be modified or deallocated until
                       TCPIsConnected returns TRUE.<p />This parameter is
                       ignored for server sockets.
    vRemoteHostType -  Any one of the following flags to identify the
                       meaning of the dwRemoteHost parameter\:
                       * TCP_OPEN_SERVER &#45; Open a server socket and
                         ignore the dwRemoteHost parameter.
                       * TCP_OPEN_RAM_HOST &#45; Open a client socket and
                         connect it to a remote host who's name is stored as a
                         null terminated string in a RAM array. Ex\:
                         "www.microchip.com" or "192.168.0.123" (BYTE&#42;
                         type)
                       * TCP_OPEN_ROM_HOST &#45; Open a client socket and
                         connect it to a remote host who's name is stored as a
                         null terminated string in a literal string or ROM
                         array. Ex\: "www.microchip.com" or "192.168.0.123"
                         (ROM BYTE&#42; type)
                       * TCP_OPEN_IP_ADDRESS &#45; Open a client socket and
                         connect it to a remote IP address. Ex\: 0x7B01A8C0
                         for 192.168.1.123 (DWORD type). Note that the byte
                         ordering is big endian.
                       * TCP_OPEN_NODE_INFO &#45; Open a client socket and
                         connect it to a remote IP and MAC addresses pair
                         stored in a NODE_INFO structure. dwRemoteHost must be
                         a pointer to the NODE_INFO structure. This option is
                         provided for backwards compatibility with
                         applications built against prior stack versions that
                         only implemented the TCPConnect() function. It can
                         also be used to skip DNS and ARP resolution steps if
                         connecting to a remote node which you've already
                         connected to and have cached addresses for.
    wPort -            TCP port to listen on or connect to\:
                       * Client sockets &#45; the remote TCP port to which a
                         connection should be made. The local port for client
                         sockets will be automatically picked by the TCP
                         module.
                       * Server sockets &#45; the local TCP port on which to
                         listen for connections.
    vSocketPurpose -   Any of the TCP_PURPOSE_* constants defined in
                       TCPIPConfig.h or the TCPIPConfig utility (see
                       TCPSocketInitializer[] array).

  Return Values:
    INVALID_SOCKET -  No sockets of the specified type were available to be
                      opened.
    Otherwise -       A TCP_SOCKET handle. Save this handle and use it when
                      calling all other TCP APIs.

  Remarks:
    This function replaces the old TCPConnect and TCPListen functions.
    
    If TCP_OPEN_RAM_HOST or TCP_OPEN_ROM_HOST are used for the destination
    type, the DNS client module must also be enabled (STACK_USE_DNS must be
    defined in TCPIPConfig.h).

  Example:
    \ \ 
    <code>
    // Open a server socket
    skt = TCPOpen(NULL, TCP_OPEN_SERVER, HTTP_PORT, TCP_PURPOSE_HTTP_SERVER);
    
    // Open a client socket to www.microchip.com
    // The double cast here prevents compiler warnings
    skt = TCPOpen((DWORD)(PTR_BASE)"www.microchip.com",
                    TCP_OPEN_ROM_HOST, 80, TCP_PURPOSE_DEFAULT);
    
    // Reopen a client socket without repeating DNS or ARP
    SOCKET_INFO cache = TCPGetSocketInfo(skt);  // Call with the old socket
    skt = TCPOpen((DWORD)(PTR_BASE)&amp;cache.remote, TCP_OPEN_NODE_INFO,
                    cache.remotePort.Val, TCP_PURPOSE_DEFAULT);
    </code>                                                    
  *****************************************************************************/
TCP_SOCKET TCPOpen(DWORD dwRemoteHost, BYTE vRemoteHostType, WORD wPort, BYTE vSocketPurpose)
{
        TCP_SOCKET hTCP;

        // Find an available socket that matches the specified socket type
        for(hTCP = 0; hTCP < TCP_SOCKET_COUNT; hTCP++)
        {
                SyncTCBStub(hTCP);

                // Sockets that are in use will be in a non-closed state
                if(MyTCBStub.smState != TCP_CLOSED)
                        continue;

                SyncTCB();

                // See if this socket matches the desired type
                if(MyTCB.vSocketPurpose != vSocketPurpose)
                        continue;

                // Start out assuming worst case Maximum Segment Size (changes when MSS 
                // option is received from remote node)
                MyTCB.wRemoteMSS = 536;

                // See if this is a server socket
                if(vRemoteHostType == TCP_OPEN_SERVER)
                {
                        MyTCB.localPort.Val = wPort;
                        MyTCBStub.Flags.bServer = TRUE;
                        MyTCBStub.smState = TCP_LISTEN;
                        MyTCBStub.remoteHash.Val = wPort;
                        #if defined(STACK_USE_SSL_SERVER)
                        MyTCB.localSSLPort.Val = 0;
                        #endif
                }
                // Handle all the client mode socket types
                else
                {
                        #if defined(STACK_CLIENT_MODE)
                        {
                                // Each new socket that is opened by this node, gets the 
                                // next sequential local port number.
                                if(NextPort < LOCAL_PORT_START_NUMBER || NextPort > LOCAL_PORT_END_NUMBER)
                                        NextPort = LOCAL_PORT_START_NUMBER;
                                
                                // Set the non-zero TCB fields
                                MyTCB.localPort.Val = NextPort++;
                                MyTCB.remotePort.Val = wPort;
        
                                // Flag to start the DNS, ARP, SYN processes
                                MyTCBStub.eventTime = TickGet();
                                MyTCBStub.Flags.bTimerEnabled = 1;
        
                                switch(vRemoteHostType)
                                {
                                        #if defined(STACK_USE_DNS)
                                        case TCP_OPEN_RAM_HOST:
                                        case TCP_OPEN_ROM_HOST:
                                                MyTCB.remote.dwRemoteHost = dwRemoteHost;
                                                MyTCB.flags.bRemoteHostIsROM = (vRemoteHostType == TCP_OPEN_ROM_HOST);
                                                MyTCBStub.smState = TCP_GET_DNS_MODULE;
                                                break;
                                        #endif
                
                                        case TCP_OPEN_IP_ADDRESS:
                                                // dwRemoteHost is a literal IP address.  This 
                                                // doesn't need DNS and can skip directly to the 
                                                // Gateway ARPing step.
                                                MyTCBStub.remoteHash.Val = (((DWORD_VAL*)&dwRemoteHost)->w[1]+((DWORD_VAL*)&dwRemoteHost)->w[0] + wPort) ^ MyTCB.localPort.Val;
                                                MyTCB.remote.niRemoteMACIP.IPAddr.Val = dwRemoteHost;
                                                MyTCB.retryCount = 0;
                                                MyTCB.retryInterval = (TICK_SECOND/4)/256;
                                                MyTCBStub.smState = TCP_GATEWAY_SEND_ARP;
                                                break;
                
                                        case TCP_OPEN_NODE_INFO:
                                                MyTCBStub.remoteHash.Val = (((NODE_INFO*)(PTR_BASE)dwRemoteHost)->IPAddr.w[1]+((NODE_INFO*)(PTR_BASE)dwRemoteHost)->IPAddr.w[0] + wPort) ^ MyTCB.localPort.Val;
                                                memcpy((void*)(BYTE*)&MyTCB.remote, (void*)(BYTE*)(PTR_BASE)dwRemoteHost, sizeof(NODE_INFO));
                                                MyTCBStub.smState = TCP_SYN_SENT;
                                                SendTCP(SYN, SENDTCP_RESET_TIMERS);
                                                break;
                                }
                        }               
                        #else
                        {
                                return INVALID_SOCKET;
                        }       
                        #endif
                }
                
                return hTCP;            
        }

        // If there is no socket available, return error.
        return INVALID_SOCKET;
}


/*****************************************************************************
  Function:
        BOOL TCPWasReset(TCP_SOCKET hTCP)

  Summary:
        Self-clearing semaphore inidicating socket reset.

  Description:
        This function is a self-clearing semaphore indicating whether or not
        a socket has been disconnected since the previous call.  This function
        works for all possible disconnections: a call to TCPDisconnect, a FIN 
        from the remote node, or an acknowledgement timeout caused by the loss
        of a network link.  It also returns TRUE after the first call to TCPInit.
        Applications should use this function to reset their state machines.
        
        This function was added due to the possibility of an error when relying
        on TCPIsConnected returing FALSE to check for a condition requiring a
        state machine reset.  If a socket is closed (due to a FIN ACK) and then
        immediately reopened (due to a the arrival of a new SYN) in the same
        cycle of the stack, calls to TCPIsConnected by the application will 
        never return FALSE even though the socket has been disconnected.  This 
        can cause errors for protocols such as HTTP in which a client will 
        immediately open a new connection upon closing of a prior one.  Relying
        on this function instead allows applications to trap those conditions 
        and properly reset their internal state for the new connection.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to check.

  Return Values:
        TRUE - The socket has been disconnected since the previous call.
        FALSE - The socket has not been disconnected since the previous call.
  ***************************************************************************/
BOOL TCPWasReset(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
        
        if(MyTCBStub.Flags.bSocketReset)
        {
                MyTCBStub.Flags.bSocketReset = 0;
                return TRUE;
        }       
        
        return FALSE;
}


/*****************************************************************************
  Function:
        BOOL TCPIsConnected(TCP_SOCKET hTCP)

  Summary:
        Determines if a socket has an established connection.

  Description:
        This function determines if a socket has an established connection to 
        a remote node.  Call this function after calling TCPOpen to determine 
        when the connection is set up and ready for use.  This function was 
        historically used to check for disconnections, but TCPWasReset is now a
        more appropriate solution. 

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to check.

  Return Values:
        TRUE - The socket has an established connection to a remote node.
        FALSE - The socket is not currently connected.

  Remarks:
        A socket is said to be connected only if it is in the TCP_ESTABLISHED
        state.  Sockets in the process of opening or closing will return FALSE.
  ***************************************************************************/
BOOL TCPIsConnected(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
        return (MyTCBStub.smState == TCP_ESTABLISHED);
}


/*****************************************************************************
  Function:
        void TCPDisconnect(TCP_SOCKET hTCP)

  Summary:
        Disconnects an open socket.

  Description:
        This function closes a connection to a remote node by sending a FIN (if 
        currently connected).
        
        The function can be called a second time to force a socket closed by 
        sending a RST packet.  This is useful when the application knows that 
        the remote node will not send an ACK (if it has crashed or lost its link),
        or when the application needs to reuse the socket immediately regardless
        of whether or not the remote node would like to transmit more data before
        closing.
        
        For client mode sockets, upon return, the hTCP handle is relinquished to 
        the TCP/IP stack and must no longer be used by the application (except for 
        an immediate subsequent call to TCPDisconnect() to force a RST 
        transmission, if needed).  
        
        For server mode sockets, upon return, the hTCP handle is NOT relinquished 
        to the TCP/IP stack.  After closing, the socket returns to the listening 
        state allowing future connection requests to be serviced.  This leaves the 
        hTCP handle in a valid state and must be retained for future operations on 
        the socket.  If you want to close the server and relinquish the socket back 
        to the TCP/IP stack, call the TCPClose() API instead of TCPDisconnect().

  Precondition:
        None

  Parameters:
        hTCP - Handle of the socket to disconnect.

  Returns:
        None

  Remarks:
        If the socket is using SSL, a CLOSE_NOTIFY record will be transmitted
        first to allow the SSL session to be resumed at a later time.
  ***************************************************************************/
void TCPDisconnect(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);

        // Delete all data in the RX FIFO
        // In this stack's API, the application TCP handle is 
        // immediately invalid after calling this function, so there 
        // is no longer any way to receive data from the TCP RX FIFO, 
        // even though the data is still there.  Leaving the data there 
        // could interfere with the remote node sending us a FIN if our
        // RX window is zero
        MyTCBStub.rxTail = MyTCBStub.rxHead;

        switch(MyTCBStub.smState)
        {
                #if defined(STACK_CLIENT_MODE) && defined(STACK_USE_DNS)
                case TCP_DNS_RESOLVE:
                        DNSEndUsage();  // Release the DNS module, since the user is aborting
                        CloseSocket();
                        break;
                #endif

                case TCP_GET_DNS_MODULE:
                case TCP_GATEWAY_SEND_ARP:
                case TCP_GATEWAY_GET_ARP:
                case TCP_SYN_SENT:
                        CloseSocket();
                        break;

                case TCP_SYN_RECEIVED:
                case TCP_ESTABLISHED:
                        #if defined(STACK_USE_SSL)
                        // When disconnecting SSL sockets, send a close_notify so we can resume later
                        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                        {
                                // Flush pending data and send close_notify
                                SSLTxRecord(hTCP, MyTCBStub.sslStubID, SSL_APPLICATION);
                                SSLTxMessage(hTCP, MyTCBStub.sslStubID, SSL_ALERT_CLOSE_NOTIFY);
                        }
                        #endif

                        // Send the FIN.  This is done in a loop to ensure that if we have 
                        // more data wating in the TX FIFO than can be sent in a single 
                        // packet (due to the remote Max Segment Size packet size limit), 
                        // we will keep generating more packets until either all data gets 
                        // transmitted or the remote node's receive window fills up.
                        do
                        {
                                SendTCP(FIN | ACK, SENDTCP_RESET_TIMERS);
                                if(MyTCB.remoteWindow == 0u)
                                        break;
                        } while(MyTCBStub.txHead != MyTCB.txUnackedTail);
                        
                        MyTCBStub.smState = TCP_FIN_WAIT_1;
                        break;

                case TCP_CLOSE_WAIT:
                        // Send the FIN.  This is done in a loop to ensure that if we have 
                        // more data wating in the TX FIFO than can be sent in a single 
                        // packet (due to the remote Max Segment Size packet size limit), 
                        // we will keep generating more packets until either all data gets 
                        // transmitted or the remote node's receive window fills up.
                        do
                        {
                                SendTCP(FIN | ACK, SENDTCP_RESET_TIMERS);
                                if(MyTCB.remoteWindow == 0u)
                                        break;
                        } while(MyTCBStub.txHead != MyTCB.txUnackedTail);

                        MyTCBStub.smState = TCP_LAST_ACK;
                        break;
                        
                // These states are all already closed or don't need explicit disconnecting -- they will disconnect by themselves after a while
                //case TCP_CLOSED:
                //case TCP_LISTEN:
                //case TCP_CLOSING:
                //case TCP_TIME_WAIT:
                //      return;

                case TCP_CLOSED_BUT_RESERVED:
                        MyTCBStub.smState = TCP_CLOSED;
                        break;

                // These states will close themselves after some delay, however, 
                // this is handled so that the user can call TCPDisconnect() 
                // twice to immediately close a socket (using an RST) without 
                // having to get an ACK back from the remote node.  This is 
                // great for instance when the application determines that 
                // the remote node has been physically disconnected and 
                // already knows that no ACK will be returned.  Alternatively, 
                // if the application needs to immediately reuse the socket 
                // regardless of what the other node's state is in (half open).
                case TCP_FIN_WAIT_1:
                case TCP_FIN_WAIT_2:
                case TCP_LAST_ACK:
                default:
                        SendTCP(RST | ACK, 0);
                        CloseSocket();
                        break;
        }
}


/*****************************************************************************
  Function:
        void TCPClose(TCP_SOCKET hTCP)

  Summary:
        Disconnects an open socket and destroys the socket handle, including server 
        mode socket handles.

  Description:
        Disconnects an open socket and destroys the socket handle, including server 
        mode socket handles.  This function performs identically to the 
        TCPDisconnect() function, except that both client and server mode socket 
        handles are relinquished to the TCP/IP stack upon return.

  Precondition:
        None

  Parameters:
        hTCP - Handle to the socket to disconnect and close.

  Returns:
        None
  ***************************************************************************/
void TCPClose(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
        MyTCBStub.Flags.bServer = FALSE;
        TCPDisconnect(hTCP);
}


/*****************************************************************************
  Function:
        SOCKET_INFO* TCPGetRemoteInfo(TCP_SOCKET hTCP)

  Summary:
        Obtains information about a currently open socket.

  Description:
        Returns the SOCKET_INFO structure associated with this socket.  This 
        contains the NODE_INFO structure with IP and MAC address (or gateway
        MAC) and the remote port.

  Precondition:
        TCP is initialized and the socket is connected.

  Parameters:
        hTCP - The socket to check.

  Returns:
        The SOCKET_INFO structure associated with this socket.  This structure is 
        allocated statically by the function and is valid only until the next 
        time TCPGetRemoteInfo() is called.
  ***************************************************************************/
SOCKET_INFO* TCPGetRemoteInfo(TCP_SOCKET hTCP)
{
        static SOCKET_INFO      RemoteInfo;

        SyncTCBStub(hTCP);
        SyncTCB();
        memcpy((void*)&RemoteInfo.remote, (void*)&MyTCB.remote, sizeof(NODE_INFO));
        RemoteInfo.remotePort.Val = MyTCB.remotePort.Val;

        return &RemoteInfo;
}



/****************************************************************************
  Section:
        Transmit Functions
  ***************************************************************************/

/*****************************************************************************
  Function:
        void TCPFlush(TCP_SOCKET hTCP)

  Summary:
        Immediately transmits all pending TX data.

  Description:
        This function immediately transmits all pending TX data with a PSH 
        flag.  If this function is not called, data will automatically be sent
        when either a) the TX buffer is half full or b) the 
        TCP_AUTO_TRANSMIT_TIMEOUT_VAL (default: 40ms) has elapsed.

  Precondition:
        TCP is initialized and the socket is connected.

  Parameters:
        hTCP - The socket whose data is to be transmitted.

  Returns:
        None

  Remarks:
        SSL application data is automatically flushed, so this function has 
        no effect for SSL sockets.
  ***************************************************************************/
void TCPFlush(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
        SyncTCB();

        // NOTE: Pending SSL data will NOT be transferred here

        if(MyTCBStub.txHead != MyTCB.txUnackedTail)
        {
                // Send the TCP segment with all unacked bytes
                SendTCP(ACK, SENDTCP_RESET_TIMERS);
        }
}


/*****************************************************************************
  Function:
        WORD TCPIsPutReady(TCP_SOCKET hTCP)

  Summary:
        Determines how much free space is available in the TCP TX buffer.

  Description:
        Call this function to determine how many bytes can be written to the 
        TCP TX buffer.  If this function returns zero, the application must 
        return to the main stack loop before continuing in order to transmit
        more data.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to check.

  Returns:
        The number of bytes available to be written in the TCP TX buffer.
  ***************************************************************************/
WORD TCPIsPutReady(TCP_SOCKET hTCP)
{
        BYTE i;

        SyncTCBStub(hTCP);

        i = MyTCBStub.smState;

        // Unconnected sockets shouldn't be transmitting anything.
        if(!( (i == (BYTE)TCP_ESTABLISHED) || (i == (BYTE)TCP_CLOSE_WAIT) ))
                return 0;

        // Calculate the free space in this socket's TX FIFO
        #if defined(STACK_USE_SSL)
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
        {// Use sslTxHead as the head pointer when SSL is active
                WORD rem;
                
                // Find out raw free space
                if(MyTCBStub.sslTxHead >= MyTCBStub.txTail)
                        rem = (MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart - 1) - (MyTCBStub.sslTxHead - MyTCBStub.txTail);
                else
                        rem = MyTCBStub.txTail - MyTCBStub.sslTxHead - 1;
                        
                // Reserve space for a new MAC and header
                if(rem > 22u)
                        return rem - 22;
                else
                        return 0;
        }
        #endif
        
        if(MyTCBStub.txHead >= MyTCBStub.txTail)
                return (MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart - 1) - (MyTCBStub.txHead - MyTCBStub.txTail);
        else
                return MyTCBStub.txTail - MyTCBStub.txHead - 1;
}


/*****************************************************************************
  Function:
        BOOL TCPPut(TCP_SOCKET hTCP, BYTE byte)

  Description:
        Writes a single byte to a TCP socket.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to which data is to be written.
        byte - The byte to write.

  Return Values:
        TRUE - The byte was written to the transmit buffer.
        FALSE - The transmit buffer was full, or the socket is not connected.
  ***************************************************************************/
BOOL TCPPut(TCP_SOCKET hTCP, BYTE byte)
{
        WORD wFreeTXSpace;

        SyncTCBStub(hTCP);

        wFreeTXSpace = TCPIsPutReady(hTCP);
        if(wFreeTXSpace == 0u)
                return FALSE;
        else if(wFreeTXSpace == 1u) // About to run out of space, lets transmit so the remote node might send an ACK back faster
                TCPFlush(hTCP); 

        // Send all current bytes if we are crossing half full
        // This is required to improve performance with the delayed 
        // acknowledgement algorithm
        if((!MyTCBStub.Flags.bHalfFullFlush) && (wFreeTXSpace <= ((MyTCBStub.bufferRxStart-MyTCBStub.bufferTxStart)>>1)))
        {
                TCPFlush(hTCP); 
                MyTCBStub.Flags.bHalfFullFlush = TRUE;
        }

        #if defined(STACK_USE_SSL)
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
        {
                TCPRAMCopy(MyTCBStub.sslTxHead, MyTCBStub.vMemoryMedium, (PTR_BASE)&byte, TCP_PIC_RAM, sizeof(byte));
                if(++MyTCBStub.sslTxHead >= MyTCBStub.bufferRxStart)
                        MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
        }
        else
        {
                TCPRAMCopy(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, (PTR_BASE)&byte, TCP_PIC_RAM, sizeof(byte));
                if(++MyTCBStub.txHead >= MyTCBStub.bufferRxStart)
                        MyTCBStub.txHead = MyTCBStub.bufferTxStart;
        }
        #else
        TCPRAMCopy(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, (PTR_BASE)&byte, TCP_PIC_RAM, sizeof(byte));
        if(++MyTCBStub.txHead >= MyTCBStub.bufferRxStart)
                MyTCBStub.txHead = MyTCBStub.bufferTxStart;
        #endif
        

        // Send the last byte as a separate packet (likely will make the remote node send back ACK faster)
        if(wFreeTXSpace == 1u)
        {
                TCPFlush(hTCP);
        }
        // If not already enabled, start a timer so this data will 
        // eventually get sent even if the application doens't call
        // TCPFlush()
        else if(!MyTCBStub.Flags.bTimer2Enabled)
        {
                MyTCBStub.Flags.bTimer2Enabled = TRUE;
                MyTCBStub.eventTime2 = (WORD)TickGetDiv256() + TCP_AUTO_TRANSMIT_TIMEOUT_VAL/256ull;
        }

        return TRUE;
}

/*****************************************************************************
  Function:
        WORD TCPPutArray(TCP_SOCKET hTCP, BYTE* data, WORD len)

  Description:
        Writes an array from RAM to a TCP socket.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to which data is to be written.
        data - Pointer to the array to be written.
        len  - Number of bytes to be written.

  Returns:
        The number of bytes written to the socket.  If less than len, the
        buffer became full or the socket is not conected.
  ***************************************************************************/
WORD TCPPutArray(TCP_SOCKET hTCP, BYTE* data, WORD len)
{
        WORD wActualLen;
        WORD wFreeTXSpace;
        WORD wRightLen = 0;

        SyncTCBStub(hTCP);

        wFreeTXSpace = TCPIsPutReady(hTCP);
        if(wFreeTXSpace == 0u)
        {
                TCPFlush(hTCP);
                return 0;
        }

        wActualLen = wFreeTXSpace;
        if(wFreeTXSpace > len)
                wActualLen = len;

        // Send all current bytes if we are crossing half full
        // This is required to improve performance with the delayed 
        // acknowledgement algorithm
        if((!MyTCBStub.Flags.bHalfFullFlush) && (wFreeTXSpace <= ((MyTCBStub.bufferRxStart-MyTCBStub.bufferTxStart)>>1)))
        {
                TCPFlush(hTCP); 
                MyTCBStub.Flags.bHalfFullFlush = TRUE;
        }
        
        #if defined(STACK_USE_SSL)
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
        {
                // See if we need a two part put
                if(MyTCBStub.sslTxHead + wActualLen >= MyTCBStub.bufferRxStart)
                {
                        wRightLen = MyTCBStub.bufferRxStart-MyTCBStub.sslTxHead;
                        TCPRAMCopy(MyTCBStub.sslTxHead, MyTCBStub.vMemoryMedium, (PTR_BASE)data, TCP_PIC_RAM, wRightLen);
                        data += wRightLen;
                        wActualLen -= wRightLen;
                        MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
                }
        
                TCPRAMCopy(MyTCBStub.sslTxHead, MyTCBStub.vMemoryMedium, (PTR_BASE)data, TCP_PIC_RAM, wActualLen);
                MyTCBStub.sslTxHead += wActualLen;
        }
        else
        {
                // See if we need a two part put
                if(MyTCBStub.txHead + wActualLen >= MyTCBStub.bufferRxStart)
                {
                        wRightLen = MyTCBStub.bufferRxStart-MyTCBStub.txHead;
                        TCPRAMCopy(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, (PTR_BASE)data, TCP_PIC_RAM, wRightLen);
                        data += wRightLen;
                        wActualLen -= wRightLen;
                        MyTCBStub.txHead = MyTCBStub.bufferTxStart;
                }
        
                TCPRAMCopy(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, (PTR_BASE)data, TCP_PIC_RAM, wActualLen);
                MyTCBStub.txHead += wActualLen;
        }
        #else
        // See if we need a two part put
        if(MyTCBStub.txHead + wActualLen >= MyTCBStub.bufferRxStart)
        {
                wRightLen = MyTCBStub.bufferRxStart-MyTCBStub.txHead;
                TCPRAMCopy(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, (PTR_BASE)data, TCP_PIC_RAM, wRightLen);
                data += wRightLen;
                wActualLen -= wRightLen;
                MyTCBStub.txHead = MyTCBStub.bufferTxStart;
        }

        TCPRAMCopy(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, (PTR_BASE)data, TCP_PIC_RAM, wActualLen);
        MyTCBStub.txHead += wActualLen;
        #endif

        // Send these bytes right now if we are out of TX buffer space
        if(wFreeTXSpace <= len)
        {
                TCPFlush(hTCP);
        }
        // If not already enabled, start a timer so this data will 
        // eventually get sent even if the application doens't call
        // TCPFlush()
        else if(!MyTCBStub.Flags.bTimer2Enabled)
        {
                MyTCBStub.Flags.bTimer2Enabled = TRUE;
                MyTCBStub.eventTime2 = (WORD)TickGetDiv256() + TCP_AUTO_TRANSMIT_TIMEOUT_VAL/256ull;
        }

        return wActualLen + wRightLen;
}

/*****************************************************************************
  Function:
        WORD TCPPutROMArray(TCP_SOCKET hTCP, ROM BYTE* data, WORD len)

  Description:
        Writes an array from ROM to a TCP socket.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to which data is to be written.
        data - Pointer to the array to be written.
        len  - Number of bytes to be written.

  Returns:
        The number of bytes written to the socket.  If less than len, the
        buffer became full or the socket is not conected.

  Remarks:
        This function is aliased to TCPPutArray on non-PIC18 platforms.
  ***************************************************************************/
#if defined(__18CXX)
WORD TCPPutROMArray(TCP_SOCKET hTCP, ROM BYTE* data, WORD len)
{
        WORD wActualLen;
        WORD wFreeTXSpace;
        WORD wRightLen = 0;

        SyncTCBStub(hTCP);

        wFreeTXSpace = TCPIsPutReady(hTCP);
        if(wFreeTXSpace == 0u)
        {
                TCPFlush(hTCP);
                return 0;
        }

        // Send all current bytes if we are crossing half full
        // This is required to improve performance with the delayed 
        // acknowledgement algorithm
        if((!MyTCBStub.Flags.bHalfFullFlush) && (wFreeTXSpace <= ((MyTCBStub.bufferRxStart-MyTCBStub.bufferTxStart)>>1)))
        {
                TCPFlush(hTCP); 
                MyTCBStub.Flags.bHalfFullFlush = TRUE;
        }
        
        wActualLen = wFreeTXSpace;
        if(wFreeTXSpace > len)
                wActualLen = len;
        
        #if defined(STACK_USE_SSL)
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
        {
                // See if we need a two part put
                if(MyTCBStub.sslTxHead + wActualLen >= MyTCBStub.bufferRxStart)
                {
                        wRightLen = MyTCBStub.bufferRxStart-MyTCBStub.sslTxHead;
                        TCPRAMCopyROM(MyTCBStub.sslTxHead, MyTCBStub.vMemoryMedium, data, wRightLen);
                        data += wRightLen;
                        wActualLen -= wRightLen;
                        MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
                }
        
                TCPRAMCopyROM(MyTCBStub.sslTxHead, MyTCBStub.vMemoryMedium, data, wActualLen);
                MyTCBStub.sslTxHead += wActualLen;
        }
        else
        {
                // See if we need a two part put
                if(MyTCBStub.txHead + wActualLen >= MyTCBStub.bufferRxStart)
                {
                        wRightLen = MyTCBStub.bufferRxStart-MyTCBStub.txHead;
                        TCPRAMCopyROM(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, data, wRightLen);
                        data += wRightLen;
                        wActualLen -= wRightLen;
                        MyTCBStub.txHead = MyTCBStub.bufferTxStart;
                }
        
                TCPRAMCopyROM(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, data, wActualLen);
                MyTCBStub.txHead += wActualLen;
        }
        #else
        // See if we need a two part put
        if(MyTCBStub.txHead + wActualLen >= MyTCBStub.bufferRxStart)
        {
                wRightLen = MyTCBStub.bufferRxStart-MyTCBStub.txHead;
                TCPRAMCopyROM(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, data, wRightLen);
                data += wRightLen;
                wActualLen -= wRightLen;
                MyTCBStub.txHead = MyTCBStub.bufferTxStart;
        }

        TCPRAMCopyROM(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, data, wActualLen);
        MyTCBStub.txHead += wActualLen;
        #endif

        // Send these bytes right now if we are out of TX buffer space
        if(wFreeTXSpace <= len)
        {
                TCPFlush(hTCP);
        }
        // If not already enabled, start a timer so this data will 
        // eventually get sent even if the application doens't call
        // TCPFlush()
        else if(!MyTCBStub.Flags.bTimer2Enabled)
        {
                MyTCBStub.Flags.bTimer2Enabled = TRUE;
                MyTCBStub.eventTime2 = (WORD)TickGetDiv256() + TCP_AUTO_TRANSMIT_TIMEOUT_VAL/256ull;
        }

        return wActualLen + wRightLen;
}
#endif

/*****************************************************************************
  Function:
        BYTE* TCPPutString(TCP_SOCKET hTCP, BYTE* data)

  Description:
        Writes a null-terminated string from RAM to a TCP socket.  The 
        null-terminator is not copied to the socket.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to which data is to be written.
        data - Pointer to the string to be written.

  Returns:
        Pointer to the byte following the last byte written to the socket.  If
        this pointer does not dereference to a NUL byte, the buffer became full
        or the socket is not connected.

  Remarks:
        The return value of this function differs from that of TCPPutArray.  To
        write long strings in a single state, initialize the *data pointer to the
        first byte, then call this function repeatedly (breaking to the main 
        stack loop after each call) until the return value dereferences to a NUL
        byte.  Save the return value as the new starting *data pointer otherwise.
  ***************************************************************************/
BYTE* TCPPutString(TCP_SOCKET hTCP, BYTE* data)
{
        return data + TCPPutArray(hTCP, data, strlen((char*)data));
}

/*****************************************************************************
  Function:
        BYTE* TCPPutROMString(TCP_SOCKET hTCP, ROM BYTE* data)

  Description:
        Writes a null-terminated string from ROM to a TCP socket.  The 
        null-terminator is not copied to the socket.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to which data is to be written.
        data - Pointer to the string to be written.

  Returns:
        Pointer to the byte following the last byte written to the socket.  If
        this pointer does not dereference to a NUL byte, the buffer became full
        or the socket is not connected.

  Remarks:
        The return value of this function differs from that of TCPPutArray.  To
        write long strings in a single state, initialize the *data pointer to the
        first byte, then call this function repeatedly (breaking to the main 
        stack loop after each call) until the return value dereferences to a NUL
        byte.  Save the return value as the new starting *data pointer otherwise.
        
        This function is aliased to TCPPutString on non-PIC18 platforms.
  ***************************************************************************/
#if defined(__18CXX)
ROM BYTE* TCPPutROMString(TCP_SOCKET hTCP, ROM BYTE* data)
{
        return data + TCPPutROMArray(hTCP, data, strlenpgm((ROM char*)data));
}
#endif

/*****************************************************************************
  Function:
        WORD TCPGetTxFIFOFull(TCP_SOCKET hTCP)

  Description:
        Determines how many bytes are pending in the TCP TX FIFO.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to check.

  Returns:
        Number of bytes pending to be flushed in the TCP TX FIFO.
  ***************************************************************************/
WORD TCPGetTxFIFOFull(TCP_SOCKET hTCP)
{
        WORD wDataLen;
        WORD wFIFOSize;

        SyncTCBStub(hTCP);

        // Calculate total usable FIFO size
        wFIFOSize = MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart - 1;

        // Find out how many data bytes are free in the TX FIFO
        wDataLen = TCPIsPutReady(hTCP);

        return wFIFOSize - wDataLen;
}



/****************************************************************************
  Section:
        Receive Functions
  ***************************************************************************/

/*****************************************************************************
  Function:
        void TCPDiscard(TCP_SOCKET hTCP)

  Description:
        Discards any pending data in the TCP RX FIFO.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket whose RX FIFO is to be cleared.

  Returns:
        None
  ***************************************************************************/
void TCPDiscard(TCP_SOCKET hTCP)
{
        if(TCPIsGetReady(hTCP))
        {
                SyncTCBStub(hTCP);
        
                // Delete all data in the RX buffer
                MyTCBStub.rxTail = MyTCBStub.rxHead;
        
                // Send a Window update message to the remote node
                SendTCP(ACK, SENDTCP_RESET_TIMERS);
        }
}


/*****************************************************************************
  Function:
        void WORD TCPIsGetReady(TCP_SOCKET hTCP)

  Summary:
        Determines how many bytes can be read from the TCP RX buffer.

  Description:
        Call this function to determine how many bytes can be read from the 
        TCP RX buffer.  If this function returns zero, the application must 
        return to the main stack loop before continuing in order to wait for
        more data to arrive.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to check.

  Returns:
        The number of bytes available to be read from the TCP RX buffer.
  ***************************************************************************/
WORD TCPIsGetReady(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
                
        if(MyTCBStub.rxHead >= MyTCBStub.rxTail)
                return MyTCBStub.rxHead - MyTCBStub.rxTail;
        else
                return (MyTCBStub.bufferEnd - MyTCBStub.rxTail + 1) + (MyTCBStub.rxHead - MyTCBStub.bufferRxStart);
}


/*****************************************************************************
  Function:
        BOOL TCPGet(TCP_SOCKET hTCP, BYTE* byte)

  Description:
        Retrieves a single byte to a TCP socket.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket from which to read.
        byte - Pointer to location in which the read byte should be stored.

  Return Values:
        TRUE - A byte was read from the buffer.
        FALSE - The buffer was empty, or the socket is not connected.
  ***************************************************************************/
BOOL TCPGet(TCP_SOCKET hTCP, BYTE* byte)
{
        WORD wGetReadyCount;

        // See if there is any data which can be read
        wGetReadyCount = TCPIsGetReady(hTCP);
        if(wGetReadyCount == 0u)
                return FALSE;

        SyncTCBStub(hTCP);
        
        if(byte)
                TCPRAMCopy((PTR_BASE)byte, TCP_PIC_RAM, MyTCBStub.rxTail, MyTCBStub.vMemoryMedium, 1);
        if(++MyTCBStub.rxTail > MyTCBStub.bufferEnd)
                MyTCBStub.rxTail = MyTCBStub.bufferRxStart;

        // Send a window update if we've run out of data
        if(wGetReadyCount == 1u)
        {
                MyTCBStub.Flags.bTXASAPWithoutTimerReset = 1;
        }
        // If not already enabled, start a timer so a window 
        // update will get sent to the remote node at some point
        else if(!MyTCBStub.Flags.bTimer2Enabled)
        {
                MyTCBStub.Flags.bTimer2Enabled = TRUE;
                MyTCBStub.eventTime2 = (WORD)TickGetDiv256() + TCP_WINDOW_UPDATE_TIMEOUT_VAL/256ull;
        }


        return TRUE;
}


/*****************************************************************************
  Function:
        WORD TCPGetArray(TCP_SOCKET hTCP, BYTE* buffer, WORD len)

  Description:
        Reads an array of data bytes from a TCP socket's receive FIFO.  The data 
        is removed from the FIFO in the process.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket from which data is to be read.
        buffer - Pointer to the array to store data that was read.
        len  - Number of bytes to be read.

  Returns:
        The number of bytes read from the socket.  If less than len, the
        RX FIFO buffer became empty or the socket is not conected.
  ***************************************************************************/
WORD TCPGetArray(TCP_SOCKET hTCP, BYTE* buffer, WORD len)
{
        WORD wGetReadyCount;
        WORD RightLen = 0;

        // See if there is any data which can be read
        wGetReadyCount = TCPIsGetReady(hTCP);
        if(wGetReadyCount == 0u)
                return 0x0000u;

        SyncTCBStub(hTCP);

        // Make sure we don't try to read more data than is available
        if(len > wGetReadyCount)
                len = wGetReadyCount;

        // See if we need a two part get
        if(MyTCBStub.rxTail + len > MyTCBStub.bufferEnd)
        {
                RightLen = MyTCBStub.bufferEnd - MyTCBStub.rxTail + 1;
                if(buffer)
                {
                        TCPRAMCopy((PTR_BASE)buffer, TCP_PIC_RAM, MyTCBStub.rxTail, MyTCBStub.vMemoryMedium, RightLen);
                        buffer += RightLen;
                }
                len -= RightLen;
                MyTCBStub.rxTail = MyTCBStub.bufferRxStart;
        }

        if(buffer)
                TCPRAMCopy((PTR_BASE)buffer, TCP_PIC_RAM, MyTCBStub.rxTail, MyTCBStub.vMemoryMedium, len);
        MyTCBStub.rxTail += len;
        len += RightLen;

        // Send a window update if we've run low on data
        if(wGetReadyCount - len <= len)
        {
                MyTCBStub.Flags.bTXASAPWithoutTimerReset = 1;
        }
        else if(!MyTCBStub.Flags.bTimer2Enabled)
        // If not already enabled, start a timer so a window 
        // update will get sent to the remote node at some point
        {
                MyTCBStub.Flags.bTimer2Enabled = TRUE;
                MyTCBStub.eventTime2 = (WORD)TickGetDiv256() + TCP_WINDOW_UPDATE_TIMEOUT_VAL/256ull;
        }

        return len;
}


/*****************************************************************************
  Function:
        WORD TCPGetRxFIFOFree(TCP_SOCKET hTCP)

  Description:
        Determines how many bytes are free in the RX FIFO.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to check.

  Returns:
        The number of bytes free in the TCP RX FIFO.  If zero, no additional 
        data can be received until the application removes some data using one
        of the TCPGet family functions.
  ***************************************************************************/
WORD TCPGetRxFIFOFree(TCP_SOCKET hTCP)
{
        WORD wDataLen;
        WORD wFIFOSize;
        
        SyncTCBStub(hTCP);
        
        // Calculate total usable FIFO size
        wFIFOSize = MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart;

        #if defined(STACK_USE_SSL)
        {
                PTR_BASE SSLtemp = MyTCBStub.rxHead;

                // Move SSL pointer to determine full buffer size
                if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                        MyTCBStub.rxHead = MyTCBStub.sslRxHead;

                // Find out how many data bytes are actually in the RX FIFO
                wDataLen = TCPIsGetReady(hTCP);
                
                // Move SSL pointer back to proper location (if we changed it)
                MyTCBStub.rxHead = SSLtemp;
        }
        #else
        {
                // Find out how many data bytes are actually in the RX FIFO
                wDataLen = TCPIsGetReady(hTCP);
        }
        #endif
        
        // Perform the calculation      
        return wFIFOSize - wDataLen;
}

/*****************************************************************************
  Function:
        WORD TCPPeekArray(TCP_SOCKET hTCP, BYTE *vBuffer, WORD wLen, WORD wStart)

  Summary:
        Reads a specified number of data bytes from the TCP RX FIFO without 
        removing them from the buffer.

  Description:
        Reads a specified number of data bytes from the TCP RX FIFO without 
        removing them from the buffer.  No TCP control actions are taken as a 
        result of this function (ex: no window update is sent to the remote node).
        
  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to peak from (read without removing from stream).
        vBuffer - Destination to write the peeked data bytes.
        wLen - Length of bytes to peak from the RX FIFO and copy to vBuffer.
        wStart - Zero-indexed starting position within the FIFO to start peeking 
                from.

  Return Values:
        Number of bytes actually peeked from the stream and copied to vBuffer.  
        This value can be less than wLen if wStart + wLen is greater than the 
        deepest possible character in the RX FIFO.

  Remarks:
        None
  ***************************************************************************/
WORD TCPPeekArray(TCP_SOCKET hTCP, BYTE *vBuffer, WORD wLen, WORD wStart)
{
        PTR_BASE ptrRead;
        WORD w;
        WORD wBytesUntilWrap;

        if(wLen == 0u)
                return 0u;

        SyncTCBStub(hTCP);

        // Find out how many bytes are in the RX FIFO and decrease read length 
        // if the start offset + read length is beyond the end of the FIFO
        w = TCPIsGetReady(hTCP);
        if(wStart + wLen > w)
                wLen = w - wStart;

        // Find the read start location
        ptrRead = MyTCBStub.rxTail + wStart;
        if(ptrRead > MyTCBStub.bufferEnd)
                ptrRead -= MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;

        // Calculate how many bytes can be read in a single go
        wBytesUntilWrap = MyTCBStub.bufferEnd - ptrRead + 1;
        if(wLen <= wBytesUntilWrap)
        {
                // Read all at once
                TCPRAMCopy((PTR_BASE)vBuffer, TCP_PIC_RAM, ptrRead, MyTCBStub.vMemoryMedium, wLen);
        }
        else
        {
                // Read all bytes up to the wrap position and then read remaining bytes 
                // at the start of the buffer
                TCPRAMCopy((PTR_BASE)vBuffer, TCP_PIC_RAM, ptrRead, MyTCBStub.vMemoryMedium, wBytesUntilWrap);
                TCPRAMCopy((PTR_BASE)vBuffer+wBytesUntilWrap, TCP_PIC_RAM, MyTCBStub.bufferRxStart, MyTCBStub.vMemoryMedium, wLen - wBytesUntilWrap);
        }
        
        return wLen;
}

/*****************************************************************************
  Function:
        BYTE TCPPeek(TCP_SOCKET hTCP, WORD wStart)

  Summary:
        Peaks at one byte in the TCP RX FIFO without removing it from the buffer.

  Description:
        Peaks at one byte in the TCP RX FIFO without removing it from the buffer.
        
  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to peak from (read without removing from stream).
        wStart - Zero-indexed starting position within the FIFO to peek from.

  Return Values:
        Byte peeked from the RX FIFO.  If there is no data in the buffer or an 
        illegal wStart starting offset is given, then an indeterminate value is 
        returned.  The caller must ensure that valid parameters are passed to avoid 
        (i.e ensure that TCPIsGetReady() returns a number that is less than wStart 
        before calling TCPPeek()).

  Remarks:
        Use the TCPPeekArray() function to read more than one byte.  It will 
        perform better than calling TCPPeek() in a loop.
  ***************************************************************************/
BYTE TCPPeek(TCP_SOCKET hTCP, WORD wStart)
{
        BYTE i;
        
        TCPPeekArray(hTCP, &i, 1, wStart);
        return i;
}


/****************************************************************************
  Section:
        Search Functions
  ***************************************************************************/

/*****************************************************************************
  Function:
        WORD TCPFindArrayEx(TCP_SOCKET hTCP, BYTE* cFindArray, WORD wLen, 
                                                WORD wStart, WORD wSearchLen, BOOL bTextCompare)

  Summary:
        Searches for a string in the TCP RX buffer.

  Description:
        This function finds the first occurrance of an array of bytes in the
        TCP RX buffer.  It can be used by an application to abstract searches 
        out of their own application code.  For increased efficiency, the 
        function is capable of limiting the scope of search to a specific
        range of bytes.  It can also perform a case-insensitive search if
        required.
        
        For example, if the buffer contains "I love PIC MCUs!" and the search
        array is "love" with a length of 4, a value of 2 will be returned.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to search within.
        cFindArray - The array of bytes to find in the buffer.
        wLen - Length of cFindArray.
        wStart - Zero-indexed starting position within the buffer.
        wSearchLen - Length from wStart to search in the buffer.
        bTextCompare - TRUE for case-insensitive text search, FALSE for binary search

  Return Values:
        0xFFFF - Search array not found
        Otherwise - Zero-indexed position of the first occurrance

  Remarks:
        Since this function usually must transfer data from external storage
        to internal RAM for comparison, its performance degrades significantly
        when the buffer is full and the array is not found.  For better 
        performance, try to search for characters that are expected to exist or
        limit the scope of the search as much as possible.  The HTTP2 module, 
        for example, uses this function to parse headers.  However, it searches 
        for newlines, then the separating colon, then reads the header name to 
        RAM for final comparison.  This has proven to be significantly faster  
        than searching for full header name strings outright.
  ***************************************************************************/
WORD TCPFindArrayEx(TCP_SOCKET hTCP, BYTE* cFindArray, WORD wLen, WORD wStart, WORD wSearchLen, BOOL bTextCompare)
{
        PTR_BASE ptrRead;
        WORD wDataLen;
        WORD wBytesUntilWrap;
        PTR_BASE ptrLocation;
        WORD wLenStart;
        BYTE *cFindArrayStart;
        BYTE i, j, k;
        BOOL isFinding;
        BYTE buffer[32];

        if(wLen == 0u)
                return 0u;

        SyncTCBStub(hTCP);

        // Find out how many bytes are in the RX FIFO and return 
        // immediately if we won't possibly find a match
        wDataLen = TCPIsGetReady(hTCP) - wStart;
        if(wDataLen < wLen)
                return 0xFFFFu;
        if(wSearchLen && (wDataLen > wSearchLen))
                wDataLen = wSearchLen;

        ptrLocation = MyTCBStub.rxTail + wStart;
        if(ptrLocation > MyTCBStub.bufferEnd)
                ptrLocation -= MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;
        ptrRead = ptrLocation;
        wBytesUntilWrap = MyTCBStub.bufferEnd - ptrLocation + 1;
        ptrLocation = wStart;
        wLenStart = wLen;
        cFindArrayStart = cFindArray;
        j = *cFindArray++;
        isFinding = FALSE;
        if(bTextCompare)
        {
                if(j >= 'a' && j <= 'z')
                        j += 'A'-'a';
        }

        // Search for the array
        while(1)
        {
                // Figure out how big of a chunk to read
                k = sizeof(buffer);
                if(k > wBytesUntilWrap)
                        k = wBytesUntilWrap;
                if((WORD)k > wDataLen)
                        k = wDataLen;

                // Read a chunk of data into the buffer
                TCPRAMCopy((PTR_BASE)buffer, TCP_PIC_RAM, ptrRead, MyTCBStub.vMemoryMedium, (WORD)k);
                ptrRead += k;
                wBytesUntilWrap -= k;

                if(wBytesUntilWrap == 0u)
                {
                        ptrRead = MyTCBStub.bufferRxStart;
                        wBytesUntilWrap = 0xFFFFu;
                }

                // Convert everything to uppercase
                if(bTextCompare)
                {
                        for(i = 0; i < k; i++)
                        {
                                if(buffer[i] >= 'a' && buffer[i] <= 'z')
                                        buffer[i] += 'A'-'a';

                                if(j == buffer[i])
                                {
                                        if(--wLen == 0u)
                                                return ptrLocation-wLenStart + i + 1;
                                        j = *cFindArray++;
                                        isFinding = TRUE;
                                        if(j >= 'a' && j <= 'z')
                                                j += 'A'-'a';
                                }
                                else
                                {
                                        wLen = wLenStart;
                                        if(isFinding)
                                        {
                                                cFindArray = cFindArrayStart;
                                                j = *cFindArray++;
                                                if(j >= 'a' && j <= 'z')
                                                        j += 'A'-'a';
                                                isFinding = FALSE;
                                        }
                                }
                        }
                }
                else    // Compare as is
                {
                        for(i = 0; i < k; i++)
                        {
                                if(j == buffer[i])
                                {
                                        if(--wLen == 0u)
                                                return ptrLocation-wLenStart + i + 1;
                                        j = *cFindArray++;
                                        isFinding = TRUE;
                                }
                                else
                                {
                                        wLen = wLenStart;
                                        if(isFinding)
                                        {
                                                cFindArray = cFindArrayStart;
                                                j = *cFindArray++;
                                                isFinding = FALSE;
                                        }
                                }
                        }
                }

                // Check to see if it is impossible to find a match
                wDataLen -= k;
                if(wDataLen < wLen)
                        return 0xFFFFu;

                ptrLocation += k;
        }
}

/*****************************************************************************
  Function:
        WORD TCPFindROMArrayEx(TCP_SOCKET hTCP, BYTE* cFindArray, WORD wLen, 
                                                WORD wStart, WORD wSearchLen, BOOL bTextCompare)

  Summary:
        Searches for a ROM string in the TCP RX buffer.

  Description:
        This function finds the first occurrance of an array of bytes in the
        TCP RX buffer.  It can be used by an application to abstract searches 
        out of their own application code.  For increased efficiency, the 
        function is capable of limiting the scope of search to a specific
        range of bytes.  It can also perform a case-insensitive search if
        required.
        
        For example, if the buffer contains "I love PIC MCUs!" and the search
        array is "love" with a length of 4, a value of 2 will be returned.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to search within.
        cFindArray - The array of bytes to find in the buffer.
        wLen - Length of cFindArray.
        wStart - Zero-indexed starting position within the buffer.
        wSearchLen - Length from wStart to search in the buffer.
        bTextCompare - TRUE for case-insensitive text search, FALSE for binary search

  Return Values:
        0xFFFF - Search array not found
        Otherwise - Zero-indexed position of the first occurrance

  Remarks:
        Since this function usually must transfer data from external storage
        to internal RAM for comparison, its performance degrades significantly
        when the buffer is full and the array is not found.  For better 
        performance, try to search for characters that are expected to exist or
        limit the scope of the search as much as possible.  The HTTP2 module, 
        for example, uses this function to parse headers.  However, it searches 
        for newlines, then the separating colon, then reads the header name to 
        RAM for final comparison.  This has proven to be significantly faster  
        than searching for full header name strings outright.
        
        This function is aliased to TCPFindArrayEx on non-PIC18 platforms.
  ***************************************************************************/
#if defined(__18CXX)
WORD TCPFindROMArrayEx(TCP_SOCKET hTCP, ROM BYTE* cFindArray, WORD wLen, WORD wStart, WORD wSearchLen, BOOL bTextCompare)
{
        PTR_BASE ptrRead;
        WORD wDataLen;
        WORD wBytesUntilWrap;
        PTR_BASE ptrLocation;
        WORD wLenStart;
        ROM BYTE *cFindArrayStart;
        BYTE i, j, k;
        BOOL isFinding;
        BYTE buffer[32];

        if(wLen == 0u)
                return 0u;

        SyncTCBStub(hTCP);

        // Find out how many bytes are in the RX FIFO and return 
        // immediately if we won't possibly find a match
        wDataLen = TCPIsGetReady(hTCP) - wStart;
        if(wDataLen < wLen)
                return 0xFFFFu;
        if(wSearchLen && (wDataLen > wSearchLen))
                wDataLen = wSearchLen;

        ptrLocation = MyTCBStub.rxTail + wStart;
        if(ptrLocation > MyTCBStub.bufferEnd)
                ptrLocation -= MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;
        ptrRead = ptrLocation;
        wBytesUntilWrap = MyTCBStub.bufferEnd - ptrLocation + 1;
        ptrLocation = wStart;
        wLenStart = wLen;
        cFindArrayStart = cFindArray;
        j = *cFindArray++;
        isFinding = FALSE;
        if(bTextCompare)
        {
                if(j >= 'a' && j <= 'z')
                        j += 'A'-'a';
        }

        // Search for the array
        while(1)
        {
                // Figure out how big of a chunk to read
                k = sizeof(buffer);
                if(k > wBytesUntilWrap)
                        k = wBytesUntilWrap;
                if((WORD)k > wDataLen)
                        k = wDataLen;

                // Read a chunk of data into the buffer
                TCPRAMCopy((PTR_BASE)buffer, TCP_PIC_RAM, ptrRead, MyTCBStub.vMemoryMedium, (WORD)k);
                ptrRead += k;
                wBytesUntilWrap -= k;

                if(wBytesUntilWrap == 0u)
                {
                        ptrRead = MyTCBStub.bufferRxStart;
                        wBytesUntilWrap = 0xFFFFu;
                }

                // Convert everything to uppercase
                if(bTextCompare)
                {
                        for(i = 0; i < k; i++)
                        {
                                if(buffer[i] >= 'a' && buffer[i] <= 'z')
                                        buffer[i] += 'A'-'a';

                                if(j == buffer[i])
                                {
                                        if(--wLen == 0u)
                                                return ptrLocation-wLenStart + i + 1;
                                        j = *cFindArray++;
                                        isFinding = TRUE;
                                        if(j >= 'a' && j <= 'z')
                                                j += 'A'-'a';
                                }
                                else
                                {
                                        wLen = wLenStart;
                                        if(isFinding)
                                        {
                                                cFindArray = cFindArrayStart;
                                                j = *cFindArray++;
                                                if(j >= 'a' && j <= 'z')
                                                        j += 'A'-'a';
                                                isFinding = FALSE;
                                        }
                                }
                        }
                }
                else    // Compare as is
                {
                        for(i = 0; i < k; i++)
                        {
                                if(j == buffer[i])
                                {
                                        if(--wLen == 0u)
                                                return ptrLocation-wLenStart + i + 1;
                                        j = *cFindArray++;
                                        isFinding = TRUE;
                                }
                                else
                                {
                                        wLen = wLenStart;
                                        if(isFinding)
                                        {
                                                cFindArray = cFindArrayStart;
                                                j = *cFindArray++;
                                                isFinding = FALSE;
                                        }
                                }
                        }
                }

                // Check to see if it is impossible to find a match
                wDataLen -= k;
                if(wDataLen < wLen)
                        return 0xFFFFu;

                ptrLocation += k;
        }
}
#endif


/*****************************************************************************
  Function:
        WORD TCPFindEx(TCP_SOCKET hTCP, BYTE cFind,
                                                WORD wStart, WORD wSearchLen, BOOL bTextCompare)

  Summary:
        Searches for a byte in the TCP RX buffer.

  Description:
        This function finds the first occurrance of a byte in the TCP RX
        buffer.  It can be used by an application to abstract searches 
        out of their own application code.  For increased efficiency, the 
        function is capable of limiting the scope of search to a specific
        range of bytes.  It can also perform a case-insensitive search if
        required.
        
        For example, if the buffer contains "I love PIC MCUs!" and the cFind
        byte is ' ', a value of 1 will be returned.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP - The socket to search within.
        cFind - The byte to find in the buffer.
        wStart - Zero-indexed starting position within the buffer.
        wSearchLen - Length from wStart to search in the buffer.
        bTextCompare - TRUE for case-insensitive text search, FALSE for binary search

  Return Values:
        0xFFFF - Search array not found
        Otherwise - Zero-indexed position of the first occurrance

  Remarks:
        Since this function usually must transfer data from external storage
        to internal RAM for comparison, its performance degrades significantly
        when the buffer is full and the array is not found.  For better 
        performance, try to search for characters that are expected to exist or
        limit the scope of the search as much as possible.  The HTTP2 module, 
        for example, uses this function to parse headers.  However, it searches 
        for newlines, then the separating colon, then reads the header name to 
        RAM for final comparison.  This has proven to be significantly faster  
        than searching for full header name strings outright.
  ***************************************************************************/
WORD TCPFindEx(TCP_SOCKET hTCP, BYTE cFind, WORD wStart, WORD wSearchLen, BOOL bTextCompare)
{
        return TCPFindArrayEx(hTCP, &cFind, sizeof(cFind), wStart, wSearchLen, bTextCompare);
}



/****************************************************************************
  Section:
        Data Processing Functions
  ***************************************************************************/

/*****************************************************************************
  Function:
        void TCPTick(void)

  Summary:
        Performs periodic TCP tasks.

  Description:
        This function performs any required periodic TCP tasks.  Each 
        socket's state machine is checked, and any elapsed timeout periods
        are handled.

  Precondition:
        TCP is initialized.

  Parameters:
        None

  Returns:
        None
  ***************************************************************************/
void TCPTick(void)
{
        TCP_SOCKET hTCP;
        BOOL bRetransmit;
        BOOL bCloseSocket;
        BYTE vFlags;
        WORD w;

        // Periodically all "not closed" sockets must perform timed operations
        for(hTCP = 0; hTCP < TCP_SOCKET_COUNT; hTCP++)
        {
                SyncTCBStub(hTCP);
                
                // Handle any SSL Processing and Message Transmission
                #if defined(STACK_USE_SSL)
                if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                {
                        // Handle any periodic tasks, such as RSA operations
                        SSLPeriodic(hTCP, MyTCBStub.sslStubID);
                        
                        // If unsent data is waiting, transmit it as an application record
                        if(MyTCBStub.sslTxHead != MyTCBStub.txHead && TCPSSLGetPendingTxSize(hTCP) != 0u)
                                SSLTxRecord(hTCP, MyTCBStub.sslStubID, SSL_APPLICATION);
                        
                        // If an SSL message is requested, send it now
                        if(MyTCBStub.sslReqMessage != SSL_NO_MESSAGE)
                                SSLTxMessage(hTCP, MyTCBStub.sslStubID, MyTCBStub.sslReqMessage);
                }
                #endif
                
                vFlags = 0x00;
                bRetransmit = FALSE;
                bCloseSocket = FALSE;

                // Transmit ASAP data if the medium is available
                if(MyTCBStub.Flags.bTXASAP || MyTCBStub.Flags.bTXASAPWithoutTimerReset)
                {
                        if(MACIsTxReady())
                        {
                                vFlags = ACK;
                                bRetransmit = MyTCBStub.Flags.bTXASAPWithoutTimerReset;
                        }
                }

                // Perform any needed window updates and data transmissions
                if(MyTCBStub.Flags.bTimer2Enabled)
                {
                        // See if the timeout has occured, and we need to send a new window update and pending data
                        if((SHORT)(MyTCBStub.eventTime2 - (WORD)TickGetDiv256()) <= (SHORT)0)
                                vFlags = ACK;
                }

                // Process Delayed ACKnowledgement timer
                if(MyTCBStub.Flags.bDelayedACKTimerEnabled)
                {
                        // See if the timeout has occured and delayed ACK needs to be sent
                        if((SHORT)(MyTCBStub.OverlappedTimers.delayedACKTime - (WORD)TickGetDiv256()) <= (SHORT)0)
                                vFlags = ACK;
                }
                
                // Process TCP_CLOSE_WAIT timer
                if(MyTCBStub.smState == TCP_CLOSE_WAIT)
                {
                        // Automatically close the socket on our end if the application 
                        // fails to call TCPDisconnect() is a reasonable amount of time.
                        if((SHORT)(MyTCBStub.OverlappedTimers.closeWaitTime - (WORD)TickGetDiv256()) <= (SHORT)0)
                        {
                                vFlags = FIN | ACK;
                                MyTCBStub.smState = TCP_LAST_ACK;
                        }
                }

                // Process listening server sockets that might have a SYN waiting in the SYNQueue[]
                #if TCP_SYN_QUEUE_MAX_ENTRIES
                        if(MyTCBStub.smState == TCP_LISTEN)
                        {
                                for(w = 0; w < TCP_SYN_QUEUE_MAX_ENTRIES; w++)
                                {
                                        // Abort search if there are no more valid records
                                        if(SYNQueue[w].wDestPort == 0u)
                                                break;
                                        
                                        // Stop searching if this SYN queue entry can be used by this socket
                                        #if defined(STACK_USE_SSL_SERVER)
                                        if(SYNQueue[w].wDestPort == MyTCBStub.remoteHash.Val || SYNQueue[w].wDestPort == MyTCBStub.sslTxHead)
                                        #else
                                        if(SYNQueue[w].wDestPort == MyTCBStub.remoteHash.Val)
                                        #endif
                                        {
                                                // Set up our socket and generate a reponse SYN+ACK packet
                                                SyncTCB();
                                                
                                                #if defined(STACK_USE_SSL_SERVER)
                                                // If this matches the SSL port, make sure that can be configured
                                                // before continuing.  If not, break and leave this in the queue
                                                if(SYNQueue[w].wDestPort == MyTCBStub.sslTxHead && !TCPStartSSLServer(hTCP))
                                                        break;
                                                #endif
                                                
                                                memcpy((void*)&MyTCB.remote.niRemoteMACIP, (void*)&SYNQueue[w].niSourceAddress, sizeof(NODE_INFO));
                                                MyTCB.remotePort.Val = SYNQueue[w].wSourcePort;
                                                MyTCB.RemoteSEQ = SYNQueue[w].dwSourceSEQ + 1;
                                                MyTCBStub.remoteHash.Val = (MyTCB.remote.niRemoteMACIP.IPAddr.w[1] + MyTCB.remote.niRemoteMACIP.IPAddr.w[0] + MyTCB.remotePort.Val) ^ MyTCB.localPort.Val;
                                                vFlags = SYN | ACK;
                                                MyTCBStub.smState = TCP_SYN_RECEIVED;
                                                
                                                // Delete this SYN from the SYNQueue and compact the SYNQueue[] array
                                                TCPRAMCopy((PTR_BASE)&SYNQueue[w], TCP_PIC_RAM, (PTR_BASE)&SYNQueue[w+1], TCP_PIC_RAM, (TCP_SYN_QUEUE_MAX_ENTRIES-1u-w)*sizeof(TCP_SYN_QUEUE));
                                                SYNQueue[TCP_SYN_QUEUE_MAX_ENTRIES-1].wDestPort = 0u;
        
                                                break;
                                        }
                                }
                        }
                #endif

                if(vFlags)
                        SendTCP(vFlags, bRetransmit ? 0 : SENDTCP_RESET_TIMERS);

                // The TCP_CLOSED, TCP_LISTEN, and sometimes the TCP_ESTABLISHED 
                // state don't need any timeout events, so see if the timer is enabled
                if(!MyTCBStub.Flags.bTimerEnabled)
                {
                        #if defined(TCP_KEEP_ALIVE_TIMEOUT)
                                // Only the established state has any use for keep-alives
                                if(MyTCBStub.smState == TCP_ESTABLISHED)
                                {
                                        // If timeout has not occured, do not do anything.
                                        if((LONG)(TickGet() - MyTCBStub.eventTime) < (LONG)0)
                                                continue;
                
                                        // If timeout has occured and the connection appears to be dead (no 
                                        // responses from remote node at all), close the connection so the 
                                        // application doesn't sit around indefinitely with a useless socket 
                                        // that it thinks is still open
                                        if(MyTCBStub.Flags.vUnackedKeepalives == TCP_MAX_UNACKED_KEEP_ALIVES)
                                        {
                                                vFlags = MyTCBStub.Flags.bServer;

                                                // Force an immediate FIN and RST transmission
                                                // Double calling TCPDisconnect() will also place us 
                                                // back in the listening state immediately if a server socket.
                                                TCPDisconnect(hTCP);
                                                TCPDisconnect(hTCP);
                                                
                                                // Prevent client mode sockets from getting reused by other applications.  
                                                // The application must call TCPDisconnect() with the handle to free this 
                                                // socket (and the handle associated with it)
                                                if(!vFlags)
                                                        MyTCBStub.smState = TCP_CLOSED_BUT_RESERVED;
                                                
                                                continue;
                                        }
                                        
                                        // Otherwise, if a timeout occured, simply send a keep-alive packet
                                        SyncTCB();
                                        SendTCP(ACK, SENDTCP_KEEP_ALIVE);
                                        MyTCBStub.eventTime = TickGet() + TCP_KEEP_ALIVE_TIMEOUT;
                                }
                        #endif
                        continue;
                }

                // If timeout has not occured, do not do anything.
                if((LONG)(TickGet() - MyTCBStub.eventTime) < (LONG)0)
                        continue;

                // Load up extended TCB information
                SyncTCB();

                // A timeout has occured.  Respond to this timeout condition
                // depending on what state this socket is in.
                switch(MyTCBStub.smState)
                {
                        #if defined(STACK_CLIENT_MODE)
                        #if defined(STACK_USE_DNS)
                        case TCP_GET_DNS_MODULE:
                                if(DNSBeginUsage())
                                {
                                        MyTCBStub.smState = TCP_DNS_RESOLVE;
                                        if(MyTCB.flags.bRemoteHostIsROM)
                                                DNSResolveROM((ROM BYTE*)(ROM_PTR_BASE)MyTCB.remote.dwRemoteHost, DNS_TYPE_A);
                                        else
                                                DNSResolve((BYTE*)(PTR_BASE)MyTCB.remote.dwRemoteHost, DNS_TYPE_A);
                                }
                                break;
                                
                        case TCP_DNS_RESOLVE:
                        {
                                IP_ADDR ipResolvedDNSIP;

                                // See if DNS resolution has finished.  Note that if the DNS 
                                // fails, the &ipResolvedDNSIP will be written with 0x00000000. 
                                // MyTCB.remote.dwRemoteHost is unioned with 
                                // MyTCB.remote.niRemoteMACIP.IPAddr, so we can't directly write 
                                // the DNS result into MyTCB.remote.niRemoteMACIP.IPAddr.  We 
                                // must copy it over only if the DNS is resolution step was 
                                // successful.
                                if(DNSIsResolved(&ipResolvedDNSIP))
                                {
                                        if(DNSEndUsage())
                                        {
                                                MyTCB.remote.niRemoteMACIP.IPAddr.Val = ipResolvedDNSIP.Val;
                                                MyTCBStub.smState = TCP_GATEWAY_SEND_ARP;
                                                MyTCBStub.remoteHash.Val = (MyTCB.remote.niRemoteMACIP.IPAddr.w[1]+MyTCB.remote.niRemoteMACIP.IPAddr.w[0] + MyTCB.remotePort.Val) ^ MyTCB.localPort.Val;
                                                MyTCB.retryCount = 0;
                                                MyTCB.retryInterval = (TICK_SECOND/4)/256;
                                        }
                                        else
                                        {
                                                MyTCBStub.eventTime = TickGet() + 10*TICK_SECOND;
                                                MyTCBStub.smState = TCP_GET_DNS_MODULE;
                                        }
                                }
                                break;
                        }
                        #endif // #if defined(STACK_USE_DNS)
                                
                        case TCP_GATEWAY_SEND_ARP:
                                // Obtain the MAC address associated with the server's IP address (either direct MAC address on same subnet, or the MAC address of the Gateway machine)
                                MyTCBStub.eventTime2 = (WORD)TickGetDiv256();
                                ARPResolve(&MyTCB.remote.niRemoteMACIP.IPAddr);
                                MyTCBStub.smState = TCP_GATEWAY_GET_ARP;
                                break;

                        case TCP_GATEWAY_GET_ARP:
                                // Wait for the MAC address to finish being obtained
                                if(!ARPIsResolved(&MyTCB.remote.niRemoteMACIP.IPAddr, &MyTCB.remote.niRemoteMACIP.MACAddr))
                                {
                                        // Time out if too much time is spent in this state
                                        // Note that this will continuously send out ARP 
                                        // requests for an infinite time if the Gateway 
                                        // never responds
                                        if((WORD)TickGetDiv256() - MyTCBStub.eventTime2 > (WORD)MyTCB.retryInterval)
                                        {
                                                // Exponentially increase timeout until we reach 6 attempts then stay constant
                                                if(MyTCB.retryCount < 6u)
                                                {
                                                        MyTCB.retryCount++;
                                                        MyTCB.retryInterval <<= 1;
                                                }

                                                // Retransmit ARP request
                                                MyTCBStub.smState = TCP_GATEWAY_SEND_ARP;
                                        }
                                        break;
                                }
                                
                                // Send out SYN connection request to remote node
                                // This automatically disables the Timer from 
                                // continuously firing for this socket
                                vFlags = SYN;
                                bRetransmit = FALSE;
                                MyTCBStub.smState = TCP_SYN_SENT;
                                break;
                        #endif // #if defined(STACK_CLIENT_MODE)
                        
                        case TCP_SYN_SENT:
                                // Keep sending SYN until we hear from remote node.
                                // This may be for infinite time, in that case
                                // caller must detect it and do something.
                                vFlags = SYN;
                                bRetransmit = TRUE;
                                break;
        
                        case TCP_SYN_RECEIVED:
                                // We must receive ACK before timeout expires.
                                // If not, resend SYN+ACK.
                                // Abort, if maximum attempts counts are reached.
                                if(MyTCB.retryCount < TCP_MAX_SYN_RETRIES)
                                {
                                        vFlags = SYN | ACK;
                                        bRetransmit = TRUE;
                                }
                                else
                                {
                                        if(MyTCBStub.Flags.bServer)
                                        {
                                                vFlags = RST | ACK;
                                                bCloseSocket = TRUE;
                                        }
                                        else
                                        {
                                                vFlags = SYN;
                                        }
                                }
                                break;
        
                        case TCP_ESTABLISHED:
                        case TCP_CLOSE_WAIT:
                                // Retransmit any unacknowledged data
                                if(MyTCB.retryCount < TCP_MAX_RETRIES)
                                {
                                        vFlags = ACK;
                                        bRetransmit = TRUE;
                                }
                                else
                                {
                                        // No response back for too long, close connection
                                        // This could happen, for instance, if the communication 
                                        // medium was lost
                                        MyTCBStub.smState = TCP_FIN_WAIT_1;
                                        vFlags = FIN | ACK;
                                }
                                break;
        
                        case TCP_FIN_WAIT_1:
                                if(MyTCB.retryCount < TCP_MAX_RETRIES)
                                {
                                        // Send another FIN
                                        vFlags = FIN | ACK;
                                        bRetransmit = TRUE;
                                }
                                else
                                {
                                        // Close on our own, we can't seem to communicate 
                                        // with the remote node anymore
                                        vFlags = RST | ACK;
                                        bCloseSocket = TRUE;
                                }
                                break;
        
                        case TCP_FIN_WAIT_2:
                                // Close on our own, we can't seem to communicate 
                                // with the remote node anymore
                                vFlags = RST | ACK;
                                bCloseSocket = TRUE;
                                break;

                        case TCP_CLOSING:
                                if(MyTCB.retryCount < TCP_MAX_RETRIES)
                                {
                                        // Send another ACK+FIN (the FIN is retransmitted 
                                        // automatically since it hasn't been acknowledged by 
                                        // the remote node yet)
                                        vFlags = ACK;
                                        bRetransmit = TRUE;
                                }
                                else
                                {
                                        // Close on our own, we can't seem to communicate 
                                        // with the remote node anymore
                                        vFlags = RST | ACK;
                                        bCloseSocket = TRUE;
                                }
                                break;
        
//                      case TCP_TIME_WAIT:
//                              // Wait around for a while (2MSL) and then goto closed state
//                              bCloseSocket = TRUE;
//                              break;
//                      

                        case TCP_LAST_ACK:
                                // Send some more FINs or close anyway
                                if(MyTCB.retryCount < TCP_MAX_RETRIES)
                                {
                                        vFlags = FIN | ACK;
                                        bRetransmit = TRUE;
                                }
                                else
                                {
                                        vFlags = RST | ACK;
                                        bCloseSocket = TRUE;
                                }
                                break;
                        
                        default:
                                break;
                }

                if(vFlags)
                {
                        if(bRetransmit)
                        {
                                // Set the appropriate retry time
                                MyTCB.retryCount++;
                                MyTCB.retryInterval <<= 1;
                
                                // Transmit all unacknowledged data over again
                                // Roll back unacknowledged TX tail pointer to cause retransmit to occur
                                MyTCB.MySEQ -= (LONG)(SHORT)(MyTCB.txUnackedTail - MyTCBStub.txTail);
                                if(MyTCB.txUnackedTail < MyTCBStub.txTail)
                                        MyTCB.MySEQ -= (LONG)(SHORT)(MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart);
                                MyTCB.txUnackedTail = MyTCBStub.txTail;         
                                SendTCP(vFlags, 0);
                        }
                        else
                                SendTCP(vFlags, SENDTCP_RESET_TIMERS);

                }
                
                if(bCloseSocket)
                        CloseSocket();
        }
        
        
        #if TCP_SYN_QUEUE_MAX_ENTRIES
                // Process SYN Queue entry timeouts
                for(w = 0; w < TCP_SYN_QUEUE_MAX_ENTRIES; w++)
                {
                        // Abort search if there are no more valid records
                        if(SYNQueue[w].wDestPort == 0u)
                                break;
                        
                        // See if this SYN has timed out
                        if((WORD)TickGetDiv256() - SYNQueue[w].wTimestamp > (WORD)(TCP_SYN_QUEUE_TIMEOUT/256ull))
                        {
                                // Delete this SYN from the SYNQueue and compact the SYNQueue[] array
                                TCPRAMCopy((PTR_BASE)&SYNQueue[w], TCP_PIC_RAM, (PTR_BASE)&SYNQueue[w+1], TCP_PIC_RAM, (TCP_SYN_QUEUE_MAX_ENTRIES-1u-w)*sizeof(TCP_SYN_QUEUE));
                                SYNQueue[TCP_SYN_QUEUE_MAX_ENTRIES-1].wDestPort = 0u;
        
                                // Since we deleted an entry, we need to roll back one 
                                // index so next loop will process the correct record
                                w--;    
                        }
                }
        #endif
}


/*****************************************************************************
  Function:
        BOOL TCPProcess(NODE_INFO* remote, IP_ADDR* localIP, WORD len)

  Summary:
        Handles incoming TCP segments.

  Description:
        This function handles incoming TCP segments.  When a segment arrives, it
        is compared to open sockets using a hash of the remote port and IP.  
        On a match, the data is passed to HandleTCPSeg for further processing.

  Precondition:
        TCP is initialized and a TCP segment is ready in the MAC buffer.

  Parameters:
        remote - Remote NODE_INFO structure
        localIP - This stack's IP address (for header checking)
        len - Total length of the waiting TCP segment

  Return Values:
        TRUE - the segment was properly handled.
        FALSE - otherwise
  ***************************************************************************/
BOOL TCPProcess(NODE_INFO* remote, IP_ADDR* localIP, WORD len)
{
        TCP_HEADER      TCPHeader;
        PSEUDO_HEADER   pseudoHeader;
        WORD_VAL        checksum1;
        WORD_VAL        checksum2;
        BYTE            optionsSize;

        // Calculate IP pseudoheader checksum.
        pseudoHeader.SourceAddress      = remote->IPAddr;
        pseudoHeader.DestAddress        = *localIP;
        pseudoHeader.Zero               = 0x0;
        pseudoHeader.Protocol           = IP_PROT_TCP;
        pseudoHeader.Length             = len;

        SwapPseudoHeader(pseudoHeader);

        checksum1.Val = ~CalcIPChecksum((BYTE*)&pseudoHeader,
                sizeof(pseudoHeader));

        // Now calculate TCP packet checksum in NIC RAM - should match
        // pesudo header checksum
        checksum2.Val = CalcIPBufferChecksum(len);

        // Compare checksums.
        if(checksum1.Val != checksum2.Val)
        {
                MACDiscardRx();
                return TRUE;
        }

#if defined(DEBUG_GENERATE_RX_LOSS)
        // Throw RX packets away randomly
        if(rand() > DEBUG_GENERATE_RX_LOSS)
        {
                MACDiscardRx();
                return TRUE;
        }
#endif

        // Retrieve TCP header.
        IPSetRxBuffer(0);
        MACGetArray((BYTE*)&TCPHeader, sizeof(TCPHeader));
        SwapTCPHeader(&TCPHeader);


        // Skip over options to retrieve data bytes
        optionsSize = (BYTE)((TCPHeader.DataOffset.Val << 2)-
                sizeof(TCPHeader));
        len = len - optionsSize - sizeof(TCPHeader);

        // Find matching socket.
        if(FindMatchingSocket(&TCPHeader, remote))
        {
                #if defined(STACK_USE_SSL)
                PTR_BASE prevRxHead;
                // For SSL connections, show HandleTCPSeg() the full data buffer
                prevRxHead = MyTCBStub.rxHead;
                if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                        MyTCBStub.rxHead = MyTCBStub.sslRxHead;
                #endif
                
                HandleTCPSeg(&TCPHeader, len);
                
                #if defined(STACK_USE_SSL)
                if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                {
                        // Restore the buffer state
                        MyTCBStub.sslRxHead = MyTCBStub.rxHead;
                        MyTCBStub.rxHead = prevRxHead;

                        // Process the new SSL data, using the currently loaded stub
                        TCPSSLHandleIncoming(hCurrentTCP);
                }
                #endif
        }
//      else
//      {
//              // NOTE: RFC 793 specifies that if the socket is closed and a segment 
//              // arrives, we should send back a RST if the RST bit in the incoming 
//              // packet is not set.  Instead, we will just silently ignore such a 
//              // packet since this is what firewalls do on purpose to enhance 
//              // security.
//              //if(!TCPHeader.Flags.bits.flagRST)
//              //      SendTCP(RST, SENDTCP_RESET_TIMERS);
//      }

        // Finished with this packet, discard it and free the Ethernet RAM for new packets
        MACDiscardRx();

        return TRUE;
}


/*****************************************************************************
  Function:
        static void SendTCP(BYTE vTCPFlags, BYTE vSendFlags)

  Summary:
        Transmits a TPC segment.

  Description:
        This function assembles and transmits a TCP segment, including any 
        pending data.  It also supports retransmissions, keep-alives, and 
        other packet types.

  Precondition:
        TCP is initialized.

  Parameters:
        vTCPFlags - Additional TCP flags to include
        vSendFlags - Any combinations of SENDTCP_* constants to modify the
                                 transmit behavior or contents.

  Returns:
        None
  ***************************************************************************/
static void SendTCP(BYTE vTCPFlags, BYTE vSendFlags)
{
        WORD_VAL        wVal;
        TCP_HEADER      header;
        TCP_OPTIONS     options;
        PSEUDO_HEADER   pseudoHeader;
        WORD                    len;
        WORD                    wEffectiveWindow;
        
        SyncTCB();

        // FINs must be handled specially
        if(vTCPFlags & FIN)
        {
                MyTCBStub.Flags.bTXFIN = 1;
                vTCPFlags &= ~FIN;
        }

        // Status will now be synched, disable automatic future 
        // status transmissions
        MyTCBStub.Flags.bTimer2Enabled = 0;
        MyTCBStub.Flags.bDelayedACKTimerEnabled = 0;
        MyTCBStub.Flags.bOneSegmentReceived = 0;
        MyTCBStub.Flags.bTXASAP = 0;
        MyTCBStub.Flags.bTXASAPWithoutTimerReset = 0;
        MyTCBStub.Flags.bHalfFullFlush = 0;

        //  Make sure that we can write to the MAC transmit area
        while(!IPIsTxReady());

        // Put all socket application data in the TX space
        if(vTCPFlags & (SYN | RST))
        {
                // Don't put any data in SYN and RST messages
                len = 0;
        }
        else
        {
                // Begin copying any application data over to the TX space
                if(MyTCBStub.txHead == MyTCB.txUnackedTail)
                {
                        // All caught up on data TX, no real data for this packet
                        len = 0;

                        // If we are to transmit a FIN, make sure we can put one in this packet
                        if(MyTCBStub.Flags.bTXFIN)
                        {
                                if(MyTCB.remoteWindow)
                                        vTCPFlags |= FIN;
                        }
                }
                else if(MyTCBStub.txHead > MyTCB.txUnackedTail)
                {
                        len = MyTCBStub.txHead - MyTCB.txUnackedTail;
                        wEffectiveWindow = MyTCB.remoteWindow;
                        if(MyTCB.txUnackedTail >= MyTCBStub.txTail)
                                wEffectiveWindow -= MyTCB.txUnackedTail - MyTCBStub.txTail;
                        else
                                wEffectiveWindow -= (MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart) - (MyTCBStub.txTail - MyTCB.txUnackedTail);

                        if(len > wEffectiveWindow)
                                len = wEffectiveWindow;

                        if(len > MyTCB.wRemoteMSS)
                        {
                                len = MyTCB.wRemoteMSS;
                                MyTCBStub.Flags.bTXASAPWithoutTimerReset = 1;
                        }

                        // If we are to transmit a FIN, make sure we can put one in this packet
                        if(MyTCBStub.Flags.bTXFIN)
                        {
                                if((len != wEffectiveWindow) && (len != MyTCB.wRemoteMSS))
                                        vTCPFlags |= FIN;
                        }

                        // Copy application data into the raw TX buffer
                        TCPRAMCopy(BASE_TX_ADDR+sizeof(ETHER_HEADER)+sizeof(IP_HEADER)+sizeof(TCP_HEADER), TCP_ETH_RAM, MyTCB.txUnackedTail, MyTCBStub.vMemoryMedium, len);
                        MyTCB.txUnackedTail += len;
                }
                else
                {
                        pseudoHeader.Length = MyTCBStub.bufferRxStart - MyTCB.txUnackedTail;
                        len = pseudoHeader.Length + MyTCBStub.txHead - MyTCBStub.bufferTxStart;

                        wEffectiveWindow = MyTCB.remoteWindow;
                        if(MyTCB.txUnackedTail >= MyTCBStub.txTail)
                                wEffectiveWindow -= MyTCB.txUnackedTail - MyTCBStub.txTail;
                        else
                                wEffectiveWindow -= (MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart) - (MyTCBStub.txTail - MyTCB.txUnackedTail);
                                
                        if(len > wEffectiveWindow)
                                len = wEffectiveWindow;

                        if(len > MyTCB.wRemoteMSS)
                        {
                                len = MyTCB.wRemoteMSS;
                                MyTCBStub.Flags.bTXASAPWithoutTimerReset = 1;
                        }

                        // If we are to transmit a FIN, make sure we can put one in this packet
                        if(MyTCBStub.Flags.bTXFIN)
                        {
                                if((len != wEffectiveWindow) && (len != MyTCB.wRemoteMSS))
                                        vTCPFlags |= FIN;
                        }

                        if(pseudoHeader.Length > len)
                                pseudoHeader.Length = len;

                        // Copy application data into the raw TX buffer
                        TCPRAMCopy(BASE_TX_ADDR+sizeof(ETHER_HEADER)+sizeof(IP_HEADER)+sizeof(TCP_HEADER), TCP_ETH_RAM, MyTCB.txUnackedTail, MyTCBStub.vMemoryMedium, pseudoHeader.Length);
                        pseudoHeader.Length = len - pseudoHeader.Length;
        
                        // Copy any left over chunks of application data over
                        if(pseudoHeader.Length)
                        {
                                TCPRAMCopy(BASE_TX_ADDR+sizeof(ETHER_HEADER)+sizeof(IP_HEADER)+sizeof(TCP_HEADER)+(MyTCBStub.bufferRxStart-MyTCB.txUnackedTail), TCP_ETH_RAM, MyTCBStub.bufferTxStart, MyTCBStub.vMemoryMedium, pseudoHeader.Length);
                        }

                        MyTCB.txUnackedTail += len;
                        if(MyTCB.txUnackedTail >= MyTCBStub.bufferRxStart)
                                MyTCB.txUnackedTail -= MyTCBStub.bufferRxStart-MyTCBStub.bufferTxStart;
                }
        }

        // Ensure that all packets with data of some kind are 
        // retransmitted by TCPTick() until acknowledged
        // Pure ACK packets with no data are not ACKed back in TCP
        if(len || (vTCPFlags & (SYN | FIN)))
        {
                // Push (PSH) all data for enhanced responsiveness on 
                // the remote end, especially with GUIs
                if(len)
                        vTCPFlags |= PSH;

                if(vSendFlags & SENDTCP_RESET_TIMERS)
                {
                        MyTCB.retryCount = 0;
                        MyTCB.retryInterval = TCP_START_TIMEOUT_VAL;
                }       

                MyTCBStub.eventTime = TickGet() + MyTCB.retryInterval;
                MyTCBStub.Flags.bTimerEnabled = 1;
        }
        else if(vSendFlags & SENDTCP_KEEP_ALIVE)
        {
                // Increment Keep Alive TX counter to handle disconnection if not response is returned
                MyTCBStub.Flags.vUnackedKeepalives++;
                
                // Generate a dummy byte
                MyTCB.MySEQ -= 1;
                len = 1;
        }
        else if(MyTCBStub.Flags.bTimerEnabled) 
        {
                // If we have data to transmit, but the remote RX window is zero, 
                // so we aren't transmitting any right now then make sure to not 
                // extend the retry counter or timer.  This will stall our TX 
                // with a periodic ACK sent to the remote node.
                if(!(vSendFlags & SENDTCP_RESET_TIMERS))
                {
                        // Roll back retry counters since we can't send anything, 
                        // but only if we incremented it in the first place
                        if(MyTCB.retryCount)
                        {
                                MyTCB.retryCount--;
                                MyTCB.retryInterval >>= 1;
                        }
                }
        
                MyTCBStub.eventTime = TickGet() + MyTCB.retryInterval;
        }
        

        header.SourcePort                       = MyTCB.localPort.Val;
        header.DestPort                         = MyTCB.remotePort.Val;
        header.SeqNumber                        = MyTCB.MySEQ;
        header.AckNumber                        = MyTCB.RemoteSEQ;
        header.Flags.bits.Reserved2     = 0;
        header.DataOffset.Reserved3     = 0;
        header.Flags.byte                       = vTCPFlags;
        header.UrgentPointer        = 0;

        // Update our send sequence number and ensure retransmissions 
        // of SYNs and FINs use the right sequence number
        MyTCB.MySEQ += (DWORD)len;
        if(vTCPFlags & SYN)
        {
                // SEG.ACK needs to be zero for the first SYN packet for compatibility 
                // with certain paranoid TCP/IP stacks, even though the ACK flag isn't 
                // set (indicating that the AckNumber field is unused).
                if(!(vTCPFlags & ACK))
                        header.AckNumber = 0x00000000;

                if(MyTCB.flags.bSYNSent)
                        header.SeqNumber--;
                else
                {
                        MyTCB.MySEQ++;
                        MyTCB.flags.bSYNSent = 1;
                }
        }
        if(vTCPFlags & FIN)
        {
                if(MyTCB.flags.bFINSent)
                        header.SeqNumber--;
                else
                {
                        MyTCB.MySEQ++;
                        MyTCB.flags.bFINSent = 1;
                }
        }

        // Calculate the amount of free space in the RX buffer area of this socket
        if(MyTCBStub.rxHead >= MyTCBStub.rxTail)
                header.Window = (MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart) - (MyTCBStub.rxHead - MyTCBStub.rxTail);
        else
                header.Window = MyTCBStub.rxTail - MyTCBStub.rxHead - 1;

        // Calculate the amount of free space in the MAC RX buffer area and adjust window if needed
        wVal.Val = MACGetFreeRxSize()-64;
        if((SHORT)wVal.Val < (SHORT)0)
                wVal.Val = 0;
        // Force the remote node to throttle back if we are running low on general RX buffer space
        if(header.Window > wVal.Val)
                header.Window = wVal.Val;

        SwapTCPHeader(&header);


        len += sizeof(header);
        header.DataOffset.Val   = sizeof(header) >> 2;

        // Insert the MSS (Maximum Segment Size) TCP option if this is SYN packet
        if(vTCPFlags & SYN)
        {
                len += sizeof(options);
                options.Kind = TCP_OPTIONS_MAX_SEG_SIZE;
                options.Length = 0x04;

                // Load MSS and swap to big endian
                options.MaxSegSize.Val = (((TCP_MAX_SEG_SIZE_RX-4)&0x00FF)<<8) | (((TCP_MAX_SEG_SIZE_RX-4)&0xFF00)>>8);

                header.DataOffset.Val   += sizeof(options) >> 2;
        }

        // Calculate IP pseudoheader checksum.
        pseudoHeader.SourceAddress      = AppConfig.MyIPAddr;
        pseudoHeader.DestAddress    = MyTCB.remote.niRemoteMACIP.IPAddr;
        pseudoHeader.Zero           = 0x0;
        pseudoHeader.Protocol       = IP_PROT_TCP;
        pseudoHeader.Length                     = len;
        SwapPseudoHeader(pseudoHeader);
        header.Checksum = ~CalcIPChecksum((BYTE*)&pseudoHeader, sizeof(pseudoHeader));

        // Write IP header
        MACSetWritePtr(BASE_TX_ADDR + sizeof(ETHER_HEADER));
        IPPutHeader(&MyTCB.remote.niRemoteMACIP, IP_PROT_TCP, len);
        MACPutArray((BYTE*)&header, sizeof(header));
        if(vTCPFlags & SYN)
                MACPutArray((BYTE*)&options, sizeof(options));

        // Update the TCP checksum
        MACSetReadPtr(BASE_TX_ADDR + sizeof(ETHER_HEADER) + sizeof(IP_HEADER));
        wVal.Val = CalcIPBufferChecksum(len);
#if defined(DEBUG_GENERATE_TX_LOSS)
        // Damage TCP checksums on TX packets randomly
        if(rand() > DEBUG_GENERATE_TX_LOSS)
        {
                wVal.Val++;
        }
#endif
        MACSetWritePtr(BASE_TX_ADDR + sizeof(ETHER_HEADER) + sizeof(IP_HEADER) + 16);
        MACPutArray((BYTE*)&wVal, sizeof(WORD));

        // Physically start the packet transmission over the network
        MACFlush();
}

/*****************************************************************************
  Function:
        static BOOL FindMatchingSocket(TCP_HEADER* h, NODE_INFO* remote)

  Summary:
        Finds a suitable socket for a TCP segment.

  Description:
        This function searches through the sockets and attempts to match one with
        a given TCP header and NODE_INFO structure.  If a socket is found, its 
        index is saved in hCurrentTCP and the associated MyTCBStub and MyTCB are
        loaded. Otherwise, INVALID_SOCKET is placed in hCurrentTCP.
        
  Precondition:
        TCP is initialized.

  Parameters:
        h - TCP header to be matched against
        remote - The remote node who sent this header

  Return Values:
        TRUE - A match was found and is loaded in hCurrentTCP
        FALSE - No suitable socket was found and hCurrentTCP is INVALID_SOCKET
  ***************************************************************************/
static BOOL FindMatchingSocket(TCP_HEADER* h, NODE_INFO* remote)
{
        TCP_SOCKET hTCP;
        TCP_SOCKET partialMatch;
        WORD hash;

        // Prevent connections on invalid port 0
        if(h->DestPort == 0u)
                return FALSE;

        partialMatch = INVALID_SOCKET;
        hash = (remote->IPAddr.w[1]+remote->IPAddr.w[0] + h->SourcePort) ^ h->DestPort;

        // Loop through all sockets looking for a socket that is expecting this 
        // packet or can handle it.
        for(hTCP = 0; hTCP < TCP_SOCKET_COUNT; hTCP++ )
        {
                SyncTCBStub(hTCP);

                if(MyTCBStub.smState == TCP_CLOSED)
                {
                        continue;
                }
                else if(MyTCBStub.smState == TCP_LISTEN)
                {// For listening ports, check if this is the correct port
                        if(MyTCBStub.remoteHash.Val == h->DestPort)
                                partialMatch = hTCP;
                        
                        #if defined(STACK_USE_SSL_SERVER)
                        // Check the SSL port as well for SSL Servers
                        // 0 is defined as an invalid port number
                        if(MyTCBStub.sslTxHead == h->DestPort)
                                partialMatch = hTCP;
                        #endif
                        
                        continue;
                }
                else if(MyTCBStub.remoteHash.Val != hash)
                {// Ignore if the hash doesn't match
                        continue;
                }

                SyncTCB();
                if(     h->DestPort == MyTCB.localPort.Val &&
                        h->SourcePort == MyTCB.remotePort.Val &&
                        remote->IPAddr.Val == MyTCB.remote.niRemoteMACIP.IPAddr.Val)
                {
                        return TRUE;
                }
        }


        // If there is a partial match, then a listening socket is currently 
        // available.  Set up the extended TCB with the info needed 
        // to establish a connection and return this socket to the 
        // caller.
        if(partialMatch != INVALID_SOCKET)
        {
                SyncTCBStub(partialMatch);
                SyncTCB();
        
                // For SSL ports, begin the SSL Handshake
                #if defined(STACK_USE_SSL_SERVER)
                if(MyTCBStub.sslTxHead == h->DestPort)
                {
                        // Try to start an SSL session.  If no stubs are available,
                        // we can't service this request right now, so ignore it.
                        if(!TCPStartSSLServer(partialMatch))
                                partialMatch = INVALID_SOCKET;
                }
                #endif
        
                // Make sure the above check didn't fail (this is unfortunately 
                // redundant for non-SSL sockets).  Otherwise, fall out to below
                // and add to the SYN queue.
                if(partialMatch != INVALID_SOCKET)
                {
                        MyTCBStub.remoteHash.Val = hash;
                
                        memcpy((void*)&MyTCB.remote, (void*)remote, sizeof(NODE_INFO));
                        MyTCB.remotePort.Val = h->SourcePort;
                        MyTCB.localPort.Val = h->DestPort;
                        MyTCB.txUnackedTail     = MyTCBStub.bufferTxStart;
                
                        // All done, and we have a match
                        return TRUE;
                }
        }

        // No available sockets are listening on this port.  (Or, for
        // SSL requests, perhaps no SSL sessions were available.  However,
        // there may be a server socket which is currently busy but 
        // could handle this packet, so we should check.
        #if TCP_SYN_QUEUE_MAX_ENTRIES
        {
                WORD wQueueInsertPos;
                
                // See if this is a SYN packet
                if(!h->Flags.bits.flagSYN)
                        return FALSE;

                // See if there is space in our SYN queue
                if(SYNQueue[TCP_SYN_QUEUE_MAX_ENTRIES-1].wDestPort)
                        return FALSE;
                
                // See if we have this SYN already in our SYN queue.
                // If not already in the queue, find out where we 
                // should insert this SYN to the queue
                for(wQueueInsertPos = 0; wQueueInsertPos < TCP_SYN_QUEUE_MAX_ENTRIES; wQueueInsertPos++)
                {
                        // Exit loop if we found a free record
                        if(SYNQueue[wQueueInsertPos].wDestPort == 0u)
                                break;

                        // Check if this SYN packet is already in the SYN queue
                        if(SYNQueue[wQueueInsertPos].wDestPort != h->DestPort)
                                continue;
                        if(SYNQueue[wQueueInsertPos].wSourcePort != h->SourcePort)
                                continue;
                        if(SYNQueue[wQueueInsertPos].niSourceAddress.IPAddr.Val != remote->IPAddr.Val)
                                continue;

                        // SYN matches SYN queue entry.  Update timestamp and do nothing.
                        SYNQueue[wQueueInsertPos].wTimestamp = TickGetDiv256();
                        return FALSE;
                }
                
                // Check to see if we have any server sockets which 
                // are currently connected, but could handle this SYN 
                // request at a later time if the client disconnects.
                for(hTCP = 0; hTCP < TCP_SOCKET_COUNT; hTCP++)
                {
                        SyncTCBStub(hTCP);
                        if(!MyTCBStub.Flags.bServer)
                                continue;

                        SyncTCB();
                        #if defined(STACK_USE_SSL_SERVER)
                        if((MyTCB.localPort.Val != h->DestPort) && (MyTCB.localSSLPort.Val != h->DestPort))
                        #else
                        if(MyTCB.localPort.Val != h->DestPort)
                        #endif
                                continue;

                        // Generate the SYN queue entry
                        memcpy((void*)&SYNQueue[wQueueInsertPos].niSourceAddress, (void*)remote, sizeof(NODE_INFO));
                        SYNQueue[wQueueInsertPos].wSourcePort = h->SourcePort;
                        SYNQueue[wQueueInsertPos].dwSourceSEQ = h->SeqNumber;
                        SYNQueue[wQueueInsertPos].wDestPort = h->DestPort;
                        SYNQueue[wQueueInsertPos].wTimestamp = TickGetDiv256();

                        return FALSE;
                }
        }
        #endif
                
        return FALSE;

}



/*****************************************************************************
  Function:
        static void SwapTCPHeader(TCP_HEADER* header)

  Summary:
        Swaps endian-ness of a TCP header.

  Description:
        This function swaps the endian-ness of a given TCP header for comparison.

  Precondition:
        None

  Parameters:
        header - The TCP header that is to be swapped

  Returns:
        None
  ***************************************************************************/
static void SwapTCPHeader(TCP_HEADER* header)
{
        header->SourcePort      = swaps(header->SourcePort);
        header->DestPort        = swaps(header->DestPort);
        header->SeqNumber       = swapl(header->SeqNumber);
        header->AckNumber       = swapl(header->AckNumber);
        header->Window          = swaps(header->Window);
        header->Checksum        = swaps(header->Checksum);
        header->UrgentPointer   = swaps(header->UrgentPointer);
}



/*****************************************************************************
  Function:
        static void CloseSocket(void)

  Summary:
        Closes a TCP socket.

  Description:
        This function closes a TCP socket.  All socket state information is 
        reset, and any buffered bytes are discarded.  The socket is no longer
        accessible by the application after this point.

  Precondition:
        The TCPStub corresponding to the socket to be closed is synced.

  Parameters:
        None

  Returns:
        None
  ***************************************************************************/
static void CloseSocket(void)
{
        SyncTCB();

        MyTCBStub.remoteHash.Val = MyTCB.localPort.Val;
        MyTCBStub.txHead = MyTCBStub.bufferTxStart;
        MyTCBStub.txTail = MyTCBStub.bufferTxStart;
        MyTCBStub.rxHead = MyTCBStub.bufferRxStart;
        MyTCBStub.rxTail = MyTCBStub.bufferRxStart;
        MyTCBStub.smState = MyTCBStub.Flags.bServer ? TCP_LISTEN : TCP_CLOSED;
        MyTCBStub.Flags.vUnackedKeepalives = 0;
        MyTCBStub.Flags.bTimerEnabled = 0;
        MyTCBStub.Flags.bTimer2Enabled = 0;
        MyTCBStub.Flags.bDelayedACKTimerEnabled = 0;
        MyTCBStub.Flags.bOneSegmentReceived = 0;
        MyTCBStub.Flags.bHalfFullFlush = 0;
        MyTCBStub.Flags.bTXASAP = 0;
        MyTCBStub.Flags.bTXASAPWithoutTimerReset = 0;
        MyTCBStub.Flags.bTXFIN = 0;
        MyTCBStub.Flags.bSocketReset = 1;

        #if defined(STACK_USE_SSL)
        // If SSL is active, then we need to close it
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
        {
                SSLTerminate(MyTCBStub.sslStubID);
                MyTCBStub.sslStubID = SSL_INVALID_ID;

                // Swap the SSL port and local port back to proper values
                MyTCBStub.remoteHash.Val = MyTCB.localSSLPort.Val;
                MyTCB.localSSLPort.Val = MyTCB.localPort.Val;
                MyTCB.localPort.Val = MyTCBStub.remoteHash.Val;
        }

        // Reset the SSL buffer pointers
        MyTCBStub.sslRxHead = MyTCBStub.bufferRxStart;
        MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
        #endif
        
        #if defined(STACK_USE_SSL_SERVER)
        MyTCBStub.sslTxHead = MyTCB.localSSLPort.Val;
        #endif

        MyTCB.flags.bFINSent = 0;
        MyTCB.flags.bSYNSent = 0;
        MyTCB.flags.bRXNoneACKed1 = 0;
        MyTCB.flags.bRXNoneACKed2 = 0;
        MyTCB.txUnackedTail = MyTCBStub.bufferTxStart;
        ((DWORD_VAL*)(&MyTCB.MySEQ))->w[0] = rand();
        ((DWORD_VAL*)(&MyTCB.MySEQ))->w[1] = rand();
        MyTCB.sHoleSize = -1;
        MyTCB.remoteWindow = 1;
}


/*****************************************************************************
  Function:
        static WORD GetMaxSegSizeOption(void)

  Summary:
        Obtains the Maximum Segment Size (MSS) TCP Option out of the TCP header 
        for the current socket.

  Description:
        Parses the current TCP packet header and extracts the Maximum Segment Size 
        option.  

  Precondition:
        Must be called while a TCP packet is present and being processed via 
        HandleTCPSeg() and only if the the TCP SYN flag is set.

  Parameters:
        None

  Returns:
        Maximum segment size option value.  If illegal or not present, a failsafe 
        value of 536 is returned.  If the option is larger than the 
        TCP_MAX_SEG_SIZE_TX upper limit, then TCP_MAX_SEG_SIZE_TX is returned.

  Remarks:
        The internal MAC Read Pointer is moved but not restored.
  ***************************************************************************/
static WORD GetMaxSegSizeOption(void)
{
        BYTE vOptionsBytes;
        BYTE vOption;
        WORD wMSS;

        // Find out how many options bytes are in this packet.
        IPSetRxBuffer(2+2+4+4); // Seek to data offset field, skipping Source port (2), Destination port (2), Sequence number (4), and Acknowledgement number (4)
        vOptionsBytes = MACGet();
        vOptionsBytes = ((vOptionsBytes&0xF0)>>2) - sizeof(TCP_HEADER);

        // Return minimum Maximum Segment Size value of 536 bytes if none are 
        // present
        if(vOptionsBytes == 0u)
                return 536;
                
        // Seek to beginning of options
        MACGetArray(NULL, 7);

        // Search for the Maximum Segment Size option   
        while(vOptionsBytes--)
        {
                vOption = MACGet();
                
                if(vOption == 0u)       // End of Options list
                        break;
                
                if(vOption == 1u)       // NOP option
                        continue;
                        
                if(vOption == 2u)       // Maximum Segment Size option
                {
                        if(vOptionsBytes < 3u)
                                break;

                        wMSS = 0;
                                
                        // Get option length
                        vOption = MACGet();
                        if(vOption == 4u)
                        {// Retrieve MSS and swap value to little endian
                                ((BYTE*)&wMSS)[1] = MACGet();
                                ((BYTE*)&wMSS)[0] = MACGet();
                        }
                        
                        if(wMSS < 536u)
                                break;
                        if(wMSS > TCP_MAX_SEG_SIZE_TX)
                                return TCP_MAX_SEG_SIZE_TX;
                        else 
                                return wMSS;
                }
                else
                { // Assume this is a multi byte option and throw it way
                        if(vOptionsBytes < 2u)
                                break;
                        vOption = MACGet();
                        if(vOptionsBytes < vOption)
                                break;
                        MACGetArray(NULL, vOption);
                        vOptionsBytes -= vOption;
                }
                
        }
        
        // Did not find MSS option, return worst case default
        return 536;
}

/*****************************************************************************
  Function:
        static void HandleTCPSeg(TCP_HEADER* h, WORD len)

  Summary:
        Processes an incoming TCP segment.

  Description:
        Once an incoming segment has been matched to a socket, this function
        performs the necessary processing with the data.  Depending on the 
        segment and the state, this may include copying data to the TCP buffer,
        re-assembling out-of order packets, continuing an initialization or 
        closing handshake, or closing the socket altogether.

  Precondition:
        TCP is initialized and the current TCP stub is already synced.

  Parameters:
        h - The TCP header for this packet
        len - The total buffer length of this segment

  Returns:
        None
  ***************************************************************************/
static void HandleTCPSeg(TCP_HEADER* h, WORD len)
{
        DWORD dwTemp;
        PTR_BASE wTemp;
        LONG lMissingBytes;
        WORD wMissingBytes;
        WORD wFreeSpace;
        BYTE localHeaderFlags;
        DWORD localAckNumber;
        DWORD localSeqNumber;
        WORD wSegmentLength;
        BOOL bSegmentAcceptable;

        // Cache a few variables in local RAM.  
        // PIC18s take a fair amount of code and execution time to 
        // dereference pointers frequently.
        localHeaderFlags = h->Flags.byte;
        localAckNumber = h->AckNumber;
        localSeqNumber = h->SeqNumber;

        // We received a packet, reset the keep alive timer and count
        #if defined(TCP_KEEP_ALIVE_TIMEOUT)
                MyTCBStub.Flags.vUnackedKeepalives = 0;
                if(!MyTCBStub.Flags.bTimerEnabled)
                        MyTCBStub.eventTime = TickGet() + TCP_KEEP_ALIVE_TIMEOUT;
        #endif

        // Handle TCP_LISTEN and TCP_SYN_SENT states
        // Both of these states will return, so code following this 
        // state machine need not check explicitly for these two 
        // states.
        switch(MyTCBStub.smState)
        {
                case TCP_LISTEN:
                        // First: check RST flag
                        if(localHeaderFlags & RST)
                        {
                                CloseSocket();  // Unbind remote IP address/port info
                                return;
                        }

                        // Second: check ACK flag, which would be invalid
                        if(localHeaderFlags & ACK)
                        {
                                // Use a believable sequence number and reset the remote node
                                MyTCB.MySEQ = localAckNumber;
                                SendTCP(RST, 0);
                                CloseSocket();  // Unbind remote IP address/port info
                                return;
                        }

                        // Third: check for SYN flag, which is what we're looking for
                        if(localHeaderFlags & SYN)
                        {
                                // We now have a sequence number for the remote node
                                MyTCB.RemoteSEQ = localSeqNumber + 1;

                                // Get MSS option
                                MyTCB.wRemoteMSS = GetMaxSegSizeOption();

                                // Set Initial Send Sequence (ISS) number
                                // Nothing to do on this step... ISS already set in CloseSocket()
                                
                                // Respond with SYN + ACK
                                SendTCP(SYN | ACK, SENDTCP_RESET_TIMERS);
                                MyTCBStub.smState = TCP_SYN_RECEIVED;
                        }
                        else
                        {
                                CloseSocket();  // Unbind remote IP address/port info
                        }

                        // Fourth: check for other text and control
                        // Nothing to do since we don't support this
                        return;

                case TCP_SYN_SENT:
                        // Second: check the RST bit
                        // This is out of order because this stack has no API for 
                        // notifying the application that the connection seems to 
                        // be failing.  Instead, the application must time out and 
                        // the stack will just keep trying in the mean time.
                        if(localHeaderFlags & RST)
                                return;

                        // First: check ACK bit
                        if(localHeaderFlags & ACK)
                        {
                                if(localAckNumber != MyTCB.MySEQ)
                                {
                                        // Send a RST packet with SEQ = SEG.ACK, but retain our SEQ 
                                        // number for arivial of any other SYN+ACK packets
                                        localSeqNumber = MyTCB.MySEQ;   // Save our original SEQ number
                                        MyTCB.MySEQ = localAckNumber;   // Set SEQ = SEG.ACK
                                        SendTCP(RST, SENDTCP_RESET_TIMERS);             // Send the RST
                                        MyTCB.MySEQ = localSeqNumber;   // Restore original SEQ number
                                        return;
                                }
                        }

                        // Third: check the security and precedence
                        // No such feature in this stack.  We want to accept all connections.

                        // Fourth: check the SYN bit
                        if(localHeaderFlags & SYN)
                        {
                                // We now have an initial sequence number and window size
                                MyTCB.RemoteSEQ = localSeqNumber + 1;
                                MyTCB.remoteWindow = h->Window;

                                // Get MSS option
                                MyTCB.wRemoteMSS = GetMaxSegSizeOption();

                                if(localHeaderFlags & ACK)
                                {
                                        SendTCP(ACK, SENDTCP_RESET_TIMERS);
                                        MyTCBStub.smState = TCP_ESTABLISHED;
                                        // Set up keep-alive timer
                                        #if defined(TCP_KEEP_ALIVE_TIMEOUT)
                                                MyTCBStub.eventTime = TickGet() + TCP_KEEP_ALIVE_TIMEOUT;
                                        #endif
                                        MyTCBStub.Flags.bTimerEnabled = 0;
                                }
                                else
                                {
                                        SendTCP(SYN | ACK, SENDTCP_RESET_TIMERS);
                                        MyTCBStub.smState = TCP_SYN_RECEIVED;
                                }
                        }

                        // Fifth: drop the segment if neither SYN or RST is set
                        return;

                default:
                        break;
        }

        //
        // First: check the sequence number
        //
        wSegmentLength = len;
        if(localHeaderFlags & FIN)
                wSegmentLength++;
        if(localHeaderFlags & SYN)
                wSegmentLength++;

        // Calculate the RX FIFO space
        if(MyTCBStub.rxHead >= MyTCBStub.rxTail)
                wFreeSpace = (MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart) - (MyTCBStub.rxHead - MyTCBStub.rxTail);
        else
                wFreeSpace = MyTCBStub.rxTail - MyTCBStub.rxHead - 1;

        // Calculate the number of bytes ahead of our head pointer this segment skips
        lMissingBytes = localSeqNumber - MyTCB.RemoteSEQ;
        wMissingBytes = (WORD)lMissingBytes;
        
        // Run TCP acceptability tests to verify that this packet has a valid sequence number
        bSegmentAcceptable = FALSE;
        if(wSegmentLength)
        {
                // Check to see if we have free space, and if so, if any of the data falls within the freespace
                if(wFreeSpace)
                {
                        // RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
                        if((lMissingBytes >= (LONG)0) && (wFreeSpace > (DWORD)lMissingBytes))
                                bSegmentAcceptable = TRUE;
                        else
                        {
                                // RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
                                if((lMissingBytes + (LONG)wSegmentLength > (LONG)0) && (lMissingBytes <= (LONG)(SHORT)(wFreeSpace - wSegmentLength)))
                                        bSegmentAcceptable = TRUE;
                        }
                        
                        if((lMissingBytes < (LONG)wFreeSpace) && ((SHORT)wMissingBytes + (SHORT)wSegmentLength > (SHORT)0))
                                bSegmentAcceptable = TRUE;
                }
                // Segments with data are not acceptable if we have no free space
        }
        else
        {
                // Zero length packets are acceptable if they fall within our free space window
                // SEG.SEQ = RCV.NXT
                if(lMissingBytes == 0)
                {
                        bSegmentAcceptable = TRUE;
                }
                else
                {
                        // RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
                        if((lMissingBytes >= (LONG)0) && (wFreeSpace > (DWORD)lMissingBytes))
                                bSegmentAcceptable = TRUE;
                }
        }
        
        if(!bSegmentAcceptable)
        {
                // Unacceptable segment, drop it and respond appropriately
                if(!(localHeaderFlags & RST)) 
                        SendTCP(ACK, SENDTCP_RESET_TIMERS);
                return;
        }


        //
        // Second: check the RST bit
        //
        //
        // Fourth: check the SYN bit
        //
        // Note, that since the third step is not implemented, we can 
        // combine this second and fourth step into a single operation.
        if(localHeaderFlags & (RST | SYN))
        {
                CloseSocket();
                return;
        }

        //
        // Third: check the security and precedence
        //
        // Feature not supported.  Let's process this segment.

        //
        // Fifth: check the ACK bit
        //
        if(!(localHeaderFlags & ACK))
                return;

        switch(MyTCBStub.smState)
        {
                case TCP_SYN_RECEIVED:
                        if(localAckNumber != MyTCB.MySEQ)
                        {
                                // Send a RST packet with SEQ = SEG.ACK, but retain our SEQ 
                                // number for arivial of any other correct packets
                                localSeqNumber = MyTCB.MySEQ;   // Save our original SEQ number
                                MyTCB.MySEQ = localAckNumber;   // Set SEQ = SEG.ACK
                                SendTCP(RST, SENDTCP_RESET_TIMERS);             // Send the RST
                                MyTCB.MySEQ = localSeqNumber;   // Restore original SEQ number
                                return;
                        }
                        MyTCBStub.smState = TCP_ESTABLISHED;
                        // No break

                case TCP_ESTABLISHED:
                case TCP_FIN_WAIT_1:
                case TCP_FIN_WAIT_2:
                case TCP_CLOSE_WAIT:
                case TCP_CLOSING:
                        // Calculate what the highest possible SEQ number in our TX FIFO is
                        wTemp = MyTCBStub.txHead - MyTCB.txUnackedTail;
                        if((SHORT)wTemp < (SHORT)0)
                                wTemp += MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart;
                        dwTemp = MyTCB.MySEQ + (DWORD)wTemp;

                        // Drop the packet if it ACKs something we haven't sent
                        if((LONG)(dwTemp - localAckNumber) < (LONG)0)
                        {
                                SendTCP(ACK, 0);
                                return;
                        }

                        // Throw away all ACKnowledged TX data:
                        // Calculate what the last acknowledged sequence number was (ignoring any FINs we sent)
                        dwTemp = MyTCB.MySEQ - (LONG)(SHORT)(MyTCB.txUnackedTail - MyTCBStub.txTail);
                        if(MyTCB.txUnackedTail < MyTCBStub.txTail)
                                dwTemp -= MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart;
        
                        // Calcluate how many bytes were ACKed with this packet
                        dwTemp = localAckNumber - dwTemp;
                        if(((LONG)(dwTemp) > (LONG)0) && (dwTemp <= MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart))
                        {
                                MyTCB.flags.bRXNoneACKed1 = 0;
                                MyTCB.flags.bRXNoneACKed2 = 0;
                                MyTCBStub.Flags.bHalfFullFlush = FALSE;
        
                                // Bytes ACKed, free up the TX FIFO space
                                wTemp = MyTCBStub.txTail;
                                MyTCBStub.txTail += dwTemp;
                                if(MyTCB.txUnackedTail >= wTemp)
                                {
                                        if(MyTCB.txUnackedTail < MyTCBStub.txTail)
                                        {
                                                MyTCB.MySEQ += MyTCBStub.txTail - MyTCB.txUnackedTail;
                                                MyTCB.txUnackedTail = MyTCBStub.txTail;
                                        }
                                }
                                else
                                {
                                        wTemp = MyTCB.txUnackedTail + (MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart);
                                        if(wTemp < MyTCBStub.txTail)
                                        {
                                                MyTCB.MySEQ += MyTCBStub.txTail - wTemp;
                                                MyTCB.txUnackedTail = MyTCBStub.txTail;
                                        }
                                }
                                if(MyTCBStub.txTail >= MyTCBStub.bufferRxStart)
                                        MyTCBStub.txTail -= MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart;
                                if(MyTCB.txUnackedTail >= MyTCBStub.bufferRxStart)
                                        MyTCB.txUnackedTail -= MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart;
                        }
                        else
                        {
                                // See if we have outstanding TX data that is waiting for an ACK
                                if(MyTCBStub.txTail != MyTCB.txUnackedTail)
                                {
                                        if(MyTCB.flags.bRXNoneACKed1)
                                        {
                                                if(MyTCB.flags.bRXNoneACKed2)
                                                {
                                                        // Set up to perform a fast retransmission
                                                        // Roll back unacknowledged TX tail pointer to cause retransmit to occur
                                                        MyTCB.MySEQ -= (LONG)(SHORT)(MyTCB.txUnackedTail - MyTCBStub.txTail);
                                                        if(MyTCB.txUnackedTail < MyTCBStub.txTail)
                                                                MyTCB.MySEQ -= (LONG)(SHORT)(MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart);
                                                        MyTCB.txUnackedTail = MyTCBStub.txTail;
                                                        MyTCBStub.Flags.bTXASAPWithoutTimerReset = 1;
                                                }
                                                MyTCB.flags.bRXNoneACKed2 = 1;
                                        }
                                        MyTCB.flags.bRXNoneACKed1 = 1;
                                }
                        }

                        // No need to keep our retransmit timer going if we have nothing that needs ACKing anymore
                        if(MyTCBStub.txTail == MyTCBStub.txHead)
                        {
                                // Make sure there isn't a "FIN byte in our TX FIFO"
                                if(MyTCBStub.Flags.bTXFIN == 0u)
                                {
                                        // Convert retransmission timer to keep-alive timer
                                        #if defined(TCP_KEEP_ALIVE_TIMEOUT)
                                                MyTCBStub.eventTime = TickGet() + TCP_KEEP_ALIVE_TIMEOUT;
                                        #endif
                                        MyTCBStub.Flags.bTimerEnabled = 0;
                                }
                                else
                                {
                                        // "Throw away" FIN byte from our TX FIFO if it has been ACKed
                                        if(MyTCB.MySEQ == localAckNumber)
                                        {
                                                MyTCBStub.Flags.bTimerEnabled = 0;
                                                MyTCBStub.Flags.bTXFIN = 0;
                                                MyTCB.flags.bFINSent = 0;       
                                        }
                                }
                        }

                        // Update the local stored copy of the RemoteWindow
                        // If previously we had a zero window, and now we don't 
                        // immediately send whatever was pending
                        if((MyTCB.remoteWindow == 0u) && h->Window)
                                MyTCBStub.Flags.bTXASAP = 1;
                        MyTCB.remoteWindow = h->Window;

                        // A couple of states must do all of the TCP_ESTABLISHED stuff, but also a little more
                        if(MyTCBStub.smState == TCP_FIN_WAIT_1)
                        {
                                // Check to see if our FIN has been ACKnowledged
                                if(MyTCB.MySEQ == localAckNumber)
                                {
                                        // Reset our timer for forced closure if the remote node 
                                        // doesn't send us a FIN in a timely manner.
                                        MyTCBStub.eventTime = TickGet() + TCP_FIN_WAIT_2_TIMEOUT;
                                        MyTCBStub.Flags.bTimerEnabled = 1;
                                        MyTCBStub.smState = TCP_FIN_WAIT_2;
                                }
                        }
                        else if(MyTCBStub.smState == TCP_FIN_WAIT_2)
                        {
                                // RFC noncompliance:
                                // The remote node should not keep sending us data 
                                // indefinitely after we send a FIN to it.  
                                // However, some bad stacks may still keep sending 
                                // us data indefinitely after ACKing our FIN.  To 
                                // prevent this from locking up our socket, let's 
                                // send a RST right now and close forcefully on 
                                // our side.
                                if(!(localHeaderFlags & FIN))
                                {
                                        MyTCB.MySEQ = localAckNumber;   // Set SEQ = SEG.ACK
                                        SendTCP(RST | ACK, 0);
                                        CloseSocket();
                                        return;
                                }
                        }
                        else if(MyTCBStub.smState == TCP_CLOSING)
                        {
                                // Check to see if our FIN has been ACKnowledged
                                if(MyTCB.MySEQ == localAckNumber)
                                {
                                        // RFC not recommended: We should be going to 
                                        // the TCP_TIME_WAIT state right here and 
                                        // starting a 2MSL timer, but since we have so 
                                        // few precious sockets, we can't afford to 
                                        // leave a socket waiting around doing nothing 
                                        // for a long time.  If the remote node does 
                                        // not recieve this ACK, it'll have to figure 
                                        // out on it's own that the connection is now 
                                        // closed.
                                        CloseSocket();
                                }

                                return;
                        }

                        break;

                case TCP_LAST_ACK:
                        // Check to see if our FIN has been ACKnowledged
                        if(MyTCB.MySEQ == localAckNumber)
                                CloseSocket();
                        return;

//              case TCP_TIME_WAIT:
//                      // Nothing is supposed to arrive here.  If it does, reset the quiet timer.
//                      SendTCP(ACK, SENDTCP_RESET_TIMERS);
//                      return;

                default:
                        break;
        }

        //
        // Sixth: Check the URG bit
        //
        // Urgent packets are not supported in this stack, so we
        // will throw them away instead
        if(localHeaderFlags & URG)
                return;

        //
        // Seventh: Process the segment text
        //
        // Throw data away if in a state that doesn't accept data
        if(MyTCBStub.smState == TCP_CLOSE_WAIT)
                return;
        if(MyTCBStub.smState == TCP_CLOSING)
                return;
        if(MyTCBStub.smState == TCP_LAST_ACK)
                return;
//      if(MyTCBStub.smState == TCP_TIME_WAIT)
//              return;

        // Copy any valid segment data into our RX FIFO, if any
        if(len)
        {
                // See if there are bytes we must skip
                if((SHORT)wMissingBytes <= 0)
                {
                        // Position packet read pointer to start of useful data area.
                        IPSetRxBuffer((h->DataOffset.Val << 2) - wMissingBytes);
                        len += wMissingBytes;           
        
                        // Truncate packets that would overflow our TCP RX FIFO
                        // and request a retransmit by sending a duplicate ACK
                        if(len > wFreeSpace)
                                len = wFreeSpace;
        
                        MyTCB.RemoteSEQ += (DWORD)len;
                
                        // Copy the application data from the packet into the socket RX FIFO
                        // See if we need a two part copy (spans bufferEnd->bufferRxStart)
                        if(MyTCBStub.rxHead + len > MyTCBStub.bufferEnd)
                        {
                                wTemp = MyTCBStub.bufferEnd - MyTCBStub.rxHead + 1;
                                TCPRAMCopy(MyTCBStub.rxHead, MyTCBStub.vMemoryMedium, (PTR_BASE)-1, TCP_ETH_RAM, wTemp);
                                TCPRAMCopy(MyTCBStub.bufferRxStart, MyTCBStub.vMemoryMedium, (PTR_BASE)-1, TCP_ETH_RAM, len - wTemp);
                                MyTCBStub.rxHead = MyTCBStub.bufferRxStart + (len - wTemp);
                        }
                        else
                        {
                                TCPRAMCopy(MyTCBStub.rxHead, MyTCBStub.vMemoryMedium, (PTR_BASE)-1, TCP_ETH_RAM, len);
                                MyTCBStub.rxHead += len;
                        }
                
                        // See if we have a hole and other data waiting already in the RX FIFO
                        if(MyTCB.sHoleSize != -1)
                        {
                                MyTCB.sHoleSize -= len;
                                wTemp = MyTCB.wFutureDataSize + MyTCB.sHoleSize;
                
                                // See if we just closed up a hole, and if so, advance head pointer
                                if((SHORT)wTemp < (SHORT)0)
                                {
                                        MyTCB.sHoleSize = -1;
                                }
                                else if(MyTCB.sHoleSize <= 0)
                                {
                                        MyTCB.RemoteSEQ += wTemp;
                                        MyTCBStub.rxHead += wTemp;
                                        if(MyTCBStub.rxHead > MyTCBStub.bufferEnd)
                                                MyTCBStub.rxHead -= MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;                                                  
                                        MyTCB.sHoleSize = -1;
                                }
                        }
                } // This packet is out of order or we lost a packet, see if we can generate a hole to accomodate it
                else if((SHORT)wMissingBytes > 0)
                {
                        // Truncate packets that would overflow our TCP RX FIFO
                        if(len + wMissingBytes > wFreeSpace)
                                len = wFreeSpace - wMissingBytes;
                
                        // Position packet read pointer to start of useful data area.
                        IPSetRxBuffer(h->DataOffset.Val << 2);
        
                        // See if we need a two part copy (spans bufferEnd->bufferRxStart)
                        if(MyTCBStub.rxHead + wMissingBytes + len > MyTCBStub.bufferEnd)
                        {
                                // Calculate number of data bytes to copy before wraparound
                                wTemp = MyTCBStub.bufferEnd - MyTCBStub.rxHead + 1 - wMissingBytes;
                                if((SHORT)wTemp >= 0)
                                {
                                        TCPRAMCopy(MyTCBStub.rxHead + wMissingBytes, MyTCBStub.vMemoryMedium, (PTR_BASE)-1, TCP_ETH_RAM, wTemp);
                                        TCPRAMCopy(MyTCBStub.bufferRxStart, MyTCBStub.vMemoryMedium, (PTR_BASE)-1, TCP_ETH_RAM, len - wTemp);
                                }
                                else
                                {
                                        TCPRAMCopy(MyTCBStub.rxHead + wMissingBytes - (MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1), MyTCBStub.vMemoryMedium, (PTR_BASE)-1, TCP_ETH_RAM, len);
                                }
                        }
                        else
                        {
                                TCPRAMCopy(MyTCBStub.rxHead + wMissingBytes, MyTCBStub.vMemoryMedium, (PTR_BASE)-1, TCP_ETH_RAM, len);
                        }
                
                        // Record the hole is here
                        if(MyTCB.sHoleSize == -1)
                        {
                                MyTCB.sHoleSize = wMissingBytes;
                                MyTCB.wFutureDataSize = len;
                        }
                        else
                        {
                                // We already have a hole, see if we can shrink the hole 
                                // or extend the future data size
                                if(wMissingBytes < (WORD)MyTCB.sHoleSize)
                                {
                                        if((wMissingBytes + len > (WORD)MyTCB.sHoleSize + MyTCB.wFutureDataSize) || (wMissingBytes + len < (WORD)MyTCB.sHoleSize))
                                                MyTCB.wFutureDataSize = len;
                                        else
                                                MyTCB.wFutureDataSize = (WORD)MyTCB.sHoleSize + MyTCB.wFutureDataSize - wMissingBytes;
                                        MyTCB.sHoleSize = wMissingBytes;
                                }
                                else if(wMissingBytes + len > (WORD)MyTCB.sHoleSize + MyTCB.wFutureDataSize)
                                {
                                        // Make sure that there isn't a second hole between 
                                        // our future data and this TCP segment's future data
                                        if(wMissingBytes <= (WORD)MyTCB.sHoleSize + MyTCB.wFutureDataSize)
                                                MyTCB.wFutureDataSize += wMissingBytes + len - (WORD)MyTCB.sHoleSize - MyTCB.wFutureDataSize;
                                }
                                
                        }
                }
        }

        // Send back an ACK of the data (+SYN | FIN) we just received, 
        // if any.  To minimize bandwidth waste, we are implementing 
        // the delayed acknowledgement algorithm here, only sending 
        // back an immediate ACK if this is the second segment received.  
        // Otherwise, a 200ms timer will cause the ACK to be transmitted.
        if(wSegmentLength)
        {
                // For non-established sockets, let's delete all data in 
                // the RX buffer immediately after receiving it.  This is 
                // not really how TCP was intended to operate since a 
                // socket cannot receive any response after it sends a FIN,
                // but our TCP application API doesn't readily accomodate
                // receiving data after calling TCPDisconnect(), which 
                // invalidates the application TCP handle.  By deleting all 
                // data, we'll ensure that the RX window is nonzero and 
                // the remote node will be able to send us a FIN response, 
                // which needs an RX window of at least 1.
                if(MyTCBStub.smState != TCP_ESTABLISHED)
                        MyTCBStub.rxTail = MyTCBStub.rxHead;

                if(MyTCBStub.Flags.bOneSegmentReceived)
                {
                        SendTCP(ACK, SENDTCP_RESET_TIMERS);
                        SyncTCB();
                        // bOneSegmentReceived is cleared in SendTCP(), so no need here
                }
                else
                {
                        MyTCBStub.Flags.bOneSegmentReceived = TRUE;     
                
                        // Do not send an ACK immediately back.  Instead, we will 
                        // perform delayed acknowledgements.  To do this, we will 
                        // just start a timer
                        if(!MyTCBStub.Flags.bDelayedACKTimerEnabled)
                        {
                                MyTCBStub.Flags.bDelayedACKTimerEnabled = 1;
                                MyTCBStub.OverlappedTimers.delayedACKTime = (WORD)TickGetDiv256() + (WORD)((TCP_DELAYED_ACK_TIMEOUT)>>8);
                        }
                }
        }

        //
        // Eighth: check the FIN bit
        //
        if(localHeaderFlags & FIN)
        {
                // Note: Since we don't have a good means of storing "FIN bytes" 
                // in our TCP RX FIFO, we must ensure that FINs are processed 
                // in-order.
                if(MyTCB.RemoteSEQ + 1 == localSeqNumber + (DWORD)wSegmentLength)
                {
                        // FINs are treated as one byte of data for ACK sequencing
                        MyTCB.RemoteSEQ++;
                        
                        switch(MyTCBStub.smState)
                        {
                                case TCP_SYN_RECEIVED:
                                        // RFC in exact: Our API has no need for the user 
                                        // to explicitly close a socket that never really 
                                        // got opened fully in the first place, so just 
                                        // transmit a FIN automatically and jump to 
                                        // TCP_LAST_ACK
                                        MyTCBStub.smState = TCP_LAST_ACK;
                                        SendTCP(FIN | ACK, SENDTCP_RESET_TIMERS);
                                        return;

                                case TCP_ESTABLISHED:
                                        // Go to TCP_CLOSE_WAIT state
                                        MyTCBStub.smState = TCP_CLOSE_WAIT;
                                        
                                        // For legacy applications that don't call 
                                        // TCPDisconnect() as needed and expect the TCP/IP 
                                        // Stack to automatically close sockets when the 
                                        // remote node sends a FIN, let's start a timer so 
                                        // that we will eventually close the socket automatically
                                        MyTCBStub.OverlappedTimers.closeWaitTime = (WORD)TickGetDiv256() + (WORD)((TCP_CLOSE_WAIT_TIMEOUT)>>8);
                                        break;
        
                                case TCP_FIN_WAIT_1:
                                        if(MyTCB.MySEQ == localAckNumber)
                                        {
                                                // RFC not recommended: We should be going to 
                                                // the TCP_TIME_WAIT state right here and 
                                                // starting a 2MSL timer, but since we have so 
                                                // few precious sockets, we can't afford to 
                                                // leave a socket waiting around doing nothing 
                                                // for a long time.  If the remote node does 
                                                // not recieve this ACK, it'll have to figure 
                                                // out on it's own that the connection is now 
                                                // closed.
                                                SendTCP(ACK, 0);
                                                CloseSocket();
                                                return;
                                        }
                                        else
                                        {
                                                MyTCBStub.smState = TCP_CLOSING;
                                        }
                                        break;
        
                                case TCP_FIN_WAIT_2:
                                        // RFC not recommended: We should be going to 
                                        // the TCP_TIME_WAIT state right here and 
                                        // starting a 2MSL timer, but since we have so 
                                        // few precious sockets, we can't afford to 
                                        // leave a socket waiting around doing nothing 
                                        // for a long time.  If the remote node does 
                                        // not recieve this ACK, it'll have to figure 
                                        // out on it's own that the connection is now 
                                        // closed.
                                        SendTCP(ACK, 0);
                                        CloseSocket();
                                        return;

                                default:
                                        break;
                        }

                        // Acknowledge receipt of FIN
                        SendTCP(ACK, SENDTCP_RESET_TIMERS);
                }
        }
}

/****************************************************************************
  Section:
        Buffer Management Functions
  ***************************************************************************/

/*****************************************************************************
  Function:
        BOOL TCPAdjustFIFOSize(TCP_SOCKET hTCP, WORD wMinRXSize, 
                                                        WORD wMinTXSize, BYTE vFlags)

  Summary:
        Adjusts the relative sizes of the RX and TX buffers.

  Description:
        This function can be used to adjust the relative sizes of the RX and
        TX FIFO depending on the immediate needs of an application.  Since a 
        larger FIFO can allow more data to be sent in a given packet, adjusting 
        the relative sizes on the fly can allow for optimal transmission speed 
        for one-sided application protocols.  For example, HTTP typically 
        begins by receiving large amounts of data from the client, then switches
        to serving large amounts of data back.  Adjusting the FIFO at these 
        points can increase performance substantially.  Once the FIFO is
        adjusted, a window update is sent.
        
        If neither or both of TCP_ADJUST_GIVE_REST_TO_TX and 
        TCP_ADJUST_GIVE_REST_TO_RX are set, the function distributes the
        remaining space equally.
        
        Received data can be preserved as long as the buffer is expanding and 
        has not wrapped.

  Precondition:
        TCP is initialized.

  Parameters:
        hTCP            - The socket to be adjusted
        wMinRXSize      - Minimum number of byte for the RX FIFO
        wMinTXSize      - Minimum number of bytes for the RX FIFO
        vFlags          - Any combination of TCP_ADJUST_GIVE_REST_TO_RX, 
                                  TCP_ADJUST_GIVE_REST_TO_TX, TCP_ADJUST_PRESERVE_RX.
                                  TCP_ADJUST_PRESERVE_TX is not currently supported.

  Return Values:
        TRUE - The FIFOs were adjusted successfully
        FALSE - Minimum RX, Minimum TX, or flags couldn't be accommodated and
                        therefore the socket was left unchanged.

  Side Effects:
        Any unacknowledged or untransmitted data in the TX FIFO is always
        deleted.

  Remarks:
        At least one byte must always be allocated to the RX buffer so that
        a FIN can be received.  The function automatically corrects for this.
  ***************************************************************************/
BOOL TCPAdjustFIFOSize(TCP_SOCKET hTCP, WORD wMinRXSize, WORD wMinTXSize, BYTE vFlags)
{
        PTR_BASE ptrTemp, ptrHead;
        WORD wTXAllocation;
        
        // Load up info on this socket
        SyncTCBStub(hTCP);

        // RX has to be at least 1 byte to receive SYN and FIN bytes 
        // from the remote node, even if they aren't stored in the RX FIFO
        if(wMinRXSize == 0u)
                wMinRXSize = 1;
                
        // SSL connections need to be able to send or receive at least 
        // a full Alert record, MAC, and FIN
        #if defined(STACK_USE_SSL)
        if(TCPIsSSL(hTCP) && wMinRXSize < 25u)
                wMinRXSize = 25;
        if(TCPIsSSL(hTCP) && wMinTXSize < 25u)
                wMinTXSize = 25;
        #endif
        
        // Make sure space is available for minimums
        ptrTemp = MyTCBStub.bufferEnd - MyTCBStub.bufferTxStart - 1;
        if(wMinRXSize + wMinTXSize > ptrTemp)
                return FALSE;

        SyncTCB();

        // Set both allocation flags if none set
        if(!(vFlags & (TCP_ADJUST_GIVE_REST_TO_TX | TCP_ADJUST_GIVE_REST_TO_RX)))
                vFlags |= TCP_ADJUST_GIVE_REST_TO_TX | TCP_ADJUST_GIVE_REST_TO_RX;
                

        // Allocate minimums
        wTXAllocation = wMinTXSize;
        ptrTemp -= wMinRXSize + wMinTXSize;

        // Allocate extra
        if(vFlags & TCP_ADJUST_GIVE_REST_TO_TX)
        {
                if(vFlags & TCP_ADJUST_GIVE_REST_TO_RX)
                {
                        // Do a 50%/50% split with any odd byte always going to the RX FIFO
                        wTXAllocation += ptrTemp>>1;
                }
                else
                {
                        wTXAllocation += ptrTemp;
                }
        }

        // Calculate new bufferRxStart pointer
        ptrTemp = MyTCBStub.bufferTxStart + wTXAllocation + 1;

        // Find the head pointer to use
        ptrHead = MyTCBStub.rxHead;
        #if defined(STACK_USE_SSL)
        if(TCPIsSSL(hTCP))
                ptrHead = MyTCBStub.sslRxHead;
        #endif
        
        // If there's out-of-order data pending, adjust the head pointer to compensate
        if(MyTCB.sHoleSize != -1)
        {
                ptrHead += MyTCB.sHoleSize + MyTCB.wFutureDataSize;
                if(ptrHead > MyTCBStub.bufferEnd)
                        ptrHead -= MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;
        }

        // Determine if resizing will lose any RX data
        if(MyTCBStub.rxTail < ptrHead)
        {
                if(ptrTemp > MyTCBStub.rxTail)
                {
                        if(vFlags & TCP_ADJUST_PRESERVE_RX)
                                return FALSE;
                        else
                        {
                                MyTCBStub.rxTail = ptrTemp;
                                MyTCBStub.rxHead = ptrTemp;

                                #if defined(STACK_USE_SSL)
                                MyTCBStub.sslRxHead = ptrTemp;
                                #endif
                        }
                }
        }
        else if(MyTCBStub.rxTail > ptrHead)
        {
                if(ptrTemp > MyTCBStub.bufferRxStart)
                {
                        if(vFlags & TCP_ADJUST_PRESERVE_RX)
                                return FALSE;
                        else
                        {
                                MyTCBStub.rxTail = ptrTemp;
                                MyTCBStub.rxHead = ptrTemp;
                                
                                #if defined(STACK_USE_SSL)
                                MyTCBStub.sslRxHead = ptrTemp;
                                #endif
                        }
                }
        }
        else
        {
                // No data to preserve, but we may need to move 
                // the pointers to stay in the RX space
                MyTCBStub.rxTail = ptrTemp;
                MyTCBStub.rxHead = ptrTemp;
                
                #if defined(STACK_USE_SSL)
                MyTCBStub.sslRxHead = ptrTemp;
                #endif
        }
        
        // If we need to preserve data that wrapped in the ring, we must copy
        if(ptrHead < MyTCBStub.rxTail && (vFlags & TCP_ADJUST_PRESERVE_RX))
        {
                TCPRAMCopy(ptrTemp, MyTCBStub.vMemoryMedium, 
                        MyTCBStub.bufferRxStart, MyTCBStub.vMemoryMedium,
                        ptrHead - MyTCBStub.bufferRxStart);

                // Move the pointers if they were in front of the tail
                #if defined(STACK_USE_SSL)
                if(TCPIsSSL(hTCP) && MyTCBStub.sslRxHead < MyTCBStub.rxTail)
                        MyTCBStub.sslRxHead -= MyTCBStub.bufferRxStart - ptrTemp;
                #endif
                if(MyTCBStub.rxHead < MyTCBStub.rxTail)
                        MyTCBStub.rxHead -= MyTCBStub.bufferRxStart - ptrTemp;
        }
        
        // Move the RX buffer pointer - it's the one that divides the two
        MyTCBStub.bufferRxStart = ptrTemp;

        // Empty the TX buffer
        MyTCB.txUnackedTail = MyTCBStub.bufferTxStart;
        MyTCBStub.txTail = MyTCBStub.bufferTxStart;
        MyTCBStub.txHead = MyTCBStub.bufferTxStart;
        
        #if defined(STACK_USE_SSL)
        if(TCPIsSSL(hTCP))
                MyTCBStub.sslTxHead = MyTCBStub.txHead + 5;
        #endif
        
        // Send a window update to notify remote node of change
        if(MyTCBStub.smState == TCP_ESTABLISHED)
                SendTCP(ACK, SENDTCP_RESET_TIMERS);

        return TRUE;

}

/*****************************************************************************
  Function:
        static void TCPRAMCopy(PTR_BASE ptrDest, BYTE vDestType, PTR_BASE ptrSource, 
                                                        BYTE vSourceType, WORD wLength)

  Summary:
        Copies data to/from various memory mediums.

  Description:
        This function copies data between memory mediums (PIC RAM, SPI
        RAM, and Ethernet buffer RAM).

  Precondition:
        TCP is initialized.

  Parameters:
        ptrDest         - Address to write to
        vDestType       - Destination meidum (TCP_PIC_RAM, TCP_ETH_RAM, TCP_SPI_RAM)
        ptrSource       - Address to copy from
        vSourceType - Source medium (TCP_PIC_RAM, TCP_ETH_RAM, or TCP_SPI_RAM)
        wLength         - Number of bytes to copy

  Returns:
        None

  Remarks:
        Copying to a destination region that overlaps with the source address 
        is supported only if the destination start address is at a lower memory 
        address (closer to 0x0000) than the source pointer.  However, if they do 
        overlap there must be at least 4 bytes of non-overlap to ensure correct 
        results due to hardware DMA requirements.
  ***************************************************************************/
static void TCPRAMCopy(PTR_BASE ptrDest, BYTE vDestType, PTR_BASE ptrSource, BYTE vSourceType, WORD wLength)
{
        #if defined(SPIRAM_CS_TRIS)
        BYTE vBuffer[16];
        WORD w;
        #endif
                
        switch(vSourceType)
        {
                case TCP_PIC_RAM:
                        switch(vDestType)
                        {
                                case TCP_PIC_RAM:
                                        memcpy((void*)(BYTE*)ptrDest, (void*)(BYTE*)ptrSource, wLength);
                                        break;
        
                                case TCP_ETH_RAM:
                                        if(ptrDest!=(PTR_BASE)-1)
                                                MACSetWritePtr(ptrDest);
                                        MACPutArray((BYTE*)ptrSource, wLength);
                                        break;
        
                                #if defined(SPIRAM_CS_TRIS)
                                case TCP_SPI_RAM:
                                        SPIRAMPutArray(ptrDest, (BYTE*)ptrSource, wLength);
                                        break;
                                #endif
                        }
                        break;
        
                case TCP_ETH_RAM:
                        switch(vDestType)
                        {
                                case TCP_PIC_RAM:
                                        if(ptrSource!=(PTR_BASE)-1)
                                                MACSetReadPtr(ptrSource);
                                        MACGetArray((BYTE*)ptrDest, wLength);
                                        break;
        
                                case TCP_ETH_RAM:
                                        MACMemCopyAsync(ptrDest, ptrSource, wLength);
                                        while(!MACIsMemCopyDone());
                                        break;
        
                                #if defined(SPIRAM_CS_TRIS)
                                case TCP_SPI_RAM:
                                        if(ptrSource!=(PTR_BASE)-1)
                                                MACSetReadPtr(ptrSource);
                                        w = sizeof(vBuffer);
                                        while(wLength)
                                        {
                                                if(w > wLength)
                                                        w = wLength;
                                                
                                                // Read and write a chunk       
                                                MACGetArray(vBuffer, w);
                                                SPIRAMPutArray(ptrDest, vBuffer, w);
                                                ptrDest += w;
                                                wLength -= w;
                                        }
                                        break;
                                #endif
                        }
                        break;
        
                #if defined(SPIRAM_CS_TRIS)
                case TCP_SPI_RAM:
                        switch(vDestType)
                        {
                                case TCP_PIC_RAM:
                                        SPIRAMGetArray(ptrSource, (BYTE*)ptrDest, wLength);
                                        break;
        
                                case TCP_ETH_RAM:
                                        if(ptrDest!=(PTR_BASE)-1)
                                                MACSetWritePtr(ptrDest);
                                        w = sizeof(vBuffer);
                                        while(wLength)
                                        {
                                                if(w > wLength)
                                                        w = wLength;
                                                
                                                // Read and write a chunk       
                                                SPIRAMGetArray(ptrSource, vBuffer, w);
                                                ptrSource += w;
                                                MACPutArray(vBuffer, w);
                                                wLength -= w;
                                        }
                                        break;
        
                                case TCP_SPI_RAM:
                                        // Copy all of the data over in chunks
                                        w = sizeof(vBuffer);
                                        while(wLength)
                                        {
                                                if(w > wLength)
                                                        w = wLength;
                                                        
                                                SPIRAMGetArray(ptrSource, vBuffer, w);
                                                SPIRAMPutArray(ptrDest, vBuffer, w);
                                                ptrSource += w;
                                                ptrDest += w;
                                                wLength -= w;
                                        }
                                        break;
                        }
                        break;
                #endif                  
        }
}

/*****************************************************************************
  Function:
        static void TCPRAMCopyROM(PTR_BASE wDest, BYTE wDestType, ROM BYTE* wSource, 
                                                                WORD wLength)

  Summary:
        Copies data to/from various memory mediums.

  Description:
        This function copies data between memory mediums (PIC RAM, SPI
        RAM, and Ethernet buffer RAM).  This function is to be used when 
        copying from ROM.

  Precondition:
        TCP is initialized.

  Parameters:
        wDest           - Address to write to
        wDestType       - Destination meidum (TCP_PIC_RAM, TCP_ETH_RAM, TCP_SPI_RAM)
        wSource         - Address to copy from
        wLength         - Number of bytes to copy

  Returns:
        None

  Remarks:
        Copying to a destination region that overlaps with the source address 
        is supported only if the destination start address is at a lower memory 
        address (closer to 0x0000) than the source pointer.
        
        This function is aliased to TCPRAMCopy on non-PIC18 platforms.
  ***************************************************************************/
#if defined(__18CXX)
static void TCPRAMCopyROM(PTR_BASE wDest, BYTE wDestType, ROM BYTE* wSource, WORD wLength)
{
        BYTE vBuffer[16];
        WORD w;
        
        switch(wDestType)
        {
                case TCP_PIC_RAM:
                        memcpypgm2ram((void*)(BYTE*)wDest, (ROM void*)wSource, wLength);
                        break;
        
                case TCP_ETH_RAM:
                        if(wDest!=(PTR_BASE)-1)
                                MACSetWritePtr(wDest);
                        w = sizeof(vBuffer);
                        while(wLength)
                        {
                                if(w > wLength)
                                        w = wLength;
                                
                                // Read and write a chunk       
                                memcpypgm2ram(vBuffer, (ROM void*)wSource, w);
                                MACPutArray(vBuffer, w);
                                wSource += w;
                                wLength -= w;
                        }
                        break;
        
                #if defined(SPIRAM_CS_TRIS)
                case TCP_SPI_RAM:
                        w = sizeof(vBuffer);
                        while(wLength)
                        {
                                if(w > wLength)
                                        w = wLength;
                                
                                // Read and write a chunk       
                                memcpypgm2ram(vBuffer, (ROM void*)wSource, w);
                                SPIRAMPutArray(wDest, vBuffer, w);
                                wDest += w;
                                wSource += w;
                                wLength -= w;
                        }
                        break;
                #endif
        }
}
#endif

/****************************************************************************
  Section:
        SSL Functions
  ***************************************************************************/

/*****************************************************************************
  Function:
        BOOL TCPStartSSLClient(TCP_SOCKET hTCP, BYTE* host)

  Summary:
        Begins an SSL client session.

  Description:
        This function escalates the current connection to an SSL secured 
        connection by initiating an SSL client handshake.

  Precondition:
        TCP is initialized and hTCP is already connected.

  Parameters:
        hTCP            - TCP connection to secure
        host            - Expected host name on certificate (currently ignored)

  Return Values:
        TRUE            - an SSL connection was initiated
        FALSE           - Insufficient SSL resources (stubs) were available

  Remarks:
        The host parameter is currently ignored and is not validated.
  ***************************************************************************/
#if defined(STACK_USE_SSL_CLIENT)
BOOL TCPStartSSLClient(TCP_SOCKET hTCP, BYTE* host)
{
        BYTE i;
        
        SyncTCBStub(hTCP);
        
        // Make sure SSL is not established already
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                return FALSE;
        
        // Try to start the session
        MyTCBStub.sslStubID = SSLStartSession(hTCP, NULL, 0);
        
        // Make sure a session stub was obtained
        if(MyTCBStub.sslStubID == SSL_INVALID_ID)
                return FALSE;

        // Mark connection as handshaking and return
        MyTCBStub.sslReqMessage = SSL_CLIENT_HELLO;
        MyTCBStub.sslRxHead = MyTCBStub.rxHead;
        MyTCBStub.sslTxHead = MyTCBStub.txHead;
        MyTCBStub.Flags.bSSLHandshaking = 1;
        for(i = 0; i < 5u; i++)
        {// Skip first 5 bytes in TX for the record header
                if(++MyTCBStub.sslTxHead >= MyTCBStub.bufferRxStart)
                        MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
        }
        return TRUE;
}
#endif // SSL Client

/*****************************************************************************
  Function:
        BOOL TCPStartSSLClientEx(TCP_SOCKET hTCP, BYTE* host, BYTE * buffer, BYTE suppDataType)

  Summary:
        Begins an SSL client session.

  Description:
        This function escalates the current connection to an SSL secured 
        connection by initiating an SSL client handshake.

  Precondition:
        TCP is initialized and hTCP is already connected.

  Parameters:
        hTCP                    - TCP connection to secure
        host                    - Expected host name on certificate (currently ignored)
        buffer          - Buffer for supplementary data return
        suppDataType    - Type of supplementary data to copy

  Return Values:
        TRUE            - an SSL connection was initiated
        FALSE           - Insufficient SSL resources (stubs) were available

  Remarks:
        The host parameter is currently ignored and is not validated.
  ***************************************************************************/
#if defined(STACK_USE_SSL_CLIENT)
BOOL TCPStartSSLClientEx(TCP_SOCKET hTCP, BYTE* host, void * buffer, BYTE suppDataType)
{
        BYTE i;
        
        SyncTCBStub(hTCP);
        
        // Make sure SSL is not established already
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                return FALSE;
        
        // Try to start the session
        MyTCBStub.sslStubID = SSLStartSession(hTCP, buffer, suppDataType);
        
        // Make sure a session stub was obtained
        if(MyTCBStub.sslStubID == SSL_INVALID_ID)
                return FALSE;

        // Mark connection as handshaking and return
        MyTCBStub.sslReqMessage = SSL_CLIENT_HELLO;
        MyTCBStub.sslRxHead = MyTCBStub.rxHead;
        MyTCBStub.sslTxHead = MyTCBStub.txHead;
        MyTCBStub.Flags.bSSLHandshaking = 1;
        for(i = 0; i < 5u; i++)
        {// Skip first 5 bytes in TX for the record header
                if(++MyTCBStub.sslTxHead >= MyTCBStub.bufferRxStart)
                        MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
        }
        return TRUE;
}
#endif // SSL Client

/*****************************************************************************
  Function:
        BOOL TCPStartSSLServer(TCP_SOCKET hTCP)

  Summary:
        Begins an SSL server session.

  Description:
        This function sets up an SSL server session when a new connection is
        established on an SSL port.

  Precondition:
        TCP is initialized and hTCP is already connected.

  Parameters:
        hTCP            - TCP connection to secure

  Return Values:
        TRUE            - an SSL connection was initiated
        FALSE           - Insufficient SSL resources (stubs) were available
  ***************************************************************************/
#if defined(STACK_USE_SSL_SERVER)
BOOL TCPStartSSLServer(TCP_SOCKET hTCP)
{
        BYTE i;
        
        SyncTCBStub(hTCP);
        SyncTCB();
        
        // Make sure SSL is not established already
        if(MyTCBStub.sslStubID != SSL_INVALID_ID)
                return TRUE;
        
        // Try to start the session
        MyTCBStub.sslStubID = SSLStartSession(hTCP, NULL, 0);
        
        // Make sure a session stub was obtained
        if(MyTCBStub.sslStubID == SSL_INVALID_ID)
                return FALSE;

        // Swap the localPort and localSSLPort
        MyTCBStub.remoteHash.Val = MyTCB.localPort.Val;
        MyTCB.localPort.Val = MyTCB.localSSLPort.Val;
        MyTCB.localSSLPort.Val = MyTCBStub.remoteHash.Val;      

        // Mark connection as handshaking and return
        MyTCBStub.sslReqMessage = SSL_NO_MESSAGE;
        MyTCBStub.sslRxHead = MyTCBStub.rxHead;
        MyTCBStub.sslTxHead = MyTCBStub.txHead;
        MyTCBStub.Flags.bSSLHandshaking = 1;
        for(i = 0; i < 5u; i++)
        {// Skip first 5 bytes in TX for the record header
                if(++MyTCBStub.sslTxHead >= MyTCBStub.bufferRxStart)
                        MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
        }
        return TRUE;
}
#endif // SSL Client

/*****************************************************************************
  Function:
        BOOL TCPAddSSLListener(TCP_SOCKET hTCP, WORD port)

  Summary:
        Listens for SSL connection on a specific port.

  Description:
        This function adds an additional listening port to a TCP connection.  
        Connections made on this alternate port will be secured via SSL.

  Precondition:
        TCP is initialized and hTCP is listening.

  Parameters:
        hTCP            - TCP connection to secure
        port            - SSL port to listen on

  Return Values:
        TRUE            - SSL port was added.
        FALSE           - The socket was not a listening socket.
  ***************************************************************************/
#if defined(STACK_USE_SSL_SERVER)
BOOL TCPAddSSLListener(TCP_SOCKET hTCP, WORD port)
{
        SyncTCBStub(hTCP);
        
        if(MyTCBStub.smState != TCP_LISTEN)
                return FALSE;
        
        SyncTCB();
        
        MyTCB.localSSLPort.Val = port;
        MyTCBStub.sslTxHead = port;

        return TRUE;
}
#endif // SSL Server

/*****************************************************************************
  Function:
        BOOL TCPRequestSSLMessage(TCP_SOCKET hTCP, BYTE msg)

  Summary:
        Requests an SSL message to be transmitted.

  Description:
        This function is called to request that a specific SSL message be
        transmitted.  This message should only be called by the SSL module.
        
  Precondition:
        TCP is initialized.

  Parameters:
        hTCP            - TCP connection to use
        msg                     - One of the SSL_MESSAGE types to transmit.

  Return Values:
        TRUE            - The message was requested.
        FALSE           - Another message is already pending transmission.
  ***************************************************************************/
#if defined(STACK_USE_SSL)
BOOL TCPRequestSSLMessage(TCP_SOCKET hTCP, BYTE msg)
{
        SyncTCBStub(hTCP);
        
        if(msg == SSL_NO_MESSAGE || MyTCBStub.sslReqMessage == SSL_NO_MESSAGE)
        {
                MyTCBStub.sslReqMessage = msg;
                return TRUE;
        }
        
        return FALSE;
}
#endif // SSL

/*****************************************************************************
  Function:
        BOOL TCPSSLIsHandshaking(TCP_SOCKET hTCP)

  Summary:
        Determines if an SSL session is still handshaking.

  Description:
        Call this function after calling TCPStartSSLClient until FALSE is
        returned.  Then your application may continue with its normal data
        transfer (which is now secured).
        
  Precondition:
        TCP is initialized and hTCP is connected.

  Parameters:
        hTCP            - TCP connection to check

  Return Values:
        TRUE            - SSL handshake is still progressing
        FALSE           - SSL handshake has completed
  ***************************************************************************/
#if defined(STACK_USE_SSL)
BOOL TCPSSLIsHandshaking(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
        return MyTCBStub.Flags.bSSLHandshaking; 
}
#endif // SSL

/*****************************************************************************
  Function:
        BOOL TCPIsSSL(TCP_SOCKET hTCP)

  Summary:
        Determines if a TCP connection is secured with SSL.

  Description:
        Call this function to determine whether or not a TCP connection is 
        secured with SSL.
        
  Precondition:
        TCP is initialized and hTCP is connected.

  Parameters:
        hTCP            - TCP connection to check

  Return Values:
        TRUE            - Connection is secured via SSL
        FALSE           - Connection is not secured
  ***************************************************************************/
#if defined(STACK_USE_SSL)
BOOL TCPIsSSL(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
        
        if(MyTCBStub.sslStubID == SSL_INVALID_ID)
                return FALSE;
        
        return TRUE;
}
#endif // SSL

/*****************************************************************************
  Function:
        void TCPSSLHandshakeComplete(TCP_SOCKET hTCP)

  Summary:
        Clears the SSL handshake flag.

  Description:
        This function clears the flag indicating that an SSL handshake is
        complete.
        
  Precondition:
        TCP is initialized and hTCP is connected.

  Parameters:
        hTCP            - TCP connection to set

  Returns:
        None

  Remarks:
        This function should never be called by an application.  It is used 
        only by the SSL module itself.
  ***************************************************************************/
#if defined(STACK_USE_SSL)
void TCPSSLHandshakeComplete(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);
        MyTCBStub.Flags.bSSLHandshaking = 0;
}
#endif // SSL

/*****************************************************************************
  Function:
        void TCPSSLDecryptMAC(TCP_SOCKET hTCP, ARCFOUR_CTX* ctx, WORD len)

  Summary:
        Decrypts and MACs data arriving via SSL.

  Description:
        This function decrypts data in the TCP buffer and calculates the MAC over
        the data.  All data is left in the exact same location in the TCP buffer.
        It is called to help process incoming SSL records.
        
  Precondition:
        TCP is initialized, hTCP is connected, and ctx's Sbox is loaded.

  Parameters:
        hTCP            - TCP connection to decrypt in
        ctx                     - ARCFOUR encryption context to use
        len             - Number of bytes to crypt
        inPlace         - TRUE to write back in place, FALSE to write at end of
                                        currently visible data.

  Returns:
        None

  Remarks:
        This function should never be called by an application.  It is used 
        only by the SSL module itself.
  ***************************************************************************/
#if defined(STACK_USE_SSL)
void TCPSSLDecryptMAC(TCP_SOCKET hTCP, ARCFOUR_CTX* ctx, WORD len)
{
        PTR_BASE wSrc, wDest, wBlockLen, wTemp;
        BYTE buffer[32];
        
        // Set up the pointers
        SyncTCBStub(hTCP);
        wSrc = MyTCBStub.rxTail;
        wDest = wSrc;
        
        // Handle 32 bytes at a time
        while(len)
        {
                // Determine how many bytes we can read
                wBlockLen = sizeof(buffer);
                if(wBlockLen > len) // Don't do more than we should
                        wBlockLen = len;
                
                // Read those bytes to a buffer
                if(wSrc + wBlockLen > MyTCBStub.bufferEnd)
                {// Two part read
                        wTemp = MyTCBStub.bufferEnd - wSrc + 1;
                        TCPRAMCopy((PTR_BASE)buffer, TCP_PIC_RAM, wSrc, MyTCBStub.vMemoryMedium, wTemp);
                        TCPRAMCopy((PTR_BASE)buffer+wTemp, TCP_PIC_RAM, MyTCBStub.bufferRxStart, MyTCBStub.vMemoryMedium, wBlockLen - wTemp);
                        wSrc = MyTCBStub.bufferRxStart + wBlockLen - wTemp;
                }
                else
                {
                        TCPRAMCopy((PTR_BASE)buffer, TCP_PIC_RAM, wSrc, MyTCBStub.vMemoryMedium, wBlockLen);
                        wSrc += wBlockLen;
                }
                
                // Decrypt and hash
                ARCFOURCrypt(ctx, buffer, wBlockLen);
                SSLMACAdd(buffer, wBlockLen);
                
                // Write decrypted bytes back
                if(wDest + wBlockLen > MyTCBStub.bufferEnd)
                {// Two part write
                        wTemp = MyTCBStub.bufferEnd - wDest + 1;
                        TCPRAMCopy(wDest, MyTCBStub.vMemoryMedium, (PTR_BASE)buffer, TCP_PIC_RAM, wTemp);
                        TCPRAMCopy(MyTCBStub.bufferRxStart, MyTCBStub.vMemoryMedium, (PTR_BASE)buffer+wTemp, TCP_PIC_RAM, wBlockLen - wTemp);
                        wDest = MyTCBStub.bufferRxStart + wBlockLen - wTemp;
                }
                else
                {
                        TCPRAMCopy(wDest, MyTCBStub.vMemoryMedium, (PTR_BASE)buffer, TCP_PIC_RAM, wBlockLen);
                        wDest += wBlockLen;
                }
                
                // Update the length remaining
                len -= wBlockLen;
        }
}       
#endif // SSL

/*****************************************************************************
  Function:
        void TCPSSLInPlaceMACEncrypt(TCP_SOCKET hTCP, ARCFOUR_CTX* ctx, 
                                                                        BYTE* MACSecret, WORD len)

  Summary:
        Encrypts and MACs data in place in the TCP TX buffer.

  Description:
        This function encrypts data in the TCP buffer while calcuating a MAC.  
        When encryption is finished, the MAC is appended to the buffer and 
        the record will be ready to transmit.
        
  Precondition:
        TCP is initialized, hTCP is connected, and ctx's Sbox is loaded.

  Parameters:
        hTCP            - TCP connection to encrypt in
        ctx                     - ARCFOUR encryption context to use
        MACSecret       - MAC encryption secret to use
        len             - Number of bytes to crypt

  Returns:
        None

  Remarks:
        This function should never be called by an application.  It is used 
        only by the SSL module itself.
  ***************************************************************************/
#if defined(STACK_USE_SSL)
void TCPSSLInPlaceMACEncrypt(TCP_SOCKET hTCP, ARCFOUR_CTX* ctx, BYTE* MACSecret, WORD len)
{
        PTR_BASE pos;
        WORD blockLen;
        BYTE buffer[32];
        
        // Set up the pointers
        SyncTCBStub(hTCP);
        pos = MyTCBStub.txHead;
        for(blockLen = 0; blockLen < 5u; blockLen++)
        {// Skips first 5 bytes for the header
                if(++pos >= MyTCBStub.bufferRxStart)
                        pos = MyTCBStub.bufferTxStart;
        }
        
        // Handle 32 bytes at a time
        while(len)
        {
                // Determine how many bytes we can read
                blockLen = sizeof(buffer);
                if(blockLen > len) // Don't do more than we should
                        blockLen = len;
                if(blockLen > MyTCBStub.bufferRxStart - pos) // Don't pass the end
                        blockLen = MyTCBStub.bufferRxStart - pos;
                
                // Read those bytes to a buffer
                TCPRAMCopy((PTR_BASE)buffer, TCP_PIC_RAM, pos, MyTCBStub.vMemoryMedium, blockLen);
                
                // Hash and encrypt
                SSLMACAdd(buffer, blockLen);
                ARCFOURCrypt(ctx, buffer, blockLen);
                
                // Put them back
                TCPRAMCopy(pos, MyTCBStub.vMemoryMedium, (PTR_BASE)buffer, TCP_PIC_RAM, blockLen);
                
                // Update the pointers
                pos += blockLen;
                len -= blockLen;
                if(pos >= MyTCBStub.bufferRxStart)
                        pos = MyTCBStub.bufferTxStart;
        }
        
        // Calculate and add the MAC
        SSLMACCalc(MACSecret, buffer);
        ARCFOURCrypt(ctx, buffer, 16);

        // Write the MAC to the TX FIFO
        // Can't use TCPPutArray here because TCPIsPutReady() saves 16 bytes for the MAC
        // TCPPut* functions use this to prevent writing too much data.  Therefore, the
        // functionality is duplicated here.
        
        len = 16;
        blockLen = 0;
        // See if we need a two part put
        if(MyTCBStub.sslTxHead + len >= MyTCBStub.bufferRxStart)
        {
                blockLen = MyTCBStub.bufferRxStart-MyTCBStub.sslTxHead;
                TCPRAMCopy(MyTCBStub.sslTxHead, MyTCBStub.vMemoryMedium, (PTR_BASE)buffer, TCP_PIC_RAM, blockLen);
                len -= blockLen;
                MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
        }
        
        TCPRAMCopy(MyTCBStub.sslTxHead, MyTCBStub.vMemoryMedium, (PTR_BASE)&buffer[blockLen], TCP_PIC_RAM, len);
        MyTCBStub.sslTxHead += len;

}       
#endif // SSL

/*****************************************************************************
  Function:
        void TCPSSLPutRecordHeader(TCP_SOCKET hTCP, BYTE* hdr, BOOL recDone)

  Summary:
        Writes an SSL record header and sends an SSL record.

  Description:
        This function writes an SSL record header to the pending TCP SSL data, 
        then indicates that the data is ready to be sent by moving the txHead
        pointer.
        
        If the record is complete, set recDone to TRUE.  The sslTxHead 
        pointer will be moved forward 5 bytes to leave space for a future 
        record header.  If the record is only partially sent, use FALSE and
        to leave the pointer where it is so that more data can be added
        to the record.  Partial records can only be used for the 
        SERVER_CERTIFICATE handshake message.
        
  Precondition:
        TCP is initialized, and hTCP is connected with an active SSL session.

  Parameters:
        hTCP            - TCP connection to write the header and transmit with
        hdr                     - Record header (5 bytes) to send or NULL to just 
                                  move the pointerctx
        recDone         - TRUE if the record is done, FALSE otherwise

  Returns:
        None

  Remarks:
        This function should never be called by an application.  It is used 
        only by the SSL module itself.
  ***************************************************************************/
#if defined(STACK_USE_SSL)
void TCPSSLPutRecordHeader(TCP_SOCKET hTCP, BYTE* hdr, BOOL recDone)
{
        BYTE i;
        
        // Set up the pointers
        SyncTCBStub(hTCP);
        
        // Write the header if needed
        if(hdr)
        {// This is a new record, so insert the header
                for(i = 0; i < 5u; i++)
                {
                        TCPRAMCopy(MyTCBStub.txHead, MyTCBStub.vMemoryMedium, (PTR_BASE)hdr+i, TCP_PIC_RAM, sizeof(BYTE));
                        if(++MyTCBStub.txHead >= MyTCBStub.bufferRxStart)
                                MyTCBStub.txHead = MyTCBStub.bufferTxStart;
                }
        }
        
        // Move the txHead pointer to indicate what data is ready
        // Also, flush just the header, then all the data.  This shotguns two 
        // packets down the line, therefore causing immediate ACKs by the 
        // remote node.  Reconnect handshakes are as much as 60% faster now.
        TCPFlush(hTCP);
        MyTCBStub.txHead = MyTCBStub.sslTxHead;
        TCPFlush(hTCP);
        
        // If this record is done, move the sslTxHead forward
        // to accomodate the next record header
        if(recDone)
        {
                for(i = 0; i < 5u; i++)
                {// Skip first 5 bytes in TX for the record header
                        if(++MyTCBStub.sslTxHead >= MyTCBStub.bufferRxStart)
                                MyTCBStub.sslTxHead = MyTCBStub.bufferTxStart;
                }
        }
}       
#endif // SSL

/*****************************************************************************
  Function:
        WORD TCPSSLGetPendingTxSize(TCP_SOCKET hTCP)

  Summary:
        Determines how many bytes are pending for a future SSL record.

  Description:
        This function determines how many bytes are pending for a future SSL
        record.
        
  Precondition:
        TCP is initialized, and hTCP is connected with an active SSL connection.

  Parameters:
        hTCP            - TCP connection to check

  Returns:
        None
  ***************************************************************************/
#if defined(STACK_USE_SSL)
WORD TCPSSLGetPendingTxSize(TCP_SOCKET hTCP)
{
        SyncTCBStub(hTCP);

        // Non-SSL connections have no pending SSL data
        //if(MyTCBStub.sslStubID == SSL_INVALID_ID)
        //      return 0;
                        
        // Determine how many bytes are waiting to be written in this record
        if(MyTCBStub.sslTxHead > MyTCBStub.txHead)
                return MyTCBStub.sslTxHead - MyTCBStub.txHead - 5;
        else
                return (MyTCBStub.bufferRxStart - MyTCBStub.bufferTxStart - 1) - (MyTCBStub.txHead - MyTCBStub.sslTxHead - 1) - 5;
}
#endif


/*****************************************************************************
  Function:
        void TCPSSLHandleIncoming(TCP_SOCKET hTCP)

  Summary:
        Hands newly arrive TCP data to the SSL module for processing.

  Description:
        This function processes incoming TCP data as an SSL record and 
        performs any necessary repositioning and decrypting.
        
  Precondition:
        TCP is initialized, and hTCP is connected with an active SSL session.

  Parameters:
        hTCP            - TCP connection to handle incoming data on

  Returns:
        None

  Remarks:
        This function should never be called by an application.  It is used 
        only by the SSL module itself.
  ***************************************************************************/
#if defined(STACK_USE_SSL)
void TCPSSLHandleIncoming(TCP_SOCKET hTCP)
{
        PTR_BASE prevRxTail, nextRxHead, startRxTail, wSrc, wDest;
        WORD wToMove, wLen, wSSLBytesThatPoofed, wDecryptedBytes;
        
        // Sync the stub
        SyncTCBStub(hTCP);

        // If new data is waiting
        if(MyTCBStub.sslRxHead != MyTCBStub.rxHead)
        {
                // Reconfigure pointers for SSL use
                prevRxTail = MyTCBStub.rxTail;
                nextRxHead = MyTCBStub.rxHead;
                MyTCBStub.rxTail = MyTCBStub.rxHead;
                MyTCBStub.rxHead = MyTCBStub.sslRxHead;
                
                do
                {
                        startRxTail = MyTCBStub.rxTail;

                        // Handle incoming data.  This function performs deframing of the 
                        // SSL records, decryption, and MAC verification.
                        wSSLBytesThatPoofed = TCPIsGetReady(hTCP);
                        wDecryptedBytes = SSLRxRecord(hTCP, MyTCBStub.sslStubID);
                        wSSLBytesThatPoofed -= TCPIsGetReady(hTCP);

                        // Now need to move data to fill the SSL header/MAC/padding hole, 
                        // if there is one
                        if(wSSLBytesThatPoofed)
                        {       
                                // Sync the TCP so we can see if there is a TCP hole
                                SyncTCB();

                                // Calculate how big the SSL hole is
                                if(MyTCB.sHoleSize == -1)
                                {// Just need to move pending SSL data
                                        wToMove = TCPIsGetReady(hTCP);
                                }
                                else
                                {// A TCP hole exists, so move all data
                                        wToMove = TCPIsGetReady(hTCP) + MyTCB.sHoleSize + MyTCB.wFutureDataSize;
                                }
                                
                                // Start with the destination as the startRxTail and source as current rxTail
                                wDest = startRxTail;
                                wSrc = MyTCBStub.rxTail;
                                
                                // If data exists between the end of the buffer and 
                                // the destination, then move it forward
                                if(wSrc > wDest)
                                {
                                        wLen = MyTCBStub.bufferEnd - wSrc + 1;
                                        if(wLen > wToMove)
                                                wLen = wToMove;
                                        TCPRAMCopy(wDest, MyTCBStub.vMemoryMedium, 
                                                           wSrc, MyTCBStub.vMemoryMedium, wLen);
                                        wDest += wLen;
                                        wSrc = MyTCBStub.bufferRxStart;
                                        wToMove -= wLen;
                                }
                                
                                // If data remains to be moved, fill in to end of buffer
                                if(wToMove)
                                {
                                        wLen = MyTCBStub.bufferEnd - wDest + 1;
                                        if(wLen > wToMove)
                                                wLen = wToMove;
                                        TCPRAMCopy(wDest, MyTCBStub.vMemoryMedium, 
                                                           wSrc, MyTCBStub.vMemoryMedium, wLen);
                                        wDest = MyTCBStub.bufferRxStart;
                                        wSrc += wLen;
                                        wToMove -= wLen;
                                }
                                
                                // If data still remains, copy from from front + len to front
                                if(wToMove)
                                {
                                        TCPRAMCopy(wDest, MyTCBStub.vMemoryMedium,
                                                           wSrc, MyTCBStub.vMemoryMedium, wToMove);
                                }

                                // Since bytes poofed, we need to move the head pointers 
                                // backwards by an equal amount.
                                MyTCBStub.rxHead -= wSSLBytesThatPoofed;
                                if(MyTCBStub.rxHead < MyTCBStub.bufferRxStart)
                                        MyTCBStub.rxHead += MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;
                                MyTCBStub.sslRxHead = MyTCBStub.rxHead;
                        }
                                
                        // Move tail pointer forward by the number of decrypted bytes ready 
                        // for the application (but not poofed bytes)
                        MyTCBStub.rxTail = startRxTail + wDecryptedBytes;
                        if(MyTCBStub.rxTail > MyTCBStub.bufferEnd)
                                MyTCBStub.rxTail -= MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;
                        nextRxHead += wDecryptedBytes;
                        
                        // Loop until SSLRxRecord() runs out of data and stops doing 
                        // anything
                } while(wSSLBytesThatPoofed || (startRxTail != MyTCBStub.rxTail));

                // Restore TCP buffer pointers to point to the decrypted application data 
                // only
                if(nextRxHead > MyTCBStub.bufferEnd)
                        nextRxHead -= MyTCBStub.bufferEnd - MyTCBStub.bufferRxStart + 1;
                MyTCBStub.rxTail = prevRxTail;
                MyTCBStub.rxHead = nextRxHead;
        }
}       
#endif


#endif //#if defined(STACK_USE_TCP)
{FILE END}
{FOOTER START}

Powered by WebSVN v2.8.3