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.
1272 lines
40 KiB
1272 lines
40 KiB
/*++
|
|
|
|
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_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_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) {
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
RxFreePool(pSmbAdminExchange->SmbCeContext.pVNetRootContext);
|
|
pSmbAdminExchange->SmbCeContext.pVNetRootContext = NULL;
|
|
}
|
|
}
|
|
if(Status != STATUS_SUCCESS) {
|
|
SmbCeDecrementActiveExchangeCount();
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|