mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2026-03-26 15:30:25 +00:00
DnsServer: implemented ResolverDnsCache for recursive resolution. Updated ProcessQuery() to change the sequence of checking for blocked zones to allow blocking correctly in case of stub zone or forwarder zone being used. Fixed implementation changes in ProcessAuthoritativeQuery(), ProcessCNAME(), ProcessANAME() and ProcessRecursiveQuery(). RecursiveResolveAsync() stopped caching failure record in case of exception due to undesirable side effects for clients. CachePrefetchSamplingAsync() added zone type checks. Code refactoring done.
This commit is contained in:
@@ -82,6 +82,8 @@ namespace DnsServerCore.Dns
|
||||
readonly BlockListZoneManager _blockListZoneManager;
|
||||
readonly CacheZoneManager _cacheZoneManager = new CacheZoneManager();
|
||||
|
||||
readonly ResolverDnsCache _dnsCache;
|
||||
|
||||
readonly DnsARecord _aRecord = new DnsARecord(IPAddress.Any);
|
||||
readonly DnsAAAARecord _aaaaRecord = new DnsAAAARecord(IPAddress.IPv6Any);
|
||||
readonly DnsTXTRecord _txtRecord = new DnsTXTRecord("blockList=custom");
|
||||
@@ -163,6 +165,8 @@ namespace DnsServerCore.Dns
|
||||
_blockedZoneManager = new BlockedZoneManager(this);
|
||||
_blockListZoneManager = new BlockListZoneManager(this);
|
||||
|
||||
_dnsCache = new ResolverDnsCache(_authZoneManager, _cacheZoneManager);
|
||||
|
||||
//init stats
|
||||
string statsFolder = Path.Combine(_configFolder, "stats");
|
||||
|
||||
@@ -847,7 +851,7 @@ namespace DnsServerCore.Dns
|
||||
{
|
||||
try
|
||||
{
|
||||
string statusString = statusCode + " " + GetStatusString((HttpStatusCode)statusCode);
|
||||
string statusString = statusCode + " " + GetHttpStatusString((HttpStatusCode)statusCode);
|
||||
byte[] bufferContent = Encoding.UTF8.GetBytes("<html><head><title>" + statusString + "</title></head><body><h1>" + statusString + "</h1>" + (message == null ? "" : "<p>" + message + "</p>") + "</body></html>");
|
||||
byte[] bufferHeader = Encoding.UTF8.GetBytes("HTTP/1.1 " + statusString + "\r\nDate: " + DateTime.UtcNow.ToString("r") + "\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " + bufferContent.Length + "\r\nX-Robots-Tag: noindex, nofollow\r\n\r\n");
|
||||
|
||||
@@ -859,7 +863,7 @@ namespace DnsServerCore.Dns
|
||||
{ }
|
||||
}
|
||||
|
||||
internal static string GetStatusString(HttpStatusCode statusCode)
|
||||
internal static string GetHttpStatusString(HttpStatusCode statusCode)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -924,15 +928,10 @@ namespace DnsServerCore.Dns
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NotImplemented, request.Question);
|
||||
|
||||
default:
|
||||
//query authoritative zone
|
||||
DnsDatagram response = ProcessAuthoritativeQuery(request, isRecursionAllowed);
|
||||
|
||||
if ((response.RCODE != DnsResponseCode.Refused) || !request.RecursionDesired || !isRecursionAllowed)
|
||||
return response;
|
||||
DnsDatagram response;
|
||||
|
||||
//check in allowed zone
|
||||
bool inAllowedZone = _allowedZoneManager.Query(request).RCODE != DnsResponseCode.Refused;
|
||||
|
||||
if (!inAllowedZone)
|
||||
{
|
||||
//check in blocked zone and block list zone
|
||||
@@ -941,8 +940,14 @@ namespace DnsServerCore.Dns
|
||||
return response;
|
||||
}
|
||||
|
||||
//query authoritative zone
|
||||
response = ProcessAuthoritativeQuery(request, isRecursionAllowed);
|
||||
|
||||
if ((response.RCODE != DnsResponseCode.Refused) || !request.RecursionDesired || !isRecursionAllowed)
|
||||
return response;
|
||||
|
||||
//do recursive query
|
||||
return ProcessRecursiveQuery(request, !inAllowedZone, null, null, false);
|
||||
return ProcessRecursiveQuery(request, null, null, !inAllowedZone, false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1190,81 +1195,7 @@ namespace DnsServerCore.Dns
|
||||
switch (lastRR.Type)
|
||||
{
|
||||
case DnsResourceRecordType.CNAME:
|
||||
List<DnsResourceRecord> responseAnswer = new List<DnsResourceRecord>();
|
||||
responseAnswer.AddRange(response.Answer);
|
||||
|
||||
DnsDatagram lastResponse;
|
||||
|
||||
int queryCount = 0;
|
||||
do
|
||||
{
|
||||
DnsDatagram newRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord((lastRR.RDATA as DnsCNAMERecord).Domain, questionType, request.Question[0].Class) });
|
||||
|
||||
//query authoritative zone first
|
||||
lastResponse = _authZoneManager.Query(newRequest);
|
||||
|
||||
if (lastResponse.RCODE == DnsResponseCode.Refused)
|
||||
{
|
||||
//not found in auth zone
|
||||
if (newRequest.RecursionDesired && isRecursionAllowed)
|
||||
{
|
||||
//do recursion
|
||||
lastResponse = ProcessRecursiveQuery(newRequest, false, null, null, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastResponse = response; //response is logically last valid response
|
||||
break; //break since no recursion allowed/desired
|
||||
}
|
||||
}
|
||||
else if ((lastResponse.Answer.Count > 0) && (lastResponse.Answer[0].Type == DnsResourceRecordType.ANAME))
|
||||
{
|
||||
lastResponse = ProcessANAME(request, lastResponse, isRecursionAllowed);
|
||||
}
|
||||
else if ((lastResponse.Answer.Count == 0) && (lastResponse.Authority.Count > 0))
|
||||
{
|
||||
//found delegated/forwarded zone
|
||||
lastResponse = ProcessAuthoritySection(newRequest, lastResponse, isRecursionAllowed);
|
||||
}
|
||||
|
||||
//check last response
|
||||
if (lastResponse.Answer.Count == 0)
|
||||
break; //cannot proceed to resolve cname further
|
||||
|
||||
responseAnswer.AddRange(lastResponse.Answer);
|
||||
|
||||
lastRR = lastResponse.Answer[lastResponse.Answer.Count - 1];
|
||||
|
||||
if (lastRR.Type != DnsResourceRecordType.CNAME)
|
||||
break; //cname was resolved
|
||||
}
|
||||
while (++queryCount < MAX_CNAME_HOPS);
|
||||
|
||||
DnsResponseCode rcode;
|
||||
IReadOnlyList<DnsResourceRecord> authority = null;
|
||||
IReadOnlyList<DnsResourceRecord> additional = null;
|
||||
|
||||
if ((lastResponse.RCODE == DnsResponseCode.Refused) && !(request.RecursionDesired && isRecursionAllowed))
|
||||
{
|
||||
rcode = DnsResponseCode.NoError;
|
||||
}
|
||||
else
|
||||
{
|
||||
rcode = lastResponse.RCODE;
|
||||
|
||||
if (lastResponse.AuthoritativeAnswer)
|
||||
{
|
||||
authority = lastResponse.Authority;
|
||||
additional = lastResponse.Additional;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((lastResponse.Authority.Count > 0) && (lastResponse.Authority[0].Type == DnsResourceRecordType.SOA))
|
||||
authority = lastResponse.Authority;
|
||||
}
|
||||
}
|
||||
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, lastResponse.AuthoritativeAnswer, false, request.RecursionDesired, isRecursionAllowed, false, false, rcode, request.Question, responseAnswer, authority, additional) { Tag = response.Tag };
|
||||
return ProcessCNAME(request, response, isRecursionAllowed, false);
|
||||
|
||||
case DnsResourceRecordType.ANAME:
|
||||
return ProcessANAME(request, response, isRecursionAllowed);
|
||||
@@ -1273,13 +1204,166 @@ namespace DnsServerCore.Dns
|
||||
}
|
||||
else if (response.Authority.Count > 0)
|
||||
{
|
||||
return ProcessAuthoritySection(request, response, isRecursionAllowed);
|
||||
switch (response.Authority[0].Type)
|
||||
{
|
||||
case DnsResourceRecordType.NS:
|
||||
if (request.RecursionDesired && isRecursionAllowed)
|
||||
{
|
||||
//do recursive resolution using response authority name servers
|
||||
List<NameServerAddress> nameServers = NameServerAddress.GetNameServersFromResponse(response, _preferIPv6, false);
|
||||
|
||||
return ProcessRecursiveQuery(request, nameServers, null, false, false);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DnsResourceRecordType.FWD:
|
||||
if ((response.Authority.Count == 1) && (response.Authority[0].RDATA as DnsForwarderRecord).Forwarder.Equals("this-server", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
//do conditional forwarding via "this-server"
|
||||
return ProcessRecursiveQuery(request, null, null, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//do conditional forwarding
|
||||
List<NameServerAddress> forwarders = new List<NameServerAddress>(response.Authority.Count);
|
||||
|
||||
foreach (DnsResourceRecord rr in response.Authority)
|
||||
{
|
||||
if (rr.Type == DnsResourceRecordType.FWD)
|
||||
forwarders.Add((rr.RDATA as DnsForwarderRecord).NameServer);
|
||||
}
|
||||
|
||||
return ProcessRecursiveQuery(request, null, forwarders, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private DnsDatagram ProcessCNAME(DnsDatagram request, DnsDatagram response, bool isRecursionAllowed, bool cacheRefreshOperation)
|
||||
{
|
||||
List<DnsResourceRecord> responseAnswer = new List<DnsResourceRecord>();
|
||||
responseAnswer.AddRange(response.Answer);
|
||||
|
||||
DnsDatagram lastResponse;
|
||||
bool isAuthoritativeAnswer = response.AuthoritativeAnswer;
|
||||
string lastDomain = (response.Answer[response.Answer.Count - 1].RDATA as DnsCNAMERecord).Domain;
|
||||
|
||||
int queryCount = 0;
|
||||
do
|
||||
{
|
||||
DnsDatagram newRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord(lastDomain, request.Question[0].Type, request.Question[0].Class) });
|
||||
|
||||
//query authoritative zone first
|
||||
lastResponse = _authZoneManager.Query(newRequest);
|
||||
|
||||
if (lastResponse.RCODE == DnsResponseCode.Refused)
|
||||
{
|
||||
//not found in auth zone
|
||||
if (newRequest.RecursionDesired && isRecursionAllowed)
|
||||
{
|
||||
//do recursion
|
||||
lastResponse = RecursiveResolve(newRequest, null, null, false, cacheRefreshOperation);
|
||||
isAuthoritativeAnswer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//break since no recursion allowed/desired
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((lastResponse.Answer.Count > 0) && (lastResponse.Answer[0].Type == DnsResourceRecordType.ANAME))
|
||||
{
|
||||
lastResponse = ProcessANAME(request, lastResponse, isRecursionAllowed);
|
||||
}
|
||||
else if ((lastResponse.Answer.Count == 0) && (lastResponse.Authority.Count > 0))
|
||||
{
|
||||
//found delegated/forwarded zone
|
||||
switch (lastResponse.Authority[0].Type)
|
||||
{
|
||||
case DnsResourceRecordType.NS:
|
||||
if (newRequest.RecursionDesired && isRecursionAllowed)
|
||||
{
|
||||
//do recursive resolution using last response authority name servers
|
||||
List<NameServerAddress> nameServers = NameServerAddress.GetNameServersFromResponse(lastResponse, _preferIPv6, false);
|
||||
|
||||
lastResponse = RecursiveResolve(newRequest, nameServers, null, false, false);
|
||||
isAuthoritativeAnswer = 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 = RecursiveResolve(newRequest, null, null, false, false);
|
||||
isAuthoritativeAnswer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//do conditional forwarding
|
||||
List<NameServerAddress> forwarders = new List<NameServerAddress>(lastResponse.Authority.Count);
|
||||
|
||||
foreach (DnsResourceRecord rr in lastResponse.Authority)
|
||||
{
|
||||
if (rr.Type == DnsResourceRecordType.FWD)
|
||||
forwarders.Add((rr.RDATA as DnsForwarderRecord).NameServer);
|
||||
}
|
||||
|
||||
lastResponse = RecursiveResolve(newRequest, null, forwarders, false, false);
|
||||
isAuthoritativeAnswer = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//check last response
|
||||
if (lastResponse.Answer.Count == 0)
|
||||
break; //cannot proceed to resolve further
|
||||
|
||||
responseAnswer.AddRange(lastResponse.Answer);
|
||||
|
||||
DnsResourceRecord lastRR = lastResponse.Answer[lastResponse.Answer.Count - 1];
|
||||
|
||||
if (lastRR.Type != DnsResourceRecordType.CNAME)
|
||||
break; //cname was resolved
|
||||
|
||||
lastDomain = (lastRR.RDATA as DnsCNAMERecord).Domain;
|
||||
}
|
||||
while (++queryCount < MAX_CNAME_HOPS);
|
||||
|
||||
DnsResponseCode rcode;
|
||||
IReadOnlyList<DnsResourceRecord> authority = null;
|
||||
IReadOnlyList<DnsResourceRecord> additional = null;
|
||||
|
||||
if ((lastResponse.RCODE == DnsResponseCode.Refused) && !(request.RecursionDesired && isRecursionAllowed))
|
||||
{
|
||||
rcode = DnsResponseCode.NoError;
|
||||
}
|
||||
else
|
||||
{
|
||||
rcode = lastResponse.RCODE;
|
||||
|
||||
if (isAuthoritativeAnswer)
|
||||
{
|
||||
authority = response.Authority;
|
||||
additional = response.Additional;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((lastResponse.Authority.Count > 0) && (lastResponse.Authority[0].Type == DnsResourceRecordType.SOA))
|
||||
authority = lastResponse.Authority;
|
||||
}
|
||||
}
|
||||
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, isAuthoritativeAnswer, false, request.RecursionDesired, isRecursionAllowed, false, false, rcode, request.Question, responseAnswer, authority, additional) { Tag = response.Tag };
|
||||
}
|
||||
|
||||
private DnsDatagram ProcessANAME(DnsDatagram request, DnsDatagram response, bool isRecursionAllowed)
|
||||
{
|
||||
List<DnsResourceRecord> responseAnswer = new List<DnsResourceRecord>();
|
||||
@@ -1302,12 +1386,42 @@ namespace DnsServerCore.Dns
|
||||
if (lastResponse.RCODE == DnsResponseCode.Refused)
|
||||
{
|
||||
//not found in auth zone; do recursion
|
||||
lastResponse = ProcessRecursiveQuery(newRequest, false, null, null, false);
|
||||
lastResponse = RecursiveResolve(newRequest, null, null, false, false);
|
||||
}
|
||||
else if ((lastResponse.Answer.Count == 0) && (lastResponse.Authority.Count > 0))
|
||||
{
|
||||
//found delegated/forwarded zone
|
||||
lastResponse = ProcessAuthoritySection(newRequest, lastResponse, true);
|
||||
switch (lastResponse.Authority[0].Type)
|
||||
{
|
||||
case DnsResourceRecordType.NS:
|
||||
//do recursive resolution using last response authority name servers
|
||||
List<NameServerAddress> nameServers = NameServerAddress.GetNameServersFromResponse(lastResponse, _preferIPv6, false);
|
||||
|
||||
lastResponse = RecursiveResolve(newRequest, nameServers, 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 = RecursiveResolve(newRequest, null, null, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//do conditional forwarding
|
||||
List<NameServerAddress> forwarders = new List<NameServerAddress>(lastResponse.Authority.Count);
|
||||
|
||||
foreach (DnsResourceRecord rr in lastResponse.Authority)
|
||||
{
|
||||
if (rr.Type == DnsResourceRecordType.FWD)
|
||||
forwarders.Add((rr.RDATA as DnsForwarderRecord).NameServer);
|
||||
}
|
||||
|
||||
lastResponse = RecursiveResolve(newRequest, null, forwarders, false, false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//check last response
|
||||
@@ -1346,45 +1460,6 @@ namespace DnsServerCore.Dns
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, isRecursionAllowed, false, false, rcode, request.Question, responseAnswer, response.Authority, response.Additional) { Tag = response.Tag };
|
||||
}
|
||||
|
||||
private DnsDatagram ProcessAuthoritySection(DnsDatagram request, DnsDatagram response, bool isRecursionAllowed)
|
||||
{
|
||||
switch (response.Authority[0].Type)
|
||||
{
|
||||
case DnsResourceRecordType.NS:
|
||||
if (request.RecursionDesired && isRecursionAllowed)
|
||||
{
|
||||
//do recursive resolution using response authority name servers
|
||||
List<NameServerAddress> nameServers = NameServerAddress.GetNameServersFromResponse(response, _preferIPv6, false);
|
||||
|
||||
return ProcessRecursiveQuery(request, false, nameServers, null, false);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DnsResourceRecordType.FWD:
|
||||
if ((response.Authority.Count == 1) && (response.Authority[0].RDATA as DnsForwarderRecord).Forwarder.Equals("this-server", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
//do conditional forwarding via "this-server"
|
||||
return ProcessRecursiveQuery(request, false, null, null, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//do conditional forwarding
|
||||
List<NameServerAddress> forwarders = new List<NameServerAddress>(response.Authority.Count);
|
||||
|
||||
foreach (DnsResourceRecord rr in response.Authority)
|
||||
{
|
||||
if (rr.Type == DnsResourceRecordType.FWD)
|
||||
forwarders.Add((rr.RDATA as DnsForwarderRecord).NameServer);
|
||||
}
|
||||
|
||||
return ProcessRecursiveQuery(request, false, null, forwarders, false);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private DnsDatagram ProcessBlockedQuery(DnsDatagram request)
|
||||
{
|
||||
DnsDatagram response = _blockedZoneManager.Query(request);
|
||||
@@ -1431,113 +1506,61 @@ namespace DnsServerCore.Dns
|
||||
return response;
|
||||
}
|
||||
|
||||
private DnsDatagram ProcessRecursiveQuery(DnsDatagram request, bool checkForCnameCloaking, IReadOnlyList<NameServerAddress> viaNameServers, IReadOnlyList<NameServerAddress> viaForwarders, bool cacheRefreshOperation)
|
||||
private DnsDatagram ProcessRecursiveQuery(DnsDatagram request, IReadOnlyList<NameServerAddress> viaNameServers, IReadOnlyList<NameServerAddress> viaForwarders, bool checkForCnameCloaking, bool cacheRefreshOperation)
|
||||
{
|
||||
DnsDatagram response = RecursiveResolve(request, viaNameServers, viaForwarders, false, cacheRefreshOperation);
|
||||
|
||||
DnsResponseCode rcode = response.RCODE;
|
||||
IReadOnlyList<DnsResourceRecord> answer = response.Answer;
|
||||
IReadOnlyList<DnsResourceRecord> authority = null;
|
||||
IReadOnlyList<DnsResourceRecord> additional = null;
|
||||
|
||||
if (response.Answer.Count > 0)
|
||||
{
|
||||
bool cnameCheckPassed = true;
|
||||
DnsResourceRecordType questionType = request.Question[0].Type;
|
||||
DnsResourceRecord lastRR = response.Answer[response.Answer.Count - 1];
|
||||
|
||||
if ((lastRR.Type != questionType) && (lastRR.Type == DnsResourceRecordType.CNAME) && (questionType != DnsResourceRecordType.ANY))
|
||||
response = ProcessCNAME(request, response, true, cacheRefreshOperation);
|
||||
|
||||
if (checkForCnameCloaking && (response.Answer.Count > 1))
|
||||
{
|
||||
List<DnsResourceRecord> responseAnswer = new List<DnsResourceRecord>();
|
||||
|
||||
foreach (DnsResourceRecord record in response.Answer)
|
||||
for (int i = 0; i < response.Answer.Count; i++)
|
||||
{
|
||||
responseAnswer.Add(record);
|
||||
DnsResourceRecord record = response.Answer[i];
|
||||
|
||||
if (record.Type == DnsResourceRecordType.CNAME)
|
||||
if (record.Type != DnsResourceRecordType.CNAME)
|
||||
break; //no further CNAME records exists
|
||||
|
||||
DnsDatagram newRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, true, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord((record.RDATA as DnsCNAMERecord).Domain, request.Question[0].Type, request.Question[0].Class) });
|
||||
DnsDatagram lastResponse = ProcessBlockedQuery(newRequest);
|
||||
if (lastResponse != null)
|
||||
{
|
||||
DnsDatagram newRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, true, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord((record.RDATA as DnsCNAMERecord).Domain, request.Question[0].Type, request.Question[0].Class) });
|
||||
DnsDatagram lastResponse = ProcessBlockedQuery(newRequest);
|
||||
if (lastResponse != null)
|
||||
{
|
||||
//found cname cloaking
|
||||
cnameCheckPassed = false;
|
||||
responseAnswer.AddRange(lastResponse.Answer);
|
||||
//found cname cloaking
|
||||
List<DnsResourceRecord> answer = new List<DnsResourceRecord>();
|
||||
|
||||
rcode = lastResponse.RCODE;
|
||||
answer = responseAnswer;
|
||||
//copy previous CNAME records
|
||||
for (int j = 0; j < i; j++)
|
||||
answer.Add(record);
|
||||
|
||||
if ((lastResponse.Authority.Count > 0) && (lastResponse.Authority[0].Type == DnsResourceRecordType.SOA))
|
||||
authority = lastResponse.Authority;
|
||||
//copy last response answers
|
||||
answer.AddRange(lastResponse.Answer);
|
||||
|
||||
response.Tag = lastResponse.Tag;
|
||||
break;
|
||||
}
|
||||
IReadOnlyList<DnsResourceRecord> authority = null;
|
||||
|
||||
if ((lastResponse.Authority.Count > 0) && (lastResponse.Authority[0].Type == DnsResourceRecordType.SOA))
|
||||
authority = lastResponse.Authority;
|
||||
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, true, true, false, false, DnsResponseCode.NoError, request.Question, answer, authority) { Tag = lastResponse.Tag };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cnameCheckPassed)
|
||||
{
|
||||
DnsResourceRecordType questionType = request.Question[0].Type;
|
||||
DnsResourceRecord lastRR = response.Answer[response.Answer.Count - 1];
|
||||
|
||||
if ((lastRR.Type != questionType) && (lastRR.Type == DnsResourceRecordType.CNAME) && (questionType != DnsResourceRecordType.ANY))
|
||||
{
|
||||
List<DnsResourceRecord> responseAnswer = new List<DnsResourceRecord>();
|
||||
responseAnswer.AddRange(response.Answer);
|
||||
|
||||
DnsDatagram lastResponse;
|
||||
int queryCount = 0;
|
||||
|
||||
do
|
||||
{
|
||||
DnsDatagram newRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, true, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord((lastRR.RDATA as DnsCNAMERecord).Domain, request.Question[0].Type, request.Question[0].Class) });
|
||||
|
||||
if (checkForCnameCloaking)
|
||||
{
|
||||
//check if cname domain is blocked to prevent cname cloaking
|
||||
lastResponse = ProcessBlockedQuery(newRequest);
|
||||
if (lastResponse == null)
|
||||
lastResponse = RecursiveResolve(newRequest, null, null, false, cacheRefreshOperation); //cname is not blocked; so do recursive query
|
||||
}
|
||||
else
|
||||
{
|
||||
//do recursive query for cname
|
||||
lastResponse = RecursiveResolve(newRequest, null, null, false, cacheRefreshOperation);
|
||||
}
|
||||
|
||||
if (lastResponse.Answer.Count == 0)
|
||||
break;
|
||||
|
||||
responseAnswer.AddRange(lastResponse.Answer);
|
||||
|
||||
lastRR = lastResponse.Answer[lastResponse.Answer.Count - 1];
|
||||
|
||||
if (lastRR.Type == questionType)
|
||||
break;
|
||||
|
||||
if (lastRR.Type != DnsResourceRecordType.CNAME)
|
||||
throw new DnsServerException("Invalid response received from DNS server.");
|
||||
}
|
||||
while (++queryCount < MAX_CNAME_HOPS);
|
||||
|
||||
rcode = lastResponse.RCODE;
|
||||
answer = responseAnswer;
|
||||
|
||||
if ((lastResponse.Authority.Count > 0) && (lastResponse.Authority[0].Type == DnsResourceRecordType.SOA))
|
||||
authority = lastResponse.Authority;
|
||||
|
||||
response.Tag = lastResponse.Tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((authority == null) && (response.Authority.Count > 0) && (response.Authority[0].Type == DnsResourceRecordType.SOA))
|
||||
authority = response.Authority;
|
||||
//return response
|
||||
{
|
||||
IReadOnlyList<DnsResourceRecord> authority = null;
|
||||
|
||||
if ((response.Additional.Count > 0) && (request.Question[0].Type == DnsResourceRecordType.MX))
|
||||
additional = response.Additional;
|
||||
if ((authority == null) && (response.Authority.Count > 0) && (response.Authority[0].Type == DnsResourceRecordType.SOA))
|
||||
authority = response.Authority;
|
||||
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, true, true, false, false, rcode, request.Question, answer, authority, additional) { Tag = response.Tag };
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, true, true, false, false, response.RCODE, request.Question, response.Answer, authority) { Tag = response.Tag };
|
||||
}
|
||||
}
|
||||
|
||||
private DnsDatagram RecursiveResolve(DnsDatagram request, IReadOnlyList<NameServerAddress> viaNameServers, IReadOnlyList<NameServerAddress> viaForwarders, bool cachePrefetchOperation, bool cacheRefreshOperation)
|
||||
@@ -1595,8 +1618,7 @@ namespace DnsServerCore.Dns
|
||||
return null; //return null as prefetch worker thread does not need valid response and thus does not need to wait
|
||||
|
||||
//wait till short timeout for response
|
||||
bool signalled = queryHandle.WaitForResponse(1800, out DnsDatagram response); //1.8 sec wait as per draft-ietf-dnsop-serve-stale-04
|
||||
if (signalled)
|
||||
if (queryHandle.WaitForResponse(1800, out DnsDatagram response)) //1.8 sec wait as per draft-ietf-dnsop-serve-stale-04
|
||||
{
|
||||
//resolver signaled
|
||||
if (response != null)
|
||||
@@ -1620,11 +1642,11 @@ namespace DnsServerCore.Dns
|
||||
if (response != null)
|
||||
return response;
|
||||
|
||||
//no response available from resolver
|
||||
//no response available from resolver or resolver had exception and no stale record was found
|
||||
}
|
||||
}
|
||||
|
||||
//no response available respond with ServerFailure
|
||||
//no response available; respond with ServerFailure
|
||||
return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.ServerFailure, request.Question);
|
||||
}
|
||||
|
||||
@@ -1655,7 +1677,7 @@ namespace DnsServerCore.Dns
|
||||
foreach (NameServerAddress nameServerAddress in forwarders)
|
||||
{
|
||||
if (nameServerAddress.IsIPEndPointStale) //refresh forwarder IPEndPoint if stale
|
||||
nameServerAddress.RecursiveResolveIPAddress(_cacheZoneManager, null, _preferIPv6, _retries, _timeout);
|
||||
nameServerAddress.RecursiveResolveIPAddress(_dnsCache, null, _preferIPv6, _retries, _timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1679,9 +1701,9 @@ namespace DnsServerCore.Dns
|
||||
IDnsCache dnsCache;
|
||||
|
||||
if (cachePrefetchOperation || cacheRefreshOperation)
|
||||
dnsCache = new ResolverPrefetchDnsCache(_cacheZoneManager, request.Question[0]);
|
||||
dnsCache = new ResolverPrefetchDnsCache(_authZoneManager, _cacheZoneManager, request.Question[0]);
|
||||
else
|
||||
dnsCache = _cacheZoneManager;
|
||||
dnsCache = _dnsCache;
|
||||
|
||||
DnsDatagram response = DnsClient.RecursiveResolve(request.Question[0], viaNameServers, dnsCache, _proxy, _preferIPv6, _retries, _timeout, _maxStackCount);
|
||||
queryHandle.Set(response);
|
||||
@@ -1720,21 +1742,10 @@ namespace DnsServerCore.Dns
|
||||
|
||||
//fetch stale record
|
||||
DnsDatagram cacheResponse = QueryCache(request, true);
|
||||
if ((cacheResponse == null) || ((cacheResponse.Answer.Count == 0) && (cacheResponse.Authority.Count == 0)))
|
||||
if (cacheResponse == null)
|
||||
{
|
||||
//no stale record or empty record found; cache new empty record to avoid frequent retries by the resolver
|
||||
DnsResponseCode rcode;
|
||||
|
||||
if (cacheResponse == null)
|
||||
rcode = DnsResponseCode.ServerFailure;
|
||||
else
|
||||
rcode = cacheResponse.RCODE;
|
||||
|
||||
DnsDatagram failureResponse = new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, rcode, request.Question);
|
||||
|
||||
_cacheZoneManager.CacheResponse(failureResponse);
|
||||
|
||||
queryHandle.Set(failureResponse);
|
||||
//no stale record was found; signal null response to release waiting threads
|
||||
queryHandle.Set(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1844,9 +1855,16 @@ namespace DnsServerCore.Dns
|
||||
AuthZoneInfo zoneInfo = _authZoneManager.GetAuthZoneInfo(query.Key.Name);
|
||||
if (zoneInfo != null)
|
||||
{
|
||||
//zone is hosted
|
||||
if (!zoneInfo.Disabled)
|
||||
continue; //no cache refresh for zone that is hosted and enabled
|
||||
switch (zoneInfo.Type)
|
||||
{
|
||||
case AuthZoneType.Primary:
|
||||
case AuthZoneType.Secondary:
|
||||
//zone is hosted
|
||||
if (!zoneInfo.Disabled)
|
||||
continue; //no cache refresh for zone that is hosted and enabled
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.Key.Type == DnsResourceRecordType.ANY)
|
||||
@@ -1903,7 +1921,7 @@ namespace DnsServerCore.Dns
|
||||
{
|
||||
//refresh cache
|
||||
DnsDatagram request = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, true, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { sampleQuestion });
|
||||
DnsDatagram response = ProcessRecursiveQuery(request, false, null, null, true);
|
||||
DnsDatagram response = ProcessRecursiveQuery(request, null, null, false, true);
|
||||
|
||||
bool removeFromSampleList = true;
|
||||
|
||||
@@ -2449,6 +2467,9 @@ namespace DnsServerCore.Dns
|
||||
public CacheZoneManager CacheZoneManager
|
||||
{ get { return _cacheZoneManager; } }
|
||||
|
||||
public IDnsCache DnsCache
|
||||
{ get { return _dnsCache; } }
|
||||
|
||||
public bool AllowRecursion
|
||||
{
|
||||
get { return _allowRecursion; }
|
||||
|
||||
Reference in New Issue
Block a user