123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512 |
- /*
- 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 <http://www.gnu.org/licenses/>.
- */
- using DnsServerCore.Dns.ResourceRecords;
- using System;
- using System.Collections.Generic;
- using System.Net;
- using System.Threading;
- using System.Threading.Tasks;
- using TechnitiumLibrary.Net;
- using TechnitiumLibrary.Net.Dns;
- using TechnitiumLibrary.Net.Dns.ResourceRecords;
- namespace DnsServerCore.Dns.Zones
- {
- public enum AuthZoneQueryAccess : byte
- {
- Deny = 0,
- Allow = 1,
- AllowOnlyPrivateNetworks = 2,
- AllowOnlyZoneNameServers = 3,
- UseSpecifiedNetworkACL = 4,
- AllowZoneNameServersAndUseSpecifiedNetworkACL = 5
- }
- public enum AuthZoneTransfer : byte
- {
- Deny = 0,
- Allow = 1,
- AllowOnlyZoneNameServers = 2,
- UseSpecifiedNetworkACL = 3,
- AllowZoneNameServersAndUseSpecifiedNetworkACL = 4
- }
- public enum AuthZoneNotify : byte
- {
- None = 0,
- ZoneNameServers = 1,
- SpecifiedNameServers = 2,
- BothZoneAndSpecifiedNameServers = 3,
- SeparateNameServersForCatalogAndMemberZones = 4
- }
- public enum AuthZoneUpdate : byte
- {
- Deny = 0,
- Allow = 1,
- AllowOnlyZoneNameServers = 2,
- UseSpecifiedNetworkACL = 3,
- AllowZoneNameServersAndUseSpecifiedNetworkACL = 4
- }
- abstract class ApexZone : AuthZone, IDisposable
- {
- #region variables
- protected readonly DnsServer _dnsServer;
- protected DateTime _lastModified;
- string _catalogZoneName;
- bool _overrideCatalogQueryAccess;
- bool _overrideCatalogZoneTransfer;
- bool _overrideCatalogNotify;
- protected AuthZoneQueryAccess _queryAccess;
- IReadOnlyCollection<NetworkAccessControl> _queryAccessNetworkACL;
- protected AuthZoneTransfer _zoneTransfer;
- IReadOnlyCollection<NetworkAccessControl> _zoneTransferNetworkACL;
- IReadOnlyDictionary<string, object> _zoneTransferTsigKeyNames;
- readonly List<DnsResourceRecord> _zoneHistory; //for IXFR support
- AuthZoneNotify _notify;
- IReadOnlyCollection<IPAddress> _notifyNameServers;
- IReadOnlyCollection<IPAddress> _notifySecondaryCatalogNameServers;
- AuthZoneUpdate _update;
- IReadOnlyCollection<NetworkAccessControl> _updateNetworkACL;
- IReadOnlyDictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> _updateSecurityPolicies;
- protected AuthZoneDnssecStatus _dnssecStatus;
- Timer _notifyTimer;
- bool _notifyTimerTriggered;
- const int NOTIFY_TIMER_INTERVAL = 10000;
- List<string> _notifyList;
- List<string> _notifyFailed;
- const int NOTIFY_TIMEOUT = 10000;
- const int NOTIFY_RETRIES = 5;
- protected bool _syncFailed;
- Timer _recordExpiryTimer;
- readonly object _recordExpiryTimerLock = new object();
- DateTime _recordExpiryTimerStartedOn;
- uint _recordExpiryTimerTtl;
- bool _recordExpiryTimerRunning;
- CatalogZone _catalogZone;
- SecondaryCatalogZone _secondaryCatalogZone;
- #endregion
- #region constructor
- protected ApexZone(DnsServer dnsServer, AuthZoneInfo zoneInfo)
- : base(zoneInfo)
- {
- _dnsServer = dnsServer;
- _catalogZoneName = zoneInfo.CatalogZoneName;
- _overrideCatalogQueryAccess = zoneInfo.OverrideCatalogQueryAccess;
- _overrideCatalogZoneTransfer = zoneInfo.OverrideCatalogZoneTransfer;
- _overrideCatalogNotify = zoneInfo.OverrideCatalogNotify;
- _queryAccess = zoneInfo.QueryAccess;
- _queryAccessNetworkACL = zoneInfo.QueryAccessNetworkACL;
- _zoneTransfer = zoneInfo.ZoneTransfer;
- _zoneTransferNetworkACL = zoneInfo.ZoneTransferNetworkACL;
- _zoneTransferTsigKeyNames = zoneInfo.ZoneTransferTsigKeyNames;
- if (zoneInfo.ZoneHistory is null)
- _zoneHistory = new List<DnsResourceRecord>();
- else
- _zoneHistory = new List<DnsResourceRecord>(zoneInfo.ZoneHistory);
- _notify = zoneInfo.Notify;
- _notifyNameServers = zoneInfo.NotifyNameServers;
- _notifySecondaryCatalogNameServers = zoneInfo.NotifySecondaryCatalogNameServers;
- _update = zoneInfo.Update;
- _updateNetworkACL = zoneInfo.UpdateNetworkACL;
- _updateSecurityPolicies = zoneInfo.UpdateSecurityPolicies;
- _lastModified = zoneInfo.LastModified;
- }
- protected ApexZone(DnsServer dnsServer, string name)
- : base(name)
- {
- _dnsServer = dnsServer;
- _queryAccess = AuthZoneQueryAccess.Allow;
- _zoneHistory = new List<DnsResourceRecord>();
- _lastModified = DateTime.UtcNow;
- }
- #endregion
- #region IDisposable
- bool _disposed;
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- return;
- if (disposing)
- {
- _notifyTimer?.Dispose();
- lock (_recordExpiryTimerLock)
- {
- if (_recordExpiryTimer is not null)
- {
- _recordExpiryTimer.Dispose();
- _recordExpiryTimer = null;
- }
- }
- }
- _disposed = true;
- }
- public void Dispose()
- {
- Dispose(true);
- }
- #endregion
- #region notify
- protected void InitNotify()
- {
- _notifyTimer = new Timer(NotifyTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
- _notifyList = new List<string>();
- _notifyFailed = new List<string>();
- }
- protected void DisableNotifyTimer()
- {
- if (_notifyTimer is not null)
- _notifyTimer.Change(Timeout.Infinite, Timeout.Infinite);
- }
- private async void NotifyTimerCallback(object state)
- {
- ApexZone apexZone = this;
- if ((apexZone.CatalogZone is not null) && !apexZone.OverrideCatalogNotify)
- apexZone = apexZone.CatalogZone;
- List<string> notifiedNameServers = new List<string>();
- async Task NotifyZoneNameServersAsync(bool onlyFailedNameServers)
- {
- string primaryNameServer = (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).PrimaryNameServer;
- IReadOnlyList<DnsResourceRecord> nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords
- //notify all secondary name servers
- List<Task> tasks = new List<Task>();
- foreach (DnsResourceRecord nsRecord in nsRecords)
- {
- if (nsRecord.GetAuthGenericRecordInfo().Disabled)
- continue;
- string nameServerHost = (nsRecord.RDATA as DnsNSRecordData).NameServer;
- if (primaryNameServer.Equals(nameServerHost, StringComparison.OrdinalIgnoreCase))
- continue; //skip primary name server
- if (onlyFailedNameServers)
- {
- lock (_notifyFailed)
- {
- if (!_notifyFailed.Contains(nameServerHost))
- continue;
- }
- }
- notifiedNameServers.Add(nameServerHost);
- List<NameServerAddress> nameServers = new List<NameServerAddress>(2);
- await ResolveNameServerAddressesAsync(nsRecord, nameServers);
- if (nameServers.Count > 0)
- {
- tasks.Add(NotifyNameServerAsync(nameServerHost, nameServers));
- }
- else
- {
- lock (_notifyFailed)
- {
- if (!_notifyFailed.Contains(nameServerHost))
- _notifyFailed.Add(nameServerHost);
- }
- _dnsServer.LogManager?.Write("DNS Server failed to notify name server '" + nameServerHost + "' due to failure in resolving its IP address for zone: " + ToString());
- }
- }
- await Task.WhenAll(tasks);
- }
- Task NotifySpecifiedNameServersAsync(bool onlyFailedNameServers)
- {
- IReadOnlyCollection<IPAddress> specifiedNameServers = apexZone._notifyNameServers;
- if (specifiedNameServers is not null)
- return NotifyNameServersAsync(specifiedNameServers, onlyFailedNameServers);
- return Task.CompletedTask;
- }
- Task NotifySecondaryCatalogNameServersAsync(bool onlyFailedNameServers)
- {
- IReadOnlyCollection<IPAddress> secondaryCatalogNameServers = apexZone._notifySecondaryCatalogNameServers;
- if (secondaryCatalogNameServers is not null)
- return NotifyNameServersAsync(secondaryCatalogNameServers, onlyFailedNameServers);
- return Task.CompletedTask;
- }
- async Task NotifyNameServersAsync(IReadOnlyCollection<IPAddress> nameServerIpAddresses, bool onlyFailedNameServers)
- {
- List<Task> tasks = new List<Task>();
- foreach (IPAddress nameServerIpAddress in nameServerIpAddresses)
- {
- string nameServerHost = nameServerIpAddress.ToString();
- if (onlyFailedNameServers)
- {
- lock (_notifyFailed)
- {
- if (!_notifyFailed.Contains(nameServerHost))
- continue;
- }
- }
- notifiedNameServers.Add(nameServerHost);
- tasks.Add(NotifyNameServerAsync(nameServerHost, [new NameServerAddress(nameServerIpAddress)]));
- }
- await Task.WhenAll(tasks);
- }
- try
- {
- switch (apexZone._notify)
- {
- case AuthZoneNotify.ZoneNameServers:
- await NotifyZoneNameServersAsync(!_notifyTimerTriggered);
- break;
- case AuthZoneNotify.SpecifiedNameServers:
- await NotifySpecifiedNameServersAsync(!_notifyTimerTriggered);
- break;
- case AuthZoneNotify.BothZoneAndSpecifiedNameServers:
- Task t1 = NotifyZoneNameServersAsync(!_notifyTimerTriggered);
- Task t2 = NotifySpecifiedNameServersAsync(!_notifyTimerTriggered);
- await Task.WhenAll(t1, t2);
- break;
- case AuthZoneNotify.SeparateNameServersForCatalogAndMemberZones:
- if (this is CatalogZone)
- await NotifySecondaryCatalogNameServersAsync(!_notifyTimerTriggered);
- else
- await NotifySpecifiedNameServersAsync(!_notifyTimerTriggered);
- break;
- }
- //remove non-existent name servers from notify failed list
- lock (_notifyFailed)
- {
- if (_notifyFailed.Count > 0)
- {
- List<string> toRemove = new List<string>();
- foreach (string failedNameServer in _notifyFailed)
- {
- if (!notifiedNameServers.Contains(failedNameServer))
- toRemove.Add(failedNameServer);
- }
- foreach (string failedNameServer in toRemove)
- _notifyFailed.Remove(failedNameServer);
- if (_notifyFailed.Count > 0)
- {
- //set timer to notify failed name servers again
- int retryInterval = (int)((_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).Retry * 1000);
- _notifyTimer.Change(retryInterval, Timeout.Infinite);
- }
- }
- }
- }
- catch (Exception ex)
- {
- _dnsServer.LogManager?.Write(ex);
- }
- finally
- {
- _notifyTimerTriggered = false;
- }
- }
- private async Task NotifyNameServerAsync(string nameServerHost, IReadOnlyList<NameServerAddress> nameServers)
- {
- //use notify list to prevent multiple threads from notifying the same name server
- lock (_notifyList)
- {
- if (_notifyList.Contains(nameServerHost))
- return; //already notifying the name server in another thread
- _notifyList.Add(nameServerHost);
- }
- try
- {
- DnsClient client = new DnsClient(nameServers);
- client.Proxy = _dnsServer.Proxy;
- client.Timeout = NOTIFY_TIMEOUT;
- client.Retries = NOTIFY_RETRIES;
- DnsDatagram notifyRequest = new DnsDatagram(0, false, DnsOpcode.Notify, true, false, false, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord(_name, DnsResourceRecordType.SOA, DnsClass.IN) }, _entries[DnsResourceRecordType.SOA]);
- DnsDatagram response = await client.RawResolveAsync(notifyRequest);
- switch (response.RCODE)
- {
- case DnsResponseCode.NoError:
- case DnsResponseCode.NotImplemented:
- {
- //transaction complete
- lock (_notifyFailed)
- {
- _notifyFailed.Remove(nameServerHost);
- }
- _dnsServer.LogManager?.Write("DNS Server successfully notified name server '" + nameServerHost + "' for zone: " + ToString());
- }
- break;
- default:
- {
- //transaction failed
- lock (_notifyFailed)
- {
- if (!_notifyFailed.Contains(nameServerHost))
- _notifyFailed.Add(nameServerHost);
- }
- _dnsServer.LogManager?.Write("DNS Server failed to notify name server '" + nameServerHost + "' (RCODE=" + response.RCODE.ToString() + ") for zone: " + ToString());
- }
- break;
- }
- }
- catch (Exception ex)
- {
- lock (_notifyFailed)
- {
- if (!_notifyFailed.Contains(nameServerHost))
- _notifyFailed.Add(nameServerHost);
- }
- _dnsServer.LogManager?.Write("DNS Server failed to notify name server '" + nameServerHost + "' for zone: " + ToString() + "\r\n" + ex.ToString());
- }
- finally
- {
- lock (_notifyList)
- {
- _notifyList.Remove(nameServerHost);
- }
- }
- }
- internal void RemoveFromNotifyFailedList(NameServerAddress allowedZoneNameServer, IPAddress allowedIPAddress)
- {
- if (_notifyFailed is null)
- return;
- lock (_notifyFailed)
- {
- if (_notifyFailed.Count == 0)
- return;
- if ((allowedZoneNameServer is not null) && (allowedZoneNameServer.DomainEndPoint is not null))
- _notifyFailed.Remove(allowedZoneNameServer.DomainEndPoint.Address);
- _notifyFailed.Remove(allowedIPAddress.ToString());
- }
- }
- public void TriggerNotify()
- {
- if (Disabled)
- return;
- ApexZone apexZone = this;
- if ((apexZone.CatalogZone is not null) && !apexZone.OverrideCatalogNotify)
- apexZone = apexZone.CatalogZone;
- if (apexZone._notify == AuthZoneNotify.None)
- {
- if (_notifyFailed is not null)
- {
- lock (_notifyFailed)
- {
- _notifyFailed.Clear();
- }
- }
- return;
- }
- if (_notifyTimerTriggered)
- return;
- if (_disposed)
- return;
- if (_notifyTimer is null)
- return;
- _notifyTimer.Change(NOTIFY_TIMER_INTERVAL, Timeout.Infinite);
- _notifyTimerTriggered = true;
- }
- #endregion
- #region record expiry
- protected void InitRecordExpiry()
- {
- _recordExpiryTimer = new Timer(RecordExpiryTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
- }
- private uint GetMinRecordExpiryTtl(uint minExpiryTtl)
- {
- if (!_recordExpiryTimerRunning)
- return Math.Min(minExpiryTtl, uint.MaxValue / 1000);
- uint elapsedSeconds = Convert.ToUInt32((DateTime.UtcNow - _recordExpiryTimerStartedOn).TotalSeconds);
- if (elapsedSeconds >= _recordExpiryTimerTtl)
- return 0u;
- uint pendingExpiryTtl = _recordExpiryTimerTtl - elapsedSeconds;
- return Math.Min(Math.Min(pendingExpiryTtl, minExpiryTtl), uint.MaxValue / 1000);
- }
- public void StartRecordExpiryTimer(uint minExpiryTtl)
- {
- lock (_recordExpiryTimerLock)
- {
- if (_recordExpiryTimer is not null)
- {
- uint minTtl = GetMinRecordExpiryTtl(minExpiryTtl);
- _recordExpiryTimer.Change(minTtl * 1000, Timeout.Infinite);
- _recordExpiryTimerStartedOn = DateTime.UtcNow;
- _recordExpiryTimerTtl = minTtl;
- _recordExpiryTimerRunning = true;
- }
- }
- }
- private void RecordExpiryTimerCallback(object state)
- {
- _recordExpiryTimerRunning = false;
- uint minExpiryTtl = 0u;
- try
- {
- IReadOnlyList<AuthZone> authZones = _dnsServer.AuthZoneManager.GetApexZoneWithSubDomainZones(_name);
- bool recordsDeleted = false;
- foreach (AuthZone authZone in authZones)
- {
- foreach (KeyValuePair<DnsResourceRecordType, IReadOnlyList<DnsResourceRecord>> entry in authZone.Entries)
- {
- foreach (DnsResourceRecord record in entry.Value)
- {
- GenericRecordInfo recordInfo = record.GetAuthGenericRecordInfo();
- if (recordInfo.ExpiryTtl > 0u)
- {
- uint pendingExpiryTtl = recordInfo.GetPendingExpiryTtl();
- if (pendingExpiryTtl == 0u)
- {
- if (_dnsServer.AuthZoneManager.DeleteRecord(_name, record))
- recordsDeleted = true;
- }
- else
- {
- if (minExpiryTtl == 0u)
- minExpiryTtl = pendingExpiryTtl;
- else
- minExpiryTtl = Math.Min(minExpiryTtl, pendingExpiryTtl);
- }
- }
- }
- }
- }
- if (recordsDeleted)
- _dnsServer.AuthZoneManager.SaveZoneFile(_name);
- }
- catch (Exception ex)
- {
- _dnsServer.LogManager?.Write(ex);
- }
- finally
- {
- if (minExpiryTtl > 0u)
- StartRecordExpiryTimer(minExpiryTtl);
- }
- }
- #endregion
- #region internal
- internal virtual void UpdateDnssecStatus()
- {
- if (!_entries.ContainsKey(DnsResourceRecordType.DNSKEY))
- _dnssecStatus = AuthZoneDnssecStatus.Unsigned;
- else if (_entries.ContainsKey(DnsResourceRecordType.NSEC3PARAM))
- _dnssecStatus = AuthZoneDnssecStatus.SignedWithNSEC3;
- else
- _dnssecStatus = AuthZoneDnssecStatus.SignedWithNSEC;
- }
- #endregion
- #region versioning
- internal virtual void CommitAndIncrementSerial(IReadOnlyList<DnsResourceRecord> deletedRecords = null, IReadOnlyList<DnsResourceRecord> addedRecords = null)
- {
- _lastModified = DateTime.UtcNow;
- if (addedRecords is not null)
- {
- uint minExpiryTtl = 0u;
- foreach (DnsResourceRecord addedRecord in addedRecords)
- {
- uint expiryTtl = addedRecord.GetAuthGenericRecordInfo().ExpiryTtl;
- if (expiryTtl > 0u)
- {
- if (minExpiryTtl == 0u)
- minExpiryTtl = expiryTtl;
- else
- minExpiryTtl = Math.Min(minExpiryTtl, expiryTtl);
- }
- }
- if (minExpiryTtl > 0u)
- StartRecordExpiryTimer(minExpiryTtl);
- }
- lock (_zoneHistory)
- {
- DnsResourceRecord oldSoaRecord = _entries[DnsResourceRecordType.SOA][0];
- DnsResourceRecord newSoaRecord;
- {
- DnsSOARecordData oldSoa = oldSoaRecord.RDATA as DnsSOARecordData;
- if ((addedRecords is not null) && (addedRecords.Count == 1) && (addedRecords[0].Type == DnsResourceRecordType.SOA))
- {
- DnsResourceRecord addSoaRecord = addedRecords[0];
- DnsSOARecordData addSoa = addSoaRecord.RDATA as DnsSOARecordData;
- uint serial = GetNewSerial(oldSoa.Serial, addSoa.Serial, addSoaRecord.GetAuthSOARecordInfo().UseSoaSerialDateScheme);
- newSoaRecord = new DnsResourceRecord(_name, DnsResourceRecordType.SOA, DnsClass.IN, addSoaRecord.TTL, new DnsSOARecordData(addSoa.PrimaryNameServer, addSoa.ResponsiblePerson, serial, addSoa.Refresh, addSoa.Retry, addSoa.Expire, addSoa.Minimum)) { Tag = addSoaRecord.Tag };
- addedRecords = null;
- }
- else
- {
- uint serial = GetNewSerial(oldSoa.Serial, 0, oldSoaRecord.GetAuthSOARecordInfo().UseSoaSerialDateScheme);
- newSoaRecord = new DnsResourceRecord(_name, DnsResourceRecordType.SOA, DnsClass.IN, oldSoaRecord.TTL, new DnsSOARecordData(oldSoa.PrimaryNameServer, oldSoa.ResponsiblePerson, serial, oldSoa.Refresh, oldSoa.Retry, oldSoa.Expire, oldSoa.Minimum)) { Tag = oldSoaRecord.Tag };
- }
- }
- DnsResourceRecord[] newSoaRecords = [newSoaRecord];
- //update SOA
- _entries[DnsResourceRecordType.SOA] = newSoaRecords;
- IReadOnlyList<DnsResourceRecord> newRRSigRecords = null;
- IReadOnlyList<DnsResourceRecord> deletedRRSigRecords = null;
- if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)
- {
- //sign SOA and update RRSig
- newRRSigRecords = SignRRSet(newSoaRecords);
- AddOrUpdateRRSigRecords(newRRSigRecords, out deletedRRSigRecords);
- }
- //remove RR info from old SOA to allow creating new history RR info for setting DeletedOn
- oldSoaRecord.Tag = null;
- //start commit
- oldSoaRecord.GetAuthHistoryRecordInfo().DeletedOn = DateTime.UtcNow;
- //write removed
- _zoneHistory.Add(oldSoaRecord);
- if (deletedRecords is not null)
- {
- foreach (DnsResourceRecord deletedRecord in deletedRecords)
- {
- if (deletedRecord.GetAuthGenericRecordInfo().Disabled)
- continue;
- _zoneHistory.Add(deletedRecord);
- if (deletedRecord.Type == DnsResourceRecordType.NS)
- {
- IReadOnlyList<DnsResourceRecord> glueRecords = deletedRecord.GetAuthNSRecordInfo().GlueRecords;
- if (glueRecords is not null)
- _zoneHistory.AddRange(glueRecords);
- }
- }
- }
- if (deletedRRSigRecords is not null)
- _zoneHistory.AddRange(deletedRRSigRecords);
- //write added
- _zoneHistory.Add(newSoaRecord);
- if (addedRecords is not null)
- {
- foreach (DnsResourceRecord addedRecord in addedRecords)
- {
- if (addedRecord.GetAuthGenericRecordInfo().Disabled)
- continue;
- _zoneHistory.Add(addedRecord);
- if (addedRecord.Type == DnsResourceRecordType.NS)
- {
- IReadOnlyList<DnsResourceRecord> glueRecords = addedRecord.GetAuthNSRecordInfo().GlueRecords;
- if (glueRecords is not null)
- _zoneHistory.AddRange(glueRecords);
- }
- }
- }
- if (newRRSigRecords is not null)
- _zoneHistory.AddRange(newRRSigRecords);
- //end commit
- CleanupHistory();
- }
- }
- protected static uint GetNewSerial(uint oldSerial, uint updateSerial, bool useSoaSerialDateScheme)
- {
- if (useSoaSerialDateScheme)
- {
- string strOldSerial = oldSerial.ToString();
- string strOldSerialDate = null;
- byte counter = 0;
- if (strOldSerial.Length == 10)
- {
- //parse old serial
- strOldSerialDate = strOldSerial.Substring(0, 8);
- counter = byte.Parse(strOldSerial.Substring(8));
- }
- string strSerialDate = DateTime.UtcNow.ToString("yyyyMMdd");
- if (strOldSerialDate is null)
- {
- //transitioning to date scheme
- return uint.Parse(strSerialDate + counter.ToString().PadLeft(2, '0'));
- }
- else if (strSerialDate.Equals(strOldSerialDate))
- {
- //same date
- if (counter < 99)
- {
- counter++;
- return uint.Parse(strSerialDate + counter.ToString().PadLeft(2, '0'));
- }
- else
- {
- //more than 100 increments
- return uint.Parse(strSerialDate + counter.ToString().PadLeft(2, '0')) + 1;
- }
- }
- else if (uint.Parse(strSerialDate) > uint.Parse(strOldSerialDate))
- {
- //later date
- return uint.Parse(strSerialDate + "00");
- }
- }
- //default
- uint serial = oldSerial;
- if (updateSerial > serial)
- serial = updateSerial;
- else if (serial < uint.MaxValue)
- serial++;
- else
- serial = 1;
- return serial;
- }
- internal void SetSoaSerial(uint newSerial)
- {
- lock (_zoneHistory)
- {
- DnsResourceRecord oldSoaRecord = _entries[DnsResourceRecordType.SOA][0];
- DnsSOARecordData oldSoa = oldSoaRecord.RDATA as DnsSOARecordData;
- DnsResourceRecord newSoaRecord = new DnsResourceRecord(_name, DnsResourceRecordType.SOA, DnsClass.IN, oldSoaRecord.TTL, new DnsSOARecordData(oldSoa.PrimaryNameServer, oldSoa.ResponsiblePerson, newSerial, oldSoa.Refresh, oldSoa.Retry, oldSoa.Expire, oldSoa.Minimum)) { Tag = oldSoaRecord.Tag };
- DnsResourceRecord[] newSoaRecords = [newSoaRecord];
- //update SOA
- _entries[DnsResourceRecordType.SOA] = newSoaRecords;
- //clear history
- _zoneHistory.Clear();
- }
- }
- public IReadOnlyList<DnsResourceRecord> GetZoneHistory()
- {
- lock (_zoneHistory)
- {
- return _zoneHistory.ToArray();
- }
- }
- protected void CleanupHistory()
- {
- DnsSOARecordData soa = _entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData;
- DateTime expiry = DateTime.UtcNow.AddSeconds(-soa.Expire);
- int index = 0;
- while (index < _zoneHistory.Count)
- {
- //check difference sequence
- if (_zoneHistory[index].GetAuthHistoryRecordInfo().DeletedOn > expiry)
- break; //found record to keep
- //skip to next difference sequence
- index++;
- int soaCount = 1;
- while (index < _zoneHistory.Count)
- {
- if (_zoneHistory[index].Type == DnsResourceRecordType.SOA)
- {
- soaCount++;
- if (soaCount == 3)
- break;
- }
- index++;
- }
- }
- if (index == _zoneHistory.Count)
- {
- //delete entire history
- _zoneHistory.Clear();
- return;
- }
- //remove expired records
- _zoneHistory.RemoveRange(0, index);
- }
- protected void CommitZoneHistory(IReadOnlyList<DnsResourceRecord> historyRecords)
- {
- lock (_zoneHistory)
- {
- historyRecords[0].GetAuthHistoryRecordInfo().DeletedOn = DateTime.UtcNow;
- //write history
- _zoneHistory.AddRange(historyRecords);
- CleanupHistory();
- }
- }
- protected void ClearZoneHistory()
- {
- lock (_zoneHistory)
- {
- _zoneHistory.Clear();
- }
- }
- #endregion
- #region catalog zone
- private IReadOnlyCollection<NetworkAccessControl> GetQueryAccessACL()
- {
- switch (_queryAccess)
- {
- case AuthZoneQueryAccess.Allow:
- return [
- new NetworkAccessControl(IPAddress.Any, 0),
- new NetworkAccessControl(IPAddress.IPv6Any, 0)
- ];
- case AuthZoneQueryAccess.AllowOnlyPrivateNetworks:
- return [
- 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)
- ];
- case AuthZoneQueryAccess.AllowOnlyZoneNameServers:
- return [
- new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32)
- ];
- case AuthZoneQueryAccess.UseSpecifiedNetworkACL:
- return _queryAccessNetworkACL;
- case AuthZoneQueryAccess.AllowZoneNameServersAndUseSpecifiedNetworkACL:
- if (_queryAccessNetworkACL is null)
- {
- return [
- new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32)
- ];
- }
- return [
- new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32),
- .._queryAccessNetworkACL
- ];
- case AuthZoneQueryAccess.Deny:
- default:
- return [
- new NetworkAccessControl(IPAddress.Parse("127.0.0.0"), 8),
- new NetworkAccessControl(IPAddress.Parse("::1"), 128)
- ];
- }
- }
- private IReadOnlyCollection<NetworkAccessControl> GetZoneTranferACL()
- {
- switch (_zoneTransfer)
- {
- case AuthZoneTransfer.Allow:
- return [
- new NetworkAccessControl(IPAddress.Any, 0),
- new NetworkAccessControl(IPAddress.IPv6Any, 0)
- ];
- case AuthZoneTransfer.AllowOnlyZoneNameServers:
- return [
- new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32)
- ];
- case AuthZoneTransfer.UseSpecifiedNetworkACL:
- return _zoneTransferNetworkACL;
- case AuthZoneTransfer.AllowZoneNameServersAndUseSpecifiedNetworkACL:
- if (_zoneTransferNetworkACL is null)
- {
- return [
- new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32)
- ];
- }
- return [
- new NetworkAccessControl(IPAddress.Parse("224.0.0.0"), 32),
- .._zoneTransferNetworkACL
- ];
- case AuthZoneTransfer.Deny:
- default:
- return [
- new NetworkAccessControl(IPAddress.Any, 0, true),
- new NetworkAccessControl(IPAddress.IPv6Any, 0, true)
- ];
- }
- }
- #endregion
- #region public
- public uint GetZoneSoaMinimum()
- {
- return (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).Minimum;
- }
- public uint GetZoneSoaExpire()
- {
- return (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).Expire;
- }
- public uint GetZoneSoaSerial()
- {
- return (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).Serial;
- }
- public abstract string GetZoneTypeName();
- public override string ToString()
- {
- return _name.Length == 0 ? "<root>" : _name;
- }
- #endregion
- #region name server address resolution
- public async Task<IReadOnlyList<NameServerAddress>> GetResolvedPrimaryNameServerAddressesAsync()
- {
- IReadOnlyList<NameServerAddress> primaryNameServers;
- if (this is SecondaryZone secondary)
- primaryNameServers = secondary.PrimaryNameServerAddresses;
- else if (this is StubZone stub)
- primaryNameServers = stub.PrimaryNameServerAddresses;
- else
- primaryNameServers = null;
- if (primaryNameServers is not null)
- return await GetResolvedNameServerAddressesAsync(primaryNameServers);
- DnsResourceRecord soaRecord = _entries[DnsResourceRecordType.SOA][0];
- string primaryNameServer = (soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer;
- IReadOnlyList<DnsResourceRecord> nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords
- List<NameServerAddress> nameServers = new List<NameServerAddress>(nsRecords.Count * 2);
- foreach (DnsResourceRecord nsRecord in nsRecords)
- {
- if (nsRecord.GetAuthGenericRecordInfo().Disabled)
- continue;
- if (primaryNameServer.Equals((nsRecord.RDATA as DnsNSRecordData).NameServer, StringComparison.OrdinalIgnoreCase))
- {
- //found primary NS
- await ResolveNameServerAddressesAsync(nsRecord, nameServers);
- break;
- }
- }
- if (nameServers.Count < 1)
- await ResolveNameServerAddressesAsync(primaryNameServer, 53, DnsTransportProtocol.Udp, nameServers);
- return nameServers;
- }
- public async Task<IReadOnlyList<NameServerAddress>> GetResolvedSecondaryNameServerAddressesAsync()
- {
- string primaryNameServer = (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).PrimaryNameServer;
- IReadOnlyList<DnsResourceRecord> nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords
- List<NameServerAddress> nameServers = new List<NameServerAddress>(nsRecords.Count * 2);
- foreach (DnsResourceRecord nsRecord in nsRecords)
- {
- if (nsRecord.GetAuthGenericRecordInfo().Disabled)
- continue;
- if (primaryNameServer.Equals((nsRecord.RDATA as DnsNSRecordData).NameServer, StringComparison.OrdinalIgnoreCase))
- continue; //skip primary name server
- await ResolveNameServerAddressesAsync(nsRecord, nameServers);
- }
- return nameServers;
- }
- public async Task<IReadOnlyList<NameServerAddress>> GetAllResolvedNameServerAddressesAsync()
- {
- IReadOnlyList<DnsResourceRecord> nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords
- List<NameServerAddress> nameServers = new List<NameServerAddress>(nsRecords.Count * 2);
- foreach (DnsResourceRecord nsRecord in nsRecords)
- {
- if (nsRecord.GetAuthGenericRecordInfo().Disabled)
- continue;
- await ResolveNameServerAddressesAsync(nsRecord, nameServers);
- }
- return nameServers;
- }
- public async Task<IReadOnlyList<NameServerAddress>> GetResolvedNameServerAddressesAsync(IReadOnlyList<NameServerAddress> nameServers)
- {
- List<NameServerAddress> resolvedNameServers = new List<NameServerAddress>(nameServers.Count * 2);
- foreach (NameServerAddress nameServer in nameServers)
- {
- if (nameServer.IsIPEndPointStale)
- await ResolveNameServerAddressesAsync(nameServer.Host, nameServer.Port, nameServer.Protocol, resolvedNameServers);
- else
- resolvedNameServers.Add(nameServer);
- }
- return resolvedNameServers;
- }
- private async Task ResolveNameServerAddressesAsync(string nsDomain, int port, DnsTransportProtocol protocol, List<NameServerAddress> outNameServers)
- {
- try
- {
- DnsDatagram response = await _dnsServer.DirectQueryAsync(new DnsQuestionRecord(nsDomain, DnsResourceRecordType.A, DnsClass.IN));
- if (response.Answer.Count > 0)
- {
- IReadOnlyList<IPAddress> addresses = DnsClient.ParseResponseA(response);
- foreach (IPAddress address in addresses)
- outNameServers.Add(new NameServerAddress(nsDomain, new IPEndPoint(address, port), protocol));
- }
- }
- catch
- { }
- if (_dnsServer.PreferIPv6)
- {
- try
- {
- DnsDatagram response = await _dnsServer.DirectQueryAsync(new DnsQuestionRecord(nsDomain, DnsResourceRecordType.AAAA, DnsClass.IN));
- if (response.Answer.Count > 0)
- {
- IReadOnlyList<IPAddress> addresses = DnsClient.ParseResponseAAAA(response);
- foreach (IPAddress address in addresses)
- outNameServers.Add(new NameServerAddress(nsDomain, new IPEndPoint(address, port), protocol));
- }
- }
- catch
- { }
- }
- }
- private Task ResolveNameServerAddressesAsync(DnsResourceRecord nsRecord, List<NameServerAddress> outNameServers)
- {
- string nsDomain = (nsRecord.RDATA as DnsNSRecordData).NameServer;
- IReadOnlyList<DnsResourceRecord> glueRecords = nsRecord.GetAuthNSRecordInfo().GlueRecords;
- if (glueRecords is not null)
- {
- foreach (DnsResourceRecord glueRecord in glueRecords)
- {
- switch (glueRecord.Type)
- {
- case DnsResourceRecordType.A:
- outNameServers.Add(new NameServerAddress(nsDomain, (glueRecord.RDATA as DnsARecordData).Address));
- break;
- case DnsResourceRecordType.AAAA:
- if (_dnsServer.PreferIPv6)
- outNameServers.Add(new NameServerAddress(nsDomain, (glueRecord.RDATA as DnsAAAARecordData).Address));
- break;
- }
- }
- return Task.CompletedTask;
- }
- else
- {
- return ResolveNameServerAddressesAsync(nsDomain, 53, DnsTransportProtocol.Udp, outNameServers);
- }
- }
- #endregion
- #region properties
- public override bool Disabled
- {
- get { return base.Disabled; }
- set
- {
- if (base.Disabled == value)
- return;
- base.Disabled = value; //set value early to be able to use it for setting catalog properties
- CatalogZone catalogZone = CatalogZone;
- if (catalogZone is not null)
- {
- if (value)
- {
- //remove catalog zone membership without removing it from zone's options
- catalogZone.RemoveMemberZone(_name);
- _dnsServer.AuthZoneManager.SaveZoneFile(catalogZone._name);
- }
- else
- {
- //add catalog zone membership
- _dnsServer.AuthZoneManager.AddCatalogMemberZone(_catalogZoneName, new AuthZoneInfo(this), true);
- }
- }
- }
- }
- public DateTime LastModified
- { get { return _lastModified; } }
- public virtual string CatalogZoneName
- {
- get { return _catalogZoneName; }
- set
- {
- if (string.IsNullOrEmpty(value))
- _catalogZoneName = null;
- else
- _catalogZoneName = value;
- //reset
- _catalogZone = null;
- _secondaryCatalogZone = null;
- }
- }
- public virtual bool OverrideCatalogQueryAccess
- {
- get { return _overrideCatalogQueryAccess; }
- set { _overrideCatalogQueryAccess = value; }
- }
- public virtual bool OverrideCatalogZoneTransfer
- {
- get { return _overrideCatalogZoneTransfer; }
- set { _overrideCatalogZoneTransfer = value; }
- }
- public virtual bool OverrideCatalogNotify
- {
- get { return _overrideCatalogNotify; }
- set { _overrideCatalogNotify = value; }
- }
- public virtual AuthZoneQueryAccess QueryAccess
- {
- get { return _queryAccess; }
- set
- {
- _queryAccess = value;
- //update catalog zone property
- if (this is CatalogZone thisCatalogZone)
- {
- //update global custom property
- thisCatalogZone.SetAllowQueryProperty(GetQueryAccessACL());
- }
- else if (!Disabled && ((this is PrimaryZone) || (this is StubZone) || (this is ForwarderZone)))
- {
- CatalogZone catalogZone = CatalogZone;
- if (catalogZone is not null)
- {
- if (_overrideCatalogQueryAccess)
- catalogZone.SetAllowQueryProperty(GetQueryAccessACL(), _name); //update member zone custom property
- else
- catalogZone.SetAllowQueryProperty(null, _name); //remove member zone custom property
- }
- }
- }
- }
- public IReadOnlyCollection<NetworkAccessControl> QueryAccessNetworkACL
- {
- get { return _queryAccessNetworkACL; }
- set
- {
- if ((value is null) || (value.Count == 0))
- _queryAccessNetworkACL = null;
- else if (value.Count > byte.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(QueryAccessNetworkACL), "Network ACL cannot have more than 255 entries.");
- else
- _queryAccessNetworkACL = value;
- }
- }
- public virtual AuthZoneTransfer ZoneTransfer
- {
- get { return _zoneTransfer; }
- set
- {
- _zoneTransfer = value;
- //update catalog zone property
- if (this is CatalogZone thisCatalogZone)
- {
- //update global custom property
- thisCatalogZone.SetAllowTransferProperty(GetZoneTranferACL());
- }
- else if (!Disabled && (this is PrimaryZone))
- {
- CatalogZone catalogZone = CatalogZone;
- if (catalogZone is not null)
- {
- if (_overrideCatalogZoneTransfer)
- catalogZone.SetAllowTransferProperty(GetZoneTranferACL(), _name); //update member zone custom property
- else
- catalogZone.SetAllowTransferProperty(null, _name); //remove member zone custom property
- }
- }
- }
- }
- public IReadOnlyCollection<NetworkAccessControl> ZoneTransferNetworkACL
- {
- get { return _zoneTransferNetworkACL; }
- set
- {
- if ((value is null) || (value.Count == 0))
- _zoneTransferNetworkACL = null;
- else if (value.Count > byte.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(ZoneTransferNetworkACL), "Network ACL cannot have more than 255 entries.");
- else
- _zoneTransferNetworkACL = value;
- }
- }
- public IReadOnlyDictionary<string, object> ZoneTransferTsigKeyNames
- {
- get { return _zoneTransferTsigKeyNames; }
- set
- {
- if ((value is null) || (value.Count == 0))
- _zoneTransferTsigKeyNames = null;
- else if (value.Count > byte.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(ZoneTransferTsigKeyNames), "Zone transfer TSIG key names cannot have more than 255 entries.");
- else
- _zoneTransferTsigKeyNames = value;
- //update catalog zone property
- if (this is CatalogZone thisCatalogZone)
- {
- //update global custom property
- thisCatalogZone.SetZoneTransferTsigKeyNamesProperty(_zoneTransferTsigKeyNames);
- }
- else if (!Disabled && (this is PrimaryZone))
- {
- CatalogZone catalogZone = CatalogZone;
- if (catalogZone is not null)
- {
- if (_overrideCatalogZoneTransfer)
- catalogZone.SetZoneTransferTsigKeyNamesProperty(_zoneTransferTsigKeyNames, _name); //update member zone custom property
- else
- catalogZone.SetZoneTransferTsigKeyNamesProperty(null, _name); //remove member zone custom property
- }
- }
- }
- }
- public virtual AuthZoneNotify Notify
- {
- get { return _notify; }
- set
- {
- _notify = value;
- lock (_notifyFailed)
- {
- _notifyFailed.Clear();
- }
- }
- }
- public IReadOnlyCollection<IPAddress> NotifyNameServers
- {
- get { return _notifyNameServers; }
- set
- {
- if ((value is null) || (value.Count == 0))
- _notifyNameServers = null;
- else if (value.Count > byte.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(NotifyNameServers), "Name server addresses cannot have more than 255 entries.");
- else
- _notifyNameServers = value;
- lock (_notifyFailed)
- {
- _notifyFailed.Clear();
- }
- }
- }
- public IReadOnlyCollection<IPAddress> NotifySecondaryCatalogNameServers
- {
- get { return _notifySecondaryCatalogNameServers; }
- set
- {
- if ((value is null) || (value.Count == 0))
- _notifySecondaryCatalogNameServers = null;
- else if (value.Count > byte.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(NotifySecondaryCatalogNameServers), "Secondary Catalog name server addresses cannot have more than 255 entries.");
- else
- _notifySecondaryCatalogNameServers = value;
- lock (_notifyFailed)
- {
- _notifyFailed.Clear();
- }
- }
- }
- public virtual AuthZoneUpdate Update
- {
- get { return _update; }
- set { _update = value; }
- }
- public IReadOnlyCollection<NetworkAccessControl> UpdateNetworkACL
- {
- get { return _updateNetworkACL; }
- set
- {
- if ((value is null) || (value.Count == 0))
- _updateNetworkACL = null;
- else if (value.Count > byte.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(UpdateNetworkACL), "Network ACL cannot have more than 255 entries.");
- else
- _updateNetworkACL = value;
- }
- }
- public IReadOnlyDictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> UpdateSecurityPolicies
- {
- get { return _updateSecurityPolicies; }
- set { _updateSecurityPolicies = value; }
- }
- public AuthZoneDnssecStatus DnssecStatus
- { get { return _dnssecStatus; } }
- public string[] NotifyFailed
- {
- get
- {
- if (_notifyFailed is null)
- return Array.Empty<string>();
- lock (_notifyFailed)
- {
- if (_notifyFailed.Count > 0)
- return _notifyFailed.ToArray();
- return Array.Empty<string>();
- }
- }
- }
- public bool SyncFailed
- { get { return _syncFailed; } }
- public CatalogZone CatalogZone
- {
- get
- {
- if (_catalogZoneName is null)
- return null;
- if (_secondaryCatalogZone is not null)
- return null;
- if (_catalogZone is null)
- {
- if ((this is PrimaryZone) || (this is ForwarderZone))
- {
- ApexZone apexZone = _dnsServer.AuthZoneManager.GetApexZone(_catalogZoneName);
- if (apexZone is CatalogZone catalogZone)
- _catalogZone = catalogZone;
- }
- else if (this is StubZone)
- {
- ApexZone apexZone = _dnsServer.AuthZoneManager.GetApexZone(_catalogZoneName);
- if (apexZone is CatalogZone catalogZone)
- _catalogZone = catalogZone;
- else if (apexZone is SecondaryCatalogZone secondaryCatalogZone)
- _secondaryCatalogZone = secondaryCatalogZone;
- }
- }
- return _catalogZone;
- }
- }
- public SecondaryCatalogZone SecondaryCatalogZone
- {
- get
- {
- if (_catalogZoneName is null)
- return null;
- if (_catalogZone is not null)
- return null;
- if (_secondaryCatalogZone is null)
- {
- if (this is SecondaryZone)
- {
- ApexZone apexZone = _dnsServer.AuthZoneManager.GetApexZone(_catalogZoneName);
- if (apexZone is SecondaryCatalogZone secondaryCatalogZone)
- _secondaryCatalogZone = secondaryCatalogZone;
- }
- else if (this is StubZone)
- {
- ApexZone apexZone = _dnsServer.AuthZoneManager.GetApexZone(_catalogZoneName);
- if (apexZone is SecondaryCatalogZone secondaryCatalogZone)
- _secondaryCatalogZone = secondaryCatalogZone;
- else if (apexZone is CatalogZone catalogZone)
- _catalogZone = catalogZone;
- }
- }
- return _secondaryCatalogZone;
- }
- }
- #endregion
- }
- }
|