windows-nt/Source/XPSP1/NT/net/nwlink/ipx/address.c

1956 lines
48 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
address.c
Abstract:
This module contains code which implements the ADDRESS object.
Routines are provided to create, destroy, reference, and dereference,
transport address objects.
Environment:
Kernel mode
Revision History:
Sanjay Anand (SanjayAn) - 22-Sept-1995
BackFill optimization changes added under #if BACK_FILL
Sanjay Anand (SanjayAn) 3-Oct-1995
Changes to support transfer of buffer ownership to transports - tagged [CH]
--*/
#include "precomp.h"
#pragma hdrstop
//
// Map all generic accesses to the same one.
//
static GENERIC_MAPPING AddressGenericMapping =
{ READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL };
TDI_ADDRESS_IPX UNALIGNED *
IpxParseTdiAddress(
IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress
)
/*++
Routine Description:
This routine scans a TRANSPORT_ADDRESS, looking for an address
of type TDI_ADDRESS_TYPE_IPX.
Arguments:
Transport - The generic TDI address.
Return Value:
A pointer to the IPX address, or NULL if none 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 IPX one.
//
for (i=0;i<TransportAddress->TAAddressCount;i++) {
if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) {
if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) {
return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address));
}
}
addressName = (TA_ADDRESS *)(addressName->Address +
addressName->AddressLength);
}
return NULL;
} /* IpxParseTdiAddress */
BOOLEAN
IpxValidateTdiAddress(
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)) {
IpxPrint0 ("IpxValidateTdiAddress: runt address\n");
return FALSE;
}
addressName = &TransportAddress->Address[0];
for (i=0;i<TransportAddress->TAAddressCount;i++) {
if (addressName->Address > AddressEnd) {
IpxPrint0 ("IpxValidateTdiAddress: address too short\n");
return FALSE;
}
addressName = (TA_ADDRESS *)(addressName->Address +
addressName->AddressLength);
}
if ((PUCHAR)addressName > AddressEnd) {
IpxPrint0 ("IpxValidateTdiAddress: address too short\n");
return FALSE;
}
return TRUE;
} /* IpxValidateTdiAddress */
#if DBG
VOID
IpxBuildTdiAddress(
IN PVOID AddressBuffer,
IN ULONG Network,
IN UCHAR Node[6],
IN USHORT Socket
)
/*++
Routine Description:
This routine fills in a TRANSPORT_ADDRESS in the specified
buffer, given the socket, network and node.
Arguments:
AddressBuffer - The buffer that will hold the address.
Network - The network number.
Node - The node address.
Socket - The socket.
Return Value:
None.
--*/
{
TA_IPX_ADDRESS UNALIGNED * IpxAddress;
IpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer;
IpxAddress->TAAddressCount = 1;
IpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX);
IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX;
IpxAddress->Address[0].Address[0].NetworkAddress = Network;
IpxAddress->Address[0].Address[0].Socket = Socket;
RtlCopyMemory(IpxAddress->Address[0].Address[0].NodeAddress, Node, 6);
} /* IpxBuildTdiAddress */
#endif
NTSTATUS
IpxOpenAddress(
IN PDEVICE Device,
IN PREQUEST Request
)
{
return(IpxOpenAddressM(Device, Request, 0));
}
NTSTATUS
IpxOpenAddressM(
IN PDEVICE Device,
IN PREQUEST Request,
IN ULONG Index
)
/*++
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:
Device - pointer to the device describing the IPX transport.
Request - a pointer to the request used for the creation of the address.
Return Value:
NTSTATUS - status of operation.
--*/
{
NTSTATUS status;
PADDRESS Address;
PADDRESS_FILE AddressFile;
PFILE_FULL_EA_INFORMATION ea;
TRANSPORT_ADDRESS UNALIGNED *name;
TA_ADDRESS *AddressName;
USHORT Socket;
ULONG DesiredShareAccess;
CTELockHandle LockHandle;
PACCESS_STATE AccessState;
ACCESS_MASK GrantedAccess;
BOOLEAN AccessAllowed;
int i;
BOOLEAN found = FALSE;
#ifdef ISN_NT
PIRP Irp = (PIRP)Request;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
#endif
INT Size = 0;
//
// If we are a dedicated router, we cannot let addresses
// be opened.
//
if (Device->DedicatedRouter && (REQUEST_CODE(Request) != MIPX_RT_CREATE)) {
return STATUS_NOT_SUPPORTED;
}
//
// The network name is in the EA, passed in the request.
//
ea = OPEN_REQUEST_EA_INFORMATION(Request);
if (ea == NULL) {
IpxPrint1("OpenAddress: REQUEST %lx has no EA\n", Request);
return STATUS_NONEXISTENT_EA_ENTRY;
}
//
// this may be a valid name; parse the name from the EA and use it if OK.
//
name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1];
//
// 126042
//
if (ea->EaValueLength < (sizeof(TRANSPORT_ADDRESS) -1)) {
IPX_DEBUG(ADDRESS, ("The ea value length does not match the TA address length\n"));
DbgPrint("IPX: STATUS_INVALID_EA_NAME - 1\n");
return STATUS_INVALID_EA_NAME;
}
AddressName = (PTA_ADDRESS)&name->Address[0];
Size = FIELD_OFFSET(TRANSPORT_ADDRESS, Address) + FIELD_OFFSET(TA_ADDRESS, Address) + AddressName->AddressLength;
//
// The name can be passed with multiple entries; we'll take and use only
// the first one of type IPX.
//
//DbgPrint("Size (%d) & EaValueLength (%d)", Size, ea->EaValueLength);
if (Size > ea->EaValueLength) {
DbgPrint("EA:%lx, Name:%lx, AddressName:%lx\n", ea, name, AddressName);
CTEAssert(FALSE);
}
for (i=0;i<name->TAAddressCount;i++) {
//
// 126042
//
if (Size > ea->EaValueLength) {
IPX_DEBUG(ADDRESS, ("The EA value length does not match the TA address length (2)\n"));
DbgPrint("IPX: STATUS_INVALID_EA_NAME - 2\n");
return STATUS_INVALID_EA_NAME;
}
if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) {
if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) {
Socket = ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket;
found = TRUE;
}
break;
} else {
AddressName = (PTA_ADDRESS)(AddressName->Address +
AddressName->AddressLength);
Size += FIELD_OFFSET(TA_ADDRESS, Address);
if (Size < ea->EaValueLength) {
Size += AddressName->AddressLength;
} else {
break;
}
}
}
if (!found) {
IPX_DEBUG (ADDRESS, ("OpenAddress, request %lx has no IPX Address\n", Request));
return STATUS_NONEXISTENT_EA_ENTRY;
}
if (Socket == 0) {
Socket = IpxAssignSocket (Device);
if (Socket == 0) {
IPX_DEBUG (ADDRESS, ("OpenAddress, no unique socket found\n"));
#ifdef SNMP
++IPX_MIB_ENTRY(Device, SysOpenSocketFails);
#endif SNMP
return STATUS_INSUFFICIENT_RESOURCES;
} else {
IPX_DEBUG (ADDRESS, ("OpenAddress, assigned socket %lx\n", REORDER_USHORT(Socket)));
}
} else {
IPX_DEBUG (ADDRESS, ("OpenAddress, socket %lx\n", REORDER_USHORT(Socket)));
}
//
// get an address file structure to represent this address.
//
AddressFile = IpxCreateAddressFile (Device);
if (AddressFile == (PADDRESS_FILE)NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// We mark this socket specially.
//
if (Socket == SAP_SOCKET) {
AddressFile->IsSapSocket = TRUE;
AddressFile->SpecialReceiveProcessing = TRUE;
}
//
// 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.
//
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite (&Device->AddressResource, TRUE);
CTEGetLock (&Device->Lock, &LockHandle);
Address = IpxLookupAddress (Device, Socket);
if (Address == NULL) {
CTEFreeLock (&Device->Lock, LockHandle);
//
// This address doesn't exist. Create it.
// registering it.
//
Address = IpxCreateAddress (
Device,
Socket);
if (Address != (PADDRESS)NULL) {
//
// Set this now in case we have to deref.
//
AddressFile->AddressLock = &Address->Lock;
if (REQUEST_CODE(Request) == MIPX_RT_CREATE) {
Address->RtAdd = TRUE;
Address->Index = Index;
} else {
Address->RtAdd = FALSE;
}
#ifdef ISN_NT
//
// 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).
//
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
status = SeAssignSecurity(
NULL, // parent descriptor
AccessState->SecurityDescriptor,
&Address->SecurityDescriptor,
FALSE, // is directory
&AccessState->SubjectSecurityContext,
&AddressGenericMapping,
NonPagedPool);
if (!NT_SUCCESS(status)) {
//
// Error, return status.
//
IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess);
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
IpxDereferenceAddress (Address, AREF_ADDRESS_FILE);
IpxDereferenceAddressFile (AddressFile, AFREF_CREATE);
return status;
}
#endif
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
//
// if the adapter isn't ready, we can't do any of this; get out
//
if (Device->State == DEVICE_STATE_STOPPING) {
IpxDereferenceAddress (Address, AREF_ADDRESS_FILE);
IpxDereferenceAddressFile (AddressFile, AFREF_CREATE);
status = STATUS_DEVICE_NOT_READY;
} else {
REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile;
REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE;
#ifdef ISN_NT
AddressFile->FileObject = IrpSp->FileObject;
#endif
AddressFile->Request = Request;
AddressFile->Address = Address;
CTEGetLock (&Address->Lock, &LockHandle);
InsertTailList (&Address->AddressFileDatabase, &AddressFile->Linkage);
CTEFreeLock (&Address->Lock, LockHandle);
AddressFile->Request = NULL;
AddressFile->State = ADDRESSFILE_STATE_OPEN;
status = STATUS_SUCCESS;
}
} else {
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
//
// If the address could not be created, and is not in the
// process of being created, then we can't open up an address.
// Since we can't use the AddressLock to deref, we just destroy
// the address file.
//
IpxDestroyAddressFile (AddressFile);
// 288208
status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
CTEFreeLock (&Device->Lock, LockHandle);
IPX_DEBUG (ADDRESS, ("Add to address %lx\n", Address));
//
// We never allow shared access to a RT address. So, check that
// we don't have a "RT address create" request and also that the
// address has not only been taken up by a RT Address request. If
// and only if both the above
//
if ((REQUEST_CODE(Request) != MIPX_RT_CREATE) && (!Address->RtAdd))
{
//
// Set this now in case we have to deref.
//
AddressFile->AddressLock = &Address->Lock;
//
// The address already exists. Check the ACL and see if we
// can access it. If so, simply use this address as our address.
//
#ifdef ISN_NT
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);
#else // ISN_NT
AccessAllowed = TRUE;
#endif // ISN_NT
if (!AccessAllowed) {
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
IpxDereferenceAddressFile (AddressFile, AFREF_CREATE);
} else {
#ifdef ISN_NT
//
// NtBug: 132051. Make sure you dont give more access than reqd.
//
AccessState->PreviouslyGrantedAccess |= GrantedAccess;
AccessState->RemainingDesiredAccess &= ~( GrantedAccess | MAXIMUM_ALLOWED );
//
// 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);
#else // ISN_NT
status = STATUS_SUCCESS;
#endif // ISN_NT
if (!NT_SUCCESS (status)) {
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
IpxDereferenceAddressFile (AddressFile, AFREF_CREATE);
} else {
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
CTEGetLock (&Address->Lock, &LockHandle);
InsertTailList (
&Address->AddressFileDatabase,
&AddressFile->Linkage);
AddressFile->Request = NULL;
AddressFile->Address = Address;
#ifdef ISN_NT
AddressFile->FileObject = IrpSp->FileObject;
#endif
AddressFile->State = ADDRESSFILE_STATE_OPEN;
IpxReferenceAddress (Address, AREF_ADDRESS_FILE);
REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile;
REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE;
CTEFreeLock (&Address->Lock, LockHandle);
status = STATUS_SUCCESS;
}
}
}
else
{
DbgPrint("IpxOpenAddress: ACCESS DENIED - duplicate address\n");
status = STATUS_ACCESS_DENIED;
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
IpxDereferenceAddressFile (AddressFile, AFREF_CREATE);
}
//
// Remove the reference from IpxLookupAddress.
//
IpxDereferenceAddress (Address, AREF_LOOKUP);
}
return status;
} /* IpxOpenAddress */
USHORT
IpxAssignSocket(
IN PDEVICE Device
)
/*++
Routine Description:
This routine assigns a socket that is unique within a range
of SocketUniqueness.
Arguments:
Device - Pointer to the device context.
Return Value:
The assigned socket number, or 0 if a unique one cannot
be found.
--*/
{
USHORT InitialSocket, CurrentSocket, AddressSocket;
ULONG CurrentHash;
BOOLEAN Conflict;
PLIST_ENTRY p;
PADDRESS Address;
CTELockHandle LockHandle;
//
// Loop through all possible sockets, starting at
// Device->CurrentSocket, looking for a suitable one.
// Device->CurrentSocket rotates through the possible
// sockets to improve the chances of finding one
// quickly.
//
CTEGetLock (&Device->Lock, &LockHandle);
InitialSocket = Device->CurrentSocket;
Device->CurrentSocket = (USHORT)(Device->CurrentSocket + Device->SocketUniqueness);
if ((USHORT)(Device->CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) {
Device->CurrentSocket = Device->SocketStart;
}
CurrentSocket = InitialSocket;
do {
//
// Scan all addresses; if we find one with a socket
// that conflicts with this one, we can't use it.
//
// NOTE: Device->Lock is acquired here.
//
Conflict = FALSE;
for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) {
for (p = Device->AddressDatabases[CurrentHash].Flink;
p != &Device->AddressDatabases[CurrentHash];
p = p->Flink) {
Address = CONTAINING_RECORD (p, ADDRESS, Linkage);
AddressSocket = REORDER_USHORT(Address->Socket);
if ((AddressSocket + Device->SocketUniqueness > CurrentSocket) &&
(AddressSocket < CurrentSocket + Device->SocketUniqueness)) {
Conflict = TRUE;
break;
}
}
//
// If we've found a conflict, no need to check the other
// queues.
//
if (Conflict) {
break;
}
}
CTEFreeLock (&Device->Lock, LockHandle);
//
// We intentionally free the lock here so that we
// never spend too much time with it held.
//
if (!Conflict) {
//
// We went through the address list without
// finding a conflict; use this socket.
//
return REORDER_USHORT(CurrentSocket);
}
CurrentSocket = (USHORT)(CurrentSocket + Device->SocketUniqueness);
if ((USHORT)(CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) {
CurrentSocket = Device->SocketStart;
}
CTEGetLock (&Device->Lock, &LockHandle);
} while (CurrentSocket != InitialSocket);
CTEFreeLock (&Device->Lock, LockHandle);
//
// Could not find one to assign.
//
return (USHORT)0;
} /* IpxAssignSocket */
PADDRESS
IpxCreateAddress(
IN PDEVICE Device,
IN USHORT Socket
)
/*++
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 Device
spinlock held.
Arguments:
Device - Pointer to the device context (which is really just
the device object with its extension) to be associated with the
address.
Socket - The socket to assign to this address.
Return Value:
The newly created address, or NULL if none can be allocated.
--*/
{
PADDRESS Address;
PIPX_SEND_RESERVED SendReserved;
PIPX_RECEIVE_RESERVED ReceiveReserved;
NDIS_STATUS Status;
IPX_DEFINE_LOCK_HANDLE (LockHandle)
Address = (PADDRESS)IpxAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address");
if (Address == NULL) {
IPX_DEBUG (ADDRESS, ("Create address %lx failed\n", REORDER_USHORT(Socket)));
return NULL;
}
IPX_DEBUG (ADDRESS, ("Create address %lx (%lx)\n", Address, REORDER_USHORT(Socket)));
RtlZeroMemory (Address, sizeof(ADDRESS));
#ifndef IPX_OWN_PACKETS
IpxAllocateSingleSendPacket(Device, &Address->SendPacket, &Status);
if (Status != NDIS_STATUS_SUCCESS) {
goto Fail1;
}
#endif
if (IpxInitializeSendPacket (Device, &Address->SendPacket, Address->SendPacketHeader) != STATUS_SUCCESS) {
#ifndef IPX_OWN_PACKETS
Fail1:
#endif
Address->SendPacketInUse = TRUE;
} else {
SendReserved = SEND_RESERVED(&Address->SendPacket);
SendReserved->Address = Address;
SendReserved->OwnedByAddress = TRUE;
Address->SendPacketInUse = FALSE;
#ifdef IPX_TRACK_POOL
SendReserved->Pool = NULL;
#endif
}
#if BACK_FILL
{
PIPX_SEND_RESERVED BackFillReserved;
#ifndef IPX_OWN_PACKETS
IpxAllocateSingleSendPacket(Device, &Address->BackFillPacket, &Status);
if (Status != NDIS_STATUS_SUCCESS) {
goto Fail2;
}
#endif
if (IpxInitializeBackFillPacket (Device, &Address->BackFillPacket, NULL) != STATUS_SUCCESS) {
#ifndef IPX_OWN_PACKETS
Fail2:
#endif
Address->BackFillPacketInUse = TRUE;
} else {
BackFillReserved = SEND_RESERVED(&Address->BackFillPacket);
BackFillReserved->Address = Address;
Address->BackFillPacketInUse = FALSE;
BackFillReserved->OwnedByAddress = TRUE;
#ifdef IPX_TRACK_POOL
BackFillReserved->Pool = NULL;
#endif
}
}
#endif
#ifndef IPX_OWN_PACKETS
IpxAllocateSingleReceivePacket(Device, &Address->ReceivePacket, &Status);
if (Status != NDIS_STATUS_SUCCESS) {
goto Fail3;
}
#endif
if (IpxInitializeReceivePacket (Device, &Address->ReceivePacket) != STATUS_SUCCESS) {
#ifndef IPX_OWN_PACKETS
Fail3:
#endif
Address->ReceivePacketInUse = TRUE;
} else {
ReceiveReserved = RECEIVE_RESERVED(&Address->ReceivePacket);
ReceiveReserved->Address = Address;
ReceiveReserved->OwnedByAddress = TRUE;
Address->ReceivePacketInUse = FALSE;
#ifdef IPX_TRACK_POOL
ReceiveReserved->Pool = NULL;
#endif
}
Address->Type = IPX_ADDRESS_SIGNATURE;
Address->Size = sizeof (ADDRESS);
Address->Device = Device;
Address->DeviceLock = &Device->Lock;
CTEInitLock (&Address->Lock);
InitializeListHead (&Address->AddressFileDatabase);
Address->ReferenceCount = 1;
#if DBG
Address->RefTypes[AREF_ADDRESS_FILE] = 1;
#endif
Address->Socket = Socket;
Address->SendSourceSocket = Socket;
//
// Save our local address for building datagrams quickly.
//
RtlCopyMemory (&Address->LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket));
Address->LocalAddress.Socket = Socket;
//
// 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.
//
IPX_GET_LOCK (&Device->Lock, &LockHandle);
InsertTailList (&Device->AddressDatabases[IPX_HASH_SOCKET(Socket)], &Address->Linkage);
IPX_FREE_LOCK (&Device->Lock, LockHandle);
IpxReferenceDevice (Device, DREF_ADDRESS);
return Address;
} /* IpxCreateAddress */
NTSTATUS
IpxVerifyAddressFile(
IN PADDRESS_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 ADDRESS_FILE object
Return Value:
STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise
--*/
{
CTELockHandle LockHandle;
NTSTATUS status = STATUS_SUCCESS;
PADDRESS 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->Size == sizeof (ADDRESS_FILE)) &&
(AddressFile->Type == IPX_ADDRESSFILE_SIGNATURE) ) {
// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) {
Address = AddressFile->Address;
if ((Address->Size == sizeof (ADDRESS)) &&
(Address->Type == IPX_ADDRESS_SIGNATURE) ) {
CTEGetLock (&Address->Lock, &LockHandle);
if (!Address->Stopping) {
IpxReferenceAddressFileLock (AddressFile, AFREF_VERIFY);
} else {
IpxPrint1("IpxVerifyAddressFile: A %lx closing\n", Address);
status = STATUS_INVALID_ADDRESS;
}
CTEFreeLock (&Address->Lock, LockHandle);
} else {
IpxPrint1("IpxVerifyAddressFile: A %lx bad signature\n", Address);
status = STATUS_INVALID_ADDRESS;
}
} else {
IpxPrint1("IpxVerifyAddressFile: AF %lx bad signature\n", AddressFile);
status = STATUS_INVALID_ADDRESS;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
IpxPrint1("IpxVerifyAddressFile: AF %lx exception\n", Address);
return GetExceptionCode();
}
return status;
} /* IpxVerifyAddressFile */
VOID
IpxDestroyAddress(
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. It is assumed
that the caller has already removed all addressfile structures associated
with this address.
It is called from a worker thread queue by IpxDerefAddress when
the reference count goes to 0.
This thread is only queued by IpxDerefAddress. 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.
--*/
{
PADDRESS Address = (PADDRESS)Parameter;
PDEVICE Device = Address->Device;
CTELockHandle LockHandle;
IPX_DEBUG (ADDRESS, ("Destroy address %lx (%lx)\n", Address, REORDER_USHORT(Address->Socket)));
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.
//
CTEGetLock (&Device->Lock, &LockHandle);
RemoveEntryList (&Address->Linkage);
CTEFreeLock (&Device->Lock, LockHandle);
if (!Address->SendPacketInUse) {
IpxDeinitializeSendPacket (Device, &Address->SendPacket);
#ifndef IPX_OWN_PACKETS
IpxFreeSingleSendPacket (Device, Address->SendPacket);
#endif
}
if (!Address->ReceivePacketInUse) {
IpxDeinitializeReceivePacket (Device, &Address->ReceivePacket);
#ifndef IPX_OWN_PACKETS
IpxFreeSingleReceivePacket (Device, Address->ReceivePacket);
#endif
}
#if BACK_FILL
if (!Address->BackFillPacketInUse) {
IpxDeinitializeBackFillPacket (Device, &Address->BackFillPacket);
#ifndef IPX_OWN_PACKETS
IpxFreeSingleSendPacket (Device, Address->BackFillPacket);
#endif
}
#endif
IpxFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address");
IpxDereferenceDevice (Device, DREF_ADDRESS);
} /* IpxDestroyAddress */
#if DBG
VOID
IpxRefAddress(
IN PADDRESS Address
)
/*++
Routine Description:
This routine increments the reference count on a transport address.
Arguments:
Address - Pointer to a transport address object.
Return Value:
none.
--*/
{
CTEAssert (Address->ReferenceCount > 0); // not perfect, but...
(VOID)InterlockedIncrement(&Address->ReferenceCount);
} /* IpxRefAddress */
VOID
IpxRefAddressLock(
IN PADDRESS Address
)
/*++
Routine Description:
This routine increments the reference count on a transport address
when the device lock is already held.
Arguments:
Address - Pointer to a transport address object.
Return Value:
none.
--*/
{
CTEAssert (Address->ReferenceCount > 0); // not perfect, but...
// ++Address->ReferenceCount;
(VOID)InterlockedIncrement(&Address->ReferenceCount);
} /* IpxRefAddressLock */
#endif
VOID
IpxDerefAddress(
IN PADDRESS 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
IpxDestroyAddress to remove it from the system.
Arguments:
Address - Pointer to a transport address object.
Return Value:
none.
--*/
{
ULONG oldvalue;
oldvalue = IPX_ADD_ULONG (
&Address->ReferenceCount,
(ULONG)-1,
Address->DeviceLock);
//
// 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.
//
CTEAssert (oldvalue != 0);
if (oldvalue == 1) {
#if ISN_NT
ExInitializeWorkItem(
&Address->u.DestroyAddressQueueItem,
IpxDestroyAddress,
(PVOID)Address);
ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue);
#else
IpxDestroyAddress(Address);
#endif
}
} /* IpxDerefAddress */
VOID
IpxDerefAddressSync(
IN PADDRESS 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
IpxDestroyAddress to remove it from the system. This routine can
only be called when we are synchronized (inside an IPX_SYNC_START/
IPX_SYNC_END pair, with a lock held, or in an indication).
Arguments:
Address - Pointer to a transport address object.
Return Value:
none.
--*/
{
ULONG oldvalue;
oldvalue = IPX_ADD_ULONG (
&Address->ReferenceCount,
(ULONG)-1,
Address->DeviceLock);
//
// 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.
//
CTEAssert (oldvalue != 0);
if (oldvalue == 1) {
#if ISN_NT
ExInitializeWorkItem(
&Address->u.DestroyAddressQueueItem,
IpxDestroyAddress,
(PVOID)Address);
ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue);
#else
IpxDestroyAddress(Address);
#endif
}
} /* IpxDerefAddressSync */
PADDRESS_FILE
IpxCreateAddressFile(
IN PDEVICE Device
)
/*++
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:
Device - Pointer to the device context (which is really just
the device object with its extension) to be associated with the
address.
Return Value:
The allocate address file or NULL.
--*/
{
CTELockHandle LockHandle;
PADDRESS_FILE AddressFile;
AddressFile = (PADDRESS_FILE)IpxAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile");
if (AddressFile == NULL) {
IPX_DEBUG (ADDRESS, ("Create address file failed\n"));
return NULL;
}
IPX_DEBUG (ADDRESS, ("Create address file %lx\n", AddressFile));
RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE));
AddressFile->Type = IPX_ADDRESSFILE_SIGNATURE;
AddressFile->Size = sizeof (ADDRESS_FILE);
CTEGetLock (&Device->Lock, &LockHandle);
InitializeListHead (&AddressFile->ReceiveDatagramQueue);
CTEFreeLock (&Device->Lock, LockHandle);
#if 0
AddressFile->SpecialReceiveProcessing = FALSE;
AddressFile->ExtendedAddressing = FALSE;
AddressFile->ReceiveIpxHeader = FALSE;
AddressFile->FilterOnPacketType = FALSE;
AddressFile->DefaultPacketType = 0;
AddressFile->Address = NULL;
#ifdef ISN_NT
AddressFile->FileObject = NULL;
#endif
#endif
AddressFile->Device = Device;
AddressFile->State = ADDRESSFILE_STATE_OPENING;
AddressFile->ReferenceCount = 1;
#if DBG
AddressFile->RefTypes[AFREF_CREATE] = 1;
#endif
AddressFile->CloseRequest = (PREQUEST)NULL;
//
// Initialize the request handlers.
//
AddressFile->RegisteredReceiveDatagramHandler = FALSE;
AddressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler;
AddressFile->ReceiveDatagramHandlerContext = NULL;
//
// [CH] Added these handlers for chained buffer receives
//
AddressFile->RegisteredChainedReceiveDatagramHandler = FALSE;
AddressFile->ChainedReceiveDatagramHandler = TdiDefaultChainedRcvDatagramHandler;
AddressFile->ChainedReceiveDatagramHandlerContext = NULL;
AddressFile->RegisteredErrorHandler = FALSE;
AddressFile->ErrorHandler = TdiDefaultErrorHandler;
AddressFile->ErrorHandlerContext = NULL;
return AddressFile;
} /* IpxCreateAddressFile */
NTSTATUS
IpxDestroyAddressFile(
IN PADDRESS_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 IpxDereferenceAddressFile. 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.
--*/
{
CTELockHandle LockHandle, LockHandle1;
PADDRESS Address;
PDEVICE Device;
PREQUEST CloseRequest;
IPX_DEBUG (ADDRESS, ("Destroy address file %lx\n", AddressFile));
Address = AddressFile->Address;
Device = AddressFile->Device;
if (Address) {
//
// This addressfile was associated with an address.
//
CTEGetLock (&Address->Lock, &LockHandle);
//
// 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.
//
CTEGetLock (&Device->Lock, &LockHandle1);
Address->Stopping = TRUE;
if (Device->LastAddress == Address) {
Device->LastAddress = NULL;
}
CTEFreeLock (&Device->Lock, LockHandle1);
}
AddressFile->Address = NULL;
#ifdef ISN_NT
AddressFile->FileObject->FsContext = NULL;
AddressFile->FileObject->FsContext2 = NULL;
#endif
CTEFreeLock (&Address->Lock, LockHandle);
//
// We will already have been removed from the ShareAccess
// of the owning address.
//
//
// Now dereference the owning address.
//
IpxDereferenceAddress (Address, AREF_ADDRESS_FILE);
}
//
// Save this for later completion.
//
CloseRequest = AddressFile->CloseRequest;
//
// return the addressFile to the pool of address files
//
IpxFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile");
if (CloseRequest != (PREQUEST)NULL) {
REQUEST_INFORMATION(CloseRequest) = 0;
REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS;
IpxCompleteRequest (CloseRequest);
IpxFreeRequest (Device, CloseRequest);
}
return STATUS_SUCCESS;
} /* IpxDestroyAddressFile */
#if DBG
VOID
IpxRefAddressFile(
IN PADDRESS_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.
--*/
{
CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but...
(VOID)IPX_ADD_ULONG (
&AddressFile->ReferenceCount,
1,
AddressFile->AddressLock);
} /* IpxRefAddressFile */
VOID
IpxRefAddressFileLock(
IN PADDRESS_FILE AddressFile
)
/*++
Routine Description:
This routine increments the reference count on an address file.
IT IS CALLED WITH THE ADDRESS LOCK HELD.
Arguments:
AddressFile - Pointer to a transport address file object.
Return Value:
none.
--*/
{
CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but...
//++AddressFile->ReferenceCount;
(VOID)InterlockedIncrement(&AddressFile->ReferenceCount);
} /* IpxRefAddressFileLock */
VOID
IpxRefAddressFileSync(
IN PADDRESS_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.
--*/
{
CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but...
(VOID)IPX_ADD_ULONG (
&AddressFile->ReferenceCount,
1,
AddressFile->AddressLock);
} /* IpxRefAddressFileSync */
VOID
IpxDerefAddressFile(
IN PADDRESS_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
IpxDestroyAddressFile to remove it from the system.
Arguments:
AddressFile - Pointer to a transport address file object.
Return Value:
none.
--*/
{
ULONG oldvalue;
oldvalue = IPX_ADD_ULONG (
&AddressFile->ReferenceCount,
(ULONG)-1,
AddressFile->AddressLock);
//
// 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.
//
CTEAssert (oldvalue > 0);
if (oldvalue == 1) {
IpxDestroyAddressFile (AddressFile);
}
} /* IpxDerefAddressFile */
VOID
IpxDerefAddressFileSync(
IN PADDRESS_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
IpxDestroyAddressFile to remove it from the system. This routine
can only be called when we are synchronized (inside an IPX_SYNC_START/
IPX_SYNC_END pair, with a lock held, or in an indication).
Arguments:
AddressFile - Pointer to a transport address file object.
Return Value:
none.
--*/
{
ULONG oldvalue;
oldvalue = IPX_ADD_ULONG (
&AddressFile->ReferenceCount,
(ULONG)-1,
AddressFile->AddressLock);
//
// 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.
//
CTEAssert (oldvalue > 0);
if (oldvalue == 1) {
IpxDestroyAddressFile (AddressFile);
}
} /* IpxDerefAddressFileSync */
#endif
PADDRESS
IpxLookupAddress(
IN PDEVICE Device,
IN USHORT Socket
)
/*++
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
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 Device
spinlock held.
Arguments:
Device - Pointer to the device object and its extension.
Socket - The socket to look up.
Return Value:
Pointer to the ADDRESS object found, or NULL if not found.
--*/
{
PADDRESS Address;
PLIST_ENTRY p;
ULONG Hash = IPX_HASH_SOCKET (Socket);
p = Device->AddressDatabases[Hash].Flink;
for (p = Device->AddressDatabases[Hash].Flink;
p != &Device->AddressDatabases[Hash];
p = p->Flink) {
Address = CONTAINING_RECORD (p, ADDRESS, Linkage);
if (Address->Stopping) {
continue;
}
if (Address->Socket == Socket) {
//
// We found the match. Bump the reference count on the address, and
// return a pointer to the address object for the caller to use.
//
IpxReferenceAddressLock (Address, AREF_LOOKUP);
return Address;
}
}
//
// The specified address was not found.
//
return NULL;
} /* IpxLookupAddress */
NTSTATUS
IpxStopAddressFile(
IN PADDRESS_FILE AddressFile
)
/*++
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
Return Value:
STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request
is not for a real address.
--*/
{
CTELockHandle LockHandle;
PREQUEST Request;
PADDRESS Address = AddressFile->Address;
PLIST_ENTRY p;
KIRQL irql;
IoAcquireCancelSpinLock( &irql );
CTEGetLock (&Address->Lock, &LockHandle);
if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) {
CTEFreeLock (&Address->Lock, LockHandle);
IoReleaseCancelSpinLock( irql );
return STATUS_SUCCESS;
}
AddressFile->State = ADDRESSFILE_STATE_CLOSING;
while (!(IsListEmpty(&AddressFile->ReceiveDatagramQueue))) {
p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue);
Request = LIST_ENTRY_TO_REQUEST (p);
REQUEST_INFORMATION(Request) = 0;
REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED;
IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL);
CTEFreeLock(&Address->Lock, LockHandle);
IoReleaseCancelSpinLock( irql );
IpxCompleteRequest (Request);
IpxFreeRequest (Device, Request);
IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM);
IoAcquireCancelSpinLock( &irql );
CTEGetLock(&Address->Lock, &LockHandle);
}
CTEFreeLock(&Address->Lock, LockHandle);
IoReleaseCancelSpinLock( irql );
return STATUS_SUCCESS;
} /* IpxStopAddressFile */
NTSTATUS
IpxCloseAddressFile(
IN PDEVICE Device,
IN PREQUEST Request
)
/*++
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:
Request - the close request.
Return Value:
STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the
request does not point to a real address.
--*/
{
PADDRESS Address;
PADDRESS_FILE AddressFile;
CTELockHandle LockHandle;
AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request);
AddressFile->CloseRequest = Request;
//
// We assume that addressFile has already been verified
// at this point.
//
Address = AddressFile->Address;
CTEAssert (Address);
//
// Remove us from the access info for this address.
//
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite (&Device->AddressResource, TRUE);
#ifdef ISN_NT
IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess);
#endif
ExReleaseResourceLite (&Device->AddressResource);
KeLeaveCriticalRegion();
//
// If this address file had broadcasts enabled, turn it off.
//
//
// Not needed anymore
//
/*
CTEGetLock (&Device->Lock, &LockHandle);
if (AddressFile->EnableBroadcast) {
AddressFile->EnableBroadcast = FALSE;
IpxRemoveBroadcast (Device);
}
CTEFreeLock (&Device->Lock, LockHandle);
*/
IpxStopAddressFile (AddressFile);
IpxDereferenceAddressFile (AddressFile, AFREF_CREATE);
return STATUS_PENDING;
} /* IpxCloseAddressFile */