3679 lines
95 KiB
C
3679 lines
95 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
connect.c
|
||
|
||
Abstract:
|
||
|
||
This routine contains the code to handle connect requests
|
||
for the Netbios module of the ISN transport.
|
||
|
||
Author:
|
||
|
||
Adam Barr (adamba) 22-November-1993
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
|
||
#ifdef RASAUTODIAL
|
||
#include <acd.h>
|
||
#include <acdapi.h>
|
||
|
||
BOOLEAN
|
||
NbiCancelTdiConnect(
|
||
IN PDEVICE pDevice,
|
||
IN PREQUEST pRequest,
|
||
IN PCONNECTION pConnection
|
||
);
|
||
#endif // RASAUTODIAL
|
||
|
||
|
||
extern POBJECT_TYPE *IoFileObjectType;
|
||
|
||
|
||
|
||
VOID
|
||
NbiFindRouteComplete(
|
||
IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest,
|
||
IN BOOLEAN FoundRoute
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a find route request
|
||
previously issued to IPX completes.
|
||
|
||
Arguments:
|
||
|
||
FindRouteRequest - The find route request that was issued.
|
||
|
||
FoundRoute - TRUE if the route was found.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION Connection;
|
||
PDEVICE Device = NbiDevice;
|
||
UINT i;
|
||
BOOLEAN LocalRoute;
|
||
USHORT TickCount;
|
||
PREQUEST RequestToComplete;
|
||
PUSHORT Counts;
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
CTELockHandle CancelLH;
|
||
|
||
Connection = CONTAINING_RECORD (FindRouteRequest, CONNECTION, FindRouteRequest);
|
||
|
||
NB_GET_CANCEL_LOCK(&CancelLH);
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
Connection->FindRouteInProgress = FALSE;
|
||
|
||
if (FoundRoute) {
|
||
|
||
//
|
||
// See if the route is local or not (for local routes
|
||
// we use the real MAC address in the local target, but
|
||
// the NIC ID may not be what we expect.
|
||
//
|
||
|
||
LocalRoute = TRUE;
|
||
|
||
for (i = 0; i < 6; i++) {
|
||
if (FindRouteRequest->LocalTarget.MacAddress[i] != 0x00) {
|
||
LocalRoute = FALSE;
|
||
}
|
||
}
|
||
|
||
if (LocalRoute) {
|
||
|
||
#if defined(_PNP_POWER)
|
||
Connection->LocalTarget.NicHandle = FindRouteRequest->LocalTarget.NicHandle;
|
||
#else
|
||
Connection->LocalTarget.NicId = FindRouteRequest->LocalTarget.NicId;
|
||
#endif _PNP_POWER
|
||
|
||
} else {
|
||
|
||
Connection->LocalTarget = FindRouteRequest->LocalTarget;
|
||
|
||
}
|
||
|
||
Counts = (PUSHORT)(&FindRouteRequest->Reserved2);
|
||
TickCount = Counts[0];
|
||
|
||
if (TickCount > 1) {
|
||
|
||
//
|
||
// Each tick is 55 ms, and for our timeout we use 10 ticks
|
||
// worth (this makes tick count of 1 be about 500 ms, the
|
||
// default).
|
||
//
|
||
// We get 55 milliseconds from
|
||
//
|
||
// 1 second * 1000 milliseconds 55 ms
|
||
// -------- ----------------- = -----
|
||
// 18.21 ticks 1 second tick
|
||
//
|
||
|
||
Connection->TickCount = TickCount;
|
||
Connection->BaseRetransmitTimeout = (TickCount * 550) / SHORT_TIMER_DELTA;
|
||
if (Connection->State != CONNECTION_STATE_ACTIVE) {
|
||
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
|
||
}
|
||
}
|
||
|
||
Connection->HopCount = Counts[1];
|
||
|
||
}
|
||
|
||
//
|
||
// If the call failed we just use whatever route we had before
|
||
// (on a connect it will be from the name query response, on
|
||
// a listen from whatever the incoming connect frame had).
|
||
//
|
||
|
||
if ((Connection->State == CONNECTION_STATE_CONNECTING) &&
|
||
(Connection->SubState == CONNECTION_SUBSTATE_C_W_ROUTE)) {
|
||
|
||
// we dont need to hold CancelSpinLock so release it,
|
||
// since we are releasing the locks out of order, we must
|
||
// swap the irql to get the priorities right.
|
||
|
||
NB_SWAP_IRQL( CancelLH, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
//
|
||
// Continue on with the session init frame.
|
||
//
|
||
|
||
(VOID)(*Device->Bind.QueryHandler)( // We should check return code
|
||
IPX_QUERY_LINE_INFO,
|
||
|
||
#if defined(_PNP_POWER)
|
||
&Connection->LocalTarget.NicHandle,
|
||
#else
|
||
Connection->LocalTarget.NicId,
|
||
#endif _PNP_POWER
|
||
&Connection->LineInfo,
|
||
sizeof(IPX_LINE_INFO),
|
||
NULL);
|
||
|
||
// Maximum packet size is the lower of RouterMtu and MaximumSendSize.
|
||
Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER) , Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION) ;
|
||
|
||
Connection->ReceiveWindowSize = 6;
|
||
Connection->SendWindowSize = 2;
|
||
Connection->MaxSendWindowSize = 6; // Base on what he sent ?
|
||
|
||
//
|
||
// Don't set RcvSequenceMax yet because we don't know
|
||
// if the connection is old or new netbios.
|
||
//
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK;
|
||
|
||
//
|
||
// We found a route, we need to start the connect
|
||
// process by sending out the session initialize
|
||
// frame. We start the timer to handle retries.
|
||
//
|
||
// CTEStartTimer doesn't deal with changing the
|
||
// expiration time of a running timer, so we have
|
||
// to stop it first. If we succeed in stopping the
|
||
// timer, then the CREF_TIMER reference from the
|
||
// previous starting of the timer remains, so we
|
||
// don't need to reference the connection again.
|
||
//
|
||
|
||
if (!CTEStopTimer (&Connection->Timer)) {
|
||
NbiReferenceConnectionLock (Connection, CREF_TIMER);
|
||
}
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
CTEStartTimer(
|
||
&Connection->Timer,
|
||
Device->ConnectionTimeout,
|
||
NbiConnectionTimeout,
|
||
(PVOID)Connection);
|
||
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
|
||
NbiSendSessionInitialize (Connection);
|
||
|
||
} else if ((Connection->State == CONNECTION_STATE_LISTENING) &&
|
||
(Connection->SubState == CONNECTION_SUBSTATE_L_W_ROUTE)) {
|
||
|
||
if (Connection->ListenRequest != NULL) {
|
||
|
||
NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_ACTIVE);
|
||
RequestToComplete = Connection->ListenRequest;
|
||
Connection->ListenRequest = NULL;
|
||
IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL);
|
||
|
||
} else if (Connection->AcceptRequest != NULL) {
|
||
|
||
NbiTransferReferenceConnection (Connection, CREF_ACCEPT, CREF_ACTIVE);
|
||
RequestToComplete = Connection->AcceptRequest;
|
||
Connection->AcceptRequest = NULL;
|
||
|
||
} else {
|
||
|
||
CTEAssert (FALSE);
|
||
RequestToComplete = NULL;
|
||
|
||
}
|
||
|
||
// we dont need to hold CancelSpinLock so release it,
|
||
// since we are releasing the locks out of order, we must
|
||
// swap the irql to get the priorities right.
|
||
|
||
NB_SWAP_IRQL( CancelLH, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
(VOID)(*Device->Bind.QueryHandler)( // We should check return code
|
||
IPX_QUERY_LINE_INFO,
|
||
#if defined(_PNP_POWER)
|
||
&Connection->LocalTarget.NicHandle,
|
||
#else
|
||
Connection->LocalTarget.NicId,
|
||
#endif _PNP_POWER
|
||
&Connection->LineInfo,
|
||
sizeof(IPX_LINE_INFO),
|
||
NULL);
|
||
|
||
|
||
// Take the lowest of MaximumPacketSize ( set from the sessionInit
|
||
// frame ), MaximumSendSize and RouterMtu.
|
||
|
||
if (Connection->MaximumPacketSize > Connection->LineInfo.MaximumSendSize - sizeof(NB_CONNECTION)) {
|
||
|
||
Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER), Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION);
|
||
|
||
} else {
|
||
|
||
// Connection->MaximumPacketSize is what was set by the sender so already
|
||
// accounts for the header.
|
||
Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER), Connection->MaximumPacketSize ) ;
|
||
|
||
}
|
||
|
||
Connection->ReceiveWindowSize = 6;
|
||
Connection->SendWindowSize = 2;
|
||
Connection->MaxSendWindowSize = 6; // Base on what he sent ?
|
||
|
||
if (Connection->NewNetbios) {
|
||
CTEAssert (Connection->LocalRcvSequenceMax == 4); // should have been set
|
||
Connection->LocalRcvSequenceMax = Connection->ReceiveWindowSize;
|
||
}
|
||
|
||
Connection->State = CONNECTION_STATE_ACTIVE;
|
||
Connection->SubState = CONNECTION_SUBSTATE_A_IDLE;
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_IDLE;
|
||
|
||
++Device->Statistics.OpenConnections;
|
||
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
//
|
||
// StartWatchdog acquires TimerLock, so we have to
|
||
// free Lock first.
|
||
//
|
||
|
||
|
||
NbiStartWatchdog (Connection);
|
||
|
||
//
|
||
// This releases the connection lock, so that SessionInitAckData
|
||
// can't be freed before it is copied.
|
||
//
|
||
|
||
NbiSendSessionInitAck(
|
||
Connection,
|
||
Connection->SessionInitAckData,
|
||
Connection->SessionInitAckDataLength,
|
||
&LockHandle1);
|
||
|
||
if (RequestToComplete != NULL) {
|
||
|
||
REQUEST_STATUS(RequestToComplete) = STATUS_SUCCESS;
|
||
|
||
NbiCompleteRequest (RequestToComplete);
|
||
NbiFreeRequest (Device, RequestToComplete);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_FIND_ROUTE);
|
||
|
||
} /* NbiFindRouteComplete */
|
||
|
||
|
||
NTSTATUS
|
||
NbiOpenConnection(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to open a connection. Note that the connection that
|
||
is open is of little use until associated with an address; until then,
|
||
the only thing that can be done with it is close it.
|
||
|
||
Arguments:
|
||
|
||
Device - Pointer to the device for this driver.
|
||
|
||
Request - Pointer to the request representing the open.
|
||
|
||
Return Value:
|
||
|
||
The function value is the status of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION Connection;
|
||
PFILE_FULL_EA_INFORMATION ea;
|
||
#ifdef ISN_NT
|
||
PIRP Irp = (PIRP)Request;
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
#endif
|
||
|
||
//
|
||
// Verify Minimum Buffer length!
|
||
// Bug#: 203814
|
||
//
|
||
ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||
if (ea->EaValueLength < sizeof(PVOID))
|
||
{
|
||
NbiPrint2("NbiOpenConnection: ERROR -- (EaValueLength=%d < Min=%d)\n",
|
||
ea->EaValueLength, sizeof(PVOID));
|
||
CTEAssert(FALSE);
|
||
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
||
}
|
||
|
||
//
|
||
// First, try to make a connection object to represent this pending
|
||
// connection. Then fill in the relevant fields.
|
||
// In addition to the creation, if successful NbfCreateConnection
|
||
// will create a second reference which is removed once the request
|
||
// references the connection, or if the function exits before that.
|
||
|
||
if (!(Connection = NbiCreateConnection (Device))) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// set the connection context so we can connect the user to this data
|
||
// structure
|
||
//
|
||
RtlCopyMemory ( &Connection->Context, &ea->EaName[ea->EaNameLength+1], sizeof (PVOID));
|
||
|
||
//
|
||
// let file object point at connection and connection at file object
|
||
//
|
||
|
||
REQUEST_OPEN_CONTEXT(Request) = (PVOID)Connection;
|
||
REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONNECTION_FILE;
|
||
#ifdef ISN_NT
|
||
Connection->FileObject = IrpSp->FileObject;
|
||
#endif
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} /* NbiOpenConnection */
|
||
|
||
|
||
VOID
|
||
NbiStopConnection(
|
||
IN PCONNECTION Connection,
|
||
IN NTSTATUS DisconnectStatus
|
||
IN NB_LOCK_HANDLE_PARAM(LockHandle)
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to stop an active connection.
|
||
|
||
THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD
|
||
AND RETURNS WITH IT RELEASED.
|
||
|
||
Arguments:
|
||
|
||
Connection - The connection to be stopped.
|
||
|
||
DisconnectStatus - The reason for the disconnect. One of:
|
||
STATUS_LINK_FAILED: We timed out trying to probe the remote.
|
||
STATUS_REMOTE_DISCONNECT: The remote sent a session end.
|
||
STATUS_LOCAL_DISCONNECT: The local side disconnected.
|
||
STATUS_CANCELLED: A send or receive on this connection was cancelled.
|
||
STATUS_INVALID_CONNECTION: The local side closed the connection.
|
||
STATUS_INVALID_ADDRESS: The local side closed the address.
|
||
|
||
LockHandle - The handle which the connection lock was acquired with.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PREQUEST ListenRequest, AcceptRequest, SendRequest, ReceiveRequest,
|
||
DisconnectWaitRequest, ConnectRequest;
|
||
PREQUEST Request, TmpRequest;
|
||
BOOLEAN DerefForPacketize;
|
||
BOOLEAN DerefForWaitPacket;
|
||
BOOLEAN DerefForActive;
|
||
BOOLEAN DerefForWaitCache;
|
||
BOOLEAN SendSessionEnd;
|
||
BOOLEAN ActiveReceive;
|
||
BOOLEAN IndicateToClient;
|
||
BOOLEAN ConnectionWasActive;
|
||
PDEVICE Device = NbiDevice;
|
||
PADDRESS_FILE AddressFile;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle2)
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle3)
|
||
CTELockHandle CancelLH;
|
||
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Stop connection %lx (%lx)\n", Connection, DisconnectStatus));
|
||
|
||
//
|
||
// These flags control our actions after we set the state to
|
||
// DISCONNECT.
|
||
//
|
||
|
||
DerefForPacketize = FALSE;
|
||
DerefForWaitPacket = FALSE;
|
||
DerefForActive = FALSE;
|
||
DerefForWaitCache = FALSE;
|
||
SendSessionEnd = FALSE;
|
||
ActiveReceive = FALSE;
|
||
IndicateToClient = FALSE;
|
||
ConnectionWasActive = FALSE;
|
||
|
||
//
|
||
// These contain requests or queues of request to complete.
|
||
//
|
||
|
||
ListenRequest = NULL;
|
||
AcceptRequest = NULL;
|
||
SendRequest = NULL;
|
||
ReceiveRequest = NULL;
|
||
DisconnectWaitRequest = NULL;
|
||
ConnectRequest = NULL;
|
||
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
--Device->Statistics.OpenConnections;
|
||
|
||
ConnectionWasActive = TRUE;
|
||
|
||
Connection->Status = DisconnectStatus;
|
||
|
||
if ((DisconnectStatus == STATUS_LINK_FAILED) ||
|
||
(DisconnectStatus == STATUS_LOCAL_DISCONNECT)) {
|
||
|
||
//
|
||
// Send out session end frames, but fewer if
|
||
// we timed out.
|
||
//
|
||
// What about STATUS_CANCELLED?
|
||
//
|
||
|
||
Connection->Retries = (DisconnectStatus == STATUS_LOCAL_DISCONNECT) ?
|
||
Device->ConnectionCount :
|
||
(Device->ConnectionCount / 2);
|
||
|
||
SendSessionEnd = TRUE;
|
||
Connection->SubState = CONNECTION_SUBSTATE_D_W_ACK;
|
||
|
||
//
|
||
// CTEStartTimer doesn't deal with changing the
|
||
// expiration time of a running timer, so we have
|
||
// to stop it first. If we succeed in stopping the
|
||
// timer, then the CREF_TIMER reference from the
|
||
// previous starting of the timer remains, so we
|
||
// don't need to reference the connection again.
|
||
//
|
||
|
||
if (!CTEStopTimer (&Connection->Timer)) {
|
||
NbiReferenceConnectionLock (Connection, CREF_TIMER);
|
||
}
|
||
|
||
CTEStartTimer(
|
||
&Connection->Timer,
|
||
Device->ConnectionTimeout,
|
||
NbiConnectionTimeout,
|
||
(PVOID)Connection);
|
||
|
||
}
|
||
|
||
if (Connection->ReceiveState == CONNECTION_RECEIVE_TRANSFER) {
|
||
ActiveReceive = TRUE;
|
||
}
|
||
|
||
Connection->State = CONNECTION_STATE_DISCONNECT;
|
||
DerefForActive = TRUE;
|
||
|
||
if (Connection->DisconnectWaitRequest != NULL) {
|
||
DisconnectWaitRequest = Connection->DisconnectWaitRequest;
|
||
Connection->DisconnectWaitRequest = NULL;
|
||
}
|
||
|
||
if ((DisconnectStatus == STATUS_LINK_FAILED) ||
|
||
(DisconnectStatus == STATUS_REMOTE_DISCONNECT) ||
|
||
(DisconnectStatus == STATUS_CANCELLED)) {
|
||
|
||
IndicateToClient = TRUE;
|
||
|
||
}
|
||
|
||
//
|
||
// If we are inside NbiAssignSequenceAndSend, add
|
||
// a reference so the connection won't go away during it.
|
||
//
|
||
|
||
if (Connection->NdisSendsInProgress > 0) {
|
||
*(Connection->NdisSendReference) = TRUE;
|
||
NB_DEBUG2 (SEND, ("Adding CREF_NDIS_SEND to %lx\n", Connection));
|
||
NbiReferenceConnectionLock (Connection, CREF_NDIS_SEND);
|
||
}
|
||
|
||
//
|
||
// Clean up some other stuff.
|
||
//
|
||
|
||
Connection->ReceiveUnaccepted = 0;
|
||
Connection->CurrentIndicateOffset = 0;
|
||
|
||
//
|
||
// Update our counters. Some of these we never use.
|
||
//
|
||
|
||
switch (DisconnectStatus) {
|
||
|
||
case STATUS_LOCAL_DISCONNECT:
|
||
++Device->Statistics.LocalDisconnects;
|
||
break;
|
||
case STATUS_REMOTE_DISCONNECT:
|
||
++Device->Statistics.RemoteDisconnects;
|
||
break;
|
||
case STATUS_LINK_FAILED:
|
||
++Device->Statistics.LinkFailures;
|
||
break;
|
||
case STATUS_IO_TIMEOUT:
|
||
++Device->Statistics.SessionTimeouts;
|
||
break;
|
||
case STATUS_CANCELLED:
|
||
++Device->Statistics.CancelledConnections;
|
||
break;
|
||
case STATUS_REMOTE_RESOURCES:
|
||
++Device->Statistics.RemoteResourceFailures;
|
||
break;
|
||
case STATUS_INVALID_CONNECTION:
|
||
case STATUS_INVALID_ADDRESS:
|
||
case STATUS_INSUFFICIENT_RESOURCES:
|
||
++Device->Statistics.LocalResourceFailures;
|
||
break;
|
||
case STATUS_BAD_NETWORK_PATH:
|
||
case STATUS_REMOTE_NOT_LISTENING:
|
||
++Device->Statistics.NotFoundFailures;
|
||
break;
|
||
default:
|
||
CTEAssert(FALSE);
|
||
break;
|
||
}
|
||
|
||
} else if (Connection->State == CONNECTION_STATE_CONNECTING) {
|
||
|
||
//
|
||
// There is a connect in progress. We have to find ourselves
|
||
// in the pending connect queue if we are there.
|
||
//
|
||
|
||
if (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) {
|
||
RemoveEntryList (REQUEST_LINKAGE(Connection->ConnectRequest));
|
||
DerefForWaitCache = TRUE;
|
||
}
|
||
|
||
if (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) {
|
||
|
||
ConnectRequest = Connection->ConnectRequest;
|
||
Connection->ConnectRequest = NULL;
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// If we allocated this memory, free it.
|
||
//
|
||
|
||
if (Connection->SessionInitAckDataLength > 0) {
|
||
|
||
NbiFreeMemory(
|
||
Connection->SessionInitAckData,
|
||
Connection->SessionInitAckDataLength,
|
||
MEMORY_CONNECTION,
|
||
"SessionInitAckData");
|
||
Connection->SessionInitAckData = NULL;
|
||
Connection->SessionInitAckDataLength = 0;
|
||
|
||
}
|
||
|
||
|
||
if (Connection->ListenRequest != NULL) {
|
||
|
||
ListenRequest = Connection->ListenRequest;
|
||
Connection->ListenRequest = NULL;
|
||
RemoveEntryList (REQUEST_LINKAGE(ListenRequest)); // take out of Device->ListenQueue
|
||
|
||
}
|
||
|
||
if (Connection->AcceptRequest != NULL) {
|
||
|
||
AcceptRequest = Connection->AcceptRequest;
|
||
Connection->AcceptRequest = NULL;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Do we need to stop the connection timer?
|
||
// I don't think so.
|
||
//
|
||
|
||
|
||
|
||
//
|
||
// A lot of this we only have to tear down if we were
|
||
// active before this, because once we are stopping nothing
|
||
// new will get started. Some of the other stuff
|
||
// can be put inside this if also.
|
||
//
|
||
|
||
if (ConnectionWasActive) {
|
||
|
||
//
|
||
// Stop any receives. If there is one that is actively
|
||
// transferring we leave it and just run down the rest
|
||
// of the queue. If not, we queue the rest of the
|
||
// queue on the back of the current one and run
|
||
// down them all.
|
||
//
|
||
|
||
if (ActiveReceive) {
|
||
|
||
ReceiveRequest = Connection->ReceiveQueue.Head;
|
||
|
||
//
|
||
// Connection->ReceiveRequest will get set to NULL
|
||
// when the transfer completes.
|
||
//
|
||
|
||
} else {
|
||
|
||
ReceiveRequest = Connection->ReceiveRequest;
|
||
if (ReceiveRequest) {
|
||
REQUEST_SINGLE_LINKAGE (ReceiveRequest) = Connection->ReceiveQueue.Head;
|
||
} else {
|
||
ReceiveRequest = Connection->ReceiveQueue.Head;
|
||
}
|
||
Connection->ReceiveRequest = NULL;
|
||
|
||
}
|
||
|
||
Connection->ReceiveQueue.Head = NULL;
|
||
|
||
|
||
if ((Request = Connection->FirstMessageRequest) != NULL) {
|
||
|
||
//
|
||
// If the current request has some sends outstanding, then
|
||
// we dequeue it from the queue to let it complete when
|
||
// the sends complete. In that case we set SendRequest
|
||
// to be the rest of the queue, which will be aborted.
|
||
// If the current request has no sends, then we put
|
||
// queue everything to SendRequest to be aborted below.
|
||
//
|
||
|
||
#if DBG
|
||
if (REQUEST_REFCOUNT(Request) > 100) {
|
||
DbgPrint ("Request %lx (%lx) has high refcount\n",
|
||
Connection, Request);
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
if (--REQUEST_REFCOUNT(Request) == 0) {
|
||
|
||
//
|
||
// NOTE: If this is a multi-request message, then
|
||
// the linkage of Request will already point to the
|
||
// send queue head, but we don't bother checking.
|
||
//
|
||
|
||
SendRequest = Request;
|
||
REQUEST_SINGLE_LINKAGE (Request) = Connection->SendQueue.Head;
|
||
|
||
} else {
|
||
|
||
if (Connection->FirstMessageRequest == Connection->LastMessageRequest) {
|
||
|
||
REQUEST_SINGLE_LINKAGE (Request) = NULL;
|
||
|
||
} else {
|
||
|
||
Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest);
|
||
REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL;
|
||
|
||
}
|
||
|
||
SendRequest = Connection->SendQueue.Head;
|
||
|
||
}
|
||
|
||
Connection->FirstMessageRequest = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This may happen if we were sending a probe when a
|
||
// send was submitted, and the probe timed out.
|
||
//
|
||
|
||
SendRequest = Connection->SendQueue.Head;
|
||
|
||
}
|
||
|
||
Connection->SendQueue.Head = NULL;
|
||
|
||
}
|
||
|
||
|
||
if (Connection->OnWaitPacketQueue) {
|
||
Connection->OnWaitPacketQueue = FALSE;
|
||
RemoveEntryList (&Connection->WaitPacketLinkage);
|
||
DerefForWaitPacket = TRUE;
|
||
}
|
||
|
||
if (Connection->OnPacketizeQueue) {
|
||
Connection->OnPacketizeQueue = FALSE;
|
||
RemoveEntryList (&Connection->PacketizeLinkage);
|
||
DerefForPacketize = TRUE;
|
||
}
|
||
|
||
//
|
||
// Should we check if DataAckPending is TRUE and send an ack??
|
||
//
|
||
|
||
Connection->DataAckPending = FALSE;
|
||
Connection->PiggybackAckTimeout = FALSE;
|
||
Connection->ReceivesWithoutAck = 0;
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
//
|
||
// We can't acquire TimerLock with Lock held, since
|
||
// we sometimes call ReferenceConnection (which does an
|
||
// interlocked add using Lock) with TimerLock held.
|
||
//
|
||
|
||
NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle3);
|
||
|
||
if (Connection->OnShortList) {
|
||
Connection->OnShortList = FALSE;
|
||
RemoveEntryList (&Connection->ShortList);
|
||
}
|
||
|
||
if (Connection->OnLongList) {
|
||
Connection->OnLongList = FALSE;
|
||
RemoveEntryList (&Connection->LongList);
|
||
}
|
||
|
||
if (Connection->OnDataAckQueue) {
|
||
Connection->OnDataAckQueue = FALSE;
|
||
RemoveEntryList (&Connection->DataAckLinkage);
|
||
Device->DataAckQueueChanged = TRUE;
|
||
}
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle3);
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
|
||
if (IndicateToClient) {
|
||
|
||
AddressFile = Connection->AddressFile;
|
||
|
||
if (AddressFile->RegisteredHandler[TDI_EVENT_DISCONNECT]) {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Session end indicated on connection %lx\n", Connection));
|
||
|
||
(*AddressFile->DisconnectHandler)(
|
||
AddressFile->HandlerContexts[TDI_EVENT_DISCONNECT],
|
||
Connection->Context,
|
||
0, // DisconnectData
|
||
NULL,
|
||
0, // DisconnectInformation
|
||
NULL,
|
||
TDI_DISCONNECT_RELEASE); // DisconnectReason.
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if (DisconnectWaitRequest != NULL) {
|
||
|
||
//
|
||
// Make the TDI tester happy by returning CONNECTION_RESET
|
||
// here.
|
||
//
|
||
|
||
if (DisconnectStatus == STATUS_REMOTE_DISCONNECT) {
|
||
REQUEST_STATUS(DisconnectWaitRequest) = STATUS_CONNECTION_RESET;
|
||
} else {
|
||
REQUEST_STATUS(DisconnectWaitRequest) = DisconnectStatus;
|
||
}
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
IoSetCancelRoutine (DisconnectWaitRequest, (PDRIVER_CANCEL)NULL);
|
||
NB_FREE_CANCEL_LOCK ( CancelLH );
|
||
|
||
NbiCompleteRequest (DisconnectWaitRequest);
|
||
NbiFreeRequest (Device, DisconnectWaitRequest);
|
||
|
||
}
|
||
|
||
if (ConnectRequest != NULL) {
|
||
|
||
REQUEST_STATUS (ConnectRequest) = STATUS_LOCAL_DISCONNECT;
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
IoSetCancelRoutine (ConnectRequest, (PDRIVER_CANCEL)NULL);
|
||
NB_FREE_CANCEL_LOCK ( CancelLH );
|
||
|
||
NbiCompleteRequest(ConnectRequest);
|
||
NbiFreeRequest (Device, ConnectRequest);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_CONNECT);
|
||
|
||
}
|
||
|
||
if (ListenRequest != NULL) {
|
||
|
||
REQUEST_INFORMATION(ListenRequest) = 0;
|
||
REQUEST_STATUS(ListenRequest) = STATUS_LOCAL_DISCONNECT;
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL);
|
||
NB_FREE_CANCEL_LOCK ( CancelLH );
|
||
|
||
NbiCompleteRequest (ListenRequest);
|
||
NbiFreeRequest(Device, ListenRequest);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_LISTEN);
|
||
|
||
}
|
||
|
||
if (AcceptRequest != NULL) {
|
||
|
||
REQUEST_INFORMATION(AcceptRequest) = 0;
|
||
REQUEST_STATUS(AcceptRequest) = STATUS_LOCAL_DISCONNECT;
|
||
|
||
NbiCompleteRequest (AcceptRequest);
|
||
NbiFreeRequest(Device, AcceptRequest);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_ACCEPT);
|
||
|
||
}
|
||
|
||
while (ReceiveRequest != NULL) {
|
||
|
||
TmpRequest = REQUEST_SINGLE_LINKAGE (ReceiveRequest);
|
||
|
||
REQUEST_STATUS (ReceiveRequest) = DisconnectStatus;
|
||
REQUEST_INFORMATION (ReceiveRequest) = 0;
|
||
|
||
NB_DEBUG2 (RECEIVE, ("StopConnection aborting receive %lx\n", ReceiveRequest));
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
IoSetCancelRoutine (ReceiveRequest, (PDRIVER_CANCEL)NULL);
|
||
NB_FREE_CANCEL_LOCK ( CancelLH );
|
||
|
||
NbiCompleteRequest (ReceiveRequest);
|
||
NbiFreeRequest (Device, ReceiveRequest);
|
||
|
||
++Connection->ConnectionInfo.ReceiveErrors;
|
||
|
||
ReceiveRequest = TmpRequest;
|
||
|
||
NbiDereferenceConnection (Connection, CREF_RECEIVE);
|
||
|
||
}
|
||
|
||
while (SendRequest != NULL) {
|
||
|
||
TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest);
|
||
|
||
REQUEST_STATUS (SendRequest) = DisconnectStatus;
|
||
REQUEST_INFORMATION (SendRequest) = 0;
|
||
|
||
NB_DEBUG2 (SEND, ("StopConnection aborting send %lx\n", SendRequest));
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL);
|
||
NB_FREE_CANCEL_LOCK ( CancelLH );
|
||
|
||
NbiCompleteRequest (SendRequest);
|
||
NbiFreeRequest (Device, SendRequest);
|
||
|
||
++Connection->ConnectionInfo.TransmissionErrors;
|
||
|
||
SendRequest = TmpRequest;
|
||
|
||
NbiDereferenceConnection (Connection, CREF_SEND);
|
||
|
||
}
|
||
|
||
if (SendSessionEnd) {
|
||
NbiSendSessionEnd (Connection);
|
||
}
|
||
|
||
if (DerefForWaitCache) {
|
||
NbiDereferenceConnection (Connection, CREF_WAIT_CACHE);
|
||
}
|
||
|
||
if (DerefForPacketize) {
|
||
NbiDereferenceConnection (Connection, CREF_PACKETIZE);
|
||
}
|
||
|
||
if (DerefForWaitPacket) {
|
||
NbiDereferenceConnection (Connection, CREF_W_PACKET);
|
||
}
|
||
|
||
if (DerefForActive) {
|
||
NbiDereferenceConnection (Connection, CREF_ACTIVE);
|
||
}
|
||
|
||
} /* NbiStopConnection */
|
||
|
||
|
||
NTSTATUS
|
||
NbiCloseConnection(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to close a connection.
|
||
|
||
Arguments:
|
||
|
||
Device - Pointer to the device for this driver.
|
||
|
||
Request - Pointer to the request representing the open.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PCONNECTION Connection;
|
||
PADDRESS_FILE AddressFile;
|
||
PADDRESS Address;
|
||
CTELockHandle LockHandle;
|
||
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Close connection %lx\n", Connection));
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
if (Connection->ReferenceCount == 0) {
|
||
|
||
//
|
||
// If we are associated with an address, we need
|
||
// to simulate a disassociate at this point.
|
||
//
|
||
|
||
if ((Connection->AddressFile != NULL) &&
|
||
(Connection->AddressFile != (PVOID)-1)) {
|
||
|
||
AddressFile = Connection->AddressFile;
|
||
Connection->AddressFile = (PVOID)-1;
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
//
|
||
// Take this connection out of the address file's list.
|
||
//
|
||
|
||
Address = AddressFile->Address;
|
||
NB_GET_LOCK (&Address->Lock, &LockHandle);
|
||
|
||
if (Connection->AddressFileLinked) {
|
||
Connection->AddressFileLinked = FALSE;
|
||
RemoveEntryList (&Connection->AddressFileLinkage);
|
||
}
|
||
|
||
//
|
||
// We are done.
|
||
//
|
||
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle);
|
||
|
||
Connection->AddressFile = NULL;
|
||
|
||
//
|
||
// Clean up the reference counts and complete any
|
||
// disassociate requests that pended.
|
||
//
|
||
|
||
NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION);
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
}
|
||
|
||
//
|
||
// Even if the ref count is zero and some thread has already done cleanup,
|
||
// we can not destroy the connection bcoz some other thread might still be
|
||
// in HandleConnectionZero routine. This could happen when 2 threads call into
|
||
// HandleConnectionZero, one thread runs thru completion, close comes along
|
||
// and the other thread is still in HandleConnectionZero routine.
|
||
//
|
||
|
||
if ( Connection->CanBeDestroyed && ( Connection->ThreadsInHandleConnectionZero == 0 ) ) {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
NbiDestroyConnection(Connection);
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
Connection->ClosePending = Request;
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
Status = STATUS_PENDING;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
Connection->ClosePending = Request;
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
Status = STATUS_PENDING;
|
||
|
||
}
|
||
|
||
return Status;
|
||
|
||
} /* NbiCloseConnection */
|
||
|
||
|
||
NTSTATUS
|
||
NbiTdiAssociateAddress(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the association of the connection and
|
||
the address for the user.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Request - The request describing the associate.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PCONNECTION Connection;
|
||
#ifdef ISN_NT
|
||
PFILE_OBJECT FileObject;
|
||
#endif
|
||
PADDRESS_FILE AddressFile;
|
||
PADDRESS Address;
|
||
PTDI_REQUEST_KERNEL_ASSOCIATE Parameters;
|
||
CTELockHandle LockHandle;
|
||
|
||
//
|
||
// Check that the file type is valid (Bug# 203827)
|
||
//
|
||
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
|
||
{
|
||
CTEAssert(FALSE);
|
||
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
||
}
|
||
|
||
//
|
||
// This references the connection.
|
||
//
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
Status = NbiVerifyConnection (Connection);
|
||
if (!NT_SUCCESS (Status))
|
||
{
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// The request request parameters hold
|
||
// get a pointer to the address FileObject, which points us to the
|
||
// transport's address object, which is where we want to put the
|
||
// connection.
|
||
//
|
||
Parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)REQUEST_PARAMETERS(Request);
|
||
|
||
Status = ObReferenceObjectByHandle (
|
||
Parameters->AddressHandle,
|
||
FILE_READ_DATA,
|
||
*IoFileObjectType,
|
||
Request->RequestorMode,
|
||
(PVOID *)&FileObject,
|
||
NULL);
|
||
|
||
if ((!NT_SUCCESS(Status)) ||
|
||
(FileObject->DeviceObject != &(NbiDevice->DeviceObject)) || // Bug# 171836
|
||
(PtrToUlong(FileObject->FsContext2) != TDI_TRANSPORT_ADDRESS_FILE))
|
||
{
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
return STATUS_INVALID_HANDLE;
|
||
}
|
||
|
||
AddressFile = (PADDRESS_FILE)(FileObject->FsContext);
|
||
|
||
//
|
||
// Make sure the address file is valid, and reference it.
|
||
//
|
||
|
||
#if defined(_PNP_POWER)
|
||
Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK);
|
||
#else
|
||
Status = NbiVerifyAddressFile (AddressFile);
|
||
#endif _PNP_POWER
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
#ifdef ISN_NT
|
||
ObDereferenceObject (FileObject);
|
||
#endif
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
return Status;
|
||
}
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Associate connection %lx with address file %lx\n",
|
||
Connection, AddressFile));
|
||
|
||
|
||
//
|
||
// Now insert the connection into the database of the address.
|
||
//
|
||
|
||
Address = AddressFile->Address;
|
||
|
||
NB_GET_LOCK (&Address->Lock, &LockHandle);
|
||
|
||
if (Connection->AddressFile != NULL) {
|
||
|
||
//
|
||
// The connection is already associated with
|
||
// an address file.
|
||
//
|
||
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle);
|
||
NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY);
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
} else {
|
||
|
||
if (AddressFile->State == ADDRESSFILE_STATE_OPEN) {
|
||
|
||
Connection->AddressFile = AddressFile;
|
||
Connection->AddressFileLinked = TRUE;
|
||
InsertHeadList (&AddressFile->ConnectionDatabase, &Connection->AddressFileLinkage);
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle);
|
||
|
||
NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_CONNECTION);
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle);
|
||
NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY);
|
||
Status = STATUS_INVALID_ADDRESS;
|
||
}
|
||
|
||
}
|
||
|
||
#ifdef ISN_NT
|
||
|
||
//
|
||
// We don't need the reference to the file object, we just
|
||
// used it to get from the handle to the object.
|
||
//
|
||
|
||
ObDereferenceObject (FileObject);
|
||
|
||
#endif
|
||
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
|
||
return Status;
|
||
|
||
} /* NbiTdiAssociateAddress */
|
||
|
||
|
||
NTSTATUS
|
||
NbiTdiDisassociateAddress(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the disassociation of the connection
|
||
and the address for the user.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Request - The request describing the associate.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION Connection;
|
||
NTSTATUS Status;
|
||
PADDRESS_FILE AddressFile;
|
||
PADDRESS Address;
|
||
CTELockHandle LockHandle;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle1)
|
||
NB_DEFINE_SYNC_CONTEXT (SyncContext)
|
||
|
||
//
|
||
// Check that the file type is valid
|
||
//
|
||
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
|
||
{
|
||
CTEAssert(FALSE);
|
||
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
||
}
|
||
|
||
//
|
||
// Check that the connection is valid. This references
|
||
// the connection.
|
||
//
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
Status = NbiVerifyConnection (Connection);
|
||
if (!NT_SUCCESS (Status)) {
|
||
return Status;
|
||
}
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Disassociate connection %lx\n", Connection));
|
||
|
||
|
||
//
|
||
// First check if the connection is still active.
|
||
//
|
||
|
||
NB_BEGIN_SYNC (&SyncContext);
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
|
||
if (Connection->State != CONNECTION_STATE_INACTIVE) {
|
||
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiStopConnection(
|
||
Connection,
|
||
STATUS_INVALID_ADDRESS
|
||
NB_LOCK_HANDLE_ARG (LockHandle1));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
|
||
}
|
||
|
||
//
|
||
// Keep the sync through the function??
|
||
//
|
||
|
||
NB_END_SYNC (&SyncContext);
|
||
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
//
|
||
// Make sure the connection is associated and is not in the
|
||
// middle of disassociating.
|
||
//
|
||
|
||
if ((Connection->AddressFile != NULL) &&
|
||
(Connection->AddressFile != (PVOID)-1) &&
|
||
(Connection->DisassociatePending == NULL)) {
|
||
|
||
if (Connection->ReferenceCount == 0) {
|
||
|
||
//
|
||
// Because the connection still has a reference to
|
||
// the address file, we know it is still valid. We
|
||
// set the connection address file to the temporary
|
||
// value of -1, which prevents somebody else from
|
||
// disassociating it and also prevents a new association.
|
||
//
|
||
|
||
AddressFile = Connection->AddressFile;
|
||
Connection->AddressFile = (PVOID)-1;
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
Address = AddressFile->Address;
|
||
NB_GET_LOCK (&Address->Lock, &LockHandle);
|
||
|
||
if (Connection->AddressFileLinked) {
|
||
Connection->AddressFileLinked = FALSE;
|
||
RemoveEntryList (&Connection->AddressFileLinkage);
|
||
}
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle);
|
||
|
||
Connection->AddressFile = NULL;
|
||
|
||
NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION);
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set this so when the count goes to 0 it will
|
||
// be disassociated and the request completed.
|
||
//
|
||
|
||
Connection->DisassociatePending = Request;
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
Status = STATUS_PENDING;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
|
||
return Status;
|
||
|
||
} /* NbiTdiDisassociateAddress */
|
||
|
||
|
||
NTSTATUS
|
||
NbiTdiListen(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine posts a listen on a connection.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Request - The request describing the listen.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PCONNECTION Connection;
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
CTELockHandle CancelLH;
|
||
|
||
//
|
||
// Check that the file type is valid
|
||
//
|
||
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
|
||
{
|
||
CTEAssert(FALSE);
|
||
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
||
}
|
||
|
||
//
|
||
// Check that the connection is valid. This references
|
||
// the connection.
|
||
//
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
Status = NbiVerifyConnection (Connection);
|
||
if (!NT_SUCCESS (Status)) {
|
||
return Status;
|
||
}
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
//
|
||
// The connection must be inactive, but associated and
|
||
// with no disassociate or close pending.
|
||
//
|
||
|
||
if ((Connection->State == CONNECTION_STATE_INACTIVE) &&
|
||
(Connection->AddressFile != NULL) &&
|
||
(Connection->AddressFile != (PVOID)-1) &&
|
||
(Connection->DisassociatePending == NULL) &&
|
||
(Connection->ClosePending == NULL)) {
|
||
|
||
Connection->State = CONNECTION_STATE_LISTENING;
|
||
Connection->SubState = CONNECTION_SUBSTATE_L_WAITING;
|
||
|
||
(VOID)NbiAssignConnectionId (Device, Connection); // Check return code.
|
||
|
||
|
||
if (!Request->Cancel) {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Queued listen %lx on %lx\n", Request, Connection));
|
||
InsertTailList (&Device->ListenQueue, REQUEST_LINKAGE(Request));
|
||
IoSetCancelRoutine (Request, NbiCancelListen);
|
||
Connection->ListenRequest = Request;
|
||
NbiReferenceConnectionLock (Connection, CREF_LISTEN);
|
||
Status = STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Cancelled listen %lx on %lx\n", Request, Connection));
|
||
Connection->State = CONNECTION_STATE_INACTIVE;
|
||
Status = STATUS_CANCELLED;
|
||
}
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
|
||
return Status;
|
||
|
||
} /* NbiTdiListen */
|
||
|
||
|
||
NTSTATUS
|
||
NbiTdiAccept(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine accepts a connection to a remote machine. The
|
||
connection must previously have completed a listen with
|
||
the TDI_QUERY_ACCEPT flag on.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Request - The request describing the accept.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PCONNECTION Connection;
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
|
||
//
|
||
// Check that the file type is valid
|
||
//
|
||
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
|
||
{
|
||
CTEAssert(FALSE);
|
||
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
||
}
|
||
|
||
//
|
||
// Check that the connection is valid. This references
|
||
// the connection.
|
||
//
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
Status = NbiVerifyConnection (Connection);
|
||
if (!NT_SUCCESS (Status)) {
|
||
return Status;
|
||
}
|
||
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
if ((Connection->State == CONNECTION_STATE_LISTENING) &&
|
||
(Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) {
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE;
|
||
|
||
NbiTransferReferenceConnection (Connection, CREF_W_ACCEPT, CREF_ACCEPT);
|
||
Connection->AcceptRequest = Request;
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE);
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
Connection->Retries = NbiDevice->KeepAliveCount;
|
||
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
|
||
*(UNALIGNED ULONG *)Connection->FindRouteRequest.Network =
|
||
*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork;
|
||
RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6);
|
||
Connection->FindRouteRequest.Identifier = IDENTIFIER_NB;
|
||
Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP;
|
||
|
||
//
|
||
// When this completes, we will send the session init
|
||
// ack. We don't call it if the client is for network 0,
|
||
// instead just fake as if no route could be found
|
||
// and we will use the local target we got here.
|
||
// The accept is completed when this completes.
|
||
//
|
||
|
||
if (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0) {
|
||
|
||
(*Device->Bind.FindRouteHandler)(
|
||
&Connection->FindRouteRequest);
|
||
|
||
} else {
|
||
|
||
NbiFindRouteComplete(
|
||
&Connection->FindRouteRequest,
|
||
FALSE);
|
||
|
||
}
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Accept received on %lx\n", Connection));
|
||
|
||
Status = STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Accept received on invalid connection %lx\n", Connection));
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
|
||
return Status;
|
||
|
||
} /* NbiTdiAccept */
|
||
|
||
|
||
NTSTATUS
|
||
NbiTdiConnect(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine connects to a remote machine.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Request - The request describing the connect.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PCONNECTION Connection;
|
||
TDI_ADDRESS_NETBIOS * RemoteName;
|
||
PTDI_REQUEST_KERNEL_CONNECT Parameters;
|
||
#if 0
|
||
PLARGE_INTEGER RequestedTimeout;
|
||
LARGE_INTEGER RealTimeout;
|
||
#endif
|
||
PNETBIOS_CACHE CacheName;
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
CTELockHandle CancelLH;
|
||
BOOLEAN bLockFreed = FALSE;
|
||
|
||
//
|
||
// Check that the file type is valid
|
||
//
|
||
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
|
||
{
|
||
CTEAssert(FALSE);
|
||
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
||
}
|
||
|
||
//
|
||
// Check that the connection is valid. This references
|
||
// the connection.
|
||
//
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
Status = NbiVerifyConnection (Connection);
|
||
if (!NT_SUCCESS (Status)) {
|
||
return Status;
|
||
}
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
//
|
||
// The connection must be inactive, but associated and
|
||
// with no disassociate or close pending.
|
||
//
|
||
|
||
if ((Connection->State == CONNECTION_STATE_INACTIVE) &&
|
||
(Connection->AddressFile != NULL) &&
|
||
(Connection->AddressFile != (PVOID)-1) &&
|
||
(Connection->DisassociatePending == NULL) &&
|
||
(Connection->ClosePending == NULL)) {
|
||
|
||
try
|
||
{
|
||
Parameters = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(Request);
|
||
RemoteName = NbiParseTdiAddress(
|
||
(PTRANSPORT_ADDRESS)(Parameters->RequestConnectionInformation->RemoteAddress),
|
||
Parameters->RequestConnectionInformation->RemoteAddressLength,
|
||
FALSE);
|
||
}
|
||
except(EXCEPTION_EXECUTE_HANDLER)
|
||
{
|
||
NbiPrint1("NbiTdiConnect: Exception <0x%x> accessing connect info\n", GetExceptionCode());
|
||
RemoteName = NULL;
|
||
}
|
||
|
||
if (RemoteName == NULL) {
|
||
|
||
//
|
||
// There is no netbios remote address specified.
|
||
//
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
Status = STATUS_BAD_NETWORK_PATH;
|
||
|
||
} else {
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_CONNECT);
|
||
Connection->State = CONNECTION_STATE_CONNECTING;
|
||
RtlCopyMemory (Connection->RemoteName, RemoteName->NetbiosName, 16);
|
||
|
||
Connection->Retries = Device->ConnectionCount;
|
||
|
||
(VOID)NbiAssignConnectionId (Device, Connection); // Check return code.
|
||
|
||
Status = NbiTdiConnectFindName(
|
||
Device,
|
||
Request,
|
||
Connection,
|
||
CancelLH,
|
||
LockHandle1,
|
||
LockHandle2,
|
||
&bLockFreed);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Connect on invalid connection %lx\n", Connection));
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
|
||
if (!bLockFreed) {
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
|
||
return Status;
|
||
|
||
} /* NbiTdiConnect */
|
||
|
||
|
||
NTSTATUS
|
||
NbiTdiConnectFindName(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request,
|
||
IN PCONNECTION Connection,
|
||
IN CTELockHandle CancelLH,
|
||
IN CTELockHandle ConnectionLH,
|
||
IN CTELockHandle DeviceLH,
|
||
IN PBOOLEAN pbLockFreed
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
PNETBIOS_CACHE CacheName;
|
||
|
||
//
|
||
// See what is up with this Netbios name.
|
||
//
|
||
|
||
Status = CacheFindName(
|
||
Device,
|
||
FindNameConnect,
|
||
Connection->RemoteName,
|
||
&CacheName);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
|
||
//
|
||
// A request for routes to this name has been
|
||
// sent out on the net, we queue up this connect
|
||
// request and processing will be resumed when
|
||
// we get a response.
|
||
//
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_FIND_NAME;
|
||
|
||
|
||
if (!Request->Cancel) {
|
||
|
||
InsertTailList( &Device->WaitingConnects, REQUEST_LINKAGE(Request));
|
||
IoSetCancelRoutine (Request, NbiCancelConnectFindName);
|
||
Connection->ConnectRequest = Request;
|
||
NbiReferenceConnectionLock (Connection, CREF_WAIT_CACHE);
|
||
NB_DEBUG2 (CONNECTION, ("Queueing up connect %lx on %lx\n",
|
||
Request, Connection));
|
||
|
||
NB_FREE_LOCK (&Device->Lock, DeviceLH);
|
||
|
||
} else {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection));
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
|
||
|
||
NB_FREE_LOCK (&Device->Lock, DeviceLH);
|
||
NbiDereferenceConnection (Connection, CREF_CONNECT);
|
||
|
||
Status = STATUS_CANCELLED;
|
||
}
|
||
|
||
} else if (Status == STATUS_SUCCESS) {
|
||
|
||
//
|
||
// We don't need to worry about referencing CacheName
|
||
// because we stop using it before we release the lock.
|
||
//
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE;
|
||
|
||
|
||
if (!Request->Cancel) {
|
||
|
||
IoSetCancelRoutine (Request, NbiCancelConnectWaitResponse);
|
||
|
||
// we dont need to hold CancelSpinLock so release it,
|
||
// since we are releasing the locks out of order, we must
|
||
// swap the irql to get the priorities right.
|
||
|
||
NB_SWAP_IRQL( CancelLH, ConnectionLH);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
Connection->LocalTarget = CacheName->Networks[0].LocalTarget;
|
||
RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12);
|
||
|
||
Connection->ConnectRequest = Request;
|
||
NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE);
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Found connect cached %lx on %lx\n",
|
||
Request, Connection));
|
||
|
||
NB_FREE_LOCK (&Device->Lock, DeviceLH);
|
||
NB_FREE_LOCK (&Connection->Lock, ConnectionLH);
|
||
|
||
*(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress;
|
||
RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6);
|
||
Connection->FindRouteRequest.Identifier = IDENTIFIER_NB;
|
||
Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED;
|
||
|
||
//
|
||
// When this completes, we will send the session init.
|
||
// We don't call it if the client is for network 0,
|
||
// instead just fake as if no route could be found
|
||
// and we will use the local target we got here.
|
||
//
|
||
|
||
if (CacheName->FirstResponse.NetworkAddress != 0) {
|
||
|
||
(*Device->Bind.FindRouteHandler)(
|
||
&Connection->FindRouteRequest);
|
||
|
||
} else {
|
||
|
||
NbiFindRouteComplete(
|
||
&Connection->FindRouteRequest,
|
||
FALSE);
|
||
|
||
}
|
||
|
||
Status = STATUS_PENDING;
|
||
|
||
//
|
||
// This jump is like falling out of the if, except
|
||
// it skips over freeing the connection lock since
|
||
// we just did that.
|
||
//
|
||
|
||
*pbLockFreed = TRUE;
|
||
|
||
} else {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection));
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
|
||
NB_FREE_LOCK (&Device->Lock, DeviceLH);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_CONNECT);
|
||
|
||
Status = STATUS_CANCELLED;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We could not find or queue a request for
|
||
// this remote, fail it. When the refcount
|
||
// drops the state will go to INACTIVE and
|
||
// the connection ID will be deassigned.
|
||
//
|
||
|
||
if (Status == STATUS_DEVICE_DOES_NOT_EXIST) {
|
||
Status = STATUS_BAD_NETWORK_PATH;
|
||
}
|
||
|
||
NB_FREE_LOCK (&Device->Lock, DeviceLH);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_CONNECT);
|
||
}
|
||
|
||
return Status;
|
||
} /* NbiTdiConnectFindName */
|
||
|
||
|
||
NTSTATUS
|
||
NbiTdiDisconnect(
|
||
IN PDEVICE Device,
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine connects to a remote machine.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Request - The request describing the connect.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PCONNECTION Connection;
|
||
BOOLEAN DisconnectWait;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle1)
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle2)
|
||
NB_DEFINE_SYNC_CONTEXT (SyncContext)
|
||
CTELockHandle CancelLH;
|
||
|
||
//
|
||
// Check that the file type is valid
|
||
//
|
||
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
|
||
{
|
||
CTEAssert(FALSE);
|
||
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
||
}
|
||
|
||
//
|
||
// Check that the connection is valid. This references
|
||
// the connection.
|
||
//
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
Status = NbiVerifyConnection (Connection);
|
||
if (!NT_SUCCESS (Status)) {
|
||
return Status;
|
||
}
|
||
|
||
DisconnectWait = (BOOLEAN)
|
||
((((PTDI_REQUEST_KERNEL_DISCONNECT)(REQUEST_PARAMETERS(Request)))->RequestFlags &
|
||
TDI_DISCONNECT_WAIT) != 0);
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
|
||
//
|
||
// We need to be inside a sync because NbiStopConnection
|
||
// expects that.
|
||
//
|
||
NB_BEGIN_SYNC (&SyncContext);
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
if (DisconnectWait) {
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
//
|
||
// This disconnect wait will get completed by
|
||
// NbiStopConnection.
|
||
//
|
||
|
||
if (Connection->DisconnectWaitRequest == NULL) {
|
||
|
||
|
||
if (!Request->Cancel) {
|
||
|
||
IoSetCancelRoutine (Request, NbiCancelDisconnectWait);
|
||
NB_DEBUG2 (CONNECTION, ("Disconnect wait queued on connection %lx\n", Connection));
|
||
Connection->DisconnectWaitRequest = Request;
|
||
Status = STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Cancelled disconnect wait on connection %lx\n", Connection));
|
||
Status = STATUS_CANCELLED;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We got a second disconnect request and we already
|
||
// have one pending.
|
||
//
|
||
|
||
NB_DEBUG (CONNECTION, ("Disconnect wait failed, already queued on connection %lx\n", Connection));
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
|
||
} else if (Connection->State == CONNECTION_STATE_DISCONNECT) {
|
||
|
||
NB_DEBUG (CONNECTION, ("Disconnect wait submitted on disconnected connection %lx\n", Connection));
|
||
Status = Connection->Status;
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Disconnect wait failed, bad state on connection %lx\n", Connection));
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
} else {
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
// we dont need to hold CancelSpinLock so release it,
|
||
// since we are releasing the locks out of order, we must
|
||
// swap the irql to get the priorities right.
|
||
|
||
NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
Connection->DisconnectRequest = Request;
|
||
Status = STATUS_PENDING;
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Disconnect of active connection %lx\n", Connection));
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
|
||
//
|
||
// This call releases the connection lock, sets
|
||
// the state to DISCONNECTING, and sends out
|
||
// the first session end.
|
||
//
|
||
|
||
NbiStopConnection(
|
||
Connection,
|
||
STATUS_LOCAL_DISCONNECT
|
||
NB_LOCK_HANDLE_ARG (LockHandle1));
|
||
|
||
} else if (Connection->State == CONNECTION_STATE_DISCONNECT) {
|
||
|
||
//
|
||
// There is already a disconnect pending. Queue
|
||
// this one up so it completes when the refcount
|
||
// goes to zero.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Disconnect of disconnecting connection %lx\n", Connection));
|
||
|
||
if (Connection->DisconnectRequest == NULL) {
|
||
Connection->DisconnectRequest = Request;
|
||
Status = STATUS_PENDING;
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK ( CancelLH );
|
||
|
||
} else if ((Connection->State == CONNECTION_STATE_LISTENING) &&
|
||
(Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) {
|
||
|
||
//
|
||
// We were waiting for an accept, but instead we got
|
||
// a disconnect. Remove the reference and the teardown
|
||
// will proceed. The disconnect will complete when the
|
||
// refcount goes to zero.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Disconnect of accept pending connection %lx\n", Connection));
|
||
|
||
if (Connection->DisconnectRequest == NULL) {
|
||
Connection->DisconnectRequest = Request;
|
||
Status = STATUS_PENDING;
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK ( CancelLH );
|
||
|
||
NbiDereferenceConnection (Connection, CREF_W_ACCEPT);
|
||
|
||
} else if (Connection->State == CONNECTION_STATE_CONNECTING) {
|
||
|
||
// we dont need to hold CancelSpinLock so release it,
|
||
// since we are releasing the locks out of order, we must
|
||
// swap the irql to get the priorities right.
|
||
|
||
NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
//
|
||
// We are connecting, and got a disconnect. We call
|
||
// NbiStopConnection which will handle this case
|
||
// and abort the connect.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Disconnect of connecting connection %lx\n", Connection));
|
||
|
||
if (Connection->DisconnectRequest == NULL) {
|
||
Connection->DisconnectRequest = Request;
|
||
Status = STATUS_PENDING;
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
//
|
||
// This call releases the connection lock and
|
||
// aborts the connect request.
|
||
//
|
||
|
||
NbiStopConnection(
|
||
Connection,
|
||
STATUS_LOCAL_DISCONNECT
|
||
NB_LOCK_HANDLE_ARG (LockHandle1));
|
||
|
||
} else {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Disconnect of invalid connection (%d) %lx\n",
|
||
Connection->State, Connection));
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
Status = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
NB_END_SYNC (&SyncContext);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_VERIFY);
|
||
|
||
return Status;
|
||
|
||
} /* NbiTdiDisconnect */
|
||
|
||
|
||
BOOLEAN
|
||
NbiAssignConnectionId(
|
||
IN PDEVICE Device,
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to assign a connection ID. It picks
|
||
one whose hash table has the fewest entries.
|
||
|
||
THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH
|
||
IT HELD. THE CONNECTION IS INSERTED INTO THE CORRECT HASH
|
||
ENTRY BY THIS CALL.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Connection - The connection that needs an ID assigned.
|
||
|
||
Return Value:
|
||
|
||
TRUE if it could be successfully assigned.
|
||
|
||
--*/
|
||
|
||
{
|
||
UINT Hash;
|
||
UINT i;
|
||
USHORT ConnectionId, HashId;
|
||
PCONNECTION CurConnection;
|
||
|
||
|
||
CTEAssert (Connection->LocalConnectionId == 0xffff);
|
||
|
||
//
|
||
// Find the hash bucket with the fewest entries.
|
||
//
|
||
|
||
Hash = 0;
|
||
for (i = 1; i < CONNECTION_HASH_COUNT; i++) {
|
||
if (Device->ConnectionHash[i].ConnectionCount < Device->ConnectionHash[Hash].ConnectionCount) {
|
||
Hash = i;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Now find a valid connection ID within that bucket.
|
||
//
|
||
|
||
ConnectionId = Device->ConnectionHash[Hash].NextConnectionId;
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Scan through the list to see if this ID is in use.
|
||
//
|
||
|
||
HashId = (USHORT)(ConnectionId | (Hash << CONNECTION_HASH_SHIFT));
|
||
|
||
CurConnection = Device->ConnectionHash[Hash].Connections;
|
||
|
||
while (CurConnection != NULL) {
|
||
if (CurConnection->LocalConnectionId != HashId) {
|
||
CurConnection = CurConnection->NextConnection;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (CurConnection == NULL) {
|
||
break;
|
||
}
|
||
|
||
if (ConnectionId >= CONNECTION_MAXIMUM_ID) {
|
||
ConnectionId = 1;
|
||
} else {
|
||
++ConnectionId;
|
||
}
|
||
|
||
//
|
||
// What if we have 64K-1 sessions and loop forever?
|
||
//
|
||
}
|
||
|
||
if (Device->ConnectionHash[Hash].NextConnectionId >= CONNECTION_MAXIMUM_ID) {
|
||
Device->ConnectionHash[Hash].NextConnectionId = 1;
|
||
} else {
|
||
++Device->ConnectionHash[Hash].NextConnectionId;
|
||
}
|
||
|
||
Connection->LocalConnectionId = HashId;
|
||
Connection->RemoteConnectionId = 0xffff;
|
||
NB_DEBUG2 (CONNECTION, ("Assigned ID %lx to %x\n", Connection->LocalConnectionId, Connection));
|
||
|
||
Connection->NextConnection = Device->ConnectionHash[Hash].Connections;
|
||
Device->ConnectionHash[Hash].Connections = Connection;
|
||
++Device->ConnectionHash[Hash].ConnectionCount;
|
||
|
||
return TRUE;
|
||
|
||
} /* NbiAssignConnectionId */
|
||
|
||
|
||
VOID
|
||
NbiDeassignConnectionId(
|
||
IN PDEVICE Device,
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to deassign a connection ID. It removes
|
||
the connection from the hash bucket for its ID.
|
||
|
||
THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH
|
||
IT HELD.
|
||
|
||
Arguments:
|
||
|
||
Device - The netbios device.
|
||
|
||
Connection - The connection that needs an ID assigned.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
UINT Hash;
|
||
PCONNECTION CurConnection;
|
||
PCONNECTION * PrevConnection;
|
||
|
||
//
|
||
// Make sure the connection has a valid ID.
|
||
//
|
||
|
||
CTEAssert (Connection->LocalConnectionId != 0xffff);
|
||
|
||
Hash = (Connection->LocalConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT;
|
||
|
||
CurConnection = Device->ConnectionHash[Hash].Connections;
|
||
PrevConnection = &Device->ConnectionHash[Hash].Connections;
|
||
|
||
while (TRUE) {
|
||
|
||
CTEAssert (CurConnection != NULL);
|
||
|
||
//
|
||
// We can loop until we find it because it should be
|
||
// on here.
|
||
//
|
||
|
||
if (CurConnection == Connection) {
|
||
*PrevConnection = Connection->NextConnection;
|
||
--Device->ConnectionHash[Hash].ConnectionCount;
|
||
break;
|
||
}
|
||
|
||
PrevConnection = &CurConnection->NextConnection;
|
||
CurConnection = CurConnection->NextConnection;
|
||
|
||
}
|
||
|
||
Connection->LocalConnectionId = 0xffff;
|
||
|
||
} /* NbiDeassignConnectionId */
|
||
|
||
|
||
VOID
|
||
NbiConnectionTimeout(
|
||
IN CTEEvent * Event,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when the connection timer expires.
|
||
This is either because we need to send the next session
|
||
initialize, or because our listen has timed out.
|
||
|
||
Arguments:
|
||
|
||
Event - The event used to queue the timer.
|
||
|
||
Context - The context, which is the connection.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION Connection = (PCONNECTION)Context;
|
||
PDEVICE Device = NbiDevice;
|
||
PREQUEST Request;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle)
|
||
NB_DEFINE_LOCK_HANDLE (CancelLH)
|
||
|
||
//
|
||
// Take the lock and see what we need to do.
|
||
//
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if ((Connection->State == CONNECTION_STATE_CONNECTING) &&
|
||
(Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) {
|
||
|
||
if (--Connection->Retries == 0) {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Timing out session initializes on %lx\n", Connection));
|
||
|
||
//
|
||
// We have just timed out this connect, we fail the
|
||
// request. When the reference count goes to 0 we
|
||
// will set the state to INACTIVE and deassign
|
||
// the connection ID.
|
||
//
|
||
|
||
Request = Connection->ConnectRequest;
|
||
Connection->ConnectRequest = NULL;
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
REQUEST_STATUS (Request) = STATUS_REMOTE_NOT_LISTENING;
|
||
NbiCompleteRequest (Request);
|
||
NbiFreeRequest (Device, Request);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_CONNECT);
|
||
NbiDereferenceConnection (Connection, CREF_TIMER);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Send the next session initialize.
|
||
//
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
NbiSendSessionInitialize (Connection);
|
||
|
||
CTEStartTimer(
|
||
&Connection->Timer,
|
||
Device->ConnectionTimeout,
|
||
NbiConnectionTimeout,
|
||
(PVOID)Connection);
|
||
}
|
||
|
||
} else if (Connection->State == CONNECTION_STATE_DISCONNECT) {
|
||
|
||
if ((Connection->SubState != CONNECTION_SUBSTATE_D_W_ACK) ||
|
||
(--Connection->Retries == 0)) {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Timing out disconnect of %lx\n", Connection));
|
||
|
||
//
|
||
// Just dereference the connection, that will cause the
|
||
// disconnect to be completed, the state to be set
|
||
// to INACTIVE, and our connection ID deassigned.
|
||
//
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_TIMER);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Send the next session end.
|
||
//
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
NbiSendSessionEnd(Connection);
|
||
|
||
CTEStartTimer(
|
||
&Connection->Timer,
|
||
Device->ConnectionTimeout,
|
||
NbiConnectionTimeout,
|
||
(PVOID)Connection);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NbiDereferenceConnection (Connection, CREF_TIMER);
|
||
|
||
}
|
||
|
||
} /* NbiConnectionTimeout */
|
||
|
||
|
||
VOID
|
||
NbiCancelListen(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to cancel a posted
|
||
listen.
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCONNECTION Connection;
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
PDEVICE Device = (PDEVICE)DeviceObject;
|
||
PREQUEST Request = (PREQUEST)Irp;
|
||
|
||
|
||
CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
||
(REQUEST_MINOR_FUNCTION(Request) == TDI_LISTEN));
|
||
|
||
CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE);
|
||
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
|
||
if ((Connection->State == CONNECTION_STATE_LISTENING) &&
|
||
(Connection->SubState == CONNECTION_SUBSTATE_L_WAITING) &&
|
||
(Connection->ListenRequest == Request)) {
|
||
|
||
//
|
||
// When the reference count goes to 0, we will set the
|
||
// state to INACTIVE and deassign the connection ID.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Cancelled listen on %lx\n", Connection));
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
Connection->ListenRequest = NULL;
|
||
RemoveEntryList (REQUEST_LINKAGE(Request));
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
REQUEST_INFORMATION(Request) = 0;
|
||
REQUEST_STATUS(Request) = STATUS_CANCELLED;
|
||
|
||
NbiCompleteRequest (Request);
|
||
NbiFreeRequest(Device, Request);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_LISTEN);
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Cancel listen on invalid connection %lx\n", Connection));
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
}
|
||
|
||
} /* NbiCancelListen */
|
||
|
||
|
||
VOID
|
||
NbiCancelConnectFindName(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to cancel a connect
|
||
request which is waiting for the name to be found.
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCONNECTION Connection;
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
PDEVICE Device = (PDEVICE)DeviceObject;
|
||
PREQUEST Request = (PREQUEST)Irp;
|
||
PLIST_ENTRY p;
|
||
BOOLEAN fCanceled = TRUE;
|
||
|
||
|
||
CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
||
(REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT));
|
||
|
||
CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE);
|
||
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
|
||
if ((Connection->State == CONNECTION_STATE_CONNECTING) &&
|
||
(Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) &&
|
||
(Connection->ConnectRequest == Request)) {
|
||
|
||
//
|
||
// Make sure the request is still on the queue
|
||
// before cancelling it.
|
||
//
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
for (p = Device->WaitingConnects.Flink;
|
||
p != &Device->WaitingConnects;
|
||
p = p->Flink) {
|
||
|
||
if (LIST_ENTRY_TO_REQUEST(p) == Request) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (p != &Device->WaitingConnects) {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Cancelled find name connect on %lx\n", Connection));
|
||
|
||
//
|
||
// When the reference count goes to 0, we will set the
|
||
// state to INACTIVE and deassign the connection ID.
|
||
//
|
||
|
||
Connection->ConnectRequest = NULL;
|
||
RemoveEntryList (REQUEST_LINKAGE(Request));
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
|
||
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
REQUEST_STATUS(Request) = STATUS_CANCELLED;
|
||
|
||
#ifdef RASAUTODIAL
|
||
if (Connection->Flags & CONNECTION_FLAGS_AUTOCONNECTING)
|
||
fCanceled = NbiCancelTdiConnect(Device, Request, Connection);
|
||
#endif // RASAUTODIAL
|
||
|
||
if (fCanceled) {
|
||
NbiCompleteRequest (Request);
|
||
NbiFreeRequest(Device, Request);
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_WAIT_CACHE);
|
||
NbiDereferenceConnection (Connection, CREF_CONNECT);
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Cancel connect not found on queue %lx\n", Connection));
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection));
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
}
|
||
|
||
} /* NbiCancelConnectFindName */
|
||
|
||
|
||
VOID
|
||
NbiCancelConnectWaitResponse(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to cancel a connect
|
||
request which is waiting for a rip or session init response
|
||
from the remote.
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCONNECTION Connection;
|
||
CTELockHandle LockHandle1;
|
||
PDEVICE Device = (PDEVICE)DeviceObject;
|
||
PREQUEST Request = (PREQUEST)Irp;
|
||
BOOLEAN TimerWasStopped = FALSE;
|
||
|
||
|
||
CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
||
(REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT));
|
||
|
||
CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE);
|
||
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
|
||
if ((Connection->State == CONNECTION_STATE_CONNECTING) &&
|
||
(Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) &&
|
||
(Connection->ConnectRequest == Request)) {
|
||
|
||
//
|
||
// When the reference count goes to 0, we will set the
|
||
// state to INACTIVE and deassign the connection ID.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Cancelled wait response connect on %lx\n", Connection));
|
||
|
||
Connection->ConnectRequest = NULL;
|
||
Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
|
||
|
||
if (CTEStopTimer (&Connection->Timer)) {
|
||
TimerWasStopped = TRUE;
|
||
}
|
||
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
REQUEST_STATUS(Request) = STATUS_CANCELLED;
|
||
|
||
NbiCompleteRequest (Request);
|
||
NbiFreeRequest(Device, Request);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_CONNECT);
|
||
|
||
if (TimerWasStopped) {
|
||
NbiDereferenceConnection (Connection, CREF_TIMER);
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection));
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
}
|
||
|
||
} /* NbiCancelConnectWaitResponse */
|
||
|
||
|
||
VOID
|
||
NbiCancelDisconnectWait(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to cancel a posted
|
||
disconnect wait.
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCONNECTION Connection;
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
PDEVICE Device = (PDEVICE)DeviceObject;
|
||
PREQUEST Request = (PREQUEST)Irp;
|
||
|
||
|
||
CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
||
(REQUEST_MINOR_FUNCTION(Request) == TDI_DISCONNECT));
|
||
|
||
CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE);
|
||
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
if (Connection->DisconnectWaitRequest == Request) {
|
||
|
||
Connection->DisconnectWaitRequest = NULL;
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
REQUEST_INFORMATION(Request) = 0;
|
||
REQUEST_STATUS(Request) = STATUS_CANCELLED;
|
||
|
||
NbiCompleteRequest (Request);
|
||
NbiFreeRequest(Device, Request);
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
}
|
||
|
||
} /* NbiCancelDisconnectWait */
|
||
|
||
|
||
PCONNECTION
|
||
NbiLookupConnectionByContext(
|
||
IN PADDRESS_FILE AddressFile,
|
||
IN CONNECTION_CONTEXT ConnectionContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks up a connection based on the context.
|
||
The connection is assumed to be associated with the
|
||
specified address file.
|
||
|
||
Arguments:
|
||
|
||
AddressFile - Pointer to an address file.
|
||
|
||
ConnectionContext - Connection context to find.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the connection we found
|
||
|
||
--*/
|
||
|
||
{
|
||
CTELockHandle LockHandle1, LockHandle2;
|
||
PLIST_ENTRY p;
|
||
PADDRESS Address = AddressFile->Address;
|
||
PCONNECTION Connection;
|
||
|
||
NB_GET_LOCK (&Address->Lock, &LockHandle1);
|
||
|
||
for (p=AddressFile->ConnectionDatabase.Flink;
|
||
p != &AddressFile->ConnectionDatabase;
|
||
p=p->Flink) {
|
||
|
||
Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage);
|
||
|
||
NB_GET_LOCK (&Connection->Lock, &LockHandle2);
|
||
|
||
//
|
||
// Does this spinlock ordering hurt us somewhere else?
|
||
//
|
||
|
||
if (Connection->Context == ConnectionContext) {
|
||
|
||
NbiReferenceConnection (Connection, CREF_BY_CONTEXT);
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle2);
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle1);
|
||
|
||
return Connection;
|
||
}
|
||
|
||
NB_FREE_LOCK (&Connection->Lock, LockHandle2);
|
||
|
||
}
|
||
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle1);
|
||
|
||
return NULL;
|
||
|
||
} /* NbiLookupConnectionByContext */
|
||
|
||
|
||
PCONNECTION
|
||
NbiCreateConnection(
|
||
IN PDEVICE Device
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a transport connection and associates it with
|
||
the specified transport device context. The reference count in the
|
||
connection is automatically set to 1, and the reference count of the
|
||
device context is incremented.
|
||
|
||
Arguments:
|
||
|
||
Device - Pointer to the device context (which is really just
|
||
the device object with its extension) to be associated with the
|
||
connection.
|
||
|
||
Return Value:
|
||
|
||
The newly created connection, or NULL if none can be allocated.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION Connection;
|
||
PNB_SEND_RESERVED SendReserved;
|
||
ULONG ConnectionSize;
|
||
ULONG HeaderLength;
|
||
NTSTATUS Status;
|
||
CTELockHandle LockHandle;
|
||
|
||
HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION);
|
||
ConnectionSize = FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + HeaderLength;
|
||
|
||
Connection = (PCONNECTION)NbiAllocateMemory (ConnectionSize, MEMORY_CONNECTION, "Connection");
|
||
if (Connection == NULL) {
|
||
NB_DEBUG (CONNECTION, ("Create connection failed\n"));
|
||
return NULL;
|
||
}
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Create connection %lx\n", Connection));
|
||
RtlZeroMemory (Connection, ConnectionSize);
|
||
|
||
|
||
#if defined(NB_OWN_PACKETS)
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
if (NbiInitializeSendPacket(
|
||
Device,
|
||
Connection->SendPacketPoolHandle,
|
||
&Connection->SendPacket,
|
||
Connection->SendPacketHeader,
|
||
HeaderLength) != STATUS_SUCCESS) {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket));
|
||
Connection->SendPacketInUse = TRUE;
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
SendReserved = SEND_RESERVED(&Connection->SendPacket);
|
||
SendReserved->u.SR_CO.Connection = Connection;
|
||
SendReserved->OwnedByConnection = TRUE;
|
||
#ifdef NB_TRACK_POOL
|
||
SendReserved->Pool = NULL;
|
||
#endif
|
||
}
|
||
|
||
#else // !NB_OWN_PACKETS
|
||
|
||
//
|
||
// if we are using ndis packets, first create packet pool for 1 packet descriptor
|
||
//
|
||
Connection->SendPacketPoolHandle = (NDIS_HANDLE) NDIS_PACKET_POOL_TAG_FOR_NWLNKNB; // Dbg info for Ndis!
|
||
NdisAllocatePacketPoolEx (&Status, &Connection->SendPacketPoolHandle, 1, 0, sizeof(NB_SEND_RESERVED));
|
||
if (!NT_SUCCESS(Status)){
|
||
NB_DEBUG (CONNECTION, ("Could not allocatee connection packet %lx\n", Status));
|
||
Connection->SendPacketInUse = TRUE;
|
||
} else {
|
||
|
||
NdisSetPacketPoolProtocolId (Connection->SendPacketPoolHandle, NDIS_PROTOCOL_ID_IPX);
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
if (NbiInitializeSendPacket(
|
||
Device,
|
||
Connection->SendPacketPoolHandle,
|
||
&Connection->SendPacket,
|
||
Connection->SendPacketHeader,
|
||
HeaderLength) != STATUS_SUCCESS) {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket));
|
||
Connection->SendPacketInUse = TRUE;
|
||
|
||
//
|
||
// Also free up the pool which we allocated above.
|
||
//
|
||
NdisFreePacketPool(Connection->SendPacketPoolHandle);
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
SendReserved = SEND_RESERVED(&Connection->SendPacket);
|
||
SendReserved->u.SR_CO.Connection = Connection;
|
||
SendReserved->OwnedByConnection = TRUE;
|
||
#ifdef NB_TRACK_POOL
|
||
SendReserved->Pool = NULL;
|
||
#endif
|
||
}
|
||
}
|
||
|
||
#endif NB_OWN_PACKETS
|
||
|
||
Connection->Type = NB_CONNECTION_SIGNATURE;
|
||
Connection->Size = (USHORT)ConnectionSize;
|
||
|
||
#if 0
|
||
Connection->AddressFileLinked = FALSE;
|
||
Connection->AddressFile = NULL;
|
||
#endif
|
||
|
||
Connection->State = CONNECTION_STATE_INACTIVE;
|
||
#if 0
|
||
Connection->SubState = 0;
|
||
Connection->ReferenceCount = 0;
|
||
#endif
|
||
|
||
Connection->CanBeDestroyed = TRUE;
|
||
|
||
Connection->TickCount = 1;
|
||
Connection->HopCount = 1;
|
||
|
||
//
|
||
// Device->InitialRetransmissionTime is in milliseconds, as is
|
||
// SHORT_TIMER_DELTA.
|
||
//
|
||
|
||
Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA;
|
||
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
|
||
|
||
//
|
||
// Device->KeepAliveTimeout is in half-seconds, while LONG_TIMER_DELTA
|
||
// is in milliseconds.
|
||
//
|
||
|
||
Connection->WatchdogTimeout = (Device->KeepAliveTimeout * 500) / LONG_TIMER_DELTA;
|
||
|
||
|
||
Connection->LocalConnectionId = 0xffff;
|
||
|
||
//
|
||
// When the connection becomes active we will replace the
|
||
// destination address of this header with the correct
|
||
// information.
|
||
//
|
||
|
||
RtlCopyMemory(&Connection->RemoteHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER));
|
||
|
||
Connection->Device = Device;
|
||
Connection->DeviceLock = &Device->Lock;
|
||
CTEInitLock (&Connection->Lock.Lock);
|
||
|
||
CTEInitTimer (&Connection->Timer);
|
||
|
||
InitializeListHead (&Connection->NdisSendQueue);
|
||
#if 0
|
||
Connection->NdisSendsInProgress = 0;
|
||
Connection->DisassociatePending = NULL;
|
||
Connection->ClosePending = NULL;
|
||
Connection->SessionInitAckData = NULL;
|
||
Connection->SessionInitAckDataLength = 0;
|
||
Connection->PiggybackAckTimeout = FALSE;
|
||
Connection->ReceivesWithoutAck = 0;
|
||
#endif
|
||
Connection->Flags = 0;
|
||
|
||
NbiReferenceDevice (Device, DREF_CONNECTION);
|
||
|
||
return Connection;
|
||
|
||
} /* NbiCreateConnection */
|
||
|
||
|
||
NTSTATUS
|
||
NbiVerifyConnection (
|
||
IN PCONNECTION 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. We reference
|
||
it to keep it from disappearing while we use it.
|
||
|
||
Arguments:
|
||
|
||
Connection - potential pointer to a CONNECTION object
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
CTELockHandle LockHandle;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PDEVICE Device = NbiDevice;
|
||
BOOLEAN LockHeld = FALSE;
|
||
|
||
try
|
||
{
|
||
if ((Connection->Size == FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) +
|
||
NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) &&
|
||
(Connection->Type == NB_CONNECTION_SIGNATURE))
|
||
{
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
LockHeld = TRUE;
|
||
|
||
if (Connection->State != CONNECTION_STATE_CLOSING)
|
||
{
|
||
NbiReferenceConnectionLock (Connection, CREF_VERIFY);
|
||
}
|
||
else
|
||
{
|
||
NbiPrint1("NbiVerifyConnection: C %lx closing\n", Connection);
|
||
status = STATUS_INVALID_CONNECTION;
|
||
}
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
}
|
||
else
|
||
{
|
||
NbiPrint1("NbiVerifyConnection: C %lx bad signature\n", Connection);
|
||
status = STATUS_INVALID_CONNECTION;
|
||
}
|
||
}
|
||
except(EXCEPTION_EXECUTE_HANDLER)
|
||
{
|
||
NbiPrint1("NbiVerifyConnection: C %lx exception\n", Connection);
|
||
if (LockHeld)
|
||
{
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
}
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
return status;
|
||
} /* NbiVerifyConnection */
|
||
|
||
|
||
VOID
|
||
NbiDestroyConnection(
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
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 nonpaged system pool.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection structure to be destroyed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE Device = Connection->Device;
|
||
#if 0
|
||
CTELockHandle LockHandle;
|
||
#endif
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Destroy connection %lx\n", Connection));
|
||
|
||
if (!Connection->SendPacketInUse) {
|
||
NbiDeinitializeSendPacket (Device, &Connection->SendPacket, Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION));
|
||
#if !defined(NB_OWN_PACKETS)
|
||
NdisFreePacketPool(Connection->SendPacketPoolHandle);
|
||
#endif
|
||
}
|
||
|
||
NbiFreeMemory (Connection, (ULONG)Connection->Size, MEMORY_CONNECTION, "Connection");
|
||
|
||
NbiDereferenceDevice (Device, DREF_CONNECTION);
|
||
|
||
} /* NbiDestroyConnection */
|
||
|
||
|
||
#if DBG
|
||
VOID
|
||
NbiRefConnection(
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine increments the reference count on a transport connection.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
(VOID)ExInterlockedAddUlong (
|
||
&Connection->ReferenceCount,
|
||
1,
|
||
&Connection->DeviceLock->Lock);
|
||
|
||
Connection->CanBeDestroyed = FALSE;
|
||
|
||
CTEAssert (Connection->ReferenceCount > 0);
|
||
|
||
} /* NbiRefConnection */
|
||
|
||
|
||
VOID
|
||
NbiRefConnectionLock(
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine increments the reference count on a transport connection
|
||
when the device lock is already held.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
++Connection->ReferenceCount;
|
||
Connection->CanBeDestroyed = FALSE;
|
||
|
||
CTEAssert (Connection->ReferenceCount > 0);
|
||
|
||
} /* NbiRefConnectionLock */
|
||
|
||
|
||
VOID
|
||
NbiRefConnectionSync(
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine increments the reference count on a transport connection
|
||
when we are in a sync routine.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
(VOID)NB_ADD_ULONG (
|
||
&Connection->ReferenceCount,
|
||
1,
|
||
Connection->DeviceLock);
|
||
|
||
Connection->CanBeDestroyed = FALSE;
|
||
|
||
CTEAssert (Connection->ReferenceCount > 0);
|
||
|
||
} /* NbiRefConnectionSync */
|
||
|
||
|
||
VOID
|
||
NbiDerefConnection(
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
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
|
||
NbiHandleConnectionZero to complete any disconnect, disassociate,
|
||
or close requests that have pended on the connection.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG oldvalue;
|
||
CTELockHandle LockHandle;
|
||
|
||
NB_GET_LOCK( Connection->DeviceLock, &LockHandle );
|
||
CTEAssert( Connection->ReferenceCount );
|
||
if ( !(--Connection->ReferenceCount) ) {
|
||
|
||
Connection->ThreadsInHandleConnectionZero++;
|
||
|
||
NB_FREE_LOCK( Connection->DeviceLock, LockHandle );
|
||
|
||
//
|
||
// If the refcount has dropped to 0, then the connection can
|
||
// become inactive. We reacquire the spinlock and if it has not
|
||
// jumped back up then we handle any disassociates and closes
|
||
// that have pended.
|
||
//
|
||
|
||
NbiHandleConnectionZero (Connection);
|
||
} else {
|
||
|
||
NB_FREE_LOCK( Connection->DeviceLock, LockHandle );
|
||
}
|
||
|
||
|
||
} /* NbiDerefConnection */
|
||
|
||
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NbiHandleConnectionZero(
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles a connection's refcount going to 0.
|
||
|
||
If two threads are in this at the same time and
|
||
the close has already come through, one of them might
|
||
destroy the connection while the other one is looking
|
||
at it. We minimize the chance of this by not derefing
|
||
the connection after calling CloseConnection.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
CTELockHandle LockHandle;
|
||
PDEVICE Device;
|
||
PADDRESS_FILE AddressFile;
|
||
PADDRESS Address;
|
||
PREQUEST DisconnectPending;
|
||
PREQUEST DisassociatePending;
|
||
PREQUEST ClosePending;
|
||
|
||
|
||
Device = Connection->Device;
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
#if DBG
|
||
//
|
||
// Make sure if our reference count is zero, all the
|
||
// sub-reference counts are also zero.
|
||
//
|
||
|
||
if (Connection->ReferenceCount == 0) {
|
||
|
||
UINT i;
|
||
for (i = 0; i < CREF_TOTAL; i++) {
|
||
if (Connection->RefTypes[i] != 0) {
|
||
DbgPrint ("NBI: Connection reftype mismatch on %lx\n", Connection);
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If the connection was assigned an ID, then remove it
|
||
// (it is assigned one when it leaves INACTIVE).
|
||
//
|
||
|
||
if (Connection->LocalConnectionId != 0xffff) {
|
||
NbiDeassignConnectionId (Device, Connection);
|
||
}
|
||
|
||
//
|
||
// Complete any pending disconnects.
|
||
//
|
||
|
||
if (Connection->DisconnectRequest != NULL) {
|
||
|
||
DisconnectPending = Connection->DisconnectRequest;
|
||
Connection->DisconnectRequest = NULL;
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
REQUEST_STATUS(DisconnectPending) = STATUS_SUCCESS;
|
||
NbiCompleteRequest (DisconnectPending);
|
||
NbiFreeRequest (Device, DisconnectPending);
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
}
|
||
|
||
//
|
||
// This should have been completed by NbiStopConnection,
|
||
// or else not allowed to be queued.
|
||
//
|
||
|
||
CTEAssert (Connection->DisconnectWaitRequest == NULL);
|
||
|
||
|
||
Connection->State = CONNECTION_STATE_INACTIVE;
|
||
|
||
RtlZeroMemory (&Connection->ConnectionInfo, sizeof(TDI_CONNECTION_INFO));
|
||
Connection->TickCount = 1;
|
||
Connection->HopCount = 1;
|
||
Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA;
|
||
|
||
Connection->ConnectionInfo.TransmittedTsdus = 0;
|
||
Connection->ConnectionInfo.TransmissionErrors = 0;
|
||
Connection->ConnectionInfo.ReceivedTsdus = 0;
|
||
Connection->ConnectionInfo.ReceiveErrors = 0;
|
||
|
||
//
|
||
// See if we need to do a disassociate now.
|
||
//
|
||
|
||
if ((Connection->ReferenceCount == 0) &&
|
||
(Connection->DisassociatePending != NULL)) {
|
||
|
||
//
|
||
// A disassociate pended, now we complete it.
|
||
//
|
||
|
||
DisassociatePending = Connection->DisassociatePending;
|
||
Connection->DisassociatePending = NULL;
|
||
|
||
if (AddressFile = Connection->AddressFile) {
|
||
|
||
//
|
||
// Set this so nobody else tries to disassociate.
|
||
//
|
||
Connection->AddressFile = (PVOID)-1;
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
//
|
||
// Take this connection out of the address file's list.
|
||
//
|
||
|
||
Address = AddressFile->Address;
|
||
NB_GET_LOCK (&Address->Lock, &LockHandle);
|
||
|
||
if (Connection->AddressFileLinked) {
|
||
Connection->AddressFileLinked = FALSE;
|
||
RemoveEntryList (&Connection->AddressFileLinkage);
|
||
}
|
||
|
||
//
|
||
// We are done.
|
||
//
|
||
|
||
Connection->AddressFile = NULL;
|
||
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle);
|
||
|
||
//
|
||
// Clean up the reference counts and complete any
|
||
// disassociate requests that pended.
|
||
//
|
||
|
||
NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION);
|
||
}
|
||
else {
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
}
|
||
|
||
if (DisassociatePending != (PVOID)-1) {
|
||
REQUEST_STATUS(DisassociatePending) = STATUS_SUCCESS;
|
||
NbiCompleteRequest (DisassociatePending);
|
||
NbiFreeRequest (Device, DisassociatePending);
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// If a close was pending, complete that.
|
||
//
|
||
|
||
NB_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
if ((Connection->ReferenceCount == 0) &&
|
||
(Connection->ClosePending)) {
|
||
|
||
ClosePending = Connection->ClosePending;
|
||
Connection->ClosePending = NULL;
|
||
|
||
//
|
||
// If we are associated with an address, we need
|
||
// to simulate a disassociate at this point.
|
||
//
|
||
|
||
if ((Connection->AddressFile != NULL) &&
|
||
(Connection->AddressFile != (PVOID)-1)) {
|
||
|
||
AddressFile = Connection->AddressFile;
|
||
Connection->AddressFile = (PVOID)-1;
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
//
|
||
// Take this connection out of the address file's list.
|
||
//
|
||
|
||
Address = AddressFile->Address;
|
||
NB_GET_LOCK (&Address->Lock, &LockHandle);
|
||
|
||
if (Connection->AddressFileLinked) {
|
||
Connection->AddressFileLinked = FALSE;
|
||
RemoveEntryList (&Connection->AddressFileLinkage);
|
||
}
|
||
|
||
//
|
||
// We are done.
|
||
//
|
||
|
||
NB_FREE_LOCK (&Address->Lock, LockHandle);
|
||
|
||
Connection->AddressFile = NULL;
|
||
|
||
//
|
||
// Clean up the reference counts and complete any
|
||
// disassociate requests that pended.
|
||
//
|
||
|
||
NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION);
|
||
|
||
} else {
|
||
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
//
|
||
// Even if the ref count is zero and we just cleaned up everything,
|
||
// we can not destroy the connection bcoz some other thread might still be
|
||
// in HandleConnectionZero routine. This could happen when 2 threads call into
|
||
// HandleConnectionZero, one thread runs thru completion, close comes along
|
||
// and the other thread is still in HandleConnectionZero routine.
|
||
//
|
||
|
||
CTEAssert( Connection->ThreadsInHandleConnectionZero );
|
||
if (ExInterlockedAddUlong ( &Connection->ThreadsInHandleConnectionZero, (ULONG)-1, &Device->Lock.Lock) == 1) {
|
||
NbiDestroyConnection(Connection);
|
||
}
|
||
|
||
REQUEST_STATUS(ClosePending) = STATUS_SUCCESS;
|
||
NbiCompleteRequest (ClosePending);
|
||
NbiFreeRequest (Device, ClosePending);
|
||
|
||
} else {
|
||
|
||
if ( Connection->ReferenceCount == 0 ) {
|
||
Connection->CanBeDestroyed = TRUE;
|
||
}
|
||
|
||
CTEAssert( Connection->ThreadsInHandleConnectionZero );
|
||
Connection->ThreadsInHandleConnectionZero--;
|
||
NB_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
} /* NbiHandleConnectionZero */
|
||
|