windows-nt/Source/XPSP1/NT/base/win32/client/compname.c

4099 lines
97 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
COMPNAME.C
Abstract:
This module contains the GetComputerName and SetComputerName APIs.
Also: DnsHostnameToComputerName
AddLocalAlternateComputerName
RemoveLocalAlternateComputerName
SetLocalPrimaryComputerName
EnumerateLocalComputerNames
Author:
Dan Hinsley (DanHi) 2-Apr-1992
Revision History:
Greg Johnson (gregjohn) 13-Feb-2001
Notes:
Currently there is no way to enumerate the list of Alternate Netbios
names. Presumably this will be fixed in a future release (Blackcomb?).
The flags parameter to all the *Local* API's is for this use.
--*/
#include <basedll.h>
#include <dnsapi.h>
typedef DNS_STATUS
(WINAPI DNS_VALIDATE_NAME_FN)(
IN LPCWSTR Name,
IN DNS_NAME_FORMAT Format
);
//
//
#define REASONABLE_LENGTH 128
#define COMPUTERNAME_ROOT \
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName"
#define NON_VOLATILE_COMPUTERNAME_NODE \
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName\\ComputerName"
#define VOLATILE_COMPUTERNAME_NODE \
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName\\ActiveComputerName"
#define ALT_COMPUTERNAME_NODE \
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\LanmanServer\\Parameters"
#define VOLATILE_COMPUTERNAME L"ActiveComputerName"
#define NON_VOLATILE_COMPUTERNAME L"ComputerName"
#define COMPUTERNAME_VALUE_NAME L"ComputerName"
#define COMPUTERNAME_OPTIONAL_NAME L"OptionalNames"
#define CLASS_STRING L"Network ComputerName"
#define TCPIP_POLICY_ROOT \
L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\System\\DNSclient"
#define TCPIP_POLICY_DOMAINNAME \
L"PrimaryDnsSuffix"
#define TCPIP_ROOT \
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters"
#define TCPIP_HOSTNAME \
L"Hostname"
#define TCPIP_NV_HOSTNAME \
L"NV Hostname"
#define TCPIP_DOMAINNAME \
L"Domain"
#define TCPIP_NV_DOMAINNAME \
L"NV Domain"
#define DNSCACHE_ROOT \
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\DnsCache\\Parameters"
#define DNS_ALT_HOSTNAME \
L"AlternateComputerNames"
//
// Allow the cluster guys to override the returned
// names with their own virtual names
//
const PWSTR ClusterNameVars[] = {
L"_CLUSTER_NETWORK_NAME_",
L"_CLUSTER_NETWORK_HOSTNAME_",
L"_CLUSTER_NETWORK_DOMAIN_",
L"_CLUSTER_NETWORK_FQDN_"
};
//
// Disallowed control characters (not including \0)
//
#define CTRL_CHARS_0 L"\001\002\003\004\005\006\007"
#define CTRL_CHARS_1 L"\010\011\012\013\014\015\016\017"
#define CTRL_CHARS_2 L"\020\021\022\023\024\025\026\027"
#define CTRL_CHARS_3 L"\030\031\032\033\034\035\036\037"
#define CTRL_CHARS_STR CTRL_CHARS_0 CTRL_CHARS_1 CTRL_CHARS_2 CTRL_CHARS_3
//
// Combinations of the above
//
#define ILLEGAL_NAME_CHARS_STR L"\"/\\[]:|<>+=;,?" CTRL_CHARS_STR
WCHAR DnsApiDllString[] = L"DNSAPI.DLL";
#define DNS_HOSTNAME 0
#define DNS_DOMAINNAME 1
DWORD
BaseMultiByteToWideCharWithAlloc(
LPCSTR lpBuffer,
LPWSTR * ppBufferW
)
/*++
Routine Description:
Converts Ansi strings to Unicode strings and allocs it's own space.
Arguments:
lpBuffer - Ansi to convert
ppBufferW - Unicode result
Return Value:
ERROR_SUCCESS, or various failures
--*/
{
ULONG cchBuffer = 0;
BOOL fSuccess = TRUE;
if (lpBuffer==NULL) {
*ppBufferW=NULL;
return ERROR_SUCCESS;
}
cchBuffer = strlen(lpBuffer);
// get enough space to cover the string and a trailing null
*ppBufferW = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (cchBuffer + 1) * sizeof(WCHAR));
if (*ppBufferW==NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
fSuccess = MultiByteToWideChar(CP_ACP,
0,
lpBuffer,
(cchBuffer+1)*sizeof(CHAR),
*ppBufferW,
cchBuffer+1
);
if (fSuccess) {
return ERROR_SUCCESS;
}
else {
return GetLastError();
}
}
DWORD
BaseWideCharToMultiByteWithAlloc(
LPCWSTR lpBuffer,
LPSTR * ppBufferA
)
/*++
Routine Description:
Converts Unicode strings to Ansi strings and allocs it's own space.
Arguments:
lpBuffer - Unicode to convert
ppBufferA - Ansi result
Return Value:
ERROR_SUCCESS, or various failures
--*/
{
ULONG cchBuffer = 0;
DWORD err = ERROR_SUCCESS;
cchBuffer = wcslen(lpBuffer);
*ppBufferA = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (cchBuffer + 1) * sizeof(CHAR));
if (*ppBufferA==NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
err = WideCharToMultiByte(CP_ACP,
0,
lpBuffer,
cchBuffer+1,
*ppBufferA,
(cchBuffer+1)*sizeof(CHAR),
NULL,
NULL
);
if (err!=0) {
return ERROR_SUCCESS;
}
else {
return GetLastError();
}
}
VOID
BaseConvertCharFree(
VOID * lpBuffer
)
/*++
Routine Description:
Frees space Convert functions.
Arguments:
lpBuffer - Buffer to free
Return Value:
None!
--*/
{
if (lpBuffer!=NULL) {
RtlFreeHeap(RtlProcessHeap(), 0, lpBuffer);
}
}
BOOL
BaseValidateFlags(
ULONG ulFlags
)
/*++
Routine Description:
Validates unused flags. For now the flags parameter of
AddLocalAlternateComputerName*
RemoveLocalAlternateComputerName*
EnumerateLocalAlternateComputerName*
SetLocalPrimaryComputerName*
are all reserved and should be 0. In subsequent releases
this function should change to check for a mask of valid
flags.
Arguments:
ulFlags -
Return Value:
BOOL
--*/
{
if (ulFlags!=0) {
return FALSE;
}
return TRUE;
}
BOOL
BaseValidateNetbiosName(
IN LPCWSTR lpComputerName
)
/*++
Routine Description:
Checks that the input is an acceptable Netbios name.
Arguments:
lpComputerName - name to validate
Return Value:
BOOL, GetLastError()
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG cchComputerName;
ULONG AnsiComputerNameLength;
cchComputerName = wcslen(lpComputerName);
//
// The name length limitation should be based on ANSI. (LanMan compatibility)
//
NtStatus = RtlUnicodeToMultiByteSize(&AnsiComputerNameLength,
(LPWSTR)lpComputerName,
cchComputerName * sizeof(WCHAR));
if ((!NT_SUCCESS(NtStatus)) ||
(AnsiComputerNameLength == 0 )||(AnsiComputerNameLength > MAX_COMPUTERNAME_LENGTH)) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// Check for illegal characters; return an error if one is found
//
if (wcscspn(lpComputerName, ILLEGAL_NAME_CHARS_STR) < cchComputerName) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// Check for leading or trailing spaces
//
if (lpComputerName[0] == L' ' ||
lpComputerName[cchComputerName-1] == L' ') {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
return(TRUE);
}
BOOL
BaseValidateDnsNames(
LPCWSTR lpDnsHostname
)
/*++
Routine Description:
Checks that the inputted name is an acceptable Dns hostname.
Arguments:
lpDnsHostName - name to validate
Return Value:
BOOL, GetLastError
--*/
{
HANDLE DnsApi ;
DNS_VALIDATE_NAME_FN * DnsValidateNameFn ;
DNS_STATUS DnsStatus ;
DnsApi = LoadLibraryW(DnsApiDllString);
if ( !DnsApi ) {
SetLastError(ERROR_DLL_NOT_FOUND);
return FALSE ;
}
DnsValidateNameFn = (DNS_VALIDATE_NAME_FN *) GetProcAddress( DnsApi, "DnsValidateName_W" );
if ( !DnsValidateNameFn )
{
FreeLibrary( DnsApi );
SetLastError(ERROR_INVALID_DLL);
return FALSE ;
}
DnsStatus = DnsValidateNameFn( lpDnsHostname, DnsNameHostnameLabel );
FreeLibrary( DnsApi );
if ( ( DnsStatus == 0 ) ||
( DnsStatus == DNS_ERROR_NON_RFC_NAME ) )
{
return TRUE;
}
else
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
DWORD
BasepGetMultiValueAddr(
IN LPWSTR lpMultiValue,
IN DWORD dwIndex,
OUT LPWSTR * ppFound,
OUT LPDWORD pcchIndex
)
/*++
Routine Description:
Given an index into a Multivalued register (string), return
the string at that index (not a copy), and it's char count location in the
full multivalued string
Arguments:
lpMultiValue - the register string (returned from NtQueryKey)
dwIndex - the index of which string to return
ppFound - the string found (if found) - user shouldn't free
pcchIndex - the location in lpMultiValue (in characters) of ppFound
Return Value:
ERROR (ERROR_NOT_FOUND if not found)
--*/
{
DWORD i = 0;
DWORD err = ERROR_SUCCESS;
DWORD cchTempIndex = 0;
// lpMultiValue is a concatenated string of (non-null)strings, null terminated
for (i=0; (i<dwIndex) && (lpMultiValue[0] != L'\0'); i++) {
cchTempIndex += wcslen(lpMultiValue) + 1;
lpMultiValue += wcslen(lpMultiValue) + 1;
}
// if we found the correct index, it's in lpMultiValue
if (lpMultiValue[0]!=L'\0') {
*ppFound = lpMultiValue;
*pcchIndex = cchTempIndex;
err = ERROR_SUCCESS;
}
else {
err = ERROR_NOT_FOUND;
}
return err;
}
DWORD
BaseGetMultiValueIndex(
IN LPWSTR lpMultiValue,
IN LPCWSTR lpValue,
OUT DWORD * pcchIndex
)
/*++
Routine Description:
Given a Multivalued register (string), and lpValue, return
the index of lpValue in lpMultiValue (ie, the 0th string, the 1st, etc).
Arguments:
lpMultiValue - the register string (returned from NtQueryKey)
lpValue - the string for which to search
pcchIndex - the index of the string matched (if found)
Return Value:
ERROR (ERROR_NOT_FOUND if not found)
--*/
{
LPWSTR lpFound = NULL;
DWORD cchFoundIndex = 0;
DWORD i = 0;
DWORD err = ERROR_SUCCESS;
BOOL fFound = FALSE;
while ((err==ERROR_SUCCESS) && !fFound) {
err = BasepGetMultiValueAddr(lpMultiValue,
i,
&lpFound,
&cchFoundIndex);
if (err == ERROR_SUCCESS) {
if ((wcslen(lpFound)==wcslen(lpValue)) && (!_memicmp(lpFound,lpValue, wcslen(lpValue)*sizeof(WCHAR)))) {
fFound = TRUE;
*pcchIndex = i;
}
}
i++;
}
return err;
}
DWORD
BaseRemoveMultiValue(
IN OUT LPWSTR lpMultiValue,
IN DWORD dwIndex,
IN OUT LPDWORD pcchMultiValue
)
/*++
Routine Description:
Given a multivalued registry value, and an index, it removes the string
located at that index.
Arguments:
lpMultiValue - the register string (returned from NtQueryKey)
dwIndex - the index of which string to remove
pcchMultiValue - number of chars in lpMultiValue (before and after)
Return Value:
ERRORS
--*/
{
DWORD err = ERROR_SUCCESS;
LPWSTR lpRest = NULL;
LPWSTR lpFound = NULL;
DWORD dwIndexFound = 0;
DWORD dwIndexRest = 0;
err = BasepGetMultiValueAddr(lpMultiValue,
dwIndex,
&lpFound,
&dwIndexFound);
if (err==ERROR_SUCCESS) {
// lpFound is a pointer to a string
// inside of lpMultiValue, to delete it,
// copy the rest of the string down
err = BasepGetMultiValueAddr(lpMultiValue,
dwIndex+1,
&lpRest,
&dwIndexRest);
if (err == ERROR_SUCCESS) {
// copy everything down
memmove(lpFound,lpRest,(*pcchMultiValue - dwIndexRest)*sizeof(WCHAR));
*pcchMultiValue = *pcchMultiValue - (dwIndexRest-dwIndexFound);
lpMultiValue[*pcchMultiValue] = L'\0';
}
else if (err == ERROR_NOT_FOUND) {
// string to remove is last string, simply write an extra null to orphan the string
*pcchMultiValue = *pcchMultiValue - (wcslen(lpFound) +1);
lpMultiValue[*pcchMultiValue] = L'\0';
err = ERROR_SUCCESS;
}
}
return err;
}
DWORD
BaseAddMultiValue(
IN OUT LPWSTR lpMultiValue,
IN LPCWSTR lpValue,
IN DWORD cchMultiValue
)
/*++
Routine Description:
Given a multivalued registry value, add another value.
Arguments:
lpMultiValue - the multivalued string (must be big enough to
hold current values + lpValue plus extra NULL
lpValue - the value to add
cchMultiValue - the count of characters USED in lpMultivalue
(not counting final null)
Return Value:
ERRORS
--*/
{
memcpy(lpMultiValue + cchMultiValue, lpValue, (wcslen(lpValue)+1)*sizeof(WCHAR));
lpMultiValue[cchMultiValue + wcslen(lpValue) + 1] = L'\0';
return ERROR_SUCCESS;
}
NTSTATUS
BasepGetNameFromReg(
PCWSTR Path,
PCWSTR Value,
PWSTR Buffer,
PDWORD Length
)
/*++
Routine Description:
This routine gets a string from the value at the specified registry key.
Arguments:
Path - Path to the registry key
Value - Name of the value to retrieve
Buffer - Buffer to return the value
Length - size of the buffer in characters
Return Value:
STATUS_SUCCESS, or various failures
--*/
{
NTSTATUS Status ;
HANDLE Key ;
OBJECT_ATTRIBUTES ObjA ;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
BYTE ValueBuffer[ REASONABLE_LENGTH ];
PKEY_VALUE_FULL_INFORMATION pKeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ValueBuffer;
BOOLEAN FreeBuffer = FALSE ;
DWORD ValueLength;
PWCHAR pTerminator;
//
// Open the node for the Subkey
//
RtlInitUnicodeString(&KeyName, Path );
InitializeObjectAttributes(&ObjA,
&KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(&Key, KEY_READ, &ObjA );
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString( &ValueName, Value );
Status = NtQueryValueKey(Key,
&ValueName,
KeyValueFullInformation,
pKeyValueInformation,
REASONABLE_LENGTH ,
&ValueLength);
if ( Status == STATUS_BUFFER_OVERFLOW )
{
pKeyValueInformation = RtlAllocateHeap( RtlProcessHeap(),
0,
ValueLength );
if ( pKeyValueInformation )
{
FreeBuffer = TRUE ;
Status = NtQueryValueKey( Key,
&ValueName,
KeyValueFullInformation,
pKeyValueInformation,
ValueLength,
&ValueLength );
}
}
if ( NT_SUCCESS(Status) ) {
//
// If the user's buffer is big enough, move it in
// First see if it's null terminated. If it is, pretend like
// it's not.
//
pTerminator = (PWCHAR)((PBYTE) pKeyValueInformation +
pKeyValueInformation->DataOffset +
pKeyValueInformation->DataLength);
pTerminator--;
if (*pTerminator == L'\0') {
pKeyValueInformation->DataLength -= sizeof(WCHAR);
}
if ( ( *Length >= pKeyValueInformation->DataLength/sizeof(WCHAR) + 1) &&
( Buffer != NULL ) ) {
//
// This isn't guaranteed to be NULL terminated, make it so
//
RtlCopyMemory(Buffer,
(LPWSTR)((PBYTE) pKeyValueInformation +
pKeyValueInformation->DataOffset),
pKeyValueInformation->DataLength);
pTerminator = (PWCHAR) ((PBYTE) Buffer +
pKeyValueInformation->DataLength);
*pTerminator = L'\0';
//
// Return the number of characters to the caller
//
*Length = pKeyValueInformation->DataLength / sizeof(WCHAR) ;
}
else {
Status = STATUS_BUFFER_OVERFLOW;
*Length = pKeyValueInformation->DataLength/sizeof(WCHAR) + 1;
}
}
NtClose( Key );
}
if ( FreeBuffer )
{
RtlFreeHeap( RtlProcessHeap(), 0, pKeyValueInformation );
}
return Status ;
}
NTSTATUS
BaseSetNameInReg(
PCWSTR Path,
PCWSTR Value,
PCWSTR Buffer
)
/*++
Routine Description:
This routine sets a string in the value at the registry key.
Arguments:
Path - Path to the registry key
Value - Name of the value to set
Buffer - Buffer to set
Return Value:
STATUS_SUCCESS, or various failures
--*/
{
NTSTATUS NtStatus;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hKey = NULL;
ULONG ValueLength;
//
// Open the ComputerName\ComputerName node
//
RtlInitUnicodeString(&KeyName, Path);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtOpenKey(&hKey, KEY_READ | KEY_WRITE, &ObjectAttributes);
if ( !NT_SUCCESS( NtStatus ) )
{
return NtStatus ;
}
//
// Update the value under this key
//
RtlInitUnicodeString(&ValueName, Value);
ValueLength = (wcslen( Buffer ) + 1) * sizeof(WCHAR);
NtStatus = NtSetValueKey(hKey,
&ValueName,
0,
REG_SZ,
(LPWSTR) Buffer,
ValueLength);
if ( NT_SUCCESS( NtStatus ) )
{
NtFlushKey( hKey );
}
NtClose(hKey);
return NtStatus ;
}
NTSTATUS
BaseSetMultiNameInReg(
PCWSTR Path,
PCWSTR Value,
PCWSTR Buffer,
DWORD BufferSize
)
/*++
Routine Description:
This routine sets a string in the value at the specified multivalued registry key.
Arguments:
Path - Path to the registry key
Value - Name of the value to set
Buffer - Buffer to set
BufferSize - Size of the buffer in characters
This is needed since there can be
many nulls in the buffer which we
want to write
Return Value:
STATUS_SUCCESS, or various failures
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hKey = NULL;
//
// Open the ComputerName\ComputerName node
//
RtlInitUnicodeString(&KeyName, Path);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtCreateKey(&hKey,
KEY_READ | KEY_WRITE,
&ObjectAttributes,
0,
NULL,
0,
NULL);
if ( !NT_SUCCESS( NtStatus ) )
{
return NtStatus ;
}
//
// Update the value under this key
//
RtlInitUnicodeString(&ValueName, Value);
NtStatus = NtSetValueKey(hKey,
&ValueName,
0,
REG_MULTI_SZ,
(LPWSTR) Buffer,
BufferSize);
if ( NT_SUCCESS( NtStatus ) )
{
NtFlushKey( hKey );
}
NtClose(hKey);
return NtStatus ;
}
NTSTATUS
BaseCreateMultiValue(
PCWSTR Path,
PCWSTR Value,
PCWSTR Buffer
)
/*++
Routine Description:
Create a multivalued registry value and initialize it with Buffer.
Arguments:
Path - Path to the registry key
Value - Name of the value to set
Buffer - Buffer to set
Return Value:
STATUS_SUCCESS, or various failures
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
LPWSTR lpMultiValue = NULL;
lpMultiValue = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG ( TMP_TAG ), (wcslen(Buffer)+2)*sizeof(WCHAR));
if (lpMultiValue==NULL) {
NtStatus = STATUS_NO_MEMORY;
}
else {
memcpy(lpMultiValue, Buffer, wcslen(Buffer)*sizeof(WCHAR));
lpMultiValue[wcslen(Buffer)] = L'\0';
lpMultiValue[wcslen(Buffer)+1] = L'\0';
NtStatus = BaseSetMultiNameInReg(Path,
Value,
lpMultiValue,
(wcslen(Buffer)+2)*sizeof(WCHAR));
RtlFreeHeap(RtlProcessHeap(), 0, lpMultiValue);
}
return NtStatus;
}
NTSTATUS
BaseAddMultiNameInReg(
PCWSTR Path,
PCWSTR Value,
PCWSTR Buffer
)
/*++
Routine Description:
This routine adds a string to the values at the specified multivalued registry key.
If the value already exists in the key, it does nothing.
Arguments:
Path - Path to the registry key
Value - Name of the value
Buffer - Buffer to add
Return Value:
STATUS_SUCCESS, or various failures
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
LPWSTR lpMultiValue = NULL;
ULONG cchMultiValue = 0;
DWORD dwIndex = 0;
DWORD err = ERROR_SUCCESS;
NtStatus = BasepGetNameFromReg(Path,
Value,
lpMultiValue,
&cchMultiValue);
if ( NtStatus==STATUS_NOT_FOUND || NtStatus==STATUS_OBJECT_NAME_NOT_FOUND) {
// create it, then we are done
NtStatus = BaseCreateMultiValue(Path,Value,Buffer);
return NtStatus;
} else if ( NtStatus==STATUS_BUFFER_OVERFLOW ) {
lpMultiValue = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (cchMultiValue+2+wcslen(Buffer))*sizeof(WCHAR));
if (lpMultiValue==NULL) {
NtStatus = STATUS_NO_MEMORY;
}
else {
NtStatus = BasepGetNameFromReg(Path,
Value,
lpMultiValue,
&cchMultiValue);
}
}
if (NT_SUCCESS( NtStatus)) {
// does it already exist in this structure?
err = BaseGetMultiValueIndex(lpMultiValue,
Buffer, &dwIndex);
// if err==ERROR_SUCCESS, then the above function found the string already in the value.
// don't add a duplicate
if (err!=ERROR_SUCCESS) {
err = BaseAddMultiValue(lpMultiValue, Buffer, cchMultiValue);
if (err == ERROR_SUCCESS) {
NtStatus = BaseSetMultiNameInReg(Path, Value, lpMultiValue, (cchMultiValue+2+wcslen(Buffer))*sizeof(WCHAR));
}
}
}
if (lpMultiValue) {
RtlFreeHeap( RtlProcessHeap(), 0, lpMultiValue);
}
return NtStatus ;
}
NTSTATUS
BaseRemoveMultiNameFromReg(
PCWSTR Path,
PCWSTR Value,
PCWSTR Buffer
)
/*++
Routine Description:
Removes a name from a multivalued registry. If the value exists more than once in the
list, removes them all.
Arguments:
Path - Path to the registry key
Value - Name of the value
Buffer - Buffer to remove
Return Value:
ERRORS
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DWORD err = ERROR_SUCCESS;
DWORD dwIndex = 0;
LPWSTR lpMultiValue = NULL;
ULONG cchNames = 0;
BOOL fNameRemoved = FALSE;
NtStatus = BasepGetNameFromReg(Path,
Value,
lpMultiValue,
&cchNames);
if (NtStatus==STATUS_BUFFER_OVERFLOW) {
lpMultiValue = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (cchNames) * sizeof(WCHAR));
if (lpMultiValue==NULL) {
NtStatus = STATUS_NO_MEMORY;
}
else {
NtStatus = BasepGetNameFromReg(Path,
Value,
lpMultiValue,
&cchNames);
err = RtlNtStatusToDosError(NtStatus);
if (err == ERROR_SUCCESS) {
// search for and remove all values in structure
while (err==ERROR_SUCCESS) {
err = BaseGetMultiValueIndex(lpMultiValue,
Buffer,
&dwIndex);
if (err == ERROR_SUCCESS) {
err = BaseRemoveMultiValue(lpMultiValue,
dwIndex,
&cchNames);
fNameRemoved = TRUE;
}
}
// if we removed a name, write it to the registry...
if (fNameRemoved) {
NtStatus = BaseSetMultiNameInReg(
Path,
Value,
lpMultiValue,
(cchNames+1)*sizeof(WCHAR));
}
else {
// Nothing to remove! ERRROR
NtStatus = STATUS_NOT_FOUND;
}
}
RtlFreeHeap(RtlProcessHeap(), 0, lpMultiValue);
}
}
return NtStatus;
}
BOOL
BaseSetNetbiosName(
IN LPCWSTR lpComputerName
)
/*++
Routine Description:
Sets the computer's net bios name
Arguments:
lpComputerName - name to set
Return Value:
BOOL, GetLastError()
--*/
{
NTSTATUS NtStatus ;
//
// Validate that the supplied computername is valid (not too long,
// no incorrect characters, no leading or trailing spaces)
//
if (!BaseValidateNetbiosName(lpComputerName)) {
return(FALSE);
}
//
// Open the ComputerName\ComputerName node
//
NtStatus = BaseSetNameInReg( NON_VOLATILE_COMPUTERNAME_NODE,
COMPUTERNAME_VALUE_NAME,
lpComputerName );
if ( !NT_SUCCESS( NtStatus ))
{
BaseSetLastNTError( NtStatus );
return FALSE ;
}
return TRUE ;
}
BOOL
BaseSetDnsName(
LPCWSTR lpComputerName
)
/*++
Routine Description:
Sets the computer's Dns hostname
Arguments:
lpComputerName - name to set
Return Value:
BOOL, GetLastError()
--*/
{
UNICODE_STRING NewComputerName ;
UNICODE_STRING DnsName ;
NTSTATUS Status ;
BOOL Return ;
HANDLE DnsApi ;
DNS_VALIDATE_NAME_FN * DnsValidateNameFn ;
DNS_STATUS DnsStatus ;
DnsApi = LoadLibraryW(DnsApiDllString);
if ( !DnsApi )
{
return FALSE ;
}
DnsValidateNameFn = (DNS_VALIDATE_NAME_FN *) GetProcAddress( DnsApi, "DnsValidateName_W" );
if ( !DnsValidateNameFn )
{
FreeLibrary( DnsApi );
return FALSE ;
}
DnsStatus = DnsValidateNameFn( lpComputerName, DnsNameHostnameLabel );
FreeLibrary( DnsApi );
if ( ( DnsStatus == 0 ) ||
( DnsStatus == DNS_ERROR_NON_RFC_NAME ) )
{
Status = BaseSetNameInReg( TCPIP_ROOT,
TCPIP_NV_HOSTNAME,
lpComputerName );
}
else
{
Status = STATUS_INVALID_PARAMETER ;
}
if ( NT_SUCCESS( Status ) )
{
RtlInitUnicodeString( &DnsName, lpComputerName );
Status = RtlDnsHostNameToComputerName( &NewComputerName,
&DnsName,
TRUE );
if ( NT_SUCCESS( Status ) )
{
Return = BaseSetNetbiosName( NewComputerName.Buffer );
RtlFreeUnicodeString( &NewComputerName );
if ( !Return )
{
//
// What? Rollback?
//
return FALSE ;
}
return TRUE ;
}
}
BaseSetLastNTError( Status ) ;
return FALSE ;
}
BOOL
BaseSetDnsDomain(
LPCWSTR lpName
)
/*++
Routine Description:
Sets the computer's Dns domain name
Arguments:
lpName - name to set
Return Value:
BOOL, GetLastError()
--*/
{
NTSTATUS Status ;
HANDLE DnsApi ;
DNS_VALIDATE_NAME_FN * DnsValidateNameFn ;
DNS_STATUS DnsStatus ;
//
// Special case the empty string, which is legal, but not according to dnsapi
//
if ( *lpName )
{
DnsApi = LoadLibraryW(DnsApiDllString);
if ( !DnsApi )
{
return FALSE ;
}
DnsValidateNameFn = (DNS_VALIDATE_NAME_FN *) GetProcAddress( DnsApi, "DnsValidateName_W" );
if ( !DnsValidateNameFn )
{
FreeLibrary( DnsApi );
return FALSE ;
}
DnsStatus = DnsValidateNameFn( lpName, DnsNameDomain );
FreeLibrary( DnsApi );
}
else
{
DnsStatus = 0 ;
}
//
// If the name is good, then keep it.
//
if ( ( DnsStatus == 0 ) ||
( DnsStatus == DNS_ERROR_NON_RFC_NAME ) )
{
Status = BaseSetNameInReg(
TCPIP_ROOT,
TCPIP_NV_DOMAINNAME,
lpName );
}
else
{
Status = STATUS_INVALID_PARAMETER ;
}
if ( !NT_SUCCESS( Status ) )
{
BaseSetLastNTError( Status );
return FALSE ;
}
return TRUE ;
}
BOOL
BaseSetAltNetBiosName(
IN LPCWSTR lpComputerName
)
/*++
Routine Description:
Sets the computer's alternate net bios name
Arguments:
lpComputerName - name to set
Return Value:
BOOL, GetLastError()
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
if (!BaseValidateNetbiosName(lpComputerName)) {
BaseSetLastNTError( STATUS_INVALID_PARAMETER );
return(FALSE);
}
NtStatus = BaseAddMultiNameInReg(
ALT_COMPUTERNAME_NODE,
COMPUTERNAME_OPTIONAL_NAME,
lpComputerName );
if ( !NT_SUCCESS( NtStatus ))
{
BaseSetLastNTError( NtStatus );
return FALSE ;
}
return TRUE ;
}
BOOL
BaseSetAltDnsFQHostname(
IN LPCWSTR lpDnsFQHostname
)
/*++
Routine Description:
Sets the computer's alternate fully qualified Dns name
Arguments:
lpDnsFQHostname - name to set
Return Value:
BOOL, GetLastError()
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NtStatus = BaseAddMultiNameInReg(
DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpDnsFQHostname);
if ( !NT_SUCCESS( NtStatus ))
{
BaseSetLastNTError( NtStatus );
return FALSE ;
}
return TRUE ;
}
BOOL
BaseIsAltDnsFQHostname(
LPCWSTR lpAltDnsFQHostname
)
/*++
Routine Description:
Verifies if lpAltDnsFQHostname is a previosly defined
alternate dns name
Arguments:
lpDnsFQHostname - name to check
Return Value:
TRUE if verifiably in use, FALSE otherwise, GetLastError()
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
LPWSTR lpNames = NULL;
ULONG cchNames = 0;
BOOL fFound = FALSE;
DWORD dwIndex = 0;
DWORD err = ERROR_SUCCESS;
NtStatus = BasepGetNameFromReg(DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpNames,
&cchNames);
if (NtStatus==STATUS_BUFFER_OVERFLOW) {
lpNames = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (cchNames) * sizeof(WCHAR));
if (lpNames!=NULL) {
NtStatus = BasepGetNameFromReg(DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpNames,
&cchNames);
err = RtlNtStatusToDosError(NtStatus);
if (err == ERROR_SUCCESS) {
err = BaseGetMultiValueIndex(lpNames,
lpAltDnsFQHostname,
&dwIndex);
fFound = err==ERROR_SUCCESS;
}
RtlFreeHeap( RtlProcessHeap(), 0, lpNames);
}
}
return fFound;
}
BOOL
BaseRemoveAltNetBiosName(
IN LPCWSTR lpAltComputerName
)
/*++
Routine Description:
Removes an alternate net bios name
Arguments:
lpAltComputerName - name to remove
Return Value:
BOOL, GetLastError()
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NtStatus = BaseRemoveMultiNameFromReg ( ALT_COMPUTERNAME_NODE,
COMPUTERNAME_OPTIONAL_NAME,
lpAltComputerName );
if ( !NT_SUCCESS( NtStatus ))
{
BaseSetLastNTError( NtStatus );
return FALSE ;
}
return TRUE ;
}
BOOL
BaseRemoveAltDnsFQHostname(
IN LPCWSTR lpAltDnsFQHostname
)
/*++
Routine Description:
Removes an alternate Dns hostname
Arguments:
lpAltDnsFqHostname - name to remove
Return Value:
BOOL, GetLastError()
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NtStatus = BaseRemoveMultiNameFromReg ( DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpAltDnsFQHostname );
if ( !NT_SUCCESS( NtStatus ))
{
BaseSetLastNTError( NtStatus );
return FALSE ;
}
return TRUE ;
}
DWORD
BaseEnumAltDnsFQHostnames(
OUT LPWSTR lpAltDnsFQHostnames,
IN OUT LPDWORD nSize
)
/*++
Routine Description:
Wrapper for BasepGetNameFromReg to return ERRORS, instead of STATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
status = BasepGetNameFromReg(
DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpAltDnsFQHostnames,
nSize);
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
if ((lpAltDnsFQHostnames!=NULL) && (*nSize>0)) {
lpAltDnsFQHostnames[0]=L'\0';
*nSize=0;
status=STATUS_SUCCESS;
}
else {
*nSize=1;
status=STATUS_BUFFER_OVERFLOW;
}
}
return RtlNtStatusToDosError(status);
}
LPWSTR
BaseParseDnsName(
IN LPCWSTR lpDnsName,
IN ULONG NamePart
)
/*++
Routine Description:
Given a dns name, parse out either the hostname or the domain name.
Arguments:
lpDnsName - a dns name, of the form hostname.domain - domain name optional
NamePart - DNS_HOSTNAME or DNS_DOMAINNAME
Return Value:
String requested
--*/
{
DWORD cchCharIndex = 0;
ULONG cchName = 0;
LPWSTR lpName = NULL;
if (lpDnsName==NULL) {
return NULL;
}
cchCharIndex = wcscspn(lpDnsName, L".");
if (NamePart==DNS_HOSTNAME) {
cchName = cchCharIndex;
}
else {
if (cchCharIndex==wcslen(lpDnsName)) {
// no period found,
cchName = 0;
}
else {
cchName = wcslen(lpDnsName)-(cchCharIndex+1);
}
}
lpName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (cchName + 1)*sizeof(WCHAR));
if (lpName==NULL) {
return NULL;
}
// copy the correct part into the structure
if (NamePart==DNS_HOSTNAME) {
wcsncpy(lpName, lpDnsName, cchName);
}
else {
wcsncpy(lpName, (LPWSTR)(lpDnsName + cchCharIndex + 1), cchName);
}
lpName[cchName] = L'\0';
return lpName;
}
BOOL
BaseIsNetBiosNameInUse(
LPWSTR lpCompName
)
/*++
Routine Description:
Verify whether lpCompName is being used by any alternate DNS names
(ie whether any existing alternate DNS names map to lpCompName with
DnsHostnameToComputerNameW)
Arguments:
lpCompName - net bios name to verify
Return Value:
FALSE if verifiably is not being used, true otherwise, GetLastError()
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
LPWSTR lpMultiValue = NULL;
ULONG cchMultiValue = 0;
LPWSTR lpAltDnsFQHostname = NULL;
ULONG cchAltDnsHostname = 0;
DWORD dwIndex = 0;
LPWSTR lpAltCompName = NULL;
ULONG cchAltCompName = 0;
DWORD err = ERROR_SUCCESS;
BOOL fInUse = FALSE;
BOOL fIsNetBiosNameInUse = TRUE;
NtStatus = BasepGetNameFromReg(DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpMultiValue,
&cchMultiValue);
err = RtlNtStatusToDosError(NtStatus);
if (err==ERROR_MORE_DATA) {
lpMultiValue = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), cchMultiValue * sizeof(WCHAR));
if (lpMultiValue==NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return TRUE;
}
NtStatus = BasepGetNameFromReg(DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpMultiValue,
&cchMultiValue);
err=RtlNtStatusToDosError(NtStatus);
}
if (err == ERROR_SUCCESS) {
dwIndex = 0;
while (err == ERROR_SUCCESS) {
err = BasepGetMultiValueAddr(lpMultiValue,
dwIndex,
&lpAltDnsFQHostname,
&cchAltDnsHostname);
// get net bios names
if (err == ERROR_SUCCESS) {
if (!DnsHostnameToComputerNameW(lpAltDnsFQHostname,
lpAltCompName,
&cchAltCompName)) {
err = GetLastError();
if (err==ERROR_MORE_DATA) {
// DnsHostNameToComputerNameW bug
cchAltCompName += 1;
// DnsHostNameToComputerNameW bug
lpAltCompName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), cchAltCompName*sizeof(WCHAR));
if (lpAltCompName==NULL) {
err = ERROR_NOT_ENOUGH_MEMORY;
}
else {
if (!DnsHostnameToComputerNameW(lpAltDnsFQHostname, lpAltCompName, &cchAltCompName)) {
err = GetLastError();
} else {
err = ERROR_SUCCESS;
}
}
}
}
if (err==ERROR_SUCCESS) {
if (!_wcsicmp(lpAltCompName, lpCompName)) {
fInUse = TRUE;
}
}
}
dwIndex++;
}
// exits the above while loop when err==ERROR_NOT_FOUND, whether found or not
if (err==ERROR_NOT_FOUND) {
fIsNetBiosNameInUse = fInUse;
err = ERROR_SUCCESS;
}
else {
// error, default to in use
fIsNetBiosNameInUse = TRUE;
}
}
if (lpMultiValue) {
RtlFreeHeap(RtlProcessHeap(), 0, lpMultiValue);
}
if (lpAltCompName) {
RtlFreeHeap(RtlProcessHeap(), 0, lpAltCompName);
}
return fIsNetBiosNameInUse;
}
//
// Worker routine
//
NTSTATUS
GetNameFromValue(
HANDLE hKey,
LPWSTR SubKeyName,
LPWSTR ValueValue,
LPDWORD nSize
)
/*++
Routine Description:
This returns the value of "ComputerName" value entry under the subkey
SubKeyName relative to hKey. This is used to get the value of the
ActiveComputerName or ComputerName values.
Arguments:
hKey - handle to the Key the SubKey exists under
SubKeyName - name of the subkey to look for the value under
ValueValue - where the value of the value entry will be returned
nSize - pointer to the size (in characters) of the ValueValue buffer
Return Value:
--*/
{
#define VALUE_BUFFER_SIZE (sizeof(KEY_VALUE_FULL_INFORMATION) + \
(sizeof( COMPUTERNAME_VALUE_NAME ) + MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR))
NTSTATUS NtStatus;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hSubKey;
BYTE ValueBuffer[VALUE_BUFFER_SIZE];
PKEY_VALUE_FULL_INFORMATION pKeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ValueBuffer;
DWORD ValueLength;
PWCHAR pTerminator;
//
// Open the node for the Subkey
//
RtlInitUnicodeString(&KeyName, SubKeyName);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
hKey,
NULL
);
NtStatus = NtOpenKey(&hSubKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(NtStatus)) {
RtlInitUnicodeString(&ValueName, COMPUTERNAME_VALUE_NAME);
NtStatus = NtQueryValueKey(hSubKey,
&ValueName,
KeyValueFullInformation,
pKeyValueInformation,
VALUE_BUFFER_SIZE,
&ValueLength);
NtClose(hSubKey);
if (NT_SUCCESS(NtStatus) &&
(pKeyValueInformation->DataLength > 0 )) {
//
// If the user's buffer is big enough, move it in
// First see if it's null terminated. If it is, pretend like
// it's not.
//
pTerminator = (PWCHAR)((PBYTE) pKeyValueInformation +
pKeyValueInformation->DataOffset +
pKeyValueInformation->DataLength);
pTerminator--;
if (*pTerminator == L'\0') {
pKeyValueInformation->DataLength -= sizeof(WCHAR);
}
if (*nSize >= pKeyValueInformation->DataLength/sizeof(WCHAR) + 1) {
//
// This isn't guaranteed to be NULL terminated, make it so
//
RtlCopyMemory(ValueValue,
(LPWSTR)((PBYTE) pKeyValueInformation +
pKeyValueInformation->DataOffset),
pKeyValueInformation->DataLength);
pTerminator = (PWCHAR) ((PBYTE) ValueValue +
pKeyValueInformation->DataLength);
*pTerminator = L'\0';
//
// Return the number of characters to the caller
//
*nSize = wcslen(ValueValue);
}
else {
NtStatus = STATUS_BUFFER_OVERFLOW;
*nSize = pKeyValueInformation->DataLength/sizeof(WCHAR) + 1;
}
}
else {
//
// If the value has been deleted (zero length data),
// return object not found.
//
if ( NT_SUCCESS( NtStatus ) )
{
NtStatus = STATUS_OBJECT_NAME_NOT_FOUND ;
}
}
}
return(NtStatus);
}
//
// UNICODE APIs
//
BOOL
WINAPI
GetComputerNameW (
LPWSTR lpBuffer,
LPDWORD nSize
)
/*++
Routine Description:
This returns the active computername. This is the computername when the
system was last booted. If this is changed (via SetComputerName) it does
not take effect until the next system boot.
Arguments:
lpBuffer - Points to the buffer that is to receive the
null-terminated character string containing the computer name.
nSize - Specifies the maximum size (in characters) of the buffer. This
value should be set to at least MAX_COMPUTERNAME_LENGTH + 1 to allow
sufficient room in the buffer for the computer name. The length
of the string is returned in nSize.
Return Value:
TRUE on success, FALSE on failure.
--*/
{
NTSTATUS NtStatus;
UNICODE_STRING KeyName;
UNICODE_STRING Class;
UNICODE_STRING ValueName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hKey = NULL;
HANDLE hNewKey = NULL;
ULONG Disposition;
ULONG ValueLength;
BOOL ReturnValue;
DWORD Status;
DWORD errcode;
//
// First check to see if the cluster computername variable is set.
// If so, this overrides the actual computername to fool the application
// into working when its network name and computer name are different.
//
ValueLength = GetEnvironmentVariableW(L"_CLUSTER_NETWORK_NAME_",
lpBuffer,
*nSize);
if (ValueLength != 0) {
//
// The environment variable exists, return it directly but make sure
// we honor return semantics
//
ReturnValue = ( *nSize >= ValueLength ? TRUE : FALSE );
if ( !ReturnValue ) {
SetLastError( ERROR_BUFFER_OVERFLOW );
}
*nSize = ValueLength;
return(ReturnValue);
}
if ( (gpTermsrvGetComputerName) &&
((errcode = gpTermsrvGetComputerName(lpBuffer, nSize)) != ERROR_RETRY) ) {
if (errcode == ERROR_BUFFER_OVERFLOW ) {
ReturnValue = FALSE;
goto Cleanup;
} else {
goto GoodReturn;
}
}
//
// Open the Computer node, both computername keys are relative
// to this node.
//
RtlInitUnicodeString(&KeyName, COMPUTERNAME_ROOT);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
//
// This should never happen! This key should have been created
// at setup, and protected by an ACL so that only the ADMIN could
// write to it. Generate an event, and return a NULL computername.
//
// NTRAID#NTBUG9-174986-2000/08/31-DavePr Log event or do alert or something.
//
// Return a NULL computername
//
if (ARGUMENT_PRESENT(lpBuffer))
{
lpBuffer[0] = L'\0';
}
*nSize = 0;
goto GoodReturn;
}
if (!NT_SUCCESS(NtStatus)) {
//
// Some other error, return it to the caller
//
goto ErrorReturn;
}
//
// Try to get the name from the volatile key
//
NtStatus = GetNameFromValue(hKey, VOLATILE_COMPUTERNAME, lpBuffer,
nSize);
//
// The user's buffer wasn't big enough, just return the error.
//
if(NtStatus == STATUS_BUFFER_OVERFLOW) {
SetLastError(ERROR_BUFFER_OVERFLOW);
ReturnValue = FALSE;
goto Cleanup;
}
if (NT_SUCCESS(NtStatus)) {
//
// The volatile copy is already there, just return it
//
goto GoodReturn;
}
//
// The volatile key isn't there, try for the non-volatile one
//
NtStatus = GetNameFromValue(hKey, NON_VOLATILE_COMPUTERNAME, lpBuffer,
nSize);
if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
//
// This should never happen! This value should have been created
// at setup, and protected by an ACL so that only the ADMIN could
// write to it. Generate an event, and return an error to the
// caller
//
// NTRAID#NTBUG9-174986-2000/08/31-DavePr Log event or do alert or something.
//
// Return a NULL computername
//
lpBuffer[0] = L'\0';
*nSize = 0;
goto GoodReturn;
}
if (!NT_SUCCESS(NtStatus)) {
//
// Some other error, return it to the caller
//
goto ErrorReturn;
}
//
// Now create the volatile key to "lock this in" until the next boot
//
RtlInitUnicodeString(&Class, CLASS_STRING);
//
// Turn KeyName into a UNICODE_STRING
//
RtlInitUnicodeString(&KeyName, VOLATILE_COMPUTERNAME);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
hKey,
NULL
);
//
// Now create the key
//
NtStatus = NtCreateKey(&hNewKey,
KEY_WRITE | KEY_READ,
&ObjectAttributes,
0,
&Class,
REG_OPTION_VOLATILE,
&Disposition);
if (Disposition == REG_OPENED_EXISTING_KEY) {
//
// Someone beat us to this, just get the value they put there
//
NtStatus = GetNameFromValue(hKey, VOLATILE_COMPUTERNAME, lpBuffer,
nSize);
if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
//
// This should never happen! It just told me it existed
//
NtStatus = STATUS_UNSUCCESSFUL;
goto ErrorReturn;
}
}
//
// Create the value under this key
//
RtlInitUnicodeString(&ValueName, COMPUTERNAME_VALUE_NAME);
ValueLength = (wcslen(lpBuffer) + 1) * sizeof(WCHAR);
NtStatus = NtSetValueKey(hNewKey,
&ValueName,
0,
REG_SZ,
lpBuffer,
ValueLength);
if (!NT_SUCCESS(NtStatus)) {
goto ErrorReturn;
}
goto GoodReturn;
ErrorReturn:
//
// An error was encountered, convert the status and return
//
BaseSetLastNTError(NtStatus);
ReturnValue = FALSE;
goto Cleanup;
GoodReturn:
//
// Everything went ok, update nSize with the length of the buffer and
// return
//
*nSize = wcslen(lpBuffer);
ReturnValue = TRUE;
goto Cleanup;
Cleanup:
if (hKey) {
NtClose(hKey);
}
if (hNewKey) {
NtClose(hNewKey);
}
return(ReturnValue);
}
BOOL
WINAPI
SetComputerNameW (
LPCWSTR lpComputerName
)
/*++
Routine Description:
This sets what the computername will be when the system is next booted. This
does not effect the active computername for the remainder of this boot, nor
what is returned by GetComputerName before the next system boot.
Arguments:
lpComputerName - points to the buffer that is contains the
null-terminated character string containing the computer name.
Return Value:
Returns TRUE on success, FALSE on failure.
--*/
{
NTSTATUS NtStatus;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hKey = NULL;
ULONG ValueLength;
ULONG ComputerNameLength;
ULONG AnsiComputerNameLength;
//
// Validate that the supplied computername is valid (not too long,
// no incorrect characters, no leading or trailing spaces)
//
ComputerNameLength = wcslen(lpComputerName);
//
// The name length limitation should be based on ANSI. (LanMan compatibility)
//
NtStatus = RtlUnicodeToMultiByteSize(&AnsiComputerNameLength,
(LPWSTR)lpComputerName,
ComputerNameLength * sizeof(WCHAR));
if ((!NT_SUCCESS(NtStatus)) ||
(AnsiComputerNameLength == 0 )||(AnsiComputerNameLength > MAX_COMPUTERNAME_LENGTH)) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// Check for illegal characters; return an error if one is found
//
if (wcscspn(lpComputerName, ILLEGAL_NAME_CHARS_STR) < ComputerNameLength) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// Check for leading or trailing spaces
//
if (lpComputerName[0] == L' ' ||
lpComputerName[ComputerNameLength-1] == L' ') {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// Open the ComputerName\ComputerName node
//
RtlInitUnicodeString(&KeyName, NON_VOLATILE_COMPUTERNAME_NODE);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtOpenKey(&hKey, KEY_READ | KEY_WRITE, &ObjectAttributes);
if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
//
// This should never happen! This key should have been created
// at setup, and protected by an ACL so that only the ADMIN could
// write to it. Generate an event, and return a NULL computername.
//
// NTRAID#NTBUG9-174986-2000/08/31-DavePr Log event or do alert or something.
// (One alternative for this instance would be to actually create the missing
// entry here -- but we'd have to be sure to get the right ACLs etc, etc.
SetLastError(ERROR_GEN_FAILURE);
return(FALSE);
}
//
// Update the value under this key
//
RtlInitUnicodeString(&ValueName, COMPUTERNAME_VALUE_NAME);
ValueLength = (wcslen(lpComputerName) + 1) * sizeof(WCHAR);
NtStatus = NtSetValueKey(hKey,
&ValueName,
0,
REG_SZ,
(LPWSTR)lpComputerName,
ValueLength);
if (!NT_SUCCESS(NtStatus)) {
BaseSetLastNTError(NtStatus);
NtClose(hKey);
return(FALSE);
}
NtFlushKey(hKey);
NtClose(hKey);
return(TRUE);
}
BOOL
WINAPI
GetComputerNameExW(
IN COMPUTER_NAME_FORMAT NameType,
OUT LPWSTR lpBuffer,
IN OUT LPDWORD nSize
)
/*++
Routine Description:
This returns the active computername in a particular format. This is the
computername when the system was last booted. If this is changed (via
SetComputerName) it does not take effect until the next system boot.
Arguments:
NameType - Possible name formats to return the computer name in:
ComputerNameNetBIOS - netbios name (compatible with GetComputerName)
ComputerNameDnsHostname - DNS host name
ComputerNameDnsDomain - DNS Domain name
ComputerNameDnsFullyQualified - DNS Fully Qualified (hostname.dnsdomain)
lpBuffer - Points to the buffer that is to receive the
null-terminated character string containing the computer name.
nSize - Specifies the maximum size (in characters) of the buffer. This
value should be set to at least MAX_COMPUTERNAME_LENGTH + 1 to allow
sufficient room in the buffer for the computer name. The length
of the string is returned in nSize.
Return Value:
TRUE on success, FALSE on failure.
--*/
{
NTSTATUS Status ;
DWORD ValueLength ;
DWORD HostLength ;
DWORD DomainLength ;
BOOL DontSetReturn = FALSE ;
COMPUTER_NAME_FORMAT HostNameFormat, DomainNameFormat ;
if ( NameType >= ComputerNameMax )
{
BaseSetLastNTError( STATUS_INVALID_PARAMETER );
return FALSE ;
}
if ((nSize==NULL) || ((lpBuffer==NULL) && (*nSize>0))) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// For general names, allow clusters to override the physical name:
//
if ( (NameType >= ComputerNameNetBIOS) &&
(NameType <= ComputerNameDnsFullyQualified ) )
{
ValueLength = GetEnvironmentVariableW(
ClusterNameVars[ NameType ],
lpBuffer,
*nSize );
if ( ValueLength )
{
BOOL ReturnValue;
//
// ValueLength is the length+NULL of the env. string regardless of
// how much was copied (gregjohn 1/30/01 note: this isn't the behaivor
// of the rest of the function, which returns length+NULL on failure
// and length on success). Indicate how many characters are in the string
// and if the user's buffer wasn't big enough, return FALSE
//
ReturnValue = ( *nSize >= ValueLength ? TRUE : FALSE );
if ( !ReturnValue ) {
SetLastError( ERROR_MORE_DATA );
}
*nSize = ValueLength ;
return ReturnValue;
}
}
if ( lpBuffer && (*nSize > 0) )
{
lpBuffer[0] = L'\0';
}
switch ( NameType )
{
case ComputerNameNetBIOS:
case ComputerNamePhysicalNetBIOS:
Status = BasepGetNameFromReg(
VOLATILE_COMPUTERNAME_NODE,
COMPUTERNAME_VALUE_NAME,
lpBuffer,
nSize );
if ( !NT_SUCCESS( Status ) )
{
if ( Status != STATUS_BUFFER_OVERFLOW )
{
//
// Hmm, the value (or key) is missing. Try the non-volatile
// one.
//
Status = BasepGetNameFromReg(
NON_VOLATILE_COMPUTERNAME_NODE,
COMPUTERNAME_VALUE_NAME,
lpBuffer,
nSize );
}
}
break;
case ComputerNameDnsHostname:
case ComputerNamePhysicalDnsHostname:
Status = BasepGetNameFromReg(
TCPIP_ROOT,
TCPIP_HOSTNAME,
lpBuffer,
nSize );
break;
case ComputerNameDnsDomain:
case ComputerNamePhysicalDnsDomain:
//
// Allow policy to override the domain name from the
// tcpip key.
//
Status = BasepGetNameFromReg(
TCPIP_POLICY_ROOT,
TCPIP_POLICY_DOMAINNAME,
lpBuffer,
nSize );
//
// If no policy read from the tcpip key.
//
if ( !NT_SUCCESS( Status ) )
{
Status = BasepGetNameFromReg(
TCPIP_ROOT,
TCPIP_DOMAINNAME,
lpBuffer,
nSize );
}
break;
case ComputerNameDnsFullyQualified:
case ComputerNamePhysicalDnsFullyQualified:
//
// This is the tricky case. We have to construct the name from
// the two components for the caller.
//
//
// In general, don't set the last status, since we'll end up using
// the other calls to handle that for us.
//
DontSetReturn = TRUE ;
Status = STATUS_UNSUCCESSFUL ;
if ( lpBuffer == NULL )
{
//
// If this is just the computation call, quickly do the
// two components
//
HostLength = DomainLength = 0 ;
GetComputerNameExW( ComputerNameDnsHostname, NULL, &HostLength );
if ( GetLastError() == ERROR_MORE_DATA )
{
GetComputerNameExW( ComputerNameDnsDomain, NULL, &DomainLength );
if ( GetLastError() == ERROR_MORE_DATA )
{
//
// Simply add. Note that since both account for a
// null terminator, the '.' that goes between them is
// covered.
//
*nSize = HostLength + DomainLength ;
Status = STATUS_BUFFER_OVERFLOW ;
DontSetReturn = FALSE ;
}
}
}
else
{
HostLength = *nSize ;
if ( GetComputerNameExW( ComputerNameDnsHostname,
lpBuffer,
&HostLength ) )
{
HostLength += 1; // Add in the zero character (or . depending on perspective)
lpBuffer[ HostLength - 1 ] = L'.';
DomainLength = *nSize - HostLength ;
if (GetComputerNameExW( ComputerNameDnsDomain,
&lpBuffer[ HostLength ],
&DomainLength ) )
{
Status = STATUS_SUCCESS ;
if ( DomainLength == 0 )
{
lpBuffer[ HostLength - 1 ] = L'\0';
HostLength-- ;
}
else if ( ( DomainLength == 1 ) &&
( lpBuffer[ HostLength ] == L'.' ) )
{
//
// Legally, the domain name can be a single
// dot '.', indicating that this host is part
// of the root domain. An odd case, to be sure,
// but needs to be handled. Since we've already
// stuck a dot separator in the result string,
// get rid of this one, and adjust the values
// accordingly.
//
lpBuffer[ HostLength ] = L'\0' ;
DomainLength = 0 ;
}
*nSize = HostLength + DomainLength ;
DontSetReturn = TRUE ;
}
else if ( GetLastError() == ERROR_MORE_DATA )
{
//
// Simply add. Note that since both account for a
// null terminator, the '.' that goes between them is
// covered.
//
*nSize = HostLength + DomainLength ;
Status = STATUS_BUFFER_OVERFLOW ;
DontSetReturn = FALSE ;
}
else
{
//
// Other error from trying to get the DNS Domain name.
// Let the error from the call trickle back.
//
*nSize = 0 ;
Status = STATUS_UNSUCCESSFUL ;
DontSetReturn = TRUE ;
}
}
else if ( GetLastError() == ERROR_MORE_DATA )
{
DomainLength = 0;
GetComputerNameExW( ComputerNameDnsDomain, NULL, &DomainLength );
if ( GetLastError() == ERROR_MORE_DATA )
{
//
// Simply add. Note that since both account for a
// null terminator, the '.' that goes between them is
// covered.
//
*nSize = HostLength + DomainLength ;
Status = STATUS_BUFFER_OVERFLOW ;
DontSetReturn = FALSE ;
}
}
else
{
//
// Other error from trying to get the DNS Hostname.
// Let the error from the call trickle back.
//
*nSize = 0 ;
Status = STATUS_UNSUCCESSFUL ;
DontSetReturn = TRUE ;
}
}
break;
}
if ( !NT_SUCCESS( Status ) )
{
if ( !DontSetReturn )
{
BaseSetLastNTError( Status );
}
return FALSE ;
}
return TRUE ;
}
BOOL
WINAPI
SetComputerNameExW(
IN COMPUTER_NAME_FORMAT NameType,
IN LPCWSTR lpBuffer
)
/*++
Routine Description:
This sets what the computername will be when the system is next booted. This
does not effect the active computername for the remainder of this boot, nor
what is returned by GetComputerName before the next system boot.
Arguments:
NameType - Name to set for the system
lpComputerName - points to the buffer that is contains the
null-terminated character string containing the computer name.
Return Value:
Returns TRUE on success, FALSE on failure.
--*/
{
ULONG Length ;
//
// Validate name:
//
if ( !lpBuffer )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE ;
}
Length = wcslen( lpBuffer );
if ( Length )
{
if ( ( lpBuffer[0] == L' ') ||
( lpBuffer[ Length - 1 ] == L' ' ) )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE ;
}
}
if (wcscspn(lpBuffer, ILLEGAL_NAME_CHARS_STR) < Length) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
switch ( NameType )
{
case ComputerNamePhysicalNetBIOS:
return BaseSetNetbiosName( lpBuffer );
case ComputerNamePhysicalDnsHostname:
return BaseSetDnsName( lpBuffer );
case ComputerNamePhysicalDnsDomain:
return BaseSetDnsDomain( lpBuffer );
default:
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE ;
}
}
//
// ANSI APIs
//
BOOL
WINAPI
GetComputerNameA (
LPSTR lpBuffer,
LPDWORD nSize
)
/*++
Routine Description:
This returns the active computername. This is the computername when the
system was last booted. If this is changed (via SetComputerName) it does
not take effect until the next system boot.
Arguments:
lpBuffer - Points to the buffer that is to receive the
null-terminated character string containing the computer name.
nSize - Specifies the maximum size (in characters) of the buffer. This
value should be set to at least MAX_COMPUTERNAME_LENGTH to allow
sufficient room in the buffer for the computer name. The length of
the string is returned in nSize.
Return Value:
TRUE on success, FALSE on failure.
--*/
{
UNICODE_STRING UnicodeString;
ANSI_STRING AnsiString;
LPWSTR UnicodeBuffer;
ULONG AnsiSize;
ULONG UnicodeSize;
//
// Work buffer needs to be twice the size of the user's buffer
//
UnicodeBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), *nSize * sizeof(WCHAR));
if (!UnicodeBuffer) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
//
// Set up an ANSI_STRING that points to the user's buffer
//
AnsiString.MaximumLength = (USHORT) *nSize;
AnsiString.Length = 0;
AnsiString.Buffer = lpBuffer;
//
// Call the UNICODE version to do the work
//
UnicodeSize = *nSize ;
if (!GetComputerNameW(UnicodeBuffer, &UnicodeSize)) {
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
return(FALSE);
}
//
// Find out the required size of the ANSI buffer and validate it against
// the passed in buffer size
//
RtlInitUnicodeString(&UnicodeString, UnicodeBuffer);
AnsiSize = RtlUnicodeStringToAnsiSize(&UnicodeString);
if (AnsiSize > *nSize) {
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
BaseSetLastNTError( STATUS_BUFFER_OVERFLOW );
*nSize = AnsiSize + 1 ;
return(FALSE);
}
//
// Now convert back to ANSI for the caller
//
RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
*nSize = AnsiString.Length;
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
return(TRUE);
}
BOOL
WINAPI
SetComputerNameA (
LPCSTR lpComputerName
)
/*++
Routine Description:
This sets what the computername will be when the system is next booted. This
does not effect the active computername for the remainder of this boot, nor
what is returned by GetComputerName before the next system boot.
Arguments:
lpComputerName - points to the buffer that is contains the
null-terminated character string containing the computer name.
Return Value:
Returns TRUE on success, FALSE on failure.
--*/
{
NTSTATUS NtStatus;
BOOL ReturnValue;
UNICODE_STRING UnicodeString;
ANSI_STRING AnsiString;
ULONG ComputerNameLength;
//
// Validate that the supplied computername is valid (not too long,
// no incorrect characters, no leading or trailing spaces)
//
ComputerNameLength = strlen(lpComputerName);
if ((ComputerNameLength == 0 )||(ComputerNameLength > MAX_COMPUTERNAME_LENGTH)) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
RtlInitAnsiString(&AnsiString, lpComputerName);
NtStatus = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString,
TRUE);
if (!NT_SUCCESS(NtStatus)) {
BaseSetLastNTError(NtStatus);
return(FALSE);
}
ReturnValue = SetComputerNameW((LPCWSTR)UnicodeString.Buffer);
RtlFreeUnicodeString(&UnicodeString);
return(ReturnValue);
}
BOOL
WINAPI
GetComputerNameExA(
IN COMPUTER_NAME_FORMAT NameType,
OUT LPSTR lpBuffer,
IN OUT LPDWORD nSize
)
/*++
Routine Description:
This returns the active computername in a particular format. This is the
computername when the system was last booted. If this is changed (via
SetComputerName) it does not take effect until the next system boot.
Arguments:
NameType - Possible name formats to return the computer name in:
ComputerNameNetBIOS - netbios name (compatible with GetComputerName)
ComputerNameDnsHostname - DNS host name
ComputerNameDnsDomain - DNS Domain name
ComputerNameDnsFullyQualified - DNS Fully Qualified (hostname.dnsdomain)
lpBuffer - Points to the buffer that is to receive the
null-terminated character string containing the computer name.
nSize - Specifies the maximum size (in characters) of the buffer. This
value should be set to at least MAX_COMPUTERNAME_LENGTH + 1 to allow
sufficient room in the buffer for the computer name. The length
of the string is returned in nSize.
Return Value:
TRUE on success, FALSE on failure.
--*/
{
LPWSTR UnicodeBuffer;
//
// Validate Input
//
if ((nSize==NULL) || ((lpBuffer==NULL) && (*nSize>0))) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// Work buffer needs to be twice the size of the user's buffer
//
UnicodeBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), *nSize * sizeof(WCHAR));
if (!UnicodeBuffer) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
//
// Call the UNICODE version to do the work
//
if ( !GetComputerNameExW(NameType, UnicodeBuffer, nSize) ) {
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
return(FALSE);
}
//
// Now convert back to ANSI for the caller
// Note: Since we passed the above if statement,
// GetComputerNameExW succeeded, and set *nSize to the number of
// characters in the string (like wcslen). We need to convert
// all these characters and the trailing NULL, so inc *nSize for
// the conversion call.
//
WideCharToMultiByte(CP_ACP,
0,
UnicodeBuffer,
*nSize+1,
lpBuffer,
(*nSize+1) * sizeof(CHAR),
NULL,
NULL);
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
return(TRUE);
}
BOOL
WINAPI
SetComputerNameExA(
IN COMPUTER_NAME_FORMAT NameType,
IN LPCSTR lpBuffer
)
/*++
Routine Description:
This sets what the computername will be when the system is next booted. This
does not effect the active computername for the remainder of this boot, nor
what is returned by GetComputerName before the next system boot.
Arguments:
NameType - Name to set for the system
lpComputerName - points to the buffer that is contains the
null-terminated character string containing the computer name.
Return Value:
Returns TRUE on success, FALSE on failure.
--*/
{
NTSTATUS NtStatus;
BOOL ReturnValue;
UNICODE_STRING UnicodeString;
ANSI_STRING AnsiString;
RtlInitAnsiString(&AnsiString, lpBuffer);
NtStatus = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString,
TRUE);
if (!NT_SUCCESS(NtStatus)) {
BaseSetLastNTError(NtStatus);
return(FALSE);
}
ReturnValue = SetComputerNameExW(NameType, (LPCWSTR)UnicodeString.Buffer );
RtlFreeUnicodeString(&UnicodeString);
return(ReturnValue);
}
DWORD
WINAPI
AddLocalAlternateComputerNameW(
LPCWSTR lpDnsFQHostname,
ULONG ulFlags
)
/*++
Routine Description:
This sets an alternate computer name for the computer to begin to
respond to.
Arguments:
lpDnsFQHostname - The alternate name to add (in ComputerNameDnsFullyQualified Format)
ulFlags - TBD
Return Value:
Returns ERROR
--*/
{
NTSTATUS status = STATUS_SUCCESS;
DWORD err = ERROR_SUCCESS;
LPWSTR lpNetBiosCompName = NULL;
ULONG ulNetBiosCompNameSize = 0;
LPWSTR lpHostname = BaseParseDnsName(lpDnsFQHostname,DNS_HOSTNAME);
//
// validate input
//
if ((lpDnsFQHostname==NULL) || (!BaseValidateFlags(ulFlags)) || (!BaseValidateDnsNames(lpHostname))) {
if (lpHostname) {
RtlFreeHeap(RtlProcessHeap(), 0, lpHostname);
}
return ERROR_INVALID_PARAMETER;
}
// get write lock?
status = BaseAddMultiNameInReg(
DNSCACHE_ROOT,
DNS_ALT_HOSTNAME,
lpDnsFQHostname);
err = RtlNtStatusToDosError(status);
if (err==ERROR_SUCCESS) {
// get NetBios name (use DNSHostNameToComputerNameW) and add that to reg for OptionalNames
if (!DnsHostnameToComputerNameW(
lpDnsFQHostname,
NULL,
&ulNetBiosCompNameSize)) {
err = GetLastError();
}
if (err==ERROR_MORE_DATA) {
// bug in DNSHostname, returns a size 1 character too small (forgets null)
// update when bug is fixed...
ulNetBiosCompNameSize += 1;
lpNetBiosCompName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), ulNetBiosCompNameSize * sizeof(WCHAR));
if (lpNetBiosCompName==NULL) {
err = ERROR_NOT_ENOUGH_MEMORY;
}
else {
if (!DnsHostnameToComputerNameW(lpDnsFQHostname,
lpNetBiosCompName,
&ulNetBiosCompNameSize)) {
err = GetLastError();
}
else {
if (!BaseSetAltNetBiosName(lpNetBiosCompName)) {
err = GetLastError();
}
}
RtlFreeHeap(RtlProcessHeap(), 0, lpNetBiosCompName);
}
}
if (err!=ERROR_SUCCESS) {
// remove multi name in reg
// rollback?
}
}
if (lpHostname) {
RtlFreeHeap(RtlProcessHeap(), 0, lpHostname);
}
// release write lock?
return RtlNtStatusToDosError(status);
}
DWORD
WINAPI
AddLocalAlternateComputerNameA(
LPCSTR lpDnsFQHostname,
ULONG ulFlags
)
{
LPWSTR lpDnsFQHostnameW = NULL;
DWORD err = ERROR_SUCCESS;
if (lpDnsFQHostname==NULL) {
return ERROR_INVALID_PARAMETER;
}
err = BaseMultiByteToWideCharWithAlloc(lpDnsFQHostname, &lpDnsFQHostnameW);
if (err==ERROR_SUCCESS) {
err = AddLocalAlternateComputerNameW(lpDnsFQHostnameW, ulFlags);
}
BaseConvertCharFree((VOID *)lpDnsFQHostnameW);
return err;
}
DWORD
WINAPI
RemoveLocalAlternateComputerNameW(
LPCWSTR lpAltDnsFQHostname,
ULONG ulFlags
)
/*++
Routine Description:
Remove an alternate computer name.
Arguments:
lpAltDnsFQHostname - The alternate name to remove(in ComputerNameDnsFullyQualified Format)
ulFlags - TBD
Return Value:
Returns ERROR
--*/
{
DWORD err = ERROR_SUCCESS;
NTSTATUS NtStatus = STATUS_SUCCESS;
LPWSTR lpAltNetBiosCompName = NULL;
ULONG cchAltNetBiosCompName = 0;
if ((!BaseValidateFlags(ulFlags)) || (lpAltDnsFQHostname==NULL)) {
return ERROR_INVALID_PARAMETER;
}
// aquire a write lock?
NtStatus = BaseRemoveMultiNameFromReg(DNSCACHE_ROOT, DNS_ALT_HOSTNAME, lpAltDnsFQHostname);
err = RtlNtStatusToDosError(NtStatus);
if (err==ERROR_SUCCESS) {
if (!DnsHostnameToComputerNameW(
lpAltDnsFQHostname,
NULL,
&cchAltNetBiosCompName)) {
err = GetLastError();
}
if (err==ERROR_MORE_DATA) {
// bug in DNSHostname, returns a size 1 character too small (forgets null)
cchAltNetBiosCompName += 1;
lpAltNetBiosCompName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), cchAltNetBiosCompName * sizeof(WCHAR));
if (lpAltNetBiosCompName==NULL) {
err = ERROR_NOT_ENOUGH_MEMORY;
}
else {
err = ERROR_SUCCESS;
if (!DnsHostnameToComputerNameW(lpAltDnsFQHostname,
lpAltNetBiosCompName,
&cchAltNetBiosCompName)) {
err = GetLastError();
} else if (BaseIsNetBiosNameInUse(lpAltNetBiosCompName)) {
// do nothing, this name is still being used by another AltDnsHostname ...
} else if (!BaseRemoveAltNetBiosName(lpAltNetBiosCompName)) {
err = GetLastError();
}
RtlFreeHeap(RtlProcessHeap(), 0, lpAltNetBiosCompName);
}
}
}
// release write lock?
return err;
}
DWORD
WINAPI
RemoveLocalAlternateComputerNameA(
LPCSTR lpAltDnsFQHostname,
ULONG ulFlags
)
{
LPWSTR lpAltDnsFQHostnameW = NULL;
DWORD err = ERROR_SUCCESS;
if (lpAltDnsFQHostname==NULL) {
return ERROR_INVALID_PARAMETER;
}
err = BaseMultiByteToWideCharWithAlloc(lpAltDnsFQHostname, &lpAltDnsFQHostnameW);
if (err==ERROR_SUCCESS) {
err = RemoveLocalAlternateComputerNameW(lpAltDnsFQHostnameW, ulFlags);
}
BaseConvertCharFree((VOID *)lpAltDnsFQHostnameW);
return err;
}
DWORD
WINAPI
SetLocalPrimaryComputerNameW(
LPCWSTR lpAltDnsFQHostname,
ULONG ulFlags
)
/*++
Routine Description:
Set the computer name to the inputed altCompName
Arguments:
lpAltDnsFQHostname - The name to set the computer to (in ComputerNameDnsFullyQualified Format)
ulFlags - TBD
Return Value:
Returns ERROR
--*/
{
DWORD err = ERROR_SUCCESS;
ULONG cchNetBiosName = 0;
LPWSTR lpNetBiosName = NULL;
ULONG cchCompName = 0;
LPWSTR lpCompName = NULL;
LPWSTR lpHostname = BaseParseDnsName(lpAltDnsFQHostname, DNS_HOSTNAME);
LPWSTR lpDomainName = BaseParseDnsName(lpAltDnsFQHostname, DNS_DOMAINNAME);
if ((lpAltDnsFQHostname==NULL) || (!BaseValidateFlags(ulFlags))) {
return ERROR_INVALID_PARAMETER;
}
// aquire a write lock?
// check to see that the given name is a valid alternate dns hostname
if (!BaseIsAltDnsFQHostname(lpAltDnsFQHostname)) {
if (lpHostname) {
RtlFreeHeap(RtlProcessHeap(), 0, lpHostname);
}
if (lpDomainName) {
RtlFreeHeap(RtlProcessHeap(), 0, lpDomainName);
}
return ERROR_INVALID_PARAMETER;
}
// get the current net bios name and add it to the alternate names
if (!GetComputerNameExW(ComputerNamePhysicalNetBIOS, NULL, &cchNetBiosName)) {
err = GetLastError();
}
if (err==ERROR_MORE_DATA) {
lpNetBiosName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), cchNetBiosName*sizeof(WCHAR));
if (lpNetBiosName==NULL) {
err = ERROR_NOT_ENOUGH_MEMORY;
}
else if (!GetComputerNameExW(ComputerNamePhysicalNetBIOS, lpNetBiosName, &cchNetBiosName)) {
err = GetLastError();
}
else if (!BaseSetAltNetBiosName(lpNetBiosName)) {
err = GetLastError();
}
else {
err = ERROR_SUCCESS;
}
if (lpNetBiosName) {
RtlFreeHeap(RtlProcessHeap(), 0, lpNetBiosName);
}
}
if (err==ERROR_SUCCESS) {
// add the physical dnsname to the list of alternate hostnames...
if (!GetComputerNameExW(ComputerNamePhysicalDnsFullyQualified, NULL, &cchCompName)) {
err = GetLastError();
}
if (err==ERROR_MORE_DATA) {
lpCompName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), cchCompName*sizeof(WCHAR));
if (lpCompName==NULL) {
err = ERROR_NOT_ENOUGH_MEMORY;
}
else if (!GetComputerNameExW(ComputerNamePhysicalDnsFullyQualified, lpCompName, &cchCompName)) {
err = GetLastError();
}
else if (!BaseSetAltDnsFQHostname(lpCompName)) {
err = GetLastError();
}
else {
err = ERROR_SUCCESS;
}
if (lpCompName) {
RtlFreeHeap(RtlProcessHeap(), 0, lpCompName);
}
}
}
// set the new physical dns hostname
if (err==ERROR_SUCCESS) {
if (!SetComputerNameExW(ComputerNamePhysicalDnsHostname, lpHostname)) {
err = GetLastError();
}
}
if (err==ERROR_SUCCESS) {
if (!SetComputerNameExW(ComputerNamePhysicalDnsDomain, lpDomainName)) {
err = GetLastError();
}
}
// remove the alternate name (now primary) from the alternate lists
if (err==ERROR_SUCCESS) {
err = RemoveLocalAlternateComputerNameW(lpAltDnsFQHostname, 0);
}
if (lpHostname) {
RtlFreeHeap(RtlProcessHeap(), 0, lpHostname);
}
if (lpDomainName) {
RtlFreeHeap(RtlProcessHeap(), 0, lpDomainName);
}
// release write lock?
return err;
}
DWORD
WINAPI
SetLocalPrimaryComputerNameA(
LPCSTR lpAltDnsFQHostname,
ULONG ulFlags
)
{
LPWSTR lpAltDnsFQHostnameW = NULL;
DWORD err = ERROR_SUCCESS;
if (lpAltDnsFQHostname==NULL) {
return ERROR_INVALID_PARAMETER;
}
err = BaseMultiByteToWideCharWithAlloc(lpAltDnsFQHostname, &lpAltDnsFQHostnameW);
if (err == ERROR_SUCCESS) {
err = SetLocalPrimaryComputerNameW(lpAltDnsFQHostnameW, ulFlags);
}
BaseConvertCharFree((VOID *)lpAltDnsFQHostnameW);
return err;
}
DWORD
WINAPI
EnumerateLocalComputerNamesW(
COMPUTER_NAME_TYPE NameType,
ULONG ulFlags,
LPWSTR lpDnsFQHostnames,
LPDWORD nSize
)
/*++
Routine Description:
Returns the value of the computer's names requested.
Arguments:
NameType - Which of the computer's names are requested
PrimaryComputerName - Similar to GetComputerEx(ComputerNamePhysicalNetBios, ...
AlternateComputerNames - All known alt names
AllComputerNames - All of the above
ulFlags - TBD
lpBuffer - Buffer to hold returned names concatenated together, and trailed with a NULL
nSize - Size of buffer to hold returned names.
Return Value:
Returns ERROR
--*/
{
DWORD err = ERROR_SUCCESS;
DWORD SizePrimary = 0;
DWORD SizeAlternate = 0;
LPWSTR lpTempCompNames = NULL;
if ((!BaseValidateFlags(ulFlags)) || (NameType>=ComputerNameTypeMax) || (NameType<PrimaryComputerName)) {
return ERROR_INVALID_PARAMETER;
}
// get read lock?
switch(NameType) {
case PrimaryComputerName:
if (!GetComputerNameExW(ComputerNamePhysicalDnsFullyQualified, lpDnsFQHostnames, nSize)) {
err = GetLastError();
}
break;
case AlternateComputerNames:
if ((nSize==NULL) || ((lpDnsFQHostnames==NULL) && (*nSize>0))) {
err = ERROR_INVALID_PARAMETER;
}
else {
err = BaseEnumAltDnsFQHostnames(lpDnsFQHostnames, nSize);
}
break;
case AllComputerNames:
if ((nSize==NULL) || ((lpDnsFQHostnames==NULL) && (*nSize>0))) {
err = ERROR_INVALID_PARAMETER;
}
else {
SizePrimary = *nSize;
lpTempCompNames = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), *nSize * sizeof(WCHAR));
if (lpTempCompNames==NULL) {
err = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Get primary name
if (!GetComputerNameExW(ComputerNamePhysicalDnsFullyQualified, lpTempCompNames, &SizePrimary)) {
err = GetLastError();
}
// on success, holds the number of characters copied into lpTempCompNames NOT counting NULL
// on failure, holds the space needed to copy in, (num characters PLUS NULL)
if (err==ERROR_SUCCESS) {
SizeAlternate = *nSize - (SizePrimary + 1);
err = BaseEnumAltDnsFQHostnames(lpTempCompNames+SizePrimary+1, &SizeAlternate);
*nSize = SizePrimary + 1 + SizeAlternate;
if (err==ERROR_SUCCESS) {
memcpy(lpDnsFQHostnames, lpTempCompNames, (*nSize+1)*sizeof(WCHAR));
}
}
else if (err==ERROR_MORE_DATA) {
// return total size required
SizeAlternate = 0;
err = BaseEnumAltDnsFQHostnames(NULL, &SizeAlternate);
if (err==ERROR_SUCCESS) {
// no alt names exist, keep ERROR_MORE_DATA to return to client
err = ERROR_MORE_DATA;
}
*nSize = SizePrimary + SizeAlternate;
}
RtlFreeHeap(RtlProcessHeap(), 0, lpTempCompNames);
}
break;
default:
err = ERROR_INVALID_PARAMETER;
break;
}
// release read lock?
return err;
}
DWORD
WINAPI
EnumerateLocalComputerNamesA(
COMPUTER_NAME_TYPE NameType,
ULONG ulFlags,
LPSTR lpDnsFQHostnames,
LPDWORD nSize
)
{
DWORD err = ERROR_SUCCESS;
LPWSTR lpDnsFQHostnamesW = NULL;
//
// Validate Input
//
if ((nSize==NULL) || ((lpDnsFQHostnames==NULL) && (*nSize>0))) {
return ERROR_INVALID_PARAMETER;
}
if (lpDnsFQHostnames!=NULL) {
lpDnsFQHostnamesW = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), *nSize * sizeof(WCHAR));
if (lpDnsFQHostnamesW==NULL) {
err = ERROR_NOT_ENOUGH_MEMORY;
}
}
if (err==ERROR_SUCCESS) {
err = EnumerateLocalComputerNamesW(NameType, ulFlags, lpDnsFQHostnamesW, nSize);
}
if (err==ERROR_SUCCESS) {
if (!WideCharToMultiByte(CP_ACP, 0, lpDnsFQHostnamesW, *nSize+1,
lpDnsFQHostnames, (*nSize+1)* sizeof(CHAR), NULL, NULL)) {
err = GetLastError();
}
}
if (lpDnsFQHostnamesW) {
RtlFreeHeap(RtlProcessHeap(), 0, lpDnsFQHostnamesW);
}
return err;
}
BOOL
WINAPI
DnsHostnameToComputerNameW(
IN LPCWSTR Hostname,
OUT LPWSTR ComputerName,
IN OUT LPDWORD nSize)
/*++
Routine Description:
This routine will convert a DNS Hostname to a Win32 Computer Name.
Arguments:
Hostname - DNS Hostname (any length)
ComputerName - Win32 Computer Name (max length of MAX_COMPUTERNAME_LENGTH)
nSize - On input, size of the buffer pointed to by ComputerName. On output,
size of the Computer Name, in characters.
Return Value:
Returns TRUE on success, FALSE on failure.
--*/
{
WCHAR CompName[ MAX_COMPUTERNAME_LENGTH + 1 ];
DWORD Size = MAX_COMPUTERNAME_LENGTH + 1 ;
UNICODE_STRING CompName_U ;
UNICODE_STRING Hostname_U ;
NTSTATUS Status ;
BOOL Ret ;
CompName[0] = L'\0';
CompName_U.Buffer = CompName ;
CompName_U.Length = 0 ;
CompName_U.MaximumLength = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof( WCHAR );
RtlInitUnicodeString( &Hostname_U, Hostname );
Status = RtlDnsHostNameToComputerName( &CompName_U,
&Hostname_U,
FALSE );
if ( NT_SUCCESS( Status ) )
{
if ( *nSize >= CompName_U.Length / sizeof(WCHAR) + 1 )
{
RtlCopyMemory( ComputerName,
CompName_U.Buffer,
CompName_U.Length );
ComputerName[ CompName_U.Length / sizeof( WCHAR ) ] = L'\0';
Ret = TRUE ;
}
else
{
BaseSetLastNTError( STATUS_BUFFER_OVERFLOW );
Ret = FALSE ;
}
//
// returns the count of characters
//
*nSize = CompName_U.Length / sizeof( WCHAR );
}
else
{
BaseSetLastNTError( Status );
Ret = FALSE ;
}
return Ret ;
}
BOOL
WINAPI
DnsHostnameToComputerNameA(
IN LPCSTR Hostname,
OUT LPSTR ComputerName,
IN OUT LPDWORD nSize)
/*++
Routine Description:
This routine will convert a DNS Hostname to a Win32 Computer Name.
Arguments:
Hostname - DNS Hostname (any length)
ComputerName - Win32 Computer Name (max length of MAX_COMPUTERNAME_LENGTH)
nSize - On input, size of the buffer pointed to by ComputerName. On output,
size of the Computer Name, in characters.
Return Value:
Returns TRUE on success, FALSE on failure.
--*/
{
WCHAR CompName[ MAX_COMPUTERNAME_LENGTH + 1 ];
DWORD Size = MAX_COMPUTERNAME_LENGTH + 1;
BOOL Ret ;
UNICODE_STRING CompName_U ;
UNICODE_STRING Hostname_U ;
NTSTATUS Status ;
ANSI_STRING CompName_A ;
Status = RtlCreateUnicodeStringFromAsciiz( &Hostname_U,
Hostname );
if ( NT_SUCCESS( Status ) )
{
CompName[0] = L'\0';
CompName_U.Buffer = CompName ;
CompName_U.Length = 0 ;
CompName_U.MaximumLength = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof( WCHAR );
Status = RtlDnsHostNameToComputerName( &CompName_U,
&Hostname_U,
FALSE );
if ( NT_SUCCESS( Status ) )
{
CompName_A.Buffer = ComputerName ;
CompName_A.Length = 0 ;
CompName_A.MaximumLength = (USHORT) *nSize ;
Status = RtlUnicodeStringToAnsiString( &CompName_A, &CompName_U, FALSE );
if ( NT_SUCCESS( Status ) )
{
*nSize = CompName_A.Length ;
}
}
}
if ( !NT_SUCCESS( Status ) )
{
BaseSetLastNTError( Status );
return FALSE ;
}
return TRUE ;
}
#include "dfsfsctl.h"
DWORD
BasepGetComputerNameFromNtPath (
PUNICODE_STRING NtPathName,
HANDLE hFile,
LPWSTR lpBuffer,
LPDWORD nSize
)
/*++
Routine Description:
Look at a path and determine the computer name of the host machine.
In the future, we should remove this code, and add the capbility to query
handles for their computer name.
The name can only be obtained for NetBios paths - if the path is IP or DNS
an error is returned. (If the NetBios name has a "." in it, it will
cause an error because it will be misinterpreted as a DNS path. This case
becomes less and less likely as the NT5 UI doesn't allow such computer names.)
For DFS paths, the leaf server's name is returned, as long as it wasn't
joined to its parent with an IP or DNS path name.
Arguments:
NtPathName - points to a unicode string with the path to query.
lpBuffer - points to buffer receives the computer name
nSize - points to dword with the size of the input buffer, and the length
(in characters, not including the null terminator) of the computer name
on output.
Return Value:
A Win32 error code.
--*/
{
ULONG cbComputer = 0;
DWORD dwError = ERROR_BAD_PATHNAME;
ULONG AvailableLength = 0;
PWCHAR PathCharacter = NULL;
BOOL CheckForDfs = TRUE;
NTSTATUS NtStatus = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
WCHAR FileNameInfoBuffer[MAX_PATH+sizeof(FILE_NAME_INFORMATION)];
PFILE_NAME_INFORMATION FileNameInfo = (PFILE_NAME_INFORMATION)FileNameInfoBuffer;
WCHAR DfsServerPathName[ MAX_PATH + 1 ];
WCHAR DosDevice[3] = { L"A:" };
WCHAR DosDeviceMapping[ MAX_PATH + 1 ];
UNICODE_STRING UnicodeComputerName;
const UNICODE_STRING NtUncPathNamePrefix = { 16, 18, L"\\??\\UNC\\"};
const ULONG cchNtUncPathNamePrefix = 8;
const UNICODE_STRING NtDrivePathNamePrefix = { 8, 10, L"\\??\\" };
const ULONG cchNtDrivePathNamePrefix = 4;
RtlInitUnicodeString( &UnicodeComputerName, NULL );
// Is this a UNC path?
if( RtlPrefixString( (PSTRING)&NtUncPathNamePrefix, (PSTRING)NtPathName, TRUE )) {
// Make sure there's some more to this path than just the prefix
if( NtPathName->Length <= NtUncPathNamePrefix.Length )
goto Exit;
// It appears to be a valid UNC path. Point to the beginning of the computer
// name, and calculate how much room is left in NtPathName after that.
UnicodeComputerName.Buffer = &NtPathName->Buffer[ NtUncPathNamePrefix.Length/sizeof(WCHAR) ];
AvailableLength = NtPathName->Length - NtUncPathNamePrefix.Length;
}
// If it's not a UNC path, then is it a drive-letter path?
else if( RtlPrefixString( (PSTRING)&NtDrivePathNamePrefix, (PSTRING)NtPathName, TRUE )
&&
NtPathName->Buffer[ cchNtDrivePathNamePrefix + 1 ] == L':' ) {
// It's a drive letter path, but it could still be local or remote
static const WCHAR RedirectorMappingPrefix[] = { L"\\Device\\LanmanRedirector\\;" };
static const WCHAR LocalVolumeMappingPrefix[] = { L"\\Device\\Harddisk" };
static const WCHAR CDRomMappingPrefix[] = { L"\\Device\\CdRom" };
static const WCHAR FloppyMappingPrefix[] = { L"\\Device\\Floppy" };
static const WCHAR DfsMappingPrefix[] = { L"\\Device\\WinDfs\\" };
// Get the correct, upper-cased, drive letter into DosDevice.
DosDevice[0] = NtPathName->Buffer[ cchNtDrivePathNamePrefix ];
if( L'a' <= DosDevice[0] && DosDevice[0] <= L'z' )
DosDevice[0] = L'A' + (DosDevice[0] - L'a');
// Map the drive letter to its symbolic link under \??. E.g., say C:, D: & R:
// are local/DFS/rdr drives, respectively. You would then see something like:
//
// C: => \Device\Volume1
// D: => \Device\WinDfs\G
// R: => \Device\LanmanRedirector\;R:0\scratch\scratch
if( !QueryDosDeviceW( DosDevice, DosDeviceMapping, sizeof(DosDeviceMapping)/sizeof(DosDeviceMapping[0]) )) {
dwError = GetLastError();
goto Exit;
}
// Now that we have the DosDeviceMapping, we can check ... Is this a rdr drive?
if( // Does it begin with "\Device\LanmanRedirector\;" ?
DosDeviceMapping == wcsstr( DosDeviceMapping, RedirectorMappingPrefix )
&&
// Are the next letters the correct drive letter, a colon, and a whack?
( DosDevice[0] == DosDeviceMapping[ sizeof(RedirectorMappingPrefix)/sizeof(WCHAR) - 1 ]
&&
L':' == DosDeviceMapping[ sizeof(RedirectorMappingPrefix)/sizeof(WCHAR) ]
&&
(UnicodeComputerName.Buffer = wcschr(&DosDeviceMapping[ sizeof(RedirectorMappingPrefix)/sizeof(WCHAR) + 1 ], L'\\'))
)) {
// We have a valid rdr drive. Point to the beginning of the computer
// name, and calculate how much room is availble in DosDeviceMapping after that.
UnicodeComputerName.Buffer += 1;
AvailableLength = sizeof(DosDeviceMapping) - sizeof(DosDeviceMapping[0]) * (ULONG)(UnicodeComputerName.Buffer - DosDeviceMapping);
// We know now that it's not a DFS path
CheckForDfs = FALSE;
}
// If it's not a rdr drive, then maybe it's a local volume, floppy, or cdrom
else if( DosDeviceMapping == wcsstr( DosDeviceMapping, LocalVolumeMappingPrefix )
||
DosDeviceMapping == wcsstr( DosDeviceMapping, CDRomMappingPrefix )
||
DosDeviceMapping == wcsstr( DosDeviceMapping, FloppyMappingPrefix ) ) {
// We have a local drive, so just return the local computer name.
CheckForDfs = FALSE;
if( !GetComputerNameW( lpBuffer, nSize))
dwError = GetLastError();
else
dwError = ERROR_SUCCESS;
goto Exit;
}
// Finally, check to see if it's a DFS drive
else if( DosDeviceMapping == wcsstr( DosDeviceMapping, DfsMappingPrefix )) {
// Get the full UNC name of this DFS path. Later, we'll call the DFS
// driver to find out what the actual server name is.
NtStatus = NtQueryInformationFile(
hFile,
&IoStatusBlock,
FileNameInfo,
sizeof(FileNameInfoBuffer),
FileNameInformation
);
if( !NT_SUCCESS(NtStatus) ) {
dwError = RtlNtStatusToDosError(NtStatus);
goto Exit;
}
UnicodeComputerName.Buffer = FileNameInfo->FileName + 1;
AvailableLength = FileNameInfo->FileNameLength;
}
// Otherwise, it's not a rdr, dfs, or local drive, so there's nothing we can do.
else
goto Exit;
} // else if( RtlPrefixString( (PSTRING)&NtDrivePathNamePrefix, (PSTRING)NtPathName, TRUE ) ...
else {
dwError = ERROR_BAD_PATHNAME;
goto Exit;
}
// If we couldn't determine above if whether or not this is a DFS path, let the
// DFS driver decide now.
if( CheckForDfs && INVALID_HANDLE_VALUE != hFile ) {
HANDLE hDFS = INVALID_HANDLE_VALUE;
UNICODE_STRING DfsDriverName;
OBJECT_ATTRIBUTES ObjectAttributes;
WCHAR *DfsPathName = UnicodeComputerName.Buffer - 1; // Back up to the whack
ULONG DfsPathNameLength = AvailableLength + sizeof(WCHAR);
// Open the DFS driver
RtlInitUnicodeString( &DfsDriverName, DFS_DRIVER_NAME );
InitializeObjectAttributes( &ObjectAttributes,
&DfsDriverName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtCreateFile(
&hDFS,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if( !NT_SUCCESS(NtStatus) ) {
dwError = RtlNtStatusToDosError(NtStatus);
goto Exit;
}
// Query DFS's cache for the server name. The name is guaranteed to
// remain in the cache as long as the file is open.
if( L'\\' != DfsPathName[0] ) {
NtClose(hDFS);
dwError = ERROR_BAD_PATHNAME;
goto Exit;
}
NtStatus = NtFsControlFile(
hDFS,
NULL, // Event,
NULL, // ApcRoutine,
NULL, // ApcContext,
&IoStatusBlock,
FSCTL_DFS_GET_SERVER_NAME,
DfsPathName,
DfsPathNameLength,
DfsServerPathName,
sizeof(DfsServerPathName)
);
NtClose( hDFS );
// STATUS_OBJECT_NAME_NOT_FOUND means that it's not a DFS path
if( !NT_SUCCESS(NtStatus) ) {
if( STATUS_OBJECT_NAME_NOT_FOUND != NtStatus ) {
dwError = RtlNtStatusToDosError(NtStatus);
goto Exit;
}
}
else if( L'\0' != DfsServerPathName[0] ) {
// The previous DFS call returns the server-specific path to the file in UNC form.
// Point UnicodeComputerName to just past the two whacks.
AvailableLength = wcslen(DfsServerPathName) * sizeof(WCHAR);
if( 3*sizeof(WCHAR) > AvailableLength
||
L'\\' != DfsServerPathName[0]
||
L'\\' != DfsServerPathName[1] )
{
dwError = ERROR_BAD_PATHNAME;
goto Exit;
}
UnicodeComputerName.Buffer = DfsServerPathName + 2;
AvailableLength -= 2 * sizeof(WCHAR);
}
}
// If we get here, then the computer name\share is pointed to by UnicodeComputerName.Buffer.
// But the Length is currently zero, so we search for the whack that separates
// the computer name from the share, and set the Length to include just the computer name.
PathCharacter = UnicodeComputerName.Buffer;
while( ( (ULONG) ((PCHAR)PathCharacter - (PCHAR)UnicodeComputerName.Buffer) < AvailableLength)
&&
*PathCharacter != L'\\' ) {
// If we found a '.', we fail because this is probably a DNS or IP name.
if( L'.' == *PathCharacter ) {
dwError = ERROR_BAD_PATHNAME;
goto Exit;
}
PathCharacter++;
}
// Set the computer name length
UnicodeComputerName.Length = UnicodeComputerName.MaximumLength
= (USHORT) ((PCHAR)PathCharacter - (PCHAR)UnicodeComputerName.Buffer);
// Fail if the computer name exceeded the length of the input NtPathName,
// or if the length exceeds that allowed.
if( UnicodeComputerName.Length >= AvailableLength
||
UnicodeComputerName.Length > MAX_COMPUTERNAME_LENGTH*sizeof(WCHAR) ) {
goto Exit;
}
// Copy the computer name into the caller's buffer, as long as there's enough
// room for the name & a terminating '\0'.
if( UnicodeComputerName.Length + sizeof(WCHAR) > *nSize * sizeof(WCHAR) ) {
dwError = ERROR_BUFFER_OVERFLOW;
goto Exit;
}
RtlCopyMemory( lpBuffer, UnicodeComputerName.Buffer, UnicodeComputerName.Length );
*nSize = UnicodeComputerName.Length / sizeof(WCHAR);
lpBuffer[ *nSize ] = L'\0';
dwError = ERROR_SUCCESS;
Exit:
return( dwError );
}