mirror of
https://github.com/reactos/reactos.git
synced 2025-06-06 01:40:36 +00:00

- Fix completing IRPs with 0 bytes received when getting the data from the connection's packet queue - Eliminate memory leaks caused by unreleased pbufs - Get rid of some commented code In principle the speed issue with lwIP should be pretty much solved now. There's still some minor things to iron out that testing will reveal probably. Initial tests like running opera, downloading stuff etc seem to be very encouraging however. svn path=/branches/GSoC_2011/TcpIpDriver/; revision=52894
395 lines
12 KiB
C
395 lines
12 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS TCP/IP protocol driver
|
|
* FILE: transport/tcp/event.c
|
|
* PURPOSE: Transmission Control Protocol
|
|
* PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#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);
|
|
}
|
|
|
|
VOID
|
|
CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, BOOLEAN Synchronous)
|
|
{
|
|
ReferenceObject(Connection);
|
|
Bucket->AssociatedEndpoint = Connection;
|
|
if (Synchronous)
|
|
{
|
|
BucketCompletionWorker(Bucket);
|
|
}
|
|
else
|
|
{
|
|
ChewCreate(BucketCompletionWorker, Bucket);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status)
|
|
{
|
|
PTDI_BUCKET Bucket;
|
|
PLIST_ENTRY Entry;
|
|
|
|
ReferenceObject(Connection);
|
|
|
|
DbgPrint("[IP, FlushAllQueues] Flushing recv/all with status: 0x%x for Connection = 0x%x\n", Status, Connection);
|
|
|
|
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);
|
|
}
|
|
|
|
// Calling with Status == STATUS_SUCCESS means that we got a graceful closure
|
|
// so we don't want to kill everything else since send is still valid in this state
|
|
//
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
DbgPrint("[IP, FlushAllQueues] Flushed recv only after graceful closure\n");
|
|
DereferenceObject(Connection);
|
|
return;
|
|
}
|
|
|
|
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
|
|
{
|
|
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
|
|
|
|
Bucket->Status = Status;
|
|
Bucket->Information = 0;
|
|
|
|
DbgPrint("[IP, FlushAllQueues] Completing Listen request for Connection = 0x%x\n", Bucket->AssociatedEndpoint);
|
|
|
|
DereferenceObject(Bucket->AssociatedEndpoint);
|
|
CompleteBucket(Connection, Bucket, FALSE);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
|
|
{
|
|
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
|
|
|
|
Bucket->Status = Status;
|
|
Bucket->Information = 0;
|
|
|
|
DbgPrint("[IP, FlushAllQueues] Completing Connection request for Connection = 0x%x\n", Bucket->AssociatedEndpoint);
|
|
|
|
CompleteBucket(Connection, Bucket, FALSE);
|
|
}
|
|
|
|
DereferenceObject(Connection);
|
|
|
|
DbgPrint("[IP, FlushAllQueues] Leaving\n");
|
|
}
|
|
|
|
VOID
|
|
TCPFinEventHandler(void *arg, err_t err)
|
|
{
|
|
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
|
|
const NTSTATUS status = TCPTranslateError(err);
|
|
|
|
DbgPrint("[IP, TCPFinEventHandler] Called for Connection( 0x%x )-> SocketContext = pcb (0x%x)\n", Connection, Connection->SocketContext);
|
|
|
|
/* Only clear the pointer if the shutdown was caused by an error */
|
|
if ((err != ERR_OK))
|
|
{
|
|
/* We're already closed by the error so we don't want to call lwip_close */
|
|
DbgPrint("[IP, TCPFinEventHandler] MAKING Connection( 0x%x )-> SocketContext = pcb (0x%x) NULL\n", Connection, Connection->SocketContext);
|
|
|
|
Connection->SocketContext = NULL;
|
|
}
|
|
|
|
FlushAllQueues(Connection, status);
|
|
|
|
DbgPrint("[IP, TCPFinEventHandler] Done\n");
|
|
}
|
|
|
|
VOID
|
|
TCPAcceptEventHandler(void *arg, struct tcp_pcb *newpcb)
|
|
{
|
|
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
|
|
PTDI_BUCKET Bucket;
|
|
PLIST_ENTRY Entry;
|
|
PIRP Irp;
|
|
NTSTATUS Status;
|
|
KIRQL OldIrql;
|
|
|
|
DbgPrint("[IP, TCPAcceptEventHandler] Called\n");
|
|
|
|
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;
|
|
|
|
DbgPrint("[IP, TCPAcceptEventHandler] Completing accept event %x\n", Status);
|
|
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
DbgPrint("[IP, TCPAcceptEventHandler] newpcb->state = %s, listen_pcb->state = %s, newpcb = 0x%x\n",
|
|
tcp_state_str[newpcb->state],
|
|
tcp_state_str[((struct tcp_pcb*)Connection->SocketContext)->state],
|
|
newpcb);
|
|
|
|
LockObject(Bucket->AssociatedEndpoint, &OldIrql);
|
|
|
|
/* sanity assert...this should never be in anything else but a CLOSED state */
|
|
ASSERT(((struct tcp_pcb*)Bucket->AssociatedEndpoint->SocketContext)->state == CLOSED);
|
|
|
|
/* free socket context created in FileOpenConnection, as we're using a new one */
|
|
LibTCPClose(Bucket->AssociatedEndpoint, TRUE);
|
|
|
|
/* 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);
|
|
|
|
DbgPrint("[IP, TCPAcceptEventHandler] Trying to unlock Bucket->AssociatedEndpoint\n");
|
|
UnlockObject(Bucket->AssociatedEndpoint, OldIrql);
|
|
}
|
|
|
|
DereferenceObject(Bucket->AssociatedEndpoint);
|
|
|
|
DbgPrint("[IP, TCPAcceptEventHandler] Done!\n");
|
|
|
|
CompleteBucket(Connection, Bucket, FALSE);
|
|
}
|
|
|
|
DereferenceObject(Connection);
|
|
}
|
|
|
|
VOID
|
|
TCPSendEventHandler(void *arg, u16_t space)
|
|
{
|
|
PCONNECTION_ENDPOINT Connection = arg;
|
|
PTDI_BUCKET Bucket;
|
|
PLIST_ENTRY Entry;
|
|
PIRP Irp;
|
|
NTSTATUS Status;
|
|
PMDL Mdl;
|
|
|
|
DbgPrint("[IP, TCPSendEventHandler] Called\n");
|
|
|
|
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,
|
|
("Getting the user buffer from %x\n", Mdl));
|
|
|
|
NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
|
|
|
|
TI_DbgPrint(DEBUG_TCP,
|
|
("Writing %d bytes to %x\n", SendLen, SendBuffer));
|
|
DbgPrint("[IP, TCPSendEventHandler] Sending out &d bytes on pcb = 0x%x\n",
|
|
Connection->SocketContext);
|
|
|
|
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;
|
|
|
|
DbgPrint("Completing send req %x\n", Status);
|
|
|
|
CompleteBucket(Connection, Bucket, FALSE);
|
|
}
|
|
}
|
|
|
|
DereferenceObject(Connection);
|
|
|
|
DbgPrint("[IP, TCPSendEventHandler] Leaving\n");
|
|
}
|
|
|
|
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);
|
|
|
|
DbgPrint("[IP, TCPRecvEventHandler] Called\n");
|
|
|
|
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)
|
|
{
|
|
DbgPrint("[IP, TCPRecvEventHandler] 0x%x: Copying %d bytes to 0x%x from 0x%x\n",
|
|
p, p->len, ((PUCHAR)RecvBuffer) + Received, p->payload);
|
|
|
|
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);
|
|
|
|
DbgPrint("[IP, TCPRecvEventHandler] Leaving\n");
|
|
|
|
return Received;
|
|
}
|
|
|
|
VOID
|
|
TCPConnectEventHandler(void *arg, err_t err)
|
|
{
|
|
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
|
|
PTDI_BUCKET Bucket;
|
|
PLIST_ENTRY Entry;
|
|
|
|
DbgPrint("[IP, TCPConnectEventHandler] Called\n");
|
|
|
|
ReferenceObject(Connection);
|
|
|
|
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
|
|
{
|
|
|
|
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
|
|
|
|
Bucket->Status = TCPTranslateError(err);
|
|
Bucket->Information = 0;
|
|
|
|
DbgPrint("[IP, TCPConnectEventHandler] Completing connection request! (0x%x)\n", err);
|
|
|
|
CompleteBucket(Connection, Bucket, FALSE);
|
|
}
|
|
|
|
DbgPrint("[IP, TCPConnectEventHandler] Done\n");
|
|
|
|
DereferenceObject(Connection);
|
|
}
|