/*
Technitium DNS Server
Copyright (C) 2024 Shreyas Zare (shreyas@technitium.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
using DnsServerCore.Auth;
using DnsServerCore.Dhcp;
using DnsServerCore.Dns;
using DnsServerCore.Dns.ZoneManagers;
using DnsServerCore.Dns.Zones;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Quic;
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.ClientConnection;
using TechnitiumLibrary.Net.Dns.ResourceRecords;
using TechnitiumLibrary.Net.Proxy;
namespace DnsServerCore
{
public sealed class DnsWebService : IAsyncDisposable, IDisposable
{
#region variables
readonly static char[] commaSeparator = new char[] { ',' };
internal readonly Version _currentVersion;
internal readonly DateTime _uptimestamp = DateTime.UtcNow;
readonly string _appFolder;
internal readonly string _configFolder;
internal readonly LogManager _log;
internal readonly AuthManager _authManager;
readonly WebServiceApi _api;
readonly WebServiceDashboardApi _dashboardApi;
internal readonly WebServiceZonesApi _zonesApi;
readonly WebServiceOtherZonesApi _otherZonesApi;
internal readonly WebServiceAppsApi _appsApi;
readonly WebServiceSettingsApi _settingsApi;
readonly WebServiceDhcpApi _dhcpApi;
readonly WebServiceAuthApi _authApi;
readonly WebServiceLogsApi _logsApi;
WebApplication _webService;
X509Certificate2Collection _webServiceCertificateCollection;
SslServerAuthenticationOptions _webServiceSslServerAuthenticationOptions;
DnsServer _dnsServer;
DhcpServer _dhcpServer;
//web service
internal IReadOnlyList _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any };
internal int _webServiceHttpPort = 5380;
internal int _webServiceTlsPort = 53443;
internal bool _webServiceEnableTls;
internal bool _webServiceEnableHttp3;
internal bool _webServiceHttpToTlsRedirect;
internal bool _webServiceUseSelfSignedTlsCertificate;
internal string _webServiceTlsCertificatePath;
internal string _webServiceTlsCertificatePassword;
DateTime _webServiceTlsCertificateLastModifiedOn;
internal string _webServiceRealIpHeader = "X-Real-IP";
//optional protocols
internal string _dnsTlsCertificatePath;
internal string _dnsTlsCertificatePassword;
DateTime _dnsTlsCertificateLastModifiedOn;
//cache
internal bool _saveCache = true;
Timer _tlsCertificateUpdateTimer;
const int TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL = 60000;
const int TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL = 60000;
List _configDisabledZones;
readonly object _saveLock = new object();
bool _pendingSave;
readonly Timer _saveTimer;
const int SAVE_TIMER_INITIAL_INTERVAL = 10000;
#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;
Directory.CreateDirectory(_configFolder);
Directory.CreateDirectory(Path.Combine(_configFolder, "blocklists"));
Directory.CreateDirectory(Path.Combine(_configFolder, "zones"));
_log = new LogManager(_configFolder);
_authManager = new AuthManager(_configFolder, _log);
_api = new WebServiceApi(this, updateCheckUri);
_dashboardApi = new WebServiceDashboardApi(this);
_zonesApi = new WebServiceZonesApi(this);
_otherZonesApi = new WebServiceOtherZonesApi(this);
_appsApi = new WebServiceAppsApi(this, appStoreUri);
_settingsApi = new WebServiceSettingsApi(this);
_dhcpApi = new WebServiceDhcpApi(this);
_authApi = new WebServiceAuthApi(this);
_logsApi = new WebServiceLogsApi(this);
_saveTimer = new Timer(delegate (object state)
{
lock (_saveLock)
{
if (_pendingSave)
{
try
{
SaveConfigFileInternal();
_pendingSave = false;
}
catch (Exception ex)
{
_log.Write(ex);
//set timer to retry again
_saveTimer.Change(SAVE_TIMER_INITIAL_INTERVAL, Timeout.Infinite);
}
}
}
});
}
#endregion
#region IDisposable
bool _disposed;
public async ValueTask DisposeAsync()
{
if (_disposed)
return;
lock (_saveLock)
{
_saveTimer?.Dispose();
if (_pendingSave)
{
try
{
SaveConfigFileInternal();
}
catch (Exception ex)
{
_log.Write(ex);
}
finally
{
_pendingSave = false;
}
}
}
await StopAsync();
if (_appsApi is not null)
_appsApi.Dispose();
if (_settingsApi is not null)
_settingsApi.Dispose();
if (_authManager is not null)
_authManager.Dispose();
if (_log is not null)
_log.Dispose();
_disposed = true;
}
public void Dispose()
{
DisposeAsync().Sync();
}
#endregion
#region internal
internal string ConvertToRelativePath(string path)
{
if (path.StartsWith(_configFolder, Environment.OSVersion.Platform == PlatformID.Win32NT ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
path = path.Substring(_configFolder.Length).TrimStart(Path.DirectorySeparatorChar);
return path;
}
internal string ConvertToAbsolutePath(string path)
{
if (path is null)
return null;
if (Path.IsPathRooted(path))
return path;
return Path.Combine(_configFolder, path);
}
#endregion
#region server version
internal string GetServerVersion()
{
return GetCleanVersion(_currentVersion);
}
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;
}
#endregion
#region web service
internal async Task TryStartWebServiceAsync(IReadOnlyList oldWebServiceLocalAddresses, int oldWebServiceHttpPort, int oldWebServiceTlsPort)
{
try
{
_webServiceLocalAddresses = DnsServer.GetValidKestralLocalAddresses(_webServiceLocalAddresses);
await StartWebServiceAsync(_webServiceLocalAddresses, _webServiceHttpPort, _webServiceTlsPort, false);
return;
}
catch (Exception ex)
{
_log.Write("Web Service failed to start: " + ex.ToString());
}
_log.Write("Attempting to revert Web Service end point changes ...");
try
{
_webServiceLocalAddresses = DnsServer.GetValidKestralLocalAddresses(oldWebServiceLocalAddresses);
_webServiceHttpPort = oldWebServiceHttpPort;
_webServiceTlsPort = oldWebServiceTlsPort;
await StartWebServiceAsync(_webServiceLocalAddresses, _webServiceHttpPort, _webServiceTlsPort, false);
SaveConfigFileInternal(); //save reverted changes
return;
}
catch (Exception ex2)
{
_log.Write("Web Service failed to start: " + ex2.ToString());
}
_log.Write("Attempting to start Web Service on ANY (0.0.0.0) fallback address...");
try
{
_webServiceLocalAddresses = new IPAddress[] { IPAddress.Any };
await StartWebServiceAsync(_webServiceLocalAddresses, _webServiceHttpPort, _webServiceTlsPort, false);
return;
}
catch (Exception ex3)
{
_log.Write("Web Service failed to start: " + ex3.ToString());
}
_log.Write("Attempting to start Web Service on loopback (127.0.0.1) fallback address...");
_webServiceLocalAddresses = new IPAddress[] { IPAddress.Loopback };
await StartWebServiceAsync(_webServiceLocalAddresses, _webServiceHttpPort, _webServiceTlsPort, true);
}
private async Task StartWebServiceAsync(IReadOnlyList webServiceLocalAddresses, int webServiceHttpPort, int webServiceTlsPort, bool safeMode)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder();
builder.Environment.ContentRootFileProvider = new PhysicalFileProvider(_appFolder)
{
UseActivePolling = true,
UsePollingFileWatcher = true
};
builder.Environment.WebRootFileProvider = new PhysicalFileProvider(Path.Combine(_appFolder, "www"))
{
UseActivePolling = true,
UsePollingFileWatcher = true
};
builder.WebHost.ConfigureKestrel(delegate (WebHostBuilderContext context, KestrelServerOptions serverOptions)
{
//http
foreach (IPAddress webServiceLocalAddress in webServiceLocalAddresses)
serverOptions.Listen(webServiceLocalAddress, webServiceHttpPort);
//https
if (!safeMode && _webServiceEnableTls && (_webServiceCertificateCollection is not null))
{
foreach (IPAddress webServiceLocalAddress in webServiceLocalAddresses)
{
serverOptions.Listen(webServiceLocalAddress, webServiceTlsPort, delegate (ListenOptions listenOptions)
{
listenOptions.Protocols = _webServiceEnableHttp3 ? HttpProtocols.Http1AndHttp2AndHttp3 : HttpProtocols.Http1AndHttp2;
listenOptions.UseHttps(delegate (SslStream stream, SslClientHelloInfo clientHelloInfo, object state, CancellationToken cancellationToken)
{
return ValueTask.FromResult(_webServiceSslServerAuthenticationOptions);
}, null);
});
}
}
serverOptions.AddServerHeader = false;
serverOptions.Limits.MaxRequestBodySize = int.MaxValue;
});
builder.Services.Configure(delegate (FormOptions options)
{
options.MultipartBodyLengthLimit = int.MaxValue;
});
builder.Logging.ClearProviders();
_webService = builder.Build();
if (_webServiceHttpToTlsRedirect && !safeMode && _webServiceEnableTls && (_webServiceCertificateCollection is not null))
_webService.Use(WebServiceHttpsRedirectionMiddleware);
_webService.UseDefaultFiles();
_webService.UseStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = delegate (StaticFileResponseContext ctx)
{
ctx.Context.Response.Headers["X-Robots-Tag"] = "noindex, nofollow";
ctx.Context.Response.Headers.CacheControl = "private, max-age=300";
},
ServeUnknownFileTypes = true
});
ConfigureWebServiceRoutes();
try
{
await _webService.StartAsync();
foreach (IPAddress webServiceLocalAddress in webServiceLocalAddresses)
{
_log?.Write(new IPEndPoint(webServiceLocalAddress, webServiceHttpPort), "Http", "Web Service was bound successfully.");
if (!safeMode && _webServiceEnableTls && (_webServiceCertificateCollection is not null))
_log?.Write(new IPEndPoint(webServiceLocalAddress, webServiceTlsPort), "Https", "Web Service was bound successfully.");
}
}
catch
{
await StopWebServiceAsync();
foreach (IPAddress webServiceLocalAddress in webServiceLocalAddresses)
{
_log?.Write(new IPEndPoint(webServiceLocalAddress, webServiceHttpPort), "Http", "Web Service failed to bind.");
if (!safeMode && _webServiceEnableTls && (_webServiceCertificateCollection is not null))
_log?.Write(new IPEndPoint(webServiceLocalAddress, webServiceTlsPort), "Https", "Web Service failed to bind.");
}
throw;
}
}
internal async Task StopWebServiceAsync()
{
if (_webService is not null)
{
await _webService.DisposeAsync();
_webService = null;
}
}
private void ConfigureWebServiceRoutes()
{
_webService.UseExceptionHandler(WebServiceExceptionHandler);
_webService.Use(WebServiceApiMiddleware);
_webService.UseRouting();
//user auth
_webService.MapGetAndPost("/api/user/login", delegate (HttpContext context) { return _authApi.LoginAsync(context, UserSessionType.Standard); });
_webService.MapGetAndPost("/api/user/createToken", delegate (HttpContext context) { return _authApi.LoginAsync(context, UserSessionType.ApiToken); });
_webService.MapGetAndPost("/api/user/logout", _authApi.Logout);
//user
_webService.MapGetAndPost("/api/user/session/get", _authApi.GetCurrentSessionDetails);
_webService.MapGetAndPost("/api/user/session/delete", delegate (HttpContext context) { _authApi.DeleteSession(context, false); });
_webService.MapGetAndPost("/api/user/changePassword", _authApi.ChangePassword);
_webService.MapGetAndPost("/api/user/profile/get", _authApi.GetProfile);
_webService.MapGetAndPost("/api/user/profile/set", _authApi.SetProfile);
_webService.MapGetAndPost("/api/user/checkForUpdate", _api.CheckForUpdateAsync);
//dashboard
_webService.MapGetAndPost("/api/dashboard/stats/get", _dashboardApi.GetStats);
_webService.MapGetAndPost("/api/dashboard/stats/getTop", _dashboardApi.GetTopStats);
_webService.MapGetAndPost("/api/dashboard/stats/deleteAll", _logsApi.DeleteAllStats);
//zones
_webService.MapGetAndPost("/api/zones/list", _zonesApi.ListZones);
_webService.MapGetAndPost("/api/zones/catalogs/list", _zonesApi.ListCatalogZones);
_webService.MapGetAndPost("/api/zones/create", _zonesApi.CreateZoneAsync);
_webService.MapGetAndPost("/api/zones/import", _zonesApi.ImportZoneAsync);
_webService.MapGetAndPost("/api/zones/export", _zonesApi.ExportZoneAsync);
_webService.MapGetAndPost("/api/zones/clone", _zonesApi.CloneZone);
_webService.MapGetAndPost("/api/zones/convert", _zonesApi.ConvertZone);
_webService.MapGetAndPost("/api/zones/enable", _zonesApi.EnableZone);
_webService.MapGetAndPost("/api/zones/disable", _zonesApi.DisableZone);
_webService.MapGetAndPost("/api/zones/delete", _zonesApi.DeleteZone);
_webService.MapGetAndPost("/api/zones/resync", _zonesApi.ResyncZone);
_webService.MapGetAndPost("/api/zones/options/get", _zonesApi.GetZoneOptions);
_webService.MapGetAndPost("/api/zones/options/set", _zonesApi.SetZoneOptions);
_webService.MapGetAndPost("/api/zones/permissions/get", delegate (HttpContext context) { _authApi.GetPermissionDetails(context, PermissionSection.Zones); });
_webService.MapGetAndPost("/api/zones/permissions/set", delegate (HttpContext context) { _authApi.SetPermissionsDetails(context, PermissionSection.Zones); });
_webService.MapGetAndPost("/api/zones/dnssec/sign", _zonesApi.SignPrimaryZone);
_webService.MapGetAndPost("/api/zones/dnssec/unsign", _zonesApi.UnsignPrimaryZone);
_webService.MapGetAndPost("/api/zones/dnssec/viewDS", _zonesApi.GetPrimaryZoneDsInfo);
_webService.MapGetAndPost("/api/zones/dnssec/properties/get", _zonesApi.GetPrimaryZoneDnssecProperties);
_webService.MapGetAndPost("/api/zones/dnssec/properties/convertToNSEC", _zonesApi.ConvertPrimaryZoneToNSEC);
_webService.MapGetAndPost("/api/zones/dnssec/properties/convertToNSEC3", _zonesApi.ConvertPrimaryZoneToNSEC3);
_webService.MapGetAndPost("/api/zones/dnssec/properties/updateNSEC3Params", _zonesApi.UpdatePrimaryZoneNSEC3Parameters);
_webService.MapGetAndPost("/api/zones/dnssec/properties/updateDnsKeyTtl", _zonesApi.UpdatePrimaryZoneDnssecDnsKeyTtl);
_webService.MapGetAndPost("/api/zones/dnssec/properties/generatePrivateKey", _zonesApi.GenerateAndAddPrimaryZoneDnssecPrivateKey);
_webService.MapGetAndPost("/api/zones/dnssec/properties/updatePrivateKey", _zonesApi.UpdatePrimaryZoneDnssecPrivateKey);
_webService.MapGetAndPost("/api/zones/dnssec/properties/deletePrivateKey", _zonesApi.DeletePrimaryZoneDnssecPrivateKey);
_webService.MapGetAndPost("/api/zones/dnssec/properties/publishAllPrivateKeys", _zonesApi.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys);
_webService.MapGetAndPost("/api/zones/dnssec/properties/rolloverDnsKey", _zonesApi.RolloverPrimaryZoneDnsKey);
_webService.MapGetAndPost("/api/zones/dnssec/properties/retireDnsKey", _zonesApi.RetirePrimaryZoneDnsKey);
_webService.MapGetAndPost("/api/zones/records/add", _zonesApi.AddRecord);
_webService.MapGetAndPost("/api/zones/records/get", _zonesApi.GetRecords);
_webService.MapGetAndPost("/api/zones/records/update", _zonesApi.UpdateRecord);
_webService.MapGetAndPost("/api/zones/records/delete", _zonesApi.DeleteRecord);
//cache
_webService.MapGetAndPost("/api/cache/list", _otherZonesApi.ListCachedZones);
_webService.MapGetAndPost("/api/cache/delete", _otherZonesApi.DeleteCachedZone);
_webService.MapGetAndPost("/api/cache/flush", _otherZonesApi.FlushCache);
//allowed
_webService.MapGetAndPost("/api/allowed/list", _otherZonesApi.ListAllowedZones);
_webService.MapGetAndPost("/api/allowed/add", _otherZonesApi.AllowZone);
_webService.MapGetAndPost("/api/allowed/delete", _otherZonesApi.DeleteAllowedZone);
_webService.MapGetAndPost("/api/allowed/flush", _otherZonesApi.FlushAllowedZone);
_webService.MapGetAndPost("/api/allowed/import", _otherZonesApi.ImportAllowedZones);
_webService.MapGetAndPost("/api/allowed/export", _otherZonesApi.ExportAllowedZonesAsync);
//blocked
_webService.MapGetAndPost("/api/blocked/list", _otherZonesApi.ListBlockedZones);
_webService.MapGetAndPost("/api/blocked/add", _otherZonesApi.BlockZone);
_webService.MapGetAndPost("/api/blocked/delete", _otherZonesApi.DeleteBlockedZone);
_webService.MapGetAndPost("/api/blocked/flush", _otherZonesApi.FlushBlockedZone);
_webService.MapGetAndPost("/api/blocked/import", _otherZonesApi.ImportBlockedZones);
_webService.MapGetAndPost("/api/blocked/export", _otherZonesApi.ExportBlockedZonesAsync);
//apps
_webService.MapGetAndPost("/api/apps/list", _appsApi.ListInstalledAppsAsync);
_webService.MapGetAndPost("/api/apps/listStoreApps", _appsApi.ListStoreApps);
_webService.MapGetAndPost("/api/apps/downloadAndInstall", _appsApi.DownloadAndInstallAppAsync);
_webService.MapGetAndPost("/api/apps/downloadAndUpdate", _appsApi.DownloadAndUpdateAppAsync);
_webService.MapPost("/api/apps/install", _appsApi.InstallAppAsync);
_webService.MapPost("/api/apps/update", _appsApi.UpdateAppAsync);
_webService.MapGetAndPost("/api/apps/uninstall", _appsApi.UninstallApp);
_webService.MapGetAndPost("/api/apps/config/get", _appsApi.GetAppConfigAsync);
_webService.MapGetAndPost("/api/apps/config/set", _appsApi.SetAppConfigAsync);
//dns client
_webService.MapGetAndPost("/api/dnsClient/resolve", _api.ResolveQueryAsync);
//settings
_webService.MapGetAndPost("/api/settings/get", _settingsApi.GetDnsSettings);
_webService.MapGetAndPost("/api/settings/set", _settingsApi.SetDnsSettings);
_webService.MapGetAndPost("/api/settings/getTsigKeyNames", _settingsApi.GetTsigKeyNames);
_webService.MapGetAndPost("/api/settings/forceUpdateBlockLists", _settingsApi.ForceUpdateBlockLists);
_webService.MapGetAndPost("/api/settings/temporaryDisableBlocking", _settingsApi.TemporaryDisableBlocking);
_webService.MapGetAndPost("/api/settings/backup", _settingsApi.BackupSettingsAsync);
_webService.MapPost("/api/settings/restore", _settingsApi.RestoreSettingsAsync);
//dhcp
_webService.MapGetAndPost("/api/dhcp/leases/list", _dhcpApi.ListDhcpLeases);
_webService.MapGetAndPost("/api/dhcp/leases/remove", _dhcpApi.RemoveDhcpLease);
_webService.MapGetAndPost("/api/dhcp/leases/convertToReserved", _dhcpApi.ConvertToReservedLease);
_webService.MapGetAndPost("/api/dhcp/leases/convertToDynamic", _dhcpApi.ConvertToDynamicLease);
_webService.MapGetAndPost("/api/dhcp/scopes/list", _dhcpApi.ListDhcpScopes);
_webService.MapGetAndPost("/api/dhcp/scopes/get", _dhcpApi.GetDhcpScope);
_webService.MapGetAndPost("/api/dhcp/scopes/set", _dhcpApi.SetDhcpScopeAsync);
_webService.MapGetAndPost("/api/dhcp/scopes/addReservedLease", _dhcpApi.AddReservedLease);
_webService.MapGetAndPost("/api/dhcp/scopes/removeReservedLease", _dhcpApi.RemoveReservedLease);
_webService.MapGetAndPost("/api/dhcp/scopes/enable", _dhcpApi.EnableDhcpScopeAsync);
_webService.MapGetAndPost("/api/dhcp/scopes/disable", _dhcpApi.DisableDhcpScope);
_webService.MapGetAndPost("/api/dhcp/scopes/delete", _dhcpApi.DeleteDhcpScope);
//administration
_webService.MapGetAndPost("/api/admin/sessions/list", _authApi.ListSessions);
_webService.MapGetAndPost("/api/admin/sessions/createToken", _authApi.CreateApiToken);
_webService.MapGetAndPost("/api/admin/sessions/delete", delegate (HttpContext context) { _authApi.DeleteSession(context, true); });
_webService.MapGetAndPost("/api/admin/users/list", _authApi.ListUsers);
_webService.MapGetAndPost("/api/admin/users/create", _authApi.CreateUser);
_webService.MapGetAndPost("/api/admin/users/get", _authApi.GetUserDetails);
_webService.MapGetAndPost("/api/admin/users/set", _authApi.SetUserDetails);
_webService.MapGetAndPost("/api/admin/users/delete", _authApi.DeleteUser);
_webService.MapGetAndPost("/api/admin/groups/list", _authApi.ListGroups);
_webService.MapGetAndPost("/api/admin/groups/create", _authApi.CreateGroup);
_webService.MapGetAndPost("/api/admin/groups/get", _authApi.GetGroupDetails);
_webService.MapGetAndPost("/api/admin/groups/set", _authApi.SetGroupDetails);
_webService.MapGetAndPost("/api/admin/groups/delete", _authApi.DeleteGroup);
_webService.MapGetAndPost("/api/admin/permissions/list", _authApi.ListPermissions);
_webService.MapGetAndPost("/api/admin/permissions/get", delegate (HttpContext context) { _authApi.GetPermissionDetails(context, PermissionSection.Unknown); });
_webService.MapGetAndPost("/api/admin/permissions/set", delegate (HttpContext context) { _authApi.SetPermissionsDetails(context, PermissionSection.Unknown); });
//logs
_webService.MapGetAndPost("/api/logs/list", _logsApi.ListLogs);
_webService.MapGetAndPost("/api/logs/download", _logsApi.DownloadLogAsync);
_webService.MapGetAndPost("/api/logs/delete", _logsApi.DeleteLog);
_webService.MapGetAndPost("/api/logs/deleteAll", _logsApi.DeleteAllLogs);
_webService.MapGetAndPost("/api/logs/query", _logsApi.QueryLogsAsync);
}
private Task WebServiceHttpsRedirectionMiddleware(HttpContext context, RequestDelegate next)
{
if (context.Request.IsHttps)
return next(context);
context.Response.Redirect("https://" + (context.Request.Host.HasValue ? context.Request.Host.Host : _dnsServer.ServerDomain) + (_webServiceTlsPort == 443 ? "" : ":" + _webServiceTlsPort) + context.Request.Path + (context.Request.QueryString.HasValue ? context.Request.QueryString.Value : ""), false, true);
return Task.CompletedTask;
}
private async Task WebServiceApiMiddleware(HttpContext context, RequestDelegate next)
{
bool needsJsonResponseObject;
switch (context.Request.Path)
{
case "/api/user/login":
case "/api/user/createToken":
case "/api/user/logout":
needsJsonResponseObject = false;
break;
case "/api/user/session/get":
{
if (!TryGetSession(context, out UserSession session))
throw new InvalidTokenWebServiceException("Invalid token or session expired.");
context.Items["session"] = session;
needsJsonResponseObject = false;
}
break;
case "/api/zones/export":
case "/api/allowed/export":
case "/api/blocked/export":
case "/api/settings/backup":
case "/api/logs/download":
{
if (!TryGetSession(context, out UserSession session))
throw new InvalidTokenWebServiceException("Invalid token or session expired.");
context.Items["session"] = session;
await next(context);
}
return;
default:
{
if (!TryGetSession(context, out UserSession session))
throw new InvalidTokenWebServiceException("Invalid token or session expired.");
context.Items["session"] = session;
needsJsonResponseObject = true;
}
break;
}
using (MemoryStream mS = new MemoryStream(4096))
{
Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS);
context.Items["jsonWriter"] = jsonWriter;
jsonWriter.WriteStartObject();
if (needsJsonResponseObject)
{
jsonWriter.WritePropertyName("response");
jsonWriter.WriteStartObject();
await next(context);
jsonWriter.WriteEndObject();
}
else
{
await next(context);
}
jsonWriter.WriteString("status", "ok");
jsonWriter.WriteEndObject();
jsonWriter.Flush();
mS.Position = 0;
HttpResponse response = context.Response;
response.StatusCode = StatusCodes.Status200OK;
response.ContentType = "application/json; charset=utf-8";
response.ContentLength = mS.Length;
await mS.CopyToAsync(response.Body);
}
}
private void WebServiceExceptionHandler(IApplicationBuilder exceptionHandlerApp)
{
exceptionHandlerApp.Run(async delegate (HttpContext context)
{
IExceptionHandlerPathFeature exceptionHandlerPathFeature = context.Features.Get();
if (exceptionHandlerPathFeature.Path.StartsWith("/api/"))
{
Exception ex = exceptionHandlerPathFeature.Error;
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.ContentType = "application/json; charset=utf-8";
await using (Utf8JsonWriter jsonWriter = new Utf8JsonWriter(context.Response.Body))
{
jsonWriter.WriteStartObject();
if (ex is InvalidTokenWebServiceException)
{
jsonWriter.WriteString("status", "invalid-token");
jsonWriter.WriteString("errorMessage", ex.Message);
}
else
{
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();
}
_log.Write(context.GetRemoteEndPoint(_webServiceRealIpHeader), ex);
}
});
}
private bool TryGetSession(HttpContext context, out UserSession session)
{
string token = context.Request.GetQueryOrForm("token");
session = _authManager.GetSession(token);
if ((session is null) || session.User.Disabled)
return false;
if (session.HasExpired())
{
_authManager.DeleteSession(session.Token);
_authManager.SaveConfigFile();
return false;
}
IPEndPoint remoteEP = context.GetRemoteEndPoint(_webServiceRealIpHeader);
session.UpdateLastSeen(remoteEP.Address, context.Request.Headers.UserAgent);
return true;
}
#endregion
#region tls
internal void StartTlsCertificateUpdateTimer()
{
if (_tlsCertificateUpdateTimer is null)
{
_tlsCertificateUpdateTimer = new Timer(delegate (object state)
{
if (!string.IsNullOrEmpty(_webServiceTlsCertificatePath))
{
string webServiceTlsCertificatePath = ConvertToAbsolutePath(_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))
{
string dnsTlsCertificatePath = ConvertToAbsolutePath(_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 is not 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);
switch (Path.GetExtension(tlsCertificatePath).ToLowerInvariant())
{
case ".pfx":
case ".p12":
break;
default:
throw new ArgumentException("Web Service TLS certificate file must be PKCS #12 formatted with .pfx or .p12 extension: " + tlsCertificatePath);
}
X509Certificate2Collection certificateCollection = new X509Certificate2Collection();
certificateCollection.Import(tlsCertificatePath, tlsCertificatePassword, X509KeyStorageFlags.PersistKeySet);
X509Certificate2 serverCertificate = null;
foreach (X509Certificate2 certificate in certificateCollection)
{
if (certificate.HasPrivateKey)
{
serverCertificate = certificate;
break;
}
}
if (serverCertificate is null)
throw new ArgumentException("Web Service TLS certificate file must contain a certificate with private key.");
_webServiceCertificateCollection = certificateCollection;
_webServiceSslServerAuthenticationOptions = new SslServerAuthenticationOptions
{
ServerCertificateContext = SslStreamCertificateContext.Create(serverCertificate, _webServiceCertificateCollection, false)
};
_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);
switch (Path.GetExtension(tlsCertificatePath).ToLowerInvariant())
{
case ".pfx":
case ".p12":
break;
default:
throw new ArgumentException("DNS Server TLS certificate file must be PKCS #12 formatted with .pfx or .p12 extension: " + tlsCertificatePath);
}
X509Certificate2Collection certificateCollection = new X509Certificate2Collection();
certificateCollection.Import(tlsCertificatePath, tlsCertificatePassword, X509KeyStorageFlags.PersistKeySet);
_dnsServer.CertificateCollection = certificateCollection;
_dnsTlsCertificateLastModifiedOn = fileInfo.LastWriteTimeUtc;
_log.Write("DNS Server TLS certificate was loaded: " + tlsCertificatePath);
}
internal void SelfSignedCertCheck(bool generateNew, bool throwException)
{
string selfSignedCertificateFilePath = Path.Combine(_configFolder, "self-signed-cert.pfx");
if (_webServiceUseSelfSignedTlsCertificate)
{
string oldSelfSignedCertificateFilePath = Path.Combine(_configFolder, "cert.pfx");
if (!oldSelfSignedCertificateFilePath.Equals(ConvertToAbsolutePath(_webServiceTlsCertificatePath), Environment.OSVersion.Platform == PlatformID.Win32NT ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) && File.Exists(oldSelfSignedCertificateFilePath) && !File.Exists(selfSignedCertificateFilePath))
File.Move(oldSelfSignedCertificateFilePath, selfSignedCertificateFilePath);
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 quic
internal static void ValidateQuicSupport(string protocolName = "DNS-over-QUIC")
{
#pragma warning disable CA2252 // This API requires opting into preview features
#pragma warning disable CA1416 // Validate platform compatibility
if (!QuicConnection.IsSupported)
throw new DnsWebServiceException(protocolName + " is supported only on Windows 11, Windows Server 2022, and Linux. On Linux, you must install 'libmsquic' manually.");
#pragma warning restore CA1416 // Validate platform compatibility
#pragma warning restore CA2252 // This API requires opting into preview features
}
internal static bool IsQuicSupported()
{
#pragma warning disable CA2252 // This API requires opting into preview features
#pragma warning disable CA1416 // Validate platform compatibility
return QuicConnection.IsSupported;
#pragma warning restore CA1416 // Validate platform compatibility
#pragma warning restore CA2252 // This API requires opting into preview features
}
#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)
SaveConfigFileInternal(); //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();
//web service
string strWebServiceLocalAddresses = Environment.GetEnvironmentVariable("DNS_SERVER_WEB_SERVICE_LOCAL_ADDRESSES");
if (!string.IsNullOrEmpty(strWebServiceLocalAddresses))
_webServiceLocalAddresses = strWebServiceLocalAddresses.Split(IPAddress.Parse, commaSeparator);
string strWebServiceHttpPort = Environment.GetEnvironmentVariable("DNS_SERVER_WEB_SERVICE_HTTP_PORT");
if (!string.IsNullOrEmpty(strWebServiceHttpPort))
_webServiceHttpPort = int.Parse(strWebServiceHttpPort);
string webServiceTlsPort = Environment.GetEnvironmentVariable("DNS_SERVER_WEB_SERVICE_HTTPS_PORT");
if (!string.IsNullOrEmpty(webServiceTlsPort))
_webServiceTlsPort = int.Parse(webServiceTlsPort);
string webServiceEnableTls = Environment.GetEnvironmentVariable("DNS_SERVER_WEB_SERVICE_ENABLE_HTTPS");
if (!string.IsNullOrEmpty(webServiceEnableTls))
_webServiceEnableTls = bool.Parse(webServiceEnableTls);
string webServiceUseSelfSignedTlsCertificate = Environment.GetEnvironmentVariable("DNS_SERVER_WEB_SERVICE_USE_SELF_SIGNED_CERT");
if (!string.IsNullOrEmpty(webServiceUseSelfSignedTlsCertificate))
_webServiceUseSelfSignedTlsCertificate = bool.Parse(webServiceUseSelfSignedTlsCertificate);
//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(strRecursion, true);
else
_dnsServer.Recursion = DnsServerRecursion.AllowOnlyForPrivateNetworks; //default for security reasons
string strRecursionNetworkACL = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION_NETWORK_ACL");
if (!string.IsNullOrEmpty(strRecursionNetworkACL))
{
_dnsServer.RecursionNetworkACL = strRecursionNetworkACL.Split(NetworkAccessControl.Parse, ',');
}
else
{
NetworkAddress[] recursionDeniedNetworks = null;
NetworkAddress[] recursionAllowedNetworks = null;
string strRecursionDeniedNetworks = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION_DENIED_NETWORKS");
if (!string.IsNullOrEmpty(strRecursionDeniedNetworks))
recursionDeniedNetworks = strRecursionDeniedNetworks.Split(NetworkAddress.Parse, ',');
string strRecursionAllowedNetworks = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION_ALLOWED_NETWORKS");
if (!string.IsNullOrEmpty(strRecursionAllowedNetworks))
recursionAllowedNetworks = strRecursionAllowedNetworks.Split(NetworkAddress.Parse, ',');
_dnsServer.RecursionNetworkACL = AuthZoneInfo.ConvertDenyAllowToACL(recursionDeniedNetworks, recursionAllowedNetworks);
}
_dnsServer.RandomizeName = false; //default false to allow resolving from bad name servers
_dnsServer.QnameMinimization = true; //default true to enable privacy feature
_dnsServer.NsRevalidation = false; //default false to allow resolving misconfigured zones
//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(commaSeparator, 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(strForwarderProtocol, true);
if (forwarderProtocol == DnsTransportProtocol.HttpsJson)
forwarderProtocol = DnsTransportProtocol.Https;
}
_dnsServer.Forwarders = strForwarders.Split(delegate (string value)
{
NameServerAddress forwarder = NameServerAddress.Parse(value);
if (forwarder.Protocol != forwarderProtocol)
forwarder = forwarder.ChangeProtocol(forwarderProtocol);
return forwarder;
}, ',');
}
//logging
_dnsServer.ResolverLogManager = _log;
string strUseLocalTime = Environment.GetEnvironmentVariable("DNS_SERVER_LOG_USING_LOCAL_TIME");
if (!string.IsNullOrEmpty(strUseLocalTime))
_log.UseLocalTime = bool.Parse(strUseLocalTime);
_dnsServer.StatsManager.EnableInMemoryStats = false;
SaveConfigFileInternal();
}
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;
}
//exclude web service TLS port to prevent socket pool from occupying it
UdpClientConnection.SocketPoolExcludedPorts = new int[] { _webServiceTlsPort };
}
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, DnsForwarderRecordProxyType.DefaultProxy, 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();
}
}
}
private void SaveConfigFileInternal()
{
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 SaveConfigFile()
{
lock (_saveLock)
{
if (_pendingSave)
return;
_pendingSave = true;
_saveTimer.Change(SAVE_TIMER_INITIAL_INTERVAL, Timeout.Infinite);
}
}
internal void InspectAndFixZonePermissions()
{
Permission permission = _authManager.GetPermission(PermissionSection.Zones);
if (permission is null)
throw new DnsWebServiceException("Failed to read 'Zones' permissions: auth.config file is probably corrupt.");
IReadOnlyDictionary subItemPermissions = permission.SubItemPermissions;
//remove ghost permissions
foreach (KeyValuePair 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
IReadOnlyList zones = _dnsServer.AuthZoneManager.GetAllZones();
Group admins = _authManager.GetGroup(Group.ADMINISTRATORS);
if (admins is null)
throw new DnsWebServiceException("Failed to find 'Administrators' group: auth.config file is probably corrupt.");
Group dnsAdmins = _authManager.GetGroup(Group.DNS_ADMINISTRATORS);
if (dnsAdmins is null)
throw new DnsWebServiceException("Failed to find 'DNS Administrators' group: auth.config file is probably corrupt.");
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 <= 38))
{
ReadConfigFrom(bR, version);
}
else if ((version >= 2) && (version <= 27))
{
ReadOldConfigFrom(bR, version);
//new default settings
DnsClientConnection.IPv4SourceAddresses = null;
DnsClientConnection.IPv6SourceAddresses = null;
_appsApi.EnableAutomaticUpdate = true;
_webServiceEnableHttp3 = _webServiceEnableTls && IsQuicSupported();
_dnsServer.EnableDnsOverHttp3 = _dnsServer.EnableDnsOverHttps && IsQuicSupported();
_webServiceRealIpHeader = "X-Real-IP";
_dnsServer.DnsOverHttpRealIpHeader = "X-Real-IP";
_dnsServer.ResponsiblePersonInternal = null;
_dnsServer.AuthZoneManager.UseSoaSerialDateScheme = false;
_dnsServer.ZoneTransferAllowedNetworks = null;
_dnsServer.NotifyAllowedNetworks = null;
_dnsServer.EDnsClientSubnet = false;
_dnsServer.EDnsClientSubnetIPv4PrefixLength = 24;
_dnsServer.EDnsClientSubnetIPv6PrefixLength = 56;
_dnsServer.EDnsClientSubnetIpv4Override = null;
_dnsServer.EDnsClientSubnetIpv6Override = null;
_dnsServer.QpmLimitBypassList = null;
_dnsServer.BlockingBypassList = null;
_dnsServer.BlockingAnswerTtl = 30;
_dnsServer.ResolverConcurrency = 2;
_dnsServer.CacheZoneManager.ServeStaleAnswerTtl = CacheZoneManager.SERVE_STALE_ANSWER_TTL;
_dnsServer.CacheZoneManager.ServeStaleResetTtl = CacheZoneManager.SERVE_STALE_RESET_TTL;
_dnsServer.ServeStaleMaxWaitTime = DnsServer.SERVE_STALE_MAX_WAIT_TIME;
_dnsServer.ConcurrentForwarding = true;
_dnsServer.ResolverLogManager = _log;
_dnsServer.StatsManager.EnableInMemoryStats = false;
}
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] = IPAddressExtensions.ReadFrom(bR);
_webServiceLocalAddresses = localAddresses;
}
else
{
_webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any };
}
}
_webServiceEnableTls = bR.ReadBoolean();
if (version >= 33)
_webServiceEnableHttp3 = bR.ReadBoolean();
else
_webServiceEnableHttp3 = _webServiceEnableTls && IsQuicSupported();
_webServiceHttpToTlsRedirect = bR.ReadBoolean();
_webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean();
_webServiceTlsCertificatePath = bR.ReadShortString();
_webServiceTlsCertificatePassword = bR.ReadShortString();
if (_webServiceTlsCertificatePath.Length == 0)
_webServiceTlsCertificatePath = null;
if (_webServiceTlsCertificatePath is not null)
{
string webServiceTlsCertificatePath = ConvertToAbsolutePath(_webServiceTlsCertificatePath);
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);
if (version >= 38)
_webServiceRealIpHeader = bR.ReadShortString();
else
_webServiceRealIpHeader = "X-Real-IP";
}
//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)EndPointExtensions.ReadFrom(bR);
_dnsServer.LocalEndPoints = localEndPoints;
}
else
{
_dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) };
}
}
if (version >= 34)
{
DnsClientConnection.IPv4SourceAddresses = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
DnsClientConnection.IPv6SourceAddresses = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
}
else
{
DnsClientConnection.IPv4SourceAddresses = null;
DnsClientConnection.IPv6SourceAddresses = null;
}
_zonesApi.DefaultRecordTtl = bR.ReadUInt32();
if (version >= 36)
{
string rp = bR.ReadString();
if (rp.Length == 0)
_dnsServer.ResponsiblePersonInternal = null;
else
_dnsServer.ResponsiblePersonInternal = new MailAddress(rp);
}
else
{
_dnsServer.ResponsiblePersonInternal = null;
}
if (version >= 33)
{
_dnsServer.AuthZoneManager.UseSoaSerialDateScheme = bR.ReadBoolean();
_dnsServer.ZoneTransferAllowedNetworks = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
}
else
{
_dnsServer.AuthZoneManager.UseSoaSerialDateScheme = false;
_dnsServer.ZoneTransferAllowedNetworks = null;
}
if (version >= 34)
_dnsServer.NotifyAllowedNetworks = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
else
_dnsServer.NotifyAllowedNetworks = null;
_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;
}
if (version >= 35)
{
if (bR.ReadBoolean())
_dnsServer.EDnsClientSubnetIpv4Override = NetworkAddress.ReadFrom(bR);
else
_dnsServer.EDnsClientSubnetIpv4Override = null;
if (bR.ReadBoolean())
_dnsServer.EDnsClientSubnetIpv6Override = NetworkAddress.ReadFrom(bR);
else
_dnsServer.EDnsClientSubnetIpv6Override = null;
}
else
{
_dnsServer.EDnsClientSubnetIpv4Override = null;
_dnsServer.EDnsClientSubnetIpv6Override = null;
}
_dnsServer.QpmLimitRequests = bR.ReadInt32();
_dnsServer.QpmLimitErrors = bR.ReadInt32();
_dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
_dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32();
_dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32();
if (version >= 34)
_dnsServer.QpmLimitBypassList = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
else
_dnsServer.QpmLimitBypassList = null;
_dnsServer.ClientTimeout = bR.ReadInt32();
if (version < 34)
{
if (_dnsServer.ClientTimeout == 4000)
_dnsServer.ClientTimeout = 2000;
}
_dnsServer.TcpSendTimeout = bR.ReadInt32();
_dnsServer.TcpReceiveTimeout = bR.ReadInt32();
if (version >= 30)
{
_dnsServer.QuicIdleTimeout = bR.ReadInt32();
_dnsServer.QuicMaxInboundStreams = bR.ReadInt32();
_dnsServer.ListenBacklog = bR.ReadInt32();
}
else
{
_dnsServer.QuicIdleTimeout = 60000;
_dnsServer.QuicMaxInboundStreams = 100;
_dnsServer.ListenBacklog = 100;
}
//optional protocols
if (version >= 32)
{
_dnsServer.EnableDnsOverUdpProxy = bR.ReadBoolean();
_dnsServer.EnableDnsOverTcpProxy = bR.ReadBoolean();
}
else
{
_dnsServer.EnableDnsOverUdpProxy = false;
_dnsServer.EnableDnsOverTcpProxy = false;
}
_dnsServer.EnableDnsOverHttp = bR.ReadBoolean();
_dnsServer.EnableDnsOverTls = bR.ReadBoolean();
_dnsServer.EnableDnsOverHttps = bR.ReadBoolean();
if (version >= 37)
_dnsServer.EnableDnsOverHttp3 = bR.ReadBoolean();
else
_dnsServer.EnableDnsOverHttp3 = _dnsServer.EnableDnsOverHttps && IsQuicSupported();
if (version >= 32)
{
_dnsServer.EnableDnsOverQuic = bR.ReadBoolean();
_dnsServer.DnsOverUdpProxyPort = bR.ReadInt32();
_dnsServer.DnsOverTcpProxyPort = bR.ReadInt32();
_dnsServer.DnsOverHttpPort = bR.ReadInt32();
_dnsServer.DnsOverTlsPort = bR.ReadInt32();
_dnsServer.DnsOverHttpsPort = bR.ReadInt32();
_dnsServer.DnsOverQuicPort = bR.ReadInt32();
}
else if (version >= 31)
{
_dnsServer.EnableDnsOverQuic = bR.ReadBoolean();
_dnsServer.DnsOverHttpPort = bR.ReadInt32();
_dnsServer.DnsOverTlsPort = bR.ReadInt32();
_dnsServer.DnsOverHttpsPort = bR.ReadInt32();
_dnsServer.DnsOverQuicPort = bR.ReadInt32();
}
else if (version >= 30)
{
_ = bR.ReadBoolean(); //removed EnableDnsOverHttpPort80 value
_dnsServer.EnableDnsOverQuic = bR.ReadBoolean();
_dnsServer.DnsOverHttpPort = bR.ReadInt32();
_dnsServer.DnsOverTlsPort = bR.ReadInt32();
_dnsServer.DnsOverHttpsPort = bR.ReadInt32();
_dnsServer.DnsOverQuicPort = bR.ReadInt32();
}
else
{
_dnsServer.EnableDnsOverQuic = false;
_dnsServer.DnsOverUdpProxyPort = 538;
_dnsServer.DnsOverTcpProxyPort = 538;
if (_dnsServer.EnableDnsOverHttps)
{
_dnsServer.EnableDnsOverHttp = true;
_dnsServer.DnsOverHttpPort = 80;
}
else if (_dnsServer.EnableDnsOverHttp)
{
_dnsServer.DnsOverHttpPort = 8053;
}
else
{
_dnsServer.DnsOverHttpPort = 80;
}
_dnsServer.DnsOverTlsPort = 853;
_dnsServer.DnsOverHttpsPort = 443;
_dnsServer.DnsOverQuicPort = 853;
}
_dnsTlsCertificatePath = bR.ReadShortString();
_dnsTlsCertificatePassword = bR.ReadShortString();
if (_dnsTlsCertificatePath.Length == 0)
_dnsTlsCertificatePath = null;
if (_dnsTlsCertificatePath != null)
{
string dnsTlsCertificatePath = ConvertToAbsolutePath(_dnsTlsCertificatePath);
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();
}
if (version >= 38)
_dnsServer.DnsOverHttpRealIpHeader = bR.ReadShortString();
else
_dnsServer.DnsOverHttpRealIpHeader = "X-Real-IP";
//tsig
{
int count = bR.ReadByte();
Dictionary tsigKeys = new Dictionary(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();
if (version >= 37)
{
_dnsServer.RecursionNetworkACL = AuthZoneInfo.ReadNetworkACLFrom(bR);
}
else
{
NetworkAddress[] recursionDeniedNetworks = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
NetworkAddress[] recursionAllowedNetworks = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
_dnsServer.RecursionNetworkACL = AuthZoneInfo.ConvertDenyAllowToACL(recursionDeniedNetworks, recursionAllowedNetworks);
}
_dnsServer.RandomizeName = bR.ReadBoolean();
_dnsServer.QnameMinimization = bR.ReadBoolean();
_dnsServer.NsRevalidation = bR.ReadBoolean();
_dnsServer.ResolverRetries = bR.ReadInt32();
_dnsServer.ResolverTimeout = bR.ReadInt32();
if (version >= 37)
_dnsServer.ResolverConcurrency = bR.ReadInt32();
else
_dnsServer.ResolverConcurrency = 2;
_dnsServer.ResolverMaxStackCount = bR.ReadInt32();
//cache
if (version >= 30)
_saveCache = bR.ReadBoolean();
else
_saveCache = true;
_dnsServer.ServeStale = bR.ReadBoolean();
_dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32();
if (version >= 36)
{
_dnsServer.CacheZoneManager.ServeStaleAnswerTtl = bR.ReadUInt32();
_dnsServer.CacheZoneManager.ServeStaleResetTtl = bR.ReadUInt32();
_dnsServer.ServeStaleMaxWaitTime = bR.ReadInt32();
}
else
{
_dnsServer.CacheZoneManager.ServeStaleAnswerTtl = CacheZoneManager.SERVE_STALE_ANSWER_TTL;
_dnsServer.CacheZoneManager.ServeStaleResetTtl = CacheZoneManager.SERVE_STALE_RESET_TTL;
_dnsServer.ServeStaleMaxWaitTime = DnsServer.SERVE_STALE_MAX_WAIT_TIME;
}
_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();
if (version >= 33)
_dnsServer.BlockingBypassList = AuthZoneInfo.ReadNetworkAddressesFrom(bR);
else
_dnsServer.BlockingBypassList = null;
_dnsServer.BlockingType = (DnsServerBlockingType)bR.ReadByte();
if (version >= 38)
_dnsServer.BlockingAnswerTtl = bR.ReadUInt32();
else
_dnsServer.BlockingAnswerTtl = 30;
{
//read custom blocking addresses
int count = bR.ReadByte();
if (count > 0)
{
List dnsARecords = new List();
List dnsAAAARecords = new List();
for (int i = 0; i < count; i++)
{
IPAddress customAddress = IPAddressExtensions.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;
}
}
{
//read block list urls
int count = bR.ReadByte();
_dnsServer.BlockListZoneManager.AllowListUrls.Clear();
_dnsServer.BlockListZoneManager.BlockListUrls.Clear();
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 bypassList = new List(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;
}
else
{
_dnsServer.Forwarders = null;
}
}
if (version >= 37)
_dnsServer.ConcurrentForwarding = bR.ReadBoolean();
else
_dnsServer.ConcurrentForwarding = true;
_dnsServer.ForwarderRetries = bR.ReadInt32();
_dnsServer.ForwarderTimeout = bR.ReadInt32();
_dnsServer.ForwarderConcurrency = bR.ReadInt32();
//logging
if (version >= 33)
{
if (bR.ReadBoolean()) //ignore resolver logs
_dnsServer.ResolverLogManager = null;
else
_dnsServer.ResolverLogManager = _log;
}
else
{
_dnsServer.ResolverLogManager = _log;
}
if (bR.ReadBoolean()) //log all queries
_dnsServer.QueryLogManager = _log;
else
_dnsServer.QueryLogManager = null;
if (version >= 34)
_dnsServer.StatsManager.EnableInMemoryStats = bR.ReadBoolean();
else
_dnsServer.StatsManager.EnableInMemoryStats = false;
_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] = IPAddressExtensions.ReadFrom(bR);
_webServiceLocalAddresses = localAddresses;
}
else
{
_webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any };
}
}
_webServiceTlsPort = bR.ReadInt32();
_webServiceEnableTls = bR.ReadBoolean();
_webServiceHttpToTlsRedirect = bR.ReadBoolean();
_webServiceTlsCertificatePath = bR.ReadShortString();
_webServiceTlsCertificatePassword = bR.ReadShortString();
if (_webServiceTlsCertificatePath.Length == 0)
_webServiceTlsCertificatePath = null;
if (_webServiceTlsCertificatePath != null)
{
string webServiceTlsCertificatePath = ConvertToAbsolutePath(_webServiceTlsCertificatePath);
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();
NetworkAddress[] recursionDeniedNetworks = null;
{
int count = bR.ReadByte();
if (count > 0)
{
NetworkAddress[] networks = new NetworkAddress[count];
for (int i = 0; i < count; i++)
networks[i] = NetworkAddress.ReadFrom(bR);
recursionDeniedNetworks = networks;
}
else
{
recursionDeniedNetworks = null;
}
}
NetworkAddress[] recursionAllowedNetworks = null;
{
int count = bR.ReadByte();
if (count > 0)
{
NetworkAddress[] networks = new NetworkAddress[count];
for (int i = 0; i < count; i++)
networks[i] = NetworkAddress.ReadFrom(bR);
recursionAllowedNetworks = networks;
}
else
{
recursionAllowedNetworks = null;
}
}
_dnsServer.RecursionNetworkACL = AuthZoneInfo.ConvertDenyAllowToACL(recursionDeniedNetworks, recursionAllowedNetworks);
}
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 = false; //default false to allow resolving from bad name servers
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 bypassList = new List(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;
}
else
{
_dnsServer.Forwarders = null;
}
}
if (version <= 10)
{
DnsTransportProtocol forwarderProtocol = (DnsTransportProtocol)bR.ReadByte();
if (forwarderProtocol == DnsTransportProtocol.HttpsJson)
forwarderProtocol = DnsTransportProtocol.Https;
if (_dnsServer.Forwarders != null)
{
List forwarders = new List();
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(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 dnsARecords = new List();
List dnsAAAARecords = new List();
for (int i = 0; i < count; i++)
{
IPAddress customAddress = IPAddressExtensions.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;
}
}
else
{
_dnsServer.CustomBlockingARecords = null;
_dnsServer.CustomBlockingAAAARecords = null;
}
if (version > 4)
{
//read block list urls
int count = bR.ReadByte();
_dnsServer.BlockListZoneManager.AllowListUrls.Clear();
_dnsServer.BlockListZoneManager.BlockListUrls.Clear();
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)EndPointExtensions.ReadFrom(bR);
_dnsServer.LocalEndPoints = localEndPoints;
}
else
{
_dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) };
}
}
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(IPAddressExtensions.ReadFrom(bR), 53);
_dnsServer.LocalEndPoints = localEndPoints;
}
else
{
_dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) };
}
}
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)
{
string dnsTlsCertificatePath = ConvertToAbsolutePath(_dnsTlsCertificatePath);
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 tsigKeys = new Dictionary(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 tsigKeys = new Dictionary(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 = false; //default false to allow resolving misconfigured zones
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();
if (_dnsServer.ClientTimeout == 4000)
_dnsServer.ClientTimeout = 2000;
_dnsServer.TcpSendTimeout = bR.ReadInt32();
_dnsServer.TcpReceiveTimeout = bR.ReadInt32();
}
else
{
_dnsServer.DnssecValidation = true;
CreateForwarderZoneToDisableDnssecForNTP();
_dnsServer.ResolverRetries = 2;
_dnsServer.ResolverTimeout = 1500;
_dnsServer.ResolverMaxStackCount = 16;
_dnsServer.ForwarderRetries = 3;
_dnsServer.ForwarderTimeout = 2000;
_dnsServer.ForwarderConcurrency = 2;
_dnsServer.ClientTimeout = 2000;
_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)38); //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(_webServiceEnableHttp3);
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);
bW.WriteShortString(_webServiceRealIpHeader);
}
//dns
{
//general
bW.WriteShortString(_dnsServer.ServerDomain);
{
bW.Write(Convert.ToByte(_dnsServer.LocalEndPoints.Count));
foreach (IPEndPoint localEP in _dnsServer.LocalEndPoints)
localEP.WriteTo(bW);
}
AuthZoneInfo.WriteNetworkAddressesTo(DnsClientConnection.IPv4SourceAddresses, bW);
AuthZoneInfo.WriteNetworkAddressesTo(DnsClientConnection.IPv6SourceAddresses, bW);
bW.Write(_zonesApi.DefaultRecordTtl);
if (_dnsServer.ResponsiblePersonInternal is null)
bW.WriteShortString("");
else
bW.WriteShortString(_dnsServer.ResponsiblePersonInternal.Address);
bW.Write(_dnsServer.AuthZoneManager.UseSoaSerialDateScheme);
AuthZoneInfo.WriteNetworkAddressesTo(_dnsServer.ZoneTransferAllowedNetworks, bW);
AuthZoneInfo.WriteNetworkAddressesTo(_dnsServer.NotifyAllowedNetworks, bW);
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);
if (_dnsServer.EDnsClientSubnetIpv4Override is null)
{
bW.Write(false);
}
else
{
bW.Write(true);
_dnsServer.EDnsClientSubnetIpv4Override.WriteTo(bW);
}
if (_dnsServer.EDnsClientSubnetIpv6Override is null)
{
bW.Write(false);
}
else
{
bW.Write(true);
_dnsServer.EDnsClientSubnetIpv6Override.WriteTo(bW);
}
bW.Write(_dnsServer.QpmLimitRequests);
bW.Write(_dnsServer.QpmLimitErrors);
bW.Write(_dnsServer.QpmLimitSampleMinutes);
bW.Write(_dnsServer.QpmLimitIPv4PrefixLength);
bW.Write(_dnsServer.QpmLimitIPv6PrefixLength);
AuthZoneInfo.WriteNetworkAddressesTo(_dnsServer.QpmLimitBypassList, bW);
bW.Write(_dnsServer.ClientTimeout);
bW.Write(_dnsServer.TcpSendTimeout);
bW.Write(_dnsServer.TcpReceiveTimeout);
bW.Write(_dnsServer.QuicIdleTimeout);
bW.Write(_dnsServer.QuicMaxInboundStreams);
bW.Write(_dnsServer.ListenBacklog);
//optional protocols
bW.Write(_dnsServer.EnableDnsOverUdpProxy);
bW.Write(_dnsServer.EnableDnsOverTcpProxy);
bW.Write(_dnsServer.EnableDnsOverHttp);
bW.Write(_dnsServer.EnableDnsOverTls);
bW.Write(_dnsServer.EnableDnsOverHttps);
bW.Write(_dnsServer.EnableDnsOverHttp3);
bW.Write(_dnsServer.EnableDnsOverQuic);
bW.Write(_dnsServer.DnsOverUdpProxyPort);
bW.Write(_dnsServer.DnsOverTcpProxyPort);
bW.Write(_dnsServer.DnsOverHttpPort);
bW.Write(_dnsServer.DnsOverTlsPort);
bW.Write(_dnsServer.DnsOverHttpsPort);
bW.Write(_dnsServer.DnsOverQuicPort);
if (_dnsTlsCertificatePath == null)
bW.WriteShortString(string.Empty);
else
bW.WriteShortString(_dnsTlsCertificatePath);
if (_dnsTlsCertificatePassword == null)
bW.WriteShortString(string.Empty);
else
bW.WriteShortString(_dnsTlsCertificatePassword);
bW.WriteShortString(_dnsServer.DnsOverHttpRealIpHeader);
//tsig
if (_dnsServer.TsigKeys is null)
{
bW.Write((byte)0);
}
else
{
bW.Write(Convert.ToByte(_dnsServer.TsigKeys.Count));
foreach (KeyValuePair 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);
AuthZoneInfo.WriteNetworkACLTo(_dnsServer.RecursionNetworkACL, bW);
bW.Write(_dnsServer.RandomizeName);
bW.Write(_dnsServer.QnameMinimization);
bW.Write(_dnsServer.NsRevalidation);
bW.Write(_dnsServer.ResolverRetries);
bW.Write(_dnsServer.ResolverTimeout);
bW.Write(_dnsServer.ResolverConcurrency);
bW.Write(_dnsServer.ResolverMaxStackCount);
//cache
bW.Write(_saveCache);
bW.Write(_dnsServer.ServeStale);
bW.Write(_dnsServer.CacheZoneManager.ServeStaleTtl);
bW.Write(_dnsServer.CacheZoneManager.ServeStaleAnswerTtl);
bW.Write(_dnsServer.CacheZoneManager.ServeStaleResetTtl);
bW.Write(_dnsServer.ServeStaleMaxWaitTime);
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);
AuthZoneInfo.WriteNetworkAddressesTo(_dnsServer.BlockingBypassList, bW);
bW.Write((byte)_dnsServer.BlockingType);
bW.Write(_dnsServer.BlockingAnswerTtl);
{
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.ConcurrentForwarding);
bW.Write(_dnsServer.ForwarderRetries);
bW.Write(_dnsServer.ForwarderTimeout);
bW.Write(_dnsServer.ForwarderConcurrency);
//logging
bW.Write(_dnsServer.ResolverLogManager is null); //ignore resolver logs
bW.Write(_dnsServer.QueryLogManager is not null); //log all queries
bW.Write(_dnsServer.StatsManager.EnableInMemoryStats);
bW.Write(_dnsServer.StatsManager.MaxStatFileDays);
}
}
#endregion
#region secondary catalog zones
private void AuthZoneManager_SecondaryCatalogZoneAdded(object sender, SecondaryCatalogEventArgs e)
{
AuthZoneInfo sourceZoneInfo = new AuthZoneInfo(sender as ApexZone);
AuthZoneInfo zoneInfo = e.ZoneInfo;
//clone user/group permissions from source zone
Permission sourceZonePermissions = _authManager.GetPermission(PermissionSection.Zones, sourceZoneInfo.Name);
foreach (KeyValuePair userPermission in sourceZonePermissions.UserPermissions)
_authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, userPermission.Key, userPermission.Value);
foreach (KeyValuePair groupPermissions in sourceZonePermissions.GroupPermissions)
_authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, groupPermissions.Key, groupPermissions.Value);
//set default permissions
_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();
}
private void AuthZoneManager_SecondaryCatalogZoneRemoved(object sender, SecondaryCatalogEventArgs e)
{
_authManager.RemoveAllPermissions(PermissionSection.Zones, e.ZoneInfo.Name);
_authManager.SaveConfigFile();
}
#endregion
#region public
public async Task StartAsync()
{
if (_disposed)
ObjectDisposedException.ThrowIf(_disposed, this);
try
{
//get initial server domain
string dnsServerDomain = Environment.MachineName.ToLowerInvariant();
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.SecondaryCatalogZoneAdded += AuthZoneManager_SecondaryCatalogZoneAdded;
_dnsServer.AuthZoneManager.SecondaryCatalogZoneRemoved += AuthZoneManager_SecondaryCatalogZoneRemoved;
_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 ((_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count) > 0)
{
ThreadPool.QueueUserWorkItem(delegate (object state)
{
try
{
_dnsServer.BlockListZoneManager.LoadBlockLists();
}
catch (Exception ex)
{
_log.Write(ex);
}
});
if (_settingsApi.BlockListUpdateIntervalHours > 0)
_settingsApi.StartBlockListUpdateTimer(false);
}
//load dns cache async
if (_saveCache)
{
ThreadPool.QueueUserWorkItem(delegate (object state)
{
try
{
_dnsServer.CacheZoneManager.LoadCacheZoneFile();
}
catch (Exception ex)
{
_log.Write("Failed to fully load DNS Cache from disk\r\n" + ex.ToString());
}
});
}
//start web service
await TryStartWebServiceAsync([IPAddress.Any, IPAddress.IPv6Any], 5380, 53443);
//start dns and dhcp
await _dnsServer.StartAsync();
_dhcpServer.Start();
_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 async Task StopAsync()
{
if (_disposed || (_dnsServer is null))
return;
try
{
//stop dns
if (_dnsServer is not null)
await _dnsServer.DisposeAsync();
//stop dhcp
if (_dhcpServer is not null)
_dhcpServer.Dispose();
//stop web service
if (_settingsApi is not null)
{
_settingsApi.StopBlockListUpdateTimer();
_settingsApi.StopTemporaryDisableBlockingTimer();
}
StopTlsCertificateUpdateTimer();
await StopWebServiceAsync();
if (_saveCache)
{
try
{
_dnsServer.CacheZoneManager.SaveCacheZoneFile();
}
catch (Exception ex)
{
_log.Write(ex);
}
}
_log?.Write("DNS Server (v" + _currentVersion.ToString() + ") was stopped successfully.");
_dnsServer = null;
}
catch (Exception ex)
{
_log?.Write("Failed to stop DNS Server (v" + _currentVersion.ToString() + ")\r\n" + ex.ToString());
throw;
}
}
public void Start()
{
StartAsync().Sync();
}
public void Stop()
{
StopAsync().Sync();
}
#endregion
#region properties
internal DnsServer DnsServer
{ get { return _dnsServer; } }
internal DhcpServer DhcpServer
{ get { return _dhcpServer; } }
public string ConfigFolder
{ get { return _configFolder; } }
public int WebServiceHttpPort
{ get { return _webServiceHttpPort; } }
public int WebServiceTlsPort
{ get { return _webServiceTlsPort; } }
#endregion
}
}