/*++ 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 #include 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; (iDataOffset + 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) || (NameType0))) { 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 ); }