
/* $Revision: 1.153 $ */
/* Copyright $Date: 2012/03/30 18:14:27 $ */
/*
 *******************************************************************************
 *
 * Copyright 1998-2010 NetBurner, Inc.  ALL RIGHTS RESERVED
 *
 *    Permission is hereby granted to purchasers of NetBurner Hardware to use or 
 *    modify this computer program for any use as long as the resultant program 
 *    is only executed on NetBurner provided hardware.
 *
 *    No other rights to use this program or its derivatives in part or in 
 *    whole are granted.
 *
 *    It may be possible to license this or other NetBurner software for use on 
 *    non-NetBurner Hardware. Contact sales@Netburner.com for more information.
 *
 *    NetBurner makes no representation or warranties with respect to the 
 *    performance of this computer program, and specifically disclaims any 
 *    responsibility for any damages, special or consequential, connected with 
 *    the use of this program.
 *
 * NetBurner
 * 5405 Morehouse Dr.
 * San Diego, Ca. 92121
 * www.netburner.com
 *
 *******************************************************************************
 */
/*lint -e661 Possible access of out-of-bounds pointer */
/*lint -e1401 member 'ShortSocketList::m_slist' (line 749) not initialized by constructor */
/*lint -specific( -e676, -e676 ) Possibly negative subscript */

/* NB Library Definitions */
#include "predef.h"

/* C Standard Library */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Portability & uCos Definitions */
#include <basictypes.h>
#include <ucos.h>
#include <ucosmcfc.h>
#include <utils.h>

/* NB Runtime Libraries */
#include <constants.h>
#include <iosys.h>
#include <iointernal.h>
#include <snmp.h>

/* NB Network Stack */
#include <nettypes.h>
#include <buffers.h>
#include <netinterface.h>
#include <counters.h>
#include <ip.h>
#include <tcp.h>

#include <sys/queue.h>

/* Multihome Support */
#if defined MULTIHOME || defined AUTOIP
   #include "multihome.h"
#endif

/* SNMP */
#ifdef SNMP
#include "snmp_table.h"
/* Function definitions for tcpConnEntry */
void ( * pAddTableElementtcpConnEntry )( void *data_el,
                                         snmp_typeIpAddr    tcpConnLocalAddress,
                                         snmp_typeINTEGER   tcpConnLocalPort,
                                         snmp_typeIpAddr    tcpConnRemAddress,
                                         snmp_typeINTEGER   tcpConnRemPort );
void ( * pRemoveTableElementtcpConnEntry )( void *data_el );
/* This table has elements : */
/*EnumVal   : tcpConnState */
/*IpAddr    : tcpConnLocalAddress */
/*INTEGER   : tcpConnLocalPort */
/*IpAddr    : tcpConnRemAddress */
/*INTEGER   : tcpConnRemPort */

#endif




#define TCP_FIN (1)
#define TCP_SYN (2)
#define TCP_RST (4)
#define TCP_PSH (8)
#define TCP_ACK (16)
#define TCP_URG (32)

/* States are defined in tcp.h */

//The following are set into gpflags;
#define NEED_TOACK     (1)
#define TCP_WANTS_TO_CLOSE (2)
#define TCP_FIN_SENT    (4)
#define TCP_ZERO_PROBING  (8)
#define TCP_USER_CLOSED  (16) /* Set only once the user has requested that the socket be closed */
#define TCP_INLISTENLIST (32) /* We are currently stored on a Listening socket list */
#define TCP_INLISTENCNT (64) /* We are currently counted on a Listening socket list */
#define TCP_SAW_SACK   (128)


#define DEFAULT_MAX_OUTSTANDING_ACK_BUFFERS (10)

//Forward declarations
struct socket_struct;
typedef socket_struct SOCKET;
typedef SOCKET *PSOCKET;


#ifdef _DEBUG
   void DumpTcpPacket( PTCPPKT pTcp, WORD len );
#endif /* ifdef _DEBUG */


#define MAXTICKS (0xFFFFFFFF)
#define TCPTOTAL_HEADER_SIZE (20+20)


const BYTE TIME_INDEX[MAX_TCP_RETRY + 1] =
{
  1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64
};





#define DT_STATE_CHANGE 1
#define DT_SEND         2
#define DT_DISCARD      3
#define DT_RX           4
#define DT_CLOSE        5
#define DT_MAXRETRY_CLOSE 6
#define DT_RETRANSMIT    7
#define DT_EXTDOCLOSE    8
#define DT_GOTFIN        9
#define DT_SAVEDATA     10
#define DT_ReadCall     11
#define DT_ReadFail1     12
#define DT_ReadFail2     13
#define DT_ReadError     14
#define DT_ReadFail4     15
#define DT_SAVEZERO      16
#define DT_ERRORTIMO     17
#define DT_NULLRT  18


int GetProperInterface( IPADDR dst );
BOOL IsLocal( IPADDR ip, int ifc );


static WORD epherialport;

DWORD GetFirstSeq()
{
   static WORD ww = 0;
   return (( TimeTick << 16 ) | ( ww++ ))^(0xD9A17815);
}


#ifdef _DEBUG
static WORD dbFlag;
void DebugTraceFunc( socket_struct *ss, PIPPKT pIp, PTCPPKT pTcp, WORD why, int line );
void RawDumpTcpDebug( PSOCKET psfilter );

#define DebugTrace(ss,pIp,pTcp,why) if (dbFlag) DebugTraceFunc(ss,pIp,pTcp,why,__LINE__ );

#else
#define DebugTrace(w,x,y,z) ((void) 0)
#endif



/*******
PoolBuffers and storage overload.
There are four elements at the top of a buffer:

void *   pBufQueuePointer; //For The OS List stuff...
VPoolPtr pPoolNext;     //A pointer to the next buffer in pool
VPoolPtr pPoolPrev;     //A pointer to the prev buffer in pool
VDWORD  dwTime;        //USed for time stamps

In the buffers_tobe_acked. Packets that have been sent, but not yet acked.
         pPoolNext is used to keep the linked list.
         pPoolPrev is used to keep the retransmit count.
         dwTime= The Time when we last sent this packet.

We need to keep the following lists of sockets:
Free sockets.
Inuse Sockets
Listenting Sockets also need a pending sockets list.


How do timers work?
Each socket has a
NextTimeAction variable that has the time of the next timer event to occur.
When the Sockets SetTimer() call is made this value will be changed.
This also automatically keeps a timer priority Queue.
When the timer expires a process Timer function is called.
When the timer is not needed call:
 SetTimer(MAXTICKS)

The timer is used for the following functions....
1)Connection Establishment timer.
2)Retranmission Timer
3)Delayed Ack timer
4)Persist Timer (Zero window probe)
5)Keep Alive (Not supported)
6)FIN_WAIT2
7)Time_Wait 2MSL
*/

#define MAXMAXPEND 8




//Please note that tis is more efficent (1 WORD per socket) than a single pointer added to the structure.
static volatile DWORD current_next_time_event;

struct socket_struct; //Formward declaration
extern socket_struct sockets[TCP_SOCKET_STRUCTS]; 

TAILQ_HEAD(socket_head_struct, socket_struct);
struct socket_struct
{
	socket_head_struct *Head;
	TAILQ_ENTRY(socket_struct) Queue;


      PSOCKET m_pListening;

volatile      DWORD NextTimeAction;//Used for the timer  dispatch
volatile      DWORD next_retransmit; //The next retransmit time we have to wake up for


      //The next three define his window for his reception / my transmission ..
      //
      //                                   |                          |                 |
      //            data that is long gone | Data waiting to be acked | data I can send | data in the future....
      //                                   |                          |                 |
      DWORD sent_and_acked; //------------A
      DWORD send_not_acked; //---------------------------------------A
      DWORD his_window;     //---------------------------------------------------------A
      DWORD my_last_window;    //Uses for SWS avoidance
      DWORD last_seq_I_rxed;//The last sequence I recieved from him....
      DWORD close_time;
     DWORD lastRxTime;

      long tick_RTTVAR; //The RTO in ticks. See Stevens IP... Volume 1 pg 300
      long tick_SRTT;   //Also reused to report errors on close
      long sock_err;
      DWORD RTO;         /* The actual RTO time out */

      fifo_buffer_storage TxBuffer; //The buffer for transmit.
      fifo_buffer_storage RxBuffer; //The buffer for received data

      PoolPtr buffers_tobe_acked_head; //Buffers that have been sent, and not ACKED
      PoolPtr buffers_tobe_acked_tail;
     
volatile  PoolPtr buffers_out_of_order;


      OS_SEM m_OsSemaphore; //This is used for all blocking calls.

	  
      DWORD my_if_ip;
      DWORD his_ip;
	  
      int options; //Socket options

      WORD his_port; //The port he is from
      WORD my_port; //The port he is aimed at
      WORD mss; //His reported max segment size.
      WORD largest_window_iveseen;

      WORD TimeBackPointer;
volatile      BYTE state; //The RFC state
volatile       BYTE gpflags; // General purpose flags... 
      BYTE dup_Ack; //Also listining_sockets
      BYTE cur_listen;
      BYTE nPendingCalls;
      BYTE max_listining_sockets;
      BYTE outstanding_acks; // Number of things on the tobe_acked list
     BYTE max_buffers_out_of_order;
volatile BYTE cur_buffers_out_of_order;


      MACADR mac_shortcut;
      int Intf_shortcut;
      BYTE MAX_OUTSTANDING_ACK_BUFFERS;
      tcp_read_notify_handler *pReadNotify;
      BOOL bLoopBack;


      //Process packets, checksup is ok when this is called
      void process( PoolPtr pp, PTCPPKT pTcp );

      //Process the options portions of the packet
      void processoptions( PoolPtr poolPtr, PTCPPKT pTcp, PIPPKT pIp );

      //We received a segment with data and it is ok to process it.
      void ProcessDataSegment( PTCPPKT pTcp, WORD data_len, PBYTE data, PoolPtr & pp );

     void TestOutofOrder();
      
     //Send ACK SYN FIN etc ...
      void SendState( WORD flags, PoolPtr pp = NULL );


      //Save received data
      void SaveData( PBYTE data, WORD len, BOOL push );

      //Set the time tick when we want the timer function to call ProcessTimer
      //If the passed in value is grater than the current value, nothing is done!
      void SetTimer( DWORD timeout );

      //Called when the timer is ready to be processed
      void ProcessTimer();

      //Move things from the tobe set to to be acked and send them out.
      void DoRawSend();

      //PRocess the ACKs in received packets.
      void ProcessAck( PTCPPKT pTcp );

      //Calculate the RTO time
      void CalcRTO( DWORD txtime );


      //Retransmit if necessary.
      void DoRetransmit( BOOL count );

      //Setup the timer for retransmit if apropos
      void SetUpNextRetransmit();

      //Close the socket
      int DoClose( int why );

      //Process received RST
      void ProcessRST( void );

      //process received FIN.
      void ProcessFIN( PTCPPKT pTcp, WORD data_len );

      //Reset and free everything
      void DoCleanup( void );

      void CleanUpDeletedListener(); 

      void SetState( int news );

      void ClearPending( int why );

      //Get the window to advertise to avoid SWS....
      WORD MyAdvertisedWindow( void );

      void OpenWindow( void );


inline  int GetMyFd() { return (( this - sockets ) + TCP_SOCKET_OFFSET); };

#ifdef SPEED_TCP
      int SockSubmitTcpBuffer4Tx( DWORD csum32bit, WORD length, PBYTE  pBuffer );

      tcp_data_handler *pDataHandle;
#endif

      void init( WORD pos, socket_head_struct *Head )
      {
         state = TCP_STATE_CLOSED;
         Head = Head;
         NextTimeAction = MAXTICKS;
         dup_Ack = 0;
         cur_listen = 0;
         TimeBackPointer = pos + 1;
         nPendingCalls = 0;
         options = 0;
         bLoopBack = FALSE;
         TxBuffer.Init( TCP_BUFFER_SEGMENTS, 0 );
         RxBuffer.Init( TCP_BUFFER_SEGMENTS, 0 );
         mac_shortcut = ENET_ZERO;
         Intf_shortcut = 0;
         MAX_OUTSTANDING_ACK_BUFFERS = DEFAULT_MAX_OUTSTANDING_ACK_BUFFERS;
         pReadNotify = NULL;
      #ifdef SPEED_TCP
       pDataHandle=NULL;
      #endif
 
      };


int CoreConnect(IPADDR addr, WORD localport, WORD remoteport, IPADDR ipa );



};





void RemoveFromUserCloseList( PSOCKET ps );


socket_struct sockets[TCP_SOCKET_STRUCTS];


void socket_struct::SetState( int news )
{
#ifdef SNMP
   if ( ( state == TCP_STATE_ESTABLISHED ) || ( state == TCP_STATE_CLOSE_WAIT ) )
   {
      if ( ( news != TCP_STATE_ESTABLISHED ) && ( news != TCP_STATE_CLOSE_WAIT ) )
      {
         SNMPDEC( snmp_var_tcpCurrEstab );
      }
   }
   else if ( ( news == TCP_STATE_CLOSED ) &&
             ( ( state == TCP_STATE_SYN_SENT ) || ( state == TCP_STATE_SYN_RCVD ) ) )
   {
      SNMPCOUNT( snmp_var_tcpAttemptFails );
   }
#endif

   state = news;
   DebugTrace( this, NULL, NULL, DT_STATE_CHANGE );
};




inline WORD GetTcpDataLen( PIPPKT pIp, PTCPPKT pTcp )
{
   WORD hlen = pTcp->header_len >> 2;

   if ( pIp->bVerHdrLen == 0x45 )
   {
      return pIp->wLength - ( 20 + hlen );
   }
   return pIp->wLength - ( ( ( pIp->bVerHdrLen & 0XF ) * 4 ) + hlen );
}


inline PBYTE GetTcpData( PTCPPKT pTcp )
{
   WORD hlen = pTcp->header_len >> 2;
   return ( ( PBYTE ) pTcp ) + hlen;
}





PSOCKET GetTcpSocket( PTCPPKT pTcp, IPADDR from );



/*lint -e415 Likely access of out-of-bounds pointer */
/*lint -e416 Likely creation of out-of-bounds pointer */
BOOL ValidateTCP( PIPPKT pIp, WORD csum )
{
   register DWORD csum2 = csum;
   register WORD len;

   if ( pIp->bVerHdrLen == 0x45 )
   {
      len = pIp->wLength - 20;
   }
   else
   {
      len = pIp->wLength - ( ( pIp->bVerHdrLen & 0x0F ) * 4 );
   }

   csum2 += ( WORD ) IP_PROTOCOL_TCP;
   csum2 += len;
   PDWORD pdw = &( pIp->ipSrc );
   register PWORD pw = reinterpret_cast<PWORD>( pdw ); /*lint !e740 Unusual pointer cast */
   csum2 += *( pw++ );
   csum2 += *( pw++ );
   csum2 += *( pw++ );
   csum2 += *( pw++ );
   while ( csum2 & 0xffff0000 )
   {
      csum2 = ( csum2 & 0xffff ) + ( csum2 >> 16 );
   }
   if ( csum2 == 0xFFFF )
   {
      return TRUE;
   }
   return FALSE;
}           
/*lint +e416 */
/*lint +e415 */


extern VWORD wGlobalPktId; /* The counter for packets */


BOOL inline IsNullMac( const MACADR &ma )
{
   return ( ( ma.phywadr[0] == 0 ) && ( ma.phywadr[1] == 0 ) && ( ma.phywadr[2] == 0 ) );
}




/* Test Cases */
/* 0x7fffffff,0x80000000 = -1 */
/* 0x80000000,0x7fffffff,= 1 */
/* 0xffffffff,0x00000000,= -1 */
/* 0x00000000,0xffffffff= 1 */
/* 0x00000000,0x00000001= -1 */
/* 0x00000001,0x00000000= 1 */


inline int modulodiff( DWORD v1, DWORD v2 )
{
   int rv = v1 - v2; /* The modulo math just works ! */ /*lint !e713 Loss of precision */
   return rv;
}




void FixHeaderAndSendTcp( PoolPtr p, PIPPKT pIp, PSOCKET ps )
{
   if ( ( ps ) && ( !IsNullMac( ps->mac_shortcut ) && ( ps->bLoopBack == FALSE ) ) )
   {
      register PEFRAME pf;
      SNMPCOUNT( snmp_var_ipOutRequests );

      //Fix the header check sum
      pIp->hCSum = 0;
      pIp->wpktId = wGlobalPktId++;
      pIp->ipSrc = ps->my_if_ip; //was InterfaceIP( ps->Intf_shortcut ), changed for both speed and to allow connections to continue after ip change

      pIp->hCSum = GetSum( ( PWORD ) pIp, ( pIp->bVerHdrLen & 0x0f ) * 4 ); /*lint !e740 Unusual pointer cast */
      //Fix the frame size and ehternet fields
      pf = GetEframe( p );
      p->usedsize = HTONS( pIp->wLength ) + 14;
      pf->src_addr = InterfaceMAC( ps->Intf_shortcut );
      pf->eType = EIP_TYPE;
      pf->dest_addr = ps->mac_shortcut;
      SNMPCOUNT( EthifOutUcastPkts );     
      TransmitBuffer( p, ps->Intf_shortcut );
   }
   else
   {
   	  if(ps)
	   pIp->ipSrc=ps->my_if_ip;
	  else
	   pIp->ipSrc=0;
      
	  FixHeaderAndSend( p, pIp );
   }
}




void TcpSendwSum( PSOCKET ps,
                  IPADDR to,
                  PoolPtr pp,
                  WORD datalen,
                  BOOL keep,
                  DWORD data_sum )
{
   if ( pp == NULL )
   {
      return;
   }
   register PTCPPKT pTcp = GetInitTcpPkt( pp );

   while ( data_sum >> 16 )
   {
      data_sum = ( data_sum & 0xffff ) + ( data_sum >> 16 );
   }



   pTcp->csum = 0;
   WORD hlen = pTcp->header_len >> 2;
   //WE use the IP header space to hold the psudeo header;


   register PPSUH pph = ( PPSUH ) ( ( PBYTE ) ( pTcp ) - sizeof( PsudeoHeader ) );

   if ( ( ps ) && ( ps->my_if_ip ) )
   {
      pph->srcip = ps->my_if_ip;
   }
   else
   {
      pph->srcip = GetSrcIp( to );
   }

   pph->dstip = to;
   pph->proto = IP_PROTOCOL_TCP;
   pph->len = datalen + hlen;

   if ( datalen )
   {
      WORD cs2 = GetSum( ( PWORD ) pph, ( hlen ) + sizeof( PsudeoHeader ) ); /*lint !e740 Unusual pointer cast */

      register DWORD sum = ( WORD ) ( ~cs2 );
      sum += data_sum;

      while ( sum >> 16 )
      {
         sum = ( sum & 0xffff ) + ( sum >> 16 );
      }

      pTcp->csum = ( WORD ) ~sum;
   }
   else
   {
      pTcp->csum = GetSum( ( PWORD ) pph, ( hlen ) + sizeof( PsudeoHeader ) ); /*lint !e740 Unusual pointer cast */
   } 


   register PIPPKT pIp = GetIpPkt( pp );

   pIp->bVerHdrLen = 0x45;
   pIp->bTos = 0;
   pIp->wLength = HTONS( 20 + hlen + datalen );
   pIp->wFlags_Frag = 0;
   pIp->proto = IP_PROTOCOL_TCP;
   pIp->ipDst = to;
   pIp->bTTL = bTTL_Default;

   DebugTrace( ps, pIp, pTcp, DT_SEND );

   if ( keep )
   {
      IncUsageCount( pp );
   }

   frames_tx_tcp++;
   SNMPCOUNT( snmp_var_tcpOutSegs );  
   FixHeaderAndSendTcp( pp, pIp, ps );
}



void TcpSend( PSOCKET ps, IPADDR to, PoolPtr pp, WORD datalen, BOOL keep = FALSE )
{
   if ( pp == NULL )
   {
      return;
   }

   PTCPPKT pTcp = GetInitTcpPkt( pp );

   DWORD csum = 0;
   if ( datalen != 0 )
   {
      csum = GetSum( ( PWORD ) pTcp->DATA, datalen );
      csum = ( WORD ) ~csum;
   }
   TcpSendwSum( ps, to, pp, datalen, keep, csum );
}









void SendTcpSynRst( PIPPKT pIp )
{
   PoolPtr pp = GetBuffer();
   if ( pp == NULL )
   {
      return;
   }
   register PTCPPKT pTcpOut = GetInitTcpPkt( pp );
   PTCPPKT pTcpIn = GetTcpPkt( pIp );
   pTcpOut->srcPort = pTcpIn->dstPort;
   pTcpOut->dstPort = pTcpIn->srcPort;
   pTcpOut->SeqNumber = 0;
   pTcpOut->AckNumber = pTcpIn->SeqNumber + 1;
   pTcpOut->header_len = 0x50;
   pTcpOut->flags = TCP_RST | TCP_ACK;
   pTcpOut->window = 0;
   pTcpOut->urgent = 0;
   pTcpOut->csum = 0;
   TcpSend( NULL, pIp->ipSrc, pp, 0 );
}


void SendTcpRst( PIPPKT pIp )
{
   PoolPtr pp = GetBuffer();
   if ( pp == NULL )
   {
      return;
   }
   register PTCPPKT pTcpOut = GetInitTcpPkt( pp );
   PTCPPKT pTcpIn = GetTcpPkt( pIp );
   pTcpOut->srcPort = pTcpIn->dstPort;
   pTcpOut->dstPort = pTcpIn->srcPort;

   if ( pTcpIn->flags & TCP_ACK )
   {
      pTcpOut->SeqNumber = pTcpIn->AckNumber;
   }
   else
   {
      pTcpOut->SeqNumber = 0;
   }

   pTcpOut->AckNumber = pTcpIn->SeqNumber;
   pTcpOut->header_len = 0x50;
   pTcpOut->flags = TCP_RST;
   pTcpOut->window = 0;
   pTcpOut->urgent = 0;
   pTcpOut->csum = 0;
   TcpSend( NULL, pIp->ipSrc, pp, 0 );
}



#define SocketHashSize 37
socket_head_struct m_slist[SocketHashSize];

class HashSocketList

{
   public:
      HashSocketList();
      void Init( void ) const;
      PSOCKET findsocket( WORD dstport, WORD myport, DWORD hisip ) const;
      void AddSocket( PSOCKET ps )const;
      void RemoveSocket( PSOCKET ps ) const;
};



/* 
The system accumulates a lot of sockets in TIME_WAIT and  FIN_WAIT_2 states.
This list keeps them without using up the Socket list resources 
This short list only gets sockets that have nothing left to Ack. */


struct ShortSocket;
class ShortSocketList
{
      WORD m_slist[SocketHashSize];
      ShortSocket *findsocket( WORD d, WORD a, DWORD ip ) const;
      ShortSocket *GetFreeShort();
      void FreeShort( ShortSocket *pShort ) const;

   public:
      ShortSocketList();
      void Init( void );
      void AddSocket( PSOCKET ps );
      int process( PoolPtr pp, PTCPPKT pTcp, DWORD ip ) const; /* Returns true if the socket was handled */
      DWORD processTick(); /* Returns the time of the next tick to deal with */
};




socket_head_struct free_list_head;
socket_head_struct user_closed_list_head;
socket_head_struct listen_list_head;


HashSocketList active_list;
ShortSocketList semiactive_list;


void process_tcp( PoolPtr pp, PIPPKT pIP, WORD csum )
{
   ChangeOwner( pp );



   if ( ValidateTCP( pIP, csum ) )
   {
      register PTCPPKT pTcp = GetTcpPkt( pIP );
      PSOCKET ps = GetTcpSocket( pTcp, pIP->ipSrc );
      if ( ps )
      {
         ps->process( pp, pTcp );
         return;
      }
      else if ( !semiactive_list.process( pp, pTcp, pIP->ipSrc ) )
      {
         DebugTrace( NULL, pIP, pTcp, DT_DISCARD );
         if ( !( pTcp->flags & TCP_RST ) )
         {
            if ( pTcp->flags & TCP_SYN )
            {
               //#warning exra stuff
               //      iprintf("************************************************\r\nWe are reseting a SYN connection\r\n");
               //   iprintf("From ");
               //   ShowIP(pIP->ipSrc);
               //   iprintf(" from  port %d to port %d flags = %04X\r\n",pTcp->srcPort,pTcp->dstPort,pTcp->flags);

               SendTcpSynRst( pIP );
            }
            else
            {
               //#warning exra stuff
               //  iprintf("************************************************\r\nWe are reseting a NON syn connection\r\n");
               SendTcpRst( pIP );
            }
            SNMPCOUNT( snmp_var_tcpOutRsts );  // Inc number of TCP packets sent with RST flag raised (by R.A)
         }
         FreeBuffer( pp );
      }
   }
   else
   {
#ifdef _DEBUG
      ShowData( pp->pData, pp->usedsize );
#endif
      SNMPCOUNT( snmp_var_tcpInErrs );  
      frames_tcp_errors++;
      FreeBuffer( pp );
   }
}


DWORD   TCPStk[TCP_STK_SIZE] __attribute__( ( aligned( 4 ) ) ) FAST_TCP_STK;

void TCPTimerTask( void *p );


OS_CRIT TCP_critical_section FAST_TCP_VAR;
OS_SEM TCP_TimerSemaphore FAST_TCP_VAR;





void InitTCP()
{
   OSCritInit( &TCP_critical_section );
   OSSemInit( &TCP_TimerSemaphore, 0 );
   {
      OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock

      // Initialize our list heads
      TAILQ_INIT(&free_list_head);
      TAILQ_INIT(&user_closed_list_head);
      TAILQ_INIT(&listen_list_head);

      // Initialize all of our sockets by default to the free list head
      WORD n;
      for ( n = 0; n < TCP_SOCKET_STRUCTS; n++ )
      {
         sockets[n].init( n, &free_list_head);
         TAILQ_INSERT_TAIL(&free_list_head, &sockets[n], Queue);
      }

      active_list.Init();
      semiactive_list.Init();
      OSTaskCreatewName( TCPTimerTask,
                    ( void * ) 0,
                    ( void * ) &TCPStk[TCP_STK_SIZE],
                    ( void * ) TCPStk,
                    TCP_PRIO,"TCPD" );
   }
}




#define INSERTLIST(ps,list_head)\
{\
OSCriticalSectionObj oscs(TCP_critical_section); /*Locks the OS the destructor will unlock*/\
ps->m_pNextActive=list_head;\
ps->m_pPrevActive=NULL;\
if (list_head) list_head->m_pPrevActive=ps;\
list_head=ps;\
}


#define INSERTBEFORE(ps,list_head,beforeps) \
{\
OSCriticalSectionObj oscs(TCP_critical_section); /*Locks the OS the destructor will unlock*/\
if (list_head)\
{ /*List not Null */\
if (beforeps!=NULL)\
 {\
  ps->m_pNextActive=beforeps;\
  ps->m_pPrevActive=beforeps->m_pPrevActive;\
  beforeps->m_pPrevActive=ps;\
  if (ps->m_pPrevActive)\
      ps->m_pPrevActive->m_pNextActive=ps;\
 }\
else\
 { /*before ps was null it must be the end of the list*/\
  PSOCKET pss=list_head;\
  while(pss->m_pNextActive) pss=pss->m_pNextActive;\
  pss->m_pNextActive=ps;\
  ps->m_pNextActive=NULL;\
  ps->m_pPrevActive=pss;\
 } /*before ps was null it must be the end of the list*/\
}/*(List Not Null*/ \
else\
{/*List was NULL */\
 ps->m_pNextActive=NULL;\
 ps->m_pPrevActive=NULL;\
 list_head=ps;\
}/*List Was Null*/\
}


#define INSERTAFTER(ps,list_head,afterps) \
{\
OSCriticalSectionObj oscs(TCP_critical_section); /*Locks the OS the destructor will unlock*/\
if (list_head) && (afterps!=NULL)\
{ /*List not Null */\
  ps->m_pNextActive=afterps->m_pNextActive;\
  ps->m_pPrevActive=afterps;\
  afterps->m_pNextActive=ps;\
  if (ps->m_pNextActive)\
      ps->m_pNextActive->m_pPrevActive=ps;\
} /*List not null */\
else\
{/*List was NULL */\
 ps->m_pNextActive=NULL;\
 ps->m_pPrevActive=NULL;\
 list_head=ps;\
}/*List Was Null*/\
}

#define REMOVE(ps,list_head)\
{\
OSCriticalSectionObj oscs(TCP_critical_section); /*Locks the OS the destructor will unlock*/\
if (ps->m_pNextActive)\
      ps->m_pNextActive->m_pPrevActive=ps->m_pPrevActive;\
if (ps->m_pPrevActive)\
      ps->m_pPrevActive->m_pNextActive=ps->m_pNextActive;\
    else /*If the pre==NULL we are head of the list so fix list_head */\
    list_head=ps->m_pNextActive;\
}


void FREESOCKET( PSOCKET ps )
{
	OSCriticalSectionObj oscs(TCP_critical_section); /*Locks the OS the destructor will unlock*/

	// If it's in a list now (it probably is), we want to remove it so it's temporarily an orphan
	if (ps->Head != NULL) {

		// Remove it from the current list
		TAILQ_REMOVE(ps->Head, ps, Queue);
		ps->Head = NULL;
		ps->Queue.tqe_next = NULL;
		ps->Queue.tqe_prev = NULL;
	}

	// If it's closed, we're going to move it to free
	if ( ps->gpflags & TCP_USER_CLOSED ) {

		// Add it to the free list head
		TAILQ_INSERT_TAIL(&free_list_head, ps, Queue);

		// Change the head of the socket
		ps->Head = &free_list_head;

	// Otherwise, we're going to move it to closed
	} else {

		// Add it to the closed list head
		TAILQ_INSERT_TAIL(&user_closed_list_head, ps, Queue);

		// Change the head of the socket
		ps->Head = &user_closed_list_head;
	}
}


void RemoveFromUserCloseList( PSOCKET ps ) {

	// If we're in the user closed list head
	if (ps->Head == &user_closed_list_head) {

		// Remove the socket from the closed list
		TAILQ_REMOVE(&user_closed_list_head, ps, Queue);
		ps->Head = NULL;
		ps->Queue.tqe_next = NULL;
		ps->Queue.tqe_prev = NULL;

		// Clear the socket err flags
		ps->sock_err = 0;

		// Perform our cleanup
		ps->DoCleanup();

		// Add to the free list head
		TAILQ_INSERT_TAIL(&free_list_head, ps, Queue);

		// Change the head of our ps
		ps->Head = &free_list_head;
	}
}







#ifdef _DEBUG

void DBSHOWDATA( int mask, const char *text, int val )
{
   DBPRINT( mask, text );
   DBNUM( mask, val );
   DBPRINT( mask, "\r\n" );
}

void DBSHOWDATA( int mask, const char *text, long val )
{
   DBPRINT( mask, text );
   DBNUM( mask, ( int ) val );
   DBPRINT( mask, "\r\n" );
}

void DBSHOWDATA( int mask, const char *text, DWORD val )
{
   DBPRINT( mask, text );
   DBNUM( mask, ( int ) val );
   DBPRINT( mask, "\r\n" );
}

void DBSHOWDATA( int mask, const char *text, WORD val )
{
   DBPRINT( mask, text );
   DBNUM( mask, ( int ) val );
   DBPRINT( mask, "\r\n" );
}

void DBSHOWDATA( int mask, const char *text, BYTE val )
{
   DBPRINT( mask, text );

   DBBYTE( mask, val );

   DBPRINT( mask, "\r\n" );
}


void ShowTcpSocket( PSOCKET psock )
{
   switch ( psock->state )
   {
     case TCP_STATE_CLOSED           :
       DBPRINT( DB_TCPIP, "CLOSED     |" ); break;
     case TCP_STATE_LISTEN           :
       DBPRINT( DB_TCPIP, "LISTEN     |" ); break;
     case TCP_STATE_SYN_SENT         :
       DBPRINT( DB_TCPIP, "SYN_SENT   |" ); break;
     case TCP_STATE_SYN_RCVD         :
       DBPRINT( DB_TCPIP, "SYN_RCVD   |" ); break;
     case TCP_STATE_WAIT_FOR_ACCEPT  :
       DBPRINT( DB_TCPIP, "Wait 4 ACPT|" ); break;
     case TCP_STATE_ESTABLISHED      :
       DBPRINT( DB_TCPIP, "ESTABLISHED|" ); break;
     case TCP_STATE_CLOSE_WAIT       :
       DBPRINT( DB_TCPIP, "CLOSE_WAIT |" ); break;
     case TCP_STATE_LAST_ACK         :
       DBPRINT( DB_TCPIP, "LAST_ACK   |" ); break;
     case TCP_STATE_FIN_WAIT_1       :
       DBPRINT( DB_TCPIP, "FIN_WAIT_1 |" ); break;
     case TCP_STATE_FIN_WAIT_2       :
       DBPRINT( DB_TCPIP, "FIN_WAIT_2 |" ); break;
     case TCP_STATE_CLOSING          :
       DBPRINT( DB_TCPIP, "CLOSING    |" ); break;
     case TCP_STATE_TIME_WAIT        :
       DBPRINT( DB_TCPIP, "TIME_WAIT  |" ); break;
     default:
       DBPRINT( DB_TCPIP, "Unknown | " ); break;
   }  

   DBPRINT( DB_TCPIP, "\r\n" ); 


   DBSHOWDATA( DB_TCPIP, "sent_and_acked", psock->sent_and_acked );  
   DBSHOWDATA( DB_TCPIP, "send_not_acked", psock->send_not_acked );  
   DBSHOWDATA( DB_TCPIP, "his_window", psock->his_window );      
   DBSHOWDATA( DB_TCPIP, "last_seq_I_rxed", psock->last_seq_I_rxed ); 
   DBSHOWDATA( DB_TCPIP, "scok_err", psock->sock_err );


   if ( psock->TxBuffer.Empty() )
   {
      DBPRINT( DB_TCPIP, "TX Empty \r\n" );
   }
   else
   {
      DBPRINT( DB_TCPIP, "TX NotEmpty \r\n" );
   }

   if ( psock->RxBuffer.Empty() )
   {
      DBPRINT( DB_TCPIP, "RX Empty \r\n" );
   }
   else
   {
      DBPRINT( DB_TCPIP, "RX NotEmpty \r\n" );
   }

   if ( psock->buffers_tobe_acked_head != NULL )
   {
      DBPRINT( DB_TCPIP, "Data to Ack\r\n" );
   }
   else
   {
      DBPRINT( DB_TCPIP, "No Data Ack\r\n" );
   }
   {
      PBYTE pb = ( PBYTE ) & ( psock->his_ip );
      DBPRINT( DB_TCPIP, "Src IP" );
      DBBYTE( DB_TCPIP, pb[0] );
      DBPRINT( DB_TCPIP, "." );
      DBBYTE( DB_TCPIP, pb[1] );
      DBPRINT( DB_TCPIP, "." );
      DBBYTE( DB_TCPIP, pb[2] );
      DBPRINT( DB_TCPIP, "." );
      DBBYTE( DB_TCPIP, pb[3] );
      DBPRINT( DB_TCPIP, ".:" );
      DBBYTE( DB_TCPIP, ( ( PBYTE ) & psock->his_port )[0] );
      DBBYTE( DB_TCPIP, ( ( PBYTE ) & psock->his_port )[1] );
      DBPRINT( DB_TCPIP, " my_port:" );
      DBBYTE( DB_TCPIP, ( ( PBYTE ) & psock->my_port )[0] );
      DBBYTE( DB_TCPIP, ( ( PBYTE ) & psock->my_port )[1] );
      DBPRINT( DB_TCPIP, "\r\n" );
   }
}



void ShowTCPStates()
{
   for ( int ii = 0; ii < TCP_SOCKET_STRUCTS; ii++ )
   {
      switch ( sockets[ii].state )
      {
        case TCP_STATE_CLOSED           :
          DBPRINT( DB_TCPIP, "CLOSED     |" ); break;
        case TCP_STATE_LISTEN           :
          DBPRINT( DB_TCPIP, "LISTEN     |" ); break;
        case TCP_STATE_SYN_SENT         :
          DBPRINT( DB_TCPIP, "SYN_SENT   |" ); break;
        case TCP_STATE_SYN_RCVD         :
          DBPRINT( DB_TCPIP, "SYN_RCVD   |" ); break;
        case TCP_STATE_WAIT_FOR_ACCEPT  :
          DBPRINT( DB_TCPIP, "Wait 4 ACPT|" ); break;
        case TCP_STATE_ESTABLISHED      :
          DBPRINT( DB_TCPIP, "ESTABLISHED|" ); break;
        case TCP_STATE_CLOSE_WAIT       :
          DBPRINT( DB_TCPIP, "CLOSE_WAIT |" ); break;
        case TCP_STATE_LAST_ACK         :
          DBPRINT( DB_TCPIP, "LAST_ACK   |" ); break;
        case TCP_STATE_FIN_WAIT_1       :
          DBPRINT( DB_TCPIP, "FIN_WAIT_1 |" ); break;
        case TCP_STATE_FIN_WAIT_2       :
          DBPRINT( DB_TCPIP, "FIN_WAIT_2 |" ); break;
        case TCP_STATE_CLOSING          :
          DBPRINT( DB_TCPIP, "CLOSING    |" ); break;
        case TCP_STATE_TIME_WAIT        :
          DBPRINT( DB_TCPIP, "TIME_WAIT  |" ); break;
        default:
          DBPRINT( DB_TCPIP, "Unknow " ); break;
      }  
      if ( sockets[ii].buffers_tobe_acked_head )
      {
         DBPRINT( DB_TCPIP, "Ack not Null\r\n" );
      }
      else
      {
         DBPRINT( DB_TCPIP, "\r\n " );
      }
   }
}

#endif



PSOCKET GetFreeSocket()
{
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/

   register PSOCKET pReturn = TAILQ_FIRST(&free_list_head);

   if ( !pReturn )
   {
      int min_time_fd = -1;
      DWORD min_time = 0xFFFFFFFF;

      for ( register int ii = 0; ii < TCP_SOCKET_STRUCTS; ii++ )
      {
         if ( sockets[ii].close_time < min_time )
         {
            min_time_fd = ii;
            min_time = sockets[ii].close_time;
         }
      }
      if ( min_time_fd == -1 )
      {
#ifdef _DEBUG
         DBPRINT( DB_TCPIP, "Socket ALLOC Failure **********************************\r\n" );
         ShowTCPStates();
#endif
         return NULL;
      }
      abortsocket( min_time_fd + TCP_SOCKET_OFFSET ); 
      pReturn = TAILQ_FIRST(&free_list_head);
   }
   if ( !pReturn )
   {
      return NULL;
   }

   // Remove the socket from our TAILQ
   TAILQ_REMOVE(&free_list_head, pReturn, Queue);
   pReturn->Head = NULL;
   pReturn->Queue.tqe_next = NULL;
   pReturn->Queue.tqe_prev = NULL;

   int fd = pReturn->GetMyFd();
   pReturn->pReadNotify = NULL;

#ifdef SPEED_TCP
    pReturn->pDataHandle=NULL;
#endif
 


   ClrDataAvail( fd );
   ClrHaveError( fd );
   ClrWriteAvail( fd );
   pReturn->close_time = 0xFFFFFFFF;
   pReturn->MAX_OUTSTANDING_ACK_BUFFERS = DEFAULT_MAX_OUTSTANDING_ACK_BUFFERS; 


   return pReturn;
}


int connect( IPADDR addr, WORD localport, WORD remoteport, DWORD timeout) {
   return connect( addr, localport, remoteport, timeout, 0 );
}



int socket_struct::CoreConnect(IPADDR addr, WORD localport, WORD remoteport, IPADDR ipa )
{
 
	if ((addr & 0xFF000000)==0x7F000000)
 {
  addr=InterfaceIP(GetFirstInterface());
  my_if_ip =addr;
 }
 else
 {
   if(ipa==0) 
	my_if_ip = GetSrcIp(addr);
	else
	my_if_ip = ipa;
 }

int fd = GetMyFd();

SetState( TCP_STATE_SYN_SENT );
his_ip = addr;
sock_err = 0;





if ( ( IsMyIp( addr ) ) || ( ( addr & 0xFF000000 ) == 0x7F000000 ) )
{
   bLoopBack = TRUE;
}  

if ( localport != 0 )
{
   my_port = localport;
}
else
{
   if ( epherialport < 0x8000 )
   {
	  epherialport = 0x8000 + TimeTick;
	  if ( epherialport < 0x8000 )
	  {
		 epherialport += 0x8000;
	  }
   }
   my_port = epherialport++;
   if ( epherialport == 0 )
   {
	  epherialport = 0x8000;
   }
}

his_port = remoteport;
sock_err = 0;


TxBuffer.Reset( TCP_BUFFER_SEGMENTS );
RxBuffer.Reset( TCP_BUFFER_SEGMENTS );
buffers_out_of_order = NULL;
max_buffers_out_of_order=0;
cur_buffers_out_of_order=0;
buffers_tobe_acked_head = NULL;
buffers_tobe_acked_tail = NULL;
outstanding_acks = 0;
sent_and_acked = GetFirstSeq();
send_not_acked =sent_and_acked+1;
his_window = 0;
largest_window_iveseen = 0;
last_seq_I_rxed = 0;
my_last_window = 0;
mss = DEFAULT_TCP_MSS;
tick_RTTVAR = DEFAULT_TCP_RTTVAR  ;//Should calc to RTO of 3 secs in ticks.
m_pListening = this;
RTO = DEFAULT_INITAL_RTO;
dup_Ack = 0;
cur_listen = 0;
gpflags = 0;
mac_shortcut = ENET_ZERO;
Intf_shortcut = 0;
options = 0; 

ClrDataAvail( fd );
ClrHaveError( fd );

OSSemInit( &m_OsSemaphore, 0 );

#ifdef SNMP
if ( pAddTableElementtcpConnEntry != NULL )
{
   pAddTableElementtcpConnEntry( ( void * ) this,
								 EthernetIP,
								 my_port,
								 his_ip,
								 his_port );
} 
#endif


active_list.AddSocket( this );

register PoolPtr pp = GetBuffer();
if ( pp != NULL )
{
   IncUsageCount( pp ); 
   pp->bInterfaceNumber = GetProperInterface( addr );
}
else
{
   gpflags |= TCP_USER_CLOSED;
   DoClose( TCP_ERR_TIMEOUT );
   return TCP_ERR_NONE_AVAIL;

   //FIXME
}

buffers_tobe_acked_head = pp; //Tail was null so head must be also
buffers_tobe_acked_tail = pp;
pp->pPoolPrev = NULL; //Set the retransmit count =0;
pp->pPoolNext=NULL; 

if ( IsLocal( addr, pp->bInterfaceNumber ) )
   {
	  next_retransmit=(TimeTick + (TICKS_PER_SECOND / 3));
   }
   else
   {
	   next_retransmit=(TimeTick + (TICKS_PER_SECOND*3));
   }

SetTimer( next_retransmit );
SendState( TCP_SYN, pp );
return fd; 
}





#if defined MULTIHOME || defined AUTOIP
int GetLocalIPInterface( IPADDR );
int NoBlockConnect( IPADDR addr, WORD localport, WORD remoteport, IPADDR interfaceIpAddress )
{
   IPADDR localIpAddress = interfaceIpAddress;
   if ( interfaceIpAddress == 0 )
   {
      localIpAddress = InterfaceIP( GetLocalIPInterface( addr ) );
   }
   return NoBlockconnectvia( addr, localport, remoteport,localIpAddress );
}
#endif

#if defined MULTIHOME || defined AUTOIP
int NoBlockconnectvia( IPADDR addr, WORD localport, WORD remoteport,IPADDR ipa )
#else
int NoBlockConnect(IPADDR addr , WORD localport, WORD remoteport,IPADDR ipa)
#endif
{
register PSOCKET ps = GetFreeSocket();

if ( ps == NULL )
{
	return TCP_ERR_NONE_AVAIL;
}
//return ps->CoreConnect(addr, localport, remoteport, ipa);

return  ps->CoreConnect(addr, localport, remoteport, ipa);

}




#if defined MULTIHOME || defined AUTOIP
int connectvia( IPADDR addr, WORD localport, WORD remoteport, DWORD timeout, IPADDR ipa )
#else
int connect( IPADDR addr, WORD localport, WORD remoteport, DWORD timeout, IPADDR ipa )
#endif
{
register PSOCKET ps = GetFreeSocket();

if ( ps == NULL )
{
	return TCP_ERR_NONE_AVAIL;
}


int fd=  ps->CoreConnect(addr, localport, remoteport, ipa);

if(fd<0) return fd;


if(OSSemPend( &ps->m_OsSemaphore, timeout )!= OS_TIMEOUT)
{
 if ( ps->state == TCP_STATE_SYN_SENT )
 {
   //We can only get here if our socket was reset
   ps->gpflags |= TCP_USER_CLOSED;
   ps->DoClose( TCP_ERR_TIMEOUT );
   return TCP_ERR_CON_RESET;
 }
 SNMPCOUNT( snmp_var_tcpActiveOpens );  
 SNMPCOUNT( snmp_var_tcpCurrEstab );
 return fd;
}

 ps->gpflags |= TCP_USER_CLOSED;
 ps->DoClose( TCP_ERR_TIMEOUT );
return TCP_ERR_TIMEOUT;
}


extern "C"
{
   int _Socket_Private_close( int fd );
}


#if defined MULTIHOME || defined AUTOIP
int GetLocalIPInterface( IPADDR );
int connect( IPADDR addr, WORD localport, WORD remoteport, DWORD timeout, 
      IPADDR interfaceIpAddress )
{
   IPADDR localIpAddress = interfaceIpAddress;
   if ( interfaceIpAddress == 0 )
   {
      localIpAddress = InterfaceIP( GetLocalIPInterface( addr ) );
   }
   return connectvia( addr, localport, remoteport, timeout, localIpAddress );
}
#endif





int listen( IPADDR addr, WORD port, BYTE maxpend )
{
   register PSOCKET ps = GetFreeSocket();
   if ( ps == NULL )
   {
      return TCP_ERR_NONE_AVAIL;
   }

   ps->SetState( TCP_STATE_LISTEN );
   ps->his_ip = addr;
   ps->my_if_ip = 0;
   ps->his_port = 0;
   ps->my_port = port;
   ps->sock_err = 0;
   if ( maxpend > MAXMAXPEND )
   {
      maxpend = MAXMAXPEND;
   }
   ps->dup_Ack = 0; /*listening sockets */
   ps->cur_listen = 0;
   ps->max_listining_sockets = maxpend;
   ps->gpflags = 0;
   ps->mac_shortcut = ENET_ZERO;
   ps->Intf_shortcut = 0;

   ps->RxBuffer.Reset( 2 );

   OSSemInit( &ps->m_OsSemaphore, 0 );

   // Lock our mutex
   OSCriticalSectionObj oscs(TCP_critical_section);

   // Add the socket to our listen head
   TAILQ_INSERT_TAIL(&listen_list_head, ps, Queue);

   // Update the socket's head
   ps->Head = &listen_list_head;

#ifdef SNMP
   if ( pAddTableElementtcpConnEntry != NULL )
   {
      pAddTableElementtcpConnEntry( ( void * ) ps, EthernetIP, port, addr, 0 );
   }
#endif


   return ps->GetMyFd(); 
}




int accept( int listening_socket, IPADDR *address, WORD *port, WORD timeout )
{
   int sl = listening_socket - TCP_SOCKET_OFFSET;

   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }

   register PSOCKET pL = sockets + sl;

   pL->nPendingCalls++;

   if ( OSSemPend( &pL->m_OsSemaphore, timeout ) )
   {
      return TCP_ERR_TIMEOUT; //We timed out
   }

   pL->nPendingCalls--;

   if ( pL->sock_err )
   {
      if ( pL->cur_listen )
      {
         pL->cur_listen--;
      }
      return pL->sock_err;
   }

   PSOCKET ps = (PSOCKET)0xDEADBEEF;

   pL->RxBuffer.ReadData( (PBYTE)&ps, sizeof( ps ) );


   ASSERT( ( ps >= sockets ) && ( ps < ( sockets + TCP_SOCKET_STRUCTS ) ) );

   if ( ( DWORD ) ps == 0xDEADBEEF )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }


{
      OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock

  if ((ps->RxBuffer.Empty()) && ( ps->state !=   TCP_STATE_WAIT_FOR_ACCEPT ))
   {
      if ( pL->cur_listen )
      {
         pL->cur_listen--;
      }
      int err = ps->sock_err;
      _Socket_Private_close( ps->GetMyFd());
      if ( pL->RxBuffer.Empty() )
      {
         ClrDataAvail( listening_socket );
      }
      return err;
   }

   ps->gpflags &= ~( TCP_INLISTENLIST | TCP_INLISTENCNT );

   if ( pL->cur_listen )
   {
      pL->cur_listen--;
   }

   ps->my_last_window = ps->last_seq_I_rxed;
   
   if( ps->state ==   TCP_STATE_WAIT_FOR_ACCEPT ) 
		ps->SetState( TCP_STATE_ESTABLISHED );
}

   ps->SendState( TCP_ACK );

   ps->gpflags &= ~TCP_USER_CLOSED; /* We are going to return the socket to the user so he has to acknowledge closing it */

   if ( address != NULL )
   {
      *address = ps->his_ip;
   }
   if ( port != NULL )
   {
      *port = ps->his_port;
   }
   int fd = ps->GetMyFd();
   {
      OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
      if ( ( ps->RxBuffer.Empty() ) )
      {
         ClrDataAvail( fd );
      }
      else
      {
         SetDataAvail( fd );
      }
      SetWriteAvail( fd );


      if ( pL->RxBuffer.Empty() )
      {
         ClrDataAvail( listening_socket );
      }
   }

#ifdef _DEBUG
   USER_ENTER_CRITICAL();
   DBPRINT( DB_TCPIP, "Accept from remote port:" );
   DBNUM( DB_TCPIP, ps->his_port ); 
   DBPRINT( DB_TCPIP, "\r\n" );
   USER_EXIT_CRITICAL();
#endif

   return fd;
}



PSOCKET FindListenSocket( WORD dstPort, IPADDR ip )
{
   register PSOCKET ps;
   TAILQ_FOREACH(ps, &listen_list_head, Queue) {
	  if ( ( ps->my_port == dstPort ) && ( ( ps->his_ip == INADDR_ANY ) || ( ps->his_ip == ip ) ) ) {
		 return ps;
	  }
   }
   return NULL;
}


PSOCKET GetTcpSocket( PTCPPKT pTcp, IPADDR ip )
{
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   register PSOCKET ps = NULL;
   if ( pTcp->flags & TCP_SYN )
   {
      ps = active_list.findsocket( pTcp->dstPort, pTcp->srcPort, ip );
      if ( ps != NULL )
      {
         return ps;
      }

      ps = FindListenSocket( pTcp->dstPort, ip );
      if ( ps != NULL )
      {
         return ps;
      }
   }

   return active_list.findsocket( pTcp->dstPort, pTcp->srcPort, ip );
}


void socket_struct::process( PoolPtr pp, PTCPPKT pTcp ) {
   register PIPPKT pIp = GetIpPkt( pp );
   WORD data_len = GetTcpDataLen( pIp, pTcp );
   PBYTE pData = GetTcpData( pTcp );

   SNMPCOUNT( snmp_var_tcpInSegs );  

   DebugTrace( this, pIp, pTcp, DT_RX );

 // record time of last RX activity  
   lastRxTime = TimeTick;

   if ( pTcp->flags & ( TCP_RST | TCP_SYN ) )
   {
      if ( pTcp->flags & TCP_RST )
      {
         ProcessRST();
         FreeBuffer( pp );
         return;
      }
      else
      {
         if ( state > TCP_STATE_SYN_RCVD )
         {
            FreeBuffer( pp );
            return;
         }
      }
   }




   switch ( state )
   {
     case TCP_STATE_CLOSED:
       //We should never get here!
       SendTcpRst( pIp );
       FreeBuffer( pp );
       return;

     case TCP_STATE_LISTEN:
       if ( pTcp->flags & TCP_ACK )
       {
          my_if_ip = pIp->ipDst; 
          SendTcpRst( pIp );
          FreeBuffer( pp );
          DBPRINT( DB_TCPIP, "Listen sent reset\r\n" );
          return;
       }
       else if ( ( pTcp->flags & TCP_SYN ) &&
                 ( cur_listen /*listening sockets */ < max_listining_sockets ) )
       {
          register PSOCKET ps = GetFreeSocket();
          if ( ps == NULL )
          {
             DBPRINT( DB_TCPIP, "Listen failed to get socket\r\n" );
             FreeBuffer( pp );
             return;
          }

          cur_listen /*listening sockets */++;
          {
             /*Critical Section */ 
             OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
             //Now to initialize the fields.....

             if ( ( IsMyIp( pIp->ipSrc ) ) ||
                  ( ( pIp->ipSrc & 0xFF000000 ) == 0x7F000000 ) )
             {
                ps->bLoopBack = TRUE;
             }
             else
             {
                ps->bLoopBack = FALSE;
             }

             ps->his_ip = pIp->ipSrc;
             ps->his_port = pTcp->srcPort;
             ps->my_port = pTcp->dstPort;
             ps->my_if_ip = pIp->ipDst;
             ps->TxBuffer.Reset( TCP_BUFFER_SEGMENTS );
             ps->RxBuffer.Reset( TCP_BUFFER_SEGMENTS );
          ps->buffers_out_of_order = NULL;
          ps->max_buffers_out_of_order=0;
          ps->cur_buffers_out_of_order=0;
             ps->buffers_tobe_acked_head = NULL;
             ps->buffers_tobe_acked_tail = NULL;
             ps->outstanding_acks = 0;
             ps->sent_and_acked = GetFirstSeq();
             ps->send_not_acked = ps->sent_and_acked;
             ps->his_window = ps->sent_and_acked + pTcp->window;
             ps->largest_window_iveseen = pTcp->window;
             ps->last_seq_I_rxed = pTcp->SeqNumber + 1;
             ps->my_last_window = 0;
             ps->mss = DEFAULT_TCP_MSS;
             ps->tick_RTTVAR = DEFAULT_TCP_RTTVAR  ;//Should calc to RTO of 3 secs in ticks.
             ps->m_pListening = this;
             ps->RTO = DEFAULT_INITAL_RTO;
             ps->dup_Ack = 0;
             ps->gpflags = TCP_USER_CLOSED | TCP_INLISTENCNT; /* The user has not yet see this socket */
             ps->sock_err = 0;
             ps->mac_shortcut = GetEframe( pp )->src_addr;
             ps->Intf_shortcut = pp->bInterfaceNumber;
             ps->options = 0;

             OSSemInit( &ps->m_OsSemaphore, 0 );
             active_list.AddSocket( ps );
#ifdef SNMP
             if ( pAddTableElementtcpConnEntry != NULL )
             {
                pAddTableElementtcpConnEntry( ( void * ) ps,
                                              EthernetIP,
                                              ps->my_port,
                                              ps->his_ip,
                                              ps->his_port );
             } 
#endif

             SNMPCOUNT( snmp_var_tcpPassiveOpens );  

             ps->SetState( TCP_STATE_SYN_RCVD );


             if ( ( pTcp->header_len & 0xF0 ) != 0x50 )
             {
                ps->processoptions( pp, pTcp, pIp );
             }


             if ( pp->bBufferFlag & BS_PHY_802_3 )
             {
                /* Adjust maximum buffer size to handle 802.3 */
                if ( mss > ( ETH_MAX_SIZE - ( TCPTOTAL_HEADER_SIZE + 8 ) ) )
                {
                   mss = ETH_MAX_SIZE - ( TCPTOTAL_HEADER_SIZE + 8 );
                }
             }

             ps->SetTimer( TimeTick + TCP_CONN_TO ); //Change this timeout if you want SYN PAckets to timeout quicker.

             //By passing in pp to send state we have effectivly freeded it
             ps->SendState( TCP_SYN | TCP_ACK, pp );
             ps->send_not_acked++; //We are waiting for the ACK of SYN
          }/*Critical Section */
          //Freed at send State
          return;
       }
       else
       {
          //Either not SYN or we have reached max pending
          DBPRINT( DB_TCPIP, "Listen overflow\r\n" );
          FreeBuffer( pp );
          return;
       }

     case TCP_STATE_SYN_SENT:
       //We should only recieve packets with both ACK and SYN
       if ( ( pTcp->flags & TCP_ACK ) &&
            ( pTcp->AckNumber == ( send_not_acked + 1 ) ) &&
            ( pTcp->flags & TCP_SYN ) )
       {
          if ( ( pTcp->header_len & 0xF0 ) != 0x50 )
          {
             processoptions( pp, pTcp, pIp );
          }
          sent_and_acked = pTcp->AckNumber;
          send_not_acked++;
          his_window = sent_and_acked + pTcp->window;
          largest_window_iveseen = pTcp->window;
          last_seq_I_rxed = pTcp->SeqNumber + 1;
          my_last_window = last_seq_I_rxed;
          SetState( TCP_STATE_ESTABLISHED );
          SendState( TCP_ACK, pp );
		  FreeBuffer(buffers_tobe_acked_head);
		  buffers_tobe_acked_head = NULL; 
		  buffers_tobe_acked_tail = NULL;
          SetWriteAvail(GetMyFd() );
          OSSemPost( &m_OsSemaphore );

       }
       else
       {
          FreeBuffer( pp );
       } /* Toss future probe packets */
       return;



     case TCP_STATE_SYN_RCVD:
       if ( ( pTcp->flags & TCP_ACK ) && ( pTcp->AckNumber == send_not_acked ) )
       {
          //We are now established.

          sent_and_acked = pTcp->AckNumber;

          SNMPCOUNT( snmp_var_tcpCurrEstab );
          SetState( TCP_STATE_WAIT_FOR_ACCEPT );
          //We need to find the listening socket that owns us.
          PSOCKET plistener = m_pListening;

          //Now fix SRTT
          tick_SRTT = 0; //See RFC 1122 pg 96
          sock_err = 0;



          //Now we pend to the Listening socket FIFO so accept can unblock
          PSOCKET meptr = this;
          {
            //if ( plistener->RxBuffer.Empty() )
             {
                OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
                SetDataAvail( ( plistener - sockets ) + TCP_SOCKET_OFFSET );
             }
             
             plistener->RxBuffer.WriteData( ( PBYTE ) & meptr, sizeof( meptr ) );
             gpflags &= ~( TCP_INLISTENCNT );
             gpflags |= TCP_INLISTENLIST;
             OSSemPost( &plistener->m_OsSemaphore );
          }
          //Tada we are all done! ready to be accepted
       }
       else
       {
          if ( pTcp->flags & TCP_SYN )
          {
             /* the origional SYN response was lost, we should retransmit */
             SetTimer( TimeTick + TCP_CONN_TO );
             //By passing in pp to send state we have effectivly freeded it
             last_seq_I_rxed = pTcp->SeqNumber + 1; 
             send_not_acked--; //Fix up for resend
             SendState( TCP_SYN | TCP_ACK, pp );
             send_not_acked++; //Fix up for resend
             return;
          }
          FreeBuffer( pp );
          return;
       }
       /*lint -fallthrough */   

       /********* DROP THROUGH!!!!! VERY IMPORTANT ********************************************
       * We are now an established socket, but we need to process any data we recievied and ACK it.*
       ********** DROP THROUGH!!!!! VERY IMPORTANT *******************************************/

     case TCP_STATE_CLOSE_WAIT:
       /*lint -fallthrough */   
     case TCP_STATE_LAST_ACK:
       /*lint -fallthrough */   
     case TCP_STATE_FIN_WAIT_2:
       /*lint -fallthrough */   
     case TCP_STATE_CLOSING:
       /*lint -fallthrough */   
     case TCP_STATE_WAIT_FOR_ACCEPT:
       /*lint -fallthrough */   
     case TCP_STATE_ESTABLISHED:

       if ( IsNullMac( mac_shortcut ) )
       {
          mac_shortcut = GetEframe( pp )->src_addr; 
          Intf_shortcut = pp->bInterfaceNumber;
       }
       
      if ( ( pTcp->header_len & 0xF0 ) != 0x50 )
      {
        processoptions( pp, pTcp, pIp );
      }
      
      if ( pTcp->flags & TCP_ACK )
       {
          ProcessAck( pTcp );
       }
       if ( modulodiff( pTcp->SeqNumber, last_seq_I_rxed ) < 0 )
       {
          SendState( TCP_ACK );
       }
       if ( data_len != 0 )
       {
          ProcessDataSegment( pTcp, data_len, pData,pp );
       }
       if ( pTcp->flags & TCP_FIN )
       {
          ProcessFIN( pTcp, data_len );
       }
       FreeBuffer( pp );
       return;

     case TCP_STATE_FIN_WAIT_1:
       if ( pTcp->flags & TCP_ACK )
       {
          ProcessAck( pTcp );
       }
       if ( data_len != 0 )
       {
          ProcessDataSegment( pTcp, data_len, pData,pp );
       }
       if ( pTcp->flags & TCP_FIN )
       {
          ProcessFIN( pTcp, data_len );
       }
       if ( state != TCP_STATE_CLOSED )
       {
          if ( ( send_not_acked == sent_and_acked ) &&
               ( ( gpflags & TCP_WANTS_TO_CLOSE ) == 0 ) )
          {
             SetState( TCP_STATE_FIN_WAIT_2 );
             semiactive_list.AddSocket( this );
          }
       }
       FreeBuffer( pp );
       return;


     case TCP_STATE_TIME_WAIT:
       FreeBuffer( pp );
       return;
       
      default:
       /* Policy ??? */
       break;   
   }//Case state
   
   FreeBuffer( pp );
   return;
}



//This does the reciever side of Silly window avoidance
WORD socket_struct::MyAdvertisedWindow()
{
   // only advertise a new window if it is at least one buffer in size.
   //my_last_window -----------------------^-------mss------^
   //last_seq_I rxed --------------------^---free----------^

   
   if ( modulodiff( ( my_last_window + mss ), ( last_seq_I_rxed + RxBuffer.LongSpaceAvail() ) ) <0 )
   {
   DWORD  space=RxBuffer.LongSpaceAvail(); 
   if(space>0xFFFF) space=0xFFFF;
      my_last_window = last_seq_I_rxed + space;
   }
   
   DWORD s=modulodiff( my_last_window, last_seq_I_rxed ); 
//   iprintf("Window=%ld\r\n",s);
   
   return (WORD)s;
}


void socket_struct::OpenWindow()
{
   if ( modulodiff( ( my_last_window + mss ), ( last_seq_I_rxed + RxBuffer.LongSpaceAvail() ) ) <=
        0 )
   {
      SendState( TCP_ACK );
   }
}





#define TCP_OPTION_END (0)
#define TCP_OPTION_NOP (1)
#define TCP_OPTION_MSS (2)
#define TCP_OPTION_SACK_PERMIT (4)
#define TCP_OPTION_SACK (5)


typedef struct _SACK_STRUCT
{BYTE kind;
 BYTE len;
 DWORD blocks[8];
 WORD pad;
}__attribute__( ( packed ) ) SACK_STRUCT;



bool fixoverlap(DWORD s1, DWORD e1, DWORD & s2, DWORD & e2)
{
 if (
    (((s2==0) && (e2==0))) || 
    ((modulodiff(s1,s2)<=0) && (modulodiff(e1,e2)>=0)) 
   )
   {//Full overlap or zero
   s2=s1; e2=e1; 
   return true;
   }
 
if ((modulodiff(s1,s2)<=0) && (modulodiff(e1,s2)>=0))
   {
    s2=s1;
    return true;
   }

 
if ((modulodiff(s1,e2)<=0) && (modulodiff(e1,e2)>=0))
   {
   e2=e1;
   return true;
   }
 
return false;
}


void socket_struct::SendState( WORD flags, PoolPtr pp )
{
   if ( pp == NULL )
   {
      pp = GetBuffer();
      if ( pp == NULL )
      {
         return;
      }
   }

   register PTCPPKT pTcp = GetInitTcpPkt( pp );
   pTcp->srcPort = my_port;
   pTcp->dstPort = his_port;
   pTcp->SeqNumber = send_not_acked;
   pTcp->AckNumber = last_seq_I_rxed;
   pTcp->header_len = 0x50;
   pTcp->flags = flags;


   /* If we are sending syn we need to add MSS length option */
   if ( flags & TCP_SYN ) 
   {  
     if ( IsLocal( his_ip, pp->bInterfaceNumber ) ) 
     {
        WORD max_mss = ETH_MAX_SIZE - TCPTOTAL_HEADER_SIZE;
      
        /* Maximum Segment Size (MSS), set to stack request default */
        WORD interfaceMssMax = max_mss;
        InterfaceBlock* interfaceBlockPtr = NULL;
      
        /* Check for interface maximum */
        interfaceBlockPtr = GetInterFaceBlock( pp->bInterfaceNumber );
      if ( ( interfaceBlockPtr != NULL ) && ( interfaceBlockPtr->netMss > 0 ) )
        {
           /* Interface has a maximum set */
           interfaceMssMax = interfaceBlockPtr->netMss;
        }
   
        /* Set maximum segment size */
        if ( max_mss > interfaceMssMax )
        {
           /* Interface size */
           max_mss = interfaceMssMax;
        }
       
        pTcp->header_len = 0x70; //4*5=20
        pTcp->DATA[0] = TCP_OPTION_MSS;
        pTcp->DATA[1] = 0x04;
        pTcp->DATA[2] = ( max_mss >> 8 ) & 0xFF;
        pTcp->DATA[3] = ( max_mss ) & 0xFF; 
        pTcp->DATA[4] = TCP_OPTION_NOP;
        pTcp->DATA[4] = TCP_OPTION_SACK_PERMIT;
        pTcp->DATA[5] = 0x02;
        pTcp->DATA[6] = TCP_OPTION_NOP;
        pTcp->DATA[7] = TCP_OPTION_END;
     }
     else
     {
        pTcp->header_len = 0x70;
        pTcp->DATA[0] = TCP_OPTION_SACK_PERMIT;
        pTcp->DATA[1] = 0x02;
        pTcp->DATA[2] = TCP_OPTION_NOP;
        pTcp->DATA[3] = TCP_OPTION_NOP;
        pTcp->DATA[4] = TCP_OPTION_NOP;
        pTcp->DATA[5] = TCP_OPTION_NOP;
        pTcp->DATA[6] = TCP_OPTION_NOP;
        pTcp->DATA[7] = TCP_OPTION_END;

     }
   }
   else
   {//Not SYN could do SACK
   if((cur_buffers_out_of_order) && (gpflags & TCP_SAW_SACK)) 
      {
      SACK_STRUCT *pSack=(SACK_STRUCT *) pTcp->DATA;

      for(int i=0; i<8; i++) pSack->blocks[i]=0;
      pSack->pad=0;
        pSack->kind=TCP_OPTION_SACK;
        
      PoolPtr poolPtr=buffers_out_of_order;

      while(poolPtr)
      {
         register PIPPKT tcpPktPtr = GetIpPkt( poolPtr );
         PTCPPKT pTcp =GetTcpPkt(poolPtr);
         WORD data_len = GetTcpDataLen( tcpPktPtr, pTcp );

          DWORD sseq=pTcp->SeqNumber;
         DWORD eseq=sseq+data_len+1;

         for(int i=0; i<4; i++)
         {
             if (fixoverlap(sseq,eseq,pSack->blocks[i*2],pSack->blocks[i*2+1])) break;
         }//For scan blocks
         poolPtr=poolPtr->pPoolNext;
        
      }
      //Now scan buffers and make chunks

    if(pSack->blocks[6] || pSack->blocks[7]) //Check block 3 for overlap, cant overlap 2 by definition
      {
       if(fixoverlap(pSack->blocks[6],pSack->blocks[7],pSack->blocks[2],pSack->blocks[3]))
         {//Set 3 overlaped 1
          pSack->blocks[6]=0;
          pSack->blocks[7]=0;
         }
       else
          if(fixoverlap(pSack->blocks[6],pSack->blocks[7],pSack->blocks[0],pSack->blocks[1]))
            {//Set 3 overlaped 0
               pSack->blocks[6]=0;
               pSack->blocks[7]=0;
             }

      }

    if(pSack->blocks[4] || pSack->blocks[5]) //Check block 2 for overlap
      {
       if(fixoverlap(pSack->blocks[4],pSack->blocks[5],pSack->blocks[2],pSack->blocks[3]))
         {//Set 2 overlaped 1
          pSack->blocks[4]=pSack->blocks[6];
          pSack->blocks[5]=pSack->blocks[7];
          pSack->blocks[6]=0;
          pSack->blocks[7]=0;
         }
       else
         if(fixoverlap(pSack->blocks[6],pSack->blocks[7],pSack->blocks[0],pSack->blocks[1]))
            {//Set 2 overlaped 0
            pSack->blocks[4]=pSack->blocks[6];
            pSack->blocks[5]=pSack->blocks[7];
            pSack->blocks[6]=0;
               pSack->blocks[7]=0;
             }

      }

     if(pSack->blocks[2] || pSack->blocks[3]) //Check block 1 for overlap
      {
       if(fixoverlap(pSack->blocks[2],pSack->blocks[3],pSack->blocks[0],pSack->blocks[1]))
         {//Set 2 overlaped 1
          pSack->blocks[2]=pSack->blocks[4];
          pSack->blocks[3]=pSack->blocks[5];
          pSack->blocks[4]=pSack->blocks[6];
          pSack->blocks[5]=pSack->blocks[7];
          pSack->blocks[6]=0;
          pSack->blocks[7]=0;
         }
      }


      int nrec=0;
      if(pSack->blocks[6]) nrec=4;
      else
      if(pSack->blocks[4]) nrec=3;
      else
      if(pSack->blocks[2]) nrec=2;
      else
      nrec=1;

      pSack->len=nrec*8+2;
        
        pTcp->header_len=(BYTE) ((5+((pSack->len+2)/4))<<4);
      
     }//We have out of order buffers
   }//We were not a SYN packet

      pTcp->window = MyAdvertisedWindow();

   pTcp->csum = 0;
   pTcp->urgent = 0;



   TcpSend( this, his_ip, pp, 0 );
}




void socket_struct::processoptions(  PoolPtr poolPtr, PTCPPKT pTcp, PIPPKT pIp )
{
   register PBYTE pb = pTcp->DATA;
   PBYTE pbmax = pb + ( ( pTcp->header_len >> 2 ) ) - 20;
   //iprintf("**********Processing OPTIONS %p  %p\r\n", pb,pbmax);

   while ( ( pb ) && ( pb < pbmax ) )
   {
     //iprintf("Doing Option %d\r\n",*pb);
      switch ( *pb )
      {
        case TCP_OPTION_END:
          //End of options
          pb = NULL;
          break;
        case TCP_OPTION_NOP:
          //NOP
          pb++;
          break;
     case TCP_OPTION_SACK_PERMIT:
         gpflags|=TCP_SAW_SACK;   
         pb+=2;
      //   iprintf("****************SAW SACK on connect ********************");
        break;

        case TCP_OPTION_MSS:
        {
            /* Maximum Segment Size (MSS), set to stack request default */
            WORD interfaceMssMax = ( ETH_MAX_SIZE - TCPTOTAL_HEADER_SIZE );
            InterfaceBlock* interfaceBlockPtr = NULL;
           
            /* Get option value requested */
            WORD optionMss = *( ( PWORD ) ( pb + 2 ) );
            pb += 4;
         
            /* Check for interface maximum */
            interfaceBlockPtr = GetInterFaceBlock( poolPtr->bInterfaceNumber );
            if ( ( interfaceBlockPtr != NULL ) && 
                  ( interfaceBlockPtr->netMss > 0 ) )
            {
               /* Interface has a maximum set */
               interfaceMssMax = interfaceBlockPtr->netMss;
            }
         
            /* Set segment size */
            if ( optionMss >= interfaceMssMax )
            {
               /* Interface size */
                mss = interfaceMssMax;
            }
            else
            {
               /* Segment size as requested */
               mss = optionMss;
            }
            /* Safety Check */
            if ( optionMss > ( ETH_MAX_SIZE - TCPTOTAL_HEADER_SIZE ) )
            {
                mss = ETH_MAX_SIZE - TCPTOTAL_HEADER_SIZE;
            }
        }
        break;
       default:
          //We do not do any other options.
          {
             PBYTE pnew = pb + ( *( pb + 1 ) );
             //Check for misformed packets or infinite loops...
             if ( ( pb == pnew ) || ( pnew >= pbmax ) )
             {
                DBPRINT( DB_TCPIP, "Escaped Unknown Option :" );
                DBNUM( DB_TCPIP, ( int ) * pb );
                pb = NULL;
             }
             else
             {
                pb = pnew;
             }
          }
          break;
      }
   }
}








void TCPTimerTask( void *p )
{
   current_next_time_event = MAXTICKS;
   DWORD temp_now,temp_next;
   DWORD next_semi = TimeTick + TCP_2MSL_WAIT;  
   DWORD nearest;

   while ( 1 )
   {
     nearest =TimeTick + TCP_2MSL_WAIT; 
      
     //Find the nearest time that needs service.
	 OSCritEnter(&TCP_critical_section, 0);
	 for ( register int i = 0; i < TCP_SOCKET_STRUCTS; i++ )
	 {
		if ( sockets[i].state != TCP_STATE_CLOSED )
		{
			if ( sockets[i].NextTimeAction <= TimeTick )
			{
				sockets[i].ProcessTimer();
			}
			if ( sockets[i].NextTimeAction < nearest )
			{
				nearest = sockets[i].NextTimeAction;
			}
		}
	 }

   //This assignment resets current_next_time_event,
   //all touching of current_next_time_Event must be inside
   //TCP_Critical_secontion protection.
	current_next_time_event = nearest;

	  if ( next_semi < current_next_time_event )
	  {
		 current_next_time_event = next_semi;
	  }
	OSCritLeave(&TCP_critical_section);



     //Now process the semiactive list...
      if ( next_semi < TimeTick )
      {
         next_semi = semiactive_list.processTick();
      }


     //Now decide if we need to sleep or just fall through 
     //We are using temps to prevent race conditions with TimeTick
     //And Current_next_time_event
     temp_now=TimeTick;
     temp_next=current_next_time_event;

     if(temp_next>temp_now)
          OSSemPend( &TCP_TimerSemaphore, temp_next-temp_now );
   }//While 1
}







void socket_struct::SetTimer( DWORD newtime )
{
   OSCritEnter(&TCP_critical_section, 0);

   if ( newtime != NextTimeAction )
   {
      NextTimeAction = newtime;

      if ( newtime < current_next_time_event )
      {
         current_next_time_event = newtime;
         OSSemPost( &TCP_TimerSemaphore );
      }
   }
   OSCritLeave(&TCP_critical_section);
}



void socket_struct::ProcessTimer()
{
   if ( state == TCP_STATE_SYN_RCVD )
   {
      //We timed out on Syn connection
      SetTimer( MAXTICKS );
      CleanUpDeletedListener();
      DoClose( TCP_ERR_TIMEOUT );
      DoCleanup();
      return;
   }

   //Now we check to see if we need to retransmit
   //There are two checks
   //1)Is there anything on the needs to be Acked queue?
   //2)Has this packet timed out yet?
   if ( next_retransmit <= TimeTick )
   {
      if ( buffers_tobe_acked_head )
      {
         DebugTrace( this, NULL, NULL, DT_RETRANSMIT );
         DoRetransmit( TRUE );
      }
      else
      {
         if ( gpflags & TCP_ZERO_PROBING )
         {
        	 OSCritEnter(&TCP_critical_section, 0);
        	 	his_window++;
            	DoRawSend();
            	his_window--;
             OSCritLeave(&TCP_critical_section);
         }
      }
   }//If next_retransmit 

   //If we have done all of the other TCP timer things and we still need to ACK!
   if ( gpflags & NEED_TOACK )
   {
      gpflags &= ~NEED_TOACK;
      SendState( TCP_ACK );
   }

   SetUpNextRetransmit();
}

/********************************************************************/
/*Buffer in the FIFO:                                               */
/*       pBufQueuPointer is used in the FIFO.                                    */
/*       pPoolNext points to the begining of data in the pool.      */
/*       usedsize is the count of bytes stored there.               */
/********************************************************************/

void socket_struct::SaveData( PBYTE src_data, WORD len, BOOL push )
{
 OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock

#ifdef  SPEED_TCP
if(pDataHandle!=NULL)
   {
     int fd = GetMyFd(); 
    if (len>0)  pDataHandle(fd,src_data,len);
    return;
   }
#endif


   BOOL wasempty = RxBuffer.Empty();
   if ( len == 0 )
   {
      DebugTrace( this, NULL, NULL, DT_SAVEZERO );
      return;
   }

   int rv = RxBuffer.WriteData( src_data, len );

   if ( rv )
   {
      OSSemPost( &m_OsSemaphore );
      if ( wasempty )
      {
         SetDataAvail(GetMyFd());
      }
   }
   else
   {
      DBPRINT( DB_TCPDATA, "Failed to write Data \r\n" );
   }
   if ( pReadNotify )
   {
      pReadNotify(GetMyFd() );
   }
}





int getsocketerror( int fd )
{
   int s = fd - TCP_SOCKET_OFFSET;
   if ( fd < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }
   PSOCKET pS = sockets + s;
   return    pS->sock_err;
};


#if defined MULTIHOME || defined AUTOIP
IPADDR GetSocketLocalAddr( int fd )
{
   int s = fd - TCP_SOCKET_OFFSET;
   if ( fd < 0 )
   {
      return 0;
   }
   PSOCKET pS = sockets + s;
   return    pS->my_if_ip;
}
#else
IPADDR GetSocketLocalAddr( int fd )
{
   int s = fd - TCP_SOCKET_OFFSET;
   if ( fd < 0 )
   {
      return 0;
   }
   PSOCKET pS = sockets + s;
   if ( pS->my_if_ip == 0 )
   {
      return GetSrcIp( pS->his_ip );
   }
   else
   {
      return  pS->my_if_ip;
   }
}
#endif


IPADDR GetSocketRemoteAddr( int fd )
{
   int s = fd - TCP_SOCKET_OFFSET;
   if ( fd < 0 )
   {
      return 0;
   }
   PSOCKET pS = sockets + s;
   return    pS->his_ip;
}


WORD GetSocketRemotePort( int fd )
{
   int s = fd - TCP_SOCKET_OFFSET;
   if ( fd < 0 )
   {
      return 0;
   }
   PSOCKET pS = sockets + s;
   return    pS->his_port;
}

WORD GetSocketLocalPort( int fd )
{
   int s = fd - TCP_SOCKET_OFFSET;
   if ( fd < 0 )
   {
      return 0;
   }
   PSOCKET pS = sockets + s;
   return    pS->my_port;
}






//Define the interface to the external functions.
extern "C"
{
   int _Socket_Private_close( int fd )
   {
      OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
      int sl = fd - TCP_SOCKET_OFFSET;
      if ( sl < 0 )
      {
         return TCP_ERR_NOSUCH_SOCKET;
      }

      PSOCKET ps = sockets + sl;

      ps->close_time = TimeTick; 

      DebugTrace( ps, NULL, NULL, DT_CLOSE );

      if ( ( !ps->TxBuffer.Empty() ) &&
           ( modulodiff( ps->send_not_acked, ps->his_window ) < 0 ) )
      {
         ps->DoRawSend();
      }

      ClrDataAvail( fd );
      {
         OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
         ps->gpflags |= TCP_USER_CLOSED;
         if ( ps->state == TCP_STATE_CLOSED )
         {
            /* we are probably on the user closed list */
            RemoveFromUserCloseList( ps );
            return TCP_ERR_NORMAL;
         }
      }
      return ps->DoClose( TCP_ERR_NORMAL );
   }


   /********************************************************************/
   /*Buffer in the FIFO:                                               */
   /*       pBufQueuePointer is used in the FIFO.                                    */
   /*       pPoolNext points to the begining of data in the pool.      */
   /*       usedsize is the count of bytes stored there.               */
   /********************************************************************/
   int SockReadWithTimeout( int fd, char *buf, int nbytes, DWORD timeout )
   {
      int sl = fd - TCP_SOCKET_OFFSET;
     
     DWORD start_time=TimeTick;

      if ( sl < 0 )
      {
         return TCP_ERR_NOSUCH_SOCKET;
      }

      register PSOCKET pL = sockets + sl;

      DebugTrace( pL, NULL, NULL, DT_ReadCall );

      if ( pL->state < TCP_STATE_WAIT_FOR_ACCEPT )
      {
         return TCP_ERR_NOCON;
      }

      int rv;

      do
      {
         pL->nPendingCalls++;
         if ( pL->sock_err == 0 )
         {
            if ( OSSemPend( &pL->m_OsSemaphore, timeout ) == OS_TIMEOUT )
            {
               pL->nPendingCalls--;
               DBPRINT( DB_TCPDATA, "Timed out on Read\r\n" );
               DebugTrace( pL, NULL, NULL, DT_ReadFail1 );
               if ( ( pL->RxBuffer.Empty() ) )
               {
                  return 0;
               }
               else
               {
                  DBPRINT( DB_TCPDATA, "Socket Semaphore Error!\r\n" );
                  DebugTrace( pL, NULL, NULL, DT_ReadFail2 );
                  return 0;
               }
            }
         }

         pL->nPendingCalls--;

         if ( ( pL->sock_err ) && ( pL->RxBuffer.Empty() ) )
         {
            DBPRINT( DB_TCPDATA, "Socket Returned Error\r\n" );
            DebugTrace( pL, NULL, NULL, DT_ReadError );
            return pL->sock_err;
         }
		/* Critical block */
		OSCritEnter(&TCP_critical_section, 0);
		if ( ( pL->RxBuffer.Empty() ) )
		{
		   DBPRINT( DB_TCPDATA, "Internal buffer empty Error\r\n" );
		   DebugTrace( pL, NULL, NULL, DT_ReadFail4 );
		   //RawDumpTcpDebug(pL);
		}

		rv = pL->RxBuffer.ReadData( ( PBYTE ) buf, nbytes );
		pL->OpenWindow();

		if ( ( pL->RxBuffer.Empty() ) )
		{
		   ClrDataAvail( fd );
		}
		OSCritLeave(&TCP_critical_section);
        /* Critical block */
      if ((rv==0) && (timeout) && ((start_time+timeout)<TimeTick)) return 0;
      }
      while ( rv == 0 );
      {
         /* Critical block */
    	 OSCritEnter(&TCP_critical_section, 0);
         if ( !pL->RxBuffer.Empty() )
         {
            OSSemPost( &pL->m_OsSemaphore );
         }
         OSCritLeave(&TCP_critical_section);
      }/* Critical block */

      return rv;
   }



   int _Socket_Private_read( int fd, char *buf, int nbytes )
   {
      return SockReadWithTimeout( fd, buf, nbytes, 0 );
   }



   int _Socket_Private_write( int fd, const char *buf, int nbytes )
   {
      int sl = fd - TCP_SOCKET_OFFSET;
      if ( sl < 0 )
      {
         return TCP_ERR_NOSUCH_SOCKET;
      }
      register PSOCKET ps = sockets + sl;
      if ( ps->state < TCP_STATE_WAIT_FOR_ACCEPT )
      {
         return TCP_ERR_NOCON;
      }
      if ( ps->state >= TCP_STATE_CLOSE_WAIT )
      {
         return TCP_ERR_CLOSING;
      }
      if ( ps->gpflags & TCP_WANTS_TO_CLOSE )
      {
         return TCP_ERR_CLOSING;
      }


      if ( ps->TxBuffer.Full() )
      {
         fd_set write_fds;
         fd_set error_fds;
         FD_ZERO( &write_fds );
         FD_ZERO( &error_fds );
         FD_SET( fd, &write_fds );
         FD_SET( fd, &error_fds );
         if ( select( FD_SETSIZE,
                      ( fd_set * ) 0,
                      &write_fds,
                      &error_fds,
                      TCP_WRITE_TIMEOUT ) == 0 )
         {
            return 0;
         }
         if ( !FD_ISSET( fd, &write_fds ) )
         {
            if ( ps->state < TCP_STATE_WAIT_FOR_ACCEPT )
            {
               return TCP_ERR_NOCON;
            }
            if ( ps->state >= TCP_STATE_CLOSE_WAIT )
            {
               return TCP_ERR_CLOSING;
            }
            if ( ps->gpflags & TCP_WANTS_TO_CLOSE )
            {
               return TCP_ERR_CLOSING;
            }
            return 0;
         }
      }

      int ntotal;
      {
         OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock

         ntotal = ps->TxBuffer.WriteData( (PBYTE)buf, nbytes ); /*lint !e1773 Const cast away */
         if ( ps->TxBuffer.Full() )
         {
            ClrWriteAvail( fd );
         }
      }


      if ( ( ps->outstanding_acks ) &&
           ( ps->TxBuffer.SpaceUsed() < ( ps->mss / 2 ) ) &&
           ( ( ps->options & SO_NONAGLE ) == 0 ) )
      {
         return ntotal;
      }


      WORD spaceavail = modulodiff( ps->send_not_acked, ps->his_window );

      if ( ( ps->TxBuffer.SpaceUsed() >= ps->mss ) && ( spaceavail ) )
      {
         ps->DoRawSend(); 
         spaceavail = modulodiff( ps->send_not_acked, ps->his_window );
      }

      if ( ( spaceavail ) &&
           ( !( ps->options & SO_NOPUSH ) ) &&
           ( !ps->TxBuffer.Empty() ) )
      {
         ps->DoRawSend();
      }
      return ntotal;
   }





   int abortsocket( int fd )
   {
      int sl = fd - TCP_SOCKET_OFFSET;
      if ( sl < 0 )
      {
         return TCP_ERR_NOSUCH_SOCKET;
      }

      register PSOCKET ps = sockets + sl;

      SNMPCOUNT( snmp_var_tcpOutRsts );  
      ps->SendState( TCP_RST );

      OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/

      switch ( ps->state )
      {
        case TCP_STATE_LISTEN :
#ifdef SNMP
          if ( pRemoveTableElementtcpConnEntry != NULL )
          {
             pRemoveTableElementtcpConnEntry( ps );
          }
#endif

          // Remove the socket from the current head to temporarily orphan it
          TAILQ_REMOVE(&listen_list_head, ps, Queue);
          ps->Head = NULL;
          ps->Queue.tqe_next = NULL;
          ps->Queue.tqe_prev = NULL;
          break;
        case TCP_STATE_CLOSED:
        	active_list.RemoveSocket(ps);
          break;

        default:
#ifdef SNMP
          if ( pRemoveTableElementtcpConnEntry != NULL )
          {
             pRemoveTableElementtcpConnEntry( ps );
          }
#endif

          active_list.RemoveSocket( ps );
      }


      ps->SetState( TCP_STATE_CLOSED );
      ps->DoCleanup();
      ps->ClearPending( TCP_ERR_CON_ABORT );

      // Add the socket to the free list
      TAILQ_INSERT_TAIL(&free_list_head, ps, Queue);

      // Update the sockets head
      ps->Head = &free_list_head;
      return TCP_ERR_NORMAL;
   }
}//Extern C


//#define FASTTCP2


/*******************************************************************************************/
/*                                                                                         */
/*This function will move packets from the not yet sent to the sent and not yet acked list.*/
/*It can be called either as the result of ann ACK where the window opened up...           */
/*Or as the result of a user action that put stuff on the to be sent pile.                 */
/*We assume that the buffers put on the tobe sent lsit have data starting at the point     */
/*where a TCP packet with no options would start.                                          */
/*The IP/TCP headers are not yet setup.                                                    */
/*The total TCP data in the packet can be had by usedsize-TCPTOTAL_HEADER_SIZE             */
/*                                                                                         */
/*******************************************************************************************/
void socket_struct::DoRawSend()
{
   DWORD csum;
   OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock


   while ( ( !TxBuffer.Empty() ) &&
           ( modulodiff( send_not_acked, his_window ) < 0 ) &&
           ( outstanding_acks < MAX_OUTSTANDING_ACK_BUFFERS ) )
   {
      //While we can send
      register PoolPtr pp = GetBuffer();
      if ( pp == NULL )
      {
         return;
      }

      register PTCPPKT pTcp = GetInitTcpPkt( pp );
      pTcp->srcPort = my_port;
      pTcp->dstPort = his_port;
      pTcp->SeqNumber = send_not_acked;
      pTcp->AckNumber = last_seq_I_rxed;
      pTcp->header_len = 0x50;
      pTcp->flags = TCP_PSH | TCP_ACK;
      if ( gpflags & NEED_TOACK )
      {
         pTcp->flags |= TCP_ACK;
         gpflags &= ~NEED_TOACK;
      }

         pTcp->window = MyAdvertisedWindow();

      pTcp->csum = 0;
      pTcp->urgent = 0;
      int bytes_to_send = modulodiff( his_window, send_not_acked );

      if ( bytes_to_send > mss )
      {
         bytes_to_send = mss;
      }
      {
         int wasfull = TxBuffer.Full();

         bytes_to_send = TxBuffer.ReadDatawSum( pTcp->DATA, bytes_to_send, csum );



         if ( ( wasfull ) && ( !TxBuffer.Full() ) )
         {
            SetWriteAvail(GetMyFd() );
         }

         //The TRUE at the end of the send causes us to not free the buffer!
         pp->usedsize = TCPTOTAL_HEADER_SIZE + bytes_to_send;
         send_not_acked += bytes_to_send; /*lint !e737 Loss of sign in promotion from int to unsigned long */
      }

      pp->dwTime = TimeTick;
      TcpSendwSum( this, his_ip, pp, bytes_to_send, TRUE, csum );



      //This is a singly linked list.
      //So we set our next to null
      pp->pPoolNext = NULL;
      {
         /* Locking section for manipulating the topbe acked stuff */
         outstanding_acks++;
         if ( buffers_tobe_acked_tail )
         {
            buffers_tobe_acked_tail->pPoolNext = pp;
         }
         else
         {
            buffers_tobe_acked_head = pp; //Tail was null so head must be also
            //We also must
            SetUpNextRetransmit();
         }

         //now set tail to us
         buffers_tobe_acked_tail = pp;
         pp->pPoolPrev = NULL; //Set the retransmit count =0;
      }/* End of locking section */
   }//While we can send

   if ( ( TxBuffer.Empty() ) && ( gpflags & TCP_WANTS_TO_CLOSE ) )
   {
      PoolPtr pp = GetBuffer();
      if ( pp == NULL )
      {
         return;
      }

      register PTCPPKT pTcp = GetInitTcpPkt( pp );
      pTcp->srcPort = my_port;
      pTcp->dstPort = his_port;
      pTcp->SeqNumber = send_not_acked;
      pTcp->AckNumber = last_seq_I_rxed;
      pTcp->header_len = 0x50;
      pTcp->flags = TCP_FIN | TCP_ACK;


      send_not_acked++; /*Account for FIN  CHANGED 6/30/99 PTB*/

      if ( gpflags & NEED_TOACK )
      {
         gpflags &= ~NEED_TOACK;
      }

         pTcp->window = MyAdvertisedWindow();

      pTcp->csum = 0;
      pTcp->urgent = 0;

      pp->usedsize = TCPTOTAL_HEADER_SIZE;

#ifdef _DEBUG
      USER_ENTER_CRITICAL();
      DBPRINT( DB_TCPIP, "Sending FIN to :" );
      DBNUM( DB_TCPIP, his_port ); 
      DBPRINT( DB_TCPIP, "\r\n" );
      USER_EXIT_CRITICAL();
#endif

      pp->dwTime = TimeTick;

      TcpSend( this, his_ip, pp, 0, TRUE );


      //This is a singly linked list.
      //So we set our next to null
      pp->pPoolNext = NULL;
      {
         /* Locking section for manipulating the topbe acked stuff */
         gpflags &= ~TCP_WANTS_TO_CLOSE;
         gpflags |= TCP_FIN_SENT;

         outstanding_acks++;
         if ( buffers_tobe_acked_tail )
         {
            buffers_tobe_acked_tail->pPoolNext = pp;
         }
         else
         {
            buffers_tobe_acked_head = pp; //Tail was null so head must be also
            //We also must
            SetUpNextRetransmit();
         }

         //now set tail to us
         buffers_tobe_acked_tail = pp;

         pp->pPoolPrev = NULL; //Set the retransmit count =0;
      }/* End Locking section */
   }//Send the FIN
}

/*******************************************************************************************/
/*This function will handle all ot the ACKs and setup retransmit timers etc....            */
/*It's purpose in normal time is to free tobe acked packets from the tobe acked list.      */
/*******************************************************************************************/
void socket_struct::ProcessAck( PTCPPKT pTcp )
{
   DWORD ackv = pTcp->AckNumber;

   his_window = ackv + pTcp->window;
   if ( pTcp->window > largest_window_iveseen )
   {
      largest_window_iveseen = pTcp->window;
   }


   if ( pTcp->window != 0 )
   {
      gpflags &= ~TCP_ZERO_PROBING;
   }


   if ( ackv == sent_and_acked )
   {
      //Duplicate ACK or Zero window expansion!
      if ( modulodiff( send_not_acked, his_window ) < 0 )
      {
         DoRawSend();
      }


      if ( buffers_tobe_acked_head == NULL )
      {
         return;
      }

      dup_Ack++;
      if ( dup_Ack >= 3 )
      {
         OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
         DebugTrace( this, NULL, NULL, DT_RETRANSMIT );
         DoRetransmit( FALSE );
         SetUpNextRetransmit();
      }
      return;
   }
   else
   {
      dup_Ack = 0;
   }

   if ( modulodiff( ackv, sent_and_acked ) <= 0 )
   {
      DBPRINT( DB_TCPIP, "Ack Error\r\n" );
      return;
   }
   sent_and_acked = ackv;


   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   register PoolPtr pp = buffers_tobe_acked_head;

   while ( pp )
   {
      PIPPKT pIp = GetIpPkt( pp );
      PTCPPKT pTcpTest = GetTcpPkt( pp );
      DWORD lastseq = ( GetTcpDataLen( pIp, pTcpTest ) + pTcpTest->SeqNumber );
      if ( pTcpTest->flags & TCP_FIN )
      {
         lastseq++;
      }

      if ( modulodiff( ackv, lastseq ) >= 0 )
      {
         //We need to free this record and
         //set the head of list to our next.
         buffers_tobe_acked_head = pp->pPoolNext;
         outstanding_acks--;
         //If the next is NULL then we are last and set tail=NULL;
         if ( pp->pPoolNext == NULL )
         {
            buffers_tobe_acked_tail = NULL;
         }

         //We store the retransmit count in pPoolPrev so if this is zero
         //We need to calculate the RTO.
         //If it is not zero we have retranmitted thus due to Karns algorythim we don't calculate a new RTO
         //As it is ambigous, which tranmission was this ACK ing?
         if ( pp->pPoolPrev == NULL )
         {
            CalcRTO( ( DWORD ) pp->dwTime );
         }
         //Free the one we just acked!
         FreeBuffer( pp );
         //Get the next one to test.
         pp = buffers_tobe_acked_head;
      }
      else
      {
         pp = NULL;
      }
   }

   if ( modulodiff( send_not_acked, his_window ) < 0 )
   {
      DoRawSend();
   }


   SetUpNextRetransmit();

   if ( ( gpflags & TCP_FIN_SENT ) && ( buffers_tobe_acked_head == NULL ) )
   {
      //We have received the ACK for the FIN.
      switch ( state )
      {
        case TCP_STATE_LAST_ACK:
#ifdef SNMP
          if ( pRemoveTableElementtcpConnEntry != NULL )
          {
             pRemoveTableElementtcpConnEntry( this );
          }
#endif
          active_list.RemoveSocket( this );
          DoCleanup();
          SetState( TCP_STATE_CLOSED );
          FREESOCKET( this );
          break;

        case TCP_STATE_CLOSING:
          SetState( TCP_STATE_TIME_WAIT );
          semiactive_list.AddSocket( this );
          return;
          
         default:
          /* Policy ??? */
          break;  
      }
   }
}




void socket_struct::CalcRTO( DWORD txtime )
{
   /*From stevens volume 2 page 832
   delta = nticks-srtt;
   srtt= (1-g)*srtt+g*delta
   rttvar=rttvar+ h*(|delta|-rttvar;

   where g= (1/8) and h= 1/4
   I
   */

   /* M = mearured value */
   /* A is smoothed RTT == long  tick_SRTT;   */
   /* D is RTT variance == long  tick_RTTVAT;   */


   register long M = TimeTick - txtime;   /*lint !e713 Loss of precision */

   if ( M <= 0 )
   {
      M = 1;
   }

   register long err = ( M - tick_SRTT );

   tick_SRTT += ( err >> 3 ); /*lint !e704 Shift right of signed quantity */

   if ( err < 0 )
   {
      err = -err;
   }

   tick_RTTVAR += ( ( err - tick_RTTVAR ) >> 2 ); /*lint !e704 Shift right of signed quantity */

   RTO = tick_SRTT + 4 * tick_RTTVAR;


   if ( RTO > TCP_MAX_RTO )
   {
      RTO = TCP_MAX_RTO;
   }
   else if ( RTO < TCP_MIN_RTO )
   {
      RTO = TCP_MIN_RTO;
   }
}




void socket_struct::DoRetransmit( BOOL count )
{
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   register PoolPtr pp = buffers_tobe_acked_head;
   if ( pp == NULL )
   {
      DebugTrace( this, NULL, NULL, DT_NULLRT );
      return;
   }

   register DWORD rtc = ( DWORD ) pp->pPoolPrev; //Set the number of times this has been retransmitted.



   if ( count ) /* Only count the timer initiated retransmits */
   {
      rtc++;
      if ( ( rtc > 6 ) && ( gpflags & TCP_ZERO_PROBING ) )
      {
         rtc--;
      }
      pp->pPoolPrev = ( PoolPtr ) rtc;
   }
   if ( rtc > 2 )
   {
      mac_shortcut = ENET_ZERO; /* Nuke the mac address hint */
      Intf_shortcut = 0;
   }
   if ( ( rtc > MAX_TCP_RETRY ) && ( !( gpflags & TCP_ZERO_PROBING ) ) )
   {
      //We drop the connection
      DebugTrace( this, NULL, NULL, DT_MAXRETRY_CLOSE );
      DBPRINT( DB_TCPIP, "Socket closed in Timeout" );
      DoClose( TCP_ERR_TIMEOUT );
      return;
   }

   //IF we got here we know that we need to retransmit the top buffer in the
   //Not yet acked pool.
   //The packet is completly unchanges so we just send it again unchanged

   //If it is still parked in the send queue then bail...
   if ( pp->bBuffer_state != BO_SOFTWARE )
   {
      DebugTrace( this, NULL, NULL, DT_NULLRT );
      return;
   }

   PIPPKT pIp = GetIpPkt( pp );
   DebugTrace( this, pIp, NULL, DT_RETRANSMIT );

   pp->dwTime = TimeTick;

   IncUsageCount( pp );

   frames_tx_tcp++;
   SNMPCOUNT( snmp_var_tcpRetransSegs );
   SNMPCOUNT( snmp_var_tcpOutSegs );  
   FixHeaderAndSendTcp( pp, pIp, this );
}




int socket_struct::DoClose( int why )
{
OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/

   //we need to release any body that was pending on our state.
   DebugTrace( this, NULL, NULL, DT_EXTDOCLOSE );

   int fd = GetMyFd();

   ClrWriteAvail( fd );
   SetHaveError( fd );

   if ( pReadNotify )
   {
      pReadNotify( -fd );
   }



   if ( why == TCP_ERR_TIMEOUT )
   {
      //Clean up 
      PoolPtr pp = buffers_tobe_acked_head;
      while ( pp )
      {
         buffers_tobe_acked_head = pp->pPoolNext;
         outstanding_acks--;
         FreeBuffer( pp );
         pp = buffers_tobe_acked_head;
      }
      SetTimer( MAXTICKS );
   }



   switch ( state )
   {
     case TCP_STATE_CLOSING:
     case TCP_STATE_LAST_ACK:
     case TCP_STATE_TIME_WAIT:
     case TCP_STATE_CLOSED:
       return TCP_ERR_CLOSING;

     case TCP_STATE_LISTEN:
       {
          PSOCKET ps;
          while ( RxBuffer.ReadData( (PBYTE)&ps, sizeof( ps ) ) == (int)sizeof( ps ) )
          {
             ps->DoClose( why );
          }
       }
       SetState( TCP_STATE_CLOSED );
       ClearPending( TCP_ERR_CLOSING );
#ifdef SNMP
       if ( pRemoveTableElementtcpConnEntry != NULL )
       {
          pRemoveTableElementtcpConnEntry( this );
       }
#endif

       // Remove the socket from our listen head
       TAILQ_REMOVE(&listen_list_head, this, Queue);
       this->Head = NULL;
       this->Queue.tqe_next = NULL;
       this->Queue.tqe_prev = NULL;

       FREESOCKET( this );
       return TCP_ERR_NORMAL;

     case TCP_STATE_SYN_SENT:
       SetState( TCP_STATE_CLOSED );
       ClearPending( TCP_ERR_CLOSING );
#ifdef SNMP
       if ( pRemoveTableElementtcpConnEntry != NULL )
       {
          pRemoveTableElementtcpConnEntry( this );
       }
#endif
       active_list.RemoveSocket( this );
       FREESOCKET( this );
       return TCP_ERR_NORMAL;


     case TCP_STATE_CLOSE_WAIT:
       gpflags |= TCP_WANTS_TO_CLOSE;
       SetState( TCP_STATE_LAST_ACK );
       if ( TxBuffer.Empty() )
       {
          DoRawSend();
       }
       return TCP_ERR_NORMAL;


     case TCP_STATE_SYN_RCVD:
     case TCP_STATE_WAIT_FOR_ACCEPT:
     case TCP_STATE_ESTABLISHED:
       if (( why == TCP_ERR_TIMEOUT ) ||(state!=TCP_STATE_ESTABLISHED))
       {
          DebugTrace( this, NULL, NULL, DT_ERRORTIMO );
#ifdef SNMP
          if ( pRemoveTableElementtcpConnEntry != NULL )
          {
             pRemoveTableElementtcpConnEntry( this );
          }
#endif
          active_list.RemoveSocket( this );
          SetState( TCP_STATE_CLOSED );
          DoCleanup();
          ClearPending( TCP_ERR_TIMEOUT );
          FREESOCKET( this );
       }
       else
       {
          gpflags |= TCP_WANTS_TO_CLOSE;
          SetState( TCP_STATE_FIN_WAIT_1 );
          if ( TxBuffer.Empty() )
          {
             DoRawSend();
          }
       }
       return TCP_ERR_NORMAL;


     case TCP_STATE_FIN_WAIT_1:
     case TCP_STATE_FIN_WAIT_2:
       if ( why == TCP_ERR_TIMEOUT )
       {
          /* We timed out on a fin */
          DebugTrace( this, NULL, NULL, DT_ERRORTIMO );
#ifdef SNMP
          if ( pRemoveTableElementtcpConnEntry != NULL )
          {
             pRemoveTableElementtcpConnEntry( this );
          }
#endif
          active_list.RemoveSocket( this );
          SetState( TCP_STATE_CLOSED );
          DoCleanup();
          ClearPending( TCP_ERR_TIMEOUT );
          FREESOCKET( this );
       }
       return TCP_ERR_NORMAL;
   
     default:
       /* Policy ??? */
       break;  
   }

   return -1;
}


void socket_struct::SetUpNextRetransmit()
{
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/

   if ( buffers_tobe_acked_head == NULL )
   {
      if ( send_not_acked == his_window )
      {
         //The zero window probe...timer....
#ifdef FAST_ZERO_PROBE
         register WORD probetime = 2 * RTO;
         if ( probetime > ( 5 * TICKS_PER_SECOND ) )
         {
            probetime = ( 5 * TICKS_PER_SECOND );
         }
         next_retransmit = TimeTick + probetime;
#else
         next_retransmit = TimeTick + ( 5 * TICKS_PER_SECOND ); 
#endif
         gpflags |= TCP_ZERO_PROBING;
      }
      else
      {
         if ( NextTimeAction == next_retransmit )
         {
            next_retransmit = MAXTICKS;
         }
      }
   }
   else
   {
      register PoolPtr pp = buffers_tobe_acked_head;
      DWORD rtc = ( ( DWORD ) ( pp->pPoolPrev ) );
      if ( rtc > MAX_TCP_RETRY )
      {
         rtc = MAX_TCP_RETRY;
      }
      next_retransmit = pp->dwTime + RTO * TIME_INDEX[rtc];
   }

   SetTimer( next_retransmit );
}

void socket_struct::CleanUpDeletedListener()
{
   PSOCKET plistener = m_pListening; 
   if ( plistener == NULL )
   {
      return;
   }
   if ( plistener->cur_listen )
   {
      plistener->cur_listen--;
   }
}


void socket_struct::ProcessRST( void )
{
   switch ( state )
   {
     case TCP_STATE_LISTEN:
       break;
   case TCP_STATE_SYN_SENT:
       
	   OSSemPost( &m_OsSemaphore );
       SetState( TCP_STATE_CLOSED );
	   DoCleanup();
       ClearPending( TCP_ERR_CON_RESET );
       break;
     case TCP_STATE_SYN_RCVD:
       /* We belong to a listening socket we need to clean up */
       gpflags |= TCP_USER_CLOSED; /* Make sure it actually gets freed */
       CleanUpDeletedListener();
       /*lint -fallthrough */   
     case TCP_STATE_WAIT_FOR_ACCEPT:
       /*lint -fallthrough */   
     case TCP_STATE_ESTABLISHED:
       /*lint -fallthrough */   
     case TCP_STATE_CLOSE_WAIT:
       SNMPCOUNT( snmp_var_tcpEstabResets );
       /*lint -fallthrough */  
     default:
       OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
#ifdef SNMP
       if ( pRemoveTableElementtcpConnEntry != NULL )
       {
          pRemoveTableElementtcpConnEntry( this );
       }
#endif
       active_list.RemoveSocket( this );
       SetState( TCP_STATE_CLOSED );
       DoCleanup();
       ClearPending( TCP_ERR_CON_RESET );
       FREESOCKET( this );
       break;
   }
   return;
}


void socket_struct::ProcessFIN( PTCPPKT pTcp, WORD data_len )
{
   DebugTrace( this, NULL, pTcp, DT_GOTFIN );

   if ( last_seq_I_rxed != ( pTcp->SeqNumber + data_len ) )
   {
      //We lost a segment in transit.
      //So we should send an immediate ACK of the earlier data
      //So we can do fast recovery.
      //The Send will use the passed in pp
      DBPRINT( DB_TCPDATA, "Bad Sequence1\r\n" );
      SendState( TCP_ACK );
      return;
   }

   switch ( state )
   {
     case TCP_STATE_SYN_SENT:
     case TCP_STATE_SYN_RCVD:
     case TCP_STATE_LISTEN:
     case TCP_STATE_TIME_WAIT:
     case TCP_STATE_CLOSED:
     case TCP_STATE_CLOSE_WAIT:
     case TCP_STATE_LAST_ACK:
     case TCP_STATE_CLOSING:
       return;
       //We have received an ACK for the last FIN, but we have not gotten a FIN
     case TCP_STATE_FIN_WAIT_2:
       SetState( TCP_STATE_TIME_WAIT );
       last_seq_I_rxed = pTcp->SeqNumber + data_len + 1;
       SendState( TCP_ACK );
       semiactive_list.AddSocket( this );
       return;

       //Inorder for Us to be here, we must have sent a FIN and not received an ACK or FIN
       //From WAIT_1 we goto ->
     case TCP_STATE_FIN_WAIT_1:
       if ( pTcp->flags & TCP_ACK )
       {
          ProcessAck( pTcp );
       }

       if ( buffers_tobe_acked_head )
       {
          //We have not ACKED everything, so we havent ACKED the FIN
          SetState( TCP_STATE_CLOSING );
          last_seq_I_rxed = pTcp->SeqNumber + data_len + 1;
          SendState( TCP_ACK );
          return;
       }
       else
       {
          SetState( TCP_STATE_TIME_WAIT );
          last_seq_I_rxed = pTcp->SeqNumber + 1 + data_len;
          SendState( TCP_ACK );
          semiactive_list.AddSocket( this );
       }
       break;

   case TCP_STATE_WAIT_FOR_ACCEPT:
   case TCP_STATE_ESTABLISHED:

#ifdef  SPEED_TCP
   if(pDataHandle!=NULL)
   {
    pDataHandle(GetMyFd(),NULL,0);
    pDataHandle=NULL;
   }
#endif

       last_seq_I_rxed = pTcp->SeqNumber + 1 + data_len;

       SetState( TCP_STATE_CLOSE_WAIT );
       SendState( TCP_ACK );
       ClearPending( TCP_ERR_CLOSING );
       break;
       
   default:
      /* Policy ??? */  
       break;
       
   }//Switch
}



int setsockoption( int fd, int option )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }
   PSOCKET pL = sockets + sl;
   pL->options |= option;
   return pL->options;
}


int getsockoption( int fd )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }
   PSOCKET pL = sockets + sl;
   return pL->options;
}


int clrsockoption( int fd, int option )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }
   PSOCKET ps = sockets + sl;
   ps->options &= ~option; /*lint !e502 Expected unsigned type */

   if ( option & SO_NOPUSH )
   {
      if ( ( !ps->TxBuffer.Empty() ) &&
           ( modulodiff( ps->send_not_acked, ps->his_window ) < 0 ) )
      {
         ps->DoRawSend();
      }
   }

   return ps->options;
}


int setsocketackbuffers( int fd, BYTE val )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }
   PSOCKET ps = sockets + sl;
   ps->MAX_OUTSTANDING_ACK_BUFFERS = val;

   return 0;
}


int SetSocketRxBuffers( int fd, int n )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }

   PSOCKET ps = sockets + sl;

   ps->RxBuffer.SetMaxBuffers( n );
  // int len=ps->RxBuffer.SpaceAvail();

  // iprintf("Set Max Buffers %d  len %d\r\n",n,len);

   return 0;
}

int SetSocketTxBuffers( int fd, int n )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }

   PSOCKET ps = sockets + sl;

   ps->TxBuffer.SetMaxBuffers( n );

   return 0;
}


void socket_struct::TestOutofOrder()
{
PoolPtr pp=buffers_out_of_order;
PoolPtr pnext;



BOOL bGotOne=false;

do{
   bGotOne=false;
   
   pp=buffers_out_of_order;    

   while(pp)
   {
      pnext=pp->pPoolNext;
   
   register PIPPKT pIp = GetIpPkt( pp );
   PTCPPKT pTcp =GetTcpPkt(pp);
   WORD data_len = GetTcpDataLen( pIp, pTcp );
   PBYTE pData = GetTcpData( pTcp );
   
   if ( modulodiff( pTcp->SeqNumber, last_seq_I_rxed ) <= 0 )
      {//Early or right on

      if ( pTcp->SeqNumber != ( last_seq_I_rxed ) )
      {     DWORD dladjust=modulodiff( ( last_seq_I_rxed ), pTcp->SeqNumber);

         pData += modulodiff( ( last_seq_I_rxed ), pTcp->SeqNumber );

         if(data_len<dladjust) data_len=0;
         else
          data_len -= dladjust;
      }

      if(data_len==0)
      {//We have already gotten all this packet 
         cur_buffers_out_of_order--;

         if(pp->pPoolPrev==NULL)
            {
            buffers_out_of_order=pp->pPoolNext; 
            }

         if(pp->pPoolNext) pp->pPoolNext->pPoolPrev=pp->pPoolPrev;
         if(pp->pPoolPrev) pp->pPoolPrev->pPoolNext=pp->pPoolNext;
         //iprintf("Killed an extraone %p %d left\r\n",pp,cur_buffers_out_of_order);

         FreeBuffer(pp);
         bGotOne=true;
      }
      else
      if ( last_seq_I_rxed != my_last_window )
      {
         /* Test for packet length greater than capacity */
         if ( data_len <= RxBuffer.SpaceAvail() )
         {
            last_seq_I_rxed += data_len;
            SaveData( pData, data_len, ( pTcp->flags & ( TCP_FIN | TCP_PSH ) ) );
            /* bGotOne = true;       */
            
            if(pp->pPoolPrev==NULL)
            {
               buffers_out_of_order=pp->pPoolNext; 
            }

            if(pp->pPoolNext) pp->pPoolNext->pPoolPrev=pp->pPoolPrev;
            if(pp->pPoolPrev) pp->pPoolPrev->pPoolNext=pp->pPoolNext;

             cur_buffers_out_of_order--;
            
            if(pp->pPoolPrev==NULL)
            {
               buffers_out_of_order=pp->pPoolNext; 
            }

            if( pp->pPoolNext != NULL ) 
               pp->pPoolNext->pPoolPrev=pp->pPoolPrev;
            //iprintf("Used a saved one %p %d left\r\n",pp,cur_buffers_out_of_order);
            FreeBuffer( pp );
            gpflags |= NEED_TOACK;
            bGotOne = true;
         }
        }

   }
   pp=pnext;
   }

  } while(bGotOne);
  //iprintf("x");

}



void socket_struct::ProcessDataSegment( PTCPPKT pTcp, WORD data_len, PBYTE pData, PoolPtr & pp )
{
   
  

   if ( modulodiff( pTcp->SeqNumber, last_seq_I_rxed ) > 0 )
   {
      //We lost a segment in transit.
      //So we should send an immediate ACK of the earlier data
      //So we can do fast recovery.
      //The Send will use the passed in pp
      
         DBPRINT( DB_TCPDATA, "Bad Sequence1\r\n" );

         if ((max_buffers_out_of_order) && (max_buffers_out_of_order>cur_buffers_out_of_order) && (data_len>0))
         {
            cur_buffers_out_of_order++;

            //iprintf(" %p Stored[%d] on linked list\r\n",pp,cur_buffers_out_of_order);
            //Add to linked list 
            pp->pPoolNext=buffers_out_of_order;
            pp->pPoolPrev=NULL;
            if(pp->pPoolNext) pp->pPoolNext->pPoolPrev=pp;
            buffers_out_of_order=pp;
               IncUsageCount( pp ); 
         }
         SendState( TCP_ACK );
      return;
   }

   if ( pTcp->SeqNumber != ( last_seq_I_rxed ) )
   {
      //To get here the seq number is less than what we expected
      if ( modulodiff( ( pTcp->SeqNumber + data_len ), ( last_seq_I_rxed ) ) <= 0 )
      {
         //We have nothing to process.
         DBPRINT( DB_TCPDATA, "Bad Sequence 2\r\n" );
         SendState( TCP_ACK );
         return;
      }
      pData += modulodiff( ( last_seq_I_rxed ), pTcp->SeqNumber );
      data_len -= modulodiff( ( last_seq_I_rxed ), pTcp->SeqNumber );
   }
   
   if ( last_seq_I_rxed == my_last_window )
   {
      // We received a zero window probe we should ack it.
      SendState( TCP_ACK );
      return;
   }
   else
   {
      /* Test for packet length greater than capacity */
      if ( data_len > RxBuffer.SpaceAvail() )
      {
         /* More data than capacity */
         DBPRINT( DB_TCPDATA, "Data size exceed window\r\n" );
         SendState( TCP_ACK );
         return;
      }
      

      last_seq_I_rxed += data_len;

      SaveData( pData, data_len, ( pTcp->flags & ( TCP_FIN | TCP_PSH ) ) );
      DebugTrace( this, NULL, pTcp, DT_SAVEDATA );
     
     if(cur_buffers_out_of_order) TestOutofOrder(); 


      if ( ( gpflags & NEED_TOACK ) ||
           ( options & SO_NONAGLE ) ||
           ( data_len > ( mss / 2 ) ) )
      {
         gpflags &= ~NEED_TOACK;
         SendState( TCP_ACK );
      }
      else
      {
         gpflags |= NEED_TOACK;
         SetTimer( TimeTick + TCP_ACK_TICK_DLY );
      }
   }



   return;
}


void socket_struct::DoCleanup()
{
   TxBuffer.Reset( 0 ); //The buffer for transmit.
   RxBuffer.Reset( 0 ); //The buffer for received data


   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   register PoolPtr pp = buffers_tobe_acked_head;
   while ( pp )
   {
      buffers_tobe_acked_head = pp->pPoolNext;
      FreeBuffer( pp );
      pp = buffers_tobe_acked_head;
   }
   
   
   pp = buffers_out_of_order;
   while ( pp )
   {
      buffers_out_of_order = pp->pPoolNext;
      FreeBuffer( pp );
      pp = buffers_out_of_order;
   }

   
   buffers_tobe_acked_head = NULL; //Buffers that have been sent, and not ACKED
   buffers_tobe_acked_tail = NULL;
   buffers_out_of_order=NULL;

   max_buffers_out_of_order=0; 
   cur_buffers_out_of_order=0;

   SetTimer( MAXTICKS );

   if ( gpflags & TCP_INLISTENLIST )
   {
      /* We have been accepted into the listen list and must be removed*/
      /* Now we must find the listening socket that has us ...... */

      register PSOCKET ps = FindListenSocket( his_port, his_ip );
      if ( ( ps >= sockets ) && ( ps < ( sockets + TCP_SOCKET_STRUCTS ) ) )
      {
         PoolPtr poolPtr = GetBuffer();
         if ( poolPtr != NULL )
         {
            int n = ps->RxBuffer.ReadData( poolPtr->pData, ETHER_BUFFER_SIZE );
            PSOCKET pscomp = ( PSOCKET ) poolPtr->pData;
            ps->RxBuffer.Reset( 1 );
            while ( n > 0 )
            {
               if ( pscomp + ( n / 4 ) != this )
               {
                  ps->RxBuffer.WriteData( ( PBYTE ) ( pscomp + ( n / 4 ) ),
                                          sizeof( PSOCKET ) );
               } 
               n -= 4;
            }
            OSSemPend( &ps->m_OsSemaphore, 1 ); 
            FreeBuffer( poolPtr );
            if ( ps->cur_listen )
            {
               ps->cur_listen--;
            }
         }
      }
      gpflags &= ~( TCP_INLISTENLIST | TCP_INLISTENCNT );
   }

   if ( gpflags & TCP_INLISTENCNT )
   {
      /* We are in the accepted list count, but not in the data list.*/ 
      PSOCKET plistener = ( PSOCKET ) tick_SRTT;
      if ( ( plistener >= sockets ) && ( plistener < ( sockets + TCP_SOCKET_STRUCTS ) ) )
      {
         if ( plistener->cur_listen )
         {
            plistener->cur_listen--;
         }
         gpflags &= ~( TCP_INLISTENLIST | TCP_INLISTENCNT );
      }
   }
}


void socket_struct::ClearPending( int why )
{
   int fd = GetMyFd();
   if ( why != 0 )
   {
      sock_err = why;
      SetHaveError( fd );
      if ( pReadNotify )
      {
         pReadNotify( -fd );
      }
   }

   if ( nPendingCalls )
   {
      OSSemPost( &m_OsSemaphore );
   }
}

#ifdef _DEBUG
struct dt_Trace_info
{
      DWORD seq;
      DWORD ack;
      PSOCKET ps;
      DWORD time;
      long serr;
      WORD why;
      WORD len;
      WORD srcPort;
      WORD dstPort;
      WORD window;
      BYTE state;
      BYTE flags;
      int line_num;
};

#define MAX_TRACE 1000

dt_Trace_info Trace[MAX_TRACE];
WORD dt_Trace_Get;
WORD dt_Trace_Put;


void DebugTraceFunc( socket_struct *ss, PIPPKT pIp, PTCPPKT pTcp, WORD why, int line )
{
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   dt_Trace_info *dt = &Trace[dt_Trace_Put];
   dt_Trace_Put = ( ( dt_Trace_Put + 1 ) % MAX_TRACE );
   if ( dt_Trace_Put == dt_Trace_Get )
   {
      dt_Trace_Get = ( ( dt_Trace_Get + 1 ) % MAX_TRACE );
   }

   dt->ps = ss;
   if ( ss != NULL )
   {
      dt->serr = ss->sock_err;
   }
   else
   {
      dt->serr = 0;
   }

   dt->line_num = line;

   dt->time = TimeTick;
   if ( ss != NULL )
   {
      dt->srcPort = ss->his_port;
      dt->dstPort = ss->my_port;
      dt->state = ss->state;
   }
   else
   {
      dt->state = TCP_STATE_TIME_WAIT + 1;
   }
   dt->why = why;
   if ( pTcp != NULL )
   {
      dt->seq = pTcp->SeqNumber;
      dt->ack = pTcp->AckNumber;
      dt->flags = pTcp->flags;
      dt->srcPort = pTcp->srcPort;
      dt->dstPort = pTcp->dstPort;
      dt->window = pTcp->window;
      if ( pIp )
      {
         dt->len = GetTcpDataLen( pIp, pTcp );
      }
      else
      {
         dt->len = 0;
      }
   }
   else if ( pIp != NULL )
   {
      PTCPPKT pt = GetTcpPkt( pIp );
      dt->len = GetTcpDataLen( pIp, pt );
      dt->seq = pt->SeqNumber;
      dt->ack = pt->AckNumber;
      dt->flags = pt->flags;
      dt->srcPort = pt->srcPort;
      dt->dstPort = pt->dstPort;
      dt->window = pt->window;
   }
   else
   {
      dt->seq = 0;
      dt->ack = 0;
      dt->flags = 0;
      dt->len = 0;
      dt->window = 0;
   }
}




void EnableTcpDebug( WORD db )
{
   dbFlag = db;
}



void RawDumpTcpDebug( PSOCKET psfilter )
{
   //STATE_CHGE|1234 |[1234:1234]|1234 |1234 |12345678 |12345678 |ESTABLISHED|FSRPAU
   iprintf( "\r\nAction |S#|Ticks|Socket     |Len  |Wind |  Seq    |    Ack  | NewState | Flags\r\n" );
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   while ( dt_Trace_Get != dt_Trace_Put )
   {
      dt_Trace_info *dt = &Trace[dt_Trace_Get];
      dt_Trace_Get = ( ( dt_Trace_Get + 1 ) % MAX_TRACE );
      if ( ( psfilter == NULL ) || ( psfilter == dt->ps ) )
      {
         int fd;
         switch ( dt->why )
         {
           case DT_STATE_CHANGE  :
             iprintf( "STE_CHG" ); break;
           case DT_SEND          :
             iprintf( "SEND   " ); break;
           case DT_DISCARD       :
             iprintf( "DISCARD" ); break;
           case DT_RX            :
             iprintf( "RX     " ); break;
           case DT_CLOSE         :
             iprintf( "CLOSE  " ); break;
           case DT_MAXRETRY_CLOSE:
             iprintf( "MAXRETC" ); break;
           case DT_RETRANSMIT    :
             iprintf( "RETRAN " ); break;
           case DT_EXTDOCLOSE    :
             iprintf( "EXTCLSE" ); break;
           case DT_GOTFIN        :
             iprintf( "GOTFIN " ); break;
           case DT_SAVEDATA      :
             iprintf( "SaveDat" ); break;
           case DT_ReadCall      :
             iprintf( "Rd Call" ); break;
           case DT_ReadFail1      :
             iprintf( "Rd Fail1" ); break;
           case DT_ReadFail2      :
             iprintf( "Rd Fail2" ); break;
           case DT_ReadError      :
             iprintf( "Rd Error" ); break;
           case DT_ReadFail4      :
             iprintf( "Rd Fail4" ); break;
           case DT_SAVEZERO       :
             iprintf( "SaveZero" ); break;
           case DT_ERRORTIMO      :
             iprintf( "Errtoutt" );break;
           case DT_NULLRT         :
             iprintf( "Null RTO" );
         }
         if ( dt->ps == NULL )
         {
            fd = 0;
         }
         else
         {
            fd = ( dt->ps - sockets );
         }

         iprintf( "|%2d|%4ld |[%5d:%5d]|%4x |%4x |%8lx |%8lx |",
                  fd,
                  ( dt->time % 10000 ),
                  ( int ) dt->srcPort,
                  ( int ) dt->dstPort,
                  ( int ) dt->len,
                  ( int ) dt->window,
                  dt->seq,
                  dt->ack );

         switch ( dt->state )
         {
           case TCP_STATE_CLOSED           :
             iprintf( "CLOSED    |" ); break;
           case TCP_STATE_LISTEN           :
             iprintf( "LISTEN    |" ); break;
           case TCP_STATE_SYN_SENT         :
             iprintf( "SYN_SENT  |" ); break;
           case TCP_STATE_SYN_RCVD         :
             iprintf( "SYN_RCVD  |" ); break;
           case TCP_STATE_WAIT_FOR_ACCEPT  :
             iprintf( "Wait4ACPT |" ); break;
           case TCP_STATE_ESTABLISHED      :
             iprintf( "ESTABLISED|" ); break;
           case TCP_STATE_CLOSE_WAIT       :
             iprintf( "CLOSE_WAIT|" ); break;
           case TCP_STATE_LAST_ACK         :
             iprintf( "LAST_ACK  |" ); break;
           case TCP_STATE_FIN_WAIT_1       :
             iprintf( "FIN_WAIT_1|" ); break;
           case TCP_STATE_FIN_WAIT_2       :
             iprintf( "FIN_WAIT_2|" ); break;
           case TCP_STATE_CLOSING          :
             iprintf( "CLOSING   |" ); break;
           case TCP_STATE_TIME_WAIT        :
             iprintf( "TIME_WAIT |" ); break;
           default:
             iprintf( "UNKNOWN     |" ); break;
         }

         if ( dt->flags & TCP_FIN )
         {
            iprintf( "F" );
         }
         if ( dt->flags & TCP_SYN )
         {
            iprintf( "S" );
         }
         if ( dt->flags & TCP_RST )
         {
            iprintf( "R" );
         }
         if ( dt->flags & TCP_PSH )
         {
            iprintf( "P" );
         }
         if ( dt->flags & TCP_ACK )
         {
            iprintf( "A" );
         }
         if ( dt->flags & TCP_URG )
         {
            iprintf( "U" );
         }
         if ( dt->serr )
         {
            iprintf( "E:%ld", dt->serr );
         }

         iprintf( "-@%d", dt->line_num );
         iprintf( "\r\n" );
      }
   }//While
   iprintf( "\r\nAction |S#|Ticks|Socket     |Len  |Wind |  Seq    |    Ack  | NewState | Flags\r\n" );
   iprintf( "Dump Done\r\n" );
}


void DumpTcpDebug()
{
   RawDumpTcpDebug( NULL );
}


void DumpTcpPacket( PIPPKT pIP )
{
   PTCPPKT pTcp = GetTcpPkt( pIP );
   WORD data_len = GetTcpDataLen( pIP, pTcp );
   DumpTcpPacket( pTcp, data_len );
}

void DumpTcpPacket( PTCPPKT pTcp, WORD len )
{
   WORD hlen = ( pTcp->header_len ) >> 2;

   iprintf( "Src Port = %04x  Dest Port %04x\n",
            ( int ) pTcp->srcPort,
            ( int ) pTcp->dstPort );
   iprintf( "Seq Number = %08lx  Ack Number %08lx\n",
            pTcp->SeqNumber,
            ( int ) pTcp->AckNumber );
   iprintf( "Header is %d bytes long  Flags =", ( int ) hlen );

   if ( pTcp->flags & TCP_FIN )
   {
      iprintf( "FIN " );
   }
   if ( pTcp->flags & TCP_SYN )
   {
      iprintf( "SYN " );
   }
   if ( pTcp->flags & TCP_RST )
   {
      iprintf( "RST " );
   }
   if ( pTcp->flags & TCP_PSH )
   {
      iprintf( "PSH " );
   }
   if ( pTcp->flags & TCP_ACK )
   {
      iprintf( "ACK " );
   }
   if ( pTcp->flags & TCP_URG )
   {
      iprintf( "URG " );
   }
   iprintf( "\n" );
   iprintf( "Window = %04x csum=%04x urgent=%04x\n",
            ( int ) pTcp->window,
            ( int ) pTcp->csum,
            ( int ) pTcp->urgent );
   if ( hlen > 20 )
   {
      //We have options
      iprintf( "Options :\n" );
      PBYTE pb = pTcp->DATA;
      while ( pb )
      {
         switch ( *pb )
         {
           case 0:
             iprintf( "End of options \n" );
             pb = NULL;
             break;
           case 1:
             iprintf( "NOP \n" );
             pb++;
             break;
           case 2:
             iprintf( "MSS : %04X\n", ( int ) ( *( ( PWORD ) ( pb + 2 ) ) ) );
             pb += 4;
             break;
           case 3:
             iprintf( "Scale : %02X\n", ( int ) ( *( pb + 2 ) ) );
             pb += 3;
             break;
           case 8:
             iprintf( "Timestamp : time:%08X  echo:08x \n",
                      ( *( PDWORD ) ( pb + 2 ) ),
                      ( *( PDWORD ) ( pb + 6 ) ) );
             pb += 10;
             break;
           default:
             iprintf( "Unknown Option Code %02X length %02x",
                      ( int ) * pb,
                      ( int ) * ( pb + 1 ) );
             pb += *( pb + 1 );
             break;
         }//Switch
         if ( ( pb ) && ( ( ( pb - ( pTcp->DATA ) ) + 20 ) >= hlen ) )
         {
            pb = NULL;
         }
      }
   }//We have options
   iprintf( "Data : \n" );
   ShowData( GetTcpData( pTcp ), len );

   iprintf( "End of packet \n" );
}



#else //Debug

void DumpTcpDebug()
{
};
void EnableTcpDebug( WORD )
{
};
void DumpTcpPacket( PIPPKT pIP )
{
};
#endif


HashSocketList::HashSocketList()
{
};

void HashSocketList::Init( void ) const
{
   for ( register int i = 0; i < SocketHashSize; i++ )
   {
	  TAILQ_INIT(&m_slist[i]);
   }
}


PSOCKET HashSocketList::findsocket( WORD dstport, WORD srcport, DWORD hisip ) const
{
   register DWORD hash = dstport;
   hash += hisip;
   hash += srcport;
   hash = hash % SocketHashSize;
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   register PSOCKET ps = NULL;

   TAILQ_FOREACH(ps, &m_slist[hash], Queue) {
	  if ( ( ps->my_port == dstport ) &&
		   ( ps->his_port == srcport ) &&
		   ( ps->his_ip == hisip ) )
	  {
		 return ps;
	  }
   };

   if ( IsMyIp( hisip ) )
   {
	  TAILQ_FOREACH(ps, &m_slist[hash], Queue) {
         if ( ( ps->my_port == dstport ) &&
              ( ps->his_port == srcport ) &&
              ( ( ps->his_ip & 0x7F000000 ) == 0x7F000000 ) )
         {
            return ps;
         }
      };
   }

   return NULL;
}




void HashSocketList::AddSocket( PSOCKET psadd ) const
{
   register DWORD hash = psadd->my_port;

   if ( ( psadd->his_ip & 0x7F000000 ) == 0x7F000000 )
   {
      hash += InterfaceIP( GetFirstInterface() );
   }
   else
   {
      hash += psadd->his_ip;
   }

   hash += psadd->his_port;

   hash = hash % SocketHashSize;

   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/

   // Add the packet to the front of our hash list
   TAILQ_INSERT_HEAD(&m_slist[hash], psadd, Queue);
   psadd->Head = &m_slist[hash];
}




void HashSocketList::RemoveSocket( PSOCKET ps ) const
{
   register DWORD hash = ps->my_port;
   if ( ( ps->his_ip & 0x7F000000 ) == 0x7F000000 )
   {
      hash += InterfaceIP( GetFirstInterface() );
   }
   else
   {
      hash += ps->his_ip;
   }
   hash += ps->his_port;
   hash = hash % SocketHashSize;
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/

   // If the socket is in our active list
   if (ps->Head == &m_slist[hash]) {

	   // Remove the socket from our hash list
	   TAILQ_REMOVE(&m_slist[hash], ps, Queue);
	   ps->Head = NULL;
	   ps->Queue.tqe_next = NULL;
	   ps->Queue.tqe_prev = NULL;
   }
}





struct ShortSocket
{
   public:
      DWORD m_hisip;
      DWORD m_TimesUp;
      WORD m_hisPort;
      WORD m_myPort;
      WORD next_ptr;
      WORD m_flags;
};




#define SHORT_SOCKET_SIZE (100)

#define SHORT_IN_USE 1
#define SHORT_FIN 2

ShortSocket ShortSockets[SHORT_SOCKET_SIZE];
WORD LastShortFree;


ShortSocket * ShortSocketList::GetFreeShort()
{
   WORD offset = LastShortFree;
   register int i;
   do
   {
      if ( ShortSockets[offset].m_flags == 0 )
      {
         return &( ShortSockets[offset] );
      }
      offset++;
      if ( offset >= SHORT_SOCKET_SIZE )
      {
         offset = 1;
      }
   }
   while ( offset != LastShortFree );
   /* We have no Free Short Sockets !!!!*/
   {
      DBPRINT( DB_TCPIP, "Out of Free Short Sockets\r\n" );
      WORD oldest = 1;
      for ( i = 1; i < SHORT_SOCKET_SIZE; i++ )
      {
         if ( ShortSockets[i].m_TimesUp < ShortSockets[oldest].m_TimesUp )
         {
            oldest = i;
         }
      }
      /* Now we need to remove the oldes from the list it is attached to */
      {
         register DWORD hash = ShortSockets[oldest].m_myPort;
         hash += ShortSockets[oldest].m_hisip;
         hash += ShortSockets[oldest].m_hisPort;
         hash = hash % SocketHashSize;
         if ( m_slist[hash] == oldest )
         {
            m_slist[hash] = ShortSockets[oldest].next_ptr;
         }
         else
         {
            i = m_slist[hash];
            while ( ( ShortSockets[i].next_ptr != oldest ) && ( i ) )
            {
               i = ShortSockets[i].next_ptr;
            }
            if ( i == 0 )
            {
               DBPRINT( DB_TCPIP, "Internal Error\r\n" );
            }

            ShortSockets[i].next_ptr = ShortSockets[oldest].next_ptr;
         }
      }

      ShortSockets[oldest].m_flags = 0;
      return &( ShortSockets[oldest] );
   }
}



void ShortSocketList::FreeShort( ShortSocket *pShort ) const
{
   WORD offset = pShort - ShortSockets;
   if ( ( offset < 1 ) || ( offset >= SHORT_SOCKET_SIZE ) )
   {
      return;
   }
   ShortSockets[offset].m_flags = 0;
   LastShortFree = offset;
}



ShortSocketList::ShortSocketList()
{
}



void ShortSocketList::Init( void )
{
   register int i;
   for ( i = 0; i < SocketHashSize; i++ )
   {
      m_slist[i] = 0;
   }
   for ( i = 0; i < SHORT_SOCKET_SIZE; i++ )
   {
      ShortSockets[i].m_flags = 0;
   }
   LastShortFree = 1;
}


void ShortSocketList::AddSocket( PSOCKET ps )
{
   register DWORD hash = ps->my_port;
   hash += ps->his_ip;
   hash += ps->his_port;
   hash = hash % SocketHashSize;
   register ShortSocket *pShort;
   int state = ps->state;

   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/\
   pShort = GetFreeShort();
   pShort->m_hisip = ps->his_ip;
   pShort->m_hisPort = ps->his_port; //The port he is from
   pShort->m_myPort = ps->my_port; //The port he is aimed at


#ifdef SNMP
   if ( pRemoveTableElementtcpConnEntry != NULL )
   {
      pRemoveTableElementtcpConnEntry( ps );
   }
#endif

   active_list.RemoveSocket( ps ); 
   ps->DoCleanup();
   ps->SetState( TCP_STATE_CLOSED );

   FREESOCKET( ps );

   pShort->next_ptr = m_slist[hash];
   if ( state != TCP_STATE_FIN_WAIT_2 )
   {
      DBPRINT( DB_TCPIP, "Added TW Short\r\n" );
      pShort->m_flags = SHORT_IN_USE;
   }
   else
   {
      DBPRINT( DB_TCPIP, "Added FIN Short\r\n" );
      pShort->m_flags = SHORT_FIN | SHORT_IN_USE;
   }
   pShort->m_TimesUp = TimeTick + TCP_2MSL_WAIT;
   m_slist[hash] = ( pShort - ShortSockets );
}




int ShortSocketList::process( PoolPtr pp, PTCPPKT pTcp, DWORD ip ) const /* Returns true if the socket was handled */
{
   register ShortSocket *pShort = NULL;
   {
      OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/\
      pShort = findsocket( pTcp->dstPort, pTcp->srcPort, ip );
   }
   if ( pShort == NULL )
   {
      return 0;
   }


   DebugTrace( NULL, NULL, pTcp, DT_RX );


   DBPRINT( DB_TCPIP, "Process Short." );

   if ( pShort->m_flags & SHORT_FIN )
   {
      /* We are in finwait_2 and if we get a fin must send an ack */
      if ( pTcp->flags & TCP_FIN )
      {
         WORD tmpw;
         DWORD tmpdw;

         /* First we need to swap src and dest */
         tmpw = pTcp->srcPort;
         pTcp->srcPort = pTcp->dstPort;
         pTcp->dstPort = tmpw;

         tmpdw = pTcp->SeqNumber + 1;
         pTcp->SeqNumber = pTcp->AckNumber;  
         pTcp->AckNumber = tmpdw;
         pTcp->flags = TCP_ACK;
         pTcp->window = 0;
         /* We need to send an ACK */

         DBPRINT( DB_TCPIP, "S" );
         TcpSend( NULL, ip, pp, 0 );
         pShort->m_flags = SHORT_IN_USE;
         pShort->m_TimesUp = TimeTick + TCP_2MSL_WAIT;
      }
      else
      {
         DBPRINT( DB_TCPIP, ".NF." );
         FreeBuffer( pp );
      }
   }
   else
   {
      /* We are all done and just toss the packet */
      /* If we receive a fin then return */
      if ( pTcp->flags & TCP_FIN )
      {
         WORD tmpw;
         DWORD tmpdw;

         /* First we need to swap src and dest */
         tmpw = pTcp->srcPort;
         pTcp->srcPort = pTcp->dstPort;
         pTcp->dstPort = tmpw;

         tmpdw = pTcp->SeqNumber + 1;
         pTcp->SeqNumber = pTcp->AckNumber;  
         pTcp->AckNumber = tmpdw;
         pTcp->flags = TCP_ACK;
         pTcp->window = 0;
         /* We need to send an ACK */
         TcpSend( NULL, ip, pp, 0 );
      }
      else
      {
         DBPRINT( DB_TCPIP, ".NF." );
         FreeBuffer( pp );
      }
   }
   DBPRINT( DB_TCPIP, ".X\r\n" );
   return 1;
}



DWORD ShortSocketList::ShortSocketList::processTick()
{
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/
   DWORD lowest = TimeTick + TCP_2MSL_WAIT; 

   for ( register int i = 0; i < SocketHashSize; i++ )
   {
      if ( m_slist[i] )
      {
         register WORD *pPrev = &( m_slist[i] );
         while ( *pPrev )
         {
            if ( ShortSockets[*pPrev].m_TimesUp < TimeTick )
            {
               DBPRINT( DB_TCPIP, "Free TW Short\r\n" );
               ShortSocket *ps = &( ShortSockets[*pPrev] );
               *pPrev = ps->next_ptr;
               FreeShort( ps );
            }
            else
            {
               if ( ShortSockets[*pPrev].m_TimesUp < lowest )
               {
                  lowest = ShortSockets[*pPrev].m_TimesUp;
               }
               pPrev = &( ShortSockets[*pPrev].next_ptr );
            }
         }  /* While */
      }
   }/* If */
   return lowest;
}




ShortSocket * ShortSocketList::findsocket( WORD d, WORD s, DWORD ip ) const
{
   register DWORD hash = d;
   hash += ip;
   hash += s;
   hash = hash % SocketHashSize;
   OSCriticalSectionObj oscs( TCP_critical_section ); /*Locks the OS the destructor will unlock*/\
   WORD offset = m_slist[hash];

   while ( offset )
   {
      ShortSocket *ps = &( ShortSockets[offset] );
      if ( ( ps->m_myPort == d ) && ( ps->m_hisPort == s ) && ( ps->m_hisip == ip ) )
      {
         DBPRINT( DB_TCPIP, "DFS\r\n" );
         return ps;
      }
      offset = ShortSockets[offset].next_ptr;
   };
   DBPRINT( DB_TCPIP, "FFS\r\n" );
   return NULL;
}

int GetTcpState( int fd )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }
   PSOCKET ps = sockets + sl;
   return ps->state;
}




void RegisterTCPReadNotify( int tcp_fd, tcp_read_notify_handler *newhandler )
{
   int s = tcp_fd - TCP_SOCKET_OFFSET;
   if ( tcp_fd < 0 )
   {
      return;
   }
   PSOCKET pS = sockets + s;
   pS->pReadNotify = newhandler;
}



#ifdef SNMP
int TcpSnmpGetState( void *data_el )
{
   int rv = 0;
   register socket_struct *ps = ( socket_struct * ) data_el;
   switch ( ps->state )
   {
     case TCP_STATE_CLOSED           :
       rv = 1; break; 
     case TCP_STATE_LISTEN           :
       rv = 2; break; 
     case TCP_STATE_SYN_SENT         :
       rv = 3; break; 
     case TCP_STATE_SYN_RCVD         :
       rv = 4; break; 
     case TCP_STATE_WAIT_FOR_ACCEPT  :
       rv = 5; break; 
     case TCP_STATE_ESTABLISHED      :
       rv = 5; break; 
     case TCP_STATE_CLOSE_WAIT       :
       rv = 8; break; 
     case TCP_STATE_LAST_ACK         :
       rv = 9; break; 
     case TCP_STATE_FIN_WAIT_1       :
       rv = 6; break; 
     case TCP_STATE_FIN_WAIT_2       :
       rv = 7; break; 
     case TCP_STATE_CLOSING          :
       rv = 10; break;
     case TCP_STATE_TIME_WAIT        :
       rv = 12; break;
     default:
       break;  
   }
   return rv;
}

WORD TcpSnmpGetMyPort( void *data_el )
{
   socket_struct *ps = ( socket_struct * ) data_el;
   return ps->my_port;
}


WORD TcpSnmpGetHisPort( void *data_el )
{
   socket_struct *ps = ( socket_struct * ) data_el;
   return ps->his_port;
}


IPADDR TcpSnmpGetHisIP( void *data_el )
{
   socket_struct *ps = ( socket_struct * ) data_el;
   return ps->his_ip;
}






#endif

#ifdef  SPEED_TCP

void RegisterDataProcessor(int fd,tcp_data_handler * pdh)
{

       int sl = fd - TCP_SOCKET_OFFSET;
       if ( sl < 0 )
       {
         return;
       }
       
       {OSCriticalSectionObj oscs(TCP_critical_section); /*Locks the OS the destructor will unlock*/\
       
       PSOCKET ps = sockets + sl;

         ps->pDataHandle=pdh;
       if (dataavail(fd)) 
       {//We already have dta we must dispatch...
          PoolPtr pp = GetBuffer();
       PBYTE pb=pp->pData;
         while(dataavail(fd))
         {
         int rv=read(fd,(char *)pb,ETHER_BUFFER_SIZE);
         if (rv>0) pdh(fd,pb,rv);
         }
         FreeBuffer(pp);
       }
       }

}




/* Get The current MSS for a TCP connection */
WORD GetMSSData( int fd )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return 0;
   }
   PSOCKET ps = sockets + sl;
   if ( ps->largest_window_iveseen > ps->mss )
   {
      return ps->mss;
   }
   else
   {
      return ps->largest_window_iveseen;
   }
}


static int BufferOffset FAST_TCP_VAR;

/* Get a buffer to fill with data */ 
PBYTE GetTcpTxBuffer() /* Returns a buffer that may use up to TBD (1500) bytes in length */
{
   register PoolPtr pp = GetBuffer();
   if ( pp == NULL )
   {
      return NULL;
   }
   PTCPPKT pTcpOut = GetInitTcpPkt( pp );
   
   if (!BufferOffset) 
   {
   PBYTE p1=(PBYTE)pp;
   BufferOffset=pTcpOut->DATA-p1;
   }
   
   return pTcpOut->DATA;

}


int socket_struct::SockSubmitTcpBuffer4Tx( DWORD csum32bit, WORD length, PBYTE  pBuffer )
{
   if ( state < TCP_STATE_WAIT_FOR_ACCEPT )
   {
      return TCP_ERR_NOCON;
   }
   if ( state >= TCP_STATE_CLOSE_WAIT )
   {
      return TCP_ERR_CLOSING;
   }
   if ( gpflags & TCP_WANTS_TO_CLOSE )
   {
      return TCP_ERR_CLOSING;
   }
   int bytes_to_send = modulodiff( his_window, send_not_acked );
   if ( bytes_to_send > mss )
   {
      bytes_to_send = mss;
   }
   if ( bytes_to_send < length )
   {
      return 0;
   }
   bytes_to_send = length;
   /* We can send a buffer */
   register PoolPtr pp = ( PoolPtr ) ( pBuffer - BufferOffset );

   register PTCPPKT pTcp = GetTcpPkt( pp );
   pTcp->srcPort = my_port;
   pTcp->dstPort = his_port;
   pTcp->SeqNumber = send_not_acked;
   pTcp->AckNumber = last_seq_I_rxed;
   pTcp->header_len = 0x50;
   pTcp->flags = TCP_PSH | TCP_ACK;
   if ( gpflags & NEED_TOACK )
   {
      pTcp->flags |= TCP_ACK;
      gpflags &= ~NEED_TOACK;
   }

      pTcp->window = MyAdvertisedWindow();

   pTcp->csum = 0;
   pTcp->urgent = 0;
   {
      OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
      int wasfull = TxBuffer.Full();


      if ( ( wasfull ) && ( !TxBuffer.Full() ) )
      {
         SetWriteAvail( GetMyFd() );
      }

      //The TRUE at the end of the send causes us to not free the buffer!
      pp->usedsize = TCPTOTAL_HEADER_SIZE + bytes_to_send;
      send_not_acked += bytes_to_send;
   }

   pp->dwTime = TimeTick;
   TcpSendwSum( this, his_ip, pp, bytes_to_send, TRUE, csum32bit );



   //This is a singly linked list.
   //So we set our next to null
   pp->pPoolNext = NULL;
   {
      /* Locking section for manipulating the topbe acked stuff */
      OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
      outstanding_acks++;
      if ( buffers_tobe_acked_tail )
      {
         buffers_tobe_acked_tail->pPoolNext = pp;
      }
      else
      {
         buffers_tobe_acked_head = pp; //Tail was null so head must be also
         //We also must
         SetUpNextRetransmit();
      }

      //now set tail to us
      buffers_tobe_acked_tail = pp;
      pp->pPoolPrev = NULL; //Set the retransmit count =0;
   }/* End of locking section */

   return length;
}



/*sock  is the tcp socket
csum32bit will be the 32bit checksum of the data stored in the buffer.
length is the length of stored data in bytes . (Must be multiple of 4 for best efficiency)
pBuffer is the buffer previously returned from SubmitTcpBuffer4Tx.

Returns the length is the buffer is successfully queued for transmit.
returns <0 for socket error.
returns 0 for no space left to hold buffer.
*/
int SubmitTcpBuffer4Tx( int fd, DWORD csum32bit, WORD length, PBYTE  pBuffer )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      return TCP_ERR_NOSUCH_SOCKET;
   }
   PSOCKET ps = sockets + sl;

   return ps->SockSubmitTcpBuffer4Tx( csum32bit, length, pBuffer );
}



/* Called to unallocate a buffer you decided not to use. */
void FreeTcpTxBuffer( PBYTE  pBuffer )
{
   PoolPtr pp = ( PoolPtr ) ( pBuffer - BufferOffset );
   FreeBuffer( pp );
}

#endif



int GetTcpRtxCount(int fd)
{
int sl = fd - TCP_SOCKET_OFFSET;
if (sl < 0)
{
// invalid descriptor
return -1;
}
PSOCKET ps = sockets + sl;

// lock OS -- destructor will unlock
OSCriticalSectionObj oscs(TCP_critical_section);

PoolPtr pp = ps->buffers_tobe_acked_head;
if (pp == NULL)
{
// no packets to be acked so retransmission count is zero
return 0;
}

// packets waiting to be acked so return retransmission count
return (int) pp->pPoolPrev;
}



BYTE SetOutOfOrderbuffers(int fd,BYTE max)
{

int s = fd - TCP_SOCKET_OFFSET;
if((s<0) || (s>TCP_SOCKET_STRUCTS)) return 0;
PSOCKET pS = sockets + s;   
BYTE ov=pS->max_buffers_out_of_order;   
pS->max_buffers_out_of_order=max;  
return ov;
}

//---------------------------------------------------------------------
// KeepAlive functions

DWORD TcpGetLastRxTime(int fd)
{
   int s = fd - TCP_SOCKET_OFFSET;
   if((s<0) || (s>TCP_SOCKET_STRUCTS)) return 0;
   PSOCKET pS = sockets + s;   
   return pS-> lastRxTime;
}   

void TcpSendKeepAlive (int fd)
{
    /* Locking section for manipulating the topbe acked stuff */
     OSCriticalSectionObj oscs( TCP_critical_section ); //Locks the OS the destructor will unlock
     
   int s = fd - TCP_SOCKET_OFFSET;
   if((s<0) || (s>TCP_SOCKET_STRUCTS)) return;
   PSOCKET pS = sockets + s;   
   pS->sent_and_acked--;
   pS->send_not_acked--;
   pS->SendState(TCP_ACK);
   pS->sent_and_acked++;
   pS->send_not_acked++;
}   

//-----------------------------------------------------------------------

/*lint -e705 Arguments inconsistent with format */
void ShowFdTcpSocket( int fd )
{
   int sl = fd - TCP_SOCKET_OFFSET;
   if ( sl < 0 )
   {
      iprintf( "Bad socket %d\r\n", fd ); return;
   }
   PSOCKET ps = sockets + sl;
   long timeTick = TimeTick;  /*lint !e713 Loss of precision */ 
   iprintf( "Now = %ld\r\n", timeTick );
   iprintf( "DWORD  NextTimeAction= %ld\r\n", ps->NextTimeAction );//Used for the timer  dispatch
   iprintf( "DWORD  next_retransmit= %ld\r\n", ps->next_retransmit ); //The next retransmit time we have to wake up for


   //The next three define his window for his reception / my transmission ..
   //
   //                                   |                          |                 |
   //            data that is long gone | Data waiting to be acked | data I can send | data in the future....
   //                                   |                          |                 |
   iprintf( "DWORD  sent_and_acked= %X\r\n", ps->sent_and_acked ); //------------A
   iprintf( "DWORD  send_not_acked= %X\r\n", ps->send_not_acked ); //---------------------------------------A
   iprintf( "DWORD  his_window= %X\r\n", ps->his_window );     //---------------------------------------------------------A
   iprintf( "DWORD  my_last_window= %X\r\n", ps->my_last_window );    //Uses for SWS avoidance
   iprintf( "DWORD  last_seq_I_rxed= %X\r\n", ps->last_seq_I_rxed );//The last sequence I recieved from him....
   iprintf( "DWORD  close_time= %X\r\n", ps->close_time );

   iprintf( "long  tick_RTTVAR= %ld\r\n", ps->tick_RTTVAR ); //The RTO in ticks. See Stevens IP... Volume 1 pg 300
   iprintf( "long  tick_SRTT= %ld\r\n", ps->tick_SRTT );   //Also reused to report errors on close
   iprintf( "long  sock_err= %ld\r\n", ps->sock_err );
   iprintf( "DWORD RTO= %ld\r\n", ps->RTO );         /* The actual RTO time out */

   iprintf( " TxBuffer.Empty()=%d  \r\n", ps->TxBuffer.Empty() );
   iprintf( " TxBuffer.SpaceUsed()=%d\r\n", ps->TxBuffer.SpaceUsed() );

   iprintf( " RxBuffer.Empty()=%d  \r\n", ps->RxBuffer.Empty() );
   iprintf( " RxBuffer.SpaceUsed()=%d\r\n", ps->RxBuffer.SpaceUsed() );



   iprintf( "PoolP  buffers_tobe_acked_head= %p\r\n", ps->buffers_tobe_acked_head ); //Buffers that have been sent, and not ACKED
   iprintf( "PoolP  buffers_tobe_acked_tail= %p\r\n", ps->buffers_tobe_acked_tail );


   iprintf( "IP Addr= " ); ShowIP( ps->his_ip ); iprintf( "\r\n" );
   iprintf( "int    options= %d\r\n", ps->options ); //Socket options

   iprintf( "int    Intf_shortcut= %ld\r\n", ps->Intf_shortcut );

   iprintf( "WORD   his_port= %x\r\n", ps->his_port ); //The port he is from
   iprintf( "WORD   my_port= %x\r\n", ps->my_port ); //The port he is aimed at
   iprintf( "WORD   mss= %d\r\n", ps->mss ); //His reported max segment size.
   iprintf( "WORD   largest_window_iveseen= %d\r\n", ps->largest_window_iveseen );

   iprintf( "BYTE   state= %d\r\n", ps->state ); //The RFC state
   iprintf( "BYTE   gpflags= %d\r\n", ps->gpflags ); // General purpose flags... 
   iprintf( "BYTE   dup_Ack= %d\r\n", ps->dup_Ack ); //Also listining_sockets
   iprintf( "BYTE   cur_listen= %d\r\n", ps->cur_listen );
   iprintf( "BYTE   nPendingCalls= %d\r\n", ps->nPendingCalls );
   iprintf( "BYTE   max_listining_sockets= %d\r\n", ps->max_listining_sockets );
   iprintf( "BYTE   outstanding_acks= %d\r\n", ps->outstanding_acks ); // Number of things on the tobe_acked list
   iprintf( "BYTE  MAX_OUTSTANDING_ACK_BUFFERS= %d\r\n", ps->MAX_OUTSTANDING_ACK_BUFFERS );

   iprintf( "MACADR mac_shortcut=" ); ShowMac( &ps->mac_shortcut );
   iprintf( "\r\n" );
};
/*lint +e705 */

#ifdef DIAG_TCP_STATE
void ShowTcpStates( void )
{
   BYTE sockets_state[TCP_SOCKET_STRUCTS]; 

   USER_ENTER_CRITICAL();
   for ( int i = 0; i < TCP_SOCKET_STRUCTS; i++ )
   {
      sockets_state[i] = sockets[i].state;
   }
   USER_EXIT_CRITICAL();

   for ( int i = 0; i < TCP_SOCKET_STRUCTS; i++ )
   {
      iprintf( "Socket[%2d] = ", i );

      switch( sockets_state[i] )
      {
         case TCP_STATE_CLOSED:
            iprintf( "TCP_STATE_CLOSED\r\n" );
            break;
         case TCP_STATE_LISTEN:
            iprintf( "TCP_STATE_LISTEN\r\n" );
            break;
         case TCP_STATE_SYN_SENT:
            iprintf( "TCP_STATE_SYN_SENT\r\n" );
            break;
         case TCP_STATE_SYN_RCVD:
            iprintf( "TCP_STATE_SYN_RCVD\r\n" );
            break;
         case TCP_STATE_WAIT_FOR_ACCEPT:
            iprintf( "TCP_STATE_WAIT_FOR_ACCEPT\r\n" );
            break;
         case TCP_STATE_ESTABLISHED:
            iprintf( "TCP_STATE_ESTABLISHED\r\n" );
            break;
         case TCP_STATE_CLOSE_WAIT:
            iprintf( "TCP_STATE_CLOSE_WAIT\r\n" );
            break;
         case TCP_STATE_LAST_ACK:
            iprintf( "TCP_STATE_LAST_ACK\r\n" );
            break;
         case TCP_STATE_FIN_WAIT_1:
            iprintf( "TCP_STATE_FIN_WAIT_1\r\n" );
            break;
         case TCP_STATE_FIN_WAIT_2:
            iprintf( "TCP_STATE_FIN_WAIT_2\r\n" );
            break;
         case TCP_STATE_CLOSING:
            iprintf( "TCP_STATE_CLOSING\r\n" );
            break;
         case TCP_STATE_TIME_WAIT:
            iprintf( "TCP_STATE_TIME_WAIT\r\n" );
            break;
         default:
            iprintf( "Unknown state %d\r\n", sockets_state[i] );
      }
   }
}

#endif /* #ifdef DIAG_TCP_STATE */

int GetFreeSocketCount( void )
{
   /*
    * Locks the OS - the destructor will unlock.
    */
   OSCriticalSectionObj oscs( TCP_critical_section );

   int nfree = 0;

   for ( int i = 0; i < TCP_SOCKET_STRUCTS; i++ )
   {
      if ( ( sockets[i].state == TCP_STATE_TIME_WAIT ) ||
           ( sockets[i].state == TCP_STATE_CLOSED ) )
      {
         nfree++;
      }
   }

   return nfree;
}


/*
 *****************************************************************************-
 *
 * Check state of socket
 *
 *    Parameters:
 *       socket
 *
 *    Return:
 *       State or TCP_ERR_NOSUCH_SOCKET if not a valid socket
 *
 *    Notes:
 *       none
 *
 *****************************************************************************-
 */
BYTE TcpGetSocketState( int fd )
{
   BYTE socketState = (BYTE)TCP_ERR_NOSUCH_SOCKET;
   int socketOffset = ( fd - TCP_SOCKET_OFFSET );
   if ( ( socketOffset >= 0 ) && ( socketOffset < TCP_SOCKET_STRUCTS ) )
   {
      PSOCKET socketPtr = sockets + socketOffset;
      socketState = socketPtr->state;
   }
   
   return socketState;
}

/*
 *****************************************************************************-
 *
 * Interval in ticks since last received packet
 *
 *    Parameters:
 *       socket
 *
 *    Return:
 *       Interval is ticks from now until the last received packet
 *
 *    Notes:
 *       Will handle one rollover approx. 6 years but not more than one which
 *       given the timeliness of the use of this function should not matter.
 *
 *****************************************************************************-
 */
DWORD TcpGetLastRxInterval( int fd )
{
   DWORD interval = 0;
   int socketOffset = fd - TCP_SOCKET_OFFSET;
   if ( ( socketOffset >= 0 ) && ( socketOffset < TCP_SOCKET_STRUCTS ) )
   {
      PSOCKET socketPtr = sockets + socketOffset;   
      DWORD lastRxTick = socketPtr->lastRxTime;
      DWORD currentTick = TimeTick;
      interval = currentTick - lastRxTick;
      if ( currentTick < lastRxTick )
      {
         /* Rollover */
         interval = ( 0xFFFFFFFF - lastRxTick ) + currentTick;
      }       
   }
   return interval;
}
 
/*
 *****************************************************************************-
 *
 * Socket bytes used of receive buffer.
 *
 *    Parameters:
 *       socket
 *
 *    Return:
 *       0 through buffer allocated * ETHER_BUFFER_SIZE bytes
 *
 *    Notes:
 *       Default number of receive and transmit buffers are 
 *       TCP_BUFFER_SEGMENTS each. Can be increase by SetSocketRxBuffers and
 *       SetSocketTxBuffers in tcp.h. Since it uses packet buffers it should
 *       not be larger than needed. 
 *
 *****************************************************************************-
 */
WORD TcpGetRxBufferSpaceUsed( int fd )
{
   WORD spaceUsed = 0;
   int socketOffset = ( fd - TCP_SOCKET_OFFSET );
   if ( ( socketOffset >= 0 ) && ( socketOffset < TCP_SOCKET_STRUCTS ) )
   {
      PSOCKET socketPtr = sockets + socketOffset;
      spaceUsed = socketPtr->RxBuffer.SpaceUsed();
   }
   
   return spaceUsed;
}



//Returns true if there is no unacked data on the tcp socket.
BOOL TcpAllDataAcked( int fd )
{
   int socketOffset = ( fd - TCP_SOCKET_OFFSET );
   if ( ( socketOffset >= 0 ) && ( socketOffset < TCP_SOCKET_STRUCTS ) )
   {
      PSOCKET socketPtr = sockets + socketOffset;
     if (((socketPtr->buffers_tobe_acked_head)==NULL) && (socketPtr->TxBuffer.Empty())) 
        {
        return true;
        }
   }
   
   return false;
}


//Wait ticks time ticks for all TCP data to be acked .
BOOL WaitForSocketFlush(int fd, DWORD ticks)
{
DWORD End_Time=TimeTick+ticks;
if(ticks==0) End_Time=0xFFFFFFFF;

while(TimeTick<End_Time)
{
   if(TcpAllDataAcked(fd)) return true;
   OSTimeDly(1);
}


return FALSE;
}
