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.
2438 lines
72 KiB
2438 lines
72 KiB
/*++
|
|
|
|
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
connobj.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code which implements the TP_CONNECTION object.
|
|
Routines are provided to create, destroy, reference, and dereference,
|
|
transport connection objects.
|
|
|
|
A word about connection reference counts:
|
|
|
|
With TDI version 2, connections live on even after they have been stopped.
|
|
This necessitated changing the way NBF handles connection reference counts,
|
|
making the stopping of a connection only another way station in the life
|
|
of a connection, rather than its demise. Reference counts now work like
|
|
this:
|
|
|
|
Connection State Reference Count Flags
|
|
------------------ ----------------- --------
|
|
Opened, no activity 1 0
|
|
Open, Associated 2 FLAGS2_ASSOCIATED
|
|
Open, Assoc., Connected 3 FLAGS_READY
|
|
Above + activity >3 varies
|
|
Open, Assoc., Stopping >3 FLAGS_STOPPING
|
|
Open, Assoc., Stopped 3 FLAGS_STOPPING
|
|
Open, Disassoc. Complete 2 FLAGS_STOPPING
|
|
FLAGS2_ASSOCIATED == 0
|
|
Closing 1 FLAGS2_CLOSING
|
|
Closed 0 FLAGS2_CLOSING
|
|
|
|
Note that keeping the stopping flag set when the connection has fully
|
|
stopped avoids using the connection until it is connected again; the
|
|
CLOSING flag serves the same purpose. This allows a connection to run
|
|
down in its own time.
|
|
|
|
|
|
Author:
|
|
|
|
David Beaver (dbeaver) 1 July 1991
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef RASAUTODIAL
|
|
#include <acd.h>
|
|
#include <acdapi.h>
|
|
#endif // RASAUTODIAL
|
|
|
|
#ifdef RASAUTODIAL
|
|
extern BOOLEAN fAcdLoadedG;
|
|
extern ACD_DRIVER AcdDriverG;
|
|
|
|
//
|
|
// Imported routines
|
|
//
|
|
BOOLEAN
|
|
NbfAttemptAutoDial(
|
|
IN PTP_CONNECTION Connection,
|
|
IN ULONG ulFlags,
|
|
IN ACD_CONNECT_CALLBACK pProc,
|
|
IN PVOID pArg
|
|
);
|
|
|
|
VOID
|
|
NbfRetryTdiConnect(
|
|
IN BOOLEAN fSuccess,
|
|
IN PVOID *pArgs
|
|
);
|
|
|
|
BOOLEAN
|
|
NbfCancelTdiConnect(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp
|
|
);
|
|
#endif // RASAUTODIAL
|
|
|
|
|
|
|
|
VOID
|
|
ConnectionEstablishmentTimeout(
|
|
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 NAME_QUERY/NAME_RECOGNIZED protocol expires. The retry
|
|
count in the Connection object is decremented, and if it reaches 0,
|
|
the connection is aborted. If the retry count has not reached zero,
|
|
then the NAME QUERY is retried. The following cases are covered by
|
|
this routine:
|
|
|
|
1. Initial NAME_QUERY timeout for find_name portion of connection setup.
|
|
|
|
NQ(find_name) ------------------->
|
|
[TIMEOUT]
|
|
NQ(find_name) ------------------->
|
|
<------------------- NR(find_name)
|
|
|
|
2. Secondary NAME_QUERY timeout for connection setup.
|
|
|
|
NQ(connection) ------------------->
|
|
[TIMEOUT]
|
|
NQ(connection) ------------------->
|
|
<------------------- NR(connection)
|
|
|
|
There is another case where the data link connection does not get
|
|
established within a reasonable amount of time. This is handled by
|
|
the link layer routines.
|
|
|
|
Arguments:
|
|
|
|
Dpc - Pointer to a system DPC object.
|
|
|
|
DeferredContext - Pointer to the TP_CONNECTION block representing the
|
|
request that has timed out.
|
|
|
|
SystemArgument1 - Not used.
|
|
|
|
SystemArgument2 - Not used.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTP_CONNECTION Connection;
|
|
|
|
Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings
|
|
|
|
ENTER_NBF;
|
|
|
|
Connection = (PTP_CONNECTION)DeferredContext;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint1 ("ConnectionEstablishmentTimeout: Entered for connection %lx.\n",
|
|
Connection);
|
|
}
|
|
|
|
//
|
|
// If this connection is being run down, then we can't do anything.
|
|
//
|
|
|
|
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) {
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
NbfDereferenceConnection ("Connect timed out", Connection, CREF_TIMER);
|
|
LEAVE_NBF;
|
|
return;
|
|
}
|
|
|
|
|
|
if (Connection->Flags2 & (CONNECTION_FLAGS2_WAIT_NR_FN | CONNECTION_FLAGS2_WAIT_NR)) {
|
|
|
|
//
|
|
// We are waiting for a commital or non-commital NAME_RECOGNIZED frame.
|
|
// Decrement the retry count, and possibly resend the NAME_QUERY.
|
|
//
|
|
|
|
if (--(Connection->Retries) == 0) { // if retry count exhausted.
|
|
|
|
NTSTATUS StopStatus;
|
|
|
|
//
|
|
// See if we got a no listens response, or just
|
|
// nothing.
|
|
//
|
|
|
|
if ((Connection->Flags2 & CONNECTION_FLAGS2_NO_LISTEN) != 0) {
|
|
|
|
Connection->Flags2 &= ~CONNECTION_FLAGS2_NO_LISTEN;
|
|
StopStatus = STATUS_REMOTE_NOT_LISTENING; // no listens
|
|
|
|
} else {
|
|
|
|
StopStatus = STATUS_BAD_NETWORK_PATH; // name not found.
|
|
|
|
}
|
|
|
|
#ifdef RASAUTODIAL
|
|
//
|
|
// If this is a connect operation that has
|
|
// returned with STATUS_BAD_NETWORK_PATH, then
|
|
// attempt to create an automatic connection.
|
|
//
|
|
if (fAcdLoadedG &&
|
|
StopStatus == STATUS_BAD_NETWORK_PATH)
|
|
{
|
|
KIRQL adirql;
|
|
BOOLEAN fEnabled;
|
|
|
|
ACQUIRE_SPIN_LOCK(&AcdDriverG.SpinLock, &adirql);
|
|
fEnabled = AcdDriverG.fEnabled;
|
|
RELEASE_SPIN_LOCK(&AcdDriverG.SpinLock, adirql);
|
|
if (fEnabled && NbfAttemptAutoDial(
|
|
Connection,
|
|
0,
|
|
NbfRetryTdiConnect,
|
|
Connection))
|
|
{
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
goto done;
|
|
}
|
|
}
|
|
#endif // RASAUTODIAL
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
NbfStopConnection (Connection, StopStatus);
|
|
|
|
} else {
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
//
|
|
// We make source routing optional on every second
|
|
// name query (whenever Retries is even).
|
|
//
|
|
|
|
NbfSendNameQuery (
|
|
Connection,
|
|
(BOOLEAN)((Connection->Retries & 1) ? FALSE : TRUE));
|
|
|
|
NbfStartConnectionTimer (
|
|
Connection,
|
|
ConnectionEstablishmentTimeout,
|
|
Connection->Provider->NameQueryTimeout);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Dereference the connection, to account for the fact that the
|
|
// timer went off. Note that if we restarted the timer using
|
|
// NbfStartConnectionTimer, the reference count has already been
|
|
// incremented to account for the new timer.
|
|
//
|
|
|
|
done:
|
|
NbfDereferenceConnection ("Timer timed out",Connection, CREF_TIMER);
|
|
|
|
LEAVE_NBF;
|
|
return;
|
|
|
|
} /* ConnectionEstablishmentTimeout */
|
|
|
|
|
|
VOID
|
|
NbfAllocateConnection(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
OUT PTP_CONNECTION *TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates storage for a transport connection. Some
|
|
minimal initialization is done.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - the device context for this connection to be
|
|
associated with.
|
|
|
|
TransportConnection - Pointer to a place where this routine will
|
|
return a pointer to a transport connection structure. Returns
|
|
NULL if the storage cannot be allocated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PTP_CONNECTION Connection;
|
|
|
|
if ((DeviceContext->MemoryLimit != 0) &&
|
|
((DeviceContext->MemoryUsage + sizeof(TP_CONNECTION)) >
|
|
DeviceContext->MemoryLimit)) {
|
|
PANIC("NBF: Could not allocate connection: limit\n");
|
|
NbfWriteResourceErrorLog(
|
|
DeviceContext,
|
|
EVENT_TRANSPORT_RESOURCE_LIMIT,
|
|
103,
|
|
sizeof(TP_CONNECTION),
|
|
CONNECTION_RESOURCE_ID);
|
|
*TransportConnection = NULL;
|
|
return;
|
|
}
|
|
|
|
Connection = (PTP_CONNECTION)ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
sizeof (TP_CONNECTION),
|
|
NBF_MEM_TAG_TP_CONNECTION);
|
|
if (Connection == NULL) {
|
|
PANIC("NBF: Could not allocate connection: no pool\n");
|
|
NbfWriteResourceErrorLog(
|
|
DeviceContext,
|
|
EVENT_TRANSPORT_RESOURCE_POOL,
|
|
203,
|
|
sizeof(TP_CONNECTION),
|
|
CONNECTION_RESOURCE_ID);
|
|
*TransportConnection = NULL;
|
|
return;
|
|
}
|
|
RtlZeroMemory (Connection, sizeof(TP_CONNECTION));
|
|
|
|
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
|
NbfPrint1 ("ExAllocatePool Connection %08x\n", Connection);
|
|
}
|
|
|
|
DeviceContext->MemoryUsage += sizeof(TP_CONNECTION);
|
|
++DeviceContext->ConnectionAllocated;
|
|
|
|
Connection->Type = NBF_CONNECTION_SIGNATURE;
|
|
Connection->Size = sizeof (TP_CONNECTION);
|
|
|
|
Connection->Provider = DeviceContext;
|
|
Connection->ProviderInterlock = &DeviceContext->Interlock;
|
|
KeInitializeSpinLock (&Connection->SpinLock);
|
|
KeInitializeDpc (
|
|
&Connection->Dpc,
|
|
ConnectionEstablishmentTimeout,
|
|
(PVOID)Connection);
|
|
KeInitializeTimer (&Connection->Timer);
|
|
|
|
|
|
InitializeListHead (&Connection->LinkList);
|
|
InitializeListHead (&Connection->AddressFileList);
|
|
InitializeListHead (&Connection->AddressList);
|
|
InitializeListHead (&Connection->PacketWaitLinkage);
|
|
InitializeListHead (&Connection->PacketizeLinkage);
|
|
InitializeListHead (&Connection->SendQueue);
|
|
InitializeListHead (&Connection->ReceiveQueue);
|
|
InitializeListHead (&Connection->InProgressRequest);
|
|
InitializeListHead (&Connection->DeferredQueue);
|
|
|
|
NbfAddSendPacket (DeviceContext);
|
|
NbfAddSendPacket (DeviceContext);
|
|
NbfAddUIFrame (DeviceContext);
|
|
|
|
*TransportConnection = Connection;
|
|
|
|
} /* NbfAllocateConnection */
|
|
|
|
|
|
VOID
|
|
NbfDeallocateConnection(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees storage for a transport connection.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - the device context for this connection to be
|
|
associated with.
|
|
|
|
TransportConnection - Pointer to a transport connection structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
|
NbfPrint1 ("ExFreePool Connection: %08x\n", TransportConnection);
|
|
}
|
|
|
|
ExFreePool (TransportConnection);
|
|
--DeviceContext->ConnectionAllocated;
|
|
DeviceContext->MemoryUsage -= sizeof(TP_CONNECTION);
|
|
|
|
NbfRemoveSendPacket (DeviceContext);
|
|
NbfRemoveSendPacket (DeviceContext);
|
|
NbfRemoveUIFrame (DeviceContext);
|
|
|
|
} /* NbfDeallocateConnection */
|
|
|
|
|
|
NTSTATUS
|
|
NbfCreateConnection(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
OUT PTP_CONNECTION *TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a transport connection. The reference count in the
|
|
connection is automatically set to 1, and the reference count in the
|
|
DeviceContext is incremented.
|
|
|
|
Arguments:
|
|
|
|
Address - the address for this connection to be associated with.
|
|
|
|
TransportConnection - Pointer to a place where this routine will
|
|
return a pointer to a transport connection structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTP_CONNECTION Connection;
|
|
KIRQL oldirql;
|
|
PLIST_ENTRY p;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint0 ("NbfCreateConnection: Entered.\n");
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
|
|
|
p = RemoveHeadList (&DeviceContext->ConnectionPool);
|
|
if (p == &DeviceContext->ConnectionPool) {
|
|
|
|
if ((DeviceContext->ConnectionMaxAllocated == 0) ||
|
|
(DeviceContext->ConnectionAllocated < DeviceContext->ConnectionMaxAllocated)) {
|
|
|
|
NbfAllocateConnection (DeviceContext, &Connection);
|
|
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
|
NbfPrint1 ("NBF: Allocated connection at %lx\n", Connection);
|
|
}
|
|
|
|
} else {
|
|
|
|
NbfWriteResourceErrorLog(
|
|
DeviceContext,
|
|
EVENT_TRANSPORT_RESOURCE_SPECIFIC,
|
|
403,
|
|
sizeof(TP_CONNECTION),
|
|
CONNECTION_RESOURCE_ID);
|
|
Connection = NULL;
|
|
|
|
}
|
|
|
|
if (Connection == NULL) {
|
|
++DeviceContext->ConnectionExhausted;
|
|
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
|
PANIC ("NbfCreateConnection: Could not allocate connection object!\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
} else {
|
|
|
|
Connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
|
#if DBG
|
|
InitializeListHead (p);
|
|
#endif
|
|
|
|
}
|
|
|
|
++DeviceContext->ConnectionInUse;
|
|
if (DeviceContext->ConnectionInUse > DeviceContext->ConnectionMaxInUse) {
|
|
++DeviceContext->ConnectionMaxInUse;
|
|
}
|
|
|
|
DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse;
|
|
++DeviceContext->ConnectionSamples;
|
|
|
|
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
|
|
|
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
|
NbfPrint1 ("NbfCreateConnection: Connection at %lx.\n", Connection);
|
|
}
|
|
|
|
//
|
|
// We have two references; one is for creation, and the
|
|
// other is a temporary one so that the connection won't
|
|
// go away before the creator has a chance to access it.
|
|
//
|
|
|
|
Connection->SpecialRefCount = 1;
|
|
Connection->ReferenceCount = -1; // this is -1 based
|
|
|
|
#if DBG
|
|
{
|
|
UINT Counter;
|
|
for (Counter = 0; Counter < NUMBER_OF_CREFS; Counter++) {
|
|
Connection->RefTypes[Counter] = 0;
|
|
}
|
|
|
|
// This reference is removed by NbfCloseConnection
|
|
|
|
Connection->RefTypes[CREF_SPECIAL_CREATION] = 1;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Initialize the request queues & components of this connection.
|
|
//
|
|
|
|
InitializeListHead (&Connection->SendQueue);
|
|
InitializeListHead (&Connection->ReceiveQueue);
|
|
InitializeListHead (&Connection->InProgressRequest);
|
|
InitializeListHead (&Connection->AddressList);
|
|
InitializeListHead (&Connection->AddressFileList);
|
|
Connection->SpecialReceiveIrp = (PIRP)NULL;
|
|
Connection->Flags = 0;
|
|
Connection->Flags2 = 0;
|
|
Connection->DeferredFlags = 0;
|
|
Connection->Lsn = 0;
|
|
Connection->Rsn = 0;
|
|
Connection->Retries = 0; // no retries yet.
|
|
Connection->MessageBytesReceived = 0; // no data yet
|
|
Connection->MessageBytesAcked = 0;
|
|
Connection->MessageInitAccepted = 0;
|
|
Connection->ReceiveBytesUnaccepted = 0;
|
|
Connection->CurrentReceiveAckQueueable = FALSE;
|
|
Connection->CurrentReceiveSynchronous = FALSE;
|
|
Connection->ConsecutiveSends = 0;
|
|
Connection->ConsecutiveReceives = 0;
|
|
Connection->Link = NULL; // no datalink connection yet.
|
|
Connection->LinkSpinLock = NULL;
|
|
Connection->Context = NULL; // no context yet.
|
|
Connection->Status = STATUS_PENDING; // default NbfStopConnection status.
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
Connection->CurrentReceiveIrp = (PIRP)NULL;
|
|
Connection->DisconnectIrp = (PIRP)NULL;
|
|
Connection->CloseIrp = (PIRP)NULL;
|
|
Connection->AddressFile = NULL;
|
|
Connection->IndicationInProgress = FALSE;
|
|
Connection->OnDataAckQueue = FALSE;
|
|
Connection->OnPacketWaitQueue = FALSE;
|
|
Connection->TransferBytesPending = 0;
|
|
Connection->TotalTransferBytesPending = 0;
|
|
|
|
RtlZeroMemory (&Connection->NetbiosHeader, sizeof(NBF_HDR_CONNECTION));
|
|
|
|
#if PKT_LOG
|
|
RtlZeroMemory (&Connection->LastNRecvs, sizeof(PKT_LOG_QUE));
|
|
RtlZeroMemory (&Connection->LastNSends, sizeof(PKT_LOG_QUE));
|
|
RtlZeroMemory (&Connection->LastNIndcs, sizeof(PKT_IND_QUE));
|
|
#endif // PKT_LOG
|
|
|
|
#if DBG
|
|
Connection->Destroyed = FALSE;
|
|
Connection->TotalReferences = 0;
|
|
Connection->TotalDereferences = 0;
|
|
Connection->NextRefLoc = 0;
|
|
ExInterlockedInsertHeadList (&NbfGlobalConnectionList, &Connection->GlobalLinkage, &NbfGlobalInterlock);
|
|
StoreConnectionHistory (Connection, TRUE);
|
|
#endif
|
|
|
|
//
|
|
// Now assign this connection an ID. This is used later to identify the
|
|
// connection across multiple processes.
|
|
//
|
|
// The high bit of the ID is not user, it is off for connection
|
|
// initiating NAME.QUERY frames and on for ones that are the result
|
|
// of a FIND.NAME request.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
|
|
|
Connection->ConnectionId = DeviceContext->UniqueIdentifier;
|
|
++DeviceContext->UniqueIdentifier;
|
|
if (DeviceContext->UniqueIdentifier == 0x8000) {
|
|
DeviceContext->UniqueIdentifier = 1;
|
|
}
|
|
|
|
NbfReferenceDeviceContext ("Create Connection", DeviceContext, DCREF_CONNECTION);
|
|
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
|
|
|
*TransportConnection = Connection; // return the connection.
|
|
|
|
return STATUS_SUCCESS;
|
|
} /* NbfCreateConnection */
|
|
|
|
|
|
NTSTATUS
|
|
NbfVerifyConnectionObject (
|
|
IN PTP_CONNECTION Connection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to verify that the pointer given us in a file
|
|
object is in fact a valid connection object.
|
|
|
|
Arguments:
|
|
|
|
Connection - potential pointer to a TP_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// try to verify the connection signature. If the signature is valid,
|
|
// get the connection spinlock, check its state, and increment the
|
|
// reference count if it's ok to use it. Note that being in the stopping
|
|
// state is an OK place to be and reference the connection; we can
|
|
// disassociate the address while running down.
|
|
//
|
|
|
|
try {
|
|
|
|
if ((Connection != (PTP_CONNECTION)NULL) &&
|
|
(Connection->Size == sizeof (TP_CONNECTION)) &&
|
|
(Connection->Type == NBF_CONNECTION_SIGNATURE)) {
|
|
|
|
ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
if ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) {
|
|
|
|
NbfReferenceConnection ("Verify Temp Use", Connection, CREF_BY_ID);
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_CONNECTION;
|
|
}
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_CONNECTION;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NbfDestroyAssociation(
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys the association between a transport connection and
|
|
the address it was formerly associated with. The only action taken is
|
|
to disassociate the address and remove the connection from all address
|
|
queues.
|
|
|
|
This routine is only called by NbfDereferenceConnection. The reason for
|
|
this is that there may be multiple streams of execution which are
|
|
simultaneously referencing the same connection object, and it should
|
|
not be deleted out from under an interested stream of execution.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a transport connection structure to
|
|
be destroyed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, oldirql2;
|
|
PTP_ADDRESS address;
|
|
PTP_ADDRESS_FILE addressFile;
|
|
BOOLEAN NotAssociated = FALSE;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint1 ("NbfDestroyAssociation: Entered for connection %lx.\n",
|
|
TransportConnection);
|
|
}
|
|
|
|
try {
|
|
|
|
ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2);
|
|
if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) {
|
|
|
|
#if DBG
|
|
if (!(IsListEmpty(&TransportConnection->AddressList)) ||
|
|
!(IsListEmpty(&TransportConnection->AddressFileList))) {
|
|
DbgPrint ("NBF: C %lx, AF %lx, freed while still queued\n",
|
|
TransportConnection, TransportConnection->AddressFile);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
|
|
NotAssociated = TRUE;
|
|
} else {
|
|
TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED;
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
DbgPrint ("NBF: Got exception 1 in NbfDestroyAssociation\n");
|
|
DbgBreakPoint();
|
|
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
|
|
}
|
|
|
|
if (NotAssociated) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
addressFile = TransportConnection->AddressFile;
|
|
|
|
address = addressFile->Address;
|
|
|
|
//
|
|
// Delink this connection from its associated address connection
|
|
// database. To do this we must spin lock on the address object as
|
|
// well as on the connection,
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql);
|
|
|
|
try {
|
|
|
|
ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2);
|
|
RemoveEntryList (&TransportConnection->AddressFileList);
|
|
RemoveEntryList (&TransportConnection->AddressList);
|
|
|
|
InitializeListHead (&TransportConnection->AddressList);
|
|
InitializeListHead (&TransportConnection->AddressFileList);
|
|
|
|
//
|
|
// remove the association between the address and the connection.
|
|
//
|
|
|
|
TransportConnection->AddressFile = NULL;
|
|
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
DbgPrint ("NBF: Got exception 2 in NbfDestroyAssociation\n");
|
|
DbgBreakPoint();
|
|
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&address->SpinLock, oldirql);
|
|
|
|
//
|
|
// and remove a reference to the address
|
|
//
|
|
|
|
NbfDereferenceAddress ("Destroy association", address, AREF_CONNECTION);
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} /* NbfDestroyAssociation */
|
|
|
|
|
|
NTSTATUS
|
|
NbfIndicateDisconnect(
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine indicates a remote disconnection on this connection if it
|
|
is necessary to do so. No other action is taken here.
|
|
|
|
This routine is only called by NbfDereferenceConnection. The reason for
|
|
this is that there may be multiple streams of execution which are
|
|
simultaneously referencing the same connection object, and it should
|
|
not be deleted out from under an interested stream of execution.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a transport connection structure to
|
|
be destroyed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTP_ADDRESS_FILE addressFile;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
ULONG DisconnectReason;
|
|
PIRP DisconnectIrp;
|
|
KIRQL oldirql;
|
|
ULONG Lflags2;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint1 ("NbfIndicateDisconnect: Entered for connection %lx.\n",
|
|
TransportConnection);
|
|
}
|
|
|
|
try {
|
|
|
|
ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql);
|
|
|
|
if (((TransportConnection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) != 0)) {
|
|
|
|
ASSERT (TransportConnection->Lsn == 0);
|
|
|
|
//
|
|
// Turn off all but the still-relevant bits in the flags.
|
|
//
|
|
|
|
Lflags2 = TransportConnection->Flags2;
|
|
TransportConnection->Flags2 &=
|
|
(CONNECTION_FLAGS2_ASSOCIATED |
|
|
CONNECTION_FLAGS2_DISASSOCIATED |
|
|
CONNECTION_FLAGS2_CLOSING);
|
|
TransportConnection->Flags2 |= CONNECTION_FLAGS2_STOPPING;
|
|
|
|
//
|
|
// Clean up other stuff -- basically everything gets
|
|
// done here except for the flags and the status, since
|
|
// they are used to block other requests. When the connection
|
|
// is given back to us (in Accept, Connect, or Listen)
|
|
// they are cleared.
|
|
//
|
|
|
|
TransportConnection->NetbiosHeader.TransmitCorrelator = 0;
|
|
TransportConnection->Retries = 0; // no retries yet.
|
|
TransportConnection->MessageBytesReceived = 0; // no data yet
|
|
TransportConnection->MessageBytesAcked = 0;
|
|
TransportConnection->MessageInitAccepted = 0;
|
|
TransportConnection->ReceiveBytesUnaccepted = 0;
|
|
TransportConnection->ConsecutiveSends = 0;
|
|
TransportConnection->ConsecutiveReceives = 0;
|
|
TransportConnection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
|
|
TransportConnection->TransmittedTsdus = 0;
|
|
TransportConnection->ReceivedTsdus = 0;
|
|
|
|
TransportConnection->CurrentReceiveIrp = (PIRP)NULL;
|
|
|
|
DisconnectIrp = TransportConnection->DisconnectIrp;
|
|
TransportConnection->DisconnectIrp = (PIRP)NULL;
|
|
|
|
if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) != 0) {
|
|
addressFile = TransportConnection->AddressFile;
|
|
} else {
|
|
addressFile = NULL;
|
|
}
|
|
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);
|
|
|
|
|
|
DeviceContext = TransportConnection->Provider;
|
|
|
|
|
|
//
|
|
// If this connection was stopped by a call to TdiDisconnect,
|
|
// we have to complete that. We save the Irp so we can return
|
|
// the connection to the pool before we complete the request.
|
|
//
|
|
|
|
|
|
if (DisconnectIrp != (PIRP)NULL ||
|
|
(Lflags2 & CONNECTION_FLAGS2_LDISC) != 0) {
|
|
|
|
if (DisconnectIrp != (PIRP)NULL) {
|
|
IF_NBFDBG (NBF_DEBUG_SETUP) {
|
|
NbfPrint1("IndicateDisconnect %lx, complete IRP\n", TransportConnection);
|
|
}
|
|
|
|
//
|
|
// Now complete the IRP if needed. This will be non-null
|
|
// only if TdiDisconnect was called, and we have not
|
|
// yet completed it.
|
|
//
|
|
|
|
DisconnectIrp->IoStatus.Information = 0;
|
|
DisconnectIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest (DisconnectIrp, IO_NETWORK_INCREMENT);
|
|
}
|
|
|
|
} else if ((TransportConnection->Status != STATUS_LOCAL_DISCONNECT) &&
|
|
(addressFile != NULL) &&
|
|
(addressFile->RegisteredDisconnectHandler == TRUE)) {
|
|
|
|
//
|
|
// This was a remotely spawned disconnect, so indicate that
|
|
// to our client. Note that in the comparison above we
|
|
// check the status first, since if it is LOCAL_DISCONNECT
|
|
// addressFile may be NULL (This is sort of a hack
|
|
// for PDK2, we should really indicate the disconnect inside
|
|
// NbfStopConnection, where we know addressFile is valid).
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SETUP) {
|
|
NbfPrint1("IndicateDisconnect %lx, indicate\n", TransportConnection);
|
|
}
|
|
|
|
//
|
|
// if the disconnection was remotely spawned, then indicate
|
|
// disconnect. In the case that a disconnect was issued at
|
|
// the same time as the connection went down remotely, we
|
|
// won't do this because DisconnectIrp will be non-NULL.
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
|
NbfPrint1 ("NbfIndicateDisconnect calling DisconnectHandler, refcnt=%ld\n",
|
|
TransportConnection->ReferenceCount);
|
|
}
|
|
|
|
//
|
|
// Invoke the user's disconnection event handler, if any. We do this here
|
|
// so that any outstanding sends will complete before we tear down the
|
|
// connection.
|
|
//
|
|
|
|
DisconnectReason = 0;
|
|
if (TransportConnection->Flags2 & CONNECTION_FLAGS2_ABORT) {
|
|
DisconnectReason |= TDI_DISCONNECT_ABORT;
|
|
}
|
|
if (TransportConnection->Flags2 & CONNECTION_FLAGS2_DESTROY) {
|
|
DisconnectReason |= TDI_DISCONNECT_RELEASE;
|
|
}
|
|
|
|
(*addressFile->DisconnectHandler)(
|
|
addressFile->DisconnectHandlerContext,
|
|
TransportConnection->Context,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
TDI_DISCONNECT_ABORT);
|
|
|
|
#if MAGIC
|
|
if (NbfEnableMagic) {
|
|
extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK);
|
|
NbfSendMagicBullet (DeviceContext, NULL);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The client does not yet think that this connection
|
|
// is up...generally this happens due to request count
|
|
// fluctuation during connection setup.
|
|
//
|
|
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);
|
|
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
DbgPrint ("NBF: Got exception in NbfIndicateDisconnect\n");
|
|
DbgBreakPoint();
|
|
|
|
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);
|
|
}
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} /* NbfIndicateDisconnect */
|
|
|
|
|
|
NTSTATUS
|
|
NbfDestroyConnection(
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys a transport connection and removes all references
|
|
made by it to other objects in the transport. The connection structure
|
|
is returned to our lookaside list. It is assumed that the caller
|
|
has removed all IRPs from the connections's queues first.
|
|
|
|
This routine is only called by NbfDereferenceConnection. The reason for
|
|
this is that there may be multiple streams of execution which are
|
|
simultaneously referencing the same connection object, and it should
|
|
not be deleted out from under an interested stream of execution.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a transport connection structure to
|
|
be destroyed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
PIRP CloseIrp;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint1 ("NbfDestroyConnection: Entered for connection %lx.\n",
|
|
TransportConnection);
|
|
}
|
|
|
|
#if DBG
|
|
if (TransportConnection->Destroyed) {
|
|
NbfPrint1 ("attempt to destroy already-destroyed connection 0x%lx\n", TransportConnection);
|
|
DbgBreakPoint ();
|
|
}
|
|
if (!(TransportConnection->Flags2 & CONNECTION_FLAGS2_STOPPING)) {
|
|
NbfPrint1 ("attempt to destroy unstopped connection 0x%lx\n", TransportConnection);
|
|
DbgBreakPoint ();
|
|
}
|
|
TransportConnection->Destroyed = TRUE;
|
|
ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql);
|
|
RemoveEntryList (&TransportConnection->GlobalLinkage);
|
|
RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql);
|
|
#endif
|
|
|
|
DeviceContext = TransportConnection->Provider;
|
|
|
|
//
|
|
// Destroy any association that this connection has.
|
|
//
|
|
|
|
NbfDestroyAssociation (TransportConnection);
|
|
|
|
//
|
|
// Clear out any associated nasties hanging around the connection. Note
|
|
// that the current flags are set to STOPPING; this way anyone that may
|
|
// maliciously try to use the connection after it's dead and gone will
|
|
// just get ignored.
|
|
//
|
|
|
|
ASSERT (TransportConnection->Lsn == 0);
|
|
|
|
TransportConnection->Flags = 0;
|
|
TransportConnection->Flags2 = CONNECTION_FLAGS2_CLOSING;
|
|
TransportConnection->NetbiosHeader.TransmitCorrelator = 0;
|
|
TransportConnection->Retries = 0; // no retries yet.
|
|
TransportConnection->MessageBytesReceived = 0; // no data yet
|
|
TransportConnection->MessageBytesAcked = 0;
|
|
TransportConnection->MessageInitAccepted = 0;
|
|
TransportConnection->ReceiveBytesUnaccepted = 0;
|
|
|
|
|
|
//
|
|
// Now complete the close IRP. This will be set to non-null
|
|
// when CloseConnection was called.
|
|
//
|
|
|
|
CloseIrp = TransportConnection->CloseIrp;
|
|
|
|
if (CloseIrp != (PIRP)NULL) {
|
|
|
|
TransportConnection->CloseIrp = (PIRP)NULL;
|
|
CloseIrp->IoStatus.Information = 0;
|
|
CloseIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT);
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
NbfPrint1("Connection %x destroyed, no CloseIrp!!\n", TransportConnection);
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// Return the connection to the provider's pool.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
|
|
|
DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse;
|
|
++DeviceContext->ConnectionSamples;
|
|
--DeviceContext->ConnectionInUse;
|
|
|
|
if ((DeviceContext->ConnectionAllocated - DeviceContext->ConnectionInUse) >
|
|
DeviceContext->ConnectionInitAllocated) {
|
|
NbfDeallocateConnection (DeviceContext, TransportConnection);
|
|
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
|
NbfPrint1 ("NBF: Deallocated connection at %lx\n", TransportConnection);
|
|
}
|
|
} else {
|
|
InsertTailList (&DeviceContext->ConnectionPool, &TransportConnection->LinkList);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
|
|
|
NbfDereferenceDeviceContext ("Destroy Connection", DeviceContext, DCREF_CONNECTION);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} /* NbfDestroyConnection */
|
|
|
|
|
|
#if DBG
|
|
VOID
|
|
NbfRefConnection(
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine increments the reference count on a transport connection.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a transport connection object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG result;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint2 ("NbfReferenceConnection: entered for connection %lx, "
|
|
"current level=%ld.\n",
|
|
TransportConnection,
|
|
TransportConnection->ReferenceCount);
|
|
}
|
|
|
|
#if DBG
|
|
StoreConnectionHistory( TransportConnection, TRUE );
|
|
#endif
|
|
|
|
result = InterlockedIncrement (&TransportConnection->ReferenceCount);
|
|
|
|
if (result == 0) {
|
|
|
|
//
|
|
// The first increment causes us to increment the
|
|
// "ref count is not zero" special ref.
|
|
//
|
|
|
|
ExInterlockedAddUlong(
|
|
(PULONG)(&TransportConnection->SpecialRefCount),
|
|
1,
|
|
TransportConnection->ProviderInterlock);
|
|
|
|
#if DBG
|
|
++TransportConnection->RefTypes[CREF_SPECIAL_TEMP];
|
|
#endif
|
|
|
|
}
|
|
|
|
ASSERT (result >= 0);
|
|
|
|
} /* NbfRefConnection */
|
|
#endif
|
|
|
|
|
|
VOID
|
|
NbfDerefConnection(
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences a transport connection by decrementing the
|
|
reference count contained in the structure. If, after being
|
|
decremented, the reference count is zero, then this routine calls
|
|
NbfDestroyConnection to remove it from the system.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a transport connection object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG result;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint2 ("NbfDereferenceConnection: entered for connection %lx, "
|
|
"current level=%ld.\n",
|
|
TransportConnection,
|
|
TransportConnection->ReferenceCount);
|
|
}
|
|
|
|
#if DBG
|
|
StoreConnectionHistory( TransportConnection, FALSE );
|
|
#endif
|
|
|
|
result = InterlockedDecrement (&TransportConnection->ReferenceCount);
|
|
|
|
//
|
|
// If all the normal references to this connection are gone, then
|
|
// we can remove the special reference that stood for
|
|
// "the regular ref count is non-zero".
|
|
//
|
|
|
|
if (result < 0) {
|
|
|
|
//
|
|
// If the refcount is -1, then we need to disconnect from
|
|
// the link and indicate disconnect. However, we need to
|
|
// do this before we actually do the special deref, since
|
|
// otherwise the connection might go away while we
|
|
// are doing that.
|
|
//
|
|
// Note that both these routines are protected in that if they
|
|
// are called twice, the second call will have no effect.
|
|
//
|
|
|
|
|
|
//
|
|
// If both the connection and its link are active, then they have
|
|
// mutual references to each other. We remove the link's
|
|
// reference to the connection in NbfStopConnection, now
|
|
// the reference count has fallen enough that we know it
|
|
// is okay to remove the connection's reference to the
|
|
// link.
|
|
//
|
|
|
|
if (NbfDisconnectFromLink (TransportConnection, TRUE)) {
|
|
|
|
//
|
|
// if the reference count goes to one, we can safely indicate the
|
|
// user about disconnect states. That reference should
|
|
// be for the connection's creation.
|
|
//
|
|
|
|
NbfIndicateDisconnect (TransportConnection);
|
|
|
|
}
|
|
|
|
//
|
|
// Now it is OK to let the connection go away.
|
|
//
|
|
|
|
NbfDereferenceConnectionSpecial ("Regular ref gone", TransportConnection, CREF_SPECIAL_TEMP);
|
|
|
|
}
|
|
|
|
} /* NbfDerefConnection */
|
|
|
|
|
|
VOID
|
|
NbfDerefConnectionSpecial(
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines completes the dereferencing of a connection.
|
|
It may be called any time, but it does not do its work until
|
|
the regular reference count is also 0.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a transport connection object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint3 ("NbfDereferenceConnectionSpecial: entered for connection %lx, "
|
|
"current level=%ld (%ld).\n",
|
|
TransportConnection,
|
|
TransportConnection->ReferenceCount,
|
|
TransportConnection->SpecialRefCount);
|
|
}
|
|
|
|
#if DBG
|
|
StoreConnectionHistory( TransportConnection, FALSE );
|
|
#endif
|
|
|
|
|
|
ACQUIRE_SPIN_LOCK (TransportConnection->ProviderInterlock, &oldirql);
|
|
|
|
--TransportConnection->SpecialRefCount;
|
|
|
|
if ((TransportConnection->SpecialRefCount == 0) &&
|
|
(TransportConnection->ReferenceCount == -1)) {
|
|
|
|
//
|
|
// If we have deleted all references to this connection, 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 connection any longer.
|
|
//
|
|
|
|
#if DBG
|
|
{
|
|
BOOLEAN TimerCancelled;
|
|
TimerCancelled = KeCancelTimer (&TransportConnection->Timer);
|
|
// ASSERT (TimerCancelled);
|
|
}
|
|
#endif
|
|
|
|
RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql);
|
|
|
|
NbfDestroyConnection (TransportConnection);
|
|
|
|
} else {
|
|
|
|
RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql);
|
|
|
|
}
|
|
|
|
} /* NbfDerefConnectionSpecial */
|
|
|
|
|
|
VOID
|
|
NbfClearConnectionLsn(
|
|
IN PTP_CONNECTION TransportConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine clears the LSN field in a connection. To do this is
|
|
acquires the device context lock, and modifies the table value
|
|
for that LSN depending on the type of the connection.
|
|
|
|
NOTE: This routine is called with the connection spinlock held,
|
|
or in a state where nobody else will be accessing the
|
|
connection.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a transport connection object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
KIRQL oldirql;
|
|
|
|
DeviceContext = TransportConnection->Provider;
|
|
|
|
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
|
|
|
if (TransportConnection->Lsn != 0) {
|
|
|
|
if (TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) {
|
|
|
|
//
|
|
// It was to a group address; the count should be
|
|
// LSN_TABLE_MAX.
|
|
//
|
|
|
|
ASSERT(DeviceContext->LsnTable[TransportConnection->Lsn] == LSN_TABLE_MAX);
|
|
|
|
DeviceContext->LsnTable[TransportConnection->Lsn] = 0;
|
|
|
|
TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_GROUP_LSN;
|
|
|
|
} else {
|
|
|
|
ASSERT(DeviceContext->LsnTable[TransportConnection->Lsn] > 0);
|
|
|
|
--(DeviceContext->LsnTable[TransportConnection->Lsn]);
|
|
|
|
}
|
|
|
|
TransportConnection->Lsn = 0;
|
|
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
|
|
|
}
|
|
|
|
|
|
PTP_CONNECTION
|
|
NbfLookupConnectionById(
|
|
IN PTP_ADDRESS Address,
|
|
IN USHORT ConnectionId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine accepts a connection identifier and an address and
|
|
returns a pointer to the connection object, TP_CONNECTION. If the
|
|
connection identifier is not found on the address, then NULL is returned.
|
|
This routine automatically increments the reference count of the
|
|
TP_CONNECTION structure if it is found. It is assumed that the
|
|
TP_ADDRESS structure is already held with a reference count.
|
|
|
|
Arguments:
|
|
|
|
Address - Pointer to a transport address object.
|
|
|
|
ConnectionId - Identifier of the connection for this address.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the connection we found
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, oldirql1;
|
|
PLIST_ENTRY p;
|
|
PTP_CONNECTION Connection;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint2 ("NbfLookupConnectionById: entered, Address: %lx ID: %lx\n",
|
|
Address, ConnectionId);
|
|
}
|
|
|
|
//
|
|
// Currently, this implementation is inefficient, but brute force so
|
|
// that a system can get up and running. Later, a cache of the mappings
|
|
// of popular connection id's and pointers to their TP_CONNECTION structures
|
|
// will be searched first.
|
|
//
|
|
|
|
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_STOPPING) == 0) &&
|
|
(Connection->ConnectionId == ConnectionId)) {
|
|
|
|
// This reference is removed by the calling function
|
|
NbfReferenceConnection ("Lookup up for request", Connection, CREF_BY_ID);
|
|
Found = TRUE;
|
|
}
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
DbgPrint ("NBF: Got exception in NbfLookupConnectionById\n");
|
|
DbgBreakPoint();
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
}
|
|
|
|
if (Found) {
|
|
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
|
|
return Connection;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
|
|
|
|
return NULL;
|
|
|
|
} /* NbfLookupConnectionById */
|
|
|
|
|
|
PTP_CONNECTION
|
|
NbfLookupConnectionByContext(
|
|
IN PTP_ADDRESS Address,
|
|
IN CONNECTION_CONTEXT ConnectionContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine accepts a connection identifier and an address and
|
|
returns a pointer to the connection object, TP_CONNECTION. If the
|
|
connection identifier is not found on the address, then NULL is returned.
|
|
This routine automatically increments the reference count of the
|
|
TP_CONNECTION structure if it is found. It is assumed that the
|
|
TP_ADDRESS structure is already held with a reference count.
|
|
|
|
Should the ConnectionDatabase go in the address file?
|
|
|
|
Arguments:
|
|
|
|
Address - Pointer to a transport address object.
|
|
|
|
ConnectionContext - Connection Context for this address.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the connection we found
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, oldirql1;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN Found = FALSE;
|
|
PTP_CONNECTION Connection;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint2 ("NbfLookupConnectionByContext: entered, Address: %lx Context: %lx\n",
|
|
Address, ConnectionContext);
|
|
}
|
|
|
|
//
|
|
// Currently, this implementation is inefficient, but brute force so
|
|
// that a system can get up and running. Later, a cache of the mappings
|
|
// of popular connection id's and pointers to their TP_CONNECTION structures
|
|
// will be searched first.
|
|
//
|
|
|
|
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->Context == ConnectionContext) {
|
|
// This reference is removed by the calling function
|
|
NbfReferenceConnection ("Lookup up for request", Connection, CREF_LISTENING);
|
|
Found = TRUE;
|
|
}
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
DbgPrint ("NBF: Got exception in NbfLookupConnectionById\n");
|
|
DbgBreakPoint();
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
}
|
|
|
|
if (Found) {
|
|
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
|
|
return Connection;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
|
|
|
|
return NULL;
|
|
|
|
} /* NbfLookupConnectionByContext */
|
|
|
|
|
|
PTP_CONNECTION
|
|
NbfLookupListeningConnection(
|
|
IN PTP_ADDRESS Address,
|
|
IN PUCHAR RemoteName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans the connection database on an address to find
|
|
a TP_CONNECTION object which has LSN=0 and CONNECTION_FLAGS_WAIT_NQ
|
|
flag set. It returns a pointer to the found connection object (and
|
|
simultaneously resets the flag) or NULL if it could not be found.
|
|
The reference count is also incremented atomically on the connection.
|
|
|
|
The list is scanned for listens posted to this specific remote
|
|
name, or to those with no remote name specified.
|
|
|
|
Arguments:
|
|
|
|
Address - Pointer to a transport address object.
|
|
|
|
RemoteName - The name of the remote.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, oldirql1;
|
|
PTP_CONNECTION Connection;
|
|
PLIST_ENTRY p, q;
|
|
PTP_REQUEST ListenRequest;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint0 ("NbfLookupListeningConnection: Entered.\n");
|
|
}
|
|
|
|
//
|
|
// Currently, this implementation is inefficient, but brute force so
|
|
// that a system can get up and running. Later, a cache of the mappings
|
|
// of popular connection id's and pointers to their TP_CONNECTION structures
|
|
// will be searched first.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
|
|
|
|
for (p=Address->ConnectionDatabase.Flink;
|
|
p != &Address->ConnectionDatabase;
|
|
p=p->Flink) {
|
|
|
|
Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList);
|
|
|
|
ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql1);
|
|
|
|
if ((Connection->Lsn == 0) &&
|
|
(Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NQ)) {
|
|
|
|
q = Connection->InProgressRequest.Flink;
|
|
if (q != &Connection->InProgressRequest) {
|
|
ListenRequest = CONTAINING_RECORD (q, TP_REQUEST, Linkage);
|
|
if ((ListenRequest->Buffer2 != NULL) &&
|
|
(!RtlEqualMemory(
|
|
ListenRequest->Buffer2,
|
|
RemoteName,
|
|
NETBIOS_NAME_LENGTH))) {
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
continue;
|
|
}
|
|
} else {
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
continue;
|
|
}
|
|
// This reference is removed by the calling function
|
|
NbfReferenceConnection ("Found Listening", Connection, CREF_LISTENING);
|
|
Connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_NQ;
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
|
NbfPrint1 ("NbfLookupListeningConnection: Found Connection %lx\n",Connection);
|
|
}
|
|
return Connection;
|
|
}
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
|
|
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
|
NbfPrint0 ("NbfLookupListeningConnection: Found No Connection!\n");
|
|
}
|
|
|
|
return NULL;
|
|
|
|
} /* NbfLookupListeningConnection */
|
|
|
|
|
|
VOID
|
|
NbfStopConnection(
|
|
IN PTP_CONNECTION Connection,
|
|
IN NTSTATUS Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to terminate all activity on a connection and
|
|
destroy the object. This is done in a graceful manner; i.e., all
|
|
outstanding requests are terminated by cancelling them, etc. It is
|
|
assumed that the caller has a reference to this connection object,
|
|
but this routine will do the dereference for the one issued at creation
|
|
time.
|
|
|
|
Orderly release is a function of this routine, but it is not a provided
|
|
service of this transport provider, so there is no code to do it here.
|
|
|
|
NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Status - The status that caused us to stop the connection. This
|
|
will determine what status pending requests are aborted with,
|
|
and also how we proceed during the stop (whether to send a
|
|
session end, and whether to indicate disconnect).
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL cancelirql;
|
|
PLIST_ENTRY p;
|
|
PIRP Irp;
|
|
PTP_REQUEST Request;
|
|
BOOLEAN TimerWasCleared;
|
|
ULONG DisconnectReason;
|
|
PULONG StopCounter;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN | NBF_DEBUG_PNP) {
|
|
NbfPrint3 ("NbfStopConnection: Entered for connection %lx LSN %x RSN %x.\n",
|
|
Connection, Connection->Lsn, Connection->Rsn);
|
|
}
|
|
|
|
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
DeviceContext = Connection->Provider;
|
|
|
|
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
if (!(Connection->Flags2 & CONNECTION_FLAGS2_STOPPING)) {
|
|
|
|
//
|
|
// We are stopping the connection, record statistics
|
|
// about it.
|
|
//
|
|
|
|
if (Connection->Flags & CONNECTION_FLAGS_READY) {
|
|
DECREMENT_COUNTER (DeviceContext, OpenConnections);
|
|
}
|
|
|
|
Connection->Flags2 |= CONNECTION_FLAGS2_STOPPING;
|
|
Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID;
|
|
Connection->Status = Status;
|
|
|
|
if (Connection->Link != NULL) {
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
Connection->Flags &= ~(CONNECTION_FLAGS_READY|
|
|
CONNECTION_FLAGS_WAIT_SI|
|
|
CONNECTION_FLAGS_WAIT_SC); // no longer open for business
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
}
|
|
|
|
//
|
|
// If this flag was on, turn it off.
|
|
//
|
|
Connection->Flags &= ~CONNECTION_FLAGS_W_RESYNCH;
|
|
|
|
//
|
|
// Stop the timer if it was running.
|
|
//
|
|
|
|
TimerWasCleared = KeCancelTimer (&Connection->Timer);
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint2 ("NbfStopConnection: Timer for connection %lx "
|
|
"%s canceled.\n", Connection,
|
|
TimerWasCleared ? "was" : "was NOT" );
|
|
}
|
|
|
|
|
|
switch (Status) {
|
|
|
|
case STATUS_LOCAL_DISCONNECT:
|
|
StopCounter = &DeviceContext->Statistics.LocalDisconnects;
|
|
break;
|
|
case STATUS_REMOTE_DISCONNECT:
|
|
StopCounter = &DeviceContext->Statistics.RemoteDisconnects;
|
|
break;
|
|
case STATUS_LINK_FAILED:
|
|
StopCounter = &DeviceContext->Statistics.LinkFailures;
|
|
break;
|
|
case STATUS_IO_TIMEOUT:
|
|
StopCounter = &DeviceContext->Statistics.SessionTimeouts;
|
|
break;
|
|
case STATUS_CANCELLED:
|
|
StopCounter = &DeviceContext->Statistics.CancelledConnections;
|
|
break;
|
|
case STATUS_REMOTE_RESOURCES:
|
|
StopCounter = &DeviceContext->Statistics.RemoteResourceFailures;
|
|
break;
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
StopCounter = &DeviceContext->Statistics.LocalResourceFailures;
|
|
break;
|
|
case STATUS_BAD_NETWORK_PATH:
|
|
StopCounter = &DeviceContext->Statistics.NotFoundFailures;
|
|
break;
|
|
case STATUS_REMOTE_NOT_LISTENING:
|
|
StopCounter = &DeviceContext->Statistics.NoListenFailures;
|
|
break;
|
|
|
|
default:
|
|
StopCounter = NULL;
|
|
break;
|
|
|
|
}
|
|
|
|
if (StopCounter != NULL) {
|
|
(*StopCounter)++;
|
|
}
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
//
|
|
// Run down all TdiConnect/TdiDisconnect/TdiListen requests.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
while (TRUE) {
|
|
p = RemoveHeadList (&Connection->InProgressRequest);
|
|
if (p == &Connection->InProgressRequest) {
|
|
break;
|
|
}
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
|
IoSetCancelRoutine(Request->IoRequestPacket, NULL);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
#if DBG
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
|
LARGE_INTEGER MilliSeconds, Time;
|
|
ULONG junk;
|
|
KeQuerySystemTime (&Time);
|
|
MilliSeconds.LowPart = Time.LowPart;
|
|
MilliSeconds.HighPart = Time.HighPart;
|
|
MilliSeconds.QuadPart = MilliSeconds.QuadPart -
|
|
(Request->Time).QuadPart;
|
|
MilliSeconds = RtlExtendedLargeIntegerDivide (MilliSeconds, 10000L, &junk);
|
|
NbfPrint3 ("NbfStopConnection: Canceling pending CONNECT, Irp: %lx Time Pending: %ld%ld msec\n",
|
|
Request->IoRequestPacket, MilliSeconds.HighPart, MilliSeconds.LowPart);
|
|
}
|
|
#endif
|
|
|
|
NbfCompleteRequest (Request, Connection->Status, 0);
|
|
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
}
|
|
|
|
|
|
if (Connection->Link == NULL) {
|
|
|
|
//
|
|
// We are stopping early on.
|
|
//
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
IoReleaseCancelSpinLock (cancelirql);
|
|
|
|
if (TimerWasCleared) {
|
|
NbfDereferenceConnection ("Stopping timer", Connection, CREF_TIMER); // account for timer reference.
|
|
}
|
|
|
|
|
|
ASSERT (Connection->SendState == CONNECTION_SENDSTATE_IDLE);
|
|
ASSERT (!Connection->OnPacketWaitQueue);
|
|
ASSERT (!Connection->OnDataAckQueue);
|
|
ASSERT (!(Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK));
|
|
ASSERT (IsListEmpty(&Connection->SendQueue));
|
|
ASSERT (IsListEmpty(&Connection->ReceiveQueue));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
IoReleaseCancelSpinLock (cancelirql);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
//
|
|
// If this connection is waiting to packetize,
|
|
// remove it from the device context queue it is on.
|
|
//
|
|
// NOTE: If the connection is currently in the
|
|
// packetize queue, it will eventually go to get
|
|
// packetized and at that point it will get
|
|
// removed.
|
|
//
|
|
|
|
if (Connection->OnPacketWaitQueue) {
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint1("Stop waiting connection, flags %lx\n",
|
|
Connection->Flags);
|
|
}
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
Connection->OnPacketWaitQueue = FALSE;
|
|
ASSERT ((Connection->Flags & CONNECTION_FLAGS_SEND_SE) == 0);
|
|
Connection->Flags &= ~(CONNECTION_FLAGS_STARVED|CONNECTION_FLAGS_W_PACKETIZE);
|
|
RemoveEntryList (&Connection->PacketWaitLinkage);
|
|
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
}
|
|
|
|
|
|
//
|
|
// If we are on the data ack queue, then take ourselves off.
|
|
//
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
|
|
if (Connection->OnDataAckQueue) {
|
|
RemoveEntryList (&Connection->DataAckLinkage);
|
|
Connection->OnDataAckQueue = FALSE;
|
|
DeviceContext->DataAckQueueChanged = TRUE;
|
|
}
|
|
RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
|
|
|
|
//
|
|
// If this connection is waiting to send a piggyback ack,
|
|
// remove it from the device context queue for that, and
|
|
// send a data ack (which will get there before the
|
|
// SessionEnd).
|
|
//
|
|
|
|
if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) {
|
|
|
|
#if DBG
|
|
{
|
|
extern ULONG NbfDebugPiggybackAcks;
|
|
if (NbfDebugPiggybackAcks) {
|
|
NbfPrint1("Stop waiting connection, deferred flags %lx\n",
|
|
Connection->DeferredFlags);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Connection->DeferredFlags &=
|
|
~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q);
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
NbfSendDataAck (Connection);
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
if (TimerWasCleared) {
|
|
NbfDereferenceConnection ("Stopping timer", Connection, CREF_TIMER); // account for timer reference.
|
|
}
|
|
|
|
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
|
|
//
|
|
// Run down all TdiSend requests on this connection.
|
|
//
|
|
|
|
while (TRUE) {
|
|
p = RemoveHeadList (&Connection->SendQueue);
|
|
if (p == &Connection->SendQueue) {
|
|
break;
|
|
}
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
#if DBG
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
|
NbfPrint1("NbfStopConnection: Canceling pending SEND, Irp: %lx\n",
|
|
Irp);
|
|
}
|
|
#endif
|
|
NbfCompleteSendIrp (Irp, Connection->Status, 0);
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
++Connection->TransmissionErrors;
|
|
}
|
|
|
|
//
|
|
// NOTE: We hold the connection spinlock AND the
|
|
// cancel spinlock here.
|
|
//
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
|
|
|
//
|
|
// Run down all TdiReceive requests on this connection.
|
|
//
|
|
|
|
while (TRUE) {
|
|
p = RemoveHeadList (&Connection->ReceiveQueue);
|
|
if (p == &Connection->ReceiveQueue) {
|
|
break;
|
|
}
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
#if DBG
|
|
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
|
NbfPrint1 ("NbfStopConnection: Canceling pending RECEIVE, Irp: %lx\n",
|
|
Irp);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// It is OK to call this with the locks held.
|
|
//
|
|
NbfCompleteReceiveIrp (Irp, Connection->Status, 0);
|
|
|
|
++Connection->ReceiveErrors;
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: We hold the connection spinlock AND the
|
|
// cancel spinlock here.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
//
|
|
// If we aren't DESTROYing the link, then send a SESSION_END frame
|
|
// to the remote side. When the SESSION_END frame is acknowleged,
|
|
// we will decrement the connection's reference count by one, removing
|
|
// its creation reference. This will cause the connection object to
|
|
// be disposed of, and will begin running down the link.
|
|
// DGB: add logic to avoid blowing away link if one doesn't exist yet.
|
|
//
|
|
|
|
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
DisconnectReason = 0;
|
|
if (Connection->Flags2 & CONNECTION_FLAGS2_ABORT) {
|
|
DisconnectReason |= TDI_DISCONNECT_ABORT;
|
|
}
|
|
if (Connection->Flags2 & CONNECTION_FLAGS2_DESTROY) {
|
|
DisconnectReason |= TDI_DISCONNECT_RELEASE;
|
|
}
|
|
|
|
if (Connection->Link != NULL) {
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
if ((Status == STATUS_LOCAL_DISCONNECT) ||
|
|
(Status == STATUS_CANCELLED)) {
|
|
|
|
//
|
|
// (note that a connection should only get stopped
|
|
// with STATUS_INSUFFICIENT_RESOURCES if it is not
|
|
// yet connected to the remote).
|
|
//
|
|
|
|
//
|
|
// If this is done, when this packet is destroyed
|
|
// it will dereference the connection for CREF_LINK.
|
|
//
|
|
|
|
NbfSendSessionEnd (
|
|
Connection,
|
|
(BOOLEAN)((DisconnectReason & TDI_DISCONNECT_ABORT) != 0));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not attached to the link anymore; this dereference
|
|
// will allow our reference to fall below 3, which
|
|
// will cause NbfDisconnectFromLink to be called.
|
|
//
|
|
|
|
NbfDereferenceConnection("Stopped", Connection, CREF_LINK);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Note that we've blocked all new requests being queued during the
|
|
// time we have been in this teardown code; NbfDestroyConnection also
|
|
// sets the connection flags to STOPPING when returning the
|
|
// connection to the queue. This avoids lingerers using non-existent
|
|
// connections.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// The connection was already stopping; it may have a
|
|
// SESSION_END pending in which case we should kill
|
|
// it.
|
|
//
|
|
|
|
if ((Status != STATUS_LOCAL_DISCONNECT) &&
|
|
(Status != STATUS_CANCELLED)) {
|
|
|
|
if (Connection->Flags & CONNECTION_FLAGS_SEND_SE) {
|
|
|
|
ASSERT (Connection->Link != NULL);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
Connection->Flags &= ~CONNECTION_FLAGS_SEND_SE;
|
|
|
|
if (Connection->OnPacketWaitQueue) {
|
|
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
#if DBG
|
|
DbgPrint ("NBF: Removing connection %lx from PacketWait for SESSION_END\n", Connection);
|
|
#endif
|
|
Connection->OnPacketWaitQueue = FALSE;
|
|
RemoveEntryList (&Connection->PacketWaitLinkage);
|
|
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
NbfDereferenceConnection("Stopped again", Connection, CREF_LINK);
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
}
|
|
} /* NbfStopConnection */
|
|
|
|
|
|
VOID
|
|
NbfCancelConnection(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to cancel a connect
|
|
or a listen. It is simple since there can only be one of these
|
|
active on a connection; we just stop the connection, the IRP
|
|
will get completed as part of normal session teardown.
|
|
|
|
NOTE: This routine is called with the CancelSpinLock held and
|
|
is responsible for releasing it.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for this driver.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PTP_CONNECTION Connection;
|
|
PTP_REQUEST Request;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN fCanceled = TRUE;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP. This is where
|
|
// the function codes and parameters are stored.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
|
(IrpSp->MinorFunction == TDI_CONNECT || IrpSp->MinorFunction == TDI_LISTEN));
|
|
|
|
Connection = IrpSp->FileObject->FsContext;
|
|
|
|
//
|
|
// Since this IRP is still in the cancellable state, we know
|
|
// that the connection is still around (although it may be in
|
|
// the process of being torn down).
|
|
//
|
|
|
|
ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
NbfReferenceConnection ("Cancelling Send", Connection, CREF_TEMP);
|
|
|
|
p = RemoveHeadList (&Connection->InProgressRequest);
|
|
ASSERT (p != &Connection->InProgressRequest);
|
|
|
|
RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
|
ASSERT (Request->IoRequestPacket == Irp);
|
|
#ifdef RASAUTODIAL
|
|
//
|
|
// If there's an automatic connection in
|
|
// progress, cancel it.
|
|
//
|
|
if (Connection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTING)
|
|
fCanceled = NbfCancelTdiConnect(NULL, Irp);
|
|
#endif // RASAUTODIAL
|
|
|
|
if (fCanceled)
|
|
IoSetCancelRoutine(Request->IoRequestPacket, NULL);
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
if (fCanceled) {
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint2("NBF: Cancelled in-progress connect/listen %lx on %lx\n",
|
|
Request->IoRequestPacket, Connection);
|
|
}
|
|
|
|
KeRaiseIrql (DISPATCH_LEVEL, &oldirql);
|
|
NbfCompleteRequest (Request, STATUS_CANCELLED, 0);
|
|
NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT); // prevent indication to clients
|
|
KeLowerIrql (oldirql);
|
|
}
|
|
|
|
NbfDereferenceConnection ("Cancel done", Connection, CREF_TEMP);
|
|
|
|
}
|
|
|
|
#if 0
|
|
VOID
|
|
NbfWaitConnectionOnLink(
|
|
IN PTP_CONNECTION Connection,
|
|
IN ULONG Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to suspend a connection's activities because
|
|
the specified session-oriented frame could not be sent due to link
|
|
problems. Routines in FRAMESND.C call this.
|
|
|
|
NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Flags - Field containing bitflag set to indicate starved frame to be sent.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint0 ("NbfWaitConnectionOnLink: Entered.\n");
|
|
}
|
|
|
|
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
if (((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) ||
|
|
(Flags == CONNECTION_FLAGS_SEND_SE)) {
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
Connection->Flags |= Flags;
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
}
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
} /* NbfWaitConnectionOnLink */
|
|
#endif
|
|
|
|
|
|
VOID
|
|
NbfStartConnectionTimer(
|
|
IN PTP_CONNECTION Connection,
|
|
IN PKDEFERRED_ROUTINE TimeoutFunction,
|
|
IN ULONG WaitTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to start a timeout on NAME_QUERY/NAME_RECOGNIZED
|
|
activities on a connection.
|
|
|
|
Arguments:
|
|
|
|
TransportConnection - Pointer to a TP_CONNECTION object.
|
|
|
|
TimeoutFunction - The function to call when the timer fires.
|
|
|
|
WaitTime - a longword containing the low order time to wait.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER Timeout;
|
|
BOOLEAN AlreadyInserted;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_CONNOBJ) {
|
|
NbfPrint1 ("NbfStartConnectionTimer: Entered for connection %lx.\n",
|
|
Connection );
|
|
}
|
|
|
|
//
|
|
// Start the timer. Unlike the link timers, this is simply a kernel-
|
|
// managed object.
|
|
//
|
|
|
|
Timeout.LowPart = (ULONG)(-(LONG)WaitTime);
|
|
Timeout.HighPart = -1;
|
|
|
|
//
|
|
// Take the lock so we synchronize the cancelling with
|
|
// restarting the timer. This is so two threads won't
|
|
// both fail to cancel and then start the timer at the
|
|
// same time (it messes up the reference count).
|
|
//
|
|
|
|
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
AlreadyInserted = KeCancelTimer (&Connection->Timer);
|
|
|
|
KeInitializeDpc (
|
|
&Connection->Dpc,
|
|
TimeoutFunction,
|
|
(PVOID)Connection);
|
|
|
|
KeSetTimer (
|
|
&Connection->Timer,
|
|
Timeout,
|
|
&Connection->Dpc);
|
|
|
|
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
|
|
|
//
|
|
// If the timer wasn't already running, reference the connection to
|
|
// account for the new timer. If the timer was already started,
|
|
// then KeCancelTimer will have returned TRUE. In this
|
|
// case, the prior call to NbfStartConnectionTimer referenced the
|
|
// connection, so we don't do it again here.
|
|
//
|
|
|
|
if ( !AlreadyInserted ) {
|
|
|
|
// This reference is removed in ConnectionEstablishmentTimeout,
|
|
// or when the timer is cancelled.
|
|
|
|
NbfReferenceConnection ("starting timer", Connection, CREF_TIMER);
|
|
}
|
|
|
|
} /* NbfStartConnectionTimer */
|
|
|