mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2025-12-26 03:17:55 +00:00
3540 lines
160 KiB
C#
3540 lines
160 KiB
C#
/*
|
|
Technitium DNS Server
|
|
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
using DnsServerCore.Auth;
|
|
using DnsServerCore.Dhcp;
|
|
using DnsServerCore.Dns;
|
|
using DnsServerCore.Dns.ResourceRecords;
|
|
using DnsServerCore.Dns.ZoneManagers;
|
|
using DnsServerCore.Dns.Zones;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.Security;
|
|
using System.Net.Sockets;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using TechnitiumLibrary;
|
|
using TechnitiumLibrary.IO;
|
|
using TechnitiumLibrary.Net;
|
|
using TechnitiumLibrary.Net.Dns;
|
|
using TechnitiumLibrary.Net.Dns.ResourceRecords;
|
|
using TechnitiumLibrary.Net.Http;
|
|
using TechnitiumLibrary.Net.Proxy;
|
|
|
|
namespace DnsServerCore
|
|
{
|
|
public sealed class DnsWebService : IDisposable
|
|
{
|
|
#region enum
|
|
|
|
enum ServiceState
|
|
{
|
|
Stopped = 0,
|
|
Starting = 1,
|
|
Running = 2,
|
|
Stopping = 3
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region variables
|
|
|
|
internal readonly Version _currentVersion;
|
|
readonly string _appFolder;
|
|
internal readonly string _configFolder;
|
|
readonly Uri _updateCheckUri;
|
|
|
|
internal readonly LogManager _log;
|
|
internal readonly AuthManager _authManager;
|
|
|
|
internal readonly WebServiceSettingsApi _settingsApi;
|
|
internal readonly WebServiceAuthApi _authApi;
|
|
internal readonly WebServiceDashboardApi _dashboardApi;
|
|
internal readonly WebServiceZonesApi _zonesApi;
|
|
internal readonly WebServiceOtherZonesApi _otherZonesApi;
|
|
internal readonly WebServiceAppsApi _appsApi;
|
|
internal readonly WebServiceDhcpApi _dhcpApi;
|
|
internal readonly WebServiceLogsApi _logsApi;
|
|
|
|
internal DnsServer _dnsServer;
|
|
internal DhcpServer _dhcpServer;
|
|
|
|
internal IReadOnlyList<IPAddress> _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any };
|
|
internal int _webServiceHttpPort = 5380;
|
|
internal int _webServiceTlsPort = 53443;
|
|
internal bool _webServiceEnableTls;
|
|
internal bool _webServiceHttpToTlsRedirect;
|
|
internal bool _webServiceUseSelfSignedTlsCertificate;
|
|
internal string _webServiceTlsCertificatePath;
|
|
internal string _webServiceTlsCertificatePassword;
|
|
internal DateTime _webServiceTlsCertificateLastModifiedOn;
|
|
|
|
HttpListener _webService;
|
|
IReadOnlyList<Socket> _webServiceTlsListeners;
|
|
X509Certificate2 _webServiceTlsCertificate;
|
|
readonly IndependentTaskScheduler _webServiceTaskScheduler = new IndependentTaskScheduler(ThreadPriority.AboveNormal);
|
|
string _webServiceHostname;
|
|
IPEndPoint _webServiceHttpEP;
|
|
|
|
internal string _dnsTlsCertificatePath;
|
|
internal string _dnsTlsCertificatePassword;
|
|
DateTime _dnsTlsCertificateLastModifiedOn;
|
|
|
|
Timer _tlsCertificateUpdateTimer;
|
|
const int TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL = 60000;
|
|
const int TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL = 60000;
|
|
|
|
volatile ServiceState _state = ServiceState.Stopped;
|
|
|
|
List<string> _configDisabledZones;
|
|
|
|
#endregion
|
|
|
|
#region constructor
|
|
|
|
public DnsWebService(string configFolder = null, Uri updateCheckUri = null, Uri appStoreUri = null)
|
|
{
|
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
|
|
|
_currentVersion = assembly.GetName().Version;
|
|
_appFolder = Path.GetDirectoryName(assembly.Location);
|
|
|
|
if (configFolder is null)
|
|
_configFolder = Path.Combine(_appFolder, "config");
|
|
else
|
|
_configFolder = configFolder;
|
|
|
|
_updateCheckUri = updateCheckUri;
|
|
|
|
Directory.CreateDirectory(_configFolder);
|
|
Directory.CreateDirectory(Path.Combine(_configFolder, "blocklists"));
|
|
|
|
_log = new LogManager(_configFolder);
|
|
_authManager = new AuthManager(_configFolder, _log);
|
|
|
|
_settingsApi = new WebServiceSettingsApi(this);
|
|
_authApi = new WebServiceAuthApi(this);
|
|
_dashboardApi = new WebServiceDashboardApi(this);
|
|
_zonesApi = new WebServiceZonesApi(this);
|
|
_otherZonesApi = new WebServiceOtherZonesApi(this);
|
|
_appsApi = new WebServiceAppsApi(this, appStoreUri);
|
|
_dhcpApi = new WebServiceDhcpApi(this);
|
|
_logsApi = new WebServiceLogsApi(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
|
|
bool _disposed;
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
Stop();
|
|
|
|
if (_settingsApi is not null)
|
|
_settingsApi.Dispose();
|
|
|
|
if (_appsApi is not null)
|
|
_appsApi.Dispose();
|
|
|
|
if (_webService is not null)
|
|
_webService.Close();
|
|
|
|
if (_dnsServer is not null)
|
|
_dnsServer.Dispose();
|
|
|
|
if (_dhcpServer is not null)
|
|
_dhcpServer.Dispose();
|
|
|
|
if (_authManager is not null)
|
|
_authManager.Dispose();
|
|
|
|
if (_log is not null)
|
|
_log.Dispose();
|
|
|
|
_disposed = true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private
|
|
|
|
#region web service
|
|
|
|
private async Task AcceptWebRequestAsync()
|
|
{
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
HttpListenerContext context = await _webService.GetContextAsync();
|
|
|
|
if ((_webServiceTlsListeners != null) && (_webServiceTlsListeners.Count > 0) && _webServiceHttpToTlsRedirect)
|
|
{
|
|
IPEndPoint remoteEP = context.Request.RemoteEndPoint;
|
|
|
|
if ((remoteEP != null) && !IPAddress.IsLoopback(remoteEP.Address))
|
|
{
|
|
string domain = _webServiceTlsCertificate.GetNameInfo(X509NameType.DnsName, false);
|
|
string redirectUri = "https://" + domain + ":" + _webServiceTlsPort + context.Request.Url.PathAndQuery;
|
|
|
|
context.Response.Redirect(redirectUri);
|
|
context.Response.Close();
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
_ = ProcessRequestAsync(context.Request, context.Response);
|
|
}
|
|
}
|
|
catch (HttpListenerException ex)
|
|
{
|
|
if (ex.ErrorCode == 995)
|
|
return; //web service stopping
|
|
|
|
_log.Write(ex);
|
|
}
|
|
catch (ObjectDisposedException)
|
|
{
|
|
//web service stopped
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if ((_state == ServiceState.Stopping) || (_state == ServiceState.Stopped))
|
|
return; //web service stopping
|
|
|
|
_log.Write(ex);
|
|
}
|
|
}
|
|
|
|
private async Task AcceptTlsWebRequestAsync(Socket tlsListener)
|
|
{
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
Socket socket = await tlsListener.AcceptAsync();
|
|
|
|
_ = TlsToHttpTunnelAsync(socket);
|
|
}
|
|
}
|
|
catch (SocketException ex)
|
|
{
|
|
if (ex.SocketErrorCode == SocketError.OperationAborted)
|
|
return; //web service stopping
|
|
|
|
_log.Write(ex);
|
|
}
|
|
catch (ObjectDisposedException)
|
|
{
|
|
//web service stopped
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if ((_state == ServiceState.Stopping) || (_state == ServiceState.Stopped))
|
|
return; //web service stopping
|
|
|
|
_log.Write(ex);
|
|
}
|
|
}
|
|
|
|
private async Task TlsToHttpTunnelAsync(Socket socket)
|
|
{
|
|
Socket tunnel = null;
|
|
|
|
try
|
|
{
|
|
if (_webServiceLocalAddresses.Count < 1)
|
|
return;
|
|
|
|
string remoteIP = (socket.RemoteEndPoint as IPEndPoint).Address.ToString();
|
|
|
|
SslStream sslStream = new SslStream(new NetworkStream(socket, true));
|
|
|
|
await sslStream.AuthenticateAsServerAsync(_webServiceTlsCertificate);
|
|
|
|
tunnel = new Socket(_webServiceHttpEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
tunnel.Connect(_webServiceHttpEP);
|
|
|
|
NetworkStream tunnelStream = new NetworkStream(tunnel, true);
|
|
|
|
//copy tunnel to ssl
|
|
_ = tunnelStream.CopyToAsync(sslStream).ContinueWith(delegate (Task prevTask) { sslStream.Dispose(); tunnelStream.Dispose(); });
|
|
|
|
//copy ssl to tunnel
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
HttpRequest httpRequest = await HttpRequest.ReadRequestAsync(sslStream);
|
|
if (httpRequest == null)
|
|
return; //connection closed gracefully by client
|
|
|
|
//inject X-Real-IP & host header
|
|
httpRequest.Headers.Add("X-Real-IP", remoteIP);
|
|
httpRequest.Headers[HttpRequestHeader.Host] = "localhost:" + _webServiceHttpPort.ToString();
|
|
|
|
//relay request
|
|
await tunnelStream.WriteAsync(Encoding.ASCII.GetBytes(httpRequest.HttpMethod + " " + httpRequest.RequestPathAndQuery + " " + httpRequest.Protocol + "\r\n"));
|
|
await tunnelStream.WriteAsync(httpRequest.Headers.ToByteArray());
|
|
|
|
if (httpRequest.InputStream != null)
|
|
await httpRequest.InputStream.CopyToAsync(tunnelStream);
|
|
|
|
await tunnelStream.FlushAsync();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
sslStream.Dispose();
|
|
tunnelStream.Dispose();
|
|
}
|
|
}
|
|
catch (IOException)
|
|
{
|
|
//ignore
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write(ex);
|
|
}
|
|
finally
|
|
{
|
|
socket.Dispose();
|
|
|
|
if (tunnel != null)
|
|
tunnel.Dispose();
|
|
}
|
|
}
|
|
|
|
private async Task ProcessRequestAsync(HttpListenerRequest request, HttpListenerResponse response)
|
|
{
|
|
response.AddHeader("Server", "");
|
|
response.AddHeader("X-Robots-Tag", "noindex, nofollow");
|
|
|
|
try
|
|
{
|
|
Uri url = request.Url;
|
|
string path = url.AbsolutePath;
|
|
|
|
if (!path.StartsWith("/") || path.Contains("/../") || path.Contains("/.../"))
|
|
{
|
|
await SendErrorAsync(response, 404);
|
|
return;
|
|
}
|
|
|
|
if (path.StartsWith("/api/"))
|
|
{
|
|
using (MemoryStream mS = new MemoryStream())
|
|
{
|
|
try
|
|
{
|
|
Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS);
|
|
jsonWriter.WriteStartObject();
|
|
|
|
switch (path)
|
|
{
|
|
case "/api/user/login":
|
|
case "/api/login":
|
|
await _authApi.LoginAsync(request, jsonWriter, UserSessionType.Standard);
|
|
break;
|
|
|
|
case "/api/user/createToken":
|
|
await _authApi.LoginAsync(request, jsonWriter, UserSessionType.ApiToken);
|
|
break;
|
|
|
|
case "/api/user/logout":
|
|
case "/api/logout":
|
|
_authApi.Logout(request);
|
|
break;
|
|
|
|
case "/api/user/session/get":
|
|
_authApi.GetCurrentSessionDetails(request, jsonWriter);
|
|
break;
|
|
|
|
default:
|
|
if (!TryGetSession(request, out UserSession session))
|
|
throw new InvalidTokenWebServiceException("Invalid token or session expired.");
|
|
|
|
jsonWriter.WritePropertyName("response");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
try
|
|
{
|
|
switch (path)
|
|
{
|
|
case "/api/user/session/delete":
|
|
_authApi.DeleteSession(request, false);
|
|
break;
|
|
|
|
case "/api/user/changePassword":
|
|
case "/api/changePassword":
|
|
_authApi.ChangePassword(request);
|
|
break;
|
|
|
|
case "/api/user/profile/get":
|
|
_authApi.GetProfile(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/user/profile/set":
|
|
_authApi.SetProfile(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/user/checkForUpdate":
|
|
case "/api/checkForUpdate":
|
|
await CheckForUpdateAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/dashboard/stats/get":
|
|
case "/api/getStats":
|
|
if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _dashboardApi.GetStats(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/dashboard/stats/getTop":
|
|
case "/api/getTopStats":
|
|
if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _dashboardApi.GetTopStats(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/dashboard/stats/deleteAll":
|
|
case "/api/deleteAllStats":
|
|
if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_logsApi.DeleteAllStats(request);
|
|
break;
|
|
|
|
case "/api/zones/list":
|
|
case "/api/zone/list":
|
|
case "/api/listZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.ListZones(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/zones/create":
|
|
case "/api/zone/create":
|
|
case "/api/createZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _zonesApi.CreateZoneAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/zones/enable":
|
|
case "/api/zone/enable":
|
|
case "/api/enableZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.EnableZone(request);
|
|
break;
|
|
|
|
case "/api/zones/disable":
|
|
case "/api/zone/disable":
|
|
case "/api/disableZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.DisableZone(request);
|
|
break;
|
|
|
|
case "/api/zones/delete":
|
|
case "/api/zone/delete":
|
|
case "/api/deleteZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.DeleteZone(request);
|
|
break;
|
|
|
|
case "/api/zones/resync":
|
|
case "/api/zone/resync":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.ResyncZone(request);
|
|
break;
|
|
|
|
case "/api/zones/options/get":
|
|
case "/api/zone/options/get":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.GetZoneOptions(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/zones/options/set":
|
|
case "/api/zone/options/set":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.SetZoneOptions(request);
|
|
break;
|
|
|
|
case "/api/zones/permissions/get":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.GetPermissionDetails(request, jsonWriter, PermissionSection.Zones);
|
|
break;
|
|
|
|
case "/api/zones/permissions/set":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.SetPermissionsDetails(request, jsonWriter, PermissionSection.Zones);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/sign":
|
|
case "/api/zone/dnssec/sign":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.SignPrimaryZone(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/unsign":
|
|
case "/api/zone/dnssec/unsign":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.UnsignPrimaryZone(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/get":
|
|
case "/api/zone/dnssec/getProperties":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.GetPrimaryZoneDnssecProperties(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/convertToNSEC":
|
|
case "/api/zone/dnssec/convertToNSEC":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.ConvertPrimaryZoneToNSEC(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/convertToNSEC3":
|
|
case "/api/zone/dnssec/convertToNSEC3":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.ConvertPrimaryZoneToNSEC3(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/updateNSEC3Params":
|
|
case "/api/zone/dnssec/updateNSEC3Params":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.UpdatePrimaryZoneNSEC3Parameters(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/updateDnsKeyTtl":
|
|
case "/api/zone/dnssec/updateDnsKeyTtl":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.UpdatePrimaryZoneDnssecDnsKeyTtl(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/generatePrivateKey":
|
|
case "/api/zone/dnssec/generatePrivateKey":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.GenerateAndAddPrimaryZoneDnssecPrivateKey(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/updatePrivateKey":
|
|
case "/api/zone/dnssec/updatePrivateKey":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.UpdatePrimaryZoneDnssecPrivateKey(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/deletePrivateKey":
|
|
case "/api/zone/dnssec/deletePrivateKey":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.DeletePrimaryZoneDnssecPrivateKey(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/publishAllPrivateKeys":
|
|
case "/api/zone/dnssec/publishAllPrivateKeys":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/rolloverDnsKey":
|
|
case "/api/zone/dnssec/rolloverDnsKey":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.RolloverPrimaryZoneDnsKey(request);
|
|
break;
|
|
|
|
case "/api/zones/dnssec/properties/retireDnsKey":
|
|
case "/api/zone/dnssec/retireDnsKey":
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_zonesApi.RetirePrimaryZoneDnsKey(request);
|
|
break;
|
|
|
|
case "/api/zones/records/add":
|
|
case "/api/zone/addRecord":
|
|
case "/api/addRecord":
|
|
_zonesApi.AddRecord(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/zones/records/get":
|
|
case "/api/zone/getRecords":
|
|
case "/api/getRecords":
|
|
_zonesApi.GetRecords(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/zones/records/update":
|
|
case "/api/zone/updateRecord":
|
|
case "/api/updateRecord":
|
|
_zonesApi.UpdateRecord(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/zones/records/delete":
|
|
case "/api/zone/deleteRecord":
|
|
case "/api/deleteRecord":
|
|
_zonesApi.DeleteRecord(request);
|
|
break;
|
|
|
|
case "/api/cache/list":
|
|
case "/api/listCachedZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.ListCachedZones(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/cache/delete":
|
|
case "/api/deleteCachedZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.DeleteCachedZone(request);
|
|
break;
|
|
|
|
case "/api/cache/flush":
|
|
case "/api/flushDnsCache":
|
|
if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.FlushCache(request);
|
|
break;
|
|
|
|
case "/api/allowed/list":
|
|
case "/api/listAllowedZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.ListAllowedZones(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/allowed/add":
|
|
case "/api/allowZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.AllowZone(request);
|
|
break;
|
|
|
|
case "/api/allowed/delete":
|
|
case "/api/deleteAllowedZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.DeleteAllowedZone(request);
|
|
break;
|
|
|
|
case "/api/allowed/flush":
|
|
case "/api/flushAllowedZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.FlushAllowedZone(request);
|
|
break;
|
|
|
|
case "/api/allowed/import":
|
|
case "/api/importAllowedZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _otherZonesApi.ImportAllowedZonesAsync(request);
|
|
break;
|
|
|
|
case "/api/allowed/export":
|
|
case "/api/exportAllowedZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.ExportAllowedZones(response);
|
|
return;
|
|
|
|
case "/api/blocked/list":
|
|
case "/api/listBlockedZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.ListBlockedZones(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/blocked/add":
|
|
case "/api/blockZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.BlockZone(request);
|
|
break;
|
|
|
|
case "/api/blocked/delete":
|
|
case "/api/deleteBlockedZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.DeleteBlockedZone(request);
|
|
break;
|
|
|
|
case "/api/blocked/flush":
|
|
case "/api/flushBlockedZone":
|
|
if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.FlushBlockedZone(request);
|
|
break;
|
|
|
|
case "/api/blocked/import":
|
|
case "/api/importBlockedZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _otherZonesApi.ImportBlockedZonesAsync(request);
|
|
break;
|
|
|
|
case "/api/blocked/export":
|
|
case "/api/exportBlockedZones":
|
|
if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_otherZonesApi.ExportBlockedZones(response);
|
|
return;
|
|
|
|
case "/api/apps/list":
|
|
if (
|
|
_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View) ||
|
|
_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View) ||
|
|
_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View)
|
|
)
|
|
{
|
|
await _appsApi.ListInstalledAppsAsync(jsonWriter);
|
|
}
|
|
else
|
|
{
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
}
|
|
|
|
break;
|
|
|
|
case "/api/apps/listStoreApps":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _appsApi.ListStoreApps(jsonWriter);
|
|
break;
|
|
|
|
case "/api/apps/downloadAndInstall":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _appsApi.DownloadAndInstallAppAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/apps/downloadAndUpdate":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _appsApi.DownloadAndUpdateAppAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/apps/install":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _appsApi.InstallAppAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/apps/update":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _appsApi.UpdateAppAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/apps/uninstall":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_appsApi.UninstallApp(request);
|
|
break;
|
|
|
|
case "/api/apps/config/get":
|
|
case "/api/apps/getConfig":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _appsApi.GetAppConfigAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/apps/config/set":
|
|
case "/api/apps/setConfig":
|
|
if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _appsApi.SetAppConfigAsync(request);
|
|
break;
|
|
|
|
case "/api/dnsClient/resolve":
|
|
case "/api/resolveQuery":
|
|
if (!_authManager.IsPermitted(PermissionSection.DnsClient, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await ResolveQueryAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/settings/get":
|
|
case "/api/getDnsSettings":
|
|
if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_settingsApi.GetDnsSettings(jsonWriter);
|
|
break;
|
|
|
|
case "/api/settings/set":
|
|
case "/api/setDnsSettings":
|
|
if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_settingsApi.SetDnsSettings(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/settings/getTsigKeyNames":
|
|
if (
|
|
_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.View) ||
|
|
_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)
|
|
)
|
|
{
|
|
_settingsApi.GetTsigKeyNames(jsonWriter);
|
|
}
|
|
else
|
|
{
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
}
|
|
|
|
break;
|
|
|
|
case "/api/settings/forceUpdateBlockLists":
|
|
case "/api/forceUpdateBlockLists":
|
|
if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_settingsApi.ForceUpdateBlockLists(request);
|
|
break;
|
|
|
|
case "/api/settings/temporaryDisableBlocking":
|
|
case "/api/temporaryDisableBlocking":
|
|
if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_settingsApi.TemporaryDisableBlocking(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/settings/backup":
|
|
case "/api/backupSettings":
|
|
if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _settingsApi.BackupSettingsAsync(request, response);
|
|
return;
|
|
|
|
case "/api/settings/restore":
|
|
case "/api/restoreSettings":
|
|
if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _settingsApi.RestoreSettingsAsync(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/dhcp/leases/list":
|
|
case "/api/listDhcpLeases":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.ListDhcpLeases(jsonWriter);
|
|
break;
|
|
|
|
case "/api/dhcp/leases/remove":
|
|
case "/api/removeDhcpLease":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.RemoveDhcpLease(request);
|
|
break;
|
|
|
|
case "/api/dhcp/leases/convertToReserved":
|
|
case "/api/convertToReservedLease":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.ConvertToReservedLease(request);
|
|
break;
|
|
|
|
case "/api/dhcp/leases/convertToDynamic":
|
|
case "/api/convertToDynamicLease":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.ConvertToDynamicLease(request);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/list":
|
|
case "/api/listDhcpScopes":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.ListDhcpScopes(jsonWriter);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/get":
|
|
case "/api/getDhcpScope":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.GetDhcpScope(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/set":
|
|
case "/api/setDhcpScope":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _dhcpApi.SetDhcpScopeAsync(request);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/addReservedLease":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.AddReservedLease(request);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/removeReservedLease":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.RemoveReservedLease(request);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/enable":
|
|
case "/api/enableDhcpScope":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _dhcpApi.EnableDhcpScopeAsync(request);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/disable":
|
|
case "/api/disableDhcpScope":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.DisableDhcpScope(request);
|
|
break;
|
|
|
|
case "/api/dhcp/scopes/delete":
|
|
case "/api/deleteDhcpScope":
|
|
if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dhcpApi.DeleteDhcpScope(request);
|
|
break;
|
|
|
|
case "/api/admin/sessions/list":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.ListSessions(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/sessions/createToken":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.CreateApiToken(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/sessions/delete":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.DeleteSession(request, true);
|
|
break;
|
|
|
|
case "/api/admin/users/list":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.ListUsers(jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/users/create":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.CreateUser(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/users/get":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.GetUserDetails(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/users/set":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.SetUserDetails(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/users/delete":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.DeleteUser(request);
|
|
break;
|
|
|
|
case "/api/admin/groups/list":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.ListGroups(jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/groups/create":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.CreateGroup(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/groups/get":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.GetGroupDetails(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/groups/set":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.SetGroupDetails(request, jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/groups/delete":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.DeleteGroup(request);
|
|
break;
|
|
|
|
case "/api/admin/permissions/list":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.ListPermissions(jsonWriter);
|
|
break;
|
|
|
|
case "/api/admin/permissions/get":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.GetPermissionDetails(request, jsonWriter, PermissionSection.Unknown);
|
|
break;
|
|
|
|
case "/api/admin/permissions/set":
|
|
if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_authApi.SetPermissionsDetails(request, jsonWriter, PermissionSection.Unknown);
|
|
break;
|
|
|
|
case "/api/logs/list":
|
|
case "/api/listLogs":
|
|
if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_logsApi.ListLogs(jsonWriter);
|
|
break;
|
|
|
|
case "/api/logs/download":
|
|
if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _logsApi.DownloadLogAsync(request, response);
|
|
return;
|
|
|
|
case "/api/logs/delete":
|
|
case "/api/deleteLog":
|
|
if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_logsApi.DeleteLog(request);
|
|
break;
|
|
|
|
case "/api/logs/deleteAll":
|
|
case "/api/deleteAllLogs":
|
|
if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_logsApi.DeleteAllLogs(request);
|
|
break;
|
|
|
|
case "/api/logs/query":
|
|
case "/api/queryLogs":
|
|
if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
await _logsApi.QueryLogsAsync(request, jsonWriter);
|
|
break;
|
|
|
|
default:
|
|
await SendErrorAsync(response, 404);
|
|
return;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
break;
|
|
}
|
|
|
|
jsonWriter.WriteString("status", "ok");
|
|
|
|
jsonWriter.WriteEndObject();
|
|
jsonWriter.Flush();
|
|
}
|
|
catch (InvalidTokenWebServiceException ex)
|
|
{
|
|
mS.SetLength(0);
|
|
Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS);
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("status", "invalid-token");
|
|
jsonWriter.WriteString("errorMessage", ex.Message);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
jsonWriter.Flush();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
UserSession session = null;
|
|
|
|
string strToken = request.QueryString["token"];
|
|
if (!string.IsNullOrEmpty(strToken))
|
|
session = _authManager.GetSession(strToken);
|
|
|
|
if (session is null)
|
|
_log.Write(GetRequestRemoteEndPoint(request), ex);
|
|
else
|
|
_log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + ex.ToString());
|
|
|
|
mS.SetLength(0);
|
|
Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS);
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("status", "error");
|
|
jsonWriter.WriteString("errorMessage", ex.Message);
|
|
jsonWriter.WriteString("stackTrace", ex.StackTrace);
|
|
|
|
if (ex.InnerException is not null)
|
|
jsonWriter.WriteString("innerErrorMessage", ex.InnerException.Message);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
jsonWriter.Flush();
|
|
}
|
|
|
|
response.ContentType = "application/json; charset=utf-8";
|
|
response.ContentEncoding = Encoding.UTF8;
|
|
response.ContentLength64 = mS.Length;
|
|
|
|
mS.Position = 0;
|
|
using (Stream stream = response.OutputStream)
|
|
{
|
|
await mS.CopyToAsync(stream);
|
|
}
|
|
}
|
|
}
|
|
else if (path.StartsWith("/log/"))
|
|
{
|
|
if (!TryGetSession(request, out UserSession session))
|
|
{
|
|
await SendErrorAsync(response, 403, "Invalid token or session expired.");
|
|
return;
|
|
}
|
|
|
|
if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string[] pathParts = path.Split('/');
|
|
string logFileName = pathParts[2];
|
|
|
|
int limit = 0;
|
|
string strLimit = request.QueryString["limit"];
|
|
if (!string.IsNullOrEmpty(strLimit))
|
|
limit = int.Parse(strLimit);
|
|
|
|
await _log.DownloadLogAsync(request, response, logFileName, limit * 1024 * 1024);
|
|
}
|
|
else
|
|
{
|
|
if (path == "/")
|
|
{
|
|
path = "/index.html";
|
|
}
|
|
else if ((path == "/blocklist.txt") && !IPAddress.IsLoopback(GetRequestRemoteEndPoint(request).Address))
|
|
{
|
|
await SendErrorAsync(response, 403);
|
|
return;
|
|
}
|
|
|
|
string wwwroot = Path.Combine(_appFolder, "www");
|
|
path = Path.GetFullPath(wwwroot + path.Replace('/', Path.DirectorySeparatorChar));
|
|
|
|
if (!path.StartsWith(wwwroot) || !File.Exists(path))
|
|
{
|
|
await SendErrorAsync(response, 404);
|
|
return;
|
|
}
|
|
|
|
await SendFileAsync(request, response, path);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if ((_state == ServiceState.Stopping) || (_state == ServiceState.Stopped))
|
|
return; //web service stopping
|
|
|
|
UserSession session = null;
|
|
|
|
string strToken = request.QueryString["token"];
|
|
if (!string.IsNullOrEmpty(strToken))
|
|
session = _authManager.GetSession(strToken);
|
|
|
|
if (session is null)
|
|
_log.Write(GetRequestRemoteEndPoint(request), ex);
|
|
else
|
|
_log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + ex.ToString());
|
|
|
|
await SendError(response, ex);
|
|
}
|
|
}
|
|
|
|
internal static IPEndPoint GetRequestRemoteEndPoint(HttpListenerRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (request.RemoteEndPoint == null)
|
|
return new IPEndPoint(IPAddress.Any, 0);
|
|
|
|
if (NetUtilities.IsPrivateIP(request.RemoteEndPoint.Address))
|
|
{
|
|
string xRealIp = request.Headers["X-Real-IP"];
|
|
if (IPAddress.TryParse(xRealIp, out IPAddress address))
|
|
{
|
|
//get the real IP address of the requesting client from X-Real-IP header set in nginx proxy_pass block
|
|
return new IPEndPoint(address, 0);
|
|
}
|
|
}
|
|
|
|
return request.RemoteEndPoint;
|
|
}
|
|
catch
|
|
{
|
|
return new IPEndPoint(IPAddress.Any, 0);
|
|
}
|
|
}
|
|
|
|
public static Stream GetOutputStream(HttpListenerRequest request, HttpListenerResponse response)
|
|
{
|
|
string strAcceptEncoding = request.Headers["Accept-Encoding"];
|
|
if (string.IsNullOrEmpty(strAcceptEncoding))
|
|
{
|
|
return response.OutputStream;
|
|
}
|
|
else
|
|
{
|
|
if (strAcceptEncoding.Contains("gzip"))
|
|
{
|
|
response.AddHeader("Content-Encoding", "gzip");
|
|
return new GZipStream(response.OutputStream, CompressionMode.Compress);
|
|
}
|
|
else if (strAcceptEncoding.Contains("deflate"))
|
|
{
|
|
response.AddHeader("Content-Encoding", "deflate");
|
|
return new DeflateStream(response.OutputStream, CompressionMode.Compress);
|
|
}
|
|
else
|
|
{
|
|
return response.OutputStream;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Task SendError(HttpListenerResponse response, Exception ex)
|
|
{
|
|
return SendErrorAsync(response, 500, ex.ToString());
|
|
}
|
|
|
|
private static async Task SendErrorAsync(HttpListenerResponse response, int statusCode, string message = null)
|
|
{
|
|
try
|
|
{
|
|
string statusString = statusCode + " " + DnsServer.GetHttpStatusString((HttpStatusCode)statusCode);
|
|
byte[] buffer = Encoding.UTF8.GetBytes("<html><head><title>" + statusString + "</title></head><body><h1>" + statusString + "</h1>" + (message == null ? "" : "<p>" + message + "</p>") + "</body></html>");
|
|
|
|
response.StatusCode = statusCode;
|
|
response.ContentType = "text/html";
|
|
response.ContentLength64 = buffer.Length;
|
|
|
|
using (Stream stream = response.OutputStream)
|
|
{
|
|
await stream.WriteAsync(buffer);
|
|
}
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
|
|
private static async Task SendFileAsync(HttpListenerRequest request, HttpListenerResponse response, string filePath)
|
|
{
|
|
using (FileStream fS = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
|
{
|
|
response.ContentType = WebUtilities.GetContentType(filePath).MediaType;
|
|
response.AddHeader("Cache-Control", "private, max-age=300");
|
|
|
|
using (Stream stream = GetOutputStream(request, response))
|
|
{
|
|
try
|
|
{
|
|
await fS.CopyToAsync(stream);
|
|
}
|
|
catch (HttpListenerException)
|
|
{
|
|
//ignore this error
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal UserSession GetSession(HttpListenerRequest request)
|
|
{
|
|
string strToken = request.QueryString["token"];
|
|
if (string.IsNullOrEmpty(strToken))
|
|
throw new DnsWebServiceException("Parameter 'token' missing.");
|
|
|
|
return _authManager.GetSession(strToken);
|
|
}
|
|
|
|
internal bool TryGetSession(HttpListenerRequest request, out UserSession session)
|
|
{
|
|
session = GetSession(request);
|
|
if ((session is null) || session.User.Disabled)
|
|
return false;
|
|
|
|
if (session.HasExpired())
|
|
{
|
|
_authManager.DeleteSession(session.Token);
|
|
_authManager.SaveConfigFile();
|
|
return false;
|
|
}
|
|
|
|
IPEndPoint remoteEP = GetRequestRemoteEndPoint(request);
|
|
|
|
session.UpdateLastSeen(remoteEP.Address, request.UserAgent);
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region update api
|
|
|
|
private async Task CheckForUpdateAsync(HttpListenerRequest request, Utf8JsonWriter jsonWriter)
|
|
{
|
|
if (_updateCheckUri is null)
|
|
{
|
|
jsonWriter.WriteBoolean("updateAvailable", false);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
SocketsHttpHandler handler = new SocketsHttpHandler();
|
|
handler.Proxy = _dnsServer.Proxy;
|
|
handler.UseProxy = _dnsServer.Proxy is not null;
|
|
|
|
using (HttpClient http = new HttpClient(handler))
|
|
{
|
|
Stream response = await http.GetStreamAsync(_updateCheckUri);
|
|
using JsonDocument jsonDocument = await JsonDocument.ParseAsync(response);
|
|
JsonElement jsonResponse = jsonDocument.RootElement;
|
|
|
|
string updateVersion = jsonResponse.GetProperty("updateVersion").GetString();
|
|
string updateTitle = jsonResponse.GetPropertyValue("updateTitle", null);
|
|
string updateMessage = jsonResponse.GetPropertyValue("updateMessage", null);
|
|
string downloadLink = jsonResponse.GetPropertyValue("downloadLink", null);
|
|
string instructionsLink = jsonResponse.GetPropertyValue("instructionsLink", null);
|
|
string changeLogLink = jsonResponse.GetPropertyValue("changeLogLink", null);
|
|
|
|
bool updateAvailable = new Version(updateVersion) > _currentVersion;
|
|
|
|
jsonWriter.WriteBoolean("updateAvailable", updateAvailable);
|
|
jsonWriter.WriteString("updateVersion", updateVersion);
|
|
jsonWriter.WriteString("currentVersion", GetCleanVersion(_currentVersion));
|
|
|
|
if (updateAvailable)
|
|
{
|
|
jsonWriter.WriteString("updateTitle", updateTitle);
|
|
jsonWriter.WriteString("updateMessage", updateMessage);
|
|
jsonWriter.WriteString("downloadLink", downloadLink);
|
|
jsonWriter.WriteString("instructionsLink", instructionsLink);
|
|
jsonWriter.WriteString("changeLogLink", changeLogLink);
|
|
}
|
|
|
|
string strLog = "Check for update was done {updateAvailable: " + updateAvailable + "; updateVersion: " + updateVersion + ";";
|
|
|
|
if (!string.IsNullOrEmpty(updateTitle))
|
|
strLog += " updateTitle: " + updateTitle + ";";
|
|
|
|
if (!string.IsNullOrEmpty(updateMessage))
|
|
strLog += " updateMessage: " + updateMessage + ";";
|
|
|
|
if (!string.IsNullOrEmpty(downloadLink))
|
|
strLog += " downloadLink: " + downloadLink + ";";
|
|
|
|
if (!string.IsNullOrEmpty(instructionsLink))
|
|
strLog += " instructionsLink: " + instructionsLink + ";";
|
|
|
|
if (!string.IsNullOrEmpty(changeLogLink))
|
|
strLog += " changeLogLink: " + changeLogLink + ";";
|
|
|
|
strLog += "}";
|
|
|
|
_log.Write(GetRequestRemoteEndPoint(request), strLog);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write(GetRequestRemoteEndPoint(request), "Check for update was done {updateAvailable: False;}\r\n" + ex.ToString());
|
|
|
|
jsonWriter.WriteBoolean("updateAvailable", false);
|
|
}
|
|
}
|
|
|
|
internal static string GetCleanVersion(Version version)
|
|
{
|
|
string strVersion = version.Major + "." + version.Minor;
|
|
|
|
if (version.Build > 0)
|
|
strVersion += "." + version.Build;
|
|
|
|
if (version.Revision > 0)
|
|
strVersion += "." + version.Revision;
|
|
|
|
return strVersion;
|
|
}
|
|
|
|
internal string GetServerVersion()
|
|
{
|
|
return GetCleanVersion(_currentVersion);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region dns client api
|
|
|
|
private async Task ResolveQueryAsync(HttpListenerRequest request, Utf8JsonWriter jsonWriter)
|
|
{
|
|
string server = request.QueryString["server"];
|
|
if (string.IsNullOrEmpty(server))
|
|
throw new DnsWebServiceException("Parameter 'server' missing.");
|
|
|
|
string domain = request.QueryString["domain"];
|
|
if (string.IsNullOrEmpty(domain))
|
|
throw new DnsWebServiceException("Parameter 'domain' missing.");
|
|
|
|
domain = domain.Trim(new char[] { '\t', ' ', '.' });
|
|
|
|
string strType = request.QueryString["type"];
|
|
if (string.IsNullOrEmpty(strType))
|
|
throw new DnsWebServiceException("Parameter 'type' missing.");
|
|
|
|
DnsResourceRecordType type = Enum.Parse<DnsResourceRecordType>(strType, true);
|
|
|
|
string strProtocol = request.QueryString["protocol"];
|
|
if (string.IsNullOrEmpty(strProtocol))
|
|
strProtocol = "Udp";
|
|
|
|
bool dnssecValidation = false;
|
|
string strDnssecValidation = request.QueryString["dnssec"];
|
|
if (!string.IsNullOrEmpty(strDnssecValidation))
|
|
dnssecValidation = bool.Parse(strDnssecValidation);
|
|
|
|
bool importResponse = false;
|
|
string strImport = request.QueryString["import"];
|
|
if (!string.IsNullOrEmpty(strImport))
|
|
importResponse = bool.Parse(strImport);
|
|
|
|
NetProxy proxy = _dnsServer.Proxy;
|
|
bool preferIPv6 = _dnsServer.PreferIPv6;
|
|
ushort udpPayloadSize = _dnsServer.UdpPayloadSize;
|
|
bool randomizeName = false;
|
|
bool qnameMinimization = _dnsServer.QnameMinimization;
|
|
DnsTransportProtocol protocol = Enum.Parse<DnsTransportProtocol>(strProtocol, true);
|
|
const int RETRIES = 1;
|
|
const int TIMEOUT = 10000;
|
|
|
|
DnsDatagram dnsResponse;
|
|
string dnssecErrorMessage = null;
|
|
|
|
if (server.Equals("recursive-resolver", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (type == DnsResourceRecordType.AXFR)
|
|
throw new DnsServerException("Cannot do zone transfer (AXFR) for 'recursive-resolver'.");
|
|
|
|
DnsQuestionRecord question;
|
|
|
|
if ((type == DnsResourceRecordType.PTR) && IPAddress.TryParse(domain, out IPAddress address))
|
|
question = new DnsQuestionRecord(address, DnsClass.IN);
|
|
else
|
|
question = new DnsQuestionRecord(domain, type, DnsClass.IN);
|
|
|
|
DnsCache dnsCache = new DnsCache();
|
|
dnsCache.MinimumRecordTtl = 0;
|
|
dnsCache.MaximumRecordTtl = 7 * 24 * 60 * 60;
|
|
|
|
try
|
|
{
|
|
dnsResponse = await DnsClient.RecursiveResolveAsync(question, dnsCache, proxy, preferIPv6, udpPayloadSize, randomizeName, qnameMinimization, false, dnssecValidation, null, RETRIES, TIMEOUT);
|
|
}
|
|
catch (DnsClientResponseDnssecValidationException ex)
|
|
{
|
|
dnsResponse = ex.Response;
|
|
dnssecErrorMessage = ex.Message;
|
|
importResponse = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((type == DnsResourceRecordType.AXFR) && (protocol == DnsTransportProtocol.Udp))
|
|
protocol = DnsTransportProtocol.Tcp;
|
|
|
|
NameServerAddress nameServer;
|
|
|
|
if (server.Equals("this-server", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
switch (protocol)
|
|
{
|
|
case DnsTransportProtocol.Udp:
|
|
nameServer = _dnsServer.ThisServer;
|
|
break;
|
|
|
|
case DnsTransportProtocol.Tcp:
|
|
nameServer = _dnsServer.ThisServer.ChangeProtocol(DnsTransportProtocol.Tcp);
|
|
break;
|
|
|
|
case DnsTransportProtocol.Tls:
|
|
throw new DnsServerException("Cannot use DNS-over-TLS protocol for 'this-server'. Please use the TLS certificate domain name as the server.");
|
|
|
|
case DnsTransportProtocol.Https:
|
|
throw new DnsServerException("Cannot use DNS-over-HTTPS protocol for 'this-server'. Please use the TLS certificate domain name with a url as the server.");
|
|
|
|
default:
|
|
throw new NotSupportedException("DNS transport protocol is not supported: " + protocol.ToString());
|
|
}
|
|
|
|
proxy = null; //no proxy required for this server
|
|
}
|
|
else
|
|
{
|
|
nameServer = new NameServerAddress(server);
|
|
|
|
if (nameServer.Protocol != protocol)
|
|
nameServer = nameServer.ChangeProtocol(protocol);
|
|
|
|
if (nameServer.IsIPEndPointStale)
|
|
{
|
|
if (proxy is null)
|
|
await nameServer.ResolveIPAddressAsync(_dnsServer, _dnsServer.PreferIPv6);
|
|
}
|
|
else if ((nameServer.DomainEndPoint is null) && ((protocol == DnsTransportProtocol.Udp) || (protocol == DnsTransportProtocol.Tcp)))
|
|
{
|
|
try
|
|
{
|
|
await nameServer.ResolveDomainNameAsync(_dnsServer);
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
}
|
|
|
|
DnsClient dnsClient = new DnsClient(nameServer);
|
|
|
|
dnsClient.Proxy = proxy;
|
|
dnsClient.PreferIPv6 = preferIPv6;
|
|
dnsClient.RandomizeName = randomizeName;
|
|
dnsClient.Retries = RETRIES;
|
|
dnsClient.Timeout = TIMEOUT;
|
|
dnsClient.UdpPayloadSize = udpPayloadSize;
|
|
dnsClient.DnssecValidation = dnssecValidation;
|
|
|
|
if (dnssecValidation)
|
|
{
|
|
//load trust anchors into dns client if domain is locally hosted
|
|
_dnsServer.AuthZoneManager.LoadTrustAnchorsTo(dnsClient, domain, type);
|
|
}
|
|
|
|
try
|
|
{
|
|
dnsResponse = await dnsClient.ResolveAsync(domain, type);
|
|
}
|
|
catch (DnsClientResponseDnssecValidationException ex)
|
|
{
|
|
dnsResponse = ex.Response;
|
|
dnssecErrorMessage = ex.Message;
|
|
importResponse = false;
|
|
}
|
|
|
|
if (type == DnsResourceRecordType.AXFR)
|
|
dnsResponse = dnsResponse.Join();
|
|
}
|
|
|
|
if (importResponse)
|
|
{
|
|
UserSession session = GetSession(request);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsServer.AuthZoneManager.FindAuthZoneInfo(domain);
|
|
if ((zoneInfo is null) || ((zoneInfo.Type == AuthZoneType.Secondary) && !zoneInfo.Name.Equals(domain, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
zoneInfo = _dnsServer.AuthZoneManager.CreatePrimaryZone(domain, _dnsServer.ServerDomain, false);
|
|
if (zoneInfo is null)
|
|
throw new DnsServerException("Cannot import records: failed to create primary zone.");
|
|
|
|
//set permissions
|
|
_authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_authManager.SaveConfigFile();
|
|
}
|
|
else
|
|
{
|
|
if (!_authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
break;
|
|
|
|
case AuthZoneType.Forwarder:
|
|
if (type == DnsResourceRecordType.AXFR)
|
|
throw new DnsServerException("Cannot import records via zone transfer: import zone must be of primary type.");
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new DnsServerException("Cannot import records: import zone must be of primary or forwarder type.");
|
|
}
|
|
}
|
|
|
|
if (type == DnsResourceRecordType.AXFR)
|
|
{
|
|
_dnsServer.AuthZoneManager.SyncZoneTransferRecords(zoneInfo.Name, dnsResponse.Answer);
|
|
}
|
|
else
|
|
{
|
|
List<DnsResourceRecord> importRecords = new List<DnsResourceRecord>(dnsResponse.Answer.Count + dnsResponse.Authority.Count);
|
|
|
|
foreach (DnsResourceRecord record in dnsResponse.Answer)
|
|
{
|
|
if (record.Name.Equals(zoneInfo.Name, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneInfo.Name, StringComparison.OrdinalIgnoreCase) || (zoneInfo.Name.Length == 0))
|
|
{
|
|
record.RemoveExpiry();
|
|
importRecords.Add(record);
|
|
|
|
if (record.Type == DnsResourceRecordType.NS)
|
|
record.SyncGlueRecords(dnsResponse.Additional);
|
|
}
|
|
}
|
|
|
|
foreach (DnsResourceRecord record in dnsResponse.Authority)
|
|
{
|
|
if (record.Name.Equals(zoneInfo.Name, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneInfo.Name, StringComparison.OrdinalIgnoreCase) || (zoneInfo.Name.Length == 0))
|
|
{
|
|
record.RemoveExpiry();
|
|
importRecords.Add(record);
|
|
|
|
if (record.Type == DnsResourceRecordType.NS)
|
|
record.SyncGlueRecords(dnsResponse.Additional);
|
|
}
|
|
}
|
|
|
|
_dnsServer.AuthZoneManager.ImportRecords(zoneInfo.Name, importRecords);
|
|
}
|
|
|
|
_log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] DNS Client imported record(s) for authoritative zone {server: " + server + "; zone: " + zoneInfo.Name + "; type: " + type + ";}");
|
|
|
|
_dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
}
|
|
|
|
if (dnssecErrorMessage is not null)
|
|
jsonWriter.WriteString("warningMessage", dnssecErrorMessage);
|
|
|
|
jsonWriter.WritePropertyName("result");
|
|
dnsResponse.SerializeTo(jsonWriter);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region tls
|
|
|
|
internal void StartTlsCertificateUpdateTimer()
|
|
{
|
|
if (_tlsCertificateUpdateTimer == null)
|
|
{
|
|
_tlsCertificateUpdateTimer = new Timer(delegate (object state)
|
|
{
|
|
if (!string.IsNullOrEmpty(_webServiceTlsCertificatePath))
|
|
{
|
|
try
|
|
{
|
|
FileInfo fileInfo = new FileInfo(_webServiceTlsCertificatePath);
|
|
|
|
if (fileInfo.Exists && (fileInfo.LastWriteTimeUtc != _webServiceTlsCertificateLastModifiedOn))
|
|
LoadWebServiceTlsCertificate(_webServiceTlsCertificatePath, _webServiceTlsCertificatePassword);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while updating Web Service TLS Certificate: " + _webServiceTlsCertificatePath + "\r\n" + ex.ToString());
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(_dnsTlsCertificatePath))
|
|
{
|
|
try
|
|
{
|
|
FileInfo fileInfo = new FileInfo(_dnsTlsCertificatePath);
|
|
|
|
if (fileInfo.Exists && (fileInfo.LastWriteTimeUtc != _dnsTlsCertificateLastModifiedOn))
|
|
LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while updating DNS Server TLS Certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString());
|
|
}
|
|
}
|
|
|
|
}, null, TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL, TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL);
|
|
}
|
|
}
|
|
|
|
internal void StopTlsCertificateUpdateTimer()
|
|
{
|
|
if (_tlsCertificateUpdateTimer != null)
|
|
{
|
|
_tlsCertificateUpdateTimer.Dispose();
|
|
_tlsCertificateUpdateTimer = null;
|
|
}
|
|
}
|
|
|
|
internal void LoadWebServiceTlsCertificate(string tlsCertificatePath, string tlsCertificatePassword)
|
|
{
|
|
FileInfo fileInfo = new FileInfo(tlsCertificatePath);
|
|
|
|
if (!fileInfo.Exists)
|
|
throw new ArgumentException("Web Service TLS certificate file does not exists: " + tlsCertificatePath);
|
|
|
|
if (Path.GetExtension(tlsCertificatePath) != ".pfx")
|
|
throw new ArgumentException("Web Service TLS certificate file must be PKCS #12 formatted with .pfx extension: " + tlsCertificatePath);
|
|
|
|
X509Certificate2 certificate = new X509Certificate2(tlsCertificatePath, tlsCertificatePassword);
|
|
|
|
_webServiceTlsCertificate = certificate;
|
|
_webServiceTlsCertificateLastModifiedOn = fileInfo.LastWriteTimeUtc;
|
|
|
|
_log.Write("Web Service TLS certificate was loaded: " + tlsCertificatePath);
|
|
}
|
|
|
|
internal void LoadDnsTlsCertificate(string tlsCertificatePath, string tlsCertificatePassword)
|
|
{
|
|
FileInfo fileInfo = new FileInfo(tlsCertificatePath);
|
|
|
|
if (!fileInfo.Exists)
|
|
throw new ArgumentException("DNS Server TLS certificate file does not exists: " + tlsCertificatePath);
|
|
|
|
if (Path.GetExtension(tlsCertificatePath) != ".pfx")
|
|
throw new ArgumentException("DNS Server TLS certificate file must be PKCS #12 formatted with .pfx extension: " + tlsCertificatePath);
|
|
|
|
X509Certificate2 certificate = new X509Certificate2(tlsCertificatePath, tlsCertificatePassword);
|
|
|
|
_dnsServer.Certificate = certificate;
|
|
_dnsTlsCertificateLastModifiedOn = fileInfo.LastWriteTimeUtc;
|
|
|
|
_log.Write("DNS Server TLS certificate was loaded: " + tlsCertificatePath);
|
|
}
|
|
|
|
internal void SelfSignedCertCheck(bool generateNew, bool throwException)
|
|
{
|
|
string selfSignedCertificateFilePath = Path.Combine(_configFolder, "cert.pfx");
|
|
|
|
if (_webServiceUseSelfSignedTlsCertificate)
|
|
{
|
|
if (generateNew || !File.Exists(selfSignedCertificateFilePath))
|
|
{
|
|
RSA rsa = RSA.Create(2048);
|
|
CertificateRequest req = new CertificateRequest("cn=" + _dnsServer.ServerDomain, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
|
X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(5));
|
|
|
|
File.WriteAllBytes(selfSignedCertificateFilePath, cert.Export(X509ContentType.Pkcs12, null as string));
|
|
}
|
|
|
|
if (_webServiceEnableTls && string.IsNullOrEmpty(_webServiceTlsCertificatePath))
|
|
{
|
|
try
|
|
{
|
|
LoadWebServiceTlsCertificate(selfSignedCertificateFilePath, null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while loading self signed Web Service TLS certificate: " + selfSignedCertificateFilePath + "\r\n" + ex.ToString());
|
|
|
|
if (throwException)
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
File.Delete(selfSignedCertificateFilePath);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region config
|
|
|
|
internal void LoadConfigFile()
|
|
{
|
|
string configFile = Path.Combine(_configFolder, "dns.config");
|
|
|
|
try
|
|
{
|
|
int version;
|
|
|
|
using (FileStream fS = new FileStream(configFile, FileMode.Open, FileAccess.Read))
|
|
{
|
|
version = ReadConfigFrom(new BinaryReader(fS));
|
|
}
|
|
|
|
_log.Write("DNS Server config file was loaded: " + configFile);
|
|
|
|
if (version <= 27)
|
|
SaveConfigFile(); //save as new config version to avoid loading old version next time
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
_log.Write("DNS Server config file was not found: " + configFile);
|
|
_log.Write("DNS Server is restoring default config file.");
|
|
|
|
//general
|
|
string serverDomain = Environment.GetEnvironmentVariable("DNS_SERVER_DOMAIN");
|
|
if (!string.IsNullOrEmpty(serverDomain))
|
|
_dnsServer.ServerDomain = serverDomain;
|
|
|
|
_appsApi.EnableAutomaticUpdate = true;
|
|
|
|
string strPreferIPv6 = Environment.GetEnvironmentVariable("DNS_SERVER_PREFER_IPV6");
|
|
if (!string.IsNullOrEmpty(strPreferIPv6))
|
|
_dnsServer.PreferIPv6 = bool.Parse(strPreferIPv6);
|
|
|
|
_dnsServer.DnssecValidation = true;
|
|
CreateForwarderZoneToDisableDnssecForNTP();
|
|
|
|
//optional protocols
|
|
string strDnsOverHttp = Environment.GetEnvironmentVariable("DNS_SERVER_OPTIONAL_PROTOCOL_DNS_OVER_HTTP");
|
|
if (!string.IsNullOrEmpty(strDnsOverHttp))
|
|
_dnsServer.EnableDnsOverHttp = bool.Parse(strDnsOverHttp);
|
|
|
|
//recursion
|
|
string strRecursion = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION");
|
|
if (!string.IsNullOrEmpty(strRecursion))
|
|
_dnsServer.Recursion = Enum.Parse<DnsServerRecursion>(strRecursion, true);
|
|
else
|
|
_dnsServer.Recursion = DnsServerRecursion.AllowOnlyForPrivateNetworks; //default for security reasons
|
|
|
|
string strRecursionDeniedNetworks = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION_DENIED_NETWORKS");
|
|
if (!string.IsNullOrEmpty(strRecursionDeniedNetworks))
|
|
{
|
|
string[] strRecursionDeniedNetworkAddresses = strRecursionDeniedNetworks.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
NetworkAddress[] networks = new NetworkAddress[strRecursionDeniedNetworkAddresses.Length];
|
|
|
|
for (int i = 0; i < networks.Length; i++)
|
|
networks[i] = NetworkAddress.Parse(strRecursionDeniedNetworkAddresses[i].Trim());
|
|
|
|
_dnsServer.RecursionDeniedNetworks = networks;
|
|
}
|
|
|
|
string strRecursionAllowedNetworks = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION_ALLOWED_NETWORKS");
|
|
if (!string.IsNullOrEmpty(strRecursionAllowedNetworks))
|
|
{
|
|
string[] strRecursionAllowedNetworkAddresses = strRecursionAllowedNetworks.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
NetworkAddress[] networks = new NetworkAddress[strRecursionAllowedNetworkAddresses.Length];
|
|
|
|
for (int i = 0; i < networks.Length; i++)
|
|
networks[i] = NetworkAddress.Parse(strRecursionAllowedNetworkAddresses[i].Trim());
|
|
|
|
_dnsServer.RecursionAllowedNetworks = networks;
|
|
}
|
|
|
|
_dnsServer.RandomizeName = true; //default true to enable security feature
|
|
_dnsServer.QnameMinimization = true; //default true to enable privacy feature
|
|
_dnsServer.NsRevalidation = true; //default true for security reasons
|
|
|
|
//cache
|
|
_dnsServer.CacheZoneManager.MaximumEntries = 10000;
|
|
|
|
//blocking
|
|
string strEnableBlocking = Environment.GetEnvironmentVariable("DNS_SERVER_ENABLE_BLOCKING");
|
|
if (!string.IsNullOrEmpty(strEnableBlocking))
|
|
_dnsServer.EnableBlocking = bool.Parse(strEnableBlocking);
|
|
|
|
string strAllowTxtBlockingReport = Environment.GetEnvironmentVariable("DNS_SERVER_ALLOW_TXT_BLOCKING_REPORT");
|
|
if (!string.IsNullOrEmpty(strAllowTxtBlockingReport))
|
|
_dnsServer.AllowTxtBlockingReport = bool.Parse(strAllowTxtBlockingReport);
|
|
|
|
string strBlockListUrls = Environment.GetEnvironmentVariable("DNS_SERVER_BLOCK_LIST_URLS");
|
|
if (!string.IsNullOrEmpty(strBlockListUrls))
|
|
{
|
|
string[] strBlockListUrlList = strBlockListUrls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
foreach (string strBlockListUrl in strBlockListUrlList)
|
|
{
|
|
if (strBlockListUrl.StartsWith("!"))
|
|
{
|
|
Uri allowListUrl = new Uri(strBlockListUrl.Substring(1));
|
|
|
|
if (!_dnsServer.BlockListZoneManager.AllowListUrls.Contains(allowListUrl))
|
|
_dnsServer.BlockListZoneManager.AllowListUrls.Add(allowListUrl);
|
|
}
|
|
else
|
|
{
|
|
Uri blockListUrl = new Uri(strBlockListUrl);
|
|
|
|
if (!_dnsServer.BlockListZoneManager.BlockListUrls.Contains(blockListUrl))
|
|
_dnsServer.BlockListZoneManager.BlockListUrls.Add(blockListUrl);
|
|
}
|
|
}
|
|
}
|
|
|
|
//proxy & forwarders
|
|
string strForwarders = Environment.GetEnvironmentVariable("DNS_SERVER_FORWARDERS");
|
|
if (!string.IsNullOrEmpty(strForwarders))
|
|
{
|
|
DnsTransportProtocol forwarderProtocol;
|
|
|
|
string strForwarderProtocol = Environment.GetEnvironmentVariable("DNS_SERVER_FORWARDER_PROTOCOL");
|
|
if (string.IsNullOrEmpty(strForwarderProtocol))
|
|
{
|
|
forwarderProtocol = DnsTransportProtocol.Udp;
|
|
}
|
|
else
|
|
{
|
|
forwarderProtocol = Enum.Parse<DnsTransportProtocol>(strForwarderProtocol, true);
|
|
if (forwarderProtocol == DnsTransportProtocol.HttpsJson)
|
|
forwarderProtocol = DnsTransportProtocol.Https;
|
|
}
|
|
|
|
List<NameServerAddress> forwarders = new List<NameServerAddress>();
|
|
string[] strForwardersAddresses = strForwarders.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
foreach (string strForwarderAddress in strForwardersAddresses)
|
|
{
|
|
NameServerAddress forwarder = new NameServerAddress(strForwarderAddress.Trim());
|
|
|
|
if (forwarder.Protocol != forwarderProtocol)
|
|
forwarder = forwarder.ChangeProtocol(forwarderProtocol);
|
|
|
|
forwarders.Add(forwarder);
|
|
}
|
|
|
|
_dnsServer.Forwarders = forwarders;
|
|
}
|
|
|
|
//logging
|
|
string strUseLocalTime = Environment.GetEnvironmentVariable("DNS_SERVER_LOG_USING_LOCAL_TIME");
|
|
if (!string.IsNullOrEmpty(strUseLocalTime))
|
|
_log.UseLocalTime = bool.Parse(strUseLocalTime);
|
|
|
|
SaveConfigFile();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while loading config file: " + configFile + "\r\n" + ex.ToString());
|
|
_log.Write("Note: You may try deleting the config file to fix this issue. However, you will lose DNS settings but, zone data wont be affected.");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private void CreateForwarderZoneToDisableDnssecForNTP()
|
|
{
|
|
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
|
{
|
|
//adding a conditional forwarder zone for disabling DNSSEC validation for ntp.org so that systems with no real-time clock can sync time
|
|
string ntpDomain = "ntp.org";
|
|
string fwdRecordComments = "This forwarder zone was automatically created to disable DNSSEC validation for ntp.org to allow systems with no real-time clock (e.g. Raspberry Pi) to sync time via NTP when booting.";
|
|
if (_dnsServer.AuthZoneManager.CreateForwarderZone(ntpDomain, DnsTransportProtocol.Udp, "this-server", false, NetProxyType.None, null, 0, null, null, fwdRecordComments) is not null)
|
|
{
|
|
//set permissions
|
|
_authManager.SetPermission(PermissionSection.Zones, ntpDomain, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_authManager.SetPermission(PermissionSection.Zones, ntpDomain, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_authManager.SaveConfigFile();
|
|
|
|
Directory.CreateDirectory(Path.Combine(_dnsServer.ConfigFolder, "zones"));
|
|
_dnsServer.AuthZoneManager.SaveZoneFile(ntpDomain);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void SaveConfigFile()
|
|
{
|
|
string configFile = Path.Combine(_configFolder, "dns.config");
|
|
|
|
using (MemoryStream mS = new MemoryStream())
|
|
{
|
|
//serialize config
|
|
WriteConfigTo(new BinaryWriter(mS));
|
|
|
|
//write config
|
|
mS.Position = 0;
|
|
|
|
using (FileStream fS = new FileStream(configFile, FileMode.Create, FileAccess.Write))
|
|
{
|
|
mS.CopyTo(fS);
|
|
}
|
|
}
|
|
|
|
_log.Write("DNS Server config file was saved: " + configFile);
|
|
}
|
|
|
|
internal void InspectAndFixZonePermissions()
|
|
{
|
|
Permission permission = _authManager.GetPermission(PermissionSection.Zones);
|
|
IReadOnlyDictionary<string, Permission> subItemPermissions = permission.SubItemPermissions;
|
|
|
|
//remove ghost permissions
|
|
foreach (KeyValuePair<string, Permission> subItemPermission in subItemPermissions)
|
|
{
|
|
string zoneName = subItemPermission.Key;
|
|
|
|
if (_dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName) is null)
|
|
permission.RemoveAllSubItemPermissions(zoneName); //no such zone exists; remove permissions
|
|
}
|
|
|
|
//add missing admin permissions
|
|
List<AuthZoneInfo> zones = _dnsServer.AuthZoneManager.ListZones();
|
|
Group admins = _authManager.GetGroup(Group.ADMINISTRATORS);
|
|
Group dnsAdmins = _authManager.GetGroup(Group.DNS_ADMINISTRATORS);
|
|
|
|
foreach (AuthZoneInfo zone in zones)
|
|
{
|
|
if (zone.Internal)
|
|
{
|
|
_authManager.SetPermission(PermissionSection.Zones, zone.Name, admins, PermissionFlag.View);
|
|
_authManager.SetPermission(PermissionSection.Zones, zone.Name, dnsAdmins, PermissionFlag.View);
|
|
}
|
|
else
|
|
{
|
|
_authManager.SetPermission(PermissionSection.Zones, zone.Name, admins, PermissionFlag.ViewModifyDelete);
|
|
_authManager.SetPermission(PermissionSection.Zones, zone.Name, dnsAdmins, PermissionFlag.ViewModifyDelete);
|
|
}
|
|
}
|
|
|
|
_authManager.SaveConfigFile();
|
|
}
|
|
|
|
private int ReadConfigFrom(BinaryReader bR)
|
|
{
|
|
if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DS") //format
|
|
throw new InvalidDataException("DNS Server config file format is invalid.");
|
|
|
|
int version = bR.ReadByte();
|
|
|
|
if ((version >= 28) && (version <= 29))
|
|
{
|
|
ReadConfigFrom(bR, version);
|
|
}
|
|
else if ((version >= 2) && (version <= 27))
|
|
{
|
|
ReadOldConfigFrom(bR, version);
|
|
|
|
//new default settings
|
|
_appsApi.EnableAutomaticUpdate = true;
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidDataException("DNS Server config version not supported.");
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
private void ReadConfigFrom(BinaryReader bR, int version)
|
|
{
|
|
//web service
|
|
{
|
|
_webServiceHttpPort = bR.ReadInt32();
|
|
_webServiceTlsPort = bR.ReadInt32();
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
IPAddress[] localAddresses = new IPAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
localAddresses[i] = IPAddressExtension.ReadFrom(bR);
|
|
|
|
_webServiceLocalAddresses = localAddresses;
|
|
}
|
|
}
|
|
|
|
_webServiceEnableTls = bR.ReadBoolean();
|
|
_webServiceHttpToTlsRedirect = bR.ReadBoolean();
|
|
_webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean();
|
|
|
|
_webServiceTlsCertificatePath = bR.ReadShortString();
|
|
_webServiceTlsCertificatePassword = bR.ReadShortString();
|
|
|
|
if (_webServiceTlsCertificatePath.Length == 0)
|
|
_webServiceTlsCertificatePath = null;
|
|
|
|
if (_webServiceTlsCertificatePath != null)
|
|
{
|
|
try
|
|
{
|
|
LoadWebServiceTlsCertificate(_webServiceTlsCertificatePath, _webServiceTlsCertificatePassword);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while loading Web Service TLS certificate: " + _webServiceTlsCertificatePath + "\r\n" + ex.ToString());
|
|
}
|
|
|
|
StartTlsCertificateUpdateTimer();
|
|
}
|
|
|
|
SelfSignedCertCheck(false, false);
|
|
}
|
|
|
|
//dns
|
|
{
|
|
//general
|
|
_dnsServer.ServerDomain = bR.ReadShortString();
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
IPEndPoint[] localEndPoints = new IPEndPoint[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
localEndPoints[i] = (IPEndPoint)EndPointExtension.ReadFrom(bR);
|
|
|
|
_dnsServer.LocalEndPoints = localEndPoints;
|
|
}
|
|
}
|
|
|
|
_zonesApi.DefaultRecordTtl = bR.ReadUInt32();
|
|
_appsApi.EnableAutomaticUpdate = bR.ReadBoolean();
|
|
|
|
_dnsServer.PreferIPv6 = bR.ReadBoolean();
|
|
_dnsServer.UdpPayloadSize = bR.ReadUInt16();
|
|
_dnsServer.DnssecValidation = bR.ReadBoolean();
|
|
|
|
if (version >= 29)
|
|
{
|
|
_dnsServer.EDnsClientSubnet = bR.ReadBoolean();
|
|
_dnsServer.EDnsClientSubnetIPv4PrefixLength = bR.ReadByte();
|
|
_dnsServer.EDnsClientSubnetIPv6PrefixLength = bR.ReadByte();
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.EDnsClientSubnet = false;
|
|
_dnsServer.EDnsClientSubnetIPv4PrefixLength = 24;
|
|
_dnsServer.EDnsClientSubnetIPv6PrefixLength = 56;
|
|
}
|
|
|
|
_dnsServer.QpmLimitRequests = bR.ReadInt32();
|
|
_dnsServer.QpmLimitErrors = bR.ReadInt32();
|
|
_dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
|
|
_dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32();
|
|
_dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32();
|
|
|
|
_dnsServer.ClientTimeout = bR.ReadInt32();
|
|
_dnsServer.TcpSendTimeout = bR.ReadInt32();
|
|
_dnsServer.TcpReceiveTimeout = bR.ReadInt32();
|
|
|
|
//optional protocols
|
|
_dnsServer.EnableDnsOverHttp = bR.ReadBoolean();
|
|
_dnsServer.EnableDnsOverTls = bR.ReadBoolean();
|
|
_dnsServer.EnableDnsOverHttps = bR.ReadBoolean();
|
|
|
|
_dnsTlsCertificatePath = bR.ReadShortString();
|
|
_dnsTlsCertificatePassword = bR.ReadShortString();
|
|
|
|
if (_dnsTlsCertificatePath.Length == 0)
|
|
_dnsTlsCertificatePath = null;
|
|
|
|
if (_dnsTlsCertificatePath != null)
|
|
{
|
|
try
|
|
{
|
|
LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString());
|
|
}
|
|
|
|
StartTlsCertificateUpdateTimer();
|
|
}
|
|
|
|
//tsig
|
|
{
|
|
int count = bR.ReadByte();
|
|
Dictionary<string, TsigKey> tsigKeys = new Dictionary<string, TsigKey>(count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string keyName = bR.ReadShortString();
|
|
string sharedSecret = bR.ReadShortString();
|
|
TsigAlgorithm algorithm = (TsigAlgorithm)bR.ReadByte();
|
|
|
|
tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, algorithm));
|
|
}
|
|
|
|
_dnsServer.TsigKeys = tsigKeys;
|
|
}
|
|
|
|
//recursion
|
|
_dnsServer.Recursion = (DnsServerRecursion)bR.ReadByte();
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
NetworkAddress[] networks = new NetworkAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
networks[i] = NetworkAddress.ReadFrom(bR);
|
|
|
|
_dnsServer.RecursionDeniedNetworks = networks;
|
|
}
|
|
}
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
NetworkAddress[] networks = new NetworkAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
networks[i] = NetworkAddress.ReadFrom(bR);
|
|
|
|
_dnsServer.RecursionAllowedNetworks = networks;
|
|
}
|
|
}
|
|
|
|
_dnsServer.RandomizeName = bR.ReadBoolean();
|
|
_dnsServer.QnameMinimization = bR.ReadBoolean();
|
|
_dnsServer.NsRevalidation = bR.ReadBoolean();
|
|
|
|
_dnsServer.ResolverRetries = bR.ReadInt32();
|
|
_dnsServer.ResolverTimeout = bR.ReadInt32();
|
|
_dnsServer.ResolverMaxStackCount = bR.ReadInt32();
|
|
|
|
//cache
|
|
_dnsServer.ServeStale = bR.ReadBoolean();
|
|
_dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32();
|
|
|
|
_dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt64();
|
|
_dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32();
|
|
_dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32();
|
|
_dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32();
|
|
_dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32();
|
|
|
|
_dnsServer.CachePrefetchEligibility = bR.ReadInt32();
|
|
_dnsServer.CachePrefetchTrigger = bR.ReadInt32();
|
|
_dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32();
|
|
_dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32();
|
|
|
|
//blocking
|
|
_dnsServer.EnableBlocking = bR.ReadBoolean();
|
|
_dnsServer.AllowTxtBlockingReport = bR.ReadBoolean();
|
|
|
|
_dnsServer.BlockingType = (DnsServerBlockingType)bR.ReadByte();
|
|
|
|
{
|
|
//read custom blocking addresses
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
List<DnsARecordData> dnsARecords = new List<DnsARecordData>();
|
|
List<DnsAAAARecordData> dnsAAAARecords = new List<DnsAAAARecordData>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
IPAddress customAddress = IPAddressExtension.ReadFrom(bR);
|
|
|
|
switch (customAddress.AddressFamily)
|
|
{
|
|
case AddressFamily.InterNetwork:
|
|
dnsARecords.Add(new DnsARecordData(customAddress));
|
|
break;
|
|
|
|
case AddressFamily.InterNetworkV6:
|
|
dnsAAAARecords.Add(new DnsAAAARecordData(customAddress));
|
|
break;
|
|
}
|
|
}
|
|
|
|
_dnsServer.CustomBlockingARecords = dnsARecords;
|
|
_dnsServer.CustomBlockingAAAARecords = dnsAAAARecords;
|
|
}
|
|
}
|
|
|
|
{
|
|
//read block list urls
|
|
int count = bR.ReadByte();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string listUrl = bR.ReadShortString();
|
|
|
|
if (listUrl.StartsWith("!"))
|
|
_dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1)));
|
|
else
|
|
_dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl));
|
|
}
|
|
|
|
_settingsApi.BlockListUpdateIntervalHours = bR.ReadInt32();
|
|
_settingsApi.BlockListLastUpdatedOn = bR.ReadDateTime();
|
|
}
|
|
|
|
//proxy & forwarders
|
|
NetProxyType proxyType = (NetProxyType)bR.ReadByte();
|
|
if (proxyType != NetProxyType.None)
|
|
{
|
|
string address = bR.ReadShortString();
|
|
int port = bR.ReadInt32();
|
|
NetworkCredential credential = null;
|
|
|
|
if (bR.ReadBoolean()) //credential set
|
|
credential = new NetworkCredential(bR.ReadShortString(), bR.ReadShortString());
|
|
|
|
_dnsServer.Proxy = NetProxy.CreateProxy(proxyType, address, port, credential);
|
|
|
|
int count = bR.ReadByte();
|
|
List<NetProxyBypassItem> bypassList = new List<NetProxyBypassItem>(count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
bypassList.Add(new NetProxyBypassItem(bR.ReadShortString()));
|
|
|
|
_dnsServer.Proxy.BypassList = bypassList;
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.Proxy = null;
|
|
}
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
NameServerAddress[] forwarders = new NameServerAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
forwarders[i] = new NameServerAddress(bR);
|
|
|
|
if (forwarders[i].Protocol == DnsTransportProtocol.HttpsJson)
|
|
forwarders[i] = forwarders[i].ChangeProtocol(DnsTransportProtocol.Https);
|
|
}
|
|
|
|
_dnsServer.Forwarders = forwarders;
|
|
}
|
|
}
|
|
|
|
_dnsServer.ForwarderRetries = bR.ReadInt32();
|
|
_dnsServer.ForwarderTimeout = bR.ReadInt32();
|
|
_dnsServer.ForwarderConcurrency = bR.ReadInt32();
|
|
|
|
//logging
|
|
if (bR.ReadBoolean()) //log all queries
|
|
_dnsServer.QueryLogManager = _log;
|
|
else
|
|
_dnsServer.QueryLogManager = null;
|
|
|
|
_dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32();
|
|
}
|
|
|
|
if ((_webServiceTlsCertificatePath == null) && (_dnsTlsCertificatePath == null))
|
|
StopTlsCertificateUpdateTimer();
|
|
}
|
|
|
|
private void ReadOldConfigFrom(BinaryReader bR, int version)
|
|
{
|
|
_dnsServer.ServerDomain = bR.ReadShortString();
|
|
_webServiceHttpPort = bR.ReadInt32();
|
|
|
|
if (version >= 13)
|
|
{
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
IPAddress[] localAddresses = new IPAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
localAddresses[i] = IPAddressExtension.ReadFrom(bR);
|
|
|
|
_webServiceLocalAddresses = localAddresses;
|
|
}
|
|
}
|
|
|
|
_webServiceTlsPort = bR.ReadInt32();
|
|
_webServiceEnableTls = bR.ReadBoolean();
|
|
_webServiceHttpToTlsRedirect = bR.ReadBoolean();
|
|
_webServiceTlsCertificatePath = bR.ReadShortString();
|
|
_webServiceTlsCertificatePassword = bR.ReadShortString();
|
|
|
|
if (_webServiceTlsCertificatePath.Length == 0)
|
|
_webServiceTlsCertificatePath = null;
|
|
|
|
if (_webServiceTlsCertificatePath != null)
|
|
{
|
|
try
|
|
{
|
|
LoadWebServiceTlsCertificate(_webServiceTlsCertificatePath, _webServiceTlsCertificatePassword);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while loading Web Service TLS certificate: " + _webServiceTlsCertificatePath + "\r\n" + ex.ToString());
|
|
}
|
|
|
|
StartTlsCertificateUpdateTimer();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any };
|
|
|
|
_webServiceTlsPort = 53443;
|
|
_webServiceEnableTls = false;
|
|
_webServiceHttpToTlsRedirect = false;
|
|
_webServiceTlsCertificatePath = string.Empty;
|
|
_webServiceTlsCertificatePassword = string.Empty;
|
|
}
|
|
|
|
_dnsServer.PreferIPv6 = bR.ReadBoolean();
|
|
|
|
if (bR.ReadBoolean()) //logQueries
|
|
_dnsServer.QueryLogManager = _log;
|
|
|
|
if (version >= 14)
|
|
_dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32();
|
|
else
|
|
_dnsServer.StatsManager.MaxStatFileDays = 0;
|
|
|
|
if (version >= 17)
|
|
{
|
|
_dnsServer.Recursion = (DnsServerRecursion)bR.ReadByte();
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
NetworkAddress[] networks = new NetworkAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
networks[i] = NetworkAddress.ReadFrom(bR);
|
|
|
|
_dnsServer.RecursionDeniedNetworks = networks;
|
|
}
|
|
}
|
|
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
NetworkAddress[] networks = new NetworkAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
networks[i] = NetworkAddress.ReadFrom(bR);
|
|
|
|
_dnsServer.RecursionAllowedNetworks = networks;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool allowRecursion = bR.ReadBoolean();
|
|
bool allowRecursionOnlyForPrivateNetworks;
|
|
|
|
if (version >= 4)
|
|
allowRecursionOnlyForPrivateNetworks = bR.ReadBoolean();
|
|
else
|
|
allowRecursionOnlyForPrivateNetworks = true; //default true for security reasons
|
|
|
|
if (allowRecursion)
|
|
{
|
|
if (allowRecursionOnlyForPrivateNetworks)
|
|
_dnsServer.Recursion = DnsServerRecursion.AllowOnlyForPrivateNetworks;
|
|
else
|
|
_dnsServer.Recursion = DnsServerRecursion.Allow;
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.Recursion = DnsServerRecursion.Deny;
|
|
}
|
|
}
|
|
|
|
if (version >= 12)
|
|
_dnsServer.RandomizeName = bR.ReadBoolean();
|
|
else
|
|
_dnsServer.RandomizeName = true; //default true to enable security feature
|
|
|
|
if (version >= 15)
|
|
_dnsServer.QnameMinimization = bR.ReadBoolean();
|
|
else
|
|
_dnsServer.QnameMinimization = true; //default true to enable privacy feature
|
|
|
|
if (version >= 20)
|
|
{
|
|
_dnsServer.QpmLimitRequests = bR.ReadInt32();
|
|
_dnsServer.QpmLimitErrors = bR.ReadInt32();
|
|
_dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
|
|
_dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32();
|
|
_dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32();
|
|
}
|
|
else if (version >= 17)
|
|
{
|
|
_dnsServer.QpmLimitRequests = bR.ReadInt32();
|
|
_dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
|
|
_ = bR.ReadInt32(); //read obsolete value _dnsServer.QpmLimitSamplingIntervalInMinutes
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.QpmLimitRequests = 0;
|
|
_dnsServer.QpmLimitErrors = 0;
|
|
_dnsServer.QpmLimitSampleMinutes = 1;
|
|
_dnsServer.QpmLimitIPv4PrefixLength = 24;
|
|
_dnsServer.QpmLimitIPv6PrefixLength = 56;
|
|
}
|
|
|
|
if (version >= 13)
|
|
{
|
|
_dnsServer.ServeStale = bR.ReadBoolean();
|
|
_dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32();
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.ServeStale = true;
|
|
_dnsServer.CacheZoneManager.ServeStaleTtl = CacheZoneManager.SERVE_STALE_TTL;
|
|
}
|
|
|
|
if (version >= 9)
|
|
{
|
|
_dnsServer.CachePrefetchEligibility = bR.ReadInt32();
|
|
_dnsServer.CachePrefetchTrigger = bR.ReadInt32();
|
|
_dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32();
|
|
_dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32();
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.CachePrefetchEligibility = 2;
|
|
_dnsServer.CachePrefetchTrigger = 9;
|
|
_dnsServer.CachePrefetchSampleIntervalInMinutes = 5;
|
|
_dnsServer.CachePrefetchSampleEligibilityHitsPerHour = 30;
|
|
}
|
|
|
|
NetProxyType proxyType = (NetProxyType)bR.ReadByte();
|
|
if (proxyType != NetProxyType.None)
|
|
{
|
|
string address = bR.ReadShortString();
|
|
int port = bR.ReadInt32();
|
|
NetworkCredential credential = null;
|
|
|
|
if (bR.ReadBoolean()) //credential set
|
|
credential = new NetworkCredential(bR.ReadShortString(), bR.ReadShortString());
|
|
|
|
_dnsServer.Proxy = NetProxy.CreateProxy(proxyType, address, port, credential);
|
|
|
|
if (version >= 10)
|
|
{
|
|
int count = bR.ReadByte();
|
|
List<NetProxyBypassItem> bypassList = new List<NetProxyBypassItem>(count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
bypassList.Add(new NetProxyBypassItem(bR.ReadShortString()));
|
|
|
|
_dnsServer.Proxy.BypassList = bypassList;
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.Proxy.BypassList = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.Proxy = null;
|
|
}
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
NameServerAddress[] forwarders = new NameServerAddress[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
forwarders[i] = new NameServerAddress(bR);
|
|
if (forwarders[i].Protocol == DnsTransportProtocol.HttpsJson)
|
|
forwarders[i] = forwarders[i].ChangeProtocol(DnsTransportProtocol.Https);
|
|
}
|
|
|
|
_dnsServer.Forwarders = forwarders;
|
|
}
|
|
}
|
|
|
|
if (version <= 10)
|
|
{
|
|
DnsTransportProtocol forwarderProtocol = (DnsTransportProtocol)bR.ReadByte();
|
|
if (forwarderProtocol == DnsTransportProtocol.HttpsJson)
|
|
forwarderProtocol = DnsTransportProtocol.Https;
|
|
|
|
if (_dnsServer.Forwarders != null)
|
|
{
|
|
List<NameServerAddress> forwarders = new List<NameServerAddress>();
|
|
|
|
foreach (NameServerAddress forwarder in _dnsServer.Forwarders)
|
|
{
|
|
if (forwarder.Protocol == forwarderProtocol)
|
|
forwarders.Add(forwarder);
|
|
else
|
|
forwarders.Add(forwarder.ChangeProtocol(forwarderProtocol));
|
|
}
|
|
|
|
_dnsServer.Forwarders = forwarders;
|
|
}
|
|
}
|
|
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
if (version > 2)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string username = bR.ReadShortString();
|
|
string passwordHash = bR.ReadShortString();
|
|
|
|
if (username.Equals("admin", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_authManager.LoadOldConfig(passwordHash, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string username = bR.ReadShortString();
|
|
string password = bR.ReadShortString();
|
|
|
|
if (username.Equals("admin", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_authManager.LoadOldConfig(password, false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (version <= 6)
|
|
{
|
|
int count = bR.ReadInt32();
|
|
_configDisabledZones = new List<string>(count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string domain = bR.ReadShortString();
|
|
_configDisabledZones.Add(domain);
|
|
}
|
|
}
|
|
|
|
if (version >= 18)
|
|
_dnsServer.EnableBlocking = bR.ReadBoolean();
|
|
else
|
|
_dnsServer.EnableBlocking = true;
|
|
|
|
if (version >= 18)
|
|
_dnsServer.BlockingType = (DnsServerBlockingType)bR.ReadByte();
|
|
else if (version >= 16)
|
|
_dnsServer.BlockingType = bR.ReadBoolean() ? DnsServerBlockingType.NxDomain : DnsServerBlockingType.AnyAddress;
|
|
else
|
|
_dnsServer.BlockingType = DnsServerBlockingType.AnyAddress;
|
|
|
|
if (version >= 18)
|
|
{
|
|
//read custom blocking addresses
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
List<DnsARecordData> dnsARecords = new List<DnsARecordData>();
|
|
List<DnsAAAARecordData> dnsAAAARecords = new List<DnsAAAARecordData>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
IPAddress customAddress = IPAddressExtension.ReadFrom(bR);
|
|
|
|
switch (customAddress.AddressFamily)
|
|
{
|
|
case AddressFamily.InterNetwork:
|
|
dnsARecords.Add(new DnsARecordData(customAddress));
|
|
break;
|
|
|
|
case AddressFamily.InterNetworkV6:
|
|
dnsAAAARecords.Add(new DnsAAAARecordData(customAddress));
|
|
break;
|
|
}
|
|
}
|
|
|
|
_dnsServer.CustomBlockingARecords = dnsARecords;
|
|
_dnsServer.CustomBlockingAAAARecords = dnsAAAARecords;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.CustomBlockingARecords = null;
|
|
_dnsServer.CustomBlockingAAAARecords = null;
|
|
}
|
|
|
|
if (version > 4)
|
|
{
|
|
//read block list urls
|
|
int count = bR.ReadByte();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string listUrl = bR.ReadShortString();
|
|
|
|
if (listUrl.StartsWith("!"))
|
|
_dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1)));
|
|
else
|
|
_dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl));
|
|
}
|
|
|
|
_settingsApi.BlockListLastUpdatedOn = bR.ReadDateTime();
|
|
|
|
if (version >= 13)
|
|
_settingsApi.BlockListUpdateIntervalHours = bR.ReadInt32();
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.BlockListZoneManager.AllowListUrls.Clear();
|
|
_dnsServer.BlockListZoneManager.BlockListUrls.Clear();
|
|
_settingsApi.BlockListLastUpdatedOn = DateTime.MinValue;
|
|
_settingsApi.BlockListUpdateIntervalHours = 24;
|
|
}
|
|
|
|
if (version >= 11)
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
IPEndPoint[] localEndPoints = new IPEndPoint[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
localEndPoints[i] = (IPEndPoint)EndPointExtension.ReadFrom(bR);
|
|
|
|
_dnsServer.LocalEndPoints = localEndPoints;
|
|
}
|
|
}
|
|
else if (version >= 6)
|
|
{
|
|
int count = bR.ReadByte();
|
|
if (count > 0)
|
|
{
|
|
IPEndPoint[] localEndPoints = new IPEndPoint[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
localEndPoints[i] = new IPEndPoint(IPAddressExtension.ReadFrom(bR), 53);
|
|
|
|
_dnsServer.LocalEndPoints = localEndPoints;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) };
|
|
}
|
|
|
|
if (version >= 8)
|
|
{
|
|
_dnsServer.EnableDnsOverHttp = bR.ReadBoolean();
|
|
_dnsServer.EnableDnsOverTls = bR.ReadBoolean();
|
|
_dnsServer.EnableDnsOverHttps = bR.ReadBoolean();
|
|
_dnsTlsCertificatePath = bR.ReadShortString();
|
|
_dnsTlsCertificatePassword = bR.ReadShortString();
|
|
|
|
if (_dnsTlsCertificatePath.Length == 0)
|
|
_dnsTlsCertificatePath = null;
|
|
|
|
if (_dnsTlsCertificatePath != null)
|
|
{
|
|
try
|
|
{
|
|
LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString());
|
|
}
|
|
|
|
StartTlsCertificateUpdateTimer();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.EnableDnsOverHttp = false;
|
|
_dnsServer.EnableDnsOverTls = false;
|
|
_dnsServer.EnableDnsOverHttps = false;
|
|
_dnsTlsCertificatePath = string.Empty;
|
|
_dnsTlsCertificatePassword = string.Empty;
|
|
}
|
|
|
|
if (version >= 19)
|
|
{
|
|
_dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32();
|
|
_dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32();
|
|
_dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32();
|
|
_dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32();
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.CacheZoneManager.MinimumRecordTtl = CacheZoneManager.MINIMUM_RECORD_TTL;
|
|
_dnsServer.CacheZoneManager.MaximumRecordTtl = CacheZoneManager.MAXIMUM_RECORD_TTL;
|
|
_dnsServer.CacheZoneManager.NegativeRecordTtl = CacheZoneManager.NEGATIVE_RECORD_TTL;
|
|
_dnsServer.CacheZoneManager.FailureRecordTtl = CacheZoneManager.FAILURE_RECORD_TTL;
|
|
}
|
|
|
|
if (version >= 21)
|
|
{
|
|
int count = bR.ReadByte();
|
|
Dictionary<string, TsigKey> tsigKeys = new Dictionary<string, TsigKey>(count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string keyName = bR.ReadShortString();
|
|
string sharedSecret = bR.ReadShortString();
|
|
TsigAlgorithm algorithm = (TsigAlgorithm)bR.ReadByte();
|
|
|
|
tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, algorithm));
|
|
}
|
|
|
|
_dnsServer.TsigKeys = tsigKeys;
|
|
}
|
|
else if (version >= 20)
|
|
{
|
|
int count = bR.ReadByte();
|
|
Dictionary<string, TsigKey> tsigKeys = new Dictionary<string, TsigKey>(count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string keyName = bR.ReadShortString();
|
|
string sharedSecret = bR.ReadShortString();
|
|
|
|
tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, TsigAlgorithm.HMAC_SHA256));
|
|
}
|
|
|
|
_dnsServer.TsigKeys = tsigKeys;
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.TsigKeys = null;
|
|
}
|
|
|
|
if (version >= 22)
|
|
_dnsServer.NsRevalidation = bR.ReadBoolean();
|
|
else
|
|
_dnsServer.NsRevalidation = true; //default true for security reasons
|
|
|
|
if (version >= 23)
|
|
{
|
|
_dnsServer.AllowTxtBlockingReport = bR.ReadBoolean();
|
|
_zonesApi.DefaultRecordTtl = bR.ReadUInt32();
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.AllowTxtBlockingReport = true;
|
|
_zonesApi.DefaultRecordTtl = 3600;
|
|
}
|
|
|
|
if (version >= 24)
|
|
{
|
|
_webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean();
|
|
|
|
SelfSignedCertCheck(false, false);
|
|
}
|
|
else
|
|
{
|
|
_webServiceUseSelfSignedTlsCertificate = false;
|
|
}
|
|
|
|
if (version >= 25)
|
|
_dnsServer.UdpPayloadSize = bR.ReadUInt16();
|
|
else
|
|
_dnsServer.UdpPayloadSize = DnsDatagram.EDNS_DEFAULT_UDP_PAYLOAD_SIZE;
|
|
|
|
if (version >= 26)
|
|
{
|
|
_dnsServer.DnssecValidation = bR.ReadBoolean();
|
|
|
|
_dnsServer.ResolverRetries = bR.ReadInt32();
|
|
_dnsServer.ResolverTimeout = bR.ReadInt32();
|
|
_dnsServer.ResolverMaxStackCount = bR.ReadInt32();
|
|
|
|
_dnsServer.ForwarderRetries = bR.ReadInt32();
|
|
_dnsServer.ForwarderTimeout = bR.ReadInt32();
|
|
_dnsServer.ForwarderConcurrency = bR.ReadInt32();
|
|
|
|
_dnsServer.ClientTimeout = bR.ReadInt32();
|
|
_dnsServer.TcpSendTimeout = bR.ReadInt32();
|
|
_dnsServer.TcpReceiveTimeout = bR.ReadInt32();
|
|
}
|
|
else
|
|
{
|
|
_dnsServer.DnssecValidation = true;
|
|
CreateForwarderZoneToDisableDnssecForNTP();
|
|
|
|
_dnsServer.ResolverRetries = 2;
|
|
_dnsServer.ResolverTimeout = 2000;
|
|
_dnsServer.ResolverMaxStackCount = 16;
|
|
|
|
_dnsServer.ForwarderRetries = 3;
|
|
_dnsServer.ForwarderTimeout = 2000;
|
|
_dnsServer.ForwarderConcurrency = 2;
|
|
|
|
_dnsServer.ClientTimeout = 4000;
|
|
_dnsServer.TcpSendTimeout = 10000;
|
|
_dnsServer.TcpReceiveTimeout = 10000;
|
|
}
|
|
|
|
if (version >= 27)
|
|
_dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt32();
|
|
else
|
|
_dnsServer.CacheZoneManager.MaximumEntries = 10000;
|
|
}
|
|
|
|
private void WriteConfigTo(BinaryWriter bW)
|
|
{
|
|
bW.Write(Encoding.ASCII.GetBytes("DS")); //format
|
|
bW.Write((byte)29); //version
|
|
|
|
//web service
|
|
{
|
|
bW.Write(_webServiceHttpPort);
|
|
bW.Write(_webServiceTlsPort);
|
|
|
|
{
|
|
bW.Write(Convert.ToByte(_webServiceLocalAddresses.Count));
|
|
|
|
foreach (IPAddress localAddress in _webServiceLocalAddresses)
|
|
localAddress.WriteTo(bW);
|
|
}
|
|
|
|
bW.Write(_webServiceEnableTls);
|
|
bW.Write(_webServiceHttpToTlsRedirect);
|
|
bW.Write(_webServiceUseSelfSignedTlsCertificate);
|
|
|
|
if (_webServiceTlsCertificatePath is null)
|
|
bW.WriteShortString(string.Empty);
|
|
else
|
|
bW.WriteShortString(_webServiceTlsCertificatePath);
|
|
|
|
if (_webServiceTlsCertificatePassword is null)
|
|
bW.WriteShortString(string.Empty);
|
|
else
|
|
bW.WriteShortString(_webServiceTlsCertificatePassword);
|
|
}
|
|
|
|
//dns
|
|
{
|
|
//general
|
|
bW.WriteShortString(_dnsServer.ServerDomain);
|
|
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.LocalEndPoints.Count));
|
|
|
|
foreach (IPEndPoint localEP in _dnsServer.LocalEndPoints)
|
|
localEP.WriteTo(bW);
|
|
}
|
|
|
|
bW.Write(_zonesApi.DefaultRecordTtl);
|
|
bW.Write(_appsApi.EnableAutomaticUpdate);
|
|
|
|
bW.Write(_dnsServer.PreferIPv6);
|
|
bW.Write(_dnsServer.UdpPayloadSize);
|
|
bW.Write(_dnsServer.DnssecValidation);
|
|
|
|
bW.Write(_dnsServer.EDnsClientSubnet);
|
|
bW.Write(_dnsServer.EDnsClientSubnetIPv4PrefixLength);
|
|
bW.Write(_dnsServer.EDnsClientSubnetIPv6PrefixLength);
|
|
|
|
bW.Write(_dnsServer.QpmLimitRequests);
|
|
bW.Write(_dnsServer.QpmLimitErrors);
|
|
bW.Write(_dnsServer.QpmLimitSampleMinutes);
|
|
bW.Write(_dnsServer.QpmLimitIPv4PrefixLength);
|
|
bW.Write(_dnsServer.QpmLimitIPv6PrefixLength);
|
|
|
|
bW.Write(_dnsServer.ClientTimeout);
|
|
bW.Write(_dnsServer.TcpSendTimeout);
|
|
bW.Write(_dnsServer.TcpReceiveTimeout);
|
|
|
|
//optional protocols
|
|
bW.Write(_dnsServer.EnableDnsOverHttp);
|
|
bW.Write(_dnsServer.EnableDnsOverTls);
|
|
bW.Write(_dnsServer.EnableDnsOverHttps);
|
|
|
|
if (_dnsTlsCertificatePath == null)
|
|
bW.WriteShortString(string.Empty);
|
|
else
|
|
bW.WriteShortString(_dnsTlsCertificatePath);
|
|
|
|
if (_dnsTlsCertificatePassword == null)
|
|
bW.WriteShortString(string.Empty);
|
|
else
|
|
bW.WriteShortString(_dnsTlsCertificatePassword);
|
|
|
|
//tsig
|
|
if (_dnsServer.TsigKeys is null)
|
|
{
|
|
bW.Write((byte)0);
|
|
}
|
|
else
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.TsigKeys.Count));
|
|
|
|
foreach (KeyValuePair<string, TsigKey> tsigKey in _dnsServer.TsigKeys)
|
|
{
|
|
bW.WriteShortString(tsigKey.Key);
|
|
bW.WriteShortString(tsigKey.Value.SharedSecret);
|
|
bW.Write((byte)tsigKey.Value.Algorithm);
|
|
}
|
|
}
|
|
|
|
//recursion
|
|
bW.Write((byte)_dnsServer.Recursion);
|
|
|
|
if (_dnsServer.RecursionDeniedNetworks is null)
|
|
{
|
|
bW.Write((byte)0);
|
|
}
|
|
else
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.RecursionDeniedNetworks.Count));
|
|
foreach (NetworkAddress networkAddress in _dnsServer.RecursionDeniedNetworks)
|
|
networkAddress.WriteTo(bW);
|
|
}
|
|
|
|
if (_dnsServer.RecursionAllowedNetworks is null)
|
|
{
|
|
bW.Write((byte)0);
|
|
}
|
|
else
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.RecursionAllowedNetworks.Count));
|
|
foreach (NetworkAddress networkAddress in _dnsServer.RecursionAllowedNetworks)
|
|
networkAddress.WriteTo(bW);
|
|
}
|
|
|
|
bW.Write(_dnsServer.RandomizeName);
|
|
bW.Write(_dnsServer.QnameMinimization);
|
|
bW.Write(_dnsServer.NsRevalidation);
|
|
|
|
bW.Write(_dnsServer.ResolverRetries);
|
|
bW.Write(_dnsServer.ResolverTimeout);
|
|
bW.Write(_dnsServer.ResolverMaxStackCount);
|
|
|
|
//cache
|
|
bW.Write(_dnsServer.ServeStale);
|
|
bW.Write(_dnsServer.CacheZoneManager.ServeStaleTtl);
|
|
|
|
bW.Write(_dnsServer.CacheZoneManager.MaximumEntries);
|
|
bW.Write(_dnsServer.CacheZoneManager.MinimumRecordTtl);
|
|
bW.Write(_dnsServer.CacheZoneManager.MaximumRecordTtl);
|
|
bW.Write(_dnsServer.CacheZoneManager.NegativeRecordTtl);
|
|
bW.Write(_dnsServer.CacheZoneManager.FailureRecordTtl);
|
|
|
|
bW.Write(_dnsServer.CachePrefetchEligibility);
|
|
bW.Write(_dnsServer.CachePrefetchTrigger);
|
|
bW.Write(_dnsServer.CachePrefetchSampleIntervalInMinutes);
|
|
bW.Write(_dnsServer.CachePrefetchSampleEligibilityHitsPerHour);
|
|
|
|
//blocking
|
|
bW.Write(_dnsServer.EnableBlocking);
|
|
bW.Write(_dnsServer.AllowTxtBlockingReport);
|
|
|
|
bW.Write((byte)_dnsServer.BlockingType);
|
|
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.CustomBlockingARecords.Count + _dnsServer.CustomBlockingAAAARecords.Count));
|
|
|
|
foreach (DnsARecordData record in _dnsServer.CustomBlockingARecords)
|
|
record.Address.WriteTo(bW);
|
|
|
|
foreach (DnsAAAARecordData record in _dnsServer.CustomBlockingAAAARecords)
|
|
record.Address.WriteTo(bW);
|
|
}
|
|
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count));
|
|
|
|
foreach (Uri allowListUrl in _dnsServer.BlockListZoneManager.AllowListUrls)
|
|
bW.WriteShortString("!" + allowListUrl.AbsoluteUri);
|
|
|
|
foreach (Uri blockListUrl in _dnsServer.BlockListZoneManager.BlockListUrls)
|
|
bW.WriteShortString(blockListUrl.AbsoluteUri);
|
|
|
|
bW.Write(_settingsApi.BlockListUpdateIntervalHours);
|
|
bW.Write(_settingsApi.BlockListLastUpdatedOn);
|
|
}
|
|
|
|
//proxy & forwarders
|
|
if (_dnsServer.Proxy == null)
|
|
{
|
|
bW.Write((byte)NetProxyType.None);
|
|
}
|
|
else
|
|
{
|
|
bW.Write((byte)_dnsServer.Proxy.Type);
|
|
bW.WriteShortString(_dnsServer.Proxy.Address);
|
|
bW.Write(_dnsServer.Proxy.Port);
|
|
|
|
NetworkCredential credential = _dnsServer.Proxy.Credential;
|
|
|
|
if (credential == null)
|
|
{
|
|
bW.Write(false);
|
|
}
|
|
else
|
|
{
|
|
bW.Write(true);
|
|
bW.WriteShortString(credential.UserName);
|
|
bW.WriteShortString(credential.Password);
|
|
}
|
|
|
|
//bypass list
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.Proxy.BypassList.Count));
|
|
|
|
foreach (NetProxyBypassItem item in _dnsServer.Proxy.BypassList)
|
|
bW.WriteShortString(item.Value);
|
|
}
|
|
}
|
|
|
|
if (_dnsServer.Forwarders == null)
|
|
{
|
|
bW.Write((byte)0);
|
|
}
|
|
else
|
|
{
|
|
bW.Write(Convert.ToByte(_dnsServer.Forwarders.Count));
|
|
|
|
foreach (NameServerAddress forwarder in _dnsServer.Forwarders)
|
|
forwarder.WriteTo(bW);
|
|
}
|
|
|
|
bW.Write(_dnsServer.ForwarderRetries);
|
|
bW.Write(_dnsServer.ForwarderTimeout);
|
|
bW.Write(_dnsServer.ForwarderConcurrency);
|
|
|
|
//logging
|
|
bW.Write(_dnsServer.QueryLogManager is not null); //log all queries
|
|
bW.Write(_dnsServer.StatsManager.MaxStatFileDays);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region web service start stop
|
|
|
|
internal void StartDnsWebService()
|
|
{
|
|
int acceptTasks = Math.Max(1, Environment.ProcessorCount);
|
|
|
|
//HTTP service
|
|
try
|
|
{
|
|
string webServiceHostname = null;
|
|
|
|
_webService = new HttpListener();
|
|
IPAddress httpAddress = null;
|
|
|
|
foreach (IPAddress webServiceLocalAddress in _webServiceLocalAddresses)
|
|
{
|
|
string host;
|
|
|
|
if (webServiceLocalAddress.Equals(IPAddress.Any))
|
|
{
|
|
host = "+";
|
|
|
|
httpAddress = IPAddress.Loopback;
|
|
}
|
|
else if (webServiceLocalAddress.Equals(IPAddress.IPv6Any))
|
|
{
|
|
host = "+";
|
|
|
|
if ((httpAddress == null) || !IPAddress.IsLoopback(httpAddress))
|
|
httpAddress = IPAddress.IPv6Loopback;
|
|
}
|
|
else
|
|
{
|
|
if (webServiceLocalAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
|
host = "[" + webServiceLocalAddress.ToString() + "]";
|
|
else
|
|
host = webServiceLocalAddress.ToString();
|
|
|
|
if (httpAddress == null)
|
|
httpAddress = webServiceLocalAddress;
|
|
|
|
if (webServiceHostname == null)
|
|
webServiceHostname = host;
|
|
}
|
|
|
|
_webService.Prefixes.Add("http://" + host + ":" + _webServiceHttpPort + "/");
|
|
}
|
|
|
|
_webService.Start();
|
|
|
|
if (httpAddress == null)
|
|
httpAddress = IPAddress.Loopback;
|
|
|
|
_webServiceHttpEP = new IPEndPoint(httpAddress, _webServiceHttpPort);
|
|
|
|
_webServiceHostname = webServiceHostname ?? Environment.MachineName.ToLower();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("Web Service failed to bind using default hostname. Attempting to bind again using 'localhost' hostname.\r\n" + ex.ToString());
|
|
|
|
try
|
|
{
|
|
_webService = new HttpListener();
|
|
_webService.Prefixes.Add("http://localhost:" + _webServiceHttpPort + "/");
|
|
_webService.Prefixes.Add("http://127.0.0.1:" + _webServiceHttpPort + "/");
|
|
_webService.Start();
|
|
}
|
|
catch
|
|
{
|
|
_webService = new HttpListener();
|
|
_webService.Prefixes.Add("http://localhost:" + _webServiceHttpPort + "/");
|
|
_webService.Start();
|
|
}
|
|
|
|
_webServiceHttpEP = new IPEndPoint(IPAddress.Loopback, _webServiceHttpPort);
|
|
|
|
_webServiceHostname = "localhost";
|
|
}
|
|
|
|
_webService.IgnoreWriteExceptions = true;
|
|
|
|
for (int i = 0; i < acceptTasks; i++)
|
|
{
|
|
_ = Task.Factory.StartNew(delegate ()
|
|
{
|
|
return AcceptWebRequestAsync();
|
|
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, _webServiceTaskScheduler);
|
|
}
|
|
|
|
_log.Write(new IPEndPoint(IPAddress.Any, _webServiceHttpPort), "HTTP Web Service was started successfully.");
|
|
|
|
//TLS service
|
|
if (_webServiceEnableTls && (_webServiceTlsCertificate != null))
|
|
{
|
|
List<Socket> webServiceTlsListeners = new List<Socket>();
|
|
|
|
try
|
|
{
|
|
foreach (IPAddress webServiceLocalAddress in _webServiceLocalAddresses)
|
|
{
|
|
Socket tlsListener = new Socket(webServiceLocalAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
|
|
tlsListener.Bind(new IPEndPoint(webServiceLocalAddress, _webServiceTlsPort));
|
|
tlsListener.Listen(10);
|
|
|
|
webServiceTlsListeners.Add(tlsListener);
|
|
}
|
|
|
|
foreach (Socket tlsListener in webServiceTlsListeners)
|
|
{
|
|
for (int i = 0; i < acceptTasks; i++)
|
|
{
|
|
_ = Task.Factory.StartNew(delegate ()
|
|
{
|
|
return AcceptTlsWebRequestAsync(tlsListener);
|
|
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, _webServiceTaskScheduler);
|
|
}
|
|
}
|
|
|
|
_webServiceTlsListeners = webServiceTlsListeners;
|
|
|
|
_log.Write(new IPEndPoint(IPAddress.Any, _webServiceHttpPort), "TLS Web Service was started successfully.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("TLS Web Service failed to start.\r\n" + ex.ToString());
|
|
|
|
foreach (Socket tlsListener in webServiceTlsListeners)
|
|
tlsListener.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void StopDnsWebService()
|
|
{
|
|
_webService.Stop();
|
|
|
|
if (_webServiceTlsListeners != null)
|
|
{
|
|
foreach (Socket tlsListener in _webServiceTlsListeners)
|
|
tlsListener.Dispose();
|
|
|
|
_webServiceTlsListeners = null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region public
|
|
|
|
public void Start()
|
|
{
|
|
if (_disposed)
|
|
throw new ObjectDisposedException(nameof(DnsWebService));
|
|
|
|
if (_state != ServiceState.Stopped)
|
|
throw new InvalidOperationException("Web Service is already running.");
|
|
|
|
_state = ServiceState.Starting;
|
|
|
|
try
|
|
{
|
|
//get initial server domain
|
|
string dnsServerDomain = Environment.MachineName.ToLower();
|
|
if (!DnsClient.IsDomainNameValid(dnsServerDomain))
|
|
dnsServerDomain = "dns-server-1"; //use this name instead since machine name is not a valid domain name
|
|
|
|
//init dns server
|
|
_dnsServer = new DnsServer(dnsServerDomain, _configFolder, Path.Combine(_appFolder, "dohwww"), _log);
|
|
|
|
//init dhcp server
|
|
_dhcpServer = new DhcpServer(Path.Combine(_configFolder, "scopes"), _log);
|
|
_dhcpServer.DnsServer = _dnsServer;
|
|
_dhcpServer.AuthManager = _authManager;
|
|
|
|
//load auth config
|
|
_authManager.LoadConfigFile();
|
|
|
|
//load config
|
|
LoadConfigFile();
|
|
|
|
//load all dns applications
|
|
_dnsServer.DnsApplicationManager.LoadAllApplications();
|
|
|
|
//load all zones files
|
|
_dnsServer.AuthZoneManager.LoadAllZoneFiles();
|
|
InspectAndFixZonePermissions();
|
|
|
|
//disable zones from old config format
|
|
if (_configDisabledZones != null)
|
|
{
|
|
foreach (string domain in _configDisabledZones)
|
|
{
|
|
AuthZoneInfo zoneInfo = _dnsServer.AuthZoneManager.GetAuthZoneInfo(domain);
|
|
if (zoneInfo is not null)
|
|
{
|
|
zoneInfo.Disabled = true;
|
|
_dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
//load allowed zone and blocked zone
|
|
_dnsServer.AllowedZoneManager.LoadAllowedZoneFile();
|
|
_dnsServer.BlockedZoneManager.LoadBlockedZoneFile();
|
|
|
|
//load block list zone async
|
|
if ((_settingsApi.BlockListUpdateIntervalHours > 0) && (_dnsServer.BlockListZoneManager.BlockListUrls.Count > 0))
|
|
{
|
|
ThreadPool.QueueUserWorkItem(delegate (object state)
|
|
{
|
|
try
|
|
{
|
|
_dnsServer.BlockListZoneManager.LoadBlockLists();
|
|
_settingsApi.StartBlockListUpdateTimer();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write(ex);
|
|
}
|
|
});
|
|
}
|
|
|
|
//start dns and dhcp
|
|
_dnsServer.Start();
|
|
_dhcpServer.Start();
|
|
|
|
//start web service
|
|
StartDnsWebService();
|
|
|
|
_state = ServiceState.Running;
|
|
|
|
_log.Write("DNS Server (v" + _currentVersion.ToString() + ") was started successfully.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("Failed to start DNS Server (v" + _currentVersion.ToString() + ")\r\n" + ex.ToString());
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (_state != ServiceState.Running)
|
|
return;
|
|
|
|
_state = ServiceState.Stopping;
|
|
|
|
try
|
|
{
|
|
StopDnsWebService();
|
|
_dnsServer.Dispose();
|
|
_dhcpServer.Dispose();
|
|
|
|
_settingsApi.StopBlockListUpdateTimer();
|
|
_settingsApi.StopTemporaryDisableBlockingTimer();
|
|
StopTlsCertificateUpdateTimer();
|
|
|
|
_state = ServiceState.Stopped;
|
|
|
|
_log.Write("DNS Server (v" + _currentVersion.ToString() + ") was stopped successfully.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.Write("Failed to stop DNS Server (v" + _currentVersion.ToString() + ")\r\n" + ex.ToString());
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region properties
|
|
|
|
public string ConfigFolder
|
|
{ get { return _configFolder; } }
|
|
|
|
public int WebServiceHttpPort
|
|
{ get { return _webServiceHttpPort; } }
|
|
|
|
public int WebServiceTlsPort
|
|
{ get { return _webServiceTlsPort; } }
|
|
|
|
public string WebServiceHostname
|
|
{ get { return _webServiceHostname; } }
|
|
|
|
#endregion
|
|
}
|
|
}
|