441 lines
8.9 KiB
C++
441 lines
8.9 KiB
C++
// 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;
|
|
::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;
|
|
int 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;
|
|
}
|
|
|