From 143ad95c93a015e2f357119679b84a23ac099b52 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sat, 5 Dec 2020 19:07:38 +0530 Subject: [PATCH] DnsServer: Fixed issue in cache prefetch refresh feature that didnt take into account the Stub and Forwarder zones causing the DNS server to do usual recursive resolution to refresh cache instead of using the zone specified name servers or conditional forwarders. --- DnsServerCore/Dns/DnsServer.cs | 120 +++++++++++++++++++++++++++------ 1 file changed, 99 insertions(+), 21 deletions(-) diff --git a/DnsServerCore/Dns/DnsServer.cs b/DnsServerCore/Dns/DnsServer.cs index c4d4813e..fd5ed4b9 100644 --- a/DnsServerCore/Dns/DnsServer.cs +++ b/DnsServerCore/Dns/DnsServer.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Dns.ResourceRecords; using DnsServerCore.Dns.ZoneManagers; using DnsServerCore.Dns.Zones; using Newtonsoft.Json; @@ -119,7 +120,7 @@ namespace DnsServerCore.Dns readonly object _cachePrefetchRefreshTimerLock = new object(); const int CACHE_PREFETCH_REFRESH_TIMER_INITIAL_INTEVAL = 60000; DateTime _cachePrefetchSamplingTimerTriggersOn; - IList _cachePrefetchSampleList; + IList _cacheRefreshSampleList; Timer _cacheMaintenanceTimer; const int CACHE_MAINTENANCE_TIMER_INITIAL_INTEVAL = 60 * 60 * 1000; @@ -1705,13 +1706,13 @@ namespace DnsServerCore.Dns } } - private async Task RefreshCacheAsync(IList cacheRefreshSampleList, DnsQuestionRecord sampleQuestion, int sampleQuestionIndex) + private async Task RefreshCacheAsync(IList cacheRefreshSampleList, CacheRefreshSample sample, int sampleQuestionIndex) { try { //refresh cache - DnsDatagram request = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, true, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { sampleQuestion }); - DnsDatagram response = await ProcessRecursiveQueryAsync(request, null, null, false, true); + DnsDatagram request = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, true, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { sample.SampleQuestion }); + DnsDatagram response = await ProcessRecursiveQueryAsync(request, sample.ViaNameServers, sample.ViaForwarders, false, true); bool removeFromSampleList = true; DateTime utcNow = DateTime.UtcNow; @@ -1801,22 +1802,77 @@ namespace DnsServerCore.Dns if (stats != null) { List> eligibleQueries = stats.GetLastHourEligibleQueries(_cachePrefetchSampleEligibilityHitsPerHour); - List cacheRefreshSampleList = new List(eligibleQueries.Count); + List cacheRefreshSampleList = new List(eligibleQueries.Count); int cacheRefreshTrigger = (_cachePrefetchSampleIntervalInMinutes + 1) * 60; foreach (KeyValuePair query in eligibleQueries) { + IReadOnlyList viaNameServers = null; + IReadOnlyList viaForwarders = null; + AuthZoneInfo zoneInfo = _authZoneManager.GetAuthZoneInfo(query.Key.Name); - if (zoneInfo != null) + if ((zoneInfo != null) && !zoneInfo.Disabled) { switch (zoneInfo.Type) { case AuthZoneType.Primary: case AuthZoneType.Secondary: //zone is hosted - if (!zoneInfo.Disabled) - continue; //no cache refresh for zone that is hosted and enabled + continue; //no cache refresh for hosted zones + case AuthZoneType.Stub: //stub zone refresh via its name servers + { + IReadOnlyList nsRecords = zoneInfo.GetRecords(DnsResourceRecordType.NS); + + List nameServers = new List(); + + foreach (DnsResourceRecord nsRecord in nsRecords) + { + string nsDomain = (nsRecord.RDATA as DnsNSRecord).NameServer; + + IReadOnlyList glueRecords = nsRecord.GetGlueRecords(); + if (glueRecords.Count > 0) + { + foreach (DnsResourceRecord glueRecord in glueRecords) + { + switch (glueRecord.Type) + { + case DnsResourceRecordType.A: + nameServers.Add(new NameServerAddress(nsDomain, (glueRecord.RDATA as DnsARecord).Address)); + break; + + case DnsResourceRecordType.AAAA: + if (_preferIPv6) + nameServers.Add(new NameServerAddress(nsDomain, (glueRecord.RDATA as DnsAAAARecord).Address)); + + break; + } + } + } + else + { + nameServers.Add(new NameServerAddress(nsDomain)); + } + } + + viaNameServers = nameServers; + } + break; + + case AuthZoneType.Forwarder: //forwarder zone refresh via its forwarders + { + IReadOnlyList fwdRecords = zoneInfo.GetRecords(DnsResourceRecordType.FWD); + + List forwarders = new List(); + + foreach (DnsResourceRecord fwdRecord in fwdRecords) + { + if (!fwdRecord.IsDisabled()) + forwarders.Add((fwdRecord.RDATA as DnsForwarderRecord).NameServer); + } + + viaForwarders = forwarders; + } break; } } @@ -1826,10 +1882,19 @@ namespace DnsServerCore.Dns DnsQuestionRecord refreshQuery = GetCacheRefreshNeededQuery(query.Key, cacheRefreshTrigger); if (refreshQuery != null) - cacheRefreshSampleList.Add(refreshQuery); + { + if ((viaNameServers != null) && !refreshQuery.Name.Equals(query.Key.Name, StringComparison.OrdinalIgnoreCase)) + { + //stub zone case where refresh query is a CNAME of the original query + if (!refreshQuery.Name.Equals(zoneInfo.Name, StringComparison.OrdinalIgnoreCase) && !refreshQuery.Name.EndsWith("." + zoneInfo.Name, StringComparison.OrdinalIgnoreCase)) + viaNameServers = null; //refresh query is a CNAME that is outside of the stub zone so do usual recursive resolution + } + + cacheRefreshSampleList.Add(new CacheRefreshSample(refreshQuery, viaNameServers, viaForwarders)); + } } - _cachePrefetchSampleList = cacheRefreshSampleList; + _cacheRefreshSampleList = cacheRefreshSampleList; } } catch (Exception ex) @@ -1844,7 +1909,7 @@ namespace DnsServerCore.Dns { if (_cachePrefetchSamplingTimer != null) { - _cachePrefetchSamplingTimer.Change(_cachePrefetchSampleIntervalInMinutes * 60 * 1000, System.Threading.Timeout.Infinite); + _cachePrefetchSamplingTimer.Change(_cachePrefetchSampleIntervalInMinutes * 60 * 1000, Timeout.Infinite); _cachePrefetchSamplingTimerTriggersOn = DateTime.UtcNow.AddMinutes(_cachePrefetchSampleIntervalInMinutes); } } @@ -1855,19 +1920,19 @@ namespace DnsServerCore.Dns { try { - IList cacheRefreshSampleList = _cachePrefetchSampleList; + IList cacheRefreshSampleList = _cacheRefreshSampleList; if (cacheRefreshSampleList != null) { for (int i = 0; i < cacheRefreshSampleList.Count; i++) { - DnsQuestionRecord sampleQuestion = cacheRefreshSampleList[i]; - if (sampleQuestion == null) + CacheRefreshSample sample = cacheRefreshSampleList[i]; + if (sample == null) continue; - if (!IsCacheRefreshNeeded(sampleQuestion, _cachePrefetchTrigger + 2)) + if (!IsCacheRefreshNeeded(sample.SampleQuestion, _cachePrefetchTrigger + 2)) continue; - _ = RefreshCacheAsync(cacheRefreshSampleList, sampleQuestion, i); + _ = RefreshCacheAsync(cacheRefreshSampleList, sample, i); } } } @@ -1908,13 +1973,13 @@ namespace DnsServerCore.Dns lock (_cachePrefetchSamplingTimerLock) { if (_cachePrefetchSamplingTimer != null) - _cachePrefetchSamplingTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); + _cachePrefetchSamplingTimer.Change(Timeout.Infinite, Timeout.Infinite); } lock (_cachePrefetchRefreshTimerLock) { if (_cachePrefetchRefreshTimer != null) - _cachePrefetchRefreshTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); + _cachePrefetchRefreshTimer.Change(Timeout.Infinite, Timeout.Infinite); } } else if (_state == ServiceState.Running) @@ -1923,7 +1988,7 @@ namespace DnsServerCore.Dns { if (_cachePrefetchSamplingTimer != null) { - _cachePrefetchSamplingTimer.Change(_cachePrefetchSampleIntervalInMinutes * 60 * 1000, System.Threading.Timeout.Infinite); + _cachePrefetchSamplingTimer.Change(_cachePrefetchSampleIntervalInMinutes * 60 * 1000, Timeout.Infinite); _cachePrefetchSamplingTimerTriggersOn = DateTime.UtcNow.AddMinutes(_cachePrefetchSampleIntervalInMinutes); } } @@ -1931,10 +1996,9 @@ namespace DnsServerCore.Dns lock (_cachePrefetchRefreshTimerLock) { if (_cachePrefetchRefreshTimer != null) - _cachePrefetchRefreshTimer.Change(CACHE_PREFETCH_REFRESH_TIMER_INITIAL_INTEVAL, System.Threading.Timeout.Infinite); + _cachePrefetchRefreshTimer.Change(CACHE_PREFETCH_REFRESH_TIMER_INITIAL_INTEVAL, Timeout.Infinite); } } - } private void UpdateThisServer() @@ -2627,5 +2691,19 @@ namespace DnsServerCore.Dns } #endregion + + class CacheRefreshSample + { + public CacheRefreshSample(DnsQuestionRecord sampleQuestion, IReadOnlyList viaNameServers, IReadOnlyList viaForwarders) + { + SampleQuestion = sampleQuestion; + ViaNameServers = viaNameServers; + ViaForwarders = viaForwarders; + } + + public DnsQuestionRecord SampleQuestion { get; } + public IReadOnlyList ViaNameServers { get; } + public IReadOnlyList ViaForwarders { get; } + } } }