From c0c0a5c3961db59e244685633e767da90c6d08da Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 25 Jun 2023 17:24:12 +0530 Subject: [PATCH] ApexZone: Updated notification implementation to retry after SOA RETRY seconds to attempt to automatically recover from the notify failure. Updated NotifyFailed to return list of failed name servers. --- DnsServerCore/Dns/Zones/ApexZone.cs | 98 ++++++++++++++++++----------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/DnsServerCore/Dns/Zones/ApexZone.cs b/DnsServerCore/Dns/Zones/ApexZone.cs index 0765ccfd..cdac5e6c 100644 --- a/DnsServerCore/Dns/Zones/ApexZone.cs +++ b/DnsServerCore/Dns/Zones/ApexZone.cs @@ -199,12 +199,16 @@ namespace DnsServerCore.Dns.Zones { DnsServer dnsServer = state as DnsServer; - async Task NotifyZoneNameServers(List existingNameServers) + List notifiedNameServers = new List(); + + async Task NotifyZoneNameServersAsync(bool onlyFailedNameServers) { string primaryNameServer = (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).PrimaryNameServer; IReadOnlyList nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords //notify all secondary name servers + List tasks = new List(); + foreach (DnsResourceRecord nsRecord in nsRecords) { if (nsRecord.GetAuthRecordInfo().Disabled) @@ -215,14 +219,23 @@ namespace DnsServerCore.Dns.Zones if (primaryNameServer.Equals(nameServerHost, StringComparison.OrdinalIgnoreCase)) continue; //skip primary name server - existingNameServers.Add(nameServerHost); + if (onlyFailedNameServers) + { + lock (_notifyFailed) + { + if (!_notifyFailed.Contains(nameServerHost)) + continue; + } + } + + notifiedNameServers.Add(nameServerHost); List nameServers = new List(2); await ResolveNameServerAddressesAsync(dnsServer, nsRecord, nameServers); if (nameServers.Count > 0) { - _ = NotifyNameServerAsync(dnsServer, nameServerHost, nameServers); + tasks.Add(NotifyNameServerAsync(dnsServer, nameServerHost, nameServers)); } else { @@ -232,82 +245,90 @@ namespace DnsServerCore.Dns.Zones _notifyFailed.Add(nameServerHost); } - LogManager log = dnsServer.LogManager; - if (log != null) - log.Write("DNS Server failed to notify name server '" + nameServerHost + "' due to failure in resolving its IP address for zone: " + (_name == "" ? "" : _name)); + dnsServer.LogManager?.Write("DNS Server failed to notify name server '" + nameServerHost + "' due to failure in resolving its IP address for zone: " + (_name == "" ? "" : _name)); } } + + await Task.WhenAll(tasks); } - void NotifySpecifiedNameServers(List existingNameServers) + async Task NotifySpecifiedNameServersAsync(bool onlyFailedNameServers) { IReadOnlyCollection specifiedNameServers = _notifyNameServers; if (specifiedNameServers is not null) { + List tasks = new List(); + foreach (IPAddress specifiedNameServer in specifiedNameServers) { string nameServerHost = specifiedNameServer.ToString(); - existingNameServers.Add(nameServerHost); - _ = NotifyNameServerAsync(dnsServer, nameServerHost, new NameServerAddress[] { new NameServerAddress(specifiedNameServer) }); + if (onlyFailedNameServers) + { + lock (_notifyFailed) + { + if (!_notifyFailed.Contains(nameServerHost)) + continue; + } + } + + notifiedNameServers.Add(nameServerHost); + + tasks.Add(NotifyNameServerAsync(dnsServer, nameServerHost, new NameServerAddress[] { new NameServerAddress(specifiedNameServer) })); } + + await Task.WhenAll(tasks); } } try { - List existingNameServers = new List(); - switch (_notify) { case AuthZoneNotify.ZoneNameServers: - await NotifyZoneNameServers(existingNameServers); + await NotifyZoneNameServersAsync(!_notifyTimerTriggered); break; case AuthZoneNotify.SpecifiedNameServers: - NotifySpecifiedNameServers(existingNameServers); + await NotifySpecifiedNameServersAsync(!_notifyTimerTriggered); break; case AuthZoneNotify.BothZoneAndSpecifiedNameServers: - await NotifyZoneNameServers(existingNameServers); - NotifySpecifiedNameServers(existingNameServers); + Task t1 = NotifyZoneNameServersAsync(!_notifyTimerTriggered); + Task t2 = NotifySpecifiedNameServersAsync(!_notifyTimerTriggered); + + await Task.WhenAll(t1, t2); break; } //remove non-existent name servers from notify failed list lock (_notifyFailed) { - List toRemove = new List(); - - foreach (string failedNameServer in _notifyFailed) + if (_notifyFailed.Count > 0) { - bool found = false; + List toRemove = new List(); - foreach (string existingNameServer in existingNameServers) + foreach (string failedNameServer in _notifyFailed) { - if (failedNameServer.Equals(existingNameServer)) - { - found = true; - break; - } + if (!notifiedNameServers.Contains(failedNameServer)) + toRemove.Add(failedNameServer); } - if (!found) - toRemove.Add(failedNameServer); - } - - if (toRemove.Count > 0) - { 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) { - LogManager log = dnsServer.LogManager; - if (log != null) - log.Write(ex); + dnsServer.LogManager?.Write(ex); } finally { @@ -659,16 +680,19 @@ namespace DnsServerCore.Dns.Zones set { _updateSecurityPolicies = value; } } - public bool NotifyFailed + public string[] NotifyFailed { get { if (_notifyFailed is null) - return false; + return Array.Empty(); lock (_notifyFailed) { - return _notifyFailed.Count > 0; + if (_notifyFailed.Count > 0) + return _notifyFailed.ToArray(); + + return Array.Empty(); } } }