windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/logonsrv/server/mailslot.c

3082 lines
87 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*--
Copyright (c) 1987-1996 Microsoft Corporation
Module Name:
mailslot.c
Abstract:
Routines for doing I/O on the netlogon service's mailslots.
Author:
03-Nov-1993 (cliffv)
Environment:
User mode only.
Contains NT-specific code.
Requires ANSI C extensions: slash-slash comments, long external names.
Revision History:
--*/
//
// Common include files.
//
#include "logonsrv.h" // Include files common to entire service
#pragma hdrstop
//
// Include files specific to this .c file
//
#include <lmbrowsr.h> // I_BrowserSetNetlogonState
#include <srvann.h> // Service announcement
#include <nbtioctl.h> // IOCTL_NETBT_REMOVE_FROM_REMOTE_TABLE
//
// Define maximum buffer size returned from the browser.
//
// Header returned by browser + actual mailslot message size + name of
// mailslot + name of transport.
//
#define MAILSLOT_MESSAGE_SIZE \
(sizeof(NETLOGON_MAILSLOT)+ \
NETLOGON_MAX_MS_SIZE + \
(NETLOGON_LM_MAILSLOT_LEN+1) * sizeof(WCHAR) + \
(MAXIMUM_FILENAME_LENGTH+1) * sizeof(WCHAR))
/////////////////////////////////////////////////////////////////////////////
//
// Structure describing one of the primary mailslots the netlogon service
// will read messages from.
//
// This structure is used only by netlogon's main thread and therefore needs
// no synchronization.
//
/////////////////////////////////////////////////////////////////////////////
typedef struct _NETLOGON_MAILSLOT_DESC {
HANDLE BrowserHandle; // Handle to the browser device driver
HANDLE BrowserReadEvent;// Handle to wait on for overlapped I/O
OVERLAPPED Overlapped; // Governs overlapped I/O
BOOL ReadPending; // True if a read operation is pending
LPBYTE CurrentMessage; // Pointer to Message1 or Message2 below
LPBYTE PreviousMessage; // Previous value of CurrentMessage
//
// Buffer containing message from browser
//
// The buffers are alternated allowing us to compare if an incoming
// message is identical to the previous message.
//
// Leave room so the actual used portion of each buffer is properly aligned.
// The NETLOGON_MAILSLOT struct begins with a LARGE_INTEGER which must be
// aligned.
BYTE Message1[ MAILSLOT_MESSAGE_SIZE + ALIGN_WORST ];
BYTE Message2[ MAILSLOT_MESSAGE_SIZE + ALIGN_WORST ];
} NETLOGON_MAILSLOT_DESC, *PNETLOGON_MAILSLOT_DESC;
PNETLOGON_MAILSLOT_DESC NlGlobalMailslotDesc;
HANDLE
NlBrowserCreateEvent(
VOID
)
/*++
Routine Description:
Creates an event to be used in a DeviceIoControl to the browser.
??: Consider caching one or two events to reduce the number of create
events
Arguments:
None
Return Value:
Handle to an event or NULL if the event couldn't be allocated.
--*/
{
HANDLE EventHandle;
//
// Create a completion event
//
EventHandle = CreateEvent(
NULL, // No security ettibutes
TRUE, // Manual reset
FALSE, // Initially not signaled
NULL); // No Name
if ( EventHandle == NULL ) {
NlPrint((NL_CRITICAL, "Cannot create Browser read event %ld\n", GetLastError() ));
}
return EventHandle;
}
VOID
NlBrowserCloseEvent(
IN HANDLE EventHandle
)
/*++
Routine Description:
Closes an event used in a DeviceIoControl to the browser.
Arguments:
EventHandle - Handle of the event to close
Return Value:
None.
--*/
{
(VOID) CloseHandle( EventHandle );
}
VOID
NlBrowserClose(
VOID
);
NTSTATUS
NlBrowserDeviceIoControl(
IN HANDLE BrowserHandle,
IN DWORD FunctionCode,
IN PLMDR_REQUEST_PACKET RequestPacket,
IN DWORD RequestPacketSize,
IN LPBYTE Buffer,
IN DWORD BufferSize
)
/*++
Routine Description:
Send a DeviceIoControl syncrhonously to the browser.
Arguments:
FunctionCode - DeviceIoControl function code
RequestPacket - The request packet to send.
RequestPacketSize - Size (in bytes) of the request packet.
Buffer - Additional buffer to pass to the browser
BufferSize - Size (in bytes) of Buffer
Return Value:
Status of the operation.
STATUS_NETWORK_UNREACHABLE: Cannot write to network.
STATUS_BAD_NETWORK_PATH: The name the datagram is destined for isn't
registered
--*/
{
NTSTATUS Status;
DWORD WinStatus;
OVERLAPPED Overlapped;
DWORD BytesReturned;
//
// Initialization
//
RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
//
// Get a completion event
//
Overlapped.hEvent = NlBrowserCreateEvent();
if ( Overlapped.hEvent == NULL ) {
return NetpApiStatusToNtStatus( GetLastError() );
}
//
// Send the request to the Datagram Receiver device driver.
//
if ( !DeviceIoControl(
BrowserHandle,
FunctionCode,
RequestPacket,
RequestPacketSize,
Buffer,
BufferSize,
&BytesReturned,
&Overlapped )) {
WinStatus = GetLastError();
if ( WinStatus == ERROR_IO_PENDING ) {
if ( !GetOverlappedResult( BrowserHandle,
&Overlapped,
&BytesReturned,
TRUE )) {
WinStatus = GetLastError();
} else {
WinStatus = NO_ERROR;
}
}
} else {
WinStatus = NO_ERROR;
}
//
// Delete the completion event
//
NlBrowserCloseEvent( Overlapped.hEvent );
if ( WinStatus ) {
//
// Some transports return an error if the name cannot be resolved:
// Nbf returns ERROR_NOT_READY
// NetBt returns ERROR_BAD_NETPATH
//
if ( WinStatus == ERROR_BAD_NETPATH || WinStatus == ERROR_NOT_READY ) {
Status = STATUS_BAD_NETWORK_PATH;
} else {
NlPrint((NL_CRITICAL,"Ioctl %lx to Browser returns %ld\n", FunctionCode, WinStatus));
Status = NetpApiStatusToNtStatus( WinStatus );
}
} else {
Status = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
NlBrowserOpenDriver(
PHANDLE BrowserHandle
)
/*++
Routine Description:
This routine opens the NT LAN Man Datagram Receiver driver.
Arguments:
BrowserHandle - Upon success, returns a handle to the browser driver
Close it using NtClose
Return Value:
Status of the operation
--*/
{
NTSTATUS Status;
BOOL ReturnValue;
UNICODE_STRING DeviceName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
//
// Open the browser device.
//
RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
InitializeObjectAttributes(
&ObjectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenFile(
BrowserHandle,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
0,
0
);
if (NT_SUCCESS(Status)) {
Status = IoStatusBlock.Status;
}
return Status;
}
NTSTATUS
NlBrowserRenameDomain(
IN LPWSTR OldDomainName OPTIONAL,
IN LPWSTR NewDomainName
)
/*++
Routine Description:
Tell the browser to rename the domain.
Arguments:
OldDomainName - previous name of the domain.
If not specified, the primary domain is implied.
NewDomainName - new name of the domain.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
HANDLE BrowserHandle = NULL;
LPBYTE Where;
UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+2*(DNLEN+1)*sizeof(WCHAR)];
PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
//
// Open the browser driver.
//
Status = NlBrowserOpenDriver( &BrowserHandle );
if (Status != NERR_Success) {
return(Status);
}
//
// Build the request packet.
//
RtlInitUnicodeString( &RequestPacket->TransportName, NULL );
RequestPacket->Parameters.DomainRename.ValidateOnly = FALSE;
//
// Copy the new domain name into the packet.
//
Where = (LPBYTE) RequestPacket->Parameters.DomainRename.DomainName;
RequestPacket->Parameters.DomainRename.DomainNameLength = wcslen( NewDomainName ) * sizeof(WCHAR);
wcscpy( (LPWSTR)Where, NewDomainName );
Where += RequestPacket->Parameters.DomainRename.DomainNameLength + sizeof(WCHAR);
//
// Copy the old domain name to the request packet.
//
if ( OldDomainName == NULL ) {
RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
} else {
wcscpy( (LPWSTR)Where, OldDomainName );
RtlInitUnicodeString( &RequestPacket->EmulatedDomainName,
(LPWSTR)Where );
Where += RequestPacket->EmulatedDomainName.Length + sizeof(WCHAR);
}
//
// Pass the reeqest to the browser.
//
Status = NlBrowserDeviceIoControl(
BrowserHandle,
IOCTL_LMDR_RENAME_DOMAIN,
RequestPacket,
(ULONG)(Where - (LPBYTE)RequestPacket),
NULL,
0 );
if (Status != NERR_Success) {
NlPrint((NL_CRITICAL,
"NlBrowserRenameDomain: Unable rename domain from %ws to %ws: %lx\n",
OldDomainName,
NewDomainName,
Status ));
}
if ( BrowserHandle != NULL ) {
NtClose( BrowserHandle );
}
return Status;
}
NET_API_STATUS
NlBrowserDeviceControlGetInfo(
IN DWORD FunctionCode,
IN PLMDR_REQUEST_PACKET RequestPacket,
IN DWORD RequestPacketSize,
OUT LPVOID *OutputBuffer,
IN ULONG PreferedMaximumLength,
IN ULONG BufferHintSize
)
/*++
Routine Description:
This function allocates the buffer and fill it with the information
that is retrieved from the datagram receiver.
Arguments:
FunctionCode - DeviceIoControl function code
RequestPacket - The request packet to send.
RequestPacketSize - Size (in bytes) of the request packet.
OutputBuffer - Returns a pointer to the buffer allocated by this routine
which contains the use information requested. This buffer should
be freed using MIDL_user_free.
PreferedMaximumLength - Supplies the number of bytes of information to
return in the buffer. If this value is MAXULONG, we will try to
return all available information if there is enough memory resource.
BufferHintSize - Supplies the hint size of the output buffer so that the
memory allocated for the initial buffer will most likely be large
enough to hold all requested data.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
//
// Buffer allocation size for enumeration output buffer.
//
#define INITIAL_ALLOCATION_SIZE 48*1024 // First attempt size (48K)
#define FUDGE_FACTOR_SIZE 1024 // Second try TotalBytesNeeded
// plus this amount
NET_API_STATUS NetStatus;
NTSTATUS Status;
DWORD OutputBufferLength;
DWORD TotalBytesNeeded = 1;
ULONG OriginalResumeKey;
//
// Initialization
//
if ( NlGlobalMailslotDesc == NULL ||
NlGlobalMailslotDesc->BrowserHandle == NULL ) {
return ERROR_NOT_SUPPORTED;
}
OriginalResumeKey = RequestPacket->Parameters.EnumerateNames.ResumeHandle;
//
// If PreferedMaximumLength is MAXULONG, then we are supposed to get all
// the information, regardless of size. Allocate the output buffer of a
// reasonable size and try to use it. If this fails, the Redirector FSD
// will say how much we need to allocate.
//
if (PreferedMaximumLength == MAXULONG) {
OutputBufferLength = (BufferHintSize) ?
BufferHintSize :
INITIAL_ALLOCATION_SIZE;
} else {
OutputBufferLength = PreferedMaximumLength;
}
OutputBufferLength = ROUND_UP_COUNT(OutputBufferLength, ALIGN_WCHAR);
if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
//
// Make the request of the Datagram Receiver
//
RequestPacket->Parameters.EnumerateServers.EntriesRead = 0;
Status = NlBrowserDeviceIoControl(
NlGlobalMailslotDesc->BrowserHandle,
FunctionCode,
RequestPacket,
RequestPacketSize,
*OutputBuffer,
OutputBufferLength );
NetStatus = NetpNtStatusToApiStatus(Status);
//
// If we couldn't get all the data on the first call,
// the datagram receiver returned the needed size of the buffer.
//
if ( RequestPacket->Parameters.EnumerateNames.EntriesRead !=
RequestPacket->Parameters.EnumerateNames.TotalEntries ) {
NetpAssert(
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.EnumerateNames.TotalBytesNeeded
) ==
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.EnumerateServers.TotalBytesNeeded
)
);
NetpAssert(
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.GetBrowserServerList.TotalBytesNeeded
) ==
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.EnumerateServers.TotalBytesNeeded
)
);
TotalBytesNeeded = RequestPacket->Parameters.EnumerateNames.TotalBytesNeeded;
}
if ((TotalBytesNeeded > OutputBufferLength) &&
(PreferedMaximumLength == MAXULONG)) {
//
// Initial output buffer allocated was too small and we need to return
// all data. First free the output buffer before allocating the
// required size plus a fudge factor just in case the amount of data
// grew.
//
MIDL_user_free(*OutputBuffer);
OutputBufferLength =
ROUND_UP_COUNT((TotalBytesNeeded + FUDGE_FACTOR_SIZE),
ALIGN_WCHAR);
if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
NetpAssert(
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.EnumerateNames.ResumeHandle
) ==
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.EnumerateServers.ResumeHandle
)
);
NetpAssert(
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.EnumerateNames.ResumeHandle
) ==
FIELD_OFFSET(
LMDR_REQUEST_PACKET,
Parameters.GetBrowserServerList.ResumeHandle
)
);
RequestPacket->Parameters.EnumerateNames.ResumeHandle = OriginalResumeKey;
RequestPacket->Parameters.EnumerateServers.EntriesRead = 0;
//
// Make the request of the Datagram Receiver
//
Status = NlBrowserDeviceIoControl(
NlGlobalMailslotDesc->BrowserHandle,
FunctionCode,
RequestPacket,
RequestPacketSize,
*OutputBuffer,
OutputBufferLength );
NetStatus = NetpNtStatusToApiStatus(Status);
}
//
// If not successful in getting any data, or if the caller asked for
// all available data with PreferedMaximumLength == MAXULONG and
// our buffer overflowed, free the output buffer and set its pointer
// to NULL.
//
if ((NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA) ||
(TotalBytesNeeded == 0) ||
(PreferedMaximumLength == MAXULONG && NetStatus == ERROR_MORE_DATA) ||
(RequestPacket->Parameters.EnumerateServers.EntriesRead == 0)) {
MIDL_user_free(*OutputBuffer);
*OutputBuffer = NULL;
//
// PreferedMaximumLength == MAXULONG and buffer overflowed means
// we do not have enough memory to satisfy the request.
//
if (NetStatus == ERROR_MORE_DATA) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
}
}
return NetStatus;
}
NET_API_STATUS
NlBrowserGetTransportList(
OUT PLMDR_TRANSPORT_LIST *TransportList
)
/*++
Routine Description:
This routine returns the list of transports bound into the browser.
Arguments:
TransportList - Transport list to return.
This buffer should be freed using MIDL_user_free.
Return Value:
NERR_Success or reason for failure.
--*/
{
NET_API_STATUS NetStatus;
LMDR_REQUEST_PACKET RequestPacket;
RequestPacket.Type = EnumerateXports;
RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
NetStatus = NlBrowserDeviceControlGetInfo(
IOCTL_LMDR_ENUMERATE_TRANSPORTS,
&RequestPacket,
sizeof(RequestPacket),
TransportList,
0xffffffff,
4096 );
return NetStatus;
}
NTSTATUS
NlBrowserAddDelName(
IN PDOMAIN_INFO DomainInfo,
IN BOOLEAN AddName,
IN DGRECEIVER_NAME_TYPE NameType,
IN LPWSTR TransportName OPTIONAL,
IN PUNICODE_STRING Name OPTIONAL
)
/*++
Routine Description:
Add or delete a name in the browser.
Arguments:
DomainInfo - Hosted domain the name is to be added/deleted for.
AddName - TRUE to add the name. FALSE to delete the name.
NameType - Type of name to be added/deleted
TransportName -- Name of the transport to send on.
Use NULL to send on all transports.
Name - Name to be added
If not specified, all names of NameType are deleted for this hosted domain.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
LPBYTE Where;
PLMDR_REQUEST_PACKET RequestPacket = NULL;
ULONG TransportNameSize;
//
// Build the request packet.
//
if ( TransportName != NULL ) {
TransportNameSize = (wcslen(TransportName) + 1) * sizeof(WCHAR);
} else {
TransportNameSize = 0;
}
RequestPacket = NetpMemoryAllocate( sizeof(LMDR_REQUEST_PACKET) +
(max(CNLEN, DNLEN) + 1) * sizeof(WCHAR) +
(DNLEN + 1) * sizeof(WCHAR) +
TransportNameSize );
if (RequestPacket == NULL) {
return STATUS_NO_MEMORY;
}
RequestPacket->Parameters.AddDelName.Type = NameType;
//
// Copy the name to be added to request packet.
//
Where = (LPBYTE) RequestPacket->Parameters.AddDelName.Name;
if ( Name == NULL ) {
NlAssert( !AddName );
RequestPacket->Parameters.AddDelName.DgReceiverNameLength = 0;
} else {
RequestPacket->Parameters.AddDelName.DgReceiverNameLength =
Name->Length;
RtlCopyMemory( Where, Name->Buffer, Name->Length );
Where += Name->Length;
}
//
// Copy the hosted domain name to the request packet.
//
wcscpy( (LPWSTR)Where,
DomainInfo->DomUnicodeDomainNameString.Buffer );
RtlInitUnicodeString( &RequestPacket->EmulatedDomainName,
(LPWSTR)Where );
Where += DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR);
//
// Fill in the TransportName
//
if ( TransportName != NULL ) {
wcscpy( (LPWSTR) Where, TransportName);
RtlInitUnicodeString( &RequestPacket->TransportName, (LPWSTR) Where );
Where += TransportNameSize;
} else {
RequestPacket->TransportName.Length = 0;
RequestPacket->TransportName.Buffer = NULL;
}
//
// Do the actual work
//
Status = NlBrowserDeviceIoControl(
NlGlobalMailslotDesc->BrowserHandle,
AddName ? IOCTL_LMDR_ADD_NAME_DOM : IOCTL_LMDR_DELETE_NAME_DOM,
RequestPacket,
(ULONG)(Where - (LPBYTE)RequestPacket),
NULL,
0 );
NetpMemoryFree( RequestPacket );
return Status;
}
VOID
NlBrowserAddName(
IN PDOMAIN_INFO DomainInfo
)
/*++
Routine Description:
Add the Domain<1B> name. This is the name NetGetDcName uses to identify
the PDC.
Arguments:
DomainInfo - Hosted domain the name is to be added for.
Return Value:
None.
--*/
{
LPWSTR MsgStrings[3] = { NULL };
BOOL AtLeastOneTransportEnabled = FALSE;
BOOL NameAdded = FALSE;
if ( NlGlobalMailslotDesc == NULL) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlBrowserAddName: before browser initialized.\n" ));
goto Cleanup;
}
//
// If the domain has been renamed,
// delete any old names lying around.
//
if ( DomainInfo->DomFlags & DOM_RENAMED_1B_NAME ) {
NlBrowserDelName( DomainInfo );
}
//
// Add the <domain>0x1B name.
//
// This is the name NetGetDcName uses to identify the PDC.
//
// Do this for each transport separately and log any error
// indicating which transport failed.
//
if ( DomainInfo->DomRole == RolePrimary ) {
PLIST_ENTRY ListEntry;
PNL_TRANSPORT TransportEntry;
NTSTATUS Status = STATUS_SUCCESS;
//
// Capture the domain name for event log output.
// If we can't capture (i.e. no memory), we simply
// won't output below.
//
EnterCriticalSection( &NlGlobalDomainCritSect );
MsgStrings[0] = NetpAllocWStrFromWStr( DomainInfo->DomUnicodeDomainName );
LeaveCriticalSection( &NlGlobalDomainCritSect );
EnterCriticalSection( &NlGlobalTransportCritSect );
for ( ListEntry = NlGlobalTransportList.Flink ;
ListEntry != &NlGlobalTransportList ;
ListEntry = ListEntry->Flink) {
TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
//
// Skip deleted transports.
//
if ( !TransportEntry->TransportEnabled ) {
continue;
}
AtLeastOneTransportEnabled = TRUE;
Status = NlBrowserAddDelName( DomainInfo,
TRUE,
PrimaryDomainBrowser,
TransportEntry->TransportName,
&DomainInfo->DomUnicodeDomainNameString );
if ( NT_SUCCESS(Status) ) {
NameAdded = TRUE;
NlPrintDom(( NL_MISC, DomainInfo,
"Added the 0x1B name on transport %ws\n",
TransportEntry->TransportName ));
//
// Output the event log indicating the name of the failed transport
//
} else if ( MsgStrings[0] != NULL ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"Failed to add the 0x1B name on transport %ws\n",
TransportEntry->TransportName ));
MsgStrings[1] = TransportEntry->TransportName;
MsgStrings[2] = (LPWSTR) LongToPtr( Status );
NlpWriteEventlog(
NELOG_NetlogonAddNameFailure,
EVENTLOG_ERROR_TYPE,
(LPBYTE)&Status,
sizeof(Status),
MsgStrings,
3 | NETP_LAST_MESSAGE_IS_NTSTATUS );
}
}
LeaveCriticalSection( &NlGlobalTransportCritSect );
//
// Indicate that the name was added (at least on one transport)
//
if ( NameAdded ) {
EnterCriticalSection( &NlGlobalDomainCritSect );
DomainInfo->DomFlags |= DOM_ADDED_1B_NAME;
LeaveCriticalSection( &NlGlobalDomainCritSect );
}
if ( !AtLeastOneTransportEnabled ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"Can't add the 0x1B name because all transports are disabled\n" ));
}
}
Cleanup:
if ( MsgStrings[0] != NULL ) {
NetApiBufferFree( MsgStrings[0] );
}
}
VOID
NlBrowserDelName(
IN PDOMAIN_INFO DomainInfo
)
/*++
Routine Description:
Delete the Domain<1B> name. This is the name NetGetDcName uses to identify
the PDC.
Arguments:
DomainInfo - Hosted domain the name is to be deleted for.
Return Value:
Success (Not used)
--*/
{
NTSTATUS Status;
if ( NlGlobalMailslotDesc == NULL) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlBrowserDelName: before browser initialized.\n" ));
return;
}
//
// Delete the <domain>0x1B name.
//
EnterCriticalSection(&NlGlobalDomainCritSect);
if ( NlGlobalMailslotDesc->BrowserHandle != NULL &&
(DomainInfo->DomFlags & (DOM_ADDED_1B_NAME|DOM_RENAMED_1B_NAME)) != 0 ) {
LeaveCriticalSection(&NlGlobalDomainCritSect);
Status = NlBrowserAddDelName( DomainInfo,
FALSE,
PrimaryDomainBrowser,
NULL, // Delete on all transports
NULL ); // Delete all such names to handle the rename case
if (! NT_SUCCESS(Status)) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"Can't remove the 0x1B name: 0x%lx\n",
Status));
}
EnterCriticalSection(&NlGlobalDomainCritSect);
DomainInfo->DomFlags &= ~(DOM_ADDED_1B_NAME|DOM_RENAMED_1B_NAME);
}
LeaveCriticalSection(&NlGlobalDomainCritSect);
return;
}
NET_API_STATUS
NlBrowserFixAllNames(
IN PDOMAIN_INFO DomainInfo,
IN PVOID Context
)
/*++
Routine Description:
Scavenge the DomainName<1B> name.
Arguments:
DomainInfo - The domain being scavenged.
Context - Not Used
Return Value:
Success (not used).
--*/
{
//
// Ensure our Domain<1B> name is registered.
//
if ( NlGlobalTerminate ) {
return NERR_Success;
}
if ( DomainInfo->DomRole == RolePrimary ) {
NlBrowserAddName( DomainInfo );
} else {
NlBrowserDelName( DomainInfo );
}
return NERR_Success;
UNREFERENCED_PARAMETER( Context );
}
ULONG
NlServerType(
IN DWORD Role
)
/*++
Routine Description:
Determines server type, that is used to set in service table.
Arguments:
Role - Role to be translated
Return Value:
SV_TYPE_DOMAIN_CTRL if role is primary domain controller
SV_TYPE_DOMAIN_BAKCTRL if backup
0 if none of the above
--*/
{
switch (Role) {
case RolePrimary:
return SV_TYPE_DOMAIN_CTRL;
case RoleBackup:
return SV_TYPE_DOMAIN_BAKCTRL;
default:
return 0;
}
}
VOID
NlBrowserSyncHostedDomains(
VOID
)
/*++
Routine Description:
Tell the browser and SMB server to delete any hosted domains it has
that we don't have.
Arguments:
None.
Return Value:
None.
--*/
{
NET_API_STATUS NetStatus;
LPWSTR HostedDomainName;
LPWSTR HostedComputerName;
DWORD RoleBits;
PBROWSER_EMULATED_DOMAIN Domains;
DWORD EntriesRead;
DWORD TotalEntries;
DWORD i;
PSERVER_TRANSPORT_INFO_1 TransportInfo1;
#ifdef notdef
//
// Enumerate the Hosted domains.
// ?? Don't call this function. This function requires the browser be
// running. Rather, invent an Ioctl to the bowser to enumerate domains
// and use that ioctl here.
//
// ?? Note: Role no longer comes back from this API. Role in the browser
// is now maintained on a per "network" basis.
NetStatus = I_BrowserQueryEmulatedDomains(
NULL,
&Domains,
&EntriesRead );
if ( NetStatus != NERR_Success ) {
NlPrint((NL_CRITICAL,"NlBrowserSyncHostedDomains: Couldn't I_BrowserQueryEmulatedDomains %ld 0x%lx.\n",
NetStatus, NetStatus ));
//
// Handle each enumerated domain
//
} else if ( EntriesRead != 0 ) {
for ( i=0 ; i<EntriesRead; i++ ) {
PDOMAIN_INFO DomainInfo;
//
// If we know the specified domain,
// all is well.
//
DomainInfo = NlFindNetbiosDomain( Domains[i].DomainName, FALSE );
if ( DomainInfo != NULL ) {
//
// Ensure the hosted server name is identical.
//
if ( NlNameCompare( Domains[i].EmulatedServerName,
DomainInfo->DomUnicodeComputerNameString.Buffer,
NAMETYPE_COMPUTER) != 0 ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlBrowserSyncHostedDomains: hosted computer name missmatch: %ws %ws.\n",
Domains[i].EmulatedServerName,
DomainInfo->DomUnicodeComputerNameString.Buffer ));
// Tell the browser the name we have by deleting and re-adding
NlBrowserUpdate( DomainInfo, RoleInvalid );
NlBrowserUpdate( DomainInfo, DomainInfo->DomRole );
}
NlDereferenceDomain( DomainInfo );
//
// If we don't have the specified domain,
// delete it from the browser.
//
} else {
NlPrint((NL_CRITICAL,"%ws: NlBrowserSyncHostedDomains: Browser had an hosted domain we didn't (deleting)\n",
Domains[i].DomainName ));
// ?? Don't call this function. This function requires the browser be
// running. Rather, invent an Ioctl to the bowser to enumerate domains
// and use that ioctl here.
//
NetStatus = I_BrowserSetNetlogonState(
NULL,
Domains[i].DomainName,
NULL,
0 );
if ( NetStatus != NERR_Success ) {
NlPrint((NL_CRITICAL,"%ws: NlBrowserSyncHostedDomains: Couldn't I_BrowserSetNetlogonState %ld 0x%lx.\n",
Domains[i].DomainName,
NetStatus, NetStatus ));
// This isn't fatal
}
}
}
NetApiBufferFree( Domains );
}
#endif // notdef
//
// Enumerate the transports supported by the server.
//
NetStatus = NetServerTransportEnum(
NULL, // local
1, // level 1
(LPBYTE *) &TransportInfo1,
0xFFFFFFFF, // PrefMaxLength
&EntriesRead,
&TotalEntries,
NULL ); // No resume handle
if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
NlPrint(( NL_CRITICAL,
"NlBrowserSyncEnulatedDomains: Cannot NetServerTransportEnum %ld\n",
NetStatus ));
//
// Handle each enumerated transport.
} else if ( EntriesRead != 0 ) {
//
// Weed out duplicates.
//
// It'd be really inefficient to process duplicate entries. Especially,
// in the cases where corrective action needed to be taken.
//
for ( i=0; i<EntriesRead; i++ ) {
DWORD j;
for ( j=i+1; j<EntriesRead; j++ ) {
if ( TransportInfo1[i].svti1_domain != NULL &&
TransportInfo1[i].svti1_transportaddresslength ==
TransportInfo1[j].svti1_transportaddresslength &&
RtlEqualMemory( TransportInfo1[i].svti1_transportaddress,
TransportInfo1[j].svti1_transportaddress,
TransportInfo1[i].svti1_transportaddresslength ) &&
NlNameCompare( TransportInfo1[i].svti1_domain,
TransportInfo1[j].svti1_domain,
NAMETYPE_DOMAIN ) == 0 ) {
#ifdef notdef
NlPrint((NL_CRITICAL,
"%ws: NlBrowserSyncHostedDomains: Duplicate SMB server entry ignored.\n",
TransportInfo1[i].svti1_domain ));
#endif // notdef
TransportInfo1[j].svti1_domain = NULL;
}
}
}
//
// Process each enumerated domain.
//
for ( i=0 ; i<EntriesRead; i++ ) {
PDOMAIN_INFO DomainInfo;
WCHAR UnicodeComputerName[CNLEN+1];
ULONG UnicodeComputerNameSize;
NTSTATUS TempStatus;
//
// ignore duplicates
//
if ( TransportInfo1[i].svti1_domain == NULL ) {
continue;
}
#ifdef notdef
NlPrint((NL_CRITICAL,
"%ws: NlBrowserSyncHostedDomains: processing SMB entry.\n",
TransportInfo1[i].svti1_domain ));
#endif // notdef
//
// If we know the specified domain,
// all is well.
//
DomainInfo = NlFindNetbiosDomain( TransportInfo1[i].svti1_domain, FALSE );
if ( DomainInfo != NULL ) {
//
// Ensure the Hosted server name is identical.
//
if ( TransportInfo1[i].svti1_transportaddresslength !=
DomainInfo->DomOemComputerNameLength ||
!RtlEqualMemory( TransportInfo1[i].svti1_transportaddress,
DomainInfo->DomOemComputerName,
DomainInfo->DomOemComputerNameLength ) ) {
TempStatus = RtlOemToUnicodeN(
UnicodeComputerName,
CNLEN*sizeof(WCHAR),
&UnicodeComputerNameSize,
TransportInfo1[i].svti1_transportaddress,
TransportInfo1[i].svti1_transportaddresslength );
if ( NT_SUCCESS(TempStatus) ) {
UnicodeComputerName[UnicodeComputerNameSize/sizeof(WCHAR)] = L'\0';
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlBrowserSyncHostedDomains: hosted computer name mismatch (SMB server): %ws %ws.\n",
UnicodeComputerName,
DomainInfo->DomUnicodeComputerNameString.Buffer ));
//
// Tell the SMB server the name we have by deleting and re-adding
//
NetStatus = NetServerComputerNameDel(
NULL,
UnicodeComputerName );
if ( NetStatus != NERR_Success ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlBrowserSyncHostedDomains: can't NetServerComputerNameDel: %ws.\n",
UnicodeComputerName ));
// This isn't fatal
}
NetStatus = NlServerComputerNameAdd(
DomainInfo->DomUnicodeDomainName,
DomainInfo->DomUnicodeComputerNameString.Buffer );
if ( NetStatus != NERR_Success ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlBrowserSyncHostedDomains: can't NetServerComputerNameAdd: %ws.\n",
DomainInfo->DomUnicodeComputerNameString.Buffer ));
// This isn't fatal
}
}
}
NlDereferenceDomain( DomainInfo );
//
// If we don't have the specified domain,
// delete it from the SMB server.
//
} else {
NlPrint((NL_CRITICAL,"%ws: NlBrowserSyncHostedDomains: SMB server had a hosted domain we didn't (deleting)\n",
TransportInfo1[i].svti1_domain ));
TempStatus = RtlOemToUnicodeN(
UnicodeComputerName,
CNLEN*sizeof(WCHAR),
&UnicodeComputerNameSize,
TransportInfo1[i].svti1_transportaddress,
TransportInfo1[i].svti1_transportaddresslength );
if ( !NT_SUCCESS(TempStatus) ) {
NlPrint((NL_CRITICAL,
"%ws: NlBrowserSyncHostedDomains: can't RtlOemToUnicode: %lx.\n",
TransportInfo1[i].svti1_domain,
TempStatus ));
// This isn't fatal
} else {
UnicodeComputerName[UnicodeComputerNameSize/sizeof(WCHAR)] = L'\0';
// When we really do hosted domains,
// we have to work out a mechanism where the SMB server and
// Netlogon has the same set of hosted domains.
//
// I ran into a case where netlogon had processed a rename
// of the domain and the SMB server hadn't. In that case,
// the code below would delete the primary domain of the SMB
// server.
//
#ifdef notdef
NetStatus = NetServerComputerNameDel(
NULL,
UnicodeComputerName );
if ( NetStatus != NERR_Success ) {
NlPrint((NL_CRITICAL,
"%ws: NlBrowserSyncHostedDomains: can't NetServerComputerNameDel: %ws.\n",
TransportInfo1[i].svti1_domain,
UnicodeComputerName ));
// This isn't fatal
}
#endif // notdef
}
}
}
(VOID) NetApiBufferFree( TransportInfo1 );
}
return;
}
VOID
NlBrowserUpdate(
IN PDOMAIN_INFO DomainInfo,
IN DWORD Role
)
/*++
Routine Description:
Tell the browser and SMB server about our new role.
Arguments:
DomainInfo - Hosted domain the name is to be deleted for.
Role - Our new Role.
RoleInvalid implies the domain is being deleted.
Return Value:
None.
--*/
{
NET_API_STATUS NetStatus;
DWORD BrowserRole;
//
// Initialization.
//
switch (Role) {
case RolePrimary:
BrowserRole = BROWSER_ROLE_PDC ; break;
case RoleBackup:
BrowserRole = BROWSER_ROLE_BDC ; break;
default:
// Default to telling the browser to delete the Hosted domain.
BrowserRole = 0 ; break;
}
//
// Tell the server what role to announce
//
if ( DomainInfo->DomFlags & DOM_PRIMARY_DOMAIN ) {
BOOL Ok;
//
// Since the service controller doesn't have a mechanism to set some
// bits and turn others off, turn all of the bits off then set the right
// ones.
//
Ok = I_ScSetServiceBits( NlGlobalServiceHandle,
SV_TYPE_DOMAIN_CTRL |
SV_TYPE_DOMAIN_BAKCTRL, // Bits of interest
FALSE, // Set bits off
FALSE, // Don't force immediate announcement
NULL); // All transports
if ( !Ok ) {
NetStatus = GetLastError();
NlPrint((NL_CRITICAL,"Couldn't I_ScSetServiceBits off %ld 0x%lx.\n",
NetStatus, NetStatus ));
// This isn't fatal
}
//
// For the primary domain,
// Tell the service controller and let it tell the server service after it
// merges the bits from the other services.
//
if ( BrowserRole != 0 ) {
Ok = I_ScSetServiceBits( NlGlobalServiceHandle,
NlServerType(Role),
TRUE, // Set bits on
TRUE, // Force immediate announcement
NULL); // All transports
}
if ( !Ok ) {
NetStatus = GetLastError();
NlPrint((NL_CRITICAL,"Couldn't I_ScSetServiceBits %ld 0x%lx.\n",
NetStatus, NetStatus ));
// This isn't fatal
}
} else {
//
// For domains that aren't the primary domain,
// tell the Lanman server directly
// (since the service controller doesn't care about those doamins).
//
NetStatus = I_NetServerSetServiceBitsEx(
NULL, // Local server service
DomainInfo->DomUnicodeComputerNameString.Buffer,
NULL, // All transports
SV_TYPE_DOMAIN_CTRL |
SV_TYPE_DOMAIN_BAKCTRL, // Bits of interest
NlServerType(Role), // New Role
TRUE ); // Update immediately
if ( NetStatus != NERR_Success ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlBrowserUpdate: Couldn't I_NetServerSetServiceBitsEx %ld 0x%lx.\n",
NetStatus, NetStatus ));
// This isn't fatal
}
}
#ifdef notdef
//
// Tell the browser our role has changed.
//
// Avoid deleting the primary domain
if ( BrowserRole != 0 || !IsPrimaryDomain(DomainInfo) ) {
// ?? Don't call this function. This function requires the browser be
// running. Rather, invent an Ioctl to the bowser to enumerate domains
// and use that ioctl here.
//
// This function serves two purposes: Adding/deleting the hosted domain
// in the browser and setting the role. The first might very well be
// a function of the bowser. The later is naturally a function of
// the browser service (but should be indirected through the bowser to
// avoid domain rename issues).
//
// When we really do multiple hosted domains, create an emulated domain
// via one IOCTL to the bowser. Change it's role via another ioctl
// to the bowser. Both calls will result in notifications to the browser
// service via normal PNP notifications.
//
// One might even think that the ioctl to change its role is the one
// below that adds the 1B name. That is, if the 1B name is added, then
// this machine is the PDC. If not, then it is not the PDC.
//
// In the mean time, hack the interface to the browser service indicating
// that it should NEVER create an emulated domain based on this call.
// Otherwise, on a domain rename, we may end up creating an emulated domain
// because our notification and the browser's are asynchronous.
//
// Actually, I've updated the bowser to do the 1B trick mentioned above.
// So this code merely has to do the right thing for the hosted domain.
//
NetStatus = I_BrowserSetNetlogonState(
NULL,
DomainInfo->DomUnicodeDomainName,
DomainInfo->DomUnicodeComputerNameString.Buffer,
BrowserRole | BROWSER_ROLE_AVOID_CREATING_DOMAIN );
if ( NetStatus != NERR_Success ) {
if ( BrowserRole != 0 || NetStatus != ERROR_NO_SUCH_DOMAIN ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlBrowserUpdate: Couldn't I_BrowserSetNetlogonState %ld 0x%lx.\n",
NetStatus, NetStatus ));
}
// This isn't fatal
}
}
#endif // notdef
//
// Register or deregister the Domain<1B> name depending on the new role
//
if ( Role == RolePrimary ) {
NlBrowserAddName( DomainInfo );
} else {
NlBrowserDelName( DomainInfo );
}
//
// Tell the SMB server to delete a removed Hosted domain.
//
if ( Role == RoleInvalid && !IsPrimaryDomain(DomainInfo) ) {
NetStatus = NetServerComputerNameDel(
NULL,
DomainInfo->DomUnicodeComputerNameString.Buffer );
if ( NetStatus != NERR_Success ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlBrowserUpdate: Couldn't NetServerComputerNameDel %ld 0x%lx.\n",
NetStatus, NetStatus ));
// This isn't fatal
}
}
return;
}
BOOL
NlBrowserOpen(
VOID
)
/*++
Routine Description:
This routine opens the NT LAN Man Datagram Receiver driver and prepares
for reading mailslot messages from it.
Arguments:
None.
Return Value:
TRUE -- iff initialization is successful.
if false, NlExit will already have been called.
--*/
{
NTSTATUS Status;
BOOL ReturnValue;
BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
(max(CNLEN, DNLEN) + 1) * sizeof(WCHAR)];
PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET) Buffer;
//
// Allocate the mailslot descriptor for this mailslot
//
NlGlobalMailslotDesc = NetpMemoryAllocate( sizeof(NETLOGON_MAILSLOT_DESC) );
if ( NlGlobalMailslotDesc == NULL ) {
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
return FALSE;
}
RtlZeroMemory( NlGlobalMailslotDesc, sizeof(NETLOGON_MAILSLOT_DESC) );
NlGlobalMailslotDesc->CurrentMessage =
ROUND_UP_POINTER( NlGlobalMailslotDesc->Message1, ALIGN_WORST);
//
// Open the browser device.
//
Status = NlBrowserOpenDriver( &NlGlobalMailslotDesc->BrowserHandle );
if (! NT_SUCCESS(Status)) {
NlPrint((NL_CRITICAL,
"NtOpenFile browser driver failed: 0x%lx\n",
Status));
ReturnValue = FALSE;
goto Cleanup;
}
//
// Create a completion event
//
NlGlobalMailslotHandle =
NlGlobalMailslotDesc->BrowserReadEvent = NlBrowserCreateEvent();
if ( NlGlobalMailslotDesc->BrowserReadEvent == NULL ) {
Status = NetpApiStatusToNtStatus( GetLastError() );
ReturnValue = FALSE;
goto Cleanup;
}
//
// Set the maximum number of messages to be queued
//
RequestPacket->TransportName.Length = 0;
RequestPacket->TransportName.Buffer = NULL;
RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
RequestPacket->Parameters.NetlogonMailslotEnable.MaxMessageCount =
NlGlobalMemberWorkstation ?
1 :
NlGlobalParameters.MaximumMailslotMessages;
Status = NlBrowserDeviceIoControl(
NlGlobalMailslotDesc->BrowserHandle,
IOCTL_LMDR_NETLOGON_MAILSLOT_ENABLE,
RequestPacket,
sizeof(LMDR_REQUEST_PACKET),
NULL,
0 );
if (! NT_SUCCESS(Status)) {
NlPrint((NL_CRITICAL,"Can't set browser max message count: 0x%lx\n",
Status));
ReturnValue = FALSE;
goto Cleanup;
}
ReturnValue = TRUE;
Cleanup:
if ( !ReturnValue ) {
NET_API_STATUS NetStatus = NetpNtStatusToApiStatus(Status);
NlExit( NELOG_NetlogonBrowserDriver, NetStatus, LogErrorAndNtStatus, NULL);
NlBrowserClose();
}
return ReturnValue;
}
VOID
NlBrowserClose(
VOID
)
/*++
Routine Description:
This routine cleans up after a NlBrowserInitialize()
Arguments:
None.
Return Value:
None.
--*/
{
IO_STATUS_BLOCK IoSb;
NTSTATUS Status;
BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
(max(CNLEN, DNLEN) + 1) * sizeof(WCHAR)];
PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET) Buffer;
if ( NlGlobalMailslotDesc == NULL) {
return;
}
//
// If we've opened the browser, clean up.
//
if ( NlGlobalMailslotDesc->BrowserHandle != NULL ) {
//
// Tell the browser to stop queueing messages
//
RequestPacket->TransportName.Length = 0;
RequestPacket->TransportName.Buffer = NULL;
RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
RequestPacket->Parameters.NetlogonMailslotEnable.MaxMessageCount = 0;
Status = NlBrowserDeviceIoControl(
NlGlobalMailslotDesc->BrowserHandle,
IOCTL_LMDR_NETLOGON_MAILSLOT_ENABLE,
RequestPacket,
sizeof(LMDR_REQUEST_PACKET),
NULL,
0 );
if (! NT_SUCCESS(Status)) {
NlPrint((NL_CRITICAL,"Can't reset browser max message count: 0x%lx\n",
Status));
}
//
// Cancel the I/O operations outstanding on the browser.
//
NtCancelIoFile(NlGlobalMailslotDesc->BrowserHandle, &IoSb);
//
// Close the handle to the browser
//
NtClose(NlGlobalMailslotDesc->BrowserHandle);
NlGlobalMailslotDesc->BrowserHandle = NULL;
}
//
// Close the global browser read event
//
if ( NlGlobalMailslotDesc->BrowserReadEvent != NULL ) {
NlBrowserCloseEvent(NlGlobalMailslotDesc->BrowserReadEvent);
}
NlGlobalMailslotHandle = NULL;
//
// Free the descriptor describing the browser
//
NetpMemoryFree( NlGlobalMailslotDesc );
NlGlobalMailslotDesc = NULL;
}
NTSTATUS
NlpWriteMailslot(
IN LPWSTR MailslotName,
IN LPVOID Buffer,
IN DWORD BufferSize
)
/*++
Routine Description:
Write a message to a named mailslot
Arguments:
MailslotName - Unicode name of the mailslot to write to.
Buffer - Data to write to the mailslot.
BufferSize - Number of bytes to write to the mailslot.
Return Value:
NT status code for the operation
--*/
{
NTSTATUS Status;
NET_API_STATUS NetStatus;
//
// Write the mailslot message.
//
NetStatus = NetpLogonWriteMailslot( MailslotName, Buffer, BufferSize );
if ( NetStatus != NERR_Success ) {
Status = NetpApiStatusToNtStatus( NetStatus );
NlPrint((NL_CRITICAL, "NetpLogonWriteMailslot failed %lx\n", Status));
return Status;
}
#if NETLOGONDBG
NlPrint(( NL_MAILSLOT,
"Sent '%s' message to %ws on all transports.\n",
NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)Buffer)->Opcode),
MailslotName));
NlpDumpBuffer( NL_MAILSLOT_TEXT, Buffer, BufferSize );
#endif // NETLOGONDBG
return STATUS_SUCCESS;
}
NTSTATUS
NlFlushNetbiosCacheName(
IN LPCWSTR NetbiosDomainName,
IN CHAR Extention,
IN PNL_TRANSPORT Transport
)
/*++
Routine Description:
This routine flushes the specified name from the Netbios
remote cache table.
Arguments:
NetbiosDomainName - The name to be flushed.
Extention - the type of the name (extention added as the
16th character of the name to flush): 0x00, 0x1C, 0x1B, etc.
Transport - The transport (device) on which the name is to
be flushed.
Return Value:
STATUS_SUCCESS: The name has been successfully flushed
STATUS_RESOURCE_NAME_NOT_FOUND: The name was not found in the cache
Otherwise, an error returned by NtCreateFile or NtDeviceIoControlFile
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
CHAR NameToBeFlushed[NETBIOS_NAMESIZE];
//
// First open the Netbios device if it hasn't been done already
//
EnterCriticalSection( &NlGlobalTransportCritSect );
if ( Transport->DeviceHandle == INVALID_HANDLE_VALUE ) {
OBJECT_ATTRIBUTES Attributes;
UNICODE_STRING UnicodeString;
HANDLE LocalHandle;
RtlInitUnicodeString( &UnicodeString, Transport->TransportName );
InitializeObjectAttributes( &Attributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
NtStatus = NtCreateFile( &LocalHandle,
MAXIMUM_ALLOWED,
&Attributes,
&IoStatusBlock,
NULL, // allocation size
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL, // no EAs
0 );
if( !NT_SUCCESS(NtStatus) ) {
LeaveCriticalSection( &NlGlobalTransportCritSect );
NlPrint(( NL_CRITICAL, "NlFlushNetbiosCacheName: NtCreateFile failed 0x%lx\n",
NtStatus ));
return NtStatus;
}
Transport->DeviceHandle = LocalHandle;
}
LeaveCriticalSection( &NlGlobalTransportCritSect );
//
// Now form the name to flush
//
// Convert to upper case, blank pad to the right
// and put the appropriate Extension at the end
//
RtlFillMemory( &NameToBeFlushed, NETBIOS_NAMESIZE, ' ' );
NtStatus = RtlUpcaseUnicodeToOemN( NameToBeFlushed,
NETBIOS_NAMESIZE - 1, // Maximum for resulting string size
NULL, // Don't care about the resulting string size
(LPWSTR)NetbiosDomainName,
wcslen(NetbiosDomainName)*sizeof(WCHAR) );
if ( !NT_SUCCESS(NtStatus) ) {
NlPrint(( NL_CRITICAL, "NlFlushNetbiosCacheName: RtlUpcaseUnicodeToOemN failed 0x%lx\n",
NtStatus ));
return NtStatus;
}
//
// Set the appropriate extention
//
NameToBeFlushed[NETBIOS_NAMESIZE-1] = Extention;
//
// Finally flush the name from the cache
//
NtStatus = NtDeviceIoControlFile(
Transport->DeviceHandle, // Handle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&IoStatusBlock, // IoStatusBlock
IOCTL_NETBT_REMOVE_FROM_REMOTE_TABLE, // IoControlCode
NameToBeFlushed, // InputBuffer
sizeof(NameToBeFlushed), // InputBufferSize
NULL, // OutputBuffer
0 ); // OutputBufferSize
//
// STATUS_RESOURCE_NAME_NOT_FOUND just means that the name was not in the cache
//
if ( !NT_SUCCESS(NtStatus) && NtStatus != STATUS_RESOURCE_NAME_NOT_FOUND ) {
NlPrint(( NL_CRITICAL, "NlFlushNetbiosCacheName: NtDeviceIoControlFile failed 0x%lx\n",
NtStatus ));
}
return NtStatus;
}
NTSTATUS
NlBrowserSendDatagram(
IN PVOID ContextDomainInfo,
IN ULONG IpAddress,
IN LPWSTR UnicodeDestinationName,
IN DGRECEIVER_NAME_TYPE NameType,
IN LPWSTR TransportName,
IN LPSTR OemMailslotName,
IN PVOID Buffer,
IN ULONG BufferSize,
IN OUT PBOOL FlushNameOnOneIpTransport OPTIONAL
)
/*++
Routine Description:
Send the specified mailslot message to the specified mailslot on the
specified server on the specified transport..
Arguments:
DomainInfo - Hosted domain sending the datagram
IpAddress - IpAddress of the machine to send the message to.
If zero, UnicodeDestinationName must be specified.
If ALL_IP_TRANSPORTS, UnicodeDestination must be specified but the datagram
will only be sent on IP transports.
UnicodeDestinationName -- Name of the server to send to.
NameType -- Type of name represented by UnicodeDestinationName.
TransportName -- Name of the transport to send on.
Use NULL to send on all transports.
OemMailslotName -- Name of the mailslot to send to.
Buffer -- Specifies a pointer to the mailslot message to send.
BufferSize -- Size in bytes of the mailslot message
FlushNameOnOneIpTransport -- Used only if we send on all transports (i.e.
TransportName is NULL), otherwise ignored. If TRUE, the name specified
by UnicodeDestinationName will be flushed on one of the available IP
enabled transports prior to sending the datagram. On return, set to
FALSE if the name has been successfully flushed or the name was not
found in the cache.
Return Value:
Status of the operation.
STATUS_NETWORK_UNREACHABLE: Cannot write to network.
--*/
{
PLMDR_REQUEST_PACKET RequestPacket = NULL;
PDOMAIN_INFO DomainInfo = (PDOMAIN_INFO) ContextDomainInfo;
DWORD OemMailslotNameSize;
DWORD TransportNameSize;
DWORD DestinationNameSize;
NTSTATUS Status;
LPBYTE Where;
//
// If the transport isn't specified,
// send on all transports.
//
if ( TransportName == NULL ) {
ULONG i;
PLIST_ENTRY ListEntry;
NTSTATUS SavedStatus = STATUS_NETWORK_UNREACHABLE;
ULONG TransportCount = 0;
ULONG BadNetPathCount = 0;
//
// Send on all transports.
//
EnterCriticalSection( &NlGlobalTransportCritSect );
for ( ListEntry = NlGlobalTransportList.Flink ;
ListEntry != &NlGlobalTransportList ;
ListEntry = ListEntry->Flink) {
PNL_TRANSPORT TransportEntry;
TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
//
// Skip deleted transports.
//
if ( !TransportEntry->TransportEnabled ) {
continue;
}
//
// Skip direct host IPX transport unless sending to a particular
// machine.
//
if ( TransportEntry->DirectHostIpx &&
NameType != ComputerName ) {
continue;
}
//
// Skip non-IP transports if sending to an IP address.
//
if ( IpAddress != 0 &&
TransportEntry->IpAddress == 0 ) {
continue;
}
//
// Leave the critical section before sending the datagram
// because NetBt now doesn't return from the datagram send
// until after the name lookup completes. So, it can take
// a considerable amount of time for the datagram send to
// return to us.
//
LeaveCriticalSection( &NlGlobalTransportCritSect );
//
// If this is IP transport, flush the name if requested
//
if ( FlushNameOnOneIpTransport != NULL &&
*FlushNameOnOneIpTransport &&
TransportEntry->IsIpTransport ) {
NTSTATUS TmpStatus;
CHAR Extention;
if ( NameType == ComputerName ) {
Extention = 0x00;
} else if ( NameType == DomainName ) {
Extention = 0x1C;
} else if ( NameType == PrimaryDomainBrowser ) {
Extention = 0x1B;
} else {
NlAssert( !"[NETLOGON] Unexpected name type passed to NlBrowserSendDatagram" );
}
TmpStatus = NlFlushNetbiosCacheName( UnicodeDestinationName,
Extention,
TransportEntry );
//
// If we successfully flushed the name or the name is not in the cache,
// indicate that the name has been flushed
//
if ( NT_SUCCESS(TmpStatus) || TmpStatus == STATUS_RESOURCE_NAME_NOT_FOUND ) {
*FlushNameOnOneIpTransport = FALSE;
}
}
Status = NlBrowserSendDatagram(
DomainInfo,
IpAddress,
UnicodeDestinationName,
NameType,
TransportEntry->TransportName,
OemMailslotName,
Buffer,
BufferSize,
FlushNameOnOneIpTransport );
EnterCriticalSection( &NlGlobalTransportCritSect );
//
// Since a TransportEntry is never removed from the global
// transport list (it can only become marked as disabled
// during the time when we had the crit sect released),
// we should be able to follow its link to the next entry in
// the global list on the next iteration of the loop. The
// only problem can occur when the service was said to terminate
// and NlTransportClose was called to free up the global list.
// In this case NlGlobalTerminate is set to TRUE so we can
// successfully return from this routine.
//
if ( NlGlobalTerminate ) {
LeaveCriticalSection( &NlGlobalTransportCritSect );
Status = STATUS_SUCCESS;
goto Cleanup;
}
TransportCount ++;
if ( NT_SUCCESS(Status) ) {
// If any transport works, we've been successful
SavedStatus = STATUS_SUCCESS;
} else if ( Status == STATUS_BAD_NETWORK_PATH ) {
// Count the number of transports that couldn't resolve the name
BadNetPathCount ++;
} else {
// Remember the real reason for the failure instead of the default failure status
// Remember only the first failure.
if ( SavedStatus == STATUS_NETWORK_UNREACHABLE ) {
SavedStatus = Status;
}
}
}
LeaveCriticalSection( &NlGlobalTransportCritSect );
//
// If we're returning the default status,
// and at least one transport couldn't resolved the name,
// and all transports couldn't resolve the name,
// tell the caller we couldn't resolve the name.
//
if ( SavedStatus == STATUS_NETWORK_UNREACHABLE &&
BadNetPathCount > 0 &&
TransportCount == BadNetPathCount ) {
SavedStatus = STATUS_BAD_NETWORK_PATH;
}
//
// If we have no transports available,
// tell the caller we couldn't resolve the name
//
if ( TransportCount == 0 ) {
NlPrint(( NL_CRITICAL, "NlBrowserSendDatagram: No transports available\n" ));
SavedStatus = STATUS_BAD_NETWORK_PATH;
}
return SavedStatus;
}
//
// Allocate a request packet.
//
OemMailslotNameSize = strlen(OemMailslotName) + 1;
TransportNameSize = (wcslen(TransportName) + 1) * sizeof(WCHAR);
if ( UnicodeDestinationName == NULL ) {
return STATUS_INTERNAL_ERROR;
}
DestinationNameSize = wcslen(UnicodeDestinationName) * sizeof(WCHAR);
RequestPacket = NetpMemoryAllocate(
sizeof(LMDR_REQUEST_PACKET) +
TransportNameSize +
OemMailslotNameSize +
DestinationNameSize + sizeof(WCHAR) +
DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR) +
sizeof(WCHAR)) ; // For alignment
if (RequestPacket == NULL) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Fill in the Request Packet.
//
RequestPacket->Type = Datagram;
RequestPacket->Parameters.SendDatagram.DestinationNameType = NameType;
//
// Fill in the name of the machine to send the mailslot message to.
//
RequestPacket->Parameters.SendDatagram.NameLength = DestinationNameSize;
Where = (LPBYTE) RequestPacket->Parameters.SendDatagram.Name;
RtlCopyMemory( Where, UnicodeDestinationName, DestinationNameSize );
Where += DestinationNameSize;
//
// Fill in the name of the mailslot to send to.
//
RequestPacket->Parameters.SendDatagram.MailslotNameLength =
OemMailslotNameSize;
strcpy( Where, OemMailslotName);
Where += OemMailslotNameSize;
Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
//
// Fill in the TransportName
//
wcscpy( (LPWSTR) Where, TransportName);
RtlInitUnicodeString( &RequestPacket->TransportName, (LPWSTR) Where );
Where += TransportNameSize;
//
// Copy the hosted domain name to the request packet.
//
wcscpy( (LPWSTR)Where,
DomainInfo->DomUnicodeDomainNameString.Buffer );
RtlInitUnicodeString( &RequestPacket->EmulatedDomainName,
(LPWSTR)Where );
Where += DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR);
//
// Send the request to the browser.
//
Status = NlBrowserDeviceIoControl(
NlGlobalMailslotDesc->BrowserHandle,
IOCTL_LMDR_WRITE_MAILSLOT,
RequestPacket,
(ULONG)(Where - (LPBYTE)RequestPacket),
Buffer,
BufferSize );
//
// Free locally used resources.
//
Cleanup:
if ( RequestPacket != NULL ) {
NetpMemoryFree( RequestPacket );
}
NlpDumpBuffer( NL_MAILSLOT_TEXT, Buffer, BufferSize );
// NlPrint(( NL_MAILSLOT, "Transport %ws 0x%lx\n", TransportName, Status ));
return Status;
}
NTSTATUS
NlBrowserSendDatagramA(
IN PDOMAIN_INFO DomainInfo,
IN ULONG IpAddress,
IN LPSTR OemServerName,
IN DGRECEIVER_NAME_TYPE NameType,
IN LPWSTR TransportName,
IN LPSTR OemMailslotName,
IN PVOID Buffer,
IN ULONG BufferSize
)
/*++
Routine Description:
Send the specified mailslot message to the specified mailslot on the
specified server on the specified transport..
Arguments:
DomainInfo - Hosted domain sending the datagram
IpAddress - IpAddress of the machine to send the message to.
If zero, OemServerName must be specified.
OemServerName -- Name of the server to send to.
NameType -- Type of name represented by OemServerName.
TransportName -- Name of the transport to send on.
Use NULL to send on all transports.
OemMailslotName -- Name of the mailslot to send to.
Buffer -- Specifies a pointer to the mailslot message to send.
BufferSize -- Size in bytes of the mailslot message
Return Value:
Status of the operation.
--*/
{
NET_API_STATUS NetStatus;
WCHAR UnicodeDestinationName[CNLEN+1];
//
// Convert DestinationName to unicode
//
NetStatus = NetpNCopyStrToWStr(
UnicodeDestinationName,
OemServerName,
CNLEN );
if ( NetStatus != NERR_Success ) {
return NetpApiStatusToNtStatus( NetStatus );
}
UnicodeDestinationName[CNLEN] = L'\0';
//
// Pass the request to the function taking unicode destination name.
//
return NlBrowserSendDatagram(
DomainInfo,
IpAddress,
UnicodeDestinationName,
NameType,
TransportName,
OemMailslotName,
Buffer,
BufferSize,
NULL ); // Don't flush Netbios cache
}
VOID
NlMailslotPostRead(
IN BOOLEAN IgnoreDuplicatesOfPreviousMessage
)
/*++
Routine Description:
Post a read on the mailslot if one isn't already posted.
Arguments:
IgnoreDuplicatesOfPreviousMessage - TRUE to indicate that the next
message read should be ignored if it is a duplicate of the previous
message.
Return Value:
TRUE -- iff successful.
--*/
{
NET_API_STATUS WinStatus;
ULONG LocalBytesRead;
//
// If a read is already pending,
// immediately return to caller.
//
if ( NlGlobalMailslotDesc->ReadPending ) {
return;
}
//
// Decide which buffer to read into.
//
// Switch back and forth so we always have the current buffer and the
// previous buffer.
//
if ( IgnoreDuplicatesOfPreviousMessage ) {
NlGlobalMailslotDesc->PreviousMessage = NlGlobalMailslotDesc->CurrentMessage;
if ( NlGlobalMailslotDesc->CurrentMessage >= NlGlobalMailslotDesc->Message2 ) {
NlGlobalMailslotDesc->CurrentMessage =
ROUND_UP_POINTER( NlGlobalMailslotDesc->Message1, ALIGN_WORST);
} else {
NlGlobalMailslotDesc->CurrentMessage =
ROUND_UP_POINTER( NlGlobalMailslotDesc->Message2, ALIGN_WORST);
}
//
// If duplicates of the previous message need not be ignored,
// indicate so.
// Don't bother switching the buffer pointers.
//
} else {
NlGlobalMailslotDesc->PreviousMessage = NULL;
}
//
// Post an overlapped read to the mailslot.
//
RtlZeroMemory( &NlGlobalMailslotDesc->Overlapped,
sizeof(NlGlobalMailslotDesc->Overlapped) );
NlGlobalMailslotDesc->Overlapped.hEvent = NlGlobalMailslotDesc->BrowserReadEvent;
if ( !DeviceIoControl(
NlGlobalMailslotDesc->BrowserHandle,
IOCTL_LMDR_NETLOGON_MAILSLOT_READ,
NULL,
0,
NlGlobalMailslotDesc->CurrentMessage,
MAILSLOT_MESSAGE_SIZE,
&LocalBytesRead,
&NlGlobalMailslotDesc->Overlapped )) {
WinStatus = GetLastError();
//
// On error, wait a second before returning. This ensures we don't
// consume the system in an infinite loop. We don't shutdown netlogon
// because the error might be a temporary low memory condition.
//
if( WinStatus != ERROR_IO_PENDING ) {
LPWSTR MsgStrings[1];
NlPrint((NL_CRITICAL,
"Error in reading mailslot message from browser"
". WinStatus = %ld\n",
WinStatus ));
MsgStrings[0] = (LPWSTR) ULongToPtr( WinStatus );
NlpWriteEventlog( NELOG_NetlogonFailedToReadMailslot,
EVENTLOG_WARNING_TYPE,
(LPBYTE)&WinStatus,
sizeof(WinStatus),
MsgStrings,
1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
Sleep( 1000 );
} else {
NlGlobalMailslotDesc->ReadPending = TRUE;
}
} else {
NlGlobalMailslotDesc->ReadPending = TRUE;
}
return;
}
BOOL
NlMailslotOverlappedResult(
OUT LPBYTE *Message,
OUT PULONG BytesRead,
OUT LPWSTR *TransportName,
OUT PNL_TRANSPORT *Transport,
OUT PSOCKADDR *ClientSockAddr,
OUT LPWSTR *DestinationName,
OUT PBOOLEAN IgnoreDuplicatesOfPreviousMessage,
OUT PNETLOGON_PNP_OPCODE NlPnpOpcode
)
/*++
Routine Description:
Get the overlapped result of a previous mailslot read.
Arguments:
Message - Returns a pointer to the buffer containing the message
BytesRead - Returns the number of bytes read into the buffer
TransportName - Returns a pointer to the name of the transport the message
was received on.
Transport - Returns a pointer to the Transport structure if this is a
mailslot message.
ClientSockAddr - Returns a pointer to the SockAddr of the client that
sent the message.
Returns NULL if transport isn't running IP.
DestinationName - Returns a pointer to the name of the server or domain
the message was sent to.
IgnoreDuplicatesOfPreviousMessage - Indicates that duplicates of the
previous message are to be ignored.
NpPnpOpcode - Returns the PNP opcode if this is a PNP operation.
Returns NlPnpMailslotMessage if this is a mailslot message.
Return Value:
TRUE -- iff successful.
--*/
{
NET_API_STATUS WinStatus;
ULONG LocalBytesRead;
PNETLOGON_MAILSLOT NetlogonMailslot;
//
// Default to not ignoring duplicate messages.
// (Only ignore duplicates if a message has been properly processed.)
*IgnoreDuplicatesOfPreviousMessage = FALSE;
//
// By default, assume a mailslot message is available.
*NlPnpOpcode = NlPnpMailslotMessage;
//
// Always post another read regardless of the success or failure of
// GetOverlappedResult.
// We don't know the failure mode of GetOverlappedResult, so we don't
// know in the failure case if we're discarding a mailslot message.
// But we do know that there is no read pending, so make sure we
// issue another one.
//
NlGlobalMailslotDesc->ReadPending = FALSE; // no read pending anymore
//
// Get the result of the last read
//
if( !GetOverlappedResult( NlGlobalMailslotDesc->BrowserHandle,
&NlGlobalMailslotDesc->Overlapped,
&LocalBytesRead,
TRUE) ) { // wait for the read to complete.
LPWSTR MsgStrings[1];
// On error, wait a second before returning. This ensures we don't
// consume the system in an infinite loop. We don't shutdown netlogon
// because the error might be a temporary low memory condition.
//
WinStatus = GetLastError();
NlPrint((NL_CRITICAL,
"Error in GetOverlappedResult on mailslot read"
". WinStatus = %ld\n",
WinStatus ));
MsgStrings[0] = (LPWSTR) ULongToPtr( WinStatus );
NlpWriteEventlog( NELOG_NetlogonFailedToReadMailslot,
EVENTLOG_WARNING_TYPE,
(LPBYTE)&WinStatus,
sizeof(WinStatus),
MsgStrings,
1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
Sleep( 1000 );
return FALSE;
}
//
// On success,
// Return the mailslot message to the caller.
NetlogonMailslot = (PNETLOGON_MAILSLOT) NlGlobalMailslotDesc->CurrentMessage;
//
// Return pointers into the buffer returned by the browser
//
*Message = &NlGlobalMailslotDesc->CurrentMessage[
NetlogonMailslot->MailslotMessageOffset];
*TransportName = (LPWSTR) &NlGlobalMailslotDesc->CurrentMessage[
NetlogonMailslot->TransportNameOffset];
if ( NetlogonMailslot->ClientSockAddrSize == 0 ) {
*ClientSockAddr = NULL;
} else {
*ClientSockAddr = (PSOCKADDR) &NlGlobalMailslotDesc->CurrentMessage[
NetlogonMailslot->ClientSockAddrOffset];
}
//
// If this is a PNP notification,
// simply return the opcode and the transport name.
//
if ( NetlogonMailslot->MailslotNameSize == 0 ) {
*NlPnpOpcode = NetlogonMailslot->MailslotNameOffset;
*Message = NULL;
*BytesRead = 0;
*DestinationName = NULL;
*Transport = NULL;
NlPrint(( NL_MAILSLOT_TEXT,
"Received PNP opcode 0x%x on transport: %ws\n",
*NlPnpOpcode,
*TransportName ));
//
// If this is a mailslot message,
// return the message to the caller.
//
} else {
*BytesRead = NetlogonMailslot->MailslotMessageSize;
*DestinationName = (LPWSTR) &NlGlobalMailslotDesc->CurrentMessage[
NetlogonMailslot->DestinationNameOffset];
//
// Determine the transport the request came in on.
//
*Transport = NlTransportLookupTransportName( *TransportName );
if ( *Transport == NULL ) {
NlPrint((NL_CRITICAL,
"%ws: Received message for this unsupported transport\n",
*TransportName ));
return FALSE;
}
//
// Determine if we can discard an ancient or duplicate message
//
// Only discard messages that are either expensive to process on this
// machine or generate excessive traffic to respond to. Don't discard
// messages that we've worked hard to get (e.g., discovery responses).
//
switch ( ((PNETLOGON_LOGON_QUERY)*Message)->Opcode) {
case LOGON_REQUEST:
case LOGON_SAM_LOGON_REQUEST:
case LOGON_PRIMARY_QUERY:
//
// If the message is too old,
// discard it.
//
if ( NlTimeHasElapsedEx( &NetlogonMailslot->TimeReceived,
&NlGlobalParameters.MailslotMessageTimeout_100ns,
NULL )) {
#if NETLOGONDBG
NlPrint(( NL_MAILSLOT,
"%ws: Received '%s' message on %ws:"
" (Discarded as too old.)\n",
*DestinationName,
NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
*TransportName ));
#endif // NETLOGONDBG
return FALSE;
}
//
// If the previous message was recent,
// and this message is identical to it,
// discard the current message.
//
#ifdef notdef
NlPrint(( NL_MAILSLOT, "%ws: test prev\n", *DestinationName ));
#endif // notdef
if ( NlGlobalMailslotDesc->PreviousMessage != NULL ) {
PNETLOGON_MAILSLOT PreviousNetlogonMailslot;
PreviousNetlogonMailslot = (PNETLOGON_MAILSLOT)
NlGlobalMailslotDesc->PreviousMessage;
#ifdef notdef
NlPrint(( NL_MAILSLOT, "%ws: test time\n", *DestinationName ));
#endif // notdef
// ??: Compare source netbios name?
if ( (PreviousNetlogonMailslot->TimeReceived.QuadPart +
NlGlobalParameters.MailslotDuplicateTimeout_100ns.QuadPart >
NetlogonMailslot->TimeReceived.QuadPart) ) {
#ifdef notdef
NlPrint(( NL_MAILSLOT, "%ws: test message\n", *DestinationName ));
#endif // notdef
if ( (PreviousNetlogonMailslot->MailslotMessageSize ==
NetlogonMailslot->MailslotMessageSize) &&
RtlEqualMemory(
&NlGlobalMailslotDesc->CurrentMessage[
NetlogonMailslot->MailslotMessageOffset],
&NlGlobalMailslotDesc->PreviousMessage[
PreviousNetlogonMailslot->MailslotMessageOffset],
NetlogonMailslot->MailslotMessageSize ) ) {
//
// Ensure the next comparison is to the timestamp of the
// message we actually responded to.
//
NetlogonMailslot->TimeReceived =
PreviousNetlogonMailslot->TimeReceived;
NlPrint(( NL_MAILSLOT,
"%ws: Received '%s' message on %ws:"
" (Discarded as duplicate of previous.)\n",
*DestinationName,
NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
*TransportName ));
*IgnoreDuplicatesOfPreviousMessage = TRUE;
return FALSE;
}
}
}
//
// If this isn't an IP transport,
// and if the caller explicitly wanted one,
// discard the message.
//
// NT 5 only sends the query on IP when netlogon is running.
// When Netlogon isn't running, the query is sent on all transports
// bound to the redir. Since this DC ignores duplicate messages,
// we want to avoid responding to the non-IP requests or we'll
// ignore the IP query as being a duplicate of this one.
//
// WIN 98 with the Active Directory service pack also sets this bit
// and sends on all transports.
//
if ( !(*Transport)->IsIpTransport ) {
DWORD Version;
DWORD VersionFlags;
DWORD LocalBytesRead;
LocalBytesRead = *BytesRead;
Version = NetpLogonGetMessageVersion( *Message,
&LocalBytesRead,
&VersionFlags );
if ( VersionFlags & NETLOGON_NT_VERSION_IP ) {
NlPrint(( NL_MAILSLOT,
"%ws: Received '%s' message on %ws:"
" (Caller wants response on IP transport.)\n",
*DestinationName,
NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
*TransportName ));
return FALSE;
}
}
}
NlPrint(( NL_MAILSLOT,
"%ws: Received '%s' message on %ws\n",
*DestinationName,
NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
*TransportName ));
NlpDumpBuffer(NL_MAILSLOT_TEXT, *Message, *BytesRead);
}
return TRUE;
}
NET_API_STATUS
NlServerComputerNameAdd(
IN LPWSTR HostedDomainName,
IN LPWSTR HostedServerName
)
/*++
Routine Description:
This routine causes the SMB server to respond to requests on HostedServerName
and to announce this servername as being a member of HostedDomainName.
This code was stolen from NetServerComputerNameAdd. It is different from that
API in the following ways:
1) It only works locally.
2) HostedDomainName is not optional.
3) Failure to add the name on any transport fails the routine
Arguments:
HostedServerName --A pointer to the ASCIIZ string containing the
name which the server should stop supporting
HostedDomainName --A pointer to the ASCIIZ string containing the
domain name the server should use when announcing the presence of
'HostedServerName'
Return Value:
NERR_Success, or reason for failure
--*/
{
DWORD resumehandle = 0;
NET_API_STATUS retval;
DWORD entriesread, totalentries;
DWORD i, j;
UCHAR NetBiosName[ MAX_PATH ];
OEM_STRING NetBiosNameString;
UNICODE_STRING UniName;
PSERVER_TRANSPORT_INFO_1 psti1;
//
// Ensure a valid HostedServerName was passed in
//
if( HostedServerName == NULL ) {
return ERROR_INVALID_PARAMETER;
}
//
// Convert the HostedServerName to an OEM string
//
RtlInitUnicodeString( &UniName, HostedServerName );
NetBiosNameString.Buffer = (PCHAR)NetBiosName;
NetBiosNameString.MaximumLength = sizeof( NetBiosName );
(VOID) RtlUpcaseUnicodeStringToOemString(
&NetBiosNameString,
&UniName,
FALSE
);
//
// Enumerate all the transports so we can add the name and domain
// to each one.
//
retval = NetServerTransportEnum ( NULL,
1,
(LPBYTE *)&psti1,
(DWORD)-1,
&entriesread,
&totalentries,
&resumehandle );
if( retval == NERR_Success ) {
//
// Add the new name and domain to all of the transports
//
for( i=0; i < entriesread; i++ ) {
//
// Make sure we haven't already added to this transport
//
for( j = 0; j < i; j++ ) {
if( wcscmp( psti1[j].svti1_transportname, psti1[i].svti1_transportname ) == 0 ) {
break;
}
}
if( i != j ) {
psti1[i].svti1_transportname[0] = '\0';
continue;
}
psti1[i].svti1_transportaddress = NetBiosName;
psti1[i].svti1_transportaddresslength = strlen( NetBiosName );
psti1[i].svti1_domain = HostedDomainName;
retval = NetServerTransportAddEx( NULL, 1, (LPBYTE)&psti1[ i ] );
#ifndef NWLNKIPX_WORKS
//
// ??: The SMB server doesn't allow multiple names on NWLNK IPX.
//
if ( retval == ERROR_TOO_MANY_NAMES &&
_wcsicmp( psti1[i].svti1_transportname, L"\\Device\\NwlnkIpx" ) == 0 ) {
retval = NERR_Success;
}
#endif // NWLNKIPX_WORKS
if( retval != NERR_Success ) {
NlPrint((NL_CRITICAL,
"%ws: NlServerComputerNameAdd: Cannot add %ws to SMB server on transport %ws %ld\n",
HostedDomainName,
HostedServerName,
psti1[i].svti1_transportname,
retval ));
//
// Remove any names already added.
//
for( j=0; j < i; j++ ) {
NET_API_STATUS TempStatus;
if ( psti1[j].svti1_transportname[0] == '\0' ) {
continue;
}
TempStatus = NetServerTransportDel( NULL, 1, (LPBYTE)&psti1[ j ] );
NlPrint((NL_CRITICAL,
"%ws: NlServerComputerNameAdd: Cannot remove %ws to SMB server on transport %ws %ld\n",
HostedDomainName,
HostedServerName,
psti1[i].svti1_transportname,
TempStatus ));
}
break;
}
}
MIDL_user_free( psti1 );
}
return retval;
}