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.
3990 lines
140 KiB
3990 lines
140 KiB
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Hndlrs.c
|
|
|
|
Abstract:
|
|
|
|
|
|
This file contains the Non OS specific implementation of handlers that are
|
|
called for Connects,Receives, Disconnects, and Errors.
|
|
|
|
This file represents the TDI interface on the Bottom of NBT after it has
|
|
been decoded into procedure call symantics from the Irp symantics used by
|
|
NT.
|
|
|
|
|
|
Author:
|
|
|
|
Jim Stewart (Jimst) 10-2-92
|
|
|
|
Revision History:
|
|
|
|
Will Lees (wlees) Sep 11, 1997
|
|
Added support for message-only devices
|
|
|
|
--*/
|
|
|
|
#ifdef VXD
|
|
|
|
#define NTIndicateSessionSetup(pLowerConn,status) \
|
|
DbgPrint("Skipping NTIndicateSessionSetup\n\r")
|
|
|
|
#endif //VXD
|
|
|
|
#include "precomp.h"
|
|
#include "ctemacro.h"
|
|
#include "hndlrs.tmh"
|
|
|
|
static __inline USHORT
|
|
GetRandomNumber(PULONG Seed)
|
|
{
|
|
return (USHORT)((((*Seed) = (*Seed) * 214013L + 2531011L) >> 16) & 0x7fff);
|
|
}
|
|
|
|
__inline long
|
|
myntohl(long x)
|
|
{
|
|
return((((x) >> 24) & 0x000000FFL) |
|
|
(((x) >> 8) & 0x0000FF00L) |
|
|
(((x) << 8) & 0x00FF0000L));
|
|
}
|
|
|
|
VOID
|
|
ClearConnStructures (
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN tCONNECTELE *pConnectEle
|
|
);
|
|
|
|
NTSTATUS
|
|
CompleteSessionSetup (
|
|
IN tCLIENTELE *pClientEle,
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN tCONNECTELE *pConnectEle,
|
|
IN PCTE_IRP pIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
MakeRemoteAddressStructure(
|
|
IN PCHAR pHalfAsciiName,
|
|
IN PVOID pSourceAddr,
|
|
IN ULONG lMaxNameSize,
|
|
OUT PVOID *ppRemoteAddress,
|
|
OUT PULONG pRemoteAddressLength,
|
|
IN ULONG NumAddr
|
|
);
|
|
|
|
VOID
|
|
AddToRemoteHashTbl (
|
|
IN tDGRAMHDR UNALIGNED *pDgram,
|
|
IN ULONG BytesIndicated,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
);
|
|
|
|
|
|
VOID
|
|
DoNothingComplete (
|
|
IN PVOID pContext
|
|
);
|
|
|
|
VOID
|
|
AllocLowerConn(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PVOID pDeviceSpecial
|
|
);
|
|
|
|
VOID
|
|
GetIrpIfNotCancelled2(
|
|
IN tCONNECTELE *pConnEle,
|
|
OUT PIRP *ppIrp
|
|
);
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
Inbound(
|
|
IN PVOID ReceiveEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN USHORT ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PVOID *RcvBuffer
|
|
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to setup inbound session
|
|
once the tcp connection is up. The transport calls this routine with
|
|
a session setup request pdu.
|
|
|
|
In message-only mode, this routine may be called to cause a session to be set up even
|
|
though the session request was not received over the wire. A fake session request is
|
|
crafted up and passed to this routine. In this mode, we don't want to send session rejections
|
|
back over the wire.
|
|
|
|
NOTE!!!
|
|
// the LowerConn Lock is held prior to calling this routine
|
|
|
|
Arguments:
|
|
|
|
pClientEle - ptr to the connecition record for this session
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
tCLIENTELE *pClientEle;
|
|
tSESSIONHDR UNALIGNED *pSessionHdr;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tCONNECTELE *pConnectEle;
|
|
CTELockHandle OldIrq;
|
|
PIRP pIrp;
|
|
PLIST_ENTRY pEntry;
|
|
CONNECTION_CONTEXT ConnectId;
|
|
PTA_NETBIOS_ADDRESS pRemoteAddress;
|
|
ULONG RemoteAddressLength;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
//
|
|
// Verify that the DataSize >= sizeof (Sessionheader)
|
|
// Bug# 126111
|
|
//
|
|
if (BytesIndicated < (sizeof(tSESSIONHDR)))
|
|
{
|
|
KdPrint (("Nbt.Inbound[1]: WARNING!!! Rejecting Request -- BytesIndicated=<%d> < <%d>\n",
|
|
BytesIndicated, (sizeof(tSESSIONHDR))));
|
|
NbtTrace(NBT_TRACE_INBOUND, ("Reject on pLowerConn %p: BytesIndicated %d < sizeof(tSESSIONHDR) bytes",
|
|
ConnectionContext, BytesIndicated));
|
|
|
|
return (STATUS_INTERNAL_ERROR);
|
|
}
|
|
|
|
pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu;
|
|
|
|
// get the ptrs to the lower and upper connections
|
|
//
|
|
pLowerConn = (tLOWERCONNECTION *)ConnectionContext;
|
|
pConnectEle = pLowerConn->pUpperConnection;
|
|
pDeviceContext = pLowerConn->pDeviceContext;
|
|
|
|
//
|
|
// fake out the transport so it frees its receive buffer (i.e. we
|
|
// say that we accepted all of the data)
|
|
//
|
|
*BytesTaken = BytesIndicated;
|
|
|
|
//
|
|
// since we send keep alives on connections in the the inbound
|
|
// state it is possible to get a keep alive, so just return in that
|
|
// case
|
|
//
|
|
if (((tSESSIONHDR UNALIGNED *)pTsdu)->Type == NBT_SESSION_KEEP_ALIVE)
|
|
{
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Session ** INBOUND ** setup processing
|
|
//
|
|
if ((pSessionHdr->Type != NBT_SESSION_REQUEST) ||
|
|
(BytesIndicated < (sizeof(tSESSIONHDR) + 2*(1+2*NETBIOS_NAME_SIZE+1))))
|
|
{
|
|
//
|
|
// The Session Request packet is of the form:
|
|
// -- Session Header = 4 bytes
|
|
// -- Called name = 34+x (1 + 32 + 1(==ScopeLength) + x(==Scope))
|
|
// -- Calling name= 34+y (1 + 32 + 1(==ScopeLength) + y(==Scope))
|
|
//
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
|
|
KdPrint(("Nbt.Inbound[2]: ERROR -- Bad Session PDU - Type=<%x>, BytesInd=[%d]<[%d], Src=<%x>\n",
|
|
pSessionHdr->Type, BytesIndicated, (sizeof(tSESSIONHDR)+2*(1+2*NETBIOS_NAME_SIZE+1)),pLowerConn->SrcIpAddr));
|
|
NbtTrace(NBT_TRACE_INBOUND, ("pLowerConn %p: bad Session PDU on %!ipaddr!",
|
|
pLowerConn, pLowerConn->SrcIpAddr));
|
|
|
|
#ifdef _NETBIOSLESS
|
|
status = STATUS_INTERNAL_ERROR;
|
|
|
|
// In message-only mode, don't send session responses back over the wire
|
|
if (!IsDeviceNetbiosless(pLowerConn->pDeviceContext))
|
|
#endif
|
|
{
|
|
RejectSession(pLowerConn, NBT_NEGATIVE_SESSION_RESPONSE, SESSION_UNSPECIFIED_ERROR, TRUE);
|
|
}
|
|
goto Inbound_Exit1;
|
|
}
|
|
|
|
// the LowerConn Lock is held prior to calling this routine, so free it
|
|
// here since we need to get the joint lock first
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLockAtDpc(pLowerConn);
|
|
|
|
// it is possible for the disconnect handler to run while the pLowerConn
|
|
// lock is released above, to get the ConnEle lock, and change the state
|
|
// to disconnected.
|
|
if (pLowerConn->State != NBT_SESSION_INBOUND)
|
|
{
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
#ifdef _NETBIOSLESS
|
|
status = STATUS_CONNECTION_DISCONNECTED;
|
|
#endif
|
|
NbtTrace(NBT_TRACE_INBOUND, ("pLowerConn %p: Incorrect state", pLowerConn));
|
|
goto Inbound_Exit1;
|
|
}
|
|
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.Inbound: In SessionSetupnotOS, connection state = %X\n",pLowerConn->State));
|
|
|
|
status = FindSessionEndPoint(pTsdu,
|
|
ConnectionContext,
|
|
BytesIndicated,
|
|
&pClientEle,
|
|
&pRemoteAddress,
|
|
&RemoteAddressLength);
|
|
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// could not find the desired end point so send a negative session
|
|
// response pdu and then disconnect
|
|
//
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
NbtTrace(NBT_TRACE_INBOUND, ("pLowerConn %p: FindSessionEndPoint return %!status!", pLowerConn, status));
|
|
|
|
#ifdef _NETBIOSLESS
|
|
// In message-only mode, don't send session responses back over the wire
|
|
if (!IsDeviceNetbiosless(pLowerConn->pDeviceContext))
|
|
#endif
|
|
{
|
|
RejectSession(pLowerConn, NBT_NEGATIVE_SESSION_RESPONSE, status, TRUE);
|
|
}
|
|
|
|
KdPrint (("Nbt.Inbound[3]: WARNING!!! FindSessionEndPoint failed, Rejecting Request\n"));
|
|
goto Inbound_Exit1;
|
|
}
|
|
|
|
//
|
|
// we must first check for a valid LISTEN....
|
|
//
|
|
CTESpinLockAtDpc(pDeviceContext);
|
|
CTESpinLockAtDpc(pClientEle);
|
|
if (!IsListEmpty(&pClientEle->ListenHead))
|
|
{
|
|
tLISTENREQUESTS *pListen;
|
|
tLISTENREQUESTS *pListenTarget ;
|
|
|
|
//
|
|
// Find the first listen that matches the remote name else
|
|
// take a listen that specified '*'
|
|
//
|
|
pListenTarget = NULL;
|
|
for ( pEntry = pClientEle->ListenHead.Flink ;
|
|
pEntry != &pClientEle->ListenHead ;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage);
|
|
|
|
// in NT-land the pConnInfo structure is passed in , but the
|
|
// remote address field is nulled out... so we need to check
|
|
// both of these before going on to check the remote address.
|
|
if (pListen->pConnInfo && pListen->pConnInfo->RemoteAddress)
|
|
{
|
|
|
|
if (CTEMemEqu(((PTA_NETBIOS_ADDRESS)pListen->pConnInfo->RemoteAddress)->
|
|
Address[0].Address[0].NetbiosName,
|
|
pRemoteAddress->Address[0].Address[0].NetbiosName,
|
|
NETBIOS_NAME_SIZE))
|
|
{
|
|
pListenTarget = pListen;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Specified '*' for the remote name, save this,
|
|
// look for listen on a real name - only save if it is
|
|
// the first * listen found
|
|
//
|
|
if (!pListenTarget)
|
|
{
|
|
pListenTarget = pListen ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pListenTarget)
|
|
{
|
|
PTA_NETBIOS_ADDRESS pRemoteAddr;
|
|
|
|
RemoveEntryList( &pListenTarget->Linkage );
|
|
|
|
//
|
|
// Fill in the remote machines name to return to the client
|
|
//
|
|
if ((pListenTarget->pReturnConnInfo) &&
|
|
(pRemoteAddr = pListenTarget->pReturnConnInfo->RemoteAddress))
|
|
{
|
|
CTEMemCopy(pRemoteAddr,pRemoteAddress,RemoteAddressLength);
|
|
}
|
|
|
|
//
|
|
// get the upper connection end point out of the listen and
|
|
// hook the upper and lower connections together.
|
|
//
|
|
pConnectEle = (tCONNECTELE *)pListenTarget->pConnectEle;
|
|
CHECK_PTR(pConnectEle);
|
|
CTESpinLockAtDpc(pConnectEle);
|
|
|
|
pLowerConn->pUpperConnection = pConnectEle;
|
|
pConnectEle->pLowerConnId = pLowerConn;
|
|
pConnectEle->pIrpRcv = NULL;
|
|
|
|
//
|
|
// Previously, the LowerConnection was in the SESSION_INBOUND state
|
|
// hence we have to remove it from the WaitingForInbound Q and put
|
|
// it on the active LowerConnection list!
|
|
//
|
|
ASSERT (pLowerConn->State == NBT_SESSION_INBOUND);
|
|
RemoveEntryList (&pLowerConn->Linkage);
|
|
InsertTailList (&pLowerConn->pDeviceContext->LowerConnection, &pLowerConn->Linkage);
|
|
InterlockedDecrement (&pLowerConn->pDeviceContext->NumWaitingForInbound);
|
|
//
|
|
// Change the RefCount Context to Connected!
|
|
//
|
|
NBT_SWAP_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_WAITING_INBOUND, REF_LOWC_CONNECTED, FALSE);
|
|
|
|
//
|
|
// put the upper connection on its active list
|
|
//
|
|
RemoveEntryList(&pConnectEle->Linkage);
|
|
InsertTailList(&pConnectEle->pClientEle->ConnectActive, &pConnectEle->Linkage);
|
|
|
|
//
|
|
// Save the remote name while we still have it
|
|
//
|
|
CTEMemCopy (pConnectEle->RemoteName,
|
|
pRemoteAddress->Address[0].Address[0].NetbiosName,
|
|
NETBIOS_NAME_SIZE ) ;
|
|
|
|
//
|
|
// since the lower connection now points to pConnectEle, increment
|
|
// the reference count so we can't free pConnectEle memory until
|
|
// the lower conn no longer points to it.
|
|
ClearConnStructures(pLowerConn,pConnectEle);
|
|
NBT_REFERENCE_CONNECTION (pConnectEle, REF_CONN_CONNECT);
|
|
|
|
if (pListenTarget->Flags & TDI_QUERY_ACCEPT)
|
|
{
|
|
SET_STATE_UPPER (pConnectEle, NBT_SESSION_WAITACCEPT);
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_WAITACCEPT);
|
|
SET_STATERCV_LOWER (pLowerConn, NORMAL, RejectAnyData);
|
|
}
|
|
else
|
|
{
|
|
SET_STATE_UPPER (pConnectEle, NBT_SESSION_UP);
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_UP);
|
|
SET_STATERCV_LOWER (pLowerConn, NORMAL, Normal);
|
|
}
|
|
|
|
CTESpinFreeAtDpc(pConnectEle);
|
|
CTESpinFreeAtDpc(pClientEle);
|
|
CTESpinFreeAtDpc(pDeviceContext);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (pListenTarget->Flags & TDI_QUERY_ACCEPT)
|
|
{
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.Inbound: Completing Client's Irp to make client issue Accept\n"));
|
|
|
|
//
|
|
// complete the client listen irp, which will trigger him to
|
|
// issue an accept, which should find the connection in the
|
|
// WAIT_ACCEPT state, and subsequently cause a session response
|
|
// to be sent.
|
|
#ifndef VXD
|
|
// the irp can't get cancelled because the cancel listen routine
|
|
// also grabs the Client spin lock and removes the listen from the
|
|
// list..
|
|
CTEIoComplete( pListenTarget->pIrp,STATUS_SUCCESS,0);
|
|
#else
|
|
CTEIoComplete( pListenTarget->pIrp,STATUS_SUCCESS, (ULONG) pConnectEle);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.Inbound: Calling CompleteSessionSetup to send session response PDU\n"));
|
|
|
|
//
|
|
// We need to send a session response PDU here, since
|
|
// we do not have to wait for an accept in this case
|
|
//
|
|
CompleteSessionSetup(pClientEle, pLowerConn,pConnectEle, pListenTarget->pIrp);
|
|
}
|
|
|
|
CTEMemFree((PVOID)pRemoteAddress);
|
|
CTEMemFree(pListenTarget);
|
|
|
|
// now that we have notified the client, dereference it
|
|
//
|
|
NBT_DEREFERENCE_CLIENT(pClientEle);
|
|
|
|
PUSH_LOCATION(0x60);
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.Inbound: Accepted Connection by a Listen %X LowerConn=%X, BytesTaken=<%x>\n",
|
|
pConnectEle,pLowerConn, BytesAvailable));
|
|
|
|
// fake out the transport so it frees its receive buffer (i.e. we
|
|
// say that we accepted all of the data)
|
|
*BytesTaken = BytesAvailable;
|
|
status = STATUS_SUCCESS;
|
|
goto Inbound_Exit1;
|
|
}
|
|
}
|
|
CTESpinFreeAtDpc(pClientEle);
|
|
CTESpinFreeAtDpc(pDeviceContext);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// No LISTEN, so check for an Event handler
|
|
//
|
|
if (!pClientEle->ConEvContext)
|
|
{
|
|
NbtTrace(NBT_TRACE_INBOUND, ("pLowerConn %p: No listener", pLowerConn));
|
|
|
|
#ifdef _NETBIOSLESS
|
|
status = STATUS_REMOTE_NOT_LISTENING;
|
|
|
|
// In message-only mode, don't send session responses back over the wire
|
|
if (!IsDeviceNetbiosless(pLowerConn->pDeviceContext))
|
|
#endif
|
|
{
|
|
RejectSession(pLowerConn,
|
|
NBT_NEGATIVE_SESSION_RESPONSE,
|
|
SESSION_NOT_LISTENING_ON_CALLED_NAME,
|
|
TRUE);
|
|
}
|
|
|
|
// undo the reference done in FindEndpoint
|
|
//
|
|
NBT_DEREFERENCE_CLIENT(pClientEle);
|
|
//
|
|
// free the memory allocated for the Remote address data structure
|
|
//
|
|
CTEMemFree((PVOID)pRemoteAddress);
|
|
|
|
KdPrint (("Nbt.Inbound[4]: WARNING!!! Rejecting Request -- No Listen or EventHandler\n"));
|
|
goto Inbound_Exit1;
|
|
}
|
|
#ifdef VXD
|
|
else
|
|
{
|
|
ASSERT( FALSE ) ;
|
|
}
|
|
#endif
|
|
|
|
// now call the client's connect handler...
|
|
pIrp = NULL;
|
|
#ifndef VXD // VXD doesn't support event handlers
|
|
|
|
status = (*pClientEle->evConnect)(pClientEle->ConEvContext,
|
|
RemoteAddressLength,
|
|
pRemoteAddress,
|
|
0,
|
|
NULL,
|
|
0, // options length
|
|
NULL, // Options
|
|
&ConnectId,
|
|
&pIrp
|
|
);
|
|
NbtTrace(NBT_TRACE_INBOUND, ("pLowerConn %p: TDI_ACCEPT pIrp %p %!status! for %!NBTNAME!<%02x>",
|
|
pLowerConn, pIrp, status,
|
|
pRemoteAddress->Address[0].Address[0].NetbiosName,
|
|
(unsigned)pRemoteAddress->Address[0].Address[0].NetbiosName[15]
|
|
));
|
|
|
|
//
|
|
// With the new TDI semantics is it illegal to return STATUS_EVENT_DONE
|
|
// or STATUS_EVENT_PENDING from the connect event handler
|
|
//
|
|
ASSERT(status != STATUS_EVENT_PENDING);
|
|
ASSERT(status != STATUS_EVENT_DONE);
|
|
|
|
|
|
// now that we have notified the client, dereference it
|
|
//
|
|
NBT_DEREFERENCE_CLIENT(pClientEle);
|
|
|
|
// Check the returned status codes..
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED && pIrp != NULL)
|
|
{
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
|
|
// the pConnEle ptr was stored in the FsContext value when the connection
|
|
// was initially created.
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
pConnectEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext;
|
|
if (!NBT_VERIFY_HANDLE2 (pConnectEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
|
|
{
|
|
ASSERTMSG ("Nbt.Inbound: ERROR - Invalid Connection Handle\n", 0);
|
|
status = STATUS_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Save the remote name while we still have it
|
|
//
|
|
CHECK_PTR(pConnectEle);
|
|
CTEMemCopy( pConnectEle->RemoteName,
|
|
pRemoteAddress->Address[0].Address[0].NetbiosName,
|
|
NETBIOS_NAME_SIZE ) ;
|
|
|
|
// be sure the connection is in the correct state
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLockAtDpc(pDeviceContext);
|
|
CTESpinLockAtDpc(pClientEle);
|
|
CTESpinLockAtDpc(pConnectEle);
|
|
|
|
if (pConnectEle->state == NBT_ASSOCIATED)
|
|
{
|
|
//
|
|
// Previously, the LowerConnection was in the SESSION_INBOUND state
|
|
// hence we have to remove it from the WaitingForInbound Q and put
|
|
// it on the active LowerConnection list!
|
|
//
|
|
ASSERT (pLowerConn->State == NBT_SESSION_INBOUND);
|
|
RemoveEntryList (&pLowerConn->Linkage);
|
|
InsertTailList (&pLowerConn->pDeviceContext->LowerConnection, &pLowerConn->Linkage);
|
|
InterlockedDecrement (&pLowerConn->pDeviceContext->NumWaitingForInbound);
|
|
//
|
|
// Change the RefCount Context to Connected!
|
|
//
|
|
NBT_SWAP_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_WAITING_INBOUND,REF_LOWC_CONNECTED, FALSE);
|
|
|
|
// since the lower connection now points to pConnectEle, increment
|
|
// the reference count so we can't free pConnectEle memory until
|
|
// the lower conn no longer points to it.
|
|
//
|
|
NBT_REFERENCE_CONNECTION (pConnectEle, REF_CONN_CONNECT);
|
|
ClearConnStructures(pLowerConn,pConnectEle);
|
|
SET_STATE_UPPER (pConnectEle, NBT_SESSION_UP);
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_UP);
|
|
SetStateProc (pLowerConn, Normal);
|
|
|
|
RemoveEntryList(&pConnectEle->Linkage);
|
|
InsertTailList(&pConnectEle->pClientEle->ConnectActive, &pConnectEle->Linkage);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
CTESpinFreeAtDpc(pConnectEle);
|
|
CTESpinFreeAtDpc(pClientEle);
|
|
CTESpinFreeAtDpc(pDeviceContext);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (STATUS_SUCCESS == status)
|
|
{
|
|
CompleteSessionSetup(pClientEle,pLowerConn,pConnectEle,pIrp);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.Inbound: The client rejected in the inbound connection status = %X\n", status));
|
|
|
|
#ifdef _NETBIOSLESS
|
|
// In message-only mode, don't send session responses back over the wire
|
|
if (!IsDeviceNetbiosless(pLowerConn->pDeviceContext))
|
|
#endif
|
|
{
|
|
RejectSession(pLowerConn,
|
|
NBT_NEGATIVE_SESSION_RESPONSE,
|
|
SESSION_CALLED_NAME_PRESENT_NO_RESRC,
|
|
TRUE);
|
|
}
|
|
}
|
|
#endif
|
|
//
|
|
// free the memory allocated for the Remote address data structure
|
|
//
|
|
CTEMemFree((PVOID)pRemoteAddress);
|
|
|
|
Inbound_Exit1:
|
|
// This spin lock is held by the routine that calls this one, and
|
|
// freed when this routine starts, so we must regrab this lock before
|
|
// returning
|
|
//
|
|
CTESpinLockAtDpc(pLowerConn);
|
|
|
|
#ifdef _NETBIOSLESS
|
|
// In message only mode, return the real status
|
|
return (IsDeviceNetbiosless(pLowerConn->pDeviceContext) ? status : STATUS_SUCCESS );
|
|
#else
|
|
return(STATUS_SUCCESS);
|
|
#endif
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
ClearConnStructures (
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN tCONNECTELE *pConnectEle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets various parts of the connection datastructures to
|
|
zero, in preparation for a new connection.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
CHECK_PTR(pConnectEle);
|
|
#ifndef VXD
|
|
pConnectEle->FreeBytesInMdl = 0;
|
|
pConnectEle->CurrentRcvLen = 0;
|
|
pLowerConn->BytesInIndicate = 0;
|
|
#endif
|
|
pConnectEle->ReceiveIndicated = 0;
|
|
pConnectEle->BytesInXport = 0;
|
|
pConnectEle->BytesRcvd = 0;
|
|
pConnectEle->TotalPcktLen = 0;
|
|
pConnectEle->OffsetFromStart = 0;
|
|
pConnectEle->pIrpRcv = NULL;
|
|
pConnectEle->pIrp = NULL;
|
|
pConnectEle->pIrpDisc = NULL;
|
|
pConnectEle->pIrpClose = NULL;
|
|
pConnectEle->DiscFlag = 0;
|
|
pConnectEle->JunkMsgFlag = FALSE;
|
|
pConnectEle->pLowerConnId = pLowerConn;
|
|
InitializeListHead(&pConnectEle->RcvHead);
|
|
|
|
pLowerConn->pUpperConnection = pConnectEle;
|
|
SET_STATERCV_LOWER(pLowerConn, NORMAL, pLowerConn->CurrentStateProc);
|
|
|
|
pLowerConn->BytesRcvd = 0;
|
|
pLowerConn->BytesSent = 0;
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
CompleteSessionSetup (
|
|
IN tCLIENTELE *pClientEle,
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN tCONNECTELE *pConnectEle,
|
|
IN PCTE_IRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to setup an outbound session
|
|
once the tcp connection is up. The transport calls this routine with
|
|
a session setup response pdu.
|
|
|
|
The pConnectEle + Joinlock are held when this routine is called.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
|
|
#ifdef _NETBIOSLESS
|
|
// In message-only mode, don't send session responses back over the wire
|
|
if (IsDeviceNetbiosless(pLowerConn->pDeviceContext))
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
status = TcpSendSessionResponse(pLowerConn, NBT_POSITIVE_SESSION_RESPONSE, 0L);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.CompleteSessionSetup: Accepted ConnEle=%p LowerConn=%p\n",pConnectEle,pLowerConn));
|
|
|
|
//
|
|
// complete the client's accept Irp
|
|
//
|
|
#ifndef VXD
|
|
CTEIoComplete (pIrp, STATUS_SUCCESS, 0);
|
|
#else
|
|
CTEIoComplete (pIrp, STATUS_SUCCESS, (ULONG)pConnectEle);
|
|
#endif
|
|
}
|
|
else
|
|
{ //
|
|
// if we have some trouble sending the Session response, then
|
|
// disconnect the connection
|
|
//
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.CompleteSessionSetup: Could not send Session Response, status = %X\n", status));
|
|
|
|
RejectSession(pLowerConn, NBT_NEGATIVE_SESSION_RESPONSE, SESSION_CALLED_NAME_PRESENT_NO_RESRC, TRUE);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock, OldIrq);
|
|
RelistConnection(pConnectEle);
|
|
CTESpinFree(&NbtConfig.JointLock, OldIrq);
|
|
|
|
// Disconnect To Client - i.e. a negative Accept
|
|
// this will get done when the disconnect indication
|
|
// comes back from the transport
|
|
//
|
|
GetIrpIfNotCancelled(pConnectEle,&pIrp);
|
|
if (pIrp)
|
|
{
|
|
CTEIoComplete(pIrp,STATUS_UNSUCCESSFUL,0);
|
|
}
|
|
}
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
Outbound (
|
|
IN PVOID ReceiveEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN USHORT ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PVOID *RcvBuffer
|
|
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called while seting up an outbound session
|
|
once the tcp connection is up. The transport calls this routine with
|
|
a session setup response pdu .
|
|
|
|
|
|
Arguments:
|
|
|
|
pClientEle - ptr to the connection record for this session
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
|
|
tSESSIONHDR UNALIGNED *pSessionHdr;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
CTELockHandle OldIrq;
|
|
PIRP pIrp;
|
|
tTIMERQENTRY *pTimerEntry;
|
|
tCONNECTELE *pConnEle;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
// get the ptr to the lower connection
|
|
//
|
|
pLowerConn = (tLOWERCONNECTION *)ConnectionContext;
|
|
pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu;
|
|
pDeviceContext = pLowerConn->pDeviceContext;
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pLowerConn %p pConnEle %p pDeviceContext %p BytesIndicated %d",
|
|
pLowerConn, pLowerConn->pUpperConnection, pDeviceContext, BytesIndicated));
|
|
|
|
//
|
|
// fake out the transport so it frees its receive buffer (i.e. we
|
|
// say that we accepted all of the data)
|
|
//
|
|
*BytesTaken = BytesIndicated;
|
|
//
|
|
// since we send keep alives on connections in the the inbound
|
|
// state it is possible to get a keep alive, so just return in that
|
|
// case
|
|
//
|
|
if (((tSESSIONHDR UNALIGNED *)pTsdu)->Type == NBT_SESSION_KEEP_ALIVE)
|
|
{
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Return success for NBT_SESSION_KEEP_ALIVE"));
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
// the LowerConn Lock is held prior to calling this routine, so free it
|
|
// here since we need to get the joint lock first
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLockAtDpc(pLowerConn);
|
|
|
|
//
|
|
// it is possible for the disconnect handler to run while the pLowerConn
|
|
// lock is released above, to get the ConnEle lock, and change the state
|
|
// to disconnected.
|
|
//
|
|
if ((!(pConnEle = pLowerConn->pUpperConnection)) ||
|
|
(pConnEle->state != NBT_SESSION_OUTBOUND))
|
|
{
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Reset outbound connection %p", pConnEle));
|
|
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
RejectSession(pLowerConn,0,0,FALSE);
|
|
goto ExitCode;
|
|
}
|
|
|
|
//
|
|
// if no Connect Tracker, then SessionStartupCompletion has run and
|
|
// the connection is about to be closed, so return.
|
|
//
|
|
if (!(pTracker = (tDGRAM_SEND_TRACKING *)pConnEle->pIrpRcv))
|
|
{
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("No tracker for pConnEle %p", pConnEle));
|
|
goto ExitCode;
|
|
}
|
|
|
|
CHECK_PTR(pTracker);
|
|
CHECK_PTR(pConnEle);
|
|
|
|
pConnEle->pIrpRcv = NULL;
|
|
|
|
//
|
|
// Stop the timer started in SessionStartupCompletion to time the
|
|
// Session Setup Response message - it is possible for this routine to
|
|
// run before SessionStartupCompletion, in which case there will not be
|
|
// any timer to stop.
|
|
//
|
|
if (pTimerEntry = pTracker->pTimer)
|
|
{
|
|
pTracker->pTimer = NULL;
|
|
StopTimer(pTimerEntry,NULL,NULL);
|
|
}
|
|
|
|
if (pSessionHdr->Type == NBT_POSITIVE_SESSION_RESPONSE)
|
|
{
|
|
// zero out the number of bytes received so far, since this is a new connection
|
|
CHECK_PTR(pConnEle);
|
|
pConnEle->BytesRcvd = 0;
|
|
SET_STATE_UPPER (pConnEle, NBT_SESSION_UP);
|
|
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_UP);
|
|
SetStateProc( pLowerConn, Normal ) ;
|
|
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
|
|
GetIrpIfNotCancelled2(pConnEle,&pIrp);
|
|
|
|
//
|
|
// if SessionSetupContinue has run, it has set the refcount to zero
|
|
//
|
|
if (pTracker->RefConn == 0)
|
|
{
|
|
//
|
|
// remove the reference done when FindNameOrQuery was called, or when
|
|
// SessionSetupContinue ran
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
}
|
|
else
|
|
{
|
|
pTracker->RefConn--;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// the assumption is that if the connect irp was cancelled then the
|
|
// client should be doing a disconnect or close shortly thereafter, so
|
|
// there is no error handling code here.
|
|
if (pIrp)
|
|
{
|
|
//
|
|
// complete the client's connect request Irp
|
|
//
|
|
#ifndef VXD
|
|
CTEIoComplete( pIrp, STATUS_SUCCESS, 0 ) ;
|
|
#else
|
|
CTEIoComplete( pIrp, STATUS_SUCCESS, (ULONG)pConnEle ) ;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULONG ErrorCode;
|
|
ULONG state;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
state = pConnEle->state;
|
|
if ((NbtConfig.Unloading) ||
|
|
(!NBT_REFERENCE_DEVICE(pDeviceContext, REF_DEV_OUTBOUND, TRUE)))
|
|
{
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Outbound() gets called while unloading driver %x", state));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
|
|
// If the response is Retarget then setup another session
|
|
// to the new Ip address and port number.
|
|
//
|
|
ErrorCode = (ULONG)((tSESSIONERROR *)pSessionHdr)->ErrorCode;
|
|
|
|
#ifdef MULTIPLE_WINS
|
|
if ( (status == STATUS_SUCCESS) &&
|
|
( ((pSessionHdr->Type == NBT_RETARGET_SESSION_RESPONSE) &&
|
|
(pConnEle->SessionSetupCount--))
|
|
||
|
|
((NbtConfig.TryAllNameServers) &&
|
|
(pSessionHdr->Type == NBT_NEGATIVE_SESSION_RESPONSE) &&
|
|
pTracker->RemoteNameLength <= NETBIOS_NAME_SIZE && // Don't do it for DNS name
|
|
(pTracker->ResolutionContextFlags != 0xFF)))) // Have not finished querying
|
|
{
|
|
#else
|
|
if (pSessionHdr->Type == NBT_RETARGET_SESSION_RESPONSE)
|
|
{
|
|
//
|
|
// retry the session setup if we haven't already exceeded the
|
|
// count
|
|
//
|
|
if (pConnEle->SessionSetupCount--)
|
|
{
|
|
#endif
|
|
PVOID Context=NULL;
|
|
BOOLEAN Cancelled;
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
|
|
// for retarget the destination has specified an alternate
|
|
// port to which the session should be established.
|
|
if (pSessionHdr->Type == NBT_RETARGET_SESSION_RESPONSE)
|
|
{
|
|
pTracker->DestPort = ntohs(((tSESSIONRETARGET *)pSessionHdr)->Port);
|
|
Context = ULongToPtr(ntohl(((tSESSIONRETARGET *)pSessionHdr)->IpAddress));
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Retarget to %!ipaddr!:%!port!",
|
|
PtrToUlong(Context), (USHORT)(pTracker->DestPort)));
|
|
}
|
|
else
|
|
#ifndef MULTIPLE_WINS
|
|
if (ErrorCode == SESSION_CALLED_NAME_NOT_PRESENT)
|
|
#endif
|
|
{
|
|
// to tell DelayedReconnect to use the current name(not a retarget)
|
|
Context = NULL;
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Called Name Not Present"));
|
|
}
|
|
|
|
//
|
|
// Unlink the lower and upper connections.
|
|
//
|
|
CHECK_PTR(pConnEle);
|
|
CHECK_PTR(pLowerConn);
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
|
|
//
|
|
// put the pconnele back on the Client's ConnectHead if it
|
|
// has not been cleanedup yet.
|
|
//
|
|
if (state != NBT_IDLE)
|
|
{
|
|
RelistConnection(pConnEle);
|
|
}
|
|
|
|
// if a disconnect comes down in this state we we will handle it.
|
|
SET_STATE_UPPER (pConnEle, NBT_RECONNECTING);
|
|
|
|
CHECK_PTR(pConnEle);
|
|
|
|
#ifdef MULTIPLE_WINS
|
|
if (pSessionHdr->Type == NBT_RETARGET_SESSION_RESPONSE)
|
|
{
|
|
pConnEle->SessionSetupCount = 0;// only allow one retry
|
|
}
|
|
#else
|
|
pConnEle->SessionSetupCount = 0;// only allow one retry
|
|
#endif
|
|
|
|
pIrp = pConnEle->pIrp;
|
|
Cancelled = FALSE;
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.Outbound: Attempt Reconnect, error=%X LowerConn %X\n", ErrorCode,pLowerConn));
|
|
#ifndef VXD
|
|
// the irp can't be cancelled until the connection
|
|
// starts up again - either when the Irp is in the transport
|
|
// or when we set our cancel routine in SessionStartupCompletion
|
|
// This disconnect handler cannot complete the Irp because
|
|
// we set the pConnEle state to NBT_ASSOCIATED above, with
|
|
// the spin lock held, which prevents the Disconnect handler
|
|
// from doing anything.
|
|
IoAcquireCancelSpinLock(&OldIrq);
|
|
if (pIrp && !pConnEle->pIrp->Cancel)
|
|
{
|
|
IoSetCancelRoutine(pIrp,NULL);
|
|
}
|
|
else
|
|
{
|
|
Cancelled = TRUE;
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p is cancelled", pIrp));
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(OldIrq);
|
|
#endif
|
|
|
|
if (!Cancelled)
|
|
{
|
|
//
|
|
// The enqueuing can not fail because we have already
|
|
// verified the device state earlier
|
|
//
|
|
NTQueueToWorkerThread(
|
|
&pTracker->WorkItemReconnect,
|
|
DelayedReConnect,
|
|
pTracker,
|
|
Context,
|
|
NULL,
|
|
pDeviceContext,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
// ...else The irp was already returned, since NtCancelSession
|
|
// Must have already run, so just return
|
|
NBT_DEREFERENCE_DEVICE(pDeviceContext, REF_DEV_OUTBOUND, TRUE);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
RejectSession(pLowerConn,0,0,FALSE);
|
|
|
|
// remove the referenced added when the lower and upper
|
|
// connections were attached in nbtconnect.
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
|
|
goto ExitCode;
|
|
#ifndef MULTIPLE_WINS
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// the connection will be disconnected by the Call to RejectSession
|
|
// below, so set the state to Associated so the disconnect indication
|
|
// handler will not complete the client's irp too
|
|
//
|
|
CHECK_PTR(pConnEle);
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
pConnEle->pLowerConnId = NULL;
|
|
|
|
CTESpinFreeAtDpc(pLowerConn);
|
|
|
|
//
|
|
// if nbtcleanupconnection has not been called yet, relist it.
|
|
//
|
|
if (state != NBT_IDLE)
|
|
{
|
|
RelistConnection(pConnEle);
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.Outbound: Disconnecting... Failed connection Setup %X Lowercon %X\n",
|
|
pConnEle,pLowerConn));
|
|
|
|
GetIrpIfNotCancelled2(pConnEle,&pIrp);
|
|
|
|
//
|
|
// if SessionTimedOut has run, it has set the refcount to zero
|
|
//
|
|
if (pTracker->RefConn == 0)
|
|
{
|
|
//
|
|
// remove the reference done when FindNameOrQuery was called, or when
|
|
// SessionSetupContinue ran
|
|
//
|
|
if ((pTracker->pNameAddr->Verify == REMOTE_NAME) && // Remote names only!
|
|
(pTracker->pNameAddr->NameTypeState & STATE_RESOLVED) &&
|
|
(pTracker->pNameAddr->RefCount == 2))
|
|
{
|
|
//
|
|
// If no one else is referencing the name, then delete it from
|
|
// the hash table.
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_REMOTE, TRUE);
|
|
}
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
}
|
|
else
|
|
{
|
|
pTracker->RefConn--;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
if (status != STATUS_INVALID_DEVICE_REQUEST)
|
|
{
|
|
// this should cause a disconnect indication to come from the
|
|
// transport which will close the connection to the transport
|
|
//
|
|
RejectSession(pLowerConn,0,0,FALSE);
|
|
NBT_DEREFERENCE_DEVICE(pDeviceContext, REF_DEV_OUTBOUND, FALSE);
|
|
}
|
|
|
|
//
|
|
// tell the client that the session setup failed and disconnect
|
|
// the connection
|
|
//
|
|
if (pIrp)
|
|
{
|
|
status = STATUS_REMOTE_NOT_LISTENING;
|
|
if (ErrorCode != SESSION_CALLED_NAME_NOT_PRESENT)
|
|
{
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
CTEIoComplete(pIrp, status, 0 ) ;
|
|
}
|
|
}
|
|
|
|
ExitCode:
|
|
// the LowerConn Lock is held prior to calling this routine. It is freed
|
|
// at the start of this routine and held here again
|
|
CTESpinLockAtDpc(pLowerConn);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
GetIrpIfNotCancelled2(
|
|
IN tCONNECTELE *pConnEle,
|
|
OUT PIRP *ppIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine coordinates access to the Irp by getting the spin lock on
|
|
the client, getting the Irp and clearing the irp in the structure. The
|
|
Irp cancel routines also check the pConnEle->pIrp and if null they do not
|
|
find the irp, then they return without completing the irp.
|
|
|
|
This version of the routine is called with NbtConfig.JointLock held.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle OldIrq;
|
|
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
*ppIrp = pConnEle->pIrp;
|
|
CHECK_PTR(pConnEle);
|
|
pConnEle->pIrp = NULL;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
GetIrpIfNotCancelled(
|
|
IN tCONNECTELE *pConnEle,
|
|
OUT PIRP *ppIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine coordinates access to the Irp by getting the spin lock on
|
|
the client, getting the Irp and clearing the irp in the structure. The
|
|
Irp cancel routines also check the pConnEle->pIrp and if null they do not
|
|
find the irp, then they return without completing the irp.
|
|
|
|
This version of the routine is called with NbtConfig.JointLock free.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle OldIrq;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
GetIrpIfNotCancelled2(pConnEle,ppIrp);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
RejectAnyData(
|
|
IN PVOID ReceiveEventContext,
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN USHORT ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PVOID *ppIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the receive event indication handler when the connection
|
|
is not up - i.e. nbt thinks no data should be arriving. We just eat the
|
|
data and return. This routine should not get called.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// take all of the data so that a disconnect will not be held up
|
|
// by data still in the transport.
|
|
//
|
|
*BytesTaken = BytesAvailable;
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.RejectAnyData: Got Session Data in state %X, StateRcv= %X\n",pLowerConn->State,
|
|
pLowerConn->StateRcv));
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
RejectSession(
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN ULONG StatusCode,
|
|
IN ULONG SessionStatus,
|
|
IN BOOLEAN SendNegativeSessionResponse
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a negative session response (if the boolean is set)
|
|
and then disconnects the connection.
|
|
Cleanup connection could have been called to disconnect the call,
|
|
and it changes the state to disconnecting, so don't disconnected
|
|
again if that is happening.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
NTSTATUS status;
|
|
tCONNECTELE *pConnEle;
|
|
BOOLEAN DerefConnEle=FALSE;
|
|
|
|
//
|
|
// There is no listen event handler so return a status code to
|
|
// the caller indicating that this end is between "listens" and
|
|
// that they should try the setup again in a few milliseconds.
|
|
//
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.RejectSession: No Listen or Connect Handlr so Disconnect! LowerConn=%X Session Status=%X\n",
|
|
pLowerConn,SessionStatus));
|
|
|
|
if (SendNegativeSessionResponse)
|
|
{
|
|
status = TcpSendSessionResponse(pLowerConn,
|
|
StatusCode,
|
|
SessionStatus);
|
|
}
|
|
|
|
// need to hold this lock if we are to un connect the lower and upper
|
|
// connnections
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
CTESpinLock(pLowerConn->pDeviceContext,OldIrq2);
|
|
CTESpinLock(pLowerConn,OldIrq);
|
|
|
|
CHECK_PTR(pLowerConn);
|
|
if ((pLowerConn->State < NBT_DISCONNECTING) &&
|
|
(pLowerConn->State > NBT_CONNECTING))
|
|
{
|
|
if (pLowerConn->State == NBT_SESSION_INBOUND)
|
|
{
|
|
//
|
|
// Previously, the LowerConnection was in the SESSION_INBOUND state
|
|
// hence we have to remove it from the WaitingForInbound Q and put
|
|
// it on the active LowerConnection list!
|
|
//
|
|
RemoveEntryList (&pLowerConn->Linkage);
|
|
InsertTailList (&pLowerConn->pDeviceContext->LowerConnection, &pLowerConn->Linkage);
|
|
InterlockedDecrement (&pLowerConn->pDeviceContext->NumWaitingForInbound);
|
|
//
|
|
// Change the RefCount Context to Connected!
|
|
//
|
|
NBT_SWAP_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_WAITING_INBOUND, REF_LOWC_CONNECTED, TRUE);
|
|
}
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTING);
|
|
SetStateProc( pLowerConn, RejectAnyData ) ;
|
|
|
|
pConnEle = pLowerConn->pUpperConnection;
|
|
if (pConnEle)
|
|
{
|
|
CHECK_PTR(pConnEle);
|
|
DerefConnEle = TRUE;
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
SET_STATE_UPPER (pConnEle, NBT_DISCONNECTING);
|
|
}
|
|
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
CTESpinFree(pLowerConn->pDeviceContext,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
SendTcpDisconnect((PVOID)pLowerConn);
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
CTESpinFree(pLowerConn->pDeviceContext,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
}
|
|
|
|
if (DerefConnEle)
|
|
{
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
FindSessionEndPoint(
|
|
IN PVOID pTsdu,
|
|
IN PVOID ConnectionContext,
|
|
IN ULONG BytesIndicated,
|
|
OUT tCLIENTELE **ppClientEle,
|
|
OUT PVOID *ppRemoteAddress,
|
|
OUT PULONG pRemoteAddressLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to find an end point on the node with the matching
|
|
net bios name. It is called at session setup time when a session request
|
|
PDU has arrived. The routine returns the Client Element ptr.
|
|
The JointLock is held prior to calling this routine!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS status;
|
|
tCLIENTELE *pClientEle;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
CHAR pName[NETBIOS_NAME_SIZE];
|
|
PUCHAR pScope;
|
|
tNAMEADDR *pNameAddr;
|
|
tADDRESSELE *pAddressEle;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pHead;
|
|
ULONG lNameSize;
|
|
tSESSIONREQ UNALIGNED *pSessionReq = (tSESSIONREQ UNALIGNED *)pTsdu;
|
|
ULONG sType;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
PUCHAR pSrcName;
|
|
BOOLEAN Found;
|
|
|
|
// get the ptr to the lower connection, and from that get the ptr to the
|
|
// upper connection block
|
|
pLowerConn = (tLOWERCONNECTION *)ConnectionContext;
|
|
|
|
if (pSessionReq->Hdr.Type != NBT_SESSION_REQUEST)
|
|
{
|
|
KdPrint (("Nbt.FindSessionEndPoint: WARNING!!! Rejecting Request, pSessionReq->Hdr.Type=<%d>!=<%d>\n",
|
|
pSessionReq->Hdr.Type, NBT_SESSION_REQUEST));
|
|
return(SESSION_UNSPECIFIED_ERROR);
|
|
}
|
|
|
|
// get the called name out of the PDU
|
|
status = ConvertToAscii ((PCHAR)&pSessionReq->CalledName,
|
|
BytesIndicated - FIELD_OFFSET(tSESSIONREQ,CalledName),
|
|
pName,
|
|
&pScope,
|
|
&lNameSize);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint (("Nbt.FindSessionEndPoint: WARNING!!! Rejecting Request, ConvertToAscii FAILed!\n"));
|
|
return(SESSION_UNSPECIFIED_ERROR);
|
|
}
|
|
|
|
|
|
// now try to find the called name in this node's Local table
|
|
//
|
|
|
|
//
|
|
// in case a disconnect came in while the spin lock was released
|
|
//
|
|
if (pLowerConn->State != NBT_SESSION_INBOUND)
|
|
{
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
pNameAddr = FindName (NBT_LOCAL,pName,pScope,&sType);
|
|
|
|
if (!pNameAddr)
|
|
{
|
|
return(SESSION_CALLED_NAME_NOT_PRESENT);
|
|
}
|
|
|
|
// we got to here because the name has resolved to a name on this node,
|
|
// so accept the Session setup.
|
|
//
|
|
pAddressEle = (tADDRESSELE *)pNameAddr->pAddressEle;
|
|
|
|
// lock the address structure until we find a client on the list
|
|
//
|
|
CTESpinLock(pAddressEle,OldIrq1);
|
|
|
|
if (IsListEmpty(&pAddressEle->ClientHead))
|
|
{
|
|
CTESpinFree(pAddressEle,OldIrq1);
|
|
return(SESSION_NOT_LISTENING_ON_CALLED_NAME);
|
|
}
|
|
|
|
//
|
|
// get the first client on the list that is bound to the same
|
|
// devicecontext as the connection, with a listen posted, or a valid
|
|
// Connect event handler setup -
|
|
//
|
|
Found = FALSE;
|
|
pHead = &pAddressEle->ClientHead;
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage);
|
|
|
|
CTESpinLock(pClientEle,OldIrq2);
|
|
if ((pClientEle->pDeviceContext == pLowerConn->pDeviceContext) &&
|
|
(NBT_VERIFY_HANDLE(pClientEle, NBT_VERIFY_CLIENT))) // Ensure that client is not going away!
|
|
{
|
|
//
|
|
// if there is a listen posted or a Connect Event Handler
|
|
// then allow the connect attempt to carry on, otherwise go to the
|
|
// next client in the list
|
|
//
|
|
if ((!IsListEmpty(&pClientEle->ListenHead)) ||
|
|
(pClientEle->ConEvContext))
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
CTESpinFree(pClientEle,OldIrq2);
|
|
|
|
pEntry = pEntry->Flink;
|
|
}
|
|
|
|
if (!Found)
|
|
{
|
|
CTESpinFree(pAddressEle,OldIrq1);
|
|
return(SESSION_NOT_LISTENING_ON_CALLED_NAME);
|
|
}
|
|
|
|
//
|
|
// Ensure we are calculating the Max Buffer size (3rd parameter) properly
|
|
// Bug# 126135
|
|
//
|
|
pSrcName = (PUCHAR)((PUCHAR)&pSessionReq->CalledName.NameLength + 1+lNameSize);
|
|
status = MakeRemoteAddressStructure(
|
|
pSrcName,
|
|
0,
|
|
BytesIndicated-(FIELD_OFFSET(tSESSIONREQ,CalledName.NameLength)+1+lNameSize),
|
|
ppRemoteAddress,
|
|
pRemoteAddressLength,
|
|
1);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
CTESpinFree(pClientEle,OldIrq2);
|
|
CTESpinFree(pAddressEle,OldIrq1);
|
|
|
|
KdPrint (("Nbt.FindSessionEndPoint: WARNING!!! Rejecting Request, MakeRemoteAddressStructure FAILed!\n"));
|
|
if (status == STATUS_INSUFFICIENT_RESOURCES)
|
|
{
|
|
return(SESSION_CALLED_NAME_PRESENT_NO_RESRC);
|
|
}
|
|
else
|
|
{
|
|
return(SESSION_UNSPECIFIED_ERROR);
|
|
}
|
|
}
|
|
|
|
// prevent the client from disappearing before we can indicate to him
|
|
//
|
|
NBT_REFERENCE_CLIENT(pClientEle);
|
|
|
|
CTESpinFree(pClientEle,OldIrq2);
|
|
CTESpinFree(pAddressEle,OldIrq1);
|
|
|
|
*ppClientEle = pClientEle;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
MakeRemoteAddressStructure(
|
|
IN PCHAR pHalfAsciiName,
|
|
IN PVOID pSourceAddr,
|
|
IN ULONG lMaxNameSize,
|
|
OUT PVOID *ppRemoteAddress,
|
|
OUT PULONG pRemoteAddressLength,
|
|
IN ULONG NumAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes up the remote addres structure with the netbios name
|
|
of the source in it, so that the info can be passed to the client...what
|
|
a bother to do this!
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ULONG lNameSize;
|
|
CHAR pName[NETBIOS_NAME_SIZE];
|
|
PUCHAR pScope;
|
|
PTA_NETBIOS_ADDRESS pRemoteAddress;
|
|
|
|
// make up the remote address data structure to pass to the client
|
|
status = ConvertToAscii(
|
|
pHalfAsciiName,
|
|
lMaxNameSize,
|
|
pName,
|
|
&pScope,
|
|
&lNameSize);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint (("Nbt.MakeRemoteAddressStructure: WARNING!!! Rejecting Request, ConvertToAscii FAILed!\n"));
|
|
return(status);
|
|
}
|
|
|
|
pRemoteAddress = (PTA_NETBIOS_ADDRESS)NbtAllocMem(
|
|
NumAddr * sizeof(TA_NETBIOS_ADDRESS),NBT_TAG('2'));
|
|
if (!pRemoteAddress)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
pRemoteAddress->TAAddressCount = NumAddr;
|
|
pRemoteAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS);
|
|
pRemoteAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
|
pRemoteAddress->Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
|
CTEMemCopy(pRemoteAddress->Address[0].Address[0].NetbiosName, pName,NETBIOS_NAME_SIZE);
|
|
|
|
*pRemoteAddressLength = FIELD_OFFSET(TA_NETBIOS_ADDRESS, Address[0].Address[0].NetbiosName[NETBIOS_NAME_SIZE]);
|
|
|
|
//
|
|
// Copy over the IP address also.
|
|
//
|
|
if (NumAddr == 2)
|
|
{
|
|
TA_ADDRESS *pTAAddr;
|
|
PTRANSPORT_ADDRESS pSourceAddress;
|
|
|
|
pSourceAddress = (PTRANSPORT_ADDRESS)pSourceAddr;
|
|
|
|
pTAAddr = (TA_ADDRESS *) (((PUCHAR)pRemoteAddress)
|
|
+ pRemoteAddress->Address[0].AddressLength
|
|
+ FIELD_OFFSET(TA_NETBIOS_ADDRESS, Address[0].Address));
|
|
|
|
pTAAddr->AddressLength = sizeof(TDI_ADDRESS_IP);
|
|
pTAAddr->AddressType = TDI_ADDRESS_TYPE_IP;
|
|
((TDI_ADDRESS_IP UNALIGNED *)&pTAAddr->Address[0])->in_addr = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr;
|
|
*pRemoteAddressLength += (FIELD_OFFSET(TA_ADDRESS, Address) + pTAAddr->AddressLength);
|
|
}
|
|
|
|
*ppRemoteAddress = (PVOID)pRemoteAddress;
|
|
// *pRemoteAddressLength = sizeof(TA_NETBIOS_ADDRESS);
|
|
// *pRemoteAddressLength = FIELD_OFFSET(TA_NETBIOS_ADDRESS, Address[0].Address[0]);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
ConnectHndlrNotOs (
|
|
IN PVOID pConnectionContext,
|
|
IN LONG RemoteAddressLength,
|
|
IN PVOID pRemoteAddress,
|
|
IN int UserDataLength,
|
|
IN VOID UNALIGNED *pUserData,
|
|
OUT CONNECTION_CONTEXT *ppConnectionId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the receive connect indication handler.
|
|
|
|
It is called when a TCP connection is being setup for a NetBios session.
|
|
It simply allocates a connection and returns that information to the
|
|
transport so that the connect indication can be accepted.
|
|
|
|
Arguments:
|
|
|
|
pClientEle - ptr to the connecition record for this session
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq;
|
|
PLIST_ENTRY pList;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
PTRANSPORT_ADDRESS pSrcAddress;
|
|
NTSTATUS Status;
|
|
|
|
pDeviceContext = (tDEVICECONTEXT *)pConnectionContext;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLockAtDpc(pDeviceContext);
|
|
|
|
// check that the source is an IP address
|
|
//
|
|
pSrcAddress = pRemoteAddress;
|
|
if ((pSrcAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP) ||
|
|
(IsListEmpty(&pDeviceContext->LowerConnFreeHead)) ||
|
|
((IsDeviceNetbiosless(pDeviceContext)) && // Bug # 282190
|
|
(!pDeviceContext->NumServers)))
|
|
{
|
|
if (pSrcAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP) {
|
|
NbtTrace(NBT_TRACE_INBOUND, ("Reject connection on pDeviceContext %p: %!ipaddr!",
|
|
pDeviceContext,
|
|
pDeviceContext->IpAddress));
|
|
} else {
|
|
NbtTrace(NBT_TRACE_INBOUND, ("Reject connection on pDeviceContext %p %!ipaddr! <== %!ipaddr!:%!port!",
|
|
pDeviceContext, pDeviceContext->IpAddress,
|
|
((PTDI_ADDRESS_IP)&pSrcAddress->Address[0].Address[0])->in_addr,
|
|
((PTDI_ADDRESS_IP)&pSrcAddress->Address[0].Address[0])->sin_port
|
|
));
|
|
}
|
|
Status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// get a free connection to the transport provider to accept this
|
|
// incoming connnection on.
|
|
//
|
|
pList = RemoveHeadList(&pDeviceContext->LowerConnFreeHead);
|
|
pLowerConn = CONTAINING_RECORD (pList,tLOWERCONNECTION,Linkage);
|
|
pLowerConn->bDisconnectIrpPendingInTCP = FALSE;
|
|
|
|
InterlockedDecrement (&pDeviceContext->NumFreeLowerConnections);
|
|
|
|
//
|
|
// Move the idle connection to the WaitingForInbound connection list
|
|
//
|
|
InsertTailList (&pDeviceContext->WaitingForInbound,pList);
|
|
InterlockedIncrement (&pDeviceContext->NumWaitingForInbound);
|
|
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_INBOUND);
|
|
SET_STATERCV_LOWER (pLowerConn, NORMAL, Inbound);
|
|
|
|
// increase the reference count because we are now connected. Decrement
|
|
// it when we disconnect.
|
|
//
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_WAITING_INBOUND);
|
|
pLowerConn->bOriginator = FALSE; // this end is NOT the originator
|
|
|
|
// save the source clients IP address into the connection Structure
|
|
// *TODO check if we need to do this or not
|
|
//
|
|
pLowerConn->SrcIpAddr = ((PTDI_ADDRESS_IP)&pSrcAddress->Address[0].Address[0])->in_addr;
|
|
*ppConnectionId = (PVOID)pLowerConn;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If there are less than 2 connections remaining, we allocate another one. The check
|
|
// below is for 0 or 1 connection.
|
|
// In order to protect ourselves from SYN ATTACKS, allocate NbtConfig.SpecialConnIncrement more now until
|
|
// a certain (registry config) value is exhausted (NOTE this number is global and not
|
|
// per device).
|
|
//
|
|
if (((pDeviceContext->NumFreeLowerConnections < NbtConfig.MinFreeLowerConnections) ||
|
|
(pDeviceContext->NumFreeLowerConnections < (pDeviceContext->TotalLowerConnections/10))) &&
|
|
(pDeviceContext->NumQueuedForAlloc < (2*NbtConfig.SpecialConnIncrement)))
|
|
{
|
|
KdPrint(("Nbt.ConnectHndlrNotOs: Queueing SpecialLowerConn: pDevice=<%p>, NumSpecialLowerConn=%d\n",
|
|
pDeviceContext, pDeviceContext->NumSpecialLowerConn));
|
|
NbtTrace(NBT_TRACE_INBOUND, ("pDeviceContext %p: Increase special lower connection to %d",
|
|
pDeviceContext, pDeviceContext->NumSpecialLowerConn));
|
|
|
|
#ifdef _PNP_POWER_
|
|
if (!NbtConfig.Unloading)
|
|
#endif // _PNP_POWER_
|
|
{
|
|
if (NT_SUCCESS (NTQueueToWorkerThread(
|
|
NULL,
|
|
DelayedAllocLowerConnSpecial,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
pDeviceContext,
|
|
TRUE)))
|
|
{
|
|
InterlockedExchangeAdd (&pDeviceContext->NumQueuedForAlloc, NbtConfig.SpecialConnIncrement);
|
|
} else {
|
|
NbtTrace(NBT_TRACE_INBOUND, ("Out of memory"));
|
|
}
|
|
}
|
|
}
|
|
|
|
CTESpinFreeAtDpc(pDeviceContext);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
DisconnectHndlrNotOs (
|
|
PVOID EventContext,
|
|
PVOID ConnectionContext,
|
|
ULONG DisconnectDataLength,
|
|
PVOID pDisconnectData,
|
|
ULONG DisconnectInformationLength,
|
|
PVOID pDisconnectInformation,
|
|
ULONG DisconnectIndicators
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the receive disconnect indication handler. It is called
|
|
by the transport when a connection disconnects. It checks the state of
|
|
the lower connection and basically returns a disconnect request to the
|
|
transport, except in the case where there is an active session. In this
|
|
case it calls the the clients disconnect indication handler. The client
|
|
then turns around and calls NbtDisconnect(in some cases), which passes a disconnect
|
|
back to the transport. The transport won't disconnect until it receives
|
|
a disconnect request from its client (NBT). If the flag TDI_DISCONNECT_ABORT
|
|
is set then there is no need to pass back a disconnect to the transport.
|
|
|
|
Since the client doesn't always issue a disconnect (i.e. the server),
|
|
this routine always turns around and issues a disconnect to the transport.
|
|
In the disconnect done handling, the lower connection is put back on the
|
|
free list if it is an inbound connection. For out bound connection the
|
|
lower and upper connections are left connected, since these will always
|
|
receive a cleanup and close connection from the client (i.e. until the
|
|
client does a close the lower connection will not be freed for outbound
|
|
connections).
|
|
|
|
Arguments:
|
|
|
|
pClientEle - ptr to the connecition record for this session
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq2;
|
|
CTELockHandle OldIrq3;
|
|
CTELockHandle OldIrq4;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tCONNECTELE *pConnectEle;
|
|
tCLIENTELE *pClientEle;
|
|
enum eSTATE state, stateLower;
|
|
BOOLEAN CleanupLower=FALSE;
|
|
PIRP pIrp= NULL;
|
|
PIRP pIrpClose= NULL;
|
|
PIRP pIrpRcv= NULL;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
tTIMERQENTRY *pTimerEntry;
|
|
BOOLEAN InsertOnList=FALSE;
|
|
BOOLEAN DisconnectIt=FALSE;
|
|
BOOLEAN bDisconnecting = FALSE;
|
|
ULONG StateRcv;
|
|
COMPLETIONCLIENT pCompletion;
|
|
tDEVICECONTEXT *pDeviceContext = NULL;
|
|
|
|
pLowerConn = (tLOWERCONNECTION *)ConnectionContext;
|
|
pConnectEle = pLowerConn->pUpperConnection;
|
|
PUSH_LOCATION(0x63);
|
|
|
|
CHECK_PTR(pLowerConn);
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DisconnectHndlrNotOs: Disc Indication, LowerConn state = %X %X\n",
|
|
pLowerConn->State,pLowerConn));
|
|
|
|
NbtTrace(NBT_TRACE_DISCONNECT, ("Disconnection Indication: pLowerConn %p pConnEle %p",
|
|
pLowerConn, pConnectEle));
|
|
|
|
// get the current state with the spin lock held to avoid a race condition
|
|
// with the client disconnecting
|
|
//
|
|
if (pConnectEle)
|
|
{
|
|
CHECK_PTR(pConnectEle);
|
|
if (!NBT_VERIFY_HANDLE2 (pConnectEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
|
|
{
|
|
ASSERTMSG ("Nbt.DisconnectHndlrNotOs: Disconnect indication after already disconnected!!\n", 0);
|
|
return STATUS_UNSUCCESSFUL ;
|
|
}
|
|
|
|
//
|
|
// We got a case where the ClientEle ptr was null. This shd not happen since the
|
|
// connection shd be associated at this time.
|
|
// Assert for that case to track this better.
|
|
//
|
|
pClientEle = pConnectEle->pClientEle;
|
|
CHECK_PTR(pClientEle);
|
|
if (!NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
|
|
{
|
|
ASSERTMSG ("Nbt.DisconnectHndlrNotOs: Bad Client Handle!!\n", 0);
|
|
return STATUS_UNSUCCESSFUL ;
|
|
}
|
|
|
|
// need to hold the joint lock if unconnecting the lower and upper
|
|
// connections.
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq4);
|
|
|
|
CTESpinLock(pClientEle,OldIrq3);
|
|
CTESpinLock(pConnectEle,OldIrq2);
|
|
CTESpinLock(pLowerConn,OldIrq);
|
|
|
|
NbtTrace(NBT_TRACE_DISCONNECT, ("Disconnection Indication: pLowerConn %p pConnEle %p"
|
|
" %!NBTNAME!<%02x> <==> %!NBTNAME!<%02x>",
|
|
pLowerConn, pConnectEle,
|
|
pClientEle->pAddress->pNameAddr->Name,
|
|
(unsigned)pClientEle->pAddress->pNameAddr->Name[15],
|
|
pConnectEle->RemoteName,
|
|
(unsigned)pConnectEle->RemoteName[15]
|
|
));
|
|
|
|
state = pConnectEle->state;
|
|
stateLower = pLowerConn->State;
|
|
|
|
#ifdef VXD
|
|
DbgPrint("DisconnectHndlrNotOs: pConnectEle->state = 0x") ;
|
|
DbgPrintNum( (ULONG) state ) ;
|
|
DbgPrint("pLowerConn->state = 0x") ; DbgPrintNum( (ULONG) stateLower ) ;
|
|
DbgPrint("\r\n") ;
|
|
#endif
|
|
|
|
if ((state > NBT_ASSOCIATED) && (state < NBT_DISCONNECTING))
|
|
{
|
|
|
|
PUSH_LOCATION(0x63);
|
|
CHECK_PTR(pConnectEle);
|
|
//
|
|
// this irp gets returned to the client below in the case statement
|
|
// Except in the connecting state where the transport still has
|
|
// the irp. In that case we let SessionStartupContinue complete
|
|
// the irp.
|
|
//
|
|
if ((pConnectEle->pIrp) && (state > NBT_CONNECTING))
|
|
{
|
|
pIrp = pConnectEle->pIrp;
|
|
pConnectEle->pIrp = NULL;
|
|
}
|
|
|
|
//
|
|
// if there is a receive irp, get it out of pConnEle since pConnEle
|
|
// will be requeued and could get used again before we try to
|
|
// complete this irp down below. Null the cancel routine if not
|
|
// cancelled and just complete it below.
|
|
//
|
|
if (((state == NBT_SESSION_UP) || (state == NBT_SESSION_WAITACCEPT))
|
|
&& (pConnectEle->pIrpRcv))
|
|
{
|
|
CTELockHandle OldIrql;
|
|
|
|
pIrpRcv = pConnectEle->pIrpRcv;
|
|
|
|
#ifndef VXD
|
|
IoAcquireCancelSpinLock(&OldIrql);
|
|
//
|
|
// if its already cancelled then don't complete it again
|
|
// down below
|
|
//
|
|
if (pIrpRcv->Cancel)
|
|
{
|
|
pIrpRcv = NULL;
|
|
}
|
|
else
|
|
{
|
|
IoSetCancelRoutine(pIrpRcv,NULL);
|
|
}
|
|
IoReleaseCancelSpinLock(OldIrql);
|
|
#endif
|
|
pConnectEle->pIrpRcv = NULL;
|
|
}
|
|
|
|
// This irp is used for DisconnectWait
|
|
//
|
|
if (pIrpClose = pConnectEle->pIrpClose)
|
|
{
|
|
pConnectEle->pIrpClose = NULL;
|
|
}
|
|
|
|
#ifdef VXD
|
|
if ( pLowerConn->StateRcv == PARTIAL_RCV &&
|
|
(pLowerConn->fOnPartialRcvList == TRUE) )
|
|
{
|
|
RemoveEntryList( &pLowerConn->PartialRcvList ) ;
|
|
pLowerConn->fOnPartialRcvList = FALSE;
|
|
InitializeListHead(&pLowerConn->PartialRcvList);
|
|
}
|
|
#endif
|
|
|
|
SET_STATE_UPPER (pConnectEle, NBT_ASSOCIATED);
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTING);
|
|
NBT_DISASSOCIATE_CONNECTION (pConnectEle, pLowerConn);
|
|
|
|
//
|
|
// save whether it is a disconnect abort or disconnect release
|
|
// in case the client does a disconnect wait and wants the
|
|
// real disconnect status i.e. they do not have a disconnect
|
|
// indication handler
|
|
//
|
|
pConnectEle->DiscFlag = (UCHAR)DisconnectIndicators;
|
|
|
|
//
|
|
// pConnectEle is dereferenced below, now that the lower conn
|
|
// no longer points to it.
|
|
//
|
|
InsertOnList = TRUE;
|
|
DisconnectIt = TRUE;
|
|
|
|
//
|
|
// put pConnEle back on the list of idle connection for this
|
|
// client
|
|
//
|
|
RemoveEntryList(&pConnectEle->Linkage);
|
|
InsertTailList(&pClientEle->ConnectHead,&pConnectEle->Linkage);
|
|
|
|
if (DisconnectIndicators == TDI_DISCONNECT_RELEASE)
|
|
{
|
|
// setting the state to disconnected will allow the DisconnectDone
|
|
// routine in updsend.c to Delayedcleanupafterdisconnect, when the disconnect
|
|
// completes (since we have been indicated)
|
|
//
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTED);
|
|
}
|
|
else
|
|
{
|
|
// there is no disconnect completion to wait for ...since it
|
|
// was an abortive disconnect indication
|
|
// Change the state of the lower conn incase the client has
|
|
// done a disconnect at the same time - we don't want DisconnectDone
|
|
// to also Queue up DelayedCleanupAfterDisconnect
|
|
//
|
|
SET_STATE_LOWER (pLowerConn, NBT_IDLE);
|
|
}
|
|
}
|
|
//
|
|
// the lower connection just went from disconnecting to disconnected
|
|
// so change the state - this signals the DisconnectDone routine to
|
|
// cleanup the connection when the disconnect request completes.
|
|
//
|
|
if (stateLower == NBT_DISCONNECTING)
|
|
{
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTED);
|
|
bDisconnecting = pLowerConn->bDisconnectIrpPendingInTCP;
|
|
}
|
|
else if (stateLower == NBT_DISCONNECTED)
|
|
{
|
|
//
|
|
// we get to here if the disconnect request Irp completes before the
|
|
// disconnect indication comes back. The disconnect completes
|
|
// and checks the state, changing it to Disconnected if it
|
|
// isn't already - see disconnectDone in udpsend.c. Since
|
|
// the disconnect indication and the disconnect completion
|
|
// have occurred, cleanup the connection.
|
|
//
|
|
CleanupLower = TRUE;
|
|
|
|
// this is just a precaution that may not be needed, so we
|
|
// don't queue the cleanup twice...i.e. now that the lower state
|
|
// is disconnected, we change it's state to idle incase the
|
|
// transport hits us again with another disconnect indication.
|
|
// QueueCleanup is called below based on the value in statelower
|
|
SET_STATE_LOWER (pLowerConn, NBT_IDLE);
|
|
}
|
|
//
|
|
// During the time window that a connection is being setup and TCP
|
|
// completes the connect irp, we could get a disconnect indication.
|
|
// the RefCount must be incremented here so that DelayedCleanupAfterDisconnect
|
|
// does not delete the connection (i.e. it expects refcount >= 2).
|
|
//
|
|
if (stateLower <= NBT_CONNECTING)
|
|
{
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED);
|
|
}
|
|
|
|
#if DBG
|
|
if (bDisconnecting && (DisconnectIt || CleanupLower)) {
|
|
ASSERT(pLowerConn->pIrp);
|
|
KdPrint(("Nbt.DisconnectHndlrNotOs: Irp 0x%08lx\n", pLowerConn->pIrp));
|
|
}
|
|
#endif
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
CTESpinFree(pConnectEle,OldIrq2);
|
|
CTESpinFree(pClientEle,OldIrq3);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq4);
|
|
}
|
|
else
|
|
{
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq2);
|
|
CTESpinLock(pLowerConn->pDeviceContext,OldIrq3);
|
|
CTESpinLock(pLowerConn,OldIrq);
|
|
stateLower = pLowerConn->State;
|
|
state = NBT_IDLE;
|
|
|
|
if ((stateLower > NBT_IDLE) && (stateLower < NBT_DISCONNECTING))
|
|
{
|
|
// flag so we send back a disconnect to the transport
|
|
DisconnectIt = TRUE;
|
|
|
|
if (stateLower == NBT_SESSION_INBOUND)
|
|
{
|
|
//
|
|
// Previously, the LowerConnection was in the SESSION_INBOUND state
|
|
// hence we have to remove it from the WaitingForInbound Q and put
|
|
// it on the active LowerConnection list!
|
|
//
|
|
ASSERT (pLowerConn->State == NBT_SESSION_INBOUND);
|
|
RemoveEntryList (&pLowerConn->Linkage);
|
|
InsertTailList (&pLowerConn->pDeviceContext->LowerConnection, &pLowerConn->Linkage);
|
|
InterlockedDecrement (&pLowerConn->pDeviceContext->NumWaitingForInbound);
|
|
//
|
|
// Change the RefCount Context to Connected!
|
|
//
|
|
NBT_SWAP_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_WAITING_INBOUND, REF_LOWC_CONNECTED, TRUE);
|
|
}
|
|
//
|
|
// set state so that DisconnectDone will cleanup the connection.
|
|
//
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTED);
|
|
//
|
|
// for an abortive disconnect we will do a cleanup below, so
|
|
// set the state to idle here
|
|
//
|
|
if (DisconnectIndicators != TDI_DISCONNECT_RELEASE)
|
|
{
|
|
SET_STATE_LOWER (pLowerConn, NBT_IDLE);
|
|
}
|
|
}
|
|
else if (stateLower == NBT_DISCONNECTING)
|
|
{
|
|
// a Disconnect has already been initiated by this side so when
|
|
// DisconnectDone runs it will cleanup
|
|
//
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTED);
|
|
bDisconnecting = pLowerConn->bDisconnectIrpPendingInTCP;
|
|
}
|
|
else if ( stateLower == NBT_DISCONNECTED )
|
|
{
|
|
CleanupLower = TRUE;
|
|
SET_STATE_LOWER (pLowerConn, NBT_IDLE);
|
|
}
|
|
|
|
//
|
|
// During the time window that a connection is being setup and TCP
|
|
// completes the connect irp, we could get a disconnect indication.
|
|
// the RefCount must be incremented here so that DelayedCleanupAfterDisconnect
|
|
// does not delete the connection (i.e. it expects refcount >= 2).
|
|
//
|
|
if ((stateLower <= NBT_CONNECTING) &&
|
|
(stateLower > NBT_IDLE))
|
|
{
|
|
NBT_REFERENCE_LOWERCONN(pLowerConn, REF_LOWC_CONNECTED);
|
|
}
|
|
|
|
#if DBG
|
|
if (bDisconnecting && (DisconnectIt || CleanupLower)) {
|
|
ASSERT(pLowerConn->pIrp);
|
|
KdPrint(("Nbt.DisconnectHndlrNotOs: Irp 0x%08lx\n", pLowerConn->pIrp));
|
|
}
|
|
#endif
|
|
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
CTESpinFree(pLowerConn->pDeviceContext,OldIrq3);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
}
|
|
|
|
StateRcv = pLowerConn->StateRcv;
|
|
SetStateProc (pLowerConn, RejectAnyData);
|
|
|
|
if (!bDisconnecting && DisconnectIt)
|
|
{
|
|
if (DisconnectIndicators == TDI_DISCONNECT_RELEASE)
|
|
{
|
|
// this disconnects the connection and puts the lowerconn back on
|
|
// its free queue. Note that OutOfRsrcKill calls this routine too
|
|
// with the DisconnectIndicators set to Abort, and the code correctly
|
|
// does not attempt to disconnect the connection since the OutOfRsrc
|
|
// routine had already disconnected it.
|
|
//
|
|
PUSH_LOCATION(0x6d);
|
|
status = SendTcpDisconnect((PVOID)pLowerConn);
|
|
}
|
|
else
|
|
{
|
|
// this is an abortive disconnect from the transport, so there is
|
|
// no need to send a disconnect request back to the transport.
|
|
// So we set a flag that tells us later in the routine to close
|
|
// the lower connection.
|
|
//
|
|
PUSH_LOCATION(0x69);
|
|
CleanupLower = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// for an orderly release, turn around and send a release to the transport
|
|
// if there is no client attached to the lower connection. If there is a
|
|
// client then we must pass the disconnect indication to the client and
|
|
// wait for the client to do the disconnect.
|
|
//
|
|
//
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DisconnectHndlrNotOs: ConnEle=<%p>, state = %x\n", pConnectEle, state));
|
|
|
|
switch (state)
|
|
{
|
|
case NBT_SESSION_INBOUND:
|
|
case NBT_CONNECTING:
|
|
|
|
// if an originator, then the upper and lower connections are
|
|
// already associated, and there is a client irp to return.
|
|
// (NBT_SESSION_CONNECTING only)
|
|
//
|
|
if (pIrp)
|
|
{
|
|
if (pLowerConn->bOriginator)
|
|
{
|
|
CTEIoComplete(pIrp, STATUS_BAD_NETWORK_PATH, 0);
|
|
}
|
|
else
|
|
{
|
|
// this could be an inbound call that could not send the
|
|
// session response correctly.
|
|
//
|
|
CTEIoComplete(pIrp, STATUS_UNSUCCESSFUL, 0);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case NBT_SESSION_OUTBOUND:
|
|
//
|
|
//
|
|
// Stop the timer started in SessionStartupCompletion to time the
|
|
// Session Setup Response message
|
|
//
|
|
// NbtConnect stores the tracker in the IrpRcv ptr so that this
|
|
// routine can access it
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pConnectEle,OldIrq2);
|
|
|
|
//
|
|
// check if anyone else has freed the tracker yet.
|
|
//
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pConnectEle->pIrpRcv;
|
|
CHECK_PTR(pTracker);
|
|
if (pTracker)
|
|
{
|
|
//
|
|
// We received a Disconnect from Tcp while waiting in
|
|
// the Outbound state!
|
|
//
|
|
pConnectEle->pIrpRcv = NULL;
|
|
pTimerEntry = pTracker->pTimer;
|
|
pTracker->pTimer = NULL;
|
|
|
|
CTESpinFree(pConnectEle,OldIrq2);
|
|
|
|
//
|
|
// if the timer has expired it will not cleanup because the state
|
|
// will not be SESSION_OUTBOUND, since we changed it above to
|
|
// disconnected. So we always have to complete the irp and
|
|
// call Delayedcleanupafterdisconnect below.
|
|
//
|
|
if (pTimerEntry)
|
|
{
|
|
StopTimer(pTimerEntry,&pCompletion,NULL);
|
|
}
|
|
|
|
//
|
|
// Check if the SessionStartupCompletion has run; if so, RefConn will be 0.
|
|
// Else, decrement so that the tracker goes away when the session send completes.
|
|
//
|
|
if (pTracker->RefConn == 0)
|
|
{
|
|
if ((pTracker->pNameAddr->Verify == REMOTE_NAME) && // Remote names only!
|
|
(pTracker->pNameAddr->NameTypeState & STATE_RESOLVED) &&
|
|
(pTracker->pNameAddr->RefCount == 2))
|
|
{
|
|
//
|
|
// If no one else is referencing the name, then delete it from
|
|
// the hash table.
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_REMOTE, TRUE);
|
|
}
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
}
|
|
else
|
|
{
|
|
pTracker->RefConn--;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pConnectEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
|
|
if (pIrp)
|
|
{
|
|
CTEIoComplete(pIrp,STATUS_REMOTE_NOT_LISTENING,0);
|
|
}
|
|
|
|
break;
|
|
|
|
case NBT_SESSION_WAITACCEPT:
|
|
case NBT_SESSION_UP:
|
|
|
|
if (pIrp)
|
|
{
|
|
CTEIoComplete(pIrp,STATUS_CANCELLED,0);
|
|
}
|
|
//
|
|
// check for any RcvIrp that may be still around. If the
|
|
// transport has the Irp now then pIrpRcv = NULL. There should
|
|
// be no race condition between completing it and CompletionRcv
|
|
// setting pIrpRcv again as long as we cannot be indicated
|
|
// for a disconnect during completion of a Receive. In any
|
|
// case the access is coordinated using the Io spin lock io
|
|
// IoCancelIrp - that routine will only complete the irp once,
|
|
// then it nulls the completion routine.
|
|
//
|
|
if ((StateRcv == FILL_IRP) && pIrpRcv)
|
|
{
|
|
|
|
PUSH_LOCATION(0x6f);
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DisconnectHndlrNotOs: Cancelling RcvIrp on Disconnect Indication!!!\n"));
|
|
|
|
CTEIoComplete(pIrpRcv,STATUS_CANCELLED,0);
|
|
}
|
|
|
|
//
|
|
// this is a disconnect for an active session, so just inform the client
|
|
// and then it issues a Nbtdisconnect. We have already disconnected the
|
|
// lowerconnection with the transport, so all that remains is
|
|
// to cleanup for outgoing calls.
|
|
//
|
|
|
|
pClientEle = pConnectEle->pClientEle;
|
|
|
|
// now call the client's disconnect handler...NBT always does
|
|
// a abortive disconnect - i.e. the connection is closed when
|
|
// the disconnect indication occurs and does not REQUIRE a
|
|
// disconnect from the client to finish the job.( a disconnect
|
|
// from the client will not hurt though.
|
|
//
|
|
PUSH_LOCATION(0x64);
|
|
if ((pClientEle) &&
|
|
(pClientEle->evDisconnect ) &&
|
|
(!pIrpClose))
|
|
{
|
|
status = (*pClientEle->evDisconnect)(pClientEle->DiscEvContext,
|
|
pConnectEle->ConnectContext,
|
|
DisconnectDataLength,
|
|
pDisconnectData,
|
|
DisconnectInformationLength,
|
|
pDisconnectInformation,
|
|
TDI_DISCONNECT_ABORT);
|
|
NbtTrace(NBT_TRACE_DISCONNECT, ("Client disconnect handler returns %!status!", status));
|
|
}
|
|
else if (pIrpClose)
|
|
{
|
|
//
|
|
// the Client has issued a disconnect Wait irp, so complete
|
|
// it now, indicating to them that a disconnect has occurred.
|
|
//
|
|
if (DisconnectIndicators == TDI_DISCONNECT_RELEASE)
|
|
{
|
|
status = STATUS_GRACEFUL_DISCONNECT;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_CONNECTION_RESET;
|
|
}
|
|
|
|
CTEIoComplete(pIrpClose,status,0);
|
|
}
|
|
|
|
//
|
|
// return any rcv buffers that have been posted
|
|
//
|
|
CTESpinLock(pConnectEle,OldIrq);
|
|
FreeRcvBuffers(pConnectEle,&OldIrq);
|
|
CTESpinFree(pConnectEle,OldIrq);
|
|
|
|
break;
|
|
|
|
case NBT_DISCONNECTING:
|
|
// the retry session setup code expects the state to change
|
|
// to disconnected when the disconnect indication comes
|
|
// from the wire
|
|
SET_STATE_UPPER (pConnectEle, NBT_DISCONNECTED);
|
|
|
|
case NBT_DISCONNECTED:
|
|
case NBT_ASSOCIATED:
|
|
case NBT_IDLE:
|
|
|
|
//
|
|
// catch all other cases here to be sure the connect irp gets
|
|
// returned.
|
|
//
|
|
if (pIrp)
|
|
{
|
|
CTEIoComplete(pIrp,STATUS_CANCELLED,0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG("Nbt:Disconnect indication in unexpected state\n",0);
|
|
|
|
}
|
|
|
|
if (InsertOnList)
|
|
{
|
|
// undo the reference done when the NbtConnect Ran - this may cause
|
|
// pConnEle to be deleted if the Client had issued an NtClose before
|
|
// this routine ran. We only do this dereference if InsertOnList is
|
|
// TRUE, meaning that we just "unhooked" the lower from the Upper.
|
|
NBT_DEREFERENCE_CONNECTION (pConnectEle, REF_CONN_CONNECT);
|
|
}
|
|
|
|
|
|
// this either puts the lower connection back on its free
|
|
// queue if inbound, or closes the connection with the transport
|
|
// if out bound. (it can't be done at dispatch level).
|
|
//
|
|
if (!bDisconnecting && CleanupLower)
|
|
{
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DisconnectHndlrNotOs: Calling Worker thread to Cleanup %X\n",pLowerConn));
|
|
|
|
CTESpinLock(pLowerConn,OldIrq);
|
|
|
|
if ( pLowerConn->pIrp )
|
|
{
|
|
PCTE_IRP pIrp;
|
|
|
|
pIrp = pLowerConn->pIrp;
|
|
CHECK_PTR(pLowerConn);
|
|
pLowerConn->pIrp = NULL ;
|
|
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
// this is the irp to complete when the disconnect completes - essentially
|
|
// the irp requesting the disconnect.
|
|
CTEIoComplete( pIrp, STATUS_SUCCESS, 0 ) ;
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
|
ASSERT (pLowerConn->RefCount > 1);
|
|
|
|
if (NBT_VERIFY_HANDLE (pLowerConn->pDeviceContext, NBT_VERIFY_DEVCONTEXT))
|
|
{
|
|
pDeviceContext = pLowerConn->pDeviceContext;
|
|
}
|
|
|
|
status = NTQueueToWorkerThread(
|
|
&pLowerConn->WorkItemCleanUpAndWipeOut,
|
|
DelayedCleanupAfterDisconnect,
|
|
NULL,
|
|
pLowerConn,
|
|
NULL,
|
|
pDeviceContext,
|
|
TRUE);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DelayedCleanupAfterDisconnect(
|
|
IN tDGRAM_SEND_TRACKING *pUnused1,
|
|
IN PVOID pClientContext,
|
|
IN PVOID Unused2,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles freeing lowerconnection data structures back to the
|
|
transport, by calling NTclose (outbound only) or by putting the connection
|
|
back on the connection free queue (inbound only). For the NT case this
|
|
routine runs within the context of an excutive worker thread.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
PIRP pIrp=NULL;
|
|
|
|
pLowerConn = (tLOWERCONNECTION*) pClientContext;
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DelayedCleanupAfterDisconnect: Originator= %X, pLowerConn=%X\n",
|
|
pLowerConn->bOriginator,pLowerConn));
|
|
|
|
//
|
|
// DEBUG to catch upper connections being put on lower conn QUEUE
|
|
//
|
|
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
|
ASSERT (pLowerConn->RefCount > 1);
|
|
ASSERT (pLowerConn->pUpperConnection == NULL);
|
|
|
|
if (!pLowerConn->bOriginator)
|
|
{
|
|
// ******** THIS WAS AN INCOMING CONNECTION *************
|
|
|
|
//
|
|
// Inbound lower connections just get put back on the queue, whereas
|
|
// outbound connections simply get closed.
|
|
//
|
|
if (pLowerConn->SpecialAlloc)
|
|
{
|
|
//
|
|
// Connections allocated due to SynAttack backlog measures are not re-allocated
|
|
// If this was a special connection block, decrement the count of such connections
|
|
//
|
|
if (pDeviceContext)
|
|
{
|
|
InterlockedDecrement(&pDeviceContext->NumSpecialLowerConn);
|
|
KdPrint(("Nbt.DelayedCleanupAfterDisconnect: pDevice=<%p>, NumSpecialLowerConn= %d\n",
|
|
pDeviceContext, pDeviceContext->NumSpecialLowerConn));
|
|
}
|
|
}
|
|
else if (pDeviceContext)
|
|
{
|
|
//
|
|
// Always close the connection and then Create another since there
|
|
// could be a Rcv Irp in TCP still that will be returned at some
|
|
// later time, perhaps after this connection gets reused again.
|
|
// In that case the Rcv Irp could be lost.
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DelayedCleanupAfterDisconnect: LowerConn=<%x>, State=<%x>\n",
|
|
pLowerConn, pLowerConn->State));
|
|
|
|
if (pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT) {
|
|
DelayedAllocLowerConn (NULL, NULL, NULL, pDeviceContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
// this deref removes the reference added when the connection
|
|
// connnected. When NbtDeleteLowerConn is called it dereferences
|
|
// one more time which delete the memory.
|
|
//
|
|
CHECK_PTR (pLowerConn);
|
|
ASSERT (pLowerConn->RefCount >= 2);
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED, FALSE);
|
|
|
|
// this does a close on the lower connection, so it can go ahead
|
|
// possibly before the disconnect has completed since the transport
|
|
// will not complete the close until is completes the disconnect.
|
|
//
|
|
status = NbtDeleteLowerConn(pLowerConn);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
AllocLowerConn(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PVOID pDeviceSpecial
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a lowerconn block that will go on the lowerconnfreehead.
|
|
|
|
Arguments:
|
|
|
|
pDeviceContext - the device context
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
|
|
/*
|
|
* This should be ok since NbtOpenAndAssocConnection call NbtTdiOpenConnection which is PAGEABLE.
|
|
* Make sure we are at the right IRQL.
|
|
*/
|
|
CTEPagedCode();
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
if (pDeviceContext->Verify != NBT_VERIFY_DEVCONTEXT) {
|
|
ASSERT (pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT_DOWN);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return;
|
|
}
|
|
if (pDeviceSpecial)
|
|
{
|
|
status = NbtOpenAndAssocConnection(pDeviceContext, NULL, &pLowerConn, '0');
|
|
}
|
|
else
|
|
{
|
|
status = NbtOpenAndAssocConnection(pDeviceContext, NULL, NULL, '1');
|
|
}
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
if (pDeviceSpecial)
|
|
{
|
|
//
|
|
// Special lowerconn for Syn attacks
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
pLowerConn->SpecialAlloc = TRUE;
|
|
InterlockedIncrement(&pDeviceContext->NumSpecialLowerConn);
|
|
}
|
|
|
|
InterlockedDecrement(&pDeviceContext->NumQueuedForAlloc);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DelayedAllocLowerConn(
|
|
IN tDGRAM_SEND_TRACKING *pUnused1,
|
|
IN PVOID pDeviceSpecial,
|
|
IN PVOID pUnused3,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If lowerconn couldn't be alloced in AllocLowerConn, an event is scheduled
|
|
so that we can retry later. Well, this is "later"!
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
AllocLowerConn(pDeviceContext, pDeviceSpecial);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DelayedAllocLowerConnSpecial(
|
|
IN tDGRAM_SEND_TRACKING *pUnused1,
|
|
IN PVOID pUnused2,
|
|
IN PVOID pUnused3,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If lowerconn couldn't be alloced in AllocLowerConn, an event is scheduled
|
|
so that we can retry later. Well, this is "later"!
|
|
|
|
This is for SYN-ATTACK, so we shd create more than one to beat the incoming
|
|
requests. Create three at a time - this shd be controllable thru' registry.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
|
|
if (pDeviceContext->Verify != NBT_VERIFY_DEVCONTEXT) {
|
|
ASSERT (pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT_DOWN);
|
|
return;
|
|
}
|
|
|
|
KdPrint(("Nbt.DelayedAllocLowerConnSpecial: Allocing spl. %d lowerconn...\n",
|
|
NbtConfig.SpecialConnIncrement));
|
|
|
|
//
|
|
// Alloc SpecialConnIncrement number of more connections.
|
|
//
|
|
for (i=0; i<NbtConfig.SpecialConnIncrement; i++)
|
|
{
|
|
DelayedAllocLowerConn(NULL, pDeviceContext, NULL, pDeviceContext);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
AddToRemoteHashTbl (
|
|
IN tDGRAMHDR UNALIGNED *pDgram,
|
|
IN ULONG BytesIndicated,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds the source address of an inbound datagram to the remote
|
|
hash table so that it can be used for subsequent return sends to that node.
|
|
|
|
This routine does not need to be called if the datagram message type is
|
|
Broadcast datagram, since these are sends to the broadcast name '*' and
|
|
there is no send caching this source name
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
tNAMEADDR *pNameAddr;
|
|
CTELockHandle OldIrq;
|
|
UCHAR pName[NETBIOS_NAME_SIZE];
|
|
NTSTATUS status;
|
|
LONG Length;
|
|
ULONG SrcIpAddr;
|
|
PUCHAR pScope;
|
|
DWORD dwTimeOutCount = 0;
|
|
|
|
//
|
|
// source ip addr should never be 0. This is a workaround for UB's NBDD
|
|
// which forwards the datagram, but client puts 0 in SourceIpAddr field
|
|
// of the datagram, we cache 0 and then end up doing a broadcast when we
|
|
// really meant to do a directed datagram to the sender.
|
|
//
|
|
SrcIpAddr = ntohl(pDgram->SrcIpAddr);
|
|
if ((!SrcIpAddr) ||
|
|
(!NT_SUCCESS (status = ConvertToAscii ((PCHAR)&pDgram->SrcName,
|
|
(BytesIndicated - FIELD_OFFSET(tDGRAMHDR,SrcName)),
|
|
pName,
|
|
&pScope,
|
|
&Length))))
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (NbtConfig.InboundDgramNameCacheTimeOutCount > 1) {
|
|
LONG lRand = 0;
|
|
LONG lCurrent = 0;
|
|
PLIST_ENTRY pEntry = NULL;
|
|
PLIST_ENTRY pHead = NULL;
|
|
tNAMEADDR *pNameAddr = NULL;
|
|
|
|
if (NbtConfig.NumNameCached >= NbtConfig.MaxNumNameCached) {
|
|
|
|
lRand = ((GetRandomNumber(&NbtConfig.RandomNumberSeed)) % NbtConfig.pRemoteHashTbl->lNumBuckets);
|
|
|
|
lCurrent = lRand;
|
|
while (NbtConfig.NumNameCached >= NbtConfig.MaxNumNameCached) {
|
|
|
|
//
|
|
// Walk from the tail of the list since the oldest ones are at the end
|
|
//
|
|
pHead = &NbtConfig.pRemoteHashTbl->Bucket[lCurrent];
|
|
pEntry = pHead->Blink;
|
|
while (pEntry != pHead && NbtConfig.NumNameCached >= NbtConfig.MaxNumNameCached) {
|
|
pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage);
|
|
pEntry = pEntry->Blink;
|
|
if ((pNameAddr->NameTypeState & (STATE_RESOLVED | STATE_RELEASED)) &&
|
|
(pNameAddr->RefCount <= 1) &&
|
|
(pNameAddr->NameAddFlags & NAME_RESOLVED_BY_DGRAM_IN)) {
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE);
|
|
pNameAddr = NULL;
|
|
}
|
|
}
|
|
|
|
lCurrent++;
|
|
if (lCurrent >= NbtConfig.pRemoteHashTbl->lNumBuckets) {
|
|
lCurrent = 0;
|
|
}
|
|
|
|
if (lCurrent == lRand) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (NbtConfig.NumNameCached >= NbtConfig.MaxNumNameCached) {
|
|
//
|
|
// Drop the current name if it is still too high
|
|
//
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add the name to the remote cache.
|
|
//
|
|
status = AddToHashTable(NbtConfig.pRemoteHashTbl,
|
|
pName,
|
|
pScope,
|
|
SrcIpAddr,
|
|
NBT_UNIQUE, // always a unique address since you can't send from a group name
|
|
NULL,
|
|
&pNameAddr,
|
|
pDeviceContext,
|
|
NAME_RESOLVED_BY_DGRAM_IN);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// we only want the name to be in the remote cache for the shortest
|
|
// timeout allowed by the remote cache timer, so set the timeout
|
|
// count to 1 which is 1-2 minutes.
|
|
//
|
|
|
|
// the name is already in the cache when Pending is returned,
|
|
// so just update the ip address in case it is different.
|
|
//
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
//
|
|
// If the name is resolved then it is ok to overwrite the
|
|
// ip address with the incoming one. But if it is resolving,
|
|
// then just let it continue resolving.
|
|
//
|
|
if ( (pNameAddr->NameTypeState & STATE_RESOLVED) &&
|
|
!(pNameAddr->NameTypeState & NAMETYPE_INET_GROUP))
|
|
{
|
|
pNameAddr->TimeOutCount = NbtConfig.InboundDgramNameCacheTimeOutCount;
|
|
// only set the adapter mask for this adapter since we are
|
|
// only sure that this adapter can reach the dest.
|
|
pNameAddr->AdapterMask = pDeviceContext->AdapterMask;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pNameAddr->TimeOutCount = NbtConfig.InboundDgramNameCacheTimeOutCount;
|
|
//
|
|
// change the state to resolved
|
|
//
|
|
pNameAddr->NameTypeState &= ~NAME_STATE_MASK;
|
|
pNameAddr->NameTypeState |= STATE_RESOLVED;
|
|
pNameAddr->AdapterMask |= pDeviceContext->AdapterMask;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
DgramHndlrNotOs (
|
|
IN PVOID ReceiveEventContext,
|
|
IN ULONG SourceAddrLength,
|
|
IN PVOID pSourceAddr,
|
|
IN ULONG OptionsLength,
|
|
IN PVOID pOptions,
|
|
IN ULONG ReceiveDatagramFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG pBytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PVOID *ppRcvBuffer,
|
|
IN tCLIENTLIST **ppClientList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the receive datagram event indication handler.
|
|
|
|
It is called when an a datgram arrives from the network. The code
|
|
checks the type of datagram and then tries to route the datagram to
|
|
the correct destination on the node.
|
|
|
|
This procedure is called with the spin lock held on pDeviceContext.
|
|
|
|
Arguments:
|
|
|
|
ppRcvbuffer will contain the IRP/NCB if only one client is listening,
|
|
NULL if multiple clients are listening
|
|
ppClientList will contain the list clients that need to be completed,
|
|
NULL if only one client is listening
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
NTSTATUS LocStatus;
|
|
tCLIENTELE *pClientEle;
|
|
tCLIENTELE *pClientEleToDeref = NULL;
|
|
tNAMEADDR *pNameAddr;
|
|
tADDRESSELE *pAddress;
|
|
ULONG RetNameType;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CHAR pName[NETBIOS_NAME_SIZE];
|
|
PUCHAR pScope;
|
|
ULONG lNameSize;
|
|
ULONG iLength = 0;
|
|
ULONG RemoteAddressLength;
|
|
PVOID pRemoteAddress;
|
|
tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)ReceiveEventContext;
|
|
tDGRAMHDR UNALIGNED *pDgram = (tDGRAMHDR UNALIGNED *)pTsdu;
|
|
ULONG lClientBytesTaken;
|
|
ULONG lDgramHdrSize;
|
|
PIRP pIrp;
|
|
BOOLEAN MoreClients;
|
|
BOOLEAN UsingClientBuffer;
|
|
CTEULONGLONG AdapterMask;
|
|
ULONG BytesIndicatedOrig;
|
|
ULONG BytesAvailableOrig;
|
|
ULONG Offset;
|
|
|
|
//
|
|
// We will be processing only directed or broadcast datagrams
|
|
// Hence, there must be at least the header plus two half ascii names etc.
|
|
// which all adds up to 82 bytes with no user data
|
|
// ie. 14 + (1+32+1) + (1+32+1) ==> Assuming 0 Scopelength + 0 UserData
|
|
//
|
|
if ((BytesIndicated < 82) ||
|
|
(pDgram->MsgType < DIRECT_UNIQUE) ||
|
|
(pDgram->MsgType > BROADCAST_DGRAM))
|
|
{
|
|
KdPrint (("Nbt.DgramHndlrNotOs[1]: WARNING! Rejecting Dgram -- BytesIndicated=<%d>, MsgType=<0x%x>\n",
|
|
BytesIndicated, pDgram->MsgType));
|
|
return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
// First, find the end of the SourceName .. it should end in a 0 byte,
|
|
// but use strnlen just to be safe! (BUG # 114996)
|
|
//
|
|
// Then, find the destination name in the local name service tables
|
|
//
|
|
Offset = FIELD_OFFSET(tDGRAMHDR,SrcName.NetBiosName[0]);
|
|
if ((!NT_SUCCESS (LocStatus = strnlen ((PCHAR)pDgram->SrcName.NetBiosName,
|
|
BytesIndicated-Offset,
|
|
&iLength))) ||
|
|
(BytesIndicated < (Offset+iLength+1+1+32+1)) ||
|
|
(!NT_SUCCESS (status = ConvertToAscii ((PCHAR)&pDgram->SrcName.NetBiosName[iLength+1],
|
|
BytesIndicated-(Offset+iLength+1), // Bug#: 124441
|
|
pName,
|
|
&pScope,
|
|
&lNameSize))))
|
|
{
|
|
KdPrint (("Nbt.DgramHndlrNotOs: WARNING!!! Rejecting Dgram -- strnlen-><%x>, ConvertToAscii-><%x>\n",
|
|
LocStatus, status));
|
|
KdPrint (("Nbt.DgramHndlrNotOs: iLength=<%d>, Half-Ascii Dest=<%p>, Ascii Dest=<%p>\n",
|
|
iLength, &pDgram->SrcName.NetBiosName[iLength+1], pName));
|
|
// ASSERT (0);
|
|
return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
//
|
|
// check length again, including scopes of names too. The Src
|
|
// scope length is returned in iLength, which also includes the
|
|
// half ascii length
|
|
//
|
|
if (BytesIndicated < ( 82 // 14(Hdr) + 2 HalfAscii names (2*(1+32+1))
|
|
+(iLength-(2*NETBIOS_NAME_SIZE)) // Src ScopeLength
|
|
+(NbtConfig.ScopeLength-1))) // Dest (ie Local) ScopeLength
|
|
{
|
|
KdPrint (("Nbt.DgramHndlrNoOs[2]: WARNING!!! Rejecting Dgram -- BytesIndicated=<%d> < <%d>\n",
|
|
BytesIndicated, 82+(iLength-(2*NETBIOS_NAME_SIZE))+(NbtConfig.ScopeLength-1)));
|
|
ASSERT (0);
|
|
return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// Check for the full name first instead of considering any name with a '*' as
|
|
// the first char as a broadcast name (e.g. *SMBSERVER and *SMBDATAGRAM are not
|
|
// b'cast names).
|
|
//
|
|
pNameAddr = FindName (NBT_LOCAL, pName, pScope, &RetNameType);
|
|
|
|
//
|
|
// If we failed above, it might be because the name could start with '*' and is a
|
|
// bcast name.
|
|
//
|
|
if (!pNameAddr)
|
|
{
|
|
//
|
|
// be sure the broadcast name has 15 zeroes after it
|
|
//
|
|
if (pName[0] == '*')
|
|
{
|
|
CTEZeroMemory(&pName[1],NETBIOS_NAME_SIZE-1);
|
|
pNameAddr = FindName (NBT_LOCAL, pName, pScope, &RetNameType);
|
|
}
|
|
}
|
|
|
|
// Change the pTsdu ptr to pt to the users data
|
|
// -2 to account for the tNETBIOSNAME and +2 to add the length
|
|
// bytes for both names, +1 for the null on the end of the first
|
|
// name
|
|
|
|
lDgramHdrSize = sizeof(tDGRAMHDR) - 2 + 1+iLength+1 + 1+lNameSize;
|
|
pTsdu = (PVOID)((PUCHAR)pTsdu + lDgramHdrSize);
|
|
BytesAvailableOrig = BytesAvailable;
|
|
BytesAvailable -= lDgramHdrSize;
|
|
BytesIndicatedOrig = BytesIndicated;
|
|
BytesIndicated -= lDgramHdrSize;
|
|
|
|
//
|
|
// If the name is in the local table and has an address element
|
|
// associated with it AND the name is registered against
|
|
// this adapter, then execute the code in the 'if' block
|
|
//
|
|
AdapterMask = pDeviceContext->AdapterMask;
|
|
if ((pNameAddr) &&
|
|
(pNameAddr->pAddressEle) &&
|
|
(pNameAddr->AdapterMask & AdapterMask))
|
|
{
|
|
pAddress = pNameAddr->pAddressEle;
|
|
|
|
// Need to hold Address lock to traverse ClientHead
|
|
CTESpinLock(pAddress, OldIrq1);
|
|
|
|
if (!IsListEmpty(&pAddress->ClientHead))
|
|
{
|
|
PLIST_ENTRY pHead;
|
|
PLIST_ENTRY pEntry;
|
|
|
|
//
|
|
// Increment the reference count to prevent the
|
|
// pAddress from disappearing in the window between freeing
|
|
// the JOINT_LOCK and taking the ADDRESS_LOCK. We also need to
|
|
// keep the refcount if we are doing a multi client recv, since
|
|
// Clientlist access pAddressEle when distributing the rcv'd dgram
|
|
// in CompletionRcvDgram.
|
|
//
|
|
NBT_REFERENCE_ADDRESS (pAddress, REF_ADDR_DGRAM);
|
|
|
|
*pBytesTaken = lDgramHdrSize;
|
|
|
|
//
|
|
// Check if there is more than one client that should receive this
|
|
// datagram. If so then pass down a new buffer to get it and
|
|
// copy it to each client's buffer in the completion routine.
|
|
//
|
|
*ppRcvBuffer = NULL;
|
|
MoreClients = FALSE;
|
|
*ppClientList = NULL;
|
|
|
|
pHead = &pAddress->ClientHead;
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
PTDI_IND_RECEIVE_DATAGRAM EvRcvDgram;
|
|
PVOID RcvDgramEvContext;
|
|
PLIST_ENTRY pRcvEntry;
|
|
tRCVELE *pRcvEle;
|
|
ULONG MaxLength;
|
|
PLIST_ENTRY pSaveEntry;
|
|
|
|
pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage);
|
|
|
|
// this client must be registered against this adapter to
|
|
// get the data
|
|
//
|
|
if (!(pClientEle->pDeviceContext) ||
|
|
(pClientEle->pDeviceContext != pDeviceContext))
|
|
{
|
|
pEntry = pEntry->Flink;
|
|
continue;
|
|
}
|
|
|
|
#ifdef VXD
|
|
//
|
|
// Move all of the RcvAnyFromAny Datagrams to this client's
|
|
// RcvDatagram list so they will be processed along with the
|
|
// outstanding datagrams for this client if this isn't a
|
|
// broadcast reception (RcvAnyFromAny dgrams
|
|
// don't receive broadcasts). The first client will
|
|
// empty the list, which is ok.
|
|
//
|
|
if (*pName != '*')
|
|
{
|
|
PLIST_ENTRY pDGEntry ;
|
|
|
|
while ( !IsListEmpty( &pDeviceContext->RcvDGAnyFromAnyHead ))
|
|
{
|
|
pDGEntry = RemoveHeadList(&pDeviceContext->RcvDGAnyFromAnyHead) ;
|
|
InsertTailList( &pClientEle->RcvDgramHead, pDGEntry ) ;
|
|
}
|
|
}
|
|
#endif
|
|
// check for datagrams posted to this name, and if not call
|
|
// the recv event handler. NOTE: this assumes that the clients
|
|
// use posted recv buffer OR and event handler, but NOT BOTH.
|
|
// If two clients open the same name, one with a posted rcv
|
|
// buffer and another with an event handler, the one with the
|
|
// event handler will NOT get the datagram!
|
|
//
|
|
if (!IsListEmpty(&pClientEle->RcvDgramHead))
|
|
{
|
|
MaxLength = 0;
|
|
pSaveEntry = pEntry;
|
|
//
|
|
// go through all clients finding one that has a large
|
|
// enough buffer
|
|
//
|
|
while (pEntry != pHead)
|
|
{
|
|
pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage);
|
|
|
|
if (IsListEmpty(&pClientEle->RcvDgramHead))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pRcvEntry = pClientEle->RcvDgramHead.Flink;
|
|
pRcvEle = CONTAINING_RECORD(pRcvEntry,tRCVELE,Linkage);
|
|
|
|
if (pRcvEle->RcvLength >= BytesAvailable)
|
|
{
|
|
pSaveEntry = pEntry;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// keep the maximum rcv length around incase none
|
|
// is large enough
|
|
//
|
|
if (pRcvEle->RcvLength > MaxLength)
|
|
{
|
|
pSaveEntry = pEntry;
|
|
MaxLength = pRcvEle->RcvLength;
|
|
}
|
|
pEntry = pEntry->Flink;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the buffer off the list
|
|
//
|
|
pClientEle = CONTAINING_RECORD(pSaveEntry,tCLIENTELE,Linkage);
|
|
|
|
pRcvEntry = RemoveHeadList(&pClientEle->RcvDgramHead);
|
|
|
|
*ppRcvBuffer = pRcvEle->pIrp;
|
|
#ifdef VXD
|
|
ASSERT( pDgram->SrcName.NameLength <= NETBIOS_NAME_SIZE*2) ;
|
|
LocStatus = ConvertToAscii(
|
|
(PCHAR)&pDgram->SrcName,
|
|
pDgram->SrcName.NameLength+1,
|
|
((NCB*)*ppRcvBuffer)->ncb_callname,
|
|
&pScope,
|
|
&lNameSize);
|
|
|
|
if ( !NT_SUCCESS(LocStatus) )
|
|
{
|
|
DbgPrint("ConvertToAscii failed\r\n") ;
|
|
}
|
|
#else //VXD
|
|
|
|
//
|
|
// put the source of the datagram into the return
|
|
// connection info structure.
|
|
//
|
|
if (pRcvEle->ReturnedInfo)
|
|
{
|
|
UCHAR pSrcName[NETBIOS_NAME_SIZE];
|
|
|
|
Offset = FIELD_OFFSET(tDGRAMHDR,SrcName); // Bug#: 124434
|
|
LocStatus = ConvertToAscii(
|
|
(PCHAR)&pDgram->SrcName,
|
|
BytesIndicatedOrig-Offset,
|
|
pSrcName,
|
|
&pScope,
|
|
&lNameSize);
|
|
|
|
if (pRcvEle->ReturnedInfo->RemoteAddressLength >=
|
|
sizeof(TA_NETBIOS_ADDRESS))
|
|
{
|
|
TdiBuildNetbiosAddress(pSrcName,
|
|
FALSE,
|
|
pRcvEle->ReturnedInfo->RemoteAddress);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Null out the cancel routine since we are passing the
|
|
// irp to the transport
|
|
//
|
|
IoAcquireCancelSpinLock(&OldIrq);
|
|
IoSetCancelRoutine(pRcvEle->pIrp,NULL);
|
|
IoReleaseCancelSpinLock(OldIrq);
|
|
#endif
|
|
CTEMemFree((PVOID)pRcvEle);
|
|
|
|
if (pAddress->MultiClients)
|
|
{
|
|
// the multihomed host always passes the above test
|
|
// so we need a more discerning test for it.
|
|
if (!NbtConfig.MultiHomed)
|
|
{
|
|
// if the list is more than one on it,
|
|
// then there are several clients waiting
|
|
// to receive this datagram, so pass down a buffer to
|
|
// get it.
|
|
//
|
|
MoreClients = TRUE;
|
|
status = STATUS_SUCCESS;
|
|
|
|
UsingClientBuffer = TRUE;
|
|
|
|
// this break will jump down below where we check if
|
|
// MoreClients = TRUE
|
|
|
|
//
|
|
// We will need to keep the Client around when CompletionRcvDgram executes!
|
|
// Bug#: 124675
|
|
//
|
|
NBT_REFERENCE_CLIENT(pClientEle);
|
|
//
|
|
// Increment the RefCount by 1 here since there will be
|
|
// an extra Dereference in CompletionRcvDgram
|
|
//
|
|
NBT_REFERENCE_ADDRESS (pAddress, REF_ADDR_MULTICLIENTS);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// jump to end of while to check if we need to buffer
|
|
// the datagram source address
|
|
// in the remote hash table
|
|
//
|
|
break;
|
|
}
|
|
#ifdef VXD
|
|
break;
|
|
#else
|
|
EvRcvDgram = pClientEle->evRcvDgram;
|
|
RcvDgramEvContext = pClientEle->RcvDgramEvContext;
|
|
|
|
// don't want to call the default handler since it just
|
|
// returns data not accepted
|
|
if (pClientEle->evRcvDgram != TdiDefaultRcvDatagramHandler)
|
|
{
|
|
ULONG NumAddrs;
|
|
|
|
// finally found a real event handler set by a client
|
|
if (pAddress->MultiClients)
|
|
// if (pEntry->Flink != pHead)
|
|
{
|
|
// if the next element in the list is not the head
|
|
// of the list then there are several clients waiting
|
|
// to receive this datagram, so pass down a buffer to
|
|
// get it.
|
|
//
|
|
MoreClients = TRUE;
|
|
UsingClientBuffer = FALSE;
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// We will need to keep the Client around when CompletionRcvDgram executes!
|
|
// Bug#: 124675
|
|
//
|
|
NBT_REFERENCE_CLIENT(pClientEle);
|
|
//
|
|
// Increment the RefCount by 1 here since there will be
|
|
// an extra Dereference out of the while loop
|
|
//
|
|
NBT_REFERENCE_ADDRESS (pAddress, REF_ADDR_MULTICLIENTS);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// make up an address datastructure - subtracting the
|
|
// number of bytes skipped from the total length so
|
|
// convert to Ascii can not bug chk on bogus names.
|
|
//
|
|
if (pClientEle->ExtendedAddress)
|
|
{
|
|
NumAddrs = 2;
|
|
}
|
|
else
|
|
{
|
|
NumAddrs = 1;
|
|
}
|
|
|
|
LocStatus = MakeRemoteAddressStructure(
|
|
(PCHAR)&pDgram->SrcName.NameLength,
|
|
pSourceAddr,
|
|
BytesIndicatedOrig - FIELD_OFFSET(tDGRAMHDR,SrcName.NameLength),
|
|
&pRemoteAddress, // the end of the pdu.
|
|
&RemoteAddressLength,
|
|
NumAddrs);
|
|
|
|
if (!NT_SUCCESS(LocStatus))
|
|
{
|
|
CTESpinFree(pAddress, OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (pClientEleToDeref)
|
|
{
|
|
NBT_DEREFERENCE_CLIENT (pClientEleToDeref);
|
|
}
|
|
NBT_DEREFERENCE_ADDRESS (pAddress, REF_ADDR_DGRAM);
|
|
return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
NBT_REFERENCE_CLIENT(pClientEle);
|
|
CTESpinFree(pAddress, OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (pClientEleToDeref)
|
|
{
|
|
NBT_DEREFERENCE_CLIENT (pClientEleToDeref);
|
|
}
|
|
pClientEleToDeref = pClientEle;
|
|
|
|
pIrp = NULL;
|
|
lClientBytesTaken = 0;
|
|
LocStatus = (*EvRcvDgram)(RcvDgramEvContext,
|
|
RemoteAddressLength,
|
|
pRemoteAddress,
|
|
OptionsLength,
|
|
pOptions,
|
|
ReceiveDatagramFlags,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
&lClientBytesTaken,
|
|
pTsdu,
|
|
&pIrp);
|
|
|
|
CTEMemFree((PVOID)pRemoteAddress);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pAddress, OldIrq1);
|
|
|
|
if (pIrp)
|
|
{
|
|
// the client has passed back an irp so pass it
|
|
// on the transport
|
|
*pBytesTaken += lClientBytesTaken;
|
|
*ppRcvBuffer = pIrp;
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
|
|
pEntry = pEntry->Flink; // go to the next client in the list
|
|
#endif // VXD
|
|
}// of While
|
|
|
|
CTESpinFree(pAddress, OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (pClientEleToDeref)
|
|
{
|
|
NBT_DEREFERENCE_CLIENT (pClientEleToDeref);
|
|
}
|
|
NBT_DEREFERENCE_ADDRESS (pAddress, REF_ADDR_DGRAM);
|
|
|
|
//
|
|
// Cache the source address in the remote hash table so that
|
|
// this node can send back to the source even if the name
|
|
// is not yet in the name server yet. (only if not on the
|
|
// same subnet)
|
|
//
|
|
if ((pDgram->MsgType != BROADCAST_DGRAM))
|
|
{
|
|
ULONG SrcAddress;
|
|
PTRANSPORT_ADDRESS pSourceAddress;
|
|
ULONG SubnetMask;
|
|
|
|
pSourceAddress = (PTRANSPORT_ADDRESS)pSourceAddr;
|
|
SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr);
|
|
SubnetMask = pDeviceContext->SubnetMask;
|
|
//
|
|
// - cache only if from off the subnet
|
|
// - cache if not sent to 1E,1D,01 name and not from ourselves
|
|
//
|
|
// don't cache dgrams from ourselves, or datagrams to the
|
|
// 1E name, 1D, or 01.
|
|
//
|
|
if (((SrcAddress & SubnetMask) !=
|
|
(pDeviceContext->IpAddress & SubnetMask))
|
|
||
|
|
((pName[NETBIOS_NAME_SIZE-1] != 0x1E) &&
|
|
(pName[NETBIOS_NAME_SIZE-1] != 0x1D) &&
|
|
(pName[NETBIOS_NAME_SIZE-1] != 0x01) &&
|
|
(!SrcIsUs(SrcAddress))))
|
|
{
|
|
AddToRemoteHashTbl(pDgram,BytesIndicatedOrig,pDeviceContext);
|
|
}
|
|
}
|
|
|
|
// alloc a block of memory to track where we are in the list
|
|
// of clients so completionrcvdgram can send the dgram to the
|
|
// other clients too.
|
|
//
|
|
if (MoreClients)
|
|
{
|
|
tCLIENTLIST *pClientList;
|
|
|
|
if (pClientList = (tCLIENTLIST *)NbtAllocMem(sizeof(tCLIENTLIST),NBT_TAG('4')))
|
|
{
|
|
CTEZeroMemory (pClientList, sizeof(tCLIENTLIST));
|
|
|
|
//
|
|
// Set fProxy field to FALSE since the client list is for
|
|
// real as versus the PROXY case
|
|
//
|
|
pClientList->fProxy = FALSE;
|
|
|
|
// save some context information so we can pass the
|
|
// datagram to the clients - none of the clients have
|
|
// recvd the datagram yet.
|
|
//
|
|
*ppClientList = (PVOID)pClientList;
|
|
pClientList->pAddress = pAddress;
|
|
pClientList->pClientEle = pClientEle; // used for VXD case
|
|
pClientList->fUsingClientBuffer = UsingClientBuffer;
|
|
pClientList->ReceiveDatagramFlags = ReceiveDatagramFlags;
|
|
|
|
// make up an address datastructure
|
|
// Bug # 452211 -- since one of the clients may have the Extended
|
|
// addressing field set, create an extended address
|
|
//
|
|
LocStatus = MakeRemoteAddressStructure(
|
|
(PCHAR)&pDgram->SrcName.NameLength,
|
|
pSourceAddr,
|
|
BytesIndicatedOrig -FIELD_OFFSET(tDGRAMHDR,SrcName.NameLength),// set a max number of bytes so we don't go beyond
|
|
&pRemoteAddress, // the end of the pdu.
|
|
&RemoteAddressLength,
|
|
2);
|
|
if (NT_SUCCESS(LocStatus))
|
|
{
|
|
pClientList->pRemoteAddress = pRemoteAddress;
|
|
pClientList->RemoteAddressLength = RemoteAddressLength;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
*ppClientList = NULL;
|
|
CTEMemFree(pClientList);
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
//
|
|
// We failed, so Dereference the Client + Address we had
|
|
// reference earlier for multiple clients
|
|
//
|
|
NBT_DEREFERENCE_CLIENT (pClientEle);
|
|
NBT_DEREFERENCE_ADDRESS (pAddress, REF_ADDR_MULTICLIENTS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pAddress, OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.DgramHndlrNotOs: No client attached to the Address %16.16s<%X>\n",
|
|
pAddress->pNameAddr->Name,pAddress->pNameAddr->Name[15]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
#ifdef PROXY_NODE
|
|
IF_PROXY(NodeType)
|
|
{
|
|
ULONG SrcAddress;
|
|
PTRANSPORT_ADDRESS pSourceAddress;
|
|
|
|
pSourceAddress = (PTRANSPORT_ADDRESS)pSourceAddr;
|
|
SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr);
|
|
|
|
//
|
|
// check name in the remote name table. If it is there, it is
|
|
// an internet group and is in the resolved state, send the
|
|
// datagram to all the members except self. If it is in the
|
|
// resolving state, just return. The fact that we got a
|
|
// datagram send for an internet group name still in the
|
|
// resolving state indicates that there is a DC on the subnet
|
|
// that responded to the query for the group received
|
|
// earlier. This means that the DC will respond (unless it
|
|
// goes down) to this datagram send. If the DC is down, the
|
|
// client node will retry.
|
|
//
|
|
// Futures: Queue the Datagram if the name is in the resolving
|
|
// state.
|
|
//
|
|
// If Flags are zero then it is a non fragmented Bnode send. There
|
|
// is not point in doing datagram distribution for P,M,or H nodes
|
|
// can they can do their own.
|
|
//
|
|
if (((pDgram->Flags & SOURCE_NODE_MASK) == 0) &&
|
|
(pName[0] != '*') &&
|
|
(!SrcIsUs(SrcAddress)))
|
|
{
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
pNameAddr = FindName (NBT_REMOTE, pName, pScope, &RetNameType);
|
|
if (pNameAddr)
|
|
{
|
|
//
|
|
// We have the name in the RESOLVED state.
|
|
//
|
|
//
|
|
// If the name is an internet group, do datagram distribution
|
|
// function
|
|
// Make sure we don't distribute a datagram that has been
|
|
// sent to us by another proxy. In other words, distribute
|
|
// the datagram only if we got it first-hand from original node
|
|
//
|
|
if ((pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) &&
|
|
((((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr) == pDgram->SrcIpAddr))
|
|
{
|
|
//
|
|
// If BytesAvailable != BytesIndicated, it means that
|
|
// that we don't have the entire datagram. We need to
|
|
// get it
|
|
if (BytesAvailableOrig != BytesIndicatedOrig)
|
|
{
|
|
tCLIENTLIST *pClientList;
|
|
|
|
//
|
|
// Do some simulation to fake the caller of this fn
|
|
// (TdiRcvDatagramHndlr) into thinking that there are
|
|
// multiple clients. This will result in
|
|
// TdiRcvDatagramHndlr function getting all bytes
|
|
// available from TDI and calling
|
|
// ProxyDoDgramDist to do the datagram distribution
|
|
//
|
|
if (pClientList = (tCLIENTLIST *)NbtAllocMem(sizeof(tCLIENTLIST),NBT_TAG('5')))
|
|
{
|
|
CTEZeroMemory (pClientList, sizeof(tCLIENTLIST));
|
|
|
|
//
|
|
// save some context information in the Client List
|
|
// data structure
|
|
//
|
|
*ppClientList = (PVOID)pClientList;
|
|
//
|
|
// Set fProxy field to TRUE since the client list
|
|
// not for real
|
|
//
|
|
pClientList->fProxy = TRUE;
|
|
|
|
//
|
|
// Make use of the following fields to pass the
|
|
// information we would need in the
|
|
// CompletionRcvDgram
|
|
//
|
|
pClientList->pAddress = (tADDRESSELE *)pNameAddr;
|
|
pClientList->pRemoteAddress = pDeviceContext;
|
|
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
} // end of if (we do not have the entire datagram)
|
|
else
|
|
{
|
|
//
|
|
// Increment the reference count so that this name
|
|
// does not disappear on us after we free the spin lock.
|
|
//
|
|
// DgramSendCleanupTracker will decrement the count
|
|
//
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_SEND_DGRAM);
|
|
//
|
|
//We have the entire datagram.
|
|
//
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
(VOID)ProxyDoDgramDist(pDgram,
|
|
BytesIndicatedOrig,
|
|
pNameAddr,
|
|
pDeviceContext);
|
|
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
} // end of if (if name is an internet group name)
|
|
else
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
} // end of if (Name is there in remote hash table)
|
|
else
|
|
{
|
|
tNAMEADDR *pResp;
|
|
|
|
//
|
|
// the name is not in the cache, so try to get it from
|
|
// WINS
|
|
//
|
|
status = FindOnPendingList(pName,NULL,TRUE,NETBIOS_NAME_SIZE,&pResp);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// cache the name and contact the name
|
|
// server to get the name to IP mapping
|
|
//
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
status = RegOrQueryFromNet(
|
|
FALSE, //means it is a name query
|
|
pDeviceContext,
|
|
NULL,
|
|
lNameSize,
|
|
pName,
|
|
pScope);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// the name is on the pending list doing a name query
|
|
// now, so ignore this name query request
|
|
//
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
}
|
|
END_PROXY
|
|
#endif
|
|
|
|
return(status);
|
|
}
|
|
|
|
#ifdef PROXY_NODE
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
ProxyDoDgramDist(
|
|
IN tDGRAMHDR UNALIGNED *pDgram,
|
|
IN DWORD DgramLen,
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
ppRcvbuffer will contain the IRP/NCB if only one client is listening,
|
|
NULL if multiple clients are listening
|
|
ppClientList will contain the list clients that need to be completed,
|
|
NULL if only one client is listening
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
Called By:
|
|
|
|
DgramHdlrNotOs, CompletionRcvDgram in tdihndlr.c
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
tDGRAMHDR *pMyBuff;
|
|
|
|
//
|
|
// get a buffer for tracking Dgram Sends
|
|
//
|
|
status = GetTracker(&pTracker, NBT_TRACKER_PROXY_DGRAM_DIST);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_SEND_DGRAM, FALSE);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer and copy the contents of the datagram received
|
|
// into it. We do this because SndDgram may not have finished by the
|
|
// time we return.
|
|
//
|
|
if (!(pMyBuff = (tDGRAMHDR *) NbtAllocMem(DgramLen,NBT_TAG('6'))))
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_SEND_DGRAM, FALSE);
|
|
FreeTracker (pTracker, RELINK_TRACKER);
|
|
return STATUS_INSUFFICIENT_RESOURCES ;
|
|
}
|
|
|
|
CTEMemCopy(pMyBuff, (PUCHAR)pDgram, DgramLen);
|
|
|
|
//
|
|
// fill in the tracker data block
|
|
// note that the passed in transport address must stay valid till this
|
|
// send completes
|
|
//
|
|
CHECK_PTR(pTracker);
|
|
pTracker->SendBuffer.pDgramHdr = (PVOID)pMyBuff;
|
|
pTracker->SendBuffer.HdrLength = DgramLen;
|
|
pTracker->SendBuffer.pBuffer = NULL;
|
|
pTracker->SendBuffer.Length = 0;
|
|
pTracker->pNameAddr = pNameAddr;
|
|
pTracker->pDeviceContext = (PVOID)pDeviceContext;
|
|
pTracker->p1CNameAddr = NULL;
|
|
//
|
|
// so DgramSendCleanupTracker does not decrement the bytes allocated
|
|
// to dgram sends, since we did not increment the count when we allocated
|
|
// the dgram buffer above.
|
|
//
|
|
pTracker->AllocatedLength = 0;
|
|
|
|
pTracker->pClientIrp = NULL;
|
|
pTracker->pClientEle = NULL;
|
|
|
|
KdPrint(("Nbt.ProxyDoDgramDist: Name is %16.16s(%X)\n", pNameAddr->Name,
|
|
pNameAddr->Name[15]));
|
|
|
|
//
|
|
// Send the datagram to each IP address in the Internet group
|
|
//
|
|
//
|
|
DatagramDistribution(pTracker,pNameAddr);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NameSrvHndlrNotOs (
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PVOID pSrcAddress,
|
|
IN tNAMEHDR UNALIGNED *pNameSrv,
|
|
IN ULONG uNumBytes,
|
|
IN BOOLEAN fBroadcast
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the receive datagram event indication handler.
|
|
|
|
It is called when an a datgram arrives from the network. The code
|
|
checks the type of datagram and then tries to route the datagram to
|
|
the correct destination on the node.
|
|
|
|
This procedure is called with the spin lock held on pDeviceContext.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
{
|
|
USHORT OpCodeFlags;
|
|
NTSTATUS status;
|
|
|
|
// it appears that streams can pass a null data pointer some times
|
|
// and crash nbt...and zero length for the bytes
|
|
if (uNumBytes < sizeof(ULONG))
|
|
{
|
|
return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
OpCodeFlags = pNameSrv->OpCodeFlags;
|
|
|
|
//Pnodes always ignore Broadcasts since they only talk to the NBNS unless
|
|
// this node is also a proxy
|
|
if ( ( ((NodeType) & PNODE)) && !((NodeType) & PROXY) )
|
|
{
|
|
if (OpCodeFlags & FL_BROADCAST)
|
|
{
|
|
return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
}
|
|
|
|
|
|
// decide what type of name service packet it is by switching on the
|
|
// NM_Flags portion of the word
|
|
switch (OpCodeFlags & NM_FLAGS_MASK)
|
|
{
|
|
case OP_QUERY:
|
|
status = QueryFromNet(
|
|
pDeviceContext,
|
|
pSrcAddress,
|
|
pNameSrv,
|
|
uNumBytes, // >= NBT_MINIMUM_QUERY (== 50)
|
|
OpCodeFlags,
|
|
fBroadcast);
|
|
break;
|
|
|
|
case OP_REGISTRATION:
|
|
//
|
|
// we can get either a registration request or a response
|
|
//
|
|
// is this a request or a response? - if bit is set its a Response
|
|
|
|
if (OpCodeFlags & OP_RESPONSE)
|
|
{
|
|
// then this is a response to a previous reg. request
|
|
status = RegResponseFromNet(
|
|
pDeviceContext,
|
|
pSrcAddress,
|
|
pNameSrv,
|
|
uNumBytes, // >= NBT_MINIMUM_REGRESPONSE (== 62)
|
|
OpCodeFlags);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// check if someone else is trying to register a name
|
|
// owned by this node. Pnodes rely on the Name server to
|
|
// handle this...hence the check for Pnode
|
|
//
|
|
if (!(NodeType & PNODE))
|
|
{
|
|
status = CheckRegistrationFromNet(pDeviceContext,
|
|
pSrcAddress,
|
|
pNameSrv,
|
|
uNumBytes); // >= NBT_MINIMUM_REGREQUEST (== 68)
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OP_RELEASE:
|
|
//
|
|
// handle other nodes releasing their names by deleting any
|
|
// cached info
|
|
//
|
|
status = NameReleaseFromNet(
|
|
pDeviceContext,
|
|
pSrcAddress,
|
|
pNameSrv,
|
|
uNumBytes); // >= NBT_MINIMUM_REGRESPONSE (== 62)
|
|
break;
|
|
|
|
case OP_WACK:
|
|
if (!(NodeType & BNODE))
|
|
{
|
|
// the TTL in the WACK tells us to increase our timeout
|
|
// of the corresponding request, which means we must find
|
|
// the transaction
|
|
status = WackFromNet(pDeviceContext,
|
|
pSrcAddress,
|
|
pNameSrv,
|
|
uNumBytes); // >= NBT_MINIMUM_WACK (== 58)
|
|
}
|
|
break;
|
|
|
|
case OP_REFRESH:
|
|
case OP_REFRESH_UB:
|
|
|
|
break;
|
|
|
|
default:
|
|
IF_DBG(NBT_DEBUG_HNDLRS)
|
|
KdPrint(("Nbt.NameSrvHndlrNotOs: Unknown Name Service Pdu type OpFlags = %X\n",
|
|
OpCodeFlags));
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
VOID
|
|
DoNothingComplete (
|
|
IN PVOID pContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the completion routine for TdiDisconnect while we are
|
|
retrying connects. It does nothing.
|
|
|
|
This is required because you can't have a NULL TDI completion routine.
|
|
|
|
--*/
|
|
{
|
|
return ;
|
|
}
|