|
|
/*++
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 */
|