/*++ Copyright (c) 1990 Microsoft Corporation Module Name: bowtdi.c Abstract: This module implements all of the routines that interface with the TDI transport for NT Author: Larry Osterman (LarryO) 21-Jun-1990 Revision History: 21-Jun-1990 LarryO Created --*/ #include "precomp.h" #include #include #include #pragma hdrstop typedef struct _ENUM_TRANSPORTS_CONTEXT { PVOID OutputBuffer; PVOID OutputBufferEnd; PVOID LastOutputBuffer; // Points to the last entry in the list. ULONG OutputBufferSize; ULONG EntriesRead; ULONG TotalEntries; ULONG TotalBytesNeeded; ULONG OutputBufferDisplacement; } ENUM_TRANSPORTS_CONTEXT, *PENUM_TRANSPORTS_CONTEXT; NTSTATUS EnumerateTransportsWorker( IN PTRANSPORT Transport, IN OUT PVOID Ctx ); ERESOURCE BowserTransportDatabaseResource = {0}; // // // Forward definitions of local routines. // NTSTATUS BowserpTdiSetEventHandler ( IN PDEVICE_OBJECT DeviceObject, IN PFILE_OBJECT FileObject, IN ULONG EventType, IN PVOID EventHandler, IN PVOID TransportName ); NTSTATUS BowserDetermineProviderInformation( IN PUNICODE_STRING TransportName, OUT PTDI_PROVIDER_INFO ProviderInfo, OUT PULONG IpSubnetNumber ); NTSTATUS UnbindTransportWorker( IN PTRANSPORT Transport, IN OUT PVOID Ctx ); NTSTATUS BowserpTdiRemoveAddresses( IN PTRANSPORT Transport ); VOID BowserpFreeTransport( IN PTRANSPORT Transport ); VOID BowserDeleteTransport( IN PTRANSPORT Transport ); NTSTATUS BowserSubmitTdiRequest ( IN PFILE_OBJECT FileObject, IN PIRP Irp ); NTSTATUS BowserCompleteTdiRequest ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); NTSTATUS CompleteSendDatagram ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Ctx ); NTSTATUS BowserEnableIpxDatagramSocket( IN PTRANSPORT Transport ); NTSTATUS BowserOpenNetbiosAddress( IN PPAGED_TRANSPORT_NAME PagedTransportName, IN PTRANSPORT Transport, IN PBOWSER_NAME Name ); VOID BowserCloseNetbiosAddress( IN PTRANSPORT_NAME TransportName ); VOID BowserCloseAllNetbiosAddresses( IN PTRANSPORT Transport ); NTSTATUS BowserSendDatagram ( IN PTRANSPORT Transport, IN PVOID RecipientAddress, IN DGRECEIVER_NAME_TYPE NameType, IN PVOID Buffer, IN ULONG BufferLength, IN BOOLEAN WaitForCompletion, IN PSTRING DestinationAddress OPTIONAL, IN BOOLEAN IsHostAnnouncment ); NTSTATUS OpenIpxSocket ( OUT PHANDLE Handle, OUT PFILE_OBJECT *FileObject, OUT PDEVICE_OBJECT *DeviceObject, IN PUNICODE_STRING DeviceName, IN USHORT Socket ); NTSTATUS BowserIssueTdiAction ( IN PDEVICE_OBJECT DeviceObject, IN PFILE_OBJECT FileObject, IN PVOID Action, IN ULONG ActionSize ); NTSTATUS GetNetworkAddress ( IN PTRANSPORT_NAME TransportName ); NTSTATUS BowserIssueTdiQuery( IN PFILE_OBJECT FileObject, IN PDEVICE_OBJECT DeviceObject, IN PCHAR Buffer, IN ULONG BufferSize, IN USHORT QueryType ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, BowserTdiAllocateTransport) #pragma alloc_text(PAGE, BowserUnbindFromAllTransports) #pragma alloc_text(PAGE, UnbindTransportWorker) #pragma alloc_text(PAGE, BowserFreeTransportByName) #pragma alloc_text(PAGE, BowserEnumerateTransports) #pragma alloc_text(PAGE, EnumerateTransportsWorker) #pragma alloc_text(PAGE, BowserDereferenceTransport) #pragma alloc_text(PAGE, BowserCreateTransportName) #pragma alloc_text(PAGE, BowserpTdiRemoveAddresses) #pragma alloc_text(PAGE, BowserFindTransportName) #pragma alloc_text(PAGE, BowserFreeTransportName) #pragma alloc_text(PAGE, BowserDeleteTransport) #pragma alloc_text(PAGE, BowserpFreeTransport) #pragma alloc_text(PAGE, BowserpTdiSetEventHandler) #pragma alloc_text(PAGE, BowserBuildTransportAddress) #pragma alloc_text(PAGE, BowserUpdateProviderInformation) #pragma alloc_text(PAGE, BowserDetermineProviderInformation) #pragma alloc_text(PAGE, BowserFindTransport) #pragma alloc_text(PAGE, BowserForEachTransport) #pragma alloc_text(PAGE, BowserForEachTransportName) #pragma alloc_text(PAGE, BowserDeleteTransportNameByName) #pragma alloc_text(PAGE, BowserSubmitTdiRequest) #pragma alloc_text(PAGE, BowserSendDatagram) #pragma alloc_text(PAGE, BowserSendSecondClassMailslot) #pragma alloc_text(PAGE, BowserSendRequestAnnouncement) #pragma alloc_text(INIT, BowserpInitializeTdi) #pragma alloc_text(PAGE, BowserpUninitializeTdi) #pragma alloc_text(PAGE, BowserDereferenceTransportName) #pragma alloc_text(PAGE, BowserEnableIpxDatagramSocket) #pragma alloc_text(PAGE, BowserOpenNetbiosAddress) #pragma alloc_text(PAGE, BowserCloseNetbiosAddress) #pragma alloc_text(PAGE, BowserCloseAllNetbiosAddresses) #pragma alloc_text(PAGE, OpenIpxSocket) #pragma alloc_text(PAGE, BowserIssueTdiAction) #pragma alloc_text(PAGE, BowserIssueTdiQuery) #pragma alloc_text(PAGE4BROW, BowserCompleteTdiRequest) #pragma alloc_text(PAGE4BROW, CompleteSendDatagram) #endif // // Flag to indicate that a network isn't an IP network // #define BOWSER_NON_IP_SUBNET 0xFFFFFFFF NTSTATUS BowserTdiAllocateTransport ( PUNICODE_STRING TransportName ) /*++ Routine Description: This routine will allocate a transport descriptor and bind the bowser to the transport. Arguments: PUNICODE_STRING TransportName - Supplies the name of the transport provider Return Value: NTSTATUS - Status of operation. --*/ { NTSTATUS Status; PTRANSPORT NewTransport; BOOLEAN NameResourceAcquired = FALSE; PAGED_CODE(); // DbgBreakPoint(); dprintf(DPRT_TDI, ("BowserTdiAllocateTransport: %wZ\n", TransportName)); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); NewTransport = BowserFindTransport(TransportName); if (NewTransport == NULL) { PLIST_ENTRY NameEntry; PPAGED_TRANSPORT PagedTransport = NULL; NewTransport = ALLOCATE_POOL(NonPagedPool, sizeof(TRANSPORT), POOL_TRANSPORT); if (NewTransport == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnStatus; } RtlZeroMemory( NewTransport, sizeof(TRANSPORT) ); PagedTransport = NewTransport->PagedTransport = ALLOCATE_POOL(PagedPool, sizeof(PAGED_TRANSPORT) + max(sizeof(TA_IPX_ADDRESS), sizeof(TA_NETBIOS_ADDRESS)), POOL_PAGED_TRANSPORT); if (NewTransport->PagedTransport == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnStatus; } RtlZeroMemory( PagedTransport, sizeof(PAGED_TRANSPORT) + max(sizeof(TA_IPX_ADDRESS), sizeof(TA_NETBIOS_ADDRESS)) ); PagedTransport->NonPagedTransport = NewTransport; PagedTransport->NumberOfServersInTable = 0; NewTransport->Signature = STRUCTURE_SIGNATURE_TRANSPORT; NewTransport->Size = sizeof(TRANSPORT); PagedTransport->Signature = STRUCTURE_SIGNATURE_PAGED_TRANSPORT; PagedTransport->Size = sizeof(PAGED_TRANSPORT); NewTransport->ReferenceCount = 1; ExInitializeResource(&NewTransport->BrowserServerListResource); NewTransport->BrowserServerListToken = 0; NewTransport->BowserBackupList = NULL; NewTransport->IpxSocketFileObject = NULL; NewTransport->IpxSocketDeviceObject = NULL; KeInitializeEvent(&NewTransport->GetBackupListComplete, NotificationEvent, TRUE); ExInitializeResource(&NewTransport->Lock); BowserInitializeIrpQueue(&NewTransport->BecomeBackupQueue); BowserInitializeIrpQueue(&NewTransport->BecomeMasterQueue); BowserInitializeIrpQueue(&NewTransport->FindMasterQueue); BowserInitializeIrpQueue(&NewTransport->WaitForMasterAnnounceQueue); BowserInitializeIrpQueue(&NewTransport->WaitForNewMasterNameQueue); BowserInitializeIrpQueue(&NewTransport->ChangeRoleQueue); BowserInitializeTimer(&NewTransport->ElectionTimer); BowserInitializeTimer(&NewTransport->FindMasterTimer); PagedTransport->GlobalNext.Flink = NULL; PagedTransport->GlobalNext.Blink = NULL; InitializeListHead(&PagedTransport->NameChain); PagedTransport->MasterName.Buffer = NULL; PagedTransport->MasterBrowserAddress.Buffer = (PCHAR)(PagedTransport+1); PagedTransport->MasterBrowserAddress.MaximumLength = max(sizeof(TA_IPX_ADDRESS), sizeof(TA_NETBIOS_ADDRESS)); PagedTransport->TransportName.Buffer = ALLOCATE_POOL(PagedPool, TransportName->MaximumLength+sizeof(WCHAR), POOL_TRANSPORT_NAME); if (PagedTransport->TransportName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnStatus; } PagedTransport->TransportName.MaximumLength = TransportName->MaximumLength; RtlCopyUnicodeString(&PagedTransport->TransportName, TransportName); PagedTransport->TransportName.Buffer[(TransportName->Length/sizeof(WCHAR))] = UNICODE_NULL; PagedTransport->MasterName.Buffer = ALLOCATE_POOL(PagedPool, (LM20_CNLEN+1)*sizeof(WCHAR), POOL_MASTERNAME); if (PagedTransport->MasterName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnStatus; } NewTransport->ComputerName = NULL; NewTransport->PrimaryDomain = NULL; NewTransport->MasterBrowser = NULL; NewTransport->BrowserElection = NULL; PagedTransport->MasterName.MaximumLength = (LM20_CNLEN+1)*sizeof(WCHAR); PagedTransport->MasterName.Length = 0; PagedTransport->BrowserServerListLength = 0; PagedTransport->BrowserServerListBuffer = NULL; PagedTransport->NumberOfBrowserServers = 0; PagedTransport->Role = None; PagedTransport->ServiceStatus = 0; PagedTransport->IpxSocketHandle = NULL; PagedTransport->IpSubnetNumber = BOWSER_NON_IP_SUBNET; PagedTransport->DisabledTransport = TRUE; PagedTransport->PointToPoint = FALSE; // // Initialize the time we last saw an election packet. // PagedTransport->LastElectionSeen = 0; INITIALIZE_ANNOUNCE_DATABASE(NewTransport); RtlInitializeGenericTable(&PagedTransport->AnnouncementTable, BowserCompareAnnouncement, BowserAllocateAnnouncement, BowserFreeAnnouncement, NULL); RtlInitializeGenericTable(&PagedTransport->DomainTable, BowserCompareAnnouncement, BowserAllocateAnnouncement, BowserFreeAnnouncement, NULL); InitializeListHead(&PagedTransport->BackupBrowserList); PagedTransport->NumberOfBackupServerListEntries = 0; // // Get info from the provider // (e.g., RAS, Wannish, DatagramSize) Status= BowserUpdateProviderInformation( PagedTransport ); if (!NT_SUCCESS(Status)) { goto ReturnStatus; } PagedTransport->Flags = 0; // // We ignore any and all errors that occur when we open the IPX socket. // // // Open the IPX mailslot socket. // Status = OpenIpxSocket( &NewTransport->PagedTransport->IpxSocketHandle, &NewTransport->IpxSocketFileObject, &NewTransport->IpxSocketDeviceObject, &NewTransport->PagedTransport->TransportName, SMB_IPX_MAILSLOT_SOCKET ); if ( NT_SUCCESS(Status) ) { PagedTransport->Flags |= DIRECT_HOST_IPX; // We'll use type 20 packets to increase the reach of broadcasts // so don't treat this as a wannish protocol. PagedTransport->Wannish = FALSE; } // // Create the names for this transport. // InsertHeadList(&BowserTransportHead, &PagedTransport->GlobalNext); for (NameEntry = BowserNameHead.Flink; NameEntry != &BowserNameHead ; NameEntry = NameEntry->Flink) { PBOWSER_NAME Name = CONTAINING_RECORD(NameEntry, BOWSER_NAME, GlobalNext); // // If the name was added on all transports, // add it on this transport, too. // if ( Name->AddedOnAllTransports ) { if (!NT_SUCCESS(Status = BowserCreateTransportName(NewTransport, Name))) { goto ReturnStatus; } } } // // Start receiving broadcasts on IPX now that the names exist. // if ( NewTransport->PagedTransport->Flags & DIRECT_HOST_IPX ) { BowserEnableIpxDatagramSocket(NewTransport); } } else { BowserDereferenceTransport( NewTransport ); } Status = STATUS_SUCCESS; ReturnStatus: ExReleaseResource(&BowserTransportDatabaseResource); if (!NT_SUCCESS(Status)) { // // Delete the transport. // if ( NewTransport != NULL ) { BowserReferenceTransport( NewTransport ); BowserDeleteTransport (NewTransport); BowserDereferenceTransport( NewTransport ); } } return Status; } NTSTATUS BowserUnbindFromAllTransports( VOID ) { NTSTATUS Status; PAGED_CODE(); Status = BowserForEachTransport(UnbindTransportWorker, NULL); #if DBG if (NT_SUCCESS(Status)) { ASSERT (IsListEmpty(&BowserTransportHead)); } #endif return Status; } NTSTATUS UnbindTransportWorker( IN PTRANSPORT Transport, IN OUT PVOID Ctx ) /*++ Routine Description: This routine is the worker routine for BowserUnbindFromAllTransports. Arguments: None. Return Value: None. --*/ { PAGED_CODE(); // // Dereference the reference caused by the transport bind. // BowserDeleteTransport(Transport); // // Return success. We're done. // return(STATUS_SUCCESS); UNREFERENCED_PARAMETER(Ctx); } NTSTATUS BowserFreeTransportByName ( IN PUNICODE_STRING TransportName ) /*++ Routine Description: This routine will deallocate an allocated transport Arguments: IN PUNICODE_STRING TransportName - Supplies a pointer to the name of the transport to free Return Value: None. --*/ { PTRANSPORT Transport; PAGED_CODE(); dprintf(DPRT_TDI, ("BowserFreeTransportByName: Remove transport %wZ\n", TransportName)); Transport = BowserFindTransport(TransportName); if (Transport == NULL) { return STATUS_OBJECT_NAME_NOT_FOUND; } // // Remove the reference from the binding. // BowserDeleteTransport(Transport); // // Remove the reference from the FindTransport. // BowserDereferenceTransport(Transport); return STATUS_SUCCESS; } NTSTATUS BowserEnumerateTransports ( OUT PVOID OutputBuffer, OUT ULONG OutputBufferLength, IN OUT PULONG EntriesRead, IN OUT PULONG TotalEntries, IN OUT PULONG TotalBytesNeeded, IN ULONG OutputBufferDisplacement) /*++ Routine Description: This routine will enumerate the servers in the bowsers current announcement table. Arguments: IN ULONG ServerTypeMask - Mask of servers to return. IN PUNICODE_STRING DomainName OPTIONAL - Domain to filter (all if not specified) OUT PVOID OutputBuffer - Buffer to fill with server info. IN ULONG OutputBufferSize - Filled in with size of buffer. OUT PULONG EntriesRead - Filled in with the # of entries returned. OUT PULONG TotalEntries - Filled in with the total # of entries. OUT PULONG TotalBytesNeeded - Filled in with the # of bytes needed. Return Value: None. --*/ { PVOID OutputBufferEnd; NTSTATUS Status; ENUM_TRANSPORTS_CONTEXT Context; PAGED_CODE(); OutputBufferEnd = (PCHAR)OutputBuffer+OutputBufferLength; Context.EntriesRead = 0; Context.TotalEntries = 0; Context.TotalBytesNeeded = 0; try { Context.OutputBufferSize = OutputBufferLength; Context.OutputBuffer = OutputBuffer; Context.OutputBufferDisplacement = OutputBufferDisplacement; Context.OutputBufferEnd = OutputBufferEnd; Context.LastOutputBuffer = OutputBuffer; dprintf(DPRT_FSCTL, ("Enumerate Transports: Buffer: %lx, BufferSize: %lx, BufferEnd: %lx\n", OutputBuffer, OutputBufferLength, OutputBufferEnd)); Status = BowserForEachTransport(EnumerateTransportsWorker, &Context); *EntriesRead = Context.EntriesRead; *TotalEntries = Context.TotalEntries; *TotalBytesNeeded = Context.TotalBytesNeeded; if (*EntriesRead != 0) { ((PLMDR_TRANSPORT_LIST )Context.LastOutputBuffer)->NextEntryOffset = 0; } dprintf(DPRT_FSCTL, ("TotalEntries: %lx EntriesRead: %lx, TotalBytesNeeded: %lx\n", *TotalEntries, *EntriesRead, *TotalBytesNeeded)); if (*EntriesRead == *TotalEntries) { try_return(Status = STATUS_SUCCESS); } else { try_return(Status = STATUS_MORE_ENTRIES); } try_exit:NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } return Status; } NTSTATUS EnumerateTransportsWorker( IN PTRANSPORT Transport, IN OUT PVOID Ctx ) /*++ Routine Description: This routine is the worker routine for BowserEnumerateTransports. It is called for each of the serviced transports in the bowser and returns the size needed to enumerate the servers received on each transport. Arguments: None. Return Value: None. --*/ { PENUM_TRANSPORTS_CONTEXT Context = Ctx; PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport; PAGED_CODE(); Context->TotalEntries += 1; if ((ULONG)Context->OutputBufferEnd - (ULONG)Context->OutputBuffer > sizeof(LMDR_TRANSPORT_LIST)+PagedTransport->TransportName.Length) { PLMDR_TRANSPORT_LIST TransportEntry = (PLMDR_TRANSPORT_LIST)Context->OutputBuffer; Context->LastOutputBuffer = Context->OutputBuffer; Context->EntriesRead += 1; RtlCopyMemory(TransportEntry->TransportName, PagedTransport->TransportName.Buffer, Transport->PagedTransport->TransportName.Length+sizeof(WCHAR)); // // Null terminate the transport name. // TransportEntry->TransportName[PagedTransport->TransportName.Length/sizeof(WCHAR)] = '\0'; TransportEntry->TransportNameLength = PagedTransport->TransportName.Length; TransportEntry->Flags = 0; if (PagedTransport->Wannish) { TransportEntry->Flags |= LMDR_TRANSPORT_WANNISH; } if (PagedTransport->PointToPoint) { TransportEntry->Flags |= LMDR_TRANSPORT_RAS; } if (PagedTransport->Flags & DIRECT_HOST_IPX) { TransportEntry->Flags |= LMDR_TRANSPORT_IPX; } TransportEntry->NextEntryOffset = PagedTransport->TransportName.Length+sizeof(LMDR_TRANSPORT_LIST)+sizeof(WCHAR); TransportEntry->NextEntryOffset = ROUND_UP_COUNT(TransportEntry->NextEntryOffset, ALIGN_DWORD); (PUCHAR)(Context->OutputBuffer) += TransportEntry->NextEntryOffset; } Context->TotalBytesNeeded += sizeof(LMDR_TRANSPORT_LIST)+PagedTransport->TransportName.Length; return(STATUS_SUCCESS); } VOID BowserReferenceTransport( IN PTRANSPORT Transport ) { InterlockedIncrement(&Transport->ReferenceCount); dprintf(DPRT_TDI, ("Reference transport %lx. Count now %lx\n", Transport, Transport->ReferenceCount)); } VOID BowserDereferenceTransport( IN PTRANSPORT Transport ) { LONG Result; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); if (Transport->ReferenceCount == 0) { InternalError(("Transport Reference Count mismatch\n")); } Result = InterlockedDecrement(&Transport->ReferenceCount); dprintf(DPRT_TDI, ("Dereference transport %lx. Count now %lx\n", Transport, Transport->ReferenceCount)); if (Result == 0) { // // And free up the transport itself. // BowserpFreeTransport(Transport); } ExReleaseResource(&BowserTransportDatabaseResource); } NTSTATUS BowserCreateTransportName ( IN PTRANSPORT Transport, IN PBOWSER_NAME Name ) /*++ Routine Description: This routine creates a transport address object. Arguments: IN PTRANSPORT Transport - Supplies a transport structure describing the transport address object to be created. Return Value: NTSTATUS - Status of resulting operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; PTRANSPORT_NAME TransportName = NULL; PPAGED_TRANSPORT_NAME PagedTransportName = NULL; PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport; BOOLEAN ResourceAcquired = FALSE; PAGED_CODE(); ASSERT(Transport->Signature == STRUCTURE_SIGNATURE_TRANSPORT); dprintf(DPRT_TDI, ("BowserCreateTransportName. Transport %lx, Name %lx\n", Transport, Name)); // // Link the transport_name structure into the transport list. // ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); ResourceAcquired = TRUE; TransportName = BowserFindTransportName(Transport, Name); if (TransportName != NULL) { ExReleaseResource(&BowserTransportDatabaseResource); return(STATUS_SUCCESS); } #ifdef notdef // // Simply don't allocate certain names if the transport is disabled // if ( PagedTransport->DisabledTransport ) { if ( Name->NameType == PrimaryDomainBrowser ) { ExReleaseResource(&BowserTransportDatabaseResource); return STATUS_SUCCESS; } } #endif // notdef ASSERT (IoGetCurrentProcess() == BowserFspProcess); // // Allocate a structure to refer to this name on the transport // TransportName = ALLOCATE_POOL(NonPagedPool, sizeof(TRANSPORT_NAME) + max(sizeof(TA_NETBIOS_ADDRESS), sizeof(TA_IPX_ADDRESS)), POOL_TRANSPORTNAME); if (TransportName == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto error_cleanup; } TransportName->PagedTransportName = PagedTransportName = ALLOCATE_POOL(PagedPool, sizeof(PAGED_TRANSPORT_NAME), POOL_PAGED_TRANSPORTNAME); if (PagedTransportName == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto error_cleanup; } TransportName->Signature = STRUCTURE_SIGNATURE_TRANSPORTNAME; TransportName->Size = sizeof(TRANSPORT_NAME); TransportName->PagedTransportName = PagedTransportName; // This TransportName is considered to be referenced by the transport via // Transport->PagedTransport->NameChain. The Name->NameChain isn't // considered to be a reference. TransportName->ReferenceCount = 1; PagedTransportName->NonPagedTransportName = TransportName; PagedTransportName->Signature = STRUCTURE_SIGNATURE_PAGED_TRANSPORTNAME; PagedTransportName->Size = sizeof(PAGED_TRANSPORT_NAME); PagedTransportName->Name = Name; BowserReferenceName(Name); TransportName->Transport = Transport; // Don't reference the Transport. When the transport is unbound, we'll // make sure all the transport names are removed first. // BowserReferenceTransport(Transport); PagedTransportName->Handle = NULL; TransportName->FileObject = NULL; TransportName->DeviceObject = NULL; InsertHeadList(&Transport->PagedTransport->NameChain, &PagedTransportName->TransportNext); InsertHeadList(&Name->NameChain, &PagedTransportName->NameNext); // // If this is an OTHERDOMAIN, we want to process host announcements for // the domain, if it isn't, we want to wait until we become a master. // if (Name->NameType == OtherDomain) { BowserReferenceDiscardableCode( BowserDiscardableCodeSection ); DISCARDABLE_CODE( BowserDiscardableCodeSection ); TransportName->ProcessHostAnnouncements = TRUE; } else { TransportName->ProcessHostAnnouncements = FALSE; } // // If this name is one of our special names, we want to remember it in // the transport block. // if (Name->NameType == ComputerName) { Transport->ComputerName = TransportName; } if (Name->NameType == PrimaryDomain) { Transport->PrimaryDomain = TransportName; } if (Name->NameType == MasterBrowser) { Transport->MasterBrowser = TransportName; } if (Name->NameType == BrowserElection) { Transport->BrowserElection = TransportName; } TransportName->TransportAddress.Buffer = (PCHAR)(TransportName+1); // // Figure out what this name is, so we can match against it when // a datagram is received. // Status = BowserBuildTransportAddress(&TransportName->TransportAddress, &Name->Name, Name->NameType, Transport); if (!NT_SUCCESS(Status)) { goto error_cleanup; } TransportName->NameType = Name->NameType; #if DBG if (Name->NameType == MasterBrowser) { // // make sure that we never become a master without locking the discardable code. // DISCARDABLE_CODE( BowserDiscardableCodeSection ); } #endif ExReleaseResource(&BowserTransportDatabaseResource); ResourceAcquired = FALSE; // // On non direct host IPX transports, we need to add the name now. // if (!FlagOn(Transport->PagedTransport->Flags, DIRECT_HOST_IPX)) { Status = BowserOpenNetbiosAddress(PagedTransportName, Transport, Name); if (!NT_SUCCESS(Status)) { goto error_cleanup; } } return Status; error_cleanup: dprintf(DPRT_TDI, ("BowserCreateTransportName failed. Name: %lx, Status %lx\n", TransportName, Status)); if (TransportName != NULL) { BowserDereferenceTransportName(TransportName); } if (ResourceAcquired) { ExReleaseResource(&BowserTransportDatabaseResource); } return Status; } NTSTATUS BowserOpenNetbiosAddress( IN PPAGED_TRANSPORT_NAME PagedTransportName, IN PTRANSPORT Transport, IN PBOWSER_NAME Name ) { NTSTATUS Status; PFILE_FULL_EA_INFORMATION EABuffer = NULL; PTRANSPORT_NAME TransportName = PagedTransportName->NonPagedTransportName; OBJECT_ATTRIBUTES AddressAttributes; IO_STATUS_BLOCK IoStatusBlock; PAGED_CODE( ); try { // // Now create the address object for this name. // EABuffer = ALLOCATE_POOL(PagedPool, sizeof(FILE_FULL_EA_INFORMATION)-1 + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + sizeof(TA_NETBIOS_ADDRESS), POOL_EABUFFER); if (EABuffer == NULL) { try_return(Status = STATUS_INSUFFICIENT_RESOURCES) } EABuffer->NextEntryOffset = 0; EABuffer->Flags = 0; EABuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; EABuffer->EaValueLength = sizeof(TA_NETBIOS_ADDRESS); ASSERT (TransportName->TransportAddress.Length == sizeof(TA_NETBIOS_ADDRESS)); RtlCopyMemory(EABuffer->EaName, TdiTransportAddress, EABuffer->EaNameLength+1); RtlCopyMemory(&EABuffer->EaName[TDI_TRANSPORT_ADDRESS_LENGTH+1], TransportName->TransportAddress.Buffer, EABuffer->EaValueLength); dprintf(DPRT_TDI, ("Create endpoint of \"%Z\" (%lx)", &Transport->PagedTransport->TransportName, TransportName)); InitializeObjectAttributes (&AddressAttributes, &Transport->PagedTransport->TransportName, // Name OBJ_CASE_INSENSITIVE,// Attributes NULL, // RootDirectory NULL); // SecurityDescriptor Status = ZwCreateFile(&PagedTransportName->Handle, // Handle GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &AddressAttributes, // Object Attributes &IoStatusBlock, // Final I/O status block NULL, // Allocation Size FILE_ATTRIBUTE_NORMAL, // Normal attributes FILE_SHARE_READ,// Sharing attributes FILE_OPEN_IF, // Create disposition 0, // CreateOptions EABuffer, // EA Buffer FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + sizeof(TA_NETBIOS_ADDRESS)); // EA length FREE_POOL(EABuffer); EABuffer = NULL; if (!NT_SUCCESS(Status)) { try_return(Status); } if (!NT_SUCCESS(Status = IoStatusBlock.Status)) { try_return(Status); } // // Obtain a referenced pointer to the file object. // Status = ObReferenceObjectByHandle ( PagedTransportName->Handle, 0, *IoFileObjectType, KernelMode, (PVOID *)&TransportName->FileObject, NULL ); if (!NT_SUCCESS(Status)) { try_return(Status); } // // Get the address of the device object for the endpoint. // TransportName->DeviceObject = IoGetRelatedDeviceObject(TransportName->FileObject); Status = BowserpTdiSetEventHandler(TransportName->DeviceObject, TransportName->FileObject, TDI_EVENT_RECEIVE_DATAGRAM, (PVOID) BowserTdiReceiveDatagramHandler, TransportName); dprintf(DPRT_TDI, ("BowserCreateTransportName Succeeded. Name: %lx, Handle: %lx\n", TransportName, PagedTransportName->Handle)); try_exit:NOTHING; } finally { if (EABuffer != NULL) { FREE_POOL(EABuffer); } if (!NT_SUCCESS(Status)) { BowserCloseNetbiosAddress( TransportName ); } } return Status; } VOID BowserCloseNetbiosAddress( IN PTRANSPORT_NAME TransportName ) /*++ Routine Description: Closes the Netbios Address for a transport name. Arguments: TransportName - Transport Name whose Netbios address is to be closed. Return Value: None. --*/ { NTSTATUS Status; // PTRANSPORT Transport = TransportName->Transport; PPAGED_TRANSPORT_NAME PagedTransportName = TransportName->PagedTransportName; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); if ( TransportName->FileObject != NULL ) { ObDereferenceObject( TransportName->FileObject ); TransportName->FileObject = NULL; } if (PagedTransportName) { if ( PagedTransportName->Handle != NULL ) { BOOLEAN ProcessAttached = FALSE; if (IoGetCurrentProcess() != BowserFspProcess) { KeAttachProcess(BowserFspProcess); ProcessAttached = TRUE; } Status = ZwClose( PagedTransportName->Handle ); if (ProcessAttached) { KeDetachProcess(); } if (!NT_SUCCESS(Status)) { dprintf(DPRT_TDI, ("BowserCloseNetbiosAddress: Free name %lx failed: %X, %lx Handle: %lx\n", TransportName, Status, PagedTransportName->Handle)); } PagedTransportName->Handle = NULL; } } ExReleaseResource(&BowserTransportDatabaseResource); } VOID BowserCloseAllNetbiosAddresses( IN PTRANSPORT Transport ) /*++ Routine Description: This routine closes all the Netbios address this transport has open to the TDI driver. Arguments: Transport - The transport whose Netbios addresses are to be closed. Return Value: NTSTATUS - Status of resulting operation. --*/ { PLIST_ENTRY NameEntry; PLIST_ENTRY NextEntry; PAGED_CODE(); dprintf(DPRT_TDI, ("BowserCloseAllNetbiosAddresses: Close addresses for transport %lx\n", Transport)); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); for (NameEntry = Transport->PagedTransport->NameChain.Flink; NameEntry != &Transport->PagedTransport->NameChain; NameEntry = NextEntry) { PPAGED_TRANSPORT_NAME PagedTransportName = CONTAINING_RECORD(NameEntry, PAGED_TRANSPORT_NAME, TransportNext); PTRANSPORT_NAME TransportName = PagedTransportName->NonPagedTransportName; NextEntry = NameEntry->Flink; BowserCloseNetbiosAddress(TransportName); } ExReleaseResource(&BowserTransportDatabaseResource); return; } NTSTATUS BowserEnableIpxDatagramSocket( IN PTRANSPORT Transport ) { NTSTATUS status; NWLINK_ACTION action; PAGED_CODE( ); // // Put the endpoint in broadcast reception mode. // action.Header.TransportId = 'XPIM'; // "MIPX" action.Header.ActionCode = 0; action.Header.Reserved = 0; action.OptionType = TRUE; action.BufferLength = sizeof(action.Option); action.Option = MIPX_RCVBCAST; status = BowserIssueTdiAction( Transport->IpxSocketDeviceObject, Transport->IpxSocketFileObject, (PCHAR)&action, sizeof(action) ); if ( !NT_SUCCESS(status) ) { goto cleanup; } // // Set the default packet type to 20 to force all browser packets // through routers. // action.Header.TransportId = 'XPIM'; // "MIPX" action.Header.ActionCode = 0; action.Header.Reserved = 0; action.OptionType = TRUE; action.BufferLength = sizeof(action.Option); action.Option = MIPX_SETSENDPTYPE; action.Data[0] = IPX_BROADCAST_PACKET; status = BowserIssueTdiAction( Transport->IpxSocketDeviceObject, Transport->IpxSocketFileObject, (PCHAR)&action, sizeof(action) ); if ( !NT_SUCCESS(status) ) { goto cleanup; } // // Register the browser Receive Datagram event handler. // status = BowserpTdiSetEventHandler( Transport->IpxSocketDeviceObject, Transport->IpxSocketFileObject, TDI_EVENT_RECEIVE_DATAGRAM, BowserIpxDatagramHandler, Transport ); if ( !NT_SUCCESS(status) ) { // INTERNAL_ERROR( // ERROR_LEVEL_EXPECTED, // "OpenNonNetbiosAddress: set receive datagram event handler failed: %X", // status, // NULL // ); // SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status ); goto cleanup; } return STATUS_SUCCESS; // // Out-of-line error cleanup. // cleanup: // // Something failed. Clean up as appropriate. // if ( Transport->IpxSocketFileObject != NULL ) { ObDereferenceObject( Transport->IpxSocketFileObject ); Transport->IpxSocketFileObject = NULL; } if ( Transport->PagedTransport->IpxSocketHandle != NULL ) { ZwClose( Transport->PagedTransport->IpxSocketHandle ); Transport->PagedTransport->IpxSocketHandle = NULL; } return status; } NTSTATUS OpenIpxSocket ( OUT PHANDLE Handle, OUT PFILE_OBJECT *FileObject, OUT PDEVICE_OBJECT *DeviceObject, IN PUNICODE_STRING DeviceName, IN USHORT Socket ) { NTSTATUS status; ULONG length; PFILE_FULL_EA_INFORMATION ea; TA_IPX_ADDRESS ipxAddress; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK iosb; CHAR buffer[sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + sizeof(TA_IPX_ADDRESS)]; PAGED_CODE( ); // // Build the IPX socket address. // length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + sizeof(TA_IPX_ADDRESS); ea = (PFILE_FULL_EA_INFORMATION)buffer; ea->NextEntryOffset = 0; ea->Flags = 0; ea->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; ea->EaValueLength = sizeof (TA_IPX_ADDRESS); RtlCopyMemory( ea->EaName, TdiTransportAddress, ea->EaNameLength + 1 ); // // Create a copy of the NETBIOS address descriptor in a local // first, in order to avoid alignment problems. // ipxAddress.TAAddressCount = 1; ipxAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; ipxAddress.Address[0].AddressLength = sizeof (TDI_ADDRESS_IPX); ipxAddress.Address[0].Address[0].NetworkAddress = 0; RtlZeroMemory(ipxAddress.Address[0].Address[0].NodeAddress, sizeof(ipxAddress.Address[0].Address[0].NodeAddress)); ipxAddress.Address[0].Address[0].Socket = Socket; RtlCopyMemory( &ea->EaName[ea->EaNameLength + 1], &ipxAddress, sizeof(TA_IPX_ADDRESS) ); InitializeObjectAttributes( &objectAttributes, DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = NtCreateFile ( Handle, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access &objectAttributes, // object attributes &iosb, // returned status information NULL, // block size (unused) 0, // file attributes FILE_SHARE_READ | FILE_SHARE_WRITE, // share access FILE_CREATE, // create disposition 0, // create options buffer, // EA buffer length // EA length ); if ( !NT_SUCCESS(status) ) { // KdPrint(( "Status of opening ipx socket %x on %wZ is %x\n", // Socket, DeviceName, status )); return status; } // KdPrint(( "IPX socket %x opened!\n", Socket )); status = ObReferenceObjectByHandle ( *Handle, 0, *IoFileObjectType, KernelMode, (PVOID *)FileObject, NULL ); if (!NT_SUCCESS(status)) { ZwClose(*Handle); *Handle = NULL; } *DeviceObject = IoGetRelatedDeviceObject(*FileObject); return STATUS_SUCCESS; } // OpenIpxSocket VOID BowserReferenceTransportName( IN PTRANSPORT_NAME TransportName ) { InterlockedIncrement(&TransportName->ReferenceCount); } NTSTATUS BowserDereferenceTransportName( IN PTRANSPORT_NAME TransportName ) { NTSTATUS Status; LONG Result; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); if (TransportName->ReferenceCount == 0) { InternalError(("Transport Name Reference Count mismatch\n")); } Result = InterlockedDecrement(&TransportName->ReferenceCount); if (Result == 0) { Status = BowserFreeTransportName(TransportName); } else { Status = STATUS_SUCCESS; } ExReleaseResource(&BowserTransportDatabaseResource); return Status; } NTSTATUS BowserpTdiRemoveAddresses( IN PTRANSPORT Transport ) /*++ Routine Description: This routine removes all the transport names associated with a transport Arguments: IN PTRANSPORT Transport - Supplies a transport structure describing the transport address object to be created. Return Value: NTSTATUS - Status of resulting operation. --*/ { NTSTATUS Status; PLIST_ENTRY NameEntry; PLIST_ENTRY NextEntry; PAGED_CODE(); dprintf(DPRT_TDI, ("BowserpTdiRemoveAddresses: Remove addresses for transport %lx\n", Transport)); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); for (NameEntry = Transport->PagedTransport->NameChain.Flink; NameEntry != &Transport->PagedTransport->NameChain; NameEntry = NextEntry) { PPAGED_TRANSPORT_NAME PagedTransportName = CONTAINING_RECORD(NameEntry, PAGED_TRANSPORT_NAME, TransportNext); PTRANSPORT_NAME TransportName = PagedTransportName->NonPagedTransportName; // // Remove the TransportName from the list of transport names for // this transport. // NextEntry = NameEntry->Flink; RemoveEntryList(&PagedTransportName->TransportNext); PagedTransportName->TransportNext.Flink = NULL; PagedTransportName->TransportNext.Blink = NULL; // // Since we delinked it, we need to dereference it. // Status = BowserDereferenceTransportName(TransportName); if (!NT_SUCCESS(Status)) { ExReleaseResource(&BowserTransportDatabaseResource); return(Status); } } ExReleaseResource(&BowserTransportDatabaseResource); return STATUS_SUCCESS; } PTRANSPORT_NAME BowserFindTransportName( IN PTRANSPORT Transport, IN PBOWSER_NAME Name ) /*++ Routine Description: This routine looks up a given browser name to find its associated transport address. Arguments: IN PTRANSPORT Transport - Supplies a transport structure describing the transport address object to be created. IN PBOWSER_NAME Name - Supplies the name to look up. Return Value: The transport address found, or null. --*/ { PLIST_ENTRY NameEntry; PTRANSPORT_NAME RetValue = NULL; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); try { for (NameEntry = Transport->PagedTransport->NameChain.Flink; NameEntry != &Transport->PagedTransport->NameChain; NameEntry = NameEntry->Flink) { PPAGED_TRANSPORT_NAME PagedTransportName = CONTAINING_RECORD(NameEntry, PAGED_TRANSPORT_NAME, TransportNext); PTRANSPORT_NAME TransportName = PagedTransportName->NonPagedTransportName; if (PagedTransportName->Name == Name) { try_return(RetValue = TransportName); } try_exit:NOTHING; } } finally { ExReleaseResource(&BowserTransportDatabaseResource); } return RetValue; } NTSTATUS BowserFreeTransportName( IN PTRANSPORT_NAME TransportName ) { PTRANSPORT Transport = TransportName->Transport; PBOWSER_NAME Name = NULL; PPAGED_TRANSPORT_NAME PagedTransportName = TransportName->PagedTransportName; PAGED_CODE(); dprintf(DPRT_TDI, ("BowserFreeTransportName: Free name %lx\n", TransportName)); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); // // Close the handle to the TDI driver. // BowserCloseNetbiosAddress( TransportName ); // // If we received a message which re-referenced this transport name, // just return now. We'll be back when the reference count gets // re-dereferenced to zero. // if ( TransportName->ReferenceCount != 0 ) { ExReleaseResource(&BowserTransportDatabaseResource); return STATUS_SUCCESS; } ASSERT (TransportName->ReferenceCount == 0); if (PagedTransportName) { // // If this transport name has not yet been delinked, // delink it. // if ( PagedTransportName->TransportNext.Flink != NULL ) { // This should only happen on a failed transport name creation. RemoveEntryList(&PagedTransportName->TransportNext); PagedTransportName->TransportNext.Flink = NULL; PagedTransportName->TransportNext.Blink = NULL; } RemoveEntryList(&PagedTransportName->NameNext); // // We're removing an OtherDomain - we can remove the reference to // the discardable code section that was applied when the name was // created. // if (PagedTransportName->Name->NameType == OtherDomain) { BowserDereferenceDiscardableCode( BowserDiscardableCodeSection ); } Name = PagedTransportName->Name; FREE_POOL(PagedTransportName); } if (Name != NULL) { if (Name->NameType == ComputerName) { Transport->ComputerName = NULL; } if (Name->NameType == PrimaryDomain) { Transport->PrimaryDomain = NULL; } if (Name->NameType == MasterBrowser) { Transport->MasterBrowser = NULL; } if (Name->NameType == BrowserElection) { Transport->BrowserElection = NULL; } BowserDereferenceName(Name); } FREE_POOL(TransportName); ExReleaseResource(&BowserTransportDatabaseResource); dprintf(DPRT_TDI, ("BowserFreeTransportName: Free name %lx completed\n", TransportName)); return(STATUS_SUCCESS); } VOID BowserDeleteTransport( IN PTRANSPORT Transport ) /*++ Routine Description: Delete a transport. The caller should have a single reference to the transport. The actual transport structure will be deleted when that reference goes away. This routine will decrement the global reference made in BowserTdiAllocateTransport Arguments: IN Transport - Supplies a transport structure to be deleted. Return Value: None. --*/ { LARGE_INTEGER Interval; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); // // Prevent BowserFindTransport from adding any new references to the transport // if ( Transport->PagedTransport != NULL && Transport->PagedTransport->GlobalNext.Flink != NULL ) { RemoveEntryList(&Transport->PagedTransport->GlobalNext); } // // Close all handles to the TDI driver so we won't get any indications after // we start cleaning up the Transport structure in BowserpFreeTransport. // BowserCloseAllNetbiosAddresses( Transport ); if ( Transport->PagedTransport != NULL && Transport->PagedTransport->IpxSocketHandle != NULL) { NTSTATUS LocalStatus; BOOLEAN ProcessAttached = FALSE; if (IoGetCurrentProcess() != BowserFspProcess) { KeAttachProcess(BowserFspProcess); ProcessAttached = TRUE; } LocalStatus = ZwClose(Transport->PagedTransport->IpxSocketHandle); ASSERT(NT_SUCCESS(LocalStatus)); if (ProcessAttached) { KeDetachProcess(); } Transport->PagedTransport->IpxSocketHandle = NULL; } // // Uninitialize the timers to ensure we aren't in a timer routine while // we are cleaning up. // BowserUninitializeTimer(&Transport->ElectionTimer); BowserUninitializeTimer(&Transport->FindMasterTimer); // // Remove the global reference to the transport. // BowserDereferenceTransport( Transport ); ExReleaseResource(&BowserTransportDatabaseResource); // // Delete any mailslot messages queued to the netlogon service. // BowserNetlogonDeleteTransportFromMessageQueue ( Transport ); // // Loop until our caller has the last outstanding reference. // This is the only thing preventing the driver from unloading while there // are still references outstanding. // while ( Transport->ReferenceCount != 1) { Interval.QuadPart = -1*10*1000*10; // .01 second KeDelayExecutionThread( KernelMode, FALSE, &Interval ); } } VOID BowserpFreeTransport( IN PTRANSPORT Transport ) { PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); // // Free the Paged transport, if necessary. // if (Transport->PagedTransport != NULL) { PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport; // // Remove the Adresses. // // Do this in a separate step from the Close in BowserDeleteTransport // above to ensure the PrimaryDomain and ComputerName fields don't // get cleared until all possible references are removed. // if (!IsListEmpty( &PagedTransport->NameChain)) { BowserpTdiRemoveAddresses(Transport); } BowserDeleteGenericTable(&PagedTransport->AnnouncementTable); BowserDeleteGenericTable(&PagedTransport->DomainTable); if (PagedTransport->MasterName.Buffer != NULL) { FREE_POOL(PagedTransport->MasterName.Buffer); } if (PagedTransport->TransportName.Buffer != NULL) { FREE_POOL(PagedTransport->TransportName.Buffer); } FREE_POOL(PagedTransport); } ExDeleteResource(&Transport->BrowserServerListResource); UNINITIALIZE_ANNOUNCE_DATABASE(Transport); ExDeleteResource(&Transport->Lock); BowserUninitializeIrpQueue(&Transport->BecomeBackupQueue); BowserUninitializeIrpQueue(&Transport->BecomeMasterQueue); BowserUninitializeIrpQueue(&Transport->FindMasterQueue); BowserUninitializeIrpQueue(&Transport->WaitForMasterAnnounceQueue); if ( Transport->IpxSocketFileObject != NULL ) { ObDereferenceObject( Transport->IpxSocketFileObject ); } FREE_POOL(Transport); ExReleaseResource(&BowserTransportDatabaseResource); } NTSTATUS BowserpTdiSetEventHandler ( IN PDEVICE_OBJECT DeviceObject, IN PFILE_OBJECT FileObject, IN ULONG EventType, IN PVOID EventHandler, IN PVOID Context ) /*++ Routine Description: This routine registers an event handler with a TDI transport provider. Arguments: IN PDEVICE_OBJECT DeviceObject - Supplies the device object of the transport provider. IN PFILE_OBJECT FileObject - Supplies the address object's file object. IN ULONG EventType, - Supplies the type of event. IN PVOID EventHandler - Supplies the event handler. Return Value: NTSTATUS - Final status of the set event operation --*/ { NTSTATUS Status; PIRP Irp; PAGED_CODE(); Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (Irp == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } TdiBuildSetEventHandler(Irp, DeviceObject, FileObject, NULL, NULL, EventType, EventHandler, Context); Status = BowserSubmitTdiRequest(FileObject, Irp); IoFreeIrp(Irp); return Status; } NTSTATUS BowserIssueTdiAction ( IN PDEVICE_OBJECT DeviceObject, IN PFILE_OBJECT FileObject, IN PVOID Action, IN ULONG ActionSize ) /*++ Routine Description: This routine registers an event handler with a TDI transport provider. Arguments: IN PDEVICE_OBJECT DeviceObject - Supplies the device object of the transport provider. IN PFILE_OBJECT FileObject - Supplies the address object's file object. IN ULONG EventType, - Supplies the type of event. IN PVOID EventHandler - Supplies the event handler. Return Value: NTSTATUS - Final status of the set event operation --*/ { NTSTATUS status; PIRP irp; // PIO_STACK_LOCATION irpSp; PMDL mdl; PAGED_CODE(); irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Allocate and build an MDL that we'll use to describe the output // buffer for the request. // mdl = IoAllocateMdl( Action, ActionSize, FALSE, FALSE, NULL ); if ( mdl == NULL ) { IoFreeIrp( irp ); return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool( mdl ); TdiBuildAction( irp, DeviceObject, FileObject, NULL, NULL, mdl ); irp->AssociatedIrp.SystemBuffer = Action; if (irp == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } status = BowserSubmitTdiRequest(FileObject, irp); IoFreeIrp(irp); IoFreeMdl(mdl); return status; } NTSTATUS BowserBuildTransportAddress ( IN OUT PANSI_STRING Address, IN PUNICODE_STRING Name, IN DGRECEIVER_NAME_TYPE NameType, IN PTRANSPORT Transport ) /*++ Routine Description: This routine takes a computer name (PUNICODE_STRING) and converts it into an acceptable form for passing in as transport address. Arguments: OUT PTA_NETBIOS_ADDRESS RemoteAddress, - Supplies the structure to fill in IN PUNICODE_STRING Name - Supplies the name to put into the transport Please note that it is CRITICAL that the TA_NETBIOS_ADDRESS pointed to by RemoteAddress be of sufficient size to hold the full network name. Return Value: None. --*/ { NTSTATUS Status; OEM_STRING NetBiosName; PTRANSPORT_ADDRESS RemoteAddress = (PTRANSPORT_ADDRESS)Address->Buffer; PTDI_ADDRESS_NETBIOS NetbiosAddress = (PTDI_ADDRESS_NETBIOS)&RemoteAddress->Address[0].Address[0]; PAGED_CODE(); RemoteAddress->TAAddressCount = 1; RemoteAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; RemoteAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; Address->Length = sizeof(TA_NETBIOS_ADDRESS); if (RtlUnicodeStringToOemSize(Name) > NETBIOS_NAME_LEN) { return STATUS_BAD_NETWORK_PATH; } NetBiosName.Length = 0; NetBiosName.MaximumLength = NETBIOS_NAME_LEN; NetBiosName.Buffer = NetbiosAddress->NetbiosName; if (NameType != DomainAnnouncement) { Status = RtlUpcaseUnicodeStringToOemString(&NetBiosName, Name, FALSE); if (!NT_SUCCESS(Status)) { return Status; } RtlCopyMemory(&NetBiosName.Buffer[NetBiosName.Length], " ", NETBIOS_NAME_LEN-NetBiosName.Length); } else { // // Domain announcement names are simply filled with nulls. All other // names are padded with spaces. // ASSERT (strlen(DOMAIN_ANNOUNCEMENT_NAME) == NETBIOS_NAME_LEN); RtlCopyMemory(NetBiosName.Buffer, DOMAIN_ANNOUNCEMENT_NAME, strlen(DOMAIN_ANNOUNCEMENT_NAME)); } switch (NameType) { case DomainAnnouncement: NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; break; case ComputerName: case AlternateComputerName: NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = WORKSTATION_SIGNATURE; NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; break; case DomainName: NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = DOMAIN_CONTROLLER_SIGNATURE; NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; break; case BrowserServer: NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = SERVER_SIGNATURE; NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; break; case MasterBrowser: if (Transport->PagedTransport->Flags & DIRECT_HOST_IPX) { NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = WORKSTATION_SIGNATURE; } else { NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = MASTER_BROWSER_SIGNATURE; } NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; break; case PrimaryDomain: case OtherDomain: NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = PRIMARY_DOMAIN_SIGNATURE; NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; break; case PrimaryDomainBrowser: NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = PRIMARY_CONTROLLER_SIGNATURE; NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; break; case BrowserElection: if (Transport->PagedTransport->Flags & DIRECT_HOST_IPX) { NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = WORKSTATION_SIGNATURE; } else { NetbiosAddress->NetbiosName[NETBIOS_NAME_LEN-1] = BROWSER_ELECTION_SIGNATURE; } NetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; break; default: return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } NTSTATUS BowserUpdateProviderInformation( IN OUT PPAGED_TRANSPORT PagedTransport ) /*++ Routine Description: This routine updates status bits in the PagedTransport based on querying the TDI driver. Most importantly, the transport will be disabled if the provider is RAS or doesn't yet have an IP address. Arguments: PagedTransport - Transport to update Return Value: Status of operation. --*/ { NTSTATUS Status; TDI_PROVIDER_INFO ProviderInfo; ULONG OldIpSubnetNumber; BOOLEAN DisableThisTransport = FALSE; PLIST_ENTRY TransportEntry; PPAGED_TRANSPORT CurrentPagedTransport; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); // // Find out about the transport. // OldIpSubnetNumber = PagedTransport->IpSubnetNumber; Status = BowserDetermineProviderInformation( &PagedTransport->TransportName, &ProviderInfo, &PagedTransport->IpSubnetNumber ); if (!NT_SUCCESS(Status)) { goto ReturnStatus; } // // We can only talk to transports that support a max datagram size. // if (ProviderInfo.MaxDatagramSize == 0) { Status = STATUS_BAD_REMOTE_ADAPTER; goto ReturnStatus; } PagedTransport->NonPagedTransport->DatagramSize = ProviderInfo.MaxDatagramSize; // // Remember various attributes of the provider // (Never disable the PointToPoint bit. NetBt forgets it when the // RAS phone is hung up.) PagedTransport->Wannish = (BOOLEAN)((ProviderInfo.ServiceFlags & TDI_SERVICE_ROUTE_DIRECTED) != 0); if (ProviderInfo.ServiceFlags & TDI_SERVICE_POINT_TO_POINT) { PagedTransport->PointToPoint = TRUE; } // // If this is a RAS transport or the IP Address is not yet known, // disable browsing on the transport. // if ( PagedTransport->PointToPoint || PagedTransport->IpSubnetNumber == 0 ) { DisableThisTransport = TRUE; } // // If this isn't an IP transport, we're done. // if ( PagedTransport->IpSubnetNumber == BOWSER_NON_IP_SUBNET ) { goto ReturnStatus; } // // In the loop below, we use OldIpSubnetNumber to determine if another // transport should be enabled on that subnet. If that will NEVER be // appropriate, flag OldIpSubnetNumber now. // if ( OldIpSubnetNumber == 0 || PagedTransport->DisabledTransport || PagedTransport->IpSubnetNumber == OldIpSubnetNumber ) { OldIpSubnetNumber = BOWSER_NON_IP_SUBNET; } // // Loop through the transports enabling/disabling them as indicated by // the comments below. // for (TransportEntry = BowserTransportHead.Flink ; TransportEntry != &BowserTransportHead ; TransportEntry = CurrentPagedTransport->GlobalNext.Flink ) { CurrentPagedTransport = CONTAINING_RECORD(TransportEntry, PAGED_TRANSPORT, GlobalNext); // // If this transport isn't an IP transport, // or this transport is a RAS transport, // or this transport is the transport passed in, // skip it and go on to the next one. // if ( CurrentPagedTransport->IpSubnetNumber == BOWSER_NON_IP_SUBNET || CurrentPagedTransport->PointToPoint || CurrentPagedTransport == PagedTransport ) { continue; } // // Special case this transport if it's currently disabled // if ( CurrentPagedTransport->DisabledTransport ) { // // If this transport is disabled and the transport passed in // used to be the enabled transport for the subnet, // enable the transport // if ( CurrentPagedTransport->IpSubnetNumber == OldIpSubnetNumber ) { CurrentPagedTransport->DisabledTransport = FALSE; } // // In any case, // that's all we need to do for a disabled transport. // continue; } // // If this transport is an enabled transport for the subnet of the one // passed in, // then disable the one passed in. // if ( CurrentPagedTransport->IpSubnetNumber == PagedTransport->IpSubnetNumber ) { DisableThisTransport = TRUE; } } // // Cleanup // ReturnStatus: // // If we're disabling a previously enabled transport, // ensure we're not the master browser. // if ( DisableThisTransport && !PagedTransport->DisabledTransport ) { PagedTransport->DisabledTransport = DisableThisTransport; BowserLoseElection( PagedTransport->NonPagedTransport ); } else { PagedTransport->DisabledTransport = DisableThisTransport; } ExReleaseResource(&BowserTransportDatabaseResource); return Status; } NTSTATUS BowserDetermineProviderInformation( IN PUNICODE_STRING TransportName, OUT PTDI_PROVIDER_INFO ProviderInfo, OUT PULONG IpSubnetNumber ) /*++ Routine Description: This routine will determine provider information about a transport. Arguments: TransportName - Supplies the name of the transport provider ProviderInfo - Returns information about the provider IpSubnetNumber - returns the Ip Subnet Number of this transport. BOWSER_NON_IP_SUBNET - If this isn't an IP transport 0 - If the IP address isn't yet set Otherwise - the IP address anded with the subnet mask Return Value: Status of operation. --*/ { HANDLE TransportHandle = NULL; PFILE_OBJECT TransportObject = NULL; OBJECT_ATTRIBUTES ObjAttributes; IO_STATUS_BLOCK IoStatusBlock; PIRP Irp; PDEVICE_OBJECT DeviceObject; PMDL Mdl = NULL; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); InitializeObjectAttributes (&ObjAttributes, TransportName, // Name OBJ_CASE_INSENSITIVE, // Attributes NULL, // RootDirectory NULL); // SecurityDescriptor Status = ZwCreateFile(&TransportHandle, // Handle GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjAttributes, // Object Attributes &IoStatusBlock, // Final I/O status block NULL, // Allocation Size FILE_ATTRIBUTE_NORMAL, // Normal attributes FILE_SHARE_READ, // Sharing attributes FILE_OPEN_IF, // Create disposition 0, // CreateOptions NULL, // EA Buffer 0); // EA Buffer Length if (!NT_SUCCESS(Status)) { goto ReturnStatus; } Status = ObReferenceObjectByHandle ( TransportHandle, 0, *IoFileObjectType, KernelMode, (PVOID *)&TransportObject, NULL ); if (!NT_SUCCESS(Status)) { goto ReturnStatus; } DeviceObject = IoGetRelatedDeviceObject(TransportObject); Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (Irp == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnStatus; } // // Allocate an MDL to hold the provider info. // Mdl = IoAllocateMdl(ProviderInfo, sizeof(TDI_PROVIDER_INFO), FALSE, FALSE, NULL); MmBuildMdlForNonPagedPool(Mdl); TdiBuildQueryInformation(Irp, DeviceObject, TransportObject, NULL, NULL, TDI_QUERY_PROVIDER_INFORMATION, Mdl); Status = BowserSubmitTdiRequest(TransportObject, Irp); IoFreeIrp(Irp); // // Get the IP address for this Transport. // if ( (ProviderInfo->ServiceFlags & TDI_SERVICE_ROUTE_DIRECTED) == 0) { *IpSubnetNumber = BOWSER_NON_IP_SUBNET; } else { NTSTATUS TempStatus; IO_STATUS_BLOCK IoStatusBlock; ULONG IpAddressBuffer[2]; // IpAddress followed by subnet mask TempStatus = ZwDeviceIoControlFile( TransportHandle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_NETBT_GET_IP_SUBNET, NULL, 0, &IpAddressBuffer, sizeof(IpAddressBuffer) ); if ( !NT_SUCCESS(TempStatus) ) { *IpSubnetNumber = BOWSER_NON_IP_SUBNET; } else { ASSERT(TempStatus != STATUS_PENDING); *IpSubnetNumber = IpAddressBuffer[0] & IpAddressBuffer[1]; } } ReturnStatus: if (Mdl != NULL) { IoFreeMdl(Mdl); } if (TransportObject != NULL) { ObDereferenceObject(TransportObject); } if (TransportHandle != NULL) { ZwClose(TransportHandle); } return(Status); } NTSTATUS BowserIssueTdiQuery( IN PFILE_OBJECT FileObject, IN PDEVICE_OBJECT DeviceObject, IN PCHAR Buffer, IN ULONG BufferSize, IN USHORT QueryType ) /*++ Routine Description: This routine will determine provider information about a transport. Arguments: IN PUNICODE_STRING TransportName - Supplies the name of the transport provider Return Value: Status of operation. --*/ { PIRP Irp; PMDL Mdl; NTSTATUS status; PAGED_CODE(); Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (Irp == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnStatus; } // // Allocate an MDL to hold the provider info. // Mdl = IoAllocateMdl(Buffer, BufferSize, FALSE, FALSE, NULL); MmBuildMdlForNonPagedPool(Mdl); TdiBuildQueryInformation(Irp, DeviceObject, FileObject, NULL, NULL, QueryType, Mdl); status = BowserSubmitTdiRequest(FileObject, Irp); IoFreeIrp(Irp); ReturnStatus: if (Mdl != NULL) { IoFreeMdl(Mdl); } return(status); } PTRANSPORT BowserFindTransport ( PUNICODE_STRING TransportName ) /*++ Routine Description: This routine will locate a transport in the bowsers transport list. Arguments: PUNICODE_STRING TransportName - Supplies the name of the transport provider Return Value: PTRANSPORT - NULL if no transport was found, TRUE if transport was found. --*/ { PLIST_ENTRY TransportEntry; PTRANSPORT Transport = NULL; PPAGED_TRANSPORT PagedTransport = NULL; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); try { for (TransportEntry = BowserTransportHead.Flink ; TransportEntry != &BowserTransportHead ; TransportEntry = TransportEntry->Flink) { PagedTransport = CONTAINING_RECORD(TransportEntry, PAGED_TRANSPORT, GlobalNext); if (RtlEqualUnicodeString(TransportName, &PagedTransport->TransportName, TRUE)) { Transport = PagedTransport->NonPagedTransport; BowserReferenceTransport( Transport ); try_return(Transport); } } try_return(Transport = NULL); try_exit:NOTHING; } finally { ExReleaseResource (&BowserTransportDatabaseResource); } return Transport; } NTSTATUS BowserForEachTransport ( IN PTRANSPORT_ENUM_ROUTINE Routine, IN OUT PVOID Context ) /*++ Routine Description: This routine will enumerate the transports and call back the enum routine provided with each transport. Arguments: IN PFILE_OBJECT FileObject - Connection or Address handle for TDI request IN PIRP Irp - TDI request to submit. Return Value: NTSTATUS - Final status of request. --*/ { PLIST_ENTRY TransportEntry, NextEntry; PTRANSPORT Transport = NULL; PPAGED_TRANSPORT PagedTransport = NULL; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); for (TransportEntry = BowserTransportHead.Flink ; TransportEntry != &BowserTransportHead ; TransportEntry = NextEntry) { PagedTransport = CONTAINING_RECORD(TransportEntry, PAGED_TRANSPORT, GlobalNext); Transport = PagedTransport->NonPagedTransport; BowserReferenceTransport(Transport); ExReleaseResource(&BowserTransportDatabaseResource); Status = (Routine)(Transport, Context); if (!NT_SUCCESS(Status)) { BowserDereferenceTransport(Transport); return Status; } ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); NextEntry = PagedTransport->GlobalNext.Flink; BowserDereferenceTransport(Transport); } ExReleaseResource(&BowserTransportDatabaseResource); return Status; } NTSTATUS BowserForEachTransportName( IN PTRANSPORT Transport, IN PTRANSPORT_NAME_ENUM_ROUTINE Routine, IN OUT PVOID Context ) /*++ Routine Description: This routine will enumerate the names associated with a transport and call back the enum routine provided with each transport name. Arguments: Return Value: NTSTATUS - Final status of request. --*/ { PLIST_ENTRY TransportEntry, NextEntry; PTRANSPORT_NAME TransportName = NULL; PPAGED_TRANSPORT_NAME PagedTransportName = NULL; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); try { for (TransportEntry = Transport->PagedTransport->NameChain.Flink ; TransportEntry != &Transport->PagedTransport->NameChain ; TransportEntry = NextEntry) { PagedTransportName = CONTAINING_RECORD(TransportEntry, PAGED_TRANSPORT_NAME, TransportNext); TransportName = PagedTransportName->NonPagedTransportName; Status = (Routine)(TransportName, Context); if (!NT_SUCCESS(Status)) { try_return(Status); } NextEntry = PagedTransportName->TransportNext.Flink; } try_exit:NOTHING; } finally { ExReleaseResource(&BowserTransportDatabaseResource); } return Status; } NTSTATUS BowserDeleteTransportNameByName( IN PTRANSPORT Transport, IN PUNICODE_STRING Name, IN DGRECEIVER_NAME_TYPE NameType ) /*++ Routine Description: This routine deletes a transport name associated with a specific network. Arguments: IN PTRANSPORT Transport - Specifies the transport on which to delete the name. IN PUNICODE_STRING Name - Specifies the transport name to delete. IN DGRECEIVERNAMETYPE NameType - Specifies the name type of the name. Return Value: NTSTATUS - Final status of request. --*/ { PLIST_ENTRY TransportEntry, NextEntry; PTRANSPORT_NAME TransportName = NULL; PPAGED_TRANSPORT_NAME PagedTransportName = NULL; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); ExAcquireResourceExclusive(&BowserTransportDatabaseResource, TRUE); try { for (TransportEntry = Transport->PagedTransport->NameChain.Flink ; TransportEntry != &Transport->PagedTransport->NameChain ; TransportEntry = NextEntry) { PagedTransportName = CONTAINING_RECORD(TransportEntry, PAGED_TRANSPORT_NAME, TransportNext); TransportName = PagedTransportName->NonPagedTransportName; ASSERT (TransportName->NameType == PagedTransportName->Name->NameType); if ((TransportName->NameType == NameType) && RtlEqualUnicodeString(&PagedTransportName->Name->Name, Name, TRUE)) { NextEntry = TransportEntry->Flink; // // Remove the TransportName from the list of transport names for // this transport. // RemoveEntryList(&PagedTransportName->TransportNext); PagedTransportName->TransportNext.Flink = NULL; PagedTransportName->TransportNext.Blink = NULL; // // Since we delinked it, we need to dereference it. // Status = BowserDereferenceTransportName(TransportName); if (!NT_SUCCESS(Status)) { try_return(Status); } } else { NextEntry = PagedTransportName->TransportNext.Flink; } } try_exit:NOTHING; } finally { ExReleaseResource(&BowserTransportDatabaseResource); } return Status; } NTSTATUS BowserSubmitTdiRequest ( IN PFILE_OBJECT FileObject, IN PIRP Irp ) /*++ Routine Description: This routine submits a request to TDI and waits for it to complete. Arguments: IN PFILE_OBJECT FileObject - Connection or Address handle for TDI request IN PIRP Irp - TDI request to submit. Return Value: NTSTATUS - Final status of request. --*/ { KEVENT Event; NTSTATUS Status; PAGED_CODE(); BowserReferenceDiscardableCode( BowserDiscardableCodeSection ); KeInitializeEvent (&Event, NotificationEvent, FALSE); IoSetCompletionRoutine(Irp, BowserCompleteTdiRequest, &Event, TRUE, TRUE, TRUE); // // Submit the disconnect request // Status = IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp); // // If it failed immediately, return now, otherwise wait. // if (!NT_SUCCESS(Status)) { dprintf(DPRT_TDI, ("BowserSubmitTdiRequest: submit request. Status = %X", Status)); BowserDereferenceDiscardableCode( BowserDiscardableCodeSection ); return Status; } if (Status == STATUS_PENDING) { dprintf(DPRT_TDI, ("TDI request issued, waiting...")); Status = KeWaitForSingleObject(&Event, // Object to wait on. Executive, // Reason for waiting KernelMode, // Processor mode FALSE, // Alertable NULL); // Timeout if (!NT_SUCCESS(Status)) { dprintf(DPRT_TDI, ("Could not wait for operation to complete")); KeBugCheck( 666 ); } Status = Irp->IoStatus.Status; } BowserDereferenceDiscardableCode( BowserDiscardableCodeSection ); dprintf(DPRT_TDI, ("TDI request complete ")); return(Status); } NTSTATUS BowserCompleteTdiRequest ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Completion routine for SubmitTdiRequest operation. Arguments: IN PDEVICE_OBJECT DeviceObject, - Supplies a pointer to the device object IN PIRP Irp, - Supplies the IRP submitted IN PVOID Context - Supplies a pointer to the kernel event to release Return Value: NTSTATUS - Status of KeSetEvent We return STATUS_MORE_PROCESSING_REQUIRED to prevent the IRP completion code from processing this puppy any more. --*/ { DISCARDABLE_CODE( BowserDiscardableCodeSection ); dprintf(DPRT_TDI, ("CompleteTdiRequest: %lx\n", Context)); // // Set the event to the Signalled state with 0 priority increment and // indicate that we will not be blocking soon. // KeSetEvent((PKEVENT )Context, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; // Quiet the compiler. if (Irp || DeviceObject){}; } typedef struct _SEND_DATAGRAM_CONTEXT { PTDI_CONNECTION_INFORMATION ConnectionInformation; PVOID Header; BOOLEAN WaitForCompletion; KEVENT Event; } SEND_DATAGRAM_CONTEXT, *PSEND_DATAGRAM_CONTEXT; NTSTATUS BowserSendDatagram ( IN PTRANSPORT Transport, IN PUNICODE_STRING Domain OPTIONAL, IN DGRECEIVER_NAME_TYPE NameType, IN PVOID Buffer, IN ULONG BufferLength, IN BOOLEAN WaitForCompletion, IN PSTRING DestinationAddress OPTIONAL, IN BOOLEAN IsHostAnnouncement ) /*++ Routine Description: This routine sends a datagram to the specified domain. Arguments: Domain - the name of the domain to send to. Please note that the DOMAIN is padded with spaces and terminated with the appropriate signature byte (00 or 07). Buffer - the message to send. BufferLength - the length of the buffer, IsHostAnnouncement - True if the datagram is a host announcement Return Value: NTSTATUS - results of operation. --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG connectionInformationSize; PIRP irp = NULL; PMDL mdlAddress = NULL; PSEND_DATAGRAM_CONTEXT context; PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport; // PTRANSPORT_NAME TComputerName; ANSI_STRING AnsiString; UCHAR IpxPacketType; PFILE_OBJECT FileObject; PDEVICE_OBJECT DeviceObject; PAGED_CODE(); // // Ensure the computername has been registered for this transport // if ( Transport->ComputerName == NULL ) { return STATUS_BAD_NETWORK_PATH; } // // Ensure the Device and File object are known. // if (!FlagOn(Transport->PagedTransport->Flags, DIRECT_HOST_IPX)) { DeviceObject = Transport->ComputerName->DeviceObject; FileObject = Transport->ComputerName->FileObject; } else { DeviceObject = Transport->IpxSocketDeviceObject; FileObject = Transport->IpxSocketFileObject; } if ( DeviceObject == NULL || FileObject == NULL ) { return STATUS_BAD_NETWORK_PATH; } // // Allocate a context describing this datagram send. // context = ALLOCATE_POOL(NonPagedPool, sizeof(SEND_DATAGRAM_CONTEXT), POOL_SENDDATAGRAM); if ( context == NULL) { return STATUS_NO_MEMORY; } connectionInformationSize = sizeof(TDI_CONNECTION_INFORMATION) + max(sizeof(TA_NETBIOS_ADDRESS), sizeof(TA_IPX_ADDRESS)); if (Domain == NULL) { Domain = &Transport->PrimaryDomain->PagedTransportName->Name->Name; } if (FlagOn(Transport->PagedTransport->Flags, DIRECT_HOST_IPX)) { PSMB_IPX_NAME_PACKET NamePacket; OEM_STRING NetBiosName; context->Header = ALLOCATE_POOL(NonPagedPool, BufferLength + sizeof(SMB_IPX_NAME_PACKET), POOL_SENDDATAGRAM); if ( context->Header == NULL ) { FREE_POOL(context); return STATUS_INSUFFICIENT_RESOURCES; } NamePacket = context->Header; RtlZeroMemory(NamePacket->Route, sizeof(NamePacket->Route)); NamePacket->Operation = SMB_IPX_MAILSLOT_SEND; if (NameType == BrowserElection) { if ( IsHostAnnouncement ) { NamePacket->NameType = SMB_IPX_NAME_TYPE_BROWSER; } else { NamePacket->NameType = SMB_IPX_NAME_TYPE_WORKKGROUP; } } else if (NameType == ComputerName || NameType == AlternateComputerName ) { NamePacket->NameType = SMB_IPX_NAME_TYPE_MACHINE; } else { NamePacket->NameType = SMB_IPX_NAME_TYPE_WORKKGROUP; } NamePacket->MessageId = 0; NetBiosName.Length = 0; NetBiosName.MaximumLength = SMB_IPX_NAME_LENGTH; NetBiosName.Buffer = NamePacket->Name; status = RtlUpcaseUnicodeStringToOemString(&NetBiosName, Domain, FALSE); if (!NT_SUCCESS(status)) { FREE_POOL( context->Header ); FREE_POOL( context ); return status; } RtlCopyMemory(&NetBiosName.Buffer[NetBiosName.Length], " ", SMB_IPX_NAME_LENGTH-NetBiosName.Length); NamePacket->Name[SMB_IPX_NAME_LENGTH-1] = WORKSTATION_SIGNATURE; RtlCopyMemory(NamePacket->SourceName, ((PTA_NETBIOS_ADDRESS)(Transport->ComputerName->TransportAddress.Buffer))->Address[0].Address->NetbiosName, SMB_IPX_NAME_LENGTH); RtlCopyMemory((NamePacket+1), Buffer, BufferLength); // // We don't need the input buffer any more. // FREE_POOL(Buffer); Buffer = context->Header; BufferLength += sizeof(SMB_IPX_NAME_PACKET); } else { context->Header = Buffer; } context->ConnectionInformation = ALLOCATE_POOL(NonPagedPool, connectionInformationSize, POOL_CONNECTINFO ); if ( context->ConnectionInformation == NULL ) { FREE_POOL(context->Header); FREE_POOL(context); return STATUS_INSUFFICIENT_RESOURCES; } context->ConnectionInformation->UserDataLength = 0; context->ConnectionInformation->UserData = NULL; context->ConnectionInformation->OptionsLength = 0; context->ConnectionInformation->Options = NULL; AnsiString.Buffer = (PCHAR)(context->ConnectionInformation + 1); AnsiString.MaximumLength = (USHORT)(connectionInformationSize - sizeof(TDI_CONNECTION_INFORMATION)); context->ConnectionInformation->RemoteAddress = AnsiString.Buffer; context->WaitForCompletion = WaitForCompletion; // ComputerName = Transport->ComputerName; if (!ARGUMENT_PRESENT(DestinationAddress)) { // // If this is for our primary domain, and the request is destined // for the master browser name, then stick in the address of our // master browser if we know it. // if (RtlEqualMemory(Domain->Buffer, ((PTA_NETBIOS_ADDRESS)(Transport->ComputerName->TransportAddress.Buffer))->Address[0].Address->NetbiosName, SMB_IPX_NAME_LENGTH) && ( NameType == MasterBrowser ) && (Transport->PagedTransport->MasterBrowserAddress.Length != 0) ) { // // This is for our domain. If it's for our master browser // and we know who that is, we're done - copy over the master's address // and send it. // ASSERT (Transport->PagedTransport->MasterBrowserAddress.Length == sizeof(TA_IPX_ADDRESS)); RtlCopyMemory(context->ConnectionInformation->RemoteAddress, Transport->PagedTransport->MasterBrowserAddress.Buffer, Transport->PagedTransport->MasterBrowserAddress.Length); // // This is a directed packet, don't broadcast it. // IpxPacketType = IPX_DIRECTED_PACKET; context->ConnectionInformation->OptionsLength = sizeof(IpxPacketType); context->ConnectionInformation->Options = &IpxPacketType; } else if (FlagOn(Transport->PagedTransport->Flags, DIRECT_HOST_IPX)) { PTA_IPX_ADDRESS IpxAddress = (PTA_IPX_ADDRESS)AnsiString.Buffer; IpxAddress->TAAddressCount = 1; IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; IpxAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IPX; IpxAddress->Address[0].Address[0].NetworkAddress = 0; IpxAddress->Address[0].Address[0].NodeAddress[0] = 0xff; IpxAddress->Address[0].Address[0].NodeAddress[1] = 0xff; IpxAddress->Address[0].Address[0].NodeAddress[2] = 0xff; IpxAddress->Address[0].Address[0].NodeAddress[3] = 0xff; IpxAddress->Address[0].Address[0].NodeAddress[4] = 0xff; IpxAddress->Address[0].Address[0].NodeAddress[5] = 0xff; IpxAddress->Address[0].Address[0].Socket = SMB_IPX_MAILSLOT_SOCKET; } else { status = BowserBuildTransportAddress(&AnsiString, Domain, NameType, Transport); if (!NT_SUCCESS(status)) { FREE_POOL(context->ConnectionInformation); FREE_POOL(context->Header); FREE_POOL(context); return status; } context->ConnectionInformation->RemoteAddressLength = AnsiString.Length; } } else { // // This is already correctly formatted, so just put it on the wire. // RtlCopyMemory(context->ConnectionInformation->RemoteAddress, DestinationAddress->Buffer, DestinationAddress->Length); context->ConnectionInformation->RemoteAddressLength = DestinationAddress->Length; // // This is a directed packet, don't broadcast it. // IpxPacketType = IPX_DIRECTED_PACKET; context->ConnectionInformation->OptionsLength = sizeof(IpxPacketType); context->ConnectionInformation->Options = &IpxPacketType; } irp = IoAllocateIrp( DeviceObject->StackSize, TRUE); if (irp == NULL) { FREE_POOL(context->ConnectionInformation); FREE_POOL(context->Header); FREE_POOL(context); return STATUS_INSUFFICIENT_RESOURCES; } mdlAddress = IoAllocateMdl(Buffer, BufferLength, FALSE, FALSE, NULL); if (mdlAddress == NULL) { FREE_POOL(context->ConnectionInformation); FREE_POOL(context->Header); FREE_POOL(context); IoFreeIrp(irp); return STATUS_INSUFFICIENT_RESOURCES; } KeInitializeEvent(&context->Event, NotificationEvent, FALSE); MmBuildMdlForNonPagedPool(mdlAddress); BowserReferenceDiscardableCode( BowserDiscardableCodeSection ); ASSERT (KeGetCurrentIrql() == 0); TdiBuildSendDatagram( irp, DeviceObject, FileObject, CompleteSendDatagram, context, mdlAddress, BufferLength, context->ConnectionInformation); status = IoCallDriver(DeviceObject, irp); ASSERT (KeGetCurrentIrql() == 0); if (WaitForCompletion) { ASSERT (KeGetCurrentIrql() == 0); if (status == STATUS_PENDING) { status = KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL); } IoFreeMdl(irp->MdlAddress); // // Retrieve the status from the IRP. // status = irp->IoStatus.Status; IoFreeIrp(irp); FREE_POOL(context->ConnectionInformation); FREE_POOL(context->Header); FREE_POOL(context); } ASSERT (KeGetCurrentIrql() == 0); BowserDereferenceDiscardableCode( BowserDiscardableCodeSection ); return status; } // BowserSendDatagram NTSTATUS CompleteSendDatagram ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Ctx ) /*++ Routine Description: Completion routine for SubmitTdiRequest operation. Arguments: IN PDEVICE_OBJECT DeviceObject, - Supplies a pointer to the device object IN PIRP Irp, - Supplies the IRP submitted IN PVOID Context - Supplies a pointer to the kernel event to release Return Value: NTSTATUS - Status of KeSetEvent We return STATUS_MORE_PROCESSING_REQUIRED to prevent the IRP completion code from processing this puppy any more. --*/ { PSEND_DATAGRAM_CONTEXT Context = Ctx; DISCARDABLE_CODE( BowserDiscardableCodeSection ); dprintf(DPRT_TDI, ("CompleteTdiRequest: %lx\n", Context)); if (Context->WaitForCompletion) { // // Set the event to the Signalled state with 0 priority increment and // indicate that we will not be blocking soon. // KeSetEvent(&Context->Event, 0, FALSE); } else { FREE_POOL(Context->ConnectionInformation); FREE_POOL(Context->Header); FREE_POOL(Context); IoFreeMdl(Irp->MdlAddress); IoFreeIrp(Irp); } return STATUS_MORE_PROCESSING_REQUIRED; UNREFERENCED_PARAMETER(DeviceObject); } NTSTATUS BowserSendSecondClassMailslot ( IN PTRANSPORT Transport, IN PUNICODE_STRING Domain OPTIONAL, IN DGRECEIVER_NAME_TYPE NameType, IN PVOID Message, IN ULONG MessageLength, IN BOOLEAN WaitForCompletion, IN PCHAR mailslotNameData, IN PSTRING DestinationAddress OPTIONAL ) { ULONG dataSize; ULONG transactionDataSize; ULONG smbSize; PSMB_HEADER header; PSMB_TRANSACT_MAILSLOT parameters; PSZ mailslotName; ULONG mailslotNameLength; PSZ domainInData; PVOID message; NTSTATUS status; PAGED_CODE(); // // Determine the sizes of various fields that will go in the SMB // and the total size of the SMB. // mailslotNameLength = strlen( mailslotNameData ); transactionDataSize = MessageLength; dataSize = mailslotNameLength + 1 + transactionDataSize; smbSize = sizeof(SMB_HEADER) + sizeof(SMB_TRANSACT_MAILSLOT) - 1 + dataSize; header = ALLOCATE_POOL( NonPagedPool, smbSize, POOL_MAILSLOT_HEADER ); if ( header == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } // // Fill in the header. Most of the fields don't matter and are // zeroed. // RtlZeroMemory( header, smbSize ); header->Protocol[0] = 0xFF; header->Protocol[1] = 'S'; header->Protocol[2] = 'M'; header->Protocol[3] = 'B'; header->Command = SMB_COM_TRANSACTION; // // Get the pointer to the params and fill them in. // parameters = (PSMB_TRANSACT_MAILSLOT)( header + 1 ); mailslotName = (PSZ)( parameters + 1 ) - 1; domainInData = mailslotName + mailslotNameLength + 1; message = domainInData; parameters->WordCount = 0x11; SmbPutUshort( ¶meters->TotalDataCount, (USHORT)transactionDataSize ); SmbPutUlong( ¶meters->Timeout, 0x3E8 ); // !!! fix SmbPutUshort( ¶meters->DataCount, (USHORT)transactionDataSize ); SmbPutUshort( ¶meters->DataOffset, (USHORT)( (ULONG)message - (ULONG)header ) ); parameters->SetupWordCount = 3; SmbPutUshort( ¶meters->Opcode, MS_WRITE_OPCODE ); SmbPutUshort( ¶meters->Priority, 1); SmbPutUshort( ¶meters->Class, 2 ); SmbPutUshort( ¶meters->ByteCount, (USHORT)dataSize ); RtlCopyMemory( mailslotName, mailslotNameData, mailslotNameLength + 1 ); RtlCopyMemory( message, Message, MessageLength ); // // Send the actual mailslot message. // status = BowserSendDatagram( Transport, Domain, NameType, header, smbSize, WaitForCompletion, DestinationAddress, (BOOLEAN)(((PHOST_ANNOUNCE_PACKET)Message)->AnnounceType == LocalMasterAnnouncement) ); return status; } // BowserSendSecondClassMailslot NTSTATUS BowserSendRequestAnnouncement( IN PUNICODE_STRING DestinationName, IN DGRECEIVER_NAME_TYPE NameType, IN PTRANSPORT Transport ) { REQUEST_ANNOUNCE_PACKET AnnounceRequest; ULONG AnnouncementRequestLength; OEM_STRING AnsiServerName; NTSTATUS Status; PAGED_CODE(); AnnounceRequest.Type = AnnouncementRequest; AnnounceRequest.RequestAnnouncement.Flags = 0; AnsiServerName.Buffer = AnnounceRequest.RequestAnnouncement.Reply; AnsiServerName.MaximumLength = sizeof(REQUEST_ANNOUNCE_PACKET) - FIELD_OFFSET(REQUEST_ANNOUNCE_PACKET, RequestAnnouncement.Reply); Status = RtlUnicodeStringToOemString(&AnsiServerName, &Transport->ComputerName->PagedTransportName->Name->Name, FALSE); AnnouncementRequestLength = FIELD_OFFSET(REQUEST_ANNOUNCE_PACKET, RequestAnnouncement.Reply) + AnsiServerName.Length + 1; if (NT_SUCCESS(Status)) { Status = BowserSendSecondClassMailslot(Transport, DestinationName, NameType, &AnnounceRequest, AnnouncementRequestLength, TRUE, MAILSLOT_BROWSER_NAME, NULL); } return Status; } VOID BowserpInitializeTdi ( VOID ) /*++ Routine Description: This routine initializes the global variables used in the transport package. Arguments: None. Return Value: None. --*/ { // // Initialize the Transport list chain // InitializeListHead(&BowserTransportHead); ExInitializeResource(&BowserTransportDatabaseResource); KeInitializeSpinLock(&BowserTransportMasterNameSpinLock); } VOID BowserpUninitializeTdi ( VOID ) /*++ Routine Description: This routine initializes the global variables used in the transport package. Arguments: None. Return Value: None. --*/ { PAGED_CODE(); ASSERT (IsListEmpty(&BowserTransportHead)); ExDeleteResource(&BowserTransportDatabaseResource); }