// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // - Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // - Neither the name of the Outercurve Foundation nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Xml; using System.Xml.Serialization; using WebsitePanel.Providers; using WebsitePanel.Providers.DNS; namespace WebsitePanel.EnterpriseServer { public class DnsServerController : IImportController, IBackupController { private static DNSServer GetDNSServer(int serviceId) { DNSServer dns = new DNSServer(); ServiceProviderProxy.Init(dns, serviceId); return dns; } public static int AddZone(int packageId, int serviceId, string zoneName) { return AddZone(packageId, serviceId, zoneName, true, false); } public static int AddZone(int packageId, int serviceId, string zoneName, bool addPackageItem, bool ignoreGlobalDNSRecords) { // get DNS provider DNSServer dns = GetDNSServer(serviceId); // check if zone already exists if (dns.ZoneExists(zoneName)) return BusinessErrorCodes.ERROR_DNS_ZONE_EXISTS; // TaskManager.StartTask("DNS_ZONE", "ADD", zoneName); // int zoneItemId = default(int); // try { // get secondary DNS services StringDictionary primSettings = ServerController.GetServiceSettings(serviceId); string[] primaryIPAddresses = GetExternalIPAddressesFromString(primSettings["ListeningIPAddresses"]); List secondaryIPAddresses = new List(); List secondaryServiceIds = new List(); string strSecondaryServices = primSettings["SecondaryDNSServices"]; if (!String.IsNullOrEmpty(strSecondaryServices)) { string[] secondaryServices = strSecondaryServices.Split(','); foreach (string strSecondaryId in secondaryServices) { int secondaryId = Utils.ParseInt(strSecondaryId, 0); if (secondaryId == 0) continue; secondaryServiceIds.Add(secondaryId); StringDictionary secondarySettings = ServerController.GetServiceSettings(secondaryId); // add secondary IPs to the master array secondaryIPAddresses.AddRange( GetExternalIPAddressesFromString(secondarySettings["ListeningIPAddresses"])); } } // add "Allow zone transfers" string allowTransfers = primSettings["AllowZoneTransfers"]; if (!String.IsNullOrEmpty(allowTransfers)) { string[] ips = Utils.ParseDelimitedString(allowTransfers, '\n', ' ', ',', ';'); foreach (string ip in ips) { if (!secondaryIPAddresses.Contains(ip)) secondaryIPAddresses.Add(ip); } } // add primary zone dns.AddPrimaryZone(zoneName, secondaryIPAddresses.ToArray()); // get DNS zone records List records = ServerController.GetDnsRecordsTotal(packageId); // get name servers PackageSettings packageSettings = PackageController.GetPackageSettings(packageId, PackageSettings.NAME_SERVERS); string[] nameServers = new string[] { }; if (!String.IsNullOrEmpty(packageSettings["NameServers"])) nameServers = packageSettings["NameServers"].Split(';'); // build records list List zoneRecords = new List(); string primaryNameServer = "ns." + zoneName; if (nameServers.Length > 0) primaryNameServer = nameServers[0]; // update SOA record string hostmaster = primSettings["ResponsiblePerson"]; if (String.IsNullOrEmpty(hostmaster)) { hostmaster = "hostmaster." + zoneName; } else { hostmaster = Utils.ReplaceStringVariable(hostmaster, "domain_name", zoneName); } dns.UpdateSoaRecord(zoneName, "", primaryNameServer, hostmaster); // add name servers foreach (string nameServer in nameServers) { DnsRecord ns = new DnsRecord(); ns.RecordType = DnsRecordType.NS; ns.RecordName = ""; ns.RecordData = nameServer; zoneRecords.Add(ns); } if (!ignoreGlobalDNSRecords) { // add all other records zoneRecords.AddRange(BuildDnsResourceRecords(records, "", zoneName, "")); } // add zone records dns.AddZoneRecords(zoneName, zoneRecords.ToArray()); // add secondary zones foreach (int secondaryId in secondaryServiceIds) { try { // add secondary zone DNSServer secDns = GetDNSServer(secondaryId); secDns.AddSecondaryZone(zoneName, primaryIPAddresses); RegisterZoneItems(packageId, secondaryId, zoneName, false); } catch (Exception ex) { TaskManager.WriteError(ex, "Error adding secondary zone (service ID = " + secondaryId + ")"); } } if (!addPackageItem) return 0; // add service item zoneItemId = RegisterZoneItems(packageId, serviceId, zoneName, true); // TaskManager.ItemId = zoneItemId; } catch (Exception ex) { TaskManager.WriteError(ex); } finally { TaskManager.CompleteTask(); } // return zoneItemId; } private static int RegisterZoneItems(int spaceId, int serviceId, string zoneName, bool primaryZone) { // zone item DnsZone zone = primaryZone ? new DnsZone() : new SecondaryDnsZone(); zone.Name = zoneName; zone.PackageId = spaceId; zone.ServiceId = serviceId; int zoneItemId = PackageController.AddPackageItem(zone); return zoneItemId; } public static int DeleteZone(int zoneItemId) { // delete DNS zone if applicable DnsZone zoneItem = (DnsZone)PackageController.GetPackageItem(zoneItemId); // if (zoneItem != null) { TaskManager.StartTask("DNS_ZONE", "DELETE", zoneItem.Name, zoneItemId); // try { // delete DNS zone DNSServer dns = new DNSServer(); ServiceProviderProxy.Init(dns, zoneItem.ServiceId); // delete secondary zones StringDictionary primSettings = ServerController.GetServiceSettings(zoneItem.ServiceId); string strSecondaryServices = primSettings["SecondaryDNSServices"]; if (!String.IsNullOrEmpty(strSecondaryServices)) { string[] secondaryServices = strSecondaryServices.Split(','); foreach (string strSecondaryId in secondaryServices) { try { int secondaryId = Utils.ParseInt(strSecondaryId, 0); if (secondaryId == 0) continue; DNSServer secDns = new DNSServer(); ServiceProviderProxy.Init(secDns, secondaryId); secDns.DeleteZone(zoneItem.Name); } catch (Exception ex1) { // problem when deleting secondary zone TaskManager.WriteError(ex1, "Error deleting secondary DNS zone"); } } } try { dns.DeleteZone(zoneItem.Name); } catch (Exception ex2) { TaskManager.WriteError(ex2, "Error deleting primary DNS zone"); } // delete service item PackageController.DeletePackageItem(zoneItemId); } catch (Exception ex) { TaskManager.WriteError(ex); } finally { TaskManager.CompleteTask(); } } // return 0; } public static List BuildDnsResourceRecords(List records, string hostName, string domainName, string serviceIP) { List zoneRecords = new List(); foreach (GlobalDnsRecord record in records) { DnsRecord rr = new DnsRecord(); rr.RecordType = (DnsRecordType)Enum.Parse(typeof(DnsRecordType), record.RecordType, true); rr.RecordName = Utils.ReplaceStringVariable(record.RecordName, "host_name", hostName, true); if (record.RecordType == "A" || record.RecordType == "AAAA") { // If the service IP address and the DNS records external address are empty / null SimpleDNS will fail to properly create the zone record if (String.IsNullOrEmpty(serviceIP) && String.IsNullOrEmpty(record.ExternalIP) && String.IsNullOrEmpty(record.RecordData)) continue; rr.RecordData = String.IsNullOrEmpty(record.RecordData) ? record.ExternalIP : record.RecordData; rr.RecordData = Utils.ReplaceStringVariable(rr.RecordData, "ip", string.IsNullOrEmpty(serviceIP) ? record.ExternalIP : serviceIP); rr.RecordData = Utils.ReplaceStringVariable(rr.RecordData, "domain_name", string.IsNullOrEmpty(domainName) ? string.Empty : domainName); if (String.IsNullOrEmpty(rr.RecordData) && !String.IsNullOrEmpty(serviceIP)) rr.RecordData = serviceIP; } else if (record.RecordType == "SRV") { rr.SrvPriority = record.SrvPriority; rr.SrvWeight = record.SrvWeight; rr.SrvPort = record.SrvPort; rr.RecordText = record.RecordData; rr.RecordData = record.RecordData; } else { rr.RecordData = record.RecordData; } // substitute variables rr.RecordData = Utils.ReplaceStringVariable(rr.RecordData, "domain_name", domainName); // add MX priority if (record.RecordType == "MX") rr.MxPriority = record.MxPriority; if (!String.IsNullOrEmpty(rr.RecordData)) { if (rr.RecordName != "[host_name]") zoneRecords.Add(rr); } } return zoneRecords; } public static string[] GetExternalIPAddressesFromString(string str) { List ips = new List(); if (str != null && str.Trim() != "") { string[] sips = str.Split(','); foreach (string sip in sips) { IPAddressInfo ip = ServerController.GetIPAddress(Int32.Parse(sip)); if (ip != null) ips.Add(ip.ExternalIP); } } return ips.ToArray(); } #region IImportController Members public List GetImportableItems(int packageId, int itemTypeId, Type itemType, ResourceGroupInfo group) { List items = new List(); // get service id int serviceId = PackageController.GetPackageServiceId(packageId, group.GroupName); if (serviceId == 0) return items; // Mail provider DNSServer dns = new DNSServer(); ServiceProviderProxy.Init(dns, serviceId); if (itemType == typeof(DnsZone)) items.AddRange(dns.GetZones()); return items; } public void ImportItem(int packageId, int itemTypeId, Type itemType, ResourceGroupInfo group, string itemName) { // get service id int serviceId = PackageController.GetPackageServiceId(packageId, group.GroupName); if (serviceId == 0) return; if (itemType == typeof(DnsZone)) { // add DNS zone DnsZone zone = new DnsZone(); zone.Name = itemName; zone.ServiceId = serviceId; zone.PackageId = packageId; int zoneId = PackageController.AddPackageItem(zone); // add/update domains/pointers RestoreDomainByZone(itemName, packageId, zoneId); } } private void RestoreDomainByZone(string itemName, int packageId, int zoneId) { DomainInfo domain = ServerController.GetDomain(itemName); if (domain == null) { domain = new DomainInfo(); domain.DomainName = itemName; domain.PackageId = packageId; domain.ZoneItemId = zoneId; ServerController.AddDomainItem(domain); } else { domain.ZoneItemId = zoneId; ServerController.UpdateDomain(domain); } } #endregion #region IBackupController Members public int BackupItem(string tempFolder, XmlWriter writer, ServiceProviderItem item, ResourceGroupInfo group) { if (!(item is DnsZone)) return 0; // DNS provider DNSServer dns = GetDNSServer(item.ServiceId); // zone records serialized XmlSerializer serializer = new XmlSerializer(typeof(DnsRecord)); try { // get zone records DnsRecord[] records = dns.GetZoneRecords(item.Name); // serialize zone records foreach (DnsRecord record in records) serializer.Serialize(writer, record); } catch (Exception ex) { TaskManager.WriteError(ex, "Could not read zone records"); } return 0; } public int RestoreItem(string tempFolder, XmlNode itemNode, int itemId, Type itemType, string itemName, int packageId, int serviceId, ResourceGroupInfo group) { if (itemType != typeof(DnsZone)) return 0; // DNS provider DNSServer dns = GetDNSServer(serviceId); // check service item if (!dns.ZoneExists(itemName)) { // create primary and secondary zones AddZone(packageId, serviceId, itemName, false, false); // restore records XmlSerializer serializer = new XmlSerializer(typeof(DnsRecord)); List records = new List(); foreach (XmlNode childNode in itemNode.ChildNodes) { if (childNode.Name == "DnsRecord") { records.Add((DnsRecord)serializer.Deserialize(new XmlNodeReader(childNode))); } } dns.AddZoneRecords(itemName, records.ToArray()); } // check if meta-item exists int zoneId = 0; DnsZone item = (DnsZone)PackageController.GetPackageItemByName(packageId, itemName, typeof(DnsZone)); if (item == null) { // restore meta-item item = new DnsZone(); item.Name = itemName; item.PackageId = packageId; item.ServiceId = serviceId; zoneId = PackageController.AddPackageItem(item); } else { zoneId = item.Id; } // restore domains RestoreDomainByZone(itemName, packageId, zoneId); return 0; } #endregion } }