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.

440 lines
8.9 KiB

  1. // Copyright (C) 2000 Microsoft Corporation
  2. //
  3. // Check availability of ports used by Active Directory
  4. //
  5. // 1 Nov 2000 sburns
  6. #include "headers.hxx"
  7. #include "state.hpp"
  8. #include "resource.h"
  9. #include "CheckPortAvailability.hpp"
  10. static const DWORD HELP_MAP[] =
  11. {
  12. 0, 0
  13. };
  14. PortsUnavailableErrorDialog::PortsUnavailableErrorDialog(
  15. StringList& portsInUseList_)
  16. :
  17. Dialog(IDD_PORTS_IN_USE_ERROR, HELP_MAP),
  18. portsInUseList(portsInUseList_)
  19. {
  20. LOG_CTOR(PortsUnavailableErrorDialog);
  21. ASSERT(portsInUseList.size());
  22. }
  23. PortsUnavailableErrorDialog::~PortsUnavailableErrorDialog()
  24. {
  25. LOG_DTOR(PortsUnavailableErrorDialog);
  26. }
  27. void
  28. PortsUnavailableErrorDialog::OnInit()
  29. {
  30. LOG_FUNCTION(PortsUnavailableErrorDialog::OnInit);
  31. // Load up the edit box with the DNs we aliased in the ctor.
  32. String text;
  33. for (
  34. StringList::iterator i = portsInUseList.begin();
  35. i != portsInUseList.end();
  36. ++i)
  37. {
  38. text += *i + L"\r\n";
  39. }
  40. Win::SetDlgItemText(hwnd, IDC_PORT_LIST, text);
  41. }
  42. bool
  43. PortsUnavailableErrorDialog::OnCommand(
  44. HWND /* windowFrom */ ,
  45. unsigned controlIDFrom,
  46. unsigned code)
  47. {
  48. // LOG_FUNCTION(PortsUnavailableErrorDialog::OnCommand);
  49. if (code == BN_CLICKED)
  50. {
  51. switch (controlIDFrom)
  52. {
  53. case IDOK:
  54. case IDCANCEL:
  55. {
  56. Win::EndDialog(hwnd, controlIDFrom);
  57. return true;
  58. }
  59. default:
  60. {
  61. // do nothing
  62. }
  63. }
  64. }
  65. return false;
  66. }
  67. // Determine the service name, aliases of that name, and protocol used for a
  68. // given port number. Return S_OK on success, else return a failure code.
  69. //
  70. // winsock must have been initialized prior to calling this function.
  71. //
  72. // portNumber - in, the port number for which the info should be determined.
  73. //
  74. // name - out, the name of the service that runs on that port
  75. //
  76. // aliases - out, other names for the service that runs on the port
  77. //
  78. // protocol - out, the name of the protocol used on the port.
  79. HRESULT
  80. GetServiceOnPort(
  81. int portNumber,
  82. String& name,
  83. StringList& aliases,
  84. String& protocol)
  85. {
  86. LOG_FUNCTION2(GetServiceOnPort, String::format(L"%1!d!", portNumber));
  87. ASSERT(portNumber);
  88. HRESULT hr = S_OK;
  89. name.erase();
  90. aliases.clear();
  91. protocol.erase();
  92. int portNetByteOrder = htons((u_short) portNumber);
  93. servent* se = ::getservbyport(portNetByteOrder, 0);
  94. if (!se)
  95. {
  96. hr = Win32ToHresult((DWORD) ::WSAGetLastError());
  97. }
  98. else
  99. {
  100. if (se->s_name)
  101. {
  102. name = se->s_name;
  103. }
  104. if (se->s_proto)
  105. {
  106. protocol = se->s_proto;
  107. }
  108. char** a = se->s_aliases;
  109. while (*a)
  110. {
  111. aliases.push_back(*a);
  112. ++a;
  113. }
  114. }
  115. #ifdef LOGGING_BUILD
  116. LOG_HRESULT(hr);
  117. LOG(name);
  118. for (
  119. StringList::iterator i = aliases.begin();
  120. i != aliases.end();
  121. ++i)
  122. {
  123. LOG(*i);
  124. }
  125. LOG(protocol);
  126. #endif
  127. return hr;
  128. }
  129. // S_FALSE if an application has the port open in exclusive mode, S_OK if not,
  130. // and error otherwise.
  131. //
  132. // winsock must have been initialized prior to calling this function.
  133. //
  134. // portNumber - in, port to check.
  135. HRESULT
  136. CheckPortAvailability(int portNumber)
  137. {
  138. LOG_FUNCTION2(CheckPortAvailability, String::format(L"%1!d!", portNumber));
  139. ASSERT(portNumber);
  140. HRESULT hr = S_OK;
  141. do
  142. {
  143. sockaddr_in local;
  144. ::ZeroMemory(&local, sizeof local);
  145. local.sin_family = AF_INET;
  146. local.sin_port = htons((u_short) portNumber);
  147. local.sin_addr.s_addr = INADDR_ANY;
  148. SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
  149. if (sock == INVALID_SOCKET)
  150. {
  151. LOG(L"can't build socket");
  152. hr = Win32ToHresult((DWORD) ::WSAGetLastError());
  153. break;
  154. }
  155. if (
  156. bind(
  157. sock,
  158. (sockaddr*) &local,
  159. sizeof local) == SOCKET_ERROR)
  160. {
  161. LOG(L"bind failed");
  162. DWORD sockerr = ::WSAGetLastError();
  163. if (sockerr == WSAEADDRINUSE)
  164. {
  165. // a process on this box already has the socket open in
  166. // exclusive mode.
  167. hr = S_FALSE;
  168. }
  169. else
  170. {
  171. hr = Win32ToHresult(sockerr);
  172. }
  173. break;
  174. }
  175. // at this point, the bind was successful
  176. ASSERT(hr == S_OK);
  177. }
  178. while (0);
  179. LOG_HRESULT(hr);
  180. return hr;
  181. }
  182. // Create a string that represents the port and the service name(s) running on
  183. // that port. This string is presented in the ui.
  184. //
  185. // winsock must have been initialized prior to calling this function.
  186. //
  187. // portNumber - in, port to check.
  188. String
  189. MakeUnavailablePortListEntry(int portNumber)
  190. {
  191. LOG_FUNCTION(MakeUnavailablePortListEntry);
  192. ASSERT(portNumber);
  193. String entry;
  194. do
  195. {
  196. String name;
  197. String protocol;
  198. StringList aliases;
  199. HRESULT hr = GetServiceOnPort(portNumber, name, aliases, protocol);
  200. if (FAILED(hr))
  201. {
  202. // make a simple entry with just the port number
  203. entry = String::format(L"%1!d!", portNumber);
  204. break;
  205. }
  206. if (aliases.size())
  207. {
  208. // combine the aliases into a comma-separated list
  209. String aliasParam;
  210. int j = 0;
  211. for (
  212. StringList::iterator i = aliases.begin();
  213. i != aliases.end();
  214. ++i, ++j)
  215. {
  216. aliasParam += *i;
  217. if (j < (aliases.size() - 1))
  218. {
  219. aliasParam += L", ";
  220. }
  221. }
  222. entry =
  223. String::format(
  224. L"%1!d! %2 (%3)",
  225. portNumber,
  226. name.c_str(),
  227. aliasParam.c_str());
  228. }
  229. else
  230. {
  231. // no aliases
  232. entry = String::format(L"%1!d! %2", portNumber, name.c_str());
  233. }
  234. }
  235. while (0);
  236. LOG(entry);
  237. return entry;
  238. }
  239. // Determine if any of a set of tcp ports required by the DS is already in use
  240. // by another application on this machine. Return S_OK if the list can be
  241. // made, a failure code otherwise.
  242. //
  243. // portsInUseList - out, a list of strings representing the ports in use and
  244. // the name(s) of the services that are running on them, suitable for UI
  245. // presentation.
  246. HRESULT
  247. EnumerateRequiredPortsInUse(StringList& portsInUseList)
  248. {
  249. LOG_FUNCTION(EnumerateRequiredPortsInUse);
  250. portsInUseList.clear();
  251. HRESULT hr = S_FALSE;
  252. bool cleanupWinsock = false;
  253. do
  254. {
  255. WSADATA data;
  256. hr = Win32ToHresult((DWORD) ::WSAStartup(MAKEWORD(2,0), &data));
  257. BREAK_ON_FAILED_HRESULT(hr);
  258. cleanupWinsock = true;
  259. static const int REQUIRED_PORTS[] =
  260. {
  261. 88, // TCP/UDP Kerberos
  262. 389, // TCP LDAP
  263. 636, // TCP sldap
  264. 3268, // TCP ldap/GC
  265. 3269, // TCP sldap/GC
  266. 0
  267. };
  268. const int* port = REQUIRED_PORTS;
  269. while (*port)
  270. {
  271. HRESULT hr2 = CheckPortAvailability(*port);
  272. if (hr2 == S_FALSE)
  273. {
  274. // Make an entry in the "in use" list
  275. portsInUseList.push_back(MakeUnavailablePortListEntry(*port));
  276. }
  277. // we ignore any other type of failure and check the remaining
  278. // ports.
  279. ++port;
  280. }
  281. }
  282. while (0);
  283. if (cleanupWinsock)
  284. {
  285. ::WSACleanup();
  286. }
  287. #ifdef LOGGING_BUILD
  288. LOG_HRESULT(hr);
  289. for (
  290. StringList::iterator i = portsInUseList.begin();
  291. i != portsInUseList.end();
  292. ++i)
  293. {
  294. LOG(*i);
  295. }
  296. #endif
  297. return hr;
  298. }
  299. bool
  300. AreRequiredPortsAvailable()
  301. {
  302. LOG_FUNCTION(AreRequiredPortsAvailable);
  303. bool result = true;
  304. do
  305. {
  306. State::RunContext context = State::GetInstance().GetRunContext();
  307. if (context == State::NT5_DC)
  308. {
  309. // already a DC, so we don't care about the port status, as the
  310. // only thing the user will be able to do is demote the box.
  311. LOG(L"already a DC -- port check skipped");
  312. ASSERT(result);
  313. break;
  314. }
  315. // Find the list of IP ports required by the DS that are already in use
  316. // (if any). If we find some, gripe at the user.
  317. StringList portsInUseList;
  318. HRESULT hr = EnumerateRequiredPortsInUse(portsInUseList);
  319. if (FAILED(hr))
  320. {
  321. // if we can't figure out if the required ports are in use, then
  322. // just muddle on -- the user will have to clean up after the
  323. // promote.
  324. ASSERT(result);
  325. break;
  326. }
  327. if (hr == S_FALSE || portsInUseList.size() == 0)
  328. {
  329. LOG(L"No required ports already in use");
  330. ASSERT(result);
  331. break;
  332. }
  333. result = false;
  334. // there should be at least one port in the list.
  335. ASSERT(portsInUseList.size());
  336. PortsUnavailableErrorDialog(portsInUseList).ModalExecute(
  337. Win::GetDesktopWindow());
  338. }
  339. while (0);
  340. LOG(result ? L"true" : L"false");
  341. return result;
  342. }