diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/nbt | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/nbt')
103 files changed, 85029 insertions, 0 deletions
diff --git a/private/ntos/nbt/dirs b/private/ntos/nbt/dirs new file mode 100644 index 000000000..eac81d999 --- /dev/null +++ b/private/ntos/nbt/dirs @@ -0,0 +1,25 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + nbt \ + nt diff --git a/private/ntos/nbt/inc/ctemacro.h b/private/ntos/nbt/inc/ctemacro.h new file mode 100644 index 000000000..817628d6c --- /dev/null +++ b/private/ntos/nbt/inc/ctemacro.h @@ -0,0 +1,1468 @@ +//---------------------------------------------------------------------------- +// +// ctemacro.c +// +// This file contains macros for the common transport environment - similar +// to the way that ctestuff.c contains common procedures. +// +#ifndef _CTEMACRO_H_ +#define _CTEMACRO_H_ + + +#ifndef VXD +#define NT 1 +#include <cxport.h> +#endif + +#ifndef VXD +char LastLockFile[255] ; +int LastLockLine ; +char LastUnlockFile[255] ; +int LastUnlockLine ; +#endif + +//---------------------------------------------------------------------------- +// VOID +// NTDereferenceObject( +// PFILE_OBJECT pFileObject +// ) + +/*++ +Routine Description: + + This routine dereferences an object. + + +--*/ +#ifdef VXD +// +// No equivalent for Vxd +// +#define NTDereferenceObject( fileobject ) +#else //VXD +#define NTDereferenceObject( fileobject ) ObDereferenceObject( fileobject) +#endif + +//---------------------------------------------------------------------------- +// VOID +// CHECK_PTR( +// PVOID SpinLock, +// ) + +/*++ +Routine Description: + + This routine checks if a ptr points to freed memory + + +--*/ + +#if DBG +#define CHECK_PTR(_Ptr) \ + ASSERT((ULONG)((PULONG)_Ptr + 16) != 0xd1000000); +#else + +#define CHECK_PTR(_Ptr) +#endif + + +#ifndef VXD +/*++ + This macro verifies that an IRP passed to IoCallDriver() is + set up to call the CompletionRoutine in all cases. +--*/ +#if DBG +#define CHECK_COMPLETION(__pIrp)\ + {\ + PIO_STACK_LOCATION __pIrpSp = IoGetNextIrpStackLocation(__pIrp);\ + BOOL CompletionWillBeCalled =\ + ( __pIrpSp->Control & ( SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL ) )\ + == ( SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL );\ + ASSERT ( CompletionWillBeCalled );\ + } +#else // DBG +#define CHECK_COMPLETION(__pIrp) +#endif // DBG +#endif // VXD + +// +// Macros +// + +//++ +// +// LARGE_INTEGER +// CTEConvertMillisecondsTo100ns( +// IN LARGE_INTEGER MsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// MsTime - Time in milliseconds. +// +// Return Value: +// +// Time in hundreds of nanoseconds. +// +//-- + +#define CTEConvertMillisecondsTo100ns(MsTime) \ + RtlExtendedIntegerMultiply(MsTime, 10000) + + +//++ +// +// LARGE_INTEGER +// CTEConvert100nsToMilliseconds( +// IN LARGE_INTEGER HnsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// HnsTime - Time in hundreds of nanoseconds. +// +// Return Value: +// +// Time in milliseconds. +// +//-- + + // Used in the conversion of 100ns times to milliseconds. +static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; + +#define SHIFT10000 13 +extern LARGE_INTEGER Magic10000; + +#define CTEConvert100nsToMilliseconds(HnsTime) \ + RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) + +//---------------------------------------------------------------------------- +// +// CTELockHandle +// + +#ifndef VXD + + // + // The Spinlock structure is different between the NT driver and the VXD + // driver. This macro is defined in cxport.h when compiling as a VXD. + // + #define CTELockHandle KIRQL +#endif + +//---------------------------------------------------------------------------- +// VOID +// CTESpinLock( +// tCONNECTELE Structure, +// CTELockHandle OldIrqLevel +// ) + +/*++ +Routine Description: + + This Routine acquires a spin lock. + +Arguments: + + Size - the number of bytes to allocate + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ +#ifndef VXD +#if DBG +#define CTESpinLock(DataStruct,OldIrqLevel) \ +{ \ + AcquireSpinLockDebug(&(DataStruct)->SpinLock,&OldIrqLevel,(DataStruct)->LockNumber); \ + strcpy( LastLockFile, __FILE__ ) ; \ + LastLockLine = __LINE__ ; \ +} +#else +#define CTESpinLock(DataStruct,OldIrqLevel) \ +{ \ + CTEGetLock(&(DataStruct)->SpinLock,&(OldIrqLevel)); \ +} +#endif +#else +#ifdef DEBUG + +#define CTESpinLock(DataStruct,OldIrqLevel) \ +{ \ + CTEGetLock( &(DataStruct)->SpinLock, &OldIrqLevel ) ; \ +} + +#else +#define CTESpinLock(DataStruct,OldIrqLevel) +#endif // !DEBUG +#endif + + +//---------------------------------------------------------------------------- +// VOID +// CTESpinFree( +// PVOID SpinLock, +// CTELockHandle OldIrqLevel +// ) +/*++ +Routine Description: + + This Routine releases a spin lock. + +Arguments: + + Size - the number of bytes to allocate + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +#ifndef VXD +#if DBG +#define CTESpinFree(DataStruct,OldIrqLevel) \ +{ \ + strcpy( LastUnlockFile, __FILE__ ) ; \ + LastUnlockLine = __LINE__ ; \ + FreeSpinLockDebug(&(DataStruct)->SpinLock,OldIrqLevel,(DataStruct)->LockNumber); \ +} +#else +#define CTESpinFree(DataStruct,OldIrqLevel) \ +{ \ + CTEFreeLock((PKSPIN_LOCK)(&(DataStruct)->SpinLock),(OldIrqLevel)); \ +} +#endif +#else +#ifdef DEBUG + +#define CTESpinFree(DataStruct,OldIrqLevel) \ +{ \ + CTEFreeLock( &(DataStruct)->SpinLock, OldIrqLevel ) ; \ +} + +#else +#define CTESpinFree(DataStruct,OldIrqLevel) +#endif +#endif +//---------------------------------------------------------------------------- +// VOID +// CTESpinLockAtDpc( +// tCONNECTELE Structure +// ) + +/*++ +Routine Description: + + This Routine acquires a spin lock. + +Arguments: + + Size - the number of bytes to allocate + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +#ifndef VXD +#if DBG +#define CTESpinLockAtDpc(DataStruct) \ +{ \ + AcquireSpinLockAtDpcDebug(&(DataStruct)->SpinLock,(DataStruct)->LockNumber);\ + strcpy( LastLockFile, __FILE__ ) ; \ + LastLockLine = __LINE__ ; \ +} +#else // DBG +#define CTESpinLockAtDpc(DataStruct) \ +{ \ + CTEGetLockAtDPC((PKSPIN_LOCK)(&(DataStruct)->SpinLock), 0); \ +} +#endif // DBG +#else // VXD +#define CTESpinLockAtDpc(DataStruct) +#endif // VXD + + +//---------------------------------------------------------------------------- +// VOID +// CTESpinFreeAtDpc( +// PVOID SpinLock, +// CTELockHandle OldIrqLevel +// ) +/*++ +Routine Description: + + This Routine releases a spin lock. + +Arguments: + + Size - the number of bytes to allocate + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +#ifndef VXD +#if DBG +#define CTESpinFreeAtDpc(DataStruct) \ +{ \ + strcpy( LastUnlockFile, __FILE__ ) ; \ + LastUnlockLine = __LINE__ ; \ + FreeSpinLockAtDpcDebug(&(DataStruct)->SpinLock,(DataStruct)->LockNumber);\ +} +#else // DBG +#define CTESpinFreeAtDpc(DataStruct) \ +{ \ + CTEFreeLockFromDPC((PKSPIN_LOCK)(&(DataStruct)->SpinLock), 0); \ +} +#endif // DBG +#else // VXD +#define CTESpinFreeAtDpc(DataStruct) +#endif // VXD + + +//---------------------------------------------------------------------------- +// +// VOID +// CTEVerifyHandle( +// IN PVOID pDataStruct, +// IN ULONG Verifier, +// IN VOID TypeofStruct, +// OUT NTSTATUS *pRet +// ) +/*++ +Routine Description: + + This Routine checks that a handle pts to a data structure with the + correct verifier in it. + +Arguments: + + Size - the number of bytes to allocate + +Return Value: + + NTSTATUS + +--*/ + +#ifndef VXD +#if DBG +#define CTEVerifyHandle(_pDataStruct,_Verifier,_TypeofStruct,_ret) \ +{ \ + if ((_pDataStruct) && \ + ((((_TypeofStruct *)(_pDataStruct))->Verify) == (_Verifier))) \ + *_ret=STATUS_SUCCESS; \ + else \ + { \ + ASSERTMSG("Invalid Handle Passed to Nbt",0); \ + return(STATUS_INVALID_HANDLE); \ + } \ +} +#else +#define CTEVerifyHandle(_pDataStruct,_Verifier,_TypeofStruct,_ret) +#endif // DBG + +#else +#define CTEVerifyHandle(_pDataStruct,_Verifier,_TypeofStruct,_ret) \ +{ \ + if ((((_TypeofStruct *)(_pDataStruct))->Verify) == (_Verifier)) \ + *_ret=STATUS_SUCCESS; \ + else \ + return(STATUS_INVALID_HANDLE); \ +} +#endif + +//---------------------------------------------------------------------------- +// +// VOID +// CTEIoComplete( IN CTE_IRP * pIrp, +// IN NTSTATUS StatusCompletion, +// IN ULONG cbBytes +// ); +// +/*++ +Routine Description: + + Completes the requested IO packet. For NT this involves calling the IO + subsytem. As a VxD, the Irp is a pointer to the NCB so we set the status + variables appropriately and call the post routine if present. + +Arguments: + + pIrp - Packet to complete + StatusCompletion - Status of the completion + cbBytes - Dependent on the type of IO + +Return Value: + +--*/ +#ifndef VXD + +#define PCTE_MDL PMDL +#define CTE_IRP IRP +#define PCTE_IRP PIRP +#define CTE_ADDR_HANDLE PFILE_OBJECT + +#define CTEIoComplete( pIrp, StatusCompletion, cbBytes ) \ + NTIoComplete( pIrp, StatusCompletion, cbBytes ) + +#else +#define PCTE_MDL PVOID +#define CTE_IRP NCB +#define PCTE_IRP PNCB +#define PIRP PNCB +#define CTE_ADDR_HANDLE PVOID +#define PFILE_OBJECT CTE_ADDR_HANDLE + +#define CTEIoComplete( pIrp, StatusCompletion, cbBytes ) \ + VxdIoComplete( pIrp, StatusCompletion, cbBytes ) + +#endif + +//---------------------------------------------------------------------------- +// +// ULONG +// CTEMemCmp( PVOID S1, PVOID S2, ULONG Length ) +// +/*++ +Routine Description: + + Compares two memory regions and returns the byte count at which the + compare failed. The return value will equal Length if the memory + regions are identical. + +Arguments: + + S1, S2 - Memory source 1 and 2 to compare + Length - Count of bytes to compare + +Return Value: + +--*/ +// +// CXPORT.H defines this macro differntly and they did it after we had +// it here, so undef their definition so we can use ours without getting +// warnings. +// +#undef CTEMemCmp + +#ifndef VXD +#define CTEMemCmp( S1, S2, Length ) RtlCompareMemory( (S1), (S2), (Length) ) +#else +// +// Same thing as RtlCompareMemory except avoid standard call decoration +// +#define CTEMemCmp( S1, S2, Length ) VxdRtlCompareMemory( (S1), (S2), (Length) ) +#endif + +//---------------------------------------------------------------------------- +// +// LOGICAL +// CTEMemEqu( PVOID S1, PVOID S2, ULONG Length ) +// +/*++ +Routine Description: + + Compares two memory regions and returns a value of TRUE is the regions + match. Otherwise, FALSE is returned. + +Arguments: + + S1, S2 - Memory source 1 and 2 to compare + Length - Count of bytes to compare + +Return Value: + +--*/ + +#ifndef VXD +#define CTEMemEqu( S1, S2, Length ) RtlEqualMemory( (S1), (S2), (Length) ) +#else +// +// Same thing as RtlEqualMemory except avoid standard call decoration +// +#define CTEMemEqu( S1, S2, Length ) ( VxdRtlCompareMemory( (S1), (S2), (Length) ) == (Length) ) +#endif + +//---------------------------------------------------------------------------- +// +// Define away any try and except clauses when we are a VXD +// + +#ifndef VXD +#define CTE_try try +#define CTE_except except +#else +#define CTE_try +#define CTE_except( x ) if ( 0 ) +#endif + +// +// Misc. memory routines that get mapped when compiling as a VXD +// + +#ifdef VXD +#define CTEZeroMemory( pDest, uLength ) CTEMemSet( pDest, 0, uLength ) +#define CTEMemFree( p ) CTEFreeMem( p ) +#endif +//---------------------------------------------------------------------------- +/*++ +PVOID +CTEAllocMem( + USHORT Size + ) +Routine Description: + + This Routine allocates memory for NT drivers by calling ExAllocatePool + It uses the definition of CTEAllocMem from cxport.h + +Arguments: + + Size - the number of bytes to allocate + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +#ifndef VXD +#ifdef POOL_TAGGING +#undef ExAllocatePool +#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' tbN') +#ifdef DBGMEMNBT +#undef CTEAllocMem +#define CTEAllocMem( Size) CTEAllocMemDebug( (Size), NULL, (PUCHAR)__FILE__,(ULONG)__LINE__ ) +#undef CTEFreeMem +#define CTEFreeMem( pBuffer) CTEAllocMemDebug( 0,(pBuffer),(PUCHAR)__FILE__,(ULONG)__LINE__ ) +#endif +#endif +#endif + +#ifndef VXD +#ifdef POOL_TAGGING +#define NBT_TAG(x) (((x)<<24)|'\0tbN') +#define NbtAllocMem(size,__tag__) ExAllocatePoolWithTag(NonPagedPool,(size),(__tag__)) +#else // POOL_TAGGING +#define NBT_TAG(x) 0 +#define NbtAllocMem(size,__tag__) ExAllocatePool(NonPagedPool,(size)) +#endif // POOL_TAGGING +#else // POOL_TAGGING +#define NBT_TAG(x) 0 +#define NbtAllocMem(size,__tag__) CTEAllocMem((size)) +#endif // VXD + +#ifdef VXD +#ifdef DEBUG +#undef CTEAllocMem +#define CTEAllocMem DbgAllocMem +#undef CTEFreeMem +#define CTEFreeMem DbgFreeMem +#undef CTEMemFree +#define CTEMemFree DbgFreeMem +PVOID DbgAllocMem( DWORD ReqSize ); +VOID DbgFreeMem( PVOID pBufferToFree ); +VOID DbgMemCopy( PVOID pDestBuf, PVOID pSrcBuf, ULONG Length ); +#endif +#endif + +//---------------------------------------------------------------------------- +/*++ +PVOID +CTEAllocInitMem( + ULONG Size + ) +Routine Description: + + This Routine allocates memory and if nbt is a Vxd and it's called during + DeviceInit time, will refill the heap and retry the allocation before + failing. + +Arguments: + + Size - the number of bytes to allocate + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +#ifndef VXD +#define CTEAllocInitMem(Size) \ + ExAllocatePool(NonPagedPool, Size) +#endif + +//---------------------------------------------------------------------------- +/*++ +VOID +CTEMemFree( + PVOID pMem + ) +Routine Description: + + This Routine frees memory for NT drivers by calling ExFreePool + +Arguments: + + pMem - ptr to memory + +Return Value: + + NONE + +--*/ +#ifndef VXD +#define CTEMemFree(pMem) \ +{ \ + IF_DBG(NBT_DEBUG_MEMFREE) \ + KdPrint(("pmemfree = %X,lin %d in file %s\n",pMem,__LINE__,__FILE__)); \ + CTEFreeMem(pMem); \ +} +#endif + +//---------------------------------------------------------------------------- +/*++ +VOID +CTEZeroMemory( + PVOID pDest, + ULONG uLength + ) +Routine Description: + + This Routine sets memory to a single byte value + +Arguments: + + pDest - dest address + uLength - number to zero + +Return Value: + + NONE + +--*/ + +#ifndef VXD +#define CTEZeroMemory(pDest,uLength) \ + RtlZeroMemory(pDest,uLength) +#endif + +//---------------------------------------------------------------------------- +/*++ +NTSTATUS +CTEReadIniString( + HANDLE ParmHandle, + LPTSTR KeyName, + LPTSTR * ppStringBuff + ) +Routine Description: + + This routine retrieves a string from the users configuration profile + +Arguments: + + ParmHandle - Registry handle + KeyName - Name of value to retrieve + ppStringBuff - Pointer to allocated buffer containing found string + +Return Value: + + NTSTATUS + +--*/ + +#ifndef VXD +#define CTEReadIniString( ParmHandle, KeyName, ppStringBuff ) \ + NTReadIniString( ParmHandle, KeyName, ppStringBuff ) +#else +#define CTEReadIniString( ParmHandle, KeyName, ppStringBuff ) \ + VxdReadIniString( KeyName, ppStringBuff ) +#endif + +//---------------------------------------------------------------------------- +/*++ +ULONG +CTEReadSingleHexParameter( + HANDLE ParmHandle, + LPTSTR KeyName, + ULONG Default, + ULONG Minimum + ) +Routine Description: + + This routine retrieves a value in hex from the .ini file or registry + +Arguments: + + ParmHandle - Registry handle + KeyName - Name of value to retrieve + Default - Default value if not present + Minimum - Minimum value if present + +Return Value: + + NTSTATUS + +--*/ + +#ifndef VXD +#define CTEReadSingleIntParameter( ParmHandle, KeyName, Default, Minimum ) \ + NbtReadSingleParameter( ParmHandle, KeyName, Default, Minimum ) + +#define CTEReadSingleHexParameter( ParmHandle, KeyName, Default, Minimum ) \ + NbtReadSingleParameter( ParmHandle, KeyName, Default, Minimum ) +#else +#define CTEReadSingleIntParameter( ParmHandle, KeyName, Default, Minimum ) \ + GetProfileInt( ParmHandle, KeyName, Default, Minimum ) + +#define CTEReadSingleHexParameter( ParmHandle, KeyName, Default, Minimum ) \ + GetProfileHex( ParmHandle, KeyName, Default, Minimum ) +#endif + +//---------------------------------------------------------------------------- +// +// REFERENCE_LOWERCONN(pLowerConn) +// +/*++ +Routine Description: + + Increments the reference count on the LowerConnection and the Connection + Element + +Arguments: + + pLowerConn - Lower connection ptr + +Return Value: + None +--*/ + +#define REFERENCE_LOWERCONN( _pLowerConn ) \ +{ \ + IF_DBG(NBT_DEBUG_REF) \ + KdPrint(("Nbt:LowerConn Reference= %X, Incrementing...\n",\ + _pLowerConn->RefCount)); \ + InterlockedIncrement(&_pLowerConn->RefCount); \ +} + +//---------------------------------------------------------------------------- +// +// DEREFERENCE_LOWERCONN(pLowerConn) +// +/*++ +Routine Description: + + Decrements the reference count on the LowerConnection and the Connection + Element + +Arguments: + + pLowerConn - Lower connection ptr + +Return Value: + None +--*/ + +#define DEREFERENCE_LOWERCONN( _pLowerConn ) \ +{ \ + IF_DBG(NBT_DEBUG_REF) \ + KdPrint(("Nbt:LowerConn Reference= %X, Decrementing...\n",\ + _pLowerConn->RefCount)); \ + InterlockedDecrement(&_pLowerConn->RefCount); \ +} + +//---------------------------------------------------------------------------- +// +// CTEExInitializeResource(Resource) +// +/*++ +Routine Description: + + Initializes the Resource structure by calling an excutive support routine. + +Arguments: + + +Return Value: + + None + +--*/ +#ifndef VXD +#define CTEExInitializeResource( _Resource ) \ + ExInitializeResource(_Resource) +#else +#define CTEExInitializeResource( _Resource ) +#endif + +//---------------------------------------------------------------------------- +// +// CTEExAcquireResourceExclusive(Resource,Wait) +// +/*++ +Routine Description: + + Acquires the Resource by calling an excutive support routine. + +Arguments: + + +Return Value: + + None + +--*/ +#ifndef VXD +#define CTEExAcquireResourceExclusive( _Resource, _Wait ) \ + ExAcquireResourceExclusive(_Resource,_Wait) +#else +#define CTEExAcquireResourceExclusive( _Resource, _Wait ) +#endif + +//---------------------------------------------------------------------------- +// +// CTEExReleaseResource(Resource) +// +/*++ +Routine Description: + + Releases the Resource by calling an excutive support routine. + +Arguments: + + +Return Value: + + None + +--*/ +#ifndef VXD +#define CTEExReleaseResource( _Resource ) \ + ExReleaseResource(_Resource) +#else +#define CTEExReleaseResource( _Resource ) + +#endif + +//---------------------------------------------------------------------------- +// +// PUSH_LOCATION(Spot) +// +/*++ +Routine Description: + + This macro is used for debugging the receive code. It puts a byte value + into a circular list of byte values so that previous history can be traced + through the receive code. + +Arguments: + + Spot - the location to put in the list + +Return Value: + + None + +--*/ +#if DBG +extern unsigned char pLocBuff[256]; +extern unsigned char CurrLoc; +#define PUSH_LOCATION( _Spot) \ +{ \ + if (++CurrLoc == 256) \ + { \ + CurrLoc = 0; \ + } \ + pLocBuff[CurrLoc] = _Spot; \ +} +#else +#define PUSH_LOCATION( _Spot ) +#endif + +#if DBG +extern unsigned char Buff[256]; +extern unsigned char Loc; +#define LOCATION( _Spot) \ +{ \ + if (++Loc == 256) \ + { \ + Loc = 0; \ + } \ + Buff[Loc] = _Spot; \ +} +#else +#define LOCATION( _Spot ) +#endif + +//---------------------------------------------------------------------------- +// +// CTEQueueForNonDispProcessing( pTracker, +// pClientContext, +// ClientCompletion, +// CallBackRoutine +// pDeviceContext) ; +// +/*++ +Routine Description: + + This macro queues a request for a callback that can't be performed in + the current context (such as dispatch processing). + + In NT, this calls NTQueueToWorkerThread. + + As a VxD, we schedule an event that calls the specified routine. + +Arguments: + + pTracker - pointer to a tDGRAM_SEND_TRACKING structure (or NULL). + pClietContext - Context to pass to CallBackRoutine + ClientCompletion - + CallBackRoutine - Procedure to call at outside the current context + +Return Value: + + STATUS_SUCCESS if successful, error code otherwise + +--*/ +#ifndef VXD +#define CTEQueueForNonDispProcessing( pTracker, \ + pClientContext, \ + ClientCompletion, \ + CallBackRoutine, \ + pDeviceContext) \ + NTQueueToWorkerThread( pTracker, pClientContext, ClientCompletion, \ + CallBackRoutine, pDeviceContext ) +#else +#define CTEQueueForNonDispProcessing( pTracker, \ + pClientContext, \ + ClientCompletion, \ + CallBackRoutine, \ + pDeviceContext) \ + VxdScheduleDelayedCall( pTracker, pClientContext, ClientCompletion, \ + CallBackRoutine, pDeviceContext ) +#endif + +//---------------------------------------------------------------------------- +// +// CTESystemUpTime( OUT PTIME pTime ); +// +/*++ +Routine Description: + + This macro returns the current system time in clock tics or whatever + in the value pTime. For NT this is a Large Integer. For the VXD it is + a ULONG. + +Arguments: + + pTime + +Return Value: + + NONE +--*/ +#ifndef VXD +#define CTESystemTime LARGE_INTEGER +#define CTEQuerySystemTime( _Time ) \ + KeQuerySystemTime( &(_Time) ) + +// the lower 4 bits appear to be zero always...!! +#define RandomizeFromTime( Time, Mod ) \ + ((Time.LowTime >> 8) % Mod) +#else +#define CTESystemTime ULONG +#define CTEQuerySystemTime( _Time ) \ + _Time = CTESystemUpTime() +#define RandomizeFromTime( Time, Mod ) \ + ((Time >> 4) % Mod) +#endif + +//---------------------------------------------------------------------------- +// +// CTEPagedCode(); +// +/*++ +Routine Description: + + This macro is used in NT to check if the Irql is above zero. All + coded that is pageable has this macro call to catch any code that might + be marked as pageable when in fact it isn't. + +Arguments: + + none + +Return Value: + + NONE +--*/ +#ifndef VXD +#define CTEPagedCode() PAGED_CODE() +#else +#define CTEPagedCode() +#ifdef CHICAGO +#ifdef DEBUG +#undef CTEPagedCode +#define CTEPagedCode() _Debug_Flags_Service(DFS_TEST_REENTER+DFS_TEST_BLOCK) +#endif +#endif +#endif + + +//---------------------------------------------------------------------------- +// +// CTEMakePageable(Page,Routine); +// +/*++ +Routine Description: + + This macro is used in NT to activate teh alloc_text pragma, to put + a procedure in the pageable code segment. + +Arguments: + + none + +Return Value: + + NONE +--*/ +#define CTEMakePageable( _Page, _Routine ) \ + alloc_text(_Page,_Routine) + +#ifdef CHICAGO +#define ALLOC_PRAGMA +#define INIT _ITEXT +// #define PAGE _PTEXT "vmm.h" has a macro for this. We override it later. +#endif // CHICAGO + + +//---------------------------------------------------------------------------- +// +// NTSetCancelRoutine(pIrp,Routine); +// +/*++ +Routine Description: + + This macro removes the call to set the cancel routine for an irp from the + VXD environment. + +Arguments: + + none + +Return Value: + + NONE +--*/ +#ifdef VXD +#define NTSetCancelRoutine(_pIrp,_CancelRoutine,_pDeviceContext) (0) +#define NTCheckSetCancelRoutine(_pIrp,_CancelRoutine,_pDeviceContext) (0) +#define NTClearContextCancel(pWiContext) (0) +#endif + +//---------------------------------------------------------------------------- +// +// NbtLogEvent(LogEvent,status) +// +/*++ +Routine Description: + + This macro removes the call to the log routine for the Vxd environment + + +Arguments: + + none + +Return Value: + + NONE +--*/ +#ifdef VXD +#define NbtLogEvent(LogEvent,status) +#endif + +//---------------------------------------------------------------------------- +// +// CTEGetTimeout(_pTimeout); +// +/*++ +Routine Description: + + This macro gets the timeout value for a connect attempt + VXD environment. + +Arguments: + + none + +Return Value: + + NONE +--*/ +#ifndef VXD +#define CTEGetTimeout(pTimeout,pRetTime) \ +{ \ + LARGE_INTEGER _Timeout; \ + ULONG Remainder; \ + _Timeout.QuadPart = -(((PLARGE_INTEGER)pTimeout)->QuadPart); \ + _Timeout = RtlExtendedLargeIntegerDivide(_Timeout,MILLISEC_TO_100NS,&Remainder);\ + *pRetTime = (ULONG)_Timeout.LowPart; \ +} +#else +// +// VXD timeout is a pointer to a ULONG +// +#define CTEGetTimeout(_pTimeout, pRet ) (*pRet = ((ULONG) _pTimeout ? *((PULONG)_pTimeout) : 0 )) +#endif + +//---------------------------------------------------------------------------- +// +// CTEAttachFsp() +// +/*++ +Routine Description: + + This macro attaches a process to the File System Process to be sure + that handles are created and released in the same process + +Arguments: + +Return Value: + + STATUS_SUCCESS if successful, error code otherwise + +--*/ +#ifndef VXD +#define CTEAttachFsp(_pAttached) \ +{ \ + if (PsGetCurrentProcess() != NbtFspProcess) \ + { \ + KeAttachProcess(&NbtFspProcess->Pcb); \ + *_pAttached = TRUE; \ + } \ +} +#else +#define CTEAttachFsp( _pAttached ) +#endif + +//---------------------------------------------------------------------------- +// +// CTEAttachFsp() +// +/*++ +Routine Description: + + This macro attaches a process to the File System Process to be sure + that handles are created and released in the same process + +Arguments: + +Return Value: + + STATUS_SUCCESS if successful, error code otherwise + +--*/ +#ifndef VXD +#define CTEDetachFsp(Attached) \ +{ \ + if (Attached) \ + { \ + KeDetachProcess(); \ + } \ +} +#else +#define CTEDetachFsp( Attached ) +#endif +//---------------------------------------------------------------------------- +// +// CTEResetIrpPending(PIRP pIrp) +// +/*++ +Routine Description: + + This macro resets the irp pending bit in an irp. + +Arguments: + +Return Value: + + STATUS_SUCCESS if successful, error code otherwise + +--*/ +#ifndef VXD +#define CTEResetIrpPending(pIrp) \ +{ \ + PIO_STACK_LOCATION pIrpsp; \ + pIrpsp = IoGetCurrentIrpStackLocation(pIrp);\ + pIrpsp->Control &= ~SL_PENDING_RETURNED; \ +} +#else +#define CTEResetIrpPending( a ) +#endif + +//---------------------------------------------------------------------------- +// +// ADD_TO_LIST(pListHead,pTracker) +// +/*++ +Routine Description: + + This macro adds a tracker from the "used Tracker List" + +Arguments: + +Return Value: + + STATUS_SUCCESS if successful, error code otherwise + +--*/ +//#if DBG +#define ADD_TO_LIST(pListHead,pLinkage) \ +{ \ + CTELockHandle OldIrq; \ + CTESpinLock(&NbtConfig,OldIrq); \ + InsertTailList((pListHead),pLinkage); \ + CTESpinFree(&NbtConfig,OldIrq); \ +} +//#else +//#define ADD_TO_LIST( a,b ) +//#endif +//---------------------------------------------------------------------------- +// +// REMOVE_FROM_LIST(pLinkage) +// +/*++ +Routine Description: + + This macro removes a tracker from the "used Tracker List" + +Arguments: + +Return Value: + + STATUS_SUCCESS if successful, error code otherwise + +--*/ +//#if DBG +#define REMOVE_FROM_LIST(pLinkage) \ +{ \ + CTELockHandle OldIrq; \ + CTESpinLock(&NbtConfig,OldIrq); \ + RemoveEntryList(pLinkage); \ + CTESpinFree(&NbtConfig,OldIrq); \ +} +//#else +//#define REMOVE_FROM_LIST( a ) +//#endif +//---------------------------------------------------------------------------- +// +// CTESaveClientSecurity(pClientEle) +// +/*++ +Routine Description: + + This macro saves the client thread security context so that it can be used + later to impersonate the client when a remote lmhosts file is openned. + +Arguments: + +Return Value: + + +--*/ +#ifndef VXD +#define CTESaveClientSecurity(_pClientEle) \ + /*SaveClientSecurity(_pClientEle)*/ +#else +#define CTESaveClientSecurity(_pClientEle) +#endif + +//---------------------------------------------------------------------------- +// +// IMPERSONATE_CLIENT(pClientSecurity) +// +/*++ +Routine Description: + + This macro sets an excutive worker thread to impersonate a client + thread so that remote lmhost files can be openned by that thread. + +Arguments: + +Return Value: + + +--*/ +#ifndef VXD +#define IMPERSONATE_CLIENT(_pClientSecurity) \ + /*SeImpersonateClient((_pClientSecurity),NULL)*/ +#else +#define IMPERSONATE_CLIENT(_pClientSecurity) +#endif +//---------------------------------------------------------------------------- +// +// STOP_IMPERSONATE_CLIENT(pClientSecurity) +// +/*++ +Routine Description: + + This macro sets an excutive worker thread NOT to impersonate a client. + +Arguments: + +Return Value: + + +--*/ +#ifndef VXD +#define STOP_IMPERSONATE_CLIENT(_pClientSecurity) \ + /*NtSetInformationThread(PsGetCurrentThread(),ThreadImpersonationToken,NULL,sizeof(HANDLE))*/ +#else +#define STOP_IMPERSONATE_CLIENT(_pClientSecurity) +#endif + +//---------------------------------------------------------------------------- +// +// DELETE_CLIENT_SECURITY(pTracker) +// +/*++ +Routine Description: + + This macro deletes a client security. + +Arguments: + +Return Value: + + +--*/ +#ifndef VXD +#define DELETE_CLIENT_SECURITY(_pTracker) \ + /* NtDeleteClientSecurity(_pTracker)*/ +#else +#define DELETE_CLIENT_SECURITY(_pTracker) +#endif + + +#ifdef VXD // Taken from ntrtl.h (Vxd doesn't include NT Headers) + +// Doubly-linked list manipulation routines. Implemented as macros +// but logically these are procedures. +// + +// +// VOID +// InitializeListHead( +// PLIST_ENTRY ListHead +// ); +// + +#define InitializeListHead(ListHead) (\ + (ListHead)->Flink = (ListHead)->Blink = (ListHead)) + +// +// BOOLEAN +// IsListEmpty( +// PLIST_ENTRY ListHead +// ); +// + +#define IsListEmpty(ListHead) \ + ((ListHead)->Flink == (ListHead)) + +// +// PLIST_ENTRY +// RemoveHeadList( +// PLIST_ENTRY ListHead +// ); +// + +#define RemoveHeadList(ListHead) \ + (ListHead)->Flink;\ + {RemoveEntryList((ListHead)->Flink)} + +// +// PLIST_ENTRY +// RemoveTailList( +// PLIST_ENTRY ListHead +// ); +// + +#define RemoveTailList(ListHead) \ + (ListHead)->Blink;\ + {RemoveEntryList((ListHead)->Blink)} + +// +// VOID +// RemoveEntryList( +// PLIST_ENTRY Entry +// ); +// + +#define RemoveEntryList(Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_Flink;\ + _EX_Flink = (Entry)->Flink;\ + _EX_Blink = (Entry)->Blink;\ + _EX_Blink->Flink = _EX_Flink;\ + _EX_Flink->Blink = _EX_Blink;\ + } + +// +// VOID +// InsertTailList( +// PLIST_ENTRY ListHead, +// PLIST_ENTRY Entry +// ); +// + +#define InsertTailList(ListHead,Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_ListHead;\ + _EX_ListHead = (ListHead);\ + _EX_Blink = _EX_ListHead->Blink;\ + (Entry)->Flink = _EX_ListHead;\ + (Entry)->Blink = _EX_Blink;\ + _EX_Blink->Flink = (Entry);\ + _EX_ListHead->Blink = (Entry);\ + } + +// +// VOID +// InsertHeadList( +// PLIST_ENTRY ListHead, +// PLIST_ENTRY Entry +// ); +// + +#define InsertHeadList(ListHead,Entry) {\ + PLIST_ENTRY _EX_Flink;\ + PLIST_ENTRY _EX_ListHead;\ + _EX_ListHead = (ListHead);\ + _EX_Flink = _EX_ListHead->Flink;\ + (Entry)->Flink = _EX_Flink;\ + (Entry)->Blink = _EX_ListHead;\ + _EX_Flink->Blink = (Entry);\ + _EX_ListHead->Flink = (Entry);\ + } +#endif // VXD + + +#endif // _CTEMACRO_H_ diff --git a/private/ntos/nbt/inc/debug.h b/private/ntos/nbt/inc/debug.h new file mode 100644 index 000000000..c4b99719d --- /dev/null +++ b/private/ntos/nbt/inc/debug.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Debug.h + +Abstract: + + This file contains debug printing constants for NBT. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#ifndef _DEBUGNBT_H +#define _DEBUGNBT_H + +// +// Debug support.. this macro defines a check on a global flag that +// selectively enables and disables debugging in different parts of NBT +// NbtDebug is a global ULONG declared in driver.c +// +#if DBG +extern ULONG NbtDebug; +#endif // DBG + +#if DBG +#define IF_DBG(flags) if(NbtDebug & flags) + +#define NBT_DEBUG_REGISTRY 0x00000001 // registry.c +#define NBT_DEBUG_DRIVER 0x00000002 // driver.c +#define NBT_DEBUG_NTUTIL 0x00000004 // ntutil.c +#define NBT_DEBUG_TDIADDR 0x00000008 // tdiaddr.c +#define NBT_DEBUG_TDICNCT 0x00000010 // tidaddr.c +#define NBT_DEBUG_TDIHNDLR 0x00000020 // tdihndlr.c +#define NBT_DEBUG_NAME 0x00000040 // name.c +#define NBT_DEBUG_NTISOL 0x00000080 // ntisol.c +#define NBT_DEBUG_NBTUTILS 0x00000100 // nbtutils.c +#define NBT_DEBUG_NAMESRV 0x00000200 // namesrv.c +#define NBT_DEBUG_HNDLRS 0x00000400 // hndlrs.c +#define NBT_DEBUG_PROXY 0x00000800 // proxy.c +#define NBT_DEBUG_HASHTBL 0x00001000 // hashtbl.c +#define NBT_DEBUG_UDPSEND 0x00002000 // udpsend.c +#define NBT_DEBUG_TDIOUT 0x00004000 // tdiout.c +#define NBT_DEBUG_SEND 0x00008000 // sends +#define NBT_DEBUG_RCV 0x00010000 // rcvs +#define NBT_DEBUG_RCVIRP 0x00020000 // rcv irp processing +#define NBT_DEBUG_INDICATEBUFF 0x00040000 // tdihndlrs.c indicate buffer +#define NBT_DEBUG_MEMFREE 0x00080000 // memory alloc/free +#define NBT_DEBUG_REF 0x00100000 // reference counts +#define NBT_DEBUG_DISCONNECT 0x00200000 // Disconnects +#define NBT_DEBUG_FILLIRP 0x00400000 // Filling the Irp(Rcv) +#define NBT_DEBUG_LMHOST 0x00800000 // Lmhost file stuff +#define NBT_DEBUG_REFRESH 0x01000000 // refresh logic +#define NBT_DEBUG_FASTPATH 0x02000000 // Rcv code - fast path +#define NBT_DEBUG_WINS 0x04000000 // Wins Interface debug +#ifdef _PNP_POWER +#define NBT_DEBUG_PNP_POWER 0x08000000 // NT PNP debugging +#endif // _PNP_POWER +#define NBT_DEBUG_NETBIOS_EX 0x10000000 // NETBIOS_EX address type debugging +#else +#define IF_DBG(flags) +#endif +#endif diff --git a/private/ntos/nbt/inc/hosts.h b/private/ntos/nbt/inc/hosts.h new file mode 100644 index 000000000..1ebf4755e --- /dev/null +++ b/private/ntos/nbt/inc/hosts.h @@ -0,0 +1,279 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + lmhosts.h + +Abstract: + + This is the header file for the lmhosts facility of the nbt driver. + +Author: + + Eric Chin (ericc) April 28, 1992 + +Revision History: + +--*/ +#ifndef _LMHOSTS_H_ +#define _LMHOSTS_H_ + + + +// +// Configuration Defaults +// +// Only the first MAX_PARSE_BYTES of each line in the lmhosts file is +// examined. +// + +#define DATABASEPATH "\\SystemRoot\\nt\\system32\\drivers\\etc" + +#define LMHOSTSFILE "lmhosts" // name of lmhosts file + +#define MAX_FILE_IO_THREADS 1 // threads to read + // lmhosts file +#ifdef VXD +#define MAX_PRELOAD 100 // max cache entries + // to preload +#else +#define MAX_PRELOAD 1000 // max cache entries + // to preload +#endif + +#define SPECIAL_GROUP_SUFFIX 0x1C // for netlogon and + // the browser +#define SPECIAL_BROWSER_SUFFIX 0x1D // for the browser +#define MAX_MEMBERS_INTERNET_GROUP 50 // max size of internet group + +// +// Reserved Keywords in the lmhosts File +// +#define BEG_ALT_TOKEN "#BEGIN_ALTERNATE" // alternate block +#define DOMAIN_TOKEN "#DOM:" // specifies LM domain +#define END_ALT_TOKEN "#END_ALTERNATE" // alternate block +#define INCLUDE_TOKEN "#INCLUDE" // include a file +#define PRELOAD_TOKEN "#PRE" // preload this entry +#define NOFNR_TOKEN "#NOFNR" // no find name request + + +// +// Macro Definitions +// + +//#define min(x, y) ((x) < (y) ? (x) : (y)) + + + +// +// Public Definitions +// +// +// For each file that is opened, a LM_FILE object is created. +// +typedef struct _LM_FILE +{ +#ifndef VXD + KSPIN_LOCK f_lock; // protects this object + LONG f_refcount; // current no of references +#endif + + HANDLE f_handle; // handle from ZwOpenFile() + LONG f_lineno; // current line number + +#ifndef VXD + LARGE_INTEGER f_fileOffset; // current offset into file + + PUCHAR f_current; // buffer position to read + PUCHAR f_limit; // last byte + 1 of buffer + PUCHAR f_buffer; // start of buffer +#else + PUCHAR f_linebuffer; // line buffer + PUCHAR f_buffer; // file buffer + BOOL f_EOF ; // TRUE if EOF + ULONG f_CurPos ; // Current Pos. in File Buffer + ULONG f_EndOfData ; // Last valid data in File Buffer + PUCHAR f_BackUp; // copy here In case of #INCLUDE +#endif + +} LM_FILE, *PLM_FILE; + + +// +// The LM_IPADDRESS_LIST object contains pertinent information about a +// group of ip addresses. +// +// +typedef struct _LM_IPADDRESS_LIST +{ + + KSPIN_LOCK i_rcntlock; // protects i_refcount + LONG i_refcount; // current no of references + KSPIN_LOCK i_lock; // only when adding to i_addrs[] + int i_maxaddrs; // max capacity of i_addrs[] + int i_numaddrs; // current no of ip addresses + unsigned long i_addrs[1]; // the array of ip addresses + +} LM_IPADDRESS_LIST, *PLM_IPADDRESS_LIST; + + +// +// An LM_PARSE_FUNCTION may be called recursively to handle #INCLUDE +// directives in an lmhosts file. +// +// +typedef unsigned long (* LM_PARSE_FUNCTION) ( + IN PUCHAR path, // file to parse + IN PUCHAR target OPTIONAL, // NetBIOS name + IN BOOLEAN recurse, // process #INCLUDE's ? + OUT BOOLEAN *NoFindName // do not do find name +); + + +// +// The LM_WORK_ITEM object is the interface between lm_lookup() and +// LmFindName(). +// +// +typedef struct _LM_WORK_ITEM +{ // work for io thread(s) + + LIST_ENTRY w_list; // links to other items +// mblk_t *w_mp; // STREAMS buffer + +} LM_WORK_ITEM, *PLM_WORK_ITEM; + + + +// +// Private Function Prototypes +// +int +LmAddToDomAddrList ( + IN PUCHAR name, + IN unsigned long inaddr + ); + +NTSTATUS +LmCloseFile ( + IN PLM_FILE handle + ); + +NTSTATUS +LmCreateThreads ( + IN int nthreads + ); + +NTSTATUS +LmDeleteAllDomAddrLists ( + VOID + ); + +VOID +LmDerefDomAddrList( + PLM_IPADDRESS_LIST arrayp + ); + +char * +LmExpandName ( + OUT PUCHAR dest, + IN PUCHAR source, + IN UCHAR last + ); + +PUCHAR +LmFgets ( + IN PLM_FILE pfile, + OUT int *nbytes + ); + +NTSTATUS +LmFindName ( + VOID + ); + +PLM_IPADDRESS_LIST +LmGetDomAddrList ( + PUCHAR name + ); + +unsigned long +LmGetIpAddr ( + IN PUCHAR path, + IN PUCHAR target, + IN BOOLEAN recurse, + OUT BOOLEAN *NoFindName + ); + +NTSTATUS +LmGetFullPath ( + IN PUCHAR target, + OUT PUCHAR *path + ); + +unsigned long +LmInclude( + IN PUCHAR file, + IN LM_PARSE_FUNCTION function, + IN PUCHAR argument, + OUT BOOLEAN *NoFindName + ); + +NTSTATUS +LmInitDomAddrLists ( + VOID + ); + +VOID +LmLogOpenError ( + IN PUCHAR path, + IN NTSTATUS unused + ); + +VOID +LmLogSyntaxError ( + IN LONG lineno + ); + +PLM_FILE +LmOpenFile ( + IN PUCHAR path + ); + +int +LmPreloadEntry ( + IN PUCHAR name, + IN unsigned long inaddr, + IN unsigned int NoFNR + ); + +BOOLEAN +LmPutCacheEntry ( +// IN mblk_t *mp, + IN unsigned char *name, + IN unsigned long inaddr, + IN unsigned int ttl, + IN LONG nb_flags, + IN unsigned int NoFNR + ); + +NTSTATUS +LmTerminateThreads( + VOID + ); + +// +// Functions Imported from ..\common +// +extern unsigned long +inet_addr( + IN char *cp + ); + + + + +#endif // _LMHOSTS_H_ + diff --git a/private/ntos/nbt/inc/nbtinfo.h b/private/ntos/nbt/inc/nbtinfo.h new file mode 100644 index 000000000..93b5805fa --- /dev/null +++ b/private/ntos/nbt/inc/nbtinfo.h @@ -0,0 +1,28 @@ +/**********************************************************************/ +/** Microsoft Windows/NT **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + NbtInfo.h + + This file contains the NBT Info APIs + + + + FILE HISTORY: + Johnl 13-Dec-1993 Created + +*/ + +#ifndef _NBTINFO_H_ +#define _NBTINFO_H_ + +VOID AddrChngNotification( PVOID Context, + ULONG OldIpAddress, + ULONG NewIpAddress, + ULONG NewMask ) ; + + + +#endif //!_NBTINFO_H_ diff --git a/private/ntos/nbt/inc/nbtnt.h b/private/ntos/nbt/inc/nbtnt.h new file mode 100644 index 000000000..e708aac52 --- /dev/null +++ b/private/ntos/nbt/inc/nbtnt.h @@ -0,0 +1,272 @@ +// +// NBTNT.H +// +// This file contains common header definitions for NBT in the NT +// environment +// +// + +#ifndef _NBT_H +#define _NBT_H + +#ifndef VXD +#include <ntos.h> +#include <status.h> +#include <ntstatus.h> +#include <tdikrnl.h> +#include <tdi.h> +#include <sockets/netinet/in.h> // htons, ntohs +#include <windef.h> +#include <stdio.h> +#include <nb30.h> +#include <zwapi.h> + +#else + +#include <oscfgnbt.h> +#include <cxport.h> +#define __int64 double +#include <windef.h> +#include <nb30.h> +#include <sockets/netinet/in.h> // htons, ntohs + +// +// These definitions work around NTisms found in various difficult to change +// places. +// +typedef ULONG NTSTATUS ; +typedef PNCB PIRP ; +typedef PVOID PDEVICE_OBJECT ; + +#include <ctemacro.h> +#include <tdi.h> + +// +// These are needed because we include windef.h rather then +// ntddk.h, which end up not being defined +// +#define STATUS_NETWORK_NAME_DELETED ((NTSTATUS)0xC00000CAL) +#define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS)0xC0000206L) +#define STATUS_CONNECTION_DISCONNECTED ((NTSTATUS)0xC000020CL) +#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L) +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) + +#define STATUS_TOO_MANY_COMMANDS ((NTSTATUS)0xC00000C1L) +#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L) +#define STATUS_SHARING_VIOLATION ((NTSTATUS)0xC0000043L) +#define STATUS_DUPLICATE_NAME ((NTSTATUS)0xC00000BDL) +#define STATUS_BAD_NETWORK_PATH ((NTSTATUS)0xC00000BEL) +#define STATUS_REMOTE_NOT_LISTENING ((NTSTATUS)0xC00000BCL) +#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L) +#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL) +#define STATUS_UNEXPECTED_NETWORK_ERROR ((NTSTATUS)0xC00000C4L) +#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL) + +#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L) +#define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS)0xC0000010L) + +#define STATUS_INVALID_PARAMETER_6 ((NTSTATUS)0xC00000F4L) + +// +// The following functions are used by NBT. They are defined in the NT kernel +// TDI stuff which we are trying to avoid. +// + +typedef +NTSTATUS +(*PTDI_IND_CONNECT)( + IN PVOID TdiEventContext, + IN int RemoteAddressLength, + IN PVOID RemoteAddress, + IN int UserDataLength, + IN PVOID UserData, + IN int OptionsLength, + IN PVOID Options, + OUT CONNECTION_CONTEXT *ConnectionContext + ) + ; + +NTSTATUS +TdiDefaultConnectHandler ( + IN PVOID TdiEventContext, + IN int RemoteAddressLength, + IN PVOID RemoteAddress, + IN int UserDataLength, + IN PVOID UserData, + IN int OptionsLength, + IN PVOID Options, + OUT CONNECTION_CONTEXT *ConnectionContext + ); + +// +// Disconnection indication prototype. This is invoked when a connection is +// being disconnected for a reason other than the user requesting it. Note that +// this is a change from TDI V1, which indicated only when the remote caused +// a disconnection. Any non-directed disconnection will cause this indication. +// + +typedef +NTSTATUS +(*PTDI_IND_DISCONNECT)( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN int DisconnectDataLength, + IN PVOID DisconnectData, + IN int DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectFlags + ); + +NTSTATUS +TdiDefaultDisconnectHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN int DisconnectDataLength, + IN PVOID DisconnectData, + IN int DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectFlags + ); + +// +// A protocol error has occurred when this indication happens. This indication +// occurs only for errors of the worst type; the address this indication is +// delivered to is no longer usable for protocol-related operations, and +// should not be used for operations henceforth. All connections associated +// it are invalid. +// For NetBIOS-type providers, this indication is also delivered when a name +// in conflict or duplicate name occurs. +// + +typedef +NTSTATUS +(*PTDI_IND_ERROR)( + IN PVOID TdiEventContext, // the endpoint's file object. + IN NTSTATUS Status // status code indicating error type. + ); + +NTSTATUS +TdiDefaultErrorHandler ( + IN PVOID TdiEventContext, // the endpoint's file object. + IN NTSTATUS Status // status code indicating error type. + ); + +// +// TDI_IND_RECEIVE indication handler definition. This client routine is +// called by the transport provider when a connection-oriented TSDU is received +// that should be presented to the client. +// + +typedef +NTSTATUS +(*PTDI_IND_RECEIVE)( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ); + +NTSTATUS +TdiDefaultReceiveHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ); + +// +// TDI_IND_RECEIVE_DATAGRAM indication handler definition. This client routine +// is called by the transport provider when a connectionless TSDU is received +// that should be presented to the client. +// + +typedef +NTSTATUS +(*PTDI_IND_RECEIVE_DATAGRAM)( + IN PVOID TdiEventContext, // the event context + IN int SourceAddressLength, // length of the originator of the datagram + IN PVOID SourceAddress, // string describing the originator of the datagram + IN int OptionsLength, // options for the receive + IN PVOID Options, // + IN ULONG BytesIndicated, // number of bytes this indication + IN ULONG BytesAvailable, // number of bytes in complete Tsdu + OUT ULONG *BytesTaken, // number of bytes used + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ); + +NTSTATUS +TdiDefaultRcvDatagramHandler ( + IN PVOID TdiEventContext, // the event context + IN int SourceAddressLength, // length of the originator of the datagram + IN PVOID SourceAddress, // string describing the originator of the datagram + IN int OptionsLength, // options for the receive + IN PVOID Options, // + IN ULONG BytesIndicated, // number of bytes this indication + IN ULONG BytesAvailable, // number of bytes in complete Tsdu + OUT ULONG *BytesTaken, // number of bytes used + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ); + +// +// This indication is delivered if expedited data is received on the connection. +// This will only occur in providers that support expedited data. +// + +typedef +NTSTATUS +(*PTDI_IND_RECEIVE_EXPEDITED)( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, // + IN ULONG BytesIndicated, // number of bytes in this indication + IN ULONG BytesAvailable, // number of bytes in complete Tsdu + OUT ULONG *BytesTaken, // number of bytes used by indication routine + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ); + +NTSTATUS +TdiDefaultRcvExpeditedHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, // + IN ULONG BytesIndicated, // number of bytes in this indication + IN ULONG BytesAvailable, // number of bytes in complete Tsdu + OUT ULONG *BytesTaken, // number of bytes used by indication routine + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ); + +// +// This indication is delivered if there is room for a send in the buffer of +// a buffering protocol. +// + +typedef +NTSTATUS +(*PTDI_IND_SEND_POSSIBLE)( + IN PVOID TdiEventContext, + IN PVOID ConnectionContext, + IN ULONG BytesAvailable); + +NTSTATUS +TdiDefaultSendPossibleHandler ( + IN PVOID TdiEventContext, + IN PVOID ConnectionContext, + IN ULONG BytesAvailable); + +#endif //VXD + +#define FILE_DEVICE_NBT 0x32 +#endif + diff --git a/private/ntos/nbt/inc/nbtprocs.h b/private/ntos/nbt/inc/nbtprocs.h new file mode 100644 index 000000000..8f3d00291 --- /dev/null +++ b/private/ntos/nbt/inc/nbtprocs.h @@ -0,0 +1,1451 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Nbtprocs.h + +Abstract: + + This file contains the OS independent function prototypes. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + Johnl 05-Apr-1993 Hacked on to support VXD + +--*/ + + +#ifndef _NBTPROCS_H_ +#define _NBTPROCS_H_ + +#include "types.h" + +#ifndef VXD + #include <ntprocs.h> +#else + #include <vxdprocs.h> +#endif + +//--------------------------------------------------------------------- +// FROM NAMESRV.C +// +tNAMEADDR * +FindName( + enum eNbtLocation Location, + PCHAR pName, + PCHAR pScope, + USHORT *pRetNameType + ); + +NTSTATUS +NbtRegisterName( + IN enum eNbtLocation Location, + IN ULONG IpAddress, + IN PCHAR pName, + IN PCHAR pScope, + IN PVOID pClientContext, + IN PVOID pClientCompletion, + IN USHORT uAddressType, + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +ReleaseNameOnNet( + tNAMEADDR *pNameAddr, + PCHAR pScope, + PVOID pClientContext, + PVOID pClientCompletion, + ULONG NodeType, + tDEVICECONTEXT *pDeviceContext + ); + +VOID +NameReleaseDone( + PVOID pContext, + NTSTATUS status + ); + +VOID +NameReleaseDoneOnDynIf( + PVOID pContext, + NTSTATUS status + ); + +NTSTATUS +RegOrQueryFromNet( + IN BOOL fReg, + IN tDEVICECONTEXT *pDeviceContext, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN PCHAR pName, + IN PUCHAR pScope + ); + +NTSTATUS +QueryNameOnNet( + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN USHORT uType, + IN PVOID pClientContext, + IN PVOID pClientCompletion, + IN ULONG NodeType, + IN tNAMEADDR *pNameAddrIn, + IN tDEVICECONTEXT *pDeviceContext, + OUT tDGRAM_SEND_TRACKING **ppTracker, + IN CTELockHandle *pJointLockOldIrq + ); + +VOID +CompleteClientReq( + COMPLETIONCLIENT pClientCompletion, + tDGRAM_SEND_TRACKING *pTracker, + NTSTATUS status + ); + +VOID +DereferenceTracker( + IN tDGRAM_SEND_TRACKING *pTracker + ); + +VOID +DereferenceTrackerNoLock( + IN tDGRAM_SEND_TRACKING *pTracker + ); + +VOID +NodeStatusCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +RefreshTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +RemoteHashTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +SessionKeepAliveTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +IncrementNameStats( + IN ULONG StatType, + IN BOOLEAN IsNameServer + ); + +VOID +SaveBcastNameResolved( + IN PUCHAR pName + ); + +//--------------------------------------------------------------------- +// FROM NAME.C + +VOID +FreeRcvBuffers( + tCONNECTELE *pConnEle, + CTELockHandle *pOldIrq + ); +VOID +LockedDereferenceName( + IN tNAMEADDR *pNameAddr + ); + +NTSTATUS +NbtRegisterCompletion( + IN tCLIENTELE *pClientEle, + IN NTSTATUS Status); + +NTSTATUS +NbtOpenAddress( + IN TDI_REQUEST *pRequest, + IN TA_ADDRESS UNALIGNED *pTaAddress, + IN ULONG IpAddress, + IN PVOID pSecurityDescriptor, + IN tDEVICECONTEXT *pContext, + IN PVOID pIrp); + +NTSTATUS +NbtOpenConnection( + IN TDI_REQUEST *pRequest, + IN CONNECTION_CONTEXT pConnectionContext, + IN tDEVICECONTEXT *pContext); + +NTSTATUS +NbtOpenAndAssocConnection( + IN tLOWERCONNECTION *pLowerConn, + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +NbtAssociateAddress( + IN TDI_REQUEST *pRequest, + IN tCLIENTELE *pClientEle, + IN PVOID pIrp); + +NTSTATUS +NbtDisassociateAddress( + IN TDI_REQUEST *pRequest + ); + +NTSTATUS +NbtCloseAddress( + IN TDI_REQUEST *pRequest, + OUT TDI_REQUEST_STATUS *pRequestStatus, + IN tDEVICECONTEXT *pContext, + IN PVOID pIrp); + +NTSTATUS +NbtCleanUpAddress( + IN tCLIENTELE *pClientEle, + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +NbtCloseConnection( + IN TDI_REQUEST *pRequest, + OUT TDI_REQUEST_STATUS *pRequestStatus, + IN tDEVICECONTEXT *pContext, + IN PVOID pIrp); + +NTSTATUS +NbtCleanUpConnection( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext + ); + +VOID +RelistConnection( + IN tCONNECTELE *pConnEle + ); + +NTSTATUS +CleanupConnectingState( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext, + IN CTELockHandle *OldIrq, + IN CTELockHandle *OldIrq2 + ); + +VOID +ReConnect( + IN PVOID Context + ); + +NTSTATUS +NbtConnect( + IN TDI_REQUEST *pRequest, + IN PVOID pTimeout, + IN PTDI_CONNECTION_INFORMATION pCallInfo, + IN PTDI_CONNECTION_INFORMATION pReturnInfo, + IN PIRP pIrp + ); + +VOID +SessionSetupContinue( + IN PVOID pContext, + IN NTSTATUS status + ); + +VOID +SessionTimedOut( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +QueueCleanup( + IN tCONNECTELE *pConnEle + ); + +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); + +NTSTATUS +NbtSend( + IN TDI_REQUEST *pRequest, + IN USHORT Flags, + IN ULONG SendLength, + OUT LONG *pSentLength, + IN PVOID *pBuffer, + IN tDEVICECONTEXT *pContext, + IN PIRP pIrp + ); + +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 + ); + +NTSTATUS +SendDgram( + IN tNAMEADDR *pNameAddr, + IN tDGRAM_SEND_TRACKING *pTracker + ); +NTSTATUS + +BuildSendDgramHdr( + IN ULONG SendLength, + IN tDEVICECONTEXT *pDeviceContext, + IN PCHAR pSourceName, + IN PCHAR pDestinationName, + IN PVOID pBuffer, + OUT tDGRAMHDR **ppDgramHdr, + OUT tDGRAM_SEND_TRACKING **ppTracker + ); + +VOID +NodeStatusDone( + IN PVOID pContext, + IN NTSTATUS status + ); + +NTSTATUS +NbtSendNodeStatus( + IN tDEVICECONTEXT *pDeviceContext, + IN PCHAR pName, + IN PIRP pIrp, + IN PULONG pIpAddrsList, + IN PVOID ClientContext, + IN PVOID CompletionRoutine + ); + +NTSTATUS +NbtQueryFindName( + IN PTDI_CONNECTION_INFORMATION pInfo, + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN BOOLEAN IsIoctl + ); + +NTSTATUS +CopyFindNameData( + IN tNAMEADDR *pNameAddr, + IN PIRP pIrp, + IN ULONG SrcAddress); + +NTSTATUS +NbtListen( + IN TDI_REQUEST *pRequest, + IN ULONG Flags, + IN TDI_CONNECTION_INFORMATION *pRequestConnectInfo, + OUT TDI_CONNECTION_INFORMATION *pReturnConnectInfo, + IN PVOID pIrp); + +NTSTATUS +NbtAccept( + IN TDI_REQUEST *pRequest, + IN TDI_CONNECTION_INFORMATION *pAcceptInfo, + OUT TDI_CONNECTION_INFORMATION *pReturnAcceptInfo, + IN PIRP pIrp); + +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 + ); + +NTSTATUS +NbtSetEventHandler( + tCLIENTELE *pClientEle, + int EventType, + PVOID pEventHandler, + PVOID pEventContext + ); + +NTSTATUS +NbtQueryAdapterStatus( + IN tDEVICECONTEXT *pDeviceContext, + OUT PVOID *ppAdapterStatus, + OUT PLONG pSize + ); + +NTSTATUS +NbtQueryConnectionList( + IN tDEVICECONTEXT *pDeviceContext, + OUT PVOID *ppConnList, + IN OUT PLONG pSize + ); + +NTSTATUS +NbtResyncRemoteCache( + ); + +NTSTATUS +NbtQueryBcastVsWins( + IN tDEVICECONTEXT *pDeviceContext, + OUT PVOID *ppBuffer, + IN OUT PLONG pSize + ); + +NTSTATUS +NbtNewDhcpAddress( + tDEVICECONTEXT *pDeviceContext, + ULONG IpAddress, + ULONG SubnetMask); + +VOID +FreeTracker( + IN tDGRAM_SEND_TRACKING *pTracker, + IN ULONG Actions + ); + +NTSTATUS +DatagramDistribution( + IN tDGRAM_SEND_TRACKING *pTracker, + IN tNAMEADDR *pNameAddr + ); + +VOID +DereferenceIfNotInRcvHandler( + IN tCONNECTELE *pConnEle, + IN tLOWERCONNECTION *pLowerConn + ); +VOID +DeleteAddressElement( + IN tADDRESSELE *pAddress + ); + +VOID +DeleteClientElement( + IN tCLIENTELE *pClientEle + ); + +VOID +NbtDereferenceLowerConnection( + IN tLOWERCONNECTION *pLowerConn + ); + +VOID +ReleaseNameCompletion( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo); + +NTSTATUS +DisconnectLower( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG state, + IN ULONG Flags, + IN PVOID Timeout, + IN BOOLEAN Wait + ); + +NTSTATUS +NbtDereferenceConnection( + IN tCONNECTELE *pConnEle + ); + +VOID +NbtDereferenceName( + IN tNAMEADDR *pNameAddr + ); + +NTSTATUS +NbtDeleteLowerConn( + IN tLOWERCONNECTION *pLowerConn + ); + +USHORT +GetTransactId( + ); + +USHORT +GetTransactIdLocked( + ); + +//--------------------------------------------------------------------- +// +// FROM TDICNCT.C +// +NTSTATUS +NbtTdiOpenConnection ( + IN tLOWERCONNECTION *pLowerConn, + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +NbtTdiAssociateConnection( + IN PFILE_OBJECT pFileObject, + IN HANDLE Handle + ); + +NTSTATUS +TdiOpenandAssocConnection( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG PortNumber + ); + +NTSTATUS +NbtTdiCloseConnection( + IN tLOWERCONNECTION *pLowerConn + ); + +NTSTATUS +NbtTdiCloseAddress( + IN tLOWERCONNECTION *pLowerConn + ); + + +//--------------------------------------------------------------------- +// +// FROM TDIADDR.C +// +NTSTATUS +NbtTdiOpenAddress ( + OUT PHANDLE pFileHandle, + OUT PDEVICE_OBJECT *pDeviceObject, + OUT PFILE_OBJECT *pFileObject, + IN tDEVICECONTEXT *pDeviceContext, + IN USHORT PortNumber, + IN ULONG IpAddress, + IN ULONG Flags + ); + +NTSTATUS +CompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +NbtTdiOpenControl ( + IN tDEVICECONTEXT *pDeviceContext + ); + +//--------------------------------------------------------------------- +// +// FROM NBTUTILS.C +// + +void +FreeList( + PLIST_ENTRY pHead, + PLIST_ENTRY pFreeQ); + +void +NbtFreeAddressObj( + tADDRESSELE *pBlk); + +void +NbtFreeClientObj( + tCLIENTELE *pBlk); + +void +FreeConnectionObj( + tCONNECTELE *pBlk); + +tCLIENTELE * +NbtAllocateClientBlock(tADDRESSELE *pAddrEle); + +NTSTATUS +NbtAddPermanentName( + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +NbtAddPermanentNameNotFound( + IN tDEVICECONTEXT *pDeviceContext + ); + +VOID +NbtRemovePermanentName( + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +ConvertDottedDecimalToUlong( + IN PUCHAR pInString, + OUT PULONG IpAddress); + +NTSTATUS +NbtInitQ( + PLIST_ENTRY pListHead, + LONG iSizeBuffer, + LONG iNumBuffers); + +NTSTATUS +NbtInitTrackerQ( + PLIST_ENTRY pListHead, + LONG iNumBuffers + ); + +tDGRAM_SEND_TRACKING * +NbtAllocTracker( + IN VOID + ); + +NTSTATUS +NbtGetBuffer( + PLIST_ENTRY pListHead, + PLIST_ENTRY *ppListEntry, + enum eBUFFER_TYPES eBuffType); + +NTSTATUS +GetNetBiosNameFromTransportAddress( + IN PTA_NETBIOS_ADDRESS pTransAddr, + OUT PCHAR *pName, + OUT PULONG pNameLen, + OUT PULONG pNameType + ); + +NTSTATUS +ConvertToAscii( + IN PCHAR pNameHdr, + IN LONG NumBytes, + OUT PCHAR pName, + OUT PCHAR *pScope, + OUT PULONG pNameSize + ); + +PCHAR +ConvertToHalfAscii( + OUT PCHAR pDest, + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG ScopeSize + ); + +ULONG +Nbt_inet_addr( + IN PCHAR pName + ); + +NTSTATUS +BuildQueryResponse( + IN USHORT sNameSize, + IN tNAMEHDR *pNameHdr, + IN ULONG uTtl, + IN ULONG IpAddress, + OUT ULONG uNumBytes, + OUT PVOID pResponse, + IN PVOID pName, + IN USHORT NameType, + IN USHORT RetCode + ); + +NTSTATUS +GetTracker( + OUT tDGRAM_SEND_TRACKING **ppTracker); + +NTSTATUS +GetIrp( + OUT PIRP *ppIrp); + +NTSTATUS +NbtDereferenceAddress( + IN tADDRESSELE *pAddressEle + ); + +NTSTATUS +NbtDereferenceClient( + IN tCLIENTELE *pClientEle + ); + +ULONG +CountLocalNames(IN tNBTCONFIG *pNbtConfig + ); + +ULONG +CountUpperConnections( + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +DisableInboundConnections( + IN tDEVICECONTEXT *pDeviceContext, + OUT PLIST_ENTRY pLowerConnFreeHead + ); + +ULONG +CloseLowerConnections( + IN PLIST_ENTRY pLowerConnFreeHead + ); + +VOID +MarkForCloseLowerConnections( + IN tDEVICECONTEXT *pDeviceContext, + IN CTELockHandle OldIrqJoint, + IN CTELockHandle OldIrqDevice + ); + +NTSTATUS +NbtInitConnQ( + PLIST_ENTRY pListHead, + int iSizeBuffer, + int iNumConnections, + tDEVICECONTEXT *pDeviceContext); + +NTSTATUS +ReRegisterLocalNames( + ); + +NTSTATUS +LockedStopTimer( + tTIMERQENTRY **ppTimer); + +//--------------------------------------------------------------------- +// +// FROM hndlrs.c +// + +NTSTATUS +RcvHandlrNotOs ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *RcvBuffer + ); + +NTSTATUS +Inbound ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *RcvBuffer + + ); +NTSTATUS +Outbound ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *RcvBuffer + ); +NTSTATUS +RejectAnyData( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *ppIrp + ); + +VOID +RejectSession( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG StatusCode, + IN ULONG SessionStatus, + IN BOOLEAN SendNegativeSessionResponse + ); + +VOID +GetIrpIfNotCancelled( + IN tCONNECTELE *pConnEle, + OUT PIRP *ppIrp + ); + +NTSTATUS +FindSessionEndPoint( + IN VOID UNALIGNED *pTsdu, + IN PVOID ConnectionContext, + IN ULONG BytesIndicated, + OUT tCLIENTELE **ppClientEle, + OUT PVOID *ppRemoteAddress, + OUT PULONG pRemoteAddressLength + ); + +VOID +SessionRetry( + IN PVOID pContext, + IN PVOID pContext2, + IN tTIMERQENTRY *pTimerQEntry + ); + +tCONNECTELE * +SearchConnectionList( + IN tCLIENTELE *pClientEle, + IN PVOID pClientContext + ); + +NTSTATUS +ConnectHndlrNotOs ( + IN PVOID pConnectionContext, + IN LONG RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN PVOID UNALIGNED pUserData, + OUT CONNECTION_CONTEXT *ppConnectionId + ); + +NTSTATUS +DisconnectHndlrNotOs ( + PVOID EventContext, + PVOID ConnectionContext, + ULONG DisconnectDataLength, + PVOID UNALIGNED pDisconnectData, + ULONG DisconnectInformationLength, + PVOID pDisconnectInformation, + ULONG DisconnectIndicators + ); + +VOID +CleanupAfterDisconnect( + IN PVOID pContext + ); + +NTSTATUS +DgramHndlrNotOs( + IN PVOID ReceiveEventContext, + IN ULONG SourceAddrLength, + IN PVOID pSourceAddr, + IN ULONG OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG pBytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *ppRcvBuffer, + OUT tCLIENTLIST **ppAddressEle + ); + +NTSTATUS +NameSrvHndlrNotOs ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameSrv, + IN ULONG uNumBytes, + IN BOOLEAN fBroadcast + ); + +//--------------------------------------------------------------------- +// +// FROM proxy.c +// + +NTSTATUS +ReleaseResponseFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG NumBytes + ); + +NTSTATUS +ProxyQueryFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ); + +NTSTATUS +ProxyDoDgramDist( + IN tDGRAMHDR UNALIGNED *pDgram, + IN DWORD DgramLen, + IN tNAMEADDR *pNameAddr, + IN tDEVICECONTEXT *pDeviceContext + ); + + +VOID +ProxyTimerComplFn ( + IN PVOID pContext, + IN PVOID pContext2, + IN tTIMERQENTRY *pTimerQEntry + ); + +VOID +ProxyRespond ( + IN tQUERYRESP *pQuery, + IN PUCHAR pName, + IN tDEVICECONTEXT *pDeviceContext, + IN tNAMEHDR *pNameHdr, + IN ULONG lNameSize, + IN ULONG SrcAddress, + IN PTDI_ADDRESS_IP pAddressIp + ); + +//--------------------------------------------------------------------- +// +// FROM hashtbl.c +// +NTSTATUS +CreateHashTable( + tHASHTABLE **pHashTable, + LONG NumBuckets, + enum eNbtLocation LocalRemote + ); + +NTSTATUS +InitRemoteHashTable( + IN tNBTCONFIG *pConfig, + IN LONG NumBuckets, + IN LONG NumNames + ); + +NTSTATUS +AddNotFoundToHashTable( + IN tHASHTABLE *pHashTable, + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN enum eNbtAddrType NameType, + OUT tNAMEADDR **ppNameAddress + ); + +NTSTATUS +AddRecordToHashTable( + IN tNAMEADDR *pNameAddr, + IN PCHAR pScope + ); + +NTSTATUS +AddToHashTable( + IN tHASHTABLE *pHashTable, + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN enum eNbtAddrType NameType, + IN tNAMEADDR *pNameAddr, + OUT tNAMEADDR **ppNameAddress + ); + +NTSTATUS +DeleteFromHashTable( + tHASHTABLE *pHashTable, + PCHAR pName + ); + +NTSTATUS +ChgStateOfScopedNameInHashTable( + tHASHTABLE *pHashTable, + PCHAR pName, + PCHAR pScope, + DWORD NewState + ); + +NTSTATUS +FindInHashTable( + tHASHTABLE *pHashTable, + PCHAR pName, + PCHAR pScope, + tNAMEADDR **pNameAddress + ); + +NTSTATUS +FindNoScopeInHashTable( + tHASHTABLE *pHashTable, + PCHAR pName, + tNAMEADDR **pNameAddress + ); + +NTSTATUS +UpdateHashTable( + tHASHTABLE *pHashTable, + PCHAR pName, + PCHAR pScope, + ULONG IpAddress, + BOOLEAN bGroup, + tNAMEADDR **ppNameAddr + ); + +//--------------------------------------------------------------------- +// +// FROM timer.c +// + +NTSTATUS +InitTimerQ( + IN int NumInQ); + +NTSTATUS +InitQ( + IN int NumInQ, + IN tTIMERQ *pTimerQ, + IN USHORT uSize); + +VOID +StopTimerAndCallCompletion( + IN tTIMERQENTRY *pTimer, + IN NTSTATUS status, + IN CTELockHandle OldIrq + ); + +NTSTATUS +InterlockedCallCompletion( + IN tTIMERQENTRY *pTimer, + IN NTSTATUS status + ); + +NTSTATUS +GetEntry( + IN PLIST_ENTRY pQHead, + IN USHORT uSize, + OUT PLIST_ENTRY *ppEntry); + +NTSTATUS +LockedStartTimer( + IN ULONG DeltaTime, + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID CompletionRoutine, + IN PVOID ContextClient, + IN PVOID CompletionClient, + IN USHORT Retries, + IN tNAMEADDR *pNameAddr, + IN BOOLEAN CrossLink + ); + +NTSTATUS +StartTimer( + IN ULONG DeltaTime, + IN PVOID Context, + IN PVOID Context2, + IN PVOID CompletionRoutine, + IN PVOID ContextClient, + IN PVOID CompletionClient, + IN USHORT Retries, + OUT tTIMERQENTRY **ppTimerEntry); + +NTSTATUS +StopTimer( + IN tTIMERQENTRY *pTimerEntry, + OUT COMPLETIONCLIENT *pClient, + OUT PVOID *ppContext); + + +//--------------------------------------------------------------------- +// +// FROM udpsend.c +// + +NTSTATUS +UdpSendQueryNs( + PCHAR pName, + PCHAR pScope + ); +NTSTATUS +UdpSendQueryBcast( + IN PCHAR pName, + IN PCHAR pScope, + IN tDGRAM_SEND_TRACKING *pSentList + ); +NTSTATUS +UdpSendRegistrationNs( + PCHAR pName, + PCHAR pScope + ); + +NTSTATUS +UdpSendNSBcast( + IN tNAMEADDR *pNameAddr, + IN PCHAR pScope, + IN tDGRAM_SEND_TRACKING *pSentList, + IN PVOID pCompletionRoutine, + IN PVOID pClientContext, + IN PVOID pClientCompletion, + IN ULONG Retries, + IN ULONG Timeout, + IN enum eNSTYPE eNsType, + IN BOOL SendFlag + ); + +VOID +NsDgramSendCompleted( + PVOID pContext, + NTSTATUS status, + ULONG lInfo + ); + +VOID +NameDgramSendCompleted( + PVOID pContext, + NTSTATUS status, + ULONG lInfo + ); + +NTSTATUS +UdpSendResponse( + IN ULONG lNameSize, + IN tNAMEHDR UNALIGNED *pNameHdrIn, + IN tNAMEADDR *pNameAddr, + IN PTDI_ADDRESS_IP pDestIpAddress, + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG Rcode, + IN enum eNSTYPE NsType, + IN CTELockHandle OldIrq + ); + +NTSTATUS +UdpSendDatagram( + IN tDGRAM_SEND_TRACKING *pDgramTracker, + IN ULONG IpAddress, + IN PFILE_OBJECT TransportFileObject, + IN PVOID pCompletionRoutine, + IN PVOID CompletionContext, + IN USHORT Port, + IN ULONG Service + ); + +PVOID +CreatePdu( + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN USHORT NameType, + IN enum eNSTYPE eNsType, + OUT PVOID *pHdrs, + OUT PULONG pLength, + IN tDGRAM_SEND_TRACKING *pTracker + ); + +NTSTATUS +TcpSessionStart( + IN tDGRAM_SEND_TRACKING *pTracker, + IN ULONG IpAddress, + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pCompletionRoutine, + IN ULONG Port + ); + +NTSTATUS +TcpSendSessionResponse( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG lStatusCode, + IN ULONG lSessionStatus + ); + +NTSTATUS +TcpSendSession( + IN tDGRAM_SEND_TRACKING *pTracker, + IN tLOWERCONNECTION *LowerConn, + IN PVOID pCompletionRoutine + ); + +NTSTATUS +SendTcpDisconnect( + IN tLOWERCONNECTION *pLowerConnId + ); + +NTSTATUS +TcpDisconnect( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID Timeout, + IN ULONG Flags, + IN BOOLEAN Wait + ); + +VOID +FreeTrackerOnDisconnect( + IN tDGRAM_SEND_TRACKING *pTracker + ); + +VOID +QueryRespDone( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo); + +VOID +DisconnectDone( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo); + + +//--------------------------------------------------------------------- +// +// FROM tdiout.c +// +NTSTATUS +TdiSendDatagram( + IN PTDI_REQUEST pRequestInfo, + IN PTDI_CONNECTION_INFORMATION pSendDgramInfo, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER *pSendBuffer, + IN ULONG SendFlags + ); +PIRP +NTAllocateNbtIrp( + IN PDEVICE_OBJECT DeviceObject + ); +NTSTATUS +TdiConnect( + IN PTDI_REQUEST pRequestInfo, + IN ULONG lTimeout, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + OUT PVOID pIrp + ); +NTSTATUS +TdiSend( + IN PTDI_REQUEST pRequestInfo, + IN USHORT sFlags, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER *pSendBuffer, + IN ULONG Flags + ); + +NTSTATUS +TdiDisconnect( + IN PTDI_REQUEST pRequestInfo, + IN PVOID lTimeout, + IN ULONG Flags, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + IN PCTE_IRP pClientIrp, + IN BOOLEAN Wait + ); + +//--------------------------------------------------------------------- +// +// FROM inbound.c +// +NTSTATUS +QueryFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags, + IN BOOLEAN fBroadcast + ); + +NTSTATUS +RegResponseFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ); + +NTSTATUS +CheckRegistrationFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes + ); + +NTSTATUS +NameReleaseFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes + ); + +NTSTATUS +WackFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes + ); + +VOID +SetupRefreshTtl( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN tNAMEADDR *pNameAddr, + IN LONG lNameSize + ); + +BOOLEAN +SrcIsNameServer( + IN ULONG SrcAddress, + IN USHORT SrcPort + ); + +VOID +SwitchToBackup( + IN tDEVICECONTEXT *pDeviceContext + ); + +BOOLEAN +SrcIsUs( + IN ULONG SrcAddress + ); + +NTSTATUS +FindOnPendingList( + IN PUCHAR pName, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN BOOLEAN DontCheckTransactionId, + IN ULONG BytesToCompare, + OUT tNAMEADDR **ppNameAddr + ); + + +//--------------------------------------------------------------------- +// +// FROM init.c +// +NTSTATUS +InitNotOs( + void + ) ; + +NTSTATUS +InitTimersNotOs( + void + ); + +NTSTATUS +StopInitTimers( + void + ); + +VOID +ReadParameters( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ); + +VOID +ReadParameters2( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ); + +//--------------------------------------------------------------------- +// +// FROM parse.c +// +unsigned long +LmGetIpAddr ( + IN PUCHAR path, + IN PUCHAR target, + IN BOOLEAN recurse, + OUT BOOLEAN *bFindName + ); + +VOID +RemovePreloads ( + ); + +VOID +RemoveName ( + IN tNAMEADDR *pNameAddr + ); + +LONG +PrimeCache( + IN PUCHAR path, + IN PUCHAR ignored, + IN BOOLEAN recurse, + OUT BOOLEAN *ignored2 + ); + +NTSTATUS +NtDnsNameResolve ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID *pBuffer, + IN LONG Size, + IN PCTE_IRP pIrp + ); + +NTSTATUS +NtCheckForIPAddr ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID *pBuffer, + IN LONG Size, + IN PCTE_IRP pIrp + ); + +VOID +StartIpAddrToSrvName( + IN NBT_WORK_ITEM_CONTEXT *Context, + IN ULONG *IpAddrsList, + IN BOOLEAN IpAddrResolved + ); + +VOID +StartConnWithBestAddr( + IN NBT_WORK_ITEM_CONTEXT *Context, + IN ULONG *IpAddrsList, + IN BOOLEAN IpAddrResolved + ); + +NTSTATUS +DoDnsResolve ( + IN NBT_WORK_ITEM_CONTEXT *Context + ); + +NTSTATUS +DoCheckAddr ( + IN NBT_WORK_ITEM_CONTEXT *Context + ); + +NTSTATUS +LmHostQueueRequest( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID pClientContext, + IN PVOID ClientCompletion, + IN PVOID CallBackRoutine, + IN PVOID pDeviceContext, + IN CTELockHandle OldIrq + ); + +tNAMEADDR * +FindInDomainList ( + IN PUCHAR pName, + IN PLIST_ENTRY pDomainHead + ); + +VOID +ScanLmHostFile ( + IN PVOID Context + ); + +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (((x) > (y)) ? (x) : (y)) + +#endif // _NBTPROCS_H_ diff --git a/private/ntos/nbt/inc/ntprocs.h b/private/ntos/nbt/inc/ntprocs.h new file mode 100644 index 000000000..8d5ef8bae --- /dev/null +++ b/private/ntos/nbt/inc/ntprocs.h @@ -0,0 +1,845 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + NTProcs.c + +Abstract: + + + This file contains the function prototypes that are specific to the NT + portion of the NBT driver. + +Author: + + Johnl 29-Mar-1993 Created + +Revision History: + +--*/ + +//--------------------------------------------------------------------- +// +// FROM DRIVER.C +// +NTSTATUS +NbtDispatchCleanup( + IN PDEVICE_OBJECT Device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchClose( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchCreate( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ); + +NTSTATUS +NbtDispatchDevCtrl( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchInternalCtrl( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +PFILE_FULL_EA_INFORMATION +FindInEA( + IN PFILE_FULL_EA_INFORMATION start, + IN PCHAR wanted + ); + + +USHORT +GetDriverName( + IN PFILE_OBJECT pfileobj, + OUT PUNICODE_STRING name + ); + +int +shortreply( + IN PIRP pIrp, + IN int status, + IN int nbytes + ); + +//--------------------------------------------------------------------- +// +// FROM NTISOL.C +// +NTSTATUS +NTOpenControl( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTOpenAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTOpenConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +VOID +NTSetFileObjectContexts( + IN PIRP pIrp, + IN PVOID FsContext, + IN PVOID FsContext2); + +VOID +NTCompleteIOListen( + IN tCLIENTELE *pClientEle, + IN NTSTATUS Status); + +VOID +NTIoComplete( + IN PIRP pIrp, + IN NTSTATUS Status, + IN ULONG SentLength); + +VOID +NTCompleteRegistration( + IN tCLIENTELE *pClientEle, + IN NTSTATUS Status); + +NTSTATUS +NTAssocAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTCloseAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +VOID +NTClearFileObjectContext( + IN PIRP pIrp + ); + +NTSTATUS +NTCloseConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTSetSharedAccess( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN tADDRESSELE *pAddress); + +NTSTATUS +NTCheckSharedAccess( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN tADDRESSELE *pAddress); + +NTSTATUS +NTCleanUpAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTCleanUpConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +VOID +DiscWaitCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +VOID +NbtCancelListen( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP Irp + ); + +VOID +NTCancelRcvDgram( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NTSTATUS +NTAccept( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTAssocAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTDisAssociateAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTConnect( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTDisconnect( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTListen( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + + +NTSTATUS +NTQueryInformation( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTReceive( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTReceiveDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTSend( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTSendDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTSetEventHandler( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTSetInformation( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTCheckSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +NTSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN tDEVICECONTEXT *pDeviceContext + ); + +VOID +NTCancelSession( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +VOID +DnsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +VOID +CheckAddrIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +VOID +WaitForDnsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +VOID +NTSendSession( + IN tDGRAM_SEND_TRACKING *pTracker, + IN tLOWERCONNECTION *pLowerConn, + IN PVOID pCompletion); + +VOID +NTSendDgramNoWindup( + IN tDGRAM_SEND_TRACKING *pTracker, + IN ULONG IpAddress, + IN PVOID pCompletion); + +NTSTATUS +NTQueueToWorkerThread( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID pClientContext, + IN PVOID ClientCompletion, + IN PVOID CallBackRoutine, + IN PVOID pDeviceContext + ); + +VOID +SecurityDelete( + IN PVOID pContext + ); + +NTSTATUS +DispatchIoctls( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp); + +NTSTATUS +NTCancelCancelRoutine( + IN PIRP pIrp + ); + +VOID +NTClearContextCancel( + IN NBT_WORK_ITEM_CONTEXT *pContext + ); + +VOID +FindNameCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +//--------------------------------------------------------------------- +// +// FROM NTUTIL.C +// + +NTSTATUS +NbtCreateDeviceObject( + PDRIVER_OBJECT DriverObject, + tNBTCONFIG *pConfig, + PUNICODE_STRING pBindName, + PUNICODE_STRING pExportName, + tADDRARRAY *pAddrs, + PUNICODE_STRING RegistryPath, +#ifndef _IO_DELETE_DEVICE_SUPPORTED + BOOLEAN fReuse, +#endif + tDEVICECONTEXT **ppDeviceContext + ); + +NTSTATUS +NbtDestroyDeviceObject( + IN PVOID pBuffer + ); + +NTSTATUS +NbtProcessDhcpRequest( + tDEVICECONTEXT *pDeviceContext); + +NTSTATUS +ConvertToUlong( + IN PUNICODE_STRING pucAddress, + OUT ULONG *pulValue); + + +NTSTATUS +NbtCreateAddressObjects( + IN ULONG IpAddress, + IN ULONG SubnetMask, + OUT tDEVICECONTEXT *pDeviceContext); + +VOID +NbtGetMdl( + PMDL *ppMdl, + enum eBUFFER_TYPES eBuffType); + +NTSTATUS +NbtInitMdlQ( + PSINGLE_LIST_ENTRY pListHead, + enum eBUFFER_TYPES eBuffType); + +NTSTATUS +NTZwCloseFile( + IN HANDLE Handle + ); + + +NTSTATUS +NTReReadRegistry( + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +NbtInitIrpQ( + PLIST_ENTRY pListHead, + int iNumBuffers); + +NTSTATUS +NbtLogEvent( + IN ULONG EventCode, + IN NTSTATUS Status + ); + +NTSTATUS +SaveClientSecurity( + IN tDGRAM_SEND_TRACKING *pTracker + ); + +VOID +NtDeleteClientSecurity( + IN tDGRAM_SEND_TRACKING *pTracker + ); + +VOID +LogLockOperation( + char operation, + PKSPIN_LOCK PSpinLock, + KIRQL OldIrql, + KIRQL NewIrql, + char *File, + int Line + ); +StrmpInitializeLockLog( + VOID + ); +VOID +PadEntry( + char *EntryPtr + ); + +NTSTATUS +CloseAddressesWithTransport( + IN tDEVICECONTEXT *pDeviceContext + ); + +PVOID +CTEAllocMemDebug( + IN ULONG Size, + IN PVOID pBuffer, + IN UCHAR *File, + IN ULONG Line + ); + +VOID +AcquireSpinLockDebug( + IN PKSPIN_LOCK pSpinLock, + IN PKIRQL pOldIrq, + IN UCHAR LockNumber + ); +VOID +FreeSpinLockDebug( + IN PKSPIN_LOCK pSpinLock, + IN KIRQL OldIrq, + IN UCHAR LockNumber + ); + +VOID +AcquireSpinLockAtDpcDebug( + IN PKSPIN_LOCK pSpinLock, + IN UCHAR LockNumber + ); + +VOID +FreeSpinLockAtDpcDebug( + IN PKSPIN_LOCK pSpinLock, + IN UCHAR LockNumber + ); + +VOID +GetDgramMdl( + OUT PMDL *ppMdl); + + +//--------------------------------------------------------------------- +// +// FROM REGISTRY.C +// +NTSTATUS +NbtReadRegistry( + IN PUNICODE_STRING RegistryPath, + IN PDRIVER_OBJECT DriverObject, + OUT tNBTCONFIG *pConfig, + OUT tDEVICES **ppBindDevices, + OUT tDEVICES **ppExportDevices, + OUT tADDRARRAY **ppAddrArray + ); + +NTSTATUS +ReadNameServerAddresses ( + IN HANDLE NbtConfigHandle, + IN tDEVICES *BindDevices, + IN ULONG NumberDevices, + OUT tADDRARRAY **ppAddrArray + ); + +NTSTATUS +GetIPFromRegistry( + IN PUNICODE_STRING pucRegistryPath, + IN PUNICODE_STRING pucBindDevice, + OUT PULONG pulIpAddress, + OUT PULONG pulBroadcastAddress, + IN BOOL fWantDhcpAddresses + ); + +NTSTATUS +ReadElement( + IN HANDLE HandleToKey, + IN PWSTR pwsValueName, + OUT PUNICODE_STRING pucString + ); + +NTSTATUS +NTReadIniString ( + IN HANDLE ParametersHandle, + IN PWSTR Key, + OUT PUCHAR *ppString + ); + +ULONG +NbtReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue, + IN ULONG MinimumValue + ); + +NTSTATUS +NTGetLmHostPath( + OUT PUCHAR *ppPath + ); + +//--------------------------------------------------------------------- +// +// FROM tdihndlr.c +// +NTSTATUS +Normal( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *ppIrp + ); +NTSTATUS +FillIrp( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *ppIrp + ); +NTSTATUS +IndicateBuffer( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *ppIrp + ); +NTSTATUS +PartialRcv( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PVOID *ppIrp + ); +NTSTATUS +TdiReceiveHandler ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID UNALIGNED Tsdu, + OUT PIRP *IoRequestPacket + ); + +NTSTATUS +PassRcvToTransport( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnectEle, + IN PVOID pIoRequestPacket, + IN PULONG pRcvLength + ); + +NTSTATUS +CompletionRcv( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +NtBuildIrpForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ); + +NTSTATUS +SetEventHandler ( + IN PDEVICE_OBJECT DeviceObject, + IN PFILE_OBJECT FileObject, + IN ULONG EventType, + IN PVOID EventHandler, + IN PVOID Context + ); + +NTSTATUS +SubmitTdiRequest ( + IN PFILE_OBJECT FileObject, + IN PIRP Irp + ); + +NTSTATUS +TdiConnectHandler ( + IN PVOID pConnectEventContext, + IN int RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN PVOID UNALIGNED pUserData, + IN int OptionsLength, + IN PVOID pOptions, + OUT CONNECTION_CONTEXT *pConnectionContext, + OUT PIRP *ppAcceptIrp + ); + +NTSTATUS +TdiDisconnectHandler ( + PVOID EventContext, + PVOID ConnectionContext, + ULONG DisconnectDataLength, + PVOID UNALIGNED DisconnectData, + ULONG DisconnectInformationLength, + PVOID DisconnectInformation, + ULONG DisconnectIndicators + ); +NTSTATUS +TdiRcvDatagramHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PIRP *pIoRequestPacket + ); +NTSTATUS +TdiRcvNameSrvHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID UNALIGNED pTsdu, + OUT PIRP *pIoRequestPacket + ); +NTSTATUS +TdiErrorHandler ( + IN PVOID Context, + IN NTSTATUS Status + ); + +NTSTATUS +CompletionRcvDgram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +NTProcessAcceptIrp( + IN PIRP pIrp, + OUT tCONNECTELE **ppConnEle + ); + +NTSTATUS +AllocateMdl ( + IN tCONNECTELE *pConnEle + ); + +VOID +MakePartialMdl ( + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG ToCopy + ); + +NTSTATUS +OutOfRsrcKill( + OUT tLOWERCONNECTION *pLowerConn); + +VOID +CopyToStartofIndicate ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG DataTaken + ); + +//--------------------------------------------------------------------- +// +// FROM tdicnct.c +// +NTSTATUS +CreateDeviceString( + IN PWSTR AppendingString, + IN OUT PUNICODE_STRING pucDevice + ); + + +//--------------------------------------------------------------------- +// +// FROM winsif.c +// +NTSTATUS +NTOpenWinsAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +NTCloseWinsAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp); + +NTSTATUS +RcvIrpFromWins ( + IN tDEVICECONTEXT *pDeviceContext, + IN PCTE_IRP pIrp + ); + +NTSTATUS +PassNamePduToWins ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameSrv, + IN ULONG uNumBytes + ); + +NTSTATUS +WinsSendDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN BOOLEAN MustSend); + +NTSTATUS +WinsRegisterName( + IN tDEVICECONTEXT *pDeviceContext, + IN tNAMEADDR *pNameAddr, + IN PUCHAR pScope, + IN enum eNSTYPE eNsType + ); + + +//--------------------------------------------------------------------- +// +// FROM ntpnp.c +// + +#ifdef _PNP_POWER + +VOID +AddressArrival(IN PTA_ADDRESS Addr); + +VOID +AddressDeletion(IN PTA_ADDRESS Addr); + +extern HANDLE AddressChangeHandle; + +NTSTATUS +NbtCreateNetBTDeviceObject( + PDRIVER_OBJECT DriverObject, + tNBTCONFIG *pConfig, + PUNICODE_STRING RegistryPath + ); + +tDEVICECONTEXT * +NbtFindIPAddress( + ULONG IpAddr + ); + +NTSTATUS +NbtNtPNPInit( + VOID + ); + +VOID +NbtFailedNtPNPInit( + VOID + ); + +NTSTATUS +NbtAddressAdd( + ULONG IpAddr, + PUNICODE_STRING pucBindString, + PUNICODE_STRING pucExportString, + PULONG Inst + ); + +NTSTATUS +NbtAddNewInterface ( + IN PIRP pIrp, + IN PVOID *pBuffer, + IN ULONG Size + ); + +VOID +NbtAddressDelete( + ULONG IpAddr + ); + +tDEVICECONTEXT * +NbtFindBindName( + PUNICODE_STRING pucBindName + ); + +#ifdef WATCHBIND +VOID +BindHandler(IN PUNICODE_STRING DeviceName); + +VOID +UnbindHandler(IN PUNICODE_STRING DeviceName); + +extern HANDLE BindingHandle; +#endif // WATCHBIND + +#endif diff --git a/private/ntos/nbt/inc/oscfgnbt.h b/private/ntos/nbt/inc/oscfgnbt.h new file mode 100644 index 000000000..51c3f803b --- /dev/null +++ b/private/ntos/nbt/inc/oscfgnbt.h @@ -0,0 +1,85 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +#ifndef OSCFG_INCLUDED +#define OSCFG_INCLUDED + + +#define net_short(x) ((((x)&0xff) << 8) | (((x)&0xff00) >> 8)) + +//#define net_long(x) (((net_short((x)&0xffff)) << 16) | net_short((((x)&0xffff0000L)>>16))) +#define net_long(x) (((((ulong)(x))&0xffL)<<24) | \ + ((((ulong)(x))&0xff00L)<<8) | \ + ((((ulong)(x))&0xff0000L)>>8) | \ + ((((ulong)(x))&0xff000000L)>>24)) + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + + +#ifdef VXD +///////////////////////////////////////////////////////////////////////////// +// +// VXD definitions +// +//////////////////////////////////////////////////////////////////////////// + +#include <stddef.h> + +#ifndef CHICAGO + +#pragma code_seg("_LTEXT", "LCODE") +#pragma data_seg("_LDATA", "LCODE") + +//* pragma bodies for bracketing of initialization code. + +#define BEGIN_INIT code_seg("_ITEXT", "ICODE") +#define BEGIN_INIT_DATA data_seg("_IDATA", "ICODE") +#define END_INIT code_seg() +#define END_INIT_DATA data_seg() + +#else // CHICAGO + +#define INNOCUOUS_PRAGMA warning(4:4206) // Source File is empty + +#define BEGIN_INIT INNOCUOUS_PRAGMA +#define BEGIN_INIT_DATA INNOCUOUS_PRAGMA +#define END_INIT INNOCUOUS_PRAGMA +#define END_INIT_DATA INNOCUOUS_PRAGMA + +#endif // CHICAGO + +#else // VXD +#ifdef NT + +////////////////////////////////////////////////////////////////////////////// +// +// NT definitions +// +////////////////////////////////////////////////////////////////////////////// + +#include <ntos.h> +#include <zwapi.h> + +#define BEGIN_INIT +#define END_INIT + +#else // NT + +///////////////////////////////////////////////////////////////////////////// +// +// Definitions for additional environments go here +// +///////////////////////////////////////////////////////////////////////////// + +#error Environment specific definitions missing + +#endif // NT + +#endif // VXD + + +#endif // OSCFG_INCLUDED diff --git a/private/ntos/nbt/inc/timer.h b/private/ntos/nbt/inc/timer.h new file mode 100644 index 000000000..63c7e886f --- /dev/null +++ b/private/ntos/nbt/inc/timer.h @@ -0,0 +1,89 @@ +// +// +// timer.h +// +// This file contains the typedefinitions for the timer code + + +#ifndef __TIMERNBT_H +#define __TIMERNBT_H + +// to convert a millisecond time to 100ns time +// +#define MILLISEC_TO_100NS 10000 +// the completion routine that the client must define +typedef + VOID + (*COMPLETIONROUTINE)( + IN PVOID, // context + IN PVOID, // context2 + IN PVOID); // timerqentry +typedef + VOID + (*COMPLETIONCLIENT)( + IN PVOID, + IN NTSTATUS); + +// Timer Queue Entry - this entry looks after a timer event. It tracks who +// should be called when the timeout occurs, the time in the future of the +// timout, and a context value. +typedef struct +{ + CTETimer VxdTimer ; + LIST_ENTRY Linkage; + PVOID Context; + PVOID Context2; + COMPLETIONROUTINE CompletionRoutine; + PVOID ClientContext; + COMPLETIONCLIENT ClientCompletion; + PVOID pCacheEntry; // entry in Remote or local cache + ULONG DeltaTime; + // this is set by the completionroutine to tell the timing system to + // restart the timer again, as opposed to disposing of the timer + USHORT Flags; + USHORT Retries; // number of times to restart the timer + UCHAR RefCount; // to tell if the timer is expiring or not + +}tTIMERQENTRY; + +// Flag bits for tTIMERQENTRY +#define TIMER_RESTART 0x0001 +// to differentiate the broadcast timeouts from the timouts to the Name Service +#define TIMER_MNODEBCAST 0x0002 +#define TIMER_DOING_EXPIRY 0x0004 +#define TIMER_NOT_STARTED 0x0008 +#define TIMER_RETIMED 0x0010 // timeout has changed, restart timer without any processing + +// The timer Q itself +typedef struct +{ + DEFINE_LOCK_STRUCTURE( SpinLock ) + LIST_ENTRY ActiveHead; + LIST_ENTRY FreeHead; + +} tTIMERQ; + +// +// Function Prototype - this function is only called locally to this file +// + + +// +// TimerExpiry routine - Called by kernel upon timer expiration. Note that +// DeferredContext is the only argument used and must be named/used the +// same between NT and WFW. +// +VOID +TimerExpiry( +#ifndef VXD + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArg1, + IN PVOID SystemArg2 +#else + IN CTEEvent * pCTEEvent, + IN PVOID DeferredContext +#endif + ) ; + +#endif diff --git a/private/ntos/nbt/inc/types.h b/private/ntos/nbt/inc/types.h new file mode 100644 index 000000000..cf8b5e58b --- /dev/null +++ b/private/ntos/nbt/inc/types.h @@ -0,0 +1,2449 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Types.h + +Abstract: + + + This file contains the typedefs and constants for Nbt. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#ifndef _TYPES_H +#define _TYPES_H + +#pragma warning( disable : 4103 ) + +#include "nbtnt.h" +#include "ctemacro.h" +#include "debug.h" +#include "timer.h" +#include <nbtioctl.h> + +#ifndef VXD +#include <netevent.h> +#endif + + +//---------------------------------------------------------------------------- +// +// a flag to tell the transport to reindicate remaining data +// currently not supported by the transport +// +#define TDI_RECEIVE_REINDICATE 0x00000200 // remaining TSDU should cause another indication to the client + +// +// In debug builds, write flink and blink with invalid pointer values so that +// if an entry is removed twice from a list, we bugcheck right there, instead +// of being faced with a corrupted list some years later! +// +#if DBG +#undef RemoveEntryList +#define RemoveEntryList(Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_Flink;\ + PLIST_ENTRY _EX_OrgEntry;\ + _EX_OrgEntry = (Entry);\ + _EX_Flink = (Entry)->Flink;\ + _EX_Blink = (Entry)->Blink;\ + _EX_Blink->Flink = _EX_Flink;\ + _EX_Flink->Blink = _EX_Blink;\ + _EX_OrgEntry->Flink = (LIST_ENTRY *)__LINE__;\ + _EX_OrgEntry->Blink = (LIST_ENTRY *)__LINE__;\ + } +#endif + +// +// Netbios name size restrictions +// +#define NETBIOS_NAME_SIZE 16 +#define MAX_DNS_NAME_LENGTH 256 +#define MAX_NBT_DGRAM_SIZE 512 + +// +// To distinguish NBNS server ipaddr from DNS server ipaddr in common routines +// +#define NBNS_MODE 1 +#define DNS_MODE 2 + +// buffer size to start for registry reads +#define REGISTRY_BUFF_SIZE 512 +// +// this is the amount of memory that nbt will allocate max for datagram +// sends, before it rejects datagram sends with insufficient resources, since +// nbt buffers datagram sends to allow them to complete quickly to the +// client. - 128k - it can be set via the registry using the Key +// MaxDgramBuffering +// +#define DEFAULT_DGRAM_BUFFERING 0x20000 + +// +// the hash bucket structure - number of buckets - these should be set by +// a value read from the registry (small/medium/large). If the registry +// does not contain the values then these are used as defaults. +// +#define NUMBER_BUCKETS_LOCAL_HASH_TABLE 0x10 +#define NUMBER_BUCKETS_REMOTE_HASH_TABLE 0x10 +#define NUMBER_LOCAL_NAMES 10 +#define NUMBER_REMOTE_NAMES 10 +#define TIMER_Q_SIZE 20 + +#define MEDIUM_NUMBER_BUCKETS_LOCAL_HASH_TABLE 0x80 +#define MEDIUM_NUMBER_BUCKETS_REMOTE_HASH_TABLE 0x80 +#define MEDIUM_NUMBER_LOCAL_NAMES 20 +#define MEDIUM_NUMBER_REMOTE_NAMES 100 +#define MEDIUM_TIMER_Q_SIZE 1000 + +#define LARGE_NUMBER_BUCKETS_LOCAL_HASH_TABLE 255 +#define LARGE_NUMBER_BUCKETS_REMOTE_HASH_TABLE 255 +#define LARGE_NUMBER_LOCAL_NAMES 0xFFFF +#define LARGE_NUMBER_REMOTE_NAMES 255 +#define LARGE_TIMER_Q_SIZE 0xFFFF + +// +// max number of buffers of various types +// +#define NBT_INITIAL_NUM 2 +#define NBT_NUM_DGRAM_TRACKERS 0xFFFF +#define NBT_NUM_INITIAL_CONNECTIONS 2 +#ifndef VXD +#define NBT_NUM_IRPS 0xFFFF +#define NBT_NUM_SESSION_MDLS 0xFFFF +#define NBT_NUM_DGRAM_MDLS 0xFFFF +#else +#define NBT_NUM_SESSION_HDR 200 +#define NBT_NUM_SEND_CONTEXT 200 +#define NBT_NUM_RCV_CONTEXT 200 +#endif + +#define MEDIUM_NBT_NUM_DGRAM_TRACKERS 1000 +#define MEDIUM_NBT_NUM_INITIAL_CONNECTIONS 2 +#ifndef VXD +#define MEDIUM_NBT_NUM_IRPS 1000 +#define MEDIUM_NBT_NUM_SESSION_MDLS 1000 +#define MEDIUM_NBT_NUM_DGRAM_MDLS 1000 +#else +#define MEDIUM_NBT_NUM_SESSION_HDR 1000 +#define MEDIUM_NBT_NUM_SEND_CONTEXT 1000 +#define MEDIUM_NBT_NUM_RCV_CONTEXT 1000 +#endif + +#define LARGE_NBT_NUM_DGRAM_TRACKERS 0xFFFF +#define LARGE_NBT_NUM_INITIAL_CONNECTIONS 5 +#ifndef VXD +#define LARGE_NBT_NUM_IRPS 0xFFFF +#define LARGE_NBT_NUM_SESSION_MDLS 0xFFFF +#define LARGE_NBT_NUM_DGRAM_MDLS 0xFFFF +#else +#define LARGE_NBT_NUM_SESSION_HDR 0xFFFF +#define LARGE_NBT_NUM_SEND_CONTEXT 0xFFFF +#define LARGE_NBT_NUM_RCV_CONTEXT 0xFFFF +#endif + +// ip loop back address - does not go out on wire +// + +#define LOOP_BACK 0x7F000000 // in host order +#define NET_MASK 0xC0 // used to get network number from ip address + +// +// Nbt must indicate at least 128 bytes to its client, so it needs to be +// able to buffer 128 bytes + the session header (4) +// +#define NBT_INDICATE_BUFFER_SIZE 132 + + +#define IS_NEG_RESPONSE(OpcodeFlags) (OpcodeFlags & FL_RCODE) +#define IS_POS_RESPONSE(OpcodeFlags) (!(OpcodeFlags & FL_RCODE)) + +// +// where to locate or register a name - locally or on the network +// +enum eNbtLocation +{ + NBT_LOCAL, + NBT_REMOTE, + NBT_REMOTE_ALLOC_MEM +}; + +// these are bit mask values passed to freetracker in name.c to tell it what to do +// to free a tracker +// +#define FREE_HDR 0x0001 +#define REMOVE_LIST 0x0002 +#define RELINK_TRACKER 0x0004 + +// +// List of Ip addresses for internet group names, terminated with an address +// set to -1 +// +typedef struct +{ + ULONG IpAddr[1]; + +} tIPLIST; + + +// +// Hash Table basic structure +// +typedef struct +{ + LONG lNumBuckets; + DEFINE_LOCK_STRUCTURE( SpinLock ) + enum eNbtLocation LocalRemote; // type of data stored inhash table + LIST_ENTRY Bucket[1]; // array uTableSize long of hash buckets + +} tHASHTABLE; + +// +// the tNAMEADDR structure uses a bit mask to track which names are registered +// on which adapters. To support up to 64 adapters on NT make this a ULONGLONG, +// On the VXD the largest Long is 32 bits (they don't support _int64), so the +// adapter limit is 32 for the VXD. +// +#ifndef VXD +#define CTEULONGLONG ULONGLONG +#else +#define CTEULONGLONG ULONG +#endif + +// the format of each element linked to the hash buckets +// +typedef struct _tNAMEADDR +{ + LIST_ENTRY Linkage; // used to link onto bucket chains + + // for local names the scope is implied and is stored in the NbtGlobConfig + // structure, so the ptr to the scope block is used to point to the + // address element that corresponds to that name + union + { + struct _tNAMEADDR *pScope; // ptr to scope record in hash table + struct _Address *pAddressEle;// or ptr to address element + struct + { + USHORT MaxDomainAddrLength; // max # of ip addrs in Iplist for domain names from lmhosts + USHORT CurrentLength; // current # of above + }; + }; + + // for group names, more than one IP address is stored, so the Ipaddress + // field becomes a ptr to another block that stores the IP addresses. + // In addition, the NameTypeState must be set to NAMETYPE_GROUP + union + { + ULONG IpAddress; // 4 byte IP address + tIPLIST *pIpList; // list of ip addresses for internet group names + }; + + PULONG pIpAddrsList; // list of ipaddrs (internet grp names,multihomed dns hosts etc.) + + struct _TRACKER *pTracker; // contains tracker ptr during name resol. phase + + // if the name is a scope name, it does not have timers started against + // it, so we can use the same memory to store the length of the scope name + union + { + tTIMERQENTRY *pTimer; // ptr to active timer entry + PVOID ScopeLength; + }; + ULONG Ttl; // in milliseconds..ttl of name + ULONG RefCount; // if Greater than one, can't free memory + + ULONG NameTypeState; // group or unique name + state + + ULONG Verify; // for debug to tell remote from local names + + CTEULONGLONG AdapterMask; // bit mask of adapters name registered on (MH) + CTEULONGLONG RefreshMask; // bit mask of adapters name refreshed + + USHORT TimeOutCount; // Count to know when entry has timed out + BOOLEAN fProxyReq; //indicates whether the name was + //inserted as a result of hearing + //a name query or registration on the + //net +#ifdef PROXY_NODE + BOOLEAN fPnode; //indicates whether the node type is + //a Pnode +#endif + + CHAR Name[NETBIOS_NAME_SIZE]; // 16 byte Net Bios name + +} tNAMEADDR; + +// +// these values can be checked with a bit check since they are mutually +// exclusive bits... uses the first nibble of the field +// +#define NAMETYPE_QUICK 0x0001 // set if name is Quick Unique or Quick Group +#define NAMETYPE_UNIQUE 0x0002 +#define NAMETYPE_GROUP 0x0004 +#define NAMETYPE_INET_GROUP 0x0008 +#define NAMETYPE_SCOPE 0x1000 +// +// values for NameTypeState.. the state of the name occupies the second nibble +// of the field +// +#define STATE_RESOLVED 0x0010 // name query completed +#define STATE_RELEASED 0x0020 // no longer active +#define STATE_CONFLICT 0x0040 // proxy name addition to the name table +#define STATE_RESOLVING 0x0080 // name is waiting for Query or Reg. to complete +#define NAME_TYPE_MASK 0x000F +#define NAME_STATE_MASK 0x00F0 +#define REFRESHED 0x0100 // set if the name refresh response was received +#define TIMED_OUT 0x0200 // set if the timeout timer has happened once +#define TIMED_OUT_DEREF 0x0400 // set if the hash timeout routine has derefed the name. +#define PRELOADED 0x0800 // set if the entry is a preloaded entry - no timeout +#define REFRESH_MASK 0x0F00 + +#define LOCAL_NAME 0xDEAD0000 +#define REMOTE_NAME 0xFACF0000 + +// the number of timeouts per refresh time. The timer expires 8 times and +// refreshes every four (i.e. at twice the required refresh interval) +#define REFRESH_DIVISOR 0x0008 + +// +// two flags used in the NbtTdiOpenAddress procedure to decide which +// event handlers to setup +// +#define TCP_FLAG 0x00000001 +#define SESSION_FLAG 0x00000002 + +// +// these defines allow the code to run as either a Bnode or a Pnode +// or a proxy +// +extern USHORT NodeType; // defined in Name.c +#define BNODE 0x0001 +#define PNODE 0x0002 +#define MNODE 0x0004 +#define MSNODE 0x0008 +#define NODE_MASK 0x000F +#define PROXY 0x0010 +#define DEFAULT_NODE_TYPE 0x1000 + +// +// NT wants Unicode, Vxd wants ANSI +// +#ifdef VXD + #define __ANSI_IF_VXD(str) str +#else + #define __ANSI_IF_VXD(str) L##str +#endif +#define ANSI_IF_VXD( str ) __ANSI_IF_VXD( str ) + + +// +// Wide String defintions of values to read from the registry +// +#define WS_NUM_BCASTS ANSI_IF_VXD("BcastNameQueryCount") +#define WS_BCAST_TIMEOUT ANSI_IF_VXD("BcastQueryTimeout") +#define WS_CACHE_TIMEOUT ANSI_IF_VXD("CacheTimeout") +#define WS_NODE_TYPE ANSI_IF_VXD("NodeType") +#define WS_NS_PORT_NUM ANSI_IF_VXD("NameServerPort") +#define WS_DNS_PORT_NUM ANSI_IF_VXD("DnsServerPort") +#define WS_NAMESRV_RETRIES ANSI_IF_VXD("NameSrvQueryCount") +#define WS_NAMESRV_TIMEOUT ANSI_IF_VXD("NameSrvQueryTimeout") +#define WS_NODE_SIZE ANSI_IF_VXD("Size/Small/Medium/Large") +#define WS_KEEP_ALIVE ANSI_IF_VXD("SessionKeepAlive") +#define WS_LMHOSTS_FILE ANSI_IF_VXD("LmHostFile") +#define WS_ALLONES_BCAST ANSI_IF_VXD("BroadcastAddress") +#define NBT_SCOPEID ANSI_IF_VXD("ScopeId") +#define WS_RANDOM_ADAPTER ANSI_IF_VXD("RandomAdapter") +#define WS_SINGLE_RESPONSE ANSI_IF_VXD("SingleResponse") +#define WS_INITIAL_REFRESH ANSI_IF_VXD("InitialRefreshT.O.") +#define WS_ENABLE_DNS ANSI_IF_VXD("EnableDns") +#define WS_TRY_ALL_ADDRS ANSI_IF_VXD("TryAllIpAddrs") +#define WS_ENABLE_LMHOSTS ANSI_IF_VXD("EnableLmhosts") +#define WS_LMHOSTS_TIMEOUT ANSI_IF_VXD("LmhostsTimeout") +#define WS_MAX_DGRAM_BUFFER ANSI_IF_VXD("MaxDgramBuffering") +#define WS_ENABLE_PROXY_REG_CHECK ANSI_IF_VXD("EnableProxyRegCheck") +#define WS_WINS_DOWN_TIMEOUT ANSI_IF_VXD("WinsDownTimeout") +#define WS_MAX_CONNECTION_BACKLOG ANSI_IF_VXD("MaxConnBacklog") +#define WS_CONNECTION_BACKLOG_INCREMENT ANSI_IF_VXD("BacklogIncrement") +#define WS_REFRESH_OPCODE ANSI_IF_VXD("RefreshOpCode") + +#ifdef VXD +#define VXD_NAMETABLE_SIZE_NAME ANSI_IF_VXD("NameTableSize") +#define VXD_MIN_NAMETABLE_SIZE 1 +#define VXD_DEF_NAMETABLE_SIZE 17 + +#define VXD_SESSIONTABLE_SIZE_NAME ANSI_IF_VXD("SessionTableSize") +#define VXD_MIN_SESSIONTABLE_SIZE 1 +#define VXD_DEF_SESSIONTABLE_SIZE 255 + +#define VXD_LANABASE_NAME ANSI_IF_VXD("LANABASE") +#ifdef CHICAGO +#define VXD_ANY_LANA 0xff +#define VXD_DEF_LANABASE VXD_ANY_LANA +#else +#define VXD_DEF_LANABASE 0 +#endif + +#else + +#define WS_TRANSPORT_BIND_NAME ANSI_IF_VXD("TransportBindName") +#endif + +#ifdef PROXY_NODE + +#define NODE_TYPE_MASK 0x60 //Mask for NodeType in NBFLAGS byte of + //Query response +#define PNODE_VAL_IN_PKT 0x20 //A bit pattern of 01 in the NodeType fld + //of the Query response pkt indicates a + //P node +#define WS_IS_IT_A_PROXY ANSI_IF_VXD("EnableProxy") + +#define IS_NOT_PROXY 0 + +#endif +#define NBT_PROXY_DBG(x) KdPrint(x) + +// Various Default values if the above values cannot be read from the +// registry +// +#define DEFAULT_CACHE_TIMEOUT 360000 // 6 minutes in milliseconds +#define MIN_CACHE_TIMEOUT 60000 // 1 minutes in milliseconds +#define REMOTE_HASH_TIMEOUT 60000 // one minute timer +// +// timeouts - the defaults if the registry cannot be read. +// (time is milliseconds) +// The retry counts are the actual number of transmissions, not the number +// of retries i.e. 3 means the first transmission and 2 retries. Except +// for Bnode name registration where 3 registrations and 1 over write request +// are sent.(effectively 4 are sent). +// +#define DEFAULT_NUMBER_RETRIES 3 +#define DEFAULT_RETRY_TIMEOUT 1500 +#define MIN_RETRY_TIMEOUT 100 + +//#define MIN_RETRY_TIMEOUT 100 + +// the broadcasts values below related to broadcast name service activity +#define DEFAULT_NUMBER_BROADCASTS 3 +#define DEFAULT_BCAST_TIMEOUT 750 +#define MIN_BCAST_TIMEOUT 100 + +#define DEFAULT_NODE_SIZE 1 // BNODE +#define SMALL 1 +#define MEDIUM 2 +#define LARGE 3 + +#define DEFAULT_KEEP_ALIVE 0xFFFFFFFF // disabled by default +#define MIN_KEEP_ALIVE 60*1000 // 60 seconds in milliseconds +// +// The default is to use the subnet broadcast address for broadcasts rather +// than use 0xffffffff(i.e. when BroadcastAddress is not defined in the +// registery. If the registery variable BroadcastAddress is set to +// something that cannot be read for some reason, then the broadcast address +// gets set to this value. +// +#define DEFAULT_BCAST_ADDR 0xFFFFFFFF + +// a TTL value to use as a default (for refreshing names with WINS) +// +#define DEFAULT_TTL 5*60*1000 + +// +// Default TTL used for checking whether we need to switch back to the primary. currently 1 hour. +// +#define DEFAULT_SWITCH_TTL 1*60*60*1000 // millisecs. + +// +// we refresh every 16 minutes / 8 - so no more than once every two +// minutes until we reach WINS and get a new value +// +#define NBT_INITIAL_REFRESH_TTL 16*60*1000 // milliseconds +#define MAX_REFRESH_CHECK_INTERVAL 600000 // 10 minutes in msec + +// don't allow the refresh mechanism to run any faster than once per 5 minutes +#define NBT_MINIMUM_TTL 5*60*1000 // Milliseconds +#define NBT_MAXIMUM_TTL 0xFFFFFFFF // larges ULONG (approx 50 days) + +// +// the Mininimum and default timeouts to stop talking to WINS in the event +// that we fail to reach it once.(i.e. temporarily stop using it) +// +#define DEFAULT_WINS_DOWN_TIMEOUT 15000 // 15 seconds +#define MIN_WINS_DOWN_TIMEOUT 1000 // 1 second + +// +// Default max connections that can be in backlog +// +#define DEFAULT_CONN_BACKLOG 1000 +#define MIN_CONN_BACKLOG 2 +#define MAX_CONNECTION_BACKLOG 40000 // we allow only upto 40000 outstanding connections (~4MB) + +// +// Default max lower connection increment +// +#define DEFAULT_CONN_BACKLOG_INCREMENT 3 +#define MIN_CONN_BACKLOG_INCREMENT 3 +#define MAX_CONNECTION_BACKLOG_INCREMENT 20 // we allow only upto 20 new ones at a time + +// the minimum time to wait for a session setup pdu to complete - used to +// start a timer in name.c +#define NBT_SESSION_RETRY_TIMEOUT 10000 // 10 sec in milliseconds +// +// the number of times to attempt a session setup if the return code is +// Called Name Present but insufficient resources (0x83) - or if the +// destination does not have the name at all - in this case the session +// is just setup one more time, not 3 more times +// +#define NBT_SESSION_SETUP_COUNT 3 +// +// the following two lines allow proxy code to be compiled OUT if PROXY is not +// defined +// +#define IF_PROXY(Node) if ((Node) & PROXY) +#define END_PROXY + +#define IF_DEF_PROXY \ +#ifdef PROXY_NODE +#define END_DEF_PROXY \ +#endif + +// Status code specific to NBT that is used in the RcvHndlrNotOs to indicate +// that not enough data has been received, and more must be obtained. +// +#define STATUS_NEED_MORE_DATA 0xC0000999L + +#ifndef VXD + +// +// Logging definitions +// + +#define LOGSIZE 10000 +#define LOGWIDTH 32 + +typedef char STRM_RESOURCE_LOG[LOGSIZE+1][LOGWIDTH]; + +typedef struct { + STRM_RESOURCE_LOG Log; + CHAR Unused[3*LOGWIDTH]; // for overruns + int Index; +} STRM_PROCESSOR_LOG, *PSTRM_PROCESSOR_LOG; + +/* + * Definitions for the error logging facility + */ + +/* + * Maximum amount of data (binary dump data plus insertion strings) that + * can be added to an error log entry. + */ +#define MAX_ERROR_LOG_DATA_SIZE \ + ( (ERROR_LOG_MAXIMUM_SIZE - sizeof(IO_ERROR_LOG_PACKET) + 4) & 0xFFFFFFFC ) + + +// +// these are the names that NBT binds to, in TCP when it is opening address +// objects or creating connections. +// +#define NBT_TCP_BIND_NAME L"\\Device\\Streams\\" +#define NBT_MAINNAME_SERVICE L"NameServer" +#define NBT_BACKUP_SERVER L"NameServerBackup" +#define NBT_BIND L"Bind" +#define NBT_EXPORT L"Export" +#define NBT_PARAMETERS L"\\Parameters" +#endif + +#define NBT_ADDRESS_TYPE 01 +#define NBT_CONNECTION_TYPE 02 +#define NBT_CONTROL_TYPE 03 +#define NBT_WINS_TYPE 04 + + +// +// Maximum ip addresses that can be in an internet group - used as a sanity +// check to prevent allocating hugh amounts of memory +// +#define NBT_MAX_INTERNET_GROUP_ADDRS 1000 + +// define buffer types so we will know when we have allocated the maximum +// allowed number of buffers - this enum serves as an array index into the +// config data +// +enum eBUFFER_TYPES +{ + eNBT_DGRAM_TRACKER, + eNBT_TIMER_ENTRY, +#ifndef VXD + eNBT_FREE_IRPS, + eNBT_FREE_SESSION_MDLS, + eNBT_DGRAM_MDLS, +#else + eNBT_SESSION_HDR, + eNBT_SEND_CONTEXT, + eNBT_RCV_CONTEXT, +#endif + eNBT_NUMBER_BUFFER_TYPES // this type must be last on the list +}; + +// +// enumerate the types of name service broadcasts... either name registration +// or name query +// +enum eNSTYPE +{ + eNAME_QUERY, + eDNS_NAME_QUERY, + eDIRECT_DNS_NAME_QUERY, + eNAME_QUERY_RESPONSE, + eNAME_REGISTRATION, + eNAME_REGISTRATION_OVERWRITE, + eNAME_REGISTRATION_RESPONSE, + eNAME_RELEASE, + eNAME_REFRESH +}; + + +#define DIRECT_DNS_NAME_QUERY_BASE 0x8000 + +// +// Defines for the Verify elements of the handles passed to clients so that +// we can determine if we recieved the correct handle back from the client +// i.e. the Verify element must equal the correct define given here +// +#define NBT_VERIFY_ADDRESS 0x12345678 +#define NBT_VERIFY_LOWERCONN 0x11223344 +#define NBT_VERIFY_CONNECTION 0x87654321 +#define NBT_VERIFY_CONNECTION_DOWN 0x87654320 +#define NBT_VERIFY_CLIENT 0x18273645 +#define NBT_VERIFY_CLIENT_DOWN 0x18273640 +#define NBT_VERIFY_DEVCONTEXT 0x54637281 +#define NBT_VERIFY_CONTROL 0x56789123 +#define NBT_VERIFY_TRACKER 0x10231966 +#define NBT_VERIFY_BLOCKING_NCB 0x08121994 +// +// Session Header types from the RFC's +// +#define NBT_SESSION_MESSAGE 0x00 +#define NBT_SESSION_REQUEST 0x81 +#define NBT_POSITIVE_SESSION_RESPONSE 0x82 +#define NBT_NEGATIVE_SESSION_RESPONSE 0x83 +#define NBT_RETARGET_SESSION_RESPONSE 0x84 +#define NBT_SESSION_KEEP_ALIVE 0x85 +#define NBT_SESSION_FLAGS 0x00 // flag byte of Session hdr = 0 always +#define SESSION_NOT_LISTENING_ON_CALLED_NAME 0x80 +#define SESSION_NOT_LISTENING_FOR_CALLING_NAME 0x81 +#define SESSION_CALLED_NAME_NOT_PRESENT 0x82 +#define SESSION_CALLED_NAME_PRESENT_NO_RESRC 0x83 +#define SESSION_UNSPECIFIED_ERROR 0x8F + +// +// Address Info structure used to return buffer in TDI_QUERY_ADDRESS_INFO +// +#include <packon.h> +typedef struct +{ + ULONG ActivityCount; + TA_NETBIOS_ADDRESS NetbiosAddress; + +} tADDRESS_INFO; +#include <packoff.h> +// +// Name Registration error codes per the RFC +// +#define REGISTRATION_NO_ERROR 0x0 +#define REGISTRATION_FORMAT_ERR 0x1 +#define REGISTRATION_SERVER_ERR 0x2 +#define REGISTRATION_UNSUPP_ERR 0x4 +#define REGISTRATION_REFUSED_ERR 0x5 +#define REGISTRATION_ACTIVE_ERR 0x6 +#define REGISTRATION_CONFLICT_ERR 0x7 + +#define NBT_NAMESERVER_UDP_PORT 137 // port used by the Name Server +#define NBT_DNSSERVER_UDP_PORT 53 // port used by the DNS server +#define NBT_NAMESERVICE_UDP_PORT 137 +#define NBT_DATAGRAM_UDP_PORT 138 +#define NBT_SESSION_TCP_PORT 139 +#define IP_ANY_ADDRESS 0 // means broadcast IP address to IP +#define WINS_SIGNATURE 0xFF // put into QdCount to tell signal name reg from this node +// +// whether an address is unique or a group address... these agree with the +// values in TDI.H for TDI_ADDRESS_NETBIOS_TYPE_UNIQUE etc.. but are easier to +// type! +// +enum eNbtAddrType +{ + NBT_UNIQUE, + NBT_GROUP, + NBT_QUICK_UNIQUE, // these two imply that the name is registered on the + NBT_QUICK_GROUP // net when it is claimed +}; + +// +// this type defines the session hdr used to put session information +// into each client pdu sent +// +#include <packon.h> +typedef union +{ + union + { + struct + { + UCHAR Type; + UCHAR Flags; + USHORT Length; + }; + ULONG UlongLength; + }; + PSINGLE_LIST_ENTRY Next; +} tSESSIONHDR; + +// Session response PDU +typedef struct +{ + UCHAR Type; + UCHAR Flags; + USHORT Length; + UCHAR ErrorCode; + +} tSESSIONERROR; + +// Session Retarget response PDU +typedef struct +{ + UCHAR Type; + UCHAR Flags; + USHORT Length; + ULONG IpAddress; + USHORT Port; + +} tSESSIONRETARGET; + +// the structure for the netbios name itself, which includes a length +// byte at the start of it +typedef struct +{ + UCHAR NameLength; + CHAR NetBiosName[1]; + +} tNETBIOS_NAME; + +// session request packet...this is the first part of it. It still needs the +// calling netbios name to be appended on the end, but we can't do that +// until we know how long the Called Name is. +typedef struct +{ + tSESSIONHDR Hdr; + tNETBIOS_NAME CalledName; + +} tSESSIONREQ; + +// the type definition to queue empty session header buffers in a LIST +typedef union +{ + tSESSIONHDR Hdr; + LIST_ENTRY Linkage; +} tSESSIONFREE; + +// this type definition describes the NetBios Datagram header format on the +// wire +typedef union +{ + struct + { + UCHAR MsgType; + UCHAR Flags; + USHORT DgramId; + ULONG SrcIpAddr; + USHORT SrcPort; + USHORT DgramLength; + USHORT PckOffset; + tNETBIOS_NAME SrcName; + }; + LIST_ENTRY Linkage; + +} tDGRAMHDR; + +typedef struct +{ + UCHAR MsgType; + UCHAR Flags; + USHORT DgramId; + ULONG SrcIpAddr; + USHORT SrcPort; + UCHAR ErrorCode; + +} tDGRAMERROR; + +// define the header size since just taking the sizeof(tDGRAMHDR) will be 1 byte +// too large and if for any reason this data structure changes later, things +// fail to work for unknown reasons.... This size includes the Hdr + the +// two half ascii src and dest names + the length byte in each name + the +// It does +// not include the scope. That must be added separately(times 2). +#define DGRAM_HDR_SIZE 80 +#define MAX_SCOPE_LENGTH 255 +#define MAX_LABEL_LENGTH 63 + +// Name Service header +typedef struct +{ + USHORT TransactId; + USHORT OpCodeFlags; + UCHAR Zero1; + UCHAR QdCount; + UCHAR Zero2; + UCHAR AnCount; + UCHAR Zero3; + UCHAR NsCount; + UCHAR Zero4; + UCHAR ArCount; + tNETBIOS_NAME NameRR; + +} tNAMEHDR; + +// +// the offset from the end of the question name to the field +// in a name registration pdu ( includes 1 for the length byte of the name +// since ConvertToAscii does not count that value +// +#define QUERY_NBFLAGS_OFFSET 10 +#define NBFLAGS_OFFSET 16 +#define IPADDRESS_OFFSET 18 +#define PTR_OFFSET 4 // offset to PTR in Name registration pdu +#define NO_PTR_OFFSET 10 // offset to NbFlags after name +#define PTR_SIGNATURE 0xC0 // ptrs to names in pdus start with C + +// the structure of a DNS label is the count of the number of bytes followed +// by the label itself. Each part of a dot delimited name is a label. +// Fred.ms.com is 3 labels.- Actually 4 labels where the last one is zero +// length - hence all names end in a NULL +typedef struct +{ + UCHAR uSizeLabel; // top two bits are set to 1 when next 14 bits point to actual label in msg. + CHAR pLabel[1]; // variable length of label -> 63 bytes + +} tDNS_LABEL; +// top two bits set to signify a ptr to a name follows in the next 14 bits +#define PTR_TO_NAME 0xC0 + +// question section for the resource record modifiers +typedef struct +{ + ULONG QuestionTypeClass; +} tQUESTIONMODS; + +#define QUEST_NBINTERNET 0x00200001 // combined type/class +#define QUEST_DNSINTERNET 0x00010001 // combined type/class for dns query +#define QUEST_NETBIOS 0x0020 // General name service Resource Record +#define QUEST_STATUS 0x0021 // Node Status resource Record +#define QUEST_CLASS 0x0001 // internet class + +// Resource Record format - in the Name service packets +// General format RrType = 0x20 +typedef struct +{ + tQUESTIONMODS Question; + tDNS_LABEL RrName; + ULONG RrTypeClass; + ULONG Ttl; + USHORT Length; + USHORT Flags; + ULONG IpAddress; + +} tGENERALRR; +// Resource Record format - in the Name service packets +// General format RrType = 0x20 +typedef struct +{ + ULONG RrTypeClass; + ULONG Ttl; + USHORT Length; + USHORT Flags; + ULONG IpAddress; + +} tQUERYRESP; + +// same as tQUERYRESP, except no Flags field +// DNS servers return only 4 bytes of data (ipaddress): no flags. +typedef struct +{ + USHORT RrType; + USHORT RrClass; + ULONG Ttl; + USHORT Length; + ULONG IpAddress; + +} tDNS_QUERYRESP; + +#define DNS_CNAME 5 + +// +// the format of the tail of the node status response message +// +typedef struct +{ + UCHAR Name[NETBIOS_NAME_SIZE]; + UCHAR Flags; + UCHAR Resrved; + +} tNODENAME; + +typedef struct +// the statistics portion of the node status message +{ + UCHAR UnitId[6]; + UCHAR Jumpers; + UCHAR TestResult; + USHORT VersionNumber; + USHORT StatisticsPeriod; + USHORT NumberCrcs; + USHORT NumberAlignmentErrors; + USHORT NumberCollisions; + USHORT NumberSendAborts; + ULONG NumberSends; + ULONG NumberReceives; + USHORT NumberTransmits; + USHORT NumberNoResrcConditions; + USHORT NumberFreeCommandBlks; + USHORT TotalCommandBlocks; + USHORT MaxTotalCommandBlocks; + USHORT NumberPendingSessions; + USHORT MaxNumberPendingSessions; + USHORT MaxTotalSessionsPossible; + USHORT SessionDataPacketSize; + +} tSTATISTICS; + +typedef struct +{ + ULONG RrTypeClass; + ULONG Ttl; + USHORT Length; + UCHAR NumNames; + tNODENAME NodeName[1]; // there are NumNames of these + +} tNODESTATUS; + +typedef struct +{ + USHORT NbFlags; + ULONG IpAddr; + +} tADDSTRUCT; + +// Flags Definitions +#define FL_GROUP 0x8000 +#define FL_BNODE 0x0000 // note that this has no bits set!! +#define FL_PNODE 0x2000 +#define FL_MNODE 0x4000 + +//Redirect type Address record - RrType = 0x01 +typedef struct +{ + USHORT RrType; + USHORT RrClass; + ULONG Ttl; + USHORT DataLength; + ULONG IpAddress; + +} tIPADDRRR; + +//Redirect type - Name Server Resource Record RrType = 0x02 +typedef struct +{ + USHORT RrType; + USHORT RrClass; + ULONG Ttl; + USHORT DataLength; + CHAR Name[1]; // name starts here for N bytes - till null + +} tREDIRECTRR; + +//Null type- WACK- RrType = 0x000A +typedef struct +{ + USHORT RrType; + USHORT RrClass; + ULONG Zeroes; + USHORT Null; + +} tWACKRR; + +// definitions of the bits in the OpCode portion of the OpCodeFlag word +// These definitions work on a 16 bit word rather than the 5 bit opcode and 7 +// bit flag +#define NM_FLAGS_MASK 0x0078 +#define OP_RESPONSE 0x0080 +#define OP_QUERY 0x0000 +#define OP_REGISTRATION 0x0028 +#define OP_REGISTER_MULTI 0x0078 // new multihomed registration(Byte) op code +#define OP_RELEASE 0x0030 +#define OP_WACK 0x0038 +#define OP_REFRESH 0x0040 +#define OP_REFRESH_UB 0x0048 // UB uses 9 instead of 8 (Ref. RFC 1002) +#define REFRESH_OPCODE 0x8 +#define UB_REFRESH_OPCODE 0x9 +#define FL_RCODE 0x0F00 +#define FL_NAME_ACTIVE 0x0600 // WINS is reporting another name active +#define FL_NAME_CONFLICT 0x0700 // another node is reporting name active +#define FL_AUTHORITY 0x0004 +#define FL_TRUNCFLAG 0x0002 +#define FL_RECURDESIRE 0x0001 +#define FL_RECURAVAIL 0x8000 +#define FL_BROADCAST 0x1000 +#define FL_BROADCAST_BYTE 0x10 +// used to determine if the source is a Bnode for Datagram distribution +#define SOURCE_NODE_MASK 0xFC + +// defines for the node status message +#define GROUP_STATUS 0x80 +#define UNIQUE_STATUS 0x00 +#define NODE_NAME_PERM 0x02 +#define NODE_NAME_ACTIVE 0x04 +#define NODE_NAME_CONFLICT 0x08 +#define NODE_NAME_RELEASED 0x10 +#define STATUS_BNODE 0x00 +#define STATUS_PNODE 0x20 +#define STATUS_MNODE 0x40 + + +// Resource record defines - rrtype and rr class +#define RR_NETBIOS 0x0020 +#define RR_INTERNET 0x0001 + +// Name Query Response Codes +#define QUERY_NOERROR 00 +#define FORMAT_ERROR 01 +#define SERVER_FAILURE 02 +#define NAME_ERROR 03 +#define UNSUPP_REQ 04 +#define REFUSED_ERROR 05 +#define ACTIVE_ERROR 06 // name is already active on another node +#define CONFLICT_ERROR 07 // unique name is owned by more than one node + + +// +// Minimum Pdu lengths that will be accepted from the wire +// +#define NBT_MINIMUM_QUERY 50 +#define NBT_MINIMUM_WACK 58 +#define NBT_MINIMUM_REGRESPONSE 62 +#define NBT_MINIMUM_QUERYRESPONSE 56 +#define NBT_MINIMUM_REGREQUEST 68 +#define NBT_MINIMUM_NAME_QUERY_RESPONSE 56 +#define DNS_MINIMUM_QUERYRESPONSE 34 + +typedef struct +{ + tDGRAMHDR DgramHdr; + CHAR SrcName[NETBIOS_NAME_SIZE]; + CHAR DestName[NETBIOS_NAME_SIZE]; + +} tDGRAM_NORMAL; + +typedef struct +{ + tDGRAMHDR DgramHdr; + UCHAR ErrorCode; + +} tDGRAM_ERROR; + +typedef struct +{ + tDGRAMHDR DgramHdr; + CHAR DestName[NETBIOS_NAME_SIZE]; + +} tDGRAM_QUERY; + +#include <packoff.h> + + +// the buffer type passed to the TDI routines so that the datagram or session +// header can be included too. +typedef struct +{ + ULONG HdrLength; + PVOID pDgramHdr; + ULONG Length; + PVOID pBuffer; + +} tBUFFER; + +// +// This typedef is used by DgramHandlrNotOs to keep track of which client +// is receiving a datagram and which client's need to also get the +// datagram +typedef struct +{ + struct _Address *pAddress; + ULONG ReceiveDatagramFlags; + PVOID pRemoteAddress; + ULONG RemoteAddressLength; + struct _Client *pClientEle; + BOOLEAN fUsingClientBuffer; + BOOLEAN fProxy; //used by PROXY code for getting the + //entire datagram. See + //CompletionRcvDgram in tdihndlrs.c + +} tCLIENTLIST; + + +// Active Datagram Send List - a set of linked blocks that represent transactions +// passed to the Transport TDI for execution... these blocks could be waiting +// for name resolution or for the send to complete + +typedef struct _TRACKER +{ + // The type of address dictates the course of action, e.g., TDI_ADDRESS_NETBIOS_EX + // address avoids NETBIOS name registration. This encodes the desired address type + // associated with the connection or specified for the connection. + + ULONG AddressType; + + union { + // This datastructure tracks datagram sends. + struct + { + LIST_ENTRY Linkage; + LIST_ENTRY TrackerList; + ULONG Verify; + PCTE_IRP pClientIrp; // client's IRP + union + { + struct _Client *pClientEle; // client element block + struct _Connect *pConnEle; // connection element block + tNAMEADDR *p1CNameAddr; // DomainNames 1C pNameAddr - used sending datagrams + }; + + tBUFFER SendBuffer; // send buffer and header to send + + PTDI_CONNECTION_INFORMATION pSendInfo; + struct _DeviceContext *pDeviceContext; + union + { + ULONG UNALIGNED *pHdrIpAddress; // address of ip address in pDgramHdr + tTIMERQENTRY *pTimer; + PVOID pNodeStatus; // node status response buffer + }; + + union + { + ULONG RefCount; + USHORT IpListIndex; // index into IpList for Group sends + USHORT SavedListIndex; //last index sent when timer was started + ULONG DestPort; // used by ReTarget to specify the dest port + }; + + union + { + PCHAR pDestName; // ptr to destination ASCII name + tNAMEADDR *pNameAddr; // ptr to name addres rec in hash tbl + }; + + #ifdef VXD + PUCHAR pchDomainName; + #endif + union + { + PVOID pTimeout; // used for the TCP connect timeout -from union below + ULONG NodeStatusLen;// pNodeStatus buffer length + + // used for name queries and registrations to check if response has + // same transactionid + USHORT TransactionId; + ULONG RCount; // refcount used for datagram dist. + }; + + union + { + ULONG AllocatedLength; // used in Sending Dgrams to count mem allocated + ULONG RefConn; // used for NbtConnect + ULONG SrcIpAddress;// used for node status + }; + + // + // when two name queries go to the same name, this is the + // completion routine to call for this tracker queued to the first + // tracker. + // + COMPLETIONCLIENT CompletionRoutine; + USHORT Flags; + //#if DBG + LIST_ENTRY DebugLinkage; // to keep track of used trackers + //#endif + + }; + // this next version of the structure is used to track Session Sends as + // opposed to Datagram sends + struct + { + LIST_ENTRY Linkage; + LIST_ENTRY TrackerList; + ULONG Verify; + PCTE_IRP pClientIrp; + struct _Connect *pConnEle; // connection element block + + tBUFFER SendBuffer; // send buffer and header to send + + PTDI_CONNECTION_INFORMATION pSendInfo; + struct _DeviceContext *pDeviceContext; + tTIMERQENTRY *pTimer; // timer q entry + + ULONG RefCount; + + union + { + PCHAR pDestName; // ptr to destination ASCII name + tNAMEADDR *pNameAddr; // ptr to name addres rec in hash tbl + }; + + PVOID pTimeout; // used for the TCP connect timeout + + ULONG RefConn; + // + // when two name queries go to the same name, this is the + // completion routine to call for this tracker queued to the first + // tracker. + // + COMPLETIONCLIENT CompletionRoutine; + USHORT Flags; + + }Connect; + }; + + ULONG NumAddrs; + PULONG IpList; +} tDGRAM_SEND_TRACKING; + +// this is the type of the procedure to call in the session receive handler +// as data arrives - a different procedure per state +typedef + NTSTATUS + (*tCURRENTSTATEPROC)( + PVOID ReceiveEventContext, + struct _LowerConnection *pLowerConn, + USHORT RcvFlags, + ULONG BytesIndicated, + ULONG BytesAvailable, + PULONG pBytesTaken, + PVOID UNALIGNED pTsdu, + PVOID *ppIrp); +#ifdef VXD +#define SetStateProc( pLower, StateProc ) +#else +#define SetStateProc( pLower, StateProc ) ((pLower)->CurrentStateProc = (StateProc)) +#endif + +// this structure is necessary so that the NBT_WORK_ITEM_CONTEXT will have +// the same structure in the vxd and NT cases. +// +typedef struct +{ + LIST_ENTRY List; +} VXD_WORK_ITEM; + +// +// Work Item structure for work items put on the Kernel Excutive worker threads +// +typedef struct +{ +#ifndef VXD + WORK_QUEUE_ITEM Item; // Used by OS to queue these requests +#else + VXD_WORK_ITEM Item; +#endif + tDGRAM_SEND_TRACKING *pTracker; + PVOID pClientContext; + PVOID ClientCompletion; + BOOLEAN TimedOut; + +} NBT_WORK_ITEM_CONTEXT; + + +#ifdef VXD + +typedef void (*DCCallback)( PVOID pContext ) ; + +typedef struct +{ + NBT_WORK_ITEM_CONTEXT dc_WIC ; // Must be first item in structure + CTEEvent dc_event ; + DCCallback dc_Callback ; + struct _DeviceContext *pDeviceContext; + LIST_ENTRY Linkage; +} DELAYED_CALL_CONTEXT, *PDELAYED_CALL_CONTEXT ; + +typedef struct +{ + LIST_ENTRY Linkage; + ULONG Verify; + NCB *pNCB; + CTEBlockStruc *pWaitNCBBlock; + BOOL fNCBCompleted; + BOOL fBlocked; +} BLOCKING_NCB_CONTEXT, *PBLOCKING_NCB_CONTEXT; + +#endif + + +// A Listen is attached to a client element attached address element when a +// client does a listen +typedef VOID (*tRequestComplete) + (PVOID, + TDI_STATUS, + PVOID); + +typedef struct +{ + LIST_ENTRY Linkage; + PCTE_IRP pIrp; // IRP ptr for NT only (may not be true) + tRequestComplete CompletionRoutine; + PVOID pConnectEle; // the connection that the Listen is active on + PVOID Context; + ULONG Flags; + TDI_CONNECTION_INFORMATION *pConnInfo; // from a Listen + TDI_CONNECTION_INFORMATION *pReturnConnInfo; // from a Listen + +} tLISTENREQUESTS; + +typedef struct +{ + LIST_ENTRY Linkage; + PCTE_IRP pIrp; // IRP ptr for NT only (may not be true) + PVOID pRcvBuffer; + ULONG RcvLength; + PTDI_CONNECTION_INFORMATION ReceiveInfo; + PTDI_CONNECTION_INFORMATION ReturnedInfo; + +} tRCVELE; + +// +// Values for the Flags element above +#define NBT_BROADCAST 0x0001 +#define NBT_NAME_SERVER 0x0002 +#define NBT_NAME_SERVER_BACKUP 0x0004 +#define NBT_DNS_SERVER 0x0080 +#define NBT_DNS_SERVER_BACKUP 0x0100 +#define NBT_NAME_SERVICE 0x0010 // two flags used by Tdiout to send dgrams +#define NBT_DATAGRAM_SERVICE 0x0020 +#define TRACKER_CANCELLED 0x0040 +#define WINS_NEG_RESPONSE 0x0200 +#define REMOTE_ADAPTER_STAT_FLAG 0x1000 +#define SESSION_SETUP_FLAG 0x2000 +#define DGRAM_SEND_FLAG 0x4000 +#define FIND_NAME_FLAG 0x8000 + + +// +// this flag indicates that a datagram send is still outstanding in the +// transport - it is set in the tracker flags field. +// +#define SEND_PENDING 0x0080 + + +// Completion routine definition for calls to the Udp... routines. This routine +// type is called by the tdiout.c completion routine (the Irp completion routine), +// so this is essentially the Nbt completion routine of the Irp. +typedef + VOID + (*NBT_COMPLETION)( + IN PVOID, // context + IN NTSTATUS, // status + IN ULONG); // extra info + + +// Define datagram types +#define DIRECT_UNIQUE 0x10 +#define DIRECT_GROUP 0x11 +#define BROADCAST_DGRAM 0x12 +#define ERROR_DGRAM 0x13 +#define QUERY_REQUEST 0x14 +#define POS_QUERY_RESPONSE 0x15 +#define NEG_QUERY_RESPONSE 0x16 + +// define datagra flags byte values +#define FIRST_DGRAM 0x02 +#define MORE_DGRAMS 0x01 + +// the possible states of the lower connection to the transport +enum eSTATE +{ + NBT_IDLE, // not Transport connection + NBT_ASSOCIATED, // associated with an address element + NBT_RECONNECTING, // waiting for the Worker thread to run NbtConnect again + NBT_CONNECTING, // establishing Transport connection + NBT_SESSION_INBOUND, // waiting for a session request after tcp connection setup inbound + NBT_SESSION_WAITACCEPT, // waiting for accept after a listen has been satisfied + NBT_SESSION_OUTBOUND, // waiting for a session response after tcp connection setup + NBT_SESSION_UP, // got positive response + NBT_DISCONNECTING, // sent a disconnect down to Tcp, but it hasn't completed yet + NBT_DISCONNECTED // a session has been disconnected but not closed with TCP yet +}; + +// +// The default disconnect timeout used in several places in name.c +// +#define DEFAULT_DISC_TIMEOUT 10 // seconds + +// +// this is the value that the IpListIndex is set to when the last datagram +// has been sent. +// +#define LAST_DGRAM_DISTRIBUTION 0xFFFD +#define END_DGRAM_DISTRIBUTION 0xFFFE +// max 500 millisec timeout for ARP on dgram send before netbt sends the next +// datagram. +#define DGRAM_SEND_TIMEOUT 500 + +// +// These are other states for connections that are not explicitly used by +// NBT but are returned on the NbtQueryConnectionList call. +// +#define LISTENING 20; +#define UNBOUND 21; + +// Lower Connection states that deal with receiving to the indicate buffer +#define NORMAL 0 +#define INDICATE_BUFFER 1 +#define FILL_IRP 2 +#define PARTIAL_RCV 3 + +// Spin Lock Numbers. Each structure is assigned a number so that locks are +// always acquired in the same order. The CTESpinLock code checks the lock +// number before setting the spin lock and asserts if it is higher than +// the current one. This prevents deadlocks. +#define JOINT_LOCK 0x0001 +#define ADDRESS_LOCK 0x0002 +#define DEVICE_LOCK 0x0004 +#define CONNECT_LOCK 0x0008 +#define CLIENT_LOCK 0x0010 +#define LOWERCON_LOCK 0x0020 +#define NBTCONFIG_LOCK 0x0040 + + +// these are two bits to indicated the state of a client element record +// +#define NBT_ACTIVE 1 +#define NBT_DOWN 0 + +// this structure is used by the parse.c to hold on to an Irp from the +// lmhsvc.dll that is used for checking IP addr reachability. +// +typedef struct +{ + PCTE_IRP QueryIrp; // irp passed down from lmhsvc.dll + LIST_ENTRY ToResolve; // linked list of names Q'd to resolve + PVOID Context; // currently resolving name context block + BOOLEAN ResolvingNow; // irp is in user mode doing a resolve +} tCHECK_ADDR; + +// this structure is used by the parse.c to hold on to an Irp from the +// lmhsvc.dll that is used for DNS name queries +// +typedef struct +{ + PCTE_IRP QueryIrp; // irp passed down from lmhsvc.dll + LIST_ENTRY ToResolve; // linked list of names Q'd to resolve + PVOID Context; // currently resolving name context block + BOOLEAN ResolvingNow; // irp is in user mode doing a resolve +} tDNS_QUERIES; + +// +// This structure is used to queue lmhost name query requests while one +// request is pending with the file system, so that we do not use more than +// one executive worker thread +// +typedef struct +{ + LIST_ENTRY ToResolve; // linked list of names Q'd to resolve + PVOID Context; // currently resolving name context block + BOOLEAN ResolvingNow; // irp is in user mode doing a resolve + tTIMERQENTRY *pTimer; // non null if the timer is running + +} tLMHOST_QUERIES; + +#define DEFAULT_LMHOST_TIMEOUT 6000 // 6-12 to wait for lmhost or DNS query +#define MIN_LMHOST_TIMEOUT 1000 // 1 seconds min + +// +// Lmhosts Domain Controller List - keeps a list of #DOM names that have +// been retrieved from the LMhosts file +// +typedef struct +{ + LIST_ENTRY DomainList; + +} tDOMAIN_LIST; +// +// The pIpList of a domain name starts with 6 ulongs of space +// +#define INITIAL_DOM_SIZE sizeof(ULONG)*6 + +#ifndef VXD +// +// This structure keeps track of the WINS recv Irp and any datagram +// queued to go up to WINS (name service datagrams) +// +typedef struct +{ + PCTE_IRP RcvIrp; // irp passed down from WINS for Rcv + LIST_ENTRY RcvList; // linked list of Datagrams Q'd to rcv + LIST_ENTRY SendList; // Dgrams Q'd to be sent + ULONG RcvMemoryAllocated; // bytes buffered so far + ULONG RcvMemoryMax; // max # of bytes to buffer on Rcv + ULONG SendMemoryAllocated;// bytes for buffering dgram sends + ULONG SendMemoryMax; // max allowed for buffering dgram sends + + struct _DeviceContext *pDeviceContext; // the devicecontext used by wins for sends + +} tWINS_INFO; + +// +// Wins Rcv Buffer structure +// +typedef struct +{ + LIST_ENTRY Linkage; + ULONG DgramLength; + tREM_ADDRESS Address; + +} tWINSRCV_BUFFER; +#endif +// Connection Database... +// this tracks the connection to the transport and the address of the +// endpoint (Net Bios name) and a connection Context to return to the client +// on each Event (i.e. Receive event or Disconnect Event ). +typedef struct _Connect +{ + LIST_ENTRY Linkage; // ptrs to next in chain + + ULONG Verify; //3 set to a known value to verify block + DEFINE_LOCK_STRUCTURE( SpinLock ) //4 to lock access on an MP machine + struct _LowerConnection *pLowerConnId; //5 connection ID to transport + struct _Client *pClientEle; //6 ptr to client record + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + struct _DeviceContext *pDeviceContext; +#endif + + LIST_ENTRY Active; //7-8 list of Active sends + LIST_ENTRY RcvHead; //9-10 List of Rcv Buffers + + CONNECTION_CONTEXT ConnectContext;//11 ret to client on each event + + UCHAR RemoteName[NETBIOS_NAME_SIZE] ; // 12 +#ifndef VXD + // keep an extra MDL per connection if needed to handle multichunk rcvs + PMDL pNewMdl; // 16 + + // this tracks how full the current Mdl is when we are in the FILLIRP + // state. + ULONG CurrentRcvLen; //17 #of bytes to recv for Irp + ULONG FreeBytesInMdl;//18 (upper word) +#else + // + // Name of remote session endpoint + // + UCHAR RTO ; // NB Receive timeout (in 1/2 sec) + UCHAR STO ; // NB Send timeout + USHORT Flags ; +#endif + + ULONG TotalPcktLen; //18 length of session packet + ULONG BytesInXport; //19 number of bytes left in the transport + ULONG BytesRcvd; //20 number of bytes recvd so far + ULONG ReceiveIndicated; //20 count of number of rcv indicates not handled yet + + union + { +#ifndef VXD + // the next MDL in the chain to use as a basis for a partial MDL + PMDL pNextMdl; //21 +#endif + tDGRAM_SEND_TRACKING *pTracker; //21 used when session is setup to pt to tracker for name query + }; + + // the amount of the Mdl/NCB that has been filled + ULONG OffsetFromStart;// 22 + PCTE_IRP pIrp; //23 IRP ptr for a send + PCTE_IRP pIrpClose; //24 IRP for an NtClose + // if a disconnect comes down when the connection is pending stash + // the disconnect irp here till the connect completes + PCTE_IRP pIrpDisc; //25 + + PCTE_IRP pIrpRcv; //26 IRP that client has passed down for a session Rcv + + LONG RefCount; //27 number of active requests on the connection + enum eSTATE state; //28 + + // need to know if this is an originating connection when we cleanup so + // we know if a connection in the freelist should be deleted or not + BOOLEAN Orig; //29 + UCHAR LockNumber; //29 spin lock number for this struct + + // the session setup is tried this number of times in case the + // destination is in between listens, if the return code is + // NoListen, then retry session setup. + UCHAR SessionSetupCount; //29 + + // this tracks if the disconnect was an abort or a normal release + // it is passed to the client as a return status in NbtDisconnect if + // the client does a disconnect wait. + UCHAR DiscFlag; //29 + BOOLEAN JunkMsgFlag; + +#ifdef RASAUTODIAL + // this field is TRUE if an automatic connection is in progress + // for this connection. we use this to prevent an infinite + // number of automatic connection attempts to the same address. + BOOLEAN fAutoConnecting; + + // this field is TRUE if this connection has already been + // autoconnected. + BOOLEAN fAutoConnected; +#endif + + // The NetBt Connection logic manages a pool of lower connection blocks. These + // entries are replenished with every associate call made from the client. + // The entries are removed with a fresh invocation to NbtConnectCommon. + // Since it is conceivable that multiple invocations to NbtConnectCommon can be + // made this state needs to be recorded in the connect element. + BOOLEAN LowerConnBlockRemoved; + + // The DNS status of the remote name is recorded in this field. It is set to + // FALSE on creation of a tCONNECTELE instance and changed to TRUE if the + // DNS resolution for the Remote Name fails. This is used to throttle back + // subsequent requests for the same DNS name. + BOOLEAN RemoteNameDoesNotExistInDNS; + + // The type of address over which the connection was established. This + // field is used to store the type of address that was used to establish the + // connection. The valid address types are the TDI_ADDRESS_TYPE_* constants + // defined in tdi.h + ULONG AddressType; + +} tCONNECTELE; + + +// a list of connections to the transport. For each connection opened +// to the transport, the connection context is set to the address of this +// block so that when info comes in on the conection, the pUpperConnection +// ptr can be used to find the client connection +typedef struct _LowerConnection +{ + LIST_ENTRY Linkage; + ULONG Verify; + DEFINE_LOCK_STRUCTURE( SpinLock ) + struct _Connect *pUpperConnection; //4 ptr to upper conn. to client + + // Contains Handle/Object of a TDI Connection for incoming connections +#ifndef VXD + HANDLE FileHandle; // file handle for connection to transport +#endif + CTE_ADDR_HANDLE pFileObject; // file object for the connection + + // Address object handles - used for outgoing connections only since + // inbound connections are all bound to the same address object (port 139) + // + // The VXD uses only pAddrFileObject which contains the openned address + // handle (used for compatibility with existing code). + // +#ifndef VXD + HANDLE AddrFileHandle; // file handle for the address +#endif + CTE_ADDR_HANDLE pAddrFileObject; // file object for the address + + // this is a ptr to the device context so we can put the connection + // back on its free list at the end of the session. + struct _DeviceContext *pDeviceContext; + +#ifndef VXD + // This mdl holds up to 128 bytes that are mandated by TDI as the + // minimum number of bytes for an indication + // + PMDL pIndicateMdl; //11 + + // these are for query provider statistics + ULONGLONG BytesRcvd; //12 + ULONGLONG BytesSent; //14 + + // keep the initial value of the Client's MDL + // incase we receive a session PDU in several chunks, then we can set the + // MDL back to its original value when it has all been received + PMDL pMdl; //16 + + // the number of bytes in the indicate buffer + USHORT BytesInIndicate; +#else + // + // The VXD only has to worry about getting enough data for the + // session header (as opposed to 128 bytes for the NT indication + // buffer). + tSESSIONHDR Hdr ; + + // these are for query provider statistics + ULONG BytesRcvd; + ULONG BytesSent; + + // + // When a lower connection goes into the partial receive state, it is + // put on the pClientEle->PartialRcvHead until an NCB is submitted to + // get that data + // + LIST_ENTRY PartialRcvList ; + + // + // Number of bytes in Hdr + // + USHORT BytesInHdr ; +#endif + // the receive state = Normal/UseIndic/FillIrp/PartialRcv + USHORT StateRcv; //17 + + ULONG SrcIpAddr; //18 + + enum eSTATE State; //19 + + LONG RefCount; //20 the number of active users of record + + // + // Irp to complete after disconnect completes + // + PCTE_IRP pIrp; // 21 + +#ifndef VXD + // in the receive handler this procedure variable is used to point to the + // procedure to call for the current state (Normal,FillIrp,IndicateBuff,RcvPartial) + + tCURRENTSTATEPROC CurrentStateProc; //22 + + // this boolean tells if we are receiving into the indicate buffer right now + BOOLEAN bReceivingToIndicateBuffer; //23 +#endif + + UCHAR LockNumber; // spin lock number for this struct + + // set to TRUE if the connection originated on this side + BOOLEAN bOriginator; + // this flag is set true when executing the session Rcv Handler so we + // can detect this state and not free ConnEle or LowerConn memory. + BOOLEAN InRcvHandler; + // + // This is set when the IP address changes, and the connection is + // still open, so that when the connection is cleaned up it is + // closed rather than being put back on the free list + // + BOOLEAN DestroyConnection; //24 + + // + // Set when this connection is queued onto the OutOfRsrc list in NbtConfig to + // indicate to DeleteLowerConn not to try and remove this from any list. + // + BOOLEAN OutOfRsrcFlag; + + // + // Was this allocated to tackle the TCP/IP SynAttack problem? + // + BOOLEAN SpecialAlloc; + +#ifdef VXD + BOOLEAN fOnPartialRcvList; +#endif + +} tLOWERCONNECTION; + + +// the Client list is just a list of linked blocks where the address of the +// block is the HANDLE returned to the client - The client block is linked onto +// the chain of clients that have opened the address. +typedef struct _Client +{ + LIST_ENTRY Linkage; // double linked list to next client + ULONG Verify; // set to a known value to verify block + PCTE_IRP pIrp; // IRP ptr for NT only... during name registration + DEFINE_LOCK_STRUCTURE( SpinLock ) // spin lock synch. access to structure + + struct _Address *pAddress; // ptr to address object that this client is Q'd on + LIST_ENTRY ConnectHead; // list of connections + LIST_ENTRY ConnectActive; // list of connections that are in use + + LIST_ENTRY RcvDgramHead; // List of dgram buffers to recv into + + LIST_ENTRY ListenHead; // List of Active Listens + + LIST_ENTRY SndDgrams; // a doubly linked list of Dgrams to send + +#ifdef VXD + LIST_ENTRY RcvAnyHead ; // List of RCV_CONTEXT for NCB Receive any + + BOOL fDeregistered; // TRUE if the name has been deleted and + // we are waiting for sessions to close +#endif + PTDI_IND_CONNECT evConnect; // Client Event to call + PVOID ConEvContext; // EVENT Context to pass to client + PTDI_IND_RECEIVE evReceive; + PVOID RcvEvContext; + PTDI_IND_DISCONNECT evDisconnect; + PVOID DiscEvContext; + PTDI_IND_ERROR evError; + PVOID ErrorEvContext; + PTDI_IND_RECEIVE_DATAGRAM evRcvDgram; + PVOID RcvDgramEvContext; + PTDI_IND_RECEIVE_EXPEDITED evRcvExpedited; + PVOID RcvExpedEvContext; + PTDI_IND_SEND_POSSIBLE evSendPossible; + PVOID SendPossEvContext; + + struct _DeviceContext *pDeviceContext; // the device context associated with this connection + + LONG RefCount; + UCHAR LockNumber; // spin lock number for this struct + // if several clients register the same name at the same time this + // flag is set. It is reset in RegisterCompletion. + BOOLEAN WaitingForRegistration; + + // The address type of the associated AddressEle structure. This is + // a stashed value of the original value stored in the address element. + ULONG AddressType; + UCHAR EndpointName[NETBIOS_NAME_SIZE]; + BOOLEAN ExtendedAddress; +} tCLIENTELE; + +// The Address List is a set of blocks that contain the netbios names active +// on the node. Each time a connection request comes in or a datagram is +// received, the destination name must be found in the address list. +// There is one of these for each Netbios name in the system, although there +// can be more than one client attached to each of these. In addition, +// a client can open the same name for several different adapters. In this case +// the nbt code first finds the ptr to this address element, and then it walks +// the client list to find a client with a "deviceContext" (ip address) that +// matches the adapter that the pdu came in on. +typedef struct _Address +{ + LIST_ENTRY Linkage; // link to next item in list + ULONG Verify; // set to a known value to verify block + DEFINE_LOCK_STRUCTURE( SpinLock ) // to lock structure on MP machines + + LIST_ENTRY ClientHead; // list of client records Q'd against address + tNAMEADDR *pNameAddr; // ptr to entry in hash table + LONG RefCount; + struct _DeviceContext *pDeviceContext; // the device context associated with this connection + +#ifndef VXD + SHARE_ACCESS ShareAccess; // Used for checking share access + PSECURITY_DESCRIPTOR SecurityDescriptor; // used to hold ACLs on the address + +#endif + + USHORT NameType; // Group or Unique NAMETYPE_UNIQUE or group + + UCHAR LockNumber; // spin lock number for this struct + + // signals Dgram Rcv handler that more than one client exists - set + // when names are added in NbtOpenAddress + BOOLEAN MultiClients; + + // The type of address specified in opening the connection object. The values + // are the same as the TDI_ADDRESS_TYPE_ constants. Currently the only valid + // values are TDI_ADDRESS_TYPE_NETBIOS and TDI_ADDRESS_TYPE_NETBIOS_EX. + ULONG AddressType; +} tADDRESSELE; + + +// this structure is used to store the addresses of the name servers +// for each adapter - it is used in Registry.c and Driver.c +// +typedef struct +{ + ULONG NameServerAddress; + ULONG BackupServer; + +}tADDRARRAY; + +typedef struct +{ + UCHAR Address[6]; + +}tMAC_ADDRESS; + +// this type is the device context which includes NBT specific data +// that is initialized when "DriverEntry" is called. +// + +// +// The transport type is used to distinguish the additional transport types +// that can be supported, NETBT framing without NETBIOS name resolution/registration +// and NETBIOS over TCP. The transport type is stored as part of all the upper +// level data structures. This enables us to reuse NETBT code and expose multiple +// device objects at the top level. Currently these are not exposed. In preparation +// for exporting multiple transport device objects the DeviceContext has been +// reorganized. +// The elements that would be common to all the device objects have been gathered +// together in tCOMMONDEVICECONTEXT. This will include an enumerated type to +// distinguish between the various device objects in the future. Currently there is only +// one device context and the fields that belong to it are listed ... +// +// RegistrationHandle -- PNP power, +// enumerated type distinguishing the types. +// + +typedef struct _DeviceContext +{ +#ifndef VXD + // the I/O system's device object + DEVICE_OBJECT DeviceObject; +#endif // !VXD + + // a linkage to store these in a linked list in the tNBTCONFIG structure + LIST_ENTRY Linkage; + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + // a linkage to store free, re-usable contexts in a linked list in the tNBTCONFIG structure + LIST_ENTRY FreeLinkage; +#endif + + // to lock access to this structure on an MP machine + DEFINE_LOCK_STRUCTURE( SpinLock ) + + // a value to verify that this is a device context record + ULONG Verify; + + // ulong since we use interlocked ops to manipulate it. + ULONG IsDestroyed; + +#if DBG + BOOLEAN IsDynamic; +#endif + // connections that the clients have created + LIST_ENTRY UpConnectionInUse; + + // connections to the transport provider that are currently in use + LIST_ENTRY LowerConnection; // blocks in use + + // these are connected to the transport but not currently connected to + // a client(upper) connection - ready to accept an incoming session + // + LIST_ENTRY LowerConnFreeHead; // list of unused connections down + +#ifdef VXD + // + // List of RCV_CONTEXT for NCB Receive any from any request (is here + // because any receive to anybody can satisfy this request) + // + LIST_ENTRY RcvAnyFromAnyHead ; + + // + // When a lower connection goes into the partial receive state, it is + // put on this list until an NCB is submitted to get the data + // + LIST_ENTRY PartialRcvHead ; + + // + // Dgram receives that specified a name number of 0xff (ANY_NAME) + // + LIST_ENTRY RcvDGAnyFromAnyHead ; + + // + // all events that are scheduled for later on this device context + // + LIST_ENTRY DelayedEvents; + + tCLIENTELE * * pNameTable ; // Name table for this adapter + tCONNECTELE * * pSessionTable ; // Session table + UCHAR iNcbNum ; // Next available NCB Num in table + UCHAR iLSNum ; // Next avaialabe LSN in table + UCHAR cMaxNames ; // Maximum adapter names (exc. Perm Name) + UCHAR cMaxSessions ; // Maximum sessions + + UCHAR IPIndex ; // Index of IP address in IP driver + UCHAR iLana; // Lana index + BOOLEAN fDeviceUp; // to avoid ncb's coming when not ready + UCHAR Pad1[1] ; + + HANDLE hBroadcastAddress ;// Handle openned with NbtOpenAddress on '*' +#else + // name of the device to bind to - *TODO* remove from this struct. + UNICODE_STRING BindName; + // name exported by this device + UNICODE_STRING ExportName; +#endif //!VXD + + // addresses that need to be opened with the transport + ULONG IpAddress; + ULONG SubnetMask; + ULONG BroadcastAddress; + ULONG NetMask; // mask for the network number + + // + // handles for open addresses to the transport provider + // + // The VXD uses only the p*FileObject fields which contain TDI Addresses + // or connection IDs + // +#ifndef VXD + HANDLE hNameServer; // from ZwCreateFile + PDEVICE_OBJECT pNameServerDeviceObject; // from pObject->DeviceObject +#endif //!VXD + CTE_ADDR_HANDLE pNameServerFileObject; // from ObReferenceObjectByHandle(hNameServer) + +#ifndef VXD + HANDLE hDgram; + PDEVICE_OBJECT pDgramDeviceObject; +#endif //!VXD + CTE_ADDR_HANDLE pDgramFileObject; + +#ifndef VXD + HANDLE hSession; + PDEVICE_OBJECT pSessionDeviceObject; +#endif //!VXD + CTE_ADDR_HANDLE pSessionFileObject; + +#ifndef VXD + // these are handles to the transport control object, so we can do things + // like query provider info... *TODO* this info may not need to be kept + // around... just set it up once and then drop it? + HANDLE hControl; + PDEVICE_OBJECT pControlDeviceObject; + PFILE_OBJECT pControlFileObject; + +#endif //!VXD + + // the Ip Address of the Name Server + ULONG lNameServerAddress; + ULONG lBackupServer; + +#ifdef VXD + // the Ip Addresses of the DNS Servers + ULONG lDnsServerAddress; + ULONG lDnsBackupServer; +#endif //VXD + // ptr to the permanent name client record in case we have to delete + // it later. + tCLIENTELE *pPermClient; + + // this is a bit mask, a bit shifted to the bit location corresponding + // to this adapter number + CTEULONGLONG AdapterNumber; // bit mask for adapters 1->64 (1-32 for VXD) + tMAC_ADDRESS MacAddress; + + UCHAR LockNumber; // spin lock number for this struct + BOOLEAN RefreshToBackup; // flag to say switch to backup nameserver + BOOLEAN PointToPoint; // set if the transport is RAS device + BOOLEAN WinsIsDown; // per Devcontext flag to tell us not to contact WINS for 15 sec. +#ifdef _PNP_POWER + HANDLE RegistrationHandle; // Handle returned from TdiRegisterDeviceObject. +#endif // _PNP_POWER + ULONG InstanceNumber; +} tDEVICECONTEXT; + + +#ifdef VXD + +typedef struct +{ + // + // Book keeping. + // + LIST_ENTRY Linkage; + tTIMERQENTRY *pTimer; // ptr to active timer entry + tDEVICECONTEXT *pDeviceContext; + // + // Domain name for next query. + // + PUCHAR pchDomainName; + // + // Flags to track progress. + // + USHORT Flags; + // + // Transaction ID used in name query. + // + USHORT TransactId; + // + // Client fields follow. + // + NCB *pNCB; + PUCHAR pzDnsName; + PULONG pIpAddress; + +} DNS_DIRECT_WORK_ITEM_CONTEXT, *PDNS_DIRECT_WORK_ITEM_CONTEXT; + +typedef struct +{ + tDEVICECONTEXT *pDeviceContext; + TDI_CONNECTION_INFORMATION + SendInfo; + TA_IP_ADDRESS NameServerAddress; + tBUFFER SendBuffer; // send buffer and header to send + tNAMEHDR NameHdr; +} DNS_DIRECT_SEND_CONTEXT, *PDNS_DIRECT_SEND_CONTEXT; + +// +// Flag bits useful for DNS direct name queries. +// +#define DNS_DIRECT_CANCELLED 0x0001 // request cancelled +#define DNS_DIRECT_DNS_SERVER 0x0002 // going to main DNS +#define DNS_DIRECT_DNS_BACKUP 0x0004 // going to main DNS +#define DNS_DIRECT_TIMED_OUT 0x0008 // request timed out +#define DNS_DIRECT_NAME_HAS_DOTS 0x0010 // name has dots in it, could be fully formed + // DNS specifier +#define DNS_DIRECT_ANSWERED 0x0020 // This query has been answered +#endif // VXD + +#ifndef VXD +// configuration information is passed between the registry reading +// code and the driver entry code in this data structure +// see ntdef.h for this type.... +typedef struct +{ + // this has a pointer to the actual string buffer in it, so to work + // correctly this buffer pointer must be set to point to an actual + // buffer + UNICODE_STRING Names[NBT_MAXIMUM_BINDINGS]; + PVOID RegistrySpace; // space to store the above strings + // allocated with ExAllocatePool + +}tDEVICES; +#endif + +// this is the control object for all of NBT that tracks a variety +// of counts etc. There is a ptr to this in the GlobConfig structure +// below so that we can delete it later at clean up time +typedef struct +{ + DEFINE_LOCK_STRUCTURE( SpinLock ) + // a value to verify that this is a device context record + ULONG Verify; + + // this is a LARGE structure of goodies that is returned to the client + // when they do a QueryInformation on TDI_PROVIDER_INFO + TDI_PROVIDER_INFO ProviderInfo; + + +} tCONTROLOBJECT; + +// overall spin lock to coordinate access to timer entries and hash tables +// at the same time. Always get the joint lock FIRST and then either the +// hash or timer lock. Be sure not to get both hash and timer locks or +// dead lock could result +// +typedef struct +{ + DEFINE_LOCK_STRUCTURE( SpinLock ) + UCHAR LockNumber; + + // + // In non-debug builds, the lock structure goes away altogther + // + #if defined(VXD) && !defined( DEBUG ) + int iDummy ; + #endif + +}tJOINTLOCK; + +// Keep an Irp around for the out of resource case, so that we can still +// disconnection a connection with the transport +// Also, keep a KDPC handy so if we can use it in case lot of connections +// are to be killed in succession +// +typedef struct +{ + PIRP pIrp; + LIST_ENTRY ConnectionHead; +#ifndef VXD + PKDPC pDpc; +#endif + +} tOUTOF_RSRC; + +#define MAXIMUM_PROCESSORS 32 + +// this type holds NBT configuration information... is globally available +// within NBT using NbtConfig. +typedef struct +{ + DEFINE_LOCK_STRUCTURE( SpinLock ) + int NumConnections; // number of connections set in registry + int NumAddresses; // number of addresses node supports set in reg. + + // linked list of device contexts, one per network adapter (tDEVICECONTEXT) + LIST_ENTRY DeviceContexts; + + // linked list of device contexts, one per network adapter (tDEVICECONTEXT) + LIST_ENTRY FreeDevCtx; + + // counts to track how many buffer have been allocated so far + USHORT iCurrentNumBuff[eNBT_NUMBER_BUFFER_TYPES]; + USHORT iMaxNumBuff[eNBT_NUMBER_BUFFER_TYPES]; + USHORT iBufferSize[eNBT_NUMBER_BUFFER_TYPES]; + + USHORT Pad1 ; // eNBT_NUMBER_BUFFER_TYPES is odd + + // buffers to track Dgram sends... + LIST_ENTRY DgramTrackerFreeQ; + + // list of node status messages being sent + LIST_ENTRY NodeStatusHead; + + // allocated addresses in a linked list + LIST_ENTRY AddressHead; + + LIST_ENTRY PendingNameQueries; + +#ifdef VXD + LIST_ENTRY DNSDirectNameQueries; +#endif // VXD + +#ifndef VXD + // a ptr to keep track of the memory allocated to the control object + tCONTROLOBJECT *pControlObj; + + PDRIVER_OBJECT DriverObject; + + // a list of Irps, since we need them at Dispatch level and can't create + // them at that level + LIST_ENTRY IrpFreeList; + + // a list of MDLs for session sends to speed up sending session PDUs + SINGLE_LIST_ENTRY SessionMdlFreeSingleList; + + // a list of MDLs for datagram sends to speed up sending + SINGLE_LIST_ENTRY DgramMdlFreeSingleList; + + // a ptr to the registry Node for Netbt so we can read it later if + // DHCP requests come down. + UNICODE_STRING pRegistry; + + // a ptr to the name of the transport (i.e. \Device\Streams\") + PWSTR pTcpBindName; +#else + // + // Tracks all Send NCBs for the express purpose of checking if they have + // timed out yet. + // + LIST_ENTRY SendTimeoutHead ; + + // + // Free buffer lists, correspond to eNBT_SESSION_HDR, + // eNBT_SEND_CONTEXT and eNBT_RCV_CONTEXT buffer types respectively. + // + LIST_ENTRY SessionBufferFreeList ; + LIST_ENTRY SendContextFreeList ; // TDI_SEND_CONTEXT (not SEND_CONTEXT!) + LIST_ENTRY RcvContextFreeList ; + // + // all events that are scheduled for later (that apply to all device contexts) + // + LIST_ENTRY DelayedEvents; + LIST_ENTRY BlockingNcbs; +#endif //VXD + + // hash table to keep track of local and remote names + tHASHTABLE *pLocalHashTbl; + tHASHTABLE *pRemoteHashTbl; + +#ifndef VXD + // This structure keeps an Irp ready to disconnect a connection in the + // event we run out of resources and cannot do anything else. It also allows + // connections to Q up for disconnection. + tOUTOF_RSRC OutOfRsrc; + + // used to hold Address exclusively while registering - when mucking with + // ShareAccess and the security descriptors + // + ERESOURCE Resource; +#endif + + USHORT uNumDevices; // number of adapters counted in registry + USHORT uNumLocalNames; // size of remote hash table for Pnode + USHORT uNumRemoteNames; // size of remote hash table for Proxy + USHORT uNumBucketsRemote; + USHORT uNumBucketsLocal; + USHORT TimerQSize; // the number of timer Q blocks + +#ifdef _PNP_POWER + USHORT uDevicesStarted; // bindings/devices/local IP addresses in use + USHORT Pad2; +#endif // _PNP_POWER + + LONG uBcastTimeout; // timeout between Broadcasts + LONG uRetryTimeout; // timeout between retries + + USHORT uNumRetries; // number of times to send a dgram - applies to Queries to NS as well as Dgrams + USHORT uNumBcasts; // number of times to bcast name queries + + // the scope must begin with a length byte that gives the length of the next + // label, and it must end with a NULL indicating the zero length root + USHORT ScopeLength; // number of bytes in scope including the 0 on the end + USHORT SizeTransportAddress; // number of bytes in transport addr (sizeof TDI_ADDRESS_IP for IP) + PCHAR pScope; // scope if ScopeLength > 0 + + // a ptr to the netbios name record in the local hash table + tNAMEADDR *pBcastNetbiosName; + + // the shortest Ttl of any name registered by this node and the name that + // has the shortest Ttl + ULONG MinimumTtl; + ULONG RefreshDivisor; + ULONG RemoteHashTimeout; + // + // This is amount of time to stop talking to WINS when we fail to contact + // it on a name registration. Nominally around 5 seconds - configurable. + // + ULONG WinsDownTimeout; + + // timer entry for refreshing names with WINS + tTIMERQENTRY *pRefreshTimer; + tTIMERQENTRY *pSessionKeepAliveTimer; + tTIMERQENTRY *pRemoteHashTimer; + + ULONG InitialRefreshTimeout; // to refresh names to WINS till we hear from WINS + ULONG KeepAliveTimeout; // keep alive timeout for sessions + ULONG RegistryBcastAddr; + + // the number of connections to restore when the ip address becomes + // valid again. + // + USHORT DhcpNumConnections; + USHORT CurrentHashBucket; + + USHORT PduNodeType; // node type that goes into NS pdus + USHORT TransactionId; // for name service request, to number them + + USHORT NameServerPort; // UDP port to send queries/reg to (on the name server) +#ifdef VXD + USHORT DnsServerPort; // UDP port to send DNS queries to (on the dns server) +#endif + USHORT sTimeoutCount; // current time segment we are on for refresh + + USHORT LastSwitchTimeoutCount; // Count to know when we last switched to primary + + // this spin lock is used to coordinate access to the timer Q and the + // hash tables when either a timer is expiring or a name service PDU + // has come in from the wire. This lock must be acquired first, and + // then the timer Q lock. + tJOINTLOCK JointLock; + UCHAR LockNumber; // spin lock number for this struct + USHORT RemoteTimeoutCount; // how long to timeout remote hash entries + + // if 1, then use -1 for bcast addr - if 0 use subnet broadcast address + BOOLEAN UseRegistryBcastAddr; + + // the maximum amount of buffering that we will do for sent datagrams + // This is also used by Wins to determine inbound and outbound buffer + // limits. + ULONG MaxDgramBuffering; + // this is the time that a name query can spend queued waiting for the + // the worker thread to service it. - default is 30 seconds. + ULONG LmHostsTimeout; + + PUCHAR pLmHosts; + ULONG PathLength; // the length of the directory portion of pLmHosts +#ifdef VXD + PUCHAR pHosts; // path to the hosts file + PUCHAR pDomainName; // primary domain: used during DNS resolution + PUCHAR pDNSDomains; // "other domains" + + // chicago needs this! + // to avoid having to read parms at CreateDevice time, we read some parms from + // registry at init time and store them here (ndis may not honour our request to + // read registry at arbitrary times e.g. dhcp renew occurs). + // + ULONG lRegistryNameServerAddress; + ULONG lRegistryBackupServer; + + ULONG lRegistryDnsServerAddress; + ULONG lRegistryDnsBackupServer; + + USHORT lRegistryMaxNames; + USHORT lRegistryMaxSessions; +#endif + + UCHAR AdapterCount;// number of network adapters. + BOOLEAN MultiHomed; // True if NBT is bound to more than one adapter + BOOLEAN SingleResponse; // if TRUE it means do NOT send all ip addresses on a name query request + + // if TRUE randomly select an IP addr on name query response rather than + // return the address that the request came in on. + BOOLEAN SelectAdapter; + + // This boolean tells Nbt to attempt name resolutions with DNS + BOOLEAN ResolveWithDns; + // Nbt tries all addresses of a multi-homed machine if this is TRUE (by default its TRUE). + BOOLEAN TryAllAddr; + // This boolean tells Nbt to attempt name resolutions with LMhosts + BOOLEAN EnableLmHosts; + // This allows a proxy to do name queries to WINS to check Bnode name + // registrations. By default this functionality is off since it could + // be a RAS client who has changed its IP address and the proxy will + // deny the new registration since it only does a query and not a + // registration/challenge. + // + BOOLEAN EnableProxyRegCheck; + + // set to true when a name refresh is active so a second refresh timeout + // will not start another refresh over the first one + BOOLEAN DoingRefreshNow; + UCHAR CurrProc; + // + // allow the refresh op code to be registry configured since UB uses + // a different value than everyone else due to an ambiguity in the spec. + // - we use 0x40 and they use 0x48 (8, or 9 in the RFC) + // + USHORT OpRefresh; + +#if DBG && !defined(VXD) + // NBT's current lock level - an array entry for up to 32 processors + ULONG CurrentLockNumber[MAXIMUM_PROCESSORS]; + DEFINE_LOCK_STRUCTURE(DbgSpinLock) +#endif + + ULONG InterfaceIndex; + +#if DBG + ULONG ActualNumSpecialLowerConn; +#endif + ULONG NumQueuedForAlloc; + ULONG NumSpecialLowerConn; + ULONG MaxBackLog; + ULONG SpecialConnIncrement; + +} tNBTCONFIG; + +extern tNBTCONFIG *pNbtGlobConfig; +extern tNBTCONFIG NbtConfig; +extern tNAMESTATS_INFO NameStatsInfo; +extern tCHECK_ADDR CheckAddr; +extern tDNS_QUERIES DnsQueries; // defined in parse.c +extern tDOMAIN_LIST DomainNames; +#ifndef VXD +extern tWINS_INFO *pWinsInfo; +extern PEPROCESS NbtFspProcess; +#endif +extern tLMHOST_QUERIES LmHostQueries; // defined in parse.c +extern ULONG NbtMemoryAllocated; + +#ifdef VXD +extern ULONG DefaultDisconnectTimeout; +#else +extern LARGE_INTEGER DefaultDisconnectTimeout; +#endif +// ************** REMOVE LATER******************** +extern BOOLEAN StreamsStack; + +//#if DBG +extern LIST_ENTRY UsedTrackers; +extern LIST_ENTRY UsedIrps; +//#endif +#endif + + diff --git a/private/ntos/nbt/inc/vxddebug.h b/private/ntos/nbt/inc/vxddebug.h new file mode 100644 index 000000000..c81e76965 --- /dev/null +++ b/private/ntos/nbt/inc/vxddebug.h @@ -0,0 +1,150 @@ +/**********************************************************************/ +/** Microsoft Windows NT **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + debug.h + + This file contains a number of debug-dependent definitions. + + + FILE HISTORY: + KeithMo 20-Sep-1993 Created. + +*/ + + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + + +#ifdef DEBUG + +#include <stdarg.h> + +#define DBG_MEMALLOC_VERIFY 0x0BEEFCAFE + +typedef struct { + LIST_ENTRY Linkage; // to keep linked list of allocated blocks + DWORD Verify; // our signature + DWORD ReqSize; // original size as requested by caller + DWORD Owner[4]; // stack trace 4 deep (of ret.addrs) +} DbgMemBlkHdr; + +LIST_ENTRY DbgMemList; +ULONG DbgLeakCheck; + +// +// Debug output control flags. +// + +extern DWORD VxdDebugFlags; + + +#define VXD_DEBUG_INIT 0x00000001L +#define VXD_DEBUG_SOCKET 0x00000002L +#define VXD_DEBUG_MISC 0x00000004L +#define VXD_DEBUG_BIND 0x00000008L +#define VXD_DEBUG_ACCEPT 0x00000010L +#define VXD_DEBUG_CONNECT 0x00000020L +#define VXD_DEBUG_LISTEN 0x00000040L +#define VXD_DEBUG_RECV 0x00000080L +#define VXD_DEBUG_SEND 0x00000100L +#define VXD_DEBUG_SOCKOPT 0x00000200L +#define VXD_DEBUG_CONFIG 0x00000400L +#define VXD_DEBUG_CONNECT_EVENT 0x00000800L +#define VXD_DEBUG_DISCONNECT_EVENT 0x00001000L +#define VXD_DEBUG_ERROR_EVENT 0x00002000L +#define VXD_DEBUG_RECV_EVENT 0x00004000L +#define VXD_DEBUG_RECV_DATAGRAM_EVENT 0x00008000L +#define VXD_DEBUG_RECV_EXPEDITED_EVENT 0x00010000L +// #define VXD_DEBUG_ 0x00020000L +// #define VXD_DEBUG_ 0x00040000L +// #define VXD_DEBUG_ 0x00080000L +// #define VXD_DEBUG_ 0x00100000L +// #define VXD_DEBUG_ 0x00200000L +// #define VXD_DEBUG_ 0x00400000L +// #define VXD_DEBUG_ 0x00800000L +// #define VXD_DEBUG_ 0x01000000L +// #define VXD_DEBUG_ 0x02000000L +// #define VXD_DEBUG_ 0x04000000L +// #define VXD_DEBUG_ 0x08000000L +// #define VXD_DEBUG_ 0x10000000L +// #define VXD_DEBUG_ 0x20000000L +// #define VXD_DEBUG_ 0x40000000L +#define VXD_DEBUG_OUTPUT_TO_DEBUGGER 0x80000000L + +// +// Debug output function. +// + +void VxdPrintf( char * pszFormat, + ... ); + +int VxdSprintf( char * pszStr, + char * pszFmt, + ... ); + +#define VXD_PRINT(args) VxdPrintf args + + +// +// Assert & require. +// + +void VxdAssert( void * pAssertion, + void * pFileName, + unsigned long nLineNumber ); + +#define VXD_ASSERT(exp) if (!(exp)) VxdAssert( #exp, __FILE__, __LINE__ ) +#define VXD_REQUIRE VXD_ASSERT + + +// +// Miscellaneous goodies. +// + +void VxdDebugOutput( char * pszMessage ); + +#define DEBUG_BREAK _asm int 3 +#define DEBUG_OUTPUT(x) VxdDebugOutput(x) + + +#else // !DEBUG + + +// +// No debug output. +// + +#define IF_DEBUG(flag) if (0) + + +// +// Null debug output function. +// + +#define VXD_PRINT(args) + + +// +// Null assert & require. +// + +#define VXD_ASSERT(exp) +#define VXD_REQUIRE(exp) ((void)(exp)) + + +// +// No goodies. +// + +#define DEBUG_BREAK +#define DEBUG_OUTPUT(x) + + +#endif // DEBUG + + +#endif // _DEBUG_H_ diff --git a/private/ntos/nbt/inc/vxdprocs.h b/private/ntos/nbt/inc/vxdprocs.h new file mode 100644 index 000000000..b24946af6 --- /dev/null +++ b/private/ntos/nbt/inc/vxdprocs.h @@ -0,0 +1,1121 @@ +/**********************************************************************/ +/** Microsoft Windows/NT **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + vxdprocs.h + + This file contains VxD specific types/manifests for the NBT driver + + + FILE HISTORY: + Johnl 29-Mar-1993 Created + +*/ + +#ifndef _VXDPROCS_H_ +#define _VXDPROCS_H_ + +//-------------------------------------------------------------------- +// +// Define some ndis stuff here because tdivxd.h needs it however we can't +// include ndis3\inc\ndis.h because it conflicts with ntconfig.h and we +// can't take out ntconfig.h because it has definitions needed by other +// header files...grrrr.... +// + +#ifdef CHICAGO +#ifndef NDIS_STDCALL +#define NDIS_STDCALL 1 +#endif +#include <vmm.h> +#undef PAGE +#define PAGE _PTEXT +#endif + +#ifdef NDIS_STDCALL +#define NDIS_API __stdcall +#else +#define NDIS_API +#endif + +// +// Ndis Buffer +// + +#define BUFFER_POOL_SIGN (UINT)0X4C50424E /* NBPL */ +#define BUFFER_SIGN (UINT)0x4655424e /* NBUF */ + +typedef INT NDIS_SPIN_LOCK, * PNDIS_SPIN_LOCK; + +struct _NDIS_BUFFER; +typedef struct _NDIS_BUFFER_POOL { + UINT Signature; //character signature for debug "NBPL" + NDIS_SPIN_LOCK SpinLock; //to serialize access to the buffer pool + struct _NDIS_BUFFER *FreeList; //linked list of free slots in pool + UINT BufferLength; //amount needed for each buffer descriptor + UCHAR Buffer[1]; //actual pool memory + } NDIS_BUFFER_POOL, * PNDIS_BUFFER_POOL; + +#ifdef NDIS_STDCALL +typedef struct _NDIS_BUFFER { + struct _NDIS_BUFFER *Next; //pointer to next buffer descriptor in chain + PVOID VirtualAddress; //linear address of this buffer + PNDIS_BUFFER_POOL Pool; //pointer to pool so we can free to correct pool + UINT Length; //length of this buffer + UINT Signature; //character signature for debug "NBUF" +} NDIS_BUFFER, * PNDIS_BUFFER; + +#else + +typedef struct _NDIS_BUFFER { + UINT Signature; //character signature for debug "NBUF" + struct _NDIS_BUFFER *Next; //pointer to next buffer descriptor in chain + PVOID VirtualAddress; //linear address of this buffer + PNDIS_BUFFER_POOL Pool; //pointer to pool so we can free to correct pool + UINT Length; //length of this buffer +} NDIS_BUFFER, * PNDIS_BUFFER; +#endif + +#define NDIS_STATUS_SUCCESS 0 // Used by CTEinitBlockStruc macro + +// +// Possible data types +// + +typedef enum _NDIS_PARAMETER_TYPE { + NdisParameterInteger, + NdisParameterHexInteger, + NdisParameterString, + NdisParameterMultiString +} NDIS_PARAMETER_TYPE, *PNDIS_PARAMETER_TYPE; + +typedef struct _STRING { + USHORT Length; + USHORT MaximumLength; + PUCHAR Buffer; +} STRING, *PSTRING; + +typedef STRING NDIS_STRING, *PNDIS_STRING; +typedef PVOID NDIS_HANDLE, *PNDIS_HANDLE; + +// +// To store configuration information +// +typedef struct _NDIS_CONFIGURATION_PARAMETER { + NDIS_PARAMETER_TYPE ParameterType; + union { + ULONG IntegerData; + NDIS_STRING StringData; + } ParameterData; +} NDIS_CONFIGURATION_PARAMETER, *PNDIS_CONFIGURATION_PARAMETER; + +typedef ULONG NDIS_STATUS; +typedef NDIS_STATUS *PNDIS_STATUS; + +VOID NDIS_API +NdisOpenProtocolConfiguration( + OUT PNDIS_STATUS Status, + OUT PNDIS_HANDLE ConfigurationHandle, + IN PNDIS_STRING ProtocolName + ); + +VOID NDIS_API +NdisReadConfiguration( + OUT PNDIS_STATUS Status, + OUT PNDIS_CONFIGURATION_PARAMETER *ParameterValue, + IN NDIS_HANDLE ConfigurationHandle, + IN PNDIS_STRING Parameter, + IN NDIS_PARAMETER_TYPE ParameterType + ); + +VOID NDIS_API +NdisCloseConfiguration( + IN NDIS_HANDLE ConfigurationHandle + ); + +//-------------------------------------------------------------------- + +#include <tdivxd.h> +#include <tdistat.h> + +//-------------------------------------------------------------------- +// +// Initializes a TA_NETBIOS_ADDRESS structure +// +// ptanb - Pointer to the TA_NETBIOS_ADDRESS +// pName - Pointer to the netbios name this address structure represents +// +#define InitNBAddress( ptanb, pName ) \ +{ \ + (ptanb)->TAAddressCount = 1 ; \ + (ptanb)->Address[0].AddressLength = sizeof( TDI_ADDRESS_NETBIOS );\ + (ptanb)->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS ; \ + (ptanb)->Address[0].Address[0].NetbiosNameType = 0 ; \ + CTEMemCopy( (ptanb)->Address[0].Address[0].NetbiosName, \ + pName, \ + NCBNAMSZ ) ; \ +} + +// +// Initializes a TDI_CONNECTION_INFORMATION structure for Netbios +// +// pConnInfo - Pointer to TDI_CONNECTION_INFORMATION structure +// ptanb - same as for InitNBAddress +// pName - same as for InitNBAddress +// +#define InitNBTDIConnectInfo( pConnInfo, ptanb, pName ) \ +{ \ + InitNBAddress( ((PTA_NETBIOS_ADDRESS)ptanb), (pName) ) ; \ + (pConnInfo)->RemoteAddressLength = sizeof( TA_NETBIOS_ADDRESS ) ; \ + (pConnInfo)->RemoteAddress = (ptanb) ; \ +} + +// +// Initializes an NDIS buffer (doesn't allocate memory) +// +// pndisBuff - Pointer to NDIS buffer to initialize +// pvData - Pointer to buffer data +// cbLen - Length of user data (in bytes) +// pndisBuffnext - Next NDIS buffer in chain (or NULL if last) +// +#define InitNDISBuff( pndisBuff, pvData, cbLen, pndisBuffNext ) \ +{ \ + (pndisBuff)->Signature = BUFFER_SIGN ; \ + (pndisBuff)->Next = (pndisBuffNext) ; \ + (pndisBuff)->Length = (cbLen) ; \ + (pndisBuff)->VirtualAddress = (pvData) ; \ + (pndisBuff)->Pool = NULL ; \ +} + +// +// Proper NCB error type +// +typedef uchar NCBERR ; + +// +// This is a private NCB command used for adding name number 0 to the +// name table. It is submitted directly by the Nbt driver during +// initialization. Note that if a client tries to submit an NCB with +// this command we'll return illegal command. +// +#define NCBADD_PERMANENT_NAME 0xff + +// +// Last valid NCB session or name number +// +#define MAX_NCB_NUMS 254 + +// +// When a send or receive tick count reaches this value, it's timed out +// +#define NCB_TIMED_OUT 1 + +// +// A timeout of this value means the NCB will never timeout +// +#define NCB_INFINITE_TIME_OUT 0 + +//-------------------------------------------------------------------- +// +// Receieve session data context, set in VxdReceive. +// Allocated on the heap (too big for ncb_reserve). +// +#define RCVCONT_SIGN 0x1900BEEF +typedef struct _RCV_CONTEXT +{ + union + { + LIST_ENTRY ListEntry ; // Used when NCB is put on RcvHead + EventRcvBuffer evrcvbuf ; // Used for doing actual receive + // (after removed from RcvHead) + } ; + UINT Signature ; + tLOWERCONNECTION * pLowerConnId ; // Where data is arriving from + NCB * pNCB ; // Pointer to NCB + NDIS_BUFFER ndisBuff ; // Transport fills this buffer + UCHAR RTO ; // 1/2 second ticks till timeout + USHORT usFlags; // in case different from default +} RCV_CONTEXT, *PRCV_CONTEXT ; + +// +// Allocate, initialize and free a receive context structure +// +#define GetRcvContext( ppContext ) \ + (STATUS_SUCCESS == NbtGetBuffer( &NbtConfig.RcvContextFreeList, \ + (PLIST_ENTRY*)ppContext, \ + eNBT_RCV_CONTEXT )) + +#define FreeRcvContext( pRcvContext ) \ +{ \ + ASSERT( (pRcvContext)->Signature == RCVCONT_SIGN ) ; \ + InsertTailList( &NbtConfig.RcvContextFreeList, \ + &(pRcvContext)->ListEntry ) ; \ +} + +#define InitRcvContext( pRcvCont, pRcvLowerConn, pRcvNCB ) \ +{ \ + pRcvCont->Signature = RCVCONT_SIGN ; \ + pRcvCont->pLowerConnId= pRcvLowerConn ; \ + pRcvCont->pNCB = pRcvNCB ; \ +} + +//-------------------------------------------------------------------- +// +// Send session data context, set in VxdSend. +// Stored in ncb_reserve +// +typedef struct _SEND_CONTEXT +{ + LIST_ENTRY ListEntry ; // Kept on timeout queue + tSESSIONHDR * pHdr ; // Allocated session header + UCHAR STO ; // 1/2 second ticks till timeout +} SEND_CONTEXT, *PSEND_CONTEXT ; + + +#define GetSessionHdr( ppHdr ) \ + (STATUS_SUCCESS == NbtGetBuffer( &NbtConfig.SessionBufferFreeList, \ + (PLIST_ENTRY*)ppHdr, \ + eNBT_SESSION_HDR )) + +#define FreeSessionHdr( pSessionHdr ) \ +{ \ + InsertTailList( &NbtConfig.SessionBufferFreeList, \ + (PLIST_ENTRY) pSessionHdr ) ; \ +} + +//-------------------------------------------------------------------- +// +// TDI Send context (used by TdiSend) +// +// When handling the datagram completion routines, we need to set up +// another completion routine. We store the old completion routine +// in this structure +// +typedef union _TDI_SEND_CONTEXT +{ + LIST_ENTRY ListEntry ; // Only used when on buffer free list + + struct + { + PVOID NewContext ; + NBT_COMPLETION OldRequestNotifyObject ; + PVOID OldContext ; + NDIS_BUFFER ndisHdr ; // Generally NBT message + NDIS_BUFFER ndisData1 ; // Data or SMB + NDIS_BUFFER ndisData2 ; // Data if ndisData1 is an SMB + } ; +} TDI_SEND_CONTEXT, * PTDI_SEND_CONTEXT ; + +// +// Allocates a TDI_SEND_CONTEXT +// +#define GetSendContext( ppContext ) \ + (STATUS_SUCCESS == NbtGetBuffer( &NbtConfig.SendContextFreeList, \ + (PLIST_ENTRY*)ppContext, \ + eNBT_SEND_CONTEXT )) + +// +// Frees a send context structure and its allocated memory +// +#define FreeSendContext( psendCont ) \ +{ \ + InsertTailList( &NbtConfig.SendContextFreeList, \ + &(psendCont)->ListEntry ) ; \ +} + +//-------------------------------------------------------------------- +// +// Lana related stuff +// + +#define NBT_MAX_LANAS 8 + +typedef struct +{ + tDEVICECONTEXT * pDeviceContext ; // Adapter for this Lana +} LANA_ENTRY, *PLANA_ENTRY ; + +extern LANA_ENTRY LanaTable[NBT_MAX_LANAS] ; + +//-------------------------------------------------------------------- +// +// Procedures in ncb.c +// +// +NCBERR MapTDIStatus2NCBErr( TDI_STATUS status ) ; + +// +// Get the correct adapter for this NCBs Lana +// +tDEVICECONTEXT * +GetDeviceContext( + NCB * pNCB + ); + +BOOL +NbtWouldLoopback( + ULONG IpAddr + ); + +extern BOOL fNCBCompleted ; // Wait NCB completed before returning to submitter +extern BOOL fWaitingForNCB ; // We are blocked waiting for a Wait NCB to complete +extern CTEBlockStruc WaitNCBBlock ; // Wait on this until signaled in completion +extern UCHAR LanaBase ; + +#define IPINFO_BUFF_SIZE (sizeof(IPInfo) + MAX_IP_NETS * sizeof(NetInfo)) + +//-------------------------------------------------------------------- +// +// externs from fileio.c +// +extern PUCHAR pFileBuff; +extern PUCHAR pFilePath; + +//-------------------------------------------------------------------- +// +// TDI Dispatch table (exported from vtdi.386) +// +extern TDIDispatchTable * TdiDispatch ; + +// +// Wrappers for interfacing to the TDI Dispatch table +// +#define TdiVxdOpenAddress TdiDispatch->TdiOpenAddressEntry +#define TdiVxdCloseAddress TdiDispatch->TdiCloseAddressEntry +#define TdiVxdOpenConnection TdiDispatch->TdiOpenConnectionEntry +#define TdiVxdCloseConnection TdiDispatch->TdiCloseConnectionEntry +#define TdiVxdAssociateAddress TdiDispatch->TdiAssociateAddressEntry +#define TdiVxdDisAssociateAddress TdiDispatch->TdiDisAssociateAddressEntry +#define TdiVxdConnect TdiDispatch->TdiConnectEntry +#define TdiVxdDisconnect TdiDispatch->TdiDisconnectEntry +#define TdiVxdListen TdiDispatch->TdiListenEntry +#define TdiVxdAccept TdiDispatch->TdiAcceptEntry +#define TdiVxdReceive TdiDispatch->TdiReceiveEntry +#define TdiVxdSend TdiDispatch->TdiSendEntry +#define TdiVxdSendDatagram TdiDispatch->TdiSendDatagramEntry +#define TdiVxdReceiveDatagram TdiDispatch->TdiReceiveDatagramEntry +#define TdiVxdSetEventHandler TdiDispatch->TdiSetEventEntry +#define TdiVxdQueryInformationEx TdiDispatch->TdiQueryInformationExEntry +#define TdiVxdSetInformationEx TdiDispatch->TdiSetInformationExEntry + +//-------------------------------------------------------------------- +// +// NTSTATUS to TDI_STATUS mappings. +// +// Rather then convert from NTSTATUS to TDI_STATUS (then sometimes back to +// NTSTATUS) we'll just use TDI_STATUS codes everywhere (and map to NCBERR +// when returning codes to the Netbios interface). +// +#undef STATUS_SUCCESS +#undef STATUS_INSUFFICIENT_RESOURCES +#undef STATUS_ADDRESS_ALREADY_EXISTS +#undef STATUS_TOO_MANY_ADDRESSES +#undef STATUS_INVALID_ADDRESS +#undef STATUS_BUFFER_OVERFLOW +#undef STATUS_TRANSACTION_INVALID_TYPE +#undef STATUS_TRANSACTION_INVALID_ID +#undef STATUS_EVENT_DONE +#undef STATUS_TRANSACTION_TIMED_OUT +#undef STATUS_EVENT_PENDING +#undef STATUS_PENDING +#undef STATUS_BAD_NETWORK_NAME +#undef STATUS_REQUEST_NOT_ACCEPTED +#undef STATUS_INVALID_CONNECTION +#undef STATUS_DATA_NOT_ACCEPTED +#undef STATUS_MORE_PROCESSING_REQUIRED +#undef STATUS_IO_TIMEOUT +#undef STATUS_TIMEOUT +#undef STATUS_GRACEFUL_DISCONNECT +#undef STATUS_CONNECTION_RESET + +#define STATUS_SUCCESS TDI_SUCCESS +//#define STATUS_UNSUCCESSFUL +#define STATUS_MORE_PROCESSING_REQUIRED TDI_MORE_PROCESSING +#define STATUS_BAD_NETWORK_NAME TDI_INVALID_CONNECTION +#define STATUS_DATA_NOT_ACCEPTED TDI_NOT_ACCEPTED +//#define STATUS_REMOTE_NOT_LISTENING +//#define STATUS_DUPLICATE_NAME +//#define STATUS_INVALID_PARAMETER +//#define STATUS_OBJECT_NAME_COLLISION Duplicate Name +//#define STATUS_SHARING_VIOLATION Duplicate Name +#define STATUS_CONNECTION_INVALID TDI_INVALID_CONNECTION +#define STATUS_INVALID_CONNECTION TDI_INVALID_CONNECTION +#define STATUS_INSUFFICIENT_RESOURCES TDI_NO_RESOURCES +#define STATUS_ADDRESS_ALREADY_EXISTS TDI_ADDR_IN_USE +#define STATUS_TOO_MANY_ADDRESSES TDI_NO_FREE_ADDR +#define STATUS_INVALID_ADDRESS TDI_ADDR_INVALID +#define STATUS_BUFFER_OVERFLOW TDI_BUFFER_OVERFLOW +#define STATUS_TRANSACTION_INVALID_TYPE TDI_BAD_EVENT_TYPE +#define STATUS_TRANSACTION_INVALID_ID TDI_BAD_OPTION // ?? +#define STATUS_EVENT_DONE TDI_EVENT_DONE +#define STATUS_TRANSACTION_TIMED_OUT TDI_TIMED_OUT +#define STATUS_IO_TIMEOUT TDI_TIMED_OUT +#define STATUS_TIMEOUT TDI_TIMED_OUT +#define STATUS_EVENT_PENDING TDI_PENDING +#define STATUS_PENDING TDI_PENDING +#define STATUS_GRACEFUL_DISCONNECT TDI_GRACEFUL_DISC +#define STATUS_CONNECTION_RESET TDI_CONNECTION_RESET + +// +// This is the "Name deregistered but not deleted because of +// active sessions" error code. +// +#define STATUS_NRC_ACTSES 0xCA000001 + +// +// The NT_SUCCESS macro looks at the high bytes of the errr code which isn't +// appropriate for our mapping to TDI_STATUS error codes +// +#undef NT_SUCCESS +#define NT_SUCCESS(err) ((err==TDI_SUCCESS)||(err==TDI_PENDING)) + +//-------------------------------------------------------------------- +// +// General porting macros +// +// +//-------------------------------------------------------------------- + +// +// Note that the ExInterlocked* routines (in ntos\ex\i386) do a spinlock +// for MP machines. Since we aren't MP we shouldn't need the spin lock. +// We shouldn't need to disable interrupts either. +// +#define ExInterlockedInsertTailList(list, entry, spinlock ) \ + InsertTailList( (list), (entry) ) + +#define ExInterlockedInsertHeadList(list, entry, spinlock ) \ + InsertHeadList( (list), (entry) ) + +// +// These two definitions must be kept keep a couple of NT macros use +// the ExInterlocked* macros +// +#ifdef InterlockedIncrement +#undef InterlockedIncrement +#endif +#ifdef InterlockedIncrementLong +#undef InterlockedIncrementLong +#endif +#define InterlockedIncrement(n) \ + CTEInterlockedIncrementLong( n ) +#define InterlockedIncrementLong InterlockedIncrement + +#ifdef InterlockedDecrement +#undef InterlockedDecrement +#endif +#ifdef InterlockedDecrementLong +#undef InterlockedDecrementLong +#endif +#define InterlockedDecrement(n) \ + CTEInterlockedDecrementLong( n ) +#define InterlockedDecrementLong InterlockedDecrement + +//-------------------------------------------------------------------- +// +// Debug helper macros +// +#undef ASSERT +#undef ASSERTMSG +#ifdef DEBUG + #include <vxddebug.h> + + extern DWORD DebugFlags ; + extern char DBOut[4096] ; + extern char szOutput[1024]; + extern int iCurPos ; + void NbtDebugOut( char * ) ; + + #define DBGFLAG_ALL (0x00000001) // Everything else + #define DBGFLAG_LMHOST (0x00000002) + #define DBGFLAG_KDPRINTS (0x00000004) // Jim's KdPrint output + #define DBGFLAG_ERROR (0x00000010) + #define DBGFLAG_AUX_OUTPUT (0x00000080) + + #define DbgPrint( s ) \ + if ( DebugFlags & DBGFLAG_ALL ) \ + { \ + VxdSprintf( szOutput, s ) ; \ + VxdCopyToDBOut() ; \ + NbtDebugOut( DBOut+iCurPos ) ; \ + } + + #define DbgPrintNum( n ) \ + if ( DebugFlags & DBGFLAG_ALL ) \ + { \ + VxdSprintf( szOutput, "%x", n ) ; \ + VxdCopyToDBOut() ; \ + NbtDebugOut( DBOut+iCurPos ) ; \ + } + + #undef KdPrint + #define KdPrint( s ) \ + if ( DebugFlags & DBGFLAG_KDPRINTS ) \ + { \ + VxdPrintf s ; \ + } + + // + // Conditional print routines + // + #define CDbgPrint( flag, s ) \ + if ( DebugFlags & (flag) ) \ + { \ + VxdSprintf( szOutput, s ) ; \ + VxdCopyToDBOut() ; \ + NbtDebugOut( DBOut+iCurPos ) ; \ + } + + #define CDbgPrintNum( flag, n ) \ + if ( DebugFlags & (flag) ) \ + { \ + VxdSprintf( szOutput, "%x", n ) ; \ + VxdCopyToDBOut() ; \ + NbtDebugOut( DBOut+iCurPos ) ; \ + } + + #define DbgBreak() _asm int 3 + #define ASSERT( exp ) CTEAssert( exp ) + #define ASSERTMSG( msg, exp ) CTEAssert( exp ) + // + // REQUIRE is an ASSERT that keeps the expression under non-debug + // builds + // + #define REQUIRE( exp ) ASSERT( exp ) + + // + // Consistency checks of the interrupt vector table to help watch + // for NULL pointer writes + // + extern BYTE abVecTbl[256] ; + #define INIT_NULL_PTR_CHECK() memcpy( abVecTbl, 0, sizeof( abVecTbl )) + #define CHECK_MEM() if ( sizeof(abVecTbl) != VxdRtlCompareMemory( 0, abVecTbl, sizeof(abVecTbl)) ) \ + { \ + DbgPrint("Vector table corrupt at " ) ; \ + DbgPrintNum( VxdRtlCompareMemory( 0, abVecTbl, sizeof(abVecTbl) ) ) ;\ + DbgPrint("\n\r") ; \ + _asm int 3 \ + } \ + CTECheckMem(__FILE__) ; +#else + #define DbgPrint( s ) + #define DbgPrintNum( n ) + #define DbgBreak() + #undef KdPrint + #define KdPrint( s ) + #define ASSERT( exp ) { ; } + #define ASSERTMSG( msg, exp ) { ; } + #define REQUIRE( exp ) { exp ; } + #define INIT_NULL_PTR_CHECK() + #define CHECK_MEM() + #define CDbgPrint( flag, s ) + #define CDbgPrintNum( flag, n ) +#endif + +//--------------------------------------------------------------------- +// +// FROM tdihndlr.c +// +TDI_STATUS +TdiReceiveHandler ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID Data, + EventRcvBuffer * pevrcvbuf + ); + +TDI_STATUS +ReceiveAnyHandler ( // Handles NCBRCVANY commands, is + IN PVOID ReceiveEventContext, // called after all other receive + IN PVOID ConnectionContext, // handlers + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID Data, + PVOID * ppBuffer // Pointer to ListEntry of RCV_CONTEXT + ) ; + +TDI_STATUS +VxdDisconnectHandler ( // Cleans up Netbios stuff for remote + IN PVOID DisconnectEventContext, // disconnects + IN PVOID ConnectionContext, + IN PVOID DisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID pDisconnectInformation, + IN ULONG DisconnectIndicators + ) ; + +VOID +CompletionRcv( + IN PVOID pContext, + IN uint tdistatus, + IN uint BytesRcvd + ); + +TDI_STATUS +TdiConnectHandler ( + IN PVOID pConnectEventContext, + IN int RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN PVOID pUserData, + IN int OptionsLength, + IN PVOID pOptions, + IN PVOID * pAcceptingID, + IN ConnectEventInfo * pEventInfo + ); + +TDI_STATUS +TdiDisconnectHandler ( + PVOID EventContext, + PVOID ConnectionContext, + ULONG DisconnectDataLength, + PVOID DisconnectData, + ULONG DisconnectInformationLength, + PVOID DisconnectInformation, + ULONG DisconnectIndicators // Is this the Flags field? + ); + +TDI_STATUS +TdiRcvDatagramHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN UINT ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT EventRcvBuffer * * ppBuffer //OUT PIRP *pIoRequestPacket + ); +TDI_STATUS +TdiRcvNameSrvHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN UINT ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT EventRcvBuffer * * ppBuffer //OUT PIRP *pIoRequestPacket + ); +TDI_STATUS +TdiErrorHandler ( + IN PVOID Context, + IN ULONG Status + ); + +VOID +CompletionRcvDgram( + IN PVOID Context, + IN UINT tdistatus, + IN UINT RcvdSize + ) ; + +//--------------------------------------------------------------------- +// +// FROM init.c +// + +PVOID +CTEAllocInitMem( + IN ULONG cbBuff ) ; + +NTSTATUS +VxdReadIniString( + IN LPTSTR pchKeyName, + IN OUT LPTSTR * ppStringBuff + ) ; + +NTSTATUS CreateDeviceObject( + IN tNBTCONFIG *pConfig, + IN ULONG IpAddr, + IN ULONG IpMask, + IN ULONG IpNameServer, + IN ULONG IpBackupServer, + IN ULONG IpDnsServer, + IN ULONG IpDnsBackupServer, + IN UCHAR MacAddr[], + IN UCHAR IpIndex + ) ; + +void GetNameServerAddress( ULONG IpAddr, + PULONG pIpNameServer); + +void GetDnsServerAddress( ULONG IpAddr, + PULONG pIpNameServer); + +#define COUNT_NS_ADDR 4 // Maximum number of name server addresses +//--------------------------------------------------------------------- +// +// FROM vxdfile.asm +// + +HANDLE +VxdFileOpen( + IN char * pchFile ) ; + +ULONG +VxdFileRead( + IN HANDLE hFile, + IN ULONG BytesToRead, + IN BYTE * pBuff ) ; + +VOID +VxdFileClose( + IN HANDLE hFile ) ; + +PUCHAR +VxdWindowsPath( + ); + +//--------------------------------------------------------------------- +// +// FROM vnbtd.asm +// + +ULONG +GetProfileHex( + IN HANDLE ParametersHandle, // Not used + IN PCHAR ValueName, + IN ULONG DefaultValue, + IN ULONG MinimumValue + ); + +ULONG +GetProfileInt( + IN HANDLE ParametersHandle, // Not used + IN PCHAR ValueName, + IN ULONG DefaultValue, + IN ULONG MinimumValue + ); + +TDI_STATUS DhcpQueryInfo( UINT Type, PVOID pBuff, UINT * pSize ) ; + +//--------------------------------------------------------------------- +// +// FROM tdiout.c +// +NTSTATUS VxdDisconnectWait( tLOWERCONNECTION * pLowerConn, + tDEVICECONTEXT * pDeviceContext, + ULONG Flags, + PVOID Timeout) ; + +NTSTATUS VxdScheduleDelayedCall( tDGRAM_SEND_TRACKING * pTracker, + PVOID pClientContext, + PVOID ClientCompletion, + PVOID CallBackRoutine, + tDEVICECONTEXT *pDeviceContext ) ; + +//--------------------------------------------------------------------- +// +// FROM timer.c +// +BOOL CheckForTimedoutNCBs( CTEEvent *pEvent, PVOID pCont ) ; +VOID StopTimeoutTimer( VOID ); +NTSTATUS StartRefreshTimer( VOID ); + +//--------------------------------------------------------------------- +// +// FROM tdicnct.c +// +NTSTATUS CloseAddress( HANDLE hAddress ) ; + + +//--------------------------------------------------------------------- +// +// FROM wfw.c - Snowball specific routines +// +#ifndef CHICAGO + +BOOL GetActiveLanasFromIP( VOID ); + +#endif //!CHICAGO + + +//--------------------------------------------------------------------- +// +// FROM chic.c - Chicago specific routines +// +#ifdef CHICAGO + +NTSTATUS DestroyDeviceObject( + tNBTCONFIG *pConfig, + ULONG IpAddr + ); + +BOOL IPRegisterAddrChangeHandler( PVOID AddChangeHandler, BOOL ); + +TDI_STATUS IPNotification( ULONG IpAddress, + ULONG IpMask, + PVOID pDevNode, + USHORT IPContext, + BOOL fNew ); + +BOOL VxdInitLmHostsSupport( PUCHAR pchLmHostPath, USHORT ulPathSize ); + +VOID SaveNameDnsServerAddrs( VOID ); +BOOL VxdOpenNdis( VOID ); +VOID VxdCloseNdis( VOID ); + + +VOID ReleaseNbtConfigMem( VOID ); + +NTSTATUS VxdUnload( LPSTR pchModuleName ); + +#endif //CHICAGO + +//-------------------------------------------------------------------- +// +// Procedures in vxdisol.c +// +// +NCBERR VxdOpenName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdCloseName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdCall( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdListen( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdDgramSend( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdDgramReceive( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdReceiveAny( tDEVICECONTEXT *pDeviceContext, NCB * pNCB ) ; +NCBERR VxdReceive( tDEVICECONTEXT * pDeviceContext, NCB * pNCB, BOOL fReceive ) ; +NCBERR VxdHangup( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdCancel( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdSend( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +NCBERR VxdSessionStatus( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; +VOID DelayedSessEstablish( PVOID pContext ); + + +//-------------------------------------------------------------------- +// +// Procedures in dns.c +// +// +PCHAR +DnsStoreName( + OUT PCHAR pDest, + IN PCHAR pName, + IN PCHAR pDomainName, + IN enum eNSTYPE eNsType + ); + +VOID +DnsExtractName( + IN PCHAR pNameHdr, + IN LONG NumBytes, + OUT PCHAR pName, + OUT PULONG pNameSize + ); + +VOID +ProcessDnsResponse( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ); + +VOID +DnsCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +// +// These routines all have "Direct" at the end of the routine name +// because they are used exclusively for name queries to the DNS +// server to resolve DNS names and not NetBIOS names. +// + +VOID +ProcessDnsResponseDirect( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ); + +ULONG +DoDnsResolveDirect( + PNCB pncb, + PUCHAR pzDnsName, + PULONG pIpAddressList + ); + +BOOL +DoDnsCancelDirect( + PNCB pncb + ); + +VOID +DnsCompletionDirect( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +PDNS_DIRECT_WORK_ITEM_CONTEXT +FindContextDirect( + USHORT TransactionId + ); + +VOID +DnsActualCompletionDirect( + IN NBT_WORK_ITEM_CONTEXT * pnbtContext + ); + +VOID +DnsUnlinkAndCompleteDirect( + IN PDNS_DIRECT_WORK_ITEM_CONTEXT pContext + ); + +NTSTATUS +UdpSendDNSBcastDirect( + IN PDNS_DIRECT_WORK_ITEM_CONTEXT pContext, + IN ULONG Retries, + IN ULONG Timeout + ); + +VOID +SendDNSBcastDoneDirect( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo + ); + +PDNS_DIRECT_SEND_CONTEXT +CreateSendContextDirect( + IN PCHAR pName, + IN PCHAR pchDomainName, + OUT PVOID *pHdrs, + OUT PULONG pLength, + IN PDNS_DIRECT_WORK_ITEM_CONTEXT pContext + ); + +VOID +IpToAscii( + IN DWORD IpAddress, + IN OUT PCHAR pzAscii + ); + +// +// Flag passed to TdiSend indicating we are dealing with a chain send +// and not a normal send. +// +#define CHAIN_SEND_FLAG 0x80 +typedef struct _tBUFFERCHAINSEND +{ + tBUFFER tBuff ; // Must be first member of this structure!! + PVOID pBuffer2 ; + ULONG Length2 ; +} tBUFFERCHAINSEND ; + + +// +// Flag for pConnectEle->Flags indicating whether the client has been +// notified the session is dead (by completing an NCB with NRC_SCLOSED) +// +#define NB_CLIENT_NOTIFIED 0x01 + + +// +// Translates the name number/logical session number to the appropriate +// structure pointer +// +NCBERR VxdFindClientElement( tDEVICECONTEXT * pDeviceContext, + UCHAR ncbnum, + tCLIENTELE * * ppClientEle, + enum CLIENT_TYPE Type ) ; +NCBERR VxdFindConnectElement( tDEVICECONTEXT * pDeviceContext, + NCB * pNCB, + tCONNECTELE * * ppConnectEle ) ; +NCBERR VxdFindLSN( tDEVICECONTEXT * pDeviceContext, + tCONNECTELE * pConnectEle, + UCHAR * plsn ) ; +NCBERR VxdFindNameNum( tDEVICECONTEXT * pDeviceContext, + tADDRESSELE * pAddressEle, + UCHAR * pNum ) ; +// +// Used by Register/Unregister for selecting either the name table or the +// session table from the device context +// +typedef enum +{ + NB_NAME, + NB_SESSION +} NB_TABLE_TYPE ; + +BOOL NBRegister( tDEVICECONTEXT * pDeviceContext, + UCHAR * pNCBNum, + PVOID pElem, + NB_TABLE_TYPE NbTable ) ; +BOOL NBUnregister( tDEVICECONTEXT * pDeviceContext, + UCHAR NCBNum, + NB_TABLE_TYPE NbTable ) ; + +TDI_STATUS VxdCompleteSessionNcbs( tDEVICECONTEXT * pDeviceContext, + tCONNECTELE * pConnEle ) ; + +NCBERR VxdCleanupAddress( tDEVICECONTEXT * pDeviceContext, + NCB * pNCB, + tCLIENTELE * pClientEle, + UCHAR NameNum, + BOOL fDeleteAddress ) ; + +BOOL ActiveSessions( tCLIENTELE * pClientEle ) ; + +// +// This structure holds context information while we are waiting for +// a session setup to complete (either listen or call) +// +// It is stored in the ncb_reserve field of the NCB +// +typedef struct _SESS_SETUP_CONTEXT +{ + TDI_CONNECTION_INFORMATION * pRequestConnect ; // + TDI_CONNECTION_INFORMATION * pReturnConnect ; // Name who answered the listen + tCONNECTELE * pConnEle ; + UCHAR fIsWorldListen ; // Listenning for '*'? +} SESS_SETUP_CONTEXT, *PSESS_SETUP_CONTEXT ; + + +void VxdTearDownSession( tDEVICECONTEXT * pDevCont, + tCONNECTELE * pConnEle, + PSESS_SETUP_CONTEXT pCont, + NCB * pNCB ) ; + +// +// Finishes off a Netbios request (fill in NCB fields, call the post +// routine etc.). Is macroed as CTEIoComplete. +// +VOID VxdIoComplete( PCTE_IRP pirp, NTSTATUS status, ULONG cbExtra ) ; + +ULONG +_stdcall + + VNBT_NCB_X( PNCB pNCB, + PUCHAR pzDnsName, + PULONG pIpAddress, + PVOID pExtended, + ULONG fFlag ) ; + +ULONG +_stdcall +VNBT_LANA_MASK( + ); + +#endif //_VXDPROCS_H_ diff --git a/private/ntos/nbt/makefile.16 b/private/ntos/nbt/makefile.16 new file mode 100644 index 000000000..7171ec9a6 --- /dev/null +++ b/private/ntos/nbt/makefile.16 @@ -0,0 +1,67 @@ +# +# +# Note: Currently the VXD can only be built on an NT machine in a Razzle +# screen group. Building from DOS is not currently supported (requires +# NT tree and NT environment variables). You must also be enlisted and +# up to date in nt\private\inc, nt\private\ntos\inc, nt\private\ntos\nbt\* +# and have the nt\public tree. +# +# +# Steps to build the NBT VXD: +# +# 1) Ask GregJ for access to \\flipper\wb +# 2) Copy/Enlist \\flipper\wb\src\common to your local machine +# 3) Copy/Enlist \\flipper\wb\src\import\c8386 +# \sdk +# \win32 +# \masm6 +# \wininc +# \wintools +# 4) Copy/Enlist \\flipper\wb\src\ndis3 to your local machine (needed +# to build the TCP tree) +# 5) Enlist in the tcp project on \\peanut\rhino +# 6) Modify nbt\setenv.bat to setup the appropriate environment variables +# +# 7) Build the TCP driver (or at least tcp\vtdi which will build cxport.obj) +# 8) Type "nmake -f makefile.16" in nbt +# Note that if you get "out of far heapspace" when attempting to +# compile nbt\vxd, cd to vxd and type nmake there (out of space +# due to too much spawning). +# +# 9) The debug vnbtd.{sys, sym} will be in vxd\debug and the nondebug +# version in vxd\nodebug. +# + +!ifndef IMPORT +!error *** You must first modify and run nbt\setenv.bat before building *** +!endif + +all: + cd nbt + nmake -f makefile.16 + cd ..\vxd + nmake + +nbt: + cd nbt + nmake -f makefile.16 + cd ..\vxd + nmake + +debug: + cd nbt + nmake -f makefile.16 debug + cd ..\vxd + nmake debug + +depend: + cd nbt + nmake -f makefile.16 depend + cd ..\vxd + nmake depend + +clean: + cd nbt + nmake -f makefile.16 clean + cd ..\vxd + nmake clean diff --git a/private/ntos/nbt/nbt/depend.mk b/private/ntos/nbt/nbt/depend.mk new file mode 100644 index 000000000..4a3b35b67 --- /dev/null +++ b/private/ntos/nbt/nbt/depend.mk @@ -0,0 +1,391 @@ +$(NBTOBJD)/hashtbl.obj $(NBTDOBJD)/hashtbl.obj $(NBTSRC)/hashtbl.lst: \ + $(NBTSRC)/hashtbl.c ../../$(INC)/sockets/netinet/in.h \ + ../../$(INC)/status.h ../../$(INC)/sys/snet/ip_proto.h \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/dbgk.h ../$(INC)/ex.h ../$(INC)/exboosts.h \ + ../$(INC)/exlevels.h ../$(INC)/hal.h ../$(INC)/i386.h \ + ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h ../$(INC)/lfs.h \ + ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h ../$(INC)/ntmp.h \ + ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h ../$(INC)/ps.h \ + ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/nbtmsg.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/hndlrs.obj $(NBTDOBJD)/hndlrs.obj $(NBTSRC)/hndlrs.lst: \ + $(NBTSRC)/hndlrs.c ../../$(INC)/sockets/netinet/in.h \ + ../../$(INC)/status.h ../../$(INC)/sys/snet/ip_proto.h \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/dbgk.h ../$(INC)/ex.h ../$(INC)/exboosts.h \ + ../$(INC)/exlevels.h ../$(INC)/hal.h ../$(INC)/i386.h \ + ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h ../$(INC)/lfs.h \ + ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h ../$(INC)/ntmp.h \ + ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h ../$(INC)/ps.h \ + ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/nbtmsg.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/inbound.obj $(NBTDOBJD)/inbound.obj $(NBTSRC)/inbound.lst: \ + $(NBTSRC)/inbound.c ../../$(INC)/sockets/netinet/in.h \ + ../../$(INC)/status.h ../../$(INC)/sys/snet/ip_proto.h \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/dbgk.h ../$(INC)/ex.h ../$(INC)/exboosts.h \ + ../$(INC)/exlevels.h ../$(INC)/hal.h ../$(INC)/i386.h \ + ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h ../$(INC)/lfs.h \ + ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h ../$(INC)/ntmp.h \ + ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h ../$(INC)/ps.h \ + ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/nbtmsg.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/init.obj $(NBTDOBJD)/init.obj $(NBTSRC)/init.lst: $(NBTSRC)/init.c \ + ../../$(INC)/sockets/netinet/in.h ../../$(INC)/status.h \ + ../../$(INC)/sys/snet/ip_proto.h ../$(INC)/alpha.h \ + ../$(INC)/alpharef.h ../$(INC)/arc.h ../$(INC)/bugcodes.h \ + ../$(INC)/cache.h ../$(INC)/cm.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h \ + ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h \ + $(INC)/nbtmsg.h $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h \ + $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/name.obj $(NBTDOBJD)/name.obj $(NBTSRC)/name.lst: $(NBTSRC)/name.c \ + ../../$(INC)/sockets/netinet/in.h ../../$(INC)/status.h \ + ../../$(INC)/sys/snet/ip_proto.h ../$(INC)/alpha.h \ + ../$(INC)/alpharef.h ../$(INC)/arc.h ../$(INC)/bugcodes.h \ + ../$(INC)/cache.h ../$(INC)/cm.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h \ + ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h \ + $(INC)/nbtmsg.h $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h \ + $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/namesrv.obj $(NBTDOBJD)/namesrv.obj $(NBTSRC)/namesrv.lst: \ + $(NBTSRC)/namesrv.c ../../$(INC)/sockets/netinet/in.h \ + ../../$(INC)/status.h ../../$(INC)/sys/snet/ip_proto.h \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/dbgk.h ../$(INC)/ex.h ../$(INC)/exboosts.h \ + ../$(INC)/exlevels.h ../$(INC)/hal.h ../$(INC)/i386.h \ + ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h ../$(INC)/lfs.h \ + ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h ../$(INC)/ntmp.h \ + ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h ../$(INC)/ps.h \ + ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/nbtmsg.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/nbtutils.obj $(NBTDOBJD)/nbtutils.obj $(NBTSRC)/nbtutils.lst: \ + $(NBTSRC)/nbtutils.c ../../$(INC)/sockets/netinet/in.h \ + ../../$(INC)/status.h ../../$(INC)/sys/snet/ip_proto.h \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/dbgk.h ../$(INC)/ex.h ../$(INC)/exboosts.h \ + ../$(INC)/exlevels.h ../$(INC)/hal.h ../$(INC)/i386.h \ + ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h ../$(INC)/lfs.h \ + ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h ../$(INC)/ntmp.h \ + ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h ../$(INC)/ps.h \ + ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/nbtmsg.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/parse.obj $(NBTDOBJD)/parse.obj $(NBTSRC)/parse.lst: $(NBTSRC)/parse.c \ + ../../$(INC)/sockets/netinet/in.h ../../$(INC)/status.h \ + ../../$(INC)/sys/snet/ip_proto.h ../$(INC)/alpha.h \ + ../$(INC)/alpharef.h ../$(INC)/arc.h ../$(INC)/bugcodes.h \ + ../$(INC)/cache.h ../$(INC)/cm.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h \ + ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h \ + $(INC)/hosts.h $(INC)/nbtmsg.h $(INC)/nbtnt.h $(INC)/nbtprocs.h \ + $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h \ + $(INC)/vxdprocs.h $(IMPORT)/win32/ddk/inc/debug.h \ + $(BASEDIR)/public/sdk/inc/alphaops.h $(BASEDIR)/public/sdk/inc/crt/ctype.h \ + $(BASEDIR)/public/sdk/inc/crt/excpt.h $(BASEDIR)/public/sdk/inc/crt/io.h \ + $(BASEDIR)/public/sdk/inc/crt/stdarg.h $(BASEDIR)/public/sdk/inc/crt/stddef.h \ + $(BASEDIR)/public/sdk/inc/crt/string.h $(BASEDIR)/public/sdk/inc/devioctl.h \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/nt.h $(BASEDIR)/public/sdk/inc/ntalpha.h \ + $(BASEDIR)/public/sdk/inc/ntconfig.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntpsapi.h \ + $(BASEDIR)/public/sdk/inc/ntregapi.h $(BASEDIR)/public/sdk/inc/ntrtl.h \ + $(BASEDIR)/public/sdk/inc/ntseapi.h $(BASEDIR)/public/sdk/inc/ntstatus.h \ + $(BASEDIR)/public/sdk/inc/ntxcapi.h $(BASEDIR)/public/sdk/inc/windef.h \ + $(BASEDIR)/public/sdk/inc/winerror.h $(BASEDIR)/public/sdk/inc/winnt.h \ + $(BASEDIR)/tcp/h/cxport.h $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h \ + $(BASEDIR)/tcp/h/packoff.h $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h \ + $(BASEDIR)/tcp/h/tdikrnl.h $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/proxy.obj $(NBTDOBJD)/proxy.obj $(NBTSRC)/proxy.lst: $(NBTSRC)/proxy.c \ + ../../$(INC)/sockets/netinet/in.h ../../$(INC)/status.h \ + ../../$(INC)/sys/snet/ip_proto.h ../$(INC)/alpha.h \ + ../$(INC)/alpharef.h ../$(INC)/arc.h ../$(INC)/bugcodes.h \ + ../$(INC)/cache.h ../$(INC)/cm.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h \ + ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h \ + $(INC)/nbtmsg.h $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h \ + $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/timer.obj $(NBTDOBJD)/timer.obj $(NBTSRC)/timer.lst: $(NBTSRC)/timer.c \ + ../../$(INC)/sockets/netinet/in.h ../../$(INC)/status.h \ + ../../$(INC)/sys/snet/ip_proto.h ../$(INC)/alpha.h \ + ../$(INC)/alpharef.h ../$(INC)/arc.h ../$(INC)/bugcodes.h \ + ../$(INC)/cache.h ../$(INC)/cm.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h \ + ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h \ + $(INC)/nbtmsg.h $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h \ + $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + +$(NBTOBJD)/udpsend.obj $(NBTDOBJD)/udpsend.obj $(NBTSRC)/udpsend.lst: \ + $(NBTSRC)/udpsend.c ../../$(INC)/sockets/netinet/in.h \ + ../../$(INC)/status.h ../../$(INC)/sys/snet/ip_proto.h \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/dbgk.h ../$(INC)/ex.h ../$(INC)/exboosts.h \ + ../$(INC)/exlevels.h ../$(INC)/hal.h ../$(INC)/i386.h \ + ../$(INC)/init.h ../$(INC)/kd.h ../$(INC)/ke.h ../$(INC)/lfs.h \ + ../$(INC)/lpc.h ../$(INC)/mips.h ../$(INC)/mm.h ../$(INC)/ntmp.h \ + ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h ../$(INC)/ps.h \ + ../$(INC)/se.h ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/nbtmsg.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(IMPORT)/win32/ddk/inc/debug.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/io.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h $(BASEDIR)/tcp/h/cxport.h \ + $(BASEDIR)/tcp/h/nettypes.h $(BASEDIR)/tcp/h/oscfg.h $(BASEDIR)/tcp/h/packoff.h \ + $(BASEDIR)/tcp/h/packon.h $(BASEDIR)/tcp/h/tdi.h $(BASEDIR)/tcp/h/tdikrnl.h \ + $(BASEDIR)/tcp/h/tdistat.h $(BASEDIR)/tcp/h/tdivxd.h + diff --git a/private/ntos/nbt/nbt/dirs b/private/ntos/nbt/nbt/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/nbt/nbt/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/nbt/nbt/hashtbl.c b/private/ntos/nbt/nbt/hashtbl.c new file mode 100644 index 000000000..c2ef4625f --- /dev/null +++ b/private/ntos/nbt/nbt/hashtbl.c @@ -0,0 +1,668 @@ +// +// +// hashtbl.c +// +// This file contains the name code to implement the local and remote +// hash tables used to store local and remote names to IP addresses +// The hash table should not use more than 256 buckets since the hash +// index is only calculated to one byte! + +#include "nbtprocs.h" + + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(INIT, CreateHashTable) +#pragma CTEMakePageable(INIT, InitRemoteHashTable) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +CreateHashTable( + tHASHTABLE **pHashTable, + LONG lNumBuckets, + enum eNbtLocation LocalRemote + ) +/*++ + +Routine Description: + + This routine creates a hash table uTableSize long. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + ULONG uSize; + LONG i; + NTSTATUS status; + + CTEPagedCode(); + + uSize = (lNumBuckets-1)*sizeof(LIST_ENTRY) + sizeof(tHASHTABLE); + + *pHashTable = (tHASHTABLE *)CTEAllocInitMem(uSize); + + if (*pHashTable) + { + CTEInitLock(&(*pHashTable)->SpinLock); + + // initialize all of the buckets to have null chains off of them + for (i=0;i < lNumBuckets ;i++ ) + { + InitializeListHead(&(*pHashTable)->Bucket[i]); + } + + (*pHashTable)->LocalRemote = LocalRemote; + (*pHashTable)->lNumBuckets = lNumBuckets; + status = STATUS_SUCCESS; + } + else + { + IF_DBG(NBT_DEBUG_HASHTBL) + KdPrint(("NBT:Unable to create hash table\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +InitRemoteHashTable( + IN tNBTCONFIG *pConfig, + IN LONG NumBuckets, + IN LONG NumNames + ) +/*++ + +Routine Description: + + This routine creates a linked list of buffers to use to store remote + names in the remote hash table. These are names that the proxy node + will answer for. + + +Arguments: + + +Return Value: + + NTSTATUS - success or not - failure means no response to net + +--*/ +{ + NTSTATUS status; + + CTEPagedCode(); + status = CreateHashTable(&pConfig->pRemoteHashTbl, + NumBuckets, + NBT_REMOTE); + + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +AddNotFoundToHashTable( + IN tHASHTABLE *pHashTable, + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN enum eNbtAddrType NameType, + OUT tNAMEADDR **ppNameAddress + ) +/*++ + +Routine Description: + + This routine adds a name to IPaddress to the hash table + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + + status = FindInHashTable(pHashTable,pName,pScope,ppNameAddress); + + if (status == STATUS_SUCCESS) + { + // found it in the table so we're done - return pending to + // differentiate from the name added case. Pending passes the + // NT_SUCCESS() test as well as Success does. + // + return(STATUS_PENDING); + } + + // + //... otherwise add the new entry to the table + // + status = AddToHashTable( + pHashTable, + pName, + pScope, + IpAddress, + NameType, + NULL, + ppNameAddress); + + return(status); + + +} +//---------------------------------------------------------------------------- +NTSTATUS +AddRecordToHashTable( + IN tNAMEADDR *pNameAddr, + IN PCHAR pScope + ) +/*++ + +Routine Description: + + This routine adds a nameaddr record to the hash table. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + tNAMEADDR *pNameAddress; + + status = FindInHashTable(NbtConfig.pRemoteHashTbl, + pNameAddr->Name, + pScope, + &pNameAddress); + + if (status == STATUS_SUCCESS) + { + // + // just return since the name has already resolved + // but first add this adapter to the adapters resolving the name + // + if (pNameAddress->IpAddress == pNameAddr->IpAddress) + { + pNameAddress->AdapterMask |= pNameAddr->AdapterMask; + } + status = STATUS_UNSUCCESSFUL; + + } + else + { + + // + //... otherwise add the new entry to the table + // + + status = AddToHashTable( + NbtConfig.pRemoteHashTbl, + pNameAddr->Name, + pScope, + 0, + 0, + pNameAddr, + &pNameAddress); + } + + + return(status); + + +} + +//---------------------------------------------------------------------------- +NTSTATUS +AddToHashTable( + IN tHASHTABLE *pHashTable, + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN enum eNbtAddrType NameType, + IN tNAMEADDR *pNameAddr, + OUT tNAMEADDR **ppNameAddress + ) +/*++ + +Routine Description: + + This routine adds a name to IPaddress to the hash table + Called with the spin lock HELD. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + tNAMEADDR *pNameAddress; + tNAMEADDR *pScopeAddr; + NTSTATUS status; + int iIndex; + CTELockHandle OldIrq; + + // first hash the name to an index + // take the lower nibble of the first 2 characters.. mod table size + iIndex = ((pName[0] & 0x0F) << 4) + (pName[1] & 0x0F); + iIndex = iIndex % pHashTable->lNumBuckets; + + CTESpinLock(&NbtConfig,OldIrq); + + if (!pNameAddr) + { + // + // Allocate memory for another hash table entry + // + pNameAddress = (tNAMEADDR *)NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('0')); + if (pNameAddress) + { + CTEZeroMemory(pNameAddress,sizeof(tNAMEADDR)); + pNameAddress->RefCount = 1; + + if ((pHashTable->LocalRemote == NBT_LOCAL) || + (pHashTable->LocalRemote == NBT_REMOTE_ALLOC_MEM)) + { + pNameAddress->Verify = LOCAL_NAME; + } + else + { + pNameAddress->Verify = REMOTE_NAME; + } + } + else + { + CTESpinFree(&NbtConfig,OldIrq); + NBT_PROXY_DBG(("AddToHashTable: MAJOR ERROR - OUT OF HASH TABLE ENTRIES -- should never happen\n")); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + + // fill in the record with the name and IpAddress + pNameAddress->NameTypeState = (NameType == NBT_UNIQUE) ? + NAMETYPE_UNIQUE : NAMETYPE_GROUP; + + // set the state here to pending(conflict), so that when we set the state to its + // correct state in the routine that calls this one, we can check this + // value to be sure an interrupt has not come in and removed this entry + // from the list. + pNameAddress->NameTypeState |= STATE_CONFLICT; + pNameAddress->IpAddress = IpAddress; + + // fill in the name + CTEMemCopy(pNameAddress->Name,pName,(ULONG)NETBIOS_NAME_SIZE); + + } + else + { + pNameAddress = pNameAddr; + } + + pNameAddress->pTimer = NULL; + pNameAddress->TimeOutCount = NbtConfig.RemoteTimeoutCount; + + // put on the head of the list in case the same name is in the table + // twice (where the second one is waiting for its reference count to + // go to zero, and will ultimately be removed, we want to find the new + // name on any query of the table + // + InsertHeadList(&pHashTable->Bucket[iIndex],&pNameAddress->Linkage); + + + // check for a scope too ( on non-local names only ) + if ((pHashTable->LocalRemote != NBT_LOCAL) && (*pScope)) + { + // we must have a scope + // see if the scope is already in the hash table and add if necessary + // + status = FindInHashTable(pHashTable, + pScope, + NULL, + &pScopeAddr); + + if (!NT_SUCCESS(status)) + { + PUCHAR Scope; + status = STATUS_SUCCESS; + + // *TODO* - this check will not adequately protect against + // bad scopes passed in - i.e. we may run off into memory + // and get an access violation...however converttoascii should + // do the protection. For local names the scope should be + // ok since NBT read it from the registry and checked it first + // + iIndex = 0; + Scope = pScope; + while (*Scope && (iIndex <= 255)) + { + iIndex++; + Scope++; + } + + // the whole length must be 255 or less, so the scope can only be + // 255-16... + if (iIndex > (255 - NETBIOS_NAME_SIZE)) + { + RemoveEntryList(&pNameAddress->Linkage); + + CTEMemFree(pNameAddress); + + CTESpinFree(&NbtConfig,OldIrq); + return(STATUS_UNSUCCESSFUL); + } + + iIndex++; // to copy the null + + // + // the scope is a variable length string, so allocate enough + // memory for the tNameAddr structure based on this string length + // + pScopeAddr = (tNAMEADDR *)NbtAllocMem((USHORT)(sizeof(tNAMEADDR) + + iIndex + - NETBIOS_NAME_SIZE),NBT_TAG('1')); + if ( !pScopeAddr ) + { + RemoveEntryList(&pNameAddress->Linkage); + + CTEMemFree( pNameAddress ); + CTESpinFree(&NbtConfig,OldIrq); + return STATUS_INSUFFICIENT_RESOURCES ; + } + + + // copy the scope to the name field including the Null at the end. + // to the end of the name + CTEMemCopy(pScopeAddr->Name,pScope,iIndex); + + // mark the entry as containing a scope name for cleanup later + pScopeAddr->NameTypeState = NAMETYPE_SCOPE | STATE_RESOLVED; + + // keep the size of the name in the context value for easier name + // comparisons in FindInHashTable + + pScopeAddr->RefCount = 2; + pScopeAddr->ScopeLength = (PVOID)iIndex; + pNameAddress->pScope = pScopeAddr; + + // add the scope record to the hash table + iIndex = ((pScopeAddr->Name[0] & 0x0F) << 4) + (pScopeAddr->Name[1] & 0x0F); + iIndex = iIndex % pHashTable->lNumBuckets; + InsertTailList(&pHashTable->Bucket[iIndex],&pScopeAddr->Linkage); + + } + else + { + // the scope is already in the hash table so link the name to the + // scope + pNameAddress->pScope = pScopeAddr; + } + } + else + pNameAddress->pScope = NULL; // no scope + + + + // return the pointer to the hash table block + *ppNameAddress = pNameAddress; + CTESpinFree(&NbtConfig,OldIrq); + return(STATUS_SUCCESS); +} + +#ifdef PROXY_NODE + +//---------------------------------------------------------------------------- +NTSTATUS +DeleteFromHashTable( + tHASHTABLE *pHashTable, + PCHAR pName + ) +/*++ + +Routine Description: + + This routine deletes an entry from the table. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + tNAMEADDR *pNameAddr; + + // Remember to delete any timer that may be associated with the + // hash table entry!! Check BnodeCompletion before deleting timers since + // it deletes the timer just after it calls this routine,... we don't + // want to do it twice. + + status = FindInHashTable(pHashTable,pName,NULL,&pNameAddr); + + if (NT_SUCCESS(status)) + { + + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_CONFLICT; + NbtDereferenceName(pNameAddr); + return(STATUS_SUCCESS); + } + else + { + return(STATUS_UNSUCCESSFUL); + } + +} +//---------------------------------------------------------------------------- +NTSTATUS +ChgStateOfScopedNameInHashTable( + tHASHTABLE *pHashTable, + PCHAR pName, + PCHAR pScope, + DWORD NewState + ) +/*++ + +Routine Description: + + This routine deletes an entry from the table. + Don't call this function for changing the state of an entry in the + local table since it references the entry after calling NbtDereferenceName + which deallocates a local name if the RefCount on it goes to 0. See + comment about NbtDereferenceName in the function below + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +Called By: + + ProxyTimerCompletionFn in Proxy.c + +--*/ +{ + NTSTATUS status; + tNAMEADDR *pNameAddr; + + // Remember to delete any timer that may be associated with the + // hash table entry!! Check BnodeCompletion before deleting timers since + // it deletes the timer just after it calls this routine,... we don't + // want to do it twice. + + status = FindInHashTable(pHashTable,pName,pScope,&pNameAddr); + + if (NT_SUCCESS(status)) + { + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= NewState; + if (NewState == STATE_RELEASED) + { + NbtDereferenceName(pNameAddr); + // + // Set the state here again since NbtDereference changes the + // state to 0 when NewState is RELEASED (FUTURES: change + // NbtDereferenceName) + // + pNameAddr->NameTypeState |= NewState; + } + return(STATUS_SUCCESS); + } + else + { + return(STATUS_UNSUCCESSFUL); + } + +} +#endif +//---------------------------------------------------------------------------- +NTSTATUS +FindInHashTable( + tHASHTABLE *pHashTable, + PCHAR pName, + PCHAR pScope, + tNAMEADDR **pNameAddress + ) +/*++ + +Routine Description: + + This routine checks if the name passed in matches a hash table entry. + Called with the spin lock HELD. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + PLIST_ENTRY pEntry; + PLIST_ENTRY pHead; + tNAMEADDR *pNameAddr; + int iIndex; + ULONG uNameSize; + PCHAR pScopeTbl; + + // first hash the name to an index... + // take the lower nibble of the first 2 characters.. mod table size + // + iIndex = ((pName[0] & 0x0F) << 4) + (pName[1] & 0x0F); + iIndex = iIndex % pHashTable->lNumBuckets; + + + // check if the name is already in the table + pHead = &pHashTable->Bucket[iIndex]; + + pEntry = pHead; + + // check each entry in the hash list...until the end of the list + while ((pEntry = pEntry->Flink) != pHead) + { + + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + + if (pNameAddr->NameTypeState & NAMETYPE_SCOPE) + { + // scope names are treated differently since they are not + // 16 bytes long... the length is stored separately. + uNameSize = (ULONG)pNameAddr->ScopeLength; + } + else + uNameSize = NETBIOS_NAME_SIZE; + + + if (CTEMemEqu((PVOID)pName, (PVOID)pNameAddr->Name, uNameSize)) + { + + // now check if the scopes match. Scopes are stored differently + // on the local and remote tables. + // + if (!pScope) + { + // passing in a Null scope means try to find the name without + // a worrying about a scope matching too... + *pNameAddress = pNameAddr; + return(STATUS_SUCCESS); + } + else + if (pHashTable == NbtConfig.pLocalHashTbl) + { + // In the local hash table case the scope is the same for all + // names on the node and it it stored in the NbtConfig structure. + pScopeTbl = NbtConfig.pScope; + uNameSize = NbtConfig.ScopeLength; + + } + else + { + // Remote Hash table + // + + // check for a null scope, since remote names with no scope + // are put into the hash table with pScope == NULL + if (pNameAddr->pScope == NULL) + { + // NULL scope, in table so check if passed in scope + // points to a null. + if (*pScope == '\0') + { + *pNameAddress = pNameAddr; + return(STATUS_SUCCESS); + } + else + { + // + // Scope does not match. Continue + // + continue; + } + + } + else + { + pScopeTbl = &pNameAddr->pScope->Name[0]; + uNameSize = (ULONG)pNameAddr->pScope->ScopeLength; + } + } + + + if (CTEMemEqu((PVOID)pScope, (PVOID)pScopeTbl, uNameSize)) + { + // the scopes match so return + *pNameAddress = pNameAddr; + return(STATUS_SUCCESS); + } + + + } // end of matching name found + } + + return(STATUS_UNSUCCESSFUL); + +} diff --git a/private/ntos/nbt/nbt/hndlrs.c b/private/ntos/nbt/nbt/hndlrs.c new file mode 100644 index 000000000..677e297e7 --- /dev/null +++ b/private/ntos/nbt/nbt/hndlrs.c @@ -0,0 +1,4186 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Hndlrs.c + +Abstract: + + + This file contains the Non OS specific implementation of handlers that are + called for Connects,Receives, Disconnects, and Errors. + + This file represents the TDI interface on the Bottom of NBT after it has + been decoded into procedure call symantics from the Irp symantics used by + NT. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#ifdef VXD + +#define NTProcessAcceptIrp(pIrp, pConnectEle) \ + STATUS_NOT_SUPPORTED + +#define NTIndicateSessionSetup(pLowerConn,status) \ + DbgPrint("Skipping NTIndicateSessionSetup\n\r") + +#endif //VXD + +#include "nbtprocs.h" +#include "ctemacro.h" + +__inline long +myntohl(long x) +{ + return((((x) >> 24) & 0x000000FFL) | + (((x) >> 8) & 0x0000FF00L) | + (((x) << 8) & 0x00FF0000L)); +} + +VOID +ClearConnStructures ( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnectEle + ); + +NTSTATUS +CompleteSessionSetup ( + IN tCLIENTELE *pClientEle, + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnectEle, + IN PCTE_IRP pIrp, + IN CTELockHandle OldIrq + ); + +NTSTATUS +MakeRemoteAddressStructure( + IN PCHAR pHalfAsciiName, + IN PVOID pSourceAddr, + IN ULONG lMaxNameSize, + OUT PVOID *ppRemoteAddress, + OUT PULONG pRemoteAddressLength, + IN ULONG NumAddr + ); + +VOID +CleanupAfterDisconnect( + IN PVOID pContext + ); + +VOID +AddToRemoteHashTbl ( + IN tDGRAMHDR UNALIGNED *pDgram, + IN ULONG BytesIndicated, + IN tDEVICECONTEXT *pDeviceContext + ); + + +VOID +DoNothingComplete ( + IN PVOID pContext + ); + +VOID +AllocLowerConn( + IN tDEVICECONTEXT *pDeviceContext, + IN BOOLEAN fSpecial + ); + +VOID +DelayedAllocLowerConn( + IN PVOID pContext + ); + +VOID +DelayedAllocLowerConnSpecial( + IN PVOID pContext + ); + +VOID +GetIrpIfNotCancelled2( + IN tCONNECTELE *pConnEle, + OUT PIRP *ppIrp + ); + +#ifdef VXD +//---------------------------------------------------------------------------- + +NTSTATUS +RcvHandlrNotOs ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID *pTsdu, + OUT PVOID *RcvBuffer + + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network, when the + session has already been established (NBT_SESSION_UP state). The routine + looks for a receive buffer first and failing that looks for a receive + indication handler to pass the message to. + +Arguments: + + pClientEle - ptr to the connecition record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + NTSTATUS status; + PLIST_ENTRY pRcv; + PVOID pRcvElement; + tCLIENTELE *pClientEle; + tSESSIONHDR UNALIGNED *pSessionHdr; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + CTELockHandle OldIrq; + PIRP pIrp; + ULONG ClientBytesTaken; + BOOLEAN DebugMore; + ULONG RemainingPdu; + +//******************************************************************** +//******************************************************************** +// +// NOTE: A copy of this procedure is in Tdihndlr.c - it is inlined for +// the NT case. Therefore, only change this procedure and then +// copy the procedure body to Tdihndlr.c +// +// +//******************************************************************** +//******************************************************************** + + // get the ptr to the lower connection, and from that get the ptr to the + // upper connection block + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu; + + // + // Session ** UP ** processing + // + *BytesTaken = 0; + + pConnectEle = pLowerConn->pUpperConnection; + + ASSERT(pConnectEle->pClientEle); + + ASSERT(BytesIndicated >= sizeof(tSESSIONHDR)); + + // this routine can get called by the next part of a large pdu, so that + // we don't always started at the begining of a pdu. The Bytes Rcvd + // value is set to zero in CompletionRcv when a new pdu is expected + // + if (pConnectEle->BytesRcvd == 0) + { + + if (pSessionHdr->Type == NBT_SESSION_MESSAGE) + { + + // + // expecting the start of a new session Pkt, so get the length out + // of the pTsdu passed in + // + pConnectEle->TotalPcktLen = myntohl(pSessionHdr->UlongLength); + + // remove the Session header by adjusting the data pointer + pTsdu = (PVOID)((PUCHAR)pTsdu + sizeof(tSESSIONHDR)); + + // shorten the number of bytes since we have stripped off the + // session header + BytesIndicated -= sizeof(tSESSIONHDR); + BytesAvailable -= sizeof(tSESSIONHDR); + *BytesTaken = sizeof(tSESSIONHDR); + } + // + // Session Keep Alive + // + else + if (pSessionHdr->Type == NBT_SESSION_KEEP_ALIVE) + { + // session keep alives are simply discarded, since the act of sending + // a keep alive indicates the session is still alive, otherwise the + // transport would report an error. + + // tell the transport that we took the Pdu + *BytesTaken = sizeof(tSESSIONHDR); + return(STATUS_SUCCESS); + + } + else + { + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Unexpected Session Pdu received: type = %X\n", + pSessionHdr->Type)); + + ASSERT(0); + *BytesTaken = BytesIndicated; + return(STATUS_SUCCESS); + + } + } + + // + // check if there are any receive buffers queued against this connection + // + if (!IsListEmpty(&pConnectEle->RcvHead)) + { + // get the first buffer off the receive list + pRcv = RemoveHeadList(&pConnectEle->RcvHead); +#ifndef VXD + pRcvElement = CONTAINING_RECORD(pRcv,IRP,Tail.Overlay.ListEntry); + + // the cancel routine was set when this irp was posted to Nbt, so + // clear it now, since the irp is being passed to the transport + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine((PIRP)pRcvElement,NULL); + IoReleaseCancelSpinLock(OldIrq); + +#else + pRcvElement = CONTAINING_RECORD(pRcv, RCV_CONTEXT, ListEntry ) ; +#endif + + // + // this buffer is actually an Irp, so pass it back to the transport + // as a return parameter + // + *RcvBuffer = pRcvElement; + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + // + // No receives on this connection. Is there a receive event handler for this + // address? + // + pClientEle = pConnectEle->pClientEle; + +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + if (pClientEle->evReceive) +#endif + { + + + // check that we have not received more data than we should for + // this session Pdu. i.e. part of the next session pdu. BytesRcvd may + // have a value other than zero if the pdu has arrived in two chunks + // and the client has taken the previous one in the indication rather + // than passing back an Irp. + // +#if DBG + DebugMore = FALSE; +#endif + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + if (BytesAvailable >= RemainingPdu) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:More Data Recvd than expecting! Avail= %X,TotalLen= %X,state=%x\n", + BytesAvailable,pConnectEle->TotalPcktLen,pLowerConn->StateRcv)); +#if DBG + DebugMore =TRUE; +#endif + // shorten the indication to the client so that they don't + // get more data than the end of the pdu + // + BytesAvailable = RemainingPdu; + if (BytesIndicated > BytesAvailable) + { + BytesIndicated = BytesAvailable; + } + // + // We always indicated at raised IRQL since we call freelockatdispatch + // below + // + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE | TDI_RECEIVE_AT_DISPATCH_LEVEL; + } + else + { + // the transport may have has this flag on. We need to + // turn it off if the entire message is not present, where entire + // message means within the bytesAvailable length. We deliberately + // use bytesavailable so that Rdr/Srv can know that the next + // indication will be a new message if they set bytestaken to + // bytesavailable. + // + ReceiveFlags &= ~TDI_RECEIVE_ENTIRE_MESSAGE; + ReceiveFlags |= TDI_RECEIVE_AT_DISPATCH_LEVEL; +#ifndef VXD + BytesAvailable = RemainingPdu; +#endif + } + + // + // NT-specific code locks pLowerConn before calling this routine, + // + CTESpinFreeAtDpc(pLowerConn); + + // call the Client Event Handler + ClientBytesTaken = 0; + status = (*pClientEle->evReceive)( + pClientEle->RcvEvContext, + pConnectEle->ConnectContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &ClientBytesTaken, + pTsdu, + &pIrp); + + CTESpinLockAtDpc(pLowerConn); +#if DBG + if (DebugMore) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(( "Client TOOK %X bytes, pIrp = %X,status =%X\n", + ClientBytesTaken,pIrp,status)); + } +#endif + if (!pLowerConn->pUpperConnection) + { + // the connection was disconnected in the interim + // so do nothing. + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + *BytesTaken = BytesAvailable; + return(STATUS_SUCCESS); + } + } + else + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + ASSERT(pIrp); + // + // the client may pass back a receive in the pIrp. + // In this case pIrp is a valid receive request Irp + // and the status is MORE_PROCESSING + // + + // don't put these lines outside the if incase the client + // does not set ClientBytesTaken when it returns an error + // code... we don't want to use the value then + // + // count the bytes received so far. Most of the bytes + // will be received in the CompletionRcv handler in TdiHndlr.c + pConnectEle->BytesRcvd += ClientBytesTaken; + + // The client has taken some of the data at least... + *BytesTaken += ClientBytesTaken; + + *RcvBuffer = pIrp; + + // ** FAST PATH ** + return(status); + } + else + // + // no irp was returned... the client just took some of the bytes.. + // + if (status == STATUS_SUCCESS) + { + + // count the bytes received so far. + pConnectEle->BytesRcvd += ClientBytesTaken; + *BytesTaken += ClientBytesTaken; + + // + // look at how much data was taken and adjust some counts + // + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + // ** FAST PATH ** + CHECK_PTR(pConnectEle); + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + return(status); + } + else + if (pConnectEle->BytesRcvd > pConnectEle->TotalPcktLen) + { + //IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Too Many Bytes Rcvd!! Rcvd# = %d, TotalLen = %d\n", + pConnectEle->BytesRcvd,pConnectEle->TotalPcktLen)); + + ASSERTMSG("Nbt:Client Took Too Much Data!!!\n",0); + + // + // try to recover by saying that the client took all of the + // data so at least the transport is not confused too + // + *BytesTaken = BytesIndicated; + + } + else + // the client did not take all of the data so + // keep track of the fact + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT:Client took Indication BytesRcvd=%X, TotalLen=%X BytesAvail %X ClientTaken %X\n", + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen, + BytesAvailable, + ClientBytesTaken)); + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement the + // ReceiveIndicated counter which is set in Tdihndlr.c + + } + } + else + if (status == STATUS_DATA_NOT_ACCEPTED) + { + // client has not taken ANY data... + // + // In this case the *BytesTaken is set to 4, the session hdr. + // since we really have taken that data to setup the PduSize + // in the pConnEle structure. + // + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT: Status DATA NOT ACCEPTED returned from client Avail %X %X\n", + BytesAvailable,pConnectEle)); + + // the code in tdihndlr.c normally looks after incrementing + // the ReceiveIndicated count for data that is not taken by + // the client, but if it is a zero length send that code cannot + // detect it, so we put code here to handle that case + // + // It is possible for the client to do a disconnect after + // we release the spin lock on pLowerConn to call the Client's + // disconnect indication. If that occurs, do not overwrite + // the StateProc with PartialRcv + // + if ((pConnectEle->TotalPcktLen == 0) && + (pConnectEle->state == NBT_SESSION_UP)) + { + pLowerConn->StateRcv = PARTIAL_RCV; + SetStateProc( pLowerConn, PartialRcv ) ; + CHECK_PTR(pConnectEle); + pConnectEle->ReceiveIndicated = 0; // zero bytes waiting for client + } + else + { + // + // if any bytes were taken (i.e. the session hdr) then + // return status success. (otherwise the status is + // statusNotAccpeted). + // + if (*BytesTaken) + { + status = STATUS_SUCCESS; + } + } + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement this + // counter. + } + else + ASSERT(0); + + + return(status); + + } +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + else + { + // + // there is no client buffer to pass the data to, so keep + // track of the fact so when the next client buffer comes down + // we can get the data from the transport. + // + KdPrint(("NBT:Client did not have a Buffer posted, rcvs indicated =%X,BytesRcvd=%X, TotalLen=%X\n", + pConnectEle->ReceiveIndicated, + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen)); + + // the routine calling this one increments ReceiveIndicated and sets the + // state to PartialRcv to keep track of the fact that there is data + // waiting in the transport + // + return(STATUS_DATA_NOT_ACCEPTED); + } +#endif +} +#endif // VXD case + +//---------------------------------------------------------------------------- +NTSTATUS +Inbound ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *RcvBuffer + + ) +/*++ + +Routine Description: + + This routine is called to setup inbound session + once the tcp connection is up. The transport calls this routine with + a session setup request pdu. + + +Arguments: + + pClientEle - ptr to the connecition record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + NTSTATUS status; + tCLIENTELE *pClientEle; + tSESSIONHDR UNALIGNED *pSessionHdr; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + CTELockHandle OldIrq; + PIRP pIrp; + PLIST_ENTRY pEntry; + CONNECTION_CONTEXT ConnectId; + PTA_NETBIOS_ADDRESS pRemoteAddress; + ULONG RemoteAddressLength; + + // get the ptr to the lower connection + // + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu; + + // + // fake out the transport so it frees its receive buffer (i.e. we + // say that we accepted all of the data) + // + *BytesTaken = BytesIndicated; + + pConnectEle = pLowerConn->pUpperConnection; + + // + // since we send keep alives on connections in the the inbound + // state it is possible to get a keep alive, so just return in that + // case + // + if (((tSESSIONHDR UNALIGNED *)pTsdu)->Type == NBT_SESSION_KEEP_ALIVE) + { + return(STATUS_SUCCESS); + } + + // the LowerConn Lock is held prior to calling this routine, so free it + // here since we need to get the joint lock first + CTESpinFreeAtDpc(pLowerConn); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pLowerConn); + + // + // Session ** INBOUND ** setup processing + // + ASSERT(pLowerConn->State == NBT_SESSION_INBOUND); + // it is possible for the disconnect handler to run while the pLowerConn + // lock is released above, to get the ConnEle lock, and change the state + // to disconnected. + if (pLowerConn->State != NBT_SESSION_INBOUND) + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + goto ExitCode; + } + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt: In SessionSetupnotOS, connection state = %X\n",pLowerConn->State)); + + + if (pSessionHdr->Type != NBT_SESSION_REQUEST) + { + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Unexpected Session Pdu received during setup: type = %X\n", + pSessionHdr->Type)); + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + RejectSession(pLowerConn, + NBT_NEGATIVE_SESSION_RESPONSE, + SESSION_UNSPECIFIED_ERROR, + TRUE); + goto ExitCode; + } + else + { + CTESpinFreeAtDpc(pLowerConn); + } + + status = FindSessionEndPoint(pTsdu, + ConnectionContext, + BytesIndicated, + &pClientEle, + &pRemoteAddress, + &RemoteAddressLength); + + if (status != STATUS_SUCCESS) + { + + // + // could not find the desired end point so send a negative session + // response pdu and then disconnect + // + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + RejectSession(pLowerConn, + NBT_NEGATIVE_SESSION_RESPONSE, + status, + TRUE); + + goto ExitCode; + } + + // + // we must first check for a valid LISTEN.... + // + CTESpinLockAtDpc(pClientEle); + if (!IsListEmpty(&pClientEle->ListenHead)) + { + tLISTENREQUESTS *pListen; + tLISTENREQUESTS *pListenTarget ; + + // + // Find the first listen that matches the remote name else + // take a listen that specified '*' + // + pListenTarget = NULL; + for ( pEntry = pClientEle->ListenHead.Flink ; + pEntry != &pClientEle->ListenHead ; + pEntry = pEntry->Flink ) + { + pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage); + + // in NT-land the pConnInfo structure is passed in , but the + // remote address field is nulled out... so we need to check + // both of these before going on to check the remote address. + if ( pListen->pConnInfo && pListen->pConnInfo->RemoteAddress) + { + + if ( CTEMemEqu( + ((PTA_NETBIOS_ADDRESS)pListen->pConnInfo->RemoteAddress)-> + Address[0].Address[0].NetbiosName, + pRemoteAddress->Address[0].Address[0].NetbiosName, + NETBIOS_NAME_SIZE ) ) + { + pListenTarget = pListen ; + break ; + } + } + else + { + // + // Specified '*' for the remote name, save this, + // look for listen on a real name - only save if it is + // the first * listen found + // + if (!pListenTarget) + { + pListenTarget = pListen ; + } + } + } + + if (pListenTarget) + { + PTA_NETBIOS_ADDRESS pRemoteAddr; + + RemoveEntryList( &pListenTarget->Linkage ); + CTESpinFreeAtDpc(pClientEle); + + // + // Fill in the remote machines name to return to the client + // + if ((pListenTarget->pReturnConnInfo) && + (pRemoteAddr = pListenTarget->pReturnConnInfo->RemoteAddress)) + { + CTEMemCopy(pRemoteAddr,pRemoteAddress,RemoteAddressLength); + } + + // + // get the upper connection end point out of the listen and + // hook the upper and lower connections together. + // + pConnectEle = (tCONNECTELE *)pListenTarget->pConnectEle; + + CTESpinLockAtDpc(pConnectEle); + CTESpinLockAtDpc(pClientEle); + + pConnectEle->pLowerConnId = (PVOID)pLowerConn; + pConnectEle->state = NBT_SESSION_WAITACCEPT; + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + + pLowerConn->pUpperConnection = pConnectEle; + pLowerConn->State = NBT_SESSION_WAITACCEPT; + pLowerConn->StateRcv = NORMAL; + + CHECK_PTR(pConnectEle); + SetStateProc( pLowerConn, RejectAnyData ) ; + + // + // put the upper connection on its active list + // + RemoveEntryList(&pConnectEle->Linkage); + InsertTailList(&pConnectEle->pClientEle->ConnectActive,&pConnectEle->Linkage); + + // + // Save the remote name while we still have it + // + CTEMemCopy( pConnectEle->RemoteName, + pRemoteAddress->Address[0].Address[0].NetbiosName, + NETBIOS_NAME_SIZE ) ; + + CTESpinFreeAtDpc(pClientEle); + + if (!(pListenTarget->Flags & TDI_QUERY_ACCEPT)) + { + // + // We need to send a session response PDU here, since + // we do not have to wait for an accept in this case + // + CompleteSessionSetup(pClientEle, + pLowerConn,pConnectEle, + pListenTarget->pIrp, + OldIrq); + + } + else + { + // + // complete the client listen irp, which will trigger him to + // issue an accept, which should find the connection in the + // WAIT_ACCEPT state, and subsequently cause a session response + // to be sent. + // + // since the lower connection now points to pConnectEle, increment + // the reference count so we can't free pConnectEle memory until + // the lower conn no longer points to it. + pConnectEle->RefCount++; + + ClearConnStructures(pLowerConn,pConnectEle); + + CTESpinFreeAtDpc(pConnectEle); + CTESpinFree(&NbtConfig.JointLock,OldIrq); +#ifndef VXD + // the irp can't get cancelled because the cancel listen routine + // also grabs the Client spin lock and removes the listen from the + // list.. + CTEIoComplete( pListenTarget->pIrp,STATUS_SUCCESS,0); +#else + CTEIoComplete( pListenTarget->pIrp,STATUS_SUCCESS, (ULONG) pConnectEle); +#endif + } + + CTEMemFree((PVOID)pRemoteAddress); + CTEMemFree(pListenTarget); + + // now that we have notified the client, dereference it + // + NbtDereferenceClient(pClientEle); + + PUSH_LOCATION(0x60); + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt: Accepted Connection by a Listen %X LowerConn=%X\n",pConnectEle,pLowerConn)); + + // fake out the transport so it frees its receive buffer (i.e. we + // say that we accepted all of the data) + *BytesTaken = BytesAvailable; + goto ExitCode; + } + else + CTESpinFreeAtDpc(pClientEle); + + } + else + CTESpinFreeAtDpc(pClientEle); + + // + // No LISTEN, so check for an Event handler + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + if (!pClientEle->ConEvContext) + { + + RejectSession(pLowerConn, + NBT_NEGATIVE_SESSION_RESPONSE, + SESSION_NOT_LISTENING_ON_CALLED_NAME, + TRUE); + + // undo the reference done in FindEndpoint + // + NbtDereferenceClient(pClientEle); + + goto ExitCode; + } +#ifdef VXD + else + { + ASSERT( FALSE ) ; + } +#endif + + // now call the client's connect handler... + pIrp = NULL; +#ifndef VXD // VXD doesn't support event handlers + + status = (*pClientEle->evConnect)(pClientEle->ConEvContext, + RemoteAddressLength, + pRemoteAddress, + 0, + NULL, + 0, // options length + NULL, // Options + &ConnectId, + &pIrp + ); + // + // With the new TDI semantics is it illegal to return STATUS_EVENT_DONE + // or STATUS_EVENT_PENDING from the connect event handler + // + ASSERT(status != STATUS_EVENT_PENDING); + ASSERT(status != STATUS_EVENT_DONE); + + + // now that we have notified the client, dereference it + // + NbtDereferenceClient(pClientEle); + + // Check the returned status codes.. + if (status == STATUS_MORE_PROCESSING_REQUIRED && pIrp != NULL) + { + // connection is accepted + (VOID)NTProcessAcceptIrp(pIrp,&pConnectEle); + + // + // Save the remote name while we still have it + // + CTEMemCopy( pConnectEle->RemoteName, + pRemoteAddress->Address[0].Address[0].NetbiosName, + NETBIOS_NAME_SIZE ) ; + + // be sure the connection is in the correct state + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pConnectEle); + if (pConnectEle->state != NBT_ASSOCIATED) + { + CTESpinFreeAtDpc(pConnectEle); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + goto RejectIt; + } + else + { + pConnectEle->state = NBT_SESSION_UP; + + CHECK_PTR(pConnectEle); + + CompleteSessionSetup(pClientEle,pLowerConn,pConnectEle,pIrp,OldIrq); + + } + + } + else + { +RejectIt: + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:The client rejected in the inbound connection status = %X\n", + status)); + RejectSession(pLowerConn, + NBT_NEGATIVE_SESSION_RESPONSE, + SESSION_CALLED_NAME_PRESENT_NO_RESRC, + TRUE); + + } +#endif + // + // free the memory allocated for the Remote address data structure + // + CTEMemFree((PVOID)pRemoteAddress); + +ExitCode: + // This spin lock is held by the routine that calls this one, and + // freed when this routine starts, so we must regrab this lock before + // returning + // + CTESpinLockAtDpc(pLowerConn); + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +VOID +ClearConnStructures ( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnectEle + ) +/*++ + +Routine Description: + + This routine sets various parts of the connection datastructures to + zero, in preparation for a new connection. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + CHECK_PTR(pConnectEle); +#ifndef VXD + pConnectEle->FreeBytesInMdl = 0; + pConnectEle->CurrentRcvLen = 0; + pLowerConn->BytesInIndicate = 0; +#endif + pConnectEle->ReceiveIndicated = 0; + pConnectEle->BytesInXport = 0; + pConnectEle->BytesRcvd = 0; + pConnectEle->TotalPcktLen = 0; + pConnectEle->OffsetFromStart = 0; + pConnectEle->pIrpRcv = NULL; + pConnectEle->pIrp = NULL; + pConnectEle->pIrpDisc = NULL; + pConnectEle->pIrpClose = NULL; + pConnectEle->DiscFlag = 0; + pConnectEle->JunkMsgFlag = FALSE; + pConnectEle->pLowerConnId = (PVOID)pLowerConn; + InitializeListHead(&pConnectEle->RcvHead); + + pLowerConn->pUpperConnection = pConnectEle; + pLowerConn->StateRcv = NORMAL; + + pLowerConn->BytesRcvd = 0; + pLowerConn->BytesSent = 0; + +} +//---------------------------------------------------------------------------- +NTSTATUS +CompleteSessionSetup ( + IN tCLIENTELE *pClientEle, + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnectEle, + IN PCTE_IRP pIrp, + IN CTELockHandle OldIrq + ) +/*++ + +Routine Description: + + This routine is called to setup an outbound session + once the tcp connection is up. The transport calls this routine with + a session setup response pdu. + + The pConnectEle spin lock is held when this routine is called. + + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + NTSTATUS status; + + // + // hook the upper and lower connections together to + // complete the address list. + // + CTESpinLockAtDpc(pClientEle); + + RemoveEntryList(&pConnectEle->Linkage); + + ClearConnStructures(pLowerConn,pConnectEle); + pConnectEle->state = NBT_SESSION_UP; + + pLowerConn->State = NBT_SESSION_UP; + + SetStateProc( pLowerConn, Normal ) ; + PUSH_LOCATION(0x61); + + + InsertTailList(&pConnectEle->pClientEle->ConnectActive, + &pConnectEle->Linkage); + + + // since the lower connection now points to pConnectEle, increment + // the reference count so we can't free pConnectEle memory until + // the lower conn no longer points to it. + // + pConnectEle->RefCount++; + CTESpinFreeAtDpc(pClientEle); + // + // the pConnecteEle + if (OldIrq) + { + CTESpinFreeAtDpc(pConnectEle); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + status = TcpSendSessionResponse(pLowerConn, + NBT_POSITIVE_SESSION_RESPONSE, + 0L); + + if (NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt: Accepted Connection %X LowerConn=%X\n",pConnectEle,pLowerConn)); + + // + // complete the client's accept Irp + // +#ifndef VXD + CTEIoComplete(pIrp,STATUS_SUCCESS,0); +#else + CTEIoComplete( pIrp,STATUS_SUCCESS, (ULONG) pConnectEle); +#endif + + } + else + { // + // if we have some trouble sending the Session response, then + // disconnect the connection + // + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Could not send the Session Response to TCP status = %X\n", + status)); + + + RejectSession(pLowerConn, + NBT_NEGATIVE_SESSION_RESPONSE, + SESSION_CALLED_NAME_PRESENT_NO_RESRC, + TRUE); + + + RelistConnection(pConnectEle); + + // Disconnect To Client - i.e. a negative Accept + // this will get done when the disconnect indication + // comes back from the transport + // + GetIrpIfNotCancelled(pConnectEle,&pIrp); + if (pIrp) + { + CTEIoComplete(pIrp,STATUS_UNSUCCESSFUL,0); + } + } + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +Outbound ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *RcvBuffer + + ) +/*++ + +Routine Description: + + This routine is called to setup an outbound session + once the tcp connection is up. The transport calls this routine with + a session setup response pdu . + + +Arguments: + + pClientEle - ptr to the connection record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + tSESSIONHDR UNALIGNED *pSessionHdr; + tLOWERCONNECTION *pLowerConn; + CTELockHandle OldIrq; + PIRP pIrp; + tTIMERQENTRY *pTimerEntry; + tCONNECTELE *pConnEle; + tDGRAM_SEND_TRACKING *pTracker; + tDEVICECONTEXT *pDeviceContext; + NTSTATUS status; + + // get the ptr to the lower connection + // + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu; + pDeviceContext = pLowerConn->pDeviceContext; + + // + // fake out the transport so it frees its receive buffer (i.e. we + // say that we accepted all of the data) + // + *BytesTaken = BytesIndicated; + // + // since we send keep alives on connections in the the inbound + // state it is possible to get a keep alive, so just return in that + // case + // + if (((tSESSIONHDR UNALIGNED *)pTsdu)->Type == NBT_SESSION_KEEP_ALIVE) + { + return(STATUS_SUCCESS); + } + + pConnEle = pLowerConn->pUpperConnection; + + // the LowerConn Lock is held prior to calling this routine, so free it + // here since we need to get the joint lock first + CTESpinFreeAtDpc(pLowerConn); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pLowerConn); + + // + // it is possible for the disconnect handler to run while the pLowerConn + // lock is released above, to get the ConnEle lock, and change the state + // to disconnected. + // + if (!pConnEle || (pConnEle->state != NBT_SESSION_OUTBOUND)) + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + RejectSession(pLowerConn,0,0,FALSE); + goto ExitCode; + } + + // NbtConnect stores the tracker in the IrpRcv ptr so that this + // routine can access it + // + pTracker = (tDGRAM_SEND_TRACKING *)pConnEle->pIrpRcv; + // + // if no tracker then SessionStartupCompletion has run and the connection + // is about to be closed, so return. + // + if (!pTracker) + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + goto ExitCode; + } + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + // + // Stop the timer started in SessionStartupCompletion to time the + // Session Setup Response message - it is possible for this routine to + // run before SessionStartupCompletion, in which case there will not be + // any timer to stop. + // + if (pTimerEntry = pTracker->Connect.pTimer) + { + StopTimer(pTimerEntry,NULL,NULL); + CHECK_PTR(pTracker); + pTracker->Connect.pTimer = NULL; + } + + if (pSessionHdr->Type == NBT_POSITIVE_SESSION_RESPONSE) + { + // zero out the number of bytes received so far, since this is + // a new connection + CHECK_PTR(pConnEle); + pConnEle->BytesRcvd = 0; + pConnEle->state = NBT_SESSION_UP; + + pLowerConn->State = NBT_SESSION_UP; + SetStateProc( pLowerConn, Normal ) ; + + CTESpinFreeAtDpc(pLowerConn); + + GetIrpIfNotCancelled2(pConnEle,&pIrp); + + // + // if SessionSetupContinue has run, it has set the refcount to zero + // + if (pTracker->RefConn == 0) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + } + else + { + pTracker->RefConn--; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + CHECK_PTR(pLowerConn->pUpperConnection); + pLowerConn->pUpperConnection->pIrpRcv = NULL; + + // the assumption is that if the connect irp was cancelled then the + // client should be doing a disconnect or close shortly thereafter, so + // there is no error handling code here. + if (pIrp) + { + // + // complete the client's connect request Irp + // +#ifndef VXD + CTEIoComplete( pIrp, STATUS_SUCCESS, 0 ) ; +#else + CTEIoComplete( pIrp, STATUS_SUCCESS, (ULONG)pConnEle ) ; +#endif + } + } + else + { + ULONG ErrorCode; + ULONG state; + tNAMEADDR *pNameAddr; + + state = pConnEle->state; + + // If the response is Retarget then setup another session + // to the new Ip address and port number. + // + ErrorCode = (ULONG)((tSESSIONERROR *)pSessionHdr)->ErrorCode; + if (pSessionHdr->Type == NBT_RETARGET_SESSION_RESPONSE) + { + // + // retry the session setup if we haven't already exceeded the + // count + // + if (pConnEle->SessionSetupCount--) + { + PVOID Context=NULL; + BOOLEAN Cancelled; + + pConnEle->state = NBT_ASSOCIATED; + + // for retarget the destination has specified an alternate + // port to which the session should be established. + if (pSessionHdr->Type == NBT_RETARGET_SESSION_RESPONSE) + { + pTracker->DestPort = ntohs(((tSESSIONRETARGET *)pSessionHdr)->Port); + Context = (PVOID)ntohl(((tSESSIONRETARGET *)pSessionHdr)->IpAddress); + } + else + if (ErrorCode == SESSION_CALLED_NAME_NOT_PRESENT) + { + // to tell Reconnect to use the current name(not a retarget) + Context = NULL; + } + + // + // Unlink the lower and upper connections. + // + CHECK_PTR(pConnEle); + CHECK_PTR(pLowerConn); + pLowerConn->pUpperConnection = NULL; + pConnEle->pLowerConnId = NULL; + + CTESpinFreeAtDpc(pLowerConn); + + // + // put the pconnele back on the Client's ConnectHead if it + // has not been cleanedup yet. + // + if (state != NBT_IDLE) + { + RelistConnection(pConnEle); + } + + // if a disconnect comes down in this state we we will handle it. + pConnEle->state = NBT_RECONNECTING; + + CHECK_PTR(pConnEle); + pConnEle->SessionSetupCount = 0;// only allow one retry + + pIrp = pConnEle->pIrp; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // remove the referenced added when the lower and upper + // connections were attached in nbtconnect. + // + NbtDereferenceConnection(pConnEle); + + RejectSession(pLowerConn,0,0,FALSE); + Cancelled = FALSE; + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Attempt Reconnect after receiving error on connect, error=%X LowerConn %X\n", + ErrorCode,pLowerConn)); +#ifndef VXD + // the irp can't be cancelled until the connection + // starts up again - either when the Irp is in the transport + // or when we set our cancel routine in SessionStartupCompletion + // This disconnect handler cannot complete the Irp because + // we set the pConnEle state to NBT_ASSOCIATED above, with + // the spin lock held, which prevents the Disconnect handler + // from doing anything. + IoAcquireCancelSpinLock(&OldIrq); + if (pIrp && !pConnEle->pIrp->Cancel) + { + IoSetCancelRoutine(pIrp,NULL); + } + else + Cancelled = TRUE; + + IoReleaseCancelSpinLock(OldIrq); +#endif + + if (!Cancelled) + { + CTEQueueForNonDispProcessing(pTracker, + Context, + NULL, + ReConnect, + pDeviceContext); + } + // ...else The irp was already returned, since NtCancelSession + // Must have already run, so just return + goto ExitCode; + } + } + + pNameAddr = pTracker->pNameAddr; + // + // 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 ((pNameAddr->Verify == REMOTE_NAME) && + (pNameAddr->RefCount == 1) && + (pNameAddr->NameTypeState & STATE_RESOLVED)) + { + NbtDereferenceName(pNameAddr); + } + + // the connection will be disconnected by the Call to RejectSession + // below, so set the state to Associated so the disconnect indication + // handler will not complete the client's irp too + // + pConnEle->state = NBT_ASSOCIATED; + CHECK_PTR(pConnEle); + pConnEle->pLowerConnId = NULL; + + CTESpinFreeAtDpc(pLowerConn); + + // + // if nbtcleanupconnection has not been called yet, relist it. + // + if (state != NBT_IDLE) + { + RelistConnection(pConnEle); + } + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Disconnecting... Failed connection Setup %X Lowercon %X\n", + pConnEle,pLowerConn)); + + GetIrpIfNotCancelled2(pConnEle,&pIrp); + + // + // if SessionSetupContinue has run, it has set the refcount to zero + // + if (pTracker->RefConn == 0) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + } + else + { + pTracker->RefConn--; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + // this should cause a disconnect indication to come from the + // transport which will close the connection to the transport + // + RejectSession(pLowerConn,0,0,FALSE); + + // + // tell the client that the session setup failed and disconnect + // the connection + // + + if (pIrp) + { + status = STATUS_REMOTE_NOT_LISTENING; + if (ErrorCode == SESSION_CALLED_NAME_NOT_PRESENT) + { + status = STATUS_BAD_NETWORK_PATH; + } + + CTEIoComplete(pIrp, status, 0 ) ; + } + } + +ExitCode: + // the LowerConn Lock is held prior to calling this routine. It is freed + // at the start of this routine and held here again + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +VOID +GetIrpIfNotCancelled2( + IN tCONNECTELE *pConnEle, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + This routine coordinates access to the Irp by getting the spin lock on + the client, getting the Irp and clearing the irp in the structure. The + Irp cancel routines also check the pConnEle->pIrp and if null they do not + find the irp, then they return without completing the irp. + + This version of the routine is called with NbtConfig.JointLock held. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + CTELockHandle OldIrq; + + CTESpinLock(pConnEle,OldIrq); + + *ppIrp = pConnEle->pIrp; + CHECK_PTR(pConnEle); + pConnEle->pIrp = NULL; + + CTESpinFree(pConnEle,OldIrq); +} + +//---------------------------------------------------------------------------- +VOID +GetIrpIfNotCancelled( + IN tCONNECTELE *pConnEle, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + This routine coordinates access to the Irp by getting the spin lock on + the client, getting the Irp and clearing the irp in the structure. The + Irp cancel routines also check the pConnEle->pIrp and if null they do not + find the irp, then they return without completing the irp. + + This version of the routine is called with NbtConfig.JointLock free. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + CTELockHandle OldIrq; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + GetIrpIfNotCancelled2(pConnEle,ppIrp); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); +} +//---------------------------------------------------------------------------- +NTSTATUS +RejectAnyData( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler when the connection + is not up - i.e. nbt thinks no data should be arriving. We just eat the + data and return. This routine should not get called. + + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + + // + // take all of the data so that a disconnect will not be held up + // by data still in the transport. + // + *BytesTaken = BytesAvailable; + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Got Session Data in state %X, StateRcv= %X\n",pLowerConn->State, + pLowerConn->StateRcv)); + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +VOID +RejectSession( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG StatusCode, + IN ULONG SessionStatus, + IN BOOLEAN SendNegativeSessionResponse + ) +/*++ + +Routine Description: + + This routine sends a negative session response (if the boolean is set) + and then disconnects the connection. + Cleanup connection could have been called to disconnect the call, + and it changes the state to disconnecting, so don't disconnected + again if that is happening. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + NTSTATUS status; + tCONNECTELE *pConnEle; + BOOLEAN DerefConnEle=FALSE; + + // + // There is no listen event handler so return a status code to + // the caller indicating that this end is between "listens" and + // that they should try the setup again in a few milliseconds. + // + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt: No Listen or Connect Handlr so Disconnect! LowerConn=%X Session Status=%X\n", + pLowerConn,SessionStatus)); + + if (SendNegativeSessionResponse) + { + status = TcpSendSessionResponse(pLowerConn, + StatusCode, + SessionStatus); + } + + // need to hold this lock if we are to un connect the lower and upper + // connnections + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + CTESpinLock(pLowerConn,OldIrq); + + if ((pLowerConn->State < NBT_DISCONNECTING) && + (pLowerConn->State > NBT_CONNECTING)) + { + pLowerConn->State = NBT_DISCONNECTING; + SetStateProc( pLowerConn, RejectAnyData ) ; + CHECK_PTR(pLowerConn); + + pConnEle = pLowerConn->pUpperConnection; + if (pConnEle) + { + CHECK_PTR(pConnEle); + CHECK_PTR(pLowerConn); + DerefConnEle = TRUE; + pLowerConn->pUpperConnection = NULL; + pConnEle->pLowerConnId = NULL; + } + + CTESpinFree(pLowerConn,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + SendTcpDisconnect((PVOID)pLowerConn); + } + else + { + CTESpinFree(pLowerConn,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + + if (DerefConnEle) + { + DereferenceIfNotInRcvHandler(pConnEle,pLowerConn); + } +} +//---------------------------------------------------------------------------- +NTSTATUS +FindSessionEndPoint( + IN PVOID pTsdu, + IN PVOID ConnectionContext, + IN ULONG BytesIndicated, + OUT tCLIENTELE **ppClientEle, + OUT PVOID *ppRemoteAddress, + OUT PULONG pRemoteAddressLength + ) +/*++ + +Routine Description: + + This routine attempts to find an end point on the node with the matching + net bios name. It is called at session setup time when a session request + PDU has arrived. The routine returns the Client Element ptr. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + NTSTATUS status; + tCLIENTELE *pClientEle; + tLOWERCONNECTION *pLowerConn; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + tNAMEADDR *pNameAddr; + tADDRESSELE *pAddressEle; + PLIST_ENTRY pEntry; + PLIST_ENTRY pHead; + ULONG lNameSize; + tSESSIONREQ UNALIGNED *pSessionReq = (tSESSIONREQ UNALIGNED *)pTsdu; + USHORT sType; + CTELockHandle OldIrq2; + PUCHAR pSrcName; + BOOLEAN Found; + + // get the ptr to the lower connection, and from that get the ptr to the + // upper connection block + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + + if (pSessionReq->Hdr.Type != NBT_SESSION_REQUEST) + { + return(SESSION_UNSPECIFIED_ERROR); + } + + // get the called name out of the PDU + status = ConvertToAscii( + (PCHAR)&pSessionReq->CalledName.NameLength, + BytesIndicated - FIELD_OFFSET(tSESSIONREQ,CalledName.NameLength), + pName, + &pScope, + &lNameSize); + + if (!NT_SUCCESS(status)) + { + return(SESSION_UNSPECIFIED_ERROR); + } + + + // now try to find the called name in this node's Local table + // + + // + // in case a disconnect came in while the spin lock was released + // + if (pLowerConn->State != NBT_SESSION_INBOUND) + { + return(STATUS_UNSUCCESSFUL); + } + + pNameAddr = FindName(NBT_LOCAL,pName,pScope,&sType); + + if (!pNameAddr) + { + return(SESSION_CALLED_NAME_NOT_PRESENT); + } + + // we got to here because the name has resolved to a name on this node, + // so accept the Session setup. + // + pAddressEle = (tADDRESSELE *)pNameAddr->pAddressEle; + + // lock the address structure until we find a client on the list + // + CTESpinLock(pAddressEle,OldIrq2); + + if (IsListEmpty(&pAddressEle->ClientHead)) + { + CTESpinFree(pAddressEle,OldIrq2); + return(SESSION_NOT_LISTENING_ON_CALLED_NAME); + } + + // + // get the first client on the list that is bound to the same + // devicecontext as the connection, with a listen posted, or a valid + // Connect event handler setup - + // + Found = FALSE; + pHead = &pAddressEle->ClientHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + if (pClientEle->pDeviceContext == pLowerConn->pDeviceContext) + { + // + // if there is a listen posted or a Connect Event Handler + // then allow the connect attempt to carry on, otherwise go to the + // next client in the list + // + if ((!IsListEmpty(&pClientEle->ListenHead)) || + (pClientEle->ConEvContext)) + { + Found = TRUE; + break; + } + } + + pEntry = pEntry->Flink; + + } + + if (!Found) + { + CTESpinFree(pAddressEle,OldIrq2); + return(SESSION_NOT_LISTENING_ON_CALLED_NAME); + } + + // prevent the client from disappearing before we can indicate to him + // + CTEInterlockedIncrementLong(&pClientEle->RefCount); + + pSrcName = (PUCHAR)((PUCHAR)&pSessionReq->CalledName.NameLength + lNameSize + 1); + + status = MakeRemoteAddressStructure( + pSrcName, + 0, + BytesIndicated-lNameSize, + ppRemoteAddress, + pRemoteAddressLength, + 1); + + if (!NT_SUCCESS(status)) + { + CTESpinFree(pAddressEle,OldIrq2); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + NbtDereferenceClient(pClientEle); + CTESpinLockAtDpc(&NbtConfig.JointLock); + + if (status == STATUS_INSUFFICIENT_RESOURCES) + { + return(SESSION_CALLED_NAME_PRESENT_NO_RESRC); + } + else + return(SESSION_UNSPECIFIED_ERROR); + + } + + CTESpinFree(pAddressEle,OldIrq2); + + *ppClientEle = pClientEle; + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +MakeRemoteAddressStructure( + IN PCHAR pHalfAsciiName, + IN PVOID pSourceAddr, + IN ULONG lMaxNameSize, + OUT PVOID *ppRemoteAddress, + OUT PULONG pRemoteAddressLength, + IN ULONG NumAddr + ) +/*++ + +Routine Description: + + This routine makes up the remote addres structure with the netbios name + of the source in it, so that the info can be passed to the client...what + a bother to do this! + +Arguments: + + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + NTSTATUS status; + ULONG lNameSize; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + PTA_NETBIOS_ADDRESS pRemoteAddress; + + // make up the remote address data structure to pass to the client + status = ConvertToAscii( + pHalfAsciiName, + lMaxNameSize, + pName, + &pScope, + &lNameSize); + + if (!NT_SUCCESS(status)) + { + return(status); + } + + pRemoteAddress = (PTA_NETBIOS_ADDRESS)NbtAllocMem( + NumAddr * sizeof(TA_NETBIOS_ADDRESS),NBT_TAG('2')); + if (!pRemoteAddress) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pRemoteAddress->TAAddressCount = NumAddr; + pRemoteAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS); + pRemoteAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + pRemoteAddress->Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + CTEMemCopy(pRemoteAddress->Address[0].Address[0].NetbiosName, + pName,NETBIOS_NAME_SIZE); + + *pRemoteAddressLength = FIELD_OFFSET(TA_NETBIOS_ADDRESS, Address[0].Address[0].NetbiosName[NETBIOS_NAME_SIZE]); + + // + // Copy over the IP address also. + // + if (NumAddr == 2) { + TA_ADDRESS UNALIGNED *pTAAddr; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SubnetMask; + + pSourceAddress = (PTRANSPORT_ADDRESS)pSourceAddr; + + pTAAddr = (TA_ADDRESS UNALIGNED *) (((PUCHAR)pRemoteAddress) + pRemoteAddress->Address[0].AddressLength + FIELD_OFFSET(TA_NETBIOS_ADDRESS, Address[0].Address)); + + pTAAddr->AddressLength = sizeof(TDI_ADDRESS_IP); + pTAAddr->AddressType = TDI_ADDRESS_TYPE_IP; + ((TDI_ADDRESS_IP UNALIGNED *)&pTAAddr->Address[0])->in_addr = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr; + *pRemoteAddressLength += (FIELD_OFFSET(TA_ADDRESS, Address) + pTAAddr->AddressLength); + } + + *ppRemoteAddress = (PVOID)pRemoteAddress; +// *pRemoteAddressLength = sizeof(TA_NETBIOS_ADDRESS); +// *pRemoteAddressLength = FIELD_OFFSET(TA_NETBIOS_ADDRESS, Address[0].Address[0]); + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +ConnectHndlrNotOs ( + IN PVOID pConnectionContext, + IN LONG RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN VOID UNALIGNED *pUserData, + OUT CONNECTION_CONTEXT *ppConnectionId + ) +/*++ + +Routine Description: + + This routine is the receive connect indication handler. + + It is called when a TCP connection is being setup for a NetBios session. + It simply allocates a connection and returns that information to the + transport so that the connect indication can be accepted. + +Arguments: + + pClientEle - ptr to the connecition record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + CTELockHandle OldIrq; + PLIST_ENTRY pList; + tLOWERCONNECTION *pLowerConn; + tDEVICECONTEXT *pDeviceContext; + PTRANSPORT_ADDRESS pSrcAddress; + + pDeviceContext = (tDEVICECONTEXT *)pConnectionContext; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pDeviceContext); + + pSrcAddress = pRemoteAddress; + + // get a free connection to the transport provider to accept this + // incoming connnection on. + // + if (IsListEmpty(&pDeviceContext->LowerConnFreeHead)) + { + CTESpinFreeAtDpc(pDeviceContext); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // check that the source is an IP address + // + if (pSrcAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP) + { + CTESpinFreeAtDpc(pDeviceContext); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // take an idle connection and move it to the active connection list + // + pList = RemoveHeadList(&pDeviceContext->LowerConnFreeHead); + + // + // If there are less than 2 connections remaining, we allocate another one. The check + // below is for 0 or 1 connections. + // In order to protect ourselves from SYN ATTACKS, allocate NbtConfig.SpecialConnIncrement more now until + // a certain (registry config) value is exhausted (NOTE this number is global and not + // per device). + // + if ((pDeviceContext->LowerConnFreeHead.Flink->Flink == &pDeviceContext->LowerConnFreeHead) && + ((ULONG)InterlockedExchangeAdd(&NbtConfig.NumSpecialLowerConn, 0) <= NbtConfig.MaxBackLog)) { + + if ((ULONG)InterlockedExchangeAdd(&NbtConfig.NumQueuedForAlloc, 0) == 0) { +#ifndef VXD + KdPrint(("Queueing - SpecialLowerConn: %d, Actual: %d, NumQueuedForAlloc : %d\n", + NbtConfig.NumSpecialLowerConn, + NbtConfig.ActualNumSpecialLowerConn, + NbtConfig.NumQueuedForAlloc)); +#endif + CTEQueueForNonDispProcessing( + NULL, + pDeviceContext, + NULL, + DelayedAllocLowerConnSpecial, + pDeviceContext); + + InterlockedExchangeAdd(&NbtConfig.NumSpecialLowerConn, NbtConfig.SpecialConnIncrement); + InterlockedIncrement(&NbtConfig.NumQueuedForAlloc); + } + } + + InsertTailList(&pDeviceContext->LowerConnection,pList); + + pLowerConn = CONTAINING_RECORD(pList,tLOWERCONNECTION,Linkage); + + pLowerConn->State = NBT_SESSION_INBOUND; + pLowerConn->StateRcv = NORMAL; + + SetStateProc( pLowerConn, Inbound ) ; + + // this end is NOT the originator + pLowerConn->bOriginator = FALSE; + + // increase the reference count because we are now connected. Decrement + // it when we disconnect. + // + ASSERT(pLowerConn->RefCount == 1); + CTEInterlockedIncrementLong(&pLowerConn->RefCount); + // save the source clients IP address into the connection Structure + // *TODO check if we need to do this or not + // + pLowerConn->SrcIpAddr = + ((PTDI_ADDRESS_IP)&pSrcAddress->Address[0].Address[0])->in_addr; + + CTESpinFreeAtDpc(pDeviceContext); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + *ppConnectionId = (PVOID)pLowerConn; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +DisconnectHndlrNotOs ( + PVOID EventContext, + PVOID ConnectionContext, + ULONG DisconnectDataLength, + PVOID pDisconnectData, + ULONG DisconnectInformationLength, + PVOID pDisconnectInformation, + ULONG DisconnectIndicators + ) +/*++ + +Routine Description: + + This routine is the receive disconnect indication handler. It is called + by the transport when a connection disconnects. It checks the state of + the lower connection and basically returns a disconnect request to the + transport, except in the case where there is an active session. In this + case it calls the the clients disconnect indication handler. The client + then turns around and calls NbtDisconnect(in some cases), which passes a disconnect + back to the transport. The transport won't disconnect until it receives + a disconnect request from its client (NBT). If the flag TDI_DISCONNECT_ABORT + is set then there is no need to pass back a disconnect to the transport. + + Since the client doesn't always issue a disconnect (i.e. the server), + this routine always turns around and issues a disconnect to the transport. + In the disconnect done handling, the lower connection is put back on the + free list if it is an inbound connection. For out bound connection the + lower and upper connections are left connected, since these will always + receive a cleanup and close connection from the client (i.e. until the + client does a close the lower connection will not be freed for outbound + connections). + +Arguments: + + pClientEle - ptr to the connecition record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + NTSTATUS status; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + CTELockHandle OldIrq3; + CTELockHandle OldIrq4; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + tCLIENTELE *pClientEle; + USHORT state; + BOOLEAN CleanupLower=FALSE; + USHORT stateLower; + PIRP pIrp= NULL; + PIRP pIrpClose= NULL; + PIRP pIrpRcv= NULL; + tDGRAM_SEND_TRACKING *pTracker; + tTIMERQENTRY *pTimerEntry; + BOOLEAN InsertOnList=FALSE; + BOOLEAN DisconnectIt=FALSE; + ULONG StateRcv; + COMPLETIONCLIENT pCompletion; + + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pConnectEle = pLowerConn->pUpperConnection; + PUSH_LOCATION(0x63); + + CHECK_PTR(pLowerConn); + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Disc Indication, LowerConn state = %X %X\n", + pLowerConn->State,pLowerConn)); + + // get the current state with the spin lock held to avoid a race condition + // with the client disconnecting + // + if (pConnectEle) + { + if ( (pConnectEle->Verify != NBT_VERIFY_CONNECTION) && + (pConnectEle->Verify != NBT_VERIFY_CONNECTION_DOWN)) + { + // ASSERT( FALSE ) ; Put back in after problem is fixed +#ifdef VXD + CTEPrint("\bDisconnectHndlrNotOs: Disconnect indication after already disconnect!!\b\r\n") ; +#endif + return STATUS_UNSUCCESSFUL ; + } + CHECK_PTR(pConnectEle); + + // need to hold the joint lock if unconnecting the lower and upper + // connections. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq4); + CTESpinLock(pConnectEle,OldIrq2); + + // + // We got a case where the ClientEle ptr was null. This shd not happen since the + // connection shd be associated at this time. + // Assert for that case to track this better. + // + ASSERT(pConnectEle->pClientEle); + CTESpinLock(pConnectEle->pClientEle,OldIrq3); + CTESpinLock(pLowerConn,OldIrq); + state = pConnectEle->state; + stateLower = pLowerConn->State; + +#ifdef VXD + DbgPrint("DisconnectHndlrNotOs: pConnectEle->state = 0x") ; + DbgPrintNum( (ULONG) state ) ; + DbgPrint("pLowerConn->state = 0x") ; DbgPrintNum( (ULONG) stateLower ) ; + DbgPrint("\r\n") ; +#endif + + if ((state > NBT_ASSOCIATED) && (state < NBT_DISCONNECTING)) + { + + PUSH_LOCATION(0x63); + CHECK_PTR(pConnectEle); + + CHECK_PTR(pConnectEle); + // + // this irp gets returned to the client below in the case statement + // Except in the connecting state where the transport still has + // the irp. In that case we let SessionStartupContinue complete + // the irp. + // + if ((pConnectEle->pIrp) && (state > NBT_CONNECTING)) + { + pIrp = pConnectEle->pIrp; + pConnectEle->pIrp = NULL; + } + + // + // if there is a receive irp, get it out of pConnEle since pConnEle + // will be requeued and could get used again before we try to + // complete this irp down below. Null the cancel routine if not + // cancelled and just complete it below. + // + if (((state == NBT_SESSION_UP) || (state == NBT_SESSION_WAITACCEPT)) + && (pConnectEle->pIrpRcv)) + { + CTELockHandle OldIrql; + + pIrpRcv = pConnectEle->pIrpRcv; + +#ifndef VXD + IoAcquireCancelSpinLock(&OldIrql); + // + // if its already cancelled then don't complete it again + // down below + // + if (pIrpRcv->Cancel) + { + pIrpRcv = NULL; + } + else + { + IoSetCancelRoutine(pIrpRcv,NULL); + } + IoReleaseCancelSpinLock(OldIrql); +#endif + pConnectEle->pIrpRcv = NULL; + } + + // This irp is used for DisconnectWait + // + if (pIrpClose = pConnectEle->pIrpClose) + { + pConnectEle->pIrpClose = NULL; + } + + pConnectEle->state = NBT_ASSOCIATED; + pConnectEle->pLowerConnId = NULL; + // + // save whether it is a disconnect abort or disconnect release + // in case the client does a disconnect wait and wants the + // real disconnect status i.e. they do not have a disconnect + // indication handler + // + pConnectEle->DiscFlag = (UCHAR)DisconnectIndicators; + +#ifdef VXD + if ( pLowerConn->StateRcv == PARTIAL_RCV && + (pLowerConn->fOnPartialRcvList == TRUE) ) + { + RemoveEntryList( &pLowerConn->PartialRcvList ) ; + pLowerConn->fOnPartialRcvList = FALSE; + InitializeListHead(&pLowerConn->PartialRcvList); + } +#endif + + pLowerConn->State = NBT_DISCONNECTING; + pLowerConn->pUpperConnection = NULL; + + // + // pConnectEle is dereferenced below, now that the lower conn + // no longer points to it. + // + InsertOnList = TRUE; + + DisconnectIt = TRUE; + + // + // put pConnEle back on the list of idle connection for this + // client + // + RemoveEntryList(&pConnectEle->Linkage); + InsertTailList(&pConnectEle->pClientEle->ConnectHead,&pConnectEle->Linkage); + + if (DisconnectIndicators == TDI_DISCONNECT_RELEASE) + { + // setting the state to disconnected will allow the DisconnectDone + // routine in updsend.c to cleanupafterdisconnect, when the disconnect + // completes (since we have been indicated) + // + pLowerConn->State = NBT_DISCONNECTED; + } + else + { + // there is no disconnect completion to wait for ...since it + // was an abortive disconnect indication + // Change the state of the lower conn incase the client has + // done a disconnect at the same time - we don't want DisconnectDone + // to also Queue up CleanupAfterDisconnect + // + pLowerConn->State = NBT_IDLE; + } + } + // + // the lower connection just went from disconnecting to disconnected + // so change the state - this signals the DisconnectDone routine to + // cleanup the connection when the disconnect request completes. + // + if (stateLower == NBT_DISCONNECTING) + { + pLowerConn->State = NBT_DISCONNECTED; + } + else + if (stateLower == NBT_DISCONNECTED) + { + // + // we get to here if the disconnect request Irp completes before the + // disconnect indication comes back. The disconnect completes + // and checks the state, changing it to Disconnected if it + // isn't already - see disconnectDone in udpsend.c. Since + // the disconnect indication and the disconnect completion + // have occurred, cleanup the connection. + // + CleanupLower = TRUE; + + // this is just a precaution that may not be needed, so we + // don't queue the cleanup twice...i.e. now that the lower state + // is disconnected, we change it's state to idle incase the + // transport hits us again with another disconnect indication. + // QueueCleanup is called below based on the value in statelower + pLowerConn->State = NBT_IDLE; + } + // + // During the time window that a connection is being setup and TCP + // completes the connect irp, we could get a disconnect indication. + // the RefCount must be incremented here so that CleanupAfterDisconnect + // does not delete the connection (i.e. it expects refcount >= 2). + // + if (stateLower <= NBT_CONNECTING) + { + pLowerConn->RefCount++; + } + + CTESpinFree(pLowerConn,OldIrq); + CTESpinFree(pConnectEle->pClientEle,OldIrq3); + CTESpinFree(pConnectEle,OldIrq2); + CTESpinFree(&NbtConfig.JointLock,OldIrq4); + + + } + else + { + CTESpinLock(&NbtConfig.JointLock,OldIrq2); + CTESpinLock(pLowerConn,OldIrq); + stateLower = pLowerConn->State; + state = NBT_IDLE; + + if ((stateLower > NBT_IDLE) && (stateLower < NBT_DISCONNECTING)) + { + // flag so we send back a disconnect to the transport + DisconnectIt = TRUE; + // + // set state so that DisconnectDone will cleanup the connection. + // + pLowerConn->State = NBT_DISCONNECTED; + // + // for an abortive disconnect we will do a cleanup below, so + // set the state to idle here + // + if (DisconnectIndicators != TDI_DISCONNECT_RELEASE) + { + pLowerConn->State = NBT_IDLE; + } + + } else + if ( stateLower == NBT_DISCONNECTING ) + { + // a Disconnect has already been initiated by this side so when + // DisconnectDone runs it will cleanup + // + pLowerConn->State = NBT_DISCONNECTED ; + } + else + if ( stateLower == NBT_DISCONNECTED ) + { + CleanupLower = TRUE; + pLowerConn->State = NBT_IDLE; + } + + // + // During the time window that a connection is being setup and TCP + // completes the connect irp, we could get a disconnect indication. + // the RefCount must be incremented here so that CleanupAfterDisconnect + // does not delete the connection (i.e. it expects refcount >= 2). + // + if ((stateLower <= NBT_CONNECTING) && + (stateLower > NBT_IDLE)) + { + pLowerConn->RefCount++; + } + CTESpinFree(pLowerConn,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + } + + StateRcv = pLowerConn->StateRcv; + SetStateProc( pLowerConn, RejectAnyData ) ; + + if (DisconnectIt) + { + if (DisconnectIndicators == TDI_DISCONNECT_RELEASE) + { + // this disconnects the connection and puts the lowerconn back on + // its free queue. Note that OutOfRsrcKill calls this routine too + // with the DisconnectIndicators set to Abort, and the code correctly + // does not attempt to disconnect the connection since the OutOfRsrc + // routine had already disconnected it. + // + PUSH_LOCATION(0x6d); + status = SendTcpDisconnect((PVOID)pLowerConn); + + } + else + { + // this is an abortive disconnect from the transport, so there is + // no need to send a disconnect request back to the transport. + // So we set a flag that tells us later in the routine to close + // the lower connection. + // + PUSH_LOCATION(0x69); + CleanupLower = TRUE; + } + } + + // + // for an orderly release, turn around and send a release to the transport + // if there is no client attached to the lower connection. If there is a + // client then we must pass the disconnect indication to the client and + // wait for the client to do the disconnect. + // + // + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:ConnEle state = %X, %X\n",state,(ULONG)pConnectEle)); + + switch (state) + { + + case NBT_SESSION_INBOUND: + case NBT_CONNECTING: + + // if an originator, then the upper and lower connections are + // already associated, and there is a client irp to return. + // (NBT_SESSION_CONNECTING only) + // + if (pIrp) + { + if (pLowerConn->bOriginator) + { + CTEIoComplete(pIrp, + STATUS_BAD_NETWORK_PATH, + 0); + } + else + { + // this could be an inbound call that could not send the + // session response correctly. + // + CTEIoComplete(pIrp, + STATUS_UNSUCCESSFUL, + 0); + } + + } + + break; + + case NBT_SESSION_OUTBOUND: + // + // + // Stop the timer started in SessionStartupCompletion to time the + // Session Setup Response message + // + // NbtConnect stores the tracker in the IrpRcv ptr so that this + // routine can access it + // + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + CTESpinLock(pConnectEle,OldIrq2); + + pTracker = (tDGRAM_SEND_TRACKING *)pConnectEle->pIrpRcv; + + // + // check if anyone else has freed the tracker yet. + // + if (pTracker) + { + pConnectEle->pIrpRcv = NULL; + pTimerEntry = pTracker->Connect.pTimer; + CHECK_PTR(pTracker); + pTracker->Connect.pTimer = NULL; + + CTESpinFree(pConnectEle,OldIrq2); + + // + // if the timer has expired it will not cleanup because the state + // will not be SESSION_OUTBOUND, since we changed it above to + // disconnected. So we always have to complete the irp and + // call cleanupafterdisconnect below. + // + if (pTimerEntry) + { + StopTimer(pTimerEntry,&pCompletion,NULL); + } + + // + // Check if the SessionStartupCompletion has run; if so, RefConn will be 0. + // Else, decrement so that the tracker goes away when the session send completes. + // + // [BUGBUGWISHLIST]: Do we need the Jointlock to protect this? use InterlockedIncrement + // instead.... + // + if (pTracker->RefConn == 0) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + } + else + { + pTracker->RefConn--; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + { + CTESpinFree(pConnectEle,OldIrq2); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + + if (pIrp) + { + CTEIoComplete(pIrp,STATUS_REMOTE_NOT_LISTENING,0); + } + + break; + + case NBT_SESSION_WAITACCEPT: + case NBT_SESSION_UP: + + if (pIrp) + { + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + } + // + // check for any RcvIrp that may be still around. If the + // transport has the Irp now then pIrpRcv = NULL. There should + // be no race condition between completing it and CompletionRcv + // setting pIrpRcv again as long as we cannot be indicated + // for a disconnect during completion of a Receive. In any + // case the access is coordinated using the Io spin lock io + // IoCancelIrp - that routine will only complete the irp once, + // then it nulls the completion routine. + // + if ((StateRcv == FILL_IRP) && pIrpRcv) + { + + PUSH_LOCATION(0x6f); + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Cancelling RcvIrp on Disconnect Indication!!!\n")); + + CTEIoComplete(pIrpRcv,STATUS_CANCELLED,0); + } + + // + // this is a disconnect for an active session, so just inform the client + // and then it issues a Nbtdisconnect. We have already disconnected the + // lowerconnection with the transport, so all that remains is + // to cleanup for outgoing calls. + // + + pClientEle = pConnectEle->pClientEle; + + // now call the client's disconnect handler...NBT always does + // a abortive disconnect - i.e. the connection is closed when + // the disconnect indication occurs and does not REQUIRE a + // disconnect from the client to finish the job.( a disconnect + // from the client will not hurt though. + // + PUSH_LOCATION(0x64); + if ((pClientEle && pClientEle->evDisconnect ) && + (!pIrpClose)) + + { + status = (*pClientEle->evDisconnect)(pClientEle->DiscEvContext, + pConnectEle->ConnectContext, + DisconnectDataLength, + pDisconnectData, + DisconnectInformationLength, + pDisconnectInformation, + TDI_DISCONNECT_ABORT); + } + else + if (pIrpClose) + { + // + // the Client has issued a disconnect Wait irp, so complete + // it now, indicating to them that a disconnect has occurred. + // + if (DisconnectIndicators == TDI_DISCONNECT_RELEASE) + { + status = STATUS_GRACEFUL_DISCONNECT; + } + else + status = STATUS_CONNECTION_RESET; + + CTEIoComplete(pIrpClose,status,0); + + } + + // + // return any rcv buffers that have been posted + // + CTESpinLock(pConnectEle,OldIrq); + + FreeRcvBuffers(pConnectEle,&OldIrq); + + CTESpinFree(pConnectEle,OldIrq); + + break; + + case NBT_DISCONNECTING: + // the retry session setup code expects the state to change + // to disconnected when the disconnect indication comes + // from the wire + pConnectEle->state = NBT_DISCONNECTED; + + case NBT_DISCONNECTED: + case NBT_ASSOCIATED: + case NBT_IDLE: + + // + // catch all other cases here to be sure the connect irp gets + // returned. + // + if (pIrp) + { + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + } + break; + + default: + ASSERTMSG("Nbt:Disconnect indication in unexpected state\n",0); + + } + + if (InsertOnList) + { + // undo the reference done when the NbtConnect Ran - this may cause + // pConnEle to be deleted if the Client had issued an NtClose before + // this routine ran. We only do this dereference if InsertOnList is + // TRUE, meaning that we just "unhooked" the lower from the Upper. + PUSH_LOCATION(0x65); + DereferenceIfNotInRcvHandler(pConnectEle,pLowerConn); + } + + + // this either puts the lower connection back on its free + // queue if inbound, or closes the connection with the transport + // if out bound. (it can't be done at dispatch level). + // + if (CleanupLower) + { + PUSH_LOCATION(0x6B); + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Calling Worker thread to Cleanup Disconnect %X\n",pLowerConn)); + + CTESpinLock(pLowerConn,OldIrq); + + if ( pLowerConn->pIrp ) + { + PCTE_IRP pIrp; + + pIrp = pLowerConn->pIrp; + CHECK_PTR(pLowerConn); + pLowerConn->pIrp = NULL ; + + CTESpinFree(pLowerConn,OldIrq); + // this is the irp to complete when the disconnect completes - essentially + // the irp requesting the disconnect. + CTEIoComplete( pIrp, STATUS_SUCCESS, 0 ) ; + } + else + CTESpinFree(pLowerConn,OldIrq); + +#if !defined(VXD) && DBG + if ((pLowerConn->Verify != NBT_VERIFY_LOWERCONN ) || + (pLowerConn->RefCount == 1)) + { + DbgBreakPoint(); + } +#endif + + status = CTEQueueForNonDispProcessing( + NULL, + pLowerConn, + NULL, + CleanupAfterDisconnect, + pLowerConn->pDeviceContext); + } + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +VOID +CleanupAfterDisconnect( + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles freeing lowerconnection data structures back to the + transport, by calling NTclose (outbound only) or by putting the connection + back on the connection free queue (inbound only). For the NT case this + routine runs within the context of an excutive worker thread. + +Arguments: + + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + CTELockHandle OldIrq; + NTSTATUS status; + tLOWERCONNECTION *pLowerConn; + tDEVICECONTEXT *pDeviceContext; + PIRP pIrp=NULL; + + PUSH_LOCATION(0x67); + pLowerConn = (tLOWERCONNECTION*)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:CleanupDisconnect Orig= %X, pLowerConn=%X\n", + pLowerConn->bOriginator,pLowerConn)); + + // + // DEBUG to catch upper connections being put on lower conn QUEUE + // +#if !defined(VXD) && DBG + if ((pLowerConn->Verify != NBT_VERIFY_LOWERCONN ) || + (pLowerConn->RefCount == 1)) + { + DbgBreakPoint(); + } +#endif + + ASSERT(pLowerConn->pUpperConnection == NULL); + // + // Inbound lower connections just get put back on the queue, whereas + // outbound connections get closed. + // + // Connections allocated due to SynAttack backlog measures are not re-allocated + // + if (!(pLowerConn->bOriginator || pLowerConn->SpecialAlloc)) + { + // ******** INCOMING ************* + pDeviceContext = pLowerConn->pDeviceContext; + // + // if Dhcp says the lease on the Ip address has gone , then this + // boolean is set in NbtNewDhcpAddress, to tell this code to close + // the connection. + // + if (pLowerConn->DestroyConnection) + { + status = NbtDeleteLowerConn(pLowerConn); + } + else + { + + // + // Always close the connection and then Create another since there + // could be a Rcv Irp in TCP still that will be returned at some + // later time, perhaps after this connection gets reused again. + // In that case the Rcv Irp could be lost. + + PUSH_LOCATION(0x68); + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:CleanupDisconnect Lower Conn State = %X %X\n", + pLowerConn->State,pLowerConn)); + + ASSERT(pLowerConn->RefCount >= 2); + NbtDereferenceLowerConnection(pLowerConn); + + status = NbtDeleteLowerConn(pLowerConn); + CHECK_PTR(pLowerConn); + + AllocLowerConn(pDeviceContext, FALSE); + + } + } + else + { + // ******** OUTGOING ************* + + PUSH_LOCATION(0x6B); + // this deref removes the reference added when the connection + // connnected. When NbtDeleteLowerConn is called it dereferences + // one more time which delete the memory. + // + NbtDereferenceLowerConnection(pLowerConn); + CHECK_PTR(pLowerConn); + + // this does a close on the lower connection, so it can go ahead + // possibly before the disconnect has completed since the transport + // will not complete the close until is completes the disconnect. + // + status = NbtDeleteLowerConn(pLowerConn); + + // + // If this was a special connection block, decrement the count of such connections + // + if (pLowerConn->SpecialAlloc) { + InterlockedDecrement(&NbtConfig.NumSpecialLowerConn); +#if DBG + InterlockedDecrement(&NbtConfig.ActualNumSpecialLowerConn); +#endif +#ifndef VXD + KdPrint(("Nbt:CleanupDisconnect Special Lower Conn;NumSpecialLowerConn= %d, Actual: %d\n", + NbtConfig.NumSpecialLowerConn, NbtConfig.ActualNumSpecialLowerConn)); +#endif + } + } + CTEMemFree(pContext); +} + +//---------------------------------------------------------------------------- +VOID +AllocLowerConn( + IN tDEVICECONTEXT *pDeviceContext, + IN BOOLEAN fSpecial + ) +/*++ + +Routine Description: + + Allocate a lowerconn block that will go on the lowerconnfreehead. + +Arguments: + + pDeviceContext - the device context + +Return Value: + + +--*/ +{ + NTSTATUS status; + tLOWERCONNECTION *pLowerConn; + + + pLowerConn = (tLOWERCONNECTION *)NbtAllocMem(sizeof(tLOWERCONNECTION),NBT_TAG('3')); + + if (pLowerConn) + { + status = NbtOpenAndAssocConnection(pLowerConn,pDeviceContext); + if (!NT_SUCCESS(status)) + { + CTEMemFree(pLowerConn); + pLowerConn = NULL; + } else if (fSpecial) { + // + // Special lowerconn for Syn attacks + // + pLowerConn->SpecialAlloc = TRUE; + } + } + + // + // if malloc failed or if NbtOpenAndAsso... failed, schedule an event + // + if (!pLowerConn) + { + CTEQueueForNonDispProcessing( + NULL, + pDeviceContext, + NULL, + DelayedAllocLowerConn, + pDeviceContext); + } + +} +//---------------------------------------------------------------------------- +VOID +DelayedAllocLowerConn( + IN PVOID pContext + ) +/*++ + +Routine Description: + + If lowerconn couldn't be alloced in AllocLowerConn, an event is scheduled + so that we can retry later. Well, this is "later"! + +Arguments: + + + +Return Value: + + +--*/ +{ + tDEVICECONTEXT *pDeviceContext; + + + pDeviceContext = (tDEVICECONTEXT *)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + + ASSERT( pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + AllocLowerConn(pDeviceContext, FALSE); + + CTEMemFree(pContext); +} + +//---------------------------------------------------------------------------- +VOID +DelayedAllocLowerConnSpecial( + IN PVOID pContext + ) +/*++ + +Routine Description: + + If lowerconn couldn't be alloced in AllocLowerConn, an event is scheduled + so that we can retry later. Well, this is "later"! + + This is for SYN-ATTACK, so we shd create more than one to beat the incoming + requests. Create three at a time - this shd be controllable thru' registry. + +Arguments: + + + +Return Value: + + +--*/ +{ + tDEVICECONTEXT *pDeviceContext; + ULONG i; + + KdPrint(("Allocing spl. %d lowerconn...\n", NbtConfig.SpecialConnIncrement)); + + pDeviceContext = (tDEVICECONTEXT *)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + + ASSERT( pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + // + // Alloc SpecialConnIncrement number of more connections. + // + for (i=0; i<NbtConfig.SpecialConnIncrement; i++) { +#if DBG + InterlockedIncrement(&NbtConfig.ActualNumSpecialLowerConn); +#endif + AllocLowerConn(pDeviceContext, TRUE); + } + + InterlockedDecrement(&NbtConfig.NumQueuedForAlloc); + + CTEMemFree(pContext); +} + +//---------------------------------------------------------------------------- +VOID +AddToRemoteHashTbl ( + IN tDGRAMHDR UNALIGNED *pDgram, + IN ULONG BytesIndicated, + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine adds the source address of an inbound datagram to the remote + hash table so that it can be used for subsequent return sends to that node. + + This routine does not need to be called if the datagram message type is + Broadcast datagram, since these are sends to the broadcast name '*' and + there is no send caching this source name + +Arguments: + + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq; + UCHAR pName[NETBIOS_NAME_SIZE]; + NTSTATUS status; + LONG Length; + ULONG SrcIpAddr; + enum eNbtAddrType AddrType; + PUCHAR pScope; + + // find the src name in the header. + status = ConvertToAscii( + (PCHAR)&pDgram->SrcName.NameLength, + (ULONG)((PCHAR)BytesIndicated - FIELD_OFFSET(tDGRAMHDR,SrcName.NameLength)), + pName, + &pScope, + &Length); + + if (!NT_SUCCESS(status)) + { + return; + } + + SrcIpAddr = ntohl(pDgram->SrcIpAddr); + // + // source ip addr should never be 0. This is a workaround for UB's NBDD + // which forwards the datagram, but client puts 0 in SourceIpAddr field + // of the datagram, we cache 0 and then end up doing a broadcast when we + // really meant to do a directed datagram to the sender. + // + if (!SrcIpAddr) + return; + + // always a unique address since you can't send from a group name + // + AddrType = NBT_UNIQUE; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + // + // Add the name to the remote cache. + // + status = AddNotFoundToHashTable(NbtConfig.pRemoteHashTbl, + pName, + pScope, + SrcIpAddr, + AddrType, + &pNameAddr); + + if (NT_SUCCESS(status)) + { + // + // we only want the name to be in the remote cache for the shortest + // timeout allowed by the remote cache timer, so set the timeout + // count to 1 which is 1-2 minutes. + // + + // the name is already in the cache when Pending is returned, + // so just update the ip address in case it is different. + // + if (status == STATUS_PENDING) + { + // + // If the name is resolved then it is ok to overwrite the + // ip address with the incoming one. But if it is resolving, + // then just let it continue resolving. + // + if ( (pNameAddr->NameTypeState & STATE_RESOLVED) && + !(pNameAddr->NameTypeState & NAMETYPE_INET_GROUP)) + { + pNameAddr->IpAddress = SrcIpAddr; + pNameAddr->TimeOutCount = 1; + // only set the adapter mask for this adapter since we are + // only sure that this adapter can reach the dest. + pNameAddr->AdapterMask = pDeviceContext->AdapterNumber; + } + } + else + { + pNameAddr->TimeOutCount = 1; + // + // change the state to resolved + // + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_RESOLVED; + pNameAddr->AdapterMask |= pDeviceContext->AdapterNumber; + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +DgramHndlrNotOs ( + IN PVOID ReceiveEventContext, + IN ULONG SourceAddrLength, + IN PVOID pSourceAddr, + IN ULONG OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG pBytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppRcvBuffer, + IN tCLIENTLIST **ppClientList + ) +/*++ + +Routine Description: + + This routine is the receive datagram event indication handler. + + It is called when an a datgram arrives from the network. The code + checks the type of datagram and then tries to route the datagram to + the correct destination on the node. + + This procedure is called with the spin lock held on pDeviceContext. + +Arguments: + + ppRcvbuffer will contain the IRP/NCB if only one client is listening, + NULL if multiple clients are listening + ppClientList will contain the list clients that need to be completed, + NULL if only one client is listening + + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + NTSTATUS status; + NTSTATUS LocStatus; + tCLIENTELE *pClientEle; + tNAMEADDR *pNameAddr; + tADDRESSELE *pAddress; + USHORT RetNameType; + CTELockHandle OldIrq; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + ULONG uLength; + int iLength; + ULONG RemoteAddressLength; + PVOID pRemoteAddress; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)ReceiveEventContext; + tDGRAMHDR UNALIGNED *pDgram = (tDGRAMHDR UNALIGNED *)pTsdu; + ULONG lClientBytesTaken; + ULONG lDgramHdrSize; + PIRP pIrp; + BOOLEAN MoreClients; + BOOLEAN UsingClientBuffer; + CTEULONGLONG AdapterNumber; + ULONG BytesIndicatedOrig; + ULONG BytesAvailableOrig; + + // + // Check a few things first + // + if (BytesIndicated < 11) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + status = STATUS_DATA_NOT_ACCEPTED; + + // Check Normal DataGrams + // + if ((pDgram->MsgType >= DIRECT_UNIQUE) && + (pDgram->MsgType <= BROADCAST_DGRAM)) + { + ULONG Offset; + + // there must be at least the header plus two half ascii names etc. + // which all adds up to 82 bytes with no user data + // + if (BytesIndicated < 82 ) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // find the end of the SourceName .. it ends in a 0 byte so use + // strlen + iLength = strlen((PCHAR)pDgram->SrcName.NetBiosName); + + if (iLength <= 0) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // find the destination name in the local name service tables + // + Offset = iLength + FIELD_OFFSET(tDGRAMHDR,SrcName.NetBiosName[0]); + LocStatus = ConvertToAscii( + (PCHAR)&pDgram->SrcName.NetBiosName[iLength+1], + BytesIndicated-Offset, + pName, + &pScope, + &uLength); + + if (!NT_SUCCESS(LocStatus)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // check length again, including scopes of names too. The Src + // scope length is returned in uLength, which also includes the + // half ascii length and the length byte length + // + if (BytesIndicated < (82 + NbtConfig.ScopeLength -1 + + (uLength -2*NETBIOS_NAME_SIZE -1 ))) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // + // Check for the full name first instead of considering any name with a '*' as + // the first char as a broadcast name (e.g. *SMBSERVER and *SMBDATAGRAM are not + // b'cast names). + // + pNameAddr = (tNAMEADDR *)FindName( + NBT_LOCAL, + pName, + pScope, + &RetNameType); + + // + // If we failed above, it might be because the name could start with '*' and is a + // bcast name. + // + if (!pNameAddr) { + // + // be sure the broadcast name has 15 zeroes after it + // + if (pName[0] == '*') + { + CTEZeroMemory(&pName[1],NETBIOS_NAME_SIZE-1); + pNameAddr = (tNAMEADDR *)FindName( + NBT_LOCAL, + pName, + pScope, + &RetNameType); + } + } + + // Change the pTsdu ptr to pt to the users data + // -2 to account for the tNETBIOSNAME and +3 to add the length + // bytes for both names, plus the null on the end of the first + // name + + lDgramHdrSize = sizeof(tDGRAMHDR) - 2 + 3+ iLength + uLength; + pTsdu = (PVOID)((PUCHAR)pTsdu + lDgramHdrSize); + BytesAvailableOrig = BytesAvailable; + BytesAvailable -= lDgramHdrSize; + BytesIndicatedOrig = BytesIndicated; + BytesIndicated -= lDgramHdrSize; + + // + // If the name is in the local table and has an address element + // associated with it AND the name is registered against + // this adapter, then execute the code in the 'if' block + // + AdapterNumber = pDeviceContext->AdapterNumber; + if ((pNameAddr) && (pNameAddr->pAddressEle) && + ( AdapterNumber & pNameAddr->AdapterMask)) + { + pAddress = pNameAddr->pAddressEle; + + // + // Increment the reference count to prevent the + // pAddress from disappearing in the window between freeing + // the JOINT_LOCK and taking the ADDRESS_LOCK. We also need to + // keep the refcount if we are doing a multi client recv, since + // Clientlist access pAddressEle when distributing the rcv'd dgram + // in CompletionRcvDgram. + // + CTEInterlockedIncrementLong(&pAddress->RefCount); + + if (!IsListEmpty(&pAddress->ClientHead)) + { + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + *pBytesTaken = lDgramHdrSize; + + // + // Check if there is more than one client that should receive this + // datagram. If so then pass down a new buffer to get it and + // copy it to each client's buffer in the completion routine. + // + + *ppRcvBuffer = NULL; + MoreClients = FALSE; + *ppClientList = NULL; + + pHead = &pAddress->ClientHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + PTDI_IND_RECEIVE_DATAGRAM EvRcvDgram; + PVOID RcvDgramEvContext; + PLIST_ENTRY pRcvEntry; + tRCVELE *pRcvEle; + ULONG MaxLength; + PLIST_ENTRY pSaveEntry; + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + // this client must be registered against this adapter to + // get the data + // + if (!(pClientEle->pDeviceContext->AdapterNumber & AdapterNumber)) + { + pEntry = pEntry->Flink; + continue; + } + +#ifdef VXD + // + // Move all of the RcvAnyFromAny Datagrams to this client's + // RcvDatagram list so they will be processed along with the + // outstanding datagrams for this client if this isn't a + // broadcast reception (RcvAnyFromAny dgrams + // don't receive broadcasts). The first client will + // empty the list, which is ok. + // + if ( *pName != '*' ) + { + PLIST_ENTRY pDGEntry ; + while ( !IsListEmpty( &pDeviceContext->RcvDGAnyFromAnyHead )) + { + pDGEntry = RemoveHeadList(&pDeviceContext->RcvDGAnyFromAnyHead) ; + InsertTailList( &pClientEle->RcvDgramHead, pDGEntry ) ; + } + } +#endif + + // check for datagrams posted to this name, and if not call + // the recv event handler. NOTE: this assumes that the clients + // use posted recv buffer OR and event handler, but NOT BOTH. + // If two clients open the same name, one with a posted rcv + // buffer and another with an event handler, the one with the + // event handler will NOT get the datagram! + // + if (!IsListEmpty(&pClientEle->RcvDgramHead)) + { + MaxLength = 0; + pSaveEntry = pEntry; + // + // go through all clients finding one that has a large + // enough buffer + // + while (pEntry != pHead) + { + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + if (IsListEmpty(&pClientEle->RcvDgramHead)) + { + continue; + } + + pRcvEntry = pClientEle->RcvDgramHead.Flink; + pRcvEle = CONTAINING_RECORD(pRcvEntry,tRCVELE,Linkage); + + if (pRcvEle->RcvLength >= BytesAvailable) + { + pSaveEntry = pEntry; + break; + } + else + { + // keep the maximum rcv length around incase none + // is large enough + // + if (pRcvEle->RcvLength > MaxLength) + { + pSaveEntry = pEntry; + MaxLength = pRcvEle->RcvLength; + } + + pEntry = pEntry->Flink; + } + + } + + // + // Get the buffer off the list + // + pClientEle = CONTAINING_RECORD(pSaveEntry,tCLIENTELE,Linkage); + + pRcvEntry = RemoveHeadList(&pClientEle->RcvDgramHead); + + *ppRcvBuffer = pRcvEle->pIrp; +#ifdef VXD + ASSERT( pDgram->SrcName.NameLength <= NETBIOS_NAME_SIZE*2) ; + LocStatus = ConvertToAscii( + (PCHAR)&pDgram->SrcName, + pDgram->SrcName.NameLength+1, + ((NCB*)*ppRcvBuffer)->ncb_callname, + &pScope, + &uLength); + + if ( !NT_SUCCESS(LocStatus) ) + { + DbgPrint("ConvertToAscii failed\r\n") ; + } +#else //VXD + + // + // put the source of the datagram into the return + // connection info structure. + // + if (pRcvEle->ReturnedInfo) + { + UCHAR pSrcName[NETBIOS_NAME_SIZE]; + + Offset = FIELD_OFFSET(tDGRAMHDR,SrcName.NetBiosName[0]); + LocStatus = ConvertToAscii( + (PCHAR)&pDgram->SrcName, + BytesIndicatedOrig-Offset, + pSrcName, + &pScope, + &uLength); + + if (pRcvEle->ReturnedInfo->RemoteAddressLength >= + sizeof(TA_NETBIOS_ADDRESS)) + { + TdiBuildNetbiosAddress(pSrcName, + FALSE, + pRcvEle->ReturnedInfo->RemoteAddress); + } + + } +#endif + +#ifndef VXD + // + // Null out the cancel routine since we are passing the + // irp to the transport + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(pRcvEle->pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq); +#endif + CTEMemFree((PVOID)pRcvEle); + + if (pAddress->MultiClients) + { + // the multihomed host always passes the above test + // so we need a more discerning test for it. + if (!NbtConfig.MultiHomed) + { + // if the list is more than one on it, + // then there are several clients waiting + // to receive this datagram, so pass down a buffer to + // get it. + // + MoreClients = TRUE; + status = STATUS_SUCCESS; + + UsingClientBuffer = TRUE; + + // this break will jump down below where we check if + // MoreClients = TRUE + break; + } + else + { + + } + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + + NbtDereferenceAddress(pAddress); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + status = STATUS_SUCCESS; + + // + // jump to end of while to check if we need to buffer + // the datagram source address + // in the remote hash table + // + break; + } +#ifdef VXD + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NbtDereferenceAddress(pAddress); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + break; + } +#endif + + +#ifndef VXD + EvRcvDgram = pClientEle->evRcvDgram; + RcvDgramEvContext = pClientEle->RcvDgramEvContext; + + // don't want to call the default handler since it just + // returns data not accepted + if (pClientEle->evRcvDgram != TdiDefaultRcvDatagramHandler) + { + // finally found a real event handler set by a client + + if (pAddress->MultiClients) +// if (pEntry->Flink != pHead) + { + // if the next element in the list is not the head + // of the list then there are several clients waiting + // to receive this datagram, so pass down a buffer to + // get it. + // + MoreClients = TRUE; + UsingClientBuffer = FALSE; + status = STATUS_SUCCESS; + + break; + + } + + // + // make up an address datastructure - subtracting the + // number of bytes skipped from the total length so + // convert to Ascii can not bug chk on bogus names. + // + { + ULONG NumAddrs; + + if (pClientEle->ExtendedAddress) { + NumAddrs = 2; + } else { + NumAddrs = 1; + } + + LocStatus = MakeRemoteAddressStructure( + (PCHAR)&pDgram->SrcName.NameLength, + pSourceAddr, + BytesIndicatedOrig - FIELD_OFFSET(tDGRAMHDR,SrcName.NameLength), + &pRemoteAddress, // the end of the pdu. + &RemoteAddressLength, + NumAddrs); + } + pClientEle->RefCount++; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (!NT_SUCCESS(LocStatus)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + pIrp = NULL; + lClientBytesTaken = 0; + LocStatus = (*EvRcvDgram)(RcvDgramEvContext, + RemoteAddressLength, + pRemoteAddress, + OptionsLength, + pOptions, + ReceiveDatagramFlags, + BytesIndicated, + BytesAvailable, + &lClientBytesTaken, + pTsdu, + &pIrp); + + + CTEMemFree((PVOID)pRemoteAddress); + + NbtDereferenceClient(pClientEle); + + NbtDereferenceAddress(pAddress); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (!pIrp) + { + status = STATUS_DATA_NOT_ACCEPTED; + } + else + { + + // the client has passed back an irp so pass it + // on the transport + + *pBytesTaken += lClientBytesTaken; + *ppRcvBuffer = pIrp; + + status = STATUS_SUCCESS; + break; + } + + } + +#endif //!VXD + + // go to the next client in the list + pEntry = pEntry->Flink; + + }// of While + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Cache the source address in the remote hash table so that + // this node can send back to the source even if the name + // is not yet in the name server yet. (only if not on the + // same subnet) + // + if ((pDgram->MsgType != BROADCAST_DGRAM)) + { + ULONG SrcAddress; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SubnetMask; + + pSourceAddress = (PTRANSPORT_ADDRESS)pSourceAddr; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + SubnetMask = pDeviceContext->SubnetMask; + // + // - cache only if from off the subnet + // - cache if not sent to 1E,1D,01 name and not from ourselves + // + // don't cache dgrams from ourselves, or datagrams to the + // 1E name, 1D, or 01. + // + if (((SrcAddress & SubnetMask) != + (pDeviceContext->IpAddress & SubnetMask)) + || + ( + (pName[NETBIOS_NAME_SIZE-1] != 0x1E) && + (pName[NETBIOS_NAME_SIZE-1] != 0x1D) && + (pName[NETBIOS_NAME_SIZE-1] != 0x01) && + (!SrcIsUs(SrcAddress)))) + { + AddToRemoteHashTbl(pDgram,BytesIndicatedOrig,pDeviceContext); + } + } + + // alloc a block of memory to track where we are in the list + // of clients so completionrcvdgram can send the dgram to the + // other clients too. + // + if (MoreClients) + { + tCLIENTLIST *pClientList; + + pClientList = (tCLIENTLIST *)NbtAllocMem(sizeof(tCLIENTLIST),NBT_TAG('4')); + if (pClientList) + { + // + // Set fProxy field to FALSE since the client list is for + // real as versus the PROXY case + // + pClientList->fProxy = FALSE; + + // save some context information so we can pass the + // datagram to the clients - none of the clients have + // recvd the datagram yet. + // + *ppClientList = (PVOID)pClientList; + pClientList->pAddress = pAddress; + pClientList->pClientEle = pClientEle; // used for VXD case + pClientList->fUsingClientBuffer = UsingClientBuffer; + pClientList->ReceiveDatagramFlags = ReceiveDatagramFlags; + + // make up an address datastructure + LocStatus = MakeRemoteAddressStructure( + (PCHAR)&pDgram->SrcName.NameLength, + 0, + BytesIndicatedOrig -FIELD_OFFSET(tDGRAMHDR,SrcName.NameLength),// set a max number of bytes so we don't go beyond + &pRemoteAddress, // the end of the pdu. + &RemoteAddressLength, + 1); + if (NT_SUCCESS(LocStatus)) + { + pClientList->pRemoteAddress = pRemoteAddress; + pClientList->RemoteAddressLength = RemoteAddressLength; + return(STATUS_SUCCESS); + } + else + { + *ppClientList = NULL; + CTEMemFree(pClientList); + status = STATUS_DATA_NOT_ACCEPTED; + + } + } + else + status = STATUS_DATA_NOT_ACCEPTED; + } + + } + else + { + pAddress->RefCount--; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_DATA_NOT_ACCEPTED; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:No client attached to the Address %16.16s<%X>\n", + pAddress->pNameAddr->Name,pAddress->pNameAddr->Name[15])); + } + + } + else + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_DATA_NOT_ACCEPTED; + } + +#ifdef PROXY_NODE + IF_PROXY(NodeType) + { + ULONG SrcAddress; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SubnetMask; + + pSourceAddress = (PTRANSPORT_ADDRESS)pSourceAddr; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + + // + // check name in the remote name table. If it is there, it is + // an internet group and is in the resolved state, send the + // datagram to all the members except self. If it is in the + // resolving state, just return. The fact that we got a + // datagram send for an internet group name still in the + // resolving state indicates that there is a DC on the subnet + // that responded to the query for the group received + // earlier. This means that the DC will respond (unless it + // goes down) to this datagram send. If the DC is down, the + // client node will retry. + // + // Futures: Queue the Datagram if the name is in the resolving + // state. + // + // If Flags are zero then it is a non fragmented Bnode send. There + // is not point in doing datagram distribution for P,M,or H nodes + // can they can do their own. + // + if (((pDgram->Flags & SOURCE_NODE_MASK) == 0) && + (pName[0] != '*') && + (!SrcIsUs(SrcAddress))) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + pNameAddr = (tNAMEADDR *)FindName( + NBT_REMOTE, + pName, + pScope, + &RetNameType + ); + + if (pNameAddr) + { + // + // We have the name in the RESOLVED state. + // + // + // If the name is an internet group, do datagram distribution + // function + // Make sure we don't distribute a datagram that has been + // sent to us by another proxy. In other words, distribute + // the datagram only if we got it first-hand from original node + // + if ( (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) && + ((((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr) == pDgram->SrcIpAddr) ) + { + // + // If BytesAvailable != BYtesIndicated, it means that + // that we don't have the entire datagram. We need to + // get it + if (BytesAvailableOrig != BytesIndicatedOrig) + { + tCLIENTLIST *pClientList; + + // + // Do some simulation to fake the caller of this fn + // (TdiRcvDatagramHndlr) into thinking that there are + // multiple clients. This will result in + // TdiRcvDatagramHndlr function getting all bytes + // available from TDI and calling + // ProxyDoDgramDist to do the datagram distribution + // + pClientList = + (tCLIENTLIST *)NbtAllocMem(sizeof(tCLIENTLIST),NBT_TAG('5')); + + if (pClientList) + { + // + // save some context information in the Client List + // data structure + // + *ppClientList = (PVOID)pClientList; + // + // Set fProxy field to TRUE since the client list + // not for real + // + pClientList->fProxy = TRUE; + + // + // Make use of the following fields to pass the + // information we would need in the + // CompletionRcvDgram + // + pClientList->pAddress = (tADDRESSELE *)pNameAddr; + pClientList->pRemoteAddress = pDeviceContext; + + // + // Increment the reference count so that this name + // does not disappear on us after we free the + // spin lock. DgramSendCleanupTracker called from + // SendDgramCompletion in Name.c will + // decrement the count + // + pNameAddr->RefCount++; + status = STATUS_DATA_NOT_ACCEPTED; + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } // end of if (we do not have the entire datagram) + else + { + // + // Increment the reference count so that this name + // does not disappear on us after we free the spin lock. + // + // DgramSendCleanupTracker will decrement the count + // + pNameAddr->RefCount++; + // + //We have the entire datagram. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + (VOID)ProxyDoDgramDist( + pDgram, + BytesIndicatedOrig, + pNameAddr, + pDeviceContext + ); + + status = STATUS_DATA_NOT_ACCEPTED; + } + + } // end of if (if name is an internet group name) + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } // end of if (Name is there in remote hash table) + else + { + tNAMEADDR *pResp; + + // + // the name is not in the cache, so try to get it from + // WINS + // + status = FindOnPendingList(pName,NULL,TRUE,NETBIOS_NAME_SIZE,&pResp); + if (!NT_SUCCESS(status)) + { + // + // cache the name and contact the name + // server to get the name to IP mapping + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = RegOrQueryFromNet( + FALSE, //means it is a name query + pDeviceContext, + NULL, + uLength, + pName, + pScope); + + } + else + { + // + // the name is on the pending list doing a name query + // now, so ignore this name query request + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + + status = STATUS_DATA_NOT_ACCEPTED; + } + } + } + END_PROXY +#endif + + } // end of a directed or broadcast datagram + else + { + if (pDgram->MsgType == ERROR_DGRAM) + { + KdPrint(("Nbt:ERROR Datagram received, Error Code = %X\n", + ((tDGRAMERROR *)pDgram)->ErrorCode)); + } + else + { + KdPrint(("Nbt:Dgram Rcvd, not expecting..., MsgType = %X\n", + pDgram->MsgType)); + + } + } + return(status); +} + +#ifdef PROXY_NODE +//---------------------------------------------------------------------------- +NTSTATUS +ProxyDoDgramDist( + IN tDGRAMHDR UNALIGNED *pDgram, + IN DWORD DgramLen, + IN tNAMEADDR *pNameAddr, + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + +Arguments: + + ppRcvbuffer will contain the IRP/NCB if only one client is listening, + NULL if multiple clients are listening + ppClientList will contain the list clients that need to be completed, + NULL if only one client is listening + +Return Value: + + NTSTATUS - Status of receive operation + +Called By: + + DgramHdlrNotOs, CompletionRcvDgram in tdihndlr.c + +--*/ +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + tDGRAMHDR *pMyBuff; + + // + // get a buffer for tracking Dgram Sends + // + pTracker = NbtAllocTracker(); + if (!pTracker) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + NbtDereferenceName(pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Allocate a buffer and copy the contents of the datagram received + // into it. We do this because SndDgram may not have finished by the + // time we return. + // + pMyBuff = (tDGRAMHDR *)NbtAllocMem(DgramLen,NBT_TAG('6')); + if ( !pMyBuff ) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + NbtDereferenceName(pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEFreeMem(pTracker) ; + return STATUS_INSUFFICIENT_RESOURCES ; + } + + CTEMemCopy(pMyBuff, (PUCHAR)pDgram, DgramLen); + + // + // fill in the tracker data block + // note that the passed in transport address must stay valid till this + // send completes + // + CHECK_PTR(pTracker); + pTracker->SendBuffer.pDgramHdr = (PVOID)pMyBuff; + pTracker->SendBuffer.HdrLength = DgramLen; + pTracker->SendBuffer.pBuffer = NULL; + pTracker->SendBuffer.Length = 0; + pTracker->pNameAddr = pNameAddr; + pTracker->pDeviceContext = (PVOID)pDeviceContext; + pTracker->p1CNameAddr = NULL; + // + // so DgramSendCleanupTracker does not decrement the bytes allocated + // to dgram sends, since we did not increment the count when we allocated + // the dgram buffer above. + // + pTracker->AllocatedLength = 0; + + pTracker->pClientIrp = NULL; + pTracker->pClientEle = NULL; + + KdPrint(("ProxyDoDgramDist: Name is %16.16s(%X)\n", pNameAddr->Name, + pNameAddr->Name[15])); + + // + // Send the datagram to each IP address in the Internet group + // + // + DatagramDistribution(pTracker,pNameAddr); + + return(STATUS_SUCCESS); +} +#endif + +//---------------------------------------------------------------------------- +NTSTATUS +NameSrvHndlrNotOs ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameSrv, + IN ULONG uNumBytes, + IN BOOLEAN fBroadcast + ) +/*++ + +Routine Description: + + This routine is the receive datagram event indication handler. + + It is called when an a datgram arrives from the network. The code + checks the type of datagram and then tries to route the datagram to + the correct destination on the node. + + This procedure is called with the spin lock held on pDeviceContext. + +Arguments: + + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + USHORT OpCodeFlags; + NTSTATUS status; + + // it appears that streams can pass a null data pointer some times + // and crash nbt...and zero length for the bytes + if (uNumBytes < sizeof(ULONG)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + OpCodeFlags = pNameSrv->OpCodeFlags; + + //Pnodes always ignore Broadcasts since they only talk to the NBNS unless + // this node is also a proxy + if ( ( ((NodeType) & PNODE)) && !((NodeType) & PROXY) ) + { + if (OpCodeFlags & FL_BROADCAST) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + } + + + // decide what type of name service packet it is by switching on the + // NM_Flags portion of the word + switch (OpCodeFlags & NM_FLAGS_MASK) + { + case OP_QUERY: + status = QueryFromNet( + pDeviceContext, + pSrcAddress, + pNameSrv, + uNumBytes, + OpCodeFlags, + fBroadcast); + break; + + case OP_REGISTRATION: + // + // we can get either a registration request or a response + // + // is this a request or a response? - if bit is set its a Response + + if (OpCodeFlags & OP_RESPONSE) + { + // then this is a response to a previous reg. request + status = RegResponseFromNet( + pDeviceContext, + pSrcAddress, + pNameSrv, + uNumBytes, + OpCodeFlags); + } + else + { + // + // check if someone else is trying to register a name + // owned by this node. Pnodes rely on the Name server to + // handle this...hence the check for Pnode + // + if (!(NodeType & PNODE)) + { + status = CheckRegistrationFromNet(pDeviceContext, + pSrcAddress, + pNameSrv, + uNumBytes); + } + } + break; + + case OP_RELEASE: + // + // handle other nodes releasing their names by deleting any + // cached info + // + status = NameReleaseFromNet( + pDeviceContext, + pSrcAddress, + pNameSrv, + uNumBytes); + break; + + case OP_WACK: + if (!(NodeType & BNODE)) + { + // the TTL in the WACK tells us to increase our timeout + // of the corresponding request, which means we must find + // the transaction + status = WackFromNet(pDeviceContext, + pSrcAddress, + pNameSrv, + uNumBytes); + } + break; + + case OP_REFRESH: + case OP_REFRESH_UB: + + break; + + default: + IF_DBG(NBT_DEBUG_HNDLRS) + KdPrint(("NBT: Unknown Name Service Pdu type OpFlags = %X\n", + OpCodeFlags)); + break; + + + } + + return(STATUS_DATA_NOT_ACCEPTED); + +} + +VOID +DoNothingComplete ( + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine is the completion routine for TdiDisconnect while we are + retrying connects. It does nothing. + + This is required because you can't have a NULL TDI completion routine. + +--*/ +{ + return ; +} diff --git a/private/ntos/nbt/nbt/inbound.c b/private/ntos/nbt/nbt/inbound.c new file mode 100644 index 000000000..6c0430d89 --- /dev/null +++ b/private/ntos/nbt/nbt/inbound.c @@ -0,0 +1,4118 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Inbound.c + +Abstract: + + + This file implements the inbound name service pdu handling. It handles + name queries from the network and Registration responses from the network. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "ctemacro.h" + +NTSTATUS +DecodeNodeStatusResponse( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN ULONG Length, + IN PUCHAR pName, + IN ULONG lNameSize, + IN ULONG SrcIpAddress + ); + +NTSTATUS +ExtractServerName( + IN tNODESTATUS UNALIGNED *pNodeStatus, + IN PVOID pClientContext, + IN ULONG IpAddress + ); + +NTSTATUS +SendNodeStatusResponse( + IN tNAMEHDR UNALIGNED *pInNameHdr, + IN ULONG Length, + IN PUCHAR pName, + IN ULONG lNameSize, + IN ULONG SrcIpAddress, + IN USHORT SrcPort, + IN tDEVICECONTEXT *pDeviceContext + ); + +NTSTATUS +UpdateNameState( + IN tADDSTRUCT UNALIGNED *pAddrStruct, + IN tNAMEADDR *pNameAddr, + IN ULONG Length, + IN tDEVICECONTEXT *pDeviceContext, + IN BOOLEAN SrcIsNameServer, + IN tDGRAM_SEND_TRACKING *Context, + IN CTELockHandle OldIrq1 + ); +NTSTATUS +ChkIfValidRsp( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN tNAMEADDR *pNameAddr + ); + +VOID +CopyNodeStatusResponse( + IN PVOID pContext + ); + +NTSTATUS +ChooseBestIpAddress( + IN tADDSTRUCT UNALIGNED *pAddrStruct, + IN ULONG Len, + IN tDEVICECONTEXT *pDeviceContext, + OUT tDGRAM_SEND_TRACKING *pTracker, + OUT PULONG pIpAddress, + IN BOOLEAN fTryAllAddr + ); + +USHORT +GetNbFlags( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN ULONG lNameSize + ); + +VOID +PrintHexString( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN ULONG lNumBytes + ); + +ULONG +MakeList( + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG CountAddrs, + IN tADDSTRUCT UNALIGNED *pAddrStruct, + IN PULONG AddrArray, + IN ULONG SizeOfAddrArray, + IN BOOLEAN IsSubnetMatch + ); + + +#if DBG +#define KdPrintHexString(pHdr,NumBytes) \ + PrintHexString(pHdr,NumBytes) +#else +#define KdPrintHexString(pHdr,NumBytes) +#endif + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, CopyNodeStatusResponse) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +QueryFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags, + IN BOOLEAN fBroadcast + ) +/*++ + +Routine Description: + + This routine handles both name query requests and responses. For Queries + it checks if the name is registered on this node. If this node is a proxy + it then forwards a name query onto the Name Server, adding the name to the + remote proxy cache... + +Arguments: + + +Return Value: + + NTSTATUS - success or not - failure means no response to net + +--*/ +{ + NTSTATUS status; + LONG lNameSize; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + tNAMEADDR *pResp; + tTIMERQENTRY *pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SrcAddress; + CTELockHandle OldIrq1; + tQUERYRESP UNALIGNED *pQuery; + USHORT SrcPort; + ULONG IpAddr; + + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + + + SrcPort = ntohs(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port); + + +#ifdef VXD + // + // is this a response from a DNS server? if yes then handle it + // appropriately + // + if (SrcPort == NBT_DNSSERVER_UDP_PORT) + { + USHORT TransactionId; + TransactionId = ntohs(pNameHdr->TransactId); + if ( TransactionId >= DIRECT_DNS_NAME_QUERY_BASE ) + { + ProcessDnsResponseDirect( pDeviceContext, + pSrcAddress, + pNameHdr, + lNumBytes, + OpCodeFlags ); + } + else + { + ProcessDnsResponse( pDeviceContext, + pSrcAddress, + pNameHdr, + lNumBytes, + OpCodeFlags ); + } + + return(STATUS_DATA_NOT_ACCEPTED); + } +#endif + + // + // check the pdu size for errors - be sure the name is long enough for + // the scope on this machine. + // + if (lNumBytes < (NBT_MINIMUM_QUERY + NbtConfig.ScopeLength - 1)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Name Query PDU TOO short = %X,Src= %X\n",lNumBytes,SrcAddress)); + return(STATUS_DATA_NOT_ACCEPTED); + } + + + // get the name out of the network pdu and pass to routine to check + // local table *TODO* this assumes just one name in the Query response... + status = ConvertToAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pName, + &pScope, + &lNameSize); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + + // check if this is a request or a response pdu + + // + // *** RESPONSE *** + // + if (OpCodeFlags & OP_RESPONSE) + { + + // + // check the pdu size for errors + // + if (lNumBytes < (NBT_MINIMUM_QUERYRESPONSE + NbtConfig.ScopeLength -1)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + if (!(OpCodeFlags & FL_AUTHORITY)) + { + // + // This is a redirect response telling us to go to another + // name server, which we do not support, so just return + // *TODO* + // + return(STATUS_DATA_NOT_ACCEPTED); + } + + + // + // check if this is a node status request, since is looks very similar + // to a name query, except that the NBSTAT field is 0x21 instead of + // 0x20 + // + pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + if ( ((PUCHAR)pQuery)[1] == QUEST_STATUS ) + { + status = DecodeNodeStatusResponse(pNameHdr, + lNumBytes, + pName, + lNameSize, + SrcAddress); + + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // call this routine to find the name, since it does not interpret the + // state of the name as does FindName() + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + status = FindOnPendingList(pName,pNameHdr,FALSE,NETBIOS_NAME_SIZE,&pResp); + + if (NT_SUCCESS(status)) + { + + pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + + // remove any timer block and call the completion routine + if ((pTimer = pResp->pTimer)) + { + USHORT Flags; + tDGRAM_SEND_TRACKING *pTracker; + + pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; + + // + // If this is not a response to a request sent by the PROXY code + // and + // MSNode && Error Response code && Src is NameServer && Currently + // resolving with the name server, then switch to broadcast + // ... or if it is a Pnode or Mnode and EnableLmHost or + // ResolveWithDns is on, then + // let the timer expire again, and try Lmhost. + // + if ( + #ifdef PROXY_NODE + !pResp->fProxyReq && + #endif + (IS_NEG_RESPONSE(OpCodeFlags))) + + { + + Flags = pTracker->Flags; + + if ((NodeType & (PNODE | MNODE | MSNODE)) && + (Flags & (NBT_NAME_SERVER | NBT_NAME_SERVER_BACKUP))) + { + // this should let MsnodeCompletion try the + // backup WINS or broadcast + // processing when the timer timesout. + // + pTimer->Retries = 1; + pTracker->Flags |= WINS_NEG_RESPONSE; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + } + else + { + tDEVICECONTEXT *pDevContext; + + // + // save the devicecontext since the call to StopTimer could + // free pTracker + // + pDevContext = pTracker->pDeviceContext; + // + // this routine puts the timer block back on the timer Q, and + // handles race conditions to cancel the timer when the timer + // is expiring. + // + status = StopTimer(pTimer,&pClientCompletion,&Context); + LOCATION(0x42); + // + // we need to synchronize removing the name from the list + // with MsNodeCompletion + // + if (pClientCompletion) + { + LOCATION(0x41); + // + // Remove from the pending list + RemoveEntryList(&pResp->Linkage); + InitializeListHead(&pResp->Linkage); + + CHECK_PTR(pResp); + pResp->pTimer = NULL; + + // check the name query response ret code to see if the name + // query succeeded or not. + if (IS_POS_RESPONSE(OpCodeFlags)) + { + BOOLEAN ResolvedByWins; + + LOCATION(0x40); + // + // Keep track of how many names are resolved by WINS and + // keep a list of names not resolved by WINS + // + if (!(ResolvedByWins = SrcIsNameServer(SrcAddress,SrcPort))) + { + SaveBcastNameResolved(pName); + } + + IncrementNameStats(NAME_QUERY_SUCCESS, + ResolvedByWins); + + #ifdef PROXY_NODE + // Set flag if the node queries is a PNODE + // + IF_PROXY(NodeType) + { + pResp->fPnode = + (pNameHdr->NameRR.NetBiosName[lNameSize+QUERY_NBFLAGS_OFFSET] + & NODE_TYPE_MASK) == PNODE_VAL_IN_PKT; + + IF_DBG(NBT_DEBUG_PROXY) + KdPrint(("QueryFromNet: POSITIVE RESPONSE to name query - %16.16s(%X)\n", pResp->Name, pResp->Name[15])); + } + + #endif + + pResp->AdapterMask |= pDevContext->AdapterNumber; + + IpAddr = ((tADDSTRUCT *)&pQuery->Flags)->IpAddr; + status = UpdateNameState((tADDSTRUCT *)&pQuery->Flags, + pResp, + lNumBytes -((ULONG)&pQuery->Flags - (ULONG)pNameHdr), + pDeviceContext, + SrcIsNameServer(SrcAddress,SrcPort), + (tDGRAM_SEND_TRACKING *)Context, + OldIrq1); + // + // since pResp can be freed in UpdateNameState do not + // access it here + // + pResp = NULL; + // status = STATUS_SUCCESS; + } + else // negative query response received + { + + LOCATION(0x3f); + // + // Release the name. It will get dereferenced by the + // cache timeout function (RemoteHashTimeout). + // + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_RELEASED; + // + // the proxy maintains a negative cache of names that + // do not exist in WINS. These are timed out by + // the remote hash timer just as the resolved names + // are timed out. + // + if (!(NodeType & PROXY)) + { + NbtDereferenceName(pResp); + } + else + { + // + // Add to cache as negative name entry + // + status = AddRecordToHashTable(pResp, + NbtConfig.pScope); + if (!NT_SUCCESS(status)) + { + // + // this could delete the name so do not reference + // after this point + // + NbtDereferenceName(pResp); + } + } + + status = STATUS_BAD_NETWORK_PATH; + } + + // + // Set the backup name server to be the main name server + // since we got a response from it. + // + if ( ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr + == (ULONG)(htonl(pDeviceContext->lBackupServer))) + { + // switching the backup and the primary nameservers in + // the config data structure since we got a name + // registration response from the backup + // + SwitchToBackup(pDeviceContext); + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + +// +// chicago only has 4k of stack (yes, it's the operating system of 1995) +// schedule an event to make this tcp connection later to reduce stack usage +// +#ifdef VXD + (void) CTEQueueForNonDispProcessing( + (tDGRAM_SEND_TRACKING *)Context, + (PVOID)status, + pClientCompletion, + DelayedSessEstablish, + pDeviceContext); +#else + // the completion routine has not run yet, so run it + // + + // + // If pending is returned, we have submitted the check-for-addr request to lmhsvc. + // + if (status != STATUS_PENDING) { + CompleteClientReq(pClientCompletion, + (tDGRAM_SEND_TRACKING *)Context, + status); + } +#endif + + return(STATUS_DATA_NOT_ACCEPTED); + } + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + } + else + if (!NbtConfig.MultiHomed) + { + + // + // it is possible for two multihomed machines connected on two subnets + // to respond on both subnets to a name query. Therefore the querying + // multihomed machine will ultimately receive two name query responses + // each with a different ip address and think that a name conflict has + // occurred when it hasn't. There is no way to detect this case + // so just disable the conflict detection code below. Conflicts will + // still be detected by WINS and by the node owning the name if someone + // else tries to get the name, but conflicts will no longer be detected + // by third parties that have the name in their cache. + // + // + // The name is not in the NameQueryPending list, so check the + // remote table. + // + + // + // This code implements a Conflict timer for name + // queries. Look at name query response, and then send + // name conflict demands to the subsequent responders. + // There is no timer involved, and this node will always respond + // negatively to name query responses sent to it, for names + // it has in its remote cache, if the timer has been stopped. + // ( meaning that one response has been successfully received ). + + + status = FindInHashTable(NbtConfig.pRemoteHashTbl, + pName, + pScope, + &pResp); + + // check the src IP address and compare it to the one in the + // remote hash table + // Since it is possible for the name server to send a response + // late, do not accidently respond to those as conflicts. + // Since a bcast query of a group name will generally result in + // multiple responses, each with a different address, ignore + // this case. + // Also, ignore if the name is a preloaded lmhosts entry (though + // can't think of an obvious case where we would receive a response + // when a name is preloaded!) + // + if (NT_SUCCESS(status) && + !(pResp->NameTypeState & PRELOADED) + && + (SrcAddress != pResp->IpAddress) + && + (pResp->NameTypeState & NAMETYPE_UNIQUE) + && + (pResp->NameTypeState & STATE_RESOLVED) + && + ((SrcAddress != pDeviceContext->lNameServerAddress) + && + (SrcAddress != pDeviceContext->lBackupServer))) + { + // + // a different node is responding to the name query + // so tell them to buzz off. + // + status = UdpSendResponse( + lNameSize, + pNameHdr, + pResp, + (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], + pDeviceContext, + CONFLICT_ERROR, + eNAME_REGISTRATION_RESPONSE, + OldIrq1); + + // + // remove the name from the remote cache so the next time + // we need to talk to it we do a name query + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // set the name to the released state so that multiple + // conflicting responses don't come in and decrement the + // reference count to zero - in the case where some other + // part of NBT is still using the name, that part of NBT + // should do the final decrement - i.e. a datagram send to this + // name. + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_RELEASED; + + // + // don't deref if someone is using it now... + // + if (pResp->RefCount == 1) + { + NbtDereferenceName(pResp); + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + + return(STATUS_DATA_NOT_ACCEPTED); + + } + else // *** REQUEST *** + { + NTSTATUS Locstatus; + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // call this routine + // to see if the name is in the local table. + // + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pResp); + + if (NT_SUCCESS(status) && + ((pResp->NameTypeState & STATE_RESOLVED) || + (pResp->NameTypeState & STATE_RESOLVING))) + { + // + // check if this is a node status request, since is looks very similar + // to a name query, except that the NBSTAT field is 0x21 instead of + // 0x20 + // + pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + if ( ((PUCHAR)pQuery)[1] == QUEST_STATUS ) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // + // Reply only if this was not broadcast to us. + // + if (!fBroadcast) { + Locstatus = SendNodeStatusResponse(pNameHdr, + lNumBytes, + pName, + lNameSize, + SrcAddress, + SrcPort, + pDeviceContext); + } else { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBT: Bcast nodestatus req.- dropped\n")); + } + } + else + { + // + // check if this message came from Us or it is not + // a broadcast since WINS on this machine could send it. + // Note: this check must be AFTER the check for a node status request + // since we can send node status requests to ourselves. + // + if (!SrcIsUs(SrcAddress) || + (!(OpCodeFlags & FL_BROADCAST) +#ifndef VXD + && pWinsInfo +#endif + ) ) + { + // + // build a positive name query response pdu + // + Locstatus = UdpSendResponse( + lNameSize, + pNameHdr, + pResp, + (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], + pDeviceContext, + 0, + eNAME_QUERY_RESPONSE, + OldIrq1); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + } + + return(STATUS_DATA_NOT_ACCEPTED); + } + else + // Build a negative response if this query was directed rather than + // broadcast (since we do not want to Nack all broadcasts!) + if ( !(OpCodeFlags & FL_BROADCAST) ) + { + + // check that it is not a node status request... + // + pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + if ( ((PUCHAR)pQuery)[1] == QUEST_NETBIOS ) + { + Locstatus = UdpSendResponse( + lNameSize, + pNameHdr, + NULL, + (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], + pDeviceContext, + 0, + eNAME_QUERY_RESPONSE, + OldIrq1); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return(STATUS_DATA_NOT_ACCEPTED); + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + +#ifdef PROXY_NODE + + // + // check if this message came from Us !! (and return if so) + // + if (SrcIsUs(SrcAddress)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + IF_PROXY(NodeType) + { + // check that it is not a node status request... + if (((PUCHAR)pQuery)[1] == QUEST_NETBIOS ) + { + // + // We have a broadcast name query request for a name that + // is not in our local name table. If we are a proxy we need + // to resolve the query if not already resolved and respond + // with the address(es) to the node that sent the query. + // + // Note: We will respond only if the address that the name + // resolves to is not on our subnet. For our own subnet addresses + // the node that has the address will respond. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + // + // call this routine which looks for names without regard + // to their state + // + status = FindInHashTable(NbtConfig.pRemoteHashTbl, + pName, + pScope, + &pResp); + + if (!NT_SUCCESS(status)) + { + status = FindOnPendingList(pName,pNameHdr,TRUE,NETBIOS_NAME_SIZE,&pResp); + if (!NT_SUCCESS(status)) + { + // + // cache the name and contact the name + // server to get the name to IP mapping + // + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + status = RegOrQueryFromNet( + FALSE, //means it is a name query + pDeviceContext, + pNameHdr, + lNameSize, + pName, + pScope); + + return(STATUS_DATA_NOT_ACCEPTED); + } + else + { + // + // the name is on the pending list doing a name query + // now, so ignore this name query request + // + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + + } + } + else + { + + // + // The name can be in the RESOLVED, RESOLVING or RELEASED + // state. + // + + + // + // If in the RELEASED state, its reference count has to be + // > 0 + // + //ASSERT(pResp->NameTypeState & (STATE_RESOLVED | STATE_RESOLVING) || (pResp->NameTypeState & STATE_RELEASED) && (pResp->RefCount > 0)); + + // + // Send a response only if the name is in the RESOLVED state + // + if (pResp->NameTypeState & STATE_RESOLVED) + { + + // + // The PROXY sends a response if the address of the + // node queries is not on the same subnet as the + // node doing the query (or as us). It also responds + // if the name is a group name or if it belongs to + // a Pnode. Note: In theory there is no reason to + // respond for group names since a member of the group + // on the subnet will respond with their address if they + // are alive - if they are B or M nodes - perhaps + // the fPnode bit is not set correctly for groups, so + // therefore always respond in case all the members + // are pnodes. + // + + // + // If we have multiple network addresses in the same + // broadcast area, then this test won't be sufficient + // + if ( + ((SrcAddress & pDeviceContext->SubnetMask) + != + (pResp->IpAddress & pDeviceContext->SubnetMask)) + || + (pResp->fPnode) + || + !(pResp->NameTypeState & NAMETYPE_UNIQUE) + ) + { + IF_DBG(NBT_DEBUG_PROXY) + KdPrint(("QueryFromNet: QUERY SATISFIED by PROXY CACHE -- name is %16.16s(%X); %s entry ; Address is (%d)\n", + pResp->Name,pResp->Name[15], (pResp->NameTypeState & NAMETYPE_UNIQUE) ? "UNIQUE" : "INET_GROUP", + pResp->IpAddress)); + // + //build a positive name query response pdu + // + // UdpSendQueryResponse frees the spin lock + // + status = UdpSendResponse( + lNameSize, + pNameHdr, + pResp, + (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], + pDeviceContext, + 0, + eNAME_QUERY_RESPONSE, + OldIrq1); + + return(STATUS_DATA_NOT_ACCEPTED); + } + } + else + { + IF_DBG(NBT_DEBUG_PROXY) + KdPrint(("QueryFromNet: REQUEST for Name %16.16s(%X) in %s state\n", pResp->Name, pResp->Name[15],( pResp->NameTypeState & STATE_RELEASED ? "RELEASED" : "RESOLVING"))); + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + } + } + + } // end of proxy code +#endif + + } // end of else (it is a name query request) + + return(STATUS_DATA_NOT_ACCEPTED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +RegResponseFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ) +/*++ + +Routine Description: + + This routine handles name registration responses from the net (i.e. from + the name server most of the time since a broadcast name registration passes + when there is no response. + +*** + The response could be from an NBT node when it notices that the name + registration is for a name that it has already claimed - i.e. the node + is sending a NAME_CONFLICT_DEMAND - in this case the Rcode in the PDU + will be CFT_ERR = 7. + +Arguments: + + +Return Value: + + NTSTATUS - success or not - failure means no response to net + +--*/ +{ + NTSTATUS status; + ULONG lNameSize; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + tNAMEADDR *pResp; //Get rid of this later. Use pNameAddr + tTIMERQENTRY *pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + PTRANSPORT_ADDRESS pSourceAddress; + CTELockHandle OldIrq1; + ULONG SrcAddress; + SHORT SrcPort; + + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + SrcPort = ntohs(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port); + // + // be sure the Pdu is at least a minimum size + // + if (lNumBytes < (NBT_MINIMUM_REGRESPONSE + NbtConfig.ScopeLength -1)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Registration Response TOO short = %X, Src = %X\n", + lNumBytes,SrcAddress)); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("%.*X\n",lNumBytes/sizeof(ULONG),pNameHdr)); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // if Wins is locally attached then we will get registrations from + // ourselves!! + // + if (SrcIsUs(SrcAddress) +#ifndef VXD + && !pWinsInfo +#endif + ) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // get the name out of the network pdu and pass to routine to check + // local table *TODO* this assumes just one name in the Query response... + // We need to handle group lists from the WINS server + status = ConvertToAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pName, + &pScope, + &lNameSize); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pResp); + if (NT_SUCCESS(status) && + (pResp->AdapterMask & pDeviceContext->AdapterNumber)) + { + NTSTATUS Localstatus; + + // + // check the state of the name since this could be a registration + // response or a name conflict demand + // + switch (pResp->NameTypeState & NAME_STATE_MASK) + { + + case STATE_RESOLVING: + case STATE_RESOLVED: + + if (IS_POS_RESPONSE(OpCodeFlags)) + { + if (OpCodeFlags & FL_RECURAVAIL) + { + // turn on the refreshed bit in NextRefresh now + // (when the timer completion routine is called) + // only count names registered, not REFRESHES too! + // + if (pResp->NameTypeState & STATE_RESOLVING) + { + IncrementNameStats(NAME_REGISTRATION_SUCCESS, + SrcIsNameServer(SrcAddress,SrcPort)); + } + status = STATUS_SUCCESS; + } + else + { + // + // in this case the name server is telling this node + // to do an end node challenge on the name. However + // this node does not have the code to do a challenge + // so assume that this is a positive registration + // response. + // + status = STATUS_SUCCESS; + } + } + else + if ((OpCodeFlags & FL_RCODE) >= FL_NAME_ACTIVE) + { + // if we are multihomed, then we only allow the name server + // to send Name Active errors, since in normal operation this node + // could generate two different IP address for the same name + // query and confuse another client node into sending a Name + // Conflict. So jump out if a name conflict has been received + // from another node. + // + if ((NbtConfig.MultiHomed) && + ((OpCodeFlags & FL_RCODE) == FL_NAME_CONFLICT)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + break; + } + status = STATUS_DUPLICATE_NAME; + + + // + // if the name is resolved and we get a negative response + // then mark the name as in the conflict state so it can't + // be used for any new sessions and this node will no longer + // defend it. + // + if (pResp->NameTypeState & STATE_RESOLVED) + { + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_CONFLICT; + NbtLogEvent(EVENT_NBT_DUPLICATE_NAME,SrcAddress); + } + + } + else + { + // + // we got some kind of WINS server failure ret code + // so just ignore it and assume the name registration + // succeeded. + // + status = STATUS_SUCCESS; + } + + // remove any timer block and call the completion routine + // if the name is in the Resolving state only + // + LOCATION(0x40); + if ((pTimer = pResp->pTimer)) + { + tDGRAM_SEND_TRACKING *pTracker; + USHORT SendTransactId; + tDEVICECONTEXT *pDevContext; + + // check the transaction id to be sure it is the same as the one + // sent. + // + LOCATION(0x41); + pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; + SendTransactId = pTracker->TransactionId; + pDevContext = pTracker->pDeviceContext; + + if (pNameHdr->TransactId != SendTransactId) + { + LOCATION(0x42); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return(STATUS_DATA_NOT_ACCEPTED); + } + LOCATION(0x43); + + CHECK_PTR(pResp); + pResp->pTimer = NULL; + // + // This could be either a Refresh or a name registration. In + // either case, stop the timer and call the completion routine + // for the client.(below). + // + Localstatus = StopTimer(pTimer,&pClientCompletion,&Context); + + + // check if it is a response from the name server + // and a M, or P or MS node, since we will need to send + // refreshes to the name server for these node types + // + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + + // only accept pdus from the name server to change the Ttl. + // The first check passes the case where WINS is on this machine + // + if ( +#ifndef VXD + (pWinsInfo && + (SrcAddress == pWinsInfo->pDeviceContext->IpAddress)) || +#endif + (SrcAddress == pDeviceContext->lNameServerAddress)) + { + if (!(NodeType & BNODE) && + (status == STATUS_SUCCESS) && + (IS_POS_RESPONSE(OpCodeFlags))) + { + SetupRefreshTtl(pNameHdr,pResp,lNameSize); + + // a name refresh response if in the resolved state + } + } + else + if ( SrcAddress == pDeviceContext->lBackupServer) + { + // switching the backup and the primary nameservers in + // the config data structure since we got a name + // registration response from the backup + // + SwitchToBackup(pDeviceContext); + + if (!(NodeType & BNODE) && + (status == STATUS_SUCCESS) && + (IS_POS_RESPONSE(OpCodeFlags))) + { + + SetupRefreshTtl(pNameHdr,pResp,lNameSize); + + } + + } + // + // mark name as refreshed if we got through to WINS Ok + // + if ((pClientCompletion) && (IS_POS_RESPONSE(OpCodeFlags))) + { + pResp->RefreshMask |= pDevContext->AdapterNumber; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // the completion routine has not run yet, so run it - this + // is the Registration Completion routine and we DO want it to + // run to mark the entry refreshed (NextRefresh) + if (pClientCompletion) + { + LOCATION(0x44); + (*pClientCompletion)(Context,status); + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + + break; + + + default: + // + // if multiple (neg)registration responses are received, subsequent ones + // after the first will go through this path because the state of + // the name will have been changed by the first to CONFLICT + // + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + + } + + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + } // end of else block (If name is not in local table) + + return(STATUS_DATA_NOT_ACCEPTED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +CheckRegistrationFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes + ) +/*++ + +Routine Description: + + This routine handles name registrations from the network that are + potentially duplicates of names in the local name table. It compares + name registrations against its local table and defends any attempts to + take a name that is owned by this node. This routine handles name registration + REQUESTS. + +Arguments: + + +Return Value: + + NTSTATUS - success or not - failure means no response to net + +--*/ +{ + NTSTATUS status; + ULONG lNameSize; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + tNAMEADDR *pResp; + tTIMERQENTRY *pTimer; + PTRANSPORT_ADDRESS pSourceAddress; + USHORT RegType; + CTELockHandle OldIrq1; + ULONG SrcAddress; + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + // + // check the pdu size for errors + // + if (lNumBytes < (NBT_MINIMUM_REGREQUEST + NbtConfig.ScopeLength - 1)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Registration Request TOO short = %X,Src = %X\n", + lNumBytes,SrcAddress)); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("%.*X\n",lNumBytes/sizeof(ULONG),pNameHdr)); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // check if this message came from Us !! (and return if so) + // + + if (SrcIsUs(SrcAddress)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // get the name out of the network pdu and pass to routine to check + // local table *TODO* this assumes just one name in the Query response... + status = ConvertToAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pName, + &pScope, + &lNameSize); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pResp); + + + if (NT_SUCCESS(status)) + { + + RegType = GetNbFlags(pNameHdr,lNameSize); + + // don't defend the broadcast name + if (pName[0] == '*') + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // we defend against anyone trying to take a unique name, or anyone + // trying to register a unique name for a group name we have. - if + // the name is registered on this adapter + // + if (((pResp->NameTypeState & NAMETYPE_UNIQUE) || + ((pResp->NameTypeState & NAMETYPE_GROUP) && + ((RegType & FL_GROUP) == 0))) && + (pResp->AdapterMask & pDeviceContext->AdapterNumber)) + { + + // + // check the state of the name since this could be registration + // for the same name while we are registering the name. If another + // node claims the name at the same time, then cancel the name + // registration. + // + switch (pResp->NameTypeState & NAME_STATE_MASK) + { + + case STATE_RESOLVING: + + // remove any timer block and call the completion routine + if ((pTimer = pResp->pTimer)) + { + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + + status = StopTimer(pTimer,&pClientCompletion,&Context); + CHECK_PTR(pResp); + pResp->pTimer = NULL; + + + if (pClientCompletion) + { + + // LET THE COMPLETION ROUTINE CHANGE the state of the entry + + status = STATUS_DUPLICATE_NAME; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // the completion routine has not run yet, so run it + (*pClientCompletion)(Context,status); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + } + + } + break; + + case STATE_RESOLVED: + // + // We must defend our name against this Rogue attempting to steal + // our Name! ( unless the name is "*") + // + status = UdpSendResponse( + lNameSize, + pNameHdr, + pResp, + (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], + pDeviceContext, + REGISTRATION_ACTIVE_ERR, + eNAME_REGISTRATION_RESPONSE, + OldIrq1); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + break; + + } + + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + } + else + { + // + // NOTE: We have the Joint Lock + // + + // + // The name is not in the local name table, so check if the proxy is + // on and if we want the proxy to check name registrations. The + // trouble with checking name registrations is that the proxy code + // only does a name query, so it will fail any node that is trying + // to change its address such as a RAS client coming in on a downlevel + // NT machine that only does broadcast name registrations. If + // that same user had previously dialled in on a WINS supporting RAS + // machine, their address would be in WINS, then dialling in on the + // downlevel machine would find that 'old' registration and deny + // the new one ( even though it is the same machine just changing + // its ip address). + // + +#ifdef PROXY_NODE + + if ((NodeType & PROXY) && + (NbtConfig.EnableProxyRegCheck)) + { + + BOOLEAN fResp = (BOOLEAN)FALSE; + + // + // If name is RESOLVED in the remote table, has a different + // address than the node claiming the name, is not on the + // same subnet or is a Pnode then send a negative name + // registration response + // + + // + // call this routine to find the name, since it does not + // interpret the state of the name as does FindName() + // + status = FindInHashTable(NbtConfig.pRemoteHashTbl, + pName, + pScope, + &pResp); + + if (!NT_SUCCESS(status)) + { + // + // We need to send a query to WINS to + // see if the name is already taken or not. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + status = RegOrQueryFromNet( + TRUE, //means it is a reg. from the net + pDeviceContext, + pNameHdr, + lNameSize, + pName, + pScope + ); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // If the name is in the RESOLVED state, we need to determine + // whether we should respond or not. For a name that is not + // the RESOLVED state, the decision is simple. We don't respond + if (pResp->NameTypeState & STATE_RESOLVED) + { + + ULONG IPAdd; + + RegType = GetNbFlags(pNameHdr,lNameSize); + // + // If a unique name is being registered but our cache shows + // the name to be a group name (normal or internet), we + // send a negative name registration response. + // + // If a node on the same subnet has responded negatively also, + // it is ok. It is possible that WINS/NBNS has old + // information but there is no easy way to determine the + // network address of the node that registered the group. + // (For a normal group, there could have been several + // nodes that registered it -- their addresses are not stored + // by WINS. + // + if (!(RegType & FL_GROUP) && + !(pResp->NameTypeState & NAMETYPE_UNIQUE)) + { + fResp = TRUE; + } + else + { + tGENERALRR UNALIGNED *pResrcRecord; + + // get the Ip address out of the Registration request + pResrcRecord = (tGENERALRR *) + ((ULONG)&pNameHdr->NameRR.NetBiosName[lNameSize]); + + IPAdd = ntohl(pResrcRecord->IpAddress); + // + // If a group name is being registered but our cache shows + // the name to be a unique name (normal or internet) or + // if a UNIQUE name is being registered but clashes with + // a unique name with a different address, we check if the + // the addresses belong to the same subnet. If they do, we + // don't respond, else we send a negative registration + // response. + // + // Note: We never respond to a group registration + // that clashes with a group name in our cache. + // + if (((RegType & FL_GROUP) + && + (pResp->NameTypeState & NAMETYPE_UNIQUE)) + || + (!(RegType & FL_GROUP) + && + (pResp->NameTypeState & NAMETYPE_UNIQUE) + && + IPAdd != pResp->IpAddress)) + { + IF_DBG(NBT_DEBUG_PROXY) + KdPrint(("CheckReg:Subnet Mask = (%x)\nIPAdd=(%x)\npResp->IPAdd = (%x)\npResp->fPnode=(%d)\nIt is %s name %16.16s(%X)\nRegType Of Name Recd is %x\n---------------\n", + pDeviceContext->SubnetMask, IPAdd, pResp->IpAddress, + pResp->fPnode, + pResp->NameTypeState & NAMETYPE_GROUP ? "GROUP" : "UNIQUE", + pName, pName[15], RegType)); + // + // Are the querying node and the queried node on the + // same subnet ? + // + if (((IPAdd & pDeviceContext->SubnetMask) + != + (pResp->IpAddress & pDeviceContext->SubnetMask)) + || + (pResp->fPnode)) + { + fResp = TRUE; + } + } + } + + // + // If a negative response needs to be sent, send it now + // + if (fResp) + { + + IF_DBG(NBT_DEBUG_PROXY) + KdPrint(("CheckRegistrationFromNet: Sending a negative name registration response for name %16.16s(%X) to node with address (%d)\n", + pResp->Name, pResp->Name[15], IPAdd)); + + // + // a different node is responding to the name query + // so tell them to buzz off. + // + status = UdpSendResponse( + lNameSize, + pNameHdr, + pResp, + (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], + pDeviceContext, + REGISTRATION_ACTIVE_ERR, + eNAME_REGISTRATION_RESPONSE, + OldIrq1); + + return(STATUS_DATA_NOT_ACCEPTED); + + } + } // end of if (NAME is in the RESOLVED state) + } +#endif + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + return(STATUS_DATA_NOT_ACCEPTED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NameReleaseFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes + ) +/*++ + +Routine Description: + + This routine handles name releases that arrive from the Net. The idea + is to delete the name from the remote cache if it exists there so that + this node does not erroneously use that cached information anymore. + +Arguments: + + +Return Value: + + NTSTATUS - success or not - failure means no response to net + +--*/ +{ + NTSTATUS status; + ULONG lNameSize; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + tNAMEADDR *pResp; + PTRANSPORT_ADDRESS pSourceAddress; + CTELockHandle OldIrq1; + USHORT OpCodeFlags; + tTIMERQENTRY *pTimer; + ULONG SrcAddress; + USHORT Flags; + USHORT SendTransactId; + tDGRAM_SEND_TRACKING *pTracker; + BOOLEAN bLocalTable; + tGENERALRR UNALIGNED *pRemainder; + USHORT SrcPort; + ULONG Rcode; + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + SrcPort = ntohs(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port); + // + // check the pdu size for errors + // + if (lNumBytes < (NBT_MINIMUM_REGRESPONSE + NbtConfig.ScopeLength -1)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Release Request/Response TOO short = %X, Src = %X\n",lNumBytes, + SrcAddress)); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("%.*X\n",lNumBytes/sizeof(ULONG),pNameHdr)); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // check if this message came from Us !! + // + if (SrcIsUs(SrcAddress)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // get the name out of the network pdu and pass to routine to check + status = ConvertToAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pName, + &pScope, + &lNameSize); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + OpCodeFlags = pNameHdr->OpCodeFlags; + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + + + // + // *** RESPONSE *** + // + if (OpCodeFlags & OP_RESPONSE) + { + + // + // check the pdu size for errors + // + if (lNumBytes < (NBT_MINIMUM_REGRESPONSE + NbtConfig.ScopeLength -1)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + // + // call this routine to find the name, since it does not interpret the + // state of the name as does FindName() + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pResp); + if (!NT_SUCCESS(status)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // Get the Timer block + if (!(pTimer = pResp->pTimer)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // + // the name server is responding to a name release request + // + // check the transaction id to be sure it is the same as the one + // sent. + // + pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; + SendTransactId = pTracker->TransactionId; + if (pNameHdr->TransactId != SendTransactId) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // for MS & M nodes if there is a response from the name server, + // then switch to broadcast name release. + // + // + switch (NodeType & NODE_MASK) + { + + case MNODE: + case MSNODE: + + + if (SrcIsNameServer(SrcAddress,SrcPort)) + { + + Flags = pTracker->Flags; + + if (Flags & NBT_NAME_SERVER) + { + // + // the next timeout will then switch to broadcast name + // release. + // + pTimer->Retries = 1; + } + + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return(STATUS_DATA_NOT_ACCEPTED); + + case PNODE: + // + // + // this routine puts the timer block back on the timer Q, and + // handles race conditions to cancel the timer when the timer + // is expiring. + // + if ((pTimer = pResp->pTimer)) + { + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + + status = StopTimer(pTimer,&pClientCompletion,&Context); + + CHECK_PTR(pResp); + pResp->pTimer = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // the completion routine has not run yet, so run it + if (pClientCompletion) + { + (*pClientCompletion)(Context,STATUS_SUCCESS); + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + + return(STATUS_DATA_NOT_ACCEPTED); + + case BNODE: + default: + // + // normally there should be no response to a name release + // from a Bnode, but if there is, ignore it. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_DATA_NOT_ACCEPTED); + + } + + } + else + { + // + // It is a RELEASE REQUEST - so decide if the name should be removed + // from the remote or local table + // + + // check the pdu size for errors + // + if (lNumBytes < (NBT_MINIMUM_REGREQUEST + NbtConfig.ScopeLength -1)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // check the REMOTE hash table for the name... + // + status = FindInHashTable(NbtConfig.pRemoteHashTbl, + pName, + pScope, + &pResp); + bLocalTable = FALSE; + if (!NT_SUCCESS(status)) + { + // + // check the LOCAL name table for the name since the name server + // could be doing the equivalent of a name conflict demand + // + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pResp); + + + bLocalTable = TRUE; + } + + if (NT_SUCCESS(status)) + { + // check if the address being released corresponds to the one in + // the table - if not then ignore the release request - since someone + // else presumably tried to get the name, was refused and now is + // sending a name release request. + // + pRemainder = (tGENERALRR *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + if (pResp->IpAddress != (ULONG)ntohl(pRemainder->IpAddress)) + { + status = STATUS_UNSUCCESSFUL; + } + } + + if (NT_SUCCESS(status)) + { + + // + // Don't remove group names, since a single group member + // releasing the name does not matter. Group names time + // out of the Remote table. + // + if (pResp->NameTypeState & NAMETYPE_UNIQUE) + { + switch (pResp->NameTypeState & NAME_STATE_MASK) + { + + case STATE_RESOLVING: + // + // stop any timer that may be going + // + pTimer = pResp->pTimer; + CHECK_PTR(pResp); + pResp->pTimer = NULL; + // + // Local table means that it is a name registration + // and we must avoid calling CompleteClientReq + // + if (!bLocalTable) + { + StopTimerAndCallCompletion(pTimer, + STATUS_DUPLICATE_NAME, + OldIrq1); + } + else + { + if (pTimer) + { + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + + status = StopTimer(pTimer,&pClientCompletion,&Context); + + // the completion routine has not run yet, so run it + if (pClientCompletion) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + (*pClientCompletion)(Context,STATUS_DUPLICATE_NAME); + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + } + } + } + + break; + + case STATE_RESOLVED: + // dereference the name if it is in the remote table, + // this should change the state to RELEASED. For the + // local table just change the state to CONFLICT, since + // the local client still thinks it has the name open, + // however in the conflict state the name cannot be use + // to place new sessions and this node will not respond + // to name queries for the name. + // + if (!bLocalTable) + { + // + // if this is a pre-loaded name, just leave it alone + // + if (!(pResp->NameTypeState & PRELOADED)) + { + // + // if someone is still using the name, do not + // dereference it, since that would leave the + // ref count at 1, and allow RemoteHashTimeout + // code to remove it before the client using + // the name is done with it. Once the client is + // done with it (i.e. a connect request), they + // will deref it , setting the ref count to 1 and + // it will be suitable for reuse. + // + if (pResp->RefCount > 1) + { + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_CONFLICT; + } + else + { + NbtDereferenceName(pResp); + } + } + } + else + { + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_CONFLICT; + NbtLogEvent(EVENT_NBT_NAME_RELEASE,SrcAddress); + + } + break; + + default: + break; + + } + } + + + // + // tell WINS that the name released ok + // + Rcode = 0; + } + else + { + Rcode = NAME_ERROR; + } + + // + // Only respond if not a broadcast... + // + if (!(OpCodeFlags & FL_BROADCAST)) + { + status = UdpSendResponse( + lNameSize, + pNameHdr, + NULL, + (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], + pDeviceContext, + Rcode, + eNAME_RELEASE, + OldIrq1); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + + } // end of Release Request processing +} + +//---------------------------------------------------------------------------- +NTSTATUS +WackFromNet( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes + ) +/*++ + +Routine Description: + + This routine handles Wait Acks from the name server. It finds the corresponding + name service transaction and changes the timeout of that transaction according + to the TTL field in the WACK. + +Arguments: + + +Return Value: + + NTSTATUS - success or not - failure means no response to net + +--*/ +{ + NTSTATUS status; + ULONG lNameSize; + CHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq1; + ULONG Ttl; + tTIMERQENTRY *pTimerEntry; + + // + // check the pdu size for errors + // + if (lNumBytes < (NBT_MINIMUM_WACK + NbtConfig.ScopeLength -1)) + { + KdPrint(("Nbt:WACK TOO short = %X\n",lNumBytes)); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // get the name out of the network pdu and pass to routine to check + status = ConvertToAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pName, + &pScope, + &lNameSize); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + +#ifdef VXD + if ( FindContextDirect( pNameHdr->TransactId ) != NULL ) + { + status = STATUS_SUCCESS; + } + else + { +#endif // VXD + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pNameAddr); +#ifdef VXD + } +#endif // VXD + + if (NT_SUCCESS(status)) + { + Ttl = *(ULONG UNALIGNED *)( (ULONG)&pNameHdr->NameRR.NetBiosName[0] + + lNameSize + + FIELD_OFFSET(tQUERYRESP,Ttl) ); + Ttl = ntohl(Ttl); + + pTimerEntry = pNameAddr->pTimer; + if (pTimerEntry) + { + + // convert seconds to milliseconds and put into the DeltaTime + // field so that when the next timeout occurs it changes the timer + // value to this new one. Depending on how many timeouts are left + // this could cause the client to wait several times the WACK timeout + // value. For example a Name query nominally has two retries, so if + // the WACK returns before the first retry then the total time waited + // will be 2*Ttl. This is not a problem since the real reason for + // the timeout is to prevent waiting forever for a dead name server. + // If the server returns a WACK it is not dead and the chances are + // that it will return a response before the timeout anyway. + // + // The timeout routine checks if TIMER_RETIMED is set and restarts + // the timeout without any processing if that is true ( and clears + // the flag too). + // + Ttl *= 1000; + if (Ttl > pTimerEntry->DeltaTime) + { + pTimerEntry->DeltaTime = Ttl; + pTimerEntry->Flags |= TIMER_RETIMED; + } + + } + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return(STATUS_DATA_NOT_ACCEPTED); +} + +//---------------------------------------------------------------------------- +VOID +SetupRefreshTtl( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN tNAMEADDR *pNameAddr, + IN LONG lNameSize + ) +/*++ + +Routine Description: + + This routine handles name refresh timeouts. It looks at the Ttl in the + registration response and determines if the Node's refresh timeout should + be lengthened or shortened. To do this both the Ttl and the name associated + with the Ttl are kept in the Config structure. If the Ttl becomes longer + for the shortest names Ttl, then all the names use the longer value. + +Arguments: + +Return Value: + + NTSTATUS - success or not - failure means no response to net + +--*/ +{ + NTSTATUS status; + ULONG Ttl; + tTIMERQENTRY *pTimerQEntry; + + // the Ttl in the pdu is in seconds. We need to convert it to milliseconds + // to use for our timer. This limits the timeout value to about 50 days + // ( 2**32 / 3600/24/1000 - milliseconds converted to days.) + // + Ttl = *(ULONG UNALIGNED *)( + (PUCHAR)&pNameHdr->NameRR.NetBiosName[0] + + lNameSize + + FIELD_OFFSET(tQUERYRESP,Ttl) + ); + + Ttl = ntohl(Ttl); + + // the Ttl value may overflow the value we can store in Milliseconds, + // check for this case, and if it happens, use the longest timeout possible + // that still runs refresh, - i.e. NBT_MAXIMUM_TTL disables refresh + // altogether, so use NBT_MAXIMUM_TTL-1). + if (Ttl >= 0xFFFFFFFF/1000) + { + Ttl = NBT_MAXIMUM_TTL - 1; + } + else + { + Ttl *= 1000; // convert to milliseconds + } + + // a zero Ttl means infinite, so set time the largest timeout + // + if (Ttl == 0) + { + Ttl = NBT_MAXIMUM_TTL; // set very large number which turns off refreshes + } + else + if (Ttl < NBT_MINIMUM_TTL) + { + Ttl = NBT_MINIMUM_TTL; + } + + // Set the Ttl for the name record + // + pNameAddr->Ttl = Ttl; + + // + // decide what to do about the existing timer.... + // If the new timeout is shorter, then cancel the + // current timeout and start another one. + // + if (Ttl < NbtConfig.MinimumTtl) + { + KdPrint(("Nbt:Shortening Refresh Ttl from %d to %d\n", + NbtConfig.MinimumTtl, Ttl)); + + NbtConfig.MinimumTtl = (ULONG)Ttl; + pTimerQEntry = NbtConfig.pRefreshTimer; + IF_DBG(NBT_DEBUG_NAMESRV) + // + // don't allow the stop timer routine to call the completion routine + // for the timer. + // + CHECK_PTR(pTimerQEntry); + if (pTimerQEntry) + { + pTimerQEntry->CompletionRoutine = NULL; + status = StopTimer(pTimerQEntry,NULL,NULL); + } + + // keep the timeout for checking refreshes to about 10 minutes + // max. (MAX_REFRESH_CHECK_INTERVAL). If the refresh interval + // is less than 80 minutes then always use a refresh divisor of + // 8 - this allows the initial default ttl of 16 minutes to result + // in retries every 2 minutes. + // + NbtConfig.RefreshDivisor = NbtConfig.MinimumTtl/MAX_REFRESH_CHECK_INTERVAL; + if (NbtConfig.RefreshDivisor < REFRESH_DIVISOR) + { + NbtConfig.RefreshDivisor = REFRESH_DIVISOR; + } + + // + // start the timer + // + status = StartTimer( + Ttl/NbtConfig.RefreshDivisor, + NULL, // context value + NULL, // context2 value + RefreshTimeout, + NULL, + NULL, + 0, + &NbtConfig.pRefreshTimer); +#if DBG + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to start a new timer for refresh\n")); + } +#endif + + } + else + if (Ttl > NbtConfig.MinimumTtl) + { + tHASHTABLE *pHashTable; + LONG i; + PLIST_ENTRY pHead,pEntry; + + // PUT this code back in again, since it is possible that the name + // server could miss registering a name due to being busy and if we + // lengthen the timeout here then that name will not get into wins for + // a very long time. + + // the shortest Ttl got longer, check if there is another shortest + // Ttl by scanning the local name table. + // + 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); + // + // Find a valid name with a lower TTL if possible + // + if ((pNameAddr->Name[0] != '*') && + ((pNameAddr->NameTypeState & STATE_RESOLVED)) && + (pNameAddr->Ttl < (ULONG)Ttl) && + (!(pNameAddr->NameTypeState & NAMETYPE_QUICK))) + { + if (pNameAddr->Ttl >= NBT_MINIMUM_TTL) + { + NbtConfig.MinimumTtl = pNameAddr->Ttl; + } + return; + } + pEntry = pEntry->Flink; + } + } + + // + // if we get to here then there is no shorter ttl, so use the new + // ttl received from the WINS as the ttl. The next time the refresh + // timer expires it will restart with this new ttl + // + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Lengthening Refresh Ttl from %d to %d\n", + NbtConfig.MinimumTtl, Ttl)); + + NbtConfig.MinimumTtl = Ttl; + + // keep the timeout for checking refreshes to about 10 minutes + // max. (MAX_REFRESH_CHECK_INTERVAL). If the refresh interval + // is less than 80 minutes then always use a refresh divisor of + // 8 - this allows the initial default ttl of 16 minutes to result + // in retries every 2 minutes. + // + NbtConfig.RefreshDivisor = NbtConfig.MinimumTtl/MAX_REFRESH_CHECK_INTERVAL; + if (NbtConfig.RefreshDivisor < REFRESH_DIVISOR) + { + NbtConfig.RefreshDivisor = REFRESH_DIVISOR; + } + + } + + +} + +//---------------------------------------------------------------------------- +NTSTATUS +DecodeNodeStatusResponse( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN ULONG Length, + IN PUCHAR pName, + IN ULONG lNameSize, + IN ULONG SrcIpAddress + ) +/*++ + +Routine Description: + + This routine handles putting the node status response pdu into the clients + MDL. + +Arguments: + + +Return Value: + + none + +--*/ +{ + NTSTATUS status; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PLIST_ENTRY pNext; + tNODESTATUS UNALIGNED *pNodeStatus; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + tDGRAM_SEND_TRACKING *pTracker; + tTIMERQENTRY *pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID pClientContext; + BOOL MatchFound=FALSE; + ULONG IpAddress; + + + // first find the originating request in the NodeStatus list + CTESpinLock(&NbtConfig.JointLock,OldIrq2); + CTESpinLock(&NbtConfig,OldIrq); + + pHead = &NbtConfig.NodeStatusHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pTracker = CONTAINING_RECORD(pEntry,tDGRAM_SEND_TRACKING,Linkage); + + // + // find who sent the request originally + // + MatchFound = FALSE; + if (pTracker->Flags & REMOTE_ADAPTER_STAT_FLAG) + { + IpAddress = Nbt_inet_addr(pTracker->pNameAddr->Name); + } + else + { + IpAddress = 0; + } + if ( + (CTEMemEqu(pName,pTracker->pNameAddr->Name,NETBIOS_NAME_SIZE)) + || ((IpAddress==SrcIpAddress)&&(IpAddress!=0)) + ) + { + // + // if we directed node status request to an ipaddr without knowing + // its netbios name, then name is stored as "* ". + // + if ( (pName[0] == '*') && ( IpAddress == 0 ) ) + { + int i=0; + + // + // SrcIpAddress may not match the ipaddr to which we sent if + // remote host is multihomed: so search whole list of all + // ipaddrs for that host + // + ASSERT(pTracker->pNameAddr->pIpAddrsList); + while(pTracker->pNameAddr->pIpAddrsList[i]) + { + if (pTracker->pNameAddr->pIpAddrsList[i++] == SrcIpAddress) + { + MatchFound = TRUE; + break; + } + } + } + else + { + MatchFound = TRUE; + } + } + else + { + MatchFound = FALSE; + } + + if (MatchFound) + { + RemoveEntryList(pEntry); + + pNodeStatus = (tNODESTATUS *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + // this is the amount of data left, that we do not want to go + // beyond, otherwise the system will bugcheck + // + Length -= FIELD_OFFSET(tNAMEHDR,NameRR.NetBiosName) + lNameSize; + + pTimer = pTracker->Connect.pTimer; + if (pTimer) + { + CTESpinFree(&NbtConfig,OldIrq); + + status = StopTimer(pTimer,&pClientCompletion,&pClientContext); + + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + if (pClientCompletion) + { + // + // if there is a ClientContext then this node status qry was + // started because of a session setup attempt: extract the + // server name out of the node status response and continue + // with the session setup phase + // + if (pClientContext) + { + status = ExtractServerName(pNodeStatus, + pClientContext, + SrcIpAddress); + + DereferenceTracker(pTracker); + + CompleteClientReq(pClientCompletion, + pClientContext, + status); + // (*pClientCompletion)(pClientContext,status); + } + // + // there is no ClientContext which means this node status query + // was started because of a remote adapter status request + // (e.g. nbtstat -a): extract all names out of the node status + // response and complete the remote adapter status irp + // + else + { + PVOID pBuffer; + + // bump this up to non dispatch level processing + pBuffer = NbtAllocMem(Length,NBT_TAG('7')); + if (pBuffer) + { + CTEMemCopy(pBuffer,(PVOID)pNodeStatus,Length); + pTracker->SrcIpAddress = SrcIpAddress; + pTracker->pNodeStatus = pBuffer; + pTracker->NodeStatusLen = Length; + + CTEQueueForNonDispProcessing(pTracker, + pTracker, + pClientCompletion, + CopyNodeStatusResponse, + NULL); + } + } + } + + break; + } else { + // + // Free the lock nonetheless. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + ASSERTMSG("Nbt:found node status element, but Timer is NULL!\n",0); + } + } + else + { + pEntry = pEntry->Flink; + } + + } + + if (!MatchFound) + { + CTESpinFree(&NbtConfig,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + } + + return(STATUS_UNSUCCESSFUL); +} + +typedef enum _dest_type { + IP_ADDR, + DNS, + NETBIOS +} DEST_TYPE; + +//---------------------------------------------------------------------------- +DEST_TYPE +GetDestType( + IN PUCHAR pName + ) + +/*++ +Routine Description: + + Classifies name passed in as an IP addr/Netbios name/Dns name + +Arguments: + +Return Value: + DEST_TYPE + +--*/ +{ + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("GetDestType: input: %s\n", pName)); + + if (Nbt_inet_addr(pName)) { + return IP_ADDR; + } else { + if ((pName[NETBIOS_NAME_SIZE-1] <= 0x20 ) || + (pName[NETBIOS_NAME_SIZE-1] >= 0x7f )) { + return NETBIOS; + } else { + // + // else 16 byte DNS name + // + return DNS; + } + + } +} + +//---------------------------------------------------------------------------- +NTSTATUS +ExtractServerName( + IN tNODESTATUS UNALIGNED *pNodeStatus, + IN PVOID pClientContext, + IN ULONG IpAddress + ) + +/*++ +Routine Description: + + This Routine searches for the server name (name ending with 0x20) from the + list of names returned by node status response, and adds that name to the + remote hash table. + +Arguments: + + pNodeStatus Node status response from the remote host + pClientContext Tracker for the seutp phase + IpAddress Ip address of the node that just responded + +Return Value: + + NTSTATUS - status of the request + Success, if we find the server name + Bad_Network_Path if we couldn't find the server name + +--*/ + +{ + + NTSTATUS status; + NTSTATUS Locstatus; + ULONG i; + UCHAR Flags; + PCHAR pName; + tNAMEADDR *pNameAddr; + tDGRAM_SEND_TRACKING *pClientTracker; + tSESSIONREQ *pSessionReq; + PUCHAR pCopyTo; + CTELockHandle OldIrq; + DEST_TYPE DestType; + + + + status = STATUS_BAD_NETWORK_PATH; + + pClientTracker = (tDGRAM_SEND_TRACKING *)pClientContext; + + if (pClientTracker->SendBuffer.Length > NETBIOS_NAME_SIZE) { + DestType = DNS; + } else { + DestType = GetDestType(pClientTracker->pDestName); + } + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("ExtractSrvName: DestType: %d\n", DestType)); + + for(i =0; i<pNodeStatus->NumNames; i++) + { + pName = &pNodeStatus->NodeName[i].Name[0]; + + // + // For IP addresses and DNS names, we map the 0x20 name to the corresp 0x0 name + // for datagram sends. + // + if ((DestType == IP_ADDR) || (DestType == DNS)) { + if (pName[NETBIOS_NAME_SIZE-1] != 0x20) + continue; + + // else + if (pClientTracker->Flags & DGRAM_SEND_FLAG) { + pName[NETBIOS_NAME_SIZE-1] = 0x0; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("ExtractServerName: Mapping 0x20 name to 0x0\n")); + } + + } else { + // + // For Netbios names (resolved via DNS), we match the 16th byte exactly + // + if (pName[NETBIOS_NAME_SIZE-1] != pClientTracker->pDestName[NETBIOS_NAME_SIZE-1]) + continue; + } + + Flags = pNodeStatus->NodeName[i].Flags; + + // + // make sure it's a unique name (for connects only, for dgram sends, group names are fine) + // and is not in conflict or released + // + if ( (((pClientTracker->Flags & SESSION_SETUP_FLAG) && !(Flags & GROUP_STATUS)) || + (pClientTracker->Flags & DGRAM_SEND_FLAG)) && + !(Flags & NODE_NAME_CONFLICT) && + !(Flags & NODE_NAME_RELEASED) ) + { + + // + // fix up the connection tracker to point to the right name, now + // that we know the server name to connect to + // + + // + // The FIND_NAME_FLAG was set to indicate that this is not a session setup attempt so + // we can avoid the call to ConvertToHalfAscii. + // + if (!(pClientTracker->Flags & FIND_NAME_FLAG)) { + + if ( pClientTracker->Flags & SESSION_SETUP_FLAG ) + { + CTEMemCopy(pClientTracker->SendBuffer.pBuffer,pName,NETBIOS_NAME_SIZE); + CTEMemCopy(pClientTracker->Connect.pConnEle->RemoteName, + pName, + NETBIOS_NAME_SIZE); + #ifdef VXD + CTEMemCopy(&pClientTracker->pClientIrp->ncb_callname[0],pName,NETBIOS_NAME_SIZE); + #endif // VXD + pSessionReq = pClientTracker->SendBuffer.pDgramHdr; + + // + // overwrite the Dest HalfAscii name in the Session Pdu with the correct name + // + pCopyTo = ConvertToHalfAscii( (PCHAR)&pSessionReq->CalledName.NameLength, + pName, + NbtConfig.pScope, + NbtConfig.ScopeLength); + + } else if (pClientTracker->Flags & DGRAM_SEND_FLAG) { + PCHAR pCopyTo; + tDGRAMHDR *pDgramHdr; + + // + // Overwrite the dest name, so SendDgramContinue can find the name + // in the caches. + // + CTEMemCopy(pClientTracker->pDestName,pName,NETBIOS_NAME_SIZE); + + // + // Copy over the actual dest name in half-ascii + // This is immediately after the SourceName; so offset the + // dest by the length of the src name. + // + pDgramHdr = pClientTracker->SendBuffer.pDgramHdr; + + pCopyTo = (PVOID)&pDgramHdr->SrcName.NameLength; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("pCopyTo:%lx\n", pCopyTo)); + + pCopyTo += 1 + // Length field + 2 * NETBIOS_NAME_SIZE + // actual name in half-ascii + NbtConfig.ScopeLength; // length of scope + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("pCopyTo:%lx\n", pCopyTo)); + + ConvertToHalfAscii( pCopyTo, + pName, + NbtConfig.pScope, + NbtConfig.ScopeLength); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("Copied the remote name for dgram sends\n")); + } + + } else { + KdPrint(("ExtractServerName: Find name going on\n")); + } + + // + // add this server name to the remote hashtable + // + pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('8')); + if (!pNameAddr) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CTEZeroMemory(pNameAddr,sizeof(tNAMEADDR)); + InitializeListHead(&pNameAddr->Linkage); + CTEMemCopy(pNameAddr->Name,pName,NETBIOS_NAME_SIZE); + pNameAddr->Verify = REMOTE_NAME; + pNameAddr->RefCount = 1; + pNameAddr->NameTypeState = STATE_RESOLVED | NAMETYPE_UNIQUE; + pNameAddr->AdapterMask = (CTEULONGLONG)-1; + pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + pNameAddr->IpAddress = IpAddress; + + Locstatus = AddRecordToHashTable(pNameAddr,NbtConfig.pScope); + + status = STATUS_SUCCESS; + + //KdPrint(("Nbt NodeStat: Found SrvName. IpAddr = %x, Name = %16.16s<%X>\n",IpAddress,pName,pName[15])); + + // + // if Nameaddr couldn't be added, it means an entry already exists + // Get that entry and update its ipaddr. + // + if (!NT_SUCCESS(Locstatus)) + { + //KdPrint(("Nbt ExtractSrv: couldn't add nameaddr for %16.16s<%X>\n",pName,pName[15])); + NbtDereferenceName(pNameAddr); + Locstatus = FindInHashTable( NbtConfig.pRemoteHashTbl, + pName, + NbtConfig.pScope, + &pNameAddr); + if (!NT_SUCCESS(Locstatus)) + { + Locstatus = FindInHashTable( NbtConfig.pLocalHashTbl, + pName, + NbtConfig.pScope, + &pNameAddr); + } + + if (NT_SUCCESS(Locstatus)) + { + pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + pNameAddr->IpAddress = IpAddress; + } + else + { + ASSERTMSG("Nbt ExtractSrv: couldn't find name in remote or loc table!\n",0); + status = STATUS_BAD_NETWORK_PATH; + } + + } + + break; // found the name: done with the for loop + } + } + + return( status ); + +} + +//---------------------------------------------------------------------------- +VOID +CopyNodeStatusResponse( + IN PVOID pContext + ) + +// IN tNODESTATUS UNALIGNED *pNodeStat, +// IN ULONG TotalLength, +// IN ULONG SrcIpAddress, +// IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine copies data received from the net node status response to + the client's irp. It is called from inbound.c when a node status response + comes in from the wire. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + ULONG NumNames; + ULONG i; + PADAPTER_STATUS pAdapterStatus; + PNAME_BUFFER pNameBuffer; + ULONG BuffSize; + BOOLEAN Failed; + ULONG AccumLength; + PUCHAR pAscii; + UCHAR Flags; + ULONG DataLength; + ULONG DestSize ; + tSTATISTICS UNALIGNED *pStatistics; + ULONG SrcIpAddress; + ULONG TotalLength; + tNODESTATUS *pNodeStat; + tDGRAM_SEND_TRACKING *pTracker; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + PIRP pIrp; + + CTEPagedCode(); + + pTracker = ((NBT_WORK_ITEM_CONTEXT *)pContext)->pTracker; + + SrcIpAddress = pTracker->SrcIpAddress; + TotalLength = pTracker->NodeStatusLen; + CHECK_PTR(pTracker); + pTracker->NodeStatusLen = 0; + pNodeStat = (tNODESTATUS *)pTracker->pNodeStatus; + pIrp = pTracker->pClientIrp; + + NumNames = pNodeStat->NumNames; + + BuffSize = sizeof(ADAPTER_STATUS) + NumNames*sizeof(NAME_BUFFER); + + // sanity check that we are not allocating more than 64K for this stuff + if (BuffSize > 0xFFFF) + { + status = STATUS_UNSUCCESSFUL; + goto ExitRoutine; + } + + pAdapterStatus = NbtAllocMem((USHORT)BuffSize,NBT_TAG('9')); + if (!pAdapterStatus) + { + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitRoutine; + } + + // Fill out the adapter status structure with zeros first + CTEZeroMemory((PVOID)pAdapterStatus,BuffSize); + + // get the source MAC address from the statistics portion of the pdu + // + if (TotalLength >= (NumNames*sizeof(tNODENAME) + sizeof(tSTATISTICS))) + { + pStatistics = (tSTATISTICS UNALIGNED *)((PUCHAR)&pNodeStat->NodeName[0] + NumNames*sizeof(tNODENAME)); + + CTEMemCopy(&pAdapterStatus->adapter_address[0], + &pStatistics->UnitId[0], + sizeof(tMAC_ADDRESS)); + } + + pAdapterStatus->rev_major = 0x03; + pAdapterStatus->adapter_type = 0xFE; // pretend it is an ethernet adapter + + // + // get the ptr to the statistics field if there is one in the pdu + // + AccumLength = NumNames * sizeof(tNODENAME) + + FIELD_OFFSET(tNODESTATUS, NodeName) + sizeof(USHORT) + + FIELD_OFFSET( tSTATISTICS, SessionDataPacketSize ) ; + + if (AccumLength <= TotalLength) + { + // + // there is a whole statistics portion to the adapter status command, + // so we can get the session pdu size out of it. + // + pAdapterStatus->max_sess = ntohs((USHORT)*((PUCHAR)pNodeStat + + AccumLength + - sizeof(USHORT))); + + } + + // 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)pAdapterStatus + sizeof(ADAPTER_STATUS)); + + // set the AccumLength to the start of the node name array in the buffer + // so we can count through the buffer and be sure not to run off the end + // + AccumLength = FIELD_OFFSET(tNODESTATUS, NodeName); + + Failed = FALSE; + for(i =0; i< NumNames; i++) + { + AccumLength += sizeof(tNODENAME); + if (AccumLength > TotalLength) + { + Failed = TRUE; + break; + } + pAdapterStatus->name_count++ ; + pAscii = (PCHAR)&pNodeStat->NodeName[i].Name[0]; + Flags = pNodeStat->NodeName[i].Flags; + + pNameBuffer->name_flags = (Flags & GROUP_STATUS) ? + GROUP_NAME : UNIQUE_NAME; + + // + // map the name states + // + + if (Flags & NODE_NAME_CONFLICT) + { + if (Flags & NODE_NAME_RELEASED) + pNameBuffer->name_flags |= DUPLICATE_DEREG; + else + pNameBuffer->name_flags |= DUPLICATE; + + } + else + if (Flags & NODE_NAME_RELEASED) + { + pNameBuffer->name_flags |= DEREGISTERED; + } + else + { + pNameBuffer->name_flags |= REGISTERED; + } + + pNameBuffer->name_num = (UCHAR)i+1; + + CTEMemCopy(pNameBuffer->name,pAscii,NETBIOS_NAME_SIZE); + + pNameBuffer++; + + } + + // + // The remote buffer is incomplete, what else can we do? + // + if ( Failed ) + { + status = STATUS_UNSUCCESSFUL; + goto ExitCleanup; + } + + + // + // Reduce the name count if we can't fit the buffer + // +#ifdef VXD + DestSize = ((NCB*)pIrp)->ncb_length ; +#else + DestSize = MmGetMdlByteCount( pIrp->MdlAddress ) ; +#endif + + CHECK_PTR(pAdapterStatus); + if ( BuffSize > DestSize ) + { + if ( DestSize < sizeof( ADAPTER_STATUS )) + pAdapterStatus->name_count = 0 ; + else + pAdapterStatus->name_count = (WORD) + ( DestSize- sizeof(ADAPTER_STATUS)) / sizeof(NAME_BUFFER) ; + + } + + // + // Copy the built adapter status structure + // +#ifdef VXD + if ( BuffSize > DestSize ) + { + status = STATUS_BUFFER_OVERFLOW ; + BuffSize = DestSize ; + } + else + status = STATUS_SUCCESS ; + + CTEMemCopy( ((NCB*)pIrp)->ncb_buffer, + pAdapterStatus, + BuffSize ) ; + // + // Set here to be compatible with NT + // + ((NCB*)pIrp)->ncb_length = (WORD) BuffSize ; + +#else + status = TdiCopyBufferToMdl ( + pAdapterStatus, + 0, + BuffSize, + pIrp->MdlAddress, + 0, + &DataLength); + + pIrp->IoStatus.Information = DataLength; + + pIrp->IoStatus.Status = status; + +#endif + +ExitCleanup: + CTEMemFree((PVOID)pAdapterStatus); + +ExitRoutine: + Context = ((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + pClientCompletion = (COMPLETIONCLIENT)((NBT_WORK_ITEM_CONTEXT *)pContext)->ClientCompletion; + + CTEMemFree(pNodeStat); + CTEMemFree(pContext); + + + // call the completion routine of the original node status request in + // name.c + (*pClientCompletion)(Context,status); + + return; +} + +//---------------------------------------------------------------------------- +NTSTATUS +SendNodeStatusResponse( + IN tNAMEHDR UNALIGNED *pInNameHdr, + IN ULONG Length, + IN PUCHAR pName, + IN ULONG lNameSize, + IN ULONG SrcIpAddress, + IN USHORT SrcPort, + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine handles putting the node status response pdu into the clients + MDL. + +Arguments: + + +Return Value: + + none + +--*/ +{ + NTSTATUS status; + PUCHAR pScope; + PUCHAR pInScope; + ULONG Position; + ULONG CountNames; + ULONG BuffSize; + tNODESTATUS UNALIGNED *pNodeStatus; + tNAMEHDR *pNameHdr; + CTELockHandle OldIrq2; + ULONG i; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tADDRESSELE *pAddressEle; + tNAMEADDR *pNameAddr; + tDGRAM_SEND_TRACKING *pTracker; + ULONG InScopeLength; + tSTATISTICS UNALIGNED *pStatistics; + tNODENAME UNALIGNED *pNode; + CTEULONGLONG AdapterNumber; + ULONG Len; + + if (Length > sizeof(tNAMEHDR) + lNameSize - 1 + sizeof(ULONG)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq2); + + // verify that the requesting node is in the same scope as this node, so + // get a ptr to the scope, which starts 16*2 (32) bytes into the + // netbios name in the pdu. + // + pInScope = (PUCHAR)&pInNameHdr->NameRR.NetBiosName[(NETBIOS_NAME_SIZE <<1)]; + pScope = NbtConfig.pScope; + + Position = sizeof(tNAMEHDR) - sizeof(tNETBIOS_NAME) +1 + (NETBIOS_NAME_SIZE <<1); + + // check the scope length + InScopeLength = Length - Position - sizeof(ULONG); + if (InScopeLength != NbtConfig.ScopeLength) + { + status = STATUS_DATA_NOT_ACCEPTED; + goto ErrorExit; + } + + // compare scopes for equality and avoid running off the end of the pdu + // + i= 0; + while (i < NbtConfig.ScopeLength) + { + if (*pInScope != *pScope) + { + status = STATUS_DATA_NOT_ACCEPTED; + goto ErrorExit; + } + i++; + pInScope++; + pScope++; + } + + // get the count of names, excluding '*...' which we do not send... + // + CountNames = CountLocalNames(&NbtConfig); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Node Status Response, with %d names\n",CountNames)); + + // this is only a byte field, so only allow up to 255 names. + if (CountNames > 255) + { + CountNames = 255; + } + + + // Allocate Memory for the adapter status + + // - ULONG for the Nbstat and IN that are part of Length. CountNames-1 + // because there is one name in sizeof(tNODESTATUS) already + // + BuffSize = Length + sizeof(tNODESTATUS) - sizeof(ULONG) + (CountNames-1)*sizeof(tNODENAME) + + sizeof(tSTATISTICS); + + pNameHdr = (tNAMEHDR *)NbtAllocMem((USHORT)BuffSize,NBT_TAG('A')); + if (!pNameHdr) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // copy the request to the response and change a few bits around + // + CHECK_PTR(pNameHdr); + CTEMemCopy((PVOID)pNameHdr,(PVOID)pInNameHdr,Length); + pNameHdr->OpCodeFlags = OP_RESPONSE | FL_AUTHORITY; + pNameHdr->QdCount = 0; + pNameHdr->AnCount = 1; + + pNodeStatus = (tNODESTATUS UNALIGNED *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + CHECK_PTR(pNodeStatus); + pNodeStatus->Ttl = 0; + + + pNode = (tNODENAME UNALIGNED *)&pNodeStatus->NodeName[0]; + AdapterNumber = pDeviceContext->AdapterNumber; + + i = 0; + pEntry = pHead = &NbtConfig.AddressHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + + pNameAddr = pAddressEle->pNameAddr; + + pNode->Flags = (pAddressEle->NameType == NBT_UNIQUE) ? + UNIQUE_STATUS : GROUP_STATUS; + + // all names have this one set + // + pNode->Flags |= NODE_NAME_ACTIVE; + switch (pNameAddr->NameTypeState & NAME_STATE_MASK) + { + default: + case STATE_RESOLVED: + break; + + case STATE_CONFLICT: + pNode->Flags |= NODE_NAME_CONFLICT; + break; + + case STATE_RELEASED: + pNode->Flags |= NODE_NAME_RELEASED; + break; + + case STATE_RESOLVING: + // don't count these names. + continue; + + } + + switch (NodeType & NODE_MASK) + { + case BNODE: + pNode->Flags |= STATUS_BNODE; + break; + + case MSNODE: + case MNODE: + pNode->Flags |= STATUS_MNODE; + break; + + case PNODE: + pNode->Flags |= STATUS_PNODE; + } + + CHECK_PTR(pNode); + pNode->Resrved = 0; + + // Copy the name in the pdu + CTEMemCopy((PVOID)&pNode->Name[0], + (PVOID)pNameAddr->Name, + NETBIOS_NAME_SIZE); + + + // check for the permanent name...and add it too + // + if (pNameAddr->NameTypeState & NAMETYPE_QUICK) + { + // the permanent name is added as a Quick Add in the name + // table + // + // do not put the permanent name into the response + continue; + + } + else + if ((pNameAddr->Name[0] == '*') || + (pNameAddr->NameTypeState & STATE_RESOLVING) || + (!(pNameAddr->AdapterMask & AdapterNumber))) + { + // + // do not put the broadcast name into the response, since neither + // NBF or WFW NBT puts it there. + + // Also, to not respond with resolving names, or names that are + // not registered on this adapter (multihomed case) + // + continue; + } + + i++; + pNode++; + } + + // + // set the count of names in the response packet + // + pNodeStatus->NumNames = (UCHAR)i; + + Len = i*sizeof(tNODENAME) + 1 + sizeof(tSTATISTICS); //+1 for NumNames Byte + pNodeStatus->Length = (USHORT)htons(Len); + + // fill in some of the statistics fields which occur after the name table + // in the PDU + // + pStatistics = (tSTATISTICS UNALIGNED *)((PUCHAR)&pNodeStatus->NodeName[0] + + i*sizeof(tNODENAME)); + + CTEZeroMemory((PVOID)pStatistics,sizeof(tSTATISTICS)); + + // + // put the MAC address in the response + // + CTEMemCopy(&pStatistics->UnitId[0], + &pDeviceContext->MacAddress.Address[0], + sizeof(tMAC_ADDRESS)); + // + // Now send the node status message + // + status = GetTracker(&pTracker); + + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + if (!NT_SUCCESS(status)) + { + CTEMemFree((PVOID)pNameHdr); + + } + else + { + CHECK_PTR(pTracker); + pTracker->SendBuffer.HdrLength = BuffSize; + pTracker->SendBuffer.pDgramHdr = (PVOID)pNameHdr; + pTracker->SendBuffer.pBuffer = NULL; + pTracker->SendBuffer.Length = 0; + + status = UdpSendDatagram(pTracker, + SrcIpAddress, + pDeviceContext->pNameServerFileObject, + QueryRespDone, // this routine frees memory and puts the tracker back + pTracker, + SrcPort, // NBT_NAMESERVICE_UDP_PORT 31343 - reply to port request came on.... + NBT_NAME_SERVICE); + } + + return(status); + +ErrorExit: + + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +UpdateNameState( + IN tADDSTRUCT UNALIGNED *pAddrStruct, + IN tNAMEADDR *pNameAddr, + IN ULONG Len, + IN tDEVICECONTEXT *pDeviceContext, + IN BOOLEAN NameServerIsSrc, + IN tDGRAM_SEND_TRACKING *pTracker, + IN CTELockHandle OldIrq1 + ) +/*++ + +Routine Description: + + This routine handles putting a list of names into the hash table when + a response is received that contains one or more than one ip address. + +Arguments: + + +Return Value: + + none + +--*/ +{ + + LONG CountAddrs; + LONG i; + tIPLIST *pIpList; + ULONG ExtraNames; + NTSTATUS status; + CTELockHandle OldIrq; + + // a pos. response to a previous query, so change the state in the + // hash table to RESOLVED + if (pNameAddr->NameTypeState & STATE_RESOLVING) + { + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_RESOLVED; + + // check if a group name... + // + if (ntohs(pAddrStruct->NbFlags) & FL_GROUP) + { + pNameAddr->NameTypeState &= ~NAME_TYPE_MASK; + + // it is difficult to differentiate nameserver responses from + // single node responses, when a single ip address is returned. + // It could be the name server returning an Inet Group with one + // entry or another node simply responding with its address. The + // only way to check is to know if the source was a nameserver. + // + if ((Len == sizeof(tADDSTRUCT)) && + (!NameServerIsSrc || + (NameServerIsSrc && (pAddrStruct->IpAddr == (ULONG)-1)))) + { + + // using zero here tells the UdpSendDatagramCode to + // send to the subnet broadcast address when talking to + // that address. + CHECK_PTR(pNameAddr); + // + // For Bnodes store the Address of the node that responded + // to the group name query, since we do allow sessions to + // group names for BNODES since they can resolve the name to + // and IP address, whereas other nodes cannot. + // +// store the ipaddr regardless of nodetype. We don't know if this info will be +// used to setup a session or send a datagram. We do check NameTypeState +// while setting up session, so no need to filter out NodeType info here. + + // + // WINS puts -1 to say it's a groupname + // + if ( pAddrStruct->IpAddr == (ULONG)-1 ) + pNameAddr->IpAddress = 0; + else + pNameAddr->IpAddress = ntohl(pAddrStruct->IpAddr); + + pNameAddr->NameTypeState |= NAMETYPE_GROUP; + } + else + { + // + // one or more addresses were returned, so put them into a list + // that is pointed to by the pNameAddr record + // + CountAddrs = Len / sizeof(tADDSTRUCT); + + if (CountAddrs > NBT_MAX_INTERNET_GROUP_ADDRS) + { + // probably a badly formated packet because the max number + // is 1000 + return(STATUS_UNSUCCESSFUL); + } + + ExtraNames = 2; + // + // allocate memory for the Internet Group address (+1 because + // there is a null ptr on the end of the list, +1 more for the + // broadcast address, and maybe +1 more for the local node) + // + pIpList = NbtAllocMem((USHORT)((CountAddrs+ExtraNames)*sizeof(tIPLIST)),NBT_TAG('B')); + if ( !pIpList ) + return STATUS_INSUFFICIENT_RESOURCES ; + + // + // put the broadcast address first + // + pIpList->IpAddr[0] = 0; + + // extract the ipaddress from each address structure + for ( i = 1 ; i < CountAddrs+1; i++ ) + { + + pIpList->IpAddr[i] = ntohl(pAddrStruct->IpAddr); + pAddrStruct++; + + } + + // mark the end of the list + CHECK_PTR(pIpList); + pIpList->IpAddr[CountAddrs+1] = (ULONG)-1; + + pNameAddr->pIpList = pIpList; + pNameAddr->NameTypeState |= NAMETYPE_INET_GROUP; + } + } + else + { + if (Len > sizeof(tADDSTRUCT)) + { + ULONG IpAddress; + NBT_WORK_ITEM_CONTEXT *pContext; + + // the name query response contains several ip addresses for + // a multihomed host, so pick an address that matches one of + // our subnet masks + // + // Do the old thing for datagram sends/name queries. + // + if ((NbtConfig.TryAllAddr) && + (pTracker->Flags & SESSION_SETUP_FLAG)) { + + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Kicking off CheckAddr : %lx\n", pAddrStruct)); + + ChooseBestIpAddress(pAddrStruct,Len,pDeviceContext, pTracker, &IpAddress, TRUE); + + // + // At this point, pTracker->IPList contains the sorted list of destination + // IP addresses. Submit this list to the lmhsvc service to ping each and + // return whic is reachable. + // + pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('H')); + if (!pContext) + { + KdPrint(("Nbt: NbtConnect: couldn't alloc mem for pContext\n")); + return(STATUS_SUCCESS); + } + + pContext->pTracker = NULL; // no query tracker + pContext->pClientContext = pTracker; // the client tracker + pContext->ClientCompletion = SessionSetupContinue; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + status = DoCheckAddr(pContext); + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + if (!NT_SUCCESS(status)) { + KdPrint(("Nbt: DoCheckAddr returned %lx\n", status)); + } + + return (status); // shd be STATUS_PENDING + } else { + + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Choosing best IP addr...\n")); + + ChooseBestIpAddress(pAddrStruct,Len,pDeviceContext, pTracker, &IpAddress, FALSE); + pNameAddr->IpAddress = IpAddress; + } + } + else + { + // it is already set to a unique address...since that is the default + // when the name is queried originally. + + pNameAddr->IpAddress = ntohl(pAddrStruct->IpAddr); + } + + } + } + + status = AddRecordToHashTable(pNameAddr,NbtConfig.pScope); + if (!NT_SUCCESS(status)) + { + // + // the name must already be in the hash table, so dereference it to + // remove it + // + NbtDereferenceName(pNameAddr); + } + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +ULONG +MakeList( + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG CountAddrs, + IN tADDSTRUCT UNALIGNED *pAddrStruct, + IN PULONG AddrArray, + IN ULONG SizeOfAddrArray, + IN BOOLEAN IsSubnetMatch + ) +/*++ + +Routine Description: + + This routine gets a list of ip addresses that match the network number + This can either be the subnet number or the network number depending + on the boolean IsSubnetMatch + +Arguments: + + +Return Value: + + none + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDEVICECONTEXT *pTmpDevContext; + ULONG MatchAddrs = 0; + tADDSTRUCT UNALIGNED *pAddrs; + ULONG i; + ULONG IpAddr; + ULONG NetworkNumber; + ULONG NetworkNumberInIpAddr; + UCHAR IpAddrByte; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pAddrs = pAddrStruct; + + pTmpDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + // + // DeviceContext is non-null, if a check has to be made on a specific + // DeviceContext. Otherwise it's null (i.e. check all DeviceContexts) + // + if (pDeviceContext) + { + if (pTmpDevContext != pDeviceContext) + continue; + } + if (IsSubnetMatch) + { + NetworkNumber = pTmpDevContext->SubnetMask & pTmpDevContext->IpAddress; + } + else + { + NetworkNumber = pTmpDevContext->NetMask; + } + + // extract the ipaddress from each address structure + for ( i = 0 ; i < CountAddrs; i++ ) + { + + NetworkNumberInIpAddr = IpAddr = ntohl(pAddrs->IpAddr); + + if (IsSubnetMatch) + { + if (((pTmpDevContext->SubnetMask & IpAddr) == NetworkNumber) && + (MatchAddrs < SizeOfAddrArray/sizeof(ULONG))) + { + // put the ipaddress into a list incase multiple match + // and we want to select one randomly + // + AddrArray[MatchAddrs++] = IpAddr; + + } + pAddrs++; + } + else + { + IpAddrByte = ((PUCHAR)&IpAddr)[3]; + if ((IpAddrByte & 0x80) == 0) + { + // class A address - one byte netid + NetworkNumberInIpAddr &= 0xFF000000; + } + else + if ((IpAddrByte & 0xC0) ==0x80) + { + // class B address - two byte netid + NetworkNumberInIpAddr &= 0xFFFF0000; + } + else + if ((IpAddrByte & 0xE0) ==0xC0) + { + // class C address - three byte netid + NetworkNumberInIpAddr &= 0xFFFFFF00; + } + if ((NetworkNumberInIpAddr == NetworkNumber) && + (MatchAddrs < SizeOfAddrArray/sizeof(ULONG))) + { + // put the ipaddress into a list incase multiple match + // and we want to select one randomly + // + AddrArray[MatchAddrs++] = IpAddr; + + } + pAddrs++; + } + } + } + + return(MatchAddrs); +} +//---------------------------------------------------------------------------- +NTSTATUS +ChooseBestIpAddress( + IN tADDSTRUCT UNALIGNED *pAddrStruct, + IN ULONG Len, + IN tDEVICECONTEXT *pDeviceContext, + OUT tDGRAM_SEND_TRACKING *pTracker, + OUT PULONG pIpAddress, + IN BOOLEAN fTryAllAddr + ) +/*++ + +Routine Description: + + This routine gets a list of ip addresses and attempts to pick one of them + as the best address. This occurs when WINS returns a list of addresses + for a multihomed host and we want want the one that is on a subnet + corresponding to one of the network cards. Failing to match on + subnet mask, results in a random selection from the addresses. + +Arguments: + + +Return Value: + + none + +--*/ +{ + + ULONG CountAddrs; + CTESystemTime TimeValue; + ULONG Random; + ULONG AddrArray[60]; + ULONG MatchAddrs = 0; + + // one or more addresses were returned, + // so pick one that is best + // + CountAddrs = Len / sizeof(tADDSTRUCT); + + if (CountAddrs*sizeof(tADDSTRUCT) == Len) + { + + // + // First check if any addresses are on the same subnet as this + // devicecontext. + // + MatchAddrs = MakeList( + pDeviceContext, + CountAddrs, + pAddrStruct, + AddrArray, + sizeof(AddrArray), + TRUE); + + // + // if none of the ipaddrs is on the same subnet as this DeviceContext, + // try other DeviceContexts + // + if (!MatchAddrs) + { + MatchAddrs = MakeList( + NULL, + CountAddrs, + pAddrStruct, + AddrArray, + sizeof(AddrArray), + TRUE); + } + + // + // if none of the addresses match the subnet address of any of the + // DeviceContexts, then go through the same check looking for matches + // that have the same network number. + // + if (!MatchAddrs) + { + MatchAddrs = MakeList( + NULL, + CountAddrs, + pAddrStruct, + AddrArray, + sizeof(AddrArray), + FALSE); + } + } + else + { + // the pdu length is not an even multiple of the tADDSTRUCT data + // structure + return(STATUS_UNSUCCESSFUL); + } + + // calls the kernel routine to get the system time. + // + CTEQuerySystemTime(TimeValue); + + + if (MatchAddrs) + { + if (!fTryAllAddr) { + + Random = RandomizeFromTime( TimeValue, MatchAddrs ) ; + *pIpAddress = AddrArray[Random]; + } else { + ULONG i,j; + tADDSTRUCT temp; + + // + // Sort the IP addr list on basis of best IP addr. in AddrArray + // + // NOTE: this is not a strictly sorted list (the actual sort might be too expensive), + // instead we take all the addresses that match the subnet mask (say) and + // clump the remaining ones in the same group. This way we ensure that whatever + // we chose as the best address is still given preference as compared to the + // other addresses. + // + for (i=0; i<MatchAddrs; i++ ) { + + // + // SWAP(pAddrStruct[i], pAddrStruct[Index(AddrArray[i])]); + // + + // + // Index(AddrArray[i]) is final j + // + for (j=0; j<CountAddrs; j++) { + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:pAddrStruct[%d] : %lx AddrArray[%d]: %lx\n", j, pAddrStruct[j].IpAddr, i, AddrArray[i])); + if (pAddrStruct[j].IpAddr == (ULONG)ntohl(AddrArray[i])) { + break; + } + } + + ASSERT(j < CountAddrs); + + temp = pAddrStruct[i]; + pAddrStruct[i] = pAddrStruct[j]; + pAddrStruct[j] = temp; + } + } + } + else + { + // + // Submit the list to lmhsvc as it is... + // + + if (!fTryAllAddr) { + Random = RandomizeFromTime( TimeValue, CountAddrs ) ; + + // + // if we get to here then the returned list of ip addresses does not + // match the network number of any of our adapters, so therefore just + // pick one randomly from the original list + // + *pIpAddress = htonl(pAddrStruct[Random].IpAddr); + } + } + + if (fTryAllAddr) { + ULONG j; + + pTracker->IpList = NbtAllocMem(sizeof(ULONG)*CountAddrs,NBT_TAG('8')); + + if (pTracker->IpList) { + for (j=0; j<CountAddrs; j++) { + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:pAddrStruct[%d]: %lx\n", j, pAddrStruct[j].IpAddr)); + pTracker->IpList[j] = pAddrStruct[j].IpAddr; + } + pTracker->IpList[j] = 0; + pTracker->NumAddrs = CountAddrs; + } else { + return (STATUS_INSUFFICIENT_RESOURCES); + } + } + +} +//---------------------------------------------------------------------------- +BOOLEAN +SrcIsNameServer( + IN ULONG SrcAddress, + IN USHORT SrcPort + ) +/*++ + +Routine Description: + + This function checks the src address against all adapters' name server + address to see if it came from a name server. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL + + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDEVICECONTEXT *pDevContext; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead->Flink; + + if (SrcPort == NbtConfig.NameServerPort) + { + while (pEntry != pHead) + { + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + if ((pDevContext->lNameServerAddress == SrcAddress) || + (pDevContext->lBackupServer == SrcAddress)) + { + return(TRUE); + } + pEntry = pEntry->Flink; + } + } +#ifndef VXD + // + // If wins is on this machine the above SrcIsNameServer + // check may not be sufficient since this machine is + // the name server and that check checks the nameservers + // used for name queries. If WINS is on this machine it will send + // from the first adapter's ip address. + // + if (pWinsInfo && + (pWinsInfo->pDeviceContext->IpAddress == SrcAddress)) + { + return(TRUE); + } + +#endif + return(FALSE); + +} + + +//---------------------------------------------------------------------------- +BOOLEAN +SrcIsUs( + IN ULONG SrcAddress + ) +/*++ + +Routine Description: + + This function checks the src address against all adapters'Ip addresses + address to see if it came from this node. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL + + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDEVICECONTEXT *pDevContext; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead->Flink; + + while (pEntry != pHead) + { + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + if (pDevContext->IpAddress == SrcAddress) + { + return(TRUE); + } + pEntry = pEntry->Flink; + } + + return(FALSE); + +} +//---------------------------------------------------------------------------- +VOID +SwitchToBackup( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This function switches the primary and backup name server addresses. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL + + +--*/ +{ + ULONG SaveAddr; + + SaveAddr = pDeviceContext->lNameServerAddress; + + // + // Bug: 30511: Dont switch servers if no backup. + // + if (pDeviceContext->lBackupServer == LOOP_BACK) { + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt:Will not Switch to backup name server: devctx: %X, refreshtobackup=%X\n", + pDeviceContext, pDeviceContext->RefreshToBackup)); + return; + } + + pDeviceContext->lNameServerAddress = pDeviceContext->lBackupServer; + pDeviceContext->lBackupServer = SaveAddr; + + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt:Switching to backup name server: devctx: %X, refreshtobackup=%X\n", + pDeviceContext, pDeviceContext->RefreshToBackup)); + + // keep track if we are on the backup or not. + pDeviceContext->RefreshToBackup = ~pDeviceContext->RefreshToBackup; + +} + +//---------------------------------------------------------------------------- +USHORT +GetNbFlags( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN ULONG lNameSize + ) +/*++ + +Routine Description: + + This function finds the Nbflags field in certain pdu types and returns it. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_REMOTE_NOT_LISTENING + +Called By: RegResponseFromNet + +--*/ +{ + USHORT RegType; + ULONG QuestionNameLen; + + // + // if the question name is not a pointer to the first name then we + // must find the end of that name and adjust it so when added to + // to lNameSize we endup at NB_FLAGS + // + if (( pNameHdr->NameRR.NetBiosName[lNameSize+PTR_OFFSET] & PTR_SIGNATURE) + != PTR_SIGNATURE) + { + // add one to include the null on the end of the string + the NB, IN TTL, + // and length fields( another 10 bytes NO_PTR_OFFSET) + PTR_OFFSET(4). + // + QuestionNameLen = + strlen((PUCHAR)&pNameHdr->NameRR.NetBiosName[lNameSize+PTR_OFFSET]) + +1 + NO_PTR_OFFSET + PTR_OFFSET; + } + else + { + QuestionNameLen = NBFLAGS_OFFSET; + } + + RegType = ntohs((USHORT)pNameHdr->NameRR.NetBiosName[lNameSize + +QuestionNameLen ]); + return(RegType); +} +//---------------------------------------------------------------------------- +NTSTATUS +FindOnPendingList( + IN PUCHAR pName, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN BOOLEAN DontCheckTransactionId, + IN ULONG BytesToCompare, + OUT tNAMEADDR **ppNameAddr + + ) +/*++ + +Routine Description: + + This function is called to look for a name query request on the pending + list. It searches the list linearly to look for the name. + + The Joint Lock is held when calling this routine. + + +Arguments: + + +Return Value: + + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tNAMEADDR *pNameAddr; + tTIMERQENTRY *pTimer; + + pHead = pEntry = &NbtConfig.PendingNameQueries; + + while ((pEntry = pEntry->Flink) != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + + // + // there could be multiple entries with the same name so check the + // transaction id too + // + if (DontCheckTransactionId || + ((pTimer = pNameAddr->pTimer) && + (((tDGRAM_SEND_TRACKING *)pTimer->Context)->TransactionId == pNameHdr->TransactId)) + && + (CTEMemEqu(pNameAddr->Name,pName,BytesToCompare))) + { + *ppNameAddr = pNameAddr; + return(STATUS_SUCCESS); + } + } + + + return(STATUS_UNSUCCESSFUL); +} + + + +#if DBG +//---------------------------------------------------------------------------- +VOID +PrintHexString( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN ULONG lNumBytes + ) +/*++ + +Routine Description: + + This function is called to determine whether the name packet we heard on + the net has the same address as the one in the remote hash table. + If it has the same address or if it does not have any address, we return + SUCCESS, else we return NOT_LISTENING. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_REMOTE_NOT_LISTENING + +Called By: RegResponseFromNet + +--*/ +{ + ULONG i,Count=0; + PUCHAR pHdr=(PUCHAR)pNameHdr; + + for (i=0;i<lNumBytes ;i++ ) + { + KdPrint(("%2.2X ",*pHdr)); + pHdr++; + if (Count >= 16) + { + Count = 0; + KdPrint(("\n")); + } + else + Count++; + } + KdPrint(("\n")); +} +#endif + +#ifdef PROXY_NODE +//---------------------------------------------------------------------------- +NTSTATUS +ChkIfValidRsp( + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNameSize, + IN tNAMEADDR *pNameAddr + ) +/*++ + +Routine Description: + + This function is called to determine whether the name packet we heard on + the net has the same address as the one in the remote hash table. + If it has the same address or if it does not have any address, we return + SUCCESS, else we return NOT_LISTENING. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_REMOTE_NOT_LISTENING + +Called By: RegResponseFromNet + +--*/ +{ + ULONG IpAdd; + + IpAdd = ntohl( + pNameHdr->NameRR.NetBiosName[lNameSize+IPADDRESS_OFFSET] + ); + + // + // If the IP address in the packet received is same as the one + // in the table we return success, else we are not interested + // in the packet (we want to just drop the packet) + // + if ( + (IpAdd == pNameAddr->IpAddress) + ) + { + return(STATUS_SUCCESS); + } + else + { + return(STATUS_REMOTE_NOT_LISTENING); + } +} +#endif + + diff --git a/private/ntos/nbt/nbt/init.c b/private/ntos/nbt/nbt/init.c new file mode 100644 index 000000000..e69827f8b --- /dev/null +++ b/private/ntos/nbt/nbt/init.c @@ -0,0 +1,1037 @@ +/**********************************************************************/ +/** Microsoft Windows/NT **/ +/** Copyright(c) Microsoft Corp., 1992 **/ +/**********************************************************************/ + +/* + Init.c + + OS Independent initialization routines + + + + FILE HISTORY: + Johnl 26-Mar-1993 Created +*/ + + +#include "nbtnt.h" +#include "nbtprocs.h" + +VOID +ReadScope( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ); + +VOID +ReadLmHostFile( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(INIT, InitNotOs) +#ifdef _PNP_POWER +#pragma CTEMakePageable(PAGE, InitTimersNotOs) +#pragma CTEMakePageable(PAGE, StopInitTimers) +#else // _PNP_POWER +#pragma CTEMakePageable(INIT, InitTimersNotOs) +#pragma CTEMakePageable(INIT, StopInitTimers) +#endif // _PNP_POWER +#pragma CTEMakePageable(PAGE, ReadParameters) +#pragma CTEMakePageable(PAGE, ReadParameters2) +#pragma CTEMakePageable(PAGE, ReadScope) +#pragma CTEMakePageable(PAGE, ReadLmHostFile) +#endif +//******************* Pageable Routine Declarations **************** + +#ifdef VXD +#pragma BEGIN_INIT +#endif + +//---------------------------------------------------------------------------- +NTSTATUS +InitNotOs( + void + ) + +/*++ + +Routine Description: + + This is the initialization routine for the Non-OS Specific side of the + NBT device driver. + + pNbtGlobConfig must be initialized before this is called! + +Arguments: + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG i; + + + CTEPagedCode(); + // + // for multihomed hosts, this tracks the number of adapters as each one + // is created. + // + NbtConfig.AdapterCount = 0; + NbtConfig.MultiHomed = FALSE; + NbtConfig.SingleResponse = FALSE; + + + // + // Initialize the name statistics + // + CTEZeroMemory( &NameStatsInfo,sizeof(tNAMESTATS_INFO) ); + + InitializeListHead(&pNbtGlobConfig->DeviceContexts); + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + InitializeListHead(&pNbtGlobConfig->FreeDevCtx); +#endif + + InitializeListHead(&NbtConfig.AddressHead); + InitializeListHead(&NbtConfig.PendingNameQueries); + +#ifdef VXD + InitializeListHead(&NbtConfig.DNSDirectNameQueries); +#endif + + // initialize the spin lock + CTEInitLock(&pNbtGlobConfig->SpinLock); + CTEInitLock(&pNbtGlobConfig->JointLock.SpinLock); + NbtConfig.LockNumber = NBTCONFIG_LOCK; + NbtConfig.JointLock.LockNumber = JOINT_LOCK; + NbtMemoryAllocated = 0; + +#if DBG + for (i=0;i<MAXIMUM_PROCESSORS ;i++ ) + { + NbtConfig.CurrentLockNumber[i] = 0; + } +#endif + InitializeListHead(&UsedTrackers); + InitializeListHead(&UsedIrps); + + // create the hash tables for storing names in. + status = CreateHashTable(&pNbtGlobConfig->pLocalHashTbl, + pNbtGlobConfig->uNumBucketsLocal, + NBT_LOCAL); + + if ( !NT_SUCCESS( status ) ) + { + ASSERTMSG("NBT:Unable to create hash tables for Netbios Names\n", + (status == STATUS_SUCCESS)); + return status ; + } + // we always have a remote hash table, but if we are a Proxy, it is + // a larger table. In the Non-proxy case the remote table just caches + // names resolved with the NS. In the Proxy case it also holds names + // resolved for all other clients on the local broadcast area. + // The node size registry parameter controls the number of remote buckets. + status = InitRemoteHashTable(pNbtGlobConfig, + pNbtGlobConfig->uNumBucketsRemote, + pNbtGlobConfig->uNumRemoteNames); + + if ( !NT_SUCCESS( status ) ) + return status ; + + // + // initialize the linked lists associated with the global configuration + // data structures + // + InitializeListHead(&NbtConfig.NodeStatusHead); + InitializeListHead(&NbtConfig.DgramTrackerFreeQ); +#ifndef VXD + InitializeListHead(&NbtConfig.IrpFreeList); + pWinsInfo = 0; + + { + + // + // Setup the default disconnect timeout - 10 seconds - convert + // to negative 100 Ns. + // + DefaultDisconnectTimeout.QuadPart = Int32x32To64(DEFAULT_DISC_TIMEOUT, + MILLISEC_TO_100NS); + DefaultDisconnectTimeout.QuadPart = -(DefaultDisconnectTimeout.QuadPart); + } + +#else + DefaultDisconnectTimeout = DEFAULT_DISC_TIMEOUT * 1000; // convert to milliseconds + + InitializeListHead(&NbtConfig.SendTimeoutHead) ; + InitializeListHead(&NbtConfig.SessionBufferFreeList) ; + InitializeListHead(&NbtConfig.SendContextFreeList) ; + InitializeListHead(&NbtConfig.RcvContextFreeList) ; + + // + // For session headers, since they are only four bytes and we can't + // change the size of the structure, we'll covertly add enough for + // a full LIST_ENTRY and treat it like a standalone LIST_ENTRY structure + // when adding and removing from the list. + // + NbtConfig.iBufferSize[eNBT_SESSION_HDR] = sizeof(tSESSIONHDR) + + sizeof( LIST_ENTRY ) - sizeof(tSESSIONHDR) ; + NbtConfig.iBufferSize[eNBT_SEND_CONTEXT] = sizeof(TDI_SEND_CONTEXT); + NbtConfig.iBufferSize[eNBT_RCV_CONTEXT] = sizeof(RCV_CONTEXT); + + NbtConfig.iCurrentNumBuff[eNBT_SESSION_HDR] = NBT_INITIAL_NUM; + status = NbtInitQ( &NbtConfig.SessionBufferFreeList, + NbtConfig.iBufferSize[eNBT_SESSION_HDR], + NBT_INITIAL_NUM); + + if ( !NT_SUCCESS( status ) ) + return status ; + + NbtConfig.iCurrentNumBuff[eNBT_SEND_CONTEXT] = NBT_INITIAL_NUM; + status = NbtInitQ( &NbtConfig.SendContextFreeList, + sizeof( TDI_SEND_CONTEXT ), + NBT_INITIAL_NUM); + + if ( !NT_SUCCESS( status ) ) + return status ; + + NbtConfig.iCurrentNumBuff[eNBT_RCV_CONTEXT] = NBT_INITIAL_NUM; + status = NbtInitQ( &NbtConfig.RcvContextFreeList, + sizeof( RCV_CONTEXT ), + NBT_INITIAL_NUM); + + if ( !NT_SUCCESS( status ) ) + return status ; +#endif + + // + // create trackers List + // + pNbtGlobConfig->iBufferSize[eNBT_DGRAM_TRACKER] = + sizeof(tDGRAM_SEND_TRACKING); + + NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER] = 0; + + status = NbtInitTrackerQ( &NbtConfig.DgramTrackerFreeQ,NBT_INITIAL_NUM); + + if ( !NT_SUCCESS( status ) ) + return status ; + + + CTEZeroMemory(&LmHostQueries,sizeof(tLMHOST_QUERIES)); + InitializeListHead(&DomainNames.DomainList); + InitializeListHead(&LmHostQueries.ToResolve); + +#ifndef VXD + // set up a list for connections when we run out of resources and need to + // disconnect these connections. An Irp is also needed for this list, and + // it is allocated in Driver.C after we have created the connections to the + // transport and therefore know our Irp Stack Size. + // + InitializeListHead(&NbtConfig.OutOfRsrc.ConnectionHead); + + // use this resources to synchronize access to the security info between + // assigning security and checking it - when adding names to the + // name local name table through NbtregisterName. This also insures + // that the name is in the local hash table (from a previous Registration) + // before the next registration is allowed to proceed and check for + // the name in the table. + // + ExInitializeResource(&NbtConfig.Resource); + + // + // this resource is used to synchronize access to the Dns structure + // + CTEZeroMemory(&DnsQueries,sizeof(tDNS_QUERIES)); + + InitializeListHead(&DnsQueries.ToResolve); + + // + // this resource is used to synchronize access to the Dns structure + // + CTEZeroMemory(&CheckAddr,sizeof(tCHECK_ADDR)); + + InitializeListHead(&CheckAddr.ToResolve); +#endif // VXD + + return status ; +} + +//---------------------------------------------------------------------------- +NTSTATUS +InitTimersNotOs( + void + ) + +/*++ + +Routine Description: + + This is the initialization routine for the Non-OS Specific side of the + NBT device driver that starts the timers needed. + +Arguments: + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + tTIMERQENTRY *pTimerEntry; + + CTEPagedCode(); + // create the timer control blocks, setting the number of concurrent timers + // allowed at one time + status = InitTimerQ(NBT_INITIAL_NUM); + NbtConfig.iBufferSize[eNBT_TIMER_ENTRY] = sizeof(tTIMERQENTRY); + NbtConfig.iCurrentNumBuff[eNBT_TIMER_ENTRY] = NBT_INITIAL_NUM; + + + NbtConfig.pRefreshTimer = NULL; + NbtConfig.pRemoteHashTimer = NULL; + NbtConfig.pSessionKeepAliveTimer = NULL; + NbtConfig.RefreshDivisor = REFRESH_DIVISOR; + + if ( !NT_SUCCESS( status ) ) + return status ; + + // start a Timer to refresh names with the name service + // + if (!(NodeType & BNODE)) + { + + // the initial refresh rate until we can contact the name server + NbtConfig.MinimumTtl = NbtConfig.InitialRefreshTimeout; + NbtConfig.sTimeoutCount = 0; + + status = StartTimer( + NbtConfig.InitialRefreshTimeout/REFRESH_DIVISOR, + NULL, // context value + NULL, // context2 value + RefreshTimeout, + NULL, + NULL, + 0, + &pTimerEntry); + + if ( !NT_SUCCESS( status ) ) + return status ; + + NbtConfig.pRefreshTimer = pTimerEntry; + } + + // calculate the count necessary to timeout out names in RemoteHashTimeout + // milliseconds + // + NbtConfig.RemoteTimeoutCount = (USHORT)((NbtConfig.RemoteHashTimeout/REMOTE_HASH_TIMEOUT)); + if (NbtConfig.RemoteTimeoutCount == 0) + { + NbtConfig.RemoteTimeoutCount = 1; + } + + // start a Timer to timeout remote cached names from the Remote hash table. + // The timer is a one minute timer, and the hash entries count down to zero + // then time out. + // + status = StartTimer( + REMOTE_HASH_TIMEOUT, + NULL, // context value + NULL, // context2 value + RemoteHashTimeout, // timer expiry routine + NULL, + NULL, + 0, + &pTimerEntry); + + + if ( !NT_SUCCESS( status ) ) + { + StopInitTimers(); + return status ; + } + + NbtConfig.pRemoteHashTimer = pTimerEntry; + + // start a Timer for Session Keep Alives which sends a session keep alive + // on a connection if the timer value is not set to -1 + // + if (NbtConfig.KeepAliveTimeout != -1) + { + status = StartTimer( + NbtConfig.KeepAliveTimeout, + NULL, // context value + NULL, // context2 value + SessionKeepAliveTimeout, // timer expiry routine + NULL, + NULL, + 0, + &pTimerEntry); + + if ( !NT_SUCCESS( status ) ) + { + StopInitTimers(); + return status ; + } + + NbtConfig.pSessionKeepAliveTimer = pTimerEntry; + } + + + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +StopInitTimers( + VOID + ) + +/*++ + +Routine Description: + + This is stops the timers started in InitTimerNotOS + +Arguments: + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + CTEPagedCode(); + + if (NbtConfig.pRefreshTimer) + { + StopTimer(NbtConfig.pRefreshTimer,NULL,NULL); + } + if (NbtConfig.pSessionKeepAliveTimer) + { + StopTimer(NbtConfig.pSessionKeepAliveTimer,NULL,NULL); + } + if (NbtConfig.pRemoteHashTimer) + { + StopTimer(NbtConfig.pRemoteHashTimer,NULL,NULL); + } + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +VOID +ReadParameters( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ) + +/*++ + +Routine Description: + + This routine is called to read various parameters from the parameters + section of the NBT section of the registry. + +Arguments: + + pConfig - A pointer to the configuration data structure. + ParmHandle - a handle to the parameters Key under Nbt + +Return Value: + + Status + +--*/ + +{ + ULONG NodeSize; + ULONG Refresh; + + CTEPagedCode(); + + ReadParameters2(pConfig, ParmHandle); + + pConfig->NameServerPort = (USHORT)CTEReadSingleIntParameter(ParmHandle, + WS_NS_PORT_NUM, + NBT_NAMESERVER_UDP_PORT, + 0); +#ifdef VXD + pConfig->DnsServerPort = (USHORT)CTEReadSingleIntParameter(ParmHandle, + WS_DNS_PORT_NUM, + NBT_DNSSERVER_UDP_PORT, + 0); + + pConfig->lRegistryMaxNames = (USHORT)CTEReadSingleIntParameter( NULL, + VXD_NAMETABLE_SIZE_NAME, + VXD_DEF_NAMETABLE_SIZE, + VXD_MIN_NAMETABLE_SIZE ) ; + + pConfig->lRegistryMaxSessions = (USHORT)CTEReadSingleIntParameter( NULL, + VXD_SESSIONTABLE_SIZE_NAME, + VXD_DEF_SESSIONTABLE_SIZE, + VXD_MIN_SESSIONTABLE_SIZE ) ; + + +#endif + + pConfig->RemoteHashTimeout = CTEReadSingleIntParameter(ParmHandle, + WS_CACHE_TIMEOUT, + DEFAULT_CACHE_TIMEOUT, + MIN_CACHE_TIMEOUT); + pConfig->InitialRefreshTimeout = CTEReadSingleIntParameter(ParmHandle, + WS_INITIAL_REFRESH, + NBT_INITIAL_REFRESH_TTL, + NBT_INITIAL_REFRESH_TTL); + + // retry timeouts and number of retries for both Broadcast name resolution + // and Name Service resolution + // + pConfig->uNumBcasts = (USHORT)CTEReadSingleIntParameter(ParmHandle, + WS_NUM_BCASTS, + DEFAULT_NUMBER_BROADCASTS, + 1); + + pConfig->uBcastTimeout = CTEReadSingleIntParameter(ParmHandle, + WS_BCAST_TIMEOUT, + DEFAULT_BCAST_TIMEOUT, + MIN_BCAST_TIMEOUT); + + pConfig->uNumRetries = (USHORT)CTEReadSingleIntParameter(ParmHandle, + WS_NAMESRV_RETRIES, + DEFAULT_NUMBER_RETRIES, + 1); + + pConfig->uRetryTimeout = CTEReadSingleIntParameter(ParmHandle, + WS_NAMESRV_TIMEOUT, + DEFAULT_RETRY_TIMEOUT, + MIN_RETRY_TIMEOUT); + + pConfig->KeepAliveTimeout = CTEReadSingleIntParameter(ParmHandle, + WS_KEEP_ALIVE, + DEFAULT_KEEP_ALIVE, + MIN_KEEP_ALIVE); + + pConfig->SelectAdapter = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle, + WS_RANDOM_ADAPTER, + 0, + 0); + pConfig->SingleResponse = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle, + WS_SINGLE_RESPONSE, + 0, + 0); + pConfig->ResolveWithDns = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle, + WS_ENABLE_DNS, + 0, + 0); + pConfig->TryAllAddr = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle, + WS_TRY_ALL_ADDRS, + 1, + 1); // enabled by default + pConfig->LmHostsTimeout = CTEReadSingleIntParameter(ParmHandle, + WS_LMHOSTS_TIMEOUT, + DEFAULT_LMHOST_TIMEOUT, + MIN_LMHOST_TIMEOUT); + pConfig->MaxDgramBuffering = CTEReadSingleIntParameter(ParmHandle, + WS_MAX_DGRAM_BUFFER, + DEFAULT_DGRAM_BUFFERING, + DEFAULT_DGRAM_BUFFERING); + + pConfig->EnableProxyRegCheck = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle, + WS_ENABLE_PROXY_REG_CHECK, + 0, + 0); + + pConfig->WinsDownTimeout = (ULONG)CTEReadSingleIntParameter(ParmHandle, + WS_WINS_DOWN_TIMEOUT, + DEFAULT_WINS_DOWN_TIMEOUT, + MIN_WINS_DOWN_TIMEOUT); + + pConfig->MaxBackLog = (ULONG)CTEReadSingleIntParameter(ParmHandle, + WS_MAX_CONNECTION_BACKLOG, + DEFAULT_CONN_BACKLOG, + MIN_CONN_BACKLOG); + + pConfig->SpecialConnIncrement = (ULONG)CTEReadSingleIntParameter(ParmHandle, + WS_CONNECTION_BACKLOG_INCREMENT, + DEFAULT_CONN_BACKLOG_INCREMENT, + MIN_CONN_BACKLOG_INCREMENT); + + // + // Cap the upper limit + // + if (pConfig->MaxBackLog > MAX_CONNECTION_BACKLOG) { + pConfig->MaxBackLog = MAX_CONNECTION_BACKLOG; + } + + if (pConfig->SpecialConnIncrement > MAX_CONNECTION_BACKLOG_INCREMENT) { + pConfig->SpecialConnIncrement = MAX_CONNECTION_BACKLOG_INCREMENT; + } + + + // + // Since UB chose the wrong opcode (9) we have to allow configuration + // of that opcode incase our nodes refresh to their NBNS + // + Refresh = (ULONG)CTEReadSingleIntParameter(ParmHandle, + WS_REFRESH_OPCODE, + REFRESH_OPCODE, + REFRESH_OPCODE); + if (Refresh == UB_REFRESH_OPCODE) + { + pConfig->OpRefresh = OP_REFRESH_UB; + } + else + { + pConfig->OpRefresh = OP_REFRESH; + } + +#ifndef VXD + pConfig->EnableLmHosts = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle, + WS_ENABLE_LMHOSTS, + 0, + 0); +#endif + +#ifdef PROXY_NODE + + { + ULONG Proxy; + Proxy = CTEReadSingleIntParameter(ParmHandle, + WS_IS_IT_A_PROXY, + IS_NOT_PROXY, //default value + IS_NOT_PROXY); + + // + // If the returned value is greater than IS_NOT_PROXY, it is a proxy + // (also check that they have not entered an ascii string instead of a + // dword in the registry + // + if ((Proxy > IS_NOT_PROXY) && (Proxy < ('0'+IS_NOT_PROXY))) + { + NodeType |= PROXY; + } + } +#endif + NodeSize = CTEReadSingleIntParameter(ParmHandle, + WS_NODE_SIZE, + NodeType & PROXY ? LARGE : DEFAULT_NODE_SIZE, + NodeType & PROXY ? LARGE : SMALL); + + switch (NodeSize) + { + default: + case SMALL: + + pConfig->uNumLocalNames = NUMBER_LOCAL_NAMES; + pConfig->uNumRemoteNames = NUMBER_REMOTE_NAMES; + pConfig->uNumBucketsLocal = NUMBER_BUCKETS_LOCAL_HASH_TABLE; + pConfig->uNumBucketsRemote = NUMBER_BUCKETS_REMOTE_HASH_TABLE; + + pConfig->iMaxNumBuff[eNBT_DGRAM_TRACKER] = NBT_NUM_DGRAM_TRACKERS; + pConfig->iMaxNumBuff[eNBT_TIMER_ENTRY] = TIMER_Q_SIZE; +#ifndef VXD + pConfig->iMaxNumBuff[eNBT_FREE_IRPS] = NBT_NUM_IRPS; + pConfig->iMaxNumBuff[eNBT_DGRAM_MDLS] = NBT_NUM_DGRAM_MDLS; + pConfig->iMaxNumBuff[eNBT_FREE_SESSION_MDLS] = NBT_NUM_SESSION_MDLS; +#else + pConfig->iMaxNumBuff[eNBT_SESSION_HDR] = NBT_NUM_SESSION_HDR ; + pConfig->iMaxNumBuff[eNBT_SEND_CONTEXT] = NBT_NUM_SEND_CONTEXT ; + pConfig->iMaxNumBuff[eNBT_RCV_CONTEXT] = NBT_NUM_RCV_CONTEXT ; +#endif + break; + + case MEDIUM: + + pConfig->uNumLocalNames = MEDIUM_NUMBER_LOCAL_NAMES; + pConfig->uNumRemoteNames = MEDIUM_NUMBER_REMOTE_NAMES; + pConfig->uNumBucketsLocal = MEDIUM_NUMBER_BUCKETS_LOCAL_HASH_TABLE; + pConfig->uNumBucketsRemote = MEDIUM_NUMBER_BUCKETS_REMOTE_HASH_TABLE; + + pConfig->iMaxNumBuff[eNBT_DGRAM_TRACKER] = MEDIUM_NBT_NUM_DGRAM_TRACKERS; + pConfig->iMaxNumBuff[eNBT_TIMER_ENTRY] = MEDIUM_TIMER_Q_SIZE; +#ifndef VXD + pConfig->iMaxNumBuff[eNBT_FREE_IRPS] = MEDIUM_NBT_NUM_IRPS; + pConfig->iMaxNumBuff[eNBT_DGRAM_MDLS] = MEDIUM_NBT_NUM_DGRAM_MDLS; + pConfig->iMaxNumBuff[eNBT_FREE_SESSION_MDLS] = MEDIUM_NBT_NUM_SESSION_MDLS; +#else + pConfig->iMaxNumBuff[eNBT_SESSION_HDR] = MEDIUM_NBT_NUM_SESSION_HDR ; + pConfig->iMaxNumBuff[eNBT_SEND_CONTEXT] = MEDIUM_NBT_NUM_SEND_CONTEXT ; + pConfig->iMaxNumBuff[eNBT_RCV_CONTEXT] = MEDIUM_NBT_NUM_RCV_CONTEXT ; +#endif + break; + + case LARGE: + + pConfig->uNumLocalNames = LARGE_NUMBER_LOCAL_NAMES; + pConfig->uNumRemoteNames = LARGE_NUMBER_REMOTE_NAMES; + pConfig->uNumBucketsLocal = LARGE_NUMBER_BUCKETS_LOCAL_HASH_TABLE; + pConfig->uNumBucketsRemote = LARGE_NUMBER_BUCKETS_REMOTE_HASH_TABLE; + + pConfig->iMaxNumBuff[eNBT_DGRAM_TRACKER] = LARGE_NBT_NUM_DGRAM_TRACKERS; + pConfig->iMaxNumBuff[eNBT_TIMER_ENTRY] = LARGE_TIMER_Q_SIZE; +#ifndef VXD + pConfig->iMaxNumBuff[eNBT_FREE_IRPS] = LARGE_NBT_NUM_IRPS; + pConfig->iMaxNumBuff[eNBT_DGRAM_MDLS] = LARGE_NBT_NUM_DGRAM_MDLS; + pConfig->iMaxNumBuff[eNBT_FREE_SESSION_MDLS] = LARGE_NBT_NUM_SESSION_MDLS; +#else + pConfig->iMaxNumBuff[eNBT_SESSION_HDR] = LARGE_NBT_NUM_SESSION_HDR ; + pConfig->iMaxNumBuff[eNBT_SEND_CONTEXT] = LARGE_NBT_NUM_SEND_CONTEXT ; + pConfig->iMaxNumBuff[eNBT_RCV_CONTEXT] = LARGE_NBT_NUM_RCV_CONTEXT ; +#endif + break; + } + + ReadLmHostFile(pConfig,ParmHandle); +} + +#ifdef VXD +#pragma END_INIT +#endif + +//---------------------------------------------------------------------------- +VOID +ReadParameters2( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ) + +/*++ + +Routine Description: + + This routine is called to read DHCPable parameters from the parameters + section of the NBT section of the registry. + + This routine is primarily for the Vxd. + +Arguments: + + pConfig - A pointer to the configuration data structure. + ParmHandle - a handle to the parameters Key under Nbt + +Return Value: + + Status + +--*/ + +{ + ULONG Node; + ULONG ReadOne; + ULONG ReadTwo; + + CTEPagedCode(); + + Node = CTEReadSingleIntParameter(ParmHandle, // handle of key to look under + WS_NODE_TYPE, // wide string name + 0, // default value + 0); + + switch (Node) + { + case 2: + NodeType = PNODE; + break; + + case 4: + NodeType = MNODE; + break; + + case 8: + NodeType = MSNODE; + break; + + case 1: + NodeType = BNODE; + break; + + default: + NodeType = BNODE | DEFAULT_NODE_TYPE; + break; + } + + // do a trick here - read the registry twice for the same value, passing + // in two different defaults, in order to determine if the registry + // value has been defined or not - since it may be defined, but equal + // to one default. + ReadOne = CTEReadSingleHexParameter(ParmHandle, + WS_ALLONES_BCAST, + DEFAULT_BCAST_ADDR, + 0); + ReadTwo = CTEReadSingleHexParameter(ParmHandle, + WS_ALLONES_BCAST, + 0, + 0); + if (ReadOne != ReadTwo) + { + NbtConfig.UseRegistryBcastAddr = FALSE; + } + else + { + NbtConfig.UseRegistryBcastAddr = TRUE; + NbtConfig.RegistryBcastAddr = ReadTwo; + } + + ReadScope(pConfig,ParmHandle); +} + +//---------------------------------------------------------------------------- +VOID +ReadScope( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ) + +/*++ + +Routine Description: + + This routine is called to read the scope from registry and convert it to + a format where the intervening dots are length bytes. + +Arguments: + + pConfig - A pointer to the configuration data structure. + ParmHandle - a handle to the parameters Key under Nbt + +Return Value: + + Status + +--*/ + +{ + NTSTATUS status; + PUCHAR pScope; + PUCHAR pBuff; + PUCHAR pBuffer; + PUCHAR period; + ULONG Len; + UCHAR Chr; + + + CTEPagedCode(); + // + // this routine returns the scope in a dotted format. + // "Scope.MoreScope.More" The dots are + // converted to byte lengths by the code below. This routine allocates + // the memory for the pScope string. + // + status = CTEReadIniString(ParmHandle,NBT_SCOPEID,&pBuffer); + + + if (NT_SUCCESS(status) && strlen(pBuffer) > 0 ) + { + // + // the user can type in an * to indicate that they really want + // a null scope and that should override the DHCP scope. So check + // here for an * and if so, set the scope back to null. + // + if (pBuffer[0] == '*') + { + pBuffer[0] = 0; + } + // length of scope is num chars plus the 0 on the end, plus + // the length byte on the start(+2 total) - so allocate another buffer + // that is one longer than the previous one so it can include + // these extra two bytes. + // + Len = strlen(pBuffer); + // + // the scope cannot be longer than 255 characters as per RFC1002 + // + if (Len <= MAX_SCOPE_LENGTH) + { + pScope = CTEAllocInitMem(Len+2); + if (pScope) + { + CTEMemCopy((pScope+1),pBuffer,Len); + + // + // Put an null on the end of the scope + // + pScope[Len+1] = 0; + + Len = 1; + + // now go through the string converting periods to length + // bytes - we know the first byte is a length byte so skip it. + // + pBuff = pScope; + pBuff++; + Len++; + period = pScope; + while (Chr = *pBuff) + { + Len++; + if (Chr == '.') + { + *period = pBuff - period - 1; + + // + // Each label can be at most 63 bytes long + // + if (*period > MAX_LABEL_LENGTH) + { + status = STATUS_UNSUCCESSFUL; + NbtLogEvent(EVENT_SCOPE_LABEL_TOO_LONG,STATUS_SUCCESS); + break; + } + + // check for two periods back to back and use no scope if this + // happens + if (*period == 0) + { + status = STATUS_UNSUCCESSFUL; + break; + } + + period = pBuff++; + } + else + pBuff++; + } + if (NT_SUCCESS(status)) + { + // the last ptr is always the end of the name. + + *period = (UCHAR)(pBuff - period -1); + + pConfig->ScopeLength = (USHORT)Len; + pConfig->pScope = pScope; + CTEMemFree(pBuffer); + return; + } + CTEMemFree(pScope); + } + CTEMemFree(pBuffer); + } + else + { + status = STATUS_UNSUCCESSFUL; + NbtLogEvent(EVENT_SCOPE_LABEL_TOO_LONG,STATUS_SUCCESS); + } + + } + + + // + // the scope is one byte => '\0' - the length of the root name (zero) + // + pConfig->ScopeLength = 1; + // + // Since this routine could be called again after startup when a new + // DHCP address is used, we may have to free the old scope memory + // + if (pConfig->pScope) + { + CTEMemFree(pConfig->pScope); + } + pConfig->pScope = CTEAllocInitMem(1); + *pConfig->pScope = '\0'; + +} + +#ifdef VXD +#pragma BEGIN_INIT +#endif + +//---------------------------------------------------------------------------- +VOID +ReadLmHostFile( + IN tNBTCONFIG *pConfig, + IN HANDLE ParmHandle + ) + +/*++ + +Routine Description: + + This routine is called to read the lmhost file path from the registry. + +Arguments: + + pConfig - A pointer to the configuration data structure. + ParmHandle - a handle to the parameters Key under Nbt + +Return Value: + + Status + +--*/ + +{ + NTSTATUS status; + PUCHAR pBuffer; + PUCHAR pchr; + + CTEPagedCode(); + + // + // If we get a new Dhcp address this routine will get called again + // after startup so we need to free any current lmhosts file path + // + if (pConfig->pLmHosts) + { + CTEMemFree(pConfig->pLmHosts); + } + // + // read in the LmHosts File location + // +#ifdef VXD + status = CTEReadIniString(ParmHandle,WS_LMHOSTS_FILE,&pBuffer); +#else + status = NTGetLmHostPath(&pBuffer); +#endif + + if (NT_SUCCESS(status)) + { + NbtConfig.pLmHosts = pBuffer; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:LmHostsFile path is %s\n",NbtConfig.pLmHosts)); + + // find the last backslash so we can calculate the file path length + // + pchr = strrchr(pBuffer,'\\'); + if (pchr) + { + NbtConfig.PathLength = pchr - pBuffer + 1; // include backslash in length + } + else + { + // + // the lm host file must include a path of at least "c:\" i.e. + // the registry contains c:\lmhost, otherwise NBT won't be + // able to find the file since it doesn't know what directory + // to look in. + // + NbtConfig.pLmHosts = NULL; + NbtConfig.PathLength = 0; + } + + } + else + { + NbtConfig.pLmHosts = NULL; + NbtConfig.PathLength = 0; + } +} +#ifdef VXD +#pragma END_INIT +#endif + + diff --git a/private/ntos/nbt/nbt/makefile.16 b/private/ntos/nbt/nbt/makefile.16 new file mode 100644 index 000000000..1cfc07a9f --- /dev/null +++ b/private/ntos/nbt/nbt/makefile.16 @@ -0,0 +1,109 @@ +# +# +# Make file for Name server VxD. +# +# Have to have lots of stuff defined for this makefile to work. +# +# +# + +ROOTDIR=.. +!include ..\rules16.mk + +NBTSRC=$(ROOTDIR)\nbt +NBTOBJD=$(NBTSRC)\nodebug +NBTDOBJD=$(NBTSRC)\debug + +NBTOBJS= $(NBTOBJD)\hashtbl.obj\ + $(NBTOBJD)\hndlrs.obj\ + $(NBTOBJD)\inbound.obj\ + $(NBTOBJD)\name.obj\ + $(NBTOBJD)\namesrv.obj\ + $(NBTOBJD)\nbtutils.obj\ + $(NBTOBJD)\proxy.obj\ + $(NBTOBJD)\timer.obj\ + $(NBTOBJD)\udpsend.obj\ + $(NBTOBJD)\init.obj\ + $(NBTOBJD)\parse.obj + +NBTDOBJS=$(NBTOBJS:nodebug=debug) + +VTSF1=$(NBTSRC:\=/) +VTSF=$(VTSF1:.=\.) + +# +# Hack: Each .obj must begin with "..". Here we replace ".." with "+.." +# because the 16bit lib utility requires the operation to come before the +# obj. +# + +NBTLIBOBJS=$(NBTOBJS:..=+..) +NBTLIBDOBJS=$(NBTDOBJS:..=+..) + +NBTCFLAGS= -c -DVXD -Zp1l -G3 -Owx -nologo -D_X86_=1 -Di386=1 -DDEVL=1 -DPROXY_NODE +NBTCINC=$(TCP)\h;$(NDIS3)\inc;$(WIN32INC);$(BASEDIR)\public\sdk\inc;$(BASEDIR)\public\sdk\inc\crt;..\inc;..\..\inc;..\..\..\inc;..\..\..\..\inc;$(BASEDIR)\public\oak\inc +NBT16CINC= + + +{$(NBTSRC)}.c{$(NBTOBJD)}.obj: + set INCLUDE=$(NBTCINC) + set CL=$(NBTCFLAGS) + $(CL386) -Fo$(NBTOBJD)\$(@B).obj $(NBTSRC)\$(@B).c + +{$(NBTSRC)}.c{$(NBTDOBJD)}.obj: + set INCLUDE=$(NBTCINC) + set CL=$(NBTCFLAGS) -DDEBUG -Oy- -Zd + $(CL386) -Fo$(NBTDOBJD)\$(@B).obj $(NBTSRC)\$(@B).c + +all: NBT NBTD + +debug: NBTD + +NBT: $(NBTOBJD)\nbt.lib + +NBTD: $(NBTDOBJD)\nbt.lib + +clean: + -del $(NBTDOBJD)\*.obj + -del $(NBTDOBJD)\*.lib + -del $(NBTOBJD)\*.obj + -del $(NBTOBJD)\*.lib + +$(NBTOBJD)\NBT.LIB: $(NBTOBJS) + -del $(NBTOBJD)\NBT.LIB + $(LIBUTIL) @<< +$(NBTOBJD)\NBT.LIB +y +$(NBTLIBOBJS) +; +<< + + +$(NBTDOBJD)\NBT.LIB: $(NBTDOBJS) + -del $(NBTDOBJD)\NBT.LIB + $(LIBUTIL) @<< +$(NBTDOBJD)\NBT.LIB +y +$(NBTLIBDOBJS) +; +<< + + + +depend: $(NBTBINCS) + -copy $(NBTSRC)\depend.mk $(NBTSRC)\depend.old + echo #******************************************************************** > $(NBTSRC)\depend.mk + echo #** Copyright(c) Microsoft Corp., 1992 ** >> $(NBTSRC)\depend.mk + echo #******************************************************************** >> $(NBTSRC)\depend.mk + set INCLUDE=$(NBTCINC) + -$(INCLUDES) -i -e -S$$(NBTOBJD) -S$$(NBTDOBJD) -sobj $(NBTSRC)\*.c >> $(NBTSRC)\depend.mk + $(SED) -e s`$(IMPF)`$$(IMPORT)`g <$(NBTSRC)\depend.mk >$(NBTSRC)\depend.tmp + $(SED) -e s`$(CMNF)`$$(COMMON)`g <$(NBTSRC)\depend.tmp >$(NBTSRC)\depend.mk + $(SED) -e s`$(VTSF)`$$(NBTSRC)`g <$(NBTSRC)\depend.mk >$(NBTSRC)\depend.tmp + $(SED) -e s`$(BASEDIRF)`$$(BASEDIR)`g <$(NBTSRC)\depend.tmp > $(NBTSRC)\depend.mk + $(SED) -e s`$(INCF)`$$(INC)`g <$(NBTSRC)\depend.mk >$(NBTSRC)\depend.tmp + $(SED) -e s`$(HF)`$$(H)`g <$(NBTSRC)\depend.tmp >$(NBTSRC)\depend.mk + -del $(NBTSRC)\depend.tmp + +!include depend.mk + diff --git a/private/ntos/nbt/nbt/mp/makefile b/private/ntos/nbt/nbt/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nbt/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nbt/mp/sources b/private/ntos/nbt/nbt/mp/sources new file mode 100644 index 000000000..301687c9a --- /dev/null +++ b/private/ntos/nbt/nbt/mp/sources @@ -0,0 +1,27 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +!include ..\sources.inc diff --git a/private/ntos/nbt/nbt/name.c b/private/ntos/nbt/nbt/name.c new file mode 100644 index 000000000..70a2c2dc4 --- /dev/null +++ b/private/ntos/nbt/nbt/name.c @@ -0,0 +1,10345 @@ +/*++ + +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 "nbtprocs.h" // procedure headings +#ifndef VXD + +#include <nbtioctl.h> +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL +#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 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; + +// +// Function prototypes for functions local to this file +// +VOID +LockedRemoveFromList( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext + ); +VOID +CleanupFromRegisterFailed( + IN PUCHAR pNameRslv, + IN tCLIENTELE *pClientEle + ); + +VOID +SendDgramContinue( + IN PVOID pContext, + IN NTSTATUS status + ); + +VOID +CTECountedAllocMem( + PVOID *pBuffer, + ULONG Size + ); + +VOID +CTECountedFreeMem( + PVOID pBuffer, + ULONG Size + ); + +VOID +SendDgramCompletion( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo); + +VOID +DgramSendCleanupTracker( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS status, + IN ULONG Length + ); + +VOID +SessionSetupContinue( + IN PVOID pContext, + IN NTSTATUS status + ); +VOID +SessionStartupContinue( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo); +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 +SessionTimedOut( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); +VOID +QueryNameCompletion( + IN PVOID pContext, + IN NTSTATUS status + ); + +NTSTATUS +FindNameOrQuery( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PUCHAR pName, + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID QueryCompletion, + IN tDGRAM_SEND_TRACKING **ppTracker, + IN BOOLEAN DgramSend, + OUT tNAMEADDR **pNameAddr + ); + +tNAMEADDR * +FindNameRemoteThenLocal( + IN tDGRAM_SEND_TRACKING *pTracker, + OUT PULONG plNameType + ); + +VOID +FreeTracker( + IN tDGRAM_SEND_TRACKING *pTracker, + IN ULONG Actions + ); + +VOID +WipeOutLowerconn( + IN PVOID pContext + ); + +#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 PTDI_CONNECTION_INFORMATION pReturnInfo, + IN PIRP pIrp, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc + ); + +VOID +NbtNoteNewConnection( + IN tCONNECTELE *pConnEle, + IN tNAMEADDR *pNameAddr + ); +#endif // RASAUTODIAL + +NTSTATUS +NbtConnectCommon( + IN TDI_REQUEST *pRequest, + IN PVOID pTimeout, + IN PTDI_CONNECTION_INFORMATION pCallInfo, + IN PTDI_CONNECTION_INFORMATION pReturnInfo, + IN PIRP pIrp + ); + + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtOpenConnection) +#pragma CTEMakePageable(PAGE, NbtOpenAndAssocConnection) +#pragma CTEMakePageable(PAGE, NbtSendDatagram) +#pragma CTEMakePageable(PAGE, BuildSendDgramHdr) +#pragma CTEMakePageable(PAGE, NbtResyncRemoteCache) +#pragma CTEMakePageable(PAGE, NbtQueryFindName) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +NbtOpenAddress( + IN TDI_REQUEST *pRequest, + IN TA_ADDRESS UNALIGNED *pTaAddress, + IN ULONG IpAddress, + IN PVOID pSecurityDescriptor, + 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; + PCHAR pNameRslv; + tNAMEADDR *pNameAddr; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + tTIMERQENTRY *pTimer; + BOOLEAN ReRegister; + BOOLEAN MultiHomedReRegister; + BOOLEAN DontIncrement= FALSE; + ULONG TdiAddressType; + + + 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 = (PCHAR)pNetbiosAddress->NetbiosName; + } + break; + 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 = (PCHAR)pNetbiosExAddress->EndpointName; + } + break; + default: + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // check for a zero length address, because this means that the + // client wants to receive "Netbios Broadcasts" which are names + // that start with "*...." ( and 15 0x00's ). However they should have + // queried the broadcast address with NBT which would have returned + // "*....' + // + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Registering name = %16.16s<%X>\n",pNameRslv,pNameRslv[15])); + + // + // 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) + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // open the name since it could not be found + // + // first of all allocate memory for the address block + // + pAddrElement = (tADDRESSELE *)NbtAllocMem(sizeof (tADDRESSELE),NBT_TAG('C')); + status = STATUS_INSUFFICIENT_RESOURCES; + + if (pAddrElement) + { + CTEZeroMemory(pAddrElement,sizeof(tADDRESSELE)); + CTEInitLock(&pAddrElement->SpinLock); + pAddrElement->pDeviceContext = pContext; + pAddrElement->RefCount = 1; + pAddrElement->LockNumber = ADDRESS_LOCK; + + pAddrElement->AddressType = TdiAddressType; + + if ((uAddrType == NBT_UNIQUE ) || (uAddrType == NBT_QUICK_UNIQUE)) + { + pAddrElement->NameType = NBT_UNIQUE; + } + else + { + pAddrElement->NameType = NBT_GROUP;; + } + + + // 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. + // + InitializeListHead(&pAddrElement->ClientHead); + pClientEle = NbtAllocateClientBlock(pAddrElement); + + if (pClientEle) + { + pClientEle->AddressType = TdiAddressType; + + // we need to track the Irp so that when the name registration + // completes, we can complete the Irp. + pClientEle->pIrp = pIrp; + +#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); + + goto ExitRoutine; + + } + +#endif //!VXD + + // pass back the client block address as a handle for future reference + // to the client + pRequest->Handle.AddressHandle = (PVOID)pClientEle; + + pAddrElement->Verify = NBT_VERIFY_ADDRESS; + + InitializeListHead(&pAddrElement->Linkage); + + // keep track of which adapter this name is registered against. + pClientEle->pDeviceContext = (PVOID)pContext; + + // 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) + { +#ifndef VXD + NTSetFileObjectContexts( + pClientEle->pIrp,(PVOID)pClientEle, + (PVOID)(NBT_ADDRESS_TYPE)); +#endif + } + + // 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 + // + if (pNameAddr) + { + // + // 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 a valid ip address with + // loopback + // + if (IpAddress != LOOP_BACK) + { + pNameAddr->IpAddress = IpAddress; + } + + // need to tell NbtRegis... not to put the name in the hash table + // again + IpAddress = 0; + } + + pAddrElement->RefCount++; + + status = NbtRegisterName( + NBT_LOCAL, + IpAddress, + pNameRslv, + NbtConfig.pScope, + (PVOID)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)) + { + // link the address element to the head of the address list + // The Joint Lock protects this operation. + ExInterlockedInsertTailList(&NbtConfig.AddressHead, + &pAddrElement->Linkage, + &NbtConfig.JointLock.SpinLock); + NbtDereferenceAddress(pAddrElement); + } + else + { + NbtFreeClientObj(pClientEle); + + NbtFreeAddressObj(pAddrElement); + } + + } // if pClientEle + else + { + NbtFreeAddressObj(pAddrElement); + } + + } // if pAddrElement + + } + else + { + pAddrElement = (tADDRESSELE *)pNameAddr->pAddressEle; + // + // 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; + } + + // + // increment here before releasing the spinlock so that a name + // release done cannot free pAddrElement. + // + CTEInterlockedIncrementLong(&pAddrElement->RefCount); +#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); +#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 + + // 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->AdapterNumber))) + { + status = STATUS_SUCCESS; + } + else + status = STATUS_UNSUCCESSFUL; + + } + + if (!NT_SUCCESS(status)) + { + // + // 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. + // + 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; + } + + } + + // + // check for a sharing failure, if so , then return + // + if (!NT_SUCCESS(status)) + { + + CHECK_PTR(pRequest); + pRequest->Handle.AddressHandle = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + NbtDereferenceAddress(pAddrElement); + status = STATUS_SHARING_VIOLATION; + goto ExitRoutine; + + } + + + if (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. + 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); + pNameAddr->AdapterMask = 0; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + (*pClientCompletion)(Context,STATUS_SUCCESS); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + } + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + + } + // + // 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)) + { + ReRegister = TRUE; + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_RESOLVING; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Conflict State, re-registering name on net\n")); + } + else + { + // set status that indicates someone else has the name on the + // network. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + NbtDereferenceAddress(pAddrElement); + status = STATUS_DUPLICATE_NAME; + goto ExitRoutine; + } + + } + else + { + ReRegister = FALSE; + + // 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)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NbtDereferenceAddress(pAddrElement); + status = STATUS_SHARING_VIOLATION; + goto ExitRoutine; + } + } + else + if (!(pNameAddr->NameTypeState & NAMETYPE_GROUP)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NbtDereferenceAddress(pAddrElement); + status = STATUS_SHARING_VIOLATION; + goto ExitRoutine; + } + } + + // lock the address element so that we can + // coordinate with the name registration response handling in NBtRegister + // Completion below. + // + CTESpinLock(pAddrElement,OldIrq1); + + // create client block and link to addresslist + // pass back the client block address as a handle for future reference + // to the client + pClientEle = NbtAllocateClientBlock((tADDRESSELE *)pNameAddr->pAddressEle); + if (!pClientEle) + { + CTESpinFree(pAddrElement,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + NbtDereferenceAddress(pAddrElement); + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitRoutine; + + } + + // we need to track the Irp so that when the name registration + // completes, we can complete the Irp. + pClientEle->pIrp = pIrp; + + // keep track of which adapter this name is registered against. + pClientEle->pDeviceContext = (PVOID)pContext; + 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 + } + + if (!(pNameAddr->AdapterMask & pContext->AdapterNumber)) + { + // turn on the adapter's bit in the adapter Mask and set the + // re-register flag so we register the name out the new + // adapter. + // + pNameAddr->AdapterMask |= pContext->AdapterNumber; + + // only if the state is resolved do we set the reregister flag, + // since in the resolving state, the name will be tacked on the + // end of the current registration + // + if ( pNameAddr->NameTypeState & STATE_RESOLVED) + { + MultiHomedReRegister = TRUE; + } + else + { + MultiHomedReRegister = FALSE; + } + } + 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; + MultiHomedReRegister = FALSE; + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: Setting MultiClients True for name %16.16s<%X> pClient=%X\n", + pNameRslv,(UCHAR)pNameRslv[15],pClientEle)); + } + + // + // 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))) +// (pContext->IpAddress == 0)) // No IP from DHCP yet + { + // 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:Not Resolved State,waiting for previous registration- state= %X, ReRegister %X %X\n", + pNameAddr->NameTypeState,ReRegister,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 || ReRegister) + { + + // this flag is used by RegisterCompletion ( when true ) + pClientEle->WaitingForRegistration = FALSE; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Resolved State,But Register state= %X, ReRegister %X %X\n", + pNameAddr->NameTypeState,ReRegister,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, + NbtConfig.pScope, + (PVOID)pClientEle, + (PVOID)NbtRegisterCompletion, + uAddrType, + pContext); + + if (!NT_SUCCESS(status)) + { + NbtDereferenceAddress(pAddrElement); + } + } + 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; + } + } + } + +ExitRoutine: + + CTEExReleaseResource(&NbtConfig.Resource); + + 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. + // + pAddress->RefCount++; + pNameAddr = pAddress->pNameAddr; + + + // if the registration failed or a previous registration failed for the + // multihomed case, deny the client the name + // + if (status != STATUS_SUCCESS) + { + + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_CONFLICT; + status = STATUS_DUPLICATE_NAME; + + } + else + { + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_RESOLVED; + } + + 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--) + { + NbtDereferenceAddress(pAddress); + } + } + 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; + + // should be multihomed to get to here!! + ASSERT(NbtConfig.MultiHomed); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: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, + NbtConfig.pScope, + (PVOID)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); + + } + + // this decrements for the RefCount++ done in this routine. + NbtDereferenceAddress(pAddress); + + 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; + tLOWERCONNECTION *pLowerConn; + + CTEPagedCode(); + + pConnEle = (tCONNECTELE *)NbtAllocMem(sizeof(tCONNECTELE),NBT_TAG('D')); + if (!pConnEle) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Acquire this resource to co-ordinate with DHCP changing the IP + // address + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:OpenConnection <%X>\n",pConnEle)); + + // This ensures that all BOOLEAN values begin with a FALSE value among other things. + CTEZeroMemory(pConnEle,sizeof(tCONNECTELE)); + + CTEInitLock(&pConnEle->SpinLock); + + // initialize lists to empty + InitializeListHead(&pConnEle->Active); + InitializeListHead(&pConnEle->RcvHead); + + // store a context value to return to the client in various + // Event calls(such as Receive or Disconnect events) + pConnEle->ConnectContext = ConnectionContext; + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + pConnEle->pDeviceContext = pDeviceContext; +#endif + + pConnEle->state = NBT_IDLE; + pConnEle->Verify = NBT_VERIFY_CONNECTION; + pConnEle->RefCount = 1; // so we don't delete the connection + pConnEle->LockNumber = CONNECT_LOCK; + + // return the pointer to the block to the client as the connection + // id + pRequest->Handle.ConnectionContext = (PVOID)pConnEle; + + // 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. + // + ASSERT(pConnEle->RefCount == 1); + ExInterlockedInsertHeadList(&pDeviceContext->UpConnectionInUse, + &pConnEle->Linkage, + &pDeviceContext->SpinLock); + // + // for each connection the client(s) open, open a connection to the transport + // so that we can accept one to one from the transport. + + // allocate an MDL to be used for partial Mdls + // +#ifndef VXD + status = AllocateMdl(pConnEle); + if (NT_SUCCESS(status)) +#endif + { + if (pDeviceContext->pSessionFileObject) + { + // + // allocate memory for the lower connection block. + // + pLowerConn = (tLOWERCONNECTION *)NbtAllocMem(sizeof(tLOWERCONNECTION),NBT_TAG('E')); + + if (pLowerConn) + { + status = NbtOpenAndAssocConnection(pLowerConn,pDeviceContext); + CHECK_PTR(pLowerConn); + pLowerConn->pIrp = NULL ; + + if (NT_SUCCESS(status)) + { + CTEExReleaseResource(&NbtConfig.Resource); + return(STATUS_SUCCESS); + } + + CTEMemFree(pLowerConn); + + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES ; + } + +#ifndef VXD + IoFreeMdl(pConnEle->pNewMdl); +#endif + } + else + { // + // fake out the lower connection being openned when we are not yet + // bound to the transport + // + CTEExReleaseResource(&NbtConfig.Resource); + return(STATUS_SUCCESS); + } + } + + + // remove the pConnEle from the list + // + LockedRemoveFromList(pConnEle,pDeviceContext); + + FreeConnectionObj(pConnEle); + + CTEExReleaseResource(&NbtConfig.Resource); + + return(status); +} +//---------------------------------------------------------------------------- +VOID +LockedRemoveFromList( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine handles removing pConnele from the list with the spin lock + held. + +Arguments: + + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + CTELockHandle OldIrq; + + // remove the pConnEle from the list + // + CTESpinLock(pDeviceContext,OldIrq); + RemoveEntryList(&pConnEle->Linkage); + CTESpinFree(pDeviceContext,OldIrq); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtOpenAndAssocConnection( + IN tLOWERCONNECTION *pLowerConn, + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine handles associating a Net Bios name with an open connection. + +Arguments: + + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + NTSTATUS Locstatus; + BOOLEAN Attached=FALSE; + + CTEPagedCode(); + + CTEAttachFsp(&Attached); + + status = NbtTdiOpenConnection(pLowerConn,pDeviceContext); + + // set this to null to signify that this lower connection does not + // have an address object associated with it specifically - i.e. + // it is an inbound connection associated with the 139 address, not + // an outbound connection associated with its own address. + // + CHECK_PTR(pLowerConn); + pLowerConn->pAddrFileObject = NULL; + + if (NT_SUCCESS(status)) + { + // now associate the connection with the 139 session address + status = NbtTdiAssociateConnection( + pLowerConn->pFileObject, +#ifndef VXD + pDeviceContext->hSession); +#else + // Address handle stored in pFileObjects as a VXD + (HANDLE) pDeviceContext->pSessionFileObject); +#endif + + if (!NT_SUCCESS(status)) + { + + KdPrint(("Nbt:Unable to associate a connection with the session address status = %X\n", + status)); + + NTDereferenceObject((PVOID *)pLowerConn->pFileObject); + Locstatus = NbtTdiCloseConnection(pLowerConn); + } + else + { + ASSERT(pLowerConn->RefCount == 1); + // insert on the connection free queue + ExInterlockedInsertHeadList(&pDeviceContext->LowerConnFreeHead, + &pLowerConn->Linkage, + &pDeviceContext->SpinLock); + } + } + else + { + KdPrint(("Nbt:Unable to open a connection with the session address status = %X\n", + status)); + + } + + CTEDetachFsp(Attached); + + 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; + + pConnEle = pRequest->Handle.ConnectionContext; + + // 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* + + // check the connection element for validity + CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status) + + // check the client element for validity now! + CTEVerifyHandle(pClientHandle,NBT_VERIFY_CLIENT,tCLIENTELE,&status) + + CTESpinLock(pClientHandle->pDeviceContext,OldIrq2); + CTESpinLock(pConnEle,OldIrq1); + CTESpinLock(pClientHandle,OldIrq); + + if (pConnEle->state != NBT_IDLE) + { + // the connection is in use, so reject the associate attempt + CTESpinFree(pClientHandle,OldIrq); + CTESpinFree(pConnEle,OldIrq1); + CTESpinFree(pClientHandle->pDeviceContext,OldIrq2); + return(STATUS_INVALID_HANDLE); + } + else + { + pConnEle->state = NBT_ASSOCIATED; + // link the connection to the client so we can find the client, given + // the connection. + pConnEle->pClientEle = (PVOID)pClientHandle; + } + + // 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(pClientHandle,OldIrq); + CTESpinFree(pConnEle,OldIrq1); + + CTESpinFree(pClientHandle->pDeviceContext,OldIrq2); + + 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; + tDEVICECONTEXT *pDeviceContext; + TDI_REQUEST Request; + ULONG Flags; + + pConnEle = pRequest->Handle.ConnectionContext; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Dissassociate address, state = %X\n",pConnEle->state)); + + // check the connection element for validity + CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status) + + CHECK_PTR(pConnEle); + + pClientEle = pConnEle->pClientEle; + + 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; + + // call the non-NT specific function to disconnect the connection + // + 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: + // + // remove the connection from the client and put back on the + // unassociated list + // + pDeviceContext = pClientEle->pDeviceContext; + CTESpinLock(pClientEle,OldIrq1); + + if (pClientEle) + { + PLIST_ENTRY pHead,pEntry; + tLISTENREQUESTS *pListen; + + 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); + + CTESpinFree(pClientEle,OldIrq1); + + CTEIoComplete( pListen->pIrp,STATUS_CANCELLED,0); + + CTESpinLock(pClientEle,OldIrq1); + + CTEMemFree((PVOID)pListen); + } + } + } + + RemoveEntryList(&pConnEle->Linkage); + InitializeListHead(&pConnEle->Linkage); + CHECK_PTR(pConnEle); + CTESpinFree(pClientEle,OldIrq1); + + CTESpinLock(pConnEle,OldIrq); + CTESpinLock(pClientEle,OldIrq1); + RemoveEntryList(&pConnEle->Linkage); + pConnEle->state = NBT_IDLE; + pConnEle->pClientEle = NULL; + pConnEle->DiscFlag = 0; + CTESpinFree(pClientEle,OldIrq1); + CTESpinFree(pConnEle,OldIrq); + + + ExInterlockedInsertTailList(&pDeviceContext->UpConnectionInUse, + &pConnEle->Linkage, + &pDeviceContext->SpinLock); + + 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 + + pClientEle = (tCLIENTELE *)pRequest->Handle.ConnectionContext; + if (!pClientEle->pAddress) + { + // the address has already been deleted. + return(STATUS_SUCCESS); + } + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Close Address Hit %16.16s<%X> %X\n", + pClientEle->pAddress->pNameAddr->Name, + pClientEle->pAddress->pNameAddr->Name[15],pClientEle)); + +#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 + + status = NbtDereferenceClient(pClientEle); + +#ifndef VXD + if (status != STATUS_PENDING) + { + pIrpsp->Control = IrpFlags; + } + +#endif + + return(status); +} + +//---------------------------------------------------------------------------- +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; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + CTELockHandle OldIrq3; + PLIST_ENTRY pHead,pEntry; + PLIST_ENTRY pEntryConn; + tADDRESSELE *pAddress; + DWORD dwNumConn=0; + DWORD i; + + // 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,OldIrq3); + + 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,OldIrq3); + + while (pEntry != pHead) + { + pRcvEle = CONTAINING_RECORD(pEntry,tRCVELE,Linkage); + pRcvIrp = pRcvEle->pIrp; + + CTEIoComplete(pRcvIrp,STATUS_NETWORK_NAME_DELETED,0); + + pEntry = pEntry->Flink; + + CTEMemFree(pRcvEle); + } + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq3); + + // lock the client and the device context till we're done + CTESpinLock(pClientEle,OldIrq); + +#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); + CTESpinFree(pClientEle,OldIrq); + pConnEle = CONTAINING_RECORD( pEntry, tCONNECTELE, Linkage ) ; + +// +// 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 + + status = NbtCleanUpConnection(pConnEle,pDeviceContext); + + + CTESpinLock(pConnEle,OldIrq); + CTESpinLock(pClientEle,OldIrq2); + // + // 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); + pConnEle->state = NBT_IDLE; + CHECK_PTR(pConnEle); + pConnEle->pClientEle = NULL; + CTESpinFree(pClientEle,OldIrq2); + CTESpinFree(pConnEle,OldIrq); + PUSH_LOCATION(0x80); + + // + // put on the idle connection list, to wait for a close connection + // to come down. + // + ASSERT(pConnEle->RefCount == 1); + ExInterlockedInsertTailList(&pDeviceContext->UpConnectionInUse, + &pConnEle->Linkage, + &pDeviceContext->SpinLock); + CTESpinLock(pClientEle,OldIrq); + + + } + + // + // 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. + // + pHead = &pClientEle->ConnectHead; + pEntry = pHead->Flink; + // + // make the list look empty so no connections will be serviced inbound + // from the wire + // + InitializeListHead(pHead); + CTESpinFree(pClientEle,OldIrq); + + CTESpinLock(pDeviceContext,OldIrq); + while (pEntry != pHead ) + { + + pConnEle = CONTAINING_RECORD(pEntry,tCONNECTELE,Linkage); + + ASSERT ( ( pConnEle->Verify == NBT_VERIFY_CONNECTION ) || ( pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ); + + pEntry = pEntry->Flink; + + RemoveEntryList(&pConnEle->Linkage); + + CTESpinLock(pConnEle,OldIrq2); + + // disassociate the connection from the address by changing its state + // to idle and linking it to the pDeviceContext list of unassociated + // connections + // + pConnEle->state = NBT_IDLE; + CHECK_PTR(pConnEle); + pConnEle->Verify = NBT_VERIFY_CONNECTION_DOWN; + pConnEle->pClientEle = NULL; + ASSERT(pConnEle->RefCount == 1); + InsertTailList(&pDeviceContext->UpConnectionInUse,&pConnEle->Linkage); + + CTESpinFree(pConnEle,OldIrq2); + + // + // Count up the # of connections that were associated here so we can free that many lowerblocks + // later. + // + dwNumConn++; + } + + for (i=0; i<dwNumConn; i++) { + // + // 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); + + CTESpinFree(pDeviceContext,OldIrq); + +#ifndef VXD + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Closing Handle %X -> %X\n",pLowerConn,pLowerConn->FileHandle)); +#else + KdPrint(("Nbt:Closing Handle %X -> %X\n",pLowerConn,pLowerConn->pFileObject)); +#endif + // dereference the fileobject ptr + //NTDereferenceObject((PVOID *)pLowerConn->pFileObject); + //status = NbtTdiCloseConnection(pLowerConn); + + NbtDereferenceLowerConnection(pLowerConn); + + CTESpinLock(pDeviceContext,OldIrq); + } + } + CTESpinFree(pDeviceContext,OldIrq); + + // 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 + // + CTESpinLock(&NbtConfig.JointLock,OldIrq2); + + CTESpinLock(pClientEle,OldIrq); + + pHead = &pClientEle->ListenHead; + pEntry = pHead->Flink; + // + // make the list look empty so no connections will be serviced inbound + // from the wire + // + InitializeListHead(pHead); + + CTESpinFree(pClientEle, OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + while (pEntry != pHead ) + { + tLISTENREQUESTS * pListen ; + + pListen = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage); + pEntry = pEntry->Flink; + + CTEIoComplete( pListen->pIrp, STATUS_NETWORK_NAME_DELETED, 0); + CTEMemFree( pListen ); + } + +#ifdef VXD + // + // Complete any outstanding ReceiveAnys on this client element + // + DbgPrint("NbtCleanupAddress: Completing all RcvAny NCBs\r\n") ; + CTESpinLock(&NbtConfig.JointLock,OldIrq2); + + CTESpinLock(pClientEle,OldIrq); + + 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, OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + 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_STTTUS - status of the request + +--*/ +{ + tCONNECTELE *pConnEle; + NTSTATUS status; + + pConnEle = pRequest->Handle.ConnectionContext; + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt: Close Connection Hit!! state = %X pConnEle %X\n",pConnEle->state,pConnEle)); + +#ifndef VXD + CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION_DOWN,tCONNECTELE,&status); + + +#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 + // + pConnEle->pIrpClose = pIrp; + status = NbtDereferenceConnection(pConnEle); + + return(status); +} + +//---------------------------------------------------------------------------- +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 OldIrq2; + tLOWERCONNECTION *pLowerConn; + PLIST_ENTRY pEntry; + BOOLEAN Originator = TRUE; + ULONG LowerState = NBT_IDLE; + TDI_REQUEST Request; + tLISTENREQUESTS *pListen; + tCLIENTELE *pClientEle; + PLIST_ENTRY pHead; + BOOLEAN QueueCleanupBool=FALSE; + BOOLEAN DoDisconnect=TRUE; + BOOLEAN FreeLower; + + // + // 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 (pConnEle->state == 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 + // + PUSH_LOCATION(0x81); + } + else + { + 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) + { + status = NbtDisconnect( + &Request, + &DefaultDisconnectTimeout, + TDI_DISCONNECT_ABORT, + NULL, + NULL, + NULL + ); + } + + 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; + } + } + + // + // 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 + // + + CTESpinLock(pConnEle,OldIrq); + + FreeRcvBuffers(pConnEle,&OldIrq); + + CTESpinFree(pConnEle,OldIrq); + + + // check if any listens have been setup for this connection, and + // remove them if so + // + pClientEle = pConnEle->pClientEle; + + if (pClientEle) + { + CTESpinLock(pClientEle,OldIrq); + + 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); + + CTESpinFree(pClientEle,OldIrq); + + CTEIoComplete( pListen->pIrp,STATUS_CANCELLED,0); + + CTESpinLock(pClientEle,OldIrq); + + CTEMemFree((PVOID)pListen); + } + } + CTESpinFree(pClientEle,OldIrq); + } + + // 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 ((!pConnEle->Orig) && FreeLower) + { + // get a lower connection from the free list and close it with the + // transport. + // + CTESpinLock(pDeviceContext,OldIrq); + if (!IsListEmpty(&pDeviceContext->LowerConnFreeHead)) + { + pEntry = RemoveHeadList(&pDeviceContext->LowerConnFreeHead); + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Closing Lower Conn %X\n",pLowerConn)); + + CTESpinFree(pDeviceContext,OldIrq); + // dereference the fileobject ptr + //NTDereferenceObject((PVOID *)pLowerConn->pFileObject); + + + // close the lower connection with the transport +#ifndef VXD + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Closing Handle %X -> %X\n",pLowerConn,pLowerConn->FileHandle)); +#else + KdPrint(("Nbt:Closing Handle %X -> %X\n",pLowerConn,pLowerConn->pFileObject)); +#endif + //Locstatus = NbtTdiCloseConnection(pLowerConn); + + NbtDereferenceLowerConnection(pLowerConn); + } + else + { + CTESpinFree(pDeviceContext,OldIrq); + } + } + + // + // Unlink the connection element from the client's list or the device context + // if its not associated yet. + // + CTESpinLock(pDeviceContext,OldIrq); + if (pConnEle->state > NBT_IDLE) + { + CTESpinLock(pConnEle->pClientEle,OldIrq2); + + RemoveEntryList(&pConnEle->Linkage); + + CTESpinFree(pConnEle->pClientEle,OldIrq2); + + // do the disassociate here + // + CTESpinLock(pConnEle,OldIrq2); + pConnEle->state = NBT_IDLE; + CHECK_PTR(pConnEle); + pConnEle->pClientEle = NULL; + CTESpinFree(pConnEle,OldIrq2); + + } + else + { + RemoveEntryList(&pConnEle->Linkage); + } + + InitializeListHead(&pConnEle->Linkage); + + CTESpinFree(pDeviceContext,OldIrq); + CTEExReleaseResource(&NbtConfig.Resource); + + // this could be status pending from NbtDisconnect... + // + return(status); +} +//---------------------------------------------------------------------------- +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: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 +CheckListForTracker( + IN PLIST_ENTRY pListHead, + IN tDGRAM_SEND_TRACKING *pTracker, + OUT NBT_WORK_ITEM_CONTEXT **pContextRet + ) +/*++ +Routine Description: + + This Routine handles searching a list for a matching Tracker block. + The JointLock is held when calling this routine. + +Arguments: + + pListHead + pTracker + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PLIST_ENTRY pEntry; + PLIST_ENTRY pHead; + NBT_WORK_ITEM_CONTEXT *Context; + ULONG Count; + + // + // start by checking for LmHost Name queries + // + pHead = &LmHostQueries.ToResolve; + + Count = 2; + while (Count--) + { + pEntry = pHead->Flink; + + while (pEntry != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + if (pTracker == Context->pTracker) + { + RemoveEntryList(pEntry); + *pContextRet = Context; + return(STATUS_SUCCESS); + } + else + pEntry = pEntry->Flink; + + } + +#ifndef VXD + // + // now check the DnsQueries List + // + if (Count) + { + pHead = &DnsQueries.ToResolve; + } +#endif + } + + 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 *pTracker; + tNAMEADDR *pNameAddr; + 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; + //CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&Locstatus); + + if (pConnEle->state == NBT_CONNECTING) + { + if (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:Cleanup in the Connecting State %X\n",pConnEle)); + + if (pConnEle->pTracker) + { + LOCATION(0x6F) + // this is the QueryNameOnNet tracker, not the session setup + // tracker + // + pTracker = pConnEle->pTracker; + + pNameAddr = NULL; + status = FindInHashTable(NbtConfig.pRemoteHashTbl, + pTracker->pNameAddr->Name, + NbtConfig.pScope, + &pNameAddr); + + PUSH_LOCATION(0x82); + // + // 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; + LOCATION(0x70); + 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) + { + LOCATION(0x71); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got Cleanup During Active NameQuery: pConnEle %X\n", + pConnEle)); + CHECK_PTR(pNameAddr); + + pNameAddr->pTimer = NULL; + status = StopTimer(pTimer,&pClientCompletion,&Context); + + // + // 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) + { + NbtDereferenceName(pNameAddr); + } + + // since StopTimer should have cleaned up the tracker, null + // it out + + pTracker = NULL; + } + else + { + NBT_WORK_ITEM_CONTEXT *WiContext; + LOCATION(0x72); + status = STATUS_UNSUCCESSFUL; + + // + // check if the name is waiting on an LmHost name Query + // or a DNS name query + // + WiContext = ((NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context); + if (WiContext && (WiContext->pTracker == pTracker)) + { + LOCATION(0x73); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Found NameQuery on Lmhost Context: pConnEle %X\n", + pConnEle)); + pWiContext = WiContext; + LmHostQueries.Context = NULL; + NTClearContextCancel( pWiContext ); + status = STATUS_SUCCESS; + } + else + { + LOCATION(0x74); + // + // check the list for this tracker + // + status = CheckListForTracker(&LmHostQueries.ToResolve, + pTracker, + &pWiContext); + if (NT_SUCCESS(status)) + { + LOCATION(0x75); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Found NameQuery on Lmhost Q: pConnEle %X\n", + pConnEle)); + + } +#ifndef VXD + else + if (((NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context) && + ((NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context)->pTracker == pTracker) + { + LOCATION(0x76); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Found NameQuery on Dns Context: pConnEle %X\n", + pConnEle)); + + pWiContext = DnsQueries.Context; + DnsQueries.Context = NULL; + NTClearContextCancel( pWiContext ); + status = STATUS_SUCCESS; + + } else { + + LOCATION(0x78); + // + // check the list for this tracker + // + status = CheckListForTracker(&DnsQueries.ToResolve, + pTracker, + &pWiContext); + if (NT_SUCCESS(status)) + { + LOCATION(0x79); + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Found NameQuery on Dns Q: pConnEle %X\n", + pConnEle)); + + } + } +#endif + } + } + + } + + // ...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 + // + pConnEle->pTracker->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 (pTracker) + { + + LOCATION(0x79); + CTESpinFree(pConnEle,*OldIrq); + CTESpinFree(&NbtConfig.JointLock,*OldIrq2); + // + // remove the name from the hash table, since it did not resolve + // + RemoveName(pTracker->pNameAddr); + + DereferenceTracker(pTracker); + + 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); +} + +//---------------------------------------------------------------------------- +VOID +ReConnect( + IN PVOID Context + ) + +/*++ +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 + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + TDI_REQUEST TdiRequest; + NTSTATUS status; + tCONNECTELE *pConnEle; + PVOID DestAddr; + CTELockHandle OldIrq; + PCTE_IRP pIrp; + + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + CHECK_PTR(pTracker); + pConnEle = pTracker->Connect.pConnEle; + + pTracker->Connect.pTimer = NULL; + if (pTracker->Flags & TRACKER_CANCELLED) + { + CTELockHandle OldIrq1; + + // + // the 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; + + } + + TdiRequest.Handle.ConnectionContext = pConnEle; + // for retarget this is the destination address to connect to. + DestAddr = ((NBT_WORK_ITEM_CONTEXT *)Context)->pClientContext; + + PUSH_LOCATION(0x85); + pConnEle->state = NBT_ASSOCIATED; + status = NbtConnect(&TdiRequest, + NULL, + NULL, + (PTDI_CONNECTION_INFORMATION)pTracker, + (PIRP)DestAddr); + + CTEMemFree(Context); + + 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( pConnEle->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 PTDI_CONNECTION_INFORMATION pReturnInfo, + 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; + + + pConnEle = pRequest->Handle.ConnectionContext; + + // + // this code handles the When DHCP has not assigned an IP address yet + // + + if (pCallInfo) + { + BOOLEAN fNoIpAddress; + + 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)NTSetCancelRoutine( + pIrp, + NbtCancelPreConnect, + pConnEle->pClientEle->pDeviceContext); + if (NbtAttemptAutoDial( + pConnEle, + pTimeout, + pCallInfo, + pReturnInfo, + 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)NTCancelCancelRoutine(pIrp); + } + } +#endif // RASAUTODIAL + if (fNoIpAddress) { + return(STATUS_BAD_NETWORK_PATH); + } + + // check the connection element for validity + CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status) + + } + return NbtConnectCommon(pRequest, pTimeout, pCallInfo, pReturnInfo, pIrp); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtConnectCommon( + IN TDI_REQUEST *pRequest, + IN PVOID pTimeout, + IN PTDI_CONNECTION_INFORMATION pCallInfo, + IN PTDI_CONNECTION_INFORMATION pReturnInfo, + 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; + CTELockHandle OldIrq1; + ULONG IpAddress; + PCHAR pToName; + USHORT sLength; + tSESSIONREQ *pSessionReq = NULL; + PUCHAR pCopyTo; + tCLIENTELE *pClientEle; + LONG NameType; + ULONG NameLen; + tDGRAM_SEND_TRACKING *pTracker; + tNAMEADDR *pNameAddr; + tLOWERCONNECTION *pLowerConn; + tDEVICECONTEXT *pDeviceContext; + NBT_WORK_ITEM_CONTEXT *pContext; + ULONG RemoteIpAddress; + + + pConnEle = pRequest->Handle.ConnectionContext; + // + // Acquire this resource to co-ordinate with DHCP changing the IP + // address + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + CTESpinLock(pConnEle,OldIrq); + if ((pConnEle->state != NBT_ASSOCIATED) && + (pConnEle->state != NBT_DISCONNECTED)) + { + // the connection is Idle and is not associated with an address + // so reject the connect attempt + // + status = STATUS_INVALID_DEVICE_REQUEST; + goto ExitProc2; + } + + pClientEle = pConnEle->pClientEle; + CTESpinLock(pClientEle,OldIrq1); + if ( pClientEle->Verify != NBT_VERIFY_CLIENT ) + { + if ( pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN ) + { + status = STATUS_CANCELLED; + } + else + { + status = STATUS_INVALID_HANDLE; + } + goto ExitProc; + } + + // + // BUGBUG - Should be using pDeviceContext lock instead of + // NbtConfig.Resource, above. + // + pDeviceContext = pClientEle->pDeviceContext; + // + // this code handles the case when DHCP has not assigned an IP address yet + // + if ( + ( (pCallInfo) && (!pDeviceContext->pSessionFileObject) ) + || (pDeviceContext->IpAddress == 0) + ) + { + status = STATUS_BAD_NETWORK_PATH; + goto ExitProc; + } + + // + // check if the Reconnect got cancelled + // + if (!pCallInfo) + { + pTracker = (tDGRAM_SEND_TRACKING *)pReturnInfo; + if (pTracker->Flags & TRACKER_CANCELLED) + { + // + // the connect attempt got cancelled while waiting for + // the reconnect routine to run + // + // + // if SessionSetupContinue has run, it has set the refcount to zero + // + if (pTracker->RefConn == 0) + { + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + } + else + { + pTracker->RefConn--; + } + status = STATUS_CANCELLED; + goto ExitProc; + } + } + + // be sure the name is in the correct state for a connection + // + if (pClientEle->pAddress->pNameAddr->NameTypeState & STATE_CONFLICT) + { + status = STATUS_DUPLICATE_NAME; + goto ExitProc; + } + + pConnEle->state = NBT_CONNECTING; + + // Increment the ref count so that a cleanup cannot remove + // the pConnEle till the session is setup - one if these is removed when + // the session is setup and the other is removed when it is disconnected. + // + pConnEle->RefCount += 2; + //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. + // + if (pCallInfo) + { + PTRANSPORT_ADDRESS pRemoteAddress; + PTA_NETBIOS_ADDRESS pRemoteNetBiosAddress; + PTA_NETBIOS_EX_ADDRESS pRemoteNetbiosExAddress; + ULONG TdiAddressType; + + // we must store the client's irp in the connection element so that when + // the session sets up, we can complete the Irp. + pConnEle->pIrp = (PVOID)pIrp; + pConnEle->Orig = TRUE; + pConnEle->SessionSetupCount = NBT_SESSION_SETUP_COUNT-1; // -1 for this attempt + + pRemoteAddress = (PTRANSPORT_ADDRESS)pCallInfo->RemoteAddress; + TdiAddressType = pRemoteAddress->Address[0].AddressType; + pConnEle->pClientEle->AddressType = TdiAddressType; + pConnEle->AddressType = TdiAddressType; + + if (TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + PTDI_ADDRESS_NETBIOS pNetbiosAddress; + + pRemoteNetbiosExAddress = (PTA_NETBIOS_EX_ADDRESS)pRemoteAddress; + + CTEMemCopy(pConnEle->pClientEle->EndpointName, + pRemoteNetbiosExAddress->Address[0].Address[0].EndpointName, + sizeof(pRemoteNetbiosExAddress->Address[0].Address[0].EndpointName)); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NetBt:Handling New Address Type with SessionName %16s\n", + pConnEle->pClientEle->EndpointName)); + + pNetbiosAddress = &pRemoteNetbiosExAddress->Address[0].Address[0].NetbiosAddress; + pToName = pNetbiosAddress->NetbiosName; + NameType = pNetbiosAddress->NetbiosNameType; + NameLen = pRemoteNetbiosExAddress->Address[0].AddressLength - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress) - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS,NetbiosName); + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NetBt:NETBIOS address NameLen(%ld) Name %16s\n",NameLen,pToName)); + status = STATUS_SUCCESS; + } else if (TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS) { + pRemoteNetBiosAddress = (PTA_NETBIOS_ADDRESS)pRemoteAddress; + status = GetNetBiosNameFromTransportAddress( + pRemoteNetBiosAddress, + &pToName, + &NameLen, + &NameType); + } else { + status = STATUS_INVALID_ADDRESS_COMPONENT; + } + + if(!NT_SUCCESS(status)) + { + pConnEle->state = NBT_ASSOCIATED; + goto ExitProc1; + } + + // get a buffer for tracking Session setup + status = GetTracker(&pTracker); + if (!NT_SUCCESS(status)) + { + pConnEle->state = NBT_ASSOCIATED; + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitProc1; + } + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NbtConnectCommon:Tracker %lx\n",pTracker)); + + // save this in case we need to do a reconnect later and need the + // destination name again. + pTracker->SendBuffer.pBuffer = NbtAllocMem(NameLen,NBT_TAG('F')); + if (!pTracker->SendBuffer.pBuffer) + { + pConnEle->state = NBT_ASSOCIATED; + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitProc1; + } + CTEMemCopy(pTracker->SendBuffer.pBuffer,pToName,NameLen); + + CHECK_PTR(&pTracker); + pTracker->SendBuffer.Length = NameLen; + pTracker->pClientIrp = pIrp; + pTracker->RefConn = 1; + pTracker->Flags = SESSION_SETUP_FLAG; + + + CTESpinFree(pClientEle,OldIrq1); + CTESpinFree(pConnEle,OldIrq); + + pTracker->Connect.pDeviceContext = pDeviceContext; + pTracker->Connect.pConnEle = (PVOID)pConnEle; + pTracker->DestPort = NBT_SESSION_TCP_PORT; + + // 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->Connect.pDestName = pTracker->SendBuffer.pBuffer; + + // the timeout value is passed on through to the transport + pTracker->Connect.pTimeout = pTimeout; + + // the length is the 4 byte session hdr length + the half ascii calling + // and called names + the scope length times 2, one for each name + // + sLength = sizeof(tSESSIONREQ) + (NETBIOS_NAME_SIZE << 2) + + (NbtConfig.ScopeLength <<1); + + status = STATUS_INSUFFICIENT_RESOURCES ; + pSessionReq = (tSESSIONREQ *)NbtAllocMem(sLength,NBT_TAG('G')); + if (!pSessionReq) + { + goto NbtConnect_Error; + } + + pTracker->SendBuffer.pDgramHdr = pSessionReq; + + // + // Save the remote name while we still have it + // + CTEMemCopy( pConnEle->RemoteName, + pToName, + NETBIOS_NAME_SIZE ) ; + + } + else + { + // 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 = (tDGRAM_SEND_TRACKING *)pReturnInfo; + pTracker->RefConn++; + NameLen = NETBIOS_NAME_SIZE; + CTESpinFree(pClientEle,OldIrq1); + CTESpinFree(pConnEle,OldIrq); + } + + // for the reconnect case a null pCallInfo gets us into this If + if (!pCallInfo || pSessionReq) + { + + if (pCallInfo) + { + PCHAR pSessionName; + + if (pConnEle->pClientEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + pSessionName = pConnEle->pClientEle->EndpointName; + } else { + 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); + + + } + + + // open a connection with the transport for this session + status = TdiOpenandAssocConnection( + pConnEle, + pDeviceContext, + 0); // 0 for PortNumber means any port + + if (NT_SUCCESS(status)) + { + tLOWERCONNECTION *pLowerDump; + PLIST_ENTRY pEntry; + + // 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. + // + pConnEle->pLowerConnId->State = NBT_ASSOCIATED; + + // 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. + // + pConnEle->pIrpRcv = (PIRP)pTracker; + + // 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. + if (pCallInfo) + { + // + // 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(pDeviceContext,OldIrq1); + if (!pConnEle->LowerConnBlockRemoved && + !IsListEmpty(&pDeviceContext->LowerConnFreeHead)) + { + pEntry = RemoveHeadList(&pDeviceContext->LowerConnFreeHead); + pLowerDump = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + + pConnEle->LowerConnBlockRemoved = TRUE; + CTESpinFree(pDeviceContext,OldIrq1); + + // + // close the lower connection with the transport + // +#ifndef VXD + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:On Connect,close handle(from pool) %X -> %X\n",pLowerDump,pLowerDump->FileHandle)); +#else + KdPrint(("Nbt:Closing Handle %X -> %X\n",pLowerDump,pLowerDump->pFileObject)); +#endif + //NTDereferenceObject((PVOID *)pLowerDump->pFileObject); + //NbtTdiCloseConnection(pLowerDump); + + NbtDereferenceLowerConnection(pLowerDump); + } + else + { + CTESpinFree(pDeviceContext,OldIrq1); + } + + + } + else + { + // the original "ToName" was stashed in this unused + // ptr! - for the Reconnect case + pToName = pTracker->Connect.pConnEle->RemoteName; + // the pNameAddr part of pTracker(pDestName) needs to pt. to + // the name so that SessionSetupContinue can find the name + pTracker->Connect.pDestName = pToName; + + } + + // + // find the destination IP address + // + + // + // if the name is longer than 16 bytes, it's not a netbios name. + // skip wins, broadcast etc. and go straight to dns resolution + // + + if (pConnEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + RemoteIpAddress = Nbt_inet_addr(pToName); + } else { + RemoteIpAddress = 0; + } + + if (RemoteIpAddress != 0) { + tNAMEADDR *pRemoteNameAddr; + + // + // add this server name to the remote hashtable + // + + pRemoteNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('8')); + if (pRemoteNameAddr != NULL) + { + tNAMEADDR *pTableAddress; + + CTEZeroMemory(pRemoteNameAddr,sizeof(tNAMEADDR)); + InitializeListHead(&pRemoteNameAddr->Linkage); + CTEMemCopy(pRemoteNameAddr->Name,pToName,NETBIOS_NAME_SIZE); + pRemoteNameAddr->Verify = REMOTE_NAME; + pRemoteNameAddr->RefCount = 1; + pRemoteNameAddr->NameTypeState = STATE_RESOLVED | NAMETYPE_UNIQUE; + pRemoteNameAddr->AdapterMask = (CTEULONGLONG)-1; + pRemoteNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + pRemoteNameAddr->IpAddress = RemoteIpAddress; + + status = AddToHashTable( + NbtConfig.pRemoteHashTbl, + pRemoteNameAddr->Name, + NbtConfig.pScope, + 0, + 0, + pRemoteNameAddr, + &pTableAddress); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NbtConnectCommon ...AddRecordToHashTable %s Status %lx\n",pRemoteNameAddr->Name,status)); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (status == STATUS_SUCCESS) { + SessionSetupContinue(pTracker,status); + status = STATUS_PENDING; + } + } else { +// if ((pConnEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) || +// (NameLen > NETBIOS_NAME_SIZE)) { + if (NameLen > NETBIOS_NAME_SIZE) { + pTracker->AddressType = pConnEle->AddressType; + if (pConnEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("$$$$$ Avoiding NETBIOS name translation on connection to %16s\n",pConnEle->RemoteName)); + } + pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('H')); + if (!pContext) + { + KdPrint(("Nbt: NbtConnect: couldn't alloc mem for pContext\n")); + goto NbtConnect_Error; + } + + pContext->pTracker = NULL; // no query tracker + pContext->pClientContext = pTracker; // the client tracker + pContext->ClientCompletion = SessionSetupContinue; + status = DoDnsResolve(pContext); + } else { + status = FindNameOrQuery(pTracker, + pToName, + pDeviceContext, + SessionSetupContinue, + &pConnEle->pTracker, + TRUE, + &pNameAddr); + } + } + + if (status == STATUS_SUCCESS) + { + // + // for destinations on this machine use this devicecontext's + // ip address + // + if (pNameAddr->Verify == REMOTE_NAME) + { + IpAddress = pNameAddr->IpAddress; + } + else + { + IpAddress = pDeviceContext->IpAddress; + } + } + + // There may be a valid name address to use or it may have been + // nulled out to signify "Do Another Name Query" + if (!pCallInfo && pIrp) + { + // for the ReTarget case the Ip address is passed in the + // irp parameter + IpAddress = (ULONG)pIrp; + } + + // + // be sure that a close or disconnect has not come down and + // cancelled the tracker + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (status == STATUS_SUCCESS) + { + if ((pTracker->Flags & TRACKER_CANCELLED)) + { + NbtDereferenceName(pNameAddr); + status = STATUS_CANCELLED; + } + else + if ((pNameAddr->NameTypeState & NAMETYPE_UNIQUE ) || + (NodeType & BNODE)) + { + // set the session state to NBT_CONNECTING + CHECK_PTR(((tCONNECTELE *)pTracker->Connect.pConnEle)); + ((tCONNECTELE *)pTracker->Connect.pConnEle)->state = NBT_CONNECTING; + ((tCONNECTELE *)pTracker->Connect.pConnEle)->BytesRcvd = 0;; + ((tCONNECTELE *)pTracker->Connect.pConnEle)->ReceiveIndicated = 0; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Setting Up Session(cached entry!!) to %16.16s <%X>, %X\n", + pNameAddr->Name,pNameAddr->Name[15],(ULONG)pConnEle)); + + CHECK_PTR(pConnEle); + // keep track of the other end's ip address + pConnEle->pLowerConnId->SrcIpAddr = htonl(IpAddress); + pConnEle->pLowerConnId->State = NBT_CONNECTING; + + pConnEle->pTracker = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + status = TcpSessionStart( + pTracker, + IpAddress, + (tDEVICECONTEXT *)pTracker->Connect.pDeviceContext, + SessionStartupContinue, + pTracker->DestPort); + + // + // if TcpSessionStart fails for some reason it will still + // call the completion routine which will look after + // cleaning up + // + + CTEExReleaseResource(&NbtConfig.Resource); + +#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(pConnEle, pNameAddr); + } +#endif // RASAUTODIAL + + return(status); + } + else + { + + // the destination is a group name... + NbtDereferenceName(pNameAddr); + status = STATUS_BAD_NETWORK_PATH; + } + + + } + else + if (NT_SUCCESS(status)) + { + // i.e. pending was returned rather than success + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEExReleaseResource(&NbtConfig.Resource); + return(status); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + + } + + +NbtConnect_Error: + + // + // *** Error Handling Here *** + // + // + // unlink from the active connection list and put on idle list + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLock(pConnEle,OldIrq1); + + pLowerConn = pConnEle->pLowerConnId; + + CHECK_PTR(pConnEle); + pConnEle->pLowerConnId = NULL; + + pConnEle->state = NBT_ASSOCIATED; + RelistConnection(pConnEle); + + if (pLowerConn) + { + CHECK_PTR(pLowerConn); + pLowerConn->pUpperConnection = NULL; + + + CTESpinFree(pConnEle,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // undo the reference done in nbtconnect + DereferenceIfNotInRcvHandler(pConnEle,pLowerConn); + + // need to increment the ref count for CleanupAfterDisconnect to + // work correctly since it assumes the connection got fully connected + // + CTEInterlockedIncrementLong(&pLowerConn->RefCount); + ASSERT(pLowerConn->RefCount == 2); +#if !defined(VXD) && DBG + // + // DEBUG to catch upper connections being put on lower conn QUEUE + // + if ((pLowerConn->Verify != NBT_VERIFY_LOWERCONN ) || + (pLowerConn->RefCount == 1)) + { + DbgBreakPoint(); + } +#endif + + (void) CTEQueueForNonDispProcessing( NULL, + pLowerConn, + NULL, + CleanupAfterDisconnect, + pLowerConn->pDeviceContext); + + } + else + { + CTESpinFree(pConnEle,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NbtDereferenceConnection(pConnEle); + } + + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + + // + // Undo the second reference done above + // + NbtDereferenceConnection(pConnEle); + + CTEExReleaseResource(&NbtConfig.Resource); + return(status); + + +ExitProc1: + + RemoveEntryList(&pConnEle->Linkage); + InsertTailList(&pClientEle->ConnectHead,&pConnEle->Linkage); + pConnEle->RefCount--; + pConnEle->RefCount--; + +ExitProc: + CTESpinFree(pClientEle,OldIrq1); +ExitProc2: + CTESpinFree(pConnEle,OldIrq); + CTEExReleaseResource(&NbtConfig.Resource); + 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; + + // + // we had allocated this in nbtconnect + // + if (pTracker->SendBuffer.pBuffer) + { + CTEFreeMem(pTracker->SendBuffer.pBuffer); + pTracker->SendBuffer.pBuffer = NULL; + } + + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + + if (pConnEle->state != NBT_IDLE) + { + pConnEle->state = NBT_ASSOCIATED; + } + + CTESpinFree(pConnEle,irqlConnEle); + CTESpinFree(&NbtConfig.JointLock,irqlJointLock); + + // + // 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; + } + + // + // this will close the lower connection and dereference pConnEle once. + // + QueueCleanup(pConnEle); + + // + // 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 + // + CTESpinLock(pConnEle,irqlConnEle); + if (pConnEle->state != NBT_IDLE) + { + RelistConnection(pConnEle); + } + CTESpinFree(pConnEle,irqlConnEle); + + // + // 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. + // + NbtDereferenceConnection(pConnEle); + + 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) + { + 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; + ULONG lNameType; + PIRP pClientIrp; + PIRP pIrpDisc; + ULONG IpAddress; + tCONNECTELE *pConnEle; + tLOWERCONNECTION *pLowerConn; + BOOLEAN TrackerCancelled; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + pConnEle = pTracker->Connect.pConnEle; + + CTESpinLock(pConnEle,OldIrq); + pLowerConn = pConnEle->pLowerConnId; + + TrackerCancelled = pTracker->Flags & TRACKER_CANCELLED; + + if (status == STATUS_SUCCESS && !TrackerCancelled) + { + // this is the QueryOnNet Tracker ptr being cleared rather than the + // session setup tracker. + // + CHECK_PTR(pConnEle); + pConnEle->pTracker = NULL; + + // check the Remote table and then the Local table + // + pNameAddr = FindNameRemoteThenLocal(pTracker,&lNameType); + + if (pNameAddr) + { + // for a call to ourselves, use the ip address of this + // device context + // + if (pNameAddr->Verify == REMOTE_NAME) + { + IpAddress = pNameAddr->IpAddress; + } + else + { + IpAddress = pConnEle->pClientEle->pDeviceContext->IpAddress; + } + + // a session can only be started with a unique named destination + if ((lNameType & NAMETYPE_UNIQUE ) || (NodeType & BNODE)) + { + // set the session state, initialize a few things and setup a + // TCP connection, calling SessionStartupContinue when the TCP + // connection is up + // + CHECK_PTR(pConnEle); + pLowerConn->State = NBT_CONNECTING; + + pConnEle->state = NBT_CONNECTING; + pConnEle->BytesRcvd = 0;; + pConnEle->ReceiveIndicated = 0; + CHECK_PTR(pTracker); + pTracker->Connect.pNameAddr = pNameAddr; + + // keep track of the other end's ip address + pConnEle->pLowerConnId->SrcIpAddr = htonl(IpAddress); + pConnEle->pTracker = NULL; // cleanup connection uses this check + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Setting Up Session(after Query) to %16.16s <%X>, %X\n", + pNameAddr->Name,pNameAddr->Name[15], + (ULONG)pTracker->Connect.pConnEle)); + + // 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. + // + pNameAddr->RefCount++; + // DEBUG + ASSERT(pNameAddr->RefCount >= 2); + CTESpinFree(pConnEle,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // start the session... + status = TcpSessionStart( + pTracker, + IpAddress, + (tDEVICECONTEXT *)pTracker->Connect.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(pConnEle, pNameAddr); + } +#endif // RASAUTODIAL + + return; + + } + + } + status = STATUS_BAD_NETWORK_PATH; + } + + pClientIrp = pConnEle->pIrp; + pConnEle->pIrp = NULL; + +#ifdef RASAUTODIAL + // + // Before we return an error, give this + // address to the automatic connection driver + // to see if it can create a new network + // connection. + // + if (fAcdLoadedG && + !TrackerCancelled && + status == STATUS_BAD_NETWORK_PATH) + { + CTELockHandle adirql; + BOOLEAN fEnabled; + + CTEGetLock(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, adirql); + if (fEnabled && + NbtAttemptAutoDial( + pConnEle, + pTracker->Connect.pTimeout, + NULL, + (PTDI_CONNECTION_INFORMATION)pTracker, + pClientIrp, + 0, + NbtRetryPostConnect)) + { + CTESpinFree(pConnEle,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; + } + } +#endif // RASAUTODIAL + + CleanUpPartialConnection(status, pConnEle, pTracker, pClientIrp, OldIrq1, OldIrq); +} + +//---------------------------------------------------------------------------- +VOID +QueueCleanup( + IN tCONNECTELE *pConnEle + ) +/*++ +Routine Description + + This routine handles Queuing a request to a worker thread to cleanup + a connection(which basically closes the connection). + +Arguments: + + pConnEle - ptr to the upper connection + +Return Values: + + VOID + +--*/ + +{ + NTSTATUS status; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + CTELockHandle OldIrq2; + ULONG State; + BOOLEAN DerefConnEle; + tLOWERCONNECTION *pLowerConn; + + CHECK_PTR(pConnEle); + if (pConnEle) + { + + // to coordinate with RejectSession in hndlrs.c get the spin lock + // so we don't disconnect twice. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + pLowerConn = pConnEle->pLowerConnId; + + if (pLowerConn && + (pLowerConn->State > NBT_IDLE) && + (pLowerConn->State < NBT_DISCONNECTING)) + { + CTESpinLock(pConnEle,OldIrq2); + CTESpinLock(pLowerConn,OldIrq); + + 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; + + pLowerConn->State = NBT_DISCONNECTING; + + if (pConnEle->state != NBT_IDLE) + { + pConnEle->state = NBT_DISCONNECTED; + } + + pConnEle->pLowerConnId = NULL; + pLowerConn->pUpperConnection = NULL; + + // + // need to increment the ref count for CleanupAfterDisconnect 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 SessionTimedOut, then RefCount must + // be decremented there to account for this increment. + // + if (State < NBT_SESSION_OUTBOUND) + { + pLowerConn->RefCount++; + } + + + +#if !defined(VXD) && DBG + // + // DEBUG to catch upper connections being put on lower conn QUEUE + // + if ((pLowerConn->Verify != NBT_VERIFY_LOWERCONN ) || + (pLowerConn->RefCount == 1)) + { + DbgBreakPoint(); + } +#endif + CTESpinFree(pLowerConn,OldIrq); + CTESpinFree(pConnEle,OldIrq2); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // + // when the lower no longer points to the upper undo the reference + // done in NbtConnect, or InBound. + // + DereferenceIfNotInRcvHandler(pConnEle,pLowerConn); + + status = CTEQueueForNonDispProcessing( + NULL, + pLowerConn, + NULL, + CleanupAfterDisconnect, + pLowerConn->pDeviceContext); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + } + +} + +//---------------------------------------------------------------------------- +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; + CTELockHandle OldIrq; + tTIMERQENTRY *pTimerEntry; + + + CTESpinLock(pConnEle,OldIrq); + + + CTEGetTimeout(pTracker->Connect.pTimeout,&Timeout); + + // now start a timer to time the return of the session setup + // message + // + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Start Session Setup Timer TO = %X\n",Timeout)); + + if (Timeout < NBT_SESSION_RETRY_TIMEOUT) + { + Timeout = NBT_SESSION_RETRY_TIMEOUT; + } + status = StartTimer( + Timeout, + (PVOID)pTracker, // context value + NULL, // context2 value + SessionTimedOut, + pTracker, + SessionTimedOut, + 0, + &pTimerEntry); + + if (NT_SUCCESS(status)) + { + pTracker->Connect.pTimer = pTimerEntry; + + } + else + { + // 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->Connect.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; + tLOWERCONNECTION *pLowerConn; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + BOOLEAN TrackerCancelled; + tNAMEADDR *pNameAddr; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + pConnEle = (tCONNECTELE *)pTracker->Connect.pConnEle; + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // + // we had allocated this in nbtconnect: we don't need anymore, free it + // + if (pTracker->SendBuffer.pBuffer) + { + CTEFreeMem(pTracker->SendBuffer.pBuffer); + pTracker->SendBuffer.pBuffer = NULL; + } + + // + // remove the reference done with FindNameOrQuery was called, or when + // SessionSetupContinue ran + // + NbtDereferenceName(pTracker->Connect.pNameAddr); + + CTESpinLock(pConnEle,OldIrq); + pLowerConn = pConnEle->pLowerConnId; + + // + // NbtDisconnect can cancel the tracker if a disconnect comes in during + // the connecting phase. + // + if (!(pTracker->Flags & TRACKER_CANCELLED) && + (NT_SUCCESS(status))) + { + + // set the session state to NBT_SESSION_OUTBOUND + // + pConnEle->state = NBT_SESSION_OUTBOUND; + + // in case the connection got disconnected during the setup phase, + // check the lower conn value + if (pLowerConn) + { + + + // + // Increment the reference count on a connection while it is connected + // so that it cannot be deleted until it disconnects. + // + REFERENCE_LOWERCONN(pLowerConn); + ASSERT(pLowerConn->RefCount == 2); + + pLowerConn->State = NBT_SESSION_OUTBOUND; + pLowerConn->StateRcv = NORMAL; + + SetStateProc( pLowerConn, 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 = NTSetCancelRoutine(pConnEle->pIrp, + NTCancelSession, + pTracker->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 + // + NbtDereferenceConnection(pConnEle); + 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 + { + status = STATUS_UNSUCCESSFUL; + } + + + } + 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. + // + } + + // + // 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; + + TrackerCancelled = pTracker->Flags & TRACKER_CANCELLED; + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Startup Continue 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 + // + // + // 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. + // + pNameAddr = pTracker->Connect.pNameAddr; + if ((pNameAddr->Verify == REMOTE_NAME) && + (pNameAddr->RefCount == 1) && + (pNameAddr->NameTypeState & STATE_RESOLVED)) + { + NbtDereferenceName(pNameAddr); + } + + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + + if (pConnEle->state != NBT_IDLE) + { + pConnEle->state = NBT_ASSOCIATED; + } + + pLowerConn = pConnEle->pLowerConnId; + + CTESpinFree(pConnEle,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // 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. + // + pIrpDisc = NULL; + if (TrackerCancelled) + { + // + // Complete the Disconnect Irp that is pending too + // + pIrpDisc = pConnEle->pIrpDisc; + status = STATUS_CANCELLED; + } + + // 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. + + if (pConnEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT@@@ Will avoid repeated attempts on a nonexistent address\n")); + pConnEle->RemoteNameDoesNotExistInDNS = TRUE; + } + +#ifndef VXD + if (status == STATUS_CONNECTION_REFUSED || status == STATUS_IO_TIMEOUT) + { + status = STATUS_BAD_NETWORK_PATH; + } +#else + if (status == TDI_CONN_REFUSED || status == TDI_TIMED_OUT) + { + status = STATUS_BAD_NETWORK_PATH; + } +#endif + + QueueCleanup(pConnEle); + + // + // put back on the idle connection list if nbtcleanupconnection has not + // run and taken pconnele off the list (setting the state to Idle). + // + + CTESpinLock(pConnEle,OldIrq); + if (pConnEle->state != NBT_IDLE) + { + RelistConnection(pConnEle); + } + CTESpinFree(pConnEle,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. + // + NbtDereferenceConnection(pConnEle); + + + // 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); + } +} + +//---------------------------------------------------------------------------- +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; + ULONG state; + PCTE_IRP pClientIrp; + PCTE_IRP pIrpDisc; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + pConnEle = (tCONNECTELE *)pTracker->Connect.pConnEle; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pLowerConn = pConnEle->pLowerConnId; + // + // 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. + // + if (pTracker->Connect.pTimer) + { + StopTimer(pTracker->Connect.pTimer,&CompletionRoutine,NULL); + CHECK_PTR(pTracker); + pTracker->Connect.pTimer = NULL; + } + else + { + CompletionRoutine = NULL; + } + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Startup Completion 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 + // SessionTimedOut. + // + if (CompletionRoutine) + { + CTESpinLock(pConnEle,OldIrq); + + // for some reason sending the session setup pdu failed, so just + // close the connection and don't worry about being graceful. + // + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + pConnEle->pIrpRcv = NULL; + state = pConnEle->state; + pConnEle->state = NBT_ASSOCIATED; + + pClientIrp = pConnEle->pIrp; + CHECK_PTR(pConnEle); + pConnEle->pIrp = NULL; + pIrpDisc = pConnEle->pIrpDisc; + pConnEle->pIrpDisc = NULL; + CTESpinFree(pConnEle,OldIrq); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // if the state has changed then some other part of the code + // has done the disconnect of the Lower, so don't do it again here. + // + if (state == NBT_SESSION_OUTBOUND) + { + QueueCleanup(pConnEle); + } + + // + // Nbt_idle means that NbtCleanupConnection has run, so do not + // relist in this case because the connection is about to be freed. + // + if (state != NBT_IDLE) + { + RelistConnection(pConnEle); + } + + // + // 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. + // + NbtDereferenceConnection(pConnEle); + + if (pClientIrp) + { + CTEIoComplete(pClientIrp,status,0L); + } + } + else + { + // + // the session has probably been cancelled and the Session timeout + // routine run, so just clean up the tracker as the final activity, + // but be sure the tracker has not already been released in + // Outbound. + // + if (pTracker == (tDGRAM_SEND_TRACKING *)pConnEle->pIrpRcv) + { + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + // the tracker is stored here during connection setup so + // null it out to prevent Outbound from using it. + // + pConnEle->pIrpRcv = NULL; + } + 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. + // + NbtDereferenceConnection(pConnEle); + } + + } + else + { + + + + // + // if OutBound has run, it has set the refcount to zero + // + if (pTracker->RefConn == 0) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + } + else + { + pTracker->RefConn--; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + // remove the reference added in nbt connect + NbtDereferenceConnection(pConnEle); + + // NOTE: the last reference done on pConnEle in NbtConnect is NOT undone + // until the pLowerConn no longer points to pConnEle!! + } + +} +//---------------------------------------------------------------------------- +VOID +SessionTimedOut( + 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. + +--*/ +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + tCONNECTELE *pConnEle; + tLOWERCONNECTION *pLowerConn; + CTE_IRP *pIrp; + USHORT State; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + // if pTimerQEntry is null then the timer is being cancelled, so do nothing + if (pTimerQEntry) + { + // kill the connection + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pConnEle = pTracker->Connect.pConnEle; + pLowerConn = pConnEle->pLowerConnId; + pTracker->Connect.pTimer = NULL; + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Session Timed Out, UpperState=%X,,pConnEle=%X\n", + pConnEle->state,pConnEle)); + + if (pLowerConn) + { + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Session Timed Out, LowerState=%X,TrackerFlags=%X,pLowerConn=%X\n", + pLowerConn->State, + pLowerConn)); + + CTESpinLock(pLowerConn,OldIrq1); + + if ((pConnEle->state == NBT_SESSION_OUTBOUND) && + (pIrp = pConnEle->pIrp)) + { + + CHECK_PTR(pConnEle); + pConnEle->pIrp = NULL; + + State = pConnEle->state; + pConnEle->state = NBT_ASSOCIATED; + + CTESpinFree(pLowerConn,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + QueueCleanup(pConnEle); + + // + // Nbt_idle means that nbtcleanupConnection has run and the + // connection is about to be deleted, so don't relist. + // + if (State != NBT_IDLE) + { + CTESpinLock(pConnEle,OldIrq1); + RelistConnection(pConnEle); + CTESpinFree(pConnEle,OldIrq1); + } + +// Don't do this because the transport still must complete the session +// setup message through SessionStartupCompletion - where the tracker +// will be freed. +// +// FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + + // + // Called from NtCancelSession with the Context2 set to + // STATUS_CANCELLED when the client IRP is cancelled + // + if ((ULONG)pContext2 == STATUS_CANCELLED) + { + status = STATUS_CANCELLED; + } + else + status = STATUS_IO_TIMEOUT; + + CTEIoComplete(pIrp,status,0); + } + else + { + + CTESpinFree(pLowerConn,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + } + +} + +//---------------------------------------------------------------------------- +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; + + CTESpinLock(pConnEle->pClientEle,OldIrq); + // + // 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) + { + pConnEle->state = NBT_ASSOCIATED; + RemoveEntryList(&pConnEle->Linkage); + InsertTailList(&pConnEle->pClientEle->ConnectHead,&pConnEle->Linkage); + } + CTESpinFree(pConnEle->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; + + // now find the connection object to link this listen record to + pConnEle = ((tCONNECTELE *)pRequest->Handle.ConnectionContext); + + // be sure we have not been passed some bogus ptr + // + CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status); + + // + // Find the client record associated with this connection + // + if (pConnEle->state != NBT_ASSOCIATED) + { + return(STATUS_INVALID_HANDLE); + } + + pClientEle = (tCLIENTELE *)pConnEle->pClientEle; + + pListenReq = NbtAllocMem(sizeof(tLISTENREQUESTS),NBT_TAG('I')); + + if (!pListenReq) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // 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; + + CTESpinLock(pClientEle,OldIrq); + + // queue the listen request on the client object + InsertTailList(&pClientEle->ListenHead,&pListenReq->Linkage); + + status = NTCheckSetCancelRoutine(pIrp,(PVOID)NbtCancelListen,0); + + if (!NT_SUCCESS(status)) + { + RemoveEntryList(&pListenReq->Linkage); + status = STATUS_CANCELLED; + } + else + status = STATUS_PENDING; + + 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; + + pConnEle = pRequest->Handle.ConnectionContext; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:NbtDisconnect Hit,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 ((Flags == TDI_DISCONNECT_WAIT) && (pConnEle->DiscFlag)) + 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); + 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 = 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 = NTSetCancelRoutine(pIrp,DiscWaitCancel,pLowerConn->pDeviceContext); + // + // change the ret status so if the irp has been cancelled, + // driver.c will not also return it, since NTSetCancelRoutine + // 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->Connect.pTimer) + { + COMPLETIONCLIENT ClientRoutine; + PVOID pContext; + + // + // the Session Setup Message has been sent + // so stop the SessionSetup Timer. + // + LOCATION(0x67) + CHECK_PTR(pTracker); + pTracker->Connect.pTimer = NULL; + CTESpinFree(pConnEle,OldIrq2); + + StopTimer(pTimerEntry,&ClientRoutine,&pContext); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + if (ClientRoutine) + { + (*(COMPLETIONROUTINE)ClientRoutine)(pContext,NULL,pTimerEntry); + } + // else... + // the timer has completed and called QueueCleanup + // so all we need to do is return here. + + return(STATUS_SUCCESS); + } + 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); + } + + break; + + } + + 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 + // + pConnEle->pIrpDisc = pIrp; + + status = STATUS_PENDING; + } + else + if (status == STATUS_PENDING) + { + LOCATION(0x6B) + // the connection is being setup with the transport + // so disconnect below + // + + pTracker->Flags = TRACKER_CANCELLED; + // + // CleanupAfterDisconnect expects this ref count + // to be 2, meaning that it got connected, so increment + // here + pLowerConn->RefCount++; + break; + } + + CTESpinFree(pConnEle,OldIrq2); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + + return(status); + + break; + } + + CTESpinLock(pLowerConn,OldIrq3); + + if (pConnEle->state != NBT_SESSION_UP) + { // + // do an abortive disconnect to be sure it completes now. + // + Flags = TDI_DISCONNECT_ABORT; + PUSH_LOCATION(0xA1); + } + + LOCATION(0x6C) + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: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 + // + pConnEle->state = NBT_ASSOCIATED; + CHECK_PTR(pConnEle); + pConnEle->pLowerConnId = NULL; + + CHECK_PTR(pLowerConn); + pLowerConn->pUpperConnection = NULL; + 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 + + pLowerConn->State = NBT_DISCONNECTING; + + SetStateProc( pLowerConn, RejectAnyData ) ; + + if (!pConnEle->pIrpDisc) + { + pLowerConn->pIrp = pIrp ; + } + + CTESpinFree(pLowerConn,OldIrq3); + + PUSH_LOCATION(0x84); + CTESpinFree(pConnEle,OldIrq2); + + // remove the reference added to pConnEle when pLowerConn pointed + // to it, since that pointer link was just removed. + // if the state is not disconnecting... + // + DereferenceIfNotInRcvHandler(pConnEle,pLowerConn); + + RelistIt = TRUE; + } + else + { + LOCATION(0x6D) + PUSH_LOCATION(0x83); + CHECK_PTR(pConnEle); + CHECK_PTR(pLowerConn); + pLowerConn->pUpperConnection = NULL; + pConnEle->pLowerConnId = NULL; + 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: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: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 + // + RelistConnection(pConnEle); + } + + // + // 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); + + 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_SESSION_UP) && + (state >= NBT_CONNECTING)) + { + + // + // got a cleanup for an active connection, so send a disconnect down + // to the transport + // + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt waiting for disconnect...\n")); + + status = GetTracker(&pTracker); + if (NT_SUCCESS(status)) + { + ULONG TimeVal; + + // this should return status pending and the irp will be completed + // in CleanupAfterDisconnect in hndlrs.c + pTracker->Connect.pConnEle = (PVOID)pLowerConn; +#if DBG + if (Timeout) + { + TimeVal = ((PTIME)Timeout)->LowTime; + } + else + TimeVal = 0; + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt: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_SUCCESS,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 + // + pConnectEle->state = NBT_SESSION_UP; + + pLowerConn = (tLOWERCONNECTION *)pConnectEle->pLowerConnId; + pLowerConn->State = NBT_SESSION_UP; + pLowerConn->StateRcv = NORMAL; + SetStateProc( pLowerConn, 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; + + 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)NTCancelRcvDgram,pDeviceContext); + if (!NT_SUCCESS(status)) + { + RemoveEntryList(&pRcvEle->Linkage); + } + else + status = STATUS_PENDING; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:RcvDgram posted (pIrp) %X \n",pIrp)); + + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +FindNameOrQuery( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PUCHAR pName, + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID QueryCompletion, + IN tDGRAM_SEND_TRACKING **ppTracker, + IN BOOLEAN DgramSend, + OUT tNAMEADDR **ppNameAddr + + ) +/*++ + +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; + + + // + // 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); + + if (ppTracker) + { + *ppTracker = NULL; + } + + // 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 ; + pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('K')); + + if (pNameAddr) + { + 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 NbtDereferenceName + // is called + CHECK_PTR(pNameAddr); + pNameAddr->RefCount = 0; + pNameAddr->Verify = LOCAL_NAME; + pNameAddr->AdapterMask = (CTEULONGLONG)-1; + + // 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); + 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->NameTypeState & (NAMETYPE_GROUP | NAMETYPE_INET_GROUP))&& + !(NodeType & BNODE)) && !DgramSend)) + { + pNameAddr = NULL; + status = FindInHashTable(NbtConfig.pRemoteHashTbl, + pName, + NbtConfig.pScope, + &pNameAddr); + } + } + + // 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. + // + if (!NT_SUCCESS(status) || (pNameAddr->NameTypeState & STATE_RELEASED) || + (NT_SUCCESS(status) + && (!(pNameAddr->AdapterMask & pDeviceContext->AdapterNumber))) ) + { + // remove the released name from the name table since we will + // re-resolve it. This is needed in the proxy case where it leaves + // names in the released state in the hash table as a negative + // cache. + // + if (NT_SUCCESS(status) && (pNameAddr->NameTypeState & STATE_RELEASED) && + (pNameAddr->RefCount == 1)) + { + NbtDereferenceName(pNameAddr); + } + + // fill in some tracking values so we can complete the send later + InitializeListHead(&pTracker->TrackerList); + + // 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, + 0, //no ip address yet. + NBT_UNIQUE, //use this as the default + (PVOID)pTracker, + QueryCompletion, + NodeType & NODE_MASK, + NULL, + pDeviceContext, + ppTracker, + &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 + pNameAddr->RefCount++; + // + // Incase this name is next in line to be purged from the cache, + // bump the timeoutcount back up so it will stick around. + // + pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + + // + // 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 + + ) + { + tNAMEADDR *pNameHdr; + + // + // 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 (pNameHdr = FindInDomainList(pTracker->pDestName,&DomainNames.DomainList)) { + pTracker->p1CNameAddr = pNameHdr; + pTracker->p1CNameAddr->RefCount++; + } else { + pTracker->p1CNameAddr = NULL; + + // + // Fail all connect attempts to 1C names. + // + if (pTracker->Flags & SESSION_SETUP_FLAG) { + KdPrint(("Session setup: p1CNameAddr was NULL\n")); +#if DBG + if (NodeType & BNODE) { + ASSERT(FALSE); + } +#endif + status = STATUS_UNEXPECTED_NETWORK_ERROR; + } + } + + } + + // + // overwrite the pDestName field with the pNameAddr value + // so that SendDgramContinue can send to Internet group names + // + pTracker->pNameAddr = pNameAddr; + *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(("NbtSendDatagram: 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); + } + return(status); +} +//---------------------------------------------------------------------------- +tNAMEADDR * +FindNameRemoteThenLocal( + IN tDGRAM_SEND_TRACKING *pTracker, + 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; + + pNameAddr = FindName( + NBT_REMOTE, + pTracker->Connect.pDestName, + NbtConfig.pScope, + (PUSHORT)plNameType); + if (!pNameAddr) + { + pNameAddr = FindName( + NBT_LOCAL, + pTracker->Connect.pDestName, + NbtConfig.pScope, + (PUSHORT)plNameType); + } + else + if (pNameAddr->AdapterMask & pTracker->pDeviceContext->AdapterNumber) + { + // only if the name is resolved on this adapter, return it... + pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + } + else + { + pNameAddr = NULL; + } + + 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; + + + KdPrint(("Nbt: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->fProxyReq) + { + NTSTATUS status; + // + // Stop the proxy timer. This will result in + // cleanup of the tracker buffer + // + NBT_PROXY_DBG(("NbtSendDatagram: 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!!!. + // + status = StopTimer( pNameAddr->pTimer,NULL,NULL); + + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = 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, + 0, //no ip address yet. + NBT_UNIQUE, //use this as the default + (PVOID)pTracker, + QueryCompletion, + NodeType & NODE_MASK, + pNameAddr, + pTracker->pDeviceContext, + NULL, + &OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(status); + + // + // NOTE: QueryNameOnNet frees the pNameAddr by calling NbtDereferenceName + // 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 = (tDGRAM_SEND_TRACKING *)pNameAddr->pTimer->ClientContext; + + 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); + } +} + +//---------------------------------------------------------------------------- +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 + +--*/ +{ + + tCLIENTELE *pClientEle; + tDGRAMHDR *pDgramHdr; + NTSTATUS status; + ULONG lNameType; + tNAMEADDR *pNameAddr; + tDGRAM_SEND_TRACKING *pTracker; + PCHAR pName,pSourceName; + ULONG NameLen; + LONG NameType; + ULONG SendCount; + + + CTEPagedCode(); + + // If there is no ip address configured, pretend that the datagram + // send succeeded. + // + if ((pDeviceContext->IpAddress == 0) || + (pDeviceContext->pDgramFileObject == NULL )) + { + return(STATUS_INVALID_DEVICE_REQUEST); + } + pClientEle = (tCLIENTELE *)pRequest->Handle.AddressHandle; + CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status); + + { + PTRANSPORT_ADDRESS pRemoteAddress; + PTA_NETBIOS_ADDRESS pRemoteNetBiosAddress; + PTA_NETBIOS_EX_ADDRESS pRemoteNetbiosExAddress; + ULONG TdiAddressType; + + pRemoteAddress = (PTRANSPORT_ADDRESS)pSendInfo->RemoteAddress; + TdiAddressType = pRemoteAddress->Address[0].AddressType; + pClientEle->AddressType = TdiAddressType; + + if (TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + PTDI_ADDRESS_NETBIOS pNetbiosAddress; + + pRemoteNetbiosExAddress = (PTA_NETBIOS_EX_ADDRESS)pRemoteAddress; + + CTEMemCopy(pClientEle->EndpointName, + pRemoteNetbiosExAddress->Address[0].Address[0].EndpointName, + sizeof(pRemoteNetbiosExAddress->Address[0].Address[0].EndpointName)); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NetBt:Handling New Address Type with SessionName %16s\n", + pClientEle->EndpointName)); + + pNetbiosAddress = &pRemoteNetbiosExAddress->Address[0].Address[0].NetbiosAddress; + pName = pNetbiosAddress->NetbiosName; + NameType = pNetbiosAddress->NetbiosNameType; + NameLen = pRemoteNetbiosExAddress->Address[0].AddressLength - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress) - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS,NetbiosName); + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NetBt:NETBIOS address NameLen(%ld) Name %16s\n",NameLen,pName)); + status = STATUS_SUCCESS; + } else if (TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS) { + // don't need spin lock because the client can't close this address + // since they are in this call and the handle has a reference count > 0 + // + //pClientEle->RefCount++; + + // this routine gets a ptr to the netbios name out of the wierd + // TDI address syntax. + ASSERT(pSendInfo->RemoteAddressLength); + status = GetNetBiosNameFromTransportAddress( + pSendInfo->RemoteAddress, + &pName, + &NameLen, + &lNameType); + } else { + status = STATUS_INVALID_ADDRESS_COMPONENT; + } + } + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Unable to get dest name from address in dgramsend")); + return(STATUS_INVALID_PARAMETER); + } + + pSourceName = ((tADDRESSELE *)pClientEle->pAddress)->pNameAddr->Name; + + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Dgram Send to = %16.16s<%X>\n",pName,pName[15])); + + + { + ULONG RemoteIpAddress; + + status = BuildSendDgramHdr(SendLength, + pDeviceContext, + pSourceName, + pName, + pBuffer, + &pDgramHdr, + &pTracker); + + + if (!NT_SUCCESS(status)) + { + return(status); + } + + // + // save the devicecontext that the client is sending on. + // + pTracker->pDeviceContext = (PVOID)pDeviceContext; + pTracker->Flags = DGRAM_SEND_FLAG; + + // + // if the name is longer than 16 bytes, it's not a netbios name. + // skip wins, broadcast etc. and go straight to dns resolution + // + + if (pClientEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + RemoteIpAddress = Nbt_inet_addr(pName); + } else { + RemoteIpAddress = 0; + } + + if (RemoteIpAddress != 0) { + tNAMEADDR *pRemoteNameAddr; + + // + // add this server name to the remote hashtable + // + + pRemoteNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('8')); + if (pRemoteNameAddr != NULL) + { + tNAMEADDR *pTableAddress; + + CTEZeroMemory(pRemoteNameAddr,sizeof(tNAMEADDR)); + InitializeListHead(&pRemoteNameAddr->Linkage); + CTEMemCopy(pRemoteNameAddr->Name,pName,NETBIOS_NAME_SIZE); + pRemoteNameAddr->Verify = REMOTE_NAME; + pRemoteNameAddr->RefCount = 1; + pRemoteNameAddr->NameTypeState = STATE_RESOLVED | NAMETYPE_UNIQUE; + pRemoteNameAddr->AdapterMask = (CTEULONGLONG)-1; + pRemoteNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + pRemoteNameAddr->IpAddress = RemoteIpAddress; + + status = AddToHashTable( + NbtConfig.pRemoteHashTbl, + pRemoteNameAddr->Name, + NbtConfig.pScope, + 0, + 0, + pRemoteNameAddr, + &pTableAddress); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NbtConnectCommon ...AddRecordToHashTable %s Status %lx\n",pRemoteNameAddr->Name,status)); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (status == STATUS_SUCCESS) { + PUCHAR pCopyTo; + + // + // Copy over the called name here. + // + pCopyTo = (PVOID)&pDgramHdr->SrcName.NameLength; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("pCopyTo:%lx\n", pCopyTo)); + + pCopyTo += 1 + // Length field + 2 * NETBIOS_NAME_SIZE + // actual name in half-ascii + NbtConfig.ScopeLength; // length of scope + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("pCopyTo:%lx\n", pCopyTo)); + + ConvertToHalfAscii( pCopyTo, + pClientEle->EndpointName, + NbtConfig.pScope, + NbtConfig.ScopeLength); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("Copied the remote name for dgram sends - IP\n")); + + SendDgramContinue(pTracker,status); + status = STATUS_PENDING; + } + } else { +// if ((pConnEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) || +// (NameLen > NETBIOS_NAME_SIZE)) { + if (NameLen > NETBIOS_NAME_SIZE) { + NBT_WORK_ITEM_CONTEXT *pContext; + + pTracker->AddressType = pClientEle->AddressType; + if (pClientEle->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + //IF_DBG(NBT_DEBUG_NETBIOS_EX) + //KdPrint(("$$$$$ Avoiding NETBIOS name translation on connection to %16s\n",pClientEle->RemoteName)); + } + pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('H')); + if (!pContext) + { + KdPrint(("Nbt: NbtConnect: couldn't alloc mem for pContext\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + } else { + PUCHAR pCopyTo; + + // + // Copy over the called name here. + // + pCopyTo = (PVOID)&pDgramHdr->SrcName.NameLength; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("pCopyTo:%lx\n", pCopyTo)); + + pCopyTo += 1 + // Length field + 2 * NETBIOS_NAME_SIZE + // actual name in half-ascii + NbtConfig.ScopeLength; // length of scope + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("pCopyTo:%lx\n", pCopyTo)); + + ConvertToHalfAscii( pCopyTo, + pClientEle->EndpointName, + NbtConfig.pScope, + NbtConfig.ScopeLength); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("Copied the remote name for dgram sends DNS\n")); + + pContext->pTracker = NULL; // no query tracker + pContext->pClientContext = pTracker; // the client tracker + pContext->ClientCompletion = SendDgramContinue; + status = DoDnsResolve(pContext); + } + } else { + status = FindNameOrQuery(pTracker, + pName, + pDeviceContext, + SendDgramContinue, + NULL, + TRUE, + &pNameAddr); + } + } + + // this routine checks the hash tables and does a name query if + // it can't find the name. If it finds the name, it returns + // STATUS_SUCCESS. + + // + // in other words Pending was not returned... + // + if (status == STATUS_SUCCESS) + { + // + // don't set the status here since SendDgram cleans up the + // tracker and we don't want to clean it up again below + // if there is a failure. + // + SendDgram(pNameAddr,pTracker); + + } + + if (!NT_SUCCESS(status)) + { + // + // *** Error Handling Here *** + // +#ifdef VXD + pTracker->pNameAddr = NULL; +#endif + + DgramSendCleanupTracker(pTracker,status,0); + } + else + if (status == STATUS_PENDING) + { + // + // do not return pending since the datagram is buffered and it + // is always completed. + // + status = STATUS_SUCCESS; + } + + + } + + // the amount sent is the whole datagram, although we really don't + // know if it got sent since the name query phase has not ended yet + // + *pSentLength = SendLength; + // + // return the status to the client. + // + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +BuildSendDgramHdr( + IN ULONG SendLength, + IN tDEVICECONTEXT *pDeviceContext, + IN PCHAR pSourceName, + IN PCHAR pName, + 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; + PCTE_MDL pMdl; + CTELockHandle OldIrq; + + CTEPagedCode(); + + HdrLength = DGRAM_HDR_SIZE + (NbtConfig.ScopeLength <<1); + HLength = ((HdrLength + 3 )/4)*4; // 4 byte aligned the hdr size + TotalLength = HLength + SendLength + NETBIOS_NAME_SIZE; + + CTECountedAllocMem((PVOID *)&pDgramHdr,TotalLength); + + if (!pDgramHdr) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + *ppDgramHdr = pDgramHdr; + + // fill in the Dgram header + pDgramHdr->Flags = FIRST_DGRAM | (NbtConfig.PduNodeType >> 10); + + pDgramHdr->DgramId = htons(GetTransactId()); + + pDgramHdr->SrcPort = htons(NBT_DATAGRAM_UDP_PORT); + + // + // 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) )); + + CHECK_PTR(pDgramHdr); + pDgramHdr->PckOffset = 0; // not fragmented for now! + pDgramHdr->SrcIpAddr = htonl(pDeviceContext->IpAddress); + + 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,pName,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,pName,NETBIOS_NAME_SIZE); + + // + // copy the client's send buffer to our buffer so the send dgram can + // complete immediately. + // + pSendBuffer = (PVOID)((PUCHAR)pDgramHdr + HLength + NETBIOS_NAME_SIZE); + if (SendLength) + { +#ifdef VXD + CTEMemCopy(pSendBuffer,pBuffer,SendLength); +#else + { + ULONG BytesCopied; + + status = TdiCopyMdlToBuffer(pBuffer, + 0, + pSendBuffer, + 0, + SendLength, + &BytesCopied); + + if (NT_SUCCESS(status) && (BytesCopied == SendLength)) + { + // + // Allocate an MDL since in NT the pBuffer is really an MDL + // + pMdl = IoAllocateMdl( + pSendBuffer, + SendLength, + FALSE, + FALSE, + NULL); + if (!pMdl) + { + CTECountedFreeMem((PVOID)pDgramHdr,TotalLength); + return(STATUS_INSUFFICIENT_RESOURCES); + } + else + { + // + // Lock the Mdl buffer down + // + MmBuildMdlForNonPagedPool(pMdl); + pSendBuffer = (PVOID)pMdl; + } + + } + else + { + CTECountedFreeMem((PVOID)pDgramHdr,TotalLength); + return(STATUS_UNSUCCESSFUL); + } + } +#endif + } + else + { + pSendBuffer = NULL; + } + + // + // get a buffer for tracking Dgram Sends + // + pTracker = NbtAllocTracker(); + if (pTracker) + { + + CHECK_PTR(pTracker); + pTracker->SendBuffer.pBuffer = pSendBuffer; + pTracker->SendBuffer.Length = SendLength; + pTracker->SendBuffer.pDgramHdr = pDgramHdr; + pTracker->SendBuffer.HdrLength = HdrLength; + pTracker->pClientEle = NULL; + pTracker->pDestName = pNameBuffer; + pTracker->AllocatedLength = TotalLength; + pTracker->p1CNameAddr = NULL; + + *ppTracker = pTracker; + + status = STATUS_SUCCESS; + } + else + { +#ifndef VXD + if (pSendBuffer) + { + IoFreeMdl(pMdl); + } +#endif + CTECountedFreeMem((PVOID)pDgramHdr,TotalLength); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + return(status); + + +} +//---------------------------------------------------------------------------- +USHORT +GetTransactId( + ) +/*++ +Routine Description: + + This Routine increments the transaction id with the spin lock held. + It uses NbtConfig.JointLock. + +Arguments: + +Return Value: + + +--*/ + +{ + CTELockHandle OldIrq; + USHORT TransactId; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + TransactId = GetTransactIdLocked(); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return (TransactId); + +} + +USHORT +GetTransactIdLocked( +) +{ + USHORT TransactId; + 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 + return (TransactId); +} + +//---------------------------------------------------------------------------- +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); +} + +//---------------------------------------------------------------------------- +VOID +CTECountedFreeMem( + PVOID pBuffer, + ULONG Size + ) +/*++ +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; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + ASSERT(NbtMemoryAllocated >= Size); + if (NbtMemoryAllocated >= Size) + { + NbtMemoryAllocated -= Size; + } + else + { + NbtMemoryAllocated = 0; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CTEMemFree(pBuffer); + +} + +//---------------------------------------------------------------------------- +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 + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + tNAMEADDR *pNameAddr=NULL; + ULONG lNameType; + PLIST_ENTRY pHead; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + pHead = &pTracker->TrackerList; + + DELETE_CLIENT_SECURITY(pTracker); + + // + // 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)) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pNameAddr = FindNameRemoteThenLocal(pTracker,&lNameType); + + // + // 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 + + ) + { + pTracker->p1CNameAddr = FindInDomainList(pTracker->pDestName,&DomainNames.DomainList); + + // + // if there is no pNameAddr then just make the domain list + // name the only pNameAddr to send to. + // + if (!pNameAddr) + { + pNameAddr = pTracker->p1CNameAddr; + CHECK_PTR(pTracker); + pTracker->p1CNameAddr = NULL; + } + + } + + // 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) + { + // increment this ref count so that it can't disappear + // during the send - decrement in the sendDgramCompletion + // routine + // + pNameAddr->RefCount++; + + if (pTracker->p1CNameAddr) + { + pTracker->p1CNameAddr->RefCount++; + } + + // overwrite the pDestName field with the pNameAddr value + // so that SendDgramContinue can send to Internet group names + // + pTracker->pNameAddr = pNameAddr; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // send the first datagram queued to this name + status = SendDgram(pNameAddr,pTracker); + + // a failure ret code means the send failed, so cleanup + // the tracker etc. below. + if (NT_SUCCESS(status)) + { + return; + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + + // + // What can happen is TCP calls SessionStartupContinue with an error + // which then calls CTEIoComplete, however this request is still on + // the timer queue, so when the client request is serviced, this + // routine is called (TimerExpiry, MSnodeCompletion, + // CompleteClientReq, SendDgramContinue) which frees everything + // again! Presumably this is not isolated to Vxd-land. + + // Note: the Timer is now stopped in QueryNameOnNet if it fails to + // send to TCP, which should remove this problem... still testing it + // so leave comment here for a while. + // + + + // this is the ERROR handling if something goes wrong with the send + // it cleans up the tracker and completes the client irp + // + // set this so that the cleanup routine does not try to dereference + // the nameAddr + CHECK_PTR(pTracker); + + // check if pNameAddr refcount got incremented or not + if (!pNameAddr) + { + pTracker->pNameAddr = NULL; + } + + if (status == STATUS_TIMEOUT) + { + status = STATUS_BAD_NETWORK_PATH; + } + + DgramSendCleanupTracker(pTracker,status,0); + +} + +//---------------------------------------------------------------------------- +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. + +Arguments: + + pContext - ptr to the DGRAM_TRACKER block + NTSTATUS - completion status + +Return Values: + + VOID + +--*/ +{ + ULONG IpAddress; + NTSTATUS status; + PFILE_OBJECT pFileObject; + + 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); + } + else + { + // + // for sends to this node, use the ip address of the deviceContext + // if its a unique name. + // + if ((pNameAddr->Verify == REMOTE_NAME) || + (!(pNameAddr->NameTypeState & NAMETYPE_UNIQUE))) + { + IpAddress = pNameAddr->IpAddress; + + if (pNameAddr->NameTypeState & NAMETYPE_GROUP) + { + IpAddress = 0; + } + } + else + { + IpAddress = pTracker->pDeviceContext->IpAddress; + } + + // flag that there are no more addresses in the list + CHECK_PTR(pTracker); + pTracker->IpListIndex = 0; + + } + + // send the Datagram... + if (pTracker->pDeviceContext->IpAddress) + { + pFileObject = pTracker->pDeviceContext->pDgramFileObject; + } + else + pFileObject = NULL; + status = UdpSendDatagram( + pTracker, + IpAddress, + pFileObject, + SendDgramCompletion, + pTracker, // context for completion + NBT_DATAGRAM_UDP_PORT, + NBT_DATAGRAM_SERVICE); + + // the irp will be completed via SendDgramCompletion + // so don't complete it by the caller too + status = STATUS_PENDING; + + return(status); + +} +//---------------------------------------------------------------------------- +VOID +SendDgramDist ( + IN PVOID Context + ) + +/*++ + +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; + tDGRAM_SEND_TRACKING *pTracker; + ULONG IpAddress; + PFILE_OBJECT pFileObject; + + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + + IpAddress = (ULONG)((NBT_WORK_ITEM_CONTEXT *)Context)->pClientContext; + + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:DgramDistribution to name %15.15s<%X>:Ip %X, \n", + pTracker->pNameAddr->Name,pTracker->pNameAddr->Name[15],IpAddress)); + + // send the Datagram... + if (pTracker->pDeviceContext->IpAddress) + { + pFileObject = pTracker->pDeviceContext->pDgramFileObject; + } + else + pFileObject = NULL; + status = UdpSendDatagram( + pTracker, + IpAddress, + pFileObject, + SendDgramCompletion, + pTracker, + NBT_DATAGRAM_UDP_PORT, + NBT_DATAGRAM_SERVICE); + + CTEMemFree(Context); + +} +//---------------------------------------------------------------------------- +VOID +SendDgramCompletion( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo) +/*++ +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 + +--*/ + +{ + tDGRAM_SEND_TRACKING *pTracker; + ULONG IpAddress; + ULONG EndOfList; + CTELockHandle OldIrq; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + // + // The list ends in a -1 ipaddress, so stop when we see that + // + EndOfList = (ULONG)-1; + + // 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); + + // decrement the ref count done during the send + pTracker->RCount--; + + if (pTracker->IpListIndex) + { + if (pTracker->IpListIndex < LAST_DGRAM_DISTRIBUTION) + { + IpAddress = pTracker->pNameAddr->pIpList->IpAddr[ + pTracker->IpListIndex++]; + + if (IpAddress != EndOfList) + { + + pTracker->RCount++; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEQueueForNonDispProcessing(pTracker,(PVOID)IpAddress,NULL, + SendDgramDist,pTracker->pDeviceContext); + return; + + } + else + { + // + // set to this so that the next DgramDistTimeout will call + // DgramSendCleanupTracker + // + pTracker->IpListIndex = LAST_DGRAM_DISTRIBUTION; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + } + else + { + // we have just completed the last datagram distribution and we + // must let the DgramDistTimeout below free the tracker. If the + // timer has not been started, then fall through to call + // DgramSendCleanup in this routine. + // + if (pTracker->Connect.pTimer) + { + pTracker->IpListIndex = END_DGRAM_DISTRIBUTION; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + } + } + else + if ((pTracker->p1CNameAddr) && + (pTracker->pNameAddr->Name[NETBIOS_NAME_SIZE-1] == 0x1c)) + { + tNAMEADDR *pNameAddr; + + // + // There may be a list of addresses obtained from lmhosts for a send + // to a 1c name. In this case do a datagram distribution. + // + // + // dereference the name that is in the remote cache since + // we are done sending to it and we are about to send to the + // 1CNameAddr from the DomainNames list. + // + NbtDereferenceName(pTracker->pNameAddr); + + pNameAddr = pTracker->p1CNameAddr; + CHECK_PTR(pTracker); + pTracker->p1CNameAddr = NULL; + + pTracker->pNameAddr = pNameAddr; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + DatagramDistribution(pTracker,pNameAddr); + + return; + + } + + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + DgramSendCleanupTracker(pTracker,status,lInfo); + +} +//---------------------------------------------------------------------------- +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; + + 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:Cleanup After DgramDistribution %15.15s<%X> \n", + pTracker->pNameAddr->Name,pTracker->pNameAddr->Name[15])); + + // + // there may be another set of addresses to send to if there is + // an lmhost file + // + if (pTracker->p1CNameAddr) + { + ULONG IpAddress; + + pNameAddr = pTracker->p1CNameAddr; + CHECK_PTR(pTracker); + pTracker->p1CNameAddr = NULL; + + // + // Start up DgramDistribution for the new list + // + IpAddress = pNameAddr->pIpList->IpAddr[0]; + + // + // set this so that we can send the next datagram in + // SendDgramCompletion + // + pTracker->IpListIndex = 1; + + // + // dereference the name that is in the remote cache since + // we are done sending to it and we are about to send to the + // 1CNameAddr from the DomainNames list. + // + NbtDereferenceName(pTracker->pNameAddr); + + pTracker->pNameAddr = pNameAddr; + pTracker->RCount++; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEQueueForNonDispProcessing(pTracker,(PVOID)IpAddress,NULL, + SendDgramDist,pTracker->pDeviceContext); + + pTimerQEntry->Flags |= TIMER_RESTART; + return; + } + else + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + DgramSendCleanupTracker(pTracker,STATUS_SUCCESS,0); + 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:DgramDistribution hung up on ARP forcing next send\n")); + + pTracker->SavedListIndex = pTracker->IpListIndex; + + // increment to account for the decrement in SendDgram + pTracker->RCount++; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + SendDgramCompletion(pTracker,STATUS_SUCCESS,0); + pTimerQEntry->Flags |= TIMER_RESTART; + 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; + tIPLIST *IpList; + ULONG Index; + ULONG IpAddress; + ULONG TotalNumber; + PFILE_OBJECT pFileObject; + + // NOTE: this could be made to be paged code IF it was farmed out to + // a worker thread!!! + // + Index = 0; + IpList = pTracker->pNameAddr->pIpList; + // + // count the number of addresses to send to and put that in the tracker + // so we can count datagram completions + // + while (IpList->IpAddr[Index++] != (ULONG)-1); + + TotalNumber = Index; + pTracker->IpListIndex = (USHORT)Index; + // + // 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 = IpList->IpAddr[Index]; + + if (IpAddress != (ULONG)-1) + { + + // + // set this so that we can send the next datagram in + // SendDgramCompletion + // + pTracker->IpListIndex = 1; + + // for each send, increment ref count so it ends up a 0 when + // the last send completes + // + pTracker->RCount = 1; + + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:DgramDistribution to name %15.15s<%X>:Ip %X, \n", + pNameAddr->Name,pNameAddr->Name[15],IpAddress)); + + // send the Datagram... + if (pTracker->pDeviceContext->IpAddress) + { + pFileObject = pTracker->pDeviceContext->pDgramFileObject; + } + else + pFileObject = NULL; + status = UdpSendDatagram( + pTracker, + IpAddress, + pFileObject, + SendDgramCompletion, + pTracker, + NBT_DATAGRAM_UDP_PORT, + NBT_DATAGRAM_SERVICE); + + Locstatus = LockedStartTimer( + DGRAM_SEND_TIMEOUT, + pTracker, + DgramDistTimeout, + pTracker, + DgramDistTimeout, + 1, + pNameAddr, + FALSE); + + if (!NT_SUCCESS(Locstatus)) + { + CHECK_PTR(pTracker); + pTracker->Connect.pTimer = NULL; + } + } + + if (!NT_SUCCESS(status)) + { + // + // we failed to send probably because of a lack of + // free memory + // + pTracker->RCount--; + DgramSendCleanupTracker(pTracker,STATUS_SUCCESS,0); + + } + return(status); +} +//---------------------------------------------------------------------------- +VOID +DgramSendCleanupTracker( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS status, + IN ULONG Length + ) + +/*++ +Routine Description + + This routine cleans up after a data gram send. + +Arguments: + + pTracker + status + Length + +Return Values: + + VOID + +--*/ + +{ + CTELockHandle OldIrq; + 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) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + NbtDereferenceName(pTracker->pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + + +#ifndef VXD + // this check is necessary for the Proxy case, since it does not + // have a Buffer + // + if (pTracker->SendBuffer.pBuffer) + { + IoFreeMdl((PMDL)pTracker->SendBuffer.pBuffer); + } +#endif + // + // free the buffer used for sending the data and free + // the tracker + // + CTECountedFreeMem((PVOID)pTracker->SendBuffer.pDgramHdr, + pTracker->AllocatedLength); + + CTEFreeMem(pTracker); + + + +} + +//---------------------------------------------------------------------------- +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:EventHandler # %X set, on name %16.16s<%X>\n",EventType, + ((tADDRESSELE *)pClientEle->pAddress)->pNameAddr->Name, + ((tADDRESSELE *)pClientEle->pAddress)->pNameAddr->Name[15])); + + 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; + 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; + + default: + ASSERTMSG("Invalid Event Type passed to SetEventHandler\n", + (PVOID)0L); + + + } + } + 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; + 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; + + default: + ASSERTMSG("Invalid Event Type passed to SetEventHandler\n", + (PVOID)0L); + } + } + + CTESpinFree(pClientEle,OldIrq); + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtSendNodeStatus( + IN tDEVICECONTEXT *pDeviceContext, + IN PCHAR pName, + IN PIRP pIrp, + IN PULONG pIpAddrsList, + 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 NodeStatusDone, and ClientContext is 0. + 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 SessionSetupContinue, + 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) + +Arguments: + + +Return Values: + + TDI_STATUS - status of the request + +--*/ +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + ULONG lNameType; + ULONG Length; + PUCHAR pHdr; + tNAMEADDR *pNameAddr; + ULONG UNALIGNED * pAddress; + PFILE_OBJECT pFileObject; + ULONG IpAddress; + PCHAR pName0; + + + + status = GetTracker(&pTracker); + if (!NT_SUCCESS(status)) + { + return(status); + } + + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Send Node Status to = %16.16s<%X>\n",pName,pName[15])); + + pName0 = Nbt_inet_addr(pName) ? "*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" : pName; + + // 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); + } + + // 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 + // note that the passed in transport address must stay valid till this + // send completes + pTracker->SendBuffer.pDgramHdr = (PVOID)pHdr; + pTracker->SendBuffer.HdrLength = Length; + pTracker->SendBuffer.pBuffer = NULL; + CHECK_PTR(&pTracker); + pTracker->SendBuffer.Length = 0; + pTracker->Flags = REMOTE_ADAPTER_STAT_FLAG; + + // one for the send completion and one for the node status completion + pTracker->RefCount = 2; + + pTracker->pDestName = pName; + pTracker->pClientIrp = pIrp; + pTracker->pDeviceContext = (PVOID)pDeviceContext; + + status = FindNameOrQuery(pTracker, + pName, + pDeviceContext, + SendNodeStatusContinue, + NULL, + FALSE, + &pNameAddr); + + if (status == STATUS_SUCCESS) + { + // + // If the ip addr(s) is passed in then the name is '*', meaning, send + // an adapter status call to the ip address specified. See how many + // ip addrs are there, allocate that much memory and store them + // + if ((pIpAddrsList) && (*pIpAddrsList) && (pName[0] == '*')) + { + int i=0; + + // 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 + pNameAddr->pIpAddrsList = NbtAllocMem(i*sizeof(ULONG),NBT_TAG('M')); + if(!pNameAddr->pIpAddrsList) + { + FreeTracker(pTracker,RELINK_TRACKER); + CTEMemFree(pHdr); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + i = 0; + do + { + pNameAddr->pIpAddrsList[i] = pIpAddrsList[i]; + } while(pIpAddrsList[i++]); + } + + if (pName[0] == '*') + { + ASSERT(pNameAddr->pIpAddrsList); + } + + // + // 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 = LockedStartTimer( + NbtConfig.uRetryTimeout, + pTracker, + NodeStatusCompletion, + ClientContext, + CompletionRoutine, + NbtConfig.uNumRetries, + pNameAddr, + FALSE); + + if (NT_SUCCESS(status)) + { + // + // if its a unique name on this node then use this devicecontext's + // ip address. + // + if ((pNameAddr->Verify == REMOTE_NAME) || + (!(pNameAddr->NameTypeState & NAMETYPE_UNIQUE))) + { + // + // if we have multiple ipaddrs, just choose the first one + // + if(pNameAddr->pIpAddrsList) + { + IpAddress = pNameAddr->pIpAddrsList[0]; + pNameAddr->IpAddress = IpAddress; + } + else + { + IpAddress = pNameAddr->IpAddress; + } + + } + else + { + IpAddress = pTracker->pDeviceContext->IpAddress; + } + + if (pDeviceContext->IpAddress) + { + pFileObject = pDeviceContext->pNameServerFileObject; + } + else + pFileObject = NULL; + + // 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.SpinLock); + + status = UdpSendDatagram(pTracker, + IpAddress, + pFileObject, + NameDgramSendCompleted, + pHdr, // context + NBT_NAMESERVICE_UDP_PORT, + NBT_NAME_SERVICE); + + DereferenceTracker(pTracker); + + // + // BUGBUG - Not returning status reflecting failure to send datagram + // to client. Client will eventually get STATUS_TIMEOUT. + // + return(STATUS_PENDING); + } + else + { // + // the timer failed to start so undo the ref done in FindNameOrQuery + // + LockedDereferenceName(pNameAddr); + + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + } + + + return(status); + } + else + if (NT_SUCCESS(status)) + { + // i.e. pending was returned rather than success + return(status); + } + else + { + // + // Failed to FindNameOrQuery - probably out of memory + // + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + return(status); + } + +} + +//---------------------------------------------------------------------------- +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; + ULONG NameLen; +#ifndef VXD + PIO_STACK_LOCATION pIrpSp; + CTELockHandle OldIrq1; +#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( + pInfo->RemoteAddress, + &pName, + &NameLen, + &lNameType); + + if (!NT_SUCCESS(status) || (lNameType != TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) + || (NameLen > NETBIOS_NAME_SIZE)) + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Unable to get dest name from address in QueryFindName\n")); + return(STATUS_INVALID_PARAMETER); + } + } +#ifndef VXD + else + { + pName = ((tIPADDR_BUFFER *)pInfo)->Name; + } +#endif + + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:QueryFindName 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); + if (!NT_SUCCESS(status)) + { + return(status); + } + + pTracker->pClientIrp = pIrp; + pTracker->pDestName = pName; + pTracker->pDeviceContext = pDeviceContext; + + // + // 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). + // + pTracker->Flags = REMOTE_ADAPTER_STAT_FLAG|FIND_NAME_FLAG; + +#ifndef VXD + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->Parameters.Others.Argument4 = (PVOID)pTracker; + status = NTCheckSetCancelRoutine( pIrp,FindNameCancel,pDeviceContext ); + + if (status == STATUS_CANCELLED ) + { + FreeTracker(pTracker,RELINK_TRACKER); + return(status); + } +#endif + + status = FindNameOrQuery(pTracker, + pName, + pDeviceContext, + QueryNameCompletion, + NULL, + FALSE, + &pNameAddr); + + if ((status == STATUS_SUCCESS) || (!NT_SUCCESS(status))) + { + +#ifndef VXD + IoAcquireCancelSpinLock(&OldIrq1); + pClientIrp = pTracker->pClientIrp; + if (pClientIrp == pIrp) + { + pTracker->pClientIrp = NULL; + } + pIrpSp->Parameters.Others.Argument4 = NULL; + IoReleaseCancelSpinLock(OldIrq1); +#else + pClientIrp = pTracker->pClientIrp; +#endif + FreeTracker(pTracker,RELINK_TRACKER); + + if (pClientIrp) + { + ASSERT( pClientIrp == pIrp ); + + if (status == STATUS_SUCCESS) + { + status = CopyFindNameData(pNameAddr,pIrp,pDeviceContext->IpAddress); + + LockedDereferenceName(pNameAddr); + } + } + + // + // irp is already completed: return pending so we don't complete again + // + else + { + if (status == STATUS_SUCCESS) + { + LockedDereferenceName(pNameAddr); + } + status = STATUS_PENDING; + } + } + + 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 OldIrq, 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 (FindNameCancel) 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 + IoReleaseCancelSpinLock(OldIrq1); + +// +// Make sure all parameters are valid for the Irp processing +// + if (! ((pClientIrp) && + (pIrpSp = IoGetCurrentIrpStackLocation(pClientIrp)) && + (pIrpSp->Parameters.Others.Argument4 == pTracker))) + { + KdPrint(("Nbt: irp from Tracker <0x%4X> has been cancelled already\n", pTracker)); + FreeTracker( pTracker,RELINK_TRACKER ); + return; + } +#endif + + pIrpSp->Parameters.Others.Argument4 = NULL; + CTESpinLock(&NbtConfig.JointLock,OldIrq); + +#ifndef VXD + NTCancelCancelRoutine(pClientIrp); + NTClearFileObjectContext(pClientIrp); +#endif + + if (status == STATUS_SUCCESS) + { + // + // attempt to find the destination name in the local/remote hash table. + // + pNameAddr = FindNameRemoteThenLocal(pTracker,&lNameType); + + if (pNameAddr) + { + status = CopyFindNameData(pNameAddr,pClientIrp, + pTracker->pDeviceContext->IpAddress); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CTEIoComplete(pClientIrp,status,0xFFFFFFFF); + + FreeTracker(pTracker,RELINK_TRACKER); + return; + } + } + + // this is the ERROR handling if something goes wrong with the send + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CTEIoComplete(pClientIrp,STATUS_IO_TIMEOUT,0L); + + FreeTracker(pTracker,RELINK_TRACKER); + +} + + + +//---------------------------------------------------------------------------- +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; + tNAMEADDR *pNameAddr; + ULONG lNameType; + PLIST_ENTRY pHead; + tTIMERQENTRY *pTimerEntry; + ULONG IpAddress; + PCTE_IRP pIrp; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + pHead = &pTracker->TrackerList; + + DELETE_CLIENT_SECURITY(pTracker); + + // + // attempt to find the destination name in the remote hash table. If its + // there, then send to it. + // + if (status == STATUS_SUCCESS) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pNameAddr = FindNameRemoteThenLocal(pTracker,&lNameType); + + if (pNameAddr) + { + // increment refcount so the name does not disappear out from under us + // dereference when we get the response or timeout + pNameAddr->RefCount++; + // + // 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( + NbtConfig.uRetryTimeout, + (PVOID)pTracker, // context value + NULL, // context2 value + NodeStatusCompletion, + NULL, + NodeStatusDone, + NbtConfig.uNumRetries, + &pTimerEntry); + + if (NT_SUCCESS(status)) + { + PFILE_OBJECT pFileObject; + + pTracker->Connect.pNameAddr = pNameAddr; + pTracker->Connect.pTimer = pTimerEntry; + + // + // if the name is on this node then use this devicecontext + // ip address (if a unique name) + // + if ((pNameAddr->Verify == REMOTE_NAME) || + (!(pNameAddr->NameTypeState & NAMETYPE_UNIQUE))) + { + IpAddress = pNameAddr->IpAddress; + } + else + { + IpAddress = pTracker->pDeviceContext->IpAddress; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // send the Datagram... + if (pTracker->pDeviceContext->IpAddress) + { + pFileObject = pTracker->pDeviceContext->pNameServerFileObject; + } + else + pFileObject = NULL; + + // 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.SpinLock); + + status = UdpSendDatagram(pTracker, + IpAddress, + pFileObject, + NameDgramSendCompleted, + pTracker->SendBuffer.pDgramHdr, // context + NBT_NAMESERVICE_UDP_PORT, + NBT_NAME_SERVICE); + + // + // this undoes one of two ref's added in NbtSendNodeStatus + // + DereferenceTracker(pTracker); + // if the send fails, the timer will resend it...so no need + // to check the return code here. + return; + } + else + { + NbtDereferenceName(pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + // this is the ERROR handling if something goes wrong with the send + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + pIrp = pTracker->pClientIrp; + pTracker->pClientIrp = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); + + CTEIoComplete(pIrp,STATUS_IO_TIMEOUT,0L); + +} + +//---------------------------------------------------------------------------- +VOID +NodeStatusDone( + IN PVOID pContext, + IN NTSTATUS status + ) +/*++ + +Routine Description + + This routine handles sending nodes status data back up to the client when + the node status request completes on the network. This routine is the + client completion routine of the timer started above. + +Arguments: + + pContext - ptr to the DGRAM_TRACKER block + NTSTATUS - completion status + +Return Values: + + VOID + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + PCTE_IRP pIrp; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + LOCATION(0x3E); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + pIrp = pTracker->pClientIrp; + pTracker->pClientIrp = NULL; + + // remove the reference done in FindNameOrQuery + // + NbtDereferenceName(pTracker->Connect.pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // the tracker block was unlinked in DecodeNodeStatusResponse, + // and its header was freed when the send completed, so just relink + // it here - this deref should do the relink. + // + DereferenceTracker(pTracker); + + if (status == STATUS_SUCCESS || + status == STATUS_BUFFER_OVERFLOW ) // Only partial data copied + { + // -1 means the receive length is already set in the irp + CTEIoComplete(pIrp,status,0xFFFFFFFF); + } + else + { + // + // failed to get the adapter status, so + // return failure status to the client. + // + + CTEIoComplete(pIrp,STATUS_IO_TIMEOUT,0); + } +} + +//---------------------------------------------------------------------------- +NTSTATUS +CopyFindNameData( + IN tNAMEADDR *pNameAddr, + IN PIRP pIrp, + IN ULONG SrcAddress) + +/*++ +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; + PULONG pIpAddr; + ULONG BuffSize; + ULONG DataLength; + ULONG NumNames; + ULONG i; + + if (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) + { + NumNames = 0; + pIpAddr = pNameAddr->pIpList->IpAddr; + while (*pIpAddr != (ULONG)-1) + { + pIpAddr++; + NumNames++; + } + } + else + { + NumNames = 1; + } + +#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); + } + + pFindNameHdr = NbtAllocMem((USHORT)BuffSize,NBT_TAG('N')); + if (!pFindNameHdr) + { + 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; + + SrcAddress = htonl(SrcAddress); + if (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) + { + pIpAddr = pNameAddr->pIpList->IpAddr; + 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. + *(ULONG UNALIGNED *)&pFindNameBuffer->source_addr[2] = htonl(*pIpAddr); + pIpAddr++; + *(ULONG UNALIGNED *)&pFindNameBuffer->destination_addr[2] = SrcAddress; + pFindNameBuffer++; + + } + } + else + { + // + // if the name is on this node then use the address of this device + // context - if its a unique name. + // + if ((pNameAddr->Verify == REMOTE_NAME) || + (!(pNameAddr->NameTypeState & NAMETYPE_UNIQUE))) + { + *(ULONG UNALIGNED *)&pFindNameBuffer->source_addr[2] = htonl(pNameAddr->IpAddress); + } + else + { + + *(ULONG UNALIGNED *)&pFindNameBuffer->source_addr[2] = SrcAddress; + + } + *(ULONG UNALIGNED *)&pFindNameBuffer->destination_addr[2] = SrcAddress; + } + +#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 + + CTEMemFree((PVOID)pFindNameHdr); + + return(status); +} + + +//---------------------------------------------------------------------------- +VOID +FreeTracker( + IN tDGRAM_SEND_TRACKING *pTracker, + IN ULONG Actions + ) +/*++ + +Routine Description: + + This routine cleans up a Tracker block and puts it back on the free + queue. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + CTELockHandle OldIrq; + + CTESpinLock(&NbtConfig,OldIrq); + + if (Actions & REMOVE_LIST) + { + // + // unlink the tracker block from the NodeStatus Q + RemoveEntryList(&pTracker->Linkage); + } + + if (Actions & FREE_HDR) + { + // return the datagram hdr to the free pool + // + if (pTracker->SendBuffer.pDgramHdr) + { + CTEMemFree((PVOID)pTracker->SendBuffer.pDgramHdr); + } + + } + // + CHECK_PTR(pTracker); + +#if DBG + { + PLIST_ENTRY pHead,pEntry; + tDGRAM_SEND_TRACKING *pTrack; + + // + // check if the tracker is already in the list or not! + // + pHead = &NbtConfig.DgramTrackerFreeQ; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pTrack = CONTAINING_RECORD(pEntry,tDGRAM_SEND_TRACKING,Linkage); + ASSERT(pTrack != pTracker); + pEntry = pEntry->Flink; + } + + ASSERT(pTracker->Verify == NBT_VERIFY_TRACKER); + + pTracker->Verify -= 10; + pTracker->pClientIrp = (PVOID)0x1F1F1F1F; + + pTracker->pConnEle = (PVOID)0x1F1F1F1F; + pTracker->SendBuffer.HdrLength = 0x1F1F1F1F; + pTracker->SendBuffer.pDgramHdr = (PVOID)0x1F1F1F1F; + pTracker->SendBuffer.Length = 0x1F1F1F1F; + pTracker->SendBuffer.pBuffer = (PVOID)0x1F1F1F1F; + pTracker->pDeviceContext = (PVOID)0x1F1F1F1F; + pTracker->pTimer = (PVOID)0x1F1F1F1F; + pTracker->RefCount = 0x1F1F1F1F; + pTracker->pDestName = (PVOID)0x1F1F1F1F; + pTracker->pNameAddr = (PVOID)0x1F1F1F1F; +#ifdef VXD + pTracker->pchDomainName = (PVOID)0x1F1F1F1F; +#endif + pTracker->pTimeout = (PVOID)0x1F1F1F1F; + pTracker->SrcIpAddress = 0x1F1F1F1F; + pTracker->CompletionRoutine = (PVOID)0x1F1F1F1F; + pTracker->Flags = 0x1F1F; + } +#endif + + CHECK_PTR(pTracker); + pTracker->SendBuffer.pDgramHdr = NULL; + if (pTracker->IpList) { + ASSERT(pTracker->NumAddrs != 0); + CTEMemFree(pTracker->IpList); + + pTracker->IpList = NULL; + pTracker->NumAddrs = 0x1F1F1F1F; + } + CTESpinFree(&NbtConfig,OldIrq); + + //REMOVE_FROM_LIST(&pTracker->DebugLinkage); + ExInterlockedInsertTailList(&NbtConfig.DgramTrackerFreeQ, + &pTracker->Linkage, + &NbtConfig.SpinLock); + + + +} +//---------------------------------------------------------------------------- +VOID +DereferenceIfNotInRcvHandler +( + IN tCONNECTELE *pConnEle, + IN tLOWERCONNECTION *pLowerConn + ) +/*++ + +Routine Description + + This routine used to coordinate with the recv handler and not do the + dereference if it was in the rcv handler. Now it does it anyway and + the recv handler checks if pUpperConnection is still valid anywhere it + releases the spin lock and gets it again. + +Arguments: + + +Return Values: + + TDI_STATUS - status of the request + +--*/ +{ + + PUSH_LOCATION(0x66); + NbtDereferenceConnection(pConnEle); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtQueryAdapterStatus( + IN tDEVICECONTEXT *pDeviceContext, + OUT PVOID *ppAdapterStatus, + IN OUT PLONG pSize + ) +/*++ + +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 Count=0; + LONG i,j; + LONG BuffSize; + PADAPTER_STATUS pAdapterStatus; + PLIST_ENTRY pEntry; + PLIST_ENTRY pHead; + PNAME_BUFFER pNameBuffer; + tADDRESSELE *pAddressEle; + BOOL fOverFlow = FALSE ; + tNAMEADDR *pNameAddr; + tHASHTABLE *pHashTable; + ULONG NameSize; + USHORT MaxAllowed; + PUCHAR pMacAddr; + + // a null value for devicecontext means get the remote hash table entries + // - do this check without holding the spin lock because the macro does + // a return if it fails - which of course would not release the spin lock. + if (pDeviceContext) + { + // validate the device context + CTEVerifyHandle(pDeviceContext,NBT_VERIFY_DEVCONTEXT,tDEVICECONTEXT,&status) + } + + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + if (pDeviceContext) + { + + // count the number of netbios names + // + // CountLocalNames returns all names except the '*' names and resolving names. + // Now, we come here and bump up the count by one (on assumption that the only + // '*' name is the "*0000000" name). However, there are now al least two other + // names - "*SMBSERVER and the bowser name. + // + // So, count here only. + // + Count = 0; + for (i=0;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + // + // don't want unresolved names, or the broadcast name + // + if (!(pNameAddr->NameTypeState & STATE_RESOLVING)) + // && (pNameAddr->Name[0] != '*')) count these!! + { + Count++; + } + } + } + + // get the list of addresses for this device - local hash table + pHead = &NbtConfig.AddressHead; + pEntry = pHead->Flink; + NameSize = sizeof(NAME_BUFFER); + } + else + { + // get the list of addresses for this device - remote hash table + Count = 0; + pHashTable = NbtConfig.pRemoteHashTbl; + for (i=0;i < pHashTable->lNumBuckets ;i++ ) + { + pHead = &pHashTable->Bucket[i]; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pEntry = pEntry->Flink; + Count++; + } + } + pHead = &pHashTable->Bucket[0]; + pEntry = pHead->Flink; + NameSize = sizeof(tREMOTE_CACHE); + + } + + // Allocate Memory for the adapter status + BuffSize = sizeof(ADAPTER_STATUS) + Count*NameSize; + + // + // Is our status buffer size greater then the user's buffer? + // + if ( BuffSize > *pSize ) + { + fOverFlow = TRUE; + + // + // Recalc how many names will fit + // + if ( *pSize <= sizeof(ADAPTER_STATUS) ) + Count = 0 ; + else + Count = ( *pSize - sizeof(ADAPTER_STATUS)) / NameSize ; + + BuffSize = sizeof(ADAPTER_STATUS) + Count*NameSize; + } + + pAdapterStatus = NbtAllocMem((USHORT)BuffSize,NBT_TAG('O')); + 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 + // + if (pDeviceContext) + { + pMacAddr = &pDeviceContext->MacAddress.Address[0]; + } + else + { + tDEVICECONTEXT *pDevContext; + + // use the first adapter on the list to get the Ip address + pDevContext = CONTAINING_RECORD(NbtConfig.DeviceContexts.Flink,tDEVICECONTEXT,Linkage); + pMacAddr = &pDevContext->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)pAdapterStatus + sizeof(ADAPTER_STATUS)); + + i = 0; + j = 0; + while (Count) + { + if (pDeviceContext) + { + // ***** LOCAL HASH TABLE QUERY ***** + + // get out of while if we reach the end of the list + if (pEntry == pHead) + { + break; + } + + pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + pNameAddr = pAddressEle->pNameAddr; + + pEntry = pEntry->Flink; + // + // 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)) + { + Count--; + continue; + } + + } + + else + { + BOOLEAN done=FALSE; + ULONG Ttl; + BOOLEAN NewChain=FALSE; + + // ***** REMOTE HASH TABLE QUERY ***** + + // for the remote table, skip over scope records. + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + while (TRUE) + { + if (j == pHashTable->lNumBuckets) + { + // no more hash buckets so get out + done = TRUE; + break; + } + + if ((pEntry != pHead)) + { + // don't go to the next entry of a new chain since we + // set pEntry in the else below on the previous loop + // through the while + if (!NewChain) + { + pEntry = pEntry->Flink; + } + break; + } + else + { + // reached the end of this hash chain so go to the next + // chain. + pHead = &pHashTable->Bucket[++j]; + pEntry = pHead->Flink; + NewChain = TRUE; + } + } + + // get out of the while loop if at end of hash table + if (done) + break; + if (NewChain) + { + // this will set pNameAddr, by looping around again + continue; + } + + + // don't return scope records or resolving records + // + if ((pNameAddr->NameTypeState & NAMETYPE_SCOPE) || + (!(pNameAddr->NameTypeState & STATE_RESOLVED))) + { + // decrement the number of names returned. + pAdapterStatus->name_count--; + Count--; + continue; + } + // + // the remote cache query has a different structure that includes + // the ip address. Return the ip address to the caller. + // + if (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) + { + // if is is an internet group name, return just the first + // ip address in the group. + ((tREMOTE_CACHE *)pNameBuffer)->IpAddress = pNameAddr->pIpList->IpAddr[0]; + } + else + ((tREMOTE_CACHE *)pNameBuffer)->IpAddress = pNameAddr->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 = i+1; + + CTEMemCopy(pNameBuffer->name,pNameAddr->Name,NETBIOS_NAME_SIZE); + + if (pDeviceContext) + { + pNameBuffer++; + } + else + ((tREMOTE_CACHE *)pNameBuffer)++; + + Count--; + i++; + + } + + pAdapterStatus->name_count = (USHORT)i; + + // + // return the ptr to this wonderful structure of goodies + // + *ppAdapterStatus = (PVOID)pAdapterStatus; + *pSize = BuffSize; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return fOverFlow ? STATUS_BUFFER_OVERFLOW : 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; + BOOL fOverFlow = FALSE ; + ULONG NameSize; + tCONNECTIONS *pCons; + tCONNECTION_LIST *pConnList; + tADDRESSELE *pAddressEle; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnEle; + tCLIENTELE *pClient; + + // 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; + + // + // Is our status buffer size greater then the user's buffer? + // + if ( BuffSize > *pSize ) + { + fOverFlow = TRUE; + + // + // Recalc how many names will fit + // + if ( *pSize <= sizeof(tCONNECTION_LIST) ) + Count = 0 ; + else + Count = ( *pSize - sizeof(tCONNECTION_LIST)) / NameSize ; + + BuffSize = sizeof(tCONNECTION_LIST) + Count*NameSize; + } + + pConnList = NbtAllocMem(BuffSize,NBT_TAG('P')); + if (!pConnList) + { + CTESpinFree(&NbtConfig,OldIrq1); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Fill out the adapter status structure with zeros first + CTEZeroMemory((PVOID)pConnList,BuffSize); + + pConnList->ConnectionCount = Count; + // 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); + 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++; + + pEntry2 = pEntry2->Flink; + if (i == Count) + { + break; + } + } + 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 + pListenReq = CONTAINING_RECORD(pEntry2,tLISTENREQUESTS,Linkage); + pConnEle = (tCONNECTELE *)pListenReq->pConnectEle; + + CTEMemCopy(pCons->LocalName, + pConnEle->pClientEle->pAddress->pNameAddr->Name, + NETBIOS_NAME_SIZE); + + pCons->State = LISTENING; + + i++; + pCons++; + pEntry2 = pEntry2->Flink; + if (i == Count) + { + break; + } + } + CTESpinFree(pClient,OldIrq3); + if (i == Count) + { + break; + } + } + + CTESpinFree(pAddressEle,OldIrq2); + if (i == Count) + { + break; + } + } + + // + // return the ptr to this wonderful structure of goodies + // + *ppConnList = (PVOID)pConnList; + *pSize = BuffSize; + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return fOverFlow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ; + +} +//---------------------------------------------------------------------------- +NTSTATUS +NbtResyncRemoteCache( + ) +/*++ + +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 + +--*/ +{ + tTIMERQENTRY TimerEntry; + LONG i; + LONG lRetcode; + + 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 + // + for (i=0;i < NbtConfig.RemoteTimeoutCount+1;i++ ) + { + RemoteHashTimeout(NULL,NULL,&TimerEntry); + } + + // now remove any preloaded entries + RemovePreloads(); + + // now reload the preloads + lRetcode = PrimeCache(NbtConfig.pLmHosts, + NULL, + TRUE, + NULL); +#ifdef VXD + // + // check if things didn't go well (InDos was set etc.) + // + if (lRetcode == -1) + { + return STATUS_UNSUCCESSFUL; + } +#endif + + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +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_INSUFFICIENT_RESOURCES); + } + + 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; + + +} + +//---------------------------------------------------------------------------- +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. + +Arguments: + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + LIST_ENTRY LowerConnFreeHead; + CTEULONGLONG AdapterNumber; + ULONG DeviceIndex; + BOOLEAN Attached; + ULONG Count; + BOOLEAN times = FALSE; + + CTEPagedCode(); + + // grab the resource that synchronizes opening addresses and connections. + // to prevent the client from doing anything for a while + // + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + if (IpAddress == 0) + { + + if (pDeviceContext->IpAddress) + { + // + // The permanent name is a function of the MAC address so remove + // it since the Adapter is losing its Ip address + // + NbtRemovePermanentName(pDeviceContext); + // + // 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,&LowerConnFreeHead); + + NbtConfig.DhcpNumConnections = (USHORT)CloseLowerConnections(&LowerConnFreeHead); + + CHECK_PTR(pDeviceContext); + pDeviceContext->IpAddress = 0; + } + status = STATUS_SUCCESS; + } + else + { + CloseAddressesWithTransport(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 + // shift the adapter number once to get an index since the first + // adapter has an adapter number of 000001 + // + AdapterNumber = pDeviceContext->AdapterNumber >> 1; + + CTEAttachFsp(&Attached); + + DeviceIndex = 0; + // + // shift the adapter number down till zero to get the index for the + // current adapter + // + while ( AdapterNumber ) + { + DeviceIndex++; + AdapterNumber = AdapterNumber >> 1; + } + + Count = CountUpperConnections(pDeviceContext); + Count += NBT_NUM_INITIAL_CONNECTIONS; + +retry: + status = NbtCreateAddressObjects( + IpAddress, + SubnetMask, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + CTEDetachFsp(Attached); + NbtLogEvent(EVENT_NBT_CREATE_ADDRESS,status); + + KdPrint(("Failed to create the Address Objects after a new DHCP address, status=%X\n",status)); + KdPrint(("IpAddress: %lx, SubnetMask: %lx, pDeviceContext: %lx\n", IpAddress, SubnetMask, pDeviceContext)); + + ASSERT(FALSE); + + if (!times) { + KdPrint(("Retrying...\n")); + times = TRUE; + CTEAttachFsp(&Attached); + goto retry; + } + } + else + { + + status = NbtInitConnQ(&pDeviceContext->LowerConnFreeHead, + 0, // not used + Count, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_CREATE_CONNECTION,status); + KdPrint(("Failed to create the Connections after a new DHCP address, status=%X\n",status)); + } + } + + CTEDetachFsp(Attached); + + } + + CTEExReleaseResource(&NbtConfig.Resource); + + return(status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +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 OldIrq2; + tADDRESSELE *pAddress; + PIRP pIrp; + NTSTATUS status; + tNAMEADDR *pNameAddr; + CTEULONGLONG AdapterNumber; + + // 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 + // + ASSERT(pClientEle->RefCount); + CTESpinLock(&NbtConfig.JointLock,OldIrq2); + if (--pClientEle->RefCount) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + // return pending because we haven't been able to close the client + // completely yet + // + return(STATUS_PENDING); + } + + // + // 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. + // + RemoveEntryList(&pClientEle->Linkage); + + pAddress = pClientEle->pAddress; + + pIrp = pClientEle->pIrp; + + // + // The browser may want to release a name on a single netcard so this + // check removes one of the adapter mask bits from pNameAddr if there + // are currently more than one there. If there is only one then the name + // will get released when all clients drop the name. In addition, only + // allow this if only one client has registered the name on each card. + // + // 5/23/95: this logic applies not just to the browser name, but any name + // + pNameAddr = pAddress->pNameAddr; + AdapterNumber = pClientEle->pDeviceContext->AdapterNumber; + + if (((pNameAddr->AdapterMask & ~AdapterNumber) != 0 ) && + (pAddress->MultiClients == FALSE) ) + // (pNameAddr->Name[NETBIOS_NAME_SIZE-1] == 0x1d) ) + { + pNameAddr->AdapterMask &= ~pClientEle->pDeviceContext->AdapterNumber; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq2); + + // + // 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)); + + // the Datagram Q should be empty otherwise we shouldn't be able to get + // to this routine. + ASSERT(IsListEmpty(&pClientEle->SndDgrams)); + + + // + // check if there are more clients attached to the address, or can we + // delete the address too. + // + // + // It is possible that the address is null if the OpenAddress fails to + // send for some reason. In this case just skip the dereferenceAddress. + // + status = STATUS_SUCCESS; + if (pAddress) + { + status = NbtDereferenceAddress(pAddress); + } + + // 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 + // + + // + // free the memory associated with the client element + // + CTEMemFree((PVOID)pClientEle); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBt: 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. + // + if (pIrp) + { + + // complete the client's close address irp + CTEIoComplete(pIrp,STATUS_SUCCESS,0); + + } + // + // this status insures that driver.c does not complete the irp too since + // it is completed above. + // + return(STATUS_PENDING); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDereferenceAddress +( + IN tADDRESSELE *pAddress + ) +/*++ + +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; + PVOID pTimerContext; + USHORT uAddrType; + ULONG SaveState; + + // 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). + // + ASSERT(pAddress->RefCount); + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + CTESpinLock(pAddress,OldIrq); + if (--pAddress->RefCount) + { + CTESpinFree(pAddress,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_SUCCESS); + } + + CTESpinFree(pAddress,OldIrq); + + + + // The ClientHead should be empty otherwise we shouldn't get to this routine + // + ASSERT(IsListEmpty(&pAddress->ClientHead)); + ASSERT(pAddress->pNameAddr->Verify == LOCAL_NAME); + +#if !defined(VXD) && DBG + if (pAddress->pNameAddr->Verify != LOCAL_NAME) + { + DbgBreakPoint(); + } +#endif + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NbtDereferenceAddress - Freeing address object for %15.15s<%X>\n", + pAddress->pNameAddr->Name,pAddress->pNameAddr->Name[NETBIOS_NAME_SIZE-1] )); + + // + // change the name state in the hash table until it is released + // + SaveState = pAddress->pNameAddr->NameTypeState; + pAddress->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pAddress->pNameAddr->NameTypeState |= STATE_CONFLICT; + + // + // check for any timers outstanding against the hash table entry - there shouldn't + // be any timers though + // + pClientCompletion = NULL; + ASSERT(!pAddress->pNameAddr->pTimer); + if (pAddress->pNameAddr->pTimer) + { + status = StopTimer(pAddress->pNameAddr->pTimer, + pClientCompletion, + &pTimerContext); + + } + + // + // Release name on the network + // + if ((pAddress->pNameAddr->NameTypeState & NAME_TYPE_MASK) != NAMETYPE_UNIQUE) + { + uAddrType = NBT_GROUP; + } + else + uAddrType = NBT_UNIQUE; + + CTESpinFree(&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] != '*')) + { + status = ReleaseNameOnNet(pAddress->pNameAddr, + NbtConfig.pScope, + pAddress, + NameReleaseDone, + NodeType, + NULL); + // so the caller waits for the release to complete. + // + if (NT_SUCCESS(status)) + { + return(STATUS_PENDING); + } + } + + // + // set this to zero to prevent sending a name release on another adapter + // since we just want to complete the free the nameaddr here. + // + CHECK_PTR(pAddress->pNameAddr); + pAddress->pNameAddr->AdapterMask = 0; + + NameReleaseDone((PVOID)pAddress,STATUS_SUCCESS); + + // + // the name has been deleted, so return success + // + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +VOID +LockedDereferenceName( + IN tNAMEADDR *pNameAddr + ) +/*++ + +Routine Description + + This routine grabs the spin lock and dereferences the name. + +Arguments: + + pNameAddr -ptr to name address structure. + +Return Values: + + none. + +--*/ + +{ + CTELockHandle OldIrq; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + NbtDereferenceName(pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); +} + +//---------------------------------------------------------------------------- +VOID +NbtDereferenceName( + IN tNAMEADDR *pNameAddr + ) +/*++ + +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 must be taken + before calling this routine. + + +Arguments: + + +Return Values: + + TDI_STATUS - status of the request + +--*/ + +{ + +// GRAB THE SPIN LOCK BEFORE CALLING THIS ROUTINE!! + + ASSERT(pNameAddr->RefCount); + if (--pNameAddr->RefCount > 0) + { + return; + } + + // + // remove from the hash table + // + RemoveEntryList(&pNameAddr->Linkage); + + if (pNameAddr->Verify == REMOTE_NAME) + { + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Freeing Remote Name Memory, %16.16s<%X> %X\n", + pNameAddr->Name,pNameAddr->Name[15],pNameAddr)); + + ASSERT(pNameAddr->Verify == REMOTE_NAME); + // + // if it is an internet group name it has a list of ip addresses and that + // memory block must be deleted + // + if (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Freeing Internet Group Name Memory\n")); + CTEMemFree((PVOID)pNameAddr->pIpList); + } + + } + else + { + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Freeing Local Name Memory, %16.16s<%X> %X\n", + pNameAddr->Name,pNameAddr->Name[15],pNameAddr)); + ASSERT(pNameAddr->Verify == LOCAL_NAME); + + } + + if ( (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) == 0 ) + { + if (pNameAddr->pIpAddrsList) + { + CTEMemFree((PVOID)pNameAddr->pIpAddrsList); + } + } + + // + // free the memory now + // + CTEMemFree((PVOID)pNameAddr); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDereferenceConnection( + IN tCONNECTELE *pConnEle + ) +/*++ + +Routine Description + + This routine dereferences and possibly deletes a connection element record. + + +Arguments: + + +Return Values: + + TDI_STATUS - status of the request + +--*/ +{ + CTELockHandle OldIrq; + PCTE_IRP pIrp; + + // 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->Verify == NBT_VERIFY_CONNECTION) || + (pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN)) ; + ASSERT( pConnEle->RefCount > 0 ) ; // Check for too many derefs + + CHECK_PTR(pConnEle); + if (--pConnEle->RefCount > 0) + { + + CTESpinFree(pConnEle,OldIrq); + return(STATUS_PENDING); + + } +#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 (pConnEle->pIrpClose) + { + NTClearFileObjectContext(pConnEle->pIrpClose); + } +#endif + + // the close irp should be held in here + pIrp = pConnEle->pIrpClose; + + CTESpinFree(pConnEle,OldIrq); + + // The connection was unlinked from the ConnectHead or ConnectActive + // in the Cleanup routine, so no need to unlink again here + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBt: Delete Connection Object %X\n",pConnEle)); + + ASSERT((pConnEle->state <= NBT_CONNECTING) || + (pConnEle->state > NBT_DISCONNECTING)); + +#ifdef VXD + DbgPrint("NbtDereferenceConnection: Deleting Connecte element - 0x") ; + DbgPrintNum( (ULONG) pConnEle ) ; DbgPrint("\r\n") ; +#endif + + // free the memory block associated with the conn element + FreeConnectionObj(pConnEle); + + // 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 + // return the irp. + if (pIrp) + { + CTEIoComplete(pIrp,STATUS_SUCCESS,0); + } + return(STATUS_PENDING); +} + +//---------------------------------------------------------------------------- +VOID +NbtDereferenceLowerConnection( + IN tLOWERCONNECTION *pLowerConn + ) +/*++ +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; + tDEVICECONTEXT *pDeviceContext; + NTSTATUS status; + + pDeviceContext = pLowerConn->pDeviceContext; + + CTESpinLock(pLowerConn,OldIrq1); + + ASSERT(pLowerConn->RefCount); + + if(--pLowerConn->RefCount == 0) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBt: Delete Lower Connection Object %X\n",pLowerConn)); + + // + // it's possible that transport may indicate before we run the code + // in WipeOutLowerconn. 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; + + CTESpinFree(pLowerConn,OldIrq1); + + // + // let's come back and do this later since we may be at dpc now + // + CTEQueueForNonDispProcessing( + NULL, + pLowerConn, + NULL, + WipeOutLowerconn, + pLowerConn->pDeviceContext); + } + else + CTESpinFree(pLowerConn,OldIrq1); + +} +//---------------------------------------------------------------------------- +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; + + // 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 = (struct _LIST_ENTRY * volatile)0x00009789; + + CTESpinFree(pDeviceContext,OldIrq); + + NbtDereferenceLowerConnection(pLowerConn); + + return(status); + +} + +//---------------------------------------------------------------------------- +VOID +WipeOutLowerconn( + IN PVOID pContext + ) +/*++ +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; + PVOID pIndicate; + + + pLowerConn = (tLOWERCONNECTION*)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + + // dereference the fileobject ptr + NTDereferenceObject((PVOID *)pLowerConn->pFileObject); + + // close the lower connection with the transport +#ifndef VXD + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Closing Handle %X -> %X\n",pLowerConn,pLowerConn->FileHandle)); +#else + KdPrint(("Nbt:Closing Handle %X -> %X\n",pLowerConn,pLowerConn->pFileObject)); +#endif + + 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 + // + pIndicate = MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + CTEMemFree(pIndicate); + IoFreeMdl(pLowerConn->pIndicateMdl); +#endif + + // now free the memory block tracking this connection + CTEMemFree((PVOID)pLowerConn); + + CTEMemFree(pContext); + +} diff --git a/private/ntos/nbt/nbt/namesrv.c b/private/ntos/nbt/nbt/namesrv.c new file mode 100644 index 000000000..1c46a7b6a --- /dev/null +++ b/private/ntos/nbt/nbt/namesrv.c @@ -0,0 +1,3886 @@ + /*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Namesrv.c + +Abstract: + + This file contains the name service functions called by other parts of + the NBT code. (QueryNameOnNet, FindName, RegisterName). It also contains + the completion routines for the timeouts associated with these functions. + + The pScope values that are passed around from one routine to the next + point to the scope string for the name. If there is no scope then the + pScope ptr points at a single character '\0' - signifying a string of + zero length. Therefore the check for scope is "if (*pScope != 0)" + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" + +// +// function prototypes for completion routines that are local to this file +// +NTSTATUS +AddToPendingList( + IN PCHAR pName, + OUT tNAMEADDR **ppNameAddr + ); +VOID +MSnodeCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); +VOID +MSnodeRegCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +SetWinsDownFlag( + tDEVICECONTEXT *pDeviceContext + ); + +VOID +ReleaseCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +NextRefresh( + IN PVOID pNameAdd, + IN NTSTATUS status + ); +VOID +NextRefreshNonDispatch( + IN PVOID pContext + ); +VOID +GetNextName( + IN tNAMEADDR *pNameAddrIn, + OUT tNAMEADDR **ppNameAddr + ); + +NTSTATUS +StartRefresh( + IN tNAMEADDR *pNameAddr, + IN tDGRAM_SEND_TRACKING *pTracker, + IN BOOLEAN ResetDevice + ); + +VOID +RefreshBegin( + PVOID pContext + ); +VOID +NextKeepAlive( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS statuss, + IN ULONG Info + ); +VOID +GetNextKeepAlive( + tDEVICECONTEXT *pDeviceContext, + tDEVICECONTEXT **ppDeviceContextOut, + tLOWERCONNECTION *pLowerConnIn, + tLOWERCONNECTION **ppLowerConnOut + ); +VOID +SessionKeepAliveNonDispatch( + PVOID pContext + ); +VOID +WinsDownTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +BOOL +AppropriateNodeType( + IN PCHAR pName, + IN ULONG NodeType + ); + +BOOL +IsBrowserName( + IN PCHAR pName + ); + +#if DBG +unsigned char Buff[256]; +unsigned char Loc; +#endif + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, SessionKeepAliveNonDispatch) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +tNAMEADDR * +FindName( + enum eNbtLocation Location, + PCHAR pName, + PCHAR pScope, + USHORT *pRetNameType + ) +/*++ + +Routine Description: + + This routine searches the name table to find a name. The table searched + depends on the Location passed in - whether it searches the local table + or the network names table. The routine checks the state of the name + and only returns names in the resolved state. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + tNAMEADDR *pNameAddr; + NTSTATUS status; + tHASHTABLE *pHashTbl; + + if (Location == NBT_LOCAL) + { + pHashTbl = pNbtGlobConfig->pLocalHashTbl; + } + else + { + pHashTbl = pNbtGlobConfig->pRemoteHashTbl; + + } + status = FindInHashTable( + pHashTbl, + pName, + pScope, + &pNameAddr); + + if (!NT_SUCCESS(status)) + { + return(NULL); + } + + *pRetNameType = (USHORT)pNameAddr->NameTypeState; + + + // + // Only return names that are in the resolved state + // + if (!(pNameAddr->NameTypeState & STATE_RESOLVED)) + { + pNameAddr = NULL; + } + + return(pNameAddr); +} + +//---------------------------------------------------------------------------- +NTSTATUS +AddToPendingList( + IN PCHAR pName, + OUT tNAMEADDR **ppNameAddr + ) +/*++ +Routine Description: + + This routine Adds a name query request to the PendingNameQuery list. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + + +--*/ +{ + tNAMEADDR *pNameAddr; + + pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('R')); + if (pNameAddr) + { + CTEZeroMemory(pNameAddr,sizeof(tNAMEADDR)); + + CTEMemCopy(pNameAddr->Name,pName,NETBIOS_NAME_SIZE); + pNameAddr->NameTypeState = STATE_RESOLVING | NBT_UNIQUE; + pNameAddr->RefCount = 1; + pNameAddr->Verify = REMOTE_NAME; + pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + + InsertTailList(&NbtConfig.PendingNameQueries, + &pNameAddr->Linkage); + + *ppNameAddr = pNameAddr; + return(STATUS_SUCCESS); + } + else + return(STATUS_INSUFFICIENT_RESOURCES); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +QueryNameOnNet( + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN USHORT uType, + IN PVOID pClientContext, + IN PVOID pClientCompletion, + IN ULONG LocalNodeType, + IN tNAMEADDR *pNameAddrIn, + IN tDEVICECONTEXT *pDeviceContext, + OUT tDGRAM_SEND_TRACKING **ppTracker, + IN CTELockHandle *pJointLockOldIrq + ) +/*++ + +Routine Description: + + This routine attempts to resolve a name on the network either by a + broadcast or by talking to the NS depending on the type of node. (M,P or B) + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +Called By: ProxyQueryFromNet() in proxy.c, NbtConnect() in name.c + +--*/ + +{ + ULONG Timeout; + USHORT Retries; + NTSTATUS status; + PVOID pCompletionRoutine; + tDGRAM_SEND_TRACKING *pSentList; + tNAMEADDR *pNameAddr; + LPVOID pContext2 = NULL; + CHAR cNameType = pName[NETBIOS_NAME_SIZE-1]; + BOOL SendFlag = TRUE; + LONG IpAddr = 0; + + status = GetTracker(&pSentList); + if (!NT_SUCCESS(status)) + { + return(status); + } + if (ppTracker) + { + *ppTracker = pSentList; + } + + // set to NULL to catch any erroneous frees. + CHECK_PTR(pSentList); + pSentList->SendBuffer.pDgramHdr = NULL; + pSentList->pDeviceContext = pDeviceContext; + + // + // put the name in the remote cache to keep track of it while it resolves... + // + pNameAddr = NULL; + if (!pNameAddrIn) + { + status = AddToPendingList(pName,&pNameAddr); + + if (!NT_SUCCESS(status)) + { + FreeTracker(pSentList,RELINK_TRACKER); + return(status); + } + + // fill in the record with the name and IpAddress + pNameAddr->NameTypeState = (uType == NBT_UNIQUE) ? + NAMETYPE_UNIQUE : NAMETYPE_GROUP; + } + else + { + status = STATUS_SUCCESS; + pNameAddr = pNameAddrIn; + pNameAddr->RefCount = 1; + } + + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_RESOLVING; + pNameAddr->Ttl = NbtConfig.RemoteHashTimeout; + +#ifdef PROXY_NODE + // + // If the node type is PROXY, it means that the request is being sent + // as a result of hearing a name registration or a name query on the net. + // + // If the node type is not == PROXY (i.e. it is MSNODE | PROXY, + // PNODE | PROXY, MSNODE, PNODE, etc, then the request is being sent as + // a result of a client request + // + // Refer: RegOrQueryFromNet in Proxy.c + // + // This field is used in QueryFromNet() to determine whether or not + // to revert to Broadcast + // +#endif + if(LocalNodeType & PROXY) + { + pNameAddr->fProxyReq = (BOOLEAN)TRUE; + } + else + { + pNameAddr->fProxyReq = (BOOLEAN)FALSE; + LocalNodeType = AppropriateNodeType( pName, LocalNodeType ); + } + + // keep a ptr to the Ascii name so that we can remove the name from the + // hash table later if the query fails. + pSentList->pNameAddr = pNameAddr; + + // + // Set a few values as a precursor to registering the name either by + // broadcast or with the name server + // +#ifdef PROXY_NODE + IF_PROXY(LocalNodeType) + { + Retries = (USHORT)pNbtGlobConfig->uNumRetries; + Timeout = (ULONG)pNbtGlobConfig->uRetryTimeout; + pCompletionRoutine = ProxyTimerComplFn; + pSentList->Flags = NBT_NAME_SERVER; + pContext2 = pClientContext; + pClientContext = NULL; + + } + else +#endif + { + + Retries = pNbtGlobConfig->uNumRetries; + Timeout = (ULONG)pNbtGlobConfig->uRetryTimeout; + pCompletionRoutine = MSnodeCompletion; + pSentList->Flags = NBT_NAME_SERVER; + + // use broadcast if no name server address for MSNODE or Wins down, + // or it is Bnode,Mnode. + // for Pnode, just allow it to do the name query on the loop back + // address + // + if ((LocalNodeType & (MNODE | BNODE)) || + ((LocalNodeType & MSNODE) && + ((pDeviceContext->lNameServerAddress == LOOP_BACK) || + pDeviceContext->WinsIsDown))) + { + Retries = pNbtGlobConfig->uNumBcasts; + Timeout = (ULONG)pNbtGlobConfig->uBcastTimeout; + pSentList->Flags = NBT_BROADCAST; + } + else + if ((pDeviceContext->lNameServerAddress == LOOP_BACK) || + pDeviceContext->WinsIsDown) + { + // + // short out timeout when no wins server configured -for PNODE + // + Retries = 1; + Timeout = 10; + pSentList->Flags = NBT_NAME_SERVER_BACKUP; + } + + // + // no sense doing a name query out an adapter with no Ip address + // + if ( + (pDeviceContext->IpAddress == LOOP_BACK) + || ( IpAddr = Nbt_inet_addr(pName) ) + ) + { + + Retries = 1; + Timeout = 10; + pSentList->Flags = NBT_BROADCAST; + SendFlag = FALSE; + if (LocalNodeType & (PNODE | MNODE)) + { + pSentList->Flags = NBT_NAME_SERVER_BACKUP; + } + + } + } + + // + // set the ref count high enough so that a pdu from the wire cannot + // free the tracker while UdpSendNsBcast is running - i.e. between starting + // the timer and actually sending the datagram. + // + pSentList->RefCount = 2; + + // + // put a pointer to the tracker here so that other clients attempting to + // query the same name at the same time can tack their trackers onto + // the end of this one. - i.e. This is the tracker for the + // datagram send, or connect, not the name query. + // + pNameAddr->pTracker = pClientContext; + + // do a name query... will always return status pending... + // the pNameAddr structure cannot get deleted out from under us since + // only a timeout on the send (3 retries) will remove the name. Any + // response from the net will tend to keep the name (change state to Resolved) + // + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + CTESpinFree(&NbtConfig.JointLock,*pJointLockOldIrq); + + // + // Bug: 22542 - prevent broadcast of remote adapter status on net view of limited subnet b'cast address. + // In order to test for subnet broadcasts, we need to match against the subnet masks of all adapters. This + // is expensive and not done. + // Just check for the limited bcast. + // + if (IpAddr == 0xffffffff) { + KdPrint(("Nbt: Query on Limited broadcast - failed\n")); + status = STATUS_BAD_NETWORK_PATH; + } else { + status = UdpSendNSBcast(pNameAddr, + pScope, + pSentList, + pCompletionRoutine, + pClientContext, + pClientCompletion, + Retries, + Timeout, + eNAME_QUERY, + SendFlag); + } + + // a successful send means, Don't complete the Irp. Status Pending is + // returned to ntisol.c to tell that code not to complete the irp. The + // irp will be completed when this send either times out or a response + // is heard. In the event of an error in the send, allow that return + // code to propagate back and result in completing the irp - i.e. if + // there isn't enough memory to allocate a buffer or some such thing + // + DereferenceTracker(pSentList); + CTESpinLock(&NbtConfig.JointLock,*pJointLockOldIrq); + + if (NT_SUCCESS(status)) + { + LOCATION(0x49); + + // this return must be here to avoid freeing the tracker below. + status = STATUS_PENDING; + } + else + { + tTIMERQENTRY *pTimer; + COMPLETIONCLIENT pCompletion=NULL; + + LOCATION(0x50); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Query failed - bad retcode from UdpSendNsBcast = %X\n", + status)); + + pTimer = pNameAddr->pTimer; + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + + // save this value for below. + pCompletion = pTimer->ClientCompletion; + + // dereferencing the tracker no longer frees the dgram hdr too. + // if IP addr is limited bcast, then UdpSendNSBcast was not called. + if ((status != STATUS_INSUFFICIENT_RESOURCES) && + (IpAddr != 0xffffffff)) + { + CTEMemFree(pSentList->SendBuffer.pDgramHdr); + } + + // + // UdpSendNsBcast cannot fail AND start the timer, therefore there + // is no need to worry about stopping the timer here. + // + { + // the timer was never started... + if (ppTracker) + { + *ppTracker = NULL; + } + + // + // This will free the tracker + // + DereferenceTrackerNoLock(pSentList); + NbtDereferenceName(pNameAddr); + } + } + + return(status); +} +//---------------------------------------------------------------------------- +VOID +MSnodeCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. It must + decide if another name query should be done, and if not, then it calls the + client's completion routine (in completion2). + This routine handles the broadcast portion of the name queries (i.e. + those name queries that go out as broadcasts). + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + COMPLETIONCLIENT pClientCompletion; + USHORT Flags; + tDGRAM_SEND_TRACKING *pClientTracker; + ULONG LocalNodeType; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + LocalNodeType = AppropriateNodeType( pTracker->pNameAddr->Name, NodeType ); + + // + // check if the client completion routine is still set. If not then the + // timer has been cancelled and this routine should just clean up its + // buffers associated with the tracker. + // + if (pTimerQEntry) + { + + // + // to prevent a client from stopping the timer and deleting the + // pNameAddr, grab the lock and check if the timer has been stopped + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + ASSERT(pTracker->pNameAddr->Verify == REMOTE_NAME); +#if !defined(VXD) && DBG + if (pTracker->pNameAddr->Verify != REMOTE_NAME) + { + DbgBreakPoint(); + } +#endif + if (pTimerQEntry->Flags & TIMER_RETIMED) + { + pTimerQEntry->Flags &= ~TIMER_RETIMED; + pTimerQEntry->Flags |= TIMER_RESTART; + // + // if we are not bound to this card than use a very short timeout + // + if (!pTracker->pDeviceContext->pNameServerFileObject) + { + pTimerQEntry->DeltaTime = 10; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + + + pClientTracker = (tDGRAM_SEND_TRACKING *)pTimerQEntry->ClientContext; + + // + // if the tracker has been cancelled, don't do any more queries + // + if (pClientTracker->Flags & TRACKER_CANCELLED) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: MSnodeCompletion: tracker flag cancelled\n")); + + // + // In case the timer has been stopped, we coordinate + // through the pClientCompletionRoutine Value with StopTimer. + // + pClientCompletion = pTimerQEntry->ClientCompletion; + + // + // remove from the PendingNameQueries list + // + RemoveEntryList(&pTracker->pNameAddr->Linkage); + InitializeListHead(&pTracker->pNameAddr->Linkage); + + // remove the link from the name table to this timer block + CHECK_PTR(((tNAMEADDR *)pTimerQEntry->pCacheEntry)); + ((tNAMEADDR *)pTimerQEntry->pCacheEntry)->pTimer = NULL; + // + // to synch. with the StopTimer routine, Null the client completion + // routine so it gets called just once. + // + CHECK_PTR(pTimerQEntry); + pTimerQEntry->ClientCompletion = NULL; + + // + // remove the name from the hash table, since it did not + // resolve + // + CHECK_PTR(pTracker->pNameAddr); + pTracker->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pTracker->pNameAddr->NameTypeState |= STATE_RELEASED; + pTracker->pNameAddr->pTimer = NULL; + + NbtDereferenceName(pTracker->pNameAddr); + pTracker->pNameAddr = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // there can be a list of trackers Q'd up on this name + // query, so we must complete all of them! + // + CompleteClientReq(pClientCompletion, + pClientTracker, + STATUS_CANCELLED); + + // return the tracker block to its queue + LOCATION(0x51); + DereferenceTracker(pTracker); + + return; + + } + + if (pTimerQEntry->ClientCompletion) + { + // if number of retries is not zero then continue trying to contact the + // Name Server. + // + if (!(--pTimerQEntry->Retries)) + { + + // set the retry count again + // + pTimerQEntry->Retries = NbtConfig.uNumRetries; + Flags = pTracker->Flags; + pTracker->Flags &= ~(NBT_NAME_SERVER_BACKUP + | NBT_NAME_SERVER + | NBT_BROADCAST); + + if ((Flags & NBT_BROADCAST) && (LocalNodeType & MNODE) && + (pTracker->pDeviceContext->lNameServerAddress != LOOP_BACK) && + !pTracker->pDeviceContext->WinsIsDown) + { + LOCATION(0x44); + // *** MNODE ONLY *** + // + // Can't Resolve through broadcast, so try the name server + // + pTracker->Flags |= NBT_NAME_SERVER; + + // set a different timeout for name resolution through WINS + // + pTimerQEntry->DeltaTime = NbtConfig.uRetryTimeout; + + } + else + if ((Flags & NBT_NAME_SERVER) && !(LocalNodeType & BNODE)) + { + LOCATION(0x47); + // *** NOT BNODE *** + // + // Can't reach the name server, so try the backup + // + pTracker->Flags |= NBT_NAME_SERVER_BACKUP; + // + // short out the timeout if no backup name server + // + if ((pTracker->pDeviceContext->lBackupServer == LOOP_BACK) || + pTracker->pDeviceContext->WinsIsDown) + { + pTimerQEntry->Retries = 1; + pTimerQEntry->DeltaTime = 10; + + } + + } + else + if ((Flags & NBT_NAME_SERVER_BACKUP) + && (LocalNodeType & MSNODE)) + { + LOCATION(0x46); + // *** MSNODE ONLY *** + // + // Can't reach the name server(s), so try broadcast name queries + // + pTracker->Flags |= NBT_BROADCAST; + + // set a different timeout for broadcast name resolution + // + pTimerQEntry->DeltaTime = NbtConfig.uBcastTimeout; + pTimerQEntry->Retries = NbtConfig.uNumBcasts; + + // + // Set the WinsIsDown Flag and start a timer so we don't + // try wins again for 15 seconds or so...only if we failed + // to reach WINS, rather than WINS returning a neg response. + // + if (!(Flags & WINS_NEG_RESPONSE)) + { + SetWinsDownFlag(pTracker->pDeviceContext); + } + } + else + { + BOOLEAN bFound = FALSE; + LOCATION(0x45); + + // + // see if the name is in the lmhosts file, if it ISN'T the + // proxy making the name query request!! + // + status = STATUS_UNSUCCESSFUL; + + // + // In case the timer has been stopped, we coordinate + // through the pClientCompletionRoutine Value with StopTimer. + // + pClientCompletion = pTimerQEntry->ClientCompletion; + // + // the timeout has expired on the broadcast name resolution + // so call the client + // + + // + // remove from the PendingNameQueries list + // + RemoveEntryList(&pTracker->pNameAddr->Linkage); + InitializeListHead(&pTracker->pNameAddr->Linkage); + + // remove the link from the name table to this timer block + CHECK_PTR(((tNAMEADDR *)pTimerQEntry->pCacheEntry)); + ((tNAMEADDR *)pTimerQEntry->pCacheEntry)->pTimer = NULL; + // + // to synch. with the StopTimer routine, Null the client completion + // routine so it gets called just once. + // + CHECK_PTR(pTimerQEntry); + pTimerQEntry->ClientCompletion = NULL; + + + if ((NbtConfig.EnableLmHosts || NbtConfig.ResolveWithDns) && + (!pTracker->pNameAddr->fProxyReq )) + { + // only do this if the client completion routine has not + // been run yet. + // + if (pClientCompletion) + { + status = LmHostQueueRequest(pTracker, + pTimerQEntry->ClientContext, + pClientCompletion, + ScanLmHostFile, + pTracker->pDeviceContext, + OldIrq); + } + } + + + CHECK_PTR(pTimerQEntry); + CHECK_PTR(pTimerQEntry->pCacheEntry); + if (NT_SUCCESS(status)) + { + // if it is successfully queued to the Worker thread, + // then Null the ClientCompletion routine in the timerQ + // structure, letting + // the worker thread handle the rest of the name query + // resolution. Also null the timer ptr in the + // nameAddr entry in the name table. + // + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + else + { + + pClientTracker = (tDGRAM_SEND_TRACKING *)pTimerQEntry->ClientContext; + + // + // remove the name from the hash table, since it did not + // resolve + // + CHECK_PTR(pTracker->pNameAddr); + pTracker->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pTracker->pNameAddr->NameTypeState |= STATE_RELEASED; + pTracker->pNameAddr->pTimer = NULL; + + NbtDereferenceName(pTracker->pNameAddr); + pTracker->pNameAddr = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // there can be a list of trackers Q'd up on this name + // query, so we must complete all of them! + // + CompleteClientReq(pClientCompletion, + pClientTracker, + STATUS_TIMEOUT); + + // return the tracker block to its queue + LOCATION(0x51); + DereferenceTracker(pTracker); + } + + return; + } + + + + } + LOCATION(0x48); + pTracker->RefCount++; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + NULL,NULL,NULL, + 0,0, + eNAME_QUERY, + TRUE); + + DereferenceTracker(pTracker); + pTimerQEntry->Flags |= TIMER_RESTART; + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + else + { + // return the tracker block to its queue + LOCATION(0x52); + DereferenceTrackerNoLock((tDGRAM_SEND_TRACKING *)pContext); + } +} + +//---------------------------------------------------------------------------- +VOID +SetWinsDownFlag( + tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine sets the WinsIsDown flag if its not already set and + its not a Bnode. It starts a 15 second or so timer that un sets the + flag when it expires. + + This routine must be called while holding the Joint Lock. + +Arguments: + + None + +Return Value: + None + +--*/ +{ + NTSTATUS status; + tTIMERQENTRY *pTimer; + + if ((!pDeviceContext->WinsIsDown) && !(NodeType & BNODE)) + { + status = StartTimer(NbtConfig.WinsDownTimeout, + pDeviceContext, // context value + NULL, + WinsDownTimeout, + NULL, + NULL, + 1, // retries + &pTimer + ); + if (NT_SUCCESS(status)) + { + pDeviceContext->WinsIsDown = TRUE; + } + } +} + +//---------------------------------------------------------------------------- +VOID +WinsDownTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. + It just sets the WinsIsDown boolean to False so that we will try WINS + again. In this way we will avoid talking to WINS during this timeout. + + +Arguments: + + +Return Value: + + +--*/ +{ + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pContext; + + pDeviceContext->WinsIsDown = FALSE; + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:WINS DOWN Timed Out - Up again\n")); + +} + + +//---------------------------------------------------------------------------- +VOID +CompleteClientReq( + COMPLETIONCLIENT pClientCompletion, + tDGRAM_SEND_TRACKING *pTracker, + NTSTATUS status + ) +/*++ + +Routine Description: + + This routine is called by completion routines to complete the client + request. It may involve completing several queued up requests. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDGRAM_SEND_TRACKING *pTrack; + tDEVICECONTEXT *pDeviceContext; + CTELockHandle OldIrq; + LIST_ENTRY ListEntry; + + + // + // set up a new list head for any queued name queries. + // since we may need to do a new name query below. + // The Proxy hits this routine with a Null Tracker, so check for that. + // + pEntry = pHead = &ListEntry; + if (pTracker) + { + pDeviceContext = pTracker->pDeviceContext; + if( !IsListEmpty(&pTracker->TrackerList)) + { + ListEntry.Flink = pTracker->TrackerList.Flink; + ListEntry.Flink->Blink = &ListEntry; + ListEntry.Blink = pTracker->TrackerList.Blink; + ListEntry.Blink->Flink = &ListEntry; + + pHead = &ListEntry; + pEntry = pHead->Flink; + } + } + + + (*pClientCompletion)(pTracker,status); + + while (pEntry != pHead) + { + pTrack = CONTAINING_RECORD(pEntry,tDGRAM_SEND_TRACKING,TrackerList); + pEntry = pEntry->Flink; + + // + // if the name query failed and there is another requested queued on + // a different device context, re-attempt the name query + // + if ((pTrack->pDeviceContext != pDeviceContext) && + (status != STATUS_SUCCESS)) + { + // + // setup the correct back link since this guy is now the list + // head. The Flink is ok unless the list is empty now. + // + pTrack->TrackerList.Blink = ListEntry.Blink; + pTrack->TrackerList.Blink->Flink = &pTrack->TrackerList; + + if (pTrack->TrackerList.Flink == &ListEntry) + { + pTrack->TrackerList.Flink = &pTrack->TrackerList; + } + + // do a name query on the next name in the list + // and then wait for it to complete before processing any more + // names on the list. + CTESpinLock(&NbtConfig.JointLock,OldIrq); + status = QueryNameOnNet( + pTrack->pDestName, + NbtConfig.pScope, + 0, //no ip address yet. + NBT_UNIQUE, //use this as the default + (PVOID)pTrack, + pTrack->CompletionRoutine, + NodeType & NODE_MASK, + NULL, + pTrack->pDeviceContext, + NULL, + &OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + break; + } + else + { + // + // get the completion routine for this tracker since it may be + // different than the tracker tied to the timer block. i.e. + // pCompletionClient passed to this routine. + // + pClientCompletion = pTrack->CompletionRoutine; + (*pClientCompletion)(pTrack,status); + + } + + + } +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtRegisterName( + IN enum eNbtLocation Location, + IN ULONG IpAddress, + IN PCHAR pName, + IN PCHAR pScope, + IN PVOID pClientContext, + IN PVOID pClientCompletion, + IN USHORT uAddressType, + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine registers a name from local or from the network depending + on the value of Location. (i.e. local node uses this routine as well + as the proxy code.. although it has only been tested with the local + node registering names so far - and infact the remote code has been + removed... since it is not used. All that remains is to remove + the Location parameter. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + ULONG Timeout; + USHORT Retries; + PVOID pCompletionRoutine; + NTSTATUS status; + tNAMEADDR *pNameAddr; + USHORT uAddrType; + tDGRAM_SEND_TRACKING *pSentList= NULL; + CTELockHandle OldIrq1; + ULONG PrevNameTypeState; + ULONG LocalNodeType; + + LocalNodeType = AppropriateNodeType( pName, NodeType ); + + if ((uAddressType == (USHORT)NBT_UNIQUE ) || + (uAddressType == (USHORT)NBT_QUICK_UNIQUE)) + { + uAddrType = NBT_UNIQUE; + } + else + { + uAddrType = NBT_GROUP; + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + if (IpAddress) + { + status = AddToHashTable(pNbtGlobConfig->pLocalHashTbl, + pName, + pScope, + IpAddress, + uAddrType, + NULL, + &pNameAddr); + + CHECK_PTR(pNameAddr); + if (!NT_SUCCESS(status)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(status); + } + pNameAddr->RefreshMask = 0; + } + else + { + // in this case the name is already in the table, we just need + // to re-register it + // + status = FindInHashTable( + pNbtGlobConfig->pLocalHashTbl, + pName, + pScope, + &pNameAddr); + if (!NT_SUCCESS(status)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(status); + } + PrevNameTypeState = pNameAddr->NameTypeState; + pNameAddr->NameTypeState &= ~NAME_TYPE_MASK; + pNameAddr->NameTypeState |= (uAddrType == NBT_UNIQUE) ? + NAMETYPE_UNIQUE : NAMETYPE_GROUP; + + if (PrevNameTypeState & NAMETYPE_QUICK) + pNameAddr->NameTypeState |= NAMETYPE_QUICK; + } + + if ((uAddressType != (USHORT)NBT_UNIQUE ) && + (uAddressType != (USHORT)NBT_QUICK_UNIQUE)) + { + // this means group name so use Bcast Addr - UdpSendDgram changes this + // value to the Broadcast address of the particular adapter + // when is sees the 0. So when we send to a group name that is + // also registered on this node, it will go out as a broadcast + // to the subnet as well as to this node. + CHECK_PTR(pNameAddr); + pNameAddr->IpAddress = 0; + } + + + if (NT_SUCCESS(status)) + { + // set to two minutes until we hear differently from the Name + // Server + pNameAddr->Ttl = NbtConfig.MinimumTtl; + + // store a ptr to the hash table element in the address element + ((tCLIENTELE *)pClientContext)->pAddress->pNameAddr = pNameAddr; + + // for local names, store a back ptr to the address element + // in the pAddressEle/pScope field of the nameaddress + pNameAddr->pAddressEle = ((tCLIENTELE *)pClientContext)->pAddress; + + // + // start with the refreshed bit not set + // + pNameAddr->RefreshMask &= ~pDeviceContext->AdapterNumber; + + // turn on the adapter's bit in the adapter Mask. + // + pNameAddr->AdapterMask |= pDeviceContext->AdapterNumber; + + // check for the broadcast netbios name... this name does not + // get claimed on the network + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + if (pName[0] == '*') + { + pNameAddr->NameTypeState |= STATE_RESOLVED; + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_SUCCESS); + } + + // for "quick" adds, do not register the name on the net! + // however the name will get registered with the name server and + // refreshed later....if this is an MS or M or P node. + // + if ((pNameAddr->NameTypeState & NAMETYPE_QUICK) || + (uAddressType >= (USHORT)NBT_QUICK_UNIQUE) ) + { + pNameAddr->NameTypeState |= STATE_RESOLVED; + pNameAddr->NameTypeState |= NAMETYPE_QUICK; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_SUCCESS); + } + + // + // if the address is LoopBack, then there is no ip address for this + // adapter and we donot want to attempt a send, since it will just + // timeout, so pretend the registration succeeded. Later DHCP will + // activate the net card and the names will be registered then. + // + + if (IpAddress == LOOP_BACK || pDeviceContext->IpAddress == 0) + { + pNameAddr->NameTypeState |= STATE_RESOLVED; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(STATUS_SUCCESS); + } + + pNameAddr->NameTypeState |= STATE_RESOLVING; + + status = GetTracker(&pSentList); + if (!NT_SUCCESS(status)) + { + NbtDereferenceName(pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return(status); + } + + // there is no list of things sent yet + InitializeListHead(&pSentList->Linkage); + + // keep a ptr to the name so we can update the state of the name + // later when the registration completes + + pSentList->pNameAddr = pNameAddr; + pSentList->pDeviceContext = pDeviceContext; + + // the code must now register the name on the network, depending + // on the type of node + // + Retries = pNbtGlobConfig->uNumBcasts + 1; + Timeout = (ULONG)pNbtGlobConfig->uBcastTimeout; + + pCompletionRoutine = MSnodeRegCompletion; + pSentList->Flags = NBT_BROADCAST; + + // need to prevent the tracker from being freed by a pdu from + // the wire before the UdpSendNsBcast is done + // + pSentList->RefCount = 2; + + if (LocalNodeType & (PNODE | MSNODE)) + { + // talk to the NS only to register the name + // ( the +1 does not actually result in a name reg, it + // is just compatible with the code for M node above since + // it uses the same completion routine). + // + Retries = (USHORT)pNbtGlobConfig->uNumRetries + 1; + Timeout = (ULONG)pNbtGlobConfig->uRetryTimeout; + pSentList->Flags = NBT_NAME_SERVER; + // + // if there is no Primary WINS server short out the timeout + // so it completes faster. For Hnode this means to go broadcast. + // + if ((pDeviceContext->lNameServerAddress == LOOP_BACK) || + pDeviceContext->WinsIsDown) + { + if (LocalNodeType & MSNODE) + { + pSentList->Flags = NBT_BROADCAST; + Retries = (USHORT)pNbtGlobConfig->uNumBcasts + 1; + Timeout = (ULONG)pNbtGlobConfig->uBcastTimeout; + + IncrementNameStats(NAME_REGISTRATION_SUCCESS, + FALSE); // not name server register + } + else // its a Pnode + { + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:WINS DOWN - shorting out registration\n")); + + Retries = 1; + Timeout = 10; + pSentList->Flags = NBT_NAME_SERVER_BACKUP; + } + } + } + + // the name itself has a reference count too. + // make the count 2, so that pNameAddr won't get released until + // after DereferenceTracker is called below, since it writes to + // pNameAddr. Note that we must increment here rather than set = 2 + // since it could be a multihomed machine doing the register at + // the same time we are sending a datagram to that name. + // + pNameAddr->RefCount++; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // start the timer in this routine. + status = UdpSendNSBcast( + pNameAddr, + pScope, + pSentList, + pCompletionRoutine, + pClientContext, + pClientCompletion, + Retries, + Timeout, + eNAME_REGISTRATION, + TRUE); + + // this decrements the reference count and possibly frees the + // tracker + // + DereferenceTracker(pSentList); + + if (NT_SUCCESS(status)) + { + LockedDereferenceName(pNameAddr); + return(STATUS_PENDING); + + } + else + { + tTIMERQENTRY *pTimer; + + LOCATION(0x53); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Registration failed - bad retcode from UdpSendNsBcast = %X\n", + status)); + + // Change the state of the name, so it does not get used + // incorrectly. The completion routine will remove the + // name from the name table (NbtRegisterCompletion) by + // dereferencing the name. + // + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_CONFLICT; + + // free the datagram header since freeing the tracker does not + // free the header. + if (status != STATUS_INSUFFICIENT_RESOURCES) + { + CTEMemFree(pSentList->SendBuffer.pDgramHdr); + } + // + // this calls MSNodeRegCompletion which frees the Tracker + // back to its queue + // + pTimer = pNameAddr->pTimer; + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + + NbtDereferenceName(pNameAddr); + + DereferenceTrackerNoLock(pSentList); + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + status = STATUS_UNSUCCESSFUL; + } + + return(status); + +} + +//---------------------------------------------------------------------------- +VOID +MSnodeRegCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. It must + decide if another name registration should be done, and if not, then it calls the + client's completion routine (in completion2). + It first attempts to register a name via Broadcast, then it attempts + NameServer name registration. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + USHORT Flags; + CTELockHandle OldIrq; + enum eNSTYPE PduType; + ULONG LocalNodeType; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + PduType = eNAME_REGISTRATION; + + LocalNodeType = AppropriateNodeType( pTracker->pNameAddr->Name, NodeType ); + + // + // check if the client completion routine is still set. If not then the + // timer has been cancelled and this routine should just clean up its + // buffers associated with the tracker. + // + if (pTimerQEntry) + { + // + // to prevent a client from stopping the timer and deleting the + // pNameAddr, grab the lock and check if the timer has been stopped + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pTimerQEntry->Flags & TIMER_RETIMED) + { + pTimerQEntry->Flags &= ~TIMER_RETIMED; + pTimerQEntry->Flags |= TIMER_RESTART; + + if ((!pTracker->pDeviceContext->pNameServerFileObject) || + (pTracker->Flags & NBT_NAME_SERVER) && + (pTracker->pDeviceContext->lNameServerAddress == LOOP_BACK)) + { + // when the address is loop back there is no wins server + // so shorten the timeout. + // + pTimerQEntry->DeltaTime = 10; + } + else + if ((pTracker->Flags & NBT_NAME_SERVER_BACKUP) && + (pTracker->pDeviceContext->lBackupServer == LOOP_BACK)) + { + // when the address is loop back there is no wins server + // so shorten the timeout. + // + pTimerQEntry->DeltaTime = 10; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + if (pTimerQEntry->ClientCompletion) + { + // if number of retries is not zero then continue trying to contact the + // Name Server + // + if (--pTimerQEntry->Retries) + { + + // change the name reg pdu to a name overwrite request for the + // final broadcast ( turn off Recursion Desired bit) + // + if (pTimerQEntry->Retries == 1) + { + if (pTracker->Flags & NBT_BROADCAST) + { + // do a broadcast name registration... on the last broadcast convert it to + // a Name OverWrite Request by clearing the "Recursion Desired" bit + // in the header + // + PduType = eNAME_REGISTRATION_OVERWRITE; + } + else + if (LocalNodeType & (PNODE | MSNODE)) + { + // we want the Pnode to timeout again, right away and fall + // through to handle Timed out name registration - i.e. it + // does not do the name overwrite demand like the B,M,&MS nodes + // + pTimerQEntry->Flags |= TIMER_RESTART; + pTimerQEntry->DeltaTime = 5; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + + } + } + } + else + { + Flags = pTracker->Flags; + pTracker->Flags &= ~(NBT_BROADCAST | NBT_NAME_SERVER); + // set a different timeout for nameserver name registration + // + pTimerQEntry->DeltaTime = NbtConfig.uRetryTimeout; + pTimerQEntry->Retries = NbtConfig.uNumRetries + 1; + + if ((Flags & NBT_BROADCAST) && (LocalNodeType & MNODE)) + { + // + // Registered through broadcast, so try the name server now. + IncrementNameStats(NAME_REGISTRATION_SUCCESS, + FALSE); // not name server register + + // + pTracker->Flags |= NBT_NAME_SERVER; + if ((pTracker->pDeviceContext->lNameServerAddress == LOOP_BACK) || + pTracker->pDeviceContext->WinsIsDown) + { + pTimerQEntry->DeltaTime = 10; + pTimerQEntry->Retries = 1; + + } + } + else + if ((Flags & NBT_NAME_SERVER) && !(LocalNodeType & BNODE)) + { + // + // Can't reach the name server, so try the backup + + pTracker->Flags |= NBT_NAME_SERVER_BACKUP; + // + // short out the timer if no backup server + // + if ((pTracker->pDeviceContext->lBackupServer == LOOP_BACK) || + pTracker->pDeviceContext->WinsIsDown) + { + pTimerQEntry->DeltaTime = 10; + pTimerQEntry->Retries = 1; + + } + } + else + if ((LocalNodeType & MSNODE) && !(Flags & NBT_BROADCAST)) + { + if (Flags & NBT_NAME_SERVER_BACKUP) + { + // the msnode switches to broadcast if all else fails + // + pTracker->Flags |= NBT_BROADCAST; + IncrementNameStats(NAME_REGISTRATION_SUCCESS, + FALSE); // not name server register + + // + // change the timeout and retries since + // broadcast uses a shorter timeout + // + pTimerQEntry->DeltaTime = NbtConfig.uBcastTimeout; + pTimerQEntry->Retries = (USHORT)pNbtGlobConfig->uNumBcasts + 1; + } + } + else + { + + if (LocalNodeType & BNODE) + { + IncrementNameStats(NAME_REGISTRATION_SUCCESS, + FALSE); // not name server register + } + // + // the timeout has expired on the name registration + // so call the client + // + + // return the tracker block to its queue + LOCATION(0x54); + + // + // start a timer to stop using WINS for a short period of + // time. + // + SetWinsDownFlag(pTracker->pDeviceContext); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + DereferenceTracker(pTracker); + status = STATUS_SUCCESS; + InterlockedCallCompletion(pTimerQEntry,status); + + return; + } + } + pTracker->RefCount++; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + NULL,NULL,NULL, + 0,0, + PduType, + TRUE); + + DereferenceTracker(pTracker); + pTimerQEntry->Flags |= TIMER_RESTART; + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + else + { + // return the tracker block to its queue + LOCATION(0x55); + DereferenceTrackerNoLock(pTracker); + } + + +} + + +//---------------------------------------------------------------------------- +VOID +NodeStatusCompletion( + 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; + COMPLETIONCLIENT pClientCompletion; + PVOID pClientContext; + PCHAR pName0; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + if (pTimerQEntry) + { + + + if (--pTimerQEntry->Retries) + { + PUCHAR pHdr; + ULONG Length; + ULONG UNALIGNED * pAddress; + PFILE_OBJECT pFileObject; + + // send the Datagram...increment ref count + pTracker->RefCount++; + + // + // 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 in NsDgramSendCompleted. + // + + pName0 = Nbt_inet_addr(pTracker->pNameAddr->Name) + ? "*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" : 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 + // note that the passed in transport address must stay valid till this + // send completes + pTracker->SendBuffer.pDgramHdr = (PVOID)pHdr; + if (pTracker->pDeviceContext->IpAddress) + { + pFileObject = pTracker->pDeviceContext->pNameServerFileObject; + } + else + pFileObject = NULL; + status = UdpSendDatagram( + pTracker, + pTracker->pNameAddr->IpAddress, + pFileObject, + NameDgramSendCompleted, + pHdr, + NBT_NAMESERVICE_UDP_PORT, + NBT_NAME_SERVICE); + + } + + DereferenceTracker(pTracker); + + // always restart even if the above send fails, since it might succeed + // later. + pTimerQEntry->Flags |= TIMER_RESTART; + + } + else + { + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + pClientCompletion = pTimerQEntry->ClientCompletion; + pClientContext = pTimerQEntry->ClientContext; + CHECK_PTR(pTimerQEntry); + pTimerQEntry->ClientCompletion = 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 + // + RemoveEntryList(&pTracker->Linkage); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Do not dereference here since Node Status Done will do + // the dereference + // + // DereferenceTracker(pTracker); + + // + // pClientContext will be zero if we came here through nbtstat + // + if (!pClientContext) + pClientContext = pTracker; + + (*pClientCompletion)( + pClientContext, + STATUS_TIMEOUT); + return; + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return; + } + } + else + { + // + // Do not dereference here since Node Status Done will do + // the dereference + // + //DereferenceTrackerNoLock(pTracker);; + } + +} + +//---------------------------------------------------------------------------- +VOID +RefreshRegCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine handles the name Refresh timeouts on packets sent to the Name + Service. I.e it sends refreshes to the nameserver until a response is + heard or the number of retries is exceeded. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq; + COMPLETIONCLIENT pCompletionClient; + + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + if (pTimerQEntry) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // + // check if the timer has been stopped yet, since stopping the timer + // nulls the client completion routine. If not null, increment the + // tracker refcount, so that the last refresh completing cannot + // free the tracker out from under us. + // + pCompletionClient = pTimerQEntry->ClientCompletion; + if (pCompletionClient) + { + // if still some count left and not refreshed yet + // then do another refresh request + // + pNameAddr = pTracker->pNameAddr; + + if (--pTimerQEntry->Retries) + { + pTracker->RefCount++; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + NULL,NULL,NULL, + 0,0, + eNAME_REFRESH, + TRUE); + + // always restart even if the above send fails, since it might succeed + // later. + pTimerQEntry->Flags |= TIMER_RESTART; + DereferenceTracker(pTracker); + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // this calls the completion routine synchronizing with the + // timer expiry code. + InterlockedCallCompletion(pTimerQEntry,STATUS_TIMEOUT); + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + +} + +//---------------------------------------------------------------------------- +ULONG +SetFirstDeviceContext( + OUT tDEVICECONTEXT **ppDeviceContext, + IN tNAMEADDR *pNameAddr + ) +/*++ + +Routine Description: + + This routine finds the first adapter as specified in the name's adapter + mask and set the DeviceContext associated with it. It then clears the + bit in the adapter mask of pNameAddr. + +Arguments: + + +Return Value: + + TRUE if pNameAddr->AdapterMask != 0 + +--*/ +{ + CTEULONGLONG AdapterNumber = 1; + CTEULONGLONG i; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + while (!(AdapterNumber & pNameAddr->AdapterMask)) + { + AdapterNumber = AdapterNumber <<1; + } + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead->Flink; + for (i=1;i < AdapterNumber ;i = i << 1 ) + { + pEntry = pEntry->Flink; + } + + *ppDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + // turn off the adapter bit since we are releasing the name on this adapter + // now. + // + pNameAddr->AdapterMask &= ~AdapterNumber; + + return TRUE; +} +//---------------------------------------------------------------------------- +NTSTATUS +ReleaseNameOnNet( + tNAMEADDR *pNameAddr, + PCHAR pScope, + PVOID pClientContext, + PVOID pClientCompletion, + ULONG LocalNodeType, + tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine deletes a name on the network either by a + broadcast or by talking to the NS depending on the type of node. (M,P or B) + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +Called By: ProxyQueryFromNet() in proxy.c, NbtConnect() in name.c + +--*/ + +{ + ULONG Timeout; + USHORT Retries; + NTSTATUS status=STATUS_UNSUCCESSFUL; + tDGRAM_SEND_TRACKING *pTracker; + USHORT uAddrType; + CTELockHandle OldIrq; + tDEVICECONTEXT *pReleaseDeviceContext; + + // ASSERT(pNameAddr->AdapterMask); // This fails when NbtDestroyDeviceObject + // is called and the name has already been released + + // + // If this name is not registered on any adapters, return STATUS_UNSUCCESSFUL + // + if (pNameAddr->AdapterMask == 0) + { + return STATUS_UNSUCCESSFUL; + } + + // + // Find the DeviceContext specified by the Adapter mask in pNameAddr + // and clear the corresponding bit in pNameAddr->AdapterMask + // Return FALSE if no Adapter was specified + // + if (pDeviceContext) + { + pReleaseDeviceContext = pDeviceContext; + pNameAddr->AdapterMask &= ~(pDeviceContext->AdapterNumber); + } + else if (!SetFirstDeviceContext(&pReleaseDeviceContext,pNameAddr)) + { + return (status); + } + + status = GetTracker(&pTracker); + if (!NT_SUCCESS(status)) + { + return(status); + } + CHECK_PTR(pTracker); + + pTracker->pDeviceContext = pReleaseDeviceContext; + LocalNodeType = AppropriateNodeType( pNameAddr->Name, LocalNodeType ); + + // set to NULL to catch any erroneous frees. + pTracker->SendBuffer.pDgramHdr = NULL; + + // Set a few values as a precursor to releasing the name either by + // broadcast or with the name server + // + switch (LocalNodeType & NODE_MASK) + { + case MSNODE: + case MNODE: + case PNODE: + + Retries = (USHORT)pNbtGlobConfig->uNumRetries; + Timeout = (ULONG)pNbtGlobConfig->uRetryTimeout; + + pTracker->Flags = NBT_NAME_SERVER; + break; + + case BNODE: + default: + +#ifndef VXD + Retries = (USHORT)pNbtGlobConfig->uNumBcasts; +#else + Retries = (USHORT)1; +#endif + Timeout = (ULONG)pNbtGlobConfig->uBcastTimeout; + pTracker->Flags = NBT_BROADCAST; + } + // + // Release name on the network + // + if ((pNameAddr->NameTypeState & NAME_TYPE_MASK) != NAMETYPE_UNIQUE) + { + uAddrType = NBT_GROUP; + } + else + { + uAddrType = NBT_UNIQUE; + } + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Doing Name Release on name %16.16s<%X>\n", + pNameAddr->Name,pNameAddr->Name[15])); + + pTracker->RefCount = 2; + status = UdpSendNSBcast(pNameAddr, + pScope, + pTracker, + ReleaseCompletion, + pClientContext, + pClientCompletion, + Retries, + Timeout, + eNAME_RELEASE, + TRUE); + + DereferenceTracker(pTracker); + + if (!NT_SUCCESS(status)) + { + NTSTATUS Locstatus; + COMPLETIONCLIENT pCompletion; + PVOID pContext; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Query failed - bad retcode from UdpSendNsBcast during name release= %X\n", + status)); + + // Stopping the timer will call ReleaseCompletion which will + // free the tracker + // + pCompletion = NULL; + if (pNameAddr->pTimer) + { + Locstatus = StopTimer(pNameAddr->pTimer,&pCompletion,&pContext); + + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + } + else + { + // no timer setup, so just free the tracker - but check if we + // have a hdr to free too. + // + if (status != STATUS_INSUFFICIENT_RESOURCES) + { + FreeTracker(pTracker, FREE_HDR | RELINK_TRACKER); + } + else + { + FreeTracker(pTracker, RELINK_TRACKER); + } + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + return(status); +} +//---------------------------------------------------------------------------- +VOID +ReleaseCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. It must + decide if another name query should be done, and if not, then it calls the + client's completion routine (in completion2). + This routine handles both the broadcast portion of the name queries and + the WINS server directed sends. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + ULONG LocalNodeType; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + if (IsBrowserName(pTracker->pNameAddr->Name)) + { + LocalNodeType = BNODE; + } + else + { + LocalNodeType = NodeType; + } + + // + // check if the client completion routine is still set. If not then the + // timer has been cancelled and this routine should just clean up its + // buffers associated with the tracker. + // + if (pTimerQEntry) + { + // if number of retries is not zero then continue trying to contact the + // Name Server. + // + if (!(--pTimerQEntry->Retries)) + { + if ((LocalNodeType & MNODE) && + (pTracker->Flags & NBT_NAME_SERVER)) + { + // + // try broadcast + // + pTracker->Flags &= ~NBT_NAME_SERVER; + pTracker->Flags |= NBT_BROADCAST; + + // set a different timeout for broadcast name resolution + // + pTimerQEntry->DeltaTime = NbtConfig.uBcastTimeout; + pTimerQEntry->Retries = NbtConfig.uNumBcasts; + + + } + else + { + // + // the timeout has expired on the name release + // so call the client + // + status = InterlockedCallCompletion(pTimerQEntry,STATUS_TIMEOUT); + + // return the tracker block to its queue if we successfully + // called the completion routine since someone else might + // have done a Stop timer at this very moment and freed the + // tracker already (i.e. the last else clause in this routine). + // + if (NT_SUCCESS(status)) + { + DereferenceTracker(pTracker);; + } + return; + + } + + } + + pTracker->RefCount++; + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + NULL,NULL,NULL, + 0,0, + eNAME_RELEASE, + TRUE); + + DereferenceTracker(pTracker); + pTimerQEntry->Flags |= TIMER_RESTART; + } + else + { + // return the tracker block to its queue + DereferenceTrackerNoLock(pTracker);; + } +} + +//---------------------------------------------------------------------------- +VOID +NameReleaseDoneOnDynIf( + PVOID pContext, + NTSTATUS Status + ) +/*++ + +Routine Description: + + This routine is called when a name is released on the network for a deleted dynamic If. + Itsmain, role in life is to free the memory in Context, which is the pAddressEle + structure. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +Called By Release Completion (above) +--*/ + +{ + CTELockHandle OldIrq1; + tADDRESSELE *pAddress; + tNAMEADDR *pNameAddr; + + pAddress = (tADDRESSELE *)pContext; + pNameAddr = pAddress->pNameAddr; + + + // + // If last device, release resources. + // + if (!pNameAddr->AdapterMask) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // this should remove and delete the name from the local table. Since it + // is possible to re-register the name during the name release on the + // net, we need this check here for the ref count, since NbtOpenAddress + // will increment it if is going to reregister it. + // + if (pAddress->RefCount == 0) + { + + // remove the address object from the list of addresses tied to the + // device context for the adapter + // + RemoveEntryList(&pAddress->Linkage); + + CHECK_PTR(pAddress->pNameAddr); + pAddress->pNameAddr->pAddressEle = NULL; + + ASSERT(IsListEmpty(&pAddress->ClientHead)); + + // check if pnameaddr memory already freed + ASSERT(pAddress->pNameAddr->pAddressEle != (PVOID)0xD1000000); + + NbtDereferenceName(pAddress->pNameAddr); + + 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); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + } + } + +} +//---------------------------------------------------------------------------- +VOID +NameReleaseDone( + PVOID pContext, + NTSTATUS Status + ) +/*++ + +Routine Description: + + This routine is called when a name is released on the network. Its + main, role in life is to free the memory in Context, which is the pAddressEle + structure. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +Called By Release Completion (above) +--*/ + +{ + CTELockHandle OldIrq1; + tADDRESSELE *pAddress; + tNAMEADDR *pNameAddr; + + pAddress = (tADDRESSELE *)pContext; + pNameAddr = pAddress->pNameAddr; + + + if (pNameAddr->AdapterMask) + { + // the name is not released for all adapters yet + // + ReleaseNameOnNet(pNameAddr, + NbtConfig.pScope, + pAddress, + NameReleaseDone, + NodeType, + NULL); + } + else + { + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // this should remove and delete the name from the local table. Since it + // is possible to re-register the name during the name release on the + // net, we need this check here for the ref count, since NbtOpenAddress + // will increment it if is going to reregister it. + // + if (pAddress->RefCount == 0) + { + + // remove the address object from the list of addresses tied to the + // device context for the adapter + // + RemoveEntryList(&pAddress->Linkage); + + CHECK_PTR(pAddress->pNameAddr); + pAddress->pNameAddr->pAddressEle = NULL; + + ASSERT(IsListEmpty(&pAddress->ClientHead)); + + // check if pnameaddr memory already freed + ASSERT(pAddress->pNameAddr->pAddressEle != (PVOID)0xD1000000); + + NbtDereferenceName(pAddress->pNameAddr); + + 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); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + } + } + +} + +//---------------------------------------------------------------------------- +VOID +DereferenceTracker( + IN tDGRAM_SEND_TRACKING *pTracker + ) +/*++ + +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; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (--pTracker->RefCount) + { + LOCATION(0x56); + } + else + { + LOCATION(0x99); + + // the datagram header may have already been freed + // + FreeTracker(pTracker, RELINK_TRACKER); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); +} +//---------------------------------------------------------------------------- +VOID +DereferenceTrackerNoLock( + IN tDGRAM_SEND_TRACKING *pTracker + ) +/*++ + +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 + +--*/ +{ + if (--pTracker->RefCount) + { + LOCATION(0x56); + } + else + { + LOCATION(0x99); + + // the datagram header may have already been freed + // + FreeTracker(pTracker, RELINK_TRACKER); + } +} +//---------------------------------------------------------------------------- +VOID +DereferenceTrackerDelete( + IN tDGRAM_SEND_TRACKING *pTracker + ) +/*++ + +Routine Description: + + This routine frees the datagram hdr and puts the Tracker block back on the free + queue. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + CTELockHandle OldIrq; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (--pTracker->RefCount) + { + LOCATION(0xA9); + return; + } + else + { + LOCATION(0xAA); + + // the datagram header may have already been freed + // + FreeTracker(pTracker, FREE_HDR | RELINK_TRACKER); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +StartRefresh( + IN tNAMEADDR *pNameAddr, + IN tDGRAM_SEND_TRACKING *pTracker, + IN BOOLEAN ResetDevice + ) +/*++ + +Routine Description: + + This routine handles refreshing a name with the Name server. + + The idea is to set the timeout to T/8 and check for names with the Refresh + bit cleared - re-registering those names. At T=4 and T=0, clear all bits + and refresh all names. The Inbound code sets the refresh bit when it gets a + refresh response from the NS. + +Arguments: + + +Return Value: + + none + +--*/ +{ + CTELockHandle OldIrq; + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext; + BOOLEAN NewTracker; + + if (!pTracker) + { + LOCATION(0x9); + status = GetTracker(&pTracker); + if (!NT_SUCCESS(status)) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pTracker->Flags = NBT_NAME_SERVER; + + NewTracker = TRUE; + + // need to prevent the tracker from being freed by a pdu from + // the wire before the UdpSendNsBcast is done + // + pTracker->RefCount = 2; + } + else + { + NewTracker = FALSE; + LOCATION(0xa); + // this accounts for the dereference done after the call to + // send the datagram below. + pTracker->RefCount += 1; + } + + + // set the name to be refreshed in the tracker block + pTracker->pNameAddr = pNameAddr; + + // this is set true when a new name gets refreshed + // + if (ResetDevice) + { + PLIST_ENTRY pEntry; + CTEULONGLONG AdapterMask; + CTEULONGLONG AdapterNumber = 1; + ULONG i; + + LOCATION(0xb); + + // + // Travel to the actual device this name is registered on + // + pEntry = NbtConfig.DeviceContexts.Flink; + AdapterMask = pNameAddr->AdapterMask; + + if (AdapterMask) { + while (!(AdapterNumber & AdapterMask)) + { + AdapterNumber = AdapterNumber << 1; + } + + for (i = 1;i < AdapterNumber ;i = i << 1 ) { + pEntry = pEntry->Flink; + } + } + + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt: Refresh adapter: %lx:%lx, dev.nm: %lx for name: %lx\n", + AdapterNumber, pDeviceContext->BindName.Buffer, pNameAddr)); + + pTracker->pDeviceContext = pDeviceContext; + // + // Clear the transaction Id so that CreatePdu will increment + // it for this new name + // + CHECK_PTR(pTracker); + pTracker->TransactionId = 0; + } + + status = UdpSendNSBcast( + pNameAddr, + NbtConfig.pScope, + pTracker, + RefreshRegCompletion, + pTracker, + NextRefresh, + NbtConfig.uNumRetries, + NbtConfig.uRetryTimeout, + eNAME_REFRESH, + TRUE); + + DereferenceTracker(pTracker); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + LOCATION(0x57); + + if (!NT_SUCCESS(status)) + { + + LOCATION(0xe); + + + // + // This will free the tracker. Name refresh will stop until + // the next refresh timeout and at that point it will attempt + // to refresh the names again. + // + DereferenceTrackerNoLock(pTracker); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt:Failed to send Refresh!! status = %X****\n",status)); + return(status); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(status); + +} + +//---------------------------------------------------------------------------- +VOID +GetNextName( + IN tNAMEADDR *pNameAddrIn, + OUT tNAMEADDR **ppNameAddr + ) +/*++ + +Routine Description: + + This routine finds the next name to refresh, including incrementing the + reference count so that the name cannot be deleted during the refresh. + The JointLock spin lock is held before calling this routine. + +Arguments: + + +Return Value: + + none + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + LONG i; + tNAMEADDR *pNameAddr; + tHASHTABLE *pHashTable; + + + pHashTable = NbtConfig.pLocalHashTbl; + + for (i= NbtConfig.CurrentHashBucket;i < pHashTable->lNumBuckets ;i++ ) + { + // + // use the last name as the current position in the linked list + // only if that name is still resolved, otherwise start at the + // begining of the hash list, incase the name got deleted in the + // mean time. + // + if (pNameAddrIn && (pNameAddrIn->NameTypeState & STATE_RESOLVED)) + { + pHead = &NbtConfig.pLocalHashTbl->Bucket[NbtConfig.CurrentHashBucket]; + pEntry = pNameAddrIn->Linkage.Flink; + + pNameAddrIn = NULL; + } + else + { + pHead = &pHashTable->Bucket[i]; + pEntry = pHead->Flink; + } + + while (pEntry != pHead) + { + CTEULONGLONG AllRefreshed; + + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + + // don't refresh scope names or names in conflict or that are the + // broadcast name "* " or quick unique names - i.e. the permanent + // name is nametype quick + // + if (!(pNameAddr->NameTypeState & STATE_RESOLVED) || + (pNameAddr->Name[0] == '*') || + (pNameAddr->NameTypeState & NAMETYPE_QUICK)) + { + pEntry = pEntry->Flink; + continue; + } + + // check if the name has been refreshed yet + // build a value with ones set for all adapter numbers by shifting + // 1 one bit position further and then subtracting 1. + // i.e. 100 -1 = 11 (binary) or 4-3 = 3(11 binary) + // + AllRefreshed = ((CTEULONGLONG)1 << NbtConfig.AdapterCount) -1; + if (pNameAddr->RefreshMask == AllRefreshed) + { + pEntry = pEntry->Flink; + continue; + } + + // increment the reference count so that this name cannot + // disappear while it is being refreshed and screw up the linked + // list + CTEInterlockedIncrementLong(&pNameAddr->pAddressEle->RefCount); + + NbtConfig.CurrentHashBucket = (USHORT)i; + + *ppNameAddr = pNameAddr; + return; + } + } + + *ppNameAddr = NULL; +} + + +//---------------------------------------------------------------------------- +VOID +NextRefresh( + IN PVOID pContext, + IN NTSTATUS CompletionStatus + ) +/*++ + +Routine Description: + + This routine queues the work to an Executive worker thread to handle + refreshing the next name. + +Arguments: + + +Return Value: + + none + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + LOCATION(0xf); + CTEQueueForNonDispProcessing(pTracker,(PVOID)CompletionStatus,NULL, + NextRefreshNonDispatch,pTracker->pDeviceContext); +} + +//---------------------------------------------------------------------------- +VOID +NextRefreshNonDispatch( + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles sending subsequent refreshes to the name server. + This is the "Client Completion" routine of the Timer started above. + +Arguments: + + +Return Value: + + none + +--*/ +{ + CTELockHandle OldIrq; + tNAMEADDR *pNameAddr; + tNAMEADDR *pNameAddrNext; + NTSTATUS status; + PLIST_ENTRY pEntry; + tDGRAM_SEND_TRACKING *pTracker; + tDEVICECONTEXT *pDeviceContext; + CTEULONGLONG AdapterNumber; + CTEULONGLONG AdapterMask; + CTEULONGLONG i; + BOOLEAN AbleToReachWins = FALSE; + NTSTATUS CompletionStatus; + + pTracker = ((NBT_WORK_ITEM_CONTEXT *)pContext)->pTracker; + CompletionStatus = (NTSTATUS)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + + pNameAddr = pTracker->pNameAddr; + ASSERT(pNameAddr); + + // + // grab the resource so that a name refresh response cannot start running this + // code in a different thread before this thread has exited this routine, + // otherwise the tracker can get dereferenced twice and blown away. + // + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + LOCATION(0x1); + // turn on the bit corresponding to this adapter, since the name refresh + // completed ok + // + if (CompletionStatus == STATUS_SUCCESS) + { + LOCATION(0x2); + pNameAddr->RefreshMask |= pTracker->pDeviceContext->AdapterNumber; + AbleToReachWins = TRUE; + } + else + if (CompletionStatus != STATUS_TIMEOUT) + { + LOCATION(0x3); + // if the timer times out and we did not get to the name server, then + // that is not an error. However, any other bad status + // must be a negative response to a name refresh so mark the name + // in conflict + // + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_CONFLICT; + AbleToReachWins = TRUE; + } + + // for the multihomed case a failure to reach wins out one of the adapters + // is not necessarily a failure to reach any WINS. Since this flag + // is just an optimization to prevent clients from continually trying to + // register all of their names if WINS is unreachable, we can ignore the + // optimization for the multihomed case. The few nodes that are + // multihomed will not create that much traffic compared to possibly + // thousands that are singly homed clients. + if (NbtConfig.MultiHomed) + { + AbleToReachWins = TRUE; + } + // + // get the next name in the hash table only if we have refreshed + // all adapters for this name i.e. + // check if any higher bits are set inthe AdapterMask + // + AdapterNumber = pTracker->pDeviceContext->AdapterNumber; + + // go to the next adapter + AdapterNumber = AdapterNumber << 1; + + // + // still more adapters to check ... + // + if (pNameAddr->AdapterMask >= AdapterNumber) + { + BOOLEAN Found; + + // go to the next device context and refresh the name there + // using the same tracker. + // + LOCATION(0x8); + + // + // look for a device context with a valid IP address since there is + // no sense in refreshing names out unconnected RAS links. + // + Found = FALSE; + while (!Found) + { + AdapterMask = pNameAddr->AdapterMask; + while (!(AdapterNumber & AdapterMask)) + { + AdapterNumber = AdapterNumber << 1; + } + + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt:Refresh on Adapter %X, Name %15.15s<%X>\n", + (ULONG)AdapterNumber,pNameAddr->Name,pNameAddr->Name[15])); + + // get the nth devicecontext in the list + // + pEntry = NbtConfig.DeviceContexts.Flink; + for (i = 1;i < AdapterNumber ;i = i << 1 ) + { + pEntry = pEntry->Flink; + } + + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + // + // find an adapter with a valid ip address and name server address + // + if ((pDeviceContext->IpAddress != LOOP_BACK) && + (pDeviceContext->lNameServerAddress != LOOP_BACK)) + { + + pTracker->pDeviceContext = pDeviceContext; + + // remove the previous timer from the addressele since StartRefresh + // will start a new timer - safety measure and probably not required! + // + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // this call sends out a name registration PDU on a different adapter + // to (potentially) a different name server. The Name service PDU + // is the same as the last one though...no need to create a new one. + // + status = StartRefresh(pNameAddr,pTracker,FALSE); + if (!NT_SUCCESS(status)) + { + DereferenceTracker(pTracker); + NbtConfig.DoingRefreshNow = FALSE; + } + goto ExitRoutine; + } + + // go to the next adapter + AdapterNumber = AdapterNumber << 1; + + + if (pNameAddr->AdapterMask < AdapterNumber) + { + // no more adapters so try the next name below... + break; + } + } + } + + + if (pNameAddr->AdapterMask < AdapterNumber) + { + // the name's adapter mask does not go this high in adapters, so go on + // to the next name. + // + + // if we failed to reach WINS on the last refresh, stop refreshing + // until the next time interval. This cuts down on network traffic. + // + LOCATION(0x4); + if (AbleToReachWins) + { + LOCATION(0x5); + GetNextName(pNameAddr,&pNameAddrNext); + } + else + pNameAddrNext = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (pNameAddrNext) + { + // reset back to the first adapter for this new name + // + LOCATION(0x6); + status = StartRefresh(pNameAddrNext,pTracker,TRUE); + if (!NT_SUCCESS(status)) + { + NbtConfig.DoingRefreshNow = FALSE; + DereferenceTracker(pTracker); + KdPrint(("Nbt:StartRefresh failed on Adapter %X, Name %15.15s<%x>, status=%X\n", + (ULONG)AdapterNumber,pNameAddr->Name,pNameAddr->Name[15], status)); + } + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt:Refresh on Adapter %X, Name %15.15s<%x>\n", + (ULONG)AdapterNumber,pNameAddr->Name,pNameAddr->Name[15])); + } + + // *** this code cleans up the previously refreshed name *** + + // clear the timer entry ptr in the address record so the dereference + // can proceed to completion. NOTE: the caller of this routine must + // NOT reference pAddressEle since the memory may have been freed here + // + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + + NbtDereferenceAddress(pNameAddr->pAddressEle); + + if (!pNameAddrNext) + { + LOCATION(0x7); + // we finally delete the tracker here after using it to refresh + // all of the names. It is not deleted in the RefreshCompletion + // routine anymore! + // + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt:Refresh Done Adapter %X, Name %15.15s<%x>\n", + (ULONG)AdapterNumber,pNameAddr->Name,pNameAddr->Name[15])); + + DereferenceTracker(pTracker); + NbtConfig.DoingRefreshNow = FALSE; + + } + + + } + +ExitRoutine: + CTEMemFree(pContext); + + CTEExReleaseResource(&NbtConfig.Resource); +} + +//---------------------------------------------------------------------------- +VOID +RefreshTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine handles is the timeout handler for name refreshes to + WINS. It just queues the request to the Executive worker thread so that + the work can be done at non-dispatch level. If there is currently a + refresh going on, then the routine simply restarts the timer and + exits. + +Arguments: + + +Return Value: + + none + +--*/ +{ + CTELockHandle OldIrq; + + if (!pTimerQEntry) + { + return; + } + else + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (NodeType & BNODE) + { + // Do not restart the timer + CHECK_PTR(pTimerQEntry); + + pTimerQEntry->Flags = 0; + NbtConfig.pRefreshTimer = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return; + } + + LOCATION(0x10); + + + if (!NbtConfig.DoingRefreshNow) + { + // this is a global flag that prevents a second refresh + // from starting when one is currently going on. + // + LOCATION(0x11); + NbtConfig.DoingRefreshNow = TRUE; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CTEQueueForNonDispProcessing(NULL,NULL,NULL,RefreshBegin,NULL); + + } // doing refresh now + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + + + // set any new timeout value and restart the timer + // + pTimerQEntry->DeltaTime = NbtConfig.MinimumTtl/NbtConfig.RefreshDivisor; + pTimerQEntry->Flags |= TIMER_RESTART; + } + +} + +//---------------------------------------------------------------------------- +VOID +RefreshBegin( + PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles starting up sending name refreshes to the name server. + + The idea is to set the timeout to T/8 and check for names with the Refresh + bit cleared - re-registering those names. At T=4 and T=0, clear all bits + and refresh all names. The Inbound code sets the refresh bit when it gets a + refresh response from the NS. + +Arguments: + + +Return Value: + + none + +--*/ +{ + CTELockHandle OldIrq; + tNAMEADDR *pNameAddr; + NTSTATUS status; + tHASHTABLE *pHashTable; + PCHAR pScope; + LONG i,j; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + ULONG TimeoutCount; + tDEVICECONTEXT *pDeviceContext; + CTEULONGLONG Adapter; + BOOLEAN fTimeToSwitch = FALSE; + BOOLEAN fTimeToRefresh = FALSE; + + CTEMemFree(pContext); + LOCATION(0x12); + + pScope = NbtConfig.pScope; + + // get the timeout cycle number + + TimeoutCount = NbtConfig.sTimeoutCount++; + // + // BUG #3094: + // Currently, we try to switch back to primary every 1/2 TTl => 3 days, generally. + // We try to do this more often - every hour by checking at each timeout + // if we have not switched for an hour. + // + // We take care not to clear the refresh bits in the name on every interval by re-checking + // for the T/2 or 0 conditions later. + // + // Do this before the modulo operation. + // + fTimeToSwitch = (((NbtConfig.MinimumTtl/NbtConfig.RefreshDivisor) * + (TimeoutCount - NbtConfig.LastSwitchTimeoutCount)) >= DEFAULT_SWITCH_TTL); + + IF_DBG(NBT_DEBUG_REFRESH) + KdPrint(("Nbt:fTimeToSwitch: %d, MinTtl: %lx, RefDiv: %d, TimeoutCount: %d, LastSwTimeoutCount: %d", + fTimeToSwitch, NbtConfig.MinimumTtl, NbtConfig.RefreshDivisor, TimeoutCount, NbtConfig.LastSwitchTimeoutCount)); + + NbtConfig.sTimeoutCount %= NbtConfig.RefreshDivisor; + + // + // If the refresh timeout has been set to the maximum value then do + // not send any refreshes to the name server + // + if (NbtConfig.MinimumTtl == NBT_MAXIMUM_TTL) + { + NbtConfig.DoingRefreshNow = FALSE; + return; + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // + // go through the local table clearing the REFRESHED bit and sending + // name refreshes to the name server + // + pHashTable = NbtConfig.pLocalHashTbl; + + // clear the refreshed bits so all names get refreshed if we are + // at interval 0 or interval 8/2 + // + + fTimeToRefresh = ((TimeoutCount == (NbtConfig.RefreshDivisor/2)) || (TimeoutCount == 0)); + + if (fTimeToRefresh || fTimeToSwitch) + { + CTEULONGLONG JustSwitched = 0; + + NbtConfig.LastSwitchTimeoutCount = (USHORT)TimeoutCount; + + for (i=0 ;i < pHashTable->lNumBuckets ;i++ ) + { + + pHead = &pHashTable->Bucket[i]; + pEntry = pHead->Flink; + + // + // Go through each name in each bucket of the hashtable + // + while (pEntry != pHead) + { + PLIST_ENTRY pHead1,pEntry1; + + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + + // don't refresh scope names or names in conflict or that are the + // broadcast name "* ", or Quick added names.(since these are + // not registered on the network) + // + + if (!(pNameAddr->NameTypeState & STATE_RESOLVED) || + (pNameAddr->Name[0] == '*') || + (IsBrowserName(pNameAddr->Name)) || + (pNameAddr->NameTypeState & NAMETYPE_QUICK)) + { + pEntry = pEntry->Flink; + continue; + } + + // Go through each adapter checking + // if a name was not refreshed. If not switch to + // the backup WINS for this devicecontext. + // + pHead1 = &NbtConfig.DeviceContexts; + pEntry1 = pHead1->Flink; + j = 0; + while (pEntry1 != pHead1) + { + + Adapter = (CTEULONGLONG)1 << j++; + + pDeviceContext = CONTAINING_RECORD(pEntry1,tDEVICECONTEXT,Linkage); + pEntry1 = pEntry1->Flink; + + // if the name was registered on this + // adapter...and we have not already switched to the + // backup name server, check if we should switch now. + // + if (pNameAddr->AdapterMask & Adapter) + { + // if we haven't just switched this adapter to its + // backup and... + // if the name was not refreshed, then switch to + // the backup + // + // or if the name was refreshed, but + // to the backup, try the primary again. + // + if (!(JustSwitched & Adapter) && + (!(pNameAddr->RefreshMask & Adapter) || + ((pNameAddr->RefreshMask & Adapter) && + (pDeviceContext->RefreshToBackup)))) + { + SwitchToBackup(pDeviceContext); + // + // keep a bit mask of which adapters have + // switched to backup so we don't + // switch again and be back where we started. + // + JustSwitched |= Adapter; + } + + } + + } + + // clear the refresh mask, so we can refresh all over + // again! + CHECK_PTR(pNameAddr); + + // + // Dont clear the refresh bits when we are trying to switch (say at T/8). + // + if (fTimeToRefresh) { + pNameAddr->RefreshMask = 0; + } + + // next hash table entry + pEntry = pEntry->Flink; + } + + } + } + + // always start at the first name in the hash table. As each name gets + // refreshed NextRefresh will be hit to get the next name etc.. + // + NbtConfig.CurrentHashBucket = 0; + + status = STATUS_UNSUCCESSFUL; + + + // + // get the next(first) name in the hash table + // + GetNextName(NULL,&pNameAddr); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (pNameAddr) + { + LOCATION(0x13); + status = StartRefresh(pNameAddr,NULL,TRUE); + + // + // If this routine fails then the address element increment done in + // GetNextName has to be undone here + // + if (!NT_SUCCESS(status)) + { + NbtDereferenceAddress(pNameAddr->pAddressEle); + NbtConfig.DoingRefreshNow = FALSE; + } + + } + else + { + NbtConfig.DoingRefreshNow = 0; + } + +} + +//---------------------------------------------------------------------------- +VOID +RemoteHashTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine handles deleting names in the Remote Hash table that are + old. The basic alorithm scans the table looking at the Timed_out bit. + If it is set then the name is deleted, otherwise the bit is set. This + means the names can live as long as 2*Timeout or as little as Timeout. + So set the Timeout to 6 Minutes and names live 9 minutes +- 3 minutes. + +Arguments: + + +Return Value: + + none + +--*/ +{ + CTELockHandle OldIrq; + tNAMEADDR *pNameAddr; + tHASHTABLE *pHashTable; + PCHAR pScope; + LONG i; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + if (pTimerQEntry) + { + pScope = NbtConfig.pScope; + + // + // go through the remote table deleting names that have timeout bits + // set and setting the bits for names that have the bit clear + // + pHashTable = NbtConfig.pRemoteHashTbl; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + 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->TimeOutCount == 0) && + (pNameAddr->NameTypeState & (STATE_RESOLVED | STATE_RELEASED)) && + (pNameAddr->RefCount <= 1)) + { + NbtDereferenceName(pNameAddr); + } + else + { // + // don't mark the name as a candidate for deletion if + // someone is using it now + // + if (!(pNameAddr->NameTypeState & NAMETYPE_SCOPE) && + (pNameAddr->RefCount <= 1) && + (pNameAddr->NameTypeState & (STATE_RESOLVED | STATE_RELEASED))) + { + pNameAddr->TimeOutCount--; + } + + } + + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // restart the timer + // + pTimerQEntry->Flags |= TIMER_RESTART; + } + + return; + +} +//---------------------------------------------------------------------------- +VOID +NextKeepAlive( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS statuss, + IN ULONG Info + ) +/*++ + +Routine Description: + + This routine handles sending subsequent KeepAlives for sessions. + This is the "Client Completion" routine of the TdiSend that sends the + keep alive on the session. + +Arguments: + + +Return Value: + + none + +--*/ +{ + tLOWERCONNECTION *pLowerConnLast; + tLOWERCONNECTION *pLowerConn; + tDEVICECONTEXT *pDeviceContext; + + PUSH_LOCATION(0x92); + pDeviceContext = pTracker->pDeviceContext; + + pLowerConnLast = (tLOWERCONNECTION *)pTracker->pClientEle; + + // get the next session to send a keep alive on, if there is one, otherwise + // free the session header block. + // + GetNextKeepAlive(pDeviceContext, + &pDeviceContext, + pLowerConnLast, + &pLowerConn); + + NbtDereferenceLowerConnection(pLowerConnLast); + + if (pLowerConn) + { + + pTracker->pDeviceContext = pDeviceContext; + pTracker->pClientEle = (tCLIENTELE *)pLowerConn; + + ASSERT((pTracker->SendBuffer.HdrLength + pTracker->SendBuffer.Length) == 4); + PUSH_LOCATION(0x91); +#ifndef VXD + // this may wind up the stack if the completion occurs synchronously, + // because the completion routine is this routine, so call a routine + // that sets up a dpc to to the send, which will not run until this + // procedure returns and we get out of raised irql. + // + NTSendSession(pTracker, + pLowerConn, + NextKeepAlive); +#else + + (void) TcpSendSession( pTracker, pLowerConn, NextKeepAlive ) ; +#endif + } + else + { + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + } + + + +} + + +//---------------------------------------------------------------------------- +VOID +GetNextKeepAlive( + tDEVICECONTEXT *pDeviceContext, + tDEVICECONTEXT **ppDeviceContextOut, + tLOWERCONNECTION *pLowerConnIn, + tLOWERCONNECTION **ppLowerConnOut + ) +/*++ + +Routine Description: + + This routine handles sending session keep Alives to the other end of a + connection about once a minute or so. + +Arguments: + + +Return Value: + + none + +--*/ +{ + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + tLOWERCONNECTION *pLowerConn; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PLIST_ENTRY pHeadDevice; + PLIST_ENTRY pEntryDevice; + + // + // loop through all the adapter cards looking at all connections + // + pHeadDevice = &NbtConfig.DeviceContexts; + pEntryDevice = &pDeviceContext->Linkage; + while (pEntryDevice != pHeadDevice) + { + pDeviceContext = CONTAINING_RECORD(pEntryDevice,tDEVICECONTEXT,Linkage); + pEntryDevice = pEntryDevice->Flink; + + // grab the device context spin lock so that the lower connection + // element does not get removed from the Q while we are checking the + // connection state + // + CTESpinLock(pDeviceContext,OldIrq); + PUSH_LOCATION(0x90); + pHead = &pDeviceContext->LowerConnection; + // + // get the next lower connection after this one one the list, but + // be sure this connection is still on the active list by checking + // the state. + // + // If this connection has been cleaned up in OutOfRsrcKill, then dont trust the linkages. + // + if (pLowerConnIn && + !pLowerConnIn->OutOfRsrcFlag && + ((pLowerConnIn->State == NBT_SESSION_UP) || + (pLowerConnIn->State == NBT_SESSION_INBOUND))) + { + pEntry = pLowerConnIn->Linkage.Flink; + pLowerConnIn = NULL; + } + else + { + pEntry = pHead->Flink; + } + + while (pEntry != pHead) + { + + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + + // + // Inbound connections can hang around forever in that state if + // the session setup message never gets sent, so send keep + // alives on those too. + // + if ((pLowerConn->State == NBT_SESSION_UP) || + (pLowerConn->State == NBT_SESSION_INBOUND)) + { + + // grab the spin lock, recheck the state and + // increment the reference count so that this connection cannot + // disappear while the keep alive is being sent and screw up + // the linked list. + CTESpinLock(pLowerConn,OldIrq2); + if ((pLowerConn->State != NBT_SESSION_UP ) && + (pLowerConn->State != NBT_SESSION_INBOUND)) + { + // this connection is probably back on the free connection + // list, so we will never satisfy the pEntry = pHead and + // loop forever, so just get out and send keepalives on the + // next timeout + // + pEntry = pEntry->Flink; + PUSH_LOCATION(0x91); + CTESpinFree(pLowerConn,OldIrq2); + break; + + } + else + if (pLowerConn->RefCount >= 3 ) + { + // + // already a keep alive on this connection, or we + // are currently in the receive handler and do not + // need to send a keep alive. + // + pEntry = pEntry->Flink; + PUSH_LOCATION(0x93); + CTESpinFree(pLowerConn,OldIrq2); + continue; + } + + // + // found a connection to send a keep alive on + // + pLowerConn->RefCount++; + // + // return the current position in the list of connections + // + *ppLowerConnOut = pLowerConn; + *ppDeviceContextOut = pDeviceContext; + + CTESpinFree(pLowerConn,OldIrq2); + CTESpinFree(pDeviceContext,OldIrq); + + return; + + } + + pEntry = pEntry->Flink; + } + + CTESpinFree(pDeviceContext,OldIrq); + } + *ppLowerConnOut = NULL; + return; + +} + +//---------------------------------------------------------------------------- +VOID +SessionKeepAliveTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine handles starting the non dispatch level routine to send + keep alives. + +Arguments: + + +Return Value: + + none + +--*/ +{ + if (pTimerQEntry) + { + CTEQueueForNonDispProcessing(NULL,NULL,NULL,SessionKeepAliveNonDispatch,NULL); + + // restart the timer + // + pTimerQEntry->Flags |= TIMER_RESTART; + + } + return; + +} + +//---------------------------------------------------------------------------- +VOID +SessionKeepAliveNonDispatch( + PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles sending session keep Alives to the other end of a + connection about once a minute or so. + +Arguments: + + +Return Value: + + none + +--*/ +{ + NTSTATUS status; + tLOWERCONNECTION *pLowerConn; + tDEVICECONTEXT *pDeviceContext; + tSESSIONHDR *pSessionHdr; + tDGRAM_SEND_TRACKING *pTracker; + + + CTEPagedCode(); + // + // go through the list of connections attached to each adapter and + // send a session keep alive pdu on each + // + pDeviceContext = CONTAINING_RECORD(NbtConfig.DeviceContexts.Flink, + tDEVICECONTEXT,Linkage); + + // get the next session to send a keep alive on, if there is one, otherwise + // free the session header block. + // + GetNextKeepAlive(pDeviceContext, + &pDeviceContext, + NULL, + &pLowerConn); + + if (pLowerConn) + { + + // if we have found a connection, send the first keep alive. Subsequent + // keep alives will be sent by the completion routine, NextKeepAlive() + // + + pSessionHdr = (tSESSIONHDR *)NbtAllocMem(sizeof(tSESSIONERROR),NBT_TAG('S')); + if (!pSessionHdr) + { + return; + } + + // get a tracker structure, which has a SendInfo structure in it + status = GetTracker(&pTracker); + if (!NT_SUCCESS(status)) + { + CTEMemFree((PVOID)pSessionHdr); + return; + } + + CHECK_PTR(pTracker); + pTracker->SendBuffer.pDgramHdr = (PVOID)pSessionHdr; + pTracker->SendBuffer.HdrLength = sizeof(tSESSIONHDR); + pTracker->SendBuffer.Length = 0; + pTracker->SendBuffer.pBuffer = NULL; + + pSessionHdr->Flags = NBT_SESSION_FLAGS; // always zero + + pTracker->pDeviceContext = pDeviceContext; + pTracker->pClientEle = (tCLIENTELE *)pLowerConn; + CHECK_PTR(pSessionHdr); + pSessionHdr->Type = NBT_SESSION_KEEP_ALIVE; // 85 + pSessionHdr->Length = 0; // no data following the length byte + + status = TcpSendSession(pTracker, + pLowerConn, + NextKeepAlive); + + + } + + CTEMemFree(pContext); +} +//---------------------------------------------------------------------------- +VOID +IncrementNameStats( + IN ULONG StatType, + IN BOOLEAN IsNameServer + ) +/*++ + +Routine Description: + + This routine increments statistics on names that resolve either through + the WINS or through broadcast. + +Arguments: + + +Return Value: + + none + +--*/ +{ + + // + // Increment the stattype if the name server is true, that way we can + // differentiate queries and registrations to the name server or not. + // + if (IsNameServer) + { + StatType += 2; + } + + NameStatsInfo.Stats[StatType]++; + +} +//---------------------------------------------------------------------------- +VOID +SaveBcastNameResolved( + IN PUCHAR pName + ) +/*++ + +Routine Description: + + This routine saves the name in LIFO list, so we can see the last + N names that resolved via broadcast. + +Arguments: + + +Return Value: + + none + +--*/ +{ + ULONG Index; + + Index = NameStatsInfo.Index; + + CTEMemCopy(&NameStatsInfo.NamesReslvdByBcast[Index], + pName, + NETBIOS_NAME_SIZE); + + NameStatsInfo.Index++; + if (NameStatsInfo.Index >= SIZE_RESOLVD_BY_BCAST_CACHE) + { + NameStatsInfo.Index = 0; + } + +} + +// +// These are names that should never be sent to WINS. +// +BOOL +IsBrowserName( + IN PCHAR pName +) +{ + CHAR cNameType = pName[NETBIOS_NAME_SIZE - 1]; + + return ( + (cNameType == 0x1E) + || (cNameType == 0x1D) + || (cNameType == 0x01) + ); +} + +// +// Returns the node type that should be used with a request, +// based on NetBIOS name type. This is intended to help the +// node to behave like a BNODE for browser names only. +// +AppropriateNodeType( + IN PCHAR pName, + IN ULONG NodeType +) +{ + ULONG LocalNodeType = NodeType; + + if (LocalNodeType & BNODE) + { + if ( IsBrowserName ( pName ) ) + { + LocalNodeType &= BNODE; + } + } + return LocalNodeType; +} + diff --git a/private/ntos/nbt/nbt/nbtutils.c b/private/ntos/nbt/nbt/nbtutils.c new file mode 100644 index 000000000..b8bf40090 --- /dev/null +++ b/private/ntos/nbt/nbt/nbtutils.c @@ -0,0 +1,2042 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Nbtutils.c + +Abstract: + + This file continas a number of utility and support routines for + the NBT code. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" + + +NTSTATUS +DisableInboundConnections( + IN tDEVICECONTEXT *pDeviceContext, + OUT PLIST_ENTRY pLowerConnFreeHead + ); + +NTSTATUS +EnableInboundConnections( + IN tDEVICECONTEXT *pDeviceContext, + IN PLIST_ENTRY pLowerConnFreeHead + ); + +VOID +NbtNonDpcFreeAddrObj( + IN PVOID pContext + ); + +//#if DBG +LIST_ENTRY UsedIrps; +//#endif + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, ConvertDottedDecimalToUlong) +#pragma CTEMakePageable(PAGE, CloseLowerConnections) +#pragma CTEMakePageable(PAGE, CountUpperConnections) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +void +NbtFreeAddressObj( + tADDRESSELE *pBlk) + +/*++ + +Routine Description: + + This routine releases all memory associated with an Address object. + +Arguments: + + +Return Value: + + none + +--*/ + +{ + // + // let's come back and do this later when it's not dpc time + // + CTEQueueForNonDispProcessing( + NULL, + pBlk, + NULL, + NbtNonDpcFreeAddrObj, + pBlk->pDeviceContext); + +} + +//---------------------------------------------------------------------------- +VOID +NbtNonDpcFreeAddrObj( + IN PVOID pContext + ) + +/*++ + +Routine Description: + + This routine releases all memory associated with an Address object. + +Arguments: + + +Return Value: + + none + +--*/ + +{ + tADDRESSELE *pAddress; + + pAddress = (tADDRESSELE *)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + + if (pAddress) + { + +#ifndef VXD + if (pAddress->SecurityDescriptor) + SeDeassignSecurity(&pAddress->SecurityDescriptor); +#endif + // free the address block itself + // zero the verify value so that another user of the same memory + // block cannot accidently pass in a valid verifier + + pAddress->Verify += 10; + CTEMemFree((PVOID)pAddress); + } + + CTEMemFree(pContext); +} + +//---------------------------------------------------------------------------- +void +NbtFreeClientObj( + tCLIENTELE *pBlk) + +/*++ + +Routine Description: + + This routine releases all memory associated with Client object. + +Arguments: + + +Return Value: + + none + +--*/ + +{ + + if (pBlk) + { + // zero the verify value so that another user of the same memory + // block cannot accidently pass in a valid verifier + pBlk->Verify += 10; + CTEMemFree((PVOID)pBlk); + } +} + +//---------------------------------------------------------------------------- +void +FreeConnectionObj( + tCONNECTELE *pBlk) + +/*++ + +Routine Description: + + This routine releases all memory associated with a Connection object + and then it frees the connection object itself. + +Arguments: + + +Return Value: + + none + +--*/ + +{ + + if (pBlk) + { + + // zero the verify value so that another user of the same memory + // block cannot accidently pass in a valid verifier + pBlk->Verify += 10; + CTEMemFree(pBlk); + + } + +} + +//---------------------------------------------------------------------------- +tCLIENTELE * +NbtAllocateClientBlock(tADDRESSELE *pAddrEle) + +/*++ + +Routine Description: + + This routine allocates a block of memory for a client openning an + address. It fills in default values for the block and links the + block to the addresslist. The AddressEle spin lock is held when this + routine is called. + +Arguments: + + +Return Value: + + none + +--*/ + +{ + tCLIENTELE *pClientElement; + + // allocate memory for the client block + pClientElement = (tCLIENTELE *)CTEAllocInitMem(sizeof (tCLIENTELE)); + if (!pClientElement) + { + ASSERTMSG("Unable to allocate Memory for a client block\n", + pClientElement); + return(NULL); + } + CTEZeroMemory((PVOID)pClientElement,sizeof(tCLIENTELE)); + + CTEInitLock(&pClientElement->SpinLock); + + // Set Event handler function pointers to default routines provided by + // TDI +#ifndef VXD + pClientElement->evConnect = TdiDefaultConnectHandler; + pClientElement->evReceive = TdiDefaultReceiveHandler; + pClientElement->evDisconnect = TdiDefaultDisconnectHandler; + pClientElement->evError = TdiDefaultErrorHandler; + pClientElement->evRcvDgram = TdiDefaultRcvDatagramHandler; + pClientElement->evRcvExpedited = TdiDefaultRcvExpeditedHandler; + pClientElement->evSendPossible = TdiDefaultSendPossibleHandler; +#else + // + // VXD provides no client support for event handlers but does + // make use of some of the event handlers itself (for RcvAny processing + // and disconnect cleanup). + // + pClientElement->evConnect = NULL ; + pClientElement->evReceive = NULL ; + pClientElement->RcvEvContext = NULL ; + pClientElement->evDisconnect = NULL ; + pClientElement->evError = NULL ; + pClientElement->evRcvDgram = NULL ; + pClientElement->evRcvExpedited = NULL ; + pClientElement->evSendPossible = NULL ; +#endif + + pClientElement->RefCount = 1; + pClientElement->LockNumber = CLIENT_LOCK; + + // there are no rcvs or snds yet + InitializeListHead(&pClientElement->RcvDgramHead); + InitializeListHead(&pClientElement->ListenHead); + InitializeListHead(&pClientElement->SndDgrams); + InitializeListHead(&pClientElement->ConnectActive); + InitializeListHead(&pClientElement->ConnectHead); +#ifdef VXD + InitializeListHead(&pClientElement->RcvAnyHead); + pClientElement->fDeregistered = FALSE ; +#endif + pClientElement->pIrp = NULL; + + // copy a special value into the verify long so that we can verify + // connection ptrs passed back from the application + pClientElement->Verify = NBT_VERIFY_CLIENT; + + // back link the client block to the Address element. + pClientElement->pAddress = (PVOID)pAddrEle; + + // put the new Client element block on the end of the linked list tied to + // the address element + InsertTailList(&pAddrEle->ClientHead,&pClientElement->Linkage); + + return(pClientElement); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtAddPermanentName( + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ + +Routine Description: + + This routine adds the node permanent name to the local name table. This + is the node's MAC address padded out to 16 bytes with zeros. + +Arguments: + DeviceContext - Adapter to add permanent + pIrp - Irp (optional) to complete after name has been added + + +Return Value: + + status + +--*/ + +{ + NTSTATUS status; + TDI_REQUEST Request; + TA_NETBIOS_ADDRESS Address; + UCHAR pName[NETBIOS_NAME_SIZE]; + USHORT uType; + CTELockHandle OldIrq; + tNAMEADDR *pNameAddr; + tCLIENTELE *pClientEle; + + CTEZeroMemory(pName,NETBIOS_NAME_SIZE); + CTEMemCopy(&pName[10],&pDeviceContext->MacAddress.Address[0],sizeof(tMAC_ADDRESS)); + + // + // be sure the name has not already been added + // + if (pDeviceContext->pPermClient) + { + if (CTEMemEqu(pDeviceContext->pPermClient->pAddress->pNameAddr->Name, + pName, + NETBIOS_NAME_SIZE)) + { + return(STATUS_SUCCESS); + } + else + { + NbtRemovePermanentName(pDeviceContext); + } + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + // + // check if the name is already in the hash table + // + status = FindInHashTable( + pNbtGlobConfig->pLocalHashTbl, + pName, + NbtConfig.pScope, + &pNameAddr); + + if (NT_SUCCESS(status)) + { + // + // create client block and link to addresslist + // pass back the client block address as a handle for future reference + // to the client + // + + pClientEle = NbtAllocateClientBlock((tADDRESSELE *)pNameAddr->pAddressEle); + if (!pClientEle) + return(STATUS_INSUFFICIENT_RESOURCES); + + pNameAddr->pAddressEle->RefCount++; + // + // reset the ip address incase the the address got set to loop back + // by a client releasing and re-openning the permanent name while there + // was no ip address for this node. + // + pNameAddr->IpAddress = pDeviceContext->IpAddress; + + // keep track of which adapter this name is registered against. + pClientEle->pDeviceContext = (PVOID)pDeviceContext; + + // turn on the adapter's bit in the adapter Mask and set the + // re-register flag so we register the name out the new + // adapter. + // + pNameAddr->AdapterMask |= pDeviceContext->AdapterNumber; + + pNameAddr->NameTypeState |= NAMETYPE_QUICK; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: Adding Permanent name to existing name in table %15.15s<%X> \n", + pName,(UCHAR)pName[15])); + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // make up the Request data structure from the IRP info + Request.Handle.AddressHandle = NULL; + + // + // Make it a Quick name so it does not get registered on the net + // + Address.TAAddressCount = 1; + Address.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + Address.Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; + Address.Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE; + + CTEMemCopy(Address.Address[0].Address[0].NetbiosName,pName,NETBIOS_NAME_SIZE); + + status = NbtOpenAddress(&Request, + (PTA_ADDRESS)&Address.Address[0], + pDeviceContext->IpAddress, + NULL, + pDeviceContext, + NULL); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + pClientEle = (tCLIENTELE *)Request.Handle.AddressHandle; + + } + + // + // save the client element so we can remove the permanent name later + // if required + // + if (NT_SUCCESS(status)) + { + pDeviceContext->pPermClient = pClientEle; +#ifdef VXD + // + // 0th element is for perm. name: store it. + // + pDeviceContext->pNameTable[0] = pClientEle; +#endif + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(status); +} + + +//---------------------------------------------------------------------------- +VOID +NbtRemovePermanentName( + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ + +Routine Description: + + This routine remomves the node permanent name to the local name table. + +Arguments: + DeviceContext - Adapter to add permanent + pIrp - Irp (optional) to complete after name has been added + + +Return Value: + + status + +--*/ + +{ + NTSTATUS status; + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq; + tCLIENTELE *pClientEle; + tADDRESSELE *pAddressEle; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pDeviceContext->pPermClient) + { + + // + // We need to free the client and set the perm name ptr to null + // + pClientEle = pDeviceContext->pPermClient; + pDeviceContext->pPermClient = NULL; + +#ifdef VXD + pDeviceContext->pNameTable[0] = NULL; +#endif + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + NbtDereferenceClient(pClientEle); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } +} + +//---------------------------------------------------------------------------- +NTSTATUS +ConvertDottedDecimalToUlong( + IN PUCHAR pInString, + OUT PULONG IpAddress + ) + +/*++ + +Routine Description: + + This routine converts a unicode dotted decimal IP address into + a 4 element array with each element being USHORT. + +Arguments: + + +Return Value: + + NTSTATUS + +--*/ + +{ + USHORT i; + ULONG value; + int iSum =0; + ULONG k = 0; + UCHAR Chr; + UCHAR pArray[4]; + + CTEPagedCode(); + pArray[0] = 0; + + // go through each character in the string, skipping "periods", converting + // to integer by subtracting the value of '0' + // + while ((Chr = *pInString++) && (Chr != ' ') ) + { + if (Chr == '.') + { + // be sure not to overflow a byte. + if (iSum <= 0xFF) + pArray[k] = iSum; + else + return(STATUS_UNSUCCESSFUL); + + // check for too many periods in the address + if (++k > 3) + return STATUS_UNSUCCESSFUL; + + pArray[k] = 0; + iSum = 0; + } + else + { + Chr = Chr - '0'; + + // be sure character is a number 0..9 + if ((Chr < 0) || (Chr > 9)) + return(STATUS_UNSUCCESSFUL); + + iSum = iSum*10 + Chr; + } + } + + // save the last sum in the byte and be sure there are 4 pieces to the + // address + if ((iSum <= 0xFF) && (k == 3)) + pArray[k] = iSum; + else + return(STATUS_UNSUCCESSFUL); + + // now convert to a ULONG, in network order... + value = 0; + + // go through the array of bytes and concatenate into a ULONG + for (i=0; i < 4; i++ ) + { + value = (value << 8) + pArray[i]; + } + *IpAddress = value; + + return(STATUS_SUCCESS); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtInitQ( + PLIST_ENTRY pListHead, + LONG iSizeBuffer, + LONG iNumBuffers + ) + +/*++ + +Routine Description: + + This routine allocates memory blocks for doubly linked lists and links + them to a list. + +Arguments: + ppListHead - a ptr to a ptr to the list head to add buffer to + iSizeBuffer - size of the buffer to add to the list head + iNumBuffers - the number of buffers to add to the queue + +Return Value: + + none + +--*/ + +{ + int i; + PLIST_ENTRY pBuffer; + + // NOTE THAT THIS ASSUMES THAT THE LINKAGE PTRS FOR EACH BLOCK ARE AT + // THE START OF THE BLOCK - so it will not work correctly if + // the various types in types.h change to move "Linkage" to a position + // other than at the start of each structure to be chained + + for (i=0;i<iNumBuffers ;i++ ) + { + pBuffer =(PLIST_ENTRY)CTEAllocInitMem(iSizeBuffer); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + else + { + InsertHeadList(pListHead,pBuffer); + } + } + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtInitTrackerQ( + PLIST_ENTRY pListHead, + LONG iNumBuffers + ) + +/*++ + +Routine Description: + + This routine allocates memory blocks for doubly linked lists and links + them to a list. + +Arguments: + ppListHead - a ptr to a ptr to the list head to add buffer to + iNumBuffers - the number of buffers to add to the queue + +Return Value: + + none + +--*/ + +{ + int i; + tDGRAM_SEND_TRACKING *pTracker; + + for (i=0;i<iNumBuffers ;i++ ) + { + pTracker = NbtAllocTracker(); + if (!pTracker) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + else + { + NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER]++; + InsertHeadList(pListHead,&pTracker->Linkage); + } + } + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +tDGRAM_SEND_TRACKING * +NbtAllocTracker( + IN VOID + ) +/*++ + +Routine Description: + + This routine allocates memory for several of the structures attached to + the dgram tracking list, so that this memory does not need to be + allocated and freed for each send. + +Arguments: + + ppListHead - a ptr to a ptr to the list head + +Return Value: + + none + +--*/ + +{ + PLIST_ENTRY pEntry; + tDGRAM_SEND_TRACKING *pTracker; + PTRANSPORT_ADDRESS pTransportAddr; + ULONG TotalSize; + + // + // allocate all the tracker memory as one block and then divy it up later + // into the various buffers + // + TotalSize = sizeof(tDGRAM_SEND_TRACKING) + + sizeof(TDI_CONNECTION_INFORMATION) + + sizeof(TRANSPORT_ADDRESS) -1 + + NbtConfig.SizeTransportAddress; + + pTracker = (tDGRAM_SEND_TRACKING *)CTEAllocInitMem(TotalSize); + + if (pTracker) + { + CTEZeroMemory(pTracker,TotalSize); + + pTracker->pSendInfo = (PTDI_CONNECTION_INFORMATION)((PUCHAR)pTracker + + sizeof(tDGRAM_SEND_TRACKING)); + + // fill in the connection information - especially the Remote address + // structure + + pTracker->pSendInfo->RemoteAddressLength = sizeof(TRANSPORT_ADDRESS) -1 + + pNbtGlobConfig->SizeTransportAddress; + + // allocate the remote address structure + pTransportAddr = (PTRANSPORT_ADDRESS)((PUCHAR)pTracker->pSendInfo + + sizeof(TDI_CONNECTION_INFORMATION)); + + // fill in the remote address + pTransportAddr->TAAddressCount = 1; + pTransportAddr->Address[0].AddressLength = NbtConfig.SizeTransportAddress; + pTransportAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; + ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->sin_port = NBT_NAMESERVICE_UDP_PORT; + ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->in_addr = 0L; + + // put a ptr to this address structure into the sendinfo structure + pTracker->pSendInfo->RemoteAddress = (PVOID)pTransportAddr; + + // Empty the list of trackers linked to this one + InitializeListHead(&pTracker->TrackerList); + + } + + return(pTracker); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtGetBuffer( + PLIST_ENTRY pListHead, + PLIST_ENTRY *ppListEntry, + enum eBUFFER_TYPES eBuffType) + +/*++ + +Routine Description: + + This routine tries to get a memory block and if it fails it allocates + another set of buffers. + +Arguments: + ppListHead - a ptr to a ptr to the list head to add buffer to + iSizeBuffer - size of the buffer to add to the list head + iNumBuffers - the number of buffers to add to the queue + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + + if (IsListEmpty(pListHead)) + { + // check if we are allowed to allocate more memory blocks + if (NbtConfig.iCurrentNumBuff[eBuffType] >= + pNbtGlobConfig->iMaxNumBuff[eBuffType] ) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // no memory blocks, so allocate another one + status = NbtInitQ( + pListHead, + pNbtGlobConfig->iBufferSize[eBuffType], + 1); + if (!NT_SUCCESS(status)) + { + return(status); + } + + NbtConfig.iCurrentNumBuff[eBuffType]++; + + *ppListEntry = RemoveHeadList(pListHead); + } + else + *ppListEntry = RemoveHeadList(pListHead); + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +GetNetBiosNameFromTransportAddress( + IN PTA_NETBIOS_ADDRESS pTransAddr, + OUT PCHAR *pName, + OUT PULONG pNameLen, + OUT PULONG pNameType + ) +/*++ + +Routine Description + + This routine handles deciphering the weird transport address syntax + to retrieve the netbios name out of that address. + +Arguments: + + +Return Values: + + NTSTATUS - status of the request + +--*/ +{ + + ULONG Diff; + + // + // name could be longer than 16 bytes (dns name), but make sure it's at + // least 16 bytes (sizeof(TDI_ADDRESS_NETBIOS = 16 + sizeof(USHORT)) + // + if (pTransAddr->Address[0].AddressLength < sizeof(TDI_ADDRESS_NETBIOS)) + { + return(STATUS_INVALID_PARAMETER); + } + + *pName = (PCHAR)pTransAddr->Address[0].Address[0].NetbiosName; + + Diff = sizeof(TDI_ADDRESS_NETBIOS) - NETBIOS_NAME_SIZE; + + *pNameLen = (ULONG)pTransAddr->Address[0].AddressLength - Diff; + + *pNameType = pTransAddr->Address[0].Address[0].NetbiosNameType; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +ConvertToAscii( + IN PCHAR pNameHdr, + IN LONG NumBytes, + OUT PCHAR pName, + OUT PCHAR *pScope, + OUT PULONG pNameSize + ) +/*++ + +Routine Description: + + This routine converts half ascii to normal ascii and then appends the scope + onto the end of the name to make a full name again. + +Arguments: + NumBytes - the total number of bytes in the message - may include + more than just the name itself + +Return Value: + + NTSTATUS - success or not + This routine returns the length of the name in half ascii format including + the null at the end, but NOT including the length byte at the beginning. + For a non-scoped name it returns 33. + + It converts the name to ascii and puts 16 bytes into pName, then it returns + pScope as the Ptr to the scope that is still in pNameHdr. + + +--*/ +{ + LONG i; + int iIndex; + LONG lValue; + ULONG UNALIGNED *pHdr; + PUCHAR pTmp; + + // the first bytes should be 32 (0x20) - the length of the half + // ascii name + // + if ((*pNameHdr == NETBIOS_NAME_SIZE*2) && (NumBytes > NETBIOS_NAME_SIZE*2)) + { + pHdr = (ULONG UNALIGNED *)++pNameHdr; // to increment past the length byte + + // the Half AScii portion of the netbios name is always 32 bytes long + for (i=0; i < NETBIOS_NAME_SIZE*2 ;i +=4 ) + { + lValue = *pHdr - 0x41414141; // four A's + pHdr++; + lValue = ((lValue & 0x0F000000) >> 16) + + ((lValue & 0x0F0000) >> 4) + + ((lValue & 0x0F00) >> 8) + + ((lValue & 0x0F) << 4); + *(PUSHORT)pName = (USHORT)lValue; + ((PUSHORT)pName)++; + + } + + // verify that the name has the correct format...i.e. it is one or more + // labels each starting with the length byte for the label and the whole + // thing terminated with a 0 byte (for the root node name length of zero). + // count the length of the scope. pName should be pointing at the first + // length byte of the scope now, and pHdr should be pointing to the + // first byte after the half ascii name. + iIndex = 0; + + // + // Store the address of the start of the scope in the netbios name + // (if one is present). If there is no scope present in the name, then + // pHdr must be pointing to the NULL byte. + // + pTmp = (PUCHAR)pHdr; + + // cannot used structured exception handling at raised IRQl and expect + // it to work, since the mapper will bugcheck + while (iIndex < (NumBytes - NETBIOS_NAME_SIZE*2-1) && *pTmp) + { + pTmp++; + iIndex++; + } + iIndex++; // to include the null at the end. + + // check for an overflow on the maximum length of 256 bytes + if (iIndex > (MAX_DNS_NAME_LENGTH-NETBIOS_NAME_SIZE-1)) + { + // the name is too long..probably badly formed + return(STATUS_UNSUCCESSFUL); + } + + *pScope = (PUCHAR)pHdr; + + *pNameSize = NETBIOS_NAME_SIZE*2 + iIndex; + + return(STATUS_SUCCESS); + } + else + { + return(STATUS_UNSUCCESSFUL); + } +} + + +//---------------------------------------------------------------------------- +PCHAR +ConvertToHalfAscii( + OUT PCHAR pDest, + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG uScopeSize + ) +/*++ + +Routine Description: + + This routine converts ascii to half ascii and appends the scope on the + end + +Arguments: + + +Return Value: + + the address of the next byte in the destination after the the name + has been converted and copied + +--*/ +{ + LONG i; + + // the first byte of the name is the length field = 2*16 + *pDest++ = ((UCHAR)NETBIOS_NAME_SIZE << 1); + + // step through name converting ascii to half ascii, for 32 times + for (i=0; i < NETBIOS_NAME_SIZE ;i++ ) + { + *pDest++ = ((UCHAR)*pName >> 4) + 'A'; + *pDest++ = (*pName++ & 0x0F) + 'A'; + } + // + // put the length of the scope into the next byte followed by the + // scope itself. For 1 length scopes (the normal case), writing + // the zero(for the end of the scope is all that is needed). + // + if (uScopeSize > 1) + { + CTEMemCopy(pDest,pScope,uScopeSize); + + pDest = pDest + uScopeSize; + } + else + { + *pDest++ = 0; + } + + + // return the address of the next byte of the destination + return(pDest); +} + + +//---------------------------------------------------------------------------- +ULONG +Nbt_inet_addr( + IN PCHAR pName + ) +/*++ + +Routine Description: + + This routine converts ascii ipaddr (11.101.4.25) into a ULONG. This is + based on the inet_addr code in winsock + +Arguments: + pName - the string containing the ipaddress + +Return Value: + + the ipaddress as a ULONG if it's a valid ipaddress. Otherwise, 0. + +--*/ +{ + + PCHAR pStr; + int i; + int len, fieldLen; + int fieldsDone; + ULONG IpAddress; + BYTE ByteVal; + PCHAR pIpPtr; + BOOLEAN fDotFound; + BOOLEAN fieldOk; + + + pStr = pName; + len = 0; + pIpPtr = (PCHAR)&IpAddress; + pIpPtr += 3; // so that we store in network order + fieldsDone=0; + + // + // the 11.101.4.25 format can be atmost 15 chars, and pName is guaranteed + // to be at least 16 chars long (how convenient!!). Convert the string to + // a ULONG. + // + while(len < NETBIOS_NAME_SIZE) + { + fieldLen=0; + fieldOk = FALSE; + ByteVal = 0; + fDotFound = FALSE; + + // + // This loop traverses each of the four fields (max len of each + // field is 3, plus 1 for the '.' + // + while (fieldLen < 4) + { + if (*pStr >='0' && *pStr <='9') + { + ByteVal = (ByteVal*10) + (*pStr - '0'); + fieldOk = TRUE; + } + + else if (*pStr == '.' || *pStr == ' ' || *pStr == '\0') + { + *pIpPtr = ByteVal; + pIpPtr--; + fieldsDone++; + + if (*pStr == '.') + fDotFound = TRUE; + + // if we got a space or 0, assume it's the 4th field + if (*pStr == ' ' || *pStr == '\0') + { + break; + } + } + + // unacceptable char: can't be ipaddr + else + { + return(0); + } + + pStr++; + len++; + fieldLen++; + + // if we found the dot, we are done with this field: go to the next one + if (fDotFound) + break; + } + + // this field wasn't ok (e.g. "11.101..4" or "11.101.4." etc.) + if (!fieldOk) + { + return(0); + } + + // if we are done with all 4 fields, we are done with the outer loop too + if ( fieldsDone == 4) + break; + + if (!fDotFound) + { + return(0); + } + } + + // + // make sure the remaining chars are spaces or 0's (i.e. don't allow + // 11.101.4.25xyz to succeed) + // + for (i=len; i<NETBIOS_NAME_SIZE; i++, pStr++) + { + if (*pStr != ' ' && *pStr != '\0') + { + return(0); + } + } + + return( IpAddress ); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +GetTracker( + OUT tDGRAM_SEND_TRACKING **ppTracker) +/*++ +Routine Description: + + This Routine gets a Tracker data structure to track sending a datagram + or session packet. + +Arguments: + +Return Value: + + BOOLEAN - TRUE if IRQL is too high + +--*/ + +{ + PLIST_ENTRY pListEntry; + NTSTATUS status; + CTELockHandle OldIrq; + tDGRAM_SEND_TRACKING *pTracker; + + CTESpinLock(&NbtConfig,OldIrq); + if (!IsListEmpty(&NbtConfig.DgramTrackerFreeQ)) + { + pListEntry = RemoveHeadList(&NbtConfig.DgramTrackerFreeQ); + CTESpinFree(&NbtConfig,OldIrq); + + pTracker = CONTAINING_RECORD(pListEntry,tDGRAM_SEND_TRACKING,Linkage); + + // + // Do these initializations here - there was a problem where this tracker's transactionId + // would be set to 0x1A1A (indirectly thru unions), and used as such since the check in CreatePdu + // is for a 0 TransactionId field in the tracker returned from this function. + // +#if DBG + pTracker->Verify = NBT_VERIFY_TRACKER; + pTracker->pClientIrp = (PVOID)0x1A1A1A1A; + pTracker->pConnEle = (PVOID)0x1A1A1A1A; + pTracker->SendBuffer.HdrLength = 0x1A1A1A1A; + pTracker->SendBuffer.pDgramHdr = (PVOID)0x1A1A1A1A; + pTracker->SendBuffer.Length = 0x1A1A1A1A; + pTracker->SendBuffer.pBuffer = (PVOID)0x1A1A1A1A; + pTracker->pDeviceContext = (PVOID)0x1A1A1A1A; + pTracker->pTimer = (PVOID)0x1A1A1A1A; + pTracker->RefCount = 0x1A1A1A1A; + pTracker->pDestName = (PVOID)0x1A1A1A1A; + pTracker->pNameAddr = (PVOID)0x1A1A1A1A; +#ifdef VXD + pTracker->pchDomainName = (PVOID)0x1A1A1A1A; +#endif + pTracker->pTimeout = (PVOID)0x1A1A1A1A; + pTracker->SrcIpAddress = 0x1A1A1A1A; + pTracker->CompletionRoutine = (PVOID)0x1A1A1A1A; + pTracker->Flags = 0x1A1A; +#endif + // clear any list of trackers Q'd to this tracker + InitializeListHead(&pTracker->TrackerList); + pTracker->Connect.pTimer = NULL; + pTracker->pClientIrp = NULL; + pTracker->TransactionId = 0; + pTracker->Flags = 0; + + InitializeListHead(&pTracker->Linkage); + status = STATUS_SUCCESS; + + } + else + { + + if (NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER] >= + NbtConfig.iMaxNumBuff[eNBT_DGRAM_TRACKER]) + { + CTESpinFree(&NbtConfig,OldIrq); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pTracker = NbtAllocTracker(); + + CTESpinFree(&NbtConfig,OldIrq); + if (!pTracker) + { + KdPrint(("GetTracker: No more trackers available, failing request!\n")) ; + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { +#if DBG + pTracker->Verify = NBT_VERIFY_TRACKER; + pTracker->pClientIrp = (PVOID)0x1A1A1A1A; + pTracker->pConnEle = (PVOID)0x1A1A1A1A; + pTracker->SendBuffer.HdrLength = 0x1A1A1A1A; + pTracker->SendBuffer.pDgramHdr = (PVOID)0x1A1A1A1A; + pTracker->SendBuffer.Length = 0x1A1A1A1A; + pTracker->SendBuffer.pBuffer = (PVOID)0x1A1A1A1A; + pTracker->pDeviceContext = (PVOID)0x1A1A1A1A; + pTracker->pTimer = (PVOID)0x1A1A1A1A; + pTracker->RefCount = 0x1A1A1A1A; + pTracker->pDestName = (PVOID)0x1A1A1A1A; + pTracker->pNameAddr = (PVOID)0x1A1A1A1A; +#ifdef VXD + pTracker->pchDomainName = (PVOID)0x1A1A1A1A; +#endif + pTracker->pTimeout = (PVOID)0x1A1A1A1A; + pTracker->SrcIpAddress = 0x1A1A1A1A; + pTracker->CompletionRoutine = (PVOID)0x1A1A1A1A; + pTracker->Flags = 0x1A1A; +#endif + NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER]++; + pTracker->Connect.pTimer = NULL ; + InitializeListHead(&pTracker->Linkage); + status = STATUS_SUCCESS; + } + + + } +//#if DBG + // keep tracker on a used list for debug + if (NT_SUCCESS(status)) + { + //ADD_TO_LIST(&UsedTrackers,&pTracker->DebugLinkage); + } +//#endif + + *ppTracker = pTracker; + return(status); +} + +//---------------------------------------------------------------------------- +#ifndef VXD +NTSTATUS +GetIrp( + OUT PIRP *ppIrp) +/*++ +Routine Description: + + This Routine gets an Irp from the free queue or it allocates another one + the queue is empty. + +Arguments: + +Return Value: + + BOOLEAN - TRUE if IRQL is too high + +--*/ + +{ + PLIST_ENTRY pListEntry; + NTSTATUS status; + CTELockHandle OldIrq; + tDEVICECONTEXT *pDeviceContext; + PIRP pIrp; + + // get an Irp from the list + CTESpinLock(&NbtConfig,OldIrq); + status = STATUS_SUCCESS; + if (!IsListEmpty(&NbtConfig.IrpFreeList)) + { + pListEntry = RemoveHeadList(&NbtConfig.IrpFreeList); + *ppIrp = CONTAINING_RECORD(pListEntry,IRP,Tail.Overlay.ListEntry); + } + else + { + // check if we are allowed to allocate more memory blocks + if (NbtConfig.iCurrentNumBuff[eNBT_FREE_IRPS] >= + NbtConfig.iMaxNumBuff[eNBT_FREE_IRPS] ) + { + + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + + // use the first device in the list of adapter since we need to know + // the stack size of the Irp we are creating. It is possible to + // get here before we have put the first device on the context Q, + // especially for proxy operation, so check if the list is empty + // or not first. + // + if (IsListEmpty(&NbtConfig.DeviceContexts)) + { + status = STATUS_UNSUCCESSFUL; + } + else + { + pListEntry = NbtConfig.DeviceContexts.Flink; + pDeviceContext = CONTAINING_RECORD(pListEntry,tDEVICECONTEXT,Linkage); + + pIrp = NTAllocateNbtIrp(&pDeviceContext->DeviceObject); + + if (!pIrp) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + *ppIrp = pIrp; + + // + // Irp allocated - Increment the # + // + NbtConfig.iCurrentNumBuff[eNBT_FREE_IRPS]++; + } + } + + } + + } + + CTESpinFree(&NbtConfig,OldIrq); +//#if DBG + if (status == STATUS_SUCCESS) + { + ADD_TO_LIST(&UsedIrps,&(*ppIrp)->ThreadListEntry); + } +//#endif + + return(status); +} +#endif //!VXD + +//---------------------------------------------------------------------------- +ULONG +CountLocalNames(IN tNBTCONFIG *pNbtConfig + ) +/*++ +Routine Description: + + This Routine counts the number of names in the local name table. + +Arguments: + +Return Value: + + ULONG - the number of names + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + ULONG Count; + tNAMEADDR *pNameAddr; + LONG i; + + Count = 0; + + for (i=0;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + // + // don't want unresolved names, or the broadcast name + // + if (!(pNameAddr->NameTypeState & STATE_RESOLVING) && + (pNameAddr->Name[0] != '*')) + { + Count++; + } + } + } + + return(Count); +} +//---------------------------------------------------------------------------- +ULONG +CountUpperConnections( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ +Routine Description: + + This Routine counts the number of upper connections that have been created + in preparation for creating an equivalent number of lower connections. + + +Arguments: + +Return Value: + + ULONG - the number of names + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PLIST_ENTRY pClientHead; + PLIST_ENTRY pConnHead; + PLIST_ENTRY pClientEntry; + PLIST_ENTRY pConnEntry; + ULONG CountConnections = 0; + tADDRESSELE *pAddressEle; + tCLIENTELE *pClient; + + CTEPagedCode(); + if (!IsListEmpty(&NbtConfig.AddressHead)) + { + // get the list of addresses for this device + pHead = &NbtConfig.AddressHead; + pEntry = pHead->Flink; + + while (pEntry != pHead) + { + pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + pClientHead = &pAddressEle->ClientHead; + pClientEntry = pClientHead->Flink; + while (pClientEntry != pClientHead) + { + pClient = CONTAINING_RECORD(pClientEntry,tCLIENTELE,Linkage); + pConnHead = &pClient->ConnectHead; + pConnEntry = pConnHead->Flink; + while (pConnEntry != pConnHead) + { + CountConnections++; + pConnEntry = pConnEntry->Flink; + } + pClientEntry = pClientEntry->Flink; + } + pEntry = pEntry->Flink; + } + + } + + return(CountConnections); +} +//---------------------------------------------------------------------------- +NTSTATUS +DisableInboundConnections( + IN tDEVICECONTEXT *pDeviceContext, + OUT PLIST_ENTRY pLowerConnFreeHead + ) +/*++ + +Routine Description: + + This routine checks the devicecontext for open connections and sets + the Lower Connection free list to empty. + +Arguments: + +Return Value: + + none + +--*/ + +{ + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + PLIST_ENTRY pLowerHead; + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + CTESpinLock(pDeviceContext,OldIrq); + { + PLIST_ENTRY pLowerHead; + + if (!IsListEmpty(&pDeviceContext->LowerConnFreeHead)) + { + pLowerHead = &pDeviceContext->LowerConnFreeHead; + pLowerConnFreeHead->Flink = pLowerHead->Flink; + pLowerConnFreeHead->Blink = pLowerHead->Blink; + + // hook the list head and tail to the new head + pLowerHead->Flink->Blink = pLowerConnFreeHead; + pLowerHead->Blink->Flink = pLowerConnFreeHead; + + InitializeListHead(&pDeviceContext->LowerConnFreeHead); + } + else + { + InitializeListHead(pLowerConnFreeHead); + } + } + + MarkForCloseLowerConnections(pDeviceContext,OldIrq1,OldIrq); + + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +NTSTATUS +EnableInboundConnections( + IN tDEVICECONTEXT *pDeviceContext, + IN PLIST_ENTRY pLowerConnFreeHead + ) +/*++ + +Routine Description: + + This routine checks each device context to see if there are any open + connections, and returns SUCCESS if there are. + +Arguments: + +Return Value: + + none + +--*/ + +{ + CTELockHandle OldIrq; + + CTESpinLock(pDeviceContext,OldIrq); + pDeviceContext->LowerConnFreeHead.Flink = pLowerConnFreeHead->Flink; + pDeviceContext->LowerConnFreeHead.Blink = pLowerConnFreeHead->Blink; + CTESpinFree(pDeviceContext,OldIrq); + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +ULONG +CloseLowerConnections( + IN PLIST_ENTRY pLowerConnFreeHead + ) +/*++ + +Routine Description: + + This routine checks each device context to see if there are any open + connections, and returns SUCCESS if there are. + +Arguments: + +Return Value: + + none + +--*/ + +{ + tLOWERCONNECTION *pLowerConn; + CTELockHandle OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PLIST_ENTRY pNextEntry; + ULONG Count=0; + + CTEPagedCode(); + + pHead = pLowerConnFreeHead; + + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + pNextEntry = pEntry->Flink; + RemoveEntryList(pEntry); + pEntry = pNextEntry; + Count++; + + // + // close the lower connection with the transport + // + //DereferenceObject((PVOID *)pLowerConn->pFileObject); + //TdiCloseConnection(pLowerConn); + + NbtDereferenceLowerConnection(pLowerConn); + + } + return(Count); +} + +//---------------------------------------------------------------------------- +VOID +MarkForCloseLowerConnections( + IN tDEVICECONTEXT *pDeviceContext, + IN CTELockHandle OldIrqJoint, + IN CTELockHandle OldIrqDevice + ) +/*++ + +Routine Description: + + This routine checks each device context to see if there are any open + connections, and returns SUCCESS if there are. + +Arguments: + +Return Value: + + none + +--*/ + +{ + tLOWERCONNECTION *pLowerConn; + NTSTATUS status; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + ULONG Count=0; + tLOWERCONNECTION **pList; + + pHead = &pDeviceContext->LowerConnection; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + pLowerConn->DestroyConnection = TRUE; + + pEntry = pEntry->Flink; + Count++; + } + + // ****************************************** + // NOTE: The code after this point can probably be deleted + // because TCP should disconnect all open connections when it + // is notified of the address change. Just use this code for test. + // + if (Count) + { + pList = NbtAllocMem(Count * sizeof(tLOWERCONNECTION *),NBT_TAG('T')); + if (pList) + { + // + // save the lower connection pointers in a list + // until they can be deleted. + // + pHead = &pDeviceContext->LowerConnection; + pEntry = pHead->Flink; + Count = 0; + while (pEntry != pHead) + { + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); +// if (pLowerConn->State == NBT_SESSION_UP) + { + *pList = pLowerConn; + pList++; + InterlockedIncrement(&pLowerConn->RefCount); + pEntry = pEntry->Flink; + Count++; + } + } + + CTESpinFree(pDeviceContext,OldIrqDevice); + CTESpinFree(&NbtConfig.JointLock,OldIrqJoint); + + // + // now go through the list of Lower connections to see which are + // still up and issue disconnects on them. + // + while (Count--) + { + pLowerConn = (tLOWERCONNECTION *)*(--pList); + CTESpinLock(pLowerConn,OldIrq); + + // + // In the connecting state the TCP connection is being + // setup. + // + if ((pLowerConn->State == NBT_SESSION_UP) || + (pLowerConn->State == NBT_CONNECTING)) + { + + tCLIENTELE *pClientEle; + tCONNECTELE *pConnEle; + + if (pLowerConn->State == NBT_CONNECTING) + { + // CleanupAfterDisconnect expects this ref count + // to be 2, meaning that it got connected, so increment + // here + pLowerConn->RefCount++; + } + + pClientEle = pLowerConn->pUpperConnection->pClientEle; + pLowerConn->State = NBT_DISCONNECTING; + pConnEle = pLowerConn->pUpperConnection; + pConnEle->state = NBT_DISCONNECTED; + pConnEle->pLowerConnId = NULL; + pLowerConn->pUpperConnection = NULL; + SetStateProc(pLowerConn,RejectAnyData); + + CTESpinFree(pLowerConn,OldIrq); + + DereferenceIfNotInRcvHandler(pConnEle,pLowerConn); + + if ( pClientEle->evDisconnect ) + { + status = (*pClientEle->evDisconnect)(pClientEle->DiscEvContext, + pConnEle->ConnectContext, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT); + } + + // this should kill of the connection when the irp + // completes by calling CleanupAfterDisconnect. + // +#ifndef VXD + status = DisconnectLower(pLowerConn, + NBT_SESSION_UP, + TDI_DISCONNECT_ABORT, + &DefaultDisconnectTimeout, + TRUE); +#else + // Vxd can't wait for the disconnect + status = DisconnectLower(pLowerConn, + NBT_SESSION_UP, + TDI_DISCONNECT_ABORT, + &DefaultDisconnectTimeout, + FALSE); + +#endif + } + else + if (pLowerConn->State == NBT_IDLE) + { + tCONNECTELE *pConnEle; + + CTESpinFree(pLowerConn,OldIrq); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pConnEle = pLowerConn->pUpperConnection; + + if (pConnEle) + { + CTESpinLock(pConnEle,OldIrq2); + // + // this makes a best effort to find the connection and + // and cancel it. Anything not cancelled will eventually + // fail with a bad ret code from the transport which is + // ok too. + // + status = CleanupConnectingState(pConnEle,pDeviceContext, + &OldIrq2,&OldIrq); + CTESpinFree(pConnEle,OldIrq2); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + else + CTESpinFree(pLowerConn,OldIrq); + + // + // remove the reference added above when the list was + // created. + // + NbtDereferenceLowerConnection(pLowerConn); + } + + CTEMemFree(pList); + return; + + } + } + + CTESpinFree(pDeviceContext,OldIrqDevice); + CTESpinFree(&NbtConfig.JointLock,OldIrqJoint); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NbtInitConnQ( + PLIST_ENTRY pListHead, + int iSizeBuffer, + int iNumConnections, + tDEVICECONTEXT *pDeviceContext) + +/*++ + +Routine Description: + + This routine allocates memory blocks for connections to the transport + provider and then sets up connections with the provider. + +Arguments: + ppListHead - a ptr to a ptr to the list head to add buffer to + iSizeBuffer - size of the buffer to add to the list head + iNumConnections - the number of buffers to add to the queue + pDeviceContext - ptr to the devicecontext + +Return Value: + + status + +--*/ + +{ + USHORT i; + tLOWERCONNECTION *pLowerConn; + NTSTATUS status; + + CTEPagedCode(); + for (i=0;i < iNumConnections ;i++ ) + { + pLowerConn =(tLOWERCONNECTION *)NbtAllocMem((ULONG)sizeof(tLOWERCONNECTION),NBT_TAG('U')); + if (!pLowerConn) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + else + { + CTEZeroMemory((PVOID)pLowerConn,sizeof(tLOWERCONNECTION)); + + // open a connection with the transport provider + status = NbtTdiOpenConnection( + pLowerConn, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + KdPrint( ("Nbt:OpenConnection failed! Status:%X,", status)); + return(status); + } + + // associate the connection with the session address object + status = NbtTdiAssociateConnection( + pLowerConn->pFileObject, +#ifndef VXD + pDeviceContext->hSession); +#else + pDeviceContext->pSessionFileObject); +#endif + + + if (!NT_SUCCESS(status)) + { + KdPrint( ("Nbt:Associate failed! Status:%X,", status)); + return(status); + } + + // link the lower connection to the device context so we can free + // the lower connections back to their correct device free Q's. + pLowerConn->pDeviceContext = (PVOID)pDeviceContext; + InsertHeadList(pListHead,&pLowerConn->Linkage); + } + } + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +NTSTATUS +ReRegisterLocalNames( + ) + +/*++ + +Routine Description: + + This routine re-registers names with WINS when DHCP changes the IP + address. + +Arguments: + + pDeviceContext - ptr to the devicecontext + +Return Value: + + status + +--*/ + +{ + NTSTATUS status; + tTIMERQENTRY *pTimerEntry; + CTELockHandle OldIrq; + LONG i; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tNAMEADDR *pNameAddr; + + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pTimerEntry = NbtConfig.pRefreshTimer; + + if (pTimerEntry) + { + status = StopTimer(pTimerEntry,NULL,NULL); + NbtConfig.pRefreshTimer = NULL; + } + + // + // restart timer and use + // the initial refresh rate until we can contact the name server + // + NbtConfig.MinimumTtl = NbtConfig.InitialRefreshTimeout; + NbtConfig.RefreshDivisor = REFRESH_DIVISOR; + + // + // set this to 3 so that refreshBegin will refresh to the primary and + // then switch to backup on the next refresh interval if it doesn't + // get through. + // + NbtConfig.sTimeoutCount = 3; + + status = StartTimer( + NbtConfig.InitialRefreshTimeout/NbtConfig.RefreshDivisor, + NULL, // context value + NULL, // context2 value + RefreshTimeout, + NULL, + NULL, + 0, + &pTimerEntry); + + if ( !NT_SUCCESS( status ) ) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return status ; + } + + NbtConfig.pRefreshTimer = pTimerEntry; + + for (i=0 ;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + // + // set so that nextrefresh finds the name and does a refresh + // + if (!(pNameAddr->NameTypeState & STATE_RESOLVED) || + (pNameAddr->Name[0] == '*') || + (pNameAddr->NameTypeState & NAMETYPE_QUICK)) + { + continue; + } + else + { + pNameAddr->RefreshMask = 0; + pNameAddr->Ttl = NbtConfig.InitialRefreshTimeout; + } + + } + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // start a refresh if there isn't one currently going on + // Note that there is a time window here that if the refresh is + // currently going on then, some names will not get refreshed with + // the new IpAddress right away, but have to wait to the next + // refresh interval. It seems that this is a rather unlikely + // scenario and given the low probability of DHCP changing the + // address it makes even less sense to add the code to handle that + // case. + // + RefreshTimeout(NULL,NULL,NbtConfig.pRefreshTimer); + + return(STATUS_SUCCESS); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +LockedStopTimer( + tTIMERQENTRY **ppTimer) + +/*++ + +Routine Description: + + This routine stops the refresh timer if it is going. + +Arguments: + + pDeviceContext - ptr to the devicecontext + +Return Value: + + status + +--*/ + +{ + tTIMERQENTRY TimerEntry; + CTELockHandle OldIrq; + + // + // Check if there is a refresh timer since this node could be changing + // from a Bnode to an Hnode, where the Bnode does not have a refresh timer + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (*ppTimer) + { + StopTimer(*ppTimer,NULL,NULL); + *ppTimer = NULL; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(STATUS_SUCCESS); +} diff --git a/private/ntos/nbt/nbt/parse.c b/private/ntos/nbt/nbt/parse.c new file mode 100644 index 000000000..ae6a33839 --- /dev/null +++ b/private/ntos/nbt/nbt/parse.c @@ -0,0 +1,4140 @@ +/*++ + +Copyright (c) 1991-1993 Microsoft Corporation + +Module Name: + + parse.c + +Abstract: + + This source contains the functions that parse the lmhosts file. + +Author: + + Jim Stewart May 2, 1993 + +Revision History: + + +--*/ + +#include "types.h" +#ifndef VXD +#include <nbtioctl.h> +#endif +#include "nbtprocs.h" +#include "hosts.h" +#include <ctype.h> +#include <string.h> + +#ifdef VXD +extern BOOL fInInit; +extern BOOLEAN CachePrimed; +#endif + +// +// Returns 0 if equal, 1 if not equal. Used to avoid using c-runtime +// +#define strncmp( pch1, pch2, length ) \ + (!CTEMemEqu( pch1, pch2, length ) ) + + +// +// Private Definitions +// +// As an lmhosts file is parsed, a #INCLUDE directive is interpreted +// according to the INCLUDE_STATE at that instance. This state is +// determined by the #BEGIN_ALTERNATE and #END_ALTERNATE directives. +// +// +typedef enum _INCLUDE_STATE +{ + + MustInclude = 0, // shouldn't fail + TryToInclude, // in alternate block + SkipInclude // satisfied alternate + // block +} INCLUDE_STATE; + + +// +// LmpGetTokens() parses a line and returns the tokens in the following +// order: +// +typedef enum _TOKEN_ORDER_ +{ + + IpAddress = 0, // first token + NbName, // 2nd token + GroupName, // 3rd or 4th token + NotUsed, // #PRE, if any + NotUsed2, // #NOFNR, if any + MaxTokens // this must be last + +} TOKEN_ORDER; + + +// +// As each line in an lmhosts file is parsed, it is classified into one of +// the categories enumerated below. +// +// However, Preload is a special member of the enum. +// +// +typedef enum _TYPE_OF_LINE +{ + + Comment = 0x0000, // comment line + Ordinary = 0x0001, // ip_addr NetBIOS name + Domain = 0x0002, // ... #DOM:name + Include = 0x0003, // #INCLUDE file + BeginAlternate = 0x0004, // #BEGIN_ALTERNATE + EndAlternate = 0x0005, // #END_ALTERNATE + ErrorLine = 0x0006, // Error in line + + NoFNR = 0x4000, // ... #NOFNR + Preload = 0x8000 // ... #PRE + +} TYPE_OF_LINE; + + +// +// In an lmhosts file, the following are recognized as keywords: +// +// #BEGIN_ALTERNATE #END_ALTERNATE #PRE +// #DOM: #INCLUDE +// +// Information about each keyword is kept in a KEYWORD structure. +// +// +typedef struct _KEYWORD +{ // reserved keyword + + char *k_string; // NULL terminated + size_t k_strlen; // length of token + TYPE_OF_LINE k_type; // type of line + int k_noperands; // max operands on line + +} KEYWORD, *PKEYWORD; + + +typedef struct _LINE_CHARACTERISTICS_ +{ + + int l_category:4; // enum _TYPE_OF_LINE + int l_preload:1; // marked with #PRE ? + unsigned int l_nofnr:1; // marked with #NOFNR + +} LINE_CHARACTERISTICS, *PLINE_CHARACTERISTICS; + + + + + +// +// Local Variables +// +// +// In an lmhosts file, the token '#' in any column usually denotes that +// the rest of the line is to be ignored. However, a '#' may also be the +// first character of a keyword. +// +// Keywords are divided into two groups: +// +// 1. decorations that must either be the 3rd or 4th token of a line, +// 2. directives that must begin in column 0, +// +// +KEYWORD Decoration[] = +{ + + DOMAIN_TOKEN, sizeof(DOMAIN_TOKEN) - 1, Domain, 5, + PRELOAD_TOKEN, sizeof(PRELOAD_TOKEN) - 1, Preload, 5, + NOFNR_TOKEN, sizeof(NOFNR_TOKEN) -1, NoFNR, 5, + + NULL, 0 // must be last +}; + + +KEYWORD Directive[] = +{ + + INCLUDE_TOKEN, sizeof(INCLUDE_TOKEN) - 1, Include, 2, + BEG_ALT_TOKEN, sizeof(BEG_ALT_TOKEN) - 1, BeginAlternate, 1, + END_ALT_TOKEN, sizeof(END_ALT_TOKEN) - 1, EndAlternate, 1, + + NULL, 0 // must be last +}; + +// +// Local Variables +// +// +// Each preloaded lmhosts entry corresponds to NSUFFIXES NetBIOS names, +// each with a 16th byte from Suffix[]. +// +// For example, an lmhosts entry specifying "popcorn" causes the +// following NetBIOS names to be added to nbt.sys' name cache: +// +// "POPCORN " +// "POPCORN 0x0" +// "POPCORN 0x3" +// +// +#define NSUFFIXES 3 +UCHAR Suffix[] = { // LAN Manager Component + 0x20, // server + 0x0, // redirector + 0x03 // messenger +}; + +#ifndef VXD +// +// this structure tracks names queries that are passed up to user mode +// to resolve via DnsQueries +// +tDNS_QUERIES DnsQueries; +tCHECK_ADDR CheckAddr; +#endif + +// +// this structure tracks names queries that are passed to the LMhost processing +// to resolve. +// +tLMHOST_QUERIES LmHostQueries; + +tDOMAIN_LIST DomainNames; + +// +// Local (Private) Functions +// +LINE_CHARACTERISTICS +LmpGetTokens ( + IN OUT PUCHAR line, + OUT PUCHAR *token, + IN OUT int *pnumtokens + ); + +PKEYWORD +LmpIsKeyWord ( + IN PUCHAR string, + IN PKEYWORD table + ); + +BOOLEAN +LmpBreakRecursion( + IN PUCHAR path, + IN PUCHAR target + ); + +LONG +HandleSpecial( + IN char **pch); + +ULONG +AddToDomainList ( + IN PUCHAR pName, + IN ULONG IpAddress, + IN PLIST_ENTRY pDomainHead + ); + +VOID +ChangeStateInRemoteTable ( + IN tIPLIST *pIpList, + OUT PVOID *pContext + ); + +VOID +ChangeStateOfName ( + IN ULONG IpAddress, + IN NBT_WORK_ITEM_CONTEXT *Context, + OUT PVOID *pContext, + IN BOOLEAN LmHosts + ); + +VOID +LmHostTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ); + +VOID +TimeoutQEntries( + IN PLIST_ENTRY pHeadList, + IN PLIST_ENTRY TmpHead, + OUT USHORT *pFlags + ); + +VOID +StartLmHostTimer( + tDGRAM_SEND_TRACKING *pTracker, + NBT_WORK_ITEM_CONTEXT *pContext + ); + +NTSTATUS +GetNameToFind( + OUT PUCHAR pName + ); + +VOID +GetContext ( + OUT PVOID *pContext + ); + +VOID +MakeNewListCurrent ( + PLIST_ENTRY pTmpDomainList + ); + +VOID +RemoveNameAndCompleteReq ( + IN NBT_WORK_ITEM_CONTEXT *pContext, + IN NTSTATUS status + ); + +PCHAR +Nbtstrcat( PUCHAR pch, PUCHAR pCat, LONG Len ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, LmGetIpAddr) +#pragma CTEMakePageable(PAGE, HandleSpecial) +#pragma CTEMakePageable(PAGE, LmpGetTokens) +#pragma CTEMakePageable(PAGE, LmpIsKeyWord) +#pragma CTEMakePageable(PAGE, LmpBreakRecursion) +#pragma CTEMakePageable(PAGE, AddToDomainList) +#pragma CTEMakePageable(PAGE, LmExpandName) +#pragma CTEMakePageable(PAGE, LmInclude) +#pragma CTEMakePageable(PAGE, LmGetFullPath) +#pragma CTEMakePageable(PAGE, PrimeCache) +#pragma CTEMakePageable(PAGE, ScanLmHostFile) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- + +unsigned long +LmGetIpAddr ( + IN PUCHAR path, + IN PUCHAR target, + IN BOOLEAN recurse, + OUT BOOLEAN *bFindName + ) + +/*++ + +Routine Description: + + This function searches the file for an lmhosts entry that can be + mapped to the second level encoding. It then returns the ip address + specified in that entry. + + This function is called recursively, via LmInclude() !! + +Arguments: + + path - a fully specified path to a lmhosts file + target - the unencoded 16 byte NetBIOS name to look for + recurse - TRUE if #INCLUDE directives should be obeyed + +Return Value: + + The ip address (network byte order), or 0 if no appropriate entry was + found. + + Note that in most contexts (but not here), ip address 0 signifies + "this host." + +--*/ + + +{ + PUCHAR buffer; + PLM_FILE pfile; + NTSTATUS status; + int count, nwords; + INCLUDE_STATE incstate; + PUCHAR token[MaxTokens]; + LINE_CHARACTERISTICS current; + unsigned long inaddr, retval; + UCHAR temp[NETBIOS_NAME_SIZE+1]; + + CTEPagedCode(); + // + // Check for infinitely recursive name lookup in a #INCLUDE. + // + if (LmpBreakRecursion(path, target) == TRUE) + { + return((unsigned long)0); + } + +#ifdef VXD + // + // if we came here via nbtstat -R and InDos is set, report error: user + // can try nbtstat -R again. (since nbtstat can only be run from DOS box, + // can InDos be ever set??? Might as well play safe) + // + if ( !fInInit && GetInDosFlag() ) + { + return(0); + } +#endif + + pfile = LmOpenFile(path); + + if (!pfile) + { + return((unsigned long) 0); + } + + *bFindName = FALSE; + inaddr = 0; + incstate = MustInclude; + + while (buffer = LmFgets(pfile, &count)) + { + + nwords = MaxTokens; + current = LmpGetTokens(buffer, token, &nwords); + + switch ((ULONG)current.l_category) + { + case ErrorLine: + continue; + + case Domain: + case Ordinary: + if (current.l_preload || + ((nwords - 1) < NbName)) + { + continue; + } + break; + + case Include: + if (!recurse || (incstate == SkipInclude) || (nwords < 2)) + { + continue; + } + + retval = LmInclude(token[1], LmGetIpAddr, target, bFindName); + + if (retval == 0) + { + + if (incstate == TryToInclude) + { + incstate = SkipInclude; + } + continue; + } + else if (retval == -1) + { + + if (incstate == MustInclude) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: can't #INCLUDE \"%s\"", token[1])); + } + continue; + } + inaddr = retval; + goto found; + + case BeginAlternate: + ASSERT(nwords == 1); + incstate = TryToInclude; + continue; + + case EndAlternate: + ASSERT(nwords == 1); + incstate = MustInclude; + continue; + + default: + continue; + } + + if (strlen(token[NbName]) == (NETBIOS_NAME_SIZE)) + { + if (strncmp(token[NbName], target, (NETBIOS_NAME_SIZE)) != 0) + { + continue; + } + } else + { + // + // attempt to match, in a case insensitive manner, the first 15 + // bytes of the lmhosts entry with the target name. + // + LmExpandName(temp, token[NbName], 0); + + if (strncmp(temp, target, NETBIOS_NAME_SIZE - 1) != 0) + { + continue; + } + } + + if (current.l_nofnr) + { + *bFindName = TRUE; + } + status = ConvertDottedDecimalToUlong(token[IpAddress],&inaddr); + if (!NT_SUCCESS(status)) + { + inaddr = 0; + } + break; + } + +found: + status = LmCloseFile(pfile); + + ASSERT(status == STATUS_SUCCESS); + + if (!NT_SUCCESS(status)) + { + *bFindName = FALSE; + } + + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: LmGetIpAddr(\"%15.15s<%X>\") = %X\n",target,target[15],inaddr)); + + + return(inaddr); +} // LmGetIpAddr + + +//---------------------------------------------------------------------------- +LONG +HandleSpecial( + IN CHAR **pch) + +/*++ + +Routine Description: + + This function converts ASCII hex into a ULONG. + +Arguments: + + +Return Value: + + The ip address (network byte order), or 0 if no appropriate entry was + found. + + Note that in most contexts (but not here), ip address 0 signifies + "this host." + +--*/ + + +{ + int sval; + int rval; + char *sp = *pch; + int i; + + CTEPagedCode(); + sp++; + switch (*sp) + { + case '\\': + // the second character is also a \ so return a \ and set pch to + // point to the next character (\) + // + *pch = sp; + return((int)'\\'); + + default: + + // convert some number of characters to hex and increment pch + // the expected format is "\0x03" + // +// sscanf(sp, "%2x%n", &sval, &rval); + + sval = 0; + rval = 0; + sp++; + + // check for the 0x part of the hex number + if (*sp != 'x') + { + *pch = sp; + return(-1); + } + sp++; + for (i=0;(( i<2 ) && *sp) ;i++ ) + { + if (*sp != ' ') + { + // convert from ASCII to hex, allowing capitals too + // + if (*sp >= 'a') + { + sval = *sp - 'a' + 10 + sval*16; + } + else + if (*sp >= 'A') + { + sval = *sp - 'A' + 10 + sval*16; + } + else + { + sval = *sp - '0' + sval*16; + } + sp++; + rval++; + } + else + break; + } + + if (rval < 1) + { + *pch = sp; + return(-1); + } + + *pch += (rval+2); // remember to account for the characters 0 and x + + return(sval); + + } +} + +#define LMHASSERT(s) if (!(s)) \ +{ retval.l_category = ErrorLine; return(retval); } + +//---------------------------------------------------------------------------- + +LINE_CHARACTERISTICS +LmpGetTokens ( + IN OUT PUCHAR line, + OUT PUCHAR *token, + IN OUT int *pnumtokens + ) + +/*++ + +Routine Description: + + This function parses a line for tokens. A maximum of *pnumtokens + are collected. + +Arguments: + + line - pointer to the NULL terminated line to parse + token - an array of pointers to tokens collected + *pnumtokens - on input, number of elements in the array, token[]; + on output, number of tokens collected in token[] + +Return Value: + + The characteristics of this lmhosts line. + +Notes: + + 1. Each token must be separated by white space. Hence, the keyword + "#PRE" in the following line won't be recognized: + + 11.1.12.132 lothair#PRE + + 2. Any ordinary line can be decorated with a "#PRE", a "#DOM:name" or + both. Hence, the following lines must all be recognized: + + 111.21.112.3 kernel #DOM:ntwins #PRE + 111.21.112.4 orville #PRE #DOM:ntdev + 111.21.112.7 cliffv4 #DOM:ntlan + 111.21.112.132 lothair #PRE + +--*/ + + +{ + enum _PARSE + { // current fsm state + + StartofLine, + WhiteSpace, + AmidstToken + + } state; + + PUCHAR pch; // current fsm input + PUCHAR och; + PKEYWORD keyword; + int index, maxtokens, quoted, rchar; + LINE_CHARACTERISTICS retval; + + CTEPagedCode(); + CTEZeroMemory(token, *pnumtokens * sizeof(PUCHAR *)); + + state = StartofLine; + retval.l_category = Ordinary; + retval.l_preload = 0; + retval.l_nofnr = 0; + maxtokens = *pnumtokens; + index = 0; + quoted = 0; + + for (pch = line; *pch; pch++) + { + switch (*pch) + { + + // + // does the '#' signify the start of a reserved keyword, or the + // start of a comment ? + // + // + case '#': + if (quoted) + { + *och++ = *pch; + continue; + } + keyword = LmpIsKeyWord( + pch, + (state == StartofLine) ? Directive : Decoration); + + if (keyword) + { + state = AmidstToken; + maxtokens = keyword->k_noperands; + + switch (keyword->k_type) + { + case NoFNR: + retval.l_nofnr = 1; + continue; + + case Preload: + retval.l_preload = 1; + continue; + + default: + LMHASSERT(maxtokens <= *pnumtokens); + LMHASSERT(index < maxtokens); + + token[index++] = pch; + retval.l_category = keyword->k_type; + continue; + } + + LMHASSERT(0); + } + + if (state == StartofLine) + { + retval.l_category = Comment; + } + /* fall through */ + + case '\r': + case '\n': + *pch = (UCHAR) NULL; + if (quoted) + { + *och = (UCHAR) NULL; + } + goto done; + + case ' ': + case '\t': + if (quoted) + { + *och++ = *pch; + continue; + } + if (state == AmidstToken) + { + state = WhiteSpace; + *pch = (UCHAR) NULL; + + if (index == maxtokens) + { + goto done; + } + } + continue; + + case '"': + if ((state == AmidstToken) && quoted) + { + state = WhiteSpace; + quoted = 0; + *pch = (UCHAR) NULL; + *och = (UCHAR) NULL; + + if (index == maxtokens) + { + goto done; + } + continue; + } + + state = AmidstToken; + quoted = 1; + LMHASSERT(maxtokens <= *pnumtokens); + LMHASSERT(index < maxtokens); + token[index++] = pch + 1; + och = pch + 1; + continue; + + case '\\': + if (quoted) + { + rchar = HandleSpecial(&pch); + if (rchar == -1) + { + retval.l_category = ErrorLine; + return(retval); + } + *och++ = (UCHAR)rchar; + // + // put null on end of string + // + + continue; + } + + default: + if (quoted) + { + *och++ = *pch; + continue; + } + if (state == AmidstToken) + { + continue; + } + + state = AmidstToken; + + LMHASSERT(maxtokens <= *pnumtokens); + LMHASSERT(index < maxtokens); + token[index++] = pch; + continue; + } + } + +done: + // + // if there is no name on the line, then return an error + // + if (index <= NbName) + { + retval.l_category = ErrorLine; + } + ASSERT(!*pch); + ASSERT(maxtokens <= *pnumtokens); + ASSERT(index <= *pnumtokens); + + *pnumtokens = index; + return(retval); +} // LmpGetTokens + + + +//---------------------------------------------------------------------------- + +PKEYWORD +LmpIsKeyWord ( + IN PUCHAR string, + IN PKEYWORD table + ) + +/*++ + +Routine Description: + + This function determines whether the string is a reserved keyword. + +Arguments: + + string - the string to search + table - an array of keywords to look for + +Return Value: + + A pointer to the relevant keyword object, or NULL if unsuccessful + +--*/ + + +{ + size_t limit; + PKEYWORD special; + + CTEPagedCode(); + limit = strlen(string); + + for (special = table; special->k_string; special++) + { + + if (limit < special->k_strlen) + { + continue; + } + + if ((limit >= special->k_strlen) && + !strncmp(string, special->k_string, special->k_strlen)) + { + + return(special); + } + } + + return((PKEYWORD) NULL); +} // LmpIsKeyWord + + + +//---------------------------------------------------------------------------- + +BOOLEAN +LmpBreakRecursion( + IN PUCHAR path, + IN PUCHAR target + ) +/*++ + +Routine Description: + + This function checks that the file name we are about to open + does not use the target name of this search, which would + cause an infinite lookup loop. + +Arguments: + + path - a fully specified path to a lmhosts file + target - the unencoded 16 byte NetBIOS name to look for + +Return Value: + + TRUE if the UNC server name in the file path is the same as the + target of this search. FALSE otherwise. + +Notes: + + This function does not detect redirected drives. + +--*/ + + +{ + PCHAR keystring = "\\DosDevices\\UNC\\"; + PCHAR servername[NETBIOS_NAME_SIZE+1]; // for null on end + PCHAR marker1; + PCHAR marker2; + PCHAR marker3; + BOOLEAN retval = FALSE; + tNAMEADDR *pNameAddr; + USHORT uType; + + CTEPagedCode(); + // + // Check for and extract the UNC server name + // + if (strlen(path) > strlen(keystring)) + { + // check that the name is a unc name + if (strncmp(path, keystring, strlen(keystring)) == 0) + { + // the end of the \DosDevices\Unc\ string + marker1 = path + strlen(keystring); + + // the end of the whole path + marker3 = &path[strlen(path)-1]; + + // the end of the server name + marker2 = strchr(marker1,'\\'); + + if (marker2 != marker3) + { + *marker2 = '\0'; + + // + // attempt to match, in a case insensitive manner, the + // first 15 bytes of the lmhosts entry with the target + // name. + // + LmExpandName((PUCHAR)servername, marker1, 0); + + if (strncmp((PUCHAR)servername,target,NETBIOS_NAME_SIZE - 1) == 0) + { + // + // break the recursion + // + retval = TRUE; + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("Nbt:Not including Lmhosts #include because of recursive name %s\n", + servername)); + } + else + { + // + // check if the name has been preloaded in the cache, and + // if not, fail the request so we can't get into a loop + // trying to include the remote file while trying to + // resolve the remote name + // + pNameAddr = FindName(NBT_REMOTE, + (PCHAR)servername, + NbtConfig.pScope, + &uType); + + if (!pNameAddr || !(pNameAddr->NameTypeState & PRELOADED) ) + { + // + // break the recursion + // + retval = TRUE; + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("Nbt:Not including Lmhosts #include because name not Preloaded %s\n", + servername)); + } + } + *marker2 = '\\'; + } + } + + } + + return(retval); +} + +//---------------------------------------------------------------------------- +tNAMEADDR * +FindInDomainList ( + IN PUCHAR pName, + IN PLIST_ENTRY pDomainHead + ) + +/*++ + +Routine Description: + + This function finds a name in the domain list passed in. + +Arguments: + + name to find + head of list to look on + +Return Value: + + ptr to pNameaddr + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tNAMEADDR *pNameAddr; + + pHead = pEntry = pDomainHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + if (strncmp(pNameAddr->Name,pName,NETBIOS_NAME_SIZE) == 0) + { + return(pNameAddr); + } + } + + return(NULL); +} + +//---------------------------------------------------------------------------- +ULONG +AddToDomainList ( + IN PUCHAR pName, + IN ULONG IpAddress, + IN PLIST_ENTRY pDomainHead + ) + +/*++ + +Routine Description: + + This function adds a name and ip address to the list of domains that + are stored in a list. + + +Arguments: + +Return Value: + + +--*/ + + +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tNAMEADDR *pNameAddr=NULL; + ULONG *pIpAddr; + + + CTEPagedCode(); + + pHead = pEntry = pDomainHead; + + if (!IsListEmpty(pDomainHead)) + { + pNameAddr = FindInDomainList(pName,pDomainHead); + if (pNameAddr) + { + // + // the name matches, so add to the end of the ip address list + // + if (pNameAddr->CurrentLength < pNameAddr->MaxDomainAddrLength) + { + pIpAddr = pNameAddr->pIpList->IpAddr; + + while (*pIpAddr != (ULONG)-1) + pIpAddr++; + + *pIpAddr++ = IpAddress; + *pIpAddr = (ULONG)-1; + pNameAddr->CurrentLength += sizeof(ULONG); + } + else + { + // + // need to allocate more memory for for ip addresses + // + pIpAddr = CTEAllocInitMem(pNameAddr->MaxDomainAddrLength + + INITIAL_DOM_SIZE); + + if (pIpAddr) + { + CTEMemCopy(pIpAddr, + pNameAddr->pIpList, + pNameAddr->MaxDomainAddrLength); + + // + // Free the old chunk of memory and tack the new one on + // to the pNameaddr + // + CTEMemFree(pNameAddr->pIpList); + pNameAddr->pIpList = (tIPLIST *)pIpAddr; + + pIpAddr = (PULONG)((PUCHAR)pIpAddr + pNameAddr->MaxDomainAddrLength); + + // + // our last entry was -1: overwrite that one + // + pIpAddr--; + + *pIpAddr++ = IpAddress; + *pIpAddr = (ULONG)-1; + + // + // update the number of addresses in the list so far + // + pNameAddr->MaxDomainAddrLength += INITIAL_DOM_SIZE; + pNameAddr->CurrentLength += sizeof(ULONG); + pNameAddr->Verify = REMOTE_NAME; + } + + } + } + + } + + // + // check if we found the name or we need to add a new name + // + if (!pNameAddr) + { + // + // create a new name for the domain list + // + pNameAddr = CTEAllocInitMem(sizeof(tNAMEADDR)); + if (pNameAddr) + { + pIpAddr = CTEAllocInitMem(INITIAL_DOM_SIZE); + if (pIpAddr) + { + CTEMemCopy(pNameAddr->Name,pName,NETBIOS_NAME_SIZE); + pNameAddr->pIpList = (tIPLIST *)pIpAddr; + *pIpAddr++ = IpAddress; + *pIpAddr = (ULONG)-1; + + pNameAddr->RefCount = 1; + pNameAddr->NameTypeState = NAMETYPE_INET_GROUP; + pNameAddr->MaxDomainAddrLength = INITIAL_DOM_SIZE; + pNameAddr->CurrentLength = 2*sizeof(ULONG); + pNameAddr->Verify = REMOTE_NAME; + + InsertHeadList(pDomainHead,&pNameAddr->Linkage); + } + else + { + CTEMemFree(pNameAddr); + } + + } + } + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +VOID +MakeNewListCurrent ( + PLIST_ENTRY pTmpDomainList + ) + +/*++ + +Routine Description: + + This function frees the old entries on the DomainList and hooks up the + new entries + +Arguments: + + pTmpDomainList - list entry to the head of a new domain list + +Return Value: + + +--*/ + + +{ + CTELockHandle OldIrq; + tNAMEADDR *pNameAddr; + PLIST_ENTRY pEntry; + PLIST_ENTRY pHead; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (!IsListEmpty(pTmpDomainList)) + { + // + // free the old list elements + // + pHead = &DomainNames.DomainList; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + pEntry = pEntry->Flink; + + RemoveEntryList(&pNameAddr->Linkage); + // + // initialize linkage so that if the nameaddr is being + // referenced now, when it does get freed in a subsequent + // call to NbtDereferenceName it will not + // remove it from any lists + // + InitializeListHead(&pNameAddr->Linkage); + + // + // Since the name could be in use now we must dereference rather + // than just free it outright + // + NbtDereferenceName(pNameAddr); + + } + + DomainNames.DomainList.Flink = pTmpDomainList->Flink; + DomainNames.DomainList.Blink = pTmpDomainList->Blink; + pTmpDomainList->Flink->Blink = &DomainNames.DomainList; + pTmpDomainList->Blink->Flink = &DomainNames.DomainList; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + +} + +//---------------------------------------------------------------------------- + +char * +LmExpandName ( + OUT PUCHAR dest, + IN PUCHAR source, + IN UCHAR last + ) + +/*++ + +Routine Description: + + This function expands an lmhosts entry into a full 16 byte NetBIOS + name. It is padded with blanks up to 15 bytes; the 16th byte is the + input parameter, last. + + This function does not encode 1st level names to 2nd level names nor + vice-versa. + + Both dest and source are NULL terminated strings. + +Arguments: + + dest - sizeof(dest) must be NBT_NONCODED_NMSZ + source - the lmhosts entry + last - the 16th byte of the NetBIOS name + +Return Value: + + dest. + +--*/ + + +{ + char byte; + char *retval = dest; + char *src = source ; +#ifndef VXD + WCHAR unicodebuf[NETBIOS_NAME_SIZE+1]; + UNICODE_STRING unicode; + STRING tmp; +#endif + NTSTATUS status; + PUCHAR limit; + + CTEPagedCode(); + // + // first, copy the source OEM string to the destination, pad it, and + // add the last character. + // + limit = dest + NETBIOS_NAME_SIZE - 1; + + while ( (*source != '\0') && (dest < limit) ) + { + *dest++ = *source++; + } + + while(dest < limit) + { + *dest++ = ' '; + } + + ASSERT(dest == (retval + NETBIOS_NAME_SIZE - 1)); + + *dest = '\0'; + *(dest + 1) = '\0'; + dest = retval; + +#ifndef VXD + // + // Now, convert to unicode then to ANSI to force the OEM -> ANSI munge. + // Then convert back to Unicode and uppercase the name. Finally convert + // back to OEM. + // + unicode.Length = 0; + unicode.MaximumLength = 2*(NETBIOS_NAME_SIZE+1); + unicode.Buffer = unicodebuf; + + RtlInitString(&tmp, dest); + + status = RtlOemStringToUnicodeString(&unicode, &tmp, FALSE); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(( + "NBT LmExpandName: Oem -> Unicode failed, status %X\n", + status)); + goto oldupcase; + } + + status = RtlUnicodeStringToAnsiString(&tmp, &unicode, FALSE); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(( + "NBT LmExpandName: Unicode -> Ansi failed, status %X\n", + status + )); + goto oldupcase; + } + + status = RtlAnsiStringToUnicodeString(&unicode, &tmp, FALSE); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(( + "NBT LmExpandName: Ansi -> Unicode failed, status %X\n", + status + )); + goto oldupcase; + } + + status = RtlUpcaseUnicodeStringToOemString(&tmp, &unicode, FALSE); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(( + "NBT LmExpandName: Unicode upcase -> Oem failed, status %X\n", + status + )); + goto oldupcase; + } + + // write the last byte to "0x20" or "0x03" or whatever + // since we do not want it to go through the munge above. + // + dest[NETBIOS_NAME_SIZE-1] = last; + return(retval); + +#endif + +oldupcase: + + for ( source = src ; dest < (retval + NETBIOS_NAME_SIZE - 1); dest++) + { + byte = *(source++); + + if (!byte) + { + break; + } + + // Don't use the c-runtime (nt c defn. included first) + // BUGBUG - What about extended characters etc.? + *dest = (byte >= 'a' && byte <= 'z') ? byte-'a' + 'A' : byte ; +// *dest = islower(byte) ? toupper(byte) : byte; + } + + for (; dest < retval + NETBIOS_NAME_SIZE - 1; dest++) + { + *dest = ' '; + } + + ASSERT(dest == (retval + NETBIOS_NAME_SIZE - 1)); + + *dest = last; + *(dest + 1) = (char) NULL; + + return(retval); +} // LmExpandName + +//---------------------------------------------------------------------------- + +unsigned long +LmInclude( + IN PUCHAR file, + IN LM_PARSE_FUNCTION function, + IN PUCHAR argument OPTIONAL, + OUT BOOLEAN *NoFindName OPTIONAL + ) + +/*++ + +Routine Description: + + LmInclude() is called to process a #INCLUDE directive in the lmhosts + file. + +Arguments: + + file - the file to include + function - function to parse the included file + argument - optional second argument to the parse function + NoFindName - Are find names allowed for this address + +Return Value: + + The return value from the parse function. This should be -1 if the + file could not be processed, or else some positive number. + +--*/ + + +{ + int retval; + PUCHAR end; + NTSTATUS status; + PUCHAR path; + + CTEPagedCode(); + // + // unlike C, treat both variations of the #INCLUDE directive identically: + // + // #INCLUDE file + // #INCLUDE "file" + // + // If a leading '"' exists, skip over it. + // + if (*file == '"') + { + + file++; + + end = strchr(file, '"'); + + if (end) + { + *end = (UCHAR) NULL; + } + } + + // + // check that the file to be included has been preloaded in the cache + // since we do not want to have the name query come right back to here + // to force another inclusion of the same remote file + // + +#ifdef VXD + return (*function)(file, argument, FALSE, NoFindName ) ; +#else + status = LmGetFullPath(file, &path); + + if (status != STATUS_SUCCESS) + { + return(status); + } + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: #INCLUDE \"%s\"\n", path)); + + retval = (*function) (path, argument, FALSE, NoFindName); + + CTEMemFree(path); + + return(retval); +#endif +} // LmInclude + + +//---------------------------------------------------------------------------- + +#ifndef VXD // Not used by VXD + +NTSTATUS +LmGetFullPath ( + IN PUCHAR target, + OUT PUCHAR *ppath + ) + +/*++ + +Routine Description: + + This function returns the full path of the lmhosts file. This is done + by forming a string from the concatenation of the C strings + DatabasePath and the string, file. + +Arguments: + + target - the name of the file. This can either be a full path name + or a mere file name. + path - a pointer to a UCHAR + +Return Value: + + STATUS_SUCCESS if successful. + +Notes: + + RtlMoveMemory() handles overlapped copies; RtlCopyMemory() doesn't. + +--*/ + +{ + ULONG FileNameType; + ULONG Len; + PUCHAR path; + + CTEPagedCode(); + // + // use a count to figure out what sort of string to build up + // + // 0 - local full path file name + // 1 - local file name only, no path + // 2 - remote file name + // 3 - \SystemRoot\ starting file name, or \DosDevices\UNC\... + // + + // if the target begins with a '\', or contains a DOS drive letter, + // then assume that it specifies a full path. Otherwise, prepend the + // directory used to specify the lmhost file itself. + // + // + if (target[1] == ':') + { + FileNameType = 0; + } + else + if (strncmp(&target[1],"SystemRoot",10) == 0) + { + FileNameType = 3; + } + else + if (strncmp(&target[0],"\\DosDevices\\",12) == 0) + { + FileNameType = 3; + } + else + if (strncmp(target,"\\DosDevices\\UNC\\",sizeof("\\DosDevices\\UNC\\")-1) == 0) + { + FileNameType = 3; + } + else + { + FileNameType = 1; + } + + // + // does the directory specify a remote file ? + // + // If so, it must be prefixed with "\\DosDevices\\UNC", and the double + // slashes of the UNC name eliminated. + // + // + if ((target[1] == '\\') && (target[0] == '\\')) + { + FileNameType = 2; + } + + path = NULL; + switch (FileNameType) + { + case 0: + // + // Full file name, put \DosDevices on front of name + // + Len = sizeof("\\DosDevices\\") + strlen(target); + path = CTEAllocInitMem(Len); + if (path) + { + ULONG Length=sizeof("\\DosDevices\\"); // Took out -1 + + strncpy(path,"\\DosDevices\\",Length); + Nbtstrcat(path,target,Len); + } + break; + + + case 1: + // + // only the file name is present, with no path, so use the path + // specified for the lmhost file in the registry NbtConfig.PathLength + // includes the last backslash of the path. + // + //Len = sizeof("\\DosDevices\\") + NbtConfig.PathLength + strlen(target); + Len = NbtConfig.PathLength + strlen(target) +1; + path = CTEAllocInitMem(Len); + if (path) + { + //ULONG Length=sizeof("\\DosDevices") -1; // -1 not to count null + + //strncpy(path,"\\DosDevices",Length); + + strncpy(path,NbtConfig.pLmHosts,NbtConfig.PathLength); + + Nbtstrcat(path,target,Len); + } + + break; + + case 2: + // + // Full file name, put \DosDevices\UNC on front of name and delete + // one of the two back slashes used for the remote name + // + Len = strlen(target); + path = CTEAllocInitMem(Len + sizeof("\\DosDevices\\UNC")); + + if (path) + { + ULONG Length = sizeof("\\DosDevices\\UNC"); + + strncpy(path,"\\DosDevices\\UNC",Length); + + // to delete the first \ from the two \\ on the front of the + // remote file name add one to target. + // + Nbtstrcat(path,target+1,Len+sizeof("\\DosDevices\\UNC")); + } + break; + + case 3: + // the target is the full path + Len = strlen(target) + 1; + path = CTEAllocInitMem(Len); + if (path) + { + strncpy(path,target,Len); + } + break; + + + } + + if (path) + { + *ppath = path; + return(STATUS_SUCCESS); + } + else + return(STATUS_UNSUCCESSFUL); +} // LmGetFullPath + +//---------------------------------------------------------------------------- +NTSTATUS +NtCheckForIPAddr ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID *pBuffer, + IN LONG Size, + IN PCTE_IRP pIrp + ) +/*++ + +Routine Description: + + This function is used to allow NBT to ping multiple IP addrs, by returning the buffer + passed into this routine with the IP list in it to lmhsvc.dll + +Arguments: + +Return Value: + + STATUS_PENDING if the buffer is to be held on to, the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS status; + NTSTATUS Locstatus; + CTELockHandle OldIrq; + tIPADDR_BUFFER_DNS *pIpAddrBuf; + PVOID pClientCompletion; + PVOID pClientContext; + tDGRAM_SEND_TRACKING *pTracker; + ULONG IpAddrsList[MAX_IPADDRS_PER_HOST+1]; + PVOID Context; + BOOLEAN CompletingAnotherQuery = FALSE; + + + pIpAddrBuf = (tIPADDR_BUFFER_DNS *)pBuffer; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + CheckAddr.QueryIrp = pIrp; + + status = STATUS_PENDING; + + if (CheckAddr.ResolvingNow) + { + + // + // if the client got tired of waiting for DNS, the WaitForDnsIrpCancel + // in ntisol.c will have cleared the Context value when cancelling the + // irp, so check for that here. + // + if (CheckAddr.Context) + { + Context = CheckAddr.Context; + + CheckAddr.Context = NULL; + + NTClearContextCancel( Context ); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); +#if DBG + if (!pIpAddrBuf->Resolved) { + ASSERT(pIpAddrBuf->IpAddrsList[0] == 0); + } +#endif + StartConnWithBestAddr(Context, + pIpAddrBuf->IpAddrsList, + (BOOLEAN)pIpAddrBuf->Resolved); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + } + else + { + KdPrint(("Nbt: NtDnsNameResolve: No Context!! *******\r\n")); + } + + CheckAddr.ResolvingNow = FALSE; + // + // are there any more name query requests to process? + // + while (TRUE) + { + if (!IsListEmpty(&CheckAddr.ToResolve)) + { + PLIST_ENTRY pEntry; + + pEntry = RemoveHeadList(&CheckAddr.ToResolve); + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + Locstatus = DoCheckAddr(Context); + + // + // if it failed then complete the irp now + // + if (!NT_SUCCESS(Locstatus)) + { + KdPrint(("NtDnsNameResolve: DoDnsResolve failed with %x\r\n",Locstatus)); + pClientCompletion = ((NBT_WORK_ITEM_CONTEXT *)Context)->ClientCompletion; + pClientContext = ((NBT_WORK_ITEM_CONTEXT *)Context)->pClientContext; + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + // + // Clear the Cancel Routine now + // + (VOID)NTCancelCancelRoutine(((tDGRAM_SEND_TRACKING *)pClientContext)->pClientIrp); + + DereferenceTracker(pTracker); + + CompleteClientReq(pClientCompletion, + pClientContext, + STATUS_BAD_NETWORK_PATH); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + } + else + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CompletingAnotherQuery = TRUE; + break; + } + + } + else + { + break; + } + } + + } + + // + // We are holding onto the Irp, so set the cancel routine. + if (!CompletingAnotherQuery) + { + status = NTCheckSetCancelRoutine(pIrp,CheckAddrIrpCancel,pDeviceContext); + if (!NT_SUCCESS(status)) + { + // the irp got cancelled so complete it now + // + CheckAddr.QueryIrp = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(pIrp,status,0); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_PENDING; + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NtDnsNameResolve ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID *pBuffer, + IN LONG Size, + IN PCTE_IRP pIrp + ) +/*++ + +Routine Description: + + This function is used to allow NBT to query DNS, by returning the buffer + passed into this routine with a name in it. + +Arguments: + + target - the name of the file. This can either be a full path name + or a mere file name. + path - a pointer to a UCHAR + +Return Value: + + STATUS_PENDING if the buffer is to be held on to, the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS status; + NTSTATUS Locstatus; + CTELockHandle OldIrq; + tIPADDR_BUFFER_DNS *pIpAddrBuf; + PVOID pClientCompletion; + PVOID pClientContext; + tDGRAM_SEND_TRACKING *pTracker; + ULONG IpAddrsList[MAX_IPADDRS_PER_HOST+1]; + PVOID Context; + BOOLEAN CompletingAnotherQuery = FALSE; + + + pIpAddrBuf = (tIPADDR_BUFFER_DNS *)pBuffer; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + DnsQueries.QueryIrp = pIrp; + + status = STATUS_PENDING; + + if (DnsQueries.ResolvingNow) + { + + // + // if the client got tired of waiting for DNS, the WaitForDnsIrpCancel + // in ntisol.c will have cleared the Context value when cancelling the + // irp, so check for that here. + // + if (DnsQueries.Context) + { + Context = DnsQueries.Context; + + DnsQueries.Context = NULL; + + NTClearContextCancel( Context ); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + StartIpAddrToSrvName(Context, + pIpAddrBuf->IpAddrsList, + (BOOLEAN)pIpAddrBuf->Resolved); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + } + else + { + KdPrint(("Nbt: NtDnsNameResolve: No Context!! *******\r\n")); + } + + DnsQueries.ResolvingNow = FALSE; + // + // are there any more name query requests to process? + // + while (TRUE) + { + if (!IsListEmpty(&DnsQueries.ToResolve)) + { + PLIST_ENTRY pEntry; + + pEntry = RemoveHeadList(&DnsQueries.ToResolve); + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + Locstatus = DoDnsResolve(Context); + + // + // if it failed then complete the irp now + // + if (!NT_SUCCESS(Locstatus)) + { + KdPrint(("NtDnsNameResolve: DoDnsResolve failed with %x\r\n",Locstatus)); + pClientCompletion = ((NBT_WORK_ITEM_CONTEXT *)Context)->ClientCompletion; + pClientContext = ((NBT_WORK_ITEM_CONTEXT *)Context)->pClientContext; + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + // + // Clear the Cancel Routine now + // + (VOID)NTCancelCancelRoutine(((tDGRAM_SEND_TRACKING *)pClientContext)->pClientIrp); + + DereferenceTracker(pTracker); + + CompleteClientReq(pClientCompletion, + pClientContext, + STATUS_BAD_NETWORK_PATH); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + } + else + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CompletingAnotherQuery = TRUE; + break; + } + + } + else + { + break; + } + } + + } + + // + // We are holding onto the Irp, so set the cancel routine. + if (!CompletingAnotherQuery) + { + status = NTCheckSetCancelRoutine(pIrp,DnsIrpCancel,pDeviceContext); + if (!NT_SUCCESS(status)) + { + // the irp got cancelled so complete it now + // + DnsQueries.QueryIrp = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(pIrp,status,0); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_PENDING; + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +DoDnsResolve ( + IN NBT_WORK_ITEM_CONTEXT *Context + ) +/*++ + +Routine Description: + + This function is used to allow NBT to query DNS, by returning the buffer + passed into this routine with a name in it. + +Arguments: + + target - the name of the file. This can either be a full path name + or a mere file name. + path - a pointer to a UCHAR + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS status; + tIPADDR_BUFFER_DNS *pIpAddrBuf; + PCTE_IRP pIrp; + tDGRAM_SEND_TRACKING *pTracker; + tDGRAM_SEND_TRACKING *pClientTracker; + CTELockHandle OldIrq; + PCHAR pDestName; + ULONG NameLen; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + Context->TimedOut = FALSE; + if (!DnsQueries.QueryIrp) + { + // + // the irp either never made it down here, or it was cancelled, + // so pretend the name query timed out. + // + KdPrint(("DoDnsResolve: QueryIrp is NULL, returning\r\n")); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_BAD_NETWORK_PATH); + } + else + if (!DnsQueries.ResolvingNow) + { + DnsQueries.ResolvingNow = TRUE; + DnsQueries.Context = Context; + pIrp = DnsQueries.QueryIrp; + + // this is the session setup tracker + pClientTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + + // this is the name query tracker + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + + // + // whenever dest. name is 16 bytes long (or smaller), we have no + // way of knowing if its a netbios name or a dns name, so we presume + // it's netbios name, go to wins, broadcast etc. and then come to dns + // In this case, the name query tracker will be setup, so be non-null + // + if (pTracker) + { + pDestName = pTracker->pNameAddr->Name; + + // + // Ignore the 16th byte only if it is a non-DNS name character (we should be + // safe below 0x20). This will allow queries to DNS names which are exactly 16 + // characters long. + // + if ((pDestName[NETBIOS_NAME_SIZE-1] <= 0x20 ) || + (pDestName[NETBIOS_NAME_SIZE-1] >= 0x7f )) { + NameLen = NETBIOS_NAME_SIZE-1; // ignore 16th byte + } else { + NameLen = NETBIOS_NAME_SIZE; + } + } + + // + // if the dest name is longer than 16 bytes, it's got to be dns name so + // we bypass wins etc. and come straight to dns. In this case, we didn't + // set up a name query tracker so it will be null. Use the session setup + // tracker (i.e. pClientTracker) to get the dest name + // + else + { + ASSERT(pClientTracker); + + pDestName = pClientTracker->SendBuffer.pBuffer; + + NameLen = pClientTracker->SendBuffer.Length; + + // + // Ignore the 16th byte only if it is a non-DNS name character (we should be + // safe below 0x20). This will allow queries to DNS names which are exactly 16 + // characters long. + // + if (NameLen == NETBIOS_NAME_SIZE) { + if ((pDestName[NETBIOS_NAME_SIZE-1] <= 0x20 ) || + (pDestName[NETBIOS_NAME_SIZE-1] >= 0x7f )) { + NameLen = NETBIOS_NAME_SIZE-1; // ignore 16th byte + } + } + } + + + pIpAddrBuf = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + ASSERT(NameLen < 260); + + // + // copy the name to the Irps return buffer for lmhsvc to resolve with + // a gethostbyname call + // + CTEMemCopy(pIpAddrBuf->pName, + pDestName, + NameLen); + + pIpAddrBuf->pName[NameLen] = 0; + + pIpAddrBuf->NameLen = NameLen; + + // + // Since datagrams are buffered there is no client irp to get cancelled + // since the client's irp is returned immediately -so this check + // is only for connections being setup or QueryFindname or + // nodestatus, where we allow the irp to + // be cancelled. + // + status = STATUS_SUCCESS; + if (pClientTracker->pClientIrp) + { + // + // allow the client to cancel the name query Irp - no need to check + // if the client irp was already cancelled or not since the DNS query + // will complete and find no client request and stop. + // + status = NTCheckSetCancelRoutine(pClientTracker->pClientIrp, + WaitForDnsIrpCancel,NULL); + } + + // + // pass the irp up to lmhsvc.dll to do a gethostbyname call to + // sockets + // The Irp will return to NtDnsNameResolve, above + // + if (NT_SUCCESS(status)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(DnsQueries.QueryIrp,STATUS_SUCCESS,0); + } + else + { + // + // We failed to set the cancel routine, so undo setting up the + // the DnsQueries structure. + // + KdPrint(("DoDnsResolve: CheckSet (submitting) failed with %x\r\n",status)); + DnsQueries.ResolvingNow = FALSE; + DnsQueries.Context = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + } + else + { + // + // this is the session setup tracker + // + pClientTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + // + // Since datagrams are buffered there is no client irp to get cancelled + // since the client's irp is returned immediately -so this check + // is only for connections being setup, where we allow the irp to + // be cancelled. + // + status = STATUS_SUCCESS; + if (pClientTracker->pClientIrp) + { + // + // allow the client to cancel the name query Irp + // + status = NTCheckSetCancelRoutine(pClientTracker->pClientIrp, + WaitForDnsIrpCancel,NULL); + } + if (NT_SUCCESS(status)) + { + // the irp is busy resolving another name, so wait for it to return + // down here again, mean while, Queue the name query + // + InsertTailList(&DnsQueries.ToResolve,&Context->Item.List); + + } + else + { + KdPrint(("DoDnsResolve: CheckSet (queuing) failed with %x\r\n",status)); + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + if (NT_SUCCESS(status)) + { + status = STATUS_PENDING; + } + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +DoCheckAddr ( + IN NBT_WORK_ITEM_CONTEXT *Context + ) +/*++ + +Routine Description: + + This function is used to allow NBT to ping IP addrs, by returning the buffer + passed into this routine with the IP list in it. + +Arguments: + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS status; + tIPADDR_BUFFER_DNS *pIpAddrBuf; + PCTE_IRP pIrp; + tDGRAM_SEND_TRACKING *pTracker; + tDGRAM_SEND_TRACKING *pClientTracker; + CTELockHandle OldIrq; + PCHAR pDestName; + ULONG NameLen; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + Context->TimedOut = FALSE; + if (!CheckAddr.QueryIrp) + { + // + // the irp either never made it down here, or it was cancelled, + // so pretend the name query timed out. + // + KdPrint(("DoCheckAddr: QueryIrp is NULL, returning\r\n")); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_BAD_NETWORK_PATH); + } + else + if (!CheckAddr.ResolvingNow) + { + CheckAddr.ResolvingNow = TRUE; + CheckAddr.Context = Context; + pIrp = CheckAddr.QueryIrp; + + // this is the session setup tracker + pClientTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + + // this is the name query tracker + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + + pIpAddrBuf = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + ASSERT(pTracker == NULL); + + // + // copy the IP addrs for lmhsvc to ping... + // + CTEMemCopy(pIpAddrBuf->IpAddrsList, + pClientTracker->IpList, + (pClientTracker->NumAddrs+1) * sizeof(ULONG)); + + // + // Since datagrams are buffered there is no client irp to get cancelled + // since the client's irp is returned immediately -so this check + // is only for connections being setup or QueryFindname or + // nodestatus, where we allow the irp to + // be cancelled. + // + status = STATUS_SUCCESS; + if (pClientTracker->pClientIrp) + { + // + // allow the client to cancel the name query Irp - no need to check + // if the client irp was already cancelled or not since the DNS query + // will complete and find no client request and stop. + // + status = NTCheckSetCancelRoutine(pClientTracker->pClientIrp, + WaitForDnsIrpCancel,NULL); + } + + // + // pass the irp up to lmhsvc.dll to do a gethostbyname call to + // sockets + // The Irp will return to NtDnsNameResolve, above + // + if (NT_SUCCESS(status)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(CheckAddr.QueryIrp,STATUS_SUCCESS,0); + } + else + { + // + // We failed to set the cancel routine, so undo setting up the + // the CheckAddr structure. + // + KdPrint(("DoCheckAddr: CheckSet (submitting) failed with %x\r\n",status)); + CheckAddr.ResolvingNow = FALSE; + CheckAddr.Context = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + } + else + { + // + // this is the session setup tracker + // + pClientTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + // + // Since datagrams are buffered there is no client irp to get cancelled + // since the client's irp is returned immediately -so this check + // is only for connections being setup, where we allow the irp to + // be cancelled. + // + status = STATUS_SUCCESS; + if (pClientTracker->pClientIrp) + { + // + // allow the client to cancel the name query Irp + // + status = NTCheckSetCancelRoutine(pClientTracker->pClientIrp, + WaitForDnsIrpCancel,NULL); + } + if (NT_SUCCESS(status)) + { + // the irp is busy resolving another name, so wait for it to return + // down here again, mean while, Queue the name query + // + InsertTailList(&CheckAddr.ToResolve,&Context->Item.List); + + } + else + { + KdPrint(("DoCheckAddr: CheckSet (queuing) failed with %x\r\n",status)); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + if (NT_SUCCESS(status)) + { + status = STATUS_PENDING; + } + + return(status); + +} +#endif // !VXD + +//---------------------------------------------------------------------------- +VOID +StartIpAddrToSrvName( + IN NBT_WORK_ITEM_CONTEXT *Context, + IN ULONG *IpList, + IN BOOLEAN IpAddrResolved + ) +/*++ + +Routine Description: + + If the destination name is of the form 11.101.4.25 or is a dns name (i.e. of + the form ftp.microsoft.com) then we come to this function. In addition to + doing some house keeping, if the name did resolve then we also send out + a nodestatus request to find out the server name for that ipaddr + +Arguments: + + Context - (NBT_WORK_ITEM_CONTEXT) + IpList - Array of ipaddrs if resolved (i.e. IpAddrResolved is TRUE) + IpAddrResolved - TRUE if ipaddr could be resolved, FALSE otherwise + +Return Value: + + Nothing + +Notes: + + +--*/ + +{ + + NTSTATUS status; + CTELockHandle OldIrq; + PVOID pClientCompletion; + PVOID pClientContext; + tDGRAM_SEND_TRACKING *pTracker; + tDGRAM_SEND_TRACKING *pClientTracker; + ULONG TdiAddressType; + CHAR szName[NETBIOS_NAME_SIZE]; + ULONG IpAddrsList[MAX_IPADDRS_PER_HOST+1]; + tDEVICECONTEXT *pDeviceContext; + int i; + + + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + + pClientCompletion = ((NBT_WORK_ITEM_CONTEXT *)Context)->ClientCompletion; + pClientContext = ((NBT_WORK_ITEM_CONTEXT *)Context)->pClientContext; + pClientTracker = (tDGRAM_SEND_TRACKING *)pClientContext; + + pDeviceContext = ((tDGRAM_SEND_TRACKING *)pClientContext)->pDeviceContext; + ASSERT(pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + CTEMemFree(Context); + + TdiAddressType = ((pTracker == NULL) && + (pClientTracker->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX)) + ? TDI_ADDRESS_TYPE_NETBIOS_EX + : TDI_ADDRESS_TYPE_NETBIOS; + + + // whether or not name resolved, we don't need this nameaddr anymore + // (if name resolved, then we do a node status to that addr and create + // a new nameaddr for the server name in ExtractServerName) + // pTracker is null if we went straight to dns (without wins etc) + if (pTracker) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + NbtDereferenceName(pTracker->pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + +#ifndef VXD + (VOID)NTCancelCancelRoutine(((tDGRAM_SEND_TRACKING *)pClientContext)->pClientIrp); +#endif + + status = STATUS_BAD_NETWORK_PATH; + + if (IpAddrResolved) + { + if (TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS) { + for (i=0; i<MAX_IPADDRS_PER_HOST; i++) + { + IpAddrsList[i] = IpList[i]; + if (IpAddrsList[i] == 0) + break; + } + IpAddrsList[MAX_IPADDRS_PER_HOST] = 0; + + CTEZeroMemory(szName,NETBIOS_NAME_SIZE); + szName[0] = '*'; + + status = NbtSendNodeStatus(pDeviceContext, + szName, + NULL, + &IpAddrsList[0], + pClientContext, + pClientCompletion); + } else { + tNAMEADDR *pNameAddr; + PCHAR pRemoteName; + tCONNECTELE *pConnEle; + + pConnEle = pClientTracker->Connect.pConnEle; + pRemoteName = pConnEle->RemoteName; + + // + // add this server name to the remote hashtable + // + pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('8')); + if (pNameAddr != NULL) + { + tNAMEADDR *pTableAddress; + + CTEZeroMemory(pNameAddr,sizeof(tNAMEADDR)); + InitializeListHead(&pNameAddr->Linkage); + CTEMemCopy(pNameAddr->Name,pRemoteName,NETBIOS_NAME_SIZE); + pNameAddr->Verify = REMOTE_NAME; + pNameAddr->RefCount = 1; + pNameAddr->NameTypeState = STATE_RESOLVED | NAMETYPE_UNIQUE; + pNameAddr->AdapterMask = (CTEULONGLONG)-1; + pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + pNameAddr->IpAddress = IpList[0]; + + status = AddToHashTable( + NbtConfig.pRemoteHashTbl, + pNameAddr->Name, + NbtConfig.pScope, + 0, + 0, + pNameAddr, + &pTableAddress); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("StartIpAddrToSrv...AddRecordToHashTable Status %lx\n",status)); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + CompleteClientReq(pClientCompletion, + pClientContext, + status); + } + } else { + if (TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + tCONNECTELE *pConnEle; + pConnEle = pClientTracker->Connect.pConnEle; + pConnEle->RemoteNameDoesNotExistInDNS = TRUE; + } + } + + // pTracker is null if we went straight to dns (without wins etc) + if (pTracker) + { + DereferenceTracker(pTracker); + } + + if (!NT_SUCCESS(status)) + { + CompleteClientReq(pClientCompletion, + pClientContext, + status); + } + +} + +//---------------------------------------------------------------------------- +VOID +StartConnWithBestAddr( + IN NBT_WORK_ITEM_CONTEXT *Context, + IN ULONG *IpList, + IN BOOLEAN IpAddrResolved + ) +/*++ + +Routine Description: + + If the destination name is of the form 11.101.4.25 or is a dns name (i.e. of + the form ftp.microsoft.com) then we come to this function. In addition to + doing some house keeping, if the name did resolve then we also send out + a nodestatus request to find out the server name for that ipaddr + +Arguments: + + Context - (NBT_WORK_ITEM_CONTEXT) + IpList - Array of ipaddrs if resolved (i.e. IpAddrResolved is TRUE) + IpAddrResolved - TRUE if ipaddr could be resolved, FALSE otherwise + +Return Value: + + Nothing + +Notes: + + +--*/ + +{ + + NTSTATUS status; + CTELockHandle OldIrq; + PVOID pClientCompletion; + PVOID pClientContext; + tDGRAM_SEND_TRACKING *pTracker; + tDGRAM_SEND_TRACKING *pClientTracker; + ULONG TdiAddressType; + CHAR szName[NETBIOS_NAME_SIZE]; + ULONG IpAddrsList[MAX_IPADDRS_PER_HOST+1]; + tDEVICECONTEXT *pDeviceContext; + int i; + + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Entered StartIpAddrToSrv\n")); + + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + + pClientCompletion = ((NBT_WORK_ITEM_CONTEXT *)Context)->ClientCompletion; + pClientContext = ((NBT_WORK_ITEM_CONTEXT *)Context)->pClientContext; + pClientTracker = (tDGRAM_SEND_TRACKING *)pClientContext; + + pDeviceContext = ((tDGRAM_SEND_TRACKING *)pClientContext)->pDeviceContext; + ASSERT(pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + CTEMemFree(Context); + + // whether or not name resolved, we don't need this nameaddr anymore + // (if name resolved, then we do a node status to that addr and create + // a new nameaddr for the server name in ExtractServerName) + // pTracker is null if we went straight to dns (without wins etc) + if (pTracker) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + NbtDereferenceName(pTracker->pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + +#ifndef VXD + (VOID)NTCancelCancelRoutine(((tDGRAM_SEND_TRACKING *)pClientContext)->pClientIrp); +#endif + + status = STATUS_BAD_NETWORK_PATH; + + if (IpAddrResolved) + { + tNAMEADDR *pNameAddr; + PCHAR pRemoteName; + tCONNECTELE *pConnEle; + + pConnEle = pClientTracker->Connect.pConnEle; + pRemoteName = pConnEle->RemoteName; + + // + // add this server name to the remote hashtable + // + pNameAddr = NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('8')); + if (pNameAddr != NULL) + { + tNAMEADDR *pTableAddress; + + CTEZeroMemory(pNameAddr,sizeof(tNAMEADDR)); + InitializeListHead(&pNameAddr->Linkage); + CTEMemCopy(pNameAddr->Name,pRemoteName,NETBIOS_NAME_SIZE); + pNameAddr->Verify = REMOTE_NAME; + pNameAddr->RefCount = 1; + pNameAddr->NameTypeState = STATE_RESOLVED | NAMETYPE_UNIQUE; + pNameAddr->AdapterMask = (CTEULONGLONG)-1; + pNameAddr->TimeOutCount = NbtConfig.RemoteTimeoutCount; + pNameAddr->IpAddress = IpList[0]; + + status = AddToHashTable( + NbtConfig.pRemoteHashTbl, + pNameAddr->Name, + NbtConfig.pScope, + 0, + 0, + pNameAddr, + &pTableAddress); + + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("StartIpAddrToSrv...AddRecordToHashTable Status %lx\n",status)); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + CompleteClientReq(pClientCompletion, + pClientContext, + status); + + // pTracker is null if we went straight to dns (without wins etc) + if (pTracker) + { + DereferenceTracker(pTracker); + } +} + +//---------------------------------------------------------------------------- +VOID +ChangeStateInRemoteTable ( + IN tIPLIST *pIpList, + OUT PVOID *pContext + ) + +/*++ + +Routine Description: + + This function is not pagable - it grabs a spin lock and updates + pNameAddr. It removes the current context block from the LmHostQueries + structure in preparation for returning it to the client. + +Arguments: + + Context - + +Return Value: + + none + +--*/ + + +{ + CTELockHandle OldIrq; + NBT_WORK_ITEM_CONTEXT *Context; + tDGRAM_SEND_TRACKING *pTracker; + tNAMEADDR *pNameAddr; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (Context = LmHostQueries.Context) + { + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + pNameAddr = pTracker->pNameAddr; + LmHostQueries.Context = NULL; + NTClearContextCancel( Context ); + + pNameAddr->pIpList = pIpList; + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= NAMETYPE_INET_GROUP | STATE_RESOLVED; + *pContext = Context; + + } + else + *pContext = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +PreloadEntry +( + IN PUCHAR name, + IN unsigned long inaddr, + IN unsigned int NoFNR + ) + +/*++ + +Routine Description: + + This function adds an lmhosts entry to nbt's name cache. For each + lmhosts entry, NSUFFIXES unique cache entries are created. + + Even when some cache entries can't be created, this function doesn't + attempt to remove any that were successfully added to the cache. + +Arguments: + + name - the unencoded NetBIOS name specified in lmhosts + inaddr - the ip address, in host byte order + +Return Value: + + The number of new name cache entries created. + +--*/ + +{ + NTSTATUS status; + tNAMEADDR *pNameAddr; + LONG nentries; + LONG Len; + CHAR temp[NETBIOS_NAME_SIZE+1]; + CTELockHandle OldIrq; + LONG NumberToAdd; + + // if all 16 bytes are present then only add that name exactly as it + // is. + // + Len = strlen(name); + // + // if this string is exactly 16 characters long, do not expand + // into 0x00, 0x03,0x20 names. Just add the single name as it is. + // + if (Len == NETBIOS_NAME_SIZE) + { + NumberToAdd = 1; + } + else + { + NumberToAdd = NSUFFIXES; + } + for (nentries = 0; nentries < NumberToAdd; nentries++) + { + +// +// don't allocate memory here: AddToHashTable allocates, and this memory leaks! +// + + // for names less than 16 bytes, expand out to 16 and put a 16th byte + // on according to the suffix array + // + if (Len != NETBIOS_NAME_SIZE) + { + LmExpandName(temp, name, Suffix[nentries]); + } + else + { + CTEMemCopy(temp,name,NETBIOS_NAME_SIZE); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // do not add the name if it is already in the hash table + status = AddNotFoundToHashTable(NbtConfig.pRemoteHashTbl, + temp, + NbtConfig.pScope, + inaddr, + NBT_UNIQUE, + &pNameAddr); + + // if the name is already in the hash table, the status code is + // status pending. This could happen if the preloads are purged + // when one is still being referenced by another part of the code, + // and was therefore not deleted. We do not want to add the name + // twice, so we just change the ip address to agree with the preload + // value + // + if (status == STATUS_SUCCESS) + { // + // this prevents the name from being deleted by the Hash Timeout code + // + pNameAddr->RefCount = 2; + pNameAddr->NameTypeState |= PRELOADED | STATE_RESOLVED; + pNameAddr->NameTypeState &= ~STATE_CONFLICT; + pNameAddr->Ttl = 0xFFFFFFFF; + pNameAddr->Verify = REMOTE_NAME; + pNameAddr->AdapterMask = (CTEULONGLONG)-1; + + } + else + pNameAddr->IpAddress = inaddr; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (Len == NETBIOS_NAME_SIZE) + { + return(STATUS_SUCCESS); + } + + } + + return(STATUS_SUCCESS); + +} // PreloadEntry +//---------------------------------------------------------------------------- +VOID +RemovePreloads ( + ) + +/*++ + +Routine Description: + + This function removes preloaded entries from the remote hash table. + If it finds any of the preloaded entries are active with a ref count + above the base level of 2, then it returns true. + +Arguments: + + none +Return Value: + + none + +--*/ + +{ + tNAMEADDR *pNameAddr; + PLIST_ENTRY pHead,pEntry; + CTELockHandle OldIrq; + tHASHTABLE *pHashTable; + BOOLEAN FoundActivePreload=FALSE; + LONG i; + + // + // go through the remote table deleting names that have the PRELOAD + // bit set. + // + pHashTable = NbtConfig.pRemoteHashTbl; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + 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; + // + // Delete preloaded entries that are not in use by some other + // part of the code now. Note that preloaded entries start with + // a ref count of 2 so that the normal remote hashtimeout code + // will not delete them + // + if ((pNameAddr->NameTypeState & PRELOADED) && + (pNameAddr->RefCount == 2)) + { + // + // remove from the bucket that the name is in + // + RemoveEntryList(&pNameAddr->Linkage); + CTEMemFree((PVOID)pNameAddr); + } + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return; + +} + +//---------------------------------------------------------------------------- +LONG +PrimeCache( + IN PUCHAR path, + IN PUCHAR ignored, + IN BOOLEAN recurse, + OUT BOOLEAN *ignored2 + ) + +/*++ + +Routine Description: + + This function is called to prime the cache with entries in the lmhosts + file that are marked as preload entries. + + +Arguments: + + path - a fully specified path to a lmhosts file + ignored - unused + recurse - TRUE if process #INCLUDE, FALSE otherwise + +Return Value: + + Number of new cache entries that were added, or -1 if there was an + i/o error. + +--*/ + +{ + int nentries; + PUCHAR buffer; + PLM_FILE pfile; + NTSTATUS status; + int count, nwords; + unsigned long temp; + INCLUDE_STATE incstate; + PUCHAR token[MaxTokens]; + ULONG inaddr; + LINE_CHARACTERISTICS current; + UCHAR Name[NETBIOS_NAME_SIZE+1]; + ULONG IpAddr; + LIST_ENTRY TmpDomainList; + int domtoklen; + + CTEPagedCode(); + + if (!NbtConfig.EnableLmHosts) + { + return(STATUS_SUCCESS); + } + + InitializeListHead(&TmpDomainList); + // + // Check for infinitely recursive name lookup in a #INCLUDE. + // + if (LmpBreakRecursion(path, "") == TRUE) + { + return((unsigned long)0); + } + + pfile = LmOpenFile(path); + + if (!pfile) + { + return(-1); + } + + nentries = 0; + incstate = MustInclude; + domtoklen = strlen(DOMAIN_TOKEN); + + while (buffer = LmFgets(pfile, &count)) + { + +#ifndef VXD + if ((MAX_PRELOAD - nentries) < 3) + { + break; + } +#else + if ( nentries >= (MAX_PRELOAD - 3) ) + { + break; + } +#endif + + nwords = MaxTokens; + current = LmpGetTokens(buffer, token, &nwords); + + // if there is and error or no name on the line, then continue + // to the next line. + // + if ((current.l_category == ErrorLine) || (token[NbName] == NULL)) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("Nbt: Error line in Lmhost file\n")); + continue; + } + + if (current.l_preload) + { + status = ConvertDottedDecimalToUlong(token[IpAddress],&inaddr); + + if (NT_SUCCESS(status)) + { + status = PreloadEntry( token[NbName], + inaddr, + (unsigned int)current.l_nofnr); + if (NT_SUCCESS(status)) + { + nentries++; + } + } + } + switch ((ULONG)current.l_category) + { + case Domain: + if ((nwords - 1) < GroupName) + { + continue; + } + + // + // and add '1C' on the end + // + LmExpandName(Name, token[GroupName]+ domtoklen, SPECIAL_GROUP_SUFFIX); + + status = ConvertDottedDecimalToUlong(token[IpAddress],&IpAddr); + if (NT_SUCCESS(status)) + { + AddToDomainList(Name,IpAddr,&TmpDomainList); + } + + continue; + + case Include: + + if ((incstate == SkipInclude) || (nwords < 2)) + { + continue; + } + +#ifdef VXD + // + // the buffer which we read into is reused for the next file: we + // need the contents when we get back: back it up! + // if we can't allocate memory, just skip this include + // + if ( !BackupCurrentData(pfile) ) + { + continue; + } +#endif + + temp = LmInclude(token[1], PrimeCache, NULL, NULL); + +#ifdef VXD + // + // going back to previous file: restore the backed up data + // + RestoreOldData(pfile); +#endif + + if (temp != -1) + { + + if (incstate == TryToInclude) + { + incstate = SkipInclude; + } + nentries += temp; + continue; + } + + continue; + + case BeginAlternate: + ASSERT(nwords == 1); + incstate = TryToInclude; + continue; + + case EndAlternate: + ASSERT(nwords == 1); + incstate = MustInclude; + continue; + + default: + continue; + } + + } + + status = LmCloseFile(pfile); + ASSERT(status == STATUS_SUCCESS); + + // + // make this the new domain list + // + MakeNewListCurrent(&TmpDomainList); + + ASSERT(nentries >= 0); + return(nentries); + + +} // LmPrimeCache + +//---------------------------------------------------------------------------- +VOID +GetContext ( + OUT PVOID *pContext + ) + +/*++ + +Routine Description: + + This function is called to get the context value to check if a name + query has been cancelled or not. + +Arguments: + + Context - + +Return Value: + + none + +--*/ + + +{ + CTELockHandle OldIrq; + NBT_WORK_ITEM_CONTEXT *Context; + + // + // remove the Context value and return it. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (Context = LmHostQueries.Context) + { +#ifndef VXD + if ( NTCancelCancelRoutine( + ((tDGRAM_SEND_TRACKING *)(Context->pClientContext))->pClientIrp ) + == STATUS_CANCELLED ) + { + Context = NULL; + } + else +#endif // VXD + { + LmHostQueries.Context = NULL; + } + } + *pContext = Context; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); +} + + +//---------------------------------------------------------------------------- +VOID +ChangeStateOfName ( + IN ULONG IpAddress, + IN NBT_WORK_ITEM_CONTEXT *Context, + OUT PVOID *pContext, + BOOLEAN Lmhosts + ) + +/*++ + +Routine Description: + + This function changes the state of a name and nulls the Context + value in lmhostqueries. + When DNS processing calls this routine, the JointLock is already + held. + +Arguments: + + Context - + +Return Value: + + none + +--*/ + + +{ + NTSTATUS status; + CTELockHandle OldIrq; + tDGRAM_SEND_TRACKING *pTracker; + + if ( Context == NULL ) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + // + // change the state in the remote hash table + // + if (Lmhosts) + { + Context = LmHostQueries.Context; + LmHostQueries.Context = NULL; + } +#ifndef VXD + else + { + Context = DnsQueries.Context; + DnsQueries.Context = NULL; + } + NTClearContextCancel( Context ); +#endif + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + if (Context) + { + + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + pTracker->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pTracker->pNameAddr->NameTypeState |= STATE_RESOLVED; + + // convert broadcast addresses to zero since NBT interprets zero + // to be broadcast + // + if (IpAddress == (ULONG)-1) + { + IpAddress = 0; + } + pTracker->pNameAddr->IpAddress = IpAddress; + + // + // put the name record into the hash table if it is not already + // there. + // + pTracker->pNameAddr->AdapterMask = (CTEULONGLONG)-1; + status = AddRecordToHashTable(pTracker->pNameAddr,NbtConfig.pScope); + if (!NT_SUCCESS(status)) + { + // + // this will free the memory, so do not access this after this + // point + // + NbtDereferenceName(pTracker->pNameAddr); + pTracker->pNameAddr = NULL; + } + *pContext = Context; + } + else + *pContext = NULL; +} + +//---------------------------------------------------------------------------- +VOID +LmHostTimeout( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. It + marks all items in Lmhosts/Dns q as timed out and completes any that have + already timed out with status timeout. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + NBT_WORK_ITEM_CONTEXT *pWiContext; + LIST_ENTRY TmpHead; + + + //CTEQueueForNonDispProcessing(NULL,NULL,NULL,NonDispatchLmhostTimeout); + + InitializeListHead(&TmpHead); + CTESpinLockAtDpc(&NbtConfig.JointLock); + + // + // check the currently processing LMHOSTS entry + // + if (LmHostQueries.Context) + { + if (((NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context)->TimedOut) + { + + pWiContext = (NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context; + LmHostQueries.Context = NULL; + + // + // This asserts if the Irp has already been cancelled, which is bogus since + // it is possible to have the foll. swquence of events: + // 1. the Irp cancels into WaitForDnsIrpCancel; before it calls into DnsIrpCancelPaged, + // this routine is called. WaitForDnsIrpCancel waits at the JointLock. + // 2. so, here we try to clear the spinlock and discover that the Irp is cancelled, which is + // totally feasible. + // + + // NTClearContextCancel( pWiContext ); + (VOID)NTCancelCancelRoutine( ((tDGRAM_SEND_TRACKING *)(pWiContext->pClientContext))->pClientIrp ); + + CTESpinFreeAtDpc(&NbtConfig.JointLock); + RemoveNameAndCompleteReq(pWiContext,STATUS_TIMEOUT); + CTESpinLockAtDpc(&NbtConfig.JointLock); + } + else + { + + // + // restart the timer + // + pTimerQEntry->Flags |= TIMER_RESTART; + ((NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context)->TimedOut = TRUE; + + } + } +#ifndef VXD + // + // check the currently processing DNS entry + // + if (DnsQueries.Context) + { + if (((NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context)->TimedOut) + { + + pWiContext = (NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context; + DnsQueries.Context = NULL; + // + // This asserts if the Irp has already been cancelled, which is bogus since + // it is possible to have the foll. swquence of events: + // 1. the Irp cancels into WaitForDnsIrpCancel; before it calls into DnsIrpCancelPaged, + // this routine is called. WaitForDnsIrpCancel waits at the JointLock. + // 2. so, here we try to clear the spinlock and discover that the Irp is cancelled, which is + // totally feasible. + // + + // NTClearContextCancel( pWiContext ); + (VOID)NTCancelCancelRoutine( ((tDGRAM_SEND_TRACKING *)(pWiContext->pClientContext))->pClientIrp ); + + CTESpinFreeAtDpc(&NbtConfig.JointLock); + RemoveNameAndCompleteReq(pWiContext,STATUS_TIMEOUT); + CTESpinLockAtDpc(&NbtConfig.JointLock); + } + else + { + + // + // restart the timer + // + pTimerQEntry->Flags |= TIMER_RESTART; + ((NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context)->TimedOut = TRUE; + + } + } + // + // go through the Lmhost and Dns queries finding any that have timed out + // and put them on a tmp list then complete them below. + // + TimeoutQEntries(&DnsQueries.ToResolve,&TmpHead,&pTimerQEntry->Flags); +#endif + TimeoutQEntries(&LmHostQueries.ToResolve,&TmpHead,&pTimerQEntry->Flags); + + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + if (!IsListEmpty(&TmpHead)) + { + pHead = &TmpHead; + pEntry = pHead->Flink; + + while (pEntry != pHead) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("Nbt: Timing Out Lmhost/Dns Entry\n")); + + pWiContext = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + pEntry = pEntry->Flink; + RemoveEntryList(&pWiContext->Item.List); + + RemoveNameAndCompleteReq(pWiContext,STATUS_TIMEOUT); + } + } + + // null the timer if we are not going to restart it. + // + if (!(pTimerQEntry->Flags & TIMER_RESTART)) + { + LmHostQueries.pTimer = NULL; + } +} + +//---------------------------------------------------------------------------- +VOID +TimeoutQEntries( + IN PLIST_ENTRY pHeadList, + IN PLIST_ENTRY TmpHead, + OUT USHORT *pFlags + ) +/*++ + +Routine Description: + + This routine is called to find timed out entries in the queue of + lmhost or dns name queries. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + PLIST_ENTRY pEntry; + NBT_WORK_ITEM_CONTEXT *pWiContext; + + // + // Check the list of queued LMHOSTS entries + // + if (!IsListEmpty(pHeadList)) + { + pEntry = pHeadList->Flink; + + // + // restart the timer + // + *pFlags |= TIMER_RESTART; + + while (pEntry != pHeadList) + { + + pWiContext = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + pEntry = pEntry->Flink; + + if (pWiContext->TimedOut) + { + // + // save on a temporary list and complete below + // + RemoveEntryList(&pWiContext->Item.List); + InsertTailList(TmpHead,&pWiContext->Item.List); + } + else + { + pWiContext->TimedOut = TRUE; + } + } + } +} + +//---------------------------------------------------------------------------- +VOID +StartLmHostTimer( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NBT_WORK_ITEM_CONTEXT *pContext + ) + +/*++ +Routine Description + + This routine handles setting up a timer to time the Lmhost entry. + The Joint Spin Lock is held when this routine is called + +Arguments: + + +Return Values: + + VOID + +--*/ + +{ + NTSTATUS status; + tTIMERQENTRY *pTimerEntry; + + pContext->TimedOut = FALSE; + + // + // start the timer if it is not running + // + if (!LmHostQueries.pTimer) + { + + status = StartTimer( + NbtConfig.LmHostsTimeout, + NULL, // context value + NULL, // context2 value + LmHostTimeout, + NULL, + LmHostTimeout, + 0, + &pTimerEntry); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Start Timer to time Lmhost Qing for pConnEle= %X,\n", + pTracker->Connect.pConnEle)); + + if (NT_SUCCESS(status)) + { + LmHostQueries.pTimer = pTimerEntry; + + } + else + { + // we failed to get a timer, but that is not + // then end of the world. The lmhost query will just + // not timeout in 30 seconds. It may take longer if + // it tries to include a remove file on a dead machine. + // + LmHostQueries.pTimer = NULL; + } + } + +} +//---------------------------------------------------------------------------- +NTSTATUS +LmHostQueueRequest( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID pClientContext, + IN PVOID ClientCompletion, + IN PVOID CallBackRoutine, + IN PVOID pDeviceContext, + IN CTELockHandle OldIrq + ) +/*++ + +Routine Description: + + This routine exists so that LmHost requests will not take up more than + one executive worker thread. If a thread is busy performing an Lmhost + request, new requests are queued otherwise we could run out of worker + threads and lock up the system. + + The Joint Spin Lock is held when this routine is called + +Arguments: + pTracker - the tracker block for context + CallbackRoutine - the routine for the Workerthread to call + pDeviceContext - dev context that initiated this + +Return Value: + + +--*/ + +{ + NTSTATUS status = STATUS_UNSUCCESSFUL ; + NBT_WORK_ITEM_CONTEXT *pContext; + NBT_WORK_ITEM_CONTEXT *pContext2; + tDGRAM_SEND_TRACKING *pTrackClient; + PCTE_IRP pIrp; + BOOLEAN OnList; + + + pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('V')); + if (pContext) + { + + pContext->pTracker = pTracker; + pContext->pClientContext = pClientContext; + pContext->ClientCompletion = ClientCompletion; + + if (LmHostQueries.ResolvingNow) + { + // Lmhosts is busy resolving another name, so wait for it to return + // mean while, Queue the name query + // + InsertTailList(&LmHostQueries.ToResolve,&pContext->Item.List); + OnList = TRUE; + + } + else + { + LmHostQueries.Context = pContext; + LmHostQueries.ResolvingNow = TRUE; + OnList = FALSE; + +#ifndef VXD + pContext2 = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('W')); + + if (pContext2) + { + ExInitializeWorkItem(&pContext2->Item,CallBackRoutine,pContext2); + ExQueueWorkItem(&pContext2->Item,DelayedWorkQueue); + } +#else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + VxdScheduleDelayedCall( pTracker, pClientContext, ClientCompletion, CallBackRoutine,pDeviceContext ); + CTESpinLock(&NbtConfig.JointLock,OldIrq); +#endif + } + + // + // To prevent this name query from languishing on the Lmhost Q when + // a #include on a dead machine is trying to be openned, start the + // connection setup timer + // + StartLmHostTimer(pTracker,pContext); + + // + // this is the session setup tracker + // +#ifndef VXD + pTrackClient = (tDGRAM_SEND_TRACKING *)pClientContext; + if (pIrp = pTrackClient->pClientIrp) + { + // + // allow the client to cancel the name query Irp + // + // but do not call NTSetCancel... since it takes need to run + // at non DPC level, and it calls the completion routine + // which takes the JointLock that we already have. + + status = NTCheckSetCancelRoutine(pTrackClient->pClientIrp, + WaitForDnsIrpCancel,NULL); + + // + // since the name query is cancelled do not let lmhost processing + // handle it. + // + if (status == STATUS_CANCELLED) + { + if (OnList) + { + RemoveEntryList(&pContext->Item.List); + } + else + { + LmHostQueries.Context = NULL; + // + // do not set resolving now to False since the work item + // has been queued to the worker thread + // + } + + CTEMemFree(pContext); + + } + return(status); + } +#endif + status = STATUS_SUCCESS; + } + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +GetNameToFind( + OUT PUCHAR pName + ) + +/*++ + +Routine Description: + + This function is called to get the name to query from the LmHostQueries + list. + +Arguments: + + Context - + +Return Value: + + none + +--*/ + + +{ + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + NBT_WORK_ITEM_CONTEXT *Context; + PLIST_ENTRY pEntry; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // if the context value has been cleared then that name query has been + // cancelled, so check for another one. + // + if (!(Context = LmHostQueries.Context)) + { + // + // the current name query got canceled so see if there are any more + // to service + // + if (!IsListEmpty(&LmHostQueries.ToResolve)) + { + pEntry = RemoveHeadList(&LmHostQueries.ToResolve); + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + LmHostQueries.Context = Context; + } + else + { + // + // no more names to resolve, so clear the flag + // + LmHostQueries.ResolvingNow = FALSE; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(STATUS_UNSUCCESSFUL); + } + } + pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; + + + CTEMemCopy(pName,pTracker->pNameAddr->Name,NETBIOS_NAME_SIZE); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +VOID +ScanLmHostFile ( + IN PVOID Context + ) + +/*++ + +Routine Description: + + This function is called by the Executive Worker thread to scan the + LmHost file looking for a name. The name to query is on a list in + the DNSQueries structure. + +Arguments: + + Context - + +Return Value: + + none + +--*/ + + +{ + NTSTATUS status; + LONG IpAddress; + ULONG IpAddrsList[2]; + BOOLEAN bFound; + BOOLEAN bRecurse = TRUE; + PVOID pContext; + BOOLEAN DoingDnsResolve = FALSE; + UCHAR pName[NETBIOS_NAME_SIZE]; + ULONG LoopCount; + tDEVICECONTEXT *pDeviceContext; + tDGRAM_SEND_TRACKING *pTracker; + tDGRAM_SEND_TRACKING *pTracker0; + + CTEPagedCode(); + + +#ifdef VXD + pDeviceContext = ((DELAYED_CALL_CONTEXT *)Context)->pDeviceContext; + ASSERT( pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); +#endif + + // + // There is no useful info in Context, all the queued requests are on + // the ToResolve List, so free this memory now. + // + CTEMemFree(Context); + + LoopCount = 0; + while (TRUE) + { + + // get the next name on the linked list of LmHost name queries that + // are pending + // + pContext = NULL; + DoingDnsResolve = FALSE; + status = GetNameToFind(pName); + if ( !NT_SUCCESS(status)) + return; + LOCATION(0x63); + + LoopCount ++; + + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("Nbt: Lmhosts pName = %15.15s<%X>,LoopCount=%X\n", + pName,pName[15],LoopCount)); + + status = STATUS_TIMEOUT; + + // + // check if the name is in the lmhosts file or pass to Dns if + // DNS is enabled + // + IpAddress = 0; + if (NbtConfig.EnableLmHosts) + { + LOCATION(0x62); + +#ifdef VXD + // + // if for some reason PrimeCache failed at startup time + // then this is when we retry. + // + if (!CachePrimed) + { + if ( PrimeCache( NbtConfig.pLmHosts, NULL, TRUE, NULL) != -1 ) + { + CachePrimed = TRUE ; + } + } +#endif + IpAddress = LmGetIpAddr(NbtConfig.pLmHosts, + pName, + bRecurse, + &bFound); + +#ifdef VXD + // + // hmmm.. didn't find it in lmhosts: try hosts (if Dns is enabled) + // + if ( (IpAddress == (ULONG)0) && (NbtConfig.ResolveWithDns) ) + { + IpAddress = LmGetIpAddr(NbtConfig.pHosts, + pName, + bRecurse, + &bFound); + } +#endif + } + + + if (IpAddress == (ULONG)0) + { + // check if the name query has been cancelled + // + LOCATION(0x61); + GetContext(&pContext); + // + // for some reason we didn't find our context: maybe cancelled. + // Go back to the big while loop... + // + if (!pContext) + continue; + + // + // see if the name is in the 11.101.4.26 format: if so, we got the + // ipaddr! Use that ipaddr to get the server name + // + pTracker = ((NBT_WORK_ITEM_CONTEXT *)pContext)->pTracker; + + pTracker0 = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + + if ( pTracker0->Flags & (REMOTE_ADAPTER_STAT_FLAG|SESSION_SETUP_FLAG|DGRAM_SEND_FLAG) ) + { + IpAddress = Nbt_inet_addr(pTracker->pNameAddr->Name); + } + else + { + IpAddress = 0; + } + // + // yes, the name is the ipaddr: StartIpAddrToSrvName() starts + // the process of finding out server name for this ipaddr + // + if (IpAddress) + { + IpAddrsList[0] = IpAddress; + IpAddrsList[1] = 0; + + // + // if this is in response to an adapter stat command (e.g.nbtstat -a) then + // don't try to find the server name (using remote adapter status!) + // + if (pTracker0->Flags & REMOTE_ADAPTER_STAT_FLAG) + { + // + // change the state to resolved if the name query is still pending + // + ChangeStateOfName(IpAddress,(NBT_WORK_ITEM_CONTEXT *)pContext,&pContext,TRUE); + + status = STATUS_SUCCESS; + } + else + { + + StartIpAddrToSrvName(pContext, IpAddrsList, TRUE); + // + // done with this name query: go back to the big while loop + // + continue; + } + } + + // + // + // inet_addr failed. If DNS resolution is enabled, try DNS + else if (NbtConfig.ResolveWithDns) + { + status = DoDnsResolve(pContext); + + if (NT_SUCCESS(status)) + { + DoingDnsResolve = TRUE; + } + } + } + + else // if (IpAddress != (ULONG)0) + { + // + // change the state to resolved if the name query is still pending + // + ChangeStateOfName(IpAddress,NULL,&pContext,TRUE); + + status = STATUS_SUCCESS; + } + + // + // if DNS gets involved, then we wait for that to complete before calling + // completion routine. + // + if (!DoingDnsResolve) + { + LOCATION(0x60); + RemoveNameAndCompleteReq((NBT_WORK_ITEM_CONTEXT *)pContext, + status); + + } + + }// of while(TRUE) + +} + +//---------------------------------------------------------------------------- +VOID +RemoveNameAndCompleteReq ( + IN NBT_WORK_ITEM_CONTEXT *pContext, + IN NTSTATUS status + ) + +/*++ + +Routine Description: + + This function removes the name, cleans up the tracker + and then completes the clients request. + +Arguments: + + Context - + +Return Value: + + none + +--*/ + + +{ + tDGRAM_SEND_TRACKING *pTracker; + PVOID pClientContext; + PVOID pClientCompletion; + CTELockHandle OldIrq; + + // if pContext is null the name query was cancelled during the + // time it took to go read the lmhosts file, so don't do this + // stuff + // + if (pContext) + { + pTracker = pContext->pTracker; + pClientCompletion = pContext->ClientCompletion; + pClientContext = pContext->pClientContext; + + CTEMemFree(pContext); + +#ifndef VXD + + // + // clear out the cancel routine if there is an irp involved + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + NTCancelCancelRoutine( ((tDGRAM_SEND_TRACKING *)(pClientContext))->pClientIrp ); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); +#endif + + // remove the name from the hash table, since it did not resolve + if ((status != STATUS_SUCCESS) && pTracker && pTracker->pNameAddr) + { + RemoveName(pTracker->pNameAddr); + } + // free the tracker and call the completion routine. + // + if (pTracker) + { + DereferenceTracker(pTracker); + } + + if (pClientCompletion) + { + CompleteClientReq(pClientCompletion, + pClientContext, + status); + } + } +} + +//---------------------------------------------------------------------------- +VOID +RemoveName ( + IN tNAMEADDR *pNameAddr + ) + +/*++ + +Routine Description: + + This function dereferences the pNameAddr and sets the state to Released + just incase the dereference does not delete the entry right away, due to + another outstanding reference against the name. + +Arguments: + + Context - + +Return Value: + + none + +--*/ + + +{ + CTELockHandle OldIrq; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_RELEASED; + pNameAddr->pTracker = NULL; + NbtDereferenceName(pNameAddr); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + +} +//---------------------------------------------------------------------------- +// +// Alternative to the c-runtime +// +char * +#ifndef VXD // NT needs the CRTAPI1 modifier +_CRTAPI1 +#endif +strchr( const char * pch, int ch ) +{ + while ( *pch != ch && *pch ) + pch++ ; + + if ( *pch == ch ) // Include '\0' in comparison + { + return (char *) pch ; + } + + return NULL ; +} +//---------------------------------------------------------------------------- +// +// Alternative to the c-runtime +// +#ifndef VXD +PCHAR +Nbtstrcat( PUCHAR pch, PUCHAR pCat, LONG Len ) +{ + STRING StringIn; + STRING StringOut; + + RtlInitAnsiString(&StringIn, pCat); + RtlInitAnsiString(&StringOut, pch); + StringOut.MaximumLength = (USHORT)Len; + // + // increment to include the null on the end of the string since + // we want that on the end of the final product + // + StringIn.Length++; + RtlAppendStringToString(&StringOut,&StringIn); + + + return(pch); +} +#else +#define Nbtstrcat( a,b,c ) strcat( a,b ) +#endif + + + + diff --git a/private/ntos/nbt/nbt/proxy.c b/private/ntos/nbt/nbt/proxy.c new file mode 100644 index 000000000..f4ae4117b --- /dev/null +++ b/private/ntos/nbt/nbt/proxy.c @@ -0,0 +1,282 @@ +// +// +// proxy.c +// +// This file contains the Proxy related functions that implement the Bnode +// proxy functionality. This allows a Bnode to make use of a Name Service +// transparently since the proxy code picks up the Bnode Query broadcasts directly +// and either answers them directly or queries the NS and then answers them +// later. +// code + +#include "nbtprocs.h" + +VOID +ProxyClientCompletion( + IN PVOID pContext, + IN NTSTATUS status + ); + + +#ifdef PROXY_NODE +//---------------------------------------------------------------------------- +NTSTATUS +RegOrQueryFromNet( + IN BOOL fReg, + IN tDEVICECONTEXT *pDeviceContext, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNameSize, + IN PCHAR pNameInPkt, + IN PUCHAR pScope + ) +/*++ + +Routine Description: + + This function handles a name registration/name overwrite or a name + query that comes over the subnet. It checks the remote name table. If + the name is there, the function simply returns. If the name is not + there, the function calls QueryNameOnNet to add the name to the remote table + (in the resolving state) and to query the NS. + + Note: If the name is there in the table, it may or may not have the same + address as the registration that we got or it may be of a different + type. Not doing anything for this case is ok as explained below. + + +Arguments: + + +Return Value: + + NTSTATUS - success or not - failure means no response to the net + +Called By: + QueryFromNet() in inbound.c, NameSrvHndlrNotOs() in hndlrs.c +--*/ +{ + tGENERALRR *pResrcRecord; + ULONG IpAddress; + BOOLEAN bGroupName; + CTELockHandle OldIrq; + + + // + // if we have heard a registration on the net, get the IP address + // and the type of registration (unique/group) from the packet. + // + // if we have heard a query, use default values for the above two + // fields + // + if (fReg) + { + // get the Ip address out of the Registration request + pResrcRecord = (tGENERALRR *) + ((ULONG)&pNameHdr->NameRR.NetBiosName[lNameSize]); + IpAddress = ntohl(pResrcRecord->IpAddress); + bGroupName = pResrcRecord->Flags & FL_GROUP; + } + else + { + IpAddress = 0; + bGroupName = NBT_UNIQUE; //default value + } + // + // The name is not there in the remote name table. + // Add it in the RESOLVING state and send a name query + // to the NS. + // + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + QueryNameOnNet( + pNameInPkt, + pScope, + IpAddress, + bGroupName, + NULL, //client context + ProxyClientCompletion, + PROXY, + NULL, //we want to add the name(pNameAddr = NULL) + pDeviceContext, + NULL, //no tracker block + &OldIrq + ); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +VOID +ProxyTimerComplFn ( + IN PVOID pContext, + IN PVOID pContext2, + IN tTIMERQENTRY *pTimerQEntry + ) + +/*++ + +Routine Description: + + This function either deletes the name from the remote name table + if fReg is FALSE (i.e. the timer has expired on a name query + sent by the Proxy on behalf of a node doing a name query) or changes + the state to RESOLVED if fReg is TRUE (i.e. the timer has expired + on a name query sent on behalf of a node doing name registration) + +Arguments: + pfReg - indicates whether the timer expiry is for a name + query + +Return Value: + + NTSTATUS - success or not - failure means no response to the net + +--*/ +{ + + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + tNAMEADDR *pNameAddr; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + if (pTimerQEntry) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (--pTimerQEntry->Retries) + { + // do send below... + } + else + { + + if (pTracker->Flags & NBT_NAME_SERVER) + { + // + // Can't reach the name server, so try the backup + // + pTracker->Flags &= ~NBT_NAME_SERVER; + pTracker->Flags |= NBT_NAME_SERVER_BACKUP; + + // set the retry count again + pTimerQEntry->Retries = NbtConfig.uNumRetries; + + } + else + { + + + // + // If pContext2 is not 0, it means that this timer function was + // called by the proxy for a query which it sent on hearing a + // registration on the net. If pContext2 is 0, it means + // that the timer function was called by the proxy for a query + // which it sent on hearing a query on the net. + // + + // + // Mark the entry as released. Do not dereference the name + // The entry will remain in the remote hash table. When the proxy + // code sees a query or registration for a released entry in the + // cache it does not query the name server. This cuts down on + // name server traffic. The released entries are removed from + // the cache at cache timer expiry (kept small). + + //************************************ + + // Changed: Dereference the name because the name query timed + // out meaning that we did not contact WINS, therefore we + // do not know if the name is valid or not! + // + + pNameAddr = pTracker->pNameAddr; + pTimerQEntry->ClientCompletion = NULL; + + pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pNameAddr->NameTypeState |= STATE_RELEASED; + + // remove the link from the name table to this timer block + CHECK_PTR(pNameAddr); + pNameAddr->pTimer = NULL; + +// NBT_PROXY_DBG(("ProxyTimerComplFn: State of name %16.16s(%X) changed to (%s)\n", pTracker->pNameAddr->Name, pTracker->pNameAddr->Name[15], "RELEASED")); + + + // Remove from the pending Queries list - and put into the hash + // table for 1 minute so we do not beat up on WINS if it is down + // or slow right now. + // + RemoveEntryList(&pNameAddr->Linkage); + InitializeListHead(&pNameAddr->Linkage); + + status = AddRecordToHashTable(pNameAddr,NbtConfig.pScope); + if (!NT_SUCCESS(status)) + { + NbtDereferenceName(pNameAddr); + } + else + { + pNameAddr->TimeOutCount = 1; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // return the tracker block to its queue + DereferenceTracker(pTracker); + + return; + } + } + + pTracker->RefCount++; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + NULL,NULL,NULL, + 0,0, + eNAME_QUERY, + TRUE); + + DereferenceTracker(pTracker); + pTimerQEntry->Flags |= TIMER_RESTART; + + } + else + { + // return the tracker block to its queue + DereferenceTrackerNoLock(pTracker); + } + + return; +} + +//---------------------------------------------------------------------------- +VOID +ProxyClientCompletion( + IN PVOID pContext, + IN NTSTATUS status + ) + +/*++ + +Routine Description: + + This function does nothing since the proxy does not need to do anything + when a name query succeeds. The code in inbound.c does all that + is necessary - namely put the name in the name table. + +Arguments: + +Return Value: + + +--*/ +{ + +} + +#endif diff --git a/private/ntos/nbt/nbt/sources.inc b/private/ntos/nbt/nbt/sources.inc new file mode 100644 index 000000000..0327a43e3 --- /dev/null +++ b/private/ntos/nbt/nbt/sources.inc @@ -0,0 +1,65 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood 8/2/91 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nbt + + + +NTPROFILEINPUT=yes + +TARGETNAME=nbt +TARGETPATH=obj +TARGETTYPE=LIBRARY + +TARGETLIBS= + +INCLUDES=..\..\inc;..\..\..\inc;..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -DPROXY_NODE -D_NTDRIVER_ -DRASAUTODIAL -D_PNP_POWER -D_IO_DELETE_DEVICE_SUPPORTED + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= \ + ..\hashtbl.c \ + ..\hndlrs.c \ + ..\inbound.c \ + ..\name.c \ + ..\namesrv.c \ + ..\nbtutils.c \ + ..\proxy.c \ + ..\timer.c \ + ..\udpsend.c \ + ..\parse.c \ + ..\init.c + + +PRECOMPILED_INCLUDE=..\..\nbtprocs.h +PRECOMPILED_PCH=nbtprocs.pch +PRECOMPILED_OBJ=nbtprocs.obj +
\ No newline at end of file diff --git a/private/ntos/nbt/nbt/timer.c b/private/ntos/nbt/nbt/timer.c new file mode 100644 index 000000000..ee6b315c1 --- /dev/null +++ b/private/ntos/nbt/nbt/timer.c @@ -0,0 +1,692 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Timer.c + +Abstract: + + + This file contains the code to implement timer functions. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "timer.h" + +// the timer Q +tTIMERQ TimerQ; + +NTSTATUS +DereferenceTimer( + IN tTIMERQENTRY *pTimerEntry + ); + + +//---------------------------------------------------------------------------- +NTSTATUS +InitTimerQ( + IN int NumInQ) +/*++ + +Routine Description: + + This routine calls InitQ to initialize the timer Q. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + return(InitQ(NumInQ,&TimerQ,sizeof(tTIMERQENTRY))); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +InitQ( + IN int NumInQ, + IN tTIMERQ *pTimerQ, + IN USHORT uSize) +/*++ + +Routine Description: + + This routine sets up the timer Q to have NumInQ entries to start with. + These are blocks allocated for timers so that ExAllocatePool does not + need to be done later. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + tTIMERQENTRY *pTimerEntry; + + CTEInitLock(&pTimerQ->SpinLock); + + InitializeListHead(&pTimerQ->ActiveHead); + InitializeListHead(&pTimerQ->FreeHead); + + // allocate memory for the free list + while(NumInQ--) + { + pTimerEntry = (tTIMERQENTRY *)CTEAllocInitMem(uSize); + if (!pTimerEntry) + { + KdPrint(("Unable to allocate memory!! - for the timer Q\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + else + { + InsertHeadList(&pTimerQ->FreeHead,&pTimerEntry->Linkage); + } + } + + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +VOID +StopTimerAndCallCompletion( + IN tTIMERQENTRY *pTimer, + IN NTSTATUS status, + IN CTELockHandle OldIrq + ) +/*++ + +Routine Description: + + This routine calls the routine to stop the timer and then calls the + completion routine if it hasn't been called yet. + This routine is called with the JointLock held. + +Arguments: + +Return Value: + + there is no return value + + +--*/ +{ + NTSTATUS Locstatus; + COMPLETIONCLIENT pCompletion; + PVOID pContext; + + + if (pTimer) + { + Locstatus = StopTimer(pTimer,&pCompletion,&pContext); + + // this will complete the irp(s) back to the clients + if (pCompletion) + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CompleteClientReq(pCompletion, + pContext, + STATUS_TIMEOUT); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + } + + } +} +//---------------------------------------------------------------------------- +NTSTATUS +InterlockedCallCompletion( + IN tTIMERQENTRY *pTimer, + IN NTSTATUS status + ) +/*++ + +Routine Description: + + This routine calls the completion routine if it hasn't been called + yet, by first getting the JointLock spin lock and then getting the + Completion routine ptr. If the ptr is null then the completion routine + has already been called. Holding the Spin lock interlocks this + with the timer expiry routine to prevent more than one call to the + completion routine. + +Arguments: + +Return Value: + + there is no return value + + +--*/ +{ + CTELockHandle OldIrq; + COMPLETIONCLIENT pClientCompletion; + + // to synch. with the the Timer completion routines, Null the client completion + // routine so it gets called just once, either from here or from the + // timer completion routine setup when the timer was started.(in namesrv.c) + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pClientCompletion = pTimer->ClientCompletion; + pTimer->ClientCompletion = NULL; + + + if (pClientCompletion) + { + // remove the link from the name table to this timer block + CHECK_PTR(((tNAMEADDR *)pTimer->pCacheEntry)); + ((tNAMEADDR *)pTimer->pCacheEntry)->pTimer = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + (*pClientCompletion)( + pTimer->ClientContext, + status); + return(STATUS_SUCCESS); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return(STATUS_UNSUCCESSFUL); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +GetEntry( + IN PLIST_ENTRY pQHead, + IN USHORT uSize, + OUT PLIST_ENTRY *ppEntry) +/*++ + +Routine Description: + + This routine gets a free block from the Qhead passed in, and if the + list is empty it allocates another memory block for the queue. + NOTE: this function is called with the spin lock held on the Q structure + that contains Qhead. + +Arguments: + +Return Value: + + The function value is the status of the operation. + + +--*/ +{ + NTSTATUS status; + tTIMERQENTRY *pTimerEntry; + + status = STATUS_SUCCESS; + if (!IsListEmpty(pQHead)) + { + *ppEntry = RemoveHeadList(pQHead); + + } + else + { + pTimerEntry = (tTIMERQENTRY *)CTEAllocInitMem(uSize); + if (!pTimerEntry) + { + KdPrint(("Unable to allocate memory!! - for the timer Q\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // Note******: + // be sure to return a ptr to the Linkage ptr in the timerEntry + // structure, since the caller will use CONTAINING_RECORD to + // backup to the start of the record, since Linkage is not at + // the start of the record!!! + // + *ppEntry = &pTimerEntry->Linkage; + + } + + } + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +LockedStartTimer( + IN ULONG DeltaTime, + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID CompletionRoutine, + IN PVOID ContextClient, + IN PVOID CompletionClient, + IN USHORT Retries, + IN tNAMEADDR *pNameAddr, + IN BOOLEAN CrossLink + ) +/*++ + +Routine Description: + + This routine starts a timer. + +Arguments: + + The value passed in is in milliseconds - must be converted to 100ns + so multiply to 10,000 +Return Value: + + The function value is the status of the operation. + + +--*/ +{ + tTIMERQENTRY *pTimerEntry; + NTSTATUS status; + CTELockHandle OldIrq; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // get a free timer block + status = StartTimer( + DeltaTime, + pTracker, + NULL, + CompletionRoutine, + ContextClient, + CompletionClient, + Retries, + &pTimerEntry); + + if (NT_SUCCESS(status)) + { + // this boolean is passed in to determine which way + // to cross link the timerEntry and either the pNameaddr or + // the tracker, depending on who is calling this routine. + if (CrossLink) + { + // cross link the timer and the name address record + pTimerEntry->pCacheEntry = pNameAddr; + pNameAddr->pTimer = pTimerEntry; + } + else + { + pTracker->Connect.pNameAddr = pNameAddr; + pTracker->Connect.pTimer = pTimerEntry; + } + + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +StartTimer( + IN ULONG DeltaTime, + IN PVOID Context, + IN PVOID Context2, + IN PVOID CompletionRoutine, + IN PVOID ContextClient, + IN PVOID CompletionClient, + IN USHORT Retries, + OUT tTIMERQENTRY **ppTimerEntry) +/*++ + +Routine Description: + + This routine starts a timer. + +Arguments: + + The value passed in is in milliseconds - must be converted to 100ns + so multiply to 10,000 +Return Value: + + The function value is the status of the operation. + + +--*/ +{ + tTIMERQENTRY *pTimerEntry; + PLIST_ENTRY pEntry; + NTSTATUS status; + + // get a free timer block + status = GetEntry(&TimerQ.FreeHead,sizeof(tTIMERQENTRY),&pEntry); + if (NT_SUCCESS(status)) + { + + pTimerEntry = CONTAINING_RECORD(pEntry,tTIMERQENTRY,Linkage); + + pTimerEntry->DeltaTime = DeltaTime; + pTimerEntry->RefCount = 1; + // + // this is the context value and routine called when the timer expires, + // called by TimerExpiry below. + // + pTimerEntry->Context = Context; + pTimerEntry->Context2 = Context2; + pTimerEntry->CompletionRoutine = CompletionRoutine; + pTimerEntry->Flags = 0; // no flags + + // now fill in the client's completion routines that ultimately get called + // after one or more timeouts... + pTimerEntry->ClientContext = (PVOID)ContextClient; + pTimerEntry->ClientCompletion = (COMPLETIONCLIENT)CompletionClient; + pTimerEntry->Retries = Retries; + + CTEInitTimer(&pTimerEntry->VxdTimer); + CTEStartTimer(&pTimerEntry->VxdTimer, + pTimerEntry->DeltaTime, + (CTEEventRtn)TimerExpiry, + (PVOID)pTimerEntry); + + // check if there is a ptr to return + if (ppTimerEntry) + { + *ppTimerEntry = pTimerEntry; + } + + // put on list + + InsertHeadList(&TimerQ.ActiveHead,pEntry); + } + else + { + KdPrint(("StartTimer: Unable to get a timer block\n")); + } + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +DereferenceTimer( + IN tTIMERQENTRY *pTimerEntry + ) +/*++ + +Routine Description: + + This routine kills a timer if the reference count goes to zero. Called + with the TimerQ spin lock held. + +Arguments: + + +Return Value: + + returns the reference count after the decrement + +--*/ +{ + NTSTATUS status; + COMPLETIONROUTINE CompletionRoutine; + PVOID Context; + PVOID Context2; + + if (pTimerEntry->RefCount == 0) + { + // the expiry routine is not currently running so we can call the + // completion routine and remove the timer from the active timer Q + + CompletionRoutine = (COMPLETIONROUTINE)pTimerEntry->CompletionRoutine; + Context = pTimerEntry->Context; + Context2 = pTimerEntry->Context2; + + // move to the free list + RemoveEntryList(&pTimerEntry->Linkage); + InsertTailList(&TimerQ.FreeHead,&pTimerEntry->Linkage); + + // release any tracker block hooked to the timer entry.. This could + // be modified to not call the completion routine if there was + // no context value... ie. for those timers that do not have anything + // to cleanup ...however, for now we require all completion routines + // to have a if (pTimerQEntry) if around the code so when it gets hit + // from this call it does not access any part of pTimerQEntry. + // + if (CompletionRoutine) + { + // call the completion routine so it can clean up its own buffers + // the routine that called this one will call the client's completion + // routine. A NULL timerEntry value indicates to the routine that + // cleanup should be done. + (VOID)(*CompletionRoutine)( + Context, + Context2, + NULL); + + } + status = STATUS_SUCCESS; + } + else + { + status = STATUS_UNSUCCESSFUL; + KdPrint(("Nbt:Unable to put timer back in free Q - RefCount = %X, %X\n", + pTimerEntry->RefCount,pTimerEntry)); + } + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +StopTimer( + IN tTIMERQENTRY *pTimerEntry, + OUT COMPLETIONCLIENT *ppClientCompletion, + OUT PVOID *ppContext) +/*++ + +Routine Description: + + This routine stops a timer. Must be called with the Joint lock held. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + + // null the client completion routine so that it can't be called again + // accidently + if (ppClientCompletion) + { + + *ppClientCompletion = NULL; + } + + // it is possible that the timer expiry routine has just run and the timer + // has not been restarted, so check the refcount, it will be zero if the + // timer was not restarted and 2 if the timer expiry is currently running. + if (pTimerEntry->RefCount == 1) + { + if (!(pTimerEntry->Flags & TIMER_NOT_STARTED)) + CTEStopTimer( (CTETimer *)&pTimerEntry->VxdTimer ); + + status = STATUS_SUCCESS; + + // this allows the caller to call the client's completion routine with + // the context value. + if (ppClientCompletion) + { + *ppClientCompletion = pTimerEntry->ClientCompletion; + } + if (ppContext) + { + *ppContext = pTimerEntry->ClientContext; + } + pTimerEntry->ClientCompletion = NULL; + pTimerEntry->RefCount = 0; + + status = DereferenceTimer(pTimerEntry); + + + } + else + if (pTimerEntry->RefCount == 2) + { + // the timer expiry completion routines must set this routine to + // null with the spin lock held to synchronize with this stop timer + // routine. Likewise that routine checks this value too, to synchronize + // with this routine. + // + if (pTimerEntry->ClientCompletion) + { + // this allows the caller to call the client's completion routine with + // the context value. + if (ppClientCompletion) + { + *ppClientCompletion = pTimerEntry->ClientCompletion; + } + if (ppContext) + { + *ppContext = pTimerEntry->ClientContext; + } + // so that the timer completion routine cannot also call the client + // completion routine. + pTimerEntry->ClientCompletion = NULL; + + } + + // signal the TimerExpiry routine that the timer has been cancelled + // + pTimerEntry->RefCount++; + status = STATUS_UNSUCCESSFUL; + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + return(status); + +} + + +//---------------------------------------------------------------------------- +VOID +TimerExpiry( +#ifndef VXD + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArg1, + IN PVOID SystemArg2 +#else + IN CTEEvent * pCTEEvent, + IN PVOID DeferredContext +#endif + ) +/*++ + +Routine Description: + + This routine is the timer expiry completion routine. It is called by the + kernel when the timer expires. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + tTIMERQENTRY *pTimerEntry; + CTELockHandle OldIrq1; + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // get the timer Q list entry from the context passed in + pTimerEntry = (tTIMERQENTRY *)DeferredContext; + + + if (pTimerEntry->RefCount == 0) + { + // the timer has been cancelled already! + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; + } + + // increment the reference count because we are handling a timer completion + // now + pTimerEntry->RefCount++; + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // call the completion routine passing the context value + pTimerEntry->Flags &= ~TIMER_RESTART; // incase the clients wants to restart the timer + (*(COMPLETIONROUTINE)pTimerEntry->CompletionRoutine)( + pTimerEntry->Context, + pTimerEntry->Context2, + pTimerEntry); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + pTimerEntry->RefCount--; + if (pTimerEntry->Flags & TIMER_RESTART) + { + if (pTimerEntry->RefCount == 2) + { + // the timer was stopped during the expiry processing, so call the + // deference routine + // + pTimerEntry->RefCount = 0; + DereferenceTimer(pTimerEntry); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return; + } + else + { + + CTEStartTimer(&pTimerEntry->VxdTimer, + pTimerEntry->DeltaTime, + (CTEEventRtn)TimerExpiry, + (PVOID)pTimerEntry); + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; + + } + else + { + + // move to the free list after setting the reference count to zero + // since this tierm block is no longer active. + // + pTimerEntry->RefCount = 0; + RemoveEntryList(&pTimerEntry->Linkage); + InsertTailList(&TimerQ.FreeHead,&pTimerEntry->Linkage); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + +} + diff --git a/private/ntos/nbt/nbt/udpsend.c b/private/ntos/nbt/nbt/udpsend.c new file mode 100644 index 000000000..b30208b50 --- /dev/null +++ b/private/ntos/nbt/nbt/udpsend.c @@ -0,0 +1,1742 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Udpsend.c + +Abstract: + + + This file handles building udp(and Tcp) requests, formated to the Tdi specification + to pass to Tdiout. Tdiout formats the request in an Os specific manner and + passes it on to the transport. + + This file handles name service type functions such as query name or + register name, datagram sends. It also handles building Tcp packets. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + + +#include "nbtprocs.h" // procedure headings + + +VOID +SessionRespDone( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo); +VOID +NsDgramSendCompleted( + PVOID pContext, + NTSTATUS status, + ULONG lInfo + ); +VOID +NDgramSendCompleted( + PVOID pContext, + NTSTATUS status, + ULONG lInfo + ); + +//---------------------------------------------------------------------------- +NTSTATUS +UdpSendNSBcast( + IN tNAMEADDR *pNameAddr, + IN PCHAR pScope, + IN tDGRAM_SEND_TRACKING *pSentList, + IN PVOID pCompletionRoutine, + IN PVOID pClientContext, + IN PVOID pClientCompletion, + IN ULONG Retries, + IN ULONG Timeout, + IN enum eNSTYPE eNsType, + IN BOOL SendFlag + ) +/*++ + +Routine Description: + + This routine sends a name registration or a name query + as a broadcast on the subnet or directed to the name server. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + tNAMEHDR *pNameHdr; + ULONG uLength; + tDEVICECONTEXT *pDeviceContext; + CTELockHandle OldIrq; + ULONG UNALIGNED *pHdrIpAddress; + ULONG IpAddress; + USHORT Port; + USHORT NameType; + tDGRAM_SEND_TRACKING *pTracker; + tTIMERQENTRY *pTimerQEntry; + PFILE_OBJECT pFileObject; + + + if (pNameAddr->NameTypeState & (NAMETYPE_GROUP | NAMETYPE_INET_GROUP)) + { + NameType = NBT_GROUP; + } + else + NameType = NBT_UNIQUE; + + // build the correct type of pdu depending on the request type + + status = GetTracker(&pTracker); + if (!NT_SUCCESS(status)) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pHdrIpAddress = (ULONG UNALIGNED *)CreatePdu( + pNameAddr->Name, + pScope, + 0L, // we don't know the IP address yet + NameType, + eNsType, + (PVOID)&pNameHdr, + &uLength, + pSentList); + + if (pHdrIpAddress) + { + // + // change the dgram header for name refreshes + // + if (eNsType == eNAME_REFRESH) + { + pNameHdr->OpCodeFlags = NbtConfig.OpRefresh; + } + else + if ( (eNsType == eNAME_QUERY) +#ifdef VXD + || (eNsType == eDNS_NAME_QUERY) +#endif + ) + { + pHdrIpAddress = NULL; + } + } + else + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Failed to Create Pdu to send to WINS PduType= %X\n", + eNsType)); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + + // fill in the Datagram hdr info in the tracker structure. + // There is never a client buffer to send. + // + // Set the fields here instead of after the timer is started + // since they may be accessed by the Timer completion function + // + pTracker->SendBuffer.pDgramHdr = pNameHdr; + pTracker->SendBuffer.HdrLength = uLength; + pTracker->SendBuffer.pBuffer = NULL; + pTracker->SendBuffer.Length = 0; + pTracker->pNameAddr = pNameAddr; + + pTracker->pDeviceContext = pSentList->pDeviceContext; + + pSentList->pNameAddr = pNameAddr; + pSentList->TransactionId = pNameHdr->TransactId; // save for response checks. + + + pTracker->pHdrIpAddress = pHdrIpAddress; + + // start the timer now...We didn't start it before because it could + // have expired during the dgram setup, perhaps before the Tracker was + // fully setup. + // + if (Timeout) + { + status = StartTimer( + Timeout, + (PVOID)pSentList, // context value + NULL, + pCompletionRoutine, + pClientContext, + pClientCompletion, + (USHORT)Retries, + &pTimerQEntry + ); + + if (!NT_SUCCESS(status)) + { + // we need to differentiate the timer failing versus lack + // of resources + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEMemFree(pNameHdr); + + FreeTracker(pTracker,RELINK_TRACKER); + + return(STATUS_INVALID_PARAMETER_6); + } + // + // Cross link the nameaddr and the timer so we can stop the timer + // when the name query response occurs + // + pTimerQEntry->pCacheEntry = pNameAddr; + pNameAddr->pTimer = pTimerQEntry; + } + + pDeviceContext = pSentList->pDeviceContext; + + // + // Check the Flag value in the tracker and see if we should do a broadcast + // or a directed send to the name server + // + if (pSentList->Flags & NBT_BROADCAST) + { + // + // set the broadcast bit in the header to be ON since this may be + // an M or MS node that is changing to broadcast from directed sends. + // + ((PUCHAR)pTracker->SendBuffer.pDgramHdr)[3] |= FL_BROADCAST_BYTE; + + Port = NBT_NAMESERVICE_UDP_PORT; + + IpAddress = pDeviceContext->BroadcastAddress; + } + else + { + // + // turn off the broadcast bit in the header since this may be + // an M or MS node that is changing to directed sends from broadcasts. + // + ((PUCHAR)pTracker->SendBuffer.pDgramHdr)[3] &= ~FL_BROADCAST_BYTE; + + // check for a zero first byte in the name passed to the name server + ASSERT(((PUCHAR)pTracker->SendBuffer.pDgramHdr)[12]); + + // + // for Multihomed hosts, UNIQUE name registrations use a special new + // code (0x0F) to tell the name server this is a multihomed name that + // will have several ip addresses + // + if (NbtConfig.MultiHomed && + ((eNsType == eNAME_REGISTRATION) && (NameType == NBT_UNIQUE))) + { + // if it is a multihomed host, then use a new special registration + // opcode (0xF) + // + ((PUCHAR)pTracker->SendBuffer.pDgramHdr)[2] |= OP_REGISTER_MULTI; + + } + + Port = NbtConfig.NameServerPort; + + // name srvr, backup name srvr, dns srvr, backup dnr srvr:which one? + + if (pSentList->Flags & NBT_NAME_SERVER) + { + IpAddress = pDeviceContext->lNameServerAddress; + } + else +#ifndef VXD + { + IpAddress = pDeviceContext->lBackupServer; + } +#else + if (pSentList->Flags & NBT_NAME_SERVER_BACKUP) + { + IpAddress = pDeviceContext->lBackupServer; + } + else + if (pSentList->Flags & NBT_DNS_SERVER) + { + IpAddress = pDeviceContext->lDnsServerAddress; + Port = NbtConfig.DnsServerPort; + } + else // ----- if (pSentList->Flags & NBT_DNS_SERVER_BACKUP) ---- + { + IpAddress = pDeviceContext->lDnsBackupServer; + Port = NbtConfig.DnsServerPort; + } +#endif + + + // + // is it is a send to WINS on this machine + // + if (pNameHdr->AnCount == (UCHAR)WINS_SIGNATURE) + { + IpAddress = pDeviceContext->IpAddress; + } + } + + ASSERT(pSentList->Flags); + + // each adapter has a different source Ip address for registrations + // - pHdrIpAddress is NULL for queries... + if (pHdrIpAddress) + { + *pHdrIpAddress = htonl(pDeviceContext->IpAddress); + } + + // + // in the event that DHCP has just removed the IP address, use a null + // FileObject to signal UdpSendDatagram not to do the send + // + if ( + (pDeviceContext->IpAddress == 0) + || ( !SendFlag ) + ) + { + pFileObject = NULL; + } + else + { + // + // If the device has been destroyed, dont send anything. + // + if (InterlockedExchangeAdd(&pDeviceContext->IsDestroyed, 0) != 0) { + pFileObject = NULL; + } else { + pFileObject = pDeviceContext->pNameServerFileObject; + } + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = UdpSendDatagram( + pTracker, + IpAddress, + pFileObject, + NDgramSendCompleted, + pTracker, + Port, + NBT_NAME_SERVICE + ); + + return(status); +} +//---------------------------------------------------------------------------- +PVOID +CreatePdu( + IN PCHAR pName, + IN PCHAR pScope, + IN ULONG IpAddress, + IN USHORT NameType, + IN enum eNSTYPE eNsType, + OUT PVOID *pHdrs, + OUT PULONG pLength, + IN tDGRAM_SEND_TRACKING *pTracker + ) +/*++ + +Routine Description: + + This routine builds a registration pdu + +Arguments: + + +Return Value: + + PULONG - a ptr to the ip address in the pdu so it can be filled in later + +--*/ +{ + tNAMEHDR *pNameHdr; + ULONG uLength; + ULONG uScopeSize; + tGENERALRR *pGeneral; + CTELockHandle OldIrq; + + +#ifdef VXD + if ( (eNsType == eDNS_NAME_QUERY) || (eNsType == eDIRECT_DNS_NAME_QUERY) ) + { + uScopeSize = domnamelen(pTracker->pchDomainName) + 1; // +1 for len byte + if (uScopeSize > 1) + { + uScopeSize++; // for the null byte + } + } + else +#endif + uScopeSize = strlen(pScope) +1; // +1 for null too + + + // size is size of the namehdr structure -1 for the NetbiosName[1] + // + the 32 bytes for the half ascii name + + // scope + size of the General RR structure + uLength = sizeof(tNAMEHDR) - 1 + + (NETBIOS_NAME_SIZE << 1) + + uScopeSize; + + if (eNsType == eNAME_QUERY) + { + uLength = uLength + sizeof(ULONG); + } +#ifdef VXD + // there is no half-ascii conversion in DNS. we added 32 bytes above, but + // we need only 16. so, subtract 16. + else if (eNsType == eDNS_NAME_QUERY) + { + uLength = uLength - NETBIOS_NAME_SIZE + sizeof(ULONG); + } + // This is a "raw" DNS name query. Substitute raw string length of pName + // for NETBIOS_NAME_SIZE. + else if (eNsType == eDIRECT_DNS_NAME_QUERY) + { + uLength = uLength - (NETBIOS_NAME_SIZE << 1) + sizeof(ULONG) + strlen(pName) + 1; + } +#endif + else + { + uLength += sizeof(tGENERALRR); + } + + // Note that this memory must be deallocated when the send completes in + // tdiout.DgramSendCompletion + pNameHdr = NbtAllocMem((USHORT)uLength ,NBT_TAG('X')); + + if (!pNameHdr) + { + return(NULL); + } + + CTEZeroMemory((PVOID)pNameHdr,uLength); + + // + // for resends of the same name query or name registration, do not increment + // the transaction id + // + if (pTracker->TransactionId) + { + pNameHdr->TransactId = pTracker->TransactionId; + } + else + { + pNameHdr->TransactId = htons(GetTransactId()); + } + + pNameHdr->QdCount = 1; + pNameHdr->AnCount = 0; + pNameHdr->NsCount = 0; + + +#ifdef VXD + if ((eNsType != eDNS_NAME_QUERY)&&(eNsType != eDIRECT_DNS_NAME_QUERY)) + { +#endif + // Convert the name to half ascii and copy!! ... adding the scope too + pGeneral = (tGENERALRR *)ConvertToHalfAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + pName, + pScope, + uScopeSize); + + pGeneral->Question.QuestionTypeClass = htonl(QUEST_NBINTERNET); +#ifdef VXD + } +#endif + + *pHdrs = (PVOID)pNameHdr; + *pLength = uLength; + + switch (eNsType) + + { + +#ifdef VXD + case eDNS_NAME_QUERY: + case eDIRECT_DNS_NAME_QUERY: + + // copy the netbios name ... adding the scope too + pGeneral = (tGENERALRR *)DnsStoreName( + (PCHAR)&pNameHdr->NameRR.NameLength, + pName, + pTracker->pchDomainName, + eNsType); + + pGeneral->Question.QuestionTypeClass = htonl(QUEST_DNSINTERNET); + + pNameHdr->OpCodeFlags = (FL_RECURDESIRE); + + pNameHdr->ArCount = 0; + + // we just need to return something non-null to succeed. + return((PULONG)pNameHdr); +#endif + + case eNAME_QUERY: + + if (NodeType & BNODE) + { + pNameHdr->OpCodeFlags = (FL_BROADCAST | FL_RECURDESIRE); + } + else + pNameHdr->OpCodeFlags = (FL_RECURDESIRE); + + pNameHdr->ArCount = 0; + + // we just need to return something non-null to succeed. + return((PULONG)pNameHdr); + break; + + case eNAME_REGISTRATION_OVERWRITE: + case eNAME_REFRESH: + case eNAME_REGISTRATION: + // + // The broadcast bit is set in UdpSendNSBcast so we don't + // need to set it here. - just set the op code, since the broadcast + // bit is a function of whether we are talking to the nameserver or doing + // a broadcast. This code handles the multi-homed case with a new + // opcode for registration, and that opcode is set in the routine that + // + // The final name registration in Broadcast is called an Overwrite request + // and it does not have the FL_RECURSION Desired bit set. + // + if (eNsType == eNAME_REGISTRATION_OVERWRITE) + { + pNameHdr->OpCodeFlags = (OP_REGISTRATION); + } + else + { + pNameHdr->OpCodeFlags = (FL_RECURDESIRE | OP_REGISTRATION); + } + + + // + // If WINS is on the same machine adjust the PDU to be able to tell + // WINS that this pdu came from the local machine + // +#ifndef VXD + if (pWinsInfo && (pTracker->Flags & NBT_NAME_SERVER)) + { + pNameHdr->AnCount = (UCHAR)WINS_SIGNATURE; + } +#endif + pGeneral->Ttl = htonl(DEFAULT_TTL); + + // *** NOTE: There is no BREAK here by DESIGN!! + + case eNAME_RELEASE: + // this code sets the Broadcast bit based on the node type rather than the + // type of send....UdpSendNSBcast, resets the code according to the type of + // name, so this code may not need to set the Broadcast bit + // + if (eNsType == eNAME_RELEASE) + { + pNameHdr->OpCodeFlags = OP_RELEASE; + // + // TTL for release is zero + // + pGeneral->Ttl = 0; + } + + pNameHdr->ArCount = 1; // 1 additional resource record included + + + pGeneral->RrName.uSizeLabel = PTR_TO_NAME; // set top two bits to signify ptr + + // the offset ptr to the name added above + pGeneral->RrName.pLabel[0] = sizeof(tNAMEHDR) - sizeof(tNETBIOS_NAME); + pGeneral->RrTypeClass = htonl(QUEST_NBINTERNET); + + + pGeneral->Length = htons(6); + pGeneral->Flags = htons((USHORT)((NameType << 15) | NbtConfig.PduNodeType)); + pGeneral->IpAddress = htonl(IpAddress); + + break; + + } + + + // return the ptr to the IP address so this can be filled in later if necessary + return((PVOID)&pGeneral->IpAddress); + +} +//---------------------------------------------------------------------------- +VOID +NsDgramSendCompleted( + PVOID pContext, + NTSTATUS status, + ULONG lInfo + ) +/*++ + +Routine Description: + + This routine turns off the bit indicating that the datagram send is + still in the transport, so that we will know if it is safe to send another + one or not when the timer times out and calls MsNodeCompletion. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + pTracker->Flags &= ~SEND_PENDING; + + DereferenceTracker(pTracker); + +} +//---------------------------------------------------------------------------- +VOID +NameDgramSendCompleted( + PVOID pContext, + NTSTATUS status, + ULONG lInfo + ) +/*++ + +Routine Description: + + This routine frees the name service datagram that was allocated for + this name query or name registration in UdpSendNsBcast. + +Arguments: + + pContext = ptr to datagram header + +Return Value: + + +--*/ +{ + CTEMemFree(pContext); +} +//---------------------------------------------------------------------------- +VOID +NDgramSendCompleted( + PVOID pContext, + NTSTATUS status, + ULONG lInfo + ) +/*++ + +Routine Description: + + This routine frees the name service datagram that was allocated for + this name query or name registration in UdpSendNsBcast. + +Arguments: + + pContext = ptr to datagram header + +Return Value: + + +--*/ +{ + FreeTracker((tDGRAM_SEND_TRACKING *)pContext,FREE_HDR | RELINK_TRACKER); +} + +//---------------------------------------------------------------------------- +NTSTATUS +UdpSendResponse( + IN ULONG lNameSize, + IN tNAMEHDR UNALIGNED *pNameHdrIn, + IN tNAMEADDR *pNameAddr, + IN PTDI_ADDRESS_IP pDestIpAddress, + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG Rcode, + IN enum eNSTYPE NsType, + IN CTELockHandle OldIrq + ) +/*++ + +Routine Description: + + This routine builds a registration response pdu and sends it with the + specified Rcode. + +Arguments: + + lSize - number of bytes in the name including scope in half ascii + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + tNAMEHDR *pNameHdr; + ULONG uLength; + tDGRAM_SEND_TRACKING *pTracker; + tQUERYRESP *pQuery; + ULONG ToCopy; + LONG i; + BOOLEAN RespondWithOneAddr = TRUE; + ULONG MultiHomedSize = 0; + ULONG in_addr; + USHORT in_port; + ULONG IpAddress; + USHORT NameType; + BOOLEAN DoNonProxyCode = TRUE; + + in_addr = ntohl(pDestIpAddress->in_addr); + in_port = ntohs(pDestIpAddress->sin_port); + + // a multihomed node can have the SingleResponse registry value set so + // that it never returns a list of ip addresses. This allows multihoming + // in disjoint WINS server domains. - for name Query responses only + // + + if ((NbtConfig.MultiHomed) && + (!NbtConfig.SingleResponse) && + (NsType == eNAME_QUERY_RESPONSE)) + { + if (SrcIsNameServer(in_addr,in_port)) + { + RespondWithOneAddr = FALSE; + MultiHomedSize = (NbtConfig.AdapterCount-1)*sizeof(tADDSTRUCT); + } + } + + // size is size of the namehdr structure -1 for NetBiosName[1] + // + the 32 bytes for the half ascii name + the Query response record + // + any scope size (including the null on the end of the name) + // ( part of the lNameSize) + the number of extra adapters * the size + // of the address structure (multihomed case). + uLength = sizeof(tNAMEHDR) + + sizeof(tQUERYRESP) + + lNameSize + - 1 + + MultiHomedSize; + + // Note that this memory must be deallocated when the send completes in + // tdiout.DgramSendCompletion + pNameHdr = NbtAllocMem((USHORT)uLength ,NBT_TAG('Y')); + if (!pNameHdr) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CTEZeroMemory((PVOID)pNameHdr,uLength); + + pNameHdr->QdCount = 0; + pNameHdr->AnCount = 1; + + // + // fill in the rest of the PDU explicitly + // + pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + pQuery->RrTypeClass = htonl(QUEST_NBINTERNET); + pQuery->Ttl = 0; + pQuery->Length = htons(sizeof(tADDSTRUCT)); + pQuery->Flags = htons((USHORT)(NbtConfig.PduNodeType)); + + // set the name type to 1 if it is a group so we can shift the 1 to the 16th + // bit position + // + if (pNameAddr != NULL) + { + NameType = (pNameAddr->NameTypeState & (NAMETYPE_GROUP | NAMETYPE_INET_GROUP)) ? 1 : 0; + } + pQuery->Flags = htons((USHORT)((NameType << 15) | NbtConfig.PduNodeType)); + + // convert Rcode to network order + Rcode = htons(Rcode); + + switch (NsType) + { + + case eNAME_RELEASE: + case eNAME_REGISTRATION_RESPONSE: + + // copy the source name and the 12 bytes preceeding it to complete the + // response pdu + // + ToCopy = sizeof(tNAMEHDR) + lNameSize -1; + CTEMemCopy((PVOID)pNameHdr, + (PVOID)pNameHdrIn, + ToCopy); + + if (NsType == eNAME_RELEASE) + { + // setup the fields in the response. + pNameHdr->OpCodeFlags = (USHORT)(OP_RESPONSE | OP_RELEASE + | FL_AUTHORITY + | Rcode); + + } + else + { + // setup the fields in the response. + pNameHdr->OpCodeFlags = (USHORT)(OP_RESPONSE | OP_REGISTRATION | + FL_RECURDESIRE | FL_RECURAVAIL | FL_AUTHORITY + | Rcode); + + } + + // these two lines must be here because the memcopy above sets + // them to wrong values. + pNameHdr->QdCount = 0; + pNameHdr->AnCount = 1; + pNameHdr->ArCount = 0; + pNameHdr->NsCount = 0; + + // this code will run in the proxy case where another node does a + // registration of a unique name that conflicts with an internet + // group name in the remote table. There are never any internet group + // names in the local table - at least if there are, they are flagged + // as simple groups. + // + if (pNameAddr) + { + if (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP) + { + IpAddress = pNameAddr->pIpList->IpAddr[0]; + } + else + { + // an ipaddress of 0 and a group name means it is a local name + // table entry, where the 0 ipaddress should be switched to the + // ipaddress of this adapter. + // + if ((pNameAddr->IpAddress == 0) && + (pNameAddr->NameTypeState & NAMETYPE_GROUP)) + { + IpAddress = pDeviceContext->IpAddress; + } + else + IpAddress = pNameAddr->IpAddress; + } + } + else + { + IpAddress = 0; + } + break; + + case eNAME_QUERY_RESPONSE: + + pNameHdr->OpCodeFlags = ( OP_RESPONSE | FL_AUTHORITY | FL_RECURDESIRE ); + + pNameHdr->TransactId = pNameHdrIn->TransactId; + + // add 1 for the name length byte on the front of the name - scope is already + // included in lNameSize + // + CTEMemCopy(&pNameHdr->NameRR.NameLength, + (PVOID)&pNameHdrIn->NameRR.NameLength, + lNameSize+1); + + + if (pNameAddr == NULL) + { + // this is a negative query response record since there is no + // local name to be found + // + pNameHdr->OpCodeFlags |= htons(NAME_ERROR); + pQuery->Length = 0; + IpAddress = 0; + } + else + { + tDEVICECONTEXT *pDevContext; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + // do not send name query responses for names not registered on + // this net card, unless it is the name server for that net + // card requesting the name query, since for Multihomed nodes + // when it registers a name, WINS will do a query, which may + // come in on the other net card that the name is not active on + // yet - so we want to respond to this sort of query. Do not do + // this check for a proxy since it is responding for a name + // in the remote name table and it is not bound to an adapter. + // + if (!(NodeType & PROXY) && + !(pNameAddr->AdapterMask & pDeviceContext->AdapterNumber) && + (!((in_port == NbtConfig.NameServerPort) && + (pDeviceContext->lNameServerAddress == in_addr) || + (pDeviceContext->lBackupServer == in_addr)))) + + { + // + // Only return an address to the requestor if the + // name is registered on that adapter + // + CTEMemFree(pNameHdr); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_UNSUCCESSFUL); + } + + pQuery->Ttl = htonl(DEFAULT_TTL); + // + // In case of PROXY, we send one IP address as response to an + // internet group query. Note: there should not be any INET_GROUP + // names in the local hash table, hence a non-proxy should not execute + // this code + // +#ifdef PROXY_NODE + // + // When the proxy responds, the source node will see that it is a + // group name and convert it to a broadcast, so the Ip address doesn't + // really matter since the sender will not use it. Note that the + // source node send may not actually reach any members of the + // internet group since they may all be off the local subnet. + // + IF_PROXY(NodeType) + { + DoNonProxyCode = FALSE; + + if (pNameAddr->NameTypeState & (NAMETYPE_INET_GROUP)) + { + // + // the first address is zero meaning the broadcast address, + // so use the second, if there is a second one + // + if (pNameAddr->pIpList->IpAddr[1] != (ULONG)-1) + { + IpAddress = pNameAddr->pIpList->IpAddr[1]; + } + else + IpAddress = pNameAddr->pIpList->IpAddr[0]; + } + + // + // if this name is local and if this is a multihomed machine + // we should treat it like a regular multihomed machine, even + // though this is a Proxy node + // + else if ( (pNameAddr->Verify == LOCAL_NAME) && + (NbtConfig.MultiHomed) ) + { + DoNonProxyCode = TRUE; + } + + else + { + IpAddress = pNameAddr->IpAddress; + } + + if (IpAddress == 0) + { + // don't return 0, return the broadcast address + // + IpAddress = pDeviceContext->BroadcastAddress; + } + + } + + if (DoNonProxyCode) +#endif + { + // the node could be multihomed, but we are saying, only + // respond with one address when this flag is set. + if (RespondWithOneAddr) + { + // for multihomed hosts, SelectAdapter can be set to TRUE + // + if (NbtConfig.SelectAdapter) + { + CTESystemTime TimeValue; + LONG Index; + ULONG Count=0; + + // we are only going to return one address, but we + // can randomly select it from the available adapters + // Try to find a valid ip address 5 times. + // + IpAddress = 0; + while ((IpAddress == 0) && (Count < 5)) + { + Count++; + CTEQuerySystemTime(TimeValue); + Index = RandomizeFromTime( TimeValue, NbtConfig.AdapterCount ) ; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead->Flink; + + for (i = 0;i< Index;i++) + pEntry = pEntry->Flink; + + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + IpAddress = pDevContext->IpAddress; + } + + // + // if this adapter still has a null IpAddress then respond + // with the adapter the request came in on, since the + // other adapters could be idle RAS or waiting for a DHCP + // address just now... + // + if (IpAddress == 0) + { + IpAddress = pDeviceContext->IpAddress; + } + } + else + { + IpAddress = pDeviceContext->IpAddress; + } + } + else + { + tADDSTRUCT *pAddStruct; + USHORT Flags; + ULONG Count = 0; + + // multihomed case - go through all the adapters making + // up a structure of all adapters that the name is + // registered against. Enough memory was allocated up + // front to have the name registered against all adapeters + // on this node. + // + Flags = pQuery->Flags; + + // set to zero so we don't try to set pQuery->IpAddress + // below + IpAddress = 0; + + pAddStruct = (tADDSTRUCT *)&pQuery->Flags; + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + // + // only pass back addresses registered on this adapter + // that are not null(i.e. not RAS adapters after a disconnect) + // + if ((pDevContext->AdapterNumber & pNameAddr->AdapterMask) && + (pDevContext->IpAddress)) + { + pAddStruct->NbFlags = Flags; + pAddStruct->IpAddr = htonl(pDevContext->IpAddress); + Count++; + pAddStruct++; + } + pEntry = pEntry->Flink; + + } + // re-adjust the length of the pdu if the name is not registered + // against all adapters... + // + if (Count != NbtConfig.AdapterCount) + { + uLength -= (NbtConfig.AdapterCount - Count)*sizeof(tADDSTRUCT); + } + pQuery->Length = (USHORT)htons(Count*sizeof(tADDSTRUCT)); + } + } + } + + + } + + if (IpAddress) + { + pQuery->IpAddress = htonl(IpAddress); + } + + // get a tracker structure, which has a SendInfo structure in it + status = GetTracker(&pTracker); + if (!NT_SUCCESS(status)) + { + CTEMemFree((PVOID)pNameHdr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // fill in the connection information + pTracker->SendBuffer.HdrLength = uLength; + pTracker->SendBuffer.pDgramHdr = (PVOID)pNameHdr; + pTracker->SendBuffer.Length = 0; + pTracker->SendBuffer.pBuffer = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + status = UdpSendDatagram(pTracker, + in_addr, + pDeviceContext->pNameServerFileObject, + QueryRespDone, + pTracker, + in_port, + NBT_NAME_SERVICE); + + return(status); +} + +//---------------------------------------------------------------------------- +VOID +QueryRespDone( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo) +/*++ +Routine Description + + This routine handles cleaning up various data blocks used in conjunction + with the sending the Query response. + +Arguments: + + pContext - ptr to the DGRAM_TRACKER block + NTSTATUS - completion status + +Return Values: + + VOID + +--*/ + +{ + tDGRAM_SEND_TRACKING *pTracker; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR); +} + +//---------------------------------------------------------------------------- +NTSTATUS +UdpSendDatagram( + IN tDGRAM_SEND_TRACKING *pDgramTracker, + IN ULONG IpAddress, + IN PFILE_OBJECT TransportFileObject, + IN PVOID pCompletionRoutine, + IN PVOID CompletionContext, + IN USHORT Port, + IN ULONG Service + ) +/*++ + +Routine Description: + + This routine sends a datagram across the TDI to be sent by Udp. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + TDI_REQUEST TdiRequest; + ULONG uSentSize; + TDI_CONNECTION_INFORMATION *pSendInfo; + PTRANSPORT_ADDRESS pTransportAddr; + ULONG Length; + + + + status = STATUS_SUCCESS; + + // when there is no WINS server set in the registry we set the WINS + // ip address to LOOP_BACK, so if it is set to that here, do not send + // the datagram. If There is no Ip Address then the Transport Handle + // will be null and we do not do the send in that case either. + // + if ((IpAddress == LOOP_BACK) || (!TransportFileObject)) + { + if (pCompletionRoutine) + { + (*(NBT_COMPLETION)pCompletionRoutine)(CompletionContext,status,0); + } + return(status); + } + + pSendInfo = pDgramTracker->pSendInfo; + // + // an address of 0 means do a broadcast. When '1C' internet group + // names are built either from the Lmhost file or from the network + // the broadcast address is inserted in the list as 0. + // + if (IpAddress == 0) + { + IpAddress = pDgramTracker->pDeviceContext->BroadcastAddress; + } + + TdiRequest.Handle.AddressHandle = (PVOID)TransportFileObject; + + // the completion routine is setup to free the pDgramTracker memory block + TdiRequest.RequestNotifyObject = pCompletionRoutine; + TdiRequest.RequestContext = (PVOID)CompletionContext; + + // the send length is the client dgram length + the size of the dgram + // header + // + Length = pDgramTracker->SendBuffer.HdrLength + pDgramTracker->SendBuffer.Length; + + // fill in the connection information + pSendInfo->RemoteAddressLength = sizeof(TRANSPORT_ADDRESS) -1 + + pNbtGlobConfig->SizeTransportAddress; + + pTransportAddr = (PTRANSPORT_ADDRESS)pSendInfo->RemoteAddress; + + // fill in the remote address + pTransportAddr->TAAddressCount = 1; + pTransportAddr->Address[0].AddressLength = pNbtGlobConfig->SizeTransportAddress; + + pTransportAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; + + ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->sin_port = htons(Port); + + ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->in_addr = + htonl(IpAddress); + + Service = NBT_NAME_SERVICE; + status = TdiSendDatagram( + &TdiRequest, + pSendInfo, + Length, + &uSentSize, + &pDgramTracker->SendBuffer, + Service); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TcpSessionStart( + IN tDGRAM_SEND_TRACKING *pTracker, + IN ULONG IpAddress, + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pCompletionRoutine, + IN ULONG Port + ) +/*++ + +Routine Description: + + This routine sets up a tcp connection by passing a connect through TDI to + TCP. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + TDI_REQUEST TdiRequest; + TDI_CONNECTION_INFORMATION *pSendInfo; + PTRANSPORT_ADDRESS pTransportAddr; + tCONNECTELE *pConnEle; + CTELockHandle OldIrq; + tLOWERCONNECTION *pLowerConn; + + pSendInfo = pTracker->pSendInfo; + + // we need to pass the file handle of the connection to TCP. + pConnEle = (tCONNECTELE *)pTracker->Connect.pConnEle; + + CTESpinLock(pConnEle,OldIrq); + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + TdiRequest.Handle.AddressHandle = + (PVOID)((tLOWERCONNECTION *)pConnEle->pLowerConnId)->pFileObject; + + // the completion routine is setup to free the pTracker memory block + TdiRequest.RequestNotifyObject = pCompletionRoutine; + TdiRequest.RequestContext = (PVOID)pTracker; + + // fill in the connection information + pSendInfo->RemoteAddressLength = sizeof(TRANSPORT_ADDRESS) -1 + + pNbtGlobConfig->SizeTransportAddress; + + pTransportAddr = (PTRANSPORT_ADDRESS)pSendInfo->RemoteAddress; + + // fill in the remote address + pTransportAddr->TAAddressCount = 1; + pTransportAddr->Address[0].AddressLength = pNbtGlobConfig->SizeTransportAddress; + pTransportAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; + ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->sin_port = + htons((USHORT)Port); + + ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->in_addr = + htonl(IpAddress); + + CTESpinFree(pConnEle,OldIrq); + + // pass through the TDI I/F on the bottom of NBT, to the transport + // pass in the original irp from the client so that the client can + // cancel it ok...rather than use one of NBT's irps + // + status = TdiConnect( + &TdiRequest, + (ULONG)pTracker->Connect.pTimeout, + pSendInfo, + pConnEle->pIrp); + + } + else + { + CTESpinFree(pConnEle,OldIrq); + // + // Complete the request through the completion routine so it + // cleans up correctly + // + (*(NBT_COMPLETION)pCompletionRoutine)( + (PVOID)pTracker, + STATUS_CANCELLED, + 0L + ); + + status = STATUS_CANCELLED; + } + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TcpSendSessionResponse( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG lStatusCode, + IN ULONG lSessionStatus + ) +/*++ + +Routine Description: + + This routine sends a session PDU corresponding to the lStatusCode. This + could be a KeepAlive, PositiveSessionResponse, NegativeSessionResponse or + a Retarget (not implemented yet). For the Keep Alive case the completion + routine passed in is used rather than SessionRespDone, as is the case + for all other messages. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + tSESSIONERROR *pSessionHdr; + + pSessionHdr = (tSESSIONERROR *)NbtAllocMem(sizeof(tSESSIONERROR),NBT_TAG('Z')); + if (!pSessionHdr) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // get a tracker structure, which has a SendInfo structure in it + status = GetTracker(&pTracker); + if (NT_SUCCESS(status)) + { + pTracker->SendBuffer.pDgramHdr = (PVOID)pSessionHdr; + pTracker->SendBuffer.pBuffer = NULL; + pTracker->SendBuffer.Length = 0; + + pSessionHdr->Flags = NBT_SESSION_FLAGS; + pSessionHdr->Type = (UCHAR)lStatusCode; + + switch (lStatusCode) + { + case NBT_NEGATIVE_SESSION_RESPONSE: + pTracker->SendBuffer.HdrLength = sizeof(tSESSIONERROR); + // this length is one byte longer for the error code - different type used here + pSessionHdr->Length = htons(1); // one error code byte + pSessionHdr->ErrorCode = (UCHAR)lSessionStatus; + break; + + case NBT_POSITIVE_SESSION_RESPONSE: + pTracker->SendBuffer.HdrLength = sizeof(tSESSIONHDR); + pSessionHdr->Length = 0; // no data following the length byte + break; + + } + + status = TcpSendSession(pTracker, + pLowerConn, + SessionRespDone); + } + else + { + CTEMemFree((PVOID)pSessionHdr); + } + + return(status); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TcpSendSession( + IN tDGRAM_SEND_TRACKING *pTracker, + IN tLOWERCONNECTION *pLowerConn, + IN PVOID pCompletionRoutine + ) +/*++ + +Routine Description: + + This routine sends a message on a tcp connection. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + TDI_REQUEST TdiRequest; + ULONG lSentLength; + + // we need to pass the file handle of the connection to TCP. + TdiRequest.Handle.AddressHandle = (PVOID)pLowerConn->pFileObject; + + // the completion routine is setup to free the pTracker memory block + TdiRequest.RequestContext = (PVOID)pTracker; + + // this completion routine just puts the tracker back on its list and + // frees the memory associated with the UserData buffer. + TdiRequest.RequestNotifyObject = pCompletionRoutine; + + // pass through the TDI I/F on the bottom of NBT, to the transport + status = TdiSend( + &TdiRequest, + 0, // no send flags + (ULONG)pTracker->SendBuffer.HdrLength + + (ULONG)pTracker->SendBuffer.Length , + &lSentLength, + &pTracker->SendBuffer, + 0); // no send flags set + + return(status); + +} + +//---------------------------------------------------------------------------- +VOID +SessionRespDone( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo) +/*++ +Routine Description + + This routine handles cleaning up various data blocks used in conjunction + sending a session response at session startup time. If the session + response was negative, then kill the connection. + +Arguments: + + pContext - ptr to the DGRAM_TRACKER block + NTSTATUS - completion status + +Return Values: + + VOID + +--*/ + +{ + tDGRAM_SEND_TRACKING *pTracker; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +SendTcpDisconnect( + IN tLOWERCONNECTION *pLowerConnId + ) +/*++ +Routine Description + + This routine disconnects a TCP connection in a graceful manner which + insures that any data still in the pipe gets to the other side. Mostly + it calls TcpDisconnect which does the work. This routine just gets a + tracker for the send. + +Arguments: + + pLowerConnID - ptr to the lower connection that has the file object in it + +Return Values: + NTSTATUS - completion status + + VOID + +--*/ + +{ + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + + status = GetTracker(&pTracker); + if (NT_SUCCESS(status)) + { + pTracker->Connect.pConnEle = (PVOID)pLowerConnId; + + status = TcpDisconnect(pTracker,NULL,TDI_DISCONNECT_RELEASE,FALSE); + } + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TcpDisconnect( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID Timeout, + IN ULONG Flags, + IN BOOLEAN Wait + ) +/*++ +Routine Description + + This routine disconnects a TCP connection in a graceful manner which + insures that any data still in the pipe gets to the other side. + +Arguments: + + pTracker - ptr to the DGRAM_TRACKER block + +Return Values: + NTSTATUS - completion status + + VOID + +--*/ + +{ + TDI_REQUEST TdiRequest; + NTSTATUS status; + + // we need to pass the file handle of the connection to TCP. + TdiRequest.Handle.AddressHandle = + (PVOID)((tLOWERCONNECTION *)pTracker->Connect.pConnEle)->pFileObject; + + // the completion routine is setup to free the pTracker memory block + TdiRequest.RequestContext = (PVOID)pTracker; + + // this completion routine just puts the tracker back on its list and + // frees the memory associated with the UserData buffer. + TdiRequest.RequestNotifyObject = DisconnectDone; + pTracker->Flags = (USHORT)Flags; + + status = TdiDisconnect(&TdiRequest, + Timeout, + Flags, + pTracker->pSendInfo, + ((tLOWERCONNECTION *)pTracker->Connect.pConnEle)->pIrp, + Wait); + + + return(status); + +} + +//---------------------------------------------------------------------------- +VOID +DisconnectDone( + IN PVOID pContext, + IN NTSTATUS status, + IN ULONG lInfo) +/*++ +Routine Description + + This routine handles cleaning up after a disconnect is sent to the transport. + +Arguments: + + pContext - ptr to the DGRAM_TRACKER block + +Return Values: + + VOID + +--*/ + +{ + tDGRAM_SEND_TRACKING *pTracker; + tLOWERCONNECTION *pLowerConn; + CTELockHandle OldIrq; + PCTE_IRP pIrp; + BOOLEAN CleanupLower; + NTSTATUS DiscWaitStatus; + tCONNECTELE *pConnEle; + PCTE_IRP pIrpClose; + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + + pLowerConn = (tLOWERCONNECTION *)pTracker->Connect.pConnEle; + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pLowerConn); + + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Disconnect Irp has been returned...pLowerConn %X,state %X\n", + pLowerConn,pLowerConn->State)); + // + // if the state is disconnected, then a disconnect indication + // has come from the transport.. . if still disconnecting, + // then we have not had a disconnect indication yet, so + // wait for the indication to go through DisconnectHndlrNotOs which + // will do the cleanup. + // + + // Streams TCP always indicates before completing the disconnect request, + // so we always cleanup here for the Streams stack. + // + if (StreamsStack) + { + goto StreamsCode; + } + + // + // If the disconnect was abortive, then there will not be a disconnect + // indication, so do the cleanup now. + // + if ((pLowerConn->State == NBT_DISCONNECTING) + && (pTracker->Flags == TDI_DISCONNECT_RELEASE ) + && (status == STATUS_SUCCESS) ) + { + pLowerConn->State = NBT_DISCONNECTED ; + CleanupLower = FALSE; + PUSH_LOCATION(0xA3); + } + else + if (pLowerConn->State != NBT_IDLE) + { + +StreamsCode: + + // + // change the state to idle so that the Disconnect handler will + // not attempt to do anything with it if for some reason the transport + // indicates a disconnect after this point. + // + ASSERT((pLowerConn->State == NBT_DISCONNECTED) || + (pLowerConn->State == NBT_DISCONNECTING)); + pLowerConn->State = NBT_IDLE; + + CleanupLower = TRUE; + } + + + pConnEle = pLowerConn->pUpperConnection; + + // + // there may be a disconnect wait irp, so return that first if there + // is one waiting around. + // + if (pConnEle && pConnEle->pIrpClose) + { + pIrpClose = pConnEle->pIrpClose; + CHECK_PTR(pConnEle); + pConnEle->pIrpClose = NULL ; + if (pConnEle->DiscFlag == TDI_DISCONNECT_ABORT) + { + DiscWaitStatus = STATUS_CONNECTION_RESET; + } + else + DiscWaitStatus = STATUS_GRACEFUL_DISCONNECT; + } + else + pIrpClose = NULL; + + // + // This is the disconnect requesting Irp + // + if ( pLowerConn->pIrp ) + { + pIrp = pLowerConn->pIrp; + pLowerConn->pIrp = NULL ; + + } + else + pIrp = NULL; + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (pIrpClose) + { + CTEIoComplete( pIrpClose, DiscWaitStatus, 0 ) ; + } + + if (pIrp) + { + CTEIoComplete( pIrp, status, 0 ) ; + } + + if (CleanupLower) + { + PUSH_LOCATION(0x6c); +#if !defined(VXD) && DBG + // + // DEBUG to catch upper connections being put on lower conn QUEUE + // + if ((pLowerConn->Verify != NBT_VERIFY_LOWERCONN ) || + (pLowerConn->RefCount == 1)) + { + DbgBreakPoint(); + } +#else + ASSERT(pLowerConn->Verify == NBT_VERIFY_LOWERCONN); + ASSERT(pLowerConn->RefCount > 1); +#endif + + // this either puts the lower connection back on its free + // queue if inbound, or closes the connection with the transport + // if out bound. (it can't be done at dispatch level). + // + status = CTEQueueForNonDispProcessing(NULL, + pLowerConn, + NULL, + CleanupAfterDisconnect, + pLowerConn->pDeviceContext); + } + + FreeTracker(pTracker,RELINK_TRACKER); + +} + diff --git a/private/ntos/nbt/nbt/up/makefile b/private/ntos/nbt/nbt/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nbt/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nbt/up/sources b/private/ntos/nbt/nbt/up/sources new file mode 100644 index 000000000..91036a15a --- /dev/null +++ b/private/ntos/nbt/nbt/up/sources @@ -0,0 +1,27 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +!include ..\sources.inc diff --git a/private/ntos/nbt/nbtprocs.h b/private/ntos/nbt/nbtprocs.h new file mode 100644 index 000000000..fb00f12e4 --- /dev/null +++ b/private/ntos/nbt/nbtprocs.h @@ -0,0 +1,12 @@ +/* + + This is the dummy project header file for generating the + pre-compiled header files for NT builds of NetBT.SYS. + + It is not actually included by any of the source files + in the driver. + + */ + + +#include "inc\nbtprocs.h" diff --git a/private/ntos/nbt/nt/autodial.c b/private/ntos/nbt/nt/autodial.c new file mode 100644 index 000000000..9b83f526d --- /dev/null +++ b/private/ntos/nbt/nt/autodial.c @@ -0,0 +1,723 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + This file provides routines for interacting + with the automatic connection driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 9-6-95 + +Revision History: + +--*/ +#include "nbtprocs.h" // procedure headings + +#ifdef RASAUTODIAL + +#ifndef VXD +#include <nbtioctl.h> +#include <acd.h> +#include <acdapi.h> +#endif + +// +// Automatic connection global variables. +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbt '; + +// +// Imported routines. +// +VOID +CleanUpPartialConnection( + IN NTSTATUS status, + IN tCONNECTELE *pConnEle, + IN tDGRAM_SEND_TRACKING *pTracker, + IN PIRP pClientIrp, + IN CTELockHandle irqlJointLock, + IN CTELockHandle irqlConnEle + ); + +NTSTATUS +NbtConnectCommon( + IN TDI_REQUEST *pRequest, + IN PVOID pTimeout, + IN PTDI_CONNECTION_INFORMATION pCallInfo, + IN PTDI_CONNECTION_INFORMATION pReturnInfo, + IN PIRP pIrp + ); + + + +VOID +NbtRetryPreConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + tCONNECTELE *pConnEle = pArgs[0]; + PVOID pTimeout = pArgs[1]; + PTDI_CONNECTION_INFORMATION pCallInfo = pArgs[2]; + PTDI_CONNECTION_INFORMATION pReturnInfo = pArgs[3]; + PIRP pIrp = pArgs[4]; + TDI_REQUEST request; + KIRQL irql; + CTELockHandle OldIrq; + +#ifdef notdef // DBG + DbgPrint( + "NbtRetryPreConnect: fSuccess=%d, pIrp=0x%x, pIrp->Cancel=%d, pConnEle=0x%x\n", + fSuccess, + pIrp, + pIrp->Cancel, + pConnEle); +#endif + request.Handle.ConnectionContext = pConnEle; + status = NTCancelCancelRoutine ( pIrp ); + if ( status != STATUS_CANCELLED ) + { + // + // We're done with the connection progress, + // so clear the fAutoConnecting flag. We + // set the fAutoConnected flag to prevent us + // from re-attempting another automatic + // connection on this connection. + // + CTESpinLock(pConnEle,OldIrq); + pConnEle->fAutoConnecting = FALSE; + pConnEle->fAutoConnected = TRUE; + CTESpinFree(pConnEle,OldIrq); + + status = fSuccess ? + NbtConnectCommon( + &request, + pTimeout, + pCallInfo, + pReturnInfo, + pIrp) : + STATUS_BAD_NETWORK_PATH; + // + // We are responsible for completing + // the irp. + // + if (status != STATUS_PENDING) { + // + // Clear out the Irp pointer in the Connection object so that we dont try to + // complete it again when we clean up the connection. Do this under the connection + // lock. + // + CTESpinLock(pConnEle,OldIrq); + + pConnEle->pIrp = NULL; + + CTESpinFree(pConnEle,OldIrq); + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + } +} // NbtRetryPreConnect + + + +BOOLEAN +NbtCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ + if (nArgs != 5) + return FALSE; + + return (pArgs[4] == pArg); +} // NbtCancelAutoDialRequest + + + +BOOLEAN +NbtCancelPreConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + KIRQL irql; + ACD_ADDR addr; + BOOLEAN fCanceled; + + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + if (pConnEle == NULL) + return FALSE; +#ifdef notdef // DBG + DbgPrint("NbtCancelPreConnect: pIrp=0x%x, pConnEle=0x%x\n", + pIrp, + pConnEle); +#endif + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnEle->RemoteName, 16); + // + // Cancel the autodial request. + // + fCanceled = (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbtCancelAutoDialRequest, + pIrp); + if (fCanceled) + IoSetCancelRoutine(pIrp, NULL); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + // + // If the request could not be found + // in the driver, then it has already + // been completed, so we simply return. + // + if (!fCanceled) + return FALSE; + + KeRaiseIrql(DISPATCH_LEVEL, &irql); + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + + { + // + // Clear out the Irp pointer in the Connection object so that we dont try to + // complete it again when we clean up the connection. Do this under the connection + // lock. + // + // BUGBUG[WISHLIST] This should not be needed since before we call NbtConnectCommon, the Cancel routine + // is NULLed out, so it cannot happen that the pIrp ptr in the connection is set to the + // Irp, and this cancel routine is called. + // + CTELockHandle OldIrq; + + CTESpinLock(pConnEle,OldIrq); + + pConnEle->pIrp = NULL; + + CTESpinFree(pConnEle,OldIrq); + } + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + KeLowerIrql(irql); + + return TRUE; +} // NbtCancelPreConnect + + + +VOID +NbtRetryPostConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + tCONNECTELE *pConnEle = pArgs[0]; + PVOID pTimeout = pArgs[1]; + PTDI_CONNECTION_INFORMATION pCallInfo = pArgs[2]; + PTDI_CONNECTION_INFORMATION pReturnInfo = pArgs[3]; + PIRP pIrp = pArgs[4]; + tDGRAM_SEND_TRACKING *pTracker = (tDGRAM_SEND_TRACKING *)pReturnInfo; + TDI_REQUEST request; + CTELockHandle irqlConnEle, irqlJointLock; + KIRQL irql; + +#ifdef notdef // DBG + DbgPrint( + "NbtRetryPostConnect: fSuccess=%d, pIrp=0x%x, pIrp->Cancel=%d, pConnEle=0x%x\n", + fSuccess, + pIrp, + pIrp->Cancel, + pConnEle); +#endif + if (fSuccess) { + // + // We set the state here to fool NbtConnect + // into doing a reconnect. + // + request.Handle.ConnectionContext = pConnEle; + pConnEle->state = NBT_ASSOCIATED; + status = NTCancelCancelRoutine ( pIrp ); + if ( status != STATUS_CANCELLED ) + { + status = NbtConnectCommon( + &request, + pTimeout, + pCallInfo, + pReturnInfo, + pIrp); + // + // We are responsible for completing + // the irp. + // + if (status != STATUS_PENDING) { + // + // Clear out the Irp pointer in the Connection object so that we dont try to + // complete it again when we clean up the connection. Do this under the connection + // lock. + // + CTELockHandle OldIrq; + + CTESpinLock(pConnEle,OldIrq); + + ASSERT(pIrp == pConnEle->pIrp); + pConnEle->pIrp = NULL; + + CTESpinFree(pConnEle, OldIrq); + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + } + } + else { + CTESpinLock(&NbtConfig.JointLock,irqlJointLock); + CTESpinLock(pConnEle,irqlConnEle); + CleanUpPartialConnection( + STATUS_BAD_NETWORK_PATH, + pConnEle, + pTracker, + pIrp, + irqlJointLock, + irqlConnEle); + } +} // NbtRetryPostConnect + + + +VOID +NbtCancelPostConnect( + IN PIRP pIrp + ) +{ + PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + tCONNECTELE *pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + ACD_ADDR addr; + + if (pConnEle == NULL) + return; +#ifdef notdef // DBG + DbgPrint( + "NbtCancelPostConnect: pIrp=0x%x, pConnEle=0x%x\n", + pIrp, + pConnEle); +#endif + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnEle->RemoteName, 15); + // + // Cancel the autodial request. + // + (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbtCancelAutoDialRequest, + pIrp); +} // NbtCancelPostConnect + + + +BOOLEAN +NbtAttemptAutoDial( + IN tCONNECTELE *pConnEle, + IN PVOID pTimeout, + IN PTDI_CONNECTION_INFORMATION pCallInfo, + IN PTDI_CONNECTION_INFORMATION pReturnInfo, + IN PIRP pIrp, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. The first five parameters are + used in the call to NbtConnect after the connection + completes successfully. + +Arguments: + + ... + + ulFlags - automatic connection flags + + pProc - callback procedure when the automatic connection completes + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + NTSTATUS status; + BOOLEAN fSuccess; + ACD_ADDR addr; + KIRQL irql; + PVOID pArgs[5]; + PTRANSPORT_ADDRESS pRemoteAddress; + PTA_NETBIOS_ADDRESS pRemoteNetBiosAddress; + PTA_NETBIOS_EX_ADDRESS pRemoteNetbiosExAddress; + ULONG tdiAddressType; + PCHAR pName; + ULONG ulcbName; + LONG lNameType; + + // + // If this connection has already been through the + // automatic connection process, don't do it again. + // + if (pCallInfo == NULL || pConnEle->fAutoConnected) + return FALSE; + // + // Get the address of the connection. + // + pRemoteAddress = (PTRANSPORT_ADDRESS)pCallInfo->RemoteAddress; + tdiAddressType = pRemoteAddress->Address[0].AddressType; + if (tdiAddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + PTDI_ADDRESS_NETBIOS pNetbiosAddress; + + pRemoteNetbiosExAddress = (PTA_NETBIOS_EX_ADDRESS)pRemoteAddress; + pNetbiosAddress = &pRemoteNetbiosExAddress->Address[0].Address[0].NetbiosAddress; + pName = pNetbiosAddress->NetbiosName; + lNameType = pNetbiosAddress->NetbiosNameType; + ulcbName = pRemoteNetbiosExAddress->Address[0].AddressLength - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress) - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS,NetbiosName); + IF_DBG(NBT_DEBUG_NETBIOS_EX) { + KdPrint(( + "NetBt:NETBIOS address ulNameLen(%ld) Name %16s\n", + ulcbName, + pName)); + } + status = STATUS_SUCCESS; + } + else if (tdiAddressType == TDI_ADDRESS_TYPE_NETBIOS) { + pRemoteNetBiosAddress = (PTA_NETBIOS_ADDRESS)pRemoteAddress; + status = GetNetBiosNameFromTransportAddress( + pRemoteNetBiosAddress, + &pName, + &ulcbName, + &lNameType); + } + else + status = STATUS_INVALID_ADDRESS_COMPONENT; + // + // Save the address for pre-connect attempts, + // because if we have to cancel this irp, + // it is not saved anywhere else. + // + CTESpinLock(pConnEle, irql); + CTEMemCopy(pConnEle->RemoteName, pName, NETBIOS_NAME_SIZE); + CTESpinFree(pConnEle, irql); + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pName, NETBIOS_NAME_SIZE); +#ifdef DBG + DbgPrint("NbtAttemptAutodial: szAddr=%-15.15s\n", pName); +#endif + // + // Enqueue this request on the network + // connection pending queue. + // + pArgs[0] = pConnEle; + pArgs[1] = pTimeout; + pArgs[2] = pCallInfo; + pArgs[3] = pReturnInfo; + pArgs[4] = pIrp; + fSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 5, + pArgs); + if (fSuccess) { + // + // We set the automatic connection in progress + // flag so we know to clean up the connection + // block in the automatic connection driver if + // the request gets canceled. + // + CTESpinLock(pConnEle, irql); + pConnEle->fAutoConnecting = TRUE; + CTESpinFree(pConnEle, irql); + } + + return fSuccess; +} // NbtAttemptAutoDial + + + +VOID +NbtNoteNewConnection( + IN tCONNECTELE *pConnEle, + IN tNAMEADDR *pNameAddr + ) + +/*++ + +Routine Description: + + Inform the automatic connection driver of a + successful new connection. + +Arguments: + + pConnEle - a pointer to the upper connection + + pNameAddr - a pointer to the remote name + +Return Value: + None. + +--*/ + +{ + KIRQL irql; + ACD_ADDR addr; + ACD_ADAPTER adapter; + + if (pConnEle == NULL || pConnEle->pClientEle == NULL || + pConnEle->pClientEle->pDeviceContext == NULL) + { + return; + } + // + // Get the source IP address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pNameAddr->Name, 15); + adapter.fType = ACD_ADAPTER_IP; + adapter.ulIpaddr = 0; + CTESpinLock(pConnEle, irql); + adapter.ulIpaddr = htonl(pConnEle->pClientEle->pDeviceContext->IpAddress); + CTESpinFree(pConnEle, irql); + // + // If the connection did not have a + // TCP connection associated with it, + // then we're done. + // + if (adapter.ulIpaddr == 0) + return; + (*AcdDriverG.lpfnNewConnection)(&addr, &adapter); +} // NbtNoteNewConnection + + + +VOID +NbtAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbtAcdBind + + + +VOID +NbtAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbtAcdUnbind + +#endif // RASAUTODIAL diff --git a/private/ntos/nbt/nt/ctestuff.c b/private/ntos/nbt/nt/ctestuff.c new file mode 100644 index 000000000..0d9813a6d --- /dev/null +++ b/private/ntos/nbt/nt/ctestuff.c @@ -0,0 +1,90 @@ +// +// +// CTESTUFF.C +// +// This file contains Common Transport Environment code to handle +// OS dependent functions such as allocating memory etc. +// +// +#include "nbtprocs.h" + +// to convert a millisecond to a 100ns time +// +#define MILLISEC_TO_100NS 10000 + + +//---------------------------------------------------------------------------- +PVOID +CTEStartTimer( + IN CTETimer *pTimerIn, + IN ULONG DeltaTime, + IN CTEEventRtn TimerExpiry, + IN PVOID Context OPTIONAL + ) +/*++ +Routine Description: + + This Routine starts a timer. + +Arguments: + + Timer - Timer structure + TimerExpiry - completion routine + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +{ + LARGE_INTEGER Time; + + // + // initialize the DPC to have the correct completion routine and context + // + KeInitializeDpc(&pTimerIn->t_dpc, + (PVOID)TimerExpiry, // completion routine + Context); // context value + + // + // convert to 100 ns units by multiplying by 10,000 + // + Time.QuadPart = UInt32x32To64(DeltaTime,(LONG)MILLISEC_TO_100NS); + + // + // to make a delta time, negate the time + // + Time.QuadPart = -(Time.QuadPart); + + ASSERT(Time.QuadPart < 0); + + (VOID)KeSetTimer(&pTimerIn->t_timer,Time,&pTimerIn->t_dpc); + + return(NULL); +} +//---------------------------------------------------------------------------- +VOID +CTEInitTimer( + IN CTETimer *pTimerIn + ) +/*++ +Routine Description: + + This Routine initializes a timer. + +Arguments: + + Timer - Timer structure + TimerExpiry - completion routine + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +{ + KeInitializeTimer(&pTimerIn->t_timer); +} + diff --git a/private/ntos/nbt/nt/dirs b/private/ntos/nbt/nt/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/nbt/nt/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/nbt/nt/driver.c b/private/ntos/nbt/nt/driver.c new file mode 100644 index 000000000..0294ea69b --- /dev/null +++ b/private/ntos/nbt/nt/driver.c @@ -0,0 +1,1228 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Driver.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for the + NBT Transport and other routines that are specific to the NT implementation + of a driver. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + + +#include "nbtprocs.h" +#include <nbtioctl.h> + +#if DBG +// allocate storage for the global debug flag NbtDebug +#ifdef _PNP_POWER +ULONG NbtDebug=NBT_DEBUG_PNP_POWER; // NT PNP debugging +#else // _PNP_POWER +ULONG NbtDebug=0x00000000; // disable all debugging +#endif // _PNP_POWER +#endif // DBG + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +NbtDispatchCleanup( + IN PDEVICE_OBJECT Device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchClose( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchCreate( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ); + +NTSTATUS +NbtDispatchDevCtrl( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchInternalCtrl( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +PFILE_FULL_EA_INFORMATION +FindInEA( + IN PFILE_FULL_EA_INFORMATION start, + IN PCHAR wanted + ); + +VOID +ReturnIrp( + IN PIRP irp, + IN int status + ); + +VOID +MakePending( + IN PIRP pIrp + ); + +#ifdef RASAUTODIAL +VOID +NbtAcdBind(); +#endif // RASAUTODIAL + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(INIT, DriverEntry) +#ifdef RASAUTODIAL +#pragma CTEMakePageable(INIT, NbtAcdBind) +#endif // RASAUTODIAL +#pragma CTEMakePageable(PAGE, NbtDispatchCleanup) +#pragma CTEMakePageable(PAGE, NbtDispatchClose) +#pragma CTEMakePageable(PAGE, NbtDispatchCreate) +#pragma CTEMakePageable(PAGE, NbtDispatchDevCtrl) +#pragma CTEMakePageable(PAGE, FindInEA) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the NBT device driver. + This routine creates the device object for the NBT + device and calls a routine to perform other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + NTSTATUS status; + tADDRARRAY *pAddr; + tDEVICES *pBindDevices=NULL; + tDEVICES *pExportDevices=NULL; + tADDRARRAY *pAddrArray=NULL; + +#ifndef _PNP_LATER + int i; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + NTSTATUS Locstatus; + tDEVICECONTEXT *pDeviceContext; + ULONG DevicesStarted; +#endif // _PNP_POWER + + CTEPagedCode(); + +#ifdef _PNP_POWER + TdiInitialize(); +#endif + + // + // get the file system process for NBT since we need to know this for + // allocating and freeing handles + // + NbtFspProcess =(PEPROCESS)PsGetCurrentProcess(); + + // + // read in registry configuration data + // + status = NbtReadRegistry(RegistryPath, + DriverObject, + &NbtConfig, + &pBindDevices, + &pExportDevices, + &pAddrArray); + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Fatal Error - Failed registry read! status = %X\n", + status)); + return(status); + } + + + // + // Initialize NBT global data. + // + status = InitNotOs() ; + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_NON_OS_INIT,status); + KdPrint(("NBT:OS Independent initialization failed! status = %X\n", + status)); + return(status); + } + + // + // Initialize the driver object with this driver's entry points. + // + DriverObject->MajorFunction[IRP_MJ_CREATE] = + (PDRIVER_DISPATCH)NbtDispatchCreate; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + (PDRIVER_DISPATCH)NbtDispatchDevCtrl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = + (PDRIVER_DISPATCH)NbtDispatchInternalCtrl; + + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = + (PDRIVER_DISPATCH)NbtDispatchCleanup; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = + (PDRIVER_DISPATCH)NbtDispatchClose; + + DriverObject->DriverUnload = NULL; + +#ifndef _PNP_LATER + + // start some timers + status = InitTimersNotOs(); + + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_TIMERS,status); + KdPrint(("NBT:Failed to Initialize the Timers!,status = %X\n", + status)); + StopInitTimers(); + return(status); + } + +// #else // _PNP_POWER + + // ASSERT (status == STATUS_SUCCESS); + + NbtConfig.uDevicesStarted = 0; + +#endif // _PNP_POWER + + pNbtGlobConfig->iBufferSize[eNBT_FREE_SESSION_MDLS] = sizeof(tSESSIONHDR); + pNbtGlobConfig->iBufferSize[eNBT_DGRAM_MDLS] = DGRAM_HDR_SIZE + + (pNbtGlobConfig->ScopeLength << 1); + + // create some MDLs, for session sends to speed up the sends. + status = NbtInitMdlQ( + &NbtConfig.SessionMdlFreeSingleList, + eNBT_FREE_SESSION_MDLS); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Failed to Initialize the Session MDL Queue!,status = %X\n", + status)); + return(status); + } + // create some MDLs for datagram sends + status = NbtInitMdlQ( + &NbtConfig.DgramMdlFreeSingleList, + eNBT_DGRAM_MDLS); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Failed to Initialize the Dgram MDL Queue!,status = %X\n", + status)); + return(status); + } + +#ifndef _PNP_LATER + + // + // Create the NBT device object for each adapter configured + // + pAddr = pAddrArray; + + DevicesStarted = 0; + + for (i=0; i<pNbtGlobConfig->uNumDevices; i++ ) + { + + // this call ultimately allocates storage for the returned NameString + // that holds the Ipaddress + status = NbtCreateDeviceObject( + DriverObject, + pNbtGlobConfig, + &pBindDevices->Names[i], + &pExportDevices->Names[i], + pAddr, + RegistryPath, +#ifndef _IO_DELETE_DEVICE_SUPPORTED + FALSE, +#endif + &pDeviceContext); + + // for a Bnode there are no Wins server addresses, so this Ptr can + // be null. + if (pAddr) + { + pAddr++; + } + + // + // allow not having an address to succeed - DHCP will + // provide an address later + // + if (pDeviceContext != NULL) + { + if (status == STATUS_INVALID_ADDRESS) + { + // + // set to null so we know not to allow connections or dgram + // sends on this adapter + // + pDeviceContext->IpAddress = 0; + + DevicesStarted++; + + status = STATUS_SUCCESS; + + } + + + if ((NT_SUCCESS(status)) || + (status == STATUS_INVALID_ADDRESS_COMPONENT)) { + + status = STATUS_SUCCESS; + NbtConfig.uDevicesStarted++; + } + + + { + // + // We can tolerate the invalid address component failure since IP does not know of + // static addresses at this time. + // + if (!NT_SUCCESS(status)) + { + + pDeviceContext->RegistrationHandle = NULL; + + KdPrint((" Create Device Object Failed with status= %X, num devices = %X\n",status, + NbtConfig.uNumDevices)); + + NbtLogEvent(EVENT_NBT_CREATE_DEVICE,status); + // + // this device will not be started so decrement the count of started + // ones. + // + NbtConfig.AdapterCount--; + + // + // cleanup the mess and free the device object since we had some + // sort of failure. + // + pHead = &NbtConfig.DeviceContexts; + pEntry = RemoveTailList(pHead); + + ASSERT (pDeviceContext == CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage)); + + if (pDeviceContext->hNameServer) + { + ObDereferenceObject(pDeviceContext->pNameServerFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hNameServer); + KdPrint(("Close NameSrv File status = %X\n",Locstatus)); + } + if (pDeviceContext->hDgram) + { + ObDereferenceObject(pDeviceContext->pDgramFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hDgram); + KdPrint(("Close Dgram File status = %X\n",Locstatus)); + } + if (pDeviceContext->hSession) + { + ObDereferenceObject(pDeviceContext->pSessionFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hSession); + KdPrint(("Close Session File status = %X\n",Locstatus)); + } + if (pDeviceContext->hControl) + { + ObDereferenceObject(pDeviceContext->pControlFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hControl); + KdPrint(("Close Control File status = %X\n",Locstatus)); + } + + IoDeleteDevice((PDEVICE_OBJECT)pDeviceContext); + + } + else + { + // + // So we know that we need to register this device when an IP addres + // appears + // + pDeviceContext->RegistrationHandle = NULL; + DevicesStarted++; + pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + } + } + } + + } + // + // if no devices were created, then stop the timers and free the resources + // + if (DevicesStarted == 0) + { + ExDeleteResource(&NbtConfig.Resource); + StopInitTimers(); + } + else + { + // + // at least one device context was created successfully, so return success + // + status = STATUS_SUCCESS; + } + + if (NbtConfig.uNumDevices == 0) + { + NbtLogEvent(EVENT_NBT_NO_DEVICES,0); + } + +#endif // _PNP_POWER + + if (pBindDevices) + { + CTEMemFree((PVOID)pBindDevices->RegistrySpace); + CTEMemFree((PVOID)pBindDevices); + } + if (pExportDevices) + { + CTEMemFree((PVOID)pExportDevices->RegistrySpace); + CTEMemFree((PVOID)pExportDevices); + } + if (pAddrArray) + { + CTEMemFree((PVOID)pAddrArray); + } + +#ifndef _PNP_LATER + + // + // Get an Irp for the out of resource queue (used to disconnect sessions + // when really low on memory) + // + if (!IsListEmpty(&NbtConfig.DeviceContexts)) + { + pEntry = NbtConfig.DeviceContexts.Flink; + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + NbtConfig.OutOfRsrc.pIrp = NTAllocateNbtIrp(&pDeviceContext->DeviceObject); + + if (!NbtConfig.OutOfRsrc.pIrp) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // + // allocate a dpc structure and keep it: we might need if we hit an + // out-of-resource condition + // + NbtConfig.OutOfRsrc.pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('a')); + if (!NbtConfig.OutOfRsrc.pDpc) + { + IoFreeIrp(NbtConfig.OutOfRsrc.pIrp); + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + +#ifdef RASAUTODIAL + // + // Get the automatic connection driver + // entry points. + // + if (status == STATUS_SUCCESS) + { + NbtAcdBind(); + } +#endif + +#ifdef _PNP_POWER + + (void)TdiRegisterAddressChangeHandler( + AddressArrival, + AddressDeletion, + &AddressChangeHandle + ); + +#ifdef WATCHBIND + (void)TdiRegisterNotificationHandler( + BindHandler, + UnbindHandler, + &BindingHandle + ); +#endif // WATCHBIND + +#endif // _PNP_POWER + } + else + { + KdPrint(("NetBT!DriverEntry: Huh? Started NetBT with no devices!")); + } + +#endif // _PNP_POWER + + NbtConfig.InterfaceIndex = 0; + + // + // Return to the caller. + // + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchCleanup( + IN PDEVICE_OBJECT Device, + IN PIRP irp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for IRP_MJ_CLEANUP + requests. + + This function is called when the last reference to the handle is closed. + Hence, an NtClose() results in an IRP_MJ_CLEANUP first, and then an + IRP_MJ_CLOSE. This function runs down all activity on the object, and + when the close comes in the object is actually deleted. + +Arguments: + + device - ptr to device object for target device + irp - ptr to I/O request packet + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpsp; + tDEVICECONTEXT *pDeviceContext; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + irpsp = IoGetCurrentIrpStackLocation(irp); + + // check that we got the correct major function code + ASSERT(irpsp->MajorFunction == IRP_MJ_CLEANUP); + + // look at the context value that NBT put into the FSContext2 value to + // decide what to do + switch ((USHORT)irpsp->FileObject->FsContext2) + { + case NBT_ADDRESS_TYPE: + // the client is closing the address file, so we must cleanup + // and memory blocks associated with it. + status = NTCleanUpAddress(pDeviceContext,irp); + break; + + case NBT_CONNECTION_TYPE: + // the client is closing a connection, so we must clean up any + // memory blocks associated with it. + status = NTCleanUpConnection(pDeviceContext,irp); + break; + + case NBT_CONTROL_TYPE: + // there is nothing to do here.... + status = STATUS_SUCCESS; + break; + + default: + /* + * complete the i/o successfully. + */ + status = STATUS_SUCCESS; + break; + } + + // + // Complete the Irp + // + ReturnIrp(irp, status); + return(status); + + +} // DispatchCleanup + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchClose( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for IRP_MJ_CLOSE + requests. This is called after Cleanup (above) is called. + +Arguments: + + device - ptr to device object for target device + pIrp - ptr to I/O request packet + +Return Value: + + an NT status code. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpsp; + tDEVICECONTEXT *pDeviceContext; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + // + // close operations are synchronous. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = 0; + + irpsp = IoGetCurrentIrpStackLocation(pIrp); + ASSERT(irpsp->MajorFunction == IRP_MJ_CLOSE); + + switch ((ULONG)irpsp->FileObject->FsContext2) + { + case NBT_ADDRESS_TYPE: + status = NTCloseAddress(pDeviceContext,pIrp); + break; + + case NBT_CONNECTION_TYPE: + status = NTCloseConnection(pDeviceContext,pIrp); + break; + + case NBT_WINS_TYPE: + status = NTCloseWinsAddr(pDeviceContext,pIrp); + break; + + case NBT_CONTROL_TYPE: + // the client is closing the Control Object... + // there is nothing to do here.... + status = STATUS_SUCCESS; + break; + + default: + KdPrint(("Nbt:Close Received for unknown object type = %X\n", + irpsp->FileObject->FsContext2)); + status = STATUS_SUCCESS; + break; + } + + // NTCloseAddress can return Pending until the ref count actually gets + // to zero. + // + if (status != STATUS_PENDING) + { + ReturnIrp(pIrp, status); + } + + return(status); + +} // DispatchClose + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchCreate( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for IRP_MJ_CREATE + requests. It is called as a consequence of one of the following: + + a. TdiOpenConnection("\Device\Nbt_Elnkii0"), + b. TdiOpenAddress("\Device\Nbt_Elnkii0"), + +Arguments: + + Device - ptr to device object being opened + pIrp - ptr to I/O request packet + pIrp->Status => return status + pIrp->MajorFunction => IRP_MD_CREATE + pIrp->MinorFunction => not used + pIpr->FileObject => ptr to file obj created by I/O system. NBT fills in FsContext + pIrp->AssociatedIrp.SystemBuffer => ptr to EA buffer with address of obj to open(Netbios Name) + pIrp->Parameters.Create.EaLength => length of buffer specifying the Xport Addr. + +Return Value: + + STATUS_SUCCESS or STATUS_PENDING + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpsp; + PFILE_FULL_EA_INFORMATION ea; + tDEVICECONTEXT *pDeviceContext; + UCHAR IrpFlags; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + // + // If this device was destroyed, then reject all opens on it. + // Ideally we would like the IO sub-system to guarantee that no + // requests come down on IoDeleted devices, but..... + // + if (InterlockedExchangeAdd(&pDeviceContext->IsDestroyed, 0) != 0) { + // IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Rejecting Create minor Func\n")); + + pIrp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + pIrpsp = IoGetCurrentIrpStackLocation(pIrp); + ASSERT(pIrpsp->MajorFunction == IRP_MJ_CREATE); + IrpFlags = pIrpsp->Control; + + // + // set the pending flag here so that it is sure to be set BEFORE the + // completion routine gets hit. + // + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = STATUS_PENDING; + IoMarkIrpPending(pIrp); + + IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Internal Ctrl minor Func = %X\n",pIrpsp->MinorFunction)); + + /* + * was this a TdiOpenConnection() or TdiOpenAddress()? + * Get the Extended Attribute pointer and look at the text + * value passed in for a match with "TransportAddress" or + * "ConnectionContext" (in FindEa) + */ + ea = (PFILE_FULL_EA_INFORMATION) pIrp->AssociatedIrp.SystemBuffer; + + if (!ea) + { + // a null ea means open the control object + status = NTOpenControl(pDeviceContext,pIrp); + } + else + if (FindInEA(ea, TdiConnectionContext)) + { + // not allowed to pass in both a Connect Request and a Transport Address + ASSERT(!FindInEA(ea, TdiTransportAddress)); + status = NTOpenConnection(pDeviceContext,pIrp); + } + else + if (FindInEA(ea, TdiTransportAddress)) + { + status = NTOpenAddr(pDeviceContext,pIrp); + } + else + if (FindInEA(ea, WINS_INTERFACE_NAME)) + { + status = NTOpenWinsAddr(pDeviceContext,pIrp); + } + else + { + status = STATUS_INVALID_EA_NAME; + pIrpsp->Control = IrpFlags; + ReturnIrp(pIrp, status); + return(status); + } + + // complete the irp if the status is anything EXCEPT status_pending + // since the name query completion routine NTCompletIO completes pending + // open addresses + + if (status != STATUS_PENDING) + { + +#if DBG + // *TODO* for debug... + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: error return status = %X\n",status)); + //ASSERTMSG("An error Status reported from NBT",0L); + } +#endif + + // reset the pending returned bit, since we are NOT returning pending + pIrpsp->Control = IrpFlags; + + ReturnIrp(pIrp,status); + + } + + + return(status); + + + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchDevCtrl( + IN PDEVICE_OBJECT Device, + IN PIRP irp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for all + IRP_MJ_DEVICE_CONTROL requests. + +Arguments: + + device - ptr to device object for target device + irp - ptr to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpsp; + tDEVICECONTEXT *pDeviceContext; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + irpsp = IoGetCurrentIrpStackLocation(irp); + + // + // If this device was destroyed, then reject all requests on it. + // Ideally we would like the IO sub-system to guarantee that no + // requests come down on IoDeleted devices, but..... + // + if (InterlockedExchangeAdd(&pDeviceContext->IsDestroyed, 0) != 0) { + // IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Rejecting Dev Ctrl code = %X\n",irpsp->Parameters.DeviceIoControl.IoControlCode)); + irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + /* + * Initialize the I/O status block. + */ + irp->IoStatus.Status = STATUS_PENDING; + irp->IoStatus.Information = 0; + + ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL); + + IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt:DevCtrl hit with ControlCode == %X\n", + irpsp->Parameters.DeviceIoControl.IoControlCode)); + + if ((irpsp->Parameters.DeviceIoControl.IoControlCode >= IOCTL_NETBT_PURGE_CACHE) && + (irpsp->Parameters.DeviceIoControl.IoControlCode <IOCTL_NETBT_LAST_IOCTL)) + { + return(DispatchIoctls((tDEVICECONTEXT *)Device,irp, irpsp)); + } + else + { + /* + * if possible, convert the (external) device control into internal + * format, then treat it as if it had arrived that way. + */ + status = TdiMapUserRequest(Device, irp, irpsp); + + if (status == STATUS_SUCCESS) + { + return(NbtDispatchInternalCtrl(Device, irp)); + } + } + + ReturnIrp(irp, STATUS_INVALID_DEVICE_REQUEST); + return(STATUS_INVALID_DEVICE_REQUEST); + +} // NbtDispatchDevCtrl + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchInternalCtrl( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This is the driver's dispatch function for all + IRP_MJ_INTERNAL_DEVICE_CONTROL requests. + +Arguments: + + device - ptr to device object for target device + irp - ptr to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + tDEVICECONTEXT *pDeviceContext; + PIO_STACK_LOCATION pIrpsp; + NTSTATUS status; + UCHAR IrpFlags; + + pDeviceContext = (tDEVICECONTEXT *)Device; + + pIrpsp = IoGetCurrentIrpStackLocation(pIrp); + + // + // If this device was destroyed, then reject all operations on it. + // Ideally we would like the IO sub-system to guarantee that no + // requests come down on IoDeleted devices, but..... + // + if (InterlockedExchangeAdd(&pDeviceContext->IsDestroyed, 0) != 0) { + // IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Rejecting Internal minor fn. = %X\n",pIrpsp->MinorFunction)); + pIrp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + /* + * Initialize the I/O status block. + */ + + // + // this check if first to optimize the Send path + // + if (pIrpsp->MinorFunction ==TDI_SEND) + { + // + // this routine decides if it should complete the irp or not + // It never returns status pending, so we can turn off the + // pending bit + // + return( NTSend(pDeviceContext,pIrp) ); + + } + + IrpFlags = pIrpsp->Control; + + ASSERT(pIrpsp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); + + IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Internal Ctrl minor Func = %X\n",pIrpsp->MinorFunction)); + + switch (pIrpsp->MinorFunction) + { + case TDI_ACCEPT: + MakePending(pIrp); + status = NTAccept(pDeviceContext,pIrp); + break; + + case TDI_ASSOCIATE_ADDRESS: + MakePending(pIrp); + status = NTAssocAddress(pDeviceContext,pIrp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + MakePending(pIrp); + status = NTDisAssociateAddress(pDeviceContext,pIrp); + break; + + case TDI_CONNECT: + MakePending(pIrp); + status = NTConnect(pDeviceContext,pIrp); + break; + + case TDI_DISCONNECT: + MakePending(pIrp); + status = NTDisconnect(pDeviceContext,pIrp); + break; + + case TDI_LISTEN: + status = NTListen(pDeviceContext,pIrp); + return(status); + break; + + case TDI_QUERY_INFORMATION: + status = NTQueryInformation(pDeviceContext,pIrp); +#if DBG + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: Bad status from Query Info = %X\n",status)); + } +#endif + return(status); + break; + + case TDI_RECEIVE: + status = NTReceive(pDeviceContext,pIrp); + return(status); + + break; + + case TDI_RECEIVE_DATAGRAM: + status = NTReceiveDatagram(pDeviceContext,pIrp); + return(status); + break; + + + case TDI_SEND_DATAGRAM: + + status = NTSendDatagram(pDeviceContext,pIrp); +#if DBG + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: Bad status from Dgram Send = %X\n",status)); + } +#endif + return(status); + break; + + case TDI_SET_EVENT_HANDLER: + MakePending(pIrp); + status = NTSetEventHandler(pDeviceContext,pIrp); + break; + + case TDI_SET_INFORMATION: + MakePending(pIrp); + status = NTSetInformation(pDeviceContext,pIrp); + break; + + #if DBG + // + // 0x7f is a request by the redirector to put a "magic bullet" out on + // the wire, to trigger the Network General Sniffer. + // + case 0x7f: + KdPrint(("NBT:DispatchInternalCtrl - 07f minor function code\n")); + ReturnIrp(pIrp, STATUS_NOT_SUPPORTED); + return(STATUS_NOT_SUPPORTED); + + #endif /* DBG */ + + default: + KdPrint(("NBT:Dispatch Internal Ctl - invalid minor function %X\n", + pIrpsp->MinorFunction)); + ReturnIrp(pIrp, STATUS_INVALID_DEVICE_REQUEST); + return(STATUS_INVALID_DEVICE_REQUEST); + } + + // if the returned status is pending, then we do not complete the IRP + // here since it will be completed elsewhere in the code... + // + if (status != STATUS_PENDING) + { +#if DBG + // *TODO* for debug... + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBT:error return status = %X,MinorFunc = %X\n",status,pIrpsp->MinorFunction)); +// ASSERTMSG("An error Status reported from NBT",0L); + } + +#endif + pIrpsp->Control = IrpFlags; + + ReturnIrp(pIrp,status); + + } + + return(status); + + +} // NbtDispatchInternalCtrl + + +//---------------------------------------------------------------------------- +PFILE_FULL_EA_INFORMATION +FindInEA( + IN PFILE_FULL_EA_INFORMATION start, + IN PCHAR wanted + ) + +/*++ + +Routine Description: + + This function check for the "Wanted" string in the Ea structure and + returns a pointer to the extended attribute structure + representing the given extended attribute name. + +Arguments: + + device - ptr to device object for target device + pIrp - ptr to I/O request packet + +Return Value: + + pointer to the extended attribute structure, or NULL if not found. + +--*/ + +{ + PFILE_FULL_EA_INFORMATION eabuf; + + CTEPagedCode(); + + for (eabuf = start; eabuf; eabuf += eabuf->NextEntryOffset) + { + + if (strncmp(eabuf->EaName,wanted,eabuf->EaNameLength) == 0) + { + return eabuf; + } + + if (eabuf->NextEntryOffset == 0) + { + return((PFILE_FULL_EA_INFORMATION) NULL); + } + + } + return((PFILE_FULL_EA_INFORMATION) NULL); + +} // FindEA + + + +//---------------------------------------------------------------------------- +VOID +ReturnIrp( + IN PIRP pIrp, + IN int status + ) + +/*++ + +Routine Description: + + This function completes an IRP, and arranges for return parameters, + if any, to be copied. + + Although somewhat a misnomer, this function is named after a similar + function in the SpiderSTREAMS emulator. + +Arguments: + + pIrp - pointer to the IRP to complete + status - completion status of the IRP + +Return Value: + + number of bytes copied back to the user. + +--*/ + +{ + KIRQL oldlevel; + CCHAR priboost; + + // + // pIrp->IoStatus.Information is meaningful only for STATUS_SUCCESS + // + + // set the Irps cancel routine to null or the system may bugcheck + // with a bug code of CANCEL_STATE_IN_COMPLETED_IRP + // + // refer to IoCancelIrp() ..\ntos\io\iosubs.c + // + IoAcquireCancelSpinLock(&oldlevel); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(oldlevel); + + pIrp->IoStatus.Status = status; + + priboost = (CCHAR) ((status == STATUS_SUCCESS) ? + IO_NETWORK_INCREMENT : IO_NO_INCREMENT); + + IoCompleteRequest(pIrp, priboost); + + return; + +} +//---------------------------------------------------------------------------- +VOID +MakePending( + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This function marks an irp pending and sets the correct status. + +Arguments: + + pIrp - pointer to the IRP to complete + status - completion status of the IRP + +Return Value: + + +--*/ + +{ + IoMarkIrpPending(pIrp); + pIrp->IoStatus.Status = STATUS_PENDING; + pIrp->IoStatus.Information = 0; + +} + diff --git a/private/ntos/nbt/nt/fileio.c b/private/ntos/nbt/nt/fileio.c new file mode 100644 index 000000000..5490c3acd --- /dev/null +++ b/private/ntos/nbt/nt/fileio.c @@ -0,0 +1,384 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fileio.c + +Abstract: + + This source implements a stdio-like facility. + +Author: + + Jim Stewart June 1993 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "hosts.h" +#include <string.h> + + +// +// Private Definitions +// + + + +// +// Local Variables +// + + + +// +// Local (Private) Functions +// +PUCHAR +LmpMapFile ( + IN HANDLE handle, + IN OUT int *pnbytes + ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, LmCloseFile) +#pragma CTEMakePageable(PAGE, LmFgets) +#pragma CTEMakePageable(PAGE, LmpMapFile) +#pragma CTEMakePageable(PAGE, LmOpenFile) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- + +NTSTATUS +LmCloseFile ( + IN PLM_FILE pfile + ) + +/*++ + +Routine Description: + + This function closes a file opened via LmOpenFile(), and frees its + LM_FILE object. + +Arguments: + + pfile - pointer to the LM_FILE object + +Return Value: + + An NTSTATUS value. + +--*/ + + +{ + NTSTATUS status; + + CTEPagedCode(); + CTEMemFree(pfile->f_buffer); + + status = ZwClose(pfile->f_handle); + + ASSERT(status == STATUS_SUCCESS); + + CTEMemFree(pfile); + + return(status); + +} // LmCloseFile + + + +//---------------------------------------------------------------------------- + +PUCHAR +LmFgets ( + IN PLM_FILE pfile, + OUT int *nbytes + ) + +/*++ + +Routine Description: + + This function is vaguely similar to fgets(3). + + Starting at the current seek position, it reads through a newline + character, or the end of the file. If a newline is encountered, it + is replaced with a NULL character. + +Arguments: + + pfile - file to read from + nbytes - the number of characters read, excluding the NULL character + +Return Value: + + A pointer to the beginning of the line, or NULL if we are at or past + the end of the file. + +--*/ + + +{ + PUCHAR endOfLine; + PUCHAR startOfLine; + size_t maxBytes; + + CTEPagedCode(); + startOfLine = pfile->f_current; + + if (startOfLine >= pfile->f_limit) + { + + return((PUCHAR) NULL); + } + + maxBytes = pfile->f_limit - pfile->f_current; + endOfLine = (PUCHAR) memchr(startOfLine, (UCHAR) '\n', maxBytes); + + if (!endOfLine) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: lmhosts file doesn't end in '\\n'")); + endOfLine = pfile->f_limit; + } + + *endOfLine = (UCHAR) NULL; + + pfile->f_current = endOfLine + 1; + (pfile->f_lineno)++; + ASSERT(pfile->f_current <= pfile->f_limit+1); + + *nbytes = endOfLine - startOfLine; + + return(startOfLine); + +} // LmFgets + + + +//---------------------------------------------------------------------------- + +PUCHAR +LmpMapFile ( + IN HANDLE handle, + IN OUT int *pnbytes + ) + +/*++ + +Routine Description: + + This function reads an entire file into memory. + +Arguments: + + handle - file handle + pnbytes - size of the whole file + + +Return Value: + + the buffer allocated, or NULL if unsuccessful. + +--*/ + + +{ + PUCHAR buffer; + NTSTATUS status; + IO_STATUS_BLOCK iostatus; + FILE_STANDARD_INFORMATION stdInfo; + LARGE_INTEGER offset ={0, 0}; + LARGE_INTEGER length ={0x7fffffff, 0x7fffffff}; + + CTEPagedCode(); + + + status = ZwQueryInformationFile( + handle, // FileHandle + &iostatus, // IoStatusBlock + (PVOID) &stdInfo, // FileInformation + sizeof(stdInfo), // Length + FileStandardInformation); // FileInformationClass + + if (status != STATUS_SUCCESS) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: ZwQueryInformationFile(std) = %X\n", status)); + return(NULL); + } + + length = stdInfo.EndOfFile; // structure copy + + if (length.HighPart) + { + return(NULL); + } + + buffer = ExAllocatePool(NonPagedPool, length.LowPart+2); + + if (buffer != NULL) + { + + status = ZwReadFile( + handle, // FileHandle + NULL, // Event + NULL, // ApcRoutine + NULL, // ApcContext + &iostatus, // IoStatusBlock + buffer, // Buffer + length.LowPart, // Length + &offset, // ByteOffset + NULL); // Key + + if (status != STATUS_SUCCESS) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: ZwReadFile(std) = %X\n", status)); + } + + ASSERT(status != STATUS_PENDING); + + if (iostatus.Status != STATUS_SUCCESS || status != STATUS_SUCCESS) + { + CTEMemFree(buffer); + return(NULL); + } + + *pnbytes = length.LowPart; + } + return(buffer); + +} // LmpMapFile + + + +//---------------------------------------------------------------------------- + +PLM_FILE +LmOpenFile ( + IN PUCHAR path + ) + +/*++ + +Routine Description: + + This function opens a file for use by LmFgets(). + +Arguments: + + path - a fully specified, complete path to the file. + +Return Value: + + A pointer to an LM_FILE object, or NULL if unsuccessful. + +--*/ + + +{ + NTSTATUS status; + HANDLE handle; + PLM_FILE pfile; + IO_STATUS_BLOCK iostatus; + OBJECT_ATTRIBUTES attributes; + UNICODE_STRING ucPath; + PUCHAR start; + int nbytes; + OEM_STRING String; + PUCHAR LongerPath; + + + CTEPagedCode(); + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + status = LmGetFullPath(path,&LongerPath); + + if (NT_SUCCESS(status)) + { + RtlInitString(&String,LongerPath); + + status = RtlAnsiStringToUnicodeString(&ucPath,&String,TRUE); + + if (NT_SUCCESS(status)) + { + + InitializeObjectAttributes( + &attributes, // POBJECT_ATTRIBUTES + &ucPath, // ObjectName + OBJ_CASE_INSENSITIVE, // Attributes + (HANDLE) NULL, // RootDirectory + (PSECURITY_DESCRIPTOR) NULL); // SecurityDescriptor + + status = ZwCreateFile( + &handle, // FileHandle + SYNCHRONIZE | FILE_READ_DATA, // DesiredAccess + &attributes, // ObjectAttributes + &iostatus, // IoStatusBlock + 0, // AllocationSize + FILE_ATTRIBUTE_NORMAL, // FileAttributes + FILE_SHARE_READ | FILE_SHARE_WRITE, // ShareAccess + FILE_OPEN, // CreateDisposition + FILE_SYNCHRONOUS_IO_NONALERT, // OpenOptions + NULL, // EaBuffer + 0); // EaLength + + if (NT_SUCCESS(status)) + { + start = LmpMapFile(handle, &nbytes); + + if (start) + { + pfile = (PLM_FILE) ExAllocatePool(NonPagedPool, sizeof(LM_FILE)); + + if (pfile) + { + KeInitializeSpinLock(&(pfile->f_lock)); + + pfile->f_refcount = 1; + pfile->f_handle = handle; + pfile->f_lineno = 0; + pfile->f_fileOffset.HighPart = 0; + pfile->f_fileOffset.LowPart = 0; + + pfile->f_current = start; + pfile->f_buffer = start; + pfile->f_limit = pfile->f_buffer + nbytes; + + RtlFreeUnicodeString(&ucPath); + CTEMemFree(LongerPath); + + return(pfile); + } + + CTEMemFree(start); + } + + ZwClose(handle); + } + + RtlFreeUnicodeString(&ucPath); + + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: ZwOpenFile(std) = %X\n", status)); + + } + + CTEMemFree(LongerPath); + } + + return((PLM_FILE) NULL); + +} // LmOpenFile + + diff --git a/private/ntos/nbt/nt/mp/makefile b/private/ntos/nbt/nt/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nt/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nt/mp/netbt.prf b/private/ntos/nbt/nt/mp/netbt.prf new file mode 100644 index 000000000..84914dcb9 --- /dev/null +++ b/private/ntos/nbt/nt/mp/netbt.prf @@ -0,0 +1,170 @@ +SendCompletion@12 +NbtDispatchInternalCtrl@8 +NTSend@8 +TdiReceiveHandler@32 +CompletionRcv@12 +ProcessIrp@24 +CTECountedAllocMem@8 +DgramHndlrNotOs@48 +ConvertToAscii@20 +UdpSendDatagram@28 +NbtDereferenceClient@4 +NTIoComplete@12 +MakeRemoteAddressStructure@16 +FindName@16 +GetIrp@4 +TdiSendDatagram@24 +DgramSendComplete@12 +FindInHashTable@16 +SendDgramCompletion@12 +CTECountedFreeMem@8 +DgramSendCleanupTracker@12 +NbtDereferenceName@4 +TdiRcvDatagramHandler@44 +GetNetBiosNameFromTransportAddress@12 +FindNameOrQuery@28 +NbtAllocTracker@0 +NbtDereferenceAddress@4 +NTSendDatagram@8 +NbtSendDatagram@28 +SendDgram@8 +ConvertToHalfAscii@16 +BuildSendDgramHdr@32 +RemoteHashTimeout@12 +TimerExpiry@16 +CTEStartTimer@16 +NTQueryInformation@8 +NTOpenConnection@8 +CreateDeviceString@8 +FreeTracker@8 +GetTracker@4 +SubmitTdiRequest@8 +TdiRcvNameSrvHandler@44 +GetEntry@12 +UdpSendNSBcast@36 +NDgramSendCompleted@12 +CreatePdu@32 +FindInEA@8 +MSnodeCompletion@12 +NbtOpenAndAssocConnection@8 +SrcIsUs@4 +AddToPendingList@8 +QueryNameOnNet@44 +NbtTdiAssociateConnection@8 +CompleteClientReq@12 +ClearCancelRoutine@4 +StartTimer@32 +StartLmHostTimer@8 +LmHostQueueRequest@16 +GetNameToFind@4 +RemoveNameAndCompleteReq@8 +RemoveName@4 +NameSrvHndlrNotOs@16 +ReturnIrp@8 +CheckRegistrationFromNet@16 +DereferenceTracker@4 +CTEInitTimer@4 +TimeoutQEntries@12 +LmHostTimeout@12 +GetContext@4 +MakePending@4 +FindNameRemoteThenLocal@8 +NbtOpenConnection@12 +NbtTdiOpenConnection@8 +NbtDispatchCreate@8 +SendDgramContinue@8 +CompletionRoutine@12 +AllocateMdl@4 +NTAssocAddress@8 +NbtAssociateAddress@12 +NTAllocateNbtIrp@4 +NbtGetMdl@8 +NbtListen@20 +NbtAllocateClientBlock@4 +NbtSetEventHandler@16 +InterlockedCallCompletion@8 +NTSetEventHandler@8 +FindInDomainList@8 +NbtRegisterName@32 +NTCheckSetCancelRoutine@12 +InitTimerQ@4 +CountLocalNames@4 +NbtInitConnQ@16 +AddToHashTable@28 +NbtInitTrackerQ@8 +NbtAppendString@12 +NbtQueryAdapterStatus@12 +QueryProviderCompletion@12 +NbtAddPermanentName@4 +ReadParameters2@8 +IncrementNameStats@8 +MSnodeRegCompletion@12 +NbtRegisterCompletion@8 +NbtOpenAddress@24 +InitQ@12 +NTSetFileObjectContexts@12 +NbtCreateDeviceObject@24 +CreateHashTable@12 +InitNotOs@0 +InitTimersNotOs@0 +TcpSendSessionResponse@12 +TcpSendSession@12 +Inbound@32 +CompleteSessionSetup@20 +SessionRespDone@12 +UdpSendResponse@32 +QueryRespDone@12 +SrcIsNameServer@8 +QueryFromNet@20 +NbtDispatchCleanup@8 +ConvertDottedDecimalToUlong@8 +NTGetLmHostPath@4 +NbtDispatchClose@8 +NbtDispatchDevCtrl@8 +NTOpenControl@8 +NbtCreateAddressObjects@12 +NTSetSharedAccess@12 +FindSessionEndPoint@24 +DispatchIoctls@12 +ReadParameters@8 +NTListen@8 +ConvertToUlong@8 +NbtInitMdlQ@8 +GetExtendedAttributes@4 +ReadNameServerAddresses@16 +GetServerAddress@12 +NbtReadRegistry@24 +GetIPFromRegistry@16 +NbtReadLinkageInformation@16 +NTReadIniString@12 +ReadElement@12 +NbtReadSingleParameter@16 +NbtOpenRegistry@12 +NbtFindLastSlash@12 +ReadStringRelative@16 +OpenAndReadElement@12 +SetEventHandler@20 +NbtTdiOpenAddress@28 +NbtTdiOpenControl@4 +SetWinsDownFlag@4 +LmGetFullPath@8 +TdiConnectHandler@36 +NTOpenAddr@8 +AcceptCompletionRoutine@12 +NTCheckSharedAccess@12 +LmOpenFile@4 +LmpBreakRecursion@8 +SendSessionCompletionRoutine@12 +ScanLmHostFile@4 +TdiSend@24 +NTReceive@8 +ClientTookSomeOfTheData@20 +LmGetIpAddr@16 +ConnectHndlrNotOs@24 +NTProcessAcceptIrp@8 +InitRemoteHashTable@12 +DriverEntry@8 +ReadLmHostFile@8 +ReadScope@8 +ClearConnStructures@8 +NbtQueryConnectionList@12 diff --git a/private/ntos/nbt/nt/mp/sources b/private/ntos/nbt/nt/mp/sources new file mode 100644 index 000000000..33e022722 --- /dev/null +++ b/private/ntos/nbt/nt/mp/sources @@ -0,0 +1,30 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib +TARGETLIBS=..\..\nbt\mp\obj\*\nbt.lib + +!include ..\sources.inc diff --git a/private/ntos/nbt/nt/netbt.rc b/private/ntos/nbt/nt/netbt.rc new file mode 100644 index 000000000..198701954 --- /dev/null +++ b/private/ntos/nbt/nt/netbt.rc @@ -0,0 +1,13 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "MBT Transport driver" + +#define VER_INTERNALNAME_STR "netbt.sys" +#define VER_ORIGINALFILENAME_STR "netbt.sys" + +#include <common.ver> + diff --git a/private/ntos/nbt/nt/netbtkd/kdextlib.c b/private/ntos/nbt/nt/netbtkd/kdextlib.c new file mode 100644 index 000000000..132d57b5d --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/kdextlib.c @@ -0,0 +1,843 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + kdextlib.c + +Abstract: + + Library routines for dumping data structures given a meta level descrioption + +Author: + + Balan Sethu Raman (SethuR) 11-May-1994 + +Notes: + The implementation tends to avoid memory allocation and deallocation as much as possible. + Therefore We have choosen an arbitrary length as the default buffer size. A mechanism will + be provided to modify this buffer length through the debugger extension commands. + +Revision History: + + 11-Nov-1994 SethuR Created + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include "ntverp.h" + +#define KDEXTMODE + +#include <windef.h> +#include <ntkdexts.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <kdextlib.h> + +PNTKD_OUTPUT_ROUTINE lpOutputRoutine; +PNTKD_GET_EXPRESSION lpGetExpressionRoutine; +PNTKD_GET_SYMBOL lpGetSymbolRoutine; +PNTKD_READ_VIRTUAL_MEMORY lpReadMemoryRoutine; + +#define PRINTF lpOutputRoutine +#define ERROR lpOutputRoutine + +#define NL 1 +#define NONL 0 + +#define SETCALLBACKS() \ + lpOutputRoutine = lpExtensionApis->lpOutputRoutine; \ + lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine; \ + lpGetSymbolRoutine = lpExtensionApis->lpGetSymbolRoutine; \ + lpReadMemoryRoutine = lpExtensionApis->lpReadVirtualMemRoutine; + +#define DEFAULT_UNICODE_DATA_LENGTH 512 +USHORT s_UnicodeStringDataLength = DEFAULT_UNICODE_DATA_LENGTH; +WCHAR s_UnicodeStringData[DEFAULT_UNICODE_DATA_LENGTH]; +WCHAR *s_pUnicodeStringData = s_UnicodeStringData; + +#define DEFAULT_ANSI_DATA_LENGTH 512 +USHORT s_AnsiStringDataLength = DEFAULT_ANSI_DATA_LENGTH; +CHAR s_AnsiStringData[DEFAULT_ANSI_DATA_LENGTH]; +CHAR *s_pAnsiStringData = s_AnsiStringData; + +// +// No. of columns used to display struct fields; +// + +ULONG s_MaxNoOfColumns = 3; +ULONG s_NoOfColumns = 1; + +/* + * Fetches the data at the given address + */ +BOOLEAN +GetData( DWORD dwAddress, PVOID ptr, ULONG size) +{ + BOOL b; + ULONG BytesRead; + + b = (lpReadMemoryRoutine)((LPVOID) dwAddress, ptr, size, &BytesRead ); + + + if (!b || BytesRead != size ) { + return FALSE; + } + + return TRUE; +} + +/* + * Fetch the null terminated ASCII string at dwAddress into buf + */ +BOOL +GetString( DWORD dwAddress, PSZ buf ) +{ + do { + if( !GetData( dwAddress,buf, 1) ) + return FALSE; + + dwAddress++; + buf++; + + } while( *buf != '\0' ); + + return TRUE; +} + +/* + * Displays a byte in hexadecimal + */ +VOID +PrintHexChar( UCHAR c ) +{ + PRINTF( "%c%c", "0123456789abcdef"[ (c>>4)&7 ], "0123456789abcdef"[ c&7 ] ); +} + +/* + * Displays a buffer of data in hexadecimal + */ +VOID +PrintHexBuf( PUCHAR buf, ULONG cbuf ) +{ + while( cbuf-- ) { + PrintHexChar( *buf++ ); + PRINTF( " " ); + } +} + +/* + * Displays a unicode string + */ +BOOL +PrintStringW(LPSTR msg, PUNICODE_STRING puStr, BOOL nl ) +{ + UNICODE_STRING UnicodeString; + ANSI_STRING AnsiString; + BOOLEAN b; + + if( msg ) + PRINTF( msg ); + + if( puStr->Length == 0 ) { + if( nl ) + PRINTF( "\n" ); + return TRUE; + } + + UnicodeString.Buffer = s_pUnicodeStringData; + UnicodeString.MaximumLength = s_UnicodeStringDataLength; + UnicodeString.Length = (puStr->Length > s_UnicodeStringDataLength) + ? s_UnicodeStringDataLength + : puStr->Length; + + b = (lpReadMemoryRoutine)( + (LPVOID) puStr->Buffer, + UnicodeString.Buffer, + UnicodeString.Length, + NULL); + + if (b) { + RtlUnicodeStringToAnsiString(&AnsiString, puStr, TRUE); + PRINTF("%s%s", AnsiString.Buffer, nl ? "\n" : "" ); + RtlFreeAnsiString(&AnsiString); + } + + return b; +} + +/* + * Displays a ANSI string + */ +BOOL +PrintStringA(LPSTR msg, PANSI_STRING pStr, BOOL nl ) +{ + ANSI_STRING AnsiString; + BOOLEAN b; + + if( msg ) + PRINTF( msg ); + + if( pStr->Length == 0 ) { + if( nl ) + PRINTF( "\n" ); + return TRUE; + } + + AnsiString.Buffer = s_pAnsiStringData; + AnsiString.MaximumLength = s_AnsiStringDataLength; + AnsiString.Length = (pStr->Length > (s_AnsiStringDataLength - 1)) + ? (s_AnsiStringDataLength - 1) + : pStr->Length; + + b = (lpReadMemoryRoutine)( + (LPVOID) pStr->Buffer, + AnsiString.Buffer, + AnsiString.Length, + NULL); + + if (b) { + AnsiString.Buffer[ AnsiString.Length ] = '\0'; + PRINTF("%s%s", AnsiString.Buffer, nl ? "\n" : "" ); + } + + return b; +} + +/* + * Displays all the fields of a given struct. This is the driver routine that is called + * with the appropriate descriptor array to display all the fields in a given struct. + */ + +char *NewLine = "\n"; +char *FieldSeparator = " "; +char *DotSeparator = "."; +#define NewLineForFields(FieldNo) \ + ((((FieldNo) % s_NoOfColumns) == 0) ? NewLine : FieldSeparator) +#define FIELD_NAME_LENGTH 30 + +VOID +PrintStructFields( DWORD dwAddress, VOID *ptr, FIELD_DESCRIPTOR *pFieldDescriptors ) +{ + int i; + int j; + BYTE ch; + + // Display the fields in the struct. + for( i=0; pFieldDescriptors->Name; i++, pFieldDescriptors++ ) { + + // Indentation to begin the struct display. + PRINTF( " " ); + + if( strlen( pFieldDescriptors->Name ) > FIELD_NAME_LENGTH ) { + PRINTF( "%-17s...%s ", pFieldDescriptors->Name, pFieldDescriptors->Name+strlen(pFieldDescriptors->Name)-10 ); + } else { + PRINTF( "%-30s ", pFieldDescriptors->Name ); + } + + PRINTF( "(0x%-2X) ", pFieldDescriptors->Offset ); + + switch( pFieldDescriptors->FieldType ) { + case FieldTypeByte: + case FieldTypeChar: + PRINTF( "%-16d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeBoolean: + PRINTF( "%-16s%s", + *(BOOLEAN *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE", + NewLineForFields(i)); + break; + + case FieldTypeBool: + PRINTF( "%-16s%s", + *(BOOLEAN *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE", + NewLineForFields(i)); + break; + + case FieldTypePointer: + PRINTF( "%-16X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeULongULong: + PRINTF( "%d%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + FieldSeparator ); + PRINTF( "%d%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset + sizeof(ULONG)), + NewLineForFields(i) ); + break; + + case FieldTypeListEntry: + + if ( (ULONG)(dwAddress + pFieldDescriptors->Offset) == + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset )) + { + PRINTF( "%s", "List Empty\n" ); + } + else + { + PRINTF( "%-8X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + FieldSeparator ); + PRINTF( "%-8X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset + sizeof(ULONG)), + NewLineForFields(i) ); + } + break; + + // Ip address: 4 bytes long + case FieldTypeIpAddr: + PRINTF( "%X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + FieldSeparator ); + PRINTF( "(%d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 3), + DotSeparator ); + PRINTF( "%d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 2 ), + DotSeparator ); + PRINTF( "%d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 1 ), + DotSeparator ); + PRINTF( "%d)%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + // Mac address: 6 bytes long + case FieldTypeMacAddr: + for (j=0; j<5; j++) + { + PRINTF( "%X%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j), + FieldSeparator ); + } + PRINTF( "%X%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 5), + NewLineForFields(i) ); + break; + + // Netbios name: 16 bytes long + case FieldTypeNBName: + // + // if first byte is printable, print the first 15 bytes as characters + // and 16th byte as a hex value. otherwise, print all the 16 bytes + // as hex values + // + ch = *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset); + if (ch >= 0x20 && ch <= 0x7e) + { + for (j=0; j<15; j++) + { + PRINTF( "%c", *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j)); + } + PRINTF( "<%X>%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 15), + NewLineForFields(i) ); + } + else + { + for (j=0; j<16; j++) + { + PRINTF( "%.2X", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j)); + } + PRINTF( "%s", NewLineForFields(i) ); + } + break; + + case FieldTypeULong: + case FieldTypeLong: + PRINTF( "%-16d%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeShort: + PRINTF( "%-16X%s", + *(SHORT *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeUShort: + PRINTF( "%-16X%s", + *(USHORT *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeUnicodeString: + PrintStringW( NULL, (UNICODE_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL ); + PRINTF( NewLine ); + break; + + case FieldTypeAnsiString: + PrintStringA( NULL, (ANSI_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL ); + PRINTF( NewLine ); + break; + + case FieldTypeSymbol: + { + UCHAR SymbolName[ 200 ]; + ULONG Displacement; + PVOID sym = (PVOID)(*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset )); + + lpGetSymbolRoutine( sym, SymbolName, &Displacement ); + PRINTF( "%-16s%s", + SymbolName, + NewLineForFields(i) ); + } + break; + + case FieldTypeEnum: + { + ULONG EnumValue; + ENUM_VALUE_DESCRIPTOR *pEnumValueDescr; + // Get the associated numericla value. + + EnumValue = *((ULONG *)((BYTE *)ptr + pFieldDescriptors->Offset)); + + if ((pEnumValueDescr = pFieldDescriptors->AuxillaryInfo.pEnumValueDescriptor) + != NULL) { + // + // An auxilary textual description of the value is + // available. Display it instead of the numerical value. + // + + LPSTR pEnumName = NULL; + + while (pEnumValueDescr->EnumName != NULL) { + if (EnumValue == pEnumValueDescr->EnumValue) { + pEnumName = pEnumValueDescr->EnumName; + break; + } + } + + if (pEnumName != NULL) { + PRINTF( "%-16s ", pEnumName ); + } else { + PRINTF( "%-4d (%-10s) ", EnumValue,"@$#%^&*"); + } + + } else { + // + // No auxilary information is associated with the ehumerated type + // print the numerical value. + // + PRINTF( "%-16d",EnumValue); + } + } + break; + + case FieldTypeStruct: + PRINTF( "@%-15X%s", + (dwAddress + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeLargeInteger: + case FieldTypeFileTime: + default: + ERROR( "Unrecognized field type %c for %s\n", pFieldDescriptors->FieldType, pFieldDescriptors->Name ); + break; + } + } +} + +LPSTR LibCommands[] = { + "dump <Struct Type Name>@<address expr> ", + "columns <d> -- controls the number of columns in the display ", + "logdump <Log Address> ", + 0 +}; + +BOOL +help( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + int i; + + SETCALLBACKS(); + + for( i=0; Extensions[i]; i++ ) + PRINTF( " %s\n", Extensions[i] ); + + for( i=0; LibCommands[i]; i++ ) + PRINTF( " %s\n", LibCommands[i] ); + + return TRUE; +} + + +BOOL +columns( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + ULONG NoOfColumns; + int i; + + SETCALLBACKS(); + + sscanf(lpArgumentString,"%ld",&NoOfColumns); + + if (NoOfColumns > s_MaxNoOfColumns) { + // PRINTF( "No. Of Columns exceeds maximum(%ld) -- directive Ignored\n", s_MaxNoOfColumns ); + } else { + s_NoOfColumns = NoOfColumns; + } + + PRINTF("Not Yet Implemented\n"); + + return TRUE; +} + + + +BOOL +globals( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + DWORD dwAddress; + CHAR buf[ 100 ]; + int i; + int c=0; + + SETCALLBACKS(); + + strcpy( buf, "srv!" ); + + for( i=0; GlobalBool[i]; i++, c++ ) { + BOOL b; + + strcpy( &buf[4], GlobalBool[i] ); + dwAddress = (lpGetExpressionRoutine) ( buf ); + if( dwAddress == 0 ) { + ERROR( "Unable to get address of %s\n", GlobalBool[i] ); + continue; + } + if( !GetData( dwAddress,&b, sizeof(b)) ) + return FALSE; + + PRINTF( "%s%-30s %10s%s", + c&1 ? " " : "", + GlobalBool[i], + b ? " TRUE" : "FALSE", + c&1 ? "\n" : "" ); + } + + for( i=0; GlobalShort[i]; i++, c++ ) { + SHORT s; + + strcpy( &buf[4], GlobalShort[i] ); + dwAddress = (lpGetExpressionRoutine) ( buf ); + if( dwAddress == 0 ) { + ERROR( "Unable to get address of %s\n", GlobalShort[i] ); + continue; + } + if( !GetData( dwAddress,&s,sizeof(s)) ) + return FALSE; + + PRINTF( "%s%-30s %10d%s", + c&1 ? " " : "", + GlobalShort[i], + s, + c&1 ? "\n" : "" ); + } + + for( i=0; GlobalLong[i]; i++, c++ ) { + LONG l; + + strcpy( &buf[4], GlobalLong[i] ); + dwAddress = (lpGetExpressionRoutine) ( buf ); + if( dwAddress == 0 ) { + ERROR( "Unable to get address of %s\n", GlobalLong[i] ); + continue; + } + if( !GetData( dwAddress,&l, sizeof(l)) ) + return FALSE; + + PRINTF( "%s%-30s %10d%s", + c&1 ? " " : "", + GlobalLong[i], + l, + c&1 ? "\n" : "" ); + } + + PRINTF( "\n" ); + + return TRUE; +} + + +BOOL +version +( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ +#if VER_DEBUG + char *kind = "checked"; +#else + char *kind = "free"; +#endif + + SETCALLBACKS(); + + PRINTF( "Redirector debugger Extension dll for %s build %u\n", kind, VER_PRODUCTBUILD ); + + return TRUE; +} + +#define NAME_DELIMITER '@' +#define NAME_DELIMITERS "@" +#define INVALID_INDEX 0xffffffff +#define MIN(x,y) ((x) < (y) ? (x) : (y)) + +ULONG SearchStructs(LPSTR lpArgument) +{ + ULONG i = 0; + STRUCT_DESCRIPTOR *pStructs = Structs; + ULONG NameIndex = INVALID_INDEX; + ULONG ArgumentLength = strlen(lpArgument); + BOOLEAN fAmbigous = FALSE; + + + while ((pStructs->StructName != 0)) { + int Result = _strnicmp(lpArgument, + pStructs->StructName, + MIN(strlen(pStructs->StructName),ArgumentLength)); + + if (Result == 0) { + if (NameIndex != INVALID_INDEX) { + // We have encountered duplicate matches. Print out the + // matching strings and let the user disambiguate. + fAmbigous = TRUE; + break; + } else { + NameIndex = i; + } + + } + pStructs++;i++; + } + + if (fAmbigous) { + PRINTF("Ambigous Name Specification -- The following structs match\n"); + PRINTF("%s\n",Structs[NameIndex].StructName); + PRINTF("%s\n",Structs[i].StructName); + while (pStructs->StructName != 0) { + if (_strnicmp(lpArgument, + pStructs->StructName, + MIN(strlen(pStructs->StructName),ArgumentLength)) == 0) { + PRINTF("%s\n",pStructs->StructName); + } + pStructs++; + } + PRINTF("Dumping Information for %s\n",Structs[NameIndex].StructName); + } + + return(NameIndex); +} + +VOID DisplayStructs() +{ + STRUCT_DESCRIPTOR *pStructs = Structs; + + PRINTF("The following structs are handled .... \n"); + while (pStructs->StructName != 0) { + PRINTF("\t%s\n",pStructs->StructName); + pStructs++; + } +} + +BOOL +dump( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + DWORD dwAddress; + + SETCALLBACKS(); + + if( lpArgumentString && *lpArgumentString ) { + // Parse the argument string to determine the structure to be displayed. + // Scan for the NAME_DELIMITER ( '@' ). + + LPSTR lpName = lpArgumentString; + LPSTR lpArgs = strpbrk(lpArgumentString, NAME_DELIMITERS); + ULONG Index; + + if (lpArgs) { + // + // The specified command is of the form + // dump <name>@<address expr.> + // + // Locate the matching struct for the given name. In the case + // of ambiguity we seek user intervention for disambiguation. + // + // We do an inplace modification of the argument string to + // facilitate matching. + // + *lpArgs = '\0'; + + Index = SearchStructs(lpName); + + // + // Let us restore the original value back. + // + + *lpArgs = NAME_DELIMITER; + + if (INVALID_INDEX != Index) { + BYTE DataBuffer[512]; + + dwAddress = (lpGetExpressionRoutine)( ++lpArgs ); + if (GetData(dwAddress,DataBuffer,Structs[Index].StructSize)) { + + PRINTF( + "++++++++++++++++ %s@%lx ++++++++++++++++\n", + Structs[Index].StructName, + dwAddress); + PrintStructFields( + dwAddress, + &DataBuffer, + Structs[Index].FieldDescriptors); + PRINTF( + "---------------- %s@%lx ----------------\n", + Structs[Index].StructName, + dwAddress); + } else { + PRINTF("Error reading Memory @ %lx\n",dwAddress); + } + } else { + // No matching struct was found. Display the list of + // structs currently handled. + + DisplayStructs(); + } + } else { + // + // The command is of the form + // dump <name> + // + // Currently we do not handle this. In future we will map it to + // the name of a global variable and display it if required. + // + + DisplayStructs(); + } + } else { + // + // display the list of structs currently handled. + // + + DisplayStructs(); + } + + return TRUE; +} + +#if 0 +BOOL +logdump( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + DWORD dwAddress; + BYTE DataBuffer[512]; + + SETCALLBACKS(); + + if( lpArgumentString && *lpArgumentString ) { + RX_LOG RxLog; + + dwAddress = (lpGetExpressionRoutine)(lpArgumentString); + if (GetData(dwAddress,&RxLog,sizeof(RX_LOG))) { + // Dump the log header followed by the log entries ... + ULONG dwCurEntry; + + PRINTF("s_RxLog.State %lx\n",RxLog.State); + PRINTF("s_RxLog.pHeadEntry %lx\n",RxLog.pHeadEntry); + PRINTF("s_RxLog.pTailEntry %lx\n",RxLog.pTailEntry); + PRINTF("s_RxLog.LogBufferSize %lx\n",RxLog.LogBufferSize); + PRINTF("s_RxLog.pLogBuffer %lx\n",RxLog.pLogBuffer); + PRINTF("s_RxLog.pWrapAroundPoint %lx\n",RxLog.pWrapAroundPoint); + PRINTF("s_RxLog.NumberOfEntriesIgnored %lx\n",RxLog.NumberOfEntriesIgnored); + PRINTF("s_RxLog.NumberOfLogWriteAttempts %lx\n",RxLog.NumberOfLogWriteAttempts); + + dwCurEntry = (DWORD)RxLog.pHeadEntry; + for (;;) { + PRX_LOG_ENTRY_HEADER pHeader; + ULONG LogRecordLength; + DWORD dwNextEntry; + + if (!GetData(dwCurEntry,DataBuffer,sizeof(RX_LOG_ENTRY_HEADER))) { + PRINTF("Error reading Memory @ %lx\n",dwAddress); + break; + } + + pHeader = (PRX_LOG_ENTRY_HEADER)DataBuffer; + LogRecordLength = pHeader->EntrySize - sizeof(RX_LOG_ENTRY_HEADER); + dwNextEntry = dwCurEntry + pHeader->EntrySize; + + if ((pHeader->EntrySize > 0) && + GetData((dwCurEntry + sizeof(RX_LOG_ENTRY_HEADER)), + DataBuffer, + LogRecordLength)) { + DataBuffer[LogRecordLength] = '\0'; + PRINTF("%s",DataBuffer); + } + + + if (RxLog.pTailEntry > RxLog.pHeadEntry) { + if (dwNextEntry > (DWORD)RxLog.pTailEntry) { + break; + } + } else { + if (dwNextEntry > (DWORD)RxLog.pHeadEntry) { + if ((dwNextEntry >= (DWORD)RxLog.pWrapAroundPoint) || + (dwNextEntry >= (DWORD)((PBYTE)RxLog.pLogBuffer + RxLog.LogBufferSize))) { + dwNextEntry = (DWORD)RxLog.pLogBuffer; + } + } else if (dwNextEntry > (DWORD)RxLog.pTailEntry) { + break; + } + } + + dwCurEntry = dwNextEntry; + } + } else { + PRINTF("Error reading Memory @ %lx\n",dwAddress); + } + } else { + PRINTF("usage: logdump <log address>\n"); + } + + return TRUE; +} +#endif + diff --git a/private/ntos/nbt/nt/netbtkd/kdextlib.h b/private/ntos/nbt/nt/netbtkd/kdextlib.h new file mode 100644 index 000000000..10bb0fbf9 --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/kdextlib.h @@ -0,0 +1,139 @@ + +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + rdr2kd.h + +Abstract: + + Redirector Kernel Debugger extension + +Author: + + Balan Sethu Raman (SethuR) 11-May-1994 + +Revision History: + + 11-Nov-1994 SethuR Created + +--*/ + +#ifndef _KDEXTLIB_H_ +#define _KDEXTLIB_H_ + +#include <windef.h> + +// +// The help strings printed out +// + +extern LPSTR Extensions[]; + +// +// The FIELD_DESCRIPTOR data structure is used to describe the field in a structure sufficiently +// for displaying information during debugging. The three pieces of information that are required +// are 1) the name of the field, 2) the offset in the corresponding structure and 3) a type descriptor. +// The type descriptor covers most primitive types. +// +// The task of generating these descriptors by augmenting the front end, but that will have to +// wait till we play around with these extensions and modify the data structures to meet most +// of the requirements. +// +// There are some types that can benefit from some auxillary information in the descriptors. A +// case in point is the "enum" defeinition. Merely printing out a numerical value for an enum +// type will invariably force the person using these extensions to refer to the corresponding +// include file. In order to avoid this we will accept an additional array for enum types that +// contains a textual description of the numerical value. +// +// There are certain conventions that have been adopted to ease the definition of the macros +// as well as facilitate the automation of the generation of these descriptors. +// These are as follows .... +// +// 1) All ENUM_VALUE_DESCRIPTOR definitions are named EnumValueDescrsOf_ENUMTYPENAME, where +// ENUMTYPENAME defines the corresponding enumerated type. +// + +typedef struct _ENUM_VALUE_DESCRIPTOR { + ULONG EnumValue; + LPSTR EnumName; +} ENUM_VALUE_DESCRIPTOR; + +typedef enum _FIELD_TYPE_CLASS { + FieldTypeByte, + FieldTypeChar, + FieldTypeBoolean, + FieldTypeBool, + FieldTypeULong, + FieldTypeLong, + FieldTypeUShort, + FieldTypeShort, + FieldTypePointer, + FieldTypeULongULong, + FieldTypeListEntry, + FieldTypeIpAddr, + FieldTypeMacAddr, + FieldTypeNBName, + FieldTypeUnicodeString, + FieldTypeAnsiString, + FieldTypeSymbol, + FieldTypeEnum, + FieldTypeByteBitMask, + FieldTypeWordBitMask, + FieldTypeDWordBitMask, + FieldTypeFloat, + FieldTypeDouble, + FieldTypeStruct, + FieldTypeLargeInteger, + FieldTypeFileTime +} FIELD_TYPE_CLASS, *PFIELD_TYPE_CLASS; + +typedef struct _FIELD_DESCRIPTOR_ { + FIELD_TYPE_CLASS FieldType; // The type of variable to be printed + LPSTR Name; // The name of the field + USHORT Offset; // The offset of the field in the structure + union { + ENUM_VALUE_DESCRIPTOR *pEnumValueDescriptor; // Auxillary information for enumerated types. + } AuxillaryInfo; +} FIELD_DESCRIPTOR; + +#define FIELD3(FieldType,StructureName, FieldName) \ + {FieldType, #FieldName , FIELD_OFFSET(StructureName,FieldName) ,NULL} + +#define FIELD4(FieldType, StructureName, FieldName, AuxInfo) \ + {FieldType, #FieldName , FIELD_OFFSET(StructureName,FieldName) ,AuxInfo} + +// +// The structs that are displayed by the debugger extensions are further +// described in another array. Each entry in the array contains the name of +// the structure and the associated Field descriptor list. +// + +typedef struct _STRUCT_DESCRITOR_ { + LPSTR StructName; + ULONG StructSize; + FIELD_DESCRIPTOR *FieldDescriptors; +} STRUCT_DESCRIPTOR; + +#define STRUCT(StructTypeName,FieldDescriptors) \ + { #StructTypeName,sizeof(StructTypeName),FieldDescriptors} + +// +// The array of structs handled by the debugger extension. +// + +extern STRUCT_DESCRIPTOR Structs[]; + +// +// Support for displaying global variables +// + +extern LPSTR GlobalBool[]; +extern LPSTR GlobalShort[]; +extern LPSTR GlobalLong[]; +extern LPSTR GlobalPtrs[]; + +#endif // _KDEXTLIB_H_ + diff --git a/private/ntos/nbt/nt/netbtkd/makefile b/private/ntos/nbt/nt/netbtkd/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nt/netbtkd/netbtkd.c b/private/ntos/nbt/nt/netbtkd/netbtkd.c new file mode 100644 index 000000000..6e1c60b41 --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/netbtkd.c @@ -0,0 +1,369 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + netbtkd.c + +Abstract: + + Netbt Kernel Debugger extension + +Author: + + Shirish Koti + +Revision History: + + 6-Jun-1991 Koti Created + +--*/ + +#include "types.h" + +#include <kdextlib.h> + +/* + * RDR2 global variables. + * + */ + +LPSTR GlobalBool[] = {0}; +LPSTR GlobalShort[] = {0}; +LPSTR GlobalLong[] = {0}; +LPSTR GlobalPtrs[] = {0}; + +LPSTR Extensions[] = { + "Netbt debugger extensions", + 0 +}; + +/* + * DeviceContext debugging. + * + */ + +FIELD_DESCRIPTOR DeviceContext[] = + { + FIELD3(FieldTypeStruct,tDEVICECONTEXT,DeviceObject), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,Linkage), + FIELD3(FieldTypePointer,tDEVICECONTEXT,SpinLock), + FIELD3(FieldTypePointer,tDEVICECONTEXT,Verify), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,UpConnectionInUse), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,LowerConnection), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,LowerConnFreeHead), + FIELD3(FieldTypeUnicodeString,tDEVICECONTEXT,BindName), + FIELD3(FieldTypeUnicodeString,tDEVICECONTEXT,ExportName), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,IpAddress), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,SubnetMask), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,BroadcastAddress), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,NetMask), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hNameServer), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pNameServerDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pNameServerFileObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hDgram), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pDgramDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pDgramFileObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hSession), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pSessionDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pSessionFileObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hControl), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pControlDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pControlFileObject), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,lNameServerAddress), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,lBackupServer), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pPermClient), + FIELD3(FieldTypeULongULong,tDEVICECONTEXT,AdapterNumber), + FIELD3(FieldTypeMacAddr,tDEVICECONTEXT,MacAddress), + FIELD3(FieldTypeChar,tDEVICECONTEXT,LockNumber), + FIELD3(FieldTypeBoolean,tDEVICECONTEXT,RefreshToBackup), + FIELD3(FieldTypeBoolean,tDEVICECONTEXT,PointToPoint), + FIELD3(FieldTypeBoolean,tDEVICECONTEXT,WinsIsDown), + 0 + }; + +FIELD_DESCRIPTOR NameAddr[] = + { + FIELD3(FieldTypeListEntry,tNAMEADDR,Linkage), + FIELD3(FieldTypePointer,tNAMEADDR,pAddressEle), + FIELD3(FieldTypeIpAddr,tNAMEADDR,IpAddress), + FIELD3(FieldTypePointer,tNAMEADDR,pIpAddrsList), + FIELD3(FieldTypePointer,tNAMEADDR,pTracker), + FIELD3(FieldTypePointer,tNAMEADDR,pTimer), + FIELD3(FieldTypePointer,tNAMEADDR,Ttl), + FIELD3(FieldTypeULong,tNAMEADDR,RefCount), + FIELD3(FieldTypePointer,tNAMEADDR,NameTypeState), + FIELD3(FieldTypePointer,tNAMEADDR,Verify), + FIELD3(FieldTypeULongULong,tNAMEADDR,AdapterMask), + FIELD3(FieldTypeULongULong,tNAMEADDR,RefreshMask), + FIELD3(FieldTypeUShort,tNAMEADDR,TimeOutCount), + FIELD3(FieldTypeBoolean,tNAMEADDR,fProxyReq), +#ifdef PROXY_NODE + FIELD3(FieldTypeBoolean,tNAMEADDR,fPnode), +#endif + FIELD3(FieldTypeNBName,tNAMEADDR,Name), + 0 + }; + +FIELD_DESCRIPTOR AddressEle[] = + { + FIELD3(FieldTypeListEntry,tADDRESSELE,Linkage), + FIELD3(FieldTypePointer,tADDRESSELE,Verify), + FIELD3(FieldTypePointer,tADDRESSELE,SpinLock), + FIELD3(FieldTypeListEntry,tADDRESSELE,ClientHead), + FIELD3(FieldTypePointer,tADDRESSELE,pNameAddr), + FIELD3(FieldTypeULong,tADDRESSELE,RefCount), + FIELD3(FieldTypePointer,tADDRESSELE,pDeviceContext), + FIELD3(FieldTypePointer,tADDRESSELE,SecurityDescriptor), + FIELD3(FieldTypeUShort,tADDRESSELE,NameType), + FIELD3(FieldTypeChar,tADDRESSELE,LockNumber), + FIELD3(FieldTypeBoolean,tADDRESSELE,MultiClients), + 0 + }; + +FIELD_DESCRIPTOR ClientEle[] = + { + FIELD3(FieldTypeListEntry,tCLIENTELE,Linkage), + FIELD3(FieldTypePointer,tCLIENTELE,Verify), + FIELD3(FieldTypePointer,tCLIENTELE,pIrp), + FIELD3(FieldTypePointer,tCLIENTELE,SpinLock), + FIELD3(FieldTypePointer,tCLIENTELE,pAddress), + FIELD3(FieldTypeListEntry,tCLIENTELE,ConnectHead), + FIELD3(FieldTypeListEntry,tCLIENTELE,ConnectActive), + FIELD3(FieldTypeListEntry,tCLIENTELE,RcvDgramHead), + FIELD3(FieldTypeListEntry,tCLIENTELE,ListenHead), + FIELD3(FieldTypeListEntry,tCLIENTELE,SndDgrams), + FIELD3(FieldTypePointer,tCLIENTELE,evConnect), + FIELD3(FieldTypePointer,tCLIENTELE,ConEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evReceive), + FIELD3(FieldTypePointer,tCLIENTELE,RcvEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evDisconnect), + FIELD3(FieldTypePointer,tCLIENTELE,DiscEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evError), + FIELD3(FieldTypePointer,tCLIENTELE,ErrorEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evRcvDgram), + FIELD3(FieldTypePointer,tCLIENTELE,RcvDgramEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evRcvExpedited), + FIELD3(FieldTypePointer,tCLIENTELE,RcvExpedEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evSendPossible), + FIELD3(FieldTypePointer,tCLIENTELE,SendPossEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,pDeviceContext), + FIELD3(FieldTypeULong,tCLIENTELE,RefCount), + FIELD3(FieldTypeChar,tCLIENTELE,LockNumber), + FIELD3(FieldTypeBoolean,tCLIENTELE,WaitingForRegistration), + 0 + }; + + +FIELD_DESCRIPTOR ConnectEle[] = + { + FIELD3(FieldTypeListEntry,tCONNECTELE,Linkage), + FIELD3(FieldTypePointer,tCONNECTELE,Verify), + FIELD3(FieldTypePointer,tCONNECTELE,SpinLock), + FIELD3(FieldTypePointer,tCONNECTELE,pLowerConnId), + FIELD3(FieldTypePointer,tCONNECTELE,pClientEle), + FIELD3(FieldTypePointer,tCONNECTELE,ConnectContext), + FIELD3(FieldTypeNBName,tCONNECTELE,RemoteName), + FIELD3(FieldTypePointer,tCONNECTELE,pNewMdl), + FIELD3(FieldTypeULong,tCONNECTELE,CurrentRcvLen), + FIELD3(FieldTypeULong,tCONNECTELE,FreeBytesInMdl), + FIELD3(FieldTypeULong,tCONNECTELE,TotalPcktLen), + FIELD3(FieldTypeULong,tCONNECTELE,BytesInXport), + FIELD3(FieldTypeULong,tCONNECTELE,BytesRcvd), + FIELD3(FieldTypeULong,tCONNECTELE,ReceiveIndicated), + FIELD3(FieldTypePointer,tCONNECTELE,pNextMdl), + FIELD3(FieldTypeULong,tCONNECTELE,OffsetFromStart), + FIELD3(FieldTypePointer,tCONNECTELE,pIrp), + FIELD3(FieldTypePointer,tCONNECTELE,pIrpClose), + FIELD3(FieldTypePointer,tCONNECTELE,pIrpDisc), + FIELD3(FieldTypePointer,tCONNECTELE,pIrpRcv), + FIELD3(FieldTypeULong,tCONNECTELE,RefCount), + FIELD3(FieldTypeULong,tCONNECTELE,state), + FIELD3(FieldTypeBoolean,tCONNECTELE,Orig), + FIELD3(FieldTypeChar,tCONNECTELE,LockNumber), + FIELD3(FieldTypeChar,tCONNECTELE,SessionSetupCount), + FIELD3(FieldTypeChar,tCONNECTELE,DiscFlag), + FIELD3(FieldTypeBoolean,tCONNECTELE,JunkMsgFlag), + 0 + }; + +FIELD_DESCRIPTOR LowerConn[] = + { + FIELD3(FieldTypeListEntry,tLOWERCONNECTION,Linkage), + FIELD3(FieldTypePointer,tLOWERCONNECTION,Verify), + FIELD3(FieldTypePointer,tLOWERCONNECTION,SpinLock), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pUpperConnection), + FIELD3(FieldTypePointer,tLOWERCONNECTION,FileHandle), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pFileObject), + FIELD3(FieldTypePointer,tLOWERCONNECTION,AddrFileHandle), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pAddrFileObject), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pDeviceContext), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pIndicateMdl), + FIELD3(FieldTypeULongULong,tLOWERCONNECTION,BytesRcvd), + FIELD3(FieldTypeULongULong,tLOWERCONNECTION,BytesSent), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pMdl), + FIELD3(FieldTypeUShort,tLOWERCONNECTION,BytesInIndicate), + FIELD3(FieldTypeUShort,tLOWERCONNECTION,StateRcv), + FIELD3(FieldTypeIpAddr,tLOWERCONNECTION,SrcIpAddr), + FIELD3(FieldTypeULong,tLOWERCONNECTION,State), + FIELD3(FieldTypeULong,tLOWERCONNECTION,RefCount), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pIrp), + FIELD3(FieldTypePointer,tLOWERCONNECTION,CurrentStateProc), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,bReceivingToIndicateBuffer), + FIELD3(FieldTypeChar,tLOWERCONNECTION,LockNumber), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,bOriginator), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,InRcvHandler), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,DestroyConnection), + 0 + }; + + +FIELD_DESCRIPTOR Tracker[] = + { + FIELD3(FieldTypeListEntry,tDGRAM_SEND_TRACKING,Linkage), + FIELD3(FieldTypeListEntry,tDGRAM_SEND_TRACKING,TrackerList), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,Verify), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pClientIrp), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pConnEle), + FIELD3(FieldTypeStruct,tDGRAM_SEND_TRACKING,SendBuffer), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pSendInfo), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pDeviceContext), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pTimer), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,RefCount), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pNameAddr), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pTimeout), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,AllocatedLength), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,CompletionRoutine), + FIELD3(FieldTypeUShort,tDGRAM_SEND_TRACKING,Flags), + FIELD3(FieldTypeListEntry,tDGRAM_SEND_TRACKING,DebugLinkage), + 0 + }; + +FIELD_DESCRIPTOR Nbt_Config[] = + { + FIELD3(FieldTypePointer,tNBTCONFIG,SpinLock), + FIELD3(FieldTypeULong,tNBTCONFIG,NumConnections), + FIELD3(FieldTypeULong,tNBTCONFIG,NumAddresses), + FIELD3(FieldTypeListEntry,tNBTCONFIG,DeviceContexts), + FIELD3(FieldTypeListEntry,tNBTCONFIG,DgramTrackerFreeQ), + FIELD3(FieldTypeListEntry,tNBTCONFIG,NodeStatusHead), + FIELD3(FieldTypeListEntry,tNBTCONFIG,AddressHead), + FIELD3(FieldTypeListEntry,tNBTCONFIG,PendingNameQueries), + FIELD3(FieldTypePointer,tNBTCONFIG,pControlObj), + FIELD3(FieldTypePointer,tNBTCONFIG,DriverObject), + FIELD3(FieldTypeListEntry,tNBTCONFIG,IrpFreeList), + FIELD3(FieldTypePointer,tNBTCONFIG,SessionMdlFreeSingleList), + FIELD3(FieldTypePointer,tNBTCONFIG,DgramMdlFreeSingleList), + FIELD3(FieldTypePointer,tNBTCONFIG,pTcpBindName), + FIELD3(FieldTypePointer,tNBTCONFIG,pLocalHashTbl), + FIELD3(FieldTypePointer,tNBTCONFIG,pRemoteHashTbl), + FIELD3(FieldTypeStruct,tNBTCONFIG,OutOfRsrc), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumDevices), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumLocalNames), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumRemoteNames), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumBucketsRemote), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumBucketsLocal), + FIELD3(FieldTypeUShort,tNBTCONFIG,TimerQSize), + FIELD3(FieldTypeULong,tNBTCONFIG,uBcastTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,uRetryTimeout), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumRetries), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumBcasts), + FIELD3(FieldTypeUShort,tNBTCONFIG,ScopeLength), + FIELD3(FieldTypeUShort,tNBTCONFIG,SizeTransportAddress), + FIELD3(FieldTypePointer,tNBTCONFIG,pScope), + FIELD3(FieldTypePointer,tNBTCONFIG,pBcastNetbiosName), + FIELD3(FieldTypeULong,tNBTCONFIG,MinimumTtl), + FIELD3(FieldTypeULong,tNBTCONFIG,RefreshDivisor), + FIELD3(FieldTypeULong,tNBTCONFIG,RemoteHashTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,WinsDownTimeout), + FIELD3(FieldTypePointer,tNBTCONFIG,pRefreshTimer), + FIELD3(FieldTypePointer,tNBTCONFIG,pSessionKeepAliveTimer), + FIELD3(FieldTypePointer,tNBTCONFIG,pRemoteHashTimer), + FIELD3(FieldTypeULong,tNBTCONFIG,InitialRefreshTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,KeepAliveTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,RegistryBcastAddr), + FIELD3(FieldTypeUShort,tNBTCONFIG,DhcpNumConnections), + FIELD3(FieldTypeUShort,tNBTCONFIG,CurrentHashBucket), + FIELD3(FieldTypeUShort,tNBTCONFIG,PduNodeType), + FIELD3(FieldTypeUShort,tNBTCONFIG,TransactionId), + FIELD3(FieldTypeUShort,tNBTCONFIG,NameServerPort), + FIELD3(FieldTypeUShort,tNBTCONFIG,sTimeoutCount), + FIELD3(FieldTypeStruct,tNBTCONFIG,JointLock), + FIELD3(FieldTypeChar,tNBTCONFIG,LockNumber), + FIELD3(FieldTypeUShort,tNBTCONFIG,RemoteTimeoutCount), + FIELD3(FieldTypeBoolean,tNBTCONFIG,UseRegistryBcastAddr), + FIELD3(FieldTypeULong,tNBTCONFIG,MaxDgramBuffering), + FIELD3(FieldTypeULong,tNBTCONFIG,LmHostsTimeout), + FIELD3(FieldTypePointer,tNBTCONFIG,pLmHosts), + FIELD3(FieldTypeULong,tNBTCONFIG,PathLength), + FIELD3(FieldTypeChar,tNBTCONFIG,AdapterCount), + FIELD3(FieldTypeBoolean,tNBTCONFIG,MultiHomed), + FIELD3(FieldTypeBoolean,tNBTCONFIG,SingleResponse), + FIELD3(FieldTypeBoolean,tNBTCONFIG,SelectAdapter), + FIELD3(FieldTypeBoolean,tNBTCONFIG,ResolveWithDns), + FIELD3(FieldTypeBoolean,tNBTCONFIG,EnableLmHosts), + FIELD3(FieldTypeBoolean,tNBTCONFIG,EnableProxyRegCheck), + FIELD3(FieldTypeBoolean,tNBTCONFIG,DoingRefreshNow), + FIELD3(FieldTypeChar,tNBTCONFIG,CurrProc), + FIELD3(FieldTypeUShort,tNBTCONFIG,OpRefresh), + 0 + }; + + +FIELD_DESCRIPTOR NbtWorkContext[] = + { + FIELD3(FieldTypeStruct,NBT_WORK_ITEM_CONTEXT,Item), + FIELD3(FieldTypePointer,NBT_WORK_ITEM_CONTEXT,pTracker), + FIELD3(FieldTypePointer,NBT_WORK_ITEM_CONTEXT,pClientContext), + FIELD3(FieldTypePointer,NBT_WORK_ITEM_CONTEXT,ClientCompletion), + FIELD3(FieldTypeBoolean,NBT_WORK_ITEM_CONTEXT,TimedOut), + 0 + }; + + +FIELD_DESCRIPTOR Timer_Entry[] = + { + FIELD3(FieldTypeStruct,tTIMERQENTRY,VxdTimer), + FIELD3(FieldTypeListEntry,tTIMERQENTRY,Linkage), + FIELD3(FieldTypePointer,tTIMERQENTRY,Context), + FIELD3(FieldTypePointer,tTIMERQENTRY,Context2), + FIELD3(FieldTypePointer,tTIMERQENTRY,CompletionRoutine), + FIELD3(FieldTypePointer,tTIMERQENTRY,ClientContext), + FIELD3(FieldTypePointer,tTIMERQENTRY,ClientCompletion), + FIELD3(FieldTypePointer,tTIMERQENTRY,pCacheEntry), + FIELD3(FieldTypeULong,tTIMERQENTRY,DeltaTime), + FIELD3(FieldTypeUShort,tTIMERQENTRY,Flags), + FIELD3(FieldTypeUShort,tTIMERQENTRY,Retries), + FIELD3(FieldTypeChar,tTIMERQENTRY,RefCount), + 0 + }; + +FIELD_DESCRIPTOR Dns_Queries[] = + { + FIELD3(FieldTypePointer,tDNS_QUERIES,QueryIrp), + FIELD3(FieldTypeListEntry,tDNS_QUERIES,ToResolve), + FIELD3(FieldTypePointer,tDNS_QUERIES,Context), + FIELD3(FieldTypeBoolean,tDNS_QUERIES,ResolvingNow), + 0 + }; + +// +// List of structs currently handled by the debugger extensions +// + +STRUCT_DESCRIPTOR Structs[] = + { + STRUCT(tDEVICECONTEXT,DeviceContext), + STRUCT(tNAMEADDR,NameAddr), + STRUCT(tADDRESSELE,AddressEle), + STRUCT(tCLIENTELE,ClientEle), + STRUCT(tCONNECTELE,ConnectEle), + STRUCT(tLOWERCONNECTION,LowerConn), + STRUCT(tDGRAM_SEND_TRACKING,Tracker), + STRUCT(tNBTCONFIG,Nbt_Config), + STRUCT(NBT_WORK_ITEM_CONTEXT,NbtWorkContext), + STRUCT(tTIMERQENTRY,Timer_Entry), + STRUCT(tDNS_QUERIES,Dns_Queries), + 0 + }; diff --git a/private/ntos/nbt/nt/netbtkd/netbtkd.def b/private/ntos/nbt/nt/netbtkd/netbtkd.def new file mode 100644 index 000000000..3e91e095e --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/netbtkd.def @@ -0,0 +1,8 @@ +LIBRARY NETBTKD +DESCRIPTION 'Netbt KD extensions' + +EXPORTS + help + dump + columns + diff --git a/private/ntos/nbt/nt/netbtkd/sources b/private/ntos/nbt/nt/netbtkd/sources new file mode 100644 index 000000000..f8bca6009 --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/sources @@ -0,0 +1,53 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=netbt + +TARGETNAME=netbtkd +TARGETPATH=obj +TARGETTYPE=DYNLINK +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\libc.lib \ + $(BASEDIR)\public\sdk\lib\*\user32.lib\ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib + +C_DEFINES=-DPROXY_NODE +INCLUDES=..;..\inc;..\..\inc;..\..\..\inc;..\..\..\..\inc + +!IFNDEF DISABLE_NET_UNICODE +UNICODE=1 +NET_C_DEFINES=-DUNICODE +!ENDIF + +DLLBASE=0x1010000 + +C_DEFINES=$(C_DEFINES) -DRDBSSDBG + +SOURCES=kdextlib.c \ + netbtkd.c + +UMTYPE=console +OPTIONAL_NTTEST= + + diff --git a/private/ntos/nbt/nt/ntisol.c b/private/ntos/nbt/nt/ntisol.c new file mode 100644 index 000000000..2f3e532cc --- /dev/null +++ b/private/ntos/nbt/nt/ntisol.c @@ -0,0 +1,4873 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Ntisol.h + +Abstract: + + + This file contains the interface between the TDI interface on the top + of NBT and the OS independent code. It takes the parameters out of the + irps and puts in into procedure calls for the OS independent code (which + is mostly in name.c). + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +Notes: + + The Nbt routines have been modified to include an additional parameter, i.e, + the transport type. This transport type is used primarily to distinguish the + NETBIOS over TCP/IP implementation from the Messaging Over TCP/IP implementation. + + The primary difference between the two being that the later uses the NETBT framing + without the associated NETBIOS name registartion/resolution. It primarily uses + DNS for name resolution. All the names that are registered for the new transport + are local names and are not defended on the network. + + The primary usage is in conjuntion with an extended NETBIOS address type defined + in tdi.h. The NETBIOS name resolution/registration traffic occurs in two phases. + The first phase contains all the broadcast traffic that ensues during NETBIOS + name registration. Subsequently the NETBT implementation queries the remote + adapter status to choose the appropriate called name. This approach results in + additional traffic for querying the remote adapter status. The new address type + defined in tdi.h enables the client of netbt to supply the name to be used in + NETBT session setup. This avoids the network traffic for querying the adapter + status. + + The original design which has not been fully implemented involved exposing two + device objects from the NetBt driver -- the NetBt device object which would be + the full implementation of NETBIOS over TCP/IP and the MoTcp device object which + would be the implementation of Messaging over TCP/IP. The MoTcp device object + would use the same port address as NetBt and use the same session setup protocol + to talk to remote machines running old NetBt drivers and machines running new + NetBt drivers. + + The transport type variations combined with the address type changes present us + with four different cases which need to be handled -- the NetBt transport being + presented with a TDI_ADDRESS_NETBIOS_EX structure, the NetBt transport being + prsented with a TDI_ADDRESS_NETBIOS structure and the same two cases for the + MoTcp transport. + +--*/ + +#include "types.h" +#include "nbtprocs.h" +#include "ntprocs.h" +#include <nbtioctl.h> +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL + +NTSTATUS +SendCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + + +NTSTATUS +NTSendCleanupConnection( + IN tCONNECTELE *pConnEle, + IN PVOID pCompletionRoutine, + IN PVOID Context, + IN PIRP pIrp); + +VOID +DpcSendSession( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NBT_WORK_ITEM_CONTEXT * +DnsIrpCancelPaged( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NBT_WORK_ITEM_CONTEXT * +FindCheckAddrIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NTSTATUS +NTCancelCancelRoutine( + IN PIRP pIrp + ); + +#ifdef RASAUTODIAL +extern ACD_DRIVER AcdDriverG; + +BOOLEAN +NbtCancelPostConnect( + IN PIRP pIrp + ); +#endif // RASAUTODIAL + +NTSTATUS +NbtQueryGetAddressInfo( + IN PIO_STACK_LOCATION pIrpSp, + OUT PVOID *ppBuffer, + OUT ULONG *pSize +); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NTOpenControl) +#pragma CTEMakePageable(PAGE, NTOpenAddr) +#pragma CTEMakePageable(PAGE, NTCloseAddress) +#pragma CTEMakePageable(PAGE, NTOpenConnection) +#pragma CTEMakePageable(PAGE, NTAssocAddress) +#pragma CTEMakePageable(PAGE, NTCloseConnection) +#pragma CTEMakePageable(PAGE, NTSetSharedAccess) +#pragma CTEMakePageable(PAGE, NTCheckSharedAccess) +#pragma CTEMakePageable(PAGE, NTCleanUpConnection) +#pragma CTEMakePageable(PAGE, NTCleanUpAddress) +#pragma CTEMakePageable(PAGE, NTDisAssociateAddress) +#pragma CTEMakePageable(PAGE, NTListen) +// +// Should not be pageable since AFD can call us at raised Irql in case of AcceptEx. +// +// #pragma CTEMakePageable(PAGE, NTQueryInformation) +#pragma CTEMakePageable(PAGE, DispatchIoctls) +#endif +//******************* Pageable Routine Declarations **************** + + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenControl( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) +/*++ +Routine Description: + + This Routine handles opening the control object, which represents the + driver itself. For example QueryInformation uses the control object + as the destination of the Query message. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pIrpSp->FileObject->FsContext2 = (PVOID)(NBT_CONTROL_TYPE); + + // return a ptr the control endpoint + pIrpSp->FileObject->FsContext = (PVOID)pNbtGlobConfig->pControlObj; + + // + // the following call opens a control object with the transport below since + // several of the query information calls are passed directly on to the + // transport below. + // + if (!pDeviceContext->pControlFileObject) + { + status = NbtTdiOpenControl(pDeviceContext); + } + else + status = STATUS_SUCCESS; + + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) +/*++ +Routine Description: + + This Routine handles converting an Open Address Request from an IRP to + a procedure call so that NbtOpenAddress can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + TDI_REQUEST Request; + PVOID pSecurityDesc; + TRANSPORT_ADDRESS UNALIGNED *pTransportAddr; // structure containing counted array of TA_ADDRESS + TA_ADDRESS UNALIGNED *pAddress; + PTDI_ADDRESS_NETBIOS pNetbiosAddress; + PFILE_FULL_EA_INFORMATION ea; + int j; + NTSTATUS status=STATUS_INVALID_ADDRESS_COMPONENT; + + CTEPagedCode(); + + // make up the Request data structure from the IRP info + Request.Handle.AddressHandle = NULL; + + ea = (PFILE_FULL_EA_INFORMATION)pIrp->AssociatedIrp.SystemBuffer; + pTransportAddr = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + + pAddress = NULL; + + // loop through the addresses passed in until ONE is successfully used + // *TODO* is it really necessary to have this loop or can we just assume + // the name is at the start of the address buffer... + // *TODO does this need to handle multiple names?? + for (j=0;j < pTransportAddr->TAAddressCount ;j++ ) + { + // this includes the address type as well as the actual address + pAddress = &pTransportAddr->Address[j]; + switch (pAddress->AddressType) { + case TDI_ADDRESS_TYPE_NETBIOS: + { + if (pAddress->AddressLength == 0) + { + // zero length addresses mean the broadcast address + pAddress = NULL; + } + + // call the non-NT specific function to open an address + status = NbtOpenAddress(&Request, + pAddress, + pDeviceContext->IpAddress, + &pSecurityDesc, + pDeviceContext, + (PVOID)pIrp); + } + break; + case TDI_ADDRESS_TYPE_NETBIOS_EX: + { + + TDI_ADDRESS_NETBIOS NetbiosAddress; + PTDI_ADDRESS_NETBIOS_EX pNetbiosExAddress; + + pNetbiosExAddress = (PTDI_ADDRESS_NETBIOS_EX)pAddress->Address; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT..Opening NETBIOS_EX Address with Endpoint Name %16s\n",pNetbiosExAddress->EndpointName)); + + if (pAddress->AddressLength == 0) { + status = STATUS_INVALID_ADDRESS_COMPONENT; + } else { + // call the non-NT specific function to open an address + status = NbtOpenAddress(&Request, + pAddress, + pDeviceContext->IpAddress, + &pSecurityDesc, + pDeviceContext, + (PVOID)pIrp); + } + } + break; + default: + break; + } + } + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTCloseAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting a Close Address Request from an IRP to + a procedure call so that NbtCloseAddress can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + TDI_REQUEST_STATUS RequestStatus; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + + status = NbtCloseAddress( + &Request, + &RequestStatus, + pDeviceContext, + (PVOID)pIrp); + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting an Open Connection Request from an IRP to + a procedure call so that NbtOpenConnection can be called in an OS independent + manner. The connection must be associated with an address before it + can be used, except for in inbound call where the client returns the + connection ID in the accept. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + PFILE_FULL_EA_INFORMATION ea; + PIO_STACK_LOCATION pIrpSp; + CONNECTION_CONTEXT ConnectionContext; + NTSTATUS status; + PFILE_OBJECT pFileObject; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + + // make up the Request data structure from the IRP info + Request.Handle.ConnectionContext = NULL; + + // get the connection context out of the System buffer + ea = (PFILE_FULL_EA_INFORMATION)pIrp->AssociatedIrp.SystemBuffer; + + // the connection context value is stored in the string just after the + // name "connectionContext", and it is most likely unaligned, so just + // copy it out.( 4 bytes of copying ). + CTEMemCopy(&ConnectionContext, + (CONNECTION_CONTEXT)&ea->EaName[ea->EaNameLength+1], + sizeof(CONNECTION_CONTEXT)); + + // call the non-NT specific function to open an address + status = NbtOpenConnection( + &Request, + ConnectionContext, + pDeviceContext + ); + + pFileObject = pIrpSp->FileObject; + + if (!NT_SUCCESS(status)) + { + pFileObject->FsContext = NULL; + } + else + if (Request.Handle.ConnectionContext) + { + + // fill the IRP with successful completion information so we can + // find the connection object given the fileObject later. + pFileObject->FsContext = Request.Handle.ConnectionContext; + pFileObject->FsContext2 = (PVOID)(NBT_CONNECTION_TYPE); + status = STATUS_SUCCESS; + } + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTAssocAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting an Associate Address Request from an IRP to + a procedure call so that NbtAssociateAddress can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + PIO_STACK_LOCATION pIrpSp; + PVOID hAddress; + PFILE_OBJECT fileObject; + PTDI_REQUEST_KERNEL_ASSOCIATE parameters; // holds address handle + NTSTATUS status; + + CTEPagedCode(); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + // the address handle is buried in the Irp... + parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&pIrpSp->Parameters; + + // now get a pointer to the file object, which points to the address + // element by calling a kernel routine to convert this filehandle into + // a file pointer. + + status = ObReferenceObjectByHandle( + parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *)&fileObject, + NULL); + + if (NT_SUCCESS(status)) + { + hAddress = (PVOID)fileObject->FsContext; + // call the non-NT specific function to associate the address with + // the connection + status = NbtAssociateAddress( + &Request, + (tCLIENTELE *)hAddress, + (PVOID)pIrp); + + // we are done with the file object, so release the reference + ObDereferenceObject((PVOID)fileObject); + + return(status); + } + else + return(STATUS_INVALID_HANDLE); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCloseConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting a Close Connection Request from an IRP to + a procedure call so that NbtCloseConnection can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + TDI_REQUEST_STATUS RequestStatus; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + + CTEPagedCode(); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + status = NbtCloseConnection( + &Request, + &RequestStatus, + pDeviceContext, + (PVOID)pIrp); + + return(status); +} + +//---------------------------------------------------------------------------- +VOID +NTSetFileObjectContexts( + IN PIRP pIrp, + IN PVOID FsContext, + IN PVOID FsContext2) + +/*++ +Routine Description: + + This Routine handles fills in two context values in the Irp stack location, + that has to be done in an OS-dependent manner. This routine is called + from NbtOpenAddress() when a name is being registered on the network( i.e. + as a result of OpenAddress). + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + PIO_STACK_LOCATION pIrpSp; + PFILE_OBJECT pFileObject; + + // + // fill the IRP with context information so we can + // find the address object given the fileObject later. + // + // This must be done here, rather than after the call to NbtOpenAddress + // because that call can complete the Irp before it returns. Soooo, + // in the complete routine for the Irp, if the completion code is not + // good, it Nulls these two context values. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pFileObject = pIrpSp->FileObject; + pFileObject->FsContext = FsContext; + pFileObject->FsContext2 =FsContext2; + + +} + + +//---------------------------------------------------------------------------- +VOID +NTClearFileObjectContext( + IN PIRP pIrp + ) +/*++ +Routine Description: + + This Routine clears the context value in the file object when an address + object is closed. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + none + +--*/ + +{ + + PIO_STACK_LOCATION pIrpSp; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + CHECK_PTR(pIrpSp->FileObject); + pIrpSp->FileObject->FsContext = NULL; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTSetSharedAccess( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN tADDRESSELE *pAddress) + +/*++ +Routine Description: + + This Routine handles setting the shared access on the file object. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + PACCESS_STATE AccessState; + ULONG DesiredAccess; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + if ((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) + DesiredAccess = (ULONG)FILE_SHARE_READ; + + else + DesiredAccess = (ULONG)0; + + IoSetShareAccess( + FILE_READ_DATA, + DesiredAccess, + pIrpSp->FileObject, + &pAddress->ShareAccess); + + // assign the security descriptor ( need to to do this with the spinlock + // released because the descriptor is not mapped. Assign and CheckAccess + // are synchronized using a Resource. + + AccessState = pIrpSp->Parameters.Create.SecurityContext->AccessState; + + + status = SeAssignSecurity( + NULL, // Parent Descriptor + AccessState->SecurityDescriptor, + &pAddress->SecurityDescriptor, + FALSE, // is a directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) + { + + // + // Error, return status. + // + + IoRemoveShareAccess (pIrpSp->FileObject, &pAddress->ShareAccess); + + } + return status; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCheckSharedAccess( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN tADDRESSELE *pAddress) + +/*++ +Routine Description: + + This Routine handles setting the shared access on the file object. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + ULONG DesiredAccess; + PIO_STACK_LOCATION pIrpSp; + BOOLEAN duplicate=FALSE; + NTSTATUS status; + ULONG DesiredShareAccess; + static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + + if ((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) + DesiredAccess = (ULONG)FILE_SHARE_READ; + else + DesiredAccess = (ULONG)0; + + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + + AccessState = pIrpSp->Parameters.Create.SecurityContext->AccessState; + + status = STATUS_SUCCESS; + + // *TODO* check that this routine is doing the right thing... + // + AccessAllowed = SeAccessCheck( + pAddress->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + pIrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + pIrp->RequestorMode, + &GrantedAccess, + &status); + + + // use the status from the IoCheckShareAccess as the return access + // event if SeAccessCheck fails.... + + // + // BUGBUG: Compare DesiredAccess to GrantedAccess? + // + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + //ACQUIRE_SPIN_LOCK (&pDeviceContext->SpinLock, &oldirql); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredAccess, + pIrpSp->FileObject, + &pAddress->ShareAccess, + TRUE); + + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCleanUpAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +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; + tCLIENTELE *pClientEle; + PIO_STACK_LOCATION pIrpSp; + + + CTEPagedCode(); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Cleanup Address Hit ***\n")); + + // + // Disconnect any active connections, and for each connection that is not + // in use, remove one from the free list to the transport below. + // + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status); + + status = NbtCleanUpAddress(pClientEle,pDeviceContext); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCleanUpConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +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. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + +#if DBG + if ((pConnEle->Verify != NBT_VERIFY_CONNECTION) && + (pConnEle->Verify != NBT_VERIFY_CONNECTION_DOWN)) + { + ASSERTMSG("Invalid Connection Handle passed to NtCleanupConnection\n",0); + return(STATUS_INVALID_HANDLE); + } +#endif + + //CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Cleanup Connection Hit state= %X\n",pConnEle->state)); + + status = NbtCleanUpConnection(pConnEle,pDeviceContext); + + return(status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTAccept( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles passing an accept for an inbound connect indication to + the OS independent code. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + TDI_REQUEST TdiRequest; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_ACCEPT pRequest; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: ** Got an Accept from the Client **\n")); + + // pull the junk out of the Irp and call the non-OS specific routine. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // the Parameters value points to a Request structure... + pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters; + + // the pConnEle ptr was stored in the FsContext value when the connection + // was initially created. + TdiRequest.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + + status = NbtAccept( + &TdiRequest, + pRequest->RequestConnectionInformation, + pRequest->ReturnConnectionInformation, + pIrp); + + return(status); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTDisAssociateAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + + +{ + NTSTATUS status; + TDI_REQUEST TdiRequest; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_ACCEPT pRequest; + + + CTEPagedCode(); + + // pull the junk out of the Irp and call the non-OS specific routine. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // the Parameters value points to a Request structure... + pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters; + + // the pConnEle ptr was stored in the FsContext value when the connection + // was initially created. + TdiRequest.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + status = NbtDisassociateAddress(&TdiRequest); + + return(status); + + +} + +NTSTATUS +NbtpConnectCompletionRoutine( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp, + PVOID pCompletionContext) + +/*++ +Routine Description: + + This Routine is the completion routine for local IRPS that are generated + to handle compound transport addresses + +Arguments: + + pDeviceObject - the device object + + pIrp - a ptr to an IRP + + pCompletionContext - the completion context + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + KEVENT *pEvent = pCompletionContext; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Completing local irp %lx\n",pIrp)); + KeSetEvent((PKEVENT )pEvent, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTConnect( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles calling the non OS specific code to open a session + connection to a destination. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + TDI_REQUEST Request; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS Status; + PTDI_REQUEST_KERNEL pRequestKernel; + PTDI_CONNECTION_INFORMATION pRequestConnectionInformation; + PTRANSPORT_ADDRESS pRemoteAddress; + tCONNECTELE *pConnEle; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + pConnEle = Request.Handle.ConnectionContext; + + pRequestConnectionInformation = pRequestKernel->RequestConnectionInformation; + pRemoteAddress = pRequestConnectionInformation->RemoteAddress; + + if (pRequestConnectionInformation->RemoteAddressLength < sizeof(TRANSPORT_ADDRESS)) { + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // The round about path of creating a Local IRP and processing the request is taken if + // we are either presented with a compound address, i.e., a transport address having + // multiple TA_ADDRESSes or if it is not a locally generated IRP(completion routine check) + // and the address type is not TDI_ADDRESS_TYPE_NETBIOS. + // + if ((pRemoteAddress->TAAddressCount > 1) || + ((pIrpSp->CompletionRoutine != NbtpConnectCompletionRoutine) && + (pRemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS))) { + PIRP pLocalIrp; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Taking the roundabout path\n")); + + pLocalIrp = IoAllocateIrp(pDeviceContext->DeviceObject.StackSize,FALSE); + if (pLocalIrp != NULL) { + TDI_CONNECTION_INFORMATION LocalConnectionInformation; + PTRANSPORT_ADDRESS pTransportAddress; + PCHAR pTaAddress; + USHORT TaAddressLength,TransportAddressLength,AddressIndex; + USHORT TaAddressType; + KEVENT IrpCompletionEvent; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Allocated local irp %lx\n",pLocalIrp)); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Compound Transport address %lx Count %lx\n",pRemoteAddress,pRemoteAddress->TAAddressCount)); + + TaAddressLength = 0; + pTaAddress = (PCHAR)&pRemoteAddress->Address[0] - FIELD_OFFSET(TA_ADDRESS,Address); + + for (AddressIndex = 0; + AddressIndex < pRemoteAddress->TAAddressCount; + AddressIndex++) { + pTaAddress = (pTaAddress + TaAddressLength + FIELD_OFFSET(TA_ADDRESS,Address)); + + RtlCopyMemory( + &TaAddressLength, + (pTaAddress + FIELD_OFFSET(TA_ADDRESS,AddressLength)), + sizeof(USHORT)); + + RtlCopyMemory( + &TaAddressType, + (pTaAddress + FIELD_OFFSET(TA_ADDRESS,AddressType)), + sizeof(USHORT)); + + if (pConnEle->RemoteNameDoesNotExistInDNS) { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("Skipping address type %lx length %lx for nonexistent name, pIrp %lx\n",TaAddressType,TaAddressLength,pIrp)); + + // If the address type is such that we rely on DNS name resolution and + // if a prior attempt failed, there is no point in reissuing the request. + // We can fail them without having to go on the NET. + switch (TaAddressType) { + case TDI_ADDRESS_TYPE_NETBIOS: + if (TaAddressLength == TDI_ADDRESS_LENGTH_NETBIOS) { + Status = STATUS_SUCCESS; + break; + } + // lack of break intentional. + case TDI_ADDRESS_TYPE_NETBIOS_EX: + Status = STATUS_BAD_NETWORK_PATH; + break; + default: + Status = STATUS_INVALID_ADDRESS_COMPONENT; + } + + if (Status != STATUS_SUCCESS) { + continue; + } + } + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: pTaAddress %lx TaAddressLength %lx\n",pTaAddress,TaAddressLength)); + + // Allocate a buffer for copying the address and building a TRANSPORT_ADDRESS + // data structure. + TransportAddressLength = FIELD_OFFSET(TRANSPORT_ADDRESS,Address) + + FIELD_OFFSET(TA_ADDRESS,Address) + + TaAddressLength; + + pTransportAddress = NbtAllocMem(TransportAddressLength,NBT_TAG('b')); + if (pTransportAddress == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + pTransportAddress->TAAddressCount = 1; + + KeInitializeEvent(&IrpCompletionEvent, NotificationEvent, FALSE); + + RtlCopyMemory( + &pTransportAddress->Address[0], + pTaAddress, + (TaAddressLength + FIELD_OFFSET(TA_ADDRESS,Address))); + + pConnEle->AddressType = pTransportAddress->Address[0].AddressType; + + LocalConnectionInformation = *(pRequestKernel->RequestConnectionInformation); + LocalConnectionInformation.RemoteAddress = pTransportAddress; + LocalConnectionInformation.RemoteAddressLength = TransportAddressLength; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Building Connect Irp %lx\n",pLocalIrp)); + + TdiBuildConnect( + pLocalIrp, + &pDeviceContext->DeviceObject, + pIrpSp->FileObject, + NbtpConnectCompletionRoutine, + &IrpCompletionEvent, + pRequestKernel->RequestSpecific, + &LocalConnectionInformation, + pRequestKernel->ReturnConnectionInformation); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("Local IoCallDriver Invoked %lx %lx\n",pLocalIrp,pIrp)); + + Status = IoCallDriver(&pDeviceContext->DeviceObject,pLocalIrp); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: IoCallDriver returned %lx\n",Status)); + + if (Status == STATUS_PENDING) { + // Await the completion of the Irp. + Status = KeWaitForSingleObject(&IrpCompletionEvent, // Object to wait on. + Executive, // Reason for waiting + KernelMode, // Processor mode + FALSE, // Alertable + NULL); // Timeout + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: KeWiatForSingleObject returned %lx\n",Status)); + + // retrieve the completion status from the IRP. if it was successful exit, + // otherwise proceed to the next TA_ADDRESS in the transport address data + // structure. + Status = pLocalIrp->IoStatus.Status; + } + + if (Status != STATUS_SUCCESS) { + // Ensure that the original IRP was not cancelled before continuing. + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + + if (pIrp->Cancel) + { + Status = STATUS_CANCELLED; + } + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + } + + if (pTransportAddress != NULL) { + CTEFreeMem(pTransportAddress); + } + + if ((Status == STATUS_SUCCESS) || + (Status == STATUS_CANCELLED)) { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: exiting because of cancellation or success %lx\n",Status)); + break; + } else { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: trying next component because of failure %lx\n",Status)); + } + } + + IoFreeIrp(pLocalIrp); + } else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } else { + // call the non-NT specific function to setup the connection + Status = NbtConnect( + &Request, + pRequestKernel->RequestSpecific, // Ulong + pRequestKernel->RequestConnectionInformation, + pRequestKernel->ReturnConnectionInformation, + pIrp + ); + } + + return(Status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTDisconnect( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles calling the Non OS specific code to disconnect a + session. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + TDI_REQUEST Request; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + PTDI_REQUEST_KERNEL pRequestKernel; + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + // call the non-NT specific function to setup the connection + status = NbtDisconnect( + &Request, + pRequestKernel->RequestSpecific, // Large Integer + pRequestKernel->RequestFlags, + pRequestKernel->RequestConnectionInformation, + pRequestKernel->ReturnConnectionInformation, + pIrp + ); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTListen( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + NTSTATUS status; + TDI_REQUEST Request; + PTDI_REQUEST_KERNEL pRequestKernel; + PIO_STACK_LOCATION pIrpSp; + + CTEPagedCode(); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a LISTEN !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + // call the non-NT specific function to setup the connection + status = NbtListen( + &Request, + pRequestKernel->RequestFlags, // Ulong + pRequestKernel->RequestConnectionInformation, + pRequestKernel->ReturnConnectionInformation, + pIrp + ); + + + if (status != STATUS_PENDING) + { + NTIoComplete(pIrp,status,0); + } + return(status); + +} +//---------------------------------------------------------------------------- +VOID +NbtCancelListen( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a listen Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + tCLIENTELE *pClientEle; + KIRQL OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + tLISTENREQUESTS *pListenReq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a LISTEN Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + pClientEle = pConnEle->pClientEle; + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + + // now search the client's listen queue looking for this connection + // + CTESpinLock(pClientEle,OldIrq); + + pHead = &pClientEle->ListenHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pListenReq = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage); + if ((pListenReq->pConnectEle == pConnEle) && + (pListenReq->pIrp == pIrp)) + { + RemoveEntryList(pEntry); + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + + + CTESpinFree(pClientEle,OldIrq); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTEMemFree((PVOID)pListenReq); + + return; + + } + pEntry = pEntry->Flink; + + } + + + CTESpinFree(pClientEle,OldIrq); + + + return; + +} + +//---------------------------------------------------------------------------- +VOID +NTCancelSession( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a connect Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). It is called when + the session setup pdu has been sent, and the state is still outbound. + + The cancel routine is only setup when the timer is started to time + sending the session response pdu. + + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + KIRQL OldIrq; + PIO_STACK_LOCATION pIrpSp; + BOOLEAN DerefConnEle=FALSE; + tTIMERQENTRY *pTimer; + tDGRAM_SEND_TRACKING *pTracker; + COMPLETIONCLIENT pCompletion; + COMPLETIONROUTINE pCompletionRoutine; + PVOID pContext; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Connect Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + +#ifdef RASAUTODIAL + // + // Cancel the automatic connection if one's + // in progress. If we don't find the + // connection block in the automatic + // connection driver, then it's already + // been completed. + // + if (pConnEle->fAutoConnecting) { + if (!NbtCancelPostConnect(pIrp)) + return; + } +#endif // RASAUTODIAL + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // + // the irp could get completed between calling this cancel routine + // and this point in the code + // + if (pConnEle->pIrp) + { + pTracker = (tDGRAM_SEND_TRACKING *)pConnEle->pIrpRcv; + if (pTracker) + { + pTimer = pTracker->Connect.pTimer; + pTracker->Connect.pTimer = NULL; + pTracker->Flags |= TRACKER_CANCELLED; + + if (pTimer) + { + // + // stop the timer and only continue if the timer was stopped before + // it expired + // + pCompletionRoutine = pTimer->CompletionRoutine; + StopTimer(pTimer,&pCompletion,&pContext); + + if (pCompletion) + { + + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + (*pCompletionRoutine)(pTracker,(PVOID)STATUS_CANCELLED,pTimer); + + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + else + if (pConnEle->state == NBT_SESSION_OUTBOUND) + { + // + // for some reason there is no timer, but the connection is still + // outbound, so call the timer completion routine to kill off + // the connection. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + SessionTimedOut(pTracker,(PVOID)STATUS_CANCELLED,(PVOID)1); + } else { + // + // Free the lock + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return; + +} + +//---------------------------------------------------------------------------- +VOID +CheckAddrIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a DNS name query Irp that is passed + down to NBT from Lmhsvc, for the purpose of resolving a name with DNS. + Nbt will complete this irp each time it has a name to resolve with DNS. + + This routine will get the Resource Lock, and Null the Irp ptr in the + DnsQueries structure and then return the irp. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + BOOLEAN DerefConnEle=FALSE; + KIRQL OldIrq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Dns Irp Cancel !!! *****************\n")); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (CheckAddr.QueryIrp) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + CheckAddr.QueryIrp = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + + return; + +} + +//---------------------------------------------------------------------------- +VOID +DnsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a DNS name query Irp that is passed + down to NBT from Lmhsvc, for the purpose of resolving a name with DNS. + Nbt will complete this irp each time it has a name to resolve with DNS. + + This routine will get the Resource Lock, and Null the Irp ptr in the + DnsQueries structure and then return the irp. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + BOOLEAN DerefConnEle=FALSE; + KIRQL OldIrq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Dns Irp Cancel !!! *****************\n")); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (DnsQueries.QueryIrp) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + DnsQueries.QueryIrp = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + + return; + +} + +//---------------------------------------------------------------------------- +VOID +DiscWaitCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Disconnect Wait Irp - which has + been passed down by a client so that when a disconnect occurs this + irp will complete and inform the client. The action here is to simply + complete the irp with status cancelled. + down to NBT from Lmhsvc, for the purpose of resolving a name with DNS. + Nbt will complete this irp each time it has a name to resolve with DNS. + + This routine will get the Resource Lock, and Null the Irp ptr in the + DnsQueries structure and then return the irp. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + PIO_STACK_LOCATION pIrpSp; + CTELockHandle OldIrq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Disc Wait Irp Cancel !!! *****************\n")); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(pConnEle,OldIrq); + + if (pConnEle->pIrpClose == pIrp) + { + pConnEle->pIrpClose = NULL; + } + + CTESpinFree(pConnEle,OldIrq); + + pIrp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + return; + +} + +//---------------------------------------------------------------------------- +VOID +WaitForDnsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to DNS, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + BOOLEAN FoundIt = FALSE; + NBT_WORK_ITEM_CONTEXT *Context; + CTELockHandle OldIrq; + tDGRAM_SEND_TRACKING *pTracker; + PVOID pClientCompletion; + PVOID pClientContext; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Wait For Dns Irp Cancel !!! *****************\n")); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + Context = DnsIrpCancelPaged(DeviceContext,pIrp); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Now complete the clients request to return the irp to the client + // + if (Context) + { + // + // this is the name Query tracker + // + pTracker = Context->pTracker; + pClientCompletion = Context->ClientCompletion; + pClientContext = Context->pClientContext; + + // for dns names (NameLen>16), pTracker would be NULL + if (pTracker) + { + // name did not resolve, so delete from table + RemoveName(pTracker->pNameAddr); + + DereferenceTracker(pTracker); + } + + // + // this should complete any name queries that are waiting on + // this first name query - i.e. queries to the resolving name + // + CompleteClientReq(pClientCompletion, + pClientContext, + STATUS_CANCELLED); + + } + +} + +//---------------------------------------------------------------------------- +NBT_WORK_ITEM_CONTEXT * +FindCheckAddrIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to LmHost, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + NBT_WORK_ITEM_CONTEXT *Context; + BOOLEAN FoundIt = FALSE; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + if (CheckAddr.ResolvingNow && CheckAddr.Context) + { + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)CheckAddr.Context)->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + + Context = (NBT_WORK_ITEM_CONTEXT *)CheckAddr.Context; + CheckAddr.Context = NULL; + FoundIt = TRUE; + + } + } + else + { + // + // go through the list of Queued requests to find the correct one + // and cancel it + // + pHead = pEntry = &CheckAddr.ToResolve; + + while ((pEntry = pEntry->Flink) != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + RemoveEntryList(pEntry); + FoundIt = TRUE; + break; + + } + } + } + + return( FoundIt ? Context : NULL ); +} + +//---------------------------------------------------------------------------- +NBT_WORK_ITEM_CONTEXT * +LmHostIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to LmHost, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + NBT_WORK_ITEM_CONTEXT *Context; + BOOLEAN FoundIt = FALSE; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + if (LmHostQueries.ResolvingNow && LmHostQueries.Context) + { + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context)->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + + Context = (NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context; + LmHostQueries.Context = NULL; + FoundIt = TRUE; + + } + } + else + { + // + // go through the list of Queued requests to find the correct one + // and cancel it + // + pHead = pEntry = &LmHostQueries.ToResolve; + + while ((pEntry = pEntry->Flink) != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + RemoveEntryList(pEntry); + FoundIt = TRUE; + break; + + } + } + } + + return( FoundIt ? Context : NULL ); +} + +//---------------------------------------------------------------------------- +NBT_WORK_ITEM_CONTEXT * +DnsIrpCancelPaged( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to DNS, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pClientTracker; + NBT_WORK_ITEM_CONTEXT *Context; + BOOLEAN FoundIt = FALSE; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + // + // First check the lmhost list, then the Dns list + // + Context = LmHostIrpCancel(DeviceContext,pIrp); + + if (!Context) + { + + Context = FindCheckAddrIrpCancel(DeviceContext,pIrp); + + if (!Context) + { + + if (DnsQueries.ResolvingNow && DnsQueries.Context) + { + // + // this is the session setup tracker + // + pClientTracker = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context)->pClientContext; + if (pClientTracker->pClientIrp == pIrp) + { + + Context = (NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context; + DnsQueries.Context = NULL; + FoundIt = TRUE; + + } + } + else + { + // + // go through the list of Queued requests to find the correct one + // and cancel it + // + pHead = &DnsQueries.ToResolve; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + // this is the session setup tracker + // + pClientTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + if (pClientTracker->pClientIrp == pIrp) + { + RemoveEntryList(pEntry); + FoundIt = TRUE; + break; + + } + pEntry = pEntry->Flink; + } + } + } else { + + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Found tracker in CheckAddr list: %lx\n", Context)); + FoundIt = TRUE; + } + } + else + { + FoundIt = TRUE; + } + + return( FoundIt ? Context : NULL ); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +QueryProviderCompletion( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine handles the completion event when the Query Provider + Information completes. This routine must decrement the MaxDgramSize + and max send size by the respective NBT header sizes. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - not used + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + PTDI_PROVIDER_INFO pProvider; + ULONG HdrSize; + ULONG SubnetAddr; + ULONG ThisSubnetAddr; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDEVICECONTEXT *pDeviceContext; + tDEVICECONTEXT *pDevContext; + + + if (NT_SUCCESS(Irp->IoStatus.Status)) + { + pProvider = (PTDI_PROVIDER_INFO)MmGetMdlVirtualAddress(Irp->MdlAddress); + + if (pProvider->MaxSendSize > sizeof(tSESSIONHDR)) + { + // + // Nbt has just a two byte + 1 bit session message length, so it + // can't have a send size larger than 1ffff + // + if (pProvider->MaxSendSize > (0x1FFFF + sizeof(tSESSIONHDR))) + { + pProvider->MaxSendSize = 0x1FFFF; + } + else + { + pProvider->MaxSendSize -= sizeof(tSESSIONHDR); + } + } + else + { + pProvider->MaxSendSize = 0; + } + + // subtract the datagram hdr size and the scope size (times 2) + HdrSize = DGRAM_HDR_SIZE + (NbtConfig.ScopeLength << 1); + + if (pProvider->MaxDatagramSize > HdrSize) + { + pProvider->MaxDatagramSize -= HdrSize; + if (pProvider->MaxDatagramSize > MAX_NBT_DGRAM_SIZE) + { + pProvider->MaxDatagramSize = MAX_NBT_DGRAM_SIZE; + } + } + else + { + pProvider->MaxDatagramSize = 0; + } + + + // + // Set the correct service flags to indicate what Netbt supports. + // + pProvider->ServiceFlags = TDI_SERVICE_MESSAGE_MODE | + TDI_SERVICE_CONNECTION_MODE | + TDI_SERVICE_CONNECTIONLESS_MODE | + TDI_SERVICE_ERROR_FREE_DELIVERY | + TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_MULTICAST_SUPPORTED | + TDI_SERVICE_DELAYED_ACCEPTANCE | + TDI_SERVICE_ROUTE_DIRECTED; + + pProvider->MinimumLookaheadData = 128; + + // + // Check if any of the adapters with the same subnet address have + // the PointtoPoint bit set - and if so set it in the response. + // + pDeviceContext = (tDEVICECONTEXT *)DeviceContext; + SubnetAddr = pDeviceContext->IpAddress & pDeviceContext->SubnetMask; + + pEntry = pHead = &NbtConfig.DeviceContexts; + while ((pEntry = pEntry->Flink) != pHead) + { + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + ThisSubnetAddr = pDevContext->IpAddress & pDevContext->SubnetMask; + + if ((SubnetAddr == ThisSubnetAddr) && + (pDevContext->PointToPoint)) + { + pProvider->ServiceFlags |= TDI_SERVICE_POINT_TO_POINT; + break; + } + } + } + + + // + // Must return a non-error status otherwise the IO system will not copy + // back into the users buffer. + // + + return(STATUS_SUCCESS); + + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTQueryInformation( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query; + NTSTATUS status; + NTSTATUS Locstatus; + PVOID pBuffer; + LONG Size ; + PTA_NETBIOS_ADDRESS BroadcastAddress; + ULONG AddressLength; + ULONG BytesCopied; + PDEVICE_OBJECT pDeviceObject; + + // + // Should not be pageable since AFD can call us at raised Irql in case of AcceptEx. + // + // CTEPagedCode(); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&pIrpSp->Parameters; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("the query type is %X\n",Query->QueryType)); + + switch( Query->QueryType) + { + case TDI_QUERY_BROADCAST_ADDRESS: + + // the broadcast address is the netbios name "*0000000..." + + BroadcastAddress = (PTA_NETBIOS_ADDRESS)NbtAllocMem( + sizeof(TA_NETBIOS_ADDRESS),NBT_TAG('b')); + + if (!BroadcastAddress) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + AddressLength = sizeof(TA_NETBIOS_ADDRESS); + + BroadcastAddress->TAAddressCount = 1; + BroadcastAddress->Address[0].AddressLength = NETBIOS_NAME_SIZE + + sizeof(USHORT); + BroadcastAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + BroadcastAddress->Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; + + // the broadcast address to NetBios is "* 000000...", an * followed + // by 15 zeroes. + CTEZeroMemory(BroadcastAddress->Address[0].Address[0].NetbiosName, + NETBIOS_NAME_SIZE); + BroadcastAddress->Address[0].Address[0].NetbiosName[0] = '*'; + + + status = TdiCopyBufferToMdl ( + (PVOID)BroadcastAddress, + 0, + AddressLength, + pIrp->MdlAddress, + 0, + (PULONG)&pIrp->IoStatus.Information); + + CTEMemFree((PVOID)BroadcastAddress); + + break; + + + case TDI_QUERY_PROVIDER_INFO: + + // + // Simply pass the Irp on by to the Transport, and let it + // fill in the provider info + // + if (StreamsStack) + { + TdiBuildQueryInformation(pIrp, + pDeviceContext->pDgramDeviceObject, + pDeviceContext->pDgramFileObject, + QueryProviderCompletion, + NULL, + TDI_QUERY_PROVIDER_INFO, + pIrp->MdlAddress); + } + else + { + TdiBuildQueryInformation(pIrp, + pDeviceContext->pControlDeviceObject, + pDeviceContext->pControlFileObject, + QueryProviderCompletion, + NULL, + TDI_QUERY_PROVIDER_INFO, + pIrp->MdlAddress); + } + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(pDeviceContext->pControlDeviceObject,pIrp); + // + // we must return the next drivers ret code back to the IO subsystem + // + return(status); + + break; + + case TDI_QUERY_ADAPTER_STATUS: + + // + // check if it is a remote or local adapter status + // + if (Query->RequestConnectionInformation && + Query->RequestConnectionInformation->RemoteAddress) + { + PCHAR pName; + ULONG lNameType; + ULONG NameLen; + + // + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + + status = GetNetBiosNameFromTransportAddress( + Query->RequestConnectionInformation->RemoteAddress, + &pName, + &NameLen, + &lNameType); + + if ( NT_SUCCESS(status) && + (lNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) && + (NameLen <= NETBIOS_NAME_SIZE)) + { + status = NbtSendNodeStatus(pDeviceContext, + pName, + pIrp, + 0, + 0, + NodeStatusDone); + } + + // only complete the irp (below) for failure status's + if (status == STATUS_PENDING) + { + return(status); + } + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + } + else + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryAdapterStatus(pDeviceContext, + &pBuffer, + &Size); + + } + break; + + + + + case TDI_QUERY_CONNECTION_INFO: + { + tCONNECTELE *pConnectEle; + tLOWERCONNECTION *pLowerConn; + + // pass to transport to get the current throughput, delay and + // reliability numbers + // + + pConnectEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; +#if DBG + if (pConnectEle->Verify != NBT_VERIFY_CONNECTION) + { + status = STATUS_INVALID_HANDLE; + break; + } +#endif + pLowerConn = (tLOWERCONNECTION *)pConnectEle->pLowerConnId; + if (!pLowerConn) + { + status = STATUS_CONNECTION_INVALID; + break; + } + // + // Simply pass the Irp on by to the Transport, and let it + // fill in the info + // + pDeviceObject = IoGetRelatedDeviceObject( pLowerConn->pFileObject ); + + TdiBuildQueryInformation(pIrp, + pDeviceObject, + pLowerConn->pFileObject, + NULL, NULL, + TDI_QUERY_CONNECTION_INFO, + pIrp->MdlAddress); + + + status = IoCallDriver(pDeviceObject,pIrp); + + // + // we must return the next drivers ret code back to the IO subsystem + // + return(status); + + break; + } + + case TDI_QUERY_FIND_NAME: + // + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + status = NbtQueryFindName(Query->RequestConnectionInformation, + pDeviceContext, + pIrp, + FALSE); + + if (status == STATUS_PENDING) + { + return(status); + } + + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + break; + + case TDI_QUERY_ADDRESS_INFO: + status = NbtQueryGetAddressInfo( + pIrpSp, + &pBuffer, + &Size + ); + break; + + case TDI_QUERY_SESSION_STATUS: + default: + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt Query Info NOT SUPPORTED = %X\n",Query->QueryType)); + status = STATUS_NOT_SUPPORTED; + break; + + } + + BytesCopied = 0; + if (!NT_ERROR(status) && // allow buffer overflow to pass by + ((Query->QueryType == TDI_QUERY_ADAPTER_STATUS) || + (Query->QueryType == TDI_QUERY_ADDRESS_INFO))) + { + Locstatus = TdiCopyBufferToMdl( + pBuffer, + 0, + Size, + pIrp->MdlAddress, + 0, + &BytesCopied); + + if (Locstatus == STATUS_BUFFER_OVERFLOW) + { + status = STATUS_BUFFER_OVERFLOW; + } + CTEMemFree((PVOID)pBuffer); + } + // + // either Success or an Error + // so complete the irp + // + + NTIoComplete(pIrp,status,BytesCopied); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtQueryGetAddressInfo( + IN PIO_STACK_LOCATION pIrpSp, + OUT PVOID *ppBuffer, + OUT ULONG *pSize +) +{ + NTSTATUS status; + BOOLEAN IsGroup; + PLIST_ENTRY p; + tADDRESSELE *pAddressEle; + tNAMEADDR *pNameAddr; + tADDRESS_INFO *pAddressInfo; + tCLIENTELE *pClientEle; + tCONNECTELE *pConnectEle; + CTELockHandle OldIrq; + + pClientEle = pIrpSp->FileObject->FsContext; + if (pClientEle->Verify != NBT_VERIFY_CLIENT) + { + CTELockHandle OldIrq1; + pConnectEle = (tCONNECTELE *)pClientEle; + + // + // We crashed here since the pLowerConn was NULL below. + // Check the state of the connection, since it is possible that the connection + // was aborted and the disconnect indicated, but this query came in before the client + // got the disconnect indication. + // If the state is idle (in case of TDI_DISCONNECT_ABORT) or DISCONNECTED + // (TDI_DISCONNECT_RELEASE), error out. + // Also check for NBT_ASSOCIATED. + // + // NOTE: If NbtOpenConnection is unable to allocate the lower conn block (say, if the session fileobj + // has not been created yet), the state will be still be IDLE, so we are covered here. + // + CTESpinLock(pConnectEle,OldIrq1); + + if ((pConnectEle->Verify != NBT_VERIFY_CONNECTION) || + (pConnectEle->state <= NBT_ASSOCIATED) || // includes NBT_IDLE + (pConnectEle->state == NBT_DISCONNECTED)) + { + status = STATUS_INVALID_HANDLE; + } + else + { + // + // A TdiQueryInformation() call requesting TDI_QUERY_ADDRESS_INFO + // on a connection. Fill in a TDI_ADDRESS_INFO containing both the + // NetBIOS address and the IP address of the remote. Some of the + // fields are fudged. + // + + PNBT_ADDRESS_PAIR_INFO pAddressPairInfo; + pAddressPairInfo = NbtAllocMem(sizeof (NBT_ADDRESS_PAIR_INFO), NBT_TAG('c')); + + if (pAddressPairInfo) + { + memset ( pAddressPairInfo, 0, sizeof(NBT_ADDRESS_PAIR_INFO) ); + + pAddressPairInfo->ActivityCount = 1; + + pAddressPairInfo->AddressPair.TAAddressCount = 2; + + pAddressPairInfo->AddressPair.AddressNetBIOS.AddressLength = + TDI_ADDRESS_LENGTH_NETBIOS; + + pAddressPairInfo->AddressPair.AddressNetBIOS.AddressType = + TDI_ADDRESS_TYPE_NETBIOS; + + pAddressPairInfo->AddressPair.AddressNetBIOS.Address.NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + memcpy( &pAddressPairInfo->AddressPair.AddressNetBIOS.Address.NetbiosName[0], + &pConnectEle->RemoteName[0], + 16 + ); + + pAddressPairInfo->AddressPair.AddressIP.AddressLength = + TDI_ADDRESS_LENGTH_IP; + + pAddressPairInfo->AddressPair.AddressIP.AddressType = + TDI_ADDRESS_TYPE_IP; + + // + // Check for NULL (should not be NULL here since we check for states above). + // + // BUGBUG: Remove this check once we are sure that we are not hitting this condition + // + if (pConnectEle->pLowerConnId) { + pAddressPairInfo->AddressPair.AddressIP.Address.in_addr = + pConnectEle->pLowerConnId->SrcIpAddr; + + *ppBuffer = (PVOID)pAddressPairInfo; + *pSize = sizeof(NBT_ADDRESS_PAIR_INFO); + status = STATUS_SUCCESS; + } else { + DbgPrint("pLowerConn NULL in pConnEle%lx, state: %lx\n", pConnectEle, pConnectEle->state); + status = STATUS_INVALID_HANDLE; + } + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + CTESpinFree(pConnectEle,OldIrq1); + } + else + { + pAddressInfo = NbtAllocMem(sizeof(tADDRESS_INFO),NBT_TAG('c')); + if (pAddressInfo) + { + // + // count the clients attached to this address + // We need to spinlock the address element, which + // is why this routine is not pageable + // + pAddressInfo->ActivityCount = 0; + pAddressEle = pClientEle->pAddress; + + CTESpinLock(pAddressEle,OldIrq); + + for (p = pAddressEle->ClientHead.Flink; + p != &pAddressEle->ClientHead; + p = p->Flink) { + ++pAddressInfo->ActivityCount; + } + + CTESpinFree(pAddressEle,OldIrq); + + pNameAddr = pAddressEle->pNameAddr; + + IsGroup = (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) ? + FALSE : TRUE; + + TdiBuildNetbiosAddress((PUCHAR)pNameAddr->Name, + IsGroup, + &pAddressInfo->NetbiosAddress); + + *ppBuffer = (PVOID)pAddressInfo; + *pSize = sizeof(tADDRESS_INFO); + status = STATUS_SUCCESS; + + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + return status; +} + +//---------------------------------------------------------------------------- +NTSTATUS +DispatchIoctls( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp) + +/*++ +Routine Description: + + This Routine handles calling the OS independent routine depending on + the Ioctl passed in. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status=STATUS_UNSUCCESSFUL; + NTSTATUS Locstatus; + ULONG ControlCode; + ULONG Size; + PVOID pBuffer; + + CTEPagedCode(); + + ControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Ioctl Value is %X\n",ControlCode)); + + switch (ControlCode) + { + case IOCTL_NETBT_PURGE_CACHE: + { + + status = NbtResyncRemoteCache(); + + break; + } + break; + case IOCTL_NETBT_GET_CONNECTIONS: + { + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryConnectionList(NULL, + &pBuffer, + &Size); + } + break; + } + + case IOCTL_NETBT_ADAPTER_STATUS: + + if (pIrp->MdlAddress) + { + PIO_STACK_LOCATION pIrpSp; + tIPANDNAMEINFO *pIpAndNameInfo; + PCHAR pName; + ULONG lNameType; + ULONG NameLen; + ULONG IpAddrsList[2]; + + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIpAndNameInfo = pIrp->AssociatedIrp.SystemBuffer; + + // this routine gets a ptr to the netbios name out of the wierd + // TDI address syntax. + status = GetNetBiosNameFromTransportAddress( + &pIpAndNameInfo->NetbiosAddress, + &pName, + &NameLen, + &lNameType); + + if ( NT_SUCCESS(status) && + (lNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) && + (NameLen <= NETBIOS_NAME_SIZE)) + { + // + // Nbtstat sends down * in the first byte on Nbtstat -A <IP address> + // Make sure we let that case go ahead. + // + if ((pName[0] == '*') && + (pIpAndNameInfo->IpAddress == 0)) { + + status = STATUS_BAD_NETWORK_PATH; + } else { + IpAddrsList[0] = pIpAndNameInfo->IpAddress; + IpAddrsList[1] = 0; + status = NbtSendNodeStatus(pDeviceContext, + pName, + pIrp, + &IpAddrsList[0], + 0, + NodeStatusDone); + } + + } + // only complete the irp (below) for failure status's + if (status == STATUS_PENDING) + { + return(status); + } + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + } + break; + + case IOCTL_NETBT_GET_REMOTE_NAMES: + + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryAdapterStatus(NULL, + &pBuffer, + &Size); + } + break; + + case IOCTL_NETBT_GET_BCAST_NAMES: + { + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryBcastVsWins(pDeviceContext,&pBuffer,&Size); + } + break; + } + + case IOCTL_NETBT_REREAD_REGISTRY: + + status = NTReReadRegistry(pDeviceContext); + + break; + + case IOCTL_NETBT_ENABLE_EXTENDED_ADDR: { + // + // Enable extended addressing - pass up IP addrs on Datagram Recvs. + // + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + tCLIENTELE *pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + status = STATUS_SUCCESS; + + if (pIrpSp->FileObject->FsContext2 != (PVOID)NBT_ADDRESS_TYPE) { + status = STATUS_INVALID_ADDRESS; + } else { + pClientEle->ExtendedAddress = TRUE; + } + break; + } + + case IOCTL_NETBT_DISABLE_EXTENDED_ADDR: { + // + // Disnable extended addressing - dont pass up IP addrs on Datagram Recvs. + // + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + tCLIENTELE *pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + status = STATUS_SUCCESS; + + if (pIrpSp->FileObject->FsContext2 != (PVOID)NBT_ADDRESS_TYPE) { + status = STATUS_INVALID_ADDRESS; + } else { + pClientEle->ExtendedAddress = FALSE; + } + break; + } + + case IOCTL_NETBT_NEW_IPADDRESS: + + { + + tNEW_IP_ADDRESS *pNewAddress = (tNEW_IP_ADDRESS *)pIrp->AssociatedIrp.SystemBuffer; + + status = NbtNewDhcpAddress(pDeviceContext, + pNewAddress->IpAddress, + pNewAddress->SubnetMask); + + break; + } + + case IOCTL_NETBT_ADD_INTERFACE: + // + // Creates a dummy devicecontext which can be primed by the layer above + // with a DHCP address. This is to support multiple IP addresses per adapter + // for the Clusters group; but can be used by any module that needs support + // for more than one IP address per adapter. This private interface hides the + // devices thus created from the setup/regisrty and that is fine since the + // component (say, the clusters client) takes the responsibility for ensuring + // that the server (above us) comes to know of this new device. + // + { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("Ioctl Value is %X (IOCTL_NETBT_ADD_INTERFACE)\n",ControlCode)); + pBuffer = pIrp->AssociatedIrp.SystemBuffer; + Size = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + // + // return the export string created. + // + status = NbtAddNewInterface(pIrp, pBuffer, Size); + + NTIoComplete(pIrp,status,(ULONG)-1); + return status; + } + + case IOCTL_NETBT_DELETE_INTERFACE: + { +#if 0 + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + // + // Validate input buffer size + // + Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; + if (Size < sizeof(NETBT_ADD_DEL_IF)) { + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("NbtAddNewInterface: Output buffer too small for struct\n")); + status = STATUS_INVALID_PARAMETER; + } else { + pBuffer = pIrp->AssociatedIrp.SystemBuffer; + status = NbtDestroyDeviceObject(pBuffer); + } +#endif + // + // Delete the device this came down on.. + // + ASSERT(!pDeviceContext->IsDestroyed); + ASSERT(pDeviceContext->IsDynamic); + + status = NbtDestroyDeviceObject(pDeviceContext); + + break; + } + + case IOCTL_NETBT_QUERY_INTERFACE_INSTANCE: + { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + + // + // Validate input/output buffer size + // + Size = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; + if (Size < sizeof(NETBT_ADD_DEL_IF)) { + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("NbtQueryInstance: Output buffer too small for struct\n")); + status = STATUS_INVALID_PARAMETER; + } else { + PNETBT_ADD_DEL_IF pAddDelIf = (PNETBT_ADD_DEL_IF)pIrp->AssociatedIrp.SystemBuffer; + status = STATUS_SUCCESS; + + ASSERT(pDeviceContext->IsDynamic); + pAddDelIf->InstanceNumber = pDeviceContext->InstanceNumber; + pAddDelIf->Status = status; + pIrp->IoStatus.Information = sizeof(NETBT_ADD_DEL_IF); + + NTIoComplete(pIrp,status,(ULONG)-1); + return status; + + } + break; + } + + case IOCTL_NETBT_SET_WINS_ADDRESS: { + // + // Sets the WINS addresses for a dynamic adapter + // + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + + // + // Validate input/output buffer size + // + Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; + if (Size < sizeof(NETBT_SET_WINS_ADDR)) { + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("NbtSetWinsAddr: Input buffer too small for struct\n")); + status = STATUS_INVALID_PARAMETER; + } else { + PNETBT_SET_WINS_ADDR pSetWinsAddr = (PNETBT_SET_WINS_ADDR)pIrp->AssociatedIrp.SystemBuffer; + status = STATUS_SUCCESS; + + ASSERT(pDeviceContext->IsDynamic); + + pDeviceContext->lNameServerAddress = pSetWinsAddr->PrimaryWinsAddr; + pDeviceContext->lBackupServer = pSetWinsAddr->SecondaryWinsAddr; + + pSetWinsAddr->Status = status; + pIrp->IoStatus.Information = sizeof(NETBT_SET_WINS_ADDR); + + NTIoComplete(pIrp,status,(ULONG)-1); + return status; + + } + } + + case IOCTL_NETBT_DNS_NAME_RESOLVE: + { + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + // return an array of netbios names that are registered + status = NtDnsNameResolve(pDeviceContext,pBuffer,Size,pIrp); + + return(status); + } + break; + } + + case IOCTL_NETBT_CHECK_IP_ADDR: { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Ioctl Value is %X (IOCTL_NETBT_CHECK_IP_ADDR)\n",ControlCode)); + + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + // return an array of netbios names that are registered + status = NtCheckForIPAddr(pDeviceContext,pBuffer,Size,pIrp); + + return(status); + } + break; + } + + case IOCTL_NETBT_FIND_NAME: + { + tIPADDR_BUFFER *pIpAddrBuffer; + + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + + pIpAddrBuffer = pIrp->AssociatedIrp.SystemBuffer; + + status = NbtQueryFindName((PTDI_CONNECTION_INFORMATION)pIpAddrBuffer, + pDeviceContext, + pIrp, + TRUE); + + if (status == STATUS_PENDING) + { + return(status); + } + + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + break; + } + + case IOCTL_NETBT_GET_WINS_ADDR: + { + if (pIrp->MdlAddress) + { + tWINS_ADDRESSES *pBuffer; + + if( MmGetMdlByteCount( pIrp->MdlAddress ) >= sizeof(tWINS_ADDRESSES)) + { + pBuffer = (tWINS_ADDRESSES *)MmGetSystemAddressForMdl(pIrp->MdlAddress); + pBuffer->PrimaryWinsServer = pDeviceContext->lNameServerAddress; + pBuffer->BackupWinsServer = pDeviceContext->lBackupServer; + + status = STATUS_SUCCESS; + } + else + status = STATUS_BUFFER_OVERFLOW; + + break; + + } + break; + } + + case IOCTL_NETBT_GET_IP_ADDRS: + { + ULONG Length; + PULONG pIpAddr; + PLIST_ENTRY pEntry,pHead; + tDEVICECONTEXT *pDevContext; + + // + // return this devicecontext's ip address and all the other + // ip addrs after it. + // + if (pIrp->MdlAddress) + { + Length = MmGetMdlByteCount( pIrp->MdlAddress ); + + if (Length < sizeof(ULONG)) { + status = STATUS_BUFFER_TOO_SMALL; + } + else { + // + // Put this adapter first in the list + // + pIpAddr = (PULONG )MmGetSystemAddressForMdl(pIrp->MdlAddress); + *pIpAddr = pDeviceContext->IpAddress; + pIpAddr++; + Length -= sizeof(ULONG); + status = STATUS_SUCCESS; + + pEntry = pHead = &NbtConfig.DeviceContexts; + while ((pEntry = pEntry->Flink) != pHead) + { + if (Length < sizeof(ULONG)) { + status = STATUS_BUFFER_OVERFLOW; + break; + } + + pDevContext = CONTAINING_RECORD( + pEntry, + tDEVICECONTEXT, + Linkage + ); + + if ((pDevContext != pDeviceContext) && + (pDevContext->IpAddress)) + { + *pIpAddr = pDevContext->IpAddress; + pIpAddr++; + Length -= sizeof(ULONG); + } + } + + if (status == STATUS_SUCCESS) { + if (Length < sizeof(ULONG)) { + status = STATUS_BUFFER_OVERFLOW; + } + else { + // + // put a 0 address on the end + // + *pIpAddr = 0; + } + } + } + } + + break; + } + + case IOCTL_NETBT_GET_IP_SUBNET: + { + ULONG Length; + PULONG pIpAddr; + + // + // return this devicecontext's ip address and all the other + // ip addrs after it. + // + if (pIrp->MdlAddress) + { + Length = MmGetMdlByteCount( pIrp->MdlAddress ); + if (Length < 2*sizeof(ULONG)) + { + status = STATUS_BUFFER_OVERFLOW; + } + else + { + // + // Put this adapter first in the list + // + pIpAddr = (PULONG )MmGetSystemAddressForMdl(pIrp->MdlAddress); + *pIpAddr = pDeviceContext->IpAddress; + pIpAddr++; + *pIpAddr = pDeviceContext->SubnetMask; + + status = STATUS_SUCCESS; + } + } + } + break; + + case IOCTL_NETBT_WINS_RCV: + { + if (pIrp->MdlAddress) + { + status = RcvIrpFromWins(pDeviceContext,pIrp); + return(status); + + } + break; + } + case IOCTL_NETBT_WINS_SEND: + { + if (pIrp->MdlAddress) + { + BOOLEAN MustSend; + + status = WinsSendDatagram(pDeviceContext,pIrp,(MustSend = FALSE)); + return(status); + + break; + + } + break; + } + + } + + // + // copy the reponse to the client's Mdl + // + if (!NT_ERROR(status) && // allow buffer overflow to pass by + ((ControlCode == IOCTL_NETBT_GET_REMOTE_NAMES) || + (ControlCode == IOCTL_NETBT_GET_BCAST_NAMES) || + (ControlCode == IOCTL_NETBT_GET_CONNECTIONS)) ) + { + Locstatus = TdiCopyBufferToMdl( + pBuffer, + 0, + Size, + pIrp->MdlAddress, + 0, + (PULONG)&pIrp->IoStatus.Information); + + if (Locstatus == STATUS_BUFFER_OVERFLOW) + { + status = STATUS_BUFFER_OVERFLOW; + } + CTEMemFree((PVOID)pBuffer); + } + // + // either Success or an Error + // so complete the irp + // + NTIoComplete(pIrp,status,0); + + return(status); + +} +//---------------------------------------------------------------------------- +VOID +NTCancelReceive( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a listen Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + tLOWERCONNECTION *pLowerConn; + KIRQL OldIrq; + KIRQL OldIrq1; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + PIRP pRcvIrp; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Receive Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + CTESpinLock(pLowerConn,OldIrq); + } + + if (pConnEle->Verify == NBT_VERIFY_CONNECTION) + { + // now search the connection's receive queue looking for this Irp + // + pHead = &pConnEle->RcvHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pRcvIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + if (pRcvIrp == pIrp) + { + RemoveEntryList(pEntry); + + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + + if (pLowerConn) + { + CTESpinFree(pLowerConn,OldIrq); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + return; + + } + pEntry = pEntry->Flink; + + } + + } + + if (pLowerConn) + { + CTESpinFree(pLowerConn,OldIrq); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return; + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTReceive( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles Queuing a receive buffer on a connection or passing + the recieve buffer to the transport if there is outstanding data waiting + to be received on the connection. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status=STATUS_UNSUCCESSFUL; + PTDI_REQUEST_KERNEL pRequestKernel; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + KIRQL OldIrq; + ULONG ToCopy; + ULONG ClientRcvLen; + tLOWERCONNECTION *pLowerConn; + ULONG RemainingPdu; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + pConnEle = pIrpSp->FileObject->FsContext; + + PUSH_LOCATION(0x30); + + // be sure we have not been passed some bogus ptr + // +#if DBG + if (pConnEle->Verify != NBT_VERIFY_CONNECTION) + { + status = STATUS_INVALID_HANDLE; + NTIoComplete(pIrp,status,0); + return(status); + + } +#endif + if (pConnEle->state == NBT_SESSION_UP) + { + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + ULONG BytesCopied; + + PUSH_LOCATION(0x31); + + pLowerConn = pConnEle->pLowerConnId; + + CTESpinLock(pLowerConn,OldIrq); + + if (pLowerConn->StateRcv != PARTIAL_RCV) + { + // **** Fast Path Code **** + // + // Queue this receive buffer on to the Rcv Head + // + PUSH_LOCATION(0x46); + InsertTailList(&pConnEle->RcvHead, + &pIrp->Tail.Overlay.ListEntry); + + status = NTCheckSetCancelRoutine(pIrp,(PVOID)NTCancelReceive,pDeviceContext); + + if (!NT_SUCCESS(status)) + { + RemoveEntryList(&pIrp->Tail.Overlay.ListEntry); + CTESpinFree(pLowerConn,OldIrq); + NTIoComplete(pIrp,status,0); + return(status); + } + else + { + // + // if the irp is not cancelled, returning pending + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_PENDING); + } + + + } + else + { + + // ***** Partial Rcv - Data Still in Transport ***** + + BOOLEAN ZeroLengthSend; + + PUSH_LOCATION(0x32); + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:A Rcv Buffer posted data in Xport,InXport= %X,InIndic %X RcvIndicated %X\n", + pConnEle->BytesInXport,pLowerConn->BytesInIndicate, + pConnEle->ReceiveIndicated)); + + + // get the MDL chain length + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // Reset the Irp pending flag + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + // fill in the next irp stack location with our completion routine. + pIrpSp = IoGetNextIrpStackLocation(pIrp); + + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pConnEle->pLowerConnId; + pIrpSp->Flags = 0; + + // set flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pConnEle->pLowerConnId->pFileObject); + pIrpSp->FileObject = pConnEle->pLowerConnId->pFileObject; + + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + // Since this irp is going to traverse through CompletionRcv, we + // need to set the following, since it undoes this stuff. + // This also prevents the LowerConn from being blown away before + // the irp has returned from the transport + // + pLowerConn->RefCount++; + // + // pass the receive buffer directly to the transport, decrementing + // the number of receive bytes that have been indicated + // + ASSERT(pConnEle->TotalPcktLen >= pConnEle->BytesRcvd); + if (pClientParams->ReceiveLength > (pConnEle->TotalPcktLen - pConnEle->BytesRcvd)) + { + pParams->ReceiveLength = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + } + else + { + pParams->ReceiveLength = pClientParams->ReceiveLength; + } + + ClientRcvLen = pParams->ReceiveLength; + // + // Set the amount of data that we will receive so when the + // irp completes in completionRcv, we can fill in that + // info in the Irp + // + pConnEle->CurrentRcvLen = ClientRcvLen; + + // if a zero length send occurs, then ReceiveIndicated is set + // to zero with the state set to RcvPartial. Or, the client may + // pass down an Irp with no MDL in it!! - stupid but true + // + if ((pConnEle->ReceiveIndicated == 0) || !pIrp->MdlAddress) + { + ZeroLengthSend = TRUE; + } + else + ZeroLengthSend = FALSE; + + // calculate how many bytes are still remaining for the client. + ASSERT(pConnEle->ReceiveIndicated <= 0x20000); + if (pConnEle->ReceiveIndicated > ClientRcvLen) + { + PUSH_LOCATION(0x40); + pConnEle->ReceiveIndicated -= ClientRcvLen; + } + else + { + pConnEle->ReceiveIndicated = 0; + } + + if (pLowerConn->BytesInIndicate || ZeroLengthSend) + { + PMDL Mdl; + + PUSH_LOCATION(0x33); + if (ClientRcvLen > pLowerConn->BytesInIndicate) + { + ToCopy = pLowerConn->BytesInIndicate; + } + else + { + PUSH_LOCATION(0x41); + ToCopy = ClientRcvLen; + } + + // copy data from the indicate buffer to the client's buffer, + // remembering that there is a session header in the indicate + // buffer at the start of it... so skip that. The + // client can pass down a null Mdl address for a zero length + // rcv so check for that. + // + Mdl = pIrp->MdlAddress; + + if (Mdl) + { + TdiCopyBufferToMdl(MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl), + 0, // src offset + ToCopy, + Mdl, + 0, // dest offset + &BytesCopied); + } + else + { + BytesCopied = 0; + } + + // client's MDL is too short... + if (BytesCopied != ToCopy) + { + PUSH_LOCATION(0x42); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Receive Buffer too short for Indicate buff BytesCopied %X, ToCopy %X\n", + BytesCopied, ToCopy)); + +// ToCopy = BytesCopied; + + // so the irp will be completed, below + ClientRcvLen = BytesCopied; + } + + pLowerConn->BytesInIndicate -= (USHORT)BytesCopied; + + // this case is only if the irp is full and should be returned + // now. + if (BytesCopied == ClientRcvLen) + { + PUSH_LOCATION(0x34); + // check if the indicate buffer is empty now. If not, then + // move the data forward to the start of the buffer. + // + if (pLowerConn->BytesInIndicate) + { + PUSH_LOCATION(0x43); + CopyToStartofIndicate(pLowerConn,BytesCopied); + } + // + // the irp is full so complete it + // + // the client MDL is full, so complete his irp + // CompletionRcv increments the number of bytes rcvd + // for this session pdu (pConnEle->BytesRcvd). + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // since we are completing it and TdiRcvHandler did not set the next + // one. + // + ASSERT(pIrp->CurrentLocation > 1); + + IoSetNextIrpStackLocation(pIrp); + + // we need to track how much of the client's MDL has filled + // up to know when to return it. CompletionRcv subtracts + // from this value as it receives bytes. + pConnEle->FreeBytesInMdl = ClientRcvLen; + pConnEle->CurrentRcvLen = ClientRcvLen; + + // + // this will complete through CompletionRcv... and for that + // reason it will get any more data left in the transport. The + // Completion routine will set the correct state for the rcv when + // it processes this Irp ( to INDICATED, if needed). + // + if (pConnEle->ReceiveIndicated == 0) + { + PUSH_LOCATION(0x44); + ASSERT(pLowerConn->BytesInIndicate == 0); + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + + } + CTESpinFree(pLowerConn,OldIrq); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + return(STATUS_SUCCESS); + } + else + { + PUSH_LOCATION(0x35); + // + // clear the number of bytes in the indicate buffer since the client + // has taken more than the data left in the Indicate buffer + // + pLowerConn->BytesInIndicate = 0; + + // decrement the client rcv len by the amount already put into the + // client Mdl + // + ClientRcvLen -= BytesCopied; + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt: Pass Client Irp to Xport BytesinXport %X, ClientRcvLen %X\n", + pConnEle->BytesInXport,ClientRcvLen)); + // + // Set the amount left inthe transport after this irp + // completes + if (pConnEle->BytesInXport < ClientRcvLen ) + { + pConnEle->BytesInXport = 0; + } + else + { + PUSH_LOCATION(0x45); + pConnEle->BytesInXport -= ClientRcvLen; + + } + + // Adjust the number of bytes in the Mdl chain so far since the + // completion routine will only count the bytes filled in by the + // transport + pConnEle->BytesRcvd += BytesCopied; + + // the client is going to take more data from the transport with + // this Irp. Set the new Rcv Length that accounts for the data just + // copied to the Irp. + // + pParams->ReceiveLength = ClientRcvLen; + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:ClientRcvLen = %X, LeftinXport= %X BytesCopied= %X %X\n",ClientRcvLen, + pConnEle->BytesInXport,BytesCopied,pLowerConn)); + + // set the state to this so we can undo the MDL footwork + // in completion rcv - since we have made a partial MDL and + // put that at the start of the chain. + // + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + + // Note that the Irp Mdl address changes below + // when MakePartialMdl is called so this line cannot + // be moved to the common code below!! + pLowerConn->pMdl = pIrp->MdlAddress; + + // setup the next MDL so we can create a partial mdl correctly + // in TdiReceiveHandler + // + pConnEle->pNextMdl = pIrp->MdlAddress; + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + // Force the system to map and lock the user buffer + MmGetSystemAddressForMdl(pIrp->MdlAddress); + MakePartialMdl(pConnEle,pIrp,BytesCopied); + + // pass the Irp to the transport + // + // + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:Calling IoCallDriver\n")); + ASSERT(pIrp->CurrentLocation > 1); + + } + + } + else + { + PUSH_LOCATION(0x36); + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:Pass Irp To Xport Bytes in Xport %X, ClientRcvLen %X, RcvIndicated %X\n", + pConnEle->BytesInXport,ClientRcvLen,pConnEle->ReceiveIndicated)); + // + // there are no bytes in the indicate buffer, so just pass the + // irp on down to the transport + // + // + // Decide the next state depending on whether the transport currently + // has enough data for this irp + // + if (pConnEle->BytesInXport < ClientRcvLen) + { + PUSH_LOCATION(0x37); + pConnEle->BytesInXport = 0; + // + // to get to here, the implication is that ReceiveIndicated + // equals zero too!! Since ReceiveInd cannot be more than + // BytesInXport, so we can change the state to fill irp without + // worrying about overwriting PartialRcv + // + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + // setup the next MDL so we can create a partial mdl correctly + // in TdiReceiveHandler + // + pConnEle->pNextMdl = pIrp->MdlAddress; + + } + else + { + + PUSH_LOCATION(0x38); + pConnEle->BytesInXport -= ClientRcvLen; + + // set the state to this so we know what to do in completion rcv + // + if (pConnEle->ReceiveIndicated == 0) + { + PUSH_LOCATION(0x39); + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + } + + + // + // save the Irp so we can reconstruct things later + // + pLowerConn->pMdl = pIrp->MdlAddress; + + } + + // *** Common Code to passing irp to transport - when there is + // data in the indicate buffer and when there isn't + + // keep track of data in MDL so we know when it is full + // and we need to return it to the user + // + pConnEle->FreeBytesInMdl = pParams->ReceiveLength; + // Force the system to map and lock the user buffer + MmGetSystemAddressForMdl(pIrp->MdlAddress); + + // + // Null the Irp since we are passing it to the transport. + // + pConnEle->pIrpRcv = NULL; + CTESpinFree(pLowerConn,OldIrq); + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(IoGetRelatedDeviceObject(pLowerConn->pFileObject),pIrp); + + } + + return(status); + } + + // + // session in wrong state so reject the buffer posting + // + + PUSH_LOCATION(0x47); + // + // complete the irp, since there must have been some sort of error + // to get to here + // + NTIoComplete(pIrp,STATUS_REMOTE_DISCONNECT,0); + + return(status); + + +} +//---------------------------------------------------------------------------- +VOID +NTCancelRcvDgram( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a listen Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCLIENTELE *pClientEle; + KIRQL OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + tRCVELE *pRcvEle; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Rcv Dgram Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + if (pClientEle->Verify == NBT_VERIFY_CLIENT) + { + // now search the client's listen queue looking for this connection + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pHead = &pClientEle->RcvDgramHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pRcvEle = CONTAINING_RECORD(pEntry,tRCVELE,Linkage); + if (pRcvEle->pIrp == pIrp) + { + RemoveEntryList(pEntry); + + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTEMemFree((PVOID)pRcvEle); + + return; + + } + pEntry = pEntry->Flink; + + } + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + return; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTReceiveDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles receiving a datagram by passing the datagram rcv + buffer to the non-OS specific code. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVEDG pTdiRequest; + TDI_REQUEST Request; + ULONG ReceivedLength; + tCLIENTELE *pClientEle; + + CTEPagedCode(); + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt: Got a Receive datagram that NBT was NOT \n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + // get the sending information out of the irp + pTdiRequest = (PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters; + + Request.Handle.AddressHandle = pClientEle; + + status = NbtReceiveDatagram( + &Request, + pTdiRequest->ReceiveDatagramInformation, + pTdiRequest->ReturnDatagramInformation, + pTdiRequest->ReceiveLength, + &ReceivedLength, + (PVOID)pIrp->MdlAddress, // user data + (tDEVICECONTEXT *)pDeviceContext, + pIrp); + + if (status != STATUS_PENDING) + { + + NTIoComplete(pIrp,status,ReceivedLength); + + } + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTSend( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles sending session pdus across a connection. It is + all OS specific code. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + PTDI_REQUEST_KERNEL_SEND pTdiRequest; + PMDL pMdl; + PSINGLE_LIST_ENTRY pSingleListEntry; + tSESSIONHDR *pSessionHdr; + tCONNECTELE *pConnEle; + KIRQL OldIrq; + KIRQL OldIrq1; + PTDI_REQUEST_KERNEL_SEND pParams; + PFILE_OBJECT pFileObject; + tLOWERCONNECTION *pLowerConn; + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // get the sending information out of the irp + pTdiRequest = (PTDI_REQUEST_KERNEL_SEND)&pIrpSp->Parameters; + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + //ASSERT(pConnEle->Verify == NBT_VERIFY_CONNECTION); + + if (pConnEle) + { + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + // + // make sure lowerconn stays valid until the irp is done + // + CTESpinLock(pLowerConn,OldIrq1); + pLowerConn->RefCount++; + CTESpinFree(pLowerConn,OldIrq1); + } + else + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:attempting send when LowerConn has been freed!\n")); + + status = STATUS_INVALID_HANDLE; + + // to save on indent levels use a goto here + goto ErrorExit; + } + + CTESpinLock(pConnEle,OldIrq); + + // check the state of the connection + if (pConnEle->state == NBT_SESSION_UP) + { + // + // send the data on downward to tcp + // allocate an MDL to allow us to put the session hdr in first and then + // put the users buffer on after that, chained to the session hdr MDL. + // + CTESpinLockAtDpc(&NbtConfig); + + if (NbtConfig.SessionMdlFreeSingleList.Next) + { + pSingleListEntry = PopEntryList(&NbtConfig.SessionMdlFreeSingleList); + pMdl = CONTAINING_RECORD(pSingleListEntry,MDL,Next); + + ASSERT ( MmGetMdlByteCount ( pMdl ) == sizeof ( tSESSIONHDR ) ); + + } + else + { + NbtGetMdl(&pMdl,eNBT_FREE_SESSION_MDLS); + + if (!pMdl) + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Unable to get an MDL for a session send!\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + CTESpinFreeAtDpc(&NbtConfig); + CTESpinFree(pConnEle,OldIrq); + + // to save on indent levels use a goto here + goto ErrorExit; + } + + } + + CTESpinFreeAtDpc(&NbtConfig); + + // get the session hdr address out of the MDL + pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl); + + // the type of PDU is always a session message, since the session + // request is sent when the client issues a "connect" rather than a send + // + pSessionHdr->UlongLength = htonl(pTdiRequest->SendLength); + + // get the device object and file object for the TCP transport underneath + // link the user buffer on the end of the session header Mdl on the Irp + // + pMdl->Next = pIrp->MdlAddress; + pIrp->MdlAddress = pMdl; + + pIrpSp = IoGetNextIrpStackLocation(pIrp); + + pParams = (PTDI_REQUEST_KERNEL_SEND)&pIrpSp->Parameters; + pParams->SendFlags = pTdiRequest->SendFlags; + pParams->SendLength = pTdiRequest->SendLength + sizeof(tSESSIONHDR); + + + pIrpSp->CompletionRoutine = SendCompletion; + pIrpSp->Context = (PVOID)pLowerConn; + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_SEND; + + pFileObject = pLowerConn->pFileObject; + pLowerConn->BytesSent += pParams->SendLength; + + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + + CTESpinFree(pConnEle,OldIrq); + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(IoGetRelatedDeviceObject(pFileObject),pIrp); + + return(status); + + }//correct state + else + { + CTESpinFree(pConnEle,OldIrq); + // + // Release pLowerConn->RefCount, grabbed above. + // + CTESpinLock(pLowerConn,OldIrq1); + pLowerConn->RefCount--; + CTESpinFree(pLowerConn,OldIrq1); + + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Invalid state for connection on an attempted send, %X\n", + pConnEle)); + status = STATUS_INVALID_HANDLE; + } + } + else // pConnEle + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:attempting send with NULL Connection element!\n")); + status = STATUS_INVALID_HANDLE; + } + + +ErrorExit: + + // + // Reset the Irp pending flag + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + // + // complete the irp, since there must have been some sort of error + // to get to here + // + NTIoComplete(pIrp,status,0); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +SendCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine handles the completion event when the send completes with + the underlying transport. It must put the session hdr buffer back in + the correct free list and free the active q entry and put it back on + its free list. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + PMDL pMdl; + tLOWERCONNECTION *pLowerConn; + + // + // Do some checking to keep the Io system happy - propagate the pending + // bit up the irp stack frame.... if it was set by the driver below then + // it must be set by me + // + if (Irp->PendingReturned) + { + IoMarkIrpPending(Irp); + } + + // put the MDL we back on its free list and put the clients mdl back on the Irp + // as it was before the send + pMdl = Irp->MdlAddress; + Irp->MdlAddress = pMdl->Next; + + ASSERT ( MmGetMdlByteCount ( pMdl ) == sizeof ( tSESSIONHDR ) ); + +#if DBG + IF_DBG(NBT_DEBUG_SEND) + { + PMDL pMdl1; + ULONG ulen1,ulen2,ulen3; + UCHAR uc; + tSESSIONHDR *pSessionHdr; + PSINGLE_LIST_ENTRY pSingleListEntry; + KIRQL OldIrq; + + pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl); + ulen1 = htonl ( pSessionHdr->UlongLength ); + + for ( ulen2 = 0 , pMdl1 = pMdl ; ( pMdl1 = pMdl1->Next ) != NULL ; ) { + ulen3 = MmGetMdlByteCount ( pMdl1 ); + ASSERT ( ulen3 > 0 ); + uc = ( ( UCHAR * ) MmGetMdlVirtualAddress ( pMdl1 ) ) [ ulen3 - 1 ]; + ulen2 += ulen3; + } + + ASSERT ( ulen2 == ulen1 ); + + CTESpinLock(&NbtConfig,OldIrq); + for ( pSingleListEntry = &NbtConfig.SessionMdlFreeSingleList ; + ( pSingleListEntry = pSingleListEntry->Next ) != NULL ; + ) + { + pMdl1 = CONTAINING_RECORD(pSingleListEntry,MDL,Next); + ASSERT ( pMdl1 != pMdl ); + } + CTESpinFree(&NbtConfig,OldIrq); + } +#endif // DBG + + ExInterlockedPushEntryList(&NbtConfig.SessionMdlFreeSingleList, + (PSINGLE_LIST_ENTRY)pMdl, + &NbtConfig.SpinLock); + + // fill in the sent size so that it substracts off the session header size + // + if (Irp->IoStatus.Information > sizeof(tSESSIONHDR)) + { + + Irp->IoStatus.Information -= sizeof(tSESSIONHDR); + } + else + { + // nothing was sent + Irp->IoStatus.Information = 0; + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Zero Send Length for a session send!\n")); + } + + // + // we incremented this before the send: deref it now + // + pLowerConn = (tLOWERCONNECTION *)Context; +#if DBG + if (!pLowerConn || pLowerConn->Verify != NBT_VERIFY_LOWERCONN) + { + ASSERTMSG("Nbt: LowerConn is not valid!\n",0); + } +#endif + NbtDereferenceLowerConnection(pLowerConn); + + return(STATUS_SUCCESS); + + UNREFERENCED_PARAMETER( DeviceObject ); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTSendDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles sending a datagram down to the transport. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + LONG lSentLength; + TDI_REQUEST Request; + PTDI_REQUEST_KERNEL_SENDDG pTdiRequest; + tCLIENTELE *pClientEle; + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status); + + // get the sending information out of the irp + pTdiRequest = (PTDI_REQUEST_KERNEL_SENDDG)&pIrpSp->Parameters; + Request.Handle.AddressHandle = pClientEle; + + lSentLength = 0; + status = NbtSendDatagram( + &Request, + pTdiRequest->SendDatagramInformation, + pTdiRequest->SendLength, + &lSentLength, + (PVOID)pIrp->MdlAddress, // user data + (tDEVICECONTEXT *)pDeviceContext, + pIrp); + + + // + // either Success or an Error + // so complete the irp - PENDING is never returned!! + // + NTIoComplete(pIrp,status,lSentLength); + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTSetInformation( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles sets up event handlers that the client passes in. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + // *TODO* + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:************ Got a Set Information that was NOT expected *******\n")); + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTQueueToWorkerThread( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID pClientContext, + IN PVOID ClientCompletion, + IN PVOID CallBackRoutine, + IN PVOID pDeviceContext + ) +/*++ + +Routine Description: + + This routine simply queues a request on an excutive worker thread + for later execution. Scanning the LmHosts file must be down this way. + +Arguments: + pTracker - the tracker block for context + CallbackRoutine - the routine for the Workerthread to call + pDeviceContext - the device context which is this delayed event + pertains to. This could be NULL (meaning it's an event + pertaining to not any specific device context) + +Return Value: + + +--*/ + +{ + NTSTATUS status = STATUS_UNSUCCESSFUL ; + NBT_WORK_ITEM_CONTEXT *pContext; + + pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('e')); + if (pContext) + { + pContext->pTracker = pTracker; + pContext->pClientContext = pClientContext; + pContext->ClientCompletion = ClientCompletion; + + ExInitializeWorkItem(&pContext->Item,CallBackRoutine,pContext); + ExQueueWorkItem(&pContext->Item,DelayedWorkQueue); + status = STATUS_SUCCESS; + } + + return(status); + +} +//---------------------------------------------------------------------------- +VOID +SecurityDelete( + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles deleting a security context at non-dpc level. + +Arguments: + + +Return Value: + + none + +--*/ +{ + PSECURITY_CLIENT_CONTEXT pClientSecurity; + + pClientSecurity = (PSECURITY_CLIENT_CONTEXT)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + SeDeleteClientSecurity(pClientSecurity); + CTEMemFree(pContext); +} + +//---------------------------------------------------------------------------- +VOID +NTSendSession( + IN tDGRAM_SEND_TRACKING *pTracker, + IN tLOWERCONNECTION *pLowerConn, + IN PVOID pCompletion) + +/*++ +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 + +--*/ +{ + PKDPC pDpc; + + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('f')); + if (!pDpc) + { + return; + } + KeInitializeDpc(pDpc, + DpcSendSession, + (PVOID)pTracker); + + KeInsertQueueDpc(pDpc,(PVOID)pLowerConn,pCompletion); +} + +//---------------------------------------------------------------------------- +VOID +DpcSendSession( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls TcpSendSession from a Dpc started in + in NTSendSession (above). + +Arguments: + + +Return Value: + + +--*/ + +{ + CTEMemFree((PVOID)pDpc); + + + TcpSendSession((tDGRAM_SEND_TRACKING *)Context, + (tLOWERCONNECTION *)SystemArgument1, + (PVOID)SystemArgument2); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTSetEventHandler( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tCLIENTELE *pClientEle; + PTDI_REQUEST_KERNEL_SET_EVENT pKeSetEvent; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientEle = pIrpSp->FileObject->FsContext; + pKeSetEvent = (PTDI_REQUEST_KERNEL_SET_EVENT)&pIrpSp->Parameters; + + // call the not NT specific routine to setup the event handler in the + // nbt data structures + status = NbtSetEventHandler( + pClientEle, + pKeSetEvent->EventType, + pKeSetEvent->EventHandler, + pKeSetEvent->EventContext); + + return(status); + +} + +//---------------------------------------------------------------------------- + +VOID +NTIoComplete( + IN PIRP pIrp, + IN NTSTATUS Status, + IN ULONG SentLength) + +/*++ +Routine Description: + + This Routine handles calling the NT I/O system to complete an I/O. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + KIRQL OldIrq; + +#if DBG + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: NTIoComplete error return status = %X\n",Status)); +// ASSERTMSG("Nbt: Error Ret Code In IoComplete",0); + } +#endif + + pIrp->IoStatus.Status = Status; + + // use -1 as a flag to mean do not adjust the sent length since it is + // already set + if (SentLength != -1) + { + pIrp->IoStatus.Information = SentLength; + } + +#if DBG + if ( (Status != STATUS_SUCCESS) && + (Status != STATUS_PENDING) && + (Status != STATUS_INVALID_DEVICE_REQUEST) && + (Status != STATUS_INVALID_PARAMETER) && + (Status != STATUS_IO_TIMEOUT) && + (Status != STATUS_BUFFER_OVERFLOW) && + (Status != STATUS_BUFFER_TOO_SMALL) && + (Status != STATUS_INVALID_HANDLE) && + (Status != STATUS_INSUFFICIENT_RESOURCES) && + (Status != STATUS_CANCELLED) && + (Status != STATUS_DUPLICATE_NAME) && + (Status != STATUS_TOO_MANY_NAMES) && + (Status != STATUS_TOO_MANY_SESSIONS) && + (Status != STATUS_REMOTE_NOT_LISTENING) && + (Status != STATUS_BAD_NETWORK_PATH) && + (Status != STATUS_HOST_UNREACHABLE) && + (Status != STATUS_CONNECTION_REFUSED) && + (Status != STATUS_WORKING_SET_QUOTA) && + (Status != STATUS_REMOTE_DISCONNECT) && + (Status != STATUS_LOCAL_DISCONNECT) && + (Status != STATUS_LINK_FAILED) && + (Status != STATUS_SHARING_VIOLATION) && + (Status != STATUS_UNSUCCESSFUL) && + (Status != STATUS_ACCESS_VIOLATION) && + (Status != STATUS_NONEXISTENT_EA_ENTRY) ) + { + KdPrint(("Nbt: returning unusual status = %X\n",Status)); + } +#endif + + // set the Irps cancel routine to null or the system may bugcheck + // with a bug code of CANCEL_STATE_IN_COMPLETED_IRP + // + // refer to IoCancelIrp() ..\ntos\io\iosubs.c + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); +} +//---------------------------------------------------------------------------- + +NTSTATUS +NTGetIrpIfNotCancelled( + IN PIRP pIrp, + IN PIRP *ppIrpInStruct + ) +/*++ +Routine Description: + + This Routine gets the IOCancelSpinLock to coordinate with cancelling + irps It then returns STATUS_SUCCESS. It also nulls the irp in the structure + pointed to by the second parameter - so that the irp cancel routine + will not also be called. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + KIRQL OldIrq; + NTSTATUS status; + + IoAcquireCancelSpinLock(&OldIrq); + + // this nulls the irp in the datastructure - i.e. pConnEle->pIrp = NULL + *ppIrpInStruct = NULL; + + if (!pIrp->Cancel) + { + status = STATUS_SUCCESS; + } + else + { + status = STATUS_UNSUCCESSFUL; + } + IoSetCancelRoutine(pIrp,NULL); + + IoReleaseCancelSpinLock(OldIrq); + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTCheckSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine sets the cancel routine for an Irp. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + + // + // Check if the irp was cancelled yet and if not, then set the + // irp cancel routine. + // + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + if (pIrp->Cancel) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + status = STATUS_CANCELLED; + + } + else + { + // setup the cancel routine + IoMarkIrpPending(pIrp); + IoSetCancelRoutine(pIrp,CancelRoutine); + status = STATUS_SUCCESS; + } + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + return(status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine sets the cancel routine for an Irp. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + + // + // Check if the irp was cancelled yet and if not, then set the + // irp cancel routine. + // + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + if (pIrp->Cancel) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + status = STATUS_CANCELLED; + + // + // Note the cancel spin lock is released by the Cancel routine + // + + (*(PDRIVER_CANCEL)CancelRoutine)((PDEVICE_OBJECT)pDeviceContext,pIrp); + + } + else + { + // setup the cancel routine and mark the irp pending + // + IoMarkIrpPending(pIrp); + IoSetCancelRoutine(pIrp,CancelRoutine); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + status = STATUS_SUCCESS; + } + return(status); + +} + +//---------------------------------------------------------------------------- +VOID +NTClearContextCancel( + IN NBT_WORK_ITEM_CONTEXT *pContext + ) +/*++ +Routine Description: + + This Routine sets the cancel routine for + ((tDGRAM_SEND_TRACKING *)(pContext->pClientContext))->pClientIrp + to NULL. + + NbtConfig.JointLock should be held when this routine is called. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ +{ + NTSTATUS status; + status = NTCancelCancelRoutine( ((tDGRAM_SEND_TRACKING *)(pContext->pClientContext))->pClientIrp ); + ASSERT ( status != STATUS_CANCELLED ); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCancelCancelRoutine( + IN PIRP pIrp + ) + +/*++ +Routine Description: + + This Routine sets the cancel routine for an Irp to NULL + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + + if ( pIrp ) + { + // + // Check if the irp was cancelled yet and if not, then set the + // irp cancel routine. + // + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + + if (pIrp->Cancel) + { + status = STATUS_CANCELLED; + } + IoSetCancelRoutine(pIrp,NULL); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + } + + return(status); +} + +//---------------------------------------------------------------------------- +VOID +FindNameCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a FindName Irp - which has + been passed down by a client (e.g. ping). Typically, when ping succeeds + on another adapter, it will issue this cancel. + On receiving the cancel, we stop any timer that is running in connection + with name query and then complete the irp with status_cancelled. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + PIO_STACK_LOCATION pIrpSp; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a FindName Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pTracker = pIrpSp->Parameters.Others.Argument4; + + // + // We want to ensure that the tracker supplied by FsContext + // is the right Tracker for this Irp + // + if (pTracker && (pIrp == pTracker->pClientIrp)) + { + // + // if pClientIrp still valid, completion routine hasn't run yet: go ahead + // and complete the irp here + // + pIrpSp->Parameters.Others.Argument4 = NULL; + pTracker->pClientIrp = NULL; + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + NTIoComplete(pIrp,STATUS_CANCELLED,(ULONG)-1); + + } else + { + // + // the completion routine has run. + // + IoReleaseCancelSpinLock(pIrp->CancelIrql); + } + + return; +} + diff --git a/private/ntos/nbt/nt/ntpnp.c b/private/ntos/nbt/nt/ntpnp.c new file mode 100644 index 000000000..20dbc90b5 --- /dev/null +++ b/private/ntos/nbt/nt/ntpnp.c @@ -0,0 +1,682 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + NTPNP.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for the + NBT Transport and other routines that are specific to the NT implementation + of a driver. + +Author: + + Earle R. Horton (earleh) 08-Nov-1995 + +Revision History: + +--*/ + + +#include "nbtprocs.h" + +#ifdef _PNP_POWER + +NTSTATUS +NbtNtPNPInit( + VOID + ) +/*++ + +Routine Description: + + Some general driver initialization that we postpone until we have + an actual IP address to which to bind. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operations. + +--*/ + +{ + NTSTATUS status; + + // start some timers + status = InitTimersNotOs(); + + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_TIMERS,status); + KdPrint(("NBT:Failed to Initialize the Timers!,status = %X\n", + status)); + StopInitTimers(); + return(status); + } + +} + +VOID +NbtFailedNtPNPInit( + VOID + ) +/*++ + +Routine Description: + + Undo some general driver initialization that we postpone until we have + an actual IP address to which to bind. Called after NbtAddressAdd() + failed to add the desired address, and no other addresses had been + added previously. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operations. + +--*/ + +{ + StopInitTimers(); +} + +NTSTATUS +NbtAddressAdd( + ULONG IpAddr, + PUNICODE_STRING pucBindString, + PUNICODE_STRING pucExportString, + PULONG Inst + ) +{ + NTSTATUS status = STATUS_SUCCESS; + NTSTATUS dontcarestatus; + tDEVICES *pBindDevices = NULL; + tDEVICES *pExportDevices = NULL; + tDEVICES BindDevices; + tDEVICES ExportDevices; + tADDRARRAY *pAddrArray = NULL; + + ULONG ulIpAddress; + ULONG ulSubnetMask; + + int i; + + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + tDEVICECONTEXT *pDeviceContext = NULL; + + BOOLEAN Attached; + + if (NbtConfig.uDevicesStarted == 0) + { + status = NbtNtPNPInit(); + if ( status != STATUS_SUCCESS ) { + KdPrint(("NetBT!NbtAddressAdd: Global driver initialization failed,\nfailing to add address %d%d%d%d.\n",IpAddr&0xFF,(IpAddr>>8)&0xFF,(IpAddr>>16)&0xFF,(IpAddr>>24)&0xFF)); + return status; + } + } + + if (IpAddr) { + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + // + // Find the bind and export devices to use from the device + // described in the registry that uses this address. + // + status = NbtReadRegistry( + &NbtConfig.pRegistry, + NULL, // Null Driver Object + &NbtConfig, + &pBindDevices, + &pExportDevices, + &pAddrArray); + + CTEExReleaseResource(&NbtConfig.Resource); + } else { + status = STATUS_SUCCESS; + ASSERT(pucBindString); + ASSERT(pucExportString); + + // + // Init the bind/export structs + // + BindDevices.RegistrySpace = pucBindString->Buffer; + BindDevices.Names[0] = *pucBindString; + + ExportDevices.RegistrySpace = pucExportString->Buffer; + ExportDevices.Names[0] = *pucExportString; + + pBindDevices = &BindDevices; + pExportDevices = &ExportDevices; + } + + if ( + ( status == STATUS_SUCCESS ) + && ( pBindDevices != NULL ) + ) + { + + for (i=0; i<pNbtGlobConfig->uNumDevices; i++ ) + { + BOOLEAN fStatic = TRUE; + + // + // Fetch a static IP address from the registry. + // + status = GetIPFromRegistry( + &NbtConfig.pRegistry, + &pBindDevices->Names[i], + &ulIpAddress, + &ulSubnetMask, + FALSE); + if ( status == STATUS_INVALID_ADDRESS ) + { + fStatic = FALSE; + + // + // This one doesn't have a valid static address. Try DHCP. + // + status = GetIPFromRegistry( + &NbtConfig.pRegistry, + &pBindDevices->Names[i], + &ulIpAddress, + &ulSubnetMask, + TRUE); + } + + if (((status == STATUS_SUCCESS) && ( ulIpAddress == IpAddr )) || + !IpAddr) + { + pDeviceContext = NbtFindBindName ( &pBindDevices->Names[i] ); + + if ( pDeviceContext != NULL ) + { + // + // Device already exists. Do something sensible with it. + // + + // + // For static addresses, open the addresses with the transports; add the permanent name + // + if (fStatic) { + +#ifdef _PNP_POWER + // + // these are passed into here in the reverse byte order, wrt to the IOCTL + // from DHCP. + // + (VOID)NbtNewDhcpAddress(pDeviceContext,htonl(ulIpAddress),htonl(ulSubnetMask)); +#endif // NOTYET_PNP + // + // Add the "permanent" name to the local name table. This is the IP + // address of the node padded out to 16 bytes with zeros. + // + status = NbtAddPermanentName(pDeviceContext); + } + + // + // If the device was not registered with TDI, do so now. + // + + // + // By-pass the TDI PnP mechanism for logical interfaces + // + if (IpAddr) { + if (pDeviceContext->RegistrationHandle == NULL) { + // pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + + status = TdiRegisterDeviceObject( + &pExportDevices->Names[i], + &pDeviceContext->RegistrationHandle); + + if (!NT_SUCCESS(status)) { + KdPrint(("Couldn't register device object\n")); + } + } + } else { + pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + } + } + else + { + BOOLEAN Attached = FALSE; + + // + // Attach to the system process so that all handles are created in the + // proper context. + // + CTEAttachFsp(&Attached); + + status = NbtCreateDeviceObject( + NbtConfig.DriverObject, + &NbtConfig, + &pBindDevices->Names[i], + &pExportDevices->Names[i], + &pAddrArray[i], + &NbtConfig.pRegistry, + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + (BOOLEAN)(IpAddr == 0), +#endif + &pDeviceContext); + + // + // allow not having an address to succeed - DHCP will + // provide an address later + // + if (pDeviceContext != NULL) + { + if ((status == STATUS_INVALID_ADDRESS) || + (!IpAddr && (status == STATUS_UNSUCCESSFUL))) + { + // + // set to null so we know not to allow connections or dgram + // sends on this adapter + // + pDeviceContext->IpAddress = 0; + + status = STATUS_SUCCESS; + + } + // + // Get an Irp for the out of resource queue (used to disconnect sessions + // when really low on memory) + // + if ( NT_SUCCESS(status) && !NbtConfig.OutOfRsrc.pIrp ) + { + pEntry = NbtConfig.DeviceContexts.Flink; + + ASSERT (pDeviceContext == CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage)); + + NbtConfig.OutOfRsrc.pIrp = NTAllocateNbtIrp(&pDeviceContext->DeviceObject); + + if (!NbtConfig.OutOfRsrc.pIrp) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // + // allocate a dpc structure and keep it: we might need if we hit an + // out-of-resource condition + // + NbtConfig.OutOfRsrc.pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('a')); + if (!NbtConfig.OutOfRsrc.pDpc) + { + IoFreeIrp(NbtConfig.OutOfRsrc.pIrp); + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + if (NT_SUCCESS(status)) + { + NbtConfig.uDevicesStarted++; + pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + + // + // By-pass the TDI PnP mechanism for logical interfaces + // + if (IpAddr) { + status = TdiRegisterDeviceObject( + &pExportDevices->Names[i], + &pDeviceContext->RegistrationHandle); + } + } + // + // Clean up code if device created but we could not use it + // for some reason. + // + if (!NT_SUCCESS(status)) + { + + pDeviceContext->RegistrationHandle = NULL; + + KdPrint((" Create Device Object Failed with status= %X, num devices = %X\n",status, + NbtConfig.uNumDevices)); + + NbtLogEvent(EVENT_NBT_CREATE_DEVICE,status); + // + // this device will not be started so decrement the count of started + // ones. + // + NbtConfig.AdapterCount--; + + pHead = &NbtConfig.DeviceContexts; + pEntry = RemoveTailList(pHead); + + ASSERT (pDeviceContext == CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage)); + + if (pDeviceContext->hNameServer) + { + ObDereferenceObject(pDeviceContext->pNameServerFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hNameServer); + KdPrint(("Close NameSrv File status = %X\n",dontcarestatus)); + } + if (pDeviceContext->hDgram) + { + ObDereferenceObject(pDeviceContext->pDgramFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hDgram); + KdPrint(("Close Dgram File status = %X\n",dontcarestatus)); + } + if (pDeviceContext->hSession) + { + ObDereferenceObject(pDeviceContext->pSessionFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hSession); + KdPrint(("Close Session File status = %X\n",dontcarestatus)); + } + if (pDeviceContext->hControl) + { + ObDereferenceObject(pDeviceContext->pControlFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hControl); + KdPrint(("Close Control File status = %X\n",dontcarestatus)); + } + + IoDeleteDevice((PDEVICE_OBJECT)pDeviceContext); + } + } + + CTEDetachFsp(Attached); + } + break; + } // ( (status == STATUS_SUCCESS) && ( ulIpAddress == IpAddr ) ) + } + } + + + if (NbtConfig.uDevicesStarted == 0) + { + NbtFailedNtPNPInit(); + } + + if (IpAddr) { + if (pBindDevices) + { + CTEMemFree((PVOID)pBindDevices->RegistrySpace); + CTEMemFree((PVOID)pBindDevices); + } + if (pExportDevices) + { + CTEMemFree((PVOID)pExportDevices->RegistrySpace); + CTEMemFree((PVOID)pExportDevices); + } + if (pAddrArray) + { + CTEMemFree((PVOID)pAddrArray); + } + } else { + if (pDeviceContext) { + *Inst = pDeviceContext->InstanceNumber; +#if DBG + pDeviceContext->IsDynamic = TRUE; +#endif + } + } + + return status; +} + +tDEVICECONTEXT * +NbtFindIPAddress( + ULONG IpAddr + ) +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + tDEVICECONTEXT *pDeviceContext; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + if ( pDeviceContext->IpAddress == IpAddr ) + { + return pDeviceContext; + } + } + return (tDEVICECONTEXT *)NULL; +} + +tDEVICECONTEXT * +NbtFindBindName( + PUNICODE_STRING pucBindName + ) +{ + + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + tDEVICECONTEXT *pDeviceContext; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + if ( RtlCompareUnicodeString( + pucBindName, + &pDeviceContext->BindName, + FALSE ) + == 0 ) + { + return pDeviceContext; + } + } + return (tDEVICECONTEXT *)NULL; +} + +VOID +NbtAddressDelete( + ULONG IpAddr + ) +{ + tDEVICECONTEXT *pDeviceContext; + + if ( ( pDeviceContext = NbtFindIPAddress ( IpAddr ) ) != NULL ) + { + (VOID)NbtNewDhcpAddress(pDeviceContext,0,0); + } +} + +HANDLE AddressChangeHandle; + +#ifdef WATCHBIND +HANDLE BindingHandle; +#endif // WATCHBIND + +//* AddressArrival - Handle an IP address arriving +// +// Called by TDI when an address arrives. +// +// Input: Addr - IP address that's coming. +// +// Returns: Nothing. +// +VOID +AddressArrival(PTA_ADDRESS Addr) +{ + ULONG IpAddr; + if (Addr->AddressType == TDI_ADDRESS_TYPE_IP){ + IpAddr = ntohl(((PTDI_ADDRESS_IP)&Addr->Address[0])->in_addr); + IF_DBG(NBT_DEBUG_PNP_POWER){ + KdPrint(("NetBT!AddressArrival: %d.%d.%d.%d\n",(IpAddr>>24)&0xFF,(IpAddr>>16)&0xFF,(IpAddr>>8)&0xFF,IpAddr&0xFF)); + } + if (IpAddr) + { + (VOID)NbtAddressAdd(IpAddr, NULL, NULL, NULL); + } + } +} + +//* AddressDeletion - Handle an IP address going away. +// +// Called by TDI when an address is deleted. If it's an address we +// care about we'll clean up appropriately. +// +// Input: Addr - IP address that's going. +// +// Returns: Nothing. +// +VOID +AddressDeletion(PTA_ADDRESS Addr) +{ + ULONG IpAddr; + if (Addr->AddressType == TDI_ADDRESS_TYPE_IP){ + IpAddr = ntohl(((PTDI_ADDRESS_IP)&Addr->Address[0])->in_addr); + IF_DBG(NBT_DEBUG_PNP_POWER){ + KdPrint(("NetBT!AddressDeletion: %d.%d.%d.%d\n",(IpAddr>>24)&0xFF,(IpAddr>>16)&0xFF,(IpAddr>>8)&0xFF,IpAddr&0xFF)); + } + if (IpAddr) + { + NbtAddressDelete(IpAddr); + } + } +} + +NTSTATUS +NbtAddNewInterface ( + IN PIRP pIrp, + IN PVOID *pBuffer, + IN ULONG Size + ) +/*++ + +Routine Description: + + Creates a device context by coming up with a unique export string to name + the device. + +Arguments: + +Return Value: + +Notes: + + +--*/ +{ + ULONG nextIndex = InterlockedIncrement(&NbtConfig.InterfaceIndex); + WCHAR Suffix[16]; + WCHAR Bind[60] = L"\\Device\\If"; + WCHAR Export[60] = L"\\Device\\NetBt_If"; + UNICODE_STRING ucSuffix; + UNICODE_STRING ucBindStr; + UNICODE_STRING ucExportStr; + NTSTATUS status; + ULONG OutSize; + ULONG Inst=0; + PNETBT_ADD_DEL_IF pAddDelIf = (PNETBT_ADD_DEL_IF)pBuffer; + + // + // Validate output buffer size + // + if (Size < sizeof(NETBT_ADD_DEL_IF)) { + KdPrint(("NbtAddNewInterface: Output buffer too small for struct\n")); + return(STATUS_INVALID_PARAMETER); + } + // + // Create the bind/export strings as: + // Bind: \Device\IF<1> Export: \Device\NetBt_IF<1> + // where 1 is a unique interface index. + // + ucSuffix.Buffer = Suffix; + ucSuffix.Length = 0; + ucSuffix.MaximumLength = sizeof(Suffix); + + RtlIntegerToUnicodeString(nextIndex, 10, &ucSuffix); + + RtlInitUnicodeString(&ucBindStr, Bind); + ucBindStr.MaximumLength = sizeof(Bind); + RtlInitUnicodeString(&ucExportStr, Export); + ucExportStr.MaximumLength = sizeof(Export); + + RtlAppendUnicodeStringToString(&ucBindStr, &ucSuffix); + RtlAppendUnicodeStringToString(&ucExportStr, &ucSuffix); + + OutSize = FIELD_OFFSET (NETBT_ADD_DEL_IF, IfName[0]) + + ucExportStr.Length + sizeof(UNICODE_NULL); + + if (Size < OutSize) { + KdPrint(("NbtAddNewInterface: Buffer too small for name\n")); + pAddDelIf->Length = ucExportStr.Length + sizeof(UNICODE_NULL); + pAddDelIf->Status = STATUS_BUFFER_TOO_SMALL; + pIrp->IoStatus.Information = sizeof(NETBT_ADD_DEL_IF); + return STATUS_SUCCESS; + } + + KdPrint(( + "Created: ucBindStr: %ws ucExportStr: %ws\n", + ucBindStr.Buffer, + ucExportStr.Buffer + )); + + status = NbtAddressAdd(0, &ucBindStr, &ucExportStr, &Inst); + + if (status == STATUS_SUCCESS) { + // + // Fill up the output buffer with the export name + // + RtlCopyMemory( + &pAddDelIf->IfName[0], + ucExportStr.Buffer, + ucExportStr.Length + sizeof(UNICODE_NULL) + ); + + pAddDelIf->InstanceNumber = Inst; + pAddDelIf->Length = ucExportStr.Length + sizeof(UNICODE_NULL); + pAddDelIf->Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = OutSize; + } + + return status; +} + +#ifdef WATCHBIND + +//* BindHandler - Handle a new transport device object. +// +// Called by TDI when a new transport device object is created. +// +// Input: DeviceName - Name of the new device object. +// +// Returns: Nothing. +// + +VOID +BindHandler(IN PUNICODE_STRING DeviceName) +{ +} + +//* UnbindHandler - Handle deletion of a transport device object. +// +// Called by TDI when a transport device object is deleted. +// +// Input: DeviceName - Name of the deleted device object. +// +// Returns: Nothing. +// + +VOID +UnbindHandler(IN PUNICODE_STRING DeviceName) +{ +} + +#endif // WATCHBIND + +#endif // _PNP_POWER diff --git a/private/ntos/nbt/nt/ntutil.c b/private/ntos/nbt/nt/ntutil.c new file mode 100644 index 000000000..4a5f200b8 --- /dev/null +++ b/private/ntos/nbt/nt/ntutil.c @@ -0,0 +1,2103 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Ntutil.c + +Abstract: + + This file contains a number of utility and support routines that are + NT specific. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "stdio.h" +#include <ntddtcp.h> +#undef uint // undef to avoid a warning where tdiinfo.h redefines it +#include <tdiinfo.h> +#include <ipinfo.h> + +NTSTATUS +CreateControlObject( + tNBTCONFIG *pConfig); + +NTSTATUS +IfNotAnyLowerConnections( + IN tDEVICECONTEXT *pDeviceContext + ); +NTSTATUS +NbtProcessDhcpRequest( + tDEVICECONTEXT *pDeviceContext); +VOID +GetExtendedAttributes( + tDEVICECONTEXT *pDeviceContext + ); + +PSTRM_PROCESSOR_LOG LogAlloc ; +PSTRM_PROCESSOR_LOG LogFree ; + + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#ifdef _PNP_POWER +#pragma CTEMakePageable(PAGE, NbtCreateDeviceObject) +#else // _PNP_POWER +#pragma CTEMakePageable(INIT, NbtCreateDeviceObject) +#endif // _PNP_POWER +#pragma CTEMakePageable(PAGE, CreateControlObject) +#pragma CTEMakePageable(PAGE, NbtInitConnQ) +#pragma CTEMakePageable(PAGE, NbtProcessDhcpRequest) +#pragma CTEMakePageable(PAGE, NbtCreateAddressObjects) +#pragma CTEMakePageable(PAGE, GetExtendedAttributes) +#pragma CTEMakePageable(PAGE, ConvertToUlong) +#pragma CTEMakePageable(PAGE, NbtInitMdlQ) +#pragma CTEMakePageable(PAGE, NTZwCloseFile) +#pragma CTEMakePageable(PAGE, NTReReadRegistry) +#pragma CTEMakePageable(PAGE, SaveClientSecurity) +#endif +//******************* Pageable Routine Declarations **************** + +ulong +GetUnique32BitValue( + void + ) + +/*++ + +Routine Description: + + Returns a reasonably unique 32-bit number based on the system clock. + In NT, we take the current system time, convert it to milliseconds, + and return the low 32 bits. + +Arguments: + + None. + +Return Value: + + A reasonably unique 32-bit value. + +--*/ + +{ + LARGE_INTEGER ntTime, tmpTime; + + KeQuerySystemTime(&ntTime); + + tmpTime = CTEConvert100nsToMilliseconds(ntTime); + + return(tmpTime.LowPart); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtCreateDeviceObject( + PDRIVER_OBJECT DriverObject, + tNBTCONFIG *pConfig, + PUNICODE_STRING pucBindName, + PUNICODE_STRING pucExportName, + tADDRARRAY *pAddrs, + PUNICODE_STRING pucRegistryPath, +#ifndef _IO_DELETE_DEVICE_SUPPORTED + BOOLEAN fReuse, +#endif + tDEVICECONTEXT **ppDeviceContext + ) + +/*++ + +Routine Description: + + This routine initializes a Driver Object from the device object passed + in and the name of the driver object passed in. After the Driver Object + has been created, clients can "Open" the driver by that name. + +Arguments: + + +Return Value: + + status - the outcome + +--*/ + +{ + + NTSTATUS status; + PDEVICE_OBJECT DeviceObject = NULL; + tDEVICECONTEXT *pDeviceContext; + ULONG LinkOffset; + ULONG ulIpAddress; + ULONG ulSubnetMask; +#ifdef _PNP_POWER + PUCHAR Buffer; +#endif // _PNP_POWER +#ifndef _IO_DELETE_DEVICE_SUPPORTED + BOOLEAN fIsItReused=FALSE; +#endif + CTELockHandle OldIrq1; + + CTEPagedCode(); + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + // + // If we can re-use some of the earlier devices, so be it.... + // This will go away when base supports IoDeleteDevice properly. + // + if (fReuse) { + LIST_ENTRY *pEntry; + + CTESpinLock(&NbtConfig, OldIrq1); + if (!IsListEmpty(&NbtConfig.FreeDevCtx)) { + pEntry = RemoveHeadList( &NbtConfig.FreeDevCtx); + + DeviceObject = (PDEVICE_OBJECT) CONTAINING_RECORD( pEntry, + tDEVICECONTEXT, + FreeLinkage); + + KdPrint(("Re-using device @ %lx, bind: %ws\n", DeviceObject, ((tDEVICECONTEXT *)DeviceObject)->BindName.Buffer)); + + // + // Re-use the name and update the value returned to the user - we shd decrement + // the global ptr too, but we might hit some other (valid) device the next time on. + // + pucBindName->MaximumLength = ((tDEVICECONTEXT *)DeviceObject)->BindName.MaximumLength; + RtlCopyUnicodeString(pucBindName, &((tDEVICECONTEXT *)DeviceObject)->BindName); + + pucExportName->MaximumLength = ((tDEVICECONTEXT *)DeviceObject)->ExportName.MaximumLength; + RtlCopyUnicodeString(pucExportName, &((tDEVICECONTEXT *)DeviceObject)->ExportName); + CTEMemFree( pDeviceContext->ExportName.Buffer ); + + CTESpinFree(&NbtConfig, OldIrq1); + + fIsItReused = TRUE; +#ifdef _PNP_POWER + Buffer = NbtAllocMem(pucExportName->MaximumLength+pucBindName->MaximumLength,NBT_TAG('w')); + + if ( Buffer == NULL ) + { + return STATUS_INSUFFICIENT_RESOURCES ; + } +#endif // _PNP_POWER + goto Reuse; + } + + fIsItReused = FALSE; + CTESpinFree(&NbtConfig, OldIrq1); + } +#endif + +#ifdef _PNP_POWER + Buffer = NbtAllocMem(pucExportName->MaximumLength+pucBindName->MaximumLength,NBT_TAG('w')); + + if ( Buffer == NULL ) + { + return STATUS_INSUFFICIENT_RESOURCES ; + } +#endif // _PNP_POWER + + + status = IoCreateDevice( + DriverObject, // Driver Object + sizeof(tDEVICECONTEXT) - sizeof(DRIVER_OBJECT), //Device Extension + pucExportName, // Device Name + FILE_DEVICE_NBT, // Device type 0x32 for now... + 0, //Device Characteristics + FALSE, //Exclusive + &DeviceObject ); + + if (!NT_SUCCESS( status )) + { + KdPrint(("Failed to create the Export Device, status=%X\n",status)); + *ppDeviceContext = NULL; +#ifdef _PNP_POWER + CTEMemFree ( Buffer ); +#endif // _PNP_POWER + return status; + } + +#ifndef _IO_DELETE_DEVICE_SUPPORTED +Reuse: +#endif + + *ppDeviceContext = pDeviceContext = (tDEVICECONTEXT *)DeviceObject; + + // + // zero out the data structure, beyond the OS specific part + // + LinkOffset = (ULONG)(&((tDEVICECONTEXT *)0)->Linkage); + CTEZeroMemory(&pDeviceContext->Linkage,sizeof(tDEVICECONTEXT) - LinkOffset); + +#ifdef _PNP_POWER + pDeviceContext->ExportName.MaximumLength = pucExportName->MaximumLength; + pDeviceContext->ExportName.Buffer = (PWSTR)Buffer; + + RtlCopyUnicodeString(&pDeviceContext->ExportName,pucExportName); + + pDeviceContext->BindName.MaximumLength = pucBindName->MaximumLength; + pDeviceContext->BindName.Buffer = (PWSTR)(Buffer+pucExportName->MaximumLength); + RtlCopyUnicodeString(&pDeviceContext->BindName,pucBindName); +#endif // _PNP_POWER + + // initialize the pDeviceContext data structure. There is one of + // these data structured tied to each "device" that NBT exports + // to higher layers (i.e. one for each network adapter that it + // binds to. + // The initialization sets the forward link equal to the back link equal + // to the list head + InitializeListHead(&pDeviceContext->UpConnectionInUse); + InitializeListHead(&pDeviceContext->LowerConnection); + InitializeListHead(&pDeviceContext->LowerConnFreeHead); + + // put a verifier value into the structure so that we can check that + // we are operating on the right data when the OS passes a device context + // to NBT + pDeviceContext->Verify = NBT_VERIFY_DEVCONTEXT; + + // setup the spin lock); + CTEInitLock(&pDeviceContext->SpinLock); + + pDeviceContext->LockNumber = DEVICE_LOCK; + // + // for a Bnode pAddrs is NULL + // + if (pAddrs) + { + pDeviceContext->lNameServerAddress = pAddrs->NameServerAddress; + pDeviceContext->lBackupServer = pAddrs->BackupServer; + // + // if the node type is set to Bnode by default then switch to Hnode if + // there are any WINS servers configured. + // + if ((NodeType & DEFAULT_NODE_TYPE) && + (pAddrs->NameServerAddress || pAddrs->BackupServer)) + { + NodeType = MSNODE | (NodeType & PROXY_NODE); + } + } + + // + // We need to acquire this lock since we can have multiple devices + // being added simultaneously and hence we will need to have a unique + // Adapter Number for each device + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + // keep a bit mask around to keep track of this adapter number so we can + // quickly find if a given name is registered on a particular adapter, + // by a corresponding bit set in the tNAMEADDR - local hash table + // entry + // + pDeviceContext->AdapterNumber = (CTEULONGLONG)1 << NbtConfig.AdapterCount; + NbtConfig.AdapterCount++; + + // add this new device context on to the List in the configuration + // data structure + InsertTailList(&pConfig->DeviceContexts,&pDeviceContext->Linkage); + + if (NbtConfig.AdapterCount > 1) + { + NbtConfig.MultiHomed = TRUE; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // increase the stack size of our device object, over that of the transport + // so that clients create Irps large enough + // to pass on to the transport below. + // In theory, we should just add 1 here, to account for out presence in the + // driver chain. + + status = NbtTdiOpenControl(pDeviceContext); + if (NT_SUCCESS(status)) + { + DeviceObject->StackSize = pDeviceContext->pControlDeviceObject->StackSize + 1; + } + else + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt!NbtTdiOpenControl returned status=%X\n",status)); + return(status); + } + + + // + // An instance number is assigned to each device so that the service which + // creates logical devices in Nbt can re-use these devices in case it fails + // to destroy them in a prev. instance. + // + pDeviceContext->InstanceNumber = GetUnique32BitValue(); + + // + // To create the address objects for this device we need an address for + // TCP port 139 (session services, UDP Port 138 (datagram services) + // and UDP Port 137 (name services). The IP addresses to use for these + // port number must be found by "groveling" the registry..i.e. looking + // under each adapter in the registry for a /parameters/tcpip section + // and then pulling the IP address out of that + // + status = GetIPFromRegistry( + pucRegistryPath, + pucBindName, + &ulIpAddress, + &ulSubnetMask, + FALSE); + +#ifdef _PNP_POWER +#ifdef NOTYET_PNP + if ( status == STATUS_INVALID_ADDRESS ) + { + // + // This one doesn't have a valid static address. Try DHCP. + // + status = GetIPFromRegistry( + pucRegistryPath, + pucBindName, + &ulIpAddress, + &ulSubnetMask, + TRUE); + } +#endif NOTYET_PNP +#endif // _PNP_POWER + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt!GetIPFromRegistry returned status=%X\n",status)); + return(status); + } + +#ifdef _PNP_POWER + + // + // Now, we create all devices up-front (in driverentry); so no need to open the addresses etc. + // + return(status); +#endif + + // get the ip address out of the registry and open the required address + // objects with the underlying transport provider + status = NbtCreateAddressObjects( + ulIpAddress, + ulSubnetMask, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_CREATE_ADDRESS,status); + + KdPrint(("Failed to create the Address Object, status=%X\n",status)); + + return(status); + } + + // + // Add the "permanent" name to the local name table. This is the IP + // address of the node padded out to 16 bytes with zeros. + // + status = NbtAddPermanentName(pDeviceContext); + + // this call must converse with the transport underneath to create + // connections and associate them with the session address object + status = NbtInitConnQ( + &pDeviceContext->LowerConnFreeHead, + sizeof(tLOWERCONNECTION), + NBT_NUM_INITIAL_CONNECTIONS, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + // NEED TO PUT CODE IN HERE TO RELEASE THE DEVICE OBJECT CREATED + // ABOVE AND LOG AN ERROR... + + NbtLogEvent(EVENT_NBT_CREATE_CONNECTION,status); + + KdPrint(("Failed to create the Connection Queue, status=%X\n",status)); + + return(status); + } + + return(STATUS_SUCCESS); +} + +#ifndef _IO_DELETE_DEVICE_SUPPORTED +/******************************************************************* + + NAME: NbtMarkHandlesAsStale + + SYNOPSIS: Marks all open handles on this device as stale + + ENTRY: DeviceContext ptr + + NOTE: Should be called with NbtConfig.JointLock held. + + HISTORY: + SanjayAn 11-Sept.-1996 Created + +********************************************************************/ +VOID +NbtMarkHandlesAsStale ( + IN tDEVICECONTEXT * pDeviceContext + ) +{ + CTELockHandle OldIrq1; + CTELockHandle OldIrq2; + CTELockHandle OldIrq3; + CTELockHandle OldIrq4; + PLIST_ENTRY pEntry; + PLIST_ENTRY pEntry1; + PLIST_ENTRY pEntry2; + PLIST_ENTRY pHead; + PLIST_ENTRY pHead1; + PLIST_ENTRY pHead2; + tADDRESSELE *pAddressEle; + tCONNECTELE *pConnEle; + tCLIENTELE *pClient; + + // 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. + // + pHead = &NbtConfig.AddressHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + + CTESpinLock(pAddressEle,OldIrq2); + + if (pAddressEle->pDeviceContext != pDeviceContext) { + CTESpinFree(pAddressEle,OldIrq2); + pEntry = pEntry->Flink; + continue; + } + + pHead1 = &pAddressEle->ClientHead; + pEntry1 = pHead1->Flink; + while (pEntry1 != pHead1) + { + pClient = CONTAINING_RECORD(pEntry1,tCLIENTELE,Linkage); + pEntry1 = pEntry1->Flink; + + CTESpinLock(pClient,OldIrq3); + + ASSERT(pClient->pDeviceContext == pDeviceContext); + + // + // Mark ClientEle as down so only a close is valid on it. + // + pClient->Verify = NBT_VERIFY_CLIENT_DOWN; + + pHead2 = &pClient->ConnectActive; + pEntry2 = pHead2->Flink; + while (pEntry2 != pHead2) + { + // + // Mark ConnEle as down so only a close is valid on it. + // + pConnEle = CONTAINING_RECORD(pEntry2,tCONNECTELE,Linkage); + + CTESpinLock(pConnEle,OldIrq4); + + ASSERT(pConnEle->pDeviceContext == pDeviceContext); + + pConnEle->Verify = NBT_VERIFY_CONNECTION_DOWN; + + CTESpinFree(pConnEle,OldIrq4); + + pEntry2 = pEntry2->Flink; + } + + pHead2 = &pClient->ConnectActive; + pEntry2 = pHead2->Flink; + while (pEntry2 != pHead2) + { + tLISTENREQUESTS *pListenReq; + + // + // Mark ConnEle as down so only a close is valid on it. + // + pListenReq = CONTAINING_RECORD(pEntry2,tLISTENREQUESTS,Linkage); + pConnEle = (tCONNECTELE *)pListenReq->pConnectEle; + + CTESpinLock(pConnEle,OldIrq4); + + ASSERT(pConnEle->pDeviceContext == pDeviceContext); + + pConnEle->Verify = NBT_VERIFY_CONNECTION_DOWN; + + CTESpinFree(pConnEle,OldIrq4); + + pEntry2 = pEntry2->Flink; + } + CTESpinFree(pClient,OldIrq3); + } + CTESpinFree(pAddressEle,OldIrq2); + pEntry = pEntry->Flink; + } +} +#endif + +/******************************************************************* + + NAME: NbtDestroyDeviceObject + + SYNOPSIS: Destroys the specified device + + ENTRY: pBuffer - name of the device/ device ptr + + HISTORY: + SanjayAn 11-Sept.-1996 Created + +********************************************************************/ + +NTSTATUS +NbtDestroyDeviceObject( +#if 0 + IN PVOID pBuffer +#endif + IN tDEVICECONTEXT * pDeviceContext + ) +{ + LIST_ENTRY * pEntry; + LIST_ENTRY * pHead; + tDEVICECONTEXT * pTmpDeviceContext; + tDEVICECONTEXT * pNextDeviceContext; + tCLIENTELE * pClientEle; + tADDRESSELE * pAddress; + tNAMEADDR * pNameAddr; + tCONNECTELE * pConnEle; + tLOWERCONNECTION * pLowerConn; + tTIMERQENTRY * pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + tDGRAM_SEND_TRACKING * pTracker; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + CTELockHandle OldIrq2; + int i; + tNBTCONFIG *pConfig = &NbtConfig; + WCHAR Buffer[MAX_PATH]; + UNICODE_STRING ucExportName; + PUNICODE_STRING pucExportName; + +#if 0 + tDEVICECONTEXT * pDeviceContext; + + ucExportName.MaximumLength = sizeof(Buffer); + ucExportName.Buffer = Buffer; + pucExportName = &ucExportName; + + RtlInitUnicodeString(pucExportName, &((PNETBT_ADD_DEL_IF)pBuffer)->IfName[0]); + + // + // Find which device is going away + // Also, find out a device object that is still active: we need that info + // to update some of the address ele's. + // + pDeviceContext = NULL; + pNextDeviceContext = NULL; + + for ( pEntry = pConfig->DeviceContexts.Flink; + pEntry != &pConfig->DeviceContexts; + pEntry = pEntry->Flink ) + { + pTmpDeviceContext = CONTAINING_RECORD( pEntry, tDEVICECONTEXT, Linkage); + if ( !RtlCompareUnicodeString ( + &pTmpDeviceContext->ExportName, + pucExportName, + FALSE)) + pDeviceContext = pTmpDeviceContext; + else + pNextDeviceContext = pTmpDeviceContext; + } +#endif + + if (pDeviceContext == NULL) + return STATUS_INVALID_PARAMETER; + + if (pDeviceContext->IpAddress != 0) { + (VOID)NbtNewDhcpAddress(pDeviceContext,0,0); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLock(pDeviceContext,OldIrq1); + + if ( --NbtConfig.AdapterCount == 1) + NbtConfig.MultiHomed = FALSE; + + ASSERT(IsListEmpty(&pDeviceContext->LowerConnFreeHead)); + + // + // walk through all names and see if any is being registered on this + // device context: if so, stop and complete it! + // + for (i=0;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + pEntry = pEntry->Flink; + + if (pNameAddr->NameTypeState & STATE_RESOLVING) + { + pTimer = pNameAddr->pTimer; + + // + // if the name registration was started for this name on this device + // context, stop the timer. (Completion routine will take care of + // doing registration on other device contexts if applicable) + // + if (pTimer) + { + pTracker = pTimer->Context; + ASSERT(pTracker->pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + if (pTracker->pDeviceContext == pDeviceContext) + { + ASSERT(pTracker->pNameAddr == pNameAddr); + + pNameAddr->pTimer = NULL; + + StopTimer(pTimer,&pClientCompletion,&Context); + + if (pClientCompletion) + { + (*pClientCompletion)(Context,STATUS_NETWORK_NAME_DELETED); + } + + KdPrint(("DestroyDeviceObject: stopped name reg timer")) ; + } + } + + } + } + } + + // + // close all the TDI handles + // + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CloseAddressesWithTransport(pDeviceContext); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLock(pDeviceContext,OldIrq1); + + while (!IsListEmpty(&pDeviceContext->LowerConnection)) + { + pEntry = RemoveHeadList(&pDeviceContext->LowerConnection); + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + CTEMemFree( pLowerConn ); + } + + RemoveEntryList( &pDeviceContext->Linkage); + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + // + // IoDeleteDevice on a device with open handles is not supported currently. + // Until the base guys support this feature, we hack Netbt to never call IoDeleteDevice; + // instead we mark it as down and re-use the block on a later open. + // We also mark all open handles as invalid so we fail any request directed at them. + // + CTESpinFree(pDeviceContext,OldIrq1); + NbtMarkHandlesAsStale(pDeviceContext); + CTESpinLock(pDeviceContext,OldIrq1); +#endif + + // + // Walk through the AddressHead list. If any addresses exist and they + // point to old device context, put the next device context. Also, update + // adapter mask to reflect that this device context is now gone. + // + KdPrint(("DestroyDeviceObject: setting AddrEle,NameAddr fields\r\n")); + pHead = pEntry = &NbtConfig.AddressHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pAddress = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + ASSERT (pAddress->Verify == NBT_VERIFY_ADDRESS); + if (pAddress->pDeviceContext == pDeviceContext) + { + if (!IsListEmpty(&pConfig->DeviceContexts)) { + pAddress->pDeviceContext = CONTAINING_RECORD( pConfig->DeviceContexts.Flink, tDEVICECONTEXT, Linkage); + } else { + pAddress->pDeviceContext = NULL; + } + } + + // + // Release the name on this adapter; but dont release on other adapters + // + // 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 (!(pNameAddr->NameTypeState & (STATE_CONFLICT | NAMETYPE_QUICK)) && + (pAddress->pNameAddr->Name[0] != '*') && + (pNameAddr->AdapterMask & pDeviceContext->AdapterNumber)) + { + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + (VOID)ReleaseNameOnNet(pAddress->pNameAddr, + NbtConfig.pScope, + pAddress, + NameReleaseDoneOnDynIf, // name released on dynamic if + NodeType, + pDeviceContext); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLock(pDeviceContext,OldIrq1); + } + } + + // + // Mark in the device extension that this is not a valid device. Default is FALSE... + // + InterlockedIncrement(&pDeviceContext->IsDestroyed); + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + // + // Chain the device on the free list + // + ExInterlockedInsertTailList(&NbtConfig.FreeDevCtx, + &pDeviceContext->FreeLinkage, + &NbtConfig.SpinLock); + + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); +#else + + CTEMemFree( pDeviceContext->ExportName.Buffer ); + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoDeleteDevice((PDEVICE_OBJECT)pDeviceContext); + +#endif + + KdPrint(("DestroyDeviceObject: deleted @ %lx\n", pDeviceContext)); + + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------- +NTSTATUS +CreateControlObject( + tNBTCONFIG *pConfig) + +/*++ + +Routine Description: + + This routine allocates memory for the provider info block, tacks it + onto the global configuration and sets default values for each item. + +Arguments: + + +Return Value: + + + NTSTATUS + +--*/ + +{ + tCONTROLOBJECT *pControl; + + + CTEPagedCode(); + pControl = (tCONTROLOBJECT *)ExAllocatePool( + NonPagedPool, + sizeof(tCONTROLOBJECT)); + if (!pControl) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pControl->Verify = NBT_VERIFY_CONTROL; + + // setup the spin lock); + CTEInitLock(&pControl->SpinLock); + + pControl->ProviderInfo.Version = 1; + pControl->ProviderInfo.MaxSendSize = 0; + pControl->ProviderInfo.MaxConnectionUserData = 0; + + // we need to get these values from the transport underneath...*TODO* + // since the RDR uses this value + pControl->ProviderInfo.MaxDatagramSize = 0; + + pControl->ProviderInfo.ServiceFlags = 0; +/* pControl->ProviderInfo.TransmittedTsdus = 0; + pControl->ProviderInfo.ReceivedTsdus = 0; + pControl->ProviderInfo.TransmissionErrors = 0; + pControl->ProviderInfo.ReceiveErrors = 0; +*/ + pControl->ProviderInfo.MinimumLookaheadData = 0; + pControl->ProviderInfo.MaximumLookaheadData = 0; +/* pControl->ProviderInfo.DiscardedFrames = 0; + pControl->ProviderInfo.OversizeTsdusReceived = 0; + pControl->ProviderInfo.UndersizeTsdusReceived = 0; + pControl->ProviderInfo.MulticastTsdusReceived = 0; + pControl->ProviderInfo.BroadcastTsdusReceived = 0; + pControl->ProviderInfo.MulticastTsdusTransmitted = 0; + pControl->ProviderInfo.BroadcastTsdusTransmitted = 0; + pControl->ProviderInfo.SendTimeouts = 0; + pControl->ProviderInfo.ReceiveTimeouts = 0; + pControl->ProviderInfo.ConnectionIndicationsReceived = 0; + pControl->ProviderInfo.ConnectionIndicationsAccepted = 0; + pControl->ProviderInfo.ConnectionsInitiated = 0; + pControl->ProviderInfo.ConnectionsAccepted = 0; +*/ + // put a ptr to this info into the pConfig so we can locate it + // when we want to cleanup + pConfig->pControlObj = pControl; + + /* KEEP THIS STUFF HERE SINCE WE MAY NEED TO ALSO CREATE PROVIDER STATS!! + *TODO* + DeviceList[i].ProviderStats.Version = 2; + DeviceList[i].ProviderStats.OpenConnections = 0; + DeviceList[i].ProviderStats.ConnectionsAfterNoRetry = 0; + DeviceList[i].ProviderStats.ConnectionsAfterRetry = 0; + DeviceList[i].ProviderStats.LocalDisconnects = 0; + DeviceList[i].ProviderStats.RemoteDisconnects = 0; + DeviceList[i].ProviderStats.LinkFailures = 0; + DeviceList[i].ProviderStats.AdapterFailures = 0; + DeviceList[i].ProviderStats.SessionTimeouts = 0; + DeviceList[i].ProviderStats.CancelledConnections = 0; + DeviceList[i].ProviderStats.RemoteResourceFailures = 0; + DeviceList[i].ProviderStats.LocalResourceFailures = 0; + DeviceList[i].ProviderStats.NotFoundFailures = 0; + DeviceList[i].ProviderStats.NoListenFailures = 0; + + DeviceList[i].ProviderStats.DatagramsSent = 0; + DeviceList[i].ProviderStats.DatagramBytesSent.HighPart = 0; + DeviceList[i].ProviderStats.DatagramBytesSent.LowPart = 0; + + DeviceList[i].ProviderStats.DatagramsReceived = 0; + DeviceList[i].ProviderStats.DatagramBytesReceived.HighPart = 0; + DeviceList[i].ProviderStats.DatagramBytesReceived.LowPart = 0; + + DeviceList[i].ProviderStats.PacketsSent = 0; + DeviceList[i].ProviderStats.PacketsReceived = 0; + + DeviceList[i].ProviderStats.DataFramesSent = 0; + DeviceList[i].ProviderStats.DataFrameBytesSent.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesSent.LowPart = 0; + + DeviceList[i].ProviderStats.DataFramesReceived = 0; + DeviceList[i].ProviderStats.DataFrameBytesReceived.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesReceived.LowPart = 0; + + DeviceList[i].ProviderStats.DataFramesResent = 0; + DeviceList[i].ProviderStats.DataFrameBytesResent.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesResent.LowPart = 0; + + DeviceList[i].ProviderStats.DataFramesRejected = 0; + DeviceList[i].ProviderStats.DataFrameBytesRejected.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesRejected.LowPart = 0; + + DeviceList[i].ProviderStats.ResponseTimerExpirations = 0; + DeviceList[i].ProviderStats.AckTimerExpirations = 0; + DeviceList[i].ProviderStats.MaximumSendWindow = 0; + DeviceList[i].ProviderStats.AverageSendWindow = 0; + DeviceList[i].ProviderStats.PiggybackAckQueued = 0; + DeviceList[i].ProviderStats.PiggybackAckTimeouts = 0; + + DeviceList[i].ProviderStats.WastedPacketSpace.HighPart = 0; + DeviceList[i].ProviderStats.WastedPacketSpace.LowPart = 0; + DeviceList[i].ProviderStats.WastedSpacePackets = 0; + DeviceList[i].ProviderStats.NumberOfResources = 0; + */ + return(STATUS_SUCCESS); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +IfNotAnyLowerConnections( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine checks each device context to see if there are any open + connections, and returns SUCCESS if there are. If the DoDisable flag + is set the list head of free lower connections is returned and the + list in the Nbtconfig structure is made empty. + +Arguments: + +Return Value: + + none + +--*/ + +{ + CTELockHandle OldIrq; + + CTESpinLock(pDeviceContext,OldIrq); + if (!IsListEmpty(&pDeviceContext->LowerConnection)) + { + CTESpinFree(pDeviceContext,OldIrq); + return(STATUS_UNSUCCESSFUL); + } + CTESpinFree(pDeviceContext,OldIrq); + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +CloseAddressesWithTransport( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine checks each device context to see if there are any open + connections, and returns SUCCESS if there are. + +Arguments: + +Return Value: + + none + +--*/ + +{ + BOOLEAN Attached; + CTELockHandle OldIrq; + PFILE_OBJECT pNSFileObject, pSFileObject, pDGFileObject; + + CTEAttachFsp(&Attached); + + // + // Check for the existence of Objects under SpinLock and + // then Close them outside of the SpinLock + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pNSFileObject = pDeviceContext->pNameServerFileObject) + { + pDeviceContext->pNameServerFileObject = NULL; + } + if (pSFileObject = pDeviceContext->pSessionFileObject) + { + pDeviceContext->pSessionFileObject = NULL; + } + if (pDGFileObject = pDeviceContext->pDgramFileObject) + { + pDeviceContext->pDgramFileObject = NULL; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Now close all the necessary objects as appropriate + // + if (pNSFileObject) + { + ObDereferenceObject((PVOID *)pNSFileObject); + ZwClose(pDeviceContext->hNameServer); + } + if (pSFileObject) + { + ObDereferenceObject((PVOID *)pSFileObject); + ZwClose(pDeviceContext->hSession); + } + if (pDGFileObject) + { + ObDereferenceObject((PVOID *)pDGFileObject); + ZwClose(pDeviceContext->hDgram); + } + + CTEDetachFsp(Attached); + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtCreateAddressObjects( + IN ULONG IpAddress, + IN ULONG SubnetMask, + OUT tDEVICECONTEXT *pDeviceContext) + +/*++ + +Routine Description: + + This routine gets the ip address and subnet mask out of the registry + to calcuate the broadcast address. It then creates the address objects + with the transport. + +Arguments: + + pucRegistryPath - path to NBT config info in registry + pucBindName - name of the service to bind to. + pDeviceContext - ptr to the device context... place to store IP addr + and Broadcast address permanently + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + ULONG ValueMask; + UCHAR IpAddrByte; + + CTEPagedCode(); + // + // to get the broadcast address combine the IP address with the subnet mask + // to yield a value with 1's in the "local" portion and the IP address + // in the network portion + // + ValueMask = (SubnetMask & IpAddress) | (~SubnetMask & -1); + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Broadcastaddress = %X\n",ValueMask)); + + // + // the registry can be configured to set the subnet broadcast address to + // -1 rather than use the actual subnet broadcast address. This code + // checks for that and sets the broadcast address accordingly. + // + if (!NbtConfig.UseRegistryBcastAddr) + { + pDeviceContext->BroadcastAddress = ValueMask; + } + else + { + pDeviceContext->BroadcastAddress = NbtConfig.RegistryBcastAddr; + } + + pDeviceContext->IpAddress = IpAddress; + + pDeviceContext->SubnetMask = SubnetMask; + // + // get the network number by checking the top bits in the ip address, + // looking for 0 or 10 or 110 or 1110 + // + IpAddrByte = ((PUCHAR)&IpAddress)[3]; + if ((IpAddrByte & 0x80) == 0) + { + // class A address - one byte netid + IpAddress &= 0xFF000000; + } + else + if ((IpAddrByte & 0xC0) ==0x80) + { + // class B address - two byte netid + IpAddress &= 0xFFFF0000; + } + else + if ((IpAddrByte & 0xE0) ==0xC0) + { + // class C address - three byte netid + IpAddress &= 0xFFFFFF00; + } + + + pDeviceContext->NetMask = IpAddress; + + + // now create the address objects. + + // open the Ip Address for inbound Datagrams. + status = NbtTdiOpenAddress( + &pDeviceContext->hDgram, + &pDeviceContext->pDgramDeviceObject, + &pDeviceContext->pDgramFileObject, + pDeviceContext, + (USHORT)NBT_DATAGRAM_UDP_PORT, + pDeviceContext->IpAddress, + 0); // not a TCP port + + if (NT_SUCCESS(status)) + { + // open the Nameservice UDP port .. + status = NbtTdiOpenAddress( + &pDeviceContext->hNameServer, + &pDeviceContext->pNameServerDeviceObject, + &pDeviceContext->pNameServerFileObject, + pDeviceContext, + (USHORT)NBT_NAMESERVICE_UDP_PORT, + pDeviceContext->IpAddress, + 0); // not a TCP port + + if (NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt: Open Session port %X\n",pDeviceContext)); + + // Open the TCP port for Session Services + status = NbtTdiOpenAddress( + &pDeviceContext->hSession, + &pDeviceContext->pSessionDeviceObject, + &pDeviceContext->pSessionFileObject, + pDeviceContext, + (USHORT)NBT_SESSION_TCP_PORT, + pDeviceContext->IpAddress, + TCP_FLAG | SESSION_FLAG); // TCP port + + if (NT_SUCCESS(status)) + { + // + // This will get the MAC address for a RAS connection + // which is zero until there really is a connection to + // the RAS server + // + GetExtendedAttributes(pDeviceContext); + return(status); + } + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to Open Session address with TDI, status = %X\n",status)); + + // + // Ensure that the Object pointers are NULLed out! + // + pDeviceContext->pSessionFileObject = NULL; + + ObDereferenceObject(pDeviceContext->pNameServerFileObject); + pDeviceContext->pNameServerFileObject = NULL; + NTZwCloseFile(pDeviceContext->hNameServer); + + } + ObDereferenceObject(pDeviceContext->pDgramFileObject); + pDeviceContext->pDgramFileObject = NULL; + NTZwCloseFile(pDeviceContext->hDgram); + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to Open NameServer port with TDI, status = %X\n",status)); + } + + return(status); +} + +//---------------------------------------------------------------------------- +VOID +GetExtendedAttributes( + tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine converts a unicode dotted decimal to a ULONG + +Arguments: + + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + TCP_REQUEST_QUERY_INFORMATION_EX QueryReq; + UCHAR pBuffer[256]; + IO_STATUS_BLOCK IoStatus; + ULONG BufferSize = 256; + HANDLE event; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PWSTR pName=L"Tcp"; + PFILE_FULL_EA_INFORMATION EaBuffer; + UNICODE_STRING DeviceName; + BOOLEAN Attached = FALSE; + HANDLE hTcp; + + CTEPagedCode(); + + // + // Open a control channel to TCP for this IOCTL. + // + // NOTE: We cannot use the hControl in the DeviceContext since that was created in the context + // of the system process (address arrival from TCP/IP). Here, we are in the context of the service + // process (Ioctl down from DHCP) and so we need to open another control channel. + // + // NOTE: We still need to maintain the earlier call to create a control channel since that is + // used to submit TDI requests down to TCP/IP. + // + + // copy device name into the unicode string + Status = CreateDeviceString(pName,&DeviceName); + if (!NT_SUCCESS(Status)) + { + return; + } + InitializeObjectAttributes ( + &ObjectAttributes, + &DeviceName, + 0, + NULL, + NULL); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("tcp device to open = %ws\n",DeviceName.Buffer)); + + EaBuffer = NULL; + + Status = ZwCreateFile ( + &hTcp, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + NULL, // block size (unused). + FILE_ATTRIBUTE_NORMAL, // file attributes. + 0, + FILE_CREATE, + 0, // create options. + (PVOID)EaBuffer, // EA buffer. + 0); // Ea length + + + CTEMemFree(DeviceName.Buffer); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint( ("OpenControl CreateFile Status:%X, IoStatus:%X\n", Status, IoStatusBlock.Status)); + + if ( NT_SUCCESS( Status )) + { + // + // Initialize the TDI information buffers. + // + // + // pass in the ipaddress as the first ULONG of the context array + // + *(ULONG *)QueryReq.Context = htonl(pDeviceContext->IpAddress); + + QueryReq.ID.toi_entity.tei_entity = CL_NL_ENTITY; + QueryReq.ID.toi_entity.tei_instance = 0; + QueryReq.ID.toi_class = INFO_CLASS_PROTOCOL; + QueryReq.ID.toi_type = INFO_TYPE_PROVIDER; + QueryReq.ID.toi_id = IP_INTFC_INFO_ID; + + status = ZwCreateEvent( + &event, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE + ); + + if ( !NT_SUCCESS(status) ) + { + return; + + } + + // + // Make the actual TDI call + // + + status = ZwDeviceIoControlFile( + hTcp, + event, + NULL, + NULL, + &IoStatus, + IOCTL_TCP_QUERY_INFORMATION_EX, + &QueryReq, + sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), + pBuffer, + BufferSize + ); + + // + // If the call pended and we were supposed to wait for completion, + // then wait. + // + + if ( status == STATUS_PENDING ) + { + status = KeWaitForSingleObject( event, Executive, KernelMode, FALSE, NULL ); + + ASSERT( NT_SUCCESS(status) ); + } + + if ( NT_SUCCESS(status) ) + { + ULONG Length; + + pDeviceContext->PointToPoint = ((((IPInterfaceInfo *)pBuffer)->iii_flags & IP_INTFC_FLAG_P2P) != 0); + + // + // get the length of the mac address in case is is less than + // 6 bytes + // + Length = (((IPInterfaceInfo *)pBuffer)->iii_addrlength < sizeof(tMAC_ADDRESS)) + ? ((IPInterfaceInfo *)pBuffer)->iii_addrlength : sizeof(tMAC_ADDRESS); + + CTEZeroMemory(pDeviceContext->MacAddress.Address,sizeof(tMAC_ADDRESS)); + CTEMemCopy(&pDeviceContext->MacAddress.Address[0], + ((IPInterfaceInfo *)pBuffer)->iii_addr, + Length); + + } + + status = ZwClose( event ); + ASSERT( NT_SUCCESS(status) ); + + status = IoStatus.Status; + + // + // Close the handle to TCP since we dont need it anymore; all TDI requests go thru the + // Control handle in the DeviceContext. + // + status = ZwClose( hTcp ); + ASSERT( NT_SUCCESS(status) ); + } + else + { + KdPrint(("Nbt:Failed to Open the control connection to the transport, status1 = %X\n", + Status)); + + } + + return; +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ConvertToUlong( + IN PUNICODE_STRING pucAddress, + OUT ULONG *pulValue) + +/*++ + +Routine Description: + + This routine converts a unicode dotted decimal to a ULONG + +Arguments: + + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + OEM_STRING OemAddress; + + // create integer from unicode string + + CTEPagedCode(); + status = RtlUnicodeStringToAnsiString(&OemAddress, pucAddress, TRUE); + if (!NT_SUCCESS(status)) + { + return(status); + } + + status = ConvertDottedDecimalToUlong(OemAddress.Buffer,pulValue); + + RtlFreeAnsiString(&OemAddress); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("ERR: Bad Dotted Decimal Ip Address(must be <=255 with 4 dots) = %ws\n", + pucAddress->Buffer)); + + return(status); + } + + return(STATUS_SUCCESS); + + +} + + + +//---------------------------------------------------------------------------- +VOID +NbtGetMdl( + PMDL *ppMdl, + enum eBUFFER_TYPES eBuffType) + +/*++ + +Routine Description: + + This routine allocates an Mdl. + +Arguments: + + ppListHead - a ptr to a ptr to the list head to add buffer to + iNumBuffers - the number of buffers to add to the queue + +Return Value: + + none + +--*/ + +{ + PMDL pMdl; + ULONG lBufferSize; + PVOID pBuffer; + + if (NbtConfig.iCurrentNumBuff[eBuffType] + >= NbtConfig.iMaxNumBuff[eBuffType]) + { + *ppMdl = NULL; + return; + } + + lBufferSize = NbtConfig.iBufferSize[eBuffType]; + + pBuffer = NbtAllocMem((USHORT)lBufferSize,NBT_TAG('g')); + + if (!pBuffer) + { + *ppMdl = NULL; + return; + } + + // allocate a MDL to hold the session hdr + pMdl = IoAllocateMdl( + (PVOID)pBuffer, + lBufferSize, + FALSE, // want this to be a Primary buffer - the first in the chain + FALSE, + NULL); + + *ppMdl = pMdl; + + if (!pMdl) + { + CTEMemFree(pBuffer); + return; + } + + // fill in part of the session hdr since it is always the same + if (eBuffType == eNBT_FREE_SESSION_MDLS) + { + ((tSESSIONHDR *)pBuffer)->Flags = NBT_SESSION_FLAGS; + ((tSESSIONHDR *)pBuffer)->Type = NBT_SESSION_MESSAGE; + } + else + if (eBuffType == eNBT_DGRAM_MDLS) + { + ((tDGRAMHDR *)pBuffer)->Flags = FIRST_DGRAM | (NbtConfig.PduNodeType >> 10); + ((tDGRAMHDR *)pBuffer)->PckOffset = 0; // not fragmented + + } + + // map the Mdl properly to fill in the pages portion of the MDL + MmBuildMdlForNonPagedPool(pMdl); + + NbtConfig.iCurrentNumBuff[eBuffType]++; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtInitMdlQ( + PSINGLE_LIST_ENTRY pListHead, + enum eBUFFER_TYPES eBuffType) + +/*++ + +Routine Description: + + This routine allocates Mdls for use later. + +Arguments: + + ppListHead - a ptr to a ptr to the list head to add buffer to + iNumBuffers - the number of buffers to add to the queue + +Return Value: + + none + +--*/ + +{ + int i; + PMDL pMdl; + + + CTEPagedCode(); + // Initialize the list head, so the last element always points to NULL + pListHead->Next = NULL; + + // create a small number first and then lis the list grow with time + for (i=0;i < NBT_INITIAL_NUM ;i++ ) + { + + NbtGetMdl(&pMdl,eBuffType); + if (!pMdl) + { + KdPrint(("NBT:Unable to allocate MDL at initialization time!!\n"));\ + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // put on free list + PushEntryList(pListHead,(PSINGLE_LIST_ENTRY)pMdl); + + } + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTZwCloseFile( + IN HANDLE Handle + ) + +/*++ +Routine Description: + + This Routine handles closing a handle with NT within the context of NBT's + file system process. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + BOOLEAN Attached = FALSE; + + CTEPagedCode(); + // + // Attach to NBT's FSP (file system process) to free the handle since + // the handle is only valid in that process. + // + if (PsGetCurrentProcess() != NbtFspProcess) + { + KeAttachProcess(&NbtFspProcess->Pcb); + Attached = TRUE; + } + + status = ZwClose(Handle); + + if (Attached) + { + // + // go back to the original process + // + KeDetachProcess(); + } + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTReReadRegistry( + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine re-reads the registry values when DHCP issues the Ioctl + to do so. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + tADDRARRAY *pAddrArray=NULL; + tADDRARRAY *pAddr; + tDEVICES *pBindDevices=NULL; + tDEVICES *pExportDevices=NULL; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDEVICECONTEXT *pDevContext; + + CTEPagedCode(); + + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + // + // BUGBUG [WishList]: We look at the whole registry in NbtAddressAdd too. + // + status = NbtReadRegistry( + &NbtConfig.pRegistry, + NULL, // Null Driver Object + &NbtConfig, + &pBindDevices, + &pExportDevices, + &pAddrArray); + + if (pAddrArray) + { + ULONG i; +#if DBG + { + BOOLEAN fFound=FALSE; + + // + // Loop thru the devicecontexts in the Config struct to ensure that this DeviceContext + // is actually valid. + // + // BUGBUG[WishList]:Would be good to have signatures in the structures. + // + pAddr = pAddrArray; + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + if (pDevContext == pDeviceContext) + { + fFound = TRUE; + break; + } + } + + ASSERT(fFound == TRUE); + } +#endif + + // + // Figure out the Address entry by matching the BindDevice names against the + // name in the DeviceContext passed in. + // + for (i=0; i<NbtConfig.uNumDevices; i++) { + + if (RtlCompareUnicodeString(&pDeviceContext->BindName, + &pBindDevices->Names[i], + FALSE) == 0) { + // + // We found a match + // + pDeviceContext->lNameServerAddress = pAddrArray[i].NameServerAddress; + pDeviceContext->lBackupServer = pAddrArray[i].BackupServer; + + // + // if the node type is set to Bnode by default then switch to Hnode if + // there are any WINS servers configured. + // + if ((NodeType & DEFAULT_NODE_TYPE) && + (pAddrArray[i].NameServerAddress || pAddrArray[i].BackupServer)) + { + NodeType = MSNODE | (NodeType & PROXY); + } + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBT:Found BindName: %lx, AddrArray: %lx, i: %lx\n", pBindDevices, pAddrArray, i)); + + break; + } + } + +#if DBG + if (i == NbtConfig.uNumDevices) { + KdPrint(("Nbt:Unable to find the entry corresp. to device %lx in the registry. BindDevices: %lx\n", + pDeviceContext, pBindDevices)); + DbgBreakPoint(); + } +#endif + } + + // + // Free Allocated memory + // + if (pBindDevices) + { + CTEMemFree(pBindDevices->RegistrySpace); + CTEMemFree((PVOID)pBindDevices); + } + if (pExportDevices) + { + CTEMemFree(pExportDevices->RegistrySpace); + CTEMemFree((PVOID)pExportDevices); + } + if (pAddrArray) + { + CTEMemFree((PVOID)pAddrArray); + } + + CTEExReleaseResource(&NbtConfig.Resource); + + if (pDeviceContext->IpAddress) + { + // + // Add the "permanent" name to the local name table. This is the IP + // address of the node padded out to 16 bytes with zeros. + // + status = NbtAddPermanentName(pDeviceContext); + + if (!(NodeType & BNODE)) + { + // Probably the Ip address just changed and Dhcp is informing us + // of a new Wins Server addresses, so refresh all the names to the + // new wins server + // + ReRegisterLocalNames(); + } + else + { + // + // no need to refresh + // on a Bnode + // + LockedStopTimer(&NbtConfig.pRefreshTimer); + } + } + + return(STATUS_SUCCESS); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtLogEvent( + IN ULONG EventCode, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This function allocates an I/O error log record, fills it in and writes it + to the I/O error log. + + +Arguments: + + EventCode - Identifies the error message. + Status - The status value to log: this value is put into the + data portion of the log message. + + +Return Value: + + STATUS_SUCCESS - The error was successfully logged.. + STATUS_BUFER_OVERFLOW - The error data was too large to be logged. + STATUS_INSUFFICIENT_RESOURCES - Unable to allocate memory. + + +--*/ + +{ + PIO_ERROR_LOG_PACKET ErrorLogEntry; + PVOID LoggingObject; + + LoggingObject = NbtConfig.DriverObject; + + ErrorLogEntry = IoAllocateErrorLogEntry(LoggingObject,sizeof(IO_ERROR_LOG_PACKET)); + + if (ErrorLogEntry == NULL) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Unalbe to allocate Error Packet for Error logging\n")); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Fill in the necessary log packet fields. + // + ErrorLogEntry->UniqueErrorValue = 0; + ErrorLogEntry->ErrorCode = EventCode; + ErrorLogEntry->NumberOfStrings = 0; + ErrorLogEntry->StringOffset = 0; + ErrorLogEntry->DumpDataSize = (USHORT)sizeof(ULONG); + ErrorLogEntry->DumpData[0] = Status; + + IoWriteErrorLogEntry(ErrorLogEntry); + + return(STATUS_SUCCESS); +} +#ifdef DBGMEMNBT +VOID +PadEntry( + char *EntryPtr + ) +{ + char *Limit; + + // + // pad remainder of entry + // + Limit = EntryPtr + LOGWIDTH - 1; + ASSERT(LOGWIDTH >= (strlen(EntryPtr) + 1)); + for (EntryPtr += strlen(EntryPtr); + EntryPtr != Limit; + EntryPtr++ + ) { + *EntryPtr = ' '; + } + *EntryPtr = '\0'; +} +//---------------------------------------------------------------------------- +PVOID +CTEAllocMemDebug( + IN ULONG Size, + IN PVOID pBuffer, + IN UCHAR *File, + IN ULONG Line + ) + +/*++ + +Routine Description: + + This function logs getting and freeing memory. + +Arguments: + + +Return Value: + + +--*/ + +{ + CCHAR CurrProc; + UCHAR LockFree; + UCHAR *EntryPtr; + char *Limit; + PUCHAR pFile; + PVOID pMem; + PSTRM_PROCESSOR_LOG Log ; + + + if (!pBuffer) + { + if (!LogAlloc) + { + LogAlloc = ExAllocatePool(NonPagedPool,sizeof(STRM_PROCESSOR_LOG)); + LogAlloc->Index = 0; + } + Log = LogAlloc; + pMem = ExAllocatePool(NonPagedPool,Size); + } + else + { + if (!LogFree) + { + LogFree = ExAllocatePool(NonPagedPool,sizeof(STRM_PROCESSOR_LOG)); + LogFree->Index = 0; + } + Log = LogFree; + pMem = pBuffer; + ExFreePool(pBuffer); + } + + EntryPtr = Log->Log[Log->Index]; + + pFile = strrchr(File,'\\'); + + sprintf(EntryPtr,"%s %d %X",pFile, Line,pMem); + + PadEntry(EntryPtr); + + if (++(Log->Index) >= LOGSIZE) + { + Log->Index = 0; + } + // + // Mark next entry so we know where the log for this processor ends + // + EntryPtr = Log->Log[Log->Index]; + sprintf(EntryPtr, "*** Last Entry"); + + return(pMem); + +} +#endif + +#if DBG +//---------------------------------------------------------------------------- +VOID +AcquireSpinLockDebug( + IN PKSPIN_LOCK pSpinLock, + IN PKIRQL pOldIrq, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function gets the spin lock, and then sets the mask in Nbtconfig, per + processor. + + +Arguments: + + +Return Value: + + +--*/ + +{ + CCHAR CurrProc; + UCHAR LockFree; + + CTEGetLock(pSpinLock,pOldIrq); + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + NbtConfig.CurrProc = CurrProc; + + LockFree = (LockNumber > (UCHAR)NbtConfig.CurrentLockNumber[CurrProc]); + if (!LockFree) + { + KdPrint(("CurrProc = %X, CurrentLockNum = %X DataSTructLock = %X\n", + CurrProc,NbtConfig.CurrentLockNumber[CurrProc],LockNumber)); + } \ + + ASSERTMSG("Possible DeadLock, Getting SpinLock at a lower level\n",LockFree); + NbtConfig.CurrentLockNumber[CurrProc]|= LockNumber; + +} + +//---------------------------------------------------------------------------- +VOID +FreeSpinLockDebug( + IN PKSPIN_LOCK pSpinLock, + IN KIRQL OldIrq, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function clears the spin lock from the mask in Nbtconfig, per + processor and then releases the spin lock. + + +Arguments: + + +Return Value: + none + +--*/ + +{ + CCHAR CurrProc; + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + + NbtConfig.CurrentLockNumber[CurrProc] &= ~LockNumber; + CTEFreeLock(pSpinLock,OldIrq); + +} +//---------------------------------------------------------------------------- +VOID +AcquireSpinLockAtDpcDebug( + IN PKSPIN_LOCK pSpinLock, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function gets the spin lock, and then sets the mask in Nbtconfig, per + processor. + + +Arguments: + + +Return Value: + + +--*/ + +{ + CCHAR CurrProc; + UCHAR LockFree; + + CTEGetLockAtDPC(pSpinLock, 0); + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + NbtConfig.CurrProc = CurrProc; + + LockFree = (LockNumber > (UCHAR)NbtConfig.CurrentLockNumber[CurrProc]); + if (!LockFree) + { + KdPrint(("CurrProc = %X, CurrentLockNum = %X DataSTructLock = %X\n", + CurrProc,NbtConfig.CurrentLockNumber[CurrProc],LockNumber)); + } \ + + ASSERTMSG("Possible DeadLock, Getting SpinLock at a lower level\n",LockFree); + NbtConfig.CurrentLockNumber[CurrProc]|= LockNumber; + +} + +//---------------------------------------------------------------------------- +VOID +FreeSpinLockAtDpcDebug( + IN PKSPIN_LOCK pSpinLock, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function clears the spin lock from the mask in Nbtconfig, per + processor and then releases the spin lock. + + +Arguments: + + +Return Value: + none + +--*/ + +{ + CCHAR CurrProc; + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + + NbtConfig.CurrentLockNumber[CurrProc] &= ~LockNumber; + CTEFreeLockFromDPC(pSpinLock, 0); + +} +#endif //if Dbg + diff --git a/private/ntos/nbt/nt/registry.c b/private/ntos/nbt/nt/registry.c new file mode 100644 index 000000000..6bab53a34 --- /dev/null +++ b/private/ntos/nbt/nt/registry.c @@ -0,0 +1,1811 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + Registry.c + +Abstract: + + This contains all routines necessary to load device pathnames from the + registry. + +Author: + + Jim Stewart (Jimst) October 9 1992 + +Revision History: + + +Notes: + +--*/ + +#include "nbtprocs.h" +//#include <stdlib.h> + + +// +// Local functions used to access the registry. +// + +NTSTATUS +NbtOpenRegistry( + IN HANDLE NbConfigHandle, + IN PWSTR String, + OUT PHANDLE pHandle + ); + +VOID +NbtCloseRegistry( + IN HANDLE LinkageHandle, + IN HANDLE ParametersHandle + ); + +NTSTATUS +NbtReadLinkageInformation( + IN PWSTR pName, + IN HANDLE LinkageHandle, + OUT tDEVICES *pDevices, // place to put read in config data + OUT LONG *piNumDevice + ); + +NTSTATUS +OpenAndReadElement( + IN PUNICODE_STRING pucRootPath, + IN PWSTR pwsValueName, + OUT PUNICODE_STRING pucString + ); + +NTSTATUS +GetServerAddress ( + IN HANDLE ParametersHandle, + IN PWSTR KeyName, + OUT PULONG pIpAddr + ); + +NTSTATUS +NbtAppendString ( + IN PWSTR FirstString, + IN PWSTR SecondString, + OUT PUNICODE_STRING pucString + ); + +NTSTATUS +ReadStringRelative( + IN PUNICODE_STRING pRegistryPath, + IN PWSTR pRelativePath, + IN PWSTR pValueName, + OUT PUNICODE_STRING pOutString + ); + +VOID +NbtFindLastSlash( + IN PUNICODE_STRING pucRegistryPath, + OUT PWSTR *ppucLastElement, + IN int *piLength + ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtReadRegistry) +#pragma CTEMakePageable(PAGE, ReadNameServerAddresses) +#pragma CTEMakePageable(PAGE, GetServerAddress) +#pragma CTEMakePageable(PAGE, NTReadIniString) +#pragma CTEMakePageable(PAGE, GetIPFromRegistry) +#pragma CTEMakePageable(PAGE, NbtOpenRegistry) +#pragma CTEMakePageable(PAGE, NbtReadLinkageInformation) +#pragma CTEMakePageable(PAGE, NbtReadSingleParameter) +#pragma CTEMakePageable(PAGE, OpenAndReadElement) +#pragma CTEMakePageable(PAGE, ReadElement) +#pragma CTEMakePageable(PAGE, NTGetLmHostPath) +#pragma CTEMakePageable(PAGE, ReadStringRelative) +#pragma CTEMakePageable(PAGE, NbtFindLastSlash) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +NbtReadRegistry( + IN PUNICODE_STRING RegistryPath, + IN PDRIVER_OBJECT DriverObject, + OUT tNBTCONFIG *pConfig, + OUT tDEVICES **ppBindDevices, + OUT tDEVICES **ppExportDevices, + OUT tADDRARRAY **ppAddrArray + + ) +/*++ + +Routine Description: + + This routine is called to get information from the registry, + starting at RegistryPath to get the parameters. + +Arguments: + + RegistryPath - Supplies RegistryPath. The name of Nbt's node in the + registry. + + pNbtConfig - ptr to global configuration strucuture for NBT + +Return Value: + + NTSTATUS - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR BindName = NBT_BIND; + PWSTR ExportName = NBT_EXPORT; + PWSTR pwsNameServer = NBT_MAINNAME_SERVICE; + PWSTR pwsBackupNameServer = NBT_BACKUP_SERVER; + PWSTR pwsParmsKeyName = NBT_PARAMETERS; + NTSTATUS OpenStatus; + HANDLE LinkageHandle; + HANDLE ParametersHandle; + HANDLE NbtConfigHandle; + NTSTATUS Status; + ULONG Disposition; + OBJECT_ATTRIBUTES TmpObjectAttributes; + PWSTR LinkageString = L"Linkage"; + PWSTR ParametersString = L"Parameters"; + tDEVICES *pBindDevices; + tDEVICES *pExportDevices; + UNICODE_STRING ucString; + + CTEPagedCode(); + + *ppExportDevices = *ppBindDevices = NULL; + *ppAddrArray = NULL; + + // this procedure can be called from the DHCP activated code. In + // that case we just want to read the registry and not Zero the + // NbtConfig data structure. + if (DriverObject) + { + // + // Initialize the Configuration data structure + // + CTEZeroMemory(pConfig,sizeof(tNBTCONFIG)); + NbtConfig.TransactionId = WINS_MAXIMUM_TRANSACTION_ID + 1; + + // save the driver object for event logging purposes + // + NbtConfig.DriverObject = DriverObject; + + // save the registry path for later use when DHCP asks us + // to re-read the registry. + // + NbtConfig.pRegistry.Buffer = NbtAllocMem(RegistryPath->MaximumLength,NBT_TAG('i')); + NbtConfig.pRegistry.MaximumLength = (USHORT)RegistryPath->MaximumLength; + if (NbtConfig.pRegistry.Buffer) + { + RtlCopyUnicodeString(&NbtConfig.pRegistry,RegistryPath); + } + else + return(STATUS_INSUFFICIENT_RESOURCES); + + // clear the ptr to the broadcast netbios name. This ptr is an optimization + // that lets us resolve broadcast netbios name quickly + pConfig->pBcastNetbiosName = NULL; + } + // + // Open the registry. + // + + InitializeObjectAttributes( + &TmpObjectAttributes, + RegistryPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwCreateKey( + &NbtConfigHandle, + KEY_WRITE, + &TmpObjectAttributes, + 0, // title index + NULL, // class + 0, // create options + &Disposition); // disposition + + if (!NT_SUCCESS(Status)) + { + NbtLogEvent(EVENT_NBT_CREATE_DRIVER,Status); + + return STATUS_UNSUCCESSFUL; + } + + + OpenStatus = NbtOpenRegistry( + NbtConfigHandle, + LinkageString, + &LinkageHandle); + + if (NT_SUCCESS(OpenStatus)) + { + OpenStatus = NbtOpenRegistry( + NbtConfigHandle, + ParametersString, + &ParametersHandle); + + if (NT_SUCCESS(OpenStatus)) + { + // + // Read in the binding information (if none is present + // the array will be filled with all known drivers). + // + pBindDevices = NbtAllocMem(sizeof(tDEVICES),NBT_TAG('i')); + + if (pBindDevices) + { + pExportDevices = NbtAllocMem(sizeof(tDEVICES),NBT_TAG('i')); + if (pExportDevices) + { + ULONG NumDevices; + + // + // Read various parameters from the registry + // + ReadParameters(pConfig,ParametersHandle); + + Status = NbtReadLinkageInformation ( + BindName, + LinkageHandle, + pBindDevices, + (PLONG)&pConfig->uNumDevices); + + if ( Status == STATUS_ILL_FORMED_SERVICE_ENTRY ) + { + CTEMemFree(pBindDevices); + CTEMemFree(pExportDevices); + pBindDevices = pExportDevices = NULL; + pConfig->uNumDevices = 0; + } + else + { + if (!NT_SUCCESS(Status)) + { + NbtLogEvent(EVENT_NBT_READ_BIND,Status); + return(Status); + } + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Binddevice = %ws\n",pBindDevices->Names[0].Buffer)); + + // Read the EXPORT information as well. + Status = NbtReadLinkageInformation ( + ExportName, + LinkageHandle, + pExportDevices, + &NumDevices); + + // we want the lowest number for num devices in case there + // are more bindings than exports or viceversa + // + pConfig->uNumDevices = (USHORT)( pConfig->uNumDevices > NumDevices ? + NumDevices : pConfig->uNumDevices); + + if (!NT_SUCCESS(Status) || (pConfig->uNumDevices == 0)) + { + NbtLogEvent(EVENT_NBT_READ_EXPORT,Status); + if (NT_SUCCESS(Status)) + { + Status = STATUS_UNSUCCESSFUL; + } + return(Status); + } + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Exportdevice = %ws\n",pExportDevices->Names[0].Buffer)); + + // + // read in the NameServer IP address now + // + Status = ReadNameServerAddresses(NbtConfigHandle, + pBindDevices, + pConfig->uNumDevices, + ppAddrArray); + + if (!NT_SUCCESS(Status)) + { + if (!(NodeType & BNODE)) + { + NbtLogEvent(EVENT_NBT_NAME_SERVER_ADDRS,Status); + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt: Failed to Read the name Server Addresses!!, status = %X\n", + Status)); + } + // + // we don't fail startup if we can't read the name + // server addresses + // + Status = STATUS_SUCCESS; + } + else + { + // + // check if any WINS servers have been configured change + // to Hnode + // + if (NodeType & (BNODE | DEFAULT_NODE_TYPE)) + { + ULONG i; + for (i=0;i<pConfig->uNumDevices ;i++ ) + { + if (((*ppAddrArray)[i].NameServerAddress != LOOP_BACK) || + ((*ppAddrArray)[i].BackupServer != LOOP_BACK)) + { + NodeType = MSNODE | (NodeType & PROXY); + break; + } + } + } + } + } + // + // we have done the check for default node so turn off + // the flag + // + NodeType &= ~DEFAULT_NODE_TYPE; + // + // A Bnode cannot be a proxy too + // + if (NodeType & BNODE) + { + if (NodeType & PROXY) + { + NodeType &= ~PROXY; + } + } + + // keep the size around for allocating memory, so that when we run over + // OSI, only this value should change (in theory at least) + pConfig->SizeTransportAddress = sizeof(TDI_ADDRESS_IP); + + // fill in the node type value that is put into all name service Pdus + // that go out identifying this node type + switch (NodeType & NODE_MASK) + { + case BNODE: + pConfig->PduNodeType = 0; + break; + case PNODE: + pConfig->PduNodeType = 1 << 13; + break; + case MNODE: + pConfig->PduNodeType = 1 << 14; + break; + case MSNODE: + pConfig->PduNodeType = 3 << 13; + break; + + } + + // read the name of the transport to bind to + // + Status = ReadElement(ParametersHandle, + WS_TRANSPORT_BIND_NAME, + &ucString); + if (!NT_SUCCESS(Status)) + { + NbtConfig.pTcpBindName = NBT_TCP_BIND_NAME; + Status = STATUS_SUCCESS; + StreamsStack = TRUE; + } + else + { + UNICODE_STRING StreamsString; + + // + // if there is already a bind string, free it before + // allocating another + // + if (NbtConfig.pTcpBindName) + { + CTEMemFree(NbtConfig.pTcpBindName); + } + NbtConfig.pTcpBindName = ucString.Buffer; + + // ********** REMOVE LATER *********** + RtlInitUnicodeString(&StreamsString,NBT_TCP_BIND_NAME); + if (RtlCompareUnicodeString(&ucString,&StreamsString,FALSE)) + StreamsStack = FALSE; + else + StreamsStack = TRUE; + + } + ZwClose(LinkageHandle); + ZwClose (NbtConfigHandle); + ZwClose(ParametersHandle); + + *ppExportDevices = pExportDevices; + *ppBindDevices = pBindDevices; + return(Status); + } + + CTEMemFree(pBindDevices); + + } + ZwClose(ParametersHandle); + } + else + NbtLogEvent(EVENT_NBT_OPEN_REG_PARAMS,OpenStatus); + + ZwClose(LinkageHandle); + } + + ZwClose (NbtConfigHandle); + + NbtLogEvent(EVENT_NBT_OPEN_REG_LINKAGE,OpenStatus); + + return STATUS_UNSUCCESSFUL; + + +} +//---------------------------------------------------------------------------- +NTSTATUS +ReadNameServerAddresses ( + IN HANDLE NbtConfigHandle, + IN tDEVICES *BindDevices, + IN ULONG NumberDevices, + OUT tADDRARRAY **ppAddrArray + ) + +/*++ + +Routine Description: + + This routine is called to read the name server addresses from the registry. + It stores them in a data structure that it allocates. This memory is + subsequently freed in driver.c when the devices have been created. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ +#define ADAPTER_SIZE_MAX 200 + + UNICODE_STRING ucString; + NTSTATUS status = STATUS_UNSUCCESSFUL; + HANDLE Handle; + LONG i,j,Len; + PWSTR pwsNameServer = L"NameServer"; + PWSTR pwsDhcpNameServer = L"DhcpNameServer"; + PWSTR pwsBackup = L"NameServerBackup"; + PWSTR pwsDhcpBackup = L"DhcpNameServerBackup"; + PWSTR pwsAdapter = L"Adapters\\"; + PWSTR BackSlash = L"\\"; + tADDRARRAY *pAddrArray; + ULONG LenAdapter; + + CTEPagedCode(); + + + // this is large enough for 100 characters of adapter name. + ucString.Buffer = NbtAllocMem(ADAPTER_SIZE_MAX,NBT_TAG('i')); + + *ppAddrArray = NULL; + if (!ucString.Buffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pAddrArray = NbtAllocMem(sizeof(tADDRARRAY)*NumberDevices,NBT_TAG('i')); + CTEZeroMemory(pAddrArray,sizeof(tADDRARRAY)*NumberDevices); + + if (!pAddrArray) + { + CTEMemFree(ucString.Buffer); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + *ppAddrArray = pAddrArray; + + // get the adapter name out of the Bind string, and use it to open + // a key by the same name, to get the name server addresses + // + for (i = 0;i < (LONG)NumberDevices ;i ++ ) + { + WCHAR *pBuffer; + + Len = BindDevices->Names[i].Length/sizeof(WCHAR); + Len--; + // + // start at the end a work backwards looking for a '\' + // + j = Len; + pBuffer = &BindDevices->Names[i].Buffer[j]; + while (j) + { + if (*pBuffer != *BackSlash) + { + j--; + pBuffer--; + } + else + break; + } + + // if we don't find a backslash or at least one + // character name then continue around again, or the name + // is longer than the buffer, then go to the next device in the + // bind list + // + if ((j == 0) || + (j == Len) || + (j == Len -1) || + ((Len - j) > ADAPTER_SIZE_MAX)) + { + continue; + } + + // copy the string "Adapter\" to the buffer since the adapters all + // appear under this key in the registery + // + + LenAdapter = wcslen(pwsAdapter); + CTEMemCopy(ucString.Buffer, + pwsAdapter, + LenAdapter*sizeof(WCHAR)); + // copy just the adapter name from the Bind string, since that is + // the name of the key to open to find the name server ip addresses + // + CTEMemCopy(&ucString.Buffer[LenAdapter], + ++pBuffer, + (Len - j)*sizeof(WCHAR)); + + ucString.Buffer[Len - j + LenAdapter] = 0; + + status = NbtOpenRegistry( + NbtConfigHandle, + ucString.Buffer, + &Handle); + + pAddrArray->NameServerAddress = LOOP_BACK; + pAddrArray->BackupServer = LOOP_BACK; + + if (NT_SUCCESS(status)) + { + status = GetServerAddress(Handle, + pwsNameServer, + &pAddrArray->NameServerAddress); + // + // If there is no WINS addres in the registry or the address is + // null, which we map to Loop_Back, then try the Dhcp keys to see + // if Dhcp has written a value. + // + if (!NT_SUCCESS(status) || + (pAddrArray->NameServerAddress == LOOP_BACK)) + { + status = GetServerAddress(Handle, + pwsDhcpNameServer, + &pAddrArray->NameServerAddress); + + status = GetServerAddress(Handle, + pwsDhcpBackup, + &pAddrArray->BackupServer); + } + else + { + status = GetServerAddress(Handle, + pwsBackup, + &pAddrArray->BackupServer); + + } + + // don't want to fail this routine just because the + // name server address was not set + status = STATUS_SUCCESS; + + ZwClose(Handle); + } + pAddrArray++; + + } + + CTEMemFree(ucString.Buffer); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +GetServerAddress ( + IN HANDLE ParametersHandle, + IN PWSTR KeyName, + OUT PULONG pIpAddr + ) + +/*++ + +Routine Description: + + This routine is called to read the name server addresses from the registry. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + ULONG IpAddr; + PUCHAR NameServer; + + CTEPagedCode(); + + status = CTEReadIniString(ParametersHandle,KeyName,&NameServer); + + if (NT_SUCCESS(status)) + { + status = ConvertDottedDecimalToUlong(NameServer,&IpAddr); + if (NT_SUCCESS(status) && IpAddr) + { + *pIpAddr = IpAddr; + } + else + { + if (IpAddr != 0) + { + NbtLogEvent(EVENT_NBT_BAD_PRIMARY_WINS_ADDR,0); + } + *pIpAddr = LOOP_BACK; + } + + CTEMemFree((PVOID)NameServer); + + + } + else + { + *pIpAddr = LOOP_BACK; + } + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NbtAppendString ( + IN PWSTR FirstString, + IN PWSTR SecondString, + OUT PUNICODE_STRING pucString + ) + +/*++ + +Routine Description: + + This routine is called to append the second string to the first string. + It allocates memory for this, so the caller must be sure to free it. + +Arguments: + + +Return Value: + + None. + +--*/ +{ + NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; + ULONG Length; + PWSTR pDhcpKeyName; + + CTEPagedCode(); + + Length = (wcslen(FirstString) + wcslen(SecondString) + 1)*sizeof(WCHAR); + pDhcpKeyName = NbtAllocMem(Length,NBT_TAG('i')); + if (pDhcpKeyName) + { + pucString->Buffer = pDhcpKeyName; + pucString->Length = (USHORT)0; + pucString->MaximumLength = (USHORT)Length; + pucString->Buffer[0] = UNICODE_NULL; + + status = RtlAppendUnicodeToString(pucString,FirstString); + if (NT_SUCCESS(status)) + { + status = RtlAppendUnicodeToString(pucString,SecondString); + if (NT_SUCCESS(status)) + { + return status; + } + } + CTEFreeMem(pDhcpKeyName); + + } + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTReadIniString ( + IN HANDLE ParametersHandle, + IN PWSTR KeyName, + OUT PUCHAR *ppString + ) + +/*++ + +Routine Description: + + This routine is called to read a string of configuration information from + the registry. + +Arguments: + + ParametersHandle - handle to open key in registry + KeyName - key to read + ppString - returned string + +Return Value: + + None. + +--*/ +{ + UNICODE_STRING ucString; + STRING String; + NTSTATUS status; + PUCHAR pBuffer; + PWSTR Dhcp = L"Dhcp"; + + CTEPagedCode(); + // + // read in the Scope Id + // + status = ReadElement( + ParametersHandle, + KeyName, // Value to read + &ucString); // return value + + // + // if the key is not there or it is set to a null string try to read the + // dhcp key + // + if (!NT_SUCCESS(status) || (ucString.Length == 0)) + { + UNICODE_STRING String; + + // free the string allocated in ReadElement + if (NT_SUCCESS(status)) + { + CTEMemFree(ucString.Buffer); + } + // + // try to read a similar string that is prefixed with "DHCP" + // incase there is only the DHCP configuration information present + // and not overrides keys. + // + status = NbtAppendString(Dhcp,KeyName,&String); + if (NT_SUCCESS(status)) + { + status = ReadElement( + ParametersHandle, + String.Buffer, // Value to read + &ucString); // return value + + // free the buffer allocated in NbtAppendString + CTEFreeMem(String.Buffer); + } + } + // the scope must be less than + // 255-16 characters since the whole name is limited to 255 as per the + // RFC + // + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt: ReadIniString = %ws\n",ucString.Buffer)); + + if (NT_SUCCESS(status)) + { + if ((ucString.Length > 0) && + (ucString.Length <= (255 - NETBIOS_NAME_SIZE)*sizeof(WCHAR))) + { + + pBuffer = NbtAllocMem(ucString.MaximumLength/sizeof(WCHAR),NBT_TAG('i')); + + if (pBuffer) + { + // convert to an ascii string and store in the config data structure + // increment pBuffer to leave room for the length byte + // + String.Buffer = pBuffer; + String.MaximumLength = ucString.MaximumLength/sizeof(WCHAR); + status = RtlUnicodeStringToAnsiString(&String, + &ucString, + FALSE); + + if (NT_SUCCESS(status)) + { + *ppString = pBuffer; + } + else + { + CTEMemFree(pBuffer); + } + } + else + status = STATUS_UNSUCCESSFUL; + + + } + else + if (NT_SUCCESS(status)) + { + // force the code to setup a null scope since the one in the + // registry is null + // + status = STATUS_UNSUCCESSFUL; + } + + // free the string allocated in ReadElement + CTEMemFree(ucString.Buffer); + } + + + return(status); +} + +VOID +NbtFreeRegistryInfo ( + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to free any storage that was allocated + by NbConfigureTransport in producing the specified CONFIG_DATA structure. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + +} + +//---------------------------------------------------------------------------- +NTSTATUS +GetIPFromRegistry( + IN PUNICODE_STRING pucRegistryPath, + IN PUNICODE_STRING pucBindDevice, + OUT PULONG pulIpAddress, + OUT PULONG pulSubnetMask, + IN BOOL fWantDhcpAddresses + ) +/*++ + +Routine Description: + + This routine is called to get the IP address of an adapter from the + Registry. The Registry path variable contains the path name + for NBT's registry entries. The last element of this path name is + removed to give the path to any card in the registry. + + The BindDevice path contains a Bind string for NBT. We remove the last + element of this path (which is the adapter name \Elnkii01) and tack it + onto the modified registry path from above. We then tack on + \Parameters which will give the full path to the Tcpip key, which + we open to get the Ip address. + + +Arguments: + + pucRegistryPath - Supplies pucRegistryPath. The name of Nbt's node in the + registry. + + pNbtConfig - ptr to global configuration strucuture for NBT + +Return Value: + + NTSTATUS - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR pwsIpAddressName = ( fWantDhcpAddresses ? L"DhcpIPAddress" : L"IPAddress" ); // value name to read + PWSTR pwsSubnetMask = ( fWantDhcpAddresses ? L"DhcpSubnetMask" : L"SubnetMask" ); // value name to read + PWSTR TcpParams = L"\\Parameters\\Tcpip"; // key to open + ULONG Len; + ULONG iBindPathLength; + PVOID pBuffer; + NTSTATUS Status; + PWSTR pwsString; + UNICODE_STRING Path; + UNICODE_STRING ucIpAddress; + UNICODE_STRING ucSubnetMask; + + + CTEPagedCode(); + + // now find the last back slash in the path name to the bind device + NbtFindLastSlash(pucBindDevice,&pwsString,&iBindPathLength); + if (pwsString) + { + // get the length of the adapter name (+1 for unicode null) + // + Len = (wcslen(pwsString) + wcslen(TcpParams) + 1) * sizeof(WCHAR); + pBuffer = NbtAllocMem(Len,NBT_TAG('i')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + Path.Buffer = pBuffer; + Path.MaximumLength = (USHORT)Len; + Path.Length = 0; + + // put adapter name in the Path string + Status = RtlAppendUnicodeToString(&Path,pwsString); + + if (NT_SUCCESS(Status)) + { + // put Tcpip\parameters on the end of the adapter name + Status = RtlAppendUnicodeToString(&Path,TcpParams); + + if (NT_SUCCESS(Status)) + { + Status = ReadStringRelative(&NbtConfig.pRegistry, + Path.Buffer, + pwsIpAddressName, + &ucIpAddress); + + if (NT_SUCCESS(Status)) + { + Status = ConvertToUlong(&ucIpAddress,pulIpAddress); + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Convert Ipaddr string= %ws,ulong = %X\n",ucIpAddress.Buffer,*pulIpAddress)); + + // free the unicode string buffers allocated when the data was read + // from the registry + // + CTEMemFree(ucIpAddress.Buffer); + + // + // DHCP may put a 0 Ip address in the registry - we don't want to + // boot netbt under these conditions. + // + if (*pulIpAddress == 0) + { + Status = STATUS_INVALID_ADDRESS; + } + + if (NT_SUCCESS(Status)) + { + // read the broadcast address in now + Status = ReadStringRelative(&NbtConfig.pRegistry, + Path.Buffer, + pwsSubnetMask, + &ucSubnetMask); + + if (NT_SUCCESS(Status)) + { + // we must convert the Subnet mask to a broadcast address... + Status = ConvertToUlong(&ucSubnetMask,pulSubnetMask); + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to convert dotted decimal SubnetMask to ULONG string= %ws\n", + ucIpAddress.Buffer)); + + Status = STATUS_INVALID_ADDRESS; + } + + CTEMemFree(ucSubnetMask.Buffer); + } + else + { + Status = STATUS_INVALID_ADDRESS; + } + } + else + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to convert dotted decimal IpAddress to ULONG string= %ws\n", + ucIpAddress.Buffer)); + + Status = STATUS_INVALID_ADDRESS; + } + + } + } + + + } + // + // free the string with the path to the adapter in it + // + CTEMemFree(pBuffer); + } + else + Status = STATUS_UNSUCCESSFUL; + + return Status; + +} // GetIPFromRegistry + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtOpenRegistry( + IN HANDLE NbConfigHandle, + IN PWSTR String, + OUT PHANDLE pHandle + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to open the registry. If the registry + tree for Nbt exists, then it opens it and returns TRUE. If not, it + creates the appropriate keys in the registry, opens it, and + returns FALSE. + + +Arguments: + + NbConfigHandle - this is the root handle which String is relative to + String - the name of the key to open below the root handle + pHandle - returns the handle to the String key. + +Return Value: + + The status of the request. + +--*/ +{ + + NTSTATUS Status; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES TmpObjectAttributes; + + CTEPagedCode(); + // + // Open the Nbt key. + // + + RtlInitUnicodeString (&KeyName, String); + + InitializeObjectAttributes( + &TmpObjectAttributes, + &KeyName, // name + OBJ_CASE_INSENSITIVE, // attributes + NbConfigHandle, // root + NULL // security descriptor + ); + + Status = ZwOpenKey( + pHandle, + KEY_READ, + &TmpObjectAttributes); + + + return Status; + +} /* NbOpenRegistry */ + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtReadLinkageInformation( + IN PWSTR pName, + IN HANDLE LinkageHandle, + OUT tDEVICES *pDevices, // place to put read in config data + OUT LONG *pNumDevices + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to read its linkage information + from the registry. If there is none present, then ConfigData + is filled with a list of all the adapters that are known + to Nbt. + +Arguments: + + RegistryHandle - A pointer to the open registry. + +Return Value: + + Status + +--*/ + +{ + UNICODE_STRING BindString; + NTSTATUS RegistryStatus; + + PKEY_VALUE_FULL_INFORMATION BindValue; + ULONG BytesWritten; + USHORT ConfigBindings = 0; + PWSTR CurBindValue; + ULONG Count; + PVOID pBuffer; + + + CTEPagedCode(); + // + // We read the parameters out of the registry + // linkage key. + // + RegistryStatus = STATUS_BUFFER_OVERFLOW; + Count = 1; + while ((RegistryStatus == STATUS_BUFFER_OVERFLOW) && (Count < 20)) + { + pBuffer = NbtAllocMem(REGISTRY_BUFF_SIZE*Count,NBT_TAG('i')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + BindValue = (PKEY_VALUE_FULL_INFORMATION)pBuffer; + + // copy "Bind" or "Export" into the unicode string + RtlInitUnicodeString (&BindString, pName); + + RegistryStatus = ZwQueryValueKey( + LinkageHandle, + &BindString, // string to retrieve + KeyValueFullInformation, + (PVOID)BindValue, // returned info + REGISTRY_BUFF_SIZE*Count, + &BytesWritten // # of bytes returned + ); + Count++; + if (RegistryStatus == STATUS_BUFFER_OVERFLOW) + { + CTEMemFree(pBuffer); + } + } + + if (!NT_SUCCESS(RegistryStatus) || + (RegistryStatus == STATUS_BUFFER_OVERFLOW)) + { + CTEMemFree(pBuffer); + return RegistryStatus; + } + + if ( BytesWritten == 0 ) + { + CTEMemFree(pBuffer); + return STATUS_ILL_FORMED_SERVICE_ENTRY; + } + + + // allocate memory for the unicode strings, currently in BindValue + // on the stack + pDevices->RegistrySpace = (PVOID)NbtAllocMem((USHORT)BytesWritten,NBT_TAG('i')); + + if ( pDevices->RegistrySpace == NULL ) + { + CTEMemFree(pBuffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlMoveMemory((PVOID)pDevices->RegistrySpace, (PVOID)BindValue, BytesWritten); + + // Point to the permanent location for the strings + BindValue = (PKEY_VALUE_FULL_INFORMATION)pDevices->RegistrySpace; + + CurBindValue = (PWCHAR)((PUCHAR)BindValue + BindValue->DataOffset); + + while ((*CurBindValue != 0) && + (ConfigBindings < NBT_MAXIMUM_BINDINGS)) + { + + // this sets the buffer ptr in Names to point to CurBindValue, so + // this value must be real memory and not stack, hence the need + // to allocate memory above... + RtlInitUnicodeString (&pDevices->Names[ConfigBindings], + (PCWSTR)CurBindValue); + + ++ConfigBindings; + + // + // Now advance the "Bind" value. + // + + // wcslen => wide character string length for a unicode string + CurBindValue += wcslen((PCWSTR)CurBindValue) + 1; + + } + *pNumDevices = ConfigBindings; + + CTEMemFree(pBuffer); + return STATUS_SUCCESS; + +} /* NbtReadLinkageInformation */ + +//---------------------------------------------------------------------------- +ULONG +NbtReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue, + IN ULONG MinimumValue + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to read a single parameter + from the registry. If the parameter is found it is stored + in Data. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to search for. + + DefaultValue - The default value. + +Return Value: + + The value to use; will be the default if the value is not + found or is not in the correct range. + +--*/ + +{ + static ULONG InformationBuffer[60]; + PKEY_VALUE_FULL_INFORMATION Information = + (PKEY_VALUE_FULL_INFORMATION)InformationBuffer; + UNICODE_STRING ValueKeyName; + ULONG InformationLength; + ULONG ReturnValue=DefaultValue; + NTSTATUS Status; + ULONG Count=2; + PWSTR Dhcp = L"Dhcp"; + BOOLEAN FreeString = FALSE; + + CTEPagedCode(); + RtlInitUnicodeString (&ValueKeyName, ValueName); + + while (Count--) + { + + Status = ZwQueryValueKey( + ParametersHandle, + &ValueKeyName, + KeyValueFullInformation, + (PVOID)Information, + sizeof (InformationBuffer), + &InformationLength); + + + if ((Status == STATUS_SUCCESS) && (Information->DataLength == sizeof(ULONG))) + { + + RtlMoveMemory( + (PVOID)&ReturnValue, + ((PUCHAR)Information) + Information->DataOffset, + sizeof(ULONG)); + + if (ReturnValue < MinimumValue) + { + ReturnValue = MinimumValue; + } + + } + else + { + // + // try to read the Dhcp key instead if the first read failed. + // + Status = STATUS_SUCCESS; + if (Count) + { + Status = NbtAppendString(Dhcp,ValueName,&ValueKeyName); + } + + if (!NT_SUCCESS(Status)) + { + Count = 0; + ReturnValue = DefaultValue; + } + else + FreeString = TRUE; + + + } + } // of while + + // nbt append string allocates memory. + if (FreeString) + { + CTEMemFree(ValueKeyName.Buffer); + + } + return ReturnValue; + +} /* NbtReadSingleParameter */ + + +//---------------------------------------------------------------------------- +NTSTATUS +OpenAndReadElement( + IN PUNICODE_STRING pucRootPath, + IN PWSTR pwsValueName, + OUT PUNICODE_STRING pucString + ) +/*++ + +Routine Description: + + This routine is called by Nbt to read in the Ip address appearing in the + registry at the path pucRootPath, with a key of pwsKeyName + +Arguments: + pucRootPath - the registry path to the key to read + pwsKeyName - the key to open (i.e. Tcpip) + pwsValueName- the name of the value to read (i.e. IPAddress) + +Return Value: + + pucString - the string returns the string read from the registry + +--*/ + +{ + + NTSTATUS Status; + HANDLE hRootKey; + OBJECT_ATTRIBUTES TmpObjectAttributes; + + CTEPagedCode(); + + InitializeObjectAttributes( + &TmpObjectAttributes, + pucRootPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwOpenKey( + &hRootKey, + KEY_READ, + &TmpObjectAttributes); + + if (!NT_SUCCESS(Status)) + { + return STATUS_UNSUCCESSFUL; + } + + Status = ReadElement(hRootKey,pwsValueName,pucString); + + ZwClose (hRootKey); + return(Status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +ReadElement( + IN HANDLE HandleToKey, + IN PWSTR pwsValueName, + OUT PUNICODE_STRING pucString + ) +/*++ + +Routine Description: + + This routine is will read a string value given by pwsValueName, under a + given Key (which must be open) - given by HandleToKey. This routine + allocates memory for the buffer in the returned pucString, so the caller + must deallocate that. + +Arguments: + + pwsValueName- the name of the value to read (i.e. IPAddress) + +Return Value: + + pucString - the string returns the string read from the registry + +--*/ + +{ + ULONG ReadStorage[150]; // 600 bytes + ULONG BytesRead; + NTSTATUS Status; + PWSTR pwsSrcString; + PKEY_VALUE_FULL_INFORMATION ReadValue = + (PKEY_VALUE_FULL_INFORMATION)ReadStorage; + + CTEPagedCode(); + + // now put the name of the value to read into a unicode string + RtlInitUnicodeString(pucString,pwsValueName); + + // this read the value of IPAddress under the key opened above + Status = ZwQueryValueKey( + HandleToKey, + pucString, // string to retrieve + KeyValueFullInformation, + (PVOID)ReadValue, // returned info + sizeof(ReadStorage), + &BytesRead // # of bytes returned + ); + + if ( Status == STATUS_BUFFER_OVERFLOW ) + { + ReadValue = (PKEY_VALUE_FULL_INFORMATION) NbtAllocMem( BytesRead, NBT_TAG('i')); + if ( ReadValue == NULL ) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("ReadElement: failed to allocate %d bytes for element\n",BytesRead)); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto ReadElement_Return; + } + Status = ZwQueryValueKey( + HandleToKey, + pucString, // string to retrieve + KeyValueFullInformation, + (PVOID)ReadValue, // returned info + BytesRead, + &BytesRead // # of bytes returned + ); + } + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("failed to Query Value Status = %X\n",Status)); + goto ReadElement_Return; + } + + if ( BytesRead == 0 ) + { + Status = STATUS_ILL_FORMED_SERVICE_ENTRY; + goto ReadElement_Return; + } + else + if (ReadValue->DataLength == 0) + { + Status = STATUS_UNSUCCESSFUL; + goto ReadElement_Return; + } + + + // create the pucString and copy the data returned to it + // assumes that the ReadValue string ends in a UNICODE_NULL + //bStatus = RtlCreateUnicodeString(pucString,pwSrcString); + pwsSrcString = (PWSTR)NbtAllocMem((USHORT)ReadValue->DataLength,NBT_TAG('i')); + if (!pwsSrcString) + { + ASSERTMSG((PVOID)pwsSrcString, + (PCHAR)"Unable to allocate memory for a Unicode string"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // move the read in data from the stack to the memory allocated + // from the nonpaged pool + RtlMoveMemory( + (PVOID)pwsSrcString, + ((PUCHAR)ReadValue) + ReadValue->DataOffset, + ReadValue->DataLength); + + RtlInitUnicodeString(pucString,pwsSrcString); + // if there isn't a null on the end of the pwsSrcString, then + // it will not work correctly. - a null string comes out with a + // length of 1!! since the null is counted therefore use + // rtlinitunicode string afterall. + // pucString->MaximumLength = ReadValue->DataLength; + // pucString->Length = ReadValue->DataLength; + // pucString->Buffer = pwsSrcString; + } + +ReadElement_Return: + + if ( ( ReadValue != (PKEY_VALUE_FULL_INFORMATION)ReadStorage ) + && ( ReadValue != NULL ) ) + { + CTEFreeMem(ReadValue); + } + return(Status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTGetLmHostPath( + OUT PUCHAR *ppPath + ) +/*++ + +Routine Description: + + This routine will read the DataBasePath from under + ...\tcpip\parameters\databasepath + +Arguments: + + pPath - ptr to a buffer containing the path name. + +Return Value: + + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING ucDataBase; + STRING StringPath; + STRING LmhostsString; + ULONG StringMax; + PWSTR LmHosts = L"lmhosts"; + PWSTR TcpIpParams = L"TcpIp\\Parameters"; + PWSTR TcpParams = L"Tcp\\Parameters"; + PWSTR DataBase = L"DataBasePath"; + PCHAR ascLmhosts="\\lmhosts"; + PCHAR pBuffer; + + CTEPagedCode(); + + status = ReadStringRelative(&NbtConfig.pRegistry, + TcpIpParams, + DataBase, + &ucDataBase); + + if (!NT_SUCCESS(status)) + { + // check for the new TCP stack which a slightly different registry + // key name. + // + status = ReadStringRelative(&NbtConfig.pRegistry, + TcpParams, + DataBase, + &ucDataBase); + if (!NT_SUCCESS(status)) + { + return STATUS_UNSUCCESSFUL; + } + } + + + StringMax = ucDataBase.Length/sizeof(WCHAR) + strlen(ascLmhosts) + 1; + pBuffer = NbtAllocMem(StringMax,NBT_TAG('i')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + StringPath.Buffer = (PCHAR)pBuffer; + StringPath.MaximumLength = (USHORT)StringMax; + StringPath.Length = (USHORT)StringMax; + + // convert to ascii from unicode + status = RtlUnicodeStringToAnsiString(&StringPath,&ucDataBase,FALSE); + + // this memory was allocated in OpenAndReadElement + // + CTEMemFree(ucDataBase.Buffer); + + if (!NT_SUCCESS(status)) + { + return(STATUS_UNSUCCESSFUL); + } + + // now put the "\lmhosts" name on the end of the string + // + RtlInitString(&LmhostsString,ascLmhosts); + + status = RtlAppendStringToString(&StringPath,&LmhostsString); + + if (NT_SUCCESS(status)) + { + // + // is the first part of the directory "%SystemRoot%" ? + // + // If so, it must be changed to "\\SystemRoot\\". + // + // 0123456789 123456789 1 + // %SystemRoot%\somewhere + // + // + if (strncmp(StringPath.Buffer, "%SystemRoot%", 12) == 0) + { + + StringPath.Buffer[0] = '\\'; + StringPath.Buffer[11] = '\\'; + if (StringPath.Buffer[12] == '\\') + { + ASSERT(StringPath.Length >= 13); + + if (StringPath.Length > 13) + { + RtlMoveMemory( // overlapped copy + &(StringPath.Buffer[12]), // Destination + &(StringPath.Buffer[13]), // Source + (ULONG) StringPath.Length - 13); // Length + + StringPath.Buffer[StringPath.Length - 1] = (CHAR) NULL; + } + + StringPath.Length--; + } + } + *ppPath = (PCHAR)StringPath.Buffer; + } + else + *ppPath = NULL; + + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +ReadStringRelative( + IN PUNICODE_STRING pRegistryPath, + IN PWSTR pRelativePath, + IN PWSTR pValueName, + OUT PUNICODE_STRING pOutString + ) + +/*++ + +Routine Description: + + This routine reads a string from a registry key parallel to the + Netbt key - such as ..\tcpip\parameters\database + +Arguments: + + pRegistryPath = ptr to the Netbt Registry path + pRelativePath = path to value relative to same root as nbt. + pValueName = value to read + + + +Return Value: + + The length of the path up to and including the last slash and a ptr + to the first character of the last element of the string. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING RegistryPath; + UNICODE_STRING RelativePath; + ULONG StringMax; + PVOID pBuffer; + PWSTR pLastElement; + ULONG Length; + + CTEPagedCode(); + + StringMax = (pRegistryPath->MaximumLength + + wcslen(pRelativePath)*sizeof(WCHAR)+2); + // + // allocate some memory for the registry path so that it is large enough + // to append a string on to, for the relative key to be read + // + pBuffer = NbtAllocMem(StringMax,NBT_TAG('i')); + + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RegistryPath.MaximumLength = (USHORT)StringMax; + RegistryPath.Buffer = pBuffer; + RtlCopyUnicodeString(&RegistryPath,pRegistryPath); + + // + // find the last backslash and truncate the string + NbtFindLastSlash(&RegistryPath,&pLastElement,&Length); + RegistryPath.Length = (USHORT)Length; + + if (pLastElement) + { + *pLastElement = UNICODE_NULL; + + RtlInitUnicodeString(&RelativePath,pRelativePath); + + status = RtlAppendUnicodeStringToString(&RegistryPath,&RelativePath); + + if (NT_SUCCESS(status)) + { + status = OpenAndReadElement(&RegistryPath,pValueName,pOutString); + + if (NT_SUCCESS(status)) + { + // free the registry path + // + CTEMemFree(pBuffer); + return(status); + } + } + } + CTEMemFree(pBuffer); + + return(status); +} +//---------------------------------------------------------------------------- +VOID +NbtFindLastSlash( + IN PUNICODE_STRING pucRegistryPath, + OUT PWSTR *ppucLastElement, + IN int *piLength + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to find the last slash in a registry + path name. + +Arguments: + + +Return Value: + + The length of the path up to and including the last slash and a ptr + to the first character of the last element of the string. + +--*/ + +{ + int i; + PWSTR pwsSlash = L"\\"; + int iStart; + + CTEPagedCode(); + + // search starting at the end of the string for the last back slash + iStart = wcslen(pucRegistryPath->Buffer)-1; + for(i=iStart;i>=0 ;i-- ) + { + if (pucRegistryPath->Buffer[i] == *pwsSlash) + { + if (i==pucRegistryPath->Length-1) + { + // name ends a back slash... this is an error + break; + } + // increase one to allow for the slash + *piLength = (i+1)*sizeof(WCHAR); + if (ppucLastElement != NULL) + { + // want ptr to point at character after the slash + *ppucLastElement = &pucRegistryPath->Buffer[i+1]; + } + return; + } + } + + // null the pointer if one is passed in + if (ppucLastElement != NULL) + { + *ppucLastElement = NULL; + } + *piLength = 0; + return; +} diff --git a/private/ntos/nbt/nt/sources.inc b/private/ntos/nbt/nt/sources.inc new file mode 100644 index 000000000..4fa44b266 --- /dev/null +++ b/private/ntos/nbt/nt/sources.inc @@ -0,0 +1,71 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nbt + + + +NTPROFILEINPUT=yes + +TARGETNAME=netbt +TARGETTYPE=DRIVER + +TARGETLIBS=$(TARGETLIBS) \ + $(BASEDIR)\public\sdk\lib\*\tdi.lib + +INCLUDES=..\..\inc;..\..\..\inc;..\..\..\..\inc +!if "$(DS_BUILD)" == "1" +INCLUDES=$(BASEDIR)\private\dsinc;$(INCLUDES) +DSINC=.. +!endif + +C_DEFINES=$(C_DEFINES) -DWIN32 -DPROXY_NODE -D_NTDRIVER_ -DRASAUTODIAL -D_PNP_POWER=1 -D_IO_DELETE_DEVICE_SUPPORTED + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= \ + ..\netbt.rc \ + ..\ctestuff.c \ + ..\driver.c \ + ..\ntisol.c \ + ..\ntutil.c \ + ..\registry.c \ + ..\tdiaddr.c \ + ..\tdicnct.c \ + ..\tdihndlr.c \ + ..\fileio.c \ + ..\winsif.c \ + ..\tdiout.c \ + ..\ntpnp.c \ + ..\autodial.c + + +PRECOMPILED_INCLUDE=..\..\nbtprocs.h +PRECOMPILED_PCH=nbtprocs.pch +PRECOMPILED_OBJ=nbtprocs.obj diff --git a/private/ntos/nbt/nt/tdiaddr.c b/private/ntos/nbt/nt/tdiaddr.c new file mode 100644 index 000000000..18dbff62f --- /dev/null +++ b/private/ntos/nbt/nt/tdiaddr.c @@ -0,0 +1,672 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdihndlr.c + +Abstract: + + This file contains code relating to manipulation of address objects + that is specific to the NT operating system. It creates address endpoints + with the transport provider. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtTdiOpenAddress) +#pragma CTEMakePageable(PAGE, NbtTdiOpenControl) +#pragma CTEMakePageable(PAGE, SetEventHandler) +#pragma CTEMakePageable(PAGE, SubmitTdiRequest) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenAddress ( + OUT PHANDLE pHandle, + OUT PDEVICE_OBJECT *ppDeviceObject, + OUT PFILE_OBJECT *ppFileObject, + IN tDEVICECONTEXT *pDeviceContext, + IN USHORT PortNumber, + IN ULONG IpAddress, + IN ULONG Flags + ) +/*++ + +Routine Description: + + Note: This synchronous call may take a number of seconds. It runs in + the context of the caller. The code Opens an Address object with the + transport provider and then sets up event handlers for Receive, + Disconnect, Datagrams and Errors. + + THIS ROUTINE MUST BE CALLED IN THE CONTEXT OF THE FSP (I.E. + PROBABLY AN EXECUTIVE WORKER THREAD). + + The address data structures are found in tdi.h , but they are rather + confusing since the definitions have been spread across several data types. + This section shows the complete data type for Ip address: + + typedef struct + { + int TA_AddressCount; + struct _TA_ADDRESS + { + USHORT AddressType; + USHORT AddressLength; + struct _TDI_ADDRESS_IP + { + USHORT sin_port; + USHORT in_addr; + UCHAR sin_zero[8]; + } TDI_ADDRESS_IP + + } TA_ADDRESS[AddressCount]; + + } TRANSPORT_ADDRESS + + An EA buffer is allocated (for the IRP), with an EA name of "TransportAddress" + and value is a structure of type TRANSPORT_ADDRESS. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + + + OBJECT_ATTRIBUTES AddressAttributes; + IO_STATUS_BLOCK IoStatusBlock; + PFILE_FULL_EA_INFORMATION EaBuffer; + NTSTATUS status; + PWSTR pNameTcp=L"Tcp"; + PWSTR pNameUdp=L"Udp"; + UNICODE_STRING ucDeviceName; + PTRANSPORT_ADDRESS pTransAddressEa; + PTRANSPORT_ADDRESS pTransAddr; + TDI_ADDRESS_IP IpAddr; + BOOLEAN Attached = FALSE; + PFILE_OBJECT pFileObject; + HANDLE FileHandle; + + CTEPagedCode(); + *ppFileObject = NULL; + *ppDeviceObject = NULL; + // copy device name into the unicode string - either Udp or Tcp + // + if (Flags & TCP_FLAG) + status = CreateDeviceString(pNameTcp,&ucDeviceName); + else + status = CreateDeviceString(pNameUdp,&ucDeviceName); + + if (!NT_SUCCESS(status)) + { + return(status); + } + EaBuffer = NbtAllocMem( + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + + sizeof(TRANSPORT_ADDRESS) + + sizeof(TDI_ADDRESS_IP),NBT_TAG('j')); + + if (EaBuffer == NULL) + { + ASSERTMSG( + (PCHAR)"Unable to get memory for an Eabuffer to open an address", + (PVOID)EaBuffer); + CTEMemFree(ucDeviceName.Buffer); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + EaBuffer->NextEntryOffset = 0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + + EaBuffer->EaValueLength = sizeof(TRANSPORT_ADDRESS) -1 + + sizeof(TDI_ADDRESS_IP); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("EaValueLength = %d\n",EaBuffer->EaValueLength)); + + // put "TransportAddress" into the name + // + RtlMoveMemory( + EaBuffer->EaName, + TdiTransportAddress, + EaBuffer->EaNameLength + 1); + + // fill in the IP address and Port number + // + pTransAddressEa = (TRANSPORT_ADDRESS *)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; + + + // allocate Memory for the transport address + // + pTransAddr = NbtAllocMem( + sizeof(TDI_ADDRESS_IP)+sizeof(TRANSPORT_ADDRESS),NBT_TAG('k')); + + pTransAddr->TAAddressCount = 1; + pTransAddr->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP); + pTransAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; + + IpAddr.sin_port = htons(PortNumber); // put in network order + IpAddr.in_addr = htonl(IpAddress); + + // zero fill the last component of the IP address + // + RtlFillMemory((PVOID)&IpAddr.sin_zero, + sizeof(IpAddr.sin_zero), + 0); + + // copy the ip address to the end of the structure + // + RtlMoveMemory(pTransAddr->Address[0].Address, + (CONST PVOID)&IpAddr, + sizeof(IpAddr)); + + // copy the ip address to the end of the name in the EA structure + // + RtlMoveMemory((PVOID)pTransAddressEa, + (CONST PVOID)pTransAddr, + sizeof(TDI_ADDRESS_IP) + sizeof(TRANSPORT_ADDRESS)-1); + + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("creating Address named %ws\n",ucDeviceName.Buffer)); + + InitializeObjectAttributes( + &AddressAttributes, + &ucDeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = ZwCreateFile( + &FileHandle, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &AddressAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + 0, + (PVOID)EaBuffer, + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + EaBuffer->EaNameLength + 1 + + EaBuffer->EaValueLength); + + CTEMemFree((PVOID)pTransAddr); + CTEMemFree((PVOID)EaBuffer); + CTEMemFree(ucDeviceName.Buffer); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("NBT:Failed Create (address) File, status = %X\n",status )); + + if (NT_SUCCESS(status)) + { + // if the ZwCreate passed set the status to the IoStatus + status = IoStatusBlock.Status; + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to Open the Address to the transport, status = %X\n", + status)); + + return(status); + } + + // dereference the file object to keep the device ptr around to avoid + // this dereference at run time + // + status = ObReferenceObjectByHandle( + FileHandle, + (ULONG)0, + 0, + KernelMode, + (PVOID *)&pFileObject, + NULL); + + if (NT_SUCCESS(status)) + { + // return the handle to the caller + // + *pHandle = FileHandle; + // + // return the parameter to the caller + // + *ppFileObject = pFileObject; + + *ppDeviceObject = IoGetRelatedDeviceObject(*ppFileObject); + + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_ERROR, + (PVOID)TdiErrorHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + // if this is a TCP address being opened, then create different + // event handlers for connections + // + if (Flags & TCP_FLAG) + { + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_RECEIVE, + (PVOID)TdiReceiveHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_DISCONNECT, + (PVOID)TdiDisconnectHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + // only set a connect handler if the session flag is set. + // In this case the address being opened is the Netbios session + // port 139 + // + if (Flags & SESSION_FLAG) + { + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_CONNECT, + (PVOID)TdiConnectHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + return(status); + } + + } + else + return(status); + } + } + + + } + else + { + // Datagram ports only need this event handler + if (PortNumber == NBT_DATAGRAM_UDP_PORT) + { + // Datagram Udp Handler + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_RECEIVE_DATAGRAM, + (PVOID)TdiRcvDatagramHandler, + (PVOID)pDeviceContext); + if (NT_SUCCESS(status)) + { + return(status); + } + } + else + { + // Name Service Udp handler + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_RECEIVE_DATAGRAM, + (PVOID)TdiRcvNameSrvHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + return(status); + } + } + } + + // + // ERROR Case + // + ObDereferenceObject(pFileObject); + ZwClose(FileHandle); + + return(status); + } + + } + else + { + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("Failed Open Address (Dereference Object) status = %X\n", + status)); + + ZwClose(FileHandle); + } + + } + + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenControl ( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine opens a control object with the transport. It is very similar + to opening an address object, above. + +Arguments: + + + +Return Value: + + Status of the operation. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PWSTR pName=L"Tcp"; + PFILE_FULL_EA_INFORMATION EaBuffer; + UNICODE_STRING DeviceName; + BOOLEAN Attached = FALSE; + + + CTEPagedCode(); + // copy device name into the unicode string + Status = CreateDeviceString(pName,&DeviceName); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + InitializeObjectAttributes ( + &ObjectAttributes, + &DeviceName, + 0, + NULL, + NULL); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("tcp device to open = %ws\n",DeviceName.Buffer)); + + EaBuffer = NULL; + + Status = ZwCreateFile ( + (PHANDLE)&pDeviceContext->hControl, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + NULL, // block size (unused). + FILE_ATTRIBUTE_NORMAL, // file attributes. + 0, + FILE_CREATE, + 0, // create options. + (PVOID)EaBuffer, // EA buffer. + 0); // Ea length + + + CTEMemFree(DeviceName.Buffer); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint( ("OpenControl CreateFile Status:%X, IoStatus:%X\n", Status, IoStatusBlock.Status)); + + if ( NT_SUCCESS( Status )) + { + // if the ZwCreate passed set the status to the IoStatus + Status = IoStatusBlock.Status; + + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("Nbt:Failed to Open the control connection to the transport, status = %X\n", + Status)); + + } + else + { + // get a reference to the file object and save it since we can't + // dereference a file handle at DPC level so we do it now and keep + // the ptr around for later. + Status = ObReferenceObjectByHandle( + pDeviceContext->hControl, + 0L, + NULL, + KernelMode, + (PVOID *)&pDeviceContext->pControlFileObject, + NULL); + + if (!NT_SUCCESS(Status)) + { + ZwClose(pDeviceContext->hControl); + } + else + pDeviceContext->pControlDeviceObject = + IoGetRelatedDeviceObject(pDeviceContext->pControlFileObject); + } + + } + else + { + KdPrint(("Nbt:Failed to Open the control connection to the transport, status1 = %X\n", + Status)); + + // set control file object ptr to null so we know that we didnot open + // the control point. + // + pDeviceContext->pControlFileObject = NULL; + } + + return Status; + +} /* NbtTdiOpenConnection */ + + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine does not complete the Irp. It is used to signal to a + synchronous part of the NBT driver that it can proceed (i.e. + to allow some code that is waiting on a "KeWaitForSingleObject" to + proceeed. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the event associated with the Irp. + +Return Value: + + The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops + processing Irp stack locations at this point. + +--*/ +{ + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint( ("Completion event: %X, Irp: %X, DeviceObject: %X\n", + Context, + Irp, + DeviceObject)); + + KeSetEvent((PKEVENT )Context, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); +} + +//---------------------------------------------------------------------------- +NTSTATUS +SetEventHandler ( + IN PDEVICE_OBJECT DeviceObject, + IN PFILE_OBJECT FileObject, + IN ULONG EventType, + IN PVOID EventHandler, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine registers an event handler with a TDI transport provider. + +Arguments: + + IN PDEVICE_OBJECT DeviceObject - Supplies the device object of the transport provider. + IN PFILE_OBJECT FileObject - Supplies the address object's file object. + IN ULONG EventType, - Supplies the type of event. + IN PVOID EventHandler - Supplies the event handler. + IN PVOID Context - Supplies the context passed into the event handler when it runs + +Return Value: + + NTSTATUS - Final status of the set event operation + +--*/ + +{ + NTSTATUS Status; + PIRP Irp; + + CTEPagedCode(); + Irp = IoAllocateIrp(IoGetRelatedDeviceObject(FileObject)->StackSize, FALSE); + + if (Irp == NULL) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + TdiBuildSetEventHandler(Irp, DeviceObject, FileObject, + NULL, NULL, + EventType, EventHandler, Context); + + Status = SubmitTdiRequest(FileObject, Irp); + + IoFreeIrp(Irp); + + return Status; +} + +//---------------------------------------------------------------------------- +NTSTATUS +SubmitTdiRequest ( + IN PFILE_OBJECT FileObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine submits a request to TDI and waits for it to complete. + +Arguments: + + IN PFILE_OBJECT FileObject - Connection or Address handle for TDI request + IN PIRP Irp - TDI request to submit. + +Return Value: + + NTSTATUS - Final status of request. + +--*/ + +{ + KEVENT Event; + NTSTATUS Status; + + + CTEPagedCode(); + KeInitializeEvent (&Event, NotificationEvent, FALSE); + + // set the address of the routine to be executed when the IRP + // finishes. This routine signals the event and allows the code + // below to continue (i.e. KeWaitForSingleObject) + // + IoSetCompletionRoutine(Irp, + (PIO_COMPLETION_ROUTINE)CompletionRoutine, + (PVOID)&Event, + TRUE, TRUE, TRUE); + + CHECK_COMPLETION(Irp); + Status = IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp); + + // + // If it failed immediately, return now, otherwise wait. + // + + if (!NT_SUCCESS(Status)) + { + KdPrint(("Failed to Submit Tdi Request, status = %X\n",Status)); + return Status; + } + + if (Status == STATUS_PENDING) + { + + Status = KeWaitForSingleObject((PVOID)&Event, // Object to wait on. + Executive, // Reason for waiting + KernelMode, // Processor mode + FALSE, // Alertable + NULL); // Timeout + + if (!NT_SUCCESS(Status)) + { + KdPrint(("Failed on return from KeWaitForSingleObj in Set Event Handler, status = %X\n", + Status)); + return Status; + } + + Status = Irp->IoStatus.Status; + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("Io Status from setting event = %X\n",Status)); + } + + return(Status); +} + + + diff --git a/private/ntos/nbt/nt/tdicnct.c b/private/ntos/nbt/nt/tdicnct.c new file mode 100644 index 000000000..3ff5610e4 --- /dev/null +++ b/private/ntos/nbt/nt/tdicnct.c @@ -0,0 +1,530 @@ +// +// +// NBTCONNCT.C +// +// This file contains code relating to opening connections with the transport +// provider. The Code is NT specific. + +#include "nbtprocs.h" + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtTdiOpenConnection) +#pragma CTEMakePageable(PAGE, NbtTdiAssociateConnection) +#pragma CTEMakePageable(PAGE, TdiOpenandAssocConnection) +#pragma CTEMakePageable(PAGE, NbtTdiCloseConnection) +#pragma CTEMakePageable(PAGE, CreateDeviceString) +#pragma CTEMakePageable(PAGE, NbtTdiCloseAddress) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenConnection ( + IN tLOWERCONNECTION *pLowerConn, + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine opens a connection with the transport provider. + +Arguments: + + pLowerConn - Pointer to where the handle to the Transport for this virtual + connection should be stored. + + pNbtConfig - the name of the adapter to connect to is in this structure + +Return Value: + + Status of the operation. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PWSTR pName=L"Tcp"; + PFILE_FULL_EA_INFORMATION EaBuffer; + UNICODE_STRING DeviceName; + PMDL pMdl; + PVOID pBuffer; + BOOLEAN Attached = FALSE; + + CTEPagedCode(); + // zero out the connection data structure + CTEZeroMemory(pLowerConn,sizeof(tLOWERCONNECTION)); + pLowerConn->State = NBT_IDLE; + pLowerConn->pDeviceContext = pDeviceContext; + pLowerConn->RefCount = 1; + pLowerConn->LockNumber = LOWERCON_LOCK; + pLowerConn->Verify = NBT_VERIFY_LOWERCONN; + + Status = CreateDeviceString(pName,&DeviceName); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + // + // Allocate an MDL for the Indication buffer since we may need to buffer + // up to 128 bytes + // + pBuffer = NbtAllocMem(NBT_INDICATE_BUFFER_SIZE,NBT_TAG('l')); + + if (!pBuffer) + { + CTEMemFree(DeviceName.Buffer); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pMdl = IoAllocateMdl(pBuffer,NBT_INDICATE_BUFFER_SIZE,FALSE,FALSE,NULL); + + if (pMdl) + { + + MmBuildMdlForNonPagedPool(pMdl); + + pLowerConn->pIndicateMdl = pMdl; + + + InitializeObjectAttributes ( + &ObjectAttributes, + &DeviceName, + 0, + NULL, + NULL); + + IF_DBG(NBT_DEBUG_TDICNCT) + KdPrint(("tcp device to open = %ws\n",DeviceName.Buffer)); + + // Allocate memory for the address info to be passed to the transport + EaBuffer = (PFILE_FULL_EA_INFORMATION)NbtAllocMem ( + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_CONNECTION_CONTEXT_LENGTH + 1 + + sizeof(CONNECTION_CONTEXT),NBT_TAG('m')); + + if (EaBuffer) + { + + EaBuffer->NextEntryOffset = 0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; + EaBuffer->EaValueLength = sizeof (CONNECTION_CONTEXT); + + // TdiConnectionContext is a macro that = "ConnectionContext" - so move + // this text to EaName + RtlMoveMemory( EaBuffer->EaName, TdiConnectionContext, EaBuffer->EaNameLength + 1 ); + + // put the context value into the EaBuffer too - i.e. the value that the + // transport returns with each indication on this connection + RtlMoveMemory ( + (PVOID)&EaBuffer->EaName[EaBuffer->EaNameLength + 1], + (CONST PVOID)&pLowerConn, + sizeof (CONNECTION_CONTEXT)); + + { + + Status = ZwCreateFile ( + &pLowerConn->FileHandle, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + NULL, // block size (unused). + FILE_ATTRIBUTE_NORMAL, // file attributes. + 0, + FILE_CREATE, + 0, // create options. + (PVOID)EaBuffer, // EA buffer. + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_CONNECTION_CONTEXT_LENGTH + 1 + + sizeof(CONNECTION_CONTEXT) + ); + } + + IF_DBG(NBT_DEBUG_TDICNCT) + KdPrint( ("OpenConnection CreateFile Status:%X, IoStatus:%X\n", Status, IoStatusBlock.Status)); + + CTEMemFree((PVOID)EaBuffer); + + if ( NT_SUCCESS( Status )) + { + + // if the ZwCreate passed set the status to the IoStatus + // + Status = IoStatusBlock.Status; + + if (NT_SUCCESS(Status)) + { + // get a reference to the file object and save it since we can't + // dereference a file handle at DPC level so we do it now and keep + // the ptr around for later. + Status = ObReferenceObjectByHandle( + pLowerConn->FileHandle, + 0L, + NULL, + KernelMode, + (PVOID *)&pLowerConn->pFileObject, + NULL); + + if (NT_SUCCESS(Status)) + { + CTEMemFree(DeviceName.Buffer); + return(Status); + } + + ZwClose(pLowerConn->FileHandle); + + } + + } + + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + + IoFreeMdl(pMdl); + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + CTEMemFree(pBuffer); + CTEMemFree(DeviceName.Buffer); + + return Status; + +} /* NbtTdiOpenConnection */ + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiAssociateConnection( + IN PFILE_OBJECT pFileObject, + IN HANDLE Handle + ) +/*++ + +Routine Description: + + This routine associates an open connection with the address object. + +Arguments: + + + pFileObject - the connection file object + Handle - the address object to associate the connection with + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pIrp; + KEVENT Event; + BOOLEAN Attached = FALSE; + + CTEPagedCode(); + + KeInitializeEvent( + &Event, + SynchronizationEvent, + FALSE); + + pIrp = NTAllocateNbtIrp(IoGetRelatedDeviceObject(pFileObject)); + + if (!pIrp) + { + KdPrint(("NBT:Failed to build internal device Irp\n")); + return(STATUS_UNSUCCESSFUL); + } + + TdiBuildAssociateAddress ( + pIrp, + pFileObject->DeviceObject, + pFileObject, + CompletionRoutine, + &Event, + Handle); + + status = SubmitTdiRequest(pFileObject,pIrp); + + IoFreeIrp(pIrp); + + return status; + + +} +//---------------------------------------------------------------------------- +NTSTATUS +CreateDeviceString( + IN PWSTR AppendingString, + IN OUT PUNICODE_STRING pucDeviceName + ) +/*++ + +Routine Description: + + This routine creates a string name for the transport device such as + "\Device\Streams\Tcp" + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + ULONG Len; + PVOID pBuffer; + + CTEPagedCode(); + // copy device name into the unicode string - either Udp or Tcp + // + Len = (wcslen(NbtConfig.pTcpBindName) + wcslen(AppendingString) + 1) * sizeof(WCHAR); + + pBuffer = NbtAllocMem(Len,NBT_TAG('n')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pucDeviceName->MaximumLength = (USHORT)Len; + pucDeviceName->Length = 0; + pucDeviceName->Buffer = pBuffer; + + // this puts \Device\Streams into the string + // + status = RtlAppendUnicodeToString(pucDeviceName,NbtConfig.pTcpBindName); + if (NT_SUCCESS(status)) + { + status = RtlAppendUnicodeToString (pucDeviceName,AppendingString); + } + else + CTEMemFree(pBuffer); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiOpenandAssocConnection( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG PortNumber + ) +/*++ + +Routine Description: + + This routine opens and associates an open connection. + + This routine is called with the Spin Lock held on the pConnele. It is + released in this routine. + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + NTSTATUS Locstatus; + PDEVICE_OBJECT pDeviceObject; + tLOWERCONNECTION *pLowerConn; + BOOLEAN Attached=FALSE; + + CTEPagedCode(); + + CTEAttachFsp(&Attached); + + // allocate memory for the lower connection block. + // + pConnEle->pLowerConnId = (PVOID)NbtAllocMem(sizeof(tLOWERCONNECTION),NBT_TAG('o')); + + if (!pConnEle->pLowerConnId) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // fill in the lower connection element to point to the upper one and + // vice versa + // + pLowerConn = pConnEle->pLowerConnId; + + status = NbtTdiOpenConnection(pLowerConn,pDeviceContext); + if (!NT_SUCCESS(status)) + { + CTEDetachFsp(Attached); + CTEMemFree((PVOID)pConnEle->pLowerConnId); + pConnEle->pLowerConnId = NULL; + return(status); + } + + pLowerConn->pUpperConnection = pConnEle; + pLowerConn->State = NBT_IDLE; + + // + // 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 ) ; + + + if (NT_SUCCESS(status)) + { + + // Open an address object (aka port) + // + status = NbtTdiOpenAddress( + &pLowerConn->AddrFileHandle, + &pDeviceObject, // dummy argument, not used here + &pLowerConn->pAddrFileObject, + pDeviceContext, + (USHORT)PortNumber, // port + pDeviceContext->IpAddress, + TCP_FLAG); + + if (NT_SUCCESS(status)) + { + // now associate the two + status = NbtTdiAssociateConnection( + pLowerConn->pFileObject, + pLowerConn->AddrFileHandle); + + + if (NT_SUCCESS(status)) + { + CTEDetachFsp(Attached); + // + // put the lower connection on the Q of active lower connections for + // this device + // + ExInterlockedInsertTailList(&pDeviceContext->LowerConnection, + &pLowerConn->Linkage, + &pDeviceContext->SpinLock); + + return(status); + } + + ObDereferenceObject(pLowerConn->pAddrFileObject); + Locstatus = ZwClose(pLowerConn->AddrFileHandle); + + } + KdPrint(("Nbt:Open Xport Address Failed, status %X\n",status)); + + ObDereferenceObject(pLowerConn->pFileObject); + Locstatus = ZwClose(pLowerConn->FileHandle); + + } + + CTEDetachFsp(Attached); + + // Error Path... delete memory + // + pConnEle->pLowerConnId = NULL; + CTEMemFree((PVOID)pLowerConn); + + return(status); + +} + +//---------------------------------------------------------------------------- + +NTSTATUS +NbtTdiCloseConnection( + IN tLOWERCONNECTION * pLowerConn + ) +/*++ + +Routine Description: + + This routine closes a TDI connection + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + BOOLEAN Attached= FALSE; + + CTEPagedCode(); + ASSERT( pLowerConn != NULL ) ; + + CTEAttachFsp(&Attached); + + if (pLowerConn->FileHandle) { + status = ZwClose(pLowerConn->FileHandle); + pLowerConn->FileHandle = NULL; + } + +#if DBG + if (!NT_SUCCESS(status)) + KdPrint(("Nbt:Failed to close Connection FileHandle pLower %X, status %X\n",pLowerConn,status)); +#endif + + CTEDetachFsp(Attached); + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiCloseAddress( + IN tLOWERCONNECTION * pLowerConn + ) +/*++ + +Routine Description: + + This routine closes a TDI address + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + BOOLEAN Attached= FALSE; + + CTEPagedCode(); + + ASSERT( pLowerConn != NULL ) ; + + CTEAttachFsp(&Attached); + + status = ZwClose(pLowerConn->AddrFileHandle); +#if DBG + if (!NT_SUCCESS(status)) + KdPrint(("Nbt:Failed to close Address FileHandle pLower %X,status %X\n",pLowerConn,status)); +#endif + + CTEDetachFsp(Attached); + + return(status); + +} diff --git a/private/ntos/nbt/nt/tdihndlr.c b/private/ntos/nbt/nt/tdihndlr.c new file mode 100644 index 000000000..bcd515d97 --- /dev/null +++ b/private/ntos/nbt/nt/tdihndlr.c @@ -0,0 +1,6286 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdihndlr.c + +Abstract: + + + This file contains the TDI handlers that are setup for Connects, + Receives, Disconnects, and Errors on various objects such as connections + and udp endpoints . + + This file represents the inbound TDI interface on the Bottom of NBT. Therefore + the code basically decodes the incoming information and passes it to + a non-Os specific routine to do what it can. Upon return from that + routine additional Os specific work may need to be done. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "ctemacro.h" + +// this macro checks that the types field is always zero in the Session +// Pdu +// +#if DBG +#define CHECK_PDU( _Size,_Offset) \ + if (_Size > 1) \ + ASSERT(((PUCHAR)pTsdu)[_Offset] == 0) +#else +#define CHECK_PDU( _Size,_Offset ) +#endif + +#if DBG +UCHAR pLocBuff[256]; +UCHAR CurrLoc; + +ULONG R1; +ULONG R2; +ULONG R3; +ULONG R4; + +ULONG C1; +ULONG C2; +ULONG C3; +ULONG C4; + + +#define INCR_COUNT(_Count) _Count++ +#else +#define INCR_COUNT(_Count) +#endif + + +// +// This ntohl swaps just three bytes, since the 4th byte could be a session +// keep alive message type. +// +__inline long +myntohl(long x) +{ + return((((x) >> 24) & 0x000000FFL) | + (((x) >> 8) & 0x0000FF00L) | + (((x) << 8) & 0x00FF0000L)); +} + +NTSTATUS +LessThan4BytesRcvd( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + OUT PVOID *ppIrp + ); +NTSTATUS +ClientTookSomeOfTheData( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + IN ULONG BytesTaken, + IN ULONG PduSize + ); +NTSTATUS +MoreDataRcvdThanNeeded( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ); +NTSTATUS +NotEnoughDataYet( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN ULONG PduSize, + OUT PVOID *ppIrp + ); +NTSTATUS +ProcessIrp( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrp, + IN PVOID pBuffer, + IN PULONG BytesTaken, + IN ULONG BytesIndicted, + IN ULONG BytesAvailable + ); + +NTSTATUS +NtBuildIndicateForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ); + +NTSTATUS +AcceptCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +VOID +DpcNextOutOfRsrcKill( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +DpcGetRestOfIndication( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NTSTATUS +ClientBufferOverFlow( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG BytesRcvd + ); +VOID +DpcHandleNewSessionPdu ( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); +VOID +HandleNewSessionPdu ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Offset, + IN ULONG ToGet + ); +NTSTATUS +NewSessionCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +BuildIrpForNewSessionInIndication ( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrpIn, + IN ULONG BytesAvailable, + IN ULONG RemainingPdu, + OUT PIRP *ppIrp + ); +VOID +TrackIndicatedBytes( + IN ULONG BytesIndicated, + IN ULONG BytesTaken, + IN tCONNECTELE *pConnEle + ); + +__inline +VOID +DerefLowerConnFast ( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN CTELockHandle OldIrq + ); + +NTSTATUS +CopyDataandIndicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ); + +VOID +SumMdlLengths ( + IN PMDL pMdl, + IN ULONG BytesAvailable, + IN tCONNECTELE *pConnectEle + ); + + + +NTSTATUS +RsrcKillCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +VOID +FillIrpCancelRoutine( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NTSTATUS +NameSrvCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); +//---------------------------------------------------------------------------- +__inline +NTSTATUS +Normal( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + ASSERTMSG("Should not execute this procedure",0); + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +LessThan4BytesRcvd( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + NTSTATUS status; + + // for short indications less than 4 bytes we can't determine + // the pdu size so just get the header first then get the + // whole pdu next. + + status = NtBuildIrpForReceive(pLowerConn, + sizeof(tSESSIONHDR), + (PVOID *)ppIrp); + + pConnectEle = pLowerConn->pUpperConnection; + + pConnectEle->BytesInXport = BytesAvailable; + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return( STATUS_DATA_NOT_ACCEPTED); + } + // + // set the irp mdl length to size of session hdr so that + // we don't get more than one session pdu into the buffer + // + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + *BytesTaken = 0; + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to Ind Buff(<4 bytes), Avail = %X\n", + BytesAvailable)); + + PUSH_LOCATION(0); + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +ClientTookSomeOfTheData( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + IN ULONG BytesTaken, + IN ULONG PduSize + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + the client has not taken all of the data indicated to it. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + + // + // took some of the data, so keep track of the + // rest of the data left here by going to the PARTIALRCV + // state. + // + PUSH_LOCATION(0x5); + + pLowerConn->StateRcv = PARTIAL_RCV; + pLowerConn->CurrentStateProc = PartialRcv; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switch to Partial Rcv Indicated=%X, PduSize=%X\n", + BytesIndicated,PduSize-4)); + + // Note: PduSize must include the 4 byte session header for this to + // work correctly. + // + pConnectEle = pLowerConn->pUpperConnection; + // + // We always indicate the whole Pdu size to the client, so the amount + // indicated is that minus what was taken - typically the 4 byte + // session hdr + // + pConnectEle->ReceiveIndicated = PduSize - BytesTaken; + ASSERT(pConnectEle->ReceiveIndicated <= 0x20000); + + // amount left in the transport... + pConnectEle->BytesInXport = BytesAvailable - BytesTaken; + + // need to return this status since we took the 4 bytes + // session header at least, even if the client took none. + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +MoreDataRcvdThanNeeded( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + ULONG Length; + ULONG Remaining; + ULONG PduSize; + NTSTATUS status; + tSESSIONHDR UNALIGNED *pSessionHdr; + + + PUSH_LOCATION(0x6); + // + // there is too much data, so keep track of the + // fact that there is data left in the transport + // and get it with the indicate buffer + // + pLowerConn->StateRcv = INDICATE_BUFFER; + + + ASSERT(pLowerConn->BytesInIndicate == 0); +#if DBG + if (pLowerConn->BytesInIndicate) + { + KdPrint(("Nbt:Bytes in indicate should be ZERO, but are = %X\n", + pLowerConn->BytesInIndicate)); + } +#endif + pLowerConn->CurrentStateProc = IndicateBuffer; + pConnectEle = pLowerConn->pUpperConnection; + + pConnectEle->BytesInXport = BytesAvailable - *BytesTaken; + + // + // for short indications less than 4 bytes we can't determine + // the pdu size so just get the header first then get the + // whole pdu next. + // + Remaining = BytesIndicated - *BytesTaken; + if (Remaining < sizeof(tSESSIONHDR)) + { + status = NtBuildIrpForReceive(pLowerConn,sizeof(tSESSIONHDR),(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + // this is a serious error - we must + // kill of the connection and let the + // redirector restart it + KdPrint(("Nbt:Unable to get an Irp for RCv - Closing Connection!! %X\n",pLowerConn)); + CTESpinFreeAtDpc(pLowerConn); + + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_DATA_NOT_ACCEPTED); + } + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:< 4 Bytes,BytesTaken=%X,Avail=%X,Ind=%X,Remain=%X\n", + *BytesTaken,BytesAvailable,BytesIndicated, + Remaining)); + + // DEBUG + CTEZeroMemory(MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl), + NBT_INDICATE_BUFFER_SIZE); + + PUSH_LOCATION(0x7); + status = STATUS_MORE_PROCESSING_REQUIRED; + } + else + { + // if we get to here there are enough bytes left to determine + // the next pdu size...so we can determine how much + // data to get for the indicate buffer + // + pSessionHdr = (tSESSIONHDR UNALIGNED *)((PUCHAR)pTsdu + *BytesTaken); + + PduSize = myntohl(pSessionHdr->UlongLength) + sizeof(tSESSIONHDR); + + + Length = (PduSize > NBT_INDICATE_BUFFER_SIZE) ? + NBT_INDICATE_BUFFER_SIZE : PduSize; + + // + // The NewSessionCompletion routine recalculates + // what is left in the transport when the + // irp completes + // + status = NtBuildIrpForReceive(pLowerConn,Length,(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + // this is a serious error - we must + // kill of the connection and let the + // redirector restart it + KdPrint(("Nbt:Unable to get an Irp for RCV(2) - Closing Connection!! %X\n",pLowerConn)); + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return(STATUS_DATA_NOT_ACCEPTED); + } + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switch to Ind Buff, InXport = %X, Pdusize=%X,ToGet=%X\n", + pConnectEle->BytesInXport,PduSize-4,Length)); + + } + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NotEnoughDataYet( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN ULONG PduSize, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tCONNECTELE *pConnectEle; + ULONG Length; + + PUSH_LOCATION(0x9); + // + // not enough data indicated, so use the indicate buffer + // + Length = (PduSize > NBT_INDICATE_BUFFER_SIZE) ? + NBT_INDICATE_BUFFER_SIZE : PduSize; + + status = NtBuildIrpForReceive(pLowerConn,Length,(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return(STATUS_DATA_NOT_ACCEPTED); + } + + *BytesTaken = 0; + + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + pConnectEle = pLowerConn->pUpperConnection; + pConnectEle->BytesInXport = BytesAvailable; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Not Enough data indicated in Tdihndlr, using indic. buffer Indicated = %X,Avail=%X,PduSize= %X\n", + BytesIndicated, BytesAvailable,PduSize-4)); + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +FillIrp( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + ASSERTMSG("Should not execute this procedure",0); + return(STATUS_SUCCESS); + // do nothing + +} +//---------------------------------------------------------------------------- +NTSTATUS +IndicateBuffer( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles reception of data while in the IndicateBuffer state. + In this state the indicate buffer is receiveing data until at least + 128 bytes have been receive, or a whole pdu has been received. + + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + NTSTATUS status; + ULONG PduSize; + ULONG ToCopy; + PVOID pIndicateBuffer; + ULONG Taken; + + // + // there is data in the indicate buffer and we got a new + // indication, so copy some or all of the indication to the + // indicate buffer + // + PVOID pDest; + ULONG RemainPdu; + ULONG SpaceLeft; + ULONG TotalBytes; + ULONG ToCopy1=0; + + INCR_COUNT(R3); + PUSH_LOCATION(0xe); + pConnectEle = pLowerConn->pUpperConnection; + ASSERT(pLowerConn->StateRcv == INDICATE_BUFFER); + // + // The indicate buffer always starts with a pdu + // + pIndicateBuffer = MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + // the location to start copying the new data into is right + // after the existing data in the buffer + // + pDest = (PVOID)((PUCHAR)pIndicateBuffer + pLowerConn->BytesInIndicate); + + // + // the session header may not be all into the indicate + // buffer yet, so check that before getting the pdu length. + // + if (pLowerConn->BytesInIndicate < sizeof(tSESSIONHDR)) + { + PUSH_LOCATION(0xe); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Too Few in Indicate Buff, Adding InIndicate %X\n", + pLowerConn->BytesInIndicate)); + + ToCopy1 = sizeof(tSESSIONHDR) - pLowerConn->BytesInIndicate; + if (ToCopy1 > BytesIndicated) + { + ToCopy1 = BytesIndicated; + } + CTEMemCopy(pDest,pTsdu,ToCopy1); + + pDest = (PVOID)((PUCHAR)pDest + ToCopy1); + pTsdu = (PVOID)((PUCHAR)pTsdu + ToCopy1); + + pLowerConn->BytesInIndicate += (USHORT)ToCopy1; + + *BytesTaken = ToCopy1; + } + + // now check again, and pass down an irp to get more data if necessary + // + if (pLowerConn->BytesInIndicate < sizeof(tSESSIONHDR)) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:< 4 Bytes in IndicBuff, BytesinInd= %X, BytesIndicated=%x\n", + pLowerConn->BytesInIndicate,BytesIndicated)); + + PUSH_LOCATION(0xF); + + // + // the data left in the transport is what was Available + // minus what we just copied to the indicate buffer + // + pConnectEle->BytesInXport = BytesAvailable - ToCopy1; + + if (pConnectEle->BytesInXport) + { + PUSH_LOCATION(0x10); + // + // pass the indicate buffer down to get some more data + // to fill out to the end of the session hdr + // + NtBuildIndicateForReceive(pLowerConn, + sizeof(tSESSIONHDR)-pLowerConn->BytesInIndicate, + (PVOID *)ppIrp); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:INDIC_BUF...need more data for hdr Avail= %X, InXport = %X\n", + BytesAvailable,pConnectEle->BytesInXport,pLowerConn)); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + // if we get to here there isn't 4 bytes in the indicate buffer and + // there is no more data in the Transport, so just wait for the next + // indication. + // + return(STATUS_SUCCESS); + } + + PduSize = myntohl(((tSESSIONHDR *)pIndicateBuffer)->UlongLength) + + sizeof(tSESSIONHDR); + + // copy up to 132 bytes or the whole pdu to the indicate buffer + // + RemainPdu = PduSize - pLowerConn->BytesInIndicate; + + SpaceLeft = NBT_INDICATE_BUFFER_SIZE - pLowerConn->BytesInIndicate; + + if (RemainPdu < SpaceLeft) + ToCopy = RemainPdu; + else + ToCopy = SpaceLeft; + + if (ToCopy > (BytesIndicated-ToCopy1)) + { + ToCopy = (BytesIndicated - ToCopy1); + } + + // + // Copy the indication or part of it to the indication + // buffer + // + CTEMemCopy(pDest,pTsdu,ToCopy); + + pLowerConn->BytesInIndicate += (USHORT)ToCopy; + + TotalBytes = pLowerConn->BytesInIndicate; + + // the amount of data taken is the amount copied to the + // indicate buffer + // + *BytesTaken = ToCopy + ToCopy1; + +#if DBG + { + tSESSIONHDR UNALIGNED *pSessionHdr; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pIndicateBuffer; + ASSERT((pSessionHdr->Type == NBT_SESSION_KEEP_ALIVE) || + (pSessionHdr->Type == NBT_SESSION_MESSAGE)); + } +#endif + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:INDIC_BUFF, TotalBytes= %X, InIndic=%X, Copied(0/1)= %X %X Avail %X\n", + TotalBytes,pLowerConn->BytesInIndicate,ToCopy,ToCopy1,BytesAvailable)); + + + // the data left in the transport is what was Available + // minus what we just copied to the indicate buffer + // + pConnectEle->BytesInXport = BytesAvailable - *BytesTaken; + + // now check if we have a whole pdu or 132 bytes, either way + // enough to indicate to the client. + // + ASSERT(TotalBytes <= NBT_INDICATE_BUFFER_SIZE); + + if ((TotalBytes == NBT_INDICATE_BUFFER_SIZE) || + (TotalBytes == PduSize)) + { + + status = CopyDataandIndicate( + ReceiveEventContext, + (PVOID)pLowerConn, + ReceiveFlags, + TotalBytes, + pConnectEle->BytesInXport + TotalBytes, + &Taken, + pIndicateBuffer, + (PIRP *)ppIrp); + + } + else + { + + // not enough data in the indicate buffer yet + // NOTE: *BytesTaken should be set correctly above... + // = ToCopy + ToCopy1; + + PUSH_LOCATION(0x11); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Not Enough data indicated(INDICBUFF state), Indicated = %X,PduSize= %X,InIndic=%X\n", + BytesIndicated, PduSize, pLowerConn->BytesInIndicate)); + + + status = STATUS_SUCCESS; + } + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +PartialRcv( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + // + // the data for the client may be in the indicate buffer and + // in this case the transport could indicate us with more data. Therefore + // track the number of bytes available in the transport which + // we will get when the client finally posts a buffer. + // This state could also happen on a zero length Rcv when the + // client does not accept the data, and later posts a rcv + // buffer for the zero length rcv. + // + INCR_COUNT(R4); + PUSH_LOCATION(0x13); + ASSERT(pLowerConn->StateRcv == PARTIAL_RCV); + pConnectEle = pLowerConn->pUpperConnection; + +// ASSERT(pConnectEle->BytesInXport == 0); +#if DBG + if (pConnectEle->BytesInXport != 0) + { + KdPrint(("Netbt!PartialRcv: pConnectEle->BytesInXport != 0 Avail %X, InIndicate=%X,InXport %X %X\n", + BytesAvailable,pLowerConn->BytesInIndicate, + pConnectEle->BytesInXport,pLowerConn)); + } +#endif // DBG + pConnectEle->BytesInXport = BytesAvailable; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got Indicated while in PartialRcv state Avail %X, InIndicate=%X,InXport %X %X\n", + BytesAvailable,pLowerConn->BytesInIndicate, + pConnectEle->BytesInXport,pLowerConn)); + + *BytesTaken = 0; + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiReceiveHandler ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + IN PVOID ReceiveEventContext - Context provided for this event when event set + IN PVOID ConnectionContext - Connection Context, (pLowerConnection) + IN USHORT ReceiveFlags - Flags describing the message + IN ULONG BytesIndicated - Number of bytes available at indication time + IN ULONG BytesAvailable - Number of bytes available to receive + OUT PULONG BytesTaken - Number of bytes consumed by redirector. + IN PVOID pTsdu - Data from remote machine. + OUT PIRP *ppIrp - I/O request packet filled in if received data + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + register tLOWERCONNECTION *pLowerConn; + PIRP pIrp; + CTELockHandle OldIrq; + NTSTATUS status; + tCONNECTELE *pConnEle; + ULONG BTaken; + + *ppIrp = NULL; + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + + // NOTE: + // Access is synchronized through the spin lock on pLowerConn for all + // Session related stuff. This includes the case where the client + // posts another Rcv Buffer in NTReceive. - so there is no need to get the + // pConnEle Spin lock too. + // + + CTESpinLock(pLowerConn,OldIrq); +// pLowerConn->InRcvHandler = TRUE; + pLowerConn->RefCount++; + + // save this on the stack in case we need to dereference it below. + pConnEle = pLowerConn->pUpperConnection; + + // call the correct routine depending on the state of the connection + // Normal/FillIrp/PartialRcv/IndicateBuffer/Inbound/OutBound + // + if ((pLowerConn->State == NBT_SESSION_UP) && + (pLowerConn->StateRcv == FILL_IRP)) + { + PIO_STACK_LOCATION pIrpSp; + PMDL pNewMdl; + PFILE_OBJECT pFileObject; + ULONG RemainingPdu; + PVOID NewAddress; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + KIRQL OldIrq2; + ULONG RcvLength; + + + PUSH_LOCATION(0xa); + // we are still waiting for the rest of the session pdu so + // do not call the RcvHandlrNotOs, since we already have the buffer + // to put this data in. + // too much data may have arrived... i.e. part of the next session pdu.. + // so check and set the receive length accordingly + // + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + RcvLength = RemainingPdu; + // + // try high runner case first + // + if (BytesAvailable <= RemainingPdu) + { + PUSH_LOCATION(0xb); + // + // if the client buffer is too small to take all of the rest of the + // data, shorten the receive length and keep track of how many + // bytes are left in the transport. ReceiveIndicated should have + // been set when the irp was passed down originally. + // + if (BytesAvailable > pConnEle->FreeBytesInMdl) + { + + PUSH_LOCATION(0xb); + + RcvLength = pConnEle->FreeBytesInMdl; + pConnEle->BytesInXport = BytesAvailable - RcvLength; + } + } + else + { + // + // start of session pdu in the middle of the indication + // + PUSH_LOCATION(0xc); + // + // It is possible that the client buffer is too short, so check + // for that case. + // + if (RemainingPdu > pConnEle->FreeBytesInMdl) + { + RcvLength = pConnEle->FreeBytesInMdl; + PUSH_LOCATION(0xd); + } + /* Remember how much data is left in the transport + when this irp passes through the completionrcv routine + it will pass the indication buffer back to the transport + to get at least 4 bytes of header information so we + can determine the next session pdu's size before receiving + it. The trick is to avoid having more than one session + pdu in the buffer at once. + */ + pConnEle->BytesInXport = BytesAvailable - RcvLength; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:End of FILL_IRP, found new Pdu BytesInXport=%X\n", + pConnEle->BytesInXport)); + + + } + + pIrp = pConnEle->pIrpRcv; + + // if the transport has all of the data it says is available, then + // do the copy here ( if the client buffer is large enough - checked + // by !ReceiveIndicated) + // + if ((BytesAvailable == BytesIndicated) && + (RcvLength >= BytesIndicated) && + !pConnEle->ReceiveIndicated) + { + ULONG BytesCopied; + ULONG TotalBytes; + + PUSH_LOCATION(0x70); + + if (RcvLength > BytesIndicated) + RcvLength = BytesIndicated; + + status = TdiCopyBufferToMdl( + pTsdu, + 0, + RcvLength, + pConnEle->pNextMdl, + pConnEle->OffsetFromStart, + &BytesCopied); + + // + // if the irp is not yet full, or the free bytes have not + // been exhausted by this copy, then adjust some counts and return + // quickly, otherwise call the completion rcv routine as if the + // irp has completed normally from the transport - + // + TotalBytes = pConnEle->BytesRcvd + BytesCopied; + + if ((TotalBytes < pConnEle->TotalPcktLen) && + (BytesCopied < pConnEle->FreeBytesInMdl)) + { + PMDL pMdl; + + // + // take the short cut and do not call completion rcv since we + // are still waiting for more data + // + PUSH_LOCATION(0x81); + pConnEle->BytesRcvd += BytesCopied; + pConnEle->FreeBytesInMdl -= BytesCopied; + + // clean up the partial mdl. + // + pMdl = pConnEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + // set where the next rcvd data will start, by setting the pNextMdl and + // offset from start. + // + pMdl = pConnEle->pNextMdl; + if ((BytesCopied + pConnEle->OffsetFromStart) < MmGetMdlByteCount(pMdl)) + { + PUSH_LOCATION(0x82); + // + // All of this data will fit into the current Mdl, and + // the next data will start in the same Mdl (if there is more data) + // + pConnEle->OffsetFromStart += BytesCopied; + } + else + { + PUSH_LOCATION(0x83) + SumMdlLengths(pMdl, + pConnEle->OffsetFromStart + BytesCopied, + pConnEle); + } + *BytesTaken = BytesCopied; + status = STATUS_SUCCESS; + + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("I")); + goto ExitRoutine; + } + else + { + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("i")); + CTESpinFree(pLowerConn,OldIrq); + // + // the values are set to this so that when Completion Rcv is + // called it will increment the BytesRcvd by BytesCopied. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = BytesCopied; + + // + // now call the irp completion routine, shorting out the io + // subsystem - to process the irp + // + status = CompletionRcv(NULL,pIrp,(PVOID)pLowerConn); + // + // complete the irp back to the client if required + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + IoAcquireCancelSpinLock(&OldIrq2); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq2); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + } + // + // tell the transport we took all the data that we did take. + // Since CompletionRcv has unlocked the spin lock and decremented + // the refcount, return here. + // + *BytesTaken = BytesCopied; + return(STATUS_SUCCESS); + } + else + { + // + // Either BytesIndicated != BytesAvailable or the RcvBuffer + // is too short, so make up an Irp with a partial Mdl and pass it + // to the transport. + // + PUSH_LOCATION(0x71); + + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pConnEle->pNextMdl) + + pConnEle->OffsetFromStart); + + /* create a partial MDL so that the new data is copied after the existing data + in the MDL. Use the pNextMdl field stored in the pConnEle + that was set up during the last receive.( since at that time + we knew the BytesAvailable then). Without this we would have to + traverse the list of Mdls for each receive. + + 0 for length means map the rest of the buffer + */ + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pConnEle->pNextMdl,pNewMdl,NewAddress,0); + // + // hook the new partial mdl to the front of the MDL chain + // + pNewMdl->Next = pConnEle->pNextMdl->Next; + + pIrp->MdlAddress = pNewMdl; + ASSERT(pNewMdl); + + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + IoAcquireCancelSpinLock(&OldIrq2); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq2); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + /* this code is sped up somewhat by expanding the code here rather than calling + the TdiBuildReceive macro + + make the next stack location the current one. Normally IoCallDriver + would do this but we are not going through IoCallDriver here, since the + Irp is just passed back with RcvIndication. + */ + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + pIrpSp->CompletionRoutine = CompletionRcv; + + pParams->ReceiveLength = RcvLength; + + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pLowerConn; + + /* set flags so the completion routine is always invoked. + */ + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + + pFileObject = pLowerConn->pFileObject; + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + /* + pass the Irp back to the transport + */ + *ppIrp = (PVOID)pIrp; + *BytesTaken = 0; + + status = STATUS_MORE_PROCESSING_REQUIRED; + } + + + } + else + if ((pLowerConn->State == NBT_SESSION_UP) && + (pLowerConn->StateRcv == NORMAL)) + { + ULONG PduSize; + UCHAR Passit; + + INCR_COUNT(R1); + /* + check indication and if less than 1 pdu or 132 bytes then + copy to the indicate buffer and go to Indic_buffer state + The while loop allows us to indicate multiple Pdus to the + client in the event that several indications arrive in one + indication from the transport + NOTE: + It is possible to get an indication that occurs in the middle + of the pdu if the client took the first indication rather + than passing an irp back, and thence going to the FILL_IRP + state. So check if BytesRcvd is zero, meaning that we are + expecting a new PDU. + */ + ASSERT(pConnEle->BytesInXport == 0); + ASSERT(pLowerConn->StateRcv == NORMAL); + + if (pConnEle->BytesRcvd == 0) + { + if (BytesIndicated >= sizeof(tSESSIONHDR)) + { + PduSize = myntohl(((tSESSIONHDR UNALIGNED *)pTsdu)->UlongLength) + + sizeof(tSESSIONHDR); + Passit = FALSE; + + } + else + { + status = LessThan4BytesRcvd(pLowerConn, + BytesAvailable, + BytesTaken, + ppIrp); + goto ExitRoutine; + } + + } + else + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got rest of PDU in indication BytesInd %X, BytesAvail %X\n", + BytesIndicated, BytesAvailable)); + + /* This is the remaining pdu size + */ + PduSize = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + /* a flag to pass the if below, since we are passing the + remaining data of a pdu to the client and we do not have + to adhere to the 128 bytes restriction. + */ + PUSH_LOCATION(0x1); + if (pConnEle->JunkMsgFlag) + { + // + // in this case the client has indicated that it took the + // entire message on the previous indication, so don't + // indicate any more to it. + // + PUSH_LOCATION(0x1); + + if (BytesAvailable < PduSize) + { + BTaken = BytesAvailable; + } + else + { + BTaken = PduSize; + } + pConnEle->BytesRcvd += BTaken; + if (pConnEle->BytesRcvd == pConnEle->TotalPcktLen) + { + PUSH_LOCATION(0x1); + pConnEle->BytesRcvd = 0; // reset for the next session pdu + pConnEle->JunkMsgFlag = FALSE; + } + status = STATUS_SUCCESS; + goto SkipIndication; + } + Passit = TRUE; + + } + /* + be sure that there is at least 132 bytes or a whole pdu + Since a keep alive has a zero length byte, we check for + that because the 4 byte session hdr is added to the 0 length + giving 4, so a 4 byte Keep Alive pdu will pass this test. + */ + if ((BytesIndicated >= NBT_INDICATE_BUFFER_SIZE) || + (BytesIndicated >= PduSize) || Passit ) + { + + PUSH_LOCATION(0x2); + + /* + // Indicate to the client + */ + status = RcvHandlrNotOs( + ReceiveEventContext, + (PVOID)pLowerConn, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &BTaken, + pTsdu, + (PVOID)&pIrp + ); + + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + ULONG RemainingPdu; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // check if we can copy to the client's irp directly - meaning + // that we have received the whole pdu in this indication and + // the client's buffer is large enough, and there is no more + // data in the transport. + // + + if ((RemainingPdu == (BytesIndicated - BTaken)) && + (BytesIndicated == BytesAvailable) && + (pClientParams->ReceiveLength >= RemainingPdu) && + pIrp->MdlAddress) + { + ULONG BytesCopied; + + PUSH_LOCATION(0x88); + + status = TdiCopyBufferToMdl( + (PVOID)((PUCHAR)pTsdu + BTaken), + 0, + RemainingPdu, + pIrp->MdlAddress, + 0, + &BytesCopied); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Copy to client Buffer RcvLen=%X,StateRcv=%X\n", + RemainingPdu,pLowerConn->StateRcv)); + + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // reset a few things since this pdu has been fully recv'd + // + CHECK_PTR(pConnEle); + pConnEle->BytesRcvd = 0; + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + // + // tell the transport we took all the data that we did take. + // + *BytesTaken = BytesCopied + BTaken; + + // + // complete the irp back to the client if required + // + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("F")); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + pLowerConn->BytesRcvd += BytesCopied; + + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + return(STATUS_SUCCESS); + } + else + { + PUSH_LOCATION(0x3); + + status = ProcessIrp(pLowerConn, + pIrp, + pTsdu, + &BTaken, + BytesIndicated, + BytesAvailable); + + *BytesTaken = BTaken; + ASSERT(*BytesTaken <= (pConnEle->TotalPcktLen + sizeof(tSESSIONHDR)) ); + if (status == STATUS_RECEIVE_EXPEDITED) + { + // in this case the processirp routine has completed the + // irp, so just return since the completion routine will + // have adjusted the RefCount and InRcvHandler flag + // + *ppIrp = NULL; + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_SUCCESS); + } + else + if (status == STATUS_SUCCESS) + { + *ppIrp = NULL; + + } + else + { + *ppIrp = (PVOID)pIrp; + } + } + + + } + else + { + // for the skip indication case the client has told us it + // does not want to be indicated with any more of the data + // + SkipIndication: + // + // the client received some, all or none of the data + // For Keep Alives the PduSize is 4 and BytesTaken = 4 + // so this check and return status success + // + *BytesTaken = BTaken; + + pLowerConn->BytesRcvd += BTaken - sizeof(tSESSIONHDR); + + // + // if the connection has disonnected, then just return + // + if (!pLowerConn->pUpperConnection) + { + *BytesTaken = BytesAvailable; + status = STATUS_SUCCESS; + } + else + if (BTaken > BytesAvailable) + { + // + // in this case the client has taken all of the message + // which could be larger than the available because + // we set bytesavail to the message length. So set a flag + // that tells us to discard the rest of the message as + // it comes in. + // + pConnEle->JunkMsgFlag = TRUE; + pConnEle->BytesRcvd = BytesAvailable - sizeof(tSESSIONHDR); + *BytesTaken = BytesAvailable; + + } + else + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // this may be a zero length send -that the client has + // decided not to accept. If so then the state will be set + // to PartialRcv. In this case do NOT go down to the transport + // and get the rest of the data, but wait for the client + // to post a rcv buffer. + // + + // amount left in the transport... + pConnEle->BytesInXport = BytesAvailable - BTaken; + status = STATUS_SUCCESS; + } + else + if (BTaken == PduSize) + { + /* + Must have taken all of the pdu data, so check for + more data available - if so send down the indicate + buffer to get it. + */ + ASSERT(BTaken <= BytesIndicated); + if (BytesAvailable <= BTaken) + { + /* FAST PATH + */ + PUSH_LOCATION(0x8); + + status = STATUS_SUCCESS; + + } + else + { + /* + get remaining data with the indicate buffer + */ + status = MoreDataRcvdThanNeeded(pLowerConn, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + ppIrp); + } + } + else + { + // + // the client may have taken all the data in the + // indication!!, in which case return status success + // Note: that we check bytes available here not bytes + // indicated - since the client could take all indicated + // data but still leave data in the transport. + // + if (BTaken == BytesAvailable) + { + PUSH_LOCATION(0x4); + status = STATUS_SUCCESS; + + } + else + { + PUSH_LOCATION(0x87); + if (BTaken > PduSize) + { +#ifndef VXD +#if DBG + DbgBreakPoint(); +#endif +#endif + // + // the client took more than a PDU size worth, + // which is odd.... + // + PUSH_LOCATION(0x87); + ASSERT(BTaken <= PduSize); + + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + status = STATUS_SUCCESS; + + } + else + { + // + // otherwise the client did not take all of the data, + // which can mean that + // the client did not take all that it could, so + // go to the partial rcv state to keep track of it. + // + status = ClientTookSomeOfTheData(pLowerConn, + BytesIndicated, + BytesAvailable, + *BytesTaken, + PduSize); + } + } + } + + } + + } + else + { + status = NotEnoughDataYet(pLowerConn, + BytesIndicated, + BytesAvailable, + BytesTaken, + PduSize, + (PVOID *)ppIrp); + } + } + else + { + status = (*pLowerConn->CurrentStateProc)(ReceiveEventContext, + pLowerConn, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + ppIrp); + } + + // + // in the IndicateBuffer state we have sent the indicate buffer + // down the the transport and expect it to come back in + // NewSessionCompletionRoutine. Therefore do not dereference the lower + // connection and do not change the InRcvHandler flag. + + // If an Irp + // is returned, then do not undo the reference - but rather + // wait for CompletionRcv to be called. + // +ExitRoutine: + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection + // + PUSH_LOCATION(0x50); + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + } + else + CTESpinFree(pLowerConn,OldIrq); + + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ProcessIrp( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrp, + IN PVOID pBuffer, + IN PULONG BytesTaken, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable + ) +/*++ + +Routine Description: + + This routine handles a Receive Irp that the client has returned on an + indication. The idea here is to check the Irp's MDL length to be + sure the pdu fits into the MDL, and also keep track of the situation where + more than one data is required to fill the pdu. + +Arguments: + + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnectEle; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + ULONG RemainingPdu; + PMDL pMdl; + PFILE_OBJECT pFileObject; + ULONG ReceiveLength; + BOOLEAN QuickRoute; + BOOLEAN FromCopyData; + + pConnectEle = pLowerConn->pUpperConnection; + + status = STATUS_SUCCESS; + + // subtract session header and any bytes that the client took + // + BytesAvailable -= *BytesTaken; + + // + // put together an Irp stack location to process the receive and pass down + // to the transport. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // + // check if this will be a multiple rcv session pdu. If it is then + // allocate a partial MDL to be used for mapping part of the first + // MDL in each chunk received + // + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + ReceiveLength = RemainingPdu; + PUSH_LOCATION(0x19); + pIrpSp = IoGetNextIrpStackLocation(pIrp); + + // this code should not be hit if called by CopyDataandIndicate + // which is in the indicate buffer state since it adjusts the bytesInXport + // which is also set by the code in TdiReceiveHndlr in the INDICATE_BUFFER + // state before calling CopyDataandIndicate. Also, CopyDataandIndicate + // does not want this routine to set the state to fillIrp when Bytes + // Available < RemainingPdu + // + FromCopyData = (pLowerConn->StateRcv == INDICATE_BUFFER); + if (!FromCopyData) + { + + QuickRoute = TRUE; + // we need this code within the check since this routine is also called by the + // HandleNewSessionPdu routine, which calls IoCallDriver, which + // increments the stack location itself. + // + ASSERT(pIrp->CurrentLocation > 1); + + if (BytesAvailable == RemainingPdu) + { + if (pClientParams->ReceiveLength >= BytesAvailable) + { + // *** FAST PATH CASE **** + goto ExitCode; + } + } + else + if (BytesAvailable < RemainingPdu ) // need more data from transport + { + PUSH_LOCATION(0x14); + // it is possible for the client to pass down an irp with no + // MDL in it, so we check for that here + // + if (pIrp->MdlAddress) + { + PUSH_LOCATION(0x14); + + // + // save the client's irp address since the session pdu will arrive + // in several chunks, and we need to continually pass the irp to the + // transport for each chunk. + // + //pConnectEle->pIrpRcv = pIrp; + // NOTE: the pIrp is NOT saved here because the irp is about + // to be passed back to the transport. Hence we do not want + // to accidently complete it in DisconnectHandlrNotOs + // if a disconnect comes in while the irp is in the transport. + // pIrpRcv is set to pIrp in Completion Rcv while we have + // the irp in our possession. + + // + // keep the initial Mdl(chain) since we need to + // to copy new data after the existing data, when the session pdu arrives + // as several chunks from TCP. Keeping the Mdl around allows us to + // reconstruct the original Mdl chain when we are all done. + // + pLowerConn->pMdl = pIrp->MdlAddress; + // + // this call maps the client's Mdl so that on each partial Mdl creation + // we don't go through a mapping and unmapping (when MmPrepareMdlForReuse) + // is called in the completion routine. + // + (PVOID)MmGetSystemAddressForMdl(pIrp->MdlAddress); + + pMdl = pIrp->MdlAddress; + + // the nextmdl is setup to allow us to create a partial Mdl starting + // from the next one. CompletionRcv will adjust this if it needs to. + // + pConnectEle->pNextMdl = pMdl; + + // need more data from the transport to fill this + // irp + // + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + } + + status = STATUS_MORE_PROCESSING_REQUIRED; + + // if the client buffer is big enough, increment to the next + // io stack location and jump to the code that sets up the + // irp, since we always want to pass it to the transport in this + // case because the transport will hold onto the irp till it is full + // if it can. (faster) + // + if (pClientParams->ReceiveLength >= RemainingPdu) + { + // *** FAST PATH CASE **** + IoSetNextIrpStackLocation(pIrp); + pConnectEle->FreeBytesInMdl = ReceiveLength; + pConnectEle->CurrentRcvLen = RemainingPdu; + goto ExitCode2; + } + + // + // if there is no mdl then we want to be able to go through the + // quick route below to return the null mdl right away, so + // don't set Quickroute false here. + // + + + } + else + if (BytesAvailable > RemainingPdu) + { + PUSH_LOCATION(0x15); + // + // there is too much data, so keep track of the + // fact that there is data left in the transport + // and get it when the irp completes through + // completion recv. + // + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + // this calculation may have to be adjusted below if the client's + // buffer is too short. NOTE: BytesTaken have already been subtracted + // from BytesAvailable (above). + // + pConnectEle->BytesInXport = BytesAvailable - RemainingPdu; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to Indicate Buff(Irp), Indic = %X, Pdusize=%X\n", + BytesIndicated,pConnectEle->TotalPcktLen)); + + + status = STATUS_DATA_NOT_ACCEPTED; + } + + // DEBUG* + //IoSetNextIrpStackLocation(pIrp); + } + else + { + QuickRoute = FALSE; + } + + // + // if the receive buffer is too short then flag it so when the client + // passes another buffer to NBT, nbt will pass it to the transport + // + //if (BytesAvailable > pClientParams->ReceiveLength ) + { + + // so just check for too short of a client buffer. + // + if (RemainingPdu > pClientParams->ReceiveLength) + { + PUSH_LOCATION(0x17); + + ReceiveLength = pClientParams->ReceiveLength; + // + // Adjust the number of bytes left in the transport up by the number of + // bytes not taken by the client. Be sure not to add in the number + // of bytes in the transport twice, since it could have been done + // above where the state is set to INDICATE_BUFFER + // + if (status == STATUS_DATA_NOT_ACCEPTED) + { + // BytesInXport was already incremented to account for any + // amount over remainingPdu, so just add the amount that the + // client buffer is short of RemainingPdu + // + PUSH_LOCATION(0x18); + if (BytesAvailable > ReceiveLength ) + { + pConnectEle->BytesInXport += (RemainingPdu - ReceiveLength); + } + // the client has not taken all of the data , but has returned + // a buffer that is ReceiveLength long, therefore the amount + // that the client needs to take is just the total pdu - rcvlength. + // + pConnectEle->ReceiveIndicated = (RemainingPdu - + ReceiveLength); + } + else + { + // + // BytesInXport has not been incremented yet so add the entire + // amount that the client buffer is too short by. Check if + // the client's buffer will take all of the data. + // + if (BytesAvailable > ReceiveLength ) + { + pConnectEle->BytesInXport += (BytesAvailable - ReceiveLength); + } + // the client has not taken all of the data , but has returned + // a buffer that is ReceiveLength long, therefore the amount + // that the client needs to take is just what was indicated + // to the client - recvlength. + // + pConnectEle->ReceiveIndicated = (RemainingPdu - + ReceiveLength); + + } + + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to PartialRcv for Irp. RecvInd. =%X, RemainPdu %X Avail %X\n", + pConnectEle->ReceiveIndicated,RemainingPdu,BytesAvailable)); + } + + } + +ExitCode: + + // keep track of data in MDL so we know when it is full and we need to + // return it to the user. CurrentRcvLen tells us how many bytes the current + // Irp can have max when the Mdl is full. + // + pConnectEle->FreeBytesInMdl = ReceiveLength; + pConnectEle->CurrentRcvLen = ReceiveLength; + if (ReceiveLength > RemainingPdu) + { + pConnectEle->CurrentRcvLen = RemainingPdu; + } + if (QuickRoute) + { + // + // check if we can copy the data to the client's MDL + // right here. If the indication is too short pass an Irp down + // to the transport. + // + BytesIndicated -= *BytesTaken; + + if ((ReceiveLength <= BytesIndicated)) + { + ULONG BytesCopied; + + PUSH_LOCATION(0x76); + + if (pIrp->MdlAddress) + { + + status = TdiCopyBufferToMdl( + (PVOID)((PUCHAR)pBuffer + *BytesTaken), + 0, + ReceiveLength, + pIrp->MdlAddress, + 0, + &BytesCopied); + + } + else + { + // + // No Mdl, so just return the irp to the client, and then + // return success to the caller so we tell the transport that + // we took only BytesTaken + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:No MDL, so complete Irp\n")); + + + PUSH_LOCATION(0x77); + BytesCopied = 0; + } + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Copy to client Buffer RcvLen=%X,StateRcv=%X\n", + ReceiveLength,pLowerConn->StateRcv)); + + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + // + // now call the irp completion routine, shorting out the io + // subsystem - to process the irp + // + CTESpinFreeAtDpc(pLowerConn); + status = CompletionRcv(NULL,pIrp,(PVOID)pLowerConn); + + // + // tell the transport we took all the data that we did take. + // + *BytesTaken += BytesCopied; + + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("f")); + // + // complete the irp back to the client if required + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + PUSH_LOCATION(0x76); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Completing Irp Quickly\n")); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + } + + // since we have called CompletionRcv, that routine has + // adjusted the refcount and InRcvHandlr flag, so return this + // status to cause the caller to return directly + CTESpinLockAtDpc(pLowerConn); + return(STATUS_RECEIVE_EXPEDITED); + + } + else + { + // + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + // + IoSetNextIrpStackLocation(pIrp); + } + + } +ExitCode2: + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pLowerConn; + + // set Control flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + + pFileObject = pLowerConn->pFileObject; + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + // Set the correct receive length in the irp in case the client has + // passed one down that is larger than the message + // + pParams->ReceiveLength = ReceiveLength; + + // + // just check for a zero length send, where the client has + // passed down an Irp with a null mdl, or the pdu size is zero. We don't want to pass + // that to the transport because it will hold onto it till the next + // pdu comes in from the wire - we want to complete the irp when this routine + // returns. When this is called from CopyDataAndIndicate don't + // to this because copydataandindicate does all the checks. + // + if (!FromCopyData) + { + if ((RemainingPdu == 0) || !pIrp->MdlAddress) + { + // + // the call to IoCompleteRequest will call completionRcv which will + // decrement the RefCount. Similarly returning status success will + // cause the caller to decrement the ref count, so increment one + // more time here to account for this second decrement. + // + pLowerConn->RefCount++; + CTESpinFreeAtDpc(pLowerConn); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTESpinLockAtDpc(pLowerConn); + + status = STATUS_SUCCESS; + } + else + status = STATUS_MORE_PROCESSING_REQUIRED; + } + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ClientBufferOverFlow( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG BytesRcvd + ) +/*++ + +Routine Description: + + This routine completes the Irp by tracking the number of bytes received + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pLowerConn - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + + // *TODO* + +// ASSERT(0); + + switch (pLowerConn->StateRcv) + { + case PARTIAL_RCV: + + + case FILL_IRP: + + + case NORMAL: + + + + case INDICATE_BUFFER: + + + default: + ; + } + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRcv( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine completes the Irp by tracking the number of bytes received + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pLowerConn - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + register tCONNECTELE *pConnectEle; + NTSTATUS status; + ULONG BytesRcvd; + tLOWERCONNECTION *pLowerConn; + PKDPC pDpc; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + PMDL pMdl; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + BOOLEAN AllowDereference=TRUE; + + // + // Do some checking to keep the Io system happy - propagate the pending + // bit up the irp stack frame.... if it was set by the driver below then + // it must be set by me + // + if (Irp->PendingReturned) + { + IoMarkIrpPending(Irp); + } + + // check the bytes recvd + pLowerConn = (tLOWERCONNECTION *)Context; + // + // if the link has disconnected, do not process the irp, just pass it + // up the chain. + // + CTESpinLock(pLowerConn,OldIrq); + if (!NT_SUCCESS(Irp->IoStatus.Status) || !pLowerConn->pUpperConnection) + { + PUSH_LOCATION(0x1); + if (pLowerConn->StateRcv == FILL_IRP) + { + PUSH_LOCATION(0x1); + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + } + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + // + // the rcv failed so kill the connection since + // we can't keep track of message boundaries any more. + // + CTESpinFree(pLowerConn,OldIrq); + OutOfRsrcKill(pLowerConn); + CTESpinLock(pLowerConn,OldIrq); + + status = STATUS_SUCCESS; + goto ExitCode; + } + + pConnectEle = pLowerConn->pUpperConnection; + + // keep track of how many bytes have been received + // + BytesRcvd = Irp->IoStatus.Information; + pConnectEle->BytesRcvd += BytesRcvd; + // + // subtract the number of bytes rcvd from the length of the client + // buffer + // so when more data arrives we can determine if we are going to + // overflow the client buffer. + // + pConnectEle->FreeBytesInMdl -= BytesRcvd; + + pIrpSp = IoGetCurrentIrpStackLocation(Irp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + pLowerConn->BytesRcvd += BytesRcvd; + + CHECK_PTR(pConnectEle); + if (Irp->IoStatus.Status == STATUS_BUFFER_OVERFLOW) + { + // + // the client's buffer was too short - probably because he said it + // was longer than it really was + // + PUSH_LOCATION(0x1a); + KdPrint(("Nbt:Client Buffer Too short on CompletionRcv\n")); + + if (pLowerConn->StateRcv == FILL_IRP) + { + PUSH_LOCATION(0x1a); + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + + } + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + status = ClientBufferOverFlow(pLowerConn,pConnectEle,Irp,BytesRcvd); + + // + // the client's buffer was too short so kill the connection since + // we can't keep track of message boundaries any more. + // + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + CTESpinFree(pLowerConn,OldIrq); + OutOfRsrcKill(pLowerConn); + CTESpinLock(pLowerConn,OldIrq); + + goto ExitCode; + } + else + if ((pConnectEle->FreeBytesInMdl == 0) || + (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen)) + { + INCR_COUNT(C1); + // + // this case handles when the Irp MDL is full or the whole pdu has been + // received. + // + + // + // reset the MDL fields back to where they were + // if this was a multi-rcv session pdu + // + // + if (pLowerConn->StateRcv == FILL_IRP) + { + + INCR_COUNT(C2); + PUSH_LOCATION(0x1b); + + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + // + // allow the MDL to be used again for the next session PDU + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + pConnectEle->OffsetFromStart = 0; + + } + // + // The client may have passed down a too short irp which we are + // tracking with ReceiveIndicated, so set state to partialrcv if + // necessary. + // + if (pConnectEle->ReceiveIndicated == 0) + { + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + else + { + PUSH_LOCATION(0x26); + // + // there may still be data left in the transport + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Short Rcv, still data indicated to client\n")); + + pLowerConn->StateRcv = PARTIAL_RCV; + pLowerConn->CurrentStateProc = PartialRcv; + } + + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + // + // we have received all of the data + // so complete back to the client + // + status = STATUS_SUCCESS; + // + // the amount of data in this irp is the CurrentRcvLen which + // could be less than BytesRcvd when the client passes down + // short rcv buffers. + // + Irp->IoStatus.Information = pConnectEle->CurrentRcvLen; + + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + Irp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + PUSH_LOCATION(0x27); + // + // this MDL must be too short to take the whole pdu, so set the + // status to buffer overflow. + // + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + + // + // Check if there is still more data in the transport or if the client + // has been indicated with more data and has subsequently posted a rcv + // which we must get now and pass to the transport. + // + if ((pConnectEle->BytesInXport) || (pLowerConn->StateRcv == PARTIAL_RCV)) + { + INCR_COUNT(C3); + // + // send down another + // irp to get the data and complete the client's current irp. + // + PUSH_LOCATION(0x1c); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:ComplRcv BytesInXport= %X, %X\n",pConnectEle->BytesInXport, + pLowerConn)); + + if (pLowerConn->StateRcv != PARTIAL_RCV) + { + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + pLowerConn->BytesInIndicate = 0; + } + + CTESpinFree(pLowerConn,OldIrq); + + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(Irp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + // Complete the current Irp + IoCompleteRequest(Irp,IO_NETWORK_INCREMENT); + + CTESpinLock(pLowerConn,OldIrq); + + // rather than call HandleNewSessionPdu directly, we queue a + // Dpc since streams does not currently expect to get a recv + // posted while it is processing an indication response. The + // Dpc will run when streams is all done, and it should handle + // this posted receive ok. + + + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // + // check if the client has passed down another rcv buffer + // and if so, start a Dpc which will pass down the client's + // buffer. + // + if (!IsListEmpty(&pConnectEle->RcvHead)) + { + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('p')); + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcGetRestOfIndication, + (PVOID)pLowerConn); + + KeInsertQueueDpc(pDpc,NULL,NULL); + // + // we don't want to dereference pLowerConn at the end + // since we will use it in the DPC routine. + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + + } + } + else + { + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('q')); + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + // + // just get the session hdr to start with so we know how large + // the pdu is, then get the rest of the pdu after that completes. + // + KeInsertQueueDpc(pDpc,NULL,(PVOID)sizeof(tSESSIONHDR)); + // + // we don't want to dereference pLowerConn at the end + // since we will use it in the DPC routine. + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + } + + status = STATUS_MORE_PROCESSING_REQUIRED; + goto ExitCode; + } + } + else + if (pConnectEle->BytesRcvd < pConnectEle->TotalPcktLen) + { + ULONG Bytes; + + INCR_COUNT(C4); + PUSH_LOCATION(0x1d); + // + // in this case we have not received all of the data from the transport + // for this session pdu, so tell the io subystem not to finish processing + // the irp yet if it is not a partial Rcv. + // + status = STATUS_MORE_PROCESSING_REQUIRED; + + // clean up the partial mdl. + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + // set where the next rcvd data will start, by setting the pNextMdl and + // offset from start. + // + pMdl = pConnectEle->pNextMdl; + ASSERT(pMdl); + + Bytes = BytesRcvd + pConnectEle->OffsetFromStart; + if (Bytes < MmGetMdlByteCount(pMdl)) + { + PUSH_LOCATION(0x74); + // + // All of this data will fit into the current Mdl, and + // the next data will start in the same Mdl (if there is more data) + // + pConnectEle->OffsetFromStart += BytesRcvd; + + IF_DBG(NBT_DEBUG_FILLIRP) + KdPrint(("~")); + } + else + { + // + // sum the Mdl lengths until we find enough space for the data + // to fit into. + // + IF_DBG(NBT_DEBUG_FILLIRP) + KdPrint(("^")); + PUSH_LOCATION(0x75); + + SumMdlLengths(pMdl,Bytes,pConnectEle); + + } + + // since we are holding on to the rcv Irp, set up a cancel routine + IoAcquireCancelSpinLock(&OldIrq2); + + // if the session was disconnected while the transport had the + // irp, then cancel the irp now... + // + if ((pConnectEle->state != NBT_SESSION_UP) || Irp->Cancel) + { + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + + + IoReleaseCancelSpinLock(OldIrq2); + CTESpinFree(pLowerConn,OldIrq); + + // since the irp has been cancelled, don't touch it. + // return status success so the IO subsystem passes the irp + // back to the owner. + // + status = STATUS_SUCCESS; + +// Irp->IoStatus.Status = STATUS_CANCELLED; +// IoCompleteRequest(Irp,IO_NETWORK_INCREMENT); + + // the irp is being cancelled in mid session pdu. We can't + // recover since we have given the client only part of a pdu, + // therefore disconnect the connection. + + OutOfRsrcKill(pLowerConn); + + CTESpinLock(pLowerConn,OldIrq); + + } + else + { + // setup the cancel routine + IoSetCancelRoutine(Irp,FillIrpCancelRoutine); + + // the pIrpRcv value is set to Zero when the irp is in the + // tranport, so we can't accidently complete it twice in + // disconnectHandlrNotOs when a disconnect occurs and the + // transport has the irp. So here we save the value again so FillIrp + // will work correctly. + // + pConnectEle->pIrpRcv = Irp; + // set the irp mdl back to its original so that a cancel will + // find the irp in the right state + // + Irp->MdlAddress = pLowerConn->pMdl; + + IoReleaseCancelSpinLock(OldIrq2); + + } + } + else + { + + //IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Too Many Bytes Rcvd!! Rcvd# = %d, TotalLen = %d,NewBytes =%d,%X\n", + pConnectEle->BytesRcvd,pConnectEle->TotalPcktLen, + Irp->IoStatus.Information,pLowerConn)); + ASSERT(0); + // this status will return the irp to the user + // + status = STATUS_SUCCESS; + if (pLowerConn->StateRcv == FILL_IRP) + { + + PUSH_LOCATION(0x1f); + + Irp->MdlAddress = pLowerConn->pMdl; + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = 0; + + // + // allow the MDL to be used again for the next session PDU + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + + } + pConnectEle->OffsetFromStart = 0; + pConnectEle->BytesRcvd = 0; + + pLowerConn->StateRcv = NORMAL; + + //WHAT ELSE TO DO HERE OTHER THAN KILL THE CONNECTION, SINCE WE ARE + // PROBABLY OFF WITH RESPECT TO THE SESSION HDR.... + // ....RESET THE CONNECTION ???? + + CTESpinFree(pLowerConn,OldIrq); + + OutOfRsrcKill(pLowerConn); + + CTESpinLock(pLowerConn,OldIrq); + + } + +ExitCode: + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection - this function is __inline!! + // + PUSH_LOCATION(0x52); + DerefLowerConnFast(pLowerConn,pConnectEle,OldIrq); + + return(status); + + UNREFERENCED_PARAMETER( DeviceObject ); +} +//---------------------------------------------------------------------------- + +__inline +NTSTATUS +RcvHandlrNotOs ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *RcvBuffer + + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network, when the + session has already been established (NBT_SESSION_UP state). The routine + looks for a receive buffer first and failing that looks for a receive + indication handler to pass the message to. + +Arguments: + + pClientEle - ptr to the connecition record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + NTSTATUS status; + PLIST_ENTRY pRcv; + PVOID pRcvElement; + tCLIENTELE *pClientEle; + tSESSIONHDR UNALIGNED *pSessionHdr; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + CTELockHandle OldIrq; + PIRP pIrp; + ULONG ClientBytesTaken; + BOOLEAN DebugMore; + ULONG RemainingPdu; + +//******************************************************************** +//******************************************************************** +// +// NOTE: A copy of this procedure is in Tdihndlr.c - it is inlined for +// the NT case. Therefore, only change this procedure and then +// copy the procedure body to Tdihndlr.c +// +// +//******************************************************************** +//******************************************************************** + + // get the ptr to the lower connection, and from that get the ptr to the + // upper connection block + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu; + + // + // Session ** UP ** processing + // + *BytesTaken = 0; + + pConnectEle = pLowerConn->pUpperConnection; + + ASSERT(pConnectEle->pClientEle); + + ASSERT(BytesIndicated >= sizeof(tSESSIONHDR)); + + // this routine can get called by the next part of a large pdu, so that + // we don't always started at the begining of a pdu. The Bytes Rcvd + // value is set to zero in CompletionRcv when a new pdu is expected + // + if (pConnectEle->BytesRcvd == 0) + { + + if (pSessionHdr->Type == NBT_SESSION_MESSAGE) + { + + // + // expecting the start of a new session Pkt, so get the length out + // of the pTsdu passed in + // + pConnectEle->TotalPcktLen = myntohl(pSessionHdr->UlongLength); + + // remove the Session header by adjusting the data pointer + pTsdu = (PVOID)((PUCHAR)pTsdu + sizeof(tSESSIONHDR)); + + // shorten the number of bytes since we have stripped off the + // session header + BytesIndicated -= sizeof(tSESSIONHDR); + BytesAvailable -= sizeof(tSESSIONHDR); + *BytesTaken = sizeof(tSESSIONHDR); + } + // + // Session Keep Alive + // + else + if (pSessionHdr->Type == NBT_SESSION_KEEP_ALIVE) + { + // session keep alives are simply discarded, since the act of sending + // a keep alive indicates the session is still alive, otherwise the + // transport would report an error. + + // tell the transport that we took the Pdu + *BytesTaken = sizeof(tSESSIONHDR); + return(STATUS_SUCCESS); + + } + else + { + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Unexpected Session Pdu received: type = %X\n", + pSessionHdr->Type)); + + ASSERT(0); + *BytesTaken = BytesIndicated; + return(STATUS_SUCCESS); + + } + } + + // + // check if there are any receive buffers queued against this connection + // + if (!IsListEmpty(&pConnectEle->RcvHead)) + { + // get the first buffer off the receive list + pRcv = RemoveHeadList(&pConnectEle->RcvHead); +#ifndef VXD + pRcvElement = CONTAINING_RECORD(pRcv,IRP,Tail.Overlay.ListEntry); + + // the cancel routine was set when this irp was posted to Nbt, so + // clear it now, since the irp is being passed to the transport + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine((PIRP)pRcvElement,NULL); + IoReleaseCancelSpinLock(OldIrq); + +#else + pRcvElement = CONTAINING_RECORD(pRcv, RCV_CONTEXT, ListEntry ) ; +#endif + + // + // this buffer is actually an Irp, so pass it back to the transport + // as a return parameter + // + *RcvBuffer = pRcvElement; + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + // + // No receives on this connection. Is there a receive event handler for this + // address? + // + pClientEle = pConnectEle->pClientEle; + +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + if (pClientEle->evReceive) +#endif + { + + + // check that we have not received more data than we should for + // this session Pdu. i.e. part of the next session pdu. BytesRcvd may + // have a value other than zero if the pdu has arrived in two chunks + // and the client has taken the previous one in the indication rather + // than passing back an Irp. + // +#if DBG + DebugMore = FALSE; +#endif + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + if (BytesAvailable >= RemainingPdu) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:More Data Recvd than expecting! Avail= %X,TotalLen= %X,state=%x\n", + BytesAvailable,pConnectEle->TotalPcktLen,pLowerConn->StateRcv)); +#if DBG + DebugMore =TRUE; +#endif + // shorten the indication to the client so that they don't + // get more data than the end of the pdu + // + BytesAvailable = RemainingPdu; + if (BytesIndicated > BytesAvailable) + { + BytesIndicated = BytesAvailable; + } + // + // We always indicated at raised IRQL since we call freelockatdispatch + // below + // + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE | TDI_RECEIVE_AT_DISPATCH_LEVEL; + } + else + { + // the transport may have has this flag on. We need to + // turn it off if the entire message is not present, where entire + // message means within the bytesAvailable length. We deliberately + // use bytesavailable so that Rdr/Srv can know that the next + // indication will be a new message if they set bytestaken to + // bytesavailable. + // + ReceiveFlags &= ~TDI_RECEIVE_ENTIRE_MESSAGE; + ReceiveFlags |= TDI_RECEIVE_AT_DISPATCH_LEVEL; +#ifndef VXD + BytesAvailable = RemainingPdu; +#endif + } + + // + // NT-specific code locks pLowerConn before calling this routine, + // + CTESpinFreeAtDpc(pLowerConn); + + // call the Client Event Handler + ClientBytesTaken = 0; + status = (*pClientEle->evReceive)( + pClientEle->RcvEvContext, + pConnectEle->ConnectContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &ClientBytesTaken, + pTsdu, + &pIrp); + + CTESpinLockAtDpc(pLowerConn); +#if DBG + if (DebugMore) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(( "Client TOOK %X bytes, pIrp = %X,status =%X\n", + ClientBytesTaken,pIrp,status)); + } +#endif + if (!pLowerConn->pUpperConnection) + { + // the connection was disconnected in the interim + // so do nothing. + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + *BytesTaken = BytesAvailable; + return(STATUS_SUCCESS); + } + } + else + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + ASSERT(pIrp); + // + // the client may pass back a receive in the pIrp. + // In this case pIrp is a valid receive request Irp + // and the status is MORE_PROCESSING + // + + // don't put these lines outside the if incase the client + // does not set ClientBytesTaken when it returns an error + // code... we don't want to use the value then + // + // count the bytes received so far. Most of the bytes + // will be received in the CompletionRcv handler in TdiHndlr.c + pConnectEle->BytesRcvd += ClientBytesTaken; + + // The client has taken some of the data at least... + *BytesTaken += ClientBytesTaken; + + *RcvBuffer = pIrp; + + // ** FAST PATH ** + return(status); + } + else + // + // no irp was returned... the client just took some of the bytes.. + // + if (status == STATUS_SUCCESS) + { + + // count the bytes received so far. + pConnectEle->BytesRcvd += ClientBytesTaken; + *BytesTaken += ClientBytesTaken; + + // + // look at how much data was taken and adjust some counts + // + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + // ** FAST PATH ** + CHECK_PTR(pConnectEle); + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + return(status); + } + else + if (pConnectEle->BytesRcvd > pConnectEle->TotalPcktLen) + { + //IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Too Many Bytes Rcvd!! Rcvd# = %d, TotalLen = %d\n", + pConnectEle->BytesRcvd,pConnectEle->TotalPcktLen)); + + ASSERTMSG("Nbt:Client Took Too Much Data!!!\n",0); + + // + // try to recover by saying that the client took all of the + // data so at least the transport is not confused too + // + *BytesTaken = BytesIndicated; + + } + else + // the client did not take all of the data so + // keep track of the fact + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT:Client took Indication BytesRcvd=%X, TotalLen=%X BytesAvail %X ClientTaken %X\n", + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen, + BytesAvailable, + ClientBytesTaken)); + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement the + // ReceiveIndicated counter which is set in Tdihndlr.c + + } + } + else + if (status == STATUS_DATA_NOT_ACCEPTED) + { + // client has not taken ANY data... + // + // In this case the *BytesTaken is set to 4, the session hdr. + // since we really have taken that data to setup the PduSize + // in the pConnEle structure. + // + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT: Status DATA NOT ACCEPTED returned from client Avail %X %X\n", + BytesAvailable,pConnectEle)); + + // the code in tdihndlr.c normally looks after incrementing + // the ReceiveIndicated count for data that is not taken by + // the client, but if it is a zero length send that code cannot + // detect it, so we put code here to handle that case + // + // It is possible for the client to do a disconnect after + // we release the spin lock on pLowerConn to call the Client's + // disconnect indication. If that occurs, do not overwrite + // the StateProc with PartialRcv + // + if ((pConnectEle->TotalPcktLen == 0) && + (pConnectEle->state == NBT_SESSION_UP)) + { + pLowerConn->StateRcv = PARTIAL_RCV; + SetStateProc( pLowerConn, PartialRcv ) ; + CHECK_PTR(pConnectEle); + pConnectEle->ReceiveIndicated = 0; // zero bytes waiting for client + } + else + { + // + // if any bytes were taken (i.e. the session hdr) then + // return status success. (otherwise the status is + // statusNotAccpeted). + // + if (*BytesTaken) + { + status = STATUS_SUCCESS; + } + } + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement this + // counter. + } + else + ASSERT(0); + + + return(status); + + } +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + else + { + // + // there is no client buffer to pass the data to, so keep + // track of the fact so when the next client buffer comes down + // we can get the data from the transport. + // + KdPrint(("NBT:Client did not have a Buffer posted, rcvs indicated =%X,BytesRcvd=%X, TotalLen=%X\n", + pConnectEle->ReceiveIndicated, + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen)); + + // the routine calling this one increments ReceiveIndicated and sets the + // state to PartialRcv to keep track of the fact that there is data + // waiting in the transport + // + return(STATUS_DATA_NOT_ACCEPTED); + } +#endif +} + +//---------------------------------------------------------------------------- +__inline +VOID +DerefLowerConnFast( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN CTELockHandle OldIrq + ) +/*++ + +Routine Description: + + This routine dereferences the lower connection and if someone has + tried to do that during the execution of the routine that called + this one, the pConnEle is dereferenced too. + +Arguments: + + +Return Value: + + +--*/ + +{ + // NOTE: we do not coordinate with the pConnEle using InRcvHandler any + // more- we just check if pUpperconnection is null or not. + // + //if (pLowerConn->InRcvHandler) + { + // pLowerConn->InRcvHandler = FALSE; + if (pLowerConn->RefCount > 1) + { + // This is the FAST PATH + pLowerConn->RefCount--; + CTESpinFree(pLowerConn,OldIrq); + } + else + { + CTESpinFree(pLowerConn,OldIrq); + NbtDereferenceLowerConnection(pLowerConn); + + } + } +} +//---------------------------------------------------------------------------- +VOID +DpcGetRestOfIndication( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine is called when the client has been indicated with more + data than they will take and there is a rcv buffer on their RcvHead + list when completion rcv runs. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle OldIrq; + tCONNECTELE *pConnEle; + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + tLOWERCONNECTION *pLowerConn=(tLOWERCONNECTION *)Context; + PLIST_ENTRY pEntry; + + CTEMemFree((PVOID)pDpc); + + CTESpinLockAtDpc(&NbtConfig.JointLock); + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + if (!pLowerConn->pUpperConnection || pLowerConn->StateRcv != PARTIAL_RCV) + { + PUSH_LOCATION(0xA4); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + KdPrint(("Nbt:Unable to get an Irp - Closing Connection!!\n",0)); + status = OutOfRsrcKill(pLowerConn); + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + CTESpinLockAtDpc(pLowerConn); + + pConnEle = (tCONNECTELE *)pLowerConn->pUpperConnection; + + if (!IsListEmpty(&pConnEle->RcvHead)) + { + PUSH_LOCATION(0xA5); + pEntry = RemoveHeadList(&pConnEle->RcvHead); + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + // + // call the same routine that the client would call to post + // a recv buffer, except now we are in the PARTIAL_RCV state + // and the buffer will be passed to the transport. + // + status = NTReceive(pLowerConn->pDeviceContext,pIrp); + + + } + else + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + PUSH_LOCATION(0xA6); + } + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + +} + +//---------------------------------------------------------------------------- +VOID +DpcHandleNewSessionPdu ( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls HandleNewSessionPdu from a Dpc started in + NewSessionCompletionRoutine. + +Arguments: + + +Return Value: + + +--*/ + +{ + CTEMemFree((PVOID)pDpc); + + + HandleNewSessionPdu((tLOWERCONNECTION *)Context,(ULONG)SystemArgument1, + (ULONG)SystemArgument2); + +} + +//---------------------------------------------------------------------------- +VOID +HandleNewSessionPdu ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Offset, + IN ULONG ToGet + ) +/*++ + +Routine Description: + + This routine handles the case when a session pdu starts in the middle of + a data indication from the transport. It gets an Irp from the free list + and formulates a receive to pass to the transport to get that data. The + assumption is that the client has taken all data preceding the next session + pdu. If the client hasn't then this routine should not be called yet. + +Arguments: + + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + ULONG BytesTaken; + PIRP pIrp; + PFILE_OBJECT pFileObject; + PMDL pMdl; + ULONG BytesToGet; + tCONNECTELE *pConnEle; + + pIrp = NULL; + BytesTaken = 0; + + // we grab the joint lock because it is needed to separate the lower and + // upper connections, so with it we can check if they have been separated. + // + CTESpinLockAtDpc(&NbtConfig.JointLock); + pConnEle = pLowerConn->pUpperConnection; + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + if (!pLowerConn->pUpperConnection) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + KdPrint(("Nbt:Unable to get an Irp - Closing Connection!!\n",0)); + status = OutOfRsrcKill(pLowerConn); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + CTESpinLockAtDpc(pLowerConn); + // + // be sure the connection has not disconnected in the meantime... + // + if (pLowerConn->State != NBT_SESSION_UP) + { + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + pFileObject = pLowerConn->pFileObject; + + // use the indication buffer for the receive. + pMdl = pLowerConn->pIndicateMdl; + + // this flag is set below so we know if there is data in the indicate buffer + // or not. + if (Offset) + { + PVOID NewAddress; + PMDL pNewMdl; + + // there is still data in the indication buffer ,so only + // fill the empty space. This means adjusting the Mdl to + // to only map the last portion of the Indication Buffer + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pMdl) + + Offset); + + // create a partial MDL so that the new data is copied after the existing data + // in the MDL. + // + // 0 for length means map the rest of the buffer + // + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pMdl,pNewMdl,NewAddress,0); + + pMdl = pNewMdl; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Mapping IndicBuffer to partial Mdl Offset=%X, ToGet=%X %X\n", + Offset,ToGet, + pLowerConn)); + } + else + { + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + } + + // + // Only get the amount of data specified, which is either the 4 byte header + // or the rest of the pdu so that we never have + // more than one session pdu in the indicate buffer. + // + BytesToGet = ToGet; + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pFileObject), + pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pMdl, + (ULONG)TDI_RECEIVE_NORMAL, + BytesToGet); // only ask for the number of bytes left and no more + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(IoGetRelatedDeviceObject(pFileObject),pIrp); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NewSessionCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of the receive to get the remaining + data left in the transport when a session PDU starts in the middle of + an indication from the transport. This routine is run as the completion + of a recv Irp passed to the transport by NBT, to get the remainder of the + data in the transport. + + The routine then calls the normal receive handler, which can either + consume the data or pass back an Irp. If an Irp is passed back then + the data is copied into that irp in this routine. + +Arguments: + + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + ULONG BytesTaken; + tCONNECTELE *pConnEle; + PVOID pData; + KIRQL OldIrq; + PMDL pMdl; + ULONG BytesIndicated; + ULONG BytesAvailable; + PKDPC pDpc; + tLOWERCONNECTION *pLowerConn; + ULONG Length; + ULONG PduLen; + PIRP pRetIrp; + + // we grab the joint lock because it is needed to separate the lower and + // upper connections, so with it we can check if they have been separated. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pLowerConn = (tLOWERCONNECTION *)pContext; + pConnEle = pLowerConn->pUpperConnection; + + CTESpinLockAtDpc(pLowerConn); + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + // + if (!pConnEle) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + status = STATUS_UNSUCCESSFUL; + goto ExitRoutine; + } + + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + + BytesTaken = 0; + + pMdl = pLowerConn->pIndicateMdl; + + pData = MmGetMdlVirtualAddress(pMdl); + + // + // The Indication buffer may have more data in it than what we think + // was left in the transport, because the transport may have received more + // data in the intervening time. Check for this case. + // + if (pIrp->IoStatus.Information > pConnEle->BytesInXport) + { + // no data left in transport + // + CHECK_PTR(pConnEle); + pConnEle->BytesInXport = 0; + } + else + { + // + // subtract what we just retrieved from the transport, from the count + // of data left in the transport + // + pConnEle->BytesInXport -= pIrp->IoStatus.Information; + } + // put the irp back on its free list + CHECK_PTR(pIrp); + pIrp->MdlAddress = NULL; + + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + + // + // there may be data still in the indication buffer, + // so add that amount to what we just received. + // + pLowerConn->BytesInIndicate += (USHORT)pIrp->IoStatus.Information; + BytesIndicated = pLowerConn->BytesInIndicate; + // + // we need to set the bytes available to be the data in the Xport + the + // bytes in the indicate buffer, so that + // ReceiveIndicated gets set to the correct value if the client does + // not take all of data + // + BytesAvailable = pConnEle->BytesInXport + BytesIndicated; + pRetIrp = NULL; + + // if the number of bytes is 4 then we just have the header and must go + // back to the transport for the rest of the pdu, or we have a keep + // alive pdu... + // + // + // This could be a session keep alive pdu so check the pdu type. Keep + // alives just go to the RcvHndlrNotOs routine and return, doing nothing. + // They have a length of zero, so the overall length is 4 and they could + // be confused for session pdus otherwise. + // + status = STATUS_SUCCESS; + if (BytesIndicated == sizeof(tSESSIONHDR)) + { + + PUSH_LOCATION(0x1e) + if (((tSESSIONHDR UNALIGNED *)pData)->Type == NBT_SESSION_MESSAGE) + { + // if there is still data in the transport we must send down an + // irp to get the data, however, if there is no data left in + // the transport, then the data will come up on its own, into + // the indicate_buffer case in the main Receivehandler. + // + if (pConnEle->BytesInXport) + { + PUSH_LOCATION(0x1e); + + // tell the DPC routine to get the data at an offset of 4 for length Length + + // + // this is the first indication to find out how large the pdu is, so + // get the length and go get the rest of the pdu. + // + Length = myntohl(((tSESSIONHDR UNALIGNED *)pData)->UlongLength); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got Pdu Hdr in sessioncmplionroutine, PduLen =%X\n",Length)); + + // it is possible to get a zero length pdu, in which case we + // do NOT need to go to the transport to get more data + // + if (Length) + { + PUSH_LOCATION(0x1e); + // + // now go get this amount of data and add it to the header + // + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('r')); + + ASSERT(pDpc); + + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + + + CTESpinFree(pLowerConn,OldIrq); + + // check that the pdu is not going to overflow the indicate buffer. + // + if (Length > NBT_INDICATE_BUFFER_SIZE - sizeof(tSESSIONHDR)) + { + Length = NBT_INDICATE_BUFFER_SIZE - sizeof(tSESSIONHDR); + } + + ASSERTMSG("Nbt:Getting ZERO bytes from Xport!!\n",Length); + + KeInsertQueueDpc(pDpc,(PVOID)sizeof(tSESSIONHDR),(PVOID)Length); + + // clean up the partial mdl since we are going to turn around and reuse + // it in HandleNewSessionPdu above.. + // + // THIS CALL SHOULD NOT BE NEEDED SINCE THE INDICATE BUFFER IS NON_PAGED + // POOL +// MmPrepareMdlForReuse(pConnEle->pNewMdl); + + // return this status to stop to tell the io subsystem to stop processing + // this irp when we return it. + // + return(STATUS_MORE_PROCESSING_REQUIRED); + } + } + + + } + } + + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:NewSessComplRcv BytesinXport= %X,InIndicate=%X Indic. %X,Avail=%X %X\n", + pConnEle->BytesInXport,pLowerConn->BytesInIndicate,BytesIndicated, + BytesAvailable,pConnEle->pLowerConnId)); + + + if (!NT_SUCCESS(pIrp->IoStatus.Status)) + { + ASSERTMSG("Nbt:Not Expecting a Bad Status Code\n",0); + goto ExitRoutine; + } + + // + // check if we have a whole pdu in the indicate buffer or not. IF not + // then just return and wait for more data to hit the TdiReceiveHandler + // code. This check passes KeepAlives correctly since they have a pdu + // length of 0, and adding the header gives 4, their overall length. + // + PduLen = myntohl(((tSESSIONHDR UNALIGNED *)pData)->UlongLength); + if ((BytesIndicated < PduLen + sizeof(tSESSIONHDR)) && + (BytesIndicated != NBT_INDICATE_BUFFER_SIZE)) + + { + PUSH_LOCATION(0x1f); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Returning in NewSessionCompletion BytesIndicated = %X\n", + BytesIndicated)); + } + else + { + PUSH_LOCATION(0x20); + + status = CopyDataandIndicate( + NULL, + (PVOID)pLowerConn, + 0, // rcv flags + BytesIndicated, + BytesAvailable, + &BytesTaken, + pData, + (PVOID)&pRetIrp); + +// status = STATUS_MORE_PROCESSING_REQUIRED; + + } + +ExitRoutine: + // + // check if an irp is passed back, so we don't Deref in that case. + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection + // + PUSH_LOCATION(0x51); + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + } + else + { + CTESpinFree(pLowerConn,OldIrq); + } + + + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NtBuildIndicateForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine sets up the indicate buffer to get data from the transport + when the indicate buffer already has some data in it. A partial MDL is + built and the attached to the irp. + before we indicate. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PIRP pIrp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + PMDL pNewMdl; + PVOID NewAddress; + + // + // get an Irp from the list + // + + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Unable to get Irp, Kill connection\n")); + + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pConnEle= pLowerConn->pUpperConnection; + + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl) + + pLowerConn->BytesInIndicate); + + // create a partial MDL so that the new data is copied after the existing data + // in the MDL. + // + // 0 for length means map the rest of the buffer + // + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pLowerConn->pIndicateMdl,pNewMdl,NewAddress,0); + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pLowerConn->pFileObject), + pLowerConn->pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pNewMdl, + (ULONG)TDI_RECEIVE_NORMAL, + Length); + + // + // we need to set the next Irp stack location because this irp is returned + // as a return parameter rather than being passed through IoCallDriver + // which increments the stack location itself + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + *ppIrp = (PVOID)pIrp; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NtBuildIrpForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine gets an Irp to be used to receive data and hooks the indication + Mdl to it, so we can accumulate at least 128 bytes of data for the client + before we indicate. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PIRP pIrp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Unable to get Irp, Kill connection\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pLowerConn->pFileObject), + pLowerConn->pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pLowerConn->pIndicateMdl, + (ULONG)TDI_RECEIVE_NORMAL, + Length); + + // + // we need to set the next Irp stack location because this irp is returned + // as a return parameter rather than being passed through IoCallDriver + // which increments the stack location itself + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + *ppIrp = (PVOID)pIrp; + + return(STATUS_SUCCESS); +} + +#pragma inline_depth(0) +//---------------------------------------------------------------------------- +NTSTATUS +CopyDataandIndicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + + This routine combines data indicated with the indicate buffer to + indicate the total to the client. Any bytes Indicated are those bytes + in the indicate buffer. Bytes available adds in any bytes in the transport. + + The idea here is to copy as much as possible from the indicate buffer and + then pass back an irp if there is still more data in the transport. If + no data left in the transport, this routine completes the client irp and + returns STATUS_SUCCESS. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnEle; + ULONG BytesCopied; + ULONG Indicated; + ULONG Available; + ULONG Taken; + ULONG AmountAlreadyInIndicateBuffer; + PVOID pBuffer; + PIRP pIrp; + BOOLEAN bReIndicate=FALSE; + ULONG RemainingPdu; + ULONG ToCopy; + PKDPC pDpc; + ULONG SaveInXport; + ULONG PduSize; + + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pConnEle = pLowerConn->pUpperConnection; + + AmountAlreadyInIndicateBuffer = pLowerConn->BytesInIndicate; + + // + // set the parameters for the call to the TdiReceiveHandler routine + // + + Indicated = BytesIndicated; + Available = BytesAvailable; + Taken = 0; + + + ASSERT(pLowerConn->StateRcv == INDICATE_BUFFER); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Amount In Indicate = %X\n",AmountAlreadyInIndicateBuffer)); + + // now that we have 128 bytes (plus the session hdr = 132 total) we + // can indicate to the client + + pBuffer = MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:FromCopyData, BytesAvail= %X,BytesInd= %X,BytesRcvd= %X,Amount=%X, %X,state=%X,RcvEC=%X\n", + Available,Indicated,pConnEle->BytesRcvd, + AmountAlreadyInIndicateBuffer,pLowerConn,pLowerConn->StateRcv, + ReceiveEventContext)); + + pIrp = NULL; + + // + // Reset this count so that the routine processes the Session header correctly + // + CHECK_PTR(pConnEle); + pConnEle->BytesRcvd = 0; + PUSH_LOCATION(0x21); + status = RcvHandlrNotOs( + NULL, + ConnectionContext, + ReceiveFlags, + Indicated, + Available, + &Taken, + pBuffer, + (PVOID)&pIrp + ); + + // + // if the connection has disonnected, then just return + // + if (!pLowerConn->pUpperConnection) + { + *BytesTaken = BytesAvailable; + return(STATUS_SUCCESS); + } + + // do not use pConnEle->TotalPcktLen here becauase it won't be set for + // keep alives - must use actual buffer to get length. + PduSize = myntohl(((tSESSIONHDR UNALIGNED *)pBuffer)->UlongLength) + sizeof(tSESSIONHDR); + + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + + if (Taken <= pLowerConn->BytesInIndicate) + { + pLowerConn->BytesInIndicate -= (USHORT)Taken; + } + else + { + pLowerConn->BytesInIndicate = 0; + } + + if (pIrp) + { + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + ULONG ClientRcvLen; + + PUSH_LOCATION(0x22); + // + // BytesInXport will be recalculated by ProcessIrp based on BytesAvailable + // and the ClientRcvLength, so set it to 0 here. + // + SaveInXport = pConnEle->BytesInXport; + CHECK_PTR(pConnEle); + pConnEle->BytesInXport = 0; + status = ProcessIrp(pLowerConn, + pIrp, + pBuffer, + &Taken, + Indicated, + Available); + + // + // copy the data in the indicate buffer that was not taken by the client + // into the MDL and then update the bytes taken and pass the irp on downwar + // to the transport + // + ToCopy = Indicated - Taken; + + // the Next stack location has the correct info in it because we + // called TdiRecieveHandler with a null ReceiveEventContext, + // so that routine does not increment the stack location + // + pIrpSp = IoGetNextIrpStackLocation(pIrp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + ClientRcvLen = pParams->ReceiveLength; + + // did the client's Pdu fit entirely into the indication buffer? + // + if (ClientRcvLen <= ToCopy) + { + PUSH_LOCATION(0x23); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Took some(or all) RemainingPdu= %X, ClientRcvLen= %X,InXport=%X %X\n", + RemainingPdu,ClientRcvLen,pConnEle->BytesInXport,pLowerConn)); + + // if ProcessIrp has recalculated the bytes in the Xport + // then set it back to where it should be, Since ProcessIrp will + // put all not taken bytes as bytes in the transport - but some + // of the bytes are still in the indicate buffer. + // + pConnEle->BytesInXport = SaveInXport; + + // it could be a zero length send where the client returns a null + // mdl, or the client returns an mdl and the RcvLen is really zero. + // + if (pIrp->MdlAddress && ClientRcvLen) + { + TdiCopyBufferToMdl(pBuffer, // indicate buffer + Taken, // src offset + ClientRcvLen, + pIrp->MdlAddress, + 0, // dest offset + &BytesCopied); + } + else + BytesCopied = 0; + + // + // check for data still in the transport - subtract data copied to + // Irp, since Taken was already subtracted. + // + pLowerConn->BytesInIndicate -= (USHORT)BytesCopied; + + *BytesTaken = Taken + BytesCopied; + ASSERT(BytesCopied == ClientRcvLen); + + // the client has received all of the data, so complete his irp + // + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // since we are completing it and TdiRcvHandler did not set the next + // one. + // + ASSERT(pIrp->CurrentLocation > 1); + + // since we are completing the irp here, no need to call + // this, because it will complete through completionrcv. + IoSetNextIrpStackLocation(pIrp); + + // there should not be any data in the indicate buffer since it + // only holds either 132 bytes or a whole pdu unless the client + // receive length is too short... + // + if (pLowerConn->BytesInIndicate) + { + PUSH_LOCATION(0x23); + // when the irp goes through completionRcv it should set the + // state to PartialRcv and the next posted buffer from + // the client should pickup this data. + CopyToStartofIndicate(pLowerConn,(Taken+BytesCopied)); + } + else + { + // + // this will complete through CompletionRcv and for that + // reason it will get any more data left in the transport. The + // Completion routine will set the correct state for the rcv when + // it processes this Irp ( to INDICATED, if needed). ProcessIrp + // may have set ReceiveIndicated, so that CompletionRcv will + // set the state to PARTIAL_RCV when it runs. + // + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + + CTESpinFreeAtDpc(pLowerConn); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTESpinLockAtDpc(pLowerConn); + // + // this was undone by CompletionRcv, so redo them, since the + // caller will undo them again. + // + pLowerConn->RefCount++; + + return(STATUS_SUCCESS); + } + else + { + + PUSH_LOCATION(0x24); + // + // there is still data that we need to get to fill the PDU. There + // may be more data left in the transport or not after the irp is + // filled. + // In either case the Irps' Mdl must be adjusted to account for + // filling part of it. + // + TdiCopyBufferToMdl(pBuffer, // IndicateBuffer + Taken, // src offset + ToCopy, + pIrp->MdlAddress, + 0, // dest offset + &BytesCopied); + + // + // save the Mdl so we can reconstruct things later + // + pLowerConn->pMdl = pIrp->MdlAddress; + pConnEle->pNextMdl = pIrp->MdlAddress; + ASSERT(pIrp->MdlAddress); + // + // The irp is being passed back to the transport, so we NULL + // our ptr to it so we don't try to cancel it on a disconnect + // + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + // Adjust the number of bytes in the Mdl chain so far since the + // completion routine will only count the bytes filled in by the + // transport + // + pConnEle->BytesRcvd += BytesCopied; + + *BytesTaken = BytesIndicated; + + // + // clear the number of bytes in the indicate buffer since the client + // has taken more than the data left in the Indicate buffer + // + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + + // decrement the client rcv len by the amount already put into the + // client Mdl + // + ClientRcvLen -= BytesCopied; + // + // if ProcessIrp did recalculate the bytes in the transport + // then set back to what it was. Process irp will do this + // recalculation if the clientrcv buffer is too short only. + // + pConnEle->BytesInXport = SaveInXport; + + // + // adjust the number of bytes downward due to the client rcv + // buffer + // + if (ClientRcvLen < SaveInXport) + { + PUSH_LOCATION(0x24); + pConnEle->BytesInXport -= ClientRcvLen; + } + else + { + pConnEle->BytesInXport = 0; + } + + // ProcessIrp will set bytesinXport and ReceiveIndicated - since + // the indicate buffer is empty that calculation of BytesInXport + // will be correct. + // + + // We MUST set the state to FILL_IRP so that completion Rcv + // undoes the partial MDL stuff - i.e. it puts the original + // MdlAddress in the Irp, rather than the partial Mdl address. + // CompletionRcv will set the state to partial Rcv if ReceiveIndicated + // is not zero. + // + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + + // the client is going to take more data from the transport with + // this Irp. Set the new Rcv Length that accounts for the data just + // copied to the Irp. + // + pParams->ReceiveLength = ClientRcvLen; + + // keep track of data in MDL so we know when it is full and we need to + // return it to the user - ProcessIrp set it to ClientRcvLen, so + // shorten it here. + // + pConnEle->FreeBytesInMdl -= BytesCopied; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:ClientRcvLen = %X, LeftinXport= %X RemainingPdu= %X %X\n",ClientRcvLen, + pConnEle->BytesInXport,RemainingPdu,pLowerConn)); + + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + MakePartialMdl(pConnEle,pIrp,BytesCopied); + + *ppIrp = pIrp; + + // increments the stack location, since TdiReceiveHandler did not. + // + if (ReceiveEventContext) + { + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + // pass the Irp to the transport since we were called from + // NewSessionCompletionRoutine + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Calling IoCallDriver\n")); + ASSERT(pIrp->CurrentLocation > 1); + + CTESpinFreeAtDpc(pLowerConn); + CHECK_COMPLETION(pIrp); + IoCallDriver(IoGetRelatedDeviceObject(pLowerConn->pFileObject),pIrp); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + + } + } + else + { + PUSH_LOCATION(0x54); + // + // no Irp passed back, the client just took some or all of the data + // + *BytesTaken = Taken; + pLowerConn->BytesRcvd += Taken - sizeof(tSESSIONHDR); + + ASSERT(*BytesTaken < 0x7FFFFFFF ); + + // + // if more than the indicate buffer is taken, then the client + // is probably trying to say it doesn't want any more of the + // message. + // + if (Taken > BytesIndicated) + { + // + // in this case the client has taken more than the indicated. + // We set bytesavailable to the message length in RcvHndlrNotOs, + // so the client has probably said BytesTaken=BytesAvailable. + // So kill the connection + // because we have no way of handling this case here, since + // part of the message may still be in the transport, and we + // might have to send the indicate buffer down there multiple + // times to get all of it...a mess! The Rdr only sets bytestaken = + // bytesAvailable under select error conditions anyway. + // + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + *BytesTaken = BytesAvailable; + + } + else + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // this may be a zero length send -that the client has + // decided not to accept. If so then the state will be set + // to PartialRcv. In this case do NOT go down to the transport + // and get the rest of the data, but wait for the client + // to post a rcv buffer. + // + PUSH_LOCATION(0x54); + return(STATUS_SUCCESS); + } + else + if (Taken == PduSize) + { + // + // Must have taken all of the pdu data, so check for + // more data available - if so send down the indicate + // buffer to get it. + // + if (pConnEle->BytesInXport) + { + PUSH_LOCATION(0x28); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:CopyData BytesInXport= %X, %X\n",pConnEle->BytesInXport, + pLowerConn)); + + // + // there is still data in the transport so Q a Dpc to use + // the indicate buffer to get the data + // + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('s')); + + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + // get just the header first to see how large the pdu is + // + pLowerConn->RefCount++; + KeInsertQueueDpc(pDpc,NULL,(PVOID)sizeof(tSESSIONHDR)); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + + } + else + { + PUSH_LOCATION(0x29); + // + // clear the flag saying that we are using the indicate buffer + // + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + + } + + PUSH_LOCATION(0x2a); + return(STATUS_SUCCESS); + + } + else + { + // + // the client may have taken all the data in the + // indication!!, in which case return status success + // Note: that we check bytes available here not bytes + // indicated - since the client could take all indicated + // data but still leave data in the transport. If the client + // got told there was more available but only took the indicated, + // the we need to do the else and track ReceiveIndicated, but if + // Indicated == Available, then we take the if and wait for + // another indication from the transport. + // + if (Taken == BytesAvailable) + { + PUSH_LOCATION(0x4); + status = STATUS_SUCCESS; + + } + else + { + + // did not take all of the data in the Indication + // + + PUSH_LOCATION(0x2b); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Took Part of indication... BytesRemaining= %X, LeftInXport= %X, %X\n", + pLowerConn->BytesInIndicate,pConnEle->BytesInXport,pLowerConn)); + + // + // The amount of data Indicated to the client should not exceed + // the Pdu size, so check that, since this routine could get + // called with bytesAvailable > than the Pdu size. + // + // That is checked above where we check if Taken > BytesIndicated. + + SaveInXport = pConnEle->BytesInXport; + ASSERT(Taken <= PduSize); + status = ClientTookSomeOfTheData(pLowerConn, + Indicated, + Available, + Taken, + PduSize); + + // + // Since the data may be divided between some in the transport + // and some in the indicate buffer do not let ClientTookSomeOf... + // recalculate the amount in the transport, since it assumes all + // untaken data is in the transport. Since the client did not + // take of the indication, the Bytes in Xport have not changed. + // + pConnEle->BytesInXport = SaveInXport; + // + // need to move the data forward in the indicate buffer so that + // it begins at the start of the buffer + // + if (Taken) + { + CopyToStartofIndicate(pLowerConn,Taken); + } + + } + } + + } + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiConnectHandler ( + IN PVOID pConnectEventContext, + IN int RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN PVOID UNALIGNED pUserData, + IN int OptionsLength, + IN PVOID pOptions, + OUT CONNECTION_CONTEXT *pConnectionContext, + OUT PIRP *ppAcceptIrp + ) +/*++ + +Routine Description: + + This routine is connect event handler. It is invoked when a request for + a connection has been received by the provider. NBT accepts the connection + on one of its connections in its LowerConnFree list + + Initially a TCP connection is setup with this port. Then a Session Request + packet is sent across the connection to indicate the name of the destination + process. This packet is received in the RcvHandler. + +Arguments: + + pConnectEventContext - the context passed to the transport when this event was setup + RemoteAddressLength - the length of the source address (4 bytes for IP) + pRemoteAddress - a ptr to the source address + UserDataLength - the number of bytes of user data - includes the session Request hdr + pUserData - ptr the the user data passed in + OptionsLength - number of options to pass in + pOptions - ptr to the options + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT pFileObject; + PIRP pRequestIrp; + CONNECTION_CONTEXT pConnectionId; + tDEVICECONTEXT *pDeviceContext; + + *pConnectionContext = NULL; + + // convert the context value into the device context record ptr + pDeviceContext = (tDEVICECONTEXT *)pConnectEventContext; + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("pDeviceContxt = %X ConnectEv = %X",pDeviceContext,pConnectEventContext)); + ASSERTMSG("Bad Device context passed to the Connection Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // call the non-OS specific routine to find a free connection. + + status = ConnectHndlrNotOs( + pConnectEventContext, + RemoteAddressLength, + pRemoteAddress, + UserDataLength, + pUserData, + &pConnectionId); + + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("NO FREE CONNECTIONS in connect handler\n")); + + // put the Irp back on its free list + // + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_DATA_NOT_ACCEPTED); + } + + pFileObject = ((tLOWERCONNECTION *)pConnectionId)->pFileObject; + + TdiBuildAccept( + pRequestIrp, + IoGetRelatedDeviceObject(pFileObject), + pFileObject, + AcceptCompletionRoutine, + (PVOID)pConnectionId, + NULL, + NULL); + + // we need to null the MDL address because the transport KEEPS trying to + // release buffers!! which do not exist!!! + // + CHECK_PTR(pRequestIrp); + pRequestIrp->MdlAddress = NULL; + + + // return the connection id to accept the connect indication on. + *pConnectionContext = (CONNECTION_CONTEXT)pConnectionId; + *ppAcceptIrp = pRequestIrp; + // + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with Connect Indication. + // + ASSERT(pRequestIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pRequestIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +AcceptCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of an Accept to the transport. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + tLOWERCONNECTION *pLowerConn; + CTELockHandle OldIrq; + tDEVICECONTEXT *pDeviceContext; + + pLowerConn = (tLOWERCONNECTION *)pContext; + pDeviceContext = pLowerConn->pDeviceContext; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pLowerConn); + // + // if the connection disconnects before the connect accept irp (this irp) + // completes do not put back on the free list here but let nbtdisconnect + // handle it. + // (i.e if the state is no longer INBOUND, then don't touch the connection + // + if ((!NT_SUCCESS(pIrp->IoStatus.Status)) && + (pLowerConn->State == NBT_SESSION_INBOUND)) + { + + // + // the accept failed, so close the connection and create + // a new one to be sure all activity is run down on the connection. + // + + pLowerConn->State = NBT_IDLE; + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + KdPrint(("AcceptCompletionRoutine: error: %lx\n", pIrp->IoStatus.Status)); + + CTEQueueForNonDispProcessing( + NULL, + pLowerConn, + NULL, + CleanupAfterDisconnect, + pDeviceContext); + + PUSH_LOCATION(0x93); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + + // put the Irp back on its free list + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is not initiating thread - we are the initiator + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiDisconnectHandler ( + IN PVOID EventContext, + IN PVOID ConnectionContext, + IN ULONG DisconnectDataLength, + IN PVOID pDisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID pDisconnectInformation, + IN ULONG DisconnectIndicators + ) +/*++ + +Routine Description: + + This routine is called when a session is disconnected from a remote + machine. + +Arguments: + + IN PVOID EventContext, + IN PCONNECTION_CONTEXT ConnectionContext, + IN ULONG DisconnectDataLength, + IN PVOID DisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectIndicators + +Return Value: + + NTSTATUS - Status of event indicator + +--*/ + +{ + + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext; + + // convert the context value into the device context record ptr + pDeviceContext = (tDEVICECONTEXT *)EventContext; + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("pDeviceContxt = %X ConnectEv = %X\n",pDeviceContext,ConnectionContext)); + ASSERTMSG("Bad Device context passed to the Connection Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + // call the non-OS specific routine to find a free connection. + + status = DisconnectHndlrNotOs( + EventContext, + ConnectionContext, + DisconnectDataLength, + pDisconnectData, + DisconnectInformationLength, + pDisconnectInformation, + DisconnectIndicators); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("NO FREE CONNECTIONS in connect handler\n")); + return(STATUS_DATA_NOT_ACCEPTED); + } + + + return status; + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiRcvDatagramHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT PIRP *pIoRequestPacket + ) +/*++ + +Routine Description: + + This routine is the receive datagram event indication handler. + + It is called when an Datagram arrives from the network, it will look for a + the address with an appropriate read datagram outstanding or a Datagrm + Event handler setup. + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tDGRAMHDR UNALIGNED *pDgram = (tDGRAMHDR UNALIGNED *)pTsdu; + PIRP pIrp = NULL; + ULONG lBytesTaken; + tCLIENTLIST *pClientList; + + + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT receive datagram handler pDeviceContext: %X\n", + pDeviceContext )); + + *pIoRequestPacket = NULL; + + ASSERTMSG("NBT:Invalid Device Context passed to DgramRcv Handler!!\n", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + // call a non-OS specific routine to decide what to do with the datagrams + pIrp = NULL; + pClientList = NULL; + status = DgramHndlrNotOs( + pDgramEventContext, + SourceAddressLength, + pSourceAddress, + OptionsLength, + pOptions, + ReceiveDatagramFlags, + BytesIndicated, + BytesAvailable, + &lBytesTaken, + pTsdu, + (PVOID *)&pIrp, + &pClientList); + + + if ( !NT_SUCCESS(status) ) + { + // fail the request back to the transport provider since we + // could not find a receive buffer or receive handler or the + // data was taken in the indication handler. + // + return(STATUS_DATA_NOT_ACCEPTED); + + } + else + { + // a rcv buffer was returned, so use it for the receive.(an Irp) + PTDI_REQUEST_KERNEL_RECEIVEDG pParams; + PIO_STACK_LOCATION pIrpSp; + ULONG lRcvLength; + ULONG lRcvFlags; + + // When the client list is returned, we need to make up an irp to + // send down to the transport, which we will use in the completion + // routine to copy the data to all clients, ONLY if we are not + // using a client buffer, so check that flag first. + // + if (pClientList && !pClientList->fUsingClientBuffer) + { + PMDL pMdl; + PVOID pBuffer; + + // + // get an irp to do the receive with and attach + // a buffer to it. + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + return(STATUS_DATA_NOT_ACCEPTED); + + // + // make an Mdl for a buffer to get all of the data from + // the transprot + // + pBuffer = NbtAllocMem(BytesAvailable,NBT_TAG('t')); + + pMdl = NULL; + if (pBuffer) + { + // Allocate a MDL and set the header sizes correctly + pMdl = IoAllocateMdl( + pBuffer, + BytesAvailable, + FALSE, + FALSE, + NULL); + + } + if (!pMdl) + { + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + if (pBuffer) + CTEMemFree(pBuffer); + + //ASSERT(pMdl); + KdPrint(("Nbt:Unable to get Mdl, Kill Connection\n")); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + pIrp->MdlAddress = pMdl; + ASSERT(pMdl); + + lRcvFlags = 0; + + lRcvLength = BytesAvailable; + } + else + { + ASSERT(pIrp); + // *TODO* may have to keep track of the case where the + // client returns a buffer that is not large enough for all of the + // data indicated. So the next posting of a buffer gets passed + // directly to the transport. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + lRcvFlags = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters)->ReceiveFlags; + + lRcvLength = ((PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters)->ReceiveLength; + + if (lRcvLength < BytesIndicated - lBytesTaken) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("Nbt:Clients Buffer is too short on Rcv Dgram size= %X, needed = %X\n", + lRcvLength, BytesIndicated-lBytesTaken)); + } + } + + + // this code is sped up somewhat by expanding the code here rather than calling + // the TdiBuildReceive macro + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->CompletionRoutine = CompletionRcvDgram; + + // pass the ClientList to the completion routine so it can + // copy the datagram to several clients that may be listening on the + // same name + // + pIrpSp->Context = (PVOID)pClientList; + CHECK_PTR(pIrpSp); + pIrpSp->Flags = 0; + + // set flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE_DATAGRAM; + pIrpSp->DeviceObject = pDeviceContext->pDgramDeviceObject; + pIrpSp->FileObject = pDeviceContext->pDgramFileObject; + + pParams = (PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters; + pParams->ReceiveFlags = lRcvFlags; + pParams->ReceiveLength = lRcvLength; + + // pass back the irp to the transport provider and increment the stack + // location so it can write to the irp if it needs to. + *pIoRequestPacket = pIrp; + *pBytesTaken = lBytesTaken; + + return(STATUS_MORE_PROCESSING_REQUIRED); + + } + + // + // Transport will complete the processing of the request, we don't + // want the datagram. + // + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT receive datagram handler ignored receive, pDeviceContext: %X\n", + pDeviceContext )); + + return STATUS_DATA_NOT_ACCEPTED; + + // to keep the compiler from generating warnings... + UNREFERENCED_PARAMETER( SourceAddressLength ); + UNREFERENCED_PARAMETER( BytesIndicated ); + UNREFERENCED_PARAMETER( BytesAvailable ); + UNREFERENCED_PARAMETER( pBytesTaken ); + UNREFERENCED_PARAMETER( pTsdu ); + UNREFERENCED_PARAMETER( OptionsLength ); + UNREFERENCED_PARAMETER( pOptions ); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiRcvNameSrvHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT PIRP *pIoRequestPacket + ) +/*++ + +Routine Description: + + This routine is the Name Service datagram event indication handler. + It gets all datagrams destined for UDP port 137 + + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tNAMEHDR UNALIGNED *pNameSrv = (tNAMEHDR UNALIGNED *)pTsdu; + USHORT OpCode; + + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT: NAMEHDR datagram handler pDeviceContext: %X\n", + pDeviceContext )); + + *pIoRequestPacket = NULL; + // + // check if the whole datagram has arrived yet + // + if (BytesIndicated != BytesAvailable) + { + PIRP pIrp; + PVOID pBuffer; + PMDL pMdl; + ULONG Length; + + // + // get an irp to do the receive with and attach + // a buffer to it. + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + return(STATUS_DATA_NOT_ACCEPTED); + + // + // make an Mdl for a buffer to get all of the data from + // the transprot + // + Length = BytesAvailable + SourceAddressLength + sizeof(ULONG); + Length = ((Length + 3)/sizeof(ULONG)) * sizeof(ULONG); + pBuffer = NbtAllocMem(Length,NBT_TAG('u')); + if (pBuffer) + { + PVOID pSrcAddr; + + // + // save the source address and length in the buffer for later + // indication back to this routine. + // + *(ULONG UNALIGNED *)((PUCHAR)pBuffer + BytesAvailable) = SourceAddressLength; + pSrcAddr = (PVOID)((PUCHAR)pBuffer + BytesAvailable + sizeof(ULONG)); + + CTEMemCopy(pSrcAddr, + pSourceAddress, + SourceAddressLength); + + // Allocate a MDL and set the header sizes correctly + pMdl = IoAllocateMdl( + pBuffer, + BytesAvailable, + FALSE, + FALSE, + NULL); + + if (pMdl) + { + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + pIrp->MdlAddress = pMdl; + ASSERT(pMdl); + + TdiBuildReceive( + pIrp, + &pDeviceContext->DeviceObject, + pDeviceContext->pNameServerFileObject, + NameSrvCompletionRoutine, + (PVOID)BytesAvailable, + pMdl, + (ULONG)TDI_RECEIVE_NORMAL, + BytesAvailable); + + *pBytesTaken = 0; + + *pIoRequestPacket = pIrp; + + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + + } + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + return(STATUS_DATA_NOT_ACCEPTED); + + } + + if (pWinsInfo) + { + USHORT TransactionId; + ULONG SrcAddress; + + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&((PTRANSPORT_ADDRESS)pSourceAddress)->Address[0].Address[0])->in_addr); + // + // Pass To Wins if: + // + // 1) It is a response pdu with the transaction id in the WINS range + // that is not a WACK... OR + // 2) It is a request that is NOT broadcast....and... + // 2) It is a name query(excluding node status requests), + // Allowing queries from other netbt clients + // allowing queries from anyone not on this machine OR + // 3) It is a name release request. OR + // 4) It is a name refresh OR + // 5) It is a name registration + // + OpCode = pNameSrv->OpCodeFlags; + TransactionId = ntohs(pNameSrv->TransactId); + + if (((OpCode & OP_RESPONSE) && + (TransactionId <= WINS_MAXIMUM_TRANSACTION_ID) && + (OpCode != OP_WACK)) + || + (!(OpCode & (OP_RESPONSE | FL_BROADCAST)) && + ( + (((OpCode & NM_FLAGS_MASK) == OP_QUERY) && + (OpCode & FL_RECURDESIRE) && // not node status request + ((TransactionId > WINS_MAXIMUM_TRANSACTION_ID) || + ( !SrcIsUs(SrcAddress) ))) +// (SrcAddress != pDeviceContext->lNameServerAddress ))) + || + (OpCode & (OP_RELEASE | OP_REFRESH)) + || + (OpCode & OP_REGISTRATION) )) ) + { + status = PassNamePduToWins( + pDeviceContext, + pSourceAddress, + pNameSrv, + BytesIndicated); + +// NbtConfig.DgramBytesRcvd += BytesIndicated; + + // + // if WINS took the data then tell the transport to dump the data + // since we have buffered it already. Otherwise, let nbt take + // a look at the data + // + if (NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + } + } + + { + + // DO a quick check of the name to see if it is in the local name table + // and reject it otherwise - for name queries only, if not the proxy + // + if ((BytesIndicated >= NBT_MINIMUM_QUERY) && !(NodeType & PROXY)) + { + ULONG UNALIGNED *pHdr; + ULONG i,lValue; + UCHAR pNameStore[NETBIOS_NAME_SIZE]; + UCHAR *pName; + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq; + + // it must be a name query request, not a response, and not a + // node status request, to enter this special check + // + OpCode = pNameSrv->OpCodeFlags; + if (((OpCode & NM_FLAGS_MASK) == OP_QUERY) && + (!(OpCode & OP_RESPONSE)) && + (OpCode & FL_RECURDESIRE)) // not node status request + { + pHdr = (ULONG UNALIGNED *)pNameSrv->NameRR.NetBiosName; + pName = pNameStore; + + // the Half Ascii portion of the netbios name is always 32 bytes long + for (i=0; i < NETBIOS_NAME_SIZE*2 ;i +=4 ) + { + lValue = *pHdr - 0x41414141; // four A's + pHdr++; + lValue = ((lValue & 0x0F000000) >> 16) + + ((lValue & 0x0F0000) >> 4) + + ((lValue & 0x0F00) >> 8) + + ((lValue & 0x0F) << 4); + *(PUSHORT)pName = (USHORT)lValue; + ((PUSHORT)pName)++; + + } + CTESpinLock(&NbtConfig.JointLock,OldIrq); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pNameStore, + NULL, + &pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (!NT_SUCCESS(status)) + { + *pBytesTaken = BytesIndicated; + return(STATUS_DATA_NOT_ACCEPTED); + } + } + } + + + // call a non-OS specific routine to decide what to do with the datagrams + status = NameSrvHndlrNotOs( + pDeviceContext, + pSourceAddress, + pNameSrv, + BytesIndicated, + (BOOLEAN)((ReceiveDatagramFlags & TDI_RECEIVE_BROADCAST) != 0)); + +// NbtConfig.DgramBytesRcvd += BytesIndicated + + } + + return status; + + // to keep the compiler from generating warnings... + UNREFERENCED_PARAMETER( SourceAddressLength ); + UNREFERENCED_PARAMETER( BytesIndicated ); + UNREFERENCED_PARAMETER( BytesAvailable ); + UNREFERENCED_PARAMETER( pBytesTaken ); + UNREFERENCED_PARAMETER( pTsdu ); + UNREFERENCED_PARAMETER( OptionsLength ); + UNREFERENCED_PARAMETER( pOptions ); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NameSrvCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine handles the case when a name service datagram is too + short and and Irp has to be passed back to the transport to get the + rest of the datagram. The irp completes through here when full. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PIRP pIoRequestPacket; + ULONG BytesTaken; + ULONG Offset = (ULONG)Context; + PVOID pBuffer; + ULONG SrcAddressLength; + PVOID pSrcAddress; + + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(("NameSrvCompletionRoutine pRcvBuffer: %X, Status: %X Length %X\n", + Context, + pIrp->IoStatus.Status, + pIrp->IoStatus.Information )); + + pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + SrcAddressLength = *(ULONG UNALIGNED *)((PUCHAR)pBuffer + Offset); + pSrcAddress = (PVOID)((PUCHAR)pBuffer + Offset + sizeof(ULONG)); + // + // just call the regular indication routine as if UDP had done it. + // + TdiRcvNameSrvHandler( + DeviceObject, + SrcAddressLength, + pSrcAddress, + 0, + NULL, + TDI_RECEIVE_NORMAL, + pIrp->IoStatus.Information, + pIrp->IoStatus.Information, + &BytesTaken, + pBuffer, + &pIoRequestPacket ); + + CTEMemFree(MmGetSystemAddressForMdl(pIrp->MdlAddress)); + + IoFreeMdl(pIrp->MdlAddress); + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRcvDgram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine completes the Irp by removing the Rcv Element off the queue + and putting it back on the free list. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PVOID pRemoteAddress; + ULONG RemoteAddressLength; + ULONG BytesCopied; + PVOID pTsdu; + ULONG ReceiveFlags; + tCLIENTLIST *pClientList; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + ULONG ClientBytesTaken; + ULONG DataLength; + tADDRESSELE *pAddress; + tRCVELE *pRcvEle; + PLIST_ENTRY pRcvEntry; + tDEVICECONTEXT *pDeviceContext; + CTEULONGLONG AdapterNumber; + + + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(("CompletionRcvDgram pRcvBuffer: %X, Status: %X Length %X\n", + Context, + Irp->IoStatus.Status, + Irp->IoStatus.Information )); + + + // there may be several clients that want to see this datagram so check + // the client list to see... + // + if (Context) + { + tCLIENTELE *pClientPrev = NULL; + + DataLength = Irp->IoStatus.Information; + + pTsdu = MmGetSystemAddressForMdl(Irp->MdlAddress); + + pClientList = (tCLIENTLIST *)Context; + + // for the multihomed host, we only want to distribute the inbound + // datagram to clients on this same adapter, to avoid giving the + // datagram to the same client several times, once for each adapter + // it is bound to. + // + pDeviceContext = pClientList->pClientEle->pDeviceContext; + AdapterNumber = pDeviceContext->AdapterNumber; + + pAddress = pClientList->pAddress; + pRemoteAddress = pClientList->pRemoteAddress; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pHead = &pClientList->pAddress->ClientHead; + pEntry = pHead->Flink; + RemoteAddressLength = pClientList->RemoteAddressLength; + ReceiveFlags = pClientList->ReceiveDatagramFlags; + +#ifdef PROXY_NODE + if (!pClientList->fProxy) + { +#endif + if (!pClientList->fUsingClientBuffer) + { + + while (pEntry != pHead) + { + PTDI_IND_RECEIVE_DATAGRAM EvRcvDgram; + PVOID RcvDgramEvContext; + tCLIENTELE *pClientEle; + PIRP pRcvIrp; + + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + // for multihomed hosts only distribute the datagram to + // clients hooked to this device context to avoid duplicate + // indications + // + if (pClientEle->pDeviceContext->AdapterNumber == AdapterNumber) + { + InterlockedIncrement(&pClientEle->RefCount); + + EvRcvDgram = pClientEle->evRcvDgram; + RcvDgramEvContext = pClientEle->RcvDgramEvContext; + + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + // dereference the previous client in the list + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + + pRcvIrp = NULL; + + ClientBytesTaken = 0; + + status = (*EvRcvDgram)(RcvDgramEvContext, + RemoteAddressLength, + pRemoteAddress, + 0, + NULL, + #ifndef VXD + ReceiveFlags, + #endif + DataLength, + DataLength, + &ClientBytesTaken, + pTsdu, + &pRcvIrp); + + if (!pRcvIrp) + { + // if no buffer is returned, then the client is done + // with the data so go to the next client ...since it may + // be possible to process all clients in this loop without + // ever sending an irp down to the transport + // free the remote address mem block + + status = STATUS_DATA_NOT_ACCEPTED; + } + else + { + + // the client has passed back an irp so + // copy the data to the client's Irp + // + TdiCopyBufferToMdl(pTsdu, + ClientBytesTaken, + DataLength - ClientBytesTaken, + pRcvIrp->MdlAddress, + 0, + &BytesCopied); + + // length is copied length (since the MDL may be + // too short to take it all) + // + if (BytesCopied < (DataLength-ClientBytesTaken)) + { + pRcvIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + else + { + pRcvIrp->IoStatus.Status = STATUS_SUCCESS; + } + + pRcvIrp->IoStatus.Information = BytesCopied; + + IoCompleteRequest(pRcvIrp,IO_NETWORK_INCREMENT); + } + + CTESpinLock(&NbtConfig.JointLock, OldIrq); + + + pClientPrev = pClientEle; + } + // this code is protected from a client removing itself + // from the list of clients attached to an address by + // referencing the client prior to releasing the spin lock + // on the address. The client element does not get + // removed from the address list until its ref count goes + // to zero. We must hold the joint spin lock to prevent the + // next client from deleting itself from the list before we + // can increment its reference count. + // + pEntry = pEntry->Flink; + + + } // of while(pEntry != pHead) + + + } + else + { + // *** Client Has posted a receive Buffer, rather than using + // *** receive handler - VXD case! + // *** + while (pEntry != pHead) + { + tCLIENTELE *pClientEle; + PIRP pRcvIrp; + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + // for multihomed hosts only distribute the datagram to + // clients hooked to this device context to avoid duplicate + // indications + // + if (pClientEle->pDeviceContext->AdapterNumber == AdapterNumber) + { + InterlockedIncrement(&pClientEle->RefCount); + + // check for datagrams posted to this name + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // undo the InterlockedIncrement on the refcount + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + pClientPrev = NULL; + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pClientEle == pClientList->pClientEle) + { + // this is the client whose buffer we are using - it is + // passed up to the client after all other clients + // have been processed. + // + InterlockedDecrement(&pClientEle->RefCount); + pEntry = pEntry->Flink; + continue; + } + else + if (!IsListEmpty(&pClientEle->RcvDgramHead)) + { + + pRcvEntry = RemoveHeadList(&pClientEle->RcvDgramHead); + pRcvEle = CONTAINING_RECORD(pRcvEntry,tRCVELE,Linkage); + pRcvIrp = pRcvEle->pIrp; + + // + // copy the data to the client's Irp + // + TdiCopyBufferToMdl(pTsdu, + 0, + DataLength, + pRcvIrp->MdlAddress, + 0, + &BytesCopied); + + // length is copied length (since the MDL may be too short to take it all) + if (BytesCopied < DataLength) + { + pRcvIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + else + { + pRcvIrp->IoStatus.Status = STATUS_SUCCESS; + } + + pRcvIrp->IoStatus.Information = BytesCopied; + + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + IoCompleteRequest(pRcvIrp,IO_NETWORK_INCREMENT); + + + // free the receive block + CTEMemFree((PVOID)pRcvEle); + + CTESpinLock(&NbtConfig.JointLock, OldIrq); + } + + pEntry = pEntry->Flink; + + pClientPrev = pClientEle; + } + + } // of while(pEntry != pHead) + + // free the remote address structure and the client list + // allocated in DgramHndlrNotOs + // + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + // undo the InterlockedIncrement on the refcount + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + CTEMemFree(pClientList->pRemoteAddress); + + // + // The address was referenced in DgramRcvNotOs to be sure it did not + // disappear until this dgram rcv was done, which is now. + // + NbtDereferenceAddress(pClientList->pAddress); + + CTEMemFree(Context); + + + // returning success allows the IO subsystem to complete the + // irp that we used to get the data - i.e. one client's + // buffer + // + return(STATUS_SUCCESS); + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // dereference the previous client in the list from the RcvHANDLER + // case a page or so above... + // + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + + // + // The address was referenced in DgramRcvNotOs to be sure it did not + // disappear until this dgram rcv was done, which is now. + // + NbtDereferenceAddress(pClientList->pAddress); + + +#ifdef PROXY_NODE + } + else + { + // + // Call the ProxyDoDgramDist after freeing the jointlock. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = ProxyDoDgramDist( + pTsdu, + DataLength, + (tNAMEADDR *)pClientList->pAddress, //NameAddr + pClientList->pRemoteAddress //device context + ); + + } +#endif + + + // + // Free the buffers allocated + // + CTEMemFree(pTsdu); + if (!pClientList->fProxy) + { + CTEMemFree(pClientList->pRemoteAddress); + } + CTEMemFree(Context); + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("****Freeing Mdl: Irp = %X Mdl = %X\n",Irp,Irp->MdlAddress)); + IoFreeMdl(Irp->MdlAddress); + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&Irp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &Irp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_MORE_PROCESSING_REQUIRED); + + } + + // for the single receive case this passes the rcv up to the client + // + return(STATUS_SUCCESS); + + UNREFERENCED_PARAMETER( DeviceObject ); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiErrorHandler ( + IN PVOID Context, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called on any error indications passed back from the + transport. It implements LAN_STATUS_ALERT. + +Arguments: + + Context - Supplies the pfcb for the address. + + Status - Supplies the error. + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + + // debug *JS + KdPrint(("Nbt: Error Event HAndler hit unexpectedly\n")); + return(STATUS_DATA_NOT_ACCEPTED); + + return STATUS_SUCCESS; +} + + +//---------------------------------------------------------------------------- +VOID +SumMdlLengths ( + IN PMDL pMdl, + IN ULONG BytesCopied, + IN tCONNECTELE *pConnectEle + ) + +/*++ + +Routine Description: + + This routine is called to sum the lengths of MDLs in a chain. + +Arguments: + + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + ULONG TotalLength; + + TotalLength = 0; + + do + { + if ((TotalLength + MmGetMdlByteCount(pMdl)) > BytesCopied) + { + pConnectEle->OffsetFromStart = BytesCopied - TotalLength; + pConnectEle->pNextMdl = pMdl; + break; + } + else + { + TotalLength += MmGetMdlByteCount(pMdl); + } + } + while (pMdl=(PMDL)pMdl->Next); + + return; +} + +//---------------------------------------------------------------------------- +NTSTATUS +AllocateMdl ( + IN tCONNECTELE *pConnEle + ) + +/*++ + +Routine Description: + + This routine is called to allocate a Mdl for the Connection. + +Arguments: + + pConnEle - ptr to the connection element + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + PMDL pNewMdl; + PVOID NewAddress; + + // + // Allocate an extra MDL for this connection if there isn't one + // already allocated. + // + if (pConnEle->pNewMdl == NULL ) + { + // 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 the pConnEle as the Virtual address, since it doesn't matter + // because it will be overwritten when the partial Mdl is created. + // + NewAddress = pConnEle; + pNewMdl = IoAllocateMdl( + NewAddress, + MAXUSHORT, + FALSE, + FALSE,NULL); + if (!pNewMdl) + { + ASSERTMSG("Nbt:Unable to allocate a MDL!!\n",0); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pConnEle->pNewMdl = pNewMdl; + } + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +VOID +MakePartialMdl ( + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG ToCopy + ) + +/*++ + +Routine Description: + + This routine is called to build a partial Mdl that accounts for ToCopy + bytes of data being copied to the start of the Client's Mdl. + +Arguments: + + pConnEle - ptr to the connection element + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + PMDL pNewMdl; + PVOID NewAddress; + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + SumMdlLengths(pIrp->MdlAddress,ToCopy,pConnEle); + + // this routine has set the Mdl that the next data starts at and + // the offset from the start of that Mdl, so create a partial Mdl + // to map that buffer and tack it on the mdl chain instead of the + // original + // + pNewMdl = pConnEle->pNewMdl; + NewAddress = (PVOID)((PUCHAR)MmGetMdlVirtualAddress(pConnEle->pNextMdl) + + pConnEle->OffsetFromStart); + IoBuildPartialMdl(pConnEle->pNextMdl,pNewMdl,NewAddress,0); + + // hook the new partial mdl to the front of the MDL chain + // + pNewMdl->Next = pConnEle->pNextMdl->Next; + + pIrp->MdlAddress = pNewMdl; + ASSERT(pNewMdl); +} +//---------------------------------------------------------------------------- +VOID +CopyToStartofIndicate ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG DataTaken + ) + +/*++ + +Routine Description: + + This routine is called to copy data remaining in the indicate buffer to + the head of the indicate buffer. + +Arguments: + + pLowerConn - ptr to the lower connection element + +Return Value: + + none + +--*/ + +{ + PVOID pSrc; + ULONG DataLeft; + PVOID pMdl; + + + DataLeft = pLowerConn->BytesInIndicate; + + pMdl = (PVOID)MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + pSrc = (PVOID)( (PUCHAR)pMdl + DataTaken); + + CTEMemCopy(pMdl,pSrc,DataLeft); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTProcessAcceptIrp( + IN PIRP pIrp, + OUT tCONNECTELE **ppConnEle) + +/*++ +Routine Description: + + This Routine handles a Client's AcceptIrp, extracting the pConnEle and + returning it. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_ACCEPT pRequest; + tCONNECTELE *pConnEle; + + + // pull the junk out of the Irp and call the non-OS specific routine. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // the Parameters value points to a Request structure... + pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters; + + // the pConnEle ptr was stored in the FsContext value when the connection + // was initially created. + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + *ppConnEle = pConnEle; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- + +NTSTATUS +OutOfRsrcKill( + OUT tLOWERCONNECTION *pLowerConn) + +/*++ +Routine Description: + + This Routine handles killing a connection when an out of resource condition + occurs. It uses a special Irp that it has saved away, and a linked list + if that irp is currently in use. + +Arguments: + + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + PIRP pIrp; + PFILE_OBJECT pFileObject; + PDEVICE_OBJECT pDeviceObject; + tDEVICECONTEXT *pDeviceContext = pLowerConn->pDeviceContext; + + + CTESpinLock(pDeviceContext,OldIrq); + CTESpinLock(&NbtConfig,OldIrq1); + + InterlockedIncrement(&pLowerConn->RefCount); + if (NbtConfig.OutOfRsrc.pIrp) + { + // get an Irp to send the message in + pIrp = NbtConfig.OutOfRsrc.pIrp; + NbtConfig.OutOfRsrc.pIrp = NULL; + + pFileObject = pLowerConn->pFileObject; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + CTESpinFree(&NbtConfig,OldIrq1); + CTESpinFree(pDeviceContext,OldIrq); + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + TdiBuildDisconnect( + pIrp, + pDeviceObject, + pFileObject, + RsrcKillCompletion, + pLowerConn, //context value passed to completion routine + NULL, // Timeout... + TDI_DISCONNECT_ABORT, + NULL, // send connection info + NULL); // return connection info + + CHECK_PTR(pIrp); + pIrp->MdlAddress = NULL; + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(pDeviceObject,pIrp); + + IF_DBG(NBT_DEBUG_REF) + KdPrint(("Nbt:Out of Resource, Kill connection, %X\n",pLowerConn)); + + return(status); + + } + else + { + // + // The lower conn could get removed here, then get dequed from the ConnectionHead and come here + // (via DpcNextOutOfRsrcKill), and fail to get an Irp; we re-enque it into the OutOfRsrc list, + // but should not try to deque it here. + // + if (!pLowerConn->OutOfRsrcFlag) { + + RemoveEntryList(&pLowerConn->Linkage); + + // + // The lower conn gets removed from the inactive list here and again when CleanupAfterDisconnect + // calls NbtDeleteLowerConn. In order to prevent the second deque, we set a flag here and test + // for it in NbtDeleteLowerConn. + // + pLowerConn->OutOfRsrcFlag = TRUE; + } + + pLowerConn->Linkage.Flink = pLowerConn->Linkage.Blink = (struct _LIST_ENTRY * volatile)0x00006041; + InsertTailList(&NbtConfig.OutOfRsrc.ConnectionHead,&pLowerConn->Linkage); + + CTESpinFree(&NbtConfig,OldIrq1); + CTESpinFree(pDeviceContext,OldIrq); + } + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +RsrcKillCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a disconnect to the transport. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + KIRQL OldIrq; + PLIST_ENTRY pEntry; + tLOWERCONNECTION *pLowerConn; + PKDPC pDpc; + + + + pLowerConn = (tLOWERCONNECTION *)pContext; + + // this call will indicate the disconnect to the client and clean up + // abit. + // + status = DisconnectHndlrNotOs ( + NULL, + (PVOID)pLowerConn, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT); + + NbtDereferenceLowerConnection(pLowerConn); + + CTESpinLock(&NbtConfig,OldIrq); + NbtConfig.OutOfRsrc.pIrp = pIrp; + + if (!IsListEmpty(&NbtConfig.OutOfRsrc.ConnectionHead)) + { + if (NbtConfig.OutOfRsrc.pDpc) + { + pDpc = NbtConfig.OutOfRsrc.pDpc; + NbtConfig.OutOfRsrc.pDpc = NULL; + + pEntry = RemoveHeadList(&NbtConfig.OutOfRsrc.ConnectionHead); + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + + pLowerConn->Linkage.Flink = pLowerConn->Linkage.Blink = (struct _LIST_ENTRY * volatile)0x00006109; + KeInitializeDpc(pDpc, + DpcNextOutOfRsrcKill, + (PVOID)pLowerConn); + + KeInsertQueueDpc(pDpc,NULL,NULL); + + CTESpinFree(&NbtConfig,OldIrq); + + } + else + CTESpinFree(&NbtConfig,OldIrq); + } + else + { + CTESpinFree(&NbtConfig,OldIrq); + } + + + + // + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is no initiating thread - we are the initiator + // + return(STATUS_MORE_PROCESSING_REQUIRED); +} + + +//---------------------------------------------------------------------------- +VOID +DpcNextOutOfRsrcKill( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls OutOfRsrcKill from a Dpc started in + RsrcKillCompletion. + +Arguments: + + +Return Value: +--*/ +{ + + KIRQL OldIrq; + tLOWERCONNECTION *pLowerConn; + + + pLowerConn = (tLOWERCONNECTION *)Context; + + CTESpinLock(&NbtConfig,OldIrq); + NbtConfig.OutOfRsrc.pDpc = pDpc; + CTESpinFree(&NbtConfig,OldIrq); + + OutOfRsrcKill(pLowerConn); + + // + // to remove the extra reference put on pLowerConn when OutOfRsrc called + // + NbtDereferenceLowerConnection(pLowerConn); + +} + + +//---------------------------------------------------------------------------- +VOID +FillIrpCancelRoutine( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Receive Irp that has been saved + during the FILL_IRP state. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + KIRQL OldIrq; + KIRQL OldIrq1; + KIRQL OldIrq2; + PIO_STACK_LOCATION pIrpSp; + tLOWERCONNECTION *pLowerConn; + BOOLEAN CompleteIt = FALSE; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got a Receive Cancel Irp !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + // now look for an Irp to cancel + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + CTESpinLock(pConnEle,OldIrq); + + + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + CTESpinLock(pLowerConn,OldIrq2); + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + + } + + + CHECK_PTR(pConnEle); + + pConnEle->pIrpRcv = NULL; + + + if (pLowerConn) + { + CTESpinFree(pLowerConn,OldIrq2); + } + + CTESpinFree(pConnEle,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + if (pLowerConn) + { + // + // Cancelling a Rcv Irp in the fill irp state will cause netbt + // to lose track of where it is in the message so it must kill + // the connection. + // + OutOfRsrcKill(pLowerConn); + } + return; + +} + + diff --git a/private/ntos/nbt/nt/tdiout.c b/private/ntos/nbt/nt/tdiout.c new file mode 100644 index 000000000..da98a8850 --- /dev/null +++ b/private/ntos/nbt/nt/tdiout.c @@ -0,0 +1,727 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdiout.c + +Abstract: + + + This file represents the TDI interface on the bottom edge of NBT. + The procedures herein conform to the TDI I/F spec. and then convert + the information to NT specific Irps etc. This implementation can be + changed out to run on another OS. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + + +#include "nbtprocs.h" // procedure headings + +// function prototypes for completion routines used in this file +NTSTATUS +DgramSendComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +TcpConnectComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +TcpDisconnectComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +SendSessionCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +// DEBUG +VOID +CheckIrpList( + ); + +//---------------------------------------------------------------------------- +NTSTATUS +TdiSendDatagram( + IN PTDI_REQUEST pRequestInfo, + IN PTDI_CONNECTION_INFORMATION pSendDgramInfo, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER *pSendBuffer, + IN ULONG SendFlags + ) +/*++ + +Routine Description: + + This routine sends a datagram to the transport + +Arguments: + + pSendBuffer - this is really an Mdl in NT land. It must be tacked on + the end of the Mdl created for the Nbt datagram header. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PMDL pMdl; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + PVOID pCompletionRoutine; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiSendDatagram")); + + // call the completion routine with this status (if there is one) + if (pRequestInfo->RequestNotifyObject) + { + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + + // so the Irp is not completed twice. + return(STATUS_PENDING); + } + + pRequestIrp->CancelRoutine = NULL; + + // set up the completion routine passed in from Udp Send using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields our self! + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + // Allocate a MDL and set the head sizes correctly + pMdl = IoAllocateMdl( + pSendBuffer->pDgramHdr, + pSendBuffer->HdrLength, + FALSE, + FALSE, + NULL); + + if (!pMdl) + { + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + if (pRequestInfo->RequestNotifyObject) + { + // call the completion routine will this status + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + ASSERT(pMdl); + return(STATUS_PENDING); + } + + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + + + pCompletionRoutine = DgramSendComplete; + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + TdiBuildSendDatagram( + pRequestIrp, + pDeviceObject, + pFileObject, + pCompletionRoutine, + NULL, //context value passed to completion routine + pMdl, + SendLength, + pSendDgramInfo); + + // tack the client's send buffer (MDL) onto the end of the datagram header + // Mdl, and then pass the irp on downward to the transport + if (pSendBuffer->pBuffer) + pMdl->Next = (PMDL)pSendBuffer->pBuffer; + + CHECK_COMPLETION(pRequestIrp); + status = IoCallDriver(pDeviceObject,pRequestIrp); + + // Fill in the SentSize + *pSentSize = SendLength; + + // The transport always completes the IRP, so as long as the irp made it + // to the transport it got completed. The return code from the transport + // does not indicate if the irp was completed or not. The real status + // of the operation is in the Irp Iostatus return code. + // What we need to do is make sure NBT does not complete the irp AND the + // transport complete the Irp. Therefore this routine returns + // status pending if the Irp was passed to the transport, regardless of + // the return code from the transport. This return code signals the caller + // that the irp will be completed via the completion routine and the + // actual status of the send can be found in the Irpss IoStatus.Status + // variable. + // + // If the Caller of this routine gets a bad return code, they can assume + // that this routine failed to give the Irp to the transport and it + // is safe for them to complete the Irp themselves. + // + // If the Completion routine is set to null, then there is no danger + // of the irp completing twice and this routine will return the transport + // return code in that case. + + if (pRequestInfo->RequestNotifyObject) + return(STATUS_PENDING); + else + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +DgramSendComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a datagram send to the transport. + It must call the client completion routine and free the Irp and Mdl. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + KIRQL OldIrq; + + // check for a completion routine of the clients to call... + if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine) + { + (*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine)) + ((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext, + pIrp->IoStatus.Status, + pIrp->IoStatus.Information); // sent length + + } + + // deallocate the MDL.. this is done by the IO subsystem in IoCompleteRequest + IoFreeMdl(pIrp->MdlAddress); + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is no initiating thread - we are the initiator + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + + + +//---------------------------------------------------------------------------- +PIRP +NTAllocateNbtIrp( + IN PDEVICE_OBJECT DeviceObject + ) +/*++ + +Routine Description: + + This routine allocates an irp by calling the IO system, and then it + undoes the queuing of the irp to the current thread, since these are + NBTs own irps, and should not be attached to a thread. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + PIRP pIrp; + + + + // call the IO subsystem to allocate the irp + + pIrp = IoAllocateIrp(DeviceObject->StackSize,FALSE); + + if (!pIrp) + { + return(NULL); + } + // + // Simply return a pointer to the packet. + // + + return pIrp; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiConnect( + IN PTDI_REQUEST pRequestInfo, + IN ULONG lTimeout, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + IN PIRP pClientIrp + ) +/*++ + +Routine Description: + + This routine sends a connect request to the tranport provider, to setup + a connection to the other side... + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiConnect")); + // call the completion routine with this status + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + return(STATUS_PENDING); + } + pRequestIrp->CancelRoutine = NULL; + + // set up the completion routine passed in from Tcp SessionStart using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields ourselves + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + TdiBuildConnect( + pClientIrp, + pDeviceObject, + pFileObject, + TcpConnectComplete, + (PVOID)pRequestIrp, //context value passed to completion routine + lTimeout, // use timeout on connect + pSendInfo, + NULL); + + pRequestIrp->MdlAddress = NULL; + + CHECK_COMPLETION(pClientIrp); + status = IoCallDriver(pDeviceObject,pClientIrp); + + // the transport always completes the IRP, so we always return status pending + return(STATUS_PENDING); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiDisconnect( + IN PTDI_REQUEST pRequestInfo, + IN PVOID lTimeout, + IN ULONG Flags, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + IN PCTE_IRP pClientIrp, + IN BOOLEAN Wait + ) +/*++ + +Routine Description: + + This routine sends a connect request to the tranport provider, to setup + a connection to the other side... + +Arguments: + + pClientIrp - this is the irp that the client used when it issued an + NbtDisconnect. We pass this irp to the transport so that + the client can do a Ctrl C and cancel the irp if the + disconnect takes too long. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + status = GetIrp(&pRequestIrp); + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiDisConnect")); + // call the completion routine will this status + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + return(STATUS_PENDING); + } + if (!pClientIrp) + { + // if no client irp was passed in, then just use our Irp + pClientIrp = pRequestIrp; + } + pRequestIrp->CancelRoutine = NULL; + + // set up the completion routine passed in from Tcp SessionStart using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields ourselves + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + // Note that pRequestIrp is passed to the completion routine as a context + // value so we will know the routine to call for the client's completion. + TdiBuildDisconnect( + pClientIrp, + pDeviceObject, + pFileObject, + TcpConnectComplete, + (PVOID)pRequestIrp, //context value passed to completion routine + lTimeout, + Flags, + NULL, // send connection info + NULL); // return connection info + + pRequestIrp->MdlAddress = NULL; + + // if Wait is set, then this means do a synchronous disconnect and block + // until the irp is returned. + // + if (Wait) + { + status = SubmitTdiRequest(pFileObject,pClientIrp); + // + // return the irp to its pool + // + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + return(status); + } + else + { + CHECK_COMPLETION(pClientIrp); + status = IoCallDriver(pDeviceObject,pClientIrp); + // the transport always completes the IRP, so we always return status pending + return(STATUS_PENDING); + } + + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TcpConnectComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a TCP session setup. The TCP + connection is either setup or not depending on the status returned here. + It must called the clients completion routine (in udpsend.c). Which should + look after sending the NetBios sesion startup pdu across the TCP connection. + + The pContext value is actually one of NBTs irps that is JUST used to store + the calling routines completion routine. The real Irp used is the original + client's irp. This is done so that IoCancelIrp will cancel the connect + properly. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + KIRQL OldIrq; + PIRP pMyIrp; + + pMyIrp = (PIRP)pContext; + + // check for a completion routine of the clients to call... + if (pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine) + { + (*((NBT_COMPLETION)pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine)) + ((PVOID)pMyIrp->Overlay.AsynchronousParameters.UserApcContext, + pIrp->IoStatus.Status, + 0L); + + } + + REMOVE_FROM_LIST(&pMyIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pMyIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is not initiating thread - we are the initiator + return(STATUS_MORE_PROCESSING_REQUIRED); + +} +//---------------------------------------------------------------------------- +NTSTATUS +TdiSend( + IN PTDI_REQUEST pRequestInfo, + IN USHORT sFlags, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER *pSendBuffer, + IN ULONG Flags + ) +/*++ + +Routine Description: + + This routine sends a packet to the transport on a TCP connection + +Arguments: + + pSendBuffer - this is really an Mdl in NT land. It must be tacked on + the end of the Mdl created for the Nbt datagram header. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PMDL pMdl; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiSend")); + // call the completion routine with this status + if (pRequestInfo->RequestNotifyObject) + { + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + + return(STATUS_INSUFFICIENT_RESOURCES); + } + pRequestIrp->CancelRoutine = NULL; + + + // set up the completion routine passed in from Udp Send using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields our self! + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + + // get the MDL that is currently linked to the IRP (i.e. created at the + // same time that we created the IRP list. Set the sizes correctly in + // the MDL header. + pMdl = IoAllocateMdl( + pSendBuffer->pDgramHdr, + pSendBuffer->HdrLength, + FALSE, + FALSE, + NULL); + + if (!pMdl) + { + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // call the completion routine will this status + if (pRequestInfo->RequestNotifyObject) + { + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + + TdiBuildSend( + pRequestIrp, + pDeviceObject, + pFileObject, + SendSessionCompletionRoutine, + NULL, //context value passed to completion routine + pMdl, + sFlags, + SendLength); // include session hdr length (ULONG) + // + // tack the Client's buffer on the end, if there is one + // + if (pSendBuffer->Length) + { + pMdl->Next = pSendBuffer->pBuffer; + } + + CHECK_COMPLETION(pRequestIrp); + status = IoCallDriver(pDeviceObject,pRequestIrp); + + *pSentSize = SendLength; // the size we attempted to send + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +SendSessionCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a send to the transport. + It must call any client supplied completion routine and free the Irp + and Mdl back to its pool. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + KIRQL OldIrq; + + // + // check for a completion routine of the clients to call... + // + if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine) + { + (*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine)) + ((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext, + pIrp->IoStatus.Status, + pIrp->IoStatus.Information); // sent length + + } + + + + IoFreeMdl(pIrp->MdlAddress); + + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + // + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is no initiating thread - we are the initiator + // + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + + + diff --git a/private/ntos/nbt/nt/up/makefile b/private/ntos/nbt/nt/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nt/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nt/up/netbt.prf b/private/ntos/nbt/nt/up/netbt.prf new file mode 100644 index 000000000..84914dcb9 --- /dev/null +++ b/private/ntos/nbt/nt/up/netbt.prf @@ -0,0 +1,170 @@ +SendCompletion@12 +NbtDispatchInternalCtrl@8 +NTSend@8 +TdiReceiveHandler@32 +CompletionRcv@12 +ProcessIrp@24 +CTECountedAllocMem@8 +DgramHndlrNotOs@48 +ConvertToAscii@20 +UdpSendDatagram@28 +NbtDereferenceClient@4 +NTIoComplete@12 +MakeRemoteAddressStructure@16 +FindName@16 +GetIrp@4 +TdiSendDatagram@24 +DgramSendComplete@12 +FindInHashTable@16 +SendDgramCompletion@12 +CTECountedFreeMem@8 +DgramSendCleanupTracker@12 +NbtDereferenceName@4 +TdiRcvDatagramHandler@44 +GetNetBiosNameFromTransportAddress@12 +FindNameOrQuery@28 +NbtAllocTracker@0 +NbtDereferenceAddress@4 +NTSendDatagram@8 +NbtSendDatagram@28 +SendDgram@8 +ConvertToHalfAscii@16 +BuildSendDgramHdr@32 +RemoteHashTimeout@12 +TimerExpiry@16 +CTEStartTimer@16 +NTQueryInformation@8 +NTOpenConnection@8 +CreateDeviceString@8 +FreeTracker@8 +GetTracker@4 +SubmitTdiRequest@8 +TdiRcvNameSrvHandler@44 +GetEntry@12 +UdpSendNSBcast@36 +NDgramSendCompleted@12 +CreatePdu@32 +FindInEA@8 +MSnodeCompletion@12 +NbtOpenAndAssocConnection@8 +SrcIsUs@4 +AddToPendingList@8 +QueryNameOnNet@44 +NbtTdiAssociateConnection@8 +CompleteClientReq@12 +ClearCancelRoutine@4 +StartTimer@32 +StartLmHostTimer@8 +LmHostQueueRequest@16 +GetNameToFind@4 +RemoveNameAndCompleteReq@8 +RemoveName@4 +NameSrvHndlrNotOs@16 +ReturnIrp@8 +CheckRegistrationFromNet@16 +DereferenceTracker@4 +CTEInitTimer@4 +TimeoutQEntries@12 +LmHostTimeout@12 +GetContext@4 +MakePending@4 +FindNameRemoteThenLocal@8 +NbtOpenConnection@12 +NbtTdiOpenConnection@8 +NbtDispatchCreate@8 +SendDgramContinue@8 +CompletionRoutine@12 +AllocateMdl@4 +NTAssocAddress@8 +NbtAssociateAddress@12 +NTAllocateNbtIrp@4 +NbtGetMdl@8 +NbtListen@20 +NbtAllocateClientBlock@4 +NbtSetEventHandler@16 +InterlockedCallCompletion@8 +NTSetEventHandler@8 +FindInDomainList@8 +NbtRegisterName@32 +NTCheckSetCancelRoutine@12 +InitTimerQ@4 +CountLocalNames@4 +NbtInitConnQ@16 +AddToHashTable@28 +NbtInitTrackerQ@8 +NbtAppendString@12 +NbtQueryAdapterStatus@12 +QueryProviderCompletion@12 +NbtAddPermanentName@4 +ReadParameters2@8 +IncrementNameStats@8 +MSnodeRegCompletion@12 +NbtRegisterCompletion@8 +NbtOpenAddress@24 +InitQ@12 +NTSetFileObjectContexts@12 +NbtCreateDeviceObject@24 +CreateHashTable@12 +InitNotOs@0 +InitTimersNotOs@0 +TcpSendSessionResponse@12 +TcpSendSession@12 +Inbound@32 +CompleteSessionSetup@20 +SessionRespDone@12 +UdpSendResponse@32 +QueryRespDone@12 +SrcIsNameServer@8 +QueryFromNet@20 +NbtDispatchCleanup@8 +ConvertDottedDecimalToUlong@8 +NTGetLmHostPath@4 +NbtDispatchClose@8 +NbtDispatchDevCtrl@8 +NTOpenControl@8 +NbtCreateAddressObjects@12 +NTSetSharedAccess@12 +FindSessionEndPoint@24 +DispatchIoctls@12 +ReadParameters@8 +NTListen@8 +ConvertToUlong@8 +NbtInitMdlQ@8 +GetExtendedAttributes@4 +ReadNameServerAddresses@16 +GetServerAddress@12 +NbtReadRegistry@24 +GetIPFromRegistry@16 +NbtReadLinkageInformation@16 +NTReadIniString@12 +ReadElement@12 +NbtReadSingleParameter@16 +NbtOpenRegistry@12 +NbtFindLastSlash@12 +ReadStringRelative@16 +OpenAndReadElement@12 +SetEventHandler@20 +NbtTdiOpenAddress@28 +NbtTdiOpenControl@4 +SetWinsDownFlag@4 +LmGetFullPath@8 +TdiConnectHandler@36 +NTOpenAddr@8 +AcceptCompletionRoutine@12 +NTCheckSharedAccess@12 +LmOpenFile@4 +LmpBreakRecursion@8 +SendSessionCompletionRoutine@12 +ScanLmHostFile@4 +TdiSend@24 +NTReceive@8 +ClientTookSomeOfTheData@20 +LmGetIpAddr@16 +ConnectHndlrNotOs@24 +NTProcessAcceptIrp@8 +InitRemoteHashTable@12 +DriverEntry@8 +ReadLmHostFile@8 +ReadScope@8 +ClearConnStructures@8 +NbtQueryConnectionList@12 diff --git a/private/ntos/nbt/nt/up/sources b/private/ntos/nbt/nt/up/sources new file mode 100644 index 000000000..944b046e1 --- /dev/null +++ b/private/ntos/nbt/nt/up/sources @@ -0,0 +1,30 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj +TARGETLIBS=..\..\nbt\up\obj\*\nbt.lib + +!include ..\sources.inc diff --git a/private/ntos/nbt/nt/winsif.c b/private/ntos/nbt/nt/winsif.c new file mode 100644 index 000000000..d17ab2d7e --- /dev/null +++ b/private/ntos/nbt/nt/winsif.c @@ -0,0 +1,1390 @@ +/*++ + +Copyright (c) 1989-1994 Microsoft Corporation + +Module Name: + + Winsif.c + +Abstract: + + This module implements all the code surrounding the WINS interface to + netbt that allows WINS to share the same 137 socket as netbt. + +Author: + + Jim Stewart (Jimst) 1-30-94 + +Revision History: + +--*/ + + +#include "nbtprocs.h" +#include <nbtioctl.h> + +VOID +WinsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); +VOID +WinsSendIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); +VOID +WinsDgramCompletion( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS status, + IN ULONG Length + ); + +NTSTATUS +CheckIfLocalNameActive( + IN tREM_ADDRESS *pSendAddr + ); + +PVOID +WinsAllocMem( + IN ULONG Size, + IN BOOLEAN Rcv + ); + +VOID +WinsFreeMem( + IN PVOID pBuffer, + IN ULONG Size, + IN BOOLEAN Rcv + ); + +VOID +InitiateRefresh ( + IN tDEVICECONTEXT *pDeviceContext + ); + +BOOLEAN RefreshedYet; + +// +// take this define from Winsock.h since including winsock.h causes +// redefinition problems with various types. +// +#define AF_UNIX 1 +#define AF_INET 2 + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGENBT, NTCloseWinsAddr) +#pragma CTEMakePageable(PAGENBT, InitiateRefresh) +#pragma CTEMakePageable(PAGENBT, RcvIrpFromWins) +#pragma CTEMakePageable(PAGENBT, PassNamePduToWins) +#pragma CTEMakePageable(PAGENBT, WinsIrpCancel) +#pragma CTEMakePageable(PAGENBT, WinsSendIrpCancel) +#pragma CTEMakePageable(PAGENBT, WinsSendDatagram) +#pragma CTEMakePageable(PAGENBT, CheckIfLocalNameActive) +#pragma CTEMakePageable(PAGENBT, WinsDgramCompletion) +#pragma CTEMakePageable(PAGENBT, WinsFreeMem) +#pragma CTEMakePageable(PAGENBT, WinsAllocMem) +#endif +//******************* Pageable Routine Declarations **************** + +tWINS_INFO *pWinsInfo; +HANDLE NbtDiscardableCodeHandle={0}; + +#define COUNT_MAX 10 + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenWinsAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles opening the Wins Object that is used by + by WINS to send and receive name service datagrams on port 137. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tWINS_INFO *pWins; + CTELockHandle OldIrq; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->FileObject->FsContext2 =(PVOID)NBT_WINS_TYPE; + + + // + // if the WINs endpoint structure is not allocated, then allocate it + // and initialize it. + // + status = STATUS_UNSUCCESSFUL; + if (!pWinsInfo) + { + + pWins = NbtAllocMem(sizeof(tWINS_INFO),NBT_TAG('v')); + if (pWins) + { + + // Page in the Wins Code, if it hasn't already been paged in. + // + if (!NbtDiscardableCodeHandle) + { + NbtDiscardableCodeHandle = MmLockPagableCodeSection( NTCloseWinsAddr ); + } + + // it could fail to lock the pages so check for that + // + if (NbtDiscardableCodeHandle) + { + CTEZeroMemory(pWins,sizeof(tWINS_INFO)); + InitializeListHead(&pWins->RcvList); + InitializeListHead(&pWins->SendList); + + pWins->RcvMemoryMax = NbtConfig.MaxDgramBuffering; + pWins->SendMemoryMax = NbtConfig.MaxDgramBuffering; + + status = STATUS_SUCCESS; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pWinsInfo = pWins; + pWinsInfo->pDeviceContext = pDeviceContext; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + pIrpSp->FileObject->FsContext = (PVOID)pWinsInfo; + } + else + { + status = STATUS_UNSUCCESSFUL; + CTEMemFree(pWins); + } + + RefreshedYet = FALSE; + } + + + } + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Open Wins Address Rcvd, status= %X\n",status)); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCloseWinsAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles closing the Wins Object that is used by + by WINS to send and receive name service datagrams on port 137. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tWINS_INFO *pWins; + CTELockHandle OldIrq; + PLIST_ENTRY pHead; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->FileObject->FsContext2 = (PVOID)NBT_CONTROL_TYPE; + + // + // if the WINs endpoint structure is allocated, then deallocate it + // + pWins = pIrpSp->FileObject->FsContext; + status = STATUS_INVALID_HANDLE; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pWinsInfo && (pWins == pWinsInfo)) + { + + status = STATUS_SUCCESS; + + // + // prevent any more dgram getting queued up + // + pWinsInfo = NULL; + + // + // free any rcv buffers that may be queued up + // + pHead = &pWins->RcvList; + while (!IsListEmpty(pHead)) + { + PLIST_ENTRY pRcvEntry; + tWINSRCV_BUFFER *pRcv; + + KdPrint(("***Nbt:Freeing Rcv buffered for Wins\n")); + + pRcvEntry = RemoveHeadList(pHead); + + pRcv = CONTAINING_RECORD(pRcvEntry,tWINSRCV_BUFFER,Linkage); + + WinsFreeMem(pRcv,pRcv->DgramLength,TRUE); + + } + + // + // return any Send buffers that may be queued up + // + pHead = &pWins->SendList; + while (!IsListEmpty(pHead)) + { + PLIST_ENTRY pRcvEntry; + PIRP pIrp; + + KdPrint(("***Nbt:Freeing Send Wins Address!\n")); + + pRcvEntry = RemoveHeadList(pHead); + + pIrp = CONTAINING_RECORD(pRcvEntry,IRP,Tail.Overlay.ListEntry); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + } + + CTEMemFree(pWins); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // + // Free The Wins Code section - DONOT do this to avoid any potential + // time windows calling these routines. + // + // MmUnlockPagableImageSection( NbtDiscardableCodeHandle ); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Close Wins Address Rcvd\n")); + return(status); + +} +//---------------------------------------------------------------------------- +VOID +InitiateRefresh ( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine tries to refresh all names with WINS on THIS node. + +Arguments: + + pDeviceContext - not used + pIrp - Wins Rcv Irp + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + CTELockHandle OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + ULONG Count; + ULONG NumberNames; + + + // + // be sure all net cards have this card as the primary wins + // server since Wins has to answer name queries for this + // node. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (!(NodeType & BNODE)) + { + LONG i; + Count = 0; + + NumberNames = 0; + + for (i=0 ;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + NumberNames++; + } + } + + while (Count < COUNT_MAX) + { + if (!NbtConfig.DoingRefreshNow) + { + + // + // set this to one so that refresh begin skips trying to + // switch to the backup. + // + NbtConfig.sTimeoutCount = 1; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + ReRegisterLocalNames(); + + break; + } + else + { + LARGE_INTEGER Timout; + NTSTATUS Locstatus; + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Waiting for Refresh to finish, so names can be reregistered\n")); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // + // set a timeout that should be long enough to wait + // for all names to fail registration with a down + // wins server. + // + // 2 sec*3 retries * 8 names / 5 = 9 seconds a shot. + // for a total of 90 seconds max. + // + Timout.QuadPart = Int32x32To64( + MILLISEC_TO_100NS/(COUNT_MAX/2), + (NbtConfig.uRetryTimeout*NbtConfig.uNumRetries) + *NumberNames); + + Timout.QuadPart = -(Timout.QuadPart); + + // + // wait for a few seconds and try again. + // + Locstatus = KeDelayExecutionThread( + KernelMode, + FALSE, // Alertable + &Timout); // Timeout + + + + Count++; + if (Count < COUNT_MAX) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + } + } + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } +} + +//---------------------------------------------------------------------------- +NTSTATUS +RcvIrpFromWins ( + IN tDEVICECONTEXT *pDeviceContext, + IN PCTE_IRP pIrp + ) +/*++ + +Routine Description: + + This function takes the rcv irp posted by WINS and decides if there are + any datagram queued waiting to go up to WINS. If so then the datagram + is copied to the WINS buffer and passed back up. Otherwise the irp is + held by Netbt until a datagram does come in. + +Arguments: + + pDeviceContext - not used + pIrp - Wins Rcv Irp + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS status; + NTSTATUS Locstatus; + tREM_ADDRESS *pWinsBuffer; + tWINSRCV_BUFFER *pBuffer; + PLIST_ENTRY pEntry; + CTELockHandle OldIrq; + tWINS_INFO *pWins; + PIO_STACK_LOCATION pIrpSp; + + status = STATUS_INVALID_HANDLE; + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pWins = pIrpSp->FileObject->FsContext; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if ((!RefreshedYet) && (pWins == pWinsInfo)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + InitiateRefresh(pDeviceContext); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + RefreshedYet = TRUE; + } + + if (pWins == pWinsInfo) + { + + if (!IsListEmpty(&pWinsInfo->RcvList)) + { + PMDL pMdl; + ULONG CopyLength; + ULONG DgramLength; + ULONG BufferLength; + + // + // There is at least one datagram waiting to be received + // + pEntry = RemoveHeadList(&pWinsInfo->RcvList); + + pBuffer = CONTAINING_RECORD(pEntry,tWINSRCV_BUFFER,Linkage); + + // + // Copy the datagram and the source address to WINS buffer and + // return to WINS + // + pMdl = pIrp->MdlAddress; + pWinsBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + + BufferLength = MmGetMdlByteCount(pMdl); + DgramLength = pBuffer->DgramLength; + + CopyLength = (DgramLength <= BufferLength) ? DgramLength : BufferLength; + CTEMemCopy((PVOID)pWinsBuffer, + (PVOID)&pBuffer->Address.Family, + CopyLength); + + // + // subtract from the total amount buffered for WINS since we are + // passing a datagram up to WINS now. + // + pWinsInfo->RcvMemoryAllocated -= pBuffer->DgramLength; + CTEMemFree(pBuffer); + + ASSERT(pWinsBuffer->Port); + ASSERT(pWinsBuffer->IpAddress); + // + // pass the irp up to WINS + // + if (CopyLength < DgramLength) + { + Locstatus = STATUS_BUFFER_OVERFLOW; + } + else + { + Locstatus = STATUS_SUCCESS; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Returning Wins rcv Irp immediately with queued dgram, status=%X,pIrp=%X\n" + ,status,pIrp)); + + pIrp->IoStatus.Information = CopyLength; + pIrp->IoStatus.Status = Locstatus; + + + IoCompleteRequest(pIrp,IO_NO_INCREMENT); + + return(STATUS_SUCCESS); + + } + else + { + + status = NTCheckSetCancelRoutine(pIrp,WinsIrpCancel,pDeviceContext); + + if (!NT_SUCCESS(status)) + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(pIrp,status,0); + } + else + { + pWinsInfo->RcvIrp = pIrp; + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Holding onto Wins Rcv Irp, pIrp =%Xstatus=%X\n", + status,pIrp)); + + status = STATUS_PENDING; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INVALID_HANDLE; + NTIoComplete(pIrp,status,0); + } + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +PassNamePduToWins ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameSrv, + IN ULONG uNumBytes + ) +/*++ + +Routine Description: + + This function is used to allow NBT to pass name query service Pdu's to + WINS. Wins posts a Rcv irp to Netbt. If the Irp is here then simply + copy the data to the irp and return it, otherwise buffer the data up + to a maximum # of bytes. Beyond that limit the datagrams are discarded. + + If Retstatus is not success then the pdu will also be processed by + nbt. This allows nbt to process packets when wins pauses and + its list of queued buffers is exceeded. + +Arguments: + + pDeviceContext - card that the request can in on + pSrcAddress - source address + pNameSrv - ptr to the datagram + uNumBytes - length of datagram + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS Retstatus; + NTSTATUS status; + tREM_ADDRESS *pWinsBuffer; + PCTE_IRP pIrp; + CTELockHandle OldIrq; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SrcAddress; + SHORT SrcPort; + + + // + // Get the source port and ip address, since WINS needs this information. + // + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr; + SrcPort = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + Retstatus = STATUS_SUCCESS; + if (pWinsInfo) + { + if (!pWinsInfo->RcvIrp) + { + // + // Queue the name query pdu if we have not exeeded our current queue + // length + // + if (pWinsInfo->RcvMemoryAllocated < pWinsInfo->RcvMemoryMax) + { + tWINSRCV_BUFFER *pBuffer; + + pBuffer = NbtAllocMem(uNumBytes + sizeof(tWINSRCV_BUFFER)+8,NBT_TAG('v')); + if (pBuffer) + { + // + // check if it is a name reg from this node + // + if (pNameSrv->AnCount == WINS_SIGNATURE) + { + pNameSrv->AnCount = 0; + pBuffer->Address.Family = AF_UNIX; + } + else + { + pBuffer->Address.Family = AF_INET; + } + + CTEMemCopy((PUCHAR)((PUCHAR)pBuffer + sizeof(tWINSRCV_BUFFER)), + (PVOID)pNameSrv,uNumBytes); + + pBuffer->Address.Port = SrcPort; + pBuffer->Address.IpAddress = SrcAddress; + pBuffer->Address.LengthOfBuffer = uNumBytes; + + ASSERT(pBuffer->Address.Port); + ASSERT(pBuffer->Address.IpAddress); + + // total amount allocated + pBuffer->DgramLength = uNumBytes + sizeof(tREM_ADDRESS); + + + // + // Keep track of the total amount buffered so that we don't + // eat up all non-paged pool buffering for WINS + // + pWinsInfo->RcvMemoryAllocated += pBuffer->DgramLength; + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Buffering Wins Rcv - no Irp, status=%X\n")); + InsertTailList(&pWinsInfo->RcvList,&pBuffer->Linkage); + + } + } + else + { + // this ret status will allow netbt to process the packet. + // + Retstatus = STATUS_INSUFFICIENT_RESOURCES; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + else + { + PMDL pMdl; + ULONG CopyLength; + ULONG DgramLength; + ULONG BufferLength; + + // + // The recv irp is here so copy the data to its buffer and + // pass it up to WINS + // + pIrp = pWinsInfo->RcvIrp; + pWinsInfo->RcvIrp = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Copy the datagram and the source address to WINS buffer and + // return to WINS + // + pMdl = pIrp->MdlAddress; + pWinsBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + BufferLength = MmGetMdlByteCount(pMdl); + DgramLength = uNumBytes; + + CopyLength = (DgramLength+sizeof(tREM_ADDRESS)) <= BufferLength ? DgramLength : BufferLength; + + // + // check if it is a name reg from this node + // + if (pNameSrv->AnCount == WINS_SIGNATURE) + { + pNameSrv->AnCount = 0; + pWinsBuffer->Family = AF_UNIX; + } + else + { + pWinsBuffer->Family = AF_INET; + } + CTEMemCopy((PVOID)((PUCHAR)pWinsBuffer + sizeof(tREM_ADDRESS)), + (PVOID)pNameSrv, + CopyLength); + + pWinsBuffer->Port = SrcPort; + pWinsBuffer->IpAddress = SrcAddress; + pWinsBuffer->LengthOfBuffer = uNumBytes; + + ASSERT(pWinsBuffer->Port); + ASSERT(pWinsBuffer->IpAddress); + + // + // pass the irp up to WINS + // + if (CopyLength < DgramLength) + { + status = STATUS_BUFFER_OVERFLOW; + } + else + { + status = STATUS_SUCCESS; + } + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Returning Wins Rcv Irp - data from net, Length=%X,pIrp=%X\n" + ,uNumBytes,pIrp)); + + NTIoComplete(pIrp,status,CopyLength); + + } + } + else + { + // + // this ret status will allow netbt to process the packet. + // + Retstatus = STATUS_INSUFFICIENT_RESOURCES; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + return(Retstatus); + +} + +//---------------------------------------------------------------------------- +VOID +WinsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a WinsRcv Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + KIRQL OldIrq; + PIO_STACK_LOCATION pIrpSp; + tWINS_INFO *pWins; + + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Got a Wins Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // + // Be sure that PassNamePduToWins has not taken the RcvIrp for a + // Rcv just now. + // + if ((pWins == pWinsInfo) && (pWinsInfo->RcvIrp == pIrp)) + { + + pWinsInfo->RcvIrp = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + pIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + + +} +//---------------------------------------------------------------------------- +VOID +WinsSendIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a WinsRcv Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + KIRQL OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + tWINS_INFO *pWins; + BOOLEAN Found; + PIRP pIrpList; + + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Got a Wins Send Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pWins == pWinsInfo) + { + // + // find the matching irp on the list and remove it + // + pHead = &pWinsInfo->SendList; + pEntry = pHead; + Found = FALSE; + + while ((pEntry = pEntry->Flink) != pHead) + { + pIrpList = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + if (pIrp == pIrpList) + { + RemoveEntryList(pEntry); + Found = TRUE; + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (Found) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + + +} +//---------------------------------------------------------------------------- +NTSTATUS +WinsSendDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN BOOLEAN MustSend) + +/*++ +Routine Description: + + This Routine handles sending a datagram down to the transport. MustSend + it set true by the Send Completion routine when it attempts to send + one of the queued datagrams, in case we still don't pass the memory + allocated check and refuse to do the send - sends will just stop then without + this boolean. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tWINS_INFO *pWins; + tREM_ADDRESS *pSendAddr; + PVOID pDgram; + ULONG DgramLength; + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext; + + + status = STATUS_UNSUCCESSFUL; + + // + // check if it is a name that is registered on this machine + // + pSendAddr = (tREM_ADDRESS *)MmGetSystemAddressForMdl(pIrp->MdlAddress); + if (pSendAddr->Family == AF_UNIX) + { + status = CheckIfLocalNameActive(pSendAddr); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pWins == pWinsInfo) + { + + if ((pWins->SendMemoryAllocated < pWins->SendMemoryMax) || MustSend) + { + + if (pSendAddr->IpAddress != 0) + { + + DgramLength = pSendAddr->LengthOfBuffer; + pDgram = WinsAllocMem(DgramLength,FALSE); + + + if (pDgram) + { + CTEMemCopy(pDgram, + (PVOID)((PUCHAR)pSendAddr+sizeof(tREM_ADDRESS)), + DgramLength + ); + + // + // get a buffer for tracking Dgram Sends + // + pTracker = NbtAllocTracker(); + if (pTracker) + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + pTracker->SendBuffer.pBuffer = NULL; + pTracker->SendBuffer.Length = 0; + pTracker->SendBuffer.pDgramHdr = pDgram; + pTracker->SendBuffer.HdrLength = DgramLength; + pTracker->pClientEle = NULL; + pTracker->pDestName = NULL; + pTracker->AllocatedLength = DgramLength; + + + // send the Datagram... + status = UdpSendDatagram( + pTracker, + ntohl(pSendAddr->IpAddress), + pDeviceContext->pNameServerFileObject, + WinsDgramCompletion, + pTracker, // context for completion + (USHORT)ntohs(pSendAddr->Port), + NBT_NAME_SERVICE); + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Doing Wins Send, status=%X\n",status)); + + // sending the datagram could return status pending, + // but since we have buffered the dgram, return status + // success to the client + // + status = STATUS_SUCCESS; + // + // Fill in the sent size + // + pIrp->IoStatus.Information = DgramLength; + + } + else + { + WinsFreeMem((PVOID)pDgram,DgramLength,FALSE); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INVALID_PARAMETER; + } + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp,IO_NO_INCREMENT); + } + else + { + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Holding onto Buffering Wins Send, status=%X\n")); + + + // + // Hold onto the datagram till memory frees up + // + InsertTailList(&pWins->SendList,&pIrp->Tail.Overlay.ListEntry); + + status = NTCheckSetCancelRoutine(pIrp,WinsSendIrpCancel,pDeviceContext); + if (!NT_SUCCESS(status)) + { + RemoveEntryList(&pIrp->Tail.Overlay.ListEntry); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(pIrp,status,0); + + } + else + { + status = STATUS_PENDING; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + } + + + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INVALID_HANDLE; + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp,IO_NO_INCREMENT); + } + + return(status); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +CheckIfLocalNameActive( + IN tREM_ADDRESS *pSendAddr + ) + +/*++ +Routine Description + + This routine checks if this is a name query response and if the + name is still active on the local node. + +Arguments: + + pMdl = ptr to WINS Mdl + +Return Values: + + VOID + +--*/ + +{ + NTSTATUS status; + tNAMEHDR UNALIGNED *pNameHdr; + tNAMEADDR *pResp; + UCHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + ULONG lNameSize; + CTELockHandle OldIrq; + + pNameHdr = (tNAMEHDR UNALIGNED *)((PUCHAR)pSendAddr + sizeof(tREM_ADDRESS)); + // + // Be sure it is a name query PDU that we are checking + // + if (((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_QUERY) || + ((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_RELEASE)) + { + status = ConvertToAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + pSendAddr->LengthOfBuffer, + pName, + &pScope, + &lNameSize); + + if (NT_SUCCESS(status)) + { + + // + // see if the name is still active in the local hash table + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pResp); + + + if ((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_QUERY) + { + if (NT_SUCCESS(status)) + { + // + // if not resolved then set to negative name query resp. + // + if (!(pResp->NameTypeState & STATE_RESOLVED)) + { + pNameHdr->OpCodeFlags |= htons(NAME_ERROR); + } + } + else + { + pNameHdr->OpCodeFlags |= htons(NAME_ERROR); + } + } + else + { + // + // check if it is a release response - if so we must have + // received a name release request, so mark the name in + // conflict and return a positive release response. + // + if (pNameHdr->OpCodeFlags & OP_RESPONSE) + { + if (NT_SUCCESS(status) && + (pResp->NameTypeState & STATE_RESOLVED)) + { + NbtLogEvent(EVENT_NBT_NAME_RELEASE,pSendAddr->IpAddress); + + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_CONFLICT; + + // + // change to successful response + // + pNameHdr->OpCodeFlags &= 0xF0FF; + + } + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + // + // the name is not in the local table so fail the datagram send attempt + // + return(STATUS_SUCCESS); + +} + +//---------------------------------------------------------------------------- +VOID +WinsDgramCompletion( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS status, + IN ULONG Length + ) + +/*++ +Routine Description + + This routine cleans up after a data gram send. + +Arguments: + + pTracker + status + Length + +Return Values: + + VOID + +--*/ + +{ + CTELockHandle OldIrq; + LIST_ENTRY *pEntry; + PIRP pIrp; + BOOLEAN MustSend; + + // + // free the buffer used for sending the data and the tracker - note + // that the datagram header and the send buffer are allocated as one + // chunk. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pWinsInfo) + { + WinsFreeMem((PVOID)pTracker->SendBuffer.pDgramHdr, + pTracker->AllocatedLength, + FALSE); + + if (!IsListEmpty(&pWinsInfo->SendList)) + { + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Sending another Wins Dgram that is Queued to go\n")); + + pEntry = RemoveHeadList(&pWinsInfo->SendList); + pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Send this next datagram + // + status = WinsSendDatagram(pTracker->pDeviceContext, + pIrp, + MustSend = TRUE); + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + { + // + // just free the memory since WINS has closed its address handle. + // + CTEMemFree((PVOID)pTracker->SendBuffer.pDgramHdr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + CTEFreeMem(pTracker); + + +} + +//---------------------------------------------------------------------------- +PVOID +WinsAllocMem( + IN ULONG Size, + IN BOOLEAN Rcv + ) + +/*++ +Routine Description: + + This Routine handles allocating memory and keeping track of how + much has been allocated. + +Arguments: + + Size - number of bytes to allocate + Rcv - boolean that indicates if it is rcv or send buffering + +Return Value: + + ptr to the memory allocated + +--*/ + +{ + if (Rcv) + { + if (pWinsInfo->RcvMemoryAllocated > pWinsInfo->RcvMemoryMax) + { + return NULL; + } + else + { + pWinsInfo->RcvMemoryAllocated += Size; + return (NbtAllocMem(Size,NBT_TAG('v'))); + } + } + else + { + if (pWinsInfo->SendMemoryAllocated > pWinsInfo->SendMemoryMax) + { + return(NULL); + } + else + { + pWinsInfo->SendMemoryAllocated += Size; + return(NbtAllocMem(Size,NBT_TAG('v'))); + } + } +} +//---------------------------------------------------------------------------- +VOID +WinsFreeMem( + IN PVOID pBuffer, + IN ULONG Size, + IN BOOLEAN Rcv + ) + +/*++ +Routine Description: + + This Routine handles freeing memory and keeping track of how + much has been allocated. + +Arguments: + + pBuffer - buffer to free + Size - number of bytes to allocate + Rcv - boolean that indicates if it is rcv or send buffering + +Return Value: + + none + +--*/ + +{ + if (pWinsInfo) + { + if (Rcv) + { + pWinsInfo->RcvMemoryAllocated -= Size; + } + else + { + pWinsInfo->SendMemoryAllocated -= Size; + } + } + + CTEMemFree(pBuffer); +} diff --git a/private/ntos/nbt/rules16.mk b/private/ntos/nbt/rules16.mk new file mode 100644 index 000000000..fdda3283c --- /dev/null +++ b/private/ntos/nbt/rules16.mk @@ -0,0 +1,79 @@ +C600 = 1 +!include $(COMMON)\src\global.mk + +!ifndef IMPORT +!error IMPORT must be defined in your environment +!endif + +INCLUDE=$(INCLUDE);$(IMPORT)\ddk\386\include +PATH=$(IMPORT)\ddk\386\tools;$(IMPORT)\c700a\bin;$(IMPORT)\masm610\bin;$(PATH) +CINCLUDES=-I$(IMPORT)\C700A\H -I$(IMPORT)\sdk\include +AINCLUDES=-I$(IMPORT)\ddk\386\include +LIB=$(IMPORT)\C700A\LIB;$(IMPORT)\SDK\LIB + + +ASM = $(IMPORT)\masm6\binr\mlx.exe +WIN32 = $(IMPORT)\win32 +WIN32INC= $(WIN32)\ddk\inc +NDIS3 = $(NDIS3) +NDIS3INC = $(NDIS3)\inc + +# +# Common objects get built into Common +# +COMDEBBIN=$(ROOTDIR)\vxd\common\debug +COMNODEBBIN=$(ROOTDIR)\vxd\common\nodebug +COMDEBOBJ=$(COMDEBBIN) +COMNODEBOBJ=$(COMNODEBBIN) + +# +# Chicago specific binaries/objects +# +CDEBBIN=$(ROOTDIR)\vxd\chicago\debug +CNODEBBIN=$(ROOTDIR)\vxd\chicago\nodebug +CDEBOBJ=$(CDEBBIN) +CNODEBOBJ=$(CNODEBBIN) +CHIVNBTOBJD=CNODEBOBJ +CHIDVNBTOBJD=CDEBOBJ + +# +# Snowball specific binaries/objects +# +SDEBBIN=$(ROOTDIR)\vxd\snowball\debug +SNODEBBIN=$(ROOTDIR)\vxd\snowball\nodebug +SDEBOBJ=$(SDEBBIN) +SNODEBOBJ=$(SNODEBBIN) +SNOVNBTOBJD=SNODEBOBJ +SNODVNBTOBJD=SDEBOBJ + +BLT=$(ROOTDIR)\blt +TOOLS=$(ROOTDIR)\tools + +INC=$(ROOTDIR)\inc +H=$(ROOTDIR)\h + +BLTF1=$(BLT:\=/) +BLTF=$(BLTF1:.=\.) + +INCF1=$(INC:\=/) +INCF=$(INCF1:.=\.) + +HF1=$(H:\=/) +HF=$(HF1:.=\.) + +NDIS3F1=$(NDIS3INC:\=/) +NDIS3F=$(NDIS3F1:.=\.) + +BASEDIRF1=$(BASEDIR:\=/) +BASEDIRF=$(BASEDIRF1:.=\.) + +LINK386 = $(WIN32)\ddk\bin\link386 # flat model linker +MAPSYM386 = $(IMPORT)\wintools\bin\mapsym32 # flat model mapsym +ADDHDR = $(WIN32)\ddk\bin\addhdr.exe # windows AddHdr utility +SHTOINC=$(TOOLS)\h2inc.sed + +{$(COMMON)\h}.h{$(BLT)}.inc: + $(SED) -f $(SHTOINC) <$< >$(BLT)\$(@B).inc + +{$(H)}.h{$(BLT)}.inc: + $(SED) -f $(SHTOINC) <$< >$(BLT)\$(@B).inc diff --git a/private/ntos/nbt/setenv.bat b/private/ntos/nbt/setenv.bat new file mode 100644 index 000000000..ecf1eb322 --- /dev/null +++ b/private/ntos/nbt/setenv.bat @@ -0,0 +1,46 @@ +REM +REM Set to your local copy of the import tree from \\flipper\wb\src\import +REM +set IMPORT=d:\nt\import + +REM +REM Not needed if running from RAZZLE screen group +REM +set BASEDIR=d:\nt + +REM +REM Set to your local copy of the common tree from \\flipper\wb\src\common +REM +REM Note that I've copied it under my import tree +REM +set COMMON=d:\nt\import\common + +REM +REM This is Henry's TCP tree. Note that you must also have built in this +REM tree (we pick up the cxport.obj directly). +REM +set TCP=d:\nt\tcp + +REM +REM Point to the nbt project and the dhcp project, respectively +REM +set DHCP=d:\nt\private\net\sockets\tcpcmd\dhcp\client\vxd +set NBT=d:\nt\private\ntos\nbt + +REM +REM Points to the Snowball NDIS3 tree +REM +set NDIS3=d:\nt\import\ndis3 + +REM +REM Points to the Chicago NDIS3 tree +REM +set NDIS31=d:\nt\import\ndis31 + + +set DEFDIR=. +set DEFDRIVE=D: +set SLMREMOTE=\\flipper\wb\src +set BLDHOST=DOS + +PATH=%IMPORT%\COMMON\BIN;%IMPORT%\c8386\BINR;%PATH% diff --git a/private/ntos/nbt/tools/h2inc.sed b/private/ntos/nbt/tools/h2inc.sed new file mode 100644 index 000000000..7f82d7966 --- /dev/null +++ b/private/ntos/nbt/tools/h2inc.sed @@ -0,0 +1,63 @@ +/^\/\*NOINC\*\//,/^\/\*INC\*\// s'^[/*]*';' +/^union[ ]/,/^}/s/^/;/ +/^struct[ ]/,/^};/ { + s/^struct[ ]\(.*\)[ ]*{/\1 struc/ + s/^};[ ]*\/\* \(.*\) \*\//\1 ends/ + s/^[ ]*[^ /][^ ]*[ ]*(\*[ ]*\([^\[;]*\))([ ]*\([^\[;]*\))/ \1 dd ?/ + s/^[ ]*[^ /][^ ]*[ ]*(\*[ ]*\([^\[;]*\))([ ]*\([^\[;]*\))/ \1 dd ?/ + s/struct[ ][ ]*[^ ]*[ ]*FAR[ ]*\*[ ]*FAR[ ]*\*[ ]*\(.*\);/ \1 dd ?/ + s/struct[ ][ ]*[^ ]*[ ]*FAR[ ]*\*[ ]*\(.*\);/ \1 dd ?/ + s/struct[ ][ ]*[^ ]*[ ]*far[ ]*\*[ ]*far[ ]*\*[ ]*\(.*\);/ \1 dd ?/ + s/struct[ ][ ]*[^ ]*[ ]*far[ ]*\*[ ]*\(.*\);/ \1 dd ?/ + s/struct[ ][ ]*[^ ]*[ ]*\*[ ]*\(.*\);/ \1 dd ?/ + s/struct[ ]*\([^ ]*\)[ ]*\([^ ]*\);/\2 db (size \1) dup (?)/ + s/^[ ]*unsigned[ ][^ ][^ ]*[ ]*FAR[ ]*\*[ ]*\([^\[;]*\)/ \1 dd ?/ + s/^[ ]*unsigned[ ][^ ][^ ]*[ ]*far[ ]*\*[ ]*\([^\[;]*\)/ \1 dd ?/ + s/^[ ]*[^ /][^ ]*[ ]*_*FAR[ ]*\*[ ]*\([^\[;]*\)/ \1 dd ?/ + s/^[ ]*[^ /][^ ]*[ ]*_*far[ ]*\*[ ]*\([^\[;]*\)/ \1 dd ?/ + s/^[ ]*[^ /][^ ]*[ ]*\*[ ]*\([^\[;]*\)/ \1 dd ?/ + s/unsigned[ ]*char[ ]*\([^\[;]*\)/\1 db ?/ + s/unsigned[ ]*int[ ]*\([^\[;]*\)/ \1 dd ?/ + s/unsigned[ ]*short[ ]*\([^\[;]*\)/\1 dw ?/ + s/unsigned[ ]*long[ ]*\([^\[;]*\)/\1 dd ?/ + s/IPAddr[ ]*\([^\[;]*\)/\1 dd ?/ + s/IPMask[ ]*\([^\[;]*\)/\1 dd ?/ + s/NDIS_STRING[ ]*\([^\[;]*\)/\1 dd 2 dup (?)/ + s/^[ ]\(.*_t\)[ ][ ]*\([^\[;]*\)/% \2 \1 ?/ + s/CTETimeOutRtn[ ]*\([^\[;]*\)/\1 dd ?/ + s/CTEEventRtn[ ]*\([^\[;]*\)/\1 dd ?/ + s/u*char[ ][ ]*\([^\[;]*\)/\1 db ?/ + s/u*short[ ][ ]*\([^\[;]*\)/\1 dw ?/ + s/^\([ ]*\)u*int[ ]*\([^\[;]*\)/ \1\2 dd ?/ + s/^\([ ]*\)u*long[ ]*\([^\[;]*\)/\1\2 dd ?/ + s/unsigned[ ]*\([^\[;]*\)/\1 dw ?/ + s/?\[\(.*\)\];/\1 dup (?)/ + s/\[\(.*\)\] db (size/ db (\1*size/ + s/\[\(.*\)\] \(d[bwd]\) ?/ \2 \1 dup (?)/ + s/\[\(.*\)\] \(\$[PIF]\) ?/ \2 \1 dup (?)/ + s/?;/?/ +} +/^#define/ { +s/sizeof *(struct *\([^)]*\))/size \1/g +s/sizeof /size / +s/^#define[ ]*\([^ ]*[ ]*\)[ ]0x\([0123456789abcdefABCDEF]*\)/\1 equ 0\2h/ +s/^#define[ ][ ]*\([^ ]*\)[ ][ ]*\([^ ]*\)/\1 equ \2/ +s/>>/SHR/g +s/|/OR/g +} + +/^#include/s/^#include *"\([^ ]*\)\.h"$/include \1\.inc/ +/^\([ ]*\)[\/ ]\*\/*/s//\1;/ +/\/\/\(.*\)/s//;\1/ +/\/\* \(.*\)/s//; \1/ +/ *\*\/ *$/s/// +/volatile/s/volatile// +/^typedef/s/^/;/ +/^#if/s/#// +/^#else/s/#// +/^#endif/s/#// +/^extern API_FUNCTION/,/)[ ]*;[ ]*$/s/^/;/ +/extern[ ]/s/^/;/ +/int _cdecl/,/);$/s/^/;/ +/void _cdecl/,/);$/s/^/;/ +/^API_FUNCTION/,/);$/s/^/;/ diff --git a/private/ntos/nbt/vxd.000/makefile b/private/ntos/nbt/vxd.000/makefile new file mode 100644 index 000000000..47ff1b557 --- /dev/null +++ b/private/ntos/nbt/vxd.000/makefile @@ -0,0 +1,37 @@ +############################################################################# +# +# Microsoft Confidential +# Copyright (C) Microsoft Corporation 1995 +# All Rights Reserved. +# +# Makefile for VNBT directory +# +############################################################################# +ROOT = $(BLDROOT) +DEVICEDIR = NBT +IS_32 = TRUE +IS_PRIVATE = TRUE +IS_SDK = TRUE +IS_DDK = TRUE +DIRLIST = +COMMONMKFILE = VNBT.mk +LIBS=vxdwraps.lib + +!include $(ROOT)\DEV\MASTER.MK + +disk: + copy debug\vnbt.vxd a:vnbt.VXD + copy debug\vnbt.sym a: + +############################################################################# +# +# Beginning of saved settings used by makemake. Do not edit between here +# and the end of the file, except by deleting the entire section. Do not +# delete the blank line that precedes this comment block. +# +# MAKE SURE TO DELETE EVERYTHING FROM HERE TO THE END OF THE MAKEFILE BEFORE +# YOU CHECK IT IN. If you need to add more gunk, add it BEFORE this comment +# block. +# +# +############################################################################# diff --git a/private/ntos/nbt/vxd.000/makeres.bat b/private/ntos/nbt/vxd.000/makeres.bat new file mode 100644 index 000000000..8808af56c --- /dev/null +++ b/private/ntos/nbt/vxd.000/makeres.bat @@ -0,0 +1,5 @@ +md debug +md retail +set INCLUDE=%BLDROOT%\dev\ddk\inc;%BLDROOT%\net\user\common\h +%BLDROOT%\dev\sdk\bin\RC.exe -r -DDEBUG -fodebug\VNBT.res -i %BLDROOT%\dev\sdk\inc16 ..\vxd\vnbt.rcv +%BLDROOT%\dev\sdk\bin\RC.exe -r -foretail\VNBT.res -i %BLDROOT%\dev\sdk\inc16 ..\vxd\vnbt.rcv diff --git a/private/ntos/nbt/vxd.000/vnbt.mk b/private/ntos/nbt/vxd.000/vnbt.mk new file mode 100644 index 000000000..ae531fed2 --- /dev/null +++ b/private/ntos/nbt/vxd.000/vnbt.mk @@ -0,0 +1,122 @@ +############################################################################# +# +# Microsoft Confidential +# Copyright (C) Microsoft Corporation 1995 +# All Rights Reserved. +# +# Makefile for VNBT device +# +############################################################################# + + +ROOT = $(BLDROOT) +DHCP = $(BASEDIR)\private\net\sockets\tcpcmd\dhcp\client\vxd +NTOS = $(BASEDIR)\private\NTOS + +!ifndef NBT +NBT = ..\.. +!endif # NBT + +DEVICE = VNBT +SRCDIR = $(NBT)\VXD +ALTSRCDIR = $(NBT)\NBT +NBTINC = $(NBT)\INC +VXDINC = ..\INC + +DYNAMIC=TRUE +IS_32 = TRUE +IS_PRIVATE = TRUE +IS_SDK = TRUE +IS_DDK = TRUE +MASM6 = TRUE +WANT_MASM611C = TRUE +WANT_C1032 = TRUE +BUILD_COFF = TRUE +DEPENDNAME = ..\depend.mk +TARGETS = dev +PROPBINS = $(386DIR)\VNBT.VXD $(SYMDIR)\VNBT.sym +DEVDIR=$(ROOT)\DEV\DDK\INC +COMMON=$(BLDROOT)\net\user\common +PCHNAM=nbtprocs + +DEBUGFLAGS = -DDEBUG -DSAFE=4 + +OBJS = aaaaaaaa.obj \ + chic.obj \ + chicasm.obj \ + cinit.obj \ + client.obj \ + ctimer.obj \ + cvxdfile.obj \ + cxport.obj \ + dns.obj \ + fileio.obj \ + hashtbl.obj \ + hndlrs.obj \ + inbound.obj \ + init.obj \ + name.obj \ + namesrv.obj \ + newdns.obj \ + nbtinfo.obj \ + nbtutils.obj \ + ncb.obj \ + parse.obj \ + proxy.obj \ + tdiaddr.obj \ + tdicnct.obj \ + tdihndlr.obj \ + tdiout.obj \ + timer.obj \ + udpsend.obj \ + util.obj \ + vnbtd.obj \ + vxddebug.obj \ + vxdisol.obj + +AFLAGS = -c -DIS_32 -nologo -W2 -Cp -Cx -DMASM6 -DCHICAGO +CFLAGS = -c -DVXD -Zp1 -GB -Oxs -nologo -D_X86_=1 -Di386=1 -DDEVL=1 -DPROXY_NODE -DCHICAGO +CLEANLIST = $(SRCDIR)\cxport.asm $(PCHNAM).pch +LOCALINCS = $(SRCDIR)\cxport.asm $(VXDINC)\nbtioctl.h \ + $(VXDINC)\sockets\netinet\in.h $(VXDINC)\sys\snet\ip_proto.h \ + $(BLDROOT)\dev\ddk\inc\vnbt.inc + +!include $(ROOT)\DEV\MASTER.MK + +CFLAGS = $(CFLAGS) -Yu$(PCHNAM).h -Fp$(PCHNAM).pch + +!IF "$(VERDIR)" == "retail" +AFLAGS = $(AFLAGS) -DSAFE=0 +CFLAGS = $(CFLAGS) -DSAFE=0 +!ENDIF + +!IF "$(VERDIR)" == "debug" +AFLAGS = $(AFLAGS) $(DEBUGFLAGS) +CFLAGS = $(CFLAGS) $(DEBUGFLAGS) +!ENDIF + +INCLUDE = $(SRCDIR)\..\CMN\H;$(SRCDIR)\.;$(COMMONHDIR);$(INCLUDE); + +.\aaaaaaaa.obj: $(SRCDIR)\aaaaaaaa.c + set CL=$(CFLAGS) -Yc$(PCHNAM).h + $(CL) -Fo$*.obj $(SRCDIR)\$*.c + +# +# This is so we can get a full COFF build. TCP doesn't use +# COFF .obj files yet. [ERH] 10-18-95 +# + +$(SRCDIR)\cxport.asm: $(TCP)\vtdi\cxport.asm $(TCP)\h\cxport.h + copy $(TCP)\vtdi\cxport.asm $(SRCDIR)\cxport.asm + touch $(SRCDIR)\cxport.asm + +$(VXDINC)\nbtioctl.h: $(BASEDIR)\private\inc\nbtioctl.h + copy $(BASEDIR)\private\inc\nbtioctl.h $(VXDINC)\nbtioctl.h + +$(VXDINC)\sockets\netinet\in.h: $(BASEDIR)\private\inc\sockets\netinet\in.h + copy $(BASEDIR)\private\inc\sockets\netinet\in.h $(VXDINC)\sockets\netinet\in.h + +$(VXDINC)\sys\snet\ip_proto.h: $(BASEDIR)\private\inc\sys\snet\ip_proto.h + copy $(BASEDIR)\private\inc\sys\snet\ip_proto.h $(VXDINC)\sys\snet\ip_proto.h + +INCLUDE = $(NBTINC);$(SRCDIR)\.;$(ALTSRCDIR)\.;$(COMMON)\H;$(TCP)\H;$(TCP)\INC;$(DHCP);$(NBT)\inc;$(VXDINC);$(INCLUDE) diff --git a/private/ntos/nbt/vxd/aaaaaaaa.c b/private/ntos/nbt/vxd/aaaaaaaa.c new file mode 100644 index 000000000..ff3201866 --- /dev/null +++ b/private/ntos/nbt/vxd/aaaaaaaa.c @@ -0,0 +1,2 @@ + +#include "nbtprocs.h" diff --git a/private/ntos/nbt/vxd/chic.c b/private/ntos/nbt/vxd/chic.c new file mode 100644 index 000000000..af3fe87e6 --- /dev/null +++ b/private/ntos/nbt/vxd/chic.c @@ -0,0 +1,1787 @@ +/**********************************************************************/ +/** Microsoft Windows **/ +/** Copyright(c) Microsoft Corp., 1994 **/ +/**********************************************************************/ + +/* + + chic.c + + Contains VxD code that is specific to Chicago + + + FILE HISTORY: + Johnl 14-Mar-1994 Created + +*/ + +#include <nbtprocs.h> +#include <tdiinfo.h> +#include <llinfo.h> +#include <ipinfo.h> +#include <dhcpinfo.h> +#include <nbtinfo.h> + +#ifdef CHICAGO + +//******************* Pageable Routine Declarations **************** +// +// any digit 0 to 9 and '.' are legal characters in an ipaddr +// +#define IS_IPADDR_CHAR( ch ) ( (ch >= '0' && ch <= '9') || (ch == '.') ) + +#define MAX_ADAPTER_DESCRIPTION_LENGTH 128 + +const char szXportName[] = "MSTCP"; + +// +// asking ndis to open,read,close for every single parameter slows down +// bootup: don't open if it's already open +// +NDIS_HANDLE GlobalNdisHandle = NULL; + +// +// This flag is set to TRUE when the first adapter is initialized. It +// indicates that NBT globals (such as node type, scode ID etc) have +// had the opportunity to be set by DHCP. +// +BOOL fGlobalsInitialized = FALSE; + +// +// As each adapter gets added, the Lana offset is added +// +UCHAR iLanaOffset = 0; + +VOID GetMacAddr( ULONG IpAddress, UCHAR MacAddr[] ); +BOOL StopAllNameQueries( tDEVICECONTEXT *pDeviceContext ); +BOOL CancelAllDelayedEvents( tDEVICECONTEXT *pDeviceContext ); +extern tTIMERQ TimerQ; + +BOOL GetNdisParam( LPSTR pszKey, + ULONG * pVal, + NDIS_PARAMETER_TYPE ParameterType ); + + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, IPNotification) +#pragma CTEMakePageable(PAGE, DestroyDeviceObject) +#pragma CTEMakePageable(PAGE, SaveNameDnsServerAddrs) +#pragma CTEMakePageable(PAGE, GetDnsServerAddress) +#pragma CTEMakePageable(PAGE, GetNameServerAddress) +#pragma CTEMakePageable(PAGE, GetMacAddr) +#pragma CTEMakePageable(PAGE, VxdReadIniString) +#pragma CTEMakePageable(PAGE, GetProfileInt) +#pragma CTEMakePageable(PAGE, VxdOpenNdis) +#pragma CTEMakePageable(PAGE, GetNdisParam) +#pragma CTEMakePageable(PAGE, VxdCloseNdis) +#pragma CTEMakePageable(PAGE, StopAllNameQueries) +#pragma CTEMakePageable(PAGE, VxdUnload) +#pragma CTEMakePageable(PAGE, ReleaseNbtConfigMem) +#endif + + +/******************************************************************* + + NAME: IPNotification + + SYNOPSIS: Called by the IP driver when a new Lana needs to be created + or destroyed for an IP address. + + ENTRY: pDevNode - Plug'n'Play context + IpAddress - New ip address + IpMask - New ip mask + fNew - Are we creating or destroying this Lana? + + NOTES: This routine is only used by Chicago + + HISTORY: + Johnl 17-Mar-1994 Created + +********************************************************************/ + +TDI_STATUS IPNotification( ULONG IpAddress, + ULONG IpMask, + PVOID pDevNode, + USHORT IPContext, + BOOL fNew ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG IpNS[COUNT_NS_ADDR]; + ULONG IpDns[COUNT_NS_ADDR]; + int iLana; + UCHAR RequestedLana; + int i; + int iEmpty; + UCHAR PreviousNodeType; + UCHAR MacAddr[6]; + + CTEPagedCode(); + + KdPrint(("IPNotification entered\r\n")); + + if ( !IpAddress ) + { + return TDI_SUCCESS ; + } + + if ( fNew ) + { + // + // parameters nodetype, scope and bcastaddr are systemwide, not per + // adapter: so read only once. + // + if ( !fGlobalsInitialized ) + { + PreviousNodeType = NodeType; + + // + // This will re-read the DHCPable parameters now that we have + // a potential DHCP source + // + VxdOpenNdis(); + ReadParameters2( pNbtGlobConfig, NULL ); + VxdCloseNdis(); + + if (PreviousNodeType & PROXY) + { + NodeType |= PROXY; + } + + + fGlobalsInitialized = TRUE; + } + + // + // Get the name servers for this device context (ip address) + // + GetNameServerAddress( IpAddress, IpNS); + + // + // Get the DNS servers for this device context (ip address) + // + GetDnsServerAddress( IpAddress, IpDns); + + // + // Find a free spot in our Lana table + // + + for ( iEmpty = 0; iEmpty < NBT_MAX_LANAS; iEmpty++) + { + if (LanaTable[iEmpty].pDeviceContext == NULL) + goto Found; + } + + // + // Lana table is full so bail + // + CDbgPrint(DBGFLAG_ERROR,("IPNotification: LanaTable full\r\n")); + return STATUS_INSUFFICIENT_RESOURCES; + +Found: + + GetMacAddr( IpAddress, MacAddr ); + + status = CreateDeviceObject( pNbtGlobConfig, + htonl( IpAddress ), + htonl( IpMask ), + IpNS[0], + IpNS[1], + IpDns[0], + IpDns[1], + MacAddr, + 0 ); + if (status != STATUS_SUCCESS) + { + CDbgPrint(DBGFLAG_ERROR,("IPNotification: CreateDeviceObject Failed\r\n")); + return status; + } + + // + // We first try and ask for a specific Lana from vnetbios based on + // our Lanabase and how many other Lanas we've already added. If + // this fails, then we will ask for Any Lana. If the LANABASE + // parameter is not specified, then request Any Lana. + // + + if ( LanaBase != VXD_ANY_LANA ) + RequestedLana = LanaBase + iLanaOffset++ ; + else + RequestedLana = VXD_ANY_LANA; + +RetryRegister: + if ( (iLana = RegisterLana2( pDevNode, RequestedLana )) == 0xff ) + { + if ( RequestedLana == VXD_ANY_LANA ) + { + // + // We couldn't get *any* lanas so bail + // + CDbgPrint(DBGFLAG_ERROR,("IPNotification: RegisterLana2 Failed\r\n")); + DestroyDeviceObject( pNbtGlobConfig, htonl(IpAddress)); + return STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // + // Somebody may already have this Lana so beg for another one + // + RequestedLana = VXD_ANY_LANA; + goto RetryRegister; + } + } + + KdPrint(("IPNotification: using Lana %d\r\n", iLana )); + LanaTable[iEmpty].pDeviceContext = + (tDEVICECONTEXT*)pNbtGlobConfig->DeviceContexts.Blink ; + LanaTable[iEmpty].pDeviceContext->iLana = iLana; + + // + // remove our child (redir, that is!) and reenumerate our devnode + // so redir knows we are there! + // + ReconfigureDevnode( pDevNode ); + } + else + { + status = DestroyDeviceObject( pNbtGlobConfig, + htonl(IpAddress) ); + + } + + return status; +} + +/******************************************************************* + + NAME: DestroyDeviceObject + + SYNOPSIS: Destroys the specified device + + ENTRY: pConfig - Global config structure + IpAddr - Destroy the adapter with this address + + NOTES: This routine is only used by Chicago + + HISTORY: + Johnl 17-Mar-1994 Created + +********************************************************************/ + +NTSTATUS DestroyDeviceObject( + tNBTCONFIG *pConfig, + ULONG IpAddr + ) +{ + LIST_ENTRY * pEntry; + LIST_ENTRY * pHead; + tDEVICECONTEXT * pDeviceContext; + tDEVICECONTEXT * pTmpDeviceContext; + tDEVICECONTEXT * pNextDeviceContext; + tCLIENTELE * pClientEle; + tADDRESSELE * pAddress; + tNAMEADDR * pNameAddr; + tCONNECTELE * pConnEle; + tLOWERCONNECTION * pLowerConn; + PRCV_CONTEXT prcvCont; + tRCVELE * pRcvEle ; + tTIMERQENTRY * pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + tDGRAM_SEND_TRACKING * pTracker; + CTELockHandle OldIrq; + int i; + + + CTEPagedCode(); + + // + // Find which device is going away + // Also, find out a device object that is still active: we need that info + // to update some of the address ele's. + // + pDeviceContext = NULL; + pNextDeviceContext = NULL; + + for ( pEntry = pConfig->DeviceContexts.Flink; + pEntry != &pConfig->DeviceContexts; + pEntry = pEntry->Flink ) + { + pTmpDeviceContext = CONTAINING_RECORD( pEntry, tDEVICECONTEXT, Linkage); + if ( pTmpDeviceContext->IpAddress == IpAddr ) + pDeviceContext = pTmpDeviceContext; + else + pNextDeviceContext = pTmpDeviceContext; + } + + if (pDeviceContext == NULL) + return STATUS_INVALID_PARAMETER; + + // + // don't accept anymore ncbs on this device + // + pDeviceContext->fDeviceUp = FALSE; + + // + // Close all the connections + // + NbtNewDhcpAddress( pDeviceContext, 0, 0); + + if ( --NbtConfig.AdapterCount == 1) + NbtConfig.MultiHomed = FALSE; + + ASSERT(IsListEmpty(&pDeviceContext->LowerConnFreeHead)); + + // + // if we are destroying the last device then + // + if ( NbtConfig.AdapterCount == 0) + { + // + // Kill off all of the Receive any from any NCBs + // + while ( !IsListEmpty( &pDeviceContext->RcvAnyFromAnyHead )) + { + pEntry = RemoveHeadList( &pDeviceContext->RcvAnyFromAnyHead ) ; + prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + CTEIoComplete( prcvCont->pNCB, STATUS_NETWORK_NAME_DELETED, 0 ) ; + } + + // + // Kill off all of the Receive any datagrams from any + // + while ( !IsListEmpty(&pDeviceContext->RcvDGAnyFromAnyHead)) + { + pEntry = RemoveHeadList( &pDeviceContext->RcvDGAnyFromAnyHead ) ; + pRcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ; + CTEIoComplete( pRcvEle->pIrp, STATUS_NETWORK_NAME_DELETED, 0 ) ; + CTEMemFree( pRcvEle ) ; + } + } + + // + // if any name queries are in progress, stop them now + // + StopAllNameQueries( pDeviceContext ); + + CancelAllDelayedEvents( pDeviceContext ); + + // + // walk through all names and see if any is being registered on this + // device context: if so, stop and complete it! + // + for (i=0;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + pEntry = pEntry->Flink; + + if (pNameAddr->NameTypeState & STATE_RESOLVING) + { + pTimer = pNameAddr->pTimer; + + // + // if the name registration was started for this name on this device + // context, stop the timer. (Completion routine will take care of + // doing registration on other device contexts if applicable) + // + if (pTimer) + { + pTracker = pTimer->Context; + ASSERT(pTracker->pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + if (pTracker->pDeviceContext == pDeviceContext) + { + ASSERT(pTracker->pNameAddr == pNameAddr) + + pNameAddr->pTimer = NULL; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + StopTimer(pTimer,&pClientCompletion,&Context); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (pClientCompletion) + { + (*pClientCompletion)(Context,STATUS_NETWORK_NAME_DELETED); + } + + DbgPrint("DestroyDeviceObject: stopped name reg timer") ; + } + } + + } + } + } + + + // + // walk through all the client ele's this device context has and clean + // up all the mess this clientele created! + // + for ( i = 0 ; i <= pDeviceContext->cMaxNames ; i++ ) + { + pClientEle = pDeviceContext->pNameTable[i]; + + if ( !pClientEle ) + continue; + + VxdCleanupAddress( pDeviceContext, + NULL, + pClientEle, + (UCHAR)i, + TRUE ); + } + + // + // close all the TDI handles + // + CloseAddressesWithTransport(pDeviceContext); + + // + // if a call was started, but aborted then we could have some memory here! + // + while (!IsListEmpty(&pDeviceContext->UpConnectionInUse)) + { + pEntry = RemoveHeadList(&pDeviceContext->UpConnectionInUse); + pConnEle = CONTAINING_RECORD(pEntry,tCONNECTELE,Linkage); + CTEMemFree( pConnEle ); + } + + while (!IsListEmpty(&pDeviceContext->LowerConnection)) + { + pEntry = RemoveHeadList(&pDeviceContext->LowerConnection); + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + CTEMemFree( pLowerConn ); + } + + // + // Remove the device from our Lana table and Vnetbios + // + for ( i = 0; i < NBT_MAX_LANAS; i++) + { + if (LanaTable[i].pDeviceContext == pDeviceContext) + { + DeregisterLana(LanaTable[i].pDeviceContext->iLana); + LanaTable[i].pDeviceContext = NULL; + KdPrint(("DestroyDeviceObject: deregistered Lana %d\r\n",pDeviceContext->iLana)); + break; + } + } + + RemoveEntryList( &pDeviceContext->Linkage); + + // + // Walk through the AddressHead list. If any addresses exist and they + // point to old device context, put the next device context. Also, update + // adapter mask to reflect that this device context is now gone. + // + KdPrint(("DestroyDeviceObject: setting AddrEle,NameAddr fields\r\n")); + pHead = pEntry = &NbtConfig.AddressHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pAddress = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + ASSERT (pAddress->Verify == NBT_VERIFY_ADDRESS); + if (pAddress->pDeviceContext == pDeviceContext) + { + pAddress->pDeviceContext = pNextDeviceContext; + } + + pAddress->pNameAddr->AdapterMask &= ~pDeviceContext->AdapterNumber; + } + + if (pDeviceContext->pNameTable) + CTEMemFree( pDeviceContext->pNameTable ); + + if (pDeviceContext->pSessionTable) + CTEMemFree( pDeviceContext->pSessionTable ); + + CTEMemFree( pDeviceContext ); + + return STATUS_SUCCESS; +} + + +/******************************************************************* + + NAME: SaveNameDnsServerAddrs + + SYNOPSIS: Get the name server and dns server addrs from the registry + and save it in Nbtconfig. We do this so that when a new + adapter comes in or lease gets renewed (particularly the + latter), we don't have to go read the registry becuase it + may not be safe to do so. + This routine will have to be modified if setup ever changes + to allow configuration of name/dns servers per lana. + + ENTRY: Nothing + + + HISTORY: + Koti 20-Nov-1994 Created + +********************************************************************/ + +VOID SaveNameDnsServerAddrs( VOID ) +{ + UCHAR i ; + PUCHAR pchSrv = "NameServer$" ; + PUCHAR pchDnsSrv = "NameServer" ; + PUCHAR pchSrvNum; + LPTSTR pchString ; + PUCHAR pchCurrent, pchNext; + BOOL fOneMore=TRUE; + ULONG IpNameServers[COUNT_NS_ADDR]; + + CTEPagedCode(); + + // + // Get Name Server ipaddrs + // + + pchSrvNum = pchSrv + 10 ; // to overwrite '$' with 1,2,3 etc. + + for ( i = 0; i < COUNT_NS_ADDR; i++) + { + IpNameServers[i] = LOOP_BACK;; + *pchSrvNum = '1' + i; + + // call GetNdisParam directly so that we don't query dhcp here + // if ( !CTEReadIniString( NULL, pchSrv, &pchString ) ) + // + if ( GetNdisParam( pchSrv, (ULONG *)&pchString, NdisParameterString ) ) + { + if ( ConvertDottedDecimalToUlong( pchString, &IpNameServers[i] )) + { + DbgPrint("SaveNameDnsServerAddrs: bad name srv addr\r\n") ; + IpNameServers[i] = LOOP_BACK; + } + CTEFreeMem( pchString ) ; + } + } + + // + // store the name server ipaddrs (potentially 0 if not defined in registry) + // + NbtConfig.lRegistryNameServerAddress = IpNameServers[0]; + NbtConfig.lRegistryBackupServer = IpNameServers[1]; + + + // + // Now get Dns Server ipaddrs + // + + // + // initialize all of them to worst case + // + for ( i = 0; i < COUNT_NS_ADDR; i++) + { + IpNameServers[i] = LOOP_BACK; + } + + + // call GetNdisParam directly so that we don't query dhcp here + // if ( !CTEReadIniString( NULL, pchDnsSrv, &pchString ) ) + // + if ( GetNdisParam( pchDnsSrv, (ULONG *)&pchString, NdisParameterString ) ) + { + // + // we are generating (upto) COUNT_NS_ADDR pointers each pointing to + // one ipaddr. The string in system.ini looks like: + // NameServer = 11.101.4.26,200.200.200.200,1.2.3.4,1.91.245.10 + // + pchNext = pchCurrent = pchString; + + if ( IS_IPADDR_CHAR(*pchCurrent) ) // make sure at least one ipaddr defnd + { + i = 0; + while( (i < COUNT_NS_ADDR) && fOneMore ) + { + while( IS_IPADDR_CHAR(*pchNext) ) + pchNext++; + + if ( *pchNext == ',' ) // ',' is the separator between 2 addrs + { + *pchNext = '\0'; + pchNext++; + } + else + { + fOneMore = FALSE; // reached end of line + } + + // + // as long as at least the first one ipaddr gets converted properly, + // ignore errors in others + // + if ( ConvertDottedDecimalToUlong( pchCurrent, &IpNameServers[i] )) + { + DbgPrint("SaveNameDnsServerAddrs: bad dns srv addr\r\n") ; + IpNameServers[i] = LOOP_BACK; + } + + i++; + + pchCurrent = pchNext; // go, convert the next one + } + } + + if( pchString != NULL ) + { + CTEFreeMem( pchString ) ; + } + } + + // + // store the dns server ipaddrs (potentially 0 if not defined in registry) + // + NbtConfig.lRegistryDnsServerAddress = IpNameServers[0]; + NbtConfig.lRegistryDnsBackupServer = IpNameServers[1]; + +} + + +/******************************************************************* + + NAME: GetDnsServerAddress + + SYNOPSIS: Gets the DNS server ipaddrs from the registry. + Or, if DHCP is installed and the DNS server addresses aren't + found, we get them from DHCP + + ENTRY: IpAddr - If we can get from DHCP, get form this address + pIpDnsServer - Receives addresses if found (otherwise 0) + + NOTES: This routine is only used by Snowball + + HISTORY: + Koti 18-Oct-1994 Created + +********************************************************************/ + +void GetDnsServerAddress( ULONG IpAddr, PULONG pIpDnsServer) +{ + + UCHAR i ; + UINT OptId; + TDI_STATUS tdistatus ; + ULONG Buff[COUNT_NS_ADDR] ; + ULONG Size; + + CTEPagedCode(); + + + for ( i = 0; i < COUNT_NS_ADDR; i++) + { + pIpDnsServer[i] = LOOP_BACK ; + } + + pIpDnsServer[0] = NbtConfig.lRegistryDnsServerAddress; + pIpDnsServer[1] = NbtConfig.lRegistryDnsBackupServer; + + // + // if it was defined in the registry, we are done + // + if ( pIpDnsServer[0] != LOOP_BACK || pIpDnsServer[1] != LOOP_BACK ) + { + return; + } + + // + // it was not defined in the registry: try getting them from DHCP + // + Size = sizeof( Buff ) ; + + OptId = 6; // DNS Option + + tdistatus = DhcpQueryOption( IpAddr, + OptId, + &Buff, + &Size ) ; + + switch ( tdistatus ) + { + case TDI_SUCCESS: + case TDI_BUFFER_OVERFLOW: // May be more then one our buffer will hold + for ( i = 0; i < COUNT_NS_ADDR; i++ ) + { + if ( Size >= (sizeof(ULONG)*(i+1))) + pIpDnsServer[i] = htonl(Buff[i]) ; + } + break ; + + case TDI_INVALID_PARAMETER: // Option not found + break; + + default: + ASSERT( FALSE ) ; + break ; + } + + KdPrint(("GetDnsServerAddress: Primary: %x, backup: %x\r\n", + pIpDnsServer[0], pIpDnsServer[1] )) ; + +} + + +/******************************************************************* + + NAME: GetNameServerAddress + + SYNOPSIS: Gets the Win server for the specified Lana. + + Or, if DHCP is installed and the Name server addresses aren't + found, we get them from DHCP + + ENTRY: IpAddr - If we can get from DHCP, get form this address + pIpNameServer - Receives addresses if found (otherwise 0) + + NOTES: This routine is only used by Snowball + + HISTORY: + Johnl 21-Oct-1993 Created + +********************************************************************/ + +void GetNameServerAddress( ULONG IpAddr, + PULONG pIpNameServer) +{ + UCHAR i ; + UINT OptId; + TDI_STATUS tdistatus ; + ULONG Buff[COUNT_NS_ADDR] ; + ULONG Size; + + CTEPagedCode(); + + + for ( i = 0; i < COUNT_NS_ADDR; i++) + { + pIpNameServer[i] = LOOP_BACK ; + } + + pIpNameServer[0] = NbtConfig.lRegistryNameServerAddress; + pIpNameServer[1] = NbtConfig.lRegistryBackupServer; + + // + // if it was defined in the registry, we are done + // + if ( pIpNameServer[0] != LOOP_BACK || pIpNameServer[1] != LOOP_BACK ) + { + return; + } + + + // + // not defined in the registry: try to get it from dhcp + // + + OptId = 44; // NBNS Option + + Size = sizeof( Buff ) ; + + tdistatus = DhcpQueryOption( IpAddr, + OptId, + &Buff, + &Size ) ; + + switch ( tdistatus ) + { + case TDI_SUCCESS: + case TDI_BUFFER_OVERFLOW: // May be more then one our buffer will hold + for ( i = 0; i < COUNT_NS_ADDR; i++ ) + { + if ( Size >= (sizeof(ULONG)*(i+1))) + pIpNameServer[i] = htonl(Buff[i]) ; + } + break ; + + case TDI_INVALID_PARAMETER: // Option not found + break ; + + default: + ASSERT( FALSE ) ; + break ; + } + + KdPrint(("GetNameServerAddress: Primary: %x, backup: %x\r\n", + pIpNameServer[0], pIpNameServer[1] )) ; + +} + + +/******************************************************************* + + NAME: GetMacAddr + + SYNOPSIS: Gets the mac address for the give ipaddr + + ENTRY: IpAddress - the address for which to find mac addr + MacAddr - the array where to store mac addr + + HISTORY: + Koti 19-Oct-1994 Created + +********************************************************************/ + +VOID GetMacAddr( ULONG IpAddress, UCHAR MacAddr[] ) +{ + + TDI_STATUS tdistatus ; + int i, j, k ; + uchar Context[CONTEXT_SIZE] ; + TDIObjectID ID ; + TDIEntityID EList[MAX_TDI_ENTITIES] ; + ULONG Size ; + UINT NumReturned ; + NDIS_BUFFER ndisbuff ; + IFEntry *ifeAdapterInfo[MAX_TDI_ENTITIES]; + UINT AdptNum; + + CTEPagedCode(); + + + // + // initialize to 0, in case things don't work out + // + memset( MacAddr, 0, 6 ) ; + + // + // The first thing to do is get the list of available entities, and make + // sure that there are some interface entities present. + // + ID.toi_entity.tei_entity = GENERIC_ENTITY; + ID.toi_entity.tei_instance = 0; + ID.toi_class = INFO_CLASS_GENERIC; + ID.toi_type = INFO_TYPE_PROVIDER; + ID.toi_id = ENTITY_LIST_ID; + + Size = sizeof(EList); + InitNDISBuff( &ndisbuff, &EList, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + + if (tdistatus != TDI_SUCCESS) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Querying entity list failed\r\n")) ; + return; + } + + NumReturned = (uint)Size/sizeof(TDIEntityID); + + AdptNum = 0; + // + // first find out info about the adapters + // + for (i = 0; i < NumReturned; i++) + { + // + // if this entity/instance describes an adapter + // + if ( EList[i].tei_entity == IF_ENTITY ) + { + DWORD isMib; + + + ID.toi_entity.tei_entity = EList[i].tei_entity ; + ID.toi_entity.tei_instance = EList[i].tei_instance; + ID.toi_class = INFO_CLASS_GENERIC ; + ID.toi_type = INFO_TYPE_PROVIDER; + ID.toi_id = ENTITY_TYPE_ID ; + + Size = sizeof( isMib ); + InitNDISBuff( &ndisbuff, &isMib, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Getting isMib failed\r\n")) ; + return ; + } + + // + // Does this entity support MIB + // + if (isMib != IF_MIB) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: skipping non-MIB entity\r\n")) ; + continue; + } + + // + // MIB requests supported - query the adapter info + // + + Size = sizeof(IFEntry) + MAX_ADAPTER_DESCRIPTION_LENGTH + 1; + + ifeAdapterInfo[AdptNum] = (IFEntry *)CTEAllocInitMem(Size); + + if ( ifeAdapterInfo[AdptNum] == NULL ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Couldn't allocate AdapterInfo buffer\r\n")) ; + for ( k=0; k<AdptNum; k++ ) + { + CTEFreeMem( ifeAdapterInfo[k] ) ; + } + return; + } + + ID.toi_class = INFO_CLASS_PROTOCOL;; + ID.toi_id = IF_MIB_STATS_ID; + + Size = sizeof(IFEntry) + MAX_ADAPTER_DESCRIPTION_LENGTH + 1; + InitNDISBuff( &ndisbuff, ifeAdapterInfo[AdptNum], Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Getting IF type failed\r\n")) ; + for ( k=0; k<AdptNum; k++ ) + { + CTEFreeMem( ifeAdapterInfo[k] ) ; + } + return ; + } + + AdptNum++; + } + } + + // + // now that we know about the adapters, get the ipaddrs + // + for (i = 0; i < NumReturned; i++) + { + if ( EList[i].tei_entity == CL_NL_ENTITY ) + { + IPSNMPInfo IPStats ; + IPAddrEntry * pIAE ; + ULONG NLType ; + ULONG IpNameServer[COUNT_NS_ADDR]; + ULONG IpDnsServer[COUNT_NS_ADDR]; + + // + // Does this entity support IP? + // + + ID.toi_entity.tei_entity = EList[i].tei_entity ; + ID.toi_entity.tei_instance = EList[i].tei_instance; + ID.toi_class = INFO_CLASS_GENERIC ; + ID.toi_type = INFO_TYPE_PROVIDER; + ID.toi_id = ENTITY_TYPE_ID ; + + Size = sizeof( NLType ); + InitNDISBuff( &ndisbuff, &NLType, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Getting NL type failed\r\n")) ; + for ( k=0; k<AdptNum; k++ ) + { + CTEFreeMem( ifeAdapterInfo[k] ) ; + } + return ; + } + + if ( NLType != CL_NL_IP ) + continue ; + + // + // We've got an IP driver so get it's address table + // + + ID.toi_class = INFO_CLASS_PROTOCOL ; + ID.toi_id = IP_MIB_STATS_ID; + Size = sizeof(IPStats); + InitNDISBuff( &ndisbuff, &IPStats, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Getting IPStats failed\r\n")) ; + continue ; + } + + if ( IPStats.ipsi_numaddr < 1 ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: No IP Addresses installed\r\n")) ; + continue ; + } + + Size = sizeof(IPAddrEntry) * IPStats.ipsi_numaddr ; + if ( !(pIAE = (IPAddrEntry*) CTEAllocInitMem( Size )) ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Couldn't allocate IP table buffer\r\n")) ; + continue ; + } + + ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID ; + InitNDISBuff( &ndisbuff, pIAE, Size, NULL ) ; + memset( Context, 0, CONTEXT_SIZE ) ; + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetMacAddr: Getting IP address table failed\r\n")) ; + CTEFreeMem( pIAE ) ; + continue ; + } + + // ASSERT( Size/sizeof(IPAddrEntry) >= IPStats.ipsi_numaddr ) ; + + // + // We have the IP address table for this IP driver. Look for + // our IP address + // + + for ( j = 0 ; j < IPStats.ipsi_numaddr ; j++ ) + { + // + // find our ipaddress + // + if ( pIAE[j].iae_addr != IpAddress ) + { + continue ; + } + + // + // now find out the mac address for this ipaddr + // + for ( k=0; k<AdptNum; k++ ) + { + if ( ifeAdapterInfo[k]->if_index == pIAE[j].iae_index ) + { + CTEMemCopy( MacAddr, ifeAdapterInfo[k]->if_physaddr, 6 ); + break; + } + } + } + + if (pIAE) + { + CTEFreeMem( pIAE ) ; + } + } + } + + for ( k=0; k<AdptNum; k++ ) + { + CTEFreeMem( ifeAdapterInfo[k] ) ; + } + +} + + +/******************************************************************* + + NAME: VxdReadIniString + + SYNOPSIS: Vxd stub for CTEReadIniString + + ENTRY: pchKey - Key value to look for in the NBT section + ppchString - Pointer to buffer found string is returned in + + EXIT: ppchString will point to an allocated buffer + + RETURNS: STATUS_SUCCESS if found + + NOTES: The client must free ppchString when done with it + + HISTORY: + Johnl 30-Aug-1993 Created + +********************************************************************/ + +NTSTATUS VxdReadIniString( LPSTR pchKey, LPSTR * ppchString ) +{ + CTEPagedCode(); + + if ( GetNdisParam( pchKey, (ULONG *)ppchString, NdisParameterString ) ) + { + return STATUS_SUCCESS ; + } + else + { + // + // Does DHCP have it? + // + + if ( *ppchString = (char *) GetDhcpOption( pchKey, 0 ) ) + { + return STATUS_SUCCESS ; + } + } + + return STATUS_UNSUCCESSFUL ; +} + +/******************************************************************* + + NAME: GetProfileInt + + SYNOPSIS: Gets the specified value from the registry or DHCP + + ENTRY: pchKey - Key value to look for in the NBT section + Default - Default value if not in registry or DHCP + Min - Minimum value can be + + RETURNS: Registry Value or Dhcp value or default value + + NOTES: + + HISTORY: + Johnl 23-Mar-1994 Created + +********************************************************************/ + +ULONG GetProfileInt( PVOID p, LPSTR pchKey, ULONG Default, ULONG Min ) +{ + ULONG Val = Default; + + CTEPagedCode(); + + // + // Is the value in the registry? + // + if ( !GetNdisParam( pchKey, &Val, NdisParameterInteger ) ) + { + // + // No, Check DHCP + // + Val = GetDhcpOption( pchKey, Default ); + } + + if ( Val < Min ) + { + Val = Min; + } + + return Val; +} + +ULONG GetProfileHex( PVOID p, LPSTR pchKey, ULONG Default, ULONG Min ) +{ + return GetProfileInt( p, pchKey, Default, Min); +} + +/******************************************************************* + + NAME: VxdOpenNdis + + SYNOPSIS: Prepare Ndis to read entries from the registry. Ndis + basically opens the registry and gives a handle back + which we store in GlobalNdisHandle. + + ENTRY: Nothing (GlobalNdisHandle is global) + RETURNS: TRUE if things worked well + FALSE if some error occured + + HISTORY: + Koti Nov. 20, 94 + +********************************************************************/ + +BOOL VxdOpenNdis( VOID ) +{ + + NDIS_STATUS Status; + NDIS_STRING Name; + + CTEPagedCode(); + + + ASSERT( !GlobalNdisHandle ); + + // Open the config information. + Name.Length = strlen(szXportName) + 1; + Name.MaximumLength = Name.Length; + Name.Buffer = (PUCHAR)szXportName; + + NdisOpenProtocolConfiguration(&Status, &GlobalNdisHandle, &Name); + if (Status != NDIS_STATUS_SUCCESS) + { + // Unable to open the configuration. Fail now. + GlobalNdisHandle = NULL; + ASSERT(0); + return FALSE; + } + + return TRUE; + +} +/******************************************************************* + + NAME: GetNdisParam + + SYNOPSIS: Gets the value from the MSTCP protocol sectio of the registry + + ENTRY: pchKey - Key value to look for in the NBT section + pVal - Retrieved parameter + ParameterType - Type of parameter (string, int) + + RETURNS: TRUE if the value was found, FALSE otherwise + + NOTES: If the parameter is a string parameter, then this routine + will allocate memory which the client is responsible for + freeing. + + HISTORY: + Johnl 23-Mar-1994 Created + +********************************************************************/ + +BOOL GetNdisParam( LPSTR pszKey, + ULONG * pVal, + NDIS_PARAMETER_TYPE ParameterType ) +{ + NDIS_STATUS Status; + NDIS_STRING Name; + uint i; + PNDIS_CONFIGURATION_PARAMETER Param; + BOOL fRet = FALSE; + + CTEPagedCode(); + + + ASSERT( GlobalNdisHandle ); + + Name.Length = strlen(pszKey) + 1; + Name.MaximumLength = Name.Length; + Name.Buffer = pszKey; + NdisReadConfiguration(&Status, &Param, GlobalNdisHandle, &Name, + ParameterType); + + if (Status == NDIS_STATUS_SUCCESS) + { + if ( ParameterType == NdisParameterString) + { + LPSTR lpstr = CTEAllocInitMem( Param->ParameterData.StringData.Length + 1 ) ; + + if ( lpstr ) + { + strcpy( lpstr, Param->ParameterData.StringData.Buffer ); + *pVal = (ULONG) lpstr; + fRet = TRUE; + } + } + else + { + *pVal = Param->ParameterData.IntegerData; + fRet = TRUE; + } + } + + return fRet ; +} + +/******************************************************************* + + NAME: VxdCloseNdis + + SYNOPSIS: Close the handle that we opened in VxdOpenNdis + + ENTRY: Nothing (GlobalNdisHandle is global) + + HISTORY: + Koti Nov. 20, 94 + +********************************************************************/ + +VOID VxdCloseNdis( VOID ) +{ + + CTEPagedCode(); + + ASSERT(GlobalNdisHandle); + + if (GlobalNdisHandle != NULL) + { + NdisCloseConfiguration(GlobalNdisHandle); + GlobalNdisHandle = NULL; + } +} + + +/******************************************************************* + + NAME: StopAllNameQueries + + SYNOPSIS: This routine is called when the device context is going + away. It finds out all the name queries that are in + progress (started by someone on this device context) and + stops them. + + ENTRY: pDeviceContext - the device context that is going away. + + RETURNS: TRUE if at least one name query was found and stopped + FALSE if there wasn't any query in progress + + HISTORY: + Koti Nov. 19, 94 + +********************************************************************/ + +BOOL +StopAllNameQueries( tDEVICECONTEXT *pDeviceContext ) +{ + + tDGRAM_SEND_TRACKING *pTracker; + NBT_WORK_ITEM_CONTEXT *Context; + PVOID pClientCompletion; + PVOID pClientContext; + tNAMEADDR *pNameAddr; + tTIMERQENTRY *pTimer; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + CTELockHandle OldIrq; + + CTEPagedCode(); + + // + // first check to see if any names are on the pending list: all name + // queries over the network will be here (WINS, broadcast, DNS) + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pHead = &NbtConfig.PendingNameQueries; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + + pEntry = pEntry->Flink; + + pTimer = pNameAddr->pTimer; + if (pTimer) + { + pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; + + ASSERT (pTracker->pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + if (pTracker->pDeviceContext == pDeviceContext) + { + StopTimer(pTimer,(COMPLETIONCLIENT *)&pClientCompletion,&Context); + if (pClientCompletion) + { + // + // Remove from the pending list + // + RemoveEntryList(&pNameAddr->Linkage); + InitializeListHead(&pNameAddr->Linkage); + + pNameAddr->pTimer = NULL; + NbtDereferenceName(pNameAddr); + + // + // complete client's ncb. If requests were queued on + // the same name by other clients, this will start new + // name queries for those clients + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CompleteClientReq(pClientCompletion, + (tDGRAM_SEND_TRACKING *)Context, + STATUS_NETWORK_NAME_DELETED); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + DbgPrint("StopAllNameQueries: stopped net name query") ; + } + } + } + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // now check if any names are on the lmhosts queue: all name queries + // waiting for lmhosts (and/or hosts) parsing will be here + // + + Context = NULL; + if (LmHostQueries.ResolvingNow && LmHostQueries.Context) + { + Context = (NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context; + pTracker = Context->pTracker; + + ASSERT (pTracker->pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + if (pTracker->pDeviceContext == pDeviceContext) + { + LmHostQueries.Context = NULL; + + pClientCompletion = Context->ClientCompletion; + pClientContext = Context->pClientContext; + + // name did not resolve, so delete from table + RemoveName(pTracker->pNameAddr); + + DereferenceTracker(pTracker); + + CompleteClientReq(pClientCompletion, + pClientContext, + STATUS_NETWORK_NAME_DELETED); + DbgPrint("StopAllNameQueries: stopped lmhosts query in progress") ; + } + } + + // + // now walk through the queued items and cancel all the relevant ones + // + pHead = &LmHostQueries.ToResolve; + pEntry = pHead->Flink; + + while (pEntry != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + pEntry = pEntry->Flink; + + pTracker = Context->pTracker; + + ASSERT (pTracker->pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + if (pTracker->pDeviceContext == pDeviceContext) + { + RemoveEntryList(&Context->Item.List); + + pClientCompletion = Context->ClientCompletion; + pClientContext = Context->pClientContext; + + // name did not resolve, so delete from table + RemoveName(pTracker->pNameAddr); + + DereferenceTracker(pTracker); + + CompleteClientReq(pClientCompletion, + pClientContext, + STATUS_NETWORK_NAME_DELETED); + DbgPrint("StopAllNameQueries: cancelled lmhosts queries in queue") ; + } + } + +} + + +/******************************************************************* + + NAME: VxdUnload + + SYNOPSIS: This is where we are asked to unload. We destroy all + the device objects and then unload ourselves if the + message came from vtcp. + + ENTRY: pchModuleName - name of the module that is going away. + We take action only if vtcp is going away + (in which case, pchModuleName is "MSTCP") + + RETURNS: STATUS_SUCCESS if things went ok + + HISTORY: + Koti Oct 5, 94 + +********************************************************************/ + +NTSTATUS +VxdUnload( LPSTR pchModuleName ) +{ + + LIST_ENTRY * pEntry; + tDEVICECONTEXT * pDeviceContext; + NTSTATUS status; + + CTEPagedCode(); + + KdPrint(("VxdUnload entered\r\n")); + + if ( (pchModuleName == NULL) || + (strcmp(pchModuleName,szXportName) != 0) ) + { + CDbgPrint(DBGFLAG_ERROR,("VxdUnload: Unload msg not from MSTCP\r\n")); + return STATUS_SUCCESS; + } + + // + // all devices will be destroyed by the time VxdUnload is called: this is + // just being paranoid + // + for ( pEntry = pNbtGlobConfig->DeviceContexts.Flink; + pEntry != &pNbtGlobConfig->DeviceContexts; + ) + { + pDeviceContext = CONTAINING_RECORD( pEntry, tDEVICECONTEXT, Linkage); + pEntry = pEntry->Flink; + + status = DestroyDeviceObject(pNbtGlobConfig, pDeviceContext->IpAddress); + + if (status != STATUS_SUCCESS) + { + ASSERT(0); + } + } + + // + // events such as name refresh etc. have been scheduled to be executed + // later: cancel all those + // + CancelAllDelayedEvents( NULL ); + + // + // Tell IP not to use the handler anymore + // + IPRegisterAddrChangeHandler( IPNotification, FALSE); + + + ReleaseNbtConfigMem(); + + + // + // we don't provide any service, so don't pass our name + // + CTEUnload( (char *)NULL ); +} + + + +/******************************************************************* + + NAME: ReleaseNbtConfigMem + + SYNOPSIS: This is where we release all the memory that was allocated + at init/run time via ifs mgr. + We also stop the various timers. + + HISTORY: + Koti Oct 14, 94 + +********************************************************************/ + +VOID ReleaseNbtConfigMem( VOID ) +{ + + PLIST_ENTRY pHead, pEntry; + tTIMERQENTRY *pTimerEntry; + tDGRAM_SEND_TRACKING *pTrack; + tADDRESSELE *pAddress; + tCLIENTELE *pClientEle; + tCONNECTELE *pConnEle; + tLOWERCONNECTION *pLowerConn; + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq; + int i; + + + KdPrint(("ReleaseNbtConfigMem entered\r\n")); + + CTEPagedCode(); + + + // + // stop the timer that used to look for timed-out ncb's + // + StopTimeoutTimer(); + + // + // if any other timers are active, stop them + // + if (!IsListEmpty(&TimerQ.ActiveHead)) + { + pHead = &TimerQ.ActiveHead; + pEntry = pHead->Flink; + while( pEntry != pHead ) + { + pTimerEntry = CONTAINING_RECORD(pEntry,tTIMERQENTRY,Linkage); + pEntry = pEntry->Flink; + CTESpinLock(&NbtConfig.JointLock,OldIrq); + StopTimer(pTimerEntry,NULL,NULL); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + + // + // free all the timers on the free list + // + while (!IsListEmpty(&TimerQ.FreeHead)) + { + pEntry = RemoveHeadList(&TimerQ.FreeHead); + pTimerEntry = CONTAINING_RECORD(pEntry,tTIMERQENTRY,Linkage); + CTEMemFree(pTimerEntry); + } + + + // free the various buffers + + if ( pFileBuff ) + CTEMemFree( pFileBuff ); + + if ( NbtConfig.pHosts ) + CTEMemFree( NbtConfig.pHosts ); + + if (NbtConfig.pScope) + CTEMemFree( NbtConfig.pScope ); + + if (NbtConfig.pLmHosts) + CTEMemFree(NbtConfig.pLmHosts); + + if (NbtConfig.pDomainName) + CTEMemFree(NbtConfig.pDomainName); + + if (NbtConfig.pDNSDomains) + CTEMemFree(NbtConfig.pDNSDomains); + + + // + // NbtDereferenceAddress might have freed the addresses. But if + // ReleaseNameOnNet returned pending we wouldn't have freed the addressele + // yet, waiting for NameReleaseDone to be called. Well, we are about to + // be unloaded so free such instances now! + // + while (!IsListEmpty(&NbtConfig.AddressHead)) + { + pEntry = RemoveHeadList(&NbtConfig.AddressHead); + pAddress = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + while (!IsListEmpty(&pAddress->ClientHead)) + { + pEntry = RemoveHeadList(&pAddress->ClientHead); + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + while (!IsListEmpty(&pClientEle->ConnectActive)) + { + pEntry = RemoveHeadList(&pClientEle->ConnectActive); + pConnEle = CONTAINING_RECORD(pEntry,tCONNECTELE,Linkage); + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + CTEMemFree(pLowerConn); + CTEMemFree(pConnEle); + } + CTEMemFree(pClientEle); + } + CTEMemFree( pAddress ); + } + + + // free the DomainList + while (!IsListEmpty(&DomainNames.DomainList)) + { + pEntry = RemoveHeadList(&DomainNames.DomainList); + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + if (pNameAddr->pIpList) + CTEMemFree(pNameAddr->pIpList); + CTEMemFree(pNameAddr); + } + + + // free pLocalHashTbl + if (NbtConfig.pLocalHashTbl) + { + // + // we should have freed all the entries by now. It's possible though + // that we sent a name release which returned pending, so we are + // still hanging on to the nameaddr. If there is any such instance, + // free it now (when else? we are about to be unloaded now!) + // + for(i=0;i<NbtConfig.pLocalHashTbl->lNumBuckets;i++) + { + while (!IsListEmpty(&(NbtConfig.pLocalHashTbl->Bucket[i]))) + { + pEntry = RemoveHeadList(&(NbtConfig.pLocalHashTbl->Bucket[i])); + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + CTEMemFree(pNameAddr); + } + } + CTEMemFree( NbtConfig.pLocalHashTbl ); + } + + + // free pRemoteHashTbl + if (NbtConfig.pRemoteHashTbl) + { + for(i=0;i<NbtConfig.pRemoteHashTbl->lNumBuckets;i++) + { + while (!IsListEmpty(&(NbtConfig.pRemoteHashTbl->Bucket[i]))) + { + pEntry = RemoveHeadList(&(NbtConfig.pRemoteHashTbl->Bucket[i])); + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + CTEMemFree(pNameAddr); + } + } + CTEMemFree( NbtConfig.pRemoteHashTbl ); + } + + + // free up the DgramTrackerFreeQ + while (!IsListEmpty(&NbtConfig.DgramTrackerFreeQ)) + { + pEntry = RemoveHeadList(&NbtConfig.DgramTrackerFreeQ); + pTrack = CONTAINING_RECORD(pEntry,tDGRAM_SEND_TRACKING,Linkage); + CTEMemFree( pTrack ); + } + + // + // shouldn't see any pending name queries at this point, but just in case! + // + while (!IsListEmpty(&NbtConfig.PendingNameQueries)) + { + pEntry = RemoveHeadList(&NbtConfig.PendingNameQueries); + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + CTEMemFree( pNameAddr ); + } + + + // free the SessionBufferFreeList list (allocated, not used) + while( !IsListEmpty(&NbtConfig.SessionBufferFreeList) ) + { + pEntry = RemoveHeadList(&NbtConfig.SessionBufferFreeList); + CTEMemFree( pEntry ); + } + + + // free SendContextFreeList (allocated, not used) + while( !IsListEmpty(&NbtConfig.SendContextFreeList) ) + { + pEntry = RemoveHeadList(&NbtConfig.SendContextFreeList); + CTEMemFree( pEntry ); + } + + + // free RcvContextFreeList (allocated, not used) + while( !IsListEmpty(&NbtConfig.RcvContextFreeList) ) + { + pEntry = RemoveHeadList(&NbtConfig.RcvContextFreeList); + CTEMemFree( pEntry ); + } + +#ifdef DBG + if( !IsListEmpty(&DbgMemList) ) + { + KdPrint(("ReleaseNbtConfigMem: memory leak!\r\n")); + if (DbgLeakCheck) + { + ASSERT(0); + } + } +#endif + +} + +#endif // CHICAGO diff --git a/private/ntos/nbt/vxd/chicasm.asm b/private/ntos/nbt/vxd/chicasm.asm new file mode 100644 index 000000000..2536b30f7 --- /dev/null +++ b/private/ntos/nbt/vxd/chicasm.asm @@ -0,0 +1,122 @@ +;*****************************************************************; +;** Copyright(c) Microsoft Corp., 1988-1993 **; +;*****************************************************************; +;:ts=8 + TITLE CHICASM.ASM - Chicago (PNP) Specific vnbt routines +.XLIST +;*** VNBT -- NetBios over TCP/IP VxD +; +; + .386p + include vmm.inc + include dosmgr.inc + include netvxd.inc + include vdhcp.inc + include debug.inc + include vtdi.inc + include vip.inc + + include vnbt.inc + include vnetbios.inc +.LIST + +IFDEF CHICAGO + +EXTRN _GetDhcpOption:NEAR +EXTRN NCB_Handler:NEAR + + +VxD_CODE_SEG + +;**************************************************************************** +;** _RegisterLana2 +; +; Registers the requested lana with the VNetbios driver. +; +; Entry: [ESP+4] - PNP Device context +; [ESP+8] - Lana number to register +; +; Exit: EAX will return the lana registered or 0xff if the lana couldn't +; be registered. +; +; Uses: +; +BeginProc _RegisterLana2 + + VxDcall VNETBIOS_Get_Version + jnc Do_Register + mov eax, 0ffh ; yukk! vnetbios is not loaded! + jmp Register_Fail + +Do_Register: + mov ecx, [esp+4] ; PNP device context + mov eax, [esp+8] ; Get the request lana to register + + push ebx + push edx + + mov ebx, 1 ; Take over RM lana + mov edx, NCB_Handler + VxDcall VNETBIOS_Register2 ; Carry set on failure + jnc RegLana10 + mov eax, 0ffh ; Failed + +RegLana10: + pop edx + pop ebx +Register_Fail: + ret + +EndProc _RegisterLana2 + + +;**************************************************************************** +;** _DeregisterLana +; +; Deregisters the requested lana with the VNetbios driver. +; +; Entry: [ESP+4] - Lana number to deregister +; +; Uses: +; +BeginProc _DeregisterLana + + mov eax, [esp+4] ; Lana to deregister + + VxDcall VNETBIOS_Deregister + + ret +EndProc _DeregisterLana + +;**************************************************************************** +;** _IPRegisterAddrChangeHandler +; +; Registers a handler with IP to handle binding and unbinding +; +; Entry: [ESP+4] - Pointer to handler +; [ESP+8] - TRUE to set the handler, FALSE to remove the handler +; +; Exit: EAX will contain TRUE if successful, FALSE other wise +; +; Uses: +; +BeginProc _IPRegisterAddrChangeHandler + + mov eax, [esp+8] ; bool + push eax + mov eax, [esp+8] ; Handler (yes, it should be esp+8) + push eax + + VxDcall VIP_Register_Addr_Change; Carry set on failure + + add esp, 8 + ret + +EndProc _IPRegisterAddrChangeHandler + +VxD_CODE_ENDS + +ENDIF ;CHICAGO + +END + diff --git a/private/ntos/nbt/vxd/cinit.c b/private/ntos/nbt/vxd/cinit.c new file mode 100644 index 000000000..c8e9c4efd --- /dev/null +++ b/private/ntos/nbt/vxd/cinit.c @@ -0,0 +1,930 @@ +/**********************************************************************/ +/** Microsoft Windows **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + + Init.c + + Contains VxD initialization code + + + FILE HISTORY: + Johnl 24-Mar-1993 Created + +*/ + +#include <nbtprocs.h> +#include <tdiinfo.h> +#include <ipinfo.h> +#include <dhcpinfo.h> +#include <nbtinfo.h> +#include <hosts.h> + +int Init( void ) ; +int RegisterLana( int Lana ) ; + +NTSTATUS +NbtReadRegistry( + OUT tNBTCONFIG *pConfig + ) ; + +extern char DNSSectionName[]; // Section where we find DNS domain name + +VOID GetDNSInfo( VOID ); + +ULONG GetDhcpOption( PUCHAR ValueName, ULONG DefaultValue ); + +VOID ParseDomainNames( + PUCHAR *ppDomainName, + PUCHAR *ppDNSDomains + ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(INIT, Init) +#pragma CTEMakePageable(PAGE, NbtReadRegistry) +#pragma CTEMakePageable(PAGE, GetDNSInfo) +#pragma CTEMakePageable(PAGE, CreateDeviceObject) +#pragma CTEMakePageable(PAGE, GetDhcpOption) +#endif +//******************* Pageable Routine Declarations **************** + +// +// Initialized in VNBT_Device_Init with the protocol(s) this driver sits +// on. Note that we currently only support one. This should *not* be in +// the initialization data segments. +// +TDIDispatchTable * TdiDispatch ; +UCHAR LanaBase ; +BOOL fInInit = TRUE ; + +// +// Used in conjunction with the CHECK_INT_TABLE macro +// +#ifdef DEBUG + BYTE abVecTbl[256] ; + DWORD DebugFlags = DBGFLAG_ALL | DBGFLAG_KDPRINTS ; + char DBOut[4096] ; + int iCurPos = 0 ; + +void NbtDebugOut( char * str ) +{ + if ( DebugFlags & (DBGFLAG_AUX_OUTPUT | DBGFLAG_ERROR) ) + CTEPrint( str ) ; + + iCurPos += strlen( str ) + 1 ; + + if ( iCurPos >= sizeof(DBOut) ) + iCurPos = 0; +} + +#endif // DEBUG + +#pragma BEGIN_INIT + +// +// While reading initialization parameters, we may need to go to +// the DHCP driver. This communicates to the init routine which device +// we are currently interested in. +// 0xfffffff means get the requested option for any IP address. +// +// MUST BE IN NETWORK ORDER!!! +// +ULONG CurrentIP = 0xffffffff ; + +/******************************************************************* + + NAME: Init + + SYNOPSIS: Performs all driver initialization + + RETURNS: TRUE if initialization successful, FALSE otherwise + + NOTES: + + HISTORY: + Johnl 24-Mar-1993 Created + +********************************************************************/ + +int Init( void ) +{ + NTSTATUS status ; + int i ; + ULONG ulTmp ; + int Retval; + + + Retval = FALSE; + + if ( CTEInitialize() ) + { + DbgPrint("Init: CTEInitialize succeeded\n\r") ; + } + else + goto fail_init; + + INIT_NULL_PTR_CHECK() ; + +#ifdef DEBUG + InitializeListHead(&DbgMemList); + DbgLeakCheck = 0; +#endif + +#ifdef CHICAGO + // + // Tell TDI who to call if someone underneath unloads + // + CTESetUnloadNotifyProc( (CTENotifyRtn)VxdUnload ); + + // + // prepare to read a bunch of parms from the registry + // + VxdOpenNdis(); +#endif + + CTERefillMem() ; + CTEZeroMemory( pNbtGlobConfig, sizeof(*pNbtGlobConfig)); + + status = NbtReadRegistry( pNbtGlobConfig ) ; + if ( !NT_SUCCESS( status ) ) + { + DbgPrint("Init: NbtReadRegistry failed\n\r") ; + goto fail_init; + } + + InitializeListHead(&pNbtGlobConfig->DelayedEvents); + + InitializeListHead(&pNbtGlobConfig->BlockingNcbs); + + status = InitNotOs() ; + if ( !NT_SUCCESS( status ) ) + { + DbgPrint("Init: InitNotOs failed\n\r") ; + goto fail_init; + } + + status = InitTimersNotOs() ; + if ( !NT_SUCCESS( status ) ) + { + DbgPrint("Init: InitTimersNotOs failed\n\r") ; + StopInitTimers() ; + goto fail_init; + } + +#ifdef CHICAGO + + // + // if name server and/or dns server are defined in registry, read them now + // + SaveNameDnsServerAddrs(); + + // + // Register an IP notification routine when new adapters are added or + // DHCP brings up an address + // + + if ( !IPRegisterAddrChangeHandler( IPNotification, TRUE)) + { + DbgPrint("Init: Failed to register with IP driver\r\n") ; + StopInitTimers() ; + goto fail_init; + } +#else + // + // Find all the active Lanas + // + + if ( !GetActiveLanasFromIP() ) + { + DbgPrint("Init: Failed to get addresses from IP driver\r\n") ; + StopInitTimers() ; + goto fail_init; + } + +#endif + + // + // find out where hosts file is, what's the domain name etc. + // + GetDNSInfo(); + + // + // Get the NCB timeout timer going + // + if ( !CheckForTimedoutNCBs( NULL, NULL) ) + { + DbgPrint("Init: CheckForTimedoutNCBs failed\n\r") ; + StopInitTimers() ; + goto fail_init; + } + + fInInit = FALSE ; + CTERefillMem() ; + Retval = TRUE ; + + +fail_init: + +#ifdef CHICAGO + VxdCloseNdis(); +#endif + + return( Retval ); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtReadRegistry( + OUT tNBTCONFIG *pConfig + ) +/*++ + +Routine Description: + + This routine is called to get information from the registry, + starting at RegistryPath to get the parameters. + +Arguments: + + pNbtConfig - ptr to global configuration strucuture for NBT + +Return Value: + + NTSTATUS - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS ; + int i; + + CTEPagedCode(); + + // + // Initialize the Configuration data structure + // + CTEZeroMemory(pConfig,sizeof(tNBTCONFIG)); + + ReadParameters( pConfig, NULL ); + + // + // Allocate necessary memory for lmhosts support if a lmhosts file + // was specified (was read from .ini file in ReadParameters) + // + if ( pConfig->pLmHosts ) + { + if ( !VxdInitLmHostsSupport( pConfig->pLmHosts, + 260 /*strlen(pConfig->pLmHosts)+1*/ )) + { + return STATUS_INSUFFICIENT_RESOURCES ; + } + + pConfig->EnableLmHosts = TRUE ; + } + else + { + pConfig->EnableLmHosts = FALSE ; + } + + // keep the size around for allocating memory, so that when we run over + // OSI, only this value should change (in theory at least) + pConfig->SizeTransportAddress = sizeof(TDI_ADDRESS_IP); + + // fill in the node type value that is put into all name service Pdus + // that go out identifying this node type + switch (NodeType) + { + case BNODE: + pConfig->PduNodeType = 0; + break; + case PNODE: + pConfig->PduNodeType = 1 << 13; + break; + case MNODE: + pConfig->PduNodeType = 1 << 14; + break; + case MSNODE: + pConfig->PduNodeType = 3 << 13; + break; + + } + + LanaBase = (UCHAR) CTEReadSingleIntParameter( NULL, + VXD_LANABASE_NAME, + VXD_DEF_LANABASE, + 0 ) ; + CTEZeroMemory( LanaTable, NBT_MAX_LANAS * sizeof( LANA_ENTRY )) ; + + return Status; +} + +/******************************************************************* + + NAME: GetDNSInfo + + SYNOPSIS: Gets path to windows dir, then appends hosts to it + + RETURNS: Nothing + + NOTES: If something goes wrong, path is set to NULL + + HISTORY: + Koti 13-July-1994 Created + +********************************************************************/ + +VOID GetDNSInfo( VOID ) +{ + + PUCHAR pszWinPath; + PUCHAR pszHosts="hosts"; + PUCHAR pszHostsPath=NULL; +#ifdef CHICAGO + PUCHAR pszParmName="Domain"; + PUCHAR pszParm2Name = "SearchList"; +#else + PUCHAR pszParmName="DomainName"; + PUCHAR pszParm2Name = "DNSDomains"; +#endif + PUCHAR pszDomName; + PUCHAR pchTmp; + int len; + + + CTEPagedCode(); + + NbtConfig.pHosts = NULL; + + // + // Remember, pszWinPath has '\' at the end: (i.e. Get_Config_Directory + // returns pointer to "c:\windows\" ) + // + pszWinPath = VxdWindowsPath(); + + // + // doc implies Get_Config_Directory can't fail! But we are paranoid... + // + + if (pszWinPath == NULL) + { + pszHostsPath = NULL; + return; + } + + len = strlen(pszWinPath) + strlen(pszHosts) + 1; + + // + // allocate memory to hold "c:\windows\hosts" or whatever + // + pszHostsPath = CTEAllocInitMem( len ); + if (pszHostsPath == NULL) + return; + + strcpy(pszHostsPath, pszWinPath); + strcat(pszHostsPath, pszHosts); + + NbtConfig.pHosts = pszHostsPath; + + NbtConfig.pDomainName = NULL; +// +// chicago gets the string from the registry, snowball from system.ini +// + NbtConfig.pDNSDomains = NULL; + +#ifdef CHICAGO + if ( !CTEReadIniString( NULL, pszParmName, &pchTmp ) ) + { + NbtConfig.pDomainName = pchTmp; + } + + if ( !CTEReadIniString( NULL, pszParm2Name, &pchTmp ) ) + { + if (pchTmp[0] != '\0') + { + NbtConfig.pDNSDomains = pchTmp; + } + else + { + CTEMemFree(pchTmp); + } + } +#else + pchTmp = GetProfileString( pszParmName, NULL, DNSSectionName ); + if ( pchTmp != NULL ) + { + if ( pszDomName = CTEAllocInitMem( strlen( pchTmp ) + 1 ) ) + { + strcpy( pszDomName, pchTmp ) ; + + NbtConfig.pDomainName = pszDomName; + } + } + + pchTmp = GetProfileString( pszParm2Name, NULL, DNSSectionName ); + if ( pchTmp != NULL && pchTmp[0] != '\0') + { + if ( pszDomName = CTEAllocInitMem( strlen( pchTmp ) + 1) ) + { + strcpy( pszDomName, pchTmp ) ; + + NbtConfig.pDNSDomains = pszDomName; + } + } +#endif + + ParseDomainNames( &NbtConfig.pDomainName, &NbtConfig.pDNSDomains); + + return; +} + +/******************************************************************* + + NAME: ParseDomainNames + + SYNOPSIS: Extracts heirarchical domain names from the primary DNS + domain name, and prepends any found to the domains list. + + ENTRY: ppDomainName - pointer to pointer to primary DNS domain name + ppDNSDomains - pointer to pointer to other DNS domain names + + EXIT: *ppDNSDomains updated with pointer to new DNS domains list + if any other heirarchical levels found in *ppDomainName. + + RETURNS: nothing + + NOTES: + + HISTORY: + EarleH 10-Jan-1996 Created + +********************************************************************/ + +VOID ParseDomainNames( + PUCHAR *ppDomainName, + PUCHAR *ppDNSDomains + ) +{ + PUCHAR pStr; + UINT iCount; + PUCHAR pDomainName = *ppDomainName, pDNSDomains = *ppDNSDomains, pNewDNSDomains; + + if ( pDomainName != NULL ) + { + for ( iCount = 0, pStr = pDomainName ; pStr[0] != '\0' ; pStr++ ) + { + if ( pStr[0] == '.' ) + { + iCount += strlen ( pStr ); + } + } + if ( pDNSDomains != NULL ) + { + iCount += strlen ( pDNSDomains ); + if ( iCount ) + { + iCount++; // for the separator + } + } + if (iCount++) // ++ for the terminator + { + pNewDNSDomains = CTEAllocInitMem( iCount ); + if ( pNewDNSDomains != NULL ) + { + pNewDNSDomains[0] = '\0'; + for ( pStr = pDomainName ; pStr[0] != '\0' ; pStr++ ) + { + if ( pStr[0] == '.' ) + { + pStr++; + if ( pNewDNSDomains[0] != '\0' ) + { + strcat ( pNewDNSDomains, "," ); + } + strcat ( pNewDNSDomains, pStr ); + } + } + if ( pDNSDomains != NULL ) + { + strcat ( pNewDNSDomains, "," ); + strcat ( pNewDNSDomains, pDNSDomains ); + CTEMemFree( pDNSDomains ); + } + if ( pNewDNSDomains[0] != '\0' ) + { + *ppDNSDomains = pNewDNSDomains; + } + else + { + *ppDNSDomains = NULL; + } + } + } + } +} + +#pragma END_INIT + +#ifndef CHICAGO +#pragma BEGIN_INIT +#endif +/******************************************************************* + + NAME: CreateDeviceObject + + SYNOPSIS: Initializes the device list of the global configuration + structure + + ENTRY: pConfig - Pointer to global config structure + IpAddr - IP Address for this adapter + IpMask - IP Mask for this adapter + IpNameServer - IP Address of the name server for this adapter + IpBackupServer - IP Address of the backup name server for + this adapter + IpDnsServer - IP Address of the dns server for this adapter + IpDnsBackupServer - IP Address of the backup dns server + MacAddr - hardware address of the adapter for this IP addr + IpIndex - Index of the IP Address in the IP Driver's address + table (used for setting address by DHCP) + + EXIT: The device list in pConfig will be fully initialized + + RETURNS: STATUS_SUCCESS if successful, error otherwise + + NOTES: + + HISTORY: + Johnl 14-Apr-1993 Created + +********************************************************************/ + +NTSTATUS CreateDeviceObject( + IN tNBTCONFIG *pConfig, + IN ULONG IpAddr, + IN ULONG IpMask, + IN ULONG IpNameServer, + IN ULONG IpBackupServer, + IN ULONG IpDnsServer, + IN ULONG IpDnsBackupServer, + IN UCHAR MacAddr[], + IN UCHAR IpIndex + ) +{ + NTSTATUS status; + tDEVICECONTEXT * pDeviceContext, *pDevtmp; + ULONG ulTmp ; + NCB * pNCB ; + DHCPNotify dn ; + PLIST_ENTRY pEntry; + ULONG Adapter; + ULONG PreviousNodeType; + + CTEPagedCode(); + + pDeviceContext = CTEAllocInitMem(sizeof( tDEVICECONTEXT )) ; + if ( !pDeviceContext ) + return STATUS_INSUFFICIENT_RESOURCES ; + + // + // zero out the data structure + // + CTEZeroMemory( pDeviceContext, sizeof(tDEVICECONTEXT) ); + + // put a verifier value into the structure so that we can check that + // we are operating on the right data when the OS passes a device context + // to NBT + pDeviceContext->Verify = NBT_VERIFY_DEVCONTEXT; + + // + // we aren't up yet: don't want ncb's coming in before we are ready! + // + pDeviceContext->fDeviceUp = FALSE; + + // setup the spin lock); + CTEInitLock(&pDeviceContext->SpinLock); + pDeviceContext->LockNumber = DEVICE_LOCK; + pDeviceContext->lNameServerAddress = IpNameServer ; + pDeviceContext->lBackupServer = IpBackupServer ; + pDeviceContext->lDnsServerAddress = IpDnsServer ; + pDeviceContext->lDnsBackupServer = IpDnsBackupServer ; + + // copy the mac addresss + CTEMemCopy(&pDeviceContext->MacAddress.Address[0], MacAddr, 6); + + // + // if the node type is set to Bnode by default then switch to Hnode if + // there are any WINS servers configured. + // + PreviousNodeType = NodeType; + + if ((NodeType & DEFAULT_NODE_TYPE) && + (IpNameServer || IpBackupServer)) + { + NodeType = MSNODE; + if (PreviousNodeType & PROXY) + NodeType |= PROXY; + } + + // + // start the refresh timer (if we had already started it, this function + // just returns success) + // + status = StartRefreshTimer(); + + if ( !NT_SUCCESS( status ) ) + { + CTEFreeMem( pDeviceContext ) ; + return( status ) ; + } + + + // initialize the pDeviceContext data structure. There is one of + // these data structured tied to each "device" that NBT exports + // to higher layers (i.e. one for each network adapter that it + // binds to. + // The initialization sets the forward link equal to the back link equal + // to the list head + InitializeListHead(&pDeviceContext->UpConnectionInUse); + InitializeListHead(&pDeviceContext->LowerConnection); + InitializeListHead(&pDeviceContext->LowerConnFreeHead); + InitializeListHead(&pDeviceContext->RcvAnyFromAnyHead); + InitializeListHead(&pDeviceContext->RcvDGAnyFromAnyHead); + InitializeListHead(&pDeviceContext->PartialRcvHead) ; + + InitializeListHead(&pDeviceContext->DelayedEvents); + + // + // Pick an adapter number that hasn't been used yet + // + Adapter = 1; + for ( pEntry = pConfig->DeviceContexts.Flink; + pEntry != &pConfig->DeviceContexts; + pEntry = pEntry->Flink ) + { + pDevtmp = CONTAINING_RECORD( pEntry, tDEVICECONTEXT, Linkage ); + + if ( !(pDevtmp->AdapterNumber & Adapter) ) + break; + + Adapter <<= 1; + } + + pDeviceContext->AdapterNumber = Adapter ; + pDeviceContext->IPIndex = IpIndex ; + NbtConfig.AdapterCount++ ; + if ( NbtConfig.AdapterCount > 1 ) + { + NbtConfig.MultiHomed = TRUE ; + } + + // + // Allocate our name table and session table watching for both a + // minimum and a maximum size. + // + + pDeviceContext->cMaxNames = (UCHAR) min( pConfig->lRegistryMaxNames, MAX_NCB_NUMS ) ; + + pDeviceContext->cMaxSessions = (UCHAR) min( pConfig->lRegistryMaxSessions, MAX_NCB_NUMS ) ; + + // + // Add one to the table size for the zeroth element (used for permanent + // name in the name table). The user accessible table goes from 1 to n + // + + if ( !(pDeviceContext->pNameTable = (tCLIENTELE**) + CTEAllocInitMem((USHORT)((pDeviceContext->cMaxNames+1) * sizeof(tADDRESSELE*)))) || + !(pDeviceContext->pSessionTable = (tCONNECTELE**) + CTEAllocInitMem((pDeviceContext->cMaxSessions+1) * sizeof(tCONNECTELE*)))) + { + return STATUS_INSUFFICIENT_RESOURCES ; + } + + CTEZeroMemory( &pDeviceContext->pNameTable[0], + (pDeviceContext->cMaxNames+1)*sizeof(tCLIENTELE*)) ; + CTEZeroMemory( &pDeviceContext->pSessionTable[0], + (pDeviceContext->cMaxSessions+1)*sizeof(tCONNECTELE*) ) ; + pDeviceContext->iNcbNum = 1 ; + pDeviceContext->iLSNum = 1 ; + + // add this new device context on to the List in the configuration + // data structure + InsertTailList(&pConfig->DeviceContexts,&pDeviceContext->Linkage); + + // + // IpAddr can be 0 only in wfw case (when dhcp hasn't yet obtained one) + // (in case of chicago, we will never come this far if ipaddr is 0) + // + if (!IpAddr) + { + pDeviceContext->IpAddress = 0; + goto Skip_tdiaddr_init; + } + + // + // open the required address objects with the underlying transport provider + // + status = NbtCreateAddressObjects( + IpAddr, + IpMask, + pDeviceContext); + if (!NT_SUCCESS(status)) + { + KdPrint(("Failed to create the Address Object, status=%lC\n",status)); + return(status); + } + + // this call must converse with the transport underneath to create + // connections and associate them with the session address object + status = NbtInitConnQ( + &pDeviceContext->LowerConnFreeHead, + sizeof(tLOWERCONNECTION), + NBT_NUM_INITIAL_CONNECTIONS, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + CDbgPrint( DBGFLAG_ERROR, + ("CreateDeviceObject: NbtInitConnQ Failed!")) ; + + return(status); + } + + // + // Add the permanent name for this adapter + // + status = NbtAddPermanentName( pDeviceContext ) ; + if ( !NT_SUCCESS( status )) + { + return status ; + } + +Skip_tdiaddr_init: + + // + // ok, we are ready to function! (we are setting this to TRUE even if + // there is no ipaddr yet (in case of wfw only) so that rdr,srv etc. can + // add names without error + // + pDeviceContext->fDeviceUp = TRUE; + +#ifndef CHICAGO + // + // Set up a DHCP notification for this device in case the IP address + // changes + // + dn.dn_pfnNotifyRoutine = AddrChngNotification ; + dn.dn_pContext = pDeviceContext ; + + status = DhcpSetInfo( DHCP_SET_NOTIFY_HANDLER, + pDeviceContext->IPIndex, + &dn, + sizeof( dn )) ; + if ( status ) + { + ASSERT(0); + CDbgPrint( DBGFLAG_ERROR, + ("CreateDeviceObject: Warning - Setting Dhcp notification handler failed")) ; + } +#endif //!CHICAGO + + return(STATUS_SUCCESS); +} + +/******************************************************************* + + NAME: GetDhcpOption + + SYNOPSIS: Checks to see if the passed .ini parameter is a potential + DHCP option. If it is, it calls DHCP to get the option. + + This routine is called when retrieving parameters from + the .ini file if the parameter is not found. + + ENTRY: ValueName - String of .ini parameter name + DefaultValue - Value to return if not a DHCP option or + DHCP didn't have the option + + RETURNS: DHCP option value or DefaultValue if an error occurred. + If the requested parameter is a string option (such as + scopeid), then a pointer to an allocated string is + returned. + + NOTES: Name Server address is handled in GetNameServerAddress + + HISTORY: + Johnl 17-Dec-1993 Created + +********************************************************************/ + +#define OPTION_NETBIOS_SCOPE_OPTION 47 +#define OPTION_NETBIOS_NODE_TYPE 46 +#define OPTION_BROADCAST_ADDRESS 28 + +struct +{ + PUCHAR pchParamName ; + ULONG DhcpOptionID ; +} OptionMapping[] = + { { WS_NODE_TYPE, OPTION_NETBIOS_NODE_TYPE }, + { NBT_SCOPEID, OPTION_NETBIOS_SCOPE_OPTION }, + { WS_ALLONES_BCAST, OPTION_BROADCAST_ADDRESS } + } ; +#define NUM_OPTIONS (sizeof(OptionMapping)/sizeof(OptionMapping[0])) + + +ULONG GetDhcpOption( PUCHAR ValueName, ULONG DefaultValue ) +{ + int i ; + ULONG Val ; + TDI_STATUS tdistatus ; + ULONG Size ; + INT OptionId ; + PUCHAR pStrVal ; + + CTEPagedCode(); + + // + // Is this parameter a DHCP option? + // + for ( i = 0 ; i < NUM_OPTIONS ; i++ ) + { + if ( !strcmp( OptionMapping[i].pchParamName, ValueName )) + goto FoundOption ; + } + + return DefaultValue ; + +FoundOption: + + switch ( OptionId = OptionMapping[i].DhcpOptionID ) + { + case OPTION_NETBIOS_SCOPE_OPTION: // String options go here + + // + // Get the size of the string resource, then get the option + // + + Size = MAX_SCOPE_LENGTH+1 ; + pStrVal = CTEAllocInitMem( Size ); + if (pStrVal == NULL) + { + DbgPrint("GetDhcpOption: failed to allocate memory") ; + return 0 ; + } + + tdistatus = DhcpQueryOption( CurrentIP, + OptionId, + pStrVal, + &Size ) ; + + if ( tdistatus == TDI_SUCCESS ) + { + DbgPrint("GetDhcpOption: Successfully retrieved option ID ") ; + DbgPrintNum( OptionId ) ; DbgPrint("\r\n") ; + return (ULONG) pStrVal ; + } + else + { + DbgPrint("GetDhcpOption: returned error = 0x ") ; + DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ; + CTEMemFree( pStrVal ) ; + return 0 ; + } + + default: // ULONG options go here + Size = sizeof( Val ) ; + tdistatus = DhcpQueryOption( CurrentIP, + OptionId, + &Val, + &Size ) ; + break ; + } + + switch ( tdistatus ) + { + case TDI_SUCCESS: + case TDI_BUFFER_OVERFLOW: // May be more then one, only take the 1st + DbgPrint("GetDhcpOption: Successfully retrieved option ID ") ; + DbgPrintNum( OptionId ) ; DbgPrint("\r\n") ; + return Val ; + + case TDI_INVALID_PARAMETER: // Option not found + DbgPrint("GetDhcpOption: Failed to retrieve option ID ") ; + DbgPrintNum( OptionId ) ; DbgPrint("\r\n") ; + return DefaultValue ; + + default: + ASSERT( FALSE ) ; + break ; + } + + return DefaultValue ; +} + +#ifndef CHICAGO +#pragma END_INIT +#endif + + diff --git a/private/ntos/nbt/vxd/client.asm b/private/ntos/nbt/vxd/client.asm new file mode 100644 index 000000000..0c44bbf79 --- /dev/null +++ b/private/ntos/nbt/vxd/client.asm @@ -0,0 +1,427 @@ + page ,132 + title client16.asm - 16-bit client support routines + + +;********************************************************************** +;** Microsoft Windows ** +;** Copyright(c) Microsoft Corp., 1993-1994 ** +;********************************************************************** +; +; +; client16.asm +; +; VXDLIB routines for dealing with 16-bit clients. +; +; The following functions are exported by this module: +; +; VxdMapSegmentOffsetToFlat +; +; +; FILE HISTORY: +; Koti 14-Jun-1994 Stole from KeithMo +; +; + +.386p +include vmm.inc +include shell.inc +include vwin32.inc +include netvxd.inc +include debug.inc + + +;;; +;;; Flag to _LinPage[Un]Lock. +;;; + +ifdef CHICAGO +VxdLinPageFlag equ PAGEMAPGLOBAL +else ; !CHICAGO +VxdLinPageFlag equ 0 +endif ; CHICAGO + + +;*** +;*** Locked code segment. +;*** + +VXD_LOCKED_CODE_SEG + +;;; +;;; Public functions. +;;; + +;******************************************************************* +; +; NAME: VxdMapSegmentOffsetToFlat +; +; SYNOPSIS: Maps a segment/offset pair to the corresponding flat +; pointer. +; +; ENTRY: VirtualHandle - VM handle. +; +; UserSegment - Segment value. +; +; UserOffset - Offset value +; +; RETURNS: LPVOID - The flat pointer, -1 if unsuccessful. +; +; NOTES: This routine was more-or-less stolen from the Map_Flat +; source in dos386\vmm\vmmutil.asm. +; +; HISTORY: +; KeithMo 27-Jan-1994 Created. +; +;******************************************************************** +BeginProc _VxdMapSegmentOffsetToFlat, PUBLIC, CCALL, ESP + +ArgVar VirtualHandle, DWORD +ArgVar UserSegment, DWORD +ArgVar UserOffset, DWORD + + EnterProc + SaveReg <ebx, ecx, edx, esi> + +;;; +;;; Capture the parameters. +;;; + + mov ebx, VirtualHandle ; (EBX) = VM handle + movzx eax, word ptr UserSegment ; (EAX) = segment + movzx esi, word ptr UserOffset ; (ESI) = offset + +;;; +;;; Short-circuit for NULL pointer. This is OK. +;;; + + or eax, eax + jz vmsotf_Exit + +;;; +;;; Determine if the current virtual machine is running in V86 +;;; mode or protected mode. +;;; + + test [ebx.CB_VM_Status], VMStat_PM_Exec + jz vmsotf_V86Mode + +;;; +;;; The target virtual machine is in protected mode. Map the +;;; selector to a flat pointer, then add the offset. +;;; + + VMMCall _SelectorMapFlat <ebx, eax, 0> + cmp eax, 0FFFFFFFFh + je vmsotf_Exit + + add eax, esi + +;;; +;;; If the pointer is within the first 1Meg+64K, add in the +;;; high-linear offset. +;;; + + cmp eax, 00110000h + jae short vmsotf_Exit + +vmsotf_AddHighLinear: + + add eax, [ebx.CB_High_Linear] + +;;; +;;; Cleanup stack & return. +;;; + +vmsotf_Exit: + + RestoreReg <esi, edx, ecx, ebx> + LeaveProc + Return + +;;; +;;; The target virtual machine is in V86 mode. Map the segment/offset +;;; pair to a linear address. +;;; + +vmsotf_V86Mode: + + shl eax, 4 + add eax, esi + jmp vmsotf_AddHighLinear + +EndProc _VxdMapSegmentOffsetToFlat + + +;******************************************************************* +; +; NAME: VxdLockBuffer +; +; SYNOPSIS: Locks a user-mode buffer so it may be safely accessed +; from ring 0. +; +; ENTRY: Buffer - Starting virtual address of user-mode buffer. +; +; BufferLength - Length (in BYTEs) of user-mode buffer. +; +; RETURN: LPVOID - Global locked address if successful, +; NULL if not. +; +; HISTORY: +; KeithMo 10-Nov-1993 Created. +; +;******************************************************************** +BeginProc _VxdLockBuffer, PUBLIC, CCALL, ESP + +ArgVar Buffer, DWORD +ArgVar BufferLength, DWORD + + EnterProc + SaveReg <ebx, edi, esi> + +;;; +;;; Grab parameters from stack. +;;; + + mov eax, Buffer ; User-mode buffer address. + mov ebx, BufferLength ; Buffer length. + +;;; +;;; Short-circuit for NULL buffer or zero length. +;;; + + or eax, eax + jz lub_Exit + or ebx, ebx + jz lub_Exit + +;;; +;;; Calculate the starting page number & number of pages to lock. +;;; + + movzx ecx, ax + and ch, 0Fh ; ecx = offset within first page. + mov esi, ecx ; save it for later + add ebx, ecx + add ebx, 0FFFh + shr ebx, 12 ; ebx = number of pages to lock. + shr eax, 12 ; eax = starting page number. + +;;; +;;; Ask VMM to lock the buffer. +;;; + + VMMCall _LinPageLock, <eax, ebx, VxdLinPageFlag> + or eax, eax + jz lub_Failure + +ifdef CHICAGO + add eax, esi ; add offset into first page. +else ; !CHICAGO + mov eax, Buffer ; retrieve original address. +endif ; CHICAGO + +;;; +;;; Common exit path. Cleanup stack & return. +;;; + +lub_Exit: + + RestoreReg <esi, edi, ebx> + LeaveProc + Return + +;;; +;;; LinPageLock failure. +;;; + +lub_Failure: + + Trace_Out "VxdLockBuffer: _LinPageLock failed" + xor eax, eax + jmp lub_Exit + +EndProc _VxdLockBuffer + + +;******************************************************************* +; +; NAME: VxdUnlockBuffer +; +; SYNOPSIS: Unlocks a user-mode buffer locked with LockUserBuffer. +; +; ENTRY: Buffer - Starting virtual address of user-mode buffer. +; +; BufferLength - Length (in BYTEs) of user-mode buffer. +; +; RETURN: DWORD - !0 if successful, 0 if not. +; +; HISTORY: +; KeithMo 10-Nov-1993 Created. +; +;******************************************************************** +BeginProc _VxdUnlockBuffer, PUBLIC, CCALL, ESP + +ArgVar Buffer, DWORD +ArgVar BufferLength, DWORD + + EnterProc + SaveReg <ebx, edi, esi> + +;;; +;;; Grab parameters from stack. +;;; + + mov eax, Buffer ; User-mode buffer address. + mov ebx, BufferLength ; Buffer length. + +;;; +;;; Short-circuit for NULL buffer or zero length. +;;; + + or eax, eax + jz uub_Success + or ebx, ebx + jz uub_Success + +;;; +;;; Calculate the starting page number & number of pages to unlock. +;;; + + movzx ecx, ax + and ch, 0Fh ; ecx = offset within first page. + add ebx, ecx + add ebx, 0FFFh + shr ebx, 12 ; ebx = number of pages to lock. + shr eax, 12 ; eax = starting page number. + +;;; +;;; Ask VMM to unlock the buffer. +;;; + + VMMCall _LinPageUnLock, <eax, ebx, VxdLinPageFlag> + or eax, eax + jz uub_Failure + +uub_Success: + + mov eax, 1 ; !0 == success + +;;; +;;; Common exit path. Cleanup stack & return. +;;; + +uub_Exit: + + RestoreReg <esi, edi, ebx> + LeaveProc + Return + +;;; +;;; LinPageUnLock failure. +;;; + +uub_Failure: + + Trace_Out "VxdUnlockBuffer: _LinPageUnlock failed" + xor eax, eax + jmp uub_Exit + +EndProc _VxdUnlockBuffer + + +; +; BUGBUG: VxdValidateBuffer is currently not used. Unifdef it if needed +; + + +;******************************************************************* +; +; NAME: VxdValidateBuffer +; +; SYNOPSIS: Validates that all pages within the given buffer are +; valid. +; +; ENTRY: Buffer - Starting virtual address of user-mode buffer. +; +; BufferLength - Length (in BYTEs) of user-mode buffer. +; +; RETURN: BOOL - TRUE if all pages in buffer are valid, FALSE +; otherwise. +; +; HISTORY: +; KeithMo 20-May-1994 Created. +; +;******************************************************************** +BeginProc _VxdValidateBuffer, PUBLIC, CCALL, ESP + +ArgVar Buffer, DWORD +ArgVar BufferLength, DWORD + + EnterProc + SaveReg <ebx, edi, esi> + +;;; +;;; Grab parameters from stack. +;;; + + mov eax, Buffer ; User-mode buffer address. + mov ebx, BufferLength ; Buffer length. + +;;; +;;; Short-circuit for NULL buffer or zero length. +;;; + + or eax, eax + jz vub_Success + or ebx, ebx + jz vub_Success + +;;; +;;; Calculate the starting page number & number of pages to validate. +;;; + + movzx ecx, ax + and ch, 0Fh ; ecx = offset within first page. + add ebx, ecx + add ebx, 0FFFh + shr ebx, 12 ; ebx = number of pages to check. + shr eax, 12 ; eax = starting page number. + mov ecx, ebx ; save page count + +;;; +;;; Ask VMM to validate the buffer. +;;; + + VMMCall _PageCheckLinRange, <eax, ebx, 0> + cmp eax, ecx + jne vub_Failure + +vub_Success: + + mov eax, 1 ; TRUE == success. + +;;; +;;; Common exit path. Cleanup stack & return. +;;; + +vub_Exit: + + RestoreReg <esi, edi, ebx> + LeaveProc + Return + +;;; +;;; _PageCheckLinRange failure. +;;; + +vub_Failure: + + xor eax, eax + jmp vub_Exit + +EndProc _VxdValidateBuffer + +VXD_LOCKED_CODE_ENDS + + +END diff --git a/private/ntos/nbt/vxd/ctimer.c b/private/ntos/nbt/vxd/ctimer.c new file mode 100644 index 000000000..29cca3179 --- /dev/null +++ b/private/ntos/nbt/vxd/ctimer.c @@ -0,0 +1,237 @@ +/**********************************************************************/ +/** Microsoft Windows **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + + Timer.c + + This module checks for active sessions on all adapters for send/receive + NCBs that have timed out. + + A single timer is used for all adapters. + + If a send times out, then the session will be aborted. + + FILE HISTORY: + Johnl 23-Sep-1993 Created + +*/ + +#include <nbtprocs.h> +#include <ctemacro.h> + +CTETimer TimeoutTimer ; + +/******************************************************************* + + NAME: CheckForTimedoutNCBs + + SYNOPSIS: Traverses list of all send/receive NCBs checking for + any that have reached their timeout point every half + second. + + ENTRY: pEvent - Not used + pCont - Not used + + RETURNS: TRUE if the timer successfully started, FALSE otherwise + + NOTES: This is a self perpetuating function, each time it is called, + it schedules the timer again for a 1/2 second later to + call itself. + + To get it going, it should be called once during + initialization + + HISTORY: + Johnl 23-Sep-1993 Created + +********************************************************************/ + +BOOL CheckForTimedoutNCBs( CTEEvent *pCTEEvent, PVOID pCont ) +{ + tNAMEADDR * pNameAddr ; + tCLIENTELE * pClientEle ; + tCONNECTELE * pConnectEle ; + LIST_ENTRY * pEntry ; + LIST_ENTRY * pEntryClient ; + LIST_ENTRY * pEntryConn ; + LIST_ENTRY * pEntryRcv ; + + // + // Look for Receive NCBs first + // + for ( pEntry = NbtConfig.AddressHead.Flink ; + pEntry != &NbtConfig.AddressHead ; + pEntry = pEntry->Flink ) + { + PLIST_ENTRY pEntryClient ; + tADDRESSELE * pAddrEle = CONTAINING_RECORD( pEntry, + tADDRESSELE, + Linkage ) ; + ASSERT( pAddrEle->Verify == NBT_VERIFY_ADDRESS ) ; + + for ( pEntryClient = pAddrEle->ClientHead.Flink ; + pEntryClient != &pAddrEle->ClientHead ; + pEntryClient = pEntryClient->Flink ) + { + tCLIENTELE * pClientEle = CONTAINING_RECORD( pEntryClient, + tCLIENTELE, + Linkage ) ; + PLIST_ENTRY pEntryConn ; + ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT || + pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN ) ; + + for ( pEntryConn = pClientEle->ConnectActive.Flink ; + pEntryConn != &pClientEle->ConnectActive ; + pEntryConn = pEntryConn->Flink ) + { + PRCV_CONTEXT prcvCont ; + pConnectEle = CONTAINING_RECORD( pEntryConn, + tCONNECTELE, + Linkage ) ; + ASSERT( pConnectEle->Verify == NBT_VERIFY_CONNECTION || + pConnectEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ; + + if ( pConnectEle->RTO == NCB_INFINITE_TIME_OUT || + pConnectEle->state != NBT_SESSION_UP || + IsListEmpty( &pConnectEle->RcvHead ) ) + { + continue ; + } + + // + // Note that we only check the first receive buffer because + // the timeout is for the next receive indication (and not + // how long this NCB has been waiting). + // + pEntryRcv = pConnectEle->RcvHead.Flink ; + prcvCont = CONTAINING_RECORD( pEntryRcv, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + if ( prcvCont->RTO == NCB_TIMED_OUT ) + { + RemoveEntryList( &prcvCont->ListEntry ) ; + CTEIoComplete( prcvCont->pNCB, STATUS_TIMEOUT, 0 ) ; + } + else + { + prcvCont->RTO-- ; + } + } // ConnectActive + } // Client + } // Address + + // + // Now look for Send NCB time outs. Only connections that specified + // a timeout will be put on this list. + // + for ( pEntry = NbtConfig.SendTimeoutHead.Flink ; + pEntry != &NbtConfig.SendTimeoutHead ; + ) + { + PSEND_CONTEXT pSendCont ; + + pSendCont = CONTAINING_RECORD( pEntry, SEND_CONTEXT, ListEntry ) ; + pEntry = pEntry->Flink ; // get the next one + + pSendCont->STO-- ; + if ( pSendCont->STO == NCB_TIMED_OUT ) + { + // + // Assumes pSendCont is stored in ncb_reserve field of NCB + // This will remove it from the timeout list also. + // + CTEIoComplete( CONTAINING_RECORD( pSendCont, NCB, ncb_reserve ), + STATUS_TIMEOUT, + 0 ) ; + // the above CTEIoComplete will trigger events from the transport + // which could modify the SendTimeout list before we reach this + // point. Best just to wait until things clear up. + break; + } + } + + // + // Restart the timer for a half second from now + // + CTEInitTimer( &TimeoutTimer ) ; + return !!CTEStartTimer( &TimeoutTimer, 500, CheckForTimedoutNCBs, NULL ) ; +} + + +/******************************************************************* + + NAME: StartRefreshTimer + + SYNOPSIS: Start the refresh timer + This function was necessary because of this scenario: + No node type was defined in registry, so we defaulted to + BNODE and didn't start refresh timer. + Now, while we are still coming up, dhcp tells us we are + MSNODE. Shouldn't we start the refresh timer? + That's why this function. + + HISTORY: + Koti 9-Jun-1994 Created + +********************************************************************/ + +NTSTATUS StartRefreshTimer( VOID ) +{ + + NTSTATUS status = STATUS_SUCCESS; + tTIMERQENTRY *pTimerEntry; + + // + // make sure it's not bnode, and that timer really needs to be started + // + if (!(NodeType & BNODE) && (!NbtConfig.pRefreshTimer)) + { + + // the initial refresh rate until we can contact the name server + NbtConfig.MinimumTtl = NBT_INITIAL_REFRESH_TTL; + NbtConfig.sTimeoutCount = 0; + + status = StartTimer( + NbtConfig.InitialRefreshTimeout, + NULL, // context value + NULL, // context2 value + RefreshTimeout, + NULL, + NULL, + 0, + &pTimerEntry); + + if ( !NT_SUCCESS( status ) ) + return status ; + + NbtConfig.pRefreshTimer = pTimerEntry; + } + + return(STATUS_SUCCESS); +} + + +#ifdef CHICAGO + +/******************************************************************* + + NAME: StopTimeoutTimer + + SYNOPSIS: Stops the timer that was set in CheckForTimedoutNCBs. + This is needed only for Chicago which can dynamically + unload vnbt. + + HISTORY: + Koti 23-May-1994 Created + +********************************************************************/ + +VOID StopTimeoutTimer( VOID ) +{ + CTEStopTimer( &TimeoutTimer ); +} + +#endif diff --git a/private/ntos/nbt/vxd/cvxdfile.asm b/private/ntos/nbt/vxd/cvxdfile.asm new file mode 100644 index 000000000..451f18501 --- /dev/null +++ b/private/ntos/nbt/vxd/cvxdfile.asm @@ -0,0 +1,186 @@ +;/**********************************************************************/ +;/** Microsoft Windows/NT **/ +;/** Copyright(c) Microsoft Corp., 1994 **/ +;/**********************************************************************/ + +;/* +; cvxdFile.asm +; +; Contains simple VXD File I/O routines (that use VxdInt 21h macro) +; for dhcp.bin support +; +; FILE HISTORY: +; madana 07-May-1994 Created +; koti 10-Oct-1994 Copied over/modified for vnbt +; +;*/ + + .386p + include vmm.inc + include v86mmgr.inc + include dosmgr.inc + include opttest.inc + include netvxd.inc + include debug.inc + include pageable.inc + +EXTRN _fInInit:DWORD +EXTRN _GetInDosFlag:NEAR + + +;**************************************************************************** +;** PushState Macro +; +; Saves the client state. +; +PushState MACRO + + push edi + push esi + push ebx + + mov ecx, 0 + VMMCall Begin_Critical_Section + +ENDM + +;**************************************************************************** +;** PopState Macro +; +; Restores client state. +; +PopState MACRO + + VMMCall End_Critical_Section + + pop ebx + pop esi + pop edi + +ENDM + +NBT_PAGEABLE_CODE_SEG + +;**************************************************************************** +;** _VxdFileOpen +; +; Opens a file +; +; Entry: [ESP+4] - Pointer to full path of file, path must be flat +; pointer. +; +; Exit: EAX will contain a handle to the openned file +; +BeginProc _VxdFileOpen + + PushState ; This pushes lots of crap + + mov edx, [esp+16] ; move flat pointer file name + mov eax, 3d02h ; Open file, read/write, share + + VxDInt 21h + + jc VFO_6 ; Carry set if error + jmp VFO_10 ; successful file open, eax has file handle + +VFO_6: +; Debug_Out "VxdFileOpen failed, error #EAX" + mov eax, 0 ; Failed to open the file + +VFO_10: + PopState + ret + +EndProc _VxdFileOpen + + +;**************************************************************************** +;** _VxdFileRead +; +; Reads x bytes from a previously openned file +; +; Entry: [ESP+4] - Handle from _VxdFileOpen +; [ESP+8] - Count of bytes to read +; [ESP+12]- flat pointer to destination buffer +; +; Exit: EAX will contain the number of bytes read, 0 if EOF or +; an error occurred. +; +BeginProc _VxdFileRead + + PushState ; Pushes lots of crap + + mov ebx, [esp+16] ; File Handle + mov ecx, [esp+20] ; Bytes to read + mov edx, [esp+24] ; flat buffer pointer + mov eax, 3f00h + + VxDInt 21h + + jc VFR_6 ; Carry set if error + jmp VFR_7 ; successful file open, eax has bytes read + +VFR_6: +; Debug_Out "VxdFileRead failed" + mov eax, 0 ; Failed to read the file +VFR_7: + PopState + ret + +EndProc _VxdFileRead + +;**************************************************************************** +;** _VxdFileClose +; +; Closes a file openned with VxdOpenFile +; +; Entry: [ESP+4] - Handle from _VxdFileOpen +; +BeginProc _VxdFileClose + + PushState ; Pushes lots of crap + + mov ebx, [esp+16] ; File Handle + mov eax, 3e00h + + VxDInt 21h + + jc VFCL_6 ; Carry set if error + jmp VFCL_7 ; successful set. + +VFCL_6: + Debug_Out "VxdFileClose - Read failed" + mov eax, 0 ; Failed to close the file +VFCL_7: + PopState + ret + +EndProc _VxdFileClose + +;**************************************************************************** +;** _VxdWindowsPath +; +; Gets a pointer to (null-terminated) path to the windows directory +; +; This is an Init time only routine +; +; Entry: nothing +; +; Exit: pointer to path to windows directory +; +BeginProc _VxdWindowsPath + PushState ; Pushes lots of crap + + VmmCall Get_Config_Directory + + mov eax, edx ; path is returned in edx + + PopState ; now pop all that crap + + ret + +EndProc _VxdWindowsPath + +NBT_PAGEABLE_CODE_ENDS + +END diff --git a/private/ntos/nbt/vxd/cxport.inc b/private/ntos/nbt/vxd/cxport.inc new file mode 100644 index 000000000..edbc6d2a2 --- /dev/null +++ b/private/ntos/nbt/vxd/cxport.inc @@ -0,0 +1,4 @@ +;; Dummy include file so we can build COFF version of +;; "cxport.obj." +;; +;; Eventually, this may become obsolete. diff --git a/private/ntos/nbt/vxd/depend.mk b/private/ntos/nbt/vxd/depend.mk new file mode 100644 index 000000000..5dba4a995 --- /dev/null +++ b/private/ntos/nbt/vxd/depend.mk @@ -0,0 +1,1279 @@ +#******************************************************************** +#** Copyright(c) Microsoft Corp., 1993 ** +#******************************************************************** +$(SNOVNBTOBJD)/chicasm.obj $(SNODVNBTOBJD)/chicasm.obj $(VNBTSRC)/chicasm.lst: \ + $(VNBTSRC)/chicasm.asm ../blt/netvxd.inc ../blt/vdhcp.inc \ + $(VNBTSRC)/vnbtd.inc $(IMPORT)/win32/ddk/inc/debug.inc \ + $(IMPORT)/wininc/dosmgr.inc \ + $(IMPORT)/wininc/vnetbios.inc $(NDIS3INC)/vmm.inc \ + $(CHICAGO)/tcp/inc/vip.inc $(CHICAGO)/tcp/inc/vtdi.inc + +$(SNOVNBTOBJD)/client.obj $(SNODVNBTOBJD)/client.obj $(VNBTSRC)/client.lst: \ + $(VNBTSRC)/client.asm ../blt/netvxd.inc \ + $(IMPORT)/win32/ddk/inc/debug.inc \ + $(IMPORT)/win32/ddk/inc/shell.inc \ + $(IMPORT)/win32/ddk/inc/shellfsc.inc \ + $(NDIS3INC)/vmm.inc $(NDIS3INC)/vwin32.inc + +$(SNOVNBTOBJD)/cvxdfile.obj $(SNODVNBTOBJD)/cvxdfile.obj $(VNBTSRC)/cvxdfile.lst: \ + $(VNBTSRC)/cvxdfile.asm ../blt/netvxd.inc \ + $(IMPORT)/win32/ddk/inc/debug.inc \ + $(IMPORT)/win32/ddk/inc/opttest.inc \ + $(IMPORT)/wininc/dosmgr.inc \ + $(IMPORT)/wininc/v86mmgr.inc $(NDIS3INC)/vmm.inc + +$(SNOVNBTOBJD)/vfirst.obj $(SNODVNBTOBJD)/vfirst.obj $(VNBTSRC)/vfirst.lst: \ + $(VNBTSRC)/vfirst.asm + +$(SNOVNBTOBJD)/vnbtd.obj $(SNODVNBTOBJD)/vnbtd.obj $(VNBTSRC)/vnbtd.lst: \ + $(VNBTSRC)/vnbtd.asm ../blt/netvxd.inc ../blt/vdhcp.inc $(VNBTSRC)/vnbtd.inc \ + $(IMPORT)/win32/ddk/inc/debug.inc \ + $(IMPORT)/wininc/dosmgr.inc \ + $(IMPORT)/wininc/vnetbios.inc $(NDIS3INC)/vmm.inc \ + $(CHICAGO)/tcp/inc/vtdi.inc + +$(SNOVNBTOBJD)/vxdfile.obj $(SNODVNBTOBJD)/vxdfile.obj $(VNBTSRC)/vxdfile.lst: \ + $(VNBTSRC)/vxdfile.asm ../blt/netvxd.inc \ + $(IMPORT)/win32/ddk/inc/debug.inc \ + $(IMPORT)/win32/ddk/inc/opttest.inc \ + $(IMPORT)/wininc/dosmgr.inc \ + $(IMPORT)/wininc/v86mmgr.inc $(NDIS3INC)/vmm.inc + +$(SNOVNBTOBJD)/vxdstub.obj $(SNODVNBTOBJD)/vxdstub.obj $(VNBTSRC)/vxdstub.lst: \ + $(VNBTSRC)/vxdstub.asm $(IMPORT)/wininc/INT2FAPI.INC + +$(SNOVNBTOBJD)/wfwasm.obj $(SNODVNBTOBJD)/wfwasm.obj $(VNBTSRC)/wfwasm.lst: \ + $(VNBTSRC)/wfwasm.asm ../blt/netvxd.inc ../blt/vdhcp.inc $(VNBTSRC)/vnbtd.inc \ + $(IMPORT)/win32/ddk/inc/debug.inc \ + $(IMPORT)/wininc/dosmgr.inc \ + $(IMPORT)/wininc/vnetbios.inc $(NDIS3INC)/vmm.inc \ + $(CHICAGO)/tcp/inc/vtdi.inc + +$(CHIVNBTOBJD)/chicasm.obj $(CHIDVNBTOBJD)/chicasm.obj $(VNBTSRC)/chicasm.lst: \ + $(VNBTSRC)/chicasm.asm ../blt/vdhcp.inc $(VNBTSRC)/vnbtd.inc \ + $(CHICAGO)/dev/ddk/inc/debug.inc $(CHICAGO)/dev/ddk/inc/dosmgr.inc \ + $(CHICAGO)/dev/ddk/inc/netvxd.inc $(CHICAGO)/dev/ddk/inc/vmm.inc \ + $(CHICAGO)/dev/ddk/inc/vnetbios.inc $(CHICAGO)/tcp/inc/vip.inc \ + $(CHICAGO)/tcp/inc/vtdi.inc + +$(CHIVNBTOBJD)/client.obj $(CHIDVNBTOBJD)/client.obj $(VNBTSRC)/client.lst: \ + $(VNBTSRC)/client.asm $(CHICAGO)/dev/ddk/inc/debug.inc \ + $(CHICAGO)/dev/ddk/inc/netvxd.inc $(CHICAGO)/dev/ddk/inc/shell.inc \ + $(CHICAGO)/dev/ddk/inc/vmm.inc $(CHICAGO)/dev/ddk/inc/vwin32.inc + +$(CHIVNBTOBJD)/cvxdfile.obj $(CHIDVNBTOBJD)/cvxdfile.obj $(VNBTSRC)/cvxdfile.lst: \ + $(VNBTSRC)/cvxdfile.asm $(CHICAGO)/dev/ddk/inc/debug.inc \ + $(CHICAGO)/dev/ddk/inc/dosmgr.inc $(CHICAGO)/dev/ddk/inc/netvxd.inc \ + $(CHICAGO)/dev/ddk/inc/opttest.inc $(CHICAGO)/dev/ddk/inc/v86mmgr.inc \ + $(CHICAGO)/dev/ddk/inc/vmm.inc + +$(CHIVNBTOBJD)/vfirst.obj $(CHIDVNBTOBJD)/vfirst.obj $(VNBTSRC)/vfirst.lst: \ + $(VNBTSRC)/vfirst.asm + +$(CHIVNBTOBJD)/vnbtd.obj $(CHIDVNBTOBJD)/vnbtd.obj $(VNBTSRC)/vnbtd.lst: \ + $(VNBTSRC)/vnbtd.asm ../blt/vdhcp.inc $(VNBTSRC)/vnbtd.inc \ + $(CHICAGO)/dev/ddk/inc/debug.inc $(CHICAGO)/dev/ddk/inc/dosmgr.inc \ + $(CHICAGO)/dev/ddk/inc/netvxd.inc $(CHICAGO)/dev/ddk/inc/vmm.inc \ + $(CHICAGO)/dev/ddk/inc/vnetbios.inc $(CHICAGO)/tcp/inc/vtdi.inc + +$(CHIVNBTOBJD)/vxdfile.obj $(CHIDVNBTOBJD)/vxdfile.obj $(VNBTSRC)/vxdfile.lst: \ + $(VNBTSRC)/vxdfile.asm $(CHICAGO)/dev/ddk/inc/debug.inc \ + $(CHICAGO)/dev/ddk/inc/dosmgr.inc $(CHICAGO)/dev/ddk/inc/netvxd.inc \ + $(CHICAGO)/dev/ddk/inc/opttest.inc $(CHICAGO)/dev/ddk/inc/v86mmgr.inc \ + $(CHICAGO)/dev/ddk/inc/vmm.inc + +$(CHIVNBTOBJD)/vxdstub.obj $(CHIDVNBTOBJD)/vxdstub.obj $(VNBTSRC)/vxdstub.lst: \ + $(VNBTSRC)/vxdstub.asm $(CHICAGO)/dev/ddk/inc/INT2FAPI.INC + +$(CHIVNBTOBJD)/wfwasm.obj $(CHIDVNBTOBJD)/wfwasm.obj $(VNBTSRC)/wfwasm.lst: \ + $(VNBTSRC)/wfwasm.asm ../blt/vdhcp.inc $(VNBTSRC)/vnbtd.inc \ + $(CHICAGO)/dev/ddk/inc/debug.inc $(CHICAGO)/dev/ddk/inc/dosmgr.inc \ + $(CHICAGO)/dev/ddk/inc/netvxd.inc $(CHICAGO)/dev/ddk/inc/vmm.inc \ + $(CHICAGO)/dev/ddk/inc/vnetbios.inc $(CHICAGO)/tcp/inc/vtdi.inc + +$(SNOVNBTOBJD)/chic.obj $(SNODVNBTOBJD)/chic.obj $(VNBTSRC)/chic.lst: \ + $(VNBTSRC)/chic.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/nbtinfo.h $(INC)/nbtnt.h $(INC)/nbtprocs.h \ + $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h \ + $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/ipinfo.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdiinfo.h \ + $(BASEDIR)/private/inc/tdikrnl.h $(BASEDIR)/private/inc/tdistat.h \ + $(BASEDIR)/public/sdk/inc/alphaops.h $(BASEDIR)/public/sdk/inc/crt/ctype.h \ + $(BASEDIR)/public/sdk/inc/crt/excpt.h $(BASEDIR)/public/sdk/inc/crt/limits.h \ + $(BASEDIR)/public/sdk/inc/crt/stdarg.h $(BASEDIR)/public/sdk/inc/crt/stddef.h \ + $(BASEDIR)/public/sdk/inc/crt/string.h $(BASEDIR)/public/sdk/inc/devioctl.h \ + $(BASEDIR)/public/sdk/inc/lintfunc.hxx $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/netevent.h \ + $(BASEDIR)/public/sdk/inc/nt.h $(BASEDIR)/public/sdk/inc/ntalpha.h \ + $(BASEDIR)/public/sdk/inc/ntconfig.h $(BASEDIR)/public/sdk/inc/ntddtdi.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntppc.h $(BASEDIR)/public/sdk/inc/ntpsapi.h \ + $(BASEDIR)/public/sdk/inc/ntregapi.h $(BASEDIR)/public/sdk/inc/ntrtl.h \ + $(BASEDIR)/public/sdk/inc/ntseapi.h $(BASEDIR)/public/sdk/inc/ntstatus.h \ + $(BASEDIR)/public/sdk/inc/ntxcapi.h $(BASEDIR)/public/sdk/inc/poppack.h \ + $(BASEDIR)/public/sdk/inc/ppcinst.h $(BASEDIR)/public/sdk/inc/pshpack1.h \ + $(BASEDIR)/public/sdk/inc/pshpack4.h $(BASEDIR)/public/sdk/inc/windef.h \ + $(BASEDIR)/public/sdk/inc/winerror.h $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/fileio.obj $(SNODVNBTOBJD)/fileio.obj $(VNBTSRC)/fileio.lst: \ + $(VNBTSRC)/fileio.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/hosts.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdikrnl.h $(BASEDIR)/private/inc/tdistat.h \ + $(BASEDIR)/public/sdk/inc/alphaops.h $(BASEDIR)/public/sdk/inc/crt/ctype.h \ + $(BASEDIR)/public/sdk/inc/crt/excpt.h $(BASEDIR)/public/sdk/inc/crt/limits.h \ + $(BASEDIR)/public/sdk/inc/crt/stdarg.h $(BASEDIR)/public/sdk/inc/crt/stddef.h \ + $(BASEDIR)/public/sdk/inc/crt/string.h $(BASEDIR)/public/sdk/inc/devioctl.h \ + $(BASEDIR)/public/sdk/inc/lintfunc.hxx $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/netevent.h \ + $(BASEDIR)/public/sdk/inc/nt.h $(BASEDIR)/public/sdk/inc/ntalpha.h \ + $(BASEDIR)/public/sdk/inc/ntconfig.h $(BASEDIR)/public/sdk/inc/ntddtdi.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntppc.h $(BASEDIR)/public/sdk/inc/ntpsapi.h \ + $(BASEDIR)/public/sdk/inc/ntregapi.h $(BASEDIR)/public/sdk/inc/ntrtl.h \ + $(BASEDIR)/public/sdk/inc/ntseapi.h $(BASEDIR)/public/sdk/inc/ntstatus.h \ + $(BASEDIR)/public/sdk/inc/ntxcapi.h $(BASEDIR)/public/sdk/inc/poppack.h \ + $(BASEDIR)/public/sdk/inc/ppcinst.h $(BASEDIR)/public/sdk/inc/pshpack1.h \ + $(BASEDIR)/public/sdk/inc/pshpack4.h $(BASEDIR)/public/sdk/inc/windef.h \ + $(BASEDIR)/public/sdk/inc/winerror.h $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/init.obj $(SNODVNBTOBJD)/init.obj $(VNBTSRC)/init.lst: \ + $(VNBTSRC)/init.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/hosts.h $(INC)/nbtinfo.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/ipinfo.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdiinfo.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/nbtinfo.obj $(SNODVNBTOBJD)/nbtinfo.obj $(VNBTSRC)/nbtinfo.lst: \ + $(VNBTSRC)/nbtinfo.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/nbtinfo.h $(INC)/nbtnt.h $(INC)/nbtprocs.h \ + $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h \ + $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdikrnl.h $(BASEDIR)/private/inc/tdistat.h \ + $(BASEDIR)/public/sdk/inc/alphaops.h $(BASEDIR)/public/sdk/inc/crt/ctype.h \ + $(BASEDIR)/public/sdk/inc/crt/excpt.h $(BASEDIR)/public/sdk/inc/crt/limits.h \ + $(BASEDIR)/public/sdk/inc/crt/stdarg.h $(BASEDIR)/public/sdk/inc/crt/stddef.h \ + $(BASEDIR)/public/sdk/inc/crt/string.h $(BASEDIR)/public/sdk/inc/devioctl.h \ + $(BASEDIR)/public/sdk/inc/lintfunc.hxx $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/netevent.h \ + $(BASEDIR)/public/sdk/inc/nt.h $(BASEDIR)/public/sdk/inc/ntalpha.h \ + $(BASEDIR)/public/sdk/inc/ntconfig.h $(BASEDIR)/public/sdk/inc/ntddtdi.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntppc.h $(BASEDIR)/public/sdk/inc/ntpsapi.h \ + $(BASEDIR)/public/sdk/inc/ntregapi.h $(BASEDIR)/public/sdk/inc/ntrtl.h \ + $(BASEDIR)/public/sdk/inc/ntseapi.h $(BASEDIR)/public/sdk/inc/ntstatus.h \ + $(BASEDIR)/public/sdk/inc/ntxcapi.h $(BASEDIR)/public/sdk/inc/poppack.h \ + $(BASEDIR)/public/sdk/inc/ppcinst.h $(BASEDIR)/public/sdk/inc/pshpack1.h \ + $(BASEDIR)/public/sdk/inc/pshpack4.h $(BASEDIR)/public/sdk/inc/windef.h \ + $(BASEDIR)/public/sdk/inc/winerror.h $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/ncb.obj $(SNODVNBTOBJD)/ncb.obj $(VNBTSRC)/ncb.lst: $(VNBTSRC)/ncb.c \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/tdiaddr.obj $(SNODVNBTOBJD)/tdiaddr.obj $(VNBTSRC)/tdiaddr.lst: \ + $(VNBTSRC)/tdiaddr.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/tdicnct.obj $(SNODVNBTOBJD)/tdicnct.obj $(VNBTSRC)/tdicnct.lst: \ + $(VNBTSRC)/tdicnct.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/tdihndlr.obj $(SNODVNBTOBJD)/tdihndlr.obj $(VNBTSRC)/tdihndlr.lst: \ + $(VNBTSRC)/tdihndlr.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/tdiout.obj $(SNODVNBTOBJD)/tdiout.obj $(VNBTSRC)/tdiout.lst: \ + $(VNBTSRC)/tdiout.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/timer.obj $(SNODVNBTOBJD)/timer.obj $(VNBTSRC)/timer.lst: \ + $(VNBTSRC)/timer.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/util.obj $(SNODVNBTOBJD)/util.obj $(VNBTSRC)/util.lst: \ + $(VNBTSRC)/util.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/vxddebug.obj $(SNODVNBTOBJD)/vxddebug.obj $(VNBTSRC)/vxddebug.lst: \ + $(VNBTSRC)/vxddebug.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/vxdisol.obj $(SNODVNBTOBJD)/vxdisol.obj $(VNBTSRC)/vxdisol.lst: \ + $(VNBTSRC)/vxdisol.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(SNOVNBTOBJD)/wfw.obj $(SNODVNBTOBJD)/wfw.obj $(VNBTSRC)/wfw.lst: $(VNBTSRC)/wfw.c \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/nbtinfo.h $(INC)/nbtnt.h $(INC)/nbtprocs.h \ + $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h \ + $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/ipinfo.h $(BASEDIR)/private/inc/llinfo.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdiinfo.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/chic.obj $(CHIDVNBTOBJD)/chic.obj $(VNBTSRC)/chic.lst: \ + $(VNBTSRC)/chic.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/nbtinfo.h $(INC)/nbtnt.h $(INC)/nbtprocs.h \ + $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h \ + $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/ipinfo.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdiinfo.h \ + $(BASEDIR)/private/inc/tdikrnl.h $(BASEDIR)/private/inc/tdistat.h \ + $(BASEDIR)/public/sdk/inc/alphaops.h $(BASEDIR)/public/sdk/inc/crt/ctype.h \ + $(BASEDIR)/public/sdk/inc/crt/excpt.h $(BASEDIR)/public/sdk/inc/crt/limits.h \ + $(BASEDIR)/public/sdk/inc/crt/stdarg.h $(BASEDIR)/public/sdk/inc/crt/stddef.h \ + $(BASEDIR)/public/sdk/inc/crt/string.h $(BASEDIR)/public/sdk/inc/devioctl.h \ + $(BASEDIR)/public/sdk/inc/lintfunc.hxx $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/netevent.h \ + $(BASEDIR)/public/sdk/inc/nt.h $(BASEDIR)/public/sdk/inc/ntalpha.h \ + $(BASEDIR)/public/sdk/inc/ntconfig.h $(BASEDIR)/public/sdk/inc/ntddtdi.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntppc.h $(BASEDIR)/public/sdk/inc/ntpsapi.h \ + $(BASEDIR)/public/sdk/inc/ntregapi.h $(BASEDIR)/public/sdk/inc/ntrtl.h \ + $(BASEDIR)/public/sdk/inc/ntseapi.h $(BASEDIR)/public/sdk/inc/ntstatus.h \ + $(BASEDIR)/public/sdk/inc/ntxcapi.h $(BASEDIR)/public/sdk/inc/poppack.h \ + $(BASEDIR)/public/sdk/inc/ppcinst.h $(BASEDIR)/public/sdk/inc/pshpack1.h \ + $(BASEDIR)/public/sdk/inc/pshpack4.h $(BASEDIR)/public/sdk/inc/windef.h \ + $(BASEDIR)/public/sdk/inc/winerror.h $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/fileio.obj $(CHIDVNBTOBJD)/fileio.obj $(VNBTSRC)/fileio.lst: \ + $(VNBTSRC)/fileio.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/hosts.h \ + $(INC)/nbtnt.h $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h \ + $(INC)/types.h $(INC)/vxddebug.h $(INC)/vxdprocs.h \ + $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdikrnl.h $(BASEDIR)/private/inc/tdistat.h \ + $(BASEDIR)/public/sdk/inc/alphaops.h $(BASEDIR)/public/sdk/inc/crt/ctype.h \ + $(BASEDIR)/public/sdk/inc/crt/excpt.h $(BASEDIR)/public/sdk/inc/crt/limits.h \ + $(BASEDIR)/public/sdk/inc/crt/stdarg.h $(BASEDIR)/public/sdk/inc/crt/stddef.h \ + $(BASEDIR)/public/sdk/inc/crt/string.h $(BASEDIR)/public/sdk/inc/devioctl.h \ + $(BASEDIR)/public/sdk/inc/lintfunc.hxx $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/netevent.h \ + $(BASEDIR)/public/sdk/inc/nt.h $(BASEDIR)/public/sdk/inc/ntalpha.h \ + $(BASEDIR)/public/sdk/inc/ntconfig.h $(BASEDIR)/public/sdk/inc/ntddtdi.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntppc.h $(BASEDIR)/public/sdk/inc/ntpsapi.h \ + $(BASEDIR)/public/sdk/inc/ntregapi.h $(BASEDIR)/public/sdk/inc/ntrtl.h \ + $(BASEDIR)/public/sdk/inc/ntseapi.h $(BASEDIR)/public/sdk/inc/ntstatus.h \ + $(BASEDIR)/public/sdk/inc/ntxcapi.h $(BASEDIR)/public/sdk/inc/poppack.h \ + $(BASEDIR)/public/sdk/inc/ppcinst.h $(BASEDIR)/public/sdk/inc/pshpack1.h \ + $(BASEDIR)/public/sdk/inc/pshpack4.h $(BASEDIR)/public/sdk/inc/windef.h \ + $(BASEDIR)/public/sdk/inc/winerror.h $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/init.obj $(CHIDVNBTOBJD)/init.obj $(VNBTSRC)/init.lst: \ + $(VNBTSRC)/init.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/hosts.h $(INC)/nbtinfo.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/ipinfo.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdiinfo.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/nbtinfo.obj $(CHIDVNBTOBJD)/nbtinfo.obj $(VNBTSRC)/nbtinfo.lst: \ + $(VNBTSRC)/nbtinfo.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/nbtinfo.h $(INC)/nbtnt.h $(INC)/nbtprocs.h \ + $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h \ + $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdikrnl.h $(BASEDIR)/private/inc/tdistat.h \ + $(BASEDIR)/public/sdk/inc/alphaops.h $(BASEDIR)/public/sdk/inc/crt/ctype.h \ + $(BASEDIR)/public/sdk/inc/crt/excpt.h $(BASEDIR)/public/sdk/inc/crt/limits.h \ + $(BASEDIR)/public/sdk/inc/crt/stdarg.h $(BASEDIR)/public/sdk/inc/crt/stddef.h \ + $(BASEDIR)/public/sdk/inc/crt/string.h $(BASEDIR)/public/sdk/inc/devioctl.h \ + $(BASEDIR)/public/sdk/inc/lintfunc.hxx $(BASEDIR)/public/sdk/inc/mipsinst.h \ + $(BASEDIR)/public/sdk/inc/nb30.h $(BASEDIR)/public/sdk/inc/netevent.h \ + $(BASEDIR)/public/sdk/inc/nt.h $(BASEDIR)/public/sdk/inc/ntalpha.h \ + $(BASEDIR)/public/sdk/inc/ntconfig.h $(BASEDIR)/public/sdk/inc/ntddtdi.h \ + $(BASEDIR)/public/sdk/inc/ntdef.h $(BASEDIR)/public/sdk/inc/ntelfapi.h \ + $(BASEDIR)/public/sdk/inc/ntexapi.h $(BASEDIR)/public/sdk/inc/nti386.h \ + $(BASEDIR)/public/sdk/inc/ntimage.h $(BASEDIR)/public/sdk/inc/ntioapi.h \ + $(BASEDIR)/public/sdk/inc/ntiolog.h $(BASEDIR)/public/sdk/inc/ntkeapi.h \ + $(BASEDIR)/public/sdk/inc/ntldr.h $(BASEDIR)/public/sdk/inc/ntlpcapi.h \ + $(BASEDIR)/public/sdk/inc/ntmips.h $(BASEDIR)/public/sdk/inc/ntmmapi.h \ + $(BASEDIR)/public/sdk/inc/ntnls.h $(BASEDIR)/public/sdk/inc/ntobapi.h \ + $(BASEDIR)/public/sdk/inc/ntppc.h $(BASEDIR)/public/sdk/inc/ntpsapi.h \ + $(BASEDIR)/public/sdk/inc/ntregapi.h $(BASEDIR)/public/sdk/inc/ntrtl.h \ + $(BASEDIR)/public/sdk/inc/ntseapi.h $(BASEDIR)/public/sdk/inc/ntstatus.h \ + $(BASEDIR)/public/sdk/inc/ntxcapi.h $(BASEDIR)/public/sdk/inc/poppack.h \ + $(BASEDIR)/public/sdk/inc/ppcinst.h $(BASEDIR)/public/sdk/inc/pshpack1.h \ + $(BASEDIR)/public/sdk/inc/pshpack4.h $(BASEDIR)/public/sdk/inc/windef.h \ + $(BASEDIR)/public/sdk/inc/winerror.h $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/ncb.obj $(CHIDVNBTOBJD)/ncb.obj $(VNBTSRC)/ncb.lst: $(VNBTSRC)/ncb.c \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/tdiaddr.obj $(CHIDVNBTOBJD)/tdiaddr.obj $(VNBTSRC)/tdiaddr.lst: \ + $(VNBTSRC)/tdiaddr.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/tdicnct.obj $(CHIDVNBTOBJD)/tdicnct.obj $(VNBTSRC)/tdicnct.lst: \ + $(VNBTSRC)/tdicnct.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/tdihndlr.obj $(CHIDVNBTOBJD)/tdihndlr.obj $(VNBTSRC)/tdihndlr.lst: \ + $(VNBTSRC)/tdihndlr.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/tdiout.obj $(CHIDVNBTOBJD)/tdiout.obj $(VNBTSRC)/tdiout.lst: \ + $(VNBTSRC)/tdiout.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/timer.obj $(CHIDVNBTOBJD)/timer.obj $(VNBTSRC)/timer.lst: \ + $(VNBTSRC)/timer.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/util.obj $(CHIDVNBTOBJD)/util.obj $(VNBTSRC)/util.lst: \ + $(VNBTSRC)/util.c ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/vxddebug.obj $(CHIDVNBTOBJD)/vxddebug.obj $(VNBTSRC)/vxddebug.lst: \ + $(VNBTSRC)/vxddebug.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/vxdisol.obj $(CHIDVNBTOBJD)/vxdisol.obj $(VNBTSRC)/vxdisol.lst: \ + $(VNBTSRC)/vxdisol.c ../$(INC)/alpha.h ../$(INC)/alpharef.h \ + ../$(INC)/arc.h ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h $(INC)/ctemacro.h $(INC)/debug.h $(INC)/nbtnt.h \ + $(INC)/nbtprocs.h $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h \ + $(INC)/vxddebug.h $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h \ + $(CHICAGO)/tcp/h/tdivxd.h $(BASEDIR)/private/inc/nbtioctl.h \ + $(BASEDIR)/private/inc/nettypes.h $(BASEDIR)/private/inc/packoff.h \ + $(BASEDIR)/private/inc/packon.h $(BASEDIR)/private/inc/sockets/netinet/in.h \ + $(BASEDIR)/private/inc/status.h $(BASEDIR)/private/inc/sys/snet/ip_proto.h \ + $(BASEDIR)/private/inc/tdi.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + +$(CHIVNBTOBJD)/wfw.obj $(CHIDVNBTOBJD)/wfw.obj $(VNBTSRC)/wfw.lst: $(VNBTSRC)/wfw.c \ + ../$(INC)/alpha.h ../$(INC)/alpharef.h ../$(INC)/arc.h \ + ../$(INC)/bugcodes.h ../$(INC)/cache.h ../$(INC)/cm.h \ + ../$(INC)/cxport.h ../$(INC)/dbgk.h ../$(INC)/ex.h \ + ../$(INC)/exboosts.h ../$(INC)/exlevels.h ../$(INC)/hal.h \ + ../$(INC)/i386.h ../$(INC)/init.h ../$(INC)/io.h ../$(INC)/kd.h \ + ../$(INC)/ke.h ../$(INC)/lfs.h ../$(INC)/lpc.h ../$(INC)/mips.h \ + ../$(INC)/mm.h ../$(INC)/ntddk.h ../$(INC)/ntiologc.h \ + ../$(INC)/ntmp.h ../$(INC)/ntos.h ../$(INC)/ntosdef.h ../$(INC)/ob.h \ + ../$(INC)/ppc.h ../$(INC)/ps.h ../$(INC)/se.h ../$(INC)/updriver.h \ + ../$(INC)/v86emul.h ../blt/dhcpinfo.h $(INC)/ctemacro.h \ + $(INC)/debug.h $(INC)/nbtinfo.h $(INC)/nbtnt.h $(INC)/nbtprocs.h \ + $(INC)/ntprocs.h $(INC)/timer.h $(INC)/types.h $(INC)/vxddebug.h \ + $(INC)/vxdprocs.h $(CHICAGO)/tcp/h/oscfg.h $(CHICAGO)/tcp/h/tdivxd.h \ + $(BASEDIR)/private/inc/ipinfo.h $(BASEDIR)/private/inc/llinfo.h \ + $(BASEDIR)/private/inc/nbtioctl.h $(BASEDIR)/private/inc/nettypes.h \ + $(BASEDIR)/private/inc/packoff.h $(BASEDIR)/private/inc/packon.h \ + $(BASEDIR)/private/inc/sockets/netinet/in.h $(BASEDIR)/private/inc/status.h \ + $(BASEDIR)/private/inc/sys/snet/ip_proto.h $(BASEDIR)/private/inc/tdi.h \ + $(BASEDIR)/private/inc/tdiinfo.h $(BASEDIR)/private/inc/tdikrnl.h \ + $(BASEDIR)/private/inc/tdistat.h $(BASEDIR)/public/sdk/inc/alphaops.h \ + $(BASEDIR)/public/sdk/inc/crt/ctype.h $(BASEDIR)/public/sdk/inc/crt/excpt.h \ + $(BASEDIR)/public/sdk/inc/crt/limits.h $(BASEDIR)/public/sdk/inc/crt/stdarg.h \ + $(BASEDIR)/public/sdk/inc/crt/stddef.h $(BASEDIR)/public/sdk/inc/crt/string.h \ + $(BASEDIR)/public/sdk/inc/devioctl.h $(BASEDIR)/public/sdk/inc/lintfunc.hxx \ + $(BASEDIR)/public/sdk/inc/mipsinst.h $(BASEDIR)/public/sdk/inc/nb30.h \ + $(BASEDIR)/public/sdk/inc/netevent.h $(BASEDIR)/public/sdk/inc/nt.h \ + $(BASEDIR)/public/sdk/inc/ntalpha.h $(BASEDIR)/public/sdk/inc/ntconfig.h \ + $(BASEDIR)/public/sdk/inc/ntddtdi.h $(BASEDIR)/public/sdk/inc/ntdef.h \ + $(BASEDIR)/public/sdk/inc/ntelfapi.h $(BASEDIR)/public/sdk/inc/ntexapi.h \ + $(BASEDIR)/public/sdk/inc/nti386.h $(BASEDIR)/public/sdk/inc/ntimage.h \ + $(BASEDIR)/public/sdk/inc/ntioapi.h $(BASEDIR)/public/sdk/inc/ntiolog.h \ + $(BASEDIR)/public/sdk/inc/ntkeapi.h $(BASEDIR)/public/sdk/inc/ntldr.h \ + $(BASEDIR)/public/sdk/inc/ntlpcapi.h $(BASEDIR)/public/sdk/inc/ntmips.h \ + $(BASEDIR)/public/sdk/inc/ntmmapi.h $(BASEDIR)/public/sdk/inc/ntnls.h \ + $(BASEDIR)/public/sdk/inc/ntobapi.h $(BASEDIR)/public/sdk/inc/ntppc.h \ + $(BASEDIR)/public/sdk/inc/ntpsapi.h $(BASEDIR)/public/sdk/inc/ntregapi.h \ + $(BASEDIR)/public/sdk/inc/ntrtl.h $(BASEDIR)/public/sdk/inc/ntseapi.h \ + $(BASEDIR)/public/sdk/inc/ntstatus.h $(BASEDIR)/public/sdk/inc/ntxcapi.h \ + $(BASEDIR)/public/sdk/inc/poppack.h $(BASEDIR)/public/sdk/inc/ppcinst.h \ + $(BASEDIR)/public/sdk/inc/pshpack1.h $(BASEDIR)/public/sdk/inc/pshpack4.h \ + $(BASEDIR)/public/sdk/inc/windef.h $(BASEDIR)/public/sdk/inc/winerror.h \ + $(BASEDIR)/public/sdk/inc/winnt.h + diff --git a/private/ntos/nbt/vxd/dns.c b/private/ntos/nbt/vxd/dns.c new file mode 100644 index 000000000..f32bfccf4 --- /dev/null +++ b/private/ntos/nbt/vxd/dns.c @@ -0,0 +1,984 @@ +/*++ + +Copyright (c) 1989-1996 Microsoft Corporation + +Module Name: + + DNS.c + +Abstract: + + VxD-specific DNS routines. + + These routines try to resolve NetBIOS names using DNS. + +Author: + + Earle R. Horton (ERH) 13-Feb-1996 + +Revision History: + +--*/ + +#include "nbtprocs.h" + +// +// function prototypes for completion routines that are local to this file +// +//---------------------------------------------------------------------------- +VOID +DnsCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. It must + decide if another name query should be sent to the DNS server, and if not, + then it calls the client's completion routine (in completion2). + +Arguments: + + +Return Value: + + The function value is the status of the operation. + + +Notes: +--*/ + +{ + + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + tDEVICECONTEXT *pDeviceContext; + CTELockHandle OldIrq; + COMPLETIONCLIENT pClientCompletion; + PCHAR pchDomainName; + USHORT Flags; + BOOL fOneMoreTry; + tDGRAM_SEND_TRACKING *pClientTracker; + + + KdPrint(("DnsCompletion entered\r\n")); + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + pDeviceContext = pTracker->pDeviceContext; + + + // if the client completion routine is not set anymore, then the + // timer has been cancelled and this routine should just clean up its + // buffers associated with the tracker (and return) + // + if (!pTimerQEntry) + { + // return the tracker block to its queue + LOCATION(0x52); + DereferenceTrackerNoLock((tDGRAM_SEND_TRACKING *)pContext); + return; + } + + + // + // to prevent a client from stopping the timer and deleting the + // pNameAddr, grab the lock and check if the timer has been stopped + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pTimerQEntry->Flags & TIMER_RETIMED) + { + pTimerQEntry->Flags &= ~TIMER_RETIMED; + pTimerQEntry->Flags |= TIMER_RESTART; + // + // if we are not bound to this card than use a very short timeout + // + if (!pTracker->pDeviceContext->pNameServerFileObject) + { + pTimerQEntry->DeltaTime = 10; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + + if (!pTimerQEntry->ClientCompletion) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + + pClientTracker = (tDGRAM_SEND_TRACKING *)pTimerQEntry->ClientContext; + + // + // if the tracker has been cancelled, don't do any more queries + // + if (pClientTracker->Flags & TRACKER_CANCELLED) + { + pClientCompletion = pTimerQEntry->ClientCompletion; + + // remove the link from the name table to this timer block + CHECK_PTR(((tNAMEADDR *)pTimerQEntry->pCacheEntry)); + + ((tNAMEADDR *)pTimerQEntry->pCacheEntry)->pTimer = NULL; + + // to synch. with the StopTimer routine, Null the client + // completion routine so it gets called just once. + // + CHECK_PTR(pTimerQEntry); + pTimerQEntry->ClientCompletion = NULL; + + // + // remove the name from the hash table, since it did not + // resolve via DNS either + // + CHECK_PTR(pTracker->pNameAddr); + pTracker->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pTracker->pNameAddr->NameTypeState |= STATE_RELEASED; + pTracker->pNameAddr->pTimer = NULL; + + // + // This call will remove the name from the PendingNameQueries List + // + NbtDereferenceName(pTracker->pNameAddr); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // there can be a list of trackers Q'd up on this name + // query, so we must complete all of them! + // + CompleteClientReq(pClientCompletion, + pClientTracker, + STATUS_CANCELLED); + + // return the tracker block to its queue + LOCATION(0x51); + DereferenceTracker(pTracker); + + KdPrint(("DNS resolution cancelled by client\r\n")); + + return; + + } + + // If done with all the (3) retries with primary, try secondary DNS srvr + // If secondary not defined, or done with secondary as well, stop. + // + + fOneMoreTry = TRUE; + + if (!(--pTimerQEntry->Retries)) + { + // + // if backup server is not defined, or if it is defined but we just + // finished trying backup server, go back and try primary server for + // "other domains" + // e.g. DNSDomains was defined as "msft.dom2.com,msft.dom3.com,msft.dom" + // We were pointing at msft.dom2.com. Now, we are done with that (and + // didn't get a response), so try msft.dom3.com + // + if ( ( !pDeviceContext->lDnsBackupServer ) || + ( pDeviceContext->lDnsBackupServer == LOOP_BACK ) || + ( pTracker->Flags & NBT_DNS_SERVER_BACKUP) ) + { + // + // if we just got done trying primary domain name, try all the + // "other domains" specified + // + if (pTracker->pchDomainName == NbtConfig.pDomainName) + { + pTracker->pchDomainName = NbtConfig.pDNSDomains; + if ( pTracker->pchDomainName ) + { + pTracker->Flags &= ~NBT_DNS_SERVER_BACKUP; + pTracker->Flags |= NBT_DNS_SERVER; + pTimerQEntry->Retries = NbtConfig.uNumRetries; + } + else + { + fOneMoreTry = FALSE; + } + } + + // + // if we had already started on "other domains", advance to the + // next domain within "other domains" + // + else + { + pchDomainName = pTracker->pchDomainName; + while( *pchDomainName != ',' && // dom names separated by comma + *pchDomainName != ' ' && // or space + *pchDomainName != '\0' ) + pchDomainName++; + + if ( *pchDomainName == '\0' ) + fOneMoreTry = FALSE; + else + { + pchDomainName++; + pTracker->pchDomainName = pchDomainName; + pTracker->Flags &= ~NBT_DNS_SERVER_BACKUP; + pTracker->Flags |= NBT_DNS_SERVER; + pTimerQEntry->Retries = NbtConfig.uNumRetries; + } + } + } + + // ok, prepare to try the backup server + else + { + pTimerQEntry->Retries = NbtConfig.uNumRetries; + + pTracker->Flags &= ~NBT_DNS_SERVER; + pTracker->Flags |= NBT_DNS_SERVER_BACKUP; + } + } + + // we aren't done yet: send one more query and restart the timer + if (fOneMoreTry) + { + pTracker->RefCount++; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + NULL,NULL,NULL, + 0,0, + eDNS_NAME_QUERY, + TRUE); + + DereferenceTracker(pTracker); + + pTimerQEntry->Flags |= TIMER_RESTART; + + KdPrint(("One more DNS query sent out\r\n")); + } + + // yup, all done: didn't find the name! give client above the bad news + else + { + tDGRAM_SEND_TRACKING *pClientTracker; + + + pClientTracker = (tDGRAM_SEND_TRACKING *)pTimerQEntry->ClientContext; + + pClientCompletion = pTimerQEntry->ClientCompletion; + + // remove the link from the name table to this timer block + CHECK_PTR(((tNAMEADDR *)pTimerQEntry->pCacheEntry)); + + ((tNAMEADDR *)pTimerQEntry->pCacheEntry)->pTimer = NULL; + + // to synch. with the StopTimer routine, Null the client + // completion routine so it gets called just once. + // + CHECK_PTR(pTimerQEntry); + pTimerQEntry->ClientCompletion = NULL; + + // + // remove the name from the hash table, since it did not + // resolve via DNS either + // + CHECK_PTR(pTracker->pNameAddr); + pTracker->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pTracker->pNameAddr->NameTypeState |= STATE_RELEASED; + pTracker->pNameAddr->pTimer = NULL; + + // + // This call will remove the name from the PendingNameQueries List + // + NbtDereferenceName(pTracker->pNameAddr); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // there can be a list of trackers Q'd up on this name + // query, so we must complete all of them! + // + CompleteClientReq(pClientCompletion, + pClientTracker, + STATUS_TIMEOUT); + + // return the tracker block to its queue + LOCATION(0x51); + DereferenceTracker(pTracker); + + KdPrint(("DNS resolution failed: told client\r\n")); + } + +} + +//---------------------------------------------------------------------------- +NTSTATUS +DoDnsResolve ( + IN NBT_WORK_ITEM_CONTEXT *Context + ) +/*++ + +Routine Description: + + This function is used to allow NBT to query DNS. This is very much like + the name query sent out to WINS server or broadcast. Response from the + DNS server, if any, is handled by the QueryFromNet() routine. + +Arguments: + + *Context (NBT_WORK_ITEM_CONTEXT) + +Return Value: + + STATUS_PENDING (unless something goes wrong) + +Notes: +--*/ + +{ + + tDGRAM_SEND_TRACKING *pTracker; + tDEVICECONTEXT *pDeviceContext; + ULONG Timeout; + USHORT Retries; + NTSTATUS status; + PVOID pClientCompletion; + PVOID pCompletionRoutine; + PVOID pClientContext; + + + + KdPrint(("DoDnsResolve entered\r\n")); + + pTracker = Context->pTracker; + + pDeviceContext = pTracker->pDeviceContext; + + // + // If the primary DNS server is not defined, just return error. + if ( (!pDeviceContext->lDnsServerAddress) || + ( pDeviceContext->lDnsServerAddress == LOOP_BACK) ) + { + return( NRC_CMDTMO ); + } + + pTracker->Flags &= ~(NBT_BROADCAST|NBT_NAME_SERVER|NBT_NAME_SERVER_BACKUP); + pTracker->Flags |= NBT_DNS_SERVER; + + pClientContext = Context->pClientContext; + pClientCompletion = Context->ClientCompletion; + pCompletionRoutine = DnsCompletion; + + // + // free that memory now + // + CTEMemFree(Context); + + // + // Put on the pending name queries list again so that when the query + // response comes in from DNS we can find the pNameAddr record. + // + ExInterlockedInsertTailList(&NbtConfig.PendingNameQueries, + &pTracker->pNameAddr->Linkage, + &NbtConfig.JointLock.SpinLock); + + Timeout = (ULONG)pNbtGlobConfig->uRetryTimeout; + Retries = pNbtGlobConfig->uNumRetries; + + pTracker->RefCount++; + + // + // first time, we want to try the primary domain name + // + pTracker->pchDomainName = NbtConfig.pDomainName; + + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + pCompletionRoutine, + pClientContext, + pClientCompletion, + Retries, + Timeout, + eDNS_NAME_QUERY, + TRUE); + + DereferenceTracker(pTracker); + + KdPrint(("Leaving DoDnsResolve\r\n")); + + return( status ); + + +} + +//---------------------------------------------------------------------------- +PCHAR +DnsStoreName +( + OUT PCHAR pDest, + IN PCHAR pName, + IN PCHAR pDomainName, + IN enum eNSTYPE eNsType + ) +/*++ + +Routine Description: + + This routine copies the netbios name (and appends the scope on the + end) in the DNS namequery packet + +Arguments: + + +Return Value: + + the address of the next byte in the destination after the the name + has been copied + +--*/ +{ + LONG i; + LONG count; + PCHAR pStarting; + PCHAR pSrc; + LONG DomNameSize; + LONG OneMoreSubfield; + + LONG lMaxCount; + CHAR cTerminator; + + if (eNsType == eDIRECT_DNS_NAME_QUERY) + { + lMaxCount = 255; + cTerminator = 0; + } + else + { + lMaxCount = NETBIOS_NAME_SIZE-1; + cTerminator = 0x20; + } + + + pStarting = pDest++; + count = 0; + // + // copy until we reach the space padding + // + while ( ( count < lMaxCount ) && (*pName != cTerminator) ) + { + *pDest++ = *pName++; + count++; + } + + *pStarting = (CHAR)count; + + // + // check if domain name exists. koti.microsoft.com will be represented + // as 4KOTI9microsoft3com0 (where nos. => no. of bytes of subfield) + // + pSrc = pDomainName; + if (pSrc && pSrc[0] != '\0') + { + OneMoreSubfield = 1; + + while( OneMoreSubfield ) + { + count = 0; + pStarting = pDest++; + // + // remember, the domain name we receive can also be a set of "other + // domains" to try in the form "msft.dom2.com,msft.dom3.com" + // + while ( *pSrc != '.' && *pSrc != '\0' && *pSrc != ',') + { + *pDest++ = *pSrc++; + count++; + } + *pStarting = (CHAR)count; + + if (*pSrc == '\0' || *pSrc == ',') + OneMoreSubfield = 0; + else + pSrc++; + } + } + + *pDest++ = 0; + + + // return the address of the next byte of the destination + return(pDest); +} + + + + +//---------------------------------------------------------------------------- +VOID +DnsExtractName( + IN PCHAR pNameHdr, + IN LONG NumBytes, + OUT PCHAR pName, + OUT PULONG pNameSize + ) +/*++ + +Routine Description: + + This routine extracts the name from the packet and then appends the scope + onto the end of the name to make a full name. + +Arguments: + NumBytes - the total number of bytes in the message - may include + more than just the name itself + +Return Value: + + +--*/ +{ + + + LONG i; + int iIndex; + LONG lValue; + ULONG UNALIGNED *pHdr; + PCHAR pSavName; + ULONG Len; + + + KdPrint(("DnsExtractName entered\r\n")); + + // + // how long is the name we received + // + Len = (ULONG)((UCHAR)*pNameHdr); + + ++pNameHdr; // to increment past the length byte + + pSavName = pName; + + // copy the name (no domain) as given by DNS server (i.e., just copy + // foobar when DNS returned foobar.microsoft.com in the response + // (this is likely to be less than the usualy 16 byte len) + // + for (i=0; i < Len ;i++ ) + { + *pName = *pNameHdr; + pNameHdr++; + if (i < NETBIOS_NAME_SIZE) + { + pName++; + } + } + + // + // now, make it look like NBNS responded, by adding the 0x20 pad + // + for (i=Len; i<NETBIOS_NAME_SIZE; i++) + { + *pName++ = 0x20; + } + + // + // convert all chars to uppercase since all our names are in uppercase! + // + for (i=0; i<NETBIOS_NAME_SIZE; i++) + { + if (*pSavName >= 'a' && *pSavName <= 'z') + *pSavName = *pSavName - ('a'-'A'); + + pSavName++; + } + + // + // at this point we are pointing to the '.' after foobar. Find the + // length of the entire name + // + while ( (*pNameHdr != '\0') && (Len < NumBytes) ) + { + pNameHdr++; + Len++; + } + + Len++; // to account for the trailing 0 + + *pNameSize = Len; + + KdPrint(("Leaving DnsExtractName\r\n")); + + return; +} + + +//---------------------------------------------------------------------------- +ULONG +domnamelen( + IN PCHAR pDomainName + ) +/*++ + +Routine Description: + + This routine determines the length of the domainname. This is basically + strlen, except that the DNSDomain field is stored as a bunch of + domain names separated by commas, so we treat '\0' as well as ',' as + string terminators for this function. + +Arguments: + + +Return Value: + + length of the domain name + +--*/ +{ + + ULONG ulDomnameLen=0; + + if (pDomainName) + { + while(*pDomainName != '\0' && *pDomainName != ',') + { + pDomainName++; + ulDomnameLen++; + } + } + + return( ulDomnameLen ); +} + +//---------------------------------------------------------------------------- +VOID +ProcessDnsResponse( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ) +/*++ + +Routine Description: + + This function sets the state of the name being resolved appropriately + depending on whether DNS sends a positive or a negative response to our + query; calls the client completion routine and stops any more DNS queries + from going. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL + +--*/ +{ + + + NTSTATUS status; + tDNS_QUERYRESP UNALIGNED *pQuery; + tNAMEADDR *pResp; + tTIMERQENTRY *pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SrcAddress; + CTELockHandle OldIrq1; + LONG lNameSize; + LONG lTraversedSoFar=0; + CHAR pName[NETBIOS_NAME_SIZE]; + CHAR pJunkBuf[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + PUCHAR pchQry; + + + + KdPrint(("ProcessDnsResponse entered\r\n")); + + + // make sure this is a response + + if ( !(OpCodeFlags & OP_RESPONSE) ) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: Bad OpCodeFlags\r\n")); + + return; + } + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + + // get the name out of the network pdu and pass to routine to check + // local table + DnsExtractName( (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pName, + &lNameSize + ); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // + // we chopped off 16th byte while sending a query, so compare only first + // 15 characters for a match + // + status = FindOnPendingList(pName,pNameHdr,FALSE,NETBIOS_NAME_SIZE-1,&pResp); + + if (!NT_SUCCESS(status)) + { + // + // The name is not there in the remote name table. Nothing + // more to do. Just return. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: name not found\r\n")); + + return; + } + + // + // If the response we received doesn't resolve the name, we silently return, + // but make sure reties is set to 1 so that when timer fires again, we don't + // send another name query to the same server but instead timeout the + // attempt on this server + // + if ((pTimer = pResp->pTimer)) + { + pTimer->Retries = 1; + } + + + // + // check the pdu size for errors + // + if (lNumBytes < DNS_MINIMUM_QUERYRESPONSE) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: Bad lNumBytes\r\n")); + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return; + } + +// +// BUGBUG: should we require authoritative responses from DNS servers? +// + + // + // if it's a negative response, quit now! + // + if (IS_NEG_RESPONSE(OpCodeFlags)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; + } + + // + // if there is no answer section, return! + // + if ( !pNameHdr->AnCount ) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: No answer section\r\n")); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; + } + + // + // lNameSize is the length of the entire name, excluding the length byte + // for the first label (including length bytes of subsequent labels) and + // including the trailing 0 (tNAMEHDR struc takes care for 1st byte) + // + pchQry = (PUCHAR)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + lTraversedSoFar += lNameSize; + + // + // if the Question section is returned with the response then we have + // a little more work to do! In this case, pQuery is pointing at the + // beginning of the QTYPE field (end of the QNAME) + // + if ( pNameHdr->QdCount ) + { + pchQry += sizeof(tQUESTIONMODS); + lTraversedSoFar += sizeof(tQUESTIONMODS); + + // most common case: 1st byte will be 0xC0, which means next byte points + // to the actual name. We don't care about the name, so we skip over + // both the bytes + // + if ( (*pchQry) == PTR_TO_NAME ) + { + pchQry += sizeof(tDNS_LABEL); + lTraversedSoFar += sizeof(tDNS_LABEL); + } + + // + // if some implementation doesn't optimize and copies the whole name + // again, skip over the length of the name + // + else + { + pchQry += (lNameSize+1); // +1 because of the 1st length byte! + lTraversedSoFar += (lNameSize+1); + } + } + + pQuery = (tDNS_QUERYRESP *)pchQry; + + // + // if this rr is telling us about canonical name, skip over it and go to + // where the ipaddr is + // + if (ntohs(pQuery->RrType) == DNS_CNAME) + { + // + // since this is CNAME, there is no ipaddr. Instead, the data is the + // canonical name whose length we are adding, and subtract ipaddr's len + // + pchQry += (sizeof(tDNS_QUERYRESP) - sizeof(ULONG)); + pchQry += ntohs(pQuery->Length); + lTraversedSoFar += ntohs(pQuery->Length) + sizeof(tDNS_QUERYRESP) - sizeof(ULONG); + + ASSERT(lNumBytes > lTraversedSoFar); + + // most common case: 1st byte will be 0xC0, which means next byte points + // to the actual name. We don't care about the name, so we skip over + // both the bytes + // + if ( (*pchQry) == PTR_TO_NAME ) + { + pchQry += sizeof(tDNS_LABEL); + lTraversedSoFar += sizeof(tDNS_LABEL); + } + + // + // if some implementation doesn't optimize and copies the whole name + // again, skip over the length of the name + // + else + { + // we have already taken the name out. we are calling this routine + // just to see how big the canonical name is (i.e.lNameSize), to skip + // past it + // + DnsExtractName( pchQry, + lNumBytes-lTraversedSoFar, + pJunkBuf, + &lNameSize + ); + + // + // lNameSize is the length of the entire name, excluding the length byte + // for the first label (including length bytes of subsequent labels) and + // including the trailing 0 (tNAMEHDR struc takes care for 1st byte) + // + pchQry += lNameSize+1; // +1 for the length byte of first label + + } + + pQuery = (tDNS_QUERYRESP *)pchQry; + } + + + // if we came this far, it's a positive response. stop the timer and do + // the needful.. + + // remove any timer block and call the completion routine + if (pTimer) + { + USHORT Flags; + tDGRAM_SEND_TRACKING *pTracker; + + pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; + + // + // this routine puts the timer block back on the timer Q, and + // handles race conditions to cancel the timer when the timer + // is expiring. + status = StopTimer(pTimer,&pClientCompletion,&Context); + + // + // Synchronize with DnsCompletion + // + if (pClientCompletion) + { + CHECK_PTR(pResp); + pResp->pTimer = NULL; + + // + // Remove from the PendingNameQueries List + // + RemoveEntryList(&pResp->Linkage); + InitializeListHead(&pResp->Linkage); + + KdPrint(("ProcessDnsResponse: positive DNS response received\r\n")); + + if (pResp->NameTypeState & STATE_RESOLVING) + { + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_RESOLVED; + + pResp->IpAddress = ntohl(pQuery->IpAddress); + + pResp->AdapterMask = (CTEULONGLONG)-1; + status = AddRecordToHashTable(pResp,NbtConfig.pScope); + + if (!NT_SUCCESS(status)) + { + // + // the name must already be in the hash table, + // so dereference it to remove it + // + NbtDereferenceName(pResp); + } + + IncrementNameStats(NAME_QUERY_SUCCESS, TRUE); + } + + status = STATUS_SUCCESS; + + // + // Set the backup name server to be the main name server + // since we got a response from it. + // + if ( SrcAddress == pDeviceContext->lDnsBackupServer ) + { + pDeviceContext->lDnsBackupServer = + pDeviceContext->lDnsServerAddress; + + pDeviceContext->lDnsServerAddress = SrcAddress; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // the completion routine has not run yet, so run it + (void) CTEQueueForNonDispProcessing( + (tDGRAM_SEND_TRACKING *)Context, + (PVOID)status, + pClientCompletion, + DelayedSessEstablish, + pDeviceContext); + } + + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + + return; + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + KdPrint(("Leaving ProcessDnsResponse\r\n")); + + return; + +} diff --git a/private/ntos/nbt/vxd/fileio.c b/private/ntos/nbt/vxd/fileio.c new file mode 100644 index 000000000..b319ce290 --- /dev/null +++ b/private/ntos/nbt/vxd/fileio.c @@ -0,0 +1,476 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fileio.c + +Abstract: + + This source implements a stdio-like facility. + +Author: + + Eric Chin (ericc) April 28, 1992 + John Ludeman (johnl) Oct 8, 1993 - Rewrote for Vxd + +Revision History: + +--*/ + +#include <nbtprocs.h> +#include "hosts.h" + +// +// The maximum line length for a file in the lmhosts file is 256 bytes for +// the Vxd +// +#define MAX_LMHOSTS_LINE 256 + +// +// The number of bytes we buffer on each read +// +#define LMHOSTS_READ_BUFF_SIZE 256 + +UCHAR GetNextChar( PLM_FILE pFile ) ; + +VOID RestoreOldData( PLM_FILE pFile ); + +BackupCurrentData( PLM_FILE pFile ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, LmCloseFile) +#pragma CTEMakePageable(PAGE, LmFgets) +#pragma CTEMakePageable(PAGE, GetNextChar) +#pragma CTEMakePageable(PAGE, LmOpenFile) +#pragma CTEMakePageable(PAGE, BackupCurrentData) +#pragma CTEMakePageable(PAGE, RestoreOldData) +#endif + +#ifdef CHICAGO +// +// In case of chicago, use only linear addresses (Allocate_Global_V86_Data_Area +// call is available only at init time, not if vnbt is load dynamically!) +// +#define pMappedFileBuff pFileBuff +#define pMappedFilePath pFilePath + +#else +// +// In case of snowball, this is the address of the V86 mapped memory for +// the file read buffer and lmhosts file path +// +PVOID pMappedFileBuff = NULL ; +PVOID pMappedFilePath = NULL ; +#endif + +// +// Linear address for file buffer and path (accessible from Vxd) +// +PUCHAR pFileBuff = NULL ; +PUCHAR pFilePath = NULL ; + + +/******************************************************************* + + NAME: VxdInitLmHostsSupport + + SYNOPSIS: This function just allocates memory to read the contents + of file into. + (trying to minimize on changes to snowball side of code + whic has already shipped: that's why this function!) + + ENTRY: pchLmHostPath - path to lmhosts file (not used here) + ulPathSize - size, in chars, of the path + + RETURNS: TRUE if it works, FALSE if it doesn't. + COMMENTS: This is Chicago version of the function. Snowball's + version is in vxdfile.asm + + HISTORY: + Koti Oct 10, 94 + +********************************************************************/ + +#ifdef CHICAGO +BOOL +VxdInitLmHostsSupport( PUCHAR pchLmHostPath, USHORT ulPathSize ) +{ + + USHORT Size; + + Size = ulPathSize + LMHOSTS_READ_BUFF_SIZE; + + pFileBuff = CTEAllocInitMem( Size ); + if (pFileBuff == NULL) + { + DbgPrint("VxdInitLmHostsSupport: failed to allocate memory") ; + return( FALSE ); + } + + pFilePath = pFileBuff + LMHOSTS_READ_BUFF_SIZE; + + return( TRUE ); + +} + +#endif +//---------------------------------------------------------------------------- + +NTSTATUS +LmCloseFile ( + IN PLM_FILE pfile + ) + +/*++ + +Routine Description: + + This function closes a file opened via LmOpenFile(), and frees its + LM_FILE object. + +Arguments: + + pfile - pointer to the LM_FILE object + +Return Value: + + An NTSTATUS value. + +--*/ + + +{ + CTEPagedCode(); + + CDbgPrint(DBGFLAG_LMHOST, "LmCloseFile entered\r\n") ; + CTEFreeMem( pfile->f_linebuffer ); + + VxdFileClose( pfile->f_handle ) ; + + CTEFreeMem(pfile); + + CDbgPrint(DBGFLAG_LMHOST, "LmCloseFile leaving\r\n") ; + return STATUS_SUCCESS ; + +} // LmCloseFile + + + +//---------------------------------------------------------------------------- + +PUCHAR +LmFgets ( + IN PLM_FILE pfile, + OUT int *nbytes + ) + +/*++ + +Routine Description: + + This function is vaguely similar to fgets(3). + + Starting at the current seek position, it reads through a newline + character, or the end of the file. If a newline is encountered, it + is replaced with a NULL character. + +Arguments: + + pfile - file to read from + nbytes - the number of characters read, excluding the NULL character + +Return Value: + + A pointer to the beginning of the line, or NULL if we are at or past + the end of the file. + +--*/ +{ + ULONG cbLine = 0 ; + UCHAR ch ; + BOOL fDone = FALSE ; + BOOL fEOL = FALSE ; + + + CTEPagedCode(); + + while ( TRUE ) + { + switch ( ch = GetNextChar( pfile )) + { + case '\n': // End of line + if ( !cbLine ) // If it's just a '\n' by itself, ignore it + continue ; + // + // Fall through + // + + case '\0': // End of file + pfile->f_linebuffer[cbLine] = '\0' ; + fDone = TRUE ; + fEOL = TRUE ; + break ; + + case '\r': // Ignore + continue ; + + default: + pfile->f_linebuffer[cbLine] = ch ; + if ( cbLine == (MAX_LMHOSTS_LINE-1) ) + { + pfile->f_linebuffer[cbLine--] = '\0' ; + fDone = TRUE ; + } + break ; + } + + if ( fDone ) + break ; + + cbLine++ ; + } + + // + // Scan till the end of this line + // + if ( !fEOL ) + { + while ( (ch = GetNextChar(pfile)) && ch != '\n' ) + ; + } + + if ( cbLine ) + { + (pfile->f_lineno)++ ; + *nbytes = cbLine ; + + CDbgPrint( DBGFLAG_LMHOST, "LmFgets returning \"") ; + CDbgPrint( DBGFLAG_LMHOST, pfile->f_linebuffer ) ; + CDbgPrint( DBGFLAG_LMHOST, "\", nbytes = 0x") ; + CDbgPrintNum( DBGFLAG_LMHOST, *nbytes ) ; + CDbgPrint( DBGFLAG_LMHOST, "\r\n") ; + + return pfile->f_linebuffer ; + } + + return NULL ; +} + +/******************************************************************* + + NAME: GetNextChar + + SYNOPSIS: Gets the next character from the file or the line buffer + + ENTRY: pFile - File we are operating on + + RETURNS: Next character or '\0' if at the end of file (or there is an + embedded '\0' in the file). + + NOTES: + +********************************************************************/ + +UCHAR GetNextChar( PLM_FILE pFile ) +{ + ULONG BytesRead ; + + CTEPagedCode(); + + if ( pFile->f_CurPos < pFile->f_EndOfData ) + return pFile->f_buffer[pFile->f_CurPos++] ; + + if ( pFile->f_EOF ) + return '\0' ; + + // + // We've reached the end of the buffer, get more data + // + BytesRead = VxdFileRead( pFile->f_handle, + LMHOSTS_READ_BUFF_SIZE, + pMappedFileBuff ) ; + pFile->f_CurPos = 0 ; + if ( BytesRead < LMHOSTS_READ_BUFF_SIZE ) + pFile->f_EOF = TRUE ; + + // + // If haven't hit the end of the file, return the next character + // + if ( (pFile->f_EndOfData = BytesRead) ) + return pFile->f_buffer[pFile->f_CurPos++] ; + + return '\0' ; +} + + + +//---------------------------------------------------------------------------- + +PLM_FILE +LmOpenFile ( + IN PUCHAR path + ) + +/*++ + +Routine Description: + + This function opens a file for use by LmFgets(). + +Arguments: + + path - a fully specified, complete path to the file. + +Return Value: + + A pointer to an LM_FILE object, or NULL if unsuccessful. + +Notes: + + The first time through, we map the lmhosts memory to vm memory and + allocate a read buffer and map that to VM memory. Note that this means + the path must not change and this routine is not reentrant!! + + The reason for this is because the mapping is an expensive operation + (and there isn't a way to unmap when using Map_Lin_To_VM_Addr). + +--*/ + + +{ + HANDLE handle; + PLM_FILE pfile; + PCHAR pLineBuff = CTEAllocMem( MAX_LMHOSTS_LINE ) ; + static int fInRoutine = 0 ; + + CTEPagedCode(); + + + if (fInRoutine++) + { + CDbgPrint( DBGFLAG_LMHOST, "exiting LmOpenFile: not reentrant!\r\n") ; + goto ErrorExit; // We're not reentrant + } + + CDbgPrint( DBGFLAG_LMHOST, "LmOpenFile entered\r\n") ; + + strcpy( pFilePath, path ) ; + + if ( !pLineBuff || !pFileBuff ) + goto ErrorExit ; + + handle = (HANDLE) VxdFileOpen( pMappedFilePath ) ; + + if ( handle == NULL ) + { + goto ErrorExit ; + } + + pfile = (PLM_FILE) CTEAllocMem( sizeof(LM_FILE) ); + + if (!pfile) + { + VxdFileClose( handle ) ; + goto ErrorExit ; + } + + pfile->f_handle = handle; + pfile->f_lineno = 0; + pfile->f_buffer = pFileBuff ; + pfile->f_linebuffer = pLineBuff ; + pfile->f_EndOfData = 0 ; + pfile->f_CurPos = 0 ; + pfile->f_EOF = FALSE ; + + CDbgPrint( DBGFLAG_LMHOST, "LmOpenFile returning\r\n") ; + + fInRoutine-- ; + + return pfile ; + +ErrorExit: + + fInRoutine--; + + if ( pLineBuff ) + CTEFreeMem( pLineBuff ) ; + + return NULL ; + +} // LmOpenFile + + +//---------------------------------------------------------------------------- + +BOOL +BackupCurrentData( PLM_FILE pFile ) + +/*++ + +Routine Description: + + This function backs up all the data from lmhosts file into another buffer + (which is allocated). + This function is called before opening the next file that we encountered + via #INCLUDE. Since the same buffer is used to store the VxdReadFile data, + we need to save this data. + +Arguments: + + pFile - LMfile pointer + +Return Value: + + TRUE if everything went ok + FALSE if memory couldn't be allocated + +--*/ +{ + + CTEPagedCode(); + + pFile->f_BackUp = CTEAllocMem( MAX_LMHOSTS_LINE ) ; + if (pFile->f_BackUp == NULL ) + { + return( FALSE ); + } + + CTEMemCopy( pFile->f_BackUp, pFile->f_buffer, LMHOSTS_READ_BUFF_SIZE ); + + return( TRUE ); +} + + +//---------------------------------------------------------------------------- + +VOID RestoreOldData( PLM_FILE pFile ) +/*++ + +Routine Description: + + This function restores all the data we backed up in BackupCurrentData + +Arguments: + + pFile - LMfile pointer + +Return Value: + + TRUE if everything went ok + FALSE if memory couldn't be allocated + +--*/ +{ + + CTEPagedCode(); + + CTEMemCopy( pFile->f_buffer, pFile->f_BackUp, LMHOSTS_READ_BUFF_SIZE ); + + CTEFreeMem( pFile->f_BackUp ) ; + +} + + diff --git a/private/ntos/nbt/vxd/limit.txt b/private/ntos/nbt/vxd/limit.txt new file mode 100644 index 000000000..726b7c7d7 --- /dev/null +++ b/private/ntos/nbt/vxd/limit.txt @@ -0,0 +1,51 @@ + +============================================================================ + NBT Limitations +============================================================================ + +This document lists the currently known limitations of the NBT Vxd. + +* The following Netbios commands are not supported: + + NCBENUM + NCBLANSTALERT + NCBACTION + + NCBSEND_RCVANY (Transceive) - Not necessary to support + +* Permanent adapter name may not have all of its listens & calls on cleaned + up on reset + +* NCBCANCEL only supports cancelling the following NCBs: + NCBRECV + NCBDGRECV + NCBDGRECVBC + NCBRECVANY + NCBLISTEN + NCBSEND + NCBSENDNA + NCBCHAINSEND + NCBCHAINSENDNA + NCBCALL + +* Call NCB opens a connection which will immediately be closed by NbtConnect. + Need to pass a flag (or NULL connect element) indicating we haven't set up + an inbound connection because we don't need to in this case. + +* On remote disconnects, if a send was just submitted, we may have to timeout + the TdiDisconnect because the FYN ACK may never get submitted (queued behind + send which can't complete because session is down). + +* Would be good to keep queue of preallocated buffers for Delayed calls + +* Name table numbers may not match in ASTAT command (Not OS code assigns + numbers based on the hashtable, the Vxd needs to use its name table + +* Extended lowercase characters in lmhosts file may not be upper cased + correctly (and thus not be usable). Need better upper case code. + +* If a program tries to exec or open a UNC that requires the LMHosts file, + nbt may not be able to satisfy the request because the InDos flag will + be set, thus nbt will not be able to read the lmhosts file. Normally + it will reschedule the read for some later time, but that will not work + in this instance. It retries 10 times then times out the request. diff --git a/private/ntos/nbt/vxd/makefile b/private/ntos/nbt/vxd/makefile new file mode 100644 index 000000000..a5c9e15c5 --- /dev/null +++ b/private/ntos/nbt/vxd/makefile @@ -0,0 +1,28 @@ +# +#:ts=4 +# Makefile for the NBT component +# +ROOTDIR=.. +!include rules.mk + +all: svnbt svnbtd + +nodebug: svnbt cvnbt + +debug: svnbtd cvnbtd + +# +# Don't build Windows 95 version from this tree any more. +# Use QFE tree for Windows 95 bug fixes. +# Use makefiles in VXD.000 directory for post-Windows 95 +# versions. +# +#all: svnbt svnbtd cvnbt cvnbtd +# +#nodebug: svnbt cvnbt +# +#debug: svnbtd cvnbtd +# + +!include vnbtd.mk +!include depend.mk diff --git a/private/ntos/nbt/vxd/nbtinfo.c b/private/ntos/nbt/vxd/nbtinfo.c new file mode 100644 index 000000000..ad267685f --- /dev/null +++ b/private/ntos/nbt/vxd/nbtinfo.c @@ -0,0 +1,227 @@ +/**********************************************************************/ +/** Microsoft Windows/NT **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + Nbtinfo.c + + This file contains the NBT Info APIs + + + + FILE HISTORY: + Johnl 13-Dec-1993 Created + +*/ + + +#include <nbtprocs.h> +#include <dhcpinfo.h> +#include <nbtinfo.h> + +/******************************************************************* + + NAME: AddrChngNotification + + SYNOPSIS: Notification handler called by Dhcp when an IpAddress + lease has expired or changed. + + ENTRY: Context - Pointer to device context + OldIpAddress - in network order + NewIpAddress - in network order + NewMask - in network order + + NOTES: + + HISTORY: + Johnl 21-Dec-1993 Created + +********************************************************************/ + +VOID AddrChngNotification( PVOID Context, + ULONG OldIpAddress, + ULONG NewIpAddress, + ULONG NewMask ) +{ + tDEVICECONTEXT * pDeviceContext = (tDEVICECONTEXT*) Context ; + TDI_STATUS tdistatus ; + NTSTATUS status ; + ULONG IpBuff[4] ; + UINT Size ; + ULONG TmpNodeType; + + DbgPrint("DhcpNotification: Nbt being notified of IP Address change by DHCP\r\n") ; + + // + // NBT assumes the address goes to zero then comes up on the new + // address, so if the address is going to a new address (not to + // zero first) then fake it. + // + + if ( NewIpAddress && pDeviceContext->IpAddress ) + { + if ( status = NbtNewDhcpAddress( pDeviceContext, 0, 0 ) ) + { + CDbgPrint( DBGFLAG_ERROR, ("DhcpNotification: NbtSetNewDhcpAddress failed")) ; + } + } + + if ( NewIpAddress == 0 ) + { + if ( status = NbtNewDhcpAddress( pDeviceContext, 0, 0 ) ) + { + CDbgPrint( DBGFLAG_ERROR, ("DhcpNotification: NbtSetNewDhcpAddress failed")) ; + } + pDeviceContext->IpAddress = 0 ; + return ; + } + + // + // Get all of the values that may change when the IP address changes. + // Currently this is only NBNS (scope & broadcast address are global + // NBT config parameters). + // + + Size = sizeof( IpBuff ) ; + tdistatus = DhcpQueryOption( NewIpAddress, + 44, // NBNS + IpBuff, + &Size ) ; + + if ( tdistatus != TDI_SUCCESS && + tdistatus != TDI_BUFFER_OVERFLOW ) + { + CDbgPrint( DBGFLAG_ERROR, ("DhcpNotification: Query on NBNS failed")) ; + } + else + { + if ( Size >= 4 ) + pDeviceContext->lNameServerAddress = ntohl(IpBuff[0]) ; + + if ( Size >= 8 ) + pDeviceContext->lBackupServer = ntohl(IpBuff[1]) ; + } + + // + // if the node type is set to Bnode by default then switch to Hnode if + // there are any WINS servers configured. + // + TmpNodeType = NodeType; + + if ((NodeType & DEFAULT_NODE_TYPE) && + (pDeviceContext->lNameServerAddress || pDeviceContext->lBackupServer)) + { + NodeType = MSNODE; + if (TmpNodeType & PROXY) + NodeType |= PROXY; + } + + // + // Now set the new IP address + // + + status = NbtNewDhcpAddress( pDeviceContext, + NewIpAddress, + NewMask ) ; + + if ( NT_SUCCESS(status) ) + { + if (pDeviceContext->IpAddress) + { + // + // Add the "permanent" name to the local name table. + // + status = NbtAddPermanentName(pDeviceContext); + + if (!(NodeType & BNODE)) + { + // the Ip address just changed and Dhcp may be informing + // us of a new Wins Server addresses, so refresh all the + // names to the new wins server + // + ReRegisterLocalNames(); + } + else + { + // + // no need to refresh on a Bnode + // + LockedStopTimer(&NbtConfig.pRefreshTimer); + } + } + } + + else + { + CDbgPrint( DBGFLAG_ERROR, ("DhcpNotification: NbtSetNewDhcpAddress failed")) ; + } + + + +} + + +/******************************************************************* + + NAME: CloseAddressesWithTransport + + SYNOPSIS: Closes address objects on the passed in device + + ENTRY: pDeviceContext - Device context to close + + NOTES: Used after an IP address loses its DHCP lease by OS + independent code. + + HISTORY: + Johnl 13-Dec-1993 Created + +********************************************************************/ + +NTSTATUS +CloseAddressesWithTransport( + IN tDEVICECONTEXT *pDeviceContext ) +{ + TDI_REQUEST Request ; + NTSTATUS status; + + + if (pDeviceContext->pDgramFileObject) + { + Request.Handle.AddressHandle = pDeviceContext->pDgramFileObject ; + if ( TdiVxdCloseAddress( &Request )) + CDbgPrint( DBGFLAG_ERROR, ("NbtSetInfo: Warning - CloseAddress Failed\r\n")) ; + pDeviceContext->pDgramFileObject = NULL; + } + + if (pDeviceContext->pNameServerFileObject) + { + Request.Handle.AddressHandle = pDeviceContext->pNameServerFileObject ; + if ( TdiVxdCloseAddress( &Request )) + CDbgPrint( DBGFLAG_ERROR, ("NbtSetInfo: Warning - CloseAddress Failed\r\n")) ; + pDeviceContext->pNameServerFileObject = NULL; + } + + if (pDeviceContext->pSessionFileObject) + { + Request.Handle.AddressHandle = pDeviceContext->pSessionFileObject ; + if ( TdiVxdCloseAddress( &Request )) + CDbgPrint( DBGFLAG_ERROR, ("NbtSetInfo: Warning - CloseAddress Failed\r\n")) ; + pDeviceContext->pSessionFileObject = NULL; + } + + if (pDeviceContext->hBroadcastAddress) + { + Request.Handle.ConnectionContext = pDeviceContext->hBroadcastAddress ; + status = NbtCloseAddress( &Request, NULL, pDeviceContext, NULL ); + if ( !NT_SUCCESS(status) ) + { + CDbgPrint( DBGFLAG_ERROR, ("NbtSetInfo: Warning - Close Broadcast Address Failed\r\n")) ; + ASSERT(0); + } + } + + return STATUS_SUCCESS ; +} + + diff --git a/private/ntos/nbt/vxd/ncb.c b/private/ntos/nbt/vxd/ncb.c new file mode 100644 index 000000000..534a822e6 --- /dev/null +++ b/private/ntos/nbt/vxd/ncb.c @@ -0,0 +1,529 @@ +/**********************************************************************/ +/** Microsoft Windows/NT **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + NCB.c + + This file contains the NCB Handler that the VNetBios driver calls + + FILE HISTORY: + Johnl 25-Mar-1993 Created + +*/ + +#include <nbtprocs.h> +#include <debug.h> + +#ifdef CHICAGO + +#include <shell.h> + +#include <netvxd.h> + +// +// Do this so the VXDINLINE in the header file doesn't conflict +// with the actual function declaration in this file. +// +#define VNBT_NCB_X VNBT_NCB_X_CALL +#define VNBT_LANA_MASK VNBT_LANA_MASK_CALL +#include <vnbt.h> +#undef VNBT_LANA_MASK +#undef VNBT_NCB_X + +#endif ;; CHICAGO + +LANA_ENTRY LanaTable[NBT_MAX_LANAS] ; + +/******************************************************************* + + NAME: VNBT_NCB_X + + SYNOPSIS: All NCBs submitted by the VNetBios driver come through + here + + ENTRY: pNCB - Pointer to submitted NCB + Ipaddr - this parm is used only by nbtstat -A, which directly + calls into VNBT_NCB_X + ipaddress to which to send AdapterStatus to + + RETURNS: NCB Return code + + NOTES: + + HISTORY: + Johnl 25-Mar-1993 Created + +********************************************************************/ + +ULONG +_stdcall +VNBT_NCB_X( PNCB pNCB, + PUCHAR pzDnsName, + PULONG pIpAddress, + PVOID pExtended, + ULONG fFlag ) +{ + BOOL fAsync ; + tDEVICECONTEXT * pDeviceContext = NULL ; + NTSTATUS status = STATUS_SUCCESS ; + uchar errNCB = NRC_GOODRET ; + PBLOCKING_NCB_CONTEXT pBlkNcbContext; + ULONG Ipaddr = pIpAddress ? pIpAddress[0] : 0; + + if ( !pNCB ) + return NRC_INVADDRESS ; + + pDeviceContext = GetDeviceContext( pNCB ) ; + if ( pDeviceContext == NULL ) + return NRC_BRIDGE ; + + if (!pDeviceContext->fDeviceUp) + return NRC_BRIDGE ; + + fAsync = !!(pNCB->ncb_command & ASYNCH) ; + + if ( + ( pzDnsName != NULL ) + && ( pIpAddress != NULL ) + ) + { + if ( fAsync ) + { + return DoDnsResolveDirect( pNCB, pzDnsName, pIpAddress ); + } + else + { + return (pNCB->ncb_retcode = pNCB->ncb_cmd_cplt = NRC_ILLCMD); + } + } + else if ( + ( pIpAddress != NULL ) + && ( Ipaddr != 0 ) + && ( ( pNCB->ncb_command & ~ASYNCH ) != NCBASTAT ) + ) + { + IpToAscii( Ipaddr, &pNCB->ncb_callname[0] ); + } + + if ( !fAsync ) + { + pBlkNcbContext = CTEAllocMem( sizeof(BLOCKING_NCB_CONTEXT) ); + if (!pBlkNcbContext) + { + DbgPrint("VNBT_NCB_X: couldn't alloc pBlkNcbContext 1") ; + return NRC_NORESOURCES; + } + + pBlkNcbContext->Verify = NBT_VERIFY_BLOCKING_NCB; + InitializeListHead(&pBlkNcbContext->Linkage); + pBlkNcbContext->pNCB = pNCB; + + pBlkNcbContext->pWaitNCBBlock = CTEAllocMem( sizeof(CTEBlockStruc) ); + if (!pBlkNcbContext->pWaitNCBBlock) + { + CTEFreeMem(pBlkNcbContext); + DbgPrint("VNBT_NCB_X: couldn't alloc pBlkNcbContext 2") ; + return NRC_NORESOURCES; + } + + pBlkNcbContext->fNCBCompleted = FALSE ; + + // + // The completion routine uses this flag to know if the thread is + // blocked and needs to be signaled. + // + pBlkNcbContext->fBlocked = FALSE; + + InsertTailList(&NbtConfig.BlockingNcbs,&pBlkNcbContext->Linkage); + } + + DbgPrint("VNBT_NCB_X: NCB Commmand Rcvd: 0x") ; + DbgPrintNum( pNCB->ncb_command ) ; DbgPrint(", (") ; + DbgPrintNum( (ULONG) pNCB ) ; DbgPrint(")\r\n") ; + + pNCB->ncb_retcode = NRC_PENDING ; + pNCB->ncb_cmd_cplt = NRC_PENDING ; + + switch ( pNCB->ncb_command & ~ASYNCH ) + { + case NCBDGSEND: + case NCBDGSENDBC: + status = VxdDgramSend( pDeviceContext, pNCB ) ; + errNCB = MapTDIStatus2NCBErr( status ) ; + break ; + + case NCBDGRECV: + case NCBDGRECVBC: + errNCB = VxdDgramReceive( pDeviceContext, pNCB ) ; + break ; + + case NCBRECVANY: + errNCB = VxdReceiveAny( pDeviceContext, pNCB ) ; + break ; + + case NCBCALL: + errNCB = VxdCall( pDeviceContext, pNCB ) ; + break ; + + case NCBHANGUP: + errNCB = VxdHangup( pDeviceContext, pNCB ) ; + break ; + + case NCBLISTEN: + errNCB = VxdListen( pDeviceContext, pNCB ) ; + break ; + + case NCBRECV: + errNCB = VxdReceive( pDeviceContext, pNCB, TRUE ) ; + break ; + + case NCBSEND: + case NCBSENDNA: + case NCBCHAINSEND: + case NCBCHAINSENDNA: + errNCB = VxdSend( pDeviceContext, pNCB ) ; + break ; + +#if 0 + case NCBTRANSV: + errNCB = VxdTransceive( pDeviceContext, pNCB ) ; + break ; +#endif + + case NCBADDGRNAME: + case NCBADDNAME: + errNCB = VxdOpenName( pDeviceContext, pNCB ) ; + break ; + + case NCBDELNAME: + errNCB = VxdCloseName( pDeviceContext, pNCB ) ; + break ; + + case NCBASTAT: + errNCB = VxdAdapterStatus( pDeviceContext, pNCB, Ipaddr ) ; + break ; + + case NCBSSTAT: + errNCB = VxdSessionStatus( pDeviceContext, pNCB ) ; + break ; + + case NCBFINDNAME: + errNCB = VxdFindName( pDeviceContext, pNCB ) ; + break ; + + case NCBRESET: + errNCB = VxdReset( pDeviceContext, pNCB ) ; + break ; + + case NCBCANCEL: + if ( DoDnsCancelDirect( pNCB ) ) + { + errNCB = NRC_GOODRET; + } + else + { + errNCB = VxdCancel( pDeviceContext, pNCB ) ; + } + break ; + + // + // The following are no-ops that return success for compatibility + // + case NCBUNLINK: + case NCBTRACE: + CTEIoComplete( pNCB, STATUS_SUCCESS, 0 ) ; + break ; + + default: + DbgPrint("VNBT_NCB_X - Unsupported command: ") ; + DbgPrintNum( pNCB->ncb_command & ~ASYNCH ) ; + DbgPrint("\n\r") ; + errNCB = NRC_ILLCMD ; // Bogus error for now + break ; + } + +Exit: + // + // If we aren't pending then set the codes + // + if ( errNCB != NRC_PENDING && + errNCB != NRC_GOODRET ) + { +#ifdef DEBUG + DbgPrint("VNBT_NCB_X - Returning ") ; + DbgPrintNum( errNCB ) ; + DbgPrint(" to NCB submitter\n\r") ; +#endif + pNCB->ncb_retcode = errNCB ; + pNCB->ncb_cmd_cplt = errNCB ; + + // + // Errored NCBs don't have the completion routine called, so we + // in essence, complete it here. Note this will only set the + // state for the last Wait NCB (all others get NRC_IFBUSY). + // + if ( !fAsync ) + { + ASSERT(pBlkNcbContext->Verify == NBT_VERIFY_BLOCKING_NCB); + pBlkNcbContext->fNCBCompleted = TRUE ; + } + } + else + { + // + // Some components (AKA server) don't like returning pending + // + errNCB = NRC_GOODRET ; + + } + + // + // Block until NCB completion if this wasn't an async NCB + // + if ( !fAsync ) + { + ASSERT(pBlkNcbContext->Verify == NBT_VERIFY_BLOCKING_NCB); + if ( !pBlkNcbContext->fNCBCompleted ) + { + pBlkNcbContext->fBlocked = TRUE; + CTEInitBlockStruc( pBlkNcbContext->pWaitNCBBlock ) ; + CTEBlock( pBlkNcbContext->pWaitNCBBlock ) ; + } + else + { + RemoveEntryList(&pBlkNcbContext->Linkage); + CTEFreeMem(pBlkNcbContext->pWaitNCBBlock); + CTEFreeMem(pBlkNcbContext); + } + } + + return errNCB ; +} + +/******************************************************************* + + NAME: GetDeviceContext + + SYNOPSIS: Retrieves the device context associated with the lana + specified in the NCB + + ENTRY: pNCB - NCB to get the device context for + + RETURNS: Device context or NULL if not found + + NOTES: It is assumed that LanaTable is filled sequentially + with no holes. + + HISTORY: + Johnl 30-Aug-1993 Created + +********************************************************************/ + +tDEVICECONTEXT * GetDeviceContext( NCB * pNCB ) +{ + int i ; + + if ( !pNCB ) + return NULL ; + + for ( i = 0; i < NBT_MAX_LANAS; i++) + { + if ( LanaTable[i].pDeviceContext->iLana == pNCB->ncb_lana_num) + return LanaTable[i].pDeviceContext; + } + + return NULL; +} + +/******************************************************************* + + NAME: NbtWouldLoopback + + SYNOPSIS: Returns a BOOL that specifies whether the input + IP address would loop back to the local machine + + ENTRY: IpAddr + + RETURNS: TRUE if Nbt is bound to this address + + NOTES: It is assumed that LanaTable is filled sequentially + with no holes. + + HISTORY: + EarleH 28-Mar-1996 Created + +********************************************************************/ + +BOOL +NbtWouldLoopback( + ULONG IpAddr +) +{ + int i ; + + for ( i = 0; i < NBT_MAX_LANAS; i++) + { + if ( + ( LanaTable[i].pDeviceContext ) + && ( LanaTable[i].pDeviceContext->IpAddress == IpAddr ) + ) + return TRUE; + } + + return FALSE; +} + +/******************************************************************* + + NAME: VNBT_LANA_MASK + + SYNOPSIS: Returns a bit mask of LANA numbers being handled + by vnbt, with a DNS server configured + + ENTRY: none + + RETURNS: Bit mask of LANA numbers being handled by vnbt + + NOTES: + + HISTORY: + EarleH 26-Feb-1996 Created + +********************************************************************/ + +ULONG +_stdcall +VNBT_LANA_MASK( + ) +{ + int i; + ULONG mask = 0; + + for ( i = 0 ; i < NBT_MAX_LANAS; i++) + { + if ( + ( LanaTable[i].pDeviceContext ) + && ( LanaTable[i].pDeviceContext->fDeviceUp ) + && ( LanaTable[i].pDeviceContext->lDnsServerAddress ) + && ( LanaTable[i].pDeviceContext->lDnsServerAddress != LOOP_BACK ) + ) + { + mask |= 1 << LanaTable[i].pDeviceContext->iLana; + } + } + + return mask; +} + +/******************************************************************* + + NAME: MapTDIStatus2NCBErr + + SYNOPSIS: Maps a TDI_STATUS error value to an Netbios NCR error value + + ENTRY: tdistatus - TDI Status to map + + RETURNS: The mapped error + + NOTES: + + HISTORY: + Johnl 15-Apr-1993 Created + +********************************************************************/ + +uchar MapTDIStatus2NCBErr( TDI_STATUS tdistatus ) +{ + uchar errNCB ; + if ( tdistatus == TDI_SUCCESS ) + return NRC_GOODRET ; + else if ( tdistatus == TDI_PENDING ) + return NRC_PENDING ; + + + switch ( tdistatus ) + { + case TDI_NO_RESOURCES: + errNCB = NRC_NORES ; + break ; + + case STATUS_CANCELLED: + errNCB = NRC_CMDCAN ; + break ; + + case TDI_INVALID_CONNECTION: + case STATUS_CONNECTION_DISCONNECTED: + errNCB = NRC_SCLOSED ; + break ; + + case TDI_CONNECTION_ABORTED: + errNCB = NRC_SABORT ; + break ; + + case STATUS_TOO_MANY_COMMANDS: + errNCB = NRC_TOOMANY ; + break ; + + case STATUS_OBJECT_NAME_COLLISION: + case STATUS_SHARING_VIOLATION: + errNCB = NRC_DUPNAME ; + break ; + + case STATUS_DUPLICATE_NAME: + errNCB = NRC_INUSE ; + break ; + + // + // Call NCB submitted with a name that can't be found + // + case STATUS_BAD_NETWORK_PATH: + errNCB = NRC_NOCALL ; + break ; + + case STATUS_REMOTE_NOT_LISTENING: + errNCB = NRC_REMTFUL ; + break ; + + case TDI_TIMED_OUT: + errNCB = NRC_CMDTMO ; + break ; + + // + // Where the transport has more data available but the NCB's buffer is + // full + // + case TDI_BUFFER_OVERFLOW: + errNCB = NRC_INCOMP ; + break ; + + case STATUS_INVALID_BUFFER_SIZE: + errNCB = NRC_BUFLEN ; + break ; + + case STATUS_NETWORK_NAME_DELETED: + errNCB = NRC_NAMERR ; + break ; + + case STATUS_NRC_ACTSES: + errNCB = NRC_ACTSES ; + break ; + + default: + DbgPrint("MapTDIStatus2NCBErr - Unmapped STATUS/TDI error - " ) ; + DbgPrintNum( tdistatus ) ; + DbgPrint("\n\r") ; + + case STATUS_UNSUCCESSFUL: + case TDI_INVALID_STATE: + case STATUS_INVALID_PARAMETER: // Generally detected bad struct. signature + case STATUS_UNEXPECTED_NETWORK_ERROR: + errNCB = NRC_SYSTEM ; + break ; + } + + return errNCB ; +} + + diff --git a/private/ntos/nbt/vxd/newdns.c b/private/ntos/nbt/vxd/newdns.c new file mode 100644 index 000000000..9bcf78d4b --- /dev/null +++ b/private/ntos/nbt/vxd/newdns.c @@ -0,0 +1,1019 @@ +/*++ + +Copyright (c) 1989-1996 Microsoft Corporation + +Module Name: + + DNS.c + +Abstract: + + VxD-specific DNS routines. + + This stuff will all be obsolete when we get proper services for + Windows. Then we will just call Winsock for DNS name resolution. + +Author: + + Earle R. Horton (ERH) 13-Feb-1996 + +Revision History: + +--*/ + +#include "nbtprocs.h" + +//---------------------------------------------------------------------------- +ULONG +DoDnsResolveDirect( + PNCB pncb, + PUCHAR pzDnsName, + PULONG pIpAddress +) +{ + PDNS_DIRECT_WORK_ITEM_CONTEXT pContext; + PUCHAR pch; + tDEVICECONTEXT *pDeviceContext; + ULONG status; + CTELockHandle OldIrq; + + pDeviceContext = GetDeviceContext( pncb ) ; + + if ( pDeviceContext == NULL ) + { + return NRC_BRIDGE ; + } + + // + // If the primary DNS server is not defined, just return error. + // Return command timed out here. + // + if ( (!pDeviceContext->lDnsServerAddress) || + ( pDeviceContext->lDnsServerAddress == LOOP_BACK) ) + { + return( NRC_NOCALL ); + } + + pContext = CTEAllocMem( sizeof(DNS_DIRECT_WORK_ITEM_CONTEXT) ); + + if ( pContext == NULL ) + { + return NRC_NORESOURCES; + } + + CTEZeroMemory( pContext, sizeof(DNS_DIRECT_WORK_ITEM_CONTEXT) ); + + pContext->pDeviceContext = pDeviceContext; + pContext->pNCB = pncb; + pContext->pzDnsName = pzDnsName; + pContext->pIpAddress = pIpAddress; + + pContext->TransactId = htons(GetTransactId() + DIRECT_DNS_NAME_QUERY_BASE ); + pContext->pchDomainName = NbtConfig.pDomainName; + pContext->Flags = DNS_DIRECT_DNS_SERVER; + + for ( pch = &pzDnsName[0] ; *pch++ != '\0' ; ) + { + if ( pch[0] == '.' ) + { + pContext->Flags |= DNS_DIRECT_NAME_HAS_DOTS; + pContext->pchDomainName = NULL; + } + } + + // + // Put on the pending name queries list again so that when the query + // response comes in from DNS we can find the context. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + InsertTailList(&NbtConfig.DNSDirectNameQueries, + &pContext->Linkage + ); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + status = UdpSendDNSBcastDirect( pContext, + (ULONG)pNbtGlobConfig->uRetryTimeout, + (ULONG)pNbtGlobConfig->uNumRetries + ); + + pncb->ncb_retcode = pncb->ncb_cmd_cplt = status ; + + if ( status != NRC_PENDING ) + { + pncb->ncb_retcode = pncb->ncb_cmd_cplt = status ; + + DnsUnlinkAndCompleteDirect( pContext ); + } + else + { + status = NRC_GOODRET; + } + + return status; +} + +//---------------------------------------------------------------------------- +NTSTATUS +UdpSendDNSBcastDirect( + IN PDNS_DIRECT_WORK_ITEM_CONTEXT pContext, + IN ULONG Timeout, + IN ULONG Retries +) +/*++ + +Routine Description: + + This routine sends a name query directed to the name server. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +History: + + Adapted from UdpSendNSBcast() in "udpsend.c." + + Earle R. Horton (earleh) March 18, 1996 + +--*/ +{ + NTSTATUS status; + tNAMEHDR *pNameHdr; + ULONG uLength; + ULONG uSentSize; + CTELockHandle OldIrq; + ULONG IpAddress; + tTIMERQENTRY *pTimerQEntry; + TDI_REQUEST TdiRequest; + PDNS_DIRECT_SEND_CONTEXT pSendContext; + + pSendContext = CreateSendContextDirect( + pContext->pzDnsName, + pContext->pchDomainName, + (PVOID)&pNameHdr, + &uLength, + pContext); + + if (pSendContext == NULL) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Failed to Create Pdu to send to DNS.\n")); + return( NRC_NORES ); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + TdiRequest.Handle.AddressHandle = (PVOID)pContext->pDeviceContext->pNameServerFileObject; + + // the completion routine is setup to free the pDgramTracker memory block + TdiRequest.RequestNotifyObject = SendDNSBcastDoneDirect; + TdiRequest.RequestContext = (PVOID)pSendContext; + + // start the timer now...We didn't start it before because it could + // have expired during the dgram setup, perhaps before the Context was + // fully setup. + // + if (Timeout) + { + status = StartTimer( + Timeout, + pContext, + NULL, + DnsCompletionDirect, + NULL, + NULL, + (USHORT)Retries, + &pTimerQEntry + ); + + if (!NT_SUCCESS(status)) + { + // we need to differentiate the timer failing versus lack + // of resources + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEMemFree(pSendContext); + + return( NRC_NORES ); + } + // + // Cross link the nameaddr and the timer so we can stop the timer + // when the name query response occurs + // + pTimerQEntry->pCacheEntry = pContext; + pContext->pTimer = pTimerQEntry; + } + + // + // in the event that DHCP has just removed the IP address, just cancel + // the request + // + if (pContext->pDeviceContext->IpAddress == 0) + { + StopTimer ( pContext->pTimer, NULL, NULL ); + pContext->Flags |= DNS_DIRECT_CANCELLED; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + (VOID) TdiSendDatagram( + &TdiRequest, + &pSendContext->SendInfo, + uLength, + &uSentSize, + &pSendContext->SendBuffer, + NBT_NAME_SERVICE + ); + + return( NRC_PENDING ); +} + +//---------------------------------------------------------------------------- +VOID +SendDNSBcastDoneDirect( + IN PVOID pSendContext, + IN NTSTATUS status, + IN ULONG lInfo +) +{ + CTEMemFree(pSendContext); +} + +//---------------------------------------------------------------------------- +BOOL +DoDnsCancelDirect( + PNCB pncb +) +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PDNS_DIRECT_WORK_ITEM_CONTEXT + pContext; + CTELockHandle OldIrq; + BOOL RetVal = FALSE; + + pHead = pEntry = &NbtConfig.DNSDirectNameQueries; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + while ((pEntry = pEntry->Flink) != pHead) + { + pContext = CONTAINING_RECORD(pEntry,DNS_DIRECT_WORK_ITEM_CONTEXT,Linkage); + if ( pContext->pNCB == pncb ) + { + StopTimer ( pContext->pTimer, NULL, NULL ); + pContext->Flags |= DNS_DIRECT_CANCELLED; + RetVal = TRUE; + break; + } + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return RetVal; +} + +//---------------------------------------------------------------------------- +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. It must + decide if another name query should be sent to the DNS server, and if not, + then it completes the request. + +Arguments: + + +Notes: +--*/ +VOID +DnsCompletionDirect( + PVOID pvContext, + PVOID pvContext2, + tTIMERQENTRY *pTimerQEntry +) +{ + PDNS_DIRECT_WORK_ITEM_CONTEXT pContext; + tDEVICECONTEXT *pDeviceContext; + + NTSTATUS status; + CTELockHandle OldIrq; + PCHAR pchDomainName; + USHORT Flags; + BOOL fOneMoreTry; + + KdPrint(("DnsCompletion entered\r\n")); + + pContext = (PDNS_DIRECT_WORK_ITEM_CONTEXT)pvContext; + + + // if the client completion routine is not set anymore, then the + // timer has been cancelled or completed and this routine should + // just clean up its buffers associated with the tracker (and return) + // + if (!pTimerQEntry) + { + // complete the request + LOCATION(0x52); + DnsUnlinkAndCompleteDirect( pContext ); + return; + } + + + // + // to prevent a client from stopping the timer and deleting the + // pContext, grab the lock and check if the timer has been stopped + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pDeviceContext = pContext->pDeviceContext; + + if (pTimerQEntry->Flags & TIMER_RETIMED) + { + // + // Got a wait ACK from the server. + // + pTimerQEntry->Flags &= ~TIMER_RETIMED; + pTimerQEntry->Flags |= TIMER_RESTART; + // + // if we are not bound to this card than use a very short timeout + // + if ( + (!pDeviceContext->pNameServerFileObject) + || (pContext->Flags & DNS_DIRECT_CANCELLED) + ) + { + pTimerQEntry->DeltaTime = 10; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + + // If done with all the (3) retries with primary, try secondary DNS srvr + // If secondary not defined, or done with secondary as well, stop. + // + + fOneMoreTry = TRUE; + + if (!(--pTimerQEntry->Retries)) + { + // + // if backup server is not defined, or if it is defined but we just + // finished trying backup server, go back and try primary server for + // "other domains" + // e.g. DNSDomains was defined as "msft.dom2.com,msft.dom3.com,msft.dom" + // We were pointing at msft.dom2.com. Now, we are done with that (and + // didn't get a response), so try msft.dom3.com + // + if ( ( !pDeviceContext->lDnsBackupServer ) || + ( pDeviceContext->lDnsBackupServer == LOOP_BACK ) || + ( pContext->Flags & DNS_DIRECT_DNS_BACKUP) ) + { + // + // if we just got done trying primary domain name, try all the + // "other domains" specified + // + if (pContext->pchDomainName == NbtConfig.pDomainName) + { + pContext->pchDomainName = NbtConfig.pDNSDomains; + if ( pContext->pchDomainName ) + { + pContext->Flags &= ~DNS_DIRECT_DNS_BACKUP; + pContext->Flags |= DNS_DIRECT_DNS_SERVER; + pTimerQEntry->Retries = NbtConfig.uNumRetries; + } + else + { + fOneMoreTry = FALSE; + } + } + + // + // if we had already started on "other domains", advance to the + // next domain within "other domains" + // + else if ( pContext->pchDomainName ) + { + pchDomainName = pContext->pchDomainName; + while( *pchDomainName != ',' && // dom names separated by comma + *pchDomainName != ' ' && // or space + *pchDomainName != '\0' ) + { + pchDomainName++; + } + + if ( *pchDomainName == '\0' ) + fOneMoreTry = FALSE; + else + { + pchDomainName++; + pContext->pchDomainName = pchDomainName; + pContext->Flags &= ~DNS_DIRECT_DNS_BACKUP; + pContext->Flags |= DNS_DIRECT_DNS_SERVER; + pTimerQEntry->Retries = NbtConfig.uNumRetries; + } + } + else + { + fOneMoreTry = FALSE; + } + } + + // ok, prepare to try the backup server + else + { + pTimerQEntry->Retries = NbtConfig.uNumRetries; + + pContext->Flags &= ~DNS_DIRECT_DNS_SERVER; + pContext->Flags |= DNS_DIRECT_DNS_BACKUP; + } + } + + // we aren't done yet: send one more query and restart the timer + if (fOneMoreTry) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + status = UdpSendDNSBcastDirect( pContext, 0, 0 ); + + pTimerQEntry->Flags |= TIMER_RESTART; + + KdPrint(("One more DNS query sent out\r\n")); + } + + // yup, all done: didn't find the name! + else + { + pTimerQEntry->Flags |= TIMER_RESTART; + StopTimer(pTimerQEntry,NULL,NULL); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } +} + +//---------------------------------------------------------------------------- +/*++ + +Routine Description: + + This routine "actually" completes the request, either by completing the + asociated NCB with some kind of failure, or passing it off to the + main NCB processing code. + +Arguments: + + +Notes: +--*/ +VOID +DnsActualCompletionDirect( + IN NBT_WORK_ITEM_CONTEXT * pnbtContext +) +{ + PDNS_DIRECT_WORK_ITEM_CONTEXT pContext = pnbtContext->pClientContext; + uchar errNCB = NRC_GOODRET ; + NCB * pNCB = pContext->pNCB; + + if ( pNCB->ncb_cmd_cplt == NRC_PENDING ) + { + // + // Failed to resolve the name, or request was cancelled. + // + if ( pContext->pIpAddress[0] == 0 ) + { + errNCB = NRC_CMDTMO; + } + else if ( pContext->Flags & DNS_DIRECT_CANCELLED ) + { + errNCB = NRC_CMDCAN; + } + else + { + errNCB = VNBT_NCB_X ( pContext->pNCB, NULL, pContext->pIpAddress, NULL, 0 ); + } + if ( errNCB != NRC_GOODRET ) + { + pNCB->ncb_retcode = pNCB->ncb_cmd_cplt = errNCB ; + // + // call the post-routine only if the post-routine has been specified! + // + if ( pNCB->ncb_post ) + { + typedef void (CALLBACK * VXDNCBPost )( void ) ; + VXDNCBPost ncbpost = (VXDNCBPost) pNCB->ncb_post ; + + // + // Clients are expecting EBX to point to the NCB (instead of + // pushing it on the stack...). The post routine may trash + // ebp also, so save it. + // + _asm pushad ; + _asm mov ebx, pNCB ; + ncbpost() ; + _asm popad ; + } + } + } + + CTEMemFree(pContext); + CTEMemFree(pnbtContext); +} + +//---------------------------------------------------------------------------- +VOID +DnsUnlinkAndCompleteDirect( + IN PDNS_DIRECT_WORK_ITEM_CONTEXT pContext +) +/*++ + +Routine Description: + + This routine unlinks and completes the request. + +Arguments: + + +Notes: +--*/ +{ + RemoveEntryList(&pContext->Linkage); + + VxdScheduleDelayedCall ( NULL, + pContext, + NULL, + DnsActualCompletionDirect, + pContext->pDeviceContext + ); +} + +//---------------------------------------------------------------------------- +VOID +ProcessDnsResponseDirect( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ) +/*++ + +Routine Description: + + This function sets the state of the name being resolved appropriately + depending on whether DNS sends a positive or a negative response to our + query; calls the client completion routine and stops any more DNS queries + from going. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL + +--*/ +{ + NTSTATUS status; + tDNS_QUERYRESP UNALIGNED *pQuery; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PDNS_DIRECT_WORK_ITEM_CONTEXT + pContext; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SrcAddress; + ULONG IpAddress; + tTIMERQENTRY *pTimer = NULL; + CTELockHandle OldIrq1; + LONG lNameSize; + LONG lTraversedSoFar=0; + CHAR pJunkBuf[NETBIOS_NAME_SIZE]; + PUCHAR pchQry; + + // make sure this is a response + + if ( !(OpCodeFlags & OP_RESPONSE) ) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponseDirect: Bad OpCodeFlags\r\n")); + + return; + } + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + if ( ( pContext = FindContextDirect( pNameHdr->TransactId ) ) != NULL ) + + { + pContext->Flags |= DNS_DIRECT_ANSWERED; + if ( pTimer = pContext->pTimer ) + { + // + // If the response we received doesn't resolve the name, we silently return, + // but make sure reties is set to 1 so that when timer fires again, we don't + // send another name query to the same server but instead timeout the + // attempt on this server + // + pTimer->Retries = 1; + } + // + // check the pdu size for errors + // + if (lNumBytes < DNS_MINIMUM_QUERYRESPONSE) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponseDirect: Bad lNumBytes\r\n")); + goto done; + } + +// +// BUGBUG: should we require authoritative responses from DNS servers? +// + + // + // if it's a negative response, quit now! + // + if (IS_NEG_RESPONSE(OpCodeFlags)) + { + goto done; + } + + // + // if there is no answer section, return! + // + if ( !pNameHdr->AnCount ) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponseDirect: No answer section\r\n")); + goto done; + } + + // + // lNameSize is the length of the entire name, excluding the length byte + // for the first label (including length bytes of subsequent labels) and + // including the trailing 0 (tNAMEHDR struc takes care for 1st byte) + // + DnsExtractName( (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pJunkBuf, + &lNameSize + ); + pchQry = (PUCHAR)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + lTraversedSoFar += lNameSize; + + // + // if the Question section is returned with the response then we have + // a little more work to do! In this case, pQuery is pointing at the + // beginning of the QTYPE field (end of the QNAME) + // + if ( pNameHdr->QdCount ) + { + pchQry += sizeof(tQUESTIONMODS); + lTraversedSoFar += sizeof(tQUESTIONMODS); + + // most common case: 1st byte will be 0xC0, which means next byte points + // to the actual name. We don't care about the name, so we skip over + // both the bytes + // + if ( (*pchQry) == PTR_TO_NAME ) + { + pchQry += sizeof(tDNS_LABEL); + lTraversedSoFar += sizeof(tDNS_LABEL); + } + + // + // if some implementation doesn't optimize and copies the whole name + // again, skip over the length of the name + // + else + { + pchQry += (lNameSize+1); // +1 because of the 1st length byte! + lTraversedSoFar += (lNameSize+1); + } + } + + pQuery = (tDNS_QUERYRESP *)pchQry; + + // + // if this rr is telling us about canonical name, skip over it and go to + // where the ipaddr is + // + if (ntohs(pQuery->RrType) == DNS_CNAME) + { + // + // since this is CNAME, there is no ipaddr. Instead, the data is the + // canonical name whose length we are adding, and subtract ipaddr's len + // + pchQry += (sizeof(tDNS_QUERYRESP) - sizeof(ULONG)); + pchQry += ntohs(pQuery->Length); + lTraversedSoFar += ntohs(pQuery->Length) + sizeof(tDNS_QUERYRESP) - sizeof(ULONG); + + ASSERT(lNumBytes > lTraversedSoFar); + + // most common case: 1st byte will be 0xC0, which means next byte points + // to the actual name. We don't care about the name, so we skip over + // both the bytes + // + if ( (*pchQry) == PTR_TO_NAME ) + { + pchQry += sizeof(tDNS_LABEL); + lTraversedSoFar += sizeof(tDNS_LABEL); + } + + // + // if some implementation doesn't optimize and copies the whole name + // again, skip over the length of the name + // + else + { + // we have already taken the name out. we are calling this routine + // just to see how big the canonical name is (i.e.lNameSize), to skip + // past it + // + DnsExtractName( pchQry, + lNumBytes-lTraversedSoFar, + pJunkBuf, + &lNameSize + ); + + // + // lNameSize is the length of the entire name, excluding the length byte + // for the first label (including length bytes of subsequent labels) and + // including the trailing 0 (tNAMEHDR struc takes care for 1st byte) + // + pchQry += lNameSize+1; // +1 for the length byte of first label + + } + + pQuery = (tDNS_QUERYRESP *)pchQry; + } + + // if we came this far, it's a positive response. do the needful. + + IpAddress = ntohl(pQuery->IpAddress); + + if ( !NbtWouldLoopback( IpAddress ) ) + { + pContext->pIpAddress[0] = IpAddress; + } + else + { + pContext->Flags |= DNS_DIRECT_CANCELLED; + } + + // + // Set the backup name server to be the main name server + // if we got a response from it. + // + if ( SrcAddress == pContext->pDeviceContext->lDnsBackupServer ) + { + pContext->pDeviceContext->lDnsBackupServer = + pContext->pDeviceContext->lDnsServerAddress; + + pContext->pDeviceContext->lDnsServerAddress = SrcAddress; + } + + StopTimer(pTimer,NULL,NULL); + } + +done: + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; +} + +//---------------------------------------------------------------------------- +PDNS_DIRECT_SEND_CONTEXT +CreateSendContextDirect( + IN PCHAR pName, + IN PCHAR pchDomainName, + OUT PVOID *pHdrs, + OUT PULONG pLength, + IN PDNS_DIRECT_WORK_ITEM_CONTEXT pContext + ) +/*++ + +Routine Description: + + This routine builds a name query pdu + +Arguments: + + +Return Value: + + PDNS_DIRECT_SEND_CONTEXT - a pointer to a data structure used for the datagram + send that must be freed by the datagram send completion routine + +--*/ +{ + tNAMEHDR *pNameHdr; + ULONG uLength; + ULONG uDomainNameSize; + tGENERALRR *pGeneral; + CTELockHandle OldIrq; + PDNS_DIRECT_SEND_CONTEXT + pSendContext; + + uDomainNameSize = domnamelen(pchDomainName) + 1; // +1 for len byte + if (uDomainNameSize > 1) + { + uDomainNameSize++; // for the null byte + } + + // size is size of the namehdr structure -1 for the NetbiosName[1] + // + the 32 bytes for the half ascii name + + // scope + size of the General RR structure + uLength = sizeof(DNS_DIRECT_SEND_CONTEXT) - 1 + + uDomainNameSize + + sizeof(ULONG) + + strlen(pName) + + 1; + + // Note that this memory must be deallocated when the send completes in + // SendDNSBcastDoneDirect() + pSendContext = NbtAllocMem((USHORT)uLength ,NBT_TAG('X')); + + if (pSendContext) + { + CTEZeroMemory((PVOID)pSendContext,uLength); + + pNameHdr = &pSendContext->NameHdr; + + pNameHdr->TransactId = pContext->TransactId; + pNameHdr->QdCount = 1; + pNameHdr->AnCount = 0; + pNameHdr->NsCount = 0; + + *pHdrs = (PVOID)pNameHdr; + *pLength = uLength = uLength - ( sizeof(DNS_DIRECT_SEND_CONTEXT) - sizeof(tNAMEHDR) ); + + // copy the netbios name ... adding the scope too + if ( pContext->Flags & DNS_DIRECT_NAME_HAS_DOTS ) + { + char * p; + for ( p = pName ; *p != '.' ; p++ ); + p[0] = '\0'; + pGeneral = (tGENERALRR *)DnsStoreName( + (PCHAR)&pNameHdr->NameRR.NameLength, + pName, + &p[1], + eDIRECT_DNS_NAME_QUERY); + p[0] = '.'; + } + else + { + pGeneral = (tGENERALRR *)DnsStoreName( + (PCHAR)&pNameHdr->NameRR.NameLength, + pName, + pContext->pchDomainName, + eDIRECT_DNS_NAME_QUERY); + } + + + pGeneral->Question.QuestionTypeClass = htonl(QUEST_DNSINTERNET); + + pNameHdr->OpCodeFlags = (FL_RECURDESIRE); + + pNameHdr->ArCount = 0; + + pSendContext->SendBuffer.pDgramHdr = pNameHdr; + pSendContext->SendBuffer.HdrLength = uLength; + pSendContext->SendBuffer.pBuffer = NULL; + pSendContext->SendBuffer.Length = 0; + pSendContext->SendInfo.RemoteAddressLength = sizeof(pSendContext->NameServerAddress); + pSendContext->SendInfo.RemoteAddress = (PTRANSPORT_ADDRESS)&pSendContext->NameServerAddress; + pSendContext->NameServerAddress.TAAddressCount = 1; + pSendContext->NameServerAddress.Address[0].AddressLength = sizeof(TDI_ADDRESS_IP); + pSendContext->NameServerAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_IP; + pSendContext->NameServerAddress.Address[0].Address[0].sin_port = htons(NbtConfig.DnsServerPort); + if ( pContext->Flags & DNS_DIRECT_DNS_SERVER ) + { + pSendContext->NameServerAddress.Address[0].Address[0].in_addr = htonl(pContext->pDeviceContext->lDnsServerAddress); + } + else + { + pSendContext->NameServerAddress.Address[0].Address[0].in_addr = htonl(pContext->pDeviceContext->lDnsBackupServer); + } + } + + return(pSendContext); +} + +//---------------------------------------------------------------------------- +PDNS_DIRECT_WORK_ITEM_CONTEXT +FindContextDirect( + USHORT TransactionId +) +/*++ + +Routine Description: + + This routine find the DNS_DIRECT_WORK_ITEM_CONTEXT having the given + TransactId + +Arguments: + + +Return Value: + + PDNS_DIRECT_SEND_CONTEXT - a pointer to the pContext having the given + TransactId, or NULL + +Notes: + + Called with NbtConfig.JointLock held + +--*/ +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PDNS_DIRECT_WORK_ITEM_CONTEXT + pContext; + + pHead = pEntry = &NbtConfig.DNSDirectNameQueries; + + while ((pEntry = pEntry->Flink) != pHead) + { + pContext = CONTAINING_RECORD(pEntry,DNS_DIRECT_WORK_ITEM_CONTEXT,Linkage); + if ( pContext->TransactId == TransactionId ) + { + return pContext; + } + } + return NULL; +} + +//---------------------------------------------------------------------------- +VOID +IpToAscii( + IN DWORD IpAddress, + IN OUT PCHAR pcAscii +) +/*++ + +Routine Description: + + This routine converts an IP address to a NetBIOS name. + +Arguments: + + DWORD IP address + PCHAR String pointer allocated for 16 bytes + +Return Value: + + Note + +Notes: + + This is a gigantic hack designed to get "net use \\<dnsname>" + working under Windows 95 OPK2 without destabilizing the existing + code base over much. + +--*/ +{ + PCHAR pcIpAddressBytes = ( (PCHAR) &IpAddress ) + 3; + PCHAR pcIp = pcAscii; + + int i,j,k,l,m; + + memset ( pcAscii, 0x20, NETBIOS_NAME_SIZE ); + + for ( i = 4 ; i-- > 0 ; ) + { + + j = *pcIpAddressBytes-- & 0x000000FF; + k = j/100; + l = (j%100)/10; + m = j%10; + + if ( k ) + { + *pcIp++ = k + '0'; + *pcIp++ = l + '0'; + } + + else if ( l ) + { + *pcIp++ = l + '0'; + } + + *pcIp++ = m + '0'; + + if ( i ) + { + *pcIp++ = '.'; + } + + } + +} + diff --git a/private/ntos/nbt/vxd/pageable.inc b/private/ntos/nbt/vxd/pageable.inc new file mode 100644 index 000000000..8b13b5276 --- /dev/null +++ b/private/ntos/nbt/vxd/pageable.inc @@ -0,0 +1,36 @@ +; +; Macro to define pageable code under Chicago and later +; operating systems. +; + +NBT_PAGEABLE_CODE_SEG MACRO + +ifdef CHICAGO + +VxD_PAGEABLE_CODE_SEG + +else + +VxD_CODE_SEG + +endif + + endm + + + + + +NBT_PAGEABLE_CODE_ENDS MACRO + +ifdef CHICAGO + +VxD_PAGEABLE_CODE_ENDS + +else + +VxD_CODE_ENDS + +endif + + endm diff --git a/private/ntos/nbt/vxd/rules.mk b/private/ntos/nbt/vxd/rules.mk new file mode 100644 index 000000000..831428e2f --- /dev/null +++ b/private/ntos/nbt/vxd/rules.mk @@ -0,0 +1,4 @@ +# +#:ts=4 +# +!include ..\rules16.mk diff --git a/private/ntos/nbt/vxd/tdiaddr.c b/private/ntos/nbt/vxd/tdiaddr.c new file mode 100644 index 000000000..7b2727dfa --- /dev/null +++ b/private/ntos/nbt/vxd/tdiaddr.c @@ -0,0 +1,184 @@ +// +// +// tdiaddr.c +// +// This file contains code relating to manipulation of address objects +// that is specific to the VXD environment. It creates address endpoints +// with the transport provider. + +#include <nbtprocs.h> +#include <tdistat.h> // TDI error codes + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenAddress ( + OUT PHANDLE pFileHandle, + OUT PDEVICE_OBJECT *ppDeviceObject, + OUT PFILE_OBJECT *ppFileObject, + IN tDEVICECONTEXT *pDeviceContext, + IN USHORT PortNumber, + IN ULONG IpAddress, + IN ULONG Flags + ) +/*++ + +Routine Description: + + Note: This synchronous call may take a number of seconds. It runs in + the context of the caller. The code Opens an Address object with the + transport provider and then sets up event handlers for Receive, + Disconnect, Datagrams and Errors. + + The address data structures are found in tdi.h , but they are rather + confusing since the definitions have been spread across several data types. + This section shows the complete data type for Ip address: + + typedef struct + { + int TA_AddressCount; + struct _TA_ADDRESS + { + USHORT AddressType; + USHORT AddressLength; + struct _TDI_ADDRESS_IP + { + USHORT sin_port; + USHORT in_addr; + UCHAR sin_zero[8]; + } TDI_ADDRESS_IP + + } TA_ADDRESS[AddressCount]; + + } TRANSPORT_ADDRESS + + An EA buffer is allocated (for the IRP), with an EA name of "TransportAddress" + and value is a structure of type TRANSPORT_ADDRESS. + +Arguments: + + bTCP - a boolean to say if we are openning a TCP port or a UDP port + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS ; + TA_IP_ADDRESS taip ; // This is really a TRANSPORT_ADDRESS + TDI_REQUEST tdirequest ; + + DbgPrint("TdiOpenAddress called\n\r"); + + + taip.TAAddressCount = 1 ; + taip.Address[0].AddressLength = sizeof( TDI_ADDRESS_IP ) ; + taip.Address[0].AddressType = TDI_ADDRESS_TYPE_IP ; + taip.Address[0].Address[0].sin_port = htons(PortNumber); // put in network order + taip.Address[0].Address[0].in_addr = htonl(IpAddress); + CTEZeroMemory( taip.Address[0].Address[0].sin_zero, + sizeof( taip.Address[0].Address[0].sin_zero ) ) ; + + #define TCP_PORT 6 + #define UDP_PORT 17 + status = TdiVxdOpenAddress( &tdirequest, + (PTRANSPORT_ADDRESS) &taip, + Flags & SESSION_FLAG ? TCP_PORT : UDP_PORT, + NULL ) ; + + if ( status == TDI_SUCCESS ) + { + HANDLE hAddress = tdirequest.Handle.AddressHandle ; + + // + // As a VXD, the p*FileObject in the DeviceContext structure will + // contain the TDI Address. For compatibility with NT (may want + // to change this with an environment specific address). + // + *ppFileObject = (PFILE_OBJECT) hAddress ; + + if (Flags & TCP_FLAG) + { + // TCP port needs several event handlers for connection + // management + status = TdiVxdSetEventHandler( + hAddress, + TDI_EVENT_RECEIVE, + (PVOID)TdiReceiveHandler, + (PVOID)pDeviceContext); + + ASSERTMSG( "Failed to set Receive event handler", + status == TDI_SUCCESS ); + + status = TdiVxdSetEventHandler( + hAddress, + TDI_EVENT_DISCONNECT, + (PVOID)TdiDisconnectHandler, + (PVOID)pDeviceContext); + ASSERTMSG( "Failed to set Disconnect event handler", + status == TDI_SUCCESS); + + // only set an connect handler if the session flag is set. + // In this case the address being opened is the Netbios session + // port 139 + if (Flags & SESSION_FLAG) + { + status = TdiVxdSetEventHandler( + hAddress, + TDI_EVENT_CONNECT, + (PVOID)TdiConnectHandler, + (PVOID)pDeviceContext); + + ASSERTMSG((PUCHAR)"Failed to set Receive event handler", + status == TDI_SUCCESS ); + } + } + else + { + // Datagram ports only need this event handler + if (PortNumber == NBT_DATAGRAM_UDP_PORT) + { + // Datagram Udp Handler + status = TdiVxdSetEventHandler( + hAddress, + TDI_EVENT_RECEIVE_DATAGRAM, + (PVOID)TdiRcvDatagramHandler, + (PVOID)pDeviceContext); + ASSERTMSG("Failed to set Receive Datagram event handler", + status == TDI_SUCCESS ); + } + else + { + // Name Service Udp handler + status = TdiVxdSetEventHandler( + hAddress, + TDI_EVENT_RECEIVE_DATAGRAM, + (PVOID)TdiRcvNameSrvHandler, + (PVOID)pDeviceContext); + ASSERTMSG( "Failed to set Receive Datagram event handler", + status == TDI_SUCCESS ); + } + } + + status = TdiVxdSetEventHandler( + hAddress, + TDI_EVENT_ERROR, + (PVOID)TdiErrorHandler, + (PVOID)pDeviceContext); + ASSERTMSG("Failed to set Error event handler", + status == TDI_SUCCESS ); + + + } + +#ifdef DEBUG + if ( status != STATUS_SUCCESS ) + { + DbgPrint("NbtTdiOpenAddress: status == ") ; + DbgPrintNum( status ) ; DbgPrint("\n\r") ; + } +#endif + return(status); +} + + diff --git a/private/ntos/nbt/vxd/tdicnct.c b/private/ntos/nbt/vxd/tdicnct.c new file mode 100644 index 000000000..fea0ba781 --- /dev/null +++ b/private/ntos/nbt/vxd/tdicnct.c @@ -0,0 +1,328 @@ +// +// +// NBTCONNCT.C +// +// This file contains code relating to opening connections with the transport +// provider. The Code is NT specific. + +#include <nbtprocs.h> + +void DummyCompletion( PVOID pContext ) ; + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenConnection ( + IN tLOWERCONNECTION *pLowerConn, + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine opens a connection with the transport provider. + +Arguments: + + pLowerConn - Pointer to where the handle to the Transport for this virtual + connection should be stored. + + pNbtConfig - the name of the adapter to connect to is in this structure + +Return Value: + + Status of the operation. + + pLowerConn->pFileObject will contain the Connection ID of a successful open + +--*/ +{ + NTSTATUS status; + TDI_REQUEST Request ; + DbgPrint("NbtTdiOpenConnection Entered\n\r") ; + + CTEZeroMemory(pLowerConn,sizeof(tLOWERCONNECTION)); + pLowerConn->State = NBT_IDLE; + pLowerConn->pDeviceContext = pDeviceContext; + pLowerConn->RefCount = 1; + pLowerConn->LockNumber = LOWERCON_LOCK; + pLowerConn->Verify = NBT_VERIFY_LOWERCONN; + pLowerConn->fOnPartialRcvList = FALSE; + InitializeListHead(&pLowerConn->PartialRcvList); + + // + // Use the lower connection as the context + // + status = TdiVxdOpenConnection( &Request, pLowerConn ) ; +#ifdef DEBUG + if ( status != TDI_SUCCESS ) + { + DbgPrint("NbtTdiOpenConnection: OpenConnection failed, error ") ; + DbgPrintNum( status ) ; + DbgPrint("\n\r") ; + } +#endif + + + // + // Store the handle in the Lower Connection for future reference + // + pLowerConn->pFileObject = Request.Handle.ConnectionContext ; + DbgPrint("TdiVxdOpenConnection - pLower Conn: 0x") ; + DbgPrintNum((ULONG) pLowerConn ) ; + DbgPrint( " Connection ID: 0x") ; + DbgPrintNum( (ULONG) Request.Handle.ConnectionContext ) ; DbgPrint("\r\n") ; + + return status; +} /* NbtTdiOpenConnection */ + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiAssociateConnection( + IN PFILE_OBJECT pFileObject, + IN HANDLE Handle + ) +/*++ + +Routine Description: + + This routine associates an open connection with the address object. + +Arguments: + + + pFileObject - the connection file object (actually a connection ID) + Handle - the address object to associate the connection with + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + TDI_REQUEST Request ; + DbgPrint("NbtTdiAssociateConnection Entered\n\r") ; + + Request.Handle.ConnectionContext = (CONNECTION_CONTEXT) pFileObject ; + status = TdiVxdAssociateAddress( &Request, Handle ) ; + +#ifdef DEBUG + if ( status != TDI_SUCCESS ) + { + DbgPrint("NbtTdiAssociateConnection: AssociateAddress failed, error ") ; + DbgPrintNum( status ) ; + DbgPrint("\n\r") ; + } +#endif + return status; +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiOpenandAssocConnection( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG PortNumber + ) +/*++ + +Routine Description: + + This routine opens and associates an open connection + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + CTELockHandle OldIrq; + PDEVICE_OBJECT pDeviceObject; + tLOWERCONNECTION *pLowerConn; + + DbgPrint("TdiOpenandAssocConnection Entered\n\r") ; + + // allocate memory for the lower connection block. + pConnEle->pLowerConnId = (PVOID)CTEAllocMem(sizeof(tLOWERCONNECTION)); + if (!pConnEle->pLowerConnId) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // fill in the lower connection element to point to the upper one and + // vice versa + // + pLowerConn = pConnEle->pLowerConnId; + + // + // pLowerConn->pFileObject will contain the connection ID after + // this call + // + status = NbtTdiOpenConnection(pLowerConn,pDeviceContext); + if (!NT_SUCCESS(status)) + { + CTEMemFree((PVOID)pConnEle->pLowerConnId); + return(status); + } + + pLowerConn->pUpperConnection = pConnEle; + pLowerConn->State = NBT_CONNECTING; + + + if (NT_SUCCESS(status)) + { + + + // Open an address object (aka port) + // + status = NbtTdiOpenAddress( + NULL, //&pLowerConn->AddrFileHandle, + &pDeviceObject, // dummy argument, not used here + &pLowerConn->pAddrFileObject, // Address Handle + pDeviceContext, + (USHORT)PortNumber, // port + pDeviceContext->IpAddress, + TCP_FLAG | SESSION_FLAG ); + + if (NT_SUCCESS(status)) + { + // now associate the two + status = NbtTdiAssociateConnection( + pLowerConn->pFileObject, + pLowerConn->pAddrFileObject); + if (NT_SUCCESS(status)) + { + // + // put the lower connection on the Q of active lower connections for + // this device + // + CTESpinLock(pDeviceContext,OldIrq); + InsertTailList(&pDeviceContext->LowerConnection,&pLowerConn->Linkage); + CTESpinFree(pDeviceContext,OldIrq); + + return(status); + } + + REQUIRE( NT_SUCCESS( NbtTdiCloseAddress( pLowerConn )) ) ; + } + + REQUIRE( NT_SUCCESS( NbtTdiCloseConnection( pLowerConn )) ) ; + } + + // Error Path... delete memory + // + pConnEle->pLowerConnId = NULL; + CTEMemFree((PVOID)pLowerConn); + + return(status); +} + +//---------------------------------------------------------------------------- + +NTSTATUS +NbtTdiCloseConnection( + IN tLOWERCONNECTION * pLowerConn + ) +/*++ + +Routine Description: + + This routine closes a TDI connection + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status ; + TDI_REQUEST Request ; + DbgPrint("NbtTdiCloseConnection Entered\n\r") ; + + ASSERT( pLowerConn != NULL ) ; + + Request.Handle.ConnectionContext = pLowerConn->pFileObject ; + Request.RequestNotifyObject = DummyCompletion ; + Request.RequestContext = NULL ; + + status = TdiVxdCloseConnection( &Request ) ; + +#ifdef DEBUG + if ( !NT_SUCCESS( status )) + { + DbgPrint("NbtCloseConnection: Warning - returning 0x") ; + DbgPrintNum( status ) ; DbgPrint("\r\n") ; + } +#endif + DbgPrint("TdiVxdCloseConnection - pLowerConn: 0x") ; + DbgPrintNum((ULONG) pLowerConn ) ; + DbgPrint(" Connection ID: 0x") ; + DbgPrintNum( (ULONG) Request.Handle.ConnectionContext ) ; DbgPrint("\r\n") ; + return status ; +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiCloseAddress( + IN tLOWERCONNECTION * pLowerConn + ) +/*++ + +Routine Description: + + This routine closes a TDI address + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + DbgPrint("NbtTdiCloseAddress Entered\n\r") ; + ASSERT( pLowerConn != NULL ) ; + + return CloseAddress( pLowerConn->pAddrFileObject ) ; +} + +//---------------------------------------------------------------------------- +NTSTATUS CloseAddress( HANDLE hAddress ) +{ + NTSTATUS status ; + TDI_REQUEST Request ; + DbgPrint("CloseAddress Entered\n\r") ; + + Request.Handle.AddressHandle = hAddress ; + Request.RequestNotifyObject = DummyCompletion ; + Request.RequestContext = NULL ; + + status = TdiVxdCloseAddress( &Request ) ; + +#ifdef DEBUG + if ( !NT_SUCCESS( status )) + { + DbgPrint("CloseAddress: Warning - returning 0x") ; + DbgPrintNum( status ) ; DbgPrint("\r\n") ; + } +#endif + return status ; +} + + +// +// Dummy completion routine for Close connection and Close Address +// +void DummyCompletion( PVOID pContext ) +{ + return ; +} diff --git a/private/ntos/nbt/vxd/tdihndlr.c b/private/ntos/nbt/vxd/tdihndlr.c new file mode 100644 index 000000000..f5a9d5425 --- /dev/null +++ b/private/ntos/nbt/vxd/tdihndlr.c @@ -0,0 +1,1719 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdihndlr.c + +Abstract: + + + This file contains the TDI handlers that are setup for Connects, + Receives, Disconnects, and Errors on an address object (in tdiaddr.c). + + This file represents the TDI interface on the Bottom of NBT. Therefore + the code basically decodes the incoming information and passes it to + a non-Os specific routine to do what it can. Upon return from that + routine additional Os specific work may need to be done. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + John Ludeman (JohnL) 04-10-93 - Rewrote for VXD + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "ctemacro.h" + +// +// The event receive buffer takes a pointer to a flags variable that will +// always be the same, so just use the same variable +// +static USHORT usFlags = TDI_RECEIVE_NORMAL ; + +VOID +AcceptCompletionRoutine( + IN PVOID pContext, + IN uint tdistatus, + IN uint extra + ); +VOID +NewSessionCompletionRoutine ( + IN PVOID pContext, + IN uint tdistatus, + IN uint extra + ); +NTSTATUS +Reindicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu + ); + +// +// This ntohl swaps just three bytes, since the 4th byte could be a session +// keep alive message type. +// +__inline long +myntohl(long x) +{ + return((((x) >> 24) & 0x000000FFL) | + (((x) >> 8) & 0x0000FF00L) | + (((x) << 8) & 0x00FF0000L)); +} + +//---------------------------------------------------------------------------- +TDI_STATUS +TdiReceiveHandler ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT EventRcvBuffer * pevrcvbuf + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + IN PVOID ReceiveEventContext - Context provided for this event when event set + IN PVOID ConnectionContext - Connection Context, (pLowerConnection) + IN USHORT ReceiveFlags - Flags describing the message + IN ULONG BytesIndicated - Number of bytes available at indication time + IN ULONG BytesAvailable - Number of bytes available to receive + OUT PULONG BytesTaken - Number of bytes consumed by redirector. + IN PVOID pTsdu - Data from remote machine. + OUT EvenRcvBuffer *ppBuffer - Receive buffer to fill if set + + +Return Value: + + TDI_STATUS - Status of receive operation + +--*/ + +{ + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + PRCV_CONTEXT prcvCont = NULL ; + NTSTATUS status; + ULONG PduSize; + ULONG RemainingPdu; + + DbgPrint("TRH Entered (ConnectionContext = 0x") ; + DbgPrintNum( (ULONG) ConnectionContext) ; DbgPrint(")\r\n") ; + + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + + pLowerConn->BytesRcvd += BytesAvailable; + + // + // check if this is another part of a session pdu + // + if (pLowerConn->State == NBT_SESSION_UP) + { + DbgPrint("\tTRH: Session status is UP, Bytes: available, indicated: 0x") ; + DbgPrintNum( BytesAvailable ) ; DbgPrint(" 0x") ; + DbgPrintNum( BytesIndicated ) ; + DbgPrint("\r\n") ; + + pConnectEle = pLowerConn->pUpperConnection; + pConnectEle->BytesInXport = BytesAvailable ; + *BytesTaken = 0 ; + + DbgPrint("\tTRH: BytesInXport: 0x") ; + DbgPrintNum( pConnectEle->BytesInXport ) ; + DbgPrint("\r\n") ; + + + // + // ** RECEIVING A PDU STATE ** + // + + switch (pLowerConn->StateRcv) + { + case NORMAL: + // + // check indication and if less than the session header, + // copy to the session header buffer and go to Indic_buffer state + // and wait for the next indication. This is a rare case (and a pain + // in the butt). + // + if (BytesIndicated < sizeof(tSESSIONHDR)) + { + ASSERT( pLowerConn->BytesInHdr == 0 ) ; + DbgPrint("\tTRH - NORMAL case: Not enough for session header, requesting 0x") ; + DbgPrintNum( sizeof( pLowerConn->Hdr ) ) ; + DbgPrint(" bytes\r\n") ; + + + if ( !GetRcvContext( &prcvCont)) + return STATUS_INSUFFICIENT_RESOURCES ; + + InitRcvContext( prcvCont, pLowerConn, NULL ) ; + prcvCont->usFlags = TDI_RECEIVE_NORMAL; + pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ; + pevrcvbuf->erb_rtn = (CTEReqCmpltRtn) NewSessionCompletionRoutine ; + pevrcvbuf->erb_size = sizeof( pLowerConn->Hdr ) ; + pevrcvbuf->erb_context = prcvCont ; + pevrcvbuf->erb_flags = &usFlags ; + InitNDISBuff( pevrcvbuf->erb_buffer, + &pLowerConn->Hdr, + sizeof(pLowerConn->Hdr), + NULL ) ; + + pLowerConn->StateRcv = INDICATE_BUFFER; + + // + // Okay to use pIrpRcv here as it will only be completed if + // the state is FILL_IRP + // + pConnectEle->pIrpRcv = (PCTE_IRP) prcvCont ; + + return TDI_MORE_PROCESSING ; + } + + PduSize = myntohl(((tSESSIONHDR *)pTsdu)->UlongLength) + + sizeof(tSESSIONHDR); + DbgPrint("\tTRH: New message; Opcode, PduSize + hdr : 0x") ; + DbgPrintNum( ((tSESSIONHDR *)pTsdu)->Type ) ; DbgPrint(" 0x") ; + DbgPrintNum( PduSize ) ; DbgPrint("\r\n") ; + + // + // Indicate to the client + // + ASSERT( PduSize >= sizeof(tSESSIONHDR)) ; + status = RcvHandlrNotOs( + ReceiveEventContext, + ConnectionContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + &prcvCont + ); + + ASSERT( *BytesTaken <= pConnectEle->BytesInXport ) ; + ASSERT( *BytesTaken <= BytesIndicated); + pConnectEle->BytesInXport -= *BytesTaken; + BytesIndicated -= *BytesTaken; + BytesAvailable -= *BytesTaken; + ((BYTE*)pTsdu) += *BytesTaken ; + + DbgPrint("\tTRH: RcvHandlrNotOs returned, BytesTaken: 0x") ; + DbgPrintNum( *BytesTaken ) ; + DbgPrint("\r\n") ; + + DbgPrint("\tTRH: RcvHandlrNotOs status, prcvCont: 0x") ; + DbgPrintNum( status ) ; DbgPrint(" 0x") ; + DbgPrintNum( (ULONG)prcvCont ) ; DbgPrint("\r\n") ; + + if ( prcvCont ) + { + ULONG BytesToCopy ; + ASSERT( status == STATUS_MORE_PROCESSING_REQUIRED ); + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + // + // Record which session satisfied the request + // + prcvCont->pLowerConnId = pLowerConn ; + REQUIRE( !VxdFindLSN( pConnectEle->pClientEle->pDeviceContext, + pConnectEle, + &prcvCont->pNCB->ncb_lsn )) ; + // + // New message so strip session header + // + PduSize -= *BytesTaken ; + DbgPrint("\tTRH: Remaining PduSize = 0x") ; + DbgPrintNum( PduSize ) ; DbgPrint("\r\n") ; + + DbgPrint("\tTRH: TotalPcktLen = 0x") ; + DbgPrintNum( pConnectEle->TotalPcktLen ) ; DbgPrint("\r\n") ; + + BytesToCopy = min( pConnectEle->TotalPcktLen, + prcvCont->ndisBuff.Length ) ; + + DbgPrint("\tTRH: BytesToCopy = 0x") ; + DbgPrintNum( BytesToCopy ) ; DbgPrint("\r\n") ; + + // + // pIrpRcv is set to NULL while the request is in the + // transport. This prevents two completions if an error + // occurs + // + pLowerConn->StateRcv = FILL_IRP ; + pConnectEle->pIrpRcv = NULL ; + pConnectEle->OffsetFromStart = 0 ; + + // + // If the data is available, then just grab it now + // (also, if flag is set to TDI_RECEIVE_NO_RESPONSE_EXP, we + // need to give hint to the xport, so let xport do the copying) + // + if ( ( BytesIndicated >= BytesToCopy ) && + ( prcvCont->usFlags == TDI_RECEIVE_NORMAL ) ) + { + CTEMemCopy( prcvCont->ndisBuff.VirtualAddress, + pTsdu, + BytesToCopy ) ; + *BytesTaken += BytesToCopy ; + CompletionRcv( prcvCont, STATUS_SUCCESS, BytesToCopy ) ; + return STATUS_SUCCESS ; + } + + pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ; + pevrcvbuf->erb_rtn = CompletionRcv ; + pevrcvbuf->erb_size = BytesToCopy ; + pevrcvbuf->erb_context = prcvCont ; + if (prcvCont->usFlags == TDI_RECEIVE_NO_RESPONSE_EXP) + pevrcvbuf->erb_flags = &prcvCont->usFlags ; + else + pevrcvbuf->erb_flags = &usFlags ; + + DbgPrint("\tTRH: Rcv Dest Buff: 0x") ; + DbgPrintNum((ULONG)pevrcvbuf->erb_buffer->VirtualAddress) ; DbgPrint("\r\n") ; + return TDI_MORE_PROCESSING ; + } + else + { + // the client received some, all or none of the data + // For Keep Alives the PduSize is zero so this check + // will work correctly and go to the else + // Also, if we were attempting to complete a zero-len message + // but failed because there was no receive pending then we + // must go in PARTIAL_RCV state + // + if ( (*BytesTaken < PduSize) || + ((status == STATUS_DATA_NOT_ACCEPTED) && + (pConnectEle->TotalPcktLen == 0) && + (pConnectEle->state == NBT_SESSION_UP)) ) + { + // + // took some of the data, so keep track of the + // rest of the data left here by going to the PARTIALRCV + // state. + // + pLowerConn->StateRcv = PARTIAL_RCV; + InsertTailList( &pLowerConn->pDeviceContext->PartialRcvHead, + &pLowerConn->PartialRcvList ) ; + pLowerConn->fOnPartialRcvList = TRUE; + + DbgPrint("TdiReceiveHandler:Switch to Partial Rcv Indicated\r\n") ; + + return STATUS_SUCCESS ; + } + else + { + // + // Must have taken all of the pdu data, so check for + // more data available - if so then reindicate ourselves. + // Note that TDI will pickup the bytes taken before any posted + // receives are performed. + // + status = STATUS_SUCCESS ; + + // + // The next bytes in the transport will be the + // beginning of a Session Header so leave the state + // as NORMAL + // + + if (BytesAvailable > *BytesTaken) + { + ULONG ClientBytesTaken = 0 ; + + // + // we already added the bytes available the first time + // TdiReceiveHandler got called. They will get added + // again, so subtract now! + // + pLowerConn->BytesRcvd -= BytesAvailable; + + status = TdiReceiveHandler( ReceiveEventContext, + ConnectionContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &ClientBytesTaken, + pTsdu, + pevrcvbuf ) ; + + *BytesTaken += ClientBytesTaken ; + // + // status will be more processing if pervrcvbuf + // was setup, else it will be success if + // bytes were taken. Note that BytesInXport + // is adjusted automatically in the call to + // TdiReceiveHandler + // + } + } + } + return status ; + + case FILL_IRP: + { + NCB * pNCB = pConnectEle->pIrpRcv ; + ULONG BuffAvailable = pNCB->ncb_length - pConnectEle->OffsetFromStart ; + ULONG BytesToCopy ; + + // we are still waiting for the rest of the session pdu so + // do not call the RcvHandlrNotOs, since we already have the buffer + // to put this data in. + prcvCont = *((PRCV_CONTEXT*)&pNCB->ncb_reserve) ; + ASSERT( prcvCont->Signature = RCVCONT_SIGN ) ; + + // + // too much data may have arrived... i.e. part of the next session pdu.. + // so check and set the receive length accordingly + // + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + BytesToCopy = min( RemainingPdu, BuffAvailable ) ; + pConnectEle->pIrpRcv = NULL ; // Buffer in the transport + + DbgPrint("\tTRH - FILL_IRP case: Requesting 0x") ; + DbgPrintNum( pevrcvbuf->erb_size ) ; DbgPrint("\r\n") ; + + // + // Append the new data onto the existing data + // + ((BYTE*)prcvCont->ndisBuff.VirtualAddress) = + pNCB->ncb_buffer + pConnectEle->OffsetFromStart ; + prcvCont->ndisBuff.Length = + pNCB->ncb_length - pConnectEle->OffsetFromStart ; + + // + // If the data is available, then just grab it now + // + if ( BytesIndicated >= BytesToCopy ) + { + CTEMemCopy( prcvCont->ndisBuff.VirtualAddress, + pTsdu, + BytesToCopy ) ; + *BytesTaken += BytesToCopy ; + CompletionRcv( prcvCont, STATUS_SUCCESS, BytesToCopy ) ; + return STATUS_SUCCESS ; + } + + // + // Have to post a buffer since the data isn't available + // We also have a new offset for the *next* indication + // + pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ; + pevrcvbuf->erb_rtn = CompletionRcv ; + pevrcvbuf->erb_size = BytesToCopy ; + + pevrcvbuf->erb_context = prcvCont ; + pevrcvbuf->erb_flags = &usFlags ; + ASSERT( pConnectEle->OffsetFromStart <= pNCB->ncb_length ) ; + + DbgPrint("\tTRH: offset, address: 0x") ; + DbgPrintNum( pConnectEle->OffsetFromStart ) ; DbgPrint(", 0x") ; + DbgPrintNum( (ULONG)prcvCont->ndisBuff.VirtualAddress ) ; DbgPrint("\r\n") ; + + // + // State remains in FILL_IRP (only goes to PARTIAL_RCV when no + // NCBs are actively receiving and only part of a PDU has been + // picked up). + // + // BytesInXport adjusted in CompletinRcv + // + + return TDI_MORE_PROCESSING ; + } + break ; + + case INDICATE_BUFFER: + { + DbgPrint("\tTRH: Hit INDICATE_BUFFER state, bytes in hdr: 0x") ; + DbgPrintNum( pLowerConn->BytesInHdr ) ; + DbgPrint("\r\n") ; + + // + // Our context is still setup so adjust things such that + // the location to start copying the new data into is right + // after the existing data in the session header buffer + // + prcvCont = (PRCV_CONTEXT) pConnectEle->pIrpRcv ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + ((BYTE*)prcvCont->ndisBuff.VirtualAddress) = + ((BYTE*)&pLowerConn->Hdr) + pLowerConn->BytesInHdr ; + prcvCont->ndisBuff.Length = + sizeof( pLowerConn->Hdr ) - pLowerConn->BytesInHdr ; + pevrcvbuf->erb_size = min( BytesAvailable, + sizeof(pLowerConn->Hdr) - pLowerConn->BytesInHdr) ; + pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ; + pevrcvbuf->erb_rtn = NewSessionCompletionRoutine ; + pevrcvbuf->erb_context = prcvCont ; + pevrcvbuf->erb_flags = &usFlags ; + return TDI_MORE_PROCESSING ; + } + + case PARTIAL_RCV: + // + // If we get indicated in this state, then the client doesn't have + // any receive buffers posted and we are in the middle of a + // PDU, so just track the new byte count and continue waiting + // for the client + // + DbgPrint("\tTRH: Indicated in Partial_Rcv state\r\n") ; + return STATUS_SUCCESS ; + + default: + ASSERT( FALSE ) ; + break; + } + } + else if ( pLowerConn->State == NBT_SESSION_INBOUND ) + { + status = Inbound( + ReceiveEventContext, + ConnectionContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + &prcvCont + ); + } + else if ( pLowerConn->State == NBT_SESSION_OUTBOUND ) + { + status = Outbound( + ReceiveEventContext, + ConnectionContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + &prcvCont + ); + } + + // + // maybe disconnect is going on: reject the data + // + else + { + *BytesTaken = BytesAvailable; + status = STATUS_SUCCESS; + } + + // + // Client should *never* pass back a completion buffer (only used by + // event handler which isn't supported in a VXD + // + ASSERT( prcvCont == NULL ) ; + + return status; +} + +//---------------------------------------------------------------------------- + +TDI_STATUS +ReceiveAnyHandler ( // Handles NCBRCVANY commands, is + IN PVOID ReceiveEventContext, // called after all other receive + IN PVOID ConnectionContext, // handlers + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID Data, + PVOID * ppBuffer // Pointer to RCV_CONTEXT + ) +/*++ + +Routine Description: + + This routine handles any data not processed by TdiReceiveHandler and + RcvHandlrNotOs. It processes ReceiveAny NCBs. + + Note that TdiReceiveHandler calls RcvHandlrNotOs which may call + this routine (i.e., this is only called from RcvHandlrNotOs). + +Arguments: + +--*/ +{ + TDI_STATUS tdistatus ; + tCLIENTELE * pClientEle; + PLIST_ENTRY pEntry ; + PRCV_CONTEXT prcvCont ; + + DbgPrint("ReceiveAnyHandler Entered \r\n") ; + *ppBuffer = NULL ; + + pClientEle = (tCLIENTELE*) ReceiveEventContext ; + + ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ) ; + + // + // Are there any ReceiveAny NCBs queued for this connection or for + // any connection? + // + if ( !IsListEmpty( &pClientEle->RcvAnyHead )) + { + pEntry = RemoveHeadList( &pClientEle->RcvAnyHead ) ; + DbgPrint("ReceiveAnyHandler - Found Receive Any buffer\r\n") ; + } + else if ( !IsListEmpty( &pClientEle->pDeviceContext->RcvAnyFromAnyHead )) + { + pEntry = RemoveHeadList( &pClientEle->pDeviceContext->RcvAnyFromAnyHead ) ; + DbgPrint("ReceiveAnyHandler - Found Receive Any from Any buffer\r\n") ; + } + else + return STATUS_SUCCESS ; + // + // Found one + // + prcvCont = *ppBuffer = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + return TDI_MORE_PROCESSING ; +} + +//---------------------------------------------------------------------------- +VOID +CompletionRcv( + IN PVOID pContext, + IN uint tdistatus, + IN uint BytesRcvd ) +/*++ + +Routine Description: + + This routine completes TdiVxdReceive. The NCB is completed or further + receives are performed. + +Arguments: + + pContext - Pointer to a RCV_CONTEXT structure + tdistatus - Completion status + BytesRcvd - Bytes copied to the destination buffer + +--*/ +{ + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + PRCV_CONTEXT prcvcont = (PRCV_CONTEXT) pContext ; + + DbgPrint("CompletionRcv Entered (BytesRcvd: 0x") ; + DbgPrintNum( BytesRcvd ) ; DbgPrint(")\r\n") ; + + ASSERT( prcvcont->Signature == RCVCONT_SIGN ) ; + ASSERT( tdistatus || + (!tdistatus && ((prcvcont->pLowerConnId != NULL) && + (prcvcont->pLowerConnId->pUpperConnection != NULL))) ) ; + + // + // If an error occurred, bail + // + if ( tdistatus && tdistatus != TDI_BUFFER_OVERFLOW ) + { + DbgPrint("CompletionRcv: error occurred, status: 0x") ; + DbgPrintNum( tdistatus ) ; DbgPrint("\n\r") ; + + // + // Make sure the receive IRP doesn't get completed twice if the + // connection is still up + // + if ( prcvcont->pLowerConnId && + prcvcont->pLowerConnId->pUpperConnection ) + { + prcvcont->pLowerConnId->pUpperConnection->pIrpRcv = NULL ; + } + + CTEIoComplete( prcvcont->pNCB, tdistatus, 0 ) ; + return ; + } + + pLowerConn = prcvcont->pLowerConnId ; + pConnectEle = pLowerConn->pUpperConnection; + + ASSERT( pConnectEle->Verify == NBT_VERIFY_CONNECTION ) ; + pConnectEle->BytesRcvd += BytesRcvd; + pConnectEle->OffsetFromStart += BytesRcvd ; + + // + // Since we request NCB buffer sizes, we can get more bytes then what + // was shown as available. If that happens then we've already consumed + // all transport bytes so reset to 0. + // + if ( pConnectEle->BytesInXport <= BytesRcvd ) + pConnectEle->BytesInXport = 0 ; + else + pConnectEle->BytesInXport -= BytesRcvd ; + + // + // this case handles when all bytes in a session pdu have arrived... + // + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + // + // we have received all of the data for this message + // so complete back to the client + // + DbgPrint("CompletionRcv: PDU Receive done, about to complete client with 0x") ; + DbgPrintNum( pConnectEle->OffsetFromStart ) ; DbgPrint(" of total PDU length: 0x") ; + DbgPrintNum( pConnectEle->TotalPcktLen ) ; DbgPrint("\r\n") ; + + pConnectEle->pIrpRcv = NULL ; + + // + // change the state before completing the ncb because our client can + // turn around and post a hangup rightaway (in fact, net send does that!) + // + pLowerConn->StateRcv = NORMAL; + CTEIoComplete( prcvcont->pNCB, STATUS_SUCCESS, pConnectEle->OffsetFromStart ) ; + + // + // Freed by CTEIoComplete, make sure we don't use it again + // + prcvcont = NULL ; + + pConnectEle->OffsetFromStart = 0; + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + pLowerConn->BytesInHdr = 0 ; + + // + // Check if there is still more data in the transport and reindicate + // if there is. + // + if (pConnectEle->BytesInXport) + { + ULONG BytesTaken = 0 ; + DbgPrint("Nbt:ComplRcv - Bytes left in Xport after completing a receive, BytesInXport= 0x") ; + DbgPrintNum(pConnectEle->BytesInXport) ; + DbgPrint("\r\n") ; + + // + // The next thing to do is copy the session header into + // pLowerConn->Hdr which this will do. + // + tdistatus = Reindicate( NULL, + pLowerConn, + 0, // Rcv flags + 0, // Bytes Indicated + pConnectEle->BytesInXport, // Bytes Avail + &BytesTaken, + NULL ) ; // tsdu + } + + } + else + if (pConnectEle->BytesRcvd > pConnectEle->TotalPcktLen) + { + DbgPrint("CompletionRcv: Too Many Bytes Rcvd!! Rcvd 0x") ; + DbgPrintNum( pConnectEle->BytesRcvd ) ; + DbgPrint(" Total message length:0x") ; + DbgPrintNum( pConnectEle->TotalPcktLen ) ; + //DbgPrint(" NCB Buffer size: 0x") ; + //DbgPrintNum( prcvcont->pNCB->ncb_length ) ; + DbgPrint("\r\n") ; + ASSERT(FALSE); + + pConnectEle->pIrpRcv = NULL ; + pLowerConn->StateRcv = NORMAL; + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + pLowerConn->BytesInHdr = 0 ; + CTEIoComplete( prcvcont->pNCB, TDI_INVALID_STATE, 0 ) ; + } + else + { + ASSERT( prcvcont->pNCB ) ; + + // + // Haven't received all of the data for this PDU yet, check to + // see how much room the NCB has left in it. + // + if ( pConnectEle->OffsetFromStart >= prcvcont->pNCB->ncb_length ) + { + // If OffsetFromStart is greater then the buffer size then we + // have went beyond the end of the buffer. + ASSERT( pConnectEle->OffsetFromStart == prcvcont->pNCB->ncb_length ) ; + + DbgPrint("CompletionRcv: Completed NCB but more data available, Total BytesRecieved: 0x") ; + DbgPrintNum( pConnectEle->BytesRcvd ) ; + DbgPrint("\r\n Ncb buffer size completed: 0x") ; + DbgPrintNum( prcvcont->pNCB->ncb_length ) ; + DbgPrint("\r\n") ; + + pConnectEle->pIrpRcv = NULL ; + pConnectEle->OffsetFromStart = 0; + pLowerConn->StateRcv = PARTIAL_RCV ; + InsertTailList( &pLowerConn->pDeviceContext->PartialRcvHead, + &pLowerConn->PartialRcvList ) ; + pLowerConn->fOnPartialRcvList = TRUE; + + // + // Done with this NCB, set the status appropriately and hope + // the client will submit another NCB to pick up the rest of + // the PDU. prcvcont freed by the completion. + // + CTEIoComplete( prcvcont->pNCB, + TDI_BUFFER_OVERFLOW, //translates to: NRC_INCOMP + prcvcont->pNCB->ncb_length ) ; + + } + else + { + // + // Room still left in the NCB so wait for the transport to + // indicate when more data is available (OffsetFromStart has + // already been adjusted) + // + pConnectEle->pIrpRcv = prcvcont->pNCB ; + pLowerConn->StateRcv = FILL_IRP ; + } + } +} + +//---------------------------------------------------------------------------- +VOID +NewSessionCompletionRoutine ( + IN PVOID pContext, + IN uint tdistatus, + IN uint BytesReceived + ) +/*++ + +Routine Description: + + This routine handles the completion of the receive to get the remaining + data left in the transport when a session PDU starts in the middle of + an indication from the transport. This routine is run as the completion + of a recv Irp passed to the transport by NBT, to get the remainder of the + data in the transport. + + The routine then calls the normal receive handler, which can either + consume the data or pass back an Irp. If an Irp is passed back then + the data is copied into that irp in this routine. + + Called when a partial message header is received - puts the data back + on the + +Arguments: + + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + PRCV_CONTEXT prcvCont = pContext ; + NTSTATUS status; + ULONG BytesTaken = 0; + ULONG BytesAvailable ; + tCONNECTELE *pConnEle; + tLOWERCONNECTION *pLowerConn; + + DbgPrint("NewSessionCompletionRoutine Entered\r\n") ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + pLowerConn = prcvCont->pLowerConnId ; + pConnEle = pLowerConn->pUpperConnection ; + + // + // If an error occurred just drop the PDU on the floor (though an + // error really shouldn't happen) + // + if ( tdistatus != TDI_SUCCESS ) + { + FreeRcvContext( prcvCont ) ; + pLowerConn->StateRcv = NORMAL ; + return ; + } + + // + // Adjust BytesInXport if there were too few bytes for the + // session header + // + if ( BytesReceived > pConnEle->BytesInXport ) + pConnEle->BytesInXport = BytesReceived ; + + // + // there may be data still in the indication buffer, + // so add that amount to what we just received. The transport + // has already copied the data into the pLowerConn->Hdr so we + // just need to update the state. + // + ASSERT( BytesReceived <= sizeof( pLowerConn->Hdr ) ) ; + pLowerConn->BytesInHdr += BytesReceived ; + + // + // If we have a full header, process it + // + if ( pLowerConn->BytesInHdr == sizeof(pLowerConn->Hdr) ) + { + ULONG BytesTaken = 0 ; + pLowerConn->StateRcv = NORMAL ; // New session header + pLowerConn->BytesInHdr = 0 ; + FreeRcvContext( prcvCont ) ; + + // + // We indicate just the session header bytes, this will force + // the client to post a receive if they are interested in the + // rest of the data + // + status = Reindicate(NULL, + pLowerConn, + 0, // rcv flags + sizeof( pLowerConn->Hdr ), + pConnEle->BytesInXport, + &BytesTaken, + &pLowerConn->Hdr ) ; + ASSERT( BytesTaken <= sizeof( pLowerConn->Hdr ) ) ; + } + else + { + // + // We *still* don't have the full session header so + // wait for reindication + // + return ; + } +} +//---------------------------------------------------------------------------- +NTSTATUS +Reindicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu + ) +/*++ + +Routine Description: + + This routine copies data from the Indicate buffer to a 128 byte buffer and + then fills this with data from the current indication. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS ; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnEle; + EventRcvBuffer evrcvbuf ; + + DbgPrint("Reindicated Entered\r\n") ; + DbgPrint("\tReindicate: Bytes: available, indicated: 0x") ; + DbgPrintNum( BytesAvailable ) ; DbgPrint(" 0x") ; + DbgPrintNum( BytesIndicated ) ; + DbgPrint("\r\n") ; + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pConnEle = pLowerConn->pUpperConnection; + + // + // we already added the bytes available the first time TdiReceiveHandler + // got called. They will get added again, so subtract now! + // + pLowerConn->BytesRcvd -= BytesAvailable; + + status = TdiReceiveHandler(NULL, + pLowerConn, + 0, // rcv flags + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + &evrcvbuf ); + + // + // If a receive context was returned, then post the receive, if bytes + // were taken, the state was updated in the receive handler + // + if ( status == TDI_MORE_PROCESSING ) + { + TDI_REQUEST Request ; + PRCV_CONTEXT prcvCont = evrcvbuf.erb_context ; + ULONG cbRcvLength = evrcvbuf.erb_size ; + + ASSERT( (evrcvbuf.erb_rtn == CompletionRcv) || + (evrcvbuf.erb_rtn == NewSessionCompletionRoutine)) + Request.RequestNotifyObject = evrcvbuf.erb_rtn ; + Request.RequestContext = prcvCont ; + Request.Handle.ConnectionContext = pLowerConn->pFileObject ; + + status = TdiVxdReceive( &Request, + &usFlags, + &cbRcvLength, + &prcvCont->ndisBuff ) ; + if ( status != TDI_PENDING ) + { + DbgPrint("Reindicate: Error returned from TdiVxdReceive - 0x") ; + DbgPrintNum( status ) ; + DbgPrint("\r\n") ; + CTEIoComplete( prcvCont->pNCB, status, 0 ) ; + } + else + { + status = TDI_SUCCESS ; + } + } + + return status ; +} + +//---------------------------------------------------------------------------- + +TDI_STATUS +TdiConnectHandler ( + IN PVOID pConnectEventContext, + IN int RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN PVOID pUserData, + IN int OptionsLength, + IN PVOID pOptions, + IN PVOID * AcceptingID, + ConnectEventInfo * pEventInfo //OUT CONNECTION_CONTEXT *pConnectionContext + ) +/*++ + +Routine Description: + + This routine is connect event handler. It is invoked when a request for + a connection has been received by the provider. NBT accepts the connection + on one of its connections in its LowerConnFree list + + Initially a TCP connection is setup with this port. Then a Session Request + packet is sent across the connection to indicate the name of the destination + process. This packet is received in the RcvHandler. + +Arguments: + + pConnectEventContext - the context passed to the transport when this event was setup + RemoteAddressLength - the length of the source address (4 bytes for IP) + pRemoteAddress - a ptr to the source address + UserDataLength - the number of bytes of user data - includes the session Request hdr + pUserData - ptr the the user data passed in + OptionsLength - number of options to pass in + pOptions - ptr to the options + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tDEVICECONTEXT * pDeviceContext; + tLOWERCONNECTION * pLowerConn ; + PTDI_CONNECTION_INFO pConnInfo ; + + DbgPrint("TdiConnectHandler: Entered\r\n") ; + + // convert the context value into the device context record ptr + pDeviceContext = (tDEVICECONTEXT *)pConnectEventContext; + + ASSERTMSG("Bad Device context passed to the Connection Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + // call the non-OS specific routine to find a free connection. + + status = ConnectHndlrNotOs( + pConnectEventContext, + RemoteAddressLength, + pRemoteAddress, + UserDataLength, + pUserData, + &pLowerConn ); + + if (!NT_SUCCESS(status)) + { + DbgPrint("TdiConnectHandler: NO FREE CONNECTIONS in connect handler\r\n"); + return STATUS_DATA_NOT_ACCEPTED ; + } + + // + // Fill in the completion information + // + pEventInfo->cei_rtn = AcceptCompletionRoutine ; + pEventInfo->cei_context = pLowerConn ; + pEventInfo->cei_acceptinfo = NULL ; + pEventInfo->cei_conninfo = NULL ; + *AcceptingID = pLowerConn ; // Connection Context + + return TDI_MORE_PROCESSING ; +} + +//---------------------------------------------------------------------------- +VOID +AcceptCompletionRoutine( + IN PVOID pContext, + IN uint tdistatus, + IN uint extra + ) +/*++ + +Routine Description: + + This routine handles the completion of an Accept to the transport. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + tLOWERCONNECTION *pLowerConn; + + DbgPrint("AcceptCompletionRoutine: Entered\r\n") ; + pLowerConn = (tLOWERCONNECTION *)pContext; + + if (!NT_SUCCESS(tdistatus) && + (pLowerConn->State == NBT_SESSION_INBOUND)) + { + tDEVICECONTEXT *pDeviceContext; + DbgPrint("AcceptCompletionRoutine - Error returned: 0x") ; + DbgPrintNum( tdistatus ) ; + DbgPrint("\r\n") ; + + // the connection setup failed, so put the lower connection block + // back on the free list + pLowerConn->State = NBT_IDLE; + pDeviceContext = pLowerConn->pDeviceContext; + + // + // First remove it from pDeviceContext->LowerConnection + // + RemoveEntryList( &pLowerConn->Linkage ) ; + CTEInterlockedDecrementLong(&pLowerConn->RefCount); + InsertHeadList(&pDeviceContext->LowerConnFreeHead, + &pLowerConn->Linkage); + + } +} + +//---------------------------------------------------------------------------- +TDI_STATUS +TdiDisconnectHandler ( + PVOID EventContext, + PVOID ConnectionContext, + ULONG DisconnectDataLength, + PVOID pDisconnectData, + ULONG DisconnectInformationLength, + PVOID pDisconnectInformation, + ULONG DisconnectIndicators + ) +/*++ + +Routine Description: + + This routine is called when a session is disconnected from a remote + machine. + +Arguments: + + IN PVOID EventContext, + IN PCONNECTION_CONTEXT ConnectionContext, + IN ULONG DisconnectDataLength, + IN PVOID DisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectIndicators + +Return Value: + + NTSTATUS - Status of event indicator + +--*/ + +{ + + TDI_STATUS status ; + tDEVICECONTEXT *pDeviceContext; + + DbgPrint("TdiDisconnectHandler: Entered\r\n") ; + + pDeviceContext = (tDEVICECONTEXT *)EventContext; + ASSERTMSG("Bad Device context passed to the Disconnect Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + status = DisconnectHndlrNotOs( + EventContext, + ConnectionContext, + DisconnectDataLength, + pDisconnectData, + DisconnectInformationLength, + pDisconnectInformation, + DisconnectIndicators); + + if (!NT_SUCCESS(status)) + { + DbgPrint("NO FREE CONNECTIONS in connect handler\n\r"); + status = TDI_CONN_REFUSED ; // return(STATUS_DATA_NOT_ACCEPTED); + } + + DbgPrint("TdiDisconnectHandler: returning\r\n") ; + return status ; +} + +//---------------------------------------------------------------------------- + +TDI_STATUS +VxdDisconnectHandler ( // Cleans up Netbios stuff for remote + IN PVOID DisconnectEventContext, // disconnects + IN PVOID ConnectionContext, + IN PVOID DisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID pDisconnectInformation, + IN ULONG DisconnectIndicators + ) +/*++ + +Routine Description: + + Cleans up open Netbios stuff. + + Note this routine gets called from the NCB hangup completion also. + +Arguments: + +--*/ +{ + TDI_STATUS tdistatus ; + tCLIENTELE * pClientEle = (tCLIENTELE*) DisconnectEventContext ; + tCONNECTELE * pConnEle = (tCONNECTELE*) ConnectionContext ; + tDEVICECONTEXT * pDeviceContext = pClientEle->pDeviceContext ; + tLOWERCONNECTION * pLowerConn; + TDI_REQUEST Request ; + NCBERR errNCB ; + UCHAR lsn ; + BOOL fNotified ; + + DbgPrint("VxdDisconnectHandler Entered \r\n") ; + ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ) ; + ASSERT( (pConnEle->Verify == NBT_VERIFY_CONNECTION) || + (pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN)) ; + + // + // Session is dead, kill off everything + // + + if ( errNCB = VxdFindLSN( pDeviceContext, + pConnEle, + &lsn )) + { + // + // This shouldn't happen but watch for it in case we get in a + // weird situation + // + DbgPrint("VxdDisconnectHandler - Warning: VxdFindLsn failed\r\n") ; + } + + REQUIRE( !VxdCompleteSessionNcbs( pDeviceContext, + pConnEle )) ; + + pLowerConn = pConnEle->pLowerConnId ; + if ( pLowerConn && + (pLowerConn->fOnPartialRcvList == TRUE) && + pLowerConn->StateRcv == PARTIAL_RCV ) + { + RemoveEntryList( &pLowerConn->PartialRcvList ) ; + pLowerConn->fOnPartialRcvList = FALSE; + InitializeListHead(&pLowerConn->PartialRcvList); + } + + // + // The close may free the connection so check if the client has been + // notified now. + // + fNotified = !!(pConnEle->Flags & NB_CLIENT_NOTIFIED) ; + Request.Handle.ConnectionContext = pConnEle ; + tdistatus = NbtCloseConnection( &Request, + NULL, + pDeviceContext, + NULL ) ; + if ( tdistatus ) + { + DbgPrint("VxdDisconnectHandler: NbtCloseConnection returned 0x") ; + DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ; + } + + tdistatus = NbtDisassociateAddress( &Request ) ; + if ( tdistatus ) + { + DbgPrint("VxdDisconnectHandler: NbtDisassociateAddress returned 0x") ; + DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ; + } + + if ( !errNCB && fNotified ) + { + REQUIRE( NBUnregister( pDeviceContext, + lsn, + NB_SESSION )) ; + } + + // + // If this name has been deleted, check to see if this is the last session, + // if so, delete the name + // + if ( pClientEle->fDeregistered && !ActiveSessions(pClientEle) ) + { + UCHAR NameNum ; + if ( VxdFindNameNum( pDeviceContext, pClientEle->pAddress, &NameNum )) + { + ASSERT( FALSE ) ; + return STATUS_UNSUCCESSFUL ; + } + + (void) VxdCleanupAddress( pDeviceContext, + NULL, + pClientEle, + NameNum, + TRUE ) ; + } + + return STATUS_SUCCESS ; +} + +//---------------------------------------------------------------------------- + +// +// This structure is the context that is passed to the datagram completion +// routine when we want TDI to fill in the NCB's buffer +// +typedef struct _RCV_DG_COMP_CONTEXT +{ + EventRcvBuffer evrcvbuf ; + NCB * pncb ; + tCLIENTLIST * pClientList ; + NDIS_BUFFER ndisRcvBuf ; +} RCV_DG_COMP_CONTEXT, *PRCV_DG_COMP_CONTEXT ; + +TDI_STATUS +TdiRcvDatagramHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN UINT Flags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pData, + OUT EventRcvBuffer * * ppBuffer + ) +/*++ + +Routine Description: + + This routine is the receive datagram event indication handler. + + It is called when an Datagram arrives from the network, it will look for a + the address with an appropriate read datagram outstanding or a Datagrm + Event handler setup. + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + TDI_STATUS tdistatus ; + tDEVICECONTEXT * pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tCLIENTLIST * pClientList = NULL ; + NCB * pncb = NULL ; + + // + // Tell TDI we don't want anything unless we change our minds down below + // + *ppBuffer = NULL ; + + ASSERTMSG("NBT:Invalid Device Context passed to DgramRcv Handler!!\n", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + // call a non-OS specific routine to decide what to do with the datagrams + tdistatus = DgramHndlrNotOs( + pDgramEventContext, + SourceAddressLength, + pSourceAddress, + OptionsLength, + pOptions, + 0, // Receive data flags + BytesIndicated, + BytesAvailable, + pBytesTaken, + pData, + &pncb, + &pClientList ) ; + if ( !NT_SUCCESS( tdistatus ) ) + { + // fail the request back to the transport provider since we + // could not find a receive buffer or receive handler. + return(tdistatus); + + } + else + { + PRCV_DG_COMP_CONTEXT prcvdgContext = NULL ; + + prcvdgContext = CTEAllocMem( sizeof( RCV_DG_COMP_CONTEXT ) ) ; + if ( !prcvdgContext ) + return STATUS_DATA_NOT_ACCEPTED ; + + // + // ncb_callname filled in by the NotOs event handler + // + + prcvdgContext->evrcvbuf.erb_rtn = CompletionRcvDgram ; + prcvdgContext->evrcvbuf.erb_size = BytesAvailable - *pBytesTaken ; + prcvdgContext->evrcvbuf.erb_context = prcvdgContext ; + prcvdgContext->evrcvbuf.erb_buffer = &prcvdgContext->ndisRcvBuf ; + prcvdgContext->pncb = pncb ; + prcvdgContext->evrcvbuf.erb_flags = NULL ; + prcvdgContext->pClientList = pClientList ; + InitNDISBuff( prcvdgContext->evrcvbuf.erb_buffer, + pncb->ncb_buffer, + pncb->ncb_length, + NULL ) ; + *ppBuffer = &prcvdgContext->evrcvbuf ; + return TDI_MORE_PROCESSING ; + } + + // + // Transport will complete the processing of the request, we don't + // want the datagram. + // + + return STATUS_DATA_NOT_ACCEPTED; +} + +//---------------------------------------------------------------------------- +TDI_STATUS +TdiRcvNameSrvHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN UINT Flags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT EventRcvBuffer * * ppBuffer + ) +/*++ + +Routine Description: + + This routine is the Name Service datagram event indication handler. + It gets all datagrams destined for UDP port 137 + + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + TDI_STATUS tdistatus ; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tNAMEHDR *pNameSrv = (tNAMEHDR *)pTsdu; + + // + // No receive buffer + // + *ppBuffer = NULL ; + + ASSERTMSG("NBT:The Device Context does not have the correct Verification value!!\n", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + + // call a non-OS specific routine to decide what to do with the datagrams + status = NameSrvHndlrNotOs( + pDeviceContext, + pSourceAddress, + pNameSrv, + BytesIndicated); + + return status ; + + // to keep the compiler from generating warnings... + UNREFERENCED_PARAMETER( SourceAddressLength ); + UNREFERENCED_PARAMETER( BytesIndicated ); + UNREFERENCED_PARAMETER( BytesAvailable ); + UNREFERENCED_PARAMETER( pBytesTaken ); + UNREFERENCED_PARAMETER( pTsdu ); + UNREFERENCED_PARAMETER( OptionsLength ); + UNREFERENCED_PARAMETER( pOptions ); + +} + +//---------------------------------------------------------------------------- +VOID +CompletionRcvDgram( + IN PVOID Context, + IN UINT tdistatus, + IN UINT RcvdSize + ) +/*++ + +Routine Description: + + This routine completes the receive datagram NCB. If multiple clients + were listenning then the largest NCB is used as a template and the + rest are completed from it. + + +Arguments: + + Context - Pointer to a RCV_DG_COMP_CONTEXT structure set above + tdistatus - Completion status + Rcvdsize - Number of bytes copied + +--*/ +{ + PRCV_DG_COMP_CONTEXT prcvdgContext = Context ; + NCB * pncb = prcvdgContext->pncb ; + tCLIENTLIST * pClientList = prcvdgContext->pClientList; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + NTSTATUS status; + + // + // Check to see if our buffer was big enough to get all the data + // + if ( !tdistatus && + prcvdgContext->evrcvbuf.erb_size > pncb->ncb_length ) + { + tdistatus = STATUS_BUFFER_OVERFLOW ; + } + + // + // there may be several clients that want to see this datagram so check + // the client list to see... + // + if ( pClientList ) + { + pHead = &pClientList->pAddress->ClientHead; + pEntry = pHead->Flink; + +#ifdef PROXY_NODE + if (!pClientList->fProxy) + { +#endif + // *** Client Has posted a receive Buffer, rather than using + // *** receive handler - VXD case! + // *** + while (pEntry != pHead) + { + tCLIENTELE * pClientEle; + NCB * pncbDest ; + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + CTEInterlockedIncrementLong(&pClientEle->RefCount); + + if (pClientEle == pClientList->pClientEle) + { + // this is the client whose buffer we are using - it is + // passed up to the client after all other clients + // have been processed. + // + } + else + if (!IsListEmpty(&pClientEle->RcvDgramHead)) + { + PLIST_ENTRY pRcvEntry; + tRCVELE * pRcvEle; + TDI_STATUS tdistatusTmp ; + UINT BytesToCopy = 0 ; + + pRcvEntry = RemoveHeadList(&pClientEle->RcvDgramHead); + pRcvEle = CONTAINING_RECORD(pRcvEntry,tRCVELE,Linkage); + pncbDest = (NCB*) pRcvEle->pIrp ; + ASSERT( pncbDest ) ; + + // + // Only copy the data and call name if we successfully + // completed the datagram + // + if ( !tdistatus || tdistatus == STATUS_BUFFER_OVERFLOW ) + { + BytesToCopy = min( pncbDest->ncb_length, RcvdSize ) ; + + CTEMemCopy( pncbDest->ncb_buffer, + pncb->ncb_buffer, + BytesToCopy ) ; + CTEMemCopy( pncbDest->ncb_callname, + pncb->ncb_callname, + NETBIOS_NAME_SIZE ) ; + + if ( pncbDest->ncb_length < RcvdSize || + (pncbDest->ncb_length == RcvdSize && + tdistatus == STATUS_BUFFER_OVERFLOW) ) + { + tdistatusTmp = STATUS_BUFFER_OVERFLOW ; + } + else + tdistatusTmp = STATUS_SUCCESS ; + } + else + { + tdistatusTmp = tdistatus ; + } + + CTEIoComplete( pncbDest, tdistatusTmp, BytesToCopy ) ; + + // free the receive block + CTEMemFree((PVOID)pRcvEle); + + } + + pEntry = pEntry->Flink; + + CTEInterlockedDecrementLong(&pClientEle->RefCount); + + + } // of while(pEntry != pHead) + + // + // The address was referenced in DgramRcvNotOs to be sure + // it did not disappear until this dgram rcv was done, which + // is now. + // + NbtDereferenceAddress( pClientList->pAddress ) ; + +#ifdef PROXY_NODE + } + else + { + // + // Call the ProxyDoDgramDist + // + status = ProxyDoDgramDist( + (tDGRAMHDR *)pncb->ncb_buffer, + RcvdSize, + (tNAMEADDR *)pClientList->pAddress, //NameAddr + pClientList->pRemoteAddress //device context + ); + + } +#endif + + + // + // Free the buffers allocated + // + if (!pClientList->fProxy) + { + CTEMemFree(pClientList->pRemoteAddress); + } + CTEMemFree(Context); + } + + // + // Finally complete our template NCB (or only NCB if single receive) + // + CTEIoComplete( pncb, tdistatus, RcvdSize ) ; + CTEMemFree( prcvdgContext ) ; + +} +//---------------------------------------------------------------------------- +TDI_STATUS +TdiErrorHandler ( + IN PVOID Context, + IN ULONG Status + ) + +/*++ + +Routine Description: + + This routine is called on any error indications passed back from the + transport. It implements LAN_STATUS_ALERT. + +Arguments: + + Context - Supplies the pfcb for the address. + + Status - Supplies the error. + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + DbgPrint("Nbt: Error Event HAndler hit unexpectedly\r\n"); + return TDI_INVALID_REQUEST ; +} diff --git a/private/ntos/nbt/vxd/tdiout.c b/private/ntos/nbt/vxd/tdiout.c new file mode 100644 index 000000000..147bcb785 --- /dev/null +++ b/private/ntos/nbt/vxd/tdiout.c @@ -0,0 +1,601 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdiout.c + +Abstract: + + + This file represents the TDI interface on the bottom edge of NBT. + The procedures herein conform to the TDI I/F spec. and then convert + the information to NT specific Irps etc. This implementation can be + changed out to run on another OS. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include <nbtprocs.h> // procedure headings + +// function prototypes for completion routines used in this file +VOID +SendComplete( + PVOID pContext, + TDI_STATUS tdistatus, + UINT cbSentSize + ); +VOID +TcpConnectComplete( + PVOID pContext, + TDI_STATUS tdistatus, + PVOID pv + ); + +VOID DisconnectWaitComplete( PVOID pContext, + TDI_STATUS status, + ULONG Extra ) ; + +void VxdDelayedCallHandler( struct CTEEvent *pEvent, void * pContext ) ; + +//---------------------------------------------------------------------------- +NTSTATUS +TdiSendDatagram( + IN PTDI_REQUEST pRequest, + IN PTDI_CONNECTION_INFORMATION pSendDgramInfo, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER * pSendBuffer, + IN ULONG SendFlags + ) +/*++ + +Routine Description: + + This routine sends a datagram to the transport + +Arguments: + + pSendBuffer - this is really an Mdl in NT land. It must be tacked on + the end of the Mdl created for the Nbt datagram header. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + TDI_STATUS tdistatus = TDI_SUCCESS ; + PTDI_SEND_CONTEXT psendCont = NULL ; + + if ( !GetSendContext( &psendCont ) ) + { + tdistatus = STATUS_INSUFFICIENT_RESOURCES ; + goto ErrorExit ; + } + + // + // Save away the old completion routine and context and replace them + // with new ones. The new completion routines will call the old ones + // if they are non-NULL and then free the structure. + // + psendCont->OldRequestNotifyObject = pRequest->RequestNotifyObject ; + psendCont->OldContext = pRequest->RequestContext ; + psendCont->NewContext = NULL ; + pRequest->RequestContext = psendCont ; + + // + // Set the send completion callback + // + pRequest->RequestNotifyObject = SendComplete; + + InitNDISBuff( &psendCont->ndisHdr, + pSendBuffer->pDgramHdr, + pSendBuffer->HdrLength, + &psendCont->ndisData1 ) ; + + InitNDISBuff( &psendCont->ndisData1, + pSendBuffer->pBuffer, + pSendBuffer->Length, + NULL ) ; + + tdistatus = TdiVxdSendDatagram( pRequest, + pSendDgramInfo, + SendLength, + pSentSize, + &psendCont->ndisHdr ) ; + + if ( !NT_SUCCESS( tdistatus ) ) + goto ErrorExit ; + + return tdistatus ; + +ErrorExit: + + DbgPrint("TdiSendDatagram ErrorExit: tdistatus= ") ; + DbgPrintNum( tdistatus ) ; + DbgPrint("\n\r") ; + + // + // Call *our* completion routine which frees memory etc. + // + if ( psendCont && pRequest->RequestNotifyObject ) + { + ((NBT_COMPLETION)pRequest->RequestNotifyObject)( + psendCont, + tdistatus, + 0 ) ; + + return( STATUS_PENDING ); + } + + return tdistatus ; +} + +//---------------------------------------------------------------------------- +VOID +SendComplete( + PVOID pContext, + TDI_STATUS tdistatus, + UINT cbSentSize + ) +/*++ + +Routine Description: + + This routine handles the completion of a datagram/session send to the + transport. It must call the client completion routine and free the TDI_SEND_CONTEXT + structure pointed at by pContext. + + Note that this routine may also be called if an error is returned from + the send call. This is done to localize cleanup. + +Arguments: + + pContext - Pointer to a TDI_SEND_CONTEXT + tdistatus - Completion status of the TDI request + cbSentSize- Bytes taken by TDI + +--*/ +{ + PTDI_SEND_CONTEXT psendCont = pContext ; + if ( tdistatus != TDI_SUCCESS ) + { + DbgPrint("SendComplete: TDI Error reported: 0x") ; + DbgPrintNum( tdistatus ) ; + DbgPrint("\r\n") ; + } + + if ( psendCont ) + { + if ( psendCont->OldRequestNotifyObject ) + { + // + // This calls the name server datagram completion routine which + // in turn will call CTEIoComplete (VxdIoComplete) which will call + // the NCB post routine (and fill out the NCB) + // + psendCont->OldRequestNotifyObject( psendCont->OldContext, + tdistatus, + cbSentSize ) ; + } + + FreeSendContext( psendCont ) ; + } +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiConnect( + IN PTDI_REQUEST pRequest, + IN ULONG lTimeout, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + OUT PVOID pIrp //IN PTDI_CONNECTION_INFORMATION pReturnInfo + ) +/*++ + +Routine Description: + + This routine sends a connect request to the tranport provider, to setup + a connection to the other side... + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + TDI_STATUS status ; + DbgPrint("TdiConnect Entered\n\r") ; + status = TdiVxdConnect( pRequest, + (PVOID)lTimeout, + pSendInfo, + NULL ) ; // pReturnInfo) ; + + if ( !NT_SUCCESS( status ) ) + { + DbgPrint("TdiVxdConnect: Returned error " ) ; + DbgPrintNum( status ) ; + DbgPrint("\n\r") ; + + // + // call the completion routine with this status + // + // + (*((NBT_COMPLETION)pRequest->RequestNotifyObject)) + ((PVOID)pRequest->RequestContext, + status, + 0L); + return STATUS_PENDING; + } + else + { + DbgPrint("TdiVxdConnect - Connection ID: 0x") ; + DbgPrintNum( (ULONG) pRequest->Handle.ConnectionContext ) ; DbgPrint("\r\n") ; + } + + return status ; +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiDisconnect( + IN PTDI_REQUEST pRequest, + IN PVOID lTimeout, + IN ULONG Flags, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + IN PCTE_IRP pClientIrp, + IN BOOLEAN Wait + ) +/*++ + +Routine Description: + + This routine sends a connect request to the tranport provider, to setup + a connection to the other side... + +Arguments: + + Wait is only used for NT (used in case when deleting address object + with open connections, which Vxd doesn't allow due to Netbios spec). + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + TDI_STATUS status ; + DbgPrint("TdiDisconnect Entered\n\r") ; + DbgPrint("TdiDisconnect - Disconnecting Connection ID: 0x") ; + DbgPrintNum( (ULONG) pRequest->Handle.ConnectionContext ) ; DbgPrint("\r\n") ; + + ASSERT( Flags <= 0xffff ) ; + status = TdiVxdDisconnect( pRequest, + lTimeout, + (ushort) Flags, + pSendInfo, + NULL ) ; + + if ( !NT_SUCCESS( status ) ) + { + DbgPrint("TdiVxdConnect: Returned error " ) ; + DbgPrintNum( status ) ; + DbgPrint("\n\r") ; + } + + return status ; +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiSend( + IN PTDI_REQUEST pRequest, + IN USHORT sFlags, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER *pBuff, + IN ULONG SendFlags + ) +/*++ + +Routine Description: + + This routine sends a packet to the transport on a TCP connection + + If this is a chain send (SendFlags & CHAIN_SEND_FLAG) then pBuff will + point to a tBUFFERCHAINSEND (which contains a tBUFFER as its first element). + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + TDI_STATUS tdistatus = TDI_SUCCESS ; + PTDI_SEND_CONTEXT psendCont = NULL ; + tBUFFERCHAINSEND * pSendBuff = (tBUFFERCHAINSEND*) pBuff ; + PNDIS_BUFFER pndis2 = NULL ; + PNDIS_BUFFER pndis1 = NULL ; + DbgPrint("TdiSend Entered - sending 0x") ; DbgPrintNum( SendLength ) ; + DbgPrint(" bytes\r\n") ; + + if ( !GetSendContext( &psendCont )) + { + tdistatus = STATUS_INSUFFICIENT_RESOURCES ; + if ( pRequest->RequestNotifyObject ) + { + ((NBT_COMPLETION)pRequest->RequestNotifyObject)( + pRequest->RequestContext, + tdistatus, + 0 ) ; + } + return tdistatus ; + } + + // + // Save away the old completion routine and context and replace them + // with new ones. The new completion routines will call the old ones + // if they are non-NULL and then free the structure. + // + psendCont->OldRequestNotifyObject = pRequest->RequestNotifyObject ; + psendCont->OldContext = pRequest->RequestContext ; + psendCont->NewContext = NULL ; + pRequest->RequestContext = psendCont ; + + // + // Set the send completion callback + // + pRequest->RequestNotifyObject = SendComplete ; + + // + // Build the ndis buffer chain (Header and data) + // + + if ( (SendFlags & CHAIN_SEND_FLAG) && pSendBuff->Length2 ) + { + InitNDISBuff( &psendCont->ndisData2, + pSendBuff->pBuffer2, + pSendBuff->Length2, + NULL ) ; + pndis2 = &psendCont->ndisData2 ; + } + + if ( pSendBuff->tBuff.Length && (SendLength > pSendBuff->tBuff.HdrLength) ) + { + InitNDISBuff( &psendCont->ndisData1, + pSendBuff->tBuff.pBuffer, + pSendBuff->tBuff.Length, + pndis2 ) ; + pndis1 = &psendCont->ndisData1 ; + } + + InitNDISBuff( &psendCont->ndisHdr, + pSendBuff->tBuff.pDgramHdr, + pSendBuff->tBuff.HdrLength, + pndis1 ) ; + + tdistatus = TdiVxdSend( pRequest, + sFlags, + SendLength, + &psendCont->ndisHdr ) ; + + if ( !NT_SUCCESS( tdistatus ) ) + goto ErrorExit ; + else + *pSentSize = SendLength ; + + return tdistatus ; + +ErrorExit: + // + // Call *our* completion routine which frees memory etc. + // + if ( psendCont && pRequest->RequestNotifyObject ) + { + ((NBT_COMPLETION)pRequest->RequestNotifyObject)( + psendCont, + tdistatus, + 0 ) ; + } + + DbgPrint("TdiSend: returning ") ; + DbgPrintNum( tdistatus ) ; + DbgPrint("\n\r") ; + return tdistatus ; +} + +/******************************************************************* + + NAME: VxdScheduleDelayedCall + + SYNOPSIS: Schedules a callback at some later time + + ENTRY: pClientContext - Context to pass callback + CallBackRoutine - Routine to call + + RETURNS: STATUS_PENDING if successfully scheduled + + NOTES: This is aliased to CTEQueueForNonDispProcessing. + + The memory for the DCC is freed by the application + + HISTORY: + Johnl 2-Sep-1993 Created + +********************************************************************/ + +NTSTATUS VxdScheduleDelayedCall( tDGRAM_SEND_TRACKING * pTracker, + PVOID pClientContext, + PVOID ClientCompletion, + PVOID CallBackRoutine, + tDEVICECONTEXT *pDeviceContext ) +{ + CTELockHandle OldIrq; + PDELAYED_CALL_CONTEXT pDCC = CTEAllocMem( sizeof( DELAYED_CALL_CONTEXT )) ; + + if ( !pDCC ) + return STATUS_INSUFFICIENT_RESOURCES ; + + ASSERT( CallBackRoutine != NULL ) ; + + pDCC->dc_WIC.pTracker = pTracker ; + pDCC->dc_WIC.pClientContext = pClientContext ; + pDCC->dc_WIC.ClientCompletion = ClientCompletion ; + pDCC->dc_Callback = CallBackRoutine ; + pDCC->pDeviceContext = pDeviceContext; + + // + // put this event on the deviceContext queue if we know the devicecontext + // otherwise, on the nbtconfig queue. This allows us to cancel the event + // later if we wish to (e.g. adapter goes away in pnp, or lease expires) + // if the adapter is marked as going down, don't schedule an event but + // execute it synchronously. + // + if (pDeviceContext) + { + ASSERT( pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + if (!pDeviceContext->fDeviceUp) + { + pDCC->dc_Callback( pDCC ) ; + + DbgPrint("VxdScheduleDelayedCall: device going down,executing now\r\n") ; + return( STATUS_SUCCESS ); + } + else + { + CTESpinLock(pDeviceContext,OldIrq); + InsertTailList(&pDeviceContext->DelayedEvents,&pDCC->Linkage); + CTESpinFree(pDeviceContext,OldIrq); + } + } + else + { + CTESpinLock(&NbtConfig,OldIrq); + InsertTailList(&NbtConfig.DelayedEvents,&pDCC->Linkage); + CTESpinFree(&NbtConfig,OldIrq); + } + + CTEInitEvent( &pDCC->dc_event, VxdDelayedCallHandler ) ; + + CTEScheduleEvent( &pDCC->dc_event, pDCC) ; + + return STATUS_PENDING ; +} + + +void VxdDelayedCallHandler( struct CTEEvent *pEvent, void * pContext ) +{ + PDELAYED_CALL_CONTEXT pDCC = pContext ; + CTELockHandle OldIrq; + tDEVICECONTEXT *pDeviceContext; + + + ASSERT( pDCC != NULL && pDCC->dc_Callback != NULL ) ; + + pDeviceContext = pDCC->pDeviceContext; + + if (pDeviceContext) + { + ASSERT( pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + CTESpinLock(pDeviceContext,OldIrq); + RemoveEntryList(&pDCC->Linkage); + CTESpinFree(pDeviceContext,OldIrq); + } + else + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + RemoveEntryList(&pDCC->Linkage); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + pDCC->dc_Callback( pDCC ) ; +} + + +/******************************************************************* + + NAME: CancelAllDelayedEvents + + SYNOPSIS: Since the device (or the entire vxd!) is going away, + cancel all the events that were queued to be scheduled + later. If a particular device context is going away (but + not the entire system) then execute all those events + synchronously. + + ENTRY: pDeviceContext - the device context that's going away + NULL if the vxd is getting unloaded + + RETURNS: TRUE if at least one event present and was cancelled + FALSE if there were no events queued. + + HISTORY: + Koti Jan. 9, 95 + +********************************************************************/ + +BOOL +CancelAllDelayedEvents( tDEVICECONTEXT *pDeviceContext ) +{ + + LIST_ENTRY *pHead; + LIST_ENTRY *pEntry; + PDELAYED_CALL_CONTEXT pDCC; + CTELockHandle OldIrq; + BOOL fAtLeastOne=FALSE; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pDeviceContext) + pHead = &pDeviceContext->DelayedEvents; + else + pHead = &NbtConfig.DelayedEvents; + + pEntry = pHead->Flink; + + while (pEntry != pHead) + { + pDCC = CONTAINING_RECORD(pEntry,DELAYED_CALL_CONTEXT,Linkage); + pEntry = pEntry->Flink; + + RemoveEntryList(&pDCC->Linkage); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CTECancelEvent( &pDCC->dc_event ); + + // + // if only one device context is going away, execute the event now + // + if (pDeviceContext) + { + pDCC->dc_Callback( pDCC ) ; + } + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + fAtLeastOne = TRUE; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + ASSERT( IsListEmpty( pHead ) ); + + return( fAtLeastOne ); +} + + diff --git a/private/ntos/nbt/vxd/util.c b/private/ntos/nbt/vxd/util.c new file mode 100644 index 000000000..b7060052e --- /dev/null +++ b/private/ntos/nbt/vxd/util.c @@ -0,0 +1,192 @@ +// +// +// UTIL.C +// +// This file contains various utility procedures that are VxD specific. These +// are called by other VxD specific routines. + +#include "nbtprocs.h" + +NTSTATUS +ConvertToIntegerArray( + char *pszString, + UNALIGNED UCHAR *pArray, + int *piNumParts) ; + +NTSTATUS +NbtCreateAddressObjects( + IN ULONG IPAddr, + IN ULONG IPMask, + OUT tDEVICECONTEXT *pDeviceContext); + +char * strrchr( const char * pch, int c ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtCreateAddressObjects) +#pragma CTEMakePageable(INIT, strrchr) +#endif +//******************* Pageable Routine Declarations **************** + +#pragma BEGIN_INIT + +/******************************************************************* + + NAME: strrchr + + SYNOPSIS: Vxd-land doesn't have a strrchr runtime function so + roll our own. + + ENTRY: pch - Pointer to string to search + c - Character to search for + + RETURNS: Found position or NULL + + HISTORY: + Johnl 31-Aug-1993 Created + +********************************************************************/ + +char * strrchr( const char * pch, int c ) +{ + char * pchFound = NULL ; + + while ( *pch != '\0' ) + { + if ( *pch == c ) + pchFound = (char *)pch ; + + // + // If double byte do this twice + // + pch++ ; + } + + return pchFound ; +} + +#pragma END_INIT + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtCreateAddressObjects( + IN ULONG IPAddr, + IN ULONG IPMask, + OUT tDEVICECONTEXT *pDeviceContext) + +/*++ + +Routine Description: + + This routine gets the ip address and subnet mask out of the registry + to calcuate the broadcast address. It then creates the address objects + with the transport. + +Arguments: + + pucRegistryPath - path to NBT config info in registry + pucBindName - name of the service to bind to. + pDeviceContext - ptr to the device context... place to store IP addr + and Broadcast address permanently + +Return Value: + + none + +--*/ + +{ + NTSTATUS status ; + pDeviceContext->IpAddress = IPAddr ; + pDeviceContext->SubnetMask = IPMask ; + + CTEPagedCode(); + + if ( NbtConfig.UseRegistryBcastAddr ) + { + pDeviceContext->BroadcastAddress = NbtConfig.RegistryBcastAddr ; + } + else + { + pDeviceContext->BroadcastAddress = (IPMask & IPAddr) | (~IPMask & -1); + } + + // now create the address objects. + + // open the Ip Address for inbound Datagrams. + status = NbtTdiOpenAddress( + NULL, // &pDeviceContext->hDgram, + NULL, // &pDeviceContext->pDgramDeviceObject, + &pDeviceContext->pDgramFileObject, + pDeviceContext, + (USHORT)NBT_DATAGRAM_UDP_PORT, + IPAddr, //IP_ANY_ADDRESS, + 0); // not a TCP port + + if (NT_SUCCESS(status)) + { + // open the Nameservice UDP port .. + status = NbtTdiOpenAddress( + NULL, //&pDeviceContext->hNameServer, + NULL, //&pDeviceContext->pNameServerDeviceObject, + &pDeviceContext->pNameServerFileObject, + pDeviceContext, + (USHORT)NBT_NAMESERVICE_UDP_PORT, + IPAddr, + 0); // not a TCP port + + if (NT_SUCCESS(status)) + { + KdPrint(("Nbt: Open Session port %X\n",pDeviceContext)); + // Open the TCP port for Session Services + status = NbtTdiOpenAddress( + NULL, //&pDeviceContext->hSession, + NULL, //&pDeviceContext->pSessionDeviceObject, + &pDeviceContext->pSessionFileObject, + pDeviceContext, + (USHORT)NBT_SESSION_TCP_PORT, + IPAddr, + TCP_FLAG | SESSION_FLAG); // TCP port + + if (NT_SUCCESS(status)) + { + // + // Open the broadcast address ("*\0\0...") for this device. + // + TDI_REQUEST tdiRequest ; + TDI_ADDRESS_NETBIOS tdiaddr ; + + tdiaddr.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP ; + CTEZeroMemory( tdiaddr.NetbiosName, NETBIOS_NAME_SIZE ) ; + tdiaddr.NetbiosName[0] = '*' ; + + status = NbtOpenAddress( &tdiRequest, + &tdiaddr, + pDeviceContext->BroadcastAddress, + NULL, // Security descriptor + pDeviceContext, + NULL ) ; + if (NT_SUCCESS(status)) + { + pDeviceContext->hBroadcastAddress = tdiRequest.Handle.AddressHandle ; + return(status); + } + + DbgPrint("Unable to Open broadcast name\r\n"); + ASSERT(0); + CloseAddress( (HANDLE) pDeviceContext->pSessionFileObject ) ; + } + + KdPrint(("Unable to Open Session address with TDI, status = %X\n",status)); + CloseAddress( (HANDLE) pDeviceContext->pNameServerFileObject ) ; + + } + KdPrint(("Unable to Open NameServer port with TDI, status = %X\n",status)); + CloseAddress( (HANDLE) pDeviceContext->pDgramFileObject ) ; + } + + return(status); +} + + diff --git a/private/ntos/nbt/vxd/vfirst.asm b/private/ntos/nbt/vxd/vfirst.asm new file mode 100644 index 000000000..c2c777f44 --- /dev/null +++ b/private/ntos/nbt/vxd/vfirst.asm @@ -0,0 +1,79 @@ +;*****************************************************************; +;** Copyright(c) Microsoft Corp., 1990-1992 **; +;*****************************************************************; + page ,132 ; :ts=8 + TITLE wventry - WinVirtualRdr entrypoint + +;*** vfirst - First module in VxDRdr +; + +.386p + +;* We don't use the .MODEL statement because it wants to declare +; _DATA and DGROUP for us; which is a no-no because windows is going to. + + +;* 32 Bit locked code +_LTEXT SEGMENT DWORD USE32 PUBLIC 'LCODE' +_LTEXT ENDS + +;* Contains 32 Bit locked data +_LDATA SEGMENT DWORD PUBLIC 'LCODE' +_LDATA ENDS + +_DATA SEGMENT DWORD PUBLIC 'LCODE' +_DATA ENDS + +CONST SEGMENT DWORD PUBLIC 'LCODE' +CONST ENDS + +_BSSbeg SEGMENT DWORD PUBLIC 'LCODE' + public _BSSBegin +_BSSBegin equ this byte +_BSSbeg ENDS + +_BSS SEGMENT DWORD PUBLIC 'LCODE' +_BSS ENDS + +c_common SEGMENT DWORD PUBLIC 'LCODE' +c_common ENDS + +_BSSend SEGMENT DWORD PUBLIC 'LCODE' + public _BSSDataEnd +_BSSDataEnd dd ? ; This gaurantees that we can zero out + ; BSS in dwords with out stomping anything +_BSSend ENDS + +;* 32 Bit initialization code +_ITEXT SEGMENT DWORD USE32 PUBLIC 'ICODE' +_ITEXT ENDS + +;* Contains 32 Bit initialization data +_IDATA SEGMENT DWORD PUBLIC 'ICODE' +_IDATA ENDS + +;* 32 Bit code +_TEXT SEGMENT DWORD USE32 PUBLIC 'LCODE' +_TEXT ENDS + +;* Contains 32 Bit data +;;_DATA SEGMENT DWORD PUBLIC 'PCODE' +;;_DATA ENDS + +;* Real Mode initialization code/data for devices +_RCODE SEGMENT WORD USE16 PUBLIC 'RCODE' +_RCODE ENDS + +_LGROUP GROUP _LTEXT, _TEXT, _LDATA, _DATA, _BSSbeg, _BSS, c_common, _BSSend +;;DGROUP GROUP _DATA, CONST, _BSSbeg, _BSS, c_common, _BSSend +;;DGROUP GROUP _DATA, CONST, _BSSbeg, _BSS, _BSSend + +_IGROUP GROUP _ITEXT, _IDATA + +;;_PGROUP GROUP _TEXT, _DATA +;;_PGROUP GROUP _TEXT + +;;include segments.inc +;;include vmm.inc + + end diff --git a/private/ntos/nbt/vxd/vnbt.rcv b/private/ntos/nbt/vxd/vnbt.rcv new file mode 100644 index 000000000..aed0293f9 --- /dev/null +++ b/private/ntos/nbt/vxd/vnbt.rcv @@ -0,0 +1,15 @@ +/********************************************************************/ +/* VNBT.RCV */ +/* Version control data */ +/********************************************************************/ +#include <version.h> +#include <netvxd.h> + +#define VER_FILETYPE VFT_VXD +#define VER_FILESUBTYPE VNBT_Device_ID +#define VER_FILEDESCRIPTION_STR "VNBT VxD Driver" +#define VER_INTERNALNAME_STR "VNBT" +#define VER_LEGALCOPYRIGHT_YEARS "1993-1995" +#define VER_ORIGINALFILENAME_STR "VNBT.386" + +#include <common.ver> diff --git a/private/ntos/nbt/vxd/vnbtd.asm b/private/ntos/nbt/vxd/vnbtd.asm new file mode 100644 index 000000000..502f362f1 --- /dev/null +++ b/private/ntos/nbt/vxd/vnbtd.asm @@ -0,0 +1,993 @@ +;*****************************************************************; +;** Copyright(c) Microsoft Corp., 1988-1993 **; +;*****************************************************************; +;:ts=8 + TITLE VNBT - Netbios on TCP/IP vxd +.XLIST +;*** VNBT -- NetBios on TCP/IP VxD +; +; This module contains the device header for the NBT VxD driver. +; + .386p + include vmm.inc +ifdef CHICAGO + include ndis.inc +endif ;; CHICAGO + include dosmgr.inc + include netvxd.inc +IFDEF CHICAGO + include configmg.inc +ENDIF + include vwin32.inc + include vdhcp.inc + include debug.inc + include vtdi.inc + + Create_VNBT_Service_Table EQU True +ifdef CHICAGO + include vnbt.inc +else ;; CHICAGO + include vnbtd.inc +endif ;; CHICAGO + include vnetbios.inc + + include pageable.inc +.LIST + +Declare_Virtual_Device VNBT,3,0,VNBT_Control,VNBT_Device_ID, \ + VNBT_Init_Order,VNBT_Api_Handler,VNBT_Api_Handler + + +VxD_DATA_SEG +VxD_DATA_ENDS + +EXTRN __VxdMapSegmentOffsetToFlat:near ; from client.asm +ifndef CHICAGO +EXTRN _BSSBegin:DWORD +EXTRN _BSSDataEnd:DWORD +endif ;; CHICAGO +EXTRN _TdiDispatch:DWORD + +EXTRN _PostInit_Proc:NEAR +EXTRN _VxdApiWorker:NEAR + +ifndef CHICAGO +EXTRN _VNBT_NCB_X@20:NEAR +endif ;; CHICAGO + +EXTRN _GetDhcpOption:NEAR + + +VxD_ICODE_SEG + +EXTRN _Init:NEAR + +MSTCP db 'MSTCP',0 ; Protocol this driver sits on (this will have + ; to be changed to get it from an .ini file) +IFDEF CHICAGO +bInitialized db 0 ; 0 means not initialized, 1 means we've entered + ; the initialization, and 2 means we've left init +bSuccessInit db 0 ; 0 means initialization failed and all subsequent + ; initializations should fail +ENDIF + +NBTSectionName db 'NBT',0 ; Section where parameters are stored + +;**************************************************************************** +;** VNBT_Device_Init - VNBT device initialization service. +; +; The VNBT device initialization routine. Before calling anything +; we need to zero out the BSS data area. +; +; +; Entry: (EBX) - System VM Handle +; (EBP) - System Client Regs structure. +; +; Exit: 'CY' clear if we init. successfully. +; 'CY' set if we've failed. +; +BeginProc VNBT_Device_Init + +IFDEF CHICAGO + ; + ; Chicago calls us at both dynamic and static init, so only process + ; once + ; + cmp bInitialized, 2 ; 2 means we've already completed initialization + jne Init_Continue + clc ; Assume success (is checked below) + jmp Init_Exit + +Init_Continue: + mov bInitialized, 1 +ENDIF + +ifndef CHICAGO + mov edi, OFFSET32 _BSSBegin + mov ecx, OFFSET32 _BSSDataEnd + sub ecx, edi + shr ecx, 2 + sub eax, eax + cld + rep stosd +endif ;; CHICAGO + + VxDcall VTDI_Get_Version + jc Init_Exit ; Get out if VTDI is not installed + + ; + ; Get the TDI Vxd dispatch table for "MSTCP" to initialize the TDI + ; dispatch table (which will be needed by some of our + ; initialization stuff) + ; + mov eax, OFFSET32 MSTCP + push eax + VxDcall VTDI_Get_Info + add esp, 4 + cmp eax, 0 ; eax contains NULL or the pointer to the table + jne NoError + + Debug_Out "VNBT_Device_Init - VTDI_Get_Info failed!" + stc ; Set the carry + jmp Init_Exit + +NoError: + mov _TdiDispatch, eax + + ; + ; Initialize the rest of the driver + ; + call _Init + cmp eax, 1 ; Set 'CY' appropriately. + +Init_Exit: + +IFDEF CHICAGO + jc Exit2 ; Failed init, leave bSuccessInit 0 + + ; + ; If first time through and success, indicate successful initialization + ; + cmp bInitialized, 1 + je SetInitFlag + + ; + ; We've already been initialized once, was it successful? + ; + cmp bSuccessInit, 1 + clc ; test clears the carry so assume success + je Exit2 + + stc ; Set the carry since we failed init. last time + jmp Exit2 + +SetInitFlag: + clc + mov bSuccessInit, 1 + +Exit2: + mov bInitialized, 2 +ENDIF + ret + + + + +EndProc VNBT_Device_Init + + +VxD_ICODE_ENDS + +NBT_PAGEABLE_CODE_SEG + +ifdef CHICAGO +_NdisOpenProtocolConfiguration@12 PROC NEAR PUBLIC + VxDJmp NdisOpenProtocolConfiguration +_NdisOpenProtocolConfiguration@12 ENDP + +_NdisCloseConfiguration@4 PROC NEAR PUBLIC + VxDJmp NdisCloseConfiguration +_NdisCloseConfiguration@4 ENDP + +_NdisReadConfiguration@20 PROC NEAR PUBLIC + VxDJmp NdisReadConfiguration +_NdisReadConfiguration@20 ENDP +endif ;; CHICAGO + +NBT_PAGEABLE_CODE_ENDS + +VxD_CODE_SEG +;**************************************************************************** +;* NCB_Handler +; +; Called by VNetBios when NCBs need to be submitted to this driver +; +; ENTRY: +; EBX = NCB being submitted +; +; EXIT: +; AL - return code +; +BeginProc NCB_Handler + + ; + ; In the master portion of the mif tests, after hitting ctrl-c during + ; the attempt to synchronize, a wait add group name NCB is + ; submitted with interrupts disabled. The CTE timer event handler + ; checks this and schedules an event for when they are re-enabled, + ; however since it's a wait NCB, we deadlock in VNBT_NCB_X's block. + ; + ; Make sure interrupts are enabled. + ; BUGBUG - Why were we called with ints disabled? + ; + ;VMMcall Enable_VM_Ints + + xor eax, eax ; second parm of VNBT_NCB_X is unused when + +ifndef CHICAGO + + push eax + push eax + push eax ; called from here, but is used (e.g.nbtstat) + push eax + push ebx + call _VNBT_NCB_X@20 ; when VNBT_NCB_X is called directly + +else ;; CHICAGO +if 0 +; +; As of 08-Feb-1996 there is a bug in "vmm.h" where this doesn't +; work properly, with the result being a misaligned stack pointer. +; + VxDCall VNBT_NCB_X, <ebx, eax, eax, eax, eax> +else ;; 0 + push eax + push eax + push eax + push eax + push ebx + VxDCall VNBT_NCB_X +endif ;; 0 +endif ;; CHICAGO + + ret +EndProc NCB_Handler + +;**************************************************************************** +;** _DhcpQueryOption - Queries a DHCP option +; +; Stub callout to the Dhcp driver +; +; Entry: [ESP+4] - IP Address of interest +; [ESP+8] - DHCP Option number +; [ESP+12]- Pointer to buffer +; [ESP+16]- Pointer to buffer size +; +; + +BeginProc _DhcpQueryOption + + VxdCall VDHCP_Get_Version + jnc DQI_Installed + + mov eax, 26 ; DHCP not installed, return invalid param + ret + +DQI_Installed: + push ebp + mov ebp,esp + + mov eax, [ebp+20] ; Buff size + push eax + mov eax, [ebp+16] ; Buff + push eax + mov eax, [ebp+12] ; Option + push eax + mov eax, [ebp+8] ; IP Address + push eax + + VxdCall VDHCP_Query_Option + + add esp, 16 + + pop ebp + ret + +EndProc _DhcpQueryOption + +;**************************************************************************** +;** VNBT_Get_Version - VNBT get version service +; +; Called by using devices to make sure the VNBT driver +; is present. Also returns the version of the VNBT driver. +; +; Entry: Nothing +; +; Exit: On success, 'CY' is clear, and +; AH - Major version # of driver. +; AL - Minor version # +; +; On failure, 'CY' is set. +; +; Uses: AX +; +BeginProc VNBT_Get_Version, SERVICE + + mov ax, VNBT_VERSION + clc + ret + +EndProc VNBT_Get_Version + +VxD_CODE_ENDS +NBT_PAGEABLE_CODE_SEG + +;**************************************************************************** +;** VNBT_Device_PostProc - do postprocessing +; +; Called by the sytem when all the other stuff (in our case, rdr) is +; loaded. At this we read the lmhosts file, so that any #INCLUDE with +; UNC names in it will work. +; This routine can also be used for other stuff, but for now only lmhosts +; stuff. +; +BeginProc VNBT_Device_PostProc + + call _PostInit_Proc + clc + ret + +EndProc VNBT_Device_PostProc + +NBT_PAGEABLE_CODE_ENDS +VxD_CODE_SEG + +;**************************************************************************** +;** VNBT_Control - VNBT device control procedure +; +; This procedure dispatches VxD messages to the appropriate handler. +; +; Entry: EBX - VM handle +; (EBP) - Client reg structure +; +; Exit: 'NC' is success, 'CY' on failure +; +; Uses: All +; +BeginProc VNBT_Control + + Control_Dispatch Device_Init, VNBT_Device_Init + Control_Dispatch Sys_Dynamic_Device_Init, VNBT_Device_Init + Control_Dispatch Sys_Vm_Init, VNBT_Device_PostProc +IFDEF CHICAGO + Control_Dispatch W32_DEVICEIOCONTROL, VNBT_DeviceIoControl +ENDIF + + clc + ret + +EndProc VNBT_Control + +IFDEF CHICAGO +;******************************************************************* +;** VNBT_DeviceIoControl +; +; Dispatch routine for VxD services invoked via the Win32 +; DeviceIoControl API. +; +; Entry: (ESI) - Points to DIOCParams structure (see VWIN32.H). +; +; Exit: (EAX) - Win32 status code, -1 for asynchronous +; completion. +; HISTORY: +; Koti 10-Nov-1994 Created (Modified from wsock version) +; +;******************************************************************** +BeginProc VNBT_DeviceIoControl + +;;; +;;; Setup stack frame. +;;; + + push ebx + push esi + push edi + + mov eax, [esi.dwIoControlCode] + cmp ecx, DIOC_GETVERSION + je vdic_doNothing + cmp ecx, DIOC_CLOSEHANDLE + jne vdic_doSomething + +;;; +;;; For devioctl calls resulting from CreateFile and CloseFile, we don't do +;;; anything other than return success. +;;; + +vdic_doNothing: + xor eax, eax + jmp vdic_CommonExit + +;;; +;;; This is an ioctl requiring some work: call our api worker (VxdApiWorker) +;;; + +vdic_doSomething: + +;;; Lock the in-buffer. + + mov eax, [esi.lpvInBuffer] + or eax, eax + jz vdic_outbuf + + cCall __VxdLockBuffer, <eax, [esi.cbInBuffer]> + or eax, eax + jz vdic_LockFailure + mov [esi.lpvInBuffer], eax + +vdic_outbuf: + +;;; Lock the out-buffer. + + mov eax, [esi.lpvOutBuffer] + or eax, eax + jz vdic_callApi + + cCall __VxdLockBuffer, <eax, [esi.cbOutBuffer]> + or eax, eax + jz vdic_LockFailure + mov [esi.lpvOutBuffer], eax + +vdic_callApi: + + mov eax, 0 ; VxdApiWorker won't trash input buffer + push eax + mov eax, [esi.cbInBuffer] + push eax + mov eax, [esi.lpvInBuffer] + push eax + mov eax, [esi.cbOutBuffer] + push eax + mov eax, [esi.lpvOutBuffer] + push eax + mov eax, [esi.dwIoControlCode] + push eax + + call _VxdApiWorker + add esp, 24 + + +;;; Unlock the in-buffer. + + push eax ; save the return code from VxdApiWorker + cCall __VxdUnlockBuffer, <[esi.lpvInBuffer], [esi.cbInBuffer]> + pop eax + +;;; Unlock the out-buffer. + + push eax ; save the return code from VxdApiWorker + cCall __VxdUnlockBuffer, <[esi.lpvOutBuffer], [esi.cbOutBuffer]> + pop eax + +vdic_CommonExit: + + pop edi + pop esi + pop ebx + + ret + +;;; +;;; Failed to lock parameter structure. +;;; + +vdic_LockFailure: + + Debug_Out "VNBT_DeviceIoControl: cannot lock parameters" + + mov eax, 1784 ; ERROR_INVALID_USER_BUFFER + jmp vdic_CommonExit + +EndProc VNBT_DeviceIoControl + +;******************************************************************* +;** _ReconfigureDevnode +; +; This routine schedules an AppyTime call for the ConfigMgr to +; call us back when it's ready. It calls our routine VNBT_ConfigCalls +; +; Entry: ESP+4 our devnode on the stack +; +; Exit: (EAX) - 0 if everything goes well +; 1 if something goes wrong +; +; HISTORY: +; Koti 15-Dec-1994 Created +; +;******************************************************************** + +BeginProc _ReconfigureDevnode + + push ebp + mov ebp, esp + + xor eax, eax + push eax ; the flags + mov eax, [ebp+8] ; our devnode + push eax ; this is our RefData + lea eax, VNBT_ConfigCalls ; call back routine + push eax + VxdCall _CONFIGMG_Call_At_Appy_Time + add esp, 12 + cmp eax, CR_SUCCESS + je ReconfigureDevnode_PASS + mov eax, 1 + jmp ReconfigureDevnode_Exit + +ReconfigureDevnode_PASS: + xor eax, eax + +ReconfigureDevnode_Exit: +IFDEF DBG + or eax, eax + jz ReconfigureDevnode_GoodJob + int 3 +ReconfigureDevnode_GoodJob: +ENDIF + pop ebp + ret +EndProc _ReconfigureDevnode + +VxD_CODE_ENDS + +NBT_PAGEABLE_CODE_SEG + +;******************************************************************* +;** VNBT_ConfigCalls +; +; This routine makes all the calls to the Config Mgr so that our +; devnode is reconfigured. We first get vredir's devnode, kill it +; and then reenumerate all the children again. This way vredir +; gets the msg from ConfigMgr to do whatever it does with a new devnode. +; +; If dhcp lease expires and then comes back in, vredir had no way +; of knowing that lana is usable again: hence this convoluted way. +; +; IMPORTANT: if chicago ever changes to add more children to tcp's +; devnode, this function needs to be modified to enumerate +; rest of the children and kill them! +; +; Entry: (EDX) - our devnode +; +; Exit: (EAX) - 0 if everything goes well +; 1 if something goes wrong +; +; HISTORY: +; Koti 15-Dec-1994 Created +; +;******************************************************************** + +VnbtScratch dd 0 + +BeginProc VNBT_ConfigCalls + + push ebp + mov ebp, esp + push esi + push edi + push ecx + + ;;; First get the first child + xor eax, eax + push eax ; the flags + mov eax, [ebp+8] + push eax ; our devnode + lea eax, VnbtScratch ; this is where we will receive child node + push eax + VxdCall _CONFIGMG_Get_Child + add esp, 12 + mov esi, dword ptr [VnbtScratch] ;save the first child in esi + mov edi, 0 ; for now, make esi,edi different + cmp eax, CR_SUCCESS + je ConfigCalls_Label2 + mov ecx, 1 + jmp ConfigCalls_Exit + + ;;; Get a sibling of this child +ConfigCalls_Label2: + cmp edi, esi ; did we just kill the first child + je ConfigCalls_Label6 ; yes: we are done with all the killings + xor eax, eax + push eax ; the flags + push esi ; the first child + lea eax, VnbtScratch ; this is where we will receive sibling + push eax + VxdCall _CONFIGMG_Get_Sibling + add esp, 12 + mov edi, dword ptr [VnbtScratch] ;edi contains sibling + cmp eax, CR_SUCCESS ; found a sibling? + je ConfigCalls_Label4 ; yes, go kill it + cmp eax, CR_NO_SUCH_DEVNODE ; done with all siblings? + je ConfigCalls_Label3 ; yes, go kill the first child + mov ecx, 2 ; hmmm: something went wrong + jmp ConfigCalls_Exit + + ;;; Done with all the siblings: now kill the first child +ConfigCalls_Label3: + mov edi, esi ; esi=first child, edi=child to kill + +ConfigCalls_Label4: + ;;; Now, ask ConfigMgr if it's ok to kill our child + xor eax, eax + push eax ; the flags + push edi ; sibling's devnode + VxdCall _CONFIGMG_Query_Remove_SubTree + add esp, 8 + cmp eax, CR_SUCCESS ; ok to kill? + je ConfigCalls_Label5 ; nope! + mov ecx, 4 + jmp ConfigCalls_Exit + +ConfigCalls_Label5: + ;;; Now that ConfigMgr approved, kill the child + xor eax, eax + push eax ; the flags + push edi ; sibling's devnode + VxdCall _CONFIGMG_Remove_SubTree + add esp, 8 + cmp eax, CR_SUCCESS ; did it die? + je ConfigCalls_Label2 ; yes, it did: go kill other siblings + mov ecx, 5 ; nope! + jmp ConfigCalls_Exit + +ConfigCalls_Label6: + ;;; Ok, reenumerate our devnode so our children come back to life + xor eax, eax + push eax ; the flags + mov eax, [ebp+8] + push eax ; our devnode + VxdCall _CONFIGMG_Reenumerate_DevNode + add esp, 8 + cmp eax, CR_SUCCESS ; did it die? + je ConfigCalls_Label7 ; nope! + mov ecx, 6 + jmp ConfigCalls_Exit + +ConfigCalls_Label7: + xor eax, eax + +ConfigCalls_Exit: +IFDEF DBG + or eax, eax + jz ConfigCalls_GoodJob + int 3 +ConfigCalls_GoodJob: +ENDIF + pop ecx + pop edi + pop esi + pop ebp + ret +EndProc VNBT_ConfigCalls + +ENDIF + +NBT_PAGEABLE_CODE_ENDS + +VxD_CODE_SEG + +;**************************************************************************** +;** _GetInDosFlag - Retrieves the InDos flag +; +; +; Note: This routine cannot be called at init time (vdosmgr complains +; the variable not initialized yet) +; +; Returns the flag in ax +; + +BeginProc _GetInDosFlag + + push ebx + + VxdCall DOSMGR_Get_IndosPtr + + ; + ; Add CB_High_Linear if we are in V86 mode + ; + + VMMcall Get_Cur_VM_Handle + + test [ebx.CB_VM_Status], VMStat_PM_Exec + jnz GIF_Exit + + add eax, [ebx.CB_High_Linear] + +GIF_Exit: + movzx eax, word ptr [eax] + + pop ebx + ret +EndProc _GetInDosFlag + +;**************************************************************************** +; +; ULONG +; RtlCompareMemory ( +; IN PVOID Source1, +; IN PVOID Source2, +; IN ULONG Length +; ) +; +; Routine Description: +; +; This function compares two blocks of memory and returns the number +; of bytes that compared equal. +; +; Arguments: +; +; Source1 (ebp+8) - Supplies a pointer to the first block of memory to +; compare. +; +; Source2 (ebp+12) - Supplies a pointer to the second block of memory to +; compare. +; +; Length (ebp+16) - Supplies the Length, in bytes, of the memory to be +; compared. +; +; Return Value: +; +; The number of bytes that compared equal is returned as the function +; value. If all bytes compared equal, then the length of the orginal +; block of memory is returned. +; +;-- + +RcmSource1 equ [ebp+8] +RcmSource2 equ [ebp+12] +RcmLength equ [ebp+16] + +public _VxdRtlCompareMemory +BeginProc _VxdRtlCompareMemory + + push ebp + mov ebp,esp + push esi + push edi + cld + + mov esi,RcmSource1 ; (esi) -> first block to compare + mov edi,RcmSource2 ; (edi) -> second block to compare + mov ecx,RcmLength ; (ecx) = length in bytes + and ecx,3 ; (ecx) = length mod 4 + jz rcm10 ; 0 odd bytes, go do dwords + +; +; Compare "odd" bytes. +; + + repe cmpsb ; compare odd bytes + jnz rcm40 ; mismatch, go report how far we got + +; +; Compare dwords. +; + +rcm10: mov ecx,RcmLength ; (ecx) = length in bytes + shr ecx,2 ; (ecx) = length in dwords + jz rcm20 ; no dwords, go exit + + repe cmpsd ; compare dwords + jnz rcm30 ; mismatch, go find byte + +; +; When we come to rcm20, we matched all the way to the end. Esi +; points to the byte after the last byte in the block, so Esi - RcmSource1 +; equals the number of bytes that matched +; + +rcm20: sub esi,RcmSource1 + mov eax,esi + pop edi + pop esi + pop ebp + ret + +; +; When we come to rcm30, esi (and edi) points to the dword after the +; one which caused the mismatch. Back up 1 dword and find the byte. +; Since we know the dword didn't match, we can assume one byte won't. +; + +rcm30: sub esi,4 ; back up + sub edi,4 ; back up + mov ecx,5 ; ensure that ecx doesn't count out + repe cmpsb ; find mismatch byte + +; +; When we come to rcm40, esi points to the byte after the one that +; did not match, which is TWO after the last byte that did match. +; + +rcm40: dec esi + sub esi,RcmSource1 + mov eax,esi + pop edi + pop esi + pop ebp + ret + +EndProc _VxdRtlCompareMemory + +;**************************************************************************** +;** VNBT_Api_Handler - handles all request from other vxd's, v86-mode apps +; +; This procedure does all the address translations, memory locking etc. +; and calls the C routine VxdApiWorker() to do the actual work +; +; Entry: EBX - VM handle +; (EBP) - Client reg structure +; +; Exit: 'NC' is success, 'CY' on failure +; +; Uses: All +; +; HISTORY: +; Koti 16-Jun-1994 Created (Modified from dhcp's version) +; +;******************************************************************** +BeginProc VNBT_Api_Handler + + + push ebp + push ebx + push esi + push edi + push edx + push ecx + +;;; get function op code + + movzx edi, [ebp.Client_CX] + +;;; +;;; Convert the parameter buffer pointers from segmented to flat. +;;; + +;;; first, the output buffer + + movzx eax, [ebp.Client_BX] + push eax + movzx eax, [ebp.Client_ES] + push eax + push ebx + call __VxdMapSegmentOffsetToFlat + add esp, 12 + + cmp eax, 0FFFFFFFFh + je vnbt_Fault + +;;; +;;; Lock the output buffer. +;;; + + or eax, eax + jz vnbt_DontLock_OutBuf + + movzx ecx, [ebp.Client_DI] + cCall __VxdLockBuffer, <eax, ecx> + + or eax, eax + jz vnbt_Fault + +vnbt_DontLock_OutBuf: + + mov esi, eax + +;;; now, convert and lock the input buffer + + movzx eax, [ebp.Client_AX] + push eax + movzx eax, [ebp.Client_DX] + push eax + push ebx + call __VxdMapSegmentOffsetToFlat + add esp, 12 + + cmp eax, 0FFFFFFFFh + je vnbt_Fault + + or eax, eax + jz vnbt_DontLock + + movzx ebx, [ebp.Client_SI] + cCall __VxdLockBuffer, <eax, ebx> ; this preserves esi + + or eax, eax + jz vnbt_Fault + +vnbt_DontLock: + + mov edx, eax + +;;; call worker routine +;;; edi - opcode +;;; esi - OutBuffer pointer +;;; ecx - OutBuffer length +;;; edx - InBuffer pointer +;;; ebx - InBuffer length + +; +; RLF 05/30/94 - __VxdLockBuffer destroys contents of cx - reload +; + + push esi ; save OutBuffer addr + push edx ; save InBuffer addr + + mov eax, 1 ; VxdApiWorker will trash input buffer + push eax + movzx ecx, [ebp.Client_DI] + movzx ebx, [ebp.Client_SI] + push ebx + push edx + push ecx + push esi + push edi + + call _VxdApiWorker + add esp, 24 + + pop edx ; restore InBuffer addr + pop esi ; restore OutBuffer addr + + mov [ebp.Client_AX], ax + +;;; +;;; Unlock the parameter buffer. +;;; + + or esi, esi + jz vnbt_DontUnLock_OutBuf + + push edx ; save InBuffer addr + movzx ecx, [ebp.Client_DI] + cCall __VxdUnlockBuffer <esi, ecx> + pop edx ; restore InBuffer addr +vnbt_DontUnLock_OutBuf: + + or edx, edx + jz vnbt_DontUnLock + + movzx ebx, [ebp.Client_SI] + cCall __VxdUnlockBuffer <edx, ebx> + +vnbt_DontUnLock: + +vnbt_CommonExit: + +;;; Restore stack fame + pop ecx + pop edx + pop edi + pop esi + pop ebx + pop ebp + ret + +;;; +;;; Either failed to map a segmented pointer to flat or failed +;;; to lock the parameter buffer. +;;; + +vnbt_Fault: + + cmp eax, 0FFFFFFFFh + mov [ebp.Client_AX], ax + jmp vnbt_CommonExit + + +EndProc VNBT_Api_Handler + +VxD_CODE_ENDS +END + diff --git a/private/ntos/nbt/vxd/vnbtd.def b/private/ntos/nbt/vxd/vnbtd.def new file mode 100644 index 000000000..2133cc04d --- /dev/null +++ b/private/ntos/nbt/vxd/vnbtd.def @@ -0,0 +1,21 @@ +VXD VNBT DYNAMIC + +DESCRIPTION 'TCP/IP on NetBIOS (NBT) driver' + +EXETYPE DEV386 + +SEGMENTS + _LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE + _TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE + _LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE + _DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE + CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE + _BSSbeg CLASS 'LCODE' PRELOAD NONDISCARDABLE + _BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE + c_common CLASS 'LCODE' PRELOAD NONDISCARDABLE + _BSSend CLASS 'LCODE' PRELOAD NONDISCARDABLE + _ITEXT CLASS 'ICODE' DISCARDABLE + _IDATA CLASS 'ICODE' DISCARDABLE + +EXPORTS + VNBT_DDB @1 diff --git a/private/ntos/nbt/vxd/vnbtd.inc b/private/ntos/nbt/vxd/vnbtd.inc new file mode 100644 index 000000000..a8aa28830 --- /dev/null +++ b/private/ntos/nbt/vxd/vnbtd.inc @@ -0,0 +1,18 @@ +;****************************************************************************** +; +; (C) Copyright MICROSOFT Corp., 1992 +; +; Title: VNBT.INC - Virtual NBT Device Service Declarations +; +; Version: 1.01 +; + + +VNBT_VERSION equ 0101h + +Begin_Service_Table VNBT + +VNBT_Service VNBT_Get_Version, LOCAL + +End_Service_Table VNBT + diff --git a/private/ntos/nbt/vxd/vnbtd.mk b/private/ntos/nbt/vxd/vnbtd.mk new file mode 100644 index 000000000..6aeb2810a --- /dev/null +++ b/private/ntos/nbt/vxd/vnbtd.mk @@ -0,0 +1,318 @@ +# +#:ts=4 +# + +ROOTDIR=.. +!include rules.mk + +# +# TCP should point to the root of Henry's TCP vxd tree +# +# + +!ifndef CHICAGO +#CHICAGO=$(DEFDRIVE)$(DEFDIR)\chicago +CHICAGO=k:\ +!endif + +VNBTSRC=$(ROOTDIR)\vxd +COMVNBTOBJD=$(COMDEBOBJ) +COMVNBTOBJ=$(COMNODEBOBJ) + +NBTLIBS=$(ROOTDIR)\nbt\nodebug\nbt.lib +NBTDLIBS=$(ROOTDIR)\nbt\debug\nbt.lib + +NDISLIBR=$(TCP)\ndis31\retail\libndis.lib +NDISLIBD=$(TCP)\ndis31\debug\libndis.lib + +# +# Hack to get around include path limits when trying to get to the DHCP +# header files. +# +IMPORT_HEADERS=$(BLT)\dhcpinfo.h $(BLT)\vdhcp.inc + +$(BLT)\dhcpinfo.h: $(DHCP)\dhcpinfo.h + copy $(DHCP)\dhcpinfo.h $(BLT) + +$(BLT)\vdhcp.inc: $(TCP)\inc\vdhcp.inc + copy $(TCP)\inc\vdhcp.inc $(BLT) + + + +CHIVNBTOBJD =$(CNODEBOBJ) +CHIDVNBTOBJD=$(CDEBOBJ) +SNOVNBTOBJD =$(SNODEBOBJ) +SNODVNBTOBJD=$(SDEBOBJ) + +RC=$(CHICAGO)\dev\sdk\bin\rc +ADRC2VXD=adrc2vxd + +VNBTOBJS=\ + $(SNODEBOBJ)\cxport.obj \ + $(SNODEBOBJ)\vfirst.obj \ + $(SNODEBOBJ)\vnbtd.obj \ + $(SNODEBOBJ)\client.obj \ + $(SNODEBOBJ)\fileio.obj \ + $(SNODEBOBJ)\cinit.obj \ + $(SNODEBOBJ)\ncb.obj \ + $(SNODEBOBJ)\vxdisol.obj \ + $(SNODEBOBJ)\tdicnct.obj \ + $(SNODEBOBJ)\tdiout.obj \ + $(SNODEBOBJ)\util.obj \ + $(SNODEBOBJ)\tdiaddr.obj \ + $(SNODEBOBJ)\tdihndlr.obj \ + $(SNODEBOBJ)\ctimer.obj \ + $(SNODEBOBJ)\nbtinfo.obj \ + $(SNODEBOBJ)\vxddebug.obj \ + $(SNODEBOBJ)\chic.obj \ + $(SNODEBOBJ)\chicasm.obj \ + $(SNODEBOBJ)\wfw.obj \ + $(SNODEBOBJ)\wfwasm.obj + +VXDFILEOBJ=$(SNODEBOBJ)\vxdfile.obj +CVXDFILEOBJ=$(SNODEBOBJ)\cvxdfile.obj + +SNOVNBTOBJS=$(VNBTOBJS) $(VXDFILEOBJ) +SNODVNBTOBJS=$(SNOVNBTOBJS:nodebug=debug) + + +SNOVNBTOBJS_C=$(VNBTOBJS) $(CVXDFILEOBJ) +CHIVNBTOBJS=$(SNOVNBTOBJS_C:snowball=chicago) +CHIDVNBTOBJS=$(CHIVNBTOBJS:nodebug=debug) + +VTSF1=$(VNBTSRC:\=/) +VTSF=$(VTSF1:.=\.) +CHICAGOF=$(CHICAGO:\=/) +TCPF=$(TCP:\=/) + +VNBTBINCS= $(BLT)\netvxd.inc $(BLT)\cxport.inc $(TCP)\inc\vtdi.inc + +VNBTAFLAGS = -DIS_32 -nologo -W2 -Zd -Cp -Cx -DMASM6 -DVMMSYS + +SNOVNBTAFLAGS= $(VNBTAFLAGS) -DWIN31COMPAT +SNOVNBTAINC=$(VNBTSRC);$(NBT)\vxd;$(INC);$(BLT);$(WIN32INC);$(COMMON)\inc;$(NDIS3INC);$(IMPORT)\wininc;$(TCP)\inc;$(TCP)\blt + +CHIVNBTAFLAGS= $(VNBTAFLAGS) -DCHICAGO +CHIVNBTAINC=$(VNBTSRC);$(NBT)\vxd;$(CHICAGO)\dev\ddk\inc;$(CHICAGO)\dev\inc;$(INC);$(BLT);$(WIN32INC);$(COMMON)\inc;$(NDIS3INC);$(IMPORT)\wininc;$(TCP)\inc;$(TCP)\blt + +VNBTCFLAGS = -c -DVXD -Zp1l -Owx -nologo -D_X86_=1 -Di386=1 -DDEVL=1 -DPROXY_NODE + +# +# Note that if netvxd.inc in tcp\blt differs from tcp\blt\snowcomm in +# something vnbt uses, we'll need different assember targets with different +# include path statements +# +VNBTAINC=$(VNBTSRC);$(INC);$(BLT);$(NDIS3INC);$(WIN32INC);$(COMMON)\inc;$(IMPORT)\wininc;$(TCP)\inc;$(TCP)\blt +#SVNBTAINC=$(VNBTAINC);$(TCP)\blt\snowcomm +#CVNBTAINC=$(VNBTAINC);$(TCP)\blt + +SNOVNBTCFLAGS= $(VNBTCFLAGS) +SNOVNBTCINC=.;..\inc;$(TCP)\h;..\..\inc;$(BASEDIR)\private\inc;$(BASEDIR)\public\sdk\inc;$(BASEDIR)\public\sdk\inc\crt;$(NDIS3INC);$(WIN32INC);$(IMPORT)\c8386\inc32;$(IMPORT)\common\h;$(IMPORT)\wininc;$(BLT) + +CHIVNBTCFLAGS= $(VNBTCFLAGS) -DCHICAGO +CHIVNBTCINC=.;..\inc;$(TCP)\h;..\..\inc;$(BASEDIR)\private\inc;$(BASEDIR)\public\sdk\inc;$(BASEDIR)\public\sdk\inc\crt;$(CHICAGO)\dev\ddk\inc;$(CHICAGO)\dev\inc;$(NDIS3INC);$(WIN32INC);$(IMPORT)\c8386\inc32;$(IMPORT)\common\h;$(IMPORT)\wininc;$(BLT) + +# +# \Common rules +# +# Note that there currently isn't any platform specific .obj that needs to +# be built. If a file does become platform specific, then copy the following +# four rules and replace COM*OBJ with C*OBJ and/or S*OBJ + +{$(VNBTSRC)}.asm{$(CHIVNBTOBJD)}.obj: + set INCLUDE=$(CHIVNBTAINC) + set ML=$(CHIVNBTAFLAGS) + $(ASM) -c -Fo$(CHIVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).asm + +{$(VNBTSRC)}.asm{$(CHIDVNBTOBJD)}.obj: + set INCLUDE=$(CHIVNBTAINC) + set ML=$(CHIVNBTAFLAGS) -DDEBUG + $(ASM) -c -Fo$(CHIDVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).asm + +{$(VNBTSRC)}.asm{$(SNOVNBTOBJD)}.obj: + set INCLUDE=$(SNOVNBTAINC) + set ML=$(SNOVNBTAFLAGS) + $(ASM) -c -Fo$(SNOVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).asm + +{$(VNBTSRC)}.asm{$(SNODVNBTOBJD)}.obj: + set INCLUDE=$(SNOVNBTAINC) + set ML=$(SNOVNBTAFLAGS) -DDEBUG + $(ASM) -c -Fo$(SNODVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).asm + +{$(VNBTSRC)}.c{$(CHIVNBTOBJD)}.obj: + set INCLUDE=$(CHIVNBTCINC) + set CL=$(CHIVNBTCFLAGS) + $(CL386) -Fo$(CHIVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).c + +{$(VNBTSRC)}.c{$(CHIDVNBTOBJD)}.obj: + set INCLUDE=$(CHIVNBTCINC) + set CL=$(CHIVNBTCFLAGS) -DDEBUG -DDBG=1 -Oy- -Zd + $(CL386) -Fo$(CHIDVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).c + +{$(VNBTSRC)}.c{$(SNOVNBTOBJD)}.obj: + set INCLUDE=$(SNOVNBTCINC) + set CL=$(SNOVNBTCFLAGS) + $(CL386) -Fo$(SNOVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).c + +{$(VNBTSRC)}.c{$(SNODVNBTOBJD)}.obj: + set INCLUDE=$(SNOVNBTCINC) + set CL=$(SNOVNBTCFLAGS) -DDEBUG -DDBG=1 -Oy- -Zd + $(CL386) -Fo$(SNODVNBTOBJD)\$(@B).obj $(VNBTSRC)\$(@B).c + +{$(VNBTSRC)}.h{$(BLT)}.inc: + $(SED) -f $(SHTOINC) <$< >$(BLT)\$(@B).inc + +$(CNODEBOBJ)\cxport.obj: $(TCP)\bin\chicago\nodebug\cxport.obj + copy $(TCP)\bin\chicago\nodebug\cxport.obj $(CNODEBOBJ) + +$(CDEBOBJ)\cxport.obj: $(TCP)\bin\chicago\debug\cxport.obj + copy $(TCP)\bin\chicago\debug\cxport.obj $(CDEBOBJ) + +$(SNODEBOBJ)\cxport.obj: $(TCP)\bin\snowball\nodebug\cxport.obj + copy $(TCP)\bin\snowball\nodebug\cxport.obj $(SNODEBOBJ) + +$(SDEBOBJ)\cxport.obj: $(TCP)\bin\snowball\debug\cxport.obj + copy $(TCP)\bin\snowball\debug\cxport.obj $(SDEBOBJ) + +svnbt: $(SNODEBBIN)\VNBT.386 $(TCP)\bin\snowball\nodebug\cxport.obj + +svnbtd: $(SDEBBIN)\VNBT.386 $(TCP)\bin\snowball\debug\cxport.obj + +cvnbt: $(CNODEBBIN)\VNBT.386 $(TCP)\bin\chicago\nodebug\cxport.obj + +cvnbtd: $(CDEBBIN)\VNBT.386 $(TCP)\bin\chicago\debug\cxport.obj + +clean: + -del $(SNODEBBIN)\*.obj + -del $(SNODEBBIN)\*.sym + -del $(SNODEBBIN)\*.386 + -del $(SNODEBBIN)\*.map + -del $(SDEBBIN)\*.obj + -del $(SDEBBIN)\*.sym + -del $(SDEBBIN)\*.386 + -del $(SDEBBIN)\*.map + + -del $(CNODEBBIN)\*.obj + -del $(CNODEBBIN)\*.sym + -del $(CNODEBBIN)\*.386 + -del $(CNODEBBIN)\*.map + -del $(CDEBBIN)\*.obj + -del $(CDEBBIN)\*.sym + -del $(CDEBBIN)\*.386 + -del $(CDEBBIN)\*.map + +cleanlink: + -del $(SNODEBBIN)\*.obj + -del $(SNODEBBIN)\*.sym + -del $(SNODEBBIN)\*.386 + -del $(SNODEBBIN)\*.map + -del $(SDEBBIN)\*.obj + -del $(SDEBBIN)\*.sym + -del $(SDEBBIN)\*.386 + -del $(SDEBBIN)\*.map + + -del $(CNODEBBIN)\*.obj + -del $(CNODEBBIN)\*.sym + -del $(CNODEBBIN)\*.386 + -del $(CNODEBBIN)\*.map + -del $(CDEBBIN)\*.obj + -del $(CDEBBIN)\*.sym + -del $(CDEBBIN)\*.386 + -del $(CDEBBIN)\*.map + +#---------------------------------------------------------------------- + +$(SNODEBBIN)\VNBT.386: $(SNOVNBTOBJS) $(NBTLIBS) $(IMPORT_HEADERS) + $(LINK386) @<< +$(SNOVNBTOBJS: =+ +) /NOD /NOI /MAP /NOLOGO +$(SNODEBBIN)\VNBT.386 +$(SNODEBBIN)\VNBT.map +$(NBTLIBS) +$(VNBTSRC)\VNBTD.def +<< + $(MAPSYM386) $(SNODEBBIN)\VNBT + -del $(SNODEBBIN)\VNBT.sym + $(MV) VNBT.sym $(SNODEBBIN) + +#---------------------------------------------------------------------- + +$(SDEBBIN)\VNBT.386: $(SNODVNBTOBJS) $(NBTDLIBS) $(IMPORT_HEADERS) + $(LINK386) @<< +$(SNODVNBTOBJS: =+ +) /NOD /NOI /MAP /NOLOGO +$(SDEBBIN)\VNBT.386 +$(SDEBBIN)\VNBT.map +$(NBTDLIBS) +$(VNBTSRC)\VNBTD.def +<< + $(MAPSYM386) $(SDEBBIN)\VNBT + -del $(SDEBBIN)\VNBT.sym + $(MV) VNBT.sym $(SDEBBIN) + +#---------------------------------------------------------------------- + +$(CNODEBBIN)\VNBT.386: $(CHIVNBTOBJS) $(NBTLIBS) $(IMPORT_HEADERS) + $(LINK386) @<< +$(CHIVNBTOBJS: =+ +) /NOD /NOI /MAP /NOLOGO +$(CNODEBBIN)\VNBT.386 +$(CNODEBBIN)\VNBT.map +$(NBTLIBS) $(NDISLIBR) +$(VNBTSRC)\VNBTD.def +<< + $(RC) -i $(CHICAGO)\dev\ddk\inc -i $(CHICAGO)\dev\inc -r VNBT.RCV + $(ADRC2VXD) $(CNODEBBIN)\VNBT.386 VNBT.RES + $(MAPSYM386) $(CNODEBBIN)\VNBT + -del $(CNODEBBIN)\VNBT.sym + $(MV) VNBT.sym $(CNODEBBIN) + +#---------------------------------------------------------------------- + +$(CDEBBIN)\VNBT.386: $(CHIDVNBTOBJS) $(NBTDLIBS) $(IMPORT_HEADERS) + $(LINK386) @<< +$(CHIDVNBTOBJS: =+ +) /NOD /NOI /MAP /NOLOGO +$(CDEBBIN)\VNBT.386 +$(CDEBBIN)\VNBT.map +$(NBTDLIBS) $(NDISLIBD) +$(VNBTSRC)\VNBTD.def +<< + $(RC) -i $(CHICAGO)\dev\ddk\inc -i $(CHICAGO)\dev\inc -r VNBT.RCV + $(ADRC2VXD) $(CDEBBIN)\VNBT.386 VNBT.RES + $(MAPSYM386) $(CDEBBIN)\VNBT + -del $(CDEBBIN)\VNBT.sym + $(MV) VNBT.sym $(CDEBBIN) + + +$(BLT)\netvxd.inc: $(COMMON)\h\netvxd.h +$(BLT)\cxport.inc: $(TCP)\h\cxport.h + +depend: VNBTdep + +VNBTdep: $(VNBTBINCS) + -copy $(VNBTSRC)\depend.mk $(VNBTSRC)\depend.old + echo #******************************************************************** > $(VNBTSRC)\depend.mk + echo #** Copyright(c) Microsoft Corp., 1993 ** >> $(VNBTSRC)\depend.mk + echo #******************************************************************** >> $(VNBTSRC)\depend.mk + set INCLUDE=$(SNOVNBTAINC) + -$(INCLUDES) -i -e -S$$(SNOVNBTOBJD) -S$$(SNODVNBTOBJD) -sobj $(VNBTSRC)\*.asm >> $(VNBTSRC)\depend.mk + set INCLUDE=$(CHIVNBTAINC) + -$(INCLUDES) -i -e -S$$(CHIVNBTOBJD) -S$$(CHIDVNBTOBJD) -sobj $(VNBTSRC)\*.asm >> $(VNBTSRC)\depend.mk + set INCLUDE=$(SNOVNBTCINC) + -$(INCLUDES) -i -e -S$$(SNOVNBTOBJD) -S$$(SNODVNBTOBJD) -sobj $(VNBTSRC)\*.c >> $(VNBTSRC)\depend.mk + set INCLUDE=$(CHIVNBTCINC) + -$(INCLUDES) -i -e -S$$(CHIVNBTOBJD) -S$$(CHIDVNBTOBJD) -sobj $(VNBTSRC)\*.c >> $(VNBTSRC)\depend.mk + $(SED) -e s`$(IMPF)`$$(IMPORT)`g <$(VNBTSRC)\depend.mk > $(VNBTSRC)\depend.tmp + $(SED) -e s`$(CMNF)`$$(COMMON)`g <$(VNBTSRC)\depend.tmp > $(VNBTSRC)\depend.mk + $(SED) -e s`$(VTSF)`$$(VNBTSRC)`g <$(VNBTSRC)\depend.mk > $(VNBTSRC)\depend.tmp + $(SED) -e s`$(BASEDIRF)`$$(BASEDIR)`g <$(VNBTSRC)\depend.tmp > $(VNBTSRC)\depend.mk + $(SED) -e s`$(INCF)`$$(INC)`g <$(VNBTSRC)\depend.mk > $(VNBTSRC)\depend.tmp + $(SED) -e s`$(HF)`$$(H)`g <$(VNBTSRC)\depend.tmp > $(VNBTSRC)\depend.mk + $(SED) -e s`$(NDIS3F)`$$(NDIS3INC)`g <$(VNBTSRC)\depend.mk > $(VNBTSRC)\depend.tmp + $(SED) -e s`$(CHICAGOF)`$$(CHICAGO)`g <$(VNBTSRC)\depend.tmp > $(VNBTSRC)\depend.mk + $(SED) -e s`$(TCPF)`$$(TCP)`g <$(VNBTSRC)\depend.mk > $(VNBTSRC)\depend.tmp + copy $(VNBTSRC)\depend.tmp $(VNBTSRC)\depend.mk + -del $(VNBTSRC)\depend.tmp + +!include depend.mk diff --git a/private/ntos/nbt/vxd/vxddebug.c b/private/ntos/nbt/vxd/vxddebug.c new file mode 100644 index 000000000..663ccf780 --- /dev/null +++ b/private/ntos/nbt/vxd/vxddebug.c @@ -0,0 +1,659 @@ +/**********************************************************************/ +/** Microsoft Windows NT **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + debug.c + + This module contains debug support routines. + + + FILE HISTORY: + KeithMo 20-Sep-1993 Created. + +*/ + +#include <nbtprocs.h> +#include <vxddebug.h> + + +#ifdef DEBUG + + +// +// Private constants. +// + +#define MAX_PRINTF_OUTPUT 1024 // characters +#define MAX_SUBSTRING_LEN 256 +#define OUTPUT_LABEL "VNBT" + +#define IS_DIGIT(ch) (((ch) >= '0') && ((ch) <= '9')) + + +// +// Private types. +// + + +// +// Private globals. +// + + +// +// Private prototypes. +// + +int VxdVsprintf( char * pszStr, + char * pszFmt, + char * ArgPtr ); + +void VxdCopyToDBOut( void ); + +// +// Public functions. +// + +/******************************************************************* + + NAME: VxdAssert + + SYNOPSIS: Called if an assertion fails. Displays the failed + assertion, file name, and line number. Gives the + user the opportunity to ignore the assertion or + break into the debugger. + + ENTRY: pAssertion - The text of the failed expression. + + pFileName - The containing source file. + + nLineNumber - The guilty line number. + + HISTORY: + KeithMo 20-Sep-1993 Created. + +********************************************************************/ +void VxdAssert( void * pAssertion, + void * pFileName, + unsigned long nLineNumber ) +{ + VxdPrintf( "\n" + "*** Assertion failed: %s\n" + "*** Source file %s, line %lu\n\n", + pAssertion, + pFileName, + nLineNumber ); + + DEBUG_BREAK; + +} // VxdAssert + +/******************************************************************* + + NAME: VxdPrintf + + SYNOPSIS: Customized debug output routine. + + ENTRY: Usual printf-style parameters. + + HISTORY: + KeithMo 20-Sep-1993 Created. + +********************************************************************/ +char szOutput[MAX_PRINTF_OUTPUT]; + +void VxdPrintf( char * pszFormat, + ... ) +{ + va_list ArgList; + int cch; + + cch = VxdSprintf( szOutput, + "%s: ", + OUTPUT_LABEL ); + + va_start( ArgList, pszFormat ); + VxdVsprintf( szOutput + cch, pszFormat, ArgList ); + va_end( ArgList ); + + VxdSprintf( szOutput, "%s\r\n", szOutput ) ; + + VxdCopyToDBOut() ; + + NbtDebugOut( DBOut+iCurPos ) ; + +} // VxdPrintf + + +// +// Private functions. +// + +/******************************************************************* + NAME: VxdCopyToDBOut + + SYNOPSIS: Copies everything from szOutput into DBOut + First checks to see if DBOut has enough room to hold what we + have temporarily put in szOutput. If not, resets the iCurPos + to point to beginning of DBOut. + + RETURNS: Nothing + + *******************************************************************/ + +void VxdCopyToDBOut( void ) +{ + + int bytesTowrite; + int spaceAvailable; + int i; + + spaceAvailable = sizeof(DBOut) - iCurPos; + + bytesTowrite = strlen( szOutput ) + 1; + + // if not enough room, start at the beginning + if ( spaceAvailable <= bytesTowrite ) + { + for ( i=iCurPos; i<sizeof(DBOut); i++ ) + DBOut[i] = '+'; // so that strings don't mix + + iCurPos = 0; + + if ( bytesTowrite > sizeof(szOutput) ) + { + bytesTowrite = sizeof(szOutput); + szOutput[bytesTowrite-1] = '\0'; + } + } + + CTEMemCopy( DBOut+iCurPos, szOutput, bytesTowrite ) ; + +} + +/******************************************************************* + + NAME: VxdVsprintf + + SYNOPSIS: Half-baked vsprintf() clone for VxD environment. + + ENTRY: pszStr - Will receive the formatted string. + + pszFmt - The format, with field specifiers. + + ArgPtr - Points to the actual printf() arguments. + + RETURNS: int - Number of characters stored in *pszStr. + + HISTORY: + KeithMo 20-Sep-1993 Created. + +********************************************************************/ +int VxdVsprintf( char * pszStr, + char * pszFmt, + char * ArgPtr ) + +{ + char ch; + char * pszStrStart; + int fZeroPad; + int cchWidth; + int ccMaxToCopy; + + // + // Remember start of output, so we can calc length. + // + + pszStrStart = pszStr; + + while( ( ch = *pszFmt++ ) != '\0' ) + { + // + // Scan for format specifiers. + // + + if( ch != '%' ) + { + *pszStr++ = ch; + continue; + } + + // + // Got one. + // + + ch = *pszFmt++; + + // + // Initialize attributes for this item. + // + + fZeroPad = 0; + cchWidth = 0; + ccMaxToCopy = MAX_SUBSTRING_LEN; + + // + // Interpret the field specifiers. + // + + if( ch == '-' ) + { + // + // Left justification not supported. + // + + ch = *pszFmt++; + } + + if( ch == '0' ) + { + // + // Zero padding. + // + + fZeroPad = 1; + ch = *pszFmt++; + } + + if( ch == '*' ) + { + // + // Retrieve width from next argument. + // + + cchWidth = va_arg( ArgPtr, int ); + ch = *pszFmt++; + } + else + { + // + // Calculate width. + // + + while( IS_DIGIT(ch) ) + { + cchWidth = ( cchWidth * 10 ) + ( ch - '0' ); + ch = *pszFmt++; + } + } + + if( ch == '.' ) + { + ch = *pszFmt++; + + if( ch == '*' ) + { + ccMaxToCopy = va_arg( ArgPtr, int ); + ch = *pszFmt++; + } + else + { + ccMaxToCopy = 0; + while( IS_DIGIT(ch) ) + { + ccMaxToCopy = ( ccMaxToCopy * 10 ) + ( ch - '0' ); + ch = *pszFmt++; + } + } + } + + // + // All numbers are longs. + // + + if( ch == 'l' ) + { + ch = *pszFmt++; + } + + // + // Decipher the format specifier. + // + + if( ( ch == 'd' ) || ( ch == 'u' ) || ( ch == 'x' ) || ( ch == 'X' ) ) + { + unsigned long ul; + unsigned long radix; + char xbase; + char * pszTmp; + char * pszEnd; + int cchNum; + int fNegative; + + // + // Numeric. Retrieve the value. + // + + ul = va_arg( ArgPtr, unsigned long ); + + // + // If this is a negative number, remember and negate. + // + + if( ( ch == 'd' ) && ( (long)ul < 0 ) ) + { + fNegative = 1; + ul = (unsigned long)(-(long)ul); + } + else + { + fNegative = 0; + } + + // + // Remember start of digits. + // + + pszTmp = pszStr; + cchNum = 0; + + // + // Special goodies for hex conversion. + // + + radix = ( ( ch == 'x' ) || ( ch == 'X' ) ) ? 16 : 10; + xbase = ( ch == 'x' ) ? 'a' : 'A'; + + // + // Loop until we're out of digits. + // + + do + { + unsigned int digit; + + digit = (unsigned int)( ul % radix ); + ul /= radix; + + if( digit > 9 ) + { + *pszTmp++ = (char)( digit - 10 + xbase ); + } + else + { + *pszTmp++ = (char)( digit + '0' ); + } + + cchNum++; + + } while( ul > 0 ); + + // + // Add the negative sign if necessary. + // + + if( fNegative ) + { + *pszTmp++ = '-'; + cchNum++; + } + + // + // Add any necessary padding. + // + + while( cchNum < cchWidth ) + { + *pszTmp++ = fZeroPad ? '0' : ' '; + cchNum++; + } + + // + // Now reverse the digits. + // + + pszEnd = pszTmp--; + + do + { + char tmp; + + tmp = *pszTmp; + *pszTmp = *pszStr; + *pszStr = tmp; + + pszTmp--; + pszStr++; + + } while( pszTmp > pszStr ); + + pszStr = pszEnd; + } + else + if( ch == 's' ) + { + char * pszTmp; + int count; + + // + // Copy the string. + // + + pszTmp = va_arg( ArgPtr, char * ); + + count = 0; + while( *pszTmp ) + { + *pszStr++ = *pszTmp++; + count++; + // + // if we get passed a weird pointer, don't go on writing! That + // overwrites other things (like tdidispatch table!) and very + // bad things happen.... + // + if (count >= ccMaxToCopy) + break; + } + } + else + if( ch == 'c' ) + { + // + // A single character. + // + + *pszStr++ = (char)va_arg( ArgPtr, int ); + } + else + { + // + // Unknown. Ideally we should copy the entire + // format specifier, including any width & precision + // values, but who really cares? + // + + *pszStr++ = ch; + } + } + + // + // Terminate it properly. + // + + *pszStr = '\0'; + + // + // Return the length of the generated string. + // + + return pszStr - pszStrStart; + +} // VxdVsprintf + +/******************************************************************* + + NAME: VxdSprintf + + SYNOPSIS: Half-baked sprintf() clone for VxD environment. + + ENTRY: pszStr - Will receive the formatted string. + + pszFmt - The format, with field specifiers. + + ... - Usual printf()-like parameters. + + RETURNS: int - Number of characters stored in *pszStr. + + HISTORY: + KeithMo 20-Sep-1993 Created. + +********************************************************************/ +int VxdSprintf( char * pszStr, + char * pszFmt, + ... ) +{ + int cch; + va_list ArgPtr; + + va_start( ArgPtr, pszFmt ); + cch = VxdVsprintf( pszStr, pszFmt, ArgPtr ); + va_end( ArgPtr ); + + return( cch ); + +} // VxdSprintf + + +/******************************************************************* + + NAME: DbgAllocMem + + SYNOPSIS: Keep track of all allocated memory so we can catch + memory leak when we unload + This is only on debug builds. On non-debug builds + this function doesn't exist: calls directly go to + CTEAllocMem. + + ENTRY: ReqSize - how much memory is needed + + RETURNS: PVOID - pointer to the memory block that client will + use directly. + + HISTORY: + Koti 11-Nov-1994 Created. + +********************************************************************/ + +// +// IMPORTANT: we are undef'ing CTEAllocMem because we need to make a +// call to the actual CTE function CTEAllocMem. That's why +// this function and this undef are at the end of the file. +// +#undef CTEAllocMem +PVOID DbgAllocMem( DWORD ReqSize ) +{ + + DWORD ActualSize; + PVOID pBuffer; + DbgMemBlkHdr *pMemHdr; + PVOID pRetAddr; + + + ActualSize = ReqSize + sizeof(DbgMemBlkHdr); + pBuffer = CTEAllocMem( ActualSize ); + if ( !pBuffer ) + { + return( NULL ); + } + + pMemHdr = (DbgMemBlkHdr *)pBuffer; + + pMemHdr->Verify = DBG_MEMALLOC_VERIFY; + pMemHdr->ReqSize = ReqSize; + pRetAddr = &pMemHdr->Owner[0]; + +// +// now memory is allocated from NCBHandler, too where stack trace isn't more +// than 2 deep! unifdef when memory leaks becomes an issue... +// +#if 0 + _asm + { + push ebx + push ecx + push edx + mov ebx, pRetAddr + mov eax, ebp + mov ecx, 4 + again: + mov edx, dword ptr [eax+4] ; return address + mov dword ptr [ebx], edx + mov eax, dword ptr [eax] ; previous frame pointer + add ebx, 4 + dec ecx + cmp ecx, 0 + je done + jmp again + done: + pop edx + pop ecx + pop ebx + } +#endif + + // + // BUGBUG: if ever ported to NT (or if chicago needs MP support), + // put spinlocks. (we will need a spinlock field in DbgMemBlkHdr struct). + // + InsertTailList(&DbgMemList, &pMemHdr->Linkage); + + return( (PCHAR)pBuffer + sizeof(DbgMemBlkHdr) ); +} + +/******************************************************************* + + NAME: DbgFreeMem + + SYNOPSIS: This routine removes the memory block from our list and + frees the memory by calling the CTE function CTEFreeMem + + ENTRY: pBufferToFree - memory to free (caller's buffer) + + RETURNS: nothing + + HISTORY: + Koti 11-Nov-1994 Created. + +********************************************************************/ + +// +// IMPORTANT: we are undef'ing CTEFreeMem because we need to make a +// call to the actual CTE function CTEFreeMem. That's why +// this function and this undef are at the end of the file. +// +#undef CTEMemFree +#undef CTEFreeMem + +VOID DbgFreeMem( PVOID pBufferToFree ) +{ + + DbgMemBlkHdr *pMemHdr; + + + if ( !pBufferToFree ) + { + return; + } + + pMemHdr = (DbgMemBlkHdr *)((PCHAR)pBufferToFree - sizeof(DbgMemBlkHdr)); + + ASSERT( pMemHdr->Verify == DBG_MEMALLOC_VERIFY ); + + // + // change our signature: if we are freeing some memory twice, we'll know! + // + pMemHdr->Verify -= 1; + + // + // BUGBUG: if ever ported to NT (or if chicago needs MP support), + // put spinlocks. (we will need a spinlock field in DbgMemBlkHdr struct). + // + RemoveEntryList(&pMemHdr->Linkage); + + CTEFreeMem( (PVOID)pMemHdr ); +} + +#endif // DEBUG + diff --git a/private/ntos/nbt/vxd/vxdfile.asm b/private/ntos/nbt/vxd/vxdfile.asm new file mode 100644 index 000000000..05c7e855d --- /dev/null +++ b/private/ntos/nbt/vxd/vxdfile.asm @@ -0,0 +1,314 @@ +;/**********************************************************************/ +;/** Microsoft Windows/NT **/ +;/** Copyright(c) Microsoft Corp., 1993 **/ +;/**********************************************************************/ + +;/* +; vxdFile.asm +; +; Contains simple VXD File I/O routines for lmhosts support +; +; FILE HISTORY: +; Johnl 06-Oct-1993 Created +; +;*/ + + .386p + include vmm.inc + include v86mmgr.inc + include dosmgr.inc + include opttest.inc + include netvxd.inc + include debug.inc + +; +; Must match manifest in vxd\fileio.c +; +LMHOSTS_READ_BUFF_SIZE equ 256 + +EXTRN _pMappedFilePath:DWORD +EXTRN _pMappedFileBuff:DWORD +EXTRN _pFileBuff:DWORD +EXTRN _pFilePath:DWORD +EXTRN _fInInit:DWORD + +EXTRN _GetInDosFlag:NEAR + +;**************************************************************************** +;** CheckInDos Macro +; +; Breaks if the Indos flag is set +; +; Uses EAX +; +CheckInDos MACRO +IFDEF DEBUG + push eax + cmp _fInInit, 0 ; Can't call while initializing + jnz @f + call _GetInDosFlag + cmp ax, 0 + je @f + Debug_Out "In dos flag set and about to make dos call!" +@@: + pop eax +ENDIF +ENDM + +;**************************************************************************** +;** PushState Macro +; +; Saves the client state and begins nested exec block. ebx will contain +; the current VM's client handle +; +; Uses ECX!! +; EBX will be set to the client area +; +PushState MACRO + + CheckInDos + + push ebx + VMMcall Get_Cur_VM_Handle ; Puts current handle into EBX + mov ebx, [ebx.CB_Client_Pointer] + + mov ecx, 0 + VMMCall Begin_Critical_Section + + Push_Client_State ; This pushes lots of crap + VMMcall Begin_Nest_V86_Exec +ENDM + +;**************************************************************************** +;** PopState Macro +; +; Restores client state and ends the nested exec block +; +; +PopState MACRO + + VMMcall End_Nest_Exec + Pop_Client_State + + VMMCall End_Critical_Section + + pop ebx + +ENDM + + +VxD_ICODE_SEG + +;**************************************************************************** +;** _VxdInitLmHostsSupport +; +; Allocates and maps memory for lmhosts support +; +; This is an Init time only routine +; +; Entry: [ESP+4] - Pointer to full path of file, +; [ESP+8] - strlen of path +; +; Exit: TRUE if successful, FALSE otherwise +; +BeginProc _VxdInitLmHostsSupport + push esi + push edi + + mov ecx, [esp+16] + add ecx, LMHOSTS_READ_BUFF_SIZE + + push ecx ; save ecx for the map call + VMMCall _Allocate_Global_V86_Data_Area, <ecx, GVDAZeroInit> + pop ecx + or eax,eax ; zero if error + jz ILMH_50 + + push eax + mov _pFileBuff, eax ; Save the linear address so we can access + add eax, LMHOSTS_READ_BUFF_SIZE ; from the vxd + mov _pFilePath, eax + pop eax + + shl eax, 12 ; Convert linear to V86 address + shr ax, 12 + + mov _pMappedFileBuff, eax + add eax, LMHOSTS_READ_BUFF_SIZE + mov _pMappedFilePath, eax + + jmp ILMH_70 + +ILMH_40: + ; Free allocated V86 global memory (how?) + + +ILMH_50: + ; error occurred, eax already contains zero + + +ILMH_70: + pop edi + pop esi + ret + +EndProc _VxdInitLmHostsSupport + + +;**************************************************************************** +;** _VxdWindowsPath +; +; Gets a pointer to (null-terminated) path to the windows directory +; +; This is an Init time only routine +; +; Entry: nothing +; +; Exit: pointer to path to windows directory +; +BeginProc _VxdWindowsPath + PushState ; Pushes lots of crap + + VmmCall Get_Config_Directory + + mov eax, edx ; path is returned in edx + + PopState ; now pop all that crap + + ret + +EndProc _VxdWindowsPath + +VxD_ICODE_ENDS + +VxD_CODE_SEG + +;**************************************************************************** +;** _VxdFileOpen +; +; Opens a file +; +; Entry: [ESP+4] - Pointer to full path of file, path must be mapped +; to v86 memory before calling this +; +; Exit: EAX will contain a handle to the openned file +; +BeginProc _VxdFileOpen + + push edi + push esi + + mov dx, word ptr [esp+12] ; Just the offset + mov di, word ptr [esp+14] ; Just the segment + + PushState ; This pushes lots of crap + + mov [ebx.Client_ax], 3d00h ; Open file, read only, share + mov [ebx.Client_dx], dx + mov [ebx.Client_ds], di + + mov eax, 21h + VmmCall Exec_Int + test [ebx.Client_Flags], CF_Mask ; Carry set if error + jz VFO_6 ; Carry set if error + + mov eax, 0 ; Failed to open the file + jmp VFO_10 + +VFO_6: + movzx eax, [ebx.Client_ax] ; Handle of file + +VFO_10: + PopState + + pop esi + pop edi + ret +EndProc _VxdFileOpen + + +;**************************************************************************** +;** _VxdFileRead +; +; Reads x bytes from a previously openned file +; +; Entry: [ESP+4] - Handle from _VxdFileOpen +; [ESP+8] - Count of bytes to read +; [ESP+12]- Mapped memory of destination buffer +; +; Exit: EAX will contain the number of bytes read, 0 if EOF or +; an error occurred. +; +BeginProc _VxdFileRead + + push edi + push esi + + mov ax, [esp+12] ; File Handle + mov si, [esp+16] ; Bytes to read + mov dx, [esp+20] ; Just the offset + mov di, [esp+22] ; Just the segment + + PushState ; Pushes lots of crap (uses cx) + + mov [ebx.Client_ax], 3f00h ; File Read + mov [ebx.Client_bx], ax ; File Handle + mov [ebx.Client_cx], si ; Bytes to read + mov [ebx.Client_dx], dx ; Mapped destination buffer + mov [ebx.Client_ds], di + + mov eax, 21h + VmmCall Exec_Int + test [ebx.Client_Flags], CF_Mask ; Carry set if error + jz VFR_6 ; Carry set if error + + mov eax, 0 ; Failed to open the file + jmp VFR_7 + +VFR_6: + movzx eax, [ebx.Client_ax] ; Bytes read + +VFR_7: + +VFR_10: + PopState + + pop esi + pop edi + ret +EndProc _VxdFileRead + + +;**************************************************************************** +;** _VxdFileClose +; +; Closes a file openned with VxdOpenFile +; +; Entry: [ESP+4] - Handle from _VxdFileOpen +; +BeginProc _VxdFileClose + + mov ax, [esp+4] ; File Handle + + PushState ; Pushes lots of crap + + mov [ebx.Client_ax], 3e00h ; File Close + mov [ebx.Client_bx], ax ; File Handle + + mov eax, 21h + VmmCall Exec_Int + test [ebx.Client_Flags], CF_Mask ; Carry set if error + jz VFCL_10 ; Carry set if error + + Debug_Out "VxdFileClose - Close failed" + mov eax, 0 ; Failed to close the file + +VFCL_10: + PopState + + ret +EndProc _VxdFileClose + + +VxD_CODE_ENDS +END diff --git a/private/ntos/nbt/vxd/vxdisol.c b/private/ntos/nbt/vxd/vxdisol.c new file mode 100644 index 000000000..7c5e6dfe1 --- /dev/null +++ b/private/ntos/nbt/vxd/vxdisol.c @@ -0,0 +1,4042 @@ +/**********************************************************************/ +/** Microsoft Windows/NT **/ +/** Copyright(c) Microsoft Corp., 1993 **/ +/**********************************************************************/ + +/* + VxdIsol.c + + This file roughly corresponds to ntisol.c and contains VxD specific + portions of the NBT driver + + FILE HISTORY: + Johnl 15-Apr-1993 Created + +*/ + + +#include <nbtprocs.h> +#include <nbtioctl.h> + +// +// Used by VxdFindClientElement +// +enum CLIENT_TYPE +{ + CLIENT_BC, + CLIENT_LOCAL +} ; + +// +// Counts the number of items in the list Head +// +// Assumes pEntry is defined in the procedure +// +#define COUNT_ELEMENTS( Head, Count ) \ + for ( pEntry = (Head).Flink ; \ + pEntry != &(Head); \ + pEntry = pEntry->Flink ) \ + { \ + (Count)++ ; \ + } + +extern BOOLEAN CachePrimed; +extern BOOL fInInit; + +// +// this is used for AdapterStatus and FindName calls because we need to retain +// the info, so can't have it as a local var (both these calls are synchronous +// so need not worry about stomping on this memory) +// +TA_NETBIOS_ADDRESS tanb_global ; + + +NCBERR VxdNameToClient( tDEVICECONTEXT * pDeviceContext, + CHAR * pName, + UCHAR * pNameNum, + tCLIENTELE * * ppClientEle ) ; + +//------------------------------------------------------------------------- +// +// Allocates and frees the SESS_SETUP_CONTEXT contents (not the context +// itself). +// +TDI_STATUS AllocSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext, + BOOL fListenOnStar ) ; +void FreeSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext ) ; + +NCBERR VxdInitSessionSetup( tDEVICECONTEXT * pDeviceContext, + TDI_REQUEST * pRequest, + PSESS_SETUP_CONTEXT * ppSessSetupContext, + NCB * pNCB ) ; + +BOOL VxdCopySessionStatus( tDEVICECONTEXT * pDeviceContext, + tCLIENTELE * pClientEle, + PSESSION_HEADER pSessionHeader, + PSESSION_BUFFER * ppSessionBuff, + ULONG * pRemainingSize ) ; + +//------------------------------------------------------------------------- +// +// NCB Reset context structures +// +// + +typedef struct +{ + // + // Number of active sessions we are waiting on to close + // + int cActiveSessions ; + + // + // Number of active names we have to wait to finish deregistering + // + int cActiveNames ; + + // + // NCB Error if reset failed (failed to resize session table for example) + // + UCHAR errncb ; +} RESET_CONTEXT, *PRESET_CONTEXT ; + +NCBERR VxdResetContinue( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ; + +#define DISCONNECT_TIMEOUT 15000 + +//------------------------------------------------------------------------- +// +// Last valid NCB name and logical session number +// +#define MAX_NCB_NUMS 254 +#define ANY_NAME 255 + +NTSTATUS +PostInit_Proc(); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, PostInit_Proc) +#endif +//******************* Pageable Routine Declarations **************** + +/******************************************************************* + + NAME: VxdDgramSend + + SYNOPSIS: Vxd specific Send Datagram code + + ENTRY: pDeviceContext - Device to send the datagram on + pNCB - NCB that contains the datagram data/dest + + RETURNS: NT_SUCCESS if successful, error otherwise + + NOTES: + + HISTORY: + Johnl 19-Apr-1993 Created + +********************************************************************/ + +NCBERR VxdDgramSend( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + NTSTATUS status; + LONG lSentLength; + TDI_REQUEST Request; + tDGRAMHDR *pDgramHdr; + tCLIENTELE *pClientEle; + char *pName ; + NCBERR errNCB ; + TDI_CONNECTION_INFORMATION SendInfo ; + TA_NETBIOS_ADDRESS tanb ; + + + errNCB = VxdFindClientElement( pDeviceContext, + pNCB->ncb_num, + &pClientEle, + CLIENT_LOCAL ) ; + if ( errNCB ) + { + DbgPrint("VxdDgramSend: VxdFindClientElement Failed\r\n") ; + return errNCB ; + } + + ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ) ; + + if ( pClientEle->fDeregistered ) + return NRC_NOWILD ; + + // + // If broadcast, then use "*" for destination name + // + if ( (pNCB->ncb_command & ~ASYNCH) == NCBDGSENDBC ) + { + // + // Name must stay valid after call + // + static char BcastName[NCBNAMSZ] = "* " ; + pName = BcastName ; + } + else + { + pName = pNCB->ncb_callname ; + } + + // + // Initialize the transport address + // + // It's Ok to pass automatic variables to NbtSendDatagram, + // the necessary data is copied out before returning + // + InitNBTDIConnectInfo( &SendInfo, &tanb, pName ) ; + Request.Handle.AddressHandle = pClientEle; + + status = NbtSendDatagram( + &Request, + &SendInfo, + pNCB->ncb_length, + &lSentLength, + pNCB->ncb_buffer, // user data + pDeviceContext, + (PIRP) pNCB ); + + errNCB = MapTDIStatus2NCBErr( status ) ; + + if ( errNCB != NRC_GOODRET && errNCB != NRC_PENDING) + { + DbgPrint("VxdDgramSend - returning ncb status 0x" ) ; + DbgPrintNum( (ULONG) errNCB ) ; + DbgPrint("\r\n") ; + } + else + { + // + // Since NbtSendDatagram always buffers datagram sends, we need to + // complete the NCB here since NbtSendDatagram will not complete + // the data gram + // + CTEIoComplete(pNCB,status,pNCB->ncb_length); + } + + return errNCB ; +} + +/******************************************************************* + + NAME: VxdDgramReceive + + SYNOPSIS: Vxd specific Datagram Receive code + + ENTRY: pDeviceContext - Device to send the datagram on + pNCB - NCB that contains the datagram data/dest + + RETURNS: NT_SUCCESS if successful, error otherwise + + NOTES: For a receive datagram, the name number is who we want to + receive to, and the call name will be set to who we + received from. The name number may be 0xff to indicate + receive to any name from anyone. + + HISTORY: + Johnl 19-Apr-1993 Created + +********************************************************************/ + +NCBERR VxdDgramReceive( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + TDI_REQUEST Request; + ULONG ReceivedLength; + tCLIENTELE * pClientEle ; + NCBERR errNCB ; + TDI_CONNECTION_INFORMATION RcvInfo ; + TDI_CONNECTION_INFORMATION SendInfo ; + NTSTATUS status ; + + if ( pNCB->ncb_num != ANY_NAME ) + { + // + // For the RcvBC case, this just confirms the ncb_num is valid, the + // pClientEle is replaced with the broadcast client element (mif + // tests invalid ncb_nums with broadcasts). + // + if ( errNCB = VxdFindClientElement( pDeviceContext, + pNCB->ncb_num, + &pClientEle, + CLIENT_LOCAL ) ) + { + return errNCB ; + } + + if ( pClientEle->fDeregistered ) + return NRC_NOWILD ; + + if ( (pNCB->ncb_command & ~ASYNCH) == NCBDGRECVBC ) + { + if ( errNCB = VxdFindClientElement( pDeviceContext, + pNCB->ncb_num, + &pClientEle, + CLIENT_BC ) ) + if ( errNCB ) + return NRC_NAMERR ; + } + + Request.Handle.AddressHandle = pClientEle ; + + status = NbtReceiveDatagram( + &Request, + NULL, //pTdiRequest->ReceiveDatagramInformation, + NULL, //pTdiRequest->ReturnDatagramInformation, + pNCB->ncb_length, + &ReceivedLength, + pNCB->ncb_buffer, // user data + pDeviceContext, + pNCB ); + if ( status == STATUS_PENDING ) + return NRC_PENDING ; + } + else + { + tRCVELE * pRcvEle = (tRCVELE *)CTEAllocMem(sizeof(tRCVELE)); + if (!pRcvEle) + return NRC_NORES ; + + pRcvEle->pIrp = pNCB ; + pRcvEle->ReceiveInfo = NULL ; + pRcvEle->ReturnedInfo = NULL; + pRcvEle->RcvLength = pNCB->ncb_length ; + pRcvEle->pRcvBuffer = pNCB->ncb_buffer ; + + InsertTailList( &pDeviceContext->RcvDGAnyFromAnyHead, + &pRcvEle->Linkage ); + + return NRC_PENDING ; + } + + // + // Status should always be pending or error + // + ASSERT( status != TDI_SUCCESS ) ; + return MapTDIStatus2NCBErr( status ) ; +} + +/******************************************************************* + + NAME: VxdCall + + SYNOPSIS: Attempts to setup a session with the corresponding listen + + ENTRY: pDeviceContext - Adapter to call on + pNCB - NCB that contains the call command + + RETURNS: + + NOTES: Before we can do the listen we must first open the + connection and associate the address. + + The reserve field of the NCB is used as a SESS_SETUP_CONTEXT + structure + + HISTORY: + Johnl 14-May-1993 Created + +********************************************************************/ + +NCBERR VxdCall( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + NTSTATUS status; + NCBERR errNCB ; + TDI_REQUEST Request; + PSESS_SETUP_CONTEXT pSessSetupContext = NULL ; + + if ( ( pNCB->ncb_name[0] == '*' ) + || ( pNCB->ncb_callname[0] == '*' ) ) + { + return NRC_NOWILD ; + } + + if ( errNCB = VxdInitSessionSetup( pDeviceContext, + &Request, + &pSessSetupContext, + pNCB )) + { + return errNCB ; + } + + status = NbtConnect( &Request, + 0, // Use system timeout + pSessSetupContext->pRequestConnect, + pSessSetupContext->pReturnConnect, + pNCB + ); + + if ( !NT_SUCCESS(status) ) + { + VxdTearDownSession( pDeviceContext, + (tCONNECTELE*)Request.Handle.ConnectionContext, + pSessSetupContext, + NULL ) ; + } + + return MapTDIStatus2NCBErr( status ) ; +} + +/******************************************************************* + + NAME: VxdSend + + SYNOPSIS: Sends a netbios request + + ENTRY: pDeviceContext - Adapter to call on + pNCB - NCB that contains the send command + + EXIT: + + RETURNS: + + NOTES: pNCB->ncb_lsn - Session number to Send on + + pNCB->ncb_reserved will contain a pointer to a tSESSIONHDR + for this NCB (freed in VxdIoComplete). + + No-ack sends are treated like normal sends. + + HISTORY: + Johnl 8-Jun-1993 Created + +********************************************************************/ + +NCBERR VxdSend( tDEVICECONTEXT *pDeviceContext, NCB * pNCB ) +{ + NTSTATUS status ; + NCBERR errNCB ; + tCONNECTELE * pConnEle; + CTELockHandle OldIrq; + tLOWERCONNECTION * pLowerConn; + tSESSIONHDR * pHdr=NULL; + tBUFFERCHAINSEND SendBuff ; + TDI_REQUEST Request ; + ULONG SentSize ; + ULONG SendFlags = 0 ; + PSEND_CONTEXT pSendCont = (PSEND_CONTEXT) pNCB->ncb_reserve ; + + ASSERT( sizeof(SEND_CONTEXT) <= + sizeof(pNCB->ncb_reserve)+sizeof(pNCB->ncb_event)) ; + + if ( errNCB = VxdFindConnectElement( pDeviceContext, + pNCB, + &pConnEle )) + { + return errNCB ; + } + + ASSERT( pConnEle->Verify == NBT_VERIFY_CONNECTION ) ; + + pLowerConn = (tLOWERCONNECTION *)pConnEle->pLowerConnId ; + + // check the state of the connection + if (pConnEle->state == NBT_SESSION_UP) + { + if ( GetSessionHdr( &pHdr )) + { + // + // If this is part of a chain send, set up the 2nd buffer + // + if ( ((pNCB->ncb_command & ~ASYNCH) == NCBCHAINSEND) || + ((pNCB->ncb_command & ~ASYNCH) == NCBCHAINSENDNA) ) + { + SendBuff.Length2 = *((WORD*)pNCB->ncb_callname) ; + SendBuff.pBuffer2 = *((PUCHAR*)(pNCB->ncb_callname+2)) ; + SendFlags |= CHAIN_SEND_FLAG ; + DbgPrint("VxdSend - Doing chain send\r\n") ; + } + else + { + SendBuff.Length2 = 0 ; + } + + pHdr->Type = NBT_SESSION_MESSAGE ; + pHdr->Flags = NBT_SESSION_FLAGS ; + pHdr->UlongLength = htonl(pNCB->ncb_length + SendBuff.Length2) ; + + pSendCont->pHdr = pHdr ; + + // + // Only sends that can time out are put on the timeout list + // + if ( (pSendCont->STO = pConnEle->STO) != NCB_INFINITE_TIME_OUT ) + { + InsertTailList( &NbtConfig.SendTimeoutHead, &pSendCont->ListEntry ) ; + } + + SendBuff.tBuff.pDgramHdr = pHdr ; + SendBuff.tBuff.HdrLength = sizeof( *pHdr ) ; + SendBuff.tBuff.pBuffer = pNCB->ncb_buffer ; + SendBuff.tBuff.Length = pNCB->ncb_length ; + +#ifdef DEBUG + if ( !pNCB->ncb_length ) // Make sure 0 length buffers do + SendBuff.tBuff.pBuffer = NULL ; // the right thing +#endif + + Request.RequestNotifyObject = VxdIoComplete ; + Request.RequestContext = pNCB ; + Request.Handle.ConnectionContext = pConnEle->pLowerConnId->pFileObject ; + + status = TdiSend( &Request, + 0, + (USHORT) SendBuff.tBuff.HdrLength + + SendBuff.tBuff.Length + SendBuff.Length2, + &SentSize, + (tBUFFER*) &SendBuff, + SendFlags ) ; + ASSERT( !NT_SUCCESS( status ) || + (SentSize == (SendBuff.tBuff.HdrLength + + SendBuff.tBuff.Length + SendBuff.Length2)) ) ; + + pLowerConn->BytesSent += SentSize; + + return NRC_PENDING ; + + // + // if TdiSend fails, it will call the completion routine (directly + // or eventually, Vxdiocomplete) which will remove this from the list + // so, don't remove it here also or we overwrite redir's code segment!! + // + // // + // // Remove from the timeout list if an error occurred + // // + // if ( !NT_SUCCESS( status ) && + // pConnEle->STO != NCB_INFINITE_TIME_OUT ) + // { + // RemoveEntryList( &pSendCont->ListEntry ) ; + // } + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES ; + goto ErrorExit ; + } + } + else + { + status = TDI_INVALID_CONNECTION ; + } + + if ( !NT_SUCCESS( status ) ) + goto ErrorExit ; + + + return MapTDIStatus2NCBErr( status ) ; + +ErrorExit: + if ( pHdr ) + FreeSessionHdr( pHdr ) ; + + DbgPrint("VxdSend returning NCB error: 0x") ; + DbgPrintNum( MapTDIStatus2NCBErr( status ) ) ; DbgPrint("\r\n") ; + + return MapTDIStatus2NCBErr( status ) ; +} + +/******************************************************************* + + NAME: VxdReceiveAny + + SYNOPSIS: Handles a request to accept data from any open session + + ENTRY: pDeviceContext - Adapter to call on + pNCB - NCB that contains the receive command + + EXIT: + + RETURNS: + + NOTES: pNCB->ncb_lsn - Session set to who to receive from if + an indication is found + + The most common case (for WFW rdr) is to Receive on + a particular name number w/o any waiting connections + + HISTORY: + Johnl 8-Jun-1993 Created + +********************************************************************/ + +NCBERR VxdReceiveAny( tDEVICECONTEXT *pDeviceContext, NCB * pNCB ) +{ + NTSTATUS status; + NCBERR errNCB ; + tLOWERCONNECTION * pLowerConn; + tCLIENTELE * pClientEle = NULL ; + PLIST_ENTRY pEntry, pHead ; + +#ifdef DEBUG + DbgPrint("VxdReceiveAny posted: Ncb length, Rcv Buff Address: 0x") ; + DbgPrintNum( pNCB->ncb_length ) ; DbgPrint(", 0x") ; + DbgPrintNum( (ULONG) pNCB->ncb_buffer ) ; DbgPrint("\r\n") ; +#endif + + // + // If they've given us a name number to receive on, find it + // + if ( pNCB->ncb_num != ANY_NAME ) + { + if ( errNCB = VxdFindClientElement( pDeviceContext, + pNCB->ncb_num, + &pClientEle, + CLIENT_LOCAL ) ) + { + DbgPrint("VxdReceiveAny - Couldn't find name number\r\n") ; + return errNCB ; + } + + if ( !pClientEle->fDeregistered ) + { + if ( IsListEmpty( &pDeviceContext->PartialRcvHead ) ) + { + goto QueueRcv ; + } + } + else + { + return NRC_NOWILD ; + } + } + else + { + if ( IsListEmpty( &pDeviceContext->PartialRcvHead ) ) + { + goto QueueRcv ; + } + } + + // + // Scan for all active sessions looking for one that has indicated + // data that will satisfy this ReceiveAny + // + pHead = &pDeviceContext->PartialRcvHead ; + pEntry = pHead->Flink ; + ASSERT( pEntry ); + while ( pEntry != pHead ) + { + DbgPrint("VxdReceiveAny: scanning lower connections for partial receive\r\n") ; + pLowerConn = CONTAINING_RECORD( pEntry, tLOWERCONNECTION, PartialRcvList ) ; + + ASSERT( pLowerConn->State < NBT_DISCONNECTING ); + // + // If Receive any from any, then the first one we find + // will work, otherwise compare the names + // + + if ( pNCB->ncb_num == ANY_NAME ) + break ; + + else + { + if ( CTEMemCmp( pClientEle->pAddress->pNameAddr->Name, + pLowerConn->pUpperConnection->pClientEle-> + pAddress->pNameAddr->Name, + NETBIOS_NAME_SIZE ) == NETBIOS_NAME_SIZE ) + { + break ; + } + } + + pEntry = pEntry->Flink ; + } + + if ( pEntry != pHead ) + { + DbgPrint("VxdReceiveAny: Found partial receive, calling VxdReceive\r\n") ; + + ASSERT (pLowerConn->fOnPartialRcvList == TRUE); + RemoveEntryList( &pLowerConn->PartialRcvList ) ; + pLowerConn->fOnPartialRcvList = FALSE; + InitializeListHead(&pLowerConn->PartialRcvList); + + // + // Now find the session number this receive is taking place on + // + if ( errNCB = VxdFindLSN( pDeviceContext, + pLowerConn->pUpperConnection, + &pNCB->ncb_lsn )) + { + return errNCB ; + } + + return VxdReceive( pDeviceContext, pNCB, FALSE ) ; + } + else + { + // + // Nothing active so queue it + // + PRCV_CONTEXT prcvCont ; + +QueueRcv: + if ( !GetRcvContext( &prcvCont )) + return NRC_NORESOURCES ; + + InitRcvContext( prcvCont, NULL, pNCB ) ; + InitNDISBuff( &prcvCont->ndisBuff, + pNCB->ncb_buffer, + pNCB->ncb_length, + NULL ) ; + prcvCont->usFlags = TDI_RECEIVE_NORMAL; + *((PRCV_CONTEXT*)&pNCB->ncb_reserve) = prcvCont ; + + if ( pNCB->ncb_num != ANY_NAME ) + { + ASSERT( pClientEle != NULL ) ; + InsertTailList( &pClientEle->RcvAnyHead, + &prcvCont->ListEntry ) ; + + return NRC_PENDING ; + } + else + { + InsertTailList( &pDeviceContext->RcvAnyFromAnyHead, + &prcvCont->ListEntry ) ; + } + } + + return NRC_PENDING ; +} + +/******************************************************************* + + NAME: VxdReceive + + SYNOPSIS: Worker for VxdReceive and VxdReceiveAny + + ENTRY: pDeviceContext - Adapter to call on + pNCB - NCB that contains the receive command + fReceive - TRUE if we got here via a Receive ncb + - FALSE if we got here via a ReceiveAny ncb + EXIT: + + RETURNS: + + NOTES: pNCB->ncb_reserved will contain a pointer to a RCV_CONTEXT + for this NCB. + + VxdReceiveAny calls this on an element where state==NBT_SESSION_UP + and StateRcv == PARTIAL_RCV, thus the receive context should + never be added to pConnele->RcvHead. + + HISTORY: + Johnl 8-Jun-1993 Created + +********************************************************************/ + +NCBERR VxdReceive( tDEVICECONTEXT * pDeviceContext, NCB * pNCB, BOOL fReceive ) +{ + NTSTATUS status; + NCBERR errNCB ; + tCONNECTELE * pConnEle; + CTELockHandle OldIrq; + tLOWERCONNECTION * pLowerConn; + PRCV_CONTEXT prcvCont ; + + if ( errNCB = VxdFindConnectElement( pDeviceContext, + pNCB, + &pConnEle )) + { + return errNCB ; + } + + ASSERT( pConnEle->Verify == NBT_VERIFY_CONNECTION ) ; + + pLowerConn = pConnEle->pLowerConnId; + + DbgPrint("VxdReceive posted: Ncb length, Rcv buff Address: 0x") ; + DbgPrintNum( pNCB->ncb_length ) ; DbgPrint(", 0x") ; + DbgPrintNum( (ULONG) pNCB->ncb_buffer ) ; DbgPrint("\r\n") ; + + // + // Setup the receive context tracker + // + if ( GetRcvContext( &prcvCont )) + { + InitRcvContext( prcvCont, pLowerConn, pNCB ) ; + InitNDISBuff( &prcvCont->ndisBuff, + pNCB->ncb_buffer, + pNCB->ncb_length, + NULL ) ; + prcvCont->RTO = pConnEle->RTO ; + prcvCont->usFlags = TDI_RECEIVE_NORMAL; + + *((PRCV_CONTEXT*)&pNCB->ncb_reserve) = prcvCont ; + + // + // If data is not available, queue the request, otherwise get the + // data + // + if ( pLowerConn->StateRcv != PARTIAL_RCV ) + { + // + // Make sure a RcvAny didn't get to here + // + ASSERT( (pNCB->ncb_command & ~ASYNCH)== NCBRECV ) ; + + if ( !pConnEle->Orig && fReceive ) + { + prcvCont->usFlags = TDI_RECEIVE_NO_RESPONSE_EXP ; + } + + InsertTailList(&pConnEle->RcvHead, + &prcvCont->ListEntry); + + return NRC_PENDING ; + } + else + { + TDI_REQUEST Request ; + UINT cbReceiveLength ; + static USHORT usFlags = TDI_RECEIVE_NORMAL ; + + DbgPrint("VxdReceive:A Rcv Buffer posted when data in the transport, InXport= 0x") ; + DbgPrintNum( pConnEle->BytesInXport ) ; + DbgPrint("\r\n") ; + + pConnEle->OffsetFromStart = 0 ; + + Request.RequestNotifyObject = CompletionRcv ; + Request.RequestContext = prcvCont ; + Request.Handle.ConnectionContext = pLowerConn->pFileObject ; + + pConnEle->pIrpRcv = NULL ; // Buffer in the transport + pLowerConn->StateRcv = FILL_IRP ; + RemoveEntryList( &pLowerConn->PartialRcvList ) ; + pLowerConn->fOnPartialRcvList = FALSE; + InitializeListHead(&pLowerConn->PartialRcvList); + + cbReceiveLength = min( pNCB->ncb_length, + pConnEle->TotalPcktLen-pConnEle->BytesRcvd ) ; + + // + // Don't pass zero length buffers to the transport + // + if ( !cbReceiveLength ) + { + CompletionRcv( prcvCont, STATUS_SUCCESS, 0 ) ; + return NRC_GOODRET ; + } + + // + // if it's an incoming session and this is a receive (as opp. to + // receive-any) then give transport a hint that there is no response + // coming back (trying to solve the raw-write-to-smb-server-perf problem) + // + if ( !pConnEle->Orig && fReceive ) + { + usFlags = TDI_RECEIVE_NO_RESPONSE_EXP ; + } + + status = TdiVxdReceive( &Request, + &usFlags, + &cbReceiveLength, + &prcvCont->ndisBuff ) ; + + if ( status == STATUS_PENDING ) + return NRC_PENDING ; + + // + // Should always get pending unless a real error occurs + // + if ( !NT_SUCCESS(status) ) + { + DbgPrint("VxdReceive - TdiReceive failed, error 0x") ; + DbgPrintNum( status ) ; + DbgPrint("\r\n") ; + CTEIoComplete( pNCB, status, 0 ) ; + } + } + } + else + return NRC_NORESOURCES ; + + return MapTDIStatus2NCBErr( status ) ; +} + +/******************************************************************* + + NAME: VxdHangup + + SYNOPSIS: Sets up a session Hangup + + ENTRY: pDeviceContext - + pNCB - NCB that contains Hangup command + + RETURNS: + + NOTES: The code is similar to VxdDisconnectHandler. If this + changes, the VxdDisconnectHandler will probably have to + change + + HISTORY: + Johnl 12-Jul-1993 Created + +********************************************************************/ + +NCBERR VxdHangup( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + TDI_STATUS tdistatus ; + tCONNECTELE * pConnEle ; + NCBERR errNCB ; + TDI_REQUEST Request ; + ULONG TimeOut = DISCONNECT_TIMEOUT ; + tCLIENTELE * pClientEle ; + tLOWERCONNECTION * pLowerConn; + + if ( errNCB = VxdFindConnectElement( pDeviceContext, + pNCB, + &pConnEle )) + { + // + // If the session was already closed but the client hasn't been + // notified, notify them now + // + if ( errNCB == NRC_SCLOSED ) + { + CTEIoComplete( pNCB, STATUS_SUCCESS, 0 ) ; + errNCB = NRC_GOODRET ; + } + + return errNCB ; + } + + ASSERT( (pConnEle->Verify == NBT_VERIFY_CONNECTION) || + (pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN)) ; + + if ( tdistatus = VxdCompleteSessionNcbs( pDeviceContext, pConnEle ) ) + { + DbgPrint("VxdHangup: Error return from VxdCompleteSessionNcbs\r\n") ; + } + + if ( pClientEle = pConnEle->pClientEle ) + { + ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT || + pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN ) ; + } + + pLowerConn = pConnEle->pLowerConnId ; + if ( pLowerConn && + (pLowerConn->fOnPartialRcvList == TRUE) && + pLowerConn->StateRcv == PARTIAL_RCV ) + { + RemoveEntryList( &pLowerConn->PartialRcvList ) ; + pLowerConn->fOnPartialRcvList = FALSE; + InitializeListHead(&pLowerConn->PartialRcvList); + } + + Request.Handle.ConnectionContext = pConnEle ; + tdistatus = NbtDisconnect( &Request, + &TimeOut, + TDI_DISCONNECT_RELEASE, + NULL, + NULL, + NULL ) ; + if ( tdistatus && tdistatus != TDI_PENDING ) + { + DbgPrint("VxdHangup: Warning: NbtDisconnect returned error\r\n") ; + } + + tdistatus = NbtCloseConnection( &Request, + NULL, + pDeviceContext, + NULL ) ; + if ( tdistatus && tdistatus != TDI_PENDING ) + { + DbgPrint("VxdHangup: Warning: NbtCloseConnection returned error\r\n") ; + } + + tdistatus = NbtDisassociateAddress( &Request ) ; + if ( tdistatus ) + { + DbgPrint("VxdHangup: NbtDisassociateAddress returned 0x") ; + DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ; + } + + REQUIRE( NBUnregister( pDeviceContext, pNCB->ncb_lsn, NB_SESSION )) ; + + // + // If this name has been deleted but there were active sessions, check + // to see if this is the last session, if so, delete the name + // + + if ( pClientEle && + pClientEle->fDeregistered && + !ActiveSessions(pClientEle) ) + { + UCHAR NameNum ; + if ( !VxdFindNameNum( pDeviceContext, pClientEle->pAddress, &NameNum )) + { + (void) VxdCleanupAddress( pDeviceContext, + NULL, + pClientEle, + NameNum, + TRUE ) ; + } + } + + CTEIoComplete( pNCB, STATUS_SUCCESS, 0 ) ; + + return NRC_GOODRET ; +} + +/******************************************************************* + + NAME: VxdListen + + SYNOPSIS: Sets up a session listen + + ENTRY: pDeviceContext - + pNCB - NCB that contains listen command + + RETURNS: + + NOTES: Before we can do the listen we must first open the + connection and associate the address. + + The reserve field of the NCB is used as a SESS_SETUP_CONTEXT + structure + + HISTORY: + Johnl 14-May-1993 Created + +********************************************************************/ + +NCBERR VxdListen( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + NTSTATUS status; + NCBERR errNCB ; + TDI_REQUEST Request; + PSESS_SETUP_CONTEXT pSessSetupContext = NULL ; + + if ( errNCB = VxdInitSessionSetup( pDeviceContext, + &Request, + &pSessSetupContext, + pNCB )) + { + return errNCB ; + } + + status = NbtListen( &Request, + TDI_QUERY_ACCEPT, + *pNCB->ncb_callname != '*' ? + pSessSetupContext->pRequestConnect : NULL, + pSessSetupContext->pReturnConnect, + pNCB + ); + + if ( !NT_SUCCESS( status ) ) + { + VxdTearDownSession( pDeviceContext, + Request.Handle.ConnectionContext, + pSessSetupContext, + NULL ) ; + } + + return MapTDIStatus2NCBErr( status ) ; +} + +/******************************************************************* + + NAME: VxdOpenName + + SYNOPSIS: Creates an Address object in response to AddName or + AddGroupName. + + ENTRY: pDeviceContext - Device name is being added to + pNCB - NCB AddName submission + + RETURNS: STATUS_SUCCESS if successful, error code otherwise + + NOTES: + + HISTORY: + Johnl 20-Apr-1993 Created + +********************************************************************/ + +NCBERR VxdOpenName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + NTSTATUS status ; + TDI_REQUEST tdiRequest ; + TDI_ADDRESS_NETBIOS tdiaddr ; + + if ( pNCB->ncb_name[0] == '*' || + pNCB->ncb_name[0] == '\0' ) + { + return NRC_NOWILD ; + } + + // + // Fill in the TDI structures appropriately + // + switch ( pNCB->ncb_command & ~ASYNCH ) + { + case NCBADDGRNAME: + tdiaddr.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP ; + break ; + + case NCBADDNAME: + tdiaddr.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE ; + break ; + + default: + ASSERTMSG("VxdOpenName: Unexpected command type!\n", FALSE ) ; + return NRC_SYSTEM ; + } + + CTEMemCopy( tdiaddr.NetbiosName, + pNCB->ncb_name, + sizeof(pNCB->ncb_name) ) ; + + status = NbtOpenAddress( &tdiRequest, + &tdiaddr, + pDeviceContext->IpAddress, + NULL, // Security descriptor + pDeviceContext, + pNCB ) ; + if ( NT_SUCCESS( status )) + { + // + // Set our event handler to catch "Receive Any" and "Receve + // Any From Any" NCBs + // + REQUIRE( !NbtSetEventHandler( (tCLIENTELE*)tdiRequest.Handle.AddressHandle, + TDI_EVENT_RECEIVE, + ReceiveAnyHandler, + (tCLIENTELE*)tdiRequest.Handle.AddressHandle )) ; + // + // Set an event handler to cleanup up Netbios specific stuff on + // disconnect + // + REQUIRE( !NbtSetEventHandler( (tCLIENTELE*)tdiRequest.Handle.AddressHandle, + TDI_EVENT_DISCONNECT, + VxdDisconnectHandler, + (tCLIENTELE*)tdiRequest.Handle.AddressHandle)) ; + } + + // + // If we open a non-unique name twice (such as a group name) then + // NbtOpenAddress doesn't complete the IRP it just returns success. + // + if ( status == TDI_SUCCESS ) + { + CTEIoComplete( pNCB, status, (ULONG) tdiRequest.Handle.AddressHandle ) ; + } + + return MapTDIStatus2NCBErr( status ) ; +} + + +/******************************************************************* + + NAME: VxdCloseName + + SYNOPSIS: Called in response to a Netbios Delete Name request + + ENTRY: pDeviceContext - Device name should be deleted from + pNCB - Netbios Delete name submission + + RETURNS: STATUS_SUCCESS if successful, error code otherwise + + NOTES: + + HISTORY: + Johnl 23-Apr-1993 Created + +********************************************************************/ + +NCBERR VxdCloseName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + tCLIENTELE * pClientEle ; + TDI_STATUS tdistatus ; + UCHAR NameNum ; + NCBERR errNCB ; + + + if ( pNCB->ncb_name[0] == '*' || + pNCB->ncb_name[0] == '\0' ) + { + return NRC_NOWILD ; + } + + if ( errNCB = VxdNameToClient( pDeviceContext, + pNCB->ncb_name, + &NameNum, + &pClientEle )) + { + return errNCB ; + } + + // + // If any sessions are open on this name, delay deletion till last name is + // closed + // + if ( ActiveSessions( pClientEle ) ) + { + VxdCleanupAddress( pDeviceContext, pNCB, pClientEle, NameNum, FALSE ) ; + CTEIoComplete( pNCB, STATUS_NRC_ACTSES, 0 ) ; + return NRC_GOODRET ; + } + + // + // No open sessions so blow away the name + // + return VxdCleanupAddress( pDeviceContext, pNCB, pClientEle, NameNum, TRUE ) ; +} + +/******************************************************************* + + NAME: ActiveSessions + + SYNOPSIS: Returns TRUE if pClientEle has any active sessions + + ENTRY: pClientEle - Client element to check + +********************************************************************/ + +BOOL ActiveSessions( tCLIENTELE * pClientEle ) +{ + PLIST_ENTRY pHead, pEntry ; + + pHead = &pClientEle->ConnectActive ; + pEntry = pClientEle->ConnectActive.Flink ; + while ( pHead != pEntry ) + { + tCONNECTELE * pConnEle = CONTAINING_RECORD( pEntry, tCONNECTELE, Linkage ) ; + + if ( pConnEle->state > NBT_ASSOCIATED ) + { + return TRUE ; + } + + pEntry = pEntry->Flink ; + } + + return FALSE ; +} + +/******************************************************************* + + NAME: VxdCleanupAddress + + SYNOPSIS: Prepares a name for deletion and optionally deletes it + + ENTRY: pDeviceContext - Adapter we are dealing with + pNCB - Delete name NCB + pClientEle - Client of address element to delete + NameNum - Name number in table we are deleting + fDeleteAddress - TRUE if address should be deleted + + EXIT: The address element will be marked as deregistered and all + non-session NCBs will be completed. The address element + may optionally be deleted also. + + NOTES: This routine will complete pNCB as appropriate. + + HISTORY: + Johnl 22-Sep-1993 Created + +********************************************************************/ + +NCBERR VxdCleanupAddress( tDEVICECONTEXT * pDeviceContext, + NCB * pNCB, + tCLIENTELE * pClientEle, + UCHAR NameNum, + BOOL fDeleteAddress ) +{ + TDI_REQUEST Request ; + NCBERR errNCB ; + tCLIENTELE * pClientEleBcast ; + TDI_STATUS tdistatus ; + USHORT NameType ; + PLIST_ENTRY pHead, pEntry ; + PLIST_ENTRY pNextEntry; + tLISTENREQUESTS * pListen ; + PRCV_CONTEXT prcvCont ; + tRCVELE * prcvEle ; + + pClientEle->fDeregistered = TRUE ; + + // + // Delete all outstanding listens on this name + // + while ( !IsListEmpty( &pClientEle->ListenHead )) + { + pEntry = RemoveHeadList( &pClientEle->ListenHead ) ; + pListen = CONTAINING_RECORD( pEntry, tLISTENREQUESTS, Linkage ) ; + CTEIoComplete( pListen->pIrp, STATUS_NETWORK_NAME_DELETED, 0 ) ; + CTEMemFree( pListen ) ; + } + + // + // Delete all outstanding datagram receives on this name + // + while ( !IsListEmpty( &pClientEle->RcvDgramHead )) + { + pEntry = RemoveHeadList( &pClientEle->RcvDgramHead ) ; + prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ; + CTEIoComplete( prcvEle->pIrp, STATUS_NETWORK_NAME_DELETED, 0 ) ; + CTEMemFree( prcvEle ) ; + } + + // + // Delete all outstanding datagram broadcast receives on this name number + // + errNCB = VxdFindClientElement( pDeviceContext, + 0, + &pClientEleBcast, + CLIENT_BC ) ; + + if ( !errNCB ) + { + // + // Scan the NCBs looking for a receive on this name number + // + pHead = &pClientEleBcast->RcvDgramHead ; + pEntry = pClientEleBcast->RcvDgramHead.Flink ; + + while ( pEntry != pHead ) + { + prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ; + pNextEntry = pEntry->Flink ; + if ( ((NCB*)prcvEle->pIrp)->ncb_num == NameNum ) + { + RemoveEntryList( pEntry ) ; + CTEIoComplete( prcvEle->pIrp, STATUS_NETWORK_NAME_DELETED, 0 ) ; + CTEMemFree( prcvEle ) ; + } + pEntry = pNextEntry ; + } + } + + + // + // Delete all outstanding Receive Anys on this name + // + while ( !IsListEmpty( &pClientEle->RcvAnyHead )) + { + pEntry = RemoveHeadList( &pClientEle->RcvAnyHead ) ; + prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + CTEIoComplete( prcvCont->pNCB, STATUS_NETWORK_NAME_DELETED, 0 ) ; + } + + tdistatus = TDI_SUCCESS; + if ( fDeleteAddress ) + { + Request.Handle.ConnectionContext = pClientEle ; + tdistatus = NbtCloseAddress( &Request, + NULL, //&RequestStatus, + pDeviceContext, + pNCB ) ; + + if ( (tdistatus != TDI_PENDING) && pNCB ) + CTEIoComplete( pNCB, tdistatus, 0 ) ; + + REQUIRE( NBUnregister( pDeviceContext, NameNum, NB_NAME )) ; + + DbgPrint("VxdCloseName: NBUnregistered:NameNum = 0x") ; + DbgPrintNum( NameNum ) ; + DbgPrint(" ClientEle = 0x") ; + DbgPrintNum( pClientEle ) ; + DbgPrint("\r\n") ; + + if ( !NT_SUCCESS( tdistatus )) + { + DbgPrint("VxdCloseName: NbtCloseAddress failed with status 0x") ; + DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ; + } + } + + return MapTDIStatus2NCBErr( tdistatus ) ; +} + +/******************************************************************* + + NAME: VxdAccept + + SYNOPSIS: Accepts an indicated listen + + ENTRY: pConnectElem - Upper part of connection we're about to + setup + pNCB - Original Listen request + + RETURNS: TDI_SUCCESS if successful error code otherwise + + NOTES: + + HISTORY: + Johnl 27-May-1993 Created + +********************************************************************/ + +TDI_STATUS VxdAccept( tCONNECTELE * pConnectElem, NCB * pNCB ) +{ + TDI_REQUEST Request ; + PSESS_SETUP_CONTEXT pSessSetupCont = (PSESS_SETUP_CONTEXT) pNCB->ncb_reserve ; + TDI_STATUS status ; + + Request.Handle.ConnectionContext = pConnectElem ; + + status = NbtAccept( &Request, + pSessSetupCont->pRequestConnect, + pSessSetupCont->pReturnConnect, + NULL ) ; + if ( !NT_SUCCESS(status) ) + { + DbgPrint( "VxdAccept: NbtAccept returned " ) ; + DbgPrintNum( status ) ; + DbgPrint("\r\n") ; + } + + // + // It's OK if the accept is pending because it's just the + // session setup acknowledgement + // + if ( status == TDI_PENDING ) + status = TDI_SUCCESS ; + + return status ; +} + +/******************************************************************* + + NAME: VxdAdapterStatus + + SYNOPSIS: Gets the requested adapter status + + ENTRY: pDeviceContext - Adapter status to get + pNCB - Pointer to requesting NCB + + EXIT: + + NOTES: + + HISTORY: + Johnl 10-Aug-1993 Created + +********************************************************************/ + +NCBERR VxdAdapterStatus( tDEVICECONTEXT * pDeviceContext, + NCB * pNCB, + ULONG Ipaddr + ) +{ + TDI_STATUS status ; + PADAPTER_STATUS pAdapterStatus ; + ULONG ActualSize; + ULONG Size = pNCB->ncb_length ; + + // + // Ipaddr will always be 0 except in one case: if we came here + // via nbtstat -A + // + if ( !Ipaddr && *pNCB->ncb_callname == '*' ) + { + // + // Get the local adapter status + // + DbgPrint("VxdAdapterStatus: AStat for local (*)\r\n") ; + status = NbtQueryAdapterStatus( pDeviceContext, + &pAdapterStatus, + &Size ) ; + if ( !status || status == TDI_BUFFER_OVERFLOW ) + { + ActualSize = min( pNCB->ncb_length, Size ) ; + CTEMemCopy( pNCB->ncb_buffer, + pAdapterStatus, + ActualSize) ; + pNCB->ncb_length = ActualSize; + + CTEFreeMem( pAdapterStatus ) ; + CTEIoComplete( pNCB, status, 0 ) ; + + // + // Return a successful status (buffer overflow denoted + // in NCB) + // + status = NRC_GOODRET ; + } + } + else + { + + ULONG IpAddrsList[2]; + + IpAddrsList[0] = Ipaddr; + IpAddrsList[1] = 0; + + status = NbtSendNodeStatus( pDeviceContext, + pNCB->ncb_callname, + pNCB, + IpAddrsList, + 0, + NodeStatusDone); + } + + return MapTDIStatus2NCBErr( status ) ; +} + +/******************************************************************* + + NAME: VxdFindName + + SYNOPSIS: Gets the requested adapter status + + ENTRY: pDeviceContext - Adapter status to get + pNCB - Pointer to requesting NCB + + EXIT: + + NOTES: + + HISTORY: + Johnl 04-Oct-1993 Created + +********************************************************************/ + +NCBERR VxdFindName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + TDI_STATUS status ; + TDI_CONNECTION_INFORMATION RequestInfo ; + + DbgPrint("VxdFindName: Entered\r\n") ; + InitNBTDIConnectInfo( &RequestInfo, &tanb_global, pNCB->ncb_callname ) ; + status = NbtQueryFindName( &RequestInfo, + pDeviceContext, + pNCB, + FALSE ) ; + + if ( status == STATUS_SUCCESS ) + { + CTEIoComplete( pNCB, STATUS_SUCCESS, 0xffffffff ) ; + return STATUS_SUCCESS ; + } + + return MapTDIStatus2NCBErr( status ) ; +} +/******************************************************************* + + NAME: VxdSessionStatus + + SYNOPSIS: Gets the requested Session status + + ENTRY: pDeviceContext - Session status to get + pNCB - Pointer to requesting NCB + + EXIT: + + NOTES: VxdCopySessionStatus will automatically complete the NCB + if the buffer overflows. Otherwise we will. + + HISTORY: + Johnl 23-Aug-1993 Created + +********************************************************************/ + +NCBERR VxdSessionStatus( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + TDI_STATUS status = STATUS_SUCCESS ; + PSESSION_HEADER pSessionHeader = (PSESSION_HEADER) pNCB->ncb_buffer ; + PSESSION_BUFFER pSessionBuff ; + ULONG RemainingSize = pNCB->ncb_length ; + tNAMEADDR * pNameAddr = NULL ; + tCLIENTELE * pClientEle = NULL ; + tCLIENTELE * pClientEleBcast = NULL ; + USHORT NameType ; + PLIST_ENTRY pEntry ; + UCHAR i ; + NCBERR errNCB ; + + if ( RemainingSize < sizeof(SESSION_HEADER) ) + { + CTEIoComplete( pNCB, STATUS_INVALID_BUFFER_SIZE, 0 ) ; + return NRC_GOODRET ; + } + + pSessionHeader->sess_name = 0 ; + pSessionHeader->num_sess = 0 ; + pSessionHeader->rcv_dg_outstanding = 0 ; + pSessionHeader->rcv_any_outstanding = 0 ; + + // + // For broadcast datagram statistics + // + errNCB = VxdFindClientElement( pDeviceContext, + 0, + &pClientEleBcast, + CLIENT_BC ) ; + if ( errNCB ) + return errNCB ; + + // + // Get all sessions? + // + if ( pNCB->ncb_name[0] == '*' ) + { + for ( i = 1 ; i <= pDeviceContext->cMaxSessions ; i++ ) + { + if ( pDeviceContext->pSessionTable[i] != NULL ) + { + pClientEle = pDeviceContext->pSessionTable[i]->pClientEle ; + + // + // Both normal receives and broadcast receives are + // kept on the same list + // + COUNT_ELEMENTS( pClientEle->RcvDgramHead, + pSessionHeader->rcv_dg_outstanding ) ; + + COUNT_ELEMENTS( pClientEle->RcvAnyHead, + pSessionHeader->rcv_any_outstanding ) ; + } + } + + // + // Only one broadcast client element per adapter + // + COUNT_ELEMENTS( pClientEleBcast->RcvDgramHead, + pSessionHeader->rcv_dg_outstanding ) ; + + COUNT_ELEMENTS( pDeviceContext->RcvDGAnyFromAnyHead, + pSessionHeader->rcv_dg_outstanding ) ; + + pSessionHeader->sess_name = 0xff ; + + RemainingSize -= sizeof( SESSION_HEADER ) ; + pSessionBuff = (PSESSION_BUFFER) (pSessionHeader + 1) ; + + // + // From this device context, traverse all of the Address elements + // and all of its Client elements and all of its Connect Elements + // + for ( pEntry = NbtConfig.AddressHead.Flink ; + pEntry != &NbtConfig.AddressHead && !status ; + pEntry = pEntry->Flink ) + { + PLIST_ENTRY pEntryClient ; + tADDRESSELE * pAddrEle = CONTAINING_RECORD( pEntry, + tADDRESSELE, + Linkage ) ; + ASSERT( pAddrEle->Verify == NBT_VERIFY_ADDRESS ) ; + + // + // Only get addresses for this adapter + // + if ( pAddrEle->pDeviceContext != pDeviceContext ) + continue ; + + for ( pEntryClient = pAddrEle->ClientHead.Flink ; + pEntryClient != &pAddrEle->ClientHead ; + pEntryClient = pEntryClient->Flink ) + { + tCLIENTELE * pClientEle = CONTAINING_RECORD( pEntryClient, + tCLIENTELE, + Linkage ) ; + PLIST_ENTRY pEntryConn ; + ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT || + pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN ) ; + + if (!VxdCopySessionStatus( pDeviceContext, + pClientEle, + pSessionHeader, + &pSessionBuff, + &RemainingSize )) + { + status = STATUS_BUFFER_OVERFLOW ; + break ; + } + } + } + } + else + { + if ( errNCB = VxdNameToClient( pDeviceContext, + pNCB->ncb_name, + &pSessionHeader->sess_name, + &pClientEle )) + { + return errNCB ; + } + + COUNT_ELEMENTS( pClientEle->RcvDgramHead, + pSessionHeader->rcv_dg_outstanding ) ; + + COUNT_ELEMENTS( pClientEleBcast->RcvDgramHead, + pSessionHeader->rcv_dg_outstanding ) ; + + COUNT_ELEMENTS( pDeviceContext->RcvDGAnyFromAnyHead, + pSessionHeader->rcv_dg_outstanding ) ; + + COUNT_ELEMENTS( pClientEle->RcvAnyHead, + pSessionHeader->rcv_any_outstanding ) ; + + RemainingSize -= sizeof( SESSION_HEADER ) ; + pSessionBuff = (PSESSION_BUFFER) (pSessionHeader + 1) ; + if ( !VxdCopySessionStatus( pDeviceContext, + pClientEle, + pSessionHeader, + &pSessionBuff, + &RemainingSize )) + { + status = STATUS_BUFFER_OVERFLOW ; + } + } + + CTEIoComplete( pNCB, + status, + sizeof(SESSION_HEADER) + + pSessionHeader->num_sess * sizeof(SESSION_BUFFER) ) ; + + return NRC_GOODRET ; +} + +/******************************************************************* + + NAME: VxdCopySessionStatus + + SYNOPSIS: Copies all of the sessions associated with pClientEle + + ENTRY: pDeviceContext - Adapter to use + pClientEle - Client to retrieve all sessions for + pSessionHeader - Session status header + pSessionBuff - Pointer to beginning of session buffers + pRemainingSize - Remaining size of buffer + + RETURNS: TRUE if all session information was transferred, + FALSE if we ran out of buffer space + + NOTES: + + HISTORY: + Johnl 23-Aug-1993 Created + +********************************************************************/ + +BOOL VxdCopySessionStatus( tDEVICECONTEXT * pDeviceContext, + tCLIENTELE * pClientEle, + PSESSION_HEADER pSessionHeader, + PSESSION_BUFFER * ppSessionBuff, + ULONG * pRemainingSize ) +{ + PLIST_ENTRY pEntryConn ; + tCONNECTELE * pConnectEle ; + + for ( pEntryConn = pClientEle->ConnectActive.Flink ; + pEntryConn != &pClientEle->ConnectActive ; + pEntryConn = pEntryConn->Flink ) + { + PLIST_ENTRY pEntry ; + BOOL fFillRemote = FALSE ; + pConnectEle = CONTAINING_RECORD( pEntryConn, + tCONNECTELE, + Linkage ) ; + ASSERT( pConnectEle->Verify == NBT_VERIFY_CONNECTION || + pConnectEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ; + + if ( *pRemainingSize < sizeof(SESSION_BUFFER) ) + { + return FALSE ; + } + + *pRemainingSize -= sizeof(SESSION_BUFFER) ; + pSessionHeader->num_sess++ ; + (*ppSessionBuff)->rcvs_outstanding = 0 ; + (*ppSessionBuff)->sends_outstanding = 0 ; // Always 0 + REQUIRE( !VxdFindLSN( pDeviceContext, pConnectEle, &(*ppSessionBuff)->lsn )) ; + + COUNT_ELEMENTS( pConnectEle->RcvHead, + (*ppSessionBuff)->rcvs_outstanding ) ; + + // + // Set the session state + // + switch ( pConnectEle->state ) + { + case NBT_CONNECTING: // establishing Transport connection + if ( pConnectEle->Orig ) + (*ppSessionBuff)->state = CALL_PENDING ; + else + (*ppSessionBuff)->state = LISTEN_OUTSTANDING ; + break ; + + case NBT_SESSION_INBOUND: // waiting for a session request after tcp connectio + case NBT_SESSION_WAITACCEPT: // waiting for accept after a listen has been satis + (*ppSessionBuff)->state = LISTEN_OUTSTANDING ; + break ; + + case NBT_SESSION_OUTBOUND: // waiting for a session response after tcp connecti + fFillRemote = TRUE ; + (*ppSessionBuff)->state = CALL_PENDING ; + + case NBT_SESSION_UP: // got positive response + fFillRemote = TRUE ; + (*ppSessionBuff)->state = SESSION_ESTABLISHED ; + break ; + + case NBT_DISCONNECTING: // sent a disconnect down to Tcp, but it hasn't comp + (*ppSessionBuff)->state = HANGUP_PENDING; + break ; + + case NBT_DISCONNECTED: // a session has been disconnected but not closed wit + (*ppSessionBuff)->state = HANGUP_COMPLETE; + break ; + + case NBT_IDLE: // Shouldn't be on ConnectActive list + case NBT_ASSOCIATED: + default: + ASSERT( FALSE ) ; + (*ppSessionBuff)->state = SESSION_ABORTED ; + break ; + } + + // + // Copy local and/or remote name + // + CTEMemCopy( (*ppSessionBuff)->local_name, + pClientEle->pAddress->pNameAddr->Name, + NCBNAMSZ ) ; + + if ( fFillRemote ) + { + CTEMemCopy( (*ppSessionBuff)->remote_name, + pConnectEle->RemoteName, + NCBNAMSZ ) ; + } + + (*ppSessionBuff)++ ; + } + + return TRUE ; +} + +/******************************************************************* + + NAME: VxdReset + + SYNOPSIS: Clears out the name tables and completes all outstanding NCBs + + ENTRY: pDeviceContext - Adapter status to get + pNCB - Pointer to requesting NCB + + EXIT: + + NOTES: If a session is active then we have to wait till we disconnect + the connection before deleting the name. We keep count of the + active sessions and call the VxdResetContinue function after + all session disconnects have been completed. + + It is assumed this is made as a "wait" call. + + HISTORY: + Johnl 16-Aug-1993 Created + +********************************************************************/ + +NCBERR VxdReset( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + UCHAR i ; + TDI_STATUS tdistatus ; + PLIST_ENTRY pHead, pEntry ; + PRESET_CONTEXT pRstCont = (PRESET_CONTEXT) pNCB->ncb_reserve ; + + pRstCont->cActiveSessions = 0 ; + pRstCont->cActiveNames = 0 ; + pRstCont->errncb = NRC_GOODRET ; + + // + // Kill off all of the Receive any from any NCBs + // + while ( !IsListEmpty(&pDeviceContext->RcvAnyFromAnyHead)) + { + PRCV_CONTEXT prcvCont ; + pEntry = RemoveHeadList( &pDeviceContext->RcvAnyFromAnyHead ) ; + prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + CTEIoComplete( prcvCont->pNCB, + STATUS_CONNECTION_DISCONNECTED, + 0 ) ; + } + + // + // Kill off all of the Receive any datagrams from any + // + while ( !IsListEmpty(&pDeviceContext->RcvDGAnyFromAnyHead)) + { + tRCVELE * pRcvEle ; + pEntry = RemoveHeadList( &pDeviceContext->RcvDGAnyFromAnyHead ) ; + pRcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ; + CTEIoComplete( pRcvEle->pIrp, + STATUS_NETWORK_NAME_DELETED, // NRC_NAMERR + 0 ) ; + CTEMemFree( pRcvEle ) ; + } + + // + // Disconnect all sessions + // + for ( i = 1 ; i <= pDeviceContext->cMaxSessions ; i++ ) + { + // + // This will also prevent any listens from being accepted on the + // connection + // + if ( pDeviceContext->pSessionTable[i] != NULL ) + { + TDI_REQUEST Request ; + ULONG TimeOut = DISCONNECT_TIMEOUT ; + tCONNECTELE * pConnEle= pDeviceContext->pSessionTable[i] ; + + Request.Handle.ConnectionContext = pConnEle ; + pRstCont->cActiveSessions++ ; + tdistatus = NbtDisconnect( &Request, + &TimeOut, + TDI_DISCONNECT_RELEASE, + NULL, + NULL, + pNCB ) ; + + if ( tdistatus != TDI_PENDING ) + { + pRstCont->cActiveSessions-- ; + tdistatus = NbtCloseConnection( &Request, + NULL, + pDeviceContext, + NULL ) ; + REQUIRE( NBUnregister( pDeviceContext, i, NB_SESSION )) ; + } + } + } + + // + // If no active sessions, then go ahead and delete all the names + // + if ( !pRstCont->cActiveSessions ) + { + pRstCont->cActiveSessions = -1 ; + return VxdResetContinue( pDeviceContext, pNCB ) ; + } + + return NRC_GOODRET ; +} + +/******************************************************************* + + NAME: VxdResetContinue + + SYNOPSIS: Finishes the reset after all sessions have been successfully + shutdown + + ENTRY: pDeviceContext - Adapter status to get + pNCB - Pointer to requesting NCB + + EXIT: + + NOTES: + + HISTORY: + Johnl 16-Aug-1993 Created + +********************************************************************/ + +NCBERR VxdResetContinue( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + UCHAR i ; + TDI_STATUS tdistatus ; + PLIST_ENTRY pHead, pEntry ; + PRESET_CONTEXT pRstCont = (PRESET_CONTEXT) pNCB->ncb_reserve ; + PNCB pNCBPerm ; + + DbgPrint("VxdResetContinue entered\r\n") ; + + // + // Now that all of the sessions have been disconnected, close each + // connection + // + for ( i = 1 ; i <= pDeviceContext->cMaxSessions ; i++ ) + { + if ( pDeviceContext->pSessionTable[i] != NULL ) + { + TDI_REQUEST Request ; + tCONNECTELE * pConnEle = pDeviceContext->pSessionTable[i] ; + Request.Handle.ConnectionContext = pConnEle ; + + tdistatus = NbtCloseConnection( &Request, + NULL, + pDeviceContext, + NULL ) ; + REQUIRE( NBUnregister( pDeviceContext, i, NB_SESSION )) ; + } + } + + // + // Delete all the names (including the permanent name) + // + for ( i = 0 ; i <= pDeviceContext->cMaxNames ; i++ ) + { + if ( pDeviceContext->pNameTable[i] != NULL ) + { + TDI_REQUEST Request ; + + Request.Handle.ConnectionContext = pDeviceContext->pNameTable[i] ; + pRstCont->cActiveNames++ ; + + tdistatus = NbtCloseAddress( &Request, + NULL, //&RequestStatus, + pDeviceContext, + pNCB ) ; + if ( tdistatus != TDI_PENDING ) + pRstCont->cActiveNames-- ; + + // + // Go ahead and remove the name from the table since nobody + // will be able to re-register with it since this is a "wait" cmd + // + REQUIRE( NBUnregister( pDeviceContext, i, NB_NAME )) ; + + } + } + + // + // Resize the session table If an error occurs, keep the old + // session table. + // + if ( pNCB->ncb_lsn != pDeviceContext->cMaxSessions ) + { + UCHAR MaxSess = (UCHAR) pNCB->ncb_lsn ? pNCB->ncb_lsn : 6 ; + PVOID pSess = CTEAllocMem((USHORT)((MaxSess+1)*sizeof(tCONNECTELE*))) ; + + if ( !pSess ) + { + pRstCont->errncb = NRC_NORESOURCES ; + } + else + { + CTEFreeMem( pDeviceContext->pSessionTable ) ; + pDeviceContext->cMaxSessions = MaxSess ; + pDeviceContext->pSessionTable = pSess ; + CTEZeroMemory( &pDeviceContext->pSessionTable[0], + (pDeviceContext->cMaxSessions+1)*sizeof(tCONNECTELE*) ) ; + } + } + + // + // Set current session/name numbers back to 1 + // + pDeviceContext->iNcbNum = 1 ; + pDeviceContext->iLSNum = 1 ; + + // + // re-add the permanent name for this adapter, non-fatal if it fails + // + + + if ( !NT_SUCCESS( NbtAddPermanentName( pDeviceContext ))) + { + CDbgPrint( DBGFLAG_ERROR, + ("VxdResetContinue: Warning - Failed to add permanent name")) ; + } + + if ( !pRstCont->cActiveNames ) + CTEIoComplete( pNCB, NRC_GOODRET, 0 ) ; + + return NRC_GOODRET ; +} + +/******************************************************************* + + NAME: VxdCancel + + SYNOPSIS: Attempts to cancel the NCB pointed at by ncb_buffer + + ENTRY: pDeviceContext - Adapter status to get + pNCB - Pointer to requesting NCB + + EXIT: + + NOTES: + + HISTORY: + Johnl 18-Aug-1993 Created + +********************************************************************/ + +NCBERR VxdCancel( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) +{ + NCB * pNCBCancelled = (NCB*) pNCB->ncb_buffer ; + tCONNECTELE * pConnEle ; + tCLIENTELE * pClientEle ; + NCBERR errNCB = NRC_GOODRET ; + TDI_STATUS tdistatus ; + PLIST_ENTRY pHead, pEntry ; + USHORT NameType ; + tNAMEADDR * pNameAddr ; + tLISTENREQUESTS * pListen ; + PRCV_CONTEXT prcvCont ; // Used for session receives + tRCVELE * prcvEle ; // Used for Datagram receives + + if ( pNCB->ncb_lana_num != pNCBCancelled->ncb_lana_num ) + { + DbgPrint("VxdCancel: Attempt to cancel NCB w/ different lana\r\n") ; + return NRC_BRIDGE ; + } + + if ( pNCB->ncb_retcode != NRC_PENDING ) + return NRC_CANOCCR ; + + switch ( pNCBCancelled->ncb_command & ~ASYNCH ) + { + case NCBSEND: + case NCBSENDNA: + case NCBCHAINSEND: + case NCBCHAINSENDNA: + case NCBRECV: + // + // Cancelling a session NCB automatically closes the session + // + if ( VxdFindConnectElement( pDeviceContext, + pNCBCancelled, + &pConnEle )) + { + DbgPrint("VxdCancel: Attempted to cancel send NCB on non-existent session\r\n") ; + break ; + } + + if ( (pNCBCancelled->ncb_command & ~ASYNCH) == NCBRECV ) + { + errNCB = NRC_CANOCCR ; + for ( pEntry = pConnEle->RcvHead.Flink ; + pEntry != &pConnEle->RcvHead ; + pEntry = pEntry->Flink ) + { + prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + if ( prcvCont->pNCB == pNCBCancelled ) + { + RemoveEntryList( pEntry ) ; + CTEIoComplete( prcvCont->pNCB, STATUS_CANCELLED, 0 ) ; + errNCB = NRC_GOODRET ; + break ; + } + } + } + else + { + // + // Sends are immediately submitted to the transport, tell + // caller it's too late to cancel. The transport will complete + // the NCB when we close the connection below. + // + errNCB = NRC_CANOCCR ; + } + + REQUIRE( !VxdCompleteSessionNcbs( pDeviceContext, pConnEle )) ; + VxdTearDownSession( pDeviceContext, + pConnEle, + NULL, + NULL ) ; + // + // Only remove from table if we've told the client + // + if ( pConnEle->Flags & NB_CLIENT_NOTIFIED ) + { + REQUIRE( NBUnregister( pDeviceContext, + pNCBCancelled->ncb_lsn, + NB_SESSION )) ; + } + break ; + + case NCBCANCEL: + errNCB = NRC_CANCEL ; // Can't cancel a cancel + break ; + + case NCBLISTEN: + // + // Lookup the Client Element associated with this name, then scan + // the listen NCBs for one that matches the one being cancelled + // + if ( errNCB = VxdNameToClient( pDeviceContext, + pNCBCancelled->ncb_name, + NULL, + &pClientEle )) + { + DbgPrint("VxdCancel: Tried to cancel listen on non-existent name\r\n") ; + errNCB = NRC_CANOCCR ; + break ; + } + + errNCB = NRC_CANOCCR ; + for ( pEntry = pClientEle->ListenHead.Flink ; + pEntry != &pClientEle->ListenHead ; + pEntry = pEntry->Flink ) + { + pListen = CONTAINING_RECORD( pEntry, tLISTENREQUESTS, Linkage ) ; + if ( pListen->pIrp == pNCBCancelled ) + { + DbgPrint("VxdCancel: Cancelling NCB 0x") ; + DbgPrintNum( (ULONG) pNCBCancelled ) ; DbgPrint("\r\n") ; + RemoveEntryList( &pListen->Linkage ) ; + CTEIoComplete( pNCBCancelled, STATUS_CANCELLED, 0 ) ; + CTEMemFree( pListen ) ; + errNCB = NRC_GOODRET ; + break ; + } + } + break ; + + case NCBCALL: + // + // Search the ConnectActive list for our NCB and cleanup that + // connection + // + if ( errNCB = VxdNameToClient( pDeviceContext, + pNCBCancelled->ncb_name, + NULL, + &pClientEle )) + { + DbgPrint("VxdCancel: Tried to cancel call on non-existent name\r\n") ; + errNCB = NRC_CANOCCR ; + break ; + } + + errNCB = NRC_CANOCCR ; + for ( pEntry = pClientEle->ConnectActive.Flink ; + pEntry != &pClientEle->ConnectActive ; + pEntry = pEntry->Flink ) + { + pConnEle = CONTAINING_RECORD( pEntry, tCONNECTELE, Linkage ) ; + if ( pConnEle->pIrp == pNCBCancelled ) + { + tDGRAM_SEND_TRACKING * pTracker = (tDGRAM_SEND_TRACKING*) + pConnEle->pIrpRcv ; + + // + // if it's too late, just say we can't cancel it + // + if (pConnEle->state >= NBT_SESSION_OUTBOUND) + { + errNCB = NRC_CANOCCR ; + break; + } + + // + // yes, we can cancel it. we just mark the tracker to say + // this call is cancelled: both the original ncb and this + // cancel ncb will get completed at some stage. + // + DbgPrint("VxdCancel: Cancelling NCB 0x") ; + DbgPrintNum( (ULONG) pNCBCancelled ) ; DbgPrint("\r\n") ; + + pTracker->Flags |= TRACKER_CANCELLED; + pConnEle->pIrpDisc = pNCB; + + return NRC_GOODRET ; + } + } + break ; + + case NCBDGRECV: + if ( pNCBCancelled->ncb_num == ANY_NAME ) + { + pHead = &pDeviceContext->RcvDGAnyFromAnyHead ; + } + else + { + if ( errNCB = VxdFindClientElement( pDeviceContext, + pNCBCancelled->ncb_num, + &pClientEle, + CLIENT_LOCAL ) ) + { + ASSERT( FALSE ) ; + break ; + } + pHead = &pClientEle->RcvDgramHead ; + } + + errNCB = NRC_CANOCCR ; + for ( pEntry = pHead->Flink ; + pEntry != pHead ; + pEntry = pEntry->Flink ) + { + prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ; + + if ( prcvEle->pIrp == pNCBCancelled ) + { + RemoveEntryList( pEntry ) ; + CTEIoComplete( pNCBCancelled, STATUS_CANCELLED, 0 ) ; + CTEMemFree( prcvEle ) ; + errNCB = NRC_GOODRET ; + break ; + } + } + break ; + + case NCBDGRECVBC: + // + // For receive broadcast datagrams, we have to look through the list + // of clients on the Broadcast Address. + // + errNCB = VxdFindClientElement( pDeviceContext, + 0, + &pClientEle, + CLIENT_BC ) ; + if ( !errNCB ) + { + errNCB = NRC_CANOCCR ; + for ( pEntry = pClientEle->RcvDgramHead.Flink ; + pEntry != &pClientEle->RcvDgramHead ; + pEntry = pEntry->Flink ) + { + prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ; + if ( prcvEle->pIrp == pNCBCancelled ) + { + RemoveEntryList( pEntry ) ; + CTEMemFree( prcvEle ) ; + CTEIoComplete( pNCBCancelled, STATUS_CANCELLED, 0 ) ; + errNCB = NRC_GOODRET ; + break ; + } + } + } + break ; + + case NCBRECVANY: + if ( pNCBCancelled->ncb_num == ANY_NAME ) + pHead = &pDeviceContext->RcvAnyFromAnyHead ; + else + { + if ( errNCB = VxdFindClientElement( pDeviceContext, + pNCBCancelled->ncb_num, + &pClientEle, + CLIENT_LOCAL ) ) + { + ASSERT( FALSE ) ; + break ; + } + pHead = &pClientEle->RcvAnyHead ; + } + + errNCB = NRC_CANOCCR ; + pEntry = pHead->Flink ; + while ( pEntry != pHead ) + { + prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + if ( prcvCont->pNCB == pNCBCancelled ) + { + RemoveEntryList( pEntry ) ; + CTEIoComplete( prcvCont->pNCB, STATUS_CANCELLED, 0 ) ; + errNCB = NRC_GOODRET ; + break ; + } + pEntry = pEntry->Flink ; + } + break ; + + default: + errNCB = NRC_CANCEL ; + } + + + CTEIoComplete( pNCB, errNCB, 0 ) ; + + // + // No, no! Don't touch that ncb after completing it! + // + //pNCB->ncb_retcode = errNCB ; + //pNCB->ncb_cmd_cplt = errNCB ; + + return errNCB ; +} + +/******************************************************************* + + NAME: VxdIoComplete + + SYNOPSIS: Let's the NCB know that all processing is done by setting + the command completion fields and calling the post routine + if available. + + ENTRY: pirp - Pointer to the NCB to notify that we are done + (or NULL if this didn't come from the Netbios I/F + status - Status of the completion + ulExtra - Extra parameter + + NOTES: This is the procedure that CTEIoComplete maps to and is + roughly equivilent to "completing" an IRP. + + HISTORY: + Johnl 27-Apr-1993 Created + +********************************************************************/ + +VOID VxdIoComplete( PCTE_IRP pirp, + NTSTATUS status, + ULONG ulExtra ) +{ + NCB * pNCB = pirp ; + NCBERR errNCB = NRC_GOODRET ; + PSESS_SETUP_CONTEXT pSessSetupCont ; + BOOL fAsync ; + tDEVICECONTEXT * pDeviceContext ; + PRESET_CONTEXT pRstCont ; + PSEND_CONTEXT pSendCont ; + tCONNECTELE * pConnEle ; + + DbgPrint("VxdIoComplete: Completing NCB; Cmd, Addr, TDI status: 0x") ; + if ( pNCB ) + { + DbgPrintNum( pNCB->ncb_command ) ; + DbgPrint(" 0x") ; DbgPrintNum( (ULONG) pNCB ) ; + DbgPrint(" 0x") ; DbgPrintNum( status ) ; + DbgPrint("\r\n") ; + } + else + DbgPrint("NULL\r\n") ; + + // + // If no NCB to complete then we're done + // + if ( !pNCB ) + return ; + + fAsync = !!(pNCB->ncb_command & ASYNCH) ; + + pDeviceContext = GetDeviceContext( pNCB ) ; + + ASSERT(pDeviceContext); + + // + // Note that we drop through the below case statement even if an error + // occurred because some commands need to free stuff before completing + // the NCB. + // + if ( status != STATUS_SUCCESS && + ( pNCB->ncb_command & ~ASYNCH) != NCBCANCEL ) + { + errNCB = MapTDIStatus2NCBErr( status ) ; + } + + // + // Fill in any items in the NCB struct if necessary + // + switch( pNCB->ncb_command & ~ASYNCH ) + { + case NCBRECVANY: // lsn was set when the receive was posted + case NCBRECV: + FreeRcvContext( *((PRCV_CONTEXT*)&pNCB->ncb_reserve) ) ; + if ( errNCB && errNCB != NRC_INCOMP ) + { + break ; + } + + ASSERT( ulExtra <= 0xffff ) ; + ASSERT( pNCB->ncb_length >= ulExtra ) ; + pNCB->ncb_length = (WORD) ulExtra ; + + DbgPrint("\tSetting length to 0x") ; + DbgPrintNum( ulExtra ) ; + DbgPrint("\r\n") ; + break ; + + case NCBSSTAT: + case NCBDGRECV: + case NCBDGRECVBC: + if ( errNCB && errNCB != NRC_INCOMP ) + break ; + + ASSERT( ulExtra <= 0xffff ) ; + ASSERT( pNCB->ncb_length >= ulExtra ) ; + pNCB->ncb_length = (WORD) ulExtra ; + + DbgPrint("\tSetting length to 0x") ; + DbgPrintNum( ulExtra ) ; + DbgPrint("\r\n") ; + break ; + + case NCBASTAT: + case NCBFINDNAME: + if ( errNCB && errNCB != NRC_INCOMP ) + break ; + + if ( ulExtra != 0xffffffff ) // Means buffer length already set + pNCB->ncb_length = (WORD) ulExtra ; + + DbgPrint("\tAStat/Findname length is 0x") ; + DbgPrintNum( (ULONG) pNCB->ncb_length ) ; + DbgPrint("\r\n") ; + break ; + + case NCBSEND: + case NCBSENDNA: + case NCBCHAINSEND: + case NCBCHAINSENDNA: + pSendCont = (PSEND_CONTEXT) pNCB->ncb_reserve ; + if ( errNCB ) + { + // + // Sends are immediately given to the transport, so if a + // timeout occurs, we'll first be completed by the timeout + // code, then we'll be completed by the transport closing + // the connection. + // + + if ( errNCB != NRC_CMDTMO && + pSendCont->STO == NCB_TIMED_OUT ) + { + // + // The transport has completed this NCB in response to the + // Close connection because of a send timeout. Map the + // error to timeout and complete back to the client. The + // session is dead so don't disconnect it again. The send + // has already been removed from the timeout list. + // + errNCB = NRC_CMDTMO ; + } + else + { + BOOL fTimedOutNCB = pSendCont->STO == NCB_TIMED_OUT ; + + // + // Remove from timeout list + // + if ( pSendCont->STO != NCB_INFINITE_TIME_OUT ) + RemoveEntryList( &pSendCont->ListEntry ) ; + + // + // Kill the session + // + if ( VxdFindConnectElement( pDeviceContext, + pNCB, + &pConnEle )) + + { + // + // There maybe multiple sends on this session, only the + // first should disconnect + // + CTEFreeMem( pSendCont->pHdr ) ; + DbgPrint("VxdIoComplete: Error occurred on non-existent session\r\n") ; + break ; + } + + REQUIRE( !VxdCompleteSessionNcbs( pDeviceContext, pConnEle )) ; + + // + // Only remove from table if we've told the client + // + if ( pConnEle->Flags & NB_CLIENT_NOTIFIED ) + { + REQUIRE( NBUnregister( pDeviceContext, + pNCB->ncb_lsn, + NB_SESSION )) ; + } + + VxdTearDownSession( pDeviceContext, + pConnEle, + NULL, + NULL ) ; + + if ( fTimedOutNCB ) // pSendCont may already have been freed + { + // + // The Close Connection above will cause the transport to + // complete this send with a session closed error, wait + // for that before completing back to the client. + // + return ; + } + } + } + else + { + if ( pSendCont->STO != NCB_INFINITE_TIME_OUT ) + RemoveEntryList( &pSendCont->ListEntry ) ; + } + FreeSessionHdr( pSendCont->pHdr ) ; + break ; + + case NCBDGSEND: + case NCBDGSENDBC: + // + // Nothing to do + // + break ; + + // + // Need to set the ncb_num field for the following commands. Note + // that the ulExtra parameter will contain a pointer to the address + // element that was just added for this name. + // + case NCBADDNAME: + case NCBADDGRNAME: + if ( errNCB ) + break ; + + if ( !NBRegister( pDeviceContext, + &pNCB->ncb_num, + (tCLIENTELE *) ulExtra, + NB_NAME )) + { + TDI_REQUEST Request ; + TDI_STATUS tdistatus ; + + errNCB = NRC_NAMTFUL ; + Request.Handle.ConnectionContext = (tCLIENTELE *) ulExtra ; + tdistatus = NbtCloseAddress( &Request, + NULL, //&RequestStatus, + pDeviceContext, + NULL ) ; + ASSERT( NT_SUCCESS( tdistatus )) ; + } + else + { + DbgPrint("\tRegistered Name number ") ; + DbgPrintNum( pNCB->ncb_num ) ; + DbgPrint(" for Address Element ") ; + DbgPrintNum( ulExtra ) ; + DbgPrint("\r\n") ; + } + break ; + +#if 0 + // + // Private NBT NCB type for processing the permanent name + // + case NCBADD_PERMANENT_NAME: + CTEFreeMem( pNCB ) ; + + if ( errNCB ) + { + DbgPrint("VxdIoComplete: Failed to add permanent name!\r\n") ; + } + else + { + ASSERT( pDeviceContext->pNameTable[0] == NULL ) ; + pDeviceContext->pNameTable[0] = (tCLIENTELE *) ulExtra ; + } + // + // Don't do any further processing of this NCB. Not only did + // we just free it, nobody is looking for it. + // + return ; +#endif + + case NCBCALL: + case NCBLISTEN: + pSessSetupCont = (PSESS_SETUP_CONTEXT) pNCB->ncb_reserve ; + + if ( errNCB ) + { + VxdTearDownSession( pDeviceContext, + pSessSetupCont->pConnEle, + pSessSetupCont, + NULL ) ; + break ; + } + + // + // Put the connection in our LSN table and copy out the connecting + // name if necessary + // + if ( !NBRegister( pDeviceContext, + &pNCB->ncb_lsn, + (tCONNECTELE *) ulExtra, + NB_SESSION )) + { + VxdTearDownSession( pDeviceContext, + (tCONNECTELE *) ulExtra, + NULL, + NULL ) ; + errNCB = NRC_LOCTFUL ; + } + else + { + tCONNECTELE * pConnEle = (tCONNECTELE*) ulExtra ; + + // + // Were we listenning for '*'? If so, copy out the connecting + // name. + // + if ( pSessSetupCont->fIsWorldListen ) + { + DbgPrint( "VxdIoComplete: World listen accepted \"" ) ; + DbgPrint( pConnEle->RemoteName ) ; + DbgPrint("\" for connection endpoint\r\n") ; + + CTEMemCopy( pNCB->ncb_callname, + pConnEle->RemoteName, + NCBNAMSZ ) ; + } + + ((tCONNECTELE *)ulExtra)->RTO = pNCB->ncb_rto ; + ((tCONNECTELE *)ulExtra)->STO = pNCB->ncb_sto ; + ((tCONNECTELE *)ulExtra)->Flags = 0 ; + + // + // Don't delete the connection element until the client has been + // notified that the connection is down + // + ((tCONNECTELE *)ulExtra)->RefCount++ ; + + DbgPrint("\tRegistered Session number ") ; + DbgPrintNum( pNCB->ncb_lsn ) ; + DbgPrint(" for Connection Element ") ; + DbgPrintNum( (ULONG) pConnEle ) ; + DbgPrint("\r\n") ; + } + + FreeSessSetupContext( pSessSetupCont ) ; + + // + // If we're in WAITACCEPT, then this is a Listen that needs to + // be accepted + // + if ( ((tCONNECTELE *)ulExtra)->state == NBT_SESSION_WAITACCEPT ) + { + // + // Accept the connection + // + VxdAccept( (tCONNECTELE *) ulExtra, NULL ) ; + } + break ; + + case NCBRESET: + pRstCont = (PRESET_CONTEXT) pNCB->ncb_reserve ; + if ( pRstCont->cActiveSessions != -1 || + pRstCont->cActiveNames ) + { + DbgPrint("VxdIoComplete: Disconnect/Name de-reg completed from Reset, remaining disconnects, names: ") ; + DbgPrintNum( pRstCont->cActiveSessions ) ; + DbgPrintNum( pRstCont->cActiveNames ) ; DbgPrint("\r\n") ; + + // + // Only complete the Reset NCB after all session have been + // disconnected and the names have been released on the network + // + if ( pRstCont->cActiveSessions != -1 ) + { + if ( --pRstCont->cActiveSessions == 0 ) + { + // + // Starts the name deletion process + // + pRstCont->cActiveSessions = -1 ; + VxdResetContinue( pDeviceContext, pNCB ) ; + } + + return ; + } + + if ( --pRstCont->cActiveNames != 0 ) + { + return ; + } + } + + if ( !errNCB ) + errNCB = pRstCont->errncb ; + + // Fall through + + case NCBUNLINK: + pNCB->ncb_retcode = errNCB ; + pNCB->ncb_cmd_cplt = errNCB ; + goto SkipPost ; + + case NCBCANCEL: + pNCB->ncb_retcode = errNCB ; + pNCB->ncb_cmd_cplt = errNCB ; + break; + + case NCBHANGUP: + case NCBDELNAME: + case NCBTRACE: + break ; + + default: + DbgPrint("VxdIoComplete: Unexpected NCB command: 0x") ; + DbgPrintNum( pNCB->ncb_command ) ; DbgPrint("\r\n") ; + break ; + } + + if ( pNCB->ncb_retcode == NRC_PENDING ) + { + pNCB->ncb_retcode = errNCB ; + pNCB->ncb_cmd_cplt = errNCB ; + } + else + { + if ( (pNCB->ncb_command & ~ASYNCH) != NCBCANCEL ) + { + CTEPrint("VxdIoComplete: ncb_retcode already set!\r\n") ; + CTEPrint("\tCommand: 0x") ; DbgPrintNum(pNCB->ncb_command) ; + CTEPrint(" NCB Address: 0x") ; DbgPrintNum( (ULONG) pNCB ) ; + } + goto SkipPost ; + } + + // + // call the post-routine only if this was a no-wait call and if + // the post-routine has been specified! + // + if ( fAsync && pNCB->ncb_post ) + { + typedef void (CALLBACK * VXDNCBPost )( void ) ; + VXDNCBPost ncbpost = (VXDNCBPost) pNCB->ncb_post ; + + // + // Clients are expecting EBX to point to the NCB (instead of + // pushing it on the stack...). The post routine may trash + // ebp also, so save it. + // + _asm push ebp ; + _asm mov ebx, pNCB ; + ncbpost() ; + _asm pop ebp ; + } + +SkipPost: + // + // Now that we've completed the NCB, unblock if it was a Wait NCB + // + if ( !fAsync ) + { + PBLOCKING_NCB_CONTEXT pBlkNcbContext; + PLIST_ENTRY pHead, pEntry ; + + // + // find the blocking ncb context from the list corresponding to this ncb + // + pHead = &NbtConfig.BlockingNcbs; + pEntry = pHead->Flink; + while( pEntry != pHead ) + { + pBlkNcbContext = CONTAINING_RECORD( pEntry, BLOCKING_NCB_CONTEXT, Linkage ) ; + if (pBlkNcbContext->pNCB == pNCB) + break; + else + pBlkNcbContext = NULL; + pEntry = pEntry->Flink; + } + + if (pBlkNcbContext) + { + ASSERT(pBlkNcbContext->Verify == NBT_VERIFY_BLOCKING_NCB); + + // + // if the ncb is blocked for completion, remove the context from + // the list first (important!) and then signal the thread that we + // are done. Then free the memory. + // + if ( pBlkNcbContext->fBlocked ) + { + RemoveEntryList(&pBlkNcbContext->Linkage); + CTESignal( pBlkNcbContext->pWaitNCBBlock, 0 ) ; + CTEFreeMem(pBlkNcbContext->pWaitNCBBlock); + CTEFreeMem(pBlkNcbContext); + } + else + { + pBlkNcbContext->fNCBCompleted = TRUE; + } + } + else + { + DbgPrint("VxdIoComplete: didn't find blocking ncb context\r\n") ; + DbgPrint("for NCB Address: 0x") ; DbgPrintNum( (ULONG) pNCB ) ; + } + } +} + +/******************************************************************* + + NAME: VxdInitSessionSetup + + SYNOPSIS: Common initialization required for Call and Listen + + ENTRY: pDeviceContext - Adapter to setup on + pRequest - Request to fill in if successful + ppSessSetupContext - Context to be filled + pNCB - NCB doing the call/listen + + EXIT: + + RETURNS: NRC_GOODRET if successful, error code otherwise + + NOTES: + + HISTORY: + Johnl 26-May-1993 Created + +********************************************************************/ + +NCBERR VxdInitSessionSetup( tDEVICECONTEXT * pDeviceContext, + TDI_REQUEST * pRequest, + PSESS_SETUP_CONTEXT * ppSessSetupContext, + NCB * pNCB ) +{ + NTSTATUS status; + NCBERR errNCB ; + TDI_REQUEST_STATUS RequestStatus ; + tCLIENTELE * pClientEle ; + tCONNECTELE * pConnEle ; + tNAMEADDR * pNameAddr ; + USHORT NameType ; + BOOL fIsListen = ((pNCB->ncb_command & ~ASYNCH) + == NCBLISTEN) ; + + *ppSessSetupContext = NULL ; + + // + // Lookup the Client Element associated with this name and verify + // it's valid + // + if ( errNCB = VxdNameToClient( pDeviceContext, + pNCB->ncb_name, + NULL, + &pClientEle )) + { + return errNCB ; + } + + if ( pClientEle->fDeregistered ) + return NRC_NOWILD ; + + // + // Request.Handle.ConnectionContext will contain Connection + // element after we open the connection + // + if ( status = NbtOpenConnection( pRequest, + NULL, //ConnectionContext, // Passed to connect and disconnect handlers + pDeviceContext ) ) + { + return MapTDIStatus2NCBErr( status ) ; + } + + // + // Initialize the connection context (used by Vxd disconnect handler) + // + pConnEle = (tCONNECTELE *) pRequest->Handle.ConnectionContext ; + pConnEle->ConnectContext = pConnEle ; + + if ( status = NbtAssociateAddress( pRequest, + pClientEle, + NULL )) + { + goto ErrorExit1 ; + } + + ASSERT( sizeof( SESS_SETUP_CONTEXT ) <= (sizeof( pNCB->ncb_reserve ) + + sizeof( pNCB->ncb_event )) ) ; + *ppSessSetupContext = (PSESS_SETUP_CONTEXT) pNCB->ncb_reserve ; + if ( status = AllocSessSetupContext( *ppSessSetupContext, + *pNCB->ncb_callname == '*' ) ) + goto ErrorExit0 ; + + // + // Listen for '*' uses a NULL Request remote address + // + if ( *pNCB->ncb_callname != '*' ) + { + InitNBTDIConnectInfo( (*ppSessSetupContext)->pRequestConnect, + (*ppSessSetupContext)->pRequestConnect->RemoteAddress, + pNCB->ncb_callname ) ; + } + + InitNBTDIConnectInfo( (*ppSessSetupContext)->pReturnConnect, + (*ppSessSetupContext)->pReturnConnect->RemoteAddress, + pNCB->ncb_name ) ; + (*ppSessSetupContext)->fIsWorldListen = (pNCB->ncb_callname[0] == '*') ; + (*ppSessSetupContext)->pConnEle = pConnEle ; + + return NRC_GOODRET ; + +ErrorExit0: + if ( !(NbtDisassociateAddress( pRequest ) == TDI_SUCCESS)) + CTEPrint("VxdInitSessionSetup: AllocSesssetupContext failed and DisassociateAddress failed\r\n") ; + +ErrorExit1: + REQUIRE( NbtCloseConnection( pRequest, + &RequestStatus, + pDeviceContext, + NULL ) == TDI_SUCCESS ) ; + + return MapTDIStatus2NCBErr( status ) ; +} + +/******************************************************************* + + NAME: VxdFindClientElement + + SYNOPSIS: Finds the appropriate client element + + ENTRY: pDeviceContext - Device to search on + ncbnum - NCB Name Number + ppClientEle - Receives result of search + Type - If CLIENT_BC (broadcast), then the Broadcast client + element for the pDeviceContext adapter is returned + + RETURNS: STATUS_SUCCESS if the name is found, error code otherwise + + NOTES: The device context points to a list of Address elements (one + address element for each Netbios name in the system). Each + address element has a Client Element list hanging off of it. + We return the first client element off of the Address Element + as there shouldn't be more then one client (is this true?). + + HISTORY: + Johnl 23-Apr-1993 Created + +********************************************************************/ + +NCBERR VxdFindClientElement( tDEVICECONTEXT * pDeviceContext, + UCHAR ncbnum, + tCLIENTELE * * ppClientEle, + enum CLIENT_TYPE Type ) +{ + ASSERT( pDeviceContext != NULL ) ; + if ( !pDeviceContext ) + return NRC_SYSTEM ; + + if ( Type != CLIENT_BC ) + { + if ( ncbnum > pDeviceContext->cMaxNames || !pDeviceContext->pNameTable[ncbnum] ) + return NRC_ILLNN ; + + *ppClientEle = (tCLIENTELE *) pDeviceContext->pNameTable[ncbnum] ; + return NRC_GOODRET ; + } + else + { + NTSTATUS status; + tCLIENTELE * pClientEleBcast ; + UCHAR pName[NETBIOS_NAME_SIZE]; + tNAMEADDR * pNameAddr; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + // + // find the * name in the local hash table + // + CTEZeroMemory(pName,NETBIOS_NAME_SIZE); + + pName[0] = '*'; + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + NbtConfig.pScope, + &pNameAddr); + + if (NT_SUCCESS(status)) + { + pHead = &pNameAddr->pAddressEle->ClientHead; + pEntry = pHead->Flink; + + while ( pEntry != pHead ) + { + pClientEleBcast = CONTAINING_RECORD( pEntry, tCLIENTELE, Linkage ) ; + if ( pClientEleBcast->pDeviceContext == pDeviceContext ) + { + *ppClientEle = pClientEleBcast ; + break ; + } + pEntry = pEntry->Flink ; + } + } + else + { + return(NRC_ILLNN); + } + + if ( pEntry == pHead ) + return NRC_ILLNN ; + } + + return NRC_GOODRET ; +} + +/******************************************************************* + + NAME: VxdFindConnectElement + + SYNOPSIS: Finds the appropriate connect element from the + session number + + ENTRY: pDeviceContext - Device to search on + lsn - NCB LS Number + ppConnectEle - Receives result of search + + RETURNS: NRC_GOODRET if successful, error otherwise + + NOTES: LSN 0 will be disallowed because pSessionTable[0] is always + NULL + + HISTORY: + Johnl 23-Apr-1993 Created + +********************************************************************/ + +NCBERR VxdFindConnectElement( tDEVICECONTEXT * pDeviceContext, + NCB * pNCB, + tCONNECTELE * * ppConnectEle ) +{ + UCHAR lsn ; + ASSERT( pNCB != NULL ) ; + ASSERT( pDeviceContext != NULL ) ; + + if ( !pDeviceContext || !pNCB ) + return NRC_SYSTEM ; + + lsn = pNCB->ncb_lsn ; + if ( lsn > pDeviceContext->cMaxSessions || !pDeviceContext->pSessionTable[lsn] ) + return NRC_SNUMOUT ; + + *ppConnectEle = pDeviceContext->pSessionTable[lsn] ; + + // + // Check to see if the connection is down but the NB client hasn't been + // notified, if so notify them and remove the session from the table + // + if ( ( (*ppConnectEle)->state == NBT_ASSOCIATED || + (*ppConnectEle)->state == NBT_IDLE ) && + (*ppConnectEle)->RefCount == 1 ) + { + DbgPrint("VxdFindConnectElement: Deleting connection element\r\n") ; + NbtDereferenceConnection( *ppConnectEle ) ; + REQUIRE( NBUnregister( pDeviceContext, lsn, NB_SESSION )) ; + return NRC_SCLOSED ; + } + + return NRC_GOODRET ; +} + +/******************************************************************* + + NAME: VxdFindLSN + + SYNOPSIS: Finds a session number from its tCONNECTELE *. + + ENTRY: pDeviceContext - Device to search on + pConnectEle - Connect element to find + plsn - Index pConnectEle was found at + + NOTES: + + HISTORY: + Johnl 07-Jul-1993 Created + +********************************************************************/ + +NCBERR VxdFindLSN( tDEVICECONTEXT * pDeviceContext, + tCONNECTELE * pConnectEle, + UCHAR * plsn ) +{ + ASSERT( (pDeviceContext != NULL) && (pConnectEle != NULL) && (plsn != NULL)) ; + ASSERT( pConnectEle->Verify == NBT_VERIFY_CONNECTION || + pConnectEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ; + + for ( *plsn = 0 ; *plsn <= pDeviceContext->cMaxSessions ; (*plsn)++ ) + { + if ( pDeviceContext->pSessionTable[*plsn] == pConnectEle ) + return NRC_GOODRET ; + } + + ASSERT( FALSE ) ; + return NRC_SNUMOUT ; +} + +/******************************************************************* + + NAME: VxdFindNameNum + + SYNOPSIS: Finds a name number from its tADDRESSELE *. + + ENTRY: pDeviceContext - Device to search on + pAddressEle - Address element to find + pNum - Index pAddressEle was found at + + NOTES: + + HISTORY: + Johnl 07-Jul-1993 Created + +********************************************************************/ + +NCBERR VxdFindNameNum( tDEVICECONTEXT * pDeviceContext, + tADDRESSELE * pAddressEle, + UCHAR * pNum ) +{ + tCLIENTELE *pClientEle; + + ASSERT( (pDeviceContext != NULL) && (pAddressEle != NULL) && (pNum != NULL)) ; + ASSERT( pAddressEle->Verify == NBT_VERIFY_ADDRESS ) ; + + for ( *pNum = 0 ; *pNum <= pDeviceContext->cMaxNames ; (*pNum)++ ) + { + pClientEle = pDeviceContext->pNameTable[*pNum]; + + if ( (pClientEle) && (pClientEle->pAddress == pAddressEle ) ) + return NRC_GOODRET ; + } + + return NRC_ILLNN ; +} + +/******************************************************************* + + NAME: VxdNameToClient + + SYNOPSIS: Converts a ncb_callname to the corresponding client element + in the name table + + ENTRY: pDeviceContext - Device to search on + pchName - Name to find + pNameNum - Index into table name number is at (Optional) + ppClientEle - Client element in the name table + + NOTES: + + HISTORY: + Johnl 15-Oct-1993 Created + +********************************************************************/ + +NCBERR VxdNameToClient( tDEVICECONTEXT * pDeviceContext, + CHAR * pName, + UCHAR * pNameNum, + tCLIENTELE * * ppClientEle ) +{ + USHORT NameType ; + tNAMEADDR * pNameAddr ; + UCHAR NameNum ; + NTSTATUS status; + + // + // Lookup the Client Element associated with this name + // + if ( pName[0] == '*' ) + { + return NRC_NOWILD ; // Also means name not found + } + + status = FindInHashTable( + NbtConfig.pLocalHashTbl, + pName, + NbtConfig.pScope, + &pNameAddr); + + if (!NT_SUCCESS(status)) + { + return NRC_NOWILD ; // Also means name not found + } + + // + // if the name is not registered on the adapter (provided by the client) + // tell the client so! + // + if ( pNameAddr->AdapterMask && + !(pNameAddr->AdapterMask & pDeviceContext->AdapterNumber) ) + { + DbgPrint("VxdNameToClient: wrong DeviceContext element\r\n") ; + return NRC_NOWILD ; + } + + if ( VxdFindNameNum( pDeviceContext, pNameAddr->pAddressEle, &NameNum )) + { + ASSERT( FALSE ) ; + return NRC_NOWILD ; + } + + REQUIRE( !VxdFindClientElement( pDeviceContext, + NameNum, + ppClientEle, + CLIENT_LOCAL )) ; + ASSERT( (*ppClientEle)->Verify == NBT_VERIFY_CLIENT ) ; + + if ( pNameNum != NULL ) + *pNameNum = NameNum ; + + return NRC_GOODRET ; +} + +/******************************************************************* + + NAME: NBRegister + + SYNOPSIS: Finds the next available slot in apElem and assigns + pElem to that slot according to Netbios rules + + + ENTRY: pDeviceContext - Adapter we are adding name to + pNCBNum - Receives the found free slot + pElem - Element we are registering + NbTable - Indicates the Name table or session table + + EXIT: *pNCBNum will point to the found slot and + apElem[*pNCBNum] will point to pElem + + RETURNS: TRUE if we found a free slot, FALSE if the table was + full. + + NOTES: The Netbios spec states that returned NCB nums and Logical + Session numbers increase to 254 until they wrap to 1 (0 + is reserved for the adapter name). + + HISTORY: + Johnl 28-Apr-1993 Created + +********************************************************************/ + +BOOL NBRegister( tDEVICECONTEXT * pDeviceContext, + UCHAR * pNCBNum, + PVOID pElem, + NB_TABLE_TYPE NbTable ) +{ + UCHAR i ; + BOOL fFound = FALSE ; + BOOL fPassTwo = FALSE ; + UCHAR MaxNCBNum ; + UCHAR * piCurrent ; + PVOID * apElem ; + + ASSERT( pElem != NULL ) ; + + if ( NbTable == NB_NAME ) + { + MaxNCBNum = pDeviceContext->cMaxNames ; + apElem = pDeviceContext->pNameTable ; + piCurrent = &pDeviceContext->iNcbNum ; + } + else + { + MaxNCBNum = pDeviceContext->cMaxSessions ; + apElem = pDeviceContext->pSessionTable ; + piCurrent = &pDeviceContext->iLSNum ; + } + + // + // Find the next free name number and store it in pNCBNum + // + for ( i = *piCurrent ; ; i++ ) + { + if ( i > MaxNCBNum ) + i = 1 ; + + if ( !apElem[i] ) + { + fFound = TRUE ; + break ; + } + + // + // Second time we hit *piCurrent means there are no free slots + // + if ( i == *piCurrent) + { + if ( fPassTwo ) + break ; + else + fPassTwo = TRUE ; + } + } + + if ( fFound ) + { + apElem[i] = pElem ; + *pNCBNum = *piCurrent = i ; + + (*piCurrent)++ ; + if ( *piCurrent > MaxNCBNum ) + *piCurrent = 1 ; + } + + return fFound ; +} + +/******************************************************************* + + NAME: NBUnregister + + SYNOPSIS: Invalidates the passed netbios number + + ENTRY: NCBNum - Name number to unregister + + EXIT: The name number entry will be set to NULL + + + RETURNS: TRUE if we freed the slot, FALSE if the name wasn't + registered in the first place or it's out of range + + NOTES: + + HISTORY: + Johnl 05-May-1993 Created + +********************************************************************/ + +BOOL NBUnregister( tDEVICECONTEXT * pDeviceContext, + UCHAR NCBNum, + NB_TABLE_TYPE NbTable ) +{ + UCHAR MaxNCBNum ; + PVOID * apElem ; + + if ( NbTable == NB_NAME ) + { + MaxNCBNum = pDeviceContext->cMaxNames ; + apElem = pDeviceContext->pNameTable ; + } + else + { + MaxNCBNum = pDeviceContext->cMaxSessions ; + apElem = pDeviceContext->pSessionTable ; + } + + if ( NCBNum > MaxNCBNum || apElem[NCBNum] == NULL ) + { + return FALSE ; + } + + apElem[NCBNum] = NULL ; + + return TRUE ; +} + +/******************************************************************* + + NAME: VxdCompleteSessionNcbs + + SYNOPSIS: Finds all NCBs attached to a session and completes them + + ENTRY: pDeviceContext - Device we are on + pConnEle - Session connection element to complete NCBs on + + NOTES: + + HISTORY: + Johnl 16-Aug-1993 Broke out as common code + +********************************************************************/ + +TDI_STATUS VxdCompleteSessionNcbs( tDEVICECONTEXT * pDeviceContext, + tCONNECTELE * pConnEle ) +{ + PLIST_ENTRY pHead, pEntry ; + PRCV_CONTEXT prcvCont ; + BOOL fCompleteToClient = TRUE ; + UCHAR lsn ; + NCBERR errNCB ; + BOOL fAnyFound = FALSE ; + + ASSERT( pConnEle != NULL ) ; + ASSERT( pConnEle->Verify == NBT_VERIFY_CONNECTION || + pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ; + + if ( errNCB = VxdFindLSN( pDeviceContext, + pConnEle, + &lsn )) + { + // + // This shouldn't happen but watch for it in case we get in a + // weird situation + // + DbgPrint("VxdCompleteSessionNCBs - Warning: VxdFindLsn failed\r\n") ; + return STATUS_UNSUCCESSFUL ; + } + + // + // Complete the first RcvAny + // + if ( pConnEle->pClientEle && + !IsListEmpty( &pConnEle->pClientEle->RcvAnyHead )) + { + pEntry = RemoveHeadList( &pConnEle->pClientEle->RcvAnyHead ) ; + prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + // + // Set the session number so the client knows which session is going + // away. + // + prcvCont->pNCB->ncb_lsn = lsn ; + CTEIoComplete( prcvCont->pNCB, + STATUS_CONNECTION_DISCONNECTED, + 0 ) ; + fAnyFound = TRUE ; + } + + // + // Now kill all of the outstanding receives. Sends are completed as + // they are submitted so nothing to kill. + // + while ( !IsListEmpty( &pConnEle->RcvHead )) + { + pEntry = RemoveHeadList( &pConnEle->RcvHead ) ; + prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ; + ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ; + + CTEIoComplete( prcvCont->pNCB, + STATUS_CONNECTION_DISCONNECTED, + 0 ) ; + fAnyFound = TRUE ; + } + + // + // Once the client has been notified, deref the connection + // element so the memory will be deleted when the connection is + // closed. If the client wasn't notified, then the connection remains + // in our table until the next NCB on this session. + // + if ( fAnyFound && + !(pConnEle->Flags & NB_CLIENT_NOTIFIED) ) + { + DbgPrint("CompleteSessionNcbs - Marking connection as notified\r\n") ; + pConnEle->Flags |= NB_CLIENT_NOTIFIED ; + NbtDereferenceConnection( pConnEle ) ; + } + + + return TDI_SUCCESS ; +} + +/******************************************************************* + + NAME: VxdTearDownSession + + SYNOPSIS: Closes a session and deletes its session context + + ENTRY: pConnEle - Pointer to connection session element to close + pCont - Session context to delete (or NULL to ignore) + pSessSetupContext - Session context to delete if non-NULL + pNCB - NCB to complete after disconnect finishes + + NOTES: + + HISTORY: + Johnl 16-Aug-1993 Commonized + +********************************************************************/ + +void VxdTearDownSession( tDEVICECONTEXT * pDeviceContext, + tCONNECTELE * pConnEle, + PSESS_SETUP_CONTEXT pSessSetupContext, + NCB * pNCB ) +{ + TDI_STATUS tdistatus ; + TDI_REQUEST Request ; + + if ( pConnEle != NULL ) + { + ASSERT((pConnEle->Verify == NBT_VERIFY_CONNECTION) || + (pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN)) ; + + Request.Handle.ConnectionContext = pConnEle ; + + tdistatus = NbtDisconnect( &Request, 0, TDI_DISCONNECT_ABORT, NULL, NULL, NULL ) ; + if ( tdistatus && tdistatus != TDI_PENDING ) + { + DbgPrint("VxdTearDownSession - NbtDisconnect returned error " ) ; + DbgPrintNum( tdistatus ) ; + DbgPrint("\r\n") ; + } + + tdistatus = NbtCloseConnection( &Request, + NULL, + pDeviceContext, + NULL ) ; + if ( tdistatus && tdistatus != TDI_PENDING ) + { + DbgPrint("VxdTearDownSession - NbtCloseConnection returned error " ) ; + DbgPrintNum( tdistatus ) ; + DbgPrint("\r\n") ; + } + } + + if ( pSessSetupContext ) + FreeSessSetupContext( pSessSetupContext ) ; +} +/******************************************************************* + + NAME: AllocSessSetupContext + + SYNOPSIS: Allocates and initializes a listen context structure + + ENTRY: pSessSetupContext - Pointer to structure + fListenOnStar - TRUE if the request remote address should + be left as NULL + + NOTES: + + HISTORY: + Johnl 19-May-1993 Created + +********************************************************************/ + +TDI_STATUS AllocSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext, + BOOL fListenOnStar ) +{ + CTEZeroMemory( pSessSetupContext, sizeof( SESS_SETUP_CONTEXT ) ) ; + + if ( !(pSessSetupContext->pRequestConnect = + CTEAllocMem( sizeof( TDI_CONNECTION_INFORMATION ))) || + !(pSessSetupContext->pReturnConnect = + CTEAllocMem( sizeof( TDI_CONNECTION_INFORMATION))) ) + { + goto ErrorExit1 ; + } + + pSessSetupContext->pRequestConnect->RemoteAddress = NULL ; + pSessSetupContext->pReturnConnect->RemoteAddress = NULL ; + + if ( !(pSessSetupContext->pReturnConnect->RemoteAddress = + CTEAllocMem( sizeof( TA_NETBIOS_ADDRESS ))) || + (!fListenOnStar && + !(pSessSetupContext->pRequestConnect->RemoteAddress = + CTEAllocMem( sizeof( TA_NETBIOS_ADDRESS )))) ) + { + goto ErrorExit0 ; + } + + return TDI_SUCCESS ; + +ErrorExit0: + if ( pSessSetupContext->pRequestConnect->RemoteAddress) + CTEFreeMem( pSessSetupContext->pRequestConnect->RemoteAddress ) ; + + if ( pSessSetupContext->pReturnConnect->RemoteAddress) + CTEFreeMem( pSessSetupContext->pReturnConnect->RemoteAddress ) ; + +ErrorExit1: + if ( pSessSetupContext->pRequestConnect) + CTEFreeMem( pSessSetupContext->pRequestConnect ) ; + + if ( pSessSetupContext->pReturnConnect) + CTEFreeMem( pSessSetupContext->pReturnConnect ) ; + + return TDI_NO_RESOURCES ; +} + +/******************************************************************* + + NAME: FreeSessSetupContext + + SYNOPSIS: Frees a successfully initialized listen context + + ENTRY: pSessSetupContext - Context to be freed + + HISTORY: + Johnl 19-May-1993 Created + +********************************************************************/ + +void FreeSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext ) +{ + if ( pSessSetupContext->pRequestConnect->RemoteAddress ) + CTEFreeMem( pSessSetupContext->pRequestConnect->RemoteAddress ) ; + + CTEFreeMem( pSessSetupContext->pReturnConnect->RemoteAddress ) ; + CTEFreeMem( pSessSetupContext->pRequestConnect ) ; + CTEFreeMem( pSessSetupContext->pReturnConnect ) ; +} + + +/******************************************************************* + + NAME: DelayedSessEstablish + + SYNOPSIS: This routine is called by VxdScheduleDelayedEvent. + After name query is successful, we typically make a tcp + connection. We delay that step until later so that stack + usage is reduced. (yes, there is only 4k of stack on chicago!) + + ENTRY: pContext - context that contains the actual parms + + RETURNS: Nothing + + HISTORY: + Koti Dec. 19, 94 + +********************************************************************/ +VOID DelayedSessEstablish( PVOID pContext ) +{ + tDGRAM_SEND_TRACKING *pTracker; + NTSTATUS status; + COMPLETIONCLIENT pClientCompletion; + + // + // get our parameters out + // + pTracker = ((NBT_WORK_ITEM_CONTEXT *)pContext)->pTracker; + status = (NTSTATUS)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + pClientCompletion = ((NBT_WORK_ITEM_CONTEXT *)pContext)->ClientCompletion; + + CTEMemFree(pContext); + + CompleteClientReq(pClientCompletion, + pTracker, + status); +} + + + +/******************************************************************* + + NAME: VxdApiWorker + + SYNOPSIS: When clients such as another vxd or a V86 app (such as + nbtstat.exe) make requests for information or some service, + this is the routine that gets called. + + ENTRY: OpCode - what info or service is being requested + ClientBuffer - buffer in which to pass info + ClientBufLen - how big is the buffer + + RETURNS: ErrorCode from the operation (0 if success) + + HISTORY: + Koti 16-Jun-1994 Created + +********************************************************************/ + +NTSTATUS +VxdApiWorker( + DWORD Ioctl, + PVOID ClientOutBuffer, + DWORD ClientOutBufLen, + PVOID ClientInBuffer, + DWORD ClientInBufLen, + DWORD fOkToTrashInputBuffer + ) +{ + + NTSTATUS status; + USHORT OpCode; + int i; + USHORT NumLanas; + PCHAR pchBuffer; + DWORD dwSize; + DWORD dwBytesToCopy; + PULONG pIpAddr; + PLIST_ENTRY pEntry,pHead; + tDEVICECONTEXT *pDeviceContext; + NCB ncb; + UCHAR retcode; + tIPANDNAMEINFO *pIpAndNameInfo; + tIPCONFIG_INFO *pIpCfg; + + + status = STATUS_SUCCESS; + + dwSize = ClientOutBufLen; + + // always use the first adapter on the list + pDeviceContext = CONTAINING_RECORD(NbtConfig.DeviceContexts.Flink,tDEVICECONTEXT,Linkage); + + OpCode = (USHORT)Ioctl; + + switch (OpCode) + { + // nbtstat -<any option> + case IOCTL_NETBT_GET_IP_ADDRS : + + if (ClientOutBufLen < sizeof(ULONG)*(NbtConfig.AdapterCount + 1)) + { + return( STATUS_BUFFER_OVERFLOW ); + } + + if (!ClientOutBuffer) + { + return( STATUS_INVALID_PARAMETER ); + } + pIpAddr = (PULONG )ClientOutBuffer; + + pEntry = pHead = &NbtConfig.DeviceContexts; + while ((pEntry = pEntry->Flink) != pHead) + { + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + if (pDeviceContext->IpAddress) + { + *pIpAddr = pDeviceContext->IpAddress; + pIpAddr++; + } + } + // + // put a 0 address on the end + // + *pIpAddr = 0; + + status = STATUS_SUCCESS; + + break; + + // nbtstat -n (or -N) + case IOCTL_NETBT_GET_LOCAL_NAMES : + + // nbtstat -c + case IOCTL_NETBT_GET_REMOTE_NAMES : + + if (!ClientOutBuffer || ClientOutBufLen == 0) + return (STATUS_INSUFFICIENT_RESOURCES); + + if (OpCode == IOCTL_NETBT_GET_REMOTE_NAMES ) + { + // make this null, so NbtQueryAda..() knows this is for remote + pDeviceContext = NULL; + } + + // return an array of netbios names that are registered + status = NbtQueryAdapterStatus(pDeviceContext, + &pchBuffer, + &dwSize); + break; + + // nbtstat -r + case IOCTL_NETBT_GET_BCAST_NAMES : + + // return an array of netbios names that are registered + status = NbtQueryBcastVsWins(pDeviceContext,&pchBuffer,&dwSize); + + break; + + // nbtstat -R + case IOCTL_NETBT_PURGE_CACHE : + + status = NbtResyncRemoteCache(); + break; + + // nbtstat -s, nbtstat -S + case IOCTL_NETBT_GET_CONNECTIONS : + + // return an array of netbios names that are registered + status = NbtQueryConnectionList(NULL, + &pchBuffer, + &dwSize); + break; + + // nbtstat -a, nbtstat -A + case IOCTL_NETBT_ADAPTER_STATUS: + + if (!ClientOutBuffer) + { + return( STATUS_INVALID_PARAMETER ); + } + + CTEZeroMemory( &ncb, sizeof(NCB) ); + + ncb.ncb_command = NCBASTAT; + ncb.ncb_buffer = ClientOutBuffer; + ncb.ncb_length = ClientOutBufLen; + ncb.ncb_lana_num = pDeviceContext->iLana; + + if (!ClientInBuffer) + { + return( STATUS_INVALID_PARAMETER ); + } + pIpAndNameInfo = (tIPANDNAMEINFO *)ClientInBuffer; + + // + // see if Ipaddress is specified: if yes, use it + // + if ( pIpAndNameInfo->IpAddress ) + { + ncb.ncb_callname[0] = '*'; + retcode = VNBT_NCB_X( &ncb, 0, &pIpAndNameInfo->IpAddress, 0, 0 ); + } + // + // no ipaddress: use the name that's given to us + // + else + { + CTEMemCopy( + &ncb.ncb_callname[0], + &(pIpAndNameInfo->NetbiosAddress.Address[0].Address[0].NetbiosName[0]), + NCBNAMSZ ); + retcode = VNBT_NCB_X( &ncb, 0, 0, 0, 0 ); + } + + status = STATUS_UNSUCCESSFUL; + if (!retcode) + { + if (ncb.ncb_retcode == NRC_GOODRET) + status = STATUS_SUCCESS; + else if (ncb.ncb_retcode == NRC_INCOMP) + status = TDI_BUFFER_OVERFLOW; + } + + break; + + // ipconfig queries us for nodetype and scope + case IOCTL_NETBT_IPCONFIG_INFO: + + dwBytesToCopy = sizeof(tIPCONFIG_INFO) + + NbtConfig.ScopeLength; + + if ( !ClientOutBuffer || ClientOutBufLen < dwBytesToCopy ) + { + status = STATUS_BUFFER_OVERFLOW; + break; + } + + pIpCfg = (tIPCONFIG_INFO *)ClientOutBuffer; + + NumLanas = 0; + for ( i = 0; i < NBT_MAX_LANAS; i++) + { + if (LanaTable[i].pDeviceContext != NULL) + { + pDeviceContext = LanaTable[i].pDeviceContext; + pIpCfg->LanaInfo[NumLanas].LanaNumber = pDeviceContext->iLana; + pIpCfg->LanaInfo[NumLanas].IpAddress = pDeviceContext->IpAddress; + pIpCfg->LanaInfo[NumLanas].NameServerAddress = pDeviceContext->lNameServerAddress; + pIpCfg->LanaInfo[NumLanas].BackupServer = pDeviceContext->lBackupServer; + pIpCfg->LanaInfo[NumLanas].lDnsServerAddress = pDeviceContext->lDnsServerAddress; + pIpCfg->LanaInfo[NumLanas].lDnsBackupServer = pDeviceContext->lDnsBackupServer; + NumLanas++; + } + } + + pIpCfg->NumLanas = NumLanas; + + pIpCfg->NodeType = NodeType; + + pIpCfg->ScopeLength = NbtConfig.ScopeLength; + + CTEMemCopy( &pIpCfg->szScope[0], + NbtConfig.pScope, + NbtConfig.ScopeLength ); + + status = STATUS_SUCCESS; + + break; + + default: + + status = STATUS_NOT_SUPPORTED; + break; + } + + // + // Copy the output into user's buffer + // + if ( (OpCode == IOCTL_NETBT_GET_LOCAL_NAMES) || + (OpCode == IOCTL_NETBT_GET_REMOTE_NAMES) || + (OpCode == IOCTL_NETBT_GET_CONNECTIONS) || + (OpCode == IOCTL_NETBT_GET_BCAST_NAMES) ) + { + if ( NT_SUCCESS(status) || (status == STATUS_BUFFER_OVERFLOW)) + { + if ( status == STATUS_BUFFER_OVERFLOW ) + { + dwBytesToCopy = ClientOutBufLen; + } + else + { + dwBytesToCopy = dwSize; + status = STATUS_SUCCESS; + } + CTEMemCopy( ClientOutBuffer, pchBuffer, dwBytesToCopy ) ; + + CTEMemFree((PVOID)pchBuffer); + } + } + + // + // we may be called either through the vxd entry point which 16 bit apps + // will do (for now, only nbtstat.exe), or through the file system api's + // which 32 bit apps will do via CreateFile and ioctl. + // If we came here through file system (i.e.VNBT_DeviceIoControl called us) + // then don't trash the input buffer since the status gets passed back as + // it is. For 16 bit apps (i.e.VNBT_Api_Handler called us), the only way + // we can pass status back (without major changes all over) is through the + // input buffer. + // + if ( ClientInBuffer && fOkToTrashInputBuffer ) + { + *(NTSTATUS *)ClientInBuffer = status; + } + + return( status ); +} + +/******************************************************************* + + NAME: PostInit_Proc + + SYNOPSIS: After the whole system is initialized, we get the + Sys_Vm_Init message and that's when this routine gets called. + This can be used for any post-processing, but for now we + only use it to load lmhosts (this way, we can load all the + #INCLUDE files which have UNC's in them, since now we know + the net is up). + + RETURNS: ErrorCode from the operation (0 if success) + + HISTORY: + Koti 12-Jul-1994 Created + +********************************************************************/ + +NTSTATUS +PostInit_Proc() +{ + + LONG lRetcode; + + CachePrimed = FALSE; + + CTEPagedCode(); + + + lRetcode = PrimeCache( NbtConfig.pLmHosts, + NULL, + TRUE, + NULL) ; + if (lRetcode != -1) + { + CachePrimed = TRUE ; + } + +} + + +/******************************************************************* + + NAME: CTEAllocInitMem + + SYNOPSIS: Allocates memory during driver initialization + + NOTES: If first allocation fails, we refill the heap spare and + try again. We can only do this during driver initialization + because the act of refilling may yield the current + thread. + + HISTORY: + Johnl 27-Aug-1993 Created +********************************************************************/ + +PVOID CTEAllocInitMem( ULONG cbBuff ) +{ + PVOID pv = CTEAllocMem( cbBuff ) ; + + if ( pv ) + { + return pv ; + } + else if ( fInInit ) + { + DbgPrint("CTEAllocInitMem: Failed allocation, trying again\r\n") ; + CTERefillMem() ; + pv = CTEAllocMem( cbBuff ) ; + } + + return pv ; +} diff --git a/private/ntos/nbt/vxd/vxdstub.asm b/private/ntos/nbt/vxd/vxdstub.asm new file mode 100644 index 000000000..0404c3dda --- /dev/null +++ b/private/ntos/nbt/vxd/vxdstub.asm @@ -0,0 +1,250 @@ + name vxdstub +;************************************************************************ +; +; (C) Copyright MICROSOFT Corp., 1990-1991 +; +; Title: VXDSTUB.ASM +; +; Date: 1-Jun-1991 +; +; Author: Neil Sandlin +; +;************************************************************************ + INCLUDE INT2FAPI.INC +;----------------------------- M A C R O S ------------------------------ +Writel MACRO addr + push ax + push bx + push cx + push dx + + mov dx,offset &addr ;Print + mov cx,&addr&l + mov bx,1 ;stdout + mov ah,40h ;write + int 21h + + pop dx + pop cx + pop bx + pop ax + ENDM + +;----------------------------- E Q U A T E S ----------------------------- + +cr equ 0dh +lf equ 0ah + + +_TEXT segment word public 'CODE' + assume cs:_TEXT,ds:_DATA + +;*----------------------- TSR Data Area ---------------------* +InstData Win386_Startup_Info_Struc <> +oldint dd 0 + +;*----------------------- TSR Code --------------------------* + + +handle2f proc + cmp ax,1605h + jnz @f + push di + lea di,InstData + mov word ptr cs:[di].SIS_Next_Ptr,bx + mov word ptr cs:[di][2].SIS_Next_Ptr,es + pop di + push cs + pop es + lea bx,InstData +@@: + jmp DWORD PTR [oldint] +handle2f endp + + ALIGN 16 +init_fence: + +_TEXT ends + +;*---------------------- Initialization Data ------------------------* + +_DATA segment word public 'DATA' + +TSR_rsv dw ? + +intmsg db cr,lf,'Hooking interrupt ' +intmsgx dd ? + db cr,lf +intmsgl equ $-intmsg + +hndmsg db cr,lf,'ISR entry point: ' +hndmsga dd ? + db ':' +hndmsgb dd ? + db ', length=' +hndmsgc dd ? + db 'h bytes' + db cr,lf +hndmsgl equ $-hndmsg + +tsrmsg db 'TSR; reserving ' +tsrmsgx dd ? + db ' paragraphs' + db cr,lf +tsrmsgl equ $-tsrmsg + +_DATA ends + + +_TEXT segment word public 'CODE' +;*-------------------------- Initialization Code ----------------------* + +vxdstub proc far + mov ax, _DATA + mov ds, ax + +; get a pointer to the name of the load file in the environment seg. + + mov ah,62h + int 21h ;bx -> psp + mov es,bx + mov bx,2ch ;environment segment + mov es,es:[bx] + xor di,di + mov cx,-1 ;big number + xor al,al ;search for a null + cld +@@: + repne scasb ;get past one null and stop + cmp byte ptr es:[di],0 ;another null + jnz @b ;no. + add di,3 ;skip the word before the name. + +; prepare part of the instance data list. Stuff in pointer to the file name +; and refernce data + + lea si,InstData + mov word ptr CS:[si].SIS_Version,3 + mov word ptr CS:[si].SIS_Virt_Dev_File_Ptr,di + mov word ptr CS:[si][2].SIS_Virt_Dev_File_Ptr,es + + mov word ptr cs:[si].SIS_Instance_Data_Ptr,0 + mov word ptr cs:[si][2].SIS_Instance_Data_Ptr,0 + +; Write message and hook interrupt 2f + mov ax, 2fh + mov bx, offset intmsgx + call hexasc + + Writel intmsg + + mov ah, 35h + mov al, 2fh + int 21h ; get old vector + mov WORD PTR cs:oldint,bx ; save old vector here + mov WORD PTR cs:oldint+2,es + + push ds + mov dx, offset handle2f + push cs ; get current code segment + pop ds + mov ah, 25h + mov al, 2fh ; vector to hook + int 21h ; hook that vector + pop ds + + +; Print out some information about the handler + + push cs ; code segment + pop ax + mov bx, offset hndmsga + call hexasc + + mov ax, offset handle2f ; offset of ISR + mov bx, offset hndmsgb + call hexasc + + mov ax, offset init_fence ; length in bytes of handler + mov bx, offset hndmsgc + call hexasc + + Writel hndmsg + +; Compute size of TSR area + + mov dx, offset init_fence ; start of initialization code + add dx, 15 ; round it off to paragraph + shr dx, 1 ; divide by 16 + shr dx, 1 + shr dx, 1 + shr dx, 1 + add dx, 32 ; add in PSP + mov TSR_rsv, dx ; save it + + mov ax, dx + mov bx, offset tsrmsgx + call hexasc + + Writel tsrmsg + +; Terminate and stay resident + + mov ax, 3100h ; TSR + mov dx, TSR_rsv ; # of paragraphs to reserve + int 21h ; TSR +vxdstub endp + + + +;************************************************************************ +; +; HEXASC +; +; This subroutine formats hex values into ASCII +; (utility routine from Advanced MS-DOS Programming) +; +; +;************************************************************************ + +hexasc proc near ; converts word to hex ASCII + ; call with AX = value, + ; DS:BX = address for string + ; returns AX, BX destroyed + + push cx ; save registers + push dx + + mov dx,4 ; initialize character counter +hexasc1: + mov cx,4 ; isolate next four bits + rol ax,cl + mov cx,ax + and cx,0fh + add cx,'0' ; convert to ASCII + cmp cx,'9' ; is it 0-9? + jbe hexasc2 ; yes, jump + add cx,'A'-'9'-1 ; add fudge factor for A-F + +hexasc2: ; store this character + mov [bx],cl + inc bx ; bump string pointer + + dec dx ; count characters converted + jnz hexasc1 ; loop, not four yet + + pop dx ; restore registers + pop cx + ret ; back to caller + +hexasc endp + + + +_TEXT ends + + + + end vxdstub + +
\ No newline at end of file diff --git a/private/ntos/nbt/vxd/wfw.c b/private/ntos/nbt/vxd/wfw.c new file mode 100644 index 000000000..c7f9bb7ce --- /dev/null +++ b/private/ntos/nbt/vxd/wfw.c @@ -0,0 +1,694 @@ +/**********************************************************************/ +/** Microsoft Windows **/ +/** Copyright(c) Microsoft Corp., 1994 **/ +/**********************************************************************/ + +/* + + wfw.c + + Contains VxD code that is specific to WFW + + + FILE HISTORY: + Johnl 14-Mar-1994 Created + +*/ + +#include <nbtprocs.h> +#include <tdiinfo.h> +#include <llinfo.h> +#include <ipinfo.h> +#include <dhcpinfo.h> +#include <nbtinfo.h> + +// +// any digit 0 to 9 and '.' are legal characters in an ipaddr +// +#define IS_IPADDR_CHAR( ch ) ( (ch >= '0' && ch <= '9') || (ch == '.') ) + +#define MAX_ADAPTER_DESCRIPTION_LENGTH 128 + +#ifndef CHICAGO +#pragma BEGIN_INIT + +extern char NBTSectionName[]; // Section in system.ini parameters are stored +extern char DNSSectionName[]; // Section where we find DNS server ipaddrs + +void GetDnsServerAddress( ULONG IpAddr, PULONG pIpNameServer); + +extern ULONG CurrentIP ; + +/******************************************************************* + + NAME: GetActiveLanasFromIP + + SYNOPSIS: Queries TDI for all IP drivers that have a non-zero IP + address. For non-zero IP address, a DEVICE_CONTEXT is + created. + + RETURNS: TRUE if successful, FALSE otherwise + + NOTES: Even if we fail to setup a particular adapter, the Lana + count is maintained. + + This routine is only used by Snowball + +********************************************************************/ + +BOOL GetActiveLanasFromIP( VOID ) +{ + NTSTATUS status ; + TDI_STATUS tdistatus ; + int i, j, k ; + uchar Context[CONTEXT_SIZE] ; + TDIObjectID ID ; + TDIEntityID EList[MAX_TDI_ENTITIES] ; + ULONG Size ; + UINT NumReturned ; + NDIS_BUFFER ndisbuff ; + BOOL fAnyValidIPs = FALSE ; + UINT iLanaOffset = 0 ; + IFEntry *ifeAdapterInfo[MAX_TDI_ENTITIES]; + UINT AdptNum; + UCHAR MacAddr[6]; + UCHAR PreviousNodeType; + + // + // The first thing to do is get the list of available entities, and make + // sure that there are some interface entities present. + // + ID.toi_entity.tei_entity = GENERIC_ENTITY; + ID.toi_entity.tei_instance = 0; + ID.toi_class = INFO_CLASS_GENERIC; + ID.toi_type = INFO_TYPE_PROVIDER; + ID.toi_id = ENTITY_LIST_ID; + + Size = sizeof(EList); + InitNDISBuff( &ndisbuff, &EList, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + + if (tdistatus != TDI_SUCCESS) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Querying entity list failed\r\n")) ; + return FALSE ; + } + + NumReturned = (uint)Size/sizeof(TDIEntityID); + + AdptNum = 0; + // + // first find out info about the adapters + // + for (i = 0; i < NumReturned; i++) + { + // + // if this entity/instance describes an adapter + // + if ( EList[i].tei_entity == IF_ENTITY ) + { + DWORD isMib; + + + ID.toi_entity.tei_entity = EList[i].tei_entity ; + ID.toi_entity.tei_instance = EList[i].tei_instance; + ID.toi_class = INFO_CLASS_GENERIC ; + ID.toi_type = INFO_TYPE_PROVIDER; + ID.toi_id = ENTITY_TYPE_ID ; + + Size = sizeof( isMib ); + InitNDISBuff( &ndisbuff, &isMib, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Getting isMib failed\r\n")) ; + return FALSE ; + } + + // + // Does this entity support MIB + // + if (isMib != IF_MIB) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: skipping non-MIB entity\r\n")) ; + continue; + } + + // + // MIB requests supported - query the adapter info + // + + Size = sizeof(IFEntry) + MAX_ADAPTER_DESCRIPTION_LENGTH + 1; + + ifeAdapterInfo[AdptNum] = (IFEntry *)CTEAllocInitMem((USHORT)Size); + + if ( ifeAdapterInfo[AdptNum] == NULL ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Couldn't allocate AdapterInfo buffer\r\n")) ; + return FALSE; + } + + ID.toi_class = INFO_CLASS_PROTOCOL;; + ID.toi_id = IF_MIB_STATS_ID; + + Size = sizeof(IFEntry) + MAX_ADAPTER_DESCRIPTION_LENGTH + 1; + InitNDISBuff( &ndisbuff, ifeAdapterInfo[AdptNum], Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Getting IF type failed\r\n")) ; + for ( k=0; k<AdptNum; k++ ) + { + CTEFreeMem( ifeAdapterInfo[k] ) ; + } + return FALSE ; + } + + AdptNum++; + } + } + + // + // now that we know about the adapters, get the ipaddrs + // + for (i = 0; i < NumReturned; i++) + { + if ( EList[i].tei_entity == CL_NL_ENTITY ) + { + IPSNMPInfo IPStats ; + IPAddrEntry * pIAE ; + ULONG NLType ; + ULONG IpNameServer[COUNT_NS_ADDR]; + ULONG IpDnsServer[COUNT_NS_ADDR]; + UCHAR IpIndex; + + // + // Does this entity support IP? + // + + ID.toi_entity.tei_entity = EList[i].tei_entity ; + ID.toi_entity.tei_instance = EList[i].tei_instance; + ID.toi_class = INFO_CLASS_GENERIC ; + ID.toi_type = INFO_TYPE_PROVIDER; + ID.toi_id = ENTITY_TYPE_ID ; + + Size = sizeof( NLType ); + InitNDISBuff( &ndisbuff, &NLType, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Getting NL type failed\r\n")) ; + for ( k=0; k<AdptNum; k++ ) + { + CTEFreeMem( ifeAdapterInfo[k] ) ; + } + return FALSE ; + } + + if ( NLType != CL_NL_IP ) + continue ; + + // + // We've got an IP driver so get it's address table + // + + ID.toi_class = INFO_CLASS_PROTOCOL ; + ID.toi_id = IP_MIB_STATS_ID; + Size = sizeof(IPStats); + InitNDISBuff( &ndisbuff, &IPStats, Size, NULL ) ; + memset(Context, 0, CONTEXT_SIZE); + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Getting IPStats failed\r\n")) ; + continue ; + } + + if ( IPStats.ipsi_numaddr < 1 ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: No IP Addresses installed\r\n")) ; + continue ; + } + + Size = sizeof(IPAddrEntry) * IPStats.ipsi_numaddr ; + if ( !(pIAE = (IPAddrEntry*) CTEAllocInitMem( Size )) ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Couldn't allocate IP table buffer\r\n")) ; + continue ; + } + + ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID ; + InitNDISBuff( &ndisbuff, pIAE, Size, NULL ) ; + memset( Context, 0, CONTEXT_SIZE ) ; + tdistatus = TdiVxdQueryInformationEx( 0, + &ID, + &ndisbuff, + &Size, + Context); + if ( tdistatus != TDI_SUCCESS ) + { + CDbgPrint( DBGFLAG_ERROR, ( "GetActiveLanasFromIP: Getting IP address table failed\r\n")) ; + CTEFreeMem( pIAE ) ; + continue ; + } + + ASSERT( Size/sizeof(IPAddrEntry) >= IPStats.ipsi_numaddr ) ; + + // + // We have the IP address table for this IP driver. Look for + // non-zero IP addresses + // + + for ( j = 0 ; j < IPStats.ipsi_numaddr ; j++ ) + { + // + // Skip the loopback address + // + if ((pIAE[j].iae_addr & 0x000000ff) == 0x0000007f ) + { + continue ; + } + + CurrentIP = pIAE[j].iae_addr ; + if (!CurrentIP) + { + CDbgPrint( DBGFLAG_ERROR, ( "Init: ipaddr is 0, but accepting\n\r")) ; + } + + // + // now find out the mac address for this ipaddr + // + memset( MacAddr, 0, 6 ) ; + IpIndex = -1; + for ( k=0; k<AdptNum; k++ ) + { + if ( ifeAdapterInfo[k]->if_index == pIAE[j].iae_index ) + { + CTEMemCopy( MacAddr, ifeAdapterInfo[k]->if_physaddr, 6 ); + IpIndex = ifeAdapterInfo[k]->if_index; + break; + } + } + ASSERT(IpIndex != -1); + + PreviousNodeType = NodeType; + + // + // This will re-read the DHCPable parameters now that we have + // a potential DHCP source + // + ReadParameters2( pNbtGlobConfig, NULL ); + + if (PreviousNodeType & PROXY) + { + NodeType |= PROXY; + } + + // + // Get all the NBNS servers' and DNS servers' ipaddresses + // + GetNameServerAddress( CurrentIP, IpNameServer); + + GetDnsServerAddress( CurrentIP, IpDnsServer); + + // + // IP stores the address in network order, so we will un-network order + // them because that's what NBT expects (would be nice to avoid conversion) + // + status = CreateDeviceObject( pNbtGlobConfig, + htonl( pIAE[j].iae_addr ), + htonl( pIAE[j].iae_mask ), + IpNameServer[0], + IpNameServer[1], + IpDnsServer[0], + IpDnsServer[1], + MacAddr, + IpIndex + ) ; + + if ( !NT_SUCCESS( status ) ) + { + CDbgPrint( DBGFLAG_ERROR, ( "Init: CreateDeviceObject failed\n\r")) ; + iLanaOffset++ ; + continue ; + } + + if ( !RegisterLana( LanaBase + iLanaOffset ) ) + { + CDbgPrint( DBGFLAG_ERROR, ( "Init: RegisterLana failed\n\r")) ; + iLanaOffset++ ; + continue ; + } + + LanaTable[iLanaOffset].pDeviceContext = + (tDEVICECONTEXT*)pNbtGlobConfig->DeviceContexts.Blink ; + LanaTable[iLanaOffset].pDeviceContext->iLana = LanaBase + + iLanaOffset; + iLanaOffset++; + fAnyValidIPs = TRUE ; + + } // addr traversal + + CTEFreeMem( pIAE ) ; + + } // if IP + } // entity traversal + + for ( k=0; k<AdptNum; k++ ) + { + CTEFreeMem( ifeAdapterInfo[k] ) ; + } + + return fAnyValidIPs ; +} + +/******************************************************************* + + NAME: VxdReadIniString + + SYNOPSIS: Vxd stub for CTEReadIniString + + ENTRY: pchKey - Key value to look for in the NBT section + ppchString - Pointer to buffer found string is returned in + + EXIT: ppchString will point to an allocated buffer + + RETURNS: STATUS_SUCCESS if found + + NOTES: The client must free ppchString when done with it + + HISTORY: + Johnl 30-Aug-1993 Created + Koti 30-May-194 Added 3rd parm to GetProfileString + +********************************************************************/ + +CHAR * GetProfileString( LPTSTR pchKey, LPTSTR * pchDefault, PCHAR SectName ) ; + +NTSTATUS VxdReadIniString( LPSTR pchKey, LPSTR * ppchString ) +{ + char * pchTmp ; + + if ( pchTmp = GetProfileString( pchKey, NULL, NBTSectionName ) ) + { + if ( *ppchString = CTEAllocInitMem( strlen( pchTmp ) + 1) ) + { + strcpy( *ppchString, pchTmp ) ; + return STATUS_SUCCESS ; + } + else + return STATUS_INSUFFICIENT_RESOURCES ; + } + else + { + // + // Does DHCP have it? + // + + if ( pchTmp = (char *) GetDhcpOption( pchKey, 0 ) ) + { + *ppchString = pchTmp ; + return STATUS_SUCCESS ; + } + } + + return STATUS_UNSUCCESSFUL ; +} + + +/******************************************************************* + + NAME: GetDnsServerAddress + + SYNOPSIS: Gets the DNS server ipaddrs from the DNS section of system.ini + + Or, if DHCP is installed and the DNS server addresses aren't + found, we get them from DHCP + + ENTRY: IpAddr - If we can get from DHCP, get form this address + pIpDnsServer - Receives addresses if found (otherwise 0) + + NOTES: This routine is only used by Snowball + + HISTORY: + Koti 30-May-1994 Created + +********************************************************************/ + +void GetDnsServerAddress( ULONG IpAddr, PULONG pIpDnsServer) +{ + UCHAR i ; + PUCHAR pchDnsSrv = "DNSServers" ; + UINT OptId; + PUCHAR pchTmp; + PUCHAR pchCurrent, pchNext; + LPTSTR pchString=NULL ; + TDI_STATUS tdistatus ; + BOOL fPrimaryFound = FALSE; + BOOL fOneMore=TRUE; + ULONG Buff[COUNT_NS_ADDR] ; + + + + // + // initialize all of them to worst case + // + for ( i = 0; i < COUNT_NS_ADDR; i++) + { + pIpDnsServer[i] = LOOP_BACK ; + } + + pchTmp = GetProfileString( pchDnsSrv, NULL, DNSSectionName ); + if ( pchTmp == NULL ) + { + goto Not_In_Sysini; + } + + if ( !(pchString = CTEAllocInitMem( strlen( pchTmp ) + 1)) ) + { + DbgPrint("GetDnsServerAddress: CTEAllocInitMem failed!\r\n") ; + goto Not_In_Sysini; + } + + strcpy( pchString, pchTmp ) ; + + // + // we are generating (upto) COUNT_NS_ADDR pointers each pointing to + // one ipaddr. The string in system.ini looks like: + // DNSServers = 11.101.4.26,200.200.200.200,1.2.3.4,1.91.245.10 + // + pchNext = pchCurrent = pchString; + + if ( IS_IPADDR_CHAR(*pchCurrent) ) // make sure at least one ipaddr defnd + { + i = 0; + while( (i < COUNT_NS_ADDR) && fOneMore ) + { + while( IS_IPADDR_CHAR(*pchNext) ) + pchNext++; + + if ( *pchNext == ',' ) // ',' is the separator between 2 addrs + { + *pchNext = '\0'; + pchNext++; + } + else + { + fOneMore = FALSE; // reached end of line + } + + // + // as long as at least the first one ipaddr gets converted properly, + // ignore errors in others + // + if ( ConvertDottedDecimalToUlong( pchCurrent, &pIpDnsServer[i] )) + { + if ( i == 0 ) + goto Not_In_Sysini; + else + pIpDnsServer[i] = LOOP_BACK ; + } + + fPrimaryFound = TRUE; // => at least one ipaddr is good + + i++; + + pchCurrent = pchNext; // go, convert the next one + } + } + + +Not_In_Sysini: + + if( pchString != NULL ) + { + CTEFreeMem( pchString ) ; + } + + // + // if we didn't find in the .ini file, try getting them from DHCP + // + if ( !fPrimaryFound ) + { + ULONG Size = sizeof( Buff ) ; + + OptId = 6; // DNS Option + + tdistatus = DhcpQueryOption( IpAddr, + OptId, + &Buff, + &Size ) ; + + switch ( tdistatus ) + { + case TDI_SUCCESS: + case TDI_BUFFER_OVERFLOW: // May be more then one our buffer will hold + for ( i = 0; i < COUNT_NS_ADDR; i++ ) + { + if ( Size >= (sizeof(ULONG)*(i+1))) + pIpDnsServer[i] = htonl(Buff[i]) ; + } + break ; + + case TDI_INVALID_PARAMETER: // Option not found + break ; + + default: + ASSERT( FALSE ) ; + break ; + } + } + + KdPrint(("GetDnsServerAddress: Primary: %x, backup: %x\r\n", + pIpDnsServer[0], pIpDnsServer[1] )) ; + +} + +/******************************************************************* + + NAME: GetNameServerAddress + + SYNOPSIS: Gets the Win server for the specified Lana. + + Or, if DHCP is installed and the Name server addresses aren't + found, we get them from DHCP + + ENTRY: IpAddr - If we can get from DHCP, get form this address + pIpNameServer - Receives addresses if found (otherwise 0) + + NOTES: This routine is only used by Snowball + + HISTORY: + Johnl 21-Oct-1993 Created + +********************************************************************/ + +void GetNameServerAddress( ULONG IpAddr, + PULONG pIpNameServer) +{ + UCHAR i ; + PUCHAR pchSrv = "NameServer$" ; + PUCHAR pchSrvNum; + UINT OptId; + LPTSTR pchString ; + TDI_STATUS TdiStatus ; + BOOL fPrimaryFound = FALSE; + ULONG Buff[COUNT_NS_ADDR] ; + + + OptId = 44; // NBNS Option + pchSrvNum = pchSrv + 10 ; // to overwrite '$' with 1,2,3 etc. + + + for ( i = 0; i < COUNT_NS_ADDR; i++) + { + + pIpNameServer[i] = LOOP_BACK ; + *pchSrvNum = '1' + i; + + if ( !CTEReadIniString( NULL, pchSrv, &pchString ) ) + { + if ( ConvertDottedDecimalToUlong( pchString, &pIpNameServer[i] )) + { + // + // Bad IP address format + // + DbgPrint("GetNameServerAddress: ConvertDottedDecimalToUlong failed!\r\n") ; + pIpNameServer[i] = LOOP_BACK ; + } + else if ( i == 0 ) + fPrimaryFound = TRUE ; + + CTEFreeMem( pchString ) ; + } + } + + // + // Not in the .ini file, try getting them from DHCP + // + + if ( !fPrimaryFound ) + { + ULONG Size = sizeof( Buff ) ; + TDI_STATUS tdistatus ; + + tdistatus = DhcpQueryOption( IpAddr, + OptId, + &Buff, + &Size ) ; + + switch ( tdistatus ) + { + case TDI_SUCCESS: + case TDI_BUFFER_OVERFLOW: // May be more then one our buffer will hold + for ( i = 0; i < COUNT_NS_ADDR; i++ ) + { + if ( Size >= (sizeof(ULONG)*(i+1))) + pIpNameServer[i] = htonl(Buff[i]) ; + } + break ; + + case TDI_INVALID_PARAMETER: // Option not found + break ; + + default: + ASSERT( FALSE ) ; + break ; + } + } + + KdPrint(("GetNameServerAddress: Primary: %x, backup: %x\r\n", + pIpNameServer[0], pIpNameServer[1] )) ; + +} + + +#pragma END_INIT +#endif //!CHICAGO + + diff --git a/private/ntos/nbt/vxd/wfwasm.asm b/private/ntos/nbt/vxd/wfwasm.asm new file mode 100644 index 000000000..79b9cb818 --- /dev/null +++ b/private/ntos/nbt/vxd/wfwasm.asm @@ -0,0 +1,285 @@ +;*****************************************************************; +;** Copyright(c) Microsoft Corp., 1988-1993 **; +;*****************************************************************; +;:ts=8 + TITLE WFWASM.ASM - WFW Specific vnbt routines +.XLIST +;*** VNBT -- NetBios over TCP/IP VxD +; +; + .386p + include vmm.inc + include dosmgr.inc + include netvxd.inc + include vdhcp.inc + include debug.inc + include vtdi.inc + + include vnbtd.inc + include vnetbios.inc +.LIST + +IFNDEF CHICAGO + +EXTRN _GetDhcpOption:NEAR +EXTRN NCB_Handler:NEAR + +VxD_ICODE_SEG + +public _NBTSectionName +_NBTSectionName db 'NBT',0 ; Section in system.ini parameters are stored +public _DNSSectionName +_DNSSectionName db 'DNS',0 ; DNS Section in system.ini + +;**************************************************************************** +;** _GetProfileInt +; +; Reads a parameter from our system.ini file (INIT TIME ONLY!) +; +; Entry: See ReadParamParams +; +; Exit: Eax contains specified value or defaulted value +; + +ReadParamParams struc + dd ? ; Return Address + dd ? ; Saved edi + dd ? ; Saved esi + dd ? ; ParametersHandle (unused) +ValueName dd ? ; Pointer to value name string +DefaultValue dd ? ; Value to use if not in .ini file +MinimumValue dd ? ; Specified value must be >= MinimumValue + +ReadParamParams ends + +BeginProc _GetProfileInt + push edi + push esi + + ; + ; Get the value from the system.ini file (if can't be found then eax + ; will contain the default value) + ; + mov eax, [esp].DefaultValue + mov esi, OFFSET32 _NBTSectionName + mov edi, [esp].ValueName + VMMCall Get_Profile_Decimal_Int + + jnc GPI_Found + + push eax ; Default value + push edi ; Value name + call _GetDhcpOption ; Returns DHCP value or default + add esp, 8 + +GPI_Found: + ; + ; Does the value meet our standards? + ; + cmp eax, [esp].MinimumValue ; Unsigned comparison + ja RP10 + mov eax, [esp].MinimumValue + +RP10: + pop esi + pop edi + ret +EndProc _GetProfileInt + +;**************************************************************************** +;** _GetProfileHex +; +; Reads a hex parameter from our system.ini file (INIT TIME ONLY!) +; +; Entry: See ReadParamParams +; +; Exit: Eax contains specified value or defaulted value +; + +ReadParamParams struc + dd ? ; Return Address + dd ? ; Saved edi + dd ? ; Saved esi + dd ? ; ParametersHandle (unused) +ValueName dd ? ; Pointer to value name string +DefaultValue dd ? ; Value to use if not in .ini file +MinimumValue dd ? ; Specified value must be >= MinimumValue + +ReadParamParams ends + +BeginProc _GetProfileHex + push edi + push esi + + ; + ; Get the value from the system.ini file (if can't be found then eax + ; will contain the default value) + ; + mov eax, [esp].DefaultValue + mov esi, OFFSET32 _NBTSectionName + mov edi, [esp].ValueName + VMMCall Get_Profile_Hex_Int + + jnc GPH_Found + + push eax ; Default value + push edi ; Value name + call _GetDhcpOption ; Returns DHCP value or default + add esp, 8 + +GPH_Found: + ; + ; Does the value meet our standards? + ; + cmp eax, [esp].MinimumValue ; Unsigned comparison + ja RHP10 + mov eax, [esp].MinimumValue + +RHP10: + pop esi + pop edi + ret +EndProc _GetProfileHex + + +;**************************************************************************** +;** _GetProfileString +; +; Reads a string from our system.ini file (INIT TIME ONLY!) +; +; Entry: See GetProfileStrParams structure +; +; Exit: Eax contains the found value or NULL if not found +; +; History: +; 30-May-94 Koti +; this function modified to accept name of the section +; to look at as a parameter. Getting DNS server ipaddrs +; from the DNS section in system.ini demanded this change +; + +GetProfileStrParams struc + dd ? ; Return Address + dd ? ; saved edx + dd ? ; Saved edi + dd ? ; Saved esi +gps_ValueName dd ? ; Pointer to value name string +gps_DefaultValue dd ? ; Value to use if not in .ini file +gps_SectionName dd ? ; Name of the section to look at (almost always NBT) +GetProfileStrParams ends + +BeginProc _GetProfileString + push edx + push edi + push esi + + ; + ; Get the value from the system.ini file (if can't be found then eax + ; will contain the default value) + ; + mov edx, [esp].gps_DefaultValue + mov esi, [esp].gps_SectionName + mov edi, [esp].gps_ValueName + VMMCall Get_Profile_String + + jc GetProf10 + mov eax, edx ; Success + jmp short GetProf20 + +GetProf10: + mov eax, 0 ; Couldn't find the string + +GetProf20: + + pop esi + pop edi + pop edx + ret +EndProc _GetProfileString + +;**************************************************************************** +;** _RegisterLana +; +; Registers the requested lana with the VNetbios driver. +; +; Entry: [ESP+4] - Lana number to register +; +; Exit: EAX will be TRUE if successful, FALSE if not +; +; Uses: +; +BeginProc _RegisterLana + + mov eax, [esp+4] ; Get the request lana to register + + push ebx + push edx + + mov ebx, 1 ; Take over RM lana + mov edx, NCB_Handler + VxDcall VNETBIOS_Register ; Carry set on failure + jnc RegLana10 + mov eax, 0 ; Failed + jmp short RegLana20 + +RegLana10: + mov eax, 1 ; Success + +RegLana20: + pop edx + pop ebx + ret + +EndProc _RegisterLana + +;**************************************************************************** +;** _DhcpSetInfo - Sets DHCP information +; +; Stub callout to the Dhcp driver +; +; Entry: [ESP+4] - Info type +; [ESP+8] - IP Address of interest +; [ESP+12]- Pointer to buffer +; [ESP+16]- Pointer to buffer size +; +; INIT TIME ONLY! +; + +BeginProc _DhcpSetInfo + + VxdCall VDHCP_Get_Version + jnc DSI_Installed + + mov eax, 26 ; DHCP not installed, return invalid param + ret + +DSI_Installed: + push ebp + mov ebp,esp + + mov eax, [ebp+20] ; Buff size + push eax + mov eax, [ebp+16] ; Buff + push eax + mov eax, [ebp+12] ; IP Address + push eax + mov eax, [ebp+8] ; Info type + push eax + + VxdCall VDHCP_Set_Info + + add esp, 16 + + pop ebp + ret + +EndProc _DhcpSetInfo + +VxD_ICODE_ENDS + +ENDIF ;!CHICAGO + +END + + + |