/*
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.ResourceRecords;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using TechnitiumLibrary;
using TechnitiumLibrary.Net;
using TechnitiumLibrary.Net.Dns;
using TechnitiumLibrary.Net.Dns.ResourceRecords;
namespace DnsServerCore.Dns.Zones
{
class SecondaryCatalogZone : SecondaryForwarderZone
{
#region events
public event EventHandler ZoneAdded;
public event EventHandler ZoneRemoved;
#endregion
#region variables
readonly static IReadOnlyCollection _allowACL =
[
new NetworkAccessControl(IPAddress.Any, 0),
new NetworkAccessControl(IPAddress.IPv6Any, 0)
];
readonly static IReadOnlyCollection _queryAccessAllowOnlyPrivateNetworksACL =
[
new NetworkAccessControl(IPAddress.Parse("127.0.0.0"), 8),
new NetworkAccessControl(IPAddress.Parse("10.0.0.0"), 8),
new NetworkAccessControl(IPAddress.Parse("100.64.0.0"), 10),
new NetworkAccessControl(IPAddress.Parse("169.254.0.0"), 16),
new NetworkAccessControl(IPAddress.Parse("172.16.0.0"), 12),
new NetworkAccessControl(IPAddress.Parse("192.168.0.0"), 16),
new NetworkAccessControl(IPAddress.Parse("2000::"), 3, true),
new NetworkAccessControl(IPAddress.IPv6Any, 0)
];
readonly static IReadOnlyCollection _allowOnlyZoneNameServersACL =
[
new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32)
];
readonly static IReadOnlyCollection _denyACL =
[
new NetworkAccessControl(IPAddress.Parse("127.0.0.0"), 8),
new NetworkAccessControl(IPAddress.Parse("::1"), 128)
];
readonly static NetworkAccessControl _allowZoneNameServersAndUseSpecifiedNetworkACL = new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32);
Dictionary _membersIndex = new Dictionary();
#endregion
#region constructor
public SecondaryCatalogZone(DnsServer dnsServer, AuthZoneInfo zoneInfo)
: base(dnsServer, zoneInfo)
{ }
public SecondaryCatalogZone(DnsServer dnsServer, string name, IReadOnlyList primaryNameServerAddresses, DnsTransportProtocol primaryZoneTransferProtocol = DnsTransportProtocol.Tcp, string primaryZoneTransferTsigKeyName = null)
: base(dnsServer, name, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName)
{ }
#endregion
#region protected
protected override void InitZone()
{
//init secondary catalog zone with dummy SOA and NS records
DnsSOARecordData soa = new DnsSOARecordData("invalid", "invalid", 0, 300, 60, 604800, 900);
DnsResourceRecord soaRecord = new DnsResourceRecord(_name, DnsResourceRecordType.SOA, DnsClass.IN, 0, soa);
soaRecord.GetAuthGenericRecordInfo().LastModified = DateTime.UtcNow;
_entries[DnsResourceRecordType.SOA] = [soaRecord];
_entries[DnsResourceRecordType.NS] = [new DnsResourceRecord(_name, DnsResourceRecordType.NS, DnsClass.IN, 0, new DnsNSRecordData("invalid"))];
}
#endregion
#region internal
internal void BuildMembersIndex()
{
Dictionary membersIndex = new Dictionary();
foreach (KeyValuePair memberEntry in EnumerateCatalogMemberZones(_dnsServer))
membersIndex.TryAdd(memberEntry.Key.ToLowerInvariant(), memberEntry.Value);
_membersIndex = membersIndex;
}
#endregion
#region secondary catalog
public IReadOnlyCollection GetAllMemberZoneNames()
{
return _membersIndex.Keys;
}
protected override async Task FinalizeZoneTransferAsync()
{
//secondary catalog does not maintain zone history; no need to call base method
string version = GetVersion();
if ((version is null) || !version.Equals("2", StringComparison.OrdinalIgnoreCase))
{
_dnsServer.LogManager?.Write("Failed to provision Secondary Catalog zone '" + ToString() + "': catalog version not supported.");
return;
}
Dictionary updatedMembersIndex = new Dictionary();
foreach (KeyValuePair memberEntry in EnumerateCatalogMemberZones(_dnsServer))
updatedMembersIndex.TryAdd(memberEntry.Key, memberEntry.Value);
Dictionary membersToRemove = new Dictionary();
Dictionary membersToAdd = new Dictionary();
foreach (KeyValuePair memberEntry in _membersIndex)
{
if (!updatedMembersIndex.TryGetValue(memberEntry.Key, out string updatedMembersZoneDomain))
{
//member was removed from catalog zone; remove local zone
membersToRemove.Add(memberEntry.Key, null);
}
else if (!memberEntry.Value.Equals(updatedMembersZoneDomain, StringComparison.OrdinalIgnoreCase))
{
//member exists but label does not match; reprovision zone
membersToRemove.Add(memberEntry.Key, null);
membersToAdd.Add(memberEntry.Key, updatedMembersZoneDomain);
}
}
foreach (KeyValuePair updatedMemberEntry in updatedMembersIndex)
{
if (_membersIndex.TryGetValue(updatedMemberEntry.Key, out _))
{
ApexZone apexZone = _dnsServer.AuthZoneManager.GetApexZone(updatedMemberEntry.Key);
if (apexZone is not null)
continue; //zone already exists; do nothing
}
//member was added to catalog zone; provision zone
membersToAdd.TryAdd(updatedMemberEntry.Key, updatedMemberEntry.Value);
}
//set global custom properties
UpdateGlobalAllowQueryProperty();
UpdateGlobalAllowTransferAndTsigKeyNamesProperties();
//add and remove member zones
if ((membersToRemove.Count > 0) || (membersToAdd.Count > 0))
await AddAndRemoveMemberZonesAsync(membersToRemove, membersToAdd);
//set member zone custom properties
if (updatedMembersIndex.Count > 0)
UpdateMemberZoneCustomProperties(updatedMembersIndex);
_membersIndex = updatedMembersIndex;
_dnsServer.AuthZoneManager.SaveZoneFile(_name);
}
protected override async Task FinalizeIncrementalZoneTransferAsync(IReadOnlyList historyRecords)
{
//secondary catalog does not maintain zone history; no need to call base method
string version = GetVersion();
if ((version is null) || !version.Equals("2", StringComparison.OrdinalIgnoreCase))
{
_dnsServer.LogManager?.Write("Failed to provision Secondary Catalog zone '" + ToString() + "': catalog version not supported.");
return;
}
bool isAddHistoryRecord = false;
bool updateGlobalAllowQueryProperty = false;
bool updateGlobalAllowTransferAndTsigKeyNamesProperties = false;
Dictionary membersToRemove = new Dictionary();
Dictionary membersToAdd = new Dictionary();
Dictionary membersToUpdate = new Dictionary();
//inspect records in history
for (int i = 1; i < historyRecords.Count; i++)
{
DnsResourceRecord historyRecord = historyRecords[i];
if (historyRecord.Type == DnsResourceRecordType.SOA)
{
isAddHistoryRecord = true; //removed records completed
continue;
}
if (historyRecord.Name.Length == _name.Length)
continue; //skip apex records
string subdomain = historyRecord.Name.Substring(0, historyRecord.Name.Length - _name.Length - 1).ToLowerInvariant();
string[] labels = subdomain.Split('.');
Array.Reverse(labels);
switch (labels[0])
{
case "ext":
if (labels.Length > 1)
{
switch (labels[1])
{
case "allow-query":
updateGlobalAllowQueryProperty = true;
break;
case "allow-transfer":
case "transfer-tsig-key-names":
updateGlobalAllowTransferAndTsigKeyNamesProperties = true;
break;
}
}
break;
case "zones":
if (labels.Length == 2)
{
if (historyRecord.Type == DnsResourceRecordType.PTR)
{
string memberZoneName = (historyRecord.RDATA as DnsPTRRecordData).Domain.ToLowerInvariant();
if (isAddHistoryRecord)
{
string memberZoneDomain = subdomain + "." + _name;
membersToAdd.TryAdd(memberZoneName, memberZoneDomain);
membersToUpdate.TryAdd(memberZoneName, memberZoneDomain);
}
else
{
membersToRemove.TryAdd(memberZoneName, null);
}
}
}
else if (labels.Length > 2)
{
switch (labels[2])
{
case "ext":
case "coo":
string memberZoneDomain = labels[1] + "." + labels[0] + "." + _name;
DnsResourceRecord prevHistoryRecord = historyRecords[i - 1];
if (prevHistoryRecord.Name.EndsWith(memberZoneDomain))
break; //skip since its same member zone's custom property
IReadOnlyList ptrRecords = _dnsServer.AuthZoneManager.GetRecords(_name, memberZoneDomain, DnsResourceRecordType.PTR);
if (ptrRecords.Count > 0)
membersToUpdate.TryAdd((ptrRecords[0].RDATA as DnsPTRRecordData).Domain.ToLowerInvariant(), memberZoneDomain);
break;
}
}
break;
}
}
//apply changes
if (updateGlobalAllowQueryProperty)
UpdateGlobalAllowQueryProperty();
if (updateGlobalAllowTransferAndTsigKeyNamesProperties)
UpdateGlobalAllowTransferAndTsigKeyNamesProperties();
if ((membersToRemove.Count > 0) || (membersToAdd.Count > 0))
await AddAndRemoveMemberZonesAsync(membersToRemove, membersToAdd);
if (membersToUpdate.Count > 0)
UpdateMemberZoneCustomProperties(membersToUpdate);
if ((membersToRemove.Count > 0) || (membersToAdd.Count > 0))
{
//update members index
Dictionary updatedMembersIndex = new Dictionary(_membersIndex);
foreach (KeyValuePair removedMember in membersToRemove)
updatedMembersIndex.Remove(removedMember.Key);
foreach (KeyValuePair addedMember in membersToAdd)
updatedMembersIndex.TryAdd(addedMember.Key, addedMember.Value);
_membersIndex = updatedMembersIndex;
}
_dnsServer.AuthZoneManager.SaveZoneFile(_name);
}
private async Task AddAndRemoveMemberZonesAsync(Dictionary membersToRemove, Dictionary membersToAdd)
{
//remove zones
foreach (KeyValuePair removeMember in membersToRemove)
{
ApexZone apexZone = _dnsServer.AuthZoneManager.GetApexZone(removeMember.Key);
if ((apexZone is not null) && _name.Equals(apexZone.CatalogZoneName, StringComparison.OrdinalIgnoreCase))
DeleteMemberZone(apexZone);
}
//add zones
List> addZoneTasks = new List>();
foreach (KeyValuePair addMember in membersToAdd)
{
ApexZone apexZone = _dnsServer.AuthZoneManager.GetApexZone(addMember.Key);
if (apexZone is null)
{
//create zone
AuthZoneType zoneType = GetZoneTypeProperty(addMember.Value);
switch (zoneType)
{
case AuthZoneType.Primary:
{
//create secondary zone
IReadOnlyList primaryNameServerAddresses;
DnsTransportProtocol primaryZoneTransferProtocol;
string primaryZoneTransferTsigKeyName;
IReadOnlyList> primaries = GetPrimariesProperty(addMember.Value);
if (primaries.Count == 0)
primaries = GetPrimariesProperty(_name);
if (primaries.Count == 0)
{
primaryNameServerAddresses = PrimaryNameServerAddresses;
primaryZoneTransferProtocol = PrimaryZoneTransferProtocol;
primaryZoneTransferTsigKeyName = PrimaryZoneTransferTsigKeyName;
}
else
{
Tuple primary = primaries[0];
primaryNameServerAddresses = [new NameServerAddress(primary.Item1, DnsTransportProtocol.Tcp)];
primaryZoneTransferProtocol = DnsTransportProtocol.Tcp;
primaryZoneTransferTsigKeyName = primary.Item2;
}
addZoneTasks.Add(_dnsServer.AuthZoneManager.CreateSecondaryZoneAsync(addMember.Key, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName, false, true));
}
break;
case AuthZoneType.Stub:
{
//create stub zone
IReadOnlyList primaryNameServerAddresses = GetPrimaryAddressesProperty(addMember.Value);
addZoneTasks.Add(_dnsServer.AuthZoneManager.CreateStubZoneAsync(addMember.Key, primaryNameServerAddresses, true));
}
break;
case AuthZoneType.Forwarder:
{
//create secondary forwarder zone
addZoneTasks.Add(Task.FromResult(_dnsServer.AuthZoneManager.CreateSecondaryForwarderZone(addMember.Key, PrimaryNameServerAddresses, PrimaryZoneTransferProtocol, PrimaryZoneTransferTsigKeyName)));
}
break;
}
}
}
await Task.WhenAll(addZoneTasks);
//finalize add zone tasks
foreach (Task task in addZoneTasks)
{
try
{
AuthZoneInfo zoneInfo = await task;
//set as catalog zone member
zoneInfo.ApexZone.CatalogZoneName = _name;
//raise event
ZoneAdded?.Invoke(this, new SecondaryCatalogEventArgs(zoneInfo));
//write log
_dnsServer.LogManager?.Write(zoneInfo.TypeName + " zone '" + zoneInfo.DisplayName + "' was added via Secondary Catalog zone '" + ToString() + "' sucessfully.");
}
catch (Exception ex)
{
_dnsServer.LogManager?.Write(ex);
}
}
}
private void UpdateGlobalAllowQueryProperty()
{
//allow query global custom property
IReadOnlyCollection globalAllowQueryACL = GetAllowQueryProperty(_name);
if (globalAllowQueryACL.Count > 0)
{
_queryAccess = GetQueryAccessType(globalAllowQueryACL);
switch (_queryAccess)
{
case AuthZoneQueryAccess.UseSpecifiedNetworkACL:
QueryAccessNetworkACL = globalAllowQueryACL;
break;
case AuthZoneQueryAccess.AllowZoneNameServersAndUseSpecifiedNetworkACL:
QueryAccessNetworkACL = GetFilteredACL(globalAllowQueryACL);
break;
default:
QueryAccessNetworkACL = null;
break;
}
}
else
{
_queryAccess = AuthZoneQueryAccess.Allow;
QueryAccessNetworkACL = null;
}
}
private void UpdateGlobalAllowTransferAndTsigKeyNamesProperties()
{
//allow transfer global custom property
IReadOnlyCollection globalAllowTransferACL = GetAllowTransferProperty(_name);
if (globalAllowTransferACL.Count > 0)
{
_zoneTransfer = GetZoneTransferType(globalAllowTransferACL);
switch (_zoneTransfer)
{
case AuthZoneTransfer.UseSpecifiedNetworkACL:
ZoneTransferNetworkACL = globalAllowTransferACL;
break;
case AuthZoneTransfer.AllowZoneNameServersAndUseSpecifiedNetworkACL:
ZoneTransferNetworkACL = GetFilteredACL(globalAllowTransferACL);
break;
default:
ZoneTransferNetworkACL = null;
break;
}
//zone tranfer tsig key names global custom property
ZoneTransferTsigKeyNames = GetZoneTransferTsigKeyNamesProperty(_name);
}
else
{
_zoneTransfer = AuthZoneTransfer.Deny;
ZoneTransferNetworkACL = null;
ZoneTransferTsigKeyNames = null;
}
}
private void UpdateMemberZoneCustomProperties(Dictionary membersToUpdate)
{
foreach (KeyValuePair updatedMemberEntry in membersToUpdate)
{
ApexZone memberApexZone = _dnsServer.AuthZoneManager.GetApexZone(updatedMemberEntry.Key);
if ((memberApexZone is not null) && _name.Equals(memberApexZone.CatalogZoneName, StringComparison.OrdinalIgnoreCase))
{
//change of ownership property
{
string newCatalogZoneName = GetChangeOfOwnershipProperty(updatedMemberEntry.Value);
if (newCatalogZoneName is not null)
{
ApexZone catalogApexZone = _dnsServer.AuthZoneManager.GetApexZone(newCatalogZoneName);
if (catalogApexZone is SecondaryCatalogZone secondaryCatalogZone)
{
//found secondary catalog zone; transfer ownership to it
memberApexZone.CatalogZoneName = secondaryCatalogZone._name;
}
else
{
//no such secondary catalog zone exists; delete member zone
DeleteMemberZone(memberApexZone);
continue;
}
}
}
//allow query member zone custom property
{
IReadOnlyCollection allowQueryACL = GetAllowQueryProperty(updatedMemberEntry.Value);
if (allowQueryACL.Count > 0)
{
memberApexZone.QueryAccess = GetQueryAccessType(allowQueryACL);
switch (memberApexZone.QueryAccess)
{
case AuthZoneQueryAccess.UseSpecifiedNetworkACL:
memberApexZone.QueryAccessNetworkACL = allowQueryACL;
break;
case AuthZoneQueryAccess.AllowZoneNameServersAndUseSpecifiedNetworkACL:
memberApexZone.QueryAccessNetworkACL = GetFilteredACL(allowQueryACL);
break;
default:
memberApexZone.QueryAccessNetworkACL = null;
break;
}
memberApexZone.OverrideCatalogQueryAccess = true;
}
else
{
memberApexZone.OverrideCatalogQueryAccess = false;
memberApexZone.QueryAccess = AuthZoneQueryAccess.Allow;
memberApexZone.QueryAccessNetworkACL = null;
}
}
if (memberApexZone is StubZone stubZone)
{
//primary addresses property
stubZone.PrimaryNameServerAddresses = GetPrimaryAddressesProperty(updatedMemberEntry.Value);
}
else if (memberApexZone is SecondaryForwarderZone)
{
//do nothing
}
else if (memberApexZone is SecondaryZone secondaryZone)
{
//primaries property
IReadOnlyList> primaries = GetPrimariesProperty(updatedMemberEntry.Value);
if (primaries.Count == 0)
primaries = GetPrimariesProperty(_name);
if (primaries.Count > 0)
{
Tuple primary = primaries[0];
secondaryZone.PrimaryNameServerAddresses = [new NameServerAddress(primary.Item1, DnsTransportProtocol.Tcp)];
secondaryZone.PrimaryZoneTransferProtocol = DnsTransportProtocol.Tcp;
secondaryZone.PrimaryZoneTransferTsigKeyName = primary.Item2;
secondaryZone.OverrideCatalogPrimaryNameServers = true;
}
else
{
secondaryZone.OverrideCatalogPrimaryNameServers = false;
secondaryZone.PrimaryNameServerAddresses = null;
secondaryZone.PrimaryZoneTransferProtocol = DnsTransportProtocol.Tcp;
secondaryZone.PrimaryZoneTransferTsigKeyName = null;
}
//allow transfer member zone custom property
IReadOnlyCollection allowTransferACL = GetAllowTransferProperty(updatedMemberEntry.Value);
if (allowTransferACL.Count > 0)
{
memberApexZone.ZoneTransfer = GetZoneTransferType(allowTransferACL);
switch (memberApexZone.ZoneTransfer)
{
case AuthZoneTransfer.UseSpecifiedNetworkACL:
memberApexZone.ZoneTransferNetworkACL = allowTransferACL;
break;
case AuthZoneTransfer.AllowZoneNameServersAndUseSpecifiedNetworkACL:
memberApexZone.ZoneTransferNetworkACL = GetFilteredACL(allowTransferACL);
break;
default:
memberApexZone.ZoneTransferNetworkACL = null;
break;
}
//zone tranfer tsig key names member zone custom property
memberApexZone.ZoneTransferTsigKeyNames = GetZoneTransferTsigKeyNamesProperty(updatedMemberEntry.Value);
memberApexZone.OverrideCatalogZoneTransfer = true;
}
else
{
memberApexZone.OverrideCatalogZoneTransfer = false;
memberApexZone.ZoneTransfer = AuthZoneTransfer.Deny;
memberApexZone.ZoneTransferNetworkACL = null;
memberApexZone.ZoneTransferTsigKeyNames = null;
}
}
_dnsServer.AuthZoneManager.SaveZoneFile(memberApexZone.Name);
}
}
}
private void DeleteMemberZone(ApexZone apexZone)
{
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
if (_dnsServer.AuthZoneManager.DeleteZone(zoneInfo, true))
{
ZoneRemoved?.Invoke(this, new SecondaryCatalogEventArgs(zoneInfo));
_dnsServer.LogManager?.Write(apexZone.GetZoneTypeName() + " zone '" + apexZone.ToString() + "' was removed via Secondary Catalog zone '" + ToString() + "' sucessfully.");
}
}
private string GetVersion()
{
string domain = "version." + _name;
IReadOnlyList records = _dnsServer.AuthZoneManager.GetRecords(_name, domain, DnsResourceRecordType.TXT);
if (records.Count > 0)
return (records[0].RDATA as DnsTXTRecordData).GetText();
return null;
}
private string GetChangeOfOwnershipProperty(string memberZoneDomain)
{
string domain = "coo." + memberZoneDomain;
IReadOnlyList records = _dnsServer.AuthZoneManager.GetRecords(_name, domain, DnsResourceRecordType.PTR);
if (records.Count > 0)
return (records[0].RDATA as DnsPTRRecordData).Domain;
return null;
}
private AuthZoneType GetZoneTypeProperty(string memberZoneDomain)
{
string domain = "zone-type.ext." + memberZoneDomain;
IReadOnlyList records = _dnsServer.AuthZoneManager.GetRecords(_name, domain, DnsResourceRecordType.TXT);
if (records.Count > 0)
return Enum.Parse((records[0].RDATA as DnsTXTRecordData).GetText(), true);
return AuthZoneType.Primary;
}
private List> GetPrimariesProperty(string memberZoneDomain)
{
string domain = "primaries.ext." + memberZoneDomain;
List> primaries = new List>(2);
AuthZone authZone = _dnsServer.AuthZoneManager.GetAuthZone(_name, domain);
if (authZone is not null)
{
foreach (DnsResourceRecord record in authZone.GetRecords(DnsResourceRecordType.A))
primaries.Add(new Tuple((record.RDATA as DnsARecordData).Address, null));
foreach (DnsResourceRecord record in authZone.GetRecords(DnsResourceRecordType.AAAA))
primaries.Add(new Tuple((record.RDATA as DnsAAAARecordData).Address, null));
}
List subdomains = new List();
_dnsServer.AuthZoneManager.ListSubDomains(domain, subdomains);
foreach (string subdomain in subdomains)
{
AuthZone subZone = _dnsServer.AuthZoneManager.GetAuthZone(_name, subdomain + "." + domain);
if (subZone is null)
continue;
string tsigKeyName = null;
IReadOnlyList szTXTRecords = subZone.GetRecords(DnsResourceRecordType.TXT);
if (szTXTRecords.Count > 0)
tsigKeyName = (szTXTRecords[0].RDATA as DnsTXTRecordData).GetText();
foreach (DnsResourceRecord record in subZone.GetRecords(DnsResourceRecordType.A))
primaries.Add(new Tuple((record.RDATA as DnsARecordData).Address, tsigKeyName));
foreach (DnsResourceRecord record in subZone.GetRecords(DnsResourceRecordType.AAAA))
primaries.Add(new Tuple((record.RDATA as DnsAAAARecordData).Address, tsigKeyName));
}
return primaries;
}
private IReadOnlyList GetPrimaryAddressesProperty(string memberZoneDomain)
{
string domain = "primary-addresses.ext." + memberZoneDomain;
IReadOnlyList records = _dnsServer.AuthZoneManager.GetRecords(_name, domain, DnsResourceRecordType.TXT);
if (records.Count > 0)
return (records[0].RDATA as DnsTXTRecordData).CharacterStrings.Convert(NameServerAddress.Parse);
return [];
}
private IReadOnlyCollection GetAllowQueryProperty(string memberZoneDomain)
{
string domain = "allow-query.ext." + memberZoneDomain;
IReadOnlyList records = _dnsServer.AuthZoneManager.GetRecords(_name, domain, DnsResourceRecordType.APL);
if (records.Count > 0)
return NetworkAccessControl.ConvertFromAPLRecordData(records[0].RDATA as DnsAPLRecordData);
return [];
}
private IReadOnlyCollection GetAllowTransferProperty(string memberZoneDomain)
{
string domain = "allow-transfer.ext." + memberZoneDomain;
IReadOnlyList records = _dnsServer.AuthZoneManager.GetRecords(_name, domain, DnsResourceRecordType.APL);
if (records.Count > 0)
return NetworkAccessControl.ConvertFromAPLRecordData(records[0].RDATA as DnsAPLRecordData);
return [];
}
private Dictionary GetZoneTransferTsigKeyNamesProperty(string memberZoneDomain)
{
string domain = "transfer-tsig-key-names.ext." + memberZoneDomain;
IReadOnlyList records = _dnsServer.AuthZoneManager.GetRecords(_name, domain, DnsResourceRecordType.PTR);
Dictionary keyNames = new Dictionary(records.Count);
foreach (DnsResourceRecord record in records)
keyNames.TryAdd((record.RDATA as DnsPTRRecordData).Domain.ToLowerInvariant(), null);
return keyNames;
}
private static AuthZoneQueryAccess GetQueryAccessType(IReadOnlyCollection acl)
{
if (acl.HasSameItems(_allowACL))
return AuthZoneQueryAccess.Allow;
if (acl.HasSameItems(_queryAccessAllowOnlyPrivateNetworksACL))
return AuthZoneQueryAccess.AllowOnlyPrivateNetworks;
if (acl.HasSameItems(_allowOnlyZoneNameServersACL))
return AuthZoneQueryAccess.AllowOnlyZoneNameServers;
if ((acl.Count > 1) && acl.Contains(_allowZoneNameServersAndUseSpecifiedNetworkACL))
return AuthZoneQueryAccess.AllowZoneNameServersAndUseSpecifiedNetworkACL;
if (acl.HasSameItems(_denyACL))
return AuthZoneQueryAccess.Deny;
return AuthZoneQueryAccess.UseSpecifiedNetworkACL;
}
private static AuthZoneTransfer GetZoneTransferType(IReadOnlyCollection acl)
{
if (acl.HasSameItems(_allowACL))
return AuthZoneTransfer.Allow;
if (acl.HasSameItems(_allowOnlyZoneNameServersACL))
return AuthZoneTransfer.AllowOnlyZoneNameServers;
if ((acl.Count > 1) && acl.Contains(_allowZoneNameServersAndUseSpecifiedNetworkACL))
return AuthZoneTransfer.AllowZoneNameServersAndUseSpecifiedNetworkACL;
if (acl.HasSameItems(_denyACL))
return AuthZoneTransfer.Deny;
return AuthZoneTransfer.UseSpecifiedNetworkACL;
}
private static List GetFilteredACL(IReadOnlyCollection acl)
{
List filteredACL = new List(acl.Count);
foreach (NetworkAccessControl ac in acl)
{
if (ac.Equals(_allowZoneNameServersAndUseSpecifiedNetworkACL))
continue;
filteredACL.Add(ac);
}
return filteredACL;
}
#endregion
#region public
public override string GetZoneTypeName()
{
return "Secondary Catalog";
}
public override IReadOnlyList QueryRecords(DnsResourceRecordType type, bool dnssecOk)
{
return []; //secondary catalog zone is not queriable
}
#endregion
#region properties
public override string CatalogZoneName
{
get { return base.CatalogZoneName; }
set { throw new InvalidOperationException(); }
}
public override bool OverrideCatalogQueryAccess
{
get { throw new InvalidOperationException(); }
set { throw new InvalidOperationException(); }
}
public override AuthZoneQueryAccess QueryAccess
{
get { return _queryAccess; }
set { throw new InvalidOperationException(); }
}
public override AuthZoneTransfer ZoneTransfer
{
get { return _zoneTransfer; }
set { throw new InvalidOperationException(); }
}
public override AuthZoneUpdate Update
{
get { return base.Update; }
set { throw new InvalidOperationException(); }
}
#endregion
}
public class SecondaryCatalogEventArgs : EventArgs
{
#region variables
readonly AuthZoneInfo _zoneInfo;
#endregion
#region constructor
public SecondaryCatalogEventArgs(AuthZoneInfo zoneInfo)
{
_zoneInfo = zoneInfo;
}
#endregion
#region properties
public AuthZoneInfo ZoneInfo
{ get { return _zoneInfo; } }
#endregion
}
}