From 5eddfbdda10c956bf64f02813ee5bd8e378752e9 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sat, 27 Feb 2021 19:35:17 +0530 Subject: [PATCH] DnsServer: implemented app zone querying and execution logic. Added qname minimization setting. Removed feature to specify initial name servers for recursive resolution and instead providing closest name servers via ResolverDnsCache based on auth zone and cache zone. --- DnsServerCore/Dns/DnsServer.cs | 160 ++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 63 deletions(-) diff --git a/DnsServerCore/Dns/DnsServer.cs b/DnsServerCore/Dns/DnsServer.cs index 7faf5d7c..f60cb4d3 100644 --- a/DnsServerCore/Dns/DnsServer.cs +++ b/DnsServerCore/Dns/DnsServer.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Dns.Applications; using DnsServerCore.Dns.ZoneManagers; using DnsServerCore.Dns.Zones; using Newtonsoft.Json; @@ -83,6 +84,7 @@ namespace DnsServerCore.Dns readonly BlockedZoneManager _blockedZoneManager; readonly BlockListZoneManager _blockListZoneManager; readonly CacheZoneManager _cacheZoneManager = new CacheZoneManager(); + readonly DnsApplicationManager _dnsApplicationManager; readonly ResolverDnsCache _dnsCache; @@ -95,6 +97,7 @@ namespace DnsServerCore.Dns IReadOnlyList _forwarders; bool _preferIPv6; bool _randomizeName; + bool _qnameMinimization; int _forwarderRetries = 3; int _resolverRetries = 5; int _forwarderTimeout = 4000; @@ -177,6 +180,7 @@ namespace DnsServerCore.Dns _allowedZoneManager = new AllowedZoneManager(this); _blockedZoneManager = new BlockedZoneManager(this); _blockListZoneManager = new BlockListZoneManager(this); + _dnsApplicationManager = new DnsApplicationManager(_configFolder); _dnsCache = new ResolverDnsCache(_authZoneManager, _cacheZoneManager); @@ -202,6 +206,9 @@ namespace DnsServerCore.Dns if (_authZoneManager != null) _authZoneManager.Dispose(); + if (_dnsApplicationManager != null) + _dnsApplicationManager.Dispose(); + if (_stats != null) _stats.Dispose(); } @@ -914,13 +921,13 @@ namespace DnsServerCore.Dns } //query authoritative zone - response = await ProcessAuthoritativeQueryAsync(request, inAllowedZone, isRecursionAllowed); + response = await ProcessAuthoritativeQueryAsync(request, remoteEP, inAllowedZone, isRecursionAllowed, protocol); if ((response.RCODE != DnsResponseCode.Refused) || !request.RecursionDesired || !isRecursionAllowed) return response; //do recursive query - return await ProcessRecursiveQueryAsync(request, null, null, !inAllowedZone, false); + return await ProcessRecursiveQueryAsync(request, null, !inAllowedZone, false); } } catch (InvalidDomainNameException) @@ -1022,7 +1029,7 @@ namespace DnsServerCore.Dns return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, axfrRecords) { Tag = StatsResponseType.Authoritative }; } - private Task ProcessAuthoritativeQueryAsync(DnsDatagram request, bool inAllowedZone, bool isRecursionAllowed) + private async Task ProcessAuthoritativeQueryAsync(DnsDatagram request, IPEndPoint remoteEP, bool inAllowedZone, bool isRecursionAllowed, DnsTransportProtocol protocol) { DnsDatagram response = _authZoneManager.Query(request); response.Tag = StatsResponseType.Authoritative; @@ -1039,10 +1046,10 @@ namespace DnsServerCore.Dns switch (lastRR.Type) { case DnsResourceRecordType.CNAME: - return ProcessCNAMEAsync(request, response, isRecursionAllowed, false); + return await ProcessCNAMEAsync(request, response, isRecursionAllowed, false); case DnsResourceRecordType.ANAME: - return ProcessANAMEAsync(request, response, isRecursionAllowed); + return await ProcessANAMEAsync(request, response, isRecursionAllowed); } } } @@ -1053,10 +1060,8 @@ namespace DnsServerCore.Dns case DnsResourceRecordType.NS: if (request.RecursionDesired && isRecursionAllowed) { - //do recursive resolution using response authority name servers - List nameServers = NameServerAddress.GetNameServersFromResponse(response, _preferIPv6, false); - - return ProcessRecursiveQueryAsync(request, nameServers, null, !inAllowedZone, false); + //do recursive resolution; name servers will be provided via ResolverDnsCache + return await ProcessRecursiveQueryAsync(request, null, !inAllowedZone, false); } break; @@ -1065,7 +1070,7 @@ namespace DnsServerCore.Dns if ((response.Authority.Count == 1) && (response.Authority[0].RDATA as DnsForwarderRecord).Forwarder.Equals("this-server", StringComparison.OrdinalIgnoreCase)) { //do conditional forwarding via "this-server" - return ProcessRecursiveQueryAsync(request, null, null, !inAllowedZone, false); + return await ProcessRecursiveQueryAsync(request, null, !inAllowedZone, false); } else { @@ -1083,8 +1088,49 @@ namespace DnsServerCore.Dns } } - return ProcessRecursiveQueryAsync(request, null, forwarders, !inAllowedZone, false); + return await ProcessRecursiveQueryAsync(request, forwarders, !inAllowedZone, false); } + + case DnsResourceRecordType.APP: + if (response.Authority.Count > 0) + { + //do application query + DnsResourceRecord appResourceRecord = response.Authority[0]; + DnsApplicationRecord appRecord = appResourceRecord.RDATA as DnsApplicationRecord; + + if (_dnsApplicationManager.Packages.TryGetValue(appRecord.Package, out DnsApplicationPackage package)) + { + if (package.DnsApplications.TryGetValue(appRecord.ClassPath, out IDnsApplication dnsApplication)) + { + AuthZoneInfo zoneInfo = _authZoneManager.GetAuthZoneInfo(appResourceRecord.Name); + IDnsServer dnsServer = new DnsServerInternal(this, package.PackagePath); + + DnsDatagram appResponse = await dnsApplication.ProcessQueryAsync(request, remoteEP, zoneInfo.Name, appRecord.Data, isRecursionAllowed, dnsServer); + if (appResponse != null) + { + if (appResponse.AuthoritativeAnswer) + appResponse.Tag = StatsResponseType.Authoritative; + + return appResponse; //return app response + } + } + else + { + LogManager log = _log; + if (log != null) + log.Write(remoteEP, protocol, "DNS application '" + appRecord.ClassPath + "' was not found in the package: " + appRecord.Package); + } + } + else + { + LogManager log = _log; + if (log != null) + log.Write(remoteEP, protocol, "DNS application package was not found: " + appRecord.Package); + } + } + + //return refused response + return new DnsDatagram(request.Identifier, true, request.OPCODE, false, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.Refused, request.Question); } } } @@ -1092,7 +1138,7 @@ namespace DnsServerCore.Dns if (response.RecursionAvailable != isRecursionAllowed) response = new DnsDatagram(response.Identifier, response.IsResponse, response.OPCODE, response.AuthoritativeAnswer, response.Truncation, response.RecursionDesired, isRecursionAllowed, response.AuthenticData, response.CheckingDisabled, response.RCODE, response.Question, response.Answer, response.Authority, response.Additional); - return Task.FromResult(response); + return response; } private async Task ProcessCNAMEAsync(DnsDatagram request, DnsDatagram response, bool isRecursionAllowed, bool cacheRefreshOperation) @@ -1118,7 +1164,7 @@ namespace DnsServerCore.Dns if (newRequest.RecursionDesired && isRecursionAllowed) { //do recursion - lastResponse = await RecursiveResolveAsync(newRequest, null, null, false, cacheRefreshOperation); + lastResponse = await RecursiveResolveAsync(newRequest, null, false, cacheRefreshOperation); isAuthoritativeAnswer = false; } else @@ -1139,10 +1185,8 @@ namespace DnsServerCore.Dns case DnsResourceRecordType.NS: if (newRequest.RecursionDesired && isRecursionAllowed) { - //do recursive resolution using last response authority name servers - List nameServers = NameServerAddress.GetNameServersFromResponse(lastResponse, _preferIPv6, false); - - lastResponse = await RecursiveResolveAsync(newRequest, nameServers, null, false, false); + //do recursive resolution; name servers will be provided via ResolveDnsCache + lastResponse = await RecursiveResolveAsync(newRequest, null, false, false); isAuthoritativeAnswer = false; } @@ -1152,7 +1196,7 @@ namespace DnsServerCore.Dns if ((lastResponse.Authority.Count == 1) && (lastResponse.Authority[0].RDATA as DnsForwarderRecord).Forwarder.Equals("this-server", StringComparison.OrdinalIgnoreCase)) { //do conditional forwarding via "this-server" - lastResponse = await RecursiveResolveAsync(newRequest, null, null, false, false); + lastResponse = await RecursiveResolveAsync(newRequest, null, false, false); isAuthoritativeAnswer = false; } else @@ -1171,7 +1215,7 @@ namespace DnsServerCore.Dns } } - lastResponse = await RecursiveResolveAsync(newRequest, null, forwarders, false, false); + lastResponse = await RecursiveResolveAsync(newRequest, forwarders, false, false); isAuthoritativeAnswer = false; } @@ -1256,7 +1300,7 @@ namespace DnsServerCore.Dns if (lastResponse.RCODE == DnsResponseCode.Refused) { //not found in auth zone; do recursion - lastResponse = await RecursiveResolveAsync(newRequest, null, null, false, false); + lastResponse = await RecursiveResolveAsync(newRequest, null, false, false); } else if ((lastResponse.Answer.Count == 0) && (lastResponse.Authority.Count > 0)) { @@ -1264,17 +1308,15 @@ namespace DnsServerCore.Dns switch (lastResponse.Authority[0].Type) { case DnsResourceRecordType.NS: - //do recursive resolution using last response authority name servers - List nameServers = NameServerAddress.GetNameServersFromResponse(lastResponse, _preferIPv6, false); - - lastResponse = await RecursiveResolveAsync(newRequest, nameServers, null, false, false); + //do recursive resolution; name servers will be provided via ResolverDnsCache + lastResponse = await RecursiveResolveAsync(newRequest, null, false, false); break; case DnsResourceRecordType.FWD: if ((lastResponse.Authority.Count == 1) && (lastResponse.Authority[0].RDATA as DnsForwarderRecord).Forwarder.Equals("this-server", StringComparison.OrdinalIgnoreCase)) { //do conditional forwarding via "this-server" - lastResponse = await RecursiveResolveAsync(newRequest, null, null, false, false); + lastResponse = await RecursiveResolveAsync(newRequest, null, false, false); } else { @@ -1292,7 +1334,7 @@ namespace DnsServerCore.Dns } } - lastResponse = await RecursiveResolveAsync(newRequest, null, forwarders, false, false); + lastResponse = await RecursiveResolveAsync(newRequest, forwarders, false, false); } break; @@ -1381,9 +1423,9 @@ namespace DnsServerCore.Dns return response; } - private async Task ProcessRecursiveQueryAsync(DnsDatagram request, IReadOnlyList viaNameServers, IReadOnlyList viaForwarders, bool checkForCnameCloaking, bool cacheRefreshOperation) + private async Task ProcessRecursiveQueryAsync(DnsDatagram request, IReadOnlyList viaForwarders, bool checkForCnameCloaking, bool cacheRefreshOperation) { - DnsDatagram response = await RecursiveResolveAsync(request, viaNameServers, viaForwarders, false, cacheRefreshOperation); + DnsDatagram response = await RecursiveResolveAsync(request, viaForwarders, false, cacheRefreshOperation); if (response.Answer.Count > 0) { @@ -1473,7 +1515,7 @@ namespace DnsServerCore.Dns } } - private async Task RecursiveResolveAsync(DnsDatagram request, IReadOnlyList viaNameServers, IReadOnlyList viaForwarders, bool cachePrefetchOperation, bool cacheRefreshOperation) + private async Task RecursiveResolveAsync(DnsDatagram request, IReadOnlyList viaForwarders, bool cachePrefetchOperation, bool cacheRefreshOperation) { if (!cachePrefetchOperation && !cacheRefreshOperation) { @@ -1489,7 +1531,7 @@ namespace DnsServerCore.Dns if ((answer.OriginalTtlValue > _cachePrefetchEligibility) && (answer.TtlValue < _cachePrefetchTrigger)) { //trigger prefetch async - _ = PrefetchCacheAsync(request, viaNameServers, viaForwarders); + _ = PrefetchCacheAsync(request, viaForwarders); break; } } @@ -1508,7 +1550,7 @@ namespace DnsServerCore.Dns //got new resolver task added so question is not being resolved; do recursive resolution in another task on resolver thread pool _ = Task.Factory.StartNew(delegate () { - return RecursiveResolveAsync(request, viaNameServers, viaForwarders, cachePrefetchOperation, cacheRefreshOperation, resolverTaskCompletionSource); + return RecursiveResolveAsync(request, viaForwarders, cachePrefetchOperation, cacheRefreshOperation, resolverTaskCompletionSource); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, _resolverTaskScheduler); } @@ -1563,7 +1605,7 @@ namespace DnsServerCore.Dns return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.ServerFailure, request.Question); } - private async Task RecursiveResolveAsync(DnsDatagram request, IReadOnlyList viaNameServers, IReadOnlyList viaForwarders, bool cachePrefetchOperation, bool cacheRefreshOperation, TaskCompletionSource taskCompletionSource) + private async Task RecursiveResolveAsync(DnsDatagram request, IReadOnlyList viaForwarders, bool cachePrefetchOperation, bool cacheRefreshOperation, TaskCompletionSource taskCompletionSource) { IReadOnlyList forwarders = _forwarders; if (viaForwarders != null) @@ -1571,7 +1613,7 @@ namespace DnsServerCore.Dns try { - if ((viaNameServers == null) && (forwarders != null)) + if (forwarders != null) { //use forwarders if (_proxy == null) @@ -1580,7 +1622,7 @@ namespace DnsServerCore.Dns foreach (NameServerAddress nameServerAddress in forwarders) { if (nameServerAddress.IsIPEndPointStale) //refresh forwarder IPEndPoint if stale - await nameServerAddress.RecursiveResolveIPAddressAsync(_dnsCache, null, _preferIPv6, _randomizeName, _resolverRetries, _resolverTimeout); + await nameServerAddress.RecursiveResolveIPAddressAsync(_dnsCache, null, _preferIPv6, _randomizeName, _qnameMinimization, _resolverRetries, _resolverTimeout); } } @@ -1610,7 +1652,7 @@ namespace DnsServerCore.Dns else dnsCache = _dnsCache; - DnsDatagram response = await DnsClient.RecursiveResolveAsync(request.Question[0], viaNameServers, dnsCache, _proxy, _preferIPv6, _randomizeName, _resolverRetries, _resolverTimeout, _resolverMaxStackCount); + DnsDatagram response = await DnsClient.RecursiveResolveAsync(request.Question[0], dnsCache, _proxy, _preferIPv6, _randomizeName, _qnameMinimization, _resolverRetries, _resolverTimeout, _resolverMaxStackCount); taskCompletionSource.SetResult(response); } } @@ -1619,30 +1661,20 @@ namespace DnsServerCore.Dns LogManager log = _log; if (log != null) { - string nameServers = null; + string strForwarders = null; - if (viaNameServers != null) - { - foreach (NameServerAddress nameServer in viaNameServers) - { - if (nameServers == null) - nameServers = nameServer.ToString(); - else - nameServers += ", " + nameServer.ToString(); - } - } - else if (forwarders != null) + if (forwarders != null) { foreach (NameServerAddress nameServer in forwarders) { - if (nameServers == null) - nameServers = nameServer.ToString(); + if (strForwarders == null) + strForwarders = nameServer.ToString(); else - nameServers += ", " + nameServer.ToString(); + strForwarders += ", " + nameServer.ToString(); } } - log.Write("DNS Server recursive resolution failed for QNAME: " + request.Question[0].Name + "; QTYPE: " + request.Question[0].Type.ToString() + "; QCLASS: " + request.Question[0].Class.ToString() + (nameServers == null ? "" : "; Name Servers: " + nameServers) + ";\r\n" + ex.ToString()); + log.Write("DNS Server recursive resolution failed for QNAME: " + request.Question[0].Name + "; QTYPE: " + request.Question[0].Type.ToString() + "; QCLASS: " + request.Question[0].Class.ToString() + (strForwarders == null ? "" : "; Forwarders: " + strForwarders) + ";\r\n" + ex.ToString()); } if (_serveStale) @@ -1704,11 +1736,11 @@ namespace DnsServerCore.Dns return null; } - private async Task PrefetchCacheAsync(DnsDatagram request, IReadOnlyList viaNameServers, IReadOnlyList viaForwarders) + private async Task PrefetchCacheAsync(DnsDatagram request, IReadOnlyList viaForwarders) { try { - await RecursiveResolveAsync(request, viaNameServers, viaForwarders, true, false); + await RecursiveResolveAsync(request, viaForwarders, true, false); } catch (Exception ex) { @@ -1724,7 +1756,7 @@ namespace DnsServerCore.Dns { //refresh cache DnsDatagram request = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, true, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { sample.SampleQuestion }); - DnsDatagram response = await ProcessRecursiveQueryAsync(request, sample.ViaNameServers, sample.ViaForwarders, false, true); + DnsDatagram response = await ProcessRecursiveQueryAsync(request, sample.ViaForwarders, false, true); bool removeFromSampleList = true; DateTime utcNow = DateTime.UtcNow; @@ -1824,7 +1856,6 @@ namespace DnsServerCore.Dns DnsQuestionRecord refreshQuery; - IReadOnlyList viaNameServers = null; IReadOnlyList viaForwarders = null; //query auth zone for refresh query @@ -1853,10 +1884,6 @@ namespace DnsServerCore.Dns { case DnsResourceRecordType.NS: refreshQuery = GetCacheRefreshNeededQuery(eligibleQuery.Key, cacheRefreshTrigger); - - if ((refreshQuery != null) && refreshQuery.Equals(eligibleQuery.Key)) - viaNameServers = NameServerAddress.GetNameServersFromResponse(response, _preferIPv6, false); - break; case DnsResourceRecordType.FWD: @@ -1904,7 +1931,7 @@ namespace DnsServerCore.Dns } if (refreshQuery != null) - cacheRefreshSampleList.Add(new CacheRefreshSample(refreshQuery, viaNameServers, viaForwarders)); + cacheRefreshSampleList.Add(new CacheRefreshSample(refreshQuery, viaForwarders)); } _cacheRefreshSampleList = cacheRefreshSampleList; @@ -2472,6 +2499,9 @@ namespace DnsServerCore.Dns public CacheZoneManager CacheZoneManager { get { return _cacheZoneManager; } } + public DnsApplicationManager DnsApplicationManager + { get { return _dnsApplicationManager; } } + public IDnsCache DnsCache { get { return _dnsCache; } } @@ -2515,6 +2545,12 @@ namespace DnsServerCore.Dns set { _randomizeName = value; } } + public bool QnameMinimization + { + get { return _qnameMinimization; } + set { _qnameMinimization = value; } + } + public int ForwarderRetries { get { return _forwarderRetries; } @@ -2670,15 +2706,13 @@ namespace DnsServerCore.Dns class CacheRefreshSample { - public CacheRefreshSample(DnsQuestionRecord sampleQuestion, IReadOnlyList viaNameServers, IReadOnlyList viaForwarders) + public CacheRefreshSample(DnsQuestionRecord sampleQuestion, IReadOnlyList viaForwarders) { SampleQuestion = sampleQuestion; - ViaNameServers = viaNameServers; ViaForwarders = viaForwarders; } public DnsQuestionRecord SampleQuestion { get; } - public IReadOnlyList ViaNameServers { get; } public IReadOnlyList ViaForwarders { get; } } }