Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3094 lines
86 KiB

/*++
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;
//
// 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;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 *)(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;i<TransportAddress->TAAddressCount;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 */