/*++ 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