mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2032 lines
62 KiB
2032 lines
62 KiB
/*++
|
|
|
|
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:
|
|
Jiandong Ruan (jruan) April 6 2000 Add NbtReadRegistryCleanup
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#include "precomp.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,
|
|
IN ULONG MaxBindings,
|
|
OUT tDEVICES *pDevices, // place to put read in config data
|
|
OUT ULONG *pNumDevices
|
|
);
|
|
|
|
NTSTATUS
|
|
OpenAndReadElement(
|
|
IN PUNICODE_STRING pucRootPath,
|
|
IN PWSTR pwsValueName,
|
|
OUT PUNICODE_STRING pucString
|
|
);
|
|
|
|
NTSTATUS
|
|
GetIpAddressesList (
|
|
IN HANDLE ParametersHandle,
|
|
IN PWSTR pwsKeyName,
|
|
IN ULONG MaxAddresses,
|
|
OUT tIPADDRESS *pAddrArray,
|
|
OUT ULONG *pNumGoodAddresses
|
|
);
|
|
|
|
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
|
|
);
|
|
|
|
NTSTATUS
|
|
ReadSmbDeviceInfo(
|
|
IN HANDLE NbConfigHandle
|
|
);
|
|
|
|
//******************* Pageable Routine Declarations ****************
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma CTEMakePageable(PAGE, NbtReadRegistry)
|
|
#pragma CTEMakePageable(PAGE, NbtReadRegistryCleanup)
|
|
#pragma CTEMakePageable(PAGE, ReadNameServerAddresses)
|
|
#pragma CTEMakePageable(PAGE, GetIpAddressesList)
|
|
#pragma CTEMakePageable(PAGE, GetServerAddress)
|
|
#pragma CTEMakePageable(PAGE, NTReadIniString)
|
|
#pragma CTEMakePageable(PAGE, GetIPFromRegistry)
|
|
#pragma CTEMakePageable(PAGE, NbtOpenRegistry)
|
|
#pragma CTEMakePageable(PAGE, NbtParseMultiSzEntries)
|
|
#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)
|
|
#pragma CTEMakePageable(PAGE, ReadSmbDeviceInfo)
|
|
#endif
|
|
//******************* Pageable Routine Declarations ****************
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtReadRegistry(
|
|
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.
|
|
This routine must be called with the NbtConfig.Resource lock HELD
|
|
|
|
Arguments:
|
|
|
|
Before calling this routine, the following Global parameters
|
|
must have been initialized (in DriverEntry):
|
|
|
|
NbtConfig.pRegistry
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES
|
|
otherwise.
|
|
|
|
--*/
|
|
{
|
|
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;
|
|
ULONG NumBindings;
|
|
|
|
CTEPagedCode();
|
|
|
|
*ppExportDevices = *ppBindDevices = NULL;
|
|
*ppAddrArray = NULL;
|
|
|
|
//
|
|
// Open the registry.
|
|
//
|
|
InitializeObjectAttributes (&TmpObjectAttributes,
|
|
&NbtConfig.pRegistry, // name
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // attributes
|
|
NULL, // root
|
|
NULL); // security descriptor
|
|
|
|
Status = ZwCreateKey (&NbtConfigHandle,
|
|
KEY_READ,
|
|
&TmpObjectAttributes,
|
|
0, // title index
|
|
NULL, // class
|
|
0, // create options
|
|
&Disposition); // disposition
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: ZwCreateKey FAILed, status=<%x>\n", Status));
|
|
NbtLogEvent (EVENT_NBT_CREATE_DRIVER, Status, 0x114);
|
|
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).
|
|
//
|
|
if (pBindDevices = NbtAllocMem ((sizeof(tDEVICES)+2*NBT_MAXIMUM_BINDINGS*sizeof(UNICODE_STRING)),
|
|
NBT_TAG2('25')))
|
|
{
|
|
if (pExportDevices=NbtAllocMem((sizeof(tDEVICES)+2*NBT_MAXIMUM_BINDINGS*sizeof(UNICODE_STRING)),
|
|
NBT_TAG2('26')))
|
|
{
|
|
ReadParameters (&NbtConfig, ParametersHandle);// Read various parameters from the registry
|
|
ReadSmbDeviceInfo (NbtConfigHandle); // Set the information for the SmbDevice
|
|
|
|
//
|
|
// From now on, the only failures we can encounter are in reading the
|
|
// Bind, Export or Name Server address entries, hence if we fail here,
|
|
// we will still return success, but will assume 0 devices configured!
|
|
//
|
|
pBindDevices->RegistryData = pExportDevices->RegistryData = NULL;
|
|
Status = NbtReadLinkageInformation (NBT_BIND,
|
|
LinkageHandle,
|
|
2*NBT_MAXIMUM_BINDINGS,
|
|
pBindDevices,
|
|
&NumBindings);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: NbtReadLinkageInformation FAILed - BIND <%x>\n",
|
|
Status));
|
|
NbtLogEvent (EVENT_NBT_READ_BIND, Status, 0x115);
|
|
}
|
|
else // if (NT_SUCCESS(Status))
|
|
{
|
|
IF_DBG(NBT_DEBUG_NTUTIL)
|
|
KdPrint(("Binddevice = %ws\n",pBindDevices->Names[0].Buffer));
|
|
|
|
NbtConfig.uNumDevicesInRegistry = (USHORT) NumBindings;
|
|
NumBindings = 0;
|
|
|
|
// Read the EXPORT information as well.
|
|
Status = NbtReadLinkageInformation (NBT_EXPORT,
|
|
LinkageHandle,
|
|
2*NBT_MAXIMUM_BINDINGS,
|
|
pExportDevices,
|
|
&NumBindings);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
// we want the lowest number for num devices in case there
|
|
// are more bindings than exports or viceversa
|
|
//
|
|
// ASSERT (NumBindings == NbtConfig.uNumDevicesInRegistry);
|
|
NbtConfig.uNumDevicesInRegistry = (USHORT)
|
|
(NbtConfig.uNumDevicesInRegistry > NumBindings ?
|
|
NumBindings : NbtConfig.uNumDevicesInRegistry);
|
|
|
|
if (NbtConfig.uNumDevicesInRegistry == 0)
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: WARNING - NumDevicesInRegistry = 0\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: NbtReadLinkageInformation FAILed - EXPORT <%x>\n",
|
|
Status));
|
|
NbtLogEvent (EVENT_NBT_READ_EXPORT, Status, 0x116);
|
|
}
|
|
}
|
|
|
|
if ((NT_SUCCESS(Status)) &&
|
|
(NbtConfig.uNumDevicesInRegistry))
|
|
{
|
|
IF_DBG(NBT_DEBUG_NTUTIL)
|
|
KdPrint(("Exportdevice = %ws\n",pExportDevices->Names[0].Buffer));
|
|
|
|
//
|
|
// read in the NameServer IP address now
|
|
//
|
|
Status = ReadNameServerAddresses (NbtConfigHandle,
|
|
pBindDevices,
|
|
NbtConfig.uNumDevicesInRegistry,
|
|
ppAddrArray);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (!(NodeType & BNODE)) // Post Warning!
|
|
{
|
|
NbtLogEvent (EVENT_NBT_NAME_SERVER_ADDRS, Status, 0x118);
|
|
}
|
|
KdPrint(("Nbt.NbtReadRegistry: ReadNameServerAddresses returned <%x>\n", Status));
|
|
}
|
|
else // if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// check if any WINS servers have been configured change
|
|
// to Hnode
|
|
//
|
|
if (NodeType & (BNODE | DEFAULT_NODE_TYPE))
|
|
{
|
|
ULONG i;
|
|
for (i=0; i<NbtConfig.uNumDevicesInRegistry; i++)
|
|
{
|
|
if (((*ppAddrArray)[i].NameServerAddress != LOOP_BACK) ||
|
|
((*ppAddrArray)[i].BackupServer != LOOP_BACK))
|
|
{
|
|
NodeType = MSNODE | (NodeType & PROXY);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!NT_SUCCESS(Status)) ||
|
|
(0 == NbtConfig.uNumDevicesInRegistry))
|
|
{
|
|
//
|
|
// We had problems reading the Bind or Export or Address entries
|
|
//
|
|
if (pBindDevices->RegistryData)
|
|
{
|
|
CTEMemFree(pBindDevices->RegistryData);
|
|
}
|
|
CTEMemFree(pBindDevices);
|
|
|
|
if (pExportDevices->RegistryData)
|
|
{
|
|
CTEMemFree(pExportDevices->RegistryData);
|
|
}
|
|
CTEMemFree(pExportDevices);
|
|
|
|
pBindDevices = pExportDevices = NULL;
|
|
NbtConfig.uNumDevicesInRegistry = 0;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
NbtConfig.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:
|
|
NbtConfig.PduNodeType = 0;
|
|
break;
|
|
case PNODE:
|
|
NbtConfig.PduNodeType = 1 << 13;
|
|
break;
|
|
case MNODE:
|
|
NbtConfig.PduNodeType = 1 << 14;
|
|
break;
|
|
case MSNODE:
|
|
NbtConfig.PduNodeType = 3 << 13;
|
|
break;
|
|
|
|
}
|
|
|
|
// read the name of the transport to bind to
|
|
//
|
|
if (NT_SUCCESS(ReadElement(ParametersHandle, WS_TRANSPORT_BIND_NAME, &ucString)))
|
|
{
|
|
UNICODE_STRING StreamsString;
|
|
|
|
//
|
|
// if there is already a bind string, free it before
|
|
// allocating another
|
|
//
|
|
if (NbtConfig.pTcpBindName)
|
|
{
|
|
//
|
|
// CreateDeviceString in tdicnct.c could access the pTcpBindName right
|
|
// after it is freed. The right way is using a lock. But, ...
|
|
//
|
|
// Hack!!!:
|
|
// Although this doesn't completely fix the problem, it has the minimum
|
|
// side-effect.
|
|
//
|
|
// The value of WS_TRANSPORT_BIND_NAME won't change. By doing this,
|
|
// we avoid the possible access-after-free problem in most cases.
|
|
//
|
|
RtlInitUnicodeString(&StreamsString, NbtConfig.pTcpBindName);
|
|
if (RtlCompareUnicodeString(&ucString,&StreamsString,TRUE)) {
|
|
CTEMemFree(NbtConfig.pTcpBindName);
|
|
NbtConfig.pTcpBindName = ucString.Buffer;
|
|
} else {
|
|
CTEMemFree(ucString.Buffer);
|
|
ucString = StreamsString;
|
|
}
|
|
} else {
|
|
NbtConfig.pTcpBindName = ucString.Buffer;
|
|
}
|
|
|
|
// ********** REMOVE LATER ***********
|
|
RtlInitUnicodeString(&StreamsString,NBT_TCP_BIND_NAME);
|
|
if (RtlCompareUnicodeString(&ucString,&StreamsString,TRUE))
|
|
{
|
|
StreamsStack = FALSE;
|
|
}
|
|
else
|
|
{
|
|
StreamsStack = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StreamsStack = TRUE;
|
|
}
|
|
|
|
ZwClose(ParametersHandle);
|
|
ZwClose(LinkageHandle);
|
|
ZwClose(NbtConfigHandle);
|
|
|
|
*ppExportDevices = pExportDevices;
|
|
*ppBindDevices = pBindDevices;
|
|
return (Status);
|
|
}
|
|
else
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: FAILed to allocate pExportDevices\n"));
|
|
}
|
|
CTEMemFree(pBindDevices);
|
|
}
|
|
else
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: FAILed to allocate pBindDevices\n"));
|
|
}
|
|
ZwClose(ParametersHandle);
|
|
}
|
|
else
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: NbtOpenRegistry FAILed for PARAMETERS, status=<%x>\n", Status));
|
|
NbtLogEvent (EVENT_NBT_OPEN_REG_PARAMS, OpenStatus, 0x119);
|
|
}
|
|
ZwClose(LinkageHandle);
|
|
}
|
|
else
|
|
{
|
|
KdPrint (("Nbt.NbtReadRegistry: NbtOpenRegistry FAILed for LINKAGE, status=<%x>\n", Status));
|
|
NbtLogEvent (EVENT_NBT_OPEN_REG_LINKAGE, OpenStatus, 0x120);
|
|
}
|
|
|
|
ZwClose (NbtConfigHandle);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NbtReadRegistryCleanup(
|
|
IN tDEVICES **ppBindDevices,
|
|
IN tDEVICES **ppExportDevices,
|
|
IN tADDRARRAY **ppAddrArray
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to release resources allocated by NbtReadRegistry
|
|
++*/
|
|
|
|
{
|
|
CTEPagedCode();
|
|
if (ppBindDevices[0]) {
|
|
CTEMemFree((PVOID)ppBindDevices[0]->RegistryData);
|
|
CTEMemFree((PVOID)ppBindDevices[0]);
|
|
ppBindDevices[0] = NULL;
|
|
}
|
|
if (ppExportDevices[0]) {
|
|
CTEMemFree((PVOID)ppExportDevices[0]->RegistryData);
|
|
CTEMemFree((PVOID)ppExportDevices[0]);
|
|
ppExportDevices[0] = NULL;
|
|
}
|
|
if (ppAddrArray[0]) {
|
|
CTEMemFree((PVOID)ppAddrArray[0]);
|
|
ppAddrArray[0] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ReadSmbDeviceInfo(
|
|
IN HANDLE NbtConfigHandle
|
|
)
|
|
{
|
|
HANDLE SmbHandle;
|
|
NTSTATUS Status;
|
|
|
|
CTEPagedCode();
|
|
|
|
Status = NbtOpenRegistry (NbtConfigHandle, WC_SMB_PARAMETERS_LOCATION, &SmbHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
NbtConfig.DefaultSmbSessionPort = (USHORT) CTEReadSingleIntParameter (SmbHandle,
|
|
SESSION_PORT,
|
|
NBT_SMB_SESSION_TCP_PORT,
|
|
1);
|
|
|
|
NbtConfig.DefaultSmbDatagramPort = (USHORT) CTEReadSingleIntParameter (SmbHandle,
|
|
DATAGRAM_PORT,
|
|
NBT_SMB_DATAGRAM_UDP_PORT,
|
|
1);
|
|
ZwClose (SmbHandle);
|
|
}
|
|
else
|
|
{
|
|
NbtConfig.DefaultSmbSessionPort = NBT_SMB_SESSION_TCP_PORT;
|
|
NbtConfig.DefaultSmbDatagramPort = NBT_SMB_DATAGRAM_UDP_PORT;
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
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 400
|
|
|
|
UNICODE_STRING ucString;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
HANDLE Handle;
|
|
LONG i,j,Len;
|
|
PWSTR pwsAdapter = L"Parameters\\Interfaces\\";
|
|
PWSTR BackSlash = L"\\";
|
|
tADDRARRAY *pAddrArray;
|
|
ULONG LenAdapter;
|
|
#ifdef _NETBIOSLESS
|
|
ULONG Options;
|
|
#endif
|
|
ULONG NumNameServerAddresses = 0;
|
|
|
|
CTEPagedCode();
|
|
|
|
*ppAddrArray = NULL;
|
|
|
|
// this is large enough for 400 characters of adapter name.
|
|
ucString.Buffer = NbtAllocMem (ADAPTER_SIZE_MAX, NBT_TAG2('27'));
|
|
if (!ucString.Buffer)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
pAddrArray = NbtAllocMem (sizeof(tADDRARRAY)*NumberDevices, NBT_TAG2('28'));
|
|
if (!pAddrArray)
|
|
{
|
|
CTEMemFree(ucString.Buffer);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
CTEZeroMemory(pAddrArray,sizeof(tADDRARRAY)*NumberDevices);
|
|
*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;
|
|
|
|
pAddrArray->NameServerAddress = LOOP_BACK;
|
|
pAddrArray->BackupServer = LOOP_BACK;
|
|
#ifdef MULTIPLE_WINS
|
|
pAddrArray->Others[0] = LOOP_BACK; // For Safety
|
|
pAddrArray->NumOtherServers = 0;
|
|
pAddrArray->LastResponsive = 0;
|
|
#endif
|
|
|
|
status = NbtOpenRegistry (NbtConfigHandle, ucString.Buffer, &Handle);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = GetIpAddressesList(Handle, // Generic routine to read in list of Ip addresses
|
|
PWS_NAME_SERVER_LIST,
|
|
2+MAX_NUM_OTHER_NAME_SERVERS,
|
|
pAddrArray->AllNameServers,
|
|
&NumNameServerAddresses);
|
|
|
|
if (!NT_SUCCESS(status) ||
|
|
(pAddrArray->NameServerAddress == LOOP_BACK))
|
|
{
|
|
NumNameServerAddresses = 0;
|
|
status = GetIpAddressesList(Handle,
|
|
PWS_DHCP_NAME_SERVER_LIST,
|
|
2+MAX_NUM_OTHER_NAME_SERVERS,
|
|
pAddrArray->AllNameServers,
|
|
&NumNameServerAddresses);
|
|
|
|
}
|
|
|
|
//
|
|
// Continue even if we failed to read in any IP addresses
|
|
//
|
|
if (NumNameServerAddresses > 2)
|
|
{
|
|
pAddrArray->NumOtherServers = (USHORT) NumNameServerAddresses - 2;
|
|
}
|
|
|
|
#ifdef _NETBIOSLESS
|
|
// NbtReadSingle doesn't quite do what we want. In this case, if the non-dhcp-
|
|
// decorated option is present but zero, we DO want to go on to the dhcp-
|
|
// decorated one. So, try the dhcp-decorated one explicitly if we get back zero.
|
|
Options = NbtReadSingleParameter( Handle, PWS_NETBIOS_OPTIONS, 0, 0 );
|
|
if (Options == 0)
|
|
{
|
|
Options = NbtReadSingleParameter( Handle, PWS_DHCP_NETBIOS_OPTIONS, 0, 0 );
|
|
}
|
|
// Options is encoded as four bytes
|
|
// Each byte can be an independent set of flags
|
|
// The high order three bytes can be used for controlling other aspects
|
|
// Enabled option, default is TRUE
|
|
pAddrArray->NetbiosEnabled = ((Options & 0xff) != NETBT_MODE_NETBIOS_DISABLED);
|
|
#endif
|
|
pAddrArray->RasProxyFlags = NbtReadSingleParameter(Handle, PWS_RAS_PROXY_FLAGS, 0, 0);
|
|
pAddrArray->EnableNagling = (NbtReadSingleParameter(Handle, PWS_ENABLE_NAGLING, 0, 0) != FALSE);
|
|
|
|
// 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_SUCCESS);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
GetIpAddressesList (
|
|
IN HANDLE ParametersHandle,
|
|
IN PWSTR pwsKeyName,
|
|
IN ULONG MaxAddresses,
|
|
OUT tIPADDRESS *pAddrArray,
|
|
OUT ULONG *pNumGoodAddresses
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to read a list of Ip addresses from the registry.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG NumEntriesRead, NumGoodAddresses, NumAddressesAttempted;
|
|
tDEVICES *pucAddressList;
|
|
NTSTATUS Status;
|
|
STRING String;
|
|
ULONG IpAddr;
|
|
PWSTR DhcpName = L"Dhcp";
|
|
UNICODE_STRING DhcpKeyName;
|
|
|
|
CTEPagedCode();
|
|
|
|
pucAddressList=NbtAllocMem((sizeof(tDEVICES)+2*NBT_MAXIMUM_BINDINGS*sizeof(UNICODE_STRING)),NBT_TAG('i'));
|
|
if (!pucAddressList)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Since NbtReadLinkageInformation very conveniently reads in the values for
|
|
// a MULTI_SZ registry entry, we will re-use this function here!
|
|
//
|
|
//
|
|
NumEntriesRead = 0;
|
|
Status = NbtReadLinkageInformation (pwsKeyName,
|
|
ParametersHandle,
|
|
2*NBT_MAXIMUM_BINDINGS,
|
|
pucAddressList,
|
|
&NumEntriesRead);
|
|
if ((STATUS_ILL_FORMED_SERVICE_ENTRY == Status) || (!NT_SUCCESS(Status)))
|
|
{
|
|
IF_DBG(NBT_DEBUG_NTUTIL)
|
|
KdPrint(("GetIpAddressesList: ERROR -- NbtReadLinkageInformation=<%x>, <%ws>\n",
|
|
Status, pwsKeyName));
|
|
|
|
CTEMemFree(pucAddressList);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
String.Buffer = NbtAllocMem (REGISTRY_BUFF_SIZE, NBT_TAG2('29'));
|
|
if (!String.Buffer)
|
|
{
|
|
KdPrint(("GetNameServerAddresses: Failed to Allocate memory\n"));
|
|
CTEMemFree((PVOID)pucAddressList->RegistryData);
|
|
CTEMemFree(pucAddressList);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
String.MaximumLength = REGISTRY_BUFF_SIZE;
|
|
|
|
//
|
|
// NumGoodAddresses will be bound by MaxAddresses, while
|
|
// NumAddressesAttempted will be bound by NumEntriesRead
|
|
// Also, we could have read NumEntriesRead > MaxAddresses
|
|
// (some of the entries could be invalid), but we will not
|
|
// attempt to read > 2*MaxAddresses entires
|
|
//
|
|
NumGoodAddresses = 0;
|
|
NumAddressesAttempted = 0;
|
|
while ((NumGoodAddresses < MaxAddresses) &&
|
|
(NumAddressesAttempted < NumEntriesRead) &&
|
|
(NumAddressesAttempted < (2*MaxAddresses)))
|
|
{
|
|
Status = RtlUnicodeStringToAnsiString(&String, &pucAddressList->Names[NumAddressesAttempted], FALSE);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = ConvertDottedDecimalToUlong((PUCHAR) String.Buffer, &IpAddr);
|
|
if (NT_SUCCESS(Status) && IpAddr)
|
|
{
|
|
pAddrArray[NumGoodAddresses++] = IpAddr;
|
|
}
|
|
}
|
|
NumAddressesAttempted++;
|
|
}
|
|
|
|
CTEMemFree ((PVOID)String.Buffer);
|
|
CTEMemFree ((PVOID)pucAddressList->RegistryData);
|
|
CTEMemFree ((PVOID)pucAddressList);
|
|
|
|
//
|
|
// If we were able to read in at least 1 good Ip address,
|
|
// return success, otherwise return failure!
|
|
//
|
|
if (NumGoodAddresses)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_ADDRESS;
|
|
}
|
|
|
|
*pNumGoodAddresses = NumGoodAddresses;
|
|
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, 0x121);
|
|
}
|
|
*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_TAG2('30'));
|
|
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;
|
|
}
|
|
}
|
|
CTEMemFree(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
|
|
//
|
|
// if the key is not there or it is set to a null string try to read the
|
|
// dhcp key
|
|
//
|
|
status = ReadElement (ParametersHandle, KeyName, &ucString);
|
|
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, &ucString);
|
|
CTEMemFree(String.Buffer); // Free the buffer allocated in NbtAppendString
|
|
}
|
|
}
|
|
// 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_TAG2('31'));
|
|
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 pucBindDevice,
|
|
OUT tIPADDRESS *pIpAddresses,
|
|
OUT tIPADDRESS *pSubnetMask,
|
|
IN ULONG MaxIpAddresses,
|
|
OUT ULONG *pNumIpAddresses,
|
|
IN enum eNbtIPAddressType IPAddressType
|
|
)
|
|
/*++
|
|
|
|
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:
|
|
|
|
Before calling this routine, the following Global parameters
|
|
must have been initialized (in DriverEntry):
|
|
|
|
NbtConfig.pRegistry
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES
|
|
otherwise.
|
|
|
|
--*/
|
|
{
|
|
ULONG i, Len, Disposition;
|
|
PVOID pBuffer;
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL; // by default
|
|
PWSTR pwsIpAddressName, pwsSubnetMask;
|
|
PWSTR pwsAdapterGuid, pwsLastSlash;
|
|
PWSTR pwsTcpParams = L"Tcpip\\Parameters\\Interfaces\\"; // key to open
|
|
PWSTR pwsUnderScore = L"_";
|
|
UNICODE_STRING Path;
|
|
HANDLE TcpGuidHandle;
|
|
OBJECT_ATTRIBUTES TmpObjectAttributes;
|
|
|
|
CTEPagedCode();
|
|
|
|
switch (IPAddressType)
|
|
{
|
|
case (NBT_IP_STATIC):
|
|
pwsIpAddressName = STATIC_IPADDRESS_NAME;
|
|
pwsSubnetMask = STATIC_IPADDRESS_SUBNET;
|
|
break;
|
|
|
|
case (NBT_IP_DHCP):
|
|
pwsIpAddressName = DHCP_IPADDRESS_NAME;
|
|
pwsSubnetMask = DHCP_IPADDRESS_SUBNET;
|
|
break;
|
|
|
|
case (NBT_IP_AUTOCONFIGURATION):
|
|
pwsIpAddressName = DHCP_IPAUTOCONFIGURATION_NAME;
|
|
pwsSubnetMask = DHCP_IPAUTOCONFIGURATION_SUBNET;
|
|
break;
|
|
|
|
default:
|
|
IF_DBG(NBT_DEBUG_NTUTIL)
|
|
KdPrint(("Invalid IP Address Type <%x>\n", IPAddressType));
|
|
return STATUS_INVALID_ADDRESS;
|
|
}
|
|
|
|
// Extract the Adapter Guid from the BindDevice name
|
|
// pucBindDevice: \Device\TCPIP_<AdapterGuid>
|
|
// Find the last back slash in the path name to the bind device
|
|
NbtFindLastSlash (pucBindDevice, &pwsAdapterGuid, &Len);
|
|
if (pwsAdapterGuid)
|
|
{
|
|
//
|
|
// Now, search the string to find the first underscore in "TCPIP_"
|
|
//
|
|
Len = wcslen(pwsAdapterGuid);
|
|
for(i=0; i<Len; i++)
|
|
{
|
|
if (pwsAdapterGuid[i] == *pwsUnderScore)
|
|
{
|
|
// want ptr to point at character after the slash
|
|
pwsAdapterGuid = &pwsAdapterGuid[i+1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we found the underscore, then we have found the Guid!
|
|
//
|
|
if (i < Len-1)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// We could not find the Guid!
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
// Initialize the Registry key name
|
|
// Get the total length of the Registry key to open (+1 for unicode null)
|
|
Len = NbtConfig.pRegistry.MaximumLength
|
|
+ (wcslen(pwsTcpParams) + wcslen(pwsAdapterGuid) + 1) * sizeof(WCHAR);
|
|
pBuffer = NbtAllocMem (Len, NBT_TAG2('32'));
|
|
if (!pBuffer)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
Path.Buffer = pBuffer;
|
|
Path.MaximumLength = (USHORT)Len;
|
|
Path.Length = 0;
|
|
|
|
RtlCopyUnicodeString(&Path, &NbtConfig.pRegistry); // \REGISTRY\Machine\System\ControlSet\Services\NetBT
|
|
NbtFindLastSlash(&Path, &pwsLastSlash, &Len); // \REGISTRY\Machine\System\ControlSet\Services
|
|
Path.Length = (USHORT)Len;
|
|
*pwsLastSlash = UNICODE_NULL;
|
|
|
|
RtlAppendUnicodeToString(&Path, pwsTcpParams); // ...Tcpip\Parameters\Interfaces
|
|
RtlAppendUnicodeToString(&Path, pwsAdapterGuid); // ......AdapterGuid
|
|
|
|
//
|
|
// Open the registry.
|
|
//
|
|
InitializeObjectAttributes (&TmpObjectAttributes,
|
|
&Path, // name
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // attributes
|
|
NULL, // root
|
|
NULL); // security descriptor
|
|
|
|
Status = ZwCreateKey (&TcpGuidHandle,
|
|
KEY_READ, // We don't need to write any values
|
|
&TmpObjectAttributes,
|
|
0, // title index
|
|
NULL, // class
|
|
0, // create options
|
|
&Disposition); // disposition
|
|
|
|
// We are done with the Path buffer, so free it
|
|
CTEMemFree(pBuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KdPrint(("Nbt.GetIPFromRegistry: Error, ZwCreateKey <%x>\n", Status));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Status = STATUS_INVALID_ADDRESS;
|
|
*pNumIpAddresses = 0;
|
|
if (NT_SUCCESS (GetIpAddressesList(TcpGuidHandle,
|
|
pwsIpAddressName,
|
|
MaxIpAddresses,
|
|
pIpAddresses,
|
|
pNumIpAddresses)))
|
|
{
|
|
//
|
|
// DHCP may put a 0 Ip address in the registry - we don't want to
|
|
// set the address under these conditions.
|
|
//
|
|
if ((*pNumIpAddresses) && (*pIpAddresses))
|
|
{
|
|
i = 0;
|
|
if (NT_SUCCESS (GetIpAddressesList(TcpGuidHandle,
|
|
pwsSubnetMask,
|
|
1,
|
|
pSubnetMask,
|
|
&i)))
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
ZwClose (TcpGuidHandle);
|
|
|
|
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 | OBJ_KERNEL_HANDLE, // attributes
|
|
NbConfigHandle, // root
|
|
NULL); // security descriptor
|
|
|
|
Status = ZwOpenKey (pHandle, KEY_READ, &TmpObjectAttributes);
|
|
|
|
return Status;
|
|
} /* NbOpenRegistry */
|
|
|
|
|
|
NTSTATUS
|
|
NbtParseMultiSzEntries(
|
|
IN PWSTR StartBindValue,
|
|
IN PWSTR EndBindValue,
|
|
IN ULONG MaxBindings,
|
|
OUT tDEVICES *pDevices,
|
|
OUT ULONG *pNumDevices
|
|
)
|
|
{
|
|
USHORT ConfigBindings = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
CTEPagedCode();
|
|
|
|
try {
|
|
while ((StartBindValue < EndBindValue) && (*StartBindValue != 0)) {
|
|
if (ConfigBindings >= MaxBindings) {
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
}
|
|
|
|
// 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)StartBindValue);
|
|
++ConfigBindings;
|
|
|
|
//
|
|
// Now advance the "Bind" value.
|
|
//
|
|
// wcslen => wide character string length for a unicode string
|
|
StartBindValue += wcslen((PCWSTR)StartBindValue) + 1;
|
|
}
|
|
|
|
*pNumDevices = ConfigBindings;
|
|
return (status);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
KdPrint (("Nbt.NbtParseMultiSzEntries: Exception <0x%x>\n", GetExceptionCode()));
|
|
for (ConfigBindings = 0; ConfigBindings < MaxBindings; ConfigBindings++) {
|
|
pDevices->Names[ConfigBindings].Buffer = NULL;
|
|
pDevices->Names[ConfigBindings].Length = pDevices->Names[ConfigBindings].MaximumLength = 0;
|
|
}
|
|
*pNumDevices = 0;
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtReadLinkageInformation(
|
|
IN PWSTR pName,
|
|
IN HANDLE LinkageHandle,
|
|
IN ULONG MaxBindings,
|
|
OUT tDEVICES *pDevices, // place to put read in config data
|
|
OUT ULONG *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
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS RegistryStatus;
|
|
UNICODE_STRING BindString;
|
|
ULONG BytesWritten = 0;
|
|
PKEY_VALUE_FULL_INFORMATION RegistryData;
|
|
|
|
CTEPagedCode();
|
|
|
|
pDevices->RegistryData = NULL;
|
|
RtlInitUnicodeString (&BindString, pName); // copy "Bind" or "Export" into the unicode string
|
|
|
|
//
|
|
// Determine how many bytes we need to allocate for the Read buffer
|
|
RegistryStatus = ZwQueryValueKey (LinkageHandle,
|
|
&BindString, // string to retrieve
|
|
KeyValueFullInformation,
|
|
NULL,
|
|
0,
|
|
&BytesWritten); // # of bytes to read
|
|
|
|
if ((RegistryStatus != STATUS_BUFFER_TOO_SMALL) ||
|
|
(BytesWritten == 0))
|
|
{
|
|
return STATUS_ILL_FORMED_SERVICE_ENTRY;
|
|
}
|
|
|
|
if (!(RegistryData = (PKEY_VALUE_FULL_INFORMATION) NbtAllocMem (BytesWritten, NBT_TAG2('33'))))
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RegistryStatus = ZwQueryValueKey (LinkageHandle,
|
|
&BindString, // string to retrieve
|
|
KeyValueFullInformation,
|
|
(PVOID) RegistryData, // returned info
|
|
BytesWritten,
|
|
&BytesWritten); // # of bytes valid data
|
|
|
|
if (!NT_SUCCESS(RegistryStatus) ||
|
|
(RegistryStatus == STATUS_BUFFER_OVERFLOW))
|
|
{
|
|
CTEMemFree(RegistryData);
|
|
return RegistryStatus;
|
|
}
|
|
|
|
if (BytesWritten == 0)
|
|
{
|
|
CTEMemFree(RegistryData);
|
|
return STATUS_ILL_FORMED_SERVICE_ENTRY;
|
|
}
|
|
|
|
pDevices->RegistryData = RegistryData;
|
|
NbtParseMultiSzEntries ((PWCHAR)((PUCHAR)RegistryData+RegistryData->DataOffset),
|
|
(PWSTR) ((PUCHAR)RegistryData+RegistryData->DataOffset+RegistryData->DataLength),
|
|
MaxBindings,
|
|
pDevices,
|
|
pNumDevices);
|
|
|
|
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 | OBJ_KERNEL_HANDLE, // 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_TAG2('35'));
|
|
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_TAG2('36'));
|
|
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))
|
|
{
|
|
CTEMemFree(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();
|
|
|
|
*ppPath = NULL;
|
|
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_TAG2('37'));
|
|
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);
|
|
CTEMemFree(ucDataBase.Buffer); // this memory was allocated in OpenAndReadElement
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
CTEMemFree(StringPath.Buffer);
|
|
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)
|
|
{
|
|
// overlapped copy
|
|
RtlMoveMemory (&(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
|
|
{
|
|
CTEMemFree(StringPath.Buffer);
|
|
}
|
|
|
|
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
|
|
//
|
|
if (!(pBuffer = NbtAllocMem (StringMax, NBT_TAG2('38'))))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
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;
|
|
}
|