Source code of Windows XP (NT5)
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.

1242 lines
30 KiB

  1. //++
  2. //
  3. // Copyright (C) Microsoft Corporation, 1987 - 1999
  4. //
  5. // Module Name:
  6. //
  7. // dcutil.c
  8. //
  9. // Abstract:
  10. //
  11. // Test to ensure that a workstation has network (IP) connectivity to
  12. // the outside.
  13. //
  14. // Author:
  15. //
  16. // 15-Dec-1997 (cliffv)
  17. // Anilth - 4-20-1998
  18. //
  19. // Environment:
  20. //
  21. // User mode only.
  22. // Contains NT-specific code.
  23. //
  24. // Revision History:
  25. //
  26. // 1-June-1998 (denisemi) add DnsServerHasDCRecords to check DC dns records
  27. // registration
  28. //
  29. // 26-June-1998 (t-rajkup) add general tcp/ip , dhcp and routing,
  30. // winsock, ipx, wins and netbt information.
  31. //--
  32. //
  33. // Common include files.
  34. //
  35. #include "precomp.h"
  36. #include <iphlpint.h>
  37. #include "dcutil.h"
  38. #include "ipcfgtest.h"
  39. DWORD CheckDomainConfig(IN PWSTR pwzDomainName, OUT PLIST_ENTRY plmsgOutput);
  40. DWORD CheckAdapterDnsConfig( OUT PLIST_ENTRY plmsgOutput);
  41. DWORD DnsDcSrvCheck(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput);
  42. DWORD ValidateDnsDomainName(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput);
  43. PWSTR ConcatonateStrings(PWSTR pwzFirst, PWSTR pwzSecond);
  44. BOOL AddToList(PWSTR * ppwzList, PWSTR pwz);
  45. BOOL BuildDomainList(PWSTR * ppwzDomainList, PWSTR pwzDnsDomain);
  46. PWSTR AllocString(PWSTR pwz);
  47. DWORD GetInterfacesStr( PWSTR *ppwIfStr);
  48. const PWSTR g_pwzSrvRecordPrefix = L"_ldap._tcp.dc._msdcs.";
  49. //(nsun) DC related routines
  50. PTESTED_DC
  51. GetUpTestedDc(
  52. IN PTESTED_DOMAIN TestedDomain
  53. )
  54. /*++
  55. Routine Description:
  56. Returns a DC that's currently up and running.
  57. Arguments:
  58. TestedDomain - Domain the DC is in
  59. Return Value:
  60. Returns pointer to structure describing the DC
  61. NULL: There are no 'up' DCs
  62. --*/
  63. {
  64. PLIST_ENTRY ListEntry;
  65. PTESTED_DC TestedDc;
  66. //
  67. // Find a DC that's up to run the test
  68. //
  69. for ( ListEntry = TestedDomain->TestedDcs.Flink ;
  70. ListEntry != &TestedDomain->TestedDcs ;
  71. ListEntry = ListEntry->Flink ) {
  72. //
  73. // Loop through the list of DCs in this domain
  74. //
  75. TestedDc = CONTAINING_RECORD( ListEntry, TESTED_DC, Next );
  76. if ( (TestedDc->Flags & DC_IS_DOWN) == 0) {
  77. return TestedDc;
  78. }
  79. }
  80. return NULL;
  81. }
  82. PTESTED_DC
  83. AddTestedDc(
  84. IN NETDIAG_PARAMS *pParams,
  85. IN OUT NETDIAG_RESULT *pResults,
  86. IN PTESTED_DOMAIN TestedDomain,
  87. IN LPWSTR ComputerName,
  88. IN ULONG Flags
  89. )
  90. /*++
  91. Routine Description:
  92. Add a DC to the list of DCs to test in a particular domain
  93. Arguments:
  94. TestedDomain - Domain the DC is in
  95. ComputerName - Netbios or DNS computer name of the DC
  96. Without the leading \\
  97. Flags - Flags to set on the DC
  98. Return Value:
  99. Returns pointer to structure describing the DC
  100. NULL: Memory allocation failure.
  101. --*/
  102. {
  103. PTESTED_DC TestedDc = NULL;
  104. PLIST_ENTRY ListEntry;
  105. LPWSTR Period;
  106. //
  107. // Check if the domain is already defined.
  108. //
  109. TestedDc = FindTestedDc( pResults, ComputerName );
  110. //
  111. // Ensure the DC is for the right domain
  112. //
  113. if ( TestedDc != NULL )
  114. {
  115. if ( TestedDc->TestedDomain != TestedDomain )
  116. {
  117. return NULL;
  118. }
  119. }
  120. //
  121. // Allocate a structure to describe the domain.
  122. //
  123. if ( TestedDc == NULL )
  124. {
  125. TestedDc = Malloc( sizeof(TESTED_DC) );
  126. if ( TestedDc == NULL )
  127. {
  128. DebugMessage(" AddTestedDc(): Out of Memory!\n");
  129. return NULL;
  130. }
  131. ZeroMemory( TestedDc, sizeof(TESTED_DC) );
  132. TestedDc->ComputerName = NetpAllocWStrFromWStr( ComputerName );
  133. if ( TestedDc->ComputerName == NULL )
  134. {
  135. Free(TestedDc);
  136. return NULL;
  137. }
  138. //
  139. // Convert the computername to netbios (Use the API when in becomes available.
  140. //
  141. if ((Period = wcschr( ComputerName, L'.' )) == NULL )
  142. {
  143. wcsncpy( TestedDc->NetbiosDcName, ComputerName, CNLEN );
  144. TestedDc->NetbiosDcName[CNLEN] = L'\0';
  145. }
  146. else
  147. {
  148. ULONG CharsToCopy = (ULONG) min( CNLEN, Period-ComputerName);
  149. wcsncpy( TestedDc->NetbiosDcName, ComputerName, CharsToCopy );
  150. TestedDc->NetbiosDcName[CharsToCopy] = '\0';
  151. }
  152. TestedDc->TestedDomain = TestedDomain;
  153. InsertTailList( &TestedDomain->TestedDcs, &TestedDc->Next );
  154. }
  155. //
  156. // Set the flags requested by the caller.
  157. //
  158. // if ( Flags & DC_IS_NT5 ) {
  159. // if ( TestedDc->Flags & DC_IS_NT4 ) {
  160. // printf(" [WARNING] '%ws' is both an NT 5 and NT 4 DC.\n", ComputerName );
  161. // }
  162. // }
  163. // if ( Flags & DC_IS_NT4 ) {
  164. // if ( TestedDc->Flags & DC_IS_NT5 ) {
  165. // printf(" [WARNING] '%ws' is both an NT 4 and NT 5 DC.\n", ComputerName );
  166. // }
  167. // }
  168. TestedDc->Flags |= Flags;
  169. //
  170. // Ensure we have the IpAddress of this DC.
  171. //
  172. (VOID) GetIpAddressForDc( TestedDc );
  173. //
  174. // Ping the DC
  175. //
  176. if ( (TestedDc->Flags & DC_PINGED) == 0 && (TestedDc->Flags & DC_IS_DOWN) == 0)
  177. {
  178. if ( !IsIcmpResponseW( TestedDc->DcIpAddress ) ) {
  179. DebugMessage2(" [WARNING] Cannot ping '%ws' (it must be down).\n", TestedDc->ComputerName );
  180. TestedDc->Flags |= DC_IS_DOWN;
  181. TestedDc->Flags |= DC_FAILED_PING;
  182. }
  183. TestedDc->Flags |= DC_PINGED;
  184. }
  185. //try to query DC info to check if the DC is really up
  186. if( (TestedDc->Flags & DC_IS_DOWN) == 0 )
  187. {
  188. PSERVER_INFO_100 pServerInfo100 = NULL;
  189. NET_API_STATUS NetStatus;
  190. NetStatus = NetServerGetInfo( TestedDc->ComputerName,
  191. 100,
  192. (LPBYTE *)&pServerInfo100 );
  193. if(NetStatus != NO_ERROR && NetStatus != ERROR_ACCESS_DENIED)
  194. {
  195. TestedDc->Flags |= DC_IS_DOWN;
  196. // IDS_GLOBAL_DC_DOWN "Cannot get information for DC %ws. [%s] Assume it is down.\n"
  197. PrintDebug(pParams, 4, IDS_GLOBAL_DC_DOWN, TestedDc->ComputerName,
  198. NetStatusToString(NetStatus));
  199. }
  200. else
  201. NetApiBufferFree( pServerInfo100 );
  202. }
  203. return TestedDc;
  204. }
  205. PTESTED_DC
  206. FindTestedDc(
  207. IN OUT NETDIAG_RESULT *pResults,
  208. IN LPWSTR ComputerName
  209. )
  210. /*++
  211. Routine Description:
  212. Find the tested DC structure for the named DC
  213. Arguments:
  214. ComputerName - Netbios or DNS computer name of the DC
  215. Without the leading \\
  216. Return Value:
  217. Returns pointer to structure describing the DC
  218. NULL: No Such DC is currently defined
  219. --*/
  220. {
  221. PTESTED_DC TestedDc = NULL;
  222. PTESTED_DOMAIN TestedDomain = NULL;
  223. PLIST_ENTRY ListEntry;
  224. PLIST_ENTRY ListEntry2;
  225. WCHAR NetbiosDcName[CNLEN+1];
  226. LPWSTR Period;
  227. //
  228. // Convert the computername to netbios (Use the API when in becomes available.
  229. //
  230. if ((Period = wcschr( ComputerName, L'.' )) == NULL )
  231. {
  232. wcsncpy( NetbiosDcName, ComputerName, CNLEN );
  233. NetbiosDcName[CNLEN] = L'\0';
  234. }
  235. else
  236. {
  237. ULONG CharsToCopy = (ULONG) min( CNLEN, Period-ComputerName);
  238. wcsncpy( NetbiosDcName, ComputerName, CharsToCopy );
  239. NetbiosDcName[CharsToCopy] = '\0';
  240. }
  241. //
  242. // Loop through the list of domains
  243. //
  244. for ( ListEntry = pResults->Global.listTestedDomains.Flink ;
  245. ListEntry != &pResults->Global.listTestedDomains ;
  246. ListEntry = ListEntry->Flink ) {
  247. //
  248. // Loop through the list of DCs in this domain
  249. //
  250. TestedDomain = CONTAINING_RECORD( ListEntry, TESTED_DOMAIN, Next );
  251. for ( ListEntry2 = TestedDomain->TestedDcs.Flink ;
  252. ListEntry2 != &TestedDomain->TestedDcs ;
  253. ListEntry2 = ListEntry2->Flink ) {
  254. //
  255. // Loop through the list of DCs in this domain
  256. //
  257. TestedDc = CONTAINING_RECORD( ListEntry2, TESTED_DC, Next );
  258. //
  259. // If the Netbios computer names match,
  260. // we found it.
  261. //
  262. if ( _wcsicmp( TestedDc->NetbiosDcName, NetbiosDcName ) == 0 ) {
  263. return TestedDc;
  264. }
  265. }
  266. }
  267. return NULL;
  268. }
  269. NET_API_STATUS
  270. GetADc(IN NETDIAG_PARAMS *pParams,
  271. IN OUT NETDIAG_RESULT *pResults,
  272. OUT PLIST_ENTRY plmsgOutput,
  273. IN DSGETDCNAMEW *DsGetDcRoutine,
  274. IN PTESTED_DOMAIN TestedDomain,
  275. IN DWORD Flags,
  276. OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
  277. )
  278. /*++
  279. Routine Description:
  280. Does a DsGetDcName
  281. Arguments:
  282. DsGetDcRoutine - Routine to call to find a DC
  283. TestedDomain - Domain to test
  284. Flags - Flags to pass to DsGetDcName
  285. DomainControllerInfo - Return Domain Controller information
  286. Return Value:
  287. Status of the operation.
  288. --*/
  289. {
  290. NET_API_STATUS NetStatus;
  291. PDOMAIN_CONTROLLER_INFOW LocalDomainControllerInfo = NULL;
  292. PDOMAIN_CONTROLLER_INFOW LocalDomainControllerInfo2;
  293. static BOOL s_fDcNameInitialized = FALSE;
  294. //
  295. // Initialize internal version of DsGetDcName
  296. //
  297. if( !s_fDcNameInitialized )
  298. {
  299. // Commented out to port to Source Depot - smanda
  300. #ifdef SLM_TREE
  301. NetStatus = DCNameInitialize();
  302. if ( NetStatus != NO_ERROR )
  303. {
  304. DebugMessage2(" [FATAL] Cannot initialize DsGetDcName. [%s]\n",
  305. NetStatusToString(NetStatus));
  306. PrintGuru( NetStatus, DSGETDC_GURU );
  307. goto Cleanup;
  308. }
  309. #endif
  310. //$REVIEW (nsun 10/05/98) make sure we just init once
  311. s_fDcNameInitialized = TRUE;
  312. }
  313. //
  314. // Try it first not asking for IP.
  315. //
  316. // Though technically wrong, specify DS_DIRECTORY_SERVICE_PREFERRED here
  317. // or I won't be able to tell that this is an NT 5 domain below.
  318. //
  319. NetStatus = (*DsGetDcRoutine)( NULL,
  320. TestedDomain->QueryableDomainName,
  321. NULL,
  322. NULL,
  323. DS_FORCE_REDISCOVERY |
  324. DS_DIRECTORY_SERVICE_PREFERRED |
  325. Flags,
  326. &LocalDomainControllerInfo );
  327. // If DsGetDcName return ERROR_NO_SUCH_DOMAIN then try to findout the exact reason for the error
  328. // Based on DoctorDNS specs for join command
  329. if ( NetStatus == ERROR_NO_SUCH_DOMAIN && TestedDomain->QueryableDomainName != NULL && plmsgOutput != NULL ) {
  330. CheckDomainConfig(TestedDomain->QueryableDomainName, plmsgOutput);
  331. }
  332. if ( NetStatus != NO_ERROR ) {
  333. DebugMessage2( " DsGetDcRoutine failed. [%s]\n", NetStatusToString(NetStatus));
  334. goto Cleanup;
  335. }
  336. //
  337. // Add this DC to the list of DCs in the domain
  338. //
  339. (VOID) AddTestedDc( pParams,
  340. pResults,
  341. TestedDomain,
  342. LocalDomainControllerInfo->DomainControllerName+2,
  343. (LocalDomainControllerInfo->Flags & DS_DS_FLAG ) ?
  344. DC_IS_NT5 :
  345. DC_IS_NT4 );
  346. //
  347. // If this DC wasn't discovered using IP,
  348. // and it is an NT 5 DC,
  349. // try again requiring IP.
  350. //
  351. // (I can't require IP in the first place since NT 4.0 DCs can't return
  352. // their IP address.)
  353. //
  354. if ( LocalDomainControllerInfo->DomainControllerAddressType != DS_INET_ADDRESS &&
  355. (LocalDomainControllerInfo->Flags & DS_DS_FLAG) != 0 ) {
  356. NetStatus = (*DsGetDcRoutine)( NULL,
  357. TestedDomain->QueryableDomainName,
  358. NULL,
  359. NULL,
  360. DS_FORCE_REDISCOVERY |
  361. DS_IP_REQUIRED |
  362. Flags,
  363. &LocalDomainControllerInfo2 );
  364. if ( NetStatus == NO_ERROR ) {
  365. NetApiBufferFree( LocalDomainControllerInfo );
  366. LocalDomainControllerInfo = LocalDomainControllerInfo2;
  367. //
  368. // Add this DC to the list of DCs in the domain
  369. //
  370. (VOID) AddTestedDc( pParams,
  371. pResults,
  372. TestedDomain,
  373. LocalDomainControllerInfo->DomainControllerName+2,
  374. (LocalDomainControllerInfo->Flags & DS_DS_FLAG ) ?
  375. DC_IS_NT5 :
  376. DC_IS_NT4 );
  377. }
  378. }
  379. //
  380. // Check to ensure KDC consistency
  381. //
  382. // This is also checked in DoDsGetDcName()
  383. if ( (LocalDomainControllerInfo->Flags & (DS_DS_FLAG|DS_KDC_FLAG)) == DS_DS_FLAG ) {
  384. DebugMessage3(" [WARNING] KDC is not running on NT 5 DC '%ws' in domain '%ws'.",
  385. LocalDomainControllerInfo->DomainControllerName,
  386. TestedDomain->PrintableDomainName );
  387. }
  388. //
  389. // Return the info to the caller
  390. //
  391. *DomainControllerInfo = LocalDomainControllerInfo;
  392. LocalDomainControllerInfo = NULL;
  393. NetStatus = NO_ERROR;
  394. Cleanup:
  395. if ( LocalDomainControllerInfo != NULL ) {
  396. NetApiBufferFree( LocalDomainControllerInfo );
  397. LocalDomainControllerInfo = NULL;
  398. }
  399. return NetStatus;
  400. }
  401. //used in DCList and LDAP tests
  402. BOOL
  403. GetIpAddressForDc( PTESTED_DC TestedDc )
  404. /*++
  405. Routine Description:
  406. Get the IP address for the tested DC
  407. Arguments:
  408. TestedDc - DC to get the IP address for.
  409. None.
  410. Return Value:
  411. TRUE: Test suceeded.
  412. FALSE: Test failed
  413. --*/
  414. {
  415. BOOL RetVal = TRUE;
  416. NET_API_STATUS NetStatus;
  417. HOSTENT *HostEnt;
  418. LPSTR AnsiComputerName;
  419. if ( TestedDc->DcIpAddress == NULL ) {
  420. AnsiComputerName = NetpAllocStrFromWStr( TestedDc->ComputerName );
  421. if ( AnsiComputerName == NULL ) {
  422. DebugMessage( "Out of memory!\n" );
  423. RetVal = FALSE;
  424. TestedDc->Flags |= DC_IS_DOWN;
  425. } else {
  426. HostEnt = gethostbyname( AnsiComputerName );
  427. NetApiBufferFree( AnsiComputerName );
  428. if ( HostEnt == NULL )
  429. {
  430. NetStatus = WSAGetLastError();
  431. DebugMessage3(" [WARNING] Cannot gethostbyname for '%ws'. [%s]\n",
  432. TestedDc->ComputerName, NetStatusToString(NetStatus) );
  433. TestedDc->Flags |= DC_IS_DOWN;
  434. }
  435. else
  436. {
  437. WCHAR LocalIpAddressString[NL_IP_ADDRESS_LENGTH+1];
  438. NetpIpAddressToWStr( *(PULONG)HostEnt->h_addr_list[0], LocalIpAddressString );
  439. TestedDc->DcIpAddress = NetpAllocWStrFromWStr( LocalIpAddressString );
  440. if (TestedDc->DcIpAddress == NULL )
  441. {
  442. RetVal = FALSE;
  443. TestedDc->Flags |= DC_IS_DOWN;
  444. }
  445. }
  446. }
  447. }
  448. return RetVal;
  449. }
  450. DWORD CheckDomainConfig(IN PWSTR pwzDomainName, OUT PLIST_ENTRY plmsgOutput)
  451. {
  452. DNS_STATUS status;
  453. status = ValidateDnsDomainName(pwzDomainName, plmsgOutput);
  454. if (status == ERROR_SUCCESS)
  455. {
  456. status = CheckAdapterDnsConfig(plmsgOutput);
  457. if (status == ERROR_SUCCESS)
  458. {
  459. status = DnsDcSrvCheck(pwzDomainName, plmsgOutput);
  460. }
  461. else
  462. {
  463. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13242);
  464. }
  465. }
  466. return ERROR_SUCCESS;
  467. }
  468. //+----------------------------------------------------------------------------
  469. //
  470. // Function: CheckAdapterDnsConfig
  471. //
  472. // Synopsis: Check whether at least one enabled adapter/connection is
  473. // configured with a DNS server.
  474. //
  475. //-----------------------------------------------------------------------------
  476. DWORD
  477. CheckAdapterDnsConfig( OUT PLIST_ENTRY plmsgOutput )
  478. {
  479. // IpConfig reads the registry and I can't find a good alternative way to do
  480. // this remotely. For now using DnsQueryConfig which is not remoteable nor
  481. // does it return per-adapter listings.
  482. //
  483. PIP_ARRAY pipArray;
  484. DNS_STATUS status;
  485. DWORD i, dwBufSize = sizeof(IP_ARRAY);
  486. status = DnsQueryConfig(DnsConfigDnsServerList, DNS_CONFIG_FLAG_ALLOC, NULL,
  487. NULL, &pipArray, &dwBufSize);
  488. if (ERROR_SUCCESS != status || !pipArray)
  489. {
  490. DebugMessage2(L"Attempt to obtain DNS name server info failed with error %d\n", status);
  491. return status;
  492. }
  493. return (pipArray->AddrCount) ? ERROR_SUCCESS : DNS_INFO_NO_RECORDS;
  494. }
  495. //+----------------------------------------------------------------------------
  496. //
  497. // Function: DnsDcSrvCheck
  498. //
  499. // Synopsis: Check whether the SRV DNS record for
  500. // _ldap._tcp.dc._msdcs.<DNS name of Active Directory Domain>
  501. // is in place.
  502. //
  503. //-----------------------------------------------------------------------------
  504. DWORD
  505. DnsDcSrvCheck(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput)
  506. {
  507. PDNS_RECORD rgDnsRecs, pDnsRec;
  508. DNS_STATUS status;
  509. BOOL fSuccess;
  510. PWSTR pwzFullSrvRecord, pwzSrvList = NULL;
  511. pwzFullSrvRecord = ConcatonateStrings(g_pwzSrvRecordPrefix, pwzDnsDomain);
  512. if (!pwzFullSrvRecord)
  513. {
  514. return ERROR_NOT_ENOUGH_MEMORY;
  515. }
  516. // First query for the SRV records for this
  517. status = DnsQuery_W(pwzFullSrvRecord, DNS_TYPE_SRV, DNS_QUERY_BYPASS_CACHE,
  518. NULL, &rgDnsRecs, NULL);
  519. pDnsRec = rgDnsRecs;
  520. if (ERROR_SUCCESS == status)
  521. {
  522. if (!pDnsRec)
  523. {
  524. // PrintMsg(SEV_ALWAYS, DCDIAG_REPLICA_ERR_NO_SRV, pwzDnsDomain);
  525. }
  526. else
  527. {
  528. PDNS_RECORD rgARecs;
  529. fSuccess = FALSE;
  530. while (pDnsRec)
  531. {
  532. if (DNS_TYPE_SRV == pDnsRec->wType)
  533. {
  534. WCHAR UnicodeDCName[MAX_PATH+1];
  535. NetpCopyStrToWStr( UnicodeDCName, pDnsRec->Data.Srv.pNameTarget);
  536. status = DnsQuery_W(UnicodeDCName, DNS_TYPE_A,
  537. DNS_QUERY_BYPASS_CACHE,
  538. NULL, &rgARecs, NULL);
  539. if (ERROR_SUCCESS != status || !rgARecs)
  540. {
  541. // failure.
  542. if (!AddToList(&pwzSrvList, UnicodeDCName))
  543. {
  544. return ERROR_NOT_ENOUGH_MEMORY;
  545. }
  546. }
  547. else
  548. {
  549. fSuccess = TRUE;
  550. DebugMessage2(L"SRV name: %s\n",
  551. pDnsRec->Data.Srv.nameTarget);
  552. DnsRecordListFree(rgARecs, TRUE);
  553. }
  554. }
  555. pDnsRec = pDnsRec->pNext;
  556. }
  557. DnsRecordListFree(rgDnsRecs, TRUE);
  558. if (fSuccess)
  559. {
  560. // Success message
  561. }
  562. else
  563. {
  564. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13243,
  565. pwzDnsDomain, pwzSrvList);
  566. LocalFree(pwzSrvList);
  567. }
  568. }
  569. }
  570. else
  571. {
  572. PWSTR pwzDomainList;
  573. switch (status)
  574. {
  575. case DNS_ERROR_RCODE_FORMAT_ERROR:
  576. case DNS_ERROR_RCODE_NOT_IMPLEMENTED:
  577. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13244,
  578. pwzDnsDomain );
  579. break;
  580. case DNS_ERROR_RCODE_SERVER_FAILURE:
  581. if (!BuildDomainList(&pwzDomainList, pwzDnsDomain))
  582. {
  583. return ERROR_NOT_ENOUGH_MEMORY;
  584. }
  585. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13245,
  586. pwzDnsDomain, pwzFullSrvRecord, pwzDomainList );
  587. LocalFree(pwzDomainList);
  588. break;
  589. case DNS_ERROR_RCODE_NAME_ERROR:
  590. if (!BuildDomainList(&pwzDomainList, pwzDnsDomain))
  591. {
  592. return ERROR_NOT_ENOUGH_MEMORY;
  593. }
  594. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13246,
  595. pwzDnsDomain, pwzDnsDomain, pwzFullSrvRecord, pwzDomainList );
  596. LocalFree(pwzDomainList);
  597. break;
  598. case DNS_ERROR_RCODE_REFUSED:
  599. if (!BuildDomainList(&pwzDomainList, pwzDnsDomain))
  600. {
  601. return ERROR_NOT_ENOUGH_MEMORY;
  602. }
  603. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13247,
  604. pwzDnsDomain, pwzDomainList );
  605. LocalFree(pwzDomainList);
  606. break;
  607. case DNS_INFO_NO_RECORDS:
  608. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13248,
  609. pwzDnsDomain, pwzDnsDomain, pwzDnsDomain );
  610. break;
  611. case ERROR_TIMEOUT:
  612. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13249);
  613. break;
  614. default:
  615. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13250,
  616. status );
  617. break;
  618. }
  619. }
  620. LocalFree(pwzFullSrvRecord);
  621. return status;
  622. }
  623. //+----------------------------------------------------------------------------
  624. //
  625. // Function: ValidateDnsDomainName
  626. //
  627. // Synopsis: Validate the DNS domain name.
  628. //
  629. //-----------------------------------------------------------------------------
  630. DWORD
  631. ValidateDnsDomainName(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput)
  632. {
  633. DNS_STATUS status;
  634. status = DnsValidateName_W(pwzDnsDomain, DnsNameDomain);
  635. switch (status)
  636. {
  637. case ERROR_INVALID_NAME:
  638. case DNS_ERROR_INVALID_NAME_CHAR:
  639. case DNS_ERROR_NUMERIC_NAME:
  640. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13240,
  641. pwzDnsDomain, DNS_MAX_LABEL_LENGTH );
  642. return status;
  643. case DNS_ERROR_NON_RFC_NAME:
  644. //
  645. // Not an error, print warning message.
  646. //
  647. AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13241,
  648. pwzDnsDomain );
  649. break;
  650. case ERROR_SUCCESS:
  651. break;
  652. }
  653. return status;
  654. }
  655. PWSTR ConcatonateStrings(PWSTR pwzFirst, PWSTR pwzSecond)
  656. {
  657. PWSTR pwz;
  658. pwz = (PWSTR)LocalAlloc(LMEM_FIXED,
  659. ((int)wcslen(pwzFirst) + (int)wcslen(pwzSecond) + 1) * sizeof(WCHAR));
  660. if (!pwz)
  661. {
  662. return NULL;
  663. }
  664. wcscpy(pwz, pwzFirst);
  665. wcscat(pwz, pwzSecond);
  666. return pwz;
  667. }
  668. BOOL AddToList(PWSTR * ppwzList, PWSTR pwz)
  669. {
  670. PWSTR pwzTmp;
  671. if (*ppwzList)
  672. {
  673. pwzTmp = (PWSTR)LocalAlloc(LMEM_FIXED,
  674. ((int)wcslen(*ppwzList) + (int)wcslen(pwz) + 3) * sizeof(WCHAR));
  675. if (!pwzTmp)
  676. {
  677. return FALSE;
  678. }
  679. wcscpy(pwzTmp, *ppwzList);
  680. wcscat(pwzTmp, L", ");
  681. wcscat(pwzTmp, pwz);
  682. LocalFree(*ppwzList);
  683. *ppwzList = pwzTmp;
  684. }
  685. else
  686. {
  687. pwzTmp = AllocString(pwz);
  688. if (!pwzTmp)
  689. {
  690. return FALSE;
  691. }
  692. *ppwzList = pwzTmp;
  693. }
  694. return TRUE;
  695. }
  696. BOOL BuildDomainList(PWSTR * ppwzDomainList, PWSTR pwzDnsDomain)
  697. {
  698. PWSTR pwzDot, pwzTmp;
  699. pwzTmp = AllocString(pwzDnsDomain);
  700. if (!pwzTmp)
  701. {
  702. return FALSE;
  703. }
  704. pwzDot = pwzDnsDomain;
  705. while (pwzDot = wcschr(pwzDot, L'.'))
  706. {
  707. pwzDot++;
  708. if (!pwzDot)
  709. {
  710. break;
  711. }
  712. if (!AddToList(&pwzTmp, pwzDot))
  713. {
  714. return FALSE;
  715. }
  716. }
  717. *ppwzDomainList = pwzTmp;
  718. return TRUE;
  719. }
  720. // string helpers.
  721. PWSTR AllocString(PWSTR pwz)
  722. {
  723. PWSTR pwzTmp;
  724. pwzTmp = (PWSTR)LocalAlloc(LMEM_FIXED, ((int)wcslen(pwz) + 1) * sizeof(WCHAR));
  725. if (!pwzTmp)
  726. {
  727. return NULL;
  728. }
  729. wcscpy(pwzTmp, pwz);
  730. return pwzTmp;
  731. }
  732. VOID
  733. NetpIpAddressToStr(
  734. ULONG IpAddress,
  735. CHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1]
  736. )
  737. /*++
  738. Routine Description:
  739. Convert an IP address to a string.
  740. Arguments:
  741. IpAddress - IP Address to convert
  742. IpAddressString - resultant string.
  743. Return Value:
  744. None.
  745. --*/
  746. {
  747. struct in_addr InetAddr;
  748. char * InetAddrString;
  749. //
  750. // Convert the address to ascii
  751. //
  752. InetAddr.s_addr = IpAddress;
  753. InetAddrString = inet_ntoa( InetAddr );
  754. //
  755. // Copy the string our to the caller.
  756. //
  757. if ( InetAddrString == NULL || strlen(InetAddrString) > NL_IP_ADDRESS_LENGTH ) {
  758. *IpAddressString = L'\0';
  759. } else {
  760. strcpy( IpAddressString, InetAddrString );
  761. }
  762. return;
  763. }
  764. VOID
  765. NetpIpAddressToWStr(
  766. ULONG IpAddress,
  767. WCHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1]
  768. )
  769. /*++
  770. Routine Description:
  771. Convert an IP address to a string.
  772. Arguments:
  773. IpAddress - IP Address to convert
  774. IpAddressString - resultant string.
  775. Return Value:
  776. None.
  777. --*/
  778. {
  779. CHAR IpAddressStr[NL_IP_ADDRESS_LENGTH+1];
  780. NetpIpAddressToStr( IpAddress, IpAddressStr );
  781. NetpCopyStrToWStr( IpAddressString, IpAddressStr );
  782. }
  783. NET_API_STATUS
  784. NetpDcBuildPing(
  785. IN BOOL PdcOnly,
  786. IN ULONG RequestCount,
  787. IN LPCWSTR UnicodeComputerName,
  788. IN LPCWSTR UnicodeUserName OPTIONAL,
  789. IN LPCSTR ResponseMailslotName,
  790. IN ULONG AllowableAccountControlBits,
  791. IN PSID RequestedDomainSid OPTIONAL,
  792. IN ULONG NtVersion,
  793. OUT PVOID *Message,
  794. OUT PULONG MessageSize
  795. )
  796. /*++
  797. Routine Description:
  798. Build the message to ping a DC to see if it exists.
  799. Copied from net\svcdlls\logonsv\netpdc.c
  800. Arguments:
  801. PdcOnly - True if only the PDC should respond.
  802. RequestCount - Retry count of this operation.
  803. UnicodeComputerName - Netbios computer name of the machine to respond to.
  804. UnicodeUserName - Account name of the user being pinged.
  805. If NULL, DC will always respond affirmatively.
  806. ResponseMailslotName - Name of the mailslot DC is to respond to.
  807. AllowableAccountControlBits - Mask of allowable account types for UnicodeUserName.
  808. RequestedDomainSid - Sid of the domain the message is destined to.
  809. NtVersion - Version of the message.
  810. 0: For backward compatibility.
  811. NETLOGON_NT_VERSION_5: for NT 5.0 message.
  812. NETLOGON_NT_VERSION_5EX: for extended NT 5.0 message
  813. Message - Returns the message to be sent to the DC in question.
  814. Buffer must be free using NetpMemoryFree().
  815. MessageSize - Returns the size (in bytes) of the returned message
  816. Return Value:
  817. NO_ERROR - Operation completed successfully;
  818. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  819. --*/
  820. {
  821. NET_API_STATUS NetStatus;
  822. LPSTR Where;
  823. PNETLOGON_SAM_LOGON_REQUEST SamLogonRequest = NULL;
  824. LPSTR OemComputerName = NULL;
  825. //
  826. // If only the PDC should respond,
  827. // build a primary query packet.
  828. //
  829. if ( PdcOnly ) {
  830. PNETLOGON_LOGON_QUERY LogonQuery;
  831. //
  832. // Allocate memory for the primary query message.
  833. //
  834. SamLogonRequest = NetpMemoryAllocate( sizeof(NETLOGON_LOGON_QUERY) );
  835. if( SamLogonRequest == NULL ) {
  836. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  837. goto Cleanup;
  838. }
  839. LogonQuery = (PNETLOGON_LOGON_QUERY)SamLogonRequest;
  840. //
  841. // Translate to get an Oem computer name.
  842. //
  843. #ifndef WIN32_CHICAGO
  844. OemComputerName = NetpLogonUnicodeToOem( (LPWSTR)UnicodeComputerName );
  845. #else
  846. OemComputerName = MyNetpLogonUnicodeToOem( (LPWSTR)UnicodeComputerName );
  847. #endif
  848. if ( OemComputerName == NULL ) {
  849. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  850. goto Cleanup;
  851. }
  852. //
  853. // Build the query message.
  854. //
  855. LogonQuery->Opcode = LOGON_PRIMARY_QUERY;
  856. Where = LogonQuery->ComputerName;
  857. NetpLogonPutOemString(
  858. OemComputerName,
  859. sizeof(LogonQuery->ComputerName),
  860. &Where );
  861. NetpLogonPutOemString(
  862. (LPSTR) ResponseMailslotName,
  863. sizeof(LogonQuery->MailslotName),
  864. &Where );
  865. NetpLogonPutUnicodeString(
  866. (LPWSTR) UnicodeComputerName,
  867. sizeof( LogonQuery->UnicodeComputerName ),
  868. &Where );
  869. // Join common code to add NT 5 specific data.
  870. //
  871. // If any DC can respond,
  872. // build a logon query packet.
  873. //
  874. } else {
  875. ULONG DomainSidSize;
  876. //
  877. // Allocate memory for the logon request message.
  878. //
  879. #ifndef WIN32_CHICAGO
  880. if ( RequestedDomainSid != NULL ) {
  881. DomainSidSize = RtlLengthSid( RequestedDomainSid );
  882. } else {
  883. DomainSidSize = 0;
  884. }
  885. #else // WIN32_CHICAGO
  886. DomainSidSize = 0;
  887. #endif // WIN32_CHICAGO
  888. SamLogonRequest = NetpMemoryAllocate(
  889. sizeof(NETLOGON_SAM_LOGON_REQUEST) +
  890. DomainSidSize +
  891. sizeof(DWORD) // for SID alignment on 4 byte boundary
  892. );
  893. if( SamLogonRequest == NULL ) {
  894. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  895. goto Cleanup;
  896. }
  897. //
  898. // Build the query message.
  899. //
  900. SamLogonRequest->Opcode = LOGON_SAM_LOGON_REQUEST;
  901. SamLogonRequest->RequestCount = (WORD) RequestCount;
  902. Where = (PCHAR) &SamLogonRequest->UnicodeComputerName;
  903. NetpLogonPutUnicodeString(
  904. (LPWSTR) UnicodeComputerName,
  905. sizeof(SamLogonRequest->UnicodeComputerName),
  906. &Where );
  907. NetpLogonPutUnicodeString(
  908. (LPWSTR) UnicodeUserName,
  909. sizeof(SamLogonRequest->UnicodeUserName),
  910. &Where );
  911. NetpLogonPutOemString(
  912. (LPSTR) ResponseMailslotName,
  913. sizeof(SamLogonRequest->MailslotName),
  914. &Where );
  915. NetpLogonPutBytes(
  916. &AllowableAccountControlBits,
  917. sizeof(SamLogonRequest->AllowableAccountControlBits),
  918. &Where );
  919. //
  920. // Place domain SID in the message.
  921. //
  922. NetpLogonPutBytes( &DomainSidSize, sizeof(DomainSidSize), &Where );
  923. NetpLogonPutDomainSID( RequestedDomainSid, DomainSidSize, &Where );
  924. }
  925. NetpLogonPutNtToken( &Where, NtVersion );
  926. //
  927. // Return the message to the caller.
  928. //
  929. *Message = SamLogonRequest;
  930. *MessageSize = (ULONG)(Where - (PCHAR)SamLogonRequest);
  931. SamLogonRequest = NULL;
  932. NetStatus = NO_ERROR;
  933. //
  934. // Free locally used resources.
  935. //
  936. Cleanup:
  937. if ( OemComputerName != NULL ) {
  938. NetpMemoryFree( OemComputerName );
  939. }
  940. if ( SamLogonRequest != NULL ) {
  941. NetpMemoryFree( SamLogonRequest );
  942. }
  943. return NetStatus;
  944. }