You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2465 lines
88 KiB
2465 lines
88 KiB
/*++
|
|
|
|
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;
|
|
PSLIST_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 */
|
|
|