You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1929 lines
61 KiB
1929 lines
61 KiB
/*++
|
|
|
|
Copyright (c) 1989-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tdircv.c
|
|
|
|
Abstract:
|
|
|
|
TDI receive event handlers
|
|
|
|
TdiReceiveHandler is the only entry, which is called by TCP.
|
|
|
|
TdiReceiveHandler acquires the spinlock and increase the refcount
|
|
of the ConnectObject. Then it forward the control to the event
|
|
handler for current receive state.
|
|
|
|
The state event handler won't release the spinlock unless it's
|
|
going to call functions outside our control, for example,
|
|
IoCompleteionRequest and the client event handler.
|
|
|
|
Author:
|
|
|
|
Jiandong Ruan
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "tdircv.tmh"
|
|
|
|
#if DBG
|
|
BOOL
|
|
IsValidWaitingHeaderState(
|
|
IN PSMB_CONNECT ConnectObject
|
|
);
|
|
BOOL
|
|
IsValidIndicateState(
|
|
IN PSMB_CONNECT ConnectObject
|
|
);
|
|
#endif
|
|
|
|
VOID
|
|
KillConnection(
|
|
IN PSMB_CONNECT ConnectObject
|
|
);
|
|
|
|
NTSTATUS
|
|
IndicateToClient(
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN ULONG ReceiveFlags,
|
|
IN LONG BytesIndicated,
|
|
IN LONG BytesAvailable,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu
|
|
);
|
|
|
|
NTSTATUS
|
|
SmbNewMessage(
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN LONG BytesIndicated,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP *Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
SmbBuildPartialMdl(
|
|
PMDL SourceMdl,
|
|
PMDL DestMdl,
|
|
LONG Offset
|
|
);
|
|
|
|
NTSTATUS
|
|
SmbClientRcvCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSMB_CONNECT ConnectObject
|
|
);
|
|
|
|
#define TAKE(Bytes) \
|
|
ASSERT((Bytes) <= BytesIndicated); \
|
|
BytesIndicated -= (Bytes); \
|
|
BytesAvailable -= (Bytes); \
|
|
(PUCHAR)Tsdu += (Bytes); \
|
|
*BytesTaken += (Bytes)
|
|
|
|
#if DBG
|
|
DWORD
|
|
SmbGetMdlChainByteCount(
|
|
IN PMDL Mdl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the total size of buffers described by the MDL chain.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD Size;
|
|
|
|
Size = 0;
|
|
while(Mdl) {
|
|
Size += MmGetMdlByteCount(Mdl);
|
|
Mdl = Mdl->Next;
|
|
}
|
|
return Size;
|
|
}
|
|
#endif // DBG
|
|
|
|
PIRP
|
|
SmbNextReceiveRequest(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a client TDI_RECEIVE request queued in the pending list
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NULL If there is no pending receive request
|
|
PIRP The client IRP pending in the RcvList
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PIRP PendingIrp;
|
|
KIRQL Irql;
|
|
|
|
if (IsListEmpty(&ConnectObject->RcvList)) {
|
|
return NULL;
|
|
}
|
|
|
|
entry = RemoveHeadList(&ConnectObject->RcvList);
|
|
|
|
PendingIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
IoAcquireCancelSpinLock(&Irql);
|
|
IoSetCancelRoutine(PendingIrp, NULL);
|
|
IoReleaseCancelSpinLock(Irql);
|
|
return PendingIrp;
|
|
}
|
|
|
|
VOID
|
|
SmbCompleteReceiveRequest(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Complete the ConnectObject->ClientIrp
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PIRP ClientIrp = NULL;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
PTDI_REQUEST_KERNEL_RECEIVE ClientRcvParams = NULL;
|
|
LONG BytesReceived = 0;
|
|
|
|
ASSERT (NULL != ConnectObject->ClientIrp);
|
|
ASSERT (NULL != ConnectObject->ClientMdl);
|
|
ASSERT (0 == ConnectObject->BytesRemaining || 0 == ConnectObject->FreeBytesInMdl);
|
|
ASSERT (ConnectObject->FreeBytesInMdl >= 0);
|
|
|
|
BytesReceived = ConnectObject->ClientBufferSize - ConnectObject->FreeBytesInMdl;
|
|
ASSERT (BytesReceived >= 0);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x200000);
|
|
|
|
ClientIrp = ConnectObject->ClientIrp;
|
|
ConnectObject->ClientIrp = NULL;
|
|
ConnectObject->ClientMdl = NULL;
|
|
IrpSp = IoGetCurrentIrpStackLocation(ClientIrp);
|
|
ClientRcvParams = (PTDI_REQUEST_KERNEL_RECEIVE)&IrpSp->Parameters;
|
|
|
|
if (0 == ConnectObject->BytesRemaining) {
|
|
|
|
ConnectObject->StateRcvHandler = WaitingHeader;
|
|
ConnectObject->HeaderBytesRcved = 0;
|
|
ClientRcvParams->ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE;
|
|
ClientIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
PUSH_LOCATION(ConnectObject, 0x200010);
|
|
|
|
} else {
|
|
|
|
ConnectObject->StateRcvHandler = SmbPartialRcv;
|
|
ClientRcvParams->ReceiveFlags &= (~TDI_RECEIVE_ENTIRE_MESSAGE);
|
|
ClientIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
|
|
PUSH_LOCATION(ConnectObject, 0x200020);
|
|
|
|
}
|
|
|
|
ClientIrp->IoStatus.Information = BytesReceived;
|
|
ConnectObject->BytesReceived += BytesReceived;
|
|
|
|
SMB_RELEASE_SPINLOCK_DPC(ConnectObject);
|
|
IoCompleteRequest(ClientIrp, IO_NETWORK_INCREMENT);
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject);
|
|
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbFillIrp(
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN PVOID Tsdu,
|
|
IN LONG BytesIndicated,
|
|
OUT LONG *BytesTaken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the IRP has been completed
|
|
STATUS_MORE_PROCESSING_REQUIRED if the IRP hasn't been completed. More data
|
|
is needed to fill the IRP.
|
|
--*/
|
|
{
|
|
LONG BytesToCopy = 0, BytesCopied = 0;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(BytesIndicated > 0);
|
|
|
|
ASSERT(BytesIndicated <= ConnectObject->BytesRemaining);
|
|
|
|
BytesToCopy = SMB_MIN(ConnectObject->FreeBytesInMdl, BytesIndicated);
|
|
BytesCopied = 0;
|
|
status = TdiCopyBufferToMdl(
|
|
Tsdu,
|
|
0,
|
|
BytesToCopy,
|
|
ConnectObject->ClientMdl,
|
|
ConnectObject->ClientBufferSize - ConnectObject->FreeBytesInMdl,
|
|
&BytesCopied
|
|
);
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
*BytesTaken = BytesCopied;
|
|
|
|
ConnectObject->FreeBytesInMdl -= BytesCopied;
|
|
ConnectObject->BytesRemaining -= BytesCopied;
|
|
|
|
if (ConnectObject->FreeBytesInMdl) {
|
|
ASSERT (BytesIndicated == BytesCopied);
|
|
}
|
|
|
|
if (0 == ConnectObject->BytesRemaining || 0 == ConnectObject->FreeBytesInMdl) {
|
|
ASSERTMSG("Hmm. Never see this", ConnectObject->BytesRemaining == 0);
|
|
|
|
SmbCompleteReceiveRequest(ConnectObject);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT (BytesIndicated == BytesCopied);
|
|
ConnectObject->StateRcvHandler = SmbPartialRcv;
|
|
ASSERT (ConnectObject->BytesInIndicate || IsValidPartialRcvState(ConnectObject));
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
VOID
|
|
SmbPrepareReceiveIrp(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
LONG RcvLength;
|
|
PIRP ClientIrp = NULL;
|
|
|
|
status = SmbBuildPartialMdl(
|
|
ConnectObject->ClientMdl,
|
|
ConnectObject->PartialMdl,
|
|
ConnectObject->ClientBufferSize - ConnectObject->FreeBytesInMdl
|
|
);
|
|
ASSERT (STATUS_SUCCESS == status);
|
|
|
|
ClientIrp = ConnectObject->ClientIrp;
|
|
|
|
RcvLength = SMB_MIN(ConnectObject->BytesRemaining, ConnectObject->FreeBytesInMdl);
|
|
ASSERT(RcvLength > 0);
|
|
|
|
MmGetSystemAddressForMdlSafe(ConnectObject->PartialMdl, HighPagePriority);
|
|
|
|
// ASSERT(ConnectObject->FreeBytesInMdl >= ConnectObject->BytesRemaining);
|
|
|
|
TdiBuildReceive(
|
|
ClientIrp,
|
|
IoGetRelatedDeviceObject(ConnectObject->TcpContext->Connect.ConnectObject),
|
|
ConnectObject->TcpContext->Connect.ConnectObject,
|
|
(PVOID)SmbClientRcvCompletion,
|
|
ConnectObject,
|
|
ConnectObject->PartialMdl,
|
|
TDI_RECEIVE_NORMAL,
|
|
RcvLength
|
|
);
|
|
ConnectObject->StateRcvHandler = SmbPartialRcv;
|
|
ASSERT (ConnectObject->BytesInIndicate || IsValidPartialRcvState(ConnectObject));
|
|
}
|
|
|
|
NTSTATUS
|
|
IndicateToClient(
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN ULONG ReceiveFlags,
|
|
IN LONG BytesIndicated,
|
|
IN LONG BytesAvailable,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This level indication handle the cooridination between SmbReceive and Receive
|
|
Event Handler.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PTDI_IND_RECEIVE evReceive = NULL;
|
|
PVOID RcvEvContext = NULL;
|
|
PIRP ClientIrp = NULL;
|
|
LONG BytesToCopy = 0, BytesCopied = 0, ClientBytesTaken = 0;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
PTDI_REQUEST_KERNEL_RECEIVE ClientRcvParams = NULL;
|
|
|
|
*BytesTaken = 0;
|
|
|
|
ASSERT(ConnectObject->ClientMdl == NULL);
|
|
ASSERT(ConnectObject->ClientIrp == NULL);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x1000);
|
|
|
|
ASSERT(BytesIndicated <= ConnectObject->BytesRemaining);
|
|
ASSERT(BytesIndicated <= BytesAvailable);
|
|
ASSERT(BytesAvailable <= ConnectObject->CurrentPktLength);
|
|
|
|
ClientBytesTaken = 0;
|
|
//
|
|
// First fill pending requests if any.
|
|
//
|
|
while (NULL != (ClientIrp = SmbNextReceiveRequest(ConnectObject))) {
|
|
PUSH_LOCATION(ConnectObject, 0x1010);
|
|
|
|
BREAK_WHEN_TAKE();
|
|
|
|
ConnectObject->ClientIrp = ClientIrp;
|
|
ConnectObject->ClientMdl = ClientIrp->MdlAddress;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(ClientIrp);
|
|
ClientRcvParams = (PTDI_REQUEST_KERNEL_RECEIVE)&IrpSp->Parameters;
|
|
ConnectObject->ClientBufferSize = (LONG)ClientRcvParams->ReceiveLength;
|
|
ConnectObject->FreeBytesInMdl = ConnectObject->ClientBufferSize;
|
|
|
|
if (BytesIndicated == 0) {
|
|
PUSH_LOCATION(ConnectObject, 0x1020);
|
|
SmbPrepareReceiveIrp(ConnectObject);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
status = SmbFillIrp(ConnectObject, Tsdu, BytesIndicated, &ClientBytesTaken);
|
|
TAKE(ClientBytesTaken);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
ASSERT (status == STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
ASSERT (0 != ConnectObject->FreeBytesInMdl);
|
|
ASSERT (IsValidPartialRcvState(ConnectObject));
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x1030);
|
|
SmbPrepareReceiveIrp(ConnectObject);
|
|
return status;
|
|
}
|
|
|
|
if (0 == ConnectObject->BytesRemaining) {
|
|
ASSERT(0 == BytesIndicated);
|
|
ASSERT (IsValidWaitingHeaderState(ConnectObject));
|
|
PUSH_LOCATION(ConnectObject, 0x1040);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT(BytesIndicated > 0);
|
|
}
|
|
|
|
ASSERT(BytesAvailable > 0);
|
|
|
|
evReceive = ConnectObject->ClientObject->evReceive;
|
|
RcvEvContext = ConnectObject->ClientObject->RcvEvContext;
|
|
if (evReceive == NULL) {
|
|
PUSH_LOCATION(ConnectObject, 0x1050);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ClientBytesTaken = 0;
|
|
SMB_RELEASE_SPINLOCK_DPC(ConnectObject);
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("Indication to the client: BytesIndicated=%d BytesAvailable=%d\n",
|
|
BytesIndicated, BytesAvailable));
|
|
status = (*evReceive) (
|
|
RcvEvContext,
|
|
ConnectObject->ClientContext,
|
|
ReceiveFlags,
|
|
(ULONG)BytesIndicated,
|
|
(ULONG)BytesAvailable,
|
|
(PULONG)&ClientBytesTaken,
|
|
Tsdu,
|
|
&ClientIrp
|
|
);
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject);
|
|
|
|
if (status == STATUS_DATA_NOT_ACCEPTED) {
|
|
ClientBytesTaken = 0;
|
|
PUSH_LOCATION(ConnectObject, 0x1055);
|
|
}
|
|
|
|
//
|
|
// The client could disconnect the connection after the spinlock is released
|
|
//
|
|
if (NULL == ConnectObject->TcpContext) {
|
|
// BREAK_WHEN_TAKE();
|
|
SMB_RELEASE_SPINLOCK_DPC(ConnectObject);
|
|
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED && ClientIrp != NULL) {
|
|
ClientIrp->IoStatus.Status = STATUS_CONNECTION_RESET;
|
|
ClientIrp->IoStatus.Information = 0;
|
|
IoCompleteRequest(ClientIrp, IO_NETWORK_INCREMENT);
|
|
}
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject);
|
|
PUSH_LOCATION(ConnectObject, 0x1060);
|
|
ConnectObject->ClientIrp = NULL;
|
|
ConnectObject->ClientMdl = NULL;
|
|
return (*BytesTaken)? STATUS_SUCCESS: STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
if (ClientBytesTaken > BytesIndicated) {
|
|
PUSH_LOCATION(ConnectObject, 0x1070);
|
|
|
|
//
|
|
// Client takes too much data, try to recover
|
|
//
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IndicateToClient: Client takes too much data "
|
|
"Connect %p BytesTaken=%d, BytesIndicated=%d BytesAvailable=%d\n",
|
|
ConnectObject, *BytesTaken, BytesIndicated, BytesAvailable));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Client takes too much data "
|
|
"Connect %p BytesTaken=%d, BytesIndicated=%d BytesAvailable=%d",
|
|
ConnectObject, *BytesTaken, BytesIndicated, BytesAvailable));
|
|
ASSERT (0);
|
|
ClientBytesTaken = BytesIndicated;
|
|
}
|
|
|
|
TAKE(ClientBytesTaken);
|
|
ConnectObject->BytesRemaining -= ClientBytesTaken;
|
|
ConnectObject->BytesReceived += ClientBytesTaken;
|
|
|
|
//
|
|
// Check if a whole message has been taken by the client
|
|
//
|
|
if (0 == ConnectObject->BytesRemaining) {
|
|
ASSERT (status != STATUS_MORE_PROCESSING_REQUIRED);
|
|
ASSERT(BytesIndicated == 0 && BytesAvailable == 0);
|
|
ConnectObject->StateRcvHandler = WaitingHeader;
|
|
ConnectObject->HeaderBytesRcved = 0;
|
|
PUSH_LOCATION(ConnectObject, 0x1080);
|
|
return status;
|
|
}
|
|
|
|
ConnectObject->StateRcvHandler = SmbPartialRcv;
|
|
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
ASSERT (ClientIrp == NULL);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x1090);
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Client return %!status! for indication"
|
|
"Connect %p BytesTaken=%d, BytesIndicated=%d BytesAvailable=%d",
|
|
status, ConnectObject, *BytesTaken, BytesIndicated, BytesAvailable));
|
|
|
|
// BREAK_WHEN_TAKE();
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// This partial receive case
|
|
//
|
|
ASSERT (ClientIrp != NULL);
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x10a0);
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(ClientIrp);
|
|
ClientRcvParams = (PTDI_REQUEST_KERNEL_RECEIVE)&IrpSp->Parameters;
|
|
|
|
ConnectObject->ClientIrp = ClientIrp;
|
|
ConnectObject->ClientMdl = ClientIrp->MdlAddress;
|
|
ConnectObject->ClientBufferSize = (LONG)ClientRcvParams->ReceiveLength;
|
|
ConnectObject->FreeBytesInMdl = ConnectObject->ClientBufferSize;
|
|
|
|
// ASSERT(ConnectObject->FreeBytesInMdl >= ConnectObject->BytesRemaining);
|
|
|
|
if (0 != BytesIndicated) {
|
|
PUSH_LOCATION(ConnectObject, 0x10b0);
|
|
|
|
status = SmbFillIrp(ConnectObject, Tsdu, BytesIndicated, &ClientBytesTaken);
|
|
TAKE(ClientBytesTaken);
|
|
|
|
if (STATUS_SUCCESS == status) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT (status == STATUS_MORE_PROCESSING_REQUIRED);
|
|
ASSERT(0 == BytesIndicated);
|
|
}
|
|
|
|
SmbPrepareReceiveIrp(ConnectObject);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbClientRcvCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handle the completion of a client's TDI_RECEIVE request
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
KIRQL Irql;
|
|
LONG Size;
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x2000);
|
|
|
|
SMB_ACQUIRE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
ASSERT(Irp == ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE]);
|
|
ASSERT(Irp == ConnectObject->ClientIrp);
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = NULL;
|
|
|
|
if (Irp->IoStatus.Status != STATUS_SUCCESS) {
|
|
PUSH_LOCATION(ConnectObject, 0x2010);
|
|
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p status=%!status!",
|
|
ConnectObject, Irp->IoStatus.Status));
|
|
|
|
ConnectObject->ClientIrp = NULL;
|
|
ConnectObject->ClientMdl = NULL;
|
|
|
|
KillConnection(ConnectObject);
|
|
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->MdlAddress = ConnectObject->ClientMdl;
|
|
MmPrepareMdlForReuse(ConnectObject->PartialMdl);
|
|
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("RcvLength = %d\n", Irp->IoStatus.Information));
|
|
|
|
Size = (LONG)(Irp->IoStatus.Information);
|
|
ConnectObject->FreeBytesInMdl -= Size;
|
|
ConnectObject->BytesRemaining -= Size;
|
|
ConnectObject->BytesInXport -= Size;
|
|
if (ConnectObject->BytesInXport < 0) {
|
|
PUSH_LOCATION(ConnectObject, 0x2020);
|
|
ConnectObject->BytesInXport = 0;
|
|
}
|
|
|
|
ASSERT (ConnectObject->BytesRemaining >= 0);
|
|
ASSERT (ConnectObject->FreeBytesInMdl >= 0);
|
|
ASSERT (ConnectObject->BytesInXport >= 0);
|
|
|
|
ASSERT(Size <= ConnectObject->CurrentPktLength);
|
|
|
|
if (ConnectObject->FreeBytesInMdl && ConnectObject->BytesRemaining) {
|
|
PUSH_LOCATION(ConnectObject, 0x2030);
|
|
|
|
//
|
|
// In most cases, there shouldn't be no more byte in transport
|
|
//
|
|
ASSERT(ConnectObject->BytesInXport == 0);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
//
|
|
// We still need to fill the IRP
|
|
//
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Before complete the client's IRP, bump up the RefCount
|
|
//
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
|
|
//
|
|
// Complete the client's Irp
|
|
//
|
|
SmbCompleteReceiveRequest(ConnectObject);
|
|
|
|
//
|
|
// Queue DPC if necessary after complete the client's IRP.
|
|
// If we do it before, there could be a race condition
|
|
//
|
|
// We release the SPINLOCK in SmbCompleteReceiveRequest
|
|
// and if the client's completion routine lower the IRQL,
|
|
// the client coulde receive out-of-order messages
|
|
//
|
|
if (ConnectObject->BytesRemaining == 0 && ConnectObject->BytesInXport > 0) {
|
|
PUSH_LOCATION(ConnectObject, 0x2060);
|
|
//
|
|
// Start an DPC to get the session header
|
|
//
|
|
SmbQueueSessionHeaderDpc(ConnectObject);
|
|
}
|
|
|
|
//
|
|
// The ConnectObject should be still valid since we bump up
|
|
// the RefCount above
|
|
//
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbHeaderCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
KIRQL Irql;
|
|
NTSTATUS status;
|
|
LONG BytesTaken, BytesRcved;
|
|
PIRP NewIrp = NULL;
|
|
PDEVICE_OBJECT TcpDeviceObject = NULL;
|
|
PFILE_OBJECT TcpFileObject = NULL;
|
|
|
|
SMB_ACQUIRE_SPINLOCK(ConnectObject, Irql);
|
|
PUSH_LOCATION(ConnectObject, 0x3000);
|
|
if (NULL == ConnectObject->TcpContext) {
|
|
PUSH_LOCATION(ConnectObject, 0x3010);
|
|
BREAK_WHEN_TAKE();
|
|
goto cleanup;
|
|
}
|
|
|
|
ASSERT(Irp == ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE]);
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = NULL;
|
|
|
|
BytesRcved = (LONG)Irp->IoStatus.Information;
|
|
if ((BytesRcved + ConnectObject->HeaderBytesRcved > SMB_SESSION_HEADER_SIZE) ||
|
|
(Irp->IoStatus.Status != STATUS_SUCCESS)) {
|
|
PUSH_LOCATION(ConnectObject, 0x3020);
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p status=%!status!",
|
|
ConnectObject, Irp->IoStatus.Status));
|
|
KillConnection(ConnectObject);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ConnectObject->BytesInXport > BytesRcved) {
|
|
PUSH_LOCATION(ConnectObject, 0x3040);
|
|
ConnectObject->BytesInXport -= BytesRcved;
|
|
} else {
|
|
PUSH_LOCATION(ConnectObject, 0x3050);
|
|
ConnectObject->BytesInXport = 0;
|
|
}
|
|
ConnectObject->HeaderBytesRcved += BytesRcved;
|
|
if (ConnectObject->HeaderBytesRcved < SMB_SESSION_HEADER_SIZE) {
|
|
ASSERT(ConnectObject->BytesInXport == 0);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x3060);
|
|
|
|
//
|
|
// Wait for the transport to indicate the remaining bytes
|
|
//
|
|
goto cleanup;
|
|
}
|
|
|
|
status = SmbNewMessage(
|
|
ConnectObject,
|
|
0,
|
|
&BytesTaken,
|
|
NULL,
|
|
&NewIrp
|
|
);
|
|
ASSERT(BytesTaken == 0);
|
|
|
|
if (ConnectObject->CurrentPktLength == 0) {
|
|
// BREAK_WHEN_TAKE();
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x3070);
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
|
|
if (ConnectObject->BytesInXport > 0) {
|
|
// BREAK_WHEN_TAKE();
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x3080);
|
|
SmbQueueSessionHeaderDpc(ConnectObject);
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
PUSH_LOCATION(ConnectObject, 0x3090);
|
|
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p status=%!status!",
|
|
ConnectObject, status));
|
|
|
|
ASSERT(ConnectObject->ClientIrp == NULL);
|
|
ASSERT(ConnectObject->ClientMdl == NULL);
|
|
// ASSERT(!ConnectObject->Originator);
|
|
BREAK_WHEN_TAKE();
|
|
|
|
//
|
|
// Client doesn't give us any IRP, let's waiting
|
|
//
|
|
goto cleanup;
|
|
}
|
|
|
|
ASSERT(IsValidPartialRcvState(ConnectObject) || IsValidIndicateState(ConnectObject));
|
|
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = NewIrp;
|
|
|
|
//
|
|
// Reference the file object while we still holding the spinlock
|
|
//
|
|
TcpFileObject = ConnectObject->TcpContext->Connect.ConnectObject;
|
|
TcpDeviceObject = IoGetRelatedDeviceObject(TcpFileObject);
|
|
ObReferenceObject(TcpFileObject);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
status = IoCallDriver(TcpDeviceObject, NewIrp);
|
|
ObDereferenceObject(TcpFileObject);
|
|
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
SmbFreeIrp(Irp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
cleanup:
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
SmbFreeIrp(Irp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbBuildPartialMdl(
|
|
PMDL SourceMdl,
|
|
PMDL DestMdl,
|
|
LONG Offset
|
|
)
|
|
{
|
|
LONG TotalLength;
|
|
PMDL PartialMdl;
|
|
PUCHAR NewAddress;
|
|
|
|
//
|
|
// Find the 1st partial filled MDL
|
|
//
|
|
TotalLength = 0;
|
|
PartialMdl = SourceMdl;
|
|
do {
|
|
if (TotalLength + (LONG)(MmGetMdlByteCount(PartialMdl)) > Offset) {
|
|
break;
|
|
}
|
|
|
|
TotalLength += MmGetMdlByteCount(PartialMdl);
|
|
PartialMdl = PartialMdl->Next;
|
|
} while (PartialMdl);
|
|
|
|
ASSERT(PartialMdl);
|
|
if (PartialMdl == NULL) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NewAddress = MmGetMdlVirtualAddress(PartialMdl);
|
|
NewAddress += (Offset - TotalLength);
|
|
IoBuildPartialMdl(PartialMdl, DestMdl, NewAddress, 0);
|
|
DestMdl->Next = PartialMdl->Next;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#ifdef NO_ZERO_BYTE_INDICATE
|
|
NTSTATUS
|
|
SmbClientRcvCompletionRdr(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
{
|
|
KIRQL Irql;
|
|
NTSTATUS status;
|
|
LONG BytesTaken, RcvLength, BytesCopied;
|
|
|
|
SMB_ACQUIRE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
ASSERT(Irp == ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE]);
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = NULL;
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x4000);
|
|
|
|
if (Irp->IoStatus.Status != STATUS_SUCCESS) {
|
|
PUSH_LOCATION(ConnectObject, 0x4010);
|
|
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p status=%!status!",
|
|
ConnectObject, Irp->IoStatus.Status));
|
|
|
|
KillConnection(ConnectObject);
|
|
goto cleanup;
|
|
}
|
|
|
|
ASSERT(ConnectObject->BytesRemaining == ConnectObject->CurrentPktLength);
|
|
|
|
if (ConnectObject->BytesInXport > (LONG)Irp->IoStatus.Information) {
|
|
ConnectObject->BytesInXport -= (LONG)Irp->IoStatus.Information;
|
|
PUSH_LOCATION(ConnectObject, 0x4020);
|
|
} else {
|
|
ConnectObject->BytesInXport = 0;
|
|
PUSH_LOCATION(ConnectObject, 0x4030);
|
|
}
|
|
ConnectObject->BytesInIndicate += (LONG)(Irp->IoStatus.Information);
|
|
|
|
//
|
|
// If we didn't get enough bytes, bail out
|
|
//
|
|
if (ConnectObject->BytesInIndicate < MINIMUM_RDR_BUFFER &&
|
|
ConnectObject->BytesInIndicate < ConnectObject->CurrentPktLength) {
|
|
ASSERT(ConnectObject->BytesInXport == 0);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x4040);
|
|
goto cleanup;
|
|
}
|
|
|
|
ConnectObject->StateRcvHandler = SmbPartialRcv;
|
|
status = IndicateToClient(
|
|
ConnectObject->Device,
|
|
ConnectObject,
|
|
TDI_RECEIVE_NORMAL | TDI_RECEIVE_ENTIRE_MESSAGE,
|
|
ConnectObject->BytesInIndicate,
|
|
ConnectObject->CurrentPktLength,
|
|
&BytesTaken,
|
|
ConnectObject->IndicateBuffer
|
|
);
|
|
ASSERT(BytesTaken <= ConnectObject->BytesInIndicate);
|
|
ConnectObject->BytesInIndicate -= BytesTaken;
|
|
ASSERT(ConnectObject->BytesInIndicate >= 0);
|
|
|
|
if (BytesTaken && ConnectObject->BytesInIndicate) {
|
|
PUSH_LOCATION(ConnectObject, 0x4050);
|
|
RtlMoveMemory(
|
|
ConnectObject->IndicateBuffer + BytesTaken,
|
|
ConnectObject->IndicateBuffer,
|
|
ConnectObject->BytesInIndicate
|
|
);
|
|
ASSERT(STATUS_SUCCESS == status || STATUS_DATA_NOT_ACCEPTED == status);
|
|
}
|
|
|
|
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
PUSH_LOCATION(ConnectObject, 0x4060);
|
|
if (ConnectObject->StateRcvHandler == WaitingHeader && ConnectObject->BytesInXport > 0) {
|
|
ASSERT(ConnectObject->BytesRemaining == 0);
|
|
SmbQueueSessionHeaderDpc(ConnectObject);
|
|
}
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
ASSERT(IsValidPartialRcvState(ConnectObject));
|
|
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = ConnectObject->ClientIrp;
|
|
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
status = IoCallDriver(
|
|
IoGetRelatedDeviceObject(ConnectObject->TcpContext->Connect.ConnectObject),
|
|
ConnectObject->ClientIrp
|
|
);
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IoCallDriver return 0x%08lx %d of %s\n", status, __LINE__, __FILE__));
|
|
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
SmbFreeIrp(Irp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
cleanup:
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
SmbFreeIrp(Irp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
PIRP
|
|
SmbPrepareIndicateIrp(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
{
|
|
PIRP Irp = NULL;
|
|
PMDL Mdl = NULL;
|
|
LONG BytesToRcv = 0;
|
|
|
|
ASSERT(ConnectObject->CurrentPktLength > 0);
|
|
ASSERT(ConnectObject->CurrentPktLength == ConnectObject->BytesRemaining);
|
|
ASSERT(ConnectObject->BytesInIndicate >= 0 && ConnectObject->BytesInIndicate < MINIMUM_RDR_BUFFER);
|
|
ASSERT(ConnectObject->BytesInIndicate < ConnectObject->CurrentPktLength);
|
|
|
|
BytesToRcv = SMB_MIN(MINIMUM_RDR_BUFFER - ConnectObject->BytesInIndicate,
|
|
ConnectObject->CurrentPktLength - ConnectObject->BytesInIndicate);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x6000);
|
|
|
|
Mdl = IoAllocateMdl(
|
|
ConnectObject->IndicateBuffer + ConnectObject->BytesInIndicate,
|
|
BytesToRcv,
|
|
FALSE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
Irp = SmbAllocIrp(ConnectObject->Device->DeviceObject.StackSize - 1);
|
|
if (NULL == Mdl || NULL == Irp) {
|
|
goto cleanup;
|
|
}
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x6010);
|
|
|
|
MmBuildMdlForNonPagedPool(Mdl);
|
|
|
|
TdiBuildReceive(
|
|
Irp,
|
|
IoGetRelatedDeviceObject(ConnectObject->TcpContext->Connect.ConnectObject),
|
|
ConnectObject->TcpContext->Connect.ConnectObject,
|
|
(PVOID)SmbClientRcvCompletionRdr,
|
|
ConnectObject,
|
|
Mdl,
|
|
TDI_RECEIVE_NORMAL,
|
|
BytesToRcv
|
|
);
|
|
|
|
return Irp;
|
|
|
|
cleanup:
|
|
PUSH_LOCATION(ConnectObject, 0x6020);
|
|
|
|
BREAK_WHEN_TAKE();
|
|
|
|
if (NULL != Irp) {
|
|
SmbFreeIrp(Irp);
|
|
}
|
|
|
|
if (NULL != Mdl) {
|
|
IoFreeMdl(Mdl);
|
|
}
|
|
|
|
KillConnection(ConnectObject);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
SmbNewMessage(
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN LONG BytesIndicated,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we just receive a new message header
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG BytesToIndicate, BytesCopied, RcvLength;
|
|
NTSTATUS status;
|
|
|
|
*BytesTaken = 0;
|
|
*Irp = NULL;
|
|
|
|
ASSERT(ConnectObject->HeaderBytesRcved == SMB_SESSION_HEADER_SIZE);
|
|
ConnectObject->HeaderBytesRcved = 0;
|
|
|
|
ConnectObject->CurrentPktLength = (LONG)((htonl(ConnectObject->SmbHeader.Length)) & SMB_HEADER_LENGTH_MASK);
|
|
ConnectObject->BytesRemaining = ConnectObject->CurrentPktLength;
|
|
PUSH_LOCATION(ConnectObject, 0x7000);
|
|
if (ConnectObject->CurrentPktLength == 0) {
|
|
PUSH_LOCATION(ConnectObject, 0x7010);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BytesToIndicate = SMB_MIN(BytesIndicated, ConnectObject->CurrentPktLength);
|
|
|
|
//
|
|
// Don't indicate ZERO byte to srv.sys. Like RDR, srv could access beyond
|
|
// the indicated area in some corner case!!!
|
|
//
|
|
#if 0
|
|
if (BytesToIndicate < ConnectObject->CurrentPktLength &&
|
|
ConnectObject->Originator && BytesToIndicate < MINIMUM_RDR_BUFFER) {
|
|
#endif
|
|
if (BytesToIndicate < ConnectObject->CurrentPktLength &&
|
|
BytesToIndicate < MINIMUM_RDR_BUFFER) {
|
|
|
|
//
|
|
// We need to indicate at least 128 bytes to RDR
|
|
//
|
|
|
|
ConnectObject->BytesInIndicate = 0;
|
|
*Irp = SmbPrepareIndicateIrp(ConnectObject);
|
|
if (NULL != *Irp) {
|
|
ConnectObject->StateRcvHandler = Indicate;
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
ASSERT(IsValidIndicateState(ConnectObject));
|
|
PUSH_LOCATION(ConnectObject, 0x7030);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} else {
|
|
PUSH_LOCATION(ConnectObject, 0x7040);
|
|
ASSERT(NULL == ConnectObject->TcpContext);
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
|
|
ConnectObject->StateRcvHandler = SmbPartialRcv;
|
|
status = IndicateToClient(
|
|
ConnectObject->Device,
|
|
ConnectObject,
|
|
TDI_RECEIVE_NORMAL | TDI_RECEIVE_ENTIRE_MESSAGE,
|
|
BytesToIndicate,
|
|
ConnectObject->CurrentPktLength,
|
|
BytesTaken,
|
|
Tsdu
|
|
);
|
|
ASSERT(*BytesTaken <= BytesToIndicate);
|
|
|
|
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p status=%!status!",
|
|
ConnectObject, status));
|
|
|
|
ASSERT(ConnectObject->ClientIrp == NULL);
|
|
ASSERT(ConnectObject->ClientMdl == NULL);
|
|
|
|
ASSERTMSG("Client doesn't return an IRP",
|
|
status == STATUS_DATA_NOT_ACCEPTED ||
|
|
ConnectObject->BytesRemaining == 0 ||
|
|
ConnectObject->TcpContext == NULL);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x7050);
|
|
return status;
|
|
}
|
|
|
|
ASSERT (*BytesTaken == BytesToIndicate);
|
|
ASSERT(IsValidPartialRcvState(ConnectObject));
|
|
|
|
*Irp = ConnectObject->ClientIrp;
|
|
PUSH_LOCATION(ConnectObject, 0x7080);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
#if DBG
|
|
BOOL
|
|
IsValidIndicateState(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
{
|
|
if (ConnectObject->BytesRemaining <= 0 || ConnectObject->BytesRemaining != ConnectObject->CurrentPktLength) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidIndicateState: Connect %p BytesRemaining=%d, PktLength=%d\n",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->CurrentPktLength));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesRemaining=%d, PktLength=%d",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->CurrentPktLength));
|
|
return FALSE;
|
|
}
|
|
if (ConnectObject->BytesInIndicate < 0 || ConnectObject->BytesInIndicate >= MINIMUM_RDR_BUFFER) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidIndicateState: Connect %p BytesInIndicate=%d\n",
|
|
ConnectObject, ConnectObject->BytesInIndicate));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesInIndicate=%d",
|
|
ConnectObject, ConnectObject->BytesInIndicate));
|
|
return FALSE;
|
|
}
|
|
if (NULL != ConnectObject->ClientIrp || NULL != ConnectObject->ClientMdl) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidIndicateState: Connect %p ClientIrp=%p, ClientMdl=%p\n",
|
|
ConnectObject, ConnectObject->ClientIrp, ConnectObject->ClientMdl));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p ClientIrp=%p, ClientMdl=%p",
|
|
ConnectObject, ConnectObject->ClientIrp, ConnectObject->ClientMdl));
|
|
return FALSE;
|
|
}
|
|
if (ConnectObject->StateRcvHandler != Indicate) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidIndicateState: Connect %p Wrong handler\n", ConnectObject));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Wrong handler", ConnectObject));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
Indicate(
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN ULONG ReceiveFlags,
|
|
IN LONG BytesIndicated,
|
|
IN LONG BytesAvailable,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP *Irp
|
|
)
|
|
{
|
|
PIRP ClientIrp = NULL;
|
|
PTDI_REQUEST_KERNEL_RECEIVE ClientRcvParams = NULL;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
NTSTATUS status;
|
|
|
|
LONG BytesToCopy = 0, BytesCopied = 0;
|
|
LONG BytesToRcv = 0, RcvLength = 0;
|
|
LONG ClientBytesTaken = 0;
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x8000);
|
|
|
|
ASSERT(IsValidIndicateState(ConnectObject));
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("Indicate: Connect %p Indicate=%d Available=%d\n",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Indicate=%d Available=%d",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
|
|
*BytesTaken = 0;
|
|
*Irp = NULL;
|
|
ASSERT (ConnectObject->BytesInIndicate <= ConnectObject->CurrentPktLength);
|
|
|
|
BytesToCopy = SMB_MIN(MINIMUM_RDR_BUFFER - ConnectObject->BytesInIndicate,
|
|
ConnectObject->CurrentPktLength - ConnectObject->BytesInIndicate);
|
|
BytesToCopy = SMB_MIN(BytesToCopy, BytesIndicated);
|
|
RtlCopyMemory(ConnectObject->IndicateBuffer + ConnectObject->BytesInIndicate, Tsdu, BytesToCopy);
|
|
TAKE(BytesToCopy);
|
|
ConnectObject->BytesInIndicate += BytesToCopy;
|
|
|
|
if (ConnectObject->BytesInIndicate < MINIMUM_RDR_BUFFER &&
|
|
ConnectObject->BytesInIndicate < ConnectObject->CurrentPktLength) {
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x8010);
|
|
|
|
//
|
|
// After taking all the indicated bytes, the buffer is still not filled
|
|
//
|
|
|
|
ASSERT(BytesIndicated == 0);
|
|
ConnectObject->BytesInXport = BytesAvailable;
|
|
|
|
if (BytesAvailable > 0) {
|
|
PUSH_LOCATION(ConnectObject, 0x8020);
|
|
|
|
*Irp = SmbPrepareIndicateIrp(ConnectObject);
|
|
if (NULL != *Irp) {
|
|
PUSH_LOCATION(ConnectObject, 0x8030);
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} else {
|
|
PUSH_LOCATION(ConnectObject, 0x8040);
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
ASSERT(NULL == ConnectObject->TcpContext);
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// We get a full message or at least 128 bytes
|
|
//
|
|
ASSERT (ConnectObject->BytesInIndicate == MINIMUM_RDR_BUFFER ||
|
|
ConnectObject->BytesInIndicate == ConnectObject->CurrentPktLength);
|
|
|
|
status = IndicateToClient(
|
|
ConnectObject->Device,
|
|
ConnectObject,
|
|
TDI_RECEIVE_NORMAL | TDI_RECEIVE_ENTIRE_MESSAGE,
|
|
ConnectObject->BytesInIndicate,
|
|
ConnectObject->CurrentPktLength,
|
|
&ClientBytesTaken,
|
|
ConnectObject->IndicateBuffer
|
|
);
|
|
ASSERT(ClientBytesTaken <= ConnectObject->BytesInIndicate);
|
|
ConnectObject->BytesInIndicate -= ClientBytesTaken;
|
|
|
|
if (ConnectObject->BytesInIndicate) {
|
|
RtlMoveMemory(
|
|
ConnectObject->IndicateBuffer + ClientBytesTaken,
|
|
ConnectObject->IndicateBuffer,
|
|
ConnectObject->BytesInIndicate
|
|
);
|
|
PUSH_LOCATION(ConnectObject, 0x8050);
|
|
ASSERT(STATUS_DATA_NOT_ACCEPTED == status || STATUS_SUCCESS == status);
|
|
}
|
|
|
|
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p status=%!status!",
|
|
ConnectObject, status));
|
|
PUSH_LOCATION(ConnectObject, 0x8060);
|
|
return status;
|
|
}
|
|
|
|
ASSERT(IsValidPartialRcvState(ConnectObject));
|
|
PUSH_LOCATION(ConnectObject, 0x8070);
|
|
*Irp = ConnectObject->ClientIrp;
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
#if DBG
|
|
BOOL
|
|
IsValidPartialRcvState(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
{
|
|
if (ConnectObject->BytesInIndicate != 0) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidPartialRcvState: Connect %p BytesInIndicate=%d\n",
|
|
ConnectObject, ConnectObject->BytesInIndicate));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesInIndicate=%d",
|
|
ConnectObject, ConnectObject->BytesInIndicate));
|
|
return FALSE;
|
|
}
|
|
if (ConnectObject->BytesRemaining <= 0 || ConnectObject->BytesRemaining > ConnectObject->CurrentPktLength) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidPartialRcvState: Connect %p BytesRemaining=%d, PktLength=%d\n",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->CurrentPktLength));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesRemaining=%d, PktLength=%d",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->CurrentPktLength));
|
|
return FALSE;
|
|
}
|
|
if (NULL == ConnectObject->ClientIrp || NULL == ConnectObject->ClientMdl) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidPartialRcvState: Connect %p ClientIrp=%p, ClientMdl=%p\n",
|
|
ConnectObject, ConnectObject->ClientIrp, ConnectObject->ClientMdl));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p ClientIrp=%p, ClientMdl=%p",
|
|
ConnectObject, ConnectObject->ClientIrp, ConnectObject->ClientMdl));
|
|
return FALSE;
|
|
}
|
|
/*
|
|
if (ConnectObject->FreeBytesInMdl < ConnectObject->BytesRemaining) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidPartialRcvState: Connect %p BytesRemaining=%d, FreeBytesInMdl=%d\n",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->FreeBytesInMdl));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesRemaining=%d, FreeBytesInMdl=%d",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->FreeBytesInMdl));
|
|
return FALSE;
|
|
}
|
|
*/
|
|
if (ConnectObject->StateRcvHandler != SmbPartialRcv) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidPartialRcvState: Connect %p Wrong handler\n", ConnectObject));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Wrong handler", ConnectObject));
|
|
return FALSE;
|
|
}
|
|
if (ConnectObject->CurrentPktLength - ConnectObject->BytesRemaining <
|
|
ConnectObject->ClientBufferSize - ConnectObject->FreeBytesInMdl) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidPartialRcvState: Connect %p BytesRcved=%d BytesCopiedInMdl=%d\n",
|
|
ConnectObject, ConnectObject->CurrentPktLength - ConnectObject->BytesRemaining,
|
|
ConnectObject->ClientBufferSize - ConnectObject->FreeBytesInMdl));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesRcved=%d BytesCopiedInMdl=%d",
|
|
ConnectObject, ConnectObject->CurrentPktLength - ConnectObject->BytesRemaining,
|
|
ConnectObject->ClientBufferSize - ConnectObject->FreeBytesInMdl));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
SmbPartialRcv(
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN ULONG ReceiveFlags,
|
|
IN LONG BytesIndicated,
|
|
IN LONG BytesAvailable,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP *Irp
|
|
)
|
|
{
|
|
PIRP ClientIrp = NULL;
|
|
PTDI_REQUEST_KERNEL_RECEIVE ClientRcvParams = NULL;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
LONG BytesToCopy = 0, BytesCopied = 0;
|
|
NTSTATUS status;
|
|
PLIST_ENTRY entry = NULL;
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x9000);
|
|
*BytesTaken = 0;
|
|
*Irp = NULL;
|
|
if (ConnectObject->ClientIrp == NULL || ConnectObject->ClientMdl == NULL) {
|
|
PUSH_LOCATION(ConnectObject, 0x9010);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT(IsValidPartialRcvState(ConnectObject));
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("PartialRcv: Connect %p Indicate=%d Available=%d\n",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Indicate=%d Available=%d",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
|
|
while(1) {
|
|
ClientIrp = ConnectObject->ClientIrp;
|
|
|
|
BytesToCopy = SMB_MIN(ConnectObject->BytesRemaining, BytesIndicated);
|
|
BytesToCopy = SMB_MIN(BytesToCopy, ConnectObject->FreeBytesInMdl);
|
|
|
|
status = TdiCopyBufferToMdl(
|
|
Tsdu,
|
|
0,
|
|
BytesToCopy,
|
|
ConnectObject->ClientMdl,
|
|
ConnectObject->ClientBufferSize - ConnectObject->FreeBytesInMdl,
|
|
&BytesCopied
|
|
);
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
TAKE(BytesCopied);
|
|
ConnectObject->BytesRemaining -= BytesCopied;
|
|
ConnectObject->FreeBytesInMdl -= BytesCopied;
|
|
|
|
if (ConnectObject->BytesRemaining != 0 && ConnectObject->FreeBytesInMdl != 0) {
|
|
PUSH_LOCATION(ConnectObject, 0x9020);
|
|
break;
|
|
}
|
|
|
|
ConnectObject->BytesInIndicate = 0;
|
|
SmbCompleteReceiveRequest(ConnectObject);
|
|
|
|
if (ConnectObject->BytesRemaining == 0) {
|
|
//
|
|
// Return if a full message
|
|
//
|
|
ASSERT (ConnectObject->StateRcvHandler == WaitingHeader);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0x9050);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ClientIrp = SmbNextReceiveRequest(ConnectObject);
|
|
if (NULL == ClientIrp) {
|
|
PUSH_LOCATION(ConnectObject, 0x9060);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ConnectObject->ClientIrp = ClientIrp;
|
|
ConnectObject->ClientMdl = ClientIrp->MdlAddress;
|
|
ASSERT(ConnectObject->ClientMdl == NULL);
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(ClientIrp);
|
|
ClientRcvParams = (PTDI_REQUEST_KERNEL_RECEIVE)&IrpSp->Parameters;
|
|
ConnectObject->ClientBufferSize = (LONG)ClientRcvParams->ReceiveLength;
|
|
ConnectObject->FreeBytesInMdl = ConnectObject->ClientBufferSize;
|
|
PUSH_LOCATION(ConnectObject, 0x9070);
|
|
}
|
|
|
|
ASSERT(BytesIndicated == 0);
|
|
ASSERT(BytesAvailable >= 0);
|
|
|
|
ConnectObject->BytesInXport = BytesAvailable;
|
|
|
|
ASSERT(IsValidPartialRcvState(ConnectObject));
|
|
|
|
if (BytesAvailable == 0) {
|
|
PUSH_LOCATION(ConnectObject, 0x9080);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
SmbPrepareReceiveIrp(ConnectObject);
|
|
*Irp = ConnectObject->ClientIrp;
|
|
PUSH_LOCATION(ConnectObject, 0x9090);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
#if DBG
|
|
BOOL
|
|
IsValidWaitingHeaderState(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Teat if the invariant for WaitingHeader state is true or not
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
if (ConnectObject->BytesRemaining != 0) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidWaitingHeaderState: Connect %p BytesRemaining=%d, PktLength=%d\n",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->CurrentPktLength));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesRemaining=%d, PktLength=%d",
|
|
ConnectObject, ConnectObject->BytesRemaining, ConnectObject->CurrentPktLength));
|
|
return FALSE;
|
|
}
|
|
if (ConnectObject->BytesInIndicate != 0) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidWaitingHeaderState: Connect %p BytesInIndicate=%d\n",
|
|
ConnectObject, ConnectObject->BytesInIndicate));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p BytesInIndicate=%d",
|
|
ConnectObject, ConnectObject->BytesInIndicate));
|
|
return FALSE;
|
|
}
|
|
if (NULL != ConnectObject->ClientIrp || NULL != ConnectObject->ClientMdl) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidWaitingHeaderState: Connect %p ClientIrp=%p, ClientMdl=%p\n",
|
|
ConnectObject, ConnectObject->ClientIrp, ConnectObject->ClientMdl));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p ClientIrp=%p, ClientMdl=%p",
|
|
ConnectObject, ConnectObject->ClientIrp, ConnectObject->ClientMdl));
|
|
return FALSE;
|
|
}
|
|
if (ConnectObject->StateRcvHandler != WaitingHeader) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidWaitingHeaderState: Connect %p Wrong handler\n",
|
|
ConnectObject));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Wrong handler", ConnectObject));
|
|
return FALSE;
|
|
}
|
|
if (ConnectObject->HeaderBytesRcved >= SMB_SESSION_HEADER_SIZE ||
|
|
ConnectObject->HeaderBytesRcved < 0) {
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("IsValidWaitingHeaderState: Connect %p HeaderBytesRcved=%d\n",
|
|
ConnectObject, ConnectObject->HeaderBytesRcved));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p HeaderBytesRcved=%d",
|
|
ConnectObject, ConnectObject->HeaderBytesRcved));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
PIRP __inline
|
|
SmbPrepareSessionHeaderIrp(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
{
|
|
PMDL Mdl = NULL;
|
|
PIRP Irp = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
ASSERT (ConnectObject->HeaderBytesRcved >= 0);
|
|
ASSERT (ConnectObject->HeaderBytesRcved < SMB_SESSION_HEADER_SIZE);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xa010);
|
|
Mdl = IoAllocateMdl(
|
|
((PUCHAR)(&ConnectObject->SmbHeader)) + ConnectObject->HeaderBytesRcved,
|
|
SMB_SESSION_HEADER_SIZE - ConnectObject->HeaderBytesRcved,
|
|
FALSE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
Irp = SmbAllocIrp(ConnectObject->Device->DeviceObject.StackSize - 1);
|
|
if (NULL == Mdl || NULL == Irp) {
|
|
goto cleanup;
|
|
}
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xa020);
|
|
MmBuildMdlForNonPagedPool(Mdl);
|
|
|
|
TdiBuildReceive(
|
|
Irp,
|
|
IoGetRelatedDeviceObject(ConnectObject->TcpContext->Connect.ConnectObject),
|
|
ConnectObject->TcpContext->Connect.ConnectObject,
|
|
SmbHeaderCompletion,
|
|
ConnectObject,
|
|
Mdl,
|
|
TDI_RECEIVE_NORMAL,
|
|
SMB_SESSION_HEADER_SIZE - ConnectObject->HeaderBytesRcved
|
|
);
|
|
return Irp;
|
|
|
|
cleanup:
|
|
PUSH_LOCATION(ConnectObject, 0xa030);
|
|
BREAK_WHEN_TAKE();
|
|
|
|
if (NULL != Irp) {
|
|
SmbFreeIrp(Irp);
|
|
}
|
|
|
|
if (NULL != Mdl) {
|
|
IoFreeMdl(Mdl);
|
|
}
|
|
|
|
KillConnection(ConnectObject);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
WaitingHeader(
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN ULONG ReceiveFlags,
|
|
IN LONG BytesIndicated,
|
|
IN LONG BytesAvailable,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP *Irp
|
|
)
|
|
{
|
|
LONG ClientBytesTaken = 0;
|
|
PIRP ClientIrp = NULL;
|
|
LONG BytesToCopy;
|
|
NTSTATUS status;
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xb000);
|
|
|
|
ASSERT(IsValidWaitingHeaderState(ConnectObject));
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("WaitingHeader: Connect %p Indicate=%d Available=%d\n",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Indicate=%d Available=%d",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
|
|
*BytesTaken = 0;
|
|
*Irp = NULL;
|
|
|
|
ASSERT(IsValidWaitingHeaderState(ConnectObject));
|
|
|
|
BytesToCopy = SMB_MIN((LONG)SMB_SESSION_HEADER_SIZE-ConnectObject->HeaderBytesRcved,
|
|
BytesIndicated);
|
|
|
|
if (BytesToCopy > 0) {
|
|
PUSH_LOCATION(ConnectObject, 0xb020);
|
|
|
|
//
|
|
// A new message
|
|
//
|
|
RtlCopyMemory(
|
|
(PUCHAR)(&ConnectObject->SmbHeader) + ConnectObject->HeaderBytesRcved,
|
|
Tsdu,
|
|
BytesToCopy
|
|
);
|
|
TAKE(BytesToCopy);
|
|
ConnectObject->HeaderBytesRcved += BytesToCopy;
|
|
|
|
ASSERT(ConnectObject->HeaderBytesRcved <= SMB_SESSION_HEADER_SIZE);
|
|
if (ConnectObject->HeaderBytesRcved == SMB_SESSION_HEADER_SIZE) {
|
|
status = SmbNewMessage(
|
|
ConnectObject,
|
|
BytesIndicated,
|
|
&ClientBytesTaken,
|
|
Tsdu,
|
|
&ClientIrp
|
|
);
|
|
ASSERT(ClientBytesTaken <= BytesIndicated);
|
|
TAKE(ClientBytesTaken);
|
|
ConnectObject->BytesInXport = BytesAvailable;
|
|
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
PUSH_LOCATION(ConnectObject, 0xb030);
|
|
ASSERT(ClientIrp);
|
|
*Irp = ClientIrp;
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xb040);
|
|
|
|
// BREAK_WHEN_TAKE();
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p status=%!status!",
|
|
ConnectObject, status));
|
|
return status;
|
|
}
|
|
|
|
ASSERT(ClientBytesTaken == ConnectObject->CurrentPktLength || 0 == BytesIndicated);
|
|
ASSERT(IsValidWaitingHeaderState(ConnectObject) || IsValidIndicateState(ConnectObject));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
ASSERT(BytesIndicated == 0);
|
|
ASSERT(ConnectObject->HeaderBytesRcved < SMB_SESSION_HEADER_SIZE);
|
|
|
|
if (BytesAvailable > 0) {
|
|
PUSH_LOCATION(ConnectObject, 0xb050);
|
|
|
|
ConnectObject->BytesInXport = BytesAvailable;
|
|
*Irp = SmbPrepareSessionHeaderIrp(ConnectObject);
|
|
if (*Irp) {
|
|
PUSH_LOCATION(ConnectObject, 0xb060);
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} else {
|
|
PUSH_LOCATION(ConnectObject, 0xb070);
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
|
|
ASSERT(BytesAvailable == 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbTdiReceiveHandler (
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_TCP_CONNECT TcpConnect,
|
|
IN ULONG ReceiveFlags,
|
|
IN LONG BytesIndicated,
|
|
IN LONG BytesAvailable,
|
|
OUT LONG *BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the TDI receive event handler we register with TCP.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSMB_CONNECT ConnectObject;
|
|
KIRQL Irql;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
*BytesTaken = 0;
|
|
*Irp = NULL;
|
|
if (BytesAvailable == 0) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Reference the ConnectObject
|
|
//
|
|
SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql);
|
|
ConnectObject = TcpConnect->UpperConnect;
|
|
if (NULL == ConnectObject) {
|
|
SMB_RELEASE_SPINLOCK(&SmbCfg, Irql);
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
SMB_RELEASE_SPINLOCK_DPC(&SmbCfg);
|
|
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("TdiReceiveHandler: Connect %p Indicate=%d Available=%d\n",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Indicate=%d Available=%d",
|
|
ConnectObject, BytesIndicated, BytesAvailable));
|
|
|
|
//
|
|
// Access is synchronized through the spinlock. We won't release the
|
|
// spinlock unless we'll call IoCompleteRequest or Client's event
|
|
// handler
|
|
//
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject);
|
|
PUSH_LOCATION(ConnectObject, 0xc000);
|
|
|
|
if (ConnectObject->State != SMB_CONNECTED) {
|
|
// ASSERT(0);
|
|
PUSH_LOCATION(ConnectObject, 0xc010);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
//
|
|
// An indication comes in while we have an IRP in TCP
|
|
//
|
|
if (ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE]) {
|
|
BREAK_WHEN_TAKE();
|
|
|
|
//
|
|
// This might happen if the DPC routine was called
|
|
// The SmbHeaderDpc release the spin lock before calling IoCallDriver.
|
|
// Just in between, we get an indication!!!
|
|
//
|
|
ASSERT(ConnectObject->DpcRequestQueued);
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xc020);
|
|
ConnectObject->BytesInXport = BytesAvailable;
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
ASSERT(ConnectObject->DpcRequestQueued || ConnectObject->BytesInXport == 0);
|
|
SmbRemoveSessionHeaderDpc(ConnectObject);
|
|
|
|
while (BytesAvailable > 0) {
|
|
LONG ClientBytesTaken;
|
|
PIRP ClientIrp;
|
|
|
|
ClientIrp = NULL;
|
|
ClientBytesTaken = 0;
|
|
status = (*ConnectObject->StateRcvHandler)(
|
|
DeviceObject,
|
|
ConnectObject,
|
|
ReceiveFlags,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
&ClientBytesTaken,
|
|
Tsdu,
|
|
&ClientIrp
|
|
);
|
|
ASSERT(ClientBytesTaken <= BytesIndicated);
|
|
TAKE(ClientBytesTaken);
|
|
SmbPrint(SMB_TRACE_RECEIVE, ("TdiReceiveHandler: Connect %p Indicate=%d Available=%d"
|
|
" Taken=%d ClientTaken=%d status=0x%08lx\n",
|
|
ConnectObject, BytesIndicated, BytesAvailable, *BytesTaken, ClientBytesTaken, status));
|
|
SmbTrace(SMB_TRACE_RECEIVE, ("Connect %p Indicate=%d Available=%d"
|
|
" Taken=%d ClientTaken=%d status=%!status!",
|
|
ConnectObject, BytesIndicated, BytesAvailable, *BytesTaken, ClientBytesTaken, status));
|
|
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
PUSH_LOCATION(ConnectObject, 0xc030);
|
|
ASSERT(ClientIrp);
|
|
*Irp = ClientIrp;
|
|
goto return_IRP_Exit;
|
|
}
|
|
|
|
if (status == STATUS_DATA_NOT_ACCEPTED || (status == STATUS_SUCCESS && ClientBytesTaken == 0)) {
|
|
PUSH_LOCATION(ConnectObject, 0xc040);
|
|
goto take_some_exit;
|
|
}
|
|
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
PUSH_LOCATION(ConnectObject, 0xc050);
|
|
}
|
|
|
|
take_some_exit:
|
|
PUSH_LOCATION(ConnectObject, 0xc060);
|
|
ConnectObject->BytesInXport = BytesAvailable;
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = NULL;
|
|
|
|
//
|
|
// If any bytes taken, the status should be STATUS_SUCCESS.
|
|
// Although the client doesn't take any bytes, but we could consume the header.
|
|
// Don't return what the client returns.
|
|
//
|
|
if (*BytesTaken) {
|
|
PUSH_LOCATION(ConnectObject, 0xc070);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
ASSERT(ConnectObject->RefCount > 1);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
return status;
|
|
|
|
return_IRP_Exit:
|
|
PUSH_LOCATION(ConnectObject, 0xc080);
|
|
ConnectObject->BytesInXport = BytesAvailable;
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = *Irp;
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
ASSERT(ConnectObject->RefCount > 1);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
|
|
IoSetNextIrpStackLocation(*Irp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
VOID
|
|
SmbGetHeaderDpc(
|
|
IN PKDPC Dpc,
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
{
|
|
KIRQL Irql;
|
|
NTSTATUS status;
|
|
PIRP Irp = NULL;
|
|
PDEVICE_OBJECT TcpDeviceObject = NULL;
|
|
PFILE_OBJECT TcpFileObject = NULL;
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xd000);
|
|
|
|
ASSERT(&ConnectObject->SmbHeaderDpc == Dpc);
|
|
SMB_ACQUIRE_SPINLOCK(ConnectObject, Irql);
|
|
if (ConnectObject->State != SMB_CONNECTED) {
|
|
PUSH_LOCATION(ConnectObject, 0xd010);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_DPC);
|
|
return;
|
|
}
|
|
|
|
if (!ConnectObject->DpcRequestQueued) {
|
|
BREAK_WHEN_TAKE();
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xd020);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
//
|
|
// The TdiReceiveHandler has been called. Bail out.
|
|
//
|
|
// Note: TCP could call our receive event handler when new data comes in.
|
|
//
|
|
return;
|
|
}
|
|
|
|
ASSERT(NULL == ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE]);
|
|
ASSERT(ConnectObject->BytesInXport > 0);
|
|
ASSERT(ConnectObject->StateRcvHandler == WaitingHeader);
|
|
ASSERT(ConnectObject->BytesRemaining == 0);
|
|
ASSERT(ConnectObject->HeaderBytesRcved == 0);
|
|
|
|
Irp = SmbPrepareSessionHeaderIrp(ConnectObject);
|
|
if (NULL != Irp) {
|
|
PUSH_LOCATION(ConnectObject, 0xd030);
|
|
ConnectObject->PendingIRPs[SMB_PENDING_RECEIVE] = Irp;
|
|
ConnectObject->DpcRequestQueued = FALSE;
|
|
|
|
TcpFileObject = ConnectObject->TcpContext->Connect.ConnectObject;
|
|
TcpDeviceObject = IoGetRelatedDeviceObject(TcpFileObject);
|
|
|
|
//
|
|
// Reference the file object while we still holding the spinlock
|
|
//
|
|
ObReferenceObject(TcpFileObject);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
//
|
|
// Don't exchange the following 2 statements, we should reference in
|
|
// the context of SMB_REF_RECEIVE and then dereference the DPC context
|
|
//
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_RECEIVE);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_DPC);
|
|
|
|
status = IoCallDriver(TcpDeviceObject, Irp);
|
|
|
|
//
|
|
// Now we can dereference the TcpFileObject. It is the TCP's responsibility to
|
|
// do the synchronization between the deletion of the file object and accessing it
|
|
//
|
|
ObDereferenceObject(TcpFileObject);
|
|
} else {
|
|
PUSH_LOCATION(ConnectObject, 0xd040);
|
|
//
|
|
// The connection should already have been killed
|
|
//
|
|
ASSERT(ConnectObject->TcpContext == NULL);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_DPC);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
KillConnection(
|
|
IN PSMB_CONNECT ConnectObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Kill the connection immediately
|
|
|
|
Note: the ConnectObject->SpinLock is held when this routine is called
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// BREAK_WHEN_TAKE();
|
|
|
|
PUSH_LOCATION(ConnectObject, 0xe000);
|
|
|
|
SaveDisconnectOriginator(ConnectObject, SMB_DISCONNECT_RECEIVE_FAILURE);
|
|
if (ConnectObject->ClientObject == NULL) {
|
|
PUSH_LOCATION(ConnectObject, 0xe010);
|
|
return;
|
|
}
|
|
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_DISCONNECT);
|
|
|
|
SMB_RELEASE_SPINLOCK_DPC(ConnectObject);
|
|
CommonDisconnectHandler(SmbCfg.SmbDeviceObject, ConnectObject, TDI_DISCONNECT_ABORT);
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject);
|
|
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_DISCONNECT);
|
|
}
|