Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1841 lines
55 KiB

//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2002 Microsoft Corporation
//
// Module Name:
// NameUtilSrc.cpp
//
// Description:
// Name resolution utility.
//
// Maintained By:
// Galen Barbee (GalenB) 28-NOV-2000
//
//////////////////////////////////////////////////////////////////////////////
#include <initguid.h>
// {6968D735-ADBB-4748-A36E-7CEE0FE21116}
DEFINE_GUID( TASKID_Minor_Multiple_DNS_Records_Found,
0x6968d735, 0xadbb, 0x4748, 0xa3, 0x6e, 0x7c, 0xee, 0xf, 0xe2, 0x11, 0x16);
// {D86FAAD9-2514-451e-B359-435AF35E6038}
DEFINE_GUID( TASKID_Minor_FQDN_DNS_Binding_Succeeded,
0xd86faad9, 0x2514, 0x451e, 0xb3, 0x59, 0x43, 0x5a, 0xf3, 0x5e, 0x60, 0x38);
// {B2359972-F6B8-433d-949B-DB1CEE009321}
DEFINE_GUID( TASKID_Minor_FQDN_DNS_Binding_Failed,
0xb2359972, 0xf6b8, 0x433d, 0x94, 0x9b, 0xdb, 0x1c, 0xee, 0x0, 0x93, 0x21);
// {2FF4B2F0-800C-44db-9131-F60B30F76CB4}
DEFINE_GUID( TASKID_Minor_NETBIOS_Binding_Failed,
0x2ff4b2f0, 0x800c, 0x44db, 0x91, 0x31, 0xf6, 0xb, 0x30, 0xf7, 0x6c, 0xb4);
// {D40532E1-9286-4dbd-A559-B62DCC218929}
DEFINE_GUID( TASKID_Minor_NETBIOS_Binding_Succeeded,
0xd40532e1, 0x9286, 0x4dbd, 0xa5, 0x59, 0xb6, 0x2d, 0xcc, 0x21, 0x89, 0x29);
// {D0AB3284-8F62-4f55-8938-DA6A583604E0}
DEFINE_GUID( TASKID_Minor_NETBIOS_Name_Conversion_Succeeded,
0xd0ab3284, 0x8f62, 0x4f55, 0x89, 0x38, 0xda, 0x6a, 0x58, 0x36, 0x4, 0xe0);
// {66F8E4AA-DF71-4973-A4A3-115EB6FE9986}
DEFINE_GUID( TASKID_Minor_NETBIOS_Name_Conversion_Failed,
0x66f8e4aa, 0xdf71, 0x4973, 0xa4, 0xa3, 0x11, 0x5e, 0xb6, 0xfe, 0x99, 0x86);
// {5F18ED71-07EC-46d3-ADB9-71F1C7794DB2}
DEFINE_GUID( TASKID_Minor_NETBIOS_Reset_Failed,
0x5f18ed71, 0x7ec, 0x46d3, 0xad, 0xb9, 0x71, 0xf1, 0xc7, 0x79, 0x4d, 0xb2);
// {A6DCB5E1-1FDF-4c94-ADBA-EE18F72B8197}
DEFINE_GUID( TASKID_Minor_NETBIOS_LanaEnum_Failed,
0xa6dcb5e1, 0x1fdf, 0x4c94, 0xad, 0xba, 0xee, 0x18, 0xf7, 0x2b, 0x81, 0x97);
// Constants for use by FQName functions.
const WCHAR g_wchIPDomainMarker = L'|';
const WCHAR g_wchDNSDomainMarker = L'.';
const size_t g_cchIPAddressMax = INET_ADDRSTRLEN;
//////////////////////////////////////////////////////////////////////////////
//++
//
// CountDnsRecords
//
// Description:
// Given a list of DNS records, counts the number of records in the list
// having a given type and section.
//
// Arguments:
// pdnsRecordListIn
// Pointer to the first record in the list; can be null, which causes
// a return value of zero.
//
// nTypeIn
// The type of record to count.
//
// dnsSectionIn
// The kind of record section to count.
//
//
// Return Values:
// The number of records having the given type and section,
// or zero if the list is empty.
//
//--
//////////////////////////////////////////////////////////////////////////////
static
UINT
CountDnsRecords(
PDNS_RECORD pdnsRecordListIn
, WORD nTypeIn
, DNS_SECTION dnsSectionIn )
{
UINT cRecords = 0;
PDNS_RECORD pdnsCurrent = pdnsRecordListIn;
while ( pdnsCurrent != NULL )
{
if ( ( pdnsCurrent->wType == nTypeIn )
&& ( (DNS_SECTION) pdnsCurrent->Flags.S.Section == dnsSectionIn ) )
{
cRecords += 1;
}
pdnsCurrent = pdnsCurrent->pNext;
}
return cRecords;
} //*** CountDnsRecords
//////////////////////////////////////////////////////////////////////////////
//++
//
// FindDnsRecord
//
// Description:
// Given a list of DNS records, searches for the first record in the list
// having a given type and section.
//
// Arguments:
// pdnsRecordListIn
// Pointer to the first record in the list; can be null, which causes
// a return value of null.
//
// nTypeIn
// The type of record to find.
//
// dnsSectionIn
// The kind of record section to find.
//
// Return Values:
// A pointer to the first record having the given type and section,
// or null if the list is empty.
//
//--
//////////////////////////////////////////////////////////////////////////////
static
PDNS_RECORD
FindDnsRecord(
PDNS_RECORD pdnsRecordListIn
, WORD nTypeIn
, DNS_SECTION dnsSectionIn )
{
PDNS_RECORD pdnsCurrent = pdnsRecordListIn;
while ( ( pdnsCurrent != NULL )
&& ( ( pdnsCurrent->wType != nTypeIn )
|| ( (DNS_SECTION) pdnsCurrent->Flags.S.Section != dnsSectionIn ) ) )
{
pdnsCurrent = pdnsCurrent->pNext;
}
return pdnsCurrent;
} //*** FindDnsRecord
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrCreateBinding
//
// Description:
// Create a binding string from a name.
//
// Arguments:
// pcccbIn - IClusCfgCallback interface for sending status reports.
// pclsidLogIn - Major task ID for status reports.
// pcwszNameIn - Name (FQDN) to create a binding string for.
// pbstrBindingOut - Binding string created.
//
// Return Values:
// S_OK
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrCreateBinding(
IClusCfgCallback * pcccbIn
, const CLSID * pclsidLogIn
, LPCWSTR pcwszNameIn
, BSTR * pbstrBindingOut
)
{
TraceFunc1( "pcwszNameIn = '%ws'", pcwszNameIn );
HRESULT hr = S_FALSE; // This will always be set by the time we get to Cleanup, so the value doesn't matter.
DNS_STATUS dsDnsStatus;
LPWSTR pszIPAddress = NULL;
PDNS_RECORD pResults = NULL;
BSTR bstrNotification = NULL;
BOOL fFallbackToNetbios = TRUE;
Assert( pcwszNameIn != NULL );
Assert( pbstrBindingOut != NULL );
Assert( *pbstrBindingOut == NULL );
dsDnsStatus = DnsQuery(
pcwszNameIn
, DNS_TYPE_A
, ( DNS_QUERY_STANDARD
| DNS_QUERY_BYPASS_CACHE
| DNS_QUERY_TREAT_AS_FQDN
)
, NULL
, &pResults
, NULL
);
if ( dsDnsStatus == ERROR_SUCCESS )
{
PDNS_RECORD pdnsTypeARecord = FindDnsRecord( pResults, DNS_TYPE_A, DnsSectionAnswer );
if ( pdnsTypeARecord != NULL )
{
ULONG ulIPAddress = pdnsTypeARecord->Data.A.IpAddress;
DWORD scConversion = ERROR_SUCCESS;
//
// Send a warning to the UI if there is more than one DNS record.
//
if ( CountDnsRecords( pResults, DNS_TYPE_A, DnsSectionAnswer ) > 1 )
{
if ( pcccbIn != NULL )
{
THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_MULTIPLE_DNS_RECORDS_FOUND, &bstrNotification, pcwszNameIn ) );
hr = THR( pcccbIn->SendStatusReport( pcwszNameIn,
*pclsidLogIn,
TASKID_Minor_Multiple_DNS_Records_Found,
1,
1,
1,
S_FALSE,
bstrNotification,
NULL,
NULL
) );
// ignore error
}
} // if: more than one result returned.
//
// Convert the IP address to a string.
//
scConversion = TW32( ClRtlTcpipAddressToString( ulIPAddress, &pszIPAddress ) );
if ( scConversion != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( scConversion );
goto Cleanup;
}
TraceMemoryAddLocalAddress( pszIPAddress );
*pbstrBindingOut = TraceSysAllocString( pszIPAddress );
if ( *pbstrBindingOut == NULL )
{
hr = THR( E_OUTOFMEMORY );
goto Cleanup;
}
//
// Indicate we were successful in the UI.
//
if ( pcccbIn != NULL )
{
THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_FQDN_DNS_BINDING_SUCCEEDED, &bstrNotification, pcwszNameIn, *pbstrBindingOut ) );
hr = THR( pcccbIn->SendStatusReport( pcwszNameIn,
*pclsidLogIn,
TASKID_Minor_FQDN_DNS_Binding_Succeeded,
1,
1,
1,
S_OK,
bstrNotification,
NULL,
NULL
) );
} // if: IClusCfgCallback interface available
else
{
hr = S_OK;
}
fFallbackToNetbios = FALSE;
} // if type A dns record found
} // if: DnsQuery() succeeded
if ( fFallbackToNetbios )
{
//
// If there were any failures in the call to DnsQuery, fall back to
// performing a NetBIOS name resolution.
//
if ( pcccbIn != NULL )
{
THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_FQDN_DNS_BINDING_FAILED, &bstrNotification, pcwszNameIn ) );
hr = THR( pcccbIn->SendStatusReport( pcwszNameIn,
TASKID_Major_Client_And_Server_Log,
TASKID_Minor_FQDN_DNS_Binding_Failed,
1,
1,
1,
MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_WIN32, dsDnsStatus ),
bstrNotification,
NULL,
NULL
) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
} // if: IClusCfgCallback interface available
//
// Try to resolve the name with NetBIOS.
//
hr = THR( HrGetNetBIOSBinding( pcccbIn, pclsidLogIn, pcwszNameIn, pbstrBindingOut ) );
if ( hr != S_OK ) // Non-S_OK success codes are actually failures.
{
//
// If all else fails, use the name and attempt to bind to it.
//
*pbstrBindingOut = TraceSysAllocString( pcwszNameIn );
if ( *pbstrBindingOut == NULL )
{
hr = THR( E_OUTOFMEMORY );
goto Cleanup;
}
hr = S_FALSE;
goto Cleanup;
} // if: NetBIOS name resolution failed
} // else if: no DNS server or no DNS name
Cleanup:
#ifdef DEBUG
if ( FAILED( hr ) )
{
Assert( *pbstrBindingOut == NULL );
}
#endif
TraceSysFreeString( bstrNotification );
TraceLocalFree( pszIPAddress );
if ( pResults != NULL )
{
DnsRecordListFree( pResults, DnsFreeRecordListDeep );
}
HRETURN( hr );
} //*** HrCreateBinding
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrGetNetBIOSBinding
//
// Description:
// Get the IP address for a name from NetBIOS.
//
// Arguments:
// pcccbIn - IClusCfgCallback interface for sending status reports.
// pclsidLogIn - Major task ID for status reports.
// pcwszNameIn - Name (FQDN) to create a binding string for.
// pbstrBindingOut - Binding string created.
//
// Return Values:
// S_OK - The operation completed successfully.
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrGetNetBIOSBinding(
IClusCfgCallback * pcccbIn
, const CLSID * pclsidLogIn
, LPCWSTR pcwszNameIn
, BSTR * pbstrBindingOut
)
{
TraceFunc1( "pcwszNameIn = '%ws'", pcwszNameIn );
HRESULT hr = S_OK;
DWORD cch;
BOOL fSuccess;
WCHAR szNetBIOSName[ MAX_COMPUTERNAME_LENGTH + 1 ];
NCB ncb;
UCHAR rguchNcbCallName[ RTL_NUMBER_OF( ncb.ncb_callname ) ];
UCHAR rguchNameBuffer[ sizeof( FIND_NAME_HEADER ) + sizeof( FIND_NAME_BUFFER ) ];
LANA_ENUM leLanaEnum;
UCHAR idx;
size_t idxNcbCallname;
BSTR bstrNotification = NULL;
LPWSTR pszIPAddress = NULL;
FIND_NAME_HEADER * pfnh = (FIND_NAME_HEADER *) &rguchNameBuffer[ 0 ];
FIND_NAME_BUFFER * pfnb = (FIND_NAME_BUFFER *) &rguchNameBuffer[ sizeof( FIND_NAME_HEADER ) ];
Assert( pcwszNameIn != NULL );
Assert( pbstrBindingOut != NULL );
Assert( *pbstrBindingOut == NULL );
//
// Convert the DNS hostname to a computername (e.g. NetBIOS name).
//
cch = ARRAYSIZE( szNetBIOSName );
Assert( cch == MAX_COMPUTERNAME_LENGTH + 1 );
fSuccess = DnsHostnameToComputerName( pcwszNameIn, szNetBIOSName, &cch );
if ( fSuccess == FALSE )
{
hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_WIN32, TW32( GetLastError() ) );
if ( pcccbIn != NULL )
{
THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_NAME_CONVERSION_FAILED, &bstrNotification, pcwszNameIn ) );
hr = THR( pcccbIn->SendStatusReport(
pcwszNameIn
, *pclsidLogIn
, TASKID_Minor_NETBIOS_Name_Conversion_Failed
, 1
, 1
, 1
, hr
, bstrNotification
, NULL
, NULL
) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
} // if: IClusCfgCallback interface available
goto Cleanup;
} // if: DnsHostNameToComputerName failed
if ( pcccbIn != NULL )
{
THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_NAME_CONVERSION_SUCCEEDED, &bstrNotification, pcwszNameIn, szNetBIOSName ) );
hr = THR( pcccbIn->SendStatusReport(
pcwszNameIn
, TASKID_Major_Client_And_Server_Log
, TASKID_Minor_NETBIOS_Name_Conversion_Succeeded
, 1
, 1
, 1
, S_OK
, bstrNotification
, NULL
, NULL
) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
} // if: IClusCfgCallback interface available
//
// Convert the name to the format required by the Netbios API.
//
if ( WideCharToMultiByte(
CP_ACP // ANSI code page
, 0 // fail on unmapped characters
, szNetBIOSName
, -1 // string is null-terminated
, (LPSTR) rguchNcbCallName
, sizeof( rguchNcbCallName)
, NULL // no default characters
, NULL // don't indicate default character use
) == 0 )
{
DWORD scLastError = TW32( GetLastError() );
hr = HRESULT_FROM_WIN32( scLastError );
goto Cleanup;
}
//
// The format of the ncb_callname string when using the NCBFINDNAME
// command looks like this:
// name<space><space><nul>
// Where all characters after the name are spaces and the <nul>
// is at the last character position of the buffer. The <nul>
// is actually not a NUL-terminator, but is instead a port number.
//
for ( idxNcbCallname = strlen( reinterpret_cast< char * >( rguchNcbCallName ) )
; idxNcbCallname < RTL_NUMBER_OF( rguchNcbCallName ) - 1
; idxNcbCallname++ )
{
rguchNcbCallName[ idxNcbCallname ] = ' '; // space character
} // for: each character space after the name
// Specify a 0 port number, which means query the workstation service.
rguchNcbCallName[ RTL_NUMBER_OF( rguchNcbCallName ) - 1 ] = 0;
//
// Try to find the name using NetBIOS.
//
ZeroMemory( &ncb, sizeof( ncb ) );
//
// Enumerate the network adapters
//
ncb.ncb_command = NCBENUM; // Enumerate LANA nums (wait)
ncb.ncb_buffer = (PUCHAR) &leLanaEnum;
ncb.ncb_length = sizeof( LANA_ENUM );
Netbios( &ncb );
if ( ncb.ncb_retcode != NRC_GOODRET )
{
hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, ncb.ncb_retcode );
if ( pcccbIn != NULL )
{
THR( HrLoadStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_LANAENUM_FAILED, &bstrNotification ) );
hr = THR( pcccbIn->SendStatusReport(
pcwszNameIn
, TASKID_Major_Client_And_Server_Log
, TASKID_Minor_NETBIOS_LanaEnum_Failed
, 1
, 1
, 1
, hr
, bstrNotification
, NULL
, NULL
) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
} // if: IClusCfgCallback interface available
goto Cleanup;
} // if: the Netbios API failed
//
// Reset each adapter and try to find the name.
//
for ( idx = 0; idx < leLanaEnum.length; idx++ )
{
//
// Reset the adapter.
//
ncb.ncb_command = NCBRESET;
ncb.ncb_lana_num = leLanaEnum.lana[ idx ];
Netbios( &ncb );
if ( ncb.ncb_retcode != NRC_GOODRET )
{
hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, ncb.ncb_retcode );
if ( pcccbIn != NULL )
{
THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_RESET_FAILED, &bstrNotification, leLanaEnum.lana[ idx ] ) );
hr = THR( pcccbIn->SendStatusReport(
pcwszNameIn
, TASKID_Major_Client_And_Server_Log
, TASKID_Minor_NETBIOS_Reset_Failed
, 1
, 1
, 1
, hr
, bstrNotification
, NULL
, NULL
) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
} // if: IClusCfgCallback interface available
//
// Continue with the next adapter.
//
continue;
} // if: NetBIOS reset failed
//
// Find the name on the next adapter.
//
ncb.ncb_command = NCBFINDNAME;
ncb.ncb_buffer = rguchNameBuffer;
ncb.ncb_length = sizeof( rguchNameBuffer );
pfnh->node_count = 1;
CopyMemory( ncb.ncb_callname, rguchNcbCallName, sizeof( ncb.ncb_callname ) );
Netbios( &ncb );
if ( ncb.ncb_retcode == NRC_GOODRET )
{
DWORD scConversion;
ULONG ulIPAddress = *((u_long UNALIGNED *) &pfnb->source_addr[ 2 ]);
TraceLocalFree( pszIPAddress );
scConversion = TW32( ClRtlTcpipAddressToString( ulIPAddress, &pszIPAddress ) );
if ( scConversion != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( scConversion );
goto Cleanup;
}
TraceMemoryAddLocalAddress( pszIPAddress );
*pbstrBindingOut = TraceSysAllocString( pszIPAddress );
if ( *pbstrBindingOut == NULL )
{
hr = THR( E_OUTOFMEMORY );
goto Cleanup;
}
if ( pcccbIn != NULL )
{
LPWSTR pszConnectoidName = NULL;
TW32( ClRtlGetConnectoidNameFromLANA( leLanaEnum.lana[ idx ], &pszConnectoidName ) );
THR( HrFormatStringIntoBSTR(
g_hInstance
, IDS_TASKID_MINOR_NETBIOS_BINDING_SUCCEEDED
, &bstrNotification
, szNetBIOSName
, *pbstrBindingOut
, leLanaEnum.lana[ idx ]
, ( pszConnectoidName == NULL ? L"" : pszConnectoidName )
) );
THR( pcccbIn->SendStatusReport(
pcwszNameIn
, *pclsidLogIn
, TASKID_Minor_NETBIOS_Binding_Succeeded
, 1
, 1
, 1
, S_OK
, bstrNotification
, NULL
, NULL
) );
LocalFree( pszConnectoidName );
} // if: IClusCfgCallback interface available
else
{
hr = S_OK;
}
break; // done!
} // if: the Netbios API succeeded
hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, ncb.ncb_retcode );
if ( pcccbIn != NULL )
{
LPWSTR pszConnectoidName = NULL;
HRESULT hrSendStatusReport;
TW32( ClRtlGetConnectoidNameFromLANA( leLanaEnum.lana[ idx ], &pszConnectoidName ) );
THR( HrFormatStringIntoBSTR(
g_hInstance
, IDS_TASKID_MINOR_NETBIOS_BINDING_FAILED
, &bstrNotification
, szNetBIOSName
, leLanaEnum.lana[ idx ]
, ( pszConnectoidName == NULL ? L"" : pszConnectoidName )
) );
hrSendStatusReport = THR( pcccbIn->SendStatusReport(
pcwszNameIn
, TASKID_Major_Client_And_Server_Log
, TASKID_Minor_NETBIOS_Binding_Failed
, 1
, 1
, 1
, hr
, bstrNotification
, NULL
, NULL
) );
LocalFree( pszConnectoidName );
if ( FAILED( hrSendStatusReport ) )
{
if ( hr == S_OK )
{
hr = hrSendStatusReport;
}
goto Cleanup;
}
} // if: IClusCfgCallback interface available
} // for: each LAN adapter
Assert( SUCCEEDED( hr ) );
if ( ( hr == S_OK ) && ( *pbstrBindingOut == NULL ) )
{
hr = S_FALSE;
}
Cleanup:
Assert( ( hr != S_OK ) || ( *pbstrBindingOut != NULL ) );
TraceSysFreeString( bstrNotification );
TraceLocalFree( pszIPAddress );
HRETURN( hr );
} //*** HrGetNetBIOSBinding
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrIsValidIPAddress
//
// Description:
// Determine whether a string represents a valid IP address.
//
// Arguments:
// pcwszAddressIn - The string to examine.
//
// Return Values:
// S_OK - The string represents a valid IP address.
// S_FALSE - The string does not represent a valid IP address.
//
// Possible failure codes from ClRtlTcpipStringToAddress.
//
// Remarks:
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrIsValidIPAddress(
LPCWSTR pcwszAddressIn
)
{
TraceFunc( "" );
// Return value.
HRESULT hr = S_OK;
// Variables for converting the string.
ULONG ulAddress = 0;
DWORD scConversionResult = 0;
scConversionResult = ClRtlTcpipStringToAddress( pcwszAddressIn, &ulAddress );
if ( scConversionResult == ERROR_INVALID_PARAMETER )
{
hr = S_FALSE;
}
else if ( scConversionResult != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( scConversionResult );
}
HRETURN( hr );
} //*** HrIsValidIPAddress
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrValidateHostnameLabel
//
// Description:
// Determine whether a string is a valid hostname label.
//
// Arguments:
// pcwszLabelIn - The string to examine.
// fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
//
// Return Values:
// S_OK
// The string is a valid hostname label.
//
// HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME )
// The string contains non-RFC characters, and the caller has
// requested such characters be rejected.
//
// Other errors returned from DnsValidateName, converted to HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrValidateHostnameLabel(
LPCWSTR pcwszLabelIn
, bool fAcceptNonRFCCharsIn
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
DWORD scDnsValidateName = ERROR_SUCCESS;
scDnsValidateName = DnsValidateName_W( pcwszLabelIn, DnsNameHostnameLabel );
if ( scDnsValidateName != ERROR_SUCCESS )
{
if ( ( scDnsValidateName != DNS_ERROR_NON_RFC_NAME ) || ( fAcceptNonRFCCharsIn == FALSE ) )
{
hr = HRESULT_FROM_WIN32( scDnsValidateName );
}
}
HRETURN( hr );
} //*** HrValidateHostnameLabel
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrValidateClusterNameLabel
//
// Description:
// Determine whether a string is a valid cluster name label.
//
// Arguments:
// pcwszLabelIn - The string to examine.
// fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
//
// Return Values:
// S_OK
// The string is a valid cluster name label.
//
// HRESULT_FROM_WIN32( ERROR_NOT_FOUND )
// The string is empty.
//
// HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG )
// The string's NetBIOS representation would be too long.
//
// HRESULT_FROM_WIN32( DNS_ERROR_INVALID_NAME_CHAR )
// The string contains invalid characters.
//
// HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME )
// The string contains non-RFC characters, and the caller has
// requested such characters be rejected.
//
// HRESULT_FROM_WIN32( ERROR_INVALID_COMPUTERNAME )
// The string is not valid for some other reason.
//
// Remarks:
// This checks for NetBIOS compatibility; DnsValidateName does not.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrValidateClusterNameLabel(
LPCWSTR pcwszLabelIn
, bool fAcceptNonRFCCharsIn
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
// (jfranco, bugs 398108 and 398112)
// KB: DnsValidateName does not check the conversion of its argument
// to OEM characters, which happens when CBaseClusterAddNode::SetClusterName
// calls DnsHostnameToComputerNameW. ClRtlIsNetNameValid does
// perform this check (in addition to those performed by DnsValidateName),
// and indicates whether the name has a valid
// OEM conversion and whether that conversion is too long.
CLRTL_NAME_STATUS clrtlStatus = NetNameOk;
ClRtlIsNetNameValid( pcwszLabelIn, &clrtlStatus, FALSE ); // ignore return; use status enum instead
switch ( clrtlStatus )
{
case NetNameOk:
break;
case NetNameEmpty:
hr = HRESULT_FROM_WIN32( ERROR_NOT_FOUND );
break;
case NetNameTooLong:
hr = HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG );
break;
case NetNameInvalidChars:
hr = HRESULT_FROM_WIN32( DNS_ERROR_INVALID_NAME_CHAR );
break;
case NetNameDNSNonRFCChars:
if ( fAcceptNonRFCCharsIn == FALSE )
{
hr = HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME );
}
break;
default:
hr = HRESULT_FROM_WIN32( ERROR_INVALID_COMPUTERNAME );
break;
} // switch ( clrtlStatus )
HRETURN( hr );
} //*** HrValidateClusterNameLabel
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrValidateDomainName
//
// Description:
// Determine whether a string is valid as a domain name.
//
// Arguments:
// pcwszDomainIn - The string to examine.
// fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
//
// Return Values:
// S_OK
// The string is valid as a domain name.
//
// Possible failure codes from DnsValidateName (with DnsNameDomain as the
// second parameter), converted to HRESULTs.
//
// Remarks:
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrValidateDomainName(
LPCWSTR pcwszDomainIn
, bool fAcceptNonRFCCharsIn
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
DNS_STATUS scValidName = ERROR_SUCCESS;
bool fNameIsValid = false;
scValidName = DnsValidateName( pcwszDomainIn, DnsNameDomain );
fNameIsValid = ( ( scValidName == ERROR_SUCCESS )
|| ( ( scValidName == DNS_ERROR_NON_RFC_NAME )
&& fAcceptNonRFCCharsIn ) );
if ( fNameIsValid == FALSE )
{
hr = HRESULT_FROM_WIN32( scValidName );
}
HRETURN( hr );
} //*** HrValidateDomainName
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrValidateFQDN
//
// Description:
// Determine whether a string is valid as a fully-qualified domain name.
//
// Arguments:
// pwcszFQDNIn - The string to examine.
// fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
//
// Return Values:
// S_OK
// The string is valid as a fully-qualified domain name.
//
// HRESULT_FROM_WIN32( ERROR_NOT_FOUND )
// The hostname label part of the string is empty.
//
// HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG )
// The hostname label's NetBIOS representation would be too long.
//
// HRESULT_FROM_WIN32( DNS_ERROR_INVALID_NAME_CHAR )
// The string contains invalid characters.
//
// HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME )
// The string contains non-RFC characters, and the caller has
// requested such characters be rejected.
//
// HRESULT_FROM_WIN32( ERROR_INVALID_DOMAINNAME )
// The string is only a hostname label, without a domain name.
//
// HRESULT_FROM_WIN32( ERROR_INVALID_COMPUTERNAME )
// The string is not valid for some other reason.
//
// Other failure codes from DnsValidateName (with DnsNameHostnameFull
// as the second parameter), converted to HRESULTs.
//
// Remarks:
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrValidateFQDN(
LPCWSTR pwcszFQDNIn
, bool fAcceptNonRFCCharsIn
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
// Give DnsValidateName the first shot at it.
{
DNS_STATUS scValidName = ERROR_SUCCESS;
bool fNameIsValid = false;
scValidName = DnsValidateName( pwcszFQDNIn, DnsNameHostnameFull );
fNameIsValid = ( ( scValidName == ERROR_SUCCESS )
|| ( ( scValidName == DNS_ERROR_NON_RFC_NAME )
&& fAcceptNonRFCCharsIn ) );
if ( fNameIsValid == FALSE )
{
hr = HRESULT_FROM_WIN32( scValidName );
goto Cleanup;
}
}
// Force it to be an FQDN rather than a simple hostname label,
// which passes the DnsValidateName test above.
{
const WCHAR * pwchMarker = wcschr( pwcszFQDNIn, g_wchDNSDomainMarker );
if ( pwchMarker == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_INVALID_DOMAINNAME );
goto Cleanup;
}
hr = HrValidateDomainName( pwchMarker + 1, true );
if ( FAILED( hr ) )
{
goto Cleanup;
}
}
Cleanup:
HRETURN( hr );
} //*** HrValidateFQDN
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrMakeFQN
//
// Description:
// Creates an FQName for a given machine and domain.
//
// Arguments:
// pcwszMachineIn
// The machine part of the FQName; can be a hostname label, an FQDN,
// an IP address, or an FQIP. If it's an FQDN or an FQIP,
// the function passes it through as the result and ignores
// the domain argument.
//
// pcwszDomainIn
// The domain part of the FQName; can be null, which means to use
// the local machine's domain. Only used if pcwszMachineIn does
// not contain a domain name.
//
// fAcceptNonRFCCharsIn
// Treat non-RFC characters as valid.
//
// pbstrFQNOut
// The resulting FQName; to be freed with SysFreeString.
//
// pefeoOut
// If creation failed, indicates the source of the problem:
// the machine name, the domain name, or a system error (such as
// memory allocation). Can be null if the caller doesn't care.
//
// Return Values:
// S_OK - pbstrFQNOut points to a valid FQName.
//
// An error - pbstrFQNOut points to nothing and doesn't need to be freed.
//
// Remarks:
// An FQName extends standard fully-qualified domain names by allowing
// the machine label part of the name to be an IP address. It also
// provides a way to associate an IP address with a domain, which is
// necessary to prevent the creation of cross-domain clusters when using
// IP addresses to identify the cluster nodes.
//
// The format of an FQName can be either of the following:
// [hostname label] [dot] [domain]
// [IP address] [pipe] [domain]
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrMakeFQN(
LPCWSTR pcwszMachineIn
, LPCWSTR pcwszDomainIn
, bool fAcceptNonRFCCharsIn
, BSTR * pbstrFQNOut
, EFQNErrorOrigin * pefeoOut
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
HRESULT hrValidationError = S_OK;
BSTR bstrLocalDomain = NULL;
LPCWSTR pcwszDomainToUse = NULL;
WCHAR wchDomainMarker = g_wchIPDomainMarker; // initialize to IP address case
Assert( pcwszMachineIn != NULL );
Assert( pbstrFQNOut != NULL );
Assert( *pbstrFQNOut == NULL );
//
// If pcwszMachineIn is already an FQN, just pass it through.
//
hr = STHR( HrIsValidFQN( pcwszMachineIn, fAcceptNonRFCCharsIn, &hrValidationError ) );
if ( hr == S_OK )
{
*pbstrFQNOut = TraceSysAllocString( pcwszMachineIn );
if ( *pbstrFQNOut == NULL )
{
hr = THR( E_OUTOFMEMORY );
goto SystemError;
}
goto Cleanup;
}
else if ( FAILED( hr ) )
{
goto SystemError;
}
//
// Make sure to return the proper error in the non-RFC case.
//
if ( hrValidationError == HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME ) )
{
hr = THR( hrValidationError );
goto LabelError;
}
//
// Check whether the machine is a valid label or IP address.
//
hr = STHR( HrIsValidIPAddress( pcwszMachineIn ) );
if ( FAILED( hr ) )
{
goto SystemError;
}
else if ( hr == S_FALSE )
{
hr = THR( HrValidateHostnameLabel( pcwszMachineIn, fAcceptNonRFCCharsIn ) );
if ( FAILED( hr ) )
{
goto LabelError;
}
wchDomainMarker = g_wchDNSDomainMarker;
}
//
// If caller passed in a domain, check whether the domain is valid.
//
if ( pcwszDomainIn != NULL )
{
hr = THR( HrValidateDomainName( pcwszDomainIn, fAcceptNonRFCCharsIn ) );
if ( FAILED( hr ) )
{
goto DomainError;
}
pcwszDomainToUse = pcwszDomainIn;
}
else // Otherwise, get local machine's domain.
{
hr = THR( HrGetComputerName(
ComputerNamePhysicalDnsDomain
, &bstrLocalDomain
, FALSE // fBestEffortIn
) );
if ( FAILED( hr ) )
{
goto SystemError;
}
pcwszDomainToUse = bstrLocalDomain;
} // caller passed no domain
//
// Append the domain to the machine, with the domain marker in between.
//
hr = THR( HrFormatStringIntoBSTR( L"%1!ws!%2!wc!%3!ws!", pbstrFQNOut, pcwszMachineIn, wchDomainMarker, pcwszDomainToUse ) );
if ( FAILED( hr ) )
{
goto SystemError;
}
goto Cleanup;
LabelError:
if ( pefeoOut != NULL )
{
*pefeoOut = feoLABEL;
}
goto Cleanup;
DomainError:
if ( pefeoOut != NULL )
{
*pefeoOut = feoDOMAIN;
}
goto Cleanup;
SystemError:
if ( pefeoOut != NULL )
{
*pefeoOut = feoSYSTEM;
}
goto Cleanup;
Cleanup:
TraceSysFreeString( bstrLocalDomain );
HRETURN( hr );
} //*** HrMakeFQN
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrFQNToBindingString
//
// Description:
// Maps an FQName to a binding string.
//
// Arguments:
// pcccbIn - Passed through to HrCreateBinding.
// pclsidLogIn - Passed through to HrCreateBinding.
// pcwszFQNIn - The FQName to map.
// pbstrBindingOut - The resulting binding string.
//
// Return Values:
// S_OK - pbstrBindingOut points to a valid binding string.
//
// An error - pbstrBindingOut points to nothing and doesn't need to be freed.
//
// Remarks:
//
// This function does work equivalent to HrCreateBinding for FQNames,
// passing an FQDN through to HrCreateBinding, and simply returning the
// IP address from an FQIP.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrFQNToBindingString(
IClusCfgCallback * pcccbIn
, const CLSID * pclsidLogIn
, LPCWSTR pcwszFQNIn
, BSTR * pbstrBindingOut
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
Assert( pbstrBindingOut != NULL );
Assert( *pbstrBindingOut == NULL );
hr = STHR( HrIsValidFQN( pcwszFQNIn, true ) );
if ( FAILED( hr ) )
{
goto Cleanup;
} // if:
else if ( hr == S_FALSE )
{
hr = THR( HrCreateBinding( pcccbIn, pclsidLogIn, pcwszFQNIn, pbstrBindingOut ) );
goto Cleanup;
} // else if:
// If it's an FQDN, pass through to HrCreateBinding.
hr = STHR( HrFQNIsFQDN( pcwszFQNIn ) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
else if ( hr == S_OK )
{
hr = STHR( HrCreateBinding( pcccbIn, pclsidLogIn, pcwszFQNIn, pbstrBindingOut ) );
}
else // Otherwise, extract IP address and return it.
{
WCHAR * pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
const size_t cchAddress = pwchDomainMarker - pcwszFQNIn;
WCHAR wszIPAddress[ g_cchIPAddressMax ];
// g_cchIPAddressMax includes terminating null, so cchAddress can't be equal.
if ( cchAddress >= g_cchIPAddressMax )
{
hr = THR( E_INVALIDARG );
goto Cleanup;
}
hr = THR( StringCchCopyNW( wszIPAddress, RTL_NUMBER_OF( wszIPAddress ), pcwszFQNIn, cchAddress ) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
*pbstrBindingOut = TraceSysAllocString( wszIPAddress );
if ( *pbstrBindingOut == NULL)
{
hr = THR( E_OUTOFMEMORY );
goto Cleanup;
}
} // pcwszFQNIn is an FQIP
Cleanup:
HRETURN( hr );
} //*** HrFQNToBindingString
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrFindDomainInFQN
//
// Description:
// Determines the location of the domain part of an FQName.
//
// Arguments:
// pcwszFQNIn
// The FQName of interest.
//
// pidxDomainOut
// Receives the zero-based index of the first character of the domain
// name in the string.
//
// Return Values:
// S_OK
// The FQName is valid and the location to which pidxDomainOut
// points contains the value described above.
//
// An error
// The location to which pidxDomainOut might contain anything.
//
// Remarks:
//
// Use this function, rather than wcschr(), to find a domain in an FQN.
// For example, after the invocation
// HrFindDomainInFQN( szName, &idxDomain );
// returns success, the expression
// szName + idxDomain
// yields a null-terminated string containing just the domain.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrFindDomainInFQN(
LPCWSTR pcwszFQNIn
, size_t * pidxDomainOut
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
WCHAR * pwchDomainMarker = NULL;
Assert( pcwszFQNIn != NULL );
Assert( pidxDomainOut != NULL );
hr = STHR( HrIsValidFQN( pcwszFQNIn, true ) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
else if ( hr == S_FALSE )
{
hr = THR( E_INVALIDARG );
goto Cleanup;
}
*pidxDomainOut = 0;
pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
if ( pwchDomainMarker == NULL )
{
pwchDomainMarker = wcschr( pcwszFQNIn, g_wchDNSDomainMarker );
if ( pwchDomainMarker == NULL )
{
// If the string has neither marker, it's not a valid FQN,
// but given that the string passed HrIsValidFQN,
// this probably won't ever happen.
hr = THR( E_INVALIDARG );
goto Cleanup;
}
}
*pidxDomainOut = pwchDomainMarker - pcwszFQNIn + 1; // +1 because domain begins after marker
Cleanup:
HRETURN( hr );
} //*** HrFindDomainInFQN
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrExtractPrefixFromFQN
//
// Description:
// Makes a copy of the prefix part (either a hostname label or
// an IP address) of an FQName.
//
// Arguments:
// pcwszFQNIn
// The FQName of interest.
//
// pbstrPrefixOut
// Receives a newly allocated string containing just the prefix.
//
// Return Values:
// S_OK
// The FQName is valid and the caller must free the string
// to which pbstrPrefixOut points by calling SysFreeString.
//
// An error
// The caller must not attempt to free the string to which
// pbstrPrefixOut points.
//
// Remarks:
// Use this function, rather than wcschr(), to split the prefix out of an FQN.
// For example, after the invocation
// HrFindDomainInFQN( szName, &bstrPrefix );
// returns success, the bstrPrefix is a BSTR containing just the prefix.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrExtractPrefixFromFQN(
LPCWSTR pcwszFQNIn
, BSTR * pbstrPrefixOut
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
size_t idxDomain = 0;
size_t cchPrefix = 0;
Assert( pcwszFQNIn != NULL );
Assert( pbstrPrefixOut != NULL );
hr = THR( HrFindDomainInFQN( pcwszFQNIn, &idxDomain ) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
cchPrefix = idxDomain - 1; // -1 excludes the domain marker.
*pbstrPrefixOut = TraceSysAllocStringLen( pcwszFQNIn, ( UINT ) cchPrefix );
if ( *pbstrPrefixOut == NULL )
{
hr = THR( E_OUTOFMEMORY );
goto Cleanup;
}
Cleanup:
HRETURN( hr );
} //*** HrExtractPrefixFromFQN
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrFQNIsFQDN
//
// Description:
// Determines whether an FQName is a fully-qualified domain name.
//
// Arguments:
// pcwszFQNIn - The FQName of interest.
//
// Return Values:
// S_OK - The FQName is a valid FQDN.
// S_FALSE - The FQName is valid, but it's not an FQDN.
// An error - The FQName is not valid, or something else went wrong.
//
// Remarks:
// Use this function, rather than wcschr() or DnsValidateName(),
// to determine whether an FQName is an FQDN.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrFQNIsFQDN(
LPCWSTR pcwszFQNIn
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
WCHAR * pwchDomainMarker = NULL;
Assert( pcwszFQNIn != NULL );
hr = HrIsValidFQN( pcwszFQNIn, true );
if ( FAILED( hr ) )
{
goto Cleanup;
}
else if ( hr == S_FALSE )
{
hr = THR( E_INVALIDARG );
goto Cleanup;
}
pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
if ( pwchDomainMarker != NULL )
{
hr = S_FALSE;
}
Cleanup:
HRETURN( hr );
} //*** HrFQNIsFQDN
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrFQNIsFQIP
//
// Description:
// Determines whether an FQName is an FQIP.
//
// Arguments:
// pcwszFQNIn - The FQName of interest.
//
// Return Values:
// S_OK - The FQName is a valid FQIP.
// S_FALSE - The FQName is valid, but it's not an FQIP.
// An error - The FQName is not valid, or something else went wrong.
//
// Remarks:
// Use this function, rather than wcschr(),
// to determine whether an FQName is an FQIP.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrFQNIsFQIP(
LPCWSTR pcwszFQNIn
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
WCHAR * pwchDomainMarker = NULL;
Assert( pcwszFQNIn != NULL );
hr = HrIsValidFQN( pcwszFQNIn, true );
if ( FAILED( hr ) )
{
goto Cleanup;
}
else if ( hr == S_FALSE )
{
hr = THR( E_INVALIDARG );
goto Cleanup;
}
pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
if ( pwchDomainMarker == NULL )
{
hr = S_FALSE;
}
Cleanup:
HRETURN( hr );
} //*** HrFQNIsFQIP
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrIsValidFQN
//
// Description:
// Determines whether a string is a valid FQName.
//
// Arguments:
// pcwszFQNIn
// The string to examine.
//
// fAcceptNonRFCCharsIn
// Treat non-RFC characters as valid.
//
// phrValidationErrorOut
// If the string is not valid, indicates the reason why.
//
// Return Values:
// S_OK - The string is a valid FQName.
// S_FALSE - The string is not a valid FQName.
// E_POINTER - The pcwszFQNIn parameter was NULL.
//
// Remarks:
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrIsValidFQN(
LPCWSTR pcwszFQNIn
, bool fAcceptNonRFCCharsIn
, HRESULT * phrValidationErrorOut
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
const WCHAR * pwchMarker = NULL;
HRESULT hrValidationError = S_OK;
if ( pcwszFQNIn == NULL )
{
hr = THR( E_POINTER );
goto Cleanup;
}
// If the name contains an IP domain marker...
pwchMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
if ( pwchMarker != NULL )
{
// Check whether string preceding domain marker is a valid IP address.
{
WCHAR wszIPAddress[ g_cchIPAddressMax ];
const size_t cchAddress = pwchMarker - pcwszFQNIn;
// g_cchIPAddressMax includes terminating null, so cchAddress can't be equal.
if ( cchAddress >= g_cchIPAddressMax )
{
hrValidationError = HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG );
hr = S_FALSE;
goto Cleanup;
}
hr = THR( StringCchCopyNW( wszIPAddress, RTL_NUMBER_OF( wszIPAddress ), pcwszFQNIn, cchAddress ) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
hr = HrIsValidIPAddress( wszIPAddress );
if ( hr != S_OK ) // proceed only if valid
{
hrValidationError = E_INVALIDARG;
goto Cleanup;
}
} // checking for valid ip address
// Check whether string following domain marker is a valid domain name.
{
hr = HrValidateDomainName( pwchMarker + 1, fAcceptNonRFCCharsIn );
if ( FAILED( hr ) )
{
hrValidationError = hr;
hr = S_FALSE;
goto Cleanup;
}
} // checking for valid domain name
} // if: found IP domain marker
else // Otherwise, check whether whole string is a valid FQDN.
{
hr = HrValidateFQDN( pcwszFQNIn, fAcceptNonRFCCharsIn );
if ( FAILED( hr ) )
{
hrValidationError = hr;
hr = S_FALSE;
goto Cleanup;
}
} // else: not an FQIP
Cleanup:
if ( FAILED( hrValidationError ) && ( phrValidationErrorOut != NULL ) )
{
*phrValidationErrorOut = hrValidationError;
}
HRETURN( hr );
} //*** HrIsValidFQN
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrValidateFQNPrefix
//
// Description:
//
// Arguments:
// pcwszPrefixIn
// fAcceptNonRFCCharsIn
//
// Return Values:
//
// Remarks:
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrValidateFQNPrefix(
LPCWSTR pcwszPrefixIn
, bool fAcceptNonRFCCharsIn
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
hr = HrIsValidIPAddress( pcwszPrefixIn );
if ( hr == S_FALSE )
{
hr = HrValidateHostnameLabel( pcwszPrefixIn, fAcceptNonRFCCharsIn );
}
if ( FAILED( hr ) )
{
goto Cleanup;
}
Cleanup:
HRETURN( hr );
} //*** HrValidateFQNPrefix
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrGetFQNDisplayName
//
// Description:
// Makes a copy of the prefix part (either a hostname label or
// an IP address) of an FQName, or a copy of the whole string if it's
// not an FQName.
//
// Arguments:
// pcwszNameIn
// The string of interest.
//
// pbstrShortNameOut
// Receives a newly allocated string containing either the FQName
// prefix (in the FQName case) or a copy of the whole string.
//
// Return Values:
// S_OK
// The caller must free the string to which pbstrShortNameOut points
// by calling SysFreeString.
//
// An error
// The caller must not attempt to free the string to which
// pbstrShortNameOut points.
//
// Remarks:
// This function just wraps HrExtractPrefixFromFQN to make a copy of
// the whole string (rather than return an error) if it's not a valid FQN.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT
HrGetFQNDisplayName(
LPCWSTR pcwszNameIn
, BSTR * pbstrShortNameOut
)
{
TraceFunc( "" );
HRESULT hr = S_OK;
Assert( pcwszNameIn != NULL );
Assert( pbstrShortNameOut != NULL );
//
// If the name is fully-qualified, use just the prefix.
//
hr = STHR( HrIsValidFQN( pcwszNameIn, true ) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
else if ( hr == S_OK )
{
hr = THR( HrExtractPrefixFromFQN( pcwszNameIn, pbstrShortNameOut ) );
if ( FAILED( hr ) )
{
goto Cleanup;
}
}
else // Otherwise, use the name as is.
{
*pbstrShortNameOut = TraceSysAllocString( pcwszNameIn );
if ( *pbstrShortNameOut == NULL )
{
hr = THR( E_OUTOFMEMORY );
goto Cleanup;
}
}
Cleanup:
HRETURN( hr );
} //*** HrGetFQNDisplayName