|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
mailslot.c
Abstract:
This module implements the routines needed to process incoming mailslot requests.
Author:
Larry Osterman (larryo) 18-Oct-1991
Revision History:
18-Oct-1991 larryo
Created
--*/ #include "precomp.h"
#pragma hdrstop
#include <netlogon.h>
#define _INC_WINDOWS 1
#include <winsock2.h>
// Free list of 512-byte buffers.
LIST_ENTRY BowserMailslotBufferList = {0};
KSPIN_LOCK BowserMailslotSpinLock = {0};
// Largest "typical" datagram size
#define BOWSER_MAX_DATAGRAM_SIZE 512
// Total number of mailslot buffers currently allocated.
LONG BowserNumberOfMailslotBuffers = {0};
// Number of 512-byte buffers currently allocated.
LONG BowserNumberOfMaxSizeMailslotBuffers = {0};
// Number of 512-byte buffers currently in the free list.
LONG BowserNumberOfFreeMailslotBuffers = {0};
#if DBG
ULONG BowserMailslotCacheHitCount = 0;
ULONG BowserMailslotCacheMissCount = 0; #endif // DBG
//
// Variables describing bowser support for handling netlogon mailslot messages and
// PNP messages to Netlogon service or BrowserService.
typedef struct _BROWSER_PNP_STATE {
// Queue of mailslot messages.
LIST_ENTRY MailslotMessageQueue;
// Maximum queue length
ULONG MaxMessageCount;
// Current queue length
ULONG CurrentMessageCount;
// Queue of IRPs used to read the queues
IRP_QUEUE IrpQueue;
// Queue of PNP events
LIST_ENTRY PnpQueue;
} BROWSER_PNP_STATE, *PBROWSER_PNP_STATE;
//
// There is one BROWSER_PNP_STATE for the Netlogon service and one for the
// Browser service.
//
BROWSER_PNP_STATE BowserPnp[BOWSER_PNP_COUNT];
//
// Queue of PNP notifications to netlogon or browser service
//
typedef struct _BR_PNP_MESSAGE { LIST_ENTRY Next; // List of all queued entries.
NETLOGON_PNP_OPCODE NlPnpOpcode; // Operation to be notified
ULONG TransportFlags; // Flags describing transport
UNICODE_STRING TransportName; // Transport operation happened on
UNICODE_STRING HostedDomainName; // Hosted domain operation happened on
} BR_PNP_MESSAGE, *PBR_PNP_MESSAGE;
//
// Forwards for the alloc_text
//
NTSTATUS BowserNetlogonCopyMessage( IN PIRP Irp, IN PMAILSLOT_BUFFER MailslotBuffer );
NTSTATUS BowserCopyPnp( IN PIRP Irp, IN NETLOGON_PNP_OPCODE NlPnpOpcode, IN PUNICODE_STRING HostedDomainName, IN PUNICODE_STRING TransportName, IN ULONG TransportFlags );
VOID BowserTrimMessageQueue ( PBROWSER_PNP_STATE BrPnp );
BOOLEAN BowserProcessNetlogonMailslotWrite( IN PMAILSLOT_BUFFER MailslotBuffer );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE5NETLOGON, BowserNetlogonCopyMessage)
#pragma alloc_text(PAGE4BROW, BowserCopyPnp)
#pragma alloc_text(PAGE4BROW, BowserTrimMessageQueue)
#pragma alloc_text(PAGE5NETLOGON, BowserNetlogonDeleteTransportFromMessageQueue )
#pragma alloc_text(PAGE5NETLOGON, BowserProcessNetlogonMailslotWrite)
#pragma alloc_text(PAGE4BROW, BowserSendPnp)
#pragma alloc_text(PAGE4BROW, BowserEnablePnp )
#pragma alloc_text(PAGE4BROW, BowserReadPnp )
#pragma alloc_text(PAGE, BowserProcessMailslotWrite)
#pragma alloc_text(PAGE4BROW, BowserFreeMailslotBuffer)
#pragma alloc_text(INIT, BowserpInitializeMailslot)
#pragma alloc_text(PAGE, BowserpUninitializeMailslot)
#endif
NTSTATUS BowserNetlogonCopyMessage( IN PIRP Irp, IN PMAILSLOT_BUFFER MailslotBuffer ) /*++
Routine Description:
This routine copies the data from the specified MailslotBuffer into the IRP for the netlogon request.
This routine unconditionally frees the passed in Mailslot Buffer.
Arguments:
Irp - IRP for the IOCTL from the netlogon service.
MailslotBuffer - Buffer describing the mailslot message.
Return Value:
Status of the operation.
The caller should complete the I/O operation with this status code.
--*/ { NTSTATUS Status;
PSMB_HEADER SmbHeader; PSMB_TRANSACT_MAILSLOT MailslotSmb; PUCHAR MailslotData; OEM_STRING MailslotNameA; UNICODE_STRING MailslotNameU; UNICODE_STRING TransportName; UNICODE_STRING DestinationName; USHORT DataCount;
PNETLOGON_MAILSLOT NetlogonMailslot; PUCHAR Where;
PIO_STACK_LOCATION IrpSp;
BowserReferenceDiscardableCode( BowserNetlogonDiscardableCodeSection );
DISCARDABLE_CODE( BowserNetlogonDiscardableCodeSection );
//
// Extract the name of the mailslot and address/size of mailslot message
// from SMB.
//
SmbHeader = (PSMB_HEADER )MailslotBuffer->Buffer; MailslotSmb = (PSMB_TRANSACT_MAILSLOT)(SmbHeader+1); MailslotData = (((PCHAR )SmbHeader) + SmbGetUshort(&MailslotSmb->DataOffset)); RtlInitString(&MailslotNameA, MailslotSmb->Buffer ); DataCount = SmbGetUshort(&MailslotSmb->DataCount);
//
// Get the name of the transport and netbios name the mailslot message arrived on.
//
TransportName = MailslotBuffer->TransportName->Transport->PagedTransport->TransportName; DestinationName = MailslotBuffer->TransportName->PagedTransportName->Name->Name; IrpSp = IoGetCurrentIrpStackLocation(Irp);
try {
//
// Convert mailslot name to unicode for return.
//
Status = RtlOemStringToUnicodeString(&MailslotNameU, &MailslotNameA, TRUE);
if (!NT_SUCCESS(Status)) { BowserLogIllegalName( Status, MailslotNameA.Buffer, MailslotNameA.Length ); MailslotNameU.Buffer = NULL; try_return( NOTHING ); }
//
// Ensure the data fits in the user's output buffer.
//
if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(NETLOGON_MAILSLOT) + // Header structure
DataCount + // Actual mailslot message
sizeof(DWORD) + // alignment for socket address
sizeof(SOCKADDR_IN) + // Client Socket Address
sizeof(WCHAR) + // alignment of unicode strings
TransportName.Length + // TransportName
sizeof(WCHAR) + // zero terminator
MailslotNameU.Length + // Mailslot name
sizeof(WCHAR) + // zero terminator
DestinationName.Length + // Destination name
sizeof(WCHAR) ) { // zero terminator
try_return( Status = STATUS_BUFFER_TOO_SMALL ); }
//
// Get the address of Netlogon's buffer and fill in common portion.
//
NetlogonMailslot = MmGetSystemAddressForMdl( Irp->MdlAddress );
if ( NULL == NetlogonMailslot ) { try_return( Status = STATUS_NO_MEMORY ); }
if (!POINTER_IS_ALIGNED( NetlogonMailslot, ALIGN_DWORD) ) { try_return( Status = STATUS_INVALID_PARAMETER ); }
Where = (PUCHAR) (NetlogonMailslot+1);
NetlogonMailslot->TimeReceived = MailslotBuffer->TimeReceived;
//
// Copy the datagram to the buffer
//
NetlogonMailslot->MailslotMessageSize = DataCount; NetlogonMailslot->MailslotMessageOffset = (ULONG)(Where - (PUCHAR)NetlogonMailslot); RtlCopyMemory( Where, MailslotData, DataCount );
Where += DataCount;
//
// Copy Client IpAddress to buffer.
//
if ( MailslotBuffer->ClientIpAddress != 0 ) { PSOCKADDR_IN SockAddrIn;
*Where = 0; *(Where+1) = 0; *(Where+2) = 0; Where = ROUND_UP_POINTER( Where, ALIGN_DWORD );
NetlogonMailslot->ClientSockAddrSize = sizeof(SOCKADDR_IN); NetlogonMailslot->ClientSockAddrOffset = (ULONG)(Where - (PUCHAR)NetlogonMailslot);
SockAddrIn = (PSOCKADDR_IN) Where; RtlZeroMemory( SockAddrIn, sizeof(SOCKADDR_IN) ); SockAddrIn->sin_family = AF_INET; SockAddrIn->sin_addr.S_un.S_addr = MailslotBuffer->ClientIpAddress;
Where += sizeof(SOCKADDR_IN);
} else { NetlogonMailslot->ClientSockAddrSize = 0; NetlogonMailslot->ClientSockAddrOffset = 0; }
//
// Copy the transport name to the buffer
//
*Where = 0; Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR ); NetlogonMailslot->TransportNameSize = TransportName.Length; NetlogonMailslot->TransportNameOffset = (ULONG)(Where - (PUCHAR)NetlogonMailslot);
RtlCopyMemory( Where, TransportName.Buffer, TransportName.Length ); Where += TransportName.Length; *((PWCH)Where) = L'\0'; Where += sizeof(WCHAR);
//
// Copy the mailslot name to the buffer
//
NetlogonMailslot->MailslotNameSize = MailslotNameU.Length; NetlogonMailslot->MailslotNameOffset = (ULONG)(Where - (PUCHAR)NetlogonMailslot);
RtlCopyMemory( Where, MailslotNameU.Buffer, MailslotNameU.Length ); Where += MailslotNameU.Length; *((PWCH)Where) = L'\0'; Where += sizeof(WCHAR);
//
// Copy the destination netbios name to the buffer
//
NetlogonMailslot->DestinationNameSize = DestinationName.Length; NetlogonMailslot->DestinationNameOffset = (ULONG)(Where - (PUCHAR)NetlogonMailslot);
RtlCopyMemory( Where, DestinationName.Buffer, DestinationName.Length ); Where += DestinationName.Length; *((PWCH)Where) = L'\0'; Where += sizeof(WCHAR);
Status = STATUS_SUCCESS;
try_exit:NOTHING; } finally {
//
// Free Locally allocated buffers
//
RtlFreeUnicodeString(&MailslotNameU);
//
// Always free the incoming mailslot message
//
BowserFreeMailslotBuffer( MailslotBuffer );
}
BowserDereferenceDiscardableCode( BowserNetlogonDiscardableCodeSection ); return Status; }
NTSTATUS BowserCopyPnp( IN PIRP Irp, IN NETLOGON_PNP_OPCODE NlPnpOpcode, IN PUNICODE_STRING HostedDomainName, IN PUNICODE_STRING TransportName, IN ULONG TransportFlags ) /*++
Routine Description:
This routine copies the data for a PNP notification into the IRP for the I/O request.
Arguments:
Irp - IRP for the IOCTL from the service.
NlPnpOpcode - Opcode describing the event being notified.
HostedDomainName - Name of the hosted domain this event applies to
TransportName - Name of transport being affected.
TransportFlags - Flags describing the transport
Return Value:
Status of the operation.
The caller should complete the I/O operation with this status code.
--*/ { NTSTATUS Status;
PNETLOGON_MAILSLOT NetlogonMailslot; PUCHAR Where;
PIO_STACK_LOCATION IrpSp;
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
DISCARDABLE_CODE( BowserDiscardableCodeSection );
IrpSp = IoGetCurrentIrpStackLocation(Irp);
try {
//
// Ensure the data fits in the user's output buffer.
//
if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(NETLOGON_MAILSLOT) + // Header structure
TransportName->Length + sizeof(WCHAR) + // TransportName
HostedDomainName->Length + sizeof(WCHAR) + // DomainName
1 ) { // possible rounding requirement
try_return( Status = STATUS_BUFFER_TOO_SMALL ); }
//
// Get the address of service's buffer and fill in common portion.
//
NetlogonMailslot = MmGetSystemAddressForMdl( Irp->MdlAddress );
if ( NULL == NetlogonMailslot ) { try_return( Status = STATUS_NO_MEMORY ); }
if (!POINTER_IS_ALIGNED( NetlogonMailslot, ALIGN_DWORD) ) { try_return( Status = STATUS_INVALID_PARAMETER ); }
RtlZeroMemory( NetlogonMailslot, sizeof(NETLOGON_MAILSLOT));
//
// Copy the opcode
//
NetlogonMailslot->MailslotNameOffset = NlPnpOpcode;
//
// Copy the transport flags.
//
NetlogonMailslot->MailslotMessageOffset = TransportFlags;
//
// Copy the transport name to the buffer
//
Where = (PUCHAR) (NetlogonMailslot+1); *Where = 0; Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
NetlogonMailslot->TransportNameSize = TransportName->Length; NetlogonMailslot->TransportNameOffset = (ULONG)(Where - (PUCHAR)NetlogonMailslot);
RtlCopyMemory( Where, TransportName->Buffer, TransportName->Length ); Where += TransportName->Length; *((PWCH)Where) = L'\0'; Where += sizeof(WCHAR);
//
// Copy the hosted domain name to the buffer
//
NetlogonMailslot->DestinationNameSize = HostedDomainName->Length; NetlogonMailslot->DestinationNameOffset = (ULONG)(Where - (PUCHAR)NetlogonMailslot);
RtlCopyMemory( Where, HostedDomainName->Buffer, HostedDomainName->Length ); Where += HostedDomainName->Length; *((PWCH)Where) = L'\0'; Where += sizeof(WCHAR);
Status = STATUS_SUCCESS;
try_exit:NOTHING; } finally {
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
} return Status; }
VOID BowserTrimMessageQueue ( PBROWSER_PNP_STATE BrPnp )
/*++
Routine Description:
This routines ensures there are not too many mailslot messages in the message queue. Any excess messages are deleted.
Arguments:
BrPnp - Indicates which message queue to trim
Return Value:
None.
--*/
{ KIRQL OldIrql;
dprintf(DPRT_NETLOGON, ("Bowser: trim message queue to %ld\n", BrPnp->MaxMessageCount ));
//
//
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
DISCARDABLE_CODE( BowserDiscardableCodeSection );
//
// If too many messages are queued,
// delete the oldest messages.
//
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); while ( BrPnp->CurrentMessageCount > BrPnp->MaxMessageCount){ PLIST_ENTRY Entry; PMAILSLOT_BUFFER MailslotBuffer;
Entry = RemoveHeadList(&BrPnp->MailslotMessageQueue); BrPnp->CurrentMessageCount--; MailslotBuffer = CONTAINING_RECORD(Entry, MAILSLOT_BUFFER, Overlay.NextBuffer);
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); BowserFreeMailslotBuffer( MailslotBuffer ); ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql);
}
//
// If absolutely no queued messages are allowed,
// delete the queued PNP messages, too.
// (Either netlogon or the bowser is shutting down.)
//
if ( BrPnp->MaxMessageCount == 0 ) { while ( !IsListEmpty(&BrPnp->PnpQueue) ) { PLIST_ENTRY ListEntry; PBR_PNP_MESSAGE PnpMessage;
ListEntry = RemoveHeadList(&BrPnp->PnpQueue);
PnpMessage = CONTAINING_RECORD(ListEntry, BR_PNP_MESSAGE, Next);
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); FREE_POOL(PnpMessage); ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); } } RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
}
VOID BowserNetlogonDeleteTransportFromMessageQueue ( PTRANSPORT Transport )
/*++
Routine Description:
This routines removes queued mailslot messages that arrived on the specified transport.
Arguments:
Transport - Transport who's mailslot messages are to be deleted.
Return Value:
None.
--*/
{ KIRQL OldIrql; PLIST_ENTRY ListEntry; PBROWSER_PNP_STATE BrPnp=&BowserPnp[NETLOGON_PNP];
dprintf(DPRT_NETLOGON, ("Bowser: remove messages queued by transport %lx\n", Transport ));
//
//
BowserReferenceDiscardableCode( BowserNetlogonDiscardableCodeSection );
DISCARDABLE_CODE( BowserNetlogonDiscardableCodeSection );
//
// Loop through all of the queued messages.
//
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); for ( ListEntry = BrPnp->MailslotMessageQueue.Flink; ListEntry != &BrPnp->MailslotMessageQueue; ) {
PMAILSLOT_BUFFER MailslotBuffer;
//
// If the message wasn't queued by this transport,
// go on to the next entry.
//
MailslotBuffer = CONTAINING_RECORD(ListEntry, MAILSLOT_BUFFER, Overlay.NextBuffer);
if ( MailslotBuffer->TransportName->Transport != Transport ) { ListEntry = ListEntry->Flink;
//
// Otherwise,
// delete the entry.
//
} else {
dprintf(DPRT_ALWAYS, ("Bowser: removing message %lx queued by transport %lx\n", MailslotBuffer, Transport )); RemoveEntryList( ListEntry ); BrPnp->CurrentMessageCount--;
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); BowserFreeMailslotBuffer( MailslotBuffer ); ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql);
//
// Start over at the beginning of the list since we dropped the spinlock.
//
ListEntry = BrPnp->MailslotMessageQueue.Flink;
}
} RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); BowserDereferenceDiscardableCode( BowserNetlogonDiscardableCodeSection );
}
BOOLEAN BowserProcessNetlogonMailslotWrite( IN PMAILSLOT_BUFFER MailslotBuffer ) /*++
Routine Description:
This routine checks to see if the described mailslot message is destined to the Netlogon service and if the Bowser is currently handling such messages
Arguments:
MailslotBuffer - Buffer describing the mailslot message.
Return Value:
TRUE - iff the mailslot message was successfully queued to the netlogon service.
--*/ { KIRQL OldIrql; NTSTATUS Status;
PSMB_HEADER SmbHeader; PSMB_TRANSACT_MAILSLOT MailslotSmb; BOOLEAN TrimIt; BOOLEAN ReturnValue; PBROWSER_PNP_STATE BrPnp=&BowserPnp[NETLOGON_PNP];
PIRP Irp;
BowserReferenceDiscardableCode( BowserNetlogonDiscardableCodeSection );
DISCARDABLE_CODE( BowserNetlogonDiscardableCodeSection );
//
// If this message isn't destined to the Netlogon service,
// just return.
//
SmbHeader = (PSMB_HEADER )MailslotBuffer->Buffer; MailslotSmb = (PSMB_TRANSACT_MAILSLOT)(SmbHeader+1);
if ( _stricmp( MailslotSmb->Buffer, NETLOGON_LM_MAILSLOT_A ) != 0 && _stricmp( MailslotSmb->Buffer, NETLOGON_NT_MAILSLOT_A ) != 0 ) {
ReturnValue = FALSE;
//
// The mailslot message is destined to netlogon.
//
} else {
//
// Check to ensure we're queuing messages to Netlogon
//
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); if ( BrPnp->MaxMessageCount == 0 ) { RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); ReturnValue = FALSE;
//
// Queueing to netlogon is enabled.
//
} else {
//
// If there already is an IRP from netlogon queued,
// return this mailslot message to netlogon now.
//
// This routine locks BowserIrpQueueSpinLock so watch the spin lock
// locking order.
//
ReturnValue = TRUE;
Irp = BowserDequeueQueuedIrp( &BrPnp->IrpQueue );
if ( Irp != NULL ) {
ASSERT( IsListEmpty( &BrPnp->MailslotMessageQueue ) ); dprintf(DPRT_NETLOGON, ("Bowser: found already queued netlogon IRP\n"));
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
Status = BowserNetlogonCopyMessage( Irp, MailslotBuffer );
BowserCompleteRequest( Irp, Status );
} else {
//
// Queue the mailslot message for netlogon to pick up later.
//
InsertTailList( &BrPnp->MailslotMessageQueue, &MailslotBuffer->Overlay.NextBuffer);
BrPnp->CurrentMessageCount++;
TrimIt = (BrPnp->CurrentMessageCount > BrPnp->MaxMessageCount);
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
//
// If there are too many messages queued,
// trim entries from the front.
//
if ( TrimIt ) { BowserTrimMessageQueue(BrPnp); } } } }
BowserDereferenceDiscardableCode( BowserNetlogonDiscardableCodeSection ); return ReturnValue; }
VOID BowserSendPnp( IN NETLOGON_PNP_OPCODE NlPnpOpcode, IN PUNICODE_STRING HostedDomainName OPTIONAL, IN PUNICODE_STRING TransportName OPTIONAL, IN ULONG TransportFlags ) /*++
Routine Description:
This routine sends a PNP notification to the Netlogon service.
Arguments:
NlPnpOpcode - Opcode describing the event being notified.
HostedDomainName - Hosted domain name NULL - if the operation affects all hosted domains
TransportName - Name of transport being affected. NULL - if the operation affects all transports
TransportFlags - Flags describing the transport
Return Value:
None.
--*/ { KIRQL OldIrql; NTSTATUS Status;
PIRP Irp; PBR_PNP_MESSAGE PnpMessage = NULL; PBROWSER_PNP_STATE BrPnp; UNICODE_STRING NullUnicodeString = { 0, 0, NULL };
BowserReferenceDiscardableCode( BowserDiscardableCodeSection ); DISCARDABLE_CODE( BowserDiscardableCodeSection );
//
// Initialization.
//
if ( TransportName == NULL ) { TransportName = &NullUnicodeString; }
if ( HostedDomainName == NULL ) { HostedDomainName = &NullUnicodeString; }
//
// Send the PNP message to each service that wants it.
//
for ( BrPnp=&BowserPnp[0]; BrPnp<&BowserPnp[BOWSER_PNP_COUNT]; BrPnp++) {
//
// If this service doesn't want notification,
// skip it.
//
if ( BrPnp->MaxMessageCount == 0 ) { continue; }
//
// Preallocate the buffer since we can't do it under the spinlock.
//
if ( PnpMessage == NULL ) { PnpMessage = ALLOCATE_POOL( NonPagedPool, sizeof(BR_PNP_MESSAGE) + TransportName->Length + HostedDomainName->Length, POOL_NETLOGON_BUFFER);
//
// Copy the parameters into the newly allocated buffer.
//
if ( PnpMessage != NULL ) { LPBYTE Where; PnpMessage->NlPnpOpcode = NlPnpOpcode; PnpMessage->TransportFlags = TransportFlags; Where = (LPBYTE)(PnpMessage + 1);
// Copy the TransportName
PnpMessage->TransportName.MaximumLength = PnpMessage->TransportName.Length = TransportName->Length; PnpMessage->TransportName.Buffer = (LPWSTR) Where; RtlCopyMemory( Where, TransportName->Buffer, TransportName->Length ); Where += TransportName->Length;
// Copy the HostedDomainName
PnpMessage->HostedDomainName.MaximumLength = PnpMessage->HostedDomainName.Length = HostedDomainName->Length; PnpMessage->HostedDomainName.Buffer = (LPWSTR) Where; RtlCopyMemory( Where, HostedDomainName->Buffer, HostedDomainName->Length ); Where += HostedDomainName->Length; }
}
//
// Check to ensure we're queuing messages to this service.
//
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); if ( BrPnp->MaxMessageCount == 0 ) { RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
//
// Queueing to service is enabled.
//
} else {
//
// If there already is an IRP from the service queued,
// return this PNP message to the service now.
//
// This routine locks BowserIrpQueueSpinLock so watch the spin lock
// locking order.
//
Irp = BowserDequeueQueuedIrp( &BrPnp->IrpQueue );
if ( Irp != NULL ) {
ASSERT( IsListEmpty( &BrPnp->MailslotMessageQueue ) ); dprintf(DPRT_NETLOGON, ("Bowser: found already queued netlogon IRP\n"));
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
Status = BowserCopyPnp( Irp, NlPnpOpcode, HostedDomainName, TransportName, TransportFlags );
BowserCompleteRequest( Irp, Status );
} else {
//
// Queue the mailslot message for the service to pick up later.
// (Drop notification on the floor if there is no memory.)
//
if ( PnpMessage != NULL ) { InsertTailList( &BrPnp->PnpQueue, &PnpMessage->Next ); PnpMessage = NULL; }
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); } } }
//
// Free the PnpMessage buffer if we didn't need it.
//
if ( PnpMessage != NULL ) { FREE_POOL(PnpMessage); }
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection ); return; }
NTSTATUS BowserEnablePnp ( IN PLMDR_REQUEST_PACKET InputBuffer, IN ULONG ServiceIndex )
/*++
Routine Description:
This routine processes an IOCTL from the netlogon service to enable or disable the queueing of netlogon mailslot messages.
Arguments:
InputBuffer - Specifies the number of mailslot messages to queue. Zero disables queuing.
ServiceIndex - Index of service to set queue size for.
Return Value:
Status of operation.
Please note that this IRP is cancelable.
--*/
{ KIRQL OldIrql; NTSTATUS Status; ULONG MaxMessageCount; PBROWSER_PNP_STATE BrPnp=&BowserPnp[ServiceIndex];
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
DISCARDABLE_CODE( BowserDiscardableCodeSection );
try {
MaxMessageCount = InputBuffer->Parameters.NetlogonMailslotEnable.MaxMessageCount; dprintf(DPRT_NETLOGON, ("NtDeviceIoControlFile: Netlogon enable %ld\n", MaxMessageCount ));
//
// Set the new size of the message queue
//
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); BrPnp->MaxMessageCount = MaxMessageCount; RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
//
// Trim the message queue to the new size.
//
BowserTrimMessageQueue(BrPnp);
try_return(Status = STATUS_SUCCESS);
try_exit:NOTHING; } finally { BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
}
return Status;
}
NTSTATUS BowserReadPnp ( IN PIRP Irp, IN ULONG OutputBufferLength, IN ULONG ServiceIndex )
/*++
Routine Description:
This routine processes an IOCTL from the netlogon service to get the next mailslot message.
Arguments:
Irp - I/O request packet describing request.
ServiceIndex - Index of service to set queue size for.
Return Value:
Status of operation.
Please note that this IRP is cancelable.
--*/
{ KIRQL OldIrql; NTSTATUS Status; PBROWSER_PNP_STATE BrPnp=&BowserPnp[ServiceIndex];
//
// If this is Netlogon,
// page in BowserNetlogonCopyMessage.
//
if ( ServiceIndex == NETLOGON_PNP ) { BowserReferenceDiscardableCode( BowserNetlogonDiscardableCodeSection ); DISCARDABLE_CODE( BowserNetlogonDiscardableCodeSection ); }
//
// Reference the discardable code of this routine and
// BowserQueueNonBufferRequestReferenced()
//
BowserReferenceDiscardableCode( BowserDiscardableCodeSection ); DISCARDABLE_CODE( BowserDiscardableCodeSection );
//
// Ensure service has asked the browser to queue messages
//
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); if ( BrPnp->MaxMessageCount == 0 ) { dprintf(DPRT_NETLOGON, ("Bowser called from Netlogon when not enabled\n")); RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); Status = STATUS_NOT_SUPPORTED;
//
// If there already is a PNP message queued,
// just return it to netlogon immediately.
//
} else if ( !IsListEmpty( &BrPnp->PnpQueue )) { PBR_PNP_MESSAGE PnpMessage; PLIST_ENTRY ListEntry;
dprintf(DPRT_NETLOGON, ("Bowser found netlogon PNP message already queued\n"));
ListEntry = RemoveHeadList(&BrPnp->PnpQueue);
PnpMessage = CONTAINING_RECORD(ListEntry, BR_PNP_MESSAGE, Next);
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
Status = BowserCopyPnp( Irp, PnpMessage->NlPnpOpcode, &PnpMessage->HostedDomainName, &PnpMessage->TransportName, PnpMessage->TransportFlags );
FREE_POOL(PnpMessage);
//
// If there already is a mailslot message queued,
// just return it to netlogon immediately.
//
} else if ( ServiceIndex == NETLOGON_PNP && !IsListEmpty( &BrPnp->MailslotMessageQueue )) { PMAILSLOT_BUFFER MailslotBuffer; PLIST_ENTRY ListEntry;
dprintf(DPRT_NETLOGON, ("Bowser found netlogon mailslot message already queued\n"));
ListEntry = RemoveHeadList(&BrPnp->MailslotMessageQueue); BrPnp->CurrentMessageCount--;
MailslotBuffer = CONTAINING_RECORD(ListEntry, MAILSLOT_BUFFER, Overlay.NextBuffer);
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
Status = BowserNetlogonCopyMessage( Irp, MailslotBuffer );
//
// Otherwise, save this IRP until a mailslot message arrives.
// This routine locks BowserIrpQueueSpinLock so watch the spin lock
// locking order.
//
} else {
dprintf(DPRT_NETLOGON, ("Bowser: queue netlogon mailslot irp\n"));
Status = BowserQueueNonBufferRequestReferenced( Irp, &BrPnp->IrpQueue, BowserCancelQueuedRequest );
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); }
if ( ServiceIndex == NETLOGON_PNP ) { BowserDereferenceDiscardableCode( BowserNetlogonDiscardableCodeSection ); } BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
return Status;
}
VOID BowserProcessMailslotWrite( IN PVOID Context ) /*++
Routine Description:
This routine performs all the task time operations to perform a mailslot write.
It will open the mailslot, write the specified data into the mailslot, and close the mailslot.
Arguments:
IN PWORK_HEADER WorkHeader - Specifies the mailslot buffer holding the mailslot write
Return Value:
None.
--*/ { PSMB_HEADER SmbHeader; PSMB_TRANSACT_MAILSLOT MailslotSmb; PMAILSLOT_BUFFER MailslotBuffer = Context; PUCHAR MailslotData; HANDLE MailslotHandle = NULL; OBJECT_ATTRIBUTES ObjAttr; OEM_STRING MailslotNameA; UNICODE_STRING MailslotNameU; IO_STATUS_BLOCK IoStatusBlock; CHAR MailslotName[MAXIMUM_FILENAME_LENGTH+1]; NTSTATUS Status; ULONG DataCount; ULONG TotalDataCount;
PAGED_CODE();
ASSERT (MailslotBuffer->Signature == STRUCTURE_SIGNATURE_MAILSLOT_BUFFER);
SmbHeader = (PSMB_HEADER )MailslotBuffer->Buffer;
ASSERT (SmbHeader->Command == SMB_COM_TRANSACTION);
MailslotSmb = (PSMB_TRANSACT_MAILSLOT)(SmbHeader+1);
ASSERT (MailslotSmb->WordCount == 17);
ASSERT (MailslotSmb->Class == 2);
MailslotData = (((PCHAR )SmbHeader) + SmbGetUshort(&MailslotSmb->DataOffset));
DataCount = (ULONG)SmbGetUshort(&MailslotSmb->DataCount);
TotalDataCount = (ULONG)SmbGetUshort(&MailslotSmb->TotalDataCount);
//
// Verify that all of the data was received and that the indicated data doesn't
// overflow the received buffer.
//
if (TotalDataCount != DataCount || (MailslotData > MailslotBuffer->Buffer + MailslotBuffer->ReceiveLength) || (DataCount + SmbGetUshort(&MailslotSmb->DataOffset) > MailslotBuffer->ReceiveLength )) {
BowserLogIllegalDatagram(MailslotBuffer->TransportName, SmbHeader, (USHORT)MailslotBuffer->ReceiveLength, MailslotBuffer->ClientAddress, 0);
BowserFreeMailslotBuffer(MailslotBuffer); return; }
MailslotNameU.MaximumLength = MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)+sizeof(WCHAR);
#define DEVICE_PREFIX_LENGTH 7
strcpy(MailslotName, "\\Device");
strncpy( MailslotName+DEVICE_PREFIX_LENGTH, MailslotSmb->Buffer, sizeof(MailslotName)-DEVICE_PREFIX_LENGTH); MailslotName[sizeof(MailslotName)-1] = '\0';
RtlInitString(&MailslotNameA, MailslotName);
//
// Handle netlogon mailslot messages specially.
// Don't call the discardable code at all if netlogon isn't running
//
if ( BowserPnp[NETLOGON_PNP].MaxMessageCount != 0 && BowserProcessNetlogonMailslotWrite( MailslotBuffer ) ) { return; }
//
// Write the mailslot message to the mailslot
//
try { Status = RtlOemStringToUnicodeString(&MailslotNameU, &MailslotNameA, TRUE);
if (!NT_SUCCESS(Status)) { BowserLogIllegalName( Status, MailslotNameA.Buffer, MailslotNameA.Length ); try_return(NOTHING); }
InitializeObjectAttributes(&ObjAttr, &MailslotNameU, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtCreateFile(&MailslotHandle, // Handle
GENERIC_WRITE | SYNCHRONIZE, &ObjAttr, // Object Attributes
&IoStatusBlock, // Final I/O status block
NULL, // Allocation Size
FILE_ATTRIBUTE_NORMAL, // Normal attributes
FILE_SHARE_READ|FILE_SHARE_WRITE,// Sharing attributes
FILE_OPEN, // Create disposition
0, // CreateOptions
NULL, // EA Buffer
0); // EA Length
RtlFreeUnicodeString(&MailslotNameU);
//
// If the mailslot doesn't exist, ditch the request -
//
if (!NT_SUCCESS(Status)) { BowserStatistics.NumberOfFailedMailslotOpens += 1;
try_return(NOTHING); }
//
// Now that the mailslot is opened, write the mailslot data into
// the mailslot.
//
Status = NtWriteFile(MailslotHandle, NULL, NULL, NULL, &IoStatusBlock, MailslotData, DataCount, NULL, NULL);
if (!NT_SUCCESS(Status)) { BowserStatistics.NumberOfFailedMailslotWrites += 1; } else { BowserStatistics.NumberOfMailslotWrites += 1; }
try_exit:NOTHING; } finally {
//
// If we opened the mailslot, close it.
//
if (MailslotHandle != NULL) { ZwClose(MailslotHandle); }
//
// Free the mailslot buffer holding this mailslot.
//
BowserFreeMailslotBuffer(MailslotBuffer);
} }
PMAILSLOT_BUFFER BowserAllocateMailslotBuffer( IN PTRANSPORT_NAME TransportName, IN ULONG RequestedBufferSize ) /*++
Routine Description:
This routine will allocate a mailslot buffer from the mailslot buffer pool.
If it is unable to allocate a buffer, it will allocate the buffer from non-paged pool (up to the maximum configured by the user).
Arguments:
TransportName - The transport name for this request.
RequestedBufferSize - Minimum size of buffer to allocate.
Return Value:
MAILSLOT_BUFFER - The allocated buffer.
--*/ { KIRQL OldIrql; PMAILSLOT_BUFFER Buffer = NULL; ULONG BufferSize; BOOLEAN AllocatingMaxBuffer = FALSE;
//
// If the request fits into a cached buffer,
// and there is a cache buffer available,
// use it.
//
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql); if ( RequestedBufferSize <= BOWSER_MAX_DATAGRAM_SIZE && !IsListEmpty(&BowserMailslotBufferList)) { PMAILSLOT_BUFFER Buffer; PLIST_ENTRY Entry;
Entry = RemoveHeadList(&BowserMailslotBufferList); BowserNumberOfFreeMailslotBuffers --;
Buffer = CONTAINING_RECORD(Entry, MAILSLOT_BUFFER, Overlay.NextBuffer);
#if DBG
BowserMailslotCacheHitCount++; #endif // DBG
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
Buffer->TransportName = TransportName; BowserReferenceTransportName(TransportName); BowserReferenceTransport( TransportName->Transport );
return Buffer; }
//
// If we've got too many buffers allocated,
// don't allocate any more.
//
// BowserData.NumberOfMailslotBuffers is the maximum number we're allowed to have
// in the cache at once. It defaults to 3.
//
// BrPnp[NETLOGON].MaxMessageCount is the number of buffers the netlogon service may
// have queued at any one point in time. It may be zero when netlogon isn't
// running or if we're running on a non-DC. On DC's it defaults to 500.
//
// Add 50, to ensure we don't limit it by too much.
//
if ( (ULONG)BowserNumberOfMailslotBuffers >= max( (ULONG)BowserData.NumberOfMailslotBuffers, BowserPnp[NETLOGON_PNP].MaxMessageCount+50 )) {
BowserStatistics.NumberOfMissedMailslotDatagrams += 1; BowserNumberOfMissedMailslotDatagrams += 1; RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql); return NULL; }
//
// The first few buffers we allocate should be maximum size so we can keep a preallocated
// cache of huge buffers.
//
if ( BowserNumberOfMaxSizeMailslotBuffers < BowserData.NumberOfMailslotBuffers && RequestedBufferSize <= BOWSER_MAX_DATAGRAM_SIZE ) { BufferSize = FIELD_OFFSET(MAILSLOT_BUFFER, Buffer) + BOWSER_MAX_DATAGRAM_SIZE; AllocatingMaxBuffer = TRUE; BowserNumberOfMaxSizeMailslotBuffers += 1; } else { BufferSize = FIELD_OFFSET(MAILSLOT_BUFFER, Buffer) + RequestedBufferSize; }
BowserNumberOfMailslotBuffers += 1;
ASSERT ( (BufferSize - FIELD_OFFSET(MAILSLOT_BUFFER, Buffer)) <= 0xffff);
#if DBG
BowserMailslotCacheMissCount++; #endif // DBG
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
Buffer = ALLOCATE_POOL(NonPagedPool, BufferSize, POOL_MAILSLOT_BUFFER);
//
// If we couldn't allocate the buffer from non paged pool, give up.
//
if (Buffer == NULL) { ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql);
ASSERT (BowserNumberOfMailslotBuffers);
BowserNumberOfMailslotBuffers -= 1; if ( AllocatingMaxBuffer ) { BowserNumberOfMaxSizeMailslotBuffers -= 1; }
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
BowserStatistics.NumberOfFailedMailslotAllocations += 1;
//
// Since we couldn't allocate this buffer, we've effectively missed
// this mailslot request.
//
BowserStatistics.NumberOfMissedMailslotDatagrams += 1; BowserNumberOfMissedMailslotDatagrams += 1;
return NULL; }
Buffer->Signature = STRUCTURE_SIGNATURE_MAILSLOT_BUFFER;
Buffer->Size = FIELD_OFFSET(MAILSLOT_BUFFER, Buffer);
Buffer->BufferSize = BufferSize - FIELD_OFFSET(MAILSLOT_BUFFER, Buffer);
Buffer->TransportName = TransportName; BowserReferenceTransportName(TransportName); BowserReferenceTransport( TransportName->Transport );
return Buffer; }
VOID BowserFreeMailslotBuffer( IN PMAILSLOT_BUFFER Buffer ) /*++
Routine Description:
This routine will return a mailslot buffer to the view buffer pool.
If the buffer was allocated from must-succeed pool, it is freed back to pool. In addition, if the buffer is smaller than the current max view buffer size, we free it.
Arguments:
IN PVIEW_BUFFER Buffer - Supplies the buffer to free
Return Value:
None.
--*/ { KIRQL OldIrql; PTRANSPORT Transport;
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
DISCARDABLE_CODE( BowserDiscardableCodeSection );
Transport = Buffer->TransportName->Transport; (VOID) BowserDereferenceTransportName( Buffer->TransportName ); BowserDereferenceTransport( Transport);
ACQUIRE_SPIN_LOCK(&BowserMailslotSpinLock, &OldIrql);
//
// Also, if a new transport was added that is larger than this buffer,
// we want to free the buffer.
//
//
// If we have more mailslot buffers than the size of our lookaside list,
// free it, don't stick it on our lookaside list.
//
if (Buffer->BufferSize != BOWSER_MAX_DATAGRAM_SIZE || BowserNumberOfFreeMailslotBuffers > BowserData.NumberOfMailslotBuffers) {
//
// Since we're returning this buffer to pool, we shouldn't count it
// against our total number of mailslot buffers.
//
BowserNumberOfMailslotBuffers -= 1;
ASSERT (BowserNumberOfMailslotBuffers >= 0);
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
FREE_POOL(Buffer);
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
return; }
InsertTailList(&BowserMailslotBufferList, &Buffer->Overlay.NextBuffer); BowserNumberOfFreeMailslotBuffers ++;
RELEASE_SPIN_LOCK(&BowserMailslotSpinLock, OldIrql);
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection ); }
VOID BowserFreeMailslotBufferHighIrql( IN PMAILSLOT_BUFFER Buffer ) /*++
Routine Description:
This routine will return a mailslot buffer to the view buffer pool if the caller is at raised irql.
Arguments:
Buffer - Supplies the buffer to free
Return Value:
None.
--*/ { //
// Queue the request to a worker routine.
//
ExInitializeWorkItem(&Buffer->Overlay.WorkHeader, (PWORKER_THREAD_ROUTINE) BowserFreeMailslotBuffer, Buffer);
BowserQueueDelayedWorkItem( &Buffer->Overlay.WorkHeader ); }
VOID BowserpInitializeMailslot ( VOID ) /*++
Routine Description:
This routine will allocate a transport descriptor and bind the bowser to the transport.
Arguments:
None
Return Value:
None
--*/ { PBROWSER_PNP_STATE BrPnp;
KeInitializeSpinLock(&BowserMailslotSpinLock);
InitializeListHead(&BowserMailslotBufferList);
for ( BrPnp=&BowserPnp[0]; BrPnp<&BowserPnp[BOWSER_PNP_COUNT]; BrPnp++) { InitializeListHead(&BrPnp->MailslotMessageQueue); InitializeListHead(&BrPnp->PnpQueue);
BowserInitializeIrpQueue( &BrPnp->IrpQueue ); }
}
VOID BowserpUninitializeMailslot ( VOID ) /*++
Routine Description:
Arguments:
None
Return Value:
None
--*/ { PBROWSER_PNP_STATE BrPnp; PAGED_CODE();
//
// Trim the netlogon message queue to zero entries.
//
for ( BrPnp=&BowserPnp[0]; BrPnp<&BowserPnp[BOWSER_PNP_COUNT]; BrPnp++) { BrPnp->MaxMessageCount = 0; BowserTrimMessageQueue(BrPnp); BowserUninitializeIrpQueue( &BrPnp->IrpQueue ); }
//
// Free the mailslot buffers.
while (!IsListEmpty(&BowserMailslotBufferList)) { PLIST_ENTRY Entry; PMAILSLOT_BUFFER Buffer;
Entry = RemoveHeadList(&BowserMailslotBufferList); Buffer = CONTAINING_RECORD(Entry, MAILSLOT_BUFFER, Overlay.NextBuffer);
FREE_POOL(Buffer);
}
}
|