|
|
/*++
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);
if ( RequestPacket->Parameters.GetMasterName.MasterNameLength > MAXIMUM_FILENAME_LENGTH ) { MIDL_user_free(Context); return ERROR_BUFFER_OVERFLOW; } 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);
BrPrint( (BR_CLIENT_OP, "BrGetLocalBrowseList: returning list got from Bowser for domain <%ws>, network <%ws>\n", DomainName, Network->NetworkName.Buffer ));
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;
if ( wcslen(ServerName) > LM20_CNLEN ) { return ERROR_BUFFER_OVERFLOW; } 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);
if ( ( STRLEN(TransportName) > MAXIMUM_FILENAME_LENGTH ) || ( STRLEN(EmulatedComputerName) > CNLEN ) ) { return ERROR_BUFFER_OVERFLOW; } 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);
if ( RequestPacket->Parameters.Unbind.TransportNameLength > MAXIMUM_FILENAME_LENGTH ) { return ERROR_BUFFER_OVERFLOW; } 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 = NULL; 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 ); }
|