From 9dd2364bfe31087996eed902dd8e7ea668c324d9 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sat, 23 Apr 2022 17:54:14 +0530 Subject: [PATCH] CacheZoneManager: Implemented maximum entries feature. --- .../Dns/ZoneManagers/CacheZoneManager.cs | 137 +++++++++++++++++- 1 file changed, 129 insertions(+), 8 deletions(-) diff --git a/DnsServerCore/Dns/ZoneManagers/CacheZoneManager.cs b/DnsServerCore/Dns/ZoneManagers/CacheZoneManager.cs index 967b82f1..796aaca9 100644 --- a/DnsServerCore/Dns/ZoneManagers/CacheZoneManager.cs +++ b/DnsServerCore/Dns/ZoneManagers/CacheZoneManager.cs @@ -20,7 +20,9 @@ along with this program. If not, see . using DnsServerCore.Dns.ResourceRecords; using DnsServerCore.Dns.Trees; using DnsServerCore.Dns.Zones; +using System; using System.Collections.Generic; +using System.Threading; using TechnitiumLibrary.Net.Dns; using TechnitiumLibrary.Net.Dns.EDnsOptions; using TechnitiumLibrary.Net.Dns.ResourceRecords; @@ -41,6 +43,9 @@ namespace DnsServerCore.Dns.ZoneManagers readonly CacheZoneTree _root = new CacheZoneTree(); + long _maximumEntries; + long _totalEntries; + #endregion #region constructor @@ -103,13 +108,16 @@ namespace DnsServerCore.Dns.ZoneManagers return new CacheZone(resourceRecord.Name, 1); }); - zone.SetRecords(resourceRecord.Type, resourceRecords, _dnsServer.ServeStale); + if (zone.SetRecords(resourceRecord.Type, resourceRecords, _dnsServer.ServeStale)) + Interlocked.Increment(ref _totalEntries); } else { Dictionary>> groupedByDomainRecords = DnsResourceRecord.GroupRecords(resourceRecords); bool serveStale = _dnsServer.ServeStale; + int addedEntries = 0; + //add grouped records foreach (KeyValuePair>> groupedByTypeRecords in groupedByDomainRecords) { @@ -119,8 +127,14 @@ namespace DnsServerCore.Dns.ZoneManagers }); foreach (KeyValuePair> groupedRecords in groupedByTypeRecords.Value) - zone.SetRecords(groupedRecords.Key, groupedRecords.Value, serveStale); + { + if (zone.SetRecords(groupedRecords.Key, groupedRecords.Value, serveStale)) + addedEntries++; + } } + + if (addedEntries > 0) + Interlocked.Add(ref _totalEntries, addedEntries); } } @@ -280,6 +294,56 @@ namespace DnsServerCore.Dns.ZoneManagers } } + private int RemoveExpiredRecordsInternal(bool serveStale, long minimumEntriesToRemove) + { + int removedEntries = 0; + + foreach (CacheZone zone in _root) + { + removedEntries += zone.RemoveExpiredRecords(serveStale); + + if (zone.IsEmpty) + _root.TryRemove(zone.Name, out _); //remove empty zone + + if ((minimumEntriesToRemove > 0) && (removedEntries >= minimumEntriesToRemove)) + break; + } + + if (removedEntries > 0) + { + long totalEntries = Interlocked.Add(ref _totalEntries, -removedEntries); + if (totalEntries < 0) + Interlocked.Add(ref _totalEntries, -totalEntries); + } + + return removedEntries; + } + + private int RemoveLeastUsedRecordsInternal(DateTime cutoff, long minimumEntriesToRemove) + { + int removedEntries = 0; + + foreach (CacheZone zone in _root) + { + removedEntries += zone.RemoveLeastUsedRecords(cutoff); + + if (zone.IsEmpty) + _root.TryRemove(zone.Name, out _); //remove empty zone + + if ((minimumEntriesToRemove > 0) && (removedEntries >= minimumEntriesToRemove)) + break; + } + + if (removedEntries > 0) + { + long totalEntries = Interlocked.Add(ref _totalEntries, -removedEntries); + if (totalEntries < 0) + Interlocked.Add(ref _totalEntries, -totalEntries); + } + + return removedEntries; + } + #endregion #region public @@ -288,23 +352,61 @@ namespace DnsServerCore.Dns.ZoneManagers { bool serveStale = _dnsServer.ServeStale; - foreach (CacheZone zone in _root) - { - zone.RemoveExpiredRecords(serveStale); + //remove expired records/expired stale records + RemoveExpiredRecordsInternal(serveStale, 0); - if (zone.IsEmpty) - _root.TryRemove(zone.Name, out _); //remove empty zone + if (_maximumEntries < 1) + return; //cache limit feature disabled + + //find minimum entries to remove + long minimumEntriesToRemove = _totalEntries - _maximumEntries; + if (minimumEntriesToRemove < 1) + return; //no need to remove + + //remove stale records if they exists + if (serveStale) + minimumEntriesToRemove -= RemoveExpiredRecordsInternal(false, minimumEntriesToRemove); + + if (minimumEntriesToRemove < 1) + return; //task completed + + //remove least recently used records + for (int seconds = 86400; seconds > 0; seconds /= 2) + { + DateTime cutoff = DateTime.UtcNow.AddSeconds(-seconds); + + minimumEntriesToRemove -= RemoveLeastUsedRecordsInternal(cutoff, minimumEntriesToRemove); + + if (minimumEntriesToRemove < 1) + break; //task completed } } public override void Flush() { _root.Clear(); + + long totalEntries = _totalEntries; + totalEntries = Interlocked.Add(ref _totalEntries, -totalEntries); + if (totalEntries < 0) + Interlocked.Add(ref _totalEntries, -totalEntries); } public bool DeleteZone(string domain) { - return _root.TryRemove(domain, out _); + if (_root.TryRemoveTree(domain, out _, out int removedEntries)) + { + if (removedEntries > 0) + { + long totalEntries = Interlocked.Add(ref _totalEntries, -removedEntries); + if (totalEntries < 0) + Interlocked.Add(ref _totalEntries, -totalEntries); + } + + return true; + } + + return false; } public void ListSubDomains(string domain, List subDomains) @@ -574,5 +676,24 @@ namespace DnsServerCore.Dns.ZoneManagers } #endregion + + #region properties + + public long MaximumEntries + { + get { return _maximumEntries; } + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(MaximumEntries), "Invalid cache maximum entries value. Valid range is 0 and above."); + + _maximumEntries = value; + } + } + + public long TotalEntries + { get { return _totalEntries; } } + + #endregion } }