mirror of https://github.com/lianthony/NT4.0
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.
2469 lines
75 KiB
2469 lines
75 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
trans2.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines that exchange transact2 SMB's with
|
|
a remote server.
|
|
|
|
This file contains some routines (Trans2NetTranceiveCallback,
|
|
Trans2NetTranceiveComplete and Trans2NetTranceive that are closely
|
|
based on the routines in nettrans.c. Bug fixes should be duplicated
|
|
as appropriate.
|
|
|
|
This implementation does not add pad bytes between parameters and
|
|
data on transmission. This should not be a problem since they are
|
|
defined as optional in the SMB specification.
|
|
|
|
Author:
|
|
|
|
Colin Watson (ColinW) 29-Oct-1990
|
|
|
|
Revision History:
|
|
|
|
29-Oct-1990 ColinW
|
|
|
|
Created
|
|
|
|
--*/
|
|
|
|
#define INCLUDE_SMB_TRANSACTION
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
// Maximum PAD size between the parameters and data bytes. Officially this
|
|
// only needs to be 3 since it is optional to pad to DWORD alignment. The NT
|
|
// server is currently returning 10 bytes in certain circumstances.
|
|
|
|
#define PADMAX 16
|
|
|
|
#if RDRDBG1
|
|
void
|
|
ndump_core(
|
|
PCHAR far_p,
|
|
ULONG len
|
|
);
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
Trans2NetTranceive(
|
|
IN PTRANCEIVE2CONTEXT Context,
|
|
IN ULONG Flags,
|
|
IN PIRP Irp OPTIONAL,
|
|
IN PCONNECTLISTENTRY CLE,
|
|
IN PSMB_BUFFER SendMdl,
|
|
IN PSECURITY_ENTRY Se,
|
|
IN OUT PMPX_ENTRY *pMTE OPTIONAL
|
|
);
|
|
|
|
DBGSTATIC
|
|
STANDARD_CALLBACK_HEADER (
|
|
Trans2NetTranceiveCallback
|
|
);
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
Trans2NetTranceiveComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Ctx
|
|
);
|
|
|
|
NTSTATUS
|
|
NetTransmit(
|
|
IN ULONG Flags,
|
|
IN PIRP Irp OPTIONAL,
|
|
IN PCONNECTLISTENTRY CLE,
|
|
IN PMDL SendMdl,
|
|
IN PSECURITY_ENTRY Se,
|
|
IN OUT PMPX_ENTRY *pMTE OPTIONAL
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RdrTransact)
|
|
#pragma alloc_text(PAGE, Trans2NetTranceive)
|
|
#pragma alloc_text(PAGE, NetTransmit)
|
|
#pragma alloc_text(PAGE3FILE, Trans2NetTranceiveCallback)
|
|
#pragma alloc_text(PAGE3FILE, Trans2NetTranceiveComplete)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
RdrTransact(
|
|
IN PIRP Irp,
|
|
IN PCONNECTLISTENTRY Connection,
|
|
IN PSECURITY_ENTRY Se,
|
|
IN OUT PVOID Setup,
|
|
IN CLONG InSetupCount,
|
|
IN OUT PCLONG OutSetupCount,
|
|
IN PUNICODE_STRING Name OPTIONAL,
|
|
IN OUT VOID UNALIGNED *Parameters,
|
|
IN CLONG InParameterCount,
|
|
IN OUT PCLONG OutParameterCount,
|
|
IN VOID UNALIGNED *InData OPTIONAL,
|
|
IN CLONG InDataCount,
|
|
OUT VOID UNALIGNED *OutData OPTIONAL,
|
|
IN OUT PCLONG OutDataCount,
|
|
IN PUSHORT Fid OPTIONAL,
|
|
IN ULONG TimeoutInMilliseconds,
|
|
IN USHORT Flags,
|
|
IN USHORT NtTransactFunction,
|
|
IN PTRANSACTION_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
|
|
IN PVOID CallbackContext OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine exchanges a Transact or Transact2 with a remote server
|
|
synchronously.
|
|
|
|
The routine builds Mdl's to describe the data buffers provided by the
|
|
caller. During the transaction it builds partial Mdl's to describe
|
|
the parts of the buffer(s) which are to be tacked together for a
|
|
particular request.
|
|
|
|
|
|
Data structures for data going to the server.
|
|
============================================
|
|
|
|
SendSmbBuffer for header information and Setup bytes.
|
|
|
|
Parameter/ParameterMdl/ParameterPartialMdl is used to transmit the parameter
|
|
bytes. For each packet sent, ParameterPartialMdl is used to send a portion
|
|
of ParameterMdl if there are any parameters to send.
|
|
|
|
InData/InDataMdl are all the data bytes to be sent to the server. InPartialMdl
|
|
describes a section of this buffer that is going to be sent in a particular
|
|
SMB.
|
|
|
|
Data structures for data coming from the server.
|
|
===============================================
|
|
|
|
ReceiveSmbBuffer for header information and setup bytes.
|
|
|
|
Parameters/ParameterMdl/ParameterPartialMdl is a buffer used to receive
|
|
the parameter bytes.
|
|
The callback routine sets up ParameterPartialMdl to overlay
|
|
part of Parameters to correspond with the data bytes received in the SMB.
|
|
The incoming SMB informs the callback routine how many bytes and what
|
|
offset into Parameters.
|
|
|
|
OutData/OutDataMdl is where the caller has requested the data bytes to
|
|
be placed. The callback routine sets up OutDataPartialMdl to overlay
|
|
part of OutData to correspond with the data bytes received in the SMB.
|
|
The incoming SMB informs the callback routine how many bytes and what
|
|
offset into OutData.
|
|
|
|
Between Parameters and OutData there are 0-7 pad bytes that must not
|
|
be copied into Parameters since the parameter bytes can be received
|
|
in any order. PadMdl is used for these bytes.
|
|
|
|
Arguments:
|
|
|
|
IN PIRP Irp - Supplies an IRP to use in the request.
|
|
|
|
IN PCONNECTLISTENTRY Connection - Supplies the connection the file is
|
|
open on.
|
|
|
|
IN PSECURITY_ENTRY Se - Security context file is opened on.
|
|
|
|
|
|
IN OUT PVOID Setup - Setup buffer for both input and output
|
|
IN CLONG InSetupCount - Size in bytes
|
|
IN OUT PCLONG OutSetupCount
|
|
|
|
IN PUNICODE_STRING Name OPTIONAL - Name not used in Transact2's
|
|
|
|
IN OUT PVOID Parameters - Parameter buffer used for both input and output
|
|
so its length must be MAX( InParameterCount,
|
|
OutParameterCount)
|
|
IN CLONG InParameterCount - Supplies bytecount of parameters going to the server
|
|
IN OUT PCLONG OutParameterCount
|
|
|
|
IN PVOID InData OPTIONAL - Supplies data bytes going to the server
|
|
IN CLONG InDataCount // 0 if InData == NULL
|
|
|
|
OUT PVOID OutData OPTIONAL - Supplies to put data bytes from the server
|
|
IN OUT PCLONG OutDataCount // 0 if OutData == NULL
|
|
|
|
IN PUSHORT Fid OPTIONAL - Used in Transact2's to determine if we should
|
|
reconnect to the remote server.
|
|
|
|
IN ULONG TimeoutInMilliseconds - Value for smb_timeout
|
|
|
|
IN USHORT Flags - Supplies value for smb_flags values are:
|
|
0 |
|
|
[SMB_TRANSACTION_DISCONNECT] |
|
|
[SMB_TRANSACTION_NO_RESPONSE] |
|
|
[SMB_TRANSACTION_RECONNECTING]
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of request
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMB_BUFFER SendSmbBuffer = NULL;
|
|
PSMB_BUFFER ReceiveSmbBuffer = NULL;
|
|
PSMB_HEADER Header;
|
|
PREQ_TRANSACTION Request;
|
|
PREQ_NT_TRANSACTION NtRequest;
|
|
PMPX_ENTRY Mte = NULL; // MPX table entry used for whole exchange
|
|
|
|
NTSTATUS Status;
|
|
CLONG MaxBufferSize; // Servers negotiated buffersize
|
|
ULONG NameLen; // Calculated size of name field in Smb.
|
|
ULONG AnsiNameLen; // Calculated size of name field in Smb if
|
|
// cannot use UNICODE.
|
|
ULONG sizeofTransRequest;
|
|
ULONG sizeofTransResponse;
|
|
|
|
BOOLEAN WriteMailslot2D = FALSE;
|
|
PUSHORT pSetup;
|
|
PUSHORT pBcc;
|
|
UCHAR UNALIGNED *pParam;
|
|
UCHAR UNALIGNED *pData;
|
|
PUCHAR pName;
|
|
CLONG lParam, lData; // length of field
|
|
CLONG oParam, oData; // offset of field in SMB
|
|
CLONG dParam, dData; // displacement of these bytes
|
|
CLONG rParam, rData; // remaining bytes to be sent
|
|
|
|
// Full Mdl's covering data
|
|
PMDL InDataMdl = NULL;
|
|
BOOLEAN InDataMdlLocked = FALSE;
|
|
PMDL OutDataMdl = NULL;
|
|
BOOLEAN OutDataMdlLocked = FALSE;
|
|
PMDL ParameterMdl = NULL;
|
|
BOOLEAN ParameterMdlLocked = FALSE;
|
|
PMDL PadMdl = NULL;
|
|
BOOLEAN PadMdlLocked = FALSE;
|
|
ULONG TranceiveFlags = NT_NORMAL;
|
|
ULONG Longterm = 0;
|
|
|
|
//
|
|
// Partial Mdl covering part of [In|Out]DataMdl-position and length
|
|
// are set by the callback routine for OutDataMdl.
|
|
//
|
|
|
|
PMDL InDataPartialMdl = NULL;
|
|
PMDL OutDataPartialMdl = NULL;
|
|
|
|
// Partial Mdl used to describe Parameters
|
|
|
|
PMDL InParameterPartialMdl = NULL;
|
|
PMDL OutParameterPartialMdl = NULL;
|
|
|
|
TRANCEIVE2CONTEXT Context;
|
|
|
|
UCHAR PadBuff[PADMAX]; // 3 is maximum used for DWORD aligning data in an SMB
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Fill in the context information to be passed to the indication
|
|
// routine.
|
|
//
|
|
|
|
dprintf(DPRT_SMB, ("Trans2NetTranceive\n"));
|
|
|
|
ASSERT( (Flags & SMB_TRANSACTION_DISCONNECT) == 0);
|
|
|
|
if (ARGUMENT_PRESENT(Name)) {
|
|
AnsiNameLen = RtlUnicodeStringToAnsiSize(Name);
|
|
} else {
|
|
AnsiNameLen = NameLen = 1;
|
|
}
|
|
|
|
//
|
|
// Start the transaction.
|
|
//
|
|
//
|
|
|
|
//
|
|
// Initialize one of the cleanup fields to make sure we don't free
|
|
// up a bogus IRP.
|
|
//
|
|
|
|
Context.ReceiveIrp = NULL;
|
|
|
|
//
|
|
// Normally the maximum lengths for parameters and data is 0xffff
|
|
//
|
|
|
|
if ( NtTransactFunction == 0) {
|
|
if (( InParameterCount > 0x0ffff) ||
|
|
( *OutParameterCount > 0x0ffff) ||
|
|
( (*OutDataCount >0x0ffff) && ARGUMENT_PRESENT(OutData) ) ||
|
|
( (InDataCount >0x0ffff) && ARGUMENT_PRESENT(InData) )) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
sizeofTransRequest = sizeof( REQ_TRANSACTION );
|
|
sizeofTransResponse = sizeof( RESP_TRANSACTION );
|
|
} else {
|
|
|
|
sizeofTransRequest = sizeof( REQ_NT_TRANSACTION );
|
|
sizeofTransResponse = sizeof( RESP_NT_TRANSACTION );
|
|
}
|
|
|
|
//
|
|
// When we are writing to a mailslot, it's possible that
|
|
// we may want to limit the outgoing MaxBufferSize to 512 bytes
|
|
// if the InDataCount is small enough, and we are performing a 2nd class
|
|
// mailslot write.
|
|
//
|
|
|
|
|
|
if (ARGUMENT_PRESENT(Name) &&
|
|
(InDataCount < (512-sizeof(SMB_HEADER)-sizeofTransRequest-InSetupCount-AnsiNameLen)) &&
|
|
(InSetupCount >= 3*sizeof(USHORT)) &&
|
|
(((PUSHORT )Setup)[0] == TRANS_MAILSLOT_WRITE) &&
|
|
(((PUSHORT )Setup)[2] == 2)) {
|
|
|
|
WriteMailslot2D = TRUE;
|
|
MaxBufferSize = 512;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Give the guy a free reconnect attempt now. This guarantees that
|
|
// we know the negotiated buffer size and negotiated protocol level.
|
|
//
|
|
|
|
#ifdef _CAIRO_
|
|
if (!FlagOn(Flags, SMB_TRANSACTION_RECONNECTING)) {
|
|
Status = RdrReconnectConnection(Irp, Connection, Se);
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
#else // _CAIRO_
|
|
Status = RdrReconnectConnection(Irp, Connection, Se);
|
|
#endif // _CAIRO_
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
if (!(Connection->Server->Capabilities & DF_LANMAN10)) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
Context.Header.TransferSize = InSetupCount+*OutSetupCount+InParameterCount+*OutParameterCount+
|
|
InDataCount+*OutDataCount+sizeofTransRequest +
|
|
sizeofTransResponse;
|
|
|
|
//
|
|
// In addition, if this is a T1 SMB, we want to mark it as a longterm
|
|
// operation, otherwise, mark it as a short term operation.
|
|
//
|
|
|
|
if (TimeoutInMilliseconds >= (RdrRequestTimeout*1000)) {
|
|
Longterm = NT_LONGTERM;
|
|
} else if (ARGUMENT_PRESENT(Name)) {
|
|
if ( InSetupCount > 0 ) {
|
|
ASSERT( TRANS_SET_NMPIPE_STATE < TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_QUERY_NMPIPE_STATE < TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_QUERY_NMPIPE_INFO < TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_MAILSLOT_WRITE < TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_TRANSACT_NMPIPE > TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_READ_NMPIPE > TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_WRITE_NMPIPE > TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_WAIT_NMPIPE > TRANS_PEEK_NMPIPE );
|
|
ASSERT( TRANS_CALL_NMPIPE > TRANS_PEEK_NMPIPE );
|
|
ASSERT( *(PUSHORT)Setup != TRANS_RAW_READ_NMPIPE );
|
|
ASSERT( *(PUSHORT)Setup != TRANS_RAW_WRITE_NMPIPE );
|
|
if ( *(PUSHORT)Setup > TRANS_PEEK_NMPIPE ) {
|
|
Longterm = NT_LONGTERM;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Remote API. Try longterm, but don't require it.
|
|
// Otherwise, can't do remote APIs to Windows 95
|
|
// servers, which set MaximumRequests to 1.
|
|
//
|
|
|
|
Longterm = NT_PREFER_LONGTERM;
|
|
}
|
|
} else if ( NtTransactFunction != 0 ) {
|
|
Longterm = NT_PREFER_LONGTERM;
|
|
}
|
|
|
|
Status = RdrStartTranceive(Irp, Connection,
|
|
Se,
|
|
(BOOLEAN) (ARGUMENT_PRESENT(Fid) ? FALSE : TRUE), // Allow Reconnect
|
|
#ifdef _CAIRO_
|
|
BooleanFlagOn(Flags, SMB_TRANSACTION_RECONNECTING),
|
|
#else // _CAIRO_
|
|
FALSE, // Reconnecting
|
|
#endif // _CAIRO_
|
|
Longterm,
|
|
FALSE, // Cannot be canceled
|
|
&Mte,
|
|
Context.Header.TransferSize);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
//
|
|
// Now that we have reconnected to the remote server, determine
|
|
// the maximum buffer size based on the negotiated buffer
|
|
// size.
|
|
//
|
|
|
|
MaxBufferSize = Connection->Server->BufferSize;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Name)) {
|
|
|
|
if (Connection->Server->Capabilities & DF_UNICODE) {
|
|
NameLen = Name->Length + sizeof(WCHAR);
|
|
} else {
|
|
NameLen = AnsiNameLen;
|
|
}
|
|
|
|
//
|
|
// If this is a second class mailslot write, then the name is in
|
|
// ANSI, not unicode.
|
|
//
|
|
|
|
if (WriteMailslot2D) {
|
|
NameLen = AnsiNameLen;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that we have determined the protocol level, we can decide if we
|
|
// can allow this API to continue.
|
|
//
|
|
|
|
if ((NtTransactFunction != 0) &&
|
|
(!FlagOn(Connection->Server->Capabilities, DF_NT_SMBS))) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
//
|
|
// Build Mdl's covering the data buffers provided as arguments
|
|
//
|
|
|
|
if ( InDataCount && ARGUMENT_PRESENT( InData ) ) {
|
|
|
|
InDataMdl = IoAllocateMdl((PVOID)InData,InDataCount,FALSE,FALSE,NULL);
|
|
|
|
if (InDataMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
//
|
|
// Lock the pages associated with the Mdl that we just allocated.
|
|
//
|
|
|
|
dprintf(DPRT_SMB, ("Lock InData: %lx Length: %lx\n", InData, InDataCount));
|
|
|
|
try {
|
|
MmProbeAndLockPages( InDataMdl,
|
|
KernelMode,
|
|
IoReadAccess );
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
InDataMdlLocked = TRUE;
|
|
|
|
//
|
|
// InDataPartialMdl is created large enough to transfer the maximum
|
|
// size of data that we can send to the server in a single SMB.
|
|
// Note the use of the virtual address 0 and addition of PAGE_SIZE-1
|
|
// to allow for worst case alignment.
|
|
//
|
|
|
|
InDataPartialMdl = IoAllocateMdl(0,
|
|
MIN(MaxBufferSize-sizeof(SMB_HEADER)-sizeofTransRequest,
|
|
InDataCount) + PAGE_SIZE-1,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
if (InDataPartialMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// If InDataCount is non 0, then this means that the caller provided
|
|
// a non zero input buffer size, but no input buffer!
|
|
//
|
|
|
|
if (InDataCount) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ReturnStatus;
|
|
}
|
|
}
|
|
|
|
if ( *OutDataCount && ARGUMENT_PRESENT( OutData ) ) {
|
|
|
|
OutDataMdl = IoAllocateMdl((PVOID)OutData,*OutDataCount,FALSE,FALSE,NULL);
|
|
|
|
if (OutDataMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
//
|
|
// Lock down all of the buffer since the callback routine cannot
|
|
// do it and we don't know beforehand where OutDataPartialMdl will
|
|
// point inside OutData
|
|
//
|
|
|
|
dprintf(DPRT_SMB, ("Lock OutData: %lx Length: %lx\n", OutData, *OutDataCount ));
|
|
|
|
try {
|
|
MmProbeAndLockPages( OutDataMdl,
|
|
KernelMode,
|
|
IoWriteAccess );
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
OutDataMdlLocked = TRUE;
|
|
|
|
//
|
|
// OutDataPartialMdl is created large enough to transfer the maximum
|
|
// size of data that can come from the server in one SMB. Note the use
|
|
// of the virtual address 0 and addition of PAGE_SIZE-1 to allow
|
|
// for worst case alignment.
|
|
// The Mdl will be updated by the callback routine to point
|
|
// somewhere inside OutData when the data arrives.
|
|
//
|
|
|
|
OutDataPartialMdl = IoAllocateMdl(0,
|
|
MIN (MaxBufferSize-sizeof(SMB_HEADER)-sizeofTransResponse,
|
|
*OutDataCount ) + PAGE_SIZE-1,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (OutDataPartialMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If OutDataCount is non 0, then this means that the caller provided
|
|
// a non zero output buffer size, but no output buffer!
|
|
//
|
|
|
|
if (*OutDataCount) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ReturnStatus;
|
|
}
|
|
}
|
|
|
|
if ( *OutParameterCount || InParameterCount) {
|
|
|
|
// There may be a few pad bytes between the received and
|
|
// the data. PadBuff is used for these Pad bytes because
|
|
// the Smb protocol allows the server to send parameters
|
|
// and data in any order. If it was ascending order only we
|
|
// wouldnt need to bother.
|
|
|
|
PadMdl = IoAllocateMdl(PadBuff,
|
|
sizeof(PadBuff),
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (PadMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
try {
|
|
MmProbeAndLockPages( PadMdl,
|
|
KernelMode,
|
|
IoWriteAccess );
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
PadMdlLocked = TRUE;
|
|
|
|
ParameterMdl = IoAllocateMdl((PVOID)Parameters,
|
|
MAX(InParameterCount, *OutParameterCount),
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (ParameterMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
try {
|
|
MmProbeAndLockPages( ParameterMdl,
|
|
KernelMode,
|
|
IoWriteAccess );
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
goto ReturnStatus;
|
|
}
|
|
ParameterMdlLocked = TRUE;
|
|
|
|
InParameterPartialMdl = IoAllocateMdl(0,
|
|
MIN( InParameterCount, // Most we will tx/rx
|
|
MaxBufferSize-sizeof(SMB_HEADER)) + PAGE_SIZE-1,// in a single packet.
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (InParameterPartialMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
OutParameterPartialMdl = IoAllocateMdl(0,
|
|
MIN( *OutParameterCount, // Most we will tx/rx
|
|
MaxBufferSize-sizeof(SMB_HEADER)) + PAGE_SIZE-1,// in a single packet.
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (OutParameterPartialMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
SendSmbBuffer = RdrAllocateSMBBuffer();
|
|
|
|
if (SendSmbBuffer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
if (!(Flags & SMB_TRANSACTION_NO_RESPONSE)) {
|
|
ReceiveSmbBuffer = RdrAllocateSMBBuffer();
|
|
|
|
if (ReceiveSmbBuffer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build the primary request.
|
|
//
|
|
|
|
Header = (PSMB_HEADER)SendSmbBuffer->Buffer;
|
|
|
|
if ( ARGUMENT_PRESENT ( Name ) ) {
|
|
Header->Command = SMB_COM_TRANSACTION;
|
|
} else if (!NtTransactFunction) {
|
|
Header->Command = SMB_COM_TRANSACTION2;
|
|
} else {
|
|
Header->Command = SMB_COM_NT_TRANSACT;
|
|
}
|
|
|
|
//
|
|
// If this isn't a second class mailslot, this is an NT server,
|
|
// and we have an IRP provided, set the PID in the SMB.
|
|
//
|
|
|
|
if (!WriteMailslot2D
|
|
|
|
&&
|
|
|
|
(Connection->Server->Capabilities & DF_NT_SMBS)
|
|
|
|
&&
|
|
|
|
ARGUMENT_PRESENT(Irp)) {
|
|
|
|
ULONG Pid;
|
|
|
|
RdrSmbScrounge(Header, Connection->Server, BooleanFlagOn(Flags, SMB_TRANSACTION_DFSFILE), TRUE, TRUE);
|
|
Pid = (ULONG)IoGetRequestorProcess(Irp);
|
|
|
|
SmbPutUshort(&Header->Pid, (USHORT)(Pid & 0xFFFF));
|
|
|
|
SmbPutUshort(&Header->Reserved2[0], (USHORT)(Pid >> 16));
|
|
|
|
TranceiveFlags |= NT_DONTSCROUNGE;
|
|
}
|
|
|
|
Request = (PREQ_TRANSACTION)(Header + 1);
|
|
NtRequest = (PREQ_NT_TRANSACTION)(Header + 1);
|
|
|
|
// Zero all unused/reserved fields in the header
|
|
Request->Reserved = 0;
|
|
Request->Reserved3 = 0;
|
|
SmbPutUshort( &Request->Reserved2, 0);
|
|
|
|
if (NtTransactFunction) {
|
|
// Complete the fields in the transaction request
|
|
NtRequest->WordCount = (UCHAR)(19 + (InSetupCount/sizeof(USHORT)));
|
|
SmbPutUlong( &NtRequest->TotalParameterCount, InParameterCount );
|
|
SmbPutUlong( &NtRequest->TotalDataCount, InDataCount );
|
|
SmbPutUlong( &NtRequest->MaxParameterCount, *OutParameterCount );
|
|
SmbPutUlong( &NtRequest->MaxDataCount, *OutDataCount );
|
|
|
|
NtRequest->MaxSetupCount = (UCHAR)*OutSetupCount;
|
|
SmbPutUshort( &NtRequest->Flags, Flags );
|
|
SmbPutUshort( &NtRequest->Function, NtTransactFunction );
|
|
NtRequest->SetupCount = (UCHAR)(InSetupCount/sizeof(USHORT));
|
|
pSetup = (PUSHORT)NtRequest->Buffer;
|
|
} else {
|
|
// Complete the fields in the transaction request
|
|
Request->WordCount = (UCHAR)(14 + (InSetupCount/sizeof(USHORT)));
|
|
SmbPutUshort( &Request->TotalParameterCount, (USHORT)InParameterCount );
|
|
SmbPutUshort( &Request->TotalDataCount, (USHORT)InDataCount );
|
|
SmbPutUshort( &Request->MaxParameterCount, (USHORT)*OutParameterCount );
|
|
SmbPutUshort( &Request->MaxDataCount, (USHORT)*OutDataCount );
|
|
|
|
Request->MaxSetupCount = (UCHAR)*OutSetupCount;
|
|
SmbPutUshort( &Request->Flags, Flags );
|
|
Request->SetupCount = (UCHAR)(InSetupCount/sizeof(USHORT));
|
|
SmbPutUlong(&Request->Timeout, TimeoutInMilliseconds);
|
|
pSetup = (PUSHORT)Request->Buffer;
|
|
}
|
|
|
|
//
|
|
// The header, setup bytes, & name must fit in the first mdl
|
|
//
|
|
if ((((PUCHAR)pSetup-(PUCHAR)Header + InSetupCount + NameLen + 1) > SMB_BUFFER_SIZE ) ||
|
|
(((PUCHAR)pSetup-(PUCHAR)Header + InSetupCount + NameLen + 1) > MaxBufferSize )) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
// Copy in the setup bytes ( these must all fit into the first packet)
|
|
RtlCopyMemory( pSetup, Setup, InSetupCount );
|
|
|
|
pBcc = pSetup + (InSetupCount/2);
|
|
|
|
//
|
|
// Fill in smb_name
|
|
//
|
|
|
|
pName = (PUCHAR)(pBcc + 1);
|
|
|
|
if ((Connection->Server->Capabilities & DF_UNICODE) &&
|
|
(WriteMailslot2D == FALSE)) {
|
|
pName = ALIGN_SMB_WSTR(pName);
|
|
}
|
|
|
|
//
|
|
// Calculate where in the packet the parameter and data bytes will go.
|
|
// These will not be copied since a partial mdl will be used to chain
|
|
// parameters and data to the smbbuffer.
|
|
//
|
|
|
|
pParam = (PUCHAR)(pName + NameLen);
|
|
|
|
oParam = (pParam - (PUCHAR)Header + 3) & ~3;
|
|
pParam = (PUCHAR)Header + oParam;
|
|
lParam = InParameterCount;
|
|
|
|
//
|
|
// If there is either no data, or the parameters don't fit into a
|
|
// single SMB, don't send any data.
|
|
//
|
|
// If the parameters won't fit into a single SMB, only send the # of
|
|
// bytes of parameters that will fit into one SMB.
|
|
//
|
|
|
|
if ( oParam + lParam > MaxBufferSize || InDataCount == 0) {
|
|
|
|
//
|
|
// Only send as many parameter bytes to fill the packet
|
|
//
|
|
|
|
if (oParam + lParam > MaxBufferSize) {
|
|
lParam = MaxBufferSize - oParam;
|
|
}
|
|
|
|
pData = pParam + lParam;
|
|
oData = 0;
|
|
lData = 0;
|
|
} else {
|
|
|
|
//
|
|
// All the parameters fit into the first packet
|
|
//
|
|
|
|
pData = pParam + lParam;
|
|
|
|
oData = (pData - (PUCHAR)Header + 3) & ~3;
|
|
pData = (PUCHAR)Header + oData;
|
|
lData = InDataCount;
|
|
if ( oData + lData > MaxBufferSize ) {
|
|
// Some of the data will go in secondary requests
|
|
lData = (MaxBufferSize - oData) & ~3;
|
|
}
|
|
}
|
|
|
|
ASSERT( SMB_BUFFER_SIZE >= oParam ); // Fits in SmbBuffer?
|
|
|
|
// Set the SendSmbBuffer length to include the Smb header and setup bytes.
|
|
|
|
SendSmbBuffer->Mdl->ByteCount = oParam;
|
|
|
|
dprintf(DPRT_SMB, ("SendSmbBuffer: %lx Length: %lx\n",SendSmbBuffer->Buffer, MmGetMdlByteCount(SendSmbBuffer->Mdl) ));
|
|
|
|
if (NtTransactFunction) {
|
|
SmbPutUlong( &NtRequest->ParameterCount, lParam );
|
|
SmbPutUlong( &NtRequest->ParameterOffset, oParam );
|
|
SmbPutUlong( &NtRequest->DataCount, lData );
|
|
SmbPutUlong( &NtRequest->DataOffset, oData );
|
|
SmbPutUshort( pBcc, (USHORT)(pData - (PUCHAR)(pBcc+1) + lData) );
|
|
} else {
|
|
|
|
SmbPutUshort( &Request->ParameterCount, (USHORT)lParam );
|
|
SmbPutUshort( &Request->ParameterOffset, (USHORT)oParam );
|
|
SmbPutUshort( &Request->DataCount, (USHORT)lData );
|
|
SmbPutUshort( &Request->DataOffset, (USHORT)oData );
|
|
SmbPutUshort( pBcc, (USHORT)(pData - (PUCHAR)(pBcc+1) + lData) );
|
|
|
|
|
|
if ((Connection->Server->Capabilities & DF_UNICODE) &&
|
|
(WriteMailslot2D == FALSE )) {
|
|
|
|
if ( ARGUMENT_PRESENT(Name) ) {
|
|
|
|
RdrCopyUnicodeStringToUnicode( (PVOID *)&pName, Name, TRUE);
|
|
}
|
|
|
|
*((PWCH)pName) = L'\0';
|
|
|
|
} else {
|
|
if ( ARGUMENT_PRESENT(Name) ) {
|
|
|
|
Status = RdrCopyUnicodeStringToAscii( &pName, Name, TRUE, MAXIMUM_FILENAME_LENGTH);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
}
|
|
|
|
*pName = '\0';
|
|
}
|
|
}
|
|
|
|
if ( lParam ) {
|
|
|
|
// Set the Parameter length to the parameter length
|
|
|
|
IoBuildPartialMdl(ParameterMdl, InParameterPartialMdl,
|
|
(PVOID)Parameters, // first one is start of buffer
|
|
pData - pParam);
|
|
|
|
SendSmbBuffer->Mdl->Next = InParameterPartialMdl;
|
|
|
|
dprintf(DPRT_SMB, ("ParameterPartialMdl: %lx Length: %lx\n",
|
|
InParameterPartialMdl, MmGetMdlByteCount(InParameterPartialMdl) ));
|
|
|
|
if ( lData ) {
|
|
|
|
IoBuildPartialMdl(InDataMdl, InDataPartialMdl,
|
|
(PVOID)InData,
|
|
lData);
|
|
|
|
ASSERT (MmGetMdlByteCount(InDataPartialMdl)==lData);
|
|
|
|
//
|
|
// Now link the Mdl's to have the data after the Parameters
|
|
// the send.
|
|
//
|
|
InParameterPartialMdl->Next = InDataPartialMdl;
|
|
}
|
|
|
|
} else if ( lData ) {
|
|
|
|
IoBuildPartialMdl(InDataMdl, InDataPartialMdl,
|
|
(PVOID)InData,
|
|
lData);
|
|
|
|
ASSERT (MmGetMdlByteCount(InDataPartialMdl)==lData);
|
|
|
|
//
|
|
// Now link this new Mdl into the SMB buffer we allocated for
|
|
// the send.
|
|
//
|
|
|
|
SendSmbBuffer->Mdl->Next = InDataPartialMdl;
|
|
|
|
}
|
|
|
|
dprintf(DPRT_SMB, ("Setup Count: %x\n", Request->SetupCount ));
|
|
dprintf(DPRT_SMB, ("Parameter Count: %x Offset: %x\n", SmbGetUshort(&Request->ParameterCount) , SmbGetUshort(&Request->ParameterOffset) ));
|
|
dprintf(DPRT_SMB, ("Data Count: %x Offset: %x\n", SmbGetUshort(&Request->DataCount) , SmbGetUshort(&Request->DataOffset) ));
|
|
|
|
dParam = lParam;
|
|
rParam = InParameterCount - lParam;
|
|
dData = lData;
|
|
rData = InDataCount - lData;
|
|
|
|
//
|
|
// Build the context record to be used throughout this request.
|
|
//
|
|
|
|
Context.Header.Type = CONTEXT_TRAN2_CALLBACK;
|
|
Context.ParameterMdl = ParameterMdl;
|
|
Context.InParameterPartialMdl = InParameterPartialMdl;
|
|
Context.OutParameterPartialMdl = OutParameterPartialMdl;
|
|
Context.PadMdl = PadMdl;
|
|
Context.OutDataMdl = OutDataMdl;
|
|
Context.OutDataPartialMdl = OutDataPartialMdl;
|
|
if (ReceiveSmbBuffer != NULL) {
|
|
Context.ReceiveMdl = ReceiveSmbBuffer->Mdl;
|
|
} else {
|
|
Context.ReceiveMdl = NULL;
|
|
}
|
|
Context.Lparam = 0;
|
|
Context.Ldata = 0;
|
|
Context.Lsetup = 0;
|
|
Context.SetupWords = Setup;
|
|
Context.MaxSetupWords = *OutSetupCount;
|
|
Context.ParametersExpected = *OutParameterCount;
|
|
Context.DataExpected = *OutDataCount;
|
|
Context.ErrorMoreData = FALSE;
|
|
Context.Flags = Flags;
|
|
Context.MpxEntry = Mte;
|
|
|
|
Context.Routine = CompletionRoutine;
|
|
Context.CallbackContext = CallbackContext;
|
|
|
|
if (Mte != NULL) {
|
|
Context.TransactionStartTime = Mte->StartTime;
|
|
}
|
|
|
|
//
|
|
// Send the primary request, and receive either the interim response
|
|
// or the first (possibly only) secondary response.
|
|
//
|
|
dprintf(DPRT_SMB, ("Send the Primary request\n"));
|
|
|
|
//
|
|
// If this is a no response SMB, and
|
|
//
|
|
|
|
if (WriteMailslot2D) {
|
|
UNICODE_STRING DestinationName;
|
|
UCHAR SignatureByte = PRIMARY_DOMAIN_SIGNATURE;
|
|
|
|
ASSERT (PRIMARY_DOMAIN_SIGNATURE == WORKSTATION_SIGNATURE);
|
|
|
|
//
|
|
// If this transaction goes to "*", send it to the primary domain.
|
|
//
|
|
|
|
if ((Connection->Server->Text.Length == sizeof(WCHAR)) &&
|
|
(Connection->Server->Text.Buffer[0] == L'*')) {
|
|
DestinationName = RdrPrimaryDomain;
|
|
} else if (Connection->Server->Text.Buffer[(Connection->Server->Text.Length/sizeof(WCHAR))-1] == L'*') {
|
|
DestinationName = Connection->Server->Text;
|
|
if ( Connection->Server->Text.Length > 2*sizeof(WCHAR) &&
|
|
Connection->Server->Text.Buffer[(Connection->Server->Text.Length/sizeof(WCHAR))-2] == L'*') {
|
|
DestinationName.Length -= 2 * sizeof(WCHAR);
|
|
SignatureByte = PRIMARY_CONTROLLER_SIGNATURE;
|
|
} else {
|
|
DestinationName.Length -= sizeof(WCHAR);
|
|
SignatureByte = DOMAIN_CONTROLLER_SIGNATURE;
|
|
}
|
|
} else {
|
|
DestinationName = Connection->Server->Text;
|
|
}
|
|
|
|
//
|
|
// Clean up the SMB header somewhat.
|
|
//
|
|
|
|
RdrSmbScrounge(Header, NULL, BooleanFlagOn(Flags, SMB_TRANSACTION_DFSFILE), TRUE, TRUE);
|
|
|
|
Status = RdrTdiSendDatagramOnAllTransports(&DestinationName, SignatureByte, SendSmbBuffer->Mdl);
|
|
|
|
goto ReturnStatus;
|
|
|
|
} else {
|
|
|
|
TranceiveFlags |= Longterm;
|
|
|
|
if (FlagOn(Flags, SMB_TRANSACTION_DFSFILE)) {
|
|
TranceiveFlags |= NT_DFSFILE;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Fid)) {
|
|
TranceiveFlags |= NT_NORECONNECT;
|
|
}
|
|
|
|
#ifdef _CAIRO_
|
|
if (FlagOn(Flags, SMB_TRANSACTION_RECONNECTING)) {
|
|
TranceiveFlags |= NT_RECONNECTING;
|
|
}
|
|
#endif // _CAIRO_
|
|
|
|
if (!(Connection->Server->Capabilities & DF_LANMAN10)) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
//
|
|
// If there are no parameters or data to send,
|
|
// set the context block up to indicate that
|
|
// we are sending our last chunk of data.
|
|
//
|
|
|
|
if ((Flags & SMB_TRANSACTION_NO_RESPONSE) &&
|
|
(rParam == 0) &&
|
|
(rData == 0) ) {
|
|
|
|
Context.Header.Type = CONTEXT_TRAN2_END;
|
|
|
|
//
|
|
// We will not be receiving any response messages on this exchange.
|
|
//
|
|
|
|
TranceiveFlags |= NT_NORESPONSE;
|
|
}
|
|
|
|
Status = Trans2NetTranceive(&Context,
|
|
TranceiveFlags,
|
|
Irp,
|
|
Connection,
|
|
SendSmbBuffer,
|
|
Se,
|
|
&Mte);
|
|
|
|
|
|
dprintf(DPRT_SMB, ("Primary request completed with status:%X\n",Status));
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
//
|
|
// If there's more data to send, then interpret the response as an
|
|
// interim one, and send the remaining request messages.
|
|
//
|
|
|
|
if ( rParam | rData) {
|
|
|
|
// Set the event that indicates we have received everything back to
|
|
// the not-signalled state. When we received the InterimPacket it
|
|
// was set to signalled. Before we send the last chunk of parameters
|
|
// and data it must be reset.
|
|
|
|
KeClearEvent( &Context.Header.KernelEvent );
|
|
|
|
}
|
|
|
|
while ( rParam | rData ) {
|
|
PREQ_TRANSACTION_SECONDARY Request; // overrides outer declaration
|
|
PREQ_NT_TRANSACTION_SECONDARY NtRequest; // overrides outer declaration
|
|
|
|
dprintf(DPRT_SMB, ("rParam: %lx rData: %lx\n", rParam, rData));
|
|
|
|
//
|
|
// We're going to be re-using the parameter and data partial MDL,
|
|
// so we want to prepare it to be re-used.
|
|
//
|
|
|
|
if (InParameterPartialMdl != NULL) {
|
|
MmPrepareMdlForReuse(InParameterPartialMdl);
|
|
}
|
|
|
|
if (InDataPartialMdl != NULL) {
|
|
MmPrepareMdlForReuse(InDataPartialMdl);
|
|
}
|
|
|
|
//
|
|
// If we are expecting a response to this transaction, reload
|
|
// the callback routine.
|
|
//
|
|
|
|
if (!FlagOn(Flags, SMB_TRANSACTION_NO_RESPONSE)) {
|
|
|
|
ASSERT (!(Context.Flags & SMB_TRANSACTION_NO_RESPONSE));
|
|
|
|
// Tell indication routine its ok to use the Context.
|
|
|
|
Context.Header.Type = CONTEXT_TRAN2_CALLBACK;
|
|
|
|
RdrSetCallbackTranceive(Context.MpxEntry, Context.TransactionStartTime, Trans2NetTranceiveCallback);
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Build the secondary request.
|
|
//
|
|
|
|
Request = (PREQ_TRANSACTION_SECONDARY)(Header + 1);
|
|
NtRequest = (PREQ_NT_TRANSACTION_SECONDARY)(Header + 1);
|
|
|
|
RtlZeroMemory( (PVOID)Request, sizeof(REQ_TRANSACTION_SECONDARY) );
|
|
|
|
if ( ARGUMENT_PRESENT ( Name ) ) {
|
|
Header->Command = SMB_COM_TRANSACTION_SECONDARY;
|
|
Request->WordCount = (UCHAR)8;
|
|
} else if (!NtTransactFunction) {
|
|
Header->Command = SMB_COM_TRANSACTION2_SECONDARY;
|
|
Request->WordCount = (UCHAR)9;
|
|
} else {
|
|
Header->Command = SMB_COM_NT_TRANSACT_SECONDARY;
|
|
NtRequest->WordCount = (UCHAR)18;
|
|
|
|
}
|
|
|
|
if (NtTransactFunction) {
|
|
SmbPutUlong(
|
|
&NtRequest->TotalParameterCount,
|
|
InParameterCount
|
|
);
|
|
SmbPutUlong( &NtRequest->TotalDataCount, InDataCount );
|
|
pParam = NtRequest->Buffer + 2; // Leave space for 9th word (fid)
|
|
|
|
} else {
|
|
|
|
SmbPutUshort(
|
|
&Request->TotalParameterCount,
|
|
(USHORT)InParameterCount
|
|
);
|
|
SmbPutUshort( &Request->TotalDataCount, (USHORT)InDataCount );
|
|
pParam = Request->Buffer + 2; // Leave space for 9th word (fid)
|
|
}
|
|
|
|
oParam = (pParam - (PUCHAR)Header + 3) & ~3;
|
|
pParam = (PUCHAR)Header + oParam;
|
|
lParam = rParam;
|
|
if ( oParam + lParam > MaxBufferSize ) {
|
|
lParam = MaxBufferSize - oParam; // Pad bytes are optional
|
|
// we omit them!
|
|
pData = pParam + lParam;
|
|
oData = 0;
|
|
lData = 0;
|
|
} else {
|
|
pData = pParam + lParam;
|
|
|
|
//
|
|
// Don't pad if there is no data to be sent.
|
|
//
|
|
|
|
lData = rData;
|
|
if ( rData != 0 ) {
|
|
oData = (pData - (PUCHAR)Header + 3) & ~3;
|
|
pData = (PUCHAR)Header + oData;
|
|
|
|
if ( oData + lData > (MaxBufferSize ) ) {
|
|
lData = (MaxBufferSize - oData) & ~3;
|
|
}
|
|
|
|
} else {
|
|
oData = pData - (PUCHAR)Header;
|
|
}
|
|
}
|
|
|
|
if (NtTransactFunction) {
|
|
|
|
SmbPutUlong( &NtRequest->ParameterCount, lParam );
|
|
SmbPutUlong( &NtRequest->ParameterOffset, oParam );
|
|
SmbPutUlong( &NtRequest->ParameterDisplacement, dParam );
|
|
SmbPutUlong( &NtRequest->DataCount, lData );
|
|
SmbPutUlong( &NtRequest->DataOffset, oData );
|
|
SmbPutUlong( &NtRequest->DataDisplacement, dData );
|
|
|
|
} else {
|
|
|
|
SmbPutUshort( &Request->ParameterCount, (USHORT)lParam );
|
|
SmbPutUshort( &Request->ParameterOffset, (USHORT)oParam );
|
|
SmbPutUshort( &Request->ParameterDisplacement, (USHORT)dParam );
|
|
SmbPutUshort( &Request->DataCount, (USHORT)lData );
|
|
SmbPutUshort( &Request->DataOffset, (USHORT)oData );
|
|
SmbPutUshort( &Request->DataDisplacement, (USHORT)dData );
|
|
|
|
if (ARGUMENT_PRESENT(Name)) {
|
|
|
|
SmbPutUshort(
|
|
&Request->ByteCount,
|
|
(USHORT)(pData - (Request->Buffer) + lData)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Since we are using the transaction response description,
|
|
// but this is a transaction2, put the ByteCount 2 bytes
|
|
// back so that it is in the right place.
|
|
//
|
|
|
|
SmbPutUshort(
|
|
&(Request->ByteCount) + 1,
|
|
(USHORT)(pData - (Request->Buffer+2) + lData)
|
|
);
|
|
|
|
}
|
|
}
|
|
// Tell the transport how long the header is.
|
|
|
|
SendSmbBuffer->Mdl->ByteCount = oParam;
|
|
|
|
if ( lParam > 0 ) {
|
|
// Set the Parameter length to the parameter length
|
|
|
|
IoBuildPartialMdl(ParameterMdl, InParameterPartialMdl,
|
|
(PCHAR)Parameters+dParam,
|
|
lParam);
|
|
|
|
SendSmbBuffer->Mdl->Next = InParameterPartialMdl;
|
|
|
|
dprintf(DPRT_SMB, ("InParameterPartialMdl: %lx Length: %lx\n",
|
|
InParameterPartialMdl, MmGetMdlByteCount(InParameterPartialMdl) ));
|
|
|
|
if ( lData > 0 ) {
|
|
|
|
IoBuildPartialMdl(InDataMdl, InDataPartialMdl,
|
|
(PCHAR)InData+dData,
|
|
lData);
|
|
|
|
ASSERT (MmGetMdlByteCount(InDataPartialMdl)==lData);
|
|
|
|
//
|
|
// Now link this new Mdl into the SMB buffer we allocated for
|
|
// the send.
|
|
//
|
|
|
|
InParameterPartialMdl->Next = InDataPartialMdl;
|
|
|
|
}
|
|
|
|
} else if ( lData > 0 ) {
|
|
|
|
IoBuildPartialMdl(InDataMdl, InDataPartialMdl,
|
|
(PCHAR)InData+dData,
|
|
lData);
|
|
|
|
ASSERT (MmGetMdlByteCount(InDataPartialMdl)==lData);
|
|
|
|
//
|
|
// Now link this new Mdl into the SMB buffer we allocated for
|
|
// the send.
|
|
//
|
|
|
|
SendSmbBuffer->Mdl->Next = InDataPartialMdl;
|
|
|
|
}
|
|
|
|
dParam = lParam;
|
|
rParam = rParam - lParam;
|
|
dData = dData + lData;
|
|
rData = rData - lData;
|
|
|
|
//
|
|
// If there is no more data or parameters to send, set things
|
|
// up so we will be kicked out when we finish this send.
|
|
//
|
|
|
|
if ((Flags & SMB_TRANSACTION_NO_RESPONSE) &&
|
|
(rParam == 0) &&
|
|
(rData == 0) ) {
|
|
Context.Header.Type = CONTEXT_TRAN2_END;
|
|
}
|
|
|
|
if ((Connection->Server->Capabilities & DF_NT_SMBS)
|
|
|
|
&&
|
|
|
|
ARGUMENT_PRESENT(Irp)) {
|
|
|
|
ULONG Pid;
|
|
|
|
//
|
|
// Stick the PID in the SMB.
|
|
//
|
|
|
|
RdrSmbScrounge(Header, Connection->Server, BooleanFlagOn(Flags, SMB_TRANSACTION_DFSFILE), TRUE, TRUE);
|
|
|
|
Pid = (ULONG)IoGetRequestorProcess(Irp);
|
|
|
|
SmbPutUshort(&Header->Pid, (USHORT)(Pid & 0xFFFF));
|
|
|
|
SmbPutUshort(&Header->Reserved2[0], (USHORT)(Pid >> 16));
|
|
|
|
} else {
|
|
|
|
RdrSmbScrounge(Header, Connection->Server, FALSE, TRUE, TRUE);
|
|
|
|
}
|
|
|
|
Status = NetTransmit(NT_NORMAL | NT_DONTSCROUNGE,
|
|
Irp,
|
|
Connection,
|
|
SendSmbBuffer->Mdl,
|
|
Se,
|
|
&Mte);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto ReturnStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// All request messages have been sent, and the first response has
|
|
// been received. Wait for all the params/data to be received.
|
|
//
|
|
|
|
if (Context.Header.Type != CONTEXT_TRAN2_END) {
|
|
|
|
Status = RdrWaitTranceive(Mte);
|
|
}
|
|
|
|
if (Context.Header.ErrorType==NoError) {
|
|
ASSERT(Context.Header.Type == CONTEXT_TRAN2_END);
|
|
|
|
// if (Context.ErrorMoreData == FALSE) {
|
|
Status = STATUS_SUCCESS;
|
|
// } else {
|
|
// Status = STATUS_BUFFER_OVERFLOW;
|
|
// }
|
|
} else {
|
|
Status = Context.Header.ErrorCode;
|
|
}
|
|
|
|
// Inform caller of how much was actually received
|
|
|
|
*OutDataCount = Context.Ldata;
|
|
*OutParameterCount = Context.Lparam;
|
|
*OutSetupCount = Context.Lsetup;
|
|
}
|
|
|
|
ReturnStatus:
|
|
|
|
//
|
|
// Free up all datastructures. Note that several of these structures were
|
|
// allocated inside Trans2NetTranceive but are used by the callback
|
|
// routines so are deleted here -- Yuck.
|
|
//
|
|
|
|
if (Mte != NULL) {
|
|
// Free Mte, release RawResource and GateSemaphore
|
|
RdrEndTranceive(Mte);
|
|
}
|
|
|
|
if ( InDataPartialMdl != NULL ) {
|
|
IoFreeMdl(InDataPartialMdl);
|
|
}
|
|
|
|
if ( OutDataPartialMdl != NULL ) {
|
|
IoFreeMdl(OutDataPartialMdl);
|
|
}
|
|
|
|
if ( InParameterPartialMdl != NULL ) {
|
|
IoFreeMdl(InParameterPartialMdl);
|
|
}
|
|
|
|
if ( OutParameterPartialMdl != NULL ) {
|
|
IoFreeMdl(OutParameterPartialMdl);
|
|
}
|
|
|
|
if ( InDataMdl != NULL ) {
|
|
if (InDataMdlLocked) {
|
|
MmUnlockPages( InDataMdl );
|
|
}
|
|
IoFreeMdl(InDataMdl);
|
|
}
|
|
|
|
if ( OutDataMdl != NULL ) {
|
|
dprintf(DPRT_SMB, ("Unlock OutData: %lx\n", OutData));
|
|
if (OutDataMdlLocked) {
|
|
MmUnlockPages( OutDataMdl );
|
|
}
|
|
IoFreeMdl(OutDataMdl);
|
|
}
|
|
|
|
if ( ParameterMdl != NULL ) {
|
|
if (ParameterMdlLocked) {
|
|
MmUnlockPages(ParameterMdl);
|
|
}
|
|
IoFreeMdl(ParameterMdl);
|
|
}
|
|
|
|
if ( PadMdl != NULL ) {
|
|
if (PadMdlLocked) {
|
|
MmUnlockPages(PadMdl);
|
|
}
|
|
IoFreeMdl(PadMdl);
|
|
}
|
|
|
|
if (SendSmbBuffer != NULL) {
|
|
RdrFreeSMBBuffer(SendSmbBuffer);
|
|
}
|
|
|
|
if (ReceiveSmbBuffer != NULL) {
|
|
RdrFreeSMBBuffer(ReceiveSmbBuffer);
|
|
}
|
|
|
|
if ( Context.ReceiveIrp) {
|
|
FREE_IRP( Context.ReceiveIrp, 29, &Context );
|
|
}
|
|
|
|
dprintf(DPRT_SMB, ("RdrTransact return Status: %X\n", Status));
|
|
|
|
return Status;
|
|
|
|
} // Transact
|
|
|
|
|
|
|
|
NTSTATUS
|
|
Trans2NetTranceive(
|
|
IN PTRANCEIVE2CONTEXT Context,
|
|
IN ULONG Flags,
|
|
IN PIRP Irp OPTIONAL,
|
|
IN PCONNECTLISTENTRY CLE,
|
|
IN PSMB_BUFFER SendSmbBuffer,
|
|
IN PSECURITY_ENTRY Se,
|
|
IN OUT PMPX_ENTRY *pMTE OPTIONAL
|
|
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine exchanges an SMB with a remote server synchronously.
|
|
|
|
The differences between this routine and the one in nettrans.c is that
|
|
it allows the use of the same multiplex table entry over a series of
|
|
SMB's. It also puts more information into the context so that the
|
|
callback routine can direct the received SMB into the InterimParam
|
|
buffer and into the callers buffer.
|
|
|
|
Arguments:
|
|
|
|
IN PTRANCEIVE2CONTEXT Context - Supplies the environment for this sequence
|
|
of smb's being exchanged.
|
|
IN PIRP Irp - Supplies an IRP to use in the request.
|
|
IN PCONNECTLISTENTRY CLE - Supplies the SLE on which to exchange SMB
|
|
IN PMDL SendMdl - Supplies an Mdl containing the Send request.
|
|
IN PSECURITY_ENTRY Se - Security entry associated with exchange
|
|
IN OUT PMPX_ENTRY *pMTE - MPX table entry associated with exchange.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN RetryOperation = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
dprintf(DPRT_SMB, ("RdrTransact start\n"));
|
|
|
|
ASSERT (Se->Signature == STRUCTURE_SIGNATURE_SECURITYENTRY);
|
|
|
|
do {
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
RetryOperation = FALSE;
|
|
|
|
if (ARGUMENT_PRESENT(pMTE) && (*pMTE != NULL)) {
|
|
|
|
RdrEndTranceive(*pMTE);
|
|
|
|
}
|
|
|
|
#ifdef _CAIRO_
|
|
if (!FlagOn(Flags, NT_RECONNECTING))
|
|
#endif // _CAIRO_
|
|
Status = RdrReconnectConnection(Irp, CLE, Se);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
*pMTE = NULL;
|
|
|
|
goto Return;
|
|
}
|
|
|
|
RdrStatistics.Reconnects += 1;
|
|
|
|
if (ARGUMENT_PRESENT(pMTE) && (*pMTE != NULL)) {
|
|
|
|
Status = RdrStartTranceive(Irp, CLE, Se,
|
|
(BOOLEAN )(Flags & NT_NORECONNECT ? FALSE : TRUE),
|
|
FALSE, // Reconnecting
|
|
Flags & (NT_LONGTERM | NT_PREFER_LONGTERM),
|
|
(BOOLEAN )(Flags & NT_CANNOTCANCEL ? TRUE : FALSE),
|
|
pMTE,
|
|
Context->Header.TransferSize);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
*pMTE = NULL;
|
|
|
|
goto Return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reload the context state pointer to indicate that we are
|
|
// retrying the operation.
|
|
//
|
|
if ( Flags & NT_NORESPONSE ) {
|
|
Context->Header.Type = CONTEXT_TRAN2_END;
|
|
} else {
|
|
Context->Header.Type = CONTEXT_TRAN2_CALLBACK;
|
|
}
|
|
|
|
Context->TransactionStartTime = (*pMTE)->StartTime;
|
|
Context->MpxEntry = *pMTE;
|
|
}
|
|
|
|
|
|
if (Context->ReceiveMdl) {
|
|
|
|
BOOLEAN BufferLocked = TRUE;
|
|
|
|
Context->ReceiveIrp = ALLOCATE_IRP(
|
|
CLE->Server->ConnectionContext->ConnectionObject,
|
|
NULL,
|
|
22,
|
|
Context
|
|
);
|
|
|
|
if (Context->ReceiveIrp == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Return;
|
|
}
|
|
|
|
Context->Se = Se;
|
|
|
|
#if RDRDBG1
|
|
{
|
|
PIRP Irp = Context->ReceiveIrp;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
dprintf(DPRT_SMB, ("Irp %lx IrpSp %lx\n",
|
|
Irp, IrpSp));
|
|
ndump_core( (PCHAR) Irp, IoSizeOfIrp( Irp->StackCount ));
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Put the request on the wire. The context provided is
|
|
// sufficient to handle all the response information.
|
|
//
|
|
|
|
Status = RdrNetTranceiveWithCallback (
|
|
Flags | NT_NORECONNECT,
|
|
Irp,
|
|
CLE,
|
|
SendSmbBuffer->Mdl,
|
|
Context,
|
|
(Flags & NT_NORESPONSE)? NULL : Trans2NetTranceiveCallback,
|
|
Se,
|
|
pMTE);
|
|
|
|
#ifdef _CAIRO_
|
|
if (FlagOn(Flags, NT_RECONNECTING) && !NT_SUCCESS(Status)) {
|
|
dprintf(0, ("Error %08lx in tranceive during reconnect", Status));
|
|
}
|
|
#endif // _CAIRO_
|
|
|
|
} while ( !NT_SUCCESS(Status) &&
|
|
|
|
(Context->Header.ErrorType == NetError) &&
|
|
|
|
RetryOperation );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Return;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Either the network request succeeded, or there was some
|
|
// kind of either network or transport error. If there was
|
|
// no error, return success, otherwise map the error and
|
|
// return to the caller.
|
|
//
|
|
//
|
|
|
|
if (Context->Header.ErrorType != NoError) {
|
|
Status = Context->Header.ErrorCode;
|
|
goto Return;
|
|
}
|
|
|
|
Return:
|
|
|
|
dprintf(DPRT_SMB, ("Trans2NetTranceive return status: %X\n", Status));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
DBGSTATIC
|
|
STANDARD_CALLBACK_HEADER (
|
|
Trans2NetTranceiveCallback
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Trans2NetTranceiveCallback - Indication callback for user request
|
|
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked by either the receive based indication lookahead
|
|
routine from the transport, or by the connection invalidating
|
|
code.
|
|
|
|
The major difference between this callback routine and its equivalent in
|
|
nettrans.c is that this one builds a chain of Mdls so that the Smb
|
|
header and setup bytes with any trailing pad go into the SMB_BUFFER,
|
|
the parameter bytes and any trailing pad go into InterimParam and the
|
|
data goes directly into the callers buffer.
|
|
|
|
Arguments:
|
|
|
|
Smb[Length] - Incoming SMB buffer
|
|
MpxEntry - Mpx Table entry for request.
|
|
Context - Context information passed into NetTranceiveNoWait
|
|
ErrorIndicator - TRUE if the network request was in error.
|
|
NetworkErrorCode - Error code if request completed with network error
|
|
Irp - Pointer to the I/O request packet from the transport
|
|
|
|
|
|
Return Value:
|
|
|
|
Return value to be returned from receive indication routine.
|
|
|
|
|
|
Note:
|
|
|
|
This routine can be called for two different reasons. The
|
|
first (and most common) reason is when the receive indication event
|
|
notification comes from the server for this request. In that case,
|
|
this routine should format up a receive to read the response to the
|
|
request and pass the request to the transport to complete the
|
|
request.
|
|
|
|
If the connection is dropped from the transport, the code
|
|
that walks the multiplex table completing requests will call
|
|
this routine with the ErrorIndicator flag set to TRUE, and the
|
|
NetworkErrorCode field set to the error from the transport.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESP_TRANSACTION Response;
|
|
PRESP_NT_TRANSACTION NtResponse;
|
|
|
|
ULONG ParameterCount;
|
|
ULONG ParameterOffset;
|
|
ULONG ParameterDisplacement;
|
|
ULONG TotalParameterCount;
|
|
|
|
ULONG DataCount;
|
|
ULONG DataOffset;
|
|
ULONG DataDisplacement;
|
|
|
|
ULONG TotalDataCount;
|
|
|
|
VOID UNALIGNED *SetupPointer;
|
|
CCHAR SetupCount;
|
|
|
|
PTRANCEIVE2CONTEXT Context = (PTRANCEIVE2CONTEXT) Ctx;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(MpxEntry);
|
|
UNREFERENCED_PARAMETER(Server);
|
|
|
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|
|
|
ASSERT(MpxEntry->Signature == STRUCTURE_SIGNATURE_MPX_ENTRY);
|
|
|
|
Context->Header.ErrorType = NoError; // Assume no error at first
|
|
Context->Header.ErrorCode = STATUS_SUCCESS;
|
|
|
|
try {
|
|
KIRQL OldIrql;
|
|
NTSTATUS SmbStatus;
|
|
|
|
if (ErrorIndicator) {
|
|
|
|
//
|
|
// If we are still expecting a callback, fail the incoming
|
|
// request, otherwise ignore this error, since it doesn't affect
|
|
// this particular request.
|
|
//
|
|
|
|
if (Context->Header.Type == CONTEXT_TRAN2_CALLBACK) {
|
|
|
|
dprintf(DPRT_SMB|DPRT_ERROR, ("Trans2NetTranceiveCallback error indicator set\n"));
|
|
|
|
Context->Header.ErrorType = NetError;
|
|
|
|
Context->Header.ErrorCode = NetworkErrorCode;
|
|
|
|
} else {
|
|
|
|
dprintf(DPRT_SMB|DPRT_ERROR, ("Trans2NetTranceiveCallback crossed paths and error indicator set\n"));
|
|
}
|
|
|
|
try_return( Status ); // Response ignored.
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// This event had better not be in the signalled state
|
|
// now.
|
|
//
|
|
|
|
ASSERT (KeReadStateEvent(&Context->Header.KernelEvent) == 0);
|
|
|
|
ACQUIRE_SPIN_LOCK(&RdrMpxTableSpinLock, &OldIrql);
|
|
|
|
//
|
|
// Flag that this request cannot be timed out right now.
|
|
//
|
|
|
|
MpxEntry->StartTime = 0xffffffff;
|
|
|
|
RELEASE_SPIN_LOCK(&RdrMpxTableSpinLock, OldIrql);
|
|
|
|
ASSERT(Context->Header.Type == CONTEXT_TRAN2_CALLBACK);
|
|
|
|
Response = (PRESP_TRANSACTION)(Smb+1);
|
|
NtResponse = (PRESP_NT_TRANSACTION)(Smb+1);
|
|
|
|
SmbStatus = RdrMapSmbError(Smb, Server);
|
|
|
|
//
|
|
// If this SMB wasn't in error, pull the transaction information out
|
|
// of the SMB header.
|
|
//
|
|
|
|
if (Response->WordCount != 0) {
|
|
|
|
if ((Smb->Command == SMB_COM_NT_TRANSACT) ||
|
|
(Smb->Command == SMB_COM_NT_TRANSACT_SECONDARY)) {
|
|
|
|
ParameterCount = SmbGetUlong(&NtResponse->ParameterCount);
|
|
ParameterOffset = SmbGetUlong(&NtResponse->ParameterOffset);
|
|
ParameterDisplacement = SmbGetUlong(&NtResponse->ParameterDisplacement);
|
|
TotalParameterCount = SmbGetUlong(&NtResponse->TotalParameterCount);
|
|
|
|
DataCount = SmbGetUlong(&NtResponse->DataCount);
|
|
DataOffset = SmbGetUlong(&NtResponse->DataOffset);
|
|
DataDisplacement = SmbGetUlong(&NtResponse->DataDisplacement);
|
|
TotalDataCount = SmbGetUlong(&NtResponse->TotalDataCount);
|
|
|
|
SetupCount = NtResponse->SetupCount;
|
|
SetupPointer = NtResponse->Buffer;
|
|
|
|
} else {
|
|
|
|
ParameterCount = SmbGetUshort(&Response->ParameterCount);
|
|
ParameterOffset = SmbGetUshort(&Response->ParameterOffset);
|
|
ParameterDisplacement = SmbGetUshort(&Response->ParameterDisplacement);
|
|
TotalParameterCount = SmbGetUshort(&Response->TotalParameterCount);
|
|
|
|
DataCount = SmbGetUshort(&Response->DataCount);
|
|
DataOffset = SmbGetUshort(&Response->DataOffset);
|
|
DataDisplacement = SmbGetUshort(&Response->DataDisplacement);
|
|
TotalDataCount = SmbGetUshort(&Response->TotalDataCount);
|
|
|
|
SetupCount = Response->SetupCount;
|
|
SetupPointer = Response->Buffer;
|
|
}
|
|
|
|
dprintf(DPRT_SMB, ("Trans2NetTranceiveCallback SmbLength: %lx\n",*SmbLength));
|
|
|
|
dprintf(DPRT_SMB, ("Setup Count: %x\n", SetupCount));
|
|
|
|
dprintf(DPRT_SMB, ("Parameter Count: %x Offset: %x Displacement: %x, Expected: %x\n",
|
|
ParameterCount,
|
|
ParameterOffset,
|
|
ParameterDisplacement,
|
|
TotalParameterCount ));
|
|
|
|
dprintf(DPRT_SMB, ("Data Count: %x Offset: %x Displacement: %x, Expected: %x\n",
|
|
DataCount,
|
|
DataOffset,
|
|
DataDisplacement,
|
|
TotalDataCount ));
|
|
}
|
|
|
|
//
|
|
// Sometimes the server returns DataOffset of 0.
|
|
//
|
|
|
|
if (DataOffset == 0 && DataCount == 0) {
|
|
DataOffset = ParameterOffset + ParameterCount;
|
|
}
|
|
|
|
//
|
|
// Check to make sure that the data and parameter offsets are
|
|
// within the servers negotiated buffer size (and thus will fit within
|
|
// this response SMB, and that the pad data area size is less than
|
|
// PADMAX. If any of these conditions aren't met, then bounce this
|
|
// SMB.
|
|
//
|
|
// Also check to make sure that the response is smaller than the
|
|
// total number of bytes requested - if the server sends us a
|
|
// response that is larger than what we requested, the transport will
|
|
// probably bugcheck.
|
|
//
|
|
|
|
if (NT_SUCCESS(SmbStatus) &&
|
|
(Response->WordCount != 0) &&
|
|
(
|
|
((LONG)ParameterOffset < 0) ||
|
|
((LONG)ParameterCount < 0) ||
|
|
(ParameterOffset+ParameterCount > MpxEntry->SLE->BufferSize) ||
|
|
((LONG)TotalParameterCount < 0) ||
|
|
(MAX(TotalParameterCount,ParameterCount) >
|
|
((Context->ParameterMdl == NULL) ? 0 : MmGetMdlByteCount(Context->ParameterMdl))) ||
|
|
(DataOffset < ParameterOffset) ||
|
|
((LONG)DataOffset < 0) ||
|
|
((LONG)DataCount < 0) ||
|
|
(DataOffset+DataCount > MpxEntry->SLE->BufferSize) ||
|
|
((LONG)TotalDataCount < 0) ||
|
|
(MAX(TotalDataCount,DataCount) >
|
|
((Context->OutDataMdl == NULL) ? 0 : MmGetMdlByteCount(Context->OutDataMdl))) ||
|
|
((ParameterOffset != 0) &&
|
|
(DataOffset != 0) &&
|
|
((DataOffset - ParameterOffset) - ParameterCount) > PADMAX)
|
|
)
|
|
) {
|
|
|
|
//
|
|
// We can't accept this incoming transaction, it must be bad.
|
|
//
|
|
// Blow off the remainder of this request.
|
|
//
|
|
|
|
RdrWriteErrorLogEntry(Server, IO_ERR_LAYERED_FAILURE, EVENT_RDR_INVALID_SMB, STATUS_SUCCESS, Smb, (USHORT)*SmbLength);
|
|
Context->Header.ErrorType = SMBError;
|
|
Context->Header.ErrorCode = STATUS_UNEXPECTED_NETWORK_ERROR;
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// If the API failed, remember the failure mode.
|
|
//
|
|
|
|
if (!NT_SUCCESS(SmbStatus)) {
|
|
Context->Header.ErrorType = SMBError;
|
|
Context->Header.ErrorCode = SmbStatus;
|
|
}
|
|
|
|
if (Response->WordCount == 0) {
|
|
|
|
if (!NT_SUCCESS(SmbStatus)) {
|
|
|
|
//
|
|
// If the API is failing, don't bother to receive it.
|
|
//
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// This is an InterimResponse from the server. Do a subset of
|
|
// the normal receive SMB logic.
|
|
//
|
|
|
|
if (Context->ReceiveIrp != NULL) {
|
|
Context->Header.ErrorType = ReceiveIrpProcessing;
|
|
|
|
Context->ReceiveMdl->ByteCount = *SmbLength;
|
|
|
|
RdrBuildReceive(Context->ReceiveIrp, Context->MpxEntry->SLE,
|
|
Trans2NetTranceiveComplete, Context, Context->ReceiveMdl,
|
|
RdrMdlLength(Context->ReceiveMdl));
|
|
|
|
|
|
RdrStartReceiveForMpxEntry (MpxEntry, Context->ReceiveIrp);
|
|
|
|
*Irp = Context->ReceiveIrp;
|
|
|
|
//
|
|
// See "This gets kinda wierd"
|
|
//
|
|
|
|
IoSetNextIrpStackLocation( Context->ReceiveIrp );
|
|
|
|
//
|
|
// Get TDI to process IRP and copy all the data.
|
|
//
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
} else {
|
|
|
|
//
|
|
// On an interim response (ok to send), we want to
|
|
// simply complete the request without transfering any
|
|
// data at all.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
try_return( Status );
|
|
|
|
}
|
|
|
|
//
|
|
// On any Response the server can set the number of bytes it is going
|
|
// to send for this transaction down to a lower value.
|
|
//
|
|
|
|
Context->ParametersExpected = MIN(Context->ParametersExpected, TotalParameterCount);
|
|
|
|
Context->DataExpected = MIN(Context->DataExpected, TotalDataCount);
|
|
|
|
// How many parameter bytes have been received so far in this transaction.
|
|
|
|
Context->Lparam += ParameterCount;
|
|
|
|
Context->Ldata += DataCount;
|
|
|
|
Context->Lsetup += SetupCount*sizeof(WORD);
|
|
|
|
//
|
|
// If there are setup words in this response SMB, copy them over the
|
|
// setup buffer.
|
|
//
|
|
|
|
if (SetupCount) {
|
|
|
|
if (Context->MaxSetupWords > (ULONG)SetupCount) {
|
|
RtlCopyMemory(Context->SetupWords, (PVOID)SetupPointer, SetupCount*sizeof(WORD));
|
|
}
|
|
|
|
Context->MaxSetupWords -= SetupCount;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Context->ReceiveIrp)) {
|
|
|
|
Context->Header.ErrorType = ReceiveIrpProcessing;
|
|
|
|
// Build the chained Mdl. The Mdl structures we are creating depend on
|
|
// ParameterCount and DataCount:
|
|
//
|
|
// 1) ReceiveMdl
|
|
// 2) ReceiveMdl->OutParameterPartialMdl[->PadMdl]
|
|
// 3) ReceiveMdl->OutParameterPartialMdl[->PadMdl]->OutDataPartialMdl
|
|
// 4) ReceiveMdl->OutDataPartialMdl
|
|
|
|
if ( ParameterCount ) {
|
|
|
|
//
|
|
// Case 2 or 3; Parameters or Parameters and Data.
|
|
// Fill in number of bytes before first parameter byte including any
|
|
// trailing pad bytes
|
|
//
|
|
|
|
Context->ReceiveMdl->ByteCount = ParameterOffset;
|
|
|
|
|
|
// Fill in number of parameter bytes including any trailing pad bytes
|
|
|
|
if ( DataCount ) {
|
|
|
|
//
|
|
// Case 3; Parameters and Data.
|
|
//
|
|
|
|
// Describe where in Parameters the bytes in this SMB should go
|
|
|
|
IoBuildPartialMdl(Context->ParameterMdl,
|
|
Context->OutParameterPartialMdl,
|
|
(PCHAR)Context->ParameterMdl->StartVa +
|
|
Context->ParameterMdl->ByteOffset +
|
|
ParameterDisplacement,
|
|
ParameterCount);
|
|
|
|
if ( DataOffset - ParameterOffset == ParameterCount) {
|
|
|
|
// No PAD bytes
|
|
|
|
// Tell system that after the Header & setup bytes comes
|
|
// parameters followed by data
|
|
|
|
Context->ReceiveMdl->Next = Context->OutParameterPartialMdl;
|
|
|
|
Context->OutParameterPartialMdl->Next = Context->OutDataPartialMdl;
|
|
|
|
} else {
|
|
|
|
// Use the PadMdl to discard extra bytes
|
|
|
|
Context->PadMdl->ByteCount = ((DataOffset - ParameterOffset ) - ParameterCount );
|
|
|
|
ASSERT ( Context->PadMdl->ByteCount <= PADMAX );
|
|
|
|
Context->ReceiveMdl->Next = Context->OutParameterPartialMdl;
|
|
|
|
Context->OutParameterPartialMdl->Next = Context->PadMdl;
|
|
|
|
Context->PadMdl->Next = Context->OutDataPartialMdl;
|
|
}
|
|
|
|
// Create the Mdl to point directly into the callers buffer
|
|
|
|
IoBuildPartialMdl(Context->OutDataMdl,
|
|
Context->OutDataPartialMdl,
|
|
(PCHAR)Context->OutDataMdl->StartVa +
|
|
Context->OutDataMdl->ByteOffset +
|
|
DataDisplacement,
|
|
DataCount);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Case 2; Parameters, no Data.
|
|
// DataCount is 0 so just use parametercount
|
|
//
|
|
|
|
// Describe where in Parameters the bytes in this SMB should go
|
|
|
|
//
|
|
// Read ParameterCount bytes into the specified area of ParameterMdl
|
|
//
|
|
|
|
IoBuildPartialMdl(Context->ParameterMdl,
|
|
Context->OutParameterPartialMdl,
|
|
(PCHAR)Context->ParameterMdl->StartVa +
|
|
Context->ParameterMdl->ByteOffset +
|
|
ParameterDisplacement,
|
|
ParameterCount);
|
|
|
|
if ((DataOffset - ParameterOffset - ParameterCount) > 0 ) {
|
|
|
|
// There are pad bytes at the end of the Parameters.
|
|
|
|
// Use the PadMdl to discard extra bytes
|
|
|
|
Context->PadMdl->ByteCount = (DataOffset - ParameterOffset - ParameterCount);
|
|
|
|
ASSERT ( Context->PadMdl->ByteCount <= PADMAX );
|
|
|
|
Context->OutParameterPartialMdl->Next = Context->PadMdl;
|
|
|
|
Context->PadMdl->Next = NULL;
|
|
}
|
|
|
|
// Ensure server returned parameters in the right range
|
|
|
|
ASSERT( (ParameterDisplacement + ParameterCount) <= Context->ParametersExpected);
|
|
|
|
Context->ReceiveMdl->Next = Context->OutParameterPartialMdl;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Case 1 or 4. No Parameters just Data or No Parameters No Data.
|
|
//
|
|
|
|
if ( DataCount ) {
|
|
|
|
//
|
|
// Case 4; No Parameters just Data.
|
|
//
|
|
|
|
// All bytes up to the data go into the receive smbbuffer
|
|
Context->ReceiveMdl->ByteCount = DataOffset;
|
|
|
|
// Chain data Mdl after the ReceiveParamMdl
|
|
|
|
Context->ReceiveMdl->Next = Context->OutDataPartialMdl;
|
|
|
|
// Create the Mdl to point directly into the callers buffer
|
|
|
|
IoBuildPartialMdl(Context->OutDataMdl,
|
|
Context->OutDataPartialMdl,
|
|
(PCHAR)Context->OutDataMdl->StartVa +
|
|
Context->OutDataMdl->ByteOffset +
|
|
DataDisplacement,
|
|
DataCount);
|
|
|
|
// Ensure server returned data in the right range
|
|
|
|
ASSERT( (DataDisplacement + DataCount) <= Context->DataExpected);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Case 1. No Parameters No Data.
|
|
// ParameterCount and DataCount are zero so put all the data
|
|
// into ReceiveMdl.
|
|
//
|
|
// In this case, there's no reason to actually receive
|
|
// the data at all - just return from the indication.
|
|
//
|
|
|
|
//
|
|
// Blast the receive IRP, it's not needed.
|
|
//
|
|
|
|
*Irp = NULL;
|
|
|
|
try_return( Status = STATUS_SUCCESS );
|
|
|
|
}
|
|
}
|
|
|
|
RdrBuildReceive(Context->ReceiveIrp, Context->MpxEntry->SLE,
|
|
Trans2NetTranceiveComplete, Context, Context->ReceiveMdl,
|
|
RdrMdlLength(Context->ReceiveMdl));
|
|
|
|
RdrStartReceiveForMpxEntry (MpxEntry, Context->ReceiveIrp);
|
|
|
|
*Irp = Context->ReceiveIrp;
|
|
|
|
//
|
|
// This gets kinda wierd.
|
|
//
|
|
// Since IoCompleteRequest zeroes out stack locations, ReceiveRequest
|
|
// must be restored each time. We could be clever and just save the
|
|
// stack locations that get cleared out but restoring the complete
|
|
// structure is more reliable.
|
|
//
|
|
// Since this IRP is going to be completed by the transport without
|
|
// ever going to IoCallDriver, we have to update the stack location
|
|
// to make the transports stack location the current stack location
|
|
// with IoSetNextIrpStackLocation.
|
|
//
|
|
// Please note that this means that any transport provider that uses
|
|
// IoCallDriver to re-submit it's requests at indication time will
|
|
// break badly because of this code....
|
|
//
|
|
// This operation is repeated for each call to the callback routine
|
|
// with this context and irp.
|
|
|
|
IoSetNextIrpStackLocation( Context->ReceiveIrp );
|
|
|
|
#if RDRDBG1
|
|
{
|
|
PIRP Irp = Context->ReceiveIrp;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
dprintf(DPRT_SMB, ("Irp %lx IrpSp %lx\n",
|
|
Irp, IrpSp));
|
|
ndump_core( (PCHAR) Irp, IoSizeOfIrp( Irp->StackCount ));
|
|
}
|
|
#endif
|
|
ASSERT( Context->ReceiveMdl->ByteCount <= SMB_BUFFER_SIZE);
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED; // Get TDI to process
|
|
// IRP and copy all the
|
|
// data.
|
|
}
|
|
|
|
try_return( Status );
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
dprintf(DPRT_SMB, ("Trans2NetTranceiveCallback return status: %X\n",
|
|
Status));
|
|
dprintf(DPRT_SMB, ("Ldata %lx DataExpected %lx\n",
|
|
Context->Ldata, Context->DataExpected));
|
|
dprintf(DPRT_SMB, ("Lparam %lx ParametersExpected %lx\n",
|
|
Context->Lparam, Context->ParametersExpected));
|
|
|
|
if ( Status != STATUS_MORE_PROCESSING_REQUIRED ) {
|
|
|
|
if (Context->Routine != NULL) {
|
|
//
|
|
// There is a completion routine specified, call it
|
|
// to let it do appropriate post processing.
|
|
//
|
|
|
|
Context->Routine(Status,
|
|
Context->CallbackContext,
|
|
Context,
|
|
NULL, 0, // Setup
|
|
NULL, 0, // Parameters
|
|
NULL, 0);// Data
|
|
}
|
|
|
|
//
|
|
// This code is executed when an error is received from the server.
|
|
// No Irp is being returned to the transport so the completion routine
|
|
// will not be called.
|
|
// Wakeup Trans2NetTranceive.
|
|
//
|
|
|
|
Context->Header.Type = CONTEXT_TRAN2_END;
|
|
|
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We're going to receive the data inside a receive that we
|
|
// will be handing to TDI - Fill in 0 bytes of data received.
|
|
//
|
|
|
|
*SmbLength = 0;
|
|
|
|
// More parameters/data to come
|
|
|
|
Context->Header.Type = CONTEXT_TRAN2_COMPLETE;
|
|
|
|
//
|
|
// This event had better not be in the signalled state
|
|
// now.
|
|
//
|
|
|
|
ASSERT (KeReadStateEvent(&Context->Header.KernelEvent) == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
Trans2NetTranceiveComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Ctx
|
|
)
|
|
/*++
|
|
|
|
Trans2NetTranceiveComplete - Final completion for user request.
|
|
|
|
Routine Description:
|
|
|
|
This routine is called on final completion of the TDI_Receive
|
|
request from the transport. If the request completed successfully,
|
|
this routine will complete the request with no error, if the receive
|
|
completed with an error, it will flag the error and complete the
|
|
request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device structure that the request completed on.
|
|
Irp - The Irp that completed.
|
|
Context - Context information for completion.
|
|
|
|
Return Value:
|
|
|
|
Return value to be returned from receive indication routine.
|
|
--*/
|
|
|
|
|
|
{
|
|
PTRANCEIVE2CONTEXT Context = (PTRANCEIVE2CONTEXT) Ctx;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|
|
|
ASSERT (Context->MpxEntry == Context->Header.MpxTableEntry);
|
|
|
|
dprintf(DPRT_SMB, ("Trans2NetTranceiveComplete Type: %lx\n",Context->Header.Type));
|
|
|
|
ASSERT ((Context->Header.Type == CONTEXT_TRAN2_END) ||
|
|
(Context->Header.Type == CONTEXT_TRAN2_COMPLETE));
|
|
|
|
RdrCompleteReceiveForMpxEntry (Context->MpxEntry, Context->ReceiveIrp);
|
|
|
|
if (Context->OutDataPartialMdl != NULL) {
|
|
MmPrepareMdlForReuse(Context->OutDataPartialMdl);
|
|
}
|
|
|
|
if (Context->OutParameterPartialMdl != NULL) {
|
|
MmPrepareMdlForReuse(Context->OutParameterPartialMdl);
|
|
}
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
//
|
|
// Setting ReceiveIrpProcessing will cause the checks in
|
|
// RdrNetTranceive to check the incoming SMB for errors.
|
|
//
|
|
|
|
ExInterlockedAddLargeStatistic(
|
|
&RdrStatistics.BytesReceived,
|
|
Irp->IoStatus.Information );
|
|
|
|
Context->Header.ErrorType = ReceiveIrpProcessing;
|
|
|
|
} else {
|
|
|
|
RdrStatistics.FailedCompletionOperations += 1;
|
|
|
|
Context->Header.ErrorType = NetError;
|
|
Context->Header.ErrorCode = RdrMapNetworkError(Irp->IoStatus.Status);
|
|
}
|
|
|
|
#if RDRDBG
|
|
IFDEBUG(SMBTRACE) {
|
|
DumpSMB(Context->ReceiveMdl);
|
|
}
|
|
#endif
|
|
|
|
SMBTRACE_RDR(Context->ReceiveMdl);
|
|
|
|
if ( ((Context->Ldata >= Context->DataExpected) &&
|
|
(Context->Lparam >= Context->ParametersExpected)) ||
|
|
|
|
((Context->Ldata == 0 ) &&
|
|
( Context->Lparam == 0 ))
|
|
) {
|
|
|
|
if (Context->Routine != NULL) {
|
|
|
|
//
|
|
// There is a completion routine specified, call it
|
|
// to let it do appropriate post processing.
|
|
//
|
|
|
|
Context->Routine(STATUS_SUCCESS,
|
|
Context->CallbackContext,
|
|
Context,
|
|
Context->SetupWords, Context->Lsetup, // Setup
|
|
(Context->ParameterMdl != NULL ? MmGetSystemAddressForMdl(Context->ParameterMdl) : NULL), Context->Lparam, // Parameters
|
|
(Context->OutDataMdl != NULL ? MmGetSystemAddressForMdl(Context->OutDataMdl) : NULL), Context->Ldata // Data
|
|
);
|
|
}
|
|
|
|
//
|
|
// This transaction is now complete because we have received
|
|
// all the data expected, this is an interim response saying
|
|
// send more.
|
|
// Tell the CallBack routine to inform the caller.
|
|
|
|
Context->Header.Type = CONTEXT_TRAN2_END;
|
|
|
|
// Indication routine says all thats going to arrive has arrived.
|
|
// Wakeup Trans2NetTranceive.
|
|
|
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|
|
|
} else {
|
|
|
|
// Tell indication routine its ok to use the context.
|
|
|
|
Context->Header.Type = CONTEXT_TRAN2_CALLBACK;
|
|
|
|
RdrSetCallbackTranceive(Context->MpxEntry, Context->TransactionStartTime, Trans2NetTranceiveCallback);
|
|
|
|
}
|
|
|
|
//
|
|
// Short circuit I/O completion on this request now.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NetTransmit(
|
|
IN ULONG Flags,
|
|
IN PIRP Irp OPTIONAL,
|
|
IN PCONNECTLISTENTRY CLE,
|
|
IN PMDL SendMdl,
|
|
IN PSECURITY_ENTRY Se OPTIONAL,
|
|
IN OUT PMPX_ENTRY *pMTE OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
dprintf(DPRT_SMB, ("NetTransmit\n"));
|
|
|
|
if (ARGUMENT_PRESENT(Irp)) {
|
|
|
|
//
|
|
// If the IRP has been cancelled, or if the thread is terminating,
|
|
// bail out. (Actually, if the thread is terminating, the Cancel
|
|
// bit should be set, but we'll check both for completeness.)
|
|
//
|
|
|
|
if ( Irp->Cancel || PsIsThreadTerminating(Irp->Tail.Overlay.Thread) ) {
|
|
return STATUS_CANCELLED;
|
|
}
|
|
|
|
RdrMarkIrpAsNonCanceled( Irp );
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status = RdrSendSMB (
|
|
Flags | NT_NOSENDRESPONSE,
|
|
CLE,
|
|
Se,
|
|
*pMTE,
|
|
SendMdl) )) {
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Guarantee that the MPX table entries context is a context header.
|
|
//
|
|
|
|
ASSERT((*pMTE)->Signature == STRUCTURE_SIGNATURE_MPX_ENTRY);
|
|
|
|
ASSERT((*pMTE)->RequestContext->Type>=STRUCTURE_SIGNATURE_CONTEXT_BASE);
|
|
|
|
// Wait until packet sent before proceeding.
|
|
|
|
Status = KeWaitForSingleObject( &(*pMTE)->SendCompleteEvent,
|
|
Executive, KernelMode, // Wait reason, WaitMode
|
|
FALSE, NULL); // Alertable, Timeout
|
|
|
|
return Status;
|
|
|
|
}
|