diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/isnp/nb/connect.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/tdi/isnp/nb/connect.c')
-rw-r--r-- | private/ntos/tdi/isnp/nb/connect.c | 3628 |
1 files changed, 3628 insertions, 0 deletions
diff --git a/private/ntos/tdi/isnp/nb/connect.c b/private/ntos/tdi/isnp/nb/connect.c new file mode 100644 index 000000000..7ee7204b5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/connect.c @@ -0,0 +1,3628 @@ +/*++ + +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 + + + +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)( // BUGBUG: 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; // BUGBUG: 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)( // BUGBUG: 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; // BUGBUG: 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 + + // + // 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 + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + 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. + // + // BUGBUG: 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. BUGBUG: 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; + + } + + + // + // BUGBUG: 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. BUGBUG: 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; + } + + // + // BUGBUG: 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. BUGBUG: Clean it up? + + } + + } + + + 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 connection is valid. 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); + +#ifdef ISN_NT + + Status = ObReferenceObjectByHandle ( + Parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *)&FileObject, + NULL); + + if (!NT_SUCCESS(Status)) { + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + AddressFile = (PADDRESS_FILE)(FileObject->FsContext); + +#else + + // + // I don't know how this works in a VxD. + // + + AddressFile = (PADDRESS_FILE)(Parameters->AddressHandle); + +#endif + + // + // 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 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); + + } + + // + // BUGBUG: 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 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); // BUGBUG: 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 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 UNALIGNED * 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 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)) { + + Parameters = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->RequestConnectionInformation->RemoteAddress), FALSE); + + 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); // BUGBUG: 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 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; + } + + // + // BUGBUG: 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_BAD_NETWORK_PATH; + 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); + + // + // BUGBUG: 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 + // + NdisAllocatePacketPool( &Status, &Connection->SendPacketPoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (CONNECTION, ("Could not allocatee connection packet %lx\n", Status)); + Connection->SendPacketInUse = TRUE; + } else { + 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. + + BUGBUG: 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; + + // + // BUGBUG: Make NbiInitializeConnection() to take care of all this. + // + + 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; + + // + // Set this so nobody else tries to disassociate. + // + + 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); + + 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 */ + |