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.

1898 lines
40 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // code common to several pages
  4. //
  5. // 12-16-97 sburns
  6. #include "headers.hxx"
  7. #include "common.hpp"
  8. #include "resource.h"
  9. #include "state.hpp"
  10. #include "ds.hpp"
  11. #include <DiagnoseDcNotFound.hpp>
  12. // Creates the fonts for setLargeFonts().
  13. //
  14. // hDialog - handle to a dialog to be used to retrieve a device
  15. // context.
  16. //
  17. // bigBoldFont - receives the handle of the big bold font created.
  18. void
  19. InitFonts(
  20. HWND hDialog,
  21. HFONT& bigBoldFont)
  22. {
  23. ASSERT(Win::IsWindow(hDialog));
  24. HRESULT hr = S_OK;
  25. do
  26. {
  27. NONCLIENTMETRICS ncm;
  28. memset(&ncm, 0, sizeof(ncm));
  29. ncm.cbSize = sizeof(ncm);
  30. hr = Win::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
  31. BREAK_ON_FAILED_HRESULT(hr);
  32. LOGFONT bigBoldLogFont = ncm.lfMessageFont;
  33. bigBoldLogFont.lfWeight = FW_BOLD;
  34. String fontName = String::load(IDS_BIG_BOLD_FONT_NAME);
  35. // ensure null termination 260237
  36. memset(bigBoldLogFont.lfFaceName, 0, LF_FACESIZE * sizeof(TCHAR));
  37. size_t fnLen = fontName.length();
  38. fontName.copy(
  39. bigBoldLogFont.lfFaceName,
  40. // don't copy over the last null
  41. min(LF_FACESIZE - 1, fnLen));
  42. unsigned fontSize = 0;
  43. String::load(IDS_BIG_BOLD_FONT_SIZE).convert(fontSize);
  44. ASSERT(fontSize);
  45. HDC hdc = 0;
  46. hr = Win::GetDC(hDialog, hdc);
  47. BREAK_ON_FAILED_HRESULT(hr);
  48. bigBoldLogFont.lfHeight =
  49. - ::MulDiv(
  50. static_cast<int>(fontSize),
  51. Win::GetDeviceCaps(hdc, LOGPIXELSY),
  52. 72);
  53. hr = Win::CreateFontIndirect(bigBoldLogFont, bigBoldFont);
  54. BREAK_ON_FAILED_HRESULT(hr);
  55. Win::ReleaseDC(hDialog, hdc);
  56. }
  57. while (0);
  58. }
  59. void
  60. SetControlFont(HWND parentDialog, int controlID, HFONT font)
  61. {
  62. ASSERT(Win::IsWindow(parentDialog));
  63. ASSERT(controlID);
  64. ASSERT(font);
  65. HWND control = Win::GetDlgItem(parentDialog, controlID);
  66. if (control)
  67. {
  68. Win::SetWindowFont(control, font, true);
  69. }
  70. }
  71. void
  72. SetLargeFont(HWND dialog, int bigBoldResID)
  73. {
  74. ASSERT(Win::IsWindow(dialog));
  75. ASSERT(bigBoldResID);
  76. static HFONT bigBoldFont = 0;
  77. if (!bigBoldFont)
  78. {
  79. InitFonts(dialog, bigBoldFont);
  80. }
  81. SetControlFont(dialog, bigBoldResID, bigBoldFont);
  82. }
  83. bool
  84. ValidateDomainDnsNameSyntax(
  85. HWND dialog,
  86. int editResID,
  87. bool warnOnNonRFC,
  88. bool* isNonRFC)
  89. {
  90. return
  91. ValidateDomainDnsNameSyntax(
  92. dialog,
  93. String(),
  94. editResID,
  95. warnOnNonRFC,
  96. isNonRFC);
  97. }
  98. // return true if the name is a reserved name, false otherwise. If true, also
  99. // set message to an error message describing the problem.
  100. bool
  101. IsReservedDnsName(const String& dnsName, String& message)
  102. {
  103. LOG_FUNCTION2(IsReservedDnsName, dnsName);
  104. ASSERT(!dnsName.empty());
  105. message.erase();
  106. bool result = false;
  107. // We're still trying to decide if we should restrict these names
  108. //
  109. // // names with these as the last labels are illegal.
  110. //
  111. // static const String RESERVED[] =
  112. // {
  113. // L"in-addr.arpa",
  114. // L"ipv6.int",
  115. //
  116. // // RFC 2606 documents these:
  117. //
  118. // L"test",
  119. // L"example",
  120. // L"invalid",
  121. // L"localhost",
  122. // L"example.com",
  123. // L"example.org",
  124. // L"example.net"
  125. // };
  126. //
  127. // String name(dnsName);
  128. // name.to_upper();
  129. // if (name[name.length() - 1] == L'.')
  130. // {
  131. // // remove the trailing dot
  132. //
  133. // name.resize(name.length() - 1);
  134. // }
  135. //
  136. // for (int i = 0; i < sizeof(RESERVED) / sizeof(String); ++i)
  137. // {
  138. // String res = RESERVED[i];
  139. // res.to_upper();
  140. //
  141. // size_t pos = name.rfind(res);
  142. //
  143. // if (pos == String::npos)
  144. // {
  145. // continue;
  146. // }
  147. //
  148. // if (pos == 0 && name.length() == res.length())
  149. // {
  150. // ASSERT(name == res);
  151. //
  152. // result = true;
  153. // message =
  154. // String::format(
  155. // IDS_RESERVED_NAME,
  156. // dnsName.c_str());
  157. // break;
  158. // }
  159. //
  160. // if ((pos == name.length() - res.length()) && (name[pos - 1] == L'.'))
  161. // {
  162. // // the name has reserved as a suffix.
  163. //
  164. // result = true;
  165. // message =
  166. // String::format(
  167. // IDS_RESERVED_NAME_SUFFIX,
  168. // dnsName.c_str(),
  169. // RESERVED[i].c_str());
  170. // break;
  171. // }
  172. // }
  173. return result;
  174. }
  175. bool
  176. ValidateDomainDnsNameSyntax(
  177. HWND dialog,
  178. const String& domainName,
  179. int editResID,
  180. bool warnOnNonRFC,
  181. bool* isNonRFC)
  182. {
  183. LOG_FUNCTION(ValidateDomainDnsNameSyntax);
  184. ASSERT(Win::IsWindow(dialog));
  185. ASSERT(editResID > 0);
  186. bool valid = false;
  187. String message;
  188. String dnsName =
  189. domainName.empty()
  190. ? Win::GetTrimmedDlgItemText(dialog, editResID)
  191. : domainName;
  192. if (isNonRFC)
  193. {
  194. *isNonRFC = false;
  195. }
  196. LOG(L"validating " + dnsName);
  197. switch (
  198. Dns::ValidateDnsNameSyntax(
  199. dnsName,
  200. DNS_DOMAIN_NAME_MAX_LIMIT_DUE_TO_POLICY,
  201. DNS_DOMAIN_NAME_MAX_LIMIT_DUE_TO_POLICY_UTF8) )
  202. {
  203. case Dns::NON_RFC:
  204. {
  205. if (isNonRFC)
  206. {
  207. *isNonRFC = true;
  208. }
  209. if (warnOnNonRFC)
  210. {
  211. // warn about non-rfc names
  212. String msg = String::format(IDS_NON_RFC_NAME, dnsName.c_str());
  213. if (!State::GetInstance().RunHiddenUnattended())
  214. {
  215. popup.Info(
  216. dialog,
  217. msg);
  218. }
  219. else
  220. {
  221. LOG(msg);
  222. }
  223. }
  224. // fall through
  225. //lint -e616 allow fall thru
  226. }
  227. case Dns::VALID:
  228. {
  229. valid = !IsReservedDnsName(dnsName, message);
  230. break;
  231. }
  232. case Dns::TOO_LONG:
  233. {
  234. message =
  235. String::format(
  236. IDS_DNS_NAME_TOO_LONG,
  237. dnsName.c_str(),
  238. DNS_DOMAIN_NAME_MAX_LIMIT_DUE_TO_POLICY,
  239. DNS_DOMAIN_NAME_MAX_LIMIT_DUE_TO_POLICY_UTF8);
  240. break;
  241. }
  242. case Dns::NUMERIC:
  243. case Dns::BAD_CHARS:
  244. case Dns::INVALID:
  245. default:
  246. {
  247. message =
  248. String::format(
  249. IDS_BAD_DNS_SYNTAX,
  250. dnsName.c_str(),
  251. Dns::MAX_LABEL_LENGTH);
  252. break;
  253. }
  254. }
  255. if (!valid)
  256. {
  257. popup.Gripe(dialog, editResID, message);
  258. }
  259. return valid;
  260. }
  261. HRESULT
  262. GetDcName(const String& domainName, String& resultDcName)
  263. {
  264. LOG_FUNCTION(GetDcName);
  265. ASSERT(!domainName.empty());
  266. resultDcName.erase();
  267. HRESULT hr = S_OK;
  268. do
  269. {
  270. DOMAIN_CONTROLLER_INFO* info = 0;
  271. hr =
  272. MyDsGetDcName(
  273. 0,
  274. domainName,
  275. // pass the force rediscovery flag to make sure we don't pick up
  276. // a dc that is down 262221
  277. DS_DIRECTORY_SERVICE_REQUIRED | DS_FORCE_REDISCOVERY,
  278. info);
  279. BREAK_ON_FAILED_HRESULT(hr);
  280. ASSERT(info->DomainControllerName);
  281. if (info->DomainControllerName)
  282. {
  283. // we found an NT5 domain
  284. resultDcName =
  285. Computer::RemoveLeadingBackslashes(info->DomainControllerName);
  286. LOG(resultDcName);
  287. }
  288. ::NetApiBufferFree(info);
  289. if (resultDcName.empty())
  290. {
  291. hr = Win32ToHresult(ERROR_DOMAIN_CONTROLLER_NOT_FOUND);
  292. }
  293. BREAK_ON_FAILED_HRESULT(hr);
  294. }
  295. while (0);
  296. return hr;
  297. }
  298. HRESULT
  299. BrowserSetComputer(const SmartInterface<IDsBrowseDomainTree>& browser)
  300. {
  301. LOG_FUNCTION(BrowserSetComputer);
  302. HRESULT hr = S_OK;
  303. do
  304. {
  305. State& state = State::GetInstance();
  306. String username =
  307. MassageUserName(
  308. state.GetUserDomainName(),
  309. state.GetUsername());
  310. EncodedString password = state.GetPassword();
  311. String computer;
  312. hr =
  313. GetDcName(
  314. state.GetUserDomainName(),
  315. computer);
  316. BREAK_ON_FAILED_HRESULT(hr);
  317. LOG(L"Calling IDsBrowseDomainTree::SetComputer");
  318. LOG(String::format(L"pszComputerName : %1", computer.c_str()));
  319. LOG(String::format(L"pszUserName : %1", username.c_str()));
  320. WCHAR* cleartext = password.GetDecodedCopy();
  321. hr =
  322. browser->SetComputer(
  323. computer.c_str(),
  324. username.c_str(),
  325. cleartext);
  326. ::ZeroMemory(cleartext, sizeof(WCHAR) * password.GetLength());
  327. delete[] cleartext;
  328. LOG_HRESULT(hr);
  329. BREAK_ON_FAILED_HRESULT(hr);
  330. }
  331. while (0);
  332. return hr;
  333. }
  334. HRESULT
  335. ReadDomainsHelper(bool bindWithCredentials, Callback* callback)
  336. {
  337. LOG_FUNCTION(ReadDomainsHelper);
  338. HRESULT hr = S_OK;
  339. do
  340. {
  341. hr = ::CoInitialize(0);
  342. BREAK_ON_FAILED_HRESULT(hr);
  343. SmartInterface<IDsBrowseDomainTree> browser;
  344. hr = browser.AcquireViaCreateInstance(
  345. CLSID_DsDomainTreeBrowser,
  346. 0,
  347. CLSCTX_INPROC_SERVER,
  348. IID_IDsBrowseDomainTree);
  349. BREAK_ON_FAILED_HRESULT(hr);
  350. if (bindWithCredentials)
  351. {
  352. LOG(L"binding with credentials");
  353. hr = BrowserSetComputer(browser);
  354. BREAK_ON_FAILED_HRESULT(hr);
  355. }
  356. LOG(L"Calling IDsBrowseDomainTree::GetDomains");
  357. DOMAIN_TREE* tree = 0;
  358. hr = browser->GetDomains(&tree, 0);
  359. BREAK_ON_FAILED_HRESULT(hr);
  360. ASSERT(tree);
  361. if (tree && callback)
  362. {
  363. //lint -e534 ignore return value
  364. callback->Execute(tree);
  365. }
  366. hr = browser->FreeDomains(&tree);
  367. ASSERT(SUCCEEDED(hr));
  368. }
  369. while (0);
  370. return hr;
  371. }
  372. String
  373. BrowseForDomain(HWND parent)
  374. {
  375. LOG_FUNCTION(BrowseForDomain);
  376. ASSERT(Win::IsWindow(parent));
  377. String retval;
  378. HRESULT hr = S_OK;
  379. do
  380. {
  381. Win::WaitCursor cursor;
  382. hr = ::CoInitialize(0);
  383. BREAK_ON_FAILED_HRESULT(hr);
  384. // CODEWORK: the credential page could cache an instance of the browser,
  385. // rebuilding and setting the search root when only when new credentials
  386. // are entered. Since the browser caches the last result, this would
  387. // make subsequent retreivals of the domain much faster.
  388. // addendum: tho the revised browser seems pretty quick
  389. SmartInterface<IDsBrowseDomainTree> browser;
  390. hr = browser.AcquireViaCreateInstance(
  391. CLSID_DsDomainTreeBrowser,
  392. 0,
  393. CLSCTX_INPROC_SERVER,
  394. IID_IDsBrowseDomainTree);
  395. BREAK_ON_FAILED_HRESULT(hr);
  396. hr = BrowserSetComputer(browser);
  397. BREAK_ON_FAILED_HRESULT(hr);
  398. PWSTR result = 0;
  399. hr = browser->BrowseTo(parent, &result, 0);
  400. BREAK_ON_FAILED_HRESULT(hr);
  401. if (result)
  402. {
  403. retval = result;
  404. ::CoTaskMemFree(result);
  405. }
  406. }
  407. while (0);
  408. if (FAILED(hr))
  409. {
  410. popup.Error(parent, hr, IDS_CANT_BROWSE_FOREST);
  411. }
  412. return retval;
  413. }
  414. class RootDomainCollectorCallback : public Callback
  415. {
  416. public:
  417. explicit
  418. RootDomainCollectorCallback(StringList& domains_)
  419. :
  420. Callback(),
  421. domains(domains_)
  422. {
  423. ASSERT(domains.empty());
  424. domains.clear();
  425. }
  426. virtual
  427. ~RootDomainCollectorCallback()
  428. {
  429. }
  430. virtual
  431. int
  432. Execute(void* param)
  433. {
  434. ASSERT(param);
  435. // root domains are all those on the sibling link of the root
  436. // node of the domain tree.
  437. DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
  438. if (tree)
  439. {
  440. for (
  441. DOMAIN_DESC* desc = &(tree->aDomains[0]);
  442. desc;
  443. desc = desc->pdNextSibling)
  444. {
  445. LOG(
  446. String::format(
  447. L"pushing root domain %1",
  448. desc->pszName));
  449. domains.push_back(desc->pszName);
  450. }
  451. }
  452. return 0;
  453. }
  454. private:
  455. StringList& domains;
  456. };
  457. HRESULT
  458. ReadRootDomains(bool bindWithCredentials, StringList& domains)
  459. {
  460. LOG_FUNCTION(ReadRootDomains);
  461. RootDomainCollectorCallback rdcc(domains);
  462. return
  463. ReadDomainsHelper(
  464. bindWithCredentials,
  465. &rdcc);
  466. }
  467. bool
  468. IsRootDomain(bool bindWithCredentials)
  469. {
  470. LOG_FUNCTION(IsRootDomain);
  471. static bool computed = false;
  472. static bool isRoot = false;
  473. if (!computed)
  474. {
  475. StringList domains;
  476. if (SUCCEEDED(ReadRootDomains(bindWithCredentials, domains)))
  477. {
  478. String domain =
  479. State::GetInstance().GetComputer().GetDomainDnsName();
  480. for (
  481. StringList::iterator i = domains.begin();
  482. i != domains.end();
  483. ++i)
  484. {
  485. if (Dns::CompareNames((*i), domain) == DnsNameCompareEqual)
  486. {
  487. LOG(String::format(L"found match: %1", (*i).c_str()));
  488. ASSERT(!(*i).empty());
  489. isRoot = true;
  490. break;
  491. }
  492. }
  493. computed = true;
  494. }
  495. }
  496. LOG(isRoot ? L"is root" : L"is not root");
  497. return isRoot;
  498. }
  499. // first = child, second = parent
  500. typedef std::pair<String, String> StringPair;
  501. typedef
  502. std::list<
  503. StringPair,
  504. Burnslib::Heap::Allocator<StringPair> >
  505. ChildParentList;
  506. class ChildDomainCollectorCallback : public Callback
  507. {
  508. public:
  509. explicit
  510. ChildDomainCollectorCallback(ChildParentList& domains_)
  511. :
  512. Callback(),
  513. domains(domains_)
  514. {
  515. ASSERT(domains.empty());
  516. domains.clear();
  517. }
  518. virtual
  519. ~ChildDomainCollectorCallback()
  520. {
  521. }
  522. virtual
  523. int
  524. Execute(void* param)
  525. {
  526. LOG_FUNCTION(ChildDomainCollectorCallback::Execute);
  527. ASSERT(param);
  528. DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
  529. if (tree)
  530. {
  531. typedef
  532. std::deque<
  533. DOMAIN_DESC*,
  534. Burnslib::Heap::Allocator<DOMAIN_DESC*> >
  535. DDDeque;
  536. std::stack<DOMAIN_DESC*, DDDeque> s;
  537. // first we push all the nodes for all root domains. These are
  538. // the nodes on the sibling link of the tree root. Hereafter,
  539. // the sibling link is only used to chase child domains.
  540. for (
  541. DOMAIN_DESC* desc = &(tree->aDomains[0]);
  542. desc;
  543. desc = desc->pdNextSibling)
  544. {
  545. LOG(
  546. String::format(
  547. L"pushing root domain %1",
  548. desc->pszName));
  549. s.push(desc);
  550. }
  551. // next, we work thru the stack, looking for nodes that have
  552. // nodes on their child links. When we find such a node, we
  553. // collect in the child list all the children on that link, and
  554. // push them so that they will in turn be evaluated.
  555. DWORD count = 0;
  556. while (!s.empty())
  557. {
  558. DOMAIN_DESC* desc = s.top();
  559. s.pop();
  560. ASSERT(desc);
  561. if (desc)
  562. {
  563. count++;
  564. LOG(String::format(L"evaluating %1", desc->pszName));
  565. String parentname = desc->pszName;
  566. for (
  567. DOMAIN_DESC* child = desc->pdChildList;
  568. child;
  569. child = child->pdNextSibling)
  570. {
  571. s.push(child);
  572. String childname = child->pszName;
  573. LOG(
  574. String::format(
  575. L"parent: %1 child: %2",
  576. parentname.c_str(),
  577. childname.c_str()));
  578. domains.push_back(std::make_pair(childname, parentname));
  579. }
  580. }
  581. }
  582. ASSERT(count == tree->dwCount);
  583. }
  584. return 0;
  585. }
  586. private:
  587. ChildParentList& domains;
  588. };
  589. HRESULT
  590. ReadChildDomains(bool bindWithCredentials, ChildParentList& domains)
  591. {
  592. LOG_FUNCTION(ReadChildDomains);
  593. ChildDomainCollectorCallback cdcc(domains);
  594. return
  595. ReadDomainsHelper(
  596. bindWithCredentials,
  597. &cdcc);
  598. }
  599. String
  600. GetParentDomainDnsName(
  601. const String& childDomainDNSName,
  602. bool bindWithCredentials)
  603. {
  604. LOG_FUNCTION2(GetParentDomainDnsName, childDomainDNSName);
  605. ASSERT(!childDomainDNSName.empty());
  606. ChildParentList domains;
  607. if (SUCCEEDED(ReadChildDomains(bindWithCredentials, domains)))
  608. {
  609. for (
  610. ChildParentList::iterator i = domains.begin();
  611. i != domains.end();
  612. ++i)
  613. {
  614. if (
  615. Dns::CompareNames(
  616. (*i).first,
  617. childDomainDNSName) == DnsNameCompareEqual)
  618. {
  619. LOG(
  620. String::format(
  621. L"found parent: %1",
  622. (*i).second.c_str()));
  623. ASSERT(!(*i).second.empty());
  624. return (*i).second;
  625. }
  626. }
  627. }
  628. LOG(L"domain is not a child");
  629. return String();
  630. }
  631. class DomainCollectorCallback : public Callback
  632. {
  633. public:
  634. explicit
  635. DomainCollectorCallback(StringList& domains_)
  636. :
  637. Callback(),
  638. domains(domains_)
  639. {
  640. ASSERT(domains.empty());
  641. domains.clear();
  642. }
  643. virtual
  644. ~DomainCollectorCallback()
  645. {
  646. }
  647. virtual
  648. int
  649. Execute(void* param)
  650. {
  651. LOG_FUNCTION(DomainCollectorCallback::Execute);
  652. ASSERT(param);
  653. DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
  654. if (tree)
  655. {
  656. for (DWORD i = 0; i < tree->dwCount; ++i)
  657. {
  658. PCWSTR name = tree->aDomains[i].pszName;
  659. LOG(String::format(L"domain found: %1", name));
  660. domains.push_back(name);
  661. }
  662. }
  663. return 0;
  664. }
  665. private:
  666. StringList& domains;
  667. };
  668. HRESULT
  669. ReadDomains(StringList& domains)
  670. {
  671. LOG_FUNCTION(ReadDomains);
  672. DomainCollectorCallback dcc(domains);
  673. return ReadDomainsHelper(true, &dcc);
  674. }
  675. String
  676. BrowseForFolder(HWND parent, int titleResID)
  677. {
  678. LOG_FUNCTION(BrowseForFolder);
  679. ASSERT(Win::IsWindow(parent));
  680. ASSERT(titleResID > 0);
  681. String result;
  682. HRESULT hr = S_OK;
  683. LPMALLOC pmalloc = 0;
  684. LPITEMIDLIST drives = 0;
  685. LPITEMIDLIST pidl = 0;
  686. do
  687. {
  688. hr = Win::SHGetMalloc(pmalloc);
  689. if (FAILED(hr) or !pmalloc)
  690. {
  691. break;
  692. }
  693. // get a pidl for the local drives (really My Computer)
  694. hr = Win::SHGetSpecialFolderLocation(parent, CSIDL_DRIVES, drives);
  695. BREAK_ON_FAILED_HRESULT(hr);
  696. BROWSEINFO info;
  697. memset(&info, 0, sizeof(info));
  698. String title = String::load(titleResID);
  699. wchar_t buf[MAX_PATH + 1];
  700. memset(buf, 0, sizeof(buf));
  701. info.hwndOwner = parent;
  702. info.pidlRoot = drives;
  703. info.pszDisplayName = buf;
  704. info.lpszTitle = title.c_str();
  705. info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS;
  706. pidl = Win::SHBrowseForFolder(info);
  707. if (pidl)
  708. {
  709. result = Win::SHGetPathFromIDList(pidl);
  710. }
  711. }
  712. while (0);
  713. if (pmalloc)
  714. {
  715. pmalloc->Free(pidl);
  716. pmalloc->Free(drives);
  717. pmalloc->Release();
  718. }
  719. return result;
  720. }
  721. bool
  722. CheckDriveType(const String& path)
  723. {
  724. LOG_FUNCTION(CheckDriveType);
  725. ASSERT(!path.empty());
  726. UINT type = Win::GetDriveType(path);
  727. switch (type)
  728. {
  729. case DRIVE_FIXED:
  730. {
  731. return true;
  732. }
  733. case DRIVE_REMOVABLE:
  734. {
  735. // only legal iff volume = system volume
  736. String vol = FS::GetRootFolder(path);
  737. String sys = FS::GetRootFolder(Win::GetSystemDirectory());
  738. if (vol.icompare(sys) == 0)
  739. {
  740. return true;
  741. }
  742. break;
  743. }
  744. default:
  745. {
  746. // all others bad
  747. break;
  748. }
  749. }
  750. return false;
  751. }
  752. bool
  753. ValidateDcInstallPath(
  754. const String& path,
  755. HWND parent,
  756. int editResID,
  757. bool requiresNTFS5)
  758. {
  759. LOG_FUNCTION(ValidateDcInstallPath);
  760. ASSERT(!path.empty());
  761. bool result = false;
  762. String message;
  763. do
  764. {
  765. if (
  766. (path.icompare(Win::GetWindowsDirectory()) == 0)
  767. || (path.icompare(Win::GetSystemWindowsDirectory()) == 0) )
  768. {
  769. message = String::format(IDS_PATH_IS_WINDIR, path.c_str());
  770. break;
  771. }
  772. if (path.icompare(Win::GetSystemDirectory()) == 0)
  773. {
  774. message = String::format(IDS_PATH_IS_SYSTEM32, path.c_str());
  775. break;
  776. }
  777. if (FS::GetPathSyntax(path) != FS::SYNTAX_ABSOLUTE_DRIVE)
  778. {
  779. message = String::format(IDS_BAD_PATH_FORMAT, path.c_str());
  780. break;
  781. }
  782. if (!CheckDriveType(path))
  783. {
  784. message = String::format(IDS_BAD_DRIVE_TYPE, path.c_str());
  785. break;
  786. }
  787. if (requiresNTFS5 && (FS::GetFileSystemType(path) != FS::NTFS5))
  788. {
  789. message = String::format(IDS_NOT_NTFS5, path.c_str());
  790. break;
  791. }
  792. // prohibit paths that contain mounted volumes 325264
  793. // even when they don't exist 435428
  794. String mountRoot;
  795. HRESULT hr = FS::GetVolumePathName(path, mountRoot);
  796. ASSERT(SUCCEEDED(hr));
  797. // '3' == length of root of a "normal" volume ("C:\")
  798. if (mountRoot.length() > 3)
  799. {
  800. message =
  801. String::format(
  802. IDS_PATH_CONTAINS_MOUNTED_VOLUMES,
  803. path.c_str(),
  804. mountRoot.c_str());
  805. break;
  806. }
  807. DWORD attrs = 0;
  808. hr = Win::GetFileAttributes(path, attrs);
  809. if (SUCCEEDED(hr))
  810. {
  811. // path exists
  812. // reject paths that refer an existing file
  813. if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
  814. {
  815. message = String::format(IDS_PATH_NOT_DIRECTORY, path.c_str());
  816. break;
  817. }
  818. if (!FS::IsFolderEmpty(path))
  819. {
  820. if (
  821. popup.MessageBox(
  822. parent,
  823. String::format(IDS_EMPTY_PATH, path.c_str()),
  824. MB_ICONWARNING | MB_YESNO) == IDNO)
  825. {
  826. // don't gripe...silently disapprove
  827. HWND edit = Win::GetDlgItem(parent, editResID);
  828. Win::SendMessage(edit, EM_SETSEL, 0, -1);
  829. Win::SetFocus(edit);
  830. break;
  831. }
  832. }
  833. }
  834. result = true;
  835. }
  836. while (0);
  837. if (!message.empty())
  838. {
  839. popup.Gripe(parent, editResID, message);
  840. }
  841. return result;
  842. }
  843. bool
  844. DoLabelValidation(
  845. HWND dialog,
  846. int editResID,
  847. int badSyntaxResID,
  848. bool gripeOnNonRFC = true,
  849. bool gripeOnNumericLabel = true)
  850. {
  851. LOG_FUNCTION(DoLabelValidation);
  852. ASSERT(Win::IsWindow(dialog));
  853. ASSERT(editResID > 0);
  854. bool valid = false;
  855. String label = Win::GetTrimmedDlgItemText(dialog, editResID);
  856. switch (Dns::ValidateDnsLabelSyntax(label))
  857. {
  858. case Dns::NON_RFC:
  859. {
  860. if (gripeOnNonRFC)
  861. {
  862. // warn about non-rfc names
  863. popup.Info(
  864. dialog,
  865. String::format(IDS_NON_RFC_NAME, label.c_str()));
  866. }
  867. // fall thru
  868. }
  869. case Dns::VALID:
  870. {
  871. valid = true;
  872. break;
  873. }
  874. case Dns::TOO_LONG:
  875. {
  876. popup.Gripe(
  877. dialog,
  878. editResID,
  879. String::format(
  880. IDS_DNS_LABEL_TOO_LONG,
  881. label.c_str(),
  882. Dns::MAX_LABEL_LENGTH));
  883. break;
  884. }
  885. case Dns::NUMERIC:
  886. {
  887. if (!gripeOnNumericLabel)
  888. {
  889. valid = true;
  890. break;
  891. }
  892. // fall thru
  893. }
  894. case Dns::BAD_CHARS:
  895. case Dns::INVALID:
  896. default:
  897. {
  898. popup.Gripe(
  899. dialog,
  900. editResID,
  901. String::format(badSyntaxResID, label.c_str()));
  902. break;
  903. }
  904. }
  905. return valid;
  906. }
  907. bool
  908. ValidateChildDomainLeafNameLabel(
  909. HWND dialog,
  910. int editResID,
  911. bool parentIsNonRFC)
  912. {
  913. LOG_FUNCTION(ValidateChildDomainLeafNameLabel);
  914. String name = Win::GetTrimmedDlgItemText(dialog, editResID);
  915. if (name.empty())
  916. {
  917. popup.Gripe(dialog, editResID, IDS_BLANK_LEAF_NAME);
  918. return false;
  919. }
  920. // If parent is non-RFC, then so will be the child. The user has been
  921. // griped to already, so don't gripe twice
  922. // 291558
  923. return
  924. DoLabelValidation(
  925. dialog,
  926. editResID,
  927. IDS_BAD_LABEL_SYNTAX,
  928. !parentIsNonRFC,
  929. // allow numeric labels. NTRAID#NTBUG9-321168-2001/02/20-sburns
  930. false);
  931. }
  932. bool
  933. ValidateSiteName(HWND dialog, int editResID)
  934. {
  935. LOG_FUNCTION(ValidateSiteName);
  936. String name = Win::GetTrimmedDlgItemText(dialog, editResID);
  937. if (name.empty())
  938. {
  939. popup.Gripe(dialog, editResID, IDS_BLANK_SITE_NAME);
  940. return false;
  941. }
  942. // A site name is just a DNS label
  943. return DoLabelValidation(dialog, editResID, IDS_BAD_SITE_SYNTAX);
  944. }
  945. void
  946. ShowTroubleshooter(HWND parent, int topicResID)
  947. {
  948. LOG_FUNCTION(ShowTroubleshooter);
  949. ASSERT(Win::IsWindow(parent));
  950. String file = String::load(IDS_HTML_HELP_FILE);
  951. String topic = String::load(topicResID);
  952. ASSERT(!topic.empty());
  953. LOG(String::format(L"file: %1 topic: %2", file.c_str(), topic.c_str()));
  954. Win::HtmlHelp(
  955. parent,
  956. file,
  957. HH_DISPLAY_TOPIC,
  958. reinterpret_cast<DWORD_PTR>(topic.c_str()));
  959. }
  960. String
  961. MassageUserName(const String& domainName, const String& userName)
  962. {
  963. LOG_FUNCTION2(MassageUserName, userName);
  964. ASSERT(!userName.empty());
  965. String result = userName;
  966. do
  967. {
  968. if (userName.find(L"@") != String::npos)
  969. {
  970. // userName includes an @, looks like a UPN to us, so don't
  971. // mess with it further 17699
  972. LOG(L"looks like a UPN");
  973. break;
  974. }
  975. if (!domainName.empty())
  976. {
  977. static const String DOMAIN_SEP_CHAR = L"\\";
  978. String name = userName;
  979. size_t pos = userName.find(DOMAIN_SEP_CHAR);
  980. if (pos != String::npos)
  981. {
  982. // remove the domain name in the userName string and replace it
  983. // with the domainName String
  984. name = userName.substr(pos + 1);
  985. ASSERT(!name.empty());
  986. }
  987. result = domainName + DOMAIN_SEP_CHAR + name;
  988. break;
  989. }
  990. // otherwise, the username appears as "foo\bar", so we don't touch it.
  991. }
  992. while (0);
  993. LOG(result);
  994. return result;
  995. }
  996. bool
  997. IsChildDomain(bool bindWithCredentials)
  998. {
  999. LOG_FUNCTION(IsChildDomain);
  1000. static bool computed = false;
  1001. static String parent;
  1002. if (!computed)
  1003. {
  1004. parent =
  1005. GetParentDomainDnsName(
  1006. State::GetInstance().GetComputer().GetDomainDnsName(),
  1007. bindWithCredentials);
  1008. computed = true;
  1009. }
  1010. LOG(
  1011. parent.empty()
  1012. ? String(L"not a child")
  1013. : String::format(L"is child. parent: %1", parent.c_str()));
  1014. return !parent.empty();
  1015. }
  1016. bool
  1017. IsForestRootDomain()
  1018. {
  1019. LOG_FUNCTION(IsForestRootDomain);
  1020. const Computer& c = State::GetInstance().GetComputer();
  1021. bool result = (c.GetDomainDnsName() == c.GetForestDnsName());
  1022. LOG(
  1023. String::format(
  1024. L"%1 a forest root domain",
  1025. result ? L"is" : L"is not"));
  1026. return result;
  1027. }
  1028. bool
  1029. ValidateDomainExists(HWND dialog, int editResID, String& domainDnsName)
  1030. {
  1031. return ValidateDomainExists(dialog, String(), editResID, domainDnsName);
  1032. }
  1033. bool
  1034. ValidateDomainExists(
  1035. HWND dialog,
  1036. const String& domainName,
  1037. int editResId,
  1038. String& domainDnsName)
  1039. {
  1040. LOG_FUNCTION(ValidateDomainExists);
  1041. ASSERT(Win::IsWindow(dialog));
  1042. ASSERT(editResId > 0);
  1043. String name =
  1044. domainName.empty()
  1045. ? Win::GetTrimmedDlgItemText(dialog, editResId)
  1046. : domainName;
  1047. // The invoking code should verify this condition, but we will handle
  1048. // it just in case.
  1049. ASSERT(!name.empty());
  1050. domainDnsName.erase();
  1051. Win::WaitCursor cursor;
  1052. bool valid = false;
  1053. DOMAIN_CONTROLLER_INFO* info = 0;
  1054. do
  1055. {
  1056. if (name.empty())
  1057. {
  1058. popup.Gripe(
  1059. dialog,
  1060. editResId,
  1061. String::load(IDS_MUST_ENTER_DOMAIN));
  1062. break;
  1063. }
  1064. // determine whether we can reach a DC for the domain, and whether it is
  1065. // a DS dc, and whether the name we're validating is truly the DNS name
  1066. // of the domain.
  1067. LOG(L"Validating " + name);
  1068. HRESULT hr =
  1069. MyDsGetDcName(
  1070. 0,
  1071. name,
  1072. // force discovery to ensure that we don't pick up a cached
  1073. // entry for a domain that may no longer exist
  1074. DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_PREFERRED,
  1075. info);
  1076. if (FAILED(hr) || !info)
  1077. {
  1078. ShowDcNotFoundErrorDialog(
  1079. dialog,
  1080. editResId,
  1081. name,
  1082. String::load(IDS_WIZARD_TITLE),
  1083. String::format(IDS_DC_NOT_FOUND, name.c_str()),
  1084. // name might be netbios
  1085. false);
  1086. break;
  1087. }
  1088. if (!(info->Flags & DS_DS_FLAG))
  1089. {
  1090. // domain is not a DS domain, or the locator could not find a DS DC
  1091. // for that domain, so the candidate name is bad
  1092. ShowDcNotFoundErrorDialog(
  1093. dialog,
  1094. editResId,
  1095. name,
  1096. String::load(IDS_WIZARD_TITLE),
  1097. String::format(IDS_DC_NOT_FOUND, name.c_str()),
  1098. // name might be netbios
  1099. false);
  1100. break;
  1101. }
  1102. LOG(name + L" refers to DS domain");
  1103. // here we rely on the fact that if DsGetDcName is provided a flat
  1104. // domain name, then info->DomainName will also be the (same,
  1105. // normalized) flat name. Likewise, if provided a DNS domain name,
  1106. // info->DomainName will be the (same, normalized) DNS domain name.
  1107. if (info->Flags & DS_DNS_DOMAIN_FLAG)
  1108. {
  1109. // we can infer that name is a DNS domain name, since
  1110. // info->DomainName is a DNS domain name.
  1111. LOG(L"name is the DNS name");
  1112. ASSERT(
  1113. Dns::CompareNames(name, info->DomainName)
  1114. == DnsNameCompareEqual);
  1115. valid = true;
  1116. break;
  1117. }
  1118. LOG(name + L" is not the DNS domain name");
  1119. // the candidate name is not the DNS name of the domain. Make another
  1120. // call to DsGetDcName to determine the DNS domain name so we can get
  1121. // the user to confirm.
  1122. DOMAIN_CONTROLLER_INFO* info2 = 0;
  1123. hr = MyDsGetDcName(0, name, DS_RETURN_DNS_NAME, info2);
  1124. if (FAILED(hr) || !info2)
  1125. {
  1126. ShowDcNotFoundErrorDialog(
  1127. dialog,
  1128. editResId,
  1129. name,
  1130. String::load(IDS_WIZARD_TITLE),
  1131. String::format(IDS_DC_NOT_FOUND, name.c_str()),
  1132. // name is probably netbios
  1133. false);
  1134. break;
  1135. }
  1136. String message =
  1137. String::format(
  1138. IDS_CONFIRM_DNS_NAME,
  1139. name.c_str(),
  1140. info2->DomainName);
  1141. if (
  1142. popup.MessageBox(
  1143. dialog,
  1144. message,
  1145. MB_YESNO) == IDYES)
  1146. {
  1147. domainDnsName = info2->DomainName;
  1148. // The user accept the dns name as the name he meant to enter. As one
  1149. // last step, we call DsGetDcName with the dns domain name. If this
  1150. // fails, then we are in the situation where a DC can be found with
  1151. // netbios but not dns. So the user has a dns configuration problem.
  1152. // 28298
  1153. DOMAIN_CONTROLLER_INFO* info3 = 0;
  1154. hr =
  1155. MyDsGetDcName(
  1156. 0,
  1157. domainDnsName,
  1158. // force discovery to ensure that we don't pick up a cached
  1159. // entry for a domain that may no longer exist
  1160. DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_PREFERRED,
  1161. info3);
  1162. if (FAILED(hr) || !info3)
  1163. {
  1164. ShowDcNotFoundErrorDialog(
  1165. dialog,
  1166. editResId,
  1167. domainDnsName,
  1168. String::load(IDS_WIZARD_TITLE),
  1169. String::format(IDS_DC_NOT_FOUND, domainDnsName.c_str()),
  1170. // we know the name is not netbios
  1171. true);
  1172. domainDnsName.erase();
  1173. break;
  1174. }
  1175. ::NetApiBufferFree(info3);
  1176. valid = true;
  1177. }
  1178. // the user rejected the dns name, so they are admitting that what
  1179. // they entered was bogus. Don't pop up an error box in this case,
  1180. // as we have pestered the user enough.
  1181. ::NetApiBufferFree(info2);
  1182. }
  1183. while (0);
  1184. if (info)
  1185. {
  1186. ::NetApiBufferFree(info);
  1187. }
  1188. #ifdef DBG
  1189. if (!valid)
  1190. {
  1191. ASSERT(domainDnsName.empty());
  1192. }
  1193. #endif
  1194. return valid;
  1195. }
  1196. bool
  1197. ValidateDomainDoesNotExist(
  1198. HWND dialog,
  1199. int editResID)
  1200. {
  1201. return ValidateDomainDoesNotExist(dialog, String(), editResID);
  1202. }
  1203. bool
  1204. ValidateDomainDoesNotExist(
  1205. HWND dialog,
  1206. const String& domainName,
  1207. int editResID)
  1208. {
  1209. LOG_FUNCTION(ValidateDomainDoesNotExist);
  1210. ASSERT(Win::IsWindow(dialog));
  1211. ASSERT(editResID > 0);
  1212. // this can take awhile.
  1213. Win::WaitCursor cursor;
  1214. String name =
  1215. domainName.empty()
  1216. ? Win::GetTrimmedDlgItemText(dialog, editResID)
  1217. : domainName;
  1218. // The invoking code should verify this condition, but we will handle
  1219. // it just in case.
  1220. ASSERT(!name.empty());
  1221. bool valid = true;
  1222. String message;
  1223. do
  1224. {
  1225. if (name.empty())
  1226. {
  1227. message = String::load(IDS_MUST_ENTER_DOMAIN);
  1228. valid = false;
  1229. break;
  1230. }
  1231. if (IsDomainReachable(name) || DS::IsDomainNameInUse(name))
  1232. {
  1233. message = String::format(IDS_DOMAIN_NAME_IN_USE, name.c_str());
  1234. valid = false;
  1235. break;
  1236. }
  1237. // otherwise the domain does not exist
  1238. }
  1239. while (0);
  1240. if (!valid)
  1241. {
  1242. popup.Gripe(dialog, editResID, message);
  1243. }
  1244. return valid;
  1245. }
  1246. void
  1247. DisableConsoleLocking()
  1248. {
  1249. LOG_FUNCTION(disableConsoleLocking);
  1250. HRESULT hr = S_OK;
  1251. do
  1252. {
  1253. BOOL screenSaverEnabled = FALSE;
  1254. hr =
  1255. Win::SystemParametersInfo(
  1256. SPI_GETSCREENSAVEACTIVE,
  1257. 0,
  1258. &screenSaverEnabled,
  1259. 0);
  1260. BREAK_ON_FAILED_HRESULT(hr);
  1261. if (screenSaverEnabled)
  1262. {
  1263. // disable it.
  1264. screenSaverEnabled = FALSE;
  1265. hr =
  1266. Win::SystemParametersInfo(
  1267. SPI_SETSCREENSAVEACTIVE,
  1268. 0,
  1269. &screenSaverEnabled,
  1270. SPIF_SENDCHANGE);
  1271. ASSERT(SUCCEEDED(hr));
  1272. }
  1273. }
  1274. while (0);
  1275. // turn off lock computer option in winlogon
  1276. do
  1277. {
  1278. RegistryKey key;
  1279. hr =
  1280. key.Create(
  1281. HKEY_LOCAL_MACHINE,
  1282. L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
  1283. BREAK_ON_FAILED_HRESULT(hr);
  1284. // '2' means "disable for this session, reset to 0 on reboot."
  1285. hr = key.SetValue(L"DisableLockWorkstation", 2);
  1286. BREAK_ON_FAILED_HRESULT(hr);
  1287. }
  1288. while (0);
  1289. }
  1290. void
  1291. EnableConsoleLocking()
  1292. {
  1293. LOG_FUNCTION(EnableConsoleLocking);
  1294. #ifdef DBG
  1295. State& state = State::GetInstance();
  1296. ASSERT(
  1297. state.GetRunContext() != State::PDC_UPGRADE and
  1298. state.GetRunContext() != State::BDC_UPGRADE);
  1299. #endif
  1300. // CODEWORK: we don't re-enable the screensaver (we need to remember
  1301. // if it was enabled when we called DisableConsoleLocking)
  1302. HRESULT hr = S_OK;
  1303. do
  1304. {
  1305. RegistryKey key;
  1306. hr =
  1307. key.Create(
  1308. HKEY_LOCAL_MACHINE,
  1309. L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
  1310. BREAK_ON_FAILED_HRESULT(hr);
  1311. // 0 means "enable"
  1312. hr = key.SetValue(L"DisableLockWorkstation", 0);
  1313. BREAK_ON_FAILED_HRESULT(hr);
  1314. }
  1315. while (0);
  1316. }
  1317. bool
  1318. CheckDiskSpace(const String& path, unsigned minSpaceMB)
  1319. {
  1320. LOG_FUNCTION(checkDiskSpace);
  1321. ASSERT(FS::IsValidPath(path));
  1322. String vol = FS::GetRootFolder(path);
  1323. ULONGLONG spaceInBytes;
  1324. memset(&spaceInBytes, 0, sizeof(ULONGLONG));
  1325. HRESULT hr = FS::GetAvailableSpace(vol, spaceInBytes);
  1326. if (SUCCEEDED(hr))
  1327. {
  1328. ULONGLONG spaceInMb = spaceInBytes / (1 << 20);
  1329. if (spaceInMb >= minSpaceMB)
  1330. {
  1331. return true;
  1332. }
  1333. }
  1334. return false;
  1335. }
  1336. String
  1337. GetFirstNtfs5HardDrive()
  1338. {
  1339. LOG_FUNCTION(GetFirstNtfs5HardDrive);
  1340. String result;
  1341. do
  1342. {
  1343. StringVector dl;
  1344. HRESULT hr = FS::GetValidDrives(std::back_inserter(dl));
  1345. BREAK_ON_FAILED_HRESULT(hr);
  1346. ASSERT(dl.size());
  1347. for (
  1348. StringVector::iterator i = dl.begin();
  1349. i != dl.end();
  1350. ++i)
  1351. {
  1352. LOG(*i);
  1353. if (
  1354. FS::GetFileSystemType(*i) == FS::NTFS5
  1355. && Win::GetDriveType(*i) == DRIVE_FIXED )
  1356. {
  1357. // found one. good to go
  1358. LOG(String::format(L"%1 is NTFS5", i->c_str()));
  1359. result = *i;
  1360. break;
  1361. }
  1362. }
  1363. }
  1364. while (0);
  1365. LOG(result);
  1366. return result;
  1367. }
  1368. bool
  1369. ConfirmNetbiosLookingNameIsReallyDnsName(HWND parentDialog, int editResID)
  1370. {
  1371. ASSERT(Win::IsWindow(parentDialog));
  1372. ASSERT(editResID > 0);
  1373. // check if the name is a single DNS label (a single label with a trailing
  1374. // dot does not count. If the user is DNS-saavy enough to use an absolute
  1375. // DNS name, then we will pester him no further.)
  1376. String domain = Win::GetTrimmedDlgItemText(parentDialog, editResID);
  1377. if (domain.find(L'.') == String::npos)
  1378. {
  1379. // no dot found: must be a single label
  1380. if (
  1381. popup.MessageBox(
  1382. parentDialog,
  1383. String::format(
  1384. IDS_CONFIRM_NETBIOS_LOOKING_NAME,
  1385. domain.c_str(),
  1386. domain.c_str()),
  1387. MB_YESNO | MB_DEFBUTTON2) == IDNO)
  1388. {
  1389. // user goofed. or we frightened them.
  1390. HWND edit = Win::GetDlgItem(parentDialog, editResID);
  1391. Win::SendMessage(edit, EM_SETSEL, 0, -1);
  1392. Win::SetFocus(edit);
  1393. return false;
  1394. }
  1395. }
  1396. return true;
  1397. }
  1398. bool
  1399. ComputerWasRenamedAndNeedsReboot()
  1400. {
  1401. LOG_FUNCTION(ComputerWasRenamedAndNeedsReboot);
  1402. bool result = false;
  1403. do
  1404. {
  1405. String active = Computer::GetActivePhysicalNetbiosName();
  1406. String future = Computer::GetFuturePhysicalNetbiosName();
  1407. if (active.icompare(future) != 0)
  1408. {
  1409. // a name change is pending reboot.
  1410. LOG(L"netbios name was changed");
  1411. LOG(active);
  1412. LOG(future);
  1413. result = true;
  1414. break;
  1415. }
  1416. // At this point, the netbios names are the same, or there is no future
  1417. // netbios name. So check the DNS names.
  1418. if (IsTcpIpInstalled())
  1419. {
  1420. // DNS names only exist if tcp/ip is installed.
  1421. active = Computer::GetActivePhysicalFullDnsName();
  1422. future = Computer::GetFuturePhysicalFullDnsName();
  1423. if (Dns::CompareNames(active, future) == DnsNameCompareNotEqual)
  1424. {
  1425. LOG(L"dns name was changed");
  1426. LOG(active);
  1427. LOG(future);
  1428. result = true;
  1429. break;
  1430. }
  1431. }
  1432. // At this point, we have confirmed that there is no pending name
  1433. // change.
  1434. LOG(L"No pending computer name change");
  1435. }
  1436. while (0);
  1437. LOG_BOOL(result);
  1438. return result;
  1439. }
  1440. String
  1441. GetForestName(const String& domain, HRESULT* hrOut)
  1442. {
  1443. LOG_FUNCTION2(GetForestName, domain);
  1444. ASSERT(!domain.empty());
  1445. String dnsForestName;
  1446. DOMAIN_CONTROLLER_INFO* info = 0;
  1447. HRESULT hr =
  1448. MyDsGetDcName(
  1449. 0,
  1450. domain,
  1451. DS_RETURN_DNS_NAME,
  1452. info);
  1453. if (SUCCEEDED(hr) && info)
  1454. {
  1455. ASSERT(info->DnsForestName);
  1456. if (info->DnsForestName)
  1457. {
  1458. dnsForestName = info->DnsForestName;
  1459. }
  1460. ::NetApiBufferFree(info);
  1461. }
  1462. if (hrOut)
  1463. {
  1464. *hrOut = hr;
  1465. }
  1466. return dnsForestName;
  1467. }