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

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000-2002 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // NameUtilSrc.cpp
  7. //
  8. // Description:
  9. // Name resolution utility.
  10. //
  11. // Maintained By:
  12. // Galen Barbee (GalenB) 28-NOV-2000
  13. //
  14. //////////////////////////////////////////////////////////////////////////////
  15. #include <initguid.h>
  16. // {6968D735-ADBB-4748-A36E-7CEE0FE21116}
  17. DEFINE_GUID( TASKID_Minor_Multiple_DNS_Records_Found,
  18. 0x6968d735, 0xadbb, 0x4748, 0xa3, 0x6e, 0x7c, 0xee, 0xf, 0xe2, 0x11, 0x16);
  19. // {D86FAAD9-2514-451e-B359-435AF35E6038}
  20. DEFINE_GUID( TASKID_Minor_FQDN_DNS_Binding_Succeeded,
  21. 0xd86faad9, 0x2514, 0x451e, 0xb3, 0x59, 0x43, 0x5a, 0xf3, 0x5e, 0x60, 0x38);
  22. // {B2359972-F6B8-433d-949B-DB1CEE009321}
  23. DEFINE_GUID( TASKID_Minor_FQDN_DNS_Binding_Failed,
  24. 0xb2359972, 0xf6b8, 0x433d, 0x94, 0x9b, 0xdb, 0x1c, 0xee, 0x0, 0x93, 0x21);
  25. // {2FF4B2F0-800C-44db-9131-F60B30F76CB4}
  26. DEFINE_GUID( TASKID_Minor_NETBIOS_Binding_Failed,
  27. 0x2ff4b2f0, 0x800c, 0x44db, 0x91, 0x31, 0xf6, 0xb, 0x30, 0xf7, 0x6c, 0xb4);
  28. // {D40532E1-9286-4dbd-A559-B62DCC218929}
  29. DEFINE_GUID( TASKID_Minor_NETBIOS_Binding_Succeeded,
  30. 0xd40532e1, 0x9286, 0x4dbd, 0xa5, 0x59, 0xb6, 0x2d, 0xcc, 0x21, 0x89, 0x29);
  31. // {D0AB3284-8F62-4f55-8938-DA6A583604E0}
  32. DEFINE_GUID( TASKID_Minor_NETBIOS_Name_Conversion_Succeeded,
  33. 0xd0ab3284, 0x8f62, 0x4f55, 0x89, 0x38, 0xda, 0x6a, 0x58, 0x36, 0x4, 0xe0);
  34. // {66F8E4AA-DF71-4973-A4A3-115EB6FE9986}
  35. DEFINE_GUID( TASKID_Minor_NETBIOS_Name_Conversion_Failed,
  36. 0x66f8e4aa, 0xdf71, 0x4973, 0xa4, 0xa3, 0x11, 0x5e, 0xb6, 0xfe, 0x99, 0x86);
  37. // {5F18ED71-07EC-46d3-ADB9-71F1C7794DB2}
  38. DEFINE_GUID( TASKID_Minor_NETBIOS_Reset_Failed,
  39. 0x5f18ed71, 0x7ec, 0x46d3, 0xad, 0xb9, 0x71, 0xf1, 0xc7, 0x79, 0x4d, 0xb2);
  40. // {A6DCB5E1-1FDF-4c94-ADBA-EE18F72B8197}
  41. DEFINE_GUID( TASKID_Minor_NETBIOS_LanaEnum_Failed,
  42. 0xa6dcb5e1, 0x1fdf, 0x4c94, 0xad, 0xba, 0xee, 0x18, 0xf7, 0x2b, 0x81, 0x97);
  43. // Constants for use by FQName functions.
  44. const WCHAR g_wchIPDomainMarker = L'|';
  45. const WCHAR g_wchDNSDomainMarker = L'.';
  46. const size_t g_cchIPAddressMax = INET_ADDRSTRLEN;
  47. //////////////////////////////////////////////////////////////////////////////
  48. //++
  49. //
  50. // CountDnsRecords
  51. //
  52. // Description:
  53. // Given a list of DNS records, counts the number of records in the list
  54. // having a given type and section.
  55. //
  56. // Arguments:
  57. // pdnsRecordListIn
  58. // Pointer to the first record in the list; can be null, which causes
  59. // a return value of zero.
  60. //
  61. // nTypeIn
  62. // The type of record to count.
  63. //
  64. // dnsSectionIn
  65. // The kind of record section to count.
  66. //
  67. //
  68. // Return Values:
  69. // The number of records having the given type and section,
  70. // or zero if the list is empty.
  71. //
  72. //--
  73. //////////////////////////////////////////////////////////////////////////////
  74. static
  75. UINT
  76. CountDnsRecords(
  77. PDNS_RECORD pdnsRecordListIn
  78. , WORD nTypeIn
  79. , DNS_SECTION dnsSectionIn )
  80. {
  81. UINT cRecords = 0;
  82. PDNS_RECORD pdnsCurrent = pdnsRecordListIn;
  83. while ( pdnsCurrent != NULL )
  84. {
  85. if ( ( pdnsCurrent->wType == nTypeIn )
  86. && ( (DNS_SECTION) pdnsCurrent->Flags.S.Section == dnsSectionIn ) )
  87. {
  88. cRecords += 1;
  89. }
  90. pdnsCurrent = pdnsCurrent->pNext;
  91. }
  92. return cRecords;
  93. } //*** CountDnsRecords
  94. //////////////////////////////////////////////////////////////////////////////
  95. //++
  96. //
  97. // FindDnsRecord
  98. //
  99. // Description:
  100. // Given a list of DNS records, searches for the first record in the list
  101. // having a given type and section.
  102. //
  103. // Arguments:
  104. // pdnsRecordListIn
  105. // Pointer to the first record in the list; can be null, which causes
  106. // a return value of null.
  107. //
  108. // nTypeIn
  109. // The type of record to find.
  110. //
  111. // dnsSectionIn
  112. // The kind of record section to find.
  113. //
  114. // Return Values:
  115. // A pointer to the first record having the given type and section,
  116. // or null if the list is empty.
  117. //
  118. //--
  119. //////////////////////////////////////////////////////////////////////////////
  120. static
  121. PDNS_RECORD
  122. FindDnsRecord(
  123. PDNS_RECORD pdnsRecordListIn
  124. , WORD nTypeIn
  125. , DNS_SECTION dnsSectionIn )
  126. {
  127. PDNS_RECORD pdnsCurrent = pdnsRecordListIn;
  128. while ( ( pdnsCurrent != NULL )
  129. && ( ( pdnsCurrent->wType != nTypeIn )
  130. || ( (DNS_SECTION) pdnsCurrent->Flags.S.Section != dnsSectionIn ) ) )
  131. {
  132. pdnsCurrent = pdnsCurrent->pNext;
  133. }
  134. return pdnsCurrent;
  135. } //*** FindDnsRecord
  136. //////////////////////////////////////////////////////////////////////////////
  137. //++
  138. //
  139. // HrCreateBinding
  140. //
  141. // Description:
  142. // Create a binding string from a name.
  143. //
  144. // Arguments:
  145. // pcccbIn - IClusCfgCallback interface for sending status reports.
  146. // pclsidLogIn - Major task ID for status reports.
  147. // pcwszNameIn - Name (FQDN) to create a binding string for.
  148. // pbstrBindingOut - Binding string created.
  149. //
  150. // Return Values:
  151. // S_OK
  152. // Other HRESULTs.
  153. //
  154. //--
  155. //////////////////////////////////////////////////////////////////////////////
  156. HRESULT
  157. HrCreateBinding(
  158. IClusCfgCallback * pcccbIn
  159. , const CLSID * pclsidLogIn
  160. , LPCWSTR pcwszNameIn
  161. , BSTR * pbstrBindingOut
  162. )
  163. {
  164. TraceFunc1( "pcwszNameIn = '%ws'", pcwszNameIn );
  165. HRESULT hr = S_FALSE; // This will always be set by the time we get to Cleanup, so the value doesn't matter.
  166. DNS_STATUS dsDnsStatus;
  167. LPWSTR pszIPAddress = NULL;
  168. PDNS_RECORD pResults = NULL;
  169. BSTR bstrNotification = NULL;
  170. BOOL fFallbackToNetbios = TRUE;
  171. Assert( pcwszNameIn != NULL );
  172. Assert( pbstrBindingOut != NULL );
  173. Assert( *pbstrBindingOut == NULL );
  174. dsDnsStatus = DnsQuery(
  175. pcwszNameIn
  176. , DNS_TYPE_A
  177. , ( DNS_QUERY_STANDARD
  178. | DNS_QUERY_BYPASS_CACHE
  179. | DNS_QUERY_TREAT_AS_FQDN
  180. )
  181. , NULL
  182. , &pResults
  183. , NULL
  184. );
  185. if ( dsDnsStatus == ERROR_SUCCESS )
  186. {
  187. PDNS_RECORD pdnsTypeARecord = FindDnsRecord( pResults, DNS_TYPE_A, DnsSectionAnswer );
  188. if ( pdnsTypeARecord != NULL )
  189. {
  190. ULONG ulIPAddress = pdnsTypeARecord->Data.A.IpAddress;
  191. DWORD scConversion = ERROR_SUCCESS;
  192. //
  193. // Send a warning to the UI if there is more than one DNS record.
  194. //
  195. if ( CountDnsRecords( pResults, DNS_TYPE_A, DnsSectionAnswer ) > 1 )
  196. {
  197. if ( pcccbIn != NULL )
  198. {
  199. THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_MULTIPLE_DNS_RECORDS_FOUND, &bstrNotification, pcwszNameIn ) );
  200. hr = THR( pcccbIn->SendStatusReport( pcwszNameIn,
  201. *pclsidLogIn,
  202. TASKID_Minor_Multiple_DNS_Records_Found,
  203. 1,
  204. 1,
  205. 1,
  206. S_FALSE,
  207. bstrNotification,
  208. NULL,
  209. NULL
  210. ) );
  211. // ignore error
  212. }
  213. } // if: more than one result returned.
  214. //
  215. // Convert the IP address to a string.
  216. //
  217. scConversion = TW32( ClRtlTcpipAddressToString( ulIPAddress, &pszIPAddress ) );
  218. if ( scConversion != ERROR_SUCCESS )
  219. {
  220. hr = HRESULT_FROM_WIN32( scConversion );
  221. goto Cleanup;
  222. }
  223. TraceMemoryAddLocalAddress( pszIPAddress );
  224. *pbstrBindingOut = TraceSysAllocString( pszIPAddress );
  225. if ( *pbstrBindingOut == NULL )
  226. {
  227. hr = THR( E_OUTOFMEMORY );
  228. goto Cleanup;
  229. }
  230. //
  231. // Indicate we were successful in the UI.
  232. //
  233. if ( pcccbIn != NULL )
  234. {
  235. THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_FQDN_DNS_BINDING_SUCCEEDED, &bstrNotification, pcwszNameIn, *pbstrBindingOut ) );
  236. hr = THR( pcccbIn->SendStatusReport( pcwszNameIn,
  237. *pclsidLogIn,
  238. TASKID_Minor_FQDN_DNS_Binding_Succeeded,
  239. 1,
  240. 1,
  241. 1,
  242. S_OK,
  243. bstrNotification,
  244. NULL,
  245. NULL
  246. ) );
  247. } // if: IClusCfgCallback interface available
  248. else
  249. {
  250. hr = S_OK;
  251. }
  252. fFallbackToNetbios = FALSE;
  253. } // if type A dns record found
  254. } // if: DnsQuery() succeeded
  255. if ( fFallbackToNetbios )
  256. {
  257. //
  258. // If there were any failures in the call to DnsQuery, fall back to
  259. // performing a NetBIOS name resolution.
  260. //
  261. if ( pcccbIn != NULL )
  262. {
  263. THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_FQDN_DNS_BINDING_FAILED, &bstrNotification, pcwszNameIn ) );
  264. hr = THR( pcccbIn->SendStatusReport( pcwszNameIn,
  265. TASKID_Major_Client_And_Server_Log,
  266. TASKID_Minor_FQDN_DNS_Binding_Failed,
  267. 1,
  268. 1,
  269. 1,
  270. MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_WIN32, dsDnsStatus ),
  271. bstrNotification,
  272. NULL,
  273. NULL
  274. ) );
  275. if ( FAILED( hr ) )
  276. {
  277. goto Cleanup;
  278. }
  279. } // if: IClusCfgCallback interface available
  280. //
  281. // Try to resolve the name with NetBIOS.
  282. //
  283. hr = THR( HrGetNetBIOSBinding( pcccbIn, pclsidLogIn, pcwszNameIn, pbstrBindingOut ) );
  284. if ( hr != S_OK ) // Non-S_OK success codes are actually failures.
  285. {
  286. //
  287. // If all else fails, use the name and attempt to bind to it.
  288. //
  289. *pbstrBindingOut = TraceSysAllocString( pcwszNameIn );
  290. if ( *pbstrBindingOut == NULL )
  291. {
  292. hr = THR( E_OUTOFMEMORY );
  293. goto Cleanup;
  294. }
  295. hr = S_FALSE;
  296. goto Cleanup;
  297. } // if: NetBIOS name resolution failed
  298. } // else if: no DNS server or no DNS name
  299. Cleanup:
  300. #ifdef DEBUG
  301. if ( FAILED( hr ) )
  302. {
  303. Assert( *pbstrBindingOut == NULL );
  304. }
  305. #endif
  306. TraceSysFreeString( bstrNotification );
  307. TraceLocalFree( pszIPAddress );
  308. if ( pResults != NULL )
  309. {
  310. DnsRecordListFree( pResults, DnsFreeRecordListDeep );
  311. }
  312. HRETURN( hr );
  313. } //*** HrCreateBinding
  314. //////////////////////////////////////////////////////////////////////////////
  315. //++
  316. //
  317. // HrGetNetBIOSBinding
  318. //
  319. // Description:
  320. // Get the IP address for a name from NetBIOS.
  321. //
  322. // Arguments:
  323. // pcccbIn - IClusCfgCallback interface for sending status reports.
  324. // pclsidLogIn - Major task ID for status reports.
  325. // pcwszNameIn - Name (FQDN) to create a binding string for.
  326. // pbstrBindingOut - Binding string created.
  327. //
  328. // Return Values:
  329. // S_OK - The operation completed successfully.
  330. // Other HRESULTs.
  331. //
  332. //--
  333. //////////////////////////////////////////////////////////////////////////////
  334. HRESULT
  335. HrGetNetBIOSBinding(
  336. IClusCfgCallback * pcccbIn
  337. , const CLSID * pclsidLogIn
  338. , LPCWSTR pcwszNameIn
  339. , BSTR * pbstrBindingOut
  340. )
  341. {
  342. TraceFunc1( "pcwszNameIn = '%ws'", pcwszNameIn );
  343. HRESULT hr = S_OK;
  344. DWORD cch;
  345. BOOL fSuccess;
  346. WCHAR szNetBIOSName[ MAX_COMPUTERNAME_LENGTH + 1 ];
  347. NCB ncb;
  348. UCHAR rguchNcbCallName[ RTL_NUMBER_OF( ncb.ncb_callname ) ];
  349. UCHAR rguchNameBuffer[ sizeof( FIND_NAME_HEADER ) + sizeof( FIND_NAME_BUFFER ) ];
  350. LANA_ENUM leLanaEnum;
  351. UCHAR idx;
  352. size_t idxNcbCallname;
  353. BSTR bstrNotification = NULL;
  354. LPWSTR pszIPAddress = NULL;
  355. FIND_NAME_HEADER * pfnh = (FIND_NAME_HEADER *) &rguchNameBuffer[ 0 ];
  356. FIND_NAME_BUFFER * pfnb = (FIND_NAME_BUFFER *) &rguchNameBuffer[ sizeof( FIND_NAME_HEADER ) ];
  357. Assert( pcwszNameIn != NULL );
  358. Assert( pbstrBindingOut != NULL );
  359. Assert( *pbstrBindingOut == NULL );
  360. //
  361. // Convert the DNS hostname to a computername (e.g. NetBIOS name).
  362. //
  363. cch = ARRAYSIZE( szNetBIOSName );
  364. Assert( cch == MAX_COMPUTERNAME_LENGTH + 1 );
  365. fSuccess = DnsHostnameToComputerName( pcwszNameIn, szNetBIOSName, &cch );
  366. if ( fSuccess == FALSE )
  367. {
  368. hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_WIN32, TW32( GetLastError() ) );
  369. if ( pcccbIn != NULL )
  370. {
  371. THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_NAME_CONVERSION_FAILED, &bstrNotification, pcwszNameIn ) );
  372. hr = THR( pcccbIn->SendStatusReport(
  373. pcwszNameIn
  374. , *pclsidLogIn
  375. , TASKID_Minor_NETBIOS_Name_Conversion_Failed
  376. , 1
  377. , 1
  378. , 1
  379. , hr
  380. , bstrNotification
  381. , NULL
  382. , NULL
  383. ) );
  384. if ( FAILED( hr ) )
  385. {
  386. goto Cleanup;
  387. }
  388. } // if: IClusCfgCallback interface available
  389. goto Cleanup;
  390. } // if: DnsHostNameToComputerName failed
  391. if ( pcccbIn != NULL )
  392. {
  393. THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_NAME_CONVERSION_SUCCEEDED, &bstrNotification, pcwszNameIn, szNetBIOSName ) );
  394. hr = THR( pcccbIn->SendStatusReport(
  395. pcwszNameIn
  396. , TASKID_Major_Client_And_Server_Log
  397. , TASKID_Minor_NETBIOS_Name_Conversion_Succeeded
  398. , 1
  399. , 1
  400. , 1
  401. , S_OK
  402. , bstrNotification
  403. , NULL
  404. , NULL
  405. ) );
  406. if ( FAILED( hr ) )
  407. {
  408. goto Cleanup;
  409. }
  410. } // if: IClusCfgCallback interface available
  411. //
  412. // Convert the name to the format required by the Netbios API.
  413. //
  414. if ( WideCharToMultiByte(
  415. CP_ACP // ANSI code page
  416. , 0 // fail on unmapped characters
  417. , szNetBIOSName
  418. , -1 // string is null-terminated
  419. , (LPSTR) rguchNcbCallName
  420. , sizeof( rguchNcbCallName)
  421. , NULL // no default characters
  422. , NULL // don't indicate default character use
  423. ) == 0 )
  424. {
  425. DWORD scLastError = TW32( GetLastError() );
  426. hr = HRESULT_FROM_WIN32( scLastError );
  427. goto Cleanup;
  428. }
  429. //
  430. // The format of the ncb_callname string when using the NCBFINDNAME
  431. // command looks like this:
  432. // name<space><space><nul>
  433. // Where all characters after the name are spaces and the <nul>
  434. // is at the last character position of the buffer. The <nul>
  435. // is actually not a NUL-terminator, but is instead a port number.
  436. //
  437. for ( idxNcbCallname = strlen( reinterpret_cast< char * >( rguchNcbCallName ) )
  438. ; idxNcbCallname < RTL_NUMBER_OF( rguchNcbCallName ) - 1
  439. ; idxNcbCallname++ )
  440. {
  441. rguchNcbCallName[ idxNcbCallname ] = ' '; // space character
  442. } // for: each character space after the name
  443. // Specify a 0 port number, which means query the workstation service.
  444. rguchNcbCallName[ RTL_NUMBER_OF( rguchNcbCallName ) - 1 ] = 0;
  445. //
  446. // Try to find the name using NetBIOS.
  447. //
  448. ZeroMemory( &ncb, sizeof( ncb ) );
  449. //
  450. // Enumerate the network adapters
  451. //
  452. ncb.ncb_command = NCBENUM; // Enumerate LANA nums (wait)
  453. ncb.ncb_buffer = (PUCHAR) &leLanaEnum;
  454. ncb.ncb_length = sizeof( LANA_ENUM );
  455. Netbios( &ncb );
  456. if ( ncb.ncb_retcode != NRC_GOODRET )
  457. {
  458. hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, ncb.ncb_retcode );
  459. if ( pcccbIn != NULL )
  460. {
  461. THR( HrLoadStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_LANAENUM_FAILED, &bstrNotification ) );
  462. hr = THR( pcccbIn->SendStatusReport(
  463. pcwszNameIn
  464. , TASKID_Major_Client_And_Server_Log
  465. , TASKID_Minor_NETBIOS_LanaEnum_Failed
  466. , 1
  467. , 1
  468. , 1
  469. , hr
  470. , bstrNotification
  471. , NULL
  472. , NULL
  473. ) );
  474. if ( FAILED( hr ) )
  475. {
  476. goto Cleanup;
  477. }
  478. } // if: IClusCfgCallback interface available
  479. goto Cleanup;
  480. } // if: the Netbios API failed
  481. //
  482. // Reset each adapter and try to find the name.
  483. //
  484. for ( idx = 0; idx < leLanaEnum.length; idx++ )
  485. {
  486. //
  487. // Reset the adapter.
  488. //
  489. ncb.ncb_command = NCBRESET;
  490. ncb.ncb_lana_num = leLanaEnum.lana[ idx ];
  491. Netbios( &ncb );
  492. if ( ncb.ncb_retcode != NRC_GOODRET )
  493. {
  494. hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, ncb.ncb_retcode );
  495. if ( pcccbIn != NULL )
  496. {
  497. THR( HrFormatStringIntoBSTR( g_hInstance, IDS_TASKID_MINOR_NETBIOS_RESET_FAILED, &bstrNotification, leLanaEnum.lana[ idx ] ) );
  498. hr = THR( pcccbIn->SendStatusReport(
  499. pcwszNameIn
  500. , TASKID_Major_Client_And_Server_Log
  501. , TASKID_Minor_NETBIOS_Reset_Failed
  502. , 1
  503. , 1
  504. , 1
  505. , hr
  506. , bstrNotification
  507. , NULL
  508. , NULL
  509. ) );
  510. if ( FAILED( hr ) )
  511. {
  512. goto Cleanup;
  513. }
  514. } // if: IClusCfgCallback interface available
  515. //
  516. // Continue with the next adapter.
  517. //
  518. continue;
  519. } // if: NetBIOS reset failed
  520. //
  521. // Find the name on the next adapter.
  522. //
  523. ncb.ncb_command = NCBFINDNAME;
  524. ncb.ncb_buffer = rguchNameBuffer;
  525. ncb.ncb_length = sizeof( rguchNameBuffer );
  526. pfnh->node_count = 1;
  527. CopyMemory( ncb.ncb_callname, rguchNcbCallName, sizeof( ncb.ncb_callname ) );
  528. Netbios( &ncb );
  529. if ( ncb.ncb_retcode == NRC_GOODRET )
  530. {
  531. DWORD scConversion;
  532. ULONG ulIPAddress = *((u_long UNALIGNED *) &pfnb->source_addr[ 2 ]);
  533. TraceLocalFree( pszIPAddress );
  534. scConversion = TW32( ClRtlTcpipAddressToString( ulIPAddress, &pszIPAddress ) );
  535. if ( scConversion != ERROR_SUCCESS )
  536. {
  537. hr = HRESULT_FROM_WIN32( scConversion );
  538. goto Cleanup;
  539. }
  540. TraceMemoryAddLocalAddress( pszIPAddress );
  541. *pbstrBindingOut = TraceSysAllocString( pszIPAddress );
  542. if ( *pbstrBindingOut == NULL )
  543. {
  544. hr = THR( E_OUTOFMEMORY );
  545. goto Cleanup;
  546. }
  547. if ( pcccbIn != NULL )
  548. {
  549. LPWSTR pszConnectoidName = NULL;
  550. TW32( ClRtlGetConnectoidNameFromLANA( leLanaEnum.lana[ idx ], &pszConnectoidName ) );
  551. THR( HrFormatStringIntoBSTR(
  552. g_hInstance
  553. , IDS_TASKID_MINOR_NETBIOS_BINDING_SUCCEEDED
  554. , &bstrNotification
  555. , szNetBIOSName
  556. , *pbstrBindingOut
  557. , leLanaEnum.lana[ idx ]
  558. , ( pszConnectoidName == NULL ? L"" : pszConnectoidName )
  559. ) );
  560. THR( pcccbIn->SendStatusReport(
  561. pcwszNameIn
  562. , *pclsidLogIn
  563. , TASKID_Minor_NETBIOS_Binding_Succeeded
  564. , 1
  565. , 1
  566. , 1
  567. , S_OK
  568. , bstrNotification
  569. , NULL
  570. , NULL
  571. ) );
  572. LocalFree( pszConnectoidName );
  573. } // if: IClusCfgCallback interface available
  574. else
  575. {
  576. hr = S_OK;
  577. }
  578. break; // done!
  579. } // if: the Netbios API succeeded
  580. hr = MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, ncb.ncb_retcode );
  581. if ( pcccbIn != NULL )
  582. {
  583. LPWSTR pszConnectoidName = NULL;
  584. HRESULT hrSendStatusReport;
  585. TW32( ClRtlGetConnectoidNameFromLANA( leLanaEnum.lana[ idx ], &pszConnectoidName ) );
  586. THR( HrFormatStringIntoBSTR(
  587. g_hInstance
  588. , IDS_TASKID_MINOR_NETBIOS_BINDING_FAILED
  589. , &bstrNotification
  590. , szNetBIOSName
  591. , leLanaEnum.lana[ idx ]
  592. , ( pszConnectoidName == NULL ? L"" : pszConnectoidName )
  593. ) );
  594. hrSendStatusReport = THR( pcccbIn->SendStatusReport(
  595. pcwszNameIn
  596. , TASKID_Major_Client_And_Server_Log
  597. , TASKID_Minor_NETBIOS_Binding_Failed
  598. , 1
  599. , 1
  600. , 1
  601. , hr
  602. , bstrNotification
  603. , NULL
  604. , NULL
  605. ) );
  606. LocalFree( pszConnectoidName );
  607. if ( FAILED( hrSendStatusReport ) )
  608. {
  609. if ( hr == S_OK )
  610. {
  611. hr = hrSendStatusReport;
  612. }
  613. goto Cleanup;
  614. }
  615. } // if: IClusCfgCallback interface available
  616. } // for: each LAN adapter
  617. Assert( SUCCEEDED( hr ) );
  618. if ( ( hr == S_OK ) && ( *pbstrBindingOut == NULL ) )
  619. {
  620. hr = S_FALSE;
  621. }
  622. Cleanup:
  623. Assert( ( hr != S_OK ) || ( *pbstrBindingOut != NULL ) );
  624. TraceSysFreeString( bstrNotification );
  625. TraceLocalFree( pszIPAddress );
  626. HRETURN( hr );
  627. } //*** HrGetNetBIOSBinding
  628. //////////////////////////////////////////////////////////////////////////////
  629. //++
  630. //
  631. // HrIsValidIPAddress
  632. //
  633. // Description:
  634. // Determine whether a string represents a valid IP address.
  635. //
  636. // Arguments:
  637. // pcwszAddressIn - The string to examine.
  638. //
  639. // Return Values:
  640. // S_OK - The string represents a valid IP address.
  641. // S_FALSE - The string does not represent a valid IP address.
  642. //
  643. // Possible failure codes from ClRtlTcpipStringToAddress.
  644. //
  645. // Remarks:
  646. //
  647. //--
  648. //////////////////////////////////////////////////////////////////////////////
  649. HRESULT
  650. HrIsValidIPAddress(
  651. LPCWSTR pcwszAddressIn
  652. )
  653. {
  654. TraceFunc( "" );
  655. // Return value.
  656. HRESULT hr = S_OK;
  657. // Variables for converting the string.
  658. ULONG ulAddress = 0;
  659. DWORD scConversionResult = 0;
  660. scConversionResult = ClRtlTcpipStringToAddress( pcwszAddressIn, &ulAddress );
  661. if ( scConversionResult == ERROR_INVALID_PARAMETER )
  662. {
  663. hr = S_FALSE;
  664. }
  665. else if ( scConversionResult != ERROR_SUCCESS )
  666. {
  667. hr = HRESULT_FROM_WIN32( scConversionResult );
  668. }
  669. HRETURN( hr );
  670. } //*** HrIsValidIPAddress
  671. //////////////////////////////////////////////////////////////////////////////
  672. //++
  673. //
  674. // HrValidateHostnameLabel
  675. //
  676. // Description:
  677. // Determine whether a string is a valid hostname label.
  678. //
  679. // Arguments:
  680. // pcwszLabelIn - The string to examine.
  681. // fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
  682. //
  683. // Return Values:
  684. // S_OK
  685. // The string is a valid hostname label.
  686. //
  687. // HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME )
  688. // The string contains non-RFC characters, and the caller has
  689. // requested such characters be rejected.
  690. //
  691. // Other errors returned from DnsValidateName, converted to HRESULTs.
  692. //
  693. //--
  694. //////////////////////////////////////////////////////////////////////////////
  695. HRESULT
  696. HrValidateHostnameLabel(
  697. LPCWSTR pcwszLabelIn
  698. , bool fAcceptNonRFCCharsIn
  699. )
  700. {
  701. TraceFunc( "" );
  702. HRESULT hr = S_OK;
  703. DWORD scDnsValidateName = ERROR_SUCCESS;
  704. scDnsValidateName = DnsValidateName_W( pcwszLabelIn, DnsNameHostnameLabel );
  705. if ( scDnsValidateName != ERROR_SUCCESS )
  706. {
  707. if ( ( scDnsValidateName != DNS_ERROR_NON_RFC_NAME ) || ( fAcceptNonRFCCharsIn == FALSE ) )
  708. {
  709. hr = HRESULT_FROM_WIN32( scDnsValidateName );
  710. }
  711. }
  712. HRETURN( hr );
  713. } //*** HrValidateHostnameLabel
  714. //////////////////////////////////////////////////////////////////////////////
  715. //++
  716. //
  717. // HrValidateClusterNameLabel
  718. //
  719. // Description:
  720. // Determine whether a string is a valid cluster name label.
  721. //
  722. // Arguments:
  723. // pcwszLabelIn - The string to examine.
  724. // fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
  725. //
  726. // Return Values:
  727. // S_OK
  728. // The string is a valid cluster name label.
  729. //
  730. // HRESULT_FROM_WIN32( ERROR_NOT_FOUND )
  731. // The string is empty.
  732. //
  733. // HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG )
  734. // The string's NetBIOS representation would be too long.
  735. //
  736. // HRESULT_FROM_WIN32( DNS_ERROR_INVALID_NAME_CHAR )
  737. // The string contains invalid characters.
  738. //
  739. // HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME )
  740. // The string contains non-RFC characters, and the caller has
  741. // requested such characters be rejected.
  742. //
  743. // HRESULT_FROM_WIN32( ERROR_INVALID_COMPUTERNAME )
  744. // The string is not valid for some other reason.
  745. //
  746. // Remarks:
  747. // This checks for NetBIOS compatibility; DnsValidateName does not.
  748. //
  749. //--
  750. //////////////////////////////////////////////////////////////////////////////
  751. HRESULT
  752. HrValidateClusterNameLabel(
  753. LPCWSTR pcwszLabelIn
  754. , bool fAcceptNonRFCCharsIn
  755. )
  756. {
  757. TraceFunc( "" );
  758. HRESULT hr = S_OK;
  759. // (jfranco, bugs 398108 and 398112)
  760. // KB: DnsValidateName does not check the conversion of its argument
  761. // to OEM characters, which happens when CBaseClusterAddNode::SetClusterName
  762. // calls DnsHostnameToComputerNameW. ClRtlIsNetNameValid does
  763. // perform this check (in addition to those performed by DnsValidateName),
  764. // and indicates whether the name has a valid
  765. // OEM conversion and whether that conversion is too long.
  766. CLRTL_NAME_STATUS clrtlStatus = NetNameOk;
  767. ClRtlIsNetNameValid( pcwszLabelIn, &clrtlStatus, FALSE ); // ignore return; use status enum instead
  768. switch ( clrtlStatus )
  769. {
  770. case NetNameOk:
  771. break;
  772. case NetNameEmpty:
  773. hr = HRESULT_FROM_WIN32( ERROR_NOT_FOUND );
  774. break;
  775. case NetNameTooLong:
  776. hr = HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG );
  777. break;
  778. case NetNameInvalidChars:
  779. hr = HRESULT_FROM_WIN32( DNS_ERROR_INVALID_NAME_CHAR );
  780. break;
  781. case NetNameDNSNonRFCChars:
  782. if ( fAcceptNonRFCCharsIn == FALSE )
  783. {
  784. hr = HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME );
  785. }
  786. break;
  787. default:
  788. hr = HRESULT_FROM_WIN32( ERROR_INVALID_COMPUTERNAME );
  789. break;
  790. } // switch ( clrtlStatus )
  791. HRETURN( hr );
  792. } //*** HrValidateClusterNameLabel
  793. //////////////////////////////////////////////////////////////////////////////
  794. //++
  795. //
  796. // HrValidateDomainName
  797. //
  798. // Description:
  799. // Determine whether a string is valid as a domain name.
  800. //
  801. // Arguments:
  802. // pcwszDomainIn - The string to examine.
  803. // fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
  804. //
  805. // Return Values:
  806. // S_OK
  807. // The string is valid as a domain name.
  808. //
  809. // Possible failure codes from DnsValidateName (with DnsNameDomain as the
  810. // second parameter), converted to HRESULTs.
  811. //
  812. // Remarks:
  813. //
  814. //--
  815. //////////////////////////////////////////////////////////////////////////////
  816. HRESULT
  817. HrValidateDomainName(
  818. LPCWSTR pcwszDomainIn
  819. , bool fAcceptNonRFCCharsIn
  820. )
  821. {
  822. TraceFunc( "" );
  823. HRESULT hr = S_OK;
  824. DNS_STATUS scValidName = ERROR_SUCCESS;
  825. bool fNameIsValid = false;
  826. scValidName = DnsValidateName( pcwszDomainIn, DnsNameDomain );
  827. fNameIsValid = ( ( scValidName == ERROR_SUCCESS )
  828. || ( ( scValidName == DNS_ERROR_NON_RFC_NAME )
  829. && fAcceptNonRFCCharsIn ) );
  830. if ( fNameIsValid == FALSE )
  831. {
  832. hr = HRESULT_FROM_WIN32( scValidName );
  833. }
  834. HRETURN( hr );
  835. } //*** HrValidateDomainName
  836. //////////////////////////////////////////////////////////////////////////////
  837. //++
  838. //
  839. // HrValidateFQDN
  840. //
  841. // Description:
  842. // Determine whether a string is valid as a fully-qualified domain name.
  843. //
  844. // Arguments:
  845. // pwcszFQDNIn - The string to examine.
  846. // fAcceptNonRFCCharsIn - Treat non-RFC characters as valid.
  847. //
  848. // Return Values:
  849. // S_OK
  850. // The string is valid as a fully-qualified domain name.
  851. //
  852. // HRESULT_FROM_WIN32( ERROR_NOT_FOUND )
  853. // The hostname label part of the string is empty.
  854. //
  855. // HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG )
  856. // The hostname label's NetBIOS representation would be too long.
  857. //
  858. // HRESULT_FROM_WIN32( DNS_ERROR_INVALID_NAME_CHAR )
  859. // The string contains invalid characters.
  860. //
  861. // HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME )
  862. // The string contains non-RFC characters, and the caller has
  863. // requested such characters be rejected.
  864. //
  865. // HRESULT_FROM_WIN32( ERROR_INVALID_DOMAINNAME )
  866. // The string is only a hostname label, without a domain name.
  867. //
  868. // HRESULT_FROM_WIN32( ERROR_INVALID_COMPUTERNAME )
  869. // The string is not valid for some other reason.
  870. //
  871. // Other failure codes from DnsValidateName (with DnsNameHostnameFull
  872. // as the second parameter), converted to HRESULTs.
  873. //
  874. // Remarks:
  875. //
  876. //--
  877. //////////////////////////////////////////////////////////////////////////////
  878. HRESULT
  879. HrValidateFQDN(
  880. LPCWSTR pwcszFQDNIn
  881. , bool fAcceptNonRFCCharsIn
  882. )
  883. {
  884. TraceFunc( "" );
  885. HRESULT hr = S_OK;
  886. // Give DnsValidateName the first shot at it.
  887. {
  888. DNS_STATUS scValidName = ERROR_SUCCESS;
  889. bool fNameIsValid = false;
  890. scValidName = DnsValidateName( pwcszFQDNIn, DnsNameHostnameFull );
  891. fNameIsValid = ( ( scValidName == ERROR_SUCCESS )
  892. || ( ( scValidName == DNS_ERROR_NON_RFC_NAME )
  893. && fAcceptNonRFCCharsIn ) );
  894. if ( fNameIsValid == FALSE )
  895. {
  896. hr = HRESULT_FROM_WIN32( scValidName );
  897. goto Cleanup;
  898. }
  899. }
  900. // Force it to be an FQDN rather than a simple hostname label,
  901. // which passes the DnsValidateName test above.
  902. {
  903. const WCHAR * pwchMarker = wcschr( pwcszFQDNIn, g_wchDNSDomainMarker );
  904. if ( pwchMarker == NULL )
  905. {
  906. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DOMAINNAME );
  907. goto Cleanup;
  908. }
  909. hr = HrValidateDomainName( pwchMarker + 1, true );
  910. if ( FAILED( hr ) )
  911. {
  912. goto Cleanup;
  913. }
  914. }
  915. Cleanup:
  916. HRETURN( hr );
  917. } //*** HrValidateFQDN
  918. //////////////////////////////////////////////////////////////////////////////
  919. //++
  920. //
  921. // HrMakeFQN
  922. //
  923. // Description:
  924. // Creates an FQName for a given machine and domain.
  925. //
  926. // Arguments:
  927. // pcwszMachineIn
  928. // The machine part of the FQName; can be a hostname label, an FQDN,
  929. // an IP address, or an FQIP. If it's an FQDN or an FQIP,
  930. // the function passes it through as the result and ignores
  931. // the domain argument.
  932. //
  933. // pcwszDomainIn
  934. // The domain part of the FQName; can be null, which means to use
  935. // the local machine's domain. Only used if pcwszMachineIn does
  936. // not contain a domain name.
  937. //
  938. // fAcceptNonRFCCharsIn
  939. // Treat non-RFC characters as valid.
  940. //
  941. // pbstrFQNOut
  942. // The resulting FQName; to be freed with SysFreeString.
  943. //
  944. // pefeoOut
  945. // If creation failed, indicates the source of the problem:
  946. // the machine name, the domain name, or a system error (such as
  947. // memory allocation). Can be null if the caller doesn't care.
  948. //
  949. // Return Values:
  950. // S_OK - pbstrFQNOut points to a valid FQName.
  951. //
  952. // An error - pbstrFQNOut points to nothing and doesn't need to be freed.
  953. //
  954. // Remarks:
  955. // An FQName extends standard fully-qualified domain names by allowing
  956. // the machine label part of the name to be an IP address. It also
  957. // provides a way to associate an IP address with a domain, which is
  958. // necessary to prevent the creation of cross-domain clusters when using
  959. // IP addresses to identify the cluster nodes.
  960. //
  961. // The format of an FQName can be either of the following:
  962. // [hostname label] [dot] [domain]
  963. // [IP address] [pipe] [domain]
  964. //
  965. //--
  966. //////////////////////////////////////////////////////////////////////////////
  967. HRESULT
  968. HrMakeFQN(
  969. LPCWSTR pcwszMachineIn
  970. , LPCWSTR pcwszDomainIn
  971. , bool fAcceptNonRFCCharsIn
  972. , BSTR * pbstrFQNOut
  973. , EFQNErrorOrigin * pefeoOut
  974. )
  975. {
  976. TraceFunc( "" );
  977. HRESULT hr = S_OK;
  978. HRESULT hrValidationError = S_OK;
  979. BSTR bstrLocalDomain = NULL;
  980. LPCWSTR pcwszDomainToUse = NULL;
  981. WCHAR wchDomainMarker = g_wchIPDomainMarker; // initialize to IP address case
  982. Assert( pcwszMachineIn != NULL );
  983. Assert( pbstrFQNOut != NULL );
  984. Assert( *pbstrFQNOut == NULL );
  985. //
  986. // If pcwszMachineIn is already an FQN, just pass it through.
  987. //
  988. hr = STHR( HrIsValidFQN( pcwszMachineIn, fAcceptNonRFCCharsIn, &hrValidationError ) );
  989. if ( hr == S_OK )
  990. {
  991. *pbstrFQNOut = TraceSysAllocString( pcwszMachineIn );
  992. if ( *pbstrFQNOut == NULL )
  993. {
  994. hr = THR( E_OUTOFMEMORY );
  995. goto SystemError;
  996. }
  997. goto Cleanup;
  998. }
  999. else if ( FAILED( hr ) )
  1000. {
  1001. goto SystemError;
  1002. }
  1003. //
  1004. // Make sure to return the proper error in the non-RFC case.
  1005. //
  1006. if ( hrValidationError == HRESULT_FROM_WIN32( DNS_ERROR_NON_RFC_NAME ) )
  1007. {
  1008. hr = THR( hrValidationError );
  1009. goto LabelError;
  1010. }
  1011. //
  1012. // Check whether the machine is a valid label or IP address.
  1013. //
  1014. hr = STHR( HrIsValidIPAddress( pcwszMachineIn ) );
  1015. if ( FAILED( hr ) )
  1016. {
  1017. goto SystemError;
  1018. }
  1019. else if ( hr == S_FALSE )
  1020. {
  1021. hr = THR( HrValidateHostnameLabel( pcwszMachineIn, fAcceptNonRFCCharsIn ) );
  1022. if ( FAILED( hr ) )
  1023. {
  1024. goto LabelError;
  1025. }
  1026. wchDomainMarker = g_wchDNSDomainMarker;
  1027. }
  1028. //
  1029. // If caller passed in a domain, check whether the domain is valid.
  1030. //
  1031. if ( pcwszDomainIn != NULL )
  1032. {
  1033. hr = THR( HrValidateDomainName( pcwszDomainIn, fAcceptNonRFCCharsIn ) );
  1034. if ( FAILED( hr ) )
  1035. {
  1036. goto DomainError;
  1037. }
  1038. pcwszDomainToUse = pcwszDomainIn;
  1039. }
  1040. else // Otherwise, get local machine's domain.
  1041. {
  1042. hr = THR( HrGetComputerName(
  1043. ComputerNamePhysicalDnsDomain
  1044. , &bstrLocalDomain
  1045. , FALSE // fBestEffortIn
  1046. ) );
  1047. if ( FAILED( hr ) )
  1048. {
  1049. goto SystemError;
  1050. }
  1051. pcwszDomainToUse = bstrLocalDomain;
  1052. } // caller passed no domain
  1053. //
  1054. // Append the domain to the machine, with the domain marker in between.
  1055. //
  1056. hr = THR( HrFormatStringIntoBSTR( L"%1!ws!%2!wc!%3!ws!", pbstrFQNOut, pcwszMachineIn, wchDomainMarker, pcwszDomainToUse ) );
  1057. if ( FAILED( hr ) )
  1058. {
  1059. goto SystemError;
  1060. }
  1061. goto Cleanup;
  1062. LabelError:
  1063. if ( pefeoOut != NULL )
  1064. {
  1065. *pefeoOut = feoLABEL;
  1066. }
  1067. goto Cleanup;
  1068. DomainError:
  1069. if ( pefeoOut != NULL )
  1070. {
  1071. *pefeoOut = feoDOMAIN;
  1072. }
  1073. goto Cleanup;
  1074. SystemError:
  1075. if ( pefeoOut != NULL )
  1076. {
  1077. *pefeoOut = feoSYSTEM;
  1078. }
  1079. goto Cleanup;
  1080. Cleanup:
  1081. TraceSysFreeString( bstrLocalDomain );
  1082. HRETURN( hr );
  1083. } //*** HrMakeFQN
  1084. //////////////////////////////////////////////////////////////////////////////
  1085. //++
  1086. //
  1087. // HrFQNToBindingString
  1088. //
  1089. // Description:
  1090. // Maps an FQName to a binding string.
  1091. //
  1092. // Arguments:
  1093. // pcccbIn - Passed through to HrCreateBinding.
  1094. // pclsidLogIn - Passed through to HrCreateBinding.
  1095. // pcwszFQNIn - The FQName to map.
  1096. // pbstrBindingOut - The resulting binding string.
  1097. //
  1098. // Return Values:
  1099. // S_OK - pbstrBindingOut points to a valid binding string.
  1100. //
  1101. // An error - pbstrBindingOut points to nothing and doesn't need to be freed.
  1102. //
  1103. // Remarks:
  1104. //
  1105. // This function does work equivalent to HrCreateBinding for FQNames,
  1106. // passing an FQDN through to HrCreateBinding, and simply returning the
  1107. // IP address from an FQIP.
  1108. //
  1109. //--
  1110. //////////////////////////////////////////////////////////////////////////////
  1111. HRESULT
  1112. HrFQNToBindingString(
  1113. IClusCfgCallback * pcccbIn
  1114. , const CLSID * pclsidLogIn
  1115. , LPCWSTR pcwszFQNIn
  1116. , BSTR * pbstrBindingOut
  1117. )
  1118. {
  1119. TraceFunc( "" );
  1120. HRESULT hr = S_OK;
  1121. Assert( pbstrBindingOut != NULL );
  1122. Assert( *pbstrBindingOut == NULL );
  1123. hr = STHR( HrIsValidFQN( pcwszFQNIn, true ) );
  1124. if ( FAILED( hr ) )
  1125. {
  1126. goto Cleanup;
  1127. } // if:
  1128. else if ( hr == S_FALSE )
  1129. {
  1130. hr = THR( HrCreateBinding( pcccbIn, pclsidLogIn, pcwszFQNIn, pbstrBindingOut ) );
  1131. goto Cleanup;
  1132. } // else if:
  1133. // If it's an FQDN, pass through to HrCreateBinding.
  1134. hr = STHR( HrFQNIsFQDN( pcwszFQNIn ) );
  1135. if ( FAILED( hr ) )
  1136. {
  1137. goto Cleanup;
  1138. }
  1139. else if ( hr == S_OK )
  1140. {
  1141. hr = STHR( HrCreateBinding( pcccbIn, pclsidLogIn, pcwszFQNIn, pbstrBindingOut ) );
  1142. }
  1143. else // Otherwise, extract IP address and return it.
  1144. {
  1145. WCHAR * pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
  1146. const size_t cchAddress = pwchDomainMarker - pcwszFQNIn;
  1147. WCHAR wszIPAddress[ g_cchIPAddressMax ];
  1148. // g_cchIPAddressMax includes terminating null, so cchAddress can't be equal.
  1149. if ( cchAddress >= g_cchIPAddressMax )
  1150. {
  1151. hr = THR( E_INVALIDARG );
  1152. goto Cleanup;
  1153. }
  1154. hr = THR( StringCchCopyNW( wszIPAddress, RTL_NUMBER_OF( wszIPAddress ), pcwszFQNIn, cchAddress ) );
  1155. if ( FAILED( hr ) )
  1156. {
  1157. goto Cleanup;
  1158. }
  1159. *pbstrBindingOut = TraceSysAllocString( wszIPAddress );
  1160. if ( *pbstrBindingOut == NULL)
  1161. {
  1162. hr = THR( E_OUTOFMEMORY );
  1163. goto Cleanup;
  1164. }
  1165. } // pcwszFQNIn is an FQIP
  1166. Cleanup:
  1167. HRETURN( hr );
  1168. } //*** HrFQNToBindingString
  1169. //////////////////////////////////////////////////////////////////////////////
  1170. //++
  1171. //
  1172. // HrFindDomainInFQN
  1173. //
  1174. // Description:
  1175. // Determines the location of the domain part of an FQName.
  1176. //
  1177. // Arguments:
  1178. // pcwszFQNIn
  1179. // The FQName of interest.
  1180. //
  1181. // pidxDomainOut
  1182. // Receives the zero-based index of the first character of the domain
  1183. // name in the string.
  1184. //
  1185. // Return Values:
  1186. // S_OK
  1187. // The FQName is valid and the location to which pidxDomainOut
  1188. // points contains the value described above.
  1189. //
  1190. // An error
  1191. // The location to which pidxDomainOut might contain anything.
  1192. //
  1193. // Remarks:
  1194. //
  1195. // Use this function, rather than wcschr(), to find a domain in an FQN.
  1196. // For example, after the invocation
  1197. // HrFindDomainInFQN( szName, &idxDomain );
  1198. // returns success, the expression
  1199. // szName + idxDomain
  1200. // yields a null-terminated string containing just the domain.
  1201. //
  1202. //--
  1203. //////////////////////////////////////////////////////////////////////////////
  1204. HRESULT
  1205. HrFindDomainInFQN(
  1206. LPCWSTR pcwszFQNIn
  1207. , size_t * pidxDomainOut
  1208. )
  1209. {
  1210. TraceFunc( "" );
  1211. HRESULT hr = S_OK;
  1212. WCHAR * pwchDomainMarker = NULL;
  1213. Assert( pcwszFQNIn != NULL );
  1214. Assert( pidxDomainOut != NULL );
  1215. hr = STHR( HrIsValidFQN( pcwszFQNIn, true ) );
  1216. if ( FAILED( hr ) )
  1217. {
  1218. goto Cleanup;
  1219. }
  1220. else if ( hr == S_FALSE )
  1221. {
  1222. hr = THR( E_INVALIDARG );
  1223. goto Cleanup;
  1224. }
  1225. *pidxDomainOut = 0;
  1226. pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
  1227. if ( pwchDomainMarker == NULL )
  1228. {
  1229. pwchDomainMarker = wcschr( pcwszFQNIn, g_wchDNSDomainMarker );
  1230. if ( pwchDomainMarker == NULL )
  1231. {
  1232. // If the string has neither marker, it's not a valid FQN,
  1233. // but given that the string passed HrIsValidFQN,
  1234. // this probably won't ever happen.
  1235. hr = THR( E_INVALIDARG );
  1236. goto Cleanup;
  1237. }
  1238. }
  1239. *pidxDomainOut = pwchDomainMarker - pcwszFQNIn + 1; // +1 because domain begins after marker
  1240. Cleanup:
  1241. HRETURN( hr );
  1242. } //*** HrFindDomainInFQN
  1243. //////////////////////////////////////////////////////////////////////////////
  1244. //++
  1245. //
  1246. // HrExtractPrefixFromFQN
  1247. //
  1248. // Description:
  1249. // Makes a copy of the prefix part (either a hostname label or
  1250. // an IP address) of an FQName.
  1251. //
  1252. // Arguments:
  1253. // pcwszFQNIn
  1254. // The FQName of interest.
  1255. //
  1256. // pbstrPrefixOut
  1257. // Receives a newly allocated string containing just the prefix.
  1258. //
  1259. // Return Values:
  1260. // S_OK
  1261. // The FQName is valid and the caller must free the string
  1262. // to which pbstrPrefixOut points by calling SysFreeString.
  1263. //
  1264. // An error
  1265. // The caller must not attempt to free the string to which
  1266. // pbstrPrefixOut points.
  1267. //
  1268. // Remarks:
  1269. // Use this function, rather than wcschr(), to split the prefix out of an FQN.
  1270. // For example, after the invocation
  1271. // HrFindDomainInFQN( szName, &bstrPrefix );
  1272. // returns success, the bstrPrefix is a BSTR containing just the prefix.
  1273. //
  1274. //--
  1275. //////////////////////////////////////////////////////////////////////////////
  1276. HRESULT
  1277. HrExtractPrefixFromFQN(
  1278. LPCWSTR pcwszFQNIn
  1279. , BSTR * pbstrPrefixOut
  1280. )
  1281. {
  1282. TraceFunc( "" );
  1283. HRESULT hr = S_OK;
  1284. size_t idxDomain = 0;
  1285. size_t cchPrefix = 0;
  1286. Assert( pcwszFQNIn != NULL );
  1287. Assert( pbstrPrefixOut != NULL );
  1288. hr = THR( HrFindDomainInFQN( pcwszFQNIn, &idxDomain ) );
  1289. if ( FAILED( hr ) )
  1290. {
  1291. goto Cleanup;
  1292. }
  1293. cchPrefix = idxDomain - 1; // -1 excludes the domain marker.
  1294. *pbstrPrefixOut = TraceSysAllocStringLen( pcwszFQNIn, ( UINT ) cchPrefix );
  1295. if ( *pbstrPrefixOut == NULL )
  1296. {
  1297. hr = THR( E_OUTOFMEMORY );
  1298. goto Cleanup;
  1299. }
  1300. Cleanup:
  1301. HRETURN( hr );
  1302. } //*** HrExtractPrefixFromFQN
  1303. //////////////////////////////////////////////////////////////////////////////
  1304. //++
  1305. //
  1306. // HrFQNIsFQDN
  1307. //
  1308. // Description:
  1309. // Determines whether an FQName is a fully-qualified domain name.
  1310. //
  1311. // Arguments:
  1312. // pcwszFQNIn - The FQName of interest.
  1313. //
  1314. // Return Values:
  1315. // S_OK - The FQName is a valid FQDN.
  1316. // S_FALSE - The FQName is valid, but it's not an FQDN.
  1317. // An error - The FQName is not valid, or something else went wrong.
  1318. //
  1319. // Remarks:
  1320. // Use this function, rather than wcschr() or DnsValidateName(),
  1321. // to determine whether an FQName is an FQDN.
  1322. //
  1323. //--
  1324. //////////////////////////////////////////////////////////////////////////////
  1325. HRESULT
  1326. HrFQNIsFQDN(
  1327. LPCWSTR pcwszFQNIn
  1328. )
  1329. {
  1330. TraceFunc( "" );
  1331. HRESULT hr = S_OK;
  1332. WCHAR * pwchDomainMarker = NULL;
  1333. Assert( pcwszFQNIn != NULL );
  1334. hr = HrIsValidFQN( pcwszFQNIn, true );
  1335. if ( FAILED( hr ) )
  1336. {
  1337. goto Cleanup;
  1338. }
  1339. else if ( hr == S_FALSE )
  1340. {
  1341. hr = THR( E_INVALIDARG );
  1342. goto Cleanup;
  1343. }
  1344. pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
  1345. if ( pwchDomainMarker != NULL )
  1346. {
  1347. hr = S_FALSE;
  1348. }
  1349. Cleanup:
  1350. HRETURN( hr );
  1351. } //*** HrFQNIsFQDN
  1352. //////////////////////////////////////////////////////////////////////////////
  1353. //++
  1354. //
  1355. // HrFQNIsFQIP
  1356. //
  1357. // Description:
  1358. // Determines whether an FQName is an FQIP.
  1359. //
  1360. // Arguments:
  1361. // pcwszFQNIn - The FQName of interest.
  1362. //
  1363. // Return Values:
  1364. // S_OK - The FQName is a valid FQIP.
  1365. // S_FALSE - The FQName is valid, but it's not an FQIP.
  1366. // An error - The FQName is not valid, or something else went wrong.
  1367. //
  1368. // Remarks:
  1369. // Use this function, rather than wcschr(),
  1370. // to determine whether an FQName is an FQIP.
  1371. //
  1372. //--
  1373. //////////////////////////////////////////////////////////////////////////////
  1374. HRESULT
  1375. HrFQNIsFQIP(
  1376. LPCWSTR pcwszFQNIn
  1377. )
  1378. {
  1379. TraceFunc( "" );
  1380. HRESULT hr = S_OK;
  1381. WCHAR * pwchDomainMarker = NULL;
  1382. Assert( pcwszFQNIn != NULL );
  1383. hr = HrIsValidFQN( pcwszFQNIn, true );
  1384. if ( FAILED( hr ) )
  1385. {
  1386. goto Cleanup;
  1387. }
  1388. else if ( hr == S_FALSE )
  1389. {
  1390. hr = THR( E_INVALIDARG );
  1391. goto Cleanup;
  1392. }
  1393. pwchDomainMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
  1394. if ( pwchDomainMarker == NULL )
  1395. {
  1396. hr = S_FALSE;
  1397. }
  1398. Cleanup:
  1399. HRETURN( hr );
  1400. } //*** HrFQNIsFQIP
  1401. //////////////////////////////////////////////////////////////////////////////
  1402. //++
  1403. //
  1404. // HrIsValidFQN
  1405. //
  1406. // Description:
  1407. // Determines whether a string is a valid FQName.
  1408. //
  1409. // Arguments:
  1410. // pcwszFQNIn
  1411. // The string to examine.
  1412. //
  1413. // fAcceptNonRFCCharsIn
  1414. // Treat non-RFC characters as valid.
  1415. //
  1416. // phrValidationErrorOut
  1417. // If the string is not valid, indicates the reason why.
  1418. //
  1419. // Return Values:
  1420. // S_OK - The string is a valid FQName.
  1421. // S_FALSE - The string is not a valid FQName.
  1422. // E_POINTER - The pcwszFQNIn parameter was NULL.
  1423. //
  1424. // Remarks:
  1425. //
  1426. //--
  1427. //////////////////////////////////////////////////////////////////////////////
  1428. HRESULT
  1429. HrIsValidFQN(
  1430. LPCWSTR pcwszFQNIn
  1431. , bool fAcceptNonRFCCharsIn
  1432. , HRESULT * phrValidationErrorOut
  1433. )
  1434. {
  1435. TraceFunc( "" );
  1436. HRESULT hr = S_OK;
  1437. const WCHAR * pwchMarker = NULL;
  1438. HRESULT hrValidationError = S_OK;
  1439. if ( pcwszFQNIn == NULL )
  1440. {
  1441. hr = THR( E_POINTER );
  1442. goto Cleanup;
  1443. }
  1444. // If the name contains an IP domain marker...
  1445. pwchMarker = wcschr( pcwszFQNIn, g_wchIPDomainMarker );
  1446. if ( pwchMarker != NULL )
  1447. {
  1448. // Check whether string preceding domain marker is a valid IP address.
  1449. {
  1450. WCHAR wszIPAddress[ g_cchIPAddressMax ];
  1451. const size_t cchAddress = pwchMarker - pcwszFQNIn;
  1452. // g_cchIPAddressMax includes terminating null, so cchAddress can't be equal.
  1453. if ( cchAddress >= g_cchIPAddressMax )
  1454. {
  1455. hrValidationError = HRESULT_FROM_WIN32( ERROR_DS_NAME_TOO_LONG );
  1456. hr = S_FALSE;
  1457. goto Cleanup;
  1458. }
  1459. hr = THR( StringCchCopyNW( wszIPAddress, RTL_NUMBER_OF( wszIPAddress ), pcwszFQNIn, cchAddress ) );
  1460. if ( FAILED( hr ) )
  1461. {
  1462. goto Cleanup;
  1463. }
  1464. hr = HrIsValidIPAddress( wszIPAddress );
  1465. if ( hr != S_OK ) // proceed only if valid
  1466. {
  1467. hrValidationError = E_INVALIDARG;
  1468. goto Cleanup;
  1469. }
  1470. } // checking for valid ip address
  1471. // Check whether string following domain marker is a valid domain name.
  1472. {
  1473. hr = HrValidateDomainName( pwchMarker + 1, fAcceptNonRFCCharsIn );
  1474. if ( FAILED( hr ) )
  1475. {
  1476. hrValidationError = hr;
  1477. hr = S_FALSE;
  1478. goto Cleanup;
  1479. }
  1480. } // checking for valid domain name
  1481. } // if: found IP domain marker
  1482. else // Otherwise, check whether whole string is a valid FQDN.
  1483. {
  1484. hr = HrValidateFQDN( pcwszFQNIn, fAcceptNonRFCCharsIn );
  1485. if ( FAILED( hr ) )
  1486. {
  1487. hrValidationError = hr;
  1488. hr = S_FALSE;
  1489. goto Cleanup;
  1490. }
  1491. } // else: not an FQIP
  1492. Cleanup:
  1493. if ( FAILED( hrValidationError ) && ( phrValidationErrorOut != NULL ) )
  1494. {
  1495. *phrValidationErrorOut = hrValidationError;
  1496. }
  1497. HRETURN( hr );
  1498. } //*** HrIsValidFQN
  1499. //////////////////////////////////////////////////////////////////////////////
  1500. //++
  1501. //
  1502. // HrValidateFQNPrefix
  1503. //
  1504. // Description:
  1505. //
  1506. // Arguments:
  1507. // pcwszPrefixIn
  1508. // fAcceptNonRFCCharsIn
  1509. //
  1510. // Return Values:
  1511. //
  1512. // Remarks:
  1513. //
  1514. //--
  1515. //////////////////////////////////////////////////////////////////////////////
  1516. HRESULT
  1517. HrValidateFQNPrefix(
  1518. LPCWSTR pcwszPrefixIn
  1519. , bool fAcceptNonRFCCharsIn
  1520. )
  1521. {
  1522. TraceFunc( "" );
  1523. HRESULT hr = S_OK;
  1524. hr = HrIsValidIPAddress( pcwszPrefixIn );
  1525. if ( hr == S_FALSE )
  1526. {
  1527. hr = HrValidateHostnameLabel( pcwszPrefixIn, fAcceptNonRFCCharsIn );
  1528. }
  1529. if ( FAILED( hr ) )
  1530. {
  1531. goto Cleanup;
  1532. }
  1533. Cleanup:
  1534. HRETURN( hr );
  1535. } //*** HrValidateFQNPrefix
  1536. //////////////////////////////////////////////////////////////////////////////
  1537. //++
  1538. //
  1539. // HrGetFQNDisplayName
  1540. //
  1541. // Description:
  1542. // Makes a copy of the prefix part (either a hostname label or
  1543. // an IP address) of an FQName, or a copy of the whole string if it's
  1544. // not an FQName.
  1545. //
  1546. // Arguments:
  1547. // pcwszNameIn
  1548. // The string of interest.
  1549. //
  1550. // pbstrShortNameOut
  1551. // Receives a newly allocated string containing either the FQName
  1552. // prefix (in the FQName case) or a copy of the whole string.
  1553. //
  1554. // Return Values:
  1555. // S_OK
  1556. // The caller must free the string to which pbstrShortNameOut points
  1557. // by calling SysFreeString.
  1558. //
  1559. // An error
  1560. // The caller must not attempt to free the string to which
  1561. // pbstrShortNameOut points.
  1562. //
  1563. // Remarks:
  1564. // This function just wraps HrExtractPrefixFromFQN to make a copy of
  1565. // the whole string (rather than return an error) if it's not a valid FQN.
  1566. //
  1567. //--
  1568. //////////////////////////////////////////////////////////////////////////////
  1569. HRESULT
  1570. HrGetFQNDisplayName(
  1571. LPCWSTR pcwszNameIn
  1572. , BSTR * pbstrShortNameOut
  1573. )
  1574. {
  1575. TraceFunc( "" );
  1576. HRESULT hr = S_OK;
  1577. Assert( pcwszNameIn != NULL );
  1578. Assert( pbstrShortNameOut != NULL );
  1579. //
  1580. // If the name is fully-qualified, use just the prefix.
  1581. //
  1582. hr = STHR( HrIsValidFQN( pcwszNameIn, true ) );
  1583. if ( FAILED( hr ) )
  1584. {
  1585. goto Cleanup;
  1586. }
  1587. else if ( hr == S_OK )
  1588. {
  1589. hr = THR( HrExtractPrefixFromFQN( pcwszNameIn, pbstrShortNameOut ) );
  1590. if ( FAILED( hr ) )
  1591. {
  1592. goto Cleanup;
  1593. }
  1594. }
  1595. else // Otherwise, use the name as is.
  1596. {
  1597. *pbstrShortNameOut = TraceSysAllocString( pcwszNameIn );
  1598. if ( *pbstrShortNameOut == NULL )
  1599. {
  1600. hr = THR( E_OUTOFMEMORY );
  1601. goto Cleanup;
  1602. }
  1603. }
  1604. Cleanup:
  1605. HRETURN( hr );
  1606. } //*** HrGetFQNDisplayName