/* Technitium DNS Server Copyright (C) 2021 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 DnsServerSystemTrayApp.Properties; using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Management; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.ServiceProcess; using System.Text; using System.Windows.Forms; using TechnitiumLibrary.IO; namespace DnsServerSystemTrayApp { public class MainApplicationContext : ApplicationContext { #region variables const int SERVICE_WAIT_TIMEOUT_SECONDS = 30; private readonly ServiceController _service = new ServiceController("DnsService"); 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 ManageNetworkDnsMenuItem; private ToolStripMenuItem ServiceMenuItem; private ToolStripMenuItem StartServiceMenuItem; private ToolStripMenuItem RestartServiceMenuItem; private ToolStripMenuItem StopServiceMenuItem; private ToolStripMenuItem AboutMenuItem; private ToolStripMenuItem AutoStartMenuItem; private ToolStripMenuItem ExitMenuItem; #endregion #region constructor public MainApplicationContext(string configFile, string[] args) { _configFile = configFile; LoadConfig(); InitializeComponent(); if (args.Length > 0) { switch (args[0]) { case "--network-dns-default": DefaultNetworkDnsMenuItem_Click(this, EventArgs.Empty); break; case "--network-dns-item": foreach (DnsProvider dnsProvider in _dnsProviders) { if (dnsProvider.Name.Equals(args[1])) { NetworkDnsMenuSubItem_Click(new ToolStripMenuItem(dnsProvider.Name) { Tag = dnsProvider }, EventArgs.Empty); break; } } break; case "--network-dns-manage": ManageNetworkDnsMenuItem_Click(this, EventArgs.Empty); break; case "--service-start": StartServiceMenuItem_Click(this, EventArgs.Empty); break; case "--service-restart": RestartServiceMenuItem_Click(this, EventArgs.Empty); break; case "--service-stop": StopServiceMenuItem_Click(this, EventArgs.Empty); break; } } } #endregion #region IDisposable protected override void Dispose(bool disposing) { if (disposing) { TrayIcon?.Dispose(); } base.Dispose(disposing); } #endregion #region private private void InitializeComponent() { // // TrayIconContextMenu // TrayIconContextMenu = new ContextMenuStrip(); TrayIconContextMenu.SuspendLayout(); // // TrayIcon // TrayIcon = new NotifyIcon(); TrayIcon.Icon = Resources.logo2; TrayIcon.Visible = true; TrayIcon.MouseUp += TrayIcon_MouseUp; TrayIcon.ContextMenuStrip = TrayIconContextMenu; TrayIcon.Text = Resources.ServiceName; // // DashboardMenuItem // DashboardMenuItem = new ToolStripMenuItem(); DashboardMenuItem.Name = "DashboardMenuItem"; DashboardMenuItem.Text = Resources.DashboardMenuItem; DashboardMenuItem.Click += DashboardMenuItem_Click; // // NetworkDnsMenuItem // NetworkDnsMenuItem = new ToolStripMenuItem(); NetworkDnsMenuItem.Name = "NetworkDnsMenuItem"; NetworkDnsMenuItem.Text = Resources.NetworkDnsMenuItem; DefaultNetworkDnsMenuItem = new ToolStripMenuItem("Default"); DefaultNetworkDnsMenuItem.Click += DefaultNetworkDnsMenuItem_Click; ManageNetworkDnsMenuItem = new ToolStripMenuItem("Manage"); ManageNetworkDnsMenuItem.Click += ManageNetworkDnsMenuItem_Click; // // ServiceMenuItem // ServiceMenuItem = new ToolStripMenuItem(); ServiceMenuItem.Name = "ServiceMenuItem"; ServiceMenuItem.Text = Resources.ServiceMenuItem; StartServiceMenuItem = new ToolStripMenuItem(Resources.ServiceStartMenuItem); StartServiceMenuItem.Click += StartServiceMenuItem_Click; RestartServiceMenuItem = new ToolStripMenuItem(Resources.ServiceRestartMenuItem); RestartServiceMenuItem.Click += RestartServiceMenuItem_Click; StopServiceMenuItem = new ToolStripMenuItem(Resources.ServiceStopMenuItem); StopServiceMenuItem.Click += StopServiceMenuItem_Click; ServiceMenuItem.DropDownItems.AddRange(new ToolStripItem[] { StartServiceMenuItem, RestartServiceMenuItem, StopServiceMenuItem }); // // AboutMenuItem // AboutMenuItem = new ToolStripMenuItem(); AboutMenuItem.Name = "AboutMenuItem"; AboutMenuItem.Text = Resources.AboutMenuItem; AboutMenuItem.Click += AboutMenuItem_Click; // // AutoStartMenuItem // AutoStartMenuItem = new ToolStripMenuItem(); AutoStartMenuItem.Name = "AutoStartMenuItem"; AutoStartMenuItem.Text = "&Auto Start Icon"; AutoStartMenuItem.Click += AutoStartMenuItem_Click; // // ExitMenuItem // ExitMenuItem = new ToolStripMenuItem(); ExitMenuItem.Name = "ExitMenuItem"; ExitMenuItem.Text = Resources.ExitMenuItem; ExitMenuItem.Click += ExitMenuItem_Click; TrayIconContextMenu.Items.AddRange(new ToolStripItem[] { DashboardMenuItem, new ToolStripSeparator(), NetworkDnsMenuItem, ServiceMenuItem, AboutMenuItem, new ToolStripSeparator(), AutoStartMenuItem, ExitMenuItem }); TrayIconContextMenu.ResumeLayout(false); } 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()); _dnsProviders.Sort(); } 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(); foreach (ManagementObject obj in instances) { if ((bool)obj["IPEnabled"] && obj["SettingID"].Equals(nic.Id)) { List dnsServers = new List(); foreach (IPAddress dnsAddress in dnsAddresses) { if (dnsAddress.AddressFamily != AddressFamily.InterNetwork) continue; dnsServers.Add(dnsAddress.ToString()); } ManagementBaseObject objParameter = obj.GetMethodParameters("SetDNSServerSearchOrder"); objParameter["DNSServerSearchOrder"] = dnsServers.ToArray(); ManagementBaseObject response = obj.InvokeMethod("SetDNSServerSearchOrder", objParameter, null); uint returnValue = (uint)response.GetPropertyValue("ReturnValue"); switch (returnValue) { case 0: //success case 1: //reboot required break; case 64: throw new Exception("Method not supported on this platform. WMI error code: " + returnValue); case 65: throw new Exception("Unknown failure. WMI error code: " + returnValue); case 70: throw new Exception("Invalid IP address. WMI error code: " + returnValue); case 96: throw new Exception("Unable to notify DNS service. WMI error code: " + returnValue); case 97: throw new Exception("Interface not configurable. WMI error code: " + returnValue); default: throw new Exception("WMI error code: " + returnValue); } break; } } } private static void SetNameServerIPv6(NetworkInterface nic, ICollection dnsAddresses) { //HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\{} string nameServer = null; foreach (IPAddress dnsAddress in dnsAddresses) { if (dnsAddress.AddressFamily != AddressFamily.InterNetworkV6) continue; if (nameServer == null) nameServer = dnsAddress.ToString(); else nameServer += "," + dnsAddress.ToString(); } if (nameServer == null) nameServer = ""; using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\" + nic.Id, true)) { if (key != null) key.SetValue("NameServer", nameServer, RegistryValueKind.String); } } private static bool AddressExists(ICollection checkAddresses, ICollection addresses) { foreach (IPAddress checkAddress in checkAddresses) { foreach (IPAddress address in addresses) { if (checkAddress.Equals(address)) return true; } } return false; } private void TrayIcon_MouseUp(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { #region Network DNS List networkDnsAddresses = new List(); try { foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { if (nic.OperationalStatus != OperationalStatus.Up) continue; networkDnsAddresses.AddRange(nic.GetIPProperties().DnsAddresses); } } catch { } 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 #region service try { _service.Refresh(); switch (_service.Status) { case ServiceControllerStatus.Stopped: DashboardMenuItem.Enabled = false; StartServiceMenuItem.Enabled = true; RestartServiceMenuItem.Enabled = false; StopServiceMenuItem.Enabled = false; break; case ServiceControllerStatus.Running: DashboardMenuItem.Enabled = true; StartServiceMenuItem.Enabled = false; RestartServiceMenuItem.Enabled = true; StopServiceMenuItem.Enabled = true; break; default: DashboardMenuItem.Enabled = false; StartServiceMenuItem.Enabled = false; RestartServiceMenuItem.Enabled = false; StopServiceMenuItem.Enabled = false; break; } ServiceMenuItem.Enabled = true; } catch { DashboardMenuItem.Enabled = false; ServiceMenuItem.Enabled = false; } #endregion #region auto start try { using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true)) { if (key != null) { string autoStartPath = key.GetValue("Technitium DNS System Tray") as string; AutoStartMenuItem.Checked = (autoStartPath != null) && autoStartPath.Equals("\"" + Program.APP_PATH + "\""); } } } catch { } #endregion TrayIcon.ShowContextMenu(); } } private void DashboardMenuItem_Click(object sender, EventArgs e) { int port = 5380; try { //try finding port number from dns config file string dnsConfigFile = Path.Combine(Path.GetDirectoryName(Program.APP_PATH), "config", "dns.config"); using (FileStream fS = new FileStream(dnsConfigFile, FileMode.Open, FileAccess.Read)) { BinaryReader bR = new BinaryReader(fS); if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DS") //format throw new InvalidDataException("DnsServer config file format is invalid."); int version = bR.ReadByte(); if (version > 1) { string serverDomain = bR.ReadShortString(); port = bR.ReadInt32(); } } } catch { } ProcessStartInfo processInfo = new ProcessStartInfo("http://localhost:" + port.ToString()); processInfo.UseShellExecute = true; processInfo.Verb = "open"; Process.Start(processInfo); } private void DefaultNetworkDnsMenuItem_Click(object sender, EventArgs e) { if (!Program.IsAdmin) { Program.RunAsAdmin("--network-dns-default"); return; } try { foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { if (nic.OperationalStatus != OperationalStatus.Up) continue; SetNameServerIPv6(nic, Array.Empty()); try { IPInterfaceProperties properties = nic.GetIPProperties(); if (properties.GetIPv4Properties().IsDhcpEnabled) { SetNameServerIPv4(nic, Array.Empty()); } else if (properties.GatewayAddresses.Count > 0) { SetNameServerIPv4(nic, new IPAddress[] { properties.GatewayAddresses[0].Address }); } else { SetNameServerIPv4(nic, Array.Empty()); } } catch (NetworkInformationException) { } } MessageBox.Show("The network DNS servers were set to default successfully.", "Default DNS Set - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show("Error occured while setting default network DNS servers. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void ManageNetworkDnsMenuItem_Click(object sender, EventArgs e) { if (!Program.IsAdmin) { Program.RunAsAdmin("--network-dns-manage"); return; } using (frmManageDnsProviders frm = new frmManageDnsProviders(_dnsProviders)) { if (frm.ShowDialog() == DialogResult.OK) { _dnsProviders.Clear(); _dnsProviders.AddRange(frm.DnsProviders); _dnsProviders.Sort(); SaveConfig(); } } } private void NetworkDnsMenuSubItem_Click(object sender, EventArgs e) { ToolStripMenuItem item = sender as ToolStripMenuItem; DnsProvider dnsProvider = item.Tag as DnsProvider; if (!Program.IsAdmin) { Program.RunAsAdmin("--network-dns-item " + dnsProvider.Name); return; } 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) SetNameServer(nic, dnsProvider.Addresses); } 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 " + dnsProvider.Name + " as network DNS server. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void StartServiceMenuItem_Click(object sender, EventArgs e) { if (!Program.IsAdmin) { Program.RunAsAdmin("--service-start"); return; } try { _service.Start(); _service.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, SERVICE_WAIT_TIMEOUT_SECONDS)); MessageBox.Show("The service was started successfully.", "Service Started - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (System.ServiceProcess.TimeoutException ex) { MessageBox.Show("The service did not respond in time." + ex.Message, "Service Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } catch (Exception ex) { MessageBox.Show("Error occured while starting service. " + ex.Message, "Service Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void RestartServiceMenuItem_Click(object sender, EventArgs e) { if (!Program.IsAdmin) { Program.RunAsAdmin("--service-restart"); return; } try { _service.Stop(); _service.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, SERVICE_WAIT_TIMEOUT_SECONDS)); _service.Start(); _service.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, SERVICE_WAIT_TIMEOUT_SECONDS)); MessageBox.Show("The service was restarted successfully.", "Service Restarted - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (System.ServiceProcess.TimeoutException ex) { MessageBox.Show("The service did not respond in time." + ex.Message, "Service Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } catch (Exception ex) { MessageBox.Show("Error occured while restarting service. " + ex.Message, "Service Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void StopServiceMenuItem_Click(object sender, EventArgs e) { if (!Program.IsAdmin) { Program.RunAsAdmin("--service-stop"); return; } try { _service.Stop(); _service.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, SERVICE_WAIT_TIMEOUT_SECONDS)); MessageBox.Show("The service was stopped successfully.", "Service Stopped - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (System.ServiceProcess.TimeoutException ex) { MessageBox.Show("The service did not respond in time." + ex.Message, "Service Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } catch (Exception ex) { MessageBox.Show("Error occured while stopping service. " + ex.Message, "Service Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void AboutMenuItem_Click(object sender, EventArgs e) { using (frmAbout frm = new frmAbout()) { frm.ShowDialog(); } } private void AutoStartMenuItem_Click(object sender, EventArgs e) { if (AutoStartMenuItem.Checked) { //remove try { using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true)) { if (key != null) key.DeleteValue("Technitium DNS System Tray", false); } } catch (Exception ex) { MessageBox.Show("Error occured while removing auto start registry entry. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { //add try { using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true)) { if (key != null) key.SetValue("Technitium DNS System Tray", "\"" + Program.APP_PATH + "\"", RegistryValueKind.String); } } catch (Exception ex) { MessageBox.Show("Error occured while adding auto start registry entry. " + ex.Message, "Error - " + Resources.ServiceName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } private void ExitMenuItem_Click(object sender, EventArgs e) { if (MessageBox.Show(Resources.AreYouSureYouWantToQuit, Resources.Quit + " - " + Resources.ServiceName, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes) Application.Exit(); } #endregion } }