/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: brdevice.c Abstract: This module contains the support routines for the APIs that call into the browser or the datagram receiver. Author: Rita Wong (ritaw) 20-Feb-1991 Larry Osterman (larryo) 23-Mar-1992 Revision History: --*/ #include "precomp.h" #pragma hdrstop //-------------------------------------------------------------------// // // // Local Function Prototypes // // // //-------------------------------------------------------------------// //-------------------------------------------------------------------// // // // Global variables // // // //-------------------------------------------------------------------// // Event for synchronization of asynchronous I/O completion against the // datagram receiver HANDLE BrDgAsyncIOShutDownEvent; HANDLE BrDgAsyncIOThreadShutDownEvent; BOOL BrDgShutDownInitiated = FALSE; DWORD BrDgAsyncIOsOutstanding = 0; DWORD BrDgWorkerThreadsOutstanding = 0; CRITICAL_SECTION BrAsyncIOCriticalSection; // // Handle to the Datagram Receiver DD // HANDLE BrDgReceiverDeviceHandle = NULL; VOID CompleteAsyncBrowserIoControl( IN PVOID ApcContext, IN PIO_STATUS_BLOCK IoStatusBlock, IN ULONG Reserved ); VOID BrDecrementOutstandingIos() /*++ Routine Description: Decrements the outstanding IO count, and signals the event if necessary Arguments: None. Return Value: VOID --*/ { BOOL SignalAsyncIOShutDownEvent = FALSE; EnterCriticalSection(&BrAsyncIOCriticalSection); BrDgAsyncIOsOutstanding -= 1; if (BrDgAsyncIOsOutstanding == 0 && BrDgShutDownInitiated) { SignalAsyncIOShutDownEvent = TRUE; } LeaveCriticalSection(&BrAsyncIOCriticalSection); if (SignalAsyncIOShutDownEvent) { SetEvent(BrDgAsyncIOShutDownEvent); } } NET_API_STATUS BrOpenDgReceiver ( VOID ) /*++ Routine Description: This routine opens the NT LAN Man Datagram Receiver driver. Arguments: None. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS Status; NTSTATUS ntstatus; UNICODE_STRING DeviceName; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; // // Open the redirector device. // RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U); InitializeObjectAttributes( &ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL ); ntstatus = NtOpenFile( &BrDgReceiverDeviceHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, 0 ); if (NT_SUCCESS(ntstatus)) { ntstatus = IoStatusBlock.Status; } if (! NT_SUCCESS(ntstatus)) { BrPrint(( BR_CRITICAL,"NtOpenFile browser driver failed: 0x%08lx\n", ntstatus)); } Status = NetpNtStatusToApiStatus(ntstatus); if (NT_SUCCESS(ntstatus)) { // Initialize the event and the critical section used for async I/O try { BrDgShutDownInitiated = FALSE; BrDgAsyncIOsOutstanding = 0; BrDgWorkerThreadsOutstanding = 0; InitializeCriticalSection( &BrAsyncIOCriticalSection ); BrDgAsyncIOShutDownEvent = CreateEvent( NULL, // Event attributes TRUE, // Event must be manually reset FALSE, NULL // Initial state not signalled ); if (BrDgAsyncIOShutDownEvent == NULL) { DeleteCriticalSection(&BrAsyncIOCriticalSection); Status = GetLastError(); } BrDgAsyncIOThreadShutDownEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if( BrDgAsyncIOThreadShutDownEvent == NULL ) { CloseHandle( BrDgAsyncIOShutDownEvent ); BrDgAsyncIOShutDownEvent = NULL; DeleteCriticalSection(&BrAsyncIOCriticalSection); Status = GetLastError(); } } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = ERROR_NO_SYSTEM_RESOURCES; } } return Status; } VOID BrShutdownDgReceiver( VOID ) /*++ Routine Description: This routine close the LAN Man Redirector device. Arguments: None. Return Value: None. --*/ { IO_STATUS_BLOCK IoSb; LARGE_INTEGER timeout; BOOL WaitForAsyncIOCompletion = FALSE; DWORD waitResult = 0; EnterCriticalSection(&BrAsyncIOCriticalSection); BrDgShutDownInitiated = TRUE; if (BrDgAsyncIOsOutstanding != 0) { WaitForAsyncIOCompletion = TRUE; } LeaveCriticalSection(&BrAsyncIOCriticalSection); if (WaitForAsyncIOCompletion) { // // Cancel the I/O operations outstanding on the browser. // Then wait for the shutdown event to be signalled, but allow // APC's to be called to call our completion routine. // NtCancelIoFile(BrDgReceiverDeviceHandle, &IoSb); do { waitResult = WaitForSingleObjectEx(BrDgAsyncIOShutDownEvent,0xffffffff, TRUE); } while( waitResult == WAIT_IO_COMPLETION ); } ASSERT(BrDgAsyncIOsOutstanding == 0); EnterCriticalSection(&BrAsyncIOCriticalSection); // Wait for the final worker thread to exit if necessary if( BrDgWorkerThreadsOutstanding > 0 ) { WaitForAsyncIOCompletion = TRUE; } else { WaitForAsyncIOCompletion = FALSE; } LeaveCriticalSection(&BrAsyncIOCriticalSection); if( WaitForAsyncIOCompletion ) { // This will either be signalled from before, or the final worker thread will signal it. WaitForSingleObject( BrDgAsyncIOThreadShutDownEvent, 0xffffffff ); } if (BrDgAsyncIOShutDownEvent != NULL) { CloseHandle(BrDgAsyncIOShutDownEvent); CloseHandle(BrDgAsyncIOThreadShutDownEvent); DeleteCriticalSection(&BrAsyncIOCriticalSection); } } // // Retreive the list of bound transports from the bowser driver. // NET_API_STATUS BrGetTransportList( OUT PLMDR_TRANSPORT_LIST *TransportList ) { NET_API_STATUS Status; LMDR_REQUEST_PACKET RequestPacket; // // If we have a previous buffer that was too small, free it up. // RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM; RequestPacket.Type = EnumerateXports; RtlInitUnicodeString(&RequestPacket.TransportName, NULL); RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL); Status = DeviceControlGetInfo( BrDgReceiverDeviceHandle, IOCTL_LMDR_ENUMERATE_TRANSPORTS, &RequestPacket, sizeof(RequestPacket), (LPVOID *)TransportList, 0xffffffff, 4096, NULL ); return Status; } NET_API_STATUS BrAnnounceDomain( IN PNETWORK Network, IN ULONG Periodicity ) { NET_API_STATUS Status; UCHAR AnnounceBuffer[sizeof(BROWSE_ANNOUNCE_PACKET)+LM20_CNLEN+1]; PBROWSE_ANNOUNCE_PACKET Announcement = (PBROWSE_ANNOUNCE_PACKET )AnnounceBuffer; // // We don't announce domains on direct host IPX. // if (Network->Flags & NETWORK_IPX) { return NERR_Success; } Announcement->BrowseType = WkGroupAnnouncement; Announcement->BrowseAnnouncement.Periodicity = Periodicity; Announcement->BrowseAnnouncement.UpdateCount = 0; Announcement->BrowseAnnouncement.VersionMajor = BROWSER_CONFIG_VERSION_MAJOR; Announcement->BrowseAnnouncement.VersionMinor = BROWSER_CONFIG_VERSION_MINOR; Announcement->BrowseAnnouncement.Type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT; if (Network->Flags & NETWORK_PDC ) { Announcement->BrowseAnnouncement.Type |= SV_TYPE_DOMAIN_CTRL; } lstrcpyA(Announcement->BrowseAnnouncement.ServerName, Network->DomainInfo->DomOemDomainName); lstrcpyA(Announcement->BrowseAnnouncement.Comment, Network->DomainInfo->DomOemComputerName ); Status = SendDatagram(BrDgReceiverDeviceHandle, &Network->NetworkName, &Network->DomainInfo->DomUnicodeDomainNameString, Network->DomainInfo->DomUnicodeDomainName, DomainAnnouncement, Announcement, FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment)+ Network->DomainInfo->DomOemComputerNameLength+sizeof(UCHAR) ); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "%ws: Unable to announce domain for network %wZ: %X\n", Network->DomainInfo->DomUnicodeDomainName, &Network->NetworkName, Status)); } return Status; } NET_API_STATUS BrUpdateBrowserStatus ( IN PNETWORK Network, IN DWORD ServiceStatus ) { NET_API_STATUS Status; UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)]; PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer; RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; RequestPacket->TransportName = Network->NetworkName; RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString; RequestPacket->Parameters.UpdateStatus.NewStatus = ServiceStatus; RequestPacket->Parameters.UpdateStatus.IsLanmanNt = (BrInfo.IsLanmanNt != FALSE); #ifdef ENABLE_PSEUDO_BROWSER RequestPacket->Parameters.UpdateStatus.PseudoServerLevel = (BOOL)(BrInfo.PseudoServerLevel); #endif // RequestPacket->Parameters.UpdateStatus.IsMemberDomain = TRUE; // Not used // RequestPacket->Parameters.UpdateStatus.IsPrimaryDomainController = Network->DomainInfo->IsPrimaryDomainController; // RequestPacket->Parameters.UpdateStatus.IsDomainMaster = Network->DomainInfo->IsDomainMasterBrowser; RequestPacket->Parameters.UpdateStatus.MaintainServerList = (BrInfo.MaintainServerList == 1); // // Tell the bowser the number of servers in the server table. // RequestPacket->Parameters.UpdateStatus.NumberOfServersInTable = NumberInterimServerListElements(&Network->BrowseTable) + NumberInterimServerListElements(&Network->DomainList) + Network->TotalBackupServerListEntries + Network->TotalBackupDomainListEntries; // // This is a simple IoControl - It just updates the status. // Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_UPDATE_STATUS, RequestPacket, sizeof(LMDR_REQUEST_PACKET), NULL, 0, NULL); return Status; } NET_API_STATUS BrIssueAsyncBrowserIoControl( IN PNETWORK Network OPTIONAL, IN ULONG ControlCode, IN PBROWSER_WORKER_ROUTINE CompletionRoutine, IN PVOID OptionalParameter ) /*++ Routine Description: Issue an asynchronous Io Control to the browser. Call the specified completion routine when the IO finishes. Arguments: Network - Network the function applies to If this parameter is not supplied, the operation is not related to a particular network.. ControlCode - IoControl function code CompletionRoutine - Routine to be called when the IO finishes. OptionalParameter - Function code specific information Return Value: Status of the operation. --*/ { ULONG PacketSize; PLMDR_REQUEST_PACKET RequestPacket = NULL; NTSTATUS NtStatus; PBROWSERASYNCCONTEXT Context = NULL; BOOL IssueAsyncRequest = FALSE; // Check to see if it is OK to issue an async IO request. We do not want to // issue these request can be issued. EnterCriticalSection(&BrAsyncIOCriticalSection); if (!BrDgShutDownInitiated) { BrDgAsyncIOsOutstanding += 1; IssueAsyncRequest = TRUE; } LeaveCriticalSection(&BrAsyncIOCriticalSection); if (!IssueAsyncRequest) { return ERROR_REQ_NOT_ACCEP; } // // Allocate a buffer for the context and the request packet. // PacketSize = sizeof(LMDR_REQUEST_PACKET) + MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR); if ( ARGUMENT_PRESENT(Network) ) { PacketSize += Network->NetworkName.MaximumLength + Network->DomainInfo->DomUnicodeDomainNameString.Length; } Context = MIDL_user_allocate(sizeof(BROWSERASYNCCONTEXT) + PacketSize ); if (Context == NULL) { BrDecrementOutstandingIos(); return(ERROR_NOT_ENOUGH_MEMORY); } RequestPacket = (PLMDR_REQUEST_PACKET)(Context + 1); RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; // // Set level to FALSE to indicate that find master should not initiate // a findmaster request, simply complete when a new master announces // itself. // RequestPacket->Level = 0; // // Fill in the network specific information if it is specified. // if ( ARGUMENT_PRESENT(Network) ) { // // Stick the name of the transport associated with this request at the // end of the request packet. // RequestPacket->TransportName.MaximumLength = Network->NetworkName.MaximumLength; RequestPacket->TransportName.Buffer = (PWSTR)((PCHAR)RequestPacket+sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR))); RtlCopyUnicodeString(&RequestPacket->TransportName, &Network->NetworkName); // // Stick the domain name associated with this request at the // end of the request packet. // RequestPacket->EmulatedDomainName.MaximumLength = Network->DomainInfo->DomUnicodeDomainNameString.Length; RequestPacket->EmulatedDomainName.Length = 0; RequestPacket->EmulatedDomainName.Buffer = (PWSTR)(((PCHAR)RequestPacket->TransportName.Buffer) + RequestPacket->TransportName.MaximumLength); RtlAppendUnicodeToString(&RequestPacket->EmulatedDomainName, Network->DomainInfo->DomUnicodeDomainName ); } // // Do opcode dependent initialization of the request packet. // switch ( ControlCode ) { case IOCTL_LMDR_NEW_MASTER_NAME: if (ARGUMENT_PRESENT(OptionalParameter)) { LPWSTR MasterName = (LPWSTR) OptionalParameter; RequestPacket->Parameters.GetMasterName.MasterNameLength = wcslen(MasterName+2)*sizeof(WCHAR); wcscpy( RequestPacket->Parameters.GetMasterName.Name, MasterName+2); } else { RequestPacket->Parameters.GetMasterName.MasterNameLength = 0; } break; } // // Send the request to the bowser. // BrInitializeWorkItem(&Context->WorkItem, CompletionRoutine, Context); Context->Network = Network; Context->RequestPacket = RequestPacket; NtStatus = NtDeviceIoControlFile(BrDgReceiverDeviceHandle, NULL, CompleteAsyncBrowserIoControl, Context, &Context->IoStatusBlock, ControlCode, RequestPacket, PacketSize, RequestPacket, sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR) ); if (NT_ERROR(NtStatus)) { BrPrint(( BR_CRITICAL, "Unable to issue browser IoControl: %X\n", NtStatus)); MIDL_user_free(Context); BrDecrementOutstandingIos(); return(BrMapStatus(NtStatus)); } return NERR_Success; } VOID CompleteAsyncBrowserIoControl( IN PVOID ApcContext, IN PIO_STATUS_BLOCK IoStatusBlock, IN ULONG Reserved ) { PBROWSERASYNCCONTEXT Context = ApcContext; // // If this request was canceled, we're stopping the browser, so we // want to clean up our allocated pool. In addition, don't bother // calling into the routine - the threads are gone by now. // if (IoStatusBlock->Status == STATUS_CANCELLED) { MIDL_user_free(Context); // Signal the thread waiting on the completion in case of shut down // and reset the flag. BrDecrementOutstandingIos(); return; } // // Timestamp when this request was completed. This allows us to tell // where a request spent its time. // NtQueryPerformanceCounter(&Context->TimeCompleted, NULL); BrQueueWorkItem(&Context->WorkItem); // Signal the thread waiting on the completion in case of shut down // and reset the flag. BrDecrementOutstandingIos(); } NET_API_STATUS BrGetLocalBrowseList( IN PNETWORK Network, IN LPWSTR DomainName OPTIONAL, IN ULONG Level, IN ULONG ServerType, OUT PVOID *ServerList, OUT PULONG EntriesRead, OUT PULONG TotalEntries ) { NET_API_STATUS status; PLMDR_REQUEST_PACKET Drp; // Datagram receiver request packet ULONG DrpSize; ULONG DomainNameSize; // // Allocate the request packet large enough to hold the variable length // domain name. // DomainNameSize = ARGUMENT_PRESENT(DomainName) ? (wcslen(DomainName) + 1) * sizeof(WCHAR) : 0; DrpSize = sizeof(LMDR_REQUEST_PACKET) + DomainNameSize + Network->NetworkName.MaximumLength + Network->DomainInfo->DomUnicodeDomainNameString.Length; if ((Drp = MIDL_user_allocate(DrpSize)) == NULL) { return GetLastError(); } // // Set up request packet. Output buffer structure is of enumerate // servers type. // Drp->Version = LMDR_REQUEST_PACKET_VERSION_DOM; Drp->Type = EnumerateServers; Drp->Level = Level; Drp->Parameters.EnumerateServers.ServerType = ServerType; Drp->Parameters.EnumerateServers.ResumeHandle = 0; // // Copy the transport name into the buffer. // Drp->TransportName.Buffer = (PWSTR)((PCHAR)Drp+ sizeof(LMDR_REQUEST_PACKET) + DomainNameSize); Drp->TransportName.MaximumLength = Network->NetworkName.MaximumLength; RtlCopyUnicodeString(&Drp->TransportName, &Network->NetworkName); // // Copy the enumalated domain name into the buffer. // Drp->EmulatedDomainName.MaximumLength = Network->DomainInfo->DomUnicodeDomainNameString.Length; Drp->EmulatedDomainName.Length = 0; Drp->EmulatedDomainName.Buffer = (PWSTR)(((PCHAR)Drp->TransportName.Buffer) + Drp->TransportName.MaximumLength); RtlAppendUnicodeToString(&Drp->EmulatedDomainName, Network->DomainInfo->DomUnicodeDomainName ); // // Copy the queried domain name into the buffer. // if (ARGUMENT_PRESENT(DomainName)) { Drp->Parameters.EnumerateServers.DomainNameLength = DomainNameSize - sizeof(WCHAR); wcscpy(Drp->Parameters.EnumerateServers.DomainName, DomainName); } else { Drp->Parameters.EnumerateServers.DomainNameLength = 0; Drp->Parameters.EnumerateServers.DomainName[0] = '\0'; } // // Ask the datagram receiver to enumerate the servers // status = DeviceControlGetInfo( BrDgReceiverDeviceHandle, IOCTL_LMDR_ENUMERATE_SERVERS, Drp, DrpSize, ServerList, 0xffffffff, 4096, NULL ); *EntriesRead = Drp->Parameters.EnumerateServers.EntriesRead; *TotalEntries = Drp->Parameters.EnumerateServers.TotalEntries; (void) MIDL_user_free(Drp); return status; } NET_API_STATUS BrRemoveOtherDomain( IN PNETWORK Network, IN LPTSTR ServerName ) { NET_API_STATUS Status; UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)]; PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer; RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; RequestPacket->TransportName = Network->NetworkName; RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString; RequestPacket->Parameters.AddDelName.DgReceiverNameLength = STRLEN(ServerName)*sizeof(TCHAR); RequestPacket->Parameters.AddDelName.Type = OtherDomain; STRCPY(RequestPacket->Parameters.AddDelName.Name,ServerName); // // This is a simple IoControl - It just updates the status. // Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_DELETE_NAME_DOM, RequestPacket, sizeof(LMDR_REQUEST_PACKET), NULL, 0, NULL); return Status; } NET_API_STATUS BrAddName( IN PNETWORK Network, IN LPTSTR Name, IN DGRECEIVER_NAME_TYPE NameType ) /*++ Routine Description: Add a single name to a single transport. Arguments: Network - Transport to add the name to Name - Name to add NameType - Type of the name to add Return Value: None. --*/ { NET_API_STATUS Status; UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)]; PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer; RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; RequestPacket->TransportName = Network->NetworkName; RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString; RequestPacket->Parameters.AddDelName.DgReceiverNameLength = STRLEN(Name)*sizeof(TCHAR); RequestPacket->Parameters.AddDelName.Type = NameType; STRCPY(RequestPacket->Parameters.AddDelName.Name,Name); // // This is a simple IoControl - It just updates the status. // Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_ADD_NAME_DOM, RequestPacket, sizeof(LMDR_REQUEST_PACKET), NULL, 0, NULL); return Status; } NET_API_STATUS BrQueryOtherDomains( OUT LPSERVER_INFO_100 *ReturnedBuffer, OUT LPDWORD TotalEntries ) /*++ Routine Description: This routine returns the list of "other domains" configured for this machine. Arguments: ReturnedBuffer - Returns the list of other domains as a SERVER_INFO_100 structure. TotalEntries - Returns the total number of other domains. Return Value: NET_API_STATUS - The status of this request. --*/ { NET_API_STATUS Status; LMDR_REQUEST_PACKET RequestPacket; PDGRECEIVE_NAMES NameTable; PVOID Buffer; LPTSTR BufferEnd; PSERVER_INFO_100 ServerInfo; ULONG NumberOfOtherDomains; ULONG BufferSizeNeeded; ULONG i; RequestPacket.Type = EnumerateNames; RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM; RequestPacket.Level = 0; RequestPacket.TransportName.Length = 0; RequestPacket.TransportName.Buffer = NULL; RtlInitUnicodeString( &RequestPacket.EmulatedDomainName, NULL ); RequestPacket.Parameters.EnumerateNames.ResumeHandle = 0; Status = DeviceControlGetInfo(BrDgReceiverDeviceHandle, IOCTL_LMDR_ENUMERATE_NAMES, &RequestPacket, sizeof(RequestPacket), (LPVOID *)&NameTable, 0xffffffff, 0, NULL); if (Status != NERR_Success) { return Status; } NumberOfOtherDomains = 0; BufferSizeNeeded = 0; for (i = 0;i < RequestPacket.Parameters.EnumerateNames.EntriesRead ; i++) { if (NameTable[i].Type == OtherDomain) { NumberOfOtherDomains += 1; BufferSizeNeeded += sizeof(SERVER_INFO_100)+NameTable[i].DGReceiverName.Length+sizeof(TCHAR); } } *TotalEntries = NumberOfOtherDomains; Buffer = MIDL_user_allocate(BufferSizeNeeded); if (Buffer == NULL) { MIDL_user_free(NameTable); return(ERROR_NOT_ENOUGH_MEMORY); } ServerInfo = Buffer; BufferEnd = (LPTSTR)((PCHAR)Buffer+BufferSizeNeeded); for (i = 0;i < RequestPacket.Parameters.EnumerateNames.EntriesRead ; i++) { // Copy only OtherDomain names. // Protect from empty entries (in case transport name is empty). if (NameTable[i].Type == OtherDomain && NameTable[i].DGReceiverName.Length != 0) { WCHAR NameBuffer[DNLEN+1]; // // The name from the browser is not null terminated, so copy it // to a local buffer and null terminate it. // RtlCopyMemory(NameBuffer, NameTable[i].DGReceiverName.Buffer, NameTable[i].DGReceiverName.Length); NameBuffer[(NameTable[i].DGReceiverName.Length) / sizeof(TCHAR)] = UNICODE_NULL; ServerInfo->sv100_platform_id = PLATFORM_ID_OS2; ServerInfo->sv100_name = NameBuffer; if (!NetpPackString(&ServerInfo->sv100_name, (LPBYTE)(ServerInfo+1), &BufferEnd)) { MIDL_user_free(NameTable); return(NERR_InternalError); } ServerInfo += 1; } } MIDL_user_free(NameTable); *ReturnedBuffer = (LPSERVER_INFO_100) Buffer; Status = NERR_Success; return Status; } NET_API_STATUS BrAddOtherDomain( IN PNETWORK Network, IN LPTSTR ServerName ) { return BrAddName( Network, ServerName, OtherDomain ); } NET_API_STATUS BrBindToTransport( IN LPWSTR TransportName, IN LPWSTR EmulatedDomainName, IN LPWSTR EmulatedComputerName ) { NET_API_STATUS Status; UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH+1+CNLEN+1)*sizeof(WCHAR)]; PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer; RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; RequestPacket->Level = TRUE; // EmulatedComputerName follows transport name RequestPacket->TransportName.Length = 0; RequestPacket->TransportName.MaximumLength = 0; RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, EmulatedDomainName ); RequestPacket->Parameters.Bind.TransportNameLength = STRLEN(TransportName)*sizeof(TCHAR); STRCPY(RequestPacket->Parameters.Bind.TransportName, TransportName); STRCAT(RequestPacket->Parameters.Bind.TransportName, EmulatedComputerName ); BrPrint(( BR_NETWORK, "%ws: %ws: bind from transport sent to bowser driver\n", EmulatedDomainName, TransportName)); // // This is a simple IoControl - It just updates the status. // Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_BIND_TO_TRANSPORT_DOM, RequestPacket, FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.Bind.TransportName) + RequestPacket->Parameters.Bind.TransportNameLength + wcslen(EmulatedComputerName) * sizeof(WCHAR) + sizeof(WCHAR), NULL, 0, NULL); return Status; } NET_API_STATUS BrUnbindFromTransport( IN LPWSTR TransportName, IN LPWSTR EmulatedDomainName ) { NET_API_STATUS Status; UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH+1)*sizeof(WCHAR)]; PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer; RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; RequestPacket->TransportName.Length = 0; RequestPacket->TransportName.MaximumLength = 0; RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, EmulatedDomainName ); RequestPacket->Parameters.Unbind.TransportNameLength = STRLEN(TransportName)*sizeof(TCHAR); STRCPY(RequestPacket->Parameters.Unbind.TransportName, TransportName); BrPrint(( BR_NETWORK, "%ws: %ws: unbind from transport sent to bowser driver\n", EmulatedDomainName, TransportName)); // // This is a simple IoControl - It just updates the status. // Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_UNBIND_FROM_TRANSPORT_DOM, RequestPacket, FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.Bind.TransportName) + RequestPacket->Parameters.Bind.TransportNameLength, NULL, 0, NULL); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "%ws: %ws: unbind from transport failed %ld\n", EmulatedDomainName, TransportName, Status )); } return Status; } NET_API_STATUS BrEnablePnp( BOOL Enable ) /*++ Routine Description: This routine enables or disables PNP messages from the bowser. Arguments: Enable - TRUE if messages are to be enabled. Return Value: None. --*/ { NET_API_STATUS Status; UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)]; PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer; RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL ); RtlInitUnicodeString( &RequestPacket->TransportName, NULL ); RequestPacket->Parameters.NetlogonMailslotEnable.MaxMessageCount = Enable; // // This is a simple IoControl - It just updates the status. // Status = BrDgReceiverIoControl( BrDgReceiverDeviceHandle, IOCTL_LMDR_BROWSER_PNP_ENABLE, RequestPacket, sizeof(LMDR_REQUEST_PACKET), NULL, 0, NULL); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "Enable PNP failed: %ld %ld\n", Enable, Status)); } return Status; } VOID HandlePnpMessage ( IN PVOID Ctx ) /*++ Routine Description: This function handles a PNP message from the bowser driver. Arguments: Ctx - Context block for request. Return Value: None. --*/ { NET_API_STATUS NetStatus; PBROWSERASYNCCONTEXT Context = Ctx; PNETLOGON_MAILSLOT NetlogonMailslot = (PNETLOGON_MAILSLOT) Context->RequestPacket; LPWSTR Transport; UNICODE_STRING TransportName; LPWSTR HostedDomain; UNICODE_STRING HostedDomainName; NETLOGON_PNP_OPCODE PnpOpcode; ULONG TransportFlags; PLIST_ENTRY DomainEntry; PDOMAIN_INFO DomainInfo; PNETWORK Network; try { // // The request failed for some reason - just return immediately. // if (!NT_SUCCESS(Context->IoStatusBlock.Status)) { // // Sleep for a second to avoid consuming entire system. Sleep( 1000 ); try_return(NOTHING); } // // If the message isn't a PNP message, // someone is really confused. // if ( NetlogonMailslot->MailslotNameSize != 0 ) { BrPrint(( BR_CRITICAL, "Got malformed PNP message\n" )); // // Sleep for a second to avoid consuming entire system. Sleep( 1000 ); try_return(NOTHING); } // // Parse the message // PnpOpcode = NetlogonMailslot->MailslotNameOffset; TransportFlags = NetlogonMailslot->MailslotMessageOffset; if( NetlogonMailslot->TransportNameSize > 0 ) { Transport = (LPWSTR) &(((LPBYTE)NetlogonMailslot)[ NetlogonMailslot->TransportNameOffset]); RtlInitUnicodeString( &TransportName, Transport ); } else { RtlInitUnicodeString( &TransportName, NULL ); } if( NetlogonMailslot->DestinationNameSize > 0 ) { HostedDomain = (LPWSTR) &(((LPBYTE)NetlogonMailslot)[ NetlogonMailslot->DestinationNameOffset]); RtlInitUnicodeString( &HostedDomainName, HostedDomain ); } else { RtlInitUnicodeString( &HostedDomainName, NULL ); } // // Handle binding to a new network. // switch (PnpOpcode ) { case NlPnpTransportBind: BrPrint(( BR_NETWORK, "Received bind PNP opcode 0x%lx on transport: %ws\n", TransportFlags, Transport )); // // Ignore the direct host IPX transport. // The browser service created it so we don't need PNP notification. // if ( TransportFlags & LMDR_TRANSPORT_IPX ) { BrPrint(( BR_NETWORK, "Ignoring PNP bind of direct host IPX transport\n" )); break; } NetStatus = BrChangeConfigValue( L"DirectHostBinding", MultiSzType, NULL, &(BrInfo.DirectHostBinding), TRUE ); if ( NetStatus != NERR_Success ) { BrPrint(( BR_CRITICAL, "Unbind failed to read Registry DirectHostBinding: %ws %ld\n", Transport, NetStatus )); // // Don't abort binding on failure to read DirectHostBinding, Our internal binding // info hasn't change so we'll use whatever we have. // Ignore error. // NetStatus = NERR_Success; } else { // // DirectHostBinding sepcified. Verify consistency & fail on // inconsistent setup (since it was setup, there was an intention resulted w/ // a failure here). // EnterCriticalSection ( &BrInfo.ConfigCritSect ); if (BrInfo.DirectHostBinding != NULL && !NetpIsTStrArrayEmpty(BrInfo.DirectHostBinding)) { BrPrint(( BR_INIT,"DirectHostBinding length: %ld\n",NetpTStrArrayEntryCount(BrInfo.DirectHostBinding))); if (NetpTStrArrayEntryCount(BrInfo.DirectHostBinding) % 2 != 0) { NetApiBufferFree(BrInfo.DirectHostBinding); BrInfo.DirectHostBinding = NULL; // we fail on invalid specifications NetStatus = ERROR_INVALID_PARAMETER; } } LeaveCriticalSection ( &BrInfo.ConfigCritSect ); } // // Loop creating the network for each emulated domain. // EnterCriticalSection(&NetworkCritSect); for (DomainEntry = ServicedDomains.Flink ; DomainEntry != &ServicedDomains; DomainEntry = DomainEntry->Flink ) { DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next); DomainInfo->PnpDone = FALSE; } for (DomainEntry = ServicedDomains.Flink ; DomainEntry != &ServicedDomains; ) { DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next); // // If this domain has already been processed, // skip it. // if ( DomainInfo->PnpDone ) { DomainEntry = DomainEntry->Flink; continue; } DomainInfo->PnpDone = TRUE; // // Drop the crit sect while doing the lenghty PNP operation. // DomainInfo->ReferenceCount++; LeaveCriticalSection(&NetworkCritSect); // // Finally create the transport. // NetStatus = BrCreateNetwork( &TransportName, TransportFlags, NULL, DomainInfo ); if ( NetStatus != NERR_Success ) { BrPrint(( BR_CRITICAL, "%ws: Bind failed on transport: %ws %ld\n", DomainInfo->DomUnicodeDomainName, Transport, NetStatus )); // ?? Anything else to do on failure } // // Finish process the emulated domains // Start at the front of the list since we dropped the lock. // BrDereferenceDomain(DomainInfo); EnterCriticalSection(&NetworkCritSect); DomainEntry = ServicedDomains.Flink; } LeaveCriticalSection(&NetworkCritSect); break; // // Handle Unbinding from a network. // case NlPnpTransportUnbind: BrPrint(( BR_NETWORK, "Received unbind PNP opcode 0x%lx on transport: %ws\n", TransportFlags, Transport )); // // Ignore the direct host IPX transport. // The browser service created it so we don't need PNP notification. // if ( TransportFlags & LMDR_TRANSPORT_IPX ) { BrPrint(( BR_NETWORK, "Ignoring PNP unbind of direct host IPX transport\n" )); break; } // // Loop deleting the network for each emulated domain. // EnterCriticalSection(&NetworkCritSect); for (DomainEntry = ServicedDomains.Flink ; DomainEntry != &ServicedDomains; DomainEntry = DomainEntry->Flink ) { DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next); DomainInfo->PnpDone = FALSE; } for (DomainEntry = ServicedDomains.Flink ; DomainEntry != &ServicedDomains; ) { DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next); // // If this domain has already been processed, // skip it. // if ( DomainInfo->PnpDone ) { DomainEntry = DomainEntry->Flink; continue; } DomainInfo->PnpDone = TRUE; // // Drop the crit sect while doing the lenghty PNP operation. // DomainInfo->ReferenceCount++; LeaveCriticalSection(&NetworkCritSect); // // Finally delete the transport. // Network = BrFindNetwork( DomainInfo, &TransportName ); if ( Network == NULL ) { BrPrint(( BR_CRITICAL, "%ws: Unbind cannot find transport: %ws\n", DomainInfo->DomUnicodeDomainName, Transport )); } else { // // If the network has an alternate network, // delete it first. // if ( Network->AlternateNetwork != NULL ) { PNETWORK AlternateNetwork; AlternateNetwork = BrReferenceNetwork( Network->AlternateNetwork ); if ( AlternateNetwork != NULL) { BrPrint(( BR_NETWORK, "%ws: %ws: Unbind from alternate transport: %ws\n", DomainInfo->DomUnicodeDomainName, Transport, AlternateNetwork->NetworkName.Buffer )); NetStatus = BrDeleteNetwork( AlternateNetwork, NULL ); if ( NetStatus != NERR_Success ) { BrPrint(( BR_CRITICAL, "%ws: Unbind failed on transport: %ws %ld\n", DomainInfo->DomUnicodeDomainName, AlternateNetwork->NetworkName.Buffer, NetStatus )); // ?? Anything else to do on failure } BrDereferenceNetwork( AlternateNetwork ); } } // // Delete the network. // NetStatus = BrDeleteNetwork( Network, NULL ); if ( NetStatus != NERR_Success ) { BrPrint(( BR_CRITICAL, "%ws: Unbind failed on transport: %ws %ld\n", DomainInfo->DomUnicodeDomainName, Transport, NetStatus )); // ?? Anything else to do on failure } BrDereferenceNetwork( Network ); } // // Finish process the emulated domains // Start at the front of the list since we dropped the lock. // BrDereferenceDomain(DomainInfo); EnterCriticalSection(&NetworkCritSect); DomainEntry = ServicedDomains.Flink; } LeaveCriticalSection(&NetworkCritSect); break; // // Handle domain rename // case NlPnpDomainRename: BrPrint(( BR_NETWORK, "Received Domain Rename PNP for domain: %ws\n", HostedDomain )); // // See if we're handling the specified domain. // DomainInfo = BrFindDomain( HostedDomain, FALSE ); if ( DomainInfo == NULL ) { BrPrint(( BR_CRITICAL, "%ws: Renamed domain doesn't exist\n", HostedDomain )); } else { // // If so, // rename it. // BrRenameDomain( DomainInfo ); BrDereferenceDomain( DomainInfo ); } break; // // Handle PDC/BDC role change. // case NlPnpNewRole: BrPrint(( BR_NETWORK, "%ws: Received role change PNP opcode 0x%lx on transport: %ws\n", HostedDomain, TransportFlags, Transport )); // // Role can only change on lanman NT systems // if (!BrInfo.IsLanmanNt) { break; } // // See if we're handling the specified domain. // DomainInfo = BrFindDomain( HostedDomain, FALSE ); if ( DomainInfo == NULL ) { BrPrint(( BR_CRITICAL, "%ws: Hosted domain doesn't exist\n", HostedDomain )); } else { // // Find the specified network // Network = BrFindNetwork( DomainInfo, &TransportName ); if ( Network == NULL ) { BrPrint(( BR_CRITICAL, "%ws: Unbind cannot find transport: %ws\n", DomainInfo->DomUnicodeDomainName, Transport )); } else { if (LOCK_NETWORK(Network)) { // // Set the role to be PDC. // if ( TransportFlags & LMDR_TRANSPORT_PDC ) { // // If we think we're a BDC. Update our information. // if ( (Network->Flags & NETWORK_PDC) == 0 ) { Network->Flags |= NETWORK_PDC; // // Make sure a GetMasterAnnouncement request is pending. // (VOID) PostGetMasterAnnouncement ( Network ); // Force an election to let the PDC win (VOID) BrElectMasterOnNet( Network, (PVOID)EVENT_BROWSER_ELECTION_SENT_ROLE_CHANGED ); } // // Set the role to BDC. // } else { // // We think we're the PDC. Update our information. // if ( Network->Flags & NETWORK_PDC ) { Network->Flags &= ~NETWORK_PDC; // Force an election to let the PDC win (VOID) BrElectMasterOnNet( Network, (PVOID)EVENT_BROWSER_ELECTION_SENT_ROLE_CHANGED ); } } UNLOCK_NETWORK(Network); } BrDereferenceNetwork( Network ); } BrDereferenceDomain( DomainInfo ); } break; // // Ignore new Ip Addresses // case NlPnpNewIpAddress: BrPrint(( BR_NETWORK, "Received IP address change PNP opcode 0x%lx on transport: %ws\n", TransportFlags, Transport )); break; default: BrPrint(( BR_CRITICAL, "Received invalid PNP opcode 0x%x on transport: %ws\n", PnpOpcode, Transport )); break; } try_exit:NOTHING; } finally { MIDL_user_free(Context); // // Always finish by asking for another PNP message. // // For PNP, it is fine to only process a single PNP message at a time. // If this message mechanism starts being used for other purposes, // we may want to immediately ask for another message upon receipt // of this one. // while ((NetStatus = PostWaitForPnp()) != NERR_Success ) { BrPrint(( BR_CRITICAL, "Unable to re-issue PostWaitForPnp request (waiting): %ld\n", NetStatus)); // // On error, wait a second before returning. This ensures we don't // consume the system in an infinite loop. We don't shutdown // because the error might be a temporary low memory condition. // NetStatus = WaitForSingleObject( BrGlobalData.TerminateNowEvent, 1000 ); if ( NetStatus != WAIT_TIMEOUT ) { BrPrint(( BR_CRITICAL, "Not re-issuing PostWaitForPnp request since we're terminating: %ld\n", NetStatus)); break; } } } return; } NET_API_STATUS PostWaitForPnp ( VOID ) /*++ Routine Description: This function issues and async call to the bowser driver asking it to inform us of PNP events. Arguments: None. Return Value: Status - The status of the operation. --*/ { return BrIssueAsyncBrowserIoControl( NULL, IOCTL_LMDR_BROWSER_PNP_READ, HandlePnpMessage, NULL ); }