mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3678 lines
95 KiB
3678 lines
95 KiB
/*++
|
|
|
|
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 */
|
|
|