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.
1219 lines
30 KiB
1219 lines
30 KiB
// Copyright (C) 2000 Microsoft Corporation
|
|
//
|
|
// Dynamic DNS detection/diagnostic page
|
|
//
|
|
// 22 Aug 2000 sburns
|
|
|
|
|
|
|
|
#include "headers.hxx"
|
|
#include "page.hpp"
|
|
#include "DynamicDnsPage.hpp"
|
|
#include "resource.h"
|
|
#include "state.hpp"
|
|
|
|
|
|
|
|
HINSTANCE DynamicDnsPage::richEditHInstance = 0;
|
|
|
|
|
|
|
|
DynamicDnsPage::DynamicDnsPage()
|
|
:
|
|
DCPromoWizardPage(
|
|
IDD_DYNAMIC_DNS,
|
|
IDS_DYNAMIC_DNS_PAGE_TITLE,
|
|
IDS_DYNAMIC_DNS_PAGE_SUBTITLE),
|
|
diagnosticResultCode(UNEXPECTED_FINDING_SERVER),
|
|
needToKillSelection(false),
|
|
originalMessageHeight(0),
|
|
testPassCount(0)
|
|
{
|
|
LOG_CTOR(DynamicDnsPage);
|
|
|
|
WSADATA data;
|
|
DWORD err = ::WSAStartup(MAKEWORD(2,0), &data);
|
|
|
|
// if winsock startup fails, that's a shame. The gethostbyname will
|
|
// not work, but there's not much we can do about that.
|
|
|
|
ASSERT(!err);
|
|
|
|
if (!richEditHInstance)
|
|
{
|
|
// You have to load the rich edit dll to get the window class, etc.
|
|
// to register. Otherwise the dialog will fail create, and the page
|
|
// will not appear.
|
|
|
|
HRESULT hr = Win::LoadLibrary(L"riched20.dll", richEditHInstance);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// we've no choice than crash the app.
|
|
|
|
throw Win::Error(hr, IDS_RICHEDIT_LOAD_FAILED);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DynamicDnsPage::~DynamicDnsPage()
|
|
{
|
|
LOG_DTOR(DynamicDnsPage);
|
|
|
|
::WSACleanup();
|
|
|
|
Win::FreeLibrary(richEditHInstance);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
DynamicDnsPage::ShowButtons(bool shown)
|
|
{
|
|
LOG_FUNCTION(DynamicDnsPage::ShowButtons);
|
|
|
|
HWND ignoreButton = Win::GetDlgItem(hwnd, IDC_IGNORE);
|
|
HWND richEdit = Win::GetDlgItem(hwnd, IDC_MESSAGE);
|
|
|
|
int state = shown ? SW_SHOW : SW_HIDE;
|
|
|
|
Win::ShowWindow(Win::GetDlgItem(hwnd, IDC_RETRY), state);
|
|
Win::ShowWindow(Win::GetDlgItem(hwnd, IDC_INSTALL_DNS), state);
|
|
Win::ShowWindow(ignoreButton, state);
|
|
|
|
RECT r;
|
|
Win::GetWindowRect(richEdit, r);
|
|
|
|
// Convert r to coords relative to the parent window.
|
|
|
|
Win::MapWindowPoints(0, hwnd, r);
|
|
|
|
if (shown)
|
|
{
|
|
// If we're showing the buttons, collapse the rich edit to it's normal
|
|
// height.
|
|
|
|
Win::MoveWindow(
|
|
richEdit,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left,
|
|
originalMessageHeight,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
// If we're hiding the buttons, expand the rich edit to include their
|
|
// real estate.
|
|
|
|
RECT r1;
|
|
Win::GetWindowRect(ignoreButton, r1);
|
|
Win::MapWindowPoints(0, hwnd, r1);
|
|
|
|
Win::MoveWindow(
|
|
richEdit,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left,
|
|
r1.bottom - r.top,
|
|
true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
DynamicDnsPage::SelectRadioButton(int buttonResId)
|
|
{
|
|
// If the order of the buttons changes, then this must be changed. The
|
|
// buttons also need to have consecutively numbered res IDs in the tab
|
|
// order.
|
|
|
|
Win::CheckRadioButton(hwnd, IDC_RETRY, IDC_IGNORE, buttonResId);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
DynamicDnsPage::OnInit()
|
|
{
|
|
LOG_FUNCTION(DynamicDnsPage::OnInit);
|
|
|
|
HWND richEdit = Win::GetDlgItem(hwnd, IDC_MESSAGE);
|
|
|
|
// ask for link messages
|
|
|
|
Win::RichEdit_SetEventMask(richEdit, ENM_LINK);
|
|
|
|
// save the normal size of the message box so we can restore it later.
|
|
|
|
RECT r;
|
|
Win::GetWindowRect(richEdit, r);
|
|
|
|
originalMessageHeight = r.bottom - r.top;
|
|
|
|
Win::SendMessage(
|
|
richEdit,
|
|
EM_SETBKGNDCOLOR,
|
|
0,
|
|
Win::GetSysColor(COLOR_BTNFACE));
|
|
|
|
SelectRadioButton(IDC_IGNORE);
|
|
|
|
// Hide the radio buttons initially
|
|
|
|
ShowButtons(false);
|
|
|
|
multiLineEdit.Init(Win::GetDlgItem(hwnd, IDC_MESSAGE));
|
|
|
|
// pick the proper radio button label
|
|
|
|
if (State::GetInstance().ShouldConfigDnsClient())
|
|
{
|
|
Win::SetDlgItemText(
|
|
hwnd,
|
|
IDC_INSTALL_DNS,
|
|
IDS_INSTALL_DNS_RADIO_LABEL_WITH_CLIENT);
|
|
}
|
|
else
|
|
{
|
|
Win::SetDlgItemText(
|
|
hwnd,
|
|
IDC_INSTALL_DNS,
|
|
IDS_INSTALL_DNS_RADIO_LABEL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Adds a trailing '.' to the supplied name if one is not already present.
|
|
//
|
|
// name - in, name to add a trailing '.' to, if it doesn't already have one.
|
|
// If this value is the empty string, then '.' is returned.
|
|
|
|
String
|
|
FullyQualifyDnsName(const String& name)
|
|
{
|
|
LOG_FUNCTION2(FullyQualifyDnsName, name);
|
|
|
|
if (name.empty())
|
|
{
|
|
return L".";
|
|
}
|
|
|
|
// needs a trailing dot
|
|
|
|
// REVIEW: name[name.length() - 1] is the same as *(name.rbegin())
|
|
// which is cheaper?
|
|
|
|
if (name[name.length() - 1] != L'.')
|
|
{
|
|
return name + L".";
|
|
}
|
|
|
|
// already has a trailing dot
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
|
|
// Scans a linked list of DNS_RECORDs, returning a pointer to the first record
|
|
// of type SOA, or 0 if no record of that type is in the list.
|
|
//
|
|
// recordList - in, linked list of DNS_RECORDs, as returned from DnsQuery
|
|
|
|
DNS_RECORD*
|
|
FindSoaRecord(DNS_RECORD* recordList)
|
|
{
|
|
LOG_FUNCTION(FindSoaRecord);
|
|
ASSERT(recordList);
|
|
|
|
DNS_RECORD* result = recordList;
|
|
while (result)
|
|
{
|
|
if (result->wType == DNS_TYPE_SOA)
|
|
{
|
|
LOG(L"SOA record found");
|
|
|
|
break;
|
|
}
|
|
|
|
result = result->pNext;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// Returns the textual representation of the IP address for the given server
|
|
// name, in the form "xxx.xxx.xxx.xxx", or the empty string if not IP address
|
|
// can be determined.
|
|
//
|
|
// serverName - in, the host name of the server for which to find the IP
|
|
// address. If the value is the empty string, then the empty string is
|
|
// returned from the function.
|
|
|
|
String
|
|
GetIpAddress(const String& serverName)
|
|
{
|
|
LOG_FUNCTION2(GetIpAddress, serverName);
|
|
ASSERT(!serverName.empty());
|
|
|
|
String result;
|
|
|
|
do
|
|
{
|
|
if (serverName.empty())
|
|
{
|
|
break;
|
|
}
|
|
|
|
LOG(L"Calling gethostbyname");
|
|
|
|
AnsiString ansi;
|
|
serverName.convert(ansi);
|
|
|
|
HOSTENT* host = gethostbyname(ansi.c_str());
|
|
if (host && host->h_addr_list[0])
|
|
{
|
|
struct in_addr a;
|
|
|
|
::CopyMemory(&a.S_un.S_addr, host->h_addr_list[0], sizeof a.S_un.S_addr);
|
|
result = inet_ntoa(a);
|
|
|
|
break;
|
|
}
|
|
|
|
LOG(String::format(L"WSAGetLastError = 0x%1!0X", WSAGetLastError()));
|
|
}
|
|
while (0);
|
|
|
|
LOG(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// Find the DNS server that is authoritative for registering the given server
|
|
// name, i.e. what server would register the name. Returns NO_ERROR on
|
|
// success, or a DNS status code (a win32 error) on failure. On failure, the
|
|
// out parameters are all empty strings.
|
|
//
|
|
// serverName - in, candidate name for registration. This value should not be the
|
|
// empty string.
|
|
//
|
|
// authZone - out, the zone the name would be registered in.
|
|
//
|
|
// authServer - out, the name of the DNS server that would have the
|
|
// registration.
|
|
//
|
|
// authServerIpAddress - out, textual representation of the IP address of the
|
|
// server named by authServer.
|
|
|
|
DNS_STATUS
|
|
FindAuthoritativeServer(
|
|
const String& serverName,
|
|
String& authZone,
|
|
String& authServer,
|
|
String& authServerIpAddress)
|
|
{
|
|
LOG_FUNCTION2(FindAuthoritativeServer, serverName);
|
|
ASSERT(!serverName.empty());
|
|
|
|
authZone.erase();
|
|
authServer.erase();
|
|
authServerIpAddress.erase();
|
|
|
|
// ensure that the server name ends with a "." so that we have a stop
|
|
// point for our loop
|
|
|
|
String currentName = FullyQualifyDnsName(serverName);
|
|
|
|
DNS_STATUS result = NO_ERROR;
|
|
DNS_RECORD* queryResults = 0;
|
|
|
|
while (!currentName.empty())
|
|
{
|
|
result =
|
|
MyDnsQuery(
|
|
currentName,
|
|
DNS_TYPE_SOA,
|
|
DNS_QUERY_BYPASS_CACHE,
|
|
queryResults);
|
|
if (
|
|
result == ERROR_TIMEOUT
|
|
|| result == DNS_ERROR_RCODE_SERVER_FAILURE)
|
|
{
|
|
// we bail out entirely
|
|
|
|
LOG(L"failed to find autoritative server.");
|
|
|
|
break;
|
|
}
|
|
|
|
// search for an SOA RR
|
|
|
|
DNS_RECORD* soaRecord =
|
|
queryResults ? FindSoaRecord(queryResults) : 0;
|
|
if (soaRecord)
|
|
{
|
|
// collect return values, and we're done.
|
|
|
|
LOG(L"autoritative server found");
|
|
|
|
authZone = soaRecord->pName;
|
|
authServer = soaRecord->Data.SOA.pNamePrimaryServer;
|
|
authServerIpAddress = GetIpAddress(authServer);
|
|
|
|
break;
|
|
}
|
|
|
|
// no SOA record found.
|
|
|
|
if (currentName == L".")
|
|
{
|
|
// We've run out of names to query. This situation is so unlikely
|
|
// that the DNS server would have to be seriously broken to put
|
|
// us in this state. So this is almost an assert case.
|
|
|
|
LOG(L"Root zone reached without finding SOA record!");
|
|
|
|
result = DNS_ERROR_ZONE_HAS_NO_SOA_RECORD;
|
|
break;
|
|
}
|
|
|
|
// whack off the leftmost label, and iterate again on the parent
|
|
// zone.
|
|
|
|
currentName = Dns::GetParentDomainName(currentName);
|
|
|
|
MyDnsRecordListFree(queryResults);
|
|
queryResults = 0;
|
|
}
|
|
|
|
MyDnsRecordListFree(queryResults);
|
|
|
|
LOG(String::format(L"result = %1!08X!", result));
|
|
LOG(L"authZone = " + authZone);
|
|
LOG(L"authServer = " + authServer);
|
|
LOG(L"authServerIpAddress = " + authServerIpAddress);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
MyDnsUpdateTest(const String& name)
|
|
{
|
|
LOG_FUNCTION2(MyDnsUpdateTest, name);
|
|
ASSERT(!name.empty());
|
|
|
|
LOG(L"Calling DnsUpdateTest");
|
|
LOG( L"hContextHandle : 0");
|
|
LOG(String::format(L"pszName : %1", name.c_str()));
|
|
LOG( L"fOptions : 0");
|
|
LOG( L"aipServers : 0");
|
|
|
|
DNS_STATUS status =
|
|
::DnsUpdateTest(
|
|
0,
|
|
const_cast<wchar_t*>(name.c_str()),
|
|
0,
|
|
0);
|
|
|
|
LOG(String::format(L"status = %1!08X!", status));
|
|
LOG(MyDnsStatusString(status));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
// Returns result code that corresponds to what messages to be displayed and
|
|
// what radio buttons to make available as a result of the diagnostic.
|
|
//
|
|
// Also returns thru out parameters information to be included in the
|
|
// messages.
|
|
//
|
|
// serverName - in, the name of the domain contoller to be registered.
|
|
//
|
|
// errorCode - out, the DNS error code (a win32 error) encountered when
|
|
// running the diagnostic.
|
|
//
|
|
// authZone - out, the zone the name would be registered in.
|
|
//
|
|
// authServer - out, the name of the DNS server that would have the
|
|
// registration.
|
|
//
|
|
// authServerIpAddress - out, textual representation of the IP address of the
|
|
// server named by authServer.
|
|
|
|
DynamicDnsPage::DiagnosticCode
|
|
DynamicDnsPage::DiagnoseDnsRegistration(
|
|
const String& serverName,
|
|
DNS_STATUS& errorCode,
|
|
String& authZone,
|
|
String& authServer,
|
|
String& authServerIpAddress)
|
|
{
|
|
LOG_FUNCTION(DynamicDnsPage::DiagnoseDnsRegistration);
|
|
ASSERT(!serverName.empty());
|
|
|
|
DiagnosticCode result = UNEXPECTED_FINDING_SERVER;
|
|
|
|
errorCode =
|
|
FindAuthoritativeServer(
|
|
serverName,
|
|
authZone,
|
|
authServer,
|
|
authServerIpAddress);
|
|
|
|
switch (errorCode)
|
|
{
|
|
case NO_ERROR:
|
|
{
|
|
if (authZone == L".")
|
|
{
|
|
// Message 8
|
|
|
|
LOG(L"authZone is root");
|
|
|
|
result = ZONE_IS_ROOT;
|
|
}
|
|
else
|
|
{
|
|
errorCode = MyDnsUpdateTest(serverName);
|
|
|
|
switch (errorCode)
|
|
{
|
|
case DNS_ERROR_RCODE_NO_ERROR:
|
|
case DNS_ERROR_RCODE_YXDOMAIN:
|
|
{
|
|
// Message 1
|
|
|
|
LOG(L"DNS registration support verified.");
|
|
|
|
result = SUCCESS;
|
|
break;
|
|
}
|
|
case DNS_ERROR_RCODE_NOT_IMPLEMENTED:
|
|
case DNS_ERROR_RCODE_REFUSED:
|
|
{
|
|
// Message 2
|
|
|
|
LOG(L"Server does not support update");
|
|
|
|
result = SERVER_CANT_UPDATE;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Message 3
|
|
|
|
result = ERROR_TESTING_SERVER;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DNS_ERROR_RCODE_SERVER_FAILURE:
|
|
{
|
|
// Message 6
|
|
|
|
result = ERROR_FINDING_SERVER;
|
|
break;
|
|
}
|
|
case ERROR_TIMEOUT:
|
|
{
|
|
// Message 11
|
|
|
|
result = TIMEOUT;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Message 4
|
|
|
|
LOG(L"Unexpected error");
|
|
|
|
result = UNEXPECTED_FINDING_SERVER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LOG(String::format(L"DiagnosticCode = %1!x!", result));
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// void
|
|
// DumpSel(HWND richEdit)
|
|
// {
|
|
// CHARRANGE range;
|
|
// RichEdit_GetSel(richEdit, range);
|
|
//
|
|
// LOG(String::format("cpMin = %1!d! cpMax = %1!d!", range.cpMin, range.cpMax));
|
|
// }
|
|
|
|
|
|
|
|
void
|
|
DynamicDnsPage::UpdateMessageWindow(const String& message)
|
|
{
|
|
LOG_FUNCTION(UpdateMessageWindow);
|
|
ASSERT(!message.empty());
|
|
|
|
// this should have been set before we get here
|
|
|
|
ASSERT(!details.empty());
|
|
|
|
HWND richEdit = Win::GetDlgItem(hwnd, IDC_MESSAGE);
|
|
|
|
// Clear out the window of any prior contents. This is needed because in
|
|
// the code that follows, we take advantage of the fact that the set text
|
|
// functions create an empty selection to the end of the text, and
|
|
// subsequent ST_SELECTION type calls to set text will append at that
|
|
// point.
|
|
|
|
Win::RichEdit_SetText(richEdit, ST_DEFAULT, L"");
|
|
|
|
static const String RTF_HEADER_ON(
|
|
L"{\\rtf" // RTF header
|
|
L"\\pard" // start default paragraph style
|
|
L"\\sa100" // whitespace after paragraph = 100 twips
|
|
L"\\b "); // bold on
|
|
|
|
static const String RTF_HEADER_OFF(
|
|
L"\\b0" // bold off
|
|
L"\\par " // end paragraph
|
|
L"}"); // end RTF
|
|
|
|
Win::RichEdit_SetRtfText(
|
|
richEdit,
|
|
ST_SELECTION,
|
|
RTF_HEADER_ON
|
|
+ String::load(IDS_DIAGNOSTIC_RESULTS)
|
|
+ RTF_HEADER_OFF);
|
|
|
|
Win::RichEdit_SetText(
|
|
richEdit,
|
|
ST_SELECTION,
|
|
String::format(
|
|
(testPassCount == 1)
|
|
? IDS_DIAGNOSTIC_COUNTER_1
|
|
: IDS_DIAGNOSTIC_COUNTER_N,
|
|
testPassCount)
|
|
+ L"\r\n\r\n"
|
|
+ message
|
|
+ L"\r\n\r\n");
|
|
|
|
if (!helpTopicLink.empty())
|
|
{
|
|
// We have help to show, so insert a line with a link to click for it.
|
|
|
|
Win::RichEdit_SetText(
|
|
richEdit,
|
|
ST_SELECTION,
|
|
String::load(IDS_DIAGNOSTIC_HELP_LINK_PRE));
|
|
|
|
// At this point, we want to insert the help link and set it to the link
|
|
// style. We do this by tracking the position of the link text, and
|
|
// then selecting that text, and then setting the selection to the link
|
|
// style.
|
|
|
|
CHARRANGE beginRange;
|
|
Win::RichEdit_GetSel(richEdit, beginRange);
|
|
|
|
Win::RichEdit_SetText(
|
|
richEdit,
|
|
ST_SELECTION,
|
|
String::load(IDS_DIAGNOSTIC_HELP_LINK));
|
|
|
|
CHARRANGE endRange;
|
|
Win::RichEdit_GetSel(richEdit, endRange);
|
|
|
|
ASSERT(endRange.cpMin > beginRange.cpMax);
|
|
|
|
Win::Edit_SetSel(richEdit, beginRange.cpMax, endRange.cpMin);
|
|
|
|
CHARFORMAT2 format;
|
|
|
|
// REVIEWED-2002/02/26-sburns correct byte count passed.
|
|
|
|
::ZeroMemory(&format, sizeof format);
|
|
|
|
format.dwMask = CFM_LINK;
|
|
format.dwEffects = CFE_LINK;
|
|
|
|
Win::RichEdit_SetCharacterFormat(richEdit, SCF_SELECTION, format);
|
|
|
|
// set the selection back to the end of where the link was inserted.
|
|
|
|
Win::RichEdit_SetSel(richEdit, endRange);
|
|
|
|
// now continue to the end
|
|
|
|
Win::RichEdit_SetText(
|
|
richEdit,
|
|
ST_SELECTION,
|
|
String::load(IDS_DIAGNOSTIC_HELP_LINK_POST) + L"\r\n\r\n");
|
|
}
|
|
|
|
Win::RichEdit_SetRtfText(
|
|
richEdit,
|
|
ST_SELECTION,
|
|
RTF_HEADER_ON
|
|
+ String::load(IDS_DETAILS)
|
|
+ RTF_HEADER_OFF);
|
|
|
|
Win::RichEdit_SetText(richEdit, ST_SELECTION, details + L"\r\n\r\n");
|
|
}
|
|
|
|
|
|
|
|
|
|
// do the test, update the text on the page, update the radio buttons
|
|
// enabled state, choose a radio button default if neccessary
|
|
|
|
void
|
|
DynamicDnsPage::DoDnsTestAndUpdatePage()
|
|
{
|
|
LOG_FUNCTION(DynamicDnsPage::DoDnsTestAndUpdatePage);
|
|
|
|
// this might take a while.
|
|
|
|
Win::WaitCursor cursor;
|
|
|
|
State& state = State::GetInstance();
|
|
String domain = state.GetNewDomainDNSName();
|
|
|
|
DNS_STATUS errorCode = 0;
|
|
String authZone;
|
|
String authServer;
|
|
String authServerIpAddress;
|
|
String serverName = L"_ldap._tcp.dc._msdcs." + domain;
|
|
|
|
diagnosticResultCode =
|
|
DiagnoseDnsRegistration(
|
|
serverName,
|
|
errorCode,
|
|
authZone,
|
|
authServer,
|
|
authServerIpAddress);
|
|
++testPassCount;
|
|
|
|
String message;
|
|
int defaultButton = IDC_IGNORE;
|
|
|
|
switch (diagnosticResultCode)
|
|
{
|
|
// Message 1
|
|
|
|
case SUCCESS:
|
|
{
|
|
message = String::load(IDS_DYN_DNS_MESSAGE_SUCCESS);
|
|
|
|
String errorMessage;
|
|
if (errorCode == DNS_ERROR_RCODE_YXDOMAIN)
|
|
{
|
|
// NTRAID#NTBUG9-586579-2002/04/15-sburns
|
|
|
|
errorMessage =
|
|
String::format(
|
|
IDS_DNS_ERROR_RCODE_YXDOMAIN_ADDENDA,
|
|
serverName.c_str(),
|
|
authZone.c_str(),
|
|
serverName.c_str());
|
|
}
|
|
else
|
|
{
|
|
errorMessage = GetErrorMessage(Win32ToHresult(errorCode));
|
|
}
|
|
|
|
details =
|
|
String::format(
|
|
|
|
// NTRAID#NTBUG9-485456-2001/10/24-sburns
|
|
|
|
IDS_DYN_DNS_DETAIL_FULL_SANS_CODE,
|
|
authServer.c_str(),
|
|
authServerIpAddress.c_str(),
|
|
authZone.c_str(),
|
|
errorMessage.c_str());
|
|
helpTopicLink = L"";
|
|
defaultButton = IDC_IGNORE;
|
|
ShowButtons(false);
|
|
|
|
break;
|
|
}
|
|
|
|
// Message 2
|
|
|
|
case SERVER_CANT_UPDATE:
|
|
{
|
|
message = String::load(IDS_DYN_DNS_MESSAGE_SERVER_CANT_UPDATE);
|
|
details =
|
|
String::format(
|
|
IDS_DYN_DNS_DETAIL_FULL,
|
|
authServer.c_str(),
|
|
authServerIpAddress.c_str(),
|
|
authZone.c_str(),
|
|
GetErrorMessage(Win32ToHresult(errorCode)).c_str(),
|
|
errorCode,
|
|
MyDnsStatusString(errorCode).c_str());
|
|
|
|
if (Dns::CompareNames(authZone, domain) == DnsNameCompareEqual)
|
|
{
|
|
helpTopicLink =
|
|
L"DNSConcepts.chm::/sag_DNS_tro_dynamic_message2a.htm";
|
|
}
|
|
else
|
|
{
|
|
helpTopicLink =
|
|
L"DNSConcepts.chm::/sag_DNS_tro_dynamic_message2b.htm";
|
|
}
|
|
|
|
defaultButton = IDC_RETRY;
|
|
ShowButtons(true);
|
|
|
|
break;
|
|
}
|
|
|
|
// Message 3
|
|
|
|
case ERROR_TESTING_SERVER:
|
|
{
|
|
message = String::load(IDS_DYN_DNS_MESSAGE_ERROR_TESTING_SERVER);
|
|
details =
|
|
String::format(
|
|
IDS_DYN_DNS_DETAIL_FULL,
|
|
authServer.c_str(),
|
|
authServerIpAddress.c_str(),
|
|
authZone.c_str(),
|
|
GetErrorMessage(Win32ToHresult(errorCode)).c_str(),
|
|
errorCode,
|
|
MyDnsStatusString(errorCode).c_str());
|
|
helpTopicLink = "DNSConcepts.chm::/sag_DNS_tro_dynamic_message3.htm";
|
|
defaultButton = IDC_RETRY;
|
|
ShowButtons(true);
|
|
break;
|
|
}
|
|
|
|
// Message 6
|
|
|
|
case ERROR_FINDING_SERVER:
|
|
{
|
|
ASSERT(authServer.empty());
|
|
ASSERT(authZone.empty());
|
|
ASSERT(authServerIpAddress.empty());
|
|
|
|
message = String::load(IDS_DYN_DNS_MESSAGE_ERROR_FINDING_SERVER);
|
|
details =
|
|
String::format(
|
|
IDS_DYN_DNS_DETAIL_SCANT,
|
|
serverName.c_str(),
|
|
GetErrorMessage(Win32ToHresult(errorCode)).c_str(),
|
|
errorCode,
|
|
MyDnsStatusString(errorCode).c_str());
|
|
helpTopicLink = "DNSConcepts.chm::/sag_DNS_tro_dynamic_message6.htm";
|
|
defaultButton = IDC_INSTALL_DNS;
|
|
ShowButtons(true);
|
|
break;
|
|
}
|
|
|
|
// Message 8
|
|
|
|
case ZONE_IS_ROOT:
|
|
{
|
|
message =
|
|
String::format(
|
|
IDS_DYN_DNS_MESSAGE_ZONE_IS_ROOT,
|
|
domain.c_str(),
|
|
domain.c_str());
|
|
details =
|
|
String::format(
|
|
IDS_DYN_DNS_DETAIL_ROOT_ZONE,
|
|
authServer.c_str(),
|
|
authServerIpAddress.c_str());
|
|
helpTopicLink = L"DNSConcepts.chm::/sag_DNS_tro_dynamic_message8.htm";
|
|
defaultButton = IDC_INSTALL_DNS;
|
|
ShowButtons(true);
|
|
break;
|
|
}
|
|
|
|
// Message 11
|
|
|
|
case TIMEOUT:
|
|
{
|
|
message = String::load(IDS_DYN_DNS_MESSAGE_TIMEOUT);
|
|
details =
|
|
String::format(
|
|
IDS_DYN_DNS_DETAIL_SCANT,
|
|
serverName.c_str(),
|
|
GetErrorMessage(Win32ToHresult(errorCode)).c_str(),
|
|
errorCode,
|
|
MyDnsStatusString(errorCode).c_str());
|
|
helpTopicLink = L"DNSConcepts.chm::/sag_DNS_tro_dynamic_message11.htm";
|
|
defaultButton = IDC_INSTALL_DNS;
|
|
ShowButtons(true);
|
|
break;
|
|
}
|
|
|
|
// Message 4
|
|
|
|
case UNEXPECTED_FINDING_SERVER:
|
|
|
|
// Anything else
|
|
|
|
default:
|
|
{
|
|
|
|
#ifdef DBG
|
|
ASSERT(authServer.empty());
|
|
ASSERT(authZone.empty());
|
|
ASSERT(authServerIpAddress.empty());
|
|
|
|
if (diagnosticResultCode != UNEXPECTED_FINDING_SERVER)
|
|
{
|
|
ASSERT(false);
|
|
}
|
|
#endif
|
|
|
|
message = String::load(IDS_DYN_DNS_MESSAGE_UNEXPECTED);
|
|
|
|
details =
|
|
String::format(
|
|
IDS_DYN_DNS_DETAIL_SCANT,
|
|
serverName.c_str(),
|
|
GetErrorMessage(Win32ToHresult(errorCode)).c_str(),
|
|
errorCode,
|
|
MyDnsStatusString(errorCode).c_str());
|
|
helpTopicLink = L"DNSConcepts.chm::/sag_DNS_tro_dynamic_message4.htm";
|
|
defaultButton = IDC_RETRY;
|
|
ShowButtons(true);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
UpdateMessageWindow(message);
|
|
|
|
// success always forces the ignore option
|
|
|
|
if (diagnosticResultCode == SUCCESS)
|
|
{
|
|
SelectRadioButton(IDC_IGNORE);
|
|
}
|
|
else
|
|
{
|
|
// On the first pass only, decide what radio button to set. On
|
|
// subsequent passes, the user will have had the chance to change the
|
|
// button selection, so we don't change his selections.
|
|
|
|
if (testPassCount == 1)
|
|
{
|
|
int button = defaultButton;
|
|
|
|
ASSERT(diagnosticResultCode != SUCCESS);
|
|
|
|
// if the test failed, and the wizard is running unattended, then
|
|
// consult the answer file for the user's preference in dealing
|
|
// with the failure.
|
|
|
|
if (state.UsingAnswerFile())
|
|
{
|
|
String option =
|
|
state.GetAnswerFileOption(AnswerFile::OPTION_AUTO_CONFIG_DNS);
|
|
|
|
if (
|
|
option.empty()
|
|
|| (option.icompare(AnswerFile::VALUE_YES) == 0) )
|
|
{
|
|
button = IDC_INSTALL_DNS;
|
|
}
|
|
else
|
|
{
|
|
button = IDC_IGNORE;
|
|
}
|
|
}
|
|
|
|
SelectRadioButton(button);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
DynamicDnsPage::OnSetActive()
|
|
{
|
|
LOG_FUNCTION(DynamicDnsPage::OnSetActive);
|
|
|
|
State& state = State::GetInstance();
|
|
State::Operation oper = state.GetOperation();
|
|
|
|
// these are the only operations for which this page is valid; i.e.
|
|
// new domain scenarios
|
|
|
|
if (
|
|
oper == State::FOREST
|
|
|| oper == State::CHILD
|
|
|| oper == State::TREE)
|
|
{
|
|
DoDnsTestAndUpdatePage();
|
|
needToKillSelection = true;
|
|
}
|
|
|
|
if (
|
|
( oper != State::FOREST
|
|
&& oper != State::CHILD
|
|
&& oper != State::TREE)
|
|
|| state.RunHiddenUnattended() )
|
|
{
|
|
LOG(L"Planning to Skip DynamicDnsPage");
|
|
|
|
Wizard& wizard = GetWizard();
|
|
|
|
if (wizard.IsBacktracking())
|
|
{
|
|
// backup once again
|
|
|
|
wizard.Backtrack(hwnd);
|
|
return true;
|
|
}
|
|
|
|
int nextPage = Validate();
|
|
if (nextPage != -1)
|
|
{
|
|
LOG(L"skipping DynamicDnsPage");
|
|
wizard.SetNextPageID(hwnd, nextPage);
|
|
return true;
|
|
}
|
|
|
|
state.ClearHiddenWhileUnattended();
|
|
}
|
|
|
|
Win::PropSheet_SetWizButtons(
|
|
Win::GetParent(hwnd),
|
|
PSWIZB_BACK | PSWIZB_NEXT);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
DumpButtons(HWND dialog)
|
|
{
|
|
LOG(String::format(L"retry : (%1)", Win::IsDlgButtonChecked(dialog, IDC_RETRY) ? L"*" : L" "));
|
|
LOG(String::format(L"ignore : (%1)", Win::IsDlgButtonChecked(dialog, IDC_IGNORE) ? L"*" : L" "));
|
|
LOG(String::format(L"install: (%1)", Win::IsDlgButtonChecked(dialog, IDC_INSTALL_DNS) ? L"*" : L" "));
|
|
}
|
|
|
|
|
|
|
|
int
|
|
DynamicDnsPage::Validate()
|
|
{
|
|
LOG_FUNCTION(DynamicDnsPage::Validate);
|
|
|
|
int nextPage = -1;
|
|
|
|
do
|
|
{
|
|
State& state = State::GetInstance();
|
|
State::Operation oper = state.GetOperation();
|
|
|
|
DumpButtons(hwnd);
|
|
|
|
if (
|
|
oper != State::FOREST
|
|
&& oper != State::CHILD
|
|
&& oper != State::TREE)
|
|
{
|
|
// by definition valid, as the page does not apply
|
|
|
|
State::GetInstance().SetAutoConfigureDNS(false);
|
|
nextPage = IDD_RAS_FIXUP;
|
|
break;
|
|
}
|
|
|
|
if (
|
|
diagnosticResultCode == SUCCESS
|
|
|| Win::IsDlgButtonChecked(hwnd, IDC_IGNORE))
|
|
{
|
|
// You can go about your business. Move along, move long.
|
|
|
|
// Force ignore, even if the user previously had encountered a
|
|
// failure and chose retry or install DNS. We do this in case the
|
|
// user backed up in the wizard and corrected the domain name.
|
|
|
|
State::GetInstance().SetAutoConfigureDNS(false);
|
|
nextPage = IDD_RAS_FIXUP;
|
|
break;
|
|
}
|
|
|
|
// if the radio button selection = retry, then do the test over again,
|
|
// and stick to this page.
|
|
|
|
if (Win::IsDlgButtonChecked(hwnd, IDC_RETRY))
|
|
{
|
|
DoDnsTestAndUpdatePage();
|
|
break;
|
|
}
|
|
|
|
ASSERT(Win::IsDlgButtonChecked(hwnd, IDC_INSTALL_DNS));
|
|
|
|
State::GetInstance().SetAutoConfigureDNS(true);
|
|
nextPage = IDD_RAS_FIXUP;
|
|
break;
|
|
}
|
|
while (0);
|
|
|
|
LOG(String::format(L"nextPage = %1!d!", nextPage));
|
|
|
|
return nextPage;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
DynamicDnsPage::OnWizBack()
|
|
{
|
|
LOG_FUNCTION(DynamicDnsPage::OnWizBack);
|
|
|
|
// make sure we reset the auto-config flag => the only way it gets set
|
|
// it on the 'next' button.
|
|
|
|
State::GetInstance().SetAutoConfigureDNS(false);
|
|
|
|
return DCPromoWizardPage::OnWizBack();
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
DynamicDnsPage::OnCommand(
|
|
HWND windowFrom,
|
|
unsigned controlIdFrom,
|
|
unsigned code)
|
|
{
|
|
bool result = false;
|
|
|
|
switch (controlIdFrom)
|
|
{
|
|
case IDC_MESSAGE:
|
|
{
|
|
switch (code)
|
|
{
|
|
case EN_SETFOCUS:
|
|
{
|
|
if (needToKillSelection)
|
|
{
|
|
// kill the text selection
|
|
|
|
Win::Edit_SetSel(windowFrom, 0, 0);
|
|
needToKillSelection = false;
|
|
result = true;
|
|
}
|
|
break;
|
|
}
|
|
case MultiLineEditBoxThatForwardsEnterKey::FORWARDED_ENTER:
|
|
{
|
|
// our subclasses mutli-line edit control will send us
|
|
// WM_COMMAND messages when the enter key is pressed. We
|
|
// reinterpret this message as a press on the default button of
|
|
// the prop sheet.
|
|
// This workaround from phellyar.
|
|
// NTRAID#NTBUG9-232092-2000/11/22-sburns
|
|
|
|
HWND propSheet = Win::GetParent(hwnd);
|
|
int defaultButtonId =
|
|
Win::Dialog_GetDefaultButtonId(propSheet);
|
|
|
|
// we expect that there is always a default button on the prop sheet
|
|
|
|
ASSERT(defaultButtonId);
|
|
|
|
Win::SendMessage(
|
|
propSheet,
|
|
WM_COMMAND,
|
|
MAKELONG(defaultButtonId, BN_CLICKED),
|
|
0);
|
|
|
|
result = true;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// do nothing
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// do nothing
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
DynamicDnsPage::OnNotify(
|
|
HWND /* windowFrom */ ,
|
|
UINT_PTR controlIDFrom,
|
|
UINT code,
|
|
LPARAM lParam)
|
|
{
|
|
bool result = false;
|
|
|
|
if (controlIDFrom == IDC_MESSAGE)
|
|
{
|
|
switch (code)
|
|
{
|
|
case EN_LINK:
|
|
{
|
|
ENLINK *enlink = reinterpret_cast<ENLINK*>(lParam);
|
|
|
|
if (enlink && enlink->msg == WM_LBUTTONUP)
|
|
{
|
|
ASSERT(!helpTopicLink.empty());
|
|
|
|
if (!helpTopicLink.empty())
|
|
{
|
|
Win::HtmlHelp(hwnd, helpTopicLink, HH_DISPLAY_TOPIC, 0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// do nothing
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|