|
|
// Copyright (C) 2000 Microsoft Corporation
//
// Check availability of ports used by Active Directory
//
// 1 Nov 2000 sburns
#include "headers.hxx"
#include "state.hpp"
#include "resource.h"
#include "CheckPortAvailability.hpp"
static const DWORD HELP_MAP[] = { 0, 0 };
PortsUnavailableErrorDialog::PortsUnavailableErrorDialog( StringList& portsInUseList_) : Dialog(IDD_PORTS_IN_USE_ERROR, HELP_MAP), portsInUseList(portsInUseList_) { LOG_CTOR(PortsUnavailableErrorDialog);
ASSERT(portsInUseList.size()); }
PortsUnavailableErrorDialog::~PortsUnavailableErrorDialog() { LOG_DTOR(PortsUnavailableErrorDialog); }
void PortsUnavailableErrorDialog::OnInit() { LOG_FUNCTION(PortsUnavailableErrorDialog::OnInit);
// Load up the edit box with the DNs we aliased in the ctor.
String text; for ( StringList::iterator i = portsInUseList.begin(); i != portsInUseList.end(); ++i) { text += *i + L"\r\n"; }
Win::SetDlgItemText(hwnd, IDC_PORT_LIST, text); }
bool PortsUnavailableErrorDialog::OnCommand( HWND /* windowFrom */ , unsigned controlIDFrom, unsigned code) { // LOG_FUNCTION(PortsUnavailableErrorDialog::OnCommand);
if (code == BN_CLICKED) { switch (controlIDFrom) { case IDOK: case IDCANCEL: { Win::EndDialog(hwnd, controlIDFrom); return true; } default: { // do nothing
} } }
return false; }
// Determine the service name, aliases of that name, and protocol used for a
// given port number. Return S_OK on success, else return a failure code.
//
// winsock must have been initialized prior to calling this function.
//
// portNumber - in, the port number for which the info should be determined.
//
// name - out, the name of the service that runs on that port
//
// aliases - out, other names for the service that runs on the port
//
// protocol - out, the name of the protocol used on the port.
HRESULT GetServiceOnPort( int portNumber, String& name, StringList& aliases, String& protocol) { LOG_FUNCTION2(GetServiceOnPort, String::format(L"%1!d!", portNumber)); ASSERT(portNumber);
HRESULT hr = S_OK; name.erase(); aliases.clear(); protocol.erase();
int portNetByteOrder = htons((u_short) portNumber); servent* se = ::getservbyport(portNetByteOrder, 0); if (!se) { hr = Win32ToHresult((DWORD) ::WSAGetLastError()); } else { if (se->s_name) { name = se->s_name; } if (se->s_proto) { protocol = se->s_proto; }
char** a = se->s_aliases; while (*a) { aliases.push_back(*a); ++a; } }
#ifdef LOGGING_BUILD
LOG_HRESULT(hr); LOG(name); for ( StringList::iterator i = aliases.begin(); i != aliases.end(); ++i) { LOG(*i); } LOG(protocol); #endif
return hr; }
// S_FALSE if an application has the port open in exclusive mode, S_OK if not,
// and error otherwise.
//
// winsock must have been initialized prior to calling this function.
//
// portNumber - in, port to check.
HRESULT CheckPortAvailability(int portNumber) { LOG_FUNCTION2(CheckPortAvailability, String::format(L"%1!d!", portNumber)); ASSERT(portNumber);
HRESULT hr = S_OK;
do { sockaddr_in local;
// REVIEWED-2002/02/22-sburns call correctly passes byte count.
::ZeroMemory(&local, sizeof local); local.sin_family = AF_INET; local.sin_port = htons((u_short) portNumber); local.sin_addr.s_addr = INADDR_ANY;
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { LOG(L"can't build socket"); hr = Win32ToHresult((DWORD) ::WSAGetLastError()); break; }
if ( bind( sock, (sockaddr*) &local, sizeof local) == SOCKET_ERROR) { LOG(L"bind failed");
DWORD sockerr = ::WSAGetLastError();
if (sockerr == WSAEADDRINUSE) { // a process on this box already has the socket open in
// exclusive mode.
hr = S_FALSE; } else { hr = Win32ToHresult(sockerr); } break; }
// at this point, the bind was successful
ASSERT(hr == S_OK); } while (0);
LOG_HRESULT(hr);
return hr; }
// Create a string that represents the port and the service name(s) running on
// that port. This string is presented in the ui.
//
// winsock must have been initialized prior to calling this function.
//
// portNumber - in, port to check.
String MakeUnavailablePortListEntry(int portNumber) { LOG_FUNCTION(MakeUnavailablePortListEntry); ASSERT(portNumber);
String entry;
do { String name; String protocol; StringList aliases; HRESULT hr = GetServiceOnPort(portNumber, name, aliases, protocol); if (FAILED(hr)) { // make a simple entry with just the port number
entry = String::format(L"%1!d!", portNumber); break; } if (aliases.size()) { // combine the aliases into a comma-separated list
String aliasParam; size_t j = 0; for ( StringList::iterator i = aliases.begin(); i != aliases.end(); ++i, ++j) { aliasParam += *i; if (j < (aliases.size() - 1)) { aliasParam += L", "; } }
entry = String::format( L"%1!d! %2 (%3)", portNumber, name.c_str(), aliasParam.c_str()); } else { // no aliases
entry = String::format(L"%1!d! %2", portNumber, name.c_str()); } } while (0);
LOG(entry);
return entry; }
// Determine if any of a set of tcp ports required by the DS is already in use
// by another application on this machine. Return S_OK if the list can be
// made, a failure code otherwise.
//
// portsInUseList - out, a list of strings representing the ports in use and
// the name(s) of the services that are running on them, suitable for UI
// presentation.
HRESULT EnumerateRequiredPortsInUse(StringList& portsInUseList) { LOG_FUNCTION(EnumerateRequiredPortsInUse);
portsInUseList.clear(); HRESULT hr = S_FALSE; bool cleanupWinsock = false;
do { WSADATA data; hr = Win32ToHresult((DWORD) ::WSAStartup(MAKEWORD(2,0), &data)); BREAK_ON_FAILED_HRESULT(hr);
cleanupWinsock = true;
static const int REQUIRED_PORTS[] = { 88, // TCP/UDP Kerberos
389, // TCP LDAP
636, // TCP sldap
3268, // TCP ldap/GC
3269, // TCP sldap/GC
0 };
const int* port = REQUIRED_PORTS; while (*port) { HRESULT hr2 = CheckPortAvailability(*port); if (hr2 == S_FALSE) { // Make an entry in the "in use" list
portsInUseList.push_back(MakeUnavailablePortListEntry(*port)); } // we ignore any other type of failure and check the remaining
// ports.
++port; } } while (0);
if (cleanupWinsock) { ::WSACleanup(); }
#ifdef LOGGING_BUILD
LOG_HRESULT(hr);
for ( StringList::iterator i = portsInUseList.begin(); i != portsInUseList.end(); ++i) { LOG(*i); } #endif
return hr; }
bool AreRequiredPortsAvailable() { LOG_FUNCTION(AreRequiredPortsAvailable);
bool result = true;
do { State::RunContext context = State::GetInstance().GetRunContext(); if (context == State::NT5_DC) { // already a DC, so we don't care about the port status, as the
// only thing the user will be able to do is demote the box.
LOG(L"already a DC -- port check skipped"); ASSERT(result);
break; }
// Find the list of IP ports required by the DS that are already in use
// (if any). If we find some, gripe at the user.
StringList portsInUseList; HRESULT hr = EnumerateRequiredPortsInUse(portsInUseList); if (FAILED(hr)) { // if we can't figure out if the required ports are in use, then
// just muddle on -- the user will have to clean up after the
// promote.
ASSERT(result); break; }
if (hr == S_FALSE || portsInUseList.size() == 0) { LOG(L"No required ports already in use"); ASSERT(result); break; }
result = false; // there should be at least one port in the list.
ASSERT(portsInUseList.size());
PortsUnavailableErrorDialog(portsInUseList).ModalExecute( Win::GetDesktopWindow()); } while (0);
LOG(result ? L"true" : L"false");
return result; }
|