/*
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
}
}