123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002 |
- /*
- Technitium DNS Server
- 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
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- using DnsServerCore.Dns.Zones;
- using System;
- using System.Collections.Generic;
- using System.Threading;
- using TechnitiumLibrary.Net.Dns.ResourceRecords;
- namespace DnsServerCore.Dns.Trees
- {
- class AuthZoneTree : ZoneTree<AuthZoneNode, SubDomainZone, ApexZone>
- {
- #region private
- private static Node GetPreviousSubDomainZoneNode(byte[] key, Node currentNode, int baseDepth)
- {
- int k;
- NodeValue currentValue = currentNode.Value;
- if (currentValue is null)
- {
- //key value does not exists
- if (currentNode.Children is null)
- {
- //no children available; move to previous sibling
- k = currentNode.K - 1; //find previous node from sibling starting at k - 1
- currentNode = currentNode.Parent;
- }
- else
- {
- if (key.Length == currentNode.Depth)
- {
- //current node belongs to the key
- k = currentNode.K - 1; //find previous node from sibling starting at k - 1
- currentNode = currentNode.Parent;
- }
- else
- {
- //find the previous node for the given k in current node's children
- k = key[currentNode.Depth];
- }
- }
- }
- else
- {
- int x = DnsNSECRecordData.CanonicalComparison(currentValue.Key, key);
- if (x == 0)
- {
- //current node value matches the key
- k = currentNode.K - 1; //find previous node from sibling starting at k - 1
- currentNode = currentNode.Parent;
- }
- else if (x > 0)
- {
- //current node value is larger for the key
- k = currentNode.K - 1; //find previous node from sibling starting at k - 1
- currentNode = currentNode.Parent;
- }
- else
- {
- //current node value is smaller for the key
- if (currentNode.Children is null)
- {
- //the current node is previous node since no children exists and value is smaller for the key
- return currentNode;
- }
- else
- {
- //find the previous node for the given k in current node's children
- k = key[currentNode.Depth];
- }
- }
- }
- //start reverse tree traversal
- while ((currentNode is not null) && (currentNode.Depth >= baseDepth))
- {
- Node[] children = currentNode.Children;
- if (children is not null)
- {
- //find previous child node
- Node child = null;
- for (int i = k; i > -1; i--)
- {
- child = Volatile.Read(ref children[i]);
- if (child is not null)
- {
- bool childNodeHasApexZone = false;
- NodeValue childValue = child.Value;
- if (childValue is not null)
- {
- AuthZoneNode authZoneNode = childValue.Value;
- if (authZoneNode is not null)
- {
- if (authZoneNode.ApexZone is not null)
- childNodeHasApexZone = true; //must stop checking children of the apex of the sub zone
- }
- }
- if (!childNodeHasApexZone && child.Children is not null)
- break; //child has further children so check them first
- if (childValue is not null)
- {
- AuthZoneNode authZoneNode = childValue.Value;
- if (authZoneNode is not null)
- {
- if (authZoneNode.ParentSideZone is not null)
- {
- //is sub domain zone
- return child; //child has value so return it
- }
- if (authZoneNode.ApexZone is not null)
- {
- //is apex zone
- //skip to next child to avoid listing this auth zone's sub domains
- child = null; //set null to avoid child being set as current after the loop
- }
- }
- }
- }
- }
- if (child is not null)
- {
- //make found child as current
- k = children.Length - 1;
- currentNode = child;
- continue; //start over
- }
- }
- //no child node available; check for current node value
- {
- NodeValue value = currentNode.Value;
- if (value is not null)
- {
- AuthZoneNode authZoneNode = value.Value;
- if (authZoneNode is not null)
- {
- if ((authZoneNode.ApexZone is not null) && (currentNode.Depth == baseDepth))
- {
- //current node contains apex zone for the base depth i.e. current zone; return it
- return currentNode;
- }
- if (authZoneNode.ParentSideZone is not null)
- {
- //current node contains sub domain zone; return it
- return currentNode;
- }
- }
- }
- }
- //move up to parent node for previous sibling
- k = currentNode.K - 1;
- currentNode = currentNode.Parent;
- }
- return null;
- }
- private static Node GetNextSubDomainZoneNode(byte[] key, Node currentNode, int baseDepth)
- {
- int k;
- NodeValue currentValue = currentNode.Value;
- if (currentValue is null)
- {
- //key value does not exists
- if (currentNode.Children is null)
- {
- //no children available; move to next sibling
- k = currentNode.K + 1; //find next node from sibling starting at k + 1
- currentNode = currentNode.Parent;
- }
- else
- {
- if (key.Length == currentNode.Depth)
- {
- //current node belongs to the key
- k = 0; //find next node from first child of current node
- }
- else
- {
- //find next node for the given k in current node's children
- k = key[currentNode.Depth];
- }
- }
- }
- else
- {
- //check if node contains apex zone
- bool foundApexZone = false;
- if (currentNode.Depth > baseDepth)
- {
- AuthZoneNode authZoneNode = currentValue.Value;
- if (authZoneNode is not null)
- {
- ApexZone apexZone = authZoneNode.ApexZone;
- if (apexZone is not null)
- foundApexZone = true;
- }
- }
- if (foundApexZone)
- {
- //current contains apex for a sub zone; move up to parent node
- k = currentNode.K + 1; //find next node from sibling starting at k + 1
- currentNode = currentNode.Parent;
- }
- else
- {
- int x = DnsNSECRecordData.CanonicalComparison(currentValue.Key, key);
- if (x == 0)
- {
- //current node value matches the key
- k = 0; //find next node from children starting at k
- }
- else if (x > 0)
- {
- //current node value is larger for the key thus current is the next node
- return currentNode;
- }
- else
- {
- //current node value is smaller for the key
- k = key[currentNode.Depth]; //find next node from children starting at k = key[depth]
- }
- }
- }
- //start tree traversal
- while ((currentNode is not null) && (currentNode.Depth >= baseDepth))
- {
- Node[] children = currentNode.Children;
- if (children is not null)
- {
- //find next child node
- Node child = null;
- for (int i = k; i < children.Length; i++)
- {
- child = Volatile.Read(ref children[i]);
- if (child is not null)
- {
- NodeValue childValue = child.Value;
- if (childValue is not null)
- {
- AuthZoneNode authZoneNode = childValue.Value;
- if (authZoneNode is not null)
- {
- if (authZoneNode.ParentSideZone is not null)
- {
- //is sub domain zone
- return child; //child has value so return it
- }
- if (authZoneNode.ApexZone is not null)
- {
- //is apex zone
- //skip to next child to avoid listing this auth zone's sub domains
- child = null; //set null to avoid child being set as current after the loop
- continue;
- }
- }
- }
- if (child.Children is not null)
- break;
- }
- }
- if (child is not null)
- {
- //make found child as current
- k = 0;
- currentNode = child;
- continue; //start over
- }
- }
- //no child nodes available; move up to parent node
- k = currentNode.K + 1;
- currentNode = currentNode.Parent;
- }
- return null;
- }
- private static bool SubDomainExists(byte[] key, Node currentNode)
- {
- Node nextSubDomain = GetNextSubDomainZoneNode(key, currentNode, currentNode.Depth);
- if (nextSubDomain is null)
- return false;
- NodeValue value = nextSubDomain.Value;
- if (value is null)
- return false;
- return IsKeySubDomain(key, value.Key, false);
- }
- private static AuthZone GetAuthZoneFromNode(Node node, string zoneName)
- {
- NodeValue value = node.Value;
- if (value is not null)
- {
- AuthZoneNode authZoneNode = value.Value;
- if (authZoneNode is not null)
- return authZoneNode.GetAuthZone(zoneName);
- }
- return null;
- }
- #endregion
- #region protected
- protected override void GetClosestValuesForZone(AuthZoneNode zoneValue, out SubDomainZone closestSubDomain, out SubDomainZone closestDelegation, out ApexZone closestAuthority)
- {
- ApexZone apexZone = zoneValue.ApexZone;
- if (apexZone is not null)
- {
- //hosted primary/secondary/stub/forwarder zone found
- closestSubDomain = null;
- closestDelegation = zoneValue.ParentSideZone;
- closestAuthority = apexZone;
- }
- else
- {
- //hosted sub domain
- SubDomainZone subDomainZone = zoneValue.ParentSideZone;
- if (subDomainZone.ContainsNameServerRecords())
- {
- //delegated sub domain found
- closestSubDomain = null;
- closestDelegation = subDomainZone;
- }
- else
- {
- closestSubDomain = subDomainZone;
- closestDelegation = null;
- }
- closestAuthority = null;
- }
- }
- #endregion
- #region public
- public bool TryAdd(ApexZone zone)
- {
- AuthZoneNode zoneNode = GetOrAdd(zone.Name, delegate (string key)
- {
- return new AuthZoneNode(null, zone);
- });
- if (ReferenceEquals(zoneNode.ApexZone, zone))
- return true; //added successfully
- return zoneNode.TryAdd(zone);
- }
- public bool TryGet(string zoneName, string domain, out AuthZone authZone)
- {
- if (TryGet(domain, out AuthZoneNode authZoneNode))
- {
- authZone = authZoneNode.GetAuthZone(zoneName);
- return authZone is not null;
- }
- authZone = null;
- return false;
- }
- public bool TryGet(string zoneName, out ApexZone apexZone)
- {
- if (TryGet(zoneName, out AuthZoneNode authZoneNode) && (authZoneNode.ApexZone is not null))
- {
- apexZone = authZoneNode.ApexZone;
- return true;
- }
- apexZone = null;
- return false;
- }
- public bool TryRemove(string domain, out ApexZone apexZone)
- {
- if (!TryGet(domain, out AuthZoneNode authZoneNode, out Node currentNode) || (authZoneNode.ApexZone is null))
- {
- apexZone = null;
- return false;
- }
- apexZone = authZoneNode.ApexZone;
- if (authZoneNode.ParentSideZone is null)
- {
- //remove complete zone node
- if (!base.TryRemove(domain, out AuthZoneNode _))
- {
- apexZone = null;
- return false;
- }
- }
- else
- {
- //parent side sub domain exists; remove only apex zone from zone node
- if (!authZoneNode.TryRemove(out ApexZone _))
- {
- apexZone = null;
- return false;
- }
- }
- //remove all sub domains under current zone
- Node current = currentNode;
- byte[] currentKey = ConvertToByteKey(domain);
- do
- {
- current = GetNextSubDomainZoneNode(currentKey, current, currentNode.Depth);
- if (current is null)
- break;
- NodeValue v = current.Value;
- if (v is not null)
- {
- AuthZoneNode z = v.Value;
- if (z is not null)
- {
- if (z.ApexZone is null)
- {
- //no apex zone at this node; remove complete zone node
- current.RemoveNodeValue(v.Key, out _); //remove node value
- current.CleanThisBranch();
- }
- else
- {
- //apex node exists; remove parent size sub domain
- z.TryRemove(out SubDomainZone _);
- }
- }
- currentKey = v.Key;
- }
- }
- while (true);
- currentNode.CleanThisBranch();
- return true;
- }
- public bool TryRemove(string domain, out SubDomainZone subDomainZone)
- {
- if (!TryGet(domain, out AuthZoneNode zoneNode, out Node currentNode) || (zoneNode.ParentSideZone is null))
- {
- subDomainZone = null;
- return false;
- }
- subDomainZone = zoneNode.ParentSideZone;
- if (zoneNode.ApexZone is null)
- {
- //remove complete zone node
- if (!base.TryRemove(domain, out AuthZoneNode _))
- {
- subDomainZone = null;
- return false;
- }
- }
- else
- {
- //apex zone exists; remove only parent side sub domain from zone node
- if (!zoneNode.TryRemove(out SubDomainZone _))
- {
- subDomainZone = null;
- return false;
- }
- }
- currentNode.CleanThisBranch();
- return true;
- }
- public override bool TryRemove(string key, out AuthZoneNode authZoneNode)
- {
- throw new InvalidOperationException();
- }
- public IReadOnlyList<AuthZone> GetZoneWithSubDomainZones(string zoneName)
- {
- List<AuthZone> zones = new List<AuthZone>();
- byte[] key = ConvertToByteKey(zoneName);
- NodeValue nodeValue = _root.FindNodeValue(key, out Node currentNode);
- if (nodeValue is not null)
- {
- AuthZoneNode authZoneNode = nodeValue.Value;
- if (authZoneNode is not null)
- {
- ApexZone apexZone = authZoneNode.ApexZone;
- if (apexZone is not null)
- {
- zones.Add(apexZone);
- Node current = currentNode;
- byte[] currentKey = key;
- do
- {
- current = GetNextSubDomainZoneNode(currentKey, current, currentNode.Depth);
- if (current is null)
- break;
- NodeValue value = current.Value;
- if (value is not null)
- {
- authZoneNode = value.Value;
- if (authZoneNode is not null)
- zones.Add(authZoneNode.ParentSideZone);
- currentKey = value.Key;
- }
- }
- while (true);
- }
- }
- }
- return zones;
- }
- public AuthZone GetOrAddSubDomainZone(string zoneName, string domain, Func<SubDomainZone> valueFactory)
- {
- bool isApex = zoneName.Equals(domain, StringComparison.OrdinalIgnoreCase);
- AuthZoneNode authZoneNode = GetOrAdd(domain, delegate (string key)
- {
- if (isApex)
- throw new DnsServerException("Zone was not found for domain: " + key);
- return new AuthZoneNode(valueFactory(), null);
- });
- if (isApex)
- {
- if (authZoneNode.ApexZone is null)
- throw new DnsServerException("Zone was not found: " + zoneName);
- return authZoneNode.ApexZone;
- }
- else
- {
- return authZoneNode.GetOrAddParentSideZone(valueFactory);
- }
- }
- public AuthZone GetAuthZone(string zoneName, string domain)
- {
- if (TryGet(domain, out AuthZoneNode authZoneNode))
- return authZoneNode.GetAuthZone(zoneName);
- return null;
- }
- public AuthZone FindZone(string domain, out SubDomainZone closest, out SubDomainZone delegation, out ApexZone authority, out bool hasSubDomains)
- {
- byte[] key = ConvertToByteKey(domain);
- AuthZoneNode authZoneNode = FindZoneNode(key, true, out Node currentNode, out Node closestSubDomainNode, out _, out SubDomainZone closestSubDomain, out SubDomainZone closestDelegation, out ApexZone closestAuthority);
- if (authZoneNode is null)
- {
- //zone not found
- closest = closestSubDomain;
- delegation = closestDelegation;
- authority = closestAuthority;
- if (authority is null)
- {
- //no authority so no sub domains
- hasSubDomains = false;
- }
- else if ((closestSubDomainNode is not null) && !closestSubDomainNode.HasChildren)
- {
- //closest sub domain node does not have any children so no sub domains
- hasSubDomains = false;
- }
- else
- {
- //check if current node has sub domains
- hasSubDomains = SubDomainExists(key, currentNode);
- }
- return null;
- }
- else
- {
- //zone found
- AuthZone zone;
- ApexZone apexZone = authZoneNode.ApexZone;
- if (apexZone is not null)
- {
- zone = apexZone;
- closest = null;
- delegation = authZoneNode.ParentSideZone;
- authority = apexZone;
- }
- else
- {
- SubDomainZone subDomainZone = authZoneNode.ParentSideZone;
- zone = subDomainZone;
- closest = closestSubDomain;
- if (closestDelegation is not null)
- delegation = closestDelegation;
- else if (subDomainZone.ContainsNameServerRecords())
- delegation = subDomainZone;
- else
- delegation = null;
- authority = closestAuthority;
- }
- hasSubDomains = false; //since zone is found, it does not matter if subdomain exists or not
- return zone;
- }
- }
- public AuthZone FindPreviousSubDomainZone(string zoneName, string domain)
- {
- byte[] key = ConvertToByteKey(domain);
- AuthZoneNode authZoneNode = FindZoneNode(key, false, out Node currentNode, out _, out Node closestAuthorityNode, out _, out _, out _);
- if (authZoneNode is not null)
- {
- //zone exists
- ApexZone apexZone = authZoneNode.ApexZone;
- SubDomainZone parentSideZone = authZoneNode.ParentSideZone;
- if ((apexZone is not null) && (parentSideZone is not null))
- {
- //found ambiguity between apex zone and sub domain zone
- if (!apexZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
- {
- //zone name does not match with apex zone and thus not match with closest authority node
- //find the closest authority zone for given zone name
- if (!TryGet(zoneName, out _, out Node closestNodeForZoneName))
- throw new InvalidOperationException();
- closestAuthorityNode = closestNodeForZoneName;
- }
- }
- }
- Node previousNode = GetPreviousSubDomainZoneNode(key, currentNode, closestAuthorityNode.Depth);
- if (previousNode is not null)
- {
- AuthZone authZone = GetAuthZoneFromNode(previousNode, zoneName);
- if (authZone is not null)
- return authZone;
- }
- return null;
- }
- public AuthZone FindNextSubDomainZone(string zoneName, string domain)
- {
- byte[] key = ConvertToByteKey(domain);
- AuthZoneNode authZoneNode = FindZoneNode(key, false, out Node currentNode, out _, out Node closestAuthorityNode, out _, out _, out _);
- if (authZoneNode is not null)
- {
- //zone exists
- ApexZone apexZone = authZoneNode.ApexZone;
- SubDomainZone parentSideZone = authZoneNode.ParentSideZone;
- if ((apexZone is not null) && (parentSideZone is not null))
- {
- //found ambiguity between apex zone and sub domain zone
- if (!apexZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
- {
- //zone name does not match with apex zone and thus not match with closest authority node
- //find the closest authority zone for given zone name
- if (!TryGet(zoneName, out _, out Node closestNodeForZoneName))
- throw new InvalidOperationException();
- closestAuthorityNode = closestNodeForZoneName;
- }
- }
- }
- Node nextNode = GetNextSubDomainZoneNode(key, currentNode, closestAuthorityNode.Depth);
- if (nextNode is not null)
- {
- AuthZone authZone = GetAuthZoneFromNode(nextNode, zoneName);
- if (authZone is not null)
- return authZone;
- }
- return null;
- }
- public bool SubDomainExists(string zoneName, string domain)
- {
- AuthZone nextAuthZone = FindNextSubDomainZone(zoneName, domain);
- if (nextAuthZone is null)
- return false;
- return nextAuthZone.Name.EndsWith("." + domain);
- }
- public IReadOnlyList<DnsResourceRecord> FindNSecProofOfNonExistenceNxDomain(string domain, bool isWildcardAnswer)
- {
- List<DnsResourceRecord> nsecRecords = new List<DnsResourceRecord>(2 * 2);
- void AddProofOfCoverFor(string domain)
- {
- byte[] key = ConvertToByteKey(domain);
- AuthZoneNode authZoneNode = FindZoneNode(key, false, out Node currentNode, out _, out Node closestAuthorityNode, out _, out _, out ApexZone closestAuthority);
- if (authZoneNode is not null)
- throw new InvalidOperationException(); //domain exists! cannot prove non-existence
- Node previousNode = GetPreviousSubDomainZoneNode(key, currentNode, closestAuthorityNode.Depth);
- if (previousNode is not null)
- {
- AuthZone authZone = GetAuthZoneFromNode(previousNode, closestAuthority.Name);
- if (authZone is not null)
- {
- IReadOnlyList<DnsResourceRecord> proofOfCoverRecords = authZone.QueryRecords(DnsResourceRecordType.NSEC, true);
- foreach (DnsResourceRecord proofOfCoverRecord in proofOfCoverRecords)
- {
- if (!nsecRecords.Contains(proofOfCoverRecord))
- nsecRecords.Add(proofOfCoverRecord);
- }
- }
- }
- }
- //add proof of cover for domain
- AddProofOfCoverFor(domain);
- if (isWildcardAnswer)
- return nsecRecords;
- //add proof of cover for wildcard
- if (nsecRecords.Count > 0)
- {
- //add wildcard proof to prove that a wildcard expansion was not possible
- DnsResourceRecord nsecRecord = nsecRecords[0];
- DnsNSECRecordData nsec = nsecRecord.RDATA as DnsNSECRecordData;
- string wildcardName = DnsNSECRecordData.GetWildcardFor(nsecRecord, domain);
- if (!DnsNSECRecordData.IsDomainCovered(nsecRecord.Name, nsec.NextDomainName, wildcardName))
- AddProofOfCoverFor(wildcardName);
- }
- return nsecRecords;
- }
- public IReadOnlyList<DnsResourceRecord> FindNSec3ProofOfNonExistenceNxDomain(string domain, bool isWildcardAnswer)
- {
- List<DnsResourceRecord> nsec3Records = new List<DnsResourceRecord>(3 * 2);
- void AddProofOfCoverFor(string hashedOwnerName, string zoneName)
- {
- //find previous NSEC3 for the hashed owner name
- IReadOnlyList<DnsResourceRecord> proofOfCoverRecords = null;
- string currentOwnerName = hashedOwnerName;
- while (true)
- {
- AuthZone previousNSec3Zone = FindPreviousSubDomainZone(zoneName, currentOwnerName);
- if (previousNSec3Zone is null)
- break;
- IReadOnlyList<DnsResourceRecord> previousNSec3Records = previousNSec3Zone.QueryRecords(DnsResourceRecordType.NSEC3, true);
- if (previousNSec3Records.Count > 0)
- {
- proofOfCoverRecords = previousNSec3Records;
- break;
- }
- currentOwnerName = previousNSec3Zone.Name;
- }
- if (proofOfCoverRecords is null)
- {
- //didnt find previous NSEC3; find the last NSEC3
- currentOwnerName = hashedOwnerName;
- while (true)
- {
- AuthZone nextNSec3Zone = GetAuthZone(zoneName, currentOwnerName);
- if (nextNSec3Zone is null)
- {
- nextNSec3Zone = FindNextSubDomainZone(zoneName, currentOwnerName);
- if (nextNSec3Zone is null)
- break;
- }
- IReadOnlyList<DnsResourceRecord> nextNSec3Records = nextNSec3Zone.QueryRecords(DnsResourceRecordType.NSEC3, true);
- if (nextNSec3Records.Count > 0)
- {
- proofOfCoverRecords = nextNSec3Records;
- DnsResourceRecord previousNSec3Record = nextNSec3Records[0];
- string nextHashedOwnerNameString = (previousNSec3Record.RDATA as DnsNSEC3RecordData).NextHashedOwnerName + (zoneName.Length > 0 ? "." + zoneName : "");
- if (DnsNSECRecordData.CanonicalComparison(previousNSec3Record.Name, nextHashedOwnerNameString) >= 0)
- break; //found last NSEC3
- //jump to next hashed owner
- currentOwnerName = nextHashedOwnerNameString;
- }
- else
- {
- currentOwnerName = nextNSec3Zone.Name;
- }
- }
- }
- if (proofOfCoverRecords is null)
- throw new InvalidOperationException();
- foreach (DnsResourceRecord proofOfCoverRecord in proofOfCoverRecords)
- {
- if (!nsec3Records.Contains(proofOfCoverRecord))
- nsec3Records.Add(proofOfCoverRecord);
- }
- }
- byte[] key = ConvertToByteKey(domain);
- string closestEncloser;
- AuthZoneNode authZoneNode = FindZoneNode(key, isWildcardAnswer, out _, out _, out _, out SubDomainZone closestSubDomain, out _, out ApexZone closestAuthority);
- if (authZoneNode is not null)
- {
- if (isWildcardAnswer && (closestSubDomain is not null) && closestSubDomain.Name.Contains('*'))
- {
- closestEncloser = closestSubDomain.Name.TrimStart(new char[] { '*', '.' });
- }
- else
- {
- //subdomain that contains only NSEC3 record does not really exists: RFC5155 section 7.2.8
- if ((authZoneNode.ApexZone is not null) || ((authZoneNode.ParentSideZone is not null) && !authZoneNode.ParentSideZone.HasOnlyNSec3Records()))
- throw new InvalidOperationException(); //domain exists! cannot prove non-existence
- //continue to prove non-existence of this nsec3 owner name
- closestEncloser = closestAuthority.Name;
- }
- }
- else
- {
- if (closestSubDomain is not null)
- closestEncloser = closestSubDomain.Name;
- else if (closestAuthority is not null)
- closestEncloser = closestAuthority.Name;
- else
- throw new InvalidOperationException(); //cannot find closest encloser
- }
- IReadOnlyList<DnsResourceRecord> nsec3ParamRecords = closestAuthority.GetRecords(DnsResourceRecordType.NSEC3PARAM);
- if (nsec3ParamRecords.Count == 0)
- throw new InvalidOperationException("Zone does not have NSEC3 deployed.");
- DnsNSEC3PARAMRecordData nsec3Param = nsec3ParamRecords[0].RDATA as DnsNSEC3PARAMRecordData;
- //find correct closest encloser
- string hashedNextCloserName;
- while (true)
- {
- string nextCloserName = DnsNSEC3RecordData.GetNextCloserName(domain, closestEncloser);
- hashedNextCloserName = nsec3Param.ComputeHashedOwnerNameBase32HexString(nextCloserName) + (closestAuthority.Name.Length > 0 ? "." + closestAuthority.Name : "");
- AuthZone nsec3Zone = GetAuthZone(closestAuthority.Name, hashedNextCloserName);
- if (nsec3Zone is null)
- break; //next closer name does not exists
- //next closer name exists as an ENT
- closestEncloser = nextCloserName;
- if (domain.Equals(closestEncloser, StringComparison.OrdinalIgnoreCase))
- {
- //domain exists as an ENT; return no data proof
- return FindNSec3ProofOfNonExistenceNoData(nsec3Zone);
- }
- }
- if (isWildcardAnswer)
- {
- //add proof of cover for the domain to prove non-existence (wildcard)
- AddProofOfCoverFor(hashedNextCloserName, closestAuthority.Name);
- }
- else
- {
- //add closest encloser proof
- string hashedClosestEncloser = nsec3Param.ComputeHashedOwnerNameBase32HexString(closestEncloser) + (closestAuthority.Name.Length > 0 ? "." + closestAuthority.Name : "");
- AuthZone nsec3Zone = GetAuthZone(closestAuthority.Name, hashedClosestEncloser);
- if (nsec3Zone is null)
- throw new InvalidOperationException();
- IReadOnlyList<DnsResourceRecord> closestEncloserProofRecords = nsec3Zone.QueryRecords(DnsResourceRecordType.NSEC3, true);
- if (closestEncloserProofRecords.Count == 0)
- throw new InvalidOperationException();
- nsec3Records.AddRange(closestEncloserProofRecords);
- DnsResourceRecord closestEncloserProofRecord = closestEncloserProofRecords[0];
- DnsNSEC3RecordData closestEncloserProof = closestEncloserProofRecord.RDATA as DnsNSEC3RecordData;
- //add proof of cover for the next closer name
- if (!DnsNSECRecordData.IsDomainCovered(closestEncloserProofRecord.Name, closestEncloserProof.NextHashedOwnerName + (closestAuthority.Name.Length > 0 ? "." + closestAuthority.Name : ""), hashedNextCloserName))
- AddProofOfCoverFor(hashedNextCloserName, closestAuthority.Name);
- //add proof of cover to prove that a wildcard expansion was not possible
- string wildcardDomain = closestEncloser.Length > 0 ? "*." + closestEncloser : "*";
- string hashedWildcardDomainName = nsec3Param.ComputeHashedOwnerNameBase32HexString(wildcardDomain) + (closestAuthority.Name.Length > 0 ? "." + closestAuthority.Name : "");
- if (!DnsNSECRecordData.IsDomainCovered(closestEncloserProofRecord.Name, closestEncloserProof.NextHashedOwnerName + (closestAuthority.Name.Length > 0 ? "." + closestAuthority.Name : ""), hashedWildcardDomainName))
- AddProofOfCoverFor(hashedWildcardDomainName, closestAuthority.Name);
- }
- return nsec3Records;
- }
- public IReadOnlyList<DnsResourceRecord> FindNSecProofOfNonExistenceNoData(AuthZone zone)
- {
- IReadOnlyList<DnsResourceRecord> nsecRecords = zone.QueryRecords(DnsResourceRecordType.NSEC, true);
- if (nsecRecords.Count == 0)
- throw new InvalidOperationException("Zone does not have NSEC deployed correctly.");
- return nsecRecords;
- }
- public IReadOnlyList<DnsResourceRecord> FindNSec3ProofOfNonExistenceNoData(AuthZone zone, ApexZone apexZone)
- {
- IReadOnlyList<DnsResourceRecord> nsec3ParamRecords = apexZone.GetRecords(DnsResourceRecordType.NSEC3PARAM);
- if (nsec3ParamRecords.Count == 0)
- throw new InvalidOperationException("Zone does not have NSEC3 deployed.");
- DnsNSEC3PARAMRecordData nsec3Param = nsec3ParamRecords[0].RDATA as DnsNSEC3PARAMRecordData;
- string hashedOwnerName = nsec3Param.ComputeHashedOwnerNameBase32HexString(zone.Name) + (apexZone.Name.Length > 0 ? "." + apexZone.Name : "");
- AuthZone nsec3Zone = GetAuthZone(apexZone.Name, hashedOwnerName);
- if (nsec3Zone is null)
- {
- //this is probably since the domain in request is for an nsec3 record owner name
- return FindNSec3ProofOfNonExistenceNxDomain(zone.Name, false);
- }
- return FindNSec3ProofOfNonExistenceNoData(nsec3Zone);
- }
- public IReadOnlyList<DnsResourceRecord> FindNSec3ProofOfNonExistenceNoData(AuthZone nsec3Zone)
- {
- IReadOnlyList<DnsResourceRecord> nsec3Records = nsec3Zone.QueryRecords(DnsResourceRecordType.NSEC3, true);
- if (nsec3Records.Count > 0)
- return nsec3Records;
- return Array.Empty<DnsResourceRecord>();
- }
- #endregion
- }
- }
|