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
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
|