1272 lines
39 KiB
C
1272 lines
39 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
smbadmin.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the SMB's that need to be exchanged to facilitate
|
||
bookkeeping at the server
|
||
|
||
Author:
|
||
|
||
Balan Sethu Raman [SethuR] 7-March-1995
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, SmbCeNegotiate)
|
||
#pragma alloc_text(PAGE, SmbCeDisconnect)
|
||
#pragma alloc_text(PAGE, SmbCeLogOff)
|
||
#pragma alloc_text(PAGE, SmbCeInitializeAdminExchange)
|
||
#pragma alloc_text(PAGE, SmbCeDiscardAdminExchange)
|
||
#pragma alloc_text(PAGE, SmbCeCompleteAdminExchange)
|
||
#pragma alloc_text(PAGE, SmbAdminExchangeStart)
|
||
#endif
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (RDBSS_BUG_CHECK_SMB_NETROOT)
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_DISPATCH)
|
||
|
||
extern
|
||
SMB_EXCHANGE_DISPATCH_VECTOR EchoExchangeDispatch;
|
||
|
||
extern NTSTATUS
|
||
SmbCeInitializeAdminExchange(
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange,
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry,
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry,
|
||
UCHAR SmbCommand);
|
||
|
||
extern VOID
|
||
SmbCeDiscardAdminExchange(
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange);
|
||
|
||
extern NTSTATUS
|
||
SmbCeCompleteAdminExchange(
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange);
|
||
|
||
VOID
|
||
SmbReferenceRecord(
|
||
PREFERENCE_RECORD pReferenceRecord,
|
||
PVOID FileName,
|
||
ULONG FileLine)
|
||
{
|
||
int i;
|
||
|
||
for (i=REFERENCE_RECORD_SIZE-1;i>0;i--) {
|
||
pReferenceRecord[i].FileName = pReferenceRecord[i-1].FileName;
|
||
pReferenceRecord[i].FileLine = pReferenceRecord[i-1].FileLine;
|
||
}
|
||
|
||
pReferenceRecord[0].FileName = FileName;
|
||
pReferenceRecord[0].FileLine = FileLine;
|
||
}
|
||
|
||
PSMB_EXCHANGE
|
||
SmbSetServerEntryNegotiateExchange(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange)
|
||
{
|
||
PSMB_EXCHANGE pStoredExchange;
|
||
|
||
SmbCeIncrementPendingLocalOperations((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
pStoredExchange = InterlockedCompareExchangePointer(
|
||
&pServerEntry->pNegotiateExchange,
|
||
pSmbAdminExchange,
|
||
NULL);
|
||
|
||
if (pStoredExchange != NULL) {
|
||
SmbCeDecrementPendingLocalOperationsAndFinalize((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
}
|
||
|
||
return pStoredExchange;
|
||
}
|
||
|
||
PSMB_EXCHANGE
|
||
SmbResetServerEntryNegotiateExchange(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
||
{
|
||
PSMB_EXCHANGE pStoredExchange;
|
||
|
||
pStoredExchange = (PSMB_EXCHANGE)InterlockedCompareExchangePointer(
|
||
&pServerEntry->pNegotiateExchange,
|
||
NULL,
|
||
pServerEntry->pNegotiateExchange);
|
||
|
||
return pStoredExchange;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeNegotiate(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PMRX_SRV_CALL pSrvCall,
|
||
BOOLEAN RemoteBootSession)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine issues the negotiate SMB to the server
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry
|
||
|
||
pSrvCall - the associated srv call instance in the wrapper
|
||
|
||
RemoteBootSession - is this a negotiate for a remote boot session
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server call construction has been finalized.
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
Since the negotiate SMB can be directed at either a unknown server or a server
|
||
whose capabilitiese are known it is upto the caller to decide to wait for the
|
||
response.
|
||
|
||
On some transports a reconnect is possible without having to tear down an existing
|
||
connection, i.e. attempting to send a packet reestablishes the connection at the
|
||
lower level. Since this is not supported by all the transports ( with the exception
|
||
of TCP/IP) the reference server entry initiates this process by tearing down the
|
||
existing transport and reinitializsing it.
|
||
|
||
As part of the negotiate response the domain name to which the server belongs is
|
||
sent back. Since the negotiate response is processed at DPC level, a preparatory
|
||
allocation needs to be made ( This will ensure minimal work at DPC level).
|
||
|
||
In this routine this is accomplished by allocating a buffer from nonpaged
|
||
pool of MAX_PATH and associating it with the DomainName fild in the server entry
|
||
prior to the TRanceive. On resumption from Tranceive this buffer is deallocated and
|
||
a buffer from paged pool corresponding to the exact length is allocated to hold the
|
||
domain name.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange;
|
||
|
||
ULONG NegotiateSmbLength;
|
||
PVOID pNegotiateSmb;
|
||
|
||
PAGED_CODE();
|
||
|
||
IF_NOT_MRXSMB_BUILD_FOR_DISCONNECTED_CSC {
|
||
NOTHING;
|
||
} else {
|
||
#if 0
|
||
if (SmbCeIsServerInDisconnectedMode(pServerEntry)) {
|
||
return MRxSmbCscNegotiateDisconnected(pServerEntry);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
pSmbAdminExchange = (PSMB_ADMIN_EXCHANGE)SmbMmAllocateExchange(ADMIN_EXCHANGE,NULL);
|
||
if (pSmbAdminExchange != NULL) {
|
||
Status = SmbCeInitializeAdminExchange(
|
||
pSmbAdminExchange,
|
||
pServerEntry,
|
||
NULL,
|
||
NULL,
|
||
SMB_COM_NEGOTIATE);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
// Build the negotiate SMB and allocate the temporary buffer for
|
||
// the DOMAIN name.
|
||
|
||
Status = BuildNegotiateSmb(
|
||
&pNegotiateSmb,
|
||
&NegotiateSmbLength,
|
||
RemoteBootSession);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
pSmbAdminExchange->pSmbBuffer = pNegotiateSmb;
|
||
pSmbAdminExchange->SmbBufferLength = NegotiateSmbLength;
|
||
|
||
// Preparatory allocation for the domain name buffer
|
||
pSmbAdminExchange->Negotiate.pSrvCall = pSrvCall;
|
||
pSmbAdminExchange->Negotiate.DomainName.Length = 0;
|
||
pSmbAdminExchange->Negotiate.DomainName.MaximumLength = MAX_PATH;
|
||
pSmbAdminExchange->Negotiate.DomainName.Buffer
|
||
= (PWCHAR)RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
MAX_PATH,
|
||
MRXSMB_ADMIN_POOLTAG);
|
||
|
||
if (pSmbAdminExchange->Negotiate.DomainName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
BOOLEAN fExchangeDiscarded = FALSE;
|
||
SMBCE_RESUMPTION_CONTEXT ResumptionContext;
|
||
PSMB_EXCHANGE pStoredExchange;
|
||
|
||
SmbCeInitializeResumptionContext(&ResumptionContext);
|
||
pSmbAdminExchange->pResumptionContext = &ResumptionContext;
|
||
|
||
// Since the Negotiate SMB is the first SMB that is sent on a
|
||
// connection the MID mapping data structures have not been setup.
|
||
// Therefore a certain amount of additional initialization is
|
||
// required to ensure that the Negotiate SMB can be handled correctly.
|
||
// This involves presetting the MID field in the header and the
|
||
// SMBCE_EXCHANGE_MID_VALID field in the exchange.
|
||
//
|
||
// A beneficial side effect of implementing it this way is the reduced
|
||
// path length for the regular Send/Receives on a connection.
|
||
|
||
pSmbAdminExchange->SmbCeFlags = (SMBCE_EXCHANGE_REUSE_MID |
|
||
SMBCE_EXCHANGE_RETAIN_MID |
|
||
SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION |
|
||
SMBCE_EXCHANGE_MID_VALID);
|
||
|
||
// Prevent the admin exchange from being finalized before returning back to this routine.
|
||
SmbCeIncrementPendingLocalOperations((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
pStoredExchange = SmbSetServerEntryNegotiateExchange(
|
||
pServerEntry,
|
||
pSmbAdminExchange);
|
||
|
||
if ((pServerEntry->Header.State == SMBCEDB_CONSTRUCTION_IN_PROGRESS) &&
|
||
(pStoredExchange == NULL)) {
|
||
|
||
// The Negotiate SMB exchange has been built successfully. Initiate it.
|
||
Status = SmbCeInitiateExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
if ((pSmbAdminExchange->SmbStatus != STATUS_SUCCESS) ||
|
||
(Status != STATUS_PENDING && Status != STATUS_SUCCESS)) {
|
||
pStoredExchange = (PSMB_EXCHANGE)InterlockedCompareExchangePointer(
|
||
&pServerEntry->pNegotiateExchange,
|
||
NULL,
|
||
pSmbAdminExchange);
|
||
|
||
if (pStoredExchange == (PSMB_EXCHANGE)pSmbAdminExchange) {
|
||
SmbCeDecrementPendingLocalOperations((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
}
|
||
|
||
if (pSmbAdminExchange->SmbStatus == STATUS_SUCCESS) {
|
||
pSmbAdminExchange->SmbStatus = Status;
|
||
}
|
||
|
||
pSmbAdminExchange->Status = pSmbAdminExchange->SmbStatus;
|
||
}
|
||
|
||
// Admin exchange is ready to be finalized
|
||
SmbCeDecrementPendingLocalOperationsAndFinalize((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
// Wait for the finalization.
|
||
SmbCeSuspend(&ResumptionContext);
|
||
Status = SmbCeCompleteAdminExchange(pSmbAdminExchange);
|
||
} else {
|
||
InterlockedCompareExchangePointer(
|
||
&pServerEntry->pNegotiateExchange,
|
||
NULL,
|
||
pSmbAdminExchange);
|
||
|
||
SmbCeDiscardAdminExchange(pSmbAdminExchange);
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
}
|
||
} else {
|
||
SmbMmFreeExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
}
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status == STATUS_LOGIN_WKSTA_RESTRICTION) {
|
||
DbgPrint("MRXSMB: Cannot talk to %wZ which doesn't support Security Signature.\n",&pServerEntry->Name);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeSendEchoProbe(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PMRXSMB_ECHO_PROBE_SERVICE_CONTEXT pEchoProbeContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends an echo probe to the specified server
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry
|
||
|
||
pEchoProbeCOntext - the echo probe context
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the disconnect SMB was sent successfully
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange;
|
||
|
||
pSmbAdminExchange = (PSMB_ADMIN_EXCHANGE)SmbMmAllocateExchange(ADMIN_EXCHANGE,NULL);
|
||
if (pSmbAdminExchange != NULL) {
|
||
Status = SmbCeInitializeAdminExchange(
|
||
pSmbAdminExchange,
|
||
pServerEntry,
|
||
NULL,
|
||
NULL,
|
||
SMB_COM_ECHO);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
ULONG EchoMdlSize;
|
||
ULONG requestSize;
|
||
|
||
pSmbAdminExchange->Mid = SMBCE_ECHO_PROBE_MID;
|
||
pSmbAdminExchange->SmbCeFlags = (SMBCE_EXCHANGE_REUSE_MID |
|
||
SMBCE_EXCHANGE_RETAIN_MID |
|
||
SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION |
|
||
SMBCE_EXCHANGE_MID_VALID);
|
||
|
||
requestSize = pEchoProbeContext->EchoSmbLength + TRANSPORT_HEADER_SIZE;
|
||
|
||
EchoMdlSize = (ULONG)MmSizeOfMdl(
|
||
pEchoProbeContext->pEchoSmb,
|
||
requestSize);
|
||
pSmbAdminExchange->EchoProbe.pEchoProbeMdl =
|
||
RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
(EchoMdlSize + requestSize ),
|
||
MRXSMB_ADMIN_POOLTAG);
|
||
|
||
if (pSmbAdminExchange->EchoProbe.pEchoProbeMdl != NULL) {
|
||
PBYTE pEchoProbeBuffer;
|
||
|
||
pEchoProbeBuffer = (PBYTE)pSmbAdminExchange->EchoProbe.pEchoProbeMdl +
|
||
EchoMdlSize + TRANSPORT_HEADER_SIZE;
|
||
|
||
pSmbAdminExchange->EchoProbe.EchoProbeLength = pEchoProbeContext->EchoSmbLength;
|
||
|
||
RtlCopyMemory(
|
||
pEchoProbeBuffer,
|
||
pEchoProbeContext->pEchoSmb,
|
||
pEchoProbeContext->EchoSmbLength);
|
||
|
||
RxInitializeHeaderMdl(
|
||
pSmbAdminExchange->EchoProbe.pEchoProbeMdl,
|
||
pEchoProbeBuffer,
|
||
pEchoProbeContext->EchoSmbLength);
|
||
|
||
MmBuildMdlForNonPagedPool(
|
||
pSmbAdminExchange->EchoProbe.pEchoProbeMdl);
|
||
|
||
InterlockedIncrement(&pServerEntry->Server.NumberOfEchoProbesSent);
|
||
|
||
// The ECHO probe SMB exchange has been built successfully. Initiate it.
|
||
Status = SmbCeInitiateExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status != STATUS_PENDING) {
|
||
Status = SmbCeCompleteAdminExchange(pSmbAdminExchange);
|
||
}
|
||
} else {
|
||
SmbMmFreeExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
}
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeDisconnect(
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine issues the disconnect SMB for an existing connection to the server
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry
|
||
|
||
pVNetRootContext - the VNetRootContext
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the disconnect SMB was sent successfully
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange;
|
||
PSMB_HEADER pSmbHeader;
|
||
PREQ_TREE_DISCONNECT pReqTreeDisconnect;
|
||
|
||
PAGED_CODE();
|
||
|
||
// On mailslot servers no disconnects are required.
|
||
if (SmbCeGetServerType(pVNetRootContext->pServerEntry) == SMBCEDB_MAILSLOT_SERVER) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
pSmbAdminExchange = (PSMB_ADMIN_EXCHANGE)SmbMmAllocateExchange(ADMIN_EXCHANGE,NULL);
|
||
if (pSmbAdminExchange != NULL) {
|
||
UCHAR LastCommandInHeader;
|
||
PUCHAR pCommand;
|
||
|
||
Status = SmbCeInitializeAdminExchange(
|
||
pSmbAdminExchange,
|
||
pVNetRootContext->pServerEntry,
|
||
pVNetRootContext->pSessionEntry,
|
||
pVNetRootContext->pNetRootEntry,
|
||
SMB_COM_TREE_DISCONNECT);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
BOOLEAN fExchangeDiscarded = FALSE;
|
||
|
||
pSmbAdminExchange->pSmbBuffer =
|
||
(PCHAR) pSmbAdminExchange->Disconnect.DisconnectSmb + TRANSPORT_HEADER_SIZE;
|
||
|
||
pSmbHeader = (PSMB_HEADER)pSmbAdminExchange->pSmbBuffer;
|
||
pReqTreeDisconnect = (PREQ_TREE_DISCONNECT)(pSmbHeader + 1);
|
||
|
||
// Build the header
|
||
Status = SmbCeBuildSmbHeader(
|
||
(PSMB_EXCHANGE)pSmbAdminExchange,
|
||
pSmbAdminExchange->pSmbBuffer,
|
||
sizeof(SMB_HEADER),
|
||
&pSmbAdminExchange->SmbBufferLength,
|
||
&LastCommandInHeader,
|
||
&pCommand);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
ASSERT(LastCommandInHeader == SMB_COM_NO_ANDX_COMMAND);
|
||
*pCommand = SMB_COM_TREE_DISCONNECT;
|
||
|
||
pSmbHeader->Tid = pVNetRootContext->TreeId;
|
||
pReqTreeDisconnect->WordCount = 0;
|
||
SmbPutUshort(&pReqTreeDisconnect->ByteCount,0);
|
||
|
||
pSmbAdminExchange->SmbBufferLength += FIELD_OFFSET(REQ_TREE_DISCONNECT,Buffer);
|
||
|
||
Status = SmbCeInitiateExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
if ((Status == STATUS_PENDING) ||
|
||
(Status == STATUS_SUCCESS)) {
|
||
// async completion will also discard the exchange
|
||
fExchangeDiscarded = TRUE;
|
||
}
|
||
}
|
||
|
||
if (!fExchangeDiscarded) {
|
||
SmbCeDiscardAdminExchange(pSmbAdminExchange);
|
||
}
|
||
} else {
|
||
SmbMmFreeExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
}
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
extern BOOLEAN MRxSmbEnableDownLevelLogOff;
|
||
|
||
NTSTATUS
|
||
SmbCeLogOff(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine issues the logoff SMB for an existing session to the server
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry
|
||
|
||
pSessionEntry - the associated session entry
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the logoff was successfully sent.
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange;
|
||
PSMB_HEADER pSmbHeader;
|
||
PREQ_LOGOFF_ANDX pReqLogOffAndX;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((SmbCeGetServerType(pServerEntry) == SMBCEDB_MAILSLOT_SERVER) ||
|
||
(pServerEntry->Server.SecurityMode == SECURITY_MODE_SHARE_LEVEL)) {
|
||
if (pSessionEntry != NULL) {
|
||
SmbCeDereferenceSessionEntry(pSessionEntry);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Some servers (like linux) don't really know how to handle session logoffs.
|
||
// So, let's just be sure that we only do this to NT or better servers,
|
||
// because we know that they handle it correctly. The version of Linux we have
|
||
// seems to like to negotiate the NT dialect even though it really isn't NT. That's
|
||
// why the extra check is put in here for NT status codes.
|
||
//
|
||
// Bypass this behavior based on the 'EnableDownLevelLogOff' registry key.
|
||
//
|
||
|
||
if( MRxSmbEnableDownLevelLogOff == FALSE ) {
|
||
|
||
if( pServerEntry->Server.Dialect < NTLANMAN_DIALECT ||
|
||
!FlagOn(pServerEntry->Server.DialectFlags,DF_NT_STATUS) ) {
|
||
if (pSessionEntry != NULL) {
|
||
SmbCeDereferenceSessionEntry(pSessionEntry);
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
pSmbAdminExchange = (PSMB_ADMIN_EXCHANGE)SmbMmAllocateExchange(ADMIN_EXCHANGE,NULL);
|
||
if (pSmbAdminExchange != NULL) {
|
||
UCHAR LastCommandInHeader;
|
||
PUCHAR pCommand;
|
||
|
||
Status = SmbCeInitializeAdminExchange(
|
||
pSmbAdminExchange,
|
||
pServerEntry,
|
||
pSessionEntry,
|
||
NULL,
|
||
SMB_COM_LOGOFF_ANDX);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
BOOLEAN fExchangeDiscarded = FALSE;
|
||
|
||
pSmbAdminExchange->pSmbBuffer =
|
||
(PCHAR) pSmbAdminExchange->LogOff.LogOffSmb + TRANSPORT_HEADER_SIZE;
|
||
|
||
pSmbHeader = (PSMB_HEADER)pSmbAdminExchange->pSmbBuffer;
|
||
pReqLogOffAndX = (PREQ_LOGOFF_ANDX)(pSmbHeader + 1);
|
||
|
||
// Build the header
|
||
Status = SmbCeBuildSmbHeader(
|
||
(PSMB_EXCHANGE)pSmbAdminExchange,
|
||
pSmbAdminExchange->pSmbBuffer,
|
||
sizeof(SMB_HEADER),
|
||
&pSmbAdminExchange->SmbBufferLength,
|
||
&LastCommandInHeader,
|
||
&pCommand);
|
||
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
ASSERT(LastCommandInHeader == SMB_COM_NO_ANDX_COMMAND);
|
||
*pCommand = SMB_COM_LOGOFF_ANDX;
|
||
|
||
pReqLogOffAndX->WordCount = 2;
|
||
pReqLogOffAndX->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
|
||
pReqLogOffAndX->AndXReserved = 0;
|
||
|
||
SmbPutUshort(&pReqLogOffAndX->AndXOffset,0);
|
||
SmbPutUshort(&pReqLogOffAndX->ByteCount,0);
|
||
|
||
pSmbAdminExchange->SmbBufferLength += FIELD_OFFSET(REQ_LOGOFF_ANDX,Buffer);
|
||
|
||
Status = SmbCeInitiateExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
if ((Status == STATUS_PENDING) ||
|
||
(Status == STATUS_SUCCESS)) {
|
||
|
||
// async completion will discard the exchange
|
||
fExchangeDiscarded = TRUE;
|
||
}
|
||
}
|
||
|
||
if (!fExchangeDiscarded) {
|
||
SmbCeDiscardAdminExchange(pSmbAdminExchange);
|
||
}
|
||
} else {
|
||
SmbMmFreeExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
}
|
||
} else {
|
||
Status = RX_MAP_STATUS(INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeInitializeAdminExchange(
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange,
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry,
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry,
|
||
UCHAR SmbCommand)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the ADMIN exchange
|
||
|
||
Arguments:
|
||
|
||
pSmbAdminExchange - the exchange
|
||
|
||
pServerEntry - the associated server entry
|
||
|
||
pSessionEntry - the associated session entry
|
||
|
||
pNetRootEntry - the associated net root entry
|
||
|
||
SmbCommand - the SMB command
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the logoff was successfully sent.
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
The ADMIN_EXCHANGE is a special type of exchange used for bootstrap/teardown
|
||
situations in which the initialization of the exchange cannot follow the noraml
|
||
course of events. In some cases not all the components required for proper
|
||
initialization of the exchange are present, e.g., NEGOTIATE we do not have a
|
||
valid session/tree connect. It is for this reason that the three important
|
||
elements of initialization, i.e., Server/Session/NetRoot have to be explicitly
|
||
specified. NULL is used to signify a dont care situation for a particular component.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = SmbCeIncrementActiveExchangeCount();
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
||
|
||
pSmbAdminExchange->CancellationStatus = SMBCE_EXCHANGE_NOT_CANCELLED;
|
||
|
||
if ((SmbCommand == SMB_COM_NEGOTIATE) ||
|
||
(SmbCommand == SMB_COM_ECHO)) {
|
||
pSmbAdminExchange->SmbCeContext.pServerEntry = pServerEntry;
|
||
pSmbAdminExchange->SmbCeContext.pVNetRootContext = NULL;
|
||
} else {
|
||
pVNetRootContext = (PSMBCE_V_NET_ROOT_CONTEXT)
|
||
RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(SMBCE_V_NET_ROOT_CONTEXT),
|
||
MRXSMB_VNETROOT_POOLTAG);
|
||
|
||
if (pVNetRootContext != NULL) {
|
||
pVNetRootContext->pServerEntry = pServerEntry;
|
||
pVNetRootContext->pSessionEntry = pSessionEntry;
|
||
pVNetRootContext->pNetRootEntry = pNetRootEntry;
|
||
|
||
pSmbAdminExchange->SmbCeContext.pVNetRootContext = pVNetRootContext;
|
||
pSmbAdminExchange->SmbCeContext.pServerEntry = pServerEntry;
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
SmbCeReferenceServerEntry(pServerEntry);
|
||
|
||
pSmbAdminExchange->pSmbMdl = NULL;
|
||
pSmbAdminExchange->pSmbBuffer = NULL;
|
||
pSmbAdminExchange->SmbBufferLength = 0;
|
||
|
||
// Set the SmbCe state to overrule the common method of having to hunt
|
||
// up a valid TID/FID etc. and reconnects.
|
||
pSmbAdminExchange->SmbCommand = SmbCommand;
|
||
pSmbAdminExchange->SmbCeState = SMBCE_EXCHANGE_NETROOT_INITIALIZED;
|
||
|
||
switch (pSmbAdminExchange->SmbCommand) {
|
||
case SMB_COM_NEGOTIATE:
|
||
{
|
||
pSmbAdminExchange->Negotiate.DomainName.Length = 0;
|
||
pSmbAdminExchange->Negotiate.DomainName.MaximumLength = 0;
|
||
pSmbAdminExchange->Negotiate.DomainName.Buffer = NULL;
|
||
pSmbAdminExchange->Negotiate.pSecurityBlobMdl = NULL;
|
||
}
|
||
break;
|
||
|
||
case SMB_COM_TREE_DISCONNECT:
|
||
case SMB_COM_LOGOFF_ANDX:
|
||
break;
|
||
|
||
case SMB_COM_ECHO:
|
||
{
|
||
pSmbAdminExchange->pDispatchVector = &EchoExchangeDispatch;
|
||
pSmbAdminExchange->EchoProbe.pEchoProbeMdl = NULL;
|
||
pSmbAdminExchange->EchoProbe.EchoProbeLength = 0;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
ASSERT(!"Valid Command for Admin Exchange");
|
||
break;
|
||
}
|
||
|
||
SmbCeAcquireResource();
|
||
if ((pSessionEntry != NULL) &&
|
||
pServerEntry->SecuritySignaturesEnabled &&
|
||
!pServerEntry->SecuritySignaturesActive) {
|
||
// if security signature is enabled and not yet turned on, exchange should wait for
|
||
// outstanding extended session setup to finish before resume in order to avoid index mismatch.
|
||
Status = SmbCeSyncExchangeForSecuritySignature((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
ASSERT(Status != STATUS_PENDING);
|
||
}
|
||
SmbCeReleaseResource();
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
RxFreePool(pSmbAdminExchange->SmbCeContext.pVNetRootContext);
|
||
pSmbAdminExchange->SmbCeContext.pVNetRootContext = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
SmbCeDiscardAdminExchange(
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine discards the ADMIN exchange
|
||
|
||
Arguments:
|
||
|
||
pSmbAdminExchange - the exchange
|
||
|
||
--*/
|
||
{
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
SmbCeAcquireResource();
|
||
|
||
RemoveEntryList(&pSmbAdminExchange->ExchangeList);
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pSmbAdminExchange);
|
||
pSessionEntry = SmbCeGetExchangeSessionEntry(pSmbAdminExchange);
|
||
pNetRootEntry = SmbCeGetExchangeNetRootEntry(pSmbAdminExchange);
|
||
|
||
pVNetRootContext = SmbCeGetExchangeVNetRootContext(pSmbAdminExchange);
|
||
|
||
if (pSmbAdminExchange->pSmbMdl != NULL) {
|
||
RxUnlockHeaderPages(pSmbAdminExchange->pSmbMdl);
|
||
IoFreeMdl(pSmbAdminExchange->pSmbMdl);
|
||
}
|
||
|
||
switch (pSmbAdminExchange->SmbCommand) {
|
||
case SMB_COM_NEGOTIATE:
|
||
{
|
||
pSmbAdminExchange->pSmbBuffer = NULL;
|
||
|
||
if (pSmbAdminExchange->Negotiate.DomainName.Buffer != NULL) {
|
||
RxFreePool(
|
||
pSmbAdminExchange->Negotiate.DomainName.Buffer);
|
||
}
|
||
|
||
if (pSmbAdminExchange->Negotiate.pSecurityBlobMdl != NULL) {
|
||
IoFreeMdl(
|
||
pSmbAdminExchange->Negotiate.pSecurityBlobMdl);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SMB_COM_TREE_DISCONNECT:
|
||
break;
|
||
|
||
case SMB_COM_LOGOFF_ANDX:
|
||
SmbCeDereferenceSessionEntry(pSessionEntry);
|
||
break;
|
||
|
||
case SMB_COM_ECHO:
|
||
{
|
||
if (pSmbAdminExchange->EchoProbe.pEchoProbeMdl != NULL) {
|
||
MmPrepareMdlForReuse(pSmbAdminExchange->EchoProbe.pEchoProbeMdl);
|
||
RxFreePool(pSmbAdminExchange->EchoProbe.pEchoProbeMdl);
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
ASSERT(!"Valid Command For Admin Exchange");
|
||
break;
|
||
}
|
||
|
||
// Tear down all the copy data requests associated with this exchange
|
||
SmbCePurgeBuffersAssociatedWithExchange(pServerEntry,(PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
SmbCeUninitializeExchangeTransport((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
SmbCeDereferenceServerEntry(pServerEntry);
|
||
|
||
if (pVNetRootContext != NULL) {
|
||
RxFreePool(pVNetRootContext);
|
||
}
|
||
|
||
SmbCeFreeBufferForServerResponse((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
|
||
SmbMmFreeExchange((PSMB_EXCHANGE)pSmbAdminExchange);
|
||
SmbCeDecrementActiveExchangeCount();
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeCompleteAdminExchange(
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the routine used for completing the SMB ADMIN exchanges.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
This routine encapsulates the TAIL for all SMB admin exchanges. They carry
|
||
out the local action required based upon the outcome of the exchange.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
SMBCEDB_OBJECT_STATE ServerState;
|
||
|
||
PAGED_CODE();
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pSmbAdminExchange);
|
||
|
||
switch (pSmbAdminExchange->SmbCommand) {
|
||
case SMB_COM_NEGOTIATE:
|
||
{
|
||
if (pSmbAdminExchange->Status != STATUS_SUCCESS) {
|
||
pServerEntry->ServerStatus = pSmbAdminExchange->Status;
|
||
}
|
||
|
||
if (pServerEntry->ServerStatus == STATUS_SUCCESS) {
|
||
|
||
if (!FlagOn(pServerEntry->Server.DialectFlags,DF_EXTENDED_SECURITY)) {
|
||
if (pServerEntry->DomainName.Buffer) {
|
||
RxFreePool(pServerEntry->DomainName.Buffer);
|
||
pServerEntry->DomainName.Buffer = NULL;
|
||
}
|
||
|
||
pServerEntry->DomainName.Length = pSmbAdminExchange->Negotiate.DomainName.Length;
|
||
pServerEntry->DomainName.MaximumLength = pServerEntry->DomainName.Length;
|
||
|
||
if (pServerEntry->DomainName.Length > 0) {
|
||
pServerEntry->DomainName.Buffer = RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
pServerEntry->DomainName.Length,
|
||
MRXSMB_SERVER_POOLTAG);
|
||
}
|
||
|
||
if (pServerEntry->DomainName.Buffer != NULL) {
|
||
// Copy the domain name into the server entry
|
||
RtlCopyMemory(
|
||
pServerEntry->DomainName.Buffer,
|
||
pSmbAdminExchange->Negotiate.DomainName.Buffer,
|
||
pServerEntry->DomainName.Length);
|
||
} else {
|
||
//The downlevel server doesn't have a domain name. It's not a problem if the
|
||
//DomainName.Buffer equals to NULL.
|
||
if (pServerEntry->DomainName.Length > 0) {
|
||
pServerEntry->DomainName.Length = 0;
|
||
pServerEntry->DomainName.MaximumLength = 0;
|
||
pServerEntry->ServerStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (pServerEntry->ServerStatus == STATUS_SUCCESS) {
|
||
pServerEntry->ServerStatus = SmbCeUpdateSrvCall(pServerEntry);
|
||
}
|
||
}
|
||
|
||
Status = pServerEntry->ServerStatus;
|
||
|
||
if (pServerEntry->ServerStatus == STATUS_SUCCESS) {
|
||
pServerEntry->Server.EchoProbeState = ECHO_PROBE_IDLE;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SMB_COM_TREE_DISCONNECT:
|
||
case SMB_COM_LOGOFF_ANDX:
|
||
case SMB_COM_ECHO:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
SmbCeDiscardAdminExchange(pSmbAdminExchange);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbAdminExchangeStart(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the start routine for administrative SMB exchanges.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange;
|
||
|
||
PAGED_CODE();
|
||
|
||
pSmbAdminExchange = (PSMB_ADMIN_EXCHANGE)pExchange;
|
||
|
||
switch (pSmbAdminExchange->SmbCommand) {
|
||
case SMB_COM_NEGOTIATE:
|
||
case SMB_COM_LOGOFF_ANDX:
|
||
case SMB_COM_TREE_DISCONNECT:
|
||
{
|
||
ASSERT(pSmbAdminExchange->pSmbMdl == NULL);
|
||
RxAllocateHeaderMdl(
|
||
pSmbAdminExchange->pSmbBuffer,
|
||
pSmbAdminExchange->SmbBufferLength,
|
||
pSmbAdminExchange->pSmbMdl
|
||
);
|
||
|
||
if (pSmbAdminExchange->pSmbMdl != NULL) {
|
||
|
||
RxProbeAndLockHeaderPages(
|
||
pSmbAdminExchange->pSmbMdl,
|
||
KernelMode,
|
||
IoModifyAccess,
|
||
Status);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = SmbCeTranceive(
|
||
pExchange,
|
||
RXCE_SEND_SYNCHRONOUS,
|
||
pSmbAdminExchange->pSmbMdl,
|
||
pSmbAdminExchange->SmbBufferLength);
|
||
|
||
RxDbgTrace( 0, Dbg, ("Net Root SmbCeTranceive returned %lx\n",Status));
|
||
} else {
|
||
IoFreeMdl(pSmbAdminExchange->pSmbMdl);
|
||
pSmbAdminExchange->pSmbMdl = NULL;
|
||
}
|
||
} else {
|
||
Status = RX_MAP_STATUS(INSUFFICIENT_RESOURCES);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SMB_COM_ECHO:
|
||
{
|
||
Status = SmbCeSend(
|
||
pExchange,
|
||
0,
|
||
pSmbAdminExchange->EchoProbe.pEchoProbeMdl,
|
||
pSmbAdminExchange->EchoProbe.EchoProbeLength);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbAdminExchangeReceive(
|
||
IN struct _SMB_EXCHANGE *pExchange, // The exchange instance
|
||
IN ULONG BytesIndicated,
|
||
IN ULONG BytesAvailable,
|
||
OUT ULONG *pBytesTaken,
|
||
IN PSMB_HEADER pSmbHeader,
|
||
OUT PMDL *pDataBufferPointer,
|
||
OUT PULONG pDataSize,
|
||
IN ULONG ReceiveFlags)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the recieve indication handling routine for net root construction exchanges
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance
|
||
|
||
BytesIndicated - the number of bytes indicated
|
||
|
||
Bytes Available - the number of bytes available
|
||
|
||
pBytesTaken - the number of bytes consumed
|
||
|
||
pSmbHeader - the byte buffer
|
||
|
||
pDataBufferPointer - the buffer into which the remaining data is to be copied.
|
||
|
||
pDataSize - the buffer size.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
This routine is called at DPC level.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange;
|
||
|
||
RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesIndicated/Available %ld %ld\n",BytesIndicated,BytesAvailable));
|
||
|
||
pSmbAdminExchange = (PSMB_ADMIN_EXCHANGE)pExchange;
|
||
|
||
switch (pSmbAdminExchange->SmbCommand) {
|
||
case SMB_COM_NEGOTIATE:
|
||
{
|
||
Status = ParseNegotiateResponse(
|
||
pSmbAdminExchange,
|
||
BytesIndicated,
|
||
BytesAvailable,
|
||
pBytesTaken,
|
||
pSmbHeader,
|
||
pDataBufferPointer,
|
||
pDataSize);
|
||
|
||
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
if (*pDataBufferPointer != NULL &&
|
||
((*pBytesTaken + *pDataSize) <= BytesAvailable ||
|
||
!FlagOn(ReceiveFlags,TDI_RECEIVE_ENTIRE_MESSAGE))) {
|
||
pSmbAdminExchange->Negotiate.pSecurityBlobMdl = *pDataBufferPointer;
|
||
} else {
|
||
*pBytesTaken = BytesAvailable;
|
||
Status = STATUS_SUCCESS;
|
||
pExchange->Status = STATUS_INVALID_NETWORK_RESPONSE;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SMB_COM_TREE_DISCONNECT:
|
||
case SMB_COM_LOGOFF_ANDX:
|
||
{
|
||
*pBytesTaken = BytesAvailable;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
case SMB_COM_ECHO:
|
||
// Since the echo probe responses are handled by the receive indication routine
|
||
// at DPC level this routine should never be called for echo probes.
|
||
|
||
default:
|
||
{
|
||
*pBytesTaken = 0;
|
||
Status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbAdminExchangeCopyDataHandler(
|
||
IN PSMB_EXCHANGE pExchange, // The exchange instance
|
||
IN PMDL pCopyDataBuffer,
|
||
IN ULONG DataSize)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the copy data handling routine for administrative SMB exchanges
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
{
|
||
UNREFERENCED_PARAMETER(pExchange);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbAdminExchangeSendCallbackHandler(
|
||
IN PSMB_EXCHANGE pExchange,
|
||
IN PMDL pXmitBuffer,
|
||
IN NTSTATUS SendCompletionStatus)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the send call back indication handling routine for transact exchanges
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
{
|
||
return STATUS_SUCCESS;
|
||
UNREFERENCED_PARAMETER(pExchange);
|
||
UNREFERENCED_PARAMETER(pXmitBuffer);
|
||
UNREFERENCED_PARAMETER(SendCompletionStatus);
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbAdminExchangeFinalize(
|
||
PSMB_EXCHANGE pExchange,
|
||
BOOLEAN *pPostFinalize)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finalkzes the construct net root exchange. It resumes the RDBSS by invoking
|
||
the call back and discards the exchange
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance
|
||
|
||
CurrentIrql - the current interrupt request level
|
||
|
||
pPostFinalize - a pointer to a BOOLEAN if the request should be posted
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
{
|
||
PSMB_ADMIN_EXCHANGE pSmbAdminExchange = (PSMB_ADMIN_EXCHANGE)pExchange;
|
||
|
||
if (pSmbAdminExchange->pResumptionContext != NULL) {
|
||
// Signal the event
|
||
*pPostFinalize = FALSE;
|
||
SmbCeResume(pSmbAdminExchange->pResumptionContext);
|
||
} else {
|
||
if (RxShouldPostCompletion()) {
|
||
*pPostFinalize = TRUE;
|
||
return STATUS_SUCCESS;
|
||
} else {
|
||
*pPostFinalize = FALSE;
|
||
SmbCeCompleteAdminExchange(pSmbAdminExchange);
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
SMB_EXCHANGE_DISPATCH_VECTOR
|
||
AdminExchangeDispatch = {
|
||
SmbAdminExchangeStart,
|
||
SmbAdminExchangeReceive,
|
||
SmbAdminExchangeCopyDataHandler,
|
||
NULL, // No Send Completion handler
|
||
SmbAdminExchangeFinalize
|
||
};
|
||
|
||
SMB_EXCHANGE_DISPATCH_VECTOR
|
||
EchoExchangeDispatch = {
|
||
SmbAdminExchangeStart,
|
||
SmbAdminExchangeReceive,
|
||
SmbAdminExchangeCopyDataHandler,
|
||
SmbAdminExchangeSendCallbackHandler,
|
||
SmbAdminExchangeFinalize
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|