diff --git a/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs
index 41c49092..ac191e36 100644
--- a/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs
+++ b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs
@@ -1,6 +1,6 @@
/*
Technitium DNS Server
-Copyright (C) 2021 Shreyas Zare (shreyas@technitium.com)
+Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@ 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;
@@ -28,6 +29,7 @@ using System.Threading;
using System.Threading.Tasks;
using TechnitiumLibrary.Net.Dns;
using TechnitiumLibrary.Net.Dns.ResourceRecords;
+using TechnitiumLibrary.Net.Proxy;
namespace DnsServerCore.Dns.ZoneManagers
{
@@ -39,7 +41,7 @@ namespace DnsServerCore.Dns.ZoneManagers
string _serverDomain;
- readonly ZoneTree _root = new ZoneTree();
+ readonly AuthZoneTree _root = new AuthZoneTree();
int _totalZones;
@@ -67,8 +69,8 @@ namespace DnsServerCore.Dns.ZoneManagers
if (disposing)
{
- foreach (AuthZone zone in _root)
- zone.Dispose();
+ foreach (AuthZoneNode zoneNode in _root)
+ zoneNode.Dispose();
}
_disposed = true;
@@ -106,7 +108,7 @@ namespace DnsServerCore.Dns.ZoneManagers
if (responsiblePerson.EndsWith(_serverDomain))
responsiblePerson = responsiblePerson.Replace(_serverDomain, serverDomain);
- SetRecords(record.Name, record.Type, record.TtlValue, new DnsResourceRecordData[] { new DnsSOARecord(serverDomain, responsiblePerson, soa.Serial, soa.Refresh, soa.Retry, soa.Expire, soa.Minimum) });
+ SetRecords(zone.Name, record.Name, record.Type, record.TtlValue, new DnsResourceRecordData[] { new DnsSOARecord(serverDomain, responsiblePerson, soa.Serial, soa.Refresh, soa.Retry, soa.Expire, soa.Minimum) });
//update NS records
IReadOnlyList nsResourceRecords = zone.GetRecords(DnsResourceRecordType.NS);
@@ -115,7 +117,7 @@ namespace DnsServerCore.Dns.ZoneManagers
{
if ((nsResourceRecord.RDATA as DnsNSRecord).NameServer.Equals(_serverDomain, StringComparison.OrdinalIgnoreCase))
{
- UpdateRecord(nsResourceRecord, new DnsResourceRecord(nsResourceRecord.Name, nsResourceRecord.Type, nsResourceRecord.Class, nsResourceRecord.TtlValue, new DnsNSRecord(serverDomain)) { Tag = nsResourceRecord.Tag });
+ UpdateRecord(zone.Name, nsResourceRecord, new DnsResourceRecord(nsResourceRecord.Name, nsResourceRecord.Type, nsResourceRecord.Class, nsResourceRecord.TtlValue, new DnsNSRecord(serverDomain)) { Tag = nsResourceRecord.Tag });
break;
}
}
@@ -148,9 +150,9 @@ namespace DnsServerCore.Dns.ZoneManagers
});
}
- private AuthZone CreateEmptyZone(AuthZoneInfo zoneInfo)
+ private ApexZone CreateEmptyZone(AuthZoneInfo zoneInfo)
{
- AuthZone zone;
+ ApexZone zone;
switch (zoneInfo.Type)
{
@@ -180,70 +182,46 @@ namespace DnsServerCore.Dns.ZoneManagers
return zone;
}
- if (_root.TryGet(zoneInfo.Name, out AuthZone existingZone) && (existingZone is SubDomainZone))
- {
- _root[zoneInfo.Name] = zone;
- _totalZones++;
- return zone;
- }
-
throw new DnsServerException("Zone already exists: " + zoneInfo.Name);
}
- private void LoadRecords(AuthZone authZone, IReadOnlyList records)
+ private AuthZone GetOrAddSubDomainZone(string zoneName, string domain)
{
- Dictionary>> groupedByDomainRecords = DnsResourceRecord.GroupRecords(records);
-
- foreach (KeyValuePair>> groupedByTypeRecords in groupedByDomainRecords)
+ return _root.GetOrAddSubDomainZone(zoneName, domain, delegate ()
{
- if (authZone.Name.Equals(groupedByTypeRecords.Key, StringComparison.OrdinalIgnoreCase))
- {
- foreach (KeyValuePair> groupedRecords in groupedByTypeRecords.Value)
- authZone.LoadRecords(groupedRecords.Key, groupedRecords.Value);
- }
- else
- {
- AuthZone zone = GetOrAddSubDomainZone(groupedByTypeRecords.Key);
- if (zone is SubDomainZone subDomainZone)
- {
- foreach (KeyValuePair> groupedRecords in groupedByTypeRecords.Value)
- zone.LoadRecords(groupedRecords.Key, groupedRecords.Value);
-
- subDomainZone.AutoUpdateState();
- }
- }
- }
- }
-
- private AuthZone GetOrAddSubDomainZone(string domain)
- {
- return _root.GetOrAdd(domain, delegate (string key)
- {
- _ = _root.FindZone(domain, out _, out _, out AuthZone authZone, out _);
- if (authZone == null)
+ _ = _root.FindZone(zoneName, out _, out _, out ApexZone apexZone, out _);
+ if (apexZone is null)
throw new DnsServerException("Zone was not found for domain: " + domain);
- if (authZone is PrimaryZone primaryZone)
+ if (apexZone is PrimaryZone primaryZone)
return new PrimarySubDomainZone(primaryZone, domain);
- else if (authZone is SecondaryZone secondaryZone)
+ else if (apexZone is SecondaryZone secondaryZone)
return new SecondarySubDomainZone(secondaryZone, domain);
- else if (authZone is ForwarderZone forwarderZone)
+ else if (apexZone is ForwarderZone forwarderZone)
return new ForwarderSubDomainZone(forwarderZone, domain);
throw new DnsServerException("Zone cannot have sub domains.");
});
}
- private void ResolveCNAME(DnsQuestionRecord question, DnsResourceRecord lastCNAME, List answerRecords)
+ private void ValidateZoneNameFor(string zoneName, string domain)
+ {
+ if (domain.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || domain.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase) || (zoneName.Length == 0))
+ return;
+
+ throw new DnsServerException("The domain name does not belong to the zone: " + domain);
+ }
+
+ private void ResolveCNAME(DnsQuestionRecord question, bool dnssecOk, DnsResourceRecord lastCNAME, List answerRecords)
{
int queryCount = 0;
do
{
- if (!_root.TryGet((lastCNAME.RDATA as DnsCNAMERecord).Domain, out AuthZone authZone))
+ if (!_root.TryGet((lastCNAME.RDATA as DnsCNAMERecord).Domain, out AuthZoneNode zoneNode))
break;
- IReadOnlyList records = authZone.QueryRecords(question.Type);
+ IReadOnlyList records = zoneNode.QueryRecords(question.Type, dnssecOk);
if (records.Count < 1)
break;
@@ -259,7 +237,7 @@ namespace DnsServerCore.Dns.ZoneManagers
while (++queryCount < DnsServer.MAX_CNAME_HOPS);
}
- private bool DoDNAMESubstitution(DnsQuestionRecord question, IReadOnlyList answer, out IReadOnlyList newAnswer)
+ private bool DoDNAMESubstitution(DnsQuestionRecord question, bool dnssecOk, IReadOnlyList answer, out IReadOnlyList newAnswer)
{
DnsResourceRecord dnameRR = answer[0];
@@ -269,13 +247,12 @@ namespace DnsServerCore.Dns.ZoneManagers
{
DnsResourceRecord cnameRR = new DnsResourceRecord(question.Name, DnsResourceRecordType.CNAME, question.Class, dnameRR.TtlValue, new DnsCNAMERecord(result));
- List list = new List(5)
- {
- dnameRR,
- cnameRR
- };
+ List list = new List(5);
- ResolveCNAME(question, cnameRR, list);
+ list.AddRange(answer);
+ list.Add(cnameRR);
+
+ ResolveCNAME(question, dnssecOk, cnameRR, list);
newAnswer = list;
return true;
@@ -287,7 +264,7 @@ namespace DnsServerCore.Dns.ZoneManagers
}
}
- private IReadOnlyList GetAdditionalRecords(IReadOnlyList refRecords)
+ private IReadOnlyList GetAdditionalRecords(IReadOnlyList refRecords, bool dnssecOk)
{
List additionalRecords = new List();
@@ -303,16 +280,16 @@ namespace DnsServerCore.Dns.ZoneManagers
}
else
{
- ResolveAdditionalRecords((refRecord.RDATA as DnsNSRecord).NameServer, additionalRecords);
+ ResolveAdditionalRecords((refRecord.RDATA as DnsNSRecord).NameServer, dnssecOk, additionalRecords);
}
break;
case DnsResourceRecordType.MX:
- ResolveAdditionalRecords((refRecord.RDATA as DnsMXRecord).Exchange, additionalRecords);
+ ResolveAdditionalRecords((refRecord.RDATA as DnsMXRecord).Exchange, dnssecOk, additionalRecords);
break;
case DnsResourceRecordType.SRV:
- ResolveAdditionalRecords((refRecord.RDATA as DnsSRVRecord).Target, additionalRecords);
+ ResolveAdditionalRecords((refRecord.RDATA as DnsSRVRecord).Target, dnssecOk, additionalRecords);
break;
}
}
@@ -320,50 +297,87 @@ namespace DnsServerCore.Dns.ZoneManagers
return additionalRecords;
}
- private void ResolveAdditionalRecords(string domain, List additionalRecords)
+ private void ResolveAdditionalRecords(string domain, bool dnssecOk, List additionalRecords)
{
- if (_root.TryGet(domain, out AuthZone authZone) && authZone.IsActive)
+ if (_root.TryGet(domain, out AuthZoneNode zoneNode) && zoneNode.IsActive)
{
{
- IReadOnlyList records = authZone.QueryRecords(DnsResourceRecordType.A);
+ IReadOnlyList records = zoneNode.QueryRecords(DnsResourceRecordType.A, dnssecOk);
if ((records.Count > 0) && (records[0].Type == DnsResourceRecordType.A))
additionalRecords.AddRange(records);
}
{
- IReadOnlyList records = authZone.QueryRecords(DnsResourceRecordType.AAAA);
+ IReadOnlyList records = zoneNode.QueryRecords(DnsResourceRecordType.AAAA, dnssecOk);
if ((records.Count > 0) && (records[0].Type == DnsResourceRecordType.AAAA))
additionalRecords.AddRange(records);
}
}
}
- private DnsDatagram GetReferralResponse(DnsDatagram request, AuthZone delegationZone, bool isRecursionAllowed)
+ private DnsDatagram GetReferralResponse(DnsDatagram request, bool isZoneSigned, AuthZone delegationZone, bool isRecursionAllowed)
{
IReadOnlyList authority;
if (delegationZone is StubZone)
+ {
authority = delegationZone.GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant query
+ }
else
- authority = delegationZone.QueryRecords(DnsResourceRecordType.NS);
+ {
+ authority = delegationZone.QueryRecords(DnsResourceRecordType.NS, isZoneSigned);
- IReadOnlyList additional = GetAdditionalRecords(authority);
+ if (isZoneSigned)
+ {
+ IReadOnlyList dsRecords = delegationZone.QueryRecords(DnsResourceRecordType.DS, true);
+ if (dsRecords.Count > 0)
+ {
+ List newAuthority = new List(authority.Count + dsRecords.Count);
+
+ newAuthority.AddRange(authority);
+ newAuthority.AddRange(dsRecords);
+
+ authority = newAuthority;
+ }
+ else
+ {
+ //add proof of non existence (NODATA) to prove DS record does not exists
+ authority = AddProofOfNonExistenceNoData(delegationZone, authority);
+ }
+ }
+ }
+
+ IReadOnlyList additional = GetAdditionalRecords(authority, isZoneSigned);
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NoError, request.Question, null, authority, additional);
}
+ private IReadOnlyList AddProofOfNonExistenceNoData(AuthZone zone, IReadOnlyList existingAuthority)
+ {
+ IReadOnlyList nsecRecords = zone.QueryRecords(DnsResourceRecordType.NSEC, true);
+ if (nsecRecords.Count <= 0)
+ return existingAuthority;
+
+ List newAuthority = new List(existingAuthority.Count + nsecRecords.Count);
+
+ newAuthority.AddRange(existingAuthority);
+ newAuthority.AddRange(nsecRecords);
+
+ return newAuthority;
+ }
+
private static DnsDatagram GetForwarderResponse(DnsDatagram request, AuthZone zone, AuthZone closestZone, AuthZone forwarderZone, bool isRecursionAllowed)
{
IReadOnlyList authority = null;
if (zone is not null)
- authority = zone.QueryRecords(DnsResourceRecordType.FWD);
+ authority = zone.QueryRecords(DnsResourceRecordType.FWD, false);
if (((authority is null) || (authority.Count == 0)) && (closestZone is not null))
- authority = closestZone.QueryRecords(DnsResourceRecordType.FWD);
+ authority = closestZone.QueryRecords(DnsResourceRecordType.FWD, false);
if ((authority is null) || (authority.Count == 0))
- authority = forwarderZone.QueryRecords(DnsResourceRecordType.FWD);
+ authority = forwarderZone.QueryRecords(DnsResourceRecordType.FWD, false);
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NoError, request.Question, null, authority);
}
@@ -554,6 +568,16 @@ namespace DnsServerCore.Dns.ZoneManagers
return condensedRecords;
}
+ internal static string GetParentZone(string domain)
+ {
+ int i = domain.IndexOf('.');
+ if (i > -1)
+ return domain.Substring(i + 1);
+
+ //dont return root zone
+ return null;
+ }
+
#endregion
#region public
@@ -598,8 +622,8 @@ namespace DnsServerCore.Dns.ZoneManagers
{
{
CreatePrimaryZone("localhost", _dnsServer.ServerDomain, true);
- SetRecords("localhost", DnsResourceRecordType.A, 3600, new DnsResourceRecordData[] { new DnsARecord(IPAddress.Loopback) });
- SetRecords("localhost", DnsResourceRecordType.AAAA, 3600, new DnsResourceRecordData[] { new DnsAAAARecord(IPAddress.IPv6Loopback) });
+ SetRecords("localhost", "localhost", DnsResourceRecordType.A, 3600, new DnsResourceRecordData[] { new DnsARecord(IPAddress.Loopback) });
+ SetRecords("localhost", "localhost", DnsResourceRecordType.AAAA, 3600, new DnsResourceRecordData[] { new DnsAAAARecord(IPAddress.IPv6Loopback) });
}
{
@@ -615,17 +639,17 @@ namespace DnsServerCore.Dns.ZoneManagers
}
{
- string prtDomain = "127.in-addr.arpa";
+ string ptrZoneName = "127.in-addr.arpa";
- CreatePrimaryZone(prtDomain, _dnsServer.ServerDomain, true);
- SetRecords("1.0.0.127.in-addr.arpa", DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
+ CreatePrimaryZone(ptrZoneName, _dnsServer.ServerDomain, true);
+ SetRecords(ptrZoneName, "1.0.0.127.in-addr.arpa", DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
}
{
- string prtDomain = new DnsQuestionRecord(IPAddress.IPv6Loopback, DnsClass.IN).Name;
+ string ptrZoneName = new DnsQuestionRecord(IPAddress.IPv6Loopback, DnsClass.IN).Name;
- CreatePrimaryZone(prtDomain, _dnsServer.ServerDomain, true);
- SetRecords(prtDomain, DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
+ CreatePrimaryZone(ptrZoneName, _dnsServer.ServerDomain, true);
+ SetRecords(ptrZoneName, ptrZoneName, DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
}
}
@@ -654,112 +678,80 @@ namespace DnsServerCore.Dns.ZoneManagers
}
}
- internal AuthZoneInfo CreateSpecialPrimaryZone(string domain, DnsSOARecord soaRecord, DnsNSRecord ns)
+ internal AuthZoneInfo CreateSpecialPrimaryZone(string zoneName, DnsSOARecord soaRecord, DnsNSRecord ns)
{
- AuthZone authZone = new PrimaryZone(_dnsServer, domain, soaRecord, ns);
+ PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, soaRecord, ns);
- if (_root.TryAdd(authZone))
+ if (_root.TryAdd(apexZone))
{
_totalZones++;
- return new AuthZoneInfo(authZone);
+ return new AuthZoneInfo(apexZone);
}
return null;
}
- public AuthZoneInfo CreatePrimaryZone(string domain, string primaryNameServer, bool @internal)
+ public AuthZoneInfo CreatePrimaryZone(string zoneName, string primaryNameServer, bool @internal)
{
- PrimaryZone authZone = new PrimaryZone(_dnsServer, domain, primaryNameServer, @internal);
+ PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, primaryNameServer, @internal);
- if (_root.TryAdd(authZone))
+ if (_root.TryAdd(apexZone))
{
_totalZones++;
- return new AuthZoneInfo(authZone);
- }
-
- if (_root.TryGet(domain, out AuthZone existingZone) && (existingZone is SubDomainZone))
- {
- _root[domain] = authZone;
- _totalZones++;
- return new AuthZoneInfo(authZone);
+ return new AuthZoneInfo(apexZone);
}
return null;
}
- public async Task CreateSecondaryZoneAsync(string domain, string primaryNameServerAddresses = null, DnsTransportProtocol zoneTransferProtocol = DnsTransportProtocol.Tcp, string tsigKeyName = null)
+ public async Task CreateSecondaryZoneAsync(string zoneName, string primaryNameServerAddresses = null, DnsTransportProtocol zoneTransferProtocol = DnsTransportProtocol.Tcp, string tsigKeyName = null)
{
- SecondaryZone authZone = await SecondaryZone.CreateAsync(_dnsServer, domain, primaryNameServerAddresses, zoneTransferProtocol, tsigKeyName);
+ SecondaryZone apexZone = await SecondaryZone.CreateAsync(_dnsServer, zoneName, primaryNameServerAddresses, zoneTransferProtocol, tsigKeyName);
- if (_root.TryAdd(authZone))
+ if (_root.TryAdd(apexZone))
{
- authZone.TriggerRefresh(0);
+ apexZone.TriggerRefresh(0);
_totalZones++;
- return new AuthZoneInfo(authZone);
- }
-
- if (_root.TryGet(domain, out AuthZone existingZone) && (existingZone is SubDomainZone))
- {
- _root[domain] = authZone;
- authZone.TriggerRefresh(0);
- _totalZones++;
- return new AuthZoneInfo(authZone);
+ return new AuthZoneInfo(apexZone);
}
return null;
}
- public async Task CreateStubZoneAsync(string domain, string primaryNameServerAddresses = null)
+ public async Task CreateStubZoneAsync(string zoneName, string primaryNameServerAddresses = null)
{
- StubZone authZone = await StubZone.CreateAsync(_dnsServer, domain, primaryNameServerAddresses);
+ StubZone apexZone = await StubZone.CreateAsync(_dnsServer, zoneName, primaryNameServerAddresses);
- if (_root.TryAdd(authZone))
+ if (_root.TryAdd(apexZone))
{
- authZone.TriggerRefresh(0);
+ apexZone.TriggerRefresh(0);
_totalZones++;
- return new AuthZoneInfo(authZone);
- }
-
- if (_root.TryGet(domain, out AuthZone existingZone) && (existingZone is SubDomainZone))
- {
- _root[domain] = authZone;
- authZone.TriggerRefresh(0);
- _totalZones++;
- return new AuthZoneInfo(authZone);
+ return new AuthZoneInfo(apexZone);
}
return null;
}
- public AuthZoneInfo CreateForwarderZone(string domain, DnsTransportProtocol forwarderProtocol, string forwarder)
+ public AuthZoneInfo CreateForwarderZone(string zoneName, DnsTransportProtocol forwarderProtocol, string forwarder, bool dnssecValidation, NetProxyType proxyType, string proxyAddress, ushort proxyPort, string proxyUsername, string proxyPassword)
{
- ForwarderZone authZone = new ForwarderZone(domain, forwarderProtocol, forwarder);
+ ForwarderZone apexZone = new ForwarderZone(zoneName, forwarderProtocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword);
- if (_root.TryAdd(authZone))
+ if (_root.TryAdd(apexZone))
{
_totalZones++;
- return new AuthZoneInfo(authZone);
- }
-
- if (_root.TryGet(domain, out AuthZone existingZone) && (existingZone is SubDomainZone))
- {
- _root[domain] = authZone;
- _totalZones++;
- return new AuthZoneInfo(authZone);
+ return new AuthZoneInfo(apexZone);
}
return null;
}
- public bool DeleteZone(string domain)
+ public bool DeleteZone(string zoneName)
{
- if (_root.TryRemove(domain, out AuthZone authZone))
+ if (_root.TryRemove(zoneName, out ApexZone apexZone))
{
- authZone.Dispose();
-
- if (!(authZone is SubDomainZone))
- _totalZones--;
+ apexZone.Dispose();
+ _totalZones--;
return true;
}
@@ -768,40 +760,34 @@ namespace DnsServerCore.Dns.ZoneManagers
public AuthZoneInfo GetAuthZoneInfo(string domain, bool loadHistory = false)
{
- _ = _root.FindZone(domain, out _, out _, out AuthZone authority, out _);
- if (authority == null)
+ _ = _root.FindZone(domain, out _, out _, out ApexZone apexZone, out _);
+ if (apexZone is null)
return null;
- return new AuthZoneInfo(authority, loadHistory);
+ return new AuthZoneInfo(apexZone, loadHistory);
}
- public void ListAllRecords(string domain, List records)
+ public void ListAllRecords(string zoneName, List records)
{
- foreach (AuthZone zone in _root.GetZoneWithSubDomainZones(domain))
+ foreach (AuthZone zone in _root.GetZoneWithSubDomainZones(zoneName))
zone.ListAllRecords(records);
}
- public IReadOnlyList GetRecords(string domain, DnsResourceRecordType type)
+ public IReadOnlyList GetRecords(string zoneName, string domain, DnsResourceRecordType type)
{
- if (_root.TryGet(domain, out AuthZone zone))
- return zone.GetRecords(type);
+ ValidateZoneNameFor(zoneName, domain);
+
+ if (_root.TryGet(zoneName, domain, out AuthZone authZone))
+ return authZone.GetRecords(type);
return Array.Empty();
}
- public IReadOnlyList QueryRecords(string domain, DnsResourceRecordType type)
+ public IReadOnlyList QueryZoneTransferRecords(string zoneName)
{
- if (_root.TryGet(domain, out AuthZone zone))
- return zone.QueryRecords(type);
-
- return Array.Empty();
- }
-
- public IReadOnlyList QueryZoneTransferRecords(string domain)
- {
- AuthZoneInfo authZone = GetAuthZoneInfo(domain, false);
+ AuthZoneInfo authZone = GetAuthZoneInfo(zoneName, false);
if (authZone is null)
- throw new InvalidOperationException("Zone was not found: " + domain);
+ throw new InvalidOperationException("Zone was not found: " + zoneName);
//only primary and secondary zones support zone transfer
IReadOnlyList soaRecords = authZone.GetRecords(DnsResourceRecordType.SOA);
@@ -811,7 +797,7 @@ namespace DnsServerCore.Dns.ZoneManagers
DnsResourceRecord soaRecord = soaRecords[0];
List records = new List();
- ListAllRecords(domain, records);
+ ListAllRecords(zoneName, records);
List xfrRecords = new List(records.Count + 1);
@@ -848,11 +834,11 @@ namespace DnsServerCore.Dns.ZoneManagers
return xfrRecords;
}
- public IReadOnlyList QueryIncrementalZoneTransferRecords(string domain, DnsResourceRecord clientSoaRecord)
+ public IReadOnlyList QueryIncrementalZoneTransferRecords(string zoneName, DnsResourceRecord clientSoaRecord)
{
- AuthZoneInfo authZone = GetAuthZoneInfo(domain, true);
+ AuthZoneInfo authZone = GetAuthZoneInfo(zoneName, true);
if (authZone is null)
- throw new InvalidOperationException("Zone was not found: " + domain);
+ throw new InvalidOperationException("Zone was not found: " + zoneName);
//only primary and secondary zones support zone transfer
IReadOnlyList soaRecords = authZone.GetRecords(DnsResourceRecordType.SOA);
@@ -900,7 +886,7 @@ namespace DnsServerCore.Dns.ZoneManagers
{
//client's serial was not found in zone history
//do full zone transfer
- return QueryZoneTransferRecords(domain);
+ return QueryZoneTransferRecords(zoneName);
}
List xfrRecords = new List();
@@ -916,18 +902,18 @@ namespace DnsServerCore.Dns.ZoneManagers
xfrRecords.Add(currentSoaRecord);
//condense
- return CondenseIncrementalZoneTransferRecords(domain, clientSoaRecord, xfrRecords);
+ return CondenseIncrementalZoneTransferRecords(zoneName, clientSoaRecord, xfrRecords);
}
- public void SyncZoneTransferRecords(string domain, IReadOnlyList xfrRecords)
+ public void SyncZoneTransferRecords(string zoneName, IReadOnlyList xfrRecords)
{
- if ((xfrRecords.Count < 2) || (xfrRecords[0].Type != DnsResourceRecordType.SOA) || !xfrRecords[0].Name.Equals(domain, StringComparison.OrdinalIgnoreCase) || !xfrRecords[xfrRecords.Count - 1].Equals(xfrRecords[0]))
+ if ((xfrRecords.Count < 2) || (xfrRecords[0].Type != DnsResourceRecordType.SOA) || !xfrRecords[0].Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || !xfrRecords[xfrRecords.Count - 1].Equals(xfrRecords[0]))
throw new DnsServerException("Invalid AXFR response was received.");
List latestRecords = new List(xfrRecords.Count);
List allGlueRecords = new List(4);
- if (domain.Length == 0)
+ if (zoneName.Length == 0)
{
//root zone case
for (int i = 1; i < xfrRecords.Count; i++)
@@ -957,7 +943,7 @@ namespace DnsServerCore.Dns.ZoneManagers
{
DnsResourceRecord record = xfrRecords[i];
- if (record.Name.Equals(domain, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + domain, StringComparison.OrdinalIgnoreCase))
+ if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
{
if (!latestRecords.Contains(record))
latestRecords.Add(record);
@@ -980,7 +966,7 @@ namespace DnsServerCore.Dns.ZoneManagers
//sync records
List currentRecords = new List();
- ListAllRecords(domain, currentRecords);
+ ListAllRecords(zoneName, currentRecords);
Dictionary>> currentRecordsGroupedByDomain = DnsResourceRecord.GroupRecords(currentRecords);
Dictionary>> latestRecordsGroupedByDomain = DnsResourceRecord.GroupRecords(latestRecords);
@@ -989,34 +975,34 @@ namespace DnsServerCore.Dns.ZoneManagers
foreach (KeyValuePair>> currentDomain in currentRecordsGroupedByDomain)
{
if (!latestRecordsGroupedByDomain.ContainsKey(currentDomain.Key))
- _root.TryRemove(currentDomain.Key, out _);
+ _root.TryRemove(currentDomain.Key, out SubDomainZone _);
}
//sync new records
foreach (KeyValuePair>> latestEntries in latestRecordsGroupedByDomain)
{
- AuthZone zone = GetOrAddSubDomainZone(latestEntries.Key);
+ AuthZone zone = GetOrAddSubDomainZone(zoneName, latestEntries.Key);
- if (zone.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ if (zone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
zone.SyncRecords(latestEntries.Value);
- else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
zone.SyncRecords(latestEntries.Value);
}
}
- public IReadOnlyList SyncIncrementalZoneTransferRecords(string domain, IReadOnlyList xfrRecords)
+ public IReadOnlyList SyncIncrementalZoneTransferRecords(string zoneName, IReadOnlyList xfrRecords)
{
- if ((xfrRecords.Count < 2) || (xfrRecords[0].Type != DnsResourceRecordType.SOA) || !xfrRecords[0].Name.Equals(domain, StringComparison.OrdinalIgnoreCase) || !xfrRecords[xfrRecords.Count - 1].Equals(xfrRecords[0]))
+ if ((xfrRecords.Count < 2) || (xfrRecords[0].Type != DnsResourceRecordType.SOA) || !xfrRecords[0].Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || !xfrRecords[xfrRecords.Count - 1].Equals(xfrRecords[0]))
throw new DnsServerException("Invalid IXFR/AXFR response was received.");
if ((xfrRecords.Count < 4) || (xfrRecords[1].Type != DnsResourceRecordType.SOA))
{
//received AXFR response
- SyncZoneTransferRecords(domain, xfrRecords);
+ SyncZoneTransferRecords(zoneName, xfrRecords);
return Array.Empty();
}
- IReadOnlyList soaRecords = GetRecords(domain, DnsResourceRecordType.SOA);
+ IReadOnlyList soaRecords = GetRecords(zoneName, zoneName, DnsResourceRecordType.SOA);
if (soaRecords.Count != 1)
throw new InvalidOperationException("No authoritative zone was found for the domain.");
@@ -1024,7 +1010,7 @@ namespace DnsServerCore.Dns.ZoneManagers
DnsResourceRecord currentSoaRecord = soaRecords[0];
DnsSOARecord currentSoa = currentSoaRecord.RDATA as DnsSOARecord;
- IReadOnlyList condensedXfrRecords = CondenseIncrementalZoneTransferRecords(domain, currentSoaRecord, xfrRecords);
+ IReadOnlyList condensedXfrRecords = CondenseIncrementalZoneTransferRecords(zoneName, currentSoaRecord, xfrRecords);
List deletedRecords = new List();
List deletedGlueRecords = new List();
@@ -1039,7 +1025,7 @@ namespace DnsServerCore.Dns.ZoneManagers
{
//read deleted records
DnsResourceRecord deletedSoaRecord = condensedXfrRecords[index];
- if ((deletedSoaRecord.Type != DnsResourceRecordType.SOA) || !deletedSoaRecord.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ if ((deletedSoaRecord.Type != DnsResourceRecordType.SOA) || !deletedSoaRecord.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException();
index++;
@@ -1050,7 +1036,7 @@ namespace DnsServerCore.Dns.ZoneManagers
if (record.Type == DnsResourceRecordType.SOA)
break;
- if (domain.Length == 0)
+ if (zoneName.Length == 0)
{
//root zone case
switch (record.Type)
@@ -1067,7 +1053,7 @@ namespace DnsServerCore.Dns.ZoneManagers
}
else
{
- if (record.Name.Equals(domain, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + domain, StringComparison.OrdinalIgnoreCase))
+ if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
{
deletedRecords.Add(record);
}
@@ -1088,7 +1074,7 @@ namespace DnsServerCore.Dns.ZoneManagers
//read added records
DnsResourceRecord addedSoaRecord = condensedXfrRecords[index];
- if (!addedSoaRecord.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ if (!addedSoaRecord.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException();
index++;
@@ -1099,7 +1085,7 @@ namespace DnsServerCore.Dns.ZoneManagers
if (record.Type == DnsResourceRecordType.SOA)
break;
- if (domain.Length == 0)
+ if (zoneName.Length == 0)
{
//root zone case
switch (record.Type)
@@ -1116,7 +1102,7 @@ namespace DnsServerCore.Dns.ZoneManagers
}
else
{
- if (record.Name.Equals(domain, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + domain, StringComparison.OrdinalIgnoreCase))
+ if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
{
addedRecords.Add(record);
}
@@ -1146,11 +1132,11 @@ namespace DnsServerCore.Dns.ZoneManagers
{
foreach (KeyValuePair>> deletedEntry in DnsResourceRecord.GroupRecords(deletedRecords))
{
- AuthZone zone = GetOrAddSubDomainZone(deletedEntry.Key);
+ AuthZone zone = GetOrAddSubDomainZone(zoneName, deletedEntry.Key);
- if (zone.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ if (zone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
zone.SyncRecords(deletedEntry.Value, null);
- else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
zone.SyncRecords(deletedEntry.Value, null);
}
}
@@ -1159,23 +1145,23 @@ namespace DnsServerCore.Dns.ZoneManagers
{
foreach (KeyValuePair>> addedEntry in DnsResourceRecord.GroupRecords(addedRecords))
{
- AuthZone zone = GetOrAddSubDomainZone(addedEntry.Key);
+ AuthZone zone = GetOrAddSubDomainZone(zoneName, addedEntry.Key);
- if (zone.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ if (zone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
zone.SyncRecords(null, addedEntry.Value);
- else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
+ else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
zone.SyncRecords(null, addedEntry.Value);
}
}
if ((deletedGlueRecords.Count > 0) || (addedGlueRecords.Count > 0))
{
- foreach (AuthZone zone in _root.GetZoneWithSubDomainZones(domain))
+ foreach (AuthZone zone in _root.GetZoneWithSubDomainZones(zoneName))
zone.SyncGlueRecords(deletedGlueRecords, addedGlueRecords);
}
{
- AuthZone zone = GetOrAddSubDomainZone(domain);
+ AuthZone zone = GetOrAddSubDomainZone(zoneName, zoneName);
addedSoaRecord.CopyRecordInfoFrom(currentSoaRecord);
@@ -1200,71 +1186,148 @@ namespace DnsServerCore.Dns.ZoneManagers
return historyRecords;
}
- public void LoadRecords(IReadOnlyCollection records)
+ internal void ImportRecords(string zoneName, IReadOnlyList records)
{
+ _ = _root.FindZone(zoneName, out _, out _, out ApexZone apexZone, out _);
+ if ((apexZone is null) || !apexZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
+ throw new DnsServerException("No such zone was found: " + zoneName);
+
+ if ((apexZone is not PrimaryZone) && (apexZone is not ForwarderZone))
+ throw new DnsServerException("Zone must be a primary or forwarder type: " + zoneName);
+
foreach (KeyValuePair>> zoneEntry in DnsResourceRecord.GroupRecords(records))
{
- AuthZone zone = GetOrAddSubDomainZone(zoneEntry.Key);
+ if (zoneName.Equals(zoneEntry.Key, StringComparison.OrdinalIgnoreCase))
+ {
+ foreach (KeyValuePair> rrsetEntry in zoneEntry.Value)
+ {
+ if (rrsetEntry.Key == DnsResourceRecordType.RRSIG)
+ {
+ //RRSIG records in response are not complete RRSet
+ foreach (DnsResourceRecord record in rrsetEntry.Value)
+ apexZone.AddRecord(record);
+ }
+ else
+ {
+ apexZone.SetRecords(rrsetEntry.Key, rrsetEntry.Value);
+ }
+ }
+ }
+ else
+ {
+ ValidateZoneNameFor(zoneName, zoneEntry.Key);
- foreach (KeyValuePair> rrsetEntry in zoneEntry.Value)
- zone.LoadRecords(rrsetEntry.Key, rrsetEntry.Value);
+ AuthZone authZone = GetOrAddSubDomainZone(zoneName, zoneEntry.Key);
+
+ foreach (KeyValuePair> rrsetEntry in zoneEntry.Value)
+ {
+ if (rrsetEntry.Key == DnsResourceRecordType.RRSIG)
+ {
+ //RRSIG records in response are not complete RRSet
+ foreach (DnsResourceRecord record in rrsetEntry.Value)
+ authZone.AddRecord(record);
+ }
+ else
+ {
+ authZone.SetRecords(rrsetEntry.Key, rrsetEntry.Value);
+ }
+ }
+
+ if (authZone is SubDomainZone subDomainZone)
+ subDomainZone.AutoUpdateState();
+ }
}
}
- public void SetRecords(string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData[] records)
+ internal void LoadRecords(ApexZone apexZone, IReadOnlyList records)
{
+ foreach (KeyValuePair>> zoneEntry in DnsResourceRecord.GroupRecords(records))
+ {
+ if (apexZone.Name.Equals(zoneEntry.Key, StringComparison.OrdinalIgnoreCase))
+ {
+ foreach (KeyValuePair> rrsetEntry in zoneEntry.Value)
+ apexZone.LoadRecords(rrsetEntry.Key, rrsetEntry.Value);
+ }
+ else
+ {
+ ValidateZoneNameFor(apexZone.Name, zoneEntry.Key);
+
+ AuthZone authZone = GetOrAddSubDomainZone(apexZone.Name, zoneEntry.Key);
+
+ foreach (KeyValuePair> rrsetEntry in zoneEntry.Value)
+ authZone.LoadRecords(rrsetEntry.Key, rrsetEntry.Value);
+
+ if (authZone is SubDomainZone subDomainZone)
+ subDomainZone.AutoUpdateState();
+ }
+ }
+ }
+
+ public void SetRecords(string zoneName, string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData[] records)
+ {
+ ValidateZoneNameFor(zoneName, domain);
+
DnsResourceRecord[] resourceRecords = new DnsResourceRecord[records.Length];
for (int i = 0; i < records.Length; i++)
resourceRecords[i] = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, records[i]);
- AuthZone zone = GetOrAddSubDomainZone(domain);
+ AuthZone authZone = GetOrAddSubDomainZone(zoneName, domain);
- zone.SetRecords(type, resourceRecords);
+ authZone.SetRecords(type, resourceRecords);
- if (zone is SubDomainZone subDomainZone)
+ if (authZone is SubDomainZone subDomainZone)
subDomainZone.AutoUpdateState();
}
- public void SetRecord(DnsResourceRecord record)
+ public void SetRecord(string zoneName, DnsResourceRecord record)
{
- AuthZone zone = GetOrAddSubDomainZone(record.Name);
+ ValidateZoneNameFor(zoneName, record.Name);
- zone.SetRecords(record.Type, new DnsResourceRecord[] { record });
+ AuthZone authZone = GetOrAddSubDomainZone(zoneName, record.Name);
- if (zone is SubDomainZone subDomainZone)
+ authZone.SetRecords(record.Type, new DnsResourceRecord[] { record });
+
+ if (authZone is SubDomainZone subDomainZone)
subDomainZone.AutoUpdateState();
}
- public void AddRecord(string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData record)
+ public void AddRecord(string zoneName, string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData record)
{
- AuthZone zone = GetOrAddSubDomainZone(domain);
+ ValidateZoneNameFor(zoneName, domain);
- zone.AddRecord(new DnsResourceRecord(zone.Name, type, DnsClass.IN, ttl, record));
+ AuthZone authZone = GetOrAddSubDomainZone(zoneName, domain);
- if (zone is SubDomainZone subDomainZone)
+ authZone.AddRecord(new DnsResourceRecord(authZone.Name, type, DnsClass.IN, ttl, record));
+
+ if (authZone is SubDomainZone subDomainZone)
subDomainZone.AutoUpdateState();
}
- public void AddRecord(DnsResourceRecord record)
+ public void AddRecord(string zoneName, DnsResourceRecord record)
{
- AuthZone zone = GetOrAddSubDomainZone(record.Name);
+ ValidateZoneNameFor(zoneName, record.Name);
- zone.AddRecord(record);
+ AuthZone authZone = GetOrAddSubDomainZone(zoneName, record.Name);
- if (zone is SubDomainZone subDomainZone)
+ authZone.AddRecord(record);
+
+ if (authZone is SubDomainZone subDomainZone)
subDomainZone.AutoUpdateState();
}
- public void UpdateRecord(DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
+ public void UpdateRecord(string zoneName, DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
{
+ ValidateZoneNameFor(zoneName, oldRecord.Name);
+ ValidateZoneNameFor(zoneName, newRecord.Name);
+
if (oldRecord.Type != newRecord.Type)
throw new DnsServerException("Cannot update record: new record must be of same type.");
if (oldRecord.Type == DnsResourceRecordType.SOA)
throw new DnsServerException("Cannot update record: use SetRecords() for updating SOA record.");
- if (!_root.TryGet(oldRecord.Name, out AuthZone zone))
+ if (!_root.TryGet(zoneName, oldRecord.Name, out AuthZone authZone))
throw new DnsServerException("Cannot update record: zone does not exists.");
switch (oldRecord.Type)
@@ -1275,24 +1338,24 @@ namespace DnsServerCore.Dns.ZoneManagers
case DnsResourceRecordType.APP:
if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
{
- zone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
+ authZone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
- if (zone is SubDomainZone subDomainZone)
+ if (authZone is SubDomainZone subDomainZone)
subDomainZone.AutoUpdateState();
}
else
{
- zone.DeleteRecords(oldRecord.Type);
+ authZone.DeleteRecords(oldRecord.Type);
- if (zone is SubDomainZone subDomainZone)
+ if (authZone is SubDomainZone subDomainZone)
{
- if (zone.IsEmpty)
- _root.TryRemove(oldRecord.Name, out _); //remove empty sub zone
+ if (authZone.IsEmpty)
+ _root.TryRemove(oldRecord.Name, out SubDomainZone _); //remove empty sub zone
else
subDomainZone.AutoUpdateState();
}
- AuthZone newZone = GetOrAddSubDomainZone(newRecord.Name);
+ AuthZone newZone = GetOrAddSubDomainZone(zoneName, newRecord.Name);
newZone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
@@ -1304,24 +1367,24 @@ namespace DnsServerCore.Dns.ZoneManagers
default:
if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
{
- zone.UpdateRecord(oldRecord, newRecord);
+ authZone.UpdateRecord(oldRecord, newRecord);
- if (zone is SubDomainZone subDomainZone)
+ if (authZone is SubDomainZone subDomainZone)
subDomainZone.AutoUpdateState();
}
else
{
- zone.DeleteRecord(oldRecord.Type, oldRecord.RDATA);
+ authZone.DeleteRecord(oldRecord.Type, oldRecord.RDATA);
- if (zone is SubDomainZone subDomainZone)
+ if (authZone is SubDomainZone subDomainZone)
{
- if (zone.IsEmpty)
- _root.TryRemove(oldRecord.Name, out _); //remove empty sub zone
+ if (authZone.IsEmpty)
+ _root.TryRemove(oldRecord.Name, out SubDomainZone _); //remove empty sub zone
else
subDomainZone.AutoUpdateState();
}
- AuthZone newZone = GetOrAddSubDomainZone(newRecord.Name);
+ AuthZone newZone = GetOrAddSubDomainZone(zoneName, newRecord.Name);
newZone.AddRecord(newRecord);
@@ -1332,32 +1395,36 @@ namespace DnsServerCore.Dns.ZoneManagers
}
}
- public void DeleteRecord(string domain, DnsResourceRecordType type, DnsResourceRecordData record)
+ public void DeleteRecord(string zoneName, string domain, DnsResourceRecordType type, DnsResourceRecordData record)
{
- if (_root.TryGet(domain, out AuthZone zone))
- {
- zone.DeleteRecord(type, record);
+ ValidateZoneNameFor(zoneName, domain);
- if (zone is SubDomainZone subDomainZone)
+ if (_root.TryGet(zoneName, domain, out AuthZone authZone))
+ {
+ authZone.DeleteRecord(type, record);
+
+ if (authZone is SubDomainZone subDomainZone)
{
- if (zone.IsEmpty)
- _root.TryRemove(domain, out _); //remove empty sub zone
+ if (authZone.IsEmpty)
+ _root.TryRemove(domain, out SubDomainZone _); //remove empty sub zone
else
subDomainZone.AutoUpdateState();
}
}
}
- public void DeleteRecords(string domain, DnsResourceRecordType type)
+ public void DeleteRecords(string zoneName, string domain, DnsResourceRecordType type)
{
- if (_root.TryGet(domain, out AuthZone zone))
- {
- zone.DeleteRecords(type);
+ ValidateZoneNameFor(zoneName, domain);
- if (zone is SubDomainZone subDomainZone)
+ if (_root.TryGet(zoneName, domain, out AuthZone authZone))
+ {
+ authZone.DeleteRecords(type);
+
+ if (authZone is SubDomainZone subDomainZone)
{
- if (zone.IsEmpty)
- _root.TryRemove(domain, out _); //remove empty sub zone
+ if (authZone.IsEmpty)
+ _root.TryRemove(domain, out SubDomainZone _); //remove empty sub zone
else
subDomainZone.AutoUpdateState();
}
@@ -1368,9 +1435,13 @@ namespace DnsServerCore.Dns.ZoneManagers
{
List zones = new List();
- foreach (AuthZone zone in _root)
+ foreach (AuthZoneNode zoneNode in _root)
{
- AuthZoneInfo zoneInfo = new AuthZoneInfo(zone);
+ ApexZone apexZone = zoneNode.ApexZone;
+ if (apexZone is null)
+ continue;
+
+ AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
@@ -1394,17 +1465,12 @@ namespace DnsServerCore.Dns.ZoneManagers
public DnsDatagram QueryClosestDelegation(DnsDatagram request)
{
- _ = _root.FindZone(request.Question[0].Name, out _, out AuthZone delegation, out _, out _);
- if (delegation != null)
+ _ = _root.FindZone(request.Question[0].Name, out _, out SubDomainZone delegation, out ApexZone apexZone, out _);
+ if (delegation is not null)
{
- //return closest name servers in delegation
- IReadOnlyList closestAuthority = delegation.QueryRecords(DnsResourceRecordType.NS);
- if ((closestAuthority.Count > 0) && (closestAuthority[0].Type == DnsResourceRecordType.NS))
- {
- IReadOnlyList additional = GetAdditionalRecords(closestAuthority);
+ bool isZoneSigned = request.DnssecOk && apexZone.GetRecords(DnsResourceRecordType.DNSKEY).Count > 0;
- return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, closestAuthority, additional);
- }
+ return GetReferralResponse(request, isZoneSigned, delegation, true);
}
//no delegation found
@@ -1415,21 +1481,25 @@ namespace DnsServerCore.Dns.ZoneManagers
{
DnsQuestionRecord question = request.Question[0];
- AuthZone zone = _root.FindZone(question.Name, out AuthZone closest, out AuthZone delegation, out AuthZone authZone, out bool hasSubDomains);
+ AuthZone zone = _root.FindZone(question.Name, out SubDomainZone closest, out SubDomainZone delegation, out ApexZone apexZone, out bool hasSubDomains);
- if ((authZone == null) || !authZone.IsActive) //no authority for requested zone
- return null;
+ if ((apexZone is null) || !apexZone.IsActive)
+ return null; //no authority for requested zone
- if ((delegation != null) && delegation.IsActive)
- return GetReferralResponse(request, delegation, isRecursionAllowed);
+ bool isZoneSigned = request.DnssecOk && (apexZone.GetRecords(DnsResourceRecordType.DNSKEY).Count > 0);
+ bool isZoneNSEC3 = isZoneSigned && (apexZone.GetRecords(DnsResourceRecordType.NSEC3PARAM).Count > 0);
- if ((zone == null) || !zone.IsActive)
+ if ((zone is null) || !zone.IsActive)
{
- //zone not found
- if (authZone is StubZone)
- return GetReferralResponse(request, authZone, isRecursionAllowed);
- else if (authZone is ForwarderZone)
- return GetForwarderResponse(request, null, closest, authZone, isRecursionAllowed);
+ //zone not found
+ if ((delegation is not null) && delegation.IsActive && (delegation.Name.Length > apexZone.Name.Length))
+ return GetReferralResponse(request, isZoneSigned, delegation, isRecursionAllowed);
+
+ if (apexZone is StubZone)
+ return GetReferralResponse(request, false, apexZone, isRecursionAllowed);
+
+ if (apexZone is ForwarderZone)
+ return GetForwarderResponse(request, null, closest, apexZone, isRecursionAllowed);
DnsResponseCode rCode = DnsResponseCode.NoError;
IReadOnlyList answer = null;
@@ -1437,37 +1507,58 @@ namespace DnsServerCore.Dns.ZoneManagers
if (closest is not null)
{
- answer = closest.QueryRecords(DnsResourceRecordType.DNAME);
+ answer = closest.QueryRecords(DnsResourceRecordType.DNAME, isZoneSigned);
if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME))
{
- if (!DoDNAMESubstitution(question, answer, out answer))
+ if (!DoDNAMESubstitution(question, isZoneSigned, answer, out answer))
rCode = DnsResponseCode.YXDomain;
}
else
{
answer = null;
- authority = closest.QueryRecords(DnsResourceRecordType.APP);
+ authority = closest.QueryRecords(DnsResourceRecordType.APP, false);
}
}
if (((answer is null) || (answer.Count == 0)) && ((authority is null) || (authority.Count == 0)))
{
- answer = authZone.QueryRecords(DnsResourceRecordType.DNAME);
+ answer = apexZone.QueryRecords(DnsResourceRecordType.DNAME, isZoneSigned);
if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME))
{
- if (!DoDNAMESubstitution(question, answer, out answer))
+ if (!DoDNAMESubstitution(question, isZoneSigned, answer, out answer))
rCode = DnsResponseCode.YXDomain;
}
else
{
answer = null;
- authority = authZone.QueryRecords(DnsResourceRecordType.APP);
+ authority = apexZone.QueryRecords(DnsResourceRecordType.APP, false);
if (authority.Count == 0)
{
if (!hasSubDomains)
rCode = DnsResponseCode.NxDomain;
- authority = authZone.GetRecords(DnsResourceRecordType.SOA);
+ authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, isZoneSigned);
+
+ if (isZoneSigned)
+ {
+ //add proof of non existence (NXDOMAIN/NODATA) to prove the qname does not exists
+ IReadOnlyList nsecRecords;
+
+ if (isZoneNSEC3)
+ nsecRecords = _root.FindNSEC3ProofOfNonExistenceNxDomain(question.Name, question.Type, false);
+ else
+ nsecRecords = _root.FindNSECProofOfNonExistenceNxDomain(question.Name, question.Type, false);
+
+ if (nsecRecords.Count > 0)
+ {
+ List newAuthority = new List(authority.Count + nsecRecords.Count);
+
+ newAuthority.AddRange(authority);
+ newAuthority.AddRange(nsecRecords);
+
+ authority = newAuthority;
+ }
+ }
}
}
}
@@ -1477,29 +1568,66 @@ namespace DnsServerCore.Dns.ZoneManagers
else
{
//zone found
- IReadOnlyList authority;
+ if ((question.Type == DnsResourceRecordType.DS) && (zone is ApexZone))
+ {
+ if (delegation is null || !delegation.IsActive || (delegation.Name.Length > apexZone.Name.Length))
+ return null; //no authoritative parent side delegation zone available to answer for DS
+
+ zone = delegation; //switch zone to parent side sub domain delegation zone for DS record
+ }
+
+ IReadOnlyList authority = null;
IReadOnlyList additional;
- IReadOnlyList answers = zone.QueryRecords(question.Type);
+ IReadOnlyList answers = zone.QueryRecords(question.Type, isZoneSigned);
if (answers.Count == 0)
{
//record type not found
- if (authZone is StubZone)
- return GetReferralResponse(request, authZone, isRecursionAllowed);
- else if (authZone is ForwarderZone)
- return GetForwarderResponse(request, zone, closest, authZone, isRecursionAllowed);
+ if (question.Type == DnsResourceRecordType.DS)
+ {
+ //check for correct auth zone
+ if (apexZone.Name.Equals(question.Name, StringComparison.OrdinalIgnoreCase))
+ {
+ //current auth zone is child side; find parent side auth zone for DS
+ string parentZone = GetParentZone(question.Name);
+ if (parentZone is null)
+ parentZone = string.Empty;
- authority = zone.QueryRecords(DnsResourceRecordType.APP);
+ _ = _root.FindZone(parentZone, out _, out _, out apexZone, out _);
+
+ if ((apexZone is null) || !apexZone.IsActive)
+ return null; //no authority for requested zone
+ }
+ }
+ else
+ {
+ //check for delegation, stub & forwarder
+ if ((delegation is not null) && delegation.IsActive && (delegation.Name.Length > apexZone.Name.Length))
+ return GetReferralResponse(request, isZoneSigned, delegation, isRecursionAllowed);
+
+ if (apexZone is StubZone)
+ return GetReferralResponse(request, false, apexZone, isRecursionAllowed);
+
+ if (apexZone is ForwarderZone)
+ return GetForwarderResponse(request, zone, closest, apexZone, isRecursionAllowed);
+ }
+
+ authority = zone.QueryRecords(DnsResourceRecordType.APP, false);
if (authority.Count == 0)
{
if (closest is not null)
- authority = closest.QueryRecords(DnsResourceRecordType.APP);
+ authority = closest.QueryRecords(DnsResourceRecordType.APP, false);
if (authority.Count == 0)
{
- authority = authZone.QueryRecords(DnsResourceRecordType.APP);
+ authority = apexZone.QueryRecords(DnsResourceRecordType.APP, false);
if (authority.Count == 0)
- authority = authZone.GetRecords(DnsResourceRecordType.SOA);
+ {
+ authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, isZoneSigned);
+
+ if (isZoneSigned)
+ authority = AddProofOfNonExistenceNoData(zone, authority); //add proof of non existence (NODATA) to prove that no such type or record exists
+ }
}
}
@@ -1517,14 +1645,29 @@ namespace DnsServerCore.Dns.ZoneManagers
wildcardAnswers[i] = new DnsResourceRecord(question.Name, answers[i].Type, answers[i].Class, answers[i].TtlValue, answers[i].RDATA) { Tag = answers[i].Tag };
answers = wildcardAnswers;
+
+ //add proof of non existence (WILDCARD) to prove that the wildcard expansion was legit and the qname actually does not exists
+ if (isZoneSigned)
+ {
+ IReadOnlyList nsecRecords;
+
+ if (isZoneNSEC3)
+ nsecRecords = _root.FindNSEC3ProofOfNonExistenceNxDomain(question.Name, question.Type, true);
+ else
+ nsecRecords = _root.FindNSECProofOfNonExistenceNxDomain(question.Name, question.Type, true);
+
+ if (nsecRecords.Count > 0)
+ authority = nsecRecords;
+ }
}
DnsResourceRecord lastRR = answers[answers.Count - 1];
if ((lastRR.Type != question.Type) && (lastRR.Type == DnsResourceRecordType.CNAME) && (question.Type != DnsResourceRecordType.ANY))
{
- List newAnswers = new List(answers);
+ List newAnswers = new List(answers.Count + 1);
+ newAnswers.AddRange(answers);
- ResolveCNAME(question, lastRR, newAnswers);
+ ResolveCNAME(question, isZoneSigned, lastRR, newAnswers);
answers = newAnswers;
}
@@ -1534,12 +1677,10 @@ namespace DnsServerCore.Dns.ZoneManagers
case DnsResourceRecordType.NS:
case DnsResourceRecordType.MX:
case DnsResourceRecordType.SRV:
- authority = null;
- additional = GetAdditionalRecords(answers);
+ additional = GetAdditionalRecords(answers, isZoneSigned);
break;
default:
- authority = null;
additional = null;
break;
}
@@ -1586,12 +1727,12 @@ namespace DnsServerCore.Dns.ZoneManagers
AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, false);
//create zone
- AuthZone authZone = CreateEmptyZone(zoneInfo);
+ ApexZone apexZone = CreateEmptyZone(zoneInfo);
try
{
//load records
- LoadRecords(authZone, records);
+ LoadRecords(apexZone, records);
}
catch
{
@@ -1603,7 +1744,7 @@ namespace DnsServerCore.Dns.ZoneManagers
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
- (authZone as PrimaryZone).TriggerNotify();
+ (apexZone as PrimaryZone).TriggerNotify();
break;
}
}
@@ -1640,12 +1781,12 @@ namespace DnsServerCore.Dns.ZoneManagers
AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, zoneDisabled);
//create zone
- AuthZone authZone = CreateEmptyZone(zoneInfo);
+ ApexZone apexZone = CreateEmptyZone(zoneInfo);
try
{
//load records
- LoadRecords(authZone, records);
+ LoadRecords(apexZone, records);
}
catch
{
@@ -1657,7 +1798,7 @@ namespace DnsServerCore.Dns.ZoneManagers
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
- (authZone as PrimaryZone).TriggerNotify();
+ (apexZone as PrimaryZone).TriggerNotify();
break;
}
}
@@ -1670,7 +1811,7 @@ namespace DnsServerCore.Dns.ZoneManagers
AuthZoneInfo zoneInfo = new AuthZoneInfo(bR);
//create zone
- AuthZone authZone = CreateEmptyZone(zoneInfo);
+ ApexZone apexZone = CreateEmptyZone(zoneInfo);
//read all zone records
DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
@@ -1685,7 +1826,7 @@ namespace DnsServerCore.Dns.ZoneManagers
try
{
//load records
- LoadRecords(authZone, records);
+ LoadRecords(apexZone, records);
}
catch
{
@@ -1697,18 +1838,18 @@ namespace DnsServerCore.Dns.ZoneManagers
switch (zoneInfo.Type)
{
case AuthZoneType.Primary:
- (authZone as PrimaryZone).TriggerNotify();
+ (apexZone as PrimaryZone).TriggerNotify();
break;
case AuthZoneType.Secondary:
- SecondaryZone secondary = authZone as SecondaryZone;
+ SecondaryZone secondary = apexZone as SecondaryZone;
secondary.TriggerNotify();
secondary.TriggerRefresh();
break;
case AuthZoneType.Stub:
- (authZone as StubZone).TriggerRefresh();
+ (apexZone as StubZone).TriggerRefresh();
break;
}
}
@@ -1720,11 +1861,11 @@ namespace DnsServerCore.Dns.ZoneManagers
}
}
- public void WriteZoneTo(string domain, Stream s)
+ public void WriteZoneTo(string zoneName, Stream s)
{
- AuthZoneInfo zoneInfo = GetAuthZoneInfo(domain, true);
+ AuthZoneInfo zoneInfo = GetAuthZoneInfo(zoneName, true);
if (zoneInfo is null)
- throw new InvalidOperationException("Zone was not found: " + domain);
+ throw new InvalidOperationException("Zone was not found: " + zoneName);
//serialize zone
BinaryWriter bW = new BinaryWriter(s);
@@ -1740,7 +1881,7 @@ namespace DnsServerCore.Dns.ZoneManagers
//write all zone records
List records = new List();
- ListAllRecords(domain, records);
+ ListAllRecords(zoneName, records);
bW.Write(records.Count);
@@ -1755,19 +1896,19 @@ namespace DnsServerCore.Dns.ZoneManagers
}
}
- public void SaveZoneFile(string domain)
+ public void SaveZoneFile(string zoneName)
{
- domain = domain.ToLower();
+ zoneName = zoneName.ToLower();
using (MemoryStream mS = new MemoryStream())
{
//serialize zone
- WriteZoneTo(domain, mS);
+ WriteZoneTo(zoneName, mS);
//write to zone file
mS.Position = 0;
- using (FileStream fS = new FileStream(Path.Combine(_dnsServer.ConfigFolder, "zones", domain + ".zone"), FileMode.Create, FileAccess.Write))
+ using (FileStream fS = new FileStream(Path.Combine(_dnsServer.ConfigFolder, "zones", zoneName + ".zone"), FileMode.Create, FileAccess.Write))
{
mS.CopyTo(fS);
}
@@ -1775,18 +1916,18 @@ namespace DnsServerCore.Dns.ZoneManagers
LogManager log = _dnsServer.LogManager;
if (log != null)
- log.Write("Saved zone file for domain: " + (domain == "" ? "" : domain));
+ log.Write("Saved zone file for domain: " + (zoneName == "" ? "" : zoneName));
}
- public void DeleteZoneFile(string domain)
+ public void DeleteZoneFile(string zoneName)
{
- domain = domain.ToLower();
+ zoneName = zoneName.ToLower();
- File.Delete(Path.Combine(_dnsServer.ConfigFolder, "zones", domain + ".zone"));
+ File.Delete(Path.Combine(_dnsServer.ConfigFolder, "zones", zoneName + ".zone"));
LogManager log = _dnsServer.LogManager;
if (log != null)
- log.Write("Deleted zone file for domain: " + domain);
+ log.Write("Deleted zone file for domain: " + zoneName);
}
#endregion