/* Technitium DNS Server Copyright (C) 2024 Shreyas Zare (shreyas@technitium.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ using DnsServerCore.Dns.Dnssec; using DnsServerCore.Dns.ResourceRecords; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using TechnitiumLibrary.IO; using TechnitiumLibrary.Net; using TechnitiumLibrary.Net.Dns; using TechnitiumLibrary.Net.Dns.ResourceRecords; namespace DnsServerCore.Dns.Zones { public enum AuthZoneType : byte { Unknown = 0, Primary = 1, Secondary = 2, Stub = 3, Forwarder = 4, SecondaryForwarder = 5, Catalog = 6, SecondaryCatalog = 7 } public sealed class AuthZoneInfo : IComparable { #region variables readonly ApexZone _apexZone; readonly string _name; readonly AuthZoneType _type; readonly DateTime _lastModified; readonly bool _disabled; readonly string _catalogZoneName; readonly bool _overrideCatalogQueryAccess; readonly bool _overrideCatalogZoneTransfer; readonly bool _overrideCatalogNotify; readonly bool _overrideCatalogPrimaryNameServers; //only for secondary zones readonly AuthZoneQueryAccess _queryAccess; readonly IReadOnlyCollection _queryAccessNetworkACL; readonly AuthZoneTransfer _zoneTransfer; readonly IReadOnlyCollection _zoneTransferNetworkACL; readonly IReadOnlyDictionary _zoneTransferTsigKeyNames; readonly IReadOnlyList _zoneHistory; //for IXFR support readonly AuthZoneNotify _notify; readonly IReadOnlyCollection _notifyNameServers; readonly IReadOnlyCollection _notifySecondaryCatalogNameServers; readonly AuthZoneUpdate _update; readonly IReadOnlyCollection _updateNetworkACL; readonly IReadOnlyDictionary>> _updateSecurityPolicies; readonly IReadOnlyCollection _dnssecPrivateKeys; //only for primary zones readonly IReadOnlyList _primaryNameServerAddresses; //only for secondary and stub zones readonly DnsTransportProtocol _primaryZoneTransferProtocol; //only for secondary zones readonly string _primaryZoneTransferTsigKeyName; //only for secondary zones readonly DateTime _expiry; //only for secondary and stub zones readonly bool _validateZone; //only for secondary zones readonly bool _validationFailed; //only for secondary zones #endregion #region constructor public AuthZoneInfo(string name, AuthZoneType type, bool disabled) { _name = name; _type = type; _lastModified = DateTime.UtcNow; _disabled = disabled; _queryAccess = AuthZoneQueryAccess.Allow; switch (_type) { case AuthZoneType.Primary: _zoneTransfer = AuthZoneTransfer.AllowOnlyZoneNameServers; _notify = AuthZoneNotify.ZoneNameServers; _update = AuthZoneUpdate.Deny; break; default: _zoneTransfer = AuthZoneTransfer.Deny; _notify = AuthZoneNotify.None; _update = AuthZoneUpdate.Deny; break; } } public AuthZoneInfo(BinaryReader bR, DateTime lastModified) { byte version = bR.ReadByte(); switch (version) { case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: { _name = bR.ReadShortString(); _type = (AuthZoneType)bR.ReadByte(); _disabled = bR.ReadBoolean(); _queryAccess = AuthZoneQueryAccess.Allow; if (version >= 2) { { _zoneTransfer = (AuthZoneTransfer)bR.ReadByte(); int count = bR.ReadByte(); if (count > 0) { NetworkAddress[] networks = new NetworkAddress[count]; if (version >= 9) { for (int i = 0; i < count; i++) networks[i] = NetworkAddress.ReadFrom(bR); } else { for (int i = 0; i < count; i++) { IPAddress address = IPAddressExtensions.ReadFrom(bR); switch (address.AddressFamily) { case AddressFamily.InterNetwork: networks[i] = new NetworkAddress(address, 32); break; case AddressFamily.InterNetworkV6: networks[i] = new NetworkAddress(address, 128); break; default: throw new InvalidOperationException(); } } } _zoneTransferNetworkACL = ConvertDenyAllowToACL(null, networks); } } { _notify = (AuthZoneNotify)bR.ReadByte(); int count = bR.ReadByte(); if (count > 0) { IPAddress[] nameServers = new IPAddress[count]; for (int i = 0; i < count; i++) nameServers[i] = IPAddressExtensions.ReadFrom(bR); _notifyNameServers = nameServers; } } if (version >= 6) { _update = (AuthZoneUpdate)bR.ReadByte(); int count = bR.ReadByte(); if (count > 0) { NetworkAddress[] networks = new NetworkAddress[count]; if (version >= 9) { for (int i = 0; i < count; i++) networks[i] = NetworkAddress.ReadFrom(bR); } else { for (int i = 0; i < count; i++) { IPAddress address = IPAddressExtensions.ReadFrom(bR); switch (address.AddressFamily) { case AddressFamily.InterNetwork: networks[i] = new NetworkAddress(address, 32); break; case AddressFamily.InterNetworkV6: networks[i] = new NetworkAddress(address, 128); break; default: throw new InvalidOperationException(); } } } _updateNetworkACL = ConvertDenyAllowToACL(null, networks); } } } else { switch (_type) { case AuthZoneType.Primary: _zoneTransfer = AuthZoneTransfer.AllowOnlyZoneNameServers; _notify = AuthZoneNotify.ZoneNameServers; _update = AuthZoneUpdate.Deny; break; default: _zoneTransfer = AuthZoneTransfer.Deny; _notify = AuthZoneNotify.None; _update = AuthZoneUpdate.Deny; break; } } if (version >= 8) _lastModified = bR.ReadDateTime(); else _lastModified = lastModified; switch (_type) { case AuthZoneType.Primary: { if (version >= 3) { int count = bR.ReadInt32(); DnsResourceRecord[] zoneHistory = new DnsResourceRecord[count]; if (version >= 11) { for (int i = 0; i < count; i++) { zoneHistory[i] = new DnsResourceRecord(bR.BaseStream); if (bR.ReadBoolean()) zoneHistory[i].Tag = new HistoryRecordInfo(bR); } } else { for (int i = 0; i < count; i++) { zoneHistory[i] = new DnsResourceRecord(bR.BaseStream); zoneHistory[i].Tag = new HistoryRecordInfo(bR); } } _zoneHistory = zoneHistory; } if (version >= 4) { int count = bR.ReadByte(); Dictionary tsigKeyNames = new Dictionary(count); for (int i = 0; i < count; i++) tsigKeyNames.Add(bR.ReadShortString(), null); _zoneTransferTsigKeyNames = tsigKeyNames; } if (version >= 7) { int count = bR.ReadByte(); Dictionary>> updateSecurityPolicies = new Dictionary>>(count); for (int i = 0; i < count; i++) { string tsigKeyName = bR.ReadShortString().ToLowerInvariant(); if (!updateSecurityPolicies.TryGetValue(tsigKeyName, out IReadOnlyDictionary> policyMap)) { policyMap = new Dictionary>(); updateSecurityPolicies.Add(tsigKeyName, policyMap); } int policyCount = bR.ReadByte(); for (int j = 0; j < policyCount; j++) { string domain = bR.ReadShortString().ToLowerInvariant(); if (!policyMap.TryGetValue(domain, out IReadOnlyList types)) { types = new List(); (policyMap as Dictionary>).Add(domain, types); } int typeCount = bR.ReadByte(); for (int k = 0; k < typeCount; k++) (types as List).Add((DnsResourceRecordType)bR.ReadUInt16()); } } _updateSecurityPolicies = updateSecurityPolicies; } else if (version >= 6) { int count = bR.ReadByte(); Dictionary>> updateSecurityPolicies = new Dictionary>>(count); Dictionary> defaultAllowPolicy = new Dictionary>(1); defaultAllowPolicy.Add(_name, new List() { DnsResourceRecordType.ANY }); defaultAllowPolicy.Add("*." + _name, new List() { DnsResourceRecordType.ANY }); for (int i = 0; i < count; i++) updateSecurityPolicies.Add(bR.ReadShortString().ToLowerInvariant(), defaultAllowPolicy); _updateSecurityPolicies = updateSecurityPolicies; } if (version >= 5) { int count = bR.ReadByte(); if (count > 0) { List dnssecPrivateKeys = new List(count); for (int i = 0; i < count; i++) dnssecPrivateKeys.Add(DnssecPrivateKey.ReadFrom(bR)); _dnssecPrivateKeys = dnssecPrivateKeys; } } } break; case AuthZoneType.Secondary: { _expiry = bR.ReadDateTime(); if (version >= 4) { int count = bR.ReadInt32(); DnsResourceRecord[] zoneHistory = new DnsResourceRecord[count]; if (version >= 11) { for (int i = 0; i < count; i++) { zoneHistory[i] = new DnsResourceRecord(bR.BaseStream); if (bR.ReadBoolean()) zoneHistory[i].Tag = new HistoryRecordInfo(bR); } } else { for (int i = 0; i < count; i++) { zoneHistory[i] = new DnsResourceRecord(bR.BaseStream); zoneHistory[i].Tag = new HistoryRecordInfo(bR); } } _zoneHistory = zoneHistory; } if (version >= 4) { int count = bR.ReadByte(); Dictionary tsigKeyNames = new Dictionary(count); for (int i = 0; i < count; i++) tsigKeyNames.Add(bR.ReadShortString(), null); _zoneTransferTsigKeyNames = tsigKeyNames; } if (version == 6) { //MUST skip old version data int count = bR.ReadByte(); Dictionary tsigKeyNames = new Dictionary(count); for (int i = 0; i < count; i++) tsigKeyNames.Add(bR.ReadShortString(), null); } } break; case AuthZoneType.Stub: { _expiry = bR.ReadDateTime(); } break; case AuthZoneType.Forwarder: { if (version >= 10) { int count = bR.ReadByte(); Dictionary>> updateSecurityPolicies = new Dictionary>>(count); for (int i = 0; i < count; i++) { string tsigKeyName = bR.ReadShortString().ToLowerInvariant(); if (!updateSecurityPolicies.TryGetValue(tsigKeyName, out IReadOnlyDictionary> policyMap)) { policyMap = new Dictionary>(); updateSecurityPolicies.Add(tsigKeyName, policyMap); } int policyCount = bR.ReadByte(); for (int j = 0; j < policyCount; j++) { string domain = bR.ReadShortString().ToLowerInvariant(); if (!policyMap.TryGetValue(domain, out IReadOnlyList types)) { types = new List(); (policyMap as Dictionary>).Add(domain, types); } int typeCount = bR.ReadByte(); for (int k = 0; k < typeCount; k++) (types as List).Add((DnsResourceRecordType)bR.ReadUInt16()); } } _updateSecurityPolicies = updateSecurityPolicies; } } break; } } break; case 12: case 13: { _name = bR.ReadShortString(); _type = (AuthZoneType)bR.ReadByte(); _lastModified = bR.ReadDateTime(); _disabled = bR.ReadBoolean(); switch (_type) { case AuthZoneType.Primary: _catalogZoneName = bR.ReadShortString(); if (_catalogZoneName.Length == 0) _catalogZoneName = null; _overrideCatalogQueryAccess = bR.ReadBoolean(); _overrideCatalogZoneTransfer = bR.ReadBoolean(); _overrideCatalogNotify = bR.ReadBoolean(); _queryAccess = (AuthZoneQueryAccess)bR.ReadByte(); _queryAccessNetworkACL = ReadNetworkACLFrom(bR); _zoneTransfer = (AuthZoneTransfer)bR.ReadByte(); _zoneTransferNetworkACL = ReadNetworkACLFrom(bR); _zoneTransferTsigKeyNames = ReadZoneTransferTsigKeyNamesFrom(bR); _zoneHistory = ReadZoneHistoryFrom(bR); _notify = (AuthZoneNotify)bR.ReadByte(); _notifyNameServers = ReadIPAddressesFrom(bR); _update = (AuthZoneUpdate)bR.ReadByte(); _updateNetworkACL = ReadNetworkACLFrom(bR); _updateSecurityPolicies = ReadUpdateSecurityPoliciesFrom(bR); _dnssecPrivateKeys = ReadDnssecPrivateKeysFrom(bR); break; case AuthZoneType.Secondary: _catalogZoneName = bR.ReadShortString(); if (_catalogZoneName.Length == 0) _catalogZoneName = null; _overrideCatalogQueryAccess = bR.ReadBoolean(); _overrideCatalogZoneTransfer = bR.ReadBoolean(); _overrideCatalogPrimaryNameServers = bR.ReadBoolean(); _queryAccess = (AuthZoneQueryAccess)bR.ReadByte(); _queryAccessNetworkACL = ReadNetworkACLFrom(bR); _zoneTransfer = (AuthZoneTransfer)bR.ReadByte(); _zoneTransferNetworkACL = ReadNetworkACLFrom(bR); _zoneTransferTsigKeyNames = ReadZoneTransferTsigKeyNamesFrom(bR); _zoneHistory = ReadZoneHistoryFrom(bR); _notify = (AuthZoneNotify)bR.ReadByte(); _notifyNameServers = ReadIPAddressesFrom(bR); _update = (AuthZoneUpdate)bR.ReadByte(); _updateNetworkACL = ReadNetworkACLFrom(bR); _primaryNameServerAddresses = ReadNameServerAddressesFrom(bR); _primaryZoneTransferProtocol = (DnsTransportProtocol)bR.ReadByte(); _primaryZoneTransferTsigKeyName = bR.ReadShortString(); if (_primaryZoneTransferTsigKeyName.Length == 0) _primaryZoneTransferTsigKeyName = null; _expiry = bR.ReadDateTime(); _validateZone = bR.ReadBoolean(); _validationFailed = bR.ReadBoolean(); break; case AuthZoneType.Stub: _catalogZoneName = bR.ReadShortString(); if (_catalogZoneName.Length == 0) _catalogZoneName = null; _overrideCatalogQueryAccess = bR.ReadBoolean(); _queryAccess = (AuthZoneQueryAccess)bR.ReadByte(); _queryAccessNetworkACL = ReadNetworkACLFrom(bR); _primaryNameServerAddresses = ReadNameServerAddressesFrom(bR); _expiry = bR.ReadDateTime(); break; case AuthZoneType.Forwarder: _catalogZoneName = bR.ReadShortString(); if (_catalogZoneName.Length == 0) _catalogZoneName = null; _overrideCatalogQueryAccess = bR.ReadBoolean(); _overrideCatalogZoneTransfer = bR.ReadBoolean(); _overrideCatalogNotify = bR.ReadBoolean(); _queryAccess = (AuthZoneQueryAccess)bR.ReadByte(); _queryAccessNetworkACL = ReadNetworkACLFrom(bR); _zoneTransfer = (AuthZoneTransfer)bR.ReadByte(); _zoneTransferNetworkACL = ReadNetworkACLFrom(bR); _zoneTransferTsigKeyNames = ReadZoneTransferTsigKeyNamesFrom(bR); _zoneHistory = ReadZoneHistoryFrom(bR); _notify = (AuthZoneNotify)bR.ReadByte(); _notifyNameServers = ReadIPAddressesFrom(bR); _update = (AuthZoneUpdate)bR.ReadByte(); _updateNetworkACL = ReadNetworkACLFrom(bR); _updateSecurityPolicies = ReadUpdateSecurityPoliciesFrom(bR); break; case AuthZoneType.SecondaryForwarder: _catalogZoneName = bR.ReadShortString(); if (_catalogZoneName.Length == 0) _catalogZoneName = null; _overrideCatalogQueryAccess = bR.ReadBoolean(); _queryAccess = (AuthZoneQueryAccess)bR.ReadByte(); _queryAccessNetworkACL = ReadNetworkACLFrom(bR); _update = (AuthZoneUpdate)bR.ReadByte(); _updateNetworkACL = ReadNetworkACLFrom(bR); _primaryNameServerAddresses = ReadNameServerAddressesFrom(bR); _primaryZoneTransferProtocol = (DnsTransportProtocol)bR.ReadByte(); _primaryZoneTransferTsigKeyName = bR.ReadShortString(); if (_primaryZoneTransferTsigKeyName.Length == 0) _primaryZoneTransferTsigKeyName = null; _expiry = bR.ReadDateTime(); break; case AuthZoneType.Catalog: _queryAccess = (AuthZoneQueryAccess)bR.ReadByte(); _queryAccessNetworkACL = ReadNetworkACLFrom(bR); _zoneTransfer = (AuthZoneTransfer)bR.ReadByte(); _zoneTransferNetworkACL = ReadNetworkACLFrom(bR); _zoneTransferTsigKeyNames = ReadZoneTransferTsigKeyNamesFrom(bR); _zoneHistory = ReadZoneHistoryFrom(bR); _notify = (AuthZoneNotify)bR.ReadByte(); _notifyNameServers = ReadIPAddressesFrom(bR); if (version >= 13) _notifySecondaryCatalogNameServers = ReadIPAddressesFrom(bR); break; case AuthZoneType.SecondaryCatalog: _queryAccess = (AuthZoneQueryAccess)bR.ReadByte(); _queryAccessNetworkACL = ReadNetworkACLFrom(bR); _zoneTransfer = (AuthZoneTransfer)bR.ReadByte(); _zoneTransferNetworkACL = ReadNetworkACLFrom(bR); _zoneTransferTsigKeyNames = ReadZoneTransferTsigKeyNamesFrom(bR); _primaryNameServerAddresses = ReadNameServerAddressesFrom(bR); _primaryZoneTransferProtocol = (DnsTransportProtocol)bR.ReadByte(); _primaryZoneTransferTsigKeyName = bR.ReadShortString(); if (_primaryZoneTransferTsigKeyName.Length == 0) _primaryZoneTransferTsigKeyName = null; _expiry = bR.ReadDateTime(); break; } } break; default: throw new InvalidDataException("AuthZoneInfo format version not supported."); } } internal AuthZoneInfo(ApexZone apexZone, bool loadHistory = false) { _apexZone = apexZone; _name = _apexZone.Name; _lastModified = _apexZone.LastModified; _disabled = _apexZone.Disabled; if (_apexZone is PrimaryZone primaryZone) { _type = AuthZoneType.Primary; _catalogZoneName = _apexZone.CatalogZoneName; _overrideCatalogQueryAccess = _apexZone.OverrideCatalogQueryAccess; _overrideCatalogZoneTransfer = _apexZone.OverrideCatalogZoneTransfer; _overrideCatalogNotify = _apexZone.OverrideCatalogNotify; _queryAccess = _apexZone.QueryAccess; _queryAccessNetworkACL = _apexZone.QueryAccessNetworkACL; _zoneTransfer = _apexZone.ZoneTransfer; _zoneTransferNetworkACL = _apexZone.ZoneTransferNetworkACL; _zoneTransferTsigKeyNames = _apexZone.ZoneTransferTsigKeyNames; if (loadHistory) _zoneHistory = _apexZone.GetZoneHistory(); _notify = _apexZone.Notify; _notifyNameServers = _apexZone.NotifyNameServers; _update = _apexZone.Update; _updateNetworkACL = _apexZone.UpdateNetworkACL; _updateSecurityPolicies = _apexZone.UpdateSecurityPolicies; _dnssecPrivateKeys = primaryZone.DnssecPrivateKeys; } else if (_apexZone is SecondaryCatalogZone secondaryCatalogZone) { _type = AuthZoneType.SecondaryCatalog; _queryAccess = _apexZone.QueryAccess; _queryAccessNetworkACL = _apexZone.QueryAccessNetworkACL; _zoneTransfer = _apexZone.ZoneTransfer; _zoneTransferNetworkACL = _apexZone.ZoneTransferNetworkACL; _zoneTransferTsigKeyNames = _apexZone.ZoneTransferTsigKeyNames; _primaryNameServerAddresses = secondaryCatalogZone.PrimaryNameServerAddresses; _primaryZoneTransferProtocol = secondaryCatalogZone.PrimaryZoneTransferProtocol; _primaryZoneTransferTsigKeyName = secondaryCatalogZone.PrimaryZoneTransferTsigKeyName; _expiry = secondaryCatalogZone.Expiry; } else if (_apexZone is SecondaryForwarderZone secondaryForwarderZone) { _type = AuthZoneType.SecondaryForwarder; _catalogZoneName = _apexZone.CatalogZoneName; _overrideCatalogQueryAccess = _apexZone.OverrideCatalogQueryAccess; _queryAccess = _apexZone.QueryAccess; _queryAccessNetworkACL = _apexZone.QueryAccessNetworkACL; _update = _apexZone.Update; _updateNetworkACL = _apexZone.UpdateNetworkACL; _primaryNameServerAddresses = secondaryForwarderZone.PrimaryNameServerAddresses; _primaryZoneTransferProtocol = secondaryForwarderZone.PrimaryZoneTransferProtocol; _primaryZoneTransferTsigKeyName = secondaryForwarderZone.PrimaryZoneTransferTsigKeyName; _expiry = secondaryForwarderZone.Expiry; } else if (_apexZone is SecondaryZone secondaryZone) { _type = AuthZoneType.Secondary; _catalogZoneName = _apexZone.CatalogZoneName; _overrideCatalogQueryAccess = _apexZone.OverrideCatalogQueryAccess; _overrideCatalogZoneTransfer = _apexZone.OverrideCatalogZoneTransfer; _overrideCatalogPrimaryNameServers = secondaryZone.OverrideCatalogPrimaryNameServers; _queryAccess = _apexZone.QueryAccess; _queryAccessNetworkACL = _apexZone.QueryAccessNetworkACL; _zoneTransfer = _apexZone.ZoneTransfer; _zoneTransferNetworkACL = _apexZone.ZoneTransferNetworkACL; _zoneTransferTsigKeyNames = _apexZone.ZoneTransferTsigKeyNames; if (loadHistory) _zoneHistory = _apexZone.GetZoneHistory(); _notify = _apexZone.Notify; _notifyNameServers = _apexZone.NotifyNameServers; _update = _apexZone.Update; _updateNetworkACL = _apexZone.UpdateNetworkACL; _primaryNameServerAddresses = secondaryZone.PrimaryNameServerAddresses; _primaryZoneTransferProtocol = secondaryZone.PrimaryZoneTransferProtocol; _primaryZoneTransferTsigKeyName = secondaryZone.PrimaryZoneTransferTsigKeyName; _expiry = secondaryZone.Expiry; _validateZone = secondaryZone.ValidateZone; _validationFailed = secondaryZone.ValidationFailed; } else if (_apexZone is StubZone stubZone) { _type = AuthZoneType.Stub; _catalogZoneName = _apexZone.CatalogZoneName; _overrideCatalogQueryAccess = _apexZone.OverrideCatalogQueryAccess; _queryAccess = _apexZone.QueryAccess; _queryAccessNetworkACL = _apexZone.QueryAccessNetworkACL; _primaryNameServerAddresses = stubZone.PrimaryNameServerAddresses; _expiry = stubZone.Expiry; } else if (_apexZone is CatalogZone) { _type = AuthZoneType.Catalog; _queryAccess = _apexZone.QueryAccess; _queryAccessNetworkACL = _apexZone.QueryAccessNetworkACL; _zoneTransfer = _apexZone.ZoneTransfer; _zoneTransferNetworkACL = _apexZone.ZoneTransferNetworkACL; _zoneTransferTsigKeyNames = _apexZone.ZoneTransferTsigKeyNames; if (loadHistory) _zoneHistory = _apexZone.GetZoneHistory(); _notify = _apexZone.Notify; _notifyNameServers = _apexZone.NotifyNameServers; _notifySecondaryCatalogNameServers = _apexZone.NotifySecondaryCatalogNameServers; } else if (_apexZone is ForwarderZone) { _type = AuthZoneType.Forwarder; _catalogZoneName = _apexZone.CatalogZoneName; _overrideCatalogQueryAccess = _apexZone.OverrideCatalogQueryAccess; _overrideCatalogZoneTransfer = _apexZone.OverrideCatalogZoneTransfer; _overrideCatalogNotify = _apexZone.OverrideCatalogNotify; _queryAccess = _apexZone.QueryAccess; _queryAccessNetworkACL = _apexZone.QueryAccessNetworkACL; _zoneTransfer = _apexZone.ZoneTransfer; _zoneTransferNetworkACL = _apexZone.ZoneTransferNetworkACL; _zoneTransferTsigKeyNames = _apexZone.ZoneTransferTsigKeyNames; if (loadHistory) _zoneHistory = _apexZone.GetZoneHistory(); _notify = _apexZone.Notify; _notifyNameServers = _apexZone.NotifyNameServers; _update = _apexZone.Update; _updateNetworkACL = _apexZone.UpdateNetworkACL; _updateSecurityPolicies = _apexZone.UpdateSecurityPolicies; } else { _type = AuthZoneType.Unknown; } } #endregion #region static public static string GetZoneTypeName(AuthZoneType type) { switch (type) { case AuthZoneType.SecondaryForwarder: return "Secondary Forwarder"; case AuthZoneType.SecondaryCatalog: return "Secondary Catalog"; default: return type.ToString(); } } internal static NameServerAddress[] ReadNameServerAddressesFrom(BinaryReader bR) { int count = bR.ReadByte(); if (count < 1) return null; NameServerAddress[] nameServerAddresses = new NameServerAddress[count]; for (int i = 0; i < count; i++) nameServerAddresses[i] = new NameServerAddress(bR); return nameServerAddresses; } internal static void WriteNameServerAddressesTo(IReadOnlyCollection nameServerAddresses, BinaryWriter bW) { if (nameServerAddresses is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(nameServerAddresses.Count)); foreach (NameServerAddress network in nameServerAddresses) network.WriteTo(bW); } } internal static NetworkAccessControl[] ReadNetworkACLFrom(BinaryReader bR) { int count = bR.ReadByte(); if (count < 1) return null; NetworkAccessControl[] acl = new NetworkAccessControl[count]; for (int i = 0; i < count; i++) acl[i] = NetworkAccessControl.ReadFrom(bR); return acl; } internal static void WriteNetworkACLTo(IReadOnlyCollection acl, BinaryWriter bW) { if (acl is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(acl.Count)); foreach (NetworkAccessControl nac in acl) nac.WriteTo(bW); } } internal static NetworkAddress[] ReadNetworkAddressesFrom(BinaryReader bR) { int count = bR.ReadByte(); if (count < 1) return null; NetworkAddress[] networks = new NetworkAddress[count]; for (int i = 0; i < count; i++) networks[i] = NetworkAddress.ReadFrom(bR); return networks; } internal static void WriteNetworkAddressesTo(IReadOnlyCollection networkAddresses, BinaryWriter bW) { if (networkAddresses is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(networkAddresses.Count)); foreach (NetworkAddress network in networkAddresses) network.WriteTo(bW); } } internal static IPAddress[] ReadIPAddressesFrom(BinaryReader bR) { int count = bR.ReadByte(); if (count < 1) return null; IPAddress[] ipAddresses = new IPAddress[count]; for (int i = 0; i < count; i++) ipAddresses[i] = IPAddressExtensions.ReadFrom(bR); return ipAddresses; } internal static void WriteIPAddressesTo(IReadOnlyCollection ipAddresses, BinaryWriter bW) { if (ipAddresses is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(ipAddresses.Count)); foreach (IPAddress ipAddress in ipAddresses) ipAddress.WriteTo(bW); } } internal static List ConvertDenyAllowToACL(NetworkAddress[] deniedNetworks, NetworkAddress[] allowedNetworks) { List acl = new List(); if (deniedNetworks is not null) { foreach (NetworkAddress network in deniedNetworks) acl.Add(new NetworkAccessControl(network, true)); } if (allowedNetworks is not null) { foreach (NetworkAddress network in allowedNetworks) acl.Add(new NetworkAccessControl(network)); } if (acl.Count > 0) return acl; return null; } #endregion #region private private static Dictionary ReadZoneTransferTsigKeyNamesFrom(BinaryReader bR) { int count = bR.ReadByte(); Dictionary zoneTransferTsigKeyNames = new Dictionary(count); for (int i = 0; i < count; i++) zoneTransferTsigKeyNames.Add(bR.ReadShortString(), null); return zoneTransferTsigKeyNames; } private void WriteZoneTransferTsigKeyNamesTo(BinaryWriter bW) { if (_zoneTransferTsigKeyNames is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(_zoneTransferTsigKeyNames.Count)); foreach (KeyValuePair tsigKeyName in _zoneTransferTsigKeyNames) bW.WriteShortString(tsigKeyName.Key); } } private static DnsResourceRecord[] ReadZoneHistoryFrom(BinaryReader bR) { int count = bR.ReadInt32(); DnsResourceRecord[] zoneHistory = new DnsResourceRecord[count]; for (int i = 0; i < count; i++) { zoneHistory[i] = new DnsResourceRecord(bR.BaseStream); if (bR.ReadBoolean()) zoneHistory[i].Tag = new HistoryRecordInfo(bR); } return zoneHistory; } private void WriteZoneHistoryTo(BinaryWriter bW) { if (_zoneHistory is null) { bW.Write(0); } else { bW.Write(_zoneHistory.Count); foreach (DnsResourceRecord record in _zoneHistory) { record.WriteTo(bW.BaseStream); if (record.Tag is HistoryRecordInfo rrInfo) { bW.Write(true); rrInfo.WriteTo(bW); } else { bW.Write(false); } } } } private static Dictionary>> ReadUpdateSecurityPoliciesFrom(BinaryReader bR) { int count = bR.ReadInt32(); Dictionary>> updateSecurityPolicies = new Dictionary>>(count); for (int i = 0; i < count; i++) { string tsigKeyName = bR.ReadShortString().ToLowerInvariant(); if (!updateSecurityPolicies.TryGetValue(tsigKeyName, out IReadOnlyDictionary> policyMap)) { policyMap = new Dictionary>(); updateSecurityPolicies.Add(tsigKeyName, policyMap); } int policyCount = bR.ReadByte(); for (int j = 0; j < policyCount; j++) { string domain = bR.ReadShortString().ToLowerInvariant(); if (!policyMap.TryGetValue(domain, out IReadOnlyList types)) { types = new List(); (policyMap as Dictionary>).Add(domain, types); } int typeCount = bR.ReadByte(); for (int k = 0; k < typeCount; k++) (types as List).Add((DnsResourceRecordType)bR.ReadUInt16()); } } return updateSecurityPolicies; } private void WriteUpdateSecurityPoliciesTo(BinaryWriter bW) { if (_updateSecurityPolicies is null) { bW.Write(0); } else { bW.Write(_updateSecurityPolicies.Count); foreach (KeyValuePair>> updateSecurityPolicy in _updateSecurityPolicies) { bW.WriteShortString(updateSecurityPolicy.Key); bW.Write(Convert.ToByte(updateSecurityPolicy.Value.Count)); foreach (KeyValuePair> policyMap in updateSecurityPolicy.Value) { bW.WriteShortString(policyMap.Key); bW.Write(Convert.ToByte(policyMap.Value.Count)); foreach (DnsResourceRecordType type in policyMap.Value) bW.Write((ushort)type); } } } } private static DnssecPrivateKey[] ReadDnssecPrivateKeysFrom(BinaryReader bR) { int count = bR.ReadByte(); if (count < 1) return null; DnssecPrivateKey[] dnssecPrivateKeys = new DnssecPrivateKey[count]; for (int i = 0; i < count; i++) dnssecPrivateKeys[i] = DnssecPrivateKey.ReadFrom(bR); return dnssecPrivateKeys; } private void WriteDnssecPrivateKeysTo(BinaryWriter bW) { if (_dnssecPrivateKeys is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(_dnssecPrivateKeys.Count)); foreach (DnssecPrivateKey dnssecPrivateKey in _dnssecPrivateKeys) dnssecPrivateKey.WriteTo(bW); } } #endregion #region public public void TriggerRefresh() { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: (_apexZone as SecondaryZone).TriggerRefresh(); break; case AuthZoneType.Stub: (_apexZone as StubZone).TriggerRefresh(); break; default: throw new InvalidOperationException(); } } public void TriggerResync() { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: (_apexZone as SecondaryZone).TriggerResync(); break; case AuthZoneType.Stub: (_apexZone as StubZone).TriggerResync(); break; default: throw new InvalidOperationException(); } } public void WriteTo(BinaryWriter bW) { if (_apexZone is null) throw new InvalidOperationException(); bW.Write((byte)13); //version bW.WriteShortString(_name); bW.Write((byte)_type); bW.Write(_lastModified); bW.Write(_disabled); switch (_type) { case AuthZoneType.Primary: bW.Write(_catalogZoneName ?? ""); bW.Write(_overrideCatalogQueryAccess); bW.Write(_overrideCatalogZoneTransfer); bW.Write(_overrideCatalogNotify); bW.Write((byte)_queryAccess); WriteNetworkACLTo(_queryAccessNetworkACL, bW); bW.Write((byte)_zoneTransfer); WriteNetworkACLTo(_zoneTransferNetworkACL, bW); WriteZoneTransferTsigKeyNamesTo(bW); WriteZoneHistoryTo(bW); bW.Write((byte)_notify); WriteIPAddressesTo(_notifyNameServers, bW); bW.Write((byte)_update); WriteNetworkACLTo(_updateNetworkACL, bW); WriteUpdateSecurityPoliciesTo(bW); WriteDnssecPrivateKeysTo(bW); break; case AuthZoneType.Secondary: bW.Write(_catalogZoneName ?? ""); bW.Write(_overrideCatalogQueryAccess); bW.Write(_overrideCatalogZoneTransfer); bW.Write(_overrideCatalogPrimaryNameServers); bW.Write((byte)_queryAccess); WriteNetworkACLTo(_queryAccessNetworkACL, bW); bW.Write((byte)_zoneTransfer); WriteNetworkACLTo(_zoneTransferNetworkACL, bW); WriteZoneTransferTsigKeyNamesTo(bW); WriteZoneHistoryTo(bW); bW.Write((byte)_notify); WriteIPAddressesTo(_notifyNameServers, bW); bW.Write((byte)_update); WriteNetworkACLTo(_updateNetworkACL, bW); WriteNameServerAddressesTo(_primaryNameServerAddresses, bW); bW.Write((byte)_primaryZoneTransferProtocol); bW.Write(_primaryZoneTransferTsigKeyName ?? ""); bW.Write(_expiry); bW.Write(_validateZone); bW.Write(_validationFailed); break; case AuthZoneType.Stub: bW.Write(_catalogZoneName ?? ""); bW.Write(_overrideCatalogQueryAccess); bW.Write((byte)_queryAccess); WriteNetworkACLTo(_queryAccessNetworkACL, bW); WriteNameServerAddressesTo(_primaryNameServerAddresses, bW); bW.Write(_expiry); break; case AuthZoneType.Forwarder: bW.Write(_catalogZoneName ?? ""); bW.Write(_overrideCatalogQueryAccess); bW.Write(_overrideCatalogZoneTransfer); bW.Write(_overrideCatalogNotify); bW.Write((byte)_queryAccess); WriteNetworkACLTo(_queryAccessNetworkACL, bW); bW.Write((byte)_zoneTransfer); WriteNetworkACLTo(_zoneTransferNetworkACL, bW); WriteZoneTransferTsigKeyNamesTo(bW); WriteZoneHistoryTo(bW); bW.Write((byte)_notify); WriteIPAddressesTo(_notifyNameServers, bW); bW.Write((byte)_update); WriteNetworkACLTo(_updateNetworkACL, bW); WriteUpdateSecurityPoliciesTo(bW); break; case AuthZoneType.SecondaryForwarder: bW.Write(_catalogZoneName ?? ""); bW.Write(_overrideCatalogQueryAccess); bW.Write((byte)_queryAccess); WriteNetworkACLTo(_queryAccessNetworkACL, bW); bW.Write((byte)_update); WriteNetworkACLTo(_updateNetworkACL, bW); WriteNameServerAddressesTo(_primaryNameServerAddresses, bW); bW.Write((byte)_primaryZoneTransferProtocol); bW.Write(_primaryZoneTransferTsigKeyName ?? ""); bW.Write(_expiry); break; case AuthZoneType.Catalog: bW.Write((byte)_queryAccess); WriteNetworkACLTo(_queryAccessNetworkACL, bW); bW.Write((byte)_zoneTransfer); WriteNetworkACLTo(_zoneTransferNetworkACL, bW); WriteZoneTransferTsigKeyNamesTo(bW); WriteZoneHistoryTo(bW); bW.Write((byte)_notify); WriteIPAddressesTo(_notifyNameServers, bW); WriteIPAddressesTo(_notifySecondaryCatalogNameServers, bW); break; case AuthZoneType.SecondaryCatalog: bW.Write((byte)_queryAccess); WriteNetworkACLTo(_queryAccessNetworkACL, bW); bW.Write((byte)_zoneTransfer); WriteNetworkACLTo(_zoneTransferNetworkACL, bW); WriteZoneTransferTsigKeyNamesTo(bW); WriteNameServerAddressesTo(_primaryNameServerAddresses, bW); bW.Write((byte)_primaryZoneTransferProtocol); bW.Write(_primaryZoneTransferTsigKeyName ?? ""); bW.Write(_expiry); break; } } public int CompareTo(AuthZoneInfo other) { return _name.CompareTo(other._name); } public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) return true; if (obj is not AuthZoneInfo other) return false; return _name.Equals(other._name, StringComparison.OrdinalIgnoreCase); } public override int GetHashCode() { return HashCode.Combine(_name); } public override string ToString() { return _name.Length == 0 ? "" : _name; ; } #endregion #region properties internal ApexZone ApexZone { get { return _apexZone; } } public string Name { get { return _name; } } public string DisplayName { get { return _name.Length == 0 ? "" : _name; } } public AuthZoneType Type { get { return _type; } } public string TypeName { get { return GetZoneTypeName(_type); } } public DateTime LastModified { get { if (_apexZone is null) return _lastModified; return _apexZone.LastModified; } } public bool Disabled { get { if (_apexZone is null) return _disabled; return _apexZone.Disabled; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.Disabled = value; } } public string CatalogZoneName { get { if (_apexZone is null) return _catalogZoneName; return _apexZone.CatalogZoneName; } } public bool OverrideCatalogQueryAccess { get { if (_apexZone is null) return _overrideCatalogQueryAccess; return _apexZone.OverrideCatalogQueryAccess; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.OverrideCatalogQueryAccess = value; } } public bool OverrideCatalogZoneTransfer { get { if (_apexZone is null) return _overrideCatalogZoneTransfer; return _apexZone.OverrideCatalogZoneTransfer; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.OverrideCatalogZoneTransfer = value; } } public bool OverrideCatalogNotify { get { if (_apexZone is null) return _overrideCatalogNotify; return _apexZone.OverrideCatalogNotify; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.OverrideCatalogNotify = value; } } public bool OverrideCatalogPrimaryNameServers { get { if (_apexZone is null) return _overrideCatalogPrimaryNameServers; switch (_type) { case AuthZoneType.Secondary: return (_apexZone as SecondaryZone).OverrideCatalogPrimaryNameServers; case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: return false; default: throw new InvalidOperationException(); } } set { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: (_apexZone as SecondaryZone).OverrideCatalogPrimaryNameServers = value; break; default: throw new InvalidOperationException(); } } } public AuthZoneQueryAccess QueryAccess { get { if (_apexZone is null) return _queryAccess; return _apexZone.QueryAccess; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.QueryAccess = value; } } public IReadOnlyCollection QueryAccessNetworkACL { get { if (_apexZone is null) return _queryAccessNetworkACL; return _apexZone.QueryAccessNetworkACL; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.QueryAccessNetworkACL = value; } } public AuthZoneTransfer ZoneTransfer { get { if (_apexZone is null) return _zoneTransfer; return _apexZone.ZoneTransfer; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.ZoneTransfer = value; } } public IReadOnlyCollection ZoneTransferNetworkACL { get { if (_apexZone is null) return _zoneTransferNetworkACL; return _apexZone.ZoneTransferNetworkACL; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.ZoneTransferNetworkACL = value; } } public IReadOnlyDictionary ZoneTransferTsigKeyNames { get { if (_apexZone is null) return _zoneTransferTsigKeyNames; return _apexZone.ZoneTransferTsigKeyNames; } set { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Primary: case AuthZoneType.Secondary: case AuthZoneType.Forwarder: case AuthZoneType.Catalog: _apexZone.ZoneTransferTsigKeyNames = value; break; default: throw new InvalidOperationException(); } } } public IReadOnlyList ZoneHistory { get { if (_apexZone is null) return _zoneHistory; return _apexZone.GetZoneHistory(); } } public AuthZoneNotify Notify { get { if (_apexZone is null) return _notify; return _apexZone.Notify; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.Notify = value; } } public IReadOnlyCollection NotifyNameServers { get { if (_apexZone is null) return _notifyNameServers; return _apexZone.NotifyNameServers; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.NotifyNameServers = value; } } public IReadOnlyCollection NotifySecondaryCatalogNameServers { get { if (_apexZone is null) return _notifySecondaryCatalogNameServers; return _apexZone.NotifySecondaryCatalogNameServers; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.NotifySecondaryCatalogNameServers = value; } } public AuthZoneUpdate Update { get { if (_apexZone is null) return _update; return _apexZone.Update; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.Update = value; } } public IReadOnlyCollection UpdateNetworkACL { get { if (_apexZone is null) return _updateNetworkACL; return _apexZone.UpdateNetworkACL; } set { if (_apexZone is null) throw new InvalidOperationException(); _apexZone.UpdateNetworkACL = value; } } public IReadOnlyDictionary>> UpdateSecurityPolicies { get { if (_apexZone is null) return _updateSecurityPolicies; return _apexZone.UpdateSecurityPolicies; } set { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Primary: case AuthZoneType.Forwarder: _apexZone.UpdateSecurityPolicies = value; break; default: throw new InvalidOperationException(); } } } public IReadOnlyCollection DnssecPrivateKeys { get { if (_apexZone is null) return _dnssecPrivateKeys; switch (_type) { case AuthZoneType.Primary: return (_apexZone as PrimaryZone).DnssecPrivateKeys; default: throw new InvalidOperationException(); } } } public IReadOnlyList PrimaryNameServerAddresses { get { if (_apexZone is null) return _primaryNameServerAddresses; switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: return (_apexZone as SecondaryZone).PrimaryNameServerAddresses; case AuthZoneType.Stub: return (_apexZone as StubZone).PrimaryNameServerAddresses; default: throw new InvalidOperationException(); } } set { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: (_apexZone as SecondaryZone).PrimaryNameServerAddresses = value; break; case AuthZoneType.Stub: (_apexZone as StubZone).PrimaryNameServerAddresses = value; break; default: throw new InvalidOperationException(); } } } public DnsTransportProtocol PrimaryZoneTransferProtocol { get { if (_apexZone is null) return _primaryZoneTransferProtocol; switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: return (_apexZone as SecondaryZone).PrimaryZoneTransferProtocol; default: throw new InvalidOperationException(); } } set { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: (_apexZone as SecondaryZone).PrimaryZoneTransferProtocol = value; break; default: throw new InvalidOperationException(); } } } public string PrimaryZoneTransferTsigKeyName { get { if (_apexZone is null) return _primaryZoneTransferTsigKeyName; switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: return (_apexZone as SecondaryZone).PrimaryZoneTransferTsigKeyName; default: throw new InvalidOperationException(); } } set { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: (_apexZone as SecondaryZone).PrimaryZoneTransferTsigKeyName = value; break; default: throw new InvalidOperationException(); } } } public DateTime Expiry { get { if (_apexZone is null) return _expiry; switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: return (_apexZone as SecondaryZone).Expiry; case AuthZoneType.Stub: return (_apexZone as StubZone).Expiry; default: throw new InvalidOperationException(); } } } public bool ValidateZone { get { if (_apexZone is null) return _validateZone; switch (_type) { case AuthZoneType.Secondary: return (_apexZone as SecondaryZone).ValidateZone; default: throw new InvalidOperationException(); } } set { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: (_apexZone as SecondaryZone).ValidateZone = value; break; default: throw new InvalidOperationException(); } } } public bool ValidationFailed { get { if (_apexZone is null) return _validationFailed; switch (_type) { case AuthZoneType.Secondary: return (_apexZone as SecondaryZone).ValidationFailed; default: throw new InvalidOperationException(); } } } public uint DnsKeyTtl { get { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Primary: return (_apexZone as PrimaryZone).GetDnsKeyTtl(); default: throw new InvalidOperationException(); } } } public bool Internal { get { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Primary: return (_apexZone as PrimaryZone).Internal; default: return false; } } } public bool IsExpired { get { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: return (_apexZone as SecondaryZone).IsExpired; case AuthZoneType.Stub: return (_apexZone as StubZone).IsExpired; default: return false; } } } public string[] NotifyFailed { get { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Primary: case AuthZoneType.Secondary: case AuthZoneType.Forwarder: case AuthZoneType.Catalog: return _apexZone.NotifyFailed; default: throw new InvalidOperationException(); } } } public bool SyncFailed { get { if (_apexZone is null) throw new InvalidOperationException(); switch (_type) { case AuthZoneType.Secondary: case AuthZoneType.SecondaryForwarder: case AuthZoneType.SecondaryCatalog: case AuthZoneType.Stub: return _apexZone.SyncFailed; default: throw new InvalidOperationException(); } } } #endregion } }