diff --git a/DnsServerSystemTrayApp/DnsProvider.cs b/DnsServerSystemTrayApp/DnsProvider.cs new file mode 100644 index 00000000..c818343d --- /dev/null +++ b/DnsServerSystemTrayApp/DnsProvider.cs @@ -0,0 +1,135 @@ +/* +Technitium DNS Server +Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using TechnitiumLibrary.IO; +using TechnitiumLibrary.Net; + +namespace DnsServerSystemTrayApp +{ + public class DnsProvider : IComparable + { + #region variables + + public string Name; + public ICollection Addresses; + + #endregion + + #region constructor + + public DnsProvider(string name, ICollection addresses) + { + this.Name = name; + this.Addresses = addresses; + } + + public DnsProvider(BinaryReader bR) + { + this.Name = bR.ReadShortString(); + this.Addresses = new List(); + + int count = bR.ReadInt32(); + + for (int i = 0; i < count; i++) + this.Addresses.Add(IPAddressExtension.Parse(bR)); + } + + #endregion + + #region static + + public static DnsProvider[] GetDefaultProviders() + { + return new DnsProvider[] { + new DnsProvider("Technitium", new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback }), + new DnsProvider("Cloudflare", new IPAddress[] { IPAddress.Parse("1.1.1.1"), IPAddress.Parse("1.0.0.1"), IPAddress.Parse("[2606:4700:4700::1111]"), IPAddress.Parse("[2606:4700:4700::1001]") }), + new DnsProvider("Google", new IPAddress[] { IPAddress.Parse("8.8.8.8"), IPAddress.Parse("8.8.4.4"), IPAddress.Parse("[2001:4860:4860::8888]"), IPAddress.Parse("[2001:4860:4860::8844]") }), + new DnsProvider("Quad9", new IPAddress[] { IPAddress.Parse("9.9.9.9"), IPAddress.Parse("[2620:fe::fe]") }), + new DnsProvider("OpenDNS", new IPAddress[] { IPAddress.Parse("208.67.222.222"), IPAddress.Parse("208.67.220.220"), IPAddress.Parse("[2620:0:ccc::2]"), IPAddress.Parse("[2620:0:ccd::2]") }) + }; + } + + #endregion + + #region public + + public string GetIpv4Addresses() + { + string ipv4Addresses = null; + + foreach (IPAddress address in Addresses) + { + if (address.AddressFamily == AddressFamily.InterNetwork) + { + if (ipv4Addresses == null) + ipv4Addresses = address.ToString(); + else + ipv4Addresses += ", " + address.ToString(); + } + } + + return ipv4Addresses; + } + + public string GetIpv6Addresses() + { + string ipv6Addresses = null; + + foreach (IPAddress address in Addresses) + { + if (address.AddressFamily == AddressFamily.InterNetworkV6) + { + if (ipv6Addresses == null) + ipv6Addresses = address.ToString(); + else + ipv6Addresses += ", " + address.ToString(); + } + } + + return ipv6Addresses; + } + + public override string ToString() + { + return Name; + } + + public int CompareTo(DnsProvider other) + { + return this.Name.CompareTo(other.Name); + } + + public void WriteTo(BinaryWriter bW) + { + bW.WriteShortString(Name); + + bW.Write(Addresses.Count); + + foreach (IPAddress address in Addresses) + address.WriteTo(bW); + } + + #endregion + } +} diff --git a/DnsServerSystemTrayApp/DnsServerSystemTrayApp.csproj b/DnsServerSystemTrayApp/DnsServerSystemTrayApp.csproj index 11016048..9eeb007f 100644 --- a/DnsServerSystemTrayApp/DnsServerSystemTrayApp.csproj +++ b/DnsServerSystemTrayApp/DnsServerSystemTrayApp.csproj @@ -38,28 +38,43 @@ + + False ..\..\TechnitiumLibrary\bin\TechnitiumLibrary.IO.dll + + ..\..\TechnitiumLibrary\bin\TechnitiumLibrary.Net.dll + - + + Form - - AboutForm.cs + + frmAbout.cs + + + Form + + + frmManageDnsProviders.cs - - AboutForm.cs + + frmAbout.cs + + + frmManageDnsProviders.cs ResXFileCodeGenerator diff --git a/DnsServerSystemTrayApp/MainApplicationContext.cs b/DnsServerSystemTrayApp/MainApplicationContext.cs index ee967d0c..63d5e018 100644 --- a/DnsServerSystemTrayApp/MainApplicationContext.cs +++ b/DnsServerSystemTrayApp/MainApplicationContext.cs @@ -20,6 +20,7 @@ along with this program. If not, see . using DnsServerSystemTrayApp.Properties; using Microsoft.Win32; using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; @@ -43,19 +44,15 @@ namespace DnsServerSystemTrayApp const int SERVICE_WAIT_TIMEOUT_SECONDS = 30; private readonly ServiceController _service = new ServiceController("DnsService"); - readonly IPAddress[] _cloudflareDns = new IPAddress[] { IPAddress.Parse("1.1.1.1"), IPAddress.Parse("1.0.0.1") }; - readonly IPAddress[] _googleDns = new IPAddress[] { IPAddress.Parse("8.8.8.8"), IPAddress.Parse("8.8.4.4") }; - readonly IPAddress[] _quad9Dns = new IPAddress[] { IPAddress.Parse("9.9.9.9") }; + readonly string _configFile; + readonly List _dnsProviders = new List(); private NotifyIcon TrayIcon; private ContextMenuStrip TrayIconContextMenu; private ToolStripMenuItem DashboardMenuItem; private ToolStripMenuItem NetworkDnsMenuItem; private ToolStripMenuItem DefaultNetworkDnsMenuItem; - private ToolStripMenuItem TechnitiumNetworkDnsMenuItem; - private ToolStripMenuItem CloudflareNetworkDnsMenuItem; - private ToolStripMenuItem GoogleNetworkDnsMenuItem; - private ToolStripMenuItem Quad9NetworkDnsMenuItem; + private ToolStripMenuItem ManageNetworkDnsMenuItem; private ToolStripMenuItem ServiceMenuItem; private ToolStripMenuItem StartServiceMenuItem; private ToolStripMenuItem RestartServiceMenuItem; @@ -68,8 +65,11 @@ namespace DnsServerSystemTrayApp #region constructor - public MainApplicationContext() + public MainApplicationContext(string configFile) { + _configFile = configFile; + LoadConfig(); + InitializeComponent(); } @@ -102,7 +102,7 @@ namespace DnsServerSystemTrayApp // // TrayIcon // - var resources = new ComponentResourceManager(typeof(AboutForm)); + var resources = new ComponentResourceManager(typeof(frmAbout)); TrayIcon = new NotifyIcon(); TrayIcon.Icon = (Icon)resources.GetObject("$this.Icon"); TrayIcon.Visible = true; @@ -129,27 +129,8 @@ namespace DnsServerSystemTrayApp DefaultNetworkDnsMenuItem = new ToolStripMenuItem("Default"); DefaultNetworkDnsMenuItem.Click += DefaultNetworkDnsMenuItem_Click; - TechnitiumNetworkDnsMenuItem = new ToolStripMenuItem("Technitium"); - TechnitiumNetworkDnsMenuItem.Click += TechnitiumNetworkDnsMenuItem_Click; - - CloudflareNetworkDnsMenuItem = new ToolStripMenuItem("Cloudflare"); - CloudflareNetworkDnsMenuItem.Click += CloudflareNetworkDnsMenuItem_Click; - - GoogleNetworkDnsMenuItem = new ToolStripMenuItem("Google"); - GoogleNetworkDnsMenuItem.Click += GoogleNetworkDnsMenuItem_Click; - - Quad9NetworkDnsMenuItem = new ToolStripMenuItem("IBM Quad9"); - Quad9NetworkDnsMenuItem.Click += Quad9NetworkDnsMenuItem_Click; - - NetworkDnsMenuItem.DropDownItems.AddRange(new ToolStripItem[] - { - DefaultNetworkDnsMenuItem, - new ToolStripSeparator(), - TechnitiumNetworkDnsMenuItem, - CloudflareNetworkDnsMenuItem, - GoogleNetworkDnsMenuItem, - Quad9NetworkDnsMenuItem - }); + ManageNetworkDnsMenuItem = new ToolStripMenuItem("Manage"); + ManageNetworkDnsMenuItem.Click += ManageNetworkDnsMenuItem_Click; // // ServiceMenuItem @@ -213,7 +194,76 @@ namespace DnsServerSystemTrayApp TrayIconContextMenu.ResumeLayout(false); } - private static void SetNameServerIPv4(NetworkInterface nic, IPAddress[] dnsAddresses) + private void LoadConfig() + { + try + { + using (FileStream fS = new FileStream(_configFile, FileMode.Open, FileAccess.Read)) + { + BinaryReader bR = new BinaryReader(fS); + + if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DT") + throw new InvalidDataException("Invalid DNS Server System Tray App config file format."); + + switch (bR.ReadByte()) + { + case 1: + int count = bR.ReadInt32(); + _dnsProviders.Clear(); + + for (int i = 0; i < count; i++) + _dnsProviders.Add(new DnsProvider(bR)); + + _dnsProviders.Sort(); + break; + + default: + throw new NotSupportedException("DNS Server System Tray App config file format is not supported."); + } + } + } + catch (FileNotFoundException) + { + _dnsProviders.Clear(); + _dnsProviders.AddRange(DnsProvider.GetDefaultProviders()); + SaveConfig(); + } + catch (Exception ex) + { + MessageBox.Show("Error occured while loading config file. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void SaveConfig() + { + try + { + using (FileStream fS = new FileStream(_configFile, FileMode.Create, FileAccess.Write)) + { + BinaryWriter bW = new BinaryWriter(fS); + + bW.Write(Encoding.ASCII.GetBytes("DT")); + bW.Write((byte)1); + + bW.Write(_dnsProviders.Count); + + foreach (DnsProvider dnsProvider in _dnsProviders) + dnsProvider.WriteTo(bW); + } + } + catch (Exception ex) + { + MessageBox.Show("Error occured while saving config file. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private static void SetNameServer(NetworkInterface nic, ICollection dnsAddresses) + { + SetNameServerIPv4(nic, dnsAddresses); + SetNameServerIPv6(nic, dnsAddresses); + } + + private static void SetNameServerIPv4(NetworkInterface nic, ICollection dnsAddresses) { ManagementClass networkAdapterConfig = new ManagementClass("Win32_NetworkAdapterConfiguration"); ManagementObjectCollection instances = networkAdapterConfig.GetInstances(); @@ -222,17 +272,18 @@ namespace DnsServerSystemTrayApp { if ((bool)obj["IPEnabled"] && obj["SettingID"].Equals(nic.Id)) { - string[] dnsServers = new string[dnsAddresses.Length]; + List dnsServers = new List(); - for (int i = 0; i < dnsServers.Length; i++) + foreach (IPAddress dnsAddress in dnsAddresses) { - if (dnsAddresses[i].AddressFamily != AddressFamily.InterNetwork) - throw new ArgumentException(); + if (dnsAddress.AddressFamily != AddressFamily.InterNetwork) + continue; - dnsServers[i] = dnsAddresses[i].ToString(); + dnsServers.Add(dnsAddress.ToString()); } + ManagementBaseObject objParameter = obj.GetMethodParameters("SetDNSServerSearchOrder"); - objParameter["DNSServerSearchOrder"] = dnsServers; + objParameter["DNSServerSearchOrder"] = dnsServers.ToArray(); ManagementBaseObject response = obj.InvokeMethod("SetDNSServerSearchOrder", objParameter, null); uint returnValue = (uint)response.GetPropertyValue("ReturnValue"); @@ -267,7 +318,7 @@ namespace DnsServerSystemTrayApp } } - private static void SetNameServerIPv6(NetworkInterface nic, IPAddress[] dnsAddresses) + private static void SetNameServerIPv6(NetworkInterface nic, ICollection dnsAddresses) { //HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\{} @@ -276,7 +327,7 @@ namespace DnsServerSystemTrayApp foreach (IPAddress dnsAddress in dnsAddresses) { if (dnsAddress.AddressFamily != AddressFamily.InterNetworkV6) - throw new ArgumentException(); + continue; if (nameServer == null) nameServer = dnsAddress.ToString(); @@ -294,12 +345,15 @@ namespace DnsServerSystemTrayApp } } - private static bool AddressExists(IPAddress checkAddress, IPAddress[] addresses) + private static bool AddressExists(ICollection checkAddresses, ICollection addresses) { - foreach (IPAddress address in addresses) + foreach (IPAddress checkAddress in checkAddresses) { - if (checkAddress.Equals(address)) - return true; + foreach (IPAddress address in addresses) + { + if (checkAddress.Equals(address)) + return true; + } } return false; @@ -311,11 +365,7 @@ namespace DnsServerSystemTrayApp { #region Network DNS - bool isDefaultDns = false; - bool isTechnitiumDns = false; - bool isCloudflareDns = false; - bool isGoogleDns = false; - bool isQuad9Dns = false; + List networkDnsAddresses = new List(); try { @@ -324,42 +374,51 @@ namespace DnsServerSystemTrayApp if (nic.OperationalStatus != OperationalStatus.Up) continue; - foreach (IPAddress dnsAddress in nic.GetIPProperties().DnsAddresses) - { - if (IPAddress.IsLoopback(dnsAddress)) - { - isTechnitiumDns = true; - } - else if (AddressExists(dnsAddress, _cloudflareDns)) - { - isCloudflareDns = true; - } - else if (AddressExists(dnsAddress, _googleDns)) - { - isGoogleDns = true; - } - else if (AddressExists(dnsAddress, _quad9Dns)) - { - isQuad9Dns = true; - } - else if (!dnsAddress.IsIPv6SiteLocal) - { - isDefaultDns = true; - } - - if (isDefaultDns && isTechnitiumDns && isCloudflareDns && isGoogleDns && isQuad9Dns) - break; - } + networkDnsAddresses.AddRange(nic.GetIPProperties().DnsAddresses); } } catch { } - DefaultNetworkDnsMenuItem.Checked = isDefaultDns; - TechnitiumNetworkDnsMenuItem.Checked = isTechnitiumDns; - CloudflareNetworkDnsMenuItem.Checked = isCloudflareDns; - GoogleNetworkDnsMenuItem.Checked = isGoogleDns; - Quad9NetworkDnsMenuItem.Checked = isQuad9Dns; + + NetworkDnsMenuItem.DropDownItems.Clear(); + NetworkDnsMenuItem.DropDownItems.Add(DefaultNetworkDnsMenuItem); + NetworkDnsMenuItem.DropDownItems.Add(new ToolStripSeparator()); + + bool noItemChecked = true; + DefaultNetworkDnsMenuItem.Checked = false; + + foreach (DnsProvider dnsProvider in _dnsProviders) + { + ToolStripMenuItem item = new ToolStripMenuItem(dnsProvider.Name); + item.Tag = dnsProvider; + item.Click += NetworkDnsMenuSubItem_Click; + + if (AddressExists(networkDnsAddresses, dnsProvider.Addresses)) + { + item.Checked = true; + noItemChecked = false; + } + + NetworkDnsMenuItem.DropDownItems.Add(item); + } + + if (noItemChecked) + { + foreach (IPAddress dnsAddress in networkDnsAddresses) + { + if (!dnsAddress.IsIPv6SiteLocal) + { + DefaultNetworkDnsMenuItem.Checked = true; + break; + } + } + } + + if (_dnsProviders.Count > 0) + NetworkDnsMenuItem.DropDownItems.Add(new ToolStripSeparator()); + + NetworkDnsMenuItem.DropDownItems.Add(ManageNetworkDnsMenuItem); #endregion @@ -370,7 +429,6 @@ namespace DnsServerSystemTrayApp switch (_service.Status) { case ServiceControllerStatus.Stopped: - TechnitiumNetworkDnsMenuItem.Enabled = false; DashboardMenuItem.Enabled = false; StartServiceMenuItem.Enabled = true; RestartServiceMenuItem.Enabled = false; @@ -378,7 +436,6 @@ namespace DnsServerSystemTrayApp break; case ServiceControllerStatus.Running: - TechnitiumNetworkDnsMenuItem.Enabled = true; DashboardMenuItem.Enabled = true; StartServiceMenuItem.Enabled = false; RestartServiceMenuItem.Enabled = true; @@ -386,7 +443,6 @@ namespace DnsServerSystemTrayApp break; default: - TechnitiumNetworkDnsMenuItem.Enabled = false; DashboardMenuItem.Enabled = false; StartServiceMenuItem.Enabled = false; RestartServiceMenuItem.Enabled = false; @@ -398,7 +454,6 @@ namespace DnsServerSystemTrayApp } catch { - TechnitiumNetworkDnsMenuItem.Enabled = false; DashboardMenuItem.Enabled = false; ServiceMenuItem.Enabled = false; } @@ -471,20 +526,25 @@ namespace DnsServerSystemTrayApp SetNameServerIPv6(nic, new IPAddress[] { }); - IPInterfaceProperties properties = nic.GetIPProperties(); + try + { + IPInterfaceProperties properties = nic.GetIPProperties(); - if (properties.GetIPv4Properties().IsDhcpEnabled) - { - SetNameServerIPv4(nic, new IPAddress[] { }); - } - else if (properties.GatewayAddresses.Count > 0) - { - SetNameServerIPv4(nic, new IPAddress[] { properties.GatewayAddresses[0].Address }); - } - else - { - SetNameServerIPv4(nic, new IPAddress[] { }); + if (properties.GetIPv4Properties().IsDhcpEnabled) + { + SetNameServerIPv4(nic, new IPAddress[] { }); + } + else if (properties.GatewayAddresses.Count > 0) + { + SetNameServerIPv4(nic, new IPAddress[] { properties.GatewayAddresses[0].Address }); + } + else + { + SetNameServerIPv4(nic, new IPAddress[] { }); + } } + catch (NetworkInformationException) + { } } MessageBox.Show("The network DNS servers were set to default successfully.", "Default DNS Set - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); @@ -495,34 +555,26 @@ namespace DnsServerSystemTrayApp } } - private void TechnitiumNetworkDnsMenuItem_Click(object sender, EventArgs e) + private void ManageNetworkDnsMenuItem_Click(object sender, EventArgs e) { - try + using (frmManageDnsProviders frm = new frmManageDnsProviders(_dnsProviders)) { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + if (frm.ShowDialog() == DialogResult.OK) { - if (nic.OperationalStatus != OperationalStatus.Up) - continue; + _dnsProviders.Clear(); + _dnsProviders.AddRange(frm.DnsProviders); + _dnsProviders.Sort(); - IPInterfaceProperties properties = nic.GetIPProperties(); - - if ((properties.DnsAddresses.Count > 0) && !properties.DnsAddresses[0].IsIPv6SiteLocal) - { - SetNameServerIPv6(nic, new IPAddress[] { IPAddress.IPv6Loopback }); - SetNameServerIPv4(nic, new IPAddress[] { IPAddress.Loopback }); - } + SaveConfig(); } - - MessageBox.Show("The network DNS servers were set to Technitium DNS successfully.", "Technitium DNS Set - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); - } - catch (Exception ex) - { - MessageBox.Show("Error occured while setting Technitium as network DNS server. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } - private void CloudflareNetworkDnsMenuItem_Click(object sender, EventArgs e) + private void NetworkDnsMenuSubItem_Click(object sender, EventArgs e) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + DnsProvider dnsProvider = item.Tag as DnsProvider; + try { foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) @@ -533,69 +585,14 @@ namespace DnsServerSystemTrayApp IPInterfaceProperties properties = nic.GetIPProperties(); if ((properties.DnsAddresses.Count > 0) && !properties.DnsAddresses[0].IsIPv6SiteLocal) - { - SetNameServerIPv6(nic, new IPAddress[] { }); - SetNameServerIPv4(nic, _cloudflareDns); - } + SetNameServer(nic, dnsProvider.Addresses); } - MessageBox.Show("The network DNS servers were set to Cloudflare DNS successfully.", "Cloudflare DNS Set - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); + MessageBox.Show("The network DNS servers were set to " + dnsProvider.Name + " successfully.", dnsProvider.Name + " Configured - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { - MessageBox.Show("Error occured while setting Cloudflare as network DNS server. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - private void GoogleNetworkDnsMenuItem_Click(object sender, EventArgs e) - { - try - { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) - { - if (nic.OperationalStatus != OperationalStatus.Up) - continue; - - IPInterfaceProperties properties = nic.GetIPProperties(); - - if ((properties.DnsAddresses.Count > 0) && !properties.DnsAddresses[0].IsIPv6SiteLocal) - { - SetNameServerIPv6(nic, new IPAddress[] { }); - SetNameServerIPv4(nic, _googleDns); - } - } - - MessageBox.Show("The network DNS servers were set to Google DNS successfully.", "Google DNS Set - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); - } - catch (Exception ex) - { - MessageBox.Show("Error occured while setting Google as network DNS server. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - private void Quad9NetworkDnsMenuItem_Click(object sender, EventArgs e) - { - try - { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) - { - if (nic.OperationalStatus != OperationalStatus.Up) - continue; - - IPInterfaceProperties properties = nic.GetIPProperties(); - - if ((properties.DnsAddresses.Count > 0) && !properties.DnsAddresses[0].IsIPv6SiteLocal) - { - SetNameServerIPv6(nic, new IPAddress[] { }); - SetNameServerIPv4(nic, _quad9Dns); - } - } - - MessageBox.Show("The network DNS servers were set to IBM Quad9 DNS successfully.", "IBM Quad9 DNS Set - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); - } - catch (Exception ex) - { - MessageBox.Show("Error occured while setting IBM Quad9 as network DNS server. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show("Error occured while setting " + dnsProvider.Name + " as network DNS server. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } @@ -660,9 +657,9 @@ namespace DnsServerSystemTrayApp private void AboutMenuItem_Click(object sender, EventArgs e) { - using (AboutForm aboutForm = new AboutForm()) + using (frmAbout frm = new frmAbout()) { - aboutForm.ShowDialog(); + frm.ShowDialog(); } } diff --git a/DnsServerSystemTrayApp/Program.cs b/DnsServerSystemTrayApp/Program.cs index f59caa25..fccceaba 100644 --- a/DnsServerSystemTrayApp/Program.cs +++ b/DnsServerSystemTrayApp/Program.cs @@ -20,6 +20,7 @@ along with this program. If not, see . using System; using System.ComponentModel; using System.Diagnostics; +using System.IO; using System.Reflection; using System.Security.Principal; using System.Threading; @@ -45,12 +46,13 @@ namespace DnsServerSystemTrayApp Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); + string appPath = Assembly.GetEntryAssembly().Location; + #region admin elevation bool isAdmin = (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole(WindowsBuiltInRole.Administrator); if (!isAdmin) { - string appPath = Assembly.GetEntryAssembly().Location; ProcessStartInfo processInfo = new ProcessStartInfo(appPath, string.Join(" ", args)); processInfo.UseShellExecute = true; @@ -86,7 +88,9 @@ namespace DnsServerSystemTrayApp #endregion - Application.Run(new MainApplicationContext()); + string configFile = Path.Combine(Path.GetDirectoryName(appPath), "SystemTrayApp.config"); + + Application.Run(new MainApplicationContext(configFile)); } #endregion diff --git a/DnsServerSystemTrayApp/frmManageDnsProviders.Designer.cs b/DnsServerSystemTrayApp/frmManageDnsProviders.Designer.cs new file mode 100644 index 00000000..7e877cf5 --- /dev/null +++ b/DnsServerSystemTrayApp/frmManageDnsProviders.Designer.cs @@ -0,0 +1,270 @@ +namespace DnsServerSystemTrayApp +{ + partial class frmManageDnsProviders + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmManageDnsProviders)); + this.listView1 = new System.Windows.Forms.ListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.btnDelete = new System.Windows.Forms.Button(); + this.btnClear = new System.Windows.Forms.Button(); + this.btnAddUpdate = new System.Windows.Forms.Button(); + this.txtIpv6Addresses = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtIpv4Addresses = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtDnsProviderName = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnRestoreDefaults = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // listView1 + // + this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2, + this.columnHeader3}); + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(12, 12); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(660, 200); + this.listView1.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // columnHeader1 + // + this.columnHeader1.Text = "DNS Provider"; + this.columnHeader1.Width = 150; + // + // columnHeader2 + // + this.columnHeader2.Text = "IPv4 Addresses"; + this.columnHeader2.Width = 240; + // + // columnHeader3 + // + this.columnHeader3.Text = "IPv6 Addresses"; + this.columnHeader3.Width = 240; + // + // groupBox1 + // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox1.Controls.Add(this.btnDelete); + this.groupBox1.Controls.Add(this.btnClear); + this.groupBox1.Controls.Add(this.btnAddUpdate); + this.groupBox1.Controls.Add(this.txtIpv6Addresses); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.txtIpv4Addresses); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.txtDnsProviderName); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Location = new System.Drawing.Point(12, 216); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(661, 130); + this.groupBox1.TabIndex = 9; + this.groupBox1.TabStop = false; + // + // btnDelete + // + this.btnDelete.Enabled = false; + this.btnDelete.Location = new System.Drawing.Point(179, 97); + this.btnDelete.Name = "btnDelete"; + this.btnDelete.Size = new System.Drawing.Size(75, 23); + this.btnDelete.TabIndex = 16; + this.btnDelete.Text = "&Delete"; + this.btnDelete.UseVisualStyleBackColor = true; + this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); + // + // btnClear + // + this.btnClear.Location = new System.Drawing.Point(260, 97); + this.btnClear.Name = "btnClear"; + this.btnClear.Size = new System.Drawing.Size(75, 23); + this.btnClear.TabIndex = 17; + this.btnClear.Text = "&Clear"; + this.btnClear.UseVisualStyleBackColor = true; + this.btnClear.Click += new System.EventHandler(this.btnClear_Click); + // + // btnAddUpdate + // + this.btnAddUpdate.Location = new System.Drawing.Point(98, 97); + this.btnAddUpdate.Name = "btnAddUpdate"; + this.btnAddUpdate.Size = new System.Drawing.Size(75, 23); + this.btnAddUpdate.TabIndex = 15; + this.btnAddUpdate.Text = "&Add"; + this.btnAddUpdate.UseVisualStyleBackColor = true; + this.btnAddUpdate.Click += new System.EventHandler(this.btnAddUpdate_Click); + // + // txtIpv6Addresses + // + this.txtIpv6Addresses.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtIpv6Addresses.Location = new System.Drawing.Point(98, 71); + this.txtIpv6Addresses.MaxLength = 255; + this.txtIpv6Addresses.Name = "txtIpv6Addresses"; + this.txtIpv6Addresses.Size = new System.Drawing.Size(552, 20); + this.txtIpv6Addresses.TabIndex = 14; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(11, 74); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(81, 13); + this.label3.TabIndex = 13; + this.label3.Text = "IPv6 Addresses"; + // + // txtIpv4Addresses + // + this.txtIpv4Addresses.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtIpv4Addresses.Location = new System.Drawing.Point(98, 45); + this.txtIpv4Addresses.MaxLength = 255; + this.txtIpv4Addresses.Name = "txtIpv4Addresses"; + this.txtIpv4Addresses.Size = new System.Drawing.Size(552, 20); + this.txtIpv4Addresses.TabIndex = 12; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(11, 48); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(81, 13); + this.label2.TabIndex = 11; + this.label2.Text = "IPv4 Addresses"; + // + // txtDnsProviderName + // + this.txtDnsProviderName.Location = new System.Drawing.Point(98, 19); + this.txtDnsProviderName.MaxLength = 255; + this.txtDnsProviderName.Name = "txtDnsProviderName"; + this.txtDnsProviderName.Size = new System.Drawing.Size(200, 20); + this.txtDnsProviderName.TabIndex = 10; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(20, 22); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(72, 13); + this.label1.TabIndex = 9; + this.label1.Text = "DNS Provider"; + // + // btnOK + // + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(517, 356); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(75, 23); + this.btnOK.TabIndex = 10; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(598, 356); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 11; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnRestoreDefaults + // + this.btnRestoreDefaults.Location = new System.Drawing.Point(12, 356); + this.btnRestoreDefaults.Name = "btnRestoreDefaults"; + this.btnRestoreDefaults.Size = new System.Drawing.Size(100, 23); + this.btnRestoreDefaults.TabIndex = 12; + this.btnRestoreDefaults.Text = "Restore &Defaults"; + this.btnRestoreDefaults.UseVisualStyleBackColor = true; + this.btnRestoreDefaults.Click += new System.EventHandler(this.btnRestoreDefaults_Click); + // + // frmManageDnsProviders + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(684, 386); + this.Controls.Add(this.btnRestoreDefaults); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.listView1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "frmManageDnsProviders"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Manage Network DNS Providers - Technitium DNS Server"; + this.Load += new System.EventHandler(this.frmManageDnsProviders_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView1; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.ColumnHeader columnHeader3; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button btnClear; + private System.Windows.Forms.Button btnAddUpdate; + private System.Windows.Forms.TextBox txtIpv6Addresses; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox txtIpv4Addresses; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txtDnsProviderName; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnRestoreDefaults; + private System.Windows.Forms.Button btnDelete; + } +} \ No newline at end of file diff --git a/DnsServerSystemTrayApp/frmManageDnsProviders.cs b/DnsServerSystemTrayApp/frmManageDnsProviders.cs new file mode 100644 index 00000000..f61eca06 --- /dev/null +++ b/DnsServerSystemTrayApp/frmManageDnsProviders.cs @@ -0,0 +1,196 @@ +/* +Technitium DNS Server +Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Windows.Forms; + +namespace DnsServerSystemTrayApp +{ + public partial class frmManageDnsProviders : Form + { + #region variables + + readonly List _dnsProviders = new List(); + + #endregion + + #region constructor + + public frmManageDnsProviders(ICollection dnsProviders) + { + InitializeComponent(); + + _dnsProviders.AddRange(dnsProviders); + } + + #endregion + + #region private + + private void RefreshDnsProvidersList() + { + listView1.SuspendLayout(); + + listView1.Items.Clear(); + + foreach (DnsProvider dnsProvider in _dnsProviders) + { + ListViewItem item = listView1.Items.Add(dnsProvider.Name); + item.SubItems.Add(dnsProvider.GetIpv4Addresses()); + item.SubItems.Add(dnsProvider.GetIpv6Addresses()); + + item.Tag = dnsProvider; + } + + listView1.ResumeLayout(); + } + + private void ClearForm() + { + txtDnsProviderName.Text = ""; + txtIpv4Addresses.Text = ""; + txtIpv6Addresses.Text = ""; + btnAddUpdate.Text = "Add"; + btnDelete.Enabled = false; + } + + private void frmManageDnsProviders_Load(object sender, EventArgs e) + { + RefreshDnsProvidersList(); + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + if (listView1.SelectedItems.Count > 0) + { + ListViewItem selectedItem = listView1.SelectedItems[0]; + + txtDnsProviderName.Text = selectedItem.Text; + txtIpv4Addresses.Text = selectedItem.SubItems[1].Text; + txtIpv6Addresses.Text = selectedItem.SubItems[2].Text; + btnAddUpdate.Text = "&Update"; + btnDelete.Enabled = true; + } + else + { + ClearForm(); + } + } + + private void btnAddUpdate_Click(object sender, EventArgs e) + { + if (string.IsNullOrWhiteSpace(txtDnsProviderName.Text)) + { + MessageBox.Show("Please enter a valid DNS Provider name.", "Missing DNS Provider!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + + List addresses = new List(); + + foreach (string item in txtIpv4Addresses.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (IPAddress.TryParse(item.Trim(), out IPAddress address) && (address.AddressFamily == AddressFamily.InterNetwork)) + { + addresses.Add(address); + } + else + { + MessageBox.Show("Please enter a valid IPv4 address.", "Invalid IPv4 Address!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + } + + foreach (string item in txtIpv6Addresses.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (IPAddress.TryParse(item.Trim(), out IPAddress address) && (address.AddressFamily == AddressFamily.InterNetworkV6)) + { + addresses.Add(address); + } + else + { + MessageBox.Show("Please enter a valid IPv6 address.", "Invalid IPv6 Address!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + } + + if (addresses.Count == 0) + { + MessageBox.Show("Please enter at least one valid DNS provider IP address.", "Missing DNS Provider IP Address!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + + if ((btnAddUpdate.Text != "Add") && (listView1.SelectedItems.Count > 0)) + { + ListViewItem selectedItem = listView1.SelectedItems[0]; + DnsProvider dnsProvider = selectedItem.Tag as DnsProvider; + + dnsProvider.Name = txtDnsProviderName.Text.Trim(); + dnsProvider.Addresses = addresses; + } + else + { + _dnsProviders.Add(new DnsProvider(txtDnsProviderName.Text.Trim(), addresses)); + } + + RefreshDnsProvidersList(); + ClearForm(); + } + + private void btnDelete_Click(object sender, EventArgs e) + { + if (listView1.SelectedItems.Count > 0) + { + ListViewItem selectedItem = listView1.SelectedItems[0]; + DnsProvider dnsProvider = selectedItem.Tag as DnsProvider; + + _dnsProviders.Remove(dnsProvider); + listView1.Items.Remove(selectedItem); + } + + RefreshDnsProvidersList(); + ClearForm(); + } + + private void btnClear_Click(object sender, EventArgs e) + { + ClearForm(); + } + + private void btnRestoreDefaults_Click(object sender, EventArgs e) + { + _dnsProviders.Clear(); + _dnsProviders.AddRange(DnsProvider.GetDefaultProviders()); + + RefreshDnsProvidersList(); + ClearForm(); + } + + #endregion + + #region properties + + public List DnsProviders + { get { return _dnsProviders; } } + + #endregion + } +} diff --git a/DnsServerSystemTrayApp/frmManageDnsProviders.resx b/DnsServerSystemTrayApp/frmManageDnsProviders.resx new file mode 100644 index 00000000..06462274 --- /dev/null +++ b/DnsServerSystemTrayApp/frmManageDnsProviders.resx @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + AAABAAUAICAQAAAAAADoAgAAVgAAACAgAAAAAAAAqAgAAD4DAAAwMAAAAAAAAKgOAADmCwAAEBAQAAAA + AAAoAQAAjhoAABAQAAAAAAAAaAUAALYbAAAoAAAAIAAAAEAAAAABAAQAAAAAAIACAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAMDAwACAgIAAAAD/AAD/AAAA//8A/wAAAP8A + /wD//wAA////AMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzP//zP//////////////zMz/ + /8z//////////////8zM///M///////////////MzP//zP//////////////zMz//8zMzMzMzMz//8zM + zMzM///MzMzMzMzM///MzMzMzP//////////zP//zP//zMz//////////8z//8z//8zM///////////M + ///M///MzP//////////zP//zP//zMz//8zMzMzMzMz//8z//8zM///MzMzMzMzM///M///MzP//zP// + zMzMzP//zP//zMz//8z//8zMzMz//8z//8zM///M///MzMzM///M///MzP//zP//zMzMzP//zP//zMz/ + /8z//8zMzMzMzMz//8zM///M///MzMzMzMzM///MzP//zP//zP//////////zMz//8z//8z///////// + /8zM///M///M///////////MzP//zP//zP//////////zMzMzMz//8zMzMzMzMz//8zMzMzM///MzMzM + zMzM///MzP//////////////zP//zMz//////////////8z//8zM///////////////M///MzP////// + ////////zP//zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAIAAAAEAA + AAABAAgAAAAAAIAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAMDA + wADA3MAA8MqmAAQEBAAICAgADAwMABEREQAWFhYAHBwcACIiIgApKSkAVVVVAE1NTQBCQkIAOTk5AIB8 + /wBQUP8AkwDWAP/szADG1u8A1ufnAJCprQAAADMAAABmAAAAmQAAAMwAADMAAAAzMwAAM2YAADOZAAAz + zAAAM/8AAGYAAABmMwAAZmYAAGaZAABmzAAAZv8AAJkAAACZMwAAmWYAAJmZAACZzAAAmf8AAMwAAADM + MwAAzGYAAMyZAADMzAAAzP8AAP9mAAD/mQAA/8wAMwAAADMAMwAzAGYAMwCZADMAzAAzAP8AMzMAADMz + MwAzM2YAMzOZADMzzAAzM/8AM2YAADNmMwAzZmYAM2aZADNmzAAzZv8AM5kAADOZMwAzmWYAM5mZADOZ + zAAzmf8AM8wAADPMMwAzzGYAM8yZADPMzAAzzP8AM/8zADP/ZgAz/5kAM//MADP//wBmAAAAZgAzAGYA + ZgBmAJkAZgDMAGYA/wBmMwAAZjMzAGYzZgBmM5kAZjPMAGYz/wBmZgAAZmYzAGZmZgBmZpkAZmbMAGaZ + AABmmTMAZplmAGaZmQBmmcwAZpn/AGbMAABmzDMAZsyZAGbMzABmzP8AZv8AAGb/MwBm/5kAZv/MAMwA + /wD/AMwAmZkAAJkzmQCZAJkAmQDMAJkAAACZMzMAmQBmAJkzzACZAP8AmWYAAJlmMwCZM2YAmWaZAJlm + zACZM/8AmZkzAJmZZgCZmZkAmZnMAJmZ/wCZzAAAmcwzAGbMZgCZzJkAmczMAJnM/wCZ/wAAmf8zAJnM + ZgCZ/5kAmf/MAJn//wDMAAAAmQAzAMwAZgDMAJkAzADMAJkzAADMMzMAzDNmAMwzmQDMM8wAzDP/AMxm + AADMZjMAmWZmAMxmmQDMZswAmWb/AMyZAADMmTMAzJlmAMyZmQDMmcwAzJn/AMzMAADMzDMAzMxmAMzM + mQDMzMwAzMz/AMz/AADM/zMAmf9mAMz/mQDM/8wAzP//AMwAMwD/AGYA/wCZAMwzAAD/MzMA/zNmAP8z + mQD/M8wA/zP/AP9mAAD/ZjMAzGZmAP9mmQD/ZswAzGb/AP+ZAAD/mTMA/5lmAP+ZmQD/mcwA/5n/AP/M + AAD/zDMA/8xmAP/MmQD/zMwA/8z/AP//MwDM/2YA//+ZAP//zABmZv8AZv9mAGb//wD/ZmYA/2b/AP// + ZgAhAKUAX19fAHd3dwCGhoYAlpaWAMvLywCysrIA19fXAN3d3QDj4+MA6urqAPHx8QD4+PgA8Pv/AKSg + oACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////ANXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1f/////V1f////////////////// + ///////////V1dXV/////9XV/////////////////////////////9XV1dX/////1dX///////////// + ////////////////1dXV1f/////V1f/////////////////////////////V1dXV/////9XV1dXV1dXV + 1dXV1dXV/////9XV1dXV1dXV1dX/////1dXV1dXV1dXV1dXV1dX/////1dXV1dXV1dXV1f////////// + ///////////V1f/////V1f/////V1dXV/////////////////////9XV/////9XV/////9XV1dX///// + ////////////////1dX/////1dX/////1dXV1f/////////////////////V1f/////V1f/////V1dXV + /////9XV1dXV1dXV1dXV1dXV/////9XV/////9XV1dX/////1dXV1dXV1dXV1dXV1dX/////1dX///// + 1dXV1f/////V1f/////V1dXV1dXV1f/////V1f/////V1dXV/////9XV/////9XV1dXV1dXV/////9XV + /////9XV1dX/////1dX/////1dXV1dXV1dX/////1dX/////1dXV1f/////V1f/////V1dXV1dXV1f// + ///V1f/////V1dXV/////9XV/////9XV1dXV1dXV1dXV1dXV/////9XV1dX/////1dX/////1dXV1dXV + 1dXV1dXV1dX/////1dXV1f/////V1f/////V1f/////////////////////V1dXV/////9XV/////9XV + /////////////////////9XV1dX/////1dX/////1dX/////////////////////1dXV1f/////V1f// + ///V1f/////////////////////V1dXV1dXV1dXV/////9XV1dXV1dXV1dXV1dXV/////9XV1dXV1dXV + 1dX/////1dXV1dXV1dXV1dXV1dX/////1dXV1f/////////////////////////////V1f/////V1dXV + /////////////////////////////9XV/////9XV1dX/////////////////////////////1dX///// + 1dXV1f/////////////////////////////V1f/////V1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAwAAAAYAAAAAEA + CAAAAAAAgAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAwMDAAMDc + wADwyqYABAQEAAgICAAMDAwAERERABYWFgAcHBwAIiIiACkpKQBVVVUATU1NAEJCQgA5OTkAgHz/AFBQ + /wCTANYA/+zMAMbW7wDW5+cAkKmtAAAAMwAAAGYAAACZAAAAzAAAMwAAADMzAAAzZgAAM5kAADPMAAAz + /wAAZgAAAGYzAABmZgAAZpkAAGbMAABm/wAAmQAAAJkzAACZZgAAmZkAAJnMAACZ/wAAzAAAAMwzAADM + ZgAAzJkAAMzMAADM/wAA/2YAAP+ZAAD/zAAzAAAAMwAzADMAZgAzAJkAMwDMADMA/wAzMwAAMzMzADMz + ZgAzM5kAMzPMADMz/wAzZgAAM2YzADNmZgAzZpkAM2bMADNm/wAzmQAAM5kzADOZZgAzmZkAM5nMADOZ + /wAzzAAAM8wzADPMZgAzzJkAM8zMADPM/wAz/zMAM/9mADP/mQAz/8wAM///AGYAAABmADMAZgBmAGYA + mQBmAMwAZgD/AGYzAABmMzMAZjNmAGYzmQBmM8wAZjP/AGZmAABmZjMAZmZmAGZmmQBmZswAZpkAAGaZ + MwBmmWYAZpmZAGaZzABmmf8AZswAAGbMMwBmzJkAZszMAGbM/wBm/wAAZv8zAGb/mQBm/8wAzAD/AP8A + zACZmQAAmTOZAJkAmQCZAMwAmQAAAJkzMwCZAGYAmTPMAJkA/wCZZgAAmWYzAJkzZgCZZpkAmWbMAJkz + /wCZmTMAmZlmAJmZmQCZmcwAmZn/AJnMAACZzDMAZsxmAJnMmQCZzMwAmcz/AJn/AACZ/zMAmcxmAJn/ + mQCZ/8wAmf//AMwAAACZADMAzABmAMwAmQDMAMwAmTMAAMwzMwDMM2YAzDOZAMwzzADMM/8AzGYAAMxm + MwCZZmYAzGaZAMxmzACZZv8AzJkAAMyZMwDMmWYAzJmZAMyZzADMmf8AzMwAAMzMMwDMzGYAzMyZAMzM + zADMzP8AzP8AAMz/MwCZ/2YAzP+ZAMz/zADM//8AzAAzAP8AZgD/AJkAzDMAAP8zMwD/M2YA/zOZAP8z + zAD/M/8A/2YAAP9mMwDMZmYA/2aZAP9mzADMZv8A/5kAAP+ZMwD/mWYA/5mZAP+ZzAD/mf8A/8wAAP/M + MwD/zGYA/8yZAP/MzAD/zP8A//8zAMz/ZgD//5kA///MAGZm/wBm/2YAZv//AP9mZgD/Zv8A//9mACEA + pQBfX18Ad3d3AIaGhgCWlpYAy8vLALKysgDX19cA3d3dAOPj4wDq6uoA8fHxAPj4+ADw+/8ApKCgAICA + gAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8A1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV//// + ////1dXV////////////////////////////////////////////1dXV1dXV////////1dXV//////// + ////////////////////////////////////1dXV1dXV////////1dXV//////////////////////// + ////////////////////1dXV1dXV////////1dXV//////////////////////////////////////// + ////1dXV1dXV////////1dXV////////////////////////////////////////////1dXV1dXV//// + ////1dXV////////////////////////////////////////////1dXV1dXV////////1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV////////1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV + ////////1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV + 1dXV1dXV1dXV////////////////////////////////1dXV////////1dXV////////1dXV1dXV//// + ////////////////////////////1dXV////////1dXV////////1dXV1dXV//////////////////// + ////////////1dXV////////1dXV////////1dXV1dXV////////////////////////////////1dXV + ////////1dXV////////1dXV1dXV////////////////////////////////1dXV////////1dXV//// + ////1dXV1dXV////////////////////////////////1dXV////////1dXV////////1dXV1dXV//// + ////1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////1dXV////////1dXV1dXV////////1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV////////1dXV////////1dXV1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV + ////////1dXV////////1dXV1dXV////////1dXV////////1dXV1dXV1dXV1dXV////////1dXV//// + ////1dXV1dXV////////1dXV////////1dXV1dXV1dXV1dXV////////1dXV////////1dXV1dXV//// + ////1dXV////////1dXV1dXV1dXV1dXV////////1dXV////////1dXV1dXV////////1dXV//////// + 1dXV1dXV1dXV1dXV////////1dXV////////1dXV1dXV////////1dXV////////1dXV1dXV1dXV1dXV + ////////1dXV////////1dXV1dXV////////1dXV////////1dXV1dXV1dXV1dXV////////1dXV//// + ////1dXV1dXV////////1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV//// + ////1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV////////1dXV//////// + 1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV////////1dXV////////1dXV//////////// + ////////////////////1dXV1dXV////////1dXV////////1dXV//////////////////////////// + ////1dXV1dXV////////1dXV////////1dXV////////////////////////////////1dXV1dXV//// + ////1dXV////////1dXV////////////////////////////////1dXV1dXV////////1dXV//////// + 1dXV////////////////////////////////1dXV1dXV////////1dXV////////1dXV//////////// + ////////////////////1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV//// + ////1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV1dXV + 1dXV1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////1dXV1dXV//////////////////// + ////////////////////////1dXV////////1dXV1dXV//////////////////////////////////// + ////////1dXV////////1dXV1dXV////////////////////////////////////////////1dXV//// + ////1dXV1dXV////////////////////////////////////////////1dXV////////1dXV1dXV//// + ////////////////////////////////////////1dXV////////1dXV1dXV//////////////////// + ////////////////////////1dXV////////1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV + 1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKAAAABAAAAAgAAAAAQAEAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAADAwMAAgICAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wDMzMzMzMzMzM/8///////8z/z///////zP/MzMzP/MzM/////8/8/8z/////z/z/zP/MzMzP/P/M/8 + /8zM/8/8z/z/zMz/z/zP/P/MzMzP/M/8/8/////8z/z/z/////zMzP/MzMzP/M///////8/8z/////// + z/zMzMzMzMzMzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAoAAAAEAAAACAAAAABAAgAAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + gAAAgAAAAICAAIAAAACAAIAAgIAAAMDAwADA3MAA8MqmAAQEBAAICAgADAwMABEREQAWFhYAHBwcACIi + IgApKSkAVVVVAE1NTQBCQkIAOTk5AIB8/wBQUP8AkwDWAP/szADG1u8A1ufnAJCprQAAADMAAABmAAAA + mQAAAMwAADMAAAAzMwAAM2YAADOZAAAzzAAAM/8AAGYAAABmMwAAZmYAAGaZAABmzAAAZv8AAJkAAACZ + MwAAmWYAAJmZAACZzAAAmf8AAMwAAADMMwAAzGYAAMyZAADMzAAAzP8AAP9mAAD/mQAA/8wAMwAAADMA + MwAzAGYAMwCZADMAzAAzAP8AMzMAADMzMwAzM2YAMzOZADMzzAAzM/8AM2YAADNmMwAzZmYAM2aZADNm + zAAzZv8AM5kAADOZMwAzmWYAM5mZADOZzAAzmf8AM8wAADPMMwAzzGYAM8yZADPMzAAzzP8AM/8zADP/ + ZgAz/5kAM//MADP//wBmAAAAZgAzAGYAZgBmAJkAZgDMAGYA/wBmMwAAZjMzAGYzZgBmM5kAZjPMAGYz + /wBmZgAAZmYzAGZmZgBmZpkAZmbMAGaZAABmmTMAZplmAGaZmQBmmcwAZpn/AGbMAABmzDMAZsyZAGbM + zABmzP8AZv8AAGb/MwBm/5kAZv/MAMwA/wD/AMwAmZkAAJkzmQCZAJkAmQDMAJkAAACZMzMAmQBmAJkz + zACZAP8AmWYAAJlmMwCZM2YAmWaZAJlmzACZM/8AmZkzAJmZZgCZmZkAmZnMAJmZ/wCZzAAAmcwzAGbM + ZgCZzJkAmczMAJnM/wCZ/wAAmf8zAJnMZgCZ/5kAmf/MAJn//wDMAAAAmQAzAMwAZgDMAJkAzADMAJkz + AADMMzMAzDNmAMwzmQDMM8wAzDP/AMxmAADMZjMAmWZmAMxmmQDMZswAmWb/AMyZAADMmTMAzJlmAMyZ + mQDMmcwAzJn/AMzMAADMzDMAzMxmAMzMmQDMzMwAzMz/AMz/AADM/zMAmf9mAMz/mQDM/8wAzP//AMwA + MwD/AGYA/wCZAMwzAAD/MzMA/zNmAP8zmQD/M8wA/zP/AP9mAAD/ZjMAzGZmAP9mmQD/ZswAzGb/AP+Z + AAD/mTMA/5lmAP+ZmQD/mcwA/5n/AP/MAAD/zDMA/8xmAP/MmQD/zMwA/8z/AP//MwDM/2YA//+ZAP// + zABmZv8AZv9mAGb//wD/ZmYA/2b/AP//ZgAhAKUAX19fAHd3dwCGhoYAlpaWAMvLywCysrIA19fXAN3d + 3QDj4+MA6urqAPHx8QD4+PgA8Pv/AKSgoACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////ANXV + 1dXV1dXV1dXV1dXV1dXV///V///////////////V1f//1f//////////////1dX//9XV1dXV1dX//9XV + 1dXV///////////V///V///V1f//////////1f//1f//1dX//9XV1dXV1dX//9X//9XV///V///V1dXV + ///V///V1f//1f//1dXV1f//1f//1dX//9X//9XV1dXV1dX//9XV///V///V///////////V1f//1f// + 1f//////////1dXV1dX//9XV1dXV1dX//9XV///////////////V///V1f//////////////1f//1dXV + 1dXV1dXV1dXV1dXV1dUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA + + + \ No newline at end of file