1183 lines
39 KiB
C
1183 lines
39 KiB
C
/**********************************************************************/
|
||
/** 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 "precomp.h"
|
||
#include "hosts.h"
|
||
|
||
VOID
|
||
ReadScope(
|
||
IN tNBTCONFIG *pConfig,
|
||
IN HANDLE ParmHandle
|
||
);
|
||
|
||
VOID
|
||
ReadLmHostFile(
|
||
IN tNBTCONFIG *pConfig,
|
||
IN HANDLE ParmHandle
|
||
);
|
||
|
||
extern tTIMERQ TimerQ;
|
||
|
||
//******************* Pageable Routine Declarations ****************
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma CTEMakePageable(INIT, InitNotOs)
|
||
#pragma CTEMakePageable(PAGE, InitTimersNotOs)
|
||
#pragma CTEMakePageable(PAGE, StopInitTimers)
|
||
#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.
|
||
//
|
||
NbtMemoryAllocated = 0;
|
||
|
||
NbtConfig.AdapterCount = 0;
|
||
NbtConfig.MultiHomed = FALSE;
|
||
NbtConfig.SingleResponse = FALSE;
|
||
NbtConfig.ServerMask = 0;
|
||
NbtConfig.ClientMask = 0;
|
||
NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER] = 0;
|
||
pNbtGlobConfig->iBufferSize[eNBT_DGRAM_TRACKER] = sizeof(tDGRAM_SEND_TRACKING);
|
||
CTEZeroMemory (&NameStatsInfo,sizeof(tNAMESTATS_INFO)); // Initialize the name statistics
|
||
CTEZeroMemory (&LmHostQueries,sizeof(tLMHSVC_REQUESTS)); // Synchronize reads from the LmHosts file
|
||
InitializeListHead (&LmHostQueries.ToResolve);
|
||
|
||
|
||
//
|
||
// Initialize the linked lists associated with the global configuration
|
||
// data structures
|
||
//
|
||
InitializeListHead (&NbtConfig.DeviceContexts);
|
||
InitializeListHead (&NbtConfig.DevicesAwaitingDeletion);
|
||
InitializeListHead (&NbtConfig.AddressHead);
|
||
InitializeListHead (&NbtConfig.PendingNameQueries);
|
||
InitializeListHead (&NbtConfig.WorkerQList);
|
||
InitializeListHead (&NbtConfig.NodeStatusHead);
|
||
InitializeListHead (&NbtConfig.DgramTrackerFreeQ);
|
||
InitializeListHead (&UsedTrackers);
|
||
InitializeListHead (&UsedIrps);
|
||
InitializeListHead (&DomainNames.DomainList);
|
||
|
||
// initialize the spin lock
|
||
CTEInitLock (&NbtConfig.LockInfo.SpinLock);
|
||
CTEInitLock (&NbtConfig.JointLock.LockInfo.SpinLock);
|
||
CTEInitLock (&NbtConfig.WorkerQLock.LockInfo.SpinLock);
|
||
|
||
#ifndef VXD
|
||
pWinsInfo = NULL;
|
||
NbtConfig.NumWorkerThreadsQueued = 0;
|
||
NbtConfig.NumTimersRunning = 0;
|
||
NbtConfig.CacheTimeStamp = 0;
|
||
NbtConfig.InterfaceIndex = 0;
|
||
NbtConfig.GlobalRefreshState = 0;
|
||
NbtConfig.pWakeupRefreshTimer = NULL;
|
||
NbtConfig.TransactionId = WINS_MAXIMUM_TRANSACTION_ID + 1;
|
||
NbtConfig.RemoteCacheLen = REMOTE_CACHE_INCREMENT;
|
||
NbtConfig.iBufferSize[eNBT_FREE_SESSION_MDLS] = sizeof(tSESSIONHDR);
|
||
NbtConfig.iBufferSize[eNBT_DGRAM_MDLS] = DGRAM_HDR_SIZE + (NbtConfig.ScopeLength << 1);
|
||
|
||
//
|
||
// Set the Unitialized flag in the TimerQ, so that it can be initialized
|
||
// when needed
|
||
//
|
||
TimerQ.TimersInitialized = FALSE;
|
||
|
||
// Initialize the LastForcedReleaseTime!
|
||
CTEQuerySystemTime (NbtConfig.LastForcedReleaseTime);
|
||
CTEQuerySystemTime (NbtConfig.LastOutOfRsrcLogTime);
|
||
CTEQuerySystemTime (NbtConfig.LastRefreshTime);
|
||
ExSystemTimeToLocalTime (&NbtConfig.LastRefreshTime, &NbtConfig.LastRefreshTime);
|
||
|
||
//
|
||
// this resource is used to synchronize access to the Dns structure
|
||
//
|
||
CTEZeroMemory (&DnsQueries,sizeof(tLMHSVC_REQUESTS));
|
||
InitializeListHead (&DnsQueries.ToResolve);
|
||
//
|
||
// this resource is used to synchronize access to the CheckAddr structure
|
||
//
|
||
CTEZeroMemory(&CheckAddr,sizeof(tLMHSVC_REQUESTS));
|
||
InitializeListHead (&CheckAddr.ToResolve);
|
||
|
||
//
|
||
// 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);
|
||
|
||
InitializeListHead (&NbtConfig.IrpFreeList);
|
||
InitializeListHead (&FreeWinsList);
|
||
// 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);
|
||
|
||
KeInitializeEvent (&NbtConfig.WorkerQLastEvent, NotificationEvent, TRUE);
|
||
KeInitializeEvent (&NbtConfig.TimerQLastEvent, NotificationEvent, TRUE);
|
||
KeInitializeEvent (&NbtConfig.WakeupTimerStartedEvent, NotificationEvent, TRUE);
|
||
|
||
// 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.
|
||
//
|
||
ExInitializeResourceLite(&NbtConfig.Resource);
|
||
#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;
|
||
NbtConfig.iCurrentNumBuff[eNBT_SEND_CONTEXT] = NBT_INITIAL_NUM;
|
||
NbtConfig.iCurrentNumBuff[eNBT_RCV_CONTEXT] = NBT_INITIAL_NUM;
|
||
|
||
InitializeListHead (&NbtConfig.DNSDirectNameQueries);
|
||
#endif
|
||
|
||
#if DBG
|
||
NbtConfig.LockInfo.LockNumber = NBTCONFIG_LOCK;
|
||
NbtConfig.JointLock.LockInfo.LockNumber = JOINT_LOCK;
|
||
NbtConfig.WorkerQLock.LockInfo.LockNumber = WORKERQ_LOCK;
|
||
for (i=0; i<MAXIMUM_PROCESSORS; i++)
|
||
{
|
||
NbtConfig.CurrentLockNumber[i] = 0;
|
||
}
|
||
InitializeListHead(&NbtConfig.StaleRemoteNames);
|
||
#endif
|
||
|
||
//
|
||
// create trackers List
|
||
//
|
||
// #if DBG
|
||
for (i=0; i<NBT_TRACKER_NUM_TRACKER_TYPES; i++)
|
||
{
|
||
TrackTrackers[i] = 0;
|
||
TrackerHighWaterMark[i] = 0;
|
||
}
|
||
// #endif // DBG
|
||
|
||
//
|
||
// Now allocate any initial memory/Resources
|
||
//
|
||
#ifdef VXD
|
||
status = NbtInitQ (&NbtConfig.SessionBufferFreeList,
|
||
NbtConfig.iBufferSize[eNBT_SESSION_HDR],
|
||
NBT_INITIAL_NUM);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
return status ;
|
||
}
|
||
|
||
status = NbtInitQ( &NbtConfig.SendContextFreeList,
|
||
sizeof( TDI_SEND_CONTEXT ),
|
||
NBT_INITIAL_NUM);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
return status ;
|
||
}
|
||
|
||
status = NbtInitQ( &NbtConfig.RcvContextFreeList,
|
||
sizeof (RCV_CONTEXT),
|
||
NBT_INITIAL_NUM);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
return status ;
|
||
}
|
||
#endif
|
||
|
||
// create the hash tables for storing names in.
|
||
status = CreateHashTable(&NbtConfig.pLocalHashTbl, NbtConfig.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 = CreateHashTable (&NbtConfig.pRemoteHashTbl, NbtConfig.uNumBucketsRemote, NBT_REMOTE);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
return status;
|
||
}
|
||
|
||
status = NbtInitTrackerQ (NBT_INITIAL_NUM);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
return status;
|
||
}
|
||
|
||
// create the timer control blocks, setting the number of concurrent timers
|
||
// allowed at one time
|
||
status = InitTimerQ (NBT_INITIAL_NUM);
|
||
|
||
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;
|
||
|
||
CTEPagedCode();
|
||
|
||
//
|
||
// If the timers have already been initialized, return success
|
||
//
|
||
if (TimerQ.TimersInitialized)
|
||
{
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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 = 3;
|
||
|
||
status = StartTimer(RefreshTimeout,
|
||
NbtConfig.InitialRefreshTimeout/REFRESH_DIVISOR,
|
||
NULL, // context value
|
||
NULL, // context2 value
|
||
NULL,
|
||
NULL,
|
||
NULL, // This timer is a global timer
|
||
&NbtConfig.pRefreshTimer,
|
||
0,
|
||
FALSE);
|
||
|
||
if ( !NT_SUCCESS(status))
|
||
{
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the TimersInitialized flag
|
||
//
|
||
TimerQ.TimersInitialized = TRUE;
|
||
|
||
// calculate the count necessary to timeout out names in RemoteHashTimeout
|
||
// milliseconds
|
||
//
|
||
NbtConfig.RemoteTimeoutCount = (USHORT)((NbtConfig.RemoteHashTtl/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(RemoteHashTimeout, // timer expiry routine
|
||
REMOTE_HASH_TIMEOUT,
|
||
NULL, // context value
|
||
NULL, // context2 value
|
||
NULL,
|
||
NULL,
|
||
NULL, // This timer is a global timer
|
||
&NbtConfig.pRemoteHashTimer,
|
||
0,
|
||
FALSE);
|
||
|
||
if ( !NT_SUCCESS( status ) )
|
||
{
|
||
StopInitTimers();
|
||
return status ;
|
||
}
|
||
|
||
// 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(SessionKeepAliveTimeout, // timer expiry routine
|
||
NbtConfig.KeepAliveTimeout,
|
||
NULL, // context value
|
||
NULL, // context2 value
|
||
NULL,
|
||
NULL,
|
||
NULL, // This timer is a global timer
|
||
&NbtConfig.pSessionKeepAliveTimer,
|
||
0,
|
||
FALSE);
|
||
|
||
if ( !NT_SUCCESS( status ) )
|
||
{
|
||
StopInitTimers();
|
||
return status ;
|
||
}
|
||
}
|
||
|
||
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 the timers have already been stopped, return success
|
||
//
|
||
if (!TimerQ.TimersInitialized)
|
||
{
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Set the TimersInitialized flag to FALSE
|
||
//
|
||
TimerQ.TimersInitialized = FALSE;
|
||
|
||
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);
|
||
|
||
pConfig->MaxPreloadEntries = CTEReadSingleIntParameter(ParmHandle,
|
||
WS_MAX_PRELOADS,
|
||
DEF_PRELOAD,
|
||
DEF_PRELOAD ) ;
|
||
|
||
if (pConfig->MaxPreloadEntries > MAX_PRELOAD)
|
||
{
|
||
pConfig->MaxPreloadEntries = MAX_PRELOAD;
|
||
}
|
||
|
||
#ifdef VXD
|
||
pConfig->DnsServerPort = (USHORT)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_DNS_PORT_NUM,
|
||
NBT_DNSSERVER_UDP_PORT,
|
||
0);
|
||
|
||
pConfig->lRegistryMaxNames = (USHORT)CTEReadSingleIntParameter(ParmHandle,
|
||
VXD_NAMETABLE_SIZE_NAME,
|
||
VXD_DEF_NAMETABLE_SIZE,
|
||
VXD_MIN_NAMETABLE_SIZE ) ;
|
||
|
||
pConfig->lRegistryMaxSessions = (USHORT)CTEReadSingleIntParameter(ParmHandle,
|
||
VXD_SESSIONTABLE_SIZE_NAME,
|
||
VXD_DEF_SESSIONTABLE_SIZE,
|
||
VXD_MIN_SESSIONTABLE_SIZE ) ;
|
||
|
||
pConfig->DoDNSDevolutions = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_DO_DNS_DEVOLUTIONS,
|
||
0, // disabled by default
|
||
0);
|
||
#endif
|
||
|
||
pConfig->RemoteHashTtl = 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);
|
||
|
||
pConfig->MinimumRefreshSleepTimeout = CTEReadSingleIntParameter(ParmHandle,
|
||
WS_MINIMUM_REFRESH_SLEEP_TIME,
|
||
DEFAULT_MINIMUM_REFRESH_SLEEP_TIME,
|
||
0);
|
||
|
||
// 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->NoNameReleaseOnDemand = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_NO_NAME_RELEASE,
|
||
0,
|
||
0); // disabled by default
|
||
if (pConfig->CachePerAdapterEnabled = (BOOLEAN) CTEReadSingleIntParameter(ParmHandle,
|
||
WS_CACHE_PER_ADAPTER_ENABLED,
|
||
1, // Enabled by default
|
||
0))
|
||
{
|
||
pConfig->ConnectOnRequestedInterfaceOnly = (BOOLEAN) CTEReadSingleIntParameter(ParmHandle,
|
||
WS_CONNECT_ON_REQUESTED_IF_ONLY,
|
||
0, // Disabled by default
|
||
0);
|
||
}
|
||
else
|
||
{
|
||
pConfig->ConnectOnRequestedInterfaceOnly = FALSE;
|
||
}
|
||
pConfig->SendDgramOnRequestedInterfaceOnly = (BOOLEAN) CTEReadSingleIntParameter(ParmHandle,
|
||
WS_SEND_DGRAM_ON_REQUESTED_IF_ONLY,
|
||
1, // Enabled by default
|
||
0);
|
||
pConfig->SMBDeviceEnabled = (BOOLEAN) CTEReadSingleIntParameter(ParmHandle,
|
||
WS_SMB_DEVICE_ENABLED,
|
||
1, // Enabled by default
|
||
0);
|
||
|
||
pConfig->MultipleCacheFlags = (BOOLEAN) CTEReadSingleIntParameter(ParmHandle,
|
||
WS_MULTIPLE_CACHE_FLAGS,
|
||
0, // Not enabled by default
|
||
0);
|
||
pConfig->UseDnsOnly = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_USE_DNS_ONLY,
|
||
0,
|
||
0); // disabled by default
|
||
if (pConfig->UseDnsOnly)
|
||
{
|
||
pConfig->ResolveWithDns = TRUE;
|
||
pConfig->TryAllNameServers = FALSE;
|
||
}
|
||
else
|
||
{
|
||
pConfig->ResolveWithDns = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_ENABLE_DNS,
|
||
1, // Enabled by default
|
||
0);
|
||
#ifdef MULTIPLE_WINS
|
||
pConfig->TryAllNameServers = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_TRY_ALL_NAME_SERVERS,
|
||
0, // disabled by default
|
||
0);
|
||
#endif
|
||
}
|
||
pConfig->SmbDisableNetbiosNameCacheLookup = (BOOLEAN)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_SMB_DISABLE_NETBIOS_NAME_CACHE_LOOKUP,
|
||
1, // Enabled by default
|
||
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);
|
||
|
||
pConfig->MinFreeLowerConnections = (ULONG)CTEReadSingleIntParameter(ParmHandle,
|
||
WS_MIN_FREE_INCOMING_CONNECTIONS,
|
||
DEFAULT_NBT_NUM_INITIAL_CONNECTIONS,
|
||
MIN_NBT_NUM_INITIAL_CONNECTIONS);
|
||
|
||
pConfig->BreakOnAssert = (BOOLEAN) CTEReadSingleIntParameter(ParmHandle,
|
||
WS_BREAK_ON_ASSERT,
|
||
1, // Enabled by default
|
||
0);
|
||
#ifndef REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
||
pConfig->DhcpProcessingDelay = (ULONG) CTEReadSingleIntParameter(ParmHandle,
|
||
WS_DHCP_PROCESSING_DELAY,
|
||
DEFAULT_DHCP_PROCESSING_DELAY,
|
||
MIN_DHCP_PROCESSING_DELAY);
|
||
#endif // REMOVE_IF_TCPIP_FIX___GATEWAY_AFTER_NOTIFY_BUG
|
||
|
||
//
|
||
// 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;
|
||
RegistryNodeType |= PROXY;
|
||
NbtConfig.ProxyType = 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;
|
||
}
|
||
RegistryNodeType = NodeType;
|
||
|
||
// 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, pOldScope, pNewScope;
|
||
PUCHAR pBuff;
|
||
PUCHAR pBuffer;
|
||
PUCHAR pPeriod;
|
||
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))
|
||
{
|
||
//
|
||
// 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 ((strlen(pBuffer) == 0) || (pBuffer[0] == '*'))
|
||
{
|
||
CTEMemFree(pBuffer);
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// 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 = NbtAllocMem (Len+2, NBT_TAG2('02'));
|
||
if (pScope)
|
||
{
|
||
CTEMemCopy((pScope+1),pBuffer,Len);
|
||
|
||
//
|
||
// Put a 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++;
|
||
pPeriod = pScope;
|
||
while (Chr = *pBuff)
|
||
{
|
||
Len++;
|
||
if (Chr == '.')
|
||
{
|
||
*pPeriod = (UCHAR) (pBuff-pPeriod-1);
|
||
|
||
//
|
||
// Each label can be at most 63 bytes long
|
||
//
|
||
if (*pPeriod > MAX_LABEL_LENGTH)
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
NbtLogEvent (EVENT_SCOPE_LABEL_TOO_LONG, STATUS_SUCCESS, 0x104);
|
||
break;
|
||
}
|
||
|
||
// check for two periods back to back and use no scope if this
|
||
// happens
|
||
if (*pPeriod == 0)
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
pPeriod = pBuff++;
|
||
}
|
||
else
|
||
pBuff++;
|
||
}
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// the last ptr is always the end of the name.
|
||
|
||
*pPeriod = (UCHAR)(pBuff - pPeriod -1);
|
||
|
||
pOldScope = pConfig->pScope;
|
||
pConfig->pScope = pScope;
|
||
pConfig->ScopeLength = (USHORT)Len;
|
||
if (pOldScope)
|
||
{
|
||
CTEMemFree(pOldScope);
|
||
}
|
||
CTEMemFree(pBuffer);
|
||
return;
|
||
}
|
||
CTEMemFree(pScope);
|
||
}
|
||
CTEMemFree(pBuffer);
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
NbtLogEvent (EVENT_SCOPE_LABEL_TOO_LONG, STATUS_SUCCESS, 0x105);
|
||
}
|
||
}
|
||
|
||
//
|
||
// the scope is one byte => '\0' - the length of the root name (zero)
|
||
//
|
||
// If the old scope and new scope are the same, then don't change the
|
||
// scope tag!
|
||
//
|
||
pOldScope = pConfig->pScope;
|
||
if (!(pOldScope) ||
|
||
(*pOldScope != '\0'))
|
||
{
|
||
if (pNewScope = NbtAllocMem ((1), NBT_TAG2('03')))
|
||
{
|
||
*pNewScope = '\0';
|
||
|
||
pConfig->ScopeLength = 1;
|
||
pConfig->pScope = pNewScope;
|
||
if (pOldScope)
|
||
{
|
||
CTEMemFree(pOldScope);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#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, pOldLmHosts;
|
||
PUCHAR pchr;
|
||
|
||
CTEPagedCode();
|
||
|
||
NbtConfig.PathLength = 0;
|
||
pOldLmHosts = pConfig->pLmHosts;
|
||
NbtConfig.pLmHosts = NULL;
|
||
|
||
//
|
||
// read in the LmHosts File location
|
||
//
|
||
#ifdef VXD
|
||
status = CTEReadIniString(ParmHandle,WS_LMHOSTS_FILE,&pBuffer);
|
||
#else
|
||
status = NTGetLmHostPath(&pBuffer);
|
||
#endif
|
||
|
||
//
|
||
// Find the last backslash so we can calculate the file path length
|
||
//
|
||
// Also, 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.
|
||
//
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
if (pchr = strrchr(pBuffer,'\\'))
|
||
{
|
||
NbtConfig.pLmHosts = pBuffer;
|
||
NbtConfig.PathLength = (ULONG) (pchr-pBuffer+1); // include backslash in length
|
||
|
||
IF_DBG(NBT_DEBUG_NAMESRV)
|
||
KdPrint(("Nbt.ReadLmHostFile: LmHostsFile path is %s\n",NbtConfig.pLmHosts));
|
||
}
|
||
else
|
||
{
|
||
CTEMemFree(pBuffer);
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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 (pOldLmHosts)
|
||
{
|
||
CTEMemFree(pOldLmHosts);
|
||
}
|
||
}
|
||
#ifdef VXD
|
||
#pragma END_INIT
|
||
#endif
|
||
|