2466 lines
86 KiB
C
2466 lines
86 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
session.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code to handle session frames
|
||
for the Netbios module of the ISN transport.
|
||
|
||
Author:
|
||
|
||
Adam Barr (adamba) 28-November-1993
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#ifdef RASAUTODIAL
|
||
#include <acd.h>
|
||
#include <acdapi.h>
|
||
#endif // RASAUTODIAL
|
||
#pragma hdrstop
|
||
|
||
#ifdef RASAUTODIAL
|
||
extern BOOLEAN fAcdLoadedG;
|
||
extern ACD_DRIVER AcdDriverG;
|
||
|
||
VOID
|
||
NbiNoteNewConnection(
|
||
PCONNECTION pConnection
|
||
);
|
||
#endif
|
||
|
||
#ifdef RSRC_TIMEOUT_DBG
|
||
VOID
|
||
NbiSendDeathPacket(
|
||
IN PCONNECTION Connection,
|
||
IN CTELockHandle LockHandle
|
||
)
|
||
{
|
||
PNDIS_PACKET Packet = PACKET(&NbiGlobalDeathPacket);
|
||
PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved);
|
||
NB_CONNECTION UNALIGNED * Header;
|
||
PDEVICE Device = NbiDevice;
|
||
NDIS_STATUS NdisStatus;
|
||
|
||
if ( Reserved->SendInProgress ) {
|
||
DbgPrint("***Could not send death packet - in use\n");
|
||
NB_FREE_LOCK(&Connection->Lock, LockHandle);
|
||
return;
|
||
}
|
||
|
||
Reserved->SendInProgress = TRUE;
|
||
Reserved->Type = SEND_TYPE_DEATH_PACKET;
|
||
|
||
//
|
||
// Fill in the IPX header -- the default header has the broadcast
|
||
// address on net 0 as the destination IPX address.
|
||
//
|
||
|
||
Header = (NB_CONNECTION UNALIGNED *)
|
||
(&Reserved->Header[Device->Bind.IncludedHeaderOffset]);
|
||
RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER));
|
||
|
||
Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256;
|
||
Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256;
|
||
|
||
Header->IpxHeader.PacketType = 0x04;
|
||
|
||
//
|
||
// Now fill in the Netbios header.
|
||
//
|
||
Header->Session.ConnectionControlFlag = 0;
|
||
Header->Session.DataStreamType = NB_CMD_DEATH_PACKET;
|
||
Header->Session.SourceConnectionId = Connection->LocalConnectionId;
|
||
Header->Session.DestConnectionId = Connection->RemoteConnectionId;
|
||
Header->Session.SendSequence = 0;
|
||
Header->Session.TotalDataLength = 0;
|
||
Header->Session.Offset = 0;
|
||
Header->Session.DataLength = 0;
|
||
|
||
|
||
NB_FREE_LOCK(&Connection->Lock, LockHandle);
|
||
|
||
DbgPrint("*****Death packet is being sent for connection %lx, to <%.16s>\n",Connection, Connection->RemoteName);
|
||
//
|
||
// Now send the frame, IPX will adjust the length of the
|
||
// first buffer correctly.
|
||
//
|
||
|
||
NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION));
|
||
if ((NdisStatus =
|
||
(*Device->Bind.SendHandler)(
|
||
&Connection->LocalTarget,
|
||
Packet,
|
||
sizeof(NB_CONNECTION),
|
||
sizeof(NB_CONNECTION))) != STATUS_PENDING) {
|
||
|
||
NbiSendComplete(
|
||
Packet,
|
||
NdisStatus);
|
||
|
||
}
|
||
|
||
}
|
||
#endif //RSRC_TIMEOUT_DBG
|
||
|
||
|
||
VOID
|
||
NbiProcessSessionData(
|
||
IN NDIS_HANDLE MacBindingHandle,
|
||
IN NDIS_HANDLE MacReceiveContext,
|
||
IN PIPX_LOCAL_TARGET RemoteAddress,
|
||
IN ULONG MacOptions,
|
||
IN PUCHAR LookaheadBuffer,
|
||
IN UINT LookaheadBufferSize,
|
||
IN UINT LookaheadBufferOffset,
|
||
IN UINT PacketSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles NB_CMD_SESSION_DATA frames.
|
||
|
||
Arguments:
|
||
|
||
MacBindingHandle - A handle to use when calling NdisTransferData.
|
||
|
||
MacReceiveContext - A context to use when calling NdisTransferData.
|
||
|
||
RemoteAddress - The local target this packet was received from.
|
||
|
||
MacOptions - The MAC options for the underlying NDIS binding.
|
||
|
||
LookaheadBuffer - The lookahead buffer, starting at the IPX
|
||
header.
|
||
|
||
LookaheadBufferSize - The length of the lookahead data.
|
||
|
||
LookaheadBufferOffset - The offset to add when calling
|
||
NdisTransferData.
|
||
|
||
PacketSize - The total length of the packet, starting at the
|
||
IPX header.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer;
|
||
NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session);
|
||
PCONNECTION Connection;
|
||
PREQUEST Request;
|
||
PDEVICE Device = NbiDevice;
|
||
ULONG Hash;
|
||
ULONG ReceiveFlags;
|
||
ULONG IndicateBytesTransferred = 0;
|
||
ULONG DataAvailable, DataIndicated;
|
||
ULONG DestBytes, BytesToTransfer;
|
||
PUCHAR DataHeader;
|
||
BOOLEAN Last, CompleteReceive, EndOfMessage, PartialReceive, CopyLookahead;
|
||
NTSTATUS Status;
|
||
NDIS_STATUS NdisStatus;
|
||
ULONG NdisBytesTransferred;
|
||
PIRP ReceiveIrp;
|
||
PSINGLE_LIST_ENTRY s;
|
||
PNB_RECEIVE_RESERVED ReceiveReserved;
|
||
PNDIS_PACKET Packet;
|
||
PNDIS_BUFFER BufferChain;
|
||
ULONG BufferChainLength;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle)
|
||
CTELockHandle CancelLH;
|
||
|
||
if (Sess->DestConnectionId != 0xffff) {
|
||
|
||
//
|
||
// This is an active connection, find it using
|
||
// our session id.
|
||
//
|
||
|
||
Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT;
|
||
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
Connection = Device->ConnectionHash[Hash].Connections;
|
||
|
||
while (Connection != NULL) {
|
||
|
||
if (Connection->LocalConnectionId == Sess->DestConnectionId) {
|
||
break;
|
||
}
|
||
Connection = Connection->NextConnection;
|
||
}
|
||
|
||
if (Connection == NULL) {
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
|
||
return;
|
||
}
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_INDICATE);
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
//
|
||
// See what is happening with this connection.
|
||
//
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
#ifdef RSRC_TIMEOUT_DBG
|
||
if ( Connection->FirstMessageRequest && NbiGlobalDebugResTimeout ) {
|
||
LARGE_INTEGER CurrentTime, ElapsedTime;
|
||
KeQuerySystemTime(&CurrentTime);
|
||
ElapsedTime.QuadPart = CurrentTime.QuadPart - Connection->FirstMessageRequestTime.QuadPart;
|
||
// DbgPrint("*****Elapsed %lx.%lx time\n",ElapsedTime.HighPart,ElapsedTime.LowPart);
|
||
if ( ElapsedTime.QuadPart > NbiGlobalMaxResTimeout.QuadPart ) {
|
||
|
||
DbgPrint("*****Connection %lx is not copleting irp %lx for %lx.%lx time\n",Connection, Connection->FirstMessageRequest,
|
||
ElapsedTime.HighPart,ElapsedTime.LowPart);
|
||
DbgPrint("************irp arrived at %lx.%lx current time %lx.%lx\n",
|
||
Connection->FirstMessageRequestTime.HighPart,Connection->FirstMessageRequestTime.LowPart,
|
||
CurrentTime.HighPart, CurrentTime.LowPart);
|
||
|
||
NbiSendDeathPacket( Connection, LockHandle );
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
}
|
||
}
|
||
#endif //RSRC_TIMEOUT_DBG
|
||
|
||
//
|
||
// The connection is up, see if this is data should
|
||
// be received.
|
||
//
|
||
|
||
if (Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) {
|
||
|
||
//
|
||
// This is an ack. This call releases the lock.
|
||
//
|
||
|
||
NbiProcessDataAck(
|
||
Connection,
|
||
Sess,
|
||
RemoteAddress
|
||
NB_LOCK_HANDLE_ARG (LockHandle)
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// See if there is any piggyback ack here.
|
||
//
|
||
|
||
if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) {
|
||
|
||
//
|
||
// We are waiting for an ack, so see if this acks
|
||
// anything. Even the old netbios sometimes piggyback
|
||
// acks (and doesn't send the explicit ack).
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiReframeConnection(
|
||
Connection,
|
||
Sess->ReceiveSequence,
|
||
Sess->BytesReceived,
|
||
FALSE
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State != CONNECTION_STATE_ACTIVE) {
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
}
|
||
|
||
} else if ((Connection->NewNetbios) &&
|
||
(Connection->CurrentSend.SendSequence != Connection->UnAckedSend.SendSequence)) {
|
||
|
||
//
|
||
// For the new netbios, even if we are not waiting
|
||
// for an ack he may have acked something with this
|
||
// send and we should check, since it may allow
|
||
// us to open our send window.
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiReframeConnection(
|
||
Connection,
|
||
Sess->ReceiveSequence,
|
||
Sess->BytesReceived,
|
||
FALSE
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State != CONNECTION_STATE_ACTIVE) {
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// This is data on the connection. First make sure
|
||
// it is the data we expect next.
|
||
//
|
||
|
||
if (Connection->NewNetbios) {
|
||
|
||
if (Sess->SendSequence != Connection->ReceiveSequence) {
|
||
|
||
++Connection->ConnectionInfo.ReceiveErrors;
|
||
++Device->Statistics.DataFramesRejected;
|
||
ADD_TO_LARGE_INTEGER(
|
||
&Device->Statistics.DataFrameBytesRejected,
|
||
PacketSize - sizeof(NB_CONNECTION));
|
||
|
||
if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) ||
|
||
(Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) {
|
||
|
||
NB_ACK_TYPE AckType;
|
||
|
||
NB_DEBUG2 (RECEIVE, ("Got unexp data on %lx, %x(%d) expect %x(%d)\n",
|
||
Connection, Sess->SendSequence, Sess->Offset,
|
||
Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset));
|
||
|
||
//
|
||
// If we are receiving a packet we have already seen, just
|
||
// send a normal ack, otherwise force a resend. This test
|
||
// we do is equivalent to
|
||
// Sess->SendSequence < Connection->ReceiveSequence
|
||
// but rearranged so it works when the numbers wrap.
|
||
//
|
||
|
||
if ((SHORT)(Sess->SendSequence - Connection->ReceiveSequence) < 0) {
|
||
|
||
//
|
||
// Since this is a resend, check if the local
|
||
// target has changed.
|
||
//
|
||
#if defined(_PNP_POWER)
|
||
|
||
if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, sizeof(IPX_LOCAL_TARGET))) {
|
||
#if DBG
|
||
DbgPrint ("NBI: Switch local target for %lx, (%d,%d)\n", Connection,
|
||
Connection->LocalTarget.NicHandle.NicId, RemoteAddress->NicHandle.NicId);
|
||
#endif
|
||
Connection->LocalTarget = *RemoteAddress;
|
||
}
|
||
|
||
#else
|
||
|
||
if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) {
|
||
#if DBG
|
||
DbgPrint ("NBI: Switch local target for %lx\n", Connection);
|
||
#endif
|
||
Connection->LocalTarget = *RemoteAddress;
|
||
}
|
||
|
||
#endif _PNP_POWER
|
||
AckType = NbiAckResponse;
|
||
|
||
} else {
|
||
|
||
AckType = NbiAckResend;
|
||
}
|
||
|
||
//
|
||
// This frees the lock.
|
||
//
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
AckType
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n",
|
||
Connection, Connection->ReceiveState,
|
||
Sess->SendSequence, Sess->Offset,
|
||
Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset));
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Old netbios.
|
||
//
|
||
|
||
if ((Sess->SendSequence != Connection->ReceiveSequence) ||
|
||
(Sess->Offset != Connection->CurrentReceive.MessageOffset)) {
|
||
|
||
++Connection->ConnectionInfo.ReceiveErrors;
|
||
++Device->Statistics.DataFramesRejected;
|
||
ADD_TO_LARGE_INTEGER(
|
||
&Device->Statistics.DataFrameBytesRejected,
|
||
PacketSize - sizeof(NB_CONNECTION));
|
||
|
||
if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) ||
|
||
(Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) {
|
||
|
||
NB_ACK_TYPE AckType;
|
||
|
||
NB_DEBUG2 (RECEIVE, ("Got unexp on %lx, %x(%d) expect %x(%d)\n",
|
||
Connection, Sess->SendSequence, Sess->Offset,
|
||
Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset));
|
||
|
||
//
|
||
// If we are receiving the last packet again, just
|
||
// send a normal ack, otherwise force a resend.
|
||
//
|
||
|
||
if (((Sess->SendSequence == Connection->ReceiveSequence) &&
|
||
((ULONG)(Sess->Offset + Sess->DataLength) == Connection->CurrentReceive.MessageOffset)) ||
|
||
(Sess->SendSequence == (USHORT)(Connection->ReceiveSequence-1))) {
|
||
AckType = NbiAckResponse;
|
||
} else {
|
||
AckType = NbiAckResend;
|
||
}
|
||
|
||
//
|
||
// This frees the lock.
|
||
//
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
AckType
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n",
|
||
Connection, Connection->ReceiveState,
|
||
Sess->SendSequence, Sess->Offset,
|
||
Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset));
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
DataAvailable = PacketSize - sizeof(NB_CONNECTION);
|
||
DataIndicated = LookaheadBufferSize - sizeof(NB_CONNECTION);
|
||
DataHeader = LookaheadBuffer + sizeof(NB_CONNECTION);
|
||
|
||
++Device->TempFramesReceived;
|
||
Device->TempFrameBytesReceived += DataAvailable;
|
||
|
||
if (Connection->CurrentIndicateOffset) {
|
||
CTEAssert (DataAvailable >= Connection->CurrentIndicateOffset);
|
||
DataAvailable -= Connection->CurrentIndicateOffset;
|
||
if (DataIndicated >= Connection->CurrentIndicateOffset) {
|
||
DataIndicated -= Connection->CurrentIndicateOffset;
|
||
} else {
|
||
DataIndicated = 0;
|
||
}
|
||
DataHeader += Connection->CurrentIndicateOffset;
|
||
}
|
||
|
||
CopyLookahead = (BOOLEAN)(MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA);
|
||
|
||
if (Connection->NewNetbios) {
|
||
Last = (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_EOM) != 0);
|
||
} else {
|
||
Last = (BOOLEAN)(Sess->Offset + Sess->DataLength == Sess->TotalDataLength);
|
||
}
|
||
|
||
Connection->CurrentReceiveNoPiggyback =
|
||
(BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) != 0);
|
||
|
||
if (Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) {
|
||
|
||
//
|
||
// We don't have a receive posted, so see if we can
|
||
// get one from the queue or our client.
|
||
//
|
||
|
||
if (Connection->ReceiveQueue.Head != NULL) {
|
||
|
||
PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters;
|
||
|
||
Request = Connection->ReceiveQueue.Head;
|
||
Connection->ReceiveQueue.Head = REQUEST_SINGLE_LINKAGE(Request);
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE;
|
||
|
||
Connection->ReceiveRequest = Request;
|
||
ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE)
|
||
(REQUEST_PARAMETERS(Request));
|
||
Connection->ReceiveLength = ReceiveParameters->ReceiveLength;
|
||
|
||
//
|
||
// If there is a send in progress, then we assume
|
||
// we are not in straight request-response mode
|
||
// and disable piggybacking of this ack.
|
||
//
|
||
|
||
if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) {
|
||
Connection->NoPiggybackHeuristic = TRUE;
|
||
} else {
|
||
Connection->NoPiggybackHeuristic = (BOOLEAN)
|
||
((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0);
|
||
}
|
||
|
||
Connection->CurrentReceive.Offset = 0;
|
||
Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request);
|
||
Connection->CurrentReceive.BufferOffset = 0;
|
||
|
||
NB_DEBUG2 (RECEIVE, ("Activated receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence));
|
||
|
||
//
|
||
// Fall through the if and process the data.
|
||
//
|
||
|
||
} else {
|
||
|
||
if ((Connection->ReceiveUnaccepted == 0) &&
|
||
(Connection->AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE])) {
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_INDICATE;
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL;
|
||
if (Last) {
|
||
ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE;
|
||
}
|
||
if (CopyLookahead) {
|
||
ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD;
|
||
}
|
||
|
||
Status = (*Connection->AddressFile->ReceiveHandler)(
|
||
Connection->AddressFile->HandlerContexts[TDI_EVENT_RECEIVE],
|
||
Connection->Context,
|
||
ReceiveFlags,
|
||
DataIndicated,
|
||
DataAvailable,
|
||
&IndicateBytesTransferred,
|
||
DataHeader,
|
||
&ReceiveIrp);
|
||
|
||
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
|
||
//
|
||
// We got an IRP, activate it.
|
||
//
|
||
|
||
Request = NbiAllocateRequest (Device, ReceiveIrp);
|
||
|
||
IF_NOT_ALLOCATED(Request) {
|
||
|
||
ReceiveIrp->IoStatus.Information = 0;
|
||
ReceiveIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
IoCompleteRequest (ReceiveIrp, IO_NETWORK_INCREMENT);
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV;
|
||
|
||
if (Connection->NewNetbios) {
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
Connection->LocalRcvSequenceMax =
|
||
(USHORT)(Connection->ReceiveSequence - 1);
|
||
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
NbiAckResponse
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
|
||
CTEAssert (REQUEST_OPEN_CONTEXT(Request) == Connection);
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters;
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE;
|
||
Connection->ReceiveUnaccepted = DataAvailable - IndicateBytesTransferred;
|
||
|
||
Connection->ReceiveRequest = Request;
|
||
ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE)
|
||
(REQUEST_PARAMETERS(Request));
|
||
Connection->ReceiveLength = ReceiveParameters->ReceiveLength;
|
||
|
||
//
|
||
// If there is a send in progress, then we assume
|
||
// we are not in straight request-response mode
|
||
// and disable piggybacking of this ack.
|
||
//
|
||
|
||
if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) {
|
||
Connection->NoPiggybackHeuristic = TRUE;
|
||
} else {
|
||
Connection->NoPiggybackHeuristic = (BOOLEAN)
|
||
((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0);
|
||
}
|
||
|
||
Connection->CurrentReceive.Offset = 0;
|
||
Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request);
|
||
Connection->CurrentReceive.BufferOffset = 0;
|
||
|
||
NbiReferenceConnectionSync (Connection, CREF_RECEIVE);
|
||
|
||
NB_DEBUG2 (RECEIVE, ("Indicate got receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence));
|
||
|
||
//
|
||
// Fall through the if and process the data.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// The connection has been stopped.
|
||
//
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
}
|
||
|
||
} else if (Status == STATUS_SUCCESS) {
|
||
|
||
//
|
||
// He accepted some or all of the data.
|
||
//
|
||
|
||
NB_DEBUG2 (RECEIVE, ("Indicate took receive data %lx (%d)\n", Connection, Connection->ReceiveSequence));
|
||
|
||
if ( (IndicateBytesTransferred >= DataAvailable)) {
|
||
|
||
CTEAssert (IndicateBytesTransferred == DataAvailable);
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
++Connection->ReceiveSequence;
|
||
++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE
|
||
Connection->CurrentIndicateOffset = 0;
|
||
if ( Last ) {
|
||
Connection->CurrentReceive.MessageOffset = 0;
|
||
} else {
|
||
Connection->CurrentReceive.MessageOffset+= IndicateBytesTransferred;
|
||
}
|
||
|
||
|
||
++Connection->ConnectionInfo.ReceivedTsdus;
|
||
|
||
//
|
||
// If there is a send in progress, then we assume
|
||
// we are not in straight request-response mode
|
||
// and disable piggybacking of this ack.
|
||
//
|
||
|
||
Connection->NoPiggybackHeuristic = (BOOLEAN)
|
||
(Connection->SubState != CONNECTION_SUBSTATE_A_IDLE);
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_IDLE;
|
||
Connection->ReceiveRequest = NULL;
|
||
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiAcknowledgeReceive(
|
||
Connection
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We will do the easiest thing here, which
|
||
// is to send an ack for the amount he
|
||
// took, and force a retransmit on the
|
||
// remote. For net netbios we make a note
|
||
// of how many bytes were taken and ask
|
||
// for a resend.
|
||
//
|
||
|
||
#if DBG
|
||
DbgPrint ("NBI: Client took partial indicate data\n");
|
||
#endif
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
Connection->CurrentReceive.MessageOffset +=
|
||
IndicateBytesTransferred;
|
||
Connection->ReceiveUnaccepted =
|
||
DataAvailable - IndicateBytesTransferred;
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV;
|
||
|
||
if (Connection->NewNetbios) {
|
||
Connection->CurrentIndicateOffset = IndicateBytesTransferred;
|
||
//
|
||
// NOTE: We don't advance ReceiveSequence
|
||
//
|
||
}
|
||
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
Connection->NewNetbios ?
|
||
NbiAckResend : NbiAckResponse
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
} else {
|
||
|
||
//
|
||
// No IRP returned.
|
||
//
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
Connection->ReceiveUnaccepted = DataAvailable;
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV;
|
||
NB_DEBUG (RECEIVE, ("Indicate got no receive on %lx (%lx)\n", Connection, Status));
|
||
|
||
if (Connection->NewNetbios) {
|
||
|
||
Connection->LocalRcvSequenceMax =
|
||
(USHORT)(Connection->ReceiveSequence - 1);
|
||
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
NbiAckResponse
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// No receive handler.
|
||
//
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV;
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
if (Connection->ReceiveUnaccepted == 0) {
|
||
NB_DEBUG (RECEIVE, ("No receive, no handler on %lx\n", Connection));
|
||
} else {
|
||
NB_DEBUG (RECEIVE, ("No receive, ReceiveUnaccepted %d on %lx\n",
|
||
Connection->ReceiveUnaccepted, Connection));
|
||
}
|
||
|
||
if (Connection->NewNetbios) {
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
Connection->LocalRcvSequenceMax =
|
||
(USHORT)(Connection->ReceiveSequence - 1);
|
||
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
NbiAckResponse
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else if (Connection->ReceiveState != CONNECTION_RECEIVE_ACTIVE) {
|
||
|
||
//
|
||
// If we have a transfer in progress, or are waiting for
|
||
// a receive to be posted, then ignore this frame.
|
||
//
|
||
|
||
NB_DEBUG2 (RECEIVE, ("Got data on %lx, state %d (%d)\n", Connection, Connection->ReceiveState, Connection->ReceiveSequence));
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
else if (Connection->ReceiveUnaccepted)
|
||
{
|
||
//
|
||
// Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE
|
||
// &&
|
||
// Connection->ReceiveUnaccepted
|
||
//
|
||
Connection->ReceiveUnaccepted += DataAvailable;
|
||
}
|
||
|
||
//
|
||
// At this point we have a receive and it is set to
|
||
// the correct current location.
|
||
//
|
||
DestBytes = Connection->ReceiveLength - Connection->CurrentReceive.Offset;
|
||
BytesToTransfer = DataAvailable - IndicateBytesTransferred;
|
||
|
||
if (DestBytes < BytesToTransfer) {
|
||
|
||
//
|
||
// If the data overflows the current receive, then make a
|
||
// note that we should complete the receive at the end of
|
||
// transfer data, but with EOR false.
|
||
//
|
||
|
||
EndOfMessage = FALSE;
|
||
CompleteReceive = TRUE;
|
||
PartialReceive = TRUE;
|
||
BytesToTransfer = DestBytes;
|
||
|
||
} else if (DestBytes == BytesToTransfer) {
|
||
|
||
//
|
||
// If the data just fills the current receive, then complete
|
||
// the receive; EOR depends on whether this is a DOL or not.
|
||
//
|
||
|
||
EndOfMessage = Last;
|
||
CompleteReceive = TRUE;
|
||
PartialReceive = FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Complete the receive if this is a DOL.
|
||
//
|
||
|
||
EndOfMessage = Last;
|
||
CompleteReceive = Last;
|
||
PartialReceive = FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// If we can copy the data directly, then update our
|
||
// pointers, send an ack, and do the copy.
|
||
//
|
||
|
||
if ((BytesToTransfer > 0) &&
|
||
(IndicateBytesTransferred + BytesToTransfer <= DataIndicated)) {
|
||
|
||
ULONG BytesNow, BytesLeft;
|
||
PUCHAR CurTarget = NULL, CurSource;
|
||
ULONG CurTargetLen;
|
||
PNDIS_BUFFER CurBuffer;
|
||
ULONG CurByteOffset;
|
||
|
||
NB_DEBUG2 (RECEIVE, ("Direct copy of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence));
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER;
|
||
|
||
CurBuffer = Connection->CurrentReceive.Buffer;
|
||
CurByteOffset = Connection->CurrentReceive.BufferOffset;
|
||
CurSource = DataHeader + IndicateBytesTransferred;
|
||
BytesLeft = BytesToTransfer;
|
||
|
||
NdisQueryBufferSafe (CurBuffer, &CurTarget, &CurTargetLen, HighPagePriority);
|
||
if (CurTarget)
|
||
{
|
||
CurTarget += CurByteOffset;
|
||
CurTargetLen -= CurByteOffset;
|
||
}
|
||
|
||
while (CurTarget) {
|
||
|
||
if (CurTargetLen < BytesLeft) {
|
||
BytesNow = CurTargetLen;
|
||
} else {
|
||
BytesNow = BytesLeft;
|
||
}
|
||
TdiCopyLookaheadData(
|
||
CurTarget,
|
||
CurSource,
|
||
BytesNow,
|
||
CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0);
|
||
|
||
if (BytesNow == CurTargetLen) {
|
||
BytesLeft -= BytesNow;
|
||
CurBuffer = CurBuffer->Next;
|
||
CurByteOffset = 0;
|
||
if (BytesLeft > 0) {
|
||
NdisQueryBufferSafe (CurBuffer, &CurTarget, &CurTargetLen, HighPagePriority);
|
||
CurSource += BytesNow;
|
||
} else {
|
||
break;
|
||
}
|
||
} else {
|
||
CurByteOffset += BytesNow;
|
||
CTEAssert (BytesLeft == BytesNow);
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
Connection->CurrentReceive.Buffer = CurBuffer;
|
||
Connection->CurrentReceive.BufferOffset = CurByteOffset;
|
||
|
||
Connection->CurrentReceive.Offset += BytesToTransfer;
|
||
Connection->CurrentReceive.MessageOffset += BytesToTransfer;
|
||
|
||
//
|
||
// Release and re-acquire the lock, but this time with the
|
||
// Cancel lock
|
||
//
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NB_GET_CANCEL_LOCK( &CancelLH );
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (CompleteReceive ||
|
||
(Connection->State != CONNECTION_STATE_ACTIVE)) {
|
||
|
||
if (EndOfMessage) {
|
||
|
||
CTEAssert (!PartialReceive);
|
||
|
||
++Connection->ReceiveSequence;
|
||
++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE
|
||
Connection->CurrentReceive.MessageOffset = 0;
|
||
Connection->CurrentIndicateOffset = 0;
|
||
|
||
} else if (Connection->NewNetbios) {
|
||
|
||
if (PartialReceive) {
|
||
Connection->CurrentIndicateOffset += BytesToTransfer;
|
||
} else {
|
||
++Connection->ReceiveSequence;
|
||
++Connection->LocalRcvSequenceMax;
|
||
Connection->CurrentIndicateOffset = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// This sends an ack and releases the connection lock.
|
||
// and CANCEL Lock.
|
||
//
|
||
|
||
NbiCompleteReceive(
|
||
Connection,
|
||
EndOfMessage,
|
||
CancelLH
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_SWAP_IRQL( CancelLH, LockHandle);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
//
|
||
// CompleteReceive is FALSE, so EndOfMessage is FALSE.
|
||
//
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE;
|
||
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
if (Connection->NewNetbios) {
|
||
|
||
//
|
||
// A partial receive should only happen if we are
|
||
// completing the receive.
|
||
//
|
||
|
||
CTEAssert (!PartialReceive);
|
||
|
||
++Connection->ReceiveSequence;
|
||
++Connection->LocalRcvSequenceMax;
|
||
Connection->CurrentIndicateOffset = 0;
|
||
|
||
if ((Connection->CurrentReceiveNoPiggyback) ||
|
||
((Device->AckWindow != 0) &&
|
||
(++Connection->ReceivesWithoutAck >= Device->AckWindow))) {
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
NbiAckResponse
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
NbiAckResponse
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// We have to set up a call to transfer data and send
|
||
// the ack after it completes (if it succeeds).
|
||
//
|
||
|
||
s = NbiPopReceivePacket (Device);
|
||
if (s == NULL) {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
++Connection->ConnectionInfo.ReceiveErrors;
|
||
++Device->Statistics.DataFramesRejected;
|
||
ADD_TO_LARGE_INTEGER(
|
||
&Device->Statistics.DataFrameBytesRejected,
|
||
DataAvailable);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
}
|
||
|
||
ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage);
|
||
Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]);
|
||
|
||
//
|
||
// Initialize the receive packet.
|
||
//
|
||
|
||
ReceiveReserved->u.RR_CO.Connection = Connection;
|
||
ReceiveReserved->u.RR_CO.EndOfMessage = EndOfMessage;
|
||
ReceiveReserved->u.RR_CO.CompleteReceive = CompleteReceive;
|
||
ReceiveReserved->u.RR_CO.PartialReceive = PartialReceive;
|
||
|
||
ReceiveReserved->Type = RECEIVE_TYPE_DATA;
|
||
CTEAssert (!ReceiveReserved->TransferInProgress);
|
||
ReceiveReserved->TransferInProgress = TRUE;
|
||
|
||
//
|
||
// if we've got zero bytes left, avoid the TransferData below and
|
||
// just deliver.
|
||
//
|
||
|
||
if (BytesToTransfer <= 0) {
|
||
|
||
ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE;
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NB_DEBUG2 (RECEIVE, ("TransferData of 0 bytes %lx (%d)\n", Connection, Connection->ReceiveSequence));
|
||
NbiTransferDataComplete(
|
||
Packet,
|
||
NDIS_STATUS_SUCCESS,
|
||
0);
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If needed, build a buffer chain to describe this
|
||
// to NDIS.
|
||
//
|
||
|
||
Connection->PreviousReceive = Connection->CurrentReceive;
|
||
|
||
if ((Connection->CurrentReceive.Offset == 0) &&
|
||
CompleteReceive) {
|
||
|
||
BufferChain = Connection->CurrentReceive.Buffer;
|
||
BufferChainLength = BytesToTransfer;
|
||
Connection->CurrentReceive.Buffer = NULL;
|
||
ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE;
|
||
|
||
} else {
|
||
|
||
if (NbiBuildBufferChainFromBufferChain (
|
||
Device->NdisBufferPoolHandle,
|
||
Connection->CurrentReceive.Buffer,
|
||
Connection->CurrentReceive.BufferOffset,
|
||
BytesToTransfer,
|
||
&BufferChain,
|
||
&Connection->CurrentReceive.Buffer,
|
||
&Connection->CurrentReceive.BufferOffset,
|
||
&BufferChainLength) != NDIS_STATUS_SUCCESS) {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NB_DEBUG2 (RECEIVE, ("Could not build receive buffer chain %lx (%d)\n", Connection, Connection->ReceiveSequence));
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
return;
|
||
|
||
}
|
||
|
||
ReceiveReserved->u.RR_CO.NoNdisBuffer = FALSE;
|
||
|
||
}
|
||
|
||
|
||
NdisChainBufferAtFront (Packet, BufferChain);
|
||
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER;
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
NB_DEBUG2 (RECEIVE, ("TransferData of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence));
|
||
|
||
(*Device->Bind.TransferDataHandler) (
|
||
&NdisStatus,
|
||
MacBindingHandle,
|
||
MacReceiveContext,
|
||
LookaheadBufferOffset + sizeof(NB_CONNECTION) +
|
||
Connection->CurrentIndicateOffset + IndicateBytesTransferred,
|
||
BytesToTransfer,
|
||
Packet,
|
||
(PUINT)&NdisBytesTransferred);
|
||
|
||
if (NdisStatus != NDIS_STATUS_PENDING) {
|
||
#if DBG
|
||
if (NdisStatus == STATUS_SUCCESS) {
|
||
CTEAssert (NdisBytesTransferred == BytesToTransfer);
|
||
}
|
||
#endif
|
||
|
||
NbiTransferDataComplete (
|
||
Packet,
|
||
NdisStatus,
|
||
NdisBytesTransferred);
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
} else if ((Connection->State == CONNECTION_STATE_CONNECTING) &&
|
||
(Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) {
|
||
|
||
//
|
||
// If this is the ack for the session initialize, then
|
||
// complete the pending connects. This routine releases
|
||
// the connection lock.
|
||
//
|
||
|
||
NbiProcessSessionInitAck(
|
||
Connection,
|
||
Sess
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a session initialize frame.
|
||
//
|
||
// If there is more data than in the lookahead
|
||
// buffer, we won't be able to echo it back in the
|
||
// response.
|
||
//
|
||
|
||
NbiProcessSessionInitialize(
|
||
RemoteAddress,
|
||
MacOptions,
|
||
LookaheadBuffer,
|
||
LookaheadBufferSize);
|
||
|
||
}
|
||
|
||
} /* NbiProcessSessionData */
|
||
|
||
|
||
VOID
|
||
NbiProcessDataAck(
|
||
IN PCONNECTION Connection,
|
||
IN NB_SESSION UNALIGNED * Sess,
|
||
IN PIPX_LOCAL_TARGET RemoteAddress
|
||
NB_LOCK_HANDLE_PARAM(LockHandle)
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes an ack on an active connection.
|
||
|
||
NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD
|
||
AND RETURNS WITH IT RELEASED.
|
||
|
||
Arguments:
|
||
|
||
Connection - The connection.
|
||
|
||
Sess - The session frame.
|
||
|
||
RemoteAddress - The local target this packet was received from.
|
||
|
||
LockHandle - The handle used to acquire the lock.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Resend;
|
||
|
||
//
|
||
// Make sure we expect an ack right now.
|
||
//
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
if (((Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) ||
|
||
(Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W)) &&
|
||
((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) {
|
||
|
||
//
|
||
// We are waiting for an ack (because we completed
|
||
// packetizing a send, or ran out of receive window).
|
||
//
|
||
// This will complete any sends that are acked by
|
||
// this receive, and if necessary readjust the
|
||
// send pointer and requeue the connection for
|
||
// packetizing. It release the connection lock.
|
||
//
|
||
|
||
if (Connection->ResponseTimeout) {
|
||
Resend = TRUE;
|
||
Connection->ResponseTimeout = FALSE;
|
||
} else {
|
||
Resend = (BOOLEAN)
|
||
((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0);
|
||
}
|
||
|
||
NbiReframeConnection(
|
||
Connection,
|
||
Sess->ReceiveSequence,
|
||
Sess->BytesReceived,
|
||
Resend
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) &&
|
||
((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) {
|
||
|
||
//
|
||
// We had a probe outstanding and got a response. Restart
|
||
// the connection if needed (a send may have just been
|
||
// posted while the probe was outstanding).
|
||
//
|
||
// We should check that the response is really correct.
|
||
//
|
||
|
||
if (Connection->NewNetbios) {
|
||
Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax;
|
||
}
|
||
|
||
NbiRestartConnection (Connection);
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
} else if ((Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE) &&
|
||
((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) {
|
||
|
||
if (Connection->NewNetbios) {
|
||
|
||
//
|
||
// We are packetizing, reframe. In the unlikely
|
||
// event that this acks everything we may packetize
|
||
// in this call, but that is OK (the other thread
|
||
// will exit if we finish up). More normally we
|
||
// will just advance UnAcked send a bit.
|
||
//
|
||
|
||
NbiReframeConnection(
|
||
Connection,
|
||
Sess->ReceiveSequence,
|
||
Sess->BytesReceived,
|
||
(BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0)
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
}
|
||
|
||
#if 0
|
||
|
||
//
|
||
// Should handle this case (i.e. may be in W_PACKET).
|
||
//
|
||
|
||
} else if ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0) {
|
||
|
||
DbgPrint ("NWLNKNB: Ignoring ack, state is %d\n", Connection->SubState);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
#endif
|
||
|
||
} else {
|
||
|
||
//
|
||
// We got a probe from the remote. Some old DOS clients
|
||
// send probes that do not have the send ack bit on,
|
||
// so we respond to any probe if none of the conditions
|
||
// above are true. This call releases the lock.
|
||
//
|
||
// We use the IgnoreNextDosProbe flag to ignore every
|
||
// second probe of this nature, to avoid a data ack
|
||
// war between two machines who each think they are
|
||
// responding to the other. This flag is set to FALSE
|
||
// whenever we send an ack or a probe.
|
||
//
|
||
|
||
if (!Connection->IgnoreNextDosProbe) {
|
||
|
||
//
|
||
// Since this is a probe, check if the local
|
||
// target has changed.
|
||
//
|
||
|
||
if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) {
|
||
#if DBG
|
||
DbgPrint ("NBI: Switch local target for %lx\n", Connection);
|
||
#endif
|
||
Connection->LocalTarget = *RemoteAddress;
|
||
}
|
||
|
||
NbiSendDataAck(
|
||
Connection,
|
||
NbiAckResponse
|
||
NB_LOCK_HANDLE_ARG(LockHandle));
|
||
Connection->IgnoreNextDosProbe = TRUE;
|
||
|
||
} else {
|
||
|
||
Connection->IgnoreNextDosProbe = FALSE;
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
return;
|
||
|
||
}
|
||
|
||
} /* NbiProcessDataAck */
|
||
|
||
|
||
VOID
|
||
NbiProcessSessionInitialize(
|
||
IN PIPX_LOCAL_TARGET RemoteAddress,
|
||
IN ULONG MacOptions,
|
||
IN PUCHAR PacketBuffer,
|
||
IN UINT PacketSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles NB_CMD_SESSION frames which have
|
||
a remote connection ID of 0xffff -- these are session
|
||
initialize frames.
|
||
|
||
Arguments:
|
||
|
||
RemoteAddress - The local target this packet was received from.
|
||
|
||
MacOptions - The MAC options for the underlying NDIS binding.
|
||
|
||
PacketBuffer - The packet data, starting at the IPX
|
||
header.
|
||
|
||
PacketSize - The total length of the packet, starting at the
|
||
IPX header.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer;
|
||
NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session);
|
||
NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1);
|
||
CONNECT_INDICATION TempConnInd;
|
||
PCONNECT_INDICATION ConnInd;
|
||
PCONNECTION Connection;
|
||
PADDRESS Address;
|
||
PREQUEST Request, ListenRequest, AcceptRequest;
|
||
PDEVICE Device = NbiDevice;
|
||
PLIST_ENTRY p;
|
||
ULONG Hash;
|
||
TA_NETBIOS_ADDRESS SourceName;
|
||
PIRP AcceptIrp;
|
||
CONNECTION_CONTEXT ConnectionContext;
|
||
NTSTATUS AcceptStatus;
|
||
PADDRESS_FILE AddressFile, ReferencedAddressFile;
|
||
PTDI_REQUEST_KERNEL_LISTEN ListenParameters;
|
||
PTDI_CONNECTION_INFORMATION ListenInformation;
|
||
PTDI_CONNECTION_INFORMATION RemoteInformation;
|
||
TDI_ADDRESS_NETBIOS * ListenAddress;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle1)
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle2)
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle3)
|
||
CTELockHandle CancelLH;
|
||
|
||
//
|
||
// Verify that the whole packet is there.
|
||
//
|
||
|
||
if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT))) {
|
||
#if DBG
|
||
DbgPrint ("NBI: Got short session initialize, %d/%d\n", PacketSize,
|
||
sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT));
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Verify that MaximumDataSize that remote can support is > 0
|
||
// Bug # 19405
|
||
//
|
||
if ( SessInit->MaximumDataSize == 0 ) {
|
||
NB_DEBUG(CONNECTION, ("Connect request with MaximumDataSize == 0\n"
|
||
));
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Make sure this is for an address we care about.
|
||
//
|
||
|
||
if (Device->AddressCounts[SessInit->DestinationName[0]] == 0) {
|
||
return;
|
||
}
|
||
|
||
Address = NbiFindAddress (Device, (PUCHAR)SessInit->DestinationName);
|
||
|
||
if (Address == NULL) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// First see if we have a session to this remote. We check
|
||
// this in case our ack of the session initialize was dropped,
|
||
// we don't want to reindicate our client.
|
||
//
|
||
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3);
|
||
|
||
for (Hash = 0; Hash < CONNECTION_HASH_COUNT; Hash++) {
|
||
|
||
Connection = Device->ConnectionHash[Hash].Connections;
|
||
|
||
while (Connection != NULL) {
|
||
|
||
if ((RtlEqualMemory (&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12)) &&
|
||
(Connection->RemoteConnectionId == Sess->SourceConnectionId) &&
|
||
(Connection->State != CONNECTION_STATE_DISCONNECT)) {
|
||
|
||
//
|
||
// Yes, we are talking to this remote, if it is active then
|
||
// respond, otherwise we are in the process of connecting
|
||
// and we will respond eventually.
|
||
//
|
||
|
||
#if DBG
|
||
DbgPrint ("NBI: Got connect request on active connection %lx\n", Connection);
|
||
#endif
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_INDICATE);
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
|
||
NbiSendSessionInitAck(
|
||
Connection,
|
||
(PUCHAR)(SessInit+1),
|
||
PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)),
|
||
NULL); // lock is not held
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
|
||
}
|
||
|
||
NbiDereferenceAddress (Address, AREF_FIND);
|
||
return;
|
||
}
|
||
|
||
Connection = Connection->NextConnection;
|
||
}
|
||
}
|
||
|
||
|
||
TdiBuildNetbiosAddress ((PUCHAR)SessInit->SourceName, FALSE, &SourceName);
|
||
|
||
//
|
||
// Scan the queue of listens to see if there is one that
|
||
// satisfies this request.
|
||
//
|
||
// NOTE: The device lock is held here.
|
||
//
|
||
|
||
for (p = Device->ListenQueue.Flink;
|
||
p != &Device->ListenQueue;
|
||
p = p->Flink) {
|
||
|
||
Request = LIST_ENTRY_TO_REQUEST (p);
|
||
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
if (Connection->AddressFile->Address != Address) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Check that this listen is not specific to a different
|
||
// netbios name.
|
||
//
|
||
|
||
ListenParameters = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(Request);
|
||
ListenInformation = ListenParameters->RequestConnectionInformation;
|
||
|
||
if (ListenInformation &&
|
||
(ListenInformation->RemoteAddress) &&
|
||
(ListenAddress = NbiParseTdiAddress(ListenInformation->RemoteAddress, ListenInformation->RemoteAddressLength, FALSE)) &&
|
||
(!RtlEqualMemory(
|
||
SessInit->SourceName,
|
||
ListenAddress->NetbiosName,
|
||
16))) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// This connection is valid, so we use it.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Activating queued listen %lx\n", Connection));
|
||
|
||
RemoveEntryList (REQUEST_LINKAGE(Request));
|
||
|
||
RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12);
|
||
RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16);
|
||
Connection->LocalTarget = *RemoteAddress;
|
||
Connection->RemoteConnectionId = Sess->SourceConnectionId;
|
||
|
||
Connection->SessionInitAckDataLength =
|
||
PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT));
|
||
if (Connection->SessionInitAckDataLength > 0) {
|
||
if (Connection->SessionInitAckData = NbiAllocateMemory (Connection->SessionInitAckDataLength,
|
||
MEMORY_CONNECTION, "SessionInitAckData"))
|
||
{
|
||
RtlCopyMemory (Connection->SessionInitAckData,
|
||
(PUCHAR)(SessInit+1),
|
||
Connection->SessionInitAckDataLength);
|
||
}
|
||
else
|
||
{
|
||
Connection->SessionInitAckDataLength = 0;
|
||
}
|
||
}
|
||
|
||
|
||
Connection->MaximumPacketSize = SessInit->MaximumDataSize;
|
||
|
||
Connection->CurrentSend.SendSequence = 0;
|
||
Connection->UnAckedSend.SendSequence = 0;
|
||
Connection->RetransmitThisWindow = FALSE;
|
||
Connection->ReceiveSequence = 1;
|
||
Connection->CurrentReceive.MessageOffset = 0;
|
||
Connection->Retries = Device->KeepAliveCount;
|
||
if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) {
|
||
Connection->NewNetbios = TRUE;
|
||
Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card
|
||
Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax;
|
||
Connection->SendWindowSequenceLimit = 2;
|
||
if (Connection->RemoteRcvSequenceMax == 0) {
|
||
Connection->RemoteRcvSequenceMax = 1;
|
||
}
|
||
} else {
|
||
Connection->NewNetbios = FALSE;
|
||
}
|
||
|
||
//
|
||
// Save this information now for whenever we complete the listen.
|
||
//
|
||
|
||
RemoteInformation = ListenParameters->ReturnConnectionInformation;
|
||
|
||
if (RemoteInformation != NULL) {
|
||
|
||
RtlCopyMemory(
|
||
(PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress,
|
||
&SourceName,
|
||
(RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ?
|
||
RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS));
|
||
}
|
||
|
||
|
||
if (ListenParameters->RequestFlags & TDI_QUERY_ACCEPT) {
|
||
|
||
//
|
||
// We have to wait for an accept before sending the
|
||
// session init ack, so we complete the listen and wait.
|
||
//
|
||
|
||
ListenRequest = Request;
|
||
Connection->ListenRequest = NULL;
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Queued listen on %lx awaiting accept\n", Connection));
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_L_W_ACCEPT;
|
||
|
||
NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_W_ACCEPT);
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
|
||
} else {
|
||
|
||
//
|
||
// We are ready to go, so we send out the find route request
|
||
// for the remote. We keep the listen alive and the CREF_LISTEN
|
||
// reference on until this completes.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Activating queued listen on %lx\n", Connection));
|
||
|
||
ListenRequest = NULL;
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE;
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE);
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
|
||
*(UNALIGNED ULONG *)Connection->FindRouteRequest.Network =
|
||
*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork;
|
||
RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,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.
|
||
//
|
||
|
||
if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) {
|
||
|
||
(*Device->Bind.FindRouteHandler)(
|
||
&Connection->FindRouteRequest);
|
||
|
||
} else {
|
||
|
||
NbiFindRouteComplete(
|
||
&Connection->FindRouteRequest,
|
||
FALSE);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Complete the listen if needed.
|
||
//
|
||
|
||
if (ListenRequest != NULL) {
|
||
|
||
REQUEST_INFORMATION (ListenRequest) = 0;
|
||
REQUEST_STATUS (ListenRequest) = STATUS_SUCCESS;
|
||
|
||
NB_GET_CANCEL_LOCK ( &CancelLH );
|
||
IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL);
|
||
NB_FREE_CANCEL_LOCK( CancelLH );
|
||
|
||
NbiCompleteRequest (ListenRequest);
|
||
NbiFreeRequest (Device, ListenRequest);
|
||
|
||
}
|
||
|
||
NbiDereferenceAddress (Address, AREF_FIND);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// We could not find a listen, so we indicate to every
|
||
// client. Make sure there is no session initialize for this
|
||
// remote being indicated. If there is not, we insert
|
||
// ourselves in the queue to block others.
|
||
//
|
||
// NOTE: The device lock is held here.
|
||
//
|
||
|
||
for (p = Device->ConnectIndicationInProgress.Flink;
|
||
p != &Device->ConnectIndicationInProgress;
|
||
p = p->Flink) {
|
||
|
||
ConnInd = CONTAINING_RECORD (p, CONNECT_INDICATION, Linkage);
|
||
|
||
if ((RtlEqualMemory(ConnInd->NetbiosName, SessInit->DestinationName, 16)) &&
|
||
(RtlEqualMemory(&ConnInd->RemoteAddress, Conn->IpxHeader.SourceNetwork, 12)) &&
|
||
(ConnInd->ConnectionId == Sess->SourceConnectionId)) {
|
||
|
||
//
|
||
// We are processing a request from this remote for
|
||
// the same ID, to avoid confusion we just exit.
|
||
//
|
||
|
||
#if DBG
|
||
DbgPrint ("NBI: Already processing connect to <%.16s>\n", SessInit->DestinationName);
|
||
#endif
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
NbiDereferenceAddress (Address, AREF_FIND);
|
||
return;
|
||
}
|
||
|
||
}
|
||
|
||
RtlCopyMemory (TempConnInd.NetbiosName, SessInit->DestinationName, 16);
|
||
RtlCopyMemory (&TempConnInd.RemoteAddress, Conn->IpxHeader.SourceNetwork, 12);
|
||
TempConnInd.ConnectionId = Sess->SourceConnectionId;
|
||
|
||
InsertTailList (&Device->ConnectIndicationInProgress, &TempConnInd.Linkage);
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
|
||
|
||
//
|
||
// Now scan through the address to find someone who has
|
||
// an indication routine registed and wants this connection.
|
||
//
|
||
|
||
|
||
ReferencedAddressFile = NULL;
|
||
|
||
NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1);
|
||
|
||
for (p = Address->AddressFileDatabase.Flink;
|
||
p != &Address->AddressFileDatabase;
|
||
p = p->Flink) {
|
||
|
||
//
|
||
// Find the next open address file in the list.
|
||
//
|
||
|
||
AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage);
|
||
if (AddressFile->State != ADDRESSFILE_STATE_OPEN) {
|
||
continue;
|
||
}
|
||
|
||
NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION);
|
||
|
||
NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1);
|
||
|
||
if (ReferencedAddressFile != NULL) {
|
||
NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION);
|
||
}
|
||
ReferencedAddressFile = AddressFile;
|
||
|
||
//
|
||
// No posted listen requests; is there a kernel client?
|
||
//
|
||
|
||
if (AddressFile->RegisteredHandler[TDI_EVENT_CONNECT]) {
|
||
|
||
if ((*AddressFile->ConnectionHandler)(
|
||
AddressFile->HandlerContexts[TDI_EVENT_CONNECT],
|
||
sizeof (TA_NETBIOS_ADDRESS),
|
||
&SourceName,
|
||
0, // user data
|
||
NULL,
|
||
0, // options
|
||
NULL,
|
||
&ConnectionContext,
|
||
&AcceptIrp) != STATUS_MORE_PROCESSING_REQUIRED) {
|
||
|
||
//
|
||
// The client did not return a request, go to the
|
||
// next address file.
|
||
//
|
||
|
||
NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1);
|
||
continue;
|
||
|
||
}
|
||
|
||
AcceptRequest = NbiAllocateRequest (Device, AcceptIrp);
|
||
|
||
IF_NOT_ALLOCATED(AcceptRequest) {
|
||
|
||
AcceptStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
//
|
||
// The client accepted the connect, so activate
|
||
// the connection and complete the accept.
|
||
// listen. This lookup references the connection
|
||
// so we know it will remain valid.
|
||
//
|
||
|
||
Connection = NbiLookupConnectionByContext (
|
||
AddressFile,
|
||
ConnectionContext);
|
||
|
||
if (Connection != NULL) {
|
||
|
||
ASSERT (Connection->AddressFile == AddressFile);
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle2);
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3);
|
||
|
||
if ((Connection->State == CONNECTION_STATE_INACTIVE) &&
|
||
(Connection->DisassociatePending == NULL) &&
|
||
(Connection->ClosePending == NULL)) {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Indication on %lx returned connection %lx\n", AddressFile, Connection));
|
||
|
||
Connection->State = CONNECTION_STATE_LISTENING;
|
||
Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE;
|
||
|
||
Connection->Retries = Device->KeepAliveCount;
|
||
|
||
RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12);
|
||
RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16);
|
||
Connection->LocalTarget = *RemoteAddress;
|
||
|
||
Connection->SessionInitAckDataLength =
|
||
PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT));
|
||
if (Connection->SessionInitAckDataLength > 0) {
|
||
if (Connection->SessionInitAckData = NbiAllocateMemory(
|
||
Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"))
|
||
{
|
||
RtlCopyMemory (Connection->SessionInitAckData,
|
||
(PUCHAR)(SessInit+1),
|
||
Connection->SessionInitAckDataLength);
|
||
}
|
||
else
|
||
{
|
||
Connection->SessionInitAckDataLength = 0;
|
||
}
|
||
}
|
||
|
||
Connection->MaximumPacketSize = SessInit->MaximumDataSize;
|
||
|
||
(VOID)NbiAssignConnectionId (Device, Connection); // Check return code.
|
||
Connection->RemoteConnectionId = Sess->SourceConnectionId;
|
||
|
||
Connection->CurrentSend.SendSequence = 0;
|
||
Connection->UnAckedSend.SendSequence = 0;
|
||
Connection->RetransmitThisWindow = FALSE;
|
||
Connection->ReceiveSequence = 1;
|
||
Connection->CurrentReceive.MessageOffset = 0;
|
||
Connection->Retries = Device->KeepAliveCount;
|
||
if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) {
|
||
Connection->NewNetbios = TRUE;
|
||
Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card
|
||
Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax;
|
||
Connection->SendWindowSequenceLimit = 2;
|
||
if (Connection->RemoteRcvSequenceMax == 0) {
|
||
Connection->RemoteRcvSequenceMax = 1;
|
||
}
|
||
} else {
|
||
Connection->NewNetbios = FALSE;
|
||
}
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_ACCEPT);
|
||
NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE);
|
||
|
||
Connection->AcceptRequest = AcceptRequest;
|
||
AcceptStatus = STATUS_PENDING;
|
||
|
||
//
|
||
// Take us out of this list now, we will jump to
|
||
// FoundConnection which is past the removal below.
|
||
//
|
||
|
||
RemoveEntryList (&TempConnInd.Linkage);
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2);
|
||
|
||
*(UNALIGNED ULONG *)Connection->FindRouteRequest.Network =
|
||
*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork;
|
||
RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,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 *)Conn->IpxHeader.SourceNetwork != 0) {
|
||
|
||
(*Device->Bind.FindRouteHandler)(
|
||
&Connection->FindRouteRequest);
|
||
|
||
} else {
|
||
|
||
NbiFindRouteComplete(
|
||
&Connection->FindRouteRequest,
|
||
FALSE);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Indication on %lx returned invalid connection %lx\n", AddressFile, Connection));
|
||
AcceptStatus = STATUS_INVALID_CONNECTION;
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2);
|
||
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_BY_CONTEXT);
|
||
|
||
} else {
|
||
|
||
NB_DEBUG (CONNECTION, ("Indication on %lx returned unknown connection %lx\n", AddressFile, Connection));
|
||
AcceptStatus = STATUS_INVALID_CONNECTION;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Complete the accept request in the failure case.
|
||
//
|
||
|
||
if (AcceptStatus != STATUS_PENDING) {
|
||
|
||
REQUEST_STATUS (AcceptRequest) = AcceptStatus;
|
||
|
||
NbiCompleteRequest (AcceptRequest);
|
||
NbiFreeRequest (Device, AcceptRequest);
|
||
|
||
} else {
|
||
|
||
//
|
||
// We found a connection, so we break; this is
|
||
// a jump since the while exit assumes the
|
||
// address lock is held.
|
||
//
|
||
|
||
goto FoundConnection;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1);
|
||
|
||
} // end of for loop through the address files
|
||
|
||
NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1);
|
||
|
||
|
||
//
|
||
// Take us out of the list that blocks other indications
|
||
// from this remote to this address.
|
||
//
|
||
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3);
|
||
RemoveEntryList (&TempConnInd.Linkage);
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3);
|
||
|
||
FoundConnection:
|
||
|
||
if (ReferencedAddressFile != NULL) {
|
||
NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION);
|
||
}
|
||
|
||
NbiDereferenceAddress (Address, AREF_FIND);
|
||
|
||
} /* NbiProcessSessionInitialize */
|
||
|
||
|
||
VOID
|
||
NbiProcessSessionInitAck(
|
||
IN PCONNECTION Connection,
|
||
IN NB_SESSION UNALIGNED * Sess
|
||
IN NB_LOCK_HANDLE_PARAM(LockHandle)
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles session init ack frames.
|
||
|
||
THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD
|
||
AND RETURNS WITH IT RELEASED.
|
||
|
||
Arguments:
|
||
|
||
Connection - The connection.
|
||
|
||
Sess - The netbios header for the received frame.
|
||
|
||
LockHandle - The handle with which Connection->Lock was acquired.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PREQUEST Request;
|
||
NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1);
|
||
BOOLEAN TimerWasStopped = FALSE;
|
||
CTELockHandle CancelLH;
|
||
|
||
if ((Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) &&
|
||
(Sess->SendSequence == 0x0000) &&
|
||
(Sess->ReceiveSequence == 0x0001)) {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Completing connect on %lx\n", Connection));
|
||
|
||
if (CTEStopTimer (&Connection->Timer)) {
|
||
TimerWasStopped = TRUE;
|
||
}
|
||
|
||
Connection->State = CONNECTION_STATE_ACTIVE;
|
||
Connection->SubState = CONNECTION_SUBSTATE_A_IDLE;
|
||
Connection->ReceiveState = CONNECTION_RECEIVE_IDLE;
|
||
|
||
if (Connection->Retries == NbiDevice->ConnectionCount) {
|
||
++NbiDevice->Statistics.ConnectionsAfterNoRetry;
|
||
} else {
|
||
++NbiDevice->Statistics.ConnectionsAfterRetry;
|
||
}
|
||
++NbiDevice->Statistics.OpenConnections;
|
||
|
||
Connection->Retries = NbiDevice->KeepAliveCount;
|
||
NbiStartWatchdog (Connection);
|
||
|
||
Connection->RemoteConnectionId = Sess->SourceConnectionId;
|
||
|
||
Connection->CurrentSend.SendSequence = 1;
|
||
Connection->UnAckedSend.SendSequence = 1;
|
||
Connection->RetransmitThisWindow = FALSE;
|
||
Connection->ReceiveSequence = 0;
|
||
Connection->CurrentReceive.MessageOffset = 0;
|
||
Connection->Retries = NbiDevice->KeepAliveCount;
|
||
if (NbiDevice->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) {
|
||
Connection->NewNetbios = TRUE;
|
||
Connection->LocalRcvSequenceMax =
|
||
(USHORT)(Connection->ReceiveWindowSize - 1);
|
||
Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax;
|
||
Connection->SendWindowSequenceLimit = 3;
|
||
} else {
|
||
Connection->NewNetbios = FALSE;
|
||
}
|
||
|
||
if (Connection->MaximumPacketSize > SessInit->MaximumDataSize) {
|
||
Connection->MaximumPacketSize = SessInit->MaximumDataSize;
|
||
}
|
||
|
||
Request = Connection->ConnectRequest;
|
||
|
||
#ifdef RASAUTODIAL
|
||
//
|
||
// Check to see if we have to notify
|
||
// the automatic connection driver about
|
||
// this connection.
|
||
//
|
||
if (fAcdLoadedG) {
|
||
BOOLEAN fEnabled;
|
||
CTELockHandle AcdHandle;
|
||
|
||
CTEGetLock(&AcdDriverG.SpinLock, &AcdHandle);
|
||
fEnabled = AcdDriverG.fEnabled;
|
||
CTEFreeLock(&AcdDriverG.SpinLock, AcdHandle);
|
||
if (fEnabled)
|
||
NbiNoteNewConnection(Connection);
|
||
}
|
||
#endif // RASAUTODIAL
|
||
|
||
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_SUCCESS;
|
||
NbiCompleteRequest (Request);
|
||
NbiFreeRequest (Device, Request);
|
||
|
||
NbiTransferReferenceConnection (Connection, CREF_CONNECT, CREF_ACTIVE);
|
||
|
||
if (TimerWasStopped) {
|
||
NbiDereferenceConnection (Connection, CREF_TIMER);
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
} /* NbiProcessSessionInitAck */
|
||
|
||
|
||
VOID
|
||
NbiProcessSessionEnd(
|
||
IN PIPX_LOCAL_TARGET RemoteAddress,
|
||
IN ULONG MacOptions,
|
||
IN PUCHAR PacketBuffer,
|
||
IN UINT PacketSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles NB_CMD_SESSION_END frames.
|
||
|
||
Arguments:
|
||
|
||
RemoteAddress - The local target this packet was received from.
|
||
|
||
MacOptions - The MAC options for the underlying NDIS binding.
|
||
|
||
LookaheadBuffer - The packet data, starting at the IPX
|
||
header.
|
||
|
||
PacketSize - The total length of the packet, starting at the
|
||
IPX header.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer;
|
||
NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session);
|
||
PCONNECTION Connection;
|
||
PDEVICE Device = NbiDevice;
|
||
ULONG Hash;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle1)
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle2)
|
||
|
||
//
|
||
// This is an active connection, find it using
|
||
// our session id.
|
||
//
|
||
|
||
Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT;
|
||
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
Connection = Device->ConnectionHash[Hash].Connections;
|
||
|
||
while (Connection != NULL) {
|
||
|
||
if (Connection->LocalConnectionId == Sess->DestConnectionId) {
|
||
break;
|
||
}
|
||
Connection = Connection->NextConnection;
|
||
}
|
||
|
||
|
||
//
|
||
// We reply to any session end, even if we don't know the
|
||
// connection, to speed up the disconnect on the remote.
|
||
//
|
||
|
||
if (Connection == NULL) {
|
||
|
||
NB_DEBUG (CONNECTION, ("Session end received on unknown id %lx\n", Sess->DestConnectionId));
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
NbiSendSessionEndAck(
|
||
(TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork),
|
||
RemoteAddress,
|
||
Sess);
|
||
return;
|
||
}
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_INDICATE);
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2);
|
||
|
||
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
|
||
if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) {
|
||
|
||
//
|
||
// We are waiting for an ack, so see if this acks
|
||
// anything. We do this in case a full send has been
|
||
// received by the remote but he did not send an
|
||
// ack before the session went down -- this will
|
||
// prevent us from failing a send which actually
|
||
// succeeded. If we are not in W_ACK this may ack
|
||
// part of a send, but in that case we don't care
|
||
// since StopConnection will abort it anyway and
|
||
// the amount successfully received by the remote
|
||
// doesn't matter.
|
||
//
|
||
// This releases the lock.
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Session end at W_ACK, reframing %lx (%d)\n", Connection, Sess->ReceiveSequence));
|
||
|
||
NbiReframeConnection(
|
||
Connection,
|
||
Sess->ReceiveSequence,
|
||
Sess->BytesReceived,
|
||
FALSE
|
||
NB_LOCK_HANDLE_ARG(LockHandle1));
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1);
|
||
|
||
} else {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Session end received on connection %lx\n", Connection));
|
||
|
||
}
|
||
|
||
//
|
||
// This call sets the state to DISCONNECT and
|
||
// releases the connection lock. It will also
|
||
// complete a disconnect wait request if one
|
||
// is pending, and indicate to our client
|
||
// if needed.
|
||
//
|
||
|
||
NbiStopConnection(
|
||
Connection,
|
||
STATUS_REMOTE_DISCONNECT
|
||
NB_LOCK_HANDLE_ARG (LockHandle1));
|
||
|
||
} else {
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Session end received on inactive connection %lx\n", Connection));
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2);
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
|
||
|
||
}
|
||
|
||
NbiSendSessionEndAck(
|
||
(TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork),
|
||
RemoteAddress,
|
||
Sess);
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
|
||
} /* NbiProcessSessionEnd */
|
||
|
||
|
||
VOID
|
||
NbiProcessSessionEndAck(
|
||
IN PIPX_LOCAL_TARGET RemoteAddress,
|
||
IN ULONG MacOptions,
|
||
IN PUCHAR PacketBuffer,
|
||
IN UINT PacketSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles NB_CMD_SESSION_END_ACK frames.
|
||
|
||
Arguments:
|
||
|
||
RemoteAddress - The local target this packet was received from.
|
||
|
||
MacOptions - The MAC options for the underlying NDIS binding.
|
||
|
||
LookaheadBuffer - The packet data, starting at the IPX
|
||
header.
|
||
|
||
PacketSize - The total length of the packet, starting at the
|
||
IPX header.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer;
|
||
NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session);
|
||
PCONNECTION Connection;
|
||
PDEVICE Device = NbiDevice;
|
||
ULONG Hash;
|
||
NB_DEFINE_LOCK_HANDLE (LockHandle)
|
||
|
||
//
|
||
// This is an active connection, find it using
|
||
// our session id.
|
||
//
|
||
|
||
Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT;
|
||
|
||
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
Connection = Device->ConnectionHash[Hash].Connections;
|
||
|
||
while (Connection != NULL) {
|
||
|
||
if (Connection->LocalConnectionId == Sess->DestConnectionId) {
|
||
break;
|
||
}
|
||
Connection = Connection->NextConnection;
|
||
}
|
||
|
||
if (Connection == NULL) {
|
||
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
|
||
return;
|
||
}
|
||
|
||
NbiReferenceConnectionLock (Connection, CREF_INDICATE);
|
||
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
|
||
|
||
//
|
||
// See what is happening with this connection.
|
||
//
|
||
|
||
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
||
|
||
if (Connection->State == CONNECTION_STATE_DISCONNECT) {
|
||
|
||
//
|
||
// Stop the timer, when the reference goes away it
|
||
// will shut down. We set the substate so if the
|
||
// timer is running it will not restart (there is
|
||
// a small window here, but it is not
|
||
// harmful, we will just have to timeout one
|
||
// more time).
|
||
//
|
||
|
||
NB_DEBUG2 (CONNECTION, ("Got session end ack on %lx\n", Connection));
|
||
|
||
Connection->SubState = CONNECTION_SUBSTATE_D_GOT_ACK;
|
||
if (CTEStopTimer (&Connection->Timer)) {
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
NbiDereferenceConnection (Connection, CREF_TIMER);
|
||
} else {
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
}
|
||
|
||
} else {
|
||
|
||
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
|
||
|
||
}
|
||
|
||
NbiDereferenceConnection (Connection, CREF_INDICATE);
|
||
|
||
} /* NbiProcessSessionEndAck */
|
||
|