/*++ 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 // // 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;iuNumDevices ;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; }