// Copyright (c) 2001 Microsoft Corporation // // File: DNSInstallationUnit.cpp // // Synopsis: Defines a DNSInstallationUnit // This object has the knowledge for installing the // DNS service // // History: 02/05/2001 JeffJon Created #include "pch.h" #include "resource.h" #include "DNSInstallationUnit.h" #include "InstallationUnitProvider.h" #include "NetworkInterface.h" // Finish page help static PCWSTR CYS_DNS_FINISH_PAGE_HELP = L"cys.chm::/dns_server_role.htm"; static PCWSTR CYS_DNS_MILESTONE_HELP = L"cys.chm::/dns_server_role.htm#dnssrvsummary"; static PCWSTR CYS_DNS_AFTER_FINISH_HELP = L"cys.chm::/dns_server_role.htm#dnssrvcompletion"; DNSInstallationUnit::DNSInstallationUnit() : staticIPAddress(CYS_DEFAULT_IPADDRESS), subnetMask(CYS_DEFAULT_SUBNETMASK), forwarderIPAddress(0), manualForwarder(false), dnsRoleResult(DNS_SUCCESS), installedDescriptionID(IDS_DNS_SERVER_DESCRIPTION_INSTALLED), ExpressPathInstallationUnitBase( IDS_DNS_SERVER_TYPE, IDS_DNS_SERVER_DESCRIPTION, IDS_DNS_FINISH_TITLE, IDS_DNS_FINISH_UNINSTALL_TITLE, IDS_DNS_FINISH_MESSAGE, IDS_DNS_INSTALL_FAILED, IDS_DNS_UNINSTALL_MESSAGE, IDS_DNS_UNINSTALL_FAILED, IDS_DNS_UNINSTALL_WARNING, IDS_DNS_UNINSTALL_CHECKBOX, CYS_DNS_FINISH_PAGE_HELP, CYS_DNS_MILESTONE_HELP, CYS_DNS_AFTER_FINISH_HELP, DNS_SERVER) { LOG_CTOR(DNSInstallationUnit); } DNSInstallationUnit::~DNSInstallationUnit() { LOG_DTOR(DNSInstallationUnit); } InstallationReturnType DNSInstallationUnit::InstallService(HANDLE logfileHandle, HWND hwnd) { LOG_FUNCTION(DNSInstallationUnit::InstallService); InstallationReturnType result = INSTALL_SUCCESS; if (IsExpressPathInstall()) { result = ExpressPathInstall(logfileHandle, hwnd); LOG_INSTALL_RETURN(result); return result; } dnsRoleResult = DNS_SUCCESS; // Log the DNS header CYS_APPEND_LOG(String::load(IDS_LOG_DNS_HEADER)); UpdateInstallationProgressText(hwnd, IDS_DNS_INSTALL_PROGRESS); // Create the inf and unattend files that are used by the // Optional Component Manager String infFileText; String unattendFileText; CreateInfFileText(infFileText, IDS_DNS_INF_WINDOW_TITLE); CreateUnattendFileText(unattendFileText, CYS_DNS_SERVICE_NAME); // Install the service through the Optional Component Manager String additionalArgs = L"/z:netoc_show_unattended_messages"; // We are ignoring the ocmresult because it doesn't matter // with respect to whether the role is installed or not InstallServiceWithOcManager( infFileText, unattendFileText, additionalArgs); if (IsServiceInstalled()) { // Log the successful installation LOG(L"DNS was installed successfully"); CYS_APPEND_LOG(String::load(IDS_LOG_SERVER_START_DNS)); // Wait for the service to enter the running state NTService serviceObject(CYS_DNS_SERVICE_NAME); HRESULT hr = serviceObject.WaitForServiceState(SERVICE_RUNNING); if (FAILED(hr)) { // This is a config failure because as far as we are concerned the // service was installed properly. dnsRoleResult = DNS_SERVICE_START_FAILURE; LOG(String::format( L"The DNS service failed to start in a timely fashion: %1!x!", hr)); CYS_APPEND_LOG(String::load(IDS_LOG_DNS_SERVICE_TIMEOUT)); result = INSTALL_FAILURE; } else { // Run the DNS Wizard UpdateInstallationProgressText(hwnd, IDS_DNS_CONFIG_PROGRESS); String resultText; HRESULT unused = S_OK; if (ExecuteWizard(hwnd, CYS_DNS_SERVICE_NAME, resultText, unused)) { // Check to be sure the wizard finished completely String configWizardResults; if (ReadConfigWizardRegkeys(configWizardResults)) { // The Configure DNS Server Wizard completed successfully LOG(L"The Configure DNS Server Wizard completed successfully"); CYS_APPEND_LOG(String::load(IDS_LOG_DNS_COMPLETED_SUCCESSFULLY)); } else { // The Configure DNS Server Wizard did not finish successfully dnsRoleResult = DNS_CONFIG_FAILURE; result = INSTALL_FAILURE; if (!configWizardResults.empty()) { // An error was returned via the regkey LOG(String::format( L"The Configure DNS Server Wizard returned the error: %1", configWizardResults.c_str())); String formatString = String::load(IDS_LOG_DNS_WIZARD_ERROR); CYS_APPEND_LOG(String::format(formatString, configWizardResults.c_str())); } else { // The Configure DNS Server Wizard was cancelled by the user LOG(L"The Configure DNS Server Wizard was cancelled by the user"); CYS_APPEND_LOG(String::load(IDS_LOG_DNS_WIZARD_CANCELLED)); } } } else { dnsRoleResult = DNS_INSTALL_FAILURE; // Show an error LOG(L"DNS could not be installed."); if (!resultText.empty()) { CYS_APPEND_LOG(resultText); } } } } else { dnsRoleResult = DNS_INSTALL_FAILURE; // Log the failure LOG(L"DNS failed to install"); CYS_APPEND_LOG(String::load(IDS_LOG_DNS_SERVER_FAILED)); result = INSTALL_FAILURE; } LOG_INSTALL_RETURN(result); return result; } UnInstallReturnType DNSInstallationUnit::UnInstallService(HANDLE logfileHandle, HWND hwnd) { LOG_FUNCTION(DNSInstallationUnit::UnInstallService); UnInstallReturnType result = UNINSTALL_SUCCESS; // Log the DNS header CYS_APPEND_LOG(String::load(IDS_LOG_UNINSTALL_DNS_HEADER)); UpdateInstallationProgressText(hwnd, IDS_DNS_UNINSTALL_PROGRESS); String infFileText; String unattendFileText; CreateInfFileText(infFileText, IDS_DNS_INF_WINDOW_TITLE); CreateUnattendFileText(unattendFileText, CYS_DNS_SERVICE_NAME, false); // NTRAID#NTBUG9-736557-2002/11/13-JeffJon // Pass the /w switch to sysocmgr when uninstalling // so that if a situation occurs in which a reboot // is required, the user will be prompted. String additionalArgs = L"/w"; // We are ignoring the ocmresult because it doesn't matter // with respect to whether the role is installed or not InstallServiceWithOcManager( infFileText, unattendFileText, additionalArgs); if (IsServiceInstalled()) { CYS_APPEND_LOG(String::load(IDS_LOG_UNINSTALL_DNS_FAILED)); result = UNINSTALL_FAILURE; } else { CYS_APPEND_LOG(String::load(IDS_LOG_UNINSTALL_DNS_SUCCESS)); } LOG_UNINSTALL_RETURN(result); return result; } void DNSInstallationUnit::SetForwardersForExpressPath() { LOG_FUNCTION(DNSInstallationUnit::SetForwardersForExpressPath); // If the forwarders were set manually write them // to the registry so that we can set // a forwarder after the reboot // Note: this will write a zero entry if the user // chose not to forward. The code run after reboot // needs to handle this properly if (IsManualForwarder()) { if (!SetRegKeyValue( CYS_FIRST_DC_REGKEY, CYS_FIRST_DC_FORWARDER, forwarderIPAddress, HKEY_LOCAL_MACHINE, true)) { LOG(L"Failed to set forwarder regkey"); } } else { // Write the current DNS servers as forwarders to the registry // so that we don't have problems after the reboot IPAddressList forwarders; GetForwarders(forwarders); if (forwarders.empty()) { LOG(L"No DNS servers set on any NIC"); } else { // Format the IP addresses into a string for storage // in the registry String ipList; for (IPAddressList::iterator itr = forwarders.begin(); itr != forwarders.end(); ++itr) { if (!ipList.empty()) { ipList += L" "; } ipList += String::format( L"%1", IPAddressToString(*itr).c_str()); } if (!SetRegKeyValue( CYS_FIRST_DC_REGKEY, CYS_FIRST_DC_AUTOFORWARDER, ipList, HKEY_LOCAL_MACHINE, true)) { LOG(L"We failed to set the forwarders regkey."); } } } } InstallationReturnType DNSInstallationUnit::ExpressPathInstall(HANDLE logfileHandle, HWND hwnd) { LOG_FUNCTION(DNSInstallationUnit::ExpressPathInstall); InstallationReturnType result = INSTALL_SUCCESS; do { String netshPath = GetNetshPath(); String commandLine; HRESULT hr = S_OK; UpdateInstallationProgressText(hwnd, IDS_DNS_CLIENT_CONFIG_PROGRESS); // We ignore if the NIC is found or not because the function will return // the first NIC if the correct NIC is not found. We can then use this // to setup the network NetworkInterface* nic = State::GetInstance().GetLocalNIC(); if (!nic) { LOG(L"Couldn't find the NIC so fail"); result = INSTALL_FAILURE; CYS_APPEND_LOG( String::load(IDS_EXPRESS_DNS_LOG_STATIC_IP_FAILED)); InstallationUnitProvider::GetInstance(). GetExpressInstallationUnit().SetExpressRoleResult( ExpressInstallationUnit::EXPRESS_DNS_FAILURE); break; } // set static IP address and subnet mask String friendlyName = nic->GetFriendlyName( String::load(IDS_LOCAL_AREA_CONNECTION)); if (nic->IsDHCPEnabled() || nic->GetIPAddress(0) == 0) { // invoke netsh and wait for it to terminate String availableIPAddress = IPAddressToString( nic->GetNextAvailableIPAddress( CYS_DEFAULT_IPADDRESS, CYS_DEFAULT_SUBNETMASK)); commandLine = String::format( L"interface ip set address " L"name=\"%1\" source=static addr=%2 mask=%3 gateway=none", friendlyName.c_str(), availableIPAddress.c_str(), CYS_DEFAULT_SUBNETMASK_STRING); DWORD exitCode1 = 0; hr = ::CreateAndWaitForProcess( netshPath, commandLine, exitCode1, true); if (FAILED(hr) || exitCode1) { LOG(String::format( L"Failed to set the static IP address and subnet mask: exitCode = %1!x!", exitCode1)); result = INSTALL_FAILURE; CYS_APPEND_LOG( String::load(IDS_EXPRESS_DNS_LOG_STATIC_IP_FAILED)); InstallationUnitProvider::GetInstance(). GetExpressInstallationUnit().SetExpressRoleResult( ExpressInstallationUnit::EXPRESS_DNS_FAILURE); break; } ASSERT(SUCCEEDED(hr)); // NTRAID#NTBUG9-638337-2002/06/13-JeffJon // Now that the IP address was set, write it to a regkey so // that we can compare it to the IP address on reboot and // give a failure if they are different. if (!SetRegKeyValue( CYS_FIRST_DC_REGKEY, CYS_FIRST_DC_STATIC_IP, availableIPAddress, HKEY_LOCAL_MACHINE, true)) { LOG(L"Failed to set the static IP regkey"); } CYS_APPEND_LOG( String::format( IDS_EXPRESS_IPADDRESS_SUCCESS, availableIPAddress.c_str())); CYS_APPEND_LOG( String::format( IDS_EXPRESS_SUBNETMASK_SUCCESS, CYS_DEFAULT_SUBNETMASK_STRING)); // Set the IP address and subnet mask on the NetworkInterface object nic->SetIPAddress( StringToIPAddress(availableIPAddress), availableIPAddress); nic->SetSubnetMask( CYS_DEFAULT_SUBNETMASK, CYS_DEFAULT_SUBNETMASK_STRING); } // NTRAID#NTBUG9-664171-2002/07/15-JeffJon // The forwarders must be read and set after setting the static // IP address or else we may be adding multiple entries for // the new static IP address in the forwarders list SetForwardersForExpressPath(); // set DNS server address to same address as the private NIC of // local machine for all NICs. In most cases this will be 192.168.0.1 // netsh does not allow the dns server address to be the loopback address. for (unsigned int nicIndex = 0; nicIndex < State::GetInstance().GetNICCount(); ++nicIndex) { NetworkInterface* currentNIC = State::GetInstance().GetNIC(nicIndex); if (!currentNIC) { continue; } // First check to be sure the IP address isn't already in the list bool okToAddDNSServer = true; IPAddressList dnsServers; currentNIC->GetDNSServers(dnsServers); for (IPAddressList::iterator itr = dnsServers.begin(); itr != dnsServers.end(); ++itr) { if (itr && *itr == nic->GetIPAddress(0)) { okToAddDNSServer = false; break; } } // Add the IP address to the DNS servers since it // isn't already in the list if (okToAddDNSServer) { String currentFriendlyName = currentNIC->GetFriendlyName( String::load(IDS_LOCAL_AREA_CONNECTION)); commandLine = String::format( L"interface ip set dns name=\"%1\" source=static addr=%2", currentFriendlyName.c_str(), nic->GetStringIPAddress(0).c_str()); DWORD exitCode2 = 0; hr = ::CreateAndWaitForProcess( netshPath, commandLine, exitCode2, true); if (FAILED(hr) || exitCode2) { LOG(String::format( L"Failed to set the preferred DNS server IP address: exitCode = %1!x!", exitCode2)); // This should really only be considered a failure for the "local" NIC if (currentFriendlyName.icompare(friendlyName) == 0) { result = INSTALL_FAILURE; InstallationUnitProvider::GetInstance(). GetExpressInstallationUnit().SetExpressRoleResult( ExpressInstallationUnit::EXPRESS_DNS_FAILURE); break; } } } } if (result != INSTALL_FAILURE) { CYS_APPEND_LOG( String::format( IDS_EXPRESS_DNSSERVER_SUCCESS, nic->GetStringIPAddress(0).c_str())); } } while (false); LOG_INSTALL_RETURN(result); return result; } bool DNSInstallationUnit::ReadConfigWizardRegkeys(String& configWizardResults) const { LOG_FUNCTION(DNSInstallationUnit::ReadConfigWizardRegkeys); bool result = false; do { DWORD value = 0; result = GetRegKeyValue( DNS_WIZARD_CONFIG_REGKEY, DNS_WIZARD_CONFIG_VALUE, value); if (result && value != 0) { // The Configure DNS Server Wizard succeeded result = true; break; } // Since there was a failure (or the wizard was cancelled) // get the display string to log GetRegKeyValue( DNS_WIZARD_RESULT_REGKEY, DNS_WIZARD_RESULT_VALUE, configWizardResults); } while (false); LOG_BOOL(result); return result; } bool DNSInstallationUnit::GetMilestoneText(String& message) { LOG_FUNCTION(DNSInstallationUnit::GetMilestoneText); message = String::load(IDS_DNS_FINISH_TEXT); LOG_BOOL(true); return true; } bool DNSInstallationUnit::GetUninstallMilestoneText(String& message) { LOG_FUNCTION(DNSInstallationUnit::GetUninstallMilestoneText); message = String::load(IDS_DNS_UNINSTALL_TEXT); LOG_BOOL(true); return true; } String DNSInstallationUnit::GetUninstallWarningText() { LOG_FUNCTION(DNSInstallationUnit::GetUninstallWarningText); unsigned int messageID = uninstallMilestoneWarningID; if (State::GetInstance().IsDC()) { messageID = IDS_DNS_UNINSTALL_WARNING_ISDC; } return String::load(messageID); } void DNSInstallationUnit::SetStaticIPAddress(DWORD ipaddress) { LOG_FUNCTION2( DNSInstallationUnit::SetStaticIPAddress, IPAddressToString(ipaddress).c_str()); staticIPAddress = ipaddress; } void DNSInstallationUnit::SetSubnetMask(DWORD mask) { LOG_FUNCTION2( DNSInstallationUnit::SetSubnetMask, IPAddressToString(mask).c_str()); subnetMask = mask; } String DNSInstallationUnit::GetStaticIPAddressString() { LOG_FUNCTION(DNSInstallationUnit::GetStaticIPAddressString); String result = IPAddressToString(GetStaticIPAddress()); LOG(result); return result; } String DNSInstallationUnit::GetSubnetMaskString() { LOG_FUNCTION(DNSInstallationUnit::GetSubnetMaskString); String result = IPAddressToString(GetSubnetMask()); LOG(result); return result; } DWORD DNSInstallationUnit::GetStaticIPAddress() { LOG_FUNCTION(DNSInstallationUnit::GetStaticIPAddress); // Get the IP address from the NIC only if it // is a static IP address else use the default NetworkInterface* nic = State::GetInstance().GetNIC(0); if (nic && !nic->IsDHCPEnabled()) { staticIPAddress = nic->GetIPAddress(0); } return staticIPAddress; } DWORD DNSInstallationUnit::GetSubnetMask() { LOG_FUNCTION(DNSInstallationUnit::GetSubnetMask); // Get the subnet mask from the NIC only if it // is a static IP address else use the default NetworkInterface* nic = State::GetInstance().GetNIC(0); if (nic && !nic->IsDHCPEnabled()) { subnetMask = nic->GetSubnetMask(0); } return subnetMask; } void DNSInstallationUnit::SetForwarder(DWORD forwarderAddress) { LOG_FUNCTION2( DNSInstallationUnit::SetForwarder, String::format(L"%1!x!", forwarderAddress)); forwarderIPAddress = forwarderAddress; manualForwarder = true; } void DNSInstallationUnit::GetForwarders(IPAddressList& forwarders) const { LOG_FUNCTION(DNSInstallationUnit::GetForwarders); // clear out the list to start forwarders.clear(); if (IsManualForwarder() && forwarderIPAddress != 0) { DWORD forwarderInDisplayOrder = ConvertIPAddressOrder(forwarderIPAddress); LOG( String::format( L"Adding manual forwarder to list: %1", IPAddressToString(forwarderInDisplayOrder).c_str())); // Forwarder was assigned through the UI forwarders.push_back(forwarderIPAddress); } else if (IsManualForwarder() && forwarderIPAddress == 0) { // The user chose not to forward LOG(L"User chose not to foward"); // Do nothing. Need to check the list returned // to make sure there is a valid address } else { LOG(L"No user defined forwarder. Trying to detect through NICs"); // No forwarder assigned through the UI so // search the NICs for (unsigned int idx = 0; idx < State::GetInstance().GetNICCount(); ++idx) { NetworkInterface* nic = State::GetInstance().GetNIC(idx); // Add the DNS servers from this NIC if (nic) { nic->GetDNSServers(forwarders); } } // Make sure there are no forwarders that are the same as // the IP addresses of any of the NICs for (unsigned int idx = 0; idx < State::GetInstance().GetNICCount(); ++idx) { NetworkInterface* nic = State::GetInstance().GetNIC(idx); if (!nic) { continue; } for (DWORD ipidx = 0; ipidx < nic->GetIPAddressCount(); ++ipidx) { DWORD ipaddress = nic->GetIPAddress(ipidx); for (IPAddressList::iterator itr = forwarders.begin(); itr != forwarders.end(); ++itr) { if (ipaddress == *itr) { // The forwarder matches a local IP address // so remove it LOG(String::format( L"Can't put the local IP address in the forwarders list: %1", IPAddressToString(*itr).c_str())); forwarders.erase(itr); break; } } if (forwarders.empty()) { // It's possible that we removed the last forwarder // so break out if we did break; } } if (forwarders.empty()) { // It's possible that we removed the last forwarder // so break out if we did break; } } } } bool DNSInstallationUnit::IsManualForwarder() const { LOG_FUNCTION(DNSInstallationUnit::IsManualForwarder); LOG_BOOL(manualForwarder); return manualForwarder; } String DNSInstallationUnit::GetServiceDescription() { LOG_FUNCTION(DNSInstallationUnit::GetServiceDescription); String result; unsigned int resultID = descriptionID; if (GetStatus() == STATUS_COMPLETED) { resultID = installedDescriptionID; } result = String::load(resultID); ASSERT(!result.empty()); return result; } void DNSInstallationUnit::ServerRoleLinkSelected(int linkIndex, HWND /*hwnd*/) { LOG_FUNCTION2( DNSInstallationUnit::ServerRoleLinkSelected, String::format( L"linkIndex = %1!d!", linkIndex)); if (IsServiceInstalled()) { ASSERT(linkIndex == 0); LaunchMYS(); } else { ASSERT(linkIndex == 0); LOG(L"Showing configuration help"); ShowHelp(CYS_DNS_FINISH_PAGE_HELP); } } void DNSInstallationUnit::FinishLinkSelected(int linkIndex, HWND /*hwnd*/) { LOG_FUNCTION2( DNSInstallationUnit::FinishLinkSelected, String::format( L"linkIndex = %1!d!", linkIndex)); if (installing) { if (linkIndex == 0 && IsServiceInstalled()) { if (dnsRoleResult == DNS_SUCCESS) { LOG("Showing after checklist"); ShowHelp(CYS_DNS_AFTER_FINISH_HELP); } else if (dnsRoleResult == DNS_SERVICE_START_FAILURE) { LOG(L"Launching Services console"); LaunchMMCConsole(L"services.msc"); } else { LOG(L"Launching DNS snapin"); LaunchMMCConsole(L"dnsmgmt.msc"); } } else { LOG(L"Showing configuration help"); ShowHelp(CYS_DNS_FINISH_PAGE_HELP); } } } String DNSInstallationUnit::GetFinishText() { LOG_FUNCTION(DNSInstallationUnit::GetFinishText); unsigned int messageID = finishMessageID; if (installing) { InstallationReturnType result = GetInstallResult(); if (result != INSTALL_SUCCESS && result != INSTALL_SUCCESS_REBOOT && result != INSTALL_SUCCESS_PROMPT_REBOOT) { if (dnsRoleResult == DNS_INSTALL_FAILURE) { messageID = finishInstallFailedMessageID; } else if (dnsRoleResult == DNS_SERVICE_START_FAILURE) { messageID = IDS_DNS_SERVICE_START_FAILED; } else { messageID = IDS_DNS_CONFIG_FAILED; } } } else { messageID = finishUninstallMessageID; UnInstallReturnType result = GetUnInstallResult(); if (result != UNINSTALL_SUCCESS && result != UNINSTALL_SUCCESS_REBOOT && result != UNINSTALL_SUCCESS_PROMPT_REBOOT) { messageID = finishUninstallFailedMessageID; } } return String::load(messageID); }