- Merge the Google Summer of Code TcpIpDriver project to replace our current TCP implementation
- Remove oskittcp
- Big thanks to Claudiu Mihail for his work on the project
svn path=/trunk/; revision=53122
--- /dev/null
- /* TCPv4 header flags */
- #define TCP_URG 0x20
- #define TCP_ACK 0x10
- #define TCP_PSH 0x08
- #define TCP_RST 0x04
- #define TCP_SYN 0x02
- #define TCP_FIN 0x01
-
-
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS TCP/IP protocol driver
+ * FILE: include/tcp.h
+ * PURPOSE: Transmission Control Protocol definitions
+ */
+
+#pragma once
+
+typedef VOID
+(*PTCP_COMPLETION_ROUTINE)( PVOID Context, NTSTATUS Status, ULONG Count );
+
+/* TCPv4 header structure */
+#include <pshpack1.h>
+typedef struct TCPv4_HEADER {
+ USHORT SourcePort; /* Source port */
+ USHORT DestinationPort; /* Destination port */
+ ULONG SequenceNumber; /* Sequence number */
+ ULONG AckNumber; /* Acknowledgement number */
+ UCHAR DataOffset; /* Data offset; 32-bit words (leftmost 4 bits) */
+ UCHAR Flags; /* Control bits (rightmost 6 bits) */
+ USHORT Window; /* Maximum acceptable receive window */
+ USHORT Checksum; /* Checksum of segment */
+ USHORT Urgent; /* Pointer to urgent data */
+} TCPv4_HEADER, *PTCPv4_HEADER;
+
- NTSTATUS TCPServiceListeningSocket( PCONNECTION_ENDPOINT Listener,
- PCONNECTION_ENDPOINT Connection,
- PTDI_REQUEST_KERNEL Request );
+#define TCPOPT_END_OF_LIST 0x0
+#define TCPOPT_NO_OPERATION 0x1
+#define TCPOPT_MAX_SEG_SIZE 0x2
+
+#define TCPOPTLEN_MAX_SEG_SIZE 0x4
+
+/* Data offset; 32-bit words (leftmost 4 bits); convert to bytes */
+#define TCP_DATA_OFFSET(DataOffset)(((DataOffset) & 0xF0) >> (4-2))
+
+
+/* TCPv4 pseudo header */
+typedef struct TCPv4_PSEUDO_HEADER {
+ ULONG SourceAddress; /* Source address */
+ ULONG DestinationAddress; /* Destination address */
+ UCHAR Zero; /* Reserved */
+ UCHAR Protocol; /* Protocol */
+ USHORT TCPLength; /* Size of TCP segment */
+} TCPv4_PSEUDO_HEADER, *PTCPv4_PSEUDO_HEADER;
+#include <poppack.h>
+
+typedef struct _SLEEPING_THREAD {
+ LIST_ENTRY Entry;
+ PVOID SleepToken;
+ KEVENT Event;
+} SLEEPING_THREAD, *PSLEEPING_THREAD;
+
+typedef struct _CLIENT_DATA {
+ BOOLEAN Unlocked;
+ KSPIN_LOCK Lock;
+ KIRQL OldIrql;
+} CLIENT_DATA, *PCLIENT_DATA;
+
+/* Retransmission timeout constants */
+
+/* Lower bound for retransmission timeout in TCP timer ticks */
+#define TCP_MIN_RETRANSMISSION_TIMEOUT 1*1000 /* 1 tick */
+
+/* Upper bound for retransmission timeout in TCP timer ticks */
+#define TCP_MAX_RETRANSMISSION_TIMEOUT 1*60*1000 /* 1 tick */
+
+/* Smoothing factor */
+#define TCP_ALPHA_RETRANSMISSION_TIMEOUT(x)(((x)*8)/10) /* 0.8 */
+
+/* Delay variance factor */
+#define TCP_BETA_RETRANSMISSION_TIMEOUT(x)(((x)*16)/10) /* 1.6 */
+
++#define SEL_CONNECT 1
++#define SEL_FIN 2
++#define SEL_RST 4
++#define SEL_ABRT 8
++#define SEL_READ 16
++#define SEL_WRITE 32
++#define SEL_ACCEPT 64
++#define SEL_OOB 128
++#define SEL_ERROR 256
++#define SEL_FINOUT 512
++
++#define FREAD 0x0001
++#define FWRITE 0x0002
+
+/* Datagram/segment send request flags */
+
+#define SRF_URG TCP_URG
+#define SRF_ACK TCP_ACK
+#define SRF_PSH TCP_PSH
+#define SRF_RST TCP_RST
+#define SRF_SYN TCP_SYN
+#define SRF_FIN TCP_FIN
+
+extern LONG TCP_IPIdentification;
+extern CLIENT_DATA ClientInfo;
+
+/* accept.c */
- NTSTATUS TCPTranslateError( int OskitError );
++NTSTATUS TCPCheckPeerForAccept(PVOID Context,
++ PTDI_REQUEST_KERNEL Request);
+NTSTATUS TCPListen( PCONNECTION_ENDPOINT Connection, UINT Backlog );
+BOOLEAN TCPAbortListenForSocket( PCONNECTION_ENDPOINT Listener,
+ PCONNECTION_ENDPOINT Connection );
+NTSTATUS TCPAccept
+( PTDI_REQUEST Request,
+ PCONNECTION_ENDPOINT Listener,
+ PCONNECTION_ENDPOINT Connection,
+ PTCP_COMPLETION_ROUTINE Complete,
+ PVOID Context );
+
+/* tcp.c */
+PCONNECTION_ENDPOINT TCPAllocateConnectionEndpoint( PVOID ClientContext );
+VOID TCPFreeConnectionEndpoint( PCONNECTION_ENDPOINT Connection );
+
+NTSTATUS TCPSocket( PCONNECTION_ENDPOINT Connection,
+ UINT Family, UINT Type, UINT Proto );
+
+VOID HandleSignalledConnection(PCONNECTION_ENDPOINT Connection);
+
+PTCP_SEGMENT TCPCreateSegment(
+ PIP_PACKET IPPacket,
+ PTCPv4_HEADER TCPHeader,
+ ULONG SegmentLength);
+
+VOID TCPFreeSegment(
+ PTCP_SEGMENT Segment);
+
+VOID TCPAddSegment(
+ PCONNECTION_ENDPOINT Connection,
+ PTCP_SEGMENT Segment,
+ PULONG Acknowledged);
+
+NTSTATUS TCPConnect(
+ PCONNECTION_ENDPOINT Connection,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo,
+ PTCP_COMPLETION_ROUTINE Complete,
+ PVOID Context);
+
+NTSTATUS TCPDisconnect(
+ PCONNECTION_ENDPOINT Connection,
+ UINT Flags,
+ PLARGE_INTEGER Timeout,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo,
+ PTCP_COMPLETION_ROUTINE Complete,
+ PVOID Context);
+
+NTSTATUS TCPReceiveData(
+ PCONNECTION_ENDPOINT Connection,
+ PNDIS_BUFFER Buffer,
+ ULONG ReceiveLength,
+ PULONG BytesReceived,
+ ULONG ReceiveFlags,
+ PTCP_COMPLETION_ROUTINE Complete,
+ PVOID Context);
+
+NTSTATUS TCPSendData(
+ PCONNECTION_ENDPOINT Connection,
+ PCHAR Buffer,
+ ULONG DataSize,
+ PULONG DataUsed,
+ ULONG Flags,
+ PTCP_COMPLETION_ROUTINE Complete,
+ PVOID Context);
+
+NTSTATUS TCPClose( PCONNECTION_ENDPOINT Connection );
+
- UINT TCPAllocatePort( UINT HintPort );
++NTSTATUS TCPTranslateError( const INT8 err );
+
- VOID TCPFreePort( UINT Port );
++UINT TCPAllocatePort( const UINT HintPort );
+
++VOID TCPFreePort( const UINT Port );
+
+NTSTATUS TCPGetSockAddress
+( PCONNECTION_ENDPOINT Connection,
+ PTRANSPORT_ADDRESS TransportAddress,
+ BOOLEAN RemoteAddress );
+
+NTSTATUS TCPStartup(
+ VOID);
+
+NTSTATUS TCPShutdown(
+ VOID);
+
+BOOLEAN TCPRemoveIRP( PCONNECTION_ENDPOINT Connection, PIRP Irp );
++
++VOID
++TCPUpdateInterfaceLinkStatus(PIP_INTERFACE IF);
++
++VOID
++TCPUpdateInterfaceIPInformation(PIP_INTERFACE IF);
++
++VOID
++FlushListenQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status);
++
++VOID
++FlushConnectQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status);
++
++VOID
++FlushReceiveQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked);
++
++VOID
++FlushSendQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked);
++
++VOID
++FlushShutdownQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked);
++
++VOID
++FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status);
++
++VOID CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, const BOOLEAN Synchronous);
--- /dev/null
- /* Signals */
- UINT SignalState; /* Active signals from oskit */
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS TCP/IP protocol driver
+ * FILE: include/titypes.h
+ * PURPOSE: TCP/IP protocol driver types
+ */
+
+#pragma once
+
+/*
+ * VOID ReferenceObject(
+ * PVOID Object)
+ */
+#define ReferenceObject(Object) \
+{ \
+ InterlockedIncrement(&((Object)->RefCount)); \
+}
+
+/*
+ * VOID DereferenceObject(
+ * PVOID Object)
+ */
+#define DereferenceObject(Object) \
+{ \
+ if (InterlockedDecrement(&((Object)->RefCount)) == 0) \
+ (((Object)->Free)(Object)); \
+}
+
+/*
+ * VOID LockObject(PVOID Object, PKIRQL OldIrql)
+ */
+#define LockObject(Object, Irql) \
+{ \
+ ReferenceObject(Object); \
+ KeAcquireSpinLock(&((Object)->Lock), Irql); \
+ memcpy(&(Object)->OldIrql, Irql, sizeof(KIRQL)); \
+}
+
+/*
+ * VOID LockObjectAtDpcLevel(PVOID Object)
+ */
+#define LockObjectAtDpcLevel(Object) \
+{ \
+ ReferenceObject(Object); \
+ KeAcquireSpinLockAtDpcLevel(&((Object)->Lock)); \
+ (Object)->OldIrql = DISPATCH_LEVEL; \
+}
+
+/*
+ * VOID UnlockObject(PVOID Object, KIRQL OldIrql)
+ */
+#define UnlockObject(Object, OldIrql) \
+{ \
+ KeReleaseSpinLock(&((Object)->Lock), OldIrql); \
+ DereferenceObject(Object); \
+}
+
+/*
+ * VOID UnlockObjectFromDpcLevel(PVOID Object)
+ */
+#define UnlockObjectFromDpcLevel(Object) \
+{ \
+ KeReleaseSpinLockFromDpcLevel(&((Object)->Lock)); \
+ DereferenceObject(Object); \
+}
+
+
+
+#include <ip.h>
+
+struct _ADDRESS_FILE;
+
+/***************************************************
+* Connection-less communication support structures *
+***************************************************/
+
+typedef NTSTATUS (*DATAGRAM_SEND_ROUTINE)(
+ struct _ADDRESS_FILE *AddrFile,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ PCHAR Buffer,
+ ULONG DataSize,
+ PULONG DataUsed);
+
+/* Datagram completion handler prototype */
+typedef VOID (*DATAGRAM_COMPLETION_ROUTINE)(
+ PVOID Context,
+ NDIS_STATUS Status,
+ ULONG Count);
+
+typedef DATAGRAM_COMPLETION_ROUTINE PDATAGRAM_COMPLETION_ROUTINE;
+
+typedef struct _DATAGRAM_RECEIVE_REQUEST {
+ struct _ADDRESS_FILE *AddressFile; /* AddressFile on behalf of */
+ LIST_ENTRY ListEntry; /* Entry on list */
+ IP_ADDRESS RemoteAddress; /* Remote address we receive from (NULL means any) */
+ USHORT RemotePort; /* Remote port we receive from (0 means any) */
+ PTDI_CONNECTION_INFORMATION ReturnInfo;/* Return information */
+ PCHAR Buffer; /* Pointer to receive buffer */
+ ULONG BufferSize; /* Size of Buffer */
+ DATAGRAM_COMPLETION_ROUTINE Complete; /* Completion routine */
+ PVOID Context; /* Pointer to context information */
+ DATAGRAM_COMPLETION_ROUTINE UserComplete; /* Completion routine */
+ PVOID UserContext; /* Pointer to context information */
+ PIRP Irp; /* IRP on behalf of */
+} DATAGRAM_RECEIVE_REQUEST, *PDATAGRAM_RECEIVE_REQUEST;
+
+/* Datagram build routine prototype */
+typedef NTSTATUS (*DATAGRAM_BUILD_ROUTINE)(
+ PVOID Context,
+ PIP_ADDRESS LocalAddress,
+ USHORT LocalPort,
+ PIP_PACKET *IPPacket);
+
+typedef struct _DATAGRAM_SEND_REQUEST {
+ LIST_ENTRY ListEntry;
+ PNDIS_PACKET PacketToSend;
+ DATAGRAM_COMPLETION_ROUTINE Complete; /* Completion routine */
+ PVOID Context; /* Pointer to context information */
+ IP_PACKET Packet;
+ UINT BufferSize;
+ IP_ADDRESS RemoteAddress;
+ USHORT RemotePort;
+ ULONG Flags; /* Protocol specific flags */
+} DATAGRAM_SEND_REQUEST, *PDATAGRAM_SEND_REQUEST;
+
+/* Transport address file context structure. The FileObject->FsContext2
+ field holds a pointer to this structure */
+typedef struct _ADDRESS_FILE {
+ LIST_ENTRY ListEntry; /* Entry on list */
+ LONG RefCount; /* Reference count */
+ OBJECT_FREE_ROUTINE Free; /* Routine to use to free resources for the object */
+ KSPIN_LOCK Lock; /* Spin lock to manipulate this structure */
+ KIRQL OldIrql; /* Currently not used */
+ IP_ADDRESS Address; /* Address of this address file */
+ USHORT Family; /* Address family */
+ USHORT Protocol; /* Protocol number */
+ USHORT Port; /* Network port (network byte order) */
+ UCHAR TTL; /* Time to live stored in packets sent from this address file */
+ UINT DF; /* Don't fragment */
+ UINT BCast; /* Receive broadcast packets */
+ UINT HeaderIncl; /* Include header in RawIP packets */
+ WORK_QUEUE_ITEM WorkItem; /* Work queue item handle */
+ DATAGRAM_COMPLETION_ROUTINE Complete; /* Completion routine for delete request */
+ PVOID Context; /* Delete request context */
+ DATAGRAM_SEND_ROUTINE Send; /* Routine to send a datagram */
+ LIST_ENTRY ReceiveQueue; /* List of outstanding receive requests */
+ LIST_ENTRY TransmitQueue; /* List of outstanding transmit requests */
+ struct _CONNECTION_ENDPOINT *Connection;
+ /* Associated connection or NULL if no associated connection exist */
+ struct _CONNECTION_ENDPOINT *Listener;
+ /* Associated listener (see transport/tcp/accept.c) */
+ IP_ADDRESS AddrCache; /* One entry address cache (destination
+ address of last packet transmitted) */
+
+ /* The following members are used to control event notification */
+
+ /* Connection indication handler */
+ PTDI_IND_CONNECT ConnectHandler;
+ PVOID ConnectHandlerContext;
+ BOOLEAN RegisteredConnectHandler;
+ /* Disconnect indication handler */
+ PTDI_IND_DISCONNECT DisconnectHandler;
+ PVOID DisconnectHandlerContext;
+ BOOLEAN RegisteredDisconnectHandler;
+ /* Error indication handler */
+ PTDI_IND_ERROR ErrorHandler;
+ PVOID ErrorHandlerContext;
+ PVOID ErrorHandlerOwner;
+ BOOLEAN RegisteredErrorHandler;
+ /* Receive indication handler */
+ PTDI_IND_RECEIVE ReceiveHandler;
+ PVOID ReceiveHandlerContext;
+ BOOLEAN RegisteredReceiveHandler;
+ /* Receive datagram indication handler */
+ PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler;
+ PVOID ReceiveDatagramHandlerContext;
+ BOOLEAN RegisteredReceiveDatagramHandler;
+ /* Expedited receive indication handler */
+ PTDI_IND_RECEIVE_EXPEDITED ExpeditedReceiveHandler;
+ PVOID ExpeditedReceiveHandlerContext;
+ BOOLEAN RegisteredExpeditedReceiveHandler;
+ /* Chained receive indication handler */
+ PTDI_IND_CHAINED_RECEIVE ChainedReceiveHandler;
+ PVOID ChainedReceiveHandlerContext;
+ BOOLEAN RegisteredChainedReceiveHandler;
+ /* Chained receive datagram indication handler */
+ PTDI_IND_CHAINED_RECEIVE_DATAGRAM ChainedReceiveDatagramHandler;
+ PVOID ChainedReceiveDatagramHandlerContext;
+ BOOLEAN RegisteredChainedReceiveDatagramHandler;
+ /* Chained expedited receive indication handler */
+ PTDI_IND_CHAINED_RECEIVE_EXPEDITED ChainedReceiveExpeditedHandler;
+ PVOID ChainedReceiveExpeditedHandlerContext;
+ BOOLEAN RegisteredChainedReceiveExpeditedHandler;
+} ADDRESS_FILE, *PADDRESS_FILE;
+
+/* Structure used to search through Address Files */
+typedef struct _AF_SEARCH {
+ PLIST_ENTRY Next; /* Next address file to check */
+ PIP_ADDRESS Address; /* Pointer to address to be found */
+ USHORT Port; /* Network port */
+ USHORT Protocol; /* Protocol number */
+} AF_SEARCH, *PAF_SEARCH;
+
+/*******************************************************
+* Connection-oriented communication support structures *
+*******************************************************/
+
+typedef struct _TCP_RECEIVE_REQUEST {
+ LIST_ENTRY ListEntry; /* Entry on list */
+ PNDIS_BUFFER Buffer; /* Pointer to receive buffer */
+ ULONG BufferSize; /* Size of Buffer */
+ DATAGRAM_COMPLETION_ROUTINE Complete; /* Completion routine */
+ PVOID Context; /* Pointer to context information */
+} TCP_RECEIVE_REQUEST, *PTCP_RECEIVE_REQUEST;
+
+/* Connection states */
+typedef enum {
+ ctListen = 0, /* Waiting for incoming connection requests */
+ ctSynSent, /* Waiting for matching connection request */
+ ctSynReceived, /* Waiting for connection request acknowledgment */
+ ctEstablished, /* Connection is open for data transfer */
+ ctFinWait1, /* Waiting for termination request or ack. for same */
+ ctFinWait2, /* Waiting for termination request from remote TCP */
+ ctCloseWait, /* Waiting for termination request from local user */
+ ctClosing, /* Waiting for termination ack. from remote TCP */
+ ctLastAck, /* Waiting for termination request ack. from remote TCP */
+ ctTimeWait, /* Waiting for enough time to pass to be sure the remote TCP
+ received the ack. of its connection termination request */
+ ctClosed /* Represents a closed connection */
+} CONNECTION_STATE, *PCONNECTION_STATE;
+
+
+/* Structure for an TCP segment */
+typedef struct _TCP_SEGMENT {
+ LIST_ENTRY ListEntry;
+ PIP_PACKET IPPacket; /* Pointer to IP packet */
+ PVOID SegmentData; /* Pointer to segment data */
+ ULONG SequenceNumber; /* Sequence number of first byte in segment */
+ ULONG Length; /* Number of bytes in segment */
+ ULONG BytesDelivered; /* Number of bytes already delivered to the client */
+} TCP_SEGMENT, *PTCP_SEGMENT;
+
+typedef struct _TDI_BUCKET {
+ LIST_ENTRY Entry;
+ struct _CONNECTION_ENDPOINT *AssociatedEndpoint;
+ TDI_REQUEST Request;
+ NTSTATUS Status;
+ ULONG Information;
+} TDI_BUCKET, *PTDI_BUCKET;
+
+/* Transport connection context structure A.K.A. Transmission Control Block
+ (TCB) in TCP terminology. The FileObject->FsContext2 field holds a pointer
+ to this structure */
+typedef struct _CONNECTION_ENDPOINT {
+ PVOID SocketContext; /* Context for lower layer (MUST be first member in struct) */
+ LIST_ENTRY ListEntry; /* Entry on list */
+ LONG RefCount; /* Reference count */
+ OBJECT_FREE_ROUTINE Free; /* Routine to use to free resources for the object */
+ KSPIN_LOCK Lock; /* Spin lock to protect this structure */
+ KIRQL OldIrql; /* The old irql is stored here for use in HandleSignalledConnection */
+ PVOID ClientContext; /* Pointer to client context information */
+ PADDRESS_FILE AddressFile; /* Associated address file object (NULL if none) */
+
+ /* Requests */
+ LIST_ENTRY ConnectRequest; /* Queued connect rqueusts */
+ LIST_ENTRY ListenRequest; /* Queued listen requests */
+ LIST_ENTRY ReceiveRequest; /* Queued receive requests */
+ LIST_ENTRY SendRequest; /* Queued send requests */
+ LIST_ENTRY ShutdownRequest;/* Queued shutdown requests */
+
++ LIST_ENTRY PacketQueue; /* Queued received packets waiting to be processed */
+
+ /* Disconnect Timer */
+ KTIMER DisconnectTimer;
+ KDPC DisconnectDpc;
+
++ /* Socket state */
++ BOOLEAN SendShutdown;
++ BOOLEAN ReceiveShutdown;
++
+ struct _CONNECTION_ENDPOINT *Next; /* Next connection in address file list */
+} CONNECTION_ENDPOINT, *PCONNECTION_ENDPOINT;
+
+
+
+/*************************
+* TDI support structures *
+*************************/
+
+/* Transport control channel context structure. The FileObject->FsContext2
+ field holds a pointer to this structure */
+typedef struct _CONTROL_CHANNEL {
+ LIST_ENTRY ListEntry; /* Entry on list */
+ LONG RefCount; /* Reference count */
+ OBJECT_FREE_ROUTINE Free; /* Routine to use to free resources for the object */
+ KSPIN_LOCK Lock; /* Spin lock to protect this structure */
+} CONTROL_CHANNEL, *PCONTROL_CHANNEL;
+
+/* Transport (TCP/UDP) endpoint context structure. The FileObject->FsContext
+ field holds a pointer to this structure */
+typedef struct _TRANSPORT_CONTEXT {
+ union {
+ HANDLE AddressHandle;
+ CONNECTION_CONTEXT ConnectionContext;
+ HANDLE ControlChannel;
+ } Handle;
+ BOOLEAN CancelIrps;
+ KEVENT CleanupEvent;
+} TRANSPORT_CONTEXT, *PTRANSPORT_CONTEXT;
+
+typedef struct _TI_QUERY_CONTEXT {
+ PIRP Irp;
+ PMDL InputMdl;
+ PMDL OutputMdl;
+ TCP_REQUEST_QUERY_INFORMATION_EX QueryInfo;
+} TI_QUERY_CONTEXT, *PTI_QUERY_CONTEXT;
+
+/* EOF */
--- /dev/null
- <include base="oskittcp">include</include>
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="tcpip" type="kernelmodedriver" installbase="system32/drivers" installname="tcpip.sys">
+ <importlibrary definition="tcpip.spec" />
+ <include base="tcpip">include</include>
- <library>oskittcp</library>
++ <include base="lwip">src/include</include>
++ <include base="lwip">src/include/ipv4</include>
+ <define name="NDIS40" />
+ <define name="_NTDRIVER_" />
+ <library>ip</library>
++ <library>lwip</library>
+ <library>ndis</library>
+ <library>pseh</library>
+ <library>chew</library>
+ <library>ntoskrnl</library>
+ <library>hal</library>
+ <directory name="include">
+ <pch>precomp.h</pch>
+ </directory>
+ <directory name="datalink">
+ <file>lan.c</file>
+ </directory>
+ <directory name="tcpip">
+ <file>ainfo.c</file>
+ <file>buffer.c</file>
+ <file>dispatch.c</file>
+ <file>fileobjs.c</file>
+ <file>iinfo.c</file>
+ <file>info.c</file>
+ <file>lock.c</file>
+ <file>main.c</file>
+ <file>ninfo.c</file>
+ <file>proto.c</file>
+ <file>tinfo.c</file>
+ <file>wait.c</file>
+ </directory>
+ <file>tcpip.rc</file>
+</module>
--- /dev/null
- PCONNECTION_ENDPOINT Connection, LastConnection;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS TCP/IP protocol driver
+ * FILE: tcpip/dispatch.h
+ * PURPOSE: TDI dispatch routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ * CSH 01/08-2000 Created
+ * TODO: Validate device object in all dispatch routines
+ */
+
+#include "precomp.h"
+
+NTSTATUS IRPFinish( PIRP Irp, NTSTATUS Status ) {
+ KIRQL OldIrql;
+
+ Irp->IoStatus.Status = Status;
+
+ if( Status == STATUS_PENDING )
+ IoMarkIrpPending( Irp );
+ else {
+ IoAcquireCancelSpinLock(&OldIrql);
+ (void)IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock(OldIrql);
+
+ IoCompleteRequest( Irp, IO_NETWORK_INCREMENT );
+ }
+
+ return Status;
+}
+
+NTSTATUS DispPrepareIrpForCancel(
+ PTRANSPORT_CONTEXT Context,
+ PIRP Irp,
+ PDRIVER_CANCEL CancelRoutine)
+/*
+ * FUNCTION: Prepare an IRP for cancellation
+ * ARGUMENTS:
+ * Context = Pointer to context information
+ * Irp = Pointer to an I/O request packet
+ * CancelRoutine = Routine to be called when I/O request is cancelled
+ * RETURNS:
+ * Status of operation
+ */
+{
+ KIRQL OldIrql;
+ PIO_STACK_LOCATION IrpSp;
+ PTRANSPORT_CONTEXT TransContext;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ TransContext = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext;
+
+ IoAcquireCancelSpinLock(&OldIrql);
+
+ if (!Irp->Cancel && !TransContext->CancelIrps) {
+ (void)IoSetCancelRoutine(Irp, CancelRoutine);
+ IoReleaseCancelSpinLock(OldIrql);
+
+ TI_DbgPrint(DEBUG_IRP, ("Leaving (IRP at 0x%X can now be cancelled).\n", Irp));
+
+ return STATUS_SUCCESS;
+ }
+
+ /* IRP has already been cancelled */
+
+ IoReleaseCancelSpinLock(OldIrql);
+
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ Irp->IoStatus.Information = 0;
+
+ TI_DbgPrint(DEBUG_IRP, ("Leaving (IRP was already cancelled).\n"));
+
+ return Irp->IoStatus.Status;
+}
+
+VOID DispDataRequestComplete(
+ PVOID Context,
+ NTSTATUS Status,
+ ULONG Count)
+/*
+ * FUNCTION: Completes a send/receive IRP
+ * ARGUMENTS:
+ * Context = Pointer to context information (IRP)
+ * Status = Status of the request
+ * Count = Number of bytes sent or received
+ */
+{
+ PIRP Irp = Context;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called for irp %x (%x, %d).\n",
+ Irp, Status, Count));
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = Count;
+
+ TI_DbgPrint(MID_TRACE, ("Irp->IoStatus.Status = %x\n",
+ Irp->IoStatus.Status));
+ TI_DbgPrint(MID_TRACE, ("Irp->IoStatus.Information = %d\n",
+ Irp->IoStatus.Information));
+ TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp));
+
+ IRPFinish(Irp, Status);
+
+ TI_DbgPrint(DEBUG_IRP, ("Done Completing IRP\n"));
+}
+
+VOID NTAPI DispCancelRequest(
+ PDEVICE_OBJECT Device,
+ PIRP Irp)
+/*
+ * FUNCTION: Cancels an IRP
+ * ARGUMENTS:
+ * Device = Pointer to device object
+ * Irp = Pointer to an I/O request packet
+ */
+{
+ PIO_STACK_LOCATION IrpSp;
+ PTRANSPORT_CONTEXT TranContext;
+ PFILE_OBJECT FileObject;
+ UCHAR MinorFunction;
+ PCONNECTION_ENDPOINT Connection;
+ BOOLEAN DequeuedIrp = TRUE;
+
+ IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ FileObject = IrpSp->FileObject;
+ TranContext = (PTRANSPORT_CONTEXT)FileObject->FsContext;
+ MinorFunction = IrpSp->MinorFunction;
+
+ TI_DbgPrint(DEBUG_IRP, ("IRP at (0x%X) MinorFunction (0x%X) IrpSp (0x%X).\n", Irp, MinorFunction, IrpSp));
+
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ Irp->IoStatus.Information = 0;
+
+#if DBG
+ if (!Irp->Cancel)
+ TI_DbgPrint(MIN_TRACE, ("Irp->Cancel is FALSE, should be TRUE.\n"));
+#endif
+
+ /* Try canceling the request */
+ switch(MinorFunction) {
+ case TDI_SEND:
+ case TDI_RECEIVE:
+ DequeuedIrp = TCPRemoveIRP( TranContext->Handle.ConnectionContext, Irp );
+ break;
+
+ case TDI_SEND_DATAGRAM:
+ if (FileObject->FsContext2 != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) {
+ TI_DbgPrint(MIN_TRACE, ("TDI_SEND_DATAGRAM, but no address file.\n"));
+ break;
+ }
+
+ DequeuedIrp = DGRemoveIRP(TranContext->Handle.AddressHandle, Irp);
+ break;
+
+ case TDI_RECEIVE_DATAGRAM:
+ if (FileObject->FsContext2 != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) {
+ TI_DbgPrint(MIN_TRACE, ("TDI_RECEIVE_DATAGRAM, but no address file.\n"));
+ break;
+ }
+
+ DequeuedIrp = DGRemoveIRP(TranContext->Handle.AddressHandle, Irp);
+ break;
+
+ case TDI_CONNECT:
+ DequeuedIrp = TCPRemoveIRP(TranContext->Handle.ConnectionContext, Irp);
+ break;
+
+ case TDI_DISCONNECT:
+ Connection = (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+
+ DequeuedIrp = TCPRemoveIRP(TranContext->Handle.ConnectionContext, Irp);
+ if (DequeuedIrp)
+ {
+ if (KeCancelTimer(&Connection->DisconnectTimer))
+ {
+ DereferenceObject(Connection);
+ }
+ }
+ break;
+
+ default:
+ TI_DbgPrint(MIN_TRACE, ("Unknown IRP. MinorFunction (0x%X).\n", MinorFunction));
+ ASSERT(FALSE);
+ break;
+ }
+
+ if (DequeuedIrp)
+ IRPFinish(Irp, STATUS_CANCELLED);
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+
+VOID NTAPI DispCancelListenRequest(
+ PDEVICE_OBJECT Device,
+ PIRP Irp)
+/*
+ * FUNCTION: Cancels a listen IRP
+ * ARGUMENTS:
+ * Device = Pointer to device object
+ * Irp = Pointer to an I/O request packet
+ */
+{
+ PIO_STACK_LOCATION IrpSp;
+ PTRANSPORT_CONTEXT TranContext;
+ PFILE_OBJECT FileObject;
+ PCONNECTION_ENDPOINT Connection;
+
+ IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ FileObject = IrpSp->FileObject;
+ TranContext = (PTRANSPORT_CONTEXT)FileObject->FsContext;
+ ASSERT( TDI_LISTEN == IrpSp->MinorFunction);
+
+ TI_DbgPrint(DEBUG_IRP, ("IRP at (0x%X).\n", Irp));
+
+#if DBG
+ if (!Irp->Cancel)
+ TI_DbgPrint(MIN_TRACE, ("Irp->Cancel is FALSE, should be TRUE.\n"));
+#endif
+
+ /* Try canceling the request */
+ Connection = (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+
+ if (TCPAbortListenForSocket(Connection->AddressFile->Listener,
+ Connection))
+ {
+ Irp->IoStatus.Information = 0;
+ IRPFinish(Irp, STATUS_CANCELLED);
+ }
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+
+NTSTATUS DispTdiAccept(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_ACCEPT handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+
+NTSTATUS DispTdiAssociateAddress(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_ASSOCIATE_ADDRESS handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PTDI_REQUEST_KERNEL_ASSOCIATE Parameters;
+ PTRANSPORT_CONTEXT TranContext;
+ PIO_STACK_LOCATION IrpSp;
+ PCONNECTION_ENDPOINT Connection, LastConnection;
+ PFILE_OBJECT FileObject;
+ PADDRESS_FILE AddrFile = NULL;
+ NTSTATUS Status;
+ KIRQL OldIrql;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ /* Get associated connection endpoint file object. Quit if none exists */
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (!TranContext) {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Connection = (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+ if (!Connection) {
+ TI_DbgPrint(MID_TRACE, ("No connection endpoint file object.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&IrpSp->Parameters;
+
+ Status = ObReferenceObjectByHandle(
+ Parameters->AddressHandle,
+ 0,
+ IoFileObjectType,
+ KernelMode,
+ (PVOID*)&FileObject,
+ NULL);
+ if (!NT_SUCCESS(Status)) {
+ TI_DbgPrint(MID_TRACE, ("Bad address file object handle (0x%X): %x.\n",
+ Parameters->AddressHandle, Status));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ LockObject(Connection, &OldIrql);
+
+ if (Connection->AddressFile) {
+ ObDereferenceObject(FileObject);
+ UnlockObject(Connection, OldIrql);
+ TI_DbgPrint(MID_TRACE, ("An address file is already asscociated.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (FileObject->FsContext2 != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) {
+ ObDereferenceObject(FileObject);
+ UnlockObject(Connection, OldIrql);
+ TI_DbgPrint(MID_TRACE, ("Bad address file object. Magic (0x%X).\n",
+ FileObject->FsContext2));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Get associated address file object. Quit if none exists */
+
+ TranContext = FileObject->FsContext;
+ if (!TranContext) {
+ ObDereferenceObject(FileObject);
+ UnlockObject(Connection, OldIrql);
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ AddrFile = (PADDRESS_FILE)TranContext->Handle.AddressHandle;
+ if (!AddrFile) {
+ UnlockObject(Connection, OldIrql);
+ ObDereferenceObject(FileObject);
+ TI_DbgPrint(MID_TRACE, ("No address file object.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ LockObjectAtDpcLevel(AddrFile);
+
+ ReferenceObject(AddrFile);
+ Connection->AddressFile = AddrFile;
+
+ /* Add connection endpoint to the address file */
+ ReferenceObject(Connection);
+ if (AddrFile->Connection == NULL)
+ AddrFile->Connection = Connection;
+ else
+ {
+ LastConnection = AddrFile->Connection;
+ while (LastConnection->Next != NULL)
+ LastConnection = LastConnection->Next;
+ LastConnection->Next = Connection;
+ }
+
+ ObDereferenceObject(FileObject);
+
+ UnlockObjectFromDpcLevel(AddrFile);
+ UnlockObject(Connection, OldIrql);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS DispTdiConnect(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_CONNECT handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PCONNECTION_ENDPOINT Connection;
+ PTDI_REQUEST_KERNEL Parameters;
+ PTRANSPORT_CONTEXT TranContext;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ /* Get associated connection endpoint file object. Quit if none exists */
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (!TranContext) {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ Connection = (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+ if (!Connection) {
+ TI_DbgPrint(MID_TRACE, ("No connection endpoint file object.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ Parameters = (PTDI_REQUEST_KERNEL)&IrpSp->Parameters;
+
+ Status = DispPrepareIrpForCancel(TranContext->Handle.ConnectionContext,
+ Irp,
+ DispCancelRequest);
+
+ if (NT_SUCCESS(Status)) {
+ Status = TCPConnect(
+ TranContext->Handle.ConnectionContext,
+ Parameters->RequestConnectionInformation,
+ Parameters->ReturnConnectionInformation,
+ DispDataRequestComplete,
+ Irp );
+ }
+
+done:
+ if (Status != STATUS_PENDING) {
+ DispDataRequestComplete(Irp, Status, 0);
+ } else
+ IoMarkIrpPending(Irp);
+
+ TI_DbgPrint(MAX_TRACE, ("TCP Connect returned %08x\n", Status));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiDisassociateAddress(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_DISASSOCIATE_ADDRESS handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
- KIRQL OldIrql;
- NTSTATUS Status;
++ PCONNECTION_ENDPOINT Connection;
+ PTRANSPORT_CONTEXT TranContext;
+ PIO_STACK_LOCATION IrpSp;
- LockObject(Connection, &OldIrql);
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ /* Get associated connection endpoint file object. Quit if none exists */
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (!TranContext) {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Connection = (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+ if (!Connection) {
+ TI_DbgPrint(MID_TRACE, ("No connection endpoint file object.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
- if (!Connection->AddressFile) {
- UnlockObject(Connection, OldIrql);
- TI_DbgPrint(MID_TRACE, ("No address file is asscociated.\n"));
- return STATUS_INVALID_PARAMETER;
- }
-
- LockObjectAtDpcLevel(Connection->AddressFile);
-
- /* Unlink this connection from the address file */
- if (Connection->AddressFile->Connection == Connection)
- {
- Connection->AddressFile->Connection = Connection->Next;
- DereferenceObject(Connection);
- Status = STATUS_SUCCESS;
- }
- else
- {
- LastConnection = Connection->AddressFile->Connection;
- while (LastConnection->Next != Connection && LastConnection->Next != NULL)
- LastConnection = LastConnection->Next;
- if (LastConnection->Next == Connection)
- {
- LastConnection->Next = Connection->Next;
- DereferenceObject(Connection);
- Status = STATUS_SUCCESS;
- }
- else
- {
- Status = STATUS_INVALID_PARAMETER;
- }
- }
-
- UnlockObjectFromDpcLevel(Connection->AddressFile);
-
- if (Status == STATUS_SUCCESS)
- {
- /* Remove the address file from this connection */
- DereferenceObject(Connection->AddressFile);
- Connection->AddressFile = NULL;
- }
-
- UnlockObject(Connection, OldIrql);
-
- return Status;
++ /* NO-OP because we need the address to deallocate the port when the connection closes */
+
++ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS DispTdiDisconnect(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_DISCONNECT handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ NTSTATUS Status;
+ PTDI_REQUEST_KERNEL_DISCONNECT DisReq;
+ PCONNECTION_ENDPOINT Connection;
+ PTRANSPORT_CONTEXT TranContext;
+ PIO_STACK_LOCATION IrpSp;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ DisReq = (PTDI_REQUEST_KERNEL_DISCONNECT)&IrpSp->Parameters;
+
+ /* Get associated connection endpoint file object. Quit if none exists */
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (!TranContext) {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ Connection = (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+ if (!Connection) {
+ TI_DbgPrint(MID_TRACE, ("No connection endpoint file object.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ Status = DispPrepareIrpForCancel
+ (TranContext->Handle.ConnectionContext,
+ Irp,
+ (PDRIVER_CANCEL)DispCancelRequest);
+
+ if (NT_SUCCESS(Status))
+ {
+ Status = TCPDisconnect(TranContext->Handle.ConnectionContext,
+ DisReq->RequestFlags,
+ DisReq->RequestSpecific,
+ DisReq->RequestConnectionInformation,
+ DisReq->ReturnConnectionInformation,
+ DispDataRequestComplete,
+ Irp);
+ }
+
+done:
+ if (Status != STATUS_PENDING) {
+ DispDataRequestComplete(Irp, Status, 0);
+ } else
+ IoMarkIrpPending(Irp);
+
+ TI_DbgPrint(MAX_TRACE, ("TCP Disconnect returned %08x\n", Status));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiListen(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_LISTEN handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PCONNECTION_ENDPOINT Connection;
+ PTDI_REQUEST_KERNEL Parameters;
+ PTRANSPORT_CONTEXT TranContext;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status = STATUS_SUCCESS;
+ KIRQL OldIrql;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ /* Get associated connection endpoint file object. Quit if none exists */
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (TranContext == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ Connection = (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+ if (Connection == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("No connection endpoint file object.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ Parameters = (PTDI_REQUEST_KERNEL)&IrpSp->Parameters;
+
+ Status = DispPrepareIrpForCancel
+ (TranContext->Handle.ConnectionContext,
+ Irp,
+ (PDRIVER_CANCEL)DispCancelListenRequest);
+
+ LockObject(Connection, &OldIrql);
+
+ if (Connection->AddressFile == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("No associated address file\n"));
+ UnlockObject(Connection, OldIrql);
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ LockObjectAtDpcLevel(Connection->AddressFile);
+
+ /* Listening will require us to create a listening socket and store it in
+ * the address file. It will be signalled, and attempt to complete an irp
+ * when a new connection arrives. */
+ /* The important thing to note here is that the irp we'll complete belongs
+ * to the socket to be accepted onto, not the listener */
+ if( NT_SUCCESS(Status) && !Connection->AddressFile->Listener ) {
+ Connection->AddressFile->Listener =
+ TCPAllocateConnectionEndpoint( NULL );
+
+ if( !Connection->AddressFile->Listener )
+ Status = STATUS_NO_MEMORY;
+
+ if( NT_SUCCESS(Status) ) {
++ ReferenceObject(Connection->AddressFile);
+ Connection->AddressFile->Listener->AddressFile =
+ Connection->AddressFile;
+
+ Status = TCPSocket( Connection->AddressFile->Listener,
+ Connection->AddressFile->Family,
+ SOCK_STREAM,
+ Connection->AddressFile->Protocol );
+ }
+
+ if( NT_SUCCESS(Status) )
+ Status = TCPListen( Connection->AddressFile->Listener, 1024 );
+ /* BACKLOG */
+ }
+
+ if( NT_SUCCESS(Status) ) {
+ Status = TCPAccept
+ ( (PTDI_REQUEST)Parameters,
+ Connection->AddressFile->Listener,
+ Connection,
+ DispDataRequestComplete,
+ Irp );
+ }
+
+ UnlockObjectFromDpcLevel(Connection->AddressFile);
+ UnlockObject(Connection, OldIrql);
+
+done:
+ if (Status != STATUS_PENDING) {
+ DispDataRequestComplete(Irp, Status, 0);
+ } else
+ IoMarkIrpPending(Irp);
+
+ TI_DbgPrint(MID_TRACE,("Leaving %x\n", Status));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiQueryInformation(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_QUERY_INFORMATION handler
+ * ARGUMENTS:
+ * DeviceObject = Pointer to device object structure
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PTDI_REQUEST_KERNEL_QUERY_INFORMATION Parameters;
+ PTRANSPORT_CONTEXT TranContext;
+ PIO_STACK_LOCATION IrpSp;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ Parameters = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&IrpSp->Parameters;
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (!TranContext) {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ switch (Parameters->QueryType)
+ {
+ case TDI_QUERY_ADDRESS_INFO:
+ {
+ PTDI_ADDRESS_INFO AddressInfo;
+ PADDRESS_FILE AddrFile;
+ PTA_IP_ADDRESS Address;
+ PCONNECTION_ENDPOINT Endpoint = NULL;
+
+
+ if (MmGetMdlByteCount(Irp->MdlAddress) <
+ (FIELD_OFFSET(TDI_ADDRESS_INFO, Address.Address[0].Address) +
+ sizeof(TDI_ADDRESS_IP))) {
+ TI_DbgPrint(MID_TRACE, ("MDL buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ AddressInfo = (PTDI_ADDRESS_INFO)MmGetSystemAddressForMdl(Irp->MdlAddress);
+ Address = (PTA_IP_ADDRESS)&AddressInfo->Address;
+
+ switch ((ULONG_PTR)IrpSp->FileObject->FsContext2) {
+ case TDI_TRANSPORT_ADDRESS_FILE:
+ AddrFile = (PADDRESS_FILE)TranContext->Handle.AddressHandle;
+
+ Address->TAAddressCount = 1;
+ Address->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
+ Address->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
+ Address->Address[0].Address[0].sin_port = AddrFile->Port;
+ Address->Address[0].Address[0].in_addr = AddrFile->Address.Address.IPv4Address;
+ RtlZeroMemory(
+ &Address->Address[0].Address[0].sin_zero,
+ sizeof(Address->Address[0].Address[0].sin_zero));
+ return STATUS_SUCCESS;
+
+ case TDI_CONNECTION_FILE:
+ Endpoint =
+ (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+
+ Address->TAAddressCount = 1;
+ Address->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
+ Address->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
+ Address->Address[0].Address[0].sin_port = Endpoint->AddressFile->Port;
+ Address->Address[0].Address[0].in_addr = Endpoint->AddressFile->Address.Address.IPv4Address;
+ RtlZeroMemory(
+ &Address->Address[0].Address[0].sin_zero,
+ sizeof(Address->Address[0].Address[0].sin_zero));
+ return STATUS_SUCCESS;
+
+ default:
+ TI_DbgPrint(MIN_TRACE, ("Invalid transport context\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ case TDI_QUERY_CONNECTION_INFO:
+ {
+ PTDI_CONNECTION_INFO ConnectionInfo;
+ PCONNECTION_ENDPOINT Endpoint;
+
+ if (MmGetMdlByteCount(Irp->MdlAddress) < sizeof(*ConnectionInfo)) {
+ TI_DbgPrint(MID_TRACE, ("MDL buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ ConnectionInfo = (PTDI_CONNECTION_INFO)
+ MmGetSystemAddressForMdl(Irp->MdlAddress);
+
+ switch ((ULONG_PTR)IrpSp->FileObject->FsContext2) {
+ case TDI_CONNECTION_FILE:
+ Endpoint =
+ (PCONNECTION_ENDPOINT)TranContext->Handle.ConnectionContext;
+ RtlZeroMemory(ConnectionInfo, sizeof(*ConnectionInfo));
+ return STATUS_SUCCESS;
+
+ default:
+ TI_DbgPrint(MIN_TRACE, ("Invalid transport context\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ case TDI_QUERY_MAX_DATAGRAM_INFO:
+ {
+ PTDI_MAX_DATAGRAM_INFO MaxDatagramInfo;
+
+ if (MmGetMdlByteCount(Irp->MdlAddress) < sizeof(*MaxDatagramInfo)) {
+ TI_DbgPrint(MID_TRACE, ("MDL buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ MaxDatagramInfo = (PTDI_MAX_DATAGRAM_INFO)
+ MmGetSystemAddressForMdl(Irp->MdlAddress);
+
+ MaxDatagramInfo->MaxDatagramSize = 0xFFFF;
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+
+NTSTATUS DispTdiReceive(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_RECEIVE handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PIO_STACK_LOCATION IrpSp;
+ PTDI_REQUEST_KERNEL_RECEIVE ReceiveInfo;
+ PTRANSPORT_CONTEXT TranContext;
+ NTSTATUS Status;
+ ULONG BytesReceived = 0;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ReceiveInfo = (PTDI_REQUEST_KERNEL_RECEIVE)&(IrpSp->Parameters);
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (TranContext == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (TranContext->Handle.ConnectionContext == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("No connection endpoint file object.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Initialize a receive request */
+ Status = DispPrepareIrpForCancel
+ (TranContext->Handle.ConnectionContext,
+ Irp,
+ (PDRIVER_CANCEL)DispCancelRequest);
+
+ TI_DbgPrint(MID_TRACE,("TCPIP<<< Got an MDL: %x\n", Irp->MdlAddress));
+ if (NT_SUCCESS(Status))
+ {
+ Status = TCPReceiveData(
+ TranContext->Handle.ConnectionContext,
+ (PNDIS_BUFFER)Irp->MdlAddress,
+ ReceiveInfo->ReceiveLength,
+ &BytesReceived,
+ ReceiveInfo->ReceiveFlags,
+ DispDataRequestComplete,
+ Irp);
+ }
+
+done:
+ if (Status != STATUS_PENDING) {
+ DispDataRequestComplete(Irp, Status, BytesReceived);
+ } else
+ IoMarkIrpPending(Irp);
+
+ TI_DbgPrint(DEBUG_IRP, ("Leaving. Status is (0x%X)\n", Status));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiReceiveDatagram(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_RECEIVE_DATAGRAM handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PIO_STACK_LOCATION IrpSp;
+ PTDI_REQUEST_KERNEL_RECEIVEDG DgramInfo;
+ PTRANSPORT_CONTEXT TranContext;
+ TDI_REQUEST Request;
+ NTSTATUS Status;
+ ULONG BytesReceived = 0;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ DgramInfo = (PTDI_REQUEST_KERNEL_RECEIVEDG)&(IrpSp->Parameters);
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (TranContext == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Initialize a receive request */
+ Request.Handle.AddressHandle = TranContext->Handle.AddressHandle;
+ Request.RequestNotifyObject = DispDataRequestComplete;
+ Request.RequestContext = Irp;
+
+ Status = DispPrepareIrpForCancel(
+ IrpSp->FileObject->FsContext,
+ Irp,
+ (PDRIVER_CANCEL)DispCancelRequest);
+
+ if (NT_SUCCESS(Status))
+ {
+ PVOID DataBuffer;
+ UINT BufferSize;
+
+ NdisQueryBuffer( (PNDIS_BUFFER)Irp->MdlAddress,
+ &DataBuffer,
+ &BufferSize );
+
+ Status = DGReceiveDatagram(
+ Request.Handle.AddressHandle,
+ DgramInfo->ReceiveDatagramInformation,
+ DataBuffer,
+ DgramInfo->ReceiveLength,
+ DgramInfo->ReceiveFlags,
+ DgramInfo->ReturnDatagramInformation,
+ &BytesReceived,
+ (PDATAGRAM_COMPLETION_ROUTINE)DispDataRequestComplete,
+ Irp,
+ Irp);
+ }
+
+done:
+ if (Status != STATUS_PENDING) {
+ DispDataRequestComplete(Irp, Status, BytesReceived);
+ } else
+ IoMarkIrpPending(Irp);
+
+ TI_DbgPrint(DEBUG_IRP, ("Leaving. Status is (0x%X)\n", Status));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiSend(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_SEND handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PIO_STACK_LOCATION IrpSp;
+ PTDI_REQUEST_KERNEL_SEND SendInfo;
+ PTRANSPORT_CONTEXT TranContext;
+ NTSTATUS Status;
+ ULONG BytesSent = 0;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ SendInfo = (PTDI_REQUEST_KERNEL_SEND)&(IrpSp->Parameters);
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (TranContext == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (TranContext->Handle.ConnectionContext == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("No connection endpoint file object.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ Status = DispPrepareIrpForCancel(
+ IrpSp->FileObject->FsContext,
+ Irp,
+ (PDRIVER_CANCEL)DispCancelRequest);
+
+ TI_DbgPrint(MID_TRACE,("TCPIP<<< Got an MDL: %x\n", Irp->MdlAddress));
+ if (NT_SUCCESS(Status))
+ {
+ PVOID Data;
+ UINT Len;
+
+ NdisQueryBuffer( Irp->MdlAddress, &Data, &Len );
+
+ TI_DbgPrint(MID_TRACE,("About to TCPSendData\n"));
+ Status = TCPSendData(
+ TranContext->Handle.ConnectionContext,
+ Data,
+ SendInfo->SendLength,
+ &BytesSent,
+ SendInfo->SendFlags,
+ DispDataRequestComplete,
+ Irp);
+ }
+
+done:
+ if (Status != STATUS_PENDING) {
+ DispDataRequestComplete(Irp, Status, BytesSent);
+ } else
+ IoMarkIrpPending(Irp);
+
+ TI_DbgPrint(DEBUG_IRP, ("Leaving. Status is (0x%X)\n", Status));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiSendDatagram(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_SEND_DATAGRAM handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PIO_STACK_LOCATION IrpSp;
+ TDI_REQUEST Request;
+ PTDI_REQUEST_KERNEL_SENDDG DgramInfo;
+ PTRANSPORT_CONTEXT TranContext;
+ NTSTATUS Status;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ DgramInfo = (PTDI_REQUEST_KERNEL_SENDDG)&(IrpSp->Parameters);
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (TranContext == NULL)
+ {
+ TI_DbgPrint(MID_TRACE, ("Bad transport context.\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Initialize a send request */
+ Request.Handle.AddressHandle = TranContext->Handle.AddressHandle;
+ Request.RequestNotifyObject = DispDataRequestComplete;
+ Request.RequestContext = Irp;
+
+ Status = DispPrepareIrpForCancel(
+ IrpSp->FileObject->FsContext,
+ Irp,
+ (PDRIVER_CANCEL)DispCancelRequest);
+
+ if (NT_SUCCESS(Status)) {
+ PVOID DataBuffer;
+ UINT BufferSize;
+
+ TI_DbgPrint(MID_TRACE,("About to query buffer %x\n", Irp->MdlAddress));
+
+ NdisQueryBuffer( (PNDIS_BUFFER)Irp->MdlAddress,
+ &DataBuffer,
+ &BufferSize );
+
+ /* FIXME: DgramInfo->SendDatagramInformation->RemoteAddress
+ must be of type PTDI_ADDRESS_IP */
+ TI_DbgPrint(MID_TRACE,
+ ("About to call send routine %x\n",
+ (*((PADDRESS_FILE)Request.Handle.AddressHandle)->Send)));
+
+ if( (*((PADDRESS_FILE)Request.Handle.AddressHandle)->Send != NULL) )
+ {
+ ULONG DataUsed = 0;
+ Status = (*((PADDRESS_FILE)Request.Handle.AddressHandle)->Send)(
+ Request.Handle.AddressHandle,
+ DgramInfo->SendDatagramInformation,
+ DataBuffer,
+ BufferSize,
+ &DataUsed);
+ Irp->IoStatus.Information = DataUsed;
+ }
+ else {
+ Status = STATUS_UNSUCCESSFUL;
+ ASSERT(FALSE);
+ }
+ }
+
+done:
+ if (Status != STATUS_PENDING) {
+ DispDataRequestComplete(Irp, Status, Irp->IoStatus.Information);
+ } else
+ IoMarkIrpPending(Irp);
+
+ TI_DbgPrint(DEBUG_IRP, ("Leaving.\n"));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiSetEventHandler(PIRP Irp)
+/*
+ * FUNCTION: TDI_SET_EVENT_HANDER handler
+ * ARGUMENTS:
+ * Irp = Pointer to a I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PTDI_REQUEST_KERNEL_SET_EVENT Parameters;
+ PTRANSPORT_CONTEXT TranContext;
+ PIO_STACK_LOCATION IrpSp;
+ PADDRESS_FILE AddrFile;
+ NTSTATUS Status;
+ KIRQL OldIrql;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ /* Get associated address file object. Quit if none exists */
+
+ TranContext = IrpSp->FileObject->FsContext;
+ if (!TranContext) {
+ TI_DbgPrint(MIN_TRACE, ("Bad transport context.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ AddrFile = (PADDRESS_FILE)TranContext->Handle.AddressHandle;
+ if (!AddrFile) {
+ TI_DbgPrint(MIN_TRACE, ("No address file object.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)&IrpSp->Parameters;
+ Status = STATUS_SUCCESS;
+
+ LockObject(AddrFile, &OldIrql);
+
+ /* Set the event handler. if an event handler is associated with
+ a specific event, it's flag (RegisteredXxxHandler) is TRUE.
+ If an event handler is not used it's flag is FALSE */
+ switch (Parameters->EventType) {
+ case TDI_EVENT_CONNECT:
+ if (!Parameters->EventHandler) {
+ AddrFile->ConnectHandlerContext = NULL;
+ AddrFile->RegisteredConnectHandler = FALSE;
+ } else {
+ AddrFile->ConnectHandler =
+ (PTDI_IND_CONNECT)Parameters->EventHandler;
+ AddrFile->ConnectHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredConnectHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_DISCONNECT:
+ if (!Parameters->EventHandler) {
+ AddrFile->DisconnectHandlerContext = NULL;
+ AddrFile->RegisteredDisconnectHandler = FALSE;
+ } else {
+ AddrFile->DisconnectHandler =
+ (PTDI_IND_DISCONNECT)Parameters->EventHandler;
+ AddrFile->DisconnectHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredDisconnectHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_ERROR:
+ if (Parameters->EventHandler == NULL) {
+ AddrFile->ErrorHandlerContext = NULL;
+ AddrFile->RegisteredErrorHandler = FALSE;
+ } else {
+ AddrFile->ErrorHandler =
+ (PTDI_IND_ERROR)Parameters->EventHandler;
+ AddrFile->ErrorHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredErrorHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_RECEIVE:
+ if (Parameters->EventHandler == NULL) {
+ AddrFile->ReceiveHandlerContext = NULL;
+ AddrFile->RegisteredReceiveHandler = FALSE;
+ } else {
+ AddrFile->ReceiveHandler =
+ (PTDI_IND_RECEIVE)Parameters->EventHandler;
+ AddrFile->ReceiveHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredReceiveHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_RECEIVE_DATAGRAM:
+ if (Parameters->EventHandler == NULL) {
+ AddrFile->ReceiveDatagramHandlerContext = NULL;
+ AddrFile->RegisteredReceiveDatagramHandler = FALSE;
+ } else {
+ AddrFile->ReceiveDatagramHandler =
+ (PTDI_IND_RECEIVE_DATAGRAM)Parameters->EventHandler;
+ AddrFile->ReceiveDatagramHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredReceiveDatagramHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_RECEIVE_EXPEDITED:
+ if (Parameters->EventHandler == NULL) {
+ AddrFile->ExpeditedReceiveHandlerContext = NULL;
+ AddrFile->RegisteredExpeditedReceiveHandler = FALSE;
+ } else {
+ AddrFile->ExpeditedReceiveHandler =
+ (PTDI_IND_RECEIVE_EXPEDITED)Parameters->EventHandler;
+ AddrFile->ExpeditedReceiveHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredExpeditedReceiveHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_CHAINED_RECEIVE:
+ if (Parameters->EventHandler == NULL) {
+ AddrFile->ChainedReceiveHandlerContext = NULL;
+ AddrFile->RegisteredChainedReceiveHandler = FALSE;
+ } else {
+ AddrFile->ChainedReceiveHandler =
+ (PTDI_IND_CHAINED_RECEIVE)Parameters->EventHandler;
+ AddrFile->ChainedReceiveHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredChainedReceiveHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_CHAINED_RECEIVE_DATAGRAM:
+ if (Parameters->EventHandler == NULL) {
+ AddrFile->ChainedReceiveDatagramHandlerContext = NULL;
+ AddrFile->RegisteredChainedReceiveDatagramHandler = FALSE;
+ } else {
+ AddrFile->ChainedReceiveDatagramHandler =
+ (PTDI_IND_CHAINED_RECEIVE_DATAGRAM)Parameters->EventHandler;
+ AddrFile->ChainedReceiveDatagramHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredChainedReceiveDatagramHandler = TRUE;
+ }
+ break;
+
+ case TDI_EVENT_CHAINED_RECEIVE_EXPEDITED:
+ if (Parameters->EventHandler == NULL) {
+ AddrFile->ChainedReceiveExpeditedHandlerContext = NULL;
+ AddrFile->RegisteredChainedReceiveExpeditedHandler = FALSE;
+ } else {
+ AddrFile->ChainedReceiveExpeditedHandler =
+ (PTDI_IND_CHAINED_RECEIVE_EXPEDITED)Parameters->EventHandler;
+ AddrFile->ChainedReceiveExpeditedHandlerContext = Parameters->EventContext;
+ AddrFile->RegisteredChainedReceiveExpeditedHandler = TRUE;
+ }
+ break;
+
+ default:
+ TI_DbgPrint(MIN_TRACE, ("Unknown event type (0x%X).\n",
+ Parameters->EventType));
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ UnlockObject(AddrFile, OldIrql);
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiSetInformation(
+ PIRP Irp)
+/*
+ * FUNCTION: TDI_SET_INFORMATION handler
+ * ARGUMENTS:
+ * Irp = Pointer to an I/O request packet
+ * RETURNS:
+ * Status of operation
+ */
+{
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+
+VOID DispTdiQueryInformationExComplete(
+ PVOID Context,
+ ULONG Status,
+ UINT ByteCount)
+/*
+ * FUNCTION: Completes a TDI QueryInformationEx request
+ * ARGUMENTS:
+ * Context = Pointer to the IRP for the request
+ * Status = TDI status of the request
+ * ByteCount = Number of bytes returned in output buffer
+ */
+{
+ PTI_QUERY_CONTEXT QueryContext;
+
+ QueryContext = (PTI_QUERY_CONTEXT)Context;
+ if (NT_SUCCESS(Status)) {
+ CopyBufferToBufferChain(
+ QueryContext->InputMdl,
+ FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context),
+ (PCHAR)&QueryContext->QueryInfo.Context,
+ CONTEXT_SIZE);
+ }
+
+ MmUnlockPages(QueryContext->InputMdl);
+ IoFreeMdl(QueryContext->InputMdl);
+ if( QueryContext->OutputMdl ) {
+ MmUnlockPages(QueryContext->OutputMdl);
+ IoFreeMdl(QueryContext->OutputMdl);
+ }
+
+ QueryContext->Irp->IoStatus.Information = ByteCount;
+ QueryContext->Irp->IoStatus.Status = Status;
+
+ ExFreePoolWithTag(QueryContext, QUERY_CONTEXT_TAG);
+}
+
+
+NTSTATUS DispTdiQueryInformationEx(
+ PIRP Irp,
+ PIO_STACK_LOCATION IrpSp)
+/*
+ * FUNCTION: TDI QueryInformationEx handler
+ * ARGUMENTS:
+ * Irp = Pointer to I/O request packet
+ * IrpSp = Pointer to current stack location of Irp
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PTCP_REQUEST_QUERY_INFORMATION_EX InputBuffer;
+ PTRANSPORT_CONTEXT TranContext;
+ PTI_QUERY_CONTEXT QueryContext;
+ PVOID OutputBuffer;
+ TDI_REQUEST Request;
+ UINT Size;
+ UINT InputBufferLength;
+ UINT OutputBufferLength;
+ BOOLEAN InputMdlLocked = FALSE;
+ BOOLEAN OutputMdlLocked = FALSE;
+ PMDL InputMdl = NULL;
+ PMDL OutputMdl = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ TranContext = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext;
+
+ switch ((ULONG_PTR)IrpSp->FileObject->FsContext2) {
+ case TDI_TRANSPORT_ADDRESS_FILE:
+ Request.Handle.AddressHandle = TranContext->Handle.AddressHandle;
+ break;
+
+ case TDI_CONNECTION_FILE:
+ Request.Handle.ConnectionContext = TranContext->Handle.ConnectionContext;
+ break;
+
+ case TDI_CONTROL_CHANNEL_FILE:
+ Request.Handle.ControlChannel = TranContext->Handle.ControlChannel;
+ break;
+
+ default:
+ TI_DbgPrint(MIN_TRACE, ("Invalid transport context\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ /* Validate parameters */
+ if ((InputBufferLength == sizeof(TCP_REQUEST_QUERY_INFORMATION_EX)) &&
+ (OutputBufferLength != 0)) {
+
+ InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX)
+ IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+ OutputBuffer = Irp->UserBuffer;
+
+ QueryContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(TI_QUERY_CONTEXT), QUERY_CONTEXT_TAG);
+ if (QueryContext) {
+ _SEH2_TRY {
+ InputMdl = IoAllocateMdl(InputBuffer,
+ sizeof(TCP_REQUEST_QUERY_INFORMATION_EX),
+ FALSE, TRUE, NULL);
+
+ OutputMdl = IoAllocateMdl(OutputBuffer,
+ OutputBufferLength, FALSE, TRUE, NULL);
+
+ if (InputMdl && OutputMdl) {
+
+ MmProbeAndLockPages(InputMdl, Irp->RequestorMode,
+ IoModifyAccess);
+
+ InputMdlLocked = TRUE;
+
+ MmProbeAndLockPages(OutputMdl, Irp->RequestorMode,
+ IoWriteAccess);
+
+ OutputMdlLocked = TRUE;
+
+ RtlCopyMemory(&QueryContext->QueryInfo,
+ InputBuffer, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
+ } else
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+
+ if (NT_SUCCESS(Status)) {
+ Size = MmGetMdlByteCount(OutputMdl);
+
+ QueryContext->Irp = Irp;
+ QueryContext->InputMdl = InputMdl;
+ QueryContext->OutputMdl = OutputMdl;
+
+ Request.RequestNotifyObject = DispTdiQueryInformationExComplete;
+ Request.RequestContext = QueryContext;
+ Status = InfoTdiQueryInformationEx(&Request,
+ &QueryContext->QueryInfo.ID, OutputMdl,
+ &Size, &QueryContext->QueryInfo.Context);
+ DispTdiQueryInformationExComplete(QueryContext, Status, Size);
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving. Status = (0x%X)\n", Status));
+
+ return Status;
+ }
+
+ /* An error occurred if we get here */
+
+ if (InputMdl) {
+ if (InputMdlLocked)
+ MmUnlockPages(InputMdl);
+ IoFreeMdl(InputMdl);
+ }
+
+ if (OutputMdl) {
+ if (OutputMdlLocked)
+ MmUnlockPages(OutputMdl);
+ IoFreeMdl(OutputMdl);
+ }
+
+ ExFreePoolWithTag(QueryContext, QUERY_CONTEXT_TAG);
+ } else
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else if( InputBufferLength ==
+ sizeof(TCP_REQUEST_QUERY_INFORMATION_EX) ) {
+ /* Handle the case where the user is probing the buffer for length */
+ TI_DbgPrint(MAX_TRACE, ("InputBufferLength %d OutputBufferLength %d\n",
+ InputBufferLength, OutputBufferLength));
+ InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX)
+ IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+
+ Size = 0;
+
+ QueryContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(TI_QUERY_CONTEXT), QUERY_CONTEXT_TAG);
+ if (!QueryContext) return STATUS_INSUFFICIENT_RESOURCES;
+
+ _SEH2_TRY {
+ InputMdl = IoAllocateMdl(InputBuffer,
+ sizeof(TCP_REQUEST_QUERY_INFORMATION_EX),
+ FALSE, TRUE, NULL);
+
+ MmProbeAndLockPages(InputMdl, Irp->RequestorMode,
+ IoModifyAccess);
+
+ InputMdlLocked = TRUE;
+ Status = STATUS_SUCCESS;
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ TI_DbgPrint(MAX_TRACE, ("Failed to acquire client buffer\n"));
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+
+ if( !NT_SUCCESS(Status) || !InputMdl ) {
+ if( InputMdl ) IoFreeMdl( InputMdl );
+ ExFreePoolWithTag(QueryContext, QUERY_CONTEXT_TAG);
+ return Status;
+ }
+
+ RtlCopyMemory(&QueryContext->QueryInfo,
+ InputBuffer, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
+
+ QueryContext->Irp = Irp;
+ QueryContext->InputMdl = InputMdl;
+ QueryContext->OutputMdl = NULL;
+
+ Request.RequestNotifyObject = DispTdiQueryInformationExComplete;
+ Request.RequestContext = QueryContext;
+ Status = InfoTdiQueryInformationEx(&Request,
+ &QueryContext->QueryInfo.ID,
+ NULL,
+ &Size,
+ &QueryContext->QueryInfo.Context);
+ DispTdiQueryInformationExComplete(QueryContext, Status, Size);
+ TI_DbgPrint(MAX_TRACE, ("Leaving. Status = (0x%X)\n", Status));
+ } else Status = STATUS_INVALID_PARAMETER;
+
+ TI_DbgPrint(MIN_TRACE, ("Leaving. Status = (0x%X)\n", Status));
+
+ return Status;
+}
+
+
+NTSTATUS DispTdiSetInformationEx(
+ PIRP Irp,
+ PIO_STACK_LOCATION IrpSp)
+/*
+ * FUNCTION: TDI SetInformationEx handler
+ * ARGUMENTS:
+ * Irp = Pointer to I/O request packet
+ * IrpSp = Pointer to current stack location of Irp
+ * RETURNS:
+ * Status of operation
+ */
+{
+ PTRANSPORT_CONTEXT TranContext;
+ PTCP_REQUEST_SET_INFORMATION_EX Info;
+ TDI_REQUEST Request;
+ TDI_STATUS Status;
+
+ TI_DbgPrint(DEBUG_IRP, ("Called.\n"));
+
+ TranContext = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext;
+ Info = (PTCP_REQUEST_SET_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;
+
+ switch ((ULONG_PTR)IrpSp->FileObject->FsContext2) {
+ case TDI_TRANSPORT_ADDRESS_FILE:
+ Request.Handle.AddressHandle = TranContext->Handle.AddressHandle;
+ break;
+
+ case TDI_CONNECTION_FILE:
+ Request.Handle.ConnectionContext = TranContext->Handle.ConnectionContext;
+ break;
+
+ case TDI_CONTROL_CHANNEL_FILE:
+ Request.Handle.ControlChannel = TranContext->Handle.ControlChannel;
+ break;
+
+ default:
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Information = 0;
+
+ TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp));
+
+ return Irp->IoStatus.Status;
+ }
+
+ Status = DispPrepareIrpForCancel(TranContext, Irp, NULL);
+ if (NT_SUCCESS(Status)) {
+ Request.RequestNotifyObject = DispDataRequestComplete;
+ Request.RequestContext = Irp;
+
+ Status = InfoTdiSetInformationEx(&Request, &Info->ID,
+ &Info->Buffer, Info->BufferSize);
+ }
+
+ return Status;
+}
+
+/* TODO: Support multiple addresses per interface.
+ * For now just set the nte context to the interface index.
+ *
+ * Later on, create an NTE context and NTE instance
+ */
+
+NTSTATUS DispTdiSetIPAddress( PIRP Irp, PIO_STACK_LOCATION IrpSp ) {
+ NTSTATUS Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ PIP_SET_ADDRESS IpAddrChange =
+ (PIP_SET_ADDRESS)Irp->AssociatedIrp.SystemBuffer;
+ IF_LIST_ITER(IF);
+
+ TI_DbgPrint(MID_TRACE,("Setting IP Address for adapter %d\n",
+ IpAddrChange->NteIndex));
+
+ ForEachInterface(IF) {
+ TI_DbgPrint(MID_TRACE,("Looking at adapter %d\n", IF->Index));
+
+ if( IF->Unicast.Address.IPv4Address == IpAddrChange->Address ) {
+ Status = STATUS_DUPLICATE_OBJECTID;
+ break;
+ }
+ if( IF->Index == IpAddrChange->NteIndex ) {
+ IPRemoveInterfaceRoute( IF );
+
+ IF->Unicast.Type = IP_ADDRESS_V4;
+ IF->Unicast.Address.IPv4Address = IpAddrChange->Address;
+ IF->Netmask.Type = IP_ADDRESS_V4;
+ IF->Netmask.Address.IPv4Address = IpAddrChange->Netmask;
+ IF->Broadcast.Type = IP_ADDRESS_V4;
+ IF->Broadcast.Address.IPv4Address =
+ IF->Unicast.Address.IPv4Address |
+ ~IF->Netmask.Address.IPv4Address;
+
+ TI_DbgPrint(MID_TRACE,("New Unicast Address: %x\n",
+ IF->Unicast.Address.IPv4Address));
+ TI_DbgPrint(MID_TRACE,("New Netmask : %x\n",
+ IF->Netmask.Address.IPv4Address));
+
+ IPAddInterfaceRoute( IF );
+
+ IpAddrChange->Address = IF->Index;
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = IF->Index;
+ break;
+ }
+ } EndFor(IF);
+
+ Irp->IoStatus.Status = Status;
+ return Status;
+}
+
+NTSTATUS DispTdiDeleteIPAddress( PIRP Irp, PIO_STACK_LOCATION IrpSp ) {
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PUSHORT NteIndex = Irp->AssociatedIrp.SystemBuffer;
+ IF_LIST_ITER(IF);
+
+ ForEachInterface(IF) {
+ if( IF->Index == *NteIndex ) {
+ IPRemoveInterfaceRoute( IF );
+ IF->Unicast.Type = IP_ADDRESS_V4;
+ IF->Unicast.Address.IPv4Address = 0;
+ IF->Netmask.Type = IP_ADDRESS_V4;
+ IF->Netmask.Address.IPv4Address = 0;
+ IF->Broadcast.Type = IP_ADDRESS_V4;
+ IF->Broadcast.Address.IPv4Address = 0;
+ Status = STATUS_SUCCESS;
+ }
+ } EndFor(IF);
+
+ Irp->IoStatus.Status = Status;
+ return Status;
+}
+
+/* EOF */
--- /dev/null
- AddrFile->Listener->AddressFile = NULL;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS TCP/IP protocol driver
+ * FILE: tcpip/fileobjs.c
+ * PURPOSE: Routines for handling file objects
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ * CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+/* List of all address file objects managed by this driver */
+LIST_ENTRY AddressFileListHead;
+KSPIN_LOCK AddressFileListLock;
+
+/* List of all connection endpoint file objects managed by this driver */
+LIST_ENTRY ConnectionEndpointListHead;
+KSPIN_LOCK ConnectionEndpointListLock;
+
+/*
+ * FUNCTION: Searches through address file entries to find the first match
+ * ARGUMENTS:
+ * Address = IP address
+ * Port = Port number
+ * Protocol = Protocol number
+ * SearchContext = Pointer to search context
+ * RETURNS:
+ * Pointer to address file, NULL if none was found
+ */
+PADDRESS_FILE AddrSearchFirst(
+ PIP_ADDRESS Address,
+ USHORT Port,
+ USHORT Protocol,
+ PAF_SEARCH SearchContext)
+{
+ SearchContext->Address = Address;
+ SearchContext->Port = Port;
+ SearchContext->Next = AddressFileListHead.Flink;
+ SearchContext->Protocol = Protocol;
+
+ return AddrSearchNext(SearchContext);
+}
+
+BOOLEAN AddrIsBroadcastMatch(
+ PIP_ADDRESS UnicastAddress,
+ PIP_ADDRESS BroadcastAddress ) {
+ IF_LIST_ITER(IF);
+
+ ForEachInterface(IF) {
+ if ((AddrIsUnspecified(UnicastAddress) ||
+ AddrIsEqual(&IF->Unicast, UnicastAddress)) &&
+ (AddrIsEqual(&IF->Broadcast, BroadcastAddress)))
+ return TRUE;
+ } EndFor(IF);
+
+ return FALSE;
+}
+
+BOOLEAN AddrReceiveMatch(
+ PIP_ADDRESS LocalAddress,
+ PIP_ADDRESS RemoteAddress)
+{
+ if (AddrIsEqual(LocalAddress, RemoteAddress))
+ {
+ /* Unicast address match */
+ return TRUE;
+ }
+
+ if (AddrIsBroadcastMatch(LocalAddress, RemoteAddress))
+ {
+ /* Broadcast address match */
+ return TRUE;
+ }
+
+ if (AddrIsUnspecified(LocalAddress))
+ {
+ /* Local address unspecified */
+ return TRUE;
+ }
+
+ if (AddrIsUnspecified(RemoteAddress))
+ {
+ /* Remote address unspecified */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * FUNCTION: Searches through address file entries to find next match
+ * ARGUMENTS:
+ * SearchContext = Pointer to search context
+ * RETURNS:
+ * Pointer to address file, NULL if none was found
+ */
+PADDRESS_FILE AddrSearchNext(
+ PAF_SEARCH SearchContext)
+{
+ PLIST_ENTRY CurrentEntry;
+ PIP_ADDRESS IPAddress;
+ KIRQL OldIrql;
+ PADDRESS_FILE Current = NULL;
+ BOOLEAN Found = FALSE;
+
+ if (IsListEmpty(SearchContext->Next))
+ return NULL;
+
+ CurrentEntry = SearchContext->Next;
+
+ TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
+
+ while (CurrentEntry != &AddressFileListHead) {
+ Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry);
+
+ IPAddress = &Current->Address;
+
+ TI_DbgPrint(DEBUG_ADDRFILE, ("Comparing: ((%d, %d, %s), (%d, %d, %s)).\n",
+ WN2H(Current->Port),
+ Current->Protocol,
+ A2S(IPAddress),
+ WN2H(SearchContext->Port),
+ SearchContext->Protocol,
+ A2S(SearchContext->Address)));
+
+ /* See if this address matches the search criteria */
+ if ((Current->Port == SearchContext->Port) &&
+ (Current->Protocol == SearchContext->Protocol) &&
+ (AddrReceiveMatch(IPAddress, SearchContext->Address))) {
+ /* We've found a match */
+ Found = TRUE;
+ break;
+ }
+ CurrentEntry = CurrentEntry->Flink;
+ }
+
+ TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
+
+ if (Found) {
+ SearchContext->Next = CurrentEntry->Flink;
+ return Current;
+ } else
+ return NULL;
+}
+
+VOID AddrFileFree(
+ PVOID Object)
+/*
+ * FUNCTION: Frees an address file object
+ * ARGUMENTS:
+ * Object = Pointer to address file object to free
+ */
+{
+ PADDRESS_FILE AddrFile = Object;
+ KIRQL OldIrql;
+ PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
+ PDATAGRAM_SEND_REQUEST SendRequest;
+ PLIST_ENTRY CurrentEntry;
+
+ TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+ /* We should not be associated with a connection here */
+ ASSERT(!AddrFile->Connection);
+
+ /* Remove address file from the global list */
+ TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
+ RemoveEntryList(&AddrFile->ListEntry);
+ TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
+
+ /* FIXME: Kill TCP connections on this address file object */
+
+ /* Return pending requests with error */
+
+ TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile));
+
+ /* Go through pending receive request list and cancel them all */
+ while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
+ ReceiveRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
+ (*ReceiveRequest->Complete)(ReceiveRequest->Context, STATUS_CANCELLED, 0);
+ /* ExFreePoolWithTag(ReceiveRequest, DATAGRAM_RECV_TAG); FIXME: WTF? */
+ }
+
+ TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting send requests on address file at (0x%X).\n", AddrFile));
+
+ /* Go through pending send request list and cancel them all */
+ while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
+ SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
+ (*SendRequest->Complete)(SendRequest->Context, STATUS_CANCELLED, 0);
+ ExFreePoolWithTag(SendRequest, DATAGRAM_SEND_TAG);
+ }
+
+ /* Protocol specific handling */
+ switch (AddrFile->Protocol) {
+ case IPPROTO_TCP:
+ if (AddrFile->Port)
+ {
+ TCPFreePort(AddrFile->Port);
+ }
+ break;
+
+ case IPPROTO_UDP:
+ UDPFreePort( AddrFile->Port );
+ break;
+ }
+
+ RemoveEntityByContext(AddrFile);
+
+ ExFreePoolWithTag(Object, ADDR_FILE_TAG);
+}
+
+
+VOID ControlChannelFree(
+ PVOID Object)
+/*
+ * FUNCTION: Frees an address file object
+ * ARGUMENTS:
+ * Object = Pointer to address file object to free
+ */
+{
+ ExFreePoolWithTag(Object, CONTROL_CHANNEL_TAG);
+}
+
+
+/*
+ * FUNCTION: Open an address file object
+ * ARGUMENTS:
+ * Request = Pointer to TDI request structure for this request
+ * Address = Pointer to address to be opened
+ * Protocol = Protocol on which to open the address
+ * Options = Pointer to option buffer
+ * RETURNS:
+ * Status of operation
+ */
+NTSTATUS FileOpenAddress(
+ PTDI_REQUEST Request,
+ PTA_IP_ADDRESS Address,
+ USHORT Protocol,
+ PVOID Options)
+{
+ PADDRESS_FILE AddrFile;
+
+ TI_DbgPrint(MID_TRACE, ("Called (Proto %d).\n", Protocol));
+
+ AddrFile = ExAllocatePoolWithTag(NonPagedPool, sizeof(ADDRESS_FILE),
+ ADDR_FILE_TAG);
+ if (!AddrFile) {
+ TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(AddrFile, sizeof(ADDRESS_FILE));
+
+ AddrFile->RefCount = 1;
+ AddrFile->Free = AddrFileFree;
+
+ /* Set our default options */
+ AddrFile->TTL = 128;
+ AddrFile->DF = 0;
+ AddrFile->BCast = 1;
+ AddrFile->HeaderIncl = 1;
+
+ /* Make sure address is a local unicast address or 0 */
+ /* FIXME: IPv4 only */
+ AddrFile->Family = Address->Address[0].AddressType;
+ AddrFile->Address.Address.IPv4Address = Address->Address[0].Address[0].in_addr;
+ AddrFile->Address.Type = IP_ADDRESS_V4;
+
+ if (!AddrIsUnspecified(&AddrFile->Address) &&
+ !AddrLocateInterface(&AddrFile->Address)) {
+ ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
+ TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", A2S(&AddrFile->Address)));
+ return STATUS_INVALID_ADDRESS;
+ }
+
+ TI_DbgPrint(MID_TRACE, ("Opening address %s for communication (P=%d U=%d).\n",
+ A2S(&AddrFile->Address), Protocol, IPPROTO_UDP));
+
+ /* Protocol specific handling */
+ switch (Protocol) {
+ case IPPROTO_TCP:
+ if (Address->Address[0].Address[0].sin_port)
+ {
+ /* The client specified an explicit port so we force a bind to this */
+ AddrFile->Port = TCPAllocatePort(Address->Address[0].Address[0].sin_port);
+
+ /* Check for bind success */
+ if (AddrFile->Port == 0xffff)
+ {
+ ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
+ return STATUS_ADDRESS_ALREADY_EXISTS;
+ }
+
+ /* Sanity check */
+ ASSERT(Address->Address[0].Address[0].sin_port == AddrFile->Port);
+ }
+ else if (!AddrIsUnspecified(&AddrFile->Address))
+ {
+ /* The client is trying to bind to a local address so allocate a port now too */
+ AddrFile->Port = TCPAllocatePort(0);
+
+ /* Check for bind success */
+ if (AddrFile->Port == 0xffff)
+ {
+ ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
+ return STATUS_ADDRESS_ALREADY_EXISTS;
+ }
+ }
+ else
+ {
+ /* The client wants an unspecified port with an unspecified address so we wait to see what the TCP library gives us */
+ AddrFile->Port = 0;
+ }
+
+ AddEntity(CO_TL_ENTITY, AddrFile, CO_TL_TCP);
+
+ AddrFile->Send = NULL; /* TCPSendData */
+ break;
+
+ case IPPROTO_UDP:
+ TI_DbgPrint(MID_TRACE,("Allocating udp port\n"));
+ AddrFile->Port =
+ UDPAllocatePort(Address->Address[0].Address[0].sin_port);
+
+ if ((Address->Address[0].Address[0].sin_port &&
+ AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
+ AddrFile->Port == 0xffff)
+ {
+ ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
+ return STATUS_ADDRESS_ALREADY_EXISTS;
+ }
+
+ TI_DbgPrint(MID_TRACE,("Setting port %d (wanted %d)\n",
+ AddrFile->Port,
+ Address->Address[0].Address[0].sin_port));
+
+ AddEntity(CL_TL_ENTITY, AddrFile, CL_TL_UDP);
+
+ AddrFile->Send = UDPSendDatagram;
+ break;
+
+ case IPPROTO_ICMP:
+ AddrFile->Port = 0;
+ AddrFile->Send = ICMPSendDatagram;
+
+ /* FIXME: Verify this */
+ AddEntity(ER_ENTITY, AddrFile, ER_ICMP);
+ break;
+
+ default:
+ /* Use raw IP for all other protocols */
+ AddrFile->Port = 0;
+ AddrFile->Send = RawIPSendDatagram;
+
+ /* FIXME: Verify this */
+ AddEntity(CL_TL_ENTITY, AddrFile, 0);
+ break;
+ }
+
+ TI_DbgPrint(MID_TRACE, ("IP protocol number for address file object is %d.\n",
+ Protocol));
+
+ TI_DbgPrint(MID_TRACE, ("Port number for address file object is %d.\n",
+ WN2H(AddrFile->Port)));
+
+ /* Set protocol */
+ AddrFile->Protocol = Protocol;
+
+ /* Initialize receive and transmit queues */
+ InitializeListHead(&AddrFile->ReceiveQueue);
+ InitializeListHead(&AddrFile->TransmitQueue);
+
+ /* Initialize spin lock that protects the address file object */
+ KeInitializeSpinLock(&AddrFile->Lock);
+
+ /* Return address file object */
+ Request->Handle.AddressHandle = AddrFile;
+
+ /* Add address file to global list */
+ ExInterlockedInsertTailList(
+ &AddressFileListHead,
+ &AddrFile->ListEntry,
+ &AddressFileListLock);
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+ return STATUS_SUCCESS;
+}
+
+
+/*
+ * FUNCTION: Closes an address file object
+ * ARGUMENTS:
+ * Request = Pointer to TDI request structure for this request
+ * RETURNS:
+ * Status of operation
+ */
+NTSTATUS FileCloseAddress(
+ PTDI_REQUEST Request)
+{
+ PADDRESS_FILE AddrFile = Request->Handle.AddressHandle;
+ KIRQL OldIrql;
+
+ if (!Request->Handle.AddressHandle) return STATUS_INVALID_PARAMETER;
+
+ LockObject(AddrFile, &OldIrql);
+
+ /* We have to close this listener because we started it */
+ if( AddrFile->Listener )
+ {
+ TCPClose( AddrFile->Listener );
+ }
+
+ UnlockObject(AddrFile, OldIrql);
+
+ DereferenceObject(AddrFile);
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+ return STATUS_SUCCESS;
+}
+
+
+/*
+ * FUNCTION: Opens a connection file object
+ * ARGUMENTS:
+ * Request = Pointer to TDI request structure for this request
+ * ClientContext = Pointer to client context information
+ * RETURNS:
+ * Status of operation
+ */
+NTSTATUS FileOpenConnection(
+ PTDI_REQUEST Request,
+ PVOID ClientContext)
+{
+ NTSTATUS Status;
+ PCONNECTION_ENDPOINT Connection;
+
+ TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+ Connection = TCPAllocateConnectionEndpoint( ClientContext );
+
+ if( !Connection ) return STATUS_NO_MEMORY;
+
+ Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
+
+ if( !NT_SUCCESS(Status) ) {
+ DereferenceObject( Connection );
+ return Status;
+ }
+
+ /* Return connection endpoint file object */
+ Request->Handle.ConnectionContext = Connection;
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+ return STATUS_SUCCESS;
+}
+
+/*
+ * FUNCTION: Closes an connection file object
+ * ARGUMENTS:
+ * Request = Pointer to TDI request structure for this request
+ * RETURNS:
+ * Status of operation
+ */
+NTSTATUS FileCloseConnection(
+ PTDI_REQUEST Request)
+{
+ PCONNECTION_ENDPOINT Connection;
+
+ TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+ Connection = Request->Handle.ConnectionContext;
+
+ if (!Connection) return STATUS_INVALID_PARAMETER;
+
+ TCPClose( Connection );
+
+ Request->Handle.ConnectionContext = NULL;
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+ return STATUS_SUCCESS;
+}
+
+/*
+ * FUNCTION: Opens a control channel file object
+ * ARGUMENTS:
+ * Request = Pointer to TDI request structure for this request
+ * RETURNS:
+ * Status of operation
+ */
+NTSTATUS FileOpenControlChannel(
+ PTDI_REQUEST Request)
+{
+ PCONTROL_CHANNEL ControlChannel;
+ TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+ ControlChannel = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ControlChannel),
+ CONTROL_CHANNEL_TAG);
+
+ if (!ControlChannel) {
+ TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
+
+ /* Make sure address is a local unicast address or 0 */
+
+ /* Locate address entry. If specified address is 0, a random address is chosen */
+
+ /* Initialize receive and transmit queues */
+ InitializeListHead(&ControlChannel->ListEntry);
+
+ /* Initialize spin lock that protects the address file object */
+ KeInitializeSpinLock(&ControlChannel->Lock);
+
+ ControlChannel->RefCount = 1;
+ ControlChannel->Free = ControlChannelFree;
+
+ /* Return address file object */
+ Request->Handle.ControlChannel = ControlChannel;
+
+ TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+ return STATUS_SUCCESS;
+}
+
+/*
+ * FUNCTION: Closes a control channel file object
+ * ARGUMENTS:
+ * Request = Pointer to TDI request structure for this request
+ * RETURNS:
+ * Status of operation
+ */
+NTSTATUS FileCloseControlChannel(
+ PTDI_REQUEST Request)
+{
+ if (!Request->Handle.ControlChannel) return STATUS_INVALID_PARAMETER;
+
+ DereferenceObject((PCONTROL_CHANNEL)Request->Handle.ControlChannel);
+
+ Request->Handle.ControlChannel = NULL;
+
+ return STATUS_SUCCESS;
+}
+
+/* EOF */
--- /dev/null
- <directory name="oskittcp">
- <xi:include href="oskittcp/oskittcp.rbuild" />
+<?xml version="1.0"?>
+<!DOCTYPE group SYSTEM "../../tools/rbuild/project.dtd">
+<group xmlns:xi="http://www.w3.org/2001/XInclude">
+ <directory name="csq">
+ <xi:include href="csq/csq.rbuild" />
+ </directory>
+ <directory name="ip">
+ <xi:include href="ip/ip.rbuild" />
+ </directory>
++ <directory name="lwip">
++ <xi:include href="lwip/lwip.rbuild" />
+ </directory>
+ <directory name="chew">
+ <xi:include href="chew/chew.rbuild" />
+ </directory>
+ <directory name="sound">
+ <xi:include href="sound/sound.rbuild" />
+ </directory>
+</group>
--- /dev/null
- <include base="oskittcp">include</include>
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="ip" type="staticlibrary">
+ <define name="__NTDRIVER__"/>
+ <include base="tcpip">include</include>
++ <include base="lwip">src/include</include>
++ <include base="lwip">src/include/ipv4</include>
+ <directory name="network">
+ <if property="ARCH" value="i386">
+ <directory name="i386">
+ <file>checksum.S</file>
+ </directory>
+ </if>
+ <file>address.c</file>
+ <file>arp.c</file>
+ <file>checksum.c</file>
+ <file>icmp.c</file>
+ <file>interface.c</file>
+ <file>ip.c</file>
+ <file>loopback.c</file>
+ <file>neighbor.c</file>
+ <file>ports.c</file>
+ <file>receive.c</file>
+ <file>router.c</file>
+ <file>routines.c</file>
+ <file>transmit.c</file>
+ </directory>
+ <directory name="transport">
+ <directory name="datagram">
+ <file>datagram.c</file>
+ </directory>
+ <directory name="rawip">
+ <file>rawip.c</file>
+ </directory>
+ <directory name="tcp">
+ <file>accept.c</file>
+ <file>event.c</file>
+ <file>if.c</file>
+ <file>tcp.c</file>
+ </directory>
+ <directory name="udp">
+ <file>udp.c</file>
+ </directory>
+ </directory>
+ <pch>precomp.h</pch>
+</module>
--- /dev/null
- static VOID DisplayTCPHeader(
- PCHAR Header,
- UINT Length)
- {
- /* FIXME: IPv4 only */
- PIPv4_HEADER IPHeader = (PIPv4_HEADER)Header;
- PTCPv4_HEADER TCPHeader;
-
- if (IPHeader->Protocol != IPPROTO_TCP) {
- DbgPrint("This is not a TCP datagram. Protocol is %d\n", IPHeader->Protocol);
- return;
- }
-
- TCPHeader = (PTCPv4_HEADER)((PCHAR)IPHeader + (IPHeader->VerIHL & 0x0F) * 4);
-
- DbgPrint("TCP header:\n");
- DbgPrint(" SourcePort: %d\n", WN2H(TCPHeader->SourcePort));
- DbgPrint(" DestinationPort: %d\n", WN2H(TCPHeader->DestinationPort));
- DbgPrint(" SequenceNumber: 0x%x\n", DN2H(TCPHeader->SequenceNumber));
- DbgPrint(" AckNumber: 0x%x\n", DN2H(TCPHeader->AckNumber));
- DbgPrint(" DataOffset: 0x%x (0x%x) 32-bit words\n", TCPHeader->DataOffset, TCPHeader->DataOffset >> 4);
- DbgPrint(" Flags: 0x%x (0x%x)\n", TCPHeader->Flags, TCPHeader->Flags & 0x3F);
- if ((TCPHeader->Flags & TCP_URG) > 0) DbgPrint(" TCP_URG - Urgent Pointer field significant\n");
- if ((TCPHeader->Flags & TCP_ACK) > 0) DbgPrint(" TCP_ACK - Acknowledgement field significant\n");
- if ((TCPHeader->Flags & TCP_PSH) > 0) DbgPrint(" TCP_PSH - Push Function\n");
- if ((TCPHeader->Flags & TCP_RST) > 0) DbgPrint(" TCP_RST - Reset the connection\n");
- if ((TCPHeader->Flags & TCP_SYN) > 0) DbgPrint(" TCP_SYN - Synchronize sequence numbers\n");
- if ((TCPHeader->Flags & TCP_FIN) > 0) DbgPrint(" TCP_FIN - No more data from sender\n");
- DbgPrint(" Window: 0x%x\n", WN2H(TCPHeader->Window));
- DbgPrint(" Checksum: 0x%x\n", WN2H(TCPHeader->Checksum));
- DbgPrint(" Urgent: 0x%x\n", WN2H(TCPHeader->Urgent));
- }
-
-
- VOID DisplayTCPPacket(
- PIP_PACKET IPPacket)
- {
- UINT Length;
- PCHAR Buffer;
-
- if ((DbgQueryDebugFilterState(DPFLTR_TCPIP_ID, DEBUG_PBUFFER | DPFLTR_MASK) != TRUE) ||
- (DbgQueryDebugFilterState(DPFLTR_TCPIP_ID, DEBUG_TCP | DPFLTR_MASK) != TRUE)) {
- return;
- }
-
- if (!IPPacket) {
- TI_DbgPrint(MIN_TRACE, ("Cannot display null packet.\n"));
- return;
- }
-
- DisplayIPPacket(IPPacket);
-
- TI_DbgPrint(MIN_TRACE, ("IPPacket is at (0x%X).\n", IPPacket));
- TI_DbgPrint(MIN_TRACE, ("Header buffer is at (0x%X).\n", IPPacket->Header));
- TI_DbgPrint(MIN_TRACE, ("Header size is (%d).\n", IPPacket->HeaderSize));
- TI_DbgPrint(MIN_TRACE, ("TotalSize (%d).\n", IPPacket->TotalSize));
- TI_DbgPrint(MIN_TRACE, ("ContigSize (%d).\n", IPPacket->ContigSize));
- TI_DbgPrint(MIN_TRACE, ("NdisPacket (0x%X).\n", IPPacket->NdisPacket));
-
- if (IPPacket->NdisPacket) {
- NdisQueryPacket(IPPacket->NdisPacket, NULL, NULL, NULL, &Length);
- Buffer = ExAllocatePool(NonPagedPool, Length);
- if (Buffer) {
- Length = CopyPacketToBuffer(Buffer, IPPacket->NdisPacket, 0, Length);
- DisplayTCPHeader(Buffer, Length);
- ExFreePool(Buffer);
- }
- } else {
- Buffer = IPPacket->Header;
- Length = IPPacket->ContigSize;
- DisplayTCPHeader(Buffer, Length);
- }
- }
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS TCP/IP protocol driver
+ * FILE: tcpip/routines.c
+ * PURPOSE: Common routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ * CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+static UINT RandomNumber = 0x12345678;
+
+
+UINT Random(
+ VOID)
+/*
+ * FUNCTION: Returns a pseudo random number
+ * RETURNS:
+ * Pseudo random number
+ */
+{
+ RandomNumber ^= 0x78563412;
+
+ return RandomNumber;
+}
+
+#if DBG
+static VOID DisplayIPHeader(
+ PCHAR Header,
+ UINT Length)
+{
+ /* FIXME: IPv4 only */
+ PIPv4_HEADER IPHeader = (PIPv4_HEADER)Header;
+
+ DbgPrint("IPv4 header:\n");
+ DbgPrint("VerIHL: 0x%x (version 0x%x, length %d 32-bit words)\n",
+ IPHeader->VerIHL, (IPHeader->VerIHL & 0xF0) >> 4, IPHeader->VerIHL & 0x0F);
+ DbgPrint(" Tos: %d\n", IPHeader->Tos);
+ DbgPrint(" TotalLength: %d\n", WN2H(IPHeader->TotalLength));
+ DbgPrint(" Id: %d\n", WN2H(IPHeader->Id));
+ DbgPrint(" FlagsFragOfs: 0x%x (offset 0x%x)\n", WN2H(IPHeader->FlagsFragOfs), WN2H(IPHeader->FlagsFragOfs) & IPv4_FRAGOFS_MASK);
+ if ((WN2H(IPHeader->FlagsFragOfs) & IPv4_DF_MASK) > 0) DbgPrint(" IPv4_DF - Don't fragment\n");
+ if ((WN2H(IPHeader->FlagsFragOfs) & IPv4_MF_MASK) > 0) DbgPrint(" IPv4_MF - More fragments\n");
+ DbgPrint(" Ttl: %d\n", IPHeader->Ttl);
+ DbgPrint(" Protocol: %d\n", IPHeader->Protocol);
+ DbgPrint(" Checksum: 0x%x\n", WN2H(IPHeader->Checksum));
+ DbgPrint(" SrcAddr: %d.%d.%d.%d\n",
+ ((IPHeader->SrcAddr >> 0) & 0xFF), ((IPHeader->SrcAddr >> 8) & 0xFF),
+ ((IPHeader->SrcAddr >> 16) & 0xFF), ((IPHeader->SrcAddr >> 24) & 0xFF));
+ DbgPrint(" DstAddr: %d.%d.%d.%d\n",
+ ((IPHeader->DstAddr >> 0) & 0xFF), ((IPHeader->DstAddr >> 8) & 0xFF),
+ ((IPHeader->DstAddr >> 16) & 0xFF), ((IPHeader->DstAddr >> 24) & 0xFF));
+}
+
+#endif
+
+VOID DisplayIPPacket(
+ PIP_PACKET IPPacket)
+{
+#if DBG
+ PCHAR p;
+ UINT Length;
+ PNDIS_BUFFER Buffer;
+ PNDIS_BUFFER NextBuffer;
+ PCHAR CharBuffer;
+
+ if ((DbgQueryDebugFilterState(DPFLTR_TCPIP_ID, DEBUG_PBUFFER | DPFLTR_MASK) != TRUE) ||
+ (DbgQueryDebugFilterState(DPFLTR_TCPIP_ID, DEBUG_IP | DPFLTR_MASK) != TRUE)) {
+ return;
+ }
+
+ if (!IPPacket) {
+ TI_DbgPrint(MIN_TRACE, ("Cannot display null packet.\n"));
+ return;
+ }
+
+ TI_DbgPrint(MIN_TRACE, ("IPPacket is at (0x%X).\n", IPPacket));
+ TI_DbgPrint(MIN_TRACE, ("Header buffer is at (0x%X).\n", IPPacket->Header));
+ TI_DbgPrint(MIN_TRACE, ("Header size is (%d).\n", IPPacket->HeaderSize));
+ TI_DbgPrint(MIN_TRACE, ("TotalSize (%d).\n", IPPacket->TotalSize));
+ TI_DbgPrint(MIN_TRACE, ("ContigSize (%d).\n", IPPacket->ContigSize));
+ TI_DbgPrint(MIN_TRACE, ("NdisPacket (0x%X).\n", IPPacket->NdisPacket));
+
+ if (IPPacket->NdisPacket) {
+ NdisQueryPacket(IPPacket->NdisPacket, NULL, NULL, &Buffer, NULL);
+ for (; Buffer != NULL; Buffer = NextBuffer) {
+ NdisGetNextBuffer(Buffer, &NextBuffer);
+ NdisQueryBuffer(Buffer, (PVOID)&p, &Length);
+ //OskitDumpBuffer( p, Length );
+ }
+ } else {
+ p = IPPacket->Header;
+ Length = IPPacket->ContigSize;
+ //OskitDumpBuffer( p, Length );
+ }
+
+ CharBuffer = IPPacket->Header;
+ Length = IPPacket->ContigSize;
+ DisplayIPHeader(CharBuffer, Length);
+#endif
+}
+
--- /dev/null
- * PURPOSE: Transmission Control Protocol -- Events from oskittcp
- * PROGRAMMERS: Art Yerkes
- * REVISIONS:
- * CSH 01/08-2000 Created
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS TCP/IP protocol driver
+ * FILE: transport/tcp/event.c
- int TCPSocketState(void *ClientData,
- void *WhichSocket,
- void *WhichConnection,
- OSK_UINT NewState ) {
- PCONNECTION_ENDPOINT Connection = WhichConnection;
-
- TI_DbgPrint(DEBUG_TCP,("Connection: %x Flags: %c%c%c%c%c\n",
- Connection,
- NewState & SEL_CONNECT ? 'C' : 'c',
- NewState & SEL_READ ? 'R' : 'r',
- NewState & SEL_FIN ? 'F' : 'f',
- NewState & SEL_ACCEPT ? 'A' : 'a',
- NewState & SEL_WRITE ? 'W' : 'w'));
-
- /* If this socket is missing its socket context, that means that it
- * has been created as a new connection in sonewconn but not accepted
- * yet. We can safely ignore event notifications on these sockets.
- * Once they are accepted, they will get a socket context and we will
- * be able to process them.
- */
- if (!Connection)
- return 0;
-
- TI_DbgPrint(DEBUG_TCP,("Called: NewState %x (Conn %x) (Change %x)\n",
- NewState, Connection,
- Connection->SignalState ^ NewState,
- NewState));
-
- Connection->SignalState = NewState;
-
- HandleSignalledConnection(Connection);
-
- return 0;
++ * PURPOSE: Transmission Control Protocol
++ * PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
+ */
+
+#include "precomp.h"
+
- void TCPPacketSendComplete( PVOID Context,
- PNDIS_PACKET NdisPacket,
- NDIS_STATUS NdisStatus ) {
- TI_DbgPrint(DEBUG_TCP,("called %x\n", NdisPacket));
- FreeNdisPacket(NdisPacket);
- TI_DbgPrint(DEBUG_TCP,("done\n"));
++#include "lwip/err.h"
++#include "lwip/sys.h"
++#include "lwip/pbuf.h"
++#include "lwip/tcp.h"
++#include "lwip/api.h"
++
++#include "rosip.h"
++
++static const char * const tcp_state_str[] = {
++ "CLOSED",
++ "LISTEN",
++ "SYN_SENT",
++ "SYN_RCVD",
++ "ESTABLISHED",
++ "FIN_WAIT_1",
++ "FIN_WAIT_2",
++ "CLOSE_WAIT",
++ "CLOSING",
++ "LAST_ACK",
++ "TIME_WAIT"
++};
++
++static
++VOID
++BucketCompletionWorker(PVOID Context)
++{
++ PTDI_BUCKET Bucket = (PTDI_BUCKET)Context;
++ PTCP_COMPLETION_ROUTINE Complete;
++
++ Complete = (PTCP_COMPLETION_ROUTINE)Bucket->Request.RequestNotifyObject;
++
++ Complete(Bucket->Request.RequestContext, Bucket->Status, Bucket->Information);
++
++ DereferenceObject(Bucket->AssociatedEndpoint);
++
++ ExFreePoolWithTag(Bucket, TDI_BUCKET_TAG);
+}
+
- #define STRINGIFY(x) #x
++VOID
++CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, const BOOLEAN Synchronous)
++{
++ ReferenceObject(Connection);
++ Bucket->AssociatedEndpoint = Connection;
++ if (Synchronous)
++ {
++ BucketCompletionWorker(Bucket);
++ }
++ else
++ {
++ ChewCreate(BucketCompletionWorker, Bucket);
++ }
+}
+
- int TCPPacketSend(void *ClientData, OSK_PCHAR data, OSK_UINT len ) {
- NDIS_STATUS NdisStatus;
- PNEIGHBOR_CACHE_ENTRY NCE;
- IP_PACKET Packet = { 0 };
- IP_ADDRESS RemoteAddress, LocalAddress;
- PIPv4_HEADER Header;
++VOID
++FlushReceiveQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked)
++{
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++
++ ReferenceObject(Connection);
++
++ if (interlocked)
++ {
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->ReceiveRequest, &Connection->Lock)))
++ {
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ TI_DbgPrint(DEBUG_TCP,
++ ("Completing Receive request: %x %x\n",
++ Bucket->Request, Status));
++
++ Bucket->Status = Status;
++ Bucket->Information = 0;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
++ }
++ else
++ {
++ while (!IsListEmpty(&Connection->ReceiveRequest))
++ {
++ Entry = RemoveHeadList(&Connection->ReceiveRequest);
++
++ Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
++
++ Bucket->Information = 0;
++ Bucket->Status = Status;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
++ }
++
++ DereferenceObject(Connection);
++}
+
- if( *data == 0x45 ) { /* IPv4 */
- Header = (PIPv4_HEADER)data;
- LocalAddress.Type = IP_ADDRESS_V4;
- LocalAddress.Address.IPv4Address = Header->SrcAddr;
- RemoteAddress.Type = IP_ADDRESS_V4;
- RemoteAddress.Address.IPv4Address = Header->DstAddr;
- } else {
- TI_DbgPrint(MIN_TRACE,("Outgoing packet is not IPv4\n"));
- OskitDumpBuffer( data, len );
- return OSK_EINVAL;
++VOID
++FlushSendQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked)
++{
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++
++ ReferenceObject(Connection);
+
-
- if(!(NCE = RouteGetRouteToDestination( &RemoteAddress ))) {
- TI_DbgPrint(MIN_TRACE,("Unable to get route to %s\n", A2S(&RemoteAddress)));
- return OSK_EADDRNOTAVAIL;
++ if (interlocked)
++ {
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->SendRequest, &Connection->Lock)))
++ {
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ TI_DbgPrint(DEBUG_TCP,
++ ("Completing Send request: %x %x\n",
++ Bucket->Request, Status));
++
++ Bucket->Status = Status;
++ Bucket->Information = 0;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
+ }
- NdisStatus = AllocatePacketWithBuffer( &Packet.NdisPacket, NULL, len );
++ else
++ {
++ while (!IsListEmpty(&Connection->SendRequest))
++ {
++ Entry = RemoveHeadList(&Connection->SendRequest);
++
++ Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
++
++ Bucket->Information = 0;
++ Bucket->Status = Status;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
+ }
+
- if (NdisStatus != NDIS_STATUS_SUCCESS) {
- TI_DbgPrint(DEBUG_TCP, ("Error from NDIS: %08x\n", NdisStatus));
- return OSK_ENOBUFS;
- }
++ DereferenceObject(Connection);
++}
+
- GetDataPtr( Packet.NdisPacket, 0,
- (PCHAR *)&Packet.Header, &Packet.ContigSize );
++VOID
++FlushShutdownQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked)
++{
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++
++ ReferenceObject(Connection);
+
- RtlCopyMemory( Packet.Header, data, len );
++ if (interlocked)
++ {
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->ShutdownRequest, &Connection->Lock)))
++ {
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ Bucket->Status = Status;
++ Bucket->Information = 0;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
++ }
++ else
++ {
++ while (!IsListEmpty(&Connection->ShutdownRequest))
++ {
++ Entry = RemoveHeadList(&Connection->ShutdownRequest);
++
++ Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
++
++ Bucket->Information = 0;
++ Bucket->Status = Status;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
++ }
+
- Packet.HeaderSize = sizeof(IPv4_HEADER);
- Packet.TotalSize = len;
- Packet.SrcAddr = LocalAddress;
- Packet.DstAddr = RemoteAddress;
++ DereferenceObject(Connection);
++}
+
- if (!NT_SUCCESS(IPSendDatagram( &Packet, NCE, TCPPacketSendComplete, NULL )))
++VOID
++FlushConnectQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status)
++{
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++
++ ReferenceObject(Connection);
+
- FreeNdisPacket(Packet.NdisPacket);
- return OSK_EINVAL;
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
+ {
- return 0;
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ Bucket->Status = Status;
++ Bucket->Information = 0;
++
++ CompleteBucket(Connection, Bucket, FALSE);
+ }
+
- /* Memory management routines
- *
- * By far the most requests for memory are either for 128 or 2049 byte blocks,
- * so we want to satisfy those from lookaside lists. Unfortunately, the
- * TCPFree() function doesn't pass the size of the block to be freed, so we
- * need to keep track of it ourselves. We do it by prepending each block with
- * 4 bytes, indicating if this is a 'L'arge (2049), 'S'mall (128) or 'O'ther
- * block.
- */
++ DereferenceObject(Connection);
+}
+
- /* Set to some non-zero value to get a profile of memory allocation sizes */
- #define MEM_PROFILE 0
++VOID
++FlushListenQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status)
++{
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++
++ ReferenceObject(Connection);
+
- #define SMALL_SIZE 128
- #define LARGE_SIZE 2049
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
++ {
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ Bucket->Status = Status;
++ Bucket->Information = 0;
++
++ DereferenceObject(Bucket->AssociatedEndpoint);
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
+
- #define SIGNATURE_LARGE 'LLLL'
- #define SIGNATURE_SMALL 'SSSS'
- #define SIGNATURE_OTHER 'OOOO'
- static NPAGED_LOOKASIDE_LIST LargeLookasideList;
- static NPAGED_LOOKASIDE_LIST SmallLookasideList;
++ DereferenceObject(Connection);
++}
+
- NTSTATUS
- TCPMemStartup( void )
- {
- ExInitializeNPagedLookasideList( &LargeLookasideList,
- NULL,
- NULL,
- 0,
- LARGE_SIZE + sizeof( ULONG ),
- OSK_LARGE_TAG,
- 0 );
- ExInitializeNPagedLookasideList( &SmallLookasideList,
- NULL,
- NULL,
- 0,
- SMALL_SIZE + sizeof( ULONG ),
- OSK_SMALL_TAG,
- 0 );
-
- return STATUS_SUCCESS;
++VOID
++FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status)
++{
++ ReferenceObject(Connection);
++
++ // flush receive queue
++ FlushReceiveQueue(Connection, Status, TRUE);
+
- void *TCPMalloc( void *ClientData,
- OSK_UINT Bytes, OSK_PCHAR File, OSK_UINT Line ) {
- void *v;
- ULONG Signature;
-
- #if 0 != MEM_PROFILE
- static OSK_UINT *Sizes = NULL, *Counts = NULL, ArrayAllocated = 0;
- static OSK_UINT ArrayUsed = 0, AllocationCount = 0;
- OSK_UINT i, NewSize, *NewArray;
- int Found;
-
- Found = 0;
- for ( i = 0; i < ArrayUsed && ! Found; i++ ) {
- Found = ( Sizes[i] == Bytes );
- if ( Found ) {
- Counts[i]++;
- }
- }
- if ( ! Found ) {
- if ( ArrayAllocated <= ArrayUsed ) {
- NewSize = ( 0 == ArrayAllocated ? 16 : 2 * ArrayAllocated );
- NewArray = exAllocatePool( NonPagedPool, 2 * NewSize * sizeof( OSK_UINT ) );
- if ( NULL != NewArray ) {
- if ( 0 != ArrayAllocated ) {
- memcpy( NewArray, Sizes,
- ArrayAllocated * sizeof( OSK_UINT ) );
- exFreePool( Sizes );
- memcpy( NewArray + NewSize, Counts,
- ArrayAllocated * sizeof( OSK_UINT ) );
- exFreePool( Counts );
++ /* We completed the reads successfully but we need to return failure now */
++ if (Status == STATUS_SUCCESS)
++ {
++ Status = STATUS_FILE_CLOSED;
++ }
++
++ // flush listen queue
++ FlushListenQueue(Connection, Status);
++
++ // flush send queue
++ FlushSendQueue(Connection, Status, TRUE);
++
++ // flush connect queue
++ FlushConnectQueue(Connection, Status);
++
++ // flush shutdown queue
++ FlushShutdownQueue(Connection, Status, TRUE);
++
++ DereferenceObject(Connection);
+}
+
- Sizes = NewArray;
- Counts = NewArray + NewSize;
- ArrayAllocated = NewSize;
- } else if ( 0 != ArrayAllocated ) {
- exFreePool( Sizes );
- exFreePool( Counts );
- ArrayAllocated = 0;
++VOID
++TCPFinEventHandler(void *arg, const err_t err)
++{
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg, LastConnection;
++ const NTSTATUS Status = TCPTranslateError(err);
++ KIRQL OldIrql;
++
++ ASSERT(Connection->AddressFile);
++
++ /* Check if this was a partial socket closure */
++ if (err == ERR_OK && Connection->SocketContext)
++ {
++ /* Just flush the receive queue and get out of here */
++ FlushReceiveQueue(Connection, STATUS_SUCCESS, TRUE);
++ }
++ else
++ {
++ /* First off all, remove the PCB pointer */
++ Connection->SocketContext = NULL;
++
++ /* Complete all outstanding requests now */
++ FlushAllQueues(Connection, Status);
++
++ LockObject(Connection, &OldIrql);
++
++ LockObjectAtDpcLevel(Connection->AddressFile);
++
++ /* Unlink this connection from the address file */
++ if (Connection->AddressFile->Connection == Connection)
++ {
++ Connection->AddressFile->Connection = Connection->Next;
++ DereferenceObject(Connection);
+ }
- if ( ArrayUsed < ArrayAllocated ) {
- Sizes[ArrayUsed] = Bytes;
- Counts[ArrayUsed] = 1;
- ArrayUsed++;
- }
++ else if (Connection->AddressFile->Listener == Connection)
++ {
++ Connection->AddressFile->Listener = NULL;
++ DereferenceObject(Connection);
+ }
++ else
++ {
++ LastConnection = Connection->AddressFile->Connection;
++ while (LastConnection->Next != Connection && LastConnection->Next != NULL)
++ LastConnection = LastConnection->Next;
++ if (LastConnection->Next == Connection)
++ {
++ LastConnection->Next = Connection->Next;
++ DereferenceObject(Connection);
++ }
++ }
++
++ UnlockObjectFromDpcLevel(Connection->AddressFile);
++
++ /* Remove the address file from this connection */
++ DereferenceObject(Connection->AddressFile);
++ Connection->AddressFile = NULL;
++
++ UnlockObject(Connection, OldIrql);
+ }
- if ( 0 == (++AllocationCount % MEM_PROFILE) ) {
- TI_DbgPrint(DEBUG_TCP, ("Memory allocation size profile:\n"));
- for ( i = 0; i < ArrayUsed; i++ ) {
++}
++
++VOID
++TCPAcceptEventHandler(void *arg, PTCP_PCB newpcb)
++{
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++ PIRP Irp;
++ NTSTATUS Status;
++ KIRQL OldIrql;
++
++ ReferenceObject(Connection);
++
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
++ {
++ PIO_STACK_LOCATION IrpSp;
++
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ Irp = Bucket->Request.RequestContext;
++ IrpSp = IoGetCurrentIrpStackLocation( Irp );
++
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPAcceptEventHandler] Getting the socket\n"));
++
++ Status = TCPCheckPeerForAccept(newpcb,
++ (PTDI_REQUEST_KERNEL)&IrpSp->Parameters);
++
++ TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n", Status));
++
++ Bucket->Status = Status;
++ Bucket->Information = 0;
++
++ if (Status == STATUS_SUCCESS)
++ {
++ LockObject(Bucket->AssociatedEndpoint, &OldIrql);
++
++ /* sanity assert...this should never be in anything else but a CLOSED state */
++ ASSERT( ((PTCP_PCB)Bucket->AssociatedEndpoint->SocketContext)->state == CLOSED );
++
++ /* free socket context created in FileOpenConnection, as we're using a new one */
++ LibTCPClose(Bucket->AssociatedEndpoint, TRUE, FALSE);
++
++ /* free previously created socket context (we don't use it, we use newpcb) */
++ Bucket->AssociatedEndpoint->SocketContext = newpcb;
++
++ LibTCPAccept(newpcb, (PTCP_PCB)Connection->SocketContext, Bucket->AssociatedEndpoint);
++
++ UnlockObject(Bucket->AssociatedEndpoint, OldIrql);
++ }
++
++ DereferenceObject(Bucket->AssociatedEndpoint);
++
++ CompleteBucket(Connection, Bucket, FALSE);
+ }
++
++ DereferenceObject(Connection);
++}
+
- ("Size %4u Count %5u\n", Sizes[i], Counts[i]));
- }
- TI_DbgPrint(DEBUG_TCP, ("End of memory allocation size profile\n"));
++VOID
++TCPSendEventHandler(void *arg, u16_t space)
++{
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++ PIRP Irp;
++ NTSTATUS Status;
++ PMDL Mdl;
++
++ ReferenceObject(Connection);
++
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->SendRequest, &Connection->Lock)))
++ {
++ UINT SendLen = 0;
++ PVOID SendBuffer = 0;
++
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ Irp = Bucket->Request.RequestContext;
++ Mdl = Irp->MdlAddress;
++
+ TI_DbgPrint(DEBUG_TCP,
- #endif /* MEM_PROFILE */
++ ("Getting the user buffer from %x\n", Mdl));
++
++ NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
++
++ TI_DbgPrint(DEBUG_TCP,
++ ("Writing %d bytes to %x\n", SendLen, SendBuffer));
++
++ TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
++ TI_DbgPrint
++ (DEBUG_TCP,
++ ("Connection->SocketContext: %x\n",
++ Connection->SocketContext));
++
++ Status = TCPTranslateError(LibTCPSend(Connection,
++ SendBuffer,
++ SendLen, TRUE));
++
++ TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", SendLen));
++
++ if( Status == STATUS_PENDING )
++ {
++ ExInterlockedInsertHeadList(&Connection->SendRequest,
++ &Bucket->Entry,
++ &Connection->Lock);
++ break;
++ }
++ else
++ {
++ TI_DbgPrint(DEBUG_TCP,
++ ("Completing Send request: %x %x\n",
++ Bucket->Request, Status));
++
++ Bucket->Status = Status;
++ Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? SendLen : 0;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
+ }
- if ( SMALL_SIZE == Bytes ) {
- v = ExAllocateFromNPagedLookasideList( &SmallLookasideList );
- Signature = SIGNATURE_SMALL;
- } else if ( LARGE_SIZE == Bytes ) {
- v = ExAllocateFromNPagedLookasideList( &LargeLookasideList );
- Signature = SIGNATURE_LARGE;
- } else {
- v = ExAllocatePoolWithTag( NonPagedPool, Bytes + sizeof(ULONG),
- OSK_OTHER_TAG );
- Signature = SIGNATURE_OTHER;
- }
- if( v ) {
- *((ULONG *) v) = Signature;
- v = (void *)((char *) v + sizeof(ULONG));
- }
+
- return v;
++ // If we completed all outstanding send requests then finish all pending shutdown requests,
++ // cancel the timer and dereference the connection
++ if (IsListEmpty(&Connection->SendRequest))
++ {
++ FlushShutdownQueue(Connection, STATUS_SUCCESS, FALSE);
+
- void TCPFree( void *ClientData,
- void *data, OSK_PCHAR File, OSK_UINT Line ) {
- ULONG Signature;
-
- data = (void *)((char *) data - sizeof(ULONG));
- Signature = *((ULONG *) data);
- if ( SIGNATURE_SMALL == Signature ) {
- ExFreeToNPagedLookasideList( &SmallLookasideList, data );
- } else if ( SIGNATURE_LARGE == Signature ) {
- ExFreeToNPagedLookasideList( &LargeLookasideList, data );
- } else if ( SIGNATURE_OTHER == Signature ) {
- ExFreePoolWithTag( data, OSK_OTHER_TAG );
- } else {
- ASSERT( FALSE );
++ if (KeCancelTimer(&Connection->DisconnectTimer))
++ {
++ DereferenceObject(Connection);
++ }
++ }
++
++ DereferenceObject(Connection);
+}
+
- void
- TCPMemShutdown( void )
++u32_t
++TCPRecvEventHandler(void *arg, struct pbuf *p)
++{
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++ PIRP Irp;
++ PMDL Mdl;
++ UINT Received = 0;
++ UINT RecvLen;
++ PUCHAR RecvBuffer;
++
++ ASSERT(p);
++
++ ReferenceObject(Connection);
++
++ if ((Entry = ExInterlockedRemoveHeadList(&Connection->ReceiveRequest, &Connection->Lock)))
++ {
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ Irp = Bucket->Request.RequestContext;
++ Mdl = Irp->MdlAddress;
++
++ TI_DbgPrint(DEBUG_TCP,
++ ("[IP, TCPRecvEventHandler] Getting the user buffer from %x\n", Mdl));
++
++ NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
++
++ TI_DbgPrint(DEBUG_TCP,
++ ("[IP, TCPRecvEventHandler] Reading %d bytes to %x\n", RecvLen, RecvBuffer));
++
++ TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
++ TI_DbgPrint(DEBUG_TCP, ("[IP, TCPRecvEventHandler] Connection->SocketContext: %x\n", Connection->SocketContext));
++ TI_DbgPrint(DEBUG_TCP, ("[IP, TCPRecvEventHandler] RecvBuffer: %x\n", RecvBuffer));
++
++ RecvLen = MIN(p->tot_len, RecvLen);
++
++ for (Received = 0; Received < RecvLen; Received += p->len, p = p->next)
++ {
++ RtlCopyMemory(RecvBuffer + Received, p->payload, p->len);
++ }
++
++ TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Received));
++
++ Bucket->Status = STATUS_SUCCESS;
++ Bucket->Information = Received;
++
++ CompleteBucket(Connection, Bucket, FALSE);
+ }
++
++ DereferenceObject(Connection);
++
++ return Received;
+}
+
- ExDeleteNPagedLookasideList( &SmallLookasideList );
- ExDeleteNPagedLookasideList( &LargeLookasideList );
++VOID
++TCPConnectEventHandler(void *arg, err_t err)
+{
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
++ PTDI_BUCKET Bucket;
++ PLIST_ENTRY Entry;
++
++ ReferenceObject(Connection);
++
++ while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
++ {
++
++ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
++
++ Bucket->Status = TCPTranslateError(err);
++ Bucket->Information = 0;
++
++ CompleteBucket(Connection, Bucket, FALSE);
++ }
++
++ DereferenceObject(Connection);
+}
--- /dev/null
- static NPAGED_LOOKASIDE_LIST TCPSegmentList;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS TCP/IP protocol driver
+ * FILE: transport/tcp/tcp.c
+ * PURPOSE: Transmission Control Protocol
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * Art Yerkes (arty@users.sf.net)
+ * REVISIONS:
+ * CSH 01/08-2000 Created
+ * arty 12/21/2004 Added accept
+ */
+
+#include "precomp.h"
+
+LONG TCP_IPIdentification = 0;
+static BOOLEAN TCPInitialized = FALSE;
- CLIENT_DATA ClientInfo;
+PORT_SET TCPPorts;
- VOID
- CompleteBucketWorker(PVOID Context)
- {
- PTDI_BUCKET Bucket = Context;
- PCONNECTION_ENDPOINT Connection;
- PTCP_COMPLETION_ROUTINE Complete;
-
- ASSERT(Bucket);
-
- Connection = Bucket->AssociatedEndpoint;
- Complete = Bucket->Request.RequestNotifyObject;
-
- Complete(Bucket->Request.RequestContext, Bucket->Status, Bucket->Information);
-
- ExFreePoolWithTag(Bucket, TDI_BUCKET_TAG);
-
- DereferenceObject(Connection);
- }
-
- VOID
- CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket)
- {
- Bucket->AssociatedEndpoint = Connection;
- ReferenceObject(Connection);
- ChewCreate(CompleteBucketWorker, Bucket);
- }
-
- VOID HandleSignalledConnection(PCONNECTION_ENDPOINT Connection)
- {
- PTDI_BUCKET Bucket;
- PLIST_ENTRY Entry;
- NTSTATUS Status;
- PIRP Irp;
- PMDL Mdl;
-
- if (ClientInfo.Unlocked)
- LockObjectAtDpcLevel(Connection);
-
- TI_DbgPrint(MID_TRACE,("Handling signalled state on %x\n",
- Connection));
-
- /* Things that can happen when we try the initial connection */
- if( Connection->SignalState & (SEL_CONNECT | SEL_FIN | SEL_ERROR) ) {
- while (!IsListEmpty(&Connection->ConnectRequest)) {
- Entry = RemoveHeadList( &Connection->ConnectRequest );
-
- Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
-
- if (Connection->SignalState & SEL_ERROR)
- {
- Bucket->Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
- }
- else if (Connection->SignalState & SEL_FIN)
- {
- Bucket->Status = STATUS_CANCELLED;
- }
- else
- {
- Bucket->Status = STATUS_SUCCESS;
- }
- Bucket->Information = 0;
-
- CompleteBucket(Connection, Bucket);
- }
- }
-
- if( Connection->SignalState & (SEL_ACCEPT | SEL_FIN | SEL_ERROR) ) {
- /* Handle readable on a listening socket --
- * TODO: Implement filtering
- */
- TI_DbgPrint(DEBUG_TCP,("Accepting new connection on %x (Queue: %s)\n",
- Connection,
- IsListEmpty(&Connection->ListenRequest) ?
- "empty" : "nonempty"));
-
- while (!IsListEmpty(&Connection->ListenRequest)) {
- PIO_STACK_LOCATION IrpSp;
-
- Entry = RemoveHeadList( &Connection->ListenRequest );
-
- Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
-
- Irp = Bucket->Request.RequestContext;
- IrpSp = IoGetCurrentIrpStackLocation( Irp );
-
- TI_DbgPrint(DEBUG_TCP,("Getting the socket\n"));
-
- if (Connection->SignalState & SEL_ERROR)
- {
- Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
- }
- else if (Connection->SignalState & SEL_FIN)
- {
- Status = STATUS_CANCELLED;
- }
- else
- {
- Status = TCPServiceListeningSocket(Connection->AddressFile->Listener,
- Bucket->AssociatedEndpoint,
- (PTDI_REQUEST_KERNEL)&IrpSp->Parameters);
- }
-
- TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n"));
-
- if( Status == STATUS_PENDING ) {
- InsertHeadList( &Connection->ListenRequest, &Bucket->Entry );
- break;
- } else {
- Bucket->Status = Status;
- Bucket->Information = 0;
- DereferenceObject(Bucket->AssociatedEndpoint);
-
- CompleteBucket(Connection, Bucket);
- }
- }
- }
-
- /* Things that happen after we're connected */
- if( Connection->SignalState & (SEL_READ | SEL_FIN | SEL_ERROR) ) {
- TI_DbgPrint(DEBUG_TCP,("Readable: irp list %s\n",
- IsListEmpty(&Connection->ReceiveRequest) ?
- "empty" : "nonempty"));
-
- while (!IsListEmpty(&Connection->ReceiveRequest)) {
- OSK_UINT RecvLen = 0, Received = 0;
- PVOID RecvBuffer = 0;
-
- Entry = RemoveHeadList( &Connection->ReceiveRequest );
-
- Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
-
- Irp = Bucket->Request.RequestContext;
- Mdl = Irp->MdlAddress;
-
- TI_DbgPrint(DEBUG_TCP,
- ("Getting the user buffer from %x\n", Mdl));
-
- NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
-
- TI_DbgPrint(DEBUG_TCP,
- ("Reading %d bytes to %x\n", RecvLen, RecvBuffer));
-
- TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
- TI_DbgPrint(DEBUG_TCP, ("RecvBuffer: %x\n", RecvBuffer));
-
- if (Connection->SignalState & SEL_ERROR)
- {
- Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
- }
- else if (Connection->SignalState & SEL_FIN)
- {
- /* We got here because of a SEL_FIN event */
- Status = STATUS_CANCELLED;
- }
- else
- {
- Status = TCPTranslateError(OskitTCPRecv(Connection,
- RecvBuffer,
- RecvLen,
- &Received,
- 0));
- }
-
- TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Received));
-
- if( Status == STATUS_PENDING ) {
- InsertHeadList( &Connection->ReceiveRequest, &Bucket->Entry );
- break;
- } else {
- TI_DbgPrint(DEBUG_TCP,
- ("Completing Receive request: %x %x\n",
- Bucket->Request, Status));
-
- Bucket->Status = Status;
- Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? Received : 0;
-
- CompleteBucket(Connection, Bucket);
- }
- }
- }
- if( Connection->SignalState & (SEL_WRITE | SEL_FIN | SEL_ERROR) ) {
- TI_DbgPrint(DEBUG_TCP,("Writeable: irp list %s\n",
- IsListEmpty(&Connection->SendRequest) ?
- "empty" : "nonempty"));
-
- while (!IsListEmpty(&Connection->SendRequest)) {
- OSK_UINT SendLen = 0, Sent = 0;
- PVOID SendBuffer = 0;
-
- Entry = RemoveHeadList( &Connection->SendRequest );
-
- Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
-
- Irp = Bucket->Request.RequestContext;
- Mdl = Irp->MdlAddress;
-
- TI_DbgPrint(DEBUG_TCP,
- ("Getting the user buffer from %x\n", Mdl));
-
- NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
-
- TI_DbgPrint(DEBUG_TCP,
- ("Writing %d bytes to %x\n", SendLen, SendBuffer));
-
- TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
-
- if (Connection->SignalState & SEL_ERROR)
- {
- Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
- }
- else if (Connection->SignalState & SEL_FIN)
- {
- /* We got here because of a SEL_FIN event */
- Status = STATUS_CANCELLED;
- }
- else
- {
- Status = TCPTranslateError(OskitTCPSend(Connection,
- SendBuffer,
- SendLen,
- &Sent,
- 0));
- }
-
- TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Sent));
-
- if( Status == STATUS_PENDING ) {
- InsertHeadList( &Connection->SendRequest, &Bucket->Entry );
- break;
- } else {
- TI_DbgPrint(DEBUG_TCP,
- ("Completing Send request: %x %x\n",
- Bucket->Request, Status));
-
- Bucket->Status = Status;
- Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? Sent : 0;
-
- CompleteBucket(Connection, Bucket);
- }
- }
- }
- if( Connection->SignalState & (SEL_WRITE | SEL_FIN | SEL_ERROR) ) {
- while (!IsListEmpty(&Connection->ShutdownRequest)) {
- Entry = RemoveHeadList( &Connection->ShutdownRequest );
-
- Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
-
- if (Connection->SignalState & SEL_ERROR)
- {
- Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
- }
- else if (Connection->SignalState & SEL_FIN)
- {
- /* We were cancelled by a FIN */
- Status = STATUS_CANCELLED;
- }
- else
- {
- /* See if we can satisfy this after the events */
- if (IsListEmpty(&Connection->SendRequest))
- {
- /* Send queue is empty so we're good to go */
- Status = TCPTranslateError(OskitTCPShutdown(Connection, FWRITE));
- }
- else
- {
- /* We still have to wait */
- Status = STATUS_PENDING;
- }
- }
-
- if( Status == STATUS_PENDING ) {
- InsertHeadList( &Connection->ShutdownRequest, &Bucket->Entry );
- break;
- } else {
- TI_DbgPrint(DEBUG_TCP,
- ("Completing shutdown request: %x %x\n",
- Bucket->Request, Status));
-
- if (KeCancelTimer(&Connection->DisconnectTimer))
- {
- DereferenceObject(Connection);
- }
+
- Bucket->Status = Status;
- Bucket->Information = 0;
-
- CompleteBucket(Connection, Bucket);
- }
- }
- }
-
- if (Connection->SignalState & SEL_FIN)
- {
- Connection->SocketContext = NULL;
- DereferenceObject(Connection);
- }
-
- if (ClientInfo.Unlocked)
- UnlockObjectFromDpcLevel(Connection);
- }
++#include "lwip/pbuf.h"
++#include "lwip/ip.h"
++#include "lwip/init.h"
++#include "lwip/arch.h"
+
- PCONNECTION_ENDPOINT Connection = DeferredContext;
++#include "rosip.h"
+
+VOID NTAPI
+DisconnectTimeoutDpc(PKDPC Dpc,
+ PVOID DeferredContext,
+ PVOID SystemArgument1,
+ PVOID SystemArgument2)
+{
- OskitTCPShutdown(Connection, FWRITE);
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)DeferredContext;
+ PLIST_ENTRY Entry;
+ PTDI_BUCKET Bucket;
++ NTSTATUS Status;
+
+ LockObjectAtDpcLevel(Connection);
+
+ /* We timed out waiting for pending sends so force it to shutdown */
- CompleteBucket(Connection, Bucket);
++ Status = TCPTranslateError(LibTCPShutdown(Connection, 0, 1));
+
+ while (!IsListEmpty(&Connection->SendRequest))
+ {
+ Entry = RemoveHeadList(&Connection->SendRequest);
+
+ Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
+
+ Bucket->Information = 0;
+ Bucket->Status = STATUS_FILE_CLOSED;
+
- while (!IsListEmpty(&Connection->ShutdownRequest)) {
++ CompleteBucket(Connection, Bucket, FALSE);
+ }
+
- CompleteBucket(Connection, Bucket);
++ while (!IsListEmpty(&Connection->ShutdownRequest))
++ {
+ Entry = RemoveHeadList( &Connection->ShutdownRequest );
+
+ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
+
+ Bucket->Status = STATUS_TIMEOUT;
+ Bucket->Information = 0;
+
- VOID ConnectionFree(PVOID Object) {
- PCONNECTION_ENDPOINT Connection = Object;
++ CompleteBucket(Connection, Bucket, FALSE);
+ }
+
+ UnlockObjectFromDpcLevel(Connection);
+
+ DereferenceObject(Connection);
+}
+
- PCONNECTION_ENDPOINT TCPAllocateConnectionEndpoint( PVOID ClientContext ) {
- PCONNECTION_ENDPOINT Connection =
++VOID ConnectionFree(PVOID Object)
++{
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)Object;
+ KIRQL OldIrql;
+
+ TI_DbgPrint(DEBUG_TCP, ("Freeing TCP Endpoint\n"));
+
+ TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
+ RemoveEntryList(&Connection->ListEntry);
+ TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
+
+ ExFreePoolWithTag( Connection, CONN_ENDPT_TAG );
+}
+
-
++PCONNECTION_ENDPOINT TCPAllocateConnectionEndpoint( PVOID ClientContext )
++{
++ PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)
+ ExAllocatePoolWithTag(NonPagedPool, sizeof(CONNECTION_ENDPOINT),
+ CONN_ENDPT_TAG);
+ if (!Connection)
+ return Connection;
+
+ TI_DbgPrint(DEBUG_CPOINT, ("Connection point file object allocated at (0x%X).\n", Connection));
+
+ RtlZeroMemory(Connection, sizeof(CONNECTION_ENDPOINT));
+
+ /* Initialize spin lock that protects the connection endpoint file object */
+ KeInitializeSpinLock(&Connection->Lock);
+ InitializeListHead(&Connection->ConnectRequest);
+ InitializeListHead(&Connection->ListenRequest);
+ InitializeListHead(&Connection->ReceiveRequest);
+ InitializeListHead(&Connection->SendRequest);
+ InitializeListHead(&Connection->ShutdownRequest);
- Connection->RefCount = 2;
++ InitializeListHead(&Connection->PacketQueue);
++
++ /* Initialize disconnect timer */
+ KeInitializeTimer(&Connection->DisconnectTimer);
+ KeInitializeDpc(&Connection->DisconnectDpc, DisconnectTimeoutDpc, Connection);
+
+ /* Save client context pointer */
+ Connection->ClientContext = ClientContext;
+
- UINT Family, UINT Type, UINT Proto ) {
++ Connection->RefCount = 1;
+ Connection->Free = ConnectionFree;
+
+ /* Add connection endpoint to global list */
+ ExInterlockedInsertTailList(&ConnectionEndpointListHead,
+ &Connection->ListEntry,
+ &ConnectionEndpointListLock);
+
+ return Connection;
+}
+
+NTSTATUS TCPSocket( PCONNECTION_ENDPOINT Connection,
- TI_DbgPrint(DEBUG_TCP,("Called: Connection %x, Family %d, Type %d, "
- "Proto %d\n",
- Connection, Family, Type, Proto));
-
- Status = TCPTranslateError( OskitTCPSocket( Connection,
- &Connection->SocketContext,
- Family,
- Type,
- Proto ) );
++ UINT Family, UINT Type, UINT Proto )
++{
+ NTSTATUS Status;
+ KIRQL OldIrql;
+
+ LockObject(Connection, &OldIrql);
+
- ASSERT_KM_POINTER(Connection->SocketContext);
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSocket] Called: Connection %x, Family %d, Type %d, "
++ "Proto %d, sizeof(CONNECTION_ENDPOINT) = %d\n",
++ Connection, Family, Type, Proto, sizeof(CONNECTION_ENDPOINT)));
+
- VOID TCPReceive(PIP_INTERFACE Interface, PIP_PACKET IPPacket)
- /*
- * FUNCTION: Receives and queues TCP data
- * ARGUMENTS:
- * IPPacket = Pointer to an IP packet that was received
- * NOTES:
- * This is the low level interface for receiving TCP data
- */
++ Connection->SocketContext = LibTCPSocket(Connection);
++ if (Connection->SocketContext)
++ Status = STATUS_SUCCESS;
++ else
++ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ UnlockObject(Connection, OldIrql);
+
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSocket] Leaving. Status = 0x%x\n", Status));
++
+ return Status;
+}
+
- TI_DbgPrint(DEBUG_TCP,("Sending packet %d (%d) to oskit\n",
- IPPacket->TotalSize,
- IPPacket->HeaderSize));
++NTSTATUS TCPClose( PCONNECTION_ENDPOINT Connection )
+{
+ KIRQL OldIrql;
++ PVOID Socket;
+
- KeAcquireSpinLock(&ClientInfo.Lock, &OldIrql);
- ClientInfo.Unlocked = TRUE;
- ClientInfo.OldIrql = OldIrql;
++ LockObject(Connection, &OldIrql);
+
- OskitTCPReceiveDatagram( IPPacket->Header,
- IPPacket->TotalSize,
- IPPacket->HeaderSize );
++ Socket = Connection->SocketContext;
+
- ClientInfo.Unlocked = FALSE;
- KeReleaseSpinLock(&ClientInfo.Lock, OldIrql);
- }
++ FlushAllQueues(Connection, STATUS_CANCELLED);
+
- /* event.c */
- int TCPSocketState( void *ClientData,
- void *WhichSocket,
- void *WhichConnection,
- OSK_UINT NewState );
-
- int TCPPacketSend( void *ClientData,
- OSK_PCHAR Data,
- OSK_UINT Len );
-
- POSK_IFADDR TCPFindInterface( void *ClientData,
- OSK_UINT AddrType,
- OSK_UINT FindType,
- OSK_SOCKADDR *ReqAddr );
-
- NTSTATUS TCPMemStartup( void );
- void *TCPMalloc( void *ClientData,
- OSK_UINT bytes, OSK_PCHAR file, OSK_UINT line );
- void TCPFree( void *ClientData,
- void *data, OSK_PCHAR file, OSK_UINT line );
- void TCPMemShutdown( void );
-
- OSKITTCP_EVENT_HANDLERS EventHandlers = {
- NULL, /* Client Data */
- TCPSocketState, /* SocketState */
- TCPPacketSend, /* PacketSend */
- TCPFindInterface, /* FindInterface */
- TCPMalloc, /* Malloc */
- TCPFree, /* Free */
- NULL, /* Sleep */
- NULL, /* Wakeup */
- };
-
- static KEVENT TimerLoopEvent;
- static HANDLE TimerThreadHandle;
-
- /*
- * We are running 2 timers here, one with a 200ms interval (fast) and the other
- * with a 500ms interval (slow). So we need to time out at 200, 400, 500, 600,
- * 800, 1000 and process the "fast" events at 200, 400, 600, 800, 1000 and the
- * "slow" events at 500 and 1000.
- */
- static VOID NTAPI
- TimerThread(PVOID Context)
- {
- LARGE_INTEGER Timeout;
- NTSTATUS Status;
- unsigned Current, NextFast, NextSlow, Next;
-
- Current = 0;
- Next = 0;
- NextFast = 0;
- NextSlow = 0;
- while ( 1 ) {
- if (Next == NextFast) {
- NextFast += 2;
- }
- if (Next == NextSlow) {
- NextSlow += 5;
- }
- Next = min(NextFast, NextSlow);
- Timeout.QuadPart = (LONGLONG) (Next - Current) * -1000000; /* 100 ms */
- Status = KeWaitForSingleObject(&TimerLoopEvent, Executive, KernelMode,
- FALSE, &Timeout);
- if (Status != STATUS_TIMEOUT) {
- PsTerminateSystemThread(Status);
- }
++ LibTCPClose(Connection, FALSE, TRUE);
+
- TimerOskitTCP( Next == NextFast, Next == NextSlow );
++ UnlockObject(Connection, OldIrql);
+
- Current = Next;
- if (10 <= Current) {
- Current = 0;
- Next = 0;
- NextFast = 0;
- NextSlow = 0;
- }
- }
++ DereferenceObject(Connection);
+
- static VOID
- StartTimer(VOID)
++ return STATUS_SUCCESS;
+}
+
- KeInitializeEvent(&TimerLoopEvent, NotificationEvent, FALSE);
- PsCreateSystemThread(&TimerThreadHandle, THREAD_ALL_ACCESS, 0, 0, 0,
- TimerThread, NULL);
++VOID TCPReceive(PIP_INTERFACE Interface, PIP_PACKET IPPacket)
++/*
++ * FUNCTION: Receives and queues TCP data
++ * ARGUMENTS:
++ * IPPacket = Pointer to an IP packet that was received
++ * NOTES:
++ * This is the low level interface for receiving TCP data
++ */
+{
- Status = TCPMemStartup();
- if ( ! NT_SUCCESS(Status) ) {
- return Status;
- }
-
++ TI_DbgPrint(DEBUG_TCP,("Sending packet %d (%d) to lwIP\n",
++ IPPacket->TotalSize,
++ IPPacket->HeaderSize));
++
++ LibIPInsertPacket(Interface->TCPContext, IPPacket->Header, IPPacket->TotalSize);
+}
+
+NTSTATUS TCPStartup(VOID)
+/*
+ * FUNCTION: Initializes the TCP subsystem
+ * RETURNS:
+ * Status of operation
+ */
+{
+ NTSTATUS Status;
+
- if( !NT_SUCCESS(Status) ) {
- TCPMemShutdown();
+ Status = PortsStartup( &TCPPorts, 1, 0xfffe );
-
- KeInitializeSpinLock(&ClientInfo.Lock);
- ClientInfo.Unlocked = FALSE;
-
- RegisterOskitTCPEventHandlers( &EventHandlers );
- InitOskitTCP();
-
++ if (!NT_SUCCESS(Status))
++ {
+ return Status;
+ }
-
- ExInitializeNPagedLookasideList(
- &TCPSegmentList, /* Lookaside list */
- NULL, /* Allocate routine */
- NULL, /* Free routine */
- 0, /* Flags */
- sizeof(TCP_SEGMENT), /* Size of each entry */
- 'SPCT', /* Tag */
- 0); /* Depth */
-
- StartTimer();
-
++
++ /* Initialize our IP library */
++ LibIPInitialize();
++
+ /* Register this protocol with IP layer */
+ IPRegisterProtocol(IPPROTO_TCP, TCPReceive);
- LARGE_INTEGER WaitForThread;
-
++
+ TCPInitialized = TRUE;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS TCPShutdown(VOID)
+/*
+ * FUNCTION: Shuts down the TCP subsystem
+ * RETURNS:
+ * Status of operation
+ */
+{
-
- WaitForThread.QuadPart = -2500000; /* 250 ms */
- KeSetEvent(&TimerLoopEvent, IO_NO_INCREMENT, FALSE);
- ZwWaitForSingleObject(TimerThreadHandle, FALSE, &WaitForThread);
+ if (!TCPInitialized)
+ return STATUS_SUCCESS;
- ExDeleteNPagedLookasideList(&TCPSegmentList);
-
++
++ LibIPShutdown();
+
+ /* Deregister this protocol with IP layer */
+ IPRegisterProtocol(IPPROTO_TCP, NULL);
+
- DeinitOskitTCP();
-
+ TCPInitialized = FALSE;
+
- TCPMemShutdown();
-
+ PortsShutdown( &TCPPorts );
+
- NTSTATUS TCPTranslateError( int OskitError ) {
+ return STATUS_SUCCESS;
+}
+
- switch( OskitError ) {
- case 0: Status = STATUS_SUCCESS; break;
- case OSK_EADDRNOTAVAIL:
- Status = STATUS_INVALID_ADDRESS;
- DbgPrint("OskitTCP: EADDRNOTAVAIL\n");
- break;
- case OSK_EADDRINUSE:
- Status = STATUS_ADDRESS_ALREADY_EXISTS;
- DbgPrint("OskitTCP: EADDRINUSE\n");
- break;
- case OSK_EAFNOSUPPORT:
- Status = STATUS_INVALID_CONNECTION;
- DbgPrint("OskitTCP: EAFNOSUPPORT\n");
- break;
- case OSK_ECONNREFUSED:
- Status = STATUS_REMOTE_NOT_LISTENING;
- DbgPrint("OskitTCP: ECONNREFUSED\n");
- break;
- case OSK_ECONNRESET:
- Status = STATUS_REMOTE_DISCONNECT;
- DbgPrint("OskitTCP: ECONNRESET\n");
- break;
- case OSK_ECONNABORTED:
- Status = STATUS_LOCAL_DISCONNECT;
- DbgPrint("OskitTCP: ECONNABORTED\n");
- break;
- case OSK_EWOULDBLOCK:
- case OSK_EINPROGRESS: Status = STATUS_PENDING; break;
- case OSK_EINVAL:
- Status = STATUS_INVALID_PARAMETER;
- DbgPrint("OskitTCP: EINVAL\n");
- break;
- case OSK_ENOMEM:
- case OSK_ENOBUFS:
- Status = STATUS_INSUFFICIENT_RESOURCES;
- DbgPrint("OskitTCP: ENOMEM/ENOBUFS\n");
- break;
- case OSK_EPIPE:
- case OSK_ESHUTDOWN:
- Status = STATUS_FILE_CLOSED;
- DbgPrint("OskitTCP: ESHUTDOWN/EPIPE\n");
- break;
- case OSK_EMSGSIZE:
- Status = STATUS_BUFFER_TOO_SMALL;
- DbgPrint("OskitTCP: EMSGSIZE\n");
- break;
- case OSK_ETIMEDOUT:
- Status = STATUS_TIMEOUT;
- DbgPrint("OskitTCP: ETIMEDOUT\n");
- break;
- case OSK_ENETUNREACH:
- Status = STATUS_NETWORK_UNREACHABLE;
- DbgPrint("OskitTCP: ENETUNREACH\n");
- break;
- case OSK_EFAULT:
- Status = STATUS_ACCESS_VIOLATION;
- DbgPrint("OskitTCP: EFAULT\n");
- break;
- default:
- DbgPrint("OskitTCP returned unhandled error code: %d\n", OskitError);
- Status = STATUS_INVALID_CONNECTION;
- break;
++NTSTATUS TCPTranslateError(const err_t err)
++{
+ NTSTATUS Status;
+
- TI_DbgPrint(DEBUG_TCP,("Error %d -> %x\n", OskitError, Status));
++ switch (err)
++ {
++ case ERR_OK: Status = STATUS_SUCCESS; return Status; //0
++ case ERR_MEM: Status = STATUS_INSUFFICIENT_RESOURCES; break; //-1
++ case ERR_BUF: Status = STATUS_BUFFER_TOO_SMALL; break; //-2
++ case ERR_TIMEOUT: Status = STATUS_TIMEOUT; break; // -3
++ case ERR_RTE: Status = STATUS_NETWORK_UNREACHABLE; break; //-4
++ case ERR_INPROGRESS: Status = STATUS_PENDING; return Status; //-5
++ case ERR_VAL: Status = STATUS_INVALID_PARAMETER; break; //-6
++ case ERR_WOULDBLOCK: Status = STATUS_CANT_WAIT; break; //-7
++ case ERR_USE: Status = STATUS_ADDRESS_ALREADY_EXISTS; break; //-8
++ case ERR_ISCONN: Status = STATUS_UNSUCCESSFUL; break; //-9 (FIXME)
++ case ERR_ABRT: Status = STATUS_LOCAL_DISCONNECT; break; //-10
++ case ERR_RST: Status = STATUS_REMOTE_DISCONNECT; break; //-11
++ case ERR_CLSD: Status = STATUS_FILE_CLOSED; break; //-12
++ case ERR_CONN: Status = STATUS_INVALID_CONNECTION; break; //-13
++ case ERR_ARG: Status = STATUS_INVALID_PARAMETER; break; //-14
++ case ERR_IF: Status = STATUS_UNEXPECTED_NETWORK_ERROR; break; //-15
++ default:
++ DbgPrint("Invalid error value: %d\n", err);
++ ASSERT(FALSE);
++ Status = STATUS_UNSUCCESSFUL;
++ break;
+ }
++
++ DbgPrint("TCP operation failed: 0x%x (%d)\n", Status, err);
+
- PVOID Context ) {
+ return Status;
+}
+
+NTSTATUS TCPConnect
+( PCONNECTION_ENDPOINT Connection,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo,
+ PTCP_COMPLETION_ROUTINE Complete,
- SOCKADDR_IN AddressToConnect = { 0 }, AddressToBind = { 0 };
++ PVOID Context )
++{
+ NTSTATUS Status;
- PNEIGHBOR_CACHE_ENTRY NCE = NULL;
++ struct ip_addr bindaddr, connaddr;
+ IP_ADDRESS RemoteAddress;
+ USHORT RemotePort;
+ TA_IP_ADDRESS LocalAddress;
+ PTDI_BUCKET Bucket;
- TI_DbgPrint(DEBUG_TCP,("TCPConnect: Called\n"));
++ PNEIGHBOR_CACHE_ENTRY NCE;
+ KIRQL OldIrql;
+
- if (!NT_SUCCESS(Status)) {
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPConnect] Called\n"));
+
+ Status = AddrBuildAddress
+ ((PTRANSPORT_ADDRESS)ConnInfo->RemoteAddress,
+ &RemoteAddress,
+ &RemotePort);
+
- AddressToConnect.sin_family = AF_INET;
- AddressToBind = AddressToConnect;
-
++ if (!NT_SUCCESS(Status))
++ {
+ TI_DbgPrint(DEBUG_TCP, ("Could not AddrBuildAddress in TCPConnect\n"));
+ return Status;
+ }
+
+ /* Freed in TCPSocketState */
+ TI_DbgPrint(DEBUG_TCP,
+ ("Connecting to address %x:%x\n",
+ RemoteAddress.Address.IPv4Address,
+ RemotePort));
+
- }
+ LockObject(Connection, &OldIrql);
+
+ if (!Connection->AddressFile)
+ {
+ UnlockObject(Connection, OldIrql);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (AddrIsUnspecified(&Connection->AddressFile->Address))
+ {
+ if (!(NCE = RouteGetRouteToDestination(&RemoteAddress)))
+ {
+ UnlockObject(Connection, OldIrql);
+ return STATUS_NETWORK_UNREACHABLE;
+ }
- if (Connection->AddressFile->Port)
- {
- /* See if we had an unspecified bind address */
- if (NCE)
- {
- /* We did, so use the interface unicast address associated with the route */
- AddressToBind.sin_addr.s_addr = NCE->Interface->Unicast.Address.IPv4Address;
- }
- else
- {
- /* Bind address was explicit so use it */
- AddressToBind.sin_addr.s_addr = Connection->AddressFile->Address.Address.IPv4Address;
- }
-
- AddressToBind.sin_port = Connection->AddressFile->Port;
-
- /* Perform an explicit bind */
- Status = TCPTranslateError(OskitTCPBind(Connection,
- &AddressToBind,
- sizeof(AddressToBind)));
+
- /* An implicit bind will be performed */
- Status = STATUS_SUCCESS;
++ bindaddr.addr = NCE->Interface->Unicast.Address.IPv4Address;
+ }
+ else
+ {
- if (NT_SUCCESS(Status)) {
- if (NT_SUCCESS(Status))
++ bindaddr.addr = Connection->AddressFile->Address.Address.IPv4Address;
+ }
+
- memcpy( &AddressToConnect.sin_addr,
- &RemoteAddress.Address.IPv4Address,
- sizeof(AddressToConnect.sin_addr) );
- AddressToConnect.sin_port = RemotePort;
-
- Status = TCPTranslateError
- ( OskitTCPConnect( Connection,
- &AddressToConnect,
- sizeof(AddressToConnect) ) );
-
++ Status = TCPTranslateError(LibTCPBind(Connection,
++ &bindaddr,
++ Connection->AddressFile->Port));
++
++ if (NT_SUCCESS(Status))
++ {
++ /* Check if we had an unspecified port */
++ if (!Connection->AddressFile->Port)
+ {
- /* Check if we had an unspecified port */
- if (!Connection->AddressFile->Port)
- {
- /* We did, so we need to copy back the port */
- if (NT_SUCCESS(TCPGetSockAddress(Connection, (PTRANSPORT_ADDRESS)&LocalAddress, FALSE)))
- {
- /* Allocate the port in the port bitmap */
- Connection->AddressFile->Port = TCPAllocatePort(LocalAddress.Address[0].Address[0].sin_port);
-
- /* This should never fail */
- ASSERT(Connection->AddressFile->Port != 0xFFFF);
- }
- }
-
- /* Check if the address was unspecified */
- if (AddrIsUnspecified(&Connection->AddressFile->Address))
- {
- /* It is, so store the address of the outgoing NIC */
- Connection->AddressFile->Address = NCE->Interface->Unicast;
- }
++ /* We did, so we need to copy back the port */
++ Status = TCPGetSockAddress(Connection, (PTRANSPORT_ADDRESS)&LocalAddress, FALSE);
+ if (NT_SUCCESS(Status))
+ {
- if (Status == STATUS_PENDING)
++ /* Allocate the port in the port bitmap */
++ Connection->AddressFile->Port = TCPAllocatePort(LocalAddress.Address[0].Address[0].sin_port);
++
++ /* This should never fail */
++ ASSERT(Connection->AddressFile->Port != 0xFFFF);
+ }
++ }
+
- Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
- if( !Bucket )
- {
- UnlockObject(Connection, OldIrql);
- return STATUS_NO_MEMORY;
- }
-
- Bucket->Request.RequestNotifyObject = (PVOID)Complete;
- Bucket->Request.RequestContext = Context;
-
- InsertTailList( &Connection->ConnectRequest, &Bucket->Entry );
++ if (NT_SUCCESS(Status))
++ {
++ connaddr.addr = RemoteAddress.Address.IPv4Address;
++
++ Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
++ if (!Bucket)
+ {
- PVOID Context ) {
++ UnlockObject(Connection, OldIrql);
++ return STATUS_NO_MEMORY;
+ }
++
++ Bucket->Request.RequestNotifyObject = (PVOID)Complete;
++ Bucket->Request.RequestContext = Context;
++
++ InsertTailList( &Connection->ConnectRequest, &Bucket->Entry );
++
++ Status = TCPTranslateError(LibTCPConnect(Connection,
++ &connaddr,
++ RemotePort));
+ }
+ }
+
+ UnlockObject(Connection, OldIrql);
+
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPConnect] Leaving. Status = 0x%x\n", Status));
++
+ return Status;
+}
+
+NTSTATUS TCPDisconnect
+( PCONNECTION_ENDPOINT Connection,
+ UINT Flags,
+ PLARGE_INTEGER Timeout,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo,
+ PTCP_COMPLETION_ROUTINE Complete,
- PLIST_ENTRY Entry;
++ PVOID Context )
++{
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+ PTDI_BUCKET Bucket;
+ KIRQL OldIrql;
- TI_DbgPrint(DEBUG_TCP,("started\n"));
+ LARGE_INTEGER ActualTimeout;
+
- if (Flags & TDI_DISCONNECT_RELEASE)
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPDisconnect] Called\n"));
+
+ LockObject(Connection, &OldIrql);
+
- /* See if we can satisfy this right now */
- if (IsListEmpty(&Connection->SendRequest))
++ if (Connection->SocketContext)
+ {
- /* Send queue is empty so we're good to go */
- Status = TCPTranslateError(OskitTCPShutdown(Connection, FWRITE));
-
- UnlockObject(Connection, OldIrql);
-
- return Status;
- }
-
- /* Check if the timeout was 0 */
- if (Timeout && Timeout->QuadPart == 0)
- {
- OskitTCPShutdown(Connection, FWRITE);
-
- while (!IsListEmpty(&Connection->SendRequest))
++ if (Flags & TDI_DISCONNECT_RELEASE)
+ {
- Entry = RemoveHeadList(&Connection->SendRequest);
-
- Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
-
- Bucket->Information = 0;
- Bucket->Status = STATUS_FILE_CLOSED;
-
- CompleteBucket(Connection, Bucket);
++ if (IsListEmpty(&Connection->SendRequest))
+ {
-
- UnlockObject(Connection, OldIrql);
-
- return STATUS_TIMEOUT;
- }
-
- /* Otherwise we wait for the send queue to be empty */
- }
++ Status = TCPTranslateError(LibTCPShutdown(Connection, 0, 1));
+ }
- if ((Flags & TDI_DISCONNECT_ABORT) || !Flags)
- {
- /* This request overrides any pending graceful disconnects */
- while (!IsListEmpty(&Connection->ShutdownRequest))
- {
- Entry = RemoveHeadList(&Connection->ShutdownRequest);
-
- Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
-
- Bucket->Information = 0;
- Bucket->Status = STATUS_FILE_CLOSED;
-
- CompleteBucket(Connection, Bucket);
- }
-
- /* Also kill any pending reads and writes */
- while (!IsListEmpty(&Connection->SendRequest))
- {
- Entry = RemoveHeadList(&Connection->SendRequest);
-
- Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
-
- Bucket->Information = 0;
- Bucket->Status = STATUS_FILE_CLOSED;
-
- CompleteBucket(Connection, Bucket);
++ else if (Timeout && Timeout->QuadPart == 0)
++ {
++ FlushSendQueue(Connection, STATUS_FILE_CLOSED, FALSE);
++ TCPTranslateError(LibTCPShutdown(Connection, 0, 1));
++ Status = STATUS_TIMEOUT;
++ }
++ else
++ {
++ /* Use the timeout specified or 1 second if none was specified */
++ if (Timeout)
++ {
++ ActualTimeout = *Timeout;
++ }
++ else
++ {
++ ActualTimeout.QuadPart = -1000000;
++ }
+
-
- while (!IsListEmpty(&Connection->ReceiveRequest))
++ /* We couldn't complete the request now because we need to wait for outstanding I/O */
++ Bucket = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG);
++ if (!Bucket)
++ {
++ UnlockObject(Connection, OldIrql);
++ return STATUS_NO_MEMORY;
++ }
++
++ Bucket->Request.RequestNotifyObject = (PVOID)Complete;
++ Bucket->Request.RequestContext = Context;
++
++ InsertTailList(&Connection->ShutdownRequest, &Bucket->Entry);
++
++ ReferenceObject(Connection);
++ if (KeCancelTimer(&Connection->DisconnectTimer))
++ {
++ DereferenceObject(Connection);
++ }
++ KeSetTimer(&Connection->DisconnectTimer, ActualTimeout, &Connection->DisconnectDpc);
++
++ Status = STATUS_PENDING;
++ }
+ }
- Entry = RemoveHeadList(&Connection->ReceiveRequest);
-
- Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
-
- Bucket->Information = 0;
- Bucket->Status = STATUS_FILE_CLOSED;
-
- CompleteBucket(Connection, Bucket);
++
++ if ((Flags & TDI_DISCONNECT_ABORT) || !Flags)
+ {
-
- /* An abort never pends; we just drop everything and complete */
- Status = TCPTranslateError(OskitTCPShutdown(Connection, FWRITE | FREAD));
-
- UnlockObject(Connection, OldIrql);
-
- return Status;
- }
-
- /* We couldn't complete the request now because we need to wait for outstanding I/O */
- Bucket = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG);
- if (!Bucket)
- {
- UnlockObject(Connection, OldIrql);
- return STATUS_NO_MEMORY;
- }
-
- Bucket->Request.RequestNotifyObject = (PVOID)Complete;
- Bucket->Request.RequestContext = Context;
-
- InsertTailList(&Connection->ShutdownRequest, &Bucket->Entry);
-
- /* Use the timeout specified or 1 second if none was specified */
- if (Timeout)
- {
- ActualTimeout = *Timeout;
++ FlushReceiveQueue(Connection, STATUS_FILE_CLOSED, FALSE);
++ FlushSendQueue(Connection, STATUS_FILE_CLOSED, FALSE);
++ FlushShutdownQueue(Connection, STATUS_FILE_CLOSED, FALSE);
++ Status = TCPTranslateError(LibTCPShutdown(Connection, 1, 1));
+ }
- ActualTimeout.QuadPart = -1000000;
+ }
+ else
+ {
-
- ReferenceObject(Connection);
- KeSetTimer(&Connection->DisconnectTimer, ActualTimeout, &Connection->DisconnectDpc);
++ /* We already got closed by the other side so just return success */
++ Status = STATUS_SUCCESS;
+ }
- TI_DbgPrint(DEBUG_TCP,("finished %x\n", Status));
-
- return STATUS_PENDING;
- }
+
+ UnlockObject(Connection, OldIrql);
+
- NTSTATUS TCPClose
- ( PCONNECTION_ENDPOINT Connection )
- {
- KIRQL OldIrql;
-
- LockObject(Connection, &OldIrql);
-
- /* We should not be associated to an address file at this point */
- ASSERT(!Connection->AddressFile);
-
- OskitTCPClose(Connection);
-
- Connection->SocketContext = NULL;
-
- UnlockObject(Connection, OldIrql);
-
- DereferenceObject(Connection);
-
- return STATUS_SUCCESS;
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPDisconnect] Leaving. Status = 0x%x\n", Status));
+
- PVOID Context ) {
- PVOID DataBuffer;
- UINT DataLen, Received = 0;
- NTSTATUS Status;
++ return Status;
+}
+
+NTSTATUS TCPReceiveData
+( PCONNECTION_ENDPOINT Connection,
+ PNDIS_BUFFER Buffer,
+ ULONG ReceiveLength,
+ PULONG BytesReceived,
+ ULONG ReceiveFlags,
+ PTCP_COMPLETION_ROUTINE Complete,
- KIRQL OldIrql;
-
- NdisQueryBuffer( Buffer, &DataBuffer, &DataLen );
-
- TI_DbgPrint(DEBUG_TCP,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer, DataBuffer, DataLen));
++ PVOID Context )
++{
+ PTDI_BUCKET Bucket;
- LockObject(Connection, &OldIrql);
++ PUCHAR DataBuffer;
++ UINT DataLen, Received;
++ NTSTATUS Status;
+
- Status = TCPTranslateError
- ( OskitTCPRecv
- ( Connection,
- DataBuffer,
- DataLen,
- &Received,
- ReceiveFlags ) );
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPReceiveData] Called for %d bytes (on socket %x)\n",
++ ReceiveLength, Connection->SocketContext));
+
- TI_DbgPrint(DEBUG_TCP,("OskitTCPReceive: %x, %d\n", Status, Received));
++ NdisQueryBuffer(Buffer, &DataBuffer, &DataLen);
+
- /* Keep this request around ... there was no data yet */
- if( Status == STATUS_PENDING ) {
++ Status = LibTCPGetDataFromConnectionQueue(Connection, DataBuffer, DataLen, &Received);
+
- Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
- if( !Bucket ) {
- TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
- UnlockObject(Connection, OldIrql);
++ if (Status == STATUS_PENDING)
++ {
++
+ /* Freed in TCPSocketState */
-
++ Bucket = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG);
++ if (!Bucket)
++ {
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPReceiveData] Failed to allocate bucket\n"));
++
+ return STATUS_NO_MEMORY;
+ }
- *BytesReceived = 0;
++
+ Bucket->Request.RequestNotifyObject = Complete;
+ Bucket->Request.RequestContext = Context;
- InsertTailList( &Connection->ReceiveRequest, &Bucket->Entry );
- TI_DbgPrint(DEBUG_TCP,("Queued read irp\n"));
- } else {
- TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Received));
- *BytesReceived = Received;
- }
+
- UnlockObject(Connection, OldIrql);
++ ExInterlockedInsertTailList( &Connection->ReceiveRequest, &Bucket->Entry, &Connection->Lock );
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPReceiveData] Queued read irp\n"));
+
- TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPReceiveData] Leaving. Status = STATUS_PENDING\n"));
+
- PVOID Context ) {
- UINT Sent = 0;
++ (*BytesReceived) = 0;
++ }
++ else
++ {
++ (*BytesReceived) = Received;
++ }
+
+ return Status;
+}
+
+NTSTATUS TCPSendData
+( PCONNECTION_ENDPOINT Connection,
+ PCHAR BufferData,
+ ULONG SendLength,
+ PULONG BytesSent,
+ ULONG Flags,
+ PTCP_COMPLETION_ROUTINE Complete,
- TI_DbgPrint(DEBUG_TCP,("Connection = %x\n", Connection));
++ PVOID Context )
++{
+ NTSTATUS Status;
+ PTDI_BUCKET Bucket;
+ KIRQL OldIrql;
+
+ LockObject(Connection, &OldIrql);
+
- Status = TCPTranslateError
- ( OskitTCPSend( Connection,
- (OSK_PCHAR)BufferData, SendLength,
- &Sent, 0 ) );
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSendData] Called for %d bytes (on socket %x)\n",
++ SendLength, Connection->SocketContext));
+
- TI_DbgPrint(DEBUG_TCP,("OskitTCPSend: %x, %d\n", Status, Sent));
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSendData] Connection = %x\n", Connection));
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSendData] Connection->SocketContext = %x\n",
++ Connection->SocketContext));
+
- if( Status == STATUS_PENDING ) {
++ Status = TCPTranslateError(LibTCPSend(Connection,
++ BufferData,
++ SendLength,
++ FALSE));
++
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSendData] Send: %x, %d\n", Status, SendLength));
+
+ /* Keep this request around ... there was no data yet */
- if( !Bucket ) {
++ if (Status == STATUS_PENDING)
++ {
+ /* Freed in TCPSocketState */
+ Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
- TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
++ if (!Bucket)
++ {
+ UnlockObject(Connection, OldIrql);
- TI_DbgPrint(DEBUG_TCP,("Queued write irp\n"));
- } else {
- TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Sent));
- *BytesSent = Sent;
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSendData] Failed to allocate bucket\n"));
+ return STATUS_NO_MEMORY;
+ }
+
+ Bucket->Request.RequestNotifyObject = Complete;
+ Bucket->Request.RequestContext = Context;
+ *BytesSent = 0;
+
+ InsertTailList( &Connection->SendRequest, &Bucket->Entry );
- TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
++ TI_DbgPrint(DEBUG_TCP,("[IP, TCPSendData] Queued write irp\n"));
++ }
++ else if (Status == STATUS_SUCCESS)
++ {
++ *BytesSent = SendLength;
++ }
++ else
++ {
++ *BytesSent = 0;
+ }
+
+ UnlockObject(Connection, OldIrql);
+
- UINT TCPAllocatePort( UINT HintPort ) {
- if( HintPort ) {
- if( AllocatePort( &TCPPorts, HintPort ) ) return HintPort;
- else {
- TI_DbgPrint
- (MID_TRACE,("We got a hint port but couldn't allocate it\n"));
++ TI_DbgPrint(DEBUG_TCP, ("[IP, TCPSendData] Leaving. Status = %x\n", Status));
+
+ return Status;
+}
+
- } else return AllocatePortFromRange( &TCPPorts, 1024, 5000 );
++UINT TCPAllocatePort(const UINT HintPort)
++{
++ if (HintPort)
++ {
++ if (AllocatePort(&TCPPorts, HintPort))
++ return HintPort;
++ else
++ {
++ TI_DbgPrint(MID_TRACE,("We got a hint port but couldn't allocate it\n"));
+ return (UINT)-1;
+ }
- VOID TCPFreePort( UINT Port ) {
- DeallocatePort( &TCPPorts, Port );
++ }
++ else
++ return AllocatePortFromRange( &TCPPorts, 1024, 5000 );
+}
+
- BOOLEAN GetRemote ) {
- OSK_UINT LocalAddress, RemoteAddress;
- OSK_UI16 LocalPort, RemotePort;
++VOID TCPFreePort(const UINT Port)
++{
++ DeallocatePort(&TCPPorts, Port);
+}
+
+NTSTATUS TCPGetSockAddress
+( PCONNECTION_ENDPOINT Connection,
+ PTRANSPORT_ADDRESS Address,
- Status = TCPTranslateError(OskitTCPGetAddress(Connection,
- &LocalAddress, &LocalPort,
- &RemoteAddress, &RemotePort));
++ BOOLEAN GetRemote )
++{
+ PTA_IP_ADDRESS AddressIP = (PTA_IP_ADDRESS)Address;
++ struct ip_addr ipaddr;
+ NTSTATUS Status;
+ KIRQL OldIrql;
++
++ AddressIP->TAAddressCount = 1;
++ AddressIP->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
++ AddressIP->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
+
+ LockObject(Connection, &OldIrql);
+
-
- if (!NT_SUCCESS(Status))
- return Status;
-
- AddressIP->TAAddressCount = 1;
- AddressIP->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
- AddressIP->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
- AddressIP->Address[0].Address[0].sin_port = GetRemote ? RemotePort : LocalPort;
- AddressIP->Address[0].Address[0].in_addr = GetRemote ? RemoteAddress : LocalAddress;
++ if (GetRemote)
++ {
++ Status = TCPTranslateError(LibTCPGetPeerName(Connection->SocketContext,
++ &ipaddr,
++ &AddressIP->Address[0].Address[0].sin_port));
++ }
++ else
++ {
++ Status = TCPTranslateError(LibTCPGetHostName(Connection->SocketContext,
++ &ipaddr,
++ &AddressIP->Address[0].Address[0].sin_port));
++ }
+
+ UnlockObject(Connection, OldIrql);
- BOOLEAN TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint, PIRP Irp ) {
++
++ AddressIP->Address[0].Address[0].in_addr = ipaddr.addr;
+
+ return Status;
+}
+
++BOOLEAN TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint, PIRP Irp )
++{
+ PLIST_ENTRY Entry;
+ PLIST_ENTRY ListHead[5];
+ KIRQL OldIrql;
+ PTDI_BUCKET Bucket;
+ UINT i = 0;
+ BOOLEAN Found = FALSE;
+
+ ListHead[0] = &Endpoint->SendRequest;
+ ListHead[1] = &Endpoint->ReceiveRequest;
+ ListHead[2] = &Endpoint->ConnectRequest;
+ ListHead[3] = &Endpoint->ListenRequest;
+ ListHead[4] = &Endpoint->ShutdownRequest;
+
+ LockObject(Endpoint, &OldIrql);
+
+ for( i = 0; i < 5; i++ )
+ {
+ for( Entry = ListHead[i]->Flink;
+ Entry != ListHead[i];
+ Entry = Entry->Flink )
+ {
+ Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
+ if( Bucket->Request.RequestContext == Irp )
+ {
+ RemoveEntryList( &Bucket->Entry );
+ ExFreePoolWithTag( Bucket, TDI_BUCKET_TAG );
+ Found = TRUE;
+ break;
+ }
+ }
+ }
+
+ UnlockObject(Endpoint, OldIrql);
+
+ return Found;
+}
+
+/* EOF */
--- /dev/null
--- /dev/null
++<?xml version="1.0"?>
++<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
++<module name="lwip" type="staticlibrary" allowwarnings="true">
++ <include base="lwip">src/include</include>
++ <include base="lwip">src/include/ipv4</include>
++ <include base="tcpip">include</include>
++ <directory name="src">
++ <file>rosip.c</file>
++ <file>rostcp.c</file>
++ <file>rosmem.c</file>
++ <file>sys_arch.c</file>
++ <directory name="api">
++ <file>api_lib.c</file>
++ <file>api_msg.c</file>
++ <file>err.c</file>
++ <file>netbuf.c</file>
++ <file>netdb.c</file>
++ <file>netifapi.c</file>
++ <file>sockets.c</file>
++ <file>tcpip.c</file>
++ </directory>
++ <directory name="core">
++ <file>def.c</file>
++ <file>dhcp.c</file>
++ <file>dns.c</file>
++ <file>init.c</file>
++ <file>mem.c</file>
++ <file>memp.c</file>
++ <file>netif.c</file>
++ <file>pbuf.c</file>
++ <file>raw.c</file>
++ <file>stats.c</file>
++ <file>sys.c</file>
++ <file>tcp_in.c</file>
++ <file>tcp_out.c</file>
++ <file>tcp.c</file>
++ <file>timers.c</file>
++ <file>udp.c</file>
++ <directory name="ipv4">
++ <file>autoip.c</file>
++ <file>icmp.c</file>
++ <file>igmp.c</file>
++ <file>inet_chksum.c</file>
++ <file>inet.c</file>
++ <file>ip.c</file>
++ <file>ip_addr.c</file>
++ <file>ip_frag.c</file>
++ </directory>
++ <!--directory name="ipv6">
++ <file>icmp6.c</file>
++ <file>inet6.c</file>
++ <file>ip6_addr.c</file>
++ <file>ip6.c</file>
++ </directory-->
++ <directory name="snmp">
++ <file>asn1_dec.c</file>
++ <file>asn1_enc.c</file>
++ <file>mib_structs.c</file>
++ <file>mib2.c</file>
++ <file>msg_in.c</file>
++ <file>msg_out.c</file>
++ </directory>
++ </directory>
++ </directory>
++</module>
--- /dev/null
--- /dev/null
++/**
++ * @file
++ * Transmission Control Protocol, incoming traffic
++ *
++ * The input processing functions of the TCP layer.
++ *
++ * These functions are generally called in the order (ip_input() ->)
++ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
++ *
++ */
++
++/*
++ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without modification,
++ * are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright notice,
++ * this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright notice,
++ * this list of conditions and the following disclaimer in the documentation
++ * and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote products
++ * derived from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
++ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
++ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
++ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
++ * OF SUCH DAMAGE.
++ *
++ * This file is part of the lwIP TCP/IP stack.
++ *
++ * Author: Adam Dunkels <adam@sics.se>
++ *
++ */
++
++#include "lwip/opt.h"
++
++#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
++
++#include "lwip/tcp_impl.h"
++#include "lwip/def.h"
++#include "lwip/ip_addr.h"
++#include "lwip/netif.h"
++#include "lwip/mem.h"
++#include "lwip/memp.h"
++#include "lwip/inet_chksum.h"
++#include "lwip/stats.h"
++#include "lwip/snmp.h"
++#include "arch/perf.h"
++
++/* These variables are global to all functions involved in the input
++ processing of TCP segments. They are set by the tcp_input()
++ function. */
++static struct tcp_seg inseg;
++static struct tcp_hdr *tcphdr;
++static struct ip_hdr *iphdr;
++static u32_t seqno, ackno;
++static u8_t flags;
++static u16_t tcplen;
++
++static u8_t recv_flags;
++static struct pbuf *recv_data;
++
++struct tcp_pcb *tcp_input_pcb;
++
++/* Forward declarations. */
++static err_t tcp_process(struct tcp_pcb *pcb);
++static void tcp_receive(struct tcp_pcb *pcb);
++static void tcp_parseopt(struct tcp_pcb *pcb);
++
++static err_t tcp_listen_input(struct tcp_pcb_listen *pcb);
++static err_t tcp_timewait_input(struct tcp_pcb *pcb);
++
++static const char * const tcp_state_str[] = {
++ "CLOSED",
++ "LISTEN",
++ "SYN_SENT",
++ "SYN_RCVD",
++ "ESTABLISHED",
++ "FIN_WAIT_1",
++ "FIN_WAIT_2",
++ "CLOSE_WAIT",
++ "CLOSING",
++ "LAST_ACK",
++ "TIME_WAIT"
++};
++
++/**
++ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
++ * the segment between the PCBs and passes it on to tcp_process(), which implements
++ * the TCP finite state machine. This function is called by the IP layer (in
++ * ip_input()).
++ *
++ * @param p received TCP segment to process (p->payload pointing to the IP header)
++ * @param inp network interface on which this segment was received
++ */
++void
++tcp_input(struct pbuf *p, struct netif *inp)
++{
++ struct tcp_pcb *pcb, *prev;
++ struct tcp_pcb_listen *lpcb;
++#if SO_REUSE
++ struct tcp_pcb *lpcb_prev = NULL;
++ struct tcp_pcb_listen *lpcb_any = NULL;
++#endif /* SO_REUSE */
++ u8_t hdrlen;
++ err_t err;
++
++ PERF_START;
++
++ TCP_STATS_INC(tcp.recv);
++ snmp_inc_tcpinsegs();
++
++ iphdr = (struct ip_hdr *)p->payload;
++ tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
++
++#if TCP_INPUT_DEBUG
++ tcp_debug_print(tcphdr);
++#endif
++
++ /* remove header from payload */
++ if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
++ /* drop short packets */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
++ TCP_STATS_INC(tcp.lenerr);
++ TCP_STATS_INC(tcp.drop);
++ snmp_inc_tcpinerrs();
++ pbuf_free(p);
++ return;
++ }
++
++ /* Don't even process incoming broadcasts/multicasts. */
++ if (ip_addr_isbroadcast(¤t_iphdr_dest, inp) ||
++ ip_addr_ismulticast(¤t_iphdr_dest)) {
++ TCP_STATS_INC(tcp.proterr);
++ TCP_STATS_INC(tcp.drop);
++ snmp_inc_tcpinerrs();
++ pbuf_free(p);
++ return;
++ }
++
++#if CHECKSUM_CHECK_TCP
++ /* Verify TCP checksum. */
++ if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
++ IP_PROTO_TCP, p->tot_len) != 0) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
++ inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
++ IP_PROTO_TCP, p->tot_len)));
++#if TCP_DEBUG
++ tcp_debug_print(tcphdr);
++#endif /* TCP_DEBUG */
++ TCP_STATS_INC(tcp.chkerr);
++ TCP_STATS_INC(tcp.drop);
++ snmp_inc_tcpinerrs();
++ pbuf_free(p);
++ return;
++ }
++#endif
++
++ /* Move the payload pointer in the pbuf so that it points to the
++ TCP data instead of the TCP header. */
++ hdrlen = TCPH_HDRLEN(tcphdr);
++ if(pbuf_header(p, -(hdrlen * 4))){
++ /* drop short packets */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
++ TCP_STATS_INC(tcp.lenerr);
++ TCP_STATS_INC(tcp.drop);
++ snmp_inc_tcpinerrs();
++ pbuf_free(p);
++ return;
++ }
++
++ /* Convert fields in TCP header to host byte order. */
++ tcphdr->src = ntohs(tcphdr->src);
++ tcphdr->dest = ntohs(tcphdr->dest);
++ seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
++ ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
++ tcphdr->wnd = ntohs(tcphdr->wnd);
++
++ flags = TCPH_FLAGS(tcphdr);
++ tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
++
++ /* Demultiplex an incoming segment. First, we check if it is destined
++ for an active connection. */
++ prev = NULL;
++
++
++ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
++ LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
++ LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
++ LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
++ if (pcb->remote_port == tcphdr->src &&
++ pcb->local_port == tcphdr->dest &&
++ ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) &&
++ ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) {
++
++ /* Move this PCB to the front of the list so that subsequent
++ lookups will be faster (we exploit locality in TCP segment
++ arrivals). */
++ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
++ if (prev != NULL) {
++ prev->next = pcb->next;
++ pcb->next = tcp_active_pcbs;
++ tcp_active_pcbs = pcb;
++ }
++ LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
++ break;
++ }
++ prev = pcb;
++ }
++
++ if (pcb == NULL) {
++ /* If it did not go to an active connection, we check the connections
++ in the TIME-WAIT state. */
++ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
++ LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
++ if (pcb->remote_port == tcphdr->src &&
++ pcb->local_port == tcphdr->dest &&
++ ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) &&
++ ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) {
++ /* We don't really care enough to move this PCB to the front
++ of the list since we are not very likely to receive that
++ many segments for connections in TIME-WAIT. */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
++ tcp_timewait_input(pcb);
++ pbuf_free(p);
++ return;
++ }
++ }
++
++ /* Finally, if we still did not get a match, we check all PCBs that
++ are LISTENing for incoming connections. */
++ prev = NULL;
++ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
++ if (lpcb->local_port == tcphdr->dest) {
++#if SO_REUSE
++ if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest)) {
++ /* found an exact match */
++ break;
++ } else if(ip_addr_isany(&(lpcb->local_ip))) {
++ /* found an ANY-match */
++ lpcb_any = lpcb;
++ lpcb_prev = prev;
++ }
++#else /* SO_REUSE */
++ if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest) ||
++ ip_addr_isany(&(lpcb->local_ip))) {
++ /* found a match */
++ break;
++ }
++#endif /* SO_REUSE */
++ }
++ prev = (struct tcp_pcb *)lpcb;
++ }
++#if SO_REUSE
++ /* first try specific local IP */
++ if (lpcb == NULL) {
++ /* only pass to ANY if no specific local IP has been found */
++ lpcb = lpcb_any;
++ prev = lpcb_prev;
++ }
++#endif /* SO_REUSE */
++ if (lpcb != NULL) {
++ /* Move this PCB to the front of the list so that subsequent
++ lookups will be faster (we exploit locality in TCP segment
++ arrivals). */
++ if (prev != NULL) {
++ ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
++ /* our successor is the remainder of the listening list */
++ lpcb->next = tcp_listen_pcbs.listen_pcbs;
++ /* put this listening pcb at the head of the listening list */
++ tcp_listen_pcbs.listen_pcbs = lpcb;
++ }
++
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
++ tcp_listen_input(lpcb);
++ pbuf_free(p);
++ return;
++ }
++ }
++
++#if TCP_INPUT_DEBUG
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
++ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
++#endif /* TCP_INPUT_DEBUG */
++
++
++ if (pcb != NULL) {
++ /* The incoming segment belongs to a connection. */
++#if TCP_INPUT_DEBUG
++#if TCP_DEBUG
++ tcp_debug_print_state(pcb->state);
++#endif /* TCP_DEBUG */
++#endif /* TCP_INPUT_DEBUG */
++
++ /* Set up a tcp_seg structure. */
++ inseg.next = NULL;
++ inseg.len = p->tot_len;
++ inseg.p = p;
++ inseg.tcphdr = tcphdr;
++
++ recv_data = NULL;
++ recv_flags = 0;
++
++ /* If there is data which was previously "refused" by upper layer */
++ if (pcb->refused_data != NULL) {
++ /* Notify again application with data previously received. */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
++ TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
++ if (err == ERR_OK) {
++ pcb->refused_data = NULL;
++ } else if ((err == ERR_ABRT) || (tcplen > 0)) {
++ /* if err == ERR_ABRT, 'pcb' is already deallocated */
++ /* Drop incoming packets because pcb is "full" (only if the incoming
++ segment contains data). */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
++ TCP_STATS_INC(tcp.drop);
++ snmp_inc_tcpinerrs();
++ pbuf_free(p);
++ return;
++ }
++ }
++ tcp_input_pcb = pcb;
++ err = tcp_process(pcb);
++ /* A return value of ERR_ABRT means that tcp_abort() was called
++ and that the pcb has been freed. If so, we don't do anything. */
++ if (err != ERR_ABRT) {
++ if (recv_flags & TF_RESET) {
++ /* TF_RESET means that the connection was reset by the other
++ end. We then call the error callback to inform the
++ application that the connection is dead before we
++ deallocate the PCB. */
++ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
++ tcp_pcb_remove(&tcp_active_pcbs, pcb);
++ memp_free(MEMP_TCP_PCB, pcb);
++ } else if (recv_flags & TF_CLOSED) {
++ /* The connection has been closed and we will deallocate the
++ PCB. */
++ TCP_EVENT_CLOSED(pcb, err);
++ if (err == ERR_ABRT) {
++ goto aborted;
++ }
++ tcp_pcb_remove(&tcp_active_pcbs, pcb);
++ memp_free(MEMP_TCP_PCB, pcb);
++ } else {
++ err = ERR_OK;
++ /* If the application has registered a "sent" function to be
++ called when new send buffer space is available, we call it
++ now. */
++ if (pcb->acked > 0) {
++ TCP_EVENT_SENT(pcb, pcb->acked, err);
++ if (err == ERR_ABRT) {
++ goto aborted;
++ }
++ }
++
++ if (recv_data != NULL) {
++ LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
++ if (pcb->flags & TF_RXCLOSED) {
++ /* received data although already closed -> abort (send RST) to
++ notify the remote host that not all data has been processed */
++ pbuf_free(recv_data);
++ tcp_abort(pcb);
++ goto aborted;
++ }
++ if (flags & TCP_PSH) {
++ recv_data->flags |= PBUF_FLAG_PUSH;
++ }
++
++ /* Notify application that data has been received. */
++ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
++ if (err == ERR_ABRT) {
++ goto aborted;
++ }
++
++ /* If the upper layer can't receive this data, store it */
++ if (err != ERR_OK) {
++ pcb->refused_data = recv_data;
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
++ }
++ }
++
++ /* If a FIN segment was received, we call the callback
++ function with a NULL buffer to indicate EOF. */
++ if (recv_flags & TF_GOT_FIN) {
++ /* correct rcv_wnd as the application won't call tcp_recved()
++ for the FIN's seqno */
++ if (pcb->rcv_wnd != TCP_WND) {
++ pcb->rcv_wnd++;
++ }
++ TCP_EVENT_CLOSED(pcb, err);
++ if (err == ERR_ABRT) {
++ goto aborted;
++ }
++ }
++
++ tcp_input_pcb = NULL;
++ /* Try to send something out. */
++ tcp_output(pcb);
++#if TCP_INPUT_DEBUG
++#if TCP_DEBUG
++ tcp_debug_print_state(pcb->state);
++#endif /* TCP_DEBUG */
++#endif /* TCP_INPUT_DEBUG */
++ }
++ }
++ /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
++ Below this line, 'pcb' may not be dereferenced! */
++aborted:
++ tcp_input_pcb = NULL;
++ recv_data = NULL;
++
++ /* give up our reference to inseg.p */
++ if (inseg.p != NULL)
++ {
++ pbuf_free(inseg.p);
++ inseg.p = NULL;
++ }
++ } else {
++
++ /* If no matching PCB was found, send a TCP RST (reset) to the
++ sender. */
++ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
++ if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
++ TCP_STATS_INC(tcp.proterr);
++ TCP_STATS_INC(tcp.drop);
++ tcp_rst(ackno, seqno + tcplen,
++ ip_current_dest_addr(), ip_current_src_addr(),
++ tcphdr->dest, tcphdr->src);
++ }
++ pbuf_free(p);
++ }
++
++ LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
++ PERF_STOP("tcp_input");
++}
++
++/**
++ * Called by tcp_input() when a segment arrives for a listening
++ * connection (from tcp_input()).
++ *
++ * @param pcb the tcp_pcb_listen for which a segment arrived
++ * @return ERR_OK if the segment was processed
++ * another err_t on error
++ *
++ * @note the return value is not (yet?) used in tcp_input()
++ * @note the segment which arrived is saved in global variables, therefore only the pcb
++ * involved is passed as a parameter to this function
++ */
++static err_t
++tcp_listen_input(struct tcp_pcb_listen *pcb)
++{
++ struct tcp_pcb *npcb;
++ err_t rc;
++
++ /* In the LISTEN state, we check for incoming SYN segments,
++ creates a new PCB, and responds with a SYN|ACK. */
++ if (flags & TCP_ACK) {
++ /* For incoming segments with the ACK flag set, respond with a
++ RST. */
++ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
++ tcp_rst(ackno + 1, seqno + tcplen,
++ ip_current_dest_addr(), ip_current_src_addr(),
++ tcphdr->dest, tcphdr->src);
++ } else if (flags & TCP_SYN) {
++ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
++#if TCP_LISTEN_BACKLOG
++ if (pcb->accepts_pending >= pcb->backlog) {
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
++ return ERR_ABRT;
++ }
++#endif /* TCP_LISTEN_BACKLOG */
++ npcb = tcp_alloc(pcb->prio);
++ /* If a new PCB could not be created (probably due to lack of memory),
++ we don't do anything, but rely on the sender will retransmit the
++ SYN at a time when we have more memory available. */
++ if (npcb == NULL) {
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
++ TCP_STATS_INC(tcp.memerr);
++ return ERR_MEM;
++ }
++#if TCP_LISTEN_BACKLOG
++ pcb->accepts_pending++;
++#endif /* TCP_LISTEN_BACKLOG */
++ /* Set up the new PCB. */
++ ip_addr_copy(npcb->local_ip, current_iphdr_dest);
++ npcb->local_port = pcb->local_port;
++ ip_addr_copy(npcb->remote_ip, current_iphdr_src);
++ npcb->remote_port = tcphdr->src;
++ npcb->state = SYN_RCVD;
++ npcb->rcv_nxt = seqno + 1;
++ npcb->rcv_ann_right_edge = npcb->rcv_nxt;
++ npcb->snd_wnd = tcphdr->wnd;
++ npcb->ssthresh = npcb->snd_wnd;
++ npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
++ npcb->callback_arg = pcb->callback_arg;
++#if LWIP_CALLBACK_API
++ npcb->accept = pcb->accept;
++#endif /* LWIP_CALLBACK_API */
++ /* inherit socket options */
++ npcb->so_options = pcb->so_options & SOF_INHERITED;
++ /* Register the new PCB so that we can begin receiving segments
++ for it. */
++ TCP_REG(&tcp_active_pcbs, npcb);
++
++ /* Parse any options in the SYN. */
++ tcp_parseopt(npcb);
++#if TCP_CALCULATE_EFF_SEND_MSS
++ npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
++#endif /* TCP_CALCULATE_EFF_SEND_MSS */
++
++ snmp_inc_tcppassiveopens();
++
++ /* Send a SYN|ACK together with the MSS option. */
++ rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
++ if (rc != ERR_OK) {
++ tcp_abandon(npcb, 0);
++ return rc;
++ }
++ return tcp_output(npcb);
++ }
++ return ERR_OK;
++}
++
++/**
++ * Called by tcp_input() when a segment arrives for a connection in
++ * TIME_WAIT.
++ *
++ * @param pcb the tcp_pcb for which a segment arrived
++ *
++ * @note the segment which arrived is saved in global variables, therefore only the pcb
++ * involved is passed as a parameter to this function
++ */
++static err_t
++tcp_timewait_input(struct tcp_pcb *pcb)
++{
++ /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
++ /* RFC 793 3.9 Event Processing - Segment Arrives:
++ * - first check sequence number - we skip that one in TIME_WAIT (always
++ * acceptable since we only send ACKs)
++ * - second check the RST bit (... return) */
++ if (flags & TCP_RST) {
++ return ERR_OK;
++ }
++ /* - fourth, check the SYN bit, */
++ if (flags & TCP_SYN) {
++ /* If an incoming segment is not acceptable, an acknowledgment
++ should be sent in reply */
++ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
++ /* If the SYN is in the window it is an error, send a reset */
++ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
++ tcphdr->dest, tcphdr->src);
++ return ERR_OK;
++ }
++ } else if (flags & TCP_FIN) {
++ /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
++ Restart the 2 MSL time-wait timeout.*/
++ pcb->tmr = tcp_ticks;
++ }
++
++ if ((tcplen > 0)) {
++ /* Acknowledge data, FIN or out-of-window SYN */
++ pcb->flags |= TF_ACK_NOW;
++ return tcp_output(pcb);
++ }
++ return ERR_OK;
++}
++
++/**
++ * Implements the TCP state machine. Called by tcp_input. In some
++ * states tcp_receive() is called to receive data. The tcp_seg
++ * argument will be freed by the caller (tcp_input()) unless the
++ * recv_data pointer in the pcb is set.
++ *
++ * @param pcb the tcp_pcb for which a segment arrived
++ *
++ * @note the segment which arrived is saved in global variables, therefore only the pcb
++ * involved is passed as a parameter to this function
++ */
++static err_t
++tcp_process(struct tcp_pcb *pcb)
++{
++ struct tcp_seg *rseg;
++ u8_t acceptable = 0;
++ err_t err;
++
++ err = ERR_OK;
++
++ /* Process incoming RST segments. */
++ if (flags & TCP_RST) {
++ /* First, determine if the reset is acceptable. */
++ if (pcb->state == SYN_SENT) {
++ if (ackno == pcb->snd_nxt) {
++ acceptable = 1;
++ }
++ } else {
++ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
++ pcb->rcv_nxt+pcb->rcv_wnd)) {
++ acceptable = 1;
++ }
++ }
++
++ if (acceptable) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
++ LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
++ recv_flags |= TF_RESET;
++ pcb->flags &= ~TF_ACK_DELAY;
++ return ERR_RST;
++ } else {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
++ seqno, pcb->rcv_nxt));
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
++ seqno, pcb->rcv_nxt));
++ return ERR_OK;
++ }
++ }
++
++ if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
++ /* Cope with new connection attempt after remote end crashed */
++ tcp_ack_now(pcb);
++ return ERR_OK;
++ }
++
++ if ((pcb->flags & TF_RXCLOSED) == 0) {
++ /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
++ pcb->tmr = tcp_ticks;
++ }
++ pcb->keep_cnt_sent = 0;
++
++ tcp_parseopt(pcb);
++
++ /* Do different things depending on the TCP state. */
++ switch (pcb->state) {
++ case SYN_SENT:
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
++ pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
++ /* received SYN ACK with expected sequence number? */
++ if ((flags & TCP_ACK) && (flags & TCP_SYN)
++ && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
++ pcb->snd_buf++;
++ pcb->rcv_nxt = seqno + 1;
++ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
++ pcb->lastack = ackno;
++ pcb->snd_wnd = tcphdr->wnd;
++ pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
++ pcb->state = ESTABLISHED;
++
++#if TCP_CALCULATE_EFF_SEND_MSS
++ pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
++#endif /* TCP_CALCULATE_EFF_SEND_MSS */
++
++ /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
++ * but for the default value of pcb->mss) */
++ pcb->ssthresh = pcb->mss * 10;
++
++ pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
++ LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
++ --pcb->snd_queuelen;
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
++ rseg = pcb->unacked;
++ pcb->unacked = rseg->next;
++
++ /* If there's nothing left to acknowledge, stop the retransmit
++ timer, otherwise reset it to start again */
++ if(pcb->unacked == NULL)
++ pcb->rtime = -1;
++ else {
++ pcb->rtime = 0;
++ pcb->nrtx = 0;
++ }
++
++ tcp_seg_free(rseg);
++
++ /* Call the user specified function to call when sucessfully
++ * connected. */
++ TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
++ if (err == ERR_ABRT) {
++ return ERR_ABRT;
++ }
++ tcp_ack_now(pcb);
++ }
++ /* received ACK? possibly a half-open connection */
++ else if (flags & TCP_ACK) {
++ /* send a RST to bring the other side in a non-synchronized state. */
++ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
++ tcphdr->dest, tcphdr->src);
++ }
++ break;
++ case SYN_RCVD:
++ if (flags & TCP_ACK) {
++ /* expected ACK number? */
++ if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
++ u16_t old_cwnd;
++ pcb->state = ESTABLISHED;
++ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
++#if LWIP_CALLBACK_API
++ LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
++#endif
++ /* Call the accept function. */
++ TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
++ if (err != ERR_OK) {
++ /* If the accept function returns with an error, we abort
++ * the connection. */
++ /* Already aborted? */
++ if (err != ERR_ABRT) {
++ tcp_abort(pcb);
++ }
++ return ERR_ABRT;
++ }
++ old_cwnd = pcb->cwnd;
++ /* If there was any data contained within this ACK,
++ * we'd better pass it on to the application as well. */
++ tcp_receive(pcb);
++
++ /* Prevent ACK for SYN to generate a sent event */
++ if (pcb->acked != 0) {
++ pcb->acked--;
++ }
++
++ pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
++
++ if (recv_flags & TF_GOT_FIN) {
++ tcp_ack_now(pcb);
++ pcb->state = CLOSE_WAIT;
++ }
++ } else {
++ /* incorrect ACK number, send RST */
++ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
++ tcphdr->dest, tcphdr->src);
++ }
++ } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
++ /* Looks like another copy of the SYN - retransmit our SYN-ACK */
++ tcp_rexmit(pcb);
++ }
++ break;
++ case CLOSE_WAIT:
++ /* FALLTHROUGH */
++ case ESTABLISHED:
++ tcp_receive(pcb);
++ if (recv_flags & TF_GOT_FIN) { /* passive close */
++ tcp_ack_now(pcb);
++ pcb->state = CLOSE_WAIT;
++ }
++ break;
++ case FIN_WAIT_1:
++ tcp_receive(pcb);
++ if (recv_flags & TF_GOT_FIN) {
++ if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
++ LWIP_DEBUGF(TCP_DEBUG,
++ ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
++ tcp_ack_now(pcb);
++ tcp_pcb_purge(pcb);
++ TCP_RMV(&tcp_active_pcbs, pcb);
++ pcb->state = TIME_WAIT;
++ TCP_REG(&tcp_tw_pcbs, pcb);
++ } else {
++ tcp_ack_now(pcb);
++ pcb->state = CLOSING;
++ }
++ } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
++ pcb->state = FIN_WAIT_2;
++ }
++ break;
++ case FIN_WAIT_2:
++ tcp_receive(pcb);
++ if (recv_flags & TF_GOT_FIN) {
++ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
++ tcp_ack_now(pcb);
++ tcp_pcb_purge(pcb);
++ TCP_RMV(&tcp_active_pcbs, pcb);
++ pcb->state = TIME_WAIT;
++ TCP_REG(&tcp_tw_pcbs, pcb);
++ }
++ break;
++ case CLOSING:
++ tcp_receive(pcb);
++ if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
++ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
++ tcp_pcb_purge(pcb);
++ TCP_RMV(&tcp_active_pcbs, pcb);
++ pcb->state = TIME_WAIT;
++ TCP_REG(&tcp_tw_pcbs, pcb);
++ }
++ break;
++ case LAST_ACK:
++ tcp_receive(pcb);
++ if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
++ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
++ /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
++ recv_flags |= TF_CLOSED;
++ }
++ break;
++ default:
++ break;
++ }
++ return ERR_OK;
++}
++
++#if TCP_QUEUE_OOSEQ
++/**
++ * Insert segment into the list (segments covered with new one will be deleted)
++ *
++ * Called from tcp_receive()
++ */
++static void
++tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
++{
++ struct tcp_seg *old_seg;
++
++ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
++ /* received segment overlaps all following segments */
++ tcp_segs_free(next);
++ next = NULL;
++ }
++ else {
++ /* delete some following segments
++ oos queue may have segments with FIN flag */
++ while (next &&
++ TCP_SEQ_GEQ((seqno + cseg->len),
++ (next->tcphdr->seqno + next->len))) {
++ /* cseg with FIN already processed */
++ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
++ TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
++ }
++ old_seg = next;
++ next = next->next;
++ tcp_seg_free(old_seg);
++ }
++ if (next &&
++ TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
++ /* We need to trim the incoming segment. */
++ cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
++ pbuf_realloc(cseg->p, cseg->len);
++ }
++ }
++ cseg->next = next;
++}
++#endif /* TCP_QUEUE_OOSEQ */
++
++/**
++ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
++ * data, and if so frees the memory of the buffered data. Next, is places the
++ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
++ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
++ * i it has been removed from the buffer.
++ *
++ * If the incoming segment constitutes an ACK for a segment that was used for RTT
++ * estimation, the RTT is estimated here as well.
++ *
++ * Called from tcp_process().
++ */
++static void
++tcp_receive(struct tcp_pcb *pcb)
++{
++ struct tcp_seg *next;
++#if TCP_QUEUE_OOSEQ
++ struct tcp_seg *prev, *cseg;
++#endif /* TCP_QUEUE_OOSEQ */
++ struct pbuf *p;
++ s32_t off;
++ s16_t m;
++ u32_t right_wnd_edge;
++ u16_t new_tot_len;
++ int found_dupack = 0;
++
++ if (flags & TCP_ACK) {
++ right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
++
++ /* Update window. */
++ if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
++ (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
++ (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
++ pcb->snd_wnd = tcphdr->wnd;
++ pcb->snd_wl1 = seqno;
++ pcb->snd_wl2 = ackno;
++ if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) {
++ pcb->persist_backoff = 0;
++ }
++ LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
++#if TCP_WND_DEBUG
++ } else {
++ if (pcb->snd_wnd != tcphdr->wnd) {
++ LWIP_DEBUGF(TCP_WND_DEBUG,
++ ("tcp_receive: no window update lastack %"U32_F" ackno %"
++ U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
++ pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
++ }
++#endif /* TCP_WND_DEBUG */
++ }
++
++ /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
++ * duplicate ack if:
++ * 1) It doesn't ACK new data
++ * 2) length of received packet is zero (i.e. no payload)
++ * 3) the advertised window hasn't changed
++ * 4) There is outstanding unacknowledged data (retransmission timer running)
++ * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
++ *
++ * If it passes all five, should process as a dupack:
++ * a) dupacks < 3: do nothing
++ * b) dupacks == 3: fast retransmit
++ * c) dupacks > 3: increase cwnd
++ *
++ * If it only passes 1-3, should reset dupack counter (and add to
++ * stats, which we don't do in lwIP)
++ *
++ * If it only passes 1, should reset dupack counter
++ *
++ */
++
++ /* Clause 1 */
++ if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
++ pcb->acked = 0;
++ /* Clause 2 */
++ if (tcplen == 0) {
++ /* Clause 3 */
++ if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
++ /* Clause 4 */
++ if (pcb->rtime >= 0) {
++ /* Clause 5 */
++ if (pcb->lastack == ackno) {
++ found_dupack = 1;
++ if (pcb->dupacks + 1 > pcb->dupacks)
++ ++pcb->dupacks;
++ if (pcb->dupacks > 3) {
++ /* Inflate the congestion window, but not if it means that
++ the value overflows. */
++ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
++ pcb->cwnd += pcb->mss;
++ }
++ } else if (pcb->dupacks == 3) {
++ /* Do fast retransmit */
++ tcp_rexmit_fast(pcb);
++ }
++ }
++ }
++ }
++ }
++ /* If Clause (1) or more is true, but not a duplicate ack, reset
++ * count of consecutive duplicate acks */
++ if (!found_dupack) {
++ pcb->dupacks = 0;
++ }
++ } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
++ /* We come here when the ACK acknowledges new data. */
++
++ /* Reset the "IN Fast Retransmit" flag, since we are no longer
++ in fast retransmit. Also reset the congestion window to the
++ slow start threshold. */
++ if (pcb->flags & TF_INFR) {
++ pcb->flags &= ~TF_INFR;
++ pcb->cwnd = pcb->ssthresh;
++ }
++
++ /* Reset the number of retransmissions. */
++ pcb->nrtx = 0;
++
++ /* Reset the retransmission time-out. */
++ pcb->rto = (pcb->sa >> 3) + pcb->sv;
++
++ /* Update the send buffer space. Diff between the two can never exceed 64K? */
++ pcb->acked = (u16_t)(ackno - pcb->lastack);
++
++ pcb->snd_buf += pcb->acked;
++
++ /* Reset the fast retransmit variables. */
++ pcb->dupacks = 0;
++ pcb->lastack = ackno;
++
++ /* Update the congestion control variables (cwnd and
++ ssthresh). */
++ if (pcb->state >= ESTABLISHED) {
++ if (pcb->cwnd < pcb->ssthresh) {
++ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
++ pcb->cwnd += pcb->mss;
++ }
++ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
++ } else {
++ u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
++ if (new_cwnd > pcb->cwnd) {
++ pcb->cwnd = new_cwnd;
++ }
++ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));
++ }
++ }
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
++ ackno,
++ pcb->unacked != NULL?
++ ntohl(pcb->unacked->tcphdr->seqno): 0,
++ pcb->unacked != NULL?
++ ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
++
++ /* Remove segment from the unacknowledged list if the incoming
++ ACK acknowlegdes them. */
++ while (pcb->unacked != NULL &&
++ TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
++ TCP_TCPLEN(pcb->unacked), ackno)) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
++ ntohl(pcb->unacked->tcphdr->seqno),
++ ntohl(pcb->unacked->tcphdr->seqno) +
++ TCP_TCPLEN(pcb->unacked)));
++
++ next = pcb->unacked;
++ pcb->unacked = pcb->unacked->next;
++
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
++ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
++ /* Prevent ACK for FIN to generate a sent event */
++ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
++ pcb->acked--;
++ }
++
++ pcb->snd_queuelen -= pbuf_clen(next->p);
++ tcp_seg_free(next);
++
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen));
++ if (pcb->snd_queuelen != 0) {
++ LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
++ pcb->unsent != NULL);
++ }
++ }
++
++ /* If there's nothing left to acknowledge, stop the retransmit
++ timer, otherwise reset it to start again */
++ if(pcb->unacked == NULL)
++ pcb->rtime = -1;
++ else
++ pcb->rtime = 0;
++
++ pcb->polltmr = 0;
++ } else {
++ /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
++ pcb->acked = 0;
++ }
++
++ /* We go through the ->unsent list to see if any of the segments
++ on the list are acknowledged by the ACK. This may seem
++ strange since an "unsent" segment shouldn't be acked. The
++ rationale is that lwIP puts all outstanding segments on the
++ ->unsent list after a retransmission, so these segments may
++ in fact have been sent once. */
++ while (pcb->unsent != NULL &&
++ TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) +
++ TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
++ ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
++ TCP_TCPLEN(pcb->unsent)));
++
++ next = pcb->unsent;
++ pcb->unsent = pcb->unsent->next;
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
++ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
++ /* Prevent ACK for FIN to generate a sent event */
++ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
++ pcb->acked--;
++ }
++ pcb->snd_queuelen -= pbuf_clen(next->p);
++ tcp_seg_free(next);
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen));
++ if (pcb->snd_queuelen != 0) {
++ LWIP_ASSERT("tcp_receive: valid queue length",
++ pcb->unacked != NULL || pcb->unsent != NULL);
++ }
++ }
++ /* End of ACK for new data processing. */
++
++ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
++ pcb->rttest, pcb->rtseq, ackno));
++
++ /* RTT estimation calculations. This is done by checking if the
++ incoming segment acknowledges the segment we use to take a
++ round-trip time measurement. */
++ if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
++ /* diff between this shouldn't exceed 32K since this are tcp timer ticks
++ and a round-trip shouldn't be that long... */
++ m = (s16_t)(tcp_ticks - pcb->rttest);
++
++ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
++ m, m * TCP_SLOW_INTERVAL));
++
++ /* This is taken directly from VJs original code in his paper */
++ m = m - (pcb->sa >> 3);
++ pcb->sa += m;
++ if (m < 0) {
++ m = -m;
++ }
++ m = m - (pcb->sv >> 2);
++ pcb->sv += m;
++ pcb->rto = (pcb->sa >> 3) + pcb->sv;
++
++ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
++ pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
++
++ pcb->rttest = 0;
++ }
++ }
++
++ /* If the incoming segment contains data, we must process it
++ further. */
++ if (tcplen > 0) {
++ /* This code basically does three things:
++
++ +) If the incoming segment contains data that is the next
++ in-sequence data, this data is passed to the application. This
++ might involve trimming the first edge of the data. The rcv_nxt
++ variable and the advertised window are adjusted.
++
++ +) If the incoming segment has data that is above the next
++ sequence number expected (->rcv_nxt), the segment is placed on
++ the ->ooseq queue. This is done by finding the appropriate
++ place in the ->ooseq queue (which is ordered by sequence
++ number) and trim the segment in both ends if needed. An
++ immediate ACK is sent to indicate that we received an
++ out-of-sequence segment.
++
++ +) Finally, we check if the first segment on the ->ooseq queue
++ now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
++ rcv_nxt > ooseq->seqno, we must trim the first edge of the
++ segment on ->ooseq before we adjust rcv_nxt. The data in the
++ segments that are now on sequence are chained onto the
++ incoming segment so that we only need to call the application
++ once.
++ */
++
++ /* First, we check if we must trim the first edge. We have to do
++ this if the sequence number of the incoming segment is less
++ than rcv_nxt, and the sequence number plus the length of the
++ segment is larger than rcv_nxt. */
++ /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
++ if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
++ if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){
++ /* Trimming the first edge is done by pushing the payload
++ pointer in the pbuf downwards. This is somewhat tricky since
++ we do not want to discard the full contents of the pbuf up to
++ the new starting point of the data since we have to keep the
++ TCP header which is present in the first pbuf in the chain.
++
++ What is done is really quite a nasty hack: the first pbuf in
++ the pbuf chain is pointed to by inseg.p. Since we need to be
++ able to deallocate the whole pbuf, we cannot change this
++ inseg.p pointer to point to any of the later pbufs in the
++ chain. Instead, we point the ->payload pointer in the first
++ pbuf to data in one of the later pbufs. We also set the
++ inseg.data pointer to point to the right place. This way, the
++ ->p pointer will still point to the first pbuf, but the
++ ->p->payload pointer will point to data in another pbuf.
++
++ After we are done with adjusting the pbuf pointers we must
++ adjust the ->data pointer in the seg and the segment
++ length.*/
++
++ off = pcb->rcv_nxt - seqno;
++ p = inseg.p;
++ LWIP_ASSERT("inseg.p != NULL", inseg.p);
++ LWIP_ASSERT("insane offset!", (off < 0x7fff));
++ if (inseg.p->len < off) {
++ LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
++ new_tot_len = (u16_t)(inseg.p->tot_len - off);
++ while (p->len < off) {
++ off -= p->len;
++ /* KJM following line changed (with addition of new_tot_len var)
++ to fix bug #9076
++ inseg.p->tot_len -= p->len; */
++ p->tot_len = new_tot_len;
++ p->len = 0;
++ p = p->next;
++ }
++ if(pbuf_header(p, (s16_t)-off)) {
++ /* Do we need to cope with this failing? Assert for now */
++ LWIP_ASSERT("pbuf_header failed", 0);
++ }
++ } else {
++ if(pbuf_header(inseg.p, (s16_t)-off)) {
++ /* Do we need to cope with this failing? Assert for now */
++ LWIP_ASSERT("pbuf_header failed", 0);
++ }
++ }
++ inseg.len -= (u16_t)(pcb->rcv_nxt - seqno);
++ inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
++ }
++ else {
++ if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
++ /* the whole segment is < rcv_nxt */
++ /* must be a duplicate of a packet that has already been correctly handled */
++
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
++ tcp_ack_now(pcb);
++ }
++ }
++
++ /* The sequence number must be within the window (above rcv_nxt
++ and below rcv_nxt + rcv_wnd) in order to be further
++ processed. */
++ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
++ pcb->rcv_nxt + pcb->rcv_wnd - 1)){
++ if (pcb->rcv_nxt == seqno) {
++ /* The incoming segment is the next in sequence. We check if
++ we have to trim the end of the segment and update rcv_nxt
++ and pass the data to the application. */
++ tcplen = TCP_TCPLEN(&inseg);
++
++ if (tcplen > pcb->rcv_wnd) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG,
++ ("tcp_receive: other end overran receive window"
++ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
++ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
++ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
++ /* Must remove the FIN from the header as we're trimming
++ * that byte of sequence-space from the packet */
++ TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN);
++ }
++ /* Adjust length of segment to fit in the window. */
++ inseg.len = pcb->rcv_wnd;
++ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
++ inseg.len -= 1;
++ }
++ pbuf_realloc(inseg.p, inseg.len);
++ tcplen = TCP_TCPLEN(&inseg);
++ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
++ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
++ }
++#if TCP_QUEUE_OOSEQ
++ /* Received in-sequence data, adjust ooseq data if:
++ - FIN has been received or
++ - inseq overlaps with ooseq */
++ if (pcb->ooseq != NULL) {
++ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG,
++ ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
++ /* Received in-order FIN means anything that was received
++ * out of order must now have been received in-order, so
++ * bin the ooseq queue */
++ while (pcb->ooseq != NULL) {
++ struct tcp_seg *old_ooseq = pcb->ooseq;
++ pcb->ooseq = pcb->ooseq->next;
++ tcp_seg_free(old_ooseq);
++ }
++ }
++ else {
++ next = pcb->ooseq;
++ /* Remove all segments on ooseq that are covered by inseg already.
++ * FIN is copied from ooseq to inseg if present. */
++ while (next &&
++ TCP_SEQ_GEQ(seqno + tcplen,
++ next->tcphdr->seqno + next->len)) {
++ /* inseg cannot have FIN here (already processed above) */
++ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
++ (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
++ TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
++ tcplen = TCP_TCPLEN(&inseg);
++ }
++ prev = next;
++ next = next->next;
++ tcp_seg_free(prev);
++ }
++ /* Now trim right side of inseg if it overlaps with the first
++ * segment on ooseq */
++ if (next &&
++ TCP_SEQ_GT(seqno + tcplen,
++ next->tcphdr->seqno)) {
++ /* inseg cannot have FIN here (already processed above) */
++ inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
++ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
++ inseg.len -= 1;
++ }
++ pbuf_realloc(inseg.p, inseg.len);
++ tcplen = TCP_TCPLEN(&inseg);
++ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
++ (seqno + tcplen) == next->tcphdr->seqno);
++ }
++ pcb->ooseq = next;
++ }
++ }
++#endif /* TCP_QUEUE_OOSEQ */
++
++ pcb->rcv_nxt = seqno + tcplen;
++
++ /* Update the receiver's (our) window. */
++ LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
++ pcb->rcv_wnd -= tcplen;
++
++ tcp_update_rcv_ann_wnd(pcb);
++
++ /* If there is data in the segment, we make preparations to
++ pass this up to the application. The ->recv_data variable
++ is used for holding the pbuf that goes to the
++ application. The code for reassembling out-of-sequence data
++ chains its data on this pbuf as well.
++
++ If the segment was a FIN, we set the TF_GOT_FIN flag that will
++ be used to indicate to the application that the remote side has
++ closed its end of the connection. */
++ if (inseg.p->tot_len > 0) {
++ recv_data = inseg.p;
++ /* Since this pbuf now is the responsibility of the
++ application, we delete our reference to it so that we won't
++ (mistakingly) deallocate it. */
++ inseg.p = NULL;
++ }
++ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
++ recv_flags |= TF_GOT_FIN;
++ }
++
++#if TCP_QUEUE_OOSEQ
++ /* We now check if we have segments on the ->ooseq queue that
++ are now in sequence. */
++ while (pcb->ooseq != NULL &&
++ pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
++
++ cseg = pcb->ooseq;
++ seqno = pcb->ooseq->tcphdr->seqno;
++
++ pcb->rcv_nxt += TCP_TCPLEN(cseg);
++ LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
++ pcb->rcv_wnd >= TCP_TCPLEN(cseg));
++ pcb->rcv_wnd -= TCP_TCPLEN(cseg);
++
++ tcp_update_rcv_ann_wnd(pcb);
++
++ if (cseg->p->tot_len > 0) {
++ /* Chain this pbuf onto the pbuf that we will pass to
++ the application. */
++ if (recv_data) {
++ pbuf_cat(recv_data, cseg->p);
++ } else {
++ recv_data = cseg->p;
++ }
++ cseg->p = NULL;
++ }
++ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
++ recv_flags |= TF_GOT_FIN;
++ if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
++ pcb->state = CLOSE_WAIT;
++ }
++ }
++
++ pcb->ooseq = cseg->next;
++ tcp_seg_free(cseg);
++ }
++#endif /* TCP_QUEUE_OOSEQ */
++
++
++ /* Acknowledge the segment(s). */
++ tcp_ack(pcb);
++
++ } else {
++ /* We get here if the incoming segment is out-of-sequence. */
++ tcp_send_empty_ack(pcb);
++#if TCP_QUEUE_OOSEQ
++ /* We queue the segment on the ->ooseq queue. */
++ if (pcb->ooseq == NULL) {
++ pcb->ooseq = tcp_seg_copy(&inseg);
++ } else {
++ /* If the queue is not empty, we walk through the queue and
++ try to find a place where the sequence number of the
++ incoming segment is between the sequence numbers of the
++ previous and the next segment on the ->ooseq queue. That is
++ the place where we put the incoming segment. If needed, we
++ trim the second edges of the previous and the incoming
++ segment so that it will fit into the sequence.
++
++ If the incoming segment has the same sequence number as a
++ segment on the ->ooseq queue, we discard the segment that
++ contains less data. */
++
++ prev = NULL;
++ for(next = pcb->ooseq; next != NULL; next = next->next) {
++ if (seqno == next->tcphdr->seqno) {
++ /* The sequence number of the incoming segment is the
++ same as the sequence number of the segment on
++ ->ooseq. We check the lengths to see which one to
++ discard. */
++ if (inseg.len > next->len) {
++ /* The incoming segment is larger than the old
++ segment. We replace some segments with the new
++ one. */
++ cseg = tcp_seg_copy(&inseg);
++ if (cseg != NULL) {
++ if (prev != NULL) {
++ prev->next = cseg;
++ } else {
++ pcb->ooseq = cseg;
++ }
++ tcp_oos_insert_segment(cseg, next);
++ }
++ break;
++ } else {
++ /* Either the lenghts are the same or the incoming
++ segment was smaller than the old one; in either
++ case, we ditch the incoming segment. */
++ break;
++ }
++ } else {
++ if (prev == NULL) {
++ if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
++ /* The sequence number of the incoming segment is lower
++ than the sequence number of the first segment on the
++ queue. We put the incoming segment first on the
++ queue. */
++ cseg = tcp_seg_copy(&inseg);
++ if (cseg != NULL) {
++ pcb->ooseq = cseg;
++ tcp_oos_insert_segment(cseg, next);
++ }
++ break;
++ }
++ } else {
++ /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
++ TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
++ if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
++ /* The sequence number of the incoming segment is in
++ between the sequence numbers of the previous and
++ the next segment on ->ooseq. We trim trim the previous
++ segment, delete next segments that included in received segment
++ and trim received, if needed. */
++ cseg = tcp_seg_copy(&inseg);
++ if (cseg != NULL) {
++ if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
++ /* We need to trim the prev segment. */
++ prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
++ pbuf_realloc(prev->p, prev->len);
++ }
++ prev->next = cseg;
++ tcp_oos_insert_segment(cseg, next);
++ }
++ break;
++ }
++ }
++ /* If the "next" segment is the last segment on the
++ ooseq queue, we add the incoming segment to the end
++ of the list. */
++ if (next->next == NULL &&
++ TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
++ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
++ /* segment "next" already contains all data */
++ break;
++ }
++ next->next = tcp_seg_copy(&inseg);
++ if (next->next != NULL) {
++ if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
++ /* We need to trim the last segment. */
++ next->len = (u16_t)(seqno - next->tcphdr->seqno);
++ pbuf_realloc(next->p, next->len);
++ }
++ /* check if the remote side overruns our receive window */
++ if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG,
++ ("tcp_receive: other end overran receive window"
++ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
++ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
++ if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
++ /* Must remove the FIN from the header as we're trimming
++ * that byte of sequence-space from the packet */
++ TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN);
++ }
++ /* Adjust length of segment to fit in the window. */
++ next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
++ pbuf_realloc(next->next->p, next->next->len);
++ tcplen = TCP_TCPLEN(next->next);
++ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
++ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
++ }
++ }
++ break;
++ }
++ }
++ prev = next;
++ }
++ }
++#endif /* TCP_QUEUE_OOSEQ */
++
++ }
++ } else {
++ /* The incoming segment is not withing the window. */
++ tcp_send_empty_ack(pcb);
++ }
++ } else {
++ /* Segments with length 0 is taken care of here. Segments that
++ fall out of the window are ACKed. */
++ /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
++ TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
++ if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
++ tcp_ack_now(pcb);
++ }
++ }
++}
++
++/**
++ * Parses the options contained in the incoming segment.
++ *
++ * Called from tcp_listen_input() and tcp_process().
++ * Currently, only the MSS option is supported!
++ *
++ * @param pcb the tcp_pcb for which a segment arrived
++ */
++static void
++tcp_parseopt(struct tcp_pcb *pcb)
++{
++ u16_t c, max_c;
++ u16_t mss;
++ u8_t *opts, opt;
++#if LWIP_TCP_TIMESTAMPS
++ u32_t tsval;
++#endif
++
++ opts = (u8_t *)tcphdr + TCP_HLEN;
++
++ /* Parse the TCP MSS option, if present. */
++ if(TCPH_HDRLEN(tcphdr) > 0x5) {
++ max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
++ for (c = 0; c < max_c; ) {
++ opt = opts[c];
++ switch (opt) {
++ case 0x00:
++ /* End of options. */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
++ return;
++ case 0x01:
++ /* NOP option. */
++ ++c;
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
++ break;
++ case 0x02:
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
++ if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
++ /* Bad length */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
++ return;
++ }
++ /* An MSS option with the right option length. */
++ mss = (opts[c + 2] << 8) | opts[c + 3];
++ /* Limit the mss to the configured TCP_MSS and prevent division by zero */
++ pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
++ /* Advance to next option */
++ c += 0x04;
++ break;
++#if LWIP_TCP_TIMESTAMPS
++ case 0x08:
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
++ if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
++ /* Bad length */
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
++ return;
++ }
++ /* TCP timestamp option with valid length */
++ tsval = (opts[c+2]) | (opts[c+3] << 8) |
++ (opts[c+4] << 16) | (opts[c+5] << 24);
++ if (flags & TCP_SYN) {
++ pcb->ts_recent = ntohl(tsval);
++ pcb->flags |= TF_TIMESTAMP;
++ } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
++ pcb->ts_recent = ntohl(tsval);
++ }
++ /* Advance to next option */
++ c += 0x0A;
++ break;
++#endif
++ default:
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
++ if (opts[c + 1] == 0) {
++ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
++ /* If the length field is zero, the options are malformed
++ and we don't process them further. */
++ return;
++ }
++ /* All other options have a length field, so that we easily
++ can skip past them. */
++ c += opts[c + 1];
++ }
++ }
++ }
++}
++
++#endif /* LWIP_TCP */
--- /dev/null
--- /dev/null
++/**
++ * @file
++ * Transmission Control Protocol, outgoing traffic
++ *
++ * The output functions of TCP.
++ *
++ */
++
++/*
++ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without modification,
++ * are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright notice,
++ * this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright notice,
++ * this list of conditions and the following disclaimer in the documentation
++ * and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote products
++ * derived from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
++ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
++ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
++ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
++ * OF SUCH DAMAGE.
++ *
++ * This file is part of the lwIP TCP/IP stack.
++ *
++ * Author: Adam Dunkels <adam@sics.se>
++ *
++ */
++
++#include "lwip/opt.h"
++
++#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
++
++#include "lwip/tcp_impl.h"
++#include "lwip/def.h"
++#include "lwip/mem.h"
++#include "lwip/memp.h"
++#include "lwip/sys.h"
++#include "lwip/ip_addr.h"
++#include "lwip/netif.h"
++#include "lwip/inet_chksum.h"
++#include "lwip/stats.h"
++#include "lwip/snmp.h"
++
++#include <string.h>
++
++/* Define some copy-macros for checksum-on-copy so that the code looks
++ nicer by preventing too many ifdef's. */
++#if TCP_CHECKSUM_ON_COPY
++#define TCP_DATA_COPY(dst, src, len, seg) do { \
++ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
++ len, &seg->chksum, &seg->chksum_swapped); \
++ seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
++#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \
++ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
++#else /* TCP_CHECKSUM_ON_COPY*/
++#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len)
++#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
++#endif /* TCP_CHECKSUM_ON_COPY*/
++
++/** Define this to 1 for an extra check that the output checksum is valid
++ * (usefule when the checksum is generated by the application, not the stack) */
++#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
++#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0
++#endif
++
++/* Forward declarations.*/
++static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
++
++/** Allocate a pbuf and create a tcphdr at p->payload, used for output
++ * functions other than the default tcp_output -> tcp_output_segment
++ * (e.g. tcp_send_empty_ack, etc.)
++ *
++ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
++ * @param optlen length of header-options
++ * @param datalen length of tcp data to reserve in pbuf
++ * @param seqno_be seqno in network byte order (big-endian)
++ * @return pbuf with p->payload being the tcp_hdr
++ */
++static struct pbuf *
++tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
++ u32_t seqno_be /* already in network byte order */)
++{
++ struct tcp_hdr *tcphdr;
++ struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
++ if (p != NULL) {
++ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
++ (p->len >= TCP_HLEN + optlen));
++ tcphdr = (struct tcp_hdr *)p->payload;
++ tcphdr->src = htons(pcb->local_port);
++ tcphdr->dest = htons(pcb->remote_port);
++ tcphdr->seqno = seqno_be;
++ tcphdr->ackno = htonl(pcb->rcv_nxt);
++ TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
++ tcphdr->wnd = htons(pcb->rcv_ann_wnd);
++ tcphdr->chksum = 0;
++ tcphdr->urgp = 0;
++
++ /* If we're sending a packet, update the announced right window edge */
++ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
++ }
++ return p;
++}
++
++/**
++ * Called by tcp_close() to send a segment including FIN flag but not data.
++ *
++ * @param pcb the tcp_pcb over which to send a segment
++ * @return ERR_OK if sent, another err_t otherwise
++ */
++err_t
++tcp_send_fin(struct tcp_pcb *pcb)
++{
++ /* first, try to add the fin to the last unsent segment */
++ if (pcb->unsent != NULL) {
++ struct tcp_seg *last_unsent;
++ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
++ last_unsent = last_unsent->next);
++
++ if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
++ /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
++ TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
++ pcb->flags |= TF_FIN;
++ return ERR_OK;
++ }
++ }
++ /* no data, no length, flags, copy=1, no optdata */
++ return tcp_enqueue_flags(pcb, TCP_FIN);
++}
++
++/**
++ * Create a TCP segment with prefilled header.
++ *
++ * Called by tcp_write and tcp_enqueue_flags.
++ *
++ * @param pcb Protocol control block for the TCP connection.
++ * @param p pbuf that is used to hold the TCP header.
++ * @param flags TCP flags for header.
++ * @param seqno TCP sequence number of this packet
++ * @param optflags options to include in TCP header
++ * @return a new tcp_seg pointing to p, or NULL.
++ * The TCP header is filled in except ackno and wnd.
++ * p is freed on failure.
++ */
++static struct tcp_seg *
++tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags)
++{
++ struct tcp_seg *seg;
++ u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
++
++ if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
++ pbuf_free(p);
++ return NULL;
++ }
++ seg->flags = optflags;
++ seg->next = NULL;
++ seg->p = p;
++ seg->len = p->tot_len - optlen;
++#if TCP_OVERSIZE_DBGCHECK
++ seg->oversize_left = 0;
++#endif /* TCP_OVERSIZE_DBGCHECK */
++#if TCP_CHECKSUM_ON_COPY
++ seg->chksum = 0;
++ seg->chksum_swapped = 0;
++ /* check optflags */
++ LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
++ (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
++#endif /* TCP_CHECKSUM_ON_COPY */
++
++ /* build TCP header */
++ if (pbuf_header(p, TCP_HLEN)) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
++ TCP_STATS_INC(tcp.err);
++ tcp_seg_free(seg);
++ return NULL;
++ }
++ seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
++ seg->tcphdr->src = htons(pcb->local_port);
++ seg->tcphdr->dest = htons(pcb->remote_port);
++ seg->tcphdr->seqno = htonl(seqno);
++ /* ackno is set in tcp_output */
++ TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
++ /* wnd and chksum are set in tcp_output */
++ seg->tcphdr->urgp = 0;
++ return seg;
++}
++
++/**
++ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
++ *
++ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
++ * there may be extra bytes available at the end.
++ *
++ * @param layer flag to define header size.
++ * @param length size of the pbuf's payload.
++ * @param max_length maximum usable size of payload+oversize.
++ * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
++ * @param pcb The TCP connection that willo enqueue the pbuf.
++ * @param apiflags API flags given to tcp_write.
++ * @param first_seg true when this pbuf will be used in the first enqueued segment.
++ * @param
++ */
++#if TCP_OVERSIZE
++static struct pbuf *
++tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
++ u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,
++ u8_t first_seg)
++{
++ struct pbuf *p;
++ u16_t alloc = length;
++
++#if LWIP_NETIF_TX_SINGLE_PBUF
++ LWIP_UNUSED_ARG(max_length);
++ LWIP_UNUSED_ARG(pcb);
++ LWIP_UNUSED_ARG(apiflags);
++ LWIP_UNUSED_ARG(first_seg);
++ /* always create MSS-sized pbufs */
++ alloc = TCP_MSS;
++#else /* LWIP_NETIF_TX_SINGLE_PBUF */
++ if (length < max_length) {
++ /* Should we allocate an oversized pbuf, or just the minimum
++ * length required? If tcp_write is going to be called again
++ * before this segment is transmitted, we want the oversized
++ * buffer. If the segment will be transmitted immediately, we can
++ * save memory by allocating only length. We use a simple
++ * heuristic based on the following information:
++ *
++ * Did the user set TCP_WRITE_FLAG_MORE?
++ *
++ * Will the Nagle algorithm defer transmission of this segment?
++ */
++ if ((apiflags & TCP_WRITE_FLAG_MORE) ||
++ (!(pcb->flags & TF_NODELAY) &&
++ (!first_seg ||
++ pcb->unsent != NULL ||
++ pcb->unacked != NULL))) {
++ alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
++ }
++ }
++#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
++ p = pbuf_alloc(layer, alloc, PBUF_RAM);
++ if (p == NULL) {
++ return NULL;
++ }
++ LWIP_ASSERT("need unchained pbuf", p->next == NULL);
++ *oversize = p->len - length;
++ /* trim p->len to the currently used size */
++ p->len = p->tot_len = length;
++ return p;
++}
++#else /* TCP_OVERSIZE */
++#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
++#endif /* TCP_OVERSIZE */
++
++#if TCP_CHECKSUM_ON_COPY
++/** Add a checksum of newly added data to the segment */
++static void
++tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
++ u8_t *seg_chksum_swapped)
++{
++ u32_t helper;
++ /* add chksum to old chksum and fold to u16_t */
++ helper = chksum + *seg_chksum;
++ chksum = FOLD_U32T(helper);
++ if ((len & 1) != 0) {
++ *seg_chksum_swapped = 1 - *seg_chksum_swapped;
++ chksum = SWAP_BYTES_IN_WORD(chksum);
++ }
++ *seg_chksum = chksum;
++}
++#endif /* TCP_CHECKSUM_ON_COPY */
++
++/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
++ *
++ * @param pcb the tcp pcb to check for
++ * @param len length of data to send (checked agains snd_buf)
++ * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
++ */
++static err_t
++tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
++{
++ /* connection is in invalid state for data transmission? */
++ if ((pcb->state != ESTABLISHED) &&
++ (pcb->state != CLOSE_WAIT) &&
++ (pcb->state != SYN_SENT) &&
++ (pcb->state != SYN_RCVD)) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
++ return ERR_CONN;
++ } else if (len == 0) {
++ return ERR_OK;
++ }
++
++ /* fail on too much data */
++ if (len > pcb->snd_buf) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
++ len, pcb->snd_buf));
++ pcb->flags |= TF_NAGLEMEMERR;
++ return ERR_MEM;
++ }
++
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
++
++ /* If total number of pbufs on the unsent/unacked queues exceeds the
++ * configured maximum, return an error */
++ /* check for configured max queuelen and possible overflow */
++ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
++ pcb->snd_queuelen, TCP_SND_QUEUELEN));
++ TCP_STATS_INC(tcp.memerr);
++ pcb->flags |= TF_NAGLEMEMERR;
++ return ERR_MEM;
++ }
++ if (pcb->snd_queuelen != 0) {
++ LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
++ pcb->unacked != NULL || pcb->unsent != NULL);
++ } else {
++ LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
++ pcb->unacked == NULL && pcb->unsent == NULL);
++ }
++ return ERR_OK;
++}
++
++/**
++ * Write data for sending (but does not send it immediately).
++ *
++ * It waits in the expectation of more data being sent soon (as
++ * it can send them more efficiently by combining them together).
++ * To prompt the system to send data now, call tcp_output() after
++ * calling tcp_write().
++ *
++ * @param pcb Protocol control block for the TCP connection to enqueue data for.
++ * @param arg Pointer to the data to be enqueued for sending.
++ * @param len Data length in bytes
++ * @param apiflags combination of following flags :
++ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
++ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
++ * @return ERR_OK if enqueued, another err_t on error
++ */
++err_t
++tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
++{
++ struct pbuf *concat_p = NULL;
++ struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
++ u16_t pos = 0; /* position in 'arg' data */
++ u16_t queuelen;
++ u8_t optlen = 0;
++ u8_t optflags = 0;
++#if TCP_OVERSIZE
++ u16_t oversize = 0;
++ u16_t oversize_used = 0;
++#endif /* TCP_OVERSIZE */
++#if TCP_CHECKSUM_ON_COPY
++ u16_t concat_chksum = 0;
++ u8_t concat_chksum_swapped = 0;
++ u16_t concat_chksummed = 0;
++#endif /* TCP_CHECKSUM_ON_COPY */
++ err_t err;
++
++#if LWIP_NETIF_TX_SINGLE_PBUF
++ /* Always copy to try to create single pbufs for TX */
++ apiflags |= TCP_WRITE_FLAG_COPY;
++#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
++
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
++ (void *)pcb, arg, len, (u16_t)apiflags));
++ LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
++ arg != NULL, return ERR_ARG;);
++
++ err = tcp_write_checks(pcb, len);
++ if (err != ERR_OK) {
++ return err;
++ }
++ queuelen = pcb->snd_queuelen;
++
++#if LWIP_TCP_TIMESTAMPS
++ if ((pcb->flags & TF_TIMESTAMP)) {
++ optflags = TF_SEG_OPTS_TS;
++ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
++ }
++#endif /* LWIP_TCP_TIMESTAMPS */
++
++
++ /*
++ * TCP segmentation is done in three phases with increasing complexity:
++ *
++ * 1. Copy data directly into an oversized pbuf.
++ * 2. Chain a new pbuf to the end of pcb->unsent.
++ * 3. Create new segments.
++ *
++ * We may run out of memory at any point. In that case we must
++ * return ERR_MEM and not change anything in pcb. Therefore, all
++ * changes are recorded in local variables and committed at the end
++ * of the function. Some pcb fields are maintained in local copies:
++ *
++ * queuelen = pcb->snd_queuelen
++ * oversize = pcb->unsent_oversize
++ *
++ * These variables are set consistently by the phases:
++ *
++ * seg points to the last segment tampered with.
++ *
++ * pos records progress as data is segmented.
++ */
++
++ /* Find the tail of the unsent queue. */
++ if (pcb->unsent != NULL) {
++ u16_t space;
++ u16_t unsent_optlen;
++
++ /* @todo: this could be sped up by keeping last_unsent in the pcb */
++ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
++ last_unsent = last_unsent->next);
++
++ /* Usable space at the end of the last unsent segment */
++ unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
++ space = pcb->mss - (last_unsent->len + unsent_optlen);
++
++ /*
++ * Phase 1: Copy data directly into an oversized pbuf.
++ *
++ * The number of bytes copied is recorded in the oversize_used
++ * variable. The actual copying is done at the bottom of the
++ * function.
++ */
++#if TCP_OVERSIZE
++#if TCP_OVERSIZE_DBGCHECK
++ /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
++ LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
++ pcb->unsent_oversize == last_unsent->oversize_left);
++#endif /* TCP_OVERSIZE_DBGCHECK */
++ oversize = pcb->unsent_oversize;
++ if (oversize > 0) {
++ LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
++ seg = last_unsent;
++ oversize_used = oversize < len ? oversize : len;
++ pos += oversize_used;
++ oversize -= oversize_used;
++ space -= oversize_used;
++ }
++ /* now we are either finished or oversize is zero */
++ LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
++#endif /* TCP_OVERSIZE */
++
++ /*
++ * Phase 2: Chain a new pbuf to the end of pcb->unsent.
++ *
++ * We don't extend segments containing SYN/FIN flags or options
++ * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
++ * the end.
++ */
++ if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
++ u16_t seglen = space < len - pos ? space : len - pos;
++ seg = last_unsent;
++
++ /* Create a pbuf with a copy or reference to seglen bytes. We
++ * can use PBUF_RAW here since the data appears in the middle of
++ * a segment. A header will never be prepended. */
++ if (apiflags & TCP_WRITE_FLAG_COPY) {
++ /* Data is copied */
++ if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
++ ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
++ seglen));
++ goto memerr;
++ }
++#if TCP_OVERSIZE_DBGCHECK
++ last_unsent->oversize_left = oversize;
++#endif /* TCP_OVERSIZE_DBGCHECK */
++ TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
++#if TCP_CHECKSUM_ON_COPY
++ concat_chksummed += seglen;
++#endif /* TCP_CHECKSUM_ON_COPY */
++ } else {
++ /* Data is not copied */
++ if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
++ ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
++ goto memerr;
++ }
++#if TCP_CHECKSUM_ON_COPY
++ /* calculate the checksum of nocopy-data */
++ tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen,
++ &concat_chksum, &concat_chksum_swapped);
++ concat_chksummed += seglen;
++#endif /* TCP_CHECKSUM_ON_COPY */
++ /* reference the non-volatile payload data */
++ concat_p->payload = (u8_t*)arg + pos;
++ }
++
++ pos += seglen;
++ queuelen += pbuf_clen(concat_p);
++ }
++ } else {
++#if TCP_OVERSIZE
++ LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
++ pcb->unsent_oversize == 0);
++#endif /* TCP_OVERSIZE */
++ }
++
++ /*
++ * Phase 3: Create new segments.
++ *
++ * The new segments are chained together in the local 'queue'
++ * variable, ready to be appended to pcb->unsent.
++ */
++ while (pos < len) {
++ struct pbuf *p;
++ u16_t left = len - pos;
++ u16_t max_len = pcb->mss - optlen;
++ u16_t seglen = left > max_len ? max_len : left;
++#if TCP_CHECKSUM_ON_COPY
++ u16_t chksum = 0;
++ u8_t chksum_swapped = 0;
++#endif /* TCP_CHECKSUM_ON_COPY */
++
++ if (apiflags & TCP_WRITE_FLAG_COPY) {
++ /* If copy is set, memory should be allocated and data copied
++ * into pbuf */
++ if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
++ goto memerr;
++ }
++ LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
++ (p->len >= seglen));
++ TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
++ } else {
++ /* Copy is not set: First allocate a pbuf for holding the data.
++ * Since the referenced data is available at least until it is
++ * sent out on the link (as it has to be ACKed by the remote
++ * party) we can safely use PBUF_ROM instead of PBUF_REF here.
++ */
++ struct pbuf *p2;
++#if TCP_OVERSIZE
++ LWIP_ASSERT("oversize == 0", oversize == 0);
++#endif /* TCP_OVERSIZE */
++ if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
++ goto memerr;
++ }
++#if TCP_CHECKSUM_ON_COPY
++ /* calculate the checksum of nocopy-data */
++ chksum = ~inet_chksum((u8_t*)arg + pos, seglen);
++#endif /* TCP_CHECKSUM_ON_COPY */
++ /* reference the non-volatile payload data */
++ p2->payload = (u8_t*)arg + pos;
++
++ /* Second, allocate a pbuf for the headers. */
++ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
++ /* If allocation fails, we have to deallocate the data pbuf as
++ * well. */
++ pbuf_free(p2);
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
++ goto memerr;
++ }
++ /* Concatenate the headers and data pbufs together. */
++ pbuf_cat(p/*header*/, p2/*data*/);
++ }
++
++ queuelen += pbuf_clen(p);
++
++ /* Now that there are more segments queued, we check again if the
++ * length of the queue exceeds the configured maximum or
++ * overflows. */
++ if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
++ pbuf_free(p);
++ goto memerr;
++ }
++
++ if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
++ goto memerr;
++ }
++#if TCP_OVERSIZE_DBGCHECK
++ seg->oversize_left = oversize;
++#endif /* TCP_OVERSIZE_DBGCHECK */
++#if TCP_CHECKSUM_ON_COPY
++ seg->chksum = chksum;
++ seg->chksum_swapped = chksum_swapped;
++ seg->flags |= TF_SEG_DATA_CHECKSUMMED;
++#endif /* TCP_CHECKSUM_ON_COPY */
++
++ /* first segment of to-be-queued data? */
++ if (queue == NULL) {
++ queue = seg;
++ } else {
++ /* Attach the segment to the end of the queued segments */
++ LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
++ prev_seg->next = seg;
++ }
++ /* remember last segment of to-be-queued data for next iteration */
++ prev_seg = seg;
++
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
++ ntohl(seg->tcphdr->seqno),
++ ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
++
++ pos += seglen;
++ }
++
++ /*
++ * All three segmentation phases were successful. We can commit the
++ * transaction.
++ */
++
++ /*
++ * Phase 1: If data has been added to the preallocated tail of
++ * last_unsent, we update the length fields of the pbuf chain.
++ */
++#if TCP_OVERSIZE
++ if (oversize_used > 0) {
++ struct pbuf *p;
++ /* Bump tot_len of whole chain, len of tail */
++ for (p = last_unsent->p; p; p = p->next) {
++ p->tot_len += oversize_used;
++ if (p->next == NULL) {
++ TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
++ p->len += oversize_used;
++ }
++ }
++ last_unsent->len += oversize_used;
++#if TCP_OVERSIZE_DBGCHECK
++ last_unsent->oversize_left -= oversize_used;
++#endif /* TCP_OVERSIZE_DBGCHECK */
++ }
++ pcb->unsent_oversize = oversize;
++#endif /* TCP_OVERSIZE */
++
++ /*
++ * Phase 2: concat_p can be concatenated onto last_unsent->p
++ */
++ if (concat_p != NULL) {
++ LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
++ (last_unsent != NULL));
++ pbuf_cat(last_unsent->p, concat_p);
++ last_unsent->len += concat_p->tot_len;
++#if TCP_CHECKSUM_ON_COPY
++ if (concat_chksummed) {
++ tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
++ &last_unsent->chksum_swapped);
++ last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
++ }
++#endif /* TCP_CHECKSUM_ON_COPY */
++ }
++
++ /*
++ * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
++ * is harmless
++ */
++ if (last_unsent == NULL) {
++ pcb->unsent = queue;
++ } else {
++ last_unsent->next = queue;
++ }
++
++ /*
++ * Finally update the pcb state.
++ */
++ pcb->snd_lbb += len;
++ pcb->snd_buf -= len;
++ pcb->snd_queuelen = queuelen;
++
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
++ pcb->snd_queuelen));
++ if (pcb->snd_queuelen != 0) {
++ LWIP_ASSERT("tcp_write: valid queue length",
++ pcb->unacked != NULL || pcb->unsent != NULL);
++ }
++
++ /* Set the PSH flag in the last segment that we enqueued. */
++ if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
++ TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
++ }
++
++ return ERR_OK;
++memerr:
++ pcb->flags |= TF_NAGLEMEMERR;
++ TCP_STATS_INC(tcp.memerr);
++
++ if (concat_p != NULL) {
++ pbuf_free(concat_p);
++ }
++ if (queue != NULL) {
++ tcp_segs_free(queue);
++ }
++ if (pcb->snd_queuelen != 0) {
++ LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
++ pcb->unsent != NULL);
++ }
++ LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
++ return ERR_MEM;
++}
++
++/**
++ * Enqueue TCP options for transmission.
++ *
++ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
++ *
++ * @param pcb Protocol control block for the TCP connection.
++ * @param flags TCP header flags to set in the outgoing segment.
++ * @param optdata pointer to TCP options, or NULL.
++ * @param optlen length of TCP options in bytes.
++ */
++err_t
++tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
++{
++ struct pbuf *p;
++ struct tcp_seg *seg;
++ u8_t optflags = 0;
++ u8_t optlen = 0;
++
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
++
++ LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
++ (flags & (TCP_SYN | TCP_FIN)) != 0);
++
++ /* check for configured max queuelen and possible overflow */
++ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
++ pcb->snd_queuelen, TCP_SND_QUEUELEN));
++ TCP_STATS_INC(tcp.memerr);
++ pcb->flags |= TF_NAGLEMEMERR;
++ return ERR_MEM;
++ }
++
++ if (flags & TCP_SYN) {
++ optflags = TF_SEG_OPTS_MSS;
++ }
++#if LWIP_TCP_TIMESTAMPS
++ if ((pcb->flags & TF_TIMESTAMP)) {
++ optflags |= TF_SEG_OPTS_TS;
++ }
++#endif /* LWIP_TCP_TIMESTAMPS */
++ optlen = LWIP_TCP_OPT_LENGTH(optflags);
++
++ /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
++ * We need one available snd_buf byte to do that.
++ * This means we can't send FIN while snd_buf==0. A better fix would be to
++ * not include SYN and FIN sequence numbers in the snd_buf count. */
++ if (pcb->snd_buf == 0) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
++ TCP_STATS_INC(tcp.memerr);
++ return ERR_MEM;
++ }
++
++ /* Allocate pbuf with room for TCP header + options */
++ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
++ pcb->flags |= TF_NAGLEMEMERR;
++ TCP_STATS_INC(tcp.memerr);
++ return ERR_MEM;
++ }
++ LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
++ (p->len >= optlen));
++
++ /* Allocate memory for tcp_seg, and fill in fields. */
++ if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
++ pcb->flags |= TF_NAGLEMEMERR;
++ TCP_STATS_INC(tcp.memerr);
++ return ERR_MEM;
++ }
++ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
++ LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
++
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
++ ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
++ ntohl(seg->tcphdr->seqno),
++ ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
++ (u16_t)flags));
++
++ /* Now append seg to pcb->unsent queue */
++ if (pcb->unsent == NULL) {
++ pcb->unsent = seg;
++ } else {
++ struct tcp_seg *useg;
++ for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
++ useg->next = seg;
++ }
++#if TCP_OVERSIZE
++ /* The new unsent tail has no space */
++ pcb->unsent_oversize = 0;
++#endif /* TCP_OVERSIZE */
++
++ /* SYN and FIN bump the sequence number */
++ if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
++ pcb->snd_lbb++;
++ /* optlen does not influence snd_buf */
++ pcb->snd_buf--;
++ }
++ if (flags & TCP_FIN) {
++ pcb->flags |= TF_FIN;
++ }
++
++ /* update number of segments on the queues */
++ pcb->snd_queuelen += pbuf_clen(seg->p);
++ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
++ if (pcb->snd_queuelen != 0) {
++ LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
++ pcb->unacked != NULL || pcb->unsent != NULL);
++ }
++
++ return ERR_OK;
++}
++
++
++#if LWIP_TCP_TIMESTAMPS
++/* Build a timestamp option (12 bytes long) at the specified options pointer)
++ *
++ * @param pcb tcp_pcb
++ * @param opts option pointer where to store the timestamp option
++ */
++static void
++tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
++{
++ /* Pad with two NOP options to make everything nicely aligned */
++ opts[0] = PP_HTONL(0x0101080A);
++ opts[1] = htonl(sys_now());
++ opts[2] = htonl(pcb->ts_recent);
++}
++#endif
++
++/** Send an ACK without data.
++ *
++ * @param pcb Protocol control block for the TCP connection to send the ACK
++ */
++err_t
++tcp_send_empty_ack(struct tcp_pcb *pcb)
++{
++ struct pbuf *p;
++ struct tcp_hdr *tcphdr;
++ u8_t optlen = 0;
++
++#if LWIP_TCP_TIMESTAMPS
++ if (pcb->flags & TF_TIMESTAMP) {
++ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
++ }
++#endif
++
++ p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
++ if (p == NULL) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
++ return ERR_BUF;
++ }
++ tcphdr = (struct tcp_hdr *)p->payload;
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
++ ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
++ /* remove ACK flags from the PCB, as we send an empty ACK now */
++ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
++
++ /* NB. MSS option is only sent on SYNs, so ignore it here */
++#if LWIP_TCP_TIMESTAMPS
++ pcb->ts_lastacksent = pcb->rcv_nxt;
++
++ if (pcb->flags & TF_TIMESTAMP) {
++ tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
++ }
++#endif
++
++#if CHECKSUM_GEN_TCP
++ tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
++ IP_PROTO_TCP, p->tot_len);
++#endif
++#if LWIP_NETIF_HWADDRHINT
++ ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
++ IP_PROTO_TCP, &(pcb->addr_hint));
++#else /* LWIP_NETIF_HWADDRHINT*/
++ ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
++ IP_PROTO_TCP);
++#endif /* LWIP_NETIF_HWADDRHINT*/
++ pbuf_free(p);
++
++ return ERR_OK;
++}
++
++/**
++ * Find out what we can send and send it
++ *
++ * @param pcb Protocol control block for the TCP connection to send data
++ * @return ERR_OK if data has been sent or nothing to send
++ * another err_t on error
++ */
++err_t
++tcp_output(struct tcp_pcb *pcb)
++{
++ struct tcp_seg *seg, *useg;
++ u32_t wnd, snd_nxt;
++#if TCP_CWND_DEBUG
++ s16_t i = 0;
++#endif /* TCP_CWND_DEBUG */
++
++ /* First, check if we are invoked by the TCP input processing
++ code. If so, we do not output anything. Instead, we rely on the
++ input processing code to call us when input processing is done
++ with. */
++ if (tcp_input_pcb == pcb) {
++ return ERR_OK;
++ }
++
++ wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
++
++ seg = pcb->unsent;
++
++ /* If the TF_ACK_NOW flag is set and no data will be sent (either
++ * because the ->unsent queue is empty or because the window does
++ * not allow it), construct an empty ACK segment and send it.
++ *
++ * If data is to be sent, we will just piggyback the ACK (see below).
++ */
++ if (pcb->flags & TF_ACK_NOW &&
++ (seg == NULL ||
++ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
++ return tcp_send_empty_ack(pcb);
++ }
++
++ /* useg should point to last segment on unacked queue */
++ useg = pcb->unacked;
++ if (useg != NULL) {
++ for (; useg->next != NULL; useg = useg->next);
++ }
++
++#if TCP_OUTPUT_DEBUG
++ if (seg == NULL) {
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
++ (void*)pcb->unsent));
++ }
++#endif /* TCP_OUTPUT_DEBUG */
++#if TCP_CWND_DEBUG
++ if (seg == NULL) {
++ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
++ ", cwnd %"U16_F", wnd %"U32_F
++ ", seg == NULL, ack %"U32_F"\n",
++ pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
++ } else {
++ LWIP_DEBUGF(TCP_CWND_DEBUG,
++ ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
++ ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
++ pcb->snd_wnd, pcb->cwnd, wnd,
++ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
++ ntohl(seg->tcphdr->seqno), pcb->lastack));
++ }
++#endif /* TCP_CWND_DEBUG */
++ /* data available and window allows it to be sent? */
++ while (seg != NULL &&
++ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
++ LWIP_ASSERT("RST not expected here!",
++ (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
++ /* Stop sending if the nagle algorithm would prevent it
++ * Don't stop:
++ * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
++ * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
++ * either seg->next != NULL or pcb->unacked == NULL;
++ * RST is no sent using tcp_write/tcp_output.
++ */
++ if((tcp_do_output_nagle(pcb) == 0) &&
++ ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
++ break;
++ }
++#if TCP_CWND_DEBUG
++ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
++ pcb->snd_wnd, pcb->cwnd, wnd,
++ ntohl(seg->tcphdr->seqno) + seg->len -
++ pcb->lastack,
++ ntohl(seg->tcphdr->seqno), pcb->lastack, i));
++ ++i;
++#endif /* TCP_CWND_DEBUG */
++
++ pcb->unsent = seg->next;
++
++ if (pcb->state != SYN_SENT) {
++ TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
++ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
++ }
++
++ tcp_output_segment(seg, pcb);
++ snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
++ if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
++ pcb->snd_nxt = snd_nxt;
++ }
++ /* put segment on unacknowledged list if length > 0 */
++ if (TCP_TCPLEN(seg) > 0) {
++ seg->next = NULL;
++ /* unacked list is empty? */
++ if (pcb->unacked == NULL) {
++ pcb->unacked = seg;
++ useg = seg;
++ /* unacked list is not empty? */
++ } else {
++ /* In the case of fast retransmit, the packet should not go to the tail
++ * of the unacked queue, but rather somewhere before it. We need to check for
++ * this case. -STJ Jul 27, 2004 */
++ if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
++ /* add segment to before tail of unacked list, keeping the list sorted */
++ struct tcp_seg **cur_seg = &(pcb->unacked);
++ while (*cur_seg &&
++ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
++ cur_seg = &((*cur_seg)->next );
++ }
++ seg->next = (*cur_seg);
++ (*cur_seg) = seg;
++ } else {
++ /* add segment to tail of unacked list */
++ useg->next = seg;
++ useg = useg->next;
++ }
++ }
++ /* do not queue empty segments on the unacked list */
++ } else {
++ tcp_seg_free(seg);
++ }
++ seg = pcb->unsent;
++ }
++#if TCP_OVERSIZE
++ if (pcb->unsent == NULL) {
++ /* last unsent has been removed, reset unsent_oversize */
++ pcb->unsent_oversize = 0;
++ }
++#endif /* TCP_OVERSIZE */
++
++ if (seg != NULL && pcb->persist_backoff == 0 &&
++ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) {
++ /* prepare for persist timer */
++ pcb->persist_cnt = 0;
++ pcb->persist_backoff = 1;
++ }
++
++ pcb->flags &= ~TF_NAGLEMEMERR;
++ return ERR_OK;
++}
++
++/**
++ * Called by tcp_output() to actually send a TCP segment over IP.
++ *
++ * @param seg the tcp_seg to send
++ * @param pcb the tcp_pcb for the TCP connection used to send the segment
++ */
++static void
++tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
++{
++ u16_t len;
++ struct netif *netif;
++ u32_t *opts;
++
++ /** @bug Exclude retransmitted segments from this count. */
++ snmp_inc_tcpoutsegs();
++
++ /* The TCP header has already been constructed, but the ackno and
++ wnd fields remain. */
++ seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
++
++ /* advertise our receive window size in this TCP segment */
++ seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
++
++ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
++
++ /* Add any requested options. NB MSS option is only set on SYN
++ packets, so ignore it here */
++ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
++ opts = (u32_t *)(void *)(seg->tcphdr + 1);
++ if (seg->flags & TF_SEG_OPTS_MSS) {
++ TCP_BUILD_MSS_OPTION(*opts);
++ opts += 1;
++ }
++#if LWIP_TCP_TIMESTAMPS
++ pcb->ts_lastacksent = pcb->rcv_nxt;
++
++ if (seg->flags & TF_SEG_OPTS_TS) {
++ tcp_build_timestamp_option(pcb, opts);
++ opts += 3;
++ }
++#endif
++
++ /* Set retransmission timer running if it is not currently enabled
++ This must be set before checking the route. */
++ if (pcb->rtime == -1) {
++ pcb->rtime = 0;
++ }
++
++ /* If we don't have a local IP address, we get one by
++ calling ip_route(). */
++ if (ip_addr_isany(&(pcb->local_ip))) {
++ netif = ip_route(&(pcb->remote_ip));
++ if (netif == NULL) {
++ return;
++ }
++ ip_addr_copy(pcb->local_ip, netif->ip_addr);
++ }
++
++ if (pcb->rttest == 0) {
++ pcb->rttest = tcp_ticks;
++ pcb->rtseq = ntohl(seg->tcphdr->seqno);
++
++ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
++ }
++ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
++ htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
++ seg->len));
++
++ len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
++
++ seg->p->len -= len;
++ seg->p->tot_len -= len;
++
++ seg->p->payload = seg->tcphdr;
++
++ seg->tcphdr->chksum = 0;
++#if CHECKSUM_GEN_TCP
++#if TCP_CHECKSUM_ON_COPY
++ {
++ u32_t acc;
++#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
++ u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
++ &(pcb->remote_ip),
++ IP_PROTO_TCP, seg->p->tot_len);
++#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
++ if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
++ LWIP_ASSERT("data included but not checksummed",
++ seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
++ }
++
++ /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
++ acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip),
++ &(pcb->remote_ip),
++ IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4);
++ /* add payload checksum */
++ if (seg->chksum_swapped) {
++ seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
++ seg->chksum_swapped = 0;
++ }
++ acc += (u16_t)~(seg->chksum);
++ seg->tcphdr->chksum = FOLD_U32T(acc);
++#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
++ if (chksum_slow != seg->tcphdr->chksum) {
++ LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
++ ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
++ seg->tcphdr->chksum, chksum_slow));
++ seg->tcphdr->chksum = chksum_slow;
++ }
++#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
++ }
++#else /* TCP_CHECKSUM_ON_COPY */
++ seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
++ &(pcb->remote_ip),
++ IP_PROTO_TCP, seg->p->tot_len);
++#endif /* TCP_CHECKSUM_ON_COPY */
++#endif /* CHECKSUM_GEN_TCP */
++ TCP_STATS_INC(tcp.xmit);
++
++#if LWIP_NETIF_HWADDRHINT
++ ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
++ IP_PROTO_TCP, &(pcb->addr_hint));
++#else /* LWIP_NETIF_HWADDRHINT*/
++ ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
++ IP_PROTO_TCP);
++#endif /* LWIP_NETIF_HWADDRHINT*/
++}
++
++/**
++ * Send a TCP RESET packet (empty segment with RST flag set) either to
++ * abort a connection or to show that there is no matching local connection
++ * for a received segment.
++ *
++ * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
++ * matching local pcb was found), tcp_listen_input() (if incoming segment
++ * has ACK flag set) and tcp_process() (received segment in the wrong state)
++ *
++ * Since a RST segment is in most cases not sent for an active connection,
++ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
++ * most other segment output functions.
++ *
++ * @param seqno the sequence number to use for the outgoing segment
++ * @param ackno the acknowledge number to use for the outgoing segment
++ * @param local_ip the local IP address to send the segment from
++ * @param remote_ip the remote IP address to send the segment to
++ * @param local_port the local TCP port to send the segment from
++ * @param remote_port the remote TCP port to send the segment to
++ */
++void
++tcp_rst(u32_t seqno, u32_t ackno,
++ ip_addr_t *local_ip, ip_addr_t *remote_ip,
++ u16_t local_port, u16_t remote_port)
++{
++ struct pbuf *p;
++ struct tcp_hdr *tcphdr;
++ p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
++ if (p == NULL) {
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
++ return;
++ }
++ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
++ (p->len >= sizeof(struct tcp_hdr)));
++
++ tcphdr = (struct tcp_hdr *)p->payload;
++ tcphdr->src = htons(local_port);
++ tcphdr->dest = htons(remote_port);
++ tcphdr->seqno = htonl(seqno);
++ tcphdr->ackno = htonl(ackno);
++ TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
++ tcphdr->wnd = PP_HTONS(TCP_WND);
++ tcphdr->chksum = 0;
++ tcphdr->urgp = 0;
++
++#if CHECKSUM_GEN_TCP
++ tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
++ IP_PROTO_TCP, p->tot_len);
++#endif
++ TCP_STATS_INC(tcp.xmit);
++ snmp_inc_tcpoutrsts();
++ /* Send output with hardcoded TTL since we have no access to the pcb */
++ ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
++ pbuf_free(p);
++ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
++}
++
++/**
++ * Requeue all unacked segments for retransmission
++ *
++ * Called by tcp_slowtmr() for slow retransmission.
++ *
++ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
++ */
++void
++tcp_rexmit_rto(struct tcp_pcb *pcb)
++{
++ struct tcp_seg *seg;
++
++ if (pcb->unacked == NULL) {
++ return;
++ }
++
++ /* Move all unacked segments to the head of the unsent queue */
++ for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
++ /* concatenate unsent queue after unacked queue */
++ seg->next = pcb->unsent;
++ /* unsent queue is the concatenated queue (of unacked, unsent) */
++ pcb->unsent = pcb->unacked;
++ /* unacked queue is now empty */
++ pcb->unacked = NULL;
++
++ /* increment number of retransmissions */
++ ++pcb->nrtx;
++
++ /* Don't take any RTT measurements after retransmitting. */
++ pcb->rttest = 0;
++
++ /* Do the actual retransmission */
++ tcp_output(pcb);
++}
++
++/**
++ * Requeue the first unacked segment for retransmission
++ *
++ * Called by tcp_receive() for fast retramsmit.
++ *
++ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
++ */
++void
++tcp_rexmit(struct tcp_pcb *pcb)
++{
++ struct tcp_seg *seg;
++ struct tcp_seg **cur_seg;
++
++ if (pcb->unacked == NULL) {
++ return;
++ }
++
++ /* Move the first unacked segment to the unsent queue */
++ /* Keep the unsent queue sorted. */
++ seg = pcb->unacked;
++ pcb->unacked = seg->next;
++
++ cur_seg = &(pcb->unsent);
++ while (*cur_seg &&
++ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
++ cur_seg = &((*cur_seg)->next );
++ }
++ seg->next = *cur_seg;
++ *cur_seg = seg;
++
++ ++pcb->nrtx;
++
++ /* Don't take any rtt measurements after retransmitting. */
++ pcb->rttest = 0;
++
++ /* Do the actual retransmission. */
++ snmp_inc_tcpretranssegs();
++ /* No need to call tcp_output: we are always called from tcp_input()
++ and thus tcp_output directly returns. */
++}
++
++
++/**
++ * Handle retransmission after three dupacks received
++ *
++ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
++ */
++void
++tcp_rexmit_fast(struct tcp_pcb *pcb)
++{
++ if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
++ /* This is fast retransmit. Retransmit the first unacked segment. */
++ LWIP_DEBUGF(TCP_FR_DEBUG,
++ ("tcp_receive: dupacks %"U16_F" (%"U32_F
++ "), fast retransmit %"U32_F"\n",
++ (u16_t)pcb->dupacks, pcb->lastack,
++ ntohl(pcb->unacked->tcphdr->seqno)));
++ tcp_rexmit(pcb);
++
++ /* Set ssthresh to half of the minimum of the current
++ * cwnd and the advertised window */
++ if (pcb->cwnd > pcb->snd_wnd) {
++ pcb->ssthresh = pcb->snd_wnd / 2;
++ } else {
++ pcb->ssthresh = pcb->cwnd / 2;
++ }
++
++ /* The minimum value for ssthresh should be 2 MSS */
++ if (pcb->ssthresh < 2*pcb->mss) {
++ LWIP_DEBUGF(TCP_FR_DEBUG,
++ ("tcp_receive: The minimum value for ssthresh %"U16_F
++ " should be min 2 mss %"U16_F"...\n",
++ pcb->ssthresh, 2*pcb->mss));
++ pcb->ssthresh = 2*pcb->mss;
++ }
++
++ pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
++ pcb->flags |= TF_INFR;
++ }
++}
++
++
++/**
++ * Send keepalive packets to keep a connection active although
++ * no data is sent over it.
++ *
++ * Called by tcp_slowtmr()
++ *
++ * @param pcb the tcp_pcb for which to send a keepalive packet
++ */
++void
++tcp_keepalive(struct tcp_pcb *pcb)
++{
++ struct pbuf *p;
++ struct tcp_hdr *tcphdr;
++
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
++ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
++ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
++
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
++ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
++
++ p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
++ if(p == NULL) {
++ LWIP_DEBUGF(TCP_DEBUG,
++ ("tcp_keepalive: could not allocate memory for pbuf\n"));
++ return;
++ }
++ tcphdr = (struct tcp_hdr *)p->payload;
++
++#if CHECKSUM_GEN_TCP
++ tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
++ IP_PROTO_TCP, p->tot_len);
++#endif
++ TCP_STATS_INC(tcp.xmit);
++
++ /* Send output to IP */
++#if LWIP_NETIF_HWADDRHINT
++ ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
++ &(pcb->addr_hint));
++#else /* LWIP_NETIF_HWADDRHINT*/
++ ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
++#endif /* LWIP_NETIF_HWADDRHINT*/
++
++ pbuf_free(p);
++
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
++ pcb->snd_nxt - 1, pcb->rcv_nxt));
++}
++
++
++/**
++ * Send persist timer zero-window probes to keep a connection active
++ * when a window update is lost.
++ *
++ * Called by tcp_slowtmr()
++ *
++ * @param pcb the tcp_pcb for which to send a zero-window probe packet
++ */
++void
++tcp_zero_window_probe(struct tcp_pcb *pcb)
++{
++ struct pbuf *p;
++ struct tcp_hdr *tcphdr;
++ struct tcp_seg *seg;
++ u16_t len;
++ u8_t is_fin;
++
++ LWIP_DEBUGF(TCP_DEBUG,
++ ("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
++ U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
++ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
++ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
++
++ LWIP_DEBUGF(TCP_DEBUG,
++ ("tcp_zero_window_probe: tcp_ticks %"U32_F
++ " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
++ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
++
++ seg = pcb->unacked;
++
++ if(seg == NULL) {
++ seg = pcb->unsent;
++ }
++ if(seg == NULL) {
++ return;
++ }
++
++ is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
++ /* we want to send one seqno: either FIN or data (no options) */
++ len = is_fin ? 0 : 1;
++
++ p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
++ if(p == NULL) {
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
++ return;
++ }
++ tcphdr = (struct tcp_hdr *)p->payload;
++
++ if (is_fin) {
++ /* FIN segment, no data */
++ TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
++ } else {
++ /* Data segment, copy in one byte from the head of the unacked queue */
++ struct tcp_hdr *thdr = (struct tcp_hdr *)seg->p->payload;
++ char *d = ((char *)p->payload + TCP_HLEN);
++ pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4);
++ }
++
++#if CHECKSUM_GEN_TCP
++ tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
++ IP_PROTO_TCP, p->tot_len);
++#endif
++ TCP_STATS_INC(tcp.xmit);
++
++ /* Send output to IP */
++#if LWIP_NETIF_HWADDRHINT
++ ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
++ &(pcb->addr_hint));
++#else /* LWIP_NETIF_HWADDRHINT*/
++ ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
++#endif /* LWIP_NETIF_HWADDRHINT*/
++
++ pbuf_free(p);
++
++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
++ " ackno %"U32_F".\n",
++ pcb->snd_nxt - 1, pcb->rcv_nxt));
++}
++#endif /* LWIP_TCP */
--- /dev/null
--- /dev/null
++#ifndef _ROS_IP_H_
++#define _ROS_IP_H_
++
++#include "lwip/tcp.h"
++#include "lwip/pbuf.h"
++#include "lwip/ip_addr.h"
++#include "tcpip.h"
++
++#ifndef LWIP_TAG
++ #define LWIP_TAG 'PIwl'
++#endif
++
++typedef struct tcp_pcb* PTCP_PCB;
++
++typedef struct _QUEUE_ENTRY
++{
++ struct pbuf *p;
++ LIST_ENTRY ListEntry;
++} QUEUE_ENTRY, *PQUEUE_ENTRY;
++
++struct lwip_callback_msg
++{
++ /* Synchronization */
++ KEVENT Event;
++
++ /* Input */
++ union {
++ struct {
++ PVOID Arg;
++ } Socket;
++ struct {
++ PCONNECTION_ENDPOINT Connection;
++ struct ip_addr *IpAddress;
++ u16_t Port;
++ } Bind;
++ struct {
++ PCONNECTION_ENDPOINT Connection;
++ u8_t Backlog;
++ } Listen;
++ struct {
++ PCONNECTION_ENDPOINT Connection;
++ void *Data;
++ u16_t DataLength;
++ } Send;
++ struct {
++ PCONNECTION_ENDPOINT Connection;
++ struct ip_addr *IpAddress;
++ u16_t Port;
++ } Connect;
++ struct {
++ PCONNECTION_ENDPOINT Connection;
++ int shut_rx;
++ int shut_tx;
++ } Shutdown;
++ struct {
++ PCONNECTION_ENDPOINT Connection;
++ int Callback;
++ } Close;
++ } Input;
++
++ /* Output */
++ union {
++ struct {
++ struct tcp_pcb *NewPcb;
++ } Socket;
++ struct {
++ err_t Error;
++ } Bind;
++ struct {
++ struct tcp_pcb *NewPcb;
++ } Listen;
++ struct {
++ err_t Error;
++ } Send;
++ struct {
++ err_t Error;
++ } Connect;
++ struct {
++ err_t Error;
++ } Shutdown;
++ struct {
++ err_t Error;
++ } Close;
++ } Output;
++};
++
++NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHAR RecvBuffer, UINT RecvLen, UINT *Received);
++
++/* External TCP event handlers */
++extern void TCPConnectEventHandler(void *arg, const err_t err);
++extern void TCPAcceptEventHandler(void *arg, PTCP_PCB newpcb);
++extern void TCPSendEventHandler(void *arg, const u16_t space);
++extern void TCPFinEventHandler(void *arg, const err_t err);
++extern u32_t TCPRecvEventHandler(void *arg, struct pbuf *p);
++
++/* TCP functions */
++PTCP_PCB LibTCPSocket(void *arg);
++err_t LibTCPBind(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port);
++PTCP_PCB LibTCPListen(PCONNECTION_ENDPOINT Connection, const u8_t backlog);
++err_t LibTCPSend(PCONNECTION_ENDPOINT Connection, void *const dataptr, const u16_t len, const int safe);
++err_t LibTCPConnect(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port);
++err_t LibTCPShutdown(PCONNECTION_ENDPOINT Connection, const int shut_rx, const int shut_tx);
++err_t LibTCPClose(PCONNECTION_ENDPOINT Connection, const int safe, const int callback);
++
++err_t LibTCPGetPeerName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port);
++err_t LibTCPGetHostName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port);
++void LibTCPAccept(PTCP_PCB pcb, struct tcp_pcb *listen_pcb, void *arg);
++
++/* IP functions */
++void LibIPInsertPacket(void *ifarg, const void *const data, const u32_t size);
++void LibIPInitialize(void);
++void LibIPShutdown(void);
++
++#endif
--- /dev/null
--- /dev/null
++#include "lwip/sys.h"
++#include "lwip/tcpip.h"
++
++#include "rosip.h"
++
++#include <debug.h>
++
++static const char * const tcp_state_str[] = {
++ "CLOSED",
++ "LISTEN",
++ "SYN_SENT",
++ "SYN_RCVD",
++ "ESTABLISHED",
++ "FIN_WAIT_1",
++ "FIN_WAIT_2",
++ "CLOSE_WAIT",
++ "CLOSING",
++ "LAST_ACK",
++ "TIME_WAIT"
++};
++
++/* The way that lwIP does multi-threading is really not ideal for our purposes but
++ * we best go along with it unless we want another unstable TCP library. lwIP uses
++ * a thread called the "tcpip thread" which is the only one allowed to call raw API
++ * functions. Since this is the case, for each of our LibTCP* functions, we queue a request
++ * for a callback to "tcpip thread" which calls our LibTCP*Callback functions. Yes, this is
++ * a lot of unnecessary thread swapping and it could definitely be faster, but I don't want
++ * to going messing around in lwIP because I have no desire to create another mess like oskittcp */
++
++extern KEVENT TerminationEvent;
++extern NPAGED_LOOKASIDE_LIST MessageLookasideList;
++extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList;
++
++static
++void
++LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection)
++{
++ PLIST_ENTRY Entry;
++ PQUEUE_ENTRY qp = NULL;
++
++ ReferenceObject(Connection);
++
++ while (!IsListEmpty(&Connection->PacketQueue))
++ {
++ Entry = RemoveHeadList(&Connection->PacketQueue);
++ qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry);
++
++ /* We're in the tcpip thread here so this is safe */
++ pbuf_free(qp->p);
++
++ ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp);
++ }
++
++ DereferenceObject(Connection);
++}
++
++void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection, struct pbuf *p)
++{
++ PQUEUE_ENTRY qp;
++
++ qp = (PQUEUE_ENTRY)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList);
++ qp->p = p;
++
++ ExInterlockedInsertTailList(&Connection->PacketQueue, &qp->ListEntry, &Connection->Lock);
++}
++
++PQUEUE_ENTRY LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection)
++{
++ PLIST_ENTRY Entry;
++ PQUEUE_ENTRY qp = NULL;
++
++ if (IsListEmpty(&Connection->PacketQueue)) return NULL;
++
++ Entry = RemoveHeadList(&Connection->PacketQueue);
++
++ qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry);
++
++ return qp;
++}
++
++NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHAR RecvBuffer, UINT RecvLen, UINT *Received)
++{
++ PQUEUE_ENTRY qp;
++ struct pbuf* p;
++ NTSTATUS Status = STATUS_PENDING;
++ UINT ReadLength, ExistingDataLength, SpaceLeft;
++ KIRQL OldIrql;
++
++ (*Received) = 0;
++ SpaceLeft = RecvLen;
++
++ LockObject(Connection, &OldIrql);
++
++ if (!IsListEmpty(&Connection->PacketQueue))
++ {
++ while ((qp = LibTCPDequeuePacket(Connection)) != NULL)
++ {
++ p = qp->p;
++ ExistingDataLength = (*Received);
++
++ Status = STATUS_SUCCESS;
++
++ ReadLength = MIN(p->tot_len, SpaceLeft);
++ if (ReadLength != p->tot_len)
++ {
++ if (ExistingDataLength)
++ {
++ /* The packet was too big but we used some data already so give it another shot later */
++ InsertHeadList(&Connection->PacketQueue, &qp->ListEntry);
++ break;
++ }
++ else
++ {
++ /* The packet is just too big to fit fully in our buffer, even when empty so
++ * return an informative status but still copy all the data we can fit.
++ */
++ Status = STATUS_BUFFER_OVERFLOW;
++ }
++ }
++
++ UnlockObject(Connection, OldIrql);
++
++ /* Return to a lower IRQL because the receive buffer may be pageable memory */
++ for (; (*Received) < ReadLength + ExistingDataLength; (*Received) += p->len, p = p->next)
++ {
++ RtlCopyMemory(RecvBuffer + (*Received), p->payload, p->len);
++ }
++
++ LockObject(Connection, &OldIrql);
++
++ SpaceLeft -= ReadLength;
++
++ /* Use this special pbuf free callback function because we're outside tcpip thread */
++ pbuf_free_callback(qp->p);
++
++ ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp);
++
++ if (!RecvLen)
++ break;
++
++ if (Status != STATUS_SUCCESS)
++ break;
++ }
++ }
++ else
++ {
++ if (Connection->ReceiveShutdown)
++ Status = STATUS_SUCCESS;
++ else
++ Status = STATUS_PENDING;
++ }
++
++ UnlockObject(Connection, OldIrql);
++
++ return Status;
++}
++
++static
++BOOLEAN
++WaitForEventSafely(PRKEVENT Event)
++{
++ PVOID WaitObjects[] = {Event, &TerminationEvent};
++
++ if (KeWaitForMultipleObjects(2,
++ WaitObjects,
++ WaitAny,
++ Executive,
++ KernelMode,
++ FALSE,
++ NULL,
++ NULL) == STATUS_WAIT_0)
++ {
++ /* Signalled by the caller's event */
++ return TRUE;
++ }
++ else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
++ {
++ /* Signalled by our termination event */
++ return FALSE;
++ }
++}
++
++static
++err_t
++InternalSendEventHandler(void *arg, PTCP_PCB pcb, const u16_t space)
++{
++ /* Make sure the socket didn't get closed */
++ if (!arg) return ERR_OK;
++
++ TCPSendEventHandler(arg, space);
++
++ return ERR_OK;
++}
++
++static
++err_t
++InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err)
++{
++ PCONNECTION_ENDPOINT Connection = arg;
++ u32_t len;
++
++ /* Make sure the socket didn't get closed */
++ if (!arg)
++ {
++ if (p)
++ pbuf_free(p);
++
++ return ERR_OK;
++ }
++
++ if (p)
++ {
++ len = TCPRecvEventHandler(arg, p);
++ if (len == p->tot_len)
++ {
++ tcp_recved(pcb, len);
++
++ pbuf_free(p);
++
++ return ERR_OK;
++ }
++ else if (len != 0)
++ {
++ DbgPrint("UNTESTED CASE: NOT ALL DATA TAKEN! EXTRA DATA MAY BE LOST!\n");
++
++ tcp_recved(pcb, len);
++
++ /* Possible memory leak of pbuf here? */
++
++ return ERR_OK;
++ }
++ else
++ {
++ LibTCPEnqueuePacket(Connection, p);
++
++ tcp_recved(pcb, p->tot_len);
++
++ return ERR_OK;
++ }
++ }
++ else if (err == ERR_OK)
++ {
++ /* Complete pending reads with 0 bytes to indicate a graceful closure,
++ * but note that send is still possible in this state so we don't close the
++ * whole socket here (by calling tcp_close()) as that would violate TCP specs
++ */
++ Connection->ReceiveShutdown = TRUE;
++ TCPFinEventHandler(arg, ERR_OK);
++ }
++
++ return ERR_OK;
++}
++
++static
++err_t
++InternalAcceptEventHandler(void *arg, PTCP_PCB newpcb, const err_t err)
++{
++ /* Make sure the socket didn't get closed */
++ if (!arg)
++ return ERR_ABRT;
++
++ TCPAcceptEventHandler(arg, newpcb);
++
++ /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
++ if (newpcb->callback_arg)
++ return ERR_OK;
++ else
++ return ERR_ABRT;
++}
++
++static
++err_t
++InternalConnectEventHandler(void *arg, PTCP_PCB pcb, const err_t err)
++{
++ /* Make sure the socket didn't get closed */
++ if (!arg)
++ return ERR_OK;
++
++ TCPConnectEventHandler(arg, err);
++
++ return ERR_OK;
++}
++
++static
++void
++InternalErrorEventHandler(void *arg, const err_t err)
++{
++ /* Make sure the socket didn't get closed */
++ if (!arg) return;
++
++ TCPFinEventHandler(arg, err);
++}
++
++static
++void
++LibTCPSocketCallback(void *arg)
++{
++ struct lwip_callback_msg *msg = arg;
++
++ ASSERT(msg);
++
++ msg->Output.Socket.NewPcb = tcp_new();
++
++ if (msg->Output.Socket.NewPcb)
++ {
++ tcp_arg(msg->Output.Socket.NewPcb, msg->Input.Socket.Arg);
++ tcp_err(msg->Output.Socket.NewPcb, InternalErrorEventHandler);
++ }
++
++ KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
++}
++
++struct tcp_pcb *
++LibTCPSocket(void *arg)
++{
++ struct lwip_callback_msg *msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
++ struct tcp_pcb *ret;
++
++ if (msg)
++ {
++ KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
++ msg->Input.Socket.Arg = arg;
++
++ tcpip_callback_with_block(LibTCPSocketCallback, msg, 1);
++
++ if (WaitForEventSafely(&msg->Event))
++ ret = msg->Output.Socket.NewPcb;
++ else
++ ret = NULL;
++
++ ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
++
++ return ret;
++ }
++
++ return NULL;
++}
++
++static
++void
++LibTCPBindCallback(void *arg)
++{
++ struct lwip_callback_msg *msg = arg;
++
++ ASSERT(msg);
++
++ if (!msg->Input.Bind.Connection->SocketContext)
++ {
++ msg->Output.Bind.Error = ERR_CLSD;
++ goto done;
++ }
++
++ msg->Output.Bind.Error = tcp_bind((PTCP_PCB)msg->Input.Bind.Connection->SocketContext,
++ msg->Input.Bind.IpAddress,
++ ntohs(msg->Input.Bind.Port));
++
++done:
++ KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
++}
++
++err_t
++LibTCPBind(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
++{
++ struct lwip_callback_msg *msg;
++ err_t ret;
++
++ msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
++ if (msg)
++ {
++ KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
++ msg->Input.Bind.Connection = Connection;
++ msg->Input.Bind.IpAddress = ipaddr;
++ msg->Input.Bind.Port = port;
++
++ tcpip_callback_with_block(LibTCPBindCallback, msg, 1);
++
++ if (WaitForEventSafely(&msg->Event))
++ ret = msg->Output.Bind.Error;
++ else
++ ret = ERR_CLSD;
++
++ ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
++
++ return ret;
++ }
++
++ return ERR_MEM;
++}
++
++static
++void
++LibTCPListenCallback(void *arg)
++{
++ struct lwip_callback_msg *msg = arg;
++
++ ASSERT(msg);
++
++ if (!msg->Input.Listen.Connection->SocketContext)
++ {
++ msg->Output.Listen.NewPcb = NULL;
++ goto done;
++ }
++
++ msg->Output.Listen.NewPcb = tcp_listen_with_backlog((PTCP_PCB)msg->Input.Listen.Connection->SocketContext, msg->Input.Listen.Backlog);
++
++ if (msg->Output.Listen.NewPcb)
++ {
++ tcp_accept(msg->Output.Listen.NewPcb, InternalAcceptEventHandler);
++ }
++
++done:
++ KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
++}
++
++PTCP_PCB
++LibTCPListen(PCONNECTION_ENDPOINT Connection, const u8_t backlog)
++{
++ struct lwip_callback_msg *msg;
++ PTCP_PCB ret;
++
++ msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
++ if (msg)
++ {
++ KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
++ msg->Input.Listen.Connection = Connection;
++ msg->Input.Listen.Backlog = backlog;
++
++ tcpip_callback_with_block(LibTCPListenCallback, msg, 1);
++
++ if (WaitForEventSafely(&msg->Event))
++ ret = msg->Output.Listen.NewPcb;
++ else
++ ret = NULL;
++
++ ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
++
++ return ret;
++ }
++
++ return NULL;
++}
++
++static
++void
++LibTCPSendCallback(void *arg)
++{
++ struct lwip_callback_msg *msg = arg;
++
++ ASSERT(msg);
++
++ if (!msg->Input.Send.Connection->SocketContext)
++ {
++ msg->Output.Send.Error = ERR_CLSD;
++ goto done;
++ }
++
++ if (msg->Input.Send.Connection->SendShutdown)
++ {
++ msg->Output.Send.Error = ERR_CLSD;
++ goto done;
++ }
++
++ msg->Output.Send.Error = tcp_write((PTCP_PCB)msg->Input.Send.Connection->SocketContext,
++ msg->Input.Send.Data,
++ msg->Input.Send.DataLength,
++ TCP_WRITE_FLAG_COPY);
++ if (msg->Output.Send.Error == ERR_MEM)
++ {
++ /* No buffer space so return pending */
++ msg->Output.Send.Error = ERR_INPROGRESS;
++ }
++ else if (msg->Output.Send.Error == ERR_OK)
++ {
++ /* Queued successfully so try to send it */
++ tcp_output((PTCP_PCB)msg->Input.Send.Connection->SocketContext);
++ }
++
++done:
++ KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
++}
++
++err_t
++LibTCPSend(PCONNECTION_ENDPOINT Connection, void *const dataptr, const u16_t len, const int safe)
++{
++ err_t ret;
++ struct lwip_callback_msg *msg;
++
++ msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
++ if (msg)
++ {
++ KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
++ msg->Input.Send.Connection = Connection;
++ msg->Input.Send.Data = dataptr;
++ msg->Input.Send.DataLength = len;
++
++ if (safe)
++ LibTCPSendCallback(msg);
++ else
++ tcpip_callback_with_block(LibTCPSendCallback, msg, 1);
++
++ if (WaitForEventSafely(&msg->Event))
++ ret = msg->Output.Send.Error;
++ else
++ ret = ERR_CLSD;
++
++ ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
++
++ return ret;
++ }
++
++ return ERR_MEM;
++}
++
++static
++void
++LibTCPConnectCallback(void *arg)
++{
++ struct lwip_callback_msg *msg = arg;
++
++ ASSERT(arg);
++
++ if (!msg->Input.Connect.Connection->SocketContext)
++ {
++ msg->Output.Connect.Error = ERR_CLSD;
++ goto done;
++ }
++
++ tcp_recv((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalRecvEventHandler);
++ tcp_sent((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalSendEventHandler);
++
++ err_t Error = tcp_connect((PTCP_PCB)msg->Input.Connect.Connection->SocketContext,
++ msg->Input.Connect.IpAddress, ntohs(msg->Input.Connect.Port),
++ InternalConnectEventHandler);
++
++ msg->Output.Connect.Error = Error == ERR_OK ? ERR_INPROGRESS : Error;
++
++done:
++ KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
++}
++
++err_t
++LibTCPConnect(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
++{
++ struct lwip_callback_msg *msg;
++ err_t ret;
++
++ msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
++ if (msg)
++ {
++ KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
++ msg->Input.Connect.Connection = Connection;
++ msg->Input.Connect.IpAddress = ipaddr;
++ msg->Input.Connect.Port = port;
++
++ tcpip_callback_with_block(LibTCPConnectCallback, msg, 1);
++
++ if (WaitForEventSafely(&msg->Event))
++ {
++ ret = msg->Output.Connect.Error;
++ }
++ else
++ ret = ERR_CLSD;
++
++ ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
++
++ return ret;
++ }
++
++ return ERR_MEM;
++}
++
++static
++void
++LibTCPShutdownCallback(void *arg)
++{
++ struct lwip_callback_msg *msg = arg;
++ PTCP_PCB pcb = msg->Input.Shutdown.Connection->SocketContext;
++
++ if (!msg->Input.Shutdown.Connection->SocketContext)
++ {
++ msg->Output.Shutdown.Error = ERR_CLSD;
++ goto done;
++ }
++
++ if (pcb->state == CLOSE_WAIT)
++ {
++ /* This case actually results in a socket closure later (lwIP bug?) */
++ msg->Input.Shutdown.Connection->SocketContext = NULL;
++ }
++
++ msg->Output.Shutdown.Error = tcp_shutdown(pcb, msg->Input.Shutdown.shut_rx, msg->Input.Shutdown.shut_tx);
++ if (msg->Output.Shutdown.Error)
++ {
++ msg->Input.Shutdown.Connection->SocketContext = pcb;
++ }
++ else
++ {
++ if (msg->Input.Shutdown.shut_rx)
++ msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE;
++
++ if (msg->Input.Shutdown.shut_tx)
++ msg->Input.Shutdown.Connection->SendShutdown = TRUE;
++ }
++
++done:
++ KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
++}
++
++err_t
++LibTCPShutdown(PCONNECTION_ENDPOINT Connection, const int shut_rx, const int shut_tx)
++{
++ struct lwip_callback_msg *msg;
++ err_t ret;
++
++ msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
++ if (msg)
++ {
++ KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
++
++ msg->Input.Shutdown.Connection = Connection;
++ msg->Input.Shutdown.shut_rx = shut_rx;
++ msg->Input.Shutdown.shut_tx = shut_tx;
++
++ tcpip_callback_with_block(LibTCPShutdownCallback, msg, 1);
++
++ if (WaitForEventSafely(&msg->Event))
++ ret = msg->Output.Shutdown.Error;
++ else
++ ret = ERR_CLSD;
++
++ ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
++
++ return ret;
++ }
++
++ return ERR_MEM;
++}
++
++static
++void
++LibTCPCloseCallback(void *arg)
++{
++ struct lwip_callback_msg *msg = arg;
++ PTCP_PCB pcb = msg->Input.Close.Connection->SocketContext;
++ int state;
++
++ /* Empty the queue even if we're already "closed" */
++ LibTCPEmptyQueue(msg->Input.Close.Connection);
++
++ if (!msg->Input.Close.Connection->SocketContext)
++ {
++ msg->Output.Close.Error = ERR_OK;
++ goto done;
++ }
++
++ /* Clear the PCB pointer */
++ msg->Input.Close.Connection->SocketContext = NULL;
++
++ /* Save the old PCB state */
++ state = pcb->state;
++
++ msg->Output.Close.Error = tcp_close(pcb);
++ if (!msg->Output.Close.Error)
++ {
++ if (msg->Input.Close.Callback)
++ {
++ /* Call the FIN handler in the cases where it will not be called by lwIP */
++ switch (state)
++ {
++ case CLOSED:
++ case LISTEN:
++ case SYN_SENT:
++ TCPFinEventHandler(msg->Input.Close.Connection, ERR_OK);
++ break;
++
++ default:
++ break;
++ }
++ }
++ }
++ else
++ {
++ /* Restore the PCB pointer */
++ msg->Input.Close.Connection->SocketContext = pcb;
++ }
++
++done:
++ KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
++}
++
++err_t
++LibTCPClose(PCONNECTION_ENDPOINT Connection, const int safe, const int callback)
++{
++ err_t ret;
++ struct lwip_callback_msg *msg;
++
++ msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
++ if (msg)
++ {
++ KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
++
++ msg->Input.Close.Connection = Connection;
++ msg->Input.Close.Callback = callback;
++
++ if (safe)
++ LibTCPCloseCallback(msg);
++ else
++ tcpip_callback_with_block(LibTCPCloseCallback, msg, 1);
++
++ if (WaitForEventSafely(&msg->Event))
++ ret = msg->Output.Close.Error;
++ else
++ ret = ERR_CLSD;
++
++ ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
++
++ return ret;
++ }
++
++ return ERR_MEM;
++}
++
++void
++LibTCPAccept(PTCP_PCB pcb, struct tcp_pcb *listen_pcb, void *arg)
++{
++ ASSERT(arg);
++
++ tcp_arg(pcb, NULL);
++ tcp_recv(pcb, InternalRecvEventHandler);
++ tcp_sent(pcb, InternalSendEventHandler);
++ tcp_err(pcb, InternalErrorEventHandler);
++ tcp_arg(pcb, arg);
++
++ tcp_accepted(listen_pcb);
++}
++
++err_t
++LibTCPGetHostName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port)
++{
++ if (!pcb)
++ return ERR_CLSD;
++
++ *ipaddr = pcb->local_ip;
++ *port = pcb->local_port;
++
++ return ERR_OK;
++}
++
++err_t
++LibTCPGetPeerName(PTCP_PCB pcb, struct ip_addr * const ipaddr, u16_t * const port)
++{
++ if (!pcb)
++ return ERR_CLSD;
++
++ *ipaddr = pcb->remote_ip;
++ *port = pcb->remote_port;
++
++ return ERR_OK;
++}
--- /dev/null
--- /dev/null
++#include "lwip/sys.h"
++
++#include "lwip/tcp.h"
++#include "lwip/pbuf.h"
++#include "lwip/err.h"
++
++#include "rosip.h"
++
++#include <debug.h>
++
++static LIST_ENTRY ThreadListHead;
++static KSPIN_LOCK ThreadListLock;
++
++KEVENT TerminationEvent;
++NPAGED_LOOKASIDE_LIST MessageLookasideList;
++NPAGED_LOOKASIDE_LIST QueueEntryLookasideList;
++
++static LARGE_INTEGER StartTime;
++
++typedef struct _thread_t
++{
++ HANDLE Handle;
++ void (* ThreadFunction)(void *arg);
++ void *ThreadContext;
++ LIST_ENTRY ListEntry;
++} *thread_t;
++
++u32_t sys_now(void)
++{
++ LARGE_INTEGER CurrentTime;
++
++ KeQuerySystemTime(&CurrentTime);
++
++ return (CurrentTime.QuadPart - StartTime.QuadPart) / 10000;
++}
++
++void
++sys_arch_protect(sys_prot_t *lev)
++{
++ /* Preempt the dispatcher */
++ KeRaiseIrql(DISPATCH_LEVEL, lev);
++}
++
++void
++sys_arch_unprotect(sys_prot_t lev)
++{
++ KeLowerIrql(lev);
++}
++
++err_t
++sys_sem_new(sys_sem_t *sem, u8_t count)
++{
++ ASSERT(count == 0 || count == 1);
++
++ /* It seems lwIP uses the semaphore implementation as either a completion event or a lock
++ * so I optimize for this case by using a synchronization event and setting its initial state
++ * to signalled for a lock and non-signalled for a completion event */
++
++ KeInitializeEvent(&sem->Event, SynchronizationEvent, count);
++
++ sem->Valid = 1;
++
++ return ERR_OK;
++}
++
++int sys_sem_valid(sys_sem_t *sem)
++{
++ return sem->Valid;
++}
++
++void sys_sem_set_invalid(sys_sem_t *sem)
++{
++ sem->Valid = 0;
++}
++
++void
++sys_sem_free(sys_sem_t* sem)
++{
++ /* No op (allocated in stack) */
++
++ sys_sem_set_invalid(sem);
++}
++
++void
++sys_sem_signal(sys_sem_t* sem)
++{
++ KeSetEvent(&sem->Event, IO_NO_INCREMENT, FALSE);
++}
++
++u32_t
++sys_arch_sem_wait(sys_sem_t* sem, u32_t timeout)
++{
++ LARGE_INTEGER LargeTimeout, PreWaitTime, PostWaitTime;
++ UINT64 TimeDiff;
++ NTSTATUS Status;
++ PVOID WaitObjects[] = {&sem->Event, &TerminationEvent};
++
++ LargeTimeout.QuadPart = Int32x32To64(timeout, -10000);
++
++ KeQuerySystemTime(&PreWaitTime);
++
++ Status = KeWaitForMultipleObjects(2,
++ WaitObjects,
++ WaitAny,
++ Executive,
++ KernelMode,
++ FALSE,
++ timeout != 0 ? &LargeTimeout : NULL,
++ NULL);
++ if (Status == STATUS_WAIT_0)
++ {
++ KeQuerySystemTime(&PostWaitTime);
++ TimeDiff = PostWaitTime.QuadPart - PreWaitTime.QuadPart;
++ TimeDiff /= 10000;
++
++ return TimeDiff;
++ }
++ else if (Status == STATUS_WAIT_1)
++ {
++ /* DON'T remove ourselves from the thread list! */
++ PsTerminateSystemThread(STATUS_SUCCESS);
++
++ /* We should never get here! */
++ ASSERT(FALSE);
++
++ return 0;
++ }
++
++ return SYS_ARCH_TIMEOUT;
++}
++
++err_t
++sys_mbox_new(sys_mbox_t *mbox, int size)
++{
++ KeInitializeSpinLock(&mbox->Lock);
++
++ InitializeListHead(&mbox->ListHead);
++
++ KeInitializeEvent(&mbox->Event, NotificationEvent, FALSE);
++
++ mbox->Valid = 1;
++
++ return ERR_OK;
++}
++
++int sys_mbox_valid(sys_mbox_t *mbox)
++{
++ return mbox->Valid;
++}
++
++void sys_mbox_set_invalid(sys_mbox_t *mbox)
++{
++ mbox->Valid = 0;
++}
++
++void
++sys_mbox_free(sys_mbox_t *mbox)
++{
++ ASSERT(IsListEmpty(&mbox->ListHead));
++
++ sys_mbox_set_invalid(mbox);
++}
++
++void
++sys_mbox_post(sys_mbox_t *mbox, void *msg)
++{
++ PLWIP_MESSAGE_CONTAINER Container;
++
++ Container = ExAllocatePool(NonPagedPool, sizeof(*Container));
++ ASSERT(Container);
++
++ Container->Message = msg;
++
++ ExInterlockedInsertTailList(&mbox->ListHead,
++ &Container->ListEntry,
++ &mbox->Lock);
++
++ KeSetEvent(&mbox->Event, IO_NO_INCREMENT, FALSE);
++}
++
++u32_t
++sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
++{
++ LARGE_INTEGER LargeTimeout, PreWaitTime, PostWaitTime;
++ UINT64 TimeDiff;
++ NTSTATUS Status;
++ PVOID Message;
++ PLWIP_MESSAGE_CONTAINER Container;
++ PLIST_ENTRY Entry;
++ KIRQL OldIrql;
++ PVOID WaitObjects[] = {&mbox->Event, &TerminationEvent};
++
++ LargeTimeout.QuadPart = Int32x32To64(timeout, -10000);
++
++ KeQuerySystemTime(&PreWaitTime);
++
++ Status = KeWaitForMultipleObjects(2,
++ WaitObjects,
++ WaitAny,
++ Executive,
++ KernelMode,
++ FALSE,
++ timeout != 0 ? &LargeTimeout : NULL,
++ NULL);
++
++ if (Status == STATUS_WAIT_0)
++ {
++ KeAcquireSpinLock(&mbox->Lock, &OldIrql);
++ Entry = RemoveHeadList(&mbox->ListHead);
++ ASSERT(Entry);
++ if (IsListEmpty(&mbox->ListHead))
++ KeClearEvent(&mbox->Event);
++ KeReleaseSpinLock(&mbox->Lock, OldIrql);
++
++ Container = CONTAINING_RECORD(Entry, LWIP_MESSAGE_CONTAINER, ListEntry);
++ Message = Container->Message;
++ ExFreePool(Container);
++
++ if (msg)
++ *msg = Message;
++
++ KeQuerySystemTime(&PostWaitTime);
++ TimeDiff = PostWaitTime.QuadPart - PreWaitTime.QuadPart;
++ TimeDiff /= 10000;
++
++ return TimeDiff;
++ }
++ else if (Status == STATUS_WAIT_1)
++ {
++ /* DON'T remove ourselves from the thread list! */
++ PsTerminateSystemThread(STATUS_SUCCESS);
++
++ /* We should never get here! */
++ ASSERT(FALSE);
++
++ return 0;
++ }
++
++ return SYS_ARCH_TIMEOUT;
++}
++
++u32_t
++sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
++{
++ if (sys_arch_mbox_fetch(mbox, msg, 1) != SYS_ARCH_TIMEOUT)
++ return 0;
++ else
++ return SYS_MBOX_EMPTY;
++}
++
++err_t
++sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
++{
++ sys_mbox_post(mbox, msg);
++
++ return ERR_OK;
++}
++
++VOID
++NTAPI
++LwipThreadMain(PVOID Context)
++{
++ thread_t Container = (thread_t)Context;
++ KIRQL OldIrql;
++
++ ExInterlockedInsertHeadList(&ThreadListHead, &Container->ListEntry, &ThreadListLock);
++
++ Container->ThreadFunction(Container->ThreadContext);
++
++ KeAcquireSpinLock(&ThreadListLock, &OldIrql);
++ RemoveEntryList(&Container->ListEntry);
++ KeReleaseSpinLock(&ThreadListLock, OldIrql);
++
++ ExFreePool(Container);
++
++ PsTerminateSystemThread(STATUS_SUCCESS);
++}
++
++sys_thread_t
++sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
++{
++ thread_t Container;
++ NTSTATUS Status;
++
++ Container = ExAllocatePool(NonPagedPool, sizeof(*Container));
++ if (!Container)
++ return 0;
++
++ Container->ThreadFunction = thread;
++ Container->ThreadContext = arg;
++
++ Status = PsCreateSystemThread(&Container->Handle,
++ THREAD_ALL_ACCESS,
++ NULL,
++ NULL,
++ NULL,
++ LwipThreadMain,
++ Container);
++
++ if (!NT_SUCCESS(Status))
++ {
++ ExFreePool(Container);
++ return 0;
++ }
++
++ return 0;
++}
++
++void
++sys_init(void)
++{
++ KeInitializeSpinLock(&ThreadListLock);
++ InitializeListHead(&ThreadListHead);
++
++ KeQuerySystemTime(&StartTime);
++
++ KeInitializeEvent(&TerminationEvent, NotificationEvent, FALSE);
++
++ ExInitializeNPagedLookasideList(&MessageLookasideList,
++ NULL,
++ NULL,
++ 0,
++ sizeof(struct lwip_callback_msg),
++ LWIP_TAG,
++ 0);
++
++ ExInitializeNPagedLookasideList(&QueueEntryLookasideList,
++ NULL,
++ NULL,
++ 0,
++ sizeof(QUEUE_ENTRY),
++ LWIP_TAG,
++ 0);
++}
++
++void
++sys_shutdown(void)
++{
++ PLIST_ENTRY CurrentEntry;
++ thread_t Container;
++
++ /* Set the termination event */
++ KeSetEvent(&TerminationEvent, IO_NO_INCREMENT, FALSE);
++
++ /* Loop through the thread list and wait for each to die */
++ while ((CurrentEntry = ExInterlockedRemoveHeadList(&ThreadListHead, &ThreadListLock)))
++ {
++ Container = CONTAINING_RECORD(CurrentEntry, struct _thread_t, ListEntry);
++
++ if (Container->ThreadFunction)
++ {
++ KeWaitForSingleObject(Container->Handle,
++ Executive,
++ KernelMode,
++ FALSE,
++ NULL);
++
++ ZwClose(Container->Handle);
++ }
++ }
++
++ ExDeleteNPagedLookasideList(&MessageLookasideList);
++ ExDeleteNPagedLookasideList(&QueueEntryLookasideList);
++}