From 0d00100cd2e63dc622b58b800a8d0048bca21dbd Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sat, 14 Jan 2023 14:59:50 +0530 Subject: [PATCH] DnsWebService: Implemented support for save dns cache feature. Added TryStartWebServiceAsync() to handle web service startup failures. Updated ConfigureWebServiceRoutes() to use MapGetAndPost() to allow all API calls to read from query string and form data. Updated config serializers to support new dns options. Code refactoring done. --- DnsServerCore/DnsWebService.cs | 818 ++++++++++++++------------------- 1 file changed, 334 insertions(+), 484 deletions(-) diff --git a/DnsServerCore/DnsWebService.cs b/DnsServerCore/DnsWebService.cs index a37de8ca..ff20f45c 100644 --- a/DnsServerCore/DnsWebService.cs +++ b/DnsServerCore/DnsWebService.cs @@ -20,7 +20,6 @@ along with this program. If not, see . using DnsServerCore.Auth; using DnsServerCore.Dhcp; using DnsServerCore.Dns; -using DnsServerCore.Dns.ResourceRecords; using DnsServerCore.Dns.ZoneManagers; using DnsServerCore.Dns.Zones; using Microsoft.AspNetCore.Builder; @@ -37,7 +36,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Net; -using System.Net.Http; +using System.Net.Quic; using System.Net.Sockets; using System.Reflection; using System.Security.Cryptography; @@ -62,23 +61,27 @@ namespace DnsServerCore 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; + readonly WebServiceApi _api; + readonly WebServiceDashboardApi _dashboardApi; internal readonly WebServiceZonesApi _zonesApi; - internal readonly WebServiceOtherZonesApi _otherZonesApi; + readonly WebServiceOtherZonesApi _otherZonesApi; internal readonly WebServiceAppsApi _appsApi; - internal readonly WebServiceDhcpApi _dhcpApi; - internal readonly WebServiceLogsApi _logsApi; + readonly WebServiceSettingsApi _settingsApi; + readonly WebServiceDhcpApi _dhcpApi; + readonly WebServiceAuthApi _authApi; + readonly WebServiceLogsApi _logsApi; - internal DnsServer _dnsServer; - internal DhcpServer _dhcpServer; + WebApplication _webService; + X509Certificate2 _webServiceTlsCertificate; + DnsServer _dnsServer; + DhcpServer _dhcpServer; + + //web service internal IReadOnlyList _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any }; internal int _webServiceHttpPort = 5380; internal int _webServiceTlsPort = 53443; @@ -87,16 +90,16 @@ namespace DnsServerCore internal bool _webServiceUseSelfSignedTlsCertificate; internal string _webServiceTlsCertificatePath; internal string _webServiceTlsCertificatePassword; - internal DateTime _webServiceTlsCertificateLastModifiedOn; - - WebApplication _webService; - X509Certificate2 _webServiceTlsCertificate; - readonly IndependentTaskScheduler _webServiceTaskScheduler = new IndependentTaskScheduler(ThreadPriority.AboveNormal); + DateTime _webServiceTlsCertificateLastModifiedOn; + //optional protocols internal string _dnsTlsCertificatePath; internal string _dnsTlsCertificatePassword; DateTime _dnsTlsCertificateLastModifiedOn; + //cache + internal bool _saveCache; + Timer _tlsCertificateUpdateTimer; const int TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL = 60000; const int TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL = 60000; @@ -119,21 +122,20 @@ namespace DnsServerCore 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); + _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); } @@ -150,17 +152,11 @@ namespace DnsServerCore await StopAsync(); - if (_settingsApi is not null) - _settingsApi.Dispose(); - if (_appsApi is not null) _appsApi.Dispose(); - if (_dnsServer is not null) - _dnsServer.Dispose(); - - if (_dhcpServer is not null) - _dhcpServer.Dispose(); + if (_settingsApi is not null) + _settingsApi.Dispose(); if (_authManager is not null) _authManager.Dispose(); @@ -178,11 +174,59 @@ namespace DnsServerCore #endregion - #region private + #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 StartWebServiceAsync() + internal async Task TryStartWebServiceAsync() + { + try + { + _webServiceLocalAddresses = DnsServer.GetValidKestralLocalAddresses(_webServiceLocalAddresses); + await StartWebServiceAsync(); + } + catch (Exception ex) + { + _log.Write("Web Service failed to start: " + ex.ToString()); + _log.Write("Attempting to start Web Service on ANY (0.0.0.0) fallback address..."); + + try + { + _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any }; + await StartWebServiceAsync(); + } + catch (Exception ex2) + { + _log.Write("Web Service failed to start: " + ex2.ToString()); + _log.Write("Attempting to start Web Service on loopback (127.0.0.1) fallback address..."); + + _webServiceLocalAddresses = new IPAddress[] { IPAddress.Loopback }; + await StartWebServiceAsync(); + } + } + } + + private async Task StartWebServiceAsync() { WebApplicationBuilder builder = WebApplication.CreateBuilder(); @@ -247,14 +291,41 @@ namespace DnsServerCore ConfigureWebServiceRoutes(); - await _webService.StartAsync(); + try + { + await _webService.StartAsync(); - _log.Write(new IPEndPoint(IPAddress.Any, _webServiceHttpPort), "Web Service was started successfully."); + foreach (IPAddress webServiceLocalAddress in _webServiceLocalAddresses) + { + _log?.Write(new IPEndPoint(webServiceLocalAddress, _webServiceHttpPort), "Http", "Web Service was bound successfully."); + + if (_webServiceEnableTls && (_webServiceTlsCertificate is not null)) + _log?.Write(new IPEndPoint(webServiceLocalAddress, _webServiceHttpPort), "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 (_webServiceEnableTls && (_webServiceTlsCertificate is not null)) + _log?.Write(new IPEndPoint(webServiceLocalAddress, _webServiceHttpPort), "Https", "Web Service failed to bind."); + } + + throw; + } } internal async Task StopWebServiceAsync() { - await _webService.DisposeAsync(); + if (_webService is not null) + { + await _webService.DisposeAsync(); + _webService = null; + } } private void ConfigureWebServiceRoutes() @@ -266,134 +337,134 @@ namespace DnsServerCore _webService.UseRouting(); //user auth - _webService.MapGet("/api/user/login", delegate (HttpContext context) { return _authApi.LoginAsync(context, UserSessionType.Standard); }); - _webService.MapGet("/api/user/createToken", delegate (HttpContext context) { return _authApi.LoginAsync(context, UserSessionType.ApiToken); }); - _webService.MapGet("/api/user/logout", _authApi.Logout); + _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.MapGet("/api/user/session/get", _authApi.GetCurrentSessionDetails); - _webService.MapGet("/api/user/session/delete", delegate (HttpContext context) { _authApi.DeleteSession(context, false); }); - _webService.MapGet("/api/user/changePassword", _authApi.ChangePassword); - _webService.MapGet("/api/user/profile/get", _authApi.GetProfile); - _webService.MapGet("/api/user/profile/set", _authApi.SetProfile); - _webService.MapGet("/api/user/checkForUpdate", CheckForUpdateAsync); + _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.MapGet("/api/dashboard/stats/get", _dashboardApi.GetStats); - _webService.MapGet("/api/dashboard/stats/getTop", _dashboardApi.GetTopStats); - _webService.MapGet("/api/dashboard/stats/deleteAll", _logsApi.DeleteAllStats); + _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.MapGet("/api/zones/list", _zonesApi.ListZones); - _webService.MapGet("/api/zones/create", _zonesApi.CreateZoneAsync); - _webService.MapGet("/api/zones/enable", _zonesApi.EnableZone); - _webService.MapGet("/api/zones/disable", _zonesApi.DisableZone); - _webService.MapGet("/api/zones/delete", _zonesApi.DeleteZone); - _webService.MapGet("/api/zones/resync", _zonesApi.ResyncZone); - _webService.MapGet("/api/zones/options/get", _zonesApi.GetZoneOptions); - _webService.MapGet("/api/zones/options/set", _zonesApi.SetZoneOptions); - _webService.MapGet("/api/zones/permissions/get", delegate (HttpContext context) { _authApi.GetPermissionDetails(context, PermissionSection.Zones); }); - _webService.MapGet("/api/zones/permissions/set", delegate (HttpContext context) { _authApi.SetPermissionsDetails(context, PermissionSection.Zones); }); - _webService.MapGet("/api/zones/dnssec/sign", _zonesApi.SignPrimaryZone); - _webService.MapGet("/api/zones/dnssec/unsign", _zonesApi.UnsignPrimaryZone); - _webService.MapGet("/api/zones/dnssec/properties/get", _zonesApi.GetPrimaryZoneDnssecProperties); - _webService.MapGet("/api/zones/dnssec/properties/convertToNSEC", _zonesApi.ConvertPrimaryZoneToNSEC); - _webService.MapGet("/api/zones/dnssec/properties/convertToNSEC3", _zonesApi.ConvertPrimaryZoneToNSEC3); - _webService.MapGet("/api/zones/dnssec/properties/updateNSEC3Params", _zonesApi.UpdatePrimaryZoneNSEC3Parameters); - _webService.MapGet("/api/zones/dnssec/properties/updateDnsKeyTtl", _zonesApi.UpdatePrimaryZoneDnssecDnsKeyTtl); - _webService.MapGet("/api/zones/dnssec/properties/generatePrivateKey", _zonesApi.GenerateAndAddPrimaryZoneDnssecPrivateKey); - _webService.MapGet("/api/zones/dnssec/properties/updatePrivateKey", _zonesApi.UpdatePrimaryZoneDnssecPrivateKey); - _webService.MapGet("/api/zones/dnssec/properties/deletePrivateKey", _zonesApi.DeletePrimaryZoneDnssecPrivateKey); - _webService.MapGet("/api/zones/dnssec/properties/publishAllPrivateKeys", _zonesApi.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys); - _webService.MapGet("/api/zones/dnssec/properties/rolloverDnsKey", _zonesApi.RolloverPrimaryZoneDnsKey); - _webService.MapGet("/api/zones/dnssec/properties/retireDnsKey", _zonesApi.RetirePrimaryZoneDnsKey); - _webService.MapGet("/api/zones/records/add", _zonesApi.AddRecord); - _webService.MapGet("/api/zones/records/get", _zonesApi.GetRecords); - _webService.MapGet("/api/zones/records/update", _zonesApi.UpdateRecord); - _webService.MapGet("/api/zones/records/delete", _zonesApi.DeleteRecord); + _webService.MapGetAndPost("/api/zones/list", _zonesApi.ListZones); + _webService.MapGetAndPost("/api/zones/create", _zonesApi.CreateZoneAsync); + _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/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.MapGet("/api/cache/list", _otherZonesApi.ListCachedZones); - _webService.MapGet("/api/cache/delete", _otherZonesApi.DeleteCachedZone); - _webService.MapGet("/api/cache/flush", _otherZonesApi.FlushCache); + _webService.MapGetAndPost("/api/cache/list", _otherZonesApi.ListCachedZones); + _webService.MapGetAndPost("/api/cache/delete", _otherZonesApi.DeleteCachedZone); + _webService.MapGetAndPost("/api/cache/flush", _otherZonesApi.FlushCache); //allowed - _webService.MapGet("/api/allowed/list", _otherZonesApi.ListAllowedZones); - _webService.MapGet("/api/allowed/add", _otherZonesApi.AllowZone); - _webService.MapGet("/api/allowed/delete", _otherZonesApi.DeleteAllowedZone); - _webService.MapGet("/api/allowed/flush", _otherZonesApi.FlushAllowedZone); - _webService.MapPost("/api/allowed/import", _otherZonesApi.ImportAllowedZones); - _webService.MapGet("/api/allowed/export", _otherZonesApi.ExportAllowedZonesAsync); + _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.MapGet("/api/blocked/list", _otherZonesApi.ListBlockedZones); - _webService.MapGet("/api/blocked/add", _otherZonesApi.BlockZone); - _webService.MapGet("/api/blocked/delete", _otherZonesApi.DeleteBlockedZone); - _webService.MapGet("/api/blocked/flush", _otherZonesApi.FlushBlockedZone); - _webService.MapPost("/api/blocked/import", _otherZonesApi.ImportBlockedZones); - _webService.MapGet("/api/blocked/export", _otherZonesApi.ExportBlockedZonesAsync); + _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.MapGet("/api/apps/list", _appsApi.ListInstalledAppsAsync); - _webService.MapGet("/api/apps/listStoreApps", _appsApi.ListStoreApps); - _webService.MapGet("/api/apps/downloadAndInstall", _appsApi.DownloadAndInstallAppAsync); - _webService.MapGet("/api/apps/downloadAndUpdate", _appsApi.DownloadAndUpdateAppAsync); + _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.MapGet("/api/apps/uninstall", _appsApi.UninstallApp); - _webService.MapGet("/api/apps/config/get", _appsApi.GetAppConfigAsync); - _webService.MapPost("/api/apps/config/set", _appsApi.SetAppConfigAsync); + _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.MapGet("/api/dnsClient/resolve", ResolveQueryAsync); + _webService.MapGetAndPost("/api/dnsClient/resolve", _api.ResolveQueryAsync); //settings - _webService.MapGet("/api/settings/get", _settingsApi.GetDnsSettings); - _webService.MapGet("/api/settings/set", _settingsApi.SetDnsSettings); - _webService.MapGet("/api/settings/getTsigKeyNames", _settingsApi.GetTsigKeyNames); - _webService.MapGet("/api/settings/forceUpdateBlockLists", _settingsApi.ForceUpdateBlockLists); - _webService.MapGet("/api/settings/temporaryDisableBlocking", _settingsApi.TemporaryDisableBlocking); - _webService.MapGet("/api/settings/backup", _settingsApi.BackupSettingsAsync); + _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.MapGet("/api/dhcp/leases/list", _dhcpApi.ListDhcpLeases); - _webService.MapGet("/api/dhcp/leases/remove", _dhcpApi.RemoveDhcpLease); - _webService.MapGet("/api/dhcp/leases/convertToReserved", _dhcpApi.ConvertToReservedLease); - _webService.MapGet("/api/dhcp/leases/convertToDynamic", _dhcpApi.ConvertToDynamicLease); - _webService.MapGet("/api/dhcp/scopes/list", _dhcpApi.ListDhcpScopes); - _webService.MapGet("/api/dhcp/scopes/get", _dhcpApi.GetDhcpScope); - _webService.MapGet("/api/dhcp/scopes/set", _dhcpApi.SetDhcpScopeAsync); - _webService.MapGet("/api/dhcp/scopes/addReservedLease", _dhcpApi.AddReservedLease); - _webService.MapGet("/api/dhcp/scopes/removeReservedLease", _dhcpApi.RemoveReservedLease); - _webService.MapGet("/api/dhcp/scopes/enable", _dhcpApi.EnableDhcpScopeAsync); - _webService.MapGet("/api/dhcp/scopes/disable", _dhcpApi.DisableDhcpScope); - _webService.MapGet("/api/dhcp/scopes/delete", _dhcpApi.DeleteDhcpScope); + _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.MapGet("/api/admin/sessions/list", _authApi.ListSessions); - _webService.MapGet("/api/admin/sessions/createToken", _authApi.CreateApiToken); - _webService.MapGet("/api/admin/sessions/delete", delegate (HttpContext context) { _authApi.DeleteSession(context, true); }); - _webService.MapGet("/api/admin/users/list", _authApi.ListUsers); - _webService.MapGet("/api/admin/users/create", _authApi.CreateUser); - _webService.MapGet("/api/admin/users/get", _authApi.GetUserDetails); - _webService.MapGet("/api/admin/users/set", _authApi.SetUserDetails); - _webService.MapGet("/api/admin/users/delete", _authApi.DeleteUser); - _webService.MapGet("/api/admin/groups/list", _authApi.ListGroups); - _webService.MapGet("/api/admin/groups/create", _authApi.CreateGroup); - _webService.MapGet("/api/admin/groups/get", _authApi.GetGroupDetails); - _webService.MapGet("/api/admin/groups/set", _authApi.SetGroupDetails); - _webService.MapGet("/api/admin/groups/delete", _authApi.DeleteGroup); - _webService.MapGet("/api/admin/permissions/list", _authApi.ListPermissions); - _webService.MapGet("/api/admin/permissions/get", delegate (HttpContext context) { _authApi.GetPermissionDetails(context, PermissionSection.Unknown); }); - _webService.MapGet("/api/admin/permissions/set", delegate (HttpContext context) { _authApi.SetPermissionsDetails(context, PermissionSection.Unknown); }); + _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.MapGet("/api/logs/list", _logsApi.ListLogs); - _webService.MapGet("/api/logs/download", _logsApi.DownloadLogAsync); - _webService.MapGet("/api/logs/delete", _logsApi.DeleteLog); - _webService.MapGet("/api/logs/deleteAll", _logsApi.DeleteAllLogs); - _webService.MapGet("/api/logs/query", _logsApi.QueryLogsAsync); + _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 async Task WebServiceApiMiddleware(HttpContext context, RequestDelegate next) @@ -521,7 +592,7 @@ namespace DnsServerCore private bool TryGetSession(HttpContext context, out UserSession session) { - string token = context.Request.GetQuery("token"); + string token = context.Request.GetQueryOrForm("token"); session = _authManager.GetSession(token); if ((session is null) || session.User.Disabled) return false; @@ -541,337 +612,11 @@ namespace DnsServerCore #endregion - #region update api - - private async Task CheckForUpdateAsync(HttpContext context) - { - Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter(); - - 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(context.GetRemoteEndPoint(), strLog); - } - } - catch (Exception ex) - { - _log.Write(context.GetRemoteEndPoint(), "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(HttpContext context) - { - UserSession session = context.GetCurrentSession(); - - if (!_authManager.IsPermitted(PermissionSection.DnsClient, session.User, PermissionFlag.View)) - throw new DnsWebServiceException("Access was denied."); - - HttpRequest request = context.Request; - - string server = request.GetQuery("server"); - string domain = request.GetQuery("domain").Trim(new char[] { '\t', ' ', '.' }); - DnsResourceRecordType type = request.GetQuery("type"); - DnsTransportProtocol protocol = request.GetQuery("protocol", DnsTransportProtocol.Udp); - bool dnssecValidation = request.GetQuery("dnssec", bool.Parse, false); - bool importResponse = request.GetQuery("import", bool.Parse, false); - NetProxy proxy = _dnsServer.Proxy; - bool preferIPv6 = _dnsServer.PreferIPv6; - ushort udpPayloadSize = _dnsServer.UdpPayloadSize; - bool randomizeName = false; - bool qnameMinimization = _dnsServer.QnameMinimization; - 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 = NameServerAddress.Parse(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) - { - 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 importRecords = new List(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(context.GetRemoteEndPoint(), "[" + session.User.Username + "] DNS Client imported record(s) for authoritative zone {server: " + server + "; zone: " + zoneInfo.Name + "; type: " + type + ";}"); - - _dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); - } - - Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter(); - - if (dnssecErrorMessage is not null) - jsonWriter.WriteString("warningMessage", dnssecErrorMessage); - - jsonWriter.WritePropertyName("result"); - dnsResponse.SerializeTo(jsonWriter); - } - - #endregion - #region tls internal void StartTlsCertificateUpdateTimer() { - if (_tlsCertificateUpdateTimer == null) + if (_tlsCertificateUpdateTimer is null) { _tlsCertificateUpdateTimer = new Timer(delegate (object state) { @@ -911,7 +656,7 @@ namespace DnsServerCore internal void StopTlsCertificateUpdateTimer() { - if (_tlsCertificateUpdateTimer != null) + if (_tlsCertificateUpdateTimer is not null) { _tlsCertificateUpdateTimer.Dispose(); _tlsCertificateUpdateTimer = null; @@ -928,9 +673,7 @@ namespace DnsServerCore 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; + _webServiceTlsCertificate = new X509Certificate2(tlsCertificatePath, tlsCertificatePassword); _webServiceTlsCertificateLastModifiedOn = fileInfo.LastWriteTimeUtc; _log.Write("Web Service TLS certificate was loaded: " + tlsCertificatePath); @@ -946,9 +689,7 @@ namespace DnsServerCore 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; + _dnsServer.Certificate = new X509Certificate2(tlsCertificatePath, tlsCertificatePassword); _dnsTlsCertificateLastModifiedOn = fileInfo.LastWriteTimeUtc; _log.Write("DNS Server TLS certificate was loaded: " + tlsCertificatePath); @@ -992,6 +733,22 @@ namespace DnsServerCore #endregion + #region quic + + internal static void ValidateQuicSupport() + { +#pragma warning disable CA2252 // This API requires opting into preview features +#pragma warning disable CA1416 // Validate platform compatibility + + if (!QuicConnection.IsSupported) + throw new DnsWebServiceException("DNS-over-QUIC is supported only on Windows 11, Windows Server 2022, and Linux. On Linux, you must install 'libmsquic' and OpenSSL v1.1.1 manually."); + +#pragma warning restore CA1416 // Validate platform compatibility +#pragma warning restore CA2252 // This API requires opting into preview features + } + + #endregion + #region config internal void LoadConfigFile() @@ -1219,7 +976,7 @@ namespace DnsServerCore int version = bR.ReadByte(); - if ((version >= 28) && (version <= 29)) + if ((version >= 28) && (version <= 30)) { ReadConfigFrom(bR, version); } @@ -1333,11 +1090,45 @@ namespace DnsServerCore _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 _dnsServer.EnableDnsOverHttp = bR.ReadBoolean(); _dnsServer.EnableDnsOverTls = bR.ReadBoolean(); _dnsServer.EnableDnsOverHttps = bR.ReadBoolean(); + if (version >= 30) + { + _dnsServer.EnableDnsOverHttpPort80 = bR.ReadBoolean(); + _dnsServer.EnableDnsOverQuic = bR.ReadBoolean(); + + _dnsServer.DnsOverHttpPort = bR.ReadInt32(); + _dnsServer.DnsOverTlsPort = bR.ReadInt32(); + _dnsServer.DnsOverHttpsPort = bR.ReadInt32(); + _dnsServer.DnsOverQuicPort = bR.ReadInt32(); + } + else + { + _dnsServer.EnableDnsOverHttpPort80 = _dnsServer.EnableDnsOverHttps; + _dnsServer.EnableDnsOverQuic = false; + + _dnsServer.DnsOverHttpPort = 8053; + _dnsServer.DnsOverTlsPort = 853; + _dnsServer.DnsOverHttpsPort = 443; + _dnsServer.DnsOverQuicPort = 853; + } + _dnsTlsCertificatePath = bR.ReadShortString(); _dnsTlsCertificatePassword = bR.ReadShortString(); @@ -1413,6 +1204,11 @@ namespace DnsServerCore _dnsServer.ResolverMaxStackCount = bR.ReadInt32(); //cache + if (version >= 30) + _saveCache = bR.ReadBoolean(); + else + _saveCache = false; + _dnsServer.ServeStale = bR.ReadBoolean(); _dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32(); @@ -2102,7 +1898,7 @@ namespace DnsServerCore private void WriteConfigTo(BinaryWriter bW) { bW.Write(Encoding.ASCII.GetBytes("DS")); //format - bW.Write((byte)29); //version + bW.Write((byte)30); //version //web service { @@ -2163,11 +1959,21 @@ namespace DnsServerCore 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.EnableDnsOverHttp); bW.Write(_dnsServer.EnableDnsOverTls); bW.Write(_dnsServer.EnableDnsOverHttps); + bW.Write(_dnsServer.EnableDnsOverHttpPort80); + bW.Write(_dnsServer.EnableDnsOverQuic); + + bW.Write(_dnsServer.DnsOverHttpPort); + bW.Write(_dnsServer.DnsOverTlsPort); + bW.Write(_dnsServer.DnsOverHttpsPort); + bW.Write(_dnsServer.DnsOverQuicPort); if (_dnsTlsCertificatePath == null) bW.WriteShortString(string.Empty); @@ -2230,6 +2036,7 @@ namespace DnsServerCore bW.Write(_dnsServer.ResolverMaxStackCount); //cache + bW.Write(_saveCache); bW.Write(_dnsServer.ServeStale); bW.Write(_dnsServer.CacheZoneManager.ServeStaleTtl); @@ -2330,8 +2137,6 @@ namespace DnsServerCore #endregion - #endregion - #region public public async Task StartAsync() @@ -2402,12 +2207,28 @@ namespace DnsServerCore }); } - //start dns and dhcp - _dnsServer.Start(); - _dhcpServer.Start(); + //load dns cache async + if (_saveCache) + { + ThreadPool.QueueUserWorkItem(delegate (object state) + { + try + { + _dnsServer.CacheZoneManager.LoadCacheZoneFile(); + } + catch (Exception ex) + { + _log.Write(ex); + } + }); + } //start web service - await StartWebServiceAsync(); + await TryStartWebServiceAsync(); + + //start dns and dhcp + await _dnsServer.StartAsync(); + _dhcpServer.Start(); _log.Write("DNS Server (v" + _currentVersion.ToString() + ") was started successfully."); } @@ -2425,19 +2246,42 @@ namespace DnsServerCore try { - await StopWebServiceAsync(); - _dnsServer.Dispose(); - _dhcpServer.Dispose(); + //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(); + } - _settingsApi.StopBlockListUpdateTimer(); - _settingsApi.StopTemporaryDisableBlockingTimer(); StopTlsCertificateUpdateTimer(); - _log.Write("DNS Server (v" + _currentVersion.ToString() + ") was stopped successfully."); + await StopWebServiceAsync(); + + if (_saveCache) + { + try + { + _dnsServer.CacheZoneManager.SaveCacheZoneFile(); + } + catch (Exception ex) + { + _log.Write(ex); + } + } + + _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()); + _log?.Write("Failed to stop DNS Server (v" + _currentVersion.ToString() + ")\r\n" + ex.ToString()); throw; } } @@ -2456,6 +2300,12 @@ namespace DnsServerCore #region properties + internal DnsServer DnsServer + { get { return _dnsServer; } } + + internal DhcpServer DhcpServer + { get { return _dhcpServer; } } + public string ConfigFolder { get { return _configFolder; } }