diff --git a/DnsServerCore/DnsWebService.cs b/DnsServerCore/DnsWebService.cs
index 8f760e51..41e1164c 100644
--- a/DnsServerCore/DnsWebService.cs
+++ b/DnsServerCore/DnsWebService.cs
@@ -17,6 +17,7 @@ along with this program. If not, see .
*/
+using DnsServerCore.Auth;
using DnsServerCore.Dhcp;
using DnsServerCore.Dns;
using DnsServerCore.Dns.ResourceRecords;
@@ -25,7 +26,6 @@ using DnsServerCore.Dns.Zones;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
@@ -67,6 +67,10 @@ namespace DnsServerCore
readonly static RandomNumberGenerator _rng = RandomNumberGenerator.Create();
+ readonly LogManager _log;
+ readonly AuthManager _authManager;
+
+ readonly WebServiceAuthApi _authApi;
readonly WebServiceDashboardApi _dashboardApi;
readonly WebServiceZonesApi _zonesApi;
readonly WebServiceOtherZonesApi _otherZonesApi;
@@ -79,8 +83,6 @@ namespace DnsServerCore
readonly string _configFolder;
readonly Uri _updateCheckUri;
- readonly LogManager _log;
-
DnsServer _dnsServer;
DhcpServer _dhcpServer;
@@ -109,13 +111,6 @@ namespace DnsServerCore
const int TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL = 60000;
const int TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL = 60000;
- const int MAX_LOGIN_ATTEMPTS = 5;
- const int BLOCK_ADDRESS_INTERVAL = 5 * 60 * 1000;
- readonly ConcurrentDictionary _failedLoginAttempts = new ConcurrentDictionary();
- readonly ConcurrentDictionary _blockedAddresses = new ConcurrentDictionary();
- readonly ConcurrentDictionary _credentials = new ConcurrentDictionary();
- readonly ConcurrentDictionary _sessions = new ConcurrentDictionary();
-
volatile ServiceState _state = ServiceState.Stopped;
Timer _blockListUpdateTimer;
@@ -135,20 +130,12 @@ namespace DnsServerCore
public DnsWebService(string configFolder = null, Uri updateCheckUri = null, Uri appStoreUri = null)
{
- _dashboardApi = new WebServiceDashboardApi(this);
- _zonesApi = new WebServiceZonesApi(this);
- _otherZonesApi = new WebServiceOtherZonesApi(this);
- _appsApi = new WebServiceAppsApi(this, appStoreUri);
- _dhcpApi = new WebServiceDhcpApi(this);
- _logsApi = new WebServiceLogsApi(this);
-
Assembly assembly = Assembly.GetExecutingAssembly();
- AssemblyName assemblyName = assembly.GetName();
- _currentVersion = assemblyName.Version;
+ _currentVersion = assembly.GetName().Version;
_appFolder = Path.GetDirectoryName(assembly.Location);
- if (configFolder == null)
+ if (configFolder is null)
_configFolder = Path.Combine(_appFolder, "config");
else
_configFolder = configFolder;
@@ -159,6 +146,15 @@ namespace DnsServerCore
_updateCheckUri = updateCheckUri;
_log = new LogManager(_configFolder);
+ _authManager = new AuthManager(_configFolder, _log);
+
+ _authApi = new WebServiceAuthApi(this);
+ _dashboardApi = new WebServiceDashboardApi(this);
+ _zonesApi = new WebServiceZonesApi(this);
+ _otherZonesApi = new WebServiceOtherZonesApi(this);
+ _appsApi = new WebServiceAppsApi(this, appStoreUri);
+ _dhcpApi = new WebServiceDhcpApi(this);
+ _logsApi = new WebServiceLogsApi(this);
string blockListsFolder = Path.Combine(_configFolder, "blocklists");
@@ -179,16 +175,22 @@ namespace DnsServerCore
Stop();
- if (_webService != null)
+ if (_appsApi is not null)
+ _appsApi.Dispose();
+
+ if (_webService is not null)
_webService.Close();
- if (_dnsServer != null)
+ if (_dnsServer is not null)
_dnsServer.Dispose();
- if (_dhcpServer != null)
+ if (_dhcpServer is not null)
_dhcpServer.Dispose();
- if (_log != null)
+ if (_authManager is not null)
+ _authManager.Dispose();
+
+ if (_log is not null)
_log.Dispose();
_disposed = true;
@@ -374,16 +376,26 @@ namespace DnsServerCore
switch (path)
{
+ case "/api/user/login":
case "/api/login":
- await LoginAsync(request, jsonWriter);
+ await _authApi.LoginAsync(request, jsonWriter, UserSessionType.Standard);
break;
+ case "/api/user/createToken":
+ await _authApi.LoginAsync(request, jsonWriter, UserSessionType.ApiToken);
+ break;
+
+ case "/api/user/logout":
case "/api/logout":
- Logout(request);
+ _authApi.Logout(request);
+ break;
+
+ case "/api/user/session/get":
+ _authApi.GetCurrentSessionDetails(request, jsonWriter);
break;
default:
- if (!IsSessionValid(request))
+ if (!TryGetSession(request, out UserSession session))
throw new InvalidTokenWebServiceException("Invalid token or session expired.");
jsonWriter.WritePropertyName("response");
@@ -393,315 +405,756 @@ namespace DnsServerCore
{
switch (path)
{
- case "/api/changePassword":
- ChangePassword(request);
+ case "/api/user/session/delete":
+ _authApi.DeleteSession(request, false);
break;
+ case "/api/user/changePassword":
+ case "/api/changePassword":
+ _authApi.ChangePassword(request);
+ break;
+
+ case "/api/user/profile/get":
+ _authApi.GetProfile(request, jsonWriter);
+ break;
+
+ case "/api/user/profile/set":
+ _authApi.SetProfile(request, jsonWriter);
+ break;
+
+ case "/api/user/checkForUpdate":
case "/api/checkForUpdate":
await CheckForUpdateAsync(request, jsonWriter);
break;
- case "/api/getDnsSettings":
- GetDnsSettings(jsonWriter);
- break;
-
- case "/api/setDnsSettings":
- SetDnsSettings(request, jsonWriter);
- break;
-
- case "/api/forceUpdateBlockLists":
- ForceUpdateBlockLists(request);
- break;
-
- case "/api/temporaryDisableBlocking":
- TemporaryDisableBlocking(request, jsonWriter);
- break;
-
- case "/api/backupSettings":
- await BackupSettingsAsync(request, response);
- return;
-
- case "/api/restoreSettings":
- await RestoreSettingsAsync(request, jsonWriter);
- break;
-
+ case "/api/dashboard/stats/get":
case "/api/getStats":
+ if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
await _dashboardApi.GetStats(request, jsonWriter);
break;
+ case "/api/dashboard/stats/getTop":
case "/api/getTopStats":
+ if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
await _dashboardApi.GetTopStats(request, jsonWriter);
break;
- case "/api/flushDnsCache":
- _otherZonesApi.FlushCache(request);
- break;
-
- case "/api/listCachedZones":
- _otherZonesApi.ListCachedZones(request, jsonWriter);
- break;
-
- case "/api/deleteCachedZone":
- _otherZonesApi.DeleteCachedZone(request);
- break;
-
- case "/api/listAllowedZones":
- _otherZonesApi.ListAllowedZones(request, jsonWriter);
- break;
-
- case "/api/importAllowedZones":
- await _otherZonesApi.ImportAllowedZonesAsync(request);
- break;
-
- case "/api/exportAllowedZones":
- _otherZonesApi.ExportAllowedZones(response);
- return;
-
- case "/api/deleteAllowedZone":
- _otherZonesApi.DeleteAllowedZone(request);
- break;
-
- case "/api/flushAllowedZone":
- _otherZonesApi.FlushAllowedZone(request);
- break;
-
- case "/api/allowZone":
- _otherZonesApi.AllowZone(request);
- break;
-
- case "/api/listBlockedZones":
- _otherZonesApi.ListBlockedZones(request, jsonWriter);
- break;
-
- case "/api/importBlockedZones":
- await _otherZonesApi.ImportBlockedZonesAsync(request);
- break;
-
- case "/api/exportBlockedZones":
- _otherZonesApi.ExportBlockedZones(response);
- return;
-
- case "/api/deleteBlockedZone":
- _otherZonesApi.DeleteBlockedZone(request);
- break;
-
- case "/api/flushBlockedZone":
- _otherZonesApi.FlushBlockedZone(request);
- break;
-
- case "/api/blockZone":
- _otherZonesApi.BlockZone(request);
+ case "/api/dashboard/stats/deleteAll":
+ case "/api/deleteAllStats":
+ if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _logsApi.DeleteAllStats(request);
break;
+ case "/api/zones/list":
case "/api/zone/list":
case "/api/listZones":
- _zonesApi.ListZones(jsonWriter);
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.ListZones(request, jsonWriter);
break;
+ case "/api/zones/create":
case "/api/zone/create":
case "/api/createZone":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
await _zonesApi.CreateZoneAsync(request, jsonWriter);
break;
- case "/api/zone/dnssec/sign":
- _zonesApi.SignPrimaryZone(request);
- break;
-
- case "/api/zone/dnssec/unsign":
- _zonesApi.UnsignPrimaryZone(request);
- break;
-
- case "/api/zone/dnssec/getProperties":
- _zonesApi.GetPrimaryZoneDnssecProperties(request, jsonWriter);
- break;
-
- case "/api/zone/dnssec/convertToNSEC":
- _zonesApi.ConvertPrimaryZoneToNSEC(request);
- break;
-
- case "/api/zone/dnssec/convertToNSEC3":
- _zonesApi.ConvertPrimaryZoneToNSEC3(request);
- break;
-
- case "/api/zone/dnssec/updateNSEC3Params":
- _zonesApi.UpdatePrimaryZoneNSEC3Parameters(request);
- break;
-
- case "/api/zone/dnssec/updateDnsKeyTtl":
- _zonesApi.UpdatePrimaryZoneDnssecDnsKeyTtl(request);
- break;
-
- case "/api/zone/dnssec/generatePrivateKey":
- _zonesApi.GenerateAndAddPrimaryZoneDnssecPrivateKey(request);
- break;
-
- case "/api/zone/dnssec/updatePrivateKey":
- _zonesApi.UpdatePrimaryZoneDnssecPrivateKey(request);
- break;
-
- case "/api/zone/dnssec/deletePrivateKey":
- _zonesApi.DeletePrimaryZoneDnssecPrivateKey(request);
- break;
-
- case "/api/zone/dnssec/publishAllPrivateKeys":
- _zonesApi.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(request);
- break;
-
- case "/api/zone/dnssec/rolloverDnsKey":
- _zonesApi.RolloverPrimaryZoneDnsKey(request);
- break;
-
- case "/api/zone/dnssec/retireDnsKey":
- _zonesApi.RetirePrimaryZoneDnsKey(request);
- break;
-
- case "/api/zone/delete":
- case "/api/deleteZone":
- _zonesApi.DeleteZone(request);
- break;
-
+ case "/api/zones/enable":
case "/api/zone/enable":
case "/api/enableZone":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
_zonesApi.EnableZone(request);
break;
+ case "/api/zones/disable":
case "/api/zone/disable":
case "/api/disableZone":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
_zonesApi.DisableZone(request);
break;
- case "/api/zone/options/get":
- _zonesApi.GetZoneOptions(request, jsonWriter);
- break;
-
- case "/api/zone/options/set":
- _zonesApi.SetZoneOptions(request);
+ case "/api/zones/delete":
+ case "/api/zone/delete":
+ case "/api/deleteZone":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.DeleteZone(request);
break;
+ case "/api/zones/resync":
case "/api/zone/resync":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
_zonesApi.ResyncZone(request);
break;
+ case "/api/zones/options/get":
+ case "/api/zone/options/get":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.GetZoneOptions(request, jsonWriter);
+ break;
+
+ case "/api/zones/options/set":
+ case "/api/zone/options/set":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.SetZoneOptions(request);
+ break;
+
+ case "/api/zones/permissions/get":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.GetPermissionDetails(request, jsonWriter, PermissionSection.Zones);
+ break;
+
+ case "/api/zones/permissions/set":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.SetPermissionsDetails(request, jsonWriter, PermissionSection.Zones);
+ break;
+
+ case "/api/zones/dnssec/sign":
+ case "/api/zone/dnssec/sign":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.SignPrimaryZone(request);
+ break;
+
+ case "/api/zones/dnssec/unsign":
+ case "/api/zone/dnssec/unsign":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.UnsignPrimaryZone(request);
+ break;
+
+ case "/api/zones/dnssec/properties/get":
+ case "/api/zone/dnssec/getProperties":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.GetPrimaryZoneDnssecProperties(request, jsonWriter);
+ break;
+
+ case "/api/zones/dnssec/properties/convertToNSEC":
+ case "/api/zone/dnssec/convertToNSEC":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.ConvertPrimaryZoneToNSEC(request);
+ break;
+
+ case "/api/zones/dnssec/properties/convertToNSEC3":
+ case "/api/zone/dnssec/convertToNSEC3":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.ConvertPrimaryZoneToNSEC3(request);
+ break;
+
+ case "/api/zones/dnssec/properties/updateNSEC3Params":
+ case "/api/zone/dnssec/updateNSEC3Params":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.UpdatePrimaryZoneNSEC3Parameters(request);
+ break;
+
+ case "/api/zones/dnssec/properties/updateDnsKeyTtl":
+ case "/api/zone/dnssec/updateDnsKeyTtl":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.UpdatePrimaryZoneDnssecDnsKeyTtl(request);
+ break;
+
+ case "/api/zones/dnssec/properties/generatePrivateKey":
+ case "/api/zone/dnssec/generatePrivateKey":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.GenerateAndAddPrimaryZoneDnssecPrivateKey(request);
+ break;
+
+ case "/api/zones/dnssec/properties/updatePrivateKey":
+ case "/api/zone/dnssec/updatePrivateKey":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.UpdatePrimaryZoneDnssecPrivateKey(request);
+ break;
+
+ case "/api/zones/dnssec/properties/deletePrivateKey":
+ case "/api/zone/dnssec/deletePrivateKey":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.DeletePrimaryZoneDnssecPrivateKey(request);
+ break;
+
+ case "/api/zones/dnssec/properties/publishAllPrivateKeys":
+ case "/api/zone/dnssec/publishAllPrivateKeys":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(request);
+ break;
+
+ case "/api/zones/dnssec/properties/rolloverDnsKey":
+ case "/api/zone/dnssec/rolloverDnsKey":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.RolloverPrimaryZoneDnsKey(request);
+ break;
+
+ case "/api/zones/dnssec/properties/retireDnsKey":
+ case "/api/zone/dnssec/retireDnsKey":
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _zonesApi.RetirePrimaryZoneDnsKey(request);
+ break;
+
+ case "/api/zones/records/add":
case "/api/zone/addRecord":
case "/api/addRecord":
_zonesApi.AddRecord(request, jsonWriter);
break;
+ case "/api/zones/records/get":
case "/api/zone/getRecords":
case "/api/getRecords":
_zonesApi.GetRecords(request, jsonWriter);
break;
- case "/api/zone/deleteRecord":
- case "/api/deleteRecord":
- _zonesApi.DeleteRecord(request);
- break;
-
+ case "/api/zones/records/update":
case "/api/zone/updateRecord":
case "/api/updateRecord":
_zonesApi.UpdateRecord(request, jsonWriter);
break;
+ case "/api/zones/records/delete":
+ case "/api/zone/deleteRecord":
+ case "/api/deleteRecord":
+ _zonesApi.DeleteRecord(request);
+ break;
+
+ case "/api/cache/list":
+ case "/api/listCachedZones":
+ if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.ListCachedZones(request, jsonWriter);
+ break;
+
+ case "/api/cache/delete":
+ case "/api/deleteCachedZone":
+ if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.DeleteCachedZone(request);
+ break;
+
+ case "/api/cache/flush":
+ case "/api/flushDnsCache":
+ if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.FlushCache(request);
+ break;
+
+ case "/api/allowed/list":
+ case "/api/listAllowedZones":
+ if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.ListAllowedZones(request, jsonWriter);
+ break;
+
+ case "/api/allowed/add":
+ case "/api/allowZone":
+ if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.AllowZone(request);
+ break;
+
+ case "/api/allowed/delete":
+ case "/api/deleteAllowedZone":
+ if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.DeleteAllowedZone(request);
+ break;
+
+ case "/api/allowed/flush":
+ case "/api/flushAllowedZone":
+ if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.FlushAllowedZone(request);
+ break;
+
+ case "/api/allowed/import":
+ case "/api/importAllowedZones":
+ if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await _otherZonesApi.ImportAllowedZonesAsync(request);
+ break;
+
+ case "/api/allowed/export":
+ case "/api/exportAllowedZones":
+ if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.ExportAllowedZones(response);
+ return;
+
+ case "/api/blocked/list":
+ case "/api/listBlockedZones":
+ if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.ListBlockedZones(request, jsonWriter);
+ break;
+
+ case "/api/blocked/add":
+ case "/api/blockZone":
+ if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.BlockZone(request);
+ break;
+
+ case "/api/blocked/delete":
+ case "/api/deleteBlockedZone":
+ if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.DeleteBlockedZone(request);
+ break;
+
+ case "/api/blocked/flush":
+ case "/api/flushBlockedZone":
+ if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.FlushBlockedZone(request);
+ break;
+
+ case "/api/blocked/import":
+ case "/api/importBlockedZones":
+ if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await _otherZonesApi.ImportBlockedZonesAsync(request);
+ break;
+
+ case "/api/blocked/export":
+ case "/api/exportBlockedZones":
+ if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _otherZonesApi.ExportBlockedZones(response);
+ return;
+
case "/api/apps/list":
- await _appsApi.ListInstalledAppsAsync(jsonWriter);
+ if (_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View) ||
+ _authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View) ||
+ _authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
+ {
+ await _appsApi.ListInstalledAppsAsync(jsonWriter);
+ }
+ else
+ {
+ throw new DnsWebServiceException("Access was denied.");
+ }
+
break;
case "/api/apps/listStoreApps":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
await _appsApi.ListStoreApps(jsonWriter);
break;
case "/api/apps/downloadAndInstall":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
await _appsApi.DownloadAndInstallAppAsync(request);
break;
case "/api/apps/downloadAndUpdate":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
await _appsApi.DownloadAndUpdateAppAsync(request);
break;
case "/api/apps/install":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
await _appsApi.InstallAppAsync(request);
break;
case "/api/apps/update":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
await _appsApi.UpdateAppAsync(request);
break;
case "/api/apps/uninstall":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
_appsApi.UninstallApp(request);
break;
+ case "/api/apps/config/get":
case "/api/apps/getConfig":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
await _appsApi.GetAppConfigAsync(request, jsonWriter);
break;
+ case "/api/apps/config/set":
case "/api/apps/setConfig":
+ if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
await _appsApi.SetAppConfigAsync(request);
break;
+ case "/api/dnsClient/resolve":
case "/api/resolveQuery":
+ if (!_authManager.IsPermitted(PermissionSection.DnsClient, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
await ResolveQueryAsync(request, jsonWriter);
break;
- case "/api/listLogs":
- _logsApi.ListLogs(jsonWriter);
+ case "/api/settings/get":
+ case "/api/getDnsSettings":
+ if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ GetDnsSettings(jsonWriter);
break;
- case "/api/deleteLog":
- _logsApi.DeleteLog(request);
+ case "/api/settings/set":
+ case "/api/setDnsSettings":
+ if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ SetDnsSettings(request, jsonWriter);
break;
- case "/api/deleteAllLogs":
- _logsApi.DeleteAllLogs(request);
+ case "/api/settings/forceUpdateBlockLists":
+ case "/api/forceUpdateBlockLists":
+ if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ ForceUpdateBlockLists(request);
break;
- case "/api/deleteAllStats":
- _logsApi.DeleteAllStats(request);
+ case "/api/settings/temporaryDisableBlocking":
+ case "/api/temporaryDisableBlocking":
+ if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ TemporaryDisableBlocking(request, jsonWriter);
break;
- case "/api/queryLogs":
- await _logsApi.QueryLogsAsync(request, jsonWriter);
- break;
-
- case "/api/listDhcpScopes":
- _dhcpApi.ListDhcpScopes(jsonWriter);
+ case "/api/settings/backup":
+ case "/api/backupSettings":
+ if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await BackupSettingsAsync(request, response);
+ return;
+
+ case "/api/settings/restore":
+ case "/api/restoreSettings":
+ if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await RestoreSettingsAsync(request, jsonWriter);
break;
+ case "/api/dhcp/leases/list":
case "/api/listDhcpLeases":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
_dhcpApi.ListDhcpLeases(jsonWriter);
break;
- case "/api/getDhcpScope":
- _dhcpApi.GetDhcpScope(request, jsonWriter);
- break;
-
- case "/api/setDhcpScope":
- await _dhcpApi.SetDhcpScopeAsync(request);
- break;
-
- case "/api/enableDhcpScope":
- await _dhcpApi.EnableDhcpScopeAsync(request);
- break;
-
- case "/api/disableDhcpScope":
- _dhcpApi.DisableDhcpScope(request);
- break;
-
- case "/api/deleteDhcpScope":
- _dhcpApi.DeleteDhcpScope(request);
- break;
-
+ case "/api/dhcp/leases/remove":
case "/api/removeDhcpLease":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
_dhcpApi.RemoveDhcpLease(request);
break;
+ case "/api/dhcp/leases/convertToReserved":
case "/api/convertToReservedLease":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
_dhcpApi.ConvertToReservedLease(request);
break;
+ case "/api/dhcp/leases/convertToDynamic":
case "/api/convertToDynamicLease":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
_dhcpApi.ConvertToDynamicLease(request);
break;
+ case "/api/dhcp/scopes/list":
+ case "/api/listDhcpScopes":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _dhcpApi.ListDhcpScopes(jsonWriter);
+ break;
+
+ case "/api/dhcp/scopes/get":
+ case "/api/getDhcpScope":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _dhcpApi.GetDhcpScope(request, jsonWriter);
+ break;
+
+ case "/api/dhcp/scopes/set":
+ case "/api/setDhcpScope":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await _dhcpApi.SetDhcpScopeAsync(request);
+ break;
+
+ case "/api/dhcp/scopes/addReservedLease":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _dhcpApi.AddReservedLease(request);
+ break;
+
+ case "/api/dhcp/scopes/removeReservedLease":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _dhcpApi.RemoveReservedLease(request);
+ break;
+
+ case "/api/dhcp/scopes/enable":
+ case "/api/enableDhcpScope":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await _dhcpApi.EnableDhcpScopeAsync(request);
+ break;
+
+ case "/api/dhcp/scopes/disable":
+ case "/api/disableDhcpScope":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _dhcpApi.DisableDhcpScope(request);
+ break;
+
+ case "/api/dhcp/scopes/delete":
+ case "/api/deleteDhcpScope":
+ if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _dhcpApi.DeleteDhcpScope(request);
+ break;
+
+ case "/api/admin/sessions/list":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.ListSessions(request, jsonWriter);
+ break;
+
+ case "/api/admin/sessions/createToken":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.CreateApiToken(request, jsonWriter);
+ break;
+
+ case "/api/admin/sessions/delete":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.DeleteSession(request, true);
+ break;
+
+ case "/api/admin/users/list":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.ListUsers(jsonWriter);
+ break;
+
+ case "/api/admin/users/create":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.CreateUser(request, jsonWriter);
+ break;
+
+ case "/api/admin/users/get":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.GetUserDetails(request, jsonWriter);
+ break;
+
+ case "/api/admin/users/set":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.SetUserDetails(request, jsonWriter);
+ break;
+
+ case "/api/admin/users/delete":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.DeleteUser(request);
+ break;
+
+ case "/api/admin/groups/list":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.ListGroups(jsonWriter);
+ break;
+
+ case "/api/admin/groups/create":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.CreateGroup(request, jsonWriter);
+ break;
+
+ case "/api/admin/groups/get":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.GetGroupDetails(request, jsonWriter);
+ break;
+
+ case "/api/admin/groups/set":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.SetGroupDetails(request, jsonWriter);
+ break;
+
+ case "/api/admin/groups/delete":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.DeleteGroup(request);
+ break;
+
+ case "/api/admin/permissions/list":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.ListPermissions(jsonWriter);
+ break;
+
+ case "/api/admin/permissions/get":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.GetPermissionDetails(request, jsonWriter, PermissionSection.Unknown);
+ break;
+
+ case "/api/admin/permissions/set":
+ if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _authApi.SetPermissionsDetails(request, jsonWriter, PermissionSection.Unknown);
+ break;
+
+ case "/api/logs/list":
+ case "/api/listLogs":
+ if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _logsApi.ListLogs(jsonWriter);
+ break;
+
+ case "/api/logs/download":
+ if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await _logsApi.DownloadLogAsync(request, response);
+ return;
+
+ case "/api/logs/delete":
+ case "/api/deleteLog":
+ if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _logsApi.DeleteLog(request);
+ break;
+
+ case "/api/logs/deleteAll":
+ case "/api/deleteAllLogs":
+ if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.Delete))
+ throw new DnsWebServiceException("Access was denied.");
+
+ _logsApi.DeleteAllLogs(request);
+ break;
+
+ case "/api/logs/query":
+ case "/api/queryLogs":
+ if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
+ await _logsApi.QueryLogsAsync(request, jsonWriter);
+ break;
+
default:
await SendErrorAsync(response, 404);
return;
@@ -737,7 +1190,16 @@ namespace DnsServerCore
}
catch (Exception ex)
{
- _log.Write(GetRequestRemoteEndPoint(request), ex);
+ UserSession session = null;
+
+ string strToken = request.QueryString["token"];
+ if (!string.IsNullOrEmpty(strToken))
+ session = _authManager.GetSession(strToken);
+
+ if (session is null)
+ _log.Write(GetRequestRemoteEndPoint(request), ex);
+ else
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + ex.ToString());
mS.SetLength(0);
JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS));
@@ -775,12 +1237,15 @@ namespace DnsServerCore
}
else if (path.StartsWith("/log/"))
{
- if (!IsSessionValid(request))
+ if (!TryGetSession(request, out UserSession session))
{
await SendErrorAsync(response, 403, "Invalid token or session expired.");
return;
}
+ if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View))
+ throw new DnsWebServiceException("Access was denied.");
+
string[] pathParts = path.Split('/');
string logFileName = pathParts[2];
@@ -820,7 +1285,16 @@ namespace DnsServerCore
if ((_state == ServiceState.Stopping) || (_state == ServiceState.Stopped))
return; //web service stopping
- _log.Write(GetRequestRemoteEndPoint(request), ex);
+ UserSession session = null;
+
+ string strToken = request.QueryString["token"];
+ if (!string.IsNullOrEmpty(strToken))
+ session = _authManager.GetSession(strToken);
+
+ if (session is null)
+ _log.Write(GetRequestRemoteEndPoint(request), ex);
+ else
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + ex.ToString());
await SendError(response, ex);
}
@@ -923,198 +1397,34 @@ namespace DnsServerCore
}
}
- #endregion
-
- #region user session
-
- private string CreateSession(string username)
- {
- string token = BinaryNumber.GenerateRandomNumber256().ToString();
-
- if (!_sessions.TryAdd(token, new UserSession(username)))
- throw new DnsWebServiceException("Error while creating session. Please try again.");
-
- return token;
- }
-
- private UserSession GetSession(string token)
- {
- if (_sessions.TryGetValue(token, out UserSession session))
- return session;
-
- return null;
- }
-
internal UserSession GetSession(HttpListenerRequest request)
{
string strToken = request.QueryString["token"];
if (string.IsNullOrEmpty(strToken))
throw new DnsWebServiceException("Parameter 'token' missing.");
- return GetSession(strToken);
+ return _authManager.GetSession(strToken);
}
- private UserSession DeleteSession(string token)
+ internal bool TryGetSession(HttpListenerRequest request, out UserSession session)
{
- if (_sessions.TryRemove(token, out UserSession session))
- return session;
-
- return null;
- }
-
- private UserSession DeleteSession(HttpListenerRequest request)
- {
- string strToken = request.QueryString["token"];
- if (string.IsNullOrEmpty(strToken))
- throw new DnsWebServiceException("Parameter 'token' missing.");
-
- return DeleteSession(strToken);
- }
-
- private void FailedLoginAttempt(IPAddress address)
- {
- _failedLoginAttempts.AddOrUpdate(address, 1, delegate (IPAddress key, int attempts)
- {
- return attempts + 1;
- });
- }
-
- private bool LoginAttemptsExceedLimit(IPAddress address, int limit)
- {
- if (!_failedLoginAttempts.TryGetValue(address, out int attempts))
- return false;
-
- return attempts >= limit;
- }
-
- private void ResetFailedLoginAttempt(IPAddress address)
- {
- _failedLoginAttempts.TryRemove(address, out _);
- }
-
- private void BlockAddress(IPAddress address, int interval)
- {
- _blockedAddresses.TryAdd(address, DateTime.UtcNow.AddMilliseconds(interval));
- }
-
- private bool IsAddressBlocked(IPAddress address)
- {
- if (!_blockedAddresses.TryGetValue(address, out DateTime expiry))
- return false;
-
- if (expiry > DateTime.UtcNow)
- {
- return true;
- }
- else
- {
- UnblockAddress(address);
- ResetFailedLoginAttempt(address);
-
- return false;
- }
- }
-
- private void UnblockAddress(IPAddress address)
- {
- _blockedAddresses.TryRemove(address, out _);
- }
-
- #endregion
-
- #region auth api
-
- private async Task LoginAsync(HttpListenerRequest request, JsonTextWriter jsonWriter)
- {
- string strUsername = request.QueryString["user"];
- if (string.IsNullOrEmpty(strUsername))
- throw new DnsWebServiceException("Parameter 'user' missing.");
-
- string strPassword = request.QueryString["pass"];
- if (string.IsNullOrEmpty(strPassword))
- throw new DnsWebServiceException("Parameter 'pass' missing.");
-
- IPEndPoint remoteEP = GetRequestRemoteEndPoint(request);
-
- if (IsAddressBlocked(remoteEP.Address))
- throw new DnsWebServiceException("Max limit of " + MAX_LOGIN_ATTEMPTS + " attempts exceeded. Access blocked for " + (BLOCK_ADDRESS_INTERVAL / 1000) + " seconds.");
-
- strUsername = strUsername.Trim().ToLower();
- string strPasswordHash = GetPasswordHash(strUsername, strPassword);
-
- if (!_credentials.TryGetValue(strUsername, out string passwordHash) || (passwordHash != strPasswordHash))
- {
- if (strPassword != "admin") //exception for default password
- {
- FailedLoginAttempt(remoteEP.Address);
-
- if (LoginAttemptsExceedLimit(remoteEP.Address, MAX_LOGIN_ATTEMPTS))
- BlockAddress(remoteEP.Address, BLOCK_ADDRESS_INTERVAL);
-
- await Task.Delay(1000);
- }
-
- throw new DnsWebServiceException("Invalid username or password for user: " + strUsername);
- }
-
- ResetFailedLoginAttempt(remoteEP.Address);
-
- _log.Write(remoteEP, "[" + strUsername + "] User logged in.");
-
- string token = CreateSession(strUsername);
-
- jsonWriter.WritePropertyName("token");
- jsonWriter.WriteValue(token);
- }
-
- private bool IsSessionValid(HttpListenerRequest request)
- {
- UserSession session = GetSession(request);
- if (session == null)
+ session = GetSession(request);
+ if ((session is null) || session.User.Disabled)
return false;
if (session.HasExpired())
{
- DeleteSession(request);
+ _authManager.DeleteSession(session.Token);
+ _authManager.SaveConfigFile();
return false;
}
- session.UpdateLastSeen();
+ IPEndPoint remoteEP = GetRequestRemoteEndPoint(request);
+
+ session.UpdateLastSeen(remoteEP.Address, request.UserAgent);
return true;
}
- private void ChangePassword(HttpListenerRequest request)
- {
- string strToken = request.QueryString["token"];
- if (string.IsNullOrEmpty(strToken))
- throw new DnsWebServiceException("Parameter 'token' missing.");
-
- string strPassword = request.QueryString["pass"];
- if (string.IsNullOrEmpty(strPassword))
- throw new DnsWebServiceException("Parameter 'pass' missing.");
-
- UserSession session = GetSession(strToken);
- if (session == null)
- throw new DnsWebServiceException("User session does not exists.");
-
- SetCredentials(session.Username, strPassword);
- SaveConfigFile();
-
- _log.Write(GetRequestRemoteEndPoint(request), "[" + session.Username + "] Password was changed for user.");
- }
-
- private void Logout(HttpListenerRequest request)
- {
- string strToken = request.QueryString["token"];
- if (string.IsNullOrEmpty(strToken))
- throw new DnsWebServiceException("Parameter 'token' missing.");
-
- UserSession session = DeleteSession(strToken);
-
- if (session != null)
- _log.Write(GetRequestRemoteEndPoint(request), "[" + session.Username + "] User logged out.");
- }
-
#endregion
#region update api
@@ -1132,6 +1442,7 @@ namespace DnsServerCore
{
SocketsHttpHandler handler = new SocketsHttpHandler();
handler.Proxy = _dnsServer.Proxy;
+ handler.UseProxy = _dnsServer.Proxy is not null;
using (HttpClient http = new HttpClient(handler))
{
@@ -1218,6 +1529,11 @@ namespace DnsServerCore
return strVersion;
}
+ internal string GetServerVersion()
+ {
+ return GetCleanVersion(_currentVersion);
+ }
+
#endregion
#region settings api
@@ -1225,7 +1541,7 @@ namespace DnsServerCore
private void GetDnsSettings(JsonTextWriter jsonWriter)
{
jsonWriter.WritePropertyName("version");
- jsonWriter.WriteValue(GetCleanVersion(_currentVersion));
+ jsonWriter.WriteValue(GetServerVersion());
jsonWriter.WritePropertyName("dnsServerDomain");
jsonWriter.WriteValue(_dnsServer.ServerDomain);
@@ -1316,6 +1632,9 @@ namespace DnsServerCore
jsonWriter.WritePropertyName("defaultRecordTtl");
jsonWriter.WriteValue(_zonesApi.DefaultRecordTtl);
+ jsonWriter.WritePropertyName("dnsAppsEnableAutomaticUpdate");
+ jsonWriter.WriteValue(_appsApi.EnableAutomaticUpdate);
+
jsonWriter.WritePropertyName("preferIPv6");
jsonWriter.WriteValue(_dnsServer.PreferIPv6);
@@ -1825,6 +2144,10 @@ namespace DnsServerCore
if (!string.IsNullOrEmpty(strDefaultRecordTtl))
_zonesApi.DefaultRecordTtl = uint.Parse(strDefaultRecordTtl);
+ string strDnsAppsEnableAutomaticUpdate = request.QueryString["dnsAppsEnableAutomaticUpdate"];
+ if (!string.IsNullOrEmpty(strDnsAppsEnableAutomaticUpdate))
+ _appsApi.EnableAutomaticUpdate = bool.Parse(strDnsAppsEnableAutomaticUpdate);
+
string strPreferIPv6 = request.QueryString["preferIPv6"];
if (!string.IsNullOrEmpty(strPreferIPv6))
_dnsServer.PreferIPv6 = bool.Parse(strPreferIPv6);
@@ -2141,21 +2464,18 @@ namespace DnsServerCore
}
}
+ bool blockListUrlsUpdated = false;
string strBlockListUrls = request.QueryString["blockListUrls"];
if (!string.IsNullOrEmpty(strBlockListUrls))
{
if (strBlockListUrls == "false")
{
- StopBlockListUpdateTimer();
-
_dnsServer.BlockListZoneManager.AllowListUrls.Clear();
_dnsServer.BlockListZoneManager.BlockListUrls.Clear();
_dnsServer.BlockListZoneManager.Flush();
}
else
{
- bool updated = false;
-
string[] strBlockListUrlList = strBlockListUrls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (oldWebServiceHttpPort != _webServiceHttpPort)
@@ -2165,17 +2485,17 @@ namespace DnsServerCore
if (strBlockListUrlList[i].Contains("http://localhost:" + oldWebServiceHttpPort + "/blocklist.txt"))
{
strBlockListUrlList[i] = "http://localhost:" + _webServiceHttpPort + "/blocklist.txt";
- updated = true;
+ blockListUrlsUpdated = true;
break;
}
}
}
- if (!updated)
+ if (!blockListUrlsUpdated)
{
if (strBlockListUrlList.Length != (_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count))
{
- updated = true;
+ blockListUrlsUpdated = true;
}
else
{
@@ -2187,7 +2507,7 @@ namespace DnsServerCore
if (!_dnsServer.BlockListZoneManager.AllowListUrls.Contains(new Uri(strAllowListUrl)))
{
- updated = true;
+ blockListUrlsUpdated = true;
break;
}
}
@@ -2195,7 +2515,7 @@ namespace DnsServerCore
{
if (!_dnsServer.BlockListZoneManager.BlockListUrls.Contains(new Uri(strBlockListUrl)))
{
- updated = true;
+ blockListUrlsUpdated = true;
break;
}
}
@@ -2203,7 +2523,7 @@ namespace DnsServerCore
}
}
- if (updated)
+ if (blockListUrlsUpdated)
{
_dnsServer.BlockListZoneManager.AllowListUrls.Clear();
_dnsServer.BlockListZoneManager.BlockListUrls.Clear();
@@ -2225,8 +2545,6 @@ namespace DnsServerCore
_dnsServer.BlockListZoneManager.BlockListUrls.Add(blockListUrl);
}
}
-
- ForceUpdateBlockLists();
}
}
}
@@ -2236,12 +2554,24 @@ namespace DnsServerCore
{
int blockListUpdateIntervalHours = int.Parse(strBlockListUpdateIntervalHours);
- if ((blockListUpdateIntervalHours < 1) || (blockListUpdateIntervalHours > 168))
- throw new DnsWebServiceException("Parameter `blockListUpdateIntervalHours` must be between 1 hour and 168 hours (7 days).");
+ if ((blockListUpdateIntervalHours < 0) || (blockListUpdateIntervalHours > 168))
+ throw new DnsWebServiceException("Parameter `blockListUpdateIntervalHours` must be between 1 hour and 168 hours (7 days) or 0 to disable automatic update.");
_blockListUpdateIntervalHours = blockListUpdateIntervalHours;
}
+ if ((_blockListUpdateIntervalHours > 0) && ((_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count) > 0))
+ {
+ if (blockListUrlsUpdated || (_blockListUpdateTimer is null))
+ ForceUpdateBlockLists();
+
+ StartBlockListUpdateTimer();
+ }
+ else
+ {
+ StopBlockListUpdateTimer();
+ }
+
if ((_webServiceTlsCertificatePath == null) && (_dnsTlsCertificatePath == null))
StopTlsCertificateUpdateTimer();
@@ -2257,7 +2587,7 @@ namespace DnsServerCore
SaveConfigFile();
_log.Save();
- _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] DNS Settings were updated {dnsServerDomain: " + _dnsServer.ServerDomain + "; dnsServerLocalEndPoints: " + strDnsServerLocalEndPoints + "; webServiceLocalAddresses: " + strWebServiceLocalAddresses + "; webServiceHttpPort: " + _webServiceHttpPort + "; webServiceEnableTls: " + strWebServiceEnableTls + "; webServiceHttpToTlsRedirect: " + strWebServiceHttpToTlsRedirect + "; webServiceTlsPort: " + strWebServiceTlsPort + "; webServiceUseSelfSignedTlsCertificate: " + _webServiceUseSelfSignedTlsCertificate + "; webServiceTlsCertificatePath: " + strWebServiceTlsCertificatePath + "; enableDnsOverHttp: " + _dnsServer.EnableDnsOverHttp + "; enableDnsOverTls: " + _dnsServer.EnableDnsOverTls + "; enableDnsOverHttps: " + _dnsServer.EnableDnsOverHttps + "; dnsTlsCertificatePath: " + _dnsTlsCertificatePath + "; defaultRecordTtl: " + _zonesApi.DefaultRecordTtl + "; preferIPv6: " + _dnsServer.PreferIPv6 + "; enableLogging: " + strEnableLogging + "; logQueries: " + (_dnsServer.QueryLogManager != null) + "; useLocalTime: " + strUseLocalTime + "; logFolder: " + strLogFolder + "; maxLogFileDays: " + strMaxLogFileDays + "; recursion: " + _dnsServer.Recursion.ToString() + "; randomizeName: " + strRandomizeName + "; qnameMinimization: " + strQnameMinimization + "; serveStale: " + strServeStale + "; serveStaleTtl: " + strServeStaleTtl + "; cachePrefetchEligibility: " + strCachePrefetchEligibility + "; cachePrefetchTrigger: " + strCachePrefetchTrigger + "; cachePrefetchSampleIntervalInMinutes: " + strCachePrefetchSampleIntervalInMinutes + "; cachePrefetchSampleEligibilityHitsPerHour: " + strCachePrefetchSampleEligibilityHitsPerHour + "; proxyType: " + strProxyType + "; forwarders: " + strForwarders + "; forwarderProtocol: " + strForwarderProtocol + "; enableBlocking: " + _dnsServer.EnableBlocking + "; allowTxtBlockingReport: " + _dnsServer.AllowTxtBlockingReport + "; blockingType: " + _dnsServer.BlockingType.ToString() + "; blockListUrl: " + strBlockListUrls + "; blockListUpdateIntervalHours: " + strBlockListUpdateIntervalHours + ";}");
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] DNS Settings were updated {dnsServerDomain: " + _dnsServer.ServerDomain + "; dnsServerLocalEndPoints: " + strDnsServerLocalEndPoints + "; webServiceLocalAddresses: " + strWebServiceLocalAddresses + "; webServiceHttpPort: " + _webServiceHttpPort + "; webServiceEnableTls: " + strWebServiceEnableTls + "; webServiceHttpToTlsRedirect: " + strWebServiceHttpToTlsRedirect + "; webServiceTlsPort: " + strWebServiceTlsPort + "; webServiceUseSelfSignedTlsCertificate: " + _webServiceUseSelfSignedTlsCertificate + "; webServiceTlsCertificatePath: " + strWebServiceTlsCertificatePath + "; enableDnsOverHttp: " + _dnsServer.EnableDnsOverHttp + "; enableDnsOverTls: " + _dnsServer.EnableDnsOverTls + "; enableDnsOverHttps: " + _dnsServer.EnableDnsOverHttps + "; dnsTlsCertificatePath: " + _dnsTlsCertificatePath + "; defaultRecordTtl: " + _zonesApi.DefaultRecordTtl + "; preferIPv6: " + _dnsServer.PreferIPv6 + "; enableLogging: " + strEnableLogging + "; logQueries: " + (_dnsServer.QueryLogManager != null) + "; useLocalTime: " + strUseLocalTime + "; logFolder: " + strLogFolder + "; maxLogFileDays: " + strMaxLogFileDays + "; recursion: " + _dnsServer.Recursion.ToString() + "; randomizeName: " + strRandomizeName + "; qnameMinimization: " + strQnameMinimization + "; serveStale: " + strServeStale + "; serveStaleTtl: " + strServeStaleTtl + "; cachePrefetchEligibility: " + strCachePrefetchEligibility + "; cachePrefetchTrigger: " + strCachePrefetchTrigger + "; cachePrefetchSampleIntervalInMinutes: " + strCachePrefetchSampleIntervalInMinutes + "; cachePrefetchSampleEligibilityHitsPerHour: " + strCachePrefetchSampleEligibilityHitsPerHour + "; proxyType: " + strProxyType + "; forwarders: " + strForwarders + "; forwarderProtocol: " + strForwarderProtocol + "; enableBlocking: " + _dnsServer.EnableBlocking + "; allowTxtBlockingReport: " + _dnsServer.AllowTxtBlockingReport + "; blockingType: " + _dnsServer.BlockingType.ToString() + "; blockListUrl: " + strBlockListUrls + "; blockListUpdateIntervalHours: " + strBlockListUpdateIntervalHours + ";}");
GetDnsSettings(jsonWriter);
@@ -2358,6 +2688,7 @@ namespace DnsServerCore
bool allowedZones = false;
bool blockedZones = false;
bool dnsSettings = false;
+ bool authConfig = false;
bool logSettings = false;
string strBlockLists = request.QueryString["blockLists"];
@@ -2396,6 +2727,10 @@ namespace DnsServerCore
if (!string.IsNullOrEmpty(strDnsSettings))
dnsSettings = bool.Parse(strDnsSettings);
+ string strAuthConfig = request.QueryString["authConfig"];
+ if (!string.IsNullOrEmpty(strAuthConfig))
+ authConfig = bool.Parse(strAuthConfig);
+
string strLogSettings = request.QueryString["logSettings"];
if (!string.IsNullOrEmpty(strLogSettings))
logSettings = bool.Parse(strLogSettings);
@@ -2427,15 +2762,7 @@ namespace DnsServerCore
if (logFile.Equals(_log.CurrentLogFile, StringComparison.OrdinalIgnoreCase))
{
- using (FileStream fS = new FileStream(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- {
- ZipArchiveEntry entry = backupZip.CreateEntry(entryName);
-
- using (Stream s = entry.Open())
- {
- await fS.CopyToAsync(s);
- }
- }
+ await CreateBackupEntryFromFileAsync(backupZip, logFile, entryName);
}
else
{
@@ -2466,7 +2793,7 @@ namespace DnsServerCore
entryName = entryName.TrimStart('/');
- backupZip.CreateEntryFromFile(appFile, entryName);
+ await CreateBackupEntryFromFileAsync(backupZip, appFile, entryName);
}
}
@@ -2521,6 +2848,14 @@ namespace DnsServerCore
backupZip.CreateEntryFromFile(dnsSettingsFile, "dns.config");
}
+ if (authConfig)
+ {
+ string authSettingsFile = Path.Combine(_configFolder, "auth.config");
+
+ if (File.Exists(authSettingsFile))
+ backupZip.CreateEntryFromFile(authSettingsFile, "auth.config");
+ }
+
if (logSettings)
{
string logSettingsFile = Path.Combine(_configFolder, "log.config");
@@ -2555,7 +2890,29 @@ namespace DnsServerCore
}
}
- _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Settings backup zip file was exported.");
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Settings backup zip file was exported.");
+ }
+
+ private static async Task CreateBackupEntryFromFileAsync(ZipArchive backupZip, string sourceFileName, string entryName)
+ {
+ using (FileStream fS = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ {
+ ZipArchiveEntry entry = backupZip.CreateEntry(entryName);
+
+ DateTime lastWrite = File.GetLastWriteTime(sourceFileName);
+
+ // If file to be archived has an invalid last modified time, use the first datetime representable in the Zip timestamp format
+ // (midnight on January 1, 1980):
+ if (lastWrite.Year < 1980 || lastWrite.Year > 2107)
+ lastWrite = new DateTime(1980, 1, 1, 0, 0, 0);
+
+ entry.LastWriteTime = lastWrite;
+
+ using (Stream sE = entry.Open())
+ {
+ await fS.CopyToAsync(sE);
+ }
+ }
}
private async Task RestoreSettingsAsync(HttpListenerRequest request, JsonTextWriter jsonWriter)
@@ -2569,6 +2926,7 @@ namespace DnsServerCore
bool allowedZones = false;
bool blockedZones = false;
bool dnsSettings = false;
+ bool authConfig = false;
bool logSettings = false;
bool deleteExistingFiles = false;
@@ -2609,6 +2967,10 @@ namespace DnsServerCore
if (!string.IsNullOrEmpty(strDnsSettings))
dnsSettings = bool.Parse(strDnsSettings);
+ string strAuthConfig = request.QueryString["authConfig"];
+ if (!string.IsNullOrEmpty(strAuthConfig))
+ authConfig = bool.Parse(strAuthConfig);
+
string strLogSettings = request.QueryString["logSettings"];
if (!string.IsNullOrEmpty(strLogSettings))
logSettings = bool.Parse(strLogSettings);
@@ -2651,6 +3013,8 @@ namespace DnsServerCore
fS.Position = 0;
using (ZipArchive backupZip = new ZipArchive(fS, ZipArchiveMode.Read, false, Encoding.UTF8))
{
+ UserSession session = GetSession(request);
+
if (logSettings || logs)
{
//stop logging
@@ -2699,6 +3063,27 @@ namespace DnsServerCore
}
}
+ if (authConfig)
+ {
+ ZipArchiveEntry entry = backupZip.GetEntry("auth.config");
+ if (entry != null)
+ entry.ExtractToFile(Path.Combine(_configFolder, entry.Name), true);
+
+ //reload auth config
+ _authManager.LoadConfigFile(session);
+ }
+
+ if (dnsSettings)
+ {
+ ZipArchiveEntry entry = backupZip.GetEntry("dns.config");
+ if (entry != null)
+ entry.ExtractToFile(Path.Combine(_configFolder, entry.Name), true);
+
+ //reload settings and block list zone
+ LoadConfigFile();
+ _dnsServer.BlockListZoneManager.LoadBlockLists();
+ }
+
if (blockLists)
{
if (deleteExistingFiles)
@@ -2719,37 +3104,6 @@ namespace DnsServerCore
}
}
- if (scopes)
- {
- //stop dhcp server
- _dhcpServer.Stop();
-
- try
- {
- if (deleteExistingFiles)
- {
- //delete existing scope files
- string[] scopeFiles = Directory.GetFiles(Path.Combine(_configFolder, "scopes"), "*.scope", SearchOption.TopDirectoryOnly);
- foreach (string scopeFile in scopeFiles)
- {
- File.Delete(scopeFile);
- }
- }
-
- //extract scope files from backup
- foreach (ZipArchiveEntry entry in backupZip.Entries)
- {
- if (entry.FullName.StartsWith("scopes/"))
- entry.ExtractToFile(Path.Combine(_configFolder, "scopes", entry.Name), true);
- }
- }
- finally
- {
- //start dhcp server
- _dhcpServer.Start();
- }
- }
-
if (apps)
{
//unload apps
@@ -2788,35 +3142,6 @@ namespace DnsServerCore
_dnsServer.DnsApplicationManager.LoadAllApplications();
}
- if (stats)
- {
- if (deleteExistingFiles)
- {
- //delete existing stats files
- string[] hourlyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.stat", SearchOption.TopDirectoryOnly);
- foreach (string hourlyStatsFile in hourlyStatsFiles)
- {
- File.Delete(hourlyStatsFile);
- }
-
- string[] dailyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.dstat", SearchOption.TopDirectoryOnly);
- foreach (string dailyStatsFile in dailyStatsFiles)
- {
- File.Delete(dailyStatsFile);
- }
- }
-
- //extract stats files from backup
- foreach (ZipArchiveEntry entry in backupZip.Entries)
- {
- if (entry.FullName.StartsWith("stats/"))
- entry.ExtractToFile(Path.Combine(_configFolder, "stats", entry.Name), true);
- }
-
- //reload stats
- _dnsServer.StatsManager.ReloadStats();
- }
-
if (zones)
{
if (deleteExistingFiles)
@@ -2838,6 +3163,7 @@ namespace DnsServerCore
//reload zones
_dnsServer.AuthZoneManager.LoadAllZoneFiles();
+ InspectAndFixZonePermissions();
}
if (allowedZones)
@@ -2876,18 +3202,67 @@ namespace DnsServerCore
_dnsServer.BlockedZoneManager.LoadBlockedZoneFile();
}
- if (dnsSettings)
+ if (scopes)
{
- ZipArchiveEntry entry = backupZip.GetEntry("dns.config");
- if (entry != null)
- entry.ExtractToFile(Path.Combine(_configFolder, entry.Name), true);
+ //stop dhcp server
+ _dhcpServer.Stop();
- //reload settings and block list zone
- LoadConfigFile();
- _dnsServer.BlockListZoneManager.LoadBlockLists();
+ try
+ {
+ if (deleteExistingFiles)
+ {
+ //delete existing scope files
+ string[] scopeFiles = Directory.GetFiles(Path.Combine(_configFolder, "scopes"), "*.scope", SearchOption.TopDirectoryOnly);
+ foreach (string scopeFile in scopeFiles)
+ {
+ File.Delete(scopeFile);
+ }
+ }
+
+ //extract scope files from backup
+ foreach (ZipArchiveEntry entry in backupZip.Entries)
+ {
+ if (entry.FullName.StartsWith("scopes/"))
+ entry.ExtractToFile(Path.Combine(_configFolder, "scopes", entry.Name), true);
+ }
+ }
+ finally
+ {
+ //start dhcp server
+ _dhcpServer.Start();
+ }
}
- _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Settings backup zip file was restored.");
+ if (stats)
+ {
+ if (deleteExistingFiles)
+ {
+ //delete existing stats files
+ string[] hourlyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.stat", SearchOption.TopDirectoryOnly);
+ foreach (string hourlyStatsFile in hourlyStatsFiles)
+ {
+ File.Delete(hourlyStatsFile);
+ }
+
+ string[] dailyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.dstat", SearchOption.TopDirectoryOnly);
+ foreach (string dailyStatsFile in dailyStatsFiles)
+ {
+ File.Delete(dailyStatsFile);
+ }
+ }
+
+ //extract stats files from backup
+ foreach (ZipArchiveEntry entry in backupZip.Entries)
+ {
+ if (entry.FullName.StartsWith("stats/"))
+ entry.ExtractToFile(Path.Combine(_configFolder, "stats", entry.Name), true);
+ }
+
+ //reload stats
+ _dnsServer.StatsManager.ReloadStats();
+ }
+
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Settings backup zip file was restored.");
}
}
}
@@ -2911,8 +3286,8 @@ namespace DnsServerCore
private void ForceUpdateBlockLists(HttpListenerRequest request)
{
- if (ForceUpdateBlockLists())
- _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Block list update was triggered.");
+ ForceUpdateBlockLists();
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Block list update was triggered.");
}
private void TemporaryDisableBlocking(HttpListenerRequest request, JsonTextWriter jsonWriter)
@@ -2932,7 +3307,7 @@ namespace DnsServerCore
try
{
_dnsServer.EnableBlocking = true;
- _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Blocking was enabled after " + minutes + " minute(s) being temporarily disabled.");
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Blocking was enabled after " + minutes + " minute(s) being temporarily disabled.");
}
catch (Exception ex)
{
@@ -2947,7 +3322,7 @@ namespace DnsServerCore
_dnsServer.EnableBlocking = false;
_temporaryDisableBlockingTill = DateTime.UtcNow.AddMinutes(minutes);
- _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Blocking was temporarily disabled for " + minutes + " minute(s).");
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Blocking was temporarily disabled for " + minutes + " minute(s).");
}
else
{
@@ -3123,15 +3498,29 @@ namespace DnsServerCore
if (importResponse)
{
+ UserSession session = GetSession(request);
+
AuthZoneInfo zoneInfo = _dnsServer.AuthZoneManager.FindAuthZoneInfo(domain);
if ((zoneInfo is null) || ((zoneInfo.Type == AuthZoneType.Secondary) && !zoneInfo.Name.Equals(domain, StringComparison.OrdinalIgnoreCase)))
{
+ if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
zoneInfo = _dnsServer.AuthZoneManager.CreatePrimaryZone(domain, _dnsServer.ServerDomain, false);
if (zoneInfo is null)
throw new DnsServerException("Cannot import records: failed to create primary zone.");
+
+ //set permissions
+ _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
+ _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
+ _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
+ _authManager.SaveConfigFile();
}
else
{
+ if (!_authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
+ throw new DnsWebServiceException("Access was denied.");
+
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
@@ -3183,7 +3572,7 @@ namespace DnsServerCore
_dnsServer.AuthZoneManager.ImportRecords(zoneInfo.Name, importRecords);
}
- _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] DNS Client imported record(s) for authoritative zone {server: " + server + "; zone: " + zoneInfo.Name + "; type: " + type + ";}");
+ _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] DNS Client imported record(s) for authoritative zone {server: " + server + "; zone: " + zoneInfo.Name + "; type: " + type + ";}");
_dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
}
@@ -3200,53 +3589,25 @@ namespace DnsServerCore
#endregion
- #region auth
-
- private void SetCredentials(string username, string password)
- {
- username = username.ToLower();
- string passwordHash = GetPasswordHash(username, password);
-
- _credentials[username] = passwordHash;
- }
-
- private void LoadCredentials(string username, string passwordHash)
- {
- username = username.ToLower();
-
- _credentials[username] = passwordHash;
- }
-
- private static string GetPasswordHash(string username, string password)
- {
- using (HMAC hmac = new HMACSHA256(Encoding.UTF8.GetBytes(password)))
- {
- return Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(username))).ToLower();
- }
- }
-
- #endregion
-
#region block list
- private bool ForceUpdateBlockLists()
+ private void ForceUpdateBlockLists()
{
- if ((_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count) > 0)
+ Task.Run(async delegate ()
{
- _blockListLastUpdatedOn = new DateTime();
-
- StopBlockListUpdateTimer();
- StartBlockListUpdateTimer();
-
- return true;
- }
-
- return false;
+ if (await _dnsServer.BlockListZoneManager.UpdateBlockListsAsync())
+ {
+ //block lists were updated
+ //save last updated on time
+ _blockListLastUpdatedOn = DateTime.UtcNow;
+ SaveConfigFile();
+ }
+ });
}
private void StartBlockListUpdateTimer()
{
- if (_blockListUpdateTimer == null)
+ if (_blockListUpdateTimer is null)
{
_blockListUpdateTimer = new Timer(async delegate (object state)
{
@@ -3274,7 +3635,7 @@ namespace DnsServerCore
private void StopBlockListUpdateTimer()
{
- if (_blockListUpdateTimer != null)
+ if (_blockListUpdateTimer is not null)
{
_blockListUpdateTimer.Dispose();
_blockListUpdateTimer = null;
@@ -3380,613 +3741,16 @@ namespace DnsServerCore
try
{
- bool passwordResetOption = false;
-
- if (!File.Exists(configFile))
- {
- string passwordResetConfigFile = Path.Combine(_configFolder, "reset.config");
-
- if (File.Exists(passwordResetConfigFile))
- {
- passwordResetOption = true;
- configFile = passwordResetConfigFile;
- }
- }
-
- byte version;
+ int version;
using (FileStream fS = new FileStream(configFile, FileMode.Open, FileAccess.Read))
{
- BinaryReader bR = new BinaryReader(fS);
-
- if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DS") //format
- throw new InvalidDataException("DnsServer config file format is invalid.");
-
- version = bR.ReadByte();
- switch (version)
- {
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- case 16:
- case 17:
- case 18:
- case 19:
- case 20:
- case 21:
- case 22:
- case 23:
- case 24:
- case 25:
- case 26:
- case 27:
- _dnsServer.ServerDomain = bR.ReadShortString();
- _webServiceHttpPort = bR.ReadInt32();
-
- if (version >= 13)
- {
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- IPAddress[] localAddresses = new IPAddress[count];
-
- for (int i = 0; i < count; i++)
- localAddresses[i] = IPAddressExtension.Parse(bR);
-
- _webServiceLocalAddresses = localAddresses;
- }
- }
-
- _webServiceTlsPort = bR.ReadInt32();
- _webServiceEnableTls = bR.ReadBoolean();
- _webServiceHttpToTlsRedirect = bR.ReadBoolean();
- _webServiceTlsCertificatePath = bR.ReadShortString();
- _webServiceTlsCertificatePassword = bR.ReadShortString();
-
- if (_webServiceTlsCertificatePath.Length == 0)
- _webServiceTlsCertificatePath = null;
-
- if (_webServiceTlsCertificatePath != null)
- {
- try
- {
- LoadWebServiceTlsCertificate(_webServiceTlsCertificatePath, _webServiceTlsCertificatePassword);
- }
- catch (Exception ex)
- {
- _log.Write("DNS Server encountered an error while loading Web Service TLS certificate: " + _webServiceTlsCertificatePath + "\r\n" + ex.ToString());
- }
-
- StartTlsCertificateUpdateTimer();
- }
- }
- else
- {
- _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any };
-
- _webServiceTlsPort = 53443;
- _webServiceEnableTls = false;
- _webServiceHttpToTlsRedirect = false;
- _webServiceTlsCertificatePath = string.Empty;
- _webServiceTlsCertificatePassword = string.Empty;
- }
-
- _dnsServer.PreferIPv6 = bR.ReadBoolean();
-
- if (bR.ReadBoolean()) //logQueries
- _dnsServer.QueryLogManager = _log;
-
- if (version >= 14)
- _dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32();
- else
- _dnsServer.StatsManager.MaxStatFileDays = 0;
-
- if (version >= 17)
- {
- _dnsServer.Recursion = (DnsServerRecursion)bR.ReadByte();
-
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- NetworkAddress[] networks = new NetworkAddress[count];
-
- for (int i = 0; i < count; i++)
- networks[i] = NetworkAddress.Parse(bR);
-
- _dnsServer.RecursionDeniedNetworks = networks;
- }
- }
-
-
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- NetworkAddress[] networks = new NetworkAddress[count];
-
- for (int i = 0; i < count; i++)
- networks[i] = NetworkAddress.Parse(bR);
-
- _dnsServer.RecursionAllowedNetworks = networks;
- }
- }
- }
- else
- {
- bool allowRecursion = bR.ReadBoolean();
- bool allowRecursionOnlyForPrivateNetworks;
-
- if (version >= 4)
- allowRecursionOnlyForPrivateNetworks = bR.ReadBoolean();
- else
- allowRecursionOnlyForPrivateNetworks = true; //default true for security reasons
-
- if (allowRecursion)
- {
- if (allowRecursionOnlyForPrivateNetworks)
- _dnsServer.Recursion = DnsServerRecursion.AllowOnlyForPrivateNetworks;
- else
- _dnsServer.Recursion = DnsServerRecursion.Allow;
- }
- else
- {
- _dnsServer.Recursion = DnsServerRecursion.Deny;
- }
- }
-
- if (version >= 12)
- _dnsServer.RandomizeName = bR.ReadBoolean();
- else
- _dnsServer.RandomizeName = true; //default true to enable security feature
-
- if (version >= 15)
- _dnsServer.QnameMinimization = bR.ReadBoolean();
- else
- _dnsServer.QnameMinimization = true; //default true to enable privacy feature
-
- if (version >= 20)
- {
- _dnsServer.QpmLimitRequests = bR.ReadInt32();
- _dnsServer.QpmLimitErrors = bR.ReadInt32();
- _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
- _dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32();
- _dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32();
- }
- else if (version >= 17)
- {
- _dnsServer.QpmLimitRequests = bR.ReadInt32();
- _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
- _ = bR.ReadInt32(); //read obsolete value _dnsServer.QpmLimitSamplingIntervalInMinutes
- }
- else
- {
- _dnsServer.QpmLimitRequests = 0;
- _dnsServer.QpmLimitErrors = 0;
- _dnsServer.QpmLimitSampleMinutes = 1;
- _dnsServer.QpmLimitIPv4PrefixLength = 24;
- _dnsServer.QpmLimitIPv6PrefixLength = 56;
- }
-
- if (version >= 13)
- {
- _dnsServer.ServeStale = bR.ReadBoolean();
- _dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32();
- }
- else
- {
- _dnsServer.ServeStale = true;
- _dnsServer.CacheZoneManager.ServeStaleTtl = CacheZoneManager.SERVE_STALE_TTL;
- }
-
- if (version >= 9)
- {
- _dnsServer.CachePrefetchEligibility = bR.ReadInt32();
- _dnsServer.CachePrefetchTrigger = bR.ReadInt32();
- _dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32();
- _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32();
- }
- else
- {
- _dnsServer.CachePrefetchEligibility = 2;
- _dnsServer.CachePrefetchTrigger = 9;
- _dnsServer.CachePrefetchSampleIntervalInMinutes = 5;
- _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = 30;
- }
-
- NetProxyType proxyType = (NetProxyType)bR.ReadByte();
- if (proxyType != NetProxyType.None)
- {
- string address = bR.ReadShortString();
- int port = bR.ReadInt32();
- NetworkCredential credential = null;
-
- if (bR.ReadBoolean()) //credential set
- credential = new NetworkCredential(bR.ReadShortString(), bR.ReadShortString());
-
- _dnsServer.Proxy = NetProxy.CreateProxy(proxyType, address, port, credential);
-
- if (version >= 10)
- {
- int count = bR.ReadByte();
- List 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);
-
- _dnsServer.Forwarders = forwarders;
- }
- }
-
- if (version <= 10)
- {
- DnsTransportProtocol forwarderProtocol = (DnsTransportProtocol)bR.ReadByte();
-
- 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++)
- LoadCredentials(bR.ReadShortString(), bR.ReadShortString());
- }
- else
- {
- for (int i = 0; i < count; i++)
- SetCredentials(bR.ReadShortString(), bR.ReadShortString());
- }
- }
- }
-
- 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 = IPAddressExtension.Parse(bR);
-
- switch (customAddress.AddressFamily)
- {
- case AddressFamily.InterNetwork:
- dnsARecords.Add(new DnsARecordData(customAddress));
- break;
-
- case AddressFamily.InterNetworkV6:
- dnsAAAARecords.Add(new DnsAAAARecordData(customAddress));
- break;
- }
- }
-
- _dnsServer.CustomBlockingARecords = dnsARecords;
- _dnsServer.CustomBlockingAAAARecords = dnsAAAARecords;
- }
- }
- else
- {
- _dnsServer.CustomBlockingARecords = null;
- _dnsServer.CustomBlockingAAAARecords = null;
- }
-
- if (version > 4)
- {
- //read block list urls
- int count = bR.ReadByte();
-
- for (int i = 0; i < count; i++)
- {
- string listUrl = bR.ReadShortString();
-
- if (listUrl.StartsWith("!"))
- _dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1)));
- else
- _dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl));
- }
-
- _blockListLastUpdatedOn = bR.ReadDateTime();
-
- if (version >= 13)
- _blockListUpdateIntervalHours = bR.ReadInt32();
- }
- else
- {
- _dnsServer.BlockListZoneManager.AllowListUrls.Clear();
- _dnsServer.BlockListZoneManager.BlockListUrls.Clear();
- _blockListLastUpdatedOn = DateTime.MinValue;
- _blockListUpdateIntervalHours = 24;
- }
-
- if (version >= 11)
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- IPEndPoint[] localEndPoints = new IPEndPoint[count];
-
- for (int i = 0; i < count; i++)
- localEndPoints[i] = (IPEndPoint)EndPointExtension.Parse(bR);
-
- _dnsServer.LocalEndPoints = localEndPoints;
- }
- }
- else if (version >= 6)
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- IPEndPoint[] localEndPoints = new IPEndPoint[count];
-
- for (int i = 0; i < count; i++)
- localEndPoints[i] = new IPEndPoint(IPAddressExtension.Parse(bR), 53);
-
- _dnsServer.LocalEndPoints = localEndPoints;
- }
- }
- else
- {
- _dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) };
- }
-
- if (version >= 8)
- {
- _dnsServer.EnableDnsOverHttp = bR.ReadBoolean();
- _dnsServer.EnableDnsOverTls = bR.ReadBoolean();
- _dnsServer.EnableDnsOverHttps = bR.ReadBoolean();
- _dnsTlsCertificatePath = bR.ReadShortString();
- _dnsTlsCertificatePassword = bR.ReadShortString();
-
- if (_dnsTlsCertificatePath.Length == 0)
- _dnsTlsCertificatePath = null;
-
- if (_dnsTlsCertificatePath != null)
- {
- try
- {
- LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword);
- }
- catch (Exception ex)
- {
- _log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString());
- }
-
- StartTlsCertificateUpdateTimer();
- }
- }
- else
- {
- _dnsServer.EnableDnsOverHttp = false;
- _dnsServer.EnableDnsOverTls = false;
- _dnsServer.EnableDnsOverHttps = false;
- _dnsTlsCertificatePath = string.Empty;
- _dnsTlsCertificatePassword = string.Empty;
- }
-
- if (version >= 19)
- {
- _dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32();
- _dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32();
- _dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32();
- _dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32();
- }
- else
- {
- _dnsServer.CacheZoneManager.MinimumRecordTtl = CacheZoneManager.MINIMUM_RECORD_TTL;
- _dnsServer.CacheZoneManager.MaximumRecordTtl = CacheZoneManager.MAXIMUM_RECORD_TTL;
- _dnsServer.CacheZoneManager.NegativeRecordTtl = CacheZoneManager.NEGATIVE_RECORD_TTL;
- _dnsServer.CacheZoneManager.FailureRecordTtl = CacheZoneManager.FAILURE_RECORD_TTL;
- }
-
- if (version >= 21)
- {
- int count = bR.ReadByte();
- Dictionary 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 = true; //default true for security reasons
-
- if (version >= 23)
- {
- _dnsServer.AllowTxtBlockingReport = bR.ReadBoolean();
- _zonesApi.DefaultRecordTtl = bR.ReadUInt32();
- }
- else
- {
- _dnsServer.AllowTxtBlockingReport = true;
- _zonesApi.DefaultRecordTtl = 3600;
- }
-
- if (version >= 24)
- {
- _webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean();
-
- SelfSignedCertCheck(false, false);
- }
- else
- {
- _webServiceUseSelfSignedTlsCertificate = false;
- }
-
- if (version >= 25)
- _dnsServer.UdpPayloadSize = bR.ReadUInt16();
- else
- _dnsServer.UdpPayloadSize = DnsDatagram.EDNS_DEFAULT_UDP_PAYLOAD_SIZE;
-
- if (version >= 26)
- {
- _dnsServer.DnssecValidation = bR.ReadBoolean();
-
- _dnsServer.ResolverRetries = bR.ReadInt32();
- _dnsServer.ResolverTimeout = bR.ReadInt32();
- _dnsServer.ResolverMaxStackCount = bR.ReadInt32();
-
- _dnsServer.ForwarderRetries = bR.ReadInt32();
- _dnsServer.ForwarderTimeout = bR.ReadInt32();
- _dnsServer.ForwarderConcurrency = bR.ReadInt32();
-
- _dnsServer.ClientTimeout = bR.ReadInt32();
- _dnsServer.TcpSendTimeout = bR.ReadInt32();
- _dnsServer.TcpReceiveTimeout = bR.ReadInt32();
- }
- else
- {
- _dnsServer.DnssecValidation = true;
- CreateForwarderZoneToDisableDnssecForNTP();
-
- _dnsServer.ResolverRetries = 2;
- _dnsServer.ResolverTimeout = 2000;
- _dnsServer.ResolverMaxStackCount = 16;
-
- _dnsServer.ForwarderRetries = 3;
- _dnsServer.ForwarderTimeout = 2000;
- _dnsServer.ForwarderConcurrency = 2;
-
- _dnsServer.ClientTimeout = 4000;
- _dnsServer.TcpSendTimeout = 10000;
- _dnsServer.TcpReceiveTimeout = 10000;
- }
-
- if (version >= 27)
- _dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt32();
- else
- _dnsServer.CacheZoneManager.MaximumEntries = 10000;
-
- break;
-
- default:
- throw new InvalidDataException("DNS Server config version not supported.");
- }
+ version = ReadConfigFrom(new BinaryReader(fS));
}
_log.Write("DNS Server config file was loaded: " + configFile);
- if (passwordResetOption)
- {
- SetCredentials("admin", "admin");
-
- _log.Write("DNS Server reset password for user: admin");
- SaveConfigFile();
-
- try
- {
- File.Delete(configFile);
- }
- catch
- { }
- }
-
- if (version <= 6)
+ if (version <= 27)
SaveConfigFile(); //save as new config version to avoid loading old version next time
}
catch (FileNotFoundException)
@@ -3994,47 +3758,26 @@ namespace DnsServerCore
_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;
- string adminPassword = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD");
- string adminPasswordFile = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD_FILE");
-
- if (!string.IsNullOrEmpty(adminPassword))
- {
- SetCredentials("admin", adminPassword);
- }
- else if (!string.IsNullOrEmpty(adminPasswordFile))
- {
- try
- {
- using (StreamReader sR = new StreamReader(adminPasswordFile, true))
- {
- string password = sR.ReadLine();
- SetCredentials("admin", password);
- }
- }
- catch (Exception ex)
- {
- _log.Write(ex);
-
- SetCredentials("admin", "admin");
- }
- }
- else
- {
- SetCredentials("admin", "admin");
- }
+ _appsApi.EnableAutomaticUpdate = true;
string strPreferIPv6 = Environment.GetEnvironmentVariable("DNS_SERVER_PREFER_IPV6");
if (!string.IsNullOrEmpty(strPreferIPv6))
_dnsServer.PreferIPv6 = bool.Parse(strPreferIPv6);
+ _dnsServer.DnssecValidation = true;
+ CreateForwarderZoneToDisableDnssecForNTP();
+
+ //optional protocols
string strDnsOverHttp = Environment.GetEnvironmentVariable("DNS_SERVER_OPTIONAL_PROTOCOL_DNS_OVER_HTTP");
if (!string.IsNullOrEmpty(strDnsOverHttp))
_dnsServer.EnableDnsOverHttp = bool.Parse(strDnsOverHttp);
+ //recursion
string strRecursion = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION");
if (!string.IsNullOrEmpty(strRecursion))
_dnsServer.Recursion = Enum.Parse(strRecursion, true);
@@ -4065,6 +3808,14 @@ namespace DnsServerCore
_dnsServer.RecursionAllowedNetworks = networks;
}
+ _dnsServer.RandomizeName = true; //default true to enable security feature
+ _dnsServer.QnameMinimization = true; //default true to enable privacy feature
+ _dnsServer.NsRevalidation = true; //default true for security reasons
+
+ //cache
+ _dnsServer.CacheZoneManager.MaximumEntries = 10000;
+
+ //blocking
string strEnableBlocking = Environment.GetEnvironmentVariable("DNS_SERVER_ENABLE_BLOCKING");
if (!string.IsNullOrEmpty(strEnableBlocking))
_dnsServer.EnableBlocking = bool.Parse(strEnableBlocking);
@@ -4073,6 +3824,31 @@ namespace DnsServerCore
if (!string.IsNullOrEmpty(strAllowTxtBlockingReport))
_dnsServer.AllowTxtBlockingReport = bool.Parse(strAllowTxtBlockingReport);
+ string strBlockListUrls = Environment.GetEnvironmentVariable("DNS_SERVER_BLOCK_LIST_URLS");
+ if (!string.IsNullOrEmpty(strBlockListUrls))
+ {
+ string[] strBlockListUrlList = strBlockListUrls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (string strBlockListUrl in strBlockListUrlList)
+ {
+ if (strBlockListUrl.StartsWith("!"))
+ {
+ Uri allowListUrl = new Uri(strBlockListUrl.Substring(1));
+
+ if (!_dnsServer.BlockListZoneManager.AllowListUrls.Contains(allowListUrl))
+ _dnsServer.BlockListZoneManager.AllowListUrls.Add(allowListUrl);
+ }
+ else
+ {
+ Uri blockListUrl = new Uri(strBlockListUrl);
+
+ if (!_dnsServer.BlockListZoneManager.BlockListUrls.Contains(blockListUrl))
+ _dnsServer.BlockListZoneManager.BlockListUrls.Add(blockListUrl);
+ }
+ }
+ }
+
+ //proxy & forwarders
string strForwarders = Environment.GetEnvironmentVariable("DNS_SERVER_FORWARDERS");
if (!string.IsNullOrEmpty(strForwarders))
{
@@ -4093,19 +3869,11 @@ namespace DnsServerCore
_dnsServer.Forwarders = forwarders;
}
+ //logging
string strUseLocalTime = Environment.GetEnvironmentVariable("DNS_SERVER_LOG_USING_LOCAL_TIME");
if (!string.IsNullOrEmpty(strUseLocalTime))
_log.UseLocalTime = bool.Parse(strUseLocalTime);
- _dnsServer.RandomizeName = true; //default true to enable security feature
- _dnsServer.QnameMinimization = true; //default true to enable privacy feature
- _dnsServer.NsRevalidation = true; //default true for security reasons
-
- _dnsServer.DnssecValidation = true;
- CreateForwarderZoneToDisableDnssecForNTP();
-
- _dnsServer.CacheZoneManager.MaximumEntries = 10000;
-
SaveConfigFile();
}
catch (Exception ex)
@@ -4125,6 +3893,11 @@ namespace DnsServerCore
string fwdRecordComments = "This forwarder zone was automatically created to disable DNSSEC validation for ntp.org to allow systems with no real-time clock (e.g. Raspberry Pi) to sync time via NTP when booting.";
if (_dnsServer.AuthZoneManager.CreateForwarderZone(ntpDomain, DnsTransportProtocol.Udp, "this-server", false, NetProxyType.None, null, 0, null, null, fwdRecordComments) is not null)
{
+ //set permissions
+ _authManager.SetPermission(PermissionSection.Zones, ntpDomain, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
+ _authManager.SetPermission(PermissionSection.Zones, ntpDomain, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
+ _authManager.SaveConfigFile();
+
Directory.CreateDirectory(Path.Combine(_dnsServer.ConfigFolder, "zones"));
_dnsServer.AuthZoneManager.SaveZoneFile(ntpDomain);
}
@@ -4138,13 +3911,911 @@ namespace DnsServerCore
using (MemoryStream mS = new MemoryStream())
{
//serialize config
- BinaryWriter bW = new BinaryWriter(mS);
+ WriteConfigTo(new BinaryWriter(mS));
- bW.Write(Encoding.ASCII.GetBytes("DS")); //format
- bW.Write((byte)27); //version
+ //write config
+ mS.Position = 0;
- bW.WriteShortString(_dnsServer.ServerDomain);
+ using (FileStream fS = new FileStream(configFile, FileMode.Create, FileAccess.Write))
+ {
+ mS.CopyTo(fS);
+ }
+ }
+
+ _log.Write("DNS Server config file was saved: " + configFile);
+ }
+
+ private void InspectAndFixZonePermissions()
+ {
+ Permission permission = _authManager.GetPermission(PermissionSection.Zones);
+ 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
+ List zones = _dnsServer.AuthZoneManager.ListZones();
+ Group admins = _authManager.GetGroup(Group.ADMINISTRATORS);
+ Group dnsAdmins = _authManager.GetGroup(Group.DNS_ADMINISTRATORS);
+
+ foreach (AuthZoneInfo zone in zones)
+ {
+ if (zone.Internal)
+ {
+ _authManager.SetPermission(PermissionSection.Zones, zone.Name, admins, PermissionFlag.View);
+ _authManager.SetPermission(PermissionSection.Zones, zone.Name, dnsAdmins, PermissionFlag.View);
+ }
+ else
+ {
+ _authManager.SetPermission(PermissionSection.Zones, zone.Name, admins, PermissionFlag.ViewModifyDelete);
+ _authManager.SetPermission(PermissionSection.Zones, zone.Name, dnsAdmins, PermissionFlag.ViewModifyDelete);
+ }
+ }
+
+ _authManager.SaveConfigFile();
+ }
+
+ private int ReadConfigFrom(BinaryReader bR)
+ {
+ if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DS") //format
+ throw new InvalidDataException("DNS Server config file format is invalid.");
+
+ int version = bR.ReadByte();
+
+ if (version == 28)
+ {
+ ReadConfigFrom(bR, version);
+ }
+ else if ((version >= 2) && (version <= 27))
+ {
+ ReadOldConfigFrom(bR, version);
+
+ //new default settings
+ _appsApi.EnableAutomaticUpdate = true;
+ }
+ else
+ {
+ throw new InvalidDataException("DNS Server config version not supported.");
+ }
+
+ return version;
+ }
+
+ private void ReadConfigFrom(BinaryReader bR, int version)
+ {
+ //web service
+ {
+ _webServiceHttpPort = bR.ReadInt32();
+ _webServiceTlsPort = bR.ReadInt32();
+
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ IPAddress[] localAddresses = new IPAddress[count];
+
+ for (int i = 0; i < count; i++)
+ localAddresses[i] = IPAddressExtension.ReadFrom(bR);
+
+ _webServiceLocalAddresses = localAddresses;
+ }
+ }
+
+ _webServiceEnableTls = bR.ReadBoolean();
+ _webServiceHttpToTlsRedirect = bR.ReadBoolean();
+ _webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean();
+
+ _webServiceTlsCertificatePath = bR.ReadShortString();
+ _webServiceTlsCertificatePassword = bR.ReadShortString();
+ }
+
+ //dns
+ {
+ //general
+ _dnsServer.ServerDomain = bR.ReadShortString();
+
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ IPEndPoint[] localEndPoints = new IPEndPoint[count];
+
+ for (int i = 0; i < count; i++)
+ localEndPoints[i] = (IPEndPoint)EndPointExtension.ReadFrom(bR);
+
+ _dnsServer.LocalEndPoints = localEndPoints;
+ }
+ }
+
+ _zonesApi.DefaultRecordTtl = bR.ReadUInt32();
+ _appsApi.EnableAutomaticUpdate = bR.ReadBoolean();
+
+ _dnsServer.PreferIPv6 = bR.ReadBoolean();
+ _dnsServer.UdpPayloadSize = bR.ReadUInt16();
+ _dnsServer.DnssecValidation = bR.ReadBoolean();
+
+ _dnsServer.QpmLimitRequests = bR.ReadInt32();
+ _dnsServer.QpmLimitErrors = bR.ReadInt32();
+ _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
+ _dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32();
+ _dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32();
+
+ _dnsServer.ClientTimeout = bR.ReadInt32();
+ _dnsServer.TcpSendTimeout = bR.ReadInt32();
+ _dnsServer.TcpReceiveTimeout = bR.ReadInt32();
+
+ //optional protocols
+ _dnsServer.EnableDnsOverHttp = bR.ReadBoolean();
+ _dnsServer.EnableDnsOverTls = bR.ReadBoolean();
+ _dnsServer.EnableDnsOverHttps = bR.ReadBoolean();
+
+ _dnsTlsCertificatePath = bR.ReadShortString();
+ _dnsTlsCertificatePassword = bR.ReadShortString();
+
+ if (_dnsTlsCertificatePath.Length == 0)
+ _dnsTlsCertificatePath = null;
+
+ if (_dnsTlsCertificatePath != null)
+ {
+ try
+ {
+ LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword);
+ }
+ catch (Exception ex)
+ {
+ _log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString());
+ }
+
+ StartTlsCertificateUpdateTimer();
+ }
+ else
+ {
+ StopTlsCertificateUpdateTimer();
+ }
+
+ //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();
+
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ NetworkAddress[] networks = new NetworkAddress[count];
+
+ for (int i = 0; i < count; i++)
+ networks[i] = NetworkAddress.ReadFrom(bR);
+
+ _dnsServer.RecursionDeniedNetworks = networks;
+ }
+ }
+
+
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ NetworkAddress[] networks = new NetworkAddress[count];
+
+ for (int i = 0; i < count; i++)
+ networks[i] = NetworkAddress.ReadFrom(bR);
+
+ _dnsServer.RecursionAllowedNetworks = networks;
+ }
+ }
+
+ _dnsServer.RandomizeName = bR.ReadBoolean();
+ _dnsServer.QnameMinimization = bR.ReadBoolean();
+ _dnsServer.NsRevalidation = bR.ReadBoolean();
+
+ _dnsServer.ResolverRetries = bR.ReadInt32();
+ _dnsServer.ResolverTimeout = bR.ReadInt32();
+ _dnsServer.ResolverMaxStackCount = bR.ReadInt32();
+
+ //cache
+ _dnsServer.ServeStale = bR.ReadBoolean();
+ _dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32();
+
+ _dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt64();
+ _dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32();
+ _dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32();
+ _dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32();
+ _dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32();
+
+ _dnsServer.CachePrefetchEligibility = bR.ReadInt32();
+ _dnsServer.CachePrefetchTrigger = bR.ReadInt32();
+ _dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32();
+ _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32();
+
+ //blocking
+ _dnsServer.EnableBlocking = bR.ReadBoolean();
+ _dnsServer.AllowTxtBlockingReport = bR.ReadBoolean();
+
+ _dnsServer.BlockingType = (DnsServerBlockingType)bR.ReadByte();
+
+ {
+ //read custom blocking addresses
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ List dnsARecords = new List();
+ List dnsAAAARecords = new List();
+
+ for (int i = 0; i < count; i++)
+ {
+ IPAddress customAddress = IPAddressExtension.ReadFrom(bR);
+
+ switch (customAddress.AddressFamily)
+ {
+ case AddressFamily.InterNetwork:
+ dnsARecords.Add(new DnsARecordData(customAddress));
+ break;
+
+ case AddressFamily.InterNetworkV6:
+ dnsAAAARecords.Add(new DnsAAAARecordData(customAddress));
+ break;
+ }
+ }
+
+ _dnsServer.CustomBlockingARecords = dnsARecords;
+ _dnsServer.CustomBlockingAAAARecords = dnsAAAARecords;
+ }
+ }
+
+ {
+ //read block list urls
+ int count = bR.ReadByte();
+
+ for (int i = 0; i < count; i++)
+ {
+ string listUrl = bR.ReadShortString();
+
+ if (listUrl.StartsWith("!"))
+ _dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1)));
+ else
+ _dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl));
+ }
+
+ _blockListUpdateIntervalHours = bR.ReadInt32();
+ _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);
+
+ _dnsServer.Forwarders = forwarders;
+ }
+ }
+
+ _dnsServer.ForwarderRetries = bR.ReadInt32();
+ _dnsServer.ForwarderTimeout = bR.ReadInt32();
+ _dnsServer.ForwarderConcurrency = bR.ReadInt32();
+
+ //logging
+ if (bR.ReadBoolean()) //log all queries
+ _dnsServer.QueryLogManager = _log;
+ else
+ _dnsServer.QueryLogManager = null;
+
+ _dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32();
+ }
+ }
+
+ private void ReadOldConfigFrom(BinaryReader bR, int version)
+ {
+ _dnsServer.ServerDomain = bR.ReadShortString();
+ _webServiceHttpPort = bR.ReadInt32();
+
+ if (version >= 13)
+ {
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ IPAddress[] localAddresses = new IPAddress[count];
+
+ for (int i = 0; i < count; i++)
+ localAddresses[i] = IPAddressExtension.ReadFrom(bR);
+
+ _webServiceLocalAddresses = localAddresses;
+ }
+ }
+
+ _webServiceTlsPort = bR.ReadInt32();
+ _webServiceEnableTls = bR.ReadBoolean();
+ _webServiceHttpToTlsRedirect = bR.ReadBoolean();
+ _webServiceTlsCertificatePath = bR.ReadShortString();
+ _webServiceTlsCertificatePassword = bR.ReadShortString();
+
+ if (_webServiceTlsCertificatePath.Length == 0)
+ _webServiceTlsCertificatePath = null;
+
+ if (_webServiceTlsCertificatePath != null)
+ {
+ try
+ {
+ LoadWebServiceTlsCertificate(_webServiceTlsCertificatePath, _webServiceTlsCertificatePassword);
+ }
+ catch (Exception ex)
+ {
+ _log.Write("DNS Server encountered an error while loading Web Service TLS certificate: " + _webServiceTlsCertificatePath + "\r\n" + ex.ToString());
+ }
+
+ StartTlsCertificateUpdateTimer();
+ }
+ }
+ else
+ {
+ _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any };
+
+ _webServiceTlsPort = 53443;
+ _webServiceEnableTls = false;
+ _webServiceHttpToTlsRedirect = false;
+ _webServiceTlsCertificatePath = string.Empty;
+ _webServiceTlsCertificatePassword = string.Empty;
+ }
+
+ _dnsServer.PreferIPv6 = bR.ReadBoolean();
+
+ if (bR.ReadBoolean()) //logQueries
+ _dnsServer.QueryLogManager = _log;
+
+ if (version >= 14)
+ _dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32();
+ else
+ _dnsServer.StatsManager.MaxStatFileDays = 0;
+
+ if (version >= 17)
+ {
+ _dnsServer.Recursion = (DnsServerRecursion)bR.ReadByte();
+
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ NetworkAddress[] networks = new NetworkAddress[count];
+
+ for (int i = 0; i < count; i++)
+ networks[i] = NetworkAddress.ReadFrom(bR);
+
+ _dnsServer.RecursionDeniedNetworks = networks;
+ }
+ }
+
+
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ NetworkAddress[] networks = new NetworkAddress[count];
+
+ for (int i = 0; i < count; i++)
+ networks[i] = NetworkAddress.ReadFrom(bR);
+
+ _dnsServer.RecursionAllowedNetworks = networks;
+ }
+ }
+ }
+ else
+ {
+ bool allowRecursion = bR.ReadBoolean();
+ bool allowRecursionOnlyForPrivateNetworks;
+
+ if (version >= 4)
+ allowRecursionOnlyForPrivateNetworks = bR.ReadBoolean();
+ else
+ allowRecursionOnlyForPrivateNetworks = true; //default true for security reasons
+
+ if (allowRecursion)
+ {
+ if (allowRecursionOnlyForPrivateNetworks)
+ _dnsServer.Recursion = DnsServerRecursion.AllowOnlyForPrivateNetworks;
+ else
+ _dnsServer.Recursion = DnsServerRecursion.Allow;
+ }
+ else
+ {
+ _dnsServer.Recursion = DnsServerRecursion.Deny;
+ }
+ }
+
+ if (version >= 12)
+ _dnsServer.RandomizeName = bR.ReadBoolean();
+ else
+ _dnsServer.RandomizeName = true; //default true to enable security feature
+
+ if (version >= 15)
+ _dnsServer.QnameMinimization = bR.ReadBoolean();
+ else
+ _dnsServer.QnameMinimization = true; //default true to enable privacy feature
+
+ if (version >= 20)
+ {
+ _dnsServer.QpmLimitRequests = bR.ReadInt32();
+ _dnsServer.QpmLimitErrors = bR.ReadInt32();
+ _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
+ _dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32();
+ _dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32();
+ }
+ else if (version >= 17)
+ {
+ _dnsServer.QpmLimitRequests = bR.ReadInt32();
+ _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32();
+ _ = bR.ReadInt32(); //read obsolete value _dnsServer.QpmLimitSamplingIntervalInMinutes
+ }
+ else
+ {
+ _dnsServer.QpmLimitRequests = 0;
+ _dnsServer.QpmLimitErrors = 0;
+ _dnsServer.QpmLimitSampleMinutes = 1;
+ _dnsServer.QpmLimitIPv4PrefixLength = 24;
+ _dnsServer.QpmLimitIPv6PrefixLength = 56;
+ }
+
+ if (version >= 13)
+ {
+ _dnsServer.ServeStale = bR.ReadBoolean();
+ _dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32();
+ }
+ else
+ {
+ _dnsServer.ServeStale = true;
+ _dnsServer.CacheZoneManager.ServeStaleTtl = CacheZoneManager.SERVE_STALE_TTL;
+ }
+
+ if (version >= 9)
+ {
+ _dnsServer.CachePrefetchEligibility = bR.ReadInt32();
+ _dnsServer.CachePrefetchTrigger = bR.ReadInt32();
+ _dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32();
+ _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32();
+ }
+ else
+ {
+ _dnsServer.CachePrefetchEligibility = 2;
+ _dnsServer.CachePrefetchTrigger = 9;
+ _dnsServer.CachePrefetchSampleIntervalInMinutes = 5;
+ _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = 30;
+ }
+
+ NetProxyType proxyType = (NetProxyType)bR.ReadByte();
+ if (proxyType != NetProxyType.None)
+ {
+ string address = bR.ReadShortString();
+ int port = bR.ReadInt32();
+ NetworkCredential credential = null;
+
+ if (bR.ReadBoolean()) //credential set
+ credential = new NetworkCredential(bR.ReadShortString(), bR.ReadShortString());
+
+ _dnsServer.Proxy = NetProxy.CreateProxy(proxyType, address, port, credential);
+
+ if (version >= 10)
+ {
+ int count = bR.ReadByte();
+ List 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);
+
+ _dnsServer.Forwarders = forwarders;
+ }
+ }
+
+ if (version <= 10)
+ {
+ DnsTransportProtocol forwarderProtocol = (DnsTransportProtocol)bR.ReadByte();
+
+ 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 = IPAddressExtension.ReadFrom(bR);
+
+ switch (customAddress.AddressFamily)
+ {
+ case AddressFamily.InterNetwork:
+ dnsARecords.Add(new DnsARecordData(customAddress));
+ break;
+
+ case AddressFamily.InterNetworkV6:
+ dnsAAAARecords.Add(new DnsAAAARecordData(customAddress));
+ break;
+ }
+ }
+
+ _dnsServer.CustomBlockingARecords = dnsARecords;
+ _dnsServer.CustomBlockingAAAARecords = dnsAAAARecords;
+ }
+ }
+ else
+ {
+ _dnsServer.CustomBlockingARecords = null;
+ _dnsServer.CustomBlockingAAAARecords = null;
+ }
+
+ if (version > 4)
+ {
+ //read block list urls
+ int count = bR.ReadByte();
+
+ for (int i = 0; i < count; i++)
+ {
+ string listUrl = bR.ReadShortString();
+
+ if (listUrl.StartsWith("!"))
+ _dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1)));
+ else
+ _dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl));
+ }
+
+ _blockListLastUpdatedOn = bR.ReadDateTime();
+
+ if (version >= 13)
+ _blockListUpdateIntervalHours = bR.ReadInt32();
+ }
+ else
+ {
+ _dnsServer.BlockListZoneManager.AllowListUrls.Clear();
+ _dnsServer.BlockListZoneManager.BlockListUrls.Clear();
+ _blockListLastUpdatedOn = DateTime.MinValue;
+ _blockListUpdateIntervalHours = 24;
+ }
+
+ if (version >= 11)
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ IPEndPoint[] localEndPoints = new IPEndPoint[count];
+
+ for (int i = 0; i < count; i++)
+ localEndPoints[i] = (IPEndPoint)EndPointExtension.ReadFrom(bR);
+
+ _dnsServer.LocalEndPoints = localEndPoints;
+ }
+ }
+ else if (version >= 6)
+ {
+ int count = bR.ReadByte();
+ if (count > 0)
+ {
+ IPEndPoint[] localEndPoints = new IPEndPoint[count];
+
+ for (int i = 0; i < count; i++)
+ localEndPoints[i] = new IPEndPoint(IPAddressExtension.ReadFrom(bR), 53);
+
+ _dnsServer.LocalEndPoints = localEndPoints;
+ }
+ }
+ else
+ {
+ _dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) };
+ }
+
+ if (version >= 8)
+ {
+ _dnsServer.EnableDnsOverHttp = bR.ReadBoolean();
+ _dnsServer.EnableDnsOverTls = bR.ReadBoolean();
+ _dnsServer.EnableDnsOverHttps = bR.ReadBoolean();
+ _dnsTlsCertificatePath = bR.ReadShortString();
+ _dnsTlsCertificatePassword = bR.ReadShortString();
+
+ if (_dnsTlsCertificatePath.Length == 0)
+ _dnsTlsCertificatePath = null;
+
+ if (_dnsTlsCertificatePath != null)
+ {
+ try
+ {
+ LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword);
+ }
+ catch (Exception ex)
+ {
+ _log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString());
+ }
+
+ StartTlsCertificateUpdateTimer();
+ }
+ }
+ else
+ {
+ _dnsServer.EnableDnsOverHttp = false;
+ _dnsServer.EnableDnsOverTls = false;
+ _dnsServer.EnableDnsOverHttps = false;
+ _dnsTlsCertificatePath = string.Empty;
+ _dnsTlsCertificatePassword = string.Empty;
+ }
+
+ if (version >= 19)
+ {
+ _dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32();
+ _dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32();
+ _dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32();
+ _dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32();
+ }
+ else
+ {
+ _dnsServer.CacheZoneManager.MinimumRecordTtl = CacheZoneManager.MINIMUM_RECORD_TTL;
+ _dnsServer.CacheZoneManager.MaximumRecordTtl = CacheZoneManager.MAXIMUM_RECORD_TTL;
+ _dnsServer.CacheZoneManager.NegativeRecordTtl = CacheZoneManager.NEGATIVE_RECORD_TTL;
+ _dnsServer.CacheZoneManager.FailureRecordTtl = CacheZoneManager.FAILURE_RECORD_TTL;
+ }
+
+ if (version >= 21)
+ {
+ int count = bR.ReadByte();
+ Dictionary 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 = true; //default true for security reasons
+
+ if (version >= 23)
+ {
+ _dnsServer.AllowTxtBlockingReport = bR.ReadBoolean();
+ _zonesApi.DefaultRecordTtl = bR.ReadUInt32();
+ }
+ else
+ {
+ _dnsServer.AllowTxtBlockingReport = true;
+ _zonesApi.DefaultRecordTtl = 3600;
+ }
+
+ if (version >= 24)
+ {
+ _webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean();
+
+ SelfSignedCertCheck(false, false);
+ }
+ else
+ {
+ _webServiceUseSelfSignedTlsCertificate = false;
+ }
+
+ if (version >= 25)
+ _dnsServer.UdpPayloadSize = bR.ReadUInt16();
+ else
+ _dnsServer.UdpPayloadSize = DnsDatagram.EDNS_DEFAULT_UDP_PAYLOAD_SIZE;
+
+ if (version >= 26)
+ {
+ _dnsServer.DnssecValidation = bR.ReadBoolean();
+
+ _dnsServer.ResolverRetries = bR.ReadInt32();
+ _dnsServer.ResolverTimeout = bR.ReadInt32();
+ _dnsServer.ResolverMaxStackCount = bR.ReadInt32();
+
+ _dnsServer.ForwarderRetries = bR.ReadInt32();
+ _dnsServer.ForwarderTimeout = bR.ReadInt32();
+ _dnsServer.ForwarderConcurrency = bR.ReadInt32();
+
+ _dnsServer.ClientTimeout = bR.ReadInt32();
+ _dnsServer.TcpSendTimeout = bR.ReadInt32();
+ _dnsServer.TcpReceiveTimeout = bR.ReadInt32();
+ }
+ else
+ {
+ _dnsServer.DnssecValidation = true;
+ CreateForwarderZoneToDisableDnssecForNTP();
+
+ _dnsServer.ResolverRetries = 2;
+ _dnsServer.ResolverTimeout = 2000;
+ _dnsServer.ResolverMaxStackCount = 16;
+
+ _dnsServer.ForwarderRetries = 3;
+ _dnsServer.ForwarderTimeout = 2000;
+ _dnsServer.ForwarderConcurrency = 2;
+
+ _dnsServer.ClientTimeout = 4000;
+ _dnsServer.TcpSendTimeout = 10000;
+ _dnsServer.TcpReceiveTimeout = 10000;
+ }
+
+ if (version >= 27)
+ _dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt32();
+ else
+ _dnsServer.CacheZoneManager.MaximumEntries = 10000;
+ }
+
+ private void WriteConfigTo(BinaryWriter bW)
+ {
+ bW.Write(Encoding.ASCII.GetBytes("DS")); //format
+ bW.Write((byte)28); //version
+
+ //web service
+ {
bW.Write(_webServiceHttpPort);
+ bW.Write(_webServiceTlsPort);
{
bW.Write(Convert.ToByte(_webServiceLocalAddresses.Count));
@@ -4153,25 +4824,83 @@ namespace DnsServerCore
localAddress.WriteTo(bW);
}
- bW.Write(_webServiceTlsPort);
bW.Write(_webServiceEnableTls);
bW.Write(_webServiceHttpToTlsRedirect);
+ bW.Write(_webServiceUseSelfSignedTlsCertificate);
- if (_webServiceTlsCertificatePath == null)
+ if (_webServiceTlsCertificatePath is null)
bW.WriteShortString(string.Empty);
else
bW.WriteShortString(_webServiceTlsCertificatePath);
- if (_webServiceTlsCertificatePassword == null)
+ if (_webServiceTlsCertificatePassword is null)
bW.WriteShortString(string.Empty);
else
bW.WriteShortString(_webServiceTlsCertificatePassword);
+ }
+
+ //dns
+ {
+ //general
+ bW.WriteShortString(_dnsServer.ServerDomain);
+
+ {
+ bW.Write(Convert.ToByte(_dnsServer.LocalEndPoints.Count));
+
+ foreach (IPEndPoint localEP in _dnsServer.LocalEndPoints)
+ localEP.WriteTo(bW);
+ }
+
+ bW.Write(_zonesApi.DefaultRecordTtl);
+ bW.Write(_appsApi.EnableAutomaticUpdate);
bW.Write(_dnsServer.PreferIPv6);
+ bW.Write(_dnsServer.UdpPayloadSize);
+ bW.Write(_dnsServer.DnssecValidation);
- bW.Write(_dnsServer.QueryLogManager != null); //logQueries
- bW.Write(_dnsServer.StatsManager.MaxStatFileDays);
+ bW.Write(_dnsServer.QpmLimitRequests);
+ bW.Write(_dnsServer.QpmLimitErrors);
+ bW.Write(_dnsServer.QpmLimitSampleMinutes);
+ bW.Write(_dnsServer.QpmLimitIPv4PrefixLength);
+ bW.Write(_dnsServer.QpmLimitIPv6PrefixLength);
+ bW.Write(_dnsServer.ClientTimeout);
+ bW.Write(_dnsServer.TcpSendTimeout);
+ bW.Write(_dnsServer.TcpReceiveTimeout);
+
+ //optional protocols
+ bW.Write(_dnsServer.EnableDnsOverHttp);
+ bW.Write(_dnsServer.EnableDnsOverTls);
+ bW.Write(_dnsServer.EnableDnsOverHttps);
+
+ if (_dnsTlsCertificatePath == null)
+ bW.WriteShortString(string.Empty);
+ else
+ bW.WriteShortString(_dnsTlsCertificatePath);
+
+ if (_dnsTlsCertificatePassword == null)
+ bW.WriteShortString(string.Empty);
+ else
+ bW.WriteShortString(_dnsTlsCertificatePassword);
+
+ //tsig
+ if (_dnsServer.TsigKeys is null)
+ {
+ bW.Write((byte)0);
+ }
+ else
+ {
+ bW.Write(Convert.ToByte(_dnsServer.TsigKeys.Count));
+
+ foreach (KeyValuePair tsigKey in _dnsServer.TsigKeys)
+ {
+ bW.WriteShortString(tsigKey.Key);
+ bW.WriteShortString(tsigKey.Value.SharedSecret);
+ bW.Write((byte)tsigKey.Value.Algorithm);
+ }
+ }
+
+ //recursion
bW.Write((byte)_dnsServer.Recursion);
if (_dnsServer.RecursionDeniedNetworks is null)
@@ -4198,21 +4927,57 @@ namespace DnsServerCore
bW.Write(_dnsServer.RandomizeName);
bW.Write(_dnsServer.QnameMinimization);
+ bW.Write(_dnsServer.NsRevalidation);
- bW.Write(_dnsServer.QpmLimitRequests);
- bW.Write(_dnsServer.QpmLimitErrors);
- bW.Write(_dnsServer.QpmLimitSampleMinutes);
- bW.Write(_dnsServer.QpmLimitIPv4PrefixLength);
- bW.Write(_dnsServer.QpmLimitIPv6PrefixLength);
+ bW.Write(_dnsServer.ResolverRetries);
+ bW.Write(_dnsServer.ResolverTimeout);
+ bW.Write(_dnsServer.ResolverMaxStackCount);
+ //cache
bW.Write(_dnsServer.ServeStale);
bW.Write(_dnsServer.CacheZoneManager.ServeStaleTtl);
+ bW.Write(_dnsServer.CacheZoneManager.MaximumEntries);
+ bW.Write(_dnsServer.CacheZoneManager.MinimumRecordTtl);
+ bW.Write(_dnsServer.CacheZoneManager.MaximumRecordTtl);
+ bW.Write(_dnsServer.CacheZoneManager.NegativeRecordTtl);
+ bW.Write(_dnsServer.CacheZoneManager.FailureRecordTtl);
+
bW.Write(_dnsServer.CachePrefetchEligibility);
bW.Write(_dnsServer.CachePrefetchTrigger);
bW.Write(_dnsServer.CachePrefetchSampleIntervalInMinutes);
bW.Write(_dnsServer.CachePrefetchSampleEligibilityHitsPerHour);
+ //blocking
+ bW.Write(_dnsServer.EnableBlocking);
+ bW.Write(_dnsServer.AllowTxtBlockingReport);
+
+ bW.Write((byte)_dnsServer.BlockingType);
+
+ {
+ bW.Write(Convert.ToByte(_dnsServer.CustomBlockingARecords.Count + _dnsServer.CustomBlockingAAAARecords.Count));
+
+ foreach (DnsARecordData record in _dnsServer.CustomBlockingARecords)
+ record.Address.WriteTo(bW);
+
+ foreach (DnsAAAARecordData record in _dnsServer.CustomBlockingAAAARecords)
+ record.Address.WriteTo(bW);
+ }
+
+ {
+ bW.Write(Convert.ToByte(_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count));
+
+ foreach (Uri allowListUrl in _dnsServer.BlockListZoneManager.AllowListUrls)
+ bW.WriteShortString("!" + allowListUrl.AbsoluteUri);
+
+ foreach (Uri blockListUrl in _dnsServer.BlockListZoneManager.BlockListUrls)
+ bW.WriteShortString(blockListUrl.AbsoluteUri);
+
+ bW.Write(_blockListUpdateIntervalHours);
+ bW.Write(_blockListLastUpdatedOn);
+ }
+
+ //proxy & forwarders
if (_dnsServer.Proxy == null)
{
bW.Write((byte)NetProxyType.None);
@@ -4257,117 +5022,14 @@ namespace DnsServerCore
forwarder.WriteTo(bW);
}
- {
- bW.Write(Convert.ToByte(_credentials.Count));
-
- foreach (KeyValuePair credential in _credentials)
- {
- bW.WriteShortString(credential.Key);
- bW.WriteShortString(credential.Value);
- }
- }
-
- //block list
- bW.Write(_dnsServer.EnableBlocking);
- bW.Write((byte)_dnsServer.BlockingType);
-
- {
- bW.Write(Convert.ToByte(_dnsServer.CustomBlockingARecords.Count + _dnsServer.CustomBlockingAAAARecords.Count));
-
- foreach (DnsARecordData record in _dnsServer.CustomBlockingARecords)
- record.Address.WriteTo(bW);
-
- foreach (DnsAAAARecordData record in _dnsServer.CustomBlockingAAAARecords)
- record.Address.WriteTo(bW);
- }
-
- {
- bW.Write(Convert.ToByte(_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count));
-
- foreach (Uri allowListUrl in _dnsServer.BlockListZoneManager.AllowListUrls)
- bW.WriteShortString("!" + allowListUrl.AbsoluteUri);
-
- foreach (Uri blockListUrl in _dnsServer.BlockListZoneManager.BlockListUrls)
- bW.WriteShortString(blockListUrl.AbsoluteUri);
-
- bW.Write(_blockListLastUpdatedOn);
- bW.Write(_blockListUpdateIntervalHours);
- }
-
-
- {
- bW.Write(Convert.ToByte(_dnsServer.LocalEndPoints.Count));
-
- foreach (IPEndPoint localEP in _dnsServer.LocalEndPoints)
- localEP.WriteTo(bW);
- }
-
- bW.Write(_dnsServer.EnableDnsOverHttp);
- bW.Write(_dnsServer.EnableDnsOverTls);
- bW.Write(_dnsServer.EnableDnsOverHttps);
-
- if (_dnsTlsCertificatePath == null)
- bW.WriteShortString(string.Empty);
- else
- bW.WriteShortString(_dnsTlsCertificatePath);
-
- if (_dnsTlsCertificatePassword == null)
- bW.WriteShortString(string.Empty);
- else
- bW.WriteShortString(_dnsTlsCertificatePassword);
-
- bW.Write(_dnsServer.CacheZoneManager.MinimumRecordTtl);
- bW.Write(_dnsServer.CacheZoneManager.MaximumRecordTtl);
- bW.Write(_dnsServer.CacheZoneManager.NegativeRecordTtl);
- bW.Write(_dnsServer.CacheZoneManager.FailureRecordTtl);
-
- 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);
- }
- }
-
- bW.Write(_dnsServer.NsRevalidation);
- bW.Write(_dnsServer.AllowTxtBlockingReport);
- bW.Write(_zonesApi.DefaultRecordTtl);
- bW.Write(_webServiceUseSelfSignedTlsCertificate);
- bW.Write(_dnsServer.UdpPayloadSize);
- bW.Write(_dnsServer.DnssecValidation);
-
- bW.Write(_dnsServer.ResolverRetries);
- bW.Write(_dnsServer.ResolverTimeout);
- bW.Write(_dnsServer.ResolverMaxStackCount);
-
bW.Write(_dnsServer.ForwarderRetries);
bW.Write(_dnsServer.ForwarderTimeout);
bW.Write(_dnsServer.ForwarderConcurrency);
- bW.Write(_dnsServer.ClientTimeout);
- bW.Write(_dnsServer.TcpSendTimeout);
- bW.Write(_dnsServer.TcpReceiveTimeout);
-
- bW.Write(_dnsServer.CacheZoneManager.MaximumEntries);
-
- //write config
- mS.Position = 0;
-
- using (FileStream fS = new FileStream(configFile, FileMode.Create, FileAccess.Write))
- {
- mS.CopyTo(fS);
- }
+ //logging
+ bW.Write(_dnsServer.QueryLogManager is not null); //log all queries
+ bW.Write(_dnsServer.StatsManager.MaxStatFileDays);
}
-
- _log.Write("DNS Server config file was saved: " + configFile);
}
#endregion
@@ -4548,6 +5210,10 @@ namespace DnsServerCore
//init dhcp server
_dhcpServer = new DhcpServer(Path.Combine(_configFolder, "scopes"), _log);
_dhcpServer.AuthZoneManager = _dnsServer.AuthZoneManager;
+ _dhcpServer.AuthManager = _authManager;
+
+ //load auth config
+ _authManager.LoadConfigFile();
//load config
LoadConfigFile();
@@ -4557,6 +5223,7 @@ namespace DnsServerCore
//load all zones files
_dnsServer.AuthZoneManager.LoadAllZoneFiles();
+ InspectAndFixZonePermissions();
//disable zones from old config format
if (_configDisabledZones != null)
@@ -4577,7 +5244,7 @@ namespace DnsServerCore
_dnsServer.BlockedZoneManager.LoadBlockedZoneFile();
//load block list zone async
- if (_dnsServer.BlockListZoneManager.BlockListUrls.Count > 0)
+ if ((_blockListUpdateIntervalHours > 0) && (_dnsServer.BlockListZoneManager.BlockListUrls.Count > 0))
{
ThreadPool.QueueUserWorkItem(delegate (object state)
{
@@ -4648,12 +5315,21 @@ namespace DnsServerCore
internal LogManager Log
{ get { return _log; } }
+ internal AuthManager AuthManager
+ { get { return _authManager; } }
+
+ internal WebServiceZonesApi ZonesApi
+ { get { return _zonesApi; } }
+
internal DnsServer DnsServer
{ get { return _dnsServer; } }
internal DhcpServer DhcpServer
{ get { return _dhcpServer; } }
+ internal Version ServerVersion
+ { get { return _currentVersion; } }
+
public string ConfigFolder
{ get { return _configFolder; } }