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.

444 lines
9.5 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. // REVIEWED-2002/02/22-sburns call correctly passes byte count.
  145. ::ZeroMemory(&local, sizeof local);
  146. local.sin_family = AF_INET;
  147. local.sin_port = htons((u_short) portNumber);
  148. local.sin_addr.s_addr = INADDR_ANY;
  149. SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
  150. if (sock == INVALID_SOCKET)
  151. {
  152. LOG(L"can't build socket");
  153. hr = Win32ToHresult((DWORD) ::WSAGetLastError());
  154. break;
  155. }
  156. if (
  157. bind(
  158. sock,
  159. (sockaddr*) &local,
  160. sizeof local) == SOCKET_ERROR)
  161. {
  162. LOG(L"bind failed");
  163. DWORD sockerr = ::WSAGetLastError();
  164. if (sockerr == WSAEADDRINUSE)
  165. {
  166. // a process on this box already has the socket open in
  167. // exclusive mode.
  168. hr = S_FALSE;
  169. }
  170. else
  171. {
  172. hr = Win32ToHresult(sockerr);
  173. }
  174. break;
  175. }
  176. // at this point, the bind was successful
  177. ASSERT(hr == S_OK);
  178. }
  179. while (0);
  180. LOG_HRESULT(hr);
  181. return hr;
  182. }
  183. // Create a string that represents the port and the service name(s) running on
  184. // that port. This string is presented in the ui.
  185. //
  186. // winsock must have been initialized prior to calling this function.
  187. //
  188. // portNumber - in, port to check.
  189. String
  190. MakeUnavailablePortListEntry(int portNumber)
  191. {
  192. LOG_FUNCTION(MakeUnavailablePortListEntry);
  193. ASSERT(portNumber);
  194. String entry;
  195. do
  196. {
  197. String name;
  198. String protocol;
  199. StringList aliases;
  200. HRESULT hr = GetServiceOnPort(portNumber, name, aliases, protocol);
  201. if (FAILED(hr))
  202. {
  203. // make a simple entry with just the port number
  204. entry = String::format(L"%1!d!", portNumber);
  205. break;
  206. }
  207. if (aliases.size())
  208. {
  209. // combine the aliases into a comma-separated list
  210. String aliasParam;
  211. size_t j = 0;
  212. for (
  213. StringList::iterator i = aliases.begin();
  214. i != aliases.end();
  215. ++i, ++j)
  216. {
  217. aliasParam += *i;
  218. if (j < (aliases.size() - 1))
  219. {
  220. aliasParam += L", ";
  221. }
  222. }
  223. entry =
  224. String::format(
  225. L"%1!d! %2 (%3)",
  226. portNumber,
  227. name.c_str(),
  228. aliasParam.c_str());
  229. }
  230. else
  231. {
  232. // no aliases
  233. entry = String::format(L"%1!d! %2", portNumber, name.c_str());
  234. }
  235. }
  236. while (0);
  237. LOG(entry);
  238. return entry;
  239. }
  240. // Determine if any of a set of tcp ports required by the DS is already in use
  241. // by another application on this machine. Return S_OK if the list can be
  242. // made, a failure code otherwise.
  243. //
  244. // portsInUseList - out, a list of strings representing the ports in use and
  245. // the name(s) of the services that are running on them, suitable for UI
  246. // presentation.
  247. HRESULT
  248. EnumerateRequiredPortsInUse(StringList& portsInUseList)
  249. {
  250. LOG_FUNCTION(EnumerateRequiredPortsInUse);
  251. portsInUseList.clear();
  252. HRESULT hr = S_FALSE;
  253. bool cleanupWinsock = false;
  254. do
  255. {
  256. WSADATA data;
  257. hr = Win32ToHresult((DWORD) ::WSAStartup(MAKEWORD(2,0), &data));
  258. BREAK_ON_FAILED_HRESULT(hr);
  259. cleanupWinsock = true;
  260. static const int REQUIRED_PORTS[] =
  261. {
  262. 88, // TCP/UDP Kerberos
  263. 389, // TCP LDAP
  264. 636, // TCP sldap
  265. 3268, // TCP ldap/GC
  266. 3269, // TCP sldap/GC
  267. 0
  268. };
  269. const int* port = REQUIRED_PORTS;
  270. while (*port)
  271. {
  272. HRESULT hr2 = CheckPortAvailability(*port);
  273. if (hr2 == S_FALSE)
  274. {
  275. // Make an entry in the "in use" list
  276. portsInUseList.push_back(MakeUnavailablePortListEntry(*port));
  277. }
  278. // we ignore any other type of failure and check the remaining
  279. // ports.
  280. ++port;
  281. }
  282. }
  283. while (0);
  284. if (cleanupWinsock)
  285. {
  286. ::WSACleanup();
  287. }
  288. #ifdef LOGGING_BUILD
  289. LOG_HRESULT(hr);
  290. for (
  291. StringList::iterator i = portsInUseList.begin();
  292. i != portsInUseList.end();
  293. ++i)
  294. {
  295. LOG(*i);
  296. }
  297. #endif
  298. return hr;
  299. }
  300. bool
  301. AreRequiredPortsAvailable()
  302. {
  303. LOG_FUNCTION(AreRequiredPortsAvailable);
  304. bool result = true;
  305. do
  306. {
  307. State::RunContext context = State::GetInstance().GetRunContext();
  308. if (context == State::NT5_DC)
  309. {
  310. // already a DC, so we don't care about the port status, as the
  311. // only thing the user will be able to do is demote the box.
  312. LOG(L"already a DC -- port check skipped");
  313. ASSERT(result);
  314. break;
  315. }
  316. // Find the list of IP ports required by the DS that are already in use
  317. // (if any). If we find some, gripe at the user.
  318. StringList portsInUseList;
  319. HRESULT hr = EnumerateRequiredPortsInUse(portsInUseList);
  320. if (FAILED(hr))
  321. {
  322. // if we can't figure out if the required ports are in use, then
  323. // just muddle on -- the user will have to clean up after the
  324. // promote.
  325. ASSERT(result);
  326. break;
  327. }
  328. if (hr == S_FALSE || portsInUseList.size() == 0)
  329. {
  330. LOG(L"No required ports already in use");
  331. ASSERT(result);
  332. break;
  333. }
  334. result = false;
  335. // there should be at least one port in the list.
  336. ASSERT(portsInUseList.size());
  337. PortsUnavailableErrorDialog(portsInUseList).ModalExecute(
  338. Win::GetDesktopWindow());
  339. }
  340. while (0);
  341. LOG(result ? L"true" : L"false");
  342. return result;
  343. }