/*********************************************************************
 *
 *	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 ZG2100.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 (TX and RX)
#define TCP_MAX_SEG_SIZE			(536u)

// TCP Timeout and retransmit numbers
#define TCP_START_TIMEOUT_VAL   	((TICK)TICK_SECOND*1)	// Timeout to retransmit unacked data
#define TCP_DELAYED_ACK_TIMEOUT		((TICK)TICK_SECOND/10)	// Timeout for delayed-acknowledgement algorithm
#define TCP_FIN_WAIT_2_TIMEOUT		((TICK)TICK_SECOND*5)	// Timeout for FIN WAIT 2 state
#define TCP_KEEP_ALIVE_TIMEOUT		((TICK)TICK_SECOND*10)	// Timeout for keep-alive messages when no traffic is sent
#define TCP_CLOSE_WAIT_TIMEOUT		((TICK)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 tranmitting unflushed data

#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		((TICK)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 _TCP_HEADER
{
	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 _TCP_OPTIONS
{
	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
    host name string to query in DNS, and IP address as a string, an IP
    address in binary form, or a previously resolved NODE_INFO structure.
    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 types in TCPIPConfig.h or the
    TCPIPConfig utility.
    
    Sockets are statically allocated on boot, but can be claimed with this
    \function and freed using TCPDisconnect (for client sockets). Server
    sockets are opened permanently for the duration of the application.
    Calls to TCPDisconnect will only release the socket to the listening
    state.
    
  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. This parameter is
                       ignored for server sockets.
    vRemoteHostType -  TCP_OPEN_SERVER, TCP_OPEN_RAM_HOST,
                       TCP_OPEN_ROM_HOST, TCP_OPEN_IP_ADDRESS, or
                       TCP_OPEN_NODE_INFO.
    wPort -            For client sockets, the remote TCP port to which a
                       connection should be made. For server sockets, 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.

  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.
    
  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)&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;

		// 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 TCP_MAX_SEG_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 TCP_MAX_SEG_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_AUTO_TRANSMIT_TIMEOUT_VAL/256ull;
	}


	return TRUE;
}


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

  Description:
	Reads an array from a TCP socket.

  Precondition:
	TCP is initialized.

  Parameters:
	hTCP - The socket from which data is to be read.
	data - 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
	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_AUTO_TRANSMIT_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 > TCP_MAX_SEG_SIZE)
			{
				len = TCP_MAX_SEG_SIZE;
				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 != TCP_MAX_SEG_SIZE))
					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 > TCP_MAX_SEG_SIZE)
			{
				len = TCP_MAX_SEG_SIZE;
				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 != TCP_MAX_SEG_SIZE))
					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-4)&0x00FF)<<8) | (((TCP_MAX_SEG_SIZE-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 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;

				// 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;

				if(localHeaderFlags & ACK)
				{
					SendTCP(ACK, SENDTCP_RESET_TIMERS);
					MyTCBStub.smState = TCP_ESTABLISHED;
					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)
				{
					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(!((WORD_VAL*)&ptrDest)->bits.b15)
						MACSetWritePtr((WORD)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(!((WORD_VAL*)&ptrSource)->bits.b15)
						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(!((WORD_VAL*)&ptrSource)->bits.b15)
						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(!((WORD_VAL*)&ptrDest)->bits.b15)
						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(!((WORD_VAL*)&wDest)->bits.b15)
				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);
	
	// 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);
	
	// 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)
