Windows-Server-2003/net/nwlink/nb/connect.c

3679 lines
95 KiB
C
Raw Normal View History

2024-08-04 01:28:15 +02:00
/*++
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 */