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.
12014 lines
387 KiB
12014 lines
387 KiB
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Name.c
|
|
|
|
Abstract:
|
|
|
|
This file implements Tdi interface into the Top of NBT. In the NT
|
|
implementation, ntisol.c calls these routines after extracting the
|
|
relevent information from the Irp passed in from the Io subsystem.
|
|
|
|
|
|
Author:
|
|
|
|
Jim Stewart (Jimst) 10-2-92
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h" // procedure headings
|
|
#ifndef VXD
|
|
|
|
#ifdef RASAUTODIAL
|
|
#include <acd.h>
|
|
#include <acdapi.h>
|
|
#include <tcpinfo.h>
|
|
#include <tdiinfo.h>
|
|
#endif // RASAUTODIAL
|
|
#include "name.tmh"
|
|
#endif
|
|
|
|
//
|
|
// Allocate storage for the configuration information and setup a ptr to
|
|
// it.
|
|
//
|
|
tNBTCONFIG NbtConfig;
|
|
tNBTCONFIG *pNbtGlobConfig = &NbtConfig;
|
|
BOOLEAN CachePrimed;
|
|
|
|
//
|
|
// This structure is used to store name query and registration statistics
|
|
//
|
|
tNAMESTATS_INFO NameStatsInfo;
|
|
#ifndef VXD
|
|
//
|
|
// this tracks the original File system process that Nbt was booted by, so
|
|
// that handles can be created and destroyed in that process
|
|
//
|
|
PEPROCESS NbtFspProcess;
|
|
#endif
|
|
//
|
|
// this describes whether we are a Bnode, Mnode, MSnode or Pnode
|
|
//
|
|
USHORT RegistryNodeType;
|
|
USHORT NodeType;
|
|
//
|
|
// this is used to track the memory allocated for datagram sends
|
|
//
|
|
ULONG NbtMemoryAllocated;
|
|
|
|
// this is used to track used trackers to help solve cases where they all
|
|
// are used.
|
|
//
|
|
//#if DBG
|
|
|
|
LIST_ENTRY UsedTrackers;
|
|
|
|
//#endif
|
|
|
|
#ifdef VXD
|
|
ULONG DefaultDisconnectTimeout;
|
|
#else
|
|
LARGE_INTEGER DefaultDisconnectTimeout;
|
|
#endif
|
|
|
|
// ************* REMOVE LATER *****************88
|
|
BOOLEAN StreamsStack;
|
|
|
|
#ifdef DBG
|
|
//
|
|
// Imported routines.
|
|
//
|
|
#endif
|
|
|
|
NTSTATUS
|
|
NbtpConnectCompletionRoutine(
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
PIRP pIrp,
|
|
PVOID pCompletionContext
|
|
);
|
|
|
|
//
|
|
// Function prototypes for functions local to this file
|
|
//
|
|
VOID
|
|
CleanupFromRegisterFailed(
|
|
IN PUCHAR pNameRslv,
|
|
IN tCLIENTELE *pClientEle
|
|
);
|
|
|
|
VOID
|
|
SendDgramContinue(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
);
|
|
|
|
VOID
|
|
CTECountedAllocMem(
|
|
PVOID *pBuffer,
|
|
ULONG Size
|
|
);
|
|
|
|
VOID
|
|
CTECountedFreeMem(
|
|
IN PVOID pBuffer,
|
|
IN ULONG Size,
|
|
IN BOOLEAN fJointLockHeld
|
|
);
|
|
|
|
VOID
|
|
SendNextDgramToGroup(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN NTSTATUS status
|
|
);
|
|
|
|
VOID
|
|
SendDgramCompletion(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status,
|
|
IN ULONG lInfo);
|
|
|
|
VOID
|
|
DgramSendCleanupTracker(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN NTSTATUS status,
|
|
IN BOOLEAN fJointLockHeld
|
|
);
|
|
|
|
VOID
|
|
SessionSetupContinue(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
);
|
|
|
|
VOID
|
|
SessionStartupCompletion(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status,
|
|
IN ULONG lInfo);
|
|
|
|
|
|
VOID
|
|
SendNodeStatusContinue(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
SendToResolvingName(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN PCHAR pName,
|
|
IN CTELockHandle OldIrq,
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN PVOID QueryCompletion
|
|
);
|
|
|
|
NTSTATUS
|
|
StartSessionTimer(
|
|
tDGRAM_SEND_TRACKING *pTracker,
|
|
tCONNECTELE *pConnEle
|
|
);
|
|
|
|
VOID
|
|
QueryNameCompletion(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
);
|
|
|
|
#ifndef VXD
|
|
VOID
|
|
NTClearFindNameInfo(
|
|
tDGRAM_SEND_TRACKING *pTracker,
|
|
PIRP *ppClientIrp,
|
|
PIRP pIrp,
|
|
PIO_STACK_LOCATION pIrpSp
|
|
);
|
|
#endif // !VXD
|
|
|
|
NTSTATUS
|
|
FindNameOrQuery(
|
|
IN PUCHAR pName,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PVOID QueryCompletion,
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN ULONG NameFlags,
|
|
OUT tIPADDRESS *IpAddress,
|
|
OUT tNAMEADDR **pNameAddr,
|
|
IN ULONG NameReferenceContext,
|
|
IN BOOLEAN DgramSend
|
|
);
|
|
|
|
#ifdef RASAUTODIAL
|
|
extern BOOLEAN fAcdLoadedG;
|
|
extern ACD_DRIVER AcdDriverG;
|
|
|
|
VOID
|
|
NbtRetryPreConnect(
|
|
IN BOOLEAN fSuccess,
|
|
IN PVOID *pArgs
|
|
);
|
|
|
|
VOID
|
|
NbtCancelPreConnect(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp
|
|
);
|
|
|
|
VOID
|
|
NbtRetryPostConnect(
|
|
IN BOOLEAN fSuccess,
|
|
IN PVOID *pArgs
|
|
);
|
|
|
|
BOOLEAN
|
|
NbtAttemptAutoDial(
|
|
IN tCONNECTELE *pConnEle,
|
|
IN PVOID pTimeout,
|
|
IN PTDI_CONNECTION_INFORMATION pCallInfo,
|
|
IN PIRP pIrp,
|
|
IN ULONG ulFlags,
|
|
IN ACD_CONNECT_CALLBACK pProc
|
|
);
|
|
|
|
VOID
|
|
NbtNoteNewConnection(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN ULONG IPAddress
|
|
);
|
|
#endif // RASAUTODIAL
|
|
|
|
NTSTATUS
|
|
NbtConnectCommon(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN PVOID pTimeout,
|
|
IN PTDI_CONNECTION_INFORMATION pCallInfo,
|
|
IN PIRP pIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
GetListOfAllAddrs(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN tNAMEADDR *p1CNameAddr,
|
|
IN tIPADDRESS **ppIpBuffer,
|
|
IN ULONG *pNumAddrs
|
|
);
|
|
|
|
BOOL
|
|
IsLocalAddress(
|
|
tIPADDRESS IpAddress
|
|
);
|
|
|
|
BOOL
|
|
IsSmbBoundToOutgoingInterface(
|
|
IN tIPADDRESS IpAddress
|
|
);
|
|
|
|
//******************* Pageable Routine Declarations ****************
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma CTEMakePageable(PAGE, NbtOpenConnection)
|
|
#pragma CTEMakePageable(PAGE, NbtSendDatagram)
|
|
#pragma CTEMakePageable(PAGE, BuildSendDgramHdr)
|
|
#pragma CTEMakePageable(PAGE, DelayedNbtResyncRemoteCache)
|
|
#pragma CTEMakePageable(PAGE, NbtQueryFindName)
|
|
#pragma CTEMakePageable(PAGE, NbtCloseAddress)
|
|
#pragma CTEMakePageable(PAGE, NbtCloseConnection)
|
|
#endif
|
|
//******************* Pageable Routine Declarations ****************
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
PickBestAddress(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
OUT tIPADDRESS *pIpAddress
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine picks the best address on a name based on strictness of Source addressing specified
|
|
-- MUST be called with the JointLock held!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
tDEVICECONTEXT *pThisDeviceContext;
|
|
LIST_ENTRY *pHead, *pEntry;
|
|
BOOLEAN fFreeGroupList = FALSE;
|
|
tIPADDRESS IpAddress = 0;
|
|
tIPADDRESS *pIpNbtGroupList = NULL;
|
|
|
|
CHECK_PTR (pNameAddr);
|
|
CHECK_PTR (pDeviceContext);
|
|
|
|
if (pNameAddr->Verify == REMOTE_NAME)
|
|
{
|
|
//
|
|
// Check if this is a pre-loaded name!
|
|
//
|
|
if (pNameAddr->NameAddFlags & NAME_RESOLVED_BY_LMH_P)
|
|
{
|
|
IpAddress = pNameAddr->IpAddress;
|
|
pIpNbtGroupList = pNameAddr->pLmhSvcGroupList;
|
|
}
|
|
//
|
|
// See if we can find the preferred address
|
|
//
|
|
else if (((IsDeviceNetbiosless(pDeviceContext)) &&
|
|
(pNameAddr->pRemoteIpAddrs && pNameAddr->pRemoteIpAddrs[0].IpAddress)) ||
|
|
((!IsDeviceNetbiosless(pDeviceContext)) &&
|
|
(pNameAddr->RemoteCacheLen > pDeviceContext->AdapterNumber) &&
|
|
(pNameAddr->AdapterMask & pDeviceContext->AdapterMask)))
|
|
{
|
|
IpAddress = pNameAddr->pRemoteIpAddrs[pDeviceContext->AdapterNumber].IpAddress;
|
|
pIpNbtGroupList = pNameAddr->pRemoteIpAddrs[pDeviceContext->AdapterNumber].pOrigIpAddrs;
|
|
}
|
|
//
|
|
// If strict source routing was not set, then pick the last updated address
|
|
//
|
|
if ((!NbtConfig.ConnectOnRequestedInterfaceOnly) &&
|
|
(!IpAddress) && (!pIpNbtGroupList))
|
|
{
|
|
if (STATUS_SUCCESS == GetListOfAllAddrs(pNameAddr, NULL, &pIpNbtGroupList, NULL))
|
|
{
|
|
fFreeGroupList = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pIpNbtGroupList = NULL; // for safety
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this was a Group name, then IpAddress can be 0!
|
|
//
|
|
if ((!IpAddress) && (pIpNbtGroupList)) {
|
|
//
|
|
// Pick the first non-zero address from the group list
|
|
//
|
|
int i;
|
|
|
|
for (i = 0; pIpNbtGroupList[i] != (tIPADDRESS) -1; i++) {
|
|
if (pIpNbtGroupList[i]) {
|
|
IpAddress = pIpNbtGroupList[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fFreeGroupList)
|
|
{
|
|
CTEMemFree(pIpNbtGroupList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pNameAddr->Verify == LOCAL_NAME);
|
|
//
|
|
// For local names, first check if the name is registered on this device
|
|
//
|
|
if ((!(IsDeviceNetbiosless(pDeviceContext)) && (pDeviceContext->IpAddress) &&
|
|
(pNameAddr->AdapterMask & pDeviceContext->AdapterMask)) ||
|
|
((IsDeviceNetbiosless(pDeviceContext)) &&
|
|
(pNameAddr->NameFlags & NAME_REGISTERED_ON_SMBDEV)))
|
|
{
|
|
IpAddress = pDeviceContext->IpAddress;
|
|
}
|
|
//
|
|
// If the strict source routing option is not set, then return the first valid local address
|
|
//
|
|
else if (!NbtConfig.ConnectOnRequestedInterfaceOnly)
|
|
{
|
|
//
|
|
// Find the first device with a valid IP address that this name is registered on
|
|
//
|
|
pHead = pEntry = &NbtConfig.DeviceContexts;
|
|
while ((pEntry = pEntry->Flink) != pHead)
|
|
{
|
|
pThisDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage);
|
|
if ((pThisDeviceContext->IpAddress) &&
|
|
(pThisDeviceContext->AdapterMask & pNameAddr->AdapterMask))
|
|
{
|
|
IpAddress = pThisDeviceContext->IpAddress;
|
|
pNameAddr->IpAddress = pThisDeviceContext->IpAddress;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we failed to find the name registered on any of the legacy
|
|
// devices, then we should check if the name is registered on the
|
|
// SMBDevice and if so, return its IP address.
|
|
//
|
|
if ((!IpAddress) &&
|
|
(pNbtSmbDevice) &&
|
|
(pNameAddr->NameFlags & NAME_REGISTERED_ON_SMBDEV))
|
|
{
|
|
IpAddress = pNbtSmbDevice->IpAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((IpAddress) && (pIpAddress))
|
|
{
|
|
*pIpAddress = IpAddress;
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
RemoveDuplicateAddresses(
|
|
tIPADDRESS *pIpAddrBuffer,
|
|
ULONG *pNumAddrs
|
|
)
|
|
{
|
|
ULONG NumAddrs = *pNumAddrs;
|
|
ULONG i, j;
|
|
|
|
for (i=0; i<NumAddrs; i++) {
|
|
for (j=i+1; j<NumAddrs; j++) {
|
|
if (pIpAddrBuffer[i] == pIpAddrBuffer[j]) {
|
|
NumAddrs--;
|
|
pIpAddrBuffer[j] = pIpAddrBuffer[NumAddrs];
|
|
j--;
|
|
}
|
|
}
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.RemoveDuplicateAddresses: NumAddresses = <%d> --> <%d>\n", *pNumAddrs, NumAddrs));
|
|
|
|
*pNumAddrs = NumAddrs;
|
|
}
|
|
|
|
VOID
|
|
CountAndCopyAddrs(
|
|
tIPADDRESS *pIpAddrSrc,
|
|
tIPADDRESS *pIpAddrDest,
|
|
ULONG *pNumAddrs
|
|
)
|
|
{
|
|
ULONG NumAddrs = *pNumAddrs;
|
|
|
|
if (pIpAddrSrc)
|
|
{
|
|
while (*pIpAddrSrc != (ULONG)-1)
|
|
{
|
|
if (*pIpAddrSrc)
|
|
{
|
|
if (pIpAddrDest)
|
|
{
|
|
pIpAddrDest[NumAddrs] = *pIpAddrSrc;
|
|
}
|
|
NumAddrs++;
|
|
}
|
|
|
|
pIpAddrSrc++;
|
|
}
|
|
}
|
|
|
|
*pNumAddrs = NumAddrs;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
GetListOfAllAddrs(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN tNAMEADDR *p1CNameAddr,
|
|
IN tIPADDRESS **ppIpBuffer,
|
|
IN ULONG *pNumAddrs
|
|
)
|
|
{
|
|
ULONG i;
|
|
tIPADDRESS *pIpBuffer;
|
|
tIPADDRESS *pIpAddr;
|
|
ULONG NumAddrs = 0;
|
|
BOOLEAN fAddBcastAddr = FALSE;
|
|
|
|
*ppIpBuffer = NULL;
|
|
if (pNumAddrs)
|
|
{
|
|
*pNumAddrs = 0;
|
|
}
|
|
|
|
//
|
|
// First count all the addresses
|
|
//
|
|
if (pNameAddr->pLmhSvcGroupList) // if the name was Preloaded from LmHosts
|
|
{
|
|
ASSERT(pNameAddr->NameTypeState & NAMETYPE_INET_GROUP);
|
|
CountAndCopyAddrs (pNameAddr->pLmhSvcGroupList, NULL, &NumAddrs);
|
|
}
|
|
else
|
|
{
|
|
if (pNameAddr->IpAddress)
|
|
{
|
|
NumAddrs++;
|
|
}
|
|
|
|
//
|
|
// RemoteCacheLen will be 0 if we had failed to allocate the pRemoteIpAddrs structure earlier
|
|
//
|
|
for (i=0; i<pNameAddr->RemoteCacheLen; i++)
|
|
{
|
|
CountAndCopyAddrs (pNameAddr->pRemoteIpAddrs[i].pOrigIpAddrs, NULL, &NumAddrs);
|
|
if (pNameAddr->pRemoteIpAddrs[i].IpAddress)
|
|
{
|
|
NumAddrs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p1CNameAddr)
|
|
{
|
|
//
|
|
// This would a name that was added through LmHosts, so it will
|
|
// not have been resolved-per-interface from Wins!
|
|
//
|
|
ASSERT((p1CNameAddr->NameTypeState & NAMETYPE_INET_GROUP) && (!p1CNameAddr->pRemoteIpAddrs));
|
|
CountAndCopyAddrs (p1CNameAddr->pLmhSvcGroupList, NULL, &NumAddrs);
|
|
}
|
|
|
|
if (!NumAddrs)
|
|
{
|
|
return (STATUS_BAD_NETWORK_PATH);
|
|
}
|
|
|
|
NumAddrs++; // For the terminating address
|
|
if ((pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) &&
|
|
(!(pNameAddr->fPreload)))
|
|
{
|
|
// Add the bcast address
|
|
fAddBcastAddr = TRUE;
|
|
NumAddrs++; // For the bcast address
|
|
}
|
|
|
|
if (!(pIpBuffer = NbtAllocMem((NumAddrs*sizeof(tIPADDRESS)),NBT_TAG('N'))))
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Now copy all the addresses starting with the broadcast address if necessary
|
|
//
|
|
NumAddrs = 0;
|
|
if (fAddBcastAddr)
|
|
{
|
|
pIpBuffer[0] = 0;
|
|
NumAddrs++;
|
|
}
|
|
|
|
if (pNameAddr->pLmhSvcGroupList) // if the name was Preloaded from LmHosts
|
|
{
|
|
CountAndCopyAddrs (pNameAddr->pLmhSvcGroupList, pIpBuffer, &NumAddrs);
|
|
}
|
|
else
|
|
{
|
|
if (pNameAddr->IpAddress)
|
|
{
|
|
pIpBuffer[NumAddrs] = pNameAddr->IpAddress;
|
|
NumAddrs++;
|
|
}
|
|
for (i=0; i<pNameAddr->RemoteCacheLen; i++)
|
|
{
|
|
CountAndCopyAddrs (pNameAddr->pRemoteIpAddrs[i].pOrigIpAddrs, pIpBuffer, &NumAddrs);
|
|
if (pNameAddr->pRemoteIpAddrs[i].IpAddress)
|
|
{
|
|
pIpBuffer[NumAddrs] = pNameAddr->pRemoteIpAddrs[i].IpAddress;
|
|
NumAddrs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p1CNameAddr)
|
|
{
|
|
CountAndCopyAddrs (p1CNameAddr->pLmhSvcGroupList, pIpBuffer, &NumAddrs);
|
|
}
|
|
|
|
RemoveDuplicateAddresses(pIpBuffer, &NumAddrs);
|
|
pIpBuffer[NumAddrs] = (tIPADDRESS)-1;
|
|
|
|
*ppIpBuffer = pIpBuffer;
|
|
if (pNumAddrs)
|
|
{
|
|
*pNumAddrs = NumAddrs;
|
|
}
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FilterIpAddrsForDevice(
|
|
IN tIPADDRESS *pIpAddr,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN ULONG *pNumAddrs
|
|
)
|
|
{
|
|
ULONG i;
|
|
ULONG Interface, Metric;
|
|
ULONG NumAddrs = *pNumAddrs;
|
|
|
|
ASSERT (NumAddrs > 0);
|
|
if (NbtConfig.SendDgramOnRequestedInterfaceOnly)
|
|
{
|
|
for (i=1; i<NumAddrs; i++)
|
|
{
|
|
pDeviceContext->pFastQuery(ntohl(pIpAddr[i]), &Interface, &Metric);
|
|
if (Interface != pDeviceContext->IPInterfaceContext)
|
|
{
|
|
pIpAddr[i] = pIpAddr[NumAddrs-1];
|
|
NumAddrs--;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
*pNumAddrs = NumAddrs;
|
|
pIpAddr[NumAddrs] = (tIPADDRESS) -1;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtOpenAddress(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN TA_ADDRESS *pTaAddress,
|
|
IN tIPADDRESS IpAddress,
|
|
IN tDEVICECONTEXT *pContext,
|
|
IN PVOID pIrp)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles opening an address for a Client.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
tADDRESSELE *pAddrElement;
|
|
tCLIENTELE *pClientEle;
|
|
USHORT uAddrType;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
PUCHAR pNameRslv;
|
|
tNAMEADDR *pNameAddr;
|
|
COMPLETIONCLIENT pClientCompletion;
|
|
PVOID Context;
|
|
tTIMERQENTRY *pTimer;
|
|
BOOLEAN MultiHomedReRegister = FALSE;
|
|
BOOLEAN DontIncrement= FALSE;
|
|
ULONG TdiAddressType;
|
|
UCHAR *BroadcastName = "\x2a\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0";
|
|
LIST_ENTRY *pClientEntry;
|
|
tCLIENTELE *pClientEleTemp;
|
|
BOOLEAN fFirstClientOnDevice = TRUE;
|
|
|
|
ASSERT(pTaAddress);
|
|
if (!IpAddress)
|
|
{
|
|
//
|
|
// when there is no ip address yet, use the Loop back address as
|
|
// a default rather than null, since null tells NbtRegisterName
|
|
// that the address is already in the name table and it only needs
|
|
// to be reregistered.
|
|
//
|
|
IpAddress = LOOP_BACK;
|
|
}
|
|
|
|
TdiAddressType = pTaAddress->AddressType;
|
|
switch (TdiAddressType)
|
|
{
|
|
case TDI_ADDRESS_TYPE_NETBIOS:
|
|
{
|
|
PTDI_ADDRESS_NETBIOS pNetbiosAddress = (PTDI_ADDRESS_NETBIOS)pTaAddress->Address;
|
|
|
|
uAddrType = pNetbiosAddress->NetbiosNameType;
|
|
pNameRslv = pNetbiosAddress->NetbiosName;
|
|
|
|
break;
|
|
}
|
|
|
|
#ifndef VXD
|
|
case TDI_ADDRESS_TYPE_NETBIOS_EX:
|
|
{
|
|
// The NETBIOS_EX address passed in will have two components,
|
|
// an Endpoint name as well as the NETBIOS address.
|
|
// In this implementation we ignore the second
|
|
// component and register the Endpoint name as a netbios
|
|
// address.
|
|
|
|
PTDI_ADDRESS_NETBIOS_EX pNetbiosExAddress = (PTDI_ADDRESS_NETBIOS_EX)pTaAddress->Address;
|
|
|
|
uAddrType = TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE;
|
|
pNameRslv = pNetbiosExAddress->EndpointName;
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
return STATUS_INVALID_ADDRESS_COMPONENT;
|
|
}
|
|
|
|
//
|
|
// If the name is a Broadcast name, it can only be opened as a Group name
|
|
//
|
|
if ((CTEMemEqu (BroadcastName, pNameRslv, NETBIOS_NAME_SIZE)) &&
|
|
(uAddrType != NBT_GROUP))
|
|
{
|
|
KdPrint (("Nbt.NbtOpenAddress: Warning: Opening broadcast name as Groupname!\n"));
|
|
uAddrType = NBT_GROUP;
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtOpenAddress: Name=<%-16.16s:%x>, pDevice=<%p>\n",
|
|
pNameRslv, pNameRslv[15], pContext));
|
|
|
|
//
|
|
// be sure the broadcast name has 15 zeroes after it
|
|
//
|
|
if ((pNameRslv[0] == '*') && (TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS))
|
|
{
|
|
CTEZeroMemory(&pNameRslv[1],NETBIOS_NAME_SIZE-1);
|
|
}
|
|
|
|
// this synchronizes access to the local name table when a new name
|
|
// is registered. Basically it will not let the second registrant through
|
|
// until the first has put the name into the local table (i.e.
|
|
// NbtRegisterName has returned )
|
|
//
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
|
|
// see if the name is registered on the local node.. we call the hash
|
|
// table function directly rather than using findname, because find name
|
|
// checks the state of the name too. We want to know if the name is in
|
|
// the table at all, and don't care if it is still resolving.
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
pNameAddr = NULL;
|
|
status = FindInHashTable (pNbtGlobConfig->pLocalHashTbl, pNameRslv, NbtConfig.pScope, &pNameAddr);
|
|
|
|
//
|
|
// the name could be in the hash table, but the address element deleted
|
|
//
|
|
if (!NT_SUCCESS(status) || !pNameAddr->pAddressEle)
|
|
{
|
|
//
|
|
// pNameAddr->pAddressEle is NULL <==> the Name is currently being released
|
|
//
|
|
if (pNameAddr)
|
|
{
|
|
//
|
|
// Check if the name is about to be released on this adapter
|
|
//
|
|
if (pNameAddr->AdapterMask & pContext->AdapterMask)
|
|
{
|
|
pNameAddr->AdapterMask &= ~pContext->AdapterMask;
|
|
}
|
|
//
|
|
// Check if the name is currently being released on this adapter
|
|
//
|
|
else if (pNameAddr->ReleaseMask & pContext->AdapterMask)
|
|
{
|
|
// Set the ReleaseMask bit to 0 so that the Timeout routine
|
|
// does does not send this release out on the wire again
|
|
//
|
|
pNameAddr->ReleaseMask &= ~pContext->AdapterMask;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// open the name since it could not be found
|
|
//
|
|
// first of all allocate memory for the address block
|
|
//
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
if (pAddrElement = (tADDRESSELE *) NbtAllocMem(sizeof(tADDRESSELE),NBT_TAG('C')))
|
|
{
|
|
CTEZeroMemory(pAddrElement,sizeof(tADDRESSELE));
|
|
InitializeListHead(&pAddrElement->Linkage);
|
|
InitializeListHead(&pAddrElement->ClientHead);
|
|
CTEInitLock(&pAddrElement->LockInfo.SpinLock);
|
|
#if DBG
|
|
pAddrElement->LockInfo.LockNumber = ADDRESS_LOCK;
|
|
#endif
|
|
pAddrElement->AddressType = TdiAddressType;
|
|
if ((uAddrType == NBT_UNIQUE ) || (uAddrType == NBT_QUICK_UNIQUE))
|
|
{
|
|
pAddrElement->NameType = NBT_UNIQUE;
|
|
}
|
|
else
|
|
{
|
|
pAddrElement->NameType = NBT_GROUP;;
|
|
}
|
|
|
|
pAddrElement->Verify = NBT_VERIFY_ADDRESS;
|
|
NBT_REFERENCE_ADDRESS (pAddrElement, REF_ADDR_NEW_CLIENT);
|
|
|
|
// create client block and link to addresslist. This allows multiple
|
|
// clients to open the same address - for example a group name must
|
|
// be able to handle multiple clients, each receiving datagrams to it.
|
|
//
|
|
if (pClientEle = NbtAllocateClientBlock(pAddrElement, pContext))
|
|
{
|
|
pClientEle->AddressType = TdiAddressType;
|
|
pClientEle->pIrp = pIrp; // Track Irp -- complete it when the name registration completes
|
|
#ifndef VXD
|
|
// set the share access ( NT only ) - security descriptor stuff
|
|
if (pIrp)
|
|
{
|
|
status = NTSetSharedAccess(pContext,pIrp,pAddrElement);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// unable to set the share access correctly so release the
|
|
// address object and the client block connected to it
|
|
NbtFreeAddressObj(pAddrElement);
|
|
NbtFreeClientObj(pClientEle);
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return(status);
|
|
}
|
|
|
|
// fill in the context values passed back to the client. These must
|
|
// be done before the name is registered on the network because the
|
|
// registration can succeed (or fail) before this routine finishes).
|
|
// Since this routine can be called by NBT itself, pIrp may not be set,
|
|
// so check for it.
|
|
//
|
|
if (pIrp)
|
|
{
|
|
NTSetFileObjectContexts( pClientEle->pIrp,(PVOID)pClientEle, (PVOID)(NBT_ADDRESS_TYPE));
|
|
}
|
|
#endif //!VXD
|
|
|
|
// pass back the client block address as a handle for future reference
|
|
// to the client
|
|
pRequest->Handle.AddressHandle = (PVOID)pClientEle;
|
|
|
|
// then add it to name service local name Q, passing the address of
|
|
// the block as a context value ( so that subsequent finds return the
|
|
// context value.
|
|
// we need to know if the name is a group name or a unique name.
|
|
// This registration may take some time so we return STATUS_PENDING
|
|
// to the client
|
|
//
|
|
|
|
NBT_REFERENCE_ADDRESS (pAddrElement, REF_ADDR_REGISTER_NAME);
|
|
status = NbtRegisterName (NBT_LOCAL,
|
|
IpAddress,
|
|
pNameRslv,
|
|
NULL,
|
|
pClientEle, // context value
|
|
(PVOID)NbtRegisterCompletion, // completion routine for
|
|
uAddrType, // Name Srv to call
|
|
pContext);
|
|
//
|
|
// ret status could be either status pending or status success since Quick
|
|
// names return success - or status failure
|
|
//
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
if (pIrp)
|
|
{
|
|
pClientEle->pIrp = NULL;
|
|
NTClearFileObjectContext(pIrp);
|
|
}
|
|
|
|
ASSERT(pAddrElement->RefCount == 2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
NBT_DEREFERENCE_CLIENT (pClientEle);
|
|
NBT_DEREFERENCE_ADDRESS (pAddrElement, REF_ADDR_REGISTER_NAME);
|
|
return (status);
|
|
}
|
|
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("Client open address %!NBTNAME!<%02x> ClientEle=%p",
|
|
pNameRslv, (unsigned)pNameRslv[15], pClientEle));
|
|
|
|
// link the address element to the head of the address list
|
|
// The Joint Lock protects this operation.
|
|
ExInterlockedInsertTailList(&NbtConfig.AddressHead,
|
|
&pAddrElement->Linkage,
|
|
&NbtConfig.JointLock.LockInfo.SpinLock);
|
|
|
|
NBT_DEREFERENCE_ADDRESS (pAddrElement, REF_ADDR_REGISTER_NAME);
|
|
} // if pClientEle
|
|
else
|
|
{
|
|
NbtFreeAddressObj(pAddrElement);
|
|
pAddrElement = NULL;
|
|
}
|
|
|
|
} // if pAddrElement
|
|
|
|
}
|
|
else
|
|
{
|
|
pAddrElement = (tADDRESSELE *)pNameAddr->pAddressEle;
|
|
|
|
//
|
|
// increment here before releasing the spinlock so that a name
|
|
// release done cannot free pAddrElement.
|
|
//
|
|
NBT_REFERENCE_ADDRESS (pAddrElement, REF_ADDR_NEW_CLIENT);
|
|
|
|
#ifndef VXD
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// check the shared access of the name - this check must be done
|
|
// at Irl = 0, so no spin locks held
|
|
//
|
|
if (pIrp)
|
|
{
|
|
status = NTCheckSharedAccess (pContext, pIrp, (tADDRESSELE *)pNameAddr->pAddressEle);
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pAddrElement,OldIrq1);
|
|
#else
|
|
//
|
|
// For the Vxd, we don't allow multiple names in the local name table.
|
|
// In NT, this is prevented on a per process basis by the Netbios
|
|
// driver. If the name is being deregistered (conflict) then allow
|
|
// the client to reopen the name
|
|
//
|
|
if ( !(pNameAddr->NameTypeState & STATE_CONFLICT))
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Write the correct Ip address to the table incase this
|
|
// was a group name and has now changed to a unique
|
|
// name, but don't overwrite with the loop back address because
|
|
// that means that the adapter does not have an address yet.
|
|
// For Group names the Ip address stays as 0, so we know to do a
|
|
// broadcast.
|
|
//
|
|
if ((IpAddress != LOOP_BACK) &&
|
|
(pNameAddr->NameTypeState & NAMETYPE_UNIQUE))
|
|
{
|
|
pNameAddr->IpAddress = IpAddress;
|
|
}
|
|
|
|
// multihomed hosts register the same unique name on several adapters.
|
|
// NT DOES allow a client to share a unique name, so we must NOT
|
|
// run this next code if the NT check has passed!!
|
|
//
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// if this is a unique name being registered on another adapter
|
|
// then allow it to occur - the assumption is that the same
|
|
// client is registering on more than one adapter all at once,
|
|
// rather than two different clients.
|
|
//
|
|
if (NbtConfig.MultiHomed && (!(pNameAddr->AdapterMask & pContext->AdapterMask)))
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// check if this is a client trying to add the permanent name,
|
|
// since that name will fail the security check
|
|
// We allow a single client to use the permanent name - since its
|
|
// a unique name it will fail the Vxd check too.
|
|
//
|
|
else if (CTEMemEqu(&pNameAddr->Name[10], &pContext->MacAddress.Address[0], sizeof(tMAC_ADDRESS)))
|
|
{
|
|
// check if there is just one element on the client list. If so
|
|
// then the permanent name is not being used yet - i.e. it has
|
|
// been opened once by the NBT code itself so the node will
|
|
// answer Nodestatus requests to the name, but no client
|
|
// has opened it yet
|
|
//
|
|
if (pAddrElement->ClientHead.Flink->Flink == &pAddrElement->ClientHead)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else if ((pNameAddr->NameTypeState & STATE_CONFLICT))
|
|
{
|
|
// check if the name is in the process of being deregisterd -
|
|
// STATE_CONFLICT - in this case allow it to carry on and take over
|
|
// name.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
(pNameAddr->NameTypeState & STATE_CONFLICT))
|
|
{
|
|
// this could either be a real conflict or a name being deleted on
|
|
// the net, so stop any timer associated with the name release
|
|
// and carry on
|
|
//
|
|
if (pTimer = pNameAddr->pTimer)
|
|
{
|
|
// this routine puts the timer block back on the timer Q, and
|
|
// handles race conditions to cancel the timer when the timer
|
|
// is expiring.
|
|
pNameAddr->pTimer = NULL;
|
|
status = StopTimer(pTimer,&pClientCompletion,&Context);
|
|
|
|
// there is a client's irp waiting for the name release to finish
|
|
// so complete that irp back to them
|
|
if (pClientCompletion)
|
|
{
|
|
//
|
|
// NOTE****
|
|
// We must clear the AdapterMask so that NameReleaseDone
|
|
// does not try to release the name on another net card
|
|
// for the multihomed case.
|
|
//
|
|
CHECK_PTR(pNameAddr);
|
|
CTESpinFree(pAddrElement,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
(*pClientCompletion)(Context,STATUS_SUCCESS);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pAddrElement,OldIrq1);
|
|
}
|
|
CHECK_PTR(pNameAddr);
|
|
}
|
|
//
|
|
// this allows another client to use a name almost immediately
|
|
// after the first one releases the name on the net. However
|
|
// if the first client has not released the name yet, and is
|
|
// still on the clienthead list, then the name will not be
|
|
// reregistered, and this current registration will fail because
|
|
// the name state is conflict. That check is done below.
|
|
//
|
|
if (IsListEmpty(&pAddrElement->ClientHead))
|
|
{
|
|
pNameAddr->NameTypeState &= ~NAME_STATE_MASK;
|
|
pNameAddr->NameTypeState |= STATE_RESOLVED;
|
|
status = STATUS_SUCCESS;
|
|
MultiHomedReRegister = TRUE;
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtOpenAddress: Conflict State, re-registering name on net\n"));
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
//
|
|
// Don't log the event:
|
|
// The current name state is already in CONFLICTED state,
|
|
// we should have already logged an event when we change
|
|
// its state into CONFLICTED.
|
|
//
|
|
|
|
// set status that indicates someone else has the name on the
|
|
// network.
|
|
//
|
|
if (!IS_MESSENGER_NAME(pNameRslv))
|
|
{
|
|
//
|
|
// We need to Q this event to a Worker thread since it
|
|
// requires the name to be converted to Unicode
|
|
//
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_LOG_EVENT);
|
|
status = NTQueueToWorkerThread(NULL, DelayedNbtLogDuplicateNameEvent,
|
|
(PVOID) pNameAddr,
|
|
IntToPtr(IpAddress),
|
|
IntToPtr(0x106),
|
|
pContext,
|
|
TRUE);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_LOG_EVENT, TRUE);
|
|
NbtLogEvent (EVENT_NBT_DUPLICATE_NAME, IpAddress, 0x106);
|
|
}
|
|
}
|
|
#endif
|
|
status = STATUS_DUPLICATE_NAME;
|
|
}
|
|
}
|
|
else if (NT_SUCCESS(status))
|
|
{
|
|
// name already exists - is open; allow only another client creating a
|
|
// name of the same type
|
|
//
|
|
if ((uAddrType == NBT_UNIQUE) || ( uAddrType == NBT_QUICK_UNIQUE))
|
|
{
|
|
if (!(pNameAddr->NameTypeState & NAMETYPE_UNIQUE))
|
|
{
|
|
status = STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
else if (!(pNameAddr->NameTypeState & NAMETYPE_GROUP))
|
|
{
|
|
status = STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
// if everything is OK, create client block and link to addresslist
|
|
// pass back the client block address as a handle for future reference
|
|
// to the client
|
|
if ((NT_SUCCESS(status)) &&
|
|
(!(pClientEle = NbtAllocateClientBlock (pAddrElement, pContext))))
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// check for a failure, if so , then return
|
|
//
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
CHECK_PTR(pRequest);
|
|
pRequest->Handle.AddressHandle = NULL;
|
|
|
|
CTESpinFree(pAddrElement,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
NBT_DEREFERENCE_ADDRESS (pAddrElement, REF_ADDR_NEW_CLIENT);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return(status);
|
|
}
|
|
|
|
// we need to track the Irp so that when the name registration
|
|
// completes, we can complete the Irp.
|
|
pClientEle->pIrp = pIrp;
|
|
pClientEle->AddressType = TdiAddressType;
|
|
|
|
pRequest->Handle.AddressHandle = (PVOID)pClientEle;
|
|
|
|
// fill in the context values passed back to the client. These must
|
|
// be done before the name is registered on the network because the
|
|
// registration can succeed (or fail) before this routine finishes).
|
|
// Since this routine can be called by NBT itself, there may not be an
|
|
// irp to fill in, so check first.
|
|
if (pIrp)
|
|
{
|
|
#ifndef VXD
|
|
NTSetFileObjectContexts( pClientEle->pIrp,(PVOID)pClientEle, (PVOID)(NBT_ADDRESS_TYPE));
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// See if this is not the first Client on this Device
|
|
//
|
|
pClientEntry = &pAddrElement->ClientHead;
|
|
while ((pClientEntry = pClientEntry->Flink) != &pAddrElement->ClientHead)
|
|
{
|
|
pClientEleTemp = CONTAINING_RECORD (pClientEntry,tCLIENTELE,Linkage);
|
|
if ((pClientEleTemp != pClientEle) &&
|
|
(pClientEleTemp->pDeviceContext == pContext))
|
|
{
|
|
fFirstClientOnDevice = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fFirstClientOnDevice)
|
|
{
|
|
if (IsDeviceNetbiosless(pContext))
|
|
{
|
|
pNameAddr->NameFlags |= NAME_REGISTERED_ON_SMBDEV;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// turn on the adapter's bit in the adapter Mask and set the
|
|
// re-register flag (if the name is not resolving already) so
|
|
// we register the name out the new adapter.
|
|
//
|
|
pNameAddr->AdapterMask |= pContext->AdapterMask;
|
|
if (pNameAddr->NameTypeState & STATE_RESOLVED)
|
|
{
|
|
MultiHomedReRegister = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the adapter bit is already on in the pAddressEle, so
|
|
// this must be another client registering the same name,
|
|
// therefore turn on the MultiClient boolean so that the DgramRcv
|
|
// code will know to activate its multiple client rcv code.
|
|
//
|
|
pAddrElement->MultiClients = TRUE;
|
|
}
|
|
|
|
//
|
|
// check the state of the entry in the table. If the state is
|
|
// resolved then complete the request now,otherwise we cannot complete
|
|
// this request yet... i.e. we return Pending.
|
|
//
|
|
if (((pNameAddr->NameTypeState & STATE_RESOLVED) &&
|
|
(!MultiHomedReRegister)))
|
|
{
|
|
// basically we are all done now, so just return status success
|
|
// to the client
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
|
|
CHECK_PTR(pClientEle);
|
|
pClientEle->pIrp = NULL;
|
|
CTESpinFree(pAddrElement,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
pClientEle->WaitingForRegistration = FALSE;
|
|
}
|
|
else
|
|
{
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtOpenAddress: Waiting for prev registration- state=%x, ReRegister=%x\n",
|
|
pNameAddr->NameTypeState, MultiHomedReRegister));
|
|
|
|
// we need to track the Irp so that when the name registration
|
|
// completes, we can complete the Irp.
|
|
pClientEle->pIrp = pIrp;
|
|
|
|
CTESpinFree(pAddrElement,OldIrq1);
|
|
if (MultiHomedReRegister)
|
|
{
|
|
// this flag is used by RegisterCompletion ( when true )
|
|
pClientEle->WaitingForRegistration = FALSE;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtOpenAddress: Resolved State=%x, ReRegister=%x\n",
|
|
pNameAddr->NameTypeState, MultiHomedReRegister));
|
|
|
|
// we need to re-register the name on the net because it is not
|
|
// currently in the resolved state and there is no timer active
|
|
// We do that by calling this routine with the IpAddress set to NULL
|
|
// to signal that routine not to put the name in the hash table
|
|
// since it is already there.
|
|
//
|
|
status = NbtRegisterName (NBT_LOCAL,
|
|
0, // set to zero to signify already in tbl
|
|
pNameRslv,
|
|
pNameAddr,
|
|
pClientEle,
|
|
(PVOID)NbtRegisterCompletion,
|
|
uAddrType,
|
|
pContext);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
if (pIrp)
|
|
{
|
|
pClientEle->pIrp = NULL;
|
|
NTClearFileObjectContext(pIrp);
|
|
}
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
NBT_DEREFERENCE_CLIENT (pClientEle);
|
|
return (status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pClientEle->WaitingForRegistration = TRUE;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// for multihomed, a second registration on a second adapter
|
|
// at the same time as the first adapter is registering is
|
|
// delayed until the first completes, then its registration
|
|
// proceeds - See RegistrationCompletion below.
|
|
//
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
}
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
#ifdef _PNP_POWER_
|
|
//
|
|
// See if we need to set the Wakeup pattern on this Device
|
|
//
|
|
if ((NT_SUCCESS(status)) &&
|
|
(*pNameRslv != '*') &&
|
|
(pNameRslv[NETBIOS_NAME_SIZE-1] == SPECIAL_SERVER_SUFFIX))
|
|
{
|
|
pContext->NumServers++;
|
|
CheckSetWakeupPattern (pContext, pNameRslv, TRUE);
|
|
}
|
|
#endif
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtRegisterCompletion(
|
|
IN tCLIENTELE *pClientEleIn,
|
|
IN NTSTATUS status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles completing a name registration request. The namesrv.c
|
|
Name server calls this routine when it has registered a name. The address
|
|
of this routine is passed to the Local Name Server in the NbtRegisterName
|
|
request.
|
|
|
|
The idea is to complete the irps that are waiting on the name registration,
|
|
one per client element.
|
|
|
|
When a DHCP reregister occurs there is no client irp so the name is
|
|
not actually deleted from the table when a bad status is passed to this
|
|
routine. Hence the need for the DhcpRegister flag to change the code
|
|
path for that case.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY *pHead;
|
|
LIST_ENTRY *pEntry;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
tADDRESSELE *pAddress;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
tNAMEADDR *pNameAddr;
|
|
tCLIENTELE *pClientEle;
|
|
LIST_ENTRY TempList;
|
|
ULONG Count=0;
|
|
|
|
InitializeListHead(&TempList);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
pAddress = pClientEleIn->pAddress;
|
|
pDeviceContext = pClientEleIn->pDeviceContext;
|
|
|
|
CTESpinLock(pAddress,OldIrq);
|
|
|
|
// Several Clients can open the same address at the same time, so when the
|
|
// name registration completes, it should complete all of them!!
|
|
|
|
|
|
// increment the reference count so that the hash table entry cannot
|
|
// disappear while we are using it.
|
|
//
|
|
NBT_REFERENCE_ADDRESS (pAddress, REF_ADDR_REG_COMPLETION);
|
|
pNameAddr = pAddress->pNameAddr;
|
|
|
|
pNameAddr->NameTypeState &= ~NAME_STATE_MASK;
|
|
pNameAddr->pTimer = NULL; // Bug #: 231693
|
|
|
|
// if the registration failed or a previous registration failed for the
|
|
// multihomed case, deny the client the name
|
|
//
|
|
if ((status == STATUS_SUCCESS) || (status == STATUS_TIMEOUT))
|
|
{
|
|
pNameAddr->NameTypeState |= STATE_RESOLVED;
|
|
}
|
|
else
|
|
{
|
|
pNameAddr->NameTypeState |= STATE_CONFLICT;
|
|
pNameAddr->ConflictMask |= pDeviceContext->AdapterMask;
|
|
status = STATUS_DUPLICATE_NAME;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// find all clients that are attached to the address and complete the
|
|
// I/O requests, if they are on the same adapter that the name was
|
|
// just registered against, if successful. For failure cases complete
|
|
// all irps with the failure code - i.e. failure to register a name on
|
|
// one adapter fails all adapters.
|
|
//
|
|
FailRegistration:
|
|
pHead = &pAddress->ClientHead;
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
// complete the I/O
|
|
pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage);
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
//
|
|
// It is possible for the second registration of a name to fail so
|
|
// we do not want to attempt to return the irp on the first
|
|
// registration, which has completed ok already. Therefore
|
|
// if the status is failure, then only complete those clients that
|
|
// have the WaitingForReg... bit set
|
|
//
|
|
// if it is the client ele passed in, or one on the same device context
|
|
// that is waiting for a name registration, or it is a failure...
|
|
// AND the client IRP is still valid then return the Irp.
|
|
//
|
|
if ((pClientEle->pIrp) &&
|
|
((pClientEle == pClientEleIn) ||
|
|
((pClientEle->pDeviceContext == pDeviceContext) && (pClientEle->WaitingForRegistration)) ||
|
|
((status != STATUS_SUCCESS) && pClientEle->WaitingForRegistration)))
|
|
{
|
|
// for failed registrations, remove the client from the address list
|
|
// since we are going to delete him below.
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// turn off the adapter bit so we know not to use this name with this
|
|
// adapter - since it is a failure, turn off all adapter bits
|
|
// since a single name registration failure means all registrations
|
|
// fail.
|
|
CHECK_PTR(pNameAddr);
|
|
pNameAddr->AdapterMask = 0;
|
|
|
|
// setting this to null prevents CloseAddress and CleanupAddress
|
|
// from accessing pAddress and crashing.
|
|
//
|
|
CHECK_PTR(pClientEle);
|
|
pClientEle->pAddress = NULL;
|
|
|
|
// clear the ptr to the ClientEle that NbtRegisterName put into
|
|
// the irp ( i.e. the context values are cleared )
|
|
//
|
|
#ifndef VXD
|
|
NTSetFileObjectContexts(pClientEle->pIrp,NULL,NULL);
|
|
#endif
|
|
RemoveEntryList(&pClientEle->Linkage);
|
|
}
|
|
|
|
ASSERT(pClientEle->pIrp);
|
|
|
|
pClientEle->WaitingForRegistration = FALSE;
|
|
|
|
#ifndef VXD
|
|
// put all irps that have to be completed on a separate list
|
|
// and then complete later after releaseing the spin lock.
|
|
//
|
|
InsertTailList(&TempList,&pClientEle->pIrp->Tail.Overlay.ListEntry);
|
|
#else
|
|
//
|
|
// pAddress gets set in the name table for this NCB
|
|
//
|
|
Count++;
|
|
CTESpinFree(pAddress,OldIrq1);
|
|
CTEIoComplete( pClientEle->pIrp, status, (ULONG) pClientEle ) ;
|
|
CTESpinLock(pAddress,OldIrq1);
|
|
#endif
|
|
CHECK_PTR(pClientEle);
|
|
pClientEle->pIrp = NULL ;
|
|
|
|
// free the client object memory
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
NbtFreeClientObj(pClientEle);
|
|
}
|
|
}
|
|
}
|
|
|
|
CTESpinFree(pAddress,OldIrq1);
|
|
|
|
#ifndef VXD
|
|
//
|
|
// for the NT case where MP - ness can disrupt the list at any
|
|
// time, scan the whole list above without releasing the spin lock,
|
|
// and then complete the irps collected here
|
|
//
|
|
while (!IsListEmpty(&TempList))
|
|
{
|
|
PIRP pIrp;
|
|
|
|
pEntry = RemoveHeadList(&TempList);
|
|
pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry);
|
|
|
|
CTEIoComplete(pIrp,status,0);
|
|
Count++;
|
|
}
|
|
#endif
|
|
|
|
|
|
// if the registration failed, do one more dereference of the address
|
|
// to remove the refcount added by this client. This may cause a name
|
|
// release on the network if there are no other clients registering
|
|
// the name.
|
|
//
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// dereference the address the same number of times that we have
|
|
// returned failed registrations since each reg. referenced pAddress
|
|
// once
|
|
//
|
|
while (Count--)
|
|
{
|
|
NBT_DEREFERENCE_ADDRESS (pAddress, REF_ADDR_NEW_CLIENT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
USHORT uAddrType;
|
|
|
|
CTESpinLock(pAddress,OldIrq1);
|
|
|
|
// go through the clients and see if any are waiting to register
|
|
// a name. This happens in the multihomed case, but should not
|
|
// happen in the single adapter case.
|
|
//
|
|
pHead = &pAddress->ClientHead;
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
// complete the I/O
|
|
pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage);
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
if (pClientEle->WaitingForRegistration)
|
|
{
|
|
ULONG SaveState;
|
|
|
|
pClientEle->WaitingForRegistration = FALSE;
|
|
|
|
if (pNameAddr->NameTypeState & NAMETYPE_UNIQUE)
|
|
{
|
|
uAddrType = NBT_UNIQUE;
|
|
}
|
|
else
|
|
uAddrType = NBT_GROUP;
|
|
|
|
//
|
|
// preserve the "QUICK"ness
|
|
//
|
|
if (pNameAddr->NameTypeState & NAMETYPE_QUICK)
|
|
{
|
|
uAddrType |= NBT_QUICK_UNIQUE;
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtRegisterCompletion: Registering next name state= %X,%15s<%X>\n",
|
|
pNameAddr->NameTypeState,pNameAddr->Name,pNameAddr->Name[15]));
|
|
|
|
SaveState = pNameAddr->NameTypeState;
|
|
|
|
CTESpinFree(pAddress,OldIrq1);
|
|
|
|
// this may be a multihomed host, with another name registration
|
|
// pending out another adapter, so start that registration.
|
|
status = NbtRegisterName (NBT_LOCAL,
|
|
0, // set to zero to signify already in tbl
|
|
pNameAddr->Name,
|
|
pNameAddr,
|
|
pClientEle,
|
|
(PVOID)NbtRegisterCompletion,
|
|
uAddrType,
|
|
pClientEle->pDeviceContext);
|
|
|
|
CTESpinLock(pAddress,OldIrq1);
|
|
|
|
// since nbtregister will set the state to Resolving, when
|
|
// it might be resolved already on one adapter.
|
|
pNameAddr->NameTypeState = SaveState;
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// if this fails for some reason, then fail any other name
|
|
// registrations pending. - the registername call should not
|
|
// fail unless we are out of resources.
|
|
pClientEle->WaitingForRegistration = TRUE;
|
|
goto FailRegistration;
|
|
}
|
|
// just register one name at a time, unless we get immediate success
|
|
else if (status == STATUS_PENDING)
|
|
{
|
|
break;
|
|
}
|
|
else // SUCCESS
|
|
{
|
|
CTESpinFree(pAddress,OldIrq1);
|
|
CTEIoComplete(pClientEle->pIrp,status,0);
|
|
pClientEle->pIrp = NULL;
|
|
CTESpinLock(pAddress,OldIrq1);
|
|
}
|
|
}
|
|
}
|
|
CTESpinFree(pAddress,OldIrq1);
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// Go through all the Clients still attached, and reset their
|
|
// AdapterMasks since we could have removed them
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pAddress,OldIrq1);
|
|
|
|
pEntry = pHead = &pAddress->ClientHead;
|
|
while ((pEntry = pEntry->Flink) != pHead)
|
|
{
|
|
pClientEle = CONTAINING_RECORD (pEntry,tCLIENTELE,Linkage);
|
|
if (!IsDeviceNetbiosless(pClientEle->pDeviceContext))
|
|
{
|
|
pNameAddr->AdapterMask |= pClientEle->pDeviceContext->AdapterMask;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(pAddress,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
// this decrements for the RefCount++ done in this routine.
|
|
NBT_DEREFERENCE_ADDRESS (pAddress, REF_ADDR_REG_COMPLETION);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtOpenConnection(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles creating a connection object for the client. It
|
|
passes back a ptr to the connection so that OS specific portions of the
|
|
data structure can be filled in.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
pConnectEle - ptr to the allocated connection data structure
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS ;
|
|
tCONNECTELE *pConnEle;
|
|
|
|
CTEPagedCode();
|
|
|
|
// Acquire this resource to co-ordinate with DHCP changing the IP address
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
|
|
if ((!pDeviceContext->pSessionFileObject) ||
|
|
(!(pConnEle = (tCONNECTELE *)NbtAllocMem(sizeof(tCONNECTELE),NBT_TAG('D')))))
|
|
{
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtOpenConnection: pConnEle = <%x>\n",pConnEle));
|
|
|
|
// This ensures that all BOOLEAN values begin with a FALSE value among other things.
|
|
CTEZeroMemory(pConnEle,sizeof(tCONNECTELE));
|
|
CTEInitLock(&pConnEle->LockInfo.SpinLock);
|
|
#if DBG
|
|
pConnEle->LockInfo.LockNumber = CONNECT_LOCK;
|
|
#endif
|
|
// initialize lists to empty
|
|
InitializeListHead(&pConnEle->RcvHead);
|
|
|
|
pConnEle->Verify = NBT_VERIFY_CONNECTION;
|
|
NBT_REFERENCE_CONNECTION (pConnEle, REF_CONN_CREATE); // so we don't delete the connection
|
|
SET_STATE_UPPER (pConnEle, NBT_IDLE);
|
|
pConnEle->pDeviceContext = pDeviceContext;
|
|
pConnEle->ConnectContext = ConnectionContext; // used in various event calls (eg. Receive, Disconnect)
|
|
|
|
//
|
|
// for each connection the client(s) open, open a connection to the transport
|
|
// so that we can accept one to one from the transport.
|
|
#ifndef VXD
|
|
//
|
|
// Allocate an MDL to be used for partial Mdls
|
|
// The length of the Mdl is set to 64K(MAXUSHORT) so that there are enough
|
|
// pfns in the Mdl to map a large buffer.
|
|
//
|
|
// use pConnEle as the Virtual address, since it doesn't matter
|
|
// because it will be overwritten when the partial Mdl is created.
|
|
//
|
|
if (pConnEle->pNewMdl = IoAllocateMdl ((PVOID)pConnEle, MAXUSHORT, FALSE, FALSE, NULL))
|
|
#endif
|
|
{
|
|
//
|
|
// allocate memory for the lower connection block.
|
|
//
|
|
status = NbtOpenAndAssocConnection(pDeviceContext, NULL, NULL, '2');
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// link on to list of open connections for this device so that we
|
|
// know how many open connections there are at any time (if we need to know)
|
|
// This linkage is only in place until the client does an associate, then
|
|
// the connection is unlinked from here and linked to the client ConnectHead.
|
|
//
|
|
ExInterlockedInsertHeadList(&pDeviceContext->UpConnectionInUse,
|
|
&pConnEle->Linkage,
|
|
&NbtConfig.JointLock.LockInfo.SpinLock);
|
|
|
|
// return the pointer to the block to the client as the connection id
|
|
pRequest->Handle.ConnectionContext = (PVOID)pConnEle;
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("New connection: pConnEle=%p pLowerConn=%p pDeviceContext=%p",
|
|
pConnEle, pConnEle->pLowerConnId, pConnEle->pDeviceContext));
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
#ifndef VXD
|
|
IoFreeMdl(pConnEle->pNewMdl);
|
|
#endif
|
|
}
|
|
#ifndef VXD
|
|
else
|
|
{
|
|
// ASSERTMSG("Nbt:Unable to allocate a MDL!!\n",0);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
#endif // !VXD
|
|
|
|
FreeConnectionObj(pConnEle);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtOpenAndAssocConnection(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN tCONNECTELE *pConnEle,
|
|
OUT tLOWERCONNECTION **ppLowerConn,
|
|
IN UCHAR Identifier
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles associating a Net Bios name with an open connection.
|
|
In order to coordinate with ZwClose(hSession) in CloseAddressesWithTransport/ntutil.c,
|
|
this routine should be called with NbtConfig.Resource exclusively locked.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS Locstatus;
|
|
BOOLEAN Attached=FALSE;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
HANDLE hSession;
|
|
ULONG Id = 0;
|
|
UCHAR *Id1 = (UCHAR *) &Id;
|
|
TCP_REQUEST_SET_INFORMATION_EX *pTcpSetInfo;
|
|
struct TCPSocketOption *pSockOption;
|
|
ULONG BufferLength;
|
|
|
|
if (ppLowerConn)
|
|
{
|
|
*ppLowerConn = NULL;
|
|
}
|
|
|
|
Id1[1] = 'L';
|
|
Id1[0] = Identifier;
|
|
|
|
if (!(pLowerConn = (tLOWERCONNECTION *) NbtAllocMem(sizeof(tLOWERCONNECTION), NBT_TAG2(Id))))
|
|
{
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
CHECK_PTR(pLowerConn);
|
|
CTEZeroMemory((PVOID)pLowerConn,sizeof(tLOWERCONNECTION));
|
|
CTEAttachFsp(&Attached, REF_FSP_CONN);
|
|
|
|
status = NbtTdiOpenConnection(pLowerConn,pDeviceContext);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint(("Nbt.NbtOpenAndAssocConnection: NbtTdiOpenConnection returned ERROR=%x\n", status));
|
|
CTEDetachFsp(Attached, REF_FSP_CONN);
|
|
CTEMemFree(pLowerConn);
|
|
|
|
return(status);
|
|
}
|
|
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CREATE);
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_ASSOC_CONNECTION);
|
|
|
|
if (pConnEle)
|
|
{
|
|
//
|
|
// Open an address object (aka port)
|
|
//
|
|
|
|
//
|
|
// until the correct state proc is set (i.e.Outbound), reject any data
|
|
// (in other words, don't let this field stay NULL!)
|
|
//
|
|
SetStateProc (pLowerConn, RejectAnyData);
|
|
|
|
status = NbtTdiOpenAddress (&pLowerConn->AddrFileHandle,
|
|
&pDeviceObject, // dummy argument, not used here
|
|
&pLowerConn->pAddrFileObject,
|
|
pDeviceContext,
|
|
(USHORT) 0, // any port
|
|
pDeviceContext->IpAddress,
|
|
TCP_FLAG);
|
|
|
|
hSession = pLowerConn->AddrFileHandle;
|
|
}
|
|
else
|
|
{
|
|
#ifndef VXD
|
|
hSession = pDeviceContext->hSession;
|
|
#else
|
|
hSession = (HANDLE) pDeviceContext->pSessionFileObject); // Address handle stored in pFileObjects
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* hSession could be NULL if the IP address is being released.
|
|
*/
|
|
if (hSession == NULL) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// associate with 139 or 445 session address
|
|
status = NbtTdiAssociateConnection (pLowerConn->pFileObject, hSession);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
ASSERT(pLowerConn->RefCount == 2);
|
|
|
|
//
|
|
// Disable nagling on this connection
|
|
//
|
|
if (!pDeviceContext->EnableNagling) {
|
|
NbtSetTcpInfo (pLowerConn->FileHandle, TCP_SOCKET_NODELAY, INFO_TYPE_CONNECTION, (ULONG)TRUE);
|
|
}
|
|
|
|
if (pConnEle)
|
|
{
|
|
pLowerConn->pUpperConnection = pConnEle;
|
|
ExInterlockedInsertTailList (&pDeviceContext->LowerConnection, // put on active connections Q
|
|
&pLowerConn->Linkage,
|
|
&pDeviceContext->LockInfo.SpinLock);
|
|
}
|
|
else
|
|
{
|
|
InterlockedIncrement (&pDeviceContext->NumFreeLowerConnections);
|
|
ExInterlockedInsertTailList (&pDeviceContext->LowerConnFreeHead, // put on free list
|
|
&pLowerConn->Linkage,
|
|
&pDeviceContext->LockInfo.SpinLock);
|
|
}
|
|
InterlockedIncrement (&pDeviceContext->TotalLowerConnections);
|
|
|
|
CTEDetachFsp(Attached, REF_FSP_CONN);
|
|
if (ppLowerConn)
|
|
{
|
|
*ppLowerConn = pLowerConn;
|
|
}
|
|
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_ASSOC_CONNECTION, FALSE);
|
|
return status;
|
|
}
|
|
|
|
|
|
KdPrint(("Nbt.NbtOpenAndAssocConnection: NbtTdiAssociateConnection returned ERROR=%x\n", status));
|
|
}
|
|
else
|
|
{
|
|
KdPrint(("Nbt.NbtOpenAddress: NbtTdiOpenConnection returned ERROR=%x\n", status));
|
|
}
|
|
|
|
/*
|
|
* NBT_DEREFERENCE_LOWERCONN will decrease the TotalLowerConnections
|
|
* Without the following InterlockedIncrement, we could under-count
|
|
* the actual # of Lower Connection.
|
|
*/
|
|
InterlockedIncrement (&pDeviceContext->TotalLowerConnections);
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_ASSOC_CONNECTION, FALSE);
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CREATE, FALSE);
|
|
|
|
CTEDetachFsp(Attached, REF_FSP_CONN);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtAssociateAddress(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN tCLIENTELE *pClientHandle,
|
|
IN PVOID pIrp
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles associating a Net Bios name with an open connection.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
tCONNECTELE *pConnEle;
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
CTELockHandle OldIrq3;
|
|
|
|
pConnEle = pRequest->Handle.ConnectionContext;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq3);
|
|
// Need code here to check if the address has been registered on the net
|
|
// yet and if not, then this could must wait till then , then to the
|
|
// associate *TODO*
|
|
|
|
CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status) // check connection for validity
|
|
CTEVerifyHandle(pClientHandle,NBT_VERIFY_CLIENT,tCLIENTELE,&status) // check client for validity now!
|
|
|
|
CTESpinLock(pClientHandle->pDeviceContext,OldIrq2);
|
|
CTESpinLock(pClientHandle,OldIrq);
|
|
CTESpinLock(pConnEle,OldIrq1);
|
|
|
|
if ((pConnEle->state != NBT_IDLE) ||
|
|
(!NBT_VERIFY_HANDLE (pConnEle, NBT_VERIFY_CONNECTION)) || // NBT_VERIFY_CONNECTION_DOWN if cleaned up
|
|
(!NBT_VERIFY_HANDLE (pClientHandle, NBT_VERIFY_CLIENT))) // NBT_VERIFY_CLIENT_DOWN if cleaned up
|
|
{
|
|
// the connection is in use, so reject the associate attempt
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTESpinFree(pClientHandle,OldIrq);
|
|
CTESpinFree(pClientHandle->pDeviceContext,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq3);
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
// link the connection to the client so we can find the client, given
|
|
// the connection.
|
|
pConnEle->pClientEle = (PVOID)pClientHandle;
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Associate: pConnEle %p pDeviceContext %p Client %p",
|
|
pConnEle, pConnEle->pDeviceContext, pConnEle->pClientEle));
|
|
|
|
// there can be multiple connections hooked to each client block - i.e.
|
|
// multiple connections per address per client. This allows the client
|
|
// to find its connections.
|
|
//
|
|
// first unlink from the device context UpconnectionsInUse, which was linked
|
|
// when the connection was created.
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InsertTailList(&pClientHandle->ConnectHead,&pConnEle->Linkage);
|
|
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTESpinFree(pClientHandle,OldIrq);
|
|
CTESpinFree(pClientHandle->pDeviceContext,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq3);
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtDisassociateAddress(
|
|
IN TDI_REQUEST *pRequest
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles disassociating a Net Bios name with an open connection.
|
|
The expectation is that the
|
|
client will follow with a NtClose which will do the work in Cleanup and
|
|
Close Connection. Since not all clients call this it is duplicate work
|
|
to put some code here to. The Rdr always calls NtClose after calling
|
|
this.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
tCONNECTELE *pConnEle;
|
|
tCLIENTELE *pClientEle;
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
TDI_REQUEST Request;
|
|
ULONG Flags;
|
|
LIST_ENTRY TempList;
|
|
PLIST_ENTRY pHead,pEntry;
|
|
tLISTENREQUESTS *pListen;
|
|
|
|
pConnEle = pRequest->Handle.ConnectionContext;
|
|
// check the connection element for validity
|
|
CHECK_PTR(pConnEle);
|
|
if (!NBT_VERIFY_HANDLE2(pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
|
|
{
|
|
ASSERT(0);
|
|
return (STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDisassociateAddress: State = %X\n",pConnEle->state));
|
|
|
|
Flags = TDI_DISCONNECT_RELEASE;
|
|
|
|
switch (pConnEle->state)
|
|
{
|
|
case NBT_CONNECTING:
|
|
case NBT_RECONNECTING:
|
|
case NBT_SESSION_OUTBOUND:
|
|
case NBT_SESSION_WAITACCEPT:
|
|
case NBT_SESSION_INBOUND:
|
|
// do abortive disconnects when the session is not up yet
|
|
// to be sure the disconnect completes the client's irp.
|
|
Flags = TDI_DISCONNECT_ABORT;
|
|
case NBT_SESSION_UP:
|
|
|
|
|
|
//
|
|
// Call NbtDisconnect incase the connection has not disconnected yet
|
|
//
|
|
Request.Handle.ConnectionContext = (PVOID)pConnEle;
|
|
status = NbtDisconnect(&Request, &DefaultDisconnectTimeout, Flags, NULL, NULL, NULL);
|
|
|
|
//
|
|
// NOTE: there is no BREAK here... the next case MUST be executed too.
|
|
//
|
|
case NBT_ASSOCIATED:
|
|
case NBT_DISCONNECTING:
|
|
case NBT_DISCONNECTED:
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
CHECK_PTR(pConnEle);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InitializeListHead(&pConnEle->Linkage);
|
|
SET_STATE_UPPER (pConnEle, NBT_IDLE);
|
|
pConnEle->DiscFlag = 0;
|
|
|
|
//
|
|
// remove the connection from the client and put back on the
|
|
// unassociated list
|
|
//
|
|
if (pClientEle = pConnEle->pClientEle)
|
|
{
|
|
pConnEle->pClientEle = NULL;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinLock(pClientEle,OldIrq1);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
InitializeListHead (&TempList);
|
|
pHead = &pClientEle->ListenHead;
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage);
|
|
pEntry = pEntry->Flink; // Don't reference freed memory
|
|
|
|
if (pListen->pConnectEle == pConnEle)
|
|
{
|
|
RemoveEntryList(&pListen->Linkage);
|
|
InsertTailList (&TempList, &pListen->Linkage);
|
|
}
|
|
}
|
|
|
|
pDeviceContext = pClientEle->pDeviceContext;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
|
|
//
|
|
// Ensure that the connection has not been cleaned up in this interval
|
|
// Bug# 237836
|
|
//
|
|
if (pConnEle->Verify == NBT_VERIFY_CONNECTION)
|
|
{
|
|
ExInterlockedInsertTailList(&pDeviceContext->UpConnectionInUse,
|
|
&pConnEle->Linkage,
|
|
&pDeviceContext->LockInfo.SpinLock);
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
while ((pEntry = TempList.Flink) != &TempList)
|
|
{
|
|
pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage);
|
|
RemoveEntryList(&pListen->Linkage);
|
|
CTEIoComplete (pListen->pIrp, STATUS_CANCELLED, 0);
|
|
CTEMemFree((PVOID)pListen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtCloseAddress(
|
|
IN TDI_REQUEST *pRequest,
|
|
OUT TDI_REQUEST_STATUS *pRequestStatus,
|
|
IN tDEVICECONTEXT *pContext,
|
|
IN PVOID pIrp)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine closes an address object for the client. Any connections
|
|
associated with the address object are immediately aborted and any requests
|
|
pending on the connection associated with the address object are
|
|
immediately completed with an appropriate error code. Any event handlers
|
|
that are registered are immediately deregistered and will not be called
|
|
after this request completes.
|
|
|
|
Note the the client actually passes a handle to the client object which is
|
|
chained off the address object. It is the client object that is closed,
|
|
which represents this clients attachment to the address object. Other
|
|
clients can continue to use the address object.
|
|
|
|
Arguments:
|
|
pRequest->Handle.AddressHandle - ptr to the ClientEle object.
|
|
pRequestStatus - return status for asynchronous completions.
|
|
pContext - the NBT device that this address is valid upon
|
|
pIrp - ptr to track for NT compatibility.
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
tCLIENTELE *pClientEle;
|
|
NTSTATUS status;
|
|
#ifndef VXD
|
|
UCHAR IrpFlags;
|
|
PIO_STACK_LOCATION pIrpsp;
|
|
#endif
|
|
|
|
CTEPagedCode();
|
|
|
|
pClientEle = (tCLIENTELE *)pRequest->Handle.ConnectionContext;
|
|
if (!pClientEle->pAddress)
|
|
{
|
|
// the address has already been deleted.
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.NbtCloseAddress: Close Address Hit %16.16s<%X> %X\n",
|
|
pClientEle->pAddress->pNameAddr->Name,
|
|
pClientEle->pAddress->pNameAddr->Name[15],pClientEle));
|
|
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("close address ClientEle=%p %!NBTNAME!<%02x>", pClientEle,
|
|
pClientEle->pAddress->pNameAddr->Name,
|
|
(unsigned)pClientEle->pAddress->pNameAddr->Name[15]));
|
|
|
|
#ifdef VXD
|
|
CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status);
|
|
|
|
//
|
|
// In NT-Land, closing connections is a two stage affair. However in
|
|
// the Vxd-Land, it is just a close, so call the other cleanup function
|
|
// here to do most of the work. In the NT implementation it is called
|
|
// from Ntisol.c, NTCleanupAddress.
|
|
//
|
|
pClientEle->pIrp = pIrp ;
|
|
status = NbtCleanUpAddress(pClientEle,pClientEle->pDeviceContext);
|
|
#else
|
|
// Note the special verifier that is set during the cleanup phase.
|
|
CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT_DOWN,tCLIENTELE,&status);
|
|
|
|
//
|
|
// clear the context value in the FileObject so that the client cannot
|
|
// pass this to us again
|
|
//
|
|
(VOID)NTClearFileObjectContext(pIrp);
|
|
pClientEle->pIrp = pIrp;
|
|
|
|
pIrpsp = IoGetCurrentIrpStackLocation(((PIRP)pIrp));
|
|
|
|
IrpFlags = pIrpsp->Control;
|
|
IoMarkIrpPending(((PIRP)pIrp));
|
|
|
|
#endif
|
|
|
|
NBT_DEREFERENCE_CLIENT(pClientEle);
|
|
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtCleanUpAddress(
|
|
IN tCLIENTELE *pClientEle,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles the first stage of releasing an address object.
|
|
|
|
Arguments:
|
|
|
|
pIrp - a ptr to an IRP
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tCONNECTELE *pConnEle;
|
|
tCONNECTELE *pConnEleToDeref = NULL;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
CTELockHandle OldIrq3;
|
|
PLIST_ENTRY pHead,pEntry;
|
|
PLIST_ENTRY pEntryConn;
|
|
tADDRESSELE *pAddress;
|
|
DWORD i;
|
|
LIST_ENTRY TempList;
|
|
|
|
// to prevent connections and datagram from the wire...remove from the
|
|
// list of clients hooked to the address element
|
|
//
|
|
pAddress = pClientEle->pAddress;
|
|
if (!pAddress)
|
|
{
|
|
// the address has already been deleted.
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
// lock the address to coordinate with receiving datagrams - to avoid
|
|
// allowing the client to free datagram receive buffers in the middle
|
|
// of DgramHndlrNotOs finding a buffer
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (!IsListEmpty(&pClientEle->RcvDgramHead))
|
|
{
|
|
PLIST_ENTRY pHead;
|
|
PLIST_ENTRY pEntry;
|
|
tRCVELE *pRcvEle;
|
|
PCTE_IRP pRcvIrp;
|
|
|
|
pHead = &pClientEle->RcvDgramHead;
|
|
pEntry = pHead->Flink;
|
|
|
|
// prevent any datagram from the wire seeing this list
|
|
//
|
|
InitializeListHead(&pClientEle->RcvDgramHead);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
while (pEntry != pHead)
|
|
{
|
|
pRcvEle = CONTAINING_RECORD(pEntry,tRCVELE,Linkage);
|
|
pRcvIrp = pRcvEle->pIrp;
|
|
|
|
CTEIoComplete(pRcvIrp,STATUS_NETWORK_NAME_DELETED,0);
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
CTEMemFree(pRcvEle);
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
// lock the client and the device context till we're done
|
|
CTESpinLock(pClientEle,OldIrq2);
|
|
|
|
#ifndef VXD
|
|
//
|
|
// set to prevent reception of datagrams
|
|
// (Vxd doesn't use this handler)
|
|
//
|
|
pClientEle->evRcvDgram = TdiDefaultRcvDatagramHandler;
|
|
#endif
|
|
|
|
// so no one else can access the client element, set state to down. Therefore
|
|
// the verify checks will fail anywhere the client is accessed in the code,
|
|
// except in the NbtCloseAddress code which checks for this verifier value.
|
|
//
|
|
pClientEle->Verify = NBT_VERIFY_CLIENT_DOWN;
|
|
|
|
//
|
|
// Disassociate all Connections from this address object, first starting
|
|
// with any active connections, then followup with any idle connections.
|
|
//
|
|
pDeviceContext = pClientEle->pDeviceContext;
|
|
while ( !IsListEmpty( &pClientEle->ConnectActive ))
|
|
{
|
|
pEntry = RemoveHeadList( &pClientEle->ConnectActive ) ;
|
|
InitializeListHead(pEntry);
|
|
|
|
pConnEle = CONTAINING_RECORD( pEntry, tCONNECTELE, Linkage ) ;
|
|
CTESpinLock(pConnEle,OldIrq3);
|
|
NBT_REFERENCE_CONNECTION(pConnEle, REF_CONN_CLEANUP_ADDR); // Ensure conn stays around releasing lock below
|
|
CTESpinFree(pConnEle,OldIrq3);
|
|
|
|
CTESpinFree(pClientEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// if we had a connection in partial rcv state, make sure to remove it from
|
|
// the list
|
|
//
|
|
#ifdef VXD
|
|
pLowerConn = pConnEle->pLowerConnId;
|
|
|
|
if ( pLowerConn->StateRcv == PARTIAL_RCV &&
|
|
(pLowerConn->fOnPartialRcvList == TRUE) )
|
|
{
|
|
RemoveEntryList( &pLowerConn->PartialRcvList ) ;
|
|
pLowerConn->fOnPartialRcvList = FALSE;
|
|
InitializeListHead(&pLowerConn->PartialRcvList);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Deref any connections referenced earlier if necessary
|
|
//
|
|
if (pConnEleToDeref)
|
|
{
|
|
NBT_DEREFERENCE_CONNECTION(pConnEleToDeref, REF_CONN_CLEANUP_ADDR);
|
|
}
|
|
pConnEleToDeref = pConnEle;
|
|
|
|
status = NbtCleanUpConnection(pConnEle,pDeviceContext);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pClientEle,OldIrq2);
|
|
CTESpinLock(pConnEle,OldIrq3);
|
|
//
|
|
// remove from this list again incase SessionSetupContinue has put it
|
|
// back on the list - if no one has put it back on this list this
|
|
// call is a no op since we initialized the list head above
|
|
//
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InitializeListHead (&pConnEle->Linkage);
|
|
CHECK_PTR(pConnEle);
|
|
SET_STATE_UPPER (pConnEle, NBT_IDLE);
|
|
pConnEle->pClientEle = NULL;
|
|
|
|
CTESpinFree(pConnEle,OldIrq3);
|
|
CTESpinFree(pClientEle,OldIrq2);
|
|
PUSH_LOCATION(0x80);
|
|
|
|
//
|
|
// put on the idle connection list, to wait for a close connection
|
|
// to come down.
|
|
// Bug # 405699
|
|
// Do this only if the NTCleanupConnection did not run in the interim.
|
|
//
|
|
ASSERT(pConnEle->RefCount >= 1);
|
|
if (!pConnEle->ConnectionCleanedUp)
|
|
{
|
|
ExInterlockedInsertTailList (&pDeviceContext->UpConnectionInUse,
|
|
&pConnEle->Linkage,
|
|
&pDeviceContext->LockInfo.SpinLock);
|
|
}
|
|
|
|
|
|
CTESpinLock(pClientEle,OldIrq2);
|
|
}
|
|
|
|
CTESpinFree(pClientEle,OldIrq2);
|
|
CTESpinLock(pDeviceContext,OldIrq1);
|
|
CTESpinLock(pClientEle,OldIrq2);
|
|
// We are now holding the JointLock + DeviceLock + ClientLock
|
|
|
|
//
|
|
// each idle connection creates a lower connection to the transport for
|
|
// inbound calls, therefore close a transport connection for each
|
|
// connection in this list and then "dissassociate" the connection from
|
|
// the address.
|
|
//
|
|
// make the list look empty so no connections will be serviced inbound
|
|
// from the wire
|
|
//
|
|
while (!IsListEmpty(&pClientEle->ConnectHead))
|
|
{
|
|
pEntry = pClientEle->ConnectHead.Flink;
|
|
RemoveEntryList (pEntry);
|
|
pConnEle = CONTAINING_RECORD(pEntry,tCONNECTELE,Linkage);
|
|
CHECK_PTR(pConnEle);
|
|
ASSERT ((pConnEle->Verify==NBT_VERIFY_CONNECTION) || (pConnEle->Verify==NBT_VERIFY_CONNECTION_DOWN));
|
|
|
|
CTESpinLock(pConnEle,OldIrq3);
|
|
|
|
//
|
|
// The Connection Element could be currently being cleaned up in NbtCleanUpConnection, so verify
|
|
//
|
|
if (pConnEle->Verify != NBT_VERIFY_CONNECTION)
|
|
{
|
|
InitializeListHead (&pConnEle->Linkage);
|
|
CTESpinFree(pConnEle,OldIrq3);
|
|
continue;
|
|
}
|
|
|
|
InsertTailList(&pDeviceContext->UpConnectionInUse,&pConnEle->Linkage);
|
|
|
|
//
|
|
// Cannot enable the following ASSERT. When NetBT deregister the address,
|
|
// RDR will immediately close all its connections. However, the connections
|
|
// could be residing in the system work item list, which can have a
|
|
// refcount 4 (there could be more, but we haven't seen).
|
|
//
|
|
// ASSERT(pConnEle->RefCount == 1 || pConnEle->RefCount == 2);
|
|
//
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_IDLE);
|
|
pConnEle->Verify = NBT_VERIFY_CONNECTION_DOWN;
|
|
pConnEle->pClientEle = NULL;
|
|
|
|
CTESpinFree(pConnEle,OldIrq3);
|
|
|
|
//
|
|
// Get a free connection to the transport and close it
|
|
// for each free connection on this list. It is possible that this
|
|
// free list could be empty if an inbound connection was occurring
|
|
// right at this moment. In which case we would leave an extra connection
|
|
// object to the transport lying around... not a problem.
|
|
if (!IsListEmpty(&pDeviceContext->LowerConnFreeHead))
|
|
{
|
|
pEntryConn = RemoveHeadList(&pDeviceContext->LowerConnFreeHead);
|
|
pLowerConn = CONTAINING_RECORD(pEntryConn,tLOWERCONNECTION,Linkage);
|
|
InterlockedDecrement (&pDeviceContext->NumFreeLowerConnections);
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.NbtCleanUpAddress: Closing Handle %p->%X\n",pLowerConn,pLowerConn->FileHandle));
|
|
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CREATE, TRUE);
|
|
}
|
|
}
|
|
|
|
// check for any datagrams still outstanding. These could be waiting on
|
|
// name queries to complete, so there could be timers associated with them
|
|
//
|
|
// Complete any outstanding listens not on an active connection
|
|
//
|
|
//
|
|
// make the list look empty so no connections will be serviced inbound
|
|
// from the wire
|
|
//
|
|
//
|
|
// Move all of the Listen requests onto a temporary list
|
|
//
|
|
InitializeListHead (&TempList);
|
|
while (!IsListEmpty(&pClientEle->ListenHead))
|
|
{
|
|
pEntry = pClientEle->ListenHead.Flink;
|
|
|
|
RemoveEntryList (pEntry);
|
|
InsertTailList (&TempList, pEntry);
|
|
}
|
|
|
|
CTESpinFree(pClientEle, OldIrq2);
|
|
CTESpinFree(pDeviceContext,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
while ((pEntry = TempList.Flink) != &TempList)
|
|
{
|
|
tLISTENREQUESTS * pListen ;
|
|
|
|
pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage);
|
|
RemoveEntryList(&pListen->Linkage);
|
|
|
|
CTEIoComplete( pListen->pIrp, STATUS_NETWORK_NAME_DELETED, 0);
|
|
CTEMemFree( pListen );
|
|
}
|
|
|
|
//
|
|
// Deref any connections referenced earlier if necessary
|
|
//
|
|
if (pConnEleToDeref)
|
|
{
|
|
NBT_DEREFERENCE_CONNECTION(pConnEleToDeref, REF_CONN_CLEANUP_ADDR);
|
|
}
|
|
|
|
#ifdef VXD
|
|
//
|
|
// Complete any outstanding ReceiveAnys on this client element
|
|
//
|
|
DbgPrint("NbtCleanupAddress: Completing all RcvAny NCBs\r\n") ;
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pClientEle,OldIrq2);
|
|
|
|
pHead = &pClientEle->RcvAnyHead;
|
|
pEntry = pHead->Flink;
|
|
//
|
|
// make the list look empty so no connections will be serviced inbound
|
|
// from the wire
|
|
//
|
|
InitializeListHead(pHead);
|
|
|
|
CTESpinFree(pClientEle, OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
while (pEntry != pHead )
|
|
{
|
|
PRCV_CONTEXT pRcvContext ;
|
|
|
|
pRcvContext = CONTAINING_RECORD(pEntry,RCV_CONTEXT,ListEntry);
|
|
pEntry = pEntry->Flink;
|
|
|
|
CTEIoComplete( pRcvContext->pNCB, STATUS_NETWORK_NAME_DELETED, TRUE );
|
|
|
|
FreeRcvContext( pRcvContext );
|
|
}
|
|
#endif
|
|
|
|
// *TODO the code above only removes names that are being resolved, and
|
|
// leaves any datagram sends that are currently active with the
|
|
// transport... these should be cancelled too by cancelling the irp..
|
|
// Put this code in when the Irp cancelling code is done.
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtCloseConnection(
|
|
IN TDI_REQUEST *pRequest,
|
|
OUT TDI_REQUEST_STATUS *pRequestStatus,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PVOID pIrp)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine closes a connection object for the client. Closing is
|
|
different than disconnecting. A disconnect breaks a connection with a
|
|
peer whereas the close removes this connection endpoint from the local
|
|
NBT only. NtClose causes NTCleanup to be called first which does the
|
|
session close. This routine then does frees memory associated with the
|
|
connection elements.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
tCONNECTELE *pConnEle;
|
|
NTSTATUS status;
|
|
|
|
CTEPagedCode();
|
|
|
|
pConnEle = pRequest->Handle.ConnectionContext;
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.NbtCloseConnection: Hit!! state = %X pConnEle %X\n",pConnEle->state,pConnEle));
|
|
|
|
#ifndef VXD
|
|
CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION_DOWN,tCONNECTELE,&status);
|
|
IoMarkIrpPending((PIRP)pIrp); // Bug 261575: to make driver verifier happy
|
|
#else
|
|
CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status);
|
|
//
|
|
// Call the Cleanup function, which NT calls from ntisol, NtCleanupConnection
|
|
//
|
|
status = NbtCleanUpConnection(pConnEle,pDeviceContext );
|
|
#endif
|
|
|
|
// NOTE:
|
|
// the NBtDereference routine will complete the irp and return pending
|
|
//
|
|
NbtTrace(NBT_TRACE_DISCONNECT, ("Close connection Irp=%p Upper=%p Lower=%p Client=%p Device=%p",
|
|
pIrp, pConnEle, pConnEle->pLowerConnId, pConnEle->pClientEle, pConnEle->pDeviceContext));
|
|
|
|
pConnEle->pIrpClose = pIrp;
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CREATE);
|
|
|
|
return (STATUS_PENDING);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtCleanUpConnection(
|
|
IN tCONNECTELE *pConnEle,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles running down a connection in preparation for a close
|
|
that will come in next. NtClose hits this entry first, and then it hits
|
|
the NTCloseConnection next. If the connection was outbound, then the
|
|
address object must be closed as well as the connection. This routine
|
|
mainly deals with the pLowerconn connection to the transport whereas
|
|
NbtCloseConnection deals with closing pConnEle, the connection to the client.
|
|
|
|
If DisassociateConnection is called by the client then it will do most of
|
|
this cleanup.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
NTSTATUS Locstatus;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
PLIST_ENTRY pEntry;
|
|
BOOLEAN Originator = TRUE;
|
|
ULONG LowerState = NBT_IDLE;
|
|
TDI_REQUEST Request;
|
|
tLISTENREQUESTS *pListen;
|
|
tCLIENTELE *pClientEle;
|
|
PLIST_ENTRY pHead;
|
|
LIST_ENTRY TempList;
|
|
BOOLEAN QueueCleanupBool=FALSE;
|
|
BOOLEAN DoDisconnect=TRUE;
|
|
BOOLEAN FreeLower;
|
|
|
|
NbtTrace(NBT_TRACE_DISCONNECT, ("Cleanup connection Upper=%p Lower=%p Client=%p Device=%p",
|
|
pConnEle, pConnEle->pLowerConnId, pConnEle->pClientEle, pConnEle->pDeviceContext));
|
|
//
|
|
// save the lower connection origination flag for later
|
|
//
|
|
pLowerConn = pConnEle->pLowerConnId;
|
|
if (pLowerConn)
|
|
{
|
|
Originator = pLowerConn->bOriginator;
|
|
}
|
|
|
|
// the connection has not been associated so there is no further work to
|
|
// do here.
|
|
//
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
//
|
|
// If the state is NBT_IDLE, the connection has already been disassociated,
|
|
// and the next action will be a close, so change the verifier to allow
|
|
// the close to complete
|
|
//
|
|
if (pConnEle->state != NBT_IDLE)
|
|
{
|
|
BOOLEAN DoCleanup = FALSE;
|
|
|
|
CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status);
|
|
|
|
|
|
//
|
|
// check if there is an outstanding name query going on and if so
|
|
// then cancel the timer and call the completion routine.
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq2);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
if ((pConnEle->state == NBT_CONNECTING) ||
|
|
(pConnEle->state == NBT_RECONNECTING))
|
|
{
|
|
status = CleanupConnectingState(pConnEle,pDeviceContext,&OldIrq,&OldIrq2);
|
|
//
|
|
// Pending means that the connection is currently being setup
|
|
// by TCP, so do a disconnect, below.
|
|
//
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
//
|
|
// Since the connection is not setup with the transport yet
|
|
// there is no need to call nbtdisconnect
|
|
//
|
|
DoDisconnect = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// all other states of the connection are handled by NbtDisconnect
|
|
// which will send a disconnect down the to transport and then
|
|
// cleanup things.
|
|
//
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
Request.Handle.ConnectionContext = (PVOID)pConnEle;
|
|
|
|
if (DoDisconnect)
|
|
{
|
|
NbtTrace(NBT_TRACE_DISCONNECT, ("Abort connection ==> ConnEle %p", pConnEle));
|
|
status = NbtDisconnect(
|
|
&Request,
|
|
&DefaultDisconnectTimeout,
|
|
TDI_DISCONNECT_ABORT,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
NbtTrace(NBT_TRACE_DISCONNECT, ("NbtDisconnect returns %!status!", status));
|
|
ASSERT (STATUS_PENDING != status);
|
|
}
|
|
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
|
|
// we don't want to return Invalid connection if we disconnect an
|
|
// already disconnected connection.
|
|
if (status == STATUS_CONNECTION_INVALID)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
//
|
|
// if the verify value is already set to connection down then we have
|
|
// been through here already and do not want to free a lower connection.
|
|
// i.e. when the client calls close address then calls close connection.
|
|
//
|
|
if (pConnEle->Verify == NBT_VERIFY_CONNECTION)
|
|
{
|
|
FreeLower = TRUE;
|
|
}
|
|
else
|
|
{
|
|
FreeLower = FALSE;
|
|
}
|
|
|
|
pConnEle->Verify = NBT_VERIFY_CONNECTION_DOWN;
|
|
|
|
//
|
|
// Free any posted Rcv buffers that have not been filled
|
|
//
|
|
|
|
FreeRcvBuffers(pConnEle,&OldIrq);
|
|
|
|
// check if any listens have been setup for this connection, and
|
|
// remove them if so
|
|
//
|
|
pClientEle = pConnEle->pClientEle;
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pDeviceContext,OldIrq1);
|
|
|
|
InitializeListHead (&TempList);
|
|
if (pClientEle)
|
|
{
|
|
CTESpinLock(pClientEle,OldIrq2);
|
|
|
|
pHead = &pClientEle->ListenHead;
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage);
|
|
pEntry = pEntry->Flink; // Don't reference freed memory
|
|
|
|
if (pListen->pConnectEle == pConnEle)
|
|
{
|
|
RemoveEntryList(&pListen->Linkage);
|
|
InsertTailList(&TempList, &pListen->Linkage);
|
|
}
|
|
}
|
|
CTESpinFree(pClientEle,OldIrq2);
|
|
}
|
|
|
|
CTESpinLock(pConnEle,OldIrq2);
|
|
|
|
//
|
|
// Unlink the connection element from the client's list or the device context
|
|
// if its not associated yet.
|
|
//
|
|
CHECK_PTR(pConnEle);
|
|
if (pConnEle->state > NBT_IDLE)
|
|
{
|
|
// do the disassociate here
|
|
//
|
|
SET_STATE_UPPER (pConnEle, NBT_IDLE);
|
|
pConnEle->pClientEle = NULL;
|
|
}
|
|
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InitializeListHead(&pConnEle->Linkage);
|
|
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(pDeviceContext,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
while ((pEntry = TempList.Flink) != &TempList)
|
|
{
|
|
pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage);
|
|
|
|
RemoveEntryList(&pListen->Linkage);
|
|
CTEIoComplete (pListen->pIrp, STATUS_CANCELLED, 0);
|
|
CTEMemFree (pListen);
|
|
}
|
|
|
|
// this could be status pending from NbtDisconnect...
|
|
//
|
|
return(status);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
FreeRcvBuffers(
|
|
tCONNECTELE *pConnEle,
|
|
CTELockHandle *pOldIrq
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles freeing any recv buffers posted by the client.
|
|
The pConnEle lock could be held prior to calling this routine.
|
|
|
|
Arguments:
|
|
|
|
pListHead
|
|
pTracker
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PLIST_ENTRY pHead;
|
|
|
|
pHead = &pConnEle->RcvHead;
|
|
while (!IsListEmpty(pHead))
|
|
{
|
|
PLIST_ENTRY pRcvEntry;
|
|
PVOID pRcvElement ;
|
|
|
|
KdPrint(("Nbt.FreeRcvBuffers: ***Freeing Posted Rcvs on Connection Cleanup!\n"));
|
|
pRcvEntry = RemoveHeadList(pHead);
|
|
CTESpinFree(pConnEle,*pOldIrq);
|
|
|
|
#ifndef VXD
|
|
pRcvElement = CONTAINING_RECORD(pRcvEntry,IRP,Tail.Overlay.ListEntry);
|
|
CTEIoComplete( (PIRP) pRcvElement, STATUS_CANCELLED,0);
|
|
#else
|
|
pRcvElement = CONTAINING_RECORD(pRcvEntry, RCV_CONTEXT, ListEntry ) ;
|
|
CTEIoComplete( ((PRCV_CONTEXT)pRcvEntry)->pNCB, STATUS_CANCELLED, 0);
|
|
#endif
|
|
|
|
CTESpinLock(pConnEle,*pOldIrq);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
FindPendingRequest(
|
|
IN tLMHSVC_REQUESTS *pLmHRequests,
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
OUT NBT_WORK_ITEM_CONTEXT **pContextRet
|
|
)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
NBT_WORK_ITEM_CONTEXT *pWiContext = NULL;
|
|
|
|
pWiContext = (NBT_WORK_ITEM_CONTEXT *) pLmHRequests->Context;
|
|
if (pWiContext && (pWiContext->pTracker == pTracker))
|
|
{
|
|
pLmHRequests->Context = NULL;
|
|
NTClearContextCancel (pWiContext);
|
|
*pContextRet = pWiContext;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// check the list for this tracker
|
|
//
|
|
pEntry = pLmHRequests->ToResolve.Flink;
|
|
while (pEntry != &pLmHRequests->ToResolve)
|
|
{
|
|
pWiContext = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Linkage);
|
|
pEntry = pEntry->Flink;
|
|
|
|
if (pTracker == pWiContext->pTracker)
|
|
{
|
|
RemoveEntryList(pEntry);
|
|
*pContextRet = pWiContext;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
CleanupConnectingState(
|
|
IN tCONNECTELE *pConnEle,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN CTELockHandle *OldIrq, // pConnEle lock
|
|
IN CTELockHandle *OldIrq2 // joint lock
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles running down a connection in the NBT_CONNECTING
|
|
state since that connection could be doing a number of things such as:
|
|
1) Broadcast or WINS name Query
|
|
2) LmHosts name query
|
|
3) DNS name query
|
|
4) Tcp Connection setup
|
|
|
|
The JointLock and the pConnEle lock are held when calling this routine.
|
|
|
|
Arguments:
|
|
|
|
pConnEle - ptr to the connection
|
|
pDeviceContext - the device context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
tDGRAM_SEND_TRACKING *pTrackerName = NULL;
|
|
tDGRAM_SEND_TRACKING *pTrackerConnect = NULL;
|
|
tNAMEADDR *pNameAddr = NULL;
|
|
NBT_WORK_ITEM_CONTEXT *pWiContext = NULL;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
COMPLETIONCLIENT pClientCompletion;
|
|
PVOID Context;
|
|
NTSTATUS Locstatus;
|
|
|
|
//
|
|
// save the lower connection origination flag for later
|
|
//
|
|
pLowerConn = pConnEle->pLowerConnId;
|
|
pTrackerConnect = (tDGRAM_SEND_TRACKING *) pConnEle->pIrpRcv;
|
|
//CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&Locstatus);
|
|
|
|
if (pConnEle->state == NBT_CONNECTING)
|
|
{
|
|
if ((pLowerConn) && // The LowerConnection could have gone away if it was deleted
|
|
(pLowerConn->State == NBT_CONNECTING))
|
|
{
|
|
LOCATION(0x6E)
|
|
//
|
|
// We are setting up the TCP connection to the transport Now
|
|
// so it is safe to call NbtDisconnect on this connection and
|
|
// let that cleanup the mess - use this retcode to signify that.
|
|
//
|
|
return(STATUS_PENDING);
|
|
|
|
}
|
|
|
|
//
|
|
// check if the name query is held up in doing a LmHost or DNS
|
|
// Name Query
|
|
//
|
|
|
|
// check if there is an outstanding name query going on and if so
|
|
// then cancel the timer and call the completion routine.
|
|
//
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.CleanupConnectingState: Cleanup in the Connecting State %X\n",pConnEle));
|
|
|
|
pTrackerName = pTrackerConnect->pTrackerWorker; // QueryNameOnNet tracker
|
|
if (NBT_VERIFY_HANDLE (pTrackerName, NBT_VERIFY_TRACKER) && pTrackerConnect->pDestName)
|
|
{
|
|
status = FindInHashTable(NbtConfig.pRemoteHashTbl,
|
|
pTrackerConnect->pDestName,
|
|
NbtConfig.pScope,
|
|
&pNameAddr);
|
|
|
|
//
|
|
// if there is a timer, then the connection setup is still
|
|
// waiting on the name query. If no timer, then we could be
|
|
// waiting on an LmHosts or DNS name query or we
|
|
// are waiting on the TCP connection setup - stopping the timer
|
|
// should cleanup the tracker.
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
tTIMERQENTRY *pTimer;
|
|
|
|
CHECK_PTR(pNameAddr);
|
|
if (pNameAddr->NameTypeState & STATE_RESOLVED)
|
|
{
|
|
//
|
|
// the name has resolved, but not started setting up the
|
|
// session yet, so return this status to tell the caller
|
|
// to cancel the tracker.
|
|
//
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
else if (pTimer = pNameAddr->pTimer)
|
|
{
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.CleanupConnectingState: Cleanup During NameQuery: pConnEle=%X\n",
|
|
pConnEle));
|
|
|
|
pNameAddr->pTimer = NULL;
|
|
status = StopTimer(pTimer,&pClientCompletion,&Context);
|
|
|
|
#ifdef DEAD_CODE
|
|
//
|
|
// remove the name from the hash table, since it did not resolve
|
|
//
|
|
pNameAddr->NameTypeState &= ~STATE_RESOLVING;
|
|
pNameAddr->NameTypeState |= STATE_RELEASED;
|
|
pNameAddr->pTracker = NULL;
|
|
if (pClientCompletion)
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, TRUE);
|
|
}
|
|
#endif // DEAD_CODE
|
|
|
|
pTrackerName = NULL; // since StopTimer should have cleaned up the tracker, null it out
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// check if the name is waiting on an LmHost name Query
|
|
// or a DNS name query
|
|
//
|
|
status = FindPendingRequest (&LmHostQueries, pTrackerName, &pWiContext);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
#ifndef VXD
|
|
status = FindPendingRequest (&DnsQueries, pTrackerName, &pWiContext);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
status = FindPendingRequest (&CheckAddr, pTrackerName, &pWiContext);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.CleanupConnectingState: Found pending NameQuery for pConnEle %X\n",
|
|
pConnEle));
|
|
}
|
|
}
|
|
}
|
|
// ...else....
|
|
// the completion routine has already run, so we are
|
|
// in the state of starting a Tcp Connection, so
|
|
// let nbtdisconnect handle it. (below).
|
|
//
|
|
}
|
|
} // connnecting state
|
|
else if (pConnEle->state == NBT_RECONNECTING)
|
|
{
|
|
LOCATION(0x77);
|
|
//
|
|
// this should signal NbtConnect not to do the reconnect
|
|
//
|
|
pTrackerConnect->pTrackerWorker->Flags = TRACKER_CANCELLED;
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// for items on the LmHost or Dns queues, get the completion routine
|
|
// out of the Work Item context first
|
|
//
|
|
if (pWiContext)
|
|
{
|
|
LOCATION(0x78);
|
|
pClientCompletion = pWiContext->ClientCompletion;
|
|
Context = pWiContext->pClientContext;
|
|
|
|
// for DNS and LmHosts, the tracker needs to be freed and the name
|
|
// removed from the hash table
|
|
//
|
|
if (pTrackerName)
|
|
{
|
|
LOCATION(0x79);
|
|
CTESpinFree(pConnEle,*OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,*OldIrq2);
|
|
//
|
|
// remove the name from the hash table, since it did not resolve
|
|
//
|
|
SetNameState (pTrackerName->pNameAddr, NULL, FALSE);
|
|
NBT_DEREFERENCE_TRACKER(pTrackerName, FALSE);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,*OldIrq2);
|
|
CTESpinLock(pConnEle,*OldIrq);
|
|
}
|
|
|
|
CTEMemFree(pWiContext);
|
|
}
|
|
|
|
if (pClientCompletion)
|
|
{
|
|
LOCATION(0x7A);
|
|
CTESpinFree(pConnEle,*OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,*OldIrq2);
|
|
|
|
//
|
|
// The completion routine is SessionSetupContinue
|
|
// and it will cleanup the lower connection and
|
|
// return the client's irp
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
CompleteClientReq(pClientCompletion, Context,STATUS_CANCELLED);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,*OldIrq2);
|
|
CTESpinLock(pConnEle,*OldIrq);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CheckConnect(
|
|
IN tCONNECTELE *pConnEle,
|
|
IN tCLIENTELE *pClientEle,
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
This function should be called with the following locks held.
|
|
NbtConfig.Resource
|
|
NbtConfig.JointLock SpinLock
|
|
pClientEle SpinLock
|
|
pConnEle SpinLock
|
|
--*/
|
|
{
|
|
/*
|
|
* The state can be NBT_DISCONNECTING if this ConnectionElement
|
|
* is being reused to setup a connection to a different Endpoint
|
|
*/
|
|
if ((pConnEle->state != NBT_ASSOCIATED) &&
|
|
(pConnEle->state != NBT_DISCONNECTING) &&
|
|
(pConnEle->state != NBT_DISCONNECTED)) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
if (pClientEle->Verify != NBT_VERIFY_CLIENT ) {
|
|
return (pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN)? STATUS_CANCELLED: STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
/*
|
|
* be sure the name is in the correct state for a connection
|
|
*/
|
|
if ((!IsDeviceNetbiosless(pDeviceContext)) &&
|
|
(pClientEle->pAddress->pNameAddr->NameTypeState & STATE_CONFLICT)) {
|
|
return STATUS_DUPLICATE_NAME;
|
|
}
|
|
|
|
/*
|
|
* this code handles the case when DHCP has not assigned an IP address yet
|
|
*/
|
|
if (pDeviceContext->IpAddress == 0) {
|
|
return STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
|
|
/*
|
|
//
|
|
// this code handles the case when DHCP has not assigned an IP address yet
|
|
//
|
|
ASSERT (pDeviceContext->IpAddress == 0 || !pDeviceContext->pSessionFileObject);
|
|
if (pDeviceContext->IpAddress == 0 || !pDeviceContext->pSessionFileObject) {
|
|
return STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
*/
|
|
}
|
|
|
|
NTSTATUS
|
|
NbtReConnect(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN tIPADDRESS DestIp
|
|
)
|
|
{
|
|
tCONNECTELE *pConnEle;
|
|
tCLIENTELE *pClientEle;
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
tIPADDRESS IpAddress;
|
|
tNAMEADDR *pNameAddr;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
#ifdef _PNP_POWER_
|
|
if (NbtConfig.Unloading) {
|
|
KdPrint (("Nbt.NbtReConnect: --> ERROR New Connect request while Unloading!!!\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
#endif // _PNP_POWER_
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Reconnect to %!ipaddr! for pTracker %p", DestIp, pTracker));
|
|
|
|
/*
|
|
* Only NETBIOS name can hit requery or retarget
|
|
*/
|
|
ASSERT(pTracker->RemoteNameLength <= NETBIOS_NAME_SIZE);
|
|
pConnEle = pTracker->pConnEle;
|
|
|
|
//
|
|
// Acquire this resource to co-ordinate with DHCP changing the IP
|
|
// address
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
if ((!(NBT_VERIFY_HANDLE (pConnEle, NBT_VERIFY_CONNECTION))) || (!(pClientEle = pConnEle->pClientEle))) {
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
CTESpinLock(pClientEle,OldIrq1);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
pDeviceContext = pClientEle->pDeviceContext;
|
|
ASSERT(!IsDeviceNetbiosless(pDeviceContext)); // NetbiosLess device cannot hit reconnect or retarget case
|
|
|
|
status = CheckConnect(pConnEle, pClientEle, pDeviceContext);
|
|
if (status != STATUS_SUCCESS) {
|
|
pConnEle->pIrp = NULL;
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("CheckConnect returns %!status! for pTracker %p", status, pTracker));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// check if the Reconnect got cancelled
|
|
//
|
|
pTracker->pTrackerWorker = NULL;
|
|
if (pTracker->Flags & TRACKER_CANCELLED) {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("Connection Request is cancelled for pTracker %p", pTracker));
|
|
|
|
//
|
|
// if SessionSetupContinue has run, it has set the refcount to zero
|
|
//
|
|
if (pTracker->RefConn == 0) {
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
} else {
|
|
pTracker->RefConn--;
|
|
}
|
|
pConnEle->pIrp = NULL;
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return STATUS_CANCELLED;
|
|
}
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_CONNECTING);
|
|
|
|
// Increment the ref count so that a cleanup cannot remove
|
|
// the pConnEle till the session is setup - one of these is removed when
|
|
// the session is setup and the other is removed when it is disconnected.
|
|
//
|
|
NBT_REFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
NBT_REFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
ASSERT(pConnEle->RefCount >= 3);
|
|
//
|
|
// unlink the connection from the idle connection list and put on active list
|
|
//
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InsertTailList(&pClientEle->ConnectActive,&pConnEle->Linkage);
|
|
|
|
// this field is used to hold a disconnect irp if it comes down during
|
|
// NBT_CONNECTING or NBT_SESSION_OUTBOUND states
|
|
//
|
|
pConnEle->pIrpDisc = NULL;
|
|
|
|
// if null then this is being called to reconnect and the tracker is already
|
|
// setup.
|
|
//
|
|
// for the reconnect case we must skip most of the processing since
|
|
// the tracker is all set up already. All we need to do is
|
|
// retry the connection.
|
|
pTracker->RefConn++;
|
|
pTracker->SendBuffer.pBuffer = pTracker->pRemoteName;
|
|
|
|
// store the tracker in the Irp Rcv ptr so it can be used by the
|
|
// session setup code in hndlrs.c in the event the destination is
|
|
// between posting listens and this code should re-attempt the
|
|
// session setup. The code in hndlrs.c returns the tracker to its
|
|
// free list and frees the session hdr memory too.
|
|
//
|
|
// We need to set this while holding the ConnEle lock because the client
|
|
// can call NbtDisconnect while we are opening a Tcp handle and try to
|
|
// set the Tracker's flag to TRACKER_CANCELLED
|
|
//
|
|
pConnEle->pIrpRcv = (PIRP)pTracker;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
// open a connection with the transport for this session
|
|
status = NbtOpenAndAssocConnection (pDeviceContext, pConnEle, &pConnEle->pLowerConnId, '3');
|
|
if (!NT_SUCCESS(status)) {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("NbtOpenAndAssocConnection return %!status! for pTracker %p",
|
|
status, pTracker));
|
|
goto NbtConnect_Check;
|
|
}
|
|
|
|
// We need to track that this side originated the call so we discard this
|
|
// connection at the end
|
|
//
|
|
pConnEle->pLowerConnId->bOriginator = TRUE;
|
|
|
|
// set this state to associated so that the cancel irp routine
|
|
// can differentiate the name query stage from the setupconnection
|
|
// stage since pConnEle is in the Nbtconnecting state for both.
|
|
//
|
|
SET_STATE_LOWER (pConnEle->pLowerConnId, NBT_ASSOCIATED);
|
|
|
|
// if this routine is called to do a reconnect, DO NOT close another
|
|
// Lower Connection since one was closed the on the first
|
|
// connect attempt.
|
|
// the original "ToName" was stashed in this unused
|
|
// ptr! - for the Reconnect case
|
|
// the pNameAddr part of pTracker(pDestName) needs to pt. to
|
|
// the name so that SessionSetupContinue can find the name
|
|
pTracker->pDestName = pTracker->pConnEle->RemoteName;
|
|
pTracker->UnicodeDestName = NULL; // We don't need unicode for NetBIOS name queries
|
|
|
|
//
|
|
// For a ReQuery request, DestIp is 0, otherwise for the ReTarget
|
|
// case, DestIp is the new destination address
|
|
//
|
|
if (DestIp) {
|
|
//
|
|
// Retarget
|
|
//
|
|
status = FindNameOrQuery(pTracker->pConnEle->RemoteName,
|
|
pDeviceContext,
|
|
SessionSetupContinue,
|
|
pTracker,
|
|
(ULONG) (NAMETYPE_UNIQUE | NAMETYPE_GROUP | NAMETYPE_INET_GROUP),
|
|
&IpAddress,
|
|
&pNameAddr,
|
|
REF_NAME_CONNECT,
|
|
FALSE);
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.NbtReConnect: name=<%16.16s:%x>, Status=%lx (%d of %s)\n",
|
|
pConnEle->RemoteName, pConnEle->RemoteName[15], status, __LINE__, __FILE__));
|
|
} else {
|
|
//
|
|
// This is the ReQuery attempt
|
|
//
|
|
BOOLEAN fNameReferenced = TRUE;
|
|
|
|
status = ContinueQueryNameOnNet (pTracker,
|
|
pTracker->pConnEle->RemoteName,
|
|
pDeviceContext,
|
|
SessionSetupContinue,
|
|
&fNameReferenced);
|
|
|
|
pNameAddr = pTracker->pNameAddr;
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.NbtReConnect: name=<%16.16s:%x>, Status=%lx (%d of %s)\n",
|
|
pConnEle->RemoteName, pConnEle->RemoteName[15], status, __LINE__, __FILE__));
|
|
}
|
|
|
|
NbtConnect_Check:
|
|
if ((status == STATUS_SUCCESS) && (!IpAddress)) {
|
|
ASSERT (0);
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT, FALSE);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: returns %!status!", pTracker, status));
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS &&
|
|
IsDeviceNetbiosless(pTracker->pDeviceContext) &&
|
|
!IsSmbBoundToOutgoingInterface(IpAddress)) {
|
|
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT, FALSE);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: Fail requests on unbound SmbDevice", pTracker));
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// be sure that a close or disconnect has not come down and
|
|
// cancelled the tracker
|
|
//
|
|
if (status == STATUS_PENDING) {
|
|
// i.e. pending was returned rather than success
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return(status);
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
if (DestIp) {
|
|
IpAddress = DestIp;
|
|
}
|
|
if ((pTracker->Flags & TRACKER_CANCELLED)) {
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
status = STATUS_CANCELLED;
|
|
} else {
|
|
// set the session state to NBT_CONNECTING
|
|
CHECK_PTR(pTracker->pConnEle);
|
|
SET_STATE_UPPER (pTracker->pConnEle, NBT_CONNECTING);
|
|
pTracker->pConnEle->BytesRcvd = 0;;
|
|
pTracker->pConnEle->ReceiveIndicated = 0;
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtReConnect: Setting Up Session(cached entry!!) to %16.16s <%X>, %p\n",
|
|
pNameAddr->Name,pNameAddr->Name[15], pConnEle));
|
|
|
|
CHECK_PTR(pConnEle);
|
|
// keep track of the other end's ip address
|
|
// There may be a valid name address to use or it may have been
|
|
// nulled out to signify "Do Another Name Query"
|
|
|
|
pConnEle->pLowerConnId->SrcIpAddr = htonl(IpAddress);
|
|
SET_STATE_LOWER (pConnEle->pLowerConnId, NBT_CONNECTING);
|
|
|
|
pTracker->pTrackerWorker = NULL;
|
|
|
|
//
|
|
// We need to keep the pNameAddr data available for RAS
|
|
//
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_AUTODIAL);
|
|
|
|
pTracker->RemoteIpAddress = IpAddress;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
status = TcpSessionStart(pTracker,
|
|
IpAddress,
|
|
(tDEVICECONTEXT *)pTracker->pDeviceContext,
|
|
SessionStartupContinue,
|
|
pTracker->DestPort);
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
//
|
|
// if TcpSessionStart fails for some reason it will still
|
|
// call the completion routine which will look after
|
|
// cleaning up
|
|
//
|
|
|
|
#ifdef RASAUTODIAL
|
|
//
|
|
// Notify the automatic connection driver
|
|
// of the successful connection.
|
|
//
|
|
if (fAcdLoadedG && NT_SUCCESS(status))
|
|
{
|
|
CTELockHandle adirql;
|
|
BOOLEAN fEnabled;
|
|
|
|
CTEGetLock(&AcdDriverG.SpinLock, &adirql);
|
|
fEnabled = AcdDriverG.fEnabled;
|
|
CTEFreeLock(&AcdDriverG.SpinLock, adirql);
|
|
if (fEnabled) {
|
|
NbtNoteNewConnection(pNameAddr, pDeviceContext->IpAddress);
|
|
}
|
|
}
|
|
#endif // RASAUTODIAL
|
|
|
|
//
|
|
// pNameAddr was referenced above for RAS, so deref it now!
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_AUTODIAL, FALSE);
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// *** Error Handling Here ***
|
|
//
|
|
// ** We are still holding the JointLock **
|
|
// unlink from the active connection list and put on idle list
|
|
//
|
|
CHECK_PTR(pConnEle);
|
|
RelistConnection(pConnEle);
|
|
CTESpinLock(pConnEle,OldIrq1);
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
pConnEle->pIrp = NULL;
|
|
|
|
if (pLowerConn = pConnEle->pLowerConnId) {
|
|
CHECK_PTR(pLowerConn);
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
|
|
// need to increment the ref count for DelayedCleanupAfterDisconnect to
|
|
// work correctly since it assumes the connection got fully connected
|
|
//
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED);
|
|
ASSERT(pLowerConn->RefCount == 2);
|
|
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
|
|
|
if (NBT_VERIFY_HANDLE (pLowerConn->pDeviceContext, NBT_VERIFY_DEVCONTEXT)) {
|
|
pDeviceContext = pLowerConn->pDeviceContext;
|
|
} else {
|
|
pDeviceContext = NULL;
|
|
}
|
|
|
|
NTQueueToWorkerThread(
|
|
&pLowerConn->WorkItemCleanUpAndWipeOut,
|
|
DelayedCleanupAfterDisconnect,
|
|
NULL,
|
|
pLowerConn,
|
|
NULL,
|
|
pDeviceContext,
|
|
TRUE);
|
|
}
|
|
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR);
|
|
|
|
//
|
|
// Undo the two references done above
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
DelayedReConnect(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN PVOID DestAddr,
|
|
IN PVOID pUnused1,
|
|
IN tDEVICECONTEXT *pUnused2
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles seting up a DPC to send a session pdu so that the stack
|
|
does not get wound up in multiple sends for the keep alive timeout case.
|
|
|
|
Arguments:
|
|
|
|
pIrp - a ptr to an IRP
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
tCONNECTELE *pConnEle;
|
|
CTELockHandle OldIrq;
|
|
PCTE_IRP pIrp;
|
|
|
|
CHECK_PTR(pTracker);
|
|
// for retarget this is the destination address to connect to.
|
|
|
|
pConnEle = pTracker->pConnEle;
|
|
pTracker->pTimer = NULL;
|
|
if (pTracker->Flags & TRACKER_CANCELLED)
|
|
{
|
|
CTELockHandle OldIrq1;
|
|
|
|
//
|
|
// the connection setup got cancelled, return the connect irp
|
|
//
|
|
CTESpinLock(pConnEle,OldIrq1);
|
|
if (pIrp = pConnEle->pIrp)
|
|
{
|
|
pConnEle->pIrp = NULL;
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTEIoComplete(pIrp,STATUS_CANCELLED,0);
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
}
|
|
|
|
//
|
|
// if SessionSetupContinue has run, it has set the refcount to zero
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if (pTracker->RefConn == 0)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
}
|
|
else
|
|
{
|
|
pTracker->RefConn--;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PUSH_LOCATION(0x85);
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
status = NbtReConnect(pTracker, PtrToUlong(DestAddr));
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// Reset the Irp pending flag
|
|
// No need to do this - pending has already be returned.
|
|
//CTEResetIrpPending(pConnEle->pIrp);
|
|
|
|
//
|
|
// tell the client that the session setup failed
|
|
//
|
|
CTELockHandle OldIrq1;
|
|
|
|
CTESpinLock(pConnEle,OldIrq1);
|
|
if (pIrp = pConnEle->pIrp)
|
|
{
|
|
pConnEle->pIrp = NULL;
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
|
|
CTEIoComplete( pIrp, STATUS_REMOTE_NOT_LISTENING, 0 ) ;
|
|
} else {
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtConnect(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN PVOID pTimeout,
|
|
IN PTDI_CONNECTION_INFORMATION pCallInfo,
|
|
IN PIRP pIrp
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles setting up a connection (netbios session) to
|
|
destination. This routine is also called by the Reconnect code when
|
|
doing a Retarget or trying to reach a destination that does not have
|
|
a listen currently posted. In this case the parameters mean different
|
|
things. pIrp could be a new Ipaddress to use (Retarget) and pCallinfo
|
|
will be null.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
tCONNECTELE *pConnEle;
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
BOOLEAN fNoIpAddress;
|
|
|
|
pConnEle = pRequest->Handle.ConnectionContext;
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("TDI_CONNECT: pIrp %p pConnEle %p pClientEle %p",
|
|
pIrp, pConnEle, pConnEle->pClientEle));
|
|
|
|
if (!pConnEle->pClientEle) {
|
|
return (STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
ASSERT(pCallInfo);
|
|
|
|
//
|
|
// this code handles the When DHCP has not assigned an IP address yet
|
|
//
|
|
fNoIpAddress = (!pConnEle->pClientEle->pDeviceContext->pSessionFileObject) ||
|
|
(pConnEle->pClientEle->pDeviceContext->IpAddress == 0);
|
|
#ifdef RASAUTODIAL
|
|
if (fNoIpAddress && fAcdLoadedG) {
|
|
CTELockHandle adirql;
|
|
BOOLEAN fEnabled;
|
|
|
|
//
|
|
// There is no IP address assigned to the interface,
|
|
// attempt to create an automatic connection.
|
|
//
|
|
CTEGetLock(&AcdDriverG.SpinLock, &adirql);
|
|
fEnabled = AcdDriverG.fEnabled;
|
|
CTEFreeLock(&AcdDriverG.SpinLock, adirql);
|
|
if (fEnabled)
|
|
{
|
|
//
|
|
// Set a special cancel routine on the irp
|
|
// in case we get cancelled during the
|
|
// automatic connection.
|
|
//
|
|
(VOID)NbtSetCancelRoutine( pIrp, NbtCancelPreConnect, pConnEle->pClientEle->pDeviceContext);
|
|
if (NbtAttemptAutoDial(
|
|
pConnEle,
|
|
pTimeout,
|
|
pCallInfo,
|
|
pIrp,
|
|
0,
|
|
NbtRetryPreConnect))
|
|
{
|
|
return STATUS_PENDING;
|
|
}
|
|
//
|
|
// We did not enqueue the irp on the
|
|
// automatic connection driver, so
|
|
// clear the cancel routine we set
|
|
// above.
|
|
//
|
|
(VOID)NbtCancelCancelRoutine(pIrp);
|
|
}
|
|
}
|
|
#endif // RASAUTODIAL
|
|
|
|
if (fNoIpAddress) {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("returns STATUS_BAD_NETWORK_PATH"));
|
|
return(STATUS_BAD_NETWORK_PATH);
|
|
}
|
|
|
|
// check the connection element for validity
|
|
CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status)
|
|
return NbtConnectCommon(pRequest, pTimeout, pCallInfo, pIrp);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtConnectCommon(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN PVOID pTimeout,
|
|
IN PTDI_CONNECTION_INFORMATION pCallInfo,
|
|
IN PIRP pIrp
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles setting up a connection (netbios session) to
|
|
destination. This routine is also called by the DelayedReconnect code when
|
|
doing a Retarget or trying to reach a destination that does not have
|
|
a listen currently posted. In this case the parameters mean different
|
|
things. pIrp could be a new Ipaddress to use (Retarget) and pCallinfo
|
|
will be null.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
TDI_ADDRESS_NETBT_INTERNAL TdiAddr;
|
|
tCONNECTELE *pConnEle;
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
tIPADDRESS IpAddress;
|
|
PCHAR pToName;
|
|
USHORT sLength;
|
|
tSESSIONREQ *pSessionReq = NULL;
|
|
PUCHAR pCopyTo;
|
|
tCLIENTELE *pClientEle;
|
|
ULONG NameLen;
|
|
tDGRAM_SEND_TRACKING *pTracker, *pQueryTracker;
|
|
tNAMEADDR *pNameAddr;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
NBT_WORK_ITEM_CONTEXT *pContext;
|
|
tIPADDRESS RemoteIpAddress;
|
|
tLOWERCONNECTION *pLowerDump;
|
|
PLIST_ENTRY pEntry;
|
|
PCHAR pSessionName;
|
|
tDEVICECONTEXT *pDeviceContextOut = NULL;
|
|
|
|
#ifdef _PNP_POWER_
|
|
if (NbtConfig.Unloading) {
|
|
KdPrint (("Nbt.NbtConnectCommon: --> ERROR New Connect request while Unloading!!!\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
#endif // _PNP_POWER_
|
|
|
|
#ifdef DBG
|
|
{
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
|
|
ASSERT(pIrpSp && pIrpSp->CompletionRoutine == NbtpConnectCompletionRoutine);
|
|
}
|
|
#endif
|
|
|
|
ASSERT (pCallInfo);
|
|
|
|
/* If it is from local Irp, we always send an internal address format */
|
|
if (pCallInfo->RemoteAddressLength < sizeof (TA_NETBT_INTERNAL_ADDRESS)) {
|
|
ASSERT (0);
|
|
return (STATUS_INVALID_ADDRESS);
|
|
}
|
|
|
|
ASSERT(((PTRANSPORT_ADDRESS)pCallInfo->RemoteAddress)->Address[0].AddressType == TDI_ADDRESS_TYPE_UNSPEC);
|
|
CTEMemCopy(&TdiAddr,
|
|
(PTDI_ADDRESS_NETBT_INTERNAL)((PTRANSPORT_ADDRESS)pCallInfo->RemoteAddress)->Address[0].Address,
|
|
sizeof(TdiAddr));
|
|
|
|
pToName = TdiAddr.OEMRemoteName.Buffer;
|
|
NameLen = TdiAddr.OEMRemoteName.Length;
|
|
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.NbtConnectCommon: Remote Name: %*.*s, Length=%d\n", NameLen, NameLen, pToName, NameLen));
|
|
|
|
pConnEle = pRequest->Handle.ConnectionContext;
|
|
|
|
if (RemoteIpAddress = Nbt_inet_addr(pToName, SESSION_SETUP_FLAG)) {
|
|
pDeviceContextOut = GetDeviceFromInterface (htonl(RemoteIpAddress), TRUE);
|
|
}
|
|
|
|
//
|
|
// Acquire this resource to co-ordinate with DHCP changing the IP
|
|
// address
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpIrp %p: pConnEle %p: Connecting to %!NBTNAME!<%02x>",
|
|
pIrp, pConnEle, pToName, (unsigned)pToName[15]));
|
|
|
|
if ((!(NBT_VERIFY_HANDLE(pConnEle, NBT_VERIFY_CONNECTION))) || (!(pClientEle = pConnEle->pClientEle))) {
|
|
KdPrint (("Nbt.NbtConnectCommon: --> ERROR Address not associated for pConnEle=<%p>\n", pConnEle));
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpIrp %p: ERROR Address not associated", pIrp));
|
|
|
|
if (pDeviceContextOut) {
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContextOut, REF_DEV_OUT_FROM_IP, TRUE);
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return(STATUS_INVALID_ADDRESS);
|
|
}
|
|
|
|
CTESpinLock(pClientEle,OldIrq1);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
pDeviceContext = pClientEle->pDeviceContext;
|
|
|
|
status = CheckConnect(pConnEle, pClientEle, pDeviceContext);
|
|
if (status != STATUS_SUCCESS) {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpIrp %p: CheckConnect return %!status!", pIrp, status));
|
|
|
|
pConnEle->pIrp = NULL;
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
if (pDeviceContextOut) {
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContextOut, REF_DEV_OUT_FROM_IP, TRUE);
|
|
}
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return status;
|
|
}
|
|
|
|
if (RemoteIpAddress && NbtConfig.ConnectOnRequestedInterfaceOnly &&
|
|
!IsDeviceNetbiosless(pDeviceContext) && pDeviceContext != pDeviceContextOut) {
|
|
pConnEle->pIrp = NULL;
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
if (pDeviceContextOut) {
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContextOut, REF_DEV_OUT_FROM_IP, TRUE);
|
|
}
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpIrp %p: %!ipaddr! because Outgoing interface != RequestedInterface",
|
|
pIrp, RemoteIpAddress));
|
|
return STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_CONNECTING);
|
|
|
|
// Increment the ref count so that a cleanup cannot remove
|
|
// the pConnEle till the session is setup - one of these is removed when
|
|
// the session is setup and the other is removed when it is disconnected.
|
|
//
|
|
NBT_REFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
NBT_REFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
ASSERT(pConnEle->RefCount >= 3);
|
|
//
|
|
// unlink the connection from the idle connection list and put on active list
|
|
//
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InsertTailList(&pClientEle->ConnectActive,&pConnEle->Linkage);
|
|
|
|
// this field is used to hold a disconnect irp if it comes down during
|
|
// NBT_CONNECTING or NBT_SESSION_OUTBOUND states
|
|
//
|
|
pConnEle->pIrpDisc = NULL;
|
|
|
|
// we must store the client's irp in the connection element so that when
|
|
// the session sets up, we can complete the Irp.
|
|
ASSERT (pIrp);
|
|
pConnEle->pIrp = (PVOID) pIrp;
|
|
pConnEle->Orig = TRUE;
|
|
pConnEle->SessionSetupCount = NBT_SESSION_SETUP_COUNT-1; // -1 for this attempt
|
|
pConnEle->pClientEle->AddressType = TdiAddr.AddressType;
|
|
pConnEle->AddressType = TdiAddr.AddressType;
|
|
//
|
|
// Save the remote name while we still have it
|
|
//
|
|
CTEMemCopy (pConnEle->RemoteName, pToName, NETBIOS_NAME_SIZE);
|
|
if (TdiAddr.OEMEndpointName.Buffer) {
|
|
CTEMemCopy (pConnEle->pClientEle->EndpointName, TdiAddr.OEMEndpointName.Buffer, NETBIOS_NAME_SIZE);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpIrp %p: Endpoint %!NBTNAME!<%02lx>",
|
|
pIrp,
|
|
TdiAddr.OEMEndpointName.Buffer,
|
|
TdiAddr.OEMEndpointName.Buffer[NETBIOS_NAME_SIZE-1]));
|
|
}
|
|
|
|
// get a buffer for tracking Session setup
|
|
status = GetTracker(&pTracker, NBT_TRACKER_CONNECT);
|
|
if (!NT_SUCCESS(status)) {
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ExitProc;
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.NbtConnectCommon: Tracker %lx\n",pTracker));
|
|
|
|
// the length of the Session Request Pkt is the 4 byte session hdr length + the
|
|
// half ascii calling and called names + the scope length times 2, one for each name
|
|
//
|
|
sLength = (USHORT) (sizeof(tSESSIONREQ) + (NETBIOS_NAME_SIZE << 2) + (NbtConfig.ScopeLength <<1));
|
|
|
|
pTracker->pNetbiosUnicodeEX = TdiAddr.pNetbiosUnicodeEX;
|
|
pTracker->UnicodeRemoteName = NULL;
|
|
if (TdiAddr.pNetbiosUnicodeEX) {
|
|
pTracker->ucRemoteName = TdiAddr.pNetbiosUnicodeEX->RemoteName;
|
|
ASSERT((pTracker->ucRemoteName.MaximumLength % sizeof(WCHAR)) == 0);
|
|
ASSERT((pTracker->ucRemoteName.Length % sizeof(WCHAR)) == 0);
|
|
|
|
if (TdiAddr.pNetbiosUnicodeEX->NameBufferType != NBT_WRITEONLY) {
|
|
pTracker->UnicodeRemoteName = NbtAllocMem(pTracker->ucRemoteName.MaximumLength, NBT_TAG('F'));
|
|
if (pTracker->UnicodeRemoteName) {
|
|
pTracker->UnicodeRemoteNameLength = pTracker->ucRemoteName.Length;
|
|
CTEMemCopy(pTracker->UnicodeRemoteName, pTracker->ucRemoteName.Buffer,
|
|
pTracker->ucRemoteName.Length+sizeof(WCHAR));
|
|
}
|
|
}
|
|
// we ignore the failure because it isn't a critical feature. This failure only cause us not able to
|
|
// take advantage of the UNICODE information and return the resolved DNS name to RDR.
|
|
} else {
|
|
pTracker->ucRemoteName.Buffer = NULL;
|
|
pTracker->ucRemoteName.Length = 0;
|
|
pTracker->ucRemoteName.MaximumLength = 0;
|
|
}
|
|
|
|
/*
|
|
* Other netbt routines always assume that we have at least 16 bytes
|
|
* for remote name.
|
|
*/
|
|
if (NameLen < NETBIOS_NAME_SIZE) {
|
|
pTracker->pRemoteName = NbtAllocMem(NETBIOS_NAME_SIZE, NBT_TAG('F'));
|
|
} else {
|
|
pTracker->pRemoteName = NbtAllocMem(NameLen, NBT_TAG('F'));
|
|
}
|
|
pSessionReq = (tSESSIONREQ *)NbtAllocMem(sLength,NBT_TAG('G'));
|
|
|
|
if (pTracker->pRemoteName == NULL || pSessionReq == NULL) {
|
|
if (pTracker->pRemoteName) {
|
|
CTEMemFree(pTracker->pRemoteName);
|
|
pTracker->pRemoteName = NULL;
|
|
}
|
|
if (pTracker->UnicodeRemoteName) {
|
|
CTEMemFree(pTracker->UnicodeRemoteName);
|
|
pTracker->UnicodeRemoteName = NULL;
|
|
}
|
|
if (pSessionReq) {
|
|
CTEMemFree(pSessionReq);
|
|
pSessionReq = NULL;
|
|
}
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
goto ExitProc;
|
|
}
|
|
|
|
CTEMemCopy (pTracker->pRemoteName, pToName, NameLen);
|
|
pTracker->RemoteNameLength = NameLen; // May be needed for Dns Name resolution
|
|
pTracker->pDestName = pTracker->pRemoteName;
|
|
pTracker->UnicodeDestName = pTracker->UnicodeRemoteName; // bug #20697, #95241
|
|
pTracker->SendBuffer.pBuffer = pTracker->pRemoteName;
|
|
pTracker->SendBuffer.Length = 0;
|
|
pTracker->SendBuffer.pDgramHdr = pSessionReq;
|
|
|
|
// this is a ptr to the name in the client's, Irp, so that address must
|
|
// remain valid until this completes. It should be valid, because we
|
|
// do not complete the Irp until the transaction completes. This ptr
|
|
// is overwritten when the name resolves, so that it points the the
|
|
// pNameAddr in the hash table.
|
|
//
|
|
pTracker->RefCount = 1;
|
|
pTracker->RefConn = 1;
|
|
pTracker->pClientIrp = pIrp;
|
|
pTracker->pTimeout = pTimeout; // the timeout value is passed on through to the transport
|
|
pTracker->Flags = SESSION_SETUP_FLAG;
|
|
pTracker->pDeviceContext = pDeviceContext;
|
|
pTracker->pConnEle = pConnEle;
|
|
#ifdef _NETBIOSLESS
|
|
pTracker->DestPort = pDeviceContext->SessionPort; // Port to Send to
|
|
#else
|
|
pTracker->DestPort = NBT_SESSION_TCP_PORT;
|
|
#endif
|
|
|
|
#ifndef VXD
|
|
if (pConnEle->pClientEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX)
|
|
{
|
|
pSessionName = pConnEle->pClientEle->EndpointName;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pSessionName = pToName;
|
|
}
|
|
|
|
pSessionReq->Hdr.Type = NBT_SESSION_REQUEST;
|
|
pSessionReq->Hdr.Flags = NBT_SESSION_FLAGS;
|
|
pSessionReq->Hdr.Length = (USHORT)htons(sLength-(USHORT)sizeof(tSESSIONHDR)); // size of called and calling NB names.
|
|
|
|
pTracker->SendBuffer.HdrLength = (ULONG)sLength;
|
|
|
|
// put the Dest HalfAscii name into the Session Pdu
|
|
pCopyTo = ConvertToHalfAscii ((PCHAR)&pSessionReq->CalledName.NameLength,
|
|
pSessionName,
|
|
NbtConfig.pScope,
|
|
NbtConfig.ScopeLength);
|
|
|
|
// put the Source HalfAscii name into the Session Pdu
|
|
pCopyTo = ConvertToHalfAscii (pCopyTo,
|
|
((tADDRESSELE *)pClientEle->pAddress)->pNameAddr->Name,
|
|
NbtConfig.pScope,
|
|
NbtConfig.ScopeLength);
|
|
|
|
// store the tracker in the Irp Rcv ptr so it can be used by the
|
|
// session setup code in hndlrs.c in the event the destination is
|
|
// between posting listens and this code should re-attempt the
|
|
// session setup. The code in hndlrs.c returns the tracker to its
|
|
// free list and frees the session hdr memory too.
|
|
//
|
|
// We need to set this while holding the ConnEle lock because the client
|
|
// can call NbtDisconnect while we are opening a Tcp handle and try to
|
|
// set the Tracker's flag to TRACKER_CANCELLED
|
|
//
|
|
pConnEle->pIrpRcv = (PIRP)pTracker;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
// open a connection with the transport for this session
|
|
status = NbtOpenAndAssocConnection (pDeviceContext, pConnEle, &pConnEle->pLowerConnId, '3');
|
|
if (!NT_SUCCESS(status)) {
|
|
goto NbtConnect_Check;
|
|
}
|
|
|
|
// We need to track that this side originated the call so we discard this
|
|
// connection at the end
|
|
//
|
|
pConnEle->pLowerConnId->bOriginator = TRUE;
|
|
|
|
// set this state to associated so that the cancel irp routine
|
|
// can differentiate the name query stage from the setupconnection
|
|
// stage since pConnEle is in the Nbtconnecting state for both.
|
|
//
|
|
SET_STATE_LOWER (pConnEle->pLowerConnId, NBT_ASSOCIATED);
|
|
|
|
// if this routine is called to do a reconnect, DO NOT close another
|
|
// Lower Connection since one was closed the on the first
|
|
// connect attempt.
|
|
//
|
|
// remove a lower connection from the free list attached to the device
|
|
// context since when this pConnEle was created, a lower connectin
|
|
// was created then incase inbound calls were to be accepted on the
|
|
// connection. But since it is an outbound call, remove a lower
|
|
// connection.
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq2); // Need this for DerefLowerConn
|
|
CTESpinLock(pDeviceContext,OldIrq1);
|
|
if (!pConnEle->LowerConnBlockRemoved &&
|
|
!IsListEmpty(&pDeviceContext->LowerConnFreeHead))
|
|
{
|
|
pEntry = RemoveHeadList(&pDeviceContext->LowerConnFreeHead);
|
|
pLowerDump = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage);
|
|
InterlockedDecrement (&pDeviceContext->NumFreeLowerConnections);
|
|
|
|
pConnEle->LowerConnBlockRemoved = TRUE;
|
|
|
|
//
|
|
// close the lower connection with the transport
|
|
//
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtConnectCommon: On Connect, close handle for pLower=<%p>\n", pLowerDump));
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerDump, REF_LOWC_CREATE, TRUE);
|
|
}
|
|
|
|
CTESpinFree(pDeviceContext,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
//
|
|
// Check if the destination name is an IP address
|
|
//
|
|
#ifndef VXD
|
|
if (RemoteIpAddress)
|
|
{
|
|
//
|
|
// Tell Outbound() not to schedule a re-connect attempt when a negative response is received.
|
|
// Otherwise, we may end up with indefinitely loop
|
|
//
|
|
pTracker->ResolutionContextFlags = 0xFF;
|
|
|
|
//
|
|
// If the Address type is TDI_ADDRESS_TYPE_NETBIOS_EX, we have
|
|
// been given a specific endpoint to use, so try to setup the
|
|
// session using that Endpoint only
|
|
//
|
|
if (pConnEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX)
|
|
{
|
|
//
|
|
// add this IP address to the remote hashtable
|
|
//
|
|
status = LockAndAddToHashTable(NbtConfig.pRemoteHashTbl,
|
|
pToName,
|
|
NbtConfig.pScope,
|
|
RemoteIpAddress,
|
|
NBT_UNIQUE,
|
|
NULL,
|
|
NULL,
|
|
pDeviceContextOut,
|
|
NAME_RESOLVED_BY_IP);
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.NbtConnectCommon: AddRecordToHashTable <%-16.16s:%x>, Status %x\n",
|
|
pToName, pToName[15], status));
|
|
|
|
if (NT_SUCCESS (status)) // SUCCESS if added first time, PENDING if name already existed!
|
|
{
|
|
SessionSetupContinue(pTracker, STATUS_SUCCESS);
|
|
status = STATUS_PENDING;
|
|
} else {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
}
|
|
}
|
|
//
|
|
// Address type is TDI_ADDRESS_TYPE_NETBIOS
|
|
// The endpoint name is the same as the IP address, so send a NodeStatus
|
|
// request to the remote machine to get a proper Endpoint name
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// NbtSendNodeStatus will either return Pending or error -- it
|
|
// should never return success!
|
|
//
|
|
pTracker->CompletionRoutine = SessionSetupContinue;
|
|
status = NbtSendNodeStatus(pDeviceContext,
|
|
pToName,
|
|
NULL,
|
|
pTracker,
|
|
ExtractServerNameCompletion);
|
|
if (!NT_SUCCESS(status)) {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
}
|
|
}
|
|
}
|
|
else // the name is not an IP address!
|
|
#endif
|
|
{
|
|
if (NameLen <= NETBIOS_NAME_SIZE) {
|
|
status = FindNameOrQuery(pToName,
|
|
pDeviceContext,
|
|
SessionSetupContinue,
|
|
pTracker,
|
|
(ULONG) (NAMETYPE_UNIQUE | NAMETYPE_GROUP | NAMETYPE_INET_GROUP),
|
|
&IpAddress,
|
|
&pNameAddr,
|
|
REF_NAME_CONNECT,
|
|
FALSE);
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.NbtConnectCommon: name=<%*.*s:%x>, Len=%d, Status=%lx (%d of %s)\n",
|
|
NameLen, NameLen, pConnEle->RemoteName,
|
|
pConnEle->RemoteName[15], NameLen, status, __LINE__, __FILE__));
|
|
if (!NT_SUCCESS(status)) {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
} else if (NULL != pNameAddr && NULL != pNameAddr->FQDN.Buffer &&
|
|
pTracker->pNetbiosUnicodeEX &&
|
|
(pTracker->pNetbiosUnicodeEX->NameBufferType == NBT_READWRITE ||
|
|
pTracker->pNetbiosUnicodeEX->NameBufferType == NBT_WRITEONLY)) {
|
|
|
|
USHORT NameLength, MaxLength;
|
|
|
|
NameLength = pNameAddr->FQDN.Length;
|
|
MaxLength = pTracker->pNetbiosUnicodeEX->RemoteName.MaximumLength;
|
|
if ((SHORT)NameLength > (SHORT)(MaxLength - sizeof(WCHAR))) {
|
|
NameLength = MaxLength - sizeof(WCHAR);
|
|
}
|
|
if ((SHORT)NameLength >= 0) {
|
|
CTEMemCopy(pTracker->pNetbiosUnicodeEX->RemoteName.Buffer,
|
|
pNameAddr->FQDN.Buffer, NameLength);
|
|
pTracker->pNetbiosUnicodeEX->RemoteName.Buffer[NameLength/sizeof(WCHAR)] = L'\0';
|
|
pTracker->pNetbiosUnicodeEX->RemoteName.Length = NameLength;
|
|
pTracker->pNetbiosUnicodeEX->NameBufferType = NBT_WRITTEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if the name is longer than 16 bytes, it's not a netbios name.
|
|
// skip wins, broadcast etc. and go straight to dns resolution
|
|
// Also, we would go to DNS if the request came over the SmbDevice
|
|
//
|
|
#ifdef _NETBIOSLESS
|
|
if ((NameLen > NETBIOS_NAME_SIZE) ||
|
|
((IsDeviceNetbiosless(pDeviceContext)) && (!NT_SUCCESS(status))))
|
|
#else
|
|
if (NameLen > NETBIOS_NAME_SIZE)
|
|
#endif
|
|
{
|
|
pTracker->AddressType = pConnEle->AddressType;
|
|
#ifndef VXD
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.NbtConnectCommon: $$$$$ DNS for NETBIOS name=<%*.*s:%x>, Len=%d, Status=%lx (%d of %s)\n",
|
|
NameLen, NameLen, pConnEle->RemoteName,
|
|
pConnEle->RemoteName[15], NameLen, status, __LINE__, __FILE__));
|
|
#endif
|
|
|
|
if (pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('H'))) {
|
|
pContext->pTracker = NULL; // no query tracker
|
|
pContext->pClientContext = pTracker; // the client tracker
|
|
pContext->ClientCompletion = SessionSetupContinue;
|
|
pContext->pDeviceContext = pDeviceContext;
|
|
|
|
//
|
|
// Start the timer so that the request does not hang waiting for Dns!
|
|
//
|
|
StartLmHostTimer(pContext, FALSE);
|
|
status = NbtProcessLmhSvcRequest (pContext, NBT_RESOLVE_WITH_DNS);
|
|
if (!NT_SUCCESS (status)) {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
CTEMemFree(pContext);
|
|
}
|
|
} else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
}
|
|
}
|
|
}
|
|
|
|
NbtConnect_Check:
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status! %!ipaddr!", pIrp, pTracker, status, IpAddress));
|
|
|
|
if ((status == STATUS_SUCCESS) && (!IpAddress)) {
|
|
ASSERT(0);
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT, FALSE);
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS &&
|
|
IsDeviceNetbiosless(pTracker->pDeviceContext) &&
|
|
!IsSmbBoundToOutgoingInterface(IpAddress)) {
|
|
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT, FALSE);
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (pDeviceContextOut) {
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContextOut, REF_DEV_OUT_FROM_IP, TRUE);
|
|
pDeviceContextOut = NULL;
|
|
}
|
|
|
|
//
|
|
// be sure that a close or disconnect has not come down and
|
|
// cancelled the tracker
|
|
//
|
|
if (status == STATUS_PENDING) {
|
|
// i.e. pending was returned rather than success
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return(status);
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
if ((pTracker->Flags & TRACKER_CANCELLED))
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else // connect as long as we have an IP address (even to group names)
|
|
{
|
|
// set the session state to NBT_CONNECTING
|
|
CHECK_PTR(pTracker->pConnEle);
|
|
SET_STATE_UPPER (pTracker->pConnEle, NBT_CONNECTING);
|
|
pTracker->pConnEle->BytesRcvd = 0;;
|
|
pTracker->pConnEle->ReceiveIndicated = 0;
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtConnectCommon: Setting Up Session(cached entry!!) to %16.16s <%X>, %p\n",
|
|
pNameAddr->Name,pNameAddr->Name[15], pConnEle));
|
|
|
|
CHECK_PTR(pConnEle);
|
|
// keep track of the other end's ip address
|
|
// There may be a valid name address to use or it may have been
|
|
// nulled out to signify "Do Another Name Query"
|
|
|
|
pConnEle->pLowerConnId->SrcIpAddr = htonl(IpAddress);
|
|
SET_STATE_LOWER (pConnEle->pLowerConnId, NBT_CONNECTING);
|
|
|
|
pTracker->pTrackerWorker = NULL;
|
|
|
|
//
|
|
// We need to keep the pNameAddr data available for RAS
|
|
//
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_AUTODIAL);
|
|
|
|
pTracker->RemoteIpAddress = IpAddress;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
status = TcpSessionStart(pTracker,
|
|
IpAddress,
|
|
(tDEVICECONTEXT *)pTracker->pDeviceContext,
|
|
SessionStartupContinue,
|
|
pTracker->DestPort);
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
//
|
|
// if TcpSessionStart fails for some reason it will still
|
|
// call the completion routine which will look after
|
|
// cleaning up
|
|
//
|
|
|
|
#ifdef RASAUTODIAL
|
|
//
|
|
// Notify the automatic connection driver
|
|
// of the successful connection.
|
|
//
|
|
if (fAcdLoadedG && NT_SUCCESS(status))
|
|
{
|
|
CTELockHandle adirql;
|
|
BOOLEAN fEnabled;
|
|
|
|
CTEGetLock(&AcdDriverG.SpinLock, &adirql);
|
|
fEnabled = AcdDriverG.fEnabled;
|
|
CTEFreeLock(&AcdDriverG.SpinLock, adirql);
|
|
if (fEnabled)
|
|
{
|
|
NbtNoteNewConnection(pNameAddr, pDeviceContext->IpAddress);
|
|
}
|
|
}
|
|
#endif // RASAUTODIAL
|
|
|
|
//
|
|
// pNameAddr was referenced above for RAS, so deref it now!
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_AUTODIAL, FALSE);
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// *** Error Handling Here ***
|
|
//
|
|
// ** We are still holding the JointLock **
|
|
// unlink from the active connection list and put on idle list
|
|
//
|
|
CHECK_PTR(pConnEle);
|
|
RelistConnection(pConnEle);
|
|
CTESpinLock(pConnEle,OldIrq1);
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
pConnEle->pIrp = NULL;
|
|
|
|
if (pLowerConn = pConnEle->pLowerConnId)
|
|
{
|
|
CHECK_PTR(pLowerConn);
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
|
|
// need to increment the ref count for DelayedCleanupAfterDisconnect to
|
|
// work correctly since it assumes the connection got fully connected
|
|
//
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED);
|
|
ASSERT(pLowerConn->RefCount == 2);
|
|
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
|
|
|
if (NBT_VERIFY_HANDLE (pLowerConn->pDeviceContext, NBT_VERIFY_DEVCONTEXT))
|
|
{
|
|
pDeviceContext = pLowerConn->pDeviceContext;
|
|
}
|
|
else
|
|
{
|
|
pDeviceContext = NULL;
|
|
}
|
|
|
|
NTQueueToWorkerThread(
|
|
&pLowerConn->WorkItemCleanUpAndWipeOut,
|
|
DelayedCleanupAfterDisconnect,
|
|
NULL,
|
|
pLowerConn,
|
|
NULL,
|
|
pDeviceContext,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR);
|
|
|
|
//
|
|
// Undo the two references done above
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
|
|
return(status);
|
|
|
|
|
|
ExitProc:
|
|
pConnEle->pIrp = NULL;
|
|
|
|
//
|
|
// Put the connection back on the idle connection list
|
|
//
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InsertTailList(&pClientEle->ConnectHead,&pConnEle->Linkage);
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(pClientEle,OldIrq1);
|
|
|
|
if (pDeviceContextOut) {
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContextOut, REF_DEV_OUT_FROM_IP, TRUE);
|
|
pDeviceContextOut = NULL;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
//
|
|
// Undo the two references done above
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION(pConnEle, REF_CONN_SESSION);
|
|
NBT_DEREFERENCE_CONNECTION(pConnEle, REF_CONN_CONNECT);
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p %!status!", pIrp, pTracker, status));
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
CleanUpPartialConnection(
|
|
IN NTSTATUS status,
|
|
IN tCONNECTELE *pConnEle,
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN PIRP pClientIrp,
|
|
IN CTELockHandle irqlJointLock,
|
|
IN CTELockHandle irqlConnEle
|
|
)
|
|
{
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
PIRP pIrpDisc;
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p pConnEle %p %!status!",
|
|
pClientIrp, pTracker, pConnEle, status));
|
|
|
|
if (pConnEle->state != NBT_IDLE)
|
|
{
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
}
|
|
|
|
//
|
|
// If the tracker is cancelled then NbtDisconnect has run and there is
|
|
// a disconnect irp waiting to be returned.
|
|
//
|
|
pIrpDisc = NULL;
|
|
|
|
if (pTracker->Flags & TRACKER_CANCELLED)
|
|
{
|
|
//
|
|
// Complete the disconnect irp now too
|
|
//
|
|
pIrpDisc = pConnEle->pIrpDisc;
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
|
|
FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR);
|
|
|
|
//
|
|
// this will close the lower connection and dereference pConnEle once.
|
|
//
|
|
QueueCleanup (pConnEle, &irqlJointLock, &irqlConnEle);
|
|
|
|
CTESpinFree(pConnEle,irqlConnEle);
|
|
|
|
//
|
|
// If the state is IDLE it means that NbtCleanupConnection has run and
|
|
// the connection has been removed from the list so don't add it to
|
|
// the list again
|
|
//
|
|
if (pConnEle->state != NBT_IDLE)
|
|
{
|
|
RelistConnection(pConnEle);
|
|
}
|
|
CTESpinFree(&NbtConfig.JointLock,irqlJointLock);
|
|
|
|
//
|
|
// remove the last reference added in nbt connect. The refcount will be 2
|
|
// if nbtcleanupconnection has not run and 1, if it has. So this call
|
|
// could free pConnEle.
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pTracker %p pConnEle %p %!status!",
|
|
pClientIrp, pTracker, pConnEle, status));
|
|
|
|
if (status == STATUS_TIMEOUT)
|
|
{
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
CTEIoComplete(pClientIrp,status,0L);
|
|
|
|
//
|
|
// This is a disconnect irp that has been queued till the name query
|
|
// completed
|
|
//
|
|
if (pIrpDisc)
|
|
{
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("TDI_DISCONNECT pIrp %p", pIrpDisc));
|
|
|
|
CTEIoComplete(pIrpDisc,STATUS_SUCCESS,0L);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
SessionSetupContinue(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles setting up a session after a name has been resolved
|
|
to an IP address.
|
|
|
|
This routine is given as the completion routine to the "QueryNameOnNet" call
|
|
in NbtConnect, above. When a name query response comes in or the
|
|
timer times out after N retries, this routine is called passing STATUS_TIMEOUT
|
|
for a failure.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
tNAMEADDR *pNameAddr = NULL;
|
|
ULONG lNameType;
|
|
PIRP pClientIrp;
|
|
PIRP pIrpDisc;
|
|
ULONG IpAddress;
|
|
tCONNECTELE *pConnEle;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
pConnEle = pTracker->pConnEle;
|
|
CHECK_PTR(pConnEle);
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status!", pTracker, status));
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
/*
|
|
* Find the NameAddr and reference it
|
|
*/
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
lNameType = NAMETYPE_UNIQUE;
|
|
pNameAddr = FindNameRemoteThenLocal(pTracker, &IpAddress, &lNameType);
|
|
if (pNameAddr) {
|
|
// increment so the name cannot disappear and to be consistent
|
|
// with FindNameOrQuery , which increments the refcount, so
|
|
// we always need to deref it when the connection is setup.
|
|
//
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT);
|
|
// DEBUG
|
|
ASSERT(pNameAddr->RefCount >= 2);
|
|
} else {
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
}
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!ipaddr! %!status!", pTracker, IpAddress, status));
|
|
|
|
/*
|
|
* IsSmbBoundToOutgoingInterface won't work with JointLock held
|
|
*/
|
|
if (NT_SUCCESS(status) &&
|
|
IsDeviceNetbiosless(pTracker->pDeviceContext) &&
|
|
!IsSmbBoundToOutgoingInterface(IpAddress)) {
|
|
|
|
/* This status may be changed into STATUS_CANCELLED below */
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: Client not bound to NetbiosSmb", pTracker));
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
if ((pTracker->Flags & TRACKER_CANCELLED) ||
|
|
(!(pLowerConn = pConnEle->pLowerConnId)) || // The lower connection could have been cleaned up!
|
|
(!NBT_VERIFY_HANDLE(pTracker->pDeviceContext, NBT_VERIFY_DEVCONTEXT)))
|
|
{
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
|
|
// this is the QueryOnNet Tracker ptr being cleared rather than the
|
|
// session setup tracker.
|
|
//
|
|
pTracker->pTrackerWorker = NULL;
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
// check the Remote table and then the Local table
|
|
// a session can only be started with a unique named destination
|
|
//
|
|
if (lNameType & (NAMETYPE_UNIQUE | NAMETYPE_GROUP | NAMETYPE_INET_GROUP))
|
|
{
|
|
// set the session state, initialize a few things and setup a
|
|
// TCP connection, calling SessionStartupContinue when the TCP
|
|
// connection is up
|
|
//
|
|
CHECK_PTR(pConnEle);
|
|
SET_STATE_LOWER (pLowerConn, NBT_CONNECTING);
|
|
|
|
SET_STATE_UPPER (pConnEle, NBT_CONNECTING);
|
|
pConnEle->BytesRcvd = 0;;
|
|
pConnEle->ReceiveIndicated = 0;
|
|
CHECK_PTR(pTracker);
|
|
pTracker->pNameAddr = pNameAddr;
|
|
|
|
if (NULL == pNameAddr->FQDN.Buffer && pTracker->pNetbiosUnicodeEX &&
|
|
pTracker->pNetbiosUnicodeEX->NameBufferType == NBT_WRITTEN) {
|
|
//
|
|
// FQDN is available
|
|
// Save it into the pNameAddr
|
|
//
|
|
pNameAddr->FQDN.Buffer = NbtAllocMem(
|
|
pTracker->pNetbiosUnicodeEX->RemoteName.Length + sizeof(WCHAR),
|
|
NBT_TAG('F'));
|
|
if (NULL != pNameAddr->FQDN.Buffer) {
|
|
pNameAddr->FQDN.Length = pTracker->pNetbiosUnicodeEX->RemoteName.Length;
|
|
pNameAddr->FQDN.MaximumLength = pNameAddr->FQDN.Length + sizeof(WCHAR);
|
|
CTEMemCopy(pNameAddr->FQDN.Buffer,
|
|
pTracker->pNetbiosUnicodeEX->RemoteName.Buffer,
|
|
pNameAddr->FQDN.Length
|
|
);
|
|
pNameAddr->FQDN.Buffer[pNameAddr->FQDN.Length/sizeof(WCHAR)] = L'\0';
|
|
}
|
|
}
|
|
|
|
// keep track of the other end's ip address
|
|
pConnEle->pLowerConnId->SrcIpAddr = htonl(IpAddress);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.SessionSetupContinue: Setting Up Session(after Query) to %16.16s <%X>, %p\n",
|
|
pNameAddr->Name,pNameAddr->Name[15],
|
|
pTracker->pConnEle));
|
|
|
|
ASSERT(pNameAddr->RefCount >= 2);
|
|
//
|
|
// increment pNameAddr once more since we may need to access
|
|
// it below for RAS sessions
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_AUTODIAL);
|
|
|
|
pDeviceContext = pTracker->pDeviceContext;
|
|
pTracker->RemoteIpAddress = IpAddress;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
// start the session...
|
|
status = TcpSessionStart (pTracker,
|
|
IpAddress,
|
|
(tDEVICECONTEXT *)pTracker->pDeviceContext,
|
|
SessionStartupContinue,
|
|
pTracker->DestPort);
|
|
|
|
//
|
|
// the only failure that could occur is if the pLowerConn
|
|
// got separated from pConnEle, in which case some other
|
|
// part of the code has disconnected and cleanedup, so
|
|
// just return
|
|
//
|
|
|
|
#ifdef RASAUTODIAL
|
|
//
|
|
// Notify the automatic connection driver
|
|
// of the successful connection.
|
|
//
|
|
if (fAcdLoadedG && NT_SUCCESS(status))
|
|
{
|
|
CTELockHandle adirql;
|
|
BOOLEAN fEnabled;
|
|
|
|
CTEGetLock(&AcdDriverG.SpinLock, &adirql);
|
|
fEnabled = AcdDriverG.fEnabled;
|
|
CTEFreeLock(&AcdDriverG.SpinLock, adirql);
|
|
if (fEnabled)
|
|
{
|
|
NbtNoteNewConnection(pNameAddr, pDeviceContext->IpAddress);
|
|
}
|
|
}
|
|
#endif // RASAUTODIAL
|
|
|
|
//
|
|
// pNameAddr was referenced above for RAS, so deref it now!
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_AUTODIAL, FALSE);
|
|
return;
|
|
}
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
if (pNameAddr) {
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
}
|
|
|
|
pClientIrp = pConnEle->pIrp;
|
|
pConnEle->pIrp = NULL;
|
|
|
|
if (STATUS_REMOTE_NOT_LISTENING != status) // set in ExtractServerNameCompletion
|
|
{
|
|
status = STATUS_HOST_UNREACHABLE;
|
|
}
|
|
|
|
CleanUpPartialConnection(status, pConnEle, pTracker, pClientIrp, OldIrq1, OldIrq);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
QueueCleanup(
|
|
IN tCONNECTELE *pConnEle,
|
|
IN CTELockHandle *pOldIrqJointLock,
|
|
IN CTELockHandle *pOldIrqConnEle
|
|
)
|
|
/*++
|
|
Routine Description
|
|
|
|
This routine handles Queuing a request to a worker thread to cleanup
|
|
a connection(which basically closes the connection).
|
|
|
|
This routine is called with the JointLock + ConnEle locks held
|
|
and returns with them held
|
|
|
|
Arguments:
|
|
|
|
pConnEle - ptr to the upper connection
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
ULONG State;
|
|
BOOLEAN DerefConnEle;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tDEVICECONTEXT *pDeviceContext = NULL;
|
|
|
|
// to coordinate with RejectSession in hndlrs.c we are holding the spin lock
|
|
// so we don't disconnect twice.
|
|
//
|
|
if ((pLowerConn = pConnEle->pLowerConnId) &&
|
|
(pLowerConn->Verify == NBT_VERIFY_LOWERCONN) &&
|
|
(pLowerConn->State > NBT_IDLE) &&
|
|
(pLowerConn->State < NBT_DISCONNECTING))
|
|
{
|
|
|
|
CTESpinLock(pLowerConn,OldIrq);
|
|
|
|
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
|
if (pLowerConn->Verify != NBT_VERIFY_LOWERCONN)
|
|
{
|
|
//
|
|
// The lower connection block has already been cleaned up
|
|
// or is waiting to be cleaned up, so just return!
|
|
//
|
|
// MALAM_FIX: Fix this so that we don't have to dereference the LowerConn to find this out.
|
|
// One scenario where this happens is if the device gets destroyed in DelayedNbtDeleteDevice
|
|
// and we end up dereferencing the lowerconn which causes it to get deleted!
|
|
// ASSERT(0);
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
|
|
return;
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.QueueCleanup: State=%X, Lower=%X Upper=%X\n",
|
|
pLowerConn->State, pLowerConn,pLowerConn->pUpperConnection));
|
|
|
|
CHECK_PTR(pLowerConn);
|
|
State = pLowerConn->State;
|
|
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTING);
|
|
|
|
if (pConnEle->state != NBT_IDLE)
|
|
{
|
|
SET_STATE_UPPER (pConnEle, NBT_DISCONNECTED);
|
|
}
|
|
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
|
|
//
|
|
// need to increment the ref count for DelayedCleanupAfterDisconnect to
|
|
// work correctly since it assumes the connection got fully connected
|
|
// Note: if this routine is called AFTER the connection is fully
|
|
// connected such as in SessionStartupTimeout, then RefCount must
|
|
// be decremented there to account for this increment.
|
|
//
|
|
if (State < NBT_SESSION_OUTBOUND)
|
|
{
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED);
|
|
}
|
|
|
|
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
|
ASSERT (pLowerConn->RefCount > 1);
|
|
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
CTESpinFree(pConnEle,*pOldIrqConnEle);
|
|
|
|
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,*pOldIrqJointLock);
|
|
|
|
//
|
|
// when the lower no longer points to the upper undo the reference
|
|
// done in NbtConnect, or InBound.
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,*pOldIrqJointLock);
|
|
CTESpinLock(pConnEle,*pOldIrqConnEle);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
NTSTATUS
|
|
StartSessionTimer(
|
|
tDGRAM_SEND_TRACKING *pTracker,
|
|
tCONNECTELE *pConnEle
|
|
)
|
|
|
|
/*++
|
|
Routine Description
|
|
|
|
This routine handles setting up a timer to time the connection setup.
|
|
JointLock Spin Lock is held before calling this routine.
|
|
|
|
Arguments:
|
|
|
|
pConnEle - ptr to the connection structure
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG Timeout = 0;
|
|
CTELockHandle OldIrq;
|
|
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
if (pTracker->pTimeout)
|
|
{
|
|
CTEGetTimeout(pTracker->pTimeout,&Timeout);
|
|
}
|
|
|
|
// now start a timer to time the return of the session setup
|
|
// message
|
|
//
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.StartSessionTimer: TimeOut = %X\n",Timeout));
|
|
|
|
if (Timeout < NBT_SESSION_RETRY_TIMEOUT)
|
|
{
|
|
Timeout = NBT_SESSION_RETRY_TIMEOUT;
|
|
}
|
|
status = StartTimer(SessionStartupTimeout,
|
|
Timeout,
|
|
(PVOID)pTracker, // context value
|
|
NULL, // context2 value
|
|
pTracker,
|
|
SessionStartupTimeoutCompletion,
|
|
pConnEle->pDeviceContext,
|
|
&pTracker->pTimer,
|
|
0,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// we failed to get a timer, but the timer is only used
|
|
// to handle the destination not responding to it is
|
|
// not critical to get a timer... so carry on
|
|
//
|
|
CHECK_PTR(pTracker);
|
|
pTracker->pTimer = NULL;
|
|
}
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
SessionStartupContinue(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status,
|
|
IN ULONG lInfo)
|
|
/*++
|
|
Routine Description
|
|
|
|
This routine handles sending the session request PDU after the TCP
|
|
connection has been setup to the destination IP address.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
tCONNECTELE *pConnEle;
|
|
ULONG lSentLength;
|
|
TDI_REQUEST TdiRequest;
|
|
PIRP pClientIrp;
|
|
PIRP pIrpDisc = NULL;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
BOOLEAN fNameReferenced = TRUE; // In FindNameOrQuery or SessionSetupContinue
|
|
tNAMEADDR *pNameAddr;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
pConnEle = (tCONNECTELE *)pTracker->pConnEle;
|
|
pDeviceContext = pTracker->pDeviceContext;
|
|
|
|
ASSERT (pTracker->Verify == NBT_VERIFY_TRACKER);
|
|
CHECK_PTR (pConnEle);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status!", pTracker, status));
|
|
|
|
if (pTracker->Flags & TRACKER_CANCELLED)
|
|
{
|
|
status = STATUS_CANCELLED;
|
|
pIrpDisc = pConnEle->pIrpDisc; // Complete the Disconnect Irp that is pending too
|
|
}
|
|
|
|
#ifdef MULTIPLE_WINS
|
|
//
|
|
// If we failed to establish a connection and we still have
|
|
// not finished querying all the Name Servers, then continue
|
|
// the Query process
|
|
//
|
|
if (NbtConfig.TryAllNameServers &&
|
|
#ifdef _NETBIOSLESS
|
|
(!IsDeviceNetbiosless(pDeviceContext)) &&
|
|
#endif
|
|
(pConnEle->pLowerConnId) &&
|
|
(status != STATUS_CANCELLED) &&
|
|
(!NT_SUCCESS(status)) &&
|
|
(pTracker->ResolutionContextFlags != NAME_RESOLUTION_DONE))
|
|
{
|
|
SET_STATE_LOWER (pConnEle->pLowerConnId, NBT_ASSOCIATED);
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
//
|
|
// See if we can get another IP address and re-try!
|
|
//
|
|
if (STATUS_PENDING == ContinueQueryNameOnNet (pTracker,
|
|
pTracker->pConnEle->RemoteName,
|
|
pDeviceContext,
|
|
SessionSetupContinue,
|
|
&fNameReferenced))
|
|
{
|
|
// i.e. pending was returned
|
|
return;
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status!", pTracker, status));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Set the pBuffer ptr = NULL so that we don't try to
|
|
// set it as the Mdl->Next ptr in TdiSend!
|
|
//
|
|
if (pTracker->SendBuffer.pBuffer)
|
|
{
|
|
pTracker->SendBuffer.pBuffer = NULL;
|
|
}
|
|
|
|
pLowerConn = pConnEle->pLowerConnId;
|
|
if ((NT_SUCCESS(status)) &&
|
|
(!pLowerConn))
|
|
{
|
|
// in case the connection got disconnected during the setup phase,
|
|
// check the lower conn value
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// NbtDisconnect can cancel the tracker if a disconnect comes in during
|
|
// the connecting phase.
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
#ifdef _NETBIOSLESS
|
|
// *****************************************************************
|
|
//
|
|
// Skip session setup for message only mode
|
|
//
|
|
if (IsDeviceNetbiosless(pDeviceContext))
|
|
{
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.SessionStartupContinue: skipping session setup\n"));
|
|
|
|
// Here is where we fake out the data structures to move to the SESSION_UP state
|
|
// We enter holding jointLock and pConnEle lock
|
|
|
|
// zero out the number of bytes received so far, since this is a new connection
|
|
pConnEle->BytesRcvd = 0;
|
|
pConnEle->pIrpRcv = NULL;
|
|
pClientIrp = pConnEle->pIrp;
|
|
pConnEle->pIrp = NULL;
|
|
SET_STATE_UPPER (pConnEle, NBT_SESSION_UP);
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
|
|
if (fNameReferenced)
|
|
{
|
|
//
|
|
// remove the reference done when FindNameOrQuery was called, or when
|
|
// SessionSetupContinue ran
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
}
|
|
|
|
//
|
|
// Increment the reference count on a connection while it is connected
|
|
// so that it cannot be deleted until it disconnects.
|
|
//
|
|
CTESpinLock(pLowerConn,OldIrq);
|
|
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED);
|
|
ASSERT(pLowerConn->RefCount == 2);
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_UP);
|
|
SET_STATERCV_LOWER(pLowerConn, NORMAL, Normal);
|
|
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
|
|
// remove the reference added in nbt connect
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
|
|
// NOTE: the last reference done on pConnEle in NbtConnect is NOT undone
|
|
// until the pLowerConn no longer points to pConnEle!!
|
|
|
|
// 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 (pClientIrp)
|
|
{
|
|
//
|
|
// complete the client's connect request Irp
|
|
//
|
|
#ifndef VXD
|
|
CTEIoComplete (pClientIrp, STATUS_SUCCESS, 0 ) ;
|
|
#else
|
|
CTEIoComplete (pClientIrp, STATUS_SUCCESS, (ULONG)pConnEle ) ;
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
// *****************************************************************
|
|
#endif // _NETBIOSLESS
|
|
|
|
// set the session state to NBT_SESSION_OUTBOUND
|
|
//
|
|
SET_STATE_UPPER (pConnEle, NBT_SESSION_OUTBOUND);
|
|
|
|
//
|
|
// Increment the reference count on a connection while it is connected
|
|
// so that it cannot be deleted until it disconnects.
|
|
//
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED);
|
|
ASSERT(pLowerConn->RefCount == 2);
|
|
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_OUTBOUND);
|
|
SET_STATERCV_LOWER (pLowerConn, NORMAL, Outbound);
|
|
|
|
// we need to pass the file handle of the connection to TCP.
|
|
TdiRequest.Handle.AddressHandle = pLowerConn->pFileObject;
|
|
|
|
// the completion routine is setup to free the pTracker memory block
|
|
TdiRequest.RequestNotifyObject = SessionStartupCompletion;
|
|
TdiRequest.RequestContext = (PVOID)pTracker;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
|
|
//
|
|
// failure to get a timer causes the connection setup to fail
|
|
//
|
|
status = StartSessionTimer(pTracker,pConnEle);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
status = NbtSetCancelRoutine(pConnEle->pIrp, NbtCancelSession, pDeviceContext);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// We have closed down the connection by failing the call to
|
|
// setup up the cancel routine - it ended up calling the
|
|
// cancel routine.
|
|
//
|
|
//
|
|
// remove the second reference added in nbtconnect
|
|
//
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status!", pTracker, status));
|
|
|
|
if (pTracker->RefConn == 0) {
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_CONNECT, FALSE);
|
|
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
|
} else {
|
|
pTracker->RefConn--;
|
|
}
|
|
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
return;
|
|
}
|
|
|
|
// the only data sent is the session request buffer which is in the pSendinfo
|
|
// structure.
|
|
status = TdiSend (&TdiRequest,
|
|
0, // send flags are not set
|
|
pTracker->SendBuffer.HdrLength,
|
|
&lSentLength,
|
|
&pTracker->SendBuffer,
|
|
0);
|
|
|
|
//
|
|
// the completion routine will get called with the errors and
|
|
// handle them appropriately, so just return here
|
|
//
|
|
return;
|
|
} else {
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status!", pTracker, status));
|
|
}
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
}
|
|
else
|
|
{
|
|
// if the remote station does not have a connection to receive the
|
|
// session pdu on , then we will get back this status. We may also
|
|
// get this if the destination does not have NBT running at all. This
|
|
// is a short timeout - 250 milliseconds, times 3.
|
|
//
|
|
}
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status!", pTracker, status));
|
|
|
|
//
|
|
// this branch is taken if the TCP connection setup fails or the
|
|
// tracker has been cancelled.
|
|
//
|
|
|
|
CHECK_PTR(pConnEle);
|
|
|
|
pClientIrp = pConnEle->pIrp;
|
|
pConnEle->pIrp = NULL;
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.SessionStartupContinue: Failed, State=%X,TrackerFlags=%X pConnEle=%X\n",
|
|
pConnEle->state, pTracker->Flags, pConnEle));
|
|
|
|
//
|
|
// remove the name from the hash table since we did not connect
|
|
// (only if the request was not cancelled)!
|
|
//
|
|
//
|
|
// if it is in the remote table and still active...
|
|
// and no one else is referencing the name, then delete it from
|
|
// the hash table.
|
|
//
|
|
if (fNameReferenced)
|
|
{
|
|
if ((status != STATUS_CANCELLED) &&
|
|
(pTracker->pNameAddr->Verify == REMOTE_NAME) &&
|
|
(pTracker->pNameAddr->NameTypeState & STATE_RESOLVED) &&
|
|
(pTracker->pNameAddr->RefCount == 2))
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_REMOTE, TRUE);
|
|
}
|
|
//
|
|
// remove the reference done when FindNameOrQuery was called, or when
|
|
// SessionSetupContinue ran
|
|
//
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_CONNECT, TRUE);
|
|
}
|
|
|
|
// Either the connection failed to get setup or the send on the
|
|
// connection failed, either way, don't mess with disconnects, just
|
|
// close the connection... If the Tracker was cancelled then it means
|
|
// someother part of the code has done the disconnect already.
|
|
//
|
|
FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR);
|
|
|
|
if (pConnEle->state != NBT_IDLE)
|
|
{
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
}
|
|
|
|
// Cache the fact that an attempt to set up a TDI connection failed. This will enable us to
|
|
// weed out repeated attempts on the same remote address. The only case that is exempt is a
|
|
// NETBIOS name which we let it pass through because it adopts a different name resolution
|
|
// mechanism.
|
|
|
|
#ifndef VXD
|
|
if (pConnEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX)
|
|
{
|
|
IF_DBG(NBT_DEBUG_NETBIOS_EX)
|
|
KdPrint(("Nbt.SessionStartupContinue: Will avoid repeated attempts on a nonexistent address\n"));
|
|
pConnEle->RemoteNameDoesNotExistInDNS = TRUE;
|
|
}
|
|
|
|
if (status == STATUS_IO_TIMEOUT)
|
|
{
|
|
status = STATUS_HOST_UNREACHABLE;
|
|
}
|
|
else if (status == STATUS_CONNECTION_REFUSED)
|
|
{
|
|
if (IsDeviceNetbiosless(pDeviceContext))
|
|
{
|
|
status = STATUS_REMOTE_NOT_LISTENING;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
}
|
|
#else
|
|
if (status == TDI_CONN_REFUSED || status == TDI_TIMED_OUT)
|
|
{
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
#endif
|
|
|
|
QueueCleanup (pConnEle, &OldIrq1, &OldIrq);
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
|
|
|
|
//
|
|
// put back on the idle connection list if nbtcleanupconnection has not
|
|
// run and taken pconnele off the list (setting the state to Idle).
|
|
//
|
|
if (pConnEle->state != NBT_IDLE)
|
|
{
|
|
RelistConnection(pConnEle);
|
|
}
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
//
|
|
// remove the last reference added in nbt connect. The refcount will be 2
|
|
// if nbtcleanupconnection has not run and 1, if it has. So this call
|
|
// could free pConnEle.
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
|
|
// the cancel irp routine in Ntisol.c sets the irp to NULL if it cancels
|
|
// it.
|
|
if (pClientIrp)
|
|
{
|
|
CTEIoComplete(pClientIrp,status,0L);
|
|
}
|
|
|
|
if (pIrpDisc)
|
|
{
|
|
CTEIoComplete(pIrpDisc,STATUS_SUCCESS,0L);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
SessionStartupCompletion(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status,
|
|
IN ULONG lInfo)
|
|
/*++
|
|
Routine Description
|
|
|
|
This routine handles the completion of sending the session request pdu.
|
|
It completes the Irp back to the client indicating the outcome of the
|
|
transaction if there is an error otherwise it keeps the irp till the
|
|
session setup response is heard.
|
|
Tracker block is put back on its free Q and the
|
|
session header is freed back to the non-paged pool.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
CTELockHandle OldIrq;
|
|
tCONNECTELE *pConnEle;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
COMPLETIONCLIENT CompletionRoutine = NULL;
|
|
ULONG state;
|
|
PCTE_IRP pClientIrp;
|
|
PCTE_IRP pIrpDisc;
|
|
tTIMERQENTRY *pTimer;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
pConnEle = (tCONNECTELE *)pTracker->pConnEle;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
pLowerConn = pConnEle->pLowerConnId;
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status! pConnEle %p pLowerConn %p",
|
|
pTracker, status, pConnEle, pLowerConn));
|
|
|
|
//
|
|
// if OutBound or the SessionStartupTimeoutCompletion have run,
|
|
// they have set the refcount to zero, so just cleanup!
|
|
//
|
|
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);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
else
|
|
{
|
|
pTracker->RefConn--;
|
|
|
|
//
|
|
// a failure status means that the transport could not send the
|
|
// session startup pdu - if this happens, then disconnect the
|
|
// connection and return the client's irp with the status code
|
|
//
|
|
if ((!NT_SUCCESS(status)))
|
|
{
|
|
// we must check the status first since it is possible that the
|
|
// lower connection has disappeared already due to a disconnect/cleanup
|
|
// in the VXD case anyway. Only for a bad status can we be sure
|
|
// that pConnEle is still valid.
|
|
//
|
|
CHECK_PTR(pTracker);
|
|
if (pTimer = pTracker->pTimer)
|
|
{
|
|
pTracker->pTimer = NULL;
|
|
StopTimer(pTimer,&CompletionRoutine,NULL);
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.SessionStartupCompletion: Failed, State=%X,TrackerFlags=%X CompletionRoutine=%X,pConnEle=%X\n",
|
|
pConnEle->state, pTracker->Flags, CompletionRoutine, pConnEle));
|
|
|
|
//
|
|
// Only if the timer has not expired yet do we kill off the connection
|
|
// since if the timer has expired, it has already done this in
|
|
// SessionStartupTimeout.
|
|
//
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (CompletionRoutine)
|
|
{
|
|
(*CompletionRoutine) (pTracker, status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
}
|
|
|
|
//
|
|
// remove the last reference added in nbt connect. The refcount
|
|
// will be 2 if nbtcleanupconnection has not run and 1, if it has. So this call
|
|
// could free pConnEle.
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
SessionStartupTimeout(
|
|
PVOID pContext,
|
|
PVOID pContext2,
|
|
tTIMERQENTRY *pTimerQEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles timing out a connection setup request. The timer
|
|
is started when the connection is started and the session setup
|
|
message is about to be sent.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
|
|
// if pTimerQEntry is null then the timer is being cancelled, so do nothing
|
|
if (!pTimerQEntry)
|
|
{
|
|
pTracker->pTimer = NULL;
|
|
return;
|
|
}
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: SessionStartupTimeout", pTracker));
|
|
|
|
SessionStartupTimeoutCompletion (pTracker, STATUS_IO_TIMEOUT);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
SessionStartupTimeoutCompletion(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN NTSTATUS status
|
|
)
|
|
{
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
tCONNECTELE *pConnEle;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
CTE_IRP *pIrp;
|
|
enum eSTATE State;
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status!", pTracker, status));
|
|
|
|
// kill the connection
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
pTracker->pTimer = NULL;
|
|
|
|
if (!(pConnEle = pTracker->pConnEle) ||
|
|
!(pLowerConn = pConnEle->pLowerConnId) ||
|
|
!(pTracker == (tDGRAM_SEND_TRACKING *) pConnEle->pIrpRcv))
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return;
|
|
}
|
|
|
|
NbtTrace(NBT_TRACE_OUTBOUND, ("pTracker %p: %!status! pConnEle %p pLowerConn %p",
|
|
pTracker, status, pConnEle, pLowerConn));
|
|
|
|
CHECK_PTR(pConnEle);
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.SessionStartupTimeout: pConnEle=<%x>-->State=<%x>\n", pConnEle, pConnEle->state));
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.SessionStartupTimeout: pLowerConn=<%x>-->State=<%x>, TrackerFlags=<%x>\n",
|
|
pLowerConn, pLowerConn->State, pTracker->Flags));
|
|
|
|
CTESpinLock(pConnEle,OldIrq2);
|
|
CTESpinLock(pLowerConn,OldIrq1);
|
|
|
|
if ((pConnEle->state != NBT_SESSION_OUTBOUND) ||
|
|
(!(pIrp = pConnEle->pIrp)))
|
|
{
|
|
CTESpinFree(pLowerConn,OldIrq1);
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return;
|
|
}
|
|
|
|
pConnEle->pIrp = NULL;
|
|
|
|
State = pConnEle->state;
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
NBT_REFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION_TIMEOUT);
|
|
pLowerConn->pUpperConnection = NULL; // So that any response for this in Outbound does not succeed
|
|
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
|
|
|
CTESpinFree(pLowerConn,OldIrq1);
|
|
|
|
QueueCleanup (pConnEle, &OldIrq, &OldIrq2);
|
|
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
|
|
//
|
|
// Nbt_idle means that nbtcleanupConnection has run and the
|
|
// connection is about to be deleted, so don't relist.
|
|
//
|
|
if (State != NBT_IDLE)
|
|
{
|
|
RelistConnection(pConnEle);
|
|
}
|
|
|
|
//
|
|
// if SessionStartupCompletion has run, it has set the refcount to zero
|
|
//
|
|
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);
|
|
}
|
|
|
|
//
|
|
// 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--;
|
|
}
|
|
|
|
//
|
|
// remove the reference done when FindNameOrQuery was called, or when
|
|
// SessionSetupContinue ran
|
|
//
|
|
pConnEle->pIrpRcv = NULL; // So that SessionStartupCompletion does not also try to cleanup!
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_SESSION_TIMEOUT);
|
|
|
|
CTEIoComplete(pIrp, status, 0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
RelistConnection(
|
|
IN tCONNECTELE *pConnEle
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine unlinks the ConnEle from the ConnectActive list and puts it
|
|
back on the Connecthead. It is used when a connection goes to
|
|
NBT_ASSOCIATED state.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
tCLIENTELE *pClientEle = pConnEle->pClientEle;
|
|
|
|
//
|
|
// If pClientEle is NULL, it means the client was most probably
|
|
// cleaned up, and the connection should now be on the Device's
|
|
// UpConnectionInUse list
|
|
//
|
|
ASSERT (NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN));
|
|
if (pClientEle)
|
|
{
|
|
CTESpinLock(pClientEle,OldIrq);
|
|
CTESpinLock(pConnEle,OldIrq1);
|
|
ASSERT (NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN));
|
|
//
|
|
// if the state is NBT_IDLE it means that NbtCleanupConnection has run
|
|
// and removed the connection from its list in preparation for
|
|
// freeing the memory, so don't put it back on the list
|
|
//
|
|
if (pConnEle->state != NBT_IDLE)
|
|
{
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
RemoveEntryList(&pConnEle->Linkage);
|
|
InsertTailList(&pConnEle->pClientEle->ConnectHead,&pConnEle->Linkage);
|
|
}
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTESpinFree(pClientEle,OldIrq);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtSend(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN USHORT Flags,
|
|
IN ULONG SendLength,
|
|
OUT LONG *pSentLength,
|
|
IN PVOID *pBuffer,
|
|
IN tDEVICECONTEXT *pContext,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
... does nothing now....
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// This routine is never hit since the NTISOL.C routine NTSEND actually
|
|
// bypasses this code and passes the send directly to the transport
|
|
//
|
|
ASSERT(0);
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtListen(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN ULONG Flags,
|
|
IN TDI_CONNECTION_INFORMATION *pRequestConnectInfo,
|
|
OUT TDI_CONNECTION_INFORMATION *pReturnConnectInfo,
|
|
IN PVOID pIrp)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine posts a listen on an open connection allowing a client to
|
|
indicate that is prepared to accept inbound connections. The ConnectInfo
|
|
may contain an address to specify which remote clients may connect to
|
|
the connection although we don't currently look at that info.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
ReturnConnectInfo - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
tCLIENTELE *pClientEle;
|
|
tCONNECTELE *pConnEle;
|
|
NTSTATUS status;
|
|
tLISTENREQUESTS *pListenReq;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
|
|
pListenReq = NbtAllocMem(sizeof(tLISTENREQUESTS),NBT_TAG('I'));
|
|
if (!pListenReq)
|
|
{
|
|
NbtTrace(NBT_TRACE_INBOUND, ("Out of memory"));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
// now find the connection object to link this listen record to
|
|
pConnEle = ((tCONNECTELE *)pRequest->Handle.ConnectionContext);
|
|
|
|
//
|
|
// Find the client record associated with this connection
|
|
//
|
|
if ((!NBT_VERIFY_HANDLE (pConnEle, NBT_VERIFY_CONNECTION)) || // NBT_VERIFY_CONNECTION_DOWN if cleaned up
|
|
(!NBT_VERIFY_HANDLE ((pClientEle = pConnEle->pClientEle), NBT_VERIFY_CLIENT)))
|
|
{
|
|
CTEMemFree(pListenReq);
|
|
NbtTrace(NBT_TRACE_INBOUND, ("Invalid Handle pConnEle<%p> pClientEle<%p>", pConnEle, pClientEle));
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
CTESpinLock(pClientEle,OldIrq);
|
|
CTESpinLock(pConnEle,OldIrq1);
|
|
|
|
//
|
|
// Now reverify the Client and Connection handles, and ensure the Connection state is correct!
|
|
//
|
|
if ((!NBT_VERIFY_HANDLE (pConnEle, NBT_VERIFY_CONNECTION)) ||
|
|
(!NBT_VERIFY_HANDLE (pClientEle, NBT_VERIFY_CLIENT)) ||
|
|
(pConnEle->state != NBT_ASSOCIATED))
|
|
{
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTESpinFree(pClientEle,OldIrq);
|
|
CTEMemFree(pListenReq);
|
|
NbtTrace(NBT_TRACE_INBOUND, ("Invalid state %x", pConnEle->state));
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
//
|
|
// Fill in the Listen request
|
|
//
|
|
|
|
pListenReq->pIrp = pIrp;
|
|
pListenReq->Flags = Flags;
|
|
pListenReq->pConnectEle = pConnEle;
|
|
pListenReq->pConnInfo = pRequestConnectInfo;
|
|
pListenReq->pReturnConnInfo = pReturnConnectInfo;
|
|
pListenReq->CompletionRoutine = pRequest->RequestNotifyObject;
|
|
pListenReq->Context = pRequest->RequestContext;
|
|
|
|
// queue the listen request on the client object
|
|
InsertTailList(&pClientEle->ListenHead,&pListenReq->Linkage);
|
|
|
|
status = NTCheckSetCancelRoutine(pIrp,(PVOID)NbtCancelListen,0);
|
|
NbtTrace(NBT_TRACE_INBOUND, ("NTCheckSetCancelRoutine return %!status! for %!NBTNAME!<%02x>",
|
|
status, pClientEle->pAddress->pNameAddr->Name,
|
|
(unsigned)pClientEle->pAddress->pNameAddr->Name[15]));
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
RemoveEntryList(&pListenReq->Linkage);
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
CTESpinFree(pConnEle,OldIrq1);
|
|
CTESpinFree(pClientEle,OldIrq);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtDisconnect(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN PVOID pTimeout,
|
|
IN ULONG Flags,
|
|
IN PTDI_CONNECTION_INFORMATION pCallInfo,
|
|
IN PTDI_CONNECTION_INFORMATION pReturnInfo,
|
|
IN PIRP pIrp)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles taking down a connection (netbios session).
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
tCONNECTELE *pConnEle;
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq2;
|
|
CTELockHandle OldIrq3;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
ULONG LowerState = NBT_IDLE;
|
|
ULONG StateRcv;
|
|
BOOLEAN Originator = TRUE;
|
|
PCTE_IRP pClientIrp = NULL;
|
|
BOOLEAN RelistIt = FALSE;
|
|
BOOLEAN Wait;
|
|
PCTE_IRP pIrpDisc;
|
|
|
|
pConnEle = pRequest->Handle.ConnectionContext;
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDisconnect: state %X %X\n",pConnEle->state,pConnEle));
|
|
|
|
// check the connection element for validity
|
|
//CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status)
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if ((pConnEle->state <= NBT_ASSOCIATED) ||
|
|
(pConnEle->state >= NBT_DISCONNECTING))
|
|
{
|
|
// the connection is not connected so reject the disconnect attempt
|
|
// ( with an Invalid Connection return code ) - unless there is a
|
|
// value stored in the flag
|
|
// DiscFlag field which will be the status of a previous
|
|
// disconnect indication from the transport.
|
|
//
|
|
if ((pConnEle->DiscFlag))
|
|
{
|
|
if (Flags == TDI_DISCONNECT_WAIT)
|
|
{
|
|
if (pConnEle->DiscFlag == TDI_DISCONNECT_ABORT)
|
|
{
|
|
status = STATUS_CONNECTION_RESET;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_GRACEFUL_DISCONNECT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
// clear the flag now.
|
|
CHECK_PTR(pConnEle);
|
|
pConnEle->DiscFlag = 0;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_CONNECTION_INVALID;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return(status);
|
|
}
|
|
|
|
// to link and unlink upper and lower connections the Joint lock must
|
|
// be held. This allows coordination from the lower side and from
|
|
// the upper side. - i.e. once the joint lock is held, the upper and lower
|
|
// connections cannot become unlinked.
|
|
//
|
|
CTESpinLock(pConnEle,OldIrq2);
|
|
|
|
// Do this check with the spin lock held to avoid a race condition
|
|
// with a disconnect coming in from the transport at the same time.
|
|
//
|
|
|
|
pLowerConn = pConnEle->pLowerConnId;
|
|
|
|
//
|
|
// a disconnect wait is not really a disconnect, it is just there so that
|
|
// when a disconnect occurs, the transport will complete it, and indicate
|
|
// to the client there is a disconnect (instead of having a disconnect
|
|
// indication handler) - therefore, for Disc Wait, do NOT change state.
|
|
//
|
|
CHECK_PTR(pConnEle);
|
|
pIrpDisc = pConnEle->pIrpDisc;
|
|
pConnEle->pIrpDisc = NULL;
|
|
if (Flags == TDI_DISCONNECT_WAIT)
|
|
{
|
|
|
|
//
|
|
// save the Irp here and wait for a disconnect to return it
|
|
// to the client.
|
|
//
|
|
if ((pConnEle->state == NBT_SESSION_UP) &&
|
|
(!pConnEle->pIrpClose))
|
|
{
|
|
pConnEle->pIrpClose = pIrp;
|
|
status = STATUS_PENDING;
|
|
|
|
//
|
|
// call this routine to check if the cancel flag has been
|
|
// already set and therefore we must return the irp now
|
|
//
|
|
status = NbtSetCancelRoutine(pIrp, NbtCancelDisconnectWait,pLowerConn->pDeviceContext);
|
|
//
|
|
// change the ret status so if the irp has been cancelled,
|
|
// driver.c will not also return it, since NbtSetCancelRoutine
|
|
// will call the cancel routine and return the irp.
|
|
//
|
|
status = STATUS_PENDING;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_CONNECTION_INVALID;
|
|
}
|
|
|
|
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return(status);
|
|
}
|
|
else
|
|
{
|
|
if (pLowerConn)
|
|
{
|
|
if (pConnEle->state > NBT_ASSOCIATED)
|
|
{
|
|
ULONG state = pConnEle->state;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pConnEle->pIrpRcv;
|
|
|
|
switch (state)
|
|
{
|
|
case NBT_RECONNECTING:
|
|
{
|
|
//
|
|
// the connection is waiting on the Exworker Q to run
|
|
// nbtreconnect. When that runs the connect irp is
|
|
// returned.
|
|
//
|
|
pTracker->Flags |= TRACKER_CANCELLED;
|
|
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
FreeRcvBuffers(pConnEle,&OldIrq);
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
case NBT_SESSION_OUTBOUND:
|
|
{
|
|
tTIMERQENTRY *pTimerEntry;
|
|
|
|
LOCATION(0x66)
|
|
if (pTimerEntry = pTracker->pTimer)
|
|
{
|
|
COMPLETIONCLIENT ClientRoutine;
|
|
PVOID pContext;
|
|
|
|
//
|
|
// the Session Setup Message has been sent
|
|
// so stop the SessionSetup Timer.
|
|
//
|
|
LOCATION(0x67)
|
|
CHECK_PTR(pTracker);
|
|
pTracker->pTimer = NULL;
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
|
|
StopTimer(pTimerEntry,&ClientRoutine,&pContext);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
if (ClientRoutine)
|
|
{
|
|
(* ClientRoutine) (pContext, STATUS_CANCELLED);
|
|
}
|
|
// else...
|
|
// the timer has completed and called QueueCleanup
|
|
// so all we need to do is return here.
|
|
}
|
|
else
|
|
{
|
|
ASSERTMSG("Nbt:In outbound state, but no timer.../n",0);
|
|
pTracker->Flags |= TRACKER_CANCELLED;
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
case NBT_CONNECTING:
|
|
{
|
|
//
|
|
// This searchs for timers outstanding on name queries
|
|
// and name queries held up on Lmhosts or Dns Qs
|
|
//
|
|
LOCATION(0x69)
|
|
status = CleanupConnectingState(pConnEle,pLowerConn->pDeviceContext,&OldIrq2,&OldIrq);
|
|
if (status == STATUS_UNSUCCESSFUL)
|
|
{
|
|
LOCATION(0x6A)
|
|
//
|
|
// set this flag to tell sessionsetupcontinue or
|
|
// SessionStartupContinue not to process
|
|
// anything, except to free the tracker
|
|
//
|
|
pTracker->Flags = TRACKER_CANCELLED;
|
|
|
|
//
|
|
// failed to cancel the name query so do not deref
|
|
// pConnEle yet.
|
|
//
|
|
//
|
|
// hold on to disconnect irp here - till name query is done
|
|
// then complete both the connect and disconnect irp
|
|
//
|
|
if (pIrpDisc)
|
|
{
|
|
status = STATUS_CONNECTION_INVALID;
|
|
}
|
|
else
|
|
{
|
|
pConnEle->pIrpDisc = pIrp;
|
|
}
|
|
|
|
status = STATUS_PENDING;
|
|
ASSERT (NULL != pIrp);
|
|
}
|
|
else if (status == STATUS_PENDING)
|
|
{
|
|
LOCATION(0x6B)
|
|
// the connection is being setup with the transport
|
|
// so disconnect below
|
|
//
|
|
|
|
pTracker->Flags = TRACKER_CANCELLED;
|
|
//
|
|
// DelayedCleanupAfterDisconnect expects this ref count
|
|
// to be 2, meaning that it got connected, so increment
|
|
// here
|
|
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CONNECTED);
|
|
break;
|
|
}
|
|
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
return(status);
|
|
}
|
|
} // switch
|
|
|
|
CTESpinLock(pLowerConn,OldIrq3);
|
|
|
|
if (pConnEle->state != NBT_SESSION_UP)
|
|
{ //
|
|
// do an abortive disconnect to be sure it completes now.
|
|
//
|
|
Flags = TDI_DISCONNECT_ABORT;
|
|
}
|
|
|
|
LOCATION(0x6C)
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDisconnect: LowerConn,state %X,Src %X %X\n",
|
|
pLowerConn->State,pLowerConn->SrcIpAddr,pLowerConn));
|
|
|
|
ASSERT(pConnEle->RefCount > 1);
|
|
|
|
Originator = pLowerConn->bOriginator;
|
|
|
|
//
|
|
// the upper connection is going to be put back on its free
|
|
// list, and the lower one is going to get a Disconnect
|
|
// request, so put the upper back in associated, and separate
|
|
// the upper and lower connections
|
|
//
|
|
SET_STATE_UPPER (pConnEle, NBT_ASSOCIATED);
|
|
CHECK_PTR(pConnEle);
|
|
CHECK_PTR(pLowerConn);
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
|
|
LowerState = pLowerConn->State;
|
|
StateRcv = pLowerConn->StateRcv;
|
|
|
|
//
|
|
// if we had a connection in partial rcv state, make sure to remove it from
|
|
// the list
|
|
//
|
|
#ifdef VXD
|
|
if ((pLowerConn->StateRcv == PARTIAL_RCV) &&
|
|
(pLowerConn->fOnPartialRcvList == TRUE))
|
|
{
|
|
RemoveEntryList (&pLowerConn->PartialRcvList);
|
|
pLowerConn->fOnPartialRcvList = FALSE;
|
|
InitializeListHead(&pLowerConn->PartialRcvList);
|
|
}
|
|
#endif
|
|
|
|
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTING);
|
|
pLowerConn->bDisconnectIrpPendingInTCP = TRUE;
|
|
SetStateProc (pLowerConn, RejectAnyData);
|
|
|
|
if (!pConnEle->pIrpDisc)
|
|
{
|
|
pLowerConn->pIrp = pIrp ;
|
|
}
|
|
|
|
CTESpinFree(pLowerConn,OldIrq3);
|
|
|
|
PUSH_LOCATION(0x84);
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// remove the reference added to pConnEle when pLowerConn pointed
|
|
// to it, since that pointer link was just removed.
|
|
// if the state is not disconnecting...
|
|
//
|
|
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT);
|
|
|
|
RelistIt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
LOCATION(0x6D)
|
|
PUSH_LOCATION(0x83);
|
|
CHECK_PTR(pConnEle);
|
|
CHECK_PTR(pLowerConn);
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
StateRcv = NORMAL;
|
|
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
//
|
|
// check for any RcvIrp that may be still around
|
|
//
|
|
CTESpinLock(pLowerConn,OldIrq);
|
|
if (StateRcv == FILL_IRP)
|
|
{
|
|
if (pConnEle->pIrpRcv)
|
|
{
|
|
PCTE_IRP pIrp;
|
|
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.NbtDisconnect: Cancelling RcvIrp on Disconnect!!!\n"));
|
|
pIrp = pConnEle->pIrpRcv;
|
|
CHECK_PTR(pConnEle);
|
|
pConnEle->pIrpRcv = NULL;
|
|
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
#ifndef VXD
|
|
IoCancelIrp(pIrp);
|
|
#else
|
|
CTEIoComplete(pIrp,STATUS_CANCELLED,0);
|
|
#endif
|
|
|
|
CHECK_PTR(pConnEle);
|
|
pConnEle->pIrpRcv = NULL;
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
}
|
|
|
|
//
|
|
// when the disconnect irp is returned we will close the connection
|
|
// to avoid any peculiarities. This also lets the other side
|
|
// know that we did not get all the data.
|
|
//
|
|
Flags = TDI_DISCONNECT_ABORT;
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pLowerConn,OldIrq);
|
|
}
|
|
|
|
//
|
|
// check if there is still data waiting in the transport for this end point
|
|
// and if so do an abortive disconnect to let the other side know that something
|
|
// went wrong
|
|
//
|
|
if (pConnEle->BytesInXport)
|
|
{
|
|
PUSH_LOCATION(0xA0);
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.NbtDisconnect: Doing ABORTIVE disconnect, dataInXport = %X\n",
|
|
pConnEle->BytesInXport));
|
|
Flags = TDI_DISCONNECT_ABORT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(pConnEle,OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
}
|
|
}
|
|
|
|
ASSERT(pConnEle->RefCount > 0);
|
|
|
|
CTESpinLock (pConnEle,OldIrq);
|
|
FreeRcvBuffers (pConnEle,&OldIrq);
|
|
CTESpinFree (pConnEle,OldIrq);
|
|
|
|
if (RelistIt)
|
|
{
|
|
//
|
|
// put the upper connection back on its free list
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
RelistConnection (pConnEle);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
//
|
|
// disconnect (and delete) the lower connection
|
|
//
|
|
// when nbtdisconnect is called from cleanup connection it does not
|
|
// have an irp and it wants a synchronous disconnect, so set wait
|
|
// to true in this case
|
|
//
|
|
if (!pIrp)
|
|
{
|
|
Wait = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Wait = FALSE;
|
|
}
|
|
|
|
status = DisconnectLower(pLowerConn,LowerState,Flags,pTimeout,Wait);
|
|
ASSERT (!Wait || STATUS_PENDING != status);
|
|
|
|
if ((pConnEle->pIrpDisc) &&
|
|
(status != STATUS_INSUFFICIENT_RESOURCES))
|
|
{
|
|
// don't complete the disconnect irp yet if we are holding onto
|
|
// it
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
return(status);
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
DisconnectLower(
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN ULONG state,
|
|
IN ULONG Flags,
|
|
IN PVOID Timeout,
|
|
IN BOOLEAN Wait
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine handles disconnecting the lower half of a connection.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status=STATUS_SUCCESS;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
|
|
if (pLowerConn)
|
|
{
|
|
//
|
|
// no need to disconnect a connection in the connecting state since it
|
|
// hasn't connected yet...i.e. one where the destination refuses to
|
|
// accept the tcp connection.... hmmmm maybe we do need to disconnect
|
|
// a connection in the connecting state, since the transport is
|
|
// actively trying to connect the connection, and we need to stop
|
|
// that activity - so the Upper connection is connecting during
|
|
// name resolution, but the lower one isn't connecting until the
|
|
// tcp connection phase begins.
|
|
//
|
|
if ((state >= NBT_CONNECTING) && (state <= NBT_SESSION_UP))
|
|
{
|
|
//
|
|
// got a cleanup for an active connection, so send a disconnect down
|
|
// to the transport
|
|
//
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DisconnectLower: Waiting for disconnect...\n"));
|
|
|
|
status = GetTracker(&pTracker, NBT_TRACKER_DISCONNECT_LOWER);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
ULONG TimeVal;
|
|
|
|
// this should return status pending and the irp will be completed
|
|
// in DelayedCleanupAfterDisconnect in hndlrs.c
|
|
pTracker->pConnEle = (PVOID)pLowerConn;
|
|
|
|
#if DBG
|
|
if (Timeout)
|
|
{
|
|
TimeVal = ((PTIME)Timeout)->LowTime;
|
|
}
|
|
else
|
|
{
|
|
TimeVal = 0;
|
|
}
|
|
IF_DBG(NBT_DEBUG_DISCONNECT)
|
|
KdPrint(("Nbt.DisconnectLower: Disconnect Timout = %X,Flags=%X\n",
|
|
TimeVal,Flags));
|
|
#endif
|
|
|
|
// in the case where CleanupAddress calls cleanupConnection
|
|
// which calls nbtdisconnect, we do not have an irp to wait
|
|
// on so pass a flag down to TdiDisconnect to do a synchronous
|
|
// disconnect.
|
|
//
|
|
status = TcpDisconnect (pTracker, Timeout, Flags, Wait);
|
|
|
|
#ifndef VXD
|
|
if (Wait)
|
|
{
|
|
// we need to call disconnect done now
|
|
// to free the tracker and cleanup the connection
|
|
//
|
|
DisconnectDone(pTracker,status,0);
|
|
}
|
|
#else
|
|
//
|
|
// if the disconnect is abortive, transport doesn't call us
|
|
// back so let's call DisconnectDone so that the lowerconn gets
|
|
// cleaned up properly! (Wait parm is of no use in vxd)
|
|
//
|
|
if (Flags == TDI_DISCONNECT_ABORT)
|
|
{
|
|
// we need to call disconnect done now
|
|
// to free the tracker and cleanup the connection
|
|
//
|
|
DisconnectDone(pTracker,STATUS_SUCCESS,0);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtAccept(
|
|
TDI_REQUEST *pRequest,
|
|
IN TDI_CONNECTION_INFORMATION *pAcceptInfo,
|
|
OUT TDI_CONNECTION_INFORMATION *pReturnAcceptInfo,
|
|
IN PIRP pIrp)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles accepting an inbound connection by a client.
|
|
The client calls this routine after it has been alerted
|
|
by a Listen completing back to the client.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
tCONNECTELE *pConnectEle;
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
|
|
// get the client object associated with this connection
|
|
pConnectEle = (tCONNECTELE *)pRequest->Handle.ConnectionContext;
|
|
|
|
CTEVerifyHandle(pConnectEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status);
|
|
|
|
//
|
|
// a Listen has completed
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLockAtDpc(pConnectEle);
|
|
if (pConnectEle->state == NBT_SESSION_WAITACCEPT)
|
|
{
|
|
tLOWERCONNECTION *pLowerConn;
|
|
|
|
//
|
|
// We need to send a session response PDU here, since a Listen has
|
|
// has completed back to the client, and the session is not yet up
|
|
//
|
|
SET_STATE_UPPER (pConnectEle, NBT_SESSION_UP);
|
|
|
|
pLowerConn = (tLOWERCONNECTION *)pConnectEle->pLowerConnId;
|
|
SET_STATE_LOWER (pLowerConn, NBT_SESSION_UP);
|
|
SET_STATERCV_LOWER(pLowerConn, NORMAL, Normal);
|
|
|
|
CTESpinFreeAtDpc(pConnectEle);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
status = TcpSendSessionResponse(
|
|
pLowerConn,
|
|
NBT_POSITIVE_SESSION_RESPONSE,
|
|
0L);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
CTESpinFreeAtDpc(pConnectEle);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
return(status);
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtReceiveDatagram(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN PTDI_CONNECTION_INFORMATION pReceiveInfo,
|
|
IN PTDI_CONNECTION_INFORMATION pReturnedInfo,
|
|
IN LONG ReceiveLength,
|
|
IN LONG *pReceivedLength,
|
|
IN PVOID pBuffer,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles sending client data to the Transport TDI
|
|
interface. It is mostly a pass through routine for the data
|
|
except that this code must create a datagram header and pass that
|
|
header back to the calling routine.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS status;
|
|
tCLIENTELE *pClientEle;
|
|
CTELockHandle OldIrq;
|
|
tRCVELE *pRcvEle;
|
|
tADDRESSELE *pAddressEle;
|
|
|
|
pClientEle = (tCLIENTELE *)pRequest->Handle.AddressHandle;
|
|
CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status);
|
|
pAddressEle = pClientEle->pAddress;
|
|
*pReceivedLength = 0;
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtReceiveDatagram: RcvDgram posted (pIrp) %X \n",pIrp));
|
|
|
|
pRcvEle = (tRCVELE *)NbtAllocMem(sizeof(tRCVELE),NBT_TAG('J'));
|
|
if (!pRcvEle)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
pRcvEle->pIrp = pIrp;
|
|
pRcvEle->ReceiveInfo = pReceiveInfo;
|
|
pRcvEle->ReturnedInfo = pReturnedInfo;
|
|
pRcvEle->RcvLength = ReceiveLength;
|
|
pRcvEle->pRcvBuffer = pBuffer;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
//
|
|
// tack the receive on to the client element for later use
|
|
//
|
|
InsertTailList(&pClientEle->RcvDgramHead,&pRcvEle->Linkage);
|
|
|
|
status = NTCheckSetCancelRoutine(pIrp,(PVOID)NbtCancelRcvDgram,pDeviceContext);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
RemoveEntryList(&pRcvEle->Linkage);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
FindNameOrQuery(
|
|
IN PUCHAR pName,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PVOID QueryCompletion,
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN ULONG NameFlags,
|
|
OUT tIPADDRESS *pIpAddress,
|
|
OUT tNAMEADDR **ppNameAddr,
|
|
IN ULONG NameReferenceContext,
|
|
IN BOOLEAN DgramSend
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles finding a name in the local or remote table or doing
|
|
a name query on the network.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
|
|
tNAMEADDR *pNameAddr;
|
|
CTELockHandle OldIrq2;
|
|
NTSTATUS status=STATUS_UNSUCCESSFUL;
|
|
BOOLEAN FoundInLocalTable = FALSE;
|
|
tDEVICECONTEXT *pThisDeviceContext;
|
|
LIST_ENTRY *pHead, *pEntry;
|
|
ULONG Index;
|
|
|
|
//
|
|
// this saves the client threads security context so we can
|
|
// open remote lmhost files later.- it is outside the Spin locks
|
|
// so it can be pageable
|
|
//
|
|
CTESaveClientSecurity(pTracker);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq2);
|
|
|
|
pTracker->pTrackerWorker = NULL; // Initialize the NameQuery Tracker
|
|
|
|
#ifdef MULTIPLE_WINS
|
|
if (ppNameAddr)
|
|
{
|
|
*ppNameAddr = NULL;
|
|
}
|
|
#endif
|
|
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("pTracker %p: %!NBTNAME!<%02x>",
|
|
pTracker,
|
|
pTracker->pDestName,
|
|
pTracker->pDestName[NETBIOS_NAME_SIZE-1]));
|
|
|
|
//
|
|
// Fail all connect attempts to 1C names.
|
|
//
|
|
if ((pTracker->pDestName[NETBIOS_NAME_SIZE-1] == 0x1c) &&
|
|
(pTracker->Flags & SESSION_SETUP_FLAG))
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
DELETE_CLIENT_SECURITY(pTracker);
|
|
|
|
KdPrint(("Nbt.FindNameOrQuery: Session setup -- p1CNameAddr was NULL\n"));
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("pTracker %p: STATUS_UNEXPECTED_NETWORK_ERROR", pTracker));
|
|
return(STATUS_UNEXPECTED_NETWORK_ERROR);
|
|
}
|
|
|
|
// send to the NetBios Broadcast name, so use the subnet broadcast
|
|
// address - also - a
|
|
// Kludge to keep the browser happy - always broadcast sends to
|
|
// 1d, however NodeStatus's are sent to the node owning the 1d name now.
|
|
//
|
|
if ((pName[0] == '*') || ((pName[NETBIOS_NAME_SIZE-1] == 0x1d) && (DgramSend)))
|
|
{
|
|
// this 'fake' pNameAddr has to be setup carefully so that the memory
|
|
// is released when NbtDeferenceName is called from SendDgramCompletion
|
|
// Note that this code does not apply to NbtConnect since these names
|
|
// are group names, and NbtConnect will not allow a session to a group
|
|
// name.
|
|
status = STATUS_INSUFFICIENT_RESOURCES ;
|
|
if (pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('K')))
|
|
{
|
|
CTEZeroMemory(pNameAddr,sizeof(tNAMEADDR));
|
|
CTEMemCopy( pNameAddr->Name, pName, NETBIOS_NAME_SIZE ) ;
|
|
pNameAddr->IpAddress = pDeviceContext->BroadcastAddress;
|
|
pNameAddr->NameTypeState = NAMETYPE_GROUP | STATE_RESOLVED;
|
|
|
|
// gets incremented below, and decremented when NBT_DEREFERENCE_NAMEADDR
|
|
// is called
|
|
CHECK_PTR(pNameAddr);
|
|
pNameAddr->RefCount = 0;
|
|
pNameAddr->Verify = LOCAL_NAME;
|
|
pNameAddr->AdapterMask = pDeviceContext->AdapterMask;
|
|
pNameAddr->ReleaseMask = (CTEULONGLONG) 0;
|
|
|
|
// adjust the linked list ptr to fool the RemoveEntry routine
|
|
// so it does not do anything wierd in NbtDeferenceName
|
|
//
|
|
pNameAddr->Linkage.Flink = pNameAddr->Linkage.Blink = &pNameAddr->Linkage;
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
DELETE_CLIENT_SECURITY(pTracker);
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("pTracker %p: STATUS_INSUFFICIENT_RESOURCES", pTracker));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The pdu is all made up and ready to go except that we don't know
|
|
// the destination IP address yet, so check in the local then remote
|
|
// table for the ip address.
|
|
//
|
|
pNameAddr = NULL;
|
|
|
|
//
|
|
// Dont check local cache for 1C names, to force a WINS query; so we find other
|
|
// DCs even if we have a local DC running.
|
|
//
|
|
if ((pName[NETBIOS_NAME_SIZE-1] != 0x1c) )
|
|
{
|
|
status = FindInHashTable (NbtConfig.pLocalHashTbl, pName, NbtConfig.pScope, &pNameAddr);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// check the remote table now if not found, or if it was found in
|
|
// conflict in the local table, or if it was found and its a group name
|
|
// or if it was found to be resolving in the local table. When the
|
|
// remote query timesout, it will check the local again to see if
|
|
// it is resolved yet.
|
|
// Going to the remote table for group names
|
|
// allows special Internet group names to be registered as
|
|
// as group names in the local table and still prompt this code to go
|
|
// to the name server to check for an internet group name. Bnodes do
|
|
// not understand internet group names as being different from
|
|
// regular group names, - they just broadcast to both. (Note: this
|
|
// allows Bnodes to resolve group names in the local table and do
|
|
// a broadcast to them without a costly broadcast name query for a
|
|
// group name (where everyone responds)). Node Status uses this routine too
|
|
// and it always wants to find the singular address of the destination,
|
|
// since it doesn't make sense doing a node status to the broadcast
|
|
// address.
|
|
// DgramSend is a flag to differentiate Connect attempts from datagram
|
|
// send attempts, so the last part of the If says that if it is a
|
|
// group name and not a Bnode, and not a Dgram Send, then check the
|
|
// remote table.
|
|
//
|
|
if ((!NT_SUCCESS(status)) ||
|
|
(pNameAddr->NameTypeState & STATE_CONFLICT) ||
|
|
(pNameAddr->NameTypeState & STATE_RESOLVING))
|
|
{
|
|
pNameAddr = NULL;
|
|
status = FindInHashTable (NbtConfig.pRemoteHashTbl, pName, NbtConfig.pScope, &pNameAddr);
|
|
|
|
if (NT_SUCCESS(status) &&
|
|
NbtConfig.SmbDisableNetbiosNameCacheLookup &&
|
|
IsDeviceNetbiosless(pDeviceContext) &&
|
|
!(pNameAddr->NameTypeState & PRELOADED) &&
|
|
NULL == pNameAddr->FQDN.Buffer) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// See if we have an address resolved on this device
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
ASSERT (!(pNameAddr->NameTypeState & STATE_RELEASED));
|
|
status = PickBestAddress (pNameAddr, pDeviceContext, pIpAddress);
|
|
}
|
|
}
|
|
else if (((IsDeviceNetbiosless (pDeviceContext)) &&
|
|
(pNameAddr->NameFlags & NAME_REGISTERED_ON_SMBDEV)) ||
|
|
((!IsDeviceNetbiosless(pDeviceContext)) &&
|
|
((pDeviceContext->IpAddress) &&
|
|
(pNameAddr->AdapterMask & pDeviceContext->AdapterMask))))
|
|
{
|
|
FoundInLocalTable = TRUE;
|
|
*pIpAddress = pDeviceContext->IpAddress;
|
|
pNameAddr->IpAddress = pDeviceContext->IpAddress;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is a Local name, so find the first device this name is registered on
|
|
//
|
|
if (!IsDeviceNetbiosless (pDeviceContext)) {
|
|
pHead = pEntry = &NbtConfig.DeviceContexts;
|
|
while ((pEntry = pEntry->Flink) != pHead)
|
|
{
|
|
pThisDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage);
|
|
if ((pThisDeviceContext->IpAddress) &&
|
|
(pThisDeviceContext->AdapterMask & pNameAddr->AdapterMask))
|
|
{
|
|
pNameAddr->IpAddress = pThisDeviceContext->IpAddress;
|
|
*pIpAddress = pThisDeviceContext->IpAddress;
|
|
FoundInLocalTable = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* The name is in local name table. However, we cannot find a device which has an IP address.
|
|
*/
|
|
if (!FoundInLocalTable) {
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
DELETE_CLIENT_SECURITY(pTracker);
|
|
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("pTracker %p: STATUS_BAD_NETWORK_PATH", pTracker));
|
|
return STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we found the name, but the name does not match
|
|
// what we were looking for, return error!
|
|
//
|
|
if ((status == STATUS_SUCCESS) &&
|
|
(!(pNameAddr->NameTypeState & NameFlags)))
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
DELETE_CLIENT_SECURITY(pTracker);
|
|
|
|
KdPrint(("Nbt.FindNameOrQuery: NameFlags=<%x> != pNameAddr->NameTypeState=<%x>\n",
|
|
NameFlags, pNameAddr->NameTypeState));
|
|
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("pTracker %p: STATUS_UNEXPECTED_NETWORK_ERROR", pTracker));
|
|
return(STATUS_UNEXPECTED_NETWORK_ERROR);
|
|
}
|
|
}
|
|
|
|
// The proxy puts name in the released state, so we need to ignore those
|
|
// and do another name query
|
|
// If the name is not resolved on this adapter then do a name query.
|
|
//
|
|
// MAlam: 2/4/97
|
|
// Added fix for Local Cluster Name Resolution: If the name is in the Local
|
|
// Names Cache, we do not need to check the adapter it's registered on. This
|
|
// is mainly to facilitate names registered on pseudo-devices which have to
|
|
// be made visible locally.
|
|
//
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// fill in some tracking values so we can complete the send later
|
|
InitializeListHead(&pTracker->TrackerList);
|
|
|
|
#if _NETBIOSLESS
|
|
// Query on the Net only if this request is not on a Netbiosless Device
|
|
if (IsDeviceNetbiosless(pDeviceContext))
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// this will query the name on the network and call a routine to
|
|
// finish sending the datagram when the query completes.
|
|
status = QueryNameOnNet (pName,
|
|
NbtConfig.pScope,
|
|
NBT_UNIQUE, //use this as the default
|
|
pTracker,
|
|
QueryCompletion,
|
|
NodeType & NODE_MASK,
|
|
NULL,
|
|
pDeviceContext,
|
|
&OldIrq2);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check the name state and if resolved, send to it
|
|
if (pNameAddr->NameTypeState & STATE_RESOLVED)
|
|
{
|
|
//
|
|
// found the name in the remote hash table, so send to it
|
|
//
|
|
// increment refcount so the name does not disappear out from under us
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, NameReferenceContext);
|
|
if (DgramSend)
|
|
{
|
|
pTracker->p1CNameAddr = NULL;
|
|
//
|
|
// check if it is a 1C name and if there is a name in
|
|
// the domainname list
|
|
//
|
|
if (pTracker->pDestName[NETBIOS_NAME_SIZE-1] == 0x1c)
|
|
{
|
|
//
|
|
// If the 1CNameAddr field is NULL here, we overwrite the pConnEle element (which is
|
|
// a union in the tracker). We check for NULL here and fail the request.
|
|
//
|
|
if (pTracker->p1CNameAddr = FindInDomainList(pTracker->pDestName,&DomainNames.DomainList))
|
|
{
|
|
NBT_REFERENCE_NAMEADDR (pTracker->p1CNameAddr, NameReferenceContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// overwrite the pDestName field with the pNameAddr value
|
|
// so that SendDgramContinue can send to Internet group names
|
|
//
|
|
pTracker->pNameAddr = pNameAddr;
|
|
if (ppNameAddr)
|
|
{
|
|
*ppNameAddr = pNameAddr;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
}
|
|
else if (pNameAddr->NameTypeState & STATE_RESOLVING)
|
|
{
|
|
|
|
ASSERTMSG("A resolving name in the name table!",0);
|
|
|
|
status = SendToResolvingName(pNameAddr,
|
|
pName,
|
|
OldIrq2,
|
|
pTracker,
|
|
QueryCompletion);
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Name neither in the RESOLVED nor RESOLVING state
|
|
//
|
|
NBT_PROXY_DBG(("FindNameOrQuery: STATE of NAME %16.16s(%X) is %d\n",
|
|
pName, pName[15], pNameAddr->NameTypeState & NAME_STATE_MASK));
|
|
status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq2);
|
|
}
|
|
}
|
|
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
DELETE_CLIENT_SECURITY(pTracker);
|
|
NbtTrace(NBT_TRACE_NAMESRV, ("pTracker %p: %!status!", pTracker, status));
|
|
}
|
|
return(status);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
tNAMEADDR *
|
|
FindNameRemoteThenLocal(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
OUT tIPADDRESS *pIpAddress,
|
|
OUT PULONG plNameType
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine Queries the remote hash table then the local one for a name.
|
|
|
|
Arguments:
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - completion status
|
|
|
|
--*/
|
|
{
|
|
tNAMEADDR *pNameAddr;
|
|
tIPADDRESS IpAddress = 0;
|
|
tIPADDRESS *pIpNbtGroupList = NULL;
|
|
|
|
if (pNameAddr = FindName (NBT_REMOTE, pTracker->pDestName, NbtConfig.pScope, plNameType)) {
|
|
pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount;
|
|
} else {
|
|
pNameAddr = FindName (NBT_LOCAL, pTracker->pDestName, NbtConfig.pScope, plNameType);
|
|
}
|
|
|
|
if ((pNameAddr) &&
|
|
(!NT_SUCCESS (PickBestAddress (pNameAddr, pTracker->pDeviceContext, &IpAddress))))
|
|
{
|
|
pNameAddr = NULL;
|
|
}
|
|
|
|
|
|
if (pIpAddress)
|
|
{
|
|
*pIpAddress = IpAddress;
|
|
}
|
|
|
|
return(pNameAddr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
SendToResolvingName(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN PCHAR pName,
|
|
IN CTELockHandle OldIrq,
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN PVOID QueryCompletion
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles the situation where a session send or a datagram send
|
|
is made WHILE the name is still resolving. The idea here is to hook this
|
|
tracker on to the one already doing the name query and when the first completes
|
|
this tracker will be completed too.
|
|
|
|
Arguments:
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - completion status
|
|
|
|
--*/
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTrack;
|
|
tTIMERQENTRY *pTimer;
|
|
|
|
|
|
KdPrint(("Nbt.SendToResolvingName: Two Name Queries for the same Resolving name %15.15s <%X>\n",
|
|
pNameAddr->Name,pNameAddr->Name[NETBIOS_NAME_SIZE-1]));
|
|
|
|
#ifdef PROXY_NODE
|
|
//
|
|
// Check if the query outstanding was sent by the PROXY code.
|
|
// If yes, we stop the timer and send the query ourselves.
|
|
//
|
|
if (pNameAddr->ProxyReqType != NAMEREQ_REGULAR)
|
|
{
|
|
NTSTATUS status;
|
|
//
|
|
// Stop the proxy timer. This will result in
|
|
// cleanup of the tracker buffer
|
|
//
|
|
NBT_PROXY_DBG(("SendToResolvingName: STOPPING PROXY TIMER FOR NAME %16.16s(%X)\n", pName, pName[15]));
|
|
|
|
// **** TODO ****** the name may be resolving with LMhosts or
|
|
// DNS so we can't just stop the timer and carry on!!!.
|
|
//
|
|
CHECK_PTR(pNameAddr);
|
|
if (pTimer = pNameAddr->pTimer)
|
|
{
|
|
pNameAddr->pTimer = NULL;
|
|
status = StopTimer(pTimer,NULL,NULL);
|
|
}
|
|
|
|
pNameAddr->NameTypeState = STATE_RELEASED;
|
|
|
|
//
|
|
// this will query the name on the network and call a
|
|
// routine to finish sending the datagram when the query
|
|
// completes.
|
|
//
|
|
status = QueryNameOnNet (pName,
|
|
NbtConfig.pScope,
|
|
NBT_UNIQUE, //use this as the default
|
|
pTracker,
|
|
QueryCompletion,
|
|
NodeType & NODE_MASK,
|
|
pNameAddr,
|
|
pTracker->pDeviceContext,
|
|
&OldIrq);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return(status);
|
|
|
|
//
|
|
// NOTE: QueryNameOnNet frees the pNameAddr by calling NBT_DEREFERENCE_NAMEADDR
|
|
// if that routine fails for some reason.
|
|
//
|
|
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ASSERT(pNameAddr->pTracker);
|
|
|
|
// there is currently a name query outstanding so just hook
|
|
// our tracker to the tracker already there.. use the
|
|
// list entry TrackerList for this.
|
|
//
|
|
pTrack = pNameAddr->pTracker;
|
|
|
|
//
|
|
// save the completion routine for this tracker since it may
|
|
// be different than the tracker currently doing the query
|
|
//
|
|
pTracker->CompletionRoutine = QueryCompletion;
|
|
|
|
InsertTailList(&pTrack->TrackerList,&pTracker->TrackerList);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
|
|
// we don't want to complete the Irp, so return pending status
|
|
//
|
|
return(STATUS_PENDING);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
USHORT
|
|
GetTransactId(
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine increments the transaction id with the spin lock held.
|
|
It uses NbtConfig.JointLock.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT TransactId;
|
|
CTELockHandle OldIrq;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
TransactId = NbtConfig.TransactionId++;
|
|
#ifndef VXD
|
|
if (TransactId == 0xFFFF)
|
|
{
|
|
NbtConfig.TransactionId = WINS_MAXIMUM_TRANSACTION_ID +1;
|
|
}
|
|
#else
|
|
if (TransactId == (DIRECT_DNS_NAME_QUERY_BASE - 1))
|
|
{
|
|
NbtConfig.TransactionId = 0;
|
|
}
|
|
#endif
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
return (TransactId);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
CTECountedAllocMem(
|
|
PVOID *pBuffer,
|
|
ULONG Size
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine allocates memory and counts the amount allocated so that it
|
|
will not allocate too much - generally this is used in datagram sends
|
|
where the send datagram is buffered.
|
|
|
|
Arguments:
|
|
|
|
Size - the number of bytes to allocate
|
|
PVOID - a pointer to the memory or NULL if a failure
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle OldIrq;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if (NbtMemoryAllocated > NbtConfig.MaxDgramBuffering)
|
|
{
|
|
*pBuffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
NbtMemoryAllocated += Size;
|
|
*pBuffer = NbtAllocMem(Size,NBT_TAG('L'));
|
|
}
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
CTECountedFreeMem(
|
|
PVOID pBuffer,
|
|
ULONG Size,
|
|
BOOLEAN fJointLockHeld
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine frees memory and decrements the global count of acquired
|
|
memory.
|
|
|
|
Arguments:
|
|
|
|
PVOID - a pointer to the memory to free
|
|
Size - the number of bytes to free
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle OldIrq;
|
|
|
|
if (!fJointLockHeld)
|
|
{
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
ASSERT(NbtMemoryAllocated >= Size);
|
|
if (NbtMemoryAllocated >= Size)
|
|
{
|
|
NbtMemoryAllocated -= Size;
|
|
}
|
|
else
|
|
{
|
|
NbtMemoryAllocated = 0;
|
|
}
|
|
|
|
if (!fJointLockHeld)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
CTEMemFree(pBuffer);
|
|
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
BuildSendDgramHdr(
|
|
IN ULONG SendLength,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PCHAR pSourceName,
|
|
IN PCHAR pDestName,
|
|
IN ULONG NameLength,
|
|
IN PVOID pBuffer,
|
|
OUT tDGRAMHDR **ppDgramHdr,
|
|
OUT tDGRAM_SEND_TRACKING **ppTracker
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine builds a datagram header necessary for sending datagrams.
|
|
It include the to and from Netbios names and ip addresses.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PCHAR pCopyTo;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
tDGRAMHDR *pDgramHdr;
|
|
ULONG HdrLength;
|
|
ULONG HLength;
|
|
ULONG TotalLength;
|
|
PVOID pSendBuffer;
|
|
PVOID pNameBuffer;
|
|
ULONG BytesCopied;
|
|
USHORT TransactId;
|
|
|
|
CTEPagedCode();
|
|
|
|
HdrLength = DGRAM_HDR_SIZE + (NbtConfig.ScopeLength <<1);
|
|
HLength = ((HdrLength + 3) / 4 ) * 4; // 4 byte aligned the hdr size
|
|
TotalLength = HLength + NameLength + SendLength;
|
|
CTECountedAllocMem ((PVOID *)&pDgramHdr,TotalLength);
|
|
if (!pDgramHdr)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
*ppDgramHdr = pDgramHdr;
|
|
|
|
// fill in the Dgram header
|
|
pDgramHdr->Flags = FIRST_DGRAM | (NbtConfig.PduNodeType >> 11);
|
|
TransactId = GetTransactId();
|
|
pDgramHdr->DgramId = htons(TransactId);
|
|
#ifdef _NETBIOSLESS
|
|
pDgramHdr->SrcPort = htons(pDeviceContext->DatagramPort);
|
|
if (IsDeviceNetbiosless(pDeviceContext))
|
|
{
|
|
// We don't know which adapter will be used, so use ANY
|
|
pDgramHdr->SrcIpAddr = htonl(IP_ANY_ADDRESS);
|
|
}
|
|
else
|
|
{
|
|
pDgramHdr->SrcIpAddr = htonl(pDeviceContext->IpAddress);
|
|
}
|
|
#else
|
|
pDgramHdr->SrcPort = htons(NBT_DATAGRAM_UDP_PORT);
|
|
pDgramHdr->SrcIpAddr = htonl(pDeviceContext->IpAddress);
|
|
#endif
|
|
//
|
|
// the length is the standard datagram length (dgram_hdr_size + 2* scope)
|
|
// minus size of the header that comes before the SourceName
|
|
//
|
|
pDgramHdr->DgramLength = htons( (USHORT)SendLength + (USHORT)DGRAM_HDR_SIZE
|
|
- (USHORT)(&((tDGRAMHDR *)0)->SrcName.NameLength)
|
|
+ ( (USHORT)(NbtConfig.ScopeLength << 1) ));
|
|
pDgramHdr->PckOffset = 0; // not fragmented for now!
|
|
|
|
pCopyTo = (PVOID)&pDgramHdr->SrcName.NameLength;
|
|
pCopyTo = ConvertToHalfAscii(pCopyTo, pSourceName, NbtConfig.pScope, NbtConfig.ScopeLength);
|
|
|
|
//
|
|
// copy the destination name and scope to the pdu - we use this node's
|
|
//
|
|
ConvertToHalfAscii (pCopyTo, pDestName, NbtConfig.pScope, NbtConfig.ScopeLength);
|
|
|
|
//
|
|
// copy the name in to the buffer since we are completing the client's irp
|
|
// and we will lose his buffer with the dest name in it.
|
|
//
|
|
pNameBuffer = (PVOID)((PUCHAR)pDgramHdr + HLength);
|
|
CTEMemCopy (pNameBuffer, pDestName, NameLength);
|
|
|
|
//
|
|
// copy the client's send buffer to our buffer so the send dgram can
|
|
// complete immediately.
|
|
//
|
|
pSendBuffer = (PVOID) ((PUCHAR)pDgramHdr + NameLength + HLength);
|
|
if (SendLength)
|
|
{
|
|
#ifdef VXD
|
|
CTEMemCopy(pSendBuffer,pBuffer,SendLength);
|
|
#else
|
|
status = TdiCopyMdlToBuffer(pBuffer,
|
|
0,
|
|
pSendBuffer,
|
|
0,
|
|
SendLength,
|
|
&BytesCopied);
|
|
|
|
if (!NT_SUCCESS(status) || (BytesCopied != SendLength))
|
|
{
|
|
CTECountedFreeMem ((PVOID)pDgramHdr, TotalLength, FALSE);
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pSendBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// get a buffer for tracking Dgram Sends
|
|
//
|
|
status = GetTracker(&pTracker, NBT_TRACKER_BUILD_SEND_DGRAM);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
|
|
CHECK_PTR(pTracker);
|
|
pTracker->SendBuffer.pBuffer = pSendBuffer;
|
|
pTracker->SendBuffer.Length = SendLength;
|
|
pTracker->SendBuffer.pDgramHdr = pDgramHdr;
|
|
pTracker->SendBuffer.HdrLength = HdrLength;
|
|
|
|
pTracker->pClientIrp = NULL;
|
|
pTracker->pDestName = pNameBuffer;
|
|
pTracker->UnicodeDestName = NULL;
|
|
pTracker->pNameAddr = NULL;
|
|
pTracker->RemoteNameLength = NameLength; // May be needed for Dns Name resolution
|
|
pTracker->pClientEle = NULL;
|
|
pTracker->AllocatedLength = TotalLength;
|
|
|
|
*ppTracker = pTracker;
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
CTECountedFreeMem((PVOID)pDgramHdr,TotalLength, FALSE);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DgramSendCleanupTracker(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN NTSTATUS status,
|
|
IN BOOLEAN fJointLockHeld
|
|
)
|
|
|
|
/*++
|
|
Routine Description
|
|
|
|
This routine cleans up after a data gram send.
|
|
|
|
Arguments:
|
|
|
|
pTracker
|
|
status
|
|
Length
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
tNAMEADDR *pNameAddr=NULL;
|
|
|
|
//
|
|
// Undo the nameAddr increment done before the send started - if we have
|
|
// actually resolved the name - when the name does not resolve pNameAddr
|
|
// is set to NULL before calling this routine.
|
|
//
|
|
if (pTracker->pNameAddr)
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_SEND_DGRAM, fJointLockHeld);
|
|
}
|
|
|
|
if (pTracker->p1CNameAddr)
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->p1CNameAddr, REF_NAME_SEND_DGRAM, fJointLockHeld);
|
|
pTracker->p1CNameAddr = NULL;
|
|
}
|
|
|
|
//
|
|
// free the buffer used for sending the data and free
|
|
// the tracker
|
|
//
|
|
CTECountedFreeMem((PVOID)pTracker->SendBuffer.pDgramHdr, pTracker->AllocatedLength, fJointLockHeld);
|
|
|
|
if (pTracker->pGroupList)
|
|
{
|
|
CTEMemFree(pTracker->pGroupList);
|
|
pTracker->pGroupList = NULL;
|
|
}
|
|
|
|
FreeTracker (pTracker,RELINK_TRACKER);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtSendDatagram(
|
|
IN TDI_REQUEST *pRequest,
|
|
IN PTDI_CONNECTION_INFORMATION pSendInfo,
|
|
IN LONG SendLength,
|
|
IN LONG *pSentLength,
|
|
IN PVOID pBuffer,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles sending client data to the Transport TDI
|
|
interface. It is mostly a pass through routine for the data
|
|
except that this code must create a datagram header and pass that
|
|
header back to the calling routine.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
TDI_ADDRESS_NETBT_INTERNAL TdiAddr;
|
|
tCLIENTELE *pClientEle;
|
|
tDGRAMHDR *pDgramHdr;
|
|
NTSTATUS status;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
PCHAR pName, pEndpointName;
|
|
ULONG NameLen;
|
|
ULONG NameType;
|
|
ULONG SendCount;
|
|
tIPADDRESS RemoteIpAddress;
|
|
tDEVICECONTEXT *pDeviceContextOut = NULL;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
PUCHAR pCopyTo;
|
|
NBT_WORK_ITEM_CONTEXT *pContext;
|
|
|
|
CTEPagedCode();
|
|
|
|
//
|
|
// Check for valid address on this Device + valid ClientElement
|
|
if ((pDeviceContext->IpAddress == 0) ||
|
|
(pDeviceContext->pFileObjects == NULL))
|
|
{
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
pClientEle = (tCLIENTELE *)pRequest->Handle.AddressHandle;
|
|
if ( pClientEle->Verify != NBT_VERIFY_CLIENT )
|
|
{
|
|
if ( pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN )
|
|
{
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INVALID_HANDLE;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Check for valid destination name and for whether it is an IP address
|
|
//
|
|
status = GetNetBiosNameFromTransportAddress (
|
|
(PTRANSPORT_ADDRESS)pSendInfo->RemoteAddress, pSendInfo->RemoteAddressLength, &TdiAddr);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.NbtSendDatagram: Unable to get dest name from address in dgramsend\n"));
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
pName = TdiAddr.OEMRemoteName.Buffer;
|
|
NameLen = TdiAddr.OEMRemoteName.Length;
|
|
NameType = TdiAddr.NameType;
|
|
if (TdiAddr.OEMEndpointName.Buffer) {
|
|
CTEMemCopy (pClientEle->EndpointName, TdiAddr.OEMEndpointName.Buffer, NETBIOS_NAME_SIZE);
|
|
}
|
|
|
|
pClientEle->AddressType = TdiAddr.AddressType;
|
|
if (RemoteIpAddress = Nbt_inet_addr(pName, DGRAM_SEND_FLAG))
|
|
{
|
|
pDeviceContextOut = GetDeviceFromInterface (htonl(RemoteIpAddress), TRUE);
|
|
if ((NbtConfig.ConnectOnRequestedInterfaceOnly) &&
|
|
(!IsDeviceNetbiosless(pDeviceContext)) &&
|
|
(pDeviceContext != pDeviceContextOut))
|
|
{
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
goto NbtSendDatagram_Exit;
|
|
}
|
|
}
|
|
|
|
#ifndef VXD
|
|
if (pClientEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX)
|
|
{
|
|
pEndpointName = pClientEle->EndpointName;
|
|
}
|
|
else
|
|
#endif // !VXD
|
|
{
|
|
pEndpointName = pName;
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.NbtSendDatagram: Dgram Send to = %16.16s<%X>\n",pName,pName[15]));
|
|
|
|
status = BuildSendDgramHdr (SendLength,
|
|
pDeviceContext,
|
|
((tADDRESSELE *)pClientEle->pAddress)->pNameAddr->Name, // Source name
|
|
pEndpointName,
|
|
NameLen,
|
|
pBuffer,
|
|
&pDgramHdr,
|
|
&pTracker);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto NbtSendDatagram_Exit;
|
|
}
|
|
|
|
//
|
|
// save the devicecontext that the client is sending on.
|
|
//
|
|
pTracker->pDeviceContext = (PVOID)pDeviceContext;
|
|
pTracker->Flags = DGRAM_SEND_FLAG;
|
|
pTracker->pClientIrp = pIrp;
|
|
pTracker->AddressType = pClientEle->AddressType;
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
pIrpSp->Parameters.Others.Argument4 = pTracker;
|
|
status = NTCheckSetCancelRoutine(pIrp, NbtCancelDgramSend, pDeviceContext);
|
|
if (STATUS_CANCELLED == status)
|
|
{
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.NbtSendDatagram: Request was cancelled!\n"));
|
|
pTracker->pClientIrp = NULL;
|
|
pIrpSp->Parameters.Others.Argument4 = NULL;
|
|
DgramSendCleanupTracker(pTracker,status,FALSE);
|
|
goto NbtSendDatagram_Exit;
|
|
}
|
|
|
|
if (RemoteIpAddress)
|
|
{
|
|
//
|
|
// add this address to the remote hashtable
|
|
//
|
|
status = LockAndAddToHashTable (NbtConfig.pRemoteHashTbl,
|
|
pName,
|
|
NbtConfig.pScope,
|
|
RemoteIpAddress,
|
|
NBT_UNIQUE,
|
|
NULL,
|
|
NULL,
|
|
pDeviceContextOut,
|
|
NAME_RESOLVED_BY_IP);
|
|
|
|
if (NT_SUCCESS (status)) // SUCCESS if added first time, PENDING if name already existed!
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if the name is longer than 16 bytes, it's not a netbios name.
|
|
// skip wins, broadcast etc. and go straight to dns resolution
|
|
//
|
|
status = STATUS_UNSUCCESSFUL;
|
|
if (NameLen <= NETBIOS_NAME_SIZE)
|
|
{
|
|
status = FindNameOrQuery(pName,
|
|
pDeviceContext,
|
|
SendDgramContinue,
|
|
pTracker,
|
|
(ULONG) (NAMETYPE_UNIQUE | NAMETYPE_GROUP | NAMETYPE_INET_GROUP),
|
|
&pTracker->RemoteIpAddress,
|
|
&pTracker->pNameAddr,
|
|
REF_NAME_SEND_DGRAM,
|
|
TRUE);
|
|
}
|
|
|
|
if ((NameLen > NETBIOS_NAME_SIZE) ||
|
|
((IsDeviceNetbiosless(pDeviceContext)) && (!NT_SUCCESS(status))))
|
|
{
|
|
if (pContext = (NBT_WORK_ITEM_CONTEXT*)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('H')))
|
|
{
|
|
pContext->pTracker = NULL; // no query tracker
|
|
pContext->pClientContext = pTracker; // the client tracker
|
|
pContext->ClientCompletion = SendDgramContinue;
|
|
pContext->pDeviceContext = pDeviceContext;
|
|
|
|
//
|
|
// Start the timer so that the request does not hang waiting for Dns!
|
|
//
|
|
StartLmHostTimer(pContext, FALSE);
|
|
status = NbtProcessLmhSvcRequest (pContext, NBT_RESOLVE_WITH_DNS);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
CTEMemFree(pContext);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) // If the name was an IP address or was present in the cache
|
|
{
|
|
SendDgramContinue (pTracker, STATUS_SUCCESS);
|
|
status = STATUS_PENDING; // SendDgramContinue will cleanup and complete the Irp
|
|
}
|
|
else if (status != STATUS_PENDING)
|
|
{
|
|
*pSentLength = 0;
|
|
NTClearFindNameInfo (pTracker, &pIrp, pIrp, pIrpSp);
|
|
if (!pIrp)
|
|
{
|
|
status = STATUS_PENDING; // irp is already completed: return pending so we don't complete again
|
|
}
|
|
|
|
pTracker->pNameAddr = NULL;
|
|
DgramSendCleanupTracker(pTracker,status,FALSE);
|
|
}
|
|
|
|
NbtSendDatagram_Exit:
|
|
|
|
if (pDeviceContextOut)
|
|
{
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContextOut, REF_DEV_OUT_FROM_IP, FALSE);
|
|
}
|
|
|
|
//
|
|
// return the status to the client.
|
|
//
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
SendDgramContinue(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles sending client data to the Transport TDI
|
|
interface after the destination name has resolved to an IP address.
|
|
This routine is given as the completion routine to the "QueryNameOnNet" call
|
|
in NbtSendDatagram, above. When a name query response comes in or the
|
|
timer times out after N retries.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq;
|
|
ULONG lNameType;
|
|
tNAMEADDR *pNameAddr = NULL;
|
|
tNAMEADDR *p1CNameAddr = NULL;
|
|
tDGRAM_SEND_TRACKING *pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
tDEVICECONTEXT *pDeviceContext = pTracker->pDeviceContext;
|
|
PIRP pIrp;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
|
|
CHECK_PTR(pTracker);
|
|
DELETE_CLIENT_SECURITY(pTracker);
|
|
|
|
//
|
|
// The Tracker can get cleaned up somewhere and reassigned if we fail below
|
|
// causing the pClientIrp ptr to get lost. We need to save the Irp here
|
|
//
|
|
IoAcquireCancelSpinLock(&OldIrq);
|
|
if (pIrp = pTracker->pClientIrp)
|
|
{
|
|
pTracker->pClientIrp = NULL;
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
ASSERT (pIrpSp->Parameters.Others.Argument4 == pTracker);
|
|
pIrpSp->Parameters.Others.Argument4 = NULL;
|
|
|
|
IoSetCancelRoutine(pIrp, NULL);
|
|
}
|
|
IoReleaseCancelSpinLock(OldIrq);
|
|
|
|
//
|
|
// We have to reference the Device here for the calls to FindNameRemoteThenLocal,
|
|
// and also for SendDgram
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if ((pIrp) &&
|
|
(NBT_REFERENCE_DEVICE(pDeviceContext, REF_DEV_DGRAM, TRUE)))
|
|
{
|
|
//
|
|
// attempt to find the destination name in the remote hash table. If its
|
|
// there, then send to it. For 1c names, this node may be the only node
|
|
// with the 1c name registered, so check the local table, since we skipped
|
|
// it if the name ended in 1c.
|
|
//
|
|
if ((status == STATUS_SUCCESS) ||
|
|
(pTracker->pDestName[NETBIOS_NAME_SIZE-1] == 0x1c))
|
|
{
|
|
if (pTracker->pNameAddr)
|
|
{
|
|
pNameAddr = pTracker->pNameAddr;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Find and reference the Names if they were resolved
|
|
//
|
|
//
|
|
// check if it is a 1C name and if there is a name in the domain list
|
|
// If pNameAddr is not null, then the send to the domainlist will
|
|
// send to the p1CNameAddr after sending to pNameAddr
|
|
//
|
|
if ((pTracker->pDestName[NETBIOS_NAME_SIZE-1] == 0x1c) &&
|
|
(p1CNameAddr = FindInDomainList(pTracker->pDestName,&DomainNames.DomainList)))
|
|
{
|
|
NBT_REFERENCE_NAMEADDR (p1CNameAddr, REF_NAME_SEND_DGRAM);
|
|
}
|
|
|
|
if (pNameAddr = FindNameRemoteThenLocal(pTracker,&pTracker->RemoteIpAddress,&lNameType))
|
|
{
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_SEND_DGRAM);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if there is no pNameAddr then just make the domain list
|
|
// name the only pNameAddr to send to.
|
|
//
|
|
pNameAddr = p1CNameAddr;
|
|
p1CNameAddr = NULL;
|
|
}
|
|
|
|
pTracker->pNameAddr = pNameAddr;
|
|
pTracker->p1CNameAddr = p1CNameAddr;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// check if the name resolved or we have a list of domain names
|
|
// derived from the lmhosts file and it is a 1C name send.
|
|
//
|
|
if (pNameAddr)
|
|
{
|
|
// send the first datagram queued to this name
|
|
status = SendDgram(pNameAddr,pTracker);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
NBT_DEREFERENCE_DEVICE(pDeviceContext, REF_DEV_DGRAM, FALSE);
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
|
|
//
|
|
// set this so that the cleanup routine does not try to dereference
|
|
// the nameAddr
|
|
|
|
if (status == STATUS_TIMEOUT)
|
|
{
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
if (pIrp)
|
|
{
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
NTIoComplete (pIrp, STATUS_SUCCESS,((PTDI_REQUEST_KERNEL_SENDDG)&pIrpSp->Parameters)->SendLength);
|
|
}
|
|
else
|
|
{
|
|
// this is the ERROR handling if something goes wrong with the send
|
|
CTEIoComplete(pIrp,status,0L);
|
|
}
|
|
}
|
|
|
|
// a failure ret code means the send failed, so cleanup the tracker etc.
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DgramSendCleanupTracker(pTracker,status,FALSE);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
SendDgram(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN tDGRAM_SEND_TRACKING *pTracker
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles sending client data to the Transport TDI
|
|
interface after the destination name has resolved to an IP address. The
|
|
routine specifically handles sending to internet group names where the destination
|
|
is a list of ip addresses.
|
|
|
|
The Device must be referenced before calling this routine!
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
ULONG IpAddress;
|
|
NTSTATUS status;
|
|
PFILE_OBJECT pFileObject;
|
|
|
|
CHECK_PTR(pTracker);
|
|
|
|
if (pNameAddr->NameTypeState & NAMETYPE_UNIQUE )
|
|
{
|
|
((tDGRAMHDR *)pTracker->SendBuffer.pDgramHdr)->MsgType = DIRECT_UNIQUE;
|
|
}
|
|
else if (pNameAddr->Name[0] == '*')
|
|
{
|
|
((tDGRAMHDR *)pTracker->SendBuffer.pDgramHdr)->MsgType = BROADCAST_DGRAM;
|
|
}
|
|
else
|
|
{
|
|
// must be group, -
|
|
((tDGRAMHDR *)pTracker->SendBuffer.pDgramHdr)->MsgType = DIRECT_GROUP;
|
|
}
|
|
|
|
//
|
|
// if it is an internet group name, then send to the list of addresses
|
|
//
|
|
if (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP)
|
|
{
|
|
status = DatagramDistribution(pTracker,pNameAddr);
|
|
return(STATUS_PENDING); // DatagramDistribution will cleanup if it failed!
|
|
}
|
|
|
|
if (pNameAddr->NameTypeState & NAMETYPE_GROUP)
|
|
{
|
|
IpAddress = 0;
|
|
}
|
|
else if (pNameAddr->Verify == REMOTE_NAME)
|
|
{
|
|
IpAddress = pTracker->RemoteIpAddress;
|
|
}
|
|
// LOCAL_NAME Unique
|
|
else if (IsDeviceNetbiosless(pTracker->pDeviceContext)) // use any non-zero value for local address
|
|
{
|
|
IpAddress = LOOP_BACK;
|
|
}
|
|
else
|
|
{
|
|
IpAddress = pTracker->pDeviceContext->IpAddress;
|
|
}
|
|
|
|
pTracker->p1CNameAddr = NULL;
|
|
pTracker->IpListIndex = 0; // flag that there are no more addresses in the list
|
|
|
|
/*
|
|
* Strict source routing,
|
|
* 1. The machine should be multi-homed.
|
|
* 2. It is not turned off by the registry key.
|
|
* 3. It is a regular device (not cluster device or SMB device).
|
|
*/
|
|
if (!IsLocalAddress(IpAddress) && NbtConfig.MultiHomed && NbtConfig.SendDgramOnRequestedInterfaceOnly &&
|
|
pTracker->pDeviceContext->IPInterfaceContext != (ULONG)(-1) &&
|
|
(!IsDeviceNetbiosless(pTracker->pDeviceContext))) {
|
|
ULONG Interface, Metric;
|
|
|
|
pTracker->pDeviceContext->pFastQuery(htonl(IpAddress), &Interface, &Metric);
|
|
if (Interface != pTracker->pDeviceContext->IPInterfaceContext) {
|
|
SendDgramCompletion(pTracker, STATUS_SUCCESS, 0);
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
// send the Datagram...
|
|
status = UdpSendDatagram( pTracker,
|
|
IpAddress,
|
|
SendDgramCompletion,
|
|
pTracker, // context for completion
|
|
pTracker->pDeviceContext->DatagramPort,
|
|
NBT_DATAGRAM_SERVICE);
|
|
|
|
// the irp will be completed via SendDgramCompletion
|
|
// so don't complete it by the caller too
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
SendDgramCompletion(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status,
|
|
IN ULONG lInfo
|
|
)
|
|
{
|
|
CTELockHandle OldIrq;
|
|
tDGRAM_SEND_TRACKING *pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if (pTracker->IpListIndex)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
SendNextDgramToGroup(pTracker, status); // Further processing will be done here for Group sends
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Datagram send to a unique name!
|
|
//
|
|
DgramSendCleanupTracker(pTracker,status,TRUE);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DelayedSendDgramDist (
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN PVOID pClientContext,
|
|
IN PVOID Unused1,
|
|
IN tDEVICECONTEXT *Unused2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by the Executive Worker thread to send another
|
|
datagram for the 1C name datagram distribution function.
|
|
|
|
Arguments:
|
|
|
|
Context -
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS status;
|
|
tDEVICECONTEXT *pDeviceContext = pTracker->pDeviceContext;
|
|
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.DelayedSendDgramDist: To name %15.15s<%X>:Ip %X, \n",
|
|
pTracker->pNameAddr->Name,pTracker->pNameAddr->Name[15],pClientContext));
|
|
|
|
// send the Datagram...
|
|
if (NBT_REFERENCE_DEVICE (pDeviceContext, REF_DEV_DGRAM, FALSE))
|
|
{
|
|
status = UdpSendDatagram (pTracker,
|
|
(tIPADDRESS) PtrToUlong(pClientContext),
|
|
SendDgramCompletion,
|
|
pTracker,
|
|
#ifdef _NETBIOSLESS
|
|
pTracker->pDeviceContext->DatagramPort,
|
|
#else
|
|
NBT_DATAGRAM_UDP_PORT,
|
|
#endif
|
|
NBT_DATAGRAM_SERVICE);
|
|
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_DGRAM, FALSE);
|
|
}
|
|
else
|
|
{
|
|
SendNextDgramToGroup (pTracker, STATUS_BAD_NETWORK_PATH);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
SendNextDgramToGroup(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
Routine Description
|
|
|
|
This routine is hit when the
|
|
datagram has been sent by the transport and it completes the request back
|
|
to us ( may not have actually sent on the wire though ).
|
|
|
|
This routine also handles sending multiple datagrams for the InternetGroup name
|
|
case.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
tIPADDRESS IpAddress;
|
|
CTELockHandle OldIrq;
|
|
|
|
// if this an Internet group send, then there may be more addresses in
|
|
// the list to send to. So check the IpListIndex. For single
|
|
// sends, this value is set to 0 and the code will jump to the bottom
|
|
// where the client's irp will be completed.
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
ASSERT (pTracker->RCount); // RCount is still referenced from the last send
|
|
// The SendCompletion can happen after the Device has been unbound,
|
|
// so check for that also!
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
(pTracker->IpListIndex < END_DGRAM_DISTRIBUTION))
|
|
{
|
|
IpAddress = pTracker->pGroupList[pTracker->IpListIndex++];
|
|
|
|
if (IpAddress != (tIPADDRESS) -1) // The list ends in a -1 ipaddress, so stop when we see that
|
|
{
|
|
//
|
|
// We already have an RCount reference, so no need to do another one here!
|
|
if (NT_SUCCESS (NTQueueToWorkerThread(NULL,
|
|
DelayedSendDgramDist,
|
|
pTracker,
|
|
ULongToPtr(IpAddress),
|
|
NULL,
|
|
pTracker->pDeviceContext,
|
|
TRUE)))
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
pTracker->RCount--; // decrement the ref count done during the last send
|
|
pTracker->IpListIndex = END_DGRAM_DISTRIBUTION;
|
|
|
|
//
|
|
// Either we failed, or we are done, so if the Timer is running, let it cleanup!
|
|
//
|
|
|
|
if (!(pTracker->pTimer) &&
|
|
(pTracker->RCount == 0))
|
|
{
|
|
DgramSendCleanupTracker(pTracker,status,TRUE);
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern
|
|
VOID
|
|
DgramDistTimeout(
|
|
PVOID pContext,
|
|
PVOID pContext2,
|
|
tTIMERQENTRY *pTimerQEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles a short timeout on a datagram distribution. It
|
|
checks if the dgram send is hung up in the transport doing an ARP and
|
|
then it does the next dgram send if the first is still hung up.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
CTELockHandle OldIrq;
|
|
tNAMEADDR *pNameAddr;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
|
|
if (!pTimerQEntry)
|
|
{
|
|
pTracker->pTimer = NULL;
|
|
if ((pTracker->IpListIndex == END_DGRAM_DISTRIBUTION) &&
|
|
(pTracker->RCount == 0))
|
|
{
|
|
DgramSendCleanupTracker(pTracker,STATUS_SUCCESS,TRUE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// After the last dgram has completed the iplistindex will be set
|
|
// to this and it is time to cleanup
|
|
//
|
|
if (pTracker->IpListIndex == END_DGRAM_DISTRIBUTION)
|
|
{
|
|
if (pTracker->RCount == 0)
|
|
{
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.DgramDistTimeout: Cleanup After DgramDistribution %15.15s<%X> \n",
|
|
pTracker->pNameAddr->Name,pTracker->pNameAddr->Name[15]));
|
|
|
|
pTracker->pTimer = NULL;
|
|
DgramSendCleanupTracker(pTracker,STATUS_SUCCESS,TRUE);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Wait for the dgram that has not completed yet - which may not
|
|
// be the last dgram , since ARP could hold one up much long
|
|
// than all the rest if the destination is dead. so start the timer
|
|
// again....
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pTracker->IpListIndex == pTracker->SavedListIndex)
|
|
{
|
|
//
|
|
// The dgram send is hung up in the transport, so do the
|
|
// next one now
|
|
//
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.DgramDistTimeout: DgramDistribution hung up on ARP forcing next send\n"));
|
|
|
|
pTracker->RCount++; // Reference it here since SendDgramToGroup expects this to be ref'ed
|
|
|
|
pTimerQEntry->Flags |= TIMER_RESTART;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
SendNextDgramToGroup(pTracker,STATUS_SUCCESS);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Save the current index so we can check it the next time the timer
|
|
// expires
|
|
//
|
|
pTracker->SavedListIndex = pTracker->IpListIndex;
|
|
}
|
|
|
|
}
|
|
|
|
pTimerQEntry->Flags |= TIMER_RESTART;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
DatagramDistribution(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN tNAMEADDR *pNameAddr
|
|
)
|
|
|
|
/*++
|
|
Routine Description
|
|
|
|
This routine sends a single datagram for a 1C name. It then sends
|
|
the next one when this one completes. This is done so that if
|
|
multiple sends go to the gateway, one does not cancel the next
|
|
when an Arp is necessary to resolve the gateway.
|
|
|
|
Arguments:
|
|
|
|
pTracker
|
|
pNameAddr
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
NTSTATUS Locstatus;
|
|
tIPADDRESS *pIpList;
|
|
ULONG Index;
|
|
tIPADDRESS IpAddress;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
CTELockHandle OldIrq;
|
|
PIRP pIrp;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
status = GetListOfAllAddrs (pTracker->pNameAddr, pTracker->p1CNameAddr, &pIpList, &Index);
|
|
|
|
if (pTracker->p1CNameAddr)
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->p1CNameAddr, REF_NAME_SEND_DGRAM, TRUE);
|
|
pTracker->p1CNameAddr = NULL;
|
|
}
|
|
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_SEND_DGRAM, TRUE);
|
|
pTracker->pNameAddr = NULL;
|
|
pTracker->RCount = 1; // Send RefCount == 0 when last send completes
|
|
pDeviceContext = pTracker->pDeviceContext;
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
if (STATUS_SUCCESS == status)
|
|
{
|
|
FilterIpAddrsForDevice (pIpList, pTracker->pDeviceContext, &Index);
|
|
|
|
pTracker->pGroupList = pIpList;
|
|
|
|
//
|
|
// When the proxy calls this routine the allocated length is set to
|
|
// zero. In that case we do not want to broadcast again since it
|
|
// could setup an infinite loop with another proxy on the same
|
|
// subnet.
|
|
//
|
|
if (pTracker->AllocatedLength == 0)
|
|
{
|
|
Index = 1;
|
|
}
|
|
else
|
|
{
|
|
Index = 0;
|
|
}
|
|
|
|
IpAddress = pIpList[Index];
|
|
|
|
pTracker->SavedListIndex = (USHORT) (Index); // For the next send in SendNextDgramToGroup
|
|
pTracker->IpListIndex = pTracker->SavedListIndex + 1; // For the next send in SendNextDgramToGroup
|
|
|
|
if (IpAddress == (ULONG)-1)
|
|
{
|
|
status = STATUS_INVALID_ADDRESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
(NBT_REFERENCE_DEVICE (pDeviceContext, REF_DEV_DGRAM, FALSE)))
|
|
{
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.DgramDistribution: To name %15.15s<%X>: %X, pTracker=<%p>\n",
|
|
pNameAddr->Name,pNameAddr->Name[15],IpAddress, pTracker));
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
Locstatus = StartTimer(DgramDistTimeout,
|
|
DGRAM_SEND_TIMEOUT,
|
|
pTracker,
|
|
NULL,
|
|
pTracker,
|
|
NULL,
|
|
pDeviceContext,
|
|
&pTracker->pTimer,
|
|
1,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(Locstatus))
|
|
{
|
|
CHECK_PTR(pTracker);
|
|
pTracker->pTimer = NULL;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// send the Datagram...
|
|
status = UdpSendDatagram (pTracker,
|
|
IpAddress,
|
|
SendDgramCompletion,
|
|
pTracker,
|
|
#ifdef _NETBIOSLESS
|
|
pTracker->pDeviceContext->DatagramPort,
|
|
#else
|
|
NBT_DATAGRAM_UDP_PORT,
|
|
#endif
|
|
NBT_DATAGRAM_SERVICE);
|
|
|
|
NBT_DEREFERENCE_DEVICE(pDeviceContext, REF_DEV_DGRAM, FALSE);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// we failed to send probably because of a lack of free memory
|
|
//
|
|
IoAcquireCancelSpinLock(&OldIrq);
|
|
//
|
|
// Make sure is still there!
|
|
//
|
|
if (pIrp = pTracker->pClientIrp)
|
|
{
|
|
pTracker->pClientIrp = NULL;
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
ASSERT (pIrpSp->Parameters.Others.Argument4 == pTracker);
|
|
pIrpSp->Parameters.Others.Argument4 = NULL;
|
|
IoSetCancelRoutine(pIrp, NULL);
|
|
IoReleaseCancelSpinLock(OldIrq);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
CTEIoComplete(pIrp, STATUS_SUCCESS, 0xFFFFFFFF);
|
|
}
|
|
else
|
|
{
|
|
CTEIoComplete(pIrp, status, 0L);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IoReleaseCancelSpinLock(OldIrq);
|
|
}
|
|
|
|
pTracker->RCount--;
|
|
DgramSendCleanupTracker(pTracker,STATUS_SUCCESS,FALSE);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtSetEventHandler(
|
|
tCLIENTELE *pClientEle,
|
|
int EventType,
|
|
PVOID pEventHandler,
|
|
PVOID pEventContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine sets the event handler specified to the clients event procedure
|
|
and saves the corresponding context value to return when that event is signaled.
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
|
|
// first verify that the client element is valid
|
|
CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status)
|
|
|
|
if (!pClientEle->pAddress)
|
|
{
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
CTESpinLock(pClientEle,OldIrq);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtSetEventHandler: Handler <%x> set for Event <%x>, on name %16.16s<%X>\n",
|
|
pEventHandler, EventType,
|
|
((tADDRESSELE *)pClientEle->pAddress)->pNameAddr->Name,
|
|
((tADDRESSELE *)pClientEle->pAddress)->pNameAddr->Name[15]));
|
|
|
|
status = STATUS_SUCCESS; // by default;
|
|
|
|
if (pEventHandler)
|
|
{
|
|
switch (EventType)
|
|
{
|
|
case TDI_EVENT_CONNECT:
|
|
pClientEle->evConnect = pEventHandler;
|
|
pClientEle->ConEvContext = pEventContext;
|
|
break;
|
|
case TDI_EVENT_DISCONNECT:
|
|
pClientEle->evDisconnect = pEventHandler;
|
|
pClientEle->DiscEvContext = pEventContext;
|
|
break;
|
|
case TDI_EVENT_ERROR:
|
|
pClientEle->evError = pEventHandler;
|
|
pClientEle->ErrorEvContext = pEventContext;
|
|
break;
|
|
case TDI_EVENT_RECEIVE:
|
|
pClientEle->evReceive = pEventHandler;
|
|
pClientEle->RcvEvContext = pEventContext;
|
|
break;
|
|
case TDI_EVENT_RECEIVE_DATAGRAM:
|
|
pClientEle->evRcvDgram = pEventHandler;
|
|
pClientEle->RcvDgramEvContext = pEventContext;
|
|
break;
|
|
case TDI_EVENT_RECEIVE_EXPEDITED:
|
|
pClientEle->evRcvExpedited = pEventHandler;
|
|
pClientEle->RcvExpedEvContext = pEventContext;
|
|
break;
|
|
case TDI_EVENT_SEND_POSSIBLE:
|
|
pClientEle->evSendPossible = pEventHandler;
|
|
pClientEle->SendPossEvContext = pEventContext;
|
|
break;
|
|
|
|
case TDI_EVENT_CHAINED_RECEIVE:
|
|
case TDI_EVENT_CHAINED_RECEIVE_DATAGRAM:
|
|
case TDI_EVENT_CHAINED_RECEIVE_EXPEDITED:
|
|
case TDI_EVENT_ERROR_EX:
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG("Invalid Event Type passed to SetEventHandler\n", (PVOID)0L);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
else
|
|
{ //
|
|
// the event handlers are set to point to the TDI default event handlers
|
|
// and can only be changed to another one, but not to a null address,
|
|
// so if null is passed in, set to default handler.
|
|
//
|
|
switch (EventType)
|
|
{
|
|
case TDI_EVENT_CONNECT:
|
|
#ifndef VXD
|
|
pClientEle->evConnect = TdiDefaultConnectHandler;
|
|
#else
|
|
pClientEle->evConnect = NULL;
|
|
#endif
|
|
pClientEle->ConEvContext = NULL;
|
|
break;
|
|
case TDI_EVENT_DISCONNECT:
|
|
#ifndef VXD
|
|
pClientEle->evDisconnect = TdiDefaultDisconnectHandler;
|
|
#else
|
|
pClientEle->evDisconnect = NULL;
|
|
#endif
|
|
pClientEle->DiscEvContext = NULL;
|
|
break;
|
|
case TDI_EVENT_ERROR:
|
|
#ifndef VXD
|
|
pClientEle->evError = TdiDefaultErrorHandler;
|
|
#else
|
|
pClientEle->evError = NULL;
|
|
#endif
|
|
pClientEle->ErrorEvContext = NULL;
|
|
break;
|
|
case TDI_EVENT_RECEIVE:
|
|
#ifndef VXD
|
|
pClientEle->evReceive = TdiDefaultReceiveHandler;
|
|
#else
|
|
pClientEle->evReceive = NULL;
|
|
#endif
|
|
pClientEle->RcvEvContext = NULL;
|
|
break;
|
|
case TDI_EVENT_RECEIVE_DATAGRAM:
|
|
#ifndef VXD
|
|
pClientEle->evRcvDgram = TdiDefaultRcvDatagramHandler;
|
|
#else
|
|
pClientEle->evRcvDgram = NULL;
|
|
#endif
|
|
pClientEle->RcvDgramEvContext = NULL;
|
|
break;
|
|
case TDI_EVENT_RECEIVE_EXPEDITED:
|
|
#ifndef VXD
|
|
pClientEle->evRcvExpedited = TdiDefaultRcvExpeditedHandler;
|
|
#else
|
|
pClientEle->evRcvExpedited = NULL;
|
|
#endif
|
|
pClientEle->RcvExpedEvContext = NULL;
|
|
break;
|
|
case TDI_EVENT_SEND_POSSIBLE:
|
|
#ifndef VXD
|
|
pClientEle->evSendPossible = TdiDefaultSendPossibleHandler;
|
|
#else
|
|
pClientEle->evSendPossible = NULL;
|
|
#endif
|
|
pClientEle->SendPossEvContext = NULL;
|
|
break;
|
|
|
|
case TDI_EVENT_CHAINED_RECEIVE:
|
|
case TDI_EVENT_CHAINED_RECEIVE_DATAGRAM:
|
|
case TDI_EVENT_CHAINED_RECEIVE_EXPEDITED:
|
|
case TDI_EVENT_ERROR_EX:
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG("Invalid Event Type passed to SetEventHandler\n", (PVOID)0L);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(pClientEle,OldIrq);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtSendNodeStatus(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PCHAR pName,
|
|
IN tIPADDRESS *pIpAddrs,
|
|
IN PVOID ClientContext,
|
|
IN PVOID CompletionRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine sends a node status message to another node.
|
|
It's called for two reasons:
|
|
1) in response to nbtstat -a (or -A). In this case, CompletionRoutine that's
|
|
passed in is CopyNodeStatusResponse, and ClientContext is the Irp to be completed
|
|
2) in response to "net use \\foobar.microsoft.com" (or net use \\11.1.1.3)
|
|
In this case, CompletionRoutine that's passed in is ExtractServerName,
|
|
and ClientContext is the tracker that correspondes to session setup.
|
|
|
|
The ip addr(s) s of the destination can be passed in (pIpAddrsList) when we
|
|
want to send an adapter status to a particular host. (case 2 above and
|
|
nbtstat -A pass in the ip address(es) since they don't know the name)
|
|
|
|
Note for netbiosless. In this case, the name server file object will be null, and the
|
|
status request will be looped back in UdpSendDatagram.
|
|
|
|
Arguments:
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
ULONG Length;
|
|
PUCHAR pHdr;
|
|
tNAMEADDR *pNameAddr;
|
|
PCHAR pName0;
|
|
tIPADDRESS IpAddress;
|
|
tIPADDRESS UNALIGNED *pAddress;
|
|
tIPADDRESS pIpAddress[2];
|
|
tIPADDRESS *pIpAddrsList = pIpAddrs;
|
|
tDEVICECONTEXT *pDeviceContextOut = NULL;
|
|
DWORD i = 0;
|
|
|
|
pName0 = pName;
|
|
if ((pIpAddrsList) ||
|
|
(IpAddress = Nbt_inet_addr (pName, REMOTE_ADAPTER_STAT_FLAG)))
|
|
{
|
|
if (!pIpAddrs)
|
|
{
|
|
pIpAddress[0] = IpAddress;
|
|
pIpAddress[1] = 0;
|
|
pIpAddrsList = pIpAddress;
|
|
}
|
|
|
|
if ((*pIpAddrsList == 0) ||
|
|
(*pIpAddrsList == DEFAULT_BCAST_ADDR) ||
|
|
(*pIpAddrsList == pDeviceContext->BroadcastAddress))
|
|
{
|
|
//
|
|
// Can't do a remote adapter status to a 0 IP address or a BCast address
|
|
return(STATUS_INVALID_ADDRESS);
|
|
}
|
|
|
|
// caller is expected to make sure list terminates in 0 and is
|
|
// not bigger than MAX_IPADDRS_PER_HOST elements
|
|
while(pIpAddrsList[i])
|
|
{
|
|
i++;
|
|
}
|
|
|
|
ASSERT(i<MAX_IPADDRS_PER_HOST);
|
|
i++; // for the trailing 0
|
|
|
|
IpAddress = pIpAddrsList[0];
|
|
pDeviceContextOut = GetDeviceFromInterface (htonl(IpAddress), FALSE);
|
|
if ((NbtConfig.ConnectOnRequestedInterfaceOnly) &&
|
|
(!IsDeviceNetbiosless(pDeviceContext)) &&
|
|
(pDeviceContext != pDeviceContextOut))
|
|
{
|
|
return (STATUS_BAD_NETWORK_PATH);
|
|
}
|
|
pName0 = NBT_BROADCAST_NAME;
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.NbtSendNodeStatus: <%16.16s:%x>, IP=<%x>, Completion=<%p>, Context=<%p>\n",
|
|
pName0, pName0[15], IpAddress, CompletionRoutine, ClientContext));
|
|
|
|
status = GetTracker(&pTracker, NBT_TRACKER_SEND_NODE_STATUS);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return(status);
|
|
}
|
|
|
|
// fill in the tracker data block
|
|
// note that the passed in transport address must stay valid till this
|
|
// send completes
|
|
pTracker->SendBuffer.pDgramHdr = NULL;
|
|
pTracker->SendBuffer.pBuffer = NULL;
|
|
pTracker->SendBuffer.Length = 0;
|
|
pTracker->Flags = REMOTE_ADAPTER_STAT_FLAG;
|
|
pTracker->RefCount = 2; // 1 for the send completion + 1 for the node status completion
|
|
pTracker->pDestName = pName0;
|
|
pTracker->UnicodeDestName = NULL;
|
|
pTracker->RemoteNameLength = NETBIOS_NAME_SIZE; // May be needed for Dns Name resolution
|
|
pTracker->pDeviceContext = pDeviceContext;
|
|
pTracker->pNameAddr = NULL;
|
|
pTracker->ClientCompletion = CompletionRoutine; // FindNameO.. may use CompletionRoutine!
|
|
pTracker->ClientContext = ClientContext;
|
|
pTracker->p1CNameAddr = NULL;
|
|
|
|
// the node status is almost identical with the query pdu so use it
|
|
// as a basis and adjust it .
|
|
//
|
|
pAddress = (ULONG UNALIGNED *)CreatePdu(pName0,
|
|
NbtConfig.pScope,
|
|
0L,
|
|
0,
|
|
eNAME_QUERY,
|
|
(PVOID)&pHdr,
|
|
&Length,
|
|
pTracker);
|
|
if (!pAddress)
|
|
{
|
|
FreeTracker(pTracker,RELINK_TRACKER);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
((PUSHORT)pHdr)[1] &= ~(FL_RECURDESIRE|FL_BROADCAST); // clear the recursion desired and broadcast bit
|
|
pHdr[Length-3] = (UCHAR)QUEST_STATUS; // set the NBSTAT field to 21 rather than 20
|
|
|
|
pTracker->SendBuffer.pDgramHdr = (PVOID)pHdr;
|
|
pTracker->SendBuffer.HdrLength = Length;
|
|
|
|
if (IpAddress)
|
|
{
|
|
// this 'fake' pNameAddr has to be setup carefully so that the memory
|
|
// is released when NbtDeferenceName is called from SendDgramCompletion
|
|
// Note that this code does not apply to NbtConnect since these names
|
|
// are group names, and NbtConnect will not allow a session to a group
|
|
// name.
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
if (!(pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('K'))))
|
|
{
|
|
FreeTracker(pTracker,RELINK_TRACKER);
|
|
CTEMemFree(pHdr);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
CTEZeroMemory(pNameAddr,sizeof(tNAMEADDR));
|
|
InitializeListHead (&pNameAddr->Linkage);
|
|
CTEMemCopy (pNameAddr->Name, pName0, NETBIOS_NAME_SIZE ) ;
|
|
pNameAddr->IpAddress = IpAddress;
|
|
pNameAddr->NameTypeState = NAMETYPE_GROUP | STATE_RESOLVED;
|
|
pNameAddr->Verify = LOCAL_NAME;
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_NODE_STATUS);
|
|
if (pDeviceContextOut)
|
|
{
|
|
pNameAddr->AdapterMask = pDeviceContextOut->AdapterMask;
|
|
}
|
|
pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount;
|
|
|
|
if (!(pNameAddr->pIpAddrsList = NbtAllocMem(i*sizeof(ULONG),NBT_TAG('M'))))
|
|
{
|
|
FreeTracker(pTracker,RELINK_TRACKER);
|
|
CTEMemFree(pHdr);
|
|
CTEMemFree(pNameAddr);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
i = 0;
|
|
do
|
|
{
|
|
pNameAddr->pIpAddrsList[i] = pIpAddrsList[i];
|
|
} while(pIpAddrsList[i++]);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
status = FindNameOrQuery(pName,
|
|
pDeviceContext,
|
|
SendNodeStatusContinue,
|
|
pTracker,
|
|
(ULONG) NAMETYPE_UNIQUE,
|
|
&IpAddress,
|
|
&pNameAddr,
|
|
REF_NAME_NODE_STATUS,
|
|
FALSE);
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
pTracker->RemoteIpAddress = IpAddress;
|
|
|
|
pTracker->p1CNameAddr = pNameAddr; // Since we have already Ref'ed
|
|
pNameAddr->IpAddress = IpAddress;
|
|
|
|
SendNodeStatusContinue (pTracker, STATUS_SUCCESS);
|
|
status = STATUS_PENDING; // SendNodeStatusContinue will cleanup
|
|
}
|
|
else if (!NT_SUCCESS(status)) // i.e not pending
|
|
{
|
|
FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
SendNodeStatusContinue(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles sending a node status request to a node after the
|
|
name has been resolved on the net.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
CTELockHandle OldIrq, OldIrq1;
|
|
tNAMEADDR *pNameAddr = NULL;
|
|
ULONG lNameType;
|
|
tTIMERQENTRY *pTimerEntry;
|
|
ULONG IpAddress;
|
|
PCTE_IRP pIrp;
|
|
COMPLETIONCLIENT pClientCompletion;
|
|
PVOID pClientContext;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *) pContext;
|
|
ASSERT (NBT_VERIFY_HANDLE (pTracker, NBT_VERIFY_TRACKER));
|
|
ASSERT (pTracker->TrackerType == NBT_TRACKER_SEND_NODE_STATUS);
|
|
|
|
DELETE_CLIENT_SECURITY(pTracker);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// attempt to find the destination name in the remote hash table. If its
|
|
// there, then send to it.
|
|
//
|
|
lNameType = NAMETYPE_UNIQUE;
|
|
if ((status == STATUS_SUCCESS) &&
|
|
((pTracker->p1CNameAddr) ||
|
|
(pNameAddr = FindNameRemoteThenLocal(pTracker, &IpAddress, &lNameType))))
|
|
{
|
|
//
|
|
// found the name in the remote hash table, so send to it after
|
|
// starting a timer to be sure we really do get a response
|
|
//
|
|
status = StartTimer(NodeStatusTimeout,
|
|
NbtConfig.uRetryTimeout,
|
|
pTracker, // Timer context value
|
|
NULL, // Timer context2 value
|
|
pTracker->ClientContext, // ClientContext
|
|
pTracker->ClientCompletion, // ClientCompletion
|
|
pTracker->pDeviceContext,
|
|
&pTimerEntry,
|
|
NbtConfig.uNumRetries,
|
|
TRUE);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if (pNameAddr)
|
|
{
|
|
// increment refcount so the name does not disappear
|
|
// dereference when we get the response or timeout
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_NODE_STATUS);
|
|
pTracker->RemoteIpAddress = IpAddress;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This name was already Referenced either in NbtSendNodeStatus
|
|
// or FindNameOrQuery
|
|
//
|
|
pNameAddr = pTracker->p1CNameAddr;
|
|
pTracker->p1CNameAddr = NULL;
|
|
IpAddress = pTracker->RemoteIpAddress;
|
|
}
|
|
|
|
pTracker->pNameAddr = pNameAddr;
|
|
pTracker->pTimer = pTimerEntry;
|
|
|
|
//
|
|
// Save the transaction ID so that we can match it later on
|
|
//
|
|
pTracker->TransactionId = ((tNAMEHDR*)(pTracker->SendBuffer.pDgramHdr))->TransactId;
|
|
|
|
// send the Datagram...
|
|
// the tracker block is put on a global Q in the Config
|
|
// data structure to keep track of it.
|
|
//
|
|
ExInterlockedInsertTailList(&NbtConfig.NodeStatusHead,
|
|
&pTracker->Linkage,
|
|
&NbtConfig.LockInfo.SpinLock);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
status = UdpSendDatagram (pTracker,
|
|
IpAddress,
|
|
NameDgramSendCompleted,
|
|
pTracker, // context
|
|
#ifdef _NETBIOSLESS
|
|
pTracker->pDeviceContext->NameServerPort,
|
|
#else
|
|
NBT_NAMESERVICE_UDP_PORT,
|
|
#endif
|
|
NBT_NAME_SERVICE);
|
|
|
|
if (!(NT_SUCCESS(status)))
|
|
{
|
|
//
|
|
// this undoes one of two ref's added in NbtSendNodeStatus
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
CTEMemFree(pTracker->SendBuffer.pDgramHdr);
|
|
pTracker->SendBuffer.pDgramHdr = NULL;
|
|
NBT_DEREFERENCE_TRACKER(pTracker, TRUE);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
// if the send fails, the timer will resend it...so no need
|
|
// to check the return code here.
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pTracker->p1CNameAddr)
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->p1CNameAddr, REF_NAME_NODE_STATUS, TRUE);
|
|
pTracker->p1CNameAddr = NULL;
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
// this is the ERROR handling if we failed to resolve the name
|
|
// or the timer did not start
|
|
pClientCompletion = pTracker->ClientCompletion;
|
|
pClientContext = pTracker->ClientContext;
|
|
|
|
if (pClientCompletion)
|
|
{
|
|
(*pClientCompletion) (pClientContext, STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NodeStatusTimeout(
|
|
PVOID pContext,
|
|
PVOID pContext2,
|
|
tTIMERQENTRY *pTimerQEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the NodeStatus timeouts on packets sent to nodes
|
|
that do not respond in a timely manner to node status. This routine will
|
|
resend the request.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
CTELockHandle OldIrq, OldIrq1;
|
|
COMPLETIONCLIENT pClientCompletion;
|
|
PVOID pClientContext;
|
|
PCHAR pName0;
|
|
PUCHAR pHdr;
|
|
ULONG Length;
|
|
ULONG UNALIGNED *pAddress;
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
ASSERT (NBT_VERIFY_HANDLE (pTracker, NBT_VERIFY_TRACKER));
|
|
ASSERT (pTracker->TrackerType == NBT_TRACKER_SEND_NODE_STATUS);
|
|
|
|
if (!pTimerQEntry)
|
|
{
|
|
//
|
|
// Do not dereference here since Node Status Done will do
|
|
// the dereference
|
|
//
|
|
CTESpinLock(&NbtConfig,OldIrq1);
|
|
RemoveEntryList(&pTracker->Linkage);
|
|
InitializeListHead(&pTracker->Linkage);
|
|
CTESpinFree(&NbtConfig,OldIrq1);
|
|
|
|
pTracker->pTimer = NULL;
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_NODE_STATUS, TRUE);
|
|
NBT_DEREFERENCE_TRACKER(pTracker, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
CHECK_PTR(pTimerQEntry);
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if (pTracker->SendBuffer.pDgramHdr)
|
|
{
|
|
//
|
|
// The timer has expired before the original Datagram
|
|
// could be sent, so just restart the timer!
|
|
//
|
|
pTimerQEntry->Flags |= TIMER_RESTART;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
return;
|
|
}
|
|
|
|
if ((--pTimerQEntry->Retries) == 0)
|
|
{
|
|
pClientCompletion = pTimerQEntry->ClientCompletion;
|
|
pClientContext = pTimerQEntry->ClientContext;
|
|
pTimerQEntry->ClientCompletion = NULL;
|
|
pTracker->pTimer = NULL;
|
|
|
|
// if the client routine has not yet run, run it now.
|
|
if (pClientCompletion)
|
|
{
|
|
// unlink the tracker from the node status Q if we successfully
|
|
// called the completion routine. Note, remove from the
|
|
// list before calling the completion routine to coordinate
|
|
// with DecodeNodeStatusResponse in inbound.c
|
|
//
|
|
CTESpinLock(&NbtConfig,OldIrq1);
|
|
RemoveEntryList(&pTracker->Linkage);
|
|
InitializeListHead(&pTracker->Linkage);
|
|
CTESpinFree(&NbtConfig,OldIrq1);
|
|
|
|
NBT_DEREFERENCE_NAMEADDR (pTracker->pNameAddr, REF_NAME_NODE_STATUS, TRUE);
|
|
NBT_DEREFERENCE_TRACKER (pTracker, TRUE);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
(*pClientCompletion) (pClientContext, STATUS_TIMEOUT);
|
|
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// send the Datagram...increment ref count
|
|
NBT_REFERENCE_TRACKER (pTracker);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// the node status is almost identical with the query pdu so use it
|
|
// as a basis and adjust it . We always rebuild the Node status
|
|
// request since the datagram gets freed when the irp is returned
|
|
// from the transport.
|
|
//
|
|
|
|
if (pTracker->p1CNameAddr)
|
|
{
|
|
pName0 = pTracker->p1CNameAddr->Name;
|
|
}
|
|
else
|
|
{
|
|
pName0 = pTracker->pNameAddr->Name;
|
|
}
|
|
|
|
pAddress = (ULONG UNALIGNED *)CreatePdu(pName0,
|
|
NbtConfig.pScope,
|
|
0L,
|
|
0,
|
|
eNAME_QUERY,
|
|
(PVOID)&pHdr,
|
|
&Length,
|
|
pTracker);
|
|
if (pAddress)
|
|
{
|
|
// clear the recursion desired bit
|
|
//
|
|
((PUSHORT)pHdr)[1] &= ~FL_RECURDESIRE;
|
|
|
|
// set the NBSTAT field to 21 rather than 20
|
|
pHdr[Length-3] = (UCHAR)QUEST_STATUS;
|
|
|
|
|
|
// fill in the tracker data block
|
|
// the passed in transport address must stay valid till this send completes
|
|
pTracker->SendBuffer.pDgramHdr = (PVOID)pHdr;
|
|
status = UdpSendDatagram (pTracker,
|
|
pTracker->pNameAddr->IpAddress,
|
|
NameDgramSendCompleted,
|
|
pTracker,
|
|
#ifdef _NETBIOSLESS
|
|
pTracker->pDeviceContext->NameServerPort,
|
|
#else
|
|
NBT_NAMESERVICE_UDP_PORT,
|
|
#endif
|
|
NBT_NAME_SERVICE);
|
|
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
if (!(NT_SUCCESS(status)))
|
|
{
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if (pTracker->SendBuffer.pDgramHdr)
|
|
{
|
|
CTEMemFree(pTracker->SendBuffer.pDgramHdr);
|
|
pTracker->SendBuffer.pDgramHdr = NULL;
|
|
}
|
|
NBT_DEREFERENCE_TRACKER(pTracker, TRUE);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
// always restart even if the above send fails, since it might succeed
|
|
// later.
|
|
pTimerQEntry->Flags |= TIMER_RESTART;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
#ifndef VXD
|
|
VOID
|
|
NTClearFindNameInfo(
|
|
tDGRAM_SEND_TRACKING *pTracker,
|
|
PIRP *ppClientIrp,
|
|
PIRP pIrp,
|
|
PIO_STACK_LOCATION pIrpSp
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine clears the Find Name information from the Tracker
|
|
within the Cancel SpinLock -- since NbtQueryFindNameInfo is a
|
|
pageable function, we have to do this in non-pageable code
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq1;
|
|
|
|
IoAcquireCancelSpinLock(&OldIrq1);
|
|
*ppClientIrp = pTracker->pClientIrp;
|
|
if (*ppClientIrp == pIrp)
|
|
{
|
|
pTracker->pClientIrp = NULL;
|
|
}
|
|
pIrpSp->Parameters.Others.Argument4 = NULL;
|
|
IoReleaseCancelSpinLock(OldIrq1);
|
|
}
|
|
#endif // !VXD
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NbtQueryFindName(
|
|
IN PTDI_CONNECTION_INFORMATION pInfo,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN PIRP pIrp,
|
|
IN BOOLEAN IsIoctl
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles a Client's query to find a netbios name. It
|
|
ultimately returns the IP address of the destination.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
PCHAR pName;
|
|
ULONG lNameType;
|
|
tNAMEADDR *pNameAddr;
|
|
PIRP pClientIrp = 0;
|
|
ULONG NameLen;
|
|
TDI_ADDRESS_NETBT_INTERNAL TdiAddr;
|
|
|
|
#ifndef VXD
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
#endif
|
|
|
|
CTEPagedCode();
|
|
|
|
// this routine gets a ptr to the netbios name out of the wierd
|
|
// TDI address syntax.
|
|
if (!IsIoctl)
|
|
{
|
|
ASSERT(pInfo->RemoteAddressLength);
|
|
status = GetNetBiosNameFromTransportAddress((PTRANSPORT_ADDRESS) pInfo->RemoteAddress,
|
|
pInfo->RemoteAddressLength, &TdiAddr);
|
|
pName = TdiAddr.OEMRemoteName.Buffer;
|
|
NameLen = TdiAddr.OEMRemoteName.Length;
|
|
lNameType = TdiAddr.NameType;
|
|
|
|
if ((!NT_SUCCESS(status)) ||
|
|
(lNameType != TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) ||
|
|
(NameLen > NETBIOS_NAME_SIZE))
|
|
{
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.NbtQueryFindName: Unable to get dest name from address in QueryFindName\n"));
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
#ifndef VXD
|
|
else
|
|
{
|
|
pName = ((tIPADDR_BUFFER *)pInfo)->Name;
|
|
NameLen = NETBIOS_NAME_SIZE;
|
|
}
|
|
#endif
|
|
|
|
IF_DBG(NBT_DEBUG_SEND)
|
|
KdPrint(("Nbt.NbtQueryFindName: For = %16.16s<%X>\n",pName,pName[15]));
|
|
|
|
//
|
|
// this will query the name on the network and call a routine to
|
|
// finish sending the datagram when the query completes.
|
|
//
|
|
status = GetTracker(&pTracker, NBT_TRACKER_QUERY_FIND_NAME);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return(status);
|
|
}
|
|
|
|
pTracker->pClientIrp = pIrp;
|
|
pTracker->pDestName = pName;
|
|
pTracker->UnicodeDestName = NULL;
|
|
pTracker->pDeviceContext = pDeviceContext;
|
|
pTracker->RemoteNameLength = NameLen; // May be needed for Dns Name resolution
|
|
|
|
//
|
|
// Set the FIND_NAME_FLAG here to indicate to the DNS name resolution code that
|
|
// this is not a session setup attempt so it can avoid the call to
|
|
// ConvertToHalfAscii (where pSessionHdr is NULL).
|
|
//
|
|
if (IsIoctl)
|
|
{
|
|
// Do not do DNS query for this name since this is from GetHostByName!
|
|
pTracker->Flags = REMOTE_ADAPTER_STAT_FLAG|FIND_NAME_FLAG|NO_DNS_RESOLUTION_FLAG;
|
|
}
|
|
else
|
|
{
|
|
pTracker->Flags = REMOTE_ADAPTER_STAT_FLAG|FIND_NAME_FLAG;
|
|
}
|
|
|
|
#ifndef VXD
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
pIrpSp->Parameters.Others.Argument4 = (PVOID)pTracker;
|
|
status = NTCheckSetCancelRoutine( pIrp, NbtCancelFindName,pDeviceContext );
|
|
|
|
if (status == STATUS_CANCELLED )
|
|
{
|
|
FreeTracker(pTracker,RELINK_TRACKER);
|
|
return(status);
|
|
}
|
|
#endif
|
|
|
|
status = FindNameOrQuery(pName,
|
|
pDeviceContext,
|
|
QueryNameCompletion,
|
|
pTracker,
|
|
(ULONG) (NAMETYPE_UNIQUE | NAMETYPE_GROUP | NAMETYPE_INET_GROUP),
|
|
&pTracker->RemoteIpAddress,
|
|
&pNameAddr,
|
|
REF_NAME_FIND_NAME,
|
|
FALSE);
|
|
|
|
if ((status == STATUS_SUCCESS) || (!NT_SUCCESS(status)))
|
|
{
|
|
#ifndef VXD
|
|
NTClearFindNameInfo (pTracker, &pClientIrp, pIrp, pIrpSp);
|
|
#else
|
|
pClientIrp = pTracker->pClientIrp;
|
|
#endif
|
|
if (pClientIrp)
|
|
{
|
|
ASSERT( pClientIrp == pIrp );
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
status = CopyFindNameData(pNameAddr, pIrp, pTracker);
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_FIND_NAME, FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// irp is already completed: return pending so we don't complete again
|
|
//
|
|
else
|
|
{
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_FIND_NAME, FALSE);
|
|
}
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
|
|
FreeTracker(pTracker, RELINK_TRACKER);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
QueryNameCompletion(
|
|
IN PVOID pContext,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine handles a name query completion that was requested by the
|
|
client. If successful the client is returned the ip address of the name
|
|
passed in the original request.
|
|
|
|
Arguments:
|
|
|
|
pContext - ptr to the DGRAM_TRACKER block
|
|
NTSTATUS - completion status
|
|
|
|
Return Values:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
CTELockHandle OldIrq1;
|
|
tNAMEADDR *pNameAddr;
|
|
ULONG lNameType;
|
|
PIRP pClientIrp;
|
|
#ifndef VXD
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
|
|
//
|
|
// We now use Cancel SpinLocks to check the validity of our Irps
|
|
// This is to prevent a race condition in between the time that
|
|
// the Cancel routine (NbtCancelFindName) releases the Cancel SpinLock
|
|
// and acquires the joint lock and we complete the Irp over here
|
|
//
|
|
IoAcquireCancelSpinLock(&OldIrq1);
|
|
#endif
|
|
|
|
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
|
pClientIrp = pTracker->pClientIrp;
|
|
pTracker->pClientIrp = NULL;
|
|
|
|
#ifndef VXD
|
|
//
|
|
// Make sure all parameters are valid for the Irp processing
|
|
//
|
|
if (! ((pClientIrp) &&
|
|
(pIrpSp = IoGetCurrentIrpStackLocation(pClientIrp)) &&
|
|
(pIrpSp->Parameters.Others.Argument4 == pTracker) ) )
|
|
{
|
|
IoReleaseCancelSpinLock(OldIrq1);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt:QueryNameCompletion: Irp=<%p> was cancelled\n", pClientIrp));
|
|
|
|
FreeTracker( pTracker,RELINK_TRACKER );
|
|
return;
|
|
}
|
|
|
|
pIrpSp->Parameters.Others.Argument4 = NULL;
|
|
IoSetCancelRoutine(pClientIrp, NULL);
|
|
IoReleaseCancelSpinLock(OldIrq1);
|
|
#endif
|
|
|
|
//
|
|
// attempt to find the destination name in the local/remote hash table.
|
|
//
|
|
if ((status == STATUS_SUCCESS) &&
|
|
(NT_SUCCESS(status = CopyFindNameData (NULL, pClientIrp, pTracker))))
|
|
{
|
|
CTEIoComplete(pClientIrp,status,0xFFFFFFFF);
|
|
}
|
|
else
|
|
{
|
|
// this is the ERROR handling if something goes wrong with the send
|
|
CTEIoComplete(pClientIrp,STATUS_IO_TIMEOUT,0L);
|
|
}
|
|
|
|
FreeTracker(pTracker,RELINK_TRACKER);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
CopyFindNameData(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN PIRP pIrp,
|
|
IN tDGRAM_SEND_TRACKING *pTracker
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine copies data received from the net node status response to
|
|
the client's irp.
|
|
|
|
|
|
Arguments:
|
|
|
|
pIrp - a ptr to an IRP
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PFIND_NAME_HEADER pFindNameHdr;
|
|
PFIND_NAME_BUFFER pFindNameBuffer;
|
|
tIPADDRESS *pIpAddr = NULL;
|
|
ULONG BuffSize;
|
|
ULONG DataLength;
|
|
ULONG NumNames;
|
|
ULONG i;
|
|
ULONG lNameType;
|
|
CTELockHandle OldIrq;
|
|
tIPADDRESS SrcAddress, DestIpAddress;
|
|
tIPADDRESS *pIpAddrBuffer;
|
|
tDEVICECONTEXT *pDeviceContext = pTracker->pDeviceContext;
|
|
|
|
SrcAddress = htonl(pDeviceContext->IpAddress);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if (!pNameAddr)
|
|
{
|
|
if (pNameAddr = FindNameRemoteThenLocal (pTracker, &DestIpAddress, &lNameType))
|
|
{
|
|
pNameAddr->IpAddress = DestIpAddress;
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return (STATUS_IO_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
status = GetListOfAllAddrs(pNameAddr, NULL, &pIpAddr, &NumNames);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (STATUS_SUCCESS != status)
|
|
{
|
|
return (STATUS_IO_TIMEOUT);
|
|
}
|
|
|
|
#ifdef VXD
|
|
DataLength = ((NCB*)pIrp)->ncb_length ;
|
|
#else
|
|
DataLength = MmGetMdlByteCount( pIrp->MdlAddress ) ;
|
|
#endif
|
|
|
|
BuffSize = sizeof(FIND_NAME_HEADER) + NumNames*sizeof(FIND_NAME_BUFFER);
|
|
|
|
//
|
|
// Make sure we don't overflow our buffer
|
|
//
|
|
if (BuffSize > DataLength)
|
|
{
|
|
if (DataLength <= sizeof(FIND_NAME_HEADER))
|
|
{
|
|
NumNames = 0 ;
|
|
}
|
|
else
|
|
{
|
|
NumNames = (DataLength - sizeof(FIND_NAME_HEADER)) / sizeof(FIND_NAME_BUFFER) ;
|
|
}
|
|
|
|
BuffSize = sizeof(FIND_NAME_HEADER) + NumNames*sizeof(FIND_NAME_BUFFER);
|
|
}
|
|
|
|
// sanity check that we are not allocating more than 64K for this stuff
|
|
if (BuffSize > 0xFFFF)
|
|
{
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
else if ((NumNames == 0) ||
|
|
(!(pFindNameHdr = NbtAllocMem ((USHORT)BuffSize, NBT_TAG('N')))))
|
|
{
|
|
if (pIpAddr)
|
|
{
|
|
CTEMemFree((PVOID)pIpAddr);
|
|
}
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
// Fill out the find name structure with zeros first
|
|
CTEZeroMemory((PVOID)pFindNameHdr,BuffSize);
|
|
pFindNameBuffer = (PFIND_NAME_BUFFER)((PUCHAR)pFindNameHdr + sizeof(FIND_NAME_HEADER));
|
|
pFindNameHdr->node_count = (USHORT)NumNames;
|
|
pFindNameHdr->unique_group = (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) ? UNIQUE_NAME : GROUP_NAME;
|
|
|
|
for (i=0;i < NumNames ;i++)
|
|
{
|
|
// Note: the source and destination address appear to be
|
|
// reversed since they are supposed to be the source and
|
|
// destination of the response to the findname query, hence
|
|
// the destination of the response is this node and the
|
|
// source is the other node.
|
|
*(tIPADDRESS UNALIGNED *) &pFindNameBuffer->source_addr[2] = htonl(pIpAddr[i]);
|
|
*(tIPADDRESS UNALIGNED *) &pFindNameBuffer->destination_addr[2] = SrcAddress;
|
|
pFindNameBuffer++;
|
|
}
|
|
|
|
#ifdef VXD
|
|
CTEMemCopy (((NCB*)pIrp)->ncb_buffer, pFindNameHdr, BuffSize);
|
|
ASSERT( ((NCB*)pIrp)->ncb_length >= BuffSize ) ;
|
|
((NCB*)pIrp)->ncb_length = BuffSize ;
|
|
status = STATUS_SUCCESS ;
|
|
#else
|
|
//
|
|
// copy the buffer to the client's MDL
|
|
//
|
|
status = TdiCopyBufferToMdl (pFindNameHdr, 0, BuffSize, pIrp->MdlAddress, 0, &DataLength);
|
|
|
|
pIrp->IoStatus.Information = DataLength;
|
|
pIrp->IoStatus.Status = status;
|
|
#endif
|
|
|
|
if (pIpAddr)
|
|
{
|
|
CTEMemFree((PVOID)pIpAddr);
|
|
}
|
|
CTEMemFree((PVOID)pFindNameHdr);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtAddEntryToRemoteHashTable(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN USHORT NameAddFlag,
|
|
IN PUCHAR Name,
|
|
IN ULONG IpAddress,
|
|
IN ULONG Ttl, // in seconds
|
|
IN UCHAR name_flags
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
tNAMEADDR *pNameAddr;
|
|
CTELockHandle OldIrq;
|
|
|
|
CTESpinLock (&NbtConfig.JointLock, OldIrq);
|
|
|
|
//
|
|
// We need only the name, IpAddress, name_flags, and Ttl fields
|
|
//
|
|
if (STATUS_SUCCESS == FindInHashTable (NbtConfig.pRemoteHashTbl,
|
|
Name, NbtConfig.pScope, &pNameAddr))
|
|
{
|
|
status = STATUS_DUPLICATE_NAME;
|
|
}
|
|
else if (pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('8')))
|
|
{
|
|
CTEZeroMemory (pNameAddr,sizeof(tNAMEADDR));
|
|
InitializeListHead (&pNameAddr->Linkage);
|
|
pNameAddr->Verify = REMOTE_NAME;
|
|
if (NameAddFlag & NAME_RESOLVED_BY_CLIENT)
|
|
{
|
|
pNameAddr->AdapterMask = (CTEULONGLONG)-1;
|
|
}
|
|
else if (pDeviceContext)
|
|
{
|
|
pNameAddr->AdapterMask = pDeviceContext->AdapterMask;
|
|
}
|
|
|
|
//
|
|
// Now copy the user-supplied data
|
|
//
|
|
CTEMemCopy (pNameAddr->Name,Name,NETBIOS_NAME_SIZE);
|
|
pNameAddr->TimeOutCount = (USHORT) (Ttl / (REMOTE_HASH_TIMEOUT/1000)) + 1;
|
|
pNameAddr->IpAddress = IpAddress;
|
|
if (name_flags & GROUP_STATUS)
|
|
{
|
|
pNameAddr->NameTypeState = STATE_RESOLVED | NAMETYPE_GROUP;
|
|
}
|
|
else
|
|
{
|
|
pNameAddr->NameTypeState = STATE_RESOLVED | NAMETYPE_UNIQUE;
|
|
}
|
|
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE);
|
|
status = AddToHashTable(NbtConfig.pRemoteHashTbl,
|
|
pNameAddr->Name,
|
|
NbtConfig.pScope,
|
|
IpAddress,
|
|
0,
|
|
pNameAddr,
|
|
NULL,
|
|
pDeviceContext,
|
|
NameAddFlag);
|
|
|
|
//
|
|
// If AddToHashTable fails, it will free the pNameAddr structure
|
|
// within itself, so no need to cleanup here!
|
|
//
|
|
if (NT_SUCCESS (status)) // SUCCESS if added first time, PENDING if name already existed!
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
if (status == STATUS_SUCCESS && NameAddFlag & NAME_RESOLVED_BY_CLIENT) {
|
|
//
|
|
// this prevents the name from being deleted by the Hash Timeout code
|
|
//
|
|
NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_PRELOADED);
|
|
pNameAddr->Ttl = 0xFFFFFFFF;
|
|
pNameAddr->NameTypeState |= PRELOADED | STATE_RESOLVED;
|
|
pNameAddr->NameTypeState &= ~STATE_CONFLICT;
|
|
pNameAddr->AdapterMask = (CTEULONGLONG)-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
CTESpinFree (&NbtConfig.JointLock, OldIrq);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtAddEntryToRemoteHashTable: Name=<%16.16s:%x>, status=<%x>\n",
|
|
Name, Name[15], status));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtQueryAdapterStatus(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
OUT PVOID *ppAdapterStatus,
|
|
IN OUT PLONG pSize,
|
|
enum eNbtLocation Location
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine creates a list of netbios names that are registered and
|
|
returns a pointer to the list in pAdapterStatus.
|
|
|
|
This routine can be called with a Null DeviceContext meaning, get the
|
|
remote hash table names, rather than the local hash table names.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq1;
|
|
LONG ActualCount, AllocatedCount;
|
|
LONG j;
|
|
LONG BuffSize;
|
|
PADAPTER_STATUS pAdapterStatus;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pHead;
|
|
PNAME_BUFFER pNameBuffer;
|
|
tADDRESSELE *pAddressEle;
|
|
tNAMEADDR *pNameAddr;
|
|
tHASHTABLE *pHashTable;
|
|
ULONG NameSize;
|
|
USHORT MaxAllowed;
|
|
PUCHAR pMacAddr;
|
|
tIPADDRESS IpAddress;
|
|
tIPADDRESS *pIpNbtGroupList;
|
|
ULONG Ttl;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
AllocatedCount = 0;
|
|
if (Location == NBT_LOCAL) // ==> Local Hash table
|
|
{
|
|
pHashTable = NbtConfig.pLocalHashTbl;
|
|
NameSize = sizeof(NAME_BUFFER);
|
|
}
|
|
else // ==> Remote Hash table
|
|
{
|
|
// get the list of addresses for this device - remote hash table
|
|
pHashTable = NbtConfig.pRemoteHashTbl;
|
|
NameSize = sizeof(tREMOTE_CACHE);
|
|
}
|
|
|
|
for (j=0;j < pHashTable->lNumBuckets ;j++ )
|
|
{
|
|
pHead = &pHashTable->Bucket[j];
|
|
pEntry = pHead;
|
|
while ((pEntry = pEntry->Flink) != pHead)
|
|
{
|
|
AllocatedCount++;
|
|
}
|
|
}
|
|
|
|
// Allocate Memory for the adapter status
|
|
BuffSize = sizeof(ADAPTER_STATUS) + AllocatedCount*NameSize;
|
|
|
|
#ifdef VXD
|
|
//
|
|
// The max BuffSize for Win9x is limited by a UShort,
|
|
// so see if we are going to overflow that
|
|
//
|
|
if (BuffSize > MAXUSHORT) // Make sure BuffSize fits in a USHORT
|
|
{
|
|
BuffSize = MAXUSHORT; // Recalculate BuffSize and AllocatedCount
|
|
AllocatedCount = (BuffSize - sizeof(ADAPTER_STATUS)) / NameSize;
|
|
}
|
|
#endif // VXD
|
|
|
|
#ifdef VXD
|
|
pAdapterStatus = NbtAllocMem((USHORT)BuffSize,NBT_TAG('O'));
|
|
#else
|
|
pAdapterStatus = NbtAllocMem(BuffSize,NBT_TAG('O'));
|
|
#endif
|
|
|
|
if (!pAdapterStatus)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
// Fill out the adapter status structure with zeros first
|
|
CTEZeroMemory((PVOID)pAdapterStatus,BuffSize);
|
|
|
|
//
|
|
// Fill in the MAC address
|
|
//
|
|
pMacAddr = &pDeviceContext->MacAddress.Address[0];
|
|
CTEMemCopy(&pAdapterStatus->adapter_address[0], pMacAddr, sizeof(tMAC_ADDRESS));
|
|
|
|
pAdapterStatus->rev_major = 0x03;
|
|
pAdapterStatus->adapter_type = 0xFE; // pretend it is an ethernet adapter
|
|
|
|
//
|
|
// in the VXD land limit the number of Ncbs to 64
|
|
//
|
|
#ifndef VXD
|
|
MaxAllowed = 0xFFFF;
|
|
pAdapterStatus->max_cfg_sess = (USHORT)MaxAllowed;
|
|
pAdapterStatus->max_sess = (USHORT)MaxAllowed;
|
|
#else
|
|
MaxAllowed = 64;
|
|
pAdapterStatus->max_cfg_sess = pDeviceContext->cMaxSessions;
|
|
pAdapterStatus->max_sess = pDeviceContext->cMaxSessions;
|
|
#endif
|
|
|
|
pAdapterStatus->free_ncbs = (USHORT)MaxAllowed;
|
|
pAdapterStatus->max_cfg_ncbs = (USHORT)MaxAllowed;
|
|
pAdapterStatus->max_ncbs = (USHORT)MaxAllowed;
|
|
|
|
pAdapterStatus->max_dgram_size = MAX_NBT_DGRAM_SIZE;
|
|
pAdapterStatus->max_sess_pkt_size = 0xffff;
|
|
|
|
// get the address of the name buffer at the end of the adapter status
|
|
// structure so we can copy the names into this area.
|
|
pNameBuffer = (PNAME_BUFFER)((ULONG_PTR)pAdapterStatus + sizeof(ADAPTER_STATUS));
|
|
|
|
ActualCount = 0;
|
|
j = 0;
|
|
if (Location == NBT_LOCAL)
|
|
{
|
|
pEntry = pHead = &NbtConfig.AddressHead;
|
|
}
|
|
else
|
|
{
|
|
pEntry = pHead = &pHashTable->Bucket[0];
|
|
}
|
|
|
|
while (AllocatedCount)
|
|
{
|
|
if (Location == NBT_LOCAL)
|
|
{
|
|
// ***** LOCAL HASH TABLE QUERY *****
|
|
|
|
// get out of while if we reach the end of the list
|
|
if ((pEntry = pEntry->Flink) == pHead)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage);
|
|
pNameAddr = pAddressEle->pNameAddr;
|
|
|
|
//
|
|
// skip the broadcast name and any permanent names that are
|
|
// registered as quick names(i.e. not registered on the net).
|
|
//
|
|
if ((pAddressEle->pNameAddr->Name[0] == '*') ||
|
|
(pAddressEle->pNameAddr->NameTypeState & NAMETYPE_QUICK) ||
|
|
(!(pAddressEle->pNameAddr->AdapterMask & pDeviceContext->AdapterMask))) // This Device only
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ***** REMOTE HASH TABLE QUERY *****
|
|
|
|
//
|
|
// See if we have reached the end of the HashTable
|
|
//
|
|
if (j == pHashTable->lNumBuckets)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if we have reached the last entry in the HashBucket
|
|
//
|
|
if ((pEntry = pEntry->Flink) == pHead)
|
|
{
|
|
pEntry = pHead = &pHashTable->Bucket[++j];
|
|
continue;
|
|
}
|
|
|
|
// for the remote table, skip over scope records.
|
|
pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage);
|
|
|
|
// don't return scope records or resolving records
|
|
//
|
|
if ((pNameAddr->NameTypeState & NAMETYPE_SCOPE) ||
|
|
(!(pNameAddr->NameTypeState & STATE_RESOLVED)) ||
|
|
(!(pNameAddr->AdapterMask & pDeviceContext->AdapterMask)))
|
|
{
|
|
continue;
|
|
}
|
|
//
|
|
// the remote cache query has a different structure that includes
|
|
// the ip address. Return the ip address to the caller.
|
|
//
|
|
IpAddress = 0;
|
|
PickBestAddress (pNameAddr, pDeviceContext, &IpAddress);
|
|
((tREMOTE_CACHE *)pNameBuffer)->IpAddress = IpAddress;
|
|
|
|
// preloaded entries do not timeout
|
|
//
|
|
if (pNameAddr->NameTypeState & PRELOADED)
|
|
{
|
|
Ttl = 0xFFFFFFFF;
|
|
}
|
|
else
|
|
{
|
|
Ttl = ((pNameAddr->TimeOutCount+1) * REMOTE_HASH_TIMEOUT)/1000;
|
|
}
|
|
|
|
((tREMOTE_CACHE *)pNameBuffer)->Ttl = Ttl;
|
|
}
|
|
|
|
pNameBuffer->name_flags = (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) ? UNIQUE_NAME : GROUP_NAME;
|
|
switch (pNameAddr->NameTypeState & NAME_STATE_MASK)
|
|
{
|
|
default:
|
|
case STATE_RESOLVED:
|
|
pNameBuffer->name_flags |= REGISTERED;
|
|
break;
|
|
|
|
case STATE_CONFLICT:
|
|
pNameBuffer->name_flags |= DUPLICATE;
|
|
break;
|
|
|
|
case STATE_RELEASED:
|
|
pNameBuffer->name_flags |= DEREGISTERED;
|
|
break;
|
|
|
|
case STATE_RESOLVING:
|
|
pNameBuffer->name_flags |= REGISTERING;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// name number 0 corresponds to perm.name name, so start from 1
|
|
//
|
|
pNameBuffer->name_num = (UCHAR) (ActualCount+1);
|
|
CTEMemCopy(pNameBuffer->name,pNameAddr->Name,NETBIOS_NAME_SIZE);
|
|
|
|
if (Location == NBT_LOCAL)
|
|
{
|
|
pNameBuffer++;
|
|
}
|
|
else
|
|
{
|
|
((tREMOTE_CACHE *)pNameBuffer)++;
|
|
}
|
|
|
|
AllocatedCount--;
|
|
ActualCount++;
|
|
}
|
|
|
|
//
|
|
// ReCalculate the new BuffSize based on the number of names
|
|
// we actually copied
|
|
//
|
|
BuffSize = sizeof(ADAPTER_STATUS) + ActualCount*NameSize;
|
|
|
|
//
|
|
// Is our status buffer size greater then the user's buffer?
|
|
// If the user buffer is expected to overflow, then
|
|
// set the name_count to the maximum number of valid names
|
|
// in the buffer
|
|
//
|
|
if (BuffSize > *pSize)
|
|
{
|
|
//
|
|
// Recalc how many names will fit
|
|
//
|
|
if (*pSize <= sizeof(ADAPTER_STATUS))
|
|
{
|
|
ActualCount = 0;
|
|
}
|
|
else
|
|
{
|
|
ActualCount = (*pSize - sizeof(ADAPTER_STATUS)) / NameSize;
|
|
}
|
|
}
|
|
|
|
pAdapterStatus->name_count = (USHORT)ActualCount;
|
|
|
|
//
|
|
// return the ptr to this wonderful structure of goodies
|
|
//
|
|
*ppAdapterStatus = (PVOID)pAdapterStatus;
|
|
*pSize = BuffSize;
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
return (STATUS_SUCCESS);
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtQueryConnectionList(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
OUT PVOID *ppConnList,
|
|
IN OUT PLONG pSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine creates a list of netbios connections and returns them to the
|
|
client. It is used by the "NbtStat" console application.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq1;
|
|
CTELockHandle OldIrq2;
|
|
CTELockHandle OldIrq3;
|
|
LONG Count;
|
|
LONG i;
|
|
LONG BuffSize;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pEntry1;
|
|
PLIST_ENTRY pEntry2;
|
|
PLIST_ENTRY pHead;
|
|
PLIST_ENTRY pHead1;
|
|
PLIST_ENTRY pHead2;
|
|
ULONG NameSize;
|
|
tCONNECTIONS *pCons;
|
|
tCONNECTION_LIST *pConnList;
|
|
tADDRESSELE *pAddressEle;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tCONNECTELE *pConnEle;
|
|
tCLIENTELE *pClient;
|
|
NTSTATUS status = STATUS_SUCCESS; // default
|
|
|
|
// locking the joint lock is enough to prevent new addresses from being
|
|
// added to the list while we count the list.
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
// go through the list of addresses, then the list of clients on each
|
|
// address and then the list of connection that are in use and those that
|
|
// are currently Listening.
|
|
//
|
|
Count = 0;
|
|
pHead = &NbtConfig.AddressHead;
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage);
|
|
|
|
CTESpinLock(pAddressEle,OldIrq2);
|
|
pHead1 = &pAddressEle->ClientHead;
|
|
pEntry1 = pHead1->Flink;
|
|
while (pEntry1 != pHead1)
|
|
{
|
|
pClient = CONTAINING_RECORD(pEntry1,tCLIENTELE,Linkage);
|
|
pEntry1 = pEntry1->Flink;
|
|
|
|
CTESpinLock(pClient,OldIrq3);
|
|
pHead2 = &pClient->ConnectActive;
|
|
pEntry2 = pHead2->Flink;
|
|
while (pEntry2 != pHead2)
|
|
{
|
|
// count the connections in use
|
|
pEntry2 = pEntry2->Flink;
|
|
Count++;
|
|
}
|
|
pHead2 = &pClient->ListenHead;
|
|
pEntry2 = pHead2->Flink;
|
|
while (pEntry2 != pHead2)
|
|
{
|
|
// count the connections listening
|
|
pEntry2 = pEntry2->Flink;
|
|
Count++;
|
|
}
|
|
CTESpinFree(pClient,OldIrq3);
|
|
}
|
|
CTESpinFree(pAddressEle,OldIrq2);
|
|
pEntry = pEntry->Flink;
|
|
}
|
|
NameSize = sizeof(tCONNECTIONS);
|
|
|
|
// Allocate Memory for the adapter status
|
|
BuffSize = sizeof(tCONNECTION_LIST) + Count*NameSize;
|
|
|
|
pConnList = NbtAllocMem(BuffSize,NBT_TAG('P'));
|
|
if (!pConnList)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock, OldIrq1);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Initialize the adapter status structure
|
|
//
|
|
CTEZeroMemory ((PVOID)pConnList, BuffSize);
|
|
pConnList->ConnectionCount = Count;
|
|
*ppConnList = (PVOID)pConnList;
|
|
|
|
if (Count == 0)
|
|
{
|
|
//
|
|
// We are done!
|
|
//
|
|
*pSize = BuffSize;
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
return status;
|
|
}
|
|
|
|
// get the address of the Connection List buffer at the end of the
|
|
// structure so we can copy the Connection info into this area.
|
|
pCons = pConnList->ConnList;
|
|
|
|
pHead = &NbtConfig.AddressHead;
|
|
pEntry = pHead->Flink;
|
|
i = 0;
|
|
while (pEntry != pHead)
|
|
{
|
|
pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage);
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
CTESpinLock(pAddressEle,OldIrq2);
|
|
pHead1 = &pAddressEle->ClientHead;
|
|
pEntry1 = pHead1->Flink;
|
|
while (pEntry1 != pHead1)
|
|
{
|
|
pClient = CONTAINING_RECORD(pEntry1,tCLIENTELE,Linkage);
|
|
pEntry1 = pEntry1->Flink;
|
|
|
|
CTESpinLock(pClient,OldIrq3);
|
|
pHead2 = &pClient->ConnectActive;
|
|
pEntry2 = pHead2->Flink;
|
|
while (pEntry2 != pHead2)
|
|
{
|
|
// count the connections in use
|
|
pConnEle = CONTAINING_RECORD(pEntry2,tCONNECTELE,Linkage);
|
|
|
|
if (pConnEle->pDeviceContext == pDeviceContext)
|
|
{
|
|
CTEMemCopy(pCons->LocalName,
|
|
pConnEle->pClientEle->pAddress->pNameAddr->Name,
|
|
NETBIOS_NAME_SIZE);
|
|
|
|
pLowerConn = pConnEle->pLowerConnId;
|
|
if (pLowerConn)
|
|
{
|
|
pCons->SrcIpAddr = pLowerConn->SrcIpAddr;
|
|
pCons->Originator = (UCHAR)pLowerConn->bOriginator;
|
|
#ifndef VXD
|
|
pCons->BytesRcvd = *(PLARGE_INTEGER)&pLowerConn->BytesRcvd;
|
|
pCons->BytesSent = *(PLARGE_INTEGER)&pLowerConn->BytesSent;
|
|
#else
|
|
pCons->BytesRcvd = pLowerConn->BytesRcvd;
|
|
pCons->BytesSent = pLowerConn->BytesSent;
|
|
#endif
|
|
CTEMemCopy(pCons->RemoteName,pConnEle->RemoteName,NETBIOS_NAME_SIZE);
|
|
}
|
|
|
|
pCons->State = pConnEle->state;
|
|
i++;
|
|
pCons++;
|
|
|
|
if (i >= Count)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
pEntry2 = pEntry2->Flink;
|
|
}
|
|
if (i >= Count)
|
|
{
|
|
CTESpinFree(pClient,OldIrq3);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// now for the Listens
|
|
//
|
|
pHead2 = &pClient->ListenHead;
|
|
pEntry2 = pHead2->Flink;
|
|
while (pEntry2 != pHead2)
|
|
{
|
|
tLISTENREQUESTS *pListenReq;
|
|
|
|
// count the connections listening on this Device
|
|
pListenReq = CONTAINING_RECORD(pEntry2,tLISTENREQUESTS,Linkage);
|
|
pConnEle = (tCONNECTELE *)pListenReq->pConnectEle;
|
|
pEntry2 = pEntry2->Flink;
|
|
|
|
if (pConnEle->pDeviceContext == pDeviceContext)
|
|
{
|
|
CTEMemCopy(pCons->LocalName,
|
|
pConnEle->pClientEle->pAddress->pNameAddr->Name,
|
|
NETBIOS_NAME_SIZE);
|
|
|
|
pCons->State = LISTENING;
|
|
|
|
i++;
|
|
pCons++;
|
|
|
|
if (i >= Count)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
CTESpinFree(pClient,OldIrq3);
|
|
if (i >= Count)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(pAddressEle,OldIrq2);
|
|
if (i >= Count)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
//
|
|
// return the ptr to this wonderful structure of goodies
|
|
//
|
|
Count = i;
|
|
BuffSize = sizeof(tCONNECTION_LIST) + Count*NameSize;
|
|
|
|
//
|
|
// Is our status buffer size greater then the user's buffer?
|
|
// Set the Count value based on the number of connections
|
|
// actually being returned
|
|
//
|
|
if (BuffSize > *pSize)
|
|
{
|
|
//
|
|
// Recalc how many names will fit
|
|
// tCONNECTION_LIST already contains space for 1 tCONNECTION
|
|
// structure, but we will not include it in our calculations
|
|
// -- rather we will leave it as an overflow check
|
|
//
|
|
if (*pSize <= sizeof(tCONNECTION_LIST))
|
|
{
|
|
Count = 0 ;
|
|
}
|
|
else
|
|
{
|
|
Count = (*pSize - sizeof(tCONNECTION_LIST)) / NameSize ;
|
|
}
|
|
}
|
|
|
|
pConnList->ConnectionCount = Count;
|
|
*pSize = BuffSize;
|
|
|
|
return status;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DelayedNbtResyncRemoteCache(
|
|
IN PVOID Unused1,
|
|
IN PVOID Unused2,
|
|
IN PVOID Unused3,
|
|
IN tDEVICECONTEXT *Unused4
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine creates a list of netbios connections and returns them to the
|
|
client. It is used by the "NbtStat" console application.
|
|
It cannot be called with any lock or NbtConfig.Resource held!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
tTIMERQENTRY TimerEntry = {0};
|
|
LONG i;
|
|
LONG lRetcode;
|
|
PUCHAR LmHostsPath;
|
|
|
|
CTEPagedCode();
|
|
//
|
|
// calling this routine N+1 times should remove all names from the remote
|
|
// hash table - N to count down the TimedOutCount to zero and then
|
|
// one more to remove the name
|
|
//
|
|
RemoteHashTimeout(NbtConfig.pRemoteHashTbl,NULL,&TimerEntry);
|
|
RemovePreloads(); // now remove any preloaded entries
|
|
|
|
// now reload the preloads
|
|
#ifndef VXD
|
|
//
|
|
// The NbtConfig.pLmHosts path can change if the registry is
|
|
// read during this interval
|
|
// We cannot acquire the ResourceLock here since reading the
|
|
// LmHosts file might result in File operations + network reads
|
|
// that could cause a deadlock (Worker threads / ResourceLock)!
|
|
// Best solution at this time is to copy the path onto a local
|
|
// buffer under the Resource lock, and then try to read the file!
|
|
// Bug # 247429
|
|
//
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
if ((!NbtConfig.pLmHosts) ||
|
|
(!(LmHostsPath = NbtAllocMem ((strlen(NbtConfig.pLmHosts)+1), NBT_TAG2('23')))))
|
|
{
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
return;
|
|
}
|
|
|
|
CTEMemCopy (LmHostsPath, NbtConfig.pLmHosts, (strlen(NbtConfig.pLmHosts)+1));
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
lRetcode = PrimeCache(LmHostsPath, NULL, MAX_RECURSE_DEPTH, NULL);
|
|
|
|
CTEMemFree(LmHostsPath);
|
|
|
|
return;
|
|
#else
|
|
lRetcode = PrimeCache(NbtConfig.pLmHosts, NULL, MAX_RECURSE_DEPTH, NULL);
|
|
//
|
|
// check if things didn't go well (InDos was set etc.)
|
|
//
|
|
if (lRetcode == -1)
|
|
{
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
#endif // !VXD
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtQueryBcastVsWins(
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
OUT PVOID *ppBuffer,
|
|
IN OUT PLONG pSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine creates a list of netbios names that have been resolved
|
|
via broadcast and returns them along with the count of names resolved
|
|
via WINS and via broadcast. It lets a user know which names are not
|
|
in WINS and the relative frequency of "misses" with WINS that resort
|
|
to broadcast.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
tNAMESTATS_INFO *pStats;
|
|
LONG Count;
|
|
tNAME *pDest;
|
|
tNAME *pSrc;
|
|
LONG Index;
|
|
|
|
//
|
|
// Is our status buffer size greater then the user's buffer?
|
|
//
|
|
if ( sizeof(tNAMESTATS_INFO) > *pSize )
|
|
{
|
|
return (STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
pStats = NbtAllocMem(sizeof(tNAMESTATS_INFO),NBT_TAG('Q'));
|
|
if ( !pStats )
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
|
|
// Fill out the adapter status structure with zeros first
|
|
CTEZeroMemory((PVOID)pStats,sizeof(tNAMESTATS_INFO));
|
|
CTEMemCopy(pStats,&NameStatsInfo,FIELD_OFFSET(tNAMESTATS_INFO,NamesReslvdByBcast) );
|
|
|
|
//
|
|
// re-order the names so that names are returned in a list of newest to
|
|
// oldest down the list.
|
|
//
|
|
Count = 0;
|
|
Index = NameStatsInfo.Index;
|
|
pDest = &pStats->NamesReslvdByBcast[SIZE_RESOLVD_BY_BCAST_CACHE-1];
|
|
|
|
while (Count < SIZE_RESOLVD_BY_BCAST_CACHE)
|
|
{
|
|
pSrc = &NameStatsInfo.NamesReslvdByBcast[Index++];
|
|
|
|
CTEMemCopy(pDest,pSrc,NETBIOS_NAME_SIZE);
|
|
|
|
pDest--;
|
|
if (Index >= SIZE_RESOLVD_BY_BCAST_CACHE)
|
|
{
|
|
Index = 0;
|
|
pSrc = NameStatsInfo.NamesReslvdByBcast;
|
|
}
|
|
else
|
|
{
|
|
pSrc++;
|
|
}
|
|
|
|
Count++;
|
|
}
|
|
|
|
//
|
|
// return the ptr to this wonderful structure of goodies
|
|
//
|
|
*ppBuffer = (PVOID)pStats;
|
|
*pSize = sizeof(tNAMESTATS_INFO);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
RemoveCachedAddresses(
|
|
tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
{
|
|
LONG i;
|
|
CTELockHandle OldIrq;
|
|
tNAMEADDR *pNameAddr;
|
|
tHASHTABLE *pHashTable;
|
|
PLIST_ENTRY pHead;
|
|
PLIST_ENTRY pEntry;
|
|
ULONG Count = 0;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
//
|
|
// go through the Remote table removing addresses resolved on this interface
|
|
//
|
|
pHashTable = NbtConfig.pRemoteHashTbl;
|
|
for (i=0; i < pHashTable->lNumBuckets; i++)
|
|
{
|
|
pHead = &pHashTable->Bucket[i];
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage);
|
|
pEntry = pEntry->Flink;
|
|
//
|
|
// do not delete scope entries, and do not delete names that
|
|
// that are still resolving, and do not delete names that are
|
|
// being used by someone (refcount > 1)
|
|
//
|
|
if (pNameAddr->RemoteCacheLen > pDeviceContext->AdapterNumber)
|
|
{
|
|
pNameAddr->pRemoteIpAddrs[pDeviceContext->AdapterNumber].IpAddress = 0;
|
|
if (pNameAddr->pRemoteIpAddrs[pDeviceContext->AdapterNumber].pOrigIpAddrs)
|
|
{
|
|
CTEMemFree(pNameAddr->pRemoteIpAddrs[pDeviceContext->AdapterNumber].pOrigIpAddrs);
|
|
pNameAddr->pRemoteIpAddrs[pDeviceContext->AdapterNumber].pOrigIpAddrs = NULL;
|
|
}
|
|
pNameAddr->AdapterMask &= ~pDeviceContext->AdapterMask;
|
|
|
|
#if DBG
|
|
/*
|
|
// MALAM_FIX -- Preloaded entries have 2 References!
|
|
if ((!pNameAddr->AdapterMask) &&
|
|
(!(pNameAddr->NameTypeState & NAMETYPE_SCOPE)))
|
|
{
|
|
RemoveEntryList (&pNameAddr->Linkage);
|
|
InsertTailList(&NbtConfig.StaleRemoteNames, &pNameAddr->Linkage);
|
|
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE);
|
|
}
|
|
*/
|
|
#endif // DBG
|
|
Count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
return (Count);
|
|
}
|
|
|
|
VOID
|
|
NbtAddressChangeResyncCacheTimeout(
|
|
PVOID pContext,
|
|
PVOID pContext2,
|
|
tTIMERQENTRY *pTimerQEntry
|
|
)
|
|
{
|
|
if (!pTimerQEntry)
|
|
{
|
|
return;
|
|
}
|
|
|
|
NTQueueToWorkerThread(NULL, DelayedNbtResyncRemoteCache, NULL, NULL, NULL, NULL, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#ifndef VXD
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NbtCheckSetNameAdapterInfo(
|
|
tDEVICECONTEXT *pDeviceContext,
|
|
ULONG IpAddress
|
|
)
|
|
{
|
|
LONG i;
|
|
CTELockHandle OldIrq;
|
|
tTIMERQENTRY *pTimerEntry;
|
|
tNAMEADDR *pNameAddr;
|
|
tHASHTABLE *pHashTable;
|
|
PLIST_ENTRY pHead, pEntry;
|
|
BOOLEAN fStartRefresh = FALSE;
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (pWinsInfo)
|
|
{
|
|
if (IpAddress)
|
|
{
|
|
//
|
|
// If there is no IP address, or the new IP address is the one Wins had preferred,
|
|
// use it
|
|
//
|
|
if (((IpAddress == pWinsInfo->IpAddress) || (!pWinsInfo->pDeviceContext)) &&
|
|
(NBT_VERIFY_HANDLE (pDeviceContext, NBT_VERIFY_DEVCONTEXT)))
|
|
{
|
|
pWinsInfo->pDeviceContext = pDeviceContext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pDeviceContext == pWinsInfo->pDeviceContext)
|
|
{
|
|
pWinsInfo->pDeviceContext = GetDeviceWithIPAddress(pWinsInfo->IpAddress);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// For a Netbiosless device notification, we are done!
|
|
//
|
|
if (pDeviceContext->DeviceType == NBT_DEVICE_NETBIOSLESS)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return;
|
|
}
|
|
|
|
if (IpAddress == 0)
|
|
{
|
|
//
|
|
// See if there were any names in conflict which we need to refresh!
|
|
//
|
|
pHashTable = NbtConfig.pLocalHashTbl;
|
|
for (i=0; i < pHashTable->lNumBuckets; i++)
|
|
{
|
|
pHead = &pHashTable->Bucket[i];
|
|
pEntry = pHead->Flink;
|
|
while (pEntry != pHead)
|
|
{
|
|
pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage);
|
|
pEntry = pEntry->Flink;
|
|
|
|
if (pNameAddr->ConflictMask & pDeviceContext->AdapterMask)
|
|
{
|
|
//
|
|
// Zero out the Conflicting adapter mask
|
|
// Restart Refreshing this name if there are no more
|
|
// adapters on which the name went into conflict
|
|
//
|
|
pNameAddr->ConflictMask &= (~pDeviceContext->AdapterMask);
|
|
if (!(pNameAddr->ConflictMask))
|
|
{
|
|
pNameAddr->RefreshMask = 0;
|
|
fStartRefresh = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (NodeType & BNODE)
|
|
{
|
|
//
|
|
// Stop the RefreshTimer if it is running!
|
|
//
|
|
if (pTimerEntry = NbtConfig.pRefreshTimer)
|
|
{
|
|
NbtConfig.pRefreshTimer = NULL;
|
|
StopTimer (pTimerEntry, NULL, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fStartRefresh = TRUE;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
if (fStartRefresh)
|
|
{
|
|
//
|
|
// ReRegister all the local names on this device with the WINS server
|
|
//
|
|
ReRegisterLocalNames(pDeviceContext, FALSE);
|
|
}
|
|
|
|
}
|
|
#endif // !VXD
|
|
|
|
#ifndef REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DelayedNbtProcessDhcpRequests(
|
|
PVOID Unused1,
|
|
PVOID Unused2,
|
|
PVOID Unused3,
|
|
PVOID pDevice)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Process each DHCP requests queued by NbtNewDhcpAddress
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
NONE
|
|
--*/
|
|
{
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
CTELockHandle OldIrq;
|
|
enum eTDI_ACTION action;
|
|
|
|
pDeviceContext = (tDEVICECONTEXT*)pDevice;
|
|
|
|
CTESpinLock(pDeviceContext, OldIrq);
|
|
action = pDeviceContext->DelayedNotification;
|
|
ASSERT(action == NBT_TDI_REGISTER || action == NBT_TDI_NOACTION);
|
|
if (action != NBT_TDI_NOACTION) {
|
|
pDeviceContext->DelayedNotification = NBT_TDI_BUSY;
|
|
}
|
|
CTESpinFree(pDeviceContext, OldIrq);
|
|
|
|
if (action != NBT_TDI_NOACTION) {
|
|
NbtNotifyTdiClients (pDeviceContext, action);
|
|
CTESpinLock(pDeviceContext, OldIrq);
|
|
pDeviceContext->DelayedNotification = NBT_TDI_NOACTION;
|
|
CTESpinFree(pDeviceContext, OldIrq);
|
|
KeSetEvent(&pDeviceContext->DelayedNotificationCompleteEvent, 0, FALSE);
|
|
}
|
|
|
|
//
|
|
// Call this routine at PASSIVE_LEVEL
|
|
//
|
|
NbtDownBootCounter();
|
|
}
|
|
|
|
VOID
|
|
StartProcessNbtDhcpRequests(
|
|
PVOID pContext,
|
|
PVOID pContext2,
|
|
tTIMERQENTRY *pTimerQEntry
|
|
)
|
|
{
|
|
/*
|
|
* if pTimerQEntry == NULL, we are being called from StopTimer which is called by NbtDestroyDevice
|
|
* DelayedNbtDeleteDevice will take care of notifying the clients and proper cleanup.
|
|
*/
|
|
if (pTimerQEntry) {
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
pDeviceContext = (tDEVICECONTEXT*)(pTimerQEntry->pDeviceContext);
|
|
if (!NT_SUCCESS(NTQueueToWorkerThread(NULL, DelayedNbtProcessDhcpRequests,
|
|
NULL, NULL, NULL, (tDEVICECONTEXT*)pTimerQEntry->pDeviceContext, FALSE))) {
|
|
pDeviceContext->DelayedNotification = NBT_TDI_NOACTION;
|
|
KeSetEvent(&pDeviceContext->DelayedNotificationCompleteEvent, 0, FALSE);
|
|
|
|
NbtDownBootCounter();
|
|
}
|
|
|
|
} else {
|
|
|
|
NbtDownBootCounter();
|
|
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtQueueTdiNotification (
|
|
tDEVICECONTEXT *pDeviceContext,
|
|
enum eTDI_ACTION action
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
1. Queue the DHCP address notifications into NbtConfig.DhcpNewAddressQList.
|
|
2. Start the worker thread if needed.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq1, OldIrq2;
|
|
|
|
if (NbtConfig.DhcpProcessingDelay == 0) {
|
|
NbtNotifyTdiClients (pDeviceContext, action);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (action == NBT_TDI_DEREGISTER) {
|
|
/*
|
|
* This should be done synchronously
|
|
*/
|
|
CTESpinLock(&NbtConfig.JointLock, OldIrq1);
|
|
CTESpinLock(pDeviceContext, OldIrq2);
|
|
if (pDeviceContext->DelayedNotification != NBT_TDI_BUSY) {
|
|
pDeviceContext->DelayedNotification = NBT_TDI_NOACTION;
|
|
KeSetEvent(&pDeviceContext->DelayedNotificationCompleteEvent, 0, FALSE);
|
|
} else {
|
|
ASSERT(!KeReadStateEvent(&pDeviceContext->DelayedNotificationCompleteEvent));
|
|
}
|
|
CTESpinFree(pDeviceContext, OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock, OldIrq1);
|
|
status = KeWaitForSingleObject(
|
|
&pDeviceContext->DelayedNotificationCompleteEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
ASSERT(status == STATUS_WAIT_0);
|
|
NbtNotifyTdiClients (pDeviceContext, NBT_TDI_DEREGISTER);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Queue NBT_TDI_REGISTER
|
|
*/
|
|
ASSERT(action == NBT_TDI_REGISTER);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock, OldIrq1);
|
|
CTESpinLock(pDeviceContext, OldIrq2);
|
|
if (pDeviceContext->DelayedNotification != NBT_TDI_NOACTION) {
|
|
/*
|
|
* Do nothing because another indication is going on
|
|
*/
|
|
ASSERT(pDeviceContext->DelayedNotification == NBT_TDI_REGISTER ||
|
|
pDeviceContext->DelayedNotification == NBT_TDI_BUSY);
|
|
CTESpinFree(pDeviceContext, OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock, OldIrq1);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
status = StartTimer (StartProcessNbtDhcpRequests, NbtConfig.DhcpProcessingDelay,
|
|
NULL, NULL, NULL, NULL, pDeviceContext, NULL, 0, TRUE);
|
|
if (NT_SUCCESS(status)) {
|
|
KeResetEvent(&pDeviceContext->DelayedNotificationCompleteEvent);
|
|
pDeviceContext->DelayedNotification = action;
|
|
NbtUpBootCounter();
|
|
}
|
|
CTESpinFree(pDeviceContext, OldIrq2);
|
|
CTESpinFree(&NbtConfig.JointLock, OldIrq1);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
NbtNotifyTdiClients (pDeviceContext, action);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#endif // REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtNewDhcpAddress(
|
|
tDEVICECONTEXT *pDeviceContext,
|
|
ULONG IpAddress,
|
|
ULONG SubnetMask)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes a DHCP request to set a new ip address
|
|
for this node. Dhcp may pass in a zero for the ip address first
|
|
meaning that it is about to change the IP address, so all connections
|
|
should be shut down.
|
|
It closes all connections with the transport and all addresses. Then
|
|
It reopens them at the new ip address.
|
|
|
|
Note for NETBIOSLESS:
|
|
I have modeled a disabled adapter after an adapter with no address. I considered
|
|
not creating the device, but then, without the device, there is no handle
|
|
for setup to contact the driver in order to enable it again.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN Attached;
|
|
ULONG Count, i;
|
|
|
|
CTEPagedCode();
|
|
|
|
CHECK_PTR(pDeviceContext);
|
|
#ifdef _NETBIOSLESS
|
|
if ( (!pDeviceContext->NetbiosEnabled) && (IpAddress != 0) )
|
|
{
|
|
IF_DBG(NBT_DEBUG_PNP_POWER)
|
|
KdPrint(("NbtNewDhcpAddr: %wZ disabling address\n",&pDeviceContext->ExportName));
|
|
IpAddress = 0;
|
|
SubnetMask = 0;
|
|
}
|
|
#endif
|
|
|
|
// grab the resource that synchronizes opening addresses and connections.
|
|
// to prevent the client from doing anything for a while
|
|
//
|
|
IF_DBG(NBT_DEBUG_PNP_POWER)
|
|
{
|
|
KdPrint(("Nbt.NbtNewDhcpAddress: %d.%d.%d.%d\n",
|
|
(IpAddress) & 0xFF,
|
|
(IpAddress>>8) & 0xFF,
|
|
(IpAddress>>16) & 0xFF,
|
|
(IpAddress>>24) & 0xFF));
|
|
}
|
|
|
|
if (IpAddress == 0)
|
|
{
|
|
if (pDeviceContext->IpAddress)
|
|
{
|
|
NbtTrace(NBT_TRACE_PNP, ("remove %!ipaddr! from device %p %Z",
|
|
pDeviceContext->IpAddress, pDeviceContext, &pDeviceContext->BindName));
|
|
#ifdef VXD
|
|
//
|
|
// The permanent name is a function of the MAC address so remove
|
|
// it since the Adapter is losing its Ip address
|
|
//
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
NbtRemovePermanentName(pDeviceContext);
|
|
#else
|
|
CloseAddressesWithTransport(pDeviceContext);
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
#endif // VXD
|
|
|
|
//
|
|
// Dhcp is has passed down a null IP address meaning that it has
|
|
// lost the lease on the previous address, so close all connections
|
|
// to the transport - pLowerConn.
|
|
//
|
|
DisableInboundConnections (pDeviceContext);
|
|
|
|
#ifndef VXD
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
NbtCheckSetNameAdapterInfo (pDeviceContext, IpAddress);
|
|
|
|
if (pDeviceContext->DeviceType == NBT_DEVICE_REGULAR)
|
|
{
|
|
|
|
NbtUpBootCounter();
|
|
|
|
#ifdef REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
|
NbtNotifyTdiClients (pDeviceContext, NBT_TDI_DEREGISTER);
|
|
#else
|
|
NbtQueueTdiNotification (pDeviceContext, NBT_TDI_DEREGISTER);
|
|
#endif // REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
|
|
|
NbtDownBootCounter();
|
|
|
|
}
|
|
#endif
|
|
//
|
|
// Resync the cache since we may need to reset the outgoing interface info
|
|
//
|
|
StartTimer (NbtAddressChangeResyncCacheTimeout, ADDRESS_CHANGE_RESYNC_CACHE_TIMEOUT,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, 0, FALSE);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ASSERT((signed)(pDeviceContext->TotalLowerConnections) >= 0);
|
|
|
|
NbtTrace(NBT_TRACE_PNP, ("new %!ipaddr! for device %p %Z",
|
|
IpAddress, pDeviceContext, &pDeviceContext->BindName));
|
|
CloseAddressesWithTransport(pDeviceContext);
|
|
CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
|
|
DisableInboundConnections (pDeviceContext);
|
|
|
|
// these are passed into here in the reverse byte order
|
|
//
|
|
IpAddress = htonl(IpAddress);
|
|
SubnetMask = htonl(SubnetMask);
|
|
//
|
|
// must be a new IP address, so open up the connections.
|
|
//
|
|
// get the ip address and open the required address
|
|
// objects with the underlying transport provider
|
|
|
|
CTEAttachFsp(&Attached, REF_FSP_NEWADDR);
|
|
Count = CountUpperConnections(pDeviceContext);
|
|
Count += NbtConfig.MinFreeLowerConnections;
|
|
|
|
for (i=0; i<2; i++) // Retry once!
|
|
{
|
|
status = NbtCreateAddressObjects (IpAddress, SubnetMask, pDeviceContext);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Allocate and set up connections with the transport provider.
|
|
while ((NT_SUCCESS(status)) && (Count--))
|
|
{
|
|
status = NbtOpenAndAssocConnection(pDeviceContext, NULL, NULL, '4');
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
NbtLogEvent (EVENT_NBT_CREATE_CONNECTION, status, Count);
|
|
KdPrint(("Nbt.NbtNewDhcpAddress: NbtOpenAndAssocConnection Failed <%x>\n",status));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Log an event only if it is a retry
|
|
//
|
|
if (i > 0)
|
|
{
|
|
NbtLogEvent (EVENT_NBT_CREATE_ADDRESS, status, i);
|
|
}
|
|
KdPrint(("Nbt.NbtNewDhcpAddress[i]: NbtCreateAddressObjects Failed, status=<%x>\n",i,status));
|
|
KdPrint(("Nbt.NbtNewDhcpAddress: IpAddress: %x, SubnetMask: %x, pDeviceContext: %x\n",
|
|
IpAddress, SubnetMask, pDeviceContext));
|
|
}
|
|
|
|
CTEDetachFsp(Attached, REF_FSP_NEWADDR);
|
|
|
|
CTEExReleaseResource(&NbtConfig.Resource);
|
|
|
|
#ifdef VXD
|
|
//
|
|
// Add the "permanent" name to the local name table. This is the IP
|
|
// address of the node padded out to 16 bytes with zeros.
|
|
//
|
|
NbtAddPermanentName(pDeviceContext);
|
|
#else
|
|
NbtCheckSetNameAdapterInfo (pDeviceContext, IpAddress);
|
|
if (pDeviceContext->DeviceType == NBT_DEVICE_REGULAR)
|
|
{
|
|
//
|
|
// Register this Device for our clients
|
|
//
|
|
NbtUpBootCounter();
|
|
|
|
#ifdef REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
|
NbtNotifyTdiClients (pDeviceContext, NBT_TDI_REGISTER);
|
|
#else
|
|
NbtQueueTdiNotification (pDeviceContext, NBT_TDI_REGISTER);
|
|
#endif // REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
|
|
|
NbtDownBootCounter();
|
|
}
|
|
|
|
//
|
|
// Resync the cache since we may need to reset the outgoing interface info
|
|
//
|
|
StartTimer (NbtAddressChangeResyncCacheTimeout, ADDRESS_CHANGE_RESYNC_CACHE_TIMEOUT,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, 0, FALSE);
|
|
#endif // VXD
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtDeleteLowerConn(
|
|
IN tLOWERCONNECTION *pLowerConn
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine attempts to delete a lower connection by closing it with the
|
|
transport and dereferencing it.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
if ((pLowerConn->Verify != NBT_VERIFY_LOWERCONN) ||
|
|
(pLowerConn->RefCount > 500))
|
|
{
|
|
ASSERT (0);
|
|
return status;
|
|
}
|
|
|
|
// remove the lower connection from the active queue and then delete it
|
|
//
|
|
pDeviceContext = pLowerConn->pDeviceContext;
|
|
CTESpinLock(pDeviceContext,OldIrq);
|
|
|
|
//
|
|
// The lower conn can get removed from the inactive list in OutOfRsrcKill (when we queue it on
|
|
// the OutofRsrc.ConnectionHead). Check the flag that indicates this connection was dequed then.
|
|
//
|
|
if (!pLowerConn->OutOfRsrcFlag)
|
|
{
|
|
RemoveEntryList(&pLowerConn->Linkage);
|
|
pLowerConn->Linkage.Flink = pLowerConn->Linkage.Blink = (PLIST_ENTRY)0x00009789;
|
|
|
|
//
|
|
// Setting this TRUE to prevent from running the OutOfRsrcKill again.
|
|
//
|
|
|
|
pLowerConn->bNoOutRsrcKill = TRUE;
|
|
}
|
|
|
|
CTESpinFree(pDeviceContext,OldIrq);
|
|
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CREATE, FALSE);
|
|
|
|
return(status);
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
DelayedWipeOutLowerconn(
|
|
IN tDGRAM_SEND_TRACKING *pUnused1,
|
|
IN PVOID pClientContext,
|
|
IN PVOID Unused2,
|
|
IN tDEVICECONTEXT *Unused3
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine does all the file close etc. that we couldn't do at dpc level
|
|
and then frees the memory.
|
|
|
|
Arguments:
|
|
|
|
pLowerConn - the lower connection to be wiped out
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
tLOWERCONNECTION *pLowerConn = (tLOWERCONNECTION*) pClientContext;
|
|
|
|
ASSERT(pLowerConn->Verify == NBT_VERIFY_LOWERCONN); // Verify LowerConn structure
|
|
|
|
// dereference the fileobject ptr
|
|
NTDereferenceObject((PVOID *)pLowerConn->pFileObject);
|
|
|
|
// close the lower connection with the transport
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.DelayedWipeOutLowerconn: Closing Handle %X -> %X\n",pLowerConn,pLowerConn->FileHandle));
|
|
|
|
NbtTdiCloseConnection(pLowerConn);
|
|
|
|
// Close the Address object too since outbound connections use unique
|
|
// addresses for each connection, whereas inbound connections all use
|
|
// the same address ( and we don't want to close that address ever ).
|
|
if (pLowerConn->pAddrFileObject)
|
|
{
|
|
// dereference the fileobject ptr
|
|
NTDereferenceObject((PVOID *)pLowerConn->pAddrFileObject);
|
|
NbtTdiCloseAddress(pLowerConn);
|
|
}
|
|
|
|
#ifndef VXD
|
|
// free the indicate buffer and the mdl that holds it
|
|
//
|
|
CTEMemFree(MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl));
|
|
IoFreeMdl(pLowerConn->pIndicateMdl);
|
|
#endif
|
|
|
|
pLowerConn->Verify += 10;
|
|
// now free the memory block tracking this connection
|
|
CTEMemFree((PVOID)pLowerConn);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NbtDereferenceClient(
|
|
IN tCLIENTELE *pClientEle
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine deletes a client element record (which points to a name
|
|
in the local hash table. If this is the last client element hooked to that
|
|
name then the name is deleted too - causing a name release to be sent out.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
tADDRESSELE *pAddress;
|
|
PIRP pIrp;
|
|
NTSTATUS status;
|
|
tNAMEADDR *pNameAddr;
|
|
tTIMERQENTRY *pTimer;
|
|
tDGRAM_SEND_TRACKING *pTracker;
|
|
COMPLETIONCLIENT pClientCompletion = NULL;
|
|
PVOID Context;
|
|
LIST_ENTRY *pClientEntry;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
tCLIENTELE *pClientEleTemp;
|
|
BOOLEAN fLastClientOnDevice = TRUE;
|
|
|
|
// lock the JointLock
|
|
// so we can delete the client knowing that no one has a spin lock
|
|
// pending on the client - basically use the Joint spin lock to
|
|
// coordinate access to the AddressHead - NbtConnectionList also locks
|
|
// the JointLock to scan the AddressHead list
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
|
|
ASSERT(pClientEle->RefCount);
|
|
ASSERT ((pClientEle->Verify==NBT_VERIFY_CLIENT) || (pClientEle->Verify==NBT_VERIFY_CLIENT_DOWN));
|
|
|
|
if (--pClientEle->RefCount)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
// return pending because we haven't been able to close the client
|
|
// completely yet
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Unlink the Client in this routine after the reference count has
|
|
// gone to zero since the DgramRcv code may need to find the client in
|
|
// the Address client list when it is distributing a single received
|
|
// dgram to several clients.
|
|
//
|
|
pIrp = pClientEle->pIrp;
|
|
pDeviceContext = pClientEle->pDeviceContext;
|
|
pAddress = pClientEle->pAddress;
|
|
pNameAddr = pAddress->pNameAddr;
|
|
|
|
CTESpinLock(pAddress,OldIrq1); // Need to acquire AddressEle lock -- Bug#: 231853
|
|
RemoveEntryList(&pClientEle->Linkage);
|
|
|
|
//
|
|
// If there is no other client registered on this device, then
|
|
// clear the adapter mask, and mark the release mask
|
|
//
|
|
pClientEntry = &pAddress->ClientHead;
|
|
while ((pClientEntry = pClientEntry->Flink) != &pAddress->ClientHead)
|
|
{
|
|
pClientEleTemp = CONTAINING_RECORD (pClientEntry,tCLIENTELE,Linkage);
|
|
if (pClientEleTemp->pDeviceContext == pDeviceContext)
|
|
{
|
|
fLastClientOnDevice = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
CTESpinFree(pAddress,OldIrq1);
|
|
|
|
if (pNameAddr)
|
|
{
|
|
//
|
|
// If there is any timer running on this Client's device,
|
|
// stop it now!
|
|
//
|
|
if ((pTimer = pNameAddr->pTimer) &&
|
|
(pTracker = pTimer->Context) &&
|
|
(pTracker->pDeviceContext == pDeviceContext))
|
|
{
|
|
pNameAddr->pTimer = NULL;
|
|
StopTimer(pTimer,&pClientCompletion,&Context);
|
|
}
|
|
|
|
if (fLastClientOnDevice)
|
|
{
|
|
if (IsDeviceNetbiosless(pDeviceContext))
|
|
{
|
|
pNameAddr->NameFlags &= ~NAME_REGISTERED_ON_SMBDEV;
|
|
}
|
|
else
|
|
{
|
|
pNameAddr->AdapterMask &= ~pDeviceContext->AdapterMask;
|
|
pNameAddr->ConflictMask &= ~pDeviceContext->AdapterMask; // in case there was a conflict
|
|
pNameAddr->ReleaseMask |= pDeviceContext->AdapterMask;
|
|
}
|
|
}
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
#ifdef _PNP_POWER_
|
|
//
|
|
// Remove this name from the Adapter's Wakeup Pattern list (if set)
|
|
//
|
|
if ((pNameAddr->Name[0] != '*') &&
|
|
(pNameAddr->Name[NETBIOS_NAME_SIZE-1] == SPECIAL_SERVER_SUFFIX))
|
|
{
|
|
pDeviceContext->NumServers--;
|
|
CheckSetWakeupPattern (pDeviceContext, pNameAddr->Name, FALSE);
|
|
}
|
|
#endif // _PNP_POWER_
|
|
}
|
|
else
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
if (pClientCompletion)
|
|
{
|
|
(*pClientCompletion)(Context, STATUS_TIMEOUT);
|
|
}
|
|
//
|
|
// The Connection Q Should be Empty otherwise we shouldn't get to this routine
|
|
//
|
|
ASSERT(IsListEmpty(&pClientEle->ConnectActive));
|
|
ASSERT(IsListEmpty(&pClientEle->ConnectHead));
|
|
ASSERT(IsListEmpty(&pClientEle->ListenHead));
|
|
ASSERT(IsListEmpty(&pClientEle->SndDgrams)); // the Datagram Q should also be empty
|
|
|
|
// check if there are more clients attached to the address, or can we
|
|
// delete the address too.
|
|
//
|
|
NBT_DEREFERENCE_ADDRESS (pAddress, REF_ADDR_NEW_CLIENT);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceClient: Delete Client Object %X\n",pClientEle));
|
|
//
|
|
// if their is a client irp, complete now. When the permanent name is
|
|
// released there is no client irp.
|
|
//
|
|
// CHANGED:
|
|
// Do not hold up the client's irp until the name has released on the
|
|
// net. It is simpler to just complete it now
|
|
//
|
|
if (pIrp)
|
|
{
|
|
// complete the client's close address irp
|
|
CTEIoComplete(pIrp,STATUS_SUCCESS,0);
|
|
}
|
|
|
|
//
|
|
// free the memory associated with the client element
|
|
//
|
|
pClientEle->Verify += 10;
|
|
CTEMemFree((PVOID)pClientEle);
|
|
|
|
return;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtDereferenceAddress(
|
|
IN tADDRESSELE *pAddress,
|
|
IN ULONG Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine deletes an Address element record (which points to a name
|
|
in the local hash table). A name release is sent on the wire for this name.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
COMPLETIONCLIENT pClientCompletion = NULL;
|
|
PVOID pTimerContext;
|
|
ULONG SaveState;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
tTIMERQENTRY *pTimer;
|
|
|
|
// lock the hash table so another client cannot add a reference to this
|
|
// name before we delete it. We need the JointLock to keep the name
|
|
// refresh mechanism from finding the name in the list just as
|
|
// we are about to remove it (i.e. to synchronize with the name refresh
|
|
// code).
|
|
//
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
CTESpinLock(pAddress,OldIrq);
|
|
|
|
ASSERT(pAddress->RefCount);
|
|
ASSERT (NBT_VERIFY_HANDLE (pAddress, NBT_VERIFY_ADDRESS));
|
|
if (pAddress->pNameAddr)
|
|
{
|
|
ASSERT (NBT_VERIFY_HANDLE (pAddress->pNameAddr, LOCAL_NAME));
|
|
}
|
|
|
|
if (--pAddress->RefCount)
|
|
{
|
|
CTESpinFree(pAddress,OldIrq);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// remove the address object from the list of addresses tied to the
|
|
// device context for the adapter
|
|
//
|
|
RemoveEntryList(&pAddress->Linkage);
|
|
ASSERT(IsListEmpty(&pAddress->ClientHead)); // The ClientHead should be empty
|
|
|
|
CTESpinFree(pAddress,OldIrq);
|
|
|
|
if (pAddress->pNameAddr)
|
|
{
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceAddress: Freeing address object for <%-16.16s:%x>\n",
|
|
pAddress->pNameAddr->Name,pAddress->pNameAddr->Name[NETBIOS_NAME_SIZE-1] ));
|
|
|
|
pAddress->pNameAddr->pAddressEle = NULL;
|
|
|
|
//
|
|
// Release name on the network
|
|
// change the name state in the hash table since it is being released
|
|
//
|
|
SaveState = pAddress->pNameAddr->NameTypeState;
|
|
pAddress->pNameAddr->NameTypeState &= ~NAME_STATE_MASK;
|
|
pAddress->pNameAddr->NameTypeState |= STATE_CONFLICT;
|
|
pAddress->pNameAddr->ReleaseMask |= pAddress->pNameAddr->AdapterMask;
|
|
pAddress->pNameAddr->AdapterMask = 0;
|
|
|
|
//
|
|
// check for any timers outstanding against the hash table entry - there shouldn't
|
|
// be any timers though
|
|
//
|
|
if (pTimer = pAddress->pNameAddr->pTimer)
|
|
{
|
|
pAddress->pNameAddr->pTimer = NULL;
|
|
status = StopTimer(pTimer, &pClientCompletion, &pTimerContext);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceAddress: StopTimer returned Context <%x>\n", pTimerContext));
|
|
|
|
if (pClientCompletion)
|
|
{
|
|
ASSERT (pClientCompletion != NameReleaseDone);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
(*pClientCompletion) (pTimerContext, STATUS_TIMEOUT);
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
}
|
|
}
|
|
|
|
// only release the name on the net if it was not in conflict first
|
|
// This prevents name releases going out for names that were not actually
|
|
// claimed. Also, quick add names are not released on the net either.
|
|
//
|
|
if (!(SaveState & (STATE_CONFLICT | NAMETYPE_QUICK)) &&
|
|
(pAddress->pNameAddr->Name[0] != '*') &&
|
|
(pDeviceContext = GetAndRefNextDeviceFromNameAddr (pAddress->pNameAddr)))
|
|
{
|
|
//
|
|
// The pNameAddr has to stay around until the NameRelease has completed
|
|
//
|
|
NBT_REFERENCE_NAMEADDR (pAddress->pNameAddr, REF_NAME_RELEASE);
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
status = ReleaseNameOnNet (pAddress->pNameAddr,
|
|
NbtConfig.pScope,
|
|
NameReleaseDone,
|
|
NodeType,
|
|
pDeviceContext);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
#ifndef VXD
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_GET_REF, TRUE);
|
|
#endif // !VXD
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
NBT_DEREFERENCE_NAMEADDR (pAddress->pNameAddr, REF_NAME_RELEASE, TRUE);
|
|
}
|
|
}
|
|
|
|
NBT_DEREFERENCE_NAMEADDR (pAddress->pNameAddr, REF_NAME_LOCAL, TRUE);
|
|
}
|
|
|
|
//
|
|
// Now, cleanup the Address info (we are still holding the JointLock)
|
|
//
|
|
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
|
|
|
|
// free the memory associated with the address element
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("NBt: Deleteing Address Obj after name release on net %X\n",pAddress));
|
|
NbtFreeAddressObj(pAddress);
|
|
|
|
//
|
|
// the name has been deleted, so return success
|
|
//
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NbtDereferenceName(
|
|
IN tNAMEADDR *pNameAddr,
|
|
IN ULONG RefContext,
|
|
IN BOOLEAN fLocked
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine dereferences and possibly deletes a name element record by first unlinking from the
|
|
list it is in, and then freeing the memory if it is a local name. Remote
|
|
names remain in a circular list for reuse.
|
|
The JOINTLOCK may have been taken before calling this routine.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle OldIrq;
|
|
ULONG i;
|
|
|
|
if (!fLocked)
|
|
{
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
ASSERT (NBT_VERIFY_HANDLE2 (pNameAddr, LOCAL_NAME, REMOTE_NAME));
|
|
ASSERT (pNameAddr->RefCount);
|
|
ASSERT (pNameAddr->References[RefContext]--);
|
|
|
|
if (--pNameAddr->RefCount)
|
|
{
|
|
if (!fLocked)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceName[%s]: Freeing Name=<%16.16s:%x> %x\n",
|
|
(pNameAddr->Verify == REMOTE_NAME ? "Remote" : "Local"),
|
|
pNameAddr->Name, pNameAddr->Name[15], pNameAddr));
|
|
|
|
//
|
|
// remove from the hash table, could be set to NULL by DestroyHashTable
|
|
//
|
|
if (pNameAddr->Linkage.Flink && pNameAddr->Linkage.Blink) {
|
|
RemoveEntryList(&pNameAddr->Linkage);
|
|
} else {
|
|
// Both should be NULL
|
|
ASSERT(pNameAddr->Linkage.Flink == pNameAddr->Linkage.Blink);
|
|
}
|
|
|
|
if (pNameAddr->Verify == LOCAL_NAME)
|
|
{
|
|
ASSERT(!pNameAddr->pTimer);
|
|
ASSERT(NULL == pNameAddr->FQDN.Buffer);
|
|
ASSERT(0 == pNameAddr->FQDN.Length);
|
|
}
|
|
//
|
|
// if it is an internet group name it has a list of ip addresses and that
|
|
// memory block must be deleted
|
|
//
|
|
else if (pNameAddr->Verify == REMOTE_NAME)
|
|
{
|
|
NbtConfig.NumNameCached--;
|
|
|
|
//
|
|
// TBD: keep an exact number of the entries in the hash table.
|
|
//
|
|
// It can be negative since the pNameAddr may not be in hash table
|
|
// (It could be in the to-be-resolved list)
|
|
// for now, it is ok since exact number isn't critical.
|
|
//
|
|
if (NbtConfig.NumNameCached < 0) {
|
|
NbtConfig.NumNameCached = 0;
|
|
}
|
|
if (pNameAddr->NameAddFlags == (NAME_RESOLVED_BY_LMH_P | NAME_ADD_INET_GROUP))
|
|
{
|
|
if (pNameAddr->pLmhSvcGroupList)
|
|
{
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceName: Freeing Pre-loaded Internet Group Name Memory = <%p>\n",
|
|
pNameAddr->pLmhSvcGroupList));
|
|
CTEMemFree((PVOID)pNameAddr->pLmhSvcGroupList);
|
|
}
|
|
}
|
|
if (pNameAddr->pRemoteIpAddrs)
|
|
{
|
|
for (i=0; i<pNameAddr->RemoteCacheLen; i++)
|
|
{
|
|
if (pNameAddr->pRemoteIpAddrs[i].pOrigIpAddrs)
|
|
{
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceName: Freeing Internet Group Name Memory = <%p>\n",
|
|
pNameAddr->pRemoteIpAddrs[i].pOrigIpAddrs));
|
|
CTEMemFree((PVOID)pNameAddr->pRemoteIpAddrs[i].pOrigIpAddrs);
|
|
}
|
|
}
|
|
CTEMemFree ((PVOID)pNameAddr->pRemoteIpAddrs);
|
|
}
|
|
}
|
|
|
|
if (pNameAddr->pIpAddrsList)
|
|
{
|
|
CTEMemFree((PVOID)pNameAddr->pIpAddrsList);
|
|
}
|
|
if (NULL != pNameAddr->FQDN.Buffer) {
|
|
CTEMemFree((PVOID)pNameAddr->FQDN.Buffer);
|
|
pNameAddr->FQDN.Buffer = NULL;
|
|
pNameAddr->FQDN.Length = 0;
|
|
pNameAddr->FQDN.MaximumLength = 0;
|
|
}
|
|
|
|
//
|
|
// free the memory now
|
|
//
|
|
// #if DBG
|
|
pNameAddr->Verify += 10;
|
|
// #endif // DBG
|
|
CTEMemFree((PVOID)pNameAddr);
|
|
|
|
if (!fLocked)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NbtDereferenceConnection(
|
|
IN tCONNECTELE *pConnEle,
|
|
IN ULONG RefContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine dereferences and possibly deletes a connection element record.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
TDI_STATUS - status of the request
|
|
|
|
--*/
|
|
{
|
|
PCTE_IRP pIrp;
|
|
CTELockHandle OldIrq;
|
|
CTELockHandle OldIrq1;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
PLIST_ENTRY pEntry;
|
|
|
|
CHECK_PTR(pConnEle);
|
|
|
|
// grab the lock of the item that contains the one we are trying to
|
|
// dereference and possibly delete. This prevents anyone from incrementing
|
|
// the count in between decrementing it and checking it for zero and deleting
|
|
// it if it is zero.
|
|
|
|
CTESpinLock(pConnEle,OldIrq);
|
|
|
|
ASSERT (pConnEle->RefCount > 0) ; // Check for too many derefs
|
|
ASSERT ((pConnEle->Verify==NBT_VERIFY_CONNECTION) || (pConnEle->Verify==NBT_VERIFY_CONNECTION_DOWN));
|
|
ASSERT (pConnEle->References[RefContext]--);
|
|
|
|
if (--pConnEle->RefCount)
|
|
{
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
return;
|
|
}
|
|
|
|
ASSERT ((pConnEle->state <= NBT_CONNECTING) || (pConnEle->state > NBT_DISCONNECTING));
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceConnection: Delete Connection Object %X\n",pConnEle));
|
|
|
|
#ifndef VXD
|
|
IoFreeMdl(pConnEle->pNewMdl);
|
|
//
|
|
// Clear the context value in the Fileobject so that if this connection
|
|
// is used again (erroneously) it will not pass the VerifyHandle test
|
|
//
|
|
if (pIrp = pConnEle->pIrpClose) // the close irp should be held in here
|
|
{
|
|
NTClearFileObjectContext(pConnEle->pIrpClose);
|
|
}
|
|
#endif
|
|
|
|
pDeviceContext = pConnEle->pDeviceContext;
|
|
|
|
CTESpinFree(pConnEle,OldIrq);
|
|
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
CTESpinLock(pDeviceContext,OldIrq1);
|
|
|
|
// For outbound connections the lower connection is deleted in hndlrs.c
|
|
// For inbound connections, the lower connection is put back on the free
|
|
// list in hndlrs.c and one from that list is deleted here. Therefore
|
|
// delete a lower connection in this list if the connection is inbound.
|
|
//
|
|
if ((pDeviceContext->NumFreeLowerConnections > NbtConfig.MinFreeLowerConnections) &&
|
|
(pDeviceContext->NumFreeLowerConnections > (pDeviceContext->TotalLowerConnections/2)))
|
|
{
|
|
// get a lower connection from the free list and close it with the
|
|
// transport.
|
|
//
|
|
pEntry = RemoveHeadList(&pConnEle->pDeviceContext->LowerConnFreeHead);
|
|
pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage);
|
|
InterlockedDecrement (&pDeviceContext->NumFreeLowerConnections);
|
|
|
|
// close the lower connection with the transport
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceConnection: Closing LowerConn %X -> %X\n",
|
|
pLowerConn,pLowerConn->FileHandle));
|
|
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CREATE, TRUE);
|
|
}
|
|
CTESpinFree(pDeviceContext,OldIrq1);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
FreeConnectionObj(pConnEle); // free the memory block associated with the conn element
|
|
|
|
//
|
|
// The client may have sent down a close before NBT was done with the
|
|
// pConnEle, so Pending was returned and the irp stored in the pCOnnEle
|
|
// structure. Now that the structure is fully dereferenced, we can complete the irp.
|
|
//
|
|
if (pIrp)
|
|
{
|
|
CTEIoComplete(pIrp,STATUS_SUCCESS,0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NbtDereferenceLowerConnection(
|
|
IN tLOWERCONNECTION *pLowerConn,
|
|
IN ULONG RefContext,
|
|
IN BOOLEAN fJointLockHeld
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This Routine decrements the reference count on a Lower Connection element and
|
|
if the value is zero, deletes the connection.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle OldIrq1;
|
|
tCONNECTELE *pConnEle;
|
|
NTSTATUS status;
|
|
|
|
CTESpinLock(pLowerConn,OldIrq1);
|
|
|
|
ASSERT (pLowerConn->Verify == NBT_VERIFY_LOWERCONN); // Verify LowerConn structure
|
|
ASSERT (pLowerConn->References[RefContext]--);
|
|
if(--pLowerConn->RefCount)
|
|
{
|
|
CTESpinFree(pLowerConn,OldIrq1);
|
|
return;
|
|
}
|
|
InterlockedDecrement (&pLowerConn->pDeviceContext->TotalLowerConnections);
|
|
|
|
IF_DBG(NBT_DEBUG_NAMESRV)
|
|
KdPrint(("Nbt.NbtDereferenceLowerConnection: Delete Lower Connection Object %p\n",pLowerConn));
|
|
|
|
//
|
|
// it's possible that transport may indicate before we run the code
|
|
// in DelayedWipeOutLowerconn. If that happens, we don't want to run this
|
|
// code again ( which will queue this to worker thread again!)
|
|
// So, bump it up to some large value
|
|
//
|
|
pLowerConn->RefCount = 1000;
|
|
|
|
if (NBT_VERIFY_HANDLE2((pConnEle = pLowerConn->pUpperConnection), NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
|
|
{
|
|
//
|
|
// We still have a linked UpperConnection block, so unlink it,
|
|
//
|
|
SET_STATE_UPPER (pLowerConn->pUpperConnection, NBT_DISCONNECTED);
|
|
NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn);
|
|
}
|
|
|
|
CTESpinFree(pLowerConn,OldIrq1);
|
|
|
|
//
|
|
// let's come back and do this later since we may be at dpc now
|
|
//
|
|
NTQueueToWorkerThread(
|
|
&pLowerConn->WorkItemCleanUpAndWipeOut,
|
|
DelayedWipeOutLowerconn,
|
|
NULL,
|
|
pLowerConn,
|
|
NULL,
|
|
NULL,
|
|
fJointLockHeld
|
|
);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NbtDereferenceTracker(
|
|
IN tDGRAM_SEND_TRACKING *pTracker,
|
|
IN BOOLEAN fLocked
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up a Tracker block and puts it back on the free
|
|
queue. The JointLock Spin lock should be held before calling this
|
|
routine to coordinate access to the tracker ref count.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - success or not
|
|
|
|
--*/
|
|
{
|
|
CTELockHandle OldIrq;
|
|
|
|
if (!fLocked)
|
|
{
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
|
|
if (--pTracker->RefCount == 0)
|
|
{
|
|
// the datagram header may have already been freed
|
|
//
|
|
FreeTracker(pTracker, RELINK_TRACKER);
|
|
}
|
|
|
|
if (!fLocked)
|
|
{
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
IsLocalAddress(
|
|
tIPADDRESS IpAddress
|
|
)
|
|
{
|
|
tDEVICECONTEXT *pDeviceContext = NULL;
|
|
ULONG IPInterfaceContext = 0xffff, Metric = 0;
|
|
ULONG LoopbackIPInterfaceContext = 0xffff;
|
|
CTELockHandle OldIrq = 0;
|
|
PIPFASTQUERY pFastQuery;
|
|
|
|
if (0 == IpAddress) {
|
|
return TRUE;
|
|
}
|
|
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
|
if (IsListEmpty(&NbtConfig.DeviceContexts)) {
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
return FALSE;
|
|
}
|
|
|
|
pDeviceContext = CONTAINING_RECORD(NbtConfig.DeviceContexts.Flink, tDEVICECONTEXT, Linkage);
|
|
pFastQuery = pDeviceContext->pFastQuery;
|
|
NBT_REFERENCE_DEVICE (pDeviceContext, REF_DEV_FIND_REF, TRUE);
|
|
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
|
|
|
/*
|
|
* Hack!!!
|
|
*/
|
|
if (NbtConfig.LoopbackIfContext == 0xffff) {
|
|
(pFastQuery)(htonl(INADDR_LOOPBACK), &LoopbackIPInterfaceContext, &Metric);
|
|
if (LoopbackIPInterfaceContext != 0xffff) {
|
|
NbtConfig.LoopbackIfContext = LoopbackIPInterfaceContext;
|
|
}
|
|
}
|
|
(pFastQuery)(htonl(IpAddress), &IPInterfaceContext, &Metric);
|
|
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_FIND_REF, FALSE);
|
|
|
|
if (NbtConfig.LoopbackIfContext == 0xffff || IPInterfaceContext == 0xffff) {
|
|
return FALSE;
|
|
}
|
|
return (IPInterfaceContext == NbtConfig.LoopbackIfContext);
|
|
}
|
|
|
|
BOOL
|
|
IsSmbBoundToOutgoingInterface(
|
|
IN tIPADDRESS IpAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns TRUE if the destionation can be reached through
|
|
an interface to which the SmbDevice is bound. Otherwise it returns FALSE
|
|
|
|
|
|
Arguments:
|
|
|
|
IpAddress The address of destination
|
|
|
|
Return Value:
|
|
|
|
TRUE/FALSE
|
|
|
|
--*/
|
|
{
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
BOOL bBind;
|
|
|
|
if (IpAddress == INADDR_LOOPBACK) {
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* First check if this is a local address
|
|
* return TRUE if it is a local address
|
|
*/
|
|
if (IsLocalAddress(IpAddress)) {
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* This is not a local IP. Check with TCP
|
|
*/
|
|
pDeviceContext = GetDeviceFromInterface (htonl(IpAddress), TRUE);
|
|
bBind = (pDeviceContext && (pDeviceContext->AdapterMask & NbtConfig.ClientMask));
|
|
|
|
if (pDeviceContext) {
|
|
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_OUT_FROM_IP, FALSE);
|
|
}
|
|
|
|
return bBind;
|
|
}
|
|
|
|
// ========================================================================
|
|
// End of file.
|