windows-nt/Source/XPSP1/NT/base/fs/srv/smbproc.c

699 lines
19 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
smbproc.c
Abstract:
This module contains the high-level routines for processing SMBs.
Current contents:
SrvEndSmbProcessing
SrvProcessSmb
SrvRestartFsdComplete
SrvRestartSmbReceived
SrvSmbIllegalCommand
SrvSmbNotImplemented
SrvTransactionNotImplemented
Author:
David Treadwell (davidtr) 25-Sept-1989
Chuck Lenzmeier
Revision History:
--*/
#include "precomp.h"
#include "smbproc.tmh"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_SMBPROC
#ifdef ALLOC_PRAGMA
//#pragma alloc_text( PAGE, SrvEndSmbProcessing )
//#pragma alloc_text( PAGE, SrvProcessSmb )
#pragma alloc_text( PAGE, SrvRestartFsdComplete )
//#pragma alloc_text( PAGE, SrvRestartReceive )
#pragma alloc_text( PAGE, SrvRestartSmbReceived )
#pragma alloc_text( PAGE, SrvSmbIllegalCommand )
#pragma alloc_text( PAGE, SrvSmbNotImplemented )
#pragma alloc_text( PAGE, SrvTransactionNotImplemented )
#endif
USHORT SessionInvalidateCommand = 0xFFFF;
USHORT SessionInvalidateIndex = 0;
USHORT SessionInvalidateMod = 100;
VOID
SrvEndSmbProcessing (
IN OUT PWORK_CONTEXT WorkContext,
IN SMB_STATUS SmbStatus
)
/*++
Routine Description:
This routine is called when all request processing on an SMB is
complete. If no response is to be sent, this routine simply cleans
up and requeues the request buffer to the receive queue. If a
response is to be sent, this routine starts the sending of that
response; in this case SrvFsdRestartSmbComplete will do the rest of
the cleanup after the send completes.
Arguments:
WorkContext - Supplies a pointer to the work context block
containing information about the SMB.
SmbStatus - Either SmbStatusSendResponse or SmbStatusNoResponse.
Return Value:
None.
--*/
{
CLONG sendLength;
PAGED_CODE( );
IF_DEBUG(WORKER2) SrvPrint0( "SrvEndSmbProcessing entered\n" );
if ( SmbStatus == SmbStatusSendResponse ) {
//
// A response is to be sent. The response starts at
// WorkContext->ResponseHeader, and its length is calculated
// using WorkContext->ResponseParameters, which the SMB
// processor set to point to the next location *after* the end
// of the response.
//
sendLength = (CLONG)( (PCHAR)WorkContext->ResponseParameters -
(PCHAR)WorkContext->ResponseHeader );
WorkContext->ResponseBuffer->DataLength = sendLength;
//
// Set the bit in the SMB that indicates this is a response from
// the server.
//
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
//
// Send out the response. When the send completes,
// SrvFsdRestartSmbComplete is called. We then put the original
// buffer back on the receive queue.
//
SRV_START_SEND_2(
WorkContext,
SrvFsdRestartSmbAtSendCompletion,
NULL,
NULL
);
//
// The send has been started. Our work is done.
//
IF_DEBUG(WORKER2) SrvPrint0( "SrvEndSmbProcessing complete\n" );
return;
}
//
// There was no response to send. Dereference the work item.
//
SrvDereferenceWorkItem( WorkContext );
IF_DEBUG(WORKER2) SrvPrint0( "SrvEndSmbProcessing complete\n" );
return;
} // SrvEndSmbProcessing
VOID SRVFASTCALL
SrvProcessSmb (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This routine dispatches the command(s) in an SMB to the appropriate
processing routines. Based on the current command code, it calls
indirectly through the dispatch table (SrvFspSmbDispatchTable). The
SMB processor executes the command, updates pointers into the SMB,
and returns with an indication of whether there is another command
to be processed. If yes, this routine dispatches the next command.
If no, this routine sends a response, if any. Alternatively, if the
SMB processor starts an asynchronous operation, it can indicate so,
and this routine will simply return to its caller.
This routine is called initially from SrvRestartSmbReceived, which
is the FSP routine that gains control after a TdiReceive completion
work item is queued to the FSP. It is also called from other
restart routines when asynchronous operations, such as a file read,
complete and there are chained (AndX) commands to process.
SrvRestartSmbReceive loads SMB pointers and such into the work
context block calling this routine. Notably, it copies the first
command code in the SMB into WorkContext->NextCommand. When an AndX
command is processed, the SMB processor must load the chained
command code into NextCommand before calling this routine to process
that command.
Arguments:
WorkContext - Supplies a pointer to the work context block
containing information about the SMB to process. This block
is updated during the processing of the SMB.
Return Value:
None.
--*/
{
SMB_STATUS smbStatus;
LONG commandIndex;
PAGED_CODE( );
IF_DEBUG(WORKER2) SrvPrint0( "SrvProcessSmb entered\n" );
//
// Loop dispatching SMB processors until a status other than
// SmbStatusMoreCommands is returned. When an SMB processor returns
// this command code, it also sets the next command code in
// WorkContext->NextCommand, so that we can dispatch the next
// command.
//
if( WorkContext->ProcessingCount == 1 &&
WorkContext->Connection->SmbSecuritySignatureActive &&
SrvCheckSmbSecuritySignature( WorkContext ) == FALSE ) {
//
// We've received an SMB with an invalid security signature!
//
SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
return;
}
while ( TRUE ) {
if( ( (WorkContext->NextCommand == SessionInvalidateCommand) ||
(SessionInvalidateCommand == 0xFF00)
) &&
!((SessionInvalidateIndex++)%SessionInvalidateMod)
)
{
SrvVerifyUid( WorkContext, SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) );
if( WorkContext->Session )
{
WorkContext->Session->IsSessionExpired = TRUE;
KdPrint(( "-=- Expiring Session %p -=-\n", WorkContext->Session ));
}
}
//
// The first SMB has been validated in the FSD. It is safe to
// execute it now.
//
commandIndex = SrvSmbIndexTable[WorkContext->NextCommand];
#if DBG
IF_SMB_DEBUG( TRACE ) {
KdPrint(( "%s @%p, Blocking %d, Count %d\n",
SrvSmbDispatchTable[ commandIndex ].Name,
WorkContext,
WorkContext->UsingBlockingThread,
WorkContext->ProcessingCount ));
}
#endif
smbStatus = SrvSmbDispatchTable[commandIndex].Func( WorkContext );
//
// If the SMB processor returned SmbStatusInProgress, it started
// an asynchronous operation and will restart SMB processing
// when that operation completes.
//
if ( smbStatus == SmbStatusInProgress ) {
IF_DEBUG(WORKER2) SrvPrint0( "SrvProcessSmb complete\n" );
return;
}
//
// If the SMB processor didn't return SmbStatusMoreCommands,
// processing of the SMB is done. Call SrvEndSmbProcessing to
// send the response, if any, and rundown the WorkContext.
//
// *** SrvEndSmbProcessing is a separate function so that
// asynchronous restart routines have something to call when
// they are done processing the SMB.
//
if ( smbStatus != SmbStatusMoreCommands ) {
SrvEndSmbProcessing( WorkContext, smbStatus );
IF_DEBUG(WORKER2) SrvPrint0( "SrvProcessSmb complete\n" );
return;
}
//
// There are more commands in the SMB. Verify the SMB to make
// sure that it has a valid header, and that the word count and
// byte counts are within range.
//
if ( !SrvValidateSmb( WorkContext ) ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint0( "SrvProcessSmb: Invalid SMB.\n" );
SrvPrint1( " SMB received from %z\n",
(PCSTRING)&WorkContext->Connection->OemClientMachineNameString );
}
SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
IF_DEBUG(WORKER2) SrvPrint0( "SrvProcessSmb complete\n" );
return;
}
}
// can't get here.
} // SrvProcessSmb
VOID SRVFASTCALL
SrvRestartFsdComplete (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This is the restart routine invoked when SMB processing by the FSD
is complete. It's necessary to get back into the FSP in order to
dereference objects that were used during the processing of the SMB.
This is true because dereferencing an object may cause it to be
deleted, which cannot happen in the FSD.
This routine first dereferences control blocks. Then, if a response
SMB was sent, it checks for and processes send errors. Finally, it
requeues the work context block as a receive work item.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request
Return Value:
None.
--*/
{
PAGED_CODE( );
IF_DEBUG(WORKER1) SrvPrint0( " - SrvRestartFsdComplete\n" );
if ( WorkContext->OplockOpen ) {
SrvCheckDeferredOpenOplockBreak( WorkContext );
}
//
// Dereference the work item.
//
SrvDereferenceWorkItem( WorkContext );
IF_DEBUG(TRACE2) SrvPrint0( "SrvRestartFsdComplete complete\n" );
return;
} // SrvRestartFsdComplete
VOID SRVFASTCALL
SrvRestartReceive (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This is the restart routine for TDI Receive completion. It validates
the smb and setups header and parameter pointers in the work context
block and before forwarding the request to SmbProcessSmb.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PCONNECTION connection;
PIRP irp;
PSMB_HEADER header;
ULONG length;
PAGED_CODE( );
IF_DEBUG(WORKER1) SrvPrint0( " - SrvRestartReceive\n" );
connection = WorkContext->Connection;
irp = WorkContext->Irp;
//
// Save the length of the received message. Store the length
// in the work context block for statistics gathering.
//
length = (ULONG)irp->IoStatus.Information;
WorkContext->RequestBuffer->DataLength = length;
WorkContext->CurrentWorkQueue->stats.BytesReceived += length;
//
// Store in the work context block the time at which processing
// of the request began. Use the time that the work item was
// queued to the FSP for this purpose.
//
WorkContext->StartTime = WorkContext->Timestamp;
//
// Update the server network error count. If the TDI receive
// failed or was canceled, don't try to process an SMB.
//
if ( !irp->Cancel &&
NT_SUCCESS(irp->IoStatus.Status) ||
irp->IoStatus.Status == STATUS_BUFFER_OVERFLOW ) {
SrvUpdateErrorCount( &SrvNetworkErrorRecord, FALSE );
if( irp->IoStatus.Status == STATUS_BUFFER_OVERFLOW ) {
WorkContext->LargeIndication = TRUE;
}
//
// We (should) have received an SMB.
//
SMBTRACE_SRV2(
WorkContext->ResponseBuffer->Buffer,
WorkContext->ResponseBuffer->DataLength
);
//
// Initialize the error class and code fields in the header to
// indicate success.
//
header = WorkContext->ResponseHeader;
SmbPutUlong( &header->ErrorClass, STATUS_SUCCESS );
//
// If the connection is closing or the server is shutting down,
// ignore this SMB.
//
if ( (GET_BLOCK_STATE(connection) == BlockStateActive) &&
!SrvFspTransitioning ) {
//
// Verify the SMB to make sure that it has a valid header,
// and that the word count and byte counts are within range.
//
WorkContext->NextCommand = header->Command;
if ( SrvValidateSmb( WorkContext ) ) {
//
// If this is NOT a raw read request, clear the flag
// that indicates the we just sent an oplock break II to
// none. This allows subsequent raw reads to be
// processed.
//
if ( header->Command != SMB_COM_READ_RAW ) {
connection->BreakIIToNoneJustSent = FALSE;
}
//
// Process the received SMB. The called routine is
// responsible for sending any response(s) that are
// needed and for getting the receive buffer back onto
// the receive queue as soon as possible.
//
SrvProcessSmb( WorkContext );
IF_DEBUG(TRACE2) SrvPrint0( "SrvRestartReceive complete\n" );
return;
} else {
IF_DEBUG(SMB_ERRORS) {
SrvPrint0( "SrvProcessSmb: Invalid SMB.\n" );
SrvPrint1( " SMB received from %z\n",
(PCSTRING)&WorkContext->Connection->OemClientMachineNameString );
}
//
// The SMB is invalid. We send back an INVALID_SMB
// status, unless this looks like a Read Block Raw
// request, in which case we send back a zero-byte
// response, so as not to confuse the redirector.
//
if ( header->Command != SMB_COM_READ_RAW ) {
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
} else {
WorkContext->ResponseParameters = header;
}
if( WorkContext->LargeIndication ) {
//
// We need to consume the rest of the messaage!
//
SrvConsumeSmbData( WorkContext );
return;
}
SrvFsdSendResponse( WorkContext );
return;
}
} else {
SrvDereferenceWorkItem( WorkContext );
return;
}
} else if( irp->Cancel || (irp->IoStatus.Status == STATUS_CANCELLED) ) {
// The Cancel routine was called while we were receiving. Let us consume
// any data left on the transport and return cancelled as the user wishes.
// We don't bother to return anything if the connection is going down.
if( (GET_BLOCK_STATE(connection) == BlockStateActive) &&
!SrvFspTransitioning )
{
SrvSetSmbError( WorkContext, STATUS_CANCELLED );
if( WorkContext->LargeIndication ) {
//
// We need to consume the rest of the messaage!
//
SrvConsumeSmbData( WorkContext );
return;
}
SrvFsdSendResponse( WorkContext );
return;
}
else
{
SrvDereferenceWorkItem( WorkContext );
return;
}
} else {
IF_DEBUG(NETWORK_ERRORS) {
SrvPrint2( "SrvRestartReceive: status = %X for IRP %p\n",
irp->IoStatus.Status, irp );
}
SrvUpdateErrorCount( &SrvNetworkErrorRecord, TRUE );
SrvDereferenceWorkItem( WorkContext );
return;
}
} // SrvRestartReceive
VOID SRVFASTCALL
SrvRestartSmbReceived (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This function is the worker thread restart routine for received
SMBs. It calls SrvProcessSmb to start processing of the first
command in the SMB.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PAGED_CODE( );
IF_DEBUG(WORKER1) SrvPrint0( " - SrvRestartSmbReceived\n" );
if ( (GET_BLOCK_STATE(WorkContext->Connection) != BlockStateActive) ||
SrvFspTransitioning ) {
//
// The connection must be disconnecting. Simply ignore this SMB.
//
SrvDereferenceWorkItem( WorkContext );
} else {
//
// Process the received SMB. The called routine is responsible
// for sending any response(s) that are needed and for getting
// the receive buffer back onto the receive queue as soon as
// possible.
//
SrvProcessSmb( WorkContext );
}
IF_DEBUG(TRACE2) SrvPrint0( "SrvRestartSmbReceived complete\n" );
return;
} // SrvRestartSmbReceived
SMB_PROCESSOR_RETURN_TYPE SRVFASTCALL
SrvSmbIllegalCommand (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
This routine is called to process SMBs that have an illegal
(unassigned) command code. It builds an error response.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{
PAGED_CODE( );
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "SrvSmbIllegalCommand: command code 0x%lx\n",
(ULONG)WorkContext->NextCommand );
}
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_COMMAND );
return SmbStatusSendResponse;
} // SrvSmbIllegalCommand
SMB_PROCESSOR_RETURN_TYPE
SrvSmbNotImplemented (
SMB_PROCESSOR_PARAMETERS
)
{
PAGED_CODE( );
INTERNAL_ERROR(
ERROR_LEVEL_UNEXPECTED,
"SrvSmbNotImplemented: command code 0x%lx",
(ULONG)WorkContext->NextCommand,
NULL
);
SrvSetSmbError( WorkContext, STATUS_NOT_IMPLEMENTED );
return SmbStatusSendResponse;
} // SrvSmbNotImplemented
SMB_TRANS_STATUS
SrvTransactionNotImplemented (
IN OUT PWORK_CONTEXT WorkContext
)
{
PTRANSACTION transaction = WorkContext->Parameters.Transaction;
PAGED_CODE( );
DEBUG SrvPrint1( "SrvTransactionNotImplemented: function code %lx\n",
SmbGetUlong( (PULONG)&transaction->InSetup[0] ) );
SrvSetSmbError( WorkContext, STATUS_NOT_IMPLEMENTED );
return SmbTransStatusErrorWithoutData;
} // SrvTransactionNotImplemented