AuthZoneManager: Implemented zone index to allow paginated access to zones list.

This commit is contained in:
Shreyas Zare
2023-02-12 12:56:09 +05:30
parent 29d35fb022
commit 06282fc8c0

View File

@@ -45,7 +45,8 @@ namespace DnsServerCore.Dns.ZoneManagers
readonly AuthZoneTree _root = new AuthZoneTree();
int _totalZones;
readonly List<AuthZoneInfo> _zoneIndex = new List<AuthZoneInfo>(10);
readonly ReaderWriterLockSlim _zoneIndexLock = new ReaderWriterLockSlim();
#endregion
@@ -94,7 +95,7 @@ namespace DnsServerCore.Dns.ZoneManagers
//update authoritative zone SOA and NS records
try
{
List<AuthZoneInfo> zones = GetAllZones();
IReadOnlyList<AuthZoneInfo> zones = GetAllZones();
foreach (AuthZoneInfo zone in zones)
{
@@ -179,10 +180,7 @@ namespace DnsServerCore.Dns.ZoneManagers
}
if (_root.TryAdd(zone))
{
_totalZones++;
return zone;
}
throw new DnsServerException("Zone already exists: " + zoneInfo.Name);
}
@@ -462,7 +460,16 @@ namespace DnsServerCore.Dns.ZoneManagers
internal void Flush()
{
_root.Clear();
_zoneIndexLock.EnterWriteLock();
try
{
_root.Clear();
_zoneIndex.Clear();
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
}
private static IReadOnlyList<DnsResourceRecord> CondenseIncrementalZoneTransferRecords(string zoneName, DnsResourceRecord currentSoaRecord, IReadOnlyList<DnsResourceRecord> xfrRecords)
@@ -652,7 +659,7 @@ namespace DnsServerCore.Dns.ZoneManagers
public void LoadAllZoneFiles()
{
_root.Clear();
Flush();
string zonesFolder = Path.Combine(_dnsServer.ConfigFolder, "zones");
if (!Directory.Exists(zonesFolder))
@@ -666,7 +673,7 @@ namespace DnsServerCore.Dns.ZoneManagers
File.Move(oldZoneFile, Path.Combine(zonesFolder, Path.GetFileName(oldZoneFile)));
}
//remove old internal zones
//remove old internal zones files
{
string[] oldZoneFiles = new string[] { "localhost.zone", "1.0.0.127.in-addr.arpa.zone", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.zone" };
@@ -722,27 +729,38 @@ namespace DnsServerCore.Dns.ZoneManagers
}
//load zone files
string[] zoneFiles = Directory.GetFiles(zonesFolder, "*.zone");
foreach (string zoneFile in zoneFiles)
_zoneIndexLock.EnterWriteLock();
try
{
try
{
using (FileStream fS = new FileStream(zoneFile, FileMode.Open, FileAccess.Read))
{
LoadZoneFrom(fS);
}
string[] zoneFiles = Directory.GetFiles(zonesFolder, "*.zone");
LogManager log = _dnsServer.LogManager;
if (log != null)
log.Write("DNS Server successfully loaded zone file: " + zoneFile);
}
catch (Exception ex)
foreach (string zoneFile in zoneFiles)
{
LogManager log = _dnsServer.LogManager;
if (log != null)
log.Write("DNS Server failed to load zone file: " + zoneFile + "\r\n" + ex.ToString());
try
{
using (FileStream fS = new FileStream(zoneFile, FileMode.Open, FileAccess.Read))
{
AuthZoneInfo zoneInfo = LoadZoneFrom(fS);
_zoneIndex.Add(zoneInfo);
}
LogManager log = _dnsServer.LogManager;
if (log != null)
log.Write("DNS Server successfully loaded zone file: " + zoneFile);
}
catch (Exception ex)
{
LogManager log = _dnsServer.LogManager;
if (log != null)
log.Write("DNS Server failed to load zone file: " + zoneFile + "\r\n" + ex.ToString());
}
}
_zoneIndex.Sort();
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
}
@@ -750,10 +768,21 @@ namespace DnsServerCore.Dns.ZoneManagers
{
PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, soaRecord, ns);
if (_root.TryAdd(apexZone))
_zoneIndexLock.EnterWriteLock();
try
{
_totalZones++;
return new AuthZoneInfo(apexZone);
if (_root.TryAdd(apexZone))
{
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
_zoneIndex.Add(zoneInfo);
_zoneIndex.Sort();
return zoneInfo;
}
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
return null;
@@ -763,10 +792,21 @@ namespace DnsServerCore.Dns.ZoneManagers
{
PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, primaryNameServer, @internal);
if (_root.TryAdd(apexZone))
_zoneIndexLock.EnterWriteLock();
try
{
_totalZones++;
return new AuthZoneInfo(apexZone);
if (_root.TryAdd(apexZone))
{
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
_zoneIndex.Add(zoneInfo);
_zoneIndex.Sort();
return zoneInfo;
}
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
return null;
@@ -776,11 +816,23 @@ namespace DnsServerCore.Dns.ZoneManagers
{
SecondaryZone apexZone = await SecondaryZone.CreateAsync(_dnsServer, zoneName, primaryNameServerAddresses, zoneTransferProtocol, tsigKeyName);
if (_root.TryAdd(apexZone))
_zoneIndexLock.EnterWriteLock();
try
{
apexZone.TriggerRefresh(0);
_totalZones++;
return new AuthZoneInfo(apexZone);
if (_root.TryAdd(apexZone))
{
apexZone.TriggerRefresh(0);
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
_zoneIndex.Add(zoneInfo);
_zoneIndex.Sort();
return zoneInfo;
}
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
return null;
@@ -790,11 +842,23 @@ namespace DnsServerCore.Dns.ZoneManagers
{
StubZone apexZone = await StubZone.CreateAsync(_dnsServer, zoneName, primaryNameServerAddresses);
if (_root.TryAdd(apexZone))
_zoneIndexLock.EnterWriteLock();
try
{
apexZone.TriggerRefresh(0);
_totalZones++;
return new AuthZoneInfo(apexZone);
if (_root.TryAdd(apexZone))
{
apexZone.TriggerRefresh(0);
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
_zoneIndex.Add(zoneInfo);
_zoneIndex.Sort();
return zoneInfo;
}
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
return null;
@@ -804,10 +868,21 @@ namespace DnsServerCore.Dns.ZoneManagers
{
ForwarderZone apexZone = new ForwarderZone(zoneName, forwarderProtocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, fwdRecordComments);
if (_root.TryAdd(apexZone))
_zoneIndexLock.EnterWriteLock();
try
{
_totalZones++;
return new AuthZoneInfo(apexZone);
if (_root.TryAdd(apexZone))
{
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
_zoneIndex.Add(zoneInfo);
_zoneIndex.Sort();
return zoneInfo;
}
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
return null;
@@ -943,12 +1018,23 @@ namespace DnsServerCore.Dns.ZoneManagers
public bool DeleteZone(string zoneName)
{
if (_root.TryRemove(zoneName, out ApexZone apexZone))
_zoneIndexLock.EnterWriteLock();
try
{
apexZone.Dispose();
if (_root.TryRemove(zoneName, out ApexZone apexZone))
{
apexZone.Dispose();
_totalZones--;
return true;
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
if (!_zoneIndex.Remove(zoneInfo))
throw new InvalidOperationException("Zone deleted from tree but failed to remove from zone index.");
return true;
}
}
finally
{
_zoneIndexLock.ExitWriteLock();
}
return false;
@@ -1698,31 +1784,49 @@ namespace DnsServerCore.Dns.ZoneManagers
}
}
public List<AuthZoneInfo> GetAllZones()
public IReadOnlyList<AuthZoneInfo> GetAllZones()
{
List<AuthZoneInfo> zones = new List<AuthZoneInfo>();
foreach (AuthZoneNode zoneNode in _root)
_zoneIndexLock.EnterReadLock();
try
{
ApexZone apexZone = zoneNode.ApexZone;
if (apexZone is null)
continue;
AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
case AuthZoneType.Secondary:
case AuthZoneType.Stub:
case AuthZoneType.Forwarder:
zones.Add(zoneInfo);
break;
}
return new List<AuthZoneInfo>(_zoneIndex);
}
finally
{
_zoneIndexLock.ExitReadLock();
}
}
_totalZones = zones.Count;
public ZonesPage GetZonesPage(int pageNumber, int zonesPerPage)
{
_zoneIndexLock.EnterReadLock();
try
{
if (pageNumber < 0)
pageNumber = int.MaxValue;
else if (pageNumber == 0)
pageNumber = 1;
return zones;
int totalZones = _zoneIndex.Count;
int totalPages = (totalZones / zonesPerPage) + (totalZones % zonesPerPage > 0 ? 1 : 0);
if (pageNumber > totalPages)
pageNumber = totalPages;
int start = (pageNumber - 1) * zonesPerPage;
int end = Math.Min(start + zonesPerPage, totalZones);
List<AuthZoneInfo> zones = new List<AuthZoneInfo>(end - start);
for (int i = start; i < end; i++)
zones.Add(_zoneIndex[i]);
return new ZonesPage(pageNumber, totalPages, totalZones, zones);
}
finally
{
_zoneIndexLock.ExitReadLock();
}
}
public void ListSubDomains(string domain, List<string> subDomains)
@@ -2018,7 +2122,7 @@ namespace DnsServerCore.Dns.ZoneManagers
}
}
public void LoadZoneFrom(Stream s)
public AuthZoneInfo LoadZoneFrom(Stream s)
{
BinaryReader bR = new BinaryReader(s);
@@ -2030,108 +2134,110 @@ namespace DnsServerCore.Dns.ZoneManagers
case 2:
{
DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
if (records.Length > 0)
if (records.Length == 0)
throw new InvalidDataException("Zone does not contain SOA record.");
DnsResourceRecord soaRecord = null;
for (int i = 0; i < records.Length; i++)
{
DnsResourceRecord soaRecord = null;
records[i] = new DnsResourceRecord(s);
for (int i = 0; i < records.Length; i++)
{
records[i] = new DnsResourceRecord(s);
if (records[i].Type == DnsResourceRecordType.SOA)
soaRecord = records[i];
}
if (soaRecord == null)
throw new InvalidDataException("Zone does not contain SOA record.");
//make zone info
AuthZoneType zoneType;
if (_dnsServer.ServerDomain.Equals((soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer, StringComparison.OrdinalIgnoreCase))
zoneType = AuthZoneType.Primary;
else
zoneType = AuthZoneType.Stub;
AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, false);
//create zone
ApexZone apexZone = CreateEmptyZone(zoneInfo);
try
{
//load records
LoadRecords(apexZone, records);
}
catch
{
DeleteZone(zoneInfo.Name);
throw;
}
//init zone
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
(apexZone as PrimaryZone).TriggerNotify();
break;
}
if (records[i].Type == DnsResourceRecordType.SOA)
soaRecord = records[i];
}
if (soaRecord == null)
throw new InvalidDataException("Zone does not contain SOA record.");
//make zone info
AuthZoneType zoneType;
if (_dnsServer.ServerDomain.Equals((soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer, StringComparison.OrdinalIgnoreCase))
zoneType = AuthZoneType.Primary;
else
zoneType = AuthZoneType.Stub;
AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, false);
//create zone
ApexZone apexZone = CreateEmptyZone(zoneInfo);
try
{
//load records
LoadRecords(apexZone, records);
}
catch
{
DeleteZone(zoneInfo.Name);
throw;
}
//init zone
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
(apexZone as PrimaryZone).TriggerNotify();
break;
}
return new AuthZoneInfo(apexZone);
}
break;
case 3:
{
bool zoneDisabled = bR.ReadBoolean();
DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
if (records.Length > 0)
if (records.Length == 0)
throw new InvalidDataException("Zone does not contain SOA record.");
DnsResourceRecord soaRecord = null;
for (int i = 0; i < records.Length; i++)
{
DnsResourceRecord soaRecord = null;
records[i] = new DnsResourceRecord(s);
records[i].Tag = new AuthRecordInfo(bR, records[i].Type == DnsResourceRecordType.SOA);
for (int i = 0; i < records.Length; i++)
{
records[i] = new DnsResourceRecord(s);
records[i].Tag = new AuthRecordInfo(bR, records[i].Type == DnsResourceRecordType.SOA);
if (records[i].Type == DnsResourceRecordType.SOA)
soaRecord = records[i];
}
if (soaRecord == null)
throw new InvalidDataException("Zone does not contain SOA record.");
//make zone info
AuthZoneType zoneType;
if (_dnsServer.ServerDomain.Equals((soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer, StringComparison.OrdinalIgnoreCase))
zoneType = AuthZoneType.Primary;
else
zoneType = AuthZoneType.Stub;
AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, zoneDisabled);
//create zone
ApexZone apexZone = CreateEmptyZone(zoneInfo);
try
{
//load records
LoadRecords(apexZone, records);
}
catch
{
DeleteZone(zoneInfo.Name);
throw;
}
//init zone
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
(apexZone as PrimaryZone).TriggerNotify();
break;
}
if (records[i].Type == DnsResourceRecordType.SOA)
soaRecord = records[i];
}
if (soaRecord == null)
throw new InvalidDataException("Zone does not contain SOA record.");
//make zone info
AuthZoneType zoneType;
if (_dnsServer.ServerDomain.Equals((soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer, StringComparison.OrdinalIgnoreCase))
zoneType = AuthZoneType.Primary;
else
zoneType = AuthZoneType.Stub;
AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, zoneDisabled);
//create zone
ApexZone apexZone = CreateEmptyZone(zoneInfo);
try
{
//load records
LoadRecords(apexZone, records);
}
catch
{
DeleteZone(zoneInfo.Name);
throw;
}
//init zone
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
(apexZone as PrimaryZone).TriggerNotify();
break;
}
return new AuthZoneInfo(apexZone);
}
break;
case 4:
{
@@ -2181,8 +2287,9 @@ namespace DnsServerCore.Dns.ZoneManagers
break;
}
}
return new AuthZoneInfo(apexZone);
}
break;
default:
throw new InvalidDataException("DNS Zone file version not supported.");
@@ -2269,8 +2376,48 @@ namespace DnsServerCore.Dns.ZoneManagers
}
public int TotalZones
{ get { return _totalZones; } }
{ get { return _zoneIndex.Count; } }
#endregion
public class ZonesPage
{
#region variables
readonly long _pageNumber;
readonly long _totalPages;
readonly long _totalZones;
readonly IReadOnlyList<AuthZoneInfo> _zones;
#endregion
#region constructor
public ZonesPage(long pageNumber, long totalPages, long totalZones, IReadOnlyList<AuthZoneInfo> zones)
{
_pageNumber = pageNumber;
_totalPages = totalPages;
_totalZones = totalZones;
_zones = zones;
}
#endregion
#region properties
public long PageNumber
{ get { return _pageNumber; } }
public long TotalPages
{ get { return _totalPages; } }
public long TotalZones
{ get { return _totalZones; } }
public IReadOnlyList<AuthZoneInfo> Zones
{ get { return _zones; } }
#endregion
}
}
}