/*++ Copyright (c) 1989, 1990, 1991 Microsoft Corporation Module Name: address.c Abstract: This module contains code which implements the TP_ADDRESS object. Routines are provided to create, destroy, reference, and dereference, transport address objects. Author: David Beaver (dbeaver) 1-July-1991 Environment: Kernel mode Revision History: --*/ #include "precomp.h" #pragma hdrstop #if DBG #define NbfDbgShowAddr(TNA)\ { \ if ((TNA) == NULL) { \ NbfPrint0("\n"); \ } else { \ NbfPrint6("%c %c %c %c %d (%c)\n", \ (TNA)->NetbiosName[0], \ (TNA)->NetbiosName[1], \ (TNA)->NetbiosName[4], \ (TNA)->NetbiosName[6], \ (TNA)->NetbiosName[15], \ (TNA)->NetbiosNameType + 'A'); \ } \ } #else #define NbfDbgShowAddr(TNA) #endif // // Map all generic accesses to the same one. // STATIC GENERIC_MAPPING AddressGenericMapping = { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; VOID AddressTimeoutHandler( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This routine is executed as a DPC at DISPATCH_LEVEL when the timeout period for the ADD_NAME_QUERY/ADD_NAME_RECOGNIZED protocol expires. The retry count in the Address object is decremented, and if it reaches 0, the address is registered. If the retry count has not reached zero, then the ADD NAME QUERY is retried. Arguments: Dpc - Pointer to a system DPC object. DeferredContext - Pointer to the TP_ADDRESS block representing the address that is being registered. SystemArgument1 - Not used. SystemArgument2 - Not used. Return Value: none. --*/ { PTP_ADDRESS_FILE addressFile; PTP_ADDRESS address; PDEVICE_CONTEXT DeviceContext; PLIST_ENTRY p; LARGE_INTEGER timeout; Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings ENTER_NBF; address = (PTP_ADDRESS)DeferredContext; DeviceContext = address->Provider; // // We are waiting for an ADD_NAME_RECOGNIZED indicating that there is a // conflict. Decrement the retry count, and if it dropped to zero, // then we've waited a sufficiently long time. If there was no conflict, // complete all waiting file opens for the address. // ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); if ((address->Flags & ADDRESS_FLAGS_QUICK_REREGISTER) != 0) { BOOLEAN DuplicateName; PTP_CONNECTION Connection; DuplicateName = ((address->Flags & (ADDRESS_FLAGS_DUPLICATE_NAME|ADDRESS_FLAGS_CONFLICT)) != 0); for (p=address->ConnectionDatabase.Flink; p != &address->ConnectionDatabase; p=p->Flink) { Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { continue; } RELEASE_DPC_SPIN_LOCK (&address->SpinLock); if ((Connection->Flags2 & CONNECTION_FLAGS2_W_ADDRESS) != 0) { if (DuplicateName) { NbfStopConnection (Connection, STATUS_DUPLICATE_NAME); } else { // // Continue with the connection attempt. // ULONG NameQueryTimeout; ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); Connection->Flags2 &= ~CONNECTION_FLAGS2_W_ADDRESS; RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); KeQueryTickCount (&Connection->ConnectStartTime); NameQueryTimeout = Connection->Provider->NameQueryTimeout; if (Connection->Provider->MacInfo.MediumAsync && !Connection->Provider->MediumSpeedAccurate) { NameQueryTimeout = NAME_QUERY_TIMEOUT / 10; } NbfSendNameQuery ( Connection, TRUE); NbfStartConnectionTimer ( Connection, ConnectionEstablishmentTimeout, NameQueryTimeout); } } ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); } address->Flags &= ~ADDRESS_FLAGS_QUICK_REREGISTER; RELEASE_DPC_SPIN_LOCK (&address->SpinLock); NbfDereferenceAddress ("Timer, registered", address, AREF_TIMER); } else if ((address->Flags & (ADDRESS_FLAGS_DUPLICATE_NAME|ADDRESS_FLAGS_CONFLICT)) != 0) { PIRP irp; // // the address registration has failed. We signal the user in // the normal way (by failing the open of the address). Now clean up // the transport's data structures. // IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("AddressTimeoutHandler %lx: duplicate\n", address); } address->Flags &= ~ADDRESS_FLAGS_REGISTERING; // address->Flags |= ADDRESS_FLAGS_STOPPING; // // This is probably all overkill, the // uframes handler will already have called // NbfStopAddress, which will tear off all // the address files etc., and set the // STOPPING flag which prevents further opens. // p = address->AddressFileDatabase.Flink; while (p != &address->AddressFileDatabase) { addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); p = p->Flink; if (addressFile->Irp != NULL) { irp = addressFile->Irp; addressFile->Irp = NULL; RELEASE_DPC_SPIN_LOCK (&address->SpinLock); irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_DUPLICATE_NAME; LEAVE_NBF; IoCompleteRequest (irp, IO_NETWORK_INCREMENT); ENTER_NBF; NbfStopAddressFile (addressFile, address); ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); } } RELEASE_DPC_SPIN_LOCK (&address->SpinLock); // // There will be no more timer events happening, so we dereference the // address to account for the timer. // NbfStopAddress (address); NbfDereferenceAddress ("Timer, dup address", address, AREF_TIMER); } else { // // has the address registration succeeded? // if (--(address->Retries) <= 0) { // if retry count exhausted. PIRP irp; IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("AddressTimeoutHandler %lx: successful.\n", address); } address->Flags &= ~ADDRESS_FLAGS_REGISTERING; p = address->AddressFileDatabase.Flink; while (p != &address->AddressFileDatabase) { addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); p = p->Flink; IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint3 ("AddressTimeoutHandler %lx: Completing IRP %lx for file %lx\n", address, addressFile->Irp, addressFile); } if (addressFile->Irp != NULL) { irp = addressFile->Irp; addressFile->Irp = NULL; addressFile->State = ADDRESSFILE_STATE_OPEN; RELEASE_DPC_SPIN_LOCK (&address->SpinLock); irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_SUCCESS; LEAVE_NBF; IoCompleteRequest (irp, IO_NETWORK_INCREMENT); ENTER_NBF; ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); } } RELEASE_DPC_SPIN_LOCK (&address->SpinLock); // // Dereference the address if we're all done. // NbfDereferenceAddress ("Timer, registered", address, AREF_TIMER); } else { IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2 ("AddressTimeoutHandler %lx: step %x.\n", address, DeviceContext->AddNameQueryRetries - address->Retries); } // // restart the timer if we haven't yet completed registration // RELEASE_DPC_SPIN_LOCK (&address->SpinLock); timeout.LowPart = (ULONG)(-(LONG)DeviceContext->AddNameQueryTimeout); timeout.HighPart = -1; KeSetTimer (&address->Timer,*(PTIME)&timeout, &address->Dpc); (VOID)NbfSendAddNameQuery (address); // send another ADD_NAME_QUERY. } } LEAVE_NBF; return; } /* AddressTimeoutHandler */ TDI_ADDRESS_NETBIOS * NbfParseTdiAddress( IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, IN BOOLEAN BroadcastAddressOk ) /*++ Routine Description: This routine scans a TRANSPORT_ADDRESS, looking for an address of type TDI_ADDRESS_TYPE_NETBIOS. Arguments: Transport - The generic TDI address. BroadcastAddressOk - TRUE if we should return the broadcast address if found. If so, a value of (PVOID)-1 indicates the broadcast address. Return Value: A pointer to the Netbios address, or NULL if none is found, or (PVOID)-1 if the broadcast address is found. --*/ { TA_ADDRESS * addressName; INT i; addressName = &TransportAddress->Address[0]; // // The name can be passed with multiple entries; we'll take and use only // the Netbios one. // for (i=0;iTAAddressCount;i++) { if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { if ((addressName->AddressLength == 0) && BroadcastAddressOk) { return (PVOID)-1; } else if (addressName->AddressLength == sizeof(TDI_ADDRESS_NETBIOS)) { return((TDI_ADDRESS_NETBIOS *)(addressName->Address)); } } addressName = (TA_ADDRESS *)(addressName->Address + addressName->AddressLength); } return NULL; } /* NbfParseTdiAddress */ BOOLEAN NbfValidateTdiAddress( IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, IN ULONG TransportAddressLength ) /*++ Routine Description: This routine scans a TRANSPORT_ADDRESS, verifying that the components of the address do not extend past the specified length. Arguments: TransportAddress - The generic TDI address. TransportAddressLength - The specific length of TransportAddress. Return Value: TRUE if the address is valid, FALSE otherwise. --*/ { PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; TA_ADDRESS * addressName; INT i; if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { NbfPrint0 ("NbfValidateTdiAddress: runt address\n"); return FALSE; } addressName = &TransportAddress->Address[0]; for (i=0;iTAAddressCount;i++) { if (addressName->Address > AddressEnd) { NbfPrint0 ("NbfValidateTdiAddress: address too short\n"); return FALSE; } addressName = (TA_ADDRESS *)(addressName->Address + addressName->AddressLength); } if ((PUCHAR)addressName > AddressEnd) { NbfPrint0 ("NbfValidateTdiAddress: address too short\n"); return FALSE; } return TRUE; } /* NbfValidateTdiAddress */ NTSTATUS NbfOpenAddress( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine opens a file that points to an existing address object, or, if the object doesn't exist, creates it (note that creation of the address object includes registering the address, and may take many seconds to complete, depending upon system configuration). If the address already exists, and it has an ACL associated with it, the ACL is checked for access rights before allowing creation of the address. Arguments: DeviceObject - pointer to the device object describing the NBF transport. Irp - a pointer to the Irp used for the creation of the address. IrpSp - a pointer to the Irp stack location. Return Value: NTSTATUS - status of operation. --*/ { PDEVICE_CONTEXT DeviceContext; NTSTATUS status; PTP_ADDRESS address; PTP_ADDRESS_FILE addressFile; PNBF_NETBIOS_ADDRESS networkName; // Network name string. PFILE_FULL_EA_INFORMATION ea; TRANSPORT_ADDRESS UNALIGNED *name; TDI_ADDRESS_NETBIOS *netbiosName; ULONG DesiredShareAccess; KIRQL oldirql; PACCESS_STATE AccessState; ACCESS_MASK GrantedAccess; BOOLEAN AccessAllowed; BOOLEAN QuickAdd = FALSE; DeviceContext = (PDEVICE_CONTEXT)DeviceObject; // // The network name is in the EA, passed in AssociatedIrp.SystemBuffer // ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; if (ea == NULL) { NbfPrint1("OpenAddress: IRP %lx has no EA\n", Irp); return STATUS_INVALID_ADDRESS_COMPONENT; } // // this may be a valid name; parse the name from the EA and use it if OK. // name = (TRANSPORT_ADDRESS UNALIGNED *)&ea->EaName[ea->EaNameLength+1]; if (!NbfValidateTdiAddress(name, ea->EaValueLength)) { return STATUS_INVALID_ADDRESS_COMPONENT; } // // The name can have with multiple entries; we'll use the Netbios one. // This call returns NULL if not Netbios address is found, (PVOID)-1 // if it is the broadcast address, and a pointer to a Netbios // address otherwise. // netbiosName = NbfParseTdiAddress(name, TRUE); if (netbiosName != NULL) { if (netbiosName != (PVOID)-1) { networkName = (PNBF_NETBIOS_ADDRESS)ExAllocatePoolWithTag ( NonPagedPool, sizeof (NBF_NETBIOS_ADDRESS), NBF_MEM_TAG_NETBIOS_NAME); if (networkName == NULL) { PANIC ("NbfOpenAddress: PANIC! could not allocate networkName!\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_POOL, 1, sizeof(TA_NETBIOS_ADDRESS), ADDRESS_RESOURCE_ID); return STATUS_INSUFFICIENT_RESOURCES; } // // get the name to local storage // if ((netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) || (netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP)) { networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; } else { networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; } RtlCopyMemory (networkName->NetbiosName, netbiosName->NetbiosName, 16); if ((netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE) || (netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP)) { QuickAdd = TRUE; } } else { networkName = NULL; } } else { IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1("OpenAddress: IRP %lx has no NETBIOS address\n", Irp); } return STATUS_INVALID_ADDRESS_COMPONENT; } IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("OpenAddress %s: ", ((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? "shared" : "exclusive"); NbfDbgShowAddr (networkName); } // // get an address file structure to represent this address. // status = NbfCreateAddressFile (DeviceContext, &addressFile); if (!NT_SUCCESS (status)) { if (networkName != NULL) { ExFreePool (networkName); } return status; } // // See if this address is already established. This call automatically // increments the reference count on the address so that it won't disappear // from underneath us after this call but before we have a chance to use it. // // To ensure that we don't create two address objects for the // same address, we hold the device context AddressResource until // we have found the address or created a new one. // ACQUIRE_RESOURCE_EXCLUSIVE (&DeviceContext->AddressResource, TRUE); ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); address = NbfLookupAddress (DeviceContext, networkName); if (address == NULL) { // // This address doesn't exist. Create it, and start the process of // registering it. // status = NbfCreateAddress ( DeviceContext, networkName, &address); RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); if (NT_SUCCESS (status)) { // // Initialize the shared access now. We use read access // to control all access. // DesiredShareAccess = (ULONG) (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? FILE_SHARE_READ : 0); IoSetShareAccess( FILE_READ_DATA, DesiredShareAccess, IrpSp->FileObject, &address->u.ShareAccess); // // Assign the security descriptor (need to do this with // the spinlock released because the descriptor is not // mapped. Need to synchronize Assign and Access). // AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; status = SeAssignSecurity( NULL, // parent descriptor AccessState->SecurityDescriptor, &address->SecurityDescriptor, FALSE, // is directory &AccessState->SubjectSecurityContext, &AddressGenericMapping, PagedPool); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint3 ("Assign security A %lx AF %lx, status %lx\n", address, addressFile, status); } if (!NT_SUCCESS(status)) { // // Error, return status. // IoRemoveShareAccess (IrpSp->FileObject, &address->u.ShareAccess); // Mark as stopping so that someone does not ref it again ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); address->Flags |= ADDRESS_FLAGS_STOPPING; RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); RELEASE_RESOURCE (&DeviceContext->AddressResource); NbfDereferenceAddress ("Device context stopping", address, AREF_TEMP_CREATE); NbfDereferenceAddressFile (addressFile); return status; } RELEASE_RESOURCE (&DeviceContext->AddressResource); // // if the adapter isn't ready, we can't do any of this; get out // if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint3("OpenAddress A %lx AF %lx: DeviceContext %lx not open\n", address, addressFile, DeviceContext); } NbfDereferenceAddressFile (addressFile); status = STATUS_DEVICE_NOT_READY; } else { IrpSp->FileObject->FsContext = (PVOID)addressFile; IrpSp->FileObject->FsContext2 = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; addressFile->FileObject = IrpSp->FileObject; addressFile->Irp = Irp; addressFile->Address = address; NbfReferenceAddress("Opened new", address, AREF_OPEN); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2("OpenAddress A %lx AF %lx: created.\n", address, addressFile); } ExInterlockedInsertTailList( &address->AddressFileDatabase, &addressFile->Linkage, &address->SpinLock); // // Begin address registration unless this is the broadcast // address (which is a "fake" address with no corresponding // Netbios address) or the reserved address, which we know // is unique since it is based on the adapter address. // // Also, for "quick" add names, do not register. // if ((networkName != NULL) && (!RtlEqualMemory (networkName->NetbiosName, DeviceContext->ReservedNetBIOSAddress, NETBIOS_NAME_LENGTH)) && (!QuickAdd)) { NbfRegisterAddress (address); // begin address registration. status = STATUS_PENDING; } else { address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; addressFile->Irp = NULL; addressFile->State = ADDRESSFILE_STATE_OPEN; status = STATUS_SUCCESS; } } NbfDereferenceAddress("temp create", address, AREF_TEMP_CREATE); } else { RELEASE_RESOURCE (&DeviceContext->AddressResource); // // If the address could not be created, and is not in the process of // being created, then we can't open up an address. // if (networkName != NULL) { ExFreePool (networkName); } NbfDereferenceAddressFile (addressFile); } } else { RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); // // The address already exists. Check the ACL and see if we // can access it. If so, simply use this address as our address. // AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; AccessAllowed = SeAccessCheck( address->SecurityDescriptor, &AccessState->SubjectSecurityContext, FALSE, // lock tokens IrpSp->Parameters.Create.SecurityContext->DesiredAccess, (ACCESS_MASK)0, // previously granted NULL, // privileges &AddressGenericMapping, Irp->RequestorMode, &GrantedAccess, &status); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint4 ("Access check A %lx AF %lx, %s (%lx)\n", address, addressFile, AccessAllowed ? "allowed" : "not allowed", status); } if (AccessAllowed) { // // Access was successful, make sure Status is right. // status = STATUS_SUCCESS; // Transfer the access masks from what is desired to what is granted AccessState->PreviouslyGrantedAccess |= GrantedAccess; AccessState->RemainingDesiredAccess &= ~(GrantedAccess | MAXIMUM_ALLOWED); // // Compare DesiredAccess to GrantedAccess? // // // Check that the name is of the correct type (unique vs. group) // We don't need to check this for the broadcast address. // // This code is structured funny, the only reason // this is inside this if is to avoid indenting too much. // if (networkName != NULL) { if (address->NetworkName->NetbiosNameType != networkName->NetbiosNameType) { IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2 ("Address types differ: old %c, new %c\n", address->NetworkName->NetbiosNameType + 'A', networkName->NetbiosNameType + 'A'); } status = STATUS_DUPLICATE_NAME; } } } if (!NT_SUCCESS (status)) { RELEASE_RESOURCE (&DeviceContext->AddressResource); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2("OpenAddress A %lx AF %lx: ACL bad.\n", address, addressFile); } NbfDereferenceAddressFile (addressFile); } else { // // Now check that we can obtain the desired share // access. We use read access to control all access. // DesiredShareAccess = (ULONG) (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? FILE_SHARE_READ : 0); status = IoCheckShareAccess( FILE_READ_DATA, DesiredShareAccess, IrpSp->FileObject, &address->u.ShareAccess, TRUE); if (!NT_SUCCESS (status)) { RELEASE_RESOURCE (&DeviceContext->AddressResource); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2("OpenAddress A %lx AF %lx: ShareAccess problem.\n", address, addressFile); } NbfDereferenceAddressFile (addressFile); } else { RELEASE_RESOURCE (&DeviceContext->AddressResource); ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); // // now, if the address registered, we simply return success after // pointing the file object at the address file (which points to // the address). If the address registration is pending, we mark // the registration pending and let the registration completion // routine complete the open. If the address is bad, we simply // fail the open. // if ((address->Flags & (ADDRESS_FLAGS_CONFLICT | ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING | ADDRESS_FLAGS_DUPLICATE_NAME | ADDRESS_FLAGS_NEEDS_REG | ADDRESS_FLAGS_STOPPING | ADDRESS_FLAGS_BAD_ADDRESS | ADDRESS_FLAGS_CLOSED)) == 0) { InsertTailList ( &address->AddressFileDatabase, &addressFile->Linkage); addressFile->Irp = NULL; addressFile->Address = address; addressFile->FileObject = IrpSp->FileObject; addressFile->State = ADDRESSFILE_STATE_OPEN; NbfReferenceAddress("open ready", address, AREF_OPEN); IrpSp->FileObject->FsContext = (PVOID)addressFile; IrpSp->FileObject->FsContext2 = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2("OpenAddress A %lx AF %lx: address ready.\n", address, addressFile); } status = STATUS_SUCCESS; } else { // // if the address is still registering, make the open pending. // if ((address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_NEEDS_REG)) != 0) { InsertTailList ( &address->AddressFileDatabase, &addressFile->Linkage); addressFile->Irp = Irp; addressFile->Address = address; addressFile->FileObject = IrpSp->FileObject; NbfReferenceAddress("open registering", address, AREF_OPEN); IrpSp->FileObject->FsContext = (PVOID)addressFile; IrpSp->FileObject->FsContext2 = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2("OpenAddress A %lx AF %lx: address registering.\n", address, addressFile); } status = STATUS_PENDING; } else { if ((address->Flags & ADDRESS_FLAGS_CONFLICT) != 0) { status = STATUS_DUPLICATE_NAME; } else { status = STATUS_DRIVER_INTERNAL_ERROR; } RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint3("OpenAddress A %lx AF %lx: address flags %lx.\n", address, addressFile, address->Flags); } NbfDereferenceAddressFile (addressFile); } } } } // // This isn't needed since it was not used in the // creation of the address. // if (networkName != NULL) { ExFreePool (networkName); } // // Remove the reference from NbfLookupAddress. // NbfDereferenceAddress ("Done opening", address, AREF_LOOKUP); } return status; } /* NbfOpenAddress */ VOID NbfAllocateAddress( IN PDEVICE_CONTEXT DeviceContext, OUT PTP_ADDRESS *TransportAddress ) /*++ Routine Description: This routine allocates storage for a transport address. Some minimal initialization is done on the address. NOTE: This routine is called with the device context spinlock held, or at such a time as synchronization is unnecessary. Arguments: DeviceContext - Pointer to the device context (which is really just the device object with its extension) to be associated with the address. Address - Pointer to a place where this routine will return a pointer to a transport address structure. Returns NULL if no storage can be allocated. Return Value: None. --*/ { PTP_ADDRESS Address; PSEND_PACKET_TAG SendTag; NDIS_STATUS NdisStatus; PNDIS_PACKET NdisPacket; PNDIS_BUFFER NdisBuffer; if ((DeviceContext->MemoryLimit != 0) && ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS)) > DeviceContext->MemoryLimit)) { PANIC("NBF: Could not allocate address: limit\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_LIMIT, 101, sizeof(TP_ADDRESS), ADDRESS_RESOURCE_ID); *TransportAddress = NULL; return; } Address = (PTP_ADDRESS)ExAllocatePoolWithTag ( NonPagedPool, sizeof (TP_ADDRESS), NBF_MEM_TAG_TP_ADDRESS); if (Address == NULL) { PANIC("NBF: Could not allocate address: no pool\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_POOL, 201, sizeof(TP_ADDRESS), ADDRESS_RESOURCE_ID); *TransportAddress = NULL; return; } RtlZeroMemory (Address, sizeof(TP_ADDRESS)); // To track packet pools in NDIS allocated on NBF's behalf #if NDIS_POOL_TAGGING Address->UIFramePoolHandle = (NDIS_HANDLE) NDIS_PACKET_POOL_TAG_FOR_NBF; #endif NdisAllocatePacketPoolEx ( &NdisStatus, &Address->UIFramePoolHandle, 1, 0, sizeof(SEND_PACKET_TAG)); if (NdisStatus != NDIS_STATUS_SUCCESS) { PANIC("NBF: Could not allocate address UI frame pool: no pool\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_POOL, 311, sizeof(SEND_PACKET_TAG), ADDRESS_RESOURCE_ID); ExFreePool (Address); *TransportAddress = NULL; return; } NdisSetPacketPoolProtocolId (Address->UIFramePoolHandle, NDIS_PROTOCOL_ID_NBF); // // This code is similar to NbfAllocateUIFrame. // Address->UIFrame = (PTP_UI_FRAME) ExAllocatePoolWithTag ( NonPagedPool, DeviceContext->UIFrameLength, NBF_MEM_TAG_TP_UI_FRAME); if (Address->UIFrame == NULL) { PANIC("NBF: Could not allocate address UI frame: no pool\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_POOL, 411, DeviceContext->UIFrameLength, ADDRESS_RESOURCE_ID); NdisFreePacketPool (Address->UIFramePoolHandle); ExFreePool (Address); *TransportAddress = NULL; return; } RtlZeroMemory (Address->UIFrame, DeviceContext->UIFrameLength); NdisAllocatePacket ( &NdisStatus, &NdisPacket, Address->UIFramePoolHandle); ASSERT (NdisStatus == NDIS_STATUS_SUCCESS); Address->UIFrame->NdisPacket = NdisPacket; Address->UIFrame->DataBuffer = NULL; SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; SendTag->Type = TYPE_ADDRESS_FRAME; SendTag->Owner = (PVOID)Address; SendTag->Frame = Address->UIFrame; // // Make the packet header known to the packet descriptor // NdisAllocateBuffer( &NdisStatus, &NdisBuffer, DeviceContext->NdisBufferPool, Address->UIFrame->Header, DeviceContext->UIFrameHeaderLength); if (NdisStatus != NDIS_STATUS_SUCCESS) { PANIC("NBF: Could not allocate address UI frame buffer: no pool\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_SPECIFIC, 511, 0, UI_FRAME_RESOURCE_ID); ExFreePool (Address->UIFrame); NdisFreePacket (Address->UIFrame->NdisPacket); NdisFreePacketPool (Address->UIFramePoolHandle); ExFreePool (Address); *TransportAddress = NULL; return; } NdisChainBufferAtFront (NdisPacket, NdisBuffer); DeviceContext->MemoryUsage += sizeof(TP_ADDRESS) + sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG) + DeviceContext->UIFrameLength; ++DeviceContext->AddressAllocated; Address->Type = NBF_ADDRESS_SIGNATURE; Address->Size = sizeof (TP_ADDRESS); Address->Provider = DeviceContext; KeInitializeSpinLock (&Address->SpinLock); // KeInitializeSpinLock (&Address->Interlock); InitializeListHead (&Address->ConnectionDatabase); InitializeListHead (&Address->AddressFileDatabase); InitializeListHead (&Address->SendDatagramQueue); KeInitializeDpc (&Address->Dpc, AddressTimeoutHandler, (PVOID)Address); KeInitializeTimer (&Address->Timer); // // For each address, allocate a receive packet and a receive buffer. // NbfAddReceivePacket (DeviceContext); NbfAddReceiveBuffer (DeviceContext); *TransportAddress = Address; } /* NbfAllocateAddress */ VOID NbfDeallocateAddress( IN PDEVICE_CONTEXT DeviceContext, IN PTP_ADDRESS TransportAddress ) /*++ Routine Description: This routine frees storage for a transport address. NOTE: This routine is called with the device context spinlock held, or at such a time as synchronization is unnecessary. Arguments: DeviceContext - Pointer to the device context (which is really just the device object with its extension) to be associated with the address. Address - Pointer to a transport address structure. Return Value: None. --*/ { PNDIS_BUFFER NdisBuffer; NdisUnchainBufferAtFront (TransportAddress->UIFrame->NdisPacket, &NdisBuffer); if (NdisBuffer != NULL) { NdisFreeBuffer (NdisBuffer); } NdisFreePacket(TransportAddress->UIFrame->NdisPacket); ExFreePool (TransportAddress->UIFrame); NdisFreePacketPool (TransportAddress->UIFramePoolHandle); ExFreePool (TransportAddress); --DeviceContext->AddressAllocated; DeviceContext->MemoryUsage -= sizeof(TP_ADDRESS) + sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG) + DeviceContext->UIFrameLength; // // Remove the resources which allocating this caused. // NbfRemoveReceivePacket (DeviceContext); NbfRemoveReceiveBuffer (DeviceContext); } /* NbfDeallocateAddress */ NTSTATUS NbfCreateAddress( IN PDEVICE_CONTEXT DeviceContext, IN PNBF_NETBIOS_ADDRESS NetworkName, OUT PTP_ADDRESS *Address ) /*++ Routine Description: This routine creates a transport address and associates it with the specified transport device context. The reference count in the address is automatically set to 1, and the reference count of the device context is incremented. NOTE: This routine must be called with the DeviceContext spinlock held. Arguments: DeviceContext - Pointer to the device context (which is really just the device object with its extension) to be associated with the address. NetworkName - Pointer to an NBF_NETBIOS_ADDRESS type containing the network name to be associated with this address, if any. NOTE: This has only the basic NetbiosNameType values, not the QUICK_ ones. Address - Pointer to a place where this routine will return a pointer to a transport address structure. Return Value: NTSTATUS - status of operation. --*/ { PTP_ADDRESS pAddress; PLIST_ENTRY p; p = RemoveHeadList (&DeviceContext->AddressPool); if (p == &DeviceContext->AddressPool) { if ((DeviceContext->AddressMaxAllocated == 0) || (DeviceContext->AddressAllocated < DeviceContext->AddressMaxAllocated)) { NbfAllocateAddress (DeviceContext, &pAddress); IF_NBFDBG (NBF_DEBUG_DYNAMIC) { NbfPrint1 ("NBF: Allocated address at %lx\n", pAddress); } } else { NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_SPECIFIC, 401, sizeof(TP_ADDRESS), ADDRESS_RESOURCE_ID); pAddress = NULL; } if (pAddress == NULL) { ++DeviceContext->AddressExhausted; PANIC ("NbfCreateAddress: Could not allocate address object!\n"); return STATUS_INSUFFICIENT_RESOURCES; } } else { pAddress = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); } ++DeviceContext->AddressInUse; if (DeviceContext->AddressInUse > DeviceContext->AddressMaxInUse) { ++DeviceContext->AddressMaxInUse; } DeviceContext->AddressTotal += DeviceContext->AddressInUse; ++DeviceContext->AddressSamples; IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_UFRAMES) { NbfPrint1 ("NbfCreateAddress %lx: ", pAddress); NbfDbgShowAddr (NetworkName); } // // Initialize all of the static data for this address. // pAddress->ReferenceCount = 1; #if DBG { UINT Counter; for (Counter = 0; Counter < NUMBER_OF_AREFS; Counter++) { pAddress->RefTypes[Counter] = 0; } // This reference is removed by the caller. pAddress->RefTypes[AREF_TEMP_CREATE] = 1; } #endif pAddress->Flags = ADDRESS_FLAGS_NEEDS_REG; InitializeListHead (&pAddress->AddressFileDatabase); pAddress->NetworkName = NetworkName; if ((NetworkName != (PNBF_NETBIOS_ADDRESS)NULL) && (NetworkName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP)) { pAddress->Flags |= ADDRESS_FLAGS_GROUP; } if (NetworkName != (PNBF_NETBIOS_ADDRESS)NULL) { ++DeviceContext->AddressCounts[NetworkName->NetbiosName[0]]; } // // Now link this address into the specified device context's // address database. To do this, we need to acquire the spin lock // on the device context. // InsertTailList (&DeviceContext->AddressDatabase, &pAddress->Linkage); pAddress->Provider = DeviceContext; NbfReferenceDeviceContext ("Create Address", DeviceContext, DCREF_ADDRESS); // count refs to the device context. *Address = pAddress; // return the address. return STATUS_SUCCESS; // not finished yet. } /* NbfCreateAddress */ VOID NbfRegisterAddress( PTP_ADDRESS Address ) /*++ Routine Description: This routine starts the registration process of the transport address specified, if it has not already been started. Arguments: Address - Pointer to a transport address object to begin registering on the network. Return Value: NTSTATUS - status of operation. --*/ { KIRQL oldirql; LARGE_INTEGER Timeout; ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); if (!(Address->Flags & ADDRESS_FLAGS_NEEDS_REG)) { RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfRegisterAddress %lx: NEEDS_REG 0.\n", Address); } return; } IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfRegisterAddress %lx: registering.\n", Address); } Address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; Address->Flags |= ADDRESS_FLAGS_REGISTERING; RtlZeroMemory (Address->UniqueResponseAddress, 6); // // Keep a reference on this address until the registration process // completes or is aborted. It will be aborted in UFRAMES.C, in // either the NAME_IN_CONFLICT or ADD_NAME_RESPONSE frame handlers. // NbfReferenceAddress ("start registration", Address, AREF_TIMER); RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); // // Now start registration process by starting up a retransmission timer // and begin sending ADD_NAME_QUERY NetBIOS frames. // // On an async line that is disconnected, we only send one packet // with a short timeout. // if (Address->Provider->MacInfo.MediumAsync && !Address->Provider->MediumSpeedAccurate) { Address->Retries = 1; Timeout.LowPart = (ULONG)(-(ADD_NAME_QUERY_TIMEOUT / 10)); } else { Address->Retries = Address->Provider->AddNameQueryRetries; Timeout.LowPart = (ULONG)(-(LONG)Address->Provider->AddNameQueryTimeout); } Timeout.HighPart = -1; KeSetTimer (&Address->Timer, *(PTIME)&Timeout, &Address->Dpc); (VOID)NbfSendAddNameQuery (Address); // send first ADD_NAME_QUERY. } /* NbfRegisterAddress */ NTSTATUS NbfVerifyAddressObject ( IN PTP_ADDRESS_FILE AddressFile ) /*++ Routine Description: This routine is called to verify that the pointer given us in a file object is in fact a valid address file object. We also verify that the address object pointed to by it is a valid address object, and reference it to keep it from disappearing while we use it. Arguments: AddressFile - potential pointer to a TP_ADDRESS_FILE object Return Value: STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise --*/ { KIRQL oldirql; NTSTATUS status = STATUS_SUCCESS; PTP_ADDRESS address; // // try to verify the address file signature. If the signature is valid, // verify the address pointed to by it and get the address spinlock. // check the address's state, and increment the reference count if it's // ok to use it. Note that the only time we return an error for state is // if the address is closing. // try { if ((AddressFile != (PTP_ADDRESS_FILE)NULL) && (AddressFile->Size == sizeof (TP_ADDRESS_FILE)) && (AddressFile->Type == NBF_ADDRESSFILE_SIGNATURE) ) { // (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { address = AddressFile->Address; if ((address != (PTP_ADDRESS)NULL) && (address->Size == sizeof (TP_ADDRESS)) && (address->Type == NBF_ADDRESS_SIGNATURE) ) { ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); if ((address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { NbfReferenceAddress ("verify", address, AREF_VERIFY); } else { NbfPrint1("NbfVerifyAddress: A %lx closing\n", address); status = STATUS_INVALID_ADDRESS; } RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); } else { NbfPrint1("NbfVerifyAddress: A %lx bad signature\n", address); status = STATUS_INVALID_ADDRESS; } } else { NbfPrint1("NbfVerifyAddress: AF %lx bad signature\n", AddressFile); status = STATUS_INVALID_ADDRESS; } } except(EXCEPTION_EXECUTE_HANDLER) { NbfPrint1("NbfVerifyAddress: AF %lx exception\n", address); return GetExceptionCode(); } return status; } VOID NbfDestroyAddress( IN PVOID Parameter ) /*++ Routine Description: This routine destroys a transport address and removes all references made by it to other objects in the transport. The address structure is returned to nonpaged system pool or our lookaside list. It is assumed that the caller has already removed all addressfile structures associated with this address. The routine is called from a worker thread so that the security descriptor can be accessed. This worked thread is only queued by NbfDerefAddress. The reason for this is that there may be multiple streams of execution which are simultaneously referencing the same address object, and it should not be deleted out from under an interested stream of execution. Arguments: Address - Pointer to a transport address structure to be destroyed. Return Value: NTSTATUS - status of operation. --*/ { KIRQL oldirql; PDEVICE_CONTEXT DeviceContext; PTP_ADDRESS Address = (PTP_ADDRESS)Parameter; IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfDestroyAddress %lx:.\n", Address); } DeviceContext = Address->Provider; SeDeassignSecurity (&Address->SecurityDescriptor); // // Delink this address from its associated device context's address // database. To do this we must spin lock on the device context object, // not on the address. // ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); if (Address->NetworkName) { --DeviceContext->AddressCounts[Address->NetworkName->NetbiosName[0]]; } RemoveEntryList (&Address->Linkage); if (Address->NetworkName != NULL) { ExFreePool (Address->NetworkName); Address->NetworkName = NULL; } // // Now we can deallocate the transport address object. // DeviceContext->AddressTotal += DeviceContext->AddressInUse; ++DeviceContext->AddressSamples; --DeviceContext->AddressInUse; if ((DeviceContext->AddressAllocated - DeviceContext->AddressInUse) > DeviceContext->AddressInitAllocated) { NbfDeallocateAddress (DeviceContext, Address); IF_NBFDBG (NBF_DEBUG_DYNAMIC) { NbfPrint1 ("NBF: Deallocated address at %lx\n", Address); } } else { InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); } RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); NbfDereferenceDeviceContext ("Destroy Address", DeviceContext, DCREF_ADDRESS); // just housekeeping. } /* NbfDestroyAddress */ #if DBG VOID NbfRefAddress( IN PTP_ADDRESS Address ) /*++ Routine Description: This routine increments the reference count on a transport address. Arguments: Address - Pointer to a transport address object. Return Value: none. --*/ { ASSERT (Address->ReferenceCount > 0); // not perfect, but... (VOID)InterlockedIncrement (&Address->ReferenceCount); } /* NbfRefAddress */ #endif VOID NbfDerefAddress( IN PTP_ADDRESS Address ) /*++ Routine Description: This routine dereferences a transport address by decrementing the reference count contained in the structure. If, after being decremented, the reference count is zero, then this routine calls NbfDestroyAddress to remove it from the system. Arguments: Address - Pointer to a transport address object. Return Value: none. --*/ { LONG result; result = InterlockedDecrement (&Address->ReferenceCount); // // If we have deleted all references to this address, then we can // destroy the object. It is okay to have already released the spin // lock at this point because there is no possible way that another // stream of execution has access to the address any longer. // ASSERT (result >= 0); if (result == 0) { ASSERT ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0); ExInitializeWorkItem( &Address->u.DestroyAddressQueueItem, NbfDestroyAddress, (PVOID)Address); ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); } } /* NbfDerefAddress */ VOID NbfAllocateAddressFile( IN PDEVICE_CONTEXT DeviceContext, OUT PTP_ADDRESS_FILE *TransportAddressFile ) /*++ Routine Description: This routine allocates storage for an address file. Some minimal initialization is done on the object. NOTE: This routine is called with the device context spinlock held, or at such a time as synchronization is unnecessary. Arguments: DeviceContext - Pointer to the device context (which is really just the device object with its extension) to be associated with the address. TransportAddressFile - Pointer to a place where this routine will return a pointer to a transport address file structure. It returns NULL if no storage can be allocated. Return Value: None. --*/ { PTP_ADDRESS_FILE AddressFile; if ((DeviceContext->MemoryLimit != 0) && ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS_FILE)) > DeviceContext->MemoryLimit)) { PANIC("NBF: Could not allocate address file: limit\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_LIMIT, 102, sizeof(TP_ADDRESS_FILE), ADDRESS_FILE_RESOURCE_ID); *TransportAddressFile = NULL; return; } AddressFile = (PTP_ADDRESS_FILE)ExAllocatePoolWithTag ( NonPagedPool, sizeof (TP_ADDRESS_FILE), NBF_MEM_TAG_TP_ADDRESS_FILE); if (AddressFile == NULL) { PANIC("NBF: Could not allocate address file: no pool\n"); NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_POOL, 202, sizeof(TP_ADDRESS_FILE), ADDRESS_FILE_RESOURCE_ID); *TransportAddressFile = NULL; return; } RtlZeroMemory (AddressFile, sizeof(TP_ADDRESS_FILE)); DeviceContext->MemoryUsage += sizeof(TP_ADDRESS_FILE); ++DeviceContext->AddressFileAllocated; AddressFile->Type = NBF_ADDRESSFILE_SIGNATURE; AddressFile->Size = sizeof (TP_ADDRESS_FILE); InitializeListHead (&AddressFile->ReceiveDatagramQueue); InitializeListHead (&AddressFile->ConnectionDatabase); *TransportAddressFile = AddressFile; } /* NbfAllocateAddressFile */ VOID NbfDeallocateAddressFile( IN PDEVICE_CONTEXT DeviceContext, IN PTP_ADDRESS_FILE TransportAddressFile ) /*++ Routine Description: This routine frees storage for an address file. NOTE: This routine is called with the device context spinlock held, or at such a time as synchronization is unnecessary. Arguments: DeviceContext - Pointer to the device context (which is really just the device object with its extension) to be associated with the address. TransportAddressFile - Pointer to a transport address file structure. Return Value: None. --*/ { ExFreePool (TransportAddressFile); --DeviceContext->AddressFileAllocated; DeviceContext->MemoryUsage -= sizeof(TP_ADDRESS_FILE); } /* NbfDeallocateAddressFile */ NTSTATUS NbfCreateAddressFile( IN PDEVICE_CONTEXT DeviceContext, OUT PTP_ADDRESS_FILE * AddressFile ) /*++ Routine Description: This routine creates an address file from the pool of ther specified device context. The reference count in the address is automatically set to 1. Arguments: DeviceContext - Pointer to the device context (which is really just the device object with its extension) to be associated with the address. AddressFile - Pointer to a place where this routine will return a pointer to a transport address file structure. Return Value: NTSTATUS - status of operation. --*/ { KIRQL oldirql; PLIST_ENTRY p; PTP_ADDRESS_FILE addressFile; ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); p = RemoveHeadList (&DeviceContext->AddressFilePool); if (p == &DeviceContext->AddressFilePool) { if ((DeviceContext->AddressFileMaxAllocated == 0) || (DeviceContext->AddressFileAllocated < DeviceContext->AddressFileMaxAllocated)) { NbfAllocateAddressFile (DeviceContext, &addressFile); IF_NBFDBG (NBF_DEBUG_DYNAMIC) { NbfPrint1 ("NBF: Allocated address file at %lx\n", addressFile); } } else { NbfWriteResourceErrorLog( DeviceContext, EVENT_TRANSPORT_RESOURCE_SPECIFIC, 402, sizeof(TP_ADDRESS_FILE), ADDRESS_FILE_RESOURCE_ID); addressFile = NULL; } if (addressFile == NULL) { ++DeviceContext->AddressFileExhausted; RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); PANIC ("NbfCreateAddressFile: Could not allocate address file object!\n"); return STATUS_INSUFFICIENT_RESOURCES; } } else { addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); } ++DeviceContext->AddressFileInUse; if (DeviceContext->AddressFileInUse > DeviceContext->AddressFileMaxInUse) { ++DeviceContext->AddressFileMaxInUse; } DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; ++DeviceContext->AddressFileSamples; RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); InitializeListHead (&addressFile->ConnectionDatabase); addressFile->Address = NULL; addressFile->FileObject = NULL; addressFile->Provider = DeviceContext; addressFile->State = ADDRESSFILE_STATE_OPENING; addressFile->ConnectIndicationInProgress = FALSE; addressFile->ReferenceCount = 1; addressFile->CloseIrp = (PIRP)NULL; // // Initialize the request handlers. // addressFile->RegisteredConnectionHandler = FALSE; addressFile->ConnectionHandler = TdiDefaultConnectHandler; addressFile->ConnectionHandlerContext = NULL; addressFile->RegisteredDisconnectHandler = FALSE; addressFile->DisconnectHandler = TdiDefaultDisconnectHandler; addressFile->DisconnectHandlerContext = NULL; addressFile->RegisteredReceiveHandler = FALSE; addressFile->ReceiveHandler = TdiDefaultReceiveHandler; addressFile->ReceiveHandlerContext = NULL; addressFile->RegisteredReceiveDatagramHandler = FALSE; addressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler; addressFile->ReceiveDatagramHandlerContext = NULL; addressFile->RegisteredExpeditedDataHandler = FALSE; addressFile->ExpeditedDataHandler = TdiDefaultRcvExpeditedHandler; addressFile->ExpeditedDataHandlerContext = NULL; addressFile->RegisteredErrorHandler = FALSE; addressFile->ErrorHandler = TdiDefaultErrorHandler; addressFile->ErrorHandlerContext = NULL; *AddressFile = addressFile; return STATUS_SUCCESS; } /* NbfCreateAddress */ NTSTATUS NbfDestroyAddressFile( IN PTP_ADDRESS_FILE AddressFile ) /*++ Routine Description: This routine destroys an address file and removes all references made by it to other objects in the transport. This routine is only called by NbfDereferenceAddressFile. The reason for this is that there may be multiple streams of execution which are simultaneously referencing the same address file object, and it should not be deleted out from under an interested stream of execution. Arguments: AddressFile Pointer to a transport address file structure to be destroyed. Return Value: NTSTATUS - status of operation. --*/ { KIRQL oldirql, oldirql1; PTP_ADDRESS address; PDEVICE_CONTEXT DeviceContext; PIRP CloseIrp; address = AddressFile->Address; DeviceContext = AddressFile->Provider; if (address) { // // This addressfile was associated with an address. // ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); // // remove this addressfile from the address list and disassociate it from // the file handle. // RemoveEntryList (&AddressFile->Linkage); InitializeListHead (&AddressFile->Linkage); if (address->AddressFileDatabase.Flink == &address->AddressFileDatabase) { // // This is the last open of this address, it will close // due to normal dereferencing but we have to set the // CLOSING flag too to stop further references. // ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); address->Flags |= ADDRESS_FLAGS_STOPPING; RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); } AddressFile->Address = NULL; AddressFile->FileObject->FsContext = NULL; AddressFile->FileObject->FsContext2 = NULL; RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); // // We will already have been removed from the ShareAccess // of the owning address. // // // Now dereference the owning address. // NbfDereferenceAddress ("Close", address, AREF_OPEN); // remove the creation hold } // // Save this for later completion. // CloseIrp = AddressFile->CloseIrp; // // return the addressFile to the pool of address files // ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; ++DeviceContext->AddressFileSamples; --DeviceContext->AddressFileInUse; if ((DeviceContext->AddressFileAllocated - DeviceContext->AddressFileInUse) > DeviceContext->AddressFileInitAllocated) { NbfDeallocateAddressFile (DeviceContext, AddressFile); IF_NBFDBG (NBF_DEBUG_DYNAMIC) { NbfPrint1 ("NBF: Deallocated address file at %lx\n", AddressFile); } } else { InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); } RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); if (CloseIrp != (PIRP)NULL) { CloseIrp->IoStatus.Information = 0; CloseIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT); } return STATUS_SUCCESS; } /* NbfDestroyAddressFile */ VOID NbfReferenceAddressFile( IN PTP_ADDRESS_FILE AddressFile ) /*++ Routine Description: This routine increments the reference count on an address file. Arguments: AddressFile - Pointer to a transport address file object. Return Value: none. --*/ { ASSERT (AddressFile->ReferenceCount > 0); // not perfect, but... (VOID)InterlockedIncrement (&AddressFile->ReferenceCount); } /* NbfReferenceAddressFile */ VOID NbfDereferenceAddressFile( IN PTP_ADDRESS_FILE AddressFile ) /*++ Routine Description: This routine dereferences an address file by decrementing the reference count contained in the structure. If, after being decremented, the reference count is zero, then this routine calls NbfDestroyAddressFile to remove it from the system. Arguments: AddressFile - Pointer to a transport address file object. Return Value: none. --*/ { LONG result; result = InterlockedDecrement (&AddressFile->ReferenceCount); // // If we have deleted all references to this address file, then we can // destroy the object. It is okay to have already released the spin // lock at this point because there is no possible way that another // stream of execution has access to the address any longer. // ASSERT (result >= 0); if (result == 0) { NbfDestroyAddressFile (AddressFile); } } /* NbfDerefAddressFile */ PTP_ADDRESS NbfLookupAddress( IN PDEVICE_CONTEXT DeviceContext, IN PNBF_NETBIOS_ADDRESS NetworkName ) /*++ Routine Description: This routine scans the transport addresses defined for the given device context and compares them with the specified NETWORK NAME values. If an exact match is found, then a pointer to the TP_ADDRESS object is returned, and as a side effect, the reference count to the address object is incremented. If the address is not found, then NULL is returned. NOTE: This routine must be called with the DeviceContext spinlock held. Arguments: DeviceContext - Pointer to the device object and its extension. NetworkName - Pointer to an NBF_NETBIOS_ADDRESS structure containing the network name. Return Value: Pointer to the TP_ADDRESS object found, or NULL if not found. --*/ { PTP_ADDRESS address; PLIST_ENTRY p; ULONG i; p = DeviceContext->AddressDatabase.Flink; for (p = DeviceContext->AddressDatabase.Flink; p != &DeviceContext->AddressDatabase; p = p->Flink) { address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { continue; } // // If the network name is specified and the network names don't match, // then the addresses don't match. // i = NETBIOS_NAME_LENGTH; // length of a Netbios name if (address->NetworkName != NULL) { if (NetworkName != NULL) { if (!RtlEqualMemory ( address->NetworkName->NetbiosName, NetworkName->NetbiosName, i)) { continue; } } else { continue; } } else { if (NetworkName != NULL) { continue; } } // // We found the match. Bump the reference count on the address, and // return a pointer to the address object for the caller to use. // IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint2 ("NbfLookupAddress DC %lx: found %lx ", DeviceContext, address); NbfDbgShowAddr (NetworkName); } NbfReferenceAddress ("lookup", address, AREF_LOOKUP); return address; } /* for */ // // The specified address was not found. // IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfLookupAddress DC %lx: did not find ", address); NbfDbgShowAddr (NetworkName); } return NULL; } /* NbfLookupAddress */ PTP_CONNECTION NbfLookupRemoteName( IN PTP_ADDRESS Address, IN PUCHAR RemoteName, IN UCHAR RemoteSessionNumber ) /*++ Routine Description: This routine scans the connections associated with the given address, and determines if there is an connection associated with the specific remote address and session number which is becoming active. This is used in determining whether name queries should be processed, or ignored as duplicates. Arguments: Address - Pointer to the address object. RemoteName - The 16-character Netbios name of the remote. RemoteSessionNumber - The session number assigned to this connection by the remote. Return Value: The connection if one is found, NULL otherwise. --*/ { KIRQL oldirql, oldirql1; PLIST_ENTRY p; PTP_CONNECTION connection; BOOLEAN Found = FALSE; // // Hold the spinlock so the connection database doesn't // change. // ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); for (p=Address->ConnectionDatabase.Flink; p != &Address->ConnectionDatabase; p=p->Flink) { connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); try { ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql1); if (((connection->Flags2 & CONNECTION_FLAGS2_REMOTE_VALID) != 0) && ((connection->Flags & CONNECTION_FLAGS_READY) == 0)) { RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); // // If the remote names match, and the connection's RSN is // the same (or zero, which is a temporary condition where // we should err on the side of caution), then return the // connection, which will cause the NAME_QUERY to be ignored. // if ((RtlEqualMemory(RemoteName, connection->RemoteName, NETBIOS_NAME_LENGTH)) && ((connection->Rsn == RemoteSessionNumber) || (connection->Rsn == 0))) { RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); NbfReferenceConnection ("Lookup found", connection, CREF_LISTENING); Found = TRUE; } } else { RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); } } except(EXCEPTION_EXECUTE_HANDLER) { DbgPrint ("NBF: Got exception in NbfLookupRemoteName\n"); DbgBreakPoint(); RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); return (PTP_CONNECTION)NULL; } if (Found) { return connection; } } RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); return (PTP_CONNECTION)NULL; } BOOLEAN NbfMatchNetbiosAddress( IN PTP_ADDRESS Address, IN UCHAR NameType, IN PUCHAR NetBIOSName ) /*++ Routine Description: This routine is called to compare the addressing information in a TP_ADDRESS object with the 16-byte NetBIOS name in a frame header. If they match, then this routine returns TRUE, else it returns FALSE. Arguments: Address - Pointer to a TP_ADDRESS object. NameType - One of NETBIOS_NAME_TYPE_GROUP, NETBIOS_NAME_TYPE_UNIQUE, or NETBIOS_NAME_TYPE_EITHER. Controls what type we are matching on, if it matters. NetBIOSName - Pointer to a 16-byte character string (non-terminated), or NULL if this is a received broadcast address. Return Value: BOOLEAN, TRUE if match, FALSE if not. --*/ { PULONG AddressNamePointer; ULONG UNALIGNED * NetbiosNamePointer; // // If this is address is the Netbios broadcast address, the comparison // succeeds only if the passed in address is also NULL. // if (Address->NetworkName == NULL) { if (NetBIOSName == NULL) { return TRUE; } else { return FALSE; } } else if (NetBIOSName == NULL) { return FALSE; } // // Do a quick check of the first character in the names. // if (Address->NetworkName->NetbiosName[0] != NetBIOSName[0]) { return FALSE; } // // If name type is important and it doesn't match // this address' type, fail. // if (NameType != NETBIOS_NAME_TYPE_EITHER) { if (Address->NetworkName->NetbiosNameType != (USHORT)NameType) { return FALSE; } } IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { NbfPrint2 ("MatchNetbiosAddress %lx: compare %.16s to ", Address, NetBIOSName); NbfDbgShowAddr (Address->NetworkName); } // // Now compare the 16-character Netbios names as ULONGs // for speed. We know the one stored in the address // structure is aligned. // AddressNamePointer = (PULONG)(Address->NetworkName->NetbiosName); NetbiosNamePointer = (ULONG UNALIGNED *)NetBIOSName; if ((AddressNamePointer[0] == NetbiosNamePointer[0]) && (AddressNamePointer[1] == NetbiosNamePointer[1]) && (AddressNamePointer[2] == NetbiosNamePointer[2]) && (AddressNamePointer[3] == NetbiosNamePointer[3])) { return TRUE; } else { return FALSE; } } /* NbfMatchNetbiosAddress */ VOID NbfStopAddress( IN PTP_ADDRESS Address ) /*++ Routine Description: This routine is called to terminate all activity on an address and destroy the object. This is done in a graceful manner; i.e., all outstanding addressfiles are removed from the addressfile database, and all their activities are shut down. Arguments: Address - Pointer to a TP_ADDRESS object. Return Value: none. --*/ { KIRQL oldirql, oldirql1; PTP_ADDRESS_FILE addressFile; PLIST_ENTRY p; PDEVICE_CONTEXT DeviceContext; DeviceContext = Address->Provider; ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); // // If we're already stopping this address, then don't try to do it again. // if (!(Address->Flags & ADDRESS_FLAGS_STOPPING)) { IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfStopAddress %lx: stopping\n", Address); } NbfReferenceAddress ("Stopping", Address, AREF_TEMP_STOP); ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); Address->Flags |= ADDRESS_FLAGS_STOPPING; RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); // // Run down all addressfiles on this address. This // will leave the address with no references // potentially, but we don't need a temp one // because every place that calls NbfStopAddress // already has a temp reference. // while (!IsListEmpty (&Address->AddressFileDatabase)) { p = RemoveHeadList (&Address->AddressFileDatabase); addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); addressFile->Address = NULL; #if 0 addressFile->FileObject->FsContext = NULL; addressFile->FileObject->FsContext2 = NULL; #endif RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); // // Run-down this addressFile without the lock on. // We don't care about removing ourselves from // the address' ShareAccess because we are // tearing it down. // NbfStopAddressFile (addressFile, Address); // // return the addressFile to the pool of address files // NbfDereferenceAddressFile (addressFile); NbfDereferenceAddress ("stop address", Address, AREF_OPEN); ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); } RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); NbfDereferenceAddress ("Stopping", Address, AREF_TEMP_STOP); } else { RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfStopAddress %lx: already stopping\n", Address); } } } /* NbfStopAddress */ NTSTATUS NbfStopAddressFile( IN PTP_ADDRESS_FILE AddressFile, IN PTP_ADDRESS Address ) /*++ Routine Description: This routine is called to terminate all activity on an AddressFile and destroy the object. We remove every connection and datagram associated with this addressfile from the address database and terminate their activity. Then, if there are no other outstanding addressfiles open on this address, the address will go away. Arguments: AddressFile - pointer to the addressFile to be stopped Address - the owning address for this addressFile (we do not depend upon the pointer in the addressFile because we want this routine to be safe) Return Value: STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not point to a real address. --*/ { KIRQL oldirql, oldirql1; LIST_ENTRY localIrpList; PLIST_ENTRY p, pFlink; PIRP irp; PTP_CONNECTION connection; ULONG fStopping; ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfStopAddressFile %lx: already closing.\n", AddressFile); } return STATUS_SUCCESS; } IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_PNP) { NbfPrint1 ("NbfStopAddressFile %lx: closing.\n", AddressFile); } AddressFile->State = ADDRESSFILE_STATE_CLOSING; InitializeListHead (&localIrpList); // // Run down all connections on this addressfile, and // preform the equivalent of NbfDestroyAssociation // on them. // while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { p = RemoveHeadList (&AddressFile->ConnectionDatabase); connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressFileList); ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql1); if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { // // It is in the process of being disassociated already. // RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); continue; } connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; connection->Flags2 |= CONNECTION_FLAGS2_DESTROY; // Is this needed? RemoveEntryList (&connection->AddressList); InitializeListHead (&connection->AddressList); InitializeListHead (&connection->AddressFileList); connection->AddressFile = NULL; fStopping = connection->Flags2 & CONNECTION_FLAGS2_STOPPING; #if _DBG_ DbgPrint("conn = %p, Flags2 = %08x, fStopping = %08x\n", connection, connection->Flags2, fStopping); #endif if (!fStopping) { #if _DBG_ DbgPrint("Refing BEG\n"); #endif NbfReferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); #if _DBG_ DbgPrint("Refing END\n"); #endif } RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); #if DBG if (NbfDisconnectDebug) { STRING remoteName, localName; remoteName.Length = NETBIOS_NAME_LENGTH - 1; remoteName.Buffer = connection->RemoteName; localName.Length = NETBIOS_NAME_LENGTH - 1; localName.Buffer = AddressFile->Address->NetworkName->NetbiosName; NbfPrint2( "TpStopEndpoint stopping connection to %S from %S\n", &remoteName, &localName ); } #endif if (!fStopping) { #if _DBG_ DbgPrint("Stopping BEG\n"); #endif KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); KeLowerIrql (oldirql1); #if _DBG_ DbgPrint("Stopping END\n"); DbgPrint("Derefing BEG\n"); #endif NbfDereferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); #if _DBG_ DbgPrint("Derefing END\n"); #endif } NbfDereferenceAddress ("Destroy association", Address, AREF_CONNECTION); ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); } // // now remove all of the datagrams owned by this addressfile // // // If the address has a datagram send in progress, skip the // first one, it will complete when the NdisSend completes. // p = Address->SendDatagramQueue.Flink; if (Address->Flags & ADDRESS_FLAGS_SEND_IN_PROGRESS) { ASSERT (p != &Address->SendDatagramQueue); p = p->Flink; } for ( ; p != &Address->SendDatagramQueue; p = pFlink ) { pFlink = p->Flink; irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); if (IoGetCurrentIrpStackLocation(irp)->FileObject->FsContext == AddressFile) { RemoveEntryList (p); InitializeListHead (p); InsertTailList (&localIrpList, p); } } for (p = AddressFile->ReceiveDatagramQueue.Flink; p != &AddressFile->ReceiveDatagramQueue; p = pFlink ) { pFlink = p->Flink; RemoveEntryList (p); InitializeListHead (p); InsertTailList (&localIrpList, p); } // // and finally, signal failure if the address file was waiting for a // registration to complete (Irp is set to NULL when this succeeds). // if (AddressFile->Irp != NULL) { PIRP irp=AddressFile->Irp; #if DBG if ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0) { DbgPrint ("NBF: AddressFile %lx closed while opening!!\n", AddressFile); DbgBreakPoint(); } #endif AddressFile->Irp = NULL; RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_DUPLICATE_NAME; LEAVE_NBF; IoCompleteRequest (irp, IO_NETWORK_INCREMENT); ENTER_NBF; } else { RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); } // // cancel all the datagrams on this address file // while (!IsListEmpty (&localIrpList)) { KIRQL cancelIrql; p = RemoveHeadList (&localIrpList); irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); IoAcquireCancelSpinLock(&cancelIrql); IoSetCancelRoutine(irp, NULL); IoReleaseCancelSpinLock(cancelIrql); irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_NETWORK_NAME_DELETED; IoCompleteRequest (irp, IO_NETWORK_INCREMENT); NbfDereferenceAddress ("Datagram aborted", Address, AREF_REQUEST); } return STATUS_SUCCESS; } /* NbfStopAddressFile */ NTSTATUS NbfCloseAddress( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine is called to close the addressfile pointed to by a file object. If there is any activity to be run down, we will run it down before we terminate the addressfile. We remove every connection and datagram associated with this addressfile from the address database and terminate their activity. Then, if there are no other outstanding addressfiles open on this address, the address will go away. Arguments: Irp - the Irp Address - Pointer to a TP_ADDRESS object. Return Value: STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not point to a real address. --*/ { PTP_ADDRESS address; PTP_ADDRESS_FILE addressFile; addressFile = IrpSp->FileObject->FsContext; IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_PNP) { NbfPrint1 ("NbfCloseAddress AF %lx:\n", addressFile); } addressFile->CloseIrp = Irp; // // We assume that addressFile has already been verified // at this point. // address = addressFile->Address; ASSERT (address); // // Remove us from the access info for this address. // ACQUIRE_RESOURCE_EXCLUSIVE (&addressFile->Provider->AddressResource, TRUE); IoRemoveShareAccess (addressFile->FileObject, &address->u.ShareAccess); RELEASE_RESOURCE (&addressFile->Provider->AddressResource); NbfStopAddressFile (addressFile, address); NbfDereferenceAddressFile (addressFile); // // This removes a reference added by our caller. // NbfDereferenceAddress ("IRP_MJ_CLOSE", address, AREF_VERIFY); return STATUS_PENDING; } /* NbfCloseAddress */ NTSTATUS NbfSendDatagramsOnAddress( PTP_ADDRESS Address ) /*++ Routine Description: This routine attempts to acquire a hold on the SendDatagramQueue of the specified address, prepare the next datagram for shipment, and call NbfSendUIMdlFrame to actually do the work. When NbfSendUIMdlFrame is finished, it will cause an I/O completion routine in UFRAMES.C to be called, at which time this routine is called again to handle the next datagram in the pipeline. NOTE: This routine must be called at a point where the address has another reference that will keep it around. Arguments: Address - a pointer to the address object to send the datagram on. Return Value: NTSTATUS - status of operation. --*/ { KIRQL oldirql; PLIST_ENTRY p; PIRP Irp; TDI_ADDRESS_NETBIOS * remoteAddress; PIO_STACK_LOCATION irpSp; PDEVICE_CONTEXT DeviceContext; PUCHAR SingleSR; UINT SingleSRLength; UINT HeaderLength; PUCHAR LocalName; IF_NBFDBG (NBF_DEBUG_ADDRESS) { NbfPrint1 ("NbfSendDatagramsOnAddress %lx:\n", Address); } DeviceContext = Address->Provider; ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); if (!(Address->Flags & ADDRESS_FLAGS_SEND_IN_PROGRESS)) { // // If the queue is empty, don't do anything. // if (IsListEmpty (&Address->SendDatagramQueue)) { RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); return STATUS_SUCCESS; } // // Mark the address's send datagram queue as held so that the // MDL and NBF header will not be used for two requests at the // same time. // Address->Flags |= ADDRESS_FLAGS_SEND_IN_PROGRESS; // // We own the hold, and we've released the spinlock. So pick off the // next datagram to be sent, and ship it. // p = Address->SendDatagramQueue.Flink; RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); // // If there is no remote Address specified (the Address specified has // length 0), this is a broadcast datagram. If anything is specified, it // will be used as a netbios address. // irpSp = IoGetCurrentIrpStackLocation (Irp); remoteAddress = NbfParseTdiAddress ( ((PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters))-> SendDatagramInformation->RemoteAddress, TRUE); ASSERT (remoteAddress != NULL); // // Build the MAC header. DATAGRAM frames go out as // single-route source routing. // MacReturnSingleRouteSR( &DeviceContext->MacInfo, &SingleSR, &SingleSRLength); MacConstructHeader ( &DeviceContext->MacInfo, Address->UIFrame->Header, DeviceContext->NetBIOSAddress.Address, DeviceContext->LocalAddress.Address, sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS) + (ULONG)Irp->IoStatus.Information, SingleSR, SingleSRLength, &HeaderLength); // // Build the DLC UI frame header. // NbfBuildUIFrameHeader(&(Address->UIFrame->Header[HeaderLength])); HeaderLength += sizeof(DLC_FRAME); // // Build the correct Netbios header. // if (Address->NetworkName != NULL) { LocalName = Address->NetworkName->NetbiosName; } else { LocalName = DeviceContext->ReservedNetBIOSAddress; } if (remoteAddress == (PVOID)-1) { ConstructDatagramBroadcast ( (PNBF_HDR_CONNECTIONLESS)&(Address->UIFrame->Header[HeaderLength]), LocalName); } else { ConstructDatagram ( (PNBF_HDR_CONNECTIONLESS)&(Address->UIFrame->Header[HeaderLength]), (PNAME)remoteAddress->NetbiosName, LocalName); } HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); // // Update our statistics for this datagram. // ++DeviceContext->Statistics.DatagramsSent; ADD_TO_LARGE_INTEGER( &DeviceContext->Statistics.DatagramBytesSent, Irp->IoStatus.Information); // // Munge the packet length, append the data, and send it. // NbfSetNdisPacketLength(Address->UIFrame->NdisPacket, HeaderLength); if (Irp->MdlAddress) { NdisChainBufferAtBack (Address->UIFrame->NdisPacket, (PNDIS_BUFFER)Irp->MdlAddress); } NbfSendUIMdlFrame ( Address); // // The hold will be released in the I/O completion handler in // UFRAMES.C. At that time, if there is another outstanding datagram // to send, it will reset the hold and call this routine again. // } else { RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); } return STATUS_SUCCESS; } /* NbfSendDatagramsOnAddress */