From dddb9c61148692f7ade9ba31dff339459aa5554b Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 23 May 2021 17:49:10 +0530 Subject: [PATCH] SecondaryZone: implemented notify feature. --- DnsServerCore/Dns/Zones/SecondaryZone.cs | 153 +++++++++++++++++++++-- 1 file changed, 142 insertions(+), 11 deletions(-) diff --git a/DnsServerCore/Dns/Zones/SecondaryZone.cs b/DnsServerCore/Dns/Zones/SecondaryZone.cs index f04db136..e73b261b 100644 --- a/DnsServerCore/Dns/Zones/SecondaryZone.cs +++ b/DnsServerCore/Dns/Zones/SecondaryZone.cs @@ -20,6 +20,7 @@ along with this program. If not, see . using DnsServerCore.Dns.ResourceRecords; using System; using System.Collections.Generic; +using System.Net; using System.Threading; using System.Threading.Tasks; using TechnitiumLibrary.Net.Dns; @@ -33,6 +34,13 @@ namespace DnsServerCore.Dns.Zones DnsServer _dnsServer; + readonly Timer _notifyTimer; + const int NOTIFY_TIMER_INTERVAL = 10000; + readonly List _notifyList; + + const int NOTIFY_TIMEOUT = 10000; + const int NOTIFY_RETRIES = 5; + readonly object _refreshTimerLock = new object(); Timer _refreshTimer; const int REFRESH_TIMER_INTERVAL = 5000; @@ -48,20 +56,30 @@ namespace DnsServerCore.Dns.Zones #region constructor - private SecondaryZone(string name) - : base(name) - { } - public SecondaryZone(DnsServer dnsServer, AuthZoneInfo zoneInfo) - : base(zoneInfo.Name) + : base(zoneInfo) { _dnsServer = dnsServer; - _disabled = zoneInfo.Disabled; _expiry = zoneInfo.Expiry; _isExpired = DateTime.UtcNow > _expiry; _refreshTimer = new Timer(RefreshTimerCallback, null, Timeout.Infinite, Timeout.Infinite); + + _notifyTimer = new Timer(NotifyTimerCallback, null, Timeout.Infinite, Timeout.Infinite); + _notifyList = new List(); + } + + private SecondaryZone(DnsServer dnsServer, string name) + : base(name) + { + _dnsServer = dnsServer; + + _zoneTransfer = AuthZoneTransfer.Deny; + _notify = AuthZoneNotify.None; + + _notifyTimer = new Timer(NotifyTimerCallback, null, Timeout.Infinite, Timeout.Infinite); + _notifyList = new List(); } #endregion @@ -70,9 +88,7 @@ namespace DnsServerCore.Dns.Zones public static async Task CreateAsync(DnsServer dnsServer, string name, string primaryNameServerAddresses = null) { - SecondaryZone secondaryZone = new SecondaryZone(name); - - secondaryZone._dnsServer = dnsServer; + SecondaryZone secondaryZone = new SecondaryZone(dnsServer, name); DnsQuestionRecord soaQuestion = new DnsQuestionRecord(name, DnsResourceRecordType.SOA, DnsClass.IN); DnsDatagram soaResponse = null; @@ -140,6 +156,104 @@ namespace DnsServerCore.Dns.Zones #region private + private async void NotifyTimerCallback(object state) + { + try + { + switch (_notify) + { + case AuthZoneNotify.ZoneNameServers: + IReadOnlyList secondaryNameServers = await GetSecondaryNameServerAddressesAsync(_dnsServer); + + foreach (NameServerAddress secondaryNameServer in secondaryNameServers) + _ = NotifyNameServerAsync(secondaryNameServer); + + break; + + case AuthZoneNotify.SpecifiedNameServers: + IReadOnlyCollection specifiedNameServers = _notifyNameServers; + if (specifiedNameServers is not null) + { + foreach (IPAddress specifiedNameServer in specifiedNameServers) + _ = NotifyNameServerAsync(new NameServerAddress(specifiedNameServer)); + } + + break; + + default: + return; + } + } + catch (Exception ex) + { + LogManager log = _dnsServer.LogManager; + if (log != null) + log.Write(ex); + } + } + + private async Task NotifyNameServerAsync(NameServerAddress nameServer) + { + //use notify list to prevent multiple threads from notifying the same name server + lock (_notifyList) + { + if (_notifyList.Contains(nameServer)) + return; //already notifying the name server in another thread + + _notifyList.Add(nameServer); + } + + try + { + DnsClient client = new DnsClient(nameServer); + + 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.ResolveAsync(notifyRequest); + + switch (response.RCODE) + { + case DnsResponseCode.NoError: + case DnsResponseCode.NotImplemented: + { + //transaction complete + LogManager log = _dnsServer.LogManager; + if (log != null) + log.Write("DNS Server successfully notified name server for '" + _name + "' zone changes: " + nameServer.ToString()); + } + break; + + default: + { + //transaction failed + LogManager log = _dnsServer.LogManager; + if (log != null) + log.Write("DNS Server received RCODE=" + response.RCODE.ToString() + " from name server for '" + _name + "' zone notification: " + nameServer.ToString()); + } + break; + } + } + catch (Exception ex) + { + LogManager log = _dnsServer.LogManager; + if (log != null) + { + log.Write("DNS Server failed to notify name server for '" + _name + "' zone changes: " + nameServer.ToString()); + log.Write(ex); + } + } + finally + { + lock (_notifyList) + { + _notifyList.Remove(nameServer); + } + } + } + private async void RefreshTimerCallback(object state) { if (_disabled) @@ -210,6 +324,9 @@ namespace DnsServerCore.Dns.Zones } DnsClient client = new DnsClient(primaryNameServers); + + client.Proxy = _dnsServer.Proxy; + client.PreferIPv6 = _dnsServer.PreferIPv6; client.Timeout = REFRESH_SOA_TIMEOUT; client.Retries = REFRESH_RETRIES; @@ -255,6 +372,9 @@ namespace DnsServerCore.Dns.Zones primaryNameServers = tcpNameServers; client = new DnsClient(primaryNameServers); + + client.Proxy = _dnsServer.Proxy; + client.PreferIPv6 = _dnsServer.PreferIPv6; client.Timeout = REFRESH_AXFR_TIMEOUT; client.Retries = REFRESH_RETRIES; @@ -293,6 +413,9 @@ namespace DnsServerCore.Dns.Zones { _dnsServer.AuthZoneManager.SyncRecords(_name, axfrResponse.Answer); + //trigger notify + TriggerNotify(); + LogManager log = _dnsServer.LogManager; if (log != null) log.Write("DNS Server successfully refreshed '" + _name + "' secondary zone from: " + axfrResponse.Metadata.NameServerAddress.ToString()); @@ -333,7 +456,15 @@ namespace DnsServerCore.Dns.Zones #region public - public void RefreshZone() + public void TriggerNotify() + { + if (_notify == AuthZoneNotify.None) + return; + + _notifyTimer.Change(NOTIFY_TIMER_INTERVAL, Timeout.Infinite); + } + + public void TriggerRefresh() { if (_disabled) return; @@ -397,7 +528,7 @@ namespace DnsServerCore.Dns.Zones if (_disabled) _refreshTimer.Change(Timeout.Infinite, Timeout.Infinite); else - RefreshZone(); + TriggerRefresh(); } } }