/*-- 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 // I_BrowserSetNetlogonState #include // Service announcement #include // 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 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 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 ; iDomUnicodeComputerNameString.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; iDomOemComputerNameLength || !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; }