2081 lines
76 KiB
C
2081 lines
76 KiB
C
/********************************************************************/
|
|
/** Microsoft LAN Manager **/
|
|
/** Copyright(c) Microsoft Corp., 1990-2000 **/
|
|
/********************************************************************/
|
|
/* :ts=4 */
|
|
|
|
//** TCPDELIV.C - TCP deliver data code.
|
|
//
|
|
// This file contains the code for delivering data to the user, including
|
|
// putting data into recv. buffers and calling indication handlers.
|
|
//
|
|
|
|
#include "precomp.h"
|
|
#include "addr.h"
|
|
#include "tcp.h"
|
|
#include "tcb.h"
|
|
#include "tcprcv.h"
|
|
#include "tcpsend.h"
|
|
#include "tcpconn.h"
|
|
#include "tcpdeliv.h"
|
|
#include "tlcommon.h"
|
|
#include "tcpipbuf.h"
|
|
#include "pplasl.h"
|
|
#include "mdl2ndis.h"
|
|
|
|
extern TCPConnBlock **ConnTable;
|
|
extern HANDLE TcpRequestPool;
|
|
|
|
extern BOOLEAN
|
|
PutOnRAQ(TCB * RcvTCB, TCPRcvInfo * RcvInfo, IPRcvBuf * RcvBuf, uint Size);
|
|
|
|
NTSTATUS
|
|
TCPPrepareIrpForCancel(
|
|
PTCP_CONTEXT TcpContext,
|
|
PIRP Irp,
|
|
PDRIVER_CANCEL CancelRoutine
|
|
);
|
|
|
|
ULONG
|
|
TCPGetMdlChainByteCount(
|
|
PMDL Mdl
|
|
);
|
|
|
|
NTSTATUS
|
|
TCPDataRequestComplete(
|
|
void *Context,
|
|
unsigned int Status,
|
|
unsigned int ByteCount
|
|
);
|
|
|
|
VOID
|
|
TCPCancelRequest(
|
|
PDEVICE_OBJECT Device,
|
|
PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
CompleteRcvs(TCB * CmpltTCB);
|
|
|
|
|
|
#if DBG
|
|
ULONG DbgChainedRcvPends;
|
|
ULONG DbgChainedRcvNonPends;
|
|
ULONG DbgRegularRcv;
|
|
#endif
|
|
|
|
//* FreeRcvReq - Free a rcv request structure.
|
|
//
|
|
// Called to free a rcv request structure.
|
|
//
|
|
// Input: FreedReq - Rcv request structure to be freed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
__inline
|
|
VOID
|
|
FreeRcvReq(TCPRcvReq * Request)
|
|
{
|
|
CTEStructAssert(Request, trr);
|
|
PplFree(TcpRequestPool, Request);
|
|
}
|
|
|
|
//* GetRcvReq - Get a recv. request structure.
|
|
//
|
|
// Called to get a rcv. request structure.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: Pointer to RcvReq structure, or NULL if none.
|
|
//
|
|
__inline
|
|
TCPRcvReq *
|
|
GetRcvReq(VOID)
|
|
{
|
|
TCPRcvReq *Request;
|
|
LOGICAL FromList;
|
|
|
|
Request = PplAllocate(TcpRequestPool, &FromList);
|
|
if (Request) {
|
|
#if DBG
|
|
Request->trr_sig = trr_signature;
|
|
#endif
|
|
}
|
|
|
|
return Request;
|
|
}
|
|
|
|
//* FindLastBuffer - Find the last buffer in a chain.
|
|
//
|
|
// A utility routine to find the last buffer in an rb chain.
|
|
//
|
|
// Input: Buf - Pointer to RB chain.
|
|
//
|
|
// Returns: Pointer to last buf in chain.
|
|
//
|
|
IPRcvBuf *
|
|
FindLastBuffer(IPRcvBuf * Buf)
|
|
{
|
|
ASSERT(Buf != NULL);
|
|
|
|
while (Buf->ipr_next != NULL)
|
|
Buf = Buf->ipr_next;
|
|
|
|
return Buf;
|
|
}
|
|
|
|
//* FreePartialRB - Free part of an RB chain.
|
|
//
|
|
// Called to adjust an free part of an RB chain. We walk down the chain,
|
|
// trying to free buffers.
|
|
//
|
|
// Input: RB - RB chain to be adjusted.
|
|
// Size - Size in bytes to be freed.
|
|
//
|
|
// Returns: Pointer to adjusted RB chain.
|
|
//
|
|
IPRcvBuf *
|
|
FreePartialRB(IPRcvBuf * RB, uint Size)
|
|
{
|
|
while (Size != 0) {
|
|
IPRcvBuf *TempBuf;
|
|
|
|
ASSERT(RB != NULL);
|
|
|
|
if (Size >= RB->ipr_size) {
|
|
Size -= RB->ipr_size;
|
|
TempBuf = RB;
|
|
RB = RB->ipr_next;
|
|
if (TempBuf->ipr_owner == IPR_OWNER_TCP)
|
|
FreeTcpIpr(TempBuf);
|
|
} else {
|
|
RB->ipr_size -= Size;
|
|
RB->ipr_buffer += Size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(RB != NULL);
|
|
|
|
return RB;
|
|
|
|
}
|
|
|
|
//* CopyRBChain - Copy a chain of IP rcv buffers.
|
|
//
|
|
// Called to copy a chain of IP rcv buffers. We don't copy a buffer if it's
|
|
// already owner by TCP. We assume that all non-TCP owned buffers start
|
|
// before any TCP owner buffers, so we quit when we copy to a TCP owner buffer.
|
|
//
|
|
// Input: OrigBuf - Buffer chain to copy from.
|
|
// LastBuf - Where to return pointer to last buffer in
|
|
// chain.
|
|
// Size - Maximum size in bytes to copy.
|
|
//
|
|
// Returns: Pointer to new buffer chain.
|
|
//
|
|
IPRcvBuf *
|
|
CopyRBChain(IPRcvBuf * OrigBuf, IPRcvBuf ** LastBuf, uint Size)
|
|
{
|
|
IPRcvBuf *FirstBuf, *EndBuf;
|
|
uint BytesToCopy;
|
|
|
|
ASSERT(OrigBuf != NULL);
|
|
ASSERT(Size > 0);
|
|
|
|
if (OrigBuf->ipr_owner != IPR_OWNER_TCP) {
|
|
|
|
BytesToCopy = MIN(Size, OrigBuf->ipr_size);
|
|
FirstBuf = AllocTcpIpr(BytesToCopy, 'BPCT');
|
|
if (FirstBuf != NULL) {
|
|
|
|
EndBuf = FirstBuf;
|
|
RtlCopyMemory(FirstBuf->ipr_buffer, OrigBuf->ipr_buffer,
|
|
BytesToCopy);
|
|
Size -= BytesToCopy;
|
|
OrigBuf = OrigBuf->ipr_next;
|
|
while (OrigBuf != NULL && OrigBuf->ipr_owner != IPR_OWNER_TCP
|
|
&& Size != 0) {
|
|
IPRcvBuf *NewBuf;
|
|
|
|
BytesToCopy = MIN(Size, OrigBuf->ipr_size);
|
|
NewBuf = AllocTcpIpr(BytesToCopy, 'BPCT');
|
|
if (NewBuf != NULL) {
|
|
RtlCopyMemory(NewBuf->ipr_buffer, OrigBuf->ipr_buffer,
|
|
BytesToCopy);
|
|
EndBuf->ipr_next = NewBuf;
|
|
EndBuf = NewBuf;
|
|
Size -= BytesToCopy;
|
|
OrigBuf = OrigBuf->ipr_next;
|
|
} else {
|
|
FreeRBChain(FirstBuf);
|
|
return NULL;
|
|
}
|
|
}
|
|
EndBuf->ipr_next = OrigBuf;
|
|
} else
|
|
return NULL;
|
|
} else {
|
|
FirstBuf = OrigBuf;
|
|
EndBuf = OrigBuf;
|
|
if (Size < OrigBuf->ipr_size)
|
|
OrigBuf->ipr_size = Size;
|
|
Size -= OrigBuf->ipr_size;
|
|
}
|
|
|
|
// Now walk down the chain, until we run out of
|
|
// Size. At this point, Size is the bytes left to 'copy' (it may be 0),
|
|
// and the sizes in buffers FirstBuf...EndBuf are correct.
|
|
while (Size != 0) {
|
|
|
|
EndBuf = EndBuf->ipr_next;
|
|
ASSERT(EndBuf != NULL);
|
|
|
|
if (Size < EndBuf->ipr_size)
|
|
EndBuf->ipr_size = Size;
|
|
|
|
Size -= EndBuf->ipr_size;
|
|
}
|
|
|
|
// If there's anything left in the chain, free it now.
|
|
if (EndBuf->ipr_next != NULL) {
|
|
FreeRBChain(EndBuf->ipr_next);
|
|
EndBuf->ipr_next = NULL;
|
|
}
|
|
*LastBuf = EndBuf;
|
|
|
|
return FirstBuf;
|
|
}
|
|
|
|
//* PendData - Pend incoming data to a client.
|
|
//
|
|
// Called when we need to buffer data for a client because there's no receive
|
|
// down and we can't indicate.
|
|
//
|
|
// The TCB lock is held throughout this procedure. If this is to be changed,
|
|
// make sure consistency of tcb_pendingcnt is preserved. This routine is
|
|
// always called at DPC level.
|
|
//
|
|
// Input: RcvTCB - TCB on which to receive the data.
|
|
// RcvFlags - TCP flags for the incoming packet.
|
|
// InBuffer - Input buffer of packet.
|
|
// Size - Size in bytes of data in InBuffer.
|
|
//
|
|
// Returns: Number of bytes of data taken.
|
|
//
|
|
uint
|
|
PendData(TCB * RcvTCB, uint RcvFlags, IPRcvBuf * InBuffer, uint Size)
|
|
{
|
|
IPRcvBuf *NewBuf, *LastBuf;
|
|
|
|
CTEStructAssert(RcvTCB, tcb);
|
|
ASSERT(Size > 0);
|
|
ASSERT(InBuffer != NULL);
|
|
|
|
ASSERT(RcvTCB->tcb_refcnt != 0);
|
|
ASSERT(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV);
|
|
//ASSERT(RcvTCB->tcb_currcv == NULL);
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
|
|
|
|
CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
|
|
|
|
NewBuf = CopyRBChain(InBuffer, &LastBuf, Size);
|
|
if (NewBuf != NULL) {
|
|
// We have a duplicate chain. Put it on the end of the
|
|
// pending q.
|
|
if (RcvTCB->tcb_pendhead == NULL) {
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
RcvTCB->tcb_pendtail = LastBuf;
|
|
} else {
|
|
RcvTCB->tcb_pendtail->ipr_next = NewBuf;
|
|
RcvTCB->tcb_pendtail = LastBuf;
|
|
}
|
|
RcvTCB->tcb_pendingcnt += Size;
|
|
} else {
|
|
FreeRBChain(InBuffer);
|
|
Size = 0;
|
|
}
|
|
|
|
CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
|
|
|
|
return Size;
|
|
}
|
|
|
|
//* BufferData - Put incoming data into client's buffer.
|
|
//
|
|
// Called when we believe we have a buffer into which we can put data. We put
|
|
// it in there, and if we've filled the buffer or the incoming data has the
|
|
// push flag set we'll mark the TCB to return the buffer. Otherwise we'll
|
|
// get out and return the data later.
|
|
//
|
|
// In NT, this routine is called with the TCB lock held, and holds it for
|
|
// the duration of the call. This is important to ensure consistency of
|
|
// the tcb_pendingcnt field. If we need to change this to free the lock
|
|
// partway through, make sure to take this into account. In particular,
|
|
// TdiReceive zeros pendingcnt before calling this routine, and this routine
|
|
// may update it. If the lock is freed in here there would be a window where
|
|
// we really do have pending data, but it's not on the list or reflected in
|
|
// pendingcnt. This could mess up our windowing computations, and we'd have
|
|
// to be careful not to end up with more data pending than our window allows.
|
|
//
|
|
// Input: RcvTCB - TCB on which to receive the data.
|
|
// RcvFlags - TCP rcv flags for the incoming packet.
|
|
// InBuffer - Input buffer of packet.
|
|
// Size - Size in bytes of data in InBuffer.
|
|
//
|
|
// Returns: Number of bytes of data taken.
|
|
//
|
|
uint
|
|
BufferData(TCB * RcvTCB, uint RcvFlags, IPRcvBuf * InBuffer, uint Size)
|
|
{
|
|
uchar *DestPtr; // Destination pointer.
|
|
uchar *SrcPtr; // Src pointer.
|
|
uint SrcSize, DestSize = 0; // Sizes of current source and
|
|
// destination buffers.
|
|
uint Copied; // Total bytes to copy.
|
|
uint BytesToCopy; // Bytes of data to copy this time.
|
|
TCPRcvReq *DestReq; // Current receive request.
|
|
IPRcvBuf *SrcBuf; // Current source buffer.
|
|
PNDIS_BUFFER DestBuf; // Current receive buffer.
|
|
uint RcvCmpltd;
|
|
uint Flags;
|
|
|
|
CTEStructAssert(RcvTCB, tcb);
|
|
ASSERT(Size > 0);
|
|
ASSERT(InBuffer != NULL);
|
|
|
|
ASSERT(RcvTCB->tcb_refcnt != 0);
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
|
|
|
|
// In order to copy the received data to the application's buffers,
|
|
// we now need to map those buffers into the system's address space.
|
|
// Rather than attempting to map them below, where the going gets rough,
|
|
// we do it up-front where errors may be more readily handled.
|
|
//
|
|
// N.B. We map one buffer beyond what we need, since the code below
|
|
// will update the current receive-request to point beyond the data copied.
|
|
|
|
Copied = 0;
|
|
for (DestReq = RcvTCB->tcb_currcv; DestReq; DestReq = DestReq->trr_next) {
|
|
|
|
uint DestAvail = DestReq->trr_size - DestReq->trr_amt;
|
|
|
|
for (DestBuf = DestReq->trr_buffer, DestSize = DestReq->trr_offset;
|
|
DestBuf && DestAvail && Copied < Size;
|
|
DestBuf = NDIS_BUFFER_LINKAGE(DestBuf), DestSize = 0) {
|
|
if (!TcpipBufferVirtualAddress(DestBuf, NormalPagePriority)) {
|
|
return 0;
|
|
}
|
|
DestSize = MIN(NdisBufferLength(DestBuf) - DestSize, DestAvail);
|
|
DestAvail -= DestSize;
|
|
Copied += DestSize;
|
|
}
|
|
|
|
if (Copied >= Size) {
|
|
// We've mapped the space into which we'll copy;
|
|
// now map the space immediately beyond that.
|
|
if (DestAvail) {
|
|
// We believe space remains in the current receive-request;
|
|
// DestBuf should point to the current buffer.
|
|
ASSERT(DestBuf);
|
|
} else if ((DestReq = DestReq->trr_next) != NULL) {
|
|
// No more space in that receive-request, but there's another;
|
|
// Move to this next one, and map the start of that.
|
|
DestBuf = DestReq->trr_buffer;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (!TcpipBufferVirtualAddress(DestBuf, NormalPagePriority)) {
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Copied = 0;
|
|
RcvCmpltd = 0;
|
|
|
|
DestReq = RcvTCB->tcb_currcv;
|
|
|
|
ASSERT(DestReq != NULL);
|
|
CTEStructAssert(DestReq, trr);
|
|
|
|
DestBuf = DestReq->trr_buffer;
|
|
|
|
DestSize = MIN(NdisBufferLength(DestBuf) - DestReq->trr_offset,
|
|
DestReq->trr_size - DestReq->trr_amt);
|
|
DestPtr = (uchar *) NdisBufferVirtualAddress(DestBuf) + DestReq->trr_offset;
|
|
|
|
SrcBuf = InBuffer;
|
|
SrcSize = SrcBuf->ipr_size;
|
|
SrcPtr = SrcBuf->ipr_buffer;
|
|
|
|
Flags = (RcvFlags & TCP_FLAG_PUSH) ? TRR_PUSHED : 0;
|
|
RcvCmpltd = Flags;
|
|
DestReq->trr_flags |= Flags;
|
|
|
|
do {
|
|
|
|
BytesToCopy = MIN(Size - Copied, MIN(SrcSize, DestSize));
|
|
|
|
RtlCopyMemory(DestPtr, SrcPtr, BytesToCopy);
|
|
Copied += BytesToCopy;
|
|
DestReq->trr_amt += BytesToCopy;
|
|
|
|
// Update our source pointers.
|
|
if ((SrcSize -= BytesToCopy) == 0) {
|
|
IPRcvBuf *TempBuf;
|
|
|
|
// We've copied everything in this buffer.
|
|
TempBuf = SrcBuf;
|
|
SrcBuf = SrcBuf->ipr_next;
|
|
if (Size != Copied) {
|
|
ASSERT(SrcBuf != NULL);
|
|
SrcSize = SrcBuf->ipr_size;
|
|
SrcPtr = SrcBuf->ipr_buffer;
|
|
}
|
|
if (TempBuf->ipr_owner == IPR_OWNER_TCP)
|
|
FreeTcpIpr(TempBuf);
|
|
} else
|
|
SrcPtr += BytesToCopy;
|
|
|
|
// Now check the destination pointer, and update it if we need to.
|
|
if ((DestSize -= BytesToCopy) == 0) {
|
|
uint DestAvail;
|
|
|
|
// Exhausted this buffer. See if there's another one.
|
|
DestAvail = DestReq->trr_size - DestReq->trr_amt;
|
|
DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
|
|
|
|
if (DestBuf != NULL && (DestAvail != 0)) {
|
|
// Have another buffer in the chain. Update things.
|
|
DestSize = MIN(NdisBufferLength(DestBuf), DestAvail);
|
|
DestPtr = (uchar *) NdisBufferVirtualAddress(DestBuf);
|
|
} else {
|
|
// No more buffers in the chain. See if we have another buffer
|
|
// on the list.
|
|
DestReq->trr_flags |= TRR_PUSHED;
|
|
|
|
// If we've been told there's to be no back traffic, get an ACK
|
|
// going right away.
|
|
if (DestReq->trr_flags & TDI_RECEIVE_NO_RESPONSE_EXP)
|
|
DelayAction(RcvTCB, NEED_ACK);
|
|
|
|
RcvCmpltd = TRUE;
|
|
DestReq = DestReq->trr_next;
|
|
if (DestReq != NULL) {
|
|
DestBuf = DestReq->trr_buffer;
|
|
DestSize = MIN(NdisBufferLength(DestBuf), DestReq->trr_size);
|
|
DestPtr = (uchar *) NdisBufferVirtualAddress(DestBuf);
|
|
|
|
// If we have more to put into here, set the flags.
|
|
if (Copied != Size)
|
|
DestReq->trr_flags |= Flags;
|
|
|
|
} else {
|
|
// All out of buffer space. Reset the data handler pointer.
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
// Current buffer not empty yet.
|
|
DestPtr += BytesToCopy;
|
|
|
|
// If we've copied all that we need to, we're done.
|
|
} while (Copied != Size);
|
|
|
|
// We've finished copying, and have a few more things to do. We need to
|
|
// update the current rcv. pointer and possibly the offset in the
|
|
// recv. request. If we need to complete any receives we have to schedule
|
|
// that. If there's any data we couldn't copy we'll need to dispose of
|
|
// it.
|
|
RcvTCB->tcb_currcv = DestReq;
|
|
if (DestReq != NULL) {
|
|
DestReq->trr_buffer = DestBuf;
|
|
DestReq->trr_offset = (uint) (DestPtr - (uchar *) NdisBufferVirtualAddress(DestBuf));
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
} else
|
|
RcvTCB->tcb_rcvhndlr = PendData;
|
|
|
|
RcvTCB->tcb_indicated -= MIN(Copied, RcvTCB->tcb_indicated);
|
|
|
|
if (Size != Copied) {
|
|
IPRcvBuf *NewBuf, *LastBuf;
|
|
|
|
ASSERT(DestReq == NULL);
|
|
|
|
RcvTCB->tcb_moreflag = 1;
|
|
|
|
// We have data to dispose of. Update the first buffer of the chain
|
|
// with the current src pointer and size, and copy it.
|
|
ASSERT(SrcSize <= SrcBuf->ipr_size);
|
|
ASSERT(
|
|
((uint) (SrcPtr - SrcBuf->ipr_buffer)) ==
|
|
(SrcBuf->ipr_size - SrcSize)
|
|
);
|
|
|
|
SrcBuf->ipr_buffer = SrcPtr;
|
|
SrcBuf->ipr_size = SrcSize;
|
|
|
|
NewBuf = CopyRBChain(SrcBuf, &LastBuf, Size - Copied);
|
|
if (NewBuf != NULL) {
|
|
// We managed to copy the buffer. Push it on the pending queue.
|
|
if (RcvTCB->tcb_pendhead == NULL) {
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
RcvTCB->tcb_pendtail = LastBuf;
|
|
} else {
|
|
LastBuf->ipr_next = RcvTCB->tcb_pendhead;
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
}
|
|
RcvTCB->tcb_pendingcnt += Size - Copied;
|
|
Copied = Size;
|
|
|
|
CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
|
|
|
|
} else
|
|
FreeRBChain(SrcBuf);
|
|
} else {
|
|
// We copied Size bytes, but the chain could be longer than that. Free
|
|
// it if we need to.
|
|
if (SrcBuf != NULL)
|
|
FreeRBChain(SrcBuf);
|
|
}
|
|
|
|
if (RcvCmpltd != 0) {
|
|
DelayAction(RcvTCB, NEED_RCV_CMPLT);
|
|
} else {
|
|
//instrumentation to catch conreq null in tcb.c
|
|
// ASSERT(DestReq);
|
|
// ASSERT(DestReq->trr_amt);
|
|
// RcvTCB->tcb_lastreq = DestReq;
|
|
START_TCB_TIMER_R(RcvTCB, PUSH_TIMER, PUSH_TO);
|
|
}
|
|
|
|
return Copied;
|
|
|
|
}
|
|
|
|
//* IndicateData - Indicate incoming data to a client.
|
|
//
|
|
// Called when we need to indicate data to an upper layer client. We'll pass
|
|
// up a pointer to whatever we have available, and the client may take some
|
|
// or all of it.
|
|
//
|
|
// Input: RcvTCB - TCB on which to receive the data.
|
|
// RcvFlags - TCP rcv flags for the incoming packet.
|
|
// InBuffer - Input buffer of packet.
|
|
// Size - Size in bytes of data in InBuffer.
|
|
//
|
|
// Returns: Number of bytes of data taken.
|
|
//
|
|
uint
|
|
IndicateData(TCB * RcvTCB, uint RcvFlags, IPRcvBuf * InBuffer, uint Size)
|
|
{
|
|
TDI_STATUS Status;
|
|
PRcvEvent Event;
|
|
PVOID EventContext, ConnContext;
|
|
uint BytesTaken = 0;
|
|
#if MILLEN
|
|
EventRcvBuffer ERB;
|
|
#else // MILLEN
|
|
EventRcvBuffer *ERB = NULL;
|
|
PTDI_REQUEST_KERNEL_RECEIVE RequestInformation;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
#endif // !MILLEN
|
|
TCPRcvReq *RcvReq;
|
|
IPRcvBuf *NewBuf;
|
|
ulong IndFlags;
|
|
#if TRACE_EVENT
|
|
PTDI_DATA_REQUEST_NOTIFY_ROUTINE CPCallBack;
|
|
WMIData WMIInfo;
|
|
#endif
|
|
|
|
IPRcvBuf *LastBuf;
|
|
|
|
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+IndicateData\n")));
|
|
|
|
CTEStructAssert(RcvTCB, tcb);
|
|
ASSERT(Size > 0);
|
|
ASSERT(InBuffer != NULL);
|
|
|
|
ASSERT(RcvTCB->tcb_refcnt != 0);
|
|
ASSERT(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV);
|
|
ASSERT(RcvTCB->tcb_rcvind != NULL);
|
|
ASSERT(RcvTCB->tcb_rcvhead == NULL);
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == IndicateData);
|
|
|
|
Event = RcvTCB->tcb_rcvind;
|
|
EventContext = RcvTCB->tcb_ricontext;
|
|
ConnContext = RcvTCB->tcb_conncontext;
|
|
|
|
if (Size < InBuffer->ipr_size) {
|
|
uint NewRcvWin;
|
|
NewRcvWin = RcvWin(RcvTCB);
|
|
Size = MIN(InBuffer->ipr_size, NewRcvWin);
|
|
}
|
|
|
|
RcvTCB->tcb_indicated = Size;
|
|
RcvTCB->tcb_flags |= IN_RCV_IND;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
|
|
TCPTRACE((
|
|
"Indicating %lu bytes, %lu available\n",
|
|
InBuffer->ipr_size, Size
|
|
));
|
|
}
|
|
|
|
#if TCP_FLAG_PUSH >= TDI_RECEIVE_ENTIRE_MESSAGE
|
|
IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
|
|
TDI_RECEIVE_AT_DISPATCH_LEVEL |
|
|
((RcvFlags & TCP_FLAG_PUSH) >>
|
|
((TCP_FLAG_PUSH / TDI_RECEIVE_ENTIRE_MESSAGE) - 1));
|
|
#else
|
|
IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
|
|
TDI_RECEIVE_AT_DISPATCH_LEVEL |
|
|
((RcvFlags & TCP_FLAG_PUSH) <<
|
|
((TDI_RECEIVE_ENTIRE_MESSAGE / TCP_FLAG_PUSH) - 1));
|
|
#endif
|
|
|
|
#if DBG
|
|
DbgRegularRcv++;
|
|
#endif
|
|
|
|
if (InBuffer->ipr_pMdl && RcvTCB->tcb_chainedrcvind) {
|
|
|
|
PChainedRcvEvent ChainedEvent = RcvTCB->tcb_chainedrcvind;
|
|
CTEFreeLockFromDPC(&RcvTCB->tcb_lock, NULL);
|
|
ASSERT(InBuffer->ipr_size == Size);
|
|
|
|
Status = (*ChainedEvent) (RcvTCB->tcb_chainedrcvcontext, ConnContext,
|
|
IndFlags, InBuffer->ipr_size, InBuffer->ipr_RcvOffset,
|
|
InBuffer->ipr_pMdl, InBuffer->ipr_RcvContext);
|
|
|
|
|
|
#if TRACE_EVENT
|
|
CPCallBack = TCPCPHandlerRoutine;
|
|
if ((CPCallBack != NULL) && (Size > 0)) {
|
|
ulong GroupType;
|
|
|
|
WMIInfo.wmi_destaddr = RcvTCB->tcb_daddr;
|
|
WMIInfo.wmi_destport = RcvTCB->tcb_dport;
|
|
WMIInfo.wmi_srcaddr = RcvTCB->tcb_saddr;
|
|
WMIInfo.wmi_srcport = RcvTCB->tcb_sport;
|
|
WMIInfo.wmi_size = Size;
|
|
WMIInfo.wmi_context = RcvTCB->tcb_cpcontext;
|
|
|
|
GroupType = EVENT_TRACE_GROUP_TCPIP + EVENT_TRACE_TYPE_RECEIVE;
|
|
(*CPCallBack) (GroupType, (PVOID) &WMIInfo, sizeof(WMIInfo), NULL);
|
|
}
|
|
#endif
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
*InBuffer->ipr_pClientCnt = 1; //indicate to the ndis that
|
|
#if DBG
|
|
DbgChainedRcvPends++;
|
|
#endif
|
|
} else if (Status == TDI_SUCCESS) {
|
|
*InBuffer->ipr_pClientCnt = 0;
|
|
#if DBG
|
|
DbgChainedRcvNonPends++;
|
|
#endif
|
|
}
|
|
|
|
CTEGetLockAtDPC(&RcvTCB->tcb_lock, NULL);
|
|
RcvTCB->tcb_indicated = 0;
|
|
RcvTCB->tcb_flags &= ~IN_RCV_IND;
|
|
|
|
if (Status == TDI_NOT_ACCEPTED) {
|
|
BytesTaken = 0;
|
|
|
|
if ((RcvTCB->tcb_rcvhead != NULL) && (RcvTCB->tcb_currcv != NULL)) {
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
|
|
//ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
|
|
BytesTaken += BufferData(RcvTCB, RcvFlags, InBuffer,
|
|
Size - BytesTaken);
|
|
|
|
} else {
|
|
// Need to copy the chain and pend the data.
|
|
RcvTCB->tcb_rcvhndlr = PendData;
|
|
NewBuf = CopyRBChain(InBuffer, &LastBuf, Size - BytesTaken);
|
|
if (NewBuf != NULL) {
|
|
// We have a duplicate chain. Push it on the front of the
|
|
// pending q.
|
|
if (RcvTCB->tcb_pendhead == NULL) {
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
RcvTCB->tcb_pendtail = LastBuf;
|
|
} else {
|
|
LastBuf->ipr_next = RcvTCB->tcb_pendhead;
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
}
|
|
RcvTCB->tcb_pendingcnt += Size - BytesTaken;
|
|
BytesTaken = Size;
|
|
|
|
RcvTCB->tcb_moreflag = 3;
|
|
|
|
} else {
|
|
|
|
FreeRBChain(InBuffer);
|
|
}
|
|
}
|
|
return BytesTaken;
|
|
}
|
|
return Size;
|
|
|
|
}
|
|
if (!Event) {
|
|
|
|
// This is to safeguard against a timing window between the
|
|
// time NEED_RST is set and the tcb gets closed.
|
|
|
|
FreeRBChain(InBuffer);
|
|
return 0;
|
|
|
|
}
|
|
RcvReq = GetRcvReq();
|
|
if (RcvReq != NULL) {
|
|
// The indicate handler is saved in the TCB. Just call up into it.
|
|
CTEFreeLockFromDPC(&RcvTCB->tcb_lock, NULL);
|
|
|
|
Status = (*Event) (EventContext, ConnContext,
|
|
IndFlags, InBuffer->ipr_size, Size, &BytesTaken,
|
|
InBuffer->ipr_buffer, &ERB);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
|
|
TCPTRACE(("%lu bytes taken, status %lx\n", BytesTaken, Status));
|
|
}
|
|
|
|
// See what the client did. If the return status is MORE_PROCESSING,
|
|
// we've been given a buffer. In that case put it on the front of the
|
|
// buffer queue, and if all the data wasn't taken go ahead and copy
|
|
// it into the new buffer chain.
|
|
//
|
|
// Note that the size and buffer chain we're concerned with here is
|
|
// the one that we passed to the client. Since we're in a rcv. handler,
|
|
// any data that has come in would have been put on the reassembly
|
|
// queue.
|
|
|
|
#if TRACE_EVENT
|
|
CPCallBack = TCPCPHandlerRoutine;
|
|
if ((CPCallBack != NULL) && (BytesTaken > 0)) {
|
|
ulong GroupType;
|
|
|
|
WMIInfo.wmi_destaddr = RcvTCB->tcb_daddr;
|
|
WMIInfo.wmi_destport = RcvTCB->tcb_dport;
|
|
WMIInfo.wmi_srcaddr = RcvTCB->tcb_saddr;
|
|
WMIInfo.wmi_srcport = RcvTCB->tcb_sport;
|
|
WMIInfo.wmi_size = BytesTaken;
|
|
WMIInfo.wmi_context = RcvTCB->tcb_cpcontext;
|
|
|
|
GroupType = EVENT_TRACE_GROUP_TCPIP + EVENT_TRACE_TYPE_RECEIVE;
|
|
(*CPCallBack) (GroupType, (PVOID)&WMIInfo, sizeof(WMIInfo), NULL);
|
|
}
|
|
#endif
|
|
|
|
if (Status == TDI_MORE_PROCESSING) {
|
|
|
|
#if !MILLEN
|
|
ASSERT(ERB != NULL);
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(ERB);
|
|
|
|
Status = TCPPrepareIrpForCancel(
|
|
(PTCP_CONTEXT) IrpSp->FileObject->FsContext,
|
|
ERB,
|
|
TCPCancelRequest
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
PNDIS_BUFFER pNdisBuffer;
|
|
|
|
Status = ConvertMdlToNdisBuffer(ERB, ERB->MdlAddress, &pNdisBuffer);
|
|
ASSERT(Status == TDI_SUCCESS);
|
|
|
|
RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE)
|
|
& (IrpSp->Parameters);
|
|
|
|
RcvReq->trr_rtn = TCPDataRequestComplete;
|
|
RcvReq->trr_context = ERB;
|
|
RcvReq->trr_buffer = pNdisBuffer;
|
|
RcvReq->trr_size = RequestInformation->ReceiveLength;
|
|
RcvReq->trr_uflags = (ushort *)
|
|
& (RequestInformation->ReceiveFlags);
|
|
RcvReq->trr_flags = (uint) (RequestInformation->ReceiveFlags);
|
|
RcvReq->trr_offset = 0;
|
|
RcvReq->trr_amt = 0;
|
|
|
|
#else // !MILLEN
|
|
if (1) {
|
|
RcvReq->trr_rtn = ERB.erb_rtn;
|
|
RcvReq->trr_context = ERB.erb_context;
|
|
RcvReq->trr_buffer = ERB.erb_buffer;
|
|
RcvReq->trr_size = ERB.erb_size;
|
|
RcvReq->trr_uflags = ERB.erb_flags;
|
|
CTEAssert(ERB.erb_flags != NULL);
|
|
RcvReq->trr_flags = (uint) (*ERB.erb_flags);
|
|
RcvReq->trr_offset = 0;
|
|
RcvReq->trr_amt = 0;
|
|
#endif // MILLEN
|
|
|
|
CTEGetLockAtDPC(&RcvTCB->tcb_lock, NULL);
|
|
|
|
RcvTCB->tcb_flags &= ~IN_RCV_IND;
|
|
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == IndicateData);
|
|
|
|
// Push him on the front of the rcv. queue.
|
|
ASSERT((RcvTCB->tcb_currcv == NULL) ||
|
|
(RcvTCB->tcb_currcv->trr_amt == 0));
|
|
|
|
if (RcvTCB->tcb_rcvhead == NULL) {
|
|
RcvTCB->tcb_rcvhead = RcvReq;
|
|
RcvTCB->tcb_rcvtail = RcvReq;
|
|
RcvReq->trr_next = NULL;
|
|
} else {
|
|
RcvReq->trr_next = RcvTCB->tcb_rcvhead;
|
|
RcvTCB->tcb_rcvhead = RcvReq;
|
|
}
|
|
|
|
RcvTCB->tcb_currcv = RcvReq;
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
|
|
ASSERT(BytesTaken <= Size);
|
|
|
|
RcvTCB->tcb_indicated -= BytesTaken;
|
|
|
|
if ((Size -= BytesTaken) != 0) {
|
|
|
|
RcvTCB->tcb_moreflag = 2;
|
|
|
|
// Not everything was taken. Adjust the buffer chain to point
|
|
// beyond what was taken.
|
|
InBuffer = FreePartialRB(InBuffer, BytesTaken);
|
|
|
|
ASSERT(InBuffer != NULL);
|
|
|
|
// We've adjusted the buffer chain. Call the BufferData
|
|
// handler.
|
|
BytesTaken += BufferData(RcvTCB, RcvFlags, InBuffer, Size);
|
|
|
|
} else {
|
|
// All of the data was taken. Free the buffer chain.
|
|
FreeRBChain(InBuffer);
|
|
}
|
|
|
|
return BytesTaken;
|
|
#if !MILLEN
|
|
} else {
|
|
|
|
//
|
|
// The IRP was cancelled before it was handed back to us.
|
|
// We'll pretend we never saw it. TCPPrepareIrpForCancel
|
|
// already completed it. The client may have taken data,
|
|
// so we will act as if success was returned.
|
|
//
|
|
ERB = NULL;
|
|
Status = TDI_SUCCESS;
|
|
#endif // !MILLEN
|
|
}
|
|
|
|
|
|
}
|
|
CTEGetLockAtDPC(&RcvTCB->tcb_lock, NULL);
|
|
|
|
RcvTCB->tcb_flags &= ~IN_RCV_IND;
|
|
|
|
// Status is not more processing. If it's not SUCCESS, the client
|
|
// didn't take any of the data. In either case we now need to
|
|
// see if all of the data was taken. If it wasn't, we'll try and
|
|
// push it onto the front of the pending queue.
|
|
|
|
FreeRcvReq(RcvReq); // This won't be needed.
|
|
|
|
if (Status == TDI_NOT_ACCEPTED)
|
|
BytesTaken = 0;
|
|
|
|
ASSERT(BytesTaken <= Size);
|
|
|
|
RcvTCB->tcb_indicated -= BytesTaken;
|
|
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == IndicateData);
|
|
|
|
// Check to see if a rcv. buffer got posted during the indication.
|
|
// If it did, reset the recv. handler now.
|
|
if (RcvTCB->tcb_rcvhead != NULL)
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
|
|
// See if all of the data was taken.
|
|
if (BytesTaken == Size) {
|
|
ASSERT(RcvTCB->tcb_indicated == 0);
|
|
FreeRBChain(InBuffer);
|
|
return BytesTaken; // It was all taken.
|
|
|
|
}
|
|
// It wasn't all taken. Adjust for what was taken, and push
|
|
// on the front of the pending queue. We also need to check to
|
|
// see if a receive buffer got posted during the indication. This
|
|
// would be weird, but not impossible.
|
|
InBuffer = FreePartialRB(InBuffer, BytesTaken);
|
|
if (RcvTCB->tcb_rcvhead == NULL) {
|
|
|
|
RcvTCB->tcb_rcvhndlr = PendData;
|
|
NewBuf = CopyRBChain(InBuffer, &LastBuf, Size - BytesTaken);
|
|
if (NewBuf != NULL) {
|
|
// We have a duplicate chain. Push it on the front of the
|
|
// pending q.
|
|
if (RcvTCB->tcb_pendhead == NULL) {
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
RcvTCB->tcb_pendtail = LastBuf;
|
|
} else {
|
|
LastBuf->ipr_next = RcvTCB->tcb_pendhead;
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
}
|
|
RcvTCB->tcb_pendingcnt += Size - BytesTaken;
|
|
BytesTaken = Size;
|
|
|
|
RcvTCB->tcb_moreflag = 3;
|
|
|
|
} else {
|
|
|
|
FreeRBChain(InBuffer);
|
|
}
|
|
|
|
return BytesTaken;
|
|
} else {
|
|
// Just great. There's now a rcv. buffer on the TCB. Call the
|
|
// BufferData handler now.
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
|
|
|
|
BytesTaken += BufferData(RcvTCB, RcvFlags, InBuffer,
|
|
Size - BytesTaken);
|
|
|
|
return BytesTaken;
|
|
}
|
|
|
|
} else {
|
|
// Couldn't get a recv. request. We must be really low on resources,
|
|
// so just bail out now.
|
|
|
|
FreeRBChain(InBuffer);
|
|
return 0;
|
|
}
|
|
|
|
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-IndicateData\n")));
|
|
|
|
}
|
|
|
|
//* IndicatePendingData - Indicate pending data to a client.
|
|
//
|
|
// Called when we need to indicate pending data to an upper layer client,
|
|
// usually because data arrived when we were in a state that it couldn't
|
|
// be indicated.
|
|
//
|
|
// Many of the comments in the BufferData header about the consistency of
|
|
// tcb_pendingcnt apply here also.
|
|
//
|
|
// Input: RcvTCB - TCB on which to indicate the data.
|
|
// RcvReq - Rcv. req. to use to indicate it.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IndicatePendingData(TCB *RcvTCB, TCPRcvReq *RcvReq, CTELockHandle TCBHandle)
|
|
{
|
|
TDI_STATUS Status;
|
|
PRcvEvent Event;
|
|
PVOID EventContext, ConnContext;
|
|
#if !MILLEN
|
|
EventRcvBuffer *ERB = NULL;
|
|
PTDI_REQUEST_KERNEL_RECEIVE RequestInformation;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
#else // !MILLEN
|
|
EventRcvBuffer ERB;
|
|
#endif // MILLEN
|
|
IPRcvBuf *NewBuf;
|
|
uint Size;
|
|
uint BytesIndicated;
|
|
uint BytesAvailable;
|
|
uint BytesTaken = 0;
|
|
uchar *DataBuffer;
|
|
|
|
CTEStructAssert(RcvTCB, tcb);
|
|
|
|
ASSERT(RcvTCB->tcb_refcnt != 0);
|
|
ASSERT(RcvTCB->tcb_rcvind != NULL);
|
|
ASSERT(RcvTCB->tcb_rcvhead == NULL);
|
|
ASSERT(RcvTCB->tcb_pendingcnt != 0);
|
|
ASSERT(RcvReq != NULL);
|
|
|
|
for (;;) {
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
|
|
|
|
// The indicate handler is saved in the TCB. Just call up into it.
|
|
Event = RcvTCB->tcb_rcvind;
|
|
EventContext = RcvTCB->tcb_ricontext;
|
|
ConnContext = RcvTCB->tcb_conncontext;
|
|
BytesIndicated = RcvTCB->tcb_pendhead->ipr_size;
|
|
BytesAvailable = RcvTCB->tcb_pendingcnt;
|
|
DataBuffer = RcvTCB->tcb_pendhead->ipr_buffer;
|
|
|
|
RcvTCB->tcb_indicated = RcvTCB->tcb_pendingcnt;
|
|
RcvTCB->tcb_flags |= IN_RCV_IND;
|
|
RcvTCB->tcb_moreflag = 0;
|
|
|
|
CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
|
|
IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) {
|
|
TCPTRACE((
|
|
"Indicating pending %d bytes, %d available\n",
|
|
RcvTCB->tcb_pendhead->ipr_size, RcvTCB->tcb_pendingcnt
|
|
));
|
|
}
|
|
|
|
Status = (*Event) (EventContext, ConnContext,
|
|
TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
|
|
TDI_RECEIVE_ENTIRE_MESSAGE,
|
|
BytesIndicated, BytesAvailable, &BytesTaken,
|
|
DataBuffer, &ERB);
|
|
|
|
IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) {
|
|
TCPTRACE(("%d bytes taken\n", BytesTaken));
|
|
}
|
|
|
|
// See what the client did. If the return status is MORE_PROCESSING,
|
|
// we've been given a buffer. In that case put it on the front of the
|
|
// buffer queue, and if all the data wasn't taken go ahead and copy
|
|
// it into the new buffer chain.
|
|
if (Status == TDI_MORE_PROCESSING) {
|
|
|
|
#if !MILLEN
|
|
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
|
|
TCPTRACE(("more processing on receive\n"));
|
|
}
|
|
|
|
ASSERT(ERB != NULL);
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(ERB);
|
|
|
|
Status = TCPPrepareIrpForCancel(
|
|
(PTCP_CONTEXT) IrpSp->FileObject->FsContext,
|
|
ERB,
|
|
TCPCancelRequest
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
PNDIS_BUFFER pNdisBuffer;
|
|
|
|
Status = ConvertMdlToNdisBuffer(ERB, ERB->MdlAddress, &pNdisBuffer);
|
|
ASSERT(Status == TDI_SUCCESS);
|
|
|
|
RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE)
|
|
& (IrpSp->Parameters);
|
|
|
|
RcvReq->trr_rtn = TCPDataRequestComplete;
|
|
RcvReq->trr_context = ERB;
|
|
RcvReq->trr_buffer = pNdisBuffer;
|
|
RcvReq->trr_size = RequestInformation->ReceiveLength;
|
|
RcvReq->trr_uflags = (ushort *)
|
|
& (RequestInformation->ReceiveFlags);
|
|
RcvReq->trr_flags = (uint) (RequestInformation->ReceiveFlags);
|
|
RcvReq->trr_offset = 0;
|
|
RcvReq->trr_amt = 0;
|
|
#else // !MILLEN
|
|
if (1) {
|
|
RcvReq->trr_rtn = ERB.erb_rtn;
|
|
RcvReq->trr_context = ERB.erb_context;
|
|
RcvReq->trr_buffer = ERB.erb_buffer;
|
|
RcvReq->trr_size = ERB.erb_size;
|
|
RcvReq->trr_uflags = ERB.erb_flags;
|
|
RcvReq->trr_flags = (uint) (*ERB.erb_flags);
|
|
RcvReq->trr_offset = 0;
|
|
RcvReq->trr_amt = 0;
|
|
#endif // MILLLEN
|
|
|
|
CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle);
|
|
RcvTCB->tcb_flags &= ~IN_RCV_IND;
|
|
|
|
// Push him on the front of the rcv. queue.
|
|
ASSERT((RcvTCB->tcb_currcv == NULL) ||
|
|
(RcvTCB->tcb_currcv->trr_amt == 0));
|
|
|
|
if (RcvTCB->tcb_rcvhead == NULL) {
|
|
RcvTCB->tcb_rcvhead = RcvReq;
|
|
RcvTCB->tcb_rcvtail = RcvReq;
|
|
RcvReq->trr_next = NULL;
|
|
} else {
|
|
RcvReq->trr_next = RcvTCB->tcb_rcvhead;
|
|
RcvTCB->tcb_rcvhead = RcvReq;
|
|
}
|
|
|
|
RcvTCB->tcb_currcv = RcvReq;
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
|
|
// Have to pick up the new size and pointers now, since things could
|
|
// have changed during the upcall.
|
|
Size = RcvTCB->tcb_pendingcnt;
|
|
NewBuf = RcvTCB->tcb_pendhead;
|
|
|
|
RcvTCB->tcb_pendingcnt = 0;
|
|
RcvTCB->tcb_pendhead = NULL;
|
|
|
|
ASSERT(BytesTaken <= Size);
|
|
|
|
RcvTCB->tcb_indicated -= BytesTaken;
|
|
if ((Size -= BytesTaken) != 0) {
|
|
|
|
RcvTCB->tcb_moreflag = 4;
|
|
|
|
// Not everything was taken. Adjust the buffer chain to point
|
|
// beyond what was taken.
|
|
NewBuf = FreePartialRB(NewBuf, BytesTaken);
|
|
|
|
ASSERT(NewBuf != NULL);
|
|
|
|
// We've adjusted the buffer chain. Call the BufferData
|
|
// handler.
|
|
(void)BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf, Size);
|
|
CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
|
|
} else {
|
|
// All of the data was taken. Free the buffer chain. Since
|
|
// we were passed a buffer chain which we put on the head of
|
|
// the list, leave the rcvhandler pointing at BufferData.
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
|
|
ASSERT(RcvTCB->tcb_indicated == 0);
|
|
ASSERT(RcvTCB->tcb_rcvhead != NULL);
|
|
|
|
CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
FreeRBChain(NewBuf);
|
|
}
|
|
|
|
return;
|
|
#if !MILLEN
|
|
} else {
|
|
//
|
|
// The IRP was cancelled before it was handed back to us.
|
|
// We'll pretend we never saw it. TCPPrepareIrpForCancel
|
|
// already completed it. The client may have taken data,
|
|
// so we will act as if success was returned.
|
|
//
|
|
ERB = NULL;
|
|
Status = TDI_SUCCESS;
|
|
#endif // !MILLEN
|
|
}
|
|
|
|
}
|
|
CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle);
|
|
|
|
RcvTCB->tcb_flags &= ~IN_RCV_IND;
|
|
|
|
// Status is not more processing. If it's not SUCCESS, the client
|
|
// didn't take any of the data. In either case we now need to
|
|
// see if all of the data was taken. If it wasn't, we're done.
|
|
|
|
if (Status == TDI_NOT_ACCEPTED)
|
|
BytesTaken = 0;
|
|
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
|
|
|
|
RcvTCB->tcb_indicated -= BytesTaken;
|
|
Size = RcvTCB->tcb_pendingcnt;
|
|
NewBuf = RcvTCB->tcb_pendhead;
|
|
|
|
ASSERT(BytesTaken <= Size);
|
|
|
|
// See if all of the data was taken.
|
|
if (BytesTaken == Size) {
|
|
// It was all taken. Zap the pending data information.
|
|
RcvTCB->tcb_pendingcnt = 0;
|
|
RcvTCB->tcb_pendhead = NULL;
|
|
|
|
ASSERT(RcvTCB->tcb_indicated == 0);
|
|
if (RcvTCB->tcb_rcvhead == NULL) {
|
|
if (RcvTCB->tcb_rcvind != NULL)
|
|
RcvTCB->tcb_rcvhndlr = IndicateData;
|
|
} else
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
|
|
CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
FreeRBChain(NewBuf);
|
|
break;
|
|
}
|
|
// It wasn't all taken. Adjust for what was taken, We also need to check
|
|
// to see if a receive buffer got posted during the indication. This
|
|
// would be weird, but not impossible.
|
|
NewBuf = FreePartialRB(NewBuf, BytesTaken);
|
|
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
|
|
|
|
RcvTCB->tcb_moreflag = 5;
|
|
|
|
if (RcvTCB->tcb_rcvhead == NULL) {
|
|
|
|
RcvTCB->tcb_pendhead = NewBuf;
|
|
RcvTCB->tcb_pendingcnt -= BytesTaken;
|
|
if (RcvTCB->tcb_indicated != 0 || RcvTCB->tcb_rcvind == NULL) {
|
|
CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
break;
|
|
}
|
|
// From here, we'll loop around and indicate the new data that
|
|
// presumably came in during the previous indication.
|
|
} else {
|
|
// Just great. There's now a rcv. buffer on the TCB. Call the
|
|
// BufferData handler now.
|
|
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
RcvTCB->tcb_pendingcnt = 0;
|
|
RcvTCB->tcb_pendhead = NULL;
|
|
BytesTaken += BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf,
|
|
Size - BytesTaken);
|
|
CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
break;
|
|
}
|
|
|
|
} // for (;;)
|
|
|
|
FreeRcvReq(RcvReq); // This isn't needed anymore.
|
|
|
|
}
|
|
|
|
//* DeliverUrgent - Deliver urgent data to a client.
|
|
//
|
|
// Called to deliver urgent data to a client. We assume the input
|
|
// urgent data is in a buffer we can keep. The buffer can be NULL, in
|
|
// which case we'll just look on the urgent pending queue for data.
|
|
//
|
|
// Input: RcvTCB - TCB to deliver on.
|
|
// RcvBuf - RcvBuffer for urgent data.
|
|
// Size - Number of bytes of urgent data to deliver.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
DeliverUrgent(TCB * RcvTCB, IPRcvBuf * RcvBuf, uint Size,
|
|
CTELockHandle * TCBHandle)
|
|
{
|
|
CTELockHandle AOHandle, AOTblHandle, ConnHandle;
|
|
TCPRcvReq *RcvReq, *PrevReq;
|
|
uint BytesTaken = 0;
|
|
IPRcvBuf *LastBuf;
|
|
#if !MILLEN
|
|
EventRcvBuffer *ERB;
|
|
#else // !MILLEN
|
|
EventRcvBuffer ERB;
|
|
#endif // MILLEN
|
|
#if TRACE_EVENT
|
|
PTDI_DATA_REQUEST_NOTIFY_ROUTINE CPCallBack;
|
|
WMIData WMIInfo;
|
|
#endif
|
|
PRcvEvent ExpRcv;
|
|
PVOID ExpRcvContext;
|
|
PVOID ConnContext;
|
|
TDI_STATUS Status;
|
|
|
|
CTEStructAssert(RcvTCB, tcb);
|
|
ASSERT(RcvTCB->tcb_refcnt != 0);
|
|
|
|
CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
|
|
|
|
// See if we have new data, or are processing old data.
|
|
if (RcvBuf != NULL) {
|
|
// We have new data. If the pending queue is not NULL, or we're already
|
|
// in this routine, just put the buffer on the end of the queue.
|
|
if (RcvTCB->tcb_urgpending != NULL || (RcvTCB->tcb_flags & IN_DELIV_URG)) {
|
|
IPRcvBuf *PrevRcvBuf;
|
|
|
|
// Put him on the end of the queue.
|
|
PrevRcvBuf = STRUCT_OF(IPRcvBuf, &RcvTCB->tcb_urgpending, ipr_next);
|
|
while (PrevRcvBuf->ipr_next != NULL)
|
|
PrevRcvBuf = PrevRcvBuf->ipr_next;
|
|
|
|
PrevRcvBuf->ipr_next = RcvBuf;
|
|
RcvTCB->tcb_urgcnt += Size;
|
|
return;
|
|
}
|
|
} else {
|
|
// The input buffer is NULL. See if we have existing data, or are in
|
|
// this routine. If we have no existing data or are in this routine
|
|
// just return.
|
|
if (RcvTCB->tcb_urgpending == NULL ||
|
|
(RcvTCB->tcb_flags & IN_DELIV_URG)) {
|
|
return;
|
|
} else {
|
|
RcvBuf = RcvTCB->tcb_urgpending;
|
|
Size = RcvTCB->tcb_urgcnt;
|
|
RcvTCB->tcb_urgpending = NULL;
|
|
RcvTCB->tcb_urgcnt = 0;
|
|
}
|
|
}
|
|
|
|
ASSERT(RcvBuf != NULL);
|
|
ASSERT(!(RcvTCB->tcb_flags & IN_DELIV_URG));
|
|
|
|
// We know we have data to deliver, and we have a pointer and a size.
|
|
// Go into a loop, trying to deliver the data. On each iteration, we'll
|
|
// try to find a buffer for the data. If we find one, we'll copy and
|
|
// complete it right away. Otherwise we'll try and indicate it. If we
|
|
// can't indicate it, we'll put it on the pending queue and leave.
|
|
RcvTCB->tcb_flags |= IN_DELIV_URG;
|
|
RcvTCB->tcb_slowcount++;
|
|
RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
|
|
CheckTCBRcv(RcvTCB);
|
|
|
|
do {
|
|
CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
|
|
|
|
BytesTaken = 0;
|
|
|
|
// First check the expedited queue.
|
|
if ((RcvReq = RcvTCB->tcb_exprcv) != NULL)
|
|
RcvTCB->tcb_exprcv = RcvReq->trr_next;
|
|
else {
|
|
// Nothing in the expedited rcv. queue. Walk down the ordinary
|
|
// receive queue, looking for a buffer that we can steal.
|
|
PrevReq = STRUCT_OF(TCPRcvReq, &RcvTCB->tcb_rcvhead, trr_next);
|
|
RcvReq = PrevReq->trr_next;
|
|
while (RcvReq != NULL) {
|
|
CTEStructAssert(RcvReq, trr);
|
|
if (RcvReq->trr_flags & TDI_RECEIVE_EXPEDITED) {
|
|
// This is a candidate.
|
|
if (RcvReq->trr_amt == 0) {
|
|
|
|
ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
|
|
|
|
// And he has nothing currently in him. Pull him
|
|
// out of the queue.
|
|
if (RcvTCB->tcb_rcvtail == RcvReq) {
|
|
if (RcvTCB->tcb_rcvhead == RcvReq)
|
|
RcvTCB->tcb_rcvtail = NULL;
|
|
else
|
|
RcvTCB->tcb_rcvtail = PrevReq;
|
|
}
|
|
PrevReq->trr_next = RcvReq->trr_next;
|
|
if (RcvTCB->tcb_currcv == RcvReq) {
|
|
RcvTCB->tcb_currcv = RcvReq->trr_next;
|
|
if (RcvTCB->tcb_currcv == NULL) {
|
|
// We've taken the last receive from the list.
|
|
// Reset the rcvhndlr.
|
|
if (RcvTCB->tcb_rcvind != NULL &&
|
|
RcvTCB->tcb_indicated == 0)
|
|
RcvTCB->tcb_rcvhndlr = IndicateData;
|
|
else
|
|
RcvTCB->tcb_rcvhndlr = PendData;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
PrevReq = RcvReq;
|
|
RcvReq = PrevReq->trr_next;
|
|
}
|
|
}
|
|
|
|
// We've done our best to get a buffer. If we got one, copy into it
|
|
// now, and complete the request.
|
|
|
|
if (RcvReq != NULL) {
|
|
// Got a buffer.
|
|
CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
|
|
BytesTaken = CopyRcvToNdis(RcvBuf, RcvReq->trr_buffer, Size, 0, 0);
|
|
#if TRACE_EVENT
|
|
CPCallBack = TCPCPHandlerRoutine;
|
|
if (CPCallBack != NULL) {
|
|
ulong GroupType;
|
|
|
|
WMIInfo.wmi_destaddr = RcvTCB->tcb_daddr;
|
|
WMIInfo.wmi_destport = RcvTCB->tcb_dport;
|
|
WMIInfo.wmi_srcaddr = RcvTCB->tcb_saddr;
|
|
WMIInfo.wmi_srcport = RcvTCB->tcb_sport;
|
|
WMIInfo.wmi_size = BytesTaken;
|
|
WMIInfo.wmi_context = RcvTCB->tcb_cpcontext;
|
|
|
|
GroupType = EVENT_TRACE_GROUP_TCPIP + EVENT_TRACE_TYPE_RECEIVE;
|
|
(*CPCallBack) (GroupType, (PVOID) &WMIInfo, sizeof(WMIInfo), NULL);
|
|
}
|
|
#endif
|
|
(*RcvReq->trr_rtn) (RcvReq->trr_context, TDI_SUCCESS, BytesTaken);
|
|
FreeRcvReq(RcvReq);
|
|
CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind, BytesTaken);
|
|
|
|
} else {
|
|
// No posted buffer. If we can indicate, do so.
|
|
if (RcvTCB->tcb_urgind == 0) {
|
|
TCPConn *Conn;
|
|
|
|
// See if he has an expedited rcv handler.
|
|
ConnContext = RcvTCB->tcb_conncontext;
|
|
CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
|
|
CTEGetLock(&AddrObjTableLock.Lock, &AOTblHandle);
|
|
|
|
CTEGetLockAtDPC(&((ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)])->cb_lock), &ConnHandle);
|
|
#if DBG
|
|
ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)]->line = (uint) __LINE__;
|
|
ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)]->module = (uchar *) __FILE__;
|
|
#endif
|
|
//CTEGetLock(&ConnTableLock, &ConnHandle);
|
|
CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
if ((Conn = RcvTCB->tcb_conn) != NULL) {
|
|
CTEStructAssert(Conn, tc);
|
|
ASSERT(Conn->tc_tcb == RcvTCB);
|
|
CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
|
|
if (Conn->tc_ao != NULL) {
|
|
AddrObj *AO;
|
|
|
|
AO = Conn->tc_ao;
|
|
CTEGetLock(&AO->ao_lock, &AOHandle);
|
|
if (AO_VALID(AO) && (ExpRcv = AO->ao_exprcv) != NULL) {
|
|
ExpRcvContext = AO->ao_exprcvcontext;
|
|
CTEFreeLock(&AO->ao_lock, AOHandle);
|
|
|
|
// We're going to indicate.
|
|
RcvTCB->tcb_urgind = Size;
|
|
ASSERT(Conn->tc_ConnBlock == ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)]);
|
|
CTEFreeLockFromDPC(&(Conn->tc_ConnBlock->cb_lock), ConnHandle);
|
|
CTEFreeLock(&AddrObjTableLock.Lock, AOTblHandle);
|
|
|
|
Status = (*ExpRcv) (ExpRcvContext, ConnContext,
|
|
TDI_RECEIVE_COPY_LOOKAHEAD |
|
|
TDI_RECEIVE_ENTIRE_MESSAGE |
|
|
TDI_RECEIVE_EXPEDITED,
|
|
RcvBuf->ipr_size, Size, &BytesTaken,
|
|
RcvBuf->ipr_buffer, &ERB);
|
|
|
|
CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
|
|
// See what he did with it.
|
|
if (Status == TDI_MORE_PROCESSING) {
|
|
uint CopySize;
|
|
|
|
// He gave us a buffer.
|
|
if (BytesTaken == Size) {
|
|
// He gave us a buffer, but took all of
|
|
// it. We'll just return it to him.
|
|
CopySize = 0;
|
|
} else {
|
|
|
|
// We have some data to copy in.
|
|
RcvBuf = FreePartialRB(RcvBuf, BytesTaken);
|
|
|
|
#if !MILLEN
|
|
CopySize = CopyRcvToMdl(RcvBuf,
|
|
ERB->MdlAddress,
|
|
TCPGetMdlChainByteCount(ERB->MdlAddress),
|
|
0, 0);
|
|
#else //!MILLEN
|
|
CopySize = CopyRcvToNdis(RcvBuf,
|
|
ERB.erb_buffer,
|
|
ERB.erb_size,
|
|
0, 0);
|
|
#endif // MILLEN
|
|
|
|
}
|
|
BytesTaken += CopySize;
|
|
RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind,
|
|
BytesTaken);
|
|
CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
|
|
|
|
#if !MILLEN
|
|
ERB->IoStatus.Status = TDI_SUCCESS;
|
|
ERB->IoStatus.Information = CopySize;
|
|
IoCompleteRequest(ERB, 2);
|
|
#else // !MILLEN
|
|
(*ERB.erb_rtn) (ERB.erb_context, TDI_SUCCESS,
|
|
CopySize);
|
|
#endif // MILLEN
|
|
|
|
CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
|
|
} else {
|
|
|
|
// No buffer to deal with.
|
|
if (Status == TDI_NOT_ACCEPTED)
|
|
BytesTaken = 0;
|
|
|
|
RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind,
|
|
BytesTaken);
|
|
|
|
}
|
|
goto checksize;
|
|
} else // No rcv. handler.
|
|
|
|
CTEFreeLock(&AO->ao_lock, AOHandle);
|
|
}
|
|
// Conn->tc_ao == NULL.
|
|
ASSERT(Conn->tc_ConnBlock == ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)]);
|
|
CTEFreeLockFromDPC(&(Conn->tc_ConnBlock->cb_lock), ConnHandle);
|
|
//CTEFreeLock(&ConnTableLock, ConnHandle);
|
|
CTEFreeLock(&AddrObjTableLock.Lock, AOTblHandle);
|
|
CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
|
|
} else {
|
|
// RcvTCB has invalid index.
|
|
//CTEFreeLock(&ConnTableLock, *TCBHandle);
|
|
|
|
CTEFreeLockFromDPC(&((ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)])->cb_lock), ConnHandle);
|
|
CTEFreeLock(&AddrObjTableLock.Lock, AOTblHandle);
|
|
*TCBHandle = AOTblHandle;
|
|
}
|
|
|
|
}
|
|
// For whatever reason we couldn't indicate the data. At this point
|
|
// we hold the lock on the TCB. Push the buffer onto the pending
|
|
// queue and return.
|
|
CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
|
|
|
|
LastBuf = FindLastBuffer(RcvBuf);
|
|
LastBuf->ipr_next = RcvTCB->tcb_urgpending;
|
|
RcvTCB->tcb_urgpending = RcvBuf;
|
|
RcvTCB->tcb_urgcnt += Size;
|
|
break;
|
|
}
|
|
|
|
checksize:
|
|
// See how much we took. If we took it all, check the pending queue.
|
|
// At this point, we should hold the lock on the TCB.
|
|
if (Size == BytesTaken) {
|
|
// Took it all.
|
|
FreeRBChain(RcvBuf);
|
|
RcvBuf = RcvTCB->tcb_urgpending;
|
|
Size = RcvTCB->tcb_urgcnt;
|
|
} else {
|
|
// We didn't manage to take it all. Free what we did take,
|
|
// and then merge with the pending queue.
|
|
RcvBuf = FreePartialRB(RcvBuf, BytesTaken);
|
|
Size = Size - BytesTaken + RcvTCB->tcb_urgcnt;
|
|
if (RcvTCB->tcb_urgpending != NULL) {
|
|
|
|
// Find the end of the current RcvBuf chain, so we can
|
|
// merge.
|
|
|
|
LastBuf = FindLastBuffer(RcvBuf);
|
|
LastBuf->ipr_next = RcvTCB->tcb_urgpending;
|
|
}
|
|
}
|
|
|
|
RcvTCB->tcb_urgpending = NULL;
|
|
RcvTCB->tcb_urgcnt = 0;
|
|
|
|
} while (RcvBuf != NULL);
|
|
|
|
CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
|
|
|
|
RcvTCB->tcb_flags &= ~IN_DELIV_URG;
|
|
if (--(RcvTCB->tcb_slowcount) == 0) {
|
|
RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
|
|
CheckTCBRcv(RcvTCB);
|
|
}
|
|
}
|
|
|
|
//* PushData - Push all data back to the client.
|
|
//
|
|
// Called when we've received a FIN and need to push data to the client.
|
|
//
|
|
// Input: PushTCB - TCB to be pushed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
PushData(TCB * PushTCB)
|
|
{
|
|
TCPRcvReq *RcvReq;
|
|
|
|
CTEStructAssert(PushTCB, tcb);
|
|
|
|
RcvReq = PushTCB->tcb_rcvhead;
|
|
while (RcvReq != NULL) {
|
|
CTEStructAssert(RcvReq, trr);
|
|
RcvReq->trr_flags |= TRR_PUSHED;
|
|
RcvReq = RcvReq->trr_next;
|
|
}
|
|
|
|
RcvReq = PushTCB->tcb_exprcv;
|
|
while (RcvReq != NULL) {
|
|
CTEStructAssert(RcvReq, trr);
|
|
RcvReq->trr_flags |= TRR_PUSHED;
|
|
RcvReq = RcvReq->trr_next;
|
|
}
|
|
|
|
if (PushTCB->tcb_rcvhead != NULL || PushTCB->tcb_exprcv != NULL)
|
|
DelayAction(PushTCB, NEED_RCV_CMPLT);
|
|
|
|
}
|
|
|
|
//* SplitRcvBuf - Split an IPRcvBuf into three pieces.
|
|
//
|
|
// This function takes an input IPRcvBuf and splits it into three pieces.
|
|
// The first piece is the input buffer, which we just skip over. The second
|
|
// and third pieces are actually copied, even if we already own them, so
|
|
// that the may go to different places.
|
|
//
|
|
// Input: RcvBuf - RcvBuf chain to be split.
|
|
// Size - Total size in bytes of rcvbuf chain.
|
|
// Offset - Offset to skip over.
|
|
// SecondSize - Size in bytes of second piece.
|
|
// SecondBuf - Where to return second buffer pointer.
|
|
// ThirdBuf - Where to return third buffer pointer.
|
|
//
|
|
// Returns: Nothing. *SecondBuf and *ThirdBuf are set to NULL if we can't
|
|
// get memory for them.
|
|
//
|
|
void
|
|
SplitRcvBuf(IPRcvBuf * RcvBuf, uint Size, uint Offset, uint SecondSize,
|
|
IPRcvBuf ** SecondBuf, IPRcvBuf ** ThirdBuf)
|
|
{
|
|
IPRcvBuf *TempBuf;
|
|
uint ThirdSize;
|
|
|
|
ASSERT(Offset < Size);
|
|
ASSERT(((Offset + SecondSize) < Size) || (((Offset + SecondSize) == Size)
|
|
&& ThirdBuf == NULL));
|
|
|
|
ASSERT(RcvBuf != NULL);
|
|
|
|
// RcvBuf points at the buffer to copy from, and Offset is the offset into
|
|
// this buffer to copy from.
|
|
if (SecondBuf != NULL) {
|
|
// We need to allocate memory for a second buffer.
|
|
TempBuf = AllocTcpIpr(SecondSize, 'BPCT');
|
|
if (TempBuf != NULL) {
|
|
CopyRcvToBuffer(TempBuf->ipr_buffer, RcvBuf, SecondSize, Offset);
|
|
*SecondBuf = TempBuf;
|
|
} else {
|
|
*SecondBuf = NULL;
|
|
if (ThirdBuf != NULL)
|
|
*ThirdBuf = NULL;
|
|
return;
|
|
}
|
|
}
|
|
if (ThirdBuf != NULL) {
|
|
// We need to allocate memory for a third buffer.
|
|
ThirdSize = Size - (Offset + SecondSize);
|
|
TempBuf = AllocTcpIpr(ThirdSize, 'BPCT');
|
|
|
|
if (TempBuf != NULL) {
|
|
CopyRcvToBuffer(TempBuf->ipr_buffer, RcvBuf, ThirdSize,
|
|
Offset + SecondSize);
|
|
*ThirdBuf = TempBuf;
|
|
} else
|
|
*ThirdBuf = NULL;
|
|
}
|
|
}
|
|
|
|
//* HandleUrgent - Handle urgent data.
|
|
//
|
|
// Called when an incoming segment has urgent data in it. We make sure there
|
|
// really is urgent data in the segment, and if there is we try to dispose
|
|
// of it either by putting it into a posted buffer or calling an exp. rcv.
|
|
// indication handler.
|
|
//
|
|
// This routine is called at DPC level, and with the TCP locked.
|
|
//
|
|
// Urgent data handling is a little complicated. Each TCB has the starting
|
|
// and ending sequence numbers of the 'current' (last received) bit of urgent
|
|
// data. It is possible that the start of the current urgent data might be
|
|
// greater than tcb_rcvnext, if urgent data came in, we handled it, and then
|
|
// couldn't take the preceding normal data. The urgent valid flag is cleared
|
|
// when the next byte of data the user would read (rcvnext - pendingcnt) is
|
|
// greater than the end of urgent data - we do this so that we can correctly
|
|
// support SIOCATMARK. We always seperate urgent data out of the data stream.
|
|
// If the urgent valid field is set when we get into this routing we have
|
|
// to play a couple of games. If the incoming segment starts in front of the
|
|
// current urgent data, we truncate it before the urgent data, and put any
|
|
// data after the urgent data on the reassemble queue. These gyrations are
|
|
// done to avoid delivering the same urgent data twice. If the urgent valid
|
|
// field in the TCB is set and the segment starts after the current urgent
|
|
// data the new urgent information will replace the current urgent information.
|
|
//
|
|
// Input: RcvTCB - TCB to recv the data on.
|
|
// RcvInfo - RcvInfo structure for the incoming segment.
|
|
// RcvBuf - Pointer to IPRcvBuf train containing the
|
|
// incoming segment.
|
|
// Size - Pointer to size in bytes of data in the segment.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
HandleUrgent(TCB * RcvTCB, TCPRcvInfo * RcvInfo, IPRcvBuf * RcvBuf, uint * Size)
|
|
{
|
|
uint BytesInFront, BytesInBack; // Bytes in front of and in
|
|
// back of the urgent data.
|
|
uint UrgSize; // Size in bytes of urgent data.
|
|
SeqNum UrgStart, UrgEnd;
|
|
IPRcvBuf *EndBuf, *UrgBuf;
|
|
TCPRcvInfo NewRcvInfo;
|
|
CTELockHandle TCBHandle;
|
|
|
|
CTEStructAssert(RcvTCB, tcb);
|
|
ASSERT(RcvTCB->tcb_refcnt != 0);
|
|
ASSERT(RcvInfo->tri_flags & TCP_FLAG_URG);
|
|
ASSERT(SEQ_EQ(RcvInfo->tri_seq, RcvTCB->tcb_rcvnext));
|
|
|
|
// First, validate the urgent pointer.
|
|
if (RcvTCB->tcb_flags & BSD_URGENT) {
|
|
// We're using BSD style urgent data. We assume that the urgent
|
|
// data is one byte long, and that the urgent pointer points one
|
|
// after the urgent data instead of at the last byte of urgent data.
|
|
// See if the urgent data is in this segment.
|
|
|
|
if (RcvInfo->tri_urgent == 0 || RcvInfo->tri_urgent > *Size) {
|
|
// Not in this segment. Clear the urgent flag and return.
|
|
RcvInfo->tri_flags &= ~TCP_FLAG_URG;
|
|
return;
|
|
}
|
|
UrgSize = 1;
|
|
BytesInFront = RcvInfo->tri_urgent - 1;
|
|
|
|
} else {
|
|
|
|
// This is not BSD style urgent. We assume that the urgent data
|
|
// starts at the front of the segment and the last byte is pointed
|
|
// to by the urgent data pointer.
|
|
|
|
BytesInFront = 0;
|
|
UrgSize = MIN(RcvInfo->tri_urgent + 1, *Size);
|
|
|
|
}
|
|
|
|
BytesInBack = *Size - BytesInFront - UrgSize;
|
|
|
|
// UrgStart and UrgEnd are the first and last sequence numbers of the
|
|
// urgent data in this segment.
|
|
|
|
UrgStart = RcvInfo->tri_seq + BytesInFront;
|
|
UrgEnd = UrgStart + UrgSize - 1;
|
|
|
|
if (!(RcvTCB->tcb_flags & URG_INLINE)) {
|
|
|
|
EndBuf = NULL;
|
|
|
|
// Now see if this overlaps with any urgent data we've already seen.
|
|
if (RcvTCB->tcb_flags & URG_VALID) {
|
|
// We have some urgent data still around. See if we've advanced
|
|
// rcvnext beyond the urgent data. If we have, this is new urgent
|
|
// data, and we can go ahead and process it (although anyone doing
|
|
// an SIOCATMARK socket command might get confused). If we haven't
|
|
// consumed the data in front of the existing urgent data yet, we'll
|
|
// truncate this seg. to that amount and push the rest onto the
|
|
// reassembly queue. Note that rcvnext should never fall between
|
|
// tcb_urgstart and tcb_urgend.
|
|
|
|
ASSERT(SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart) ||
|
|
SEQ_GT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgend));
|
|
|
|
if (SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart)) {
|
|
|
|
// There appears to be some overlap in the data stream. Split
|
|
// the buffer up into pieces that come before the current urgent
|
|
// data and after the current urgent data, putting the latter
|
|
// on the reassembly queue.
|
|
|
|
UrgSize = RcvTCB->tcb_urgend - RcvTCB->tcb_urgstart + 1;
|
|
|
|
BytesInFront = MIN(RcvTCB->tcb_urgstart - RcvTCB->tcb_rcvnext,
|
|
(int)*Size);
|
|
|
|
if (SEQ_GT(RcvTCB->tcb_rcvnext + *Size, RcvTCB->tcb_urgend)) {
|
|
// We have data after this piece of urgent data.
|
|
BytesInBack = RcvTCB->tcb_rcvnext + *Size -
|
|
RcvTCB->tcb_urgend;
|
|
} else
|
|
BytesInBack = 0;
|
|
|
|
SplitRcvBuf(RcvBuf, *Size, BytesInFront, UrgSize, NULL,
|
|
(BytesInBack ? &EndBuf : NULL));
|
|
|
|
if (EndBuf != NULL) {
|
|
NewRcvInfo.tri_seq = RcvTCB->tcb_urgend + 1;
|
|
NewRcvInfo.tri_flags = RcvInfo->tri_flags;
|
|
NewRcvInfo.tri_urgent = UrgEnd - NewRcvInfo.tri_seq;
|
|
if (RcvTCB->tcb_flags & BSD_URGENT)
|
|
NewRcvInfo.tri_urgent++;
|
|
NewRcvInfo.tri_ack = RcvInfo->tri_ack;
|
|
NewRcvInfo.tri_window = RcvInfo->tri_window;
|
|
if (!PutOnRAQ(RcvTCB, &NewRcvInfo, EndBuf, BytesInBack)){
|
|
FreeRBChain(EndBuf);
|
|
}
|
|
}
|
|
|
|
*Size = BytesInFront;
|
|
|
|
//iishack fix. Do not allow lookahead
|
|
//buffer size to be more than that is allocated!!
|
|
|
|
if (RcvBuf->ipr_size >= BytesInFront) {
|
|
RcvBuf->ipr_size = BytesInFront;
|
|
}
|
|
|
|
RcvInfo->tri_flags &= ~TCP_FLAG_URG;
|
|
return;
|
|
}
|
|
}
|
|
// We have urgent data we can process now. Split it into its component
|
|
// parts, the first part, the urgent data, and the stuff after the
|
|
// urgent data.
|
|
SplitRcvBuf(RcvBuf, *Size, BytesInFront, UrgSize, &UrgBuf,
|
|
(BytesInBack ? &EndBuf : NULL));
|
|
|
|
// If we managed to split out the end stuff, put it on the queue now.
|
|
if (EndBuf != NULL) {
|
|
NewRcvInfo.tri_seq = RcvInfo->tri_seq + BytesInFront + UrgSize;
|
|
NewRcvInfo.tri_flags = RcvInfo->tri_flags & ~TCP_FLAG_URG;
|
|
NewRcvInfo.tri_ack = RcvInfo->tri_ack;
|
|
NewRcvInfo.tri_window = RcvInfo->tri_window;
|
|
PutOnRAQ(RcvTCB, &NewRcvInfo, EndBuf, BytesInBack);
|
|
}
|
|
if (UrgBuf != NULL) {
|
|
// We succesfully split the urgent data out.
|
|
if (!(RcvTCB->tcb_flags & URG_VALID)) {
|
|
RcvTCB->tcb_flags |= URG_VALID;
|
|
RcvTCB->tcb_slowcount++;
|
|
RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
|
|
CheckTCBRcv(RcvTCB);
|
|
}
|
|
RcvTCB->tcb_urgstart = UrgStart;
|
|
RcvTCB->tcb_urgend = UrgEnd;
|
|
TCBHandle = DISPATCH_LEVEL;
|
|
DeliverUrgent(RcvTCB, UrgBuf, UrgSize, &TCBHandle);
|
|
}
|
|
*Size = BytesInFront;
|
|
|
|
if (RcvBuf->ipr_size >= BytesInFront) {
|
|
RcvBuf->ipr_size = BytesInFront;
|
|
}
|
|
|
|
} else {
|
|
// Urgent data is to be processed inline. We just need to remember
|
|
// where it is and treat it as normal data. If there's already urgent
|
|
// data, we remember the latest urgent data.
|
|
|
|
RcvInfo->tri_flags &= ~TCP_FLAG_URG;
|
|
|
|
if (RcvTCB->tcb_flags & URG_VALID) {
|
|
// There is urgent data. See if this stuff comes after the existing
|
|
// urgent data.
|
|
|
|
if (SEQ_LTE(UrgEnd, RcvTCB->tcb_urgend)) {
|
|
// The existing urgent data completely overlaps this stuff,
|
|
// so ignore this.
|
|
return;
|
|
}
|
|
} else {
|
|
RcvTCB->tcb_flags |= URG_VALID;
|
|
RcvTCB->tcb_slowcount++;
|
|
RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
|
|
CheckTCBRcv(RcvTCB);
|
|
}
|
|
|
|
RcvTCB->tcb_urgstart = UrgStart;
|
|
RcvTCB->tcb_urgend = UrgEnd;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//* TdiReceive - Process a receive request.
|
|
//
|
|
// This is the main TDI receive request handler. We validate the connection
|
|
// and make sure that we have a TCB in the proper state, then we try to
|
|
// allocate a receive request structure. If that succeeds, we'll look and
|
|
// see what's happening on the TCB - if there's pending data, we'll put it
|
|
// in the buffer. Otherwise we'll just queue the receive for later.
|
|
//
|
|
// Input: Request - TDI_REQUEST structure for this request.
|
|
// Flags - Pointer to flags word.
|
|
// RcvLength - Pointer to length in bytes of receive buffer.
|
|
// Buffer - Pointer to buffer to take data.
|
|
//
|
|
// Returns: TDI_STATUS of request.
|
|
//
|
|
TDI_STATUS
|
|
TdiReceive(PTDI_REQUEST Request, ushort * Flags, uint * RcvLength,
|
|
PNDIS_BUFFER Buffer)
|
|
{
|
|
TCPConn *Conn;
|
|
TCB *RcvTCB;
|
|
TCPRcvReq *RcvReq;
|
|
CTELockHandle ConnTableHandle, TCBHandle;
|
|
TDI_STATUS Error;
|
|
ushort UFlags;
|
|
|
|
Conn = GetConnFromConnID(PtrToUlong(Request->Handle.ConnectionContext), &ConnTableHandle);
|
|
|
|
if (Conn != NULL) {
|
|
CTEStructAssert(Conn, tc);
|
|
|
|
RcvTCB = Conn->tc_tcb;
|
|
if (RcvTCB != NULL) {
|
|
CTEStructAssert(RcvTCB, tcb);
|
|
CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle);
|
|
CTEFreeLock(&(Conn->tc_ConnBlock->cb_lock), TCBHandle);
|
|
UFlags = *Flags;
|
|
|
|
if ((DATA_RCV_STATE(RcvTCB->tcb_state) ||
|
|
(RcvTCB->tcb_pendingcnt != 0 && (UFlags & TDI_RECEIVE_NORMAL)) ||
|
|
(RcvTCB->tcb_urgcnt != 0 && (UFlags & TDI_RECEIVE_EXPEDITED)) ||
|
|
(RcvTCB->tcb_indicated && (RcvTCB->tcb_state == TCB_CLOSE_WAIT)))
|
|
&& !CLOSING(RcvTCB)) {
|
|
// We have a TCB, and it's valid. Get a receive request now.
|
|
|
|
CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
|
|
|
|
RcvReq = GetRcvReq();
|
|
if (RcvReq != NULL) {
|
|
|
|
RcvReq->trr_rtn = Request->RequestNotifyObject;
|
|
RcvReq->trr_context = Request->RequestContext;
|
|
RcvReq->trr_buffer = Buffer;
|
|
RcvReq->trr_size = *RcvLength;
|
|
RcvReq->trr_uflags = Flags;
|
|
RcvReq->trr_offset = 0;
|
|
RcvReq->trr_amt = 0;
|
|
RcvReq->trr_flags = (uint) UFlags;
|
|
if ((UFlags & (TDI_RECEIVE_NORMAL | TDI_RECEIVE_EXPEDITED))
|
|
!= TDI_RECEIVE_EXPEDITED) {
|
|
// This is not an expedited only receive. Put him
|
|
// on the normal receive queue.
|
|
RcvReq->trr_next = NULL;
|
|
if (RcvTCB->tcb_rcvhead == NULL) {
|
|
// The receive queue is empty. Put him on the front.
|
|
RcvTCB->tcb_rcvhead = RcvReq;
|
|
RcvTCB->tcb_rcvtail = RcvReq;
|
|
} else {
|
|
RcvTCB->tcb_rcvtail->trr_next = RcvReq;
|
|
RcvTCB->tcb_rcvtail = RcvReq;
|
|
}
|
|
|
|
//if this rcv is for zero length complete this
|
|
// and indicate pending data again, if any
|
|
|
|
if (RcvReq->trr_size == 0) {
|
|
|
|
REFERENCE_TCB(RcvTCB);
|
|
RcvReq->trr_flags |= TRR_PUSHED;
|
|
CTEFreeLock(&RcvTCB->tcb_lock,ConnTableHandle);
|
|
CompleteRcvs(RcvTCB);
|
|
CTEGetLock(&RcvTCB->tcb_lock,&ConnTableHandle);
|
|
DerefTCB(RcvTCB, ConnTableHandle);
|
|
|
|
return TDI_PENDING;
|
|
}
|
|
|
|
|
|
|
|
// If this recv. can't hold urgent data or there isn't
|
|
// any pending urgent data continue processing.
|
|
if (!(UFlags & TDI_RECEIVE_EXPEDITED) ||
|
|
RcvTCB->tcb_urgcnt == 0) {
|
|
// If tcb_currcv is NULL, there is no currently
|
|
// active receive. In this case, check to see if
|
|
// there is pending data and that we are not
|
|
// currently in a receive indication handler. If
|
|
// both of these are true then deal with the
|
|
// pending data.
|
|
if (RcvTCB->tcb_currcv == NULL) {
|
|
RcvTCB->tcb_currcv = RcvReq;
|
|
// No currently active receive.
|
|
if (!(RcvTCB->tcb_flags & IN_RCV_IND)) {
|
|
// Not in a rcv. indication.
|
|
RcvTCB->tcb_rcvhndlr = BufferData;
|
|
if (RcvTCB->tcb_pendhead == NULL) {
|
|
CTEFreeLock(&RcvTCB->tcb_lock,
|
|
ConnTableHandle);
|
|
return TDI_PENDING;
|
|
} else {
|
|
IPRcvBuf *PendBuffer;
|
|
uint PendSize;
|
|
uint OldRcvWin;
|
|
|
|
// We have pending data to deal with.
|
|
PendBuffer = RcvTCB->tcb_pendhead;
|
|
PendSize = RcvTCB->tcb_pendingcnt;
|
|
RcvTCB->tcb_pendhead = NULL;
|
|
RcvTCB->tcb_pendingcnt = 0;
|
|
REFERENCE_TCB(RcvTCB);
|
|
|
|
// We assume that BufferData holds
|
|
// the lock (does not yield) during
|
|
// this call. If this changes for some
|
|
// reason, we'll have to fix the code
|
|
// below that does the window update
|
|
// check. See the comments in the
|
|
// BufferData() routine for more info.
|
|
(void)BufferData(RcvTCB, TCP_FLAG_PUSH,
|
|
PendBuffer, PendSize);
|
|
CheckTCBRcv(RcvTCB);
|
|
// Now we need to see if the window
|
|
// has changed. If it has, send an
|
|
// ACK.
|
|
OldRcvWin = RcvTCB->tcb_rcvwin;
|
|
if (OldRcvWin != RcvWin(RcvTCB)) {
|
|
// The window has changed, so send
|
|
// an ACK.
|
|
|
|
DelayAction(RcvTCB, NEED_ACK);
|
|
}
|
|
DerefTCB(RcvTCB, ConnTableHandle);
|
|
ProcessTCBDelayQ();
|
|
return TDI_PENDING;
|
|
}
|
|
}
|
|
// In a receive indication. The recv. request
|
|
// is now on the queue, so just fall through
|
|
// to the return.
|
|
|
|
}
|
|
// A rcv. is currently active. No need to do
|
|
// anything else.
|
|
CTEFreeLock(&RcvTCB->tcb_lock, ConnTableHandle);
|
|
return TDI_PENDING;
|
|
} else {
|
|
// This buffer can hold urgent data and we have
|
|
// some pending. Deliver it now.
|
|
REFERENCE_TCB(RcvTCB);
|
|
DeliverUrgent(RcvTCB, NULL, 0, &ConnTableHandle);
|
|
DerefTCB(RcvTCB, ConnTableHandle);
|
|
return TDI_PENDING;
|
|
}
|
|
} else {
|
|
TCPRcvReq *Temp;
|
|
|
|
// This is an expedited only receive. Just put him
|
|
// on the end of the expedited receive queue.
|
|
Temp = STRUCT_OF(TCPRcvReq, &RcvTCB->tcb_exprcv,
|
|
trr_next);
|
|
while (Temp->trr_next != NULL)
|
|
Temp = Temp->trr_next;
|
|
|
|
RcvReq->trr_next = NULL;
|
|
Temp->trr_next = RcvReq;
|
|
if (RcvTCB->tcb_urgpending != NULL) {
|
|
REFERENCE_TCB(RcvTCB);
|
|
DeliverUrgent(RcvTCB, NULL, 0, &ConnTableHandle);
|
|
DerefTCB(RcvTCB, ConnTableHandle);
|
|
return TDI_PENDING;
|
|
} else
|
|
Error = TDI_PENDING;
|
|
}
|
|
} else {
|
|
// Couldn't get a rcv. req.
|
|
Error = TDI_NO_RESOURCES;
|
|
}
|
|
} else {
|
|
// The TCB is in an invalid state.
|
|
Error = TDI_INVALID_STATE;
|
|
}
|
|
CTEFreeLock(&RcvTCB->tcb_lock, ConnTableHandle);
|
|
return Error;
|
|
} else { // No TCB for connection.
|
|
|
|
CTEFreeLock(&((Conn->tc_ConnBlock)->cb_lock), ConnTableHandle);
|
|
Error = TDI_INVALID_STATE;
|
|
}
|
|
} else // No connection.
|
|
|
|
Error = TDI_INVALID_CONNECTION;
|
|
|
|
//CTEFreeLock(&ConnTableLock, ConnTableHandle);
|
|
return Error;
|
|
|
|
}
|