diff options
Diffstat (limited to 'private/ntos/tdi/nbf/address.c')
-rw-r--r-- | private/ntos/tdi/nbf/address.c | 3046 |
1 files changed, 3046 insertions, 0 deletions
diff --git a/private/ntos/tdi/nbf/address.c b/private/ntos/tdi/nbf/address.c new file mode 100644 index 000000000..f5bda0cc7 --- /dev/null +++ b/private/ntos/tdi/nbf/address.c @@ -0,0 +1,3046 @@ +/*++ + +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("<NetBios broadcast>\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; + + // + // BUGBUG: 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 UNALIGNED * +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 UNALIGNED * 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;i<TransportAddress->TAAddressCount;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 UNALIGNED *)(addressName->Address)); + } + } + + addressName = (TA_ADDRESS UNALIGNED *)(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 UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + NbfPrint0 ("NbfValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + NbfPrint0 ("NbfValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(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 UNALIGNED *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), + 'nFBN'); + 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 { + 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. + // + + ExAcquireResourceExclusive (&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. BUGBUG: 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); + ExReleaseResource (&DeviceContext->AddressResource); + NbfDereferenceAddress ("Device context stopping", address, AREF_TEMP_CREATE); + NbfDereferenceAddressFile (addressFile); + return status; + + } + + ExReleaseResource (&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 { + + ExReleaseResource (&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, // tokens locked + 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; + + // + // BUGBUG: 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. + // + // BUGBUG: 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)) { + + ExReleaseResource (&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)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: ShareAccess problem.\n", + address, + addressFile); + } + + NbfDereferenceAddressFile (addressFile); + + } else { + + ExReleaseResource (&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), + 'aFBN'); + 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)); + + NdisAllocatePacketPool( + &NdisStatus, + &Address->UIFramePoolHandle, + 1, + 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; + } + + + // + // This code is similar to NbfAllocateUIFrame. + // + + Address->UIFrame = (PTP_UI_FRAME) ExAllocatePoolWithTag ( + NonPagedPool, + DeviceContext->UIFrameLength, + 'uFBN'); + 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); + 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); + } + 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) { + + 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), + 'fFBN'); + 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; + addressFile->FileObject->FsContext = NULL; + addressFile->FileObject->FsContext2 = NULL; + + 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; + + + 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) { + 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); + + try { + + 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; // BUGBUG: Is this needed? + RemoveEntryList (&connection->AddressList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + NbfReferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfStopAddressFile\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + continue; + } + + 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 + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); + KeLowerIrql (oldirql1); + NbfDereferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); + + 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); + } + +} /* 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) { + 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. + // + + ExAcquireResourceExclusive (&addressFile->Provider->AddressResource, TRUE); + IoRemoveShareAccess (addressFile->FileObject, &address->u.ShareAccess); + ExReleaseResource (&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 UNALIGNED * 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) + + 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 */ |