using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net.Mail; using System.Text; using System.Text.RegularExpressions; using System.Threading; using WebsitePanel.Providers.DNS; using WebsitePanel.Providers.DomainLookup; using WebsitePanel.Server; namespace WebsitePanel.EnterpriseServer { public class DomainLookupViewTask : SchedulerTask { private static readonly string TaskId = "SCHEDULE_TASK_DOMAIN_LOOKUP"; // Input parameters: private static readonly string DnsServersParameter = "DNS_SERVERS"; private static readonly string MailToParameter = "MAIL_TO"; private static readonly string MailBodyTemplateParameter = "MAIL_BODY"; private static readonly string MailBodyDomainRecordTemplateParameter = "MAIL_DOMAIN_RECORD"; private static readonly string ServerNameParameter = "SERVER_NAME"; private const string MxRecordPattern = @"mail exchanger = (.+)"; private const string NsRecordPattern = @"nameserver = (.+)"; private const string DnsTimeOutMessage = @"dns request timed out"; private const int DnsTimeOutRetryCount = 3; public override void DoWork() { BackgroundTask topTask = TaskManager.TopTask; List domainsChanges = new List(); var domainUsers = new Dictionary(); // get input parameters string dnsServersString = (string)topTask.GetParamValue(DnsServersParameter); string serverName = (string)topTask.GetParamValue(ServerNameParameter); // check input parameters if (String.IsNullOrEmpty(dnsServersString)) { TaskManager.WriteWarning("Specify 'DNS' task parameter."); return; } if (String.IsNullOrEmpty((string)topTask.GetParamValue("MAIL_TO"))) { TaskManager.WriteWarning("The e-mail message has not been sent because 'Mail To' is empty."); return; } // find server by name ServerInfo server = ServerController.GetServerByName(serverName); if (server == null) { TaskManager.WriteWarning(String.Format("Server with the name '{0}' was not found", serverName)); return; } WindowsServer winServer = new WindowsServer(); ServiceProviderProxy.ServerInit(winServer, server.ServerId); var user = UserController.GetUser(topTask.UserId); var dnsServers = dnsServersString.Split(';'); var packages = ObjectUtils.CreateListFromDataReader(DataProvider.GetAllPackages()); foreach (var package in packages) { var domains = ServerController.GetDomains(package.PackageId); domains = domains.Where(x => !x.IsSubDomain && !x.IsDomainPointer).ToList(); //Selecting top-level domains //domains = domains.Where(x => x.ZoneItemId > 0).ToList(); //Selecting only dns enabled domains foreach (var domain in domains) { if (domainsChanges.Any(x => x.DomainName == domain.DomainName)) { continue; } if (!domainUsers.ContainsKey(domain.PackageId)) { var domainUser = UserController.GetUser(packages.First(x=>x.PackageId == domain.PackageId).UserId); domainUsers.Add(domain.PackageId, domainUser); } DomainDnsChanges domainChanges = new DomainDnsChanges(); domainChanges.DomainName = domain.DomainName; domainChanges.PackageId = domain.PackageId; var dbDnsRecords = ObjectUtils.CreateListFromDataReader(DataProvider.GetDomainAllDnsRecords(domain.DomainId)); //execute server foreach (var dnsServer in dnsServers) { var dnsMxRecords = GetDomainDnsRecords(winServer, domain.DomainName, dnsServer, DnsRecordType.MX) ?? dbDnsRecords.Where(x => x.RecordType == DnsRecordType.MX).ToList(); var dnsNsRecords = GetDomainDnsRecords(winServer, domain.DomainName, dnsServer, DnsRecordType.NS) ?? dbDnsRecords.Where(x => x.RecordType == DnsRecordType.NS).ToList(); FillRecordData(dnsMxRecords, domain, dnsServer); FillRecordData(dnsNsRecords, domain, dnsServer); domainChanges.DnsChanges.AddRange(ApplyDomainRecordsChanges(dbDnsRecords.Where(x => x.RecordType == DnsRecordType.MX), dnsMxRecords, dnsServer)); domainChanges.DnsChanges.AddRange(ApplyDomainRecordsChanges(dbDnsRecords.Where(x => x.RecordType == DnsRecordType.NS), dnsNsRecords, dnsServer)); domainChanges.DnsChanges = CombineDnsRecordChanges(domainChanges.DnsChanges, dnsServer).ToList(); } domainsChanges.Add(domainChanges); } } var changedDomains = FindDomainsWithChangedRecords(domainsChanges); SendMailMessage(user, changedDomains, domainUsers); } #region Helpers private IEnumerable FindDomainsWithChangedRecords(IEnumerable domainsChanges) { var changedDomains = new List(); foreach (var domainChanges in domainsChanges) { var firstTimeAdditon = domainChanges.DnsChanges.All(x => x.Status == DomainDnsRecordStatuses.Added); if (firstTimeAdditon) { continue; } bool isChanged = domainChanges.DnsChanges.Any(d => d.Status != DomainDnsRecordStatuses.NotChanged); if (isChanged) { changedDomains.Add(domainChanges); } } return changedDomains; } private IEnumerable ApplyDomainRecordsChanges(IEnumerable dbRecords, List dnsRecords, string dnsServer) { var dnsRecordChanges = new List(); var filteredDbRecords = dbRecords.Where(x => x.DnsServer == dnsServer); foreach (var record in filteredDbRecords) { var dnsRecord = dnsRecords.FirstOrDefault(x => x.Value == record.Value); if (dnsRecord != null) { dnsRecordChanges.Add(new DnsRecordInfoChange { OldRecord = record, NewRecord = dnsRecord, Type = record.RecordType, Status = DomainDnsRecordStatuses.NotChanged, DnsServer = dnsServer }); dnsRecords.Remove(dnsRecord); } else { dnsRecordChanges.Add(new DnsRecordInfoChange { OldRecord = record, NewRecord = new DnsRecordInfo { Value = string.Empty}, Type = record.RecordType, Status = DomainDnsRecordStatuses.Removed, DnsServer = dnsServer }); RemoveRecord(record); } } foreach (var record in dnsRecords) { dnsRecordChanges.Add(new DnsRecordInfoChange { OldRecord = new DnsRecordInfo { Value = string.Empty }, NewRecord = record, Type = record.RecordType, Status = DomainDnsRecordStatuses.Added, DnsServer = dnsServer }); AddRecord(record); } return dnsRecordChanges; } private IEnumerable CombineDnsRecordChanges(IEnumerable records, string dnsServer) { var resultRecords = records.Where(x => x.DnsServer == dnsServer).ToList(); var recordsToRemove = new List(); var removedRecords = records.Where(x => x.Status == DomainDnsRecordStatuses.Removed); var addedRecords = records.Where(x => x.Status == DomainDnsRecordStatuses.Added); foreach (DnsRecordType type in (DnsRecordType[])Enum.GetValues(typeof(DnsRecordType))) { foreach (var removedRecord in removedRecords.Where(x => x.Type == type)) { var addedRecord = addedRecords.FirstOrDefault(x => x.Type == type && !recordsToRemove.Contains(x)); if (addedRecord != null) { recordsToRemove.Add(addedRecord); removedRecord.NewRecord = addedRecord.NewRecord; removedRecord.Status = DomainDnsRecordStatuses.Updated; } } } foreach (var record in recordsToRemove) { resultRecords.Remove(record); } return resultRecords; } private void FillRecordData(IEnumerable records, DomainInfo domain, string dnsServer) { foreach (var record in records) { FillRecordData(record, domain, dnsServer); } } private void FillRecordData(DnsRecordInfo record, DomainInfo domain, string dnsServer) { record.DomainId = domain.DomainId; record.Date = DateTime.Now; record.DnsServer = dnsServer; } private void RemoveRecords(IEnumerable dnsRecords) { foreach (var record in dnsRecords) { RemoveRecord(record); } } private void RemoveRecord(DnsRecordInfo dnsRecord) { DataProvider.DeleteDomainDnsRecord(dnsRecord.Id); Thread.Sleep(100); } private void AddRecords(IEnumerable dnsRecords) { foreach (var record in dnsRecords) { AddRecord(record); } } private void AddRecord(DnsRecordInfo dnsRecord) { DataProvider.AddDomainDnsRecord(dnsRecord); Thread.Sleep(100); } private void SendMailMessage(UserInfo user, IEnumerable domainsChanges, Dictionary domainUsers) { BackgroundTask topTask = TaskManager.TopTask; UserSettings settings = UserController.GetUserSettings(user.UserId, UserSettings.DOMAIN_LOOKUP_LETTER); string from = settings["From"]; var bcc = settings["CC"]; string subject = settings["Subject"]; MailPriority priority = MailPriority.Normal; if (!String.IsNullOrEmpty(settings["Priority"])) priority = (MailPriority)Enum.Parse(typeof(MailPriority), settings["Priority"], true); // input parameters string mailTo = (string)topTask.GetParamValue("MAIL_TO"); string body = string.Empty; bool isHtml = user.HtmlMail; if (domainsChanges.Any()) { body = user.HtmlMail ? settings["HtmlBody"] : settings["TextBody"]; } else { body = user.HtmlMail ? settings["NoChangesHtmlBody"] : settings["NoChangesTextBody"]; } Hashtable items = new Hashtable(); items["user"] = user; items["DomainUsers"] = domainUsers; items["Domains"] = domainsChanges; body = PackageController.EvaluateTemplate(body, items); // send mail message MailHelper.SendMessage(from, mailTo, bcc, subject, body, priority, isHtml); } public List GetDomainDnsRecords(WindowsServer winServer, string domain, string dnsServer, DnsRecordType recordType) { //nslookup -type=mx google.com 195.46.39.39 var command = "nslookup"; var args = string.Format("-type={0} {1} {2}", recordType, domain, dnsServer); // execute system command var raw = string.Empty; int triesCount = 0; do { raw = winServer.ExecuteSystemCommand(command, args); } while (raw.ToLowerInvariant().Contains(DnsTimeOutMessage) && ++triesCount < DnsTimeOutRetryCount); //timeout check if (raw.ToLowerInvariant().Contains(DnsTimeOutMessage)) { return null; } var records = ParseNsLookupResult(raw, dnsServer, recordType); return records.ToList(); } private IEnumerable ParseNsLookupResult(string raw, string dnsServer, DnsRecordType recordType) { var records = new List(); var recordTypePattern = string.Empty; switch (recordType) { case DnsRecordType.NS: { recordTypePattern = NsRecordPattern; break; } case DnsRecordType.MX: { recordTypePattern = MxRecordPattern; break; } } var regex = new Regex(recordTypePattern, RegexOptions.IgnoreCase); foreach (Match match in regex.Matches(raw)) { if (match.Groups.Count != 2) { continue; } var dnsRecord = new DnsRecordInfo { Value = match.Groups[1].Value != null ? match.Groups[1].Value.Replace("\r\n", "").Replace("\r", "").Replace("\n", "").ToLowerInvariant().Trim() : null, RecordType = recordType, DnsServer = dnsServer }; records.Add(dnsRecord); } return records; } #endregion } }