protected override void CacheRecords(IReadOnlyList <DnsResourceRecord> resourceRecords) { if (resourceRecords.Count == 1) { CacheZone zone = _root.GetOrAdd(resourceRecords[0].Name, delegate(string key) { return(new CacheZone(resourceRecords[0].Name)); }); zone.SetRecords(resourceRecords[0].Type, resourceRecords); } else { Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByDomainRecords = DnsResourceRecord.GroupRecords(resourceRecords); //add grouped records foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByTypeRecords in groupedByDomainRecords) { CacheZone zone = _root.GetOrAdd(groupedByTypeRecords.Key, delegate(string key) { return(new CacheZone(groupedByTypeRecords.Key)); }); foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > groupedRecords in groupedByTypeRecords.Value) { zone.SetRecords(groupedRecords.Key, groupedRecords.Value); } } } }
private List <DnsResourceRecord> GetAdditionalRecords(IReadOnlyCollection <DnsResourceRecord> nsRecords, bool serveStale) { List <DnsResourceRecord> additionalRecords = new List <DnsResourceRecord>(); foreach (DnsResourceRecord nsRecord in nsRecords) { if (nsRecord.Type != DnsResourceRecordType.NS) { continue; } CacheZone cacheZone = _root.FindZone((nsRecord.RDATA as DnsNSRecord).NameServer, out _, out _, out _); if (cacheZone != null) { { IReadOnlyList <DnsResourceRecord> records = cacheZone.QueryRecords(DnsResourceRecordType.A, serveStale); if ((records.Count > 0) && (records[0].RDATA is DnsARecord)) { additionalRecords.AddRange(records); } } { IReadOnlyList <DnsResourceRecord> records = cacheZone.QueryRecords(DnsResourceRecordType.AAAA, serveStale); if ((records.Count > 0) && (records[0].RDATA is DnsAAAARecord)) { additionalRecords.AddRange(records); } } } } return(additionalRecords); }
private static IReadOnlyList <DnsResourceRecord> AddDSRecordsTo(CacheZone delegation, bool serveStale, IReadOnlyList <DnsResourceRecord> nsRecords) { IReadOnlyList <DnsResourceRecord> records = delegation.QueryRecords(DnsResourceRecordType.DS, serveStale, true); if ((records.Count > 0) && (records[0].Type == DnsResourceRecordType.DS)) { List <DnsResourceRecord> newNSRecords = new List <DnsResourceRecord>(nsRecords.Count + records.Count); newNSRecords.AddRange(nsRecords); newNSRecords.AddRange(records); return(newNSRecords); } //no DS records found check for NSEC records IReadOnlyList <DnsResourceRecord> nsecRecords = nsRecords[0].GetRecordInfo().NSECRecords; if (nsecRecords is not null) { List <DnsResourceRecord> newNSRecords = new List <DnsResourceRecord>(nsRecords.Count + nsecRecords.Count); newNSRecords.AddRange(nsRecords); newNSRecords.AddRange(nsecRecords); return(newNSRecords); } //found nothing; return original NS records return(nsRecords); }
protected override void CacheRecords(IReadOnlyList <DnsResourceRecord> resourceRecords) { //read and set glue records from base class foreach (DnsResourceRecord resourceRecord in resourceRecords) { IReadOnlyList <DnsResourceRecord> glueRecords = GetGlueRecordsFrom(resourceRecord); if (glueRecords.Count > 0) { resourceRecord.SetGlueRecords(glueRecords); } } if (resourceRecords.Count == 1) { DnsResourceRecord resourceRecord = resourceRecords[0]; if (resourceRecord.Name.Contains('*')) { return; } CacheZone zone = _root.GetOrAdd(resourceRecord.Name, delegate(string key) { return(new CacheZone(resourceRecord.Name, 1)); }); zone.SetRecords(resourceRecord.Type, resourceRecords, _dnsServer.ServeStale); } else { Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByDomainRecords = DnsResourceRecord.GroupRecords(resourceRecords); bool serveStale = _dnsServer.ServeStale; //add grouped records foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByTypeRecords in groupedByDomainRecords) { if (groupedByTypeRecords.Key.Contains('*')) { continue; } CacheZone zone = _root.GetOrAdd(groupedByTypeRecords.Key, delegate(string key) { return(new CacheZone(groupedByTypeRecords.Key, groupedByTypeRecords.Value.Count)); }); foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > groupedRecords in groupedByTypeRecords.Value) { zone.SetRecords(groupedRecords.Key, groupedRecords.Value, serveStale); } } } }
private void ResolveAdditionalRecords(DnsResourceRecord refRecord, string domain, bool serveStale, List <DnsResourceRecord> additionalRecords) { IReadOnlyList <DnsResourceRecord> glueRecords = refRecord.GetGlueRecords(); if (glueRecords.Count > 0) { bool added = false; foreach (DnsResourceRecord glueRecord in glueRecords) { if (!glueRecord.IsStale) { added = true; additionalRecords.Add(glueRecord); } } if (added) { return; } } CacheZone cacheZone = _root.FindZone(domain, out _, out _, out _); if (cacheZone != null) { { IReadOnlyList <DnsResourceRecord> records = cacheZone.QueryRecords(DnsResourceRecordType.A, serveStale, true); if ((records.Count > 0) && (records[0].RDATA is DnsARecord)) { additionalRecords.AddRange(records); } } { IReadOnlyList <DnsResourceRecord> records = cacheZone.QueryRecords(DnsResourceRecordType.AAAA, serveStale, true); if ((records.Count > 0) && (records[0].RDATA is DnsAAAARecord)) { additionalRecords.AddRange(records); } } } }
private void ResolveAdditionalRecords(string domain, bool serveStale, List <DnsResourceRecord> additionalRecords) { CacheZone cacheZone = _root.FindZone(domain, out _, out _, out _); if (cacheZone != null) { { IReadOnlyList <DnsResourceRecord> records = cacheZone.QueryRecords(DnsResourceRecordType.A, serveStale); if ((records.Count > 0) && (records[0].RDATA is DnsARecord)) { additionalRecords.AddRange(records); } } { IReadOnlyList <DnsResourceRecord> records = cacheZone.QueryRecords(DnsResourceRecordType.AAAA, serveStale); if ((records.Count > 0) && (records[0].RDATA is DnsAAAARecord)) { additionalRecords.AddRange(records); } } } }
public override DnsDatagram Query(DnsDatagram request, bool serveStale = false) { CacheZone zone = _root.FindZone(request.Question[0].Name, out CacheZone delegation, out _, out _); if (zone == null) { //zone not found if (delegation == null) { //no cached delegation found return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.Refused, request.Question)); } //return closest name servers in delegation IReadOnlyList <DnsResourceRecord> authority = delegation.QueryRecords(DnsResourceRecordType.NS, serveStale, true); List <DnsResourceRecord> additional = GetAdditionalRecords(authority, serveStale); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, authority, additional)); } //zone found IReadOnlyList <DnsResourceRecord> answers = zone.QueryRecords(request.Question[0].Type, serveStale, false); if (answers.Count > 0) { //answer found in cache if (answers[0].RDATA is DnsEmptyRecord) { DnsResourceRecord[] authority = null; DnsResourceRecord soaRecord = (answers[0].RDATA as DnsEmptyRecord).Authority; if (soaRecord != null) { authority = new DnsResourceRecord[] { soaRecord } } ; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, authority)); } if (answers[0].RDATA is DnsNXRecord) { DnsResourceRecord[] authority = null; DnsResourceRecord soaRecord = (answers[0].RDATA as DnsNXRecord).Authority; if (soaRecord != null) { authority = new DnsResourceRecord[] { soaRecord } } ; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NameError, request.Question, null, authority)); } if (answers[0].RDATA is DnsANYRecord) { DnsANYRecord anyRR = answers[0].RDATA as DnsANYRecord; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, anyRR.Records)); } if (answers[0].RDATA is DnsFailureRecord) { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, (answers[0].RDATA as DnsFailureRecord).RCODE, request.Question)); } IReadOnlyList <DnsResourceRecord> additional = null; switch (request.Question[0].Type) { case DnsResourceRecordType.NS: case DnsResourceRecordType.MX: case DnsResourceRecordType.SRV: additional = GetAdditionalRecords(answers, serveStale); break; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, answers, null, additional)); } else { //no answer in cache; check for closest delegation if any if (delegation == null) { //no cached delegation found return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.Refused, request.Question)); } //return closest name servers in delegation IReadOnlyList <DnsResourceRecord> authority = delegation.QueryRecords(DnsResourceRecordType.NS, false, true); List <DnsResourceRecord> additional = GetAdditionalRecords(authority, false); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, authority, additional)); } } #endregion } }
public override DnsDatagram Query(DnsDatagram request, bool serveStaleAndResetExpiry = false) { DnsQuestionRecord question = request.Question[0]; CacheZone zone = _root.FindZone(question.Name, out CacheZone delegation, out _, out _); if (zone is null) { //zone not found if (serveStaleAndResetExpiry) { return(null); //recursive resolver does not make stale request so no need to return delegation response } if (delegation is not null) { //return closest name servers in delegation IReadOnlyList <DnsResourceRecord> closestAuthority = delegation.QueryRecords(DnsResourceRecordType.NS, serveStaleAndResetExpiry, true); if ((closestAuthority.Count > 0) && (closestAuthority[0].Type == DnsResourceRecordType.NS) && (closestAuthority[0].Name.Length > 0)) //dont trust root name servers from cache! { IReadOnlyList <DnsResourceRecord> additional = GetAdditionalRecords(closestAuthority, serveStaleAndResetExpiry); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, closestAuthority, additional)); } } //no cached delegation found return(null); } //zone found IReadOnlyList <DnsResourceRecord> answers = zone.QueryRecords(question.Type, serveStaleAndResetExpiry, false); if (answers.Count > 0) { //answer found in cache DnsResourceRecord firstRR = answers[0]; if (firstRR.RDATA is DnsEmptyRecord dnsEmptyRecord) { if (serveStaleAndResetExpiry) { if (firstRR.IsStale) { firstRR.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } if (dnsEmptyRecord.Authority is not null) { foreach (DnsResourceRecord record in dnsEmptyRecord.Authority) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } } } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, dnsEmptyRecord.Authority)); } if (firstRR.RDATA is DnsNXRecord dnsNXRecord) { if (serveStaleAndResetExpiry) { if (firstRR.IsStale) { firstRR.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } if (dnsNXRecord.Authority is not null) { foreach (DnsResourceRecord record in dnsNXRecord.Authority) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } } } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NxDomain, request.Question, null, dnsNXRecord.Authority)); } if (firstRR.RDATA is DnsFailureRecord dnsFailureRecord) { if (serveStaleAndResetExpiry) { if (firstRR.IsStale) { firstRR.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, dnsFailureRecord.RCODE, request.Question)); } DnsResourceRecord lastRR = answers[answers.Count - 1]; if ((lastRR.Type != question.Type) && (lastRR.Type == DnsResourceRecordType.CNAME) && (question.Type != DnsResourceRecordType.ANY)) { List <DnsResourceRecord> newAnswers = new List <DnsResourceRecord>(answers); ResolveCNAME(question, lastRR, serveStaleAndResetExpiry, newAnswers); answers = newAnswers; } IReadOnlyList <DnsResourceRecord> additional = null; switch (question.Type) { case DnsResourceRecordType.NS: case DnsResourceRecordType.MX: case DnsResourceRecordType.SRV: additional = GetAdditionalRecords(answers, serveStaleAndResetExpiry); break; } if (serveStaleAndResetExpiry) { foreach (DnsResourceRecord record in answers) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } if (additional is not null) { foreach (DnsResourceRecord record in additional) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } } } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, answers, null, additional)); } else { //no answer in cache if (serveStaleAndResetExpiry) { return(null); //recursive resolver does not make stale request so no need to return delegation response } //check for closest delegation if any if (delegation is not null) { //return closest name servers in delegation IReadOnlyList <DnsResourceRecord> closestAuthority = delegation.QueryRecords(DnsResourceRecordType.NS, false, true); if ((closestAuthority.Count > 0) && (closestAuthority[0].Type == DnsResourceRecordType.NS) && (closestAuthority[0].Name.Length > 0)) //dont trust root name servers from cache! { IReadOnlyList <DnsResourceRecord> additional = GetAdditionalRecords(closestAuthority, false); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, closestAuthority, additional)); } } //no cached delegation found return(null); } }
public override DnsDatagram Query(DnsDatagram request, bool serveStale = false) { DnsQuestionRecord question = request.Question[0]; CacheZone zone = _root.FindZone(question.Name, out CacheZone delegation, out _, out _); if (zone == null) { //zone not found if (delegation != null) { //return closest name servers in delegation IReadOnlyList <DnsResourceRecord> closestAuthority = delegation.QueryRecords(DnsResourceRecordType.NS, serveStale, true); if ((closestAuthority.Count > 0) && (closestAuthority[0].Type == DnsResourceRecordType.NS)) { IReadOnlyList <DnsResourceRecord> additional = GetAdditionalRecords(closestAuthority, serveStale); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, closestAuthority, additional)); } } //no cached delegation found return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.Refused, request.Question)); } //zone found IReadOnlyList <DnsResourceRecord> answers = zone.QueryRecords(question.Type, serveStale, false); if (answers.Count > 0) { //answer found in cache DnsResourceRecord firstRR = answers[0]; if (firstRR.RDATA is DnsEmptyRecord) { DnsResourceRecord[] authority = null; DnsResourceRecord soaRecord = (firstRR.RDATA as DnsEmptyRecord).Authority; if (soaRecord != null) { authority = new DnsResourceRecord[] { soaRecord } } ; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, authority)); } if (firstRR.RDATA is DnsNXRecord) { DnsResourceRecord[] authority = null; DnsResourceRecord soaRecord = (firstRR.RDATA as DnsNXRecord).Authority; if (soaRecord != null) { authority = new DnsResourceRecord[] { soaRecord } } ; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NxDomain, request.Question, null, authority)); } if (firstRR.RDATA is DnsFailureRecord) { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, (firstRR.RDATA as DnsFailureRecord).RCODE, request.Question)); } DnsResourceRecord lastRR = answers[answers.Count - 1]; if ((lastRR.Type != question.Type) && (lastRR.Type == DnsResourceRecordType.CNAME) && (question.Type != DnsResourceRecordType.ANY)) { List <DnsResourceRecord> newAnswers = new List <DnsResourceRecord>(answers); ResolveCNAME(question, lastRR, serveStale, newAnswers); answers = newAnswers; } IReadOnlyList <DnsResourceRecord> additional = null; switch (question.Type) { case DnsResourceRecordType.NS: case DnsResourceRecordType.MX: case DnsResourceRecordType.SRV: additional = GetAdditionalRecords(answers, serveStale); break; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, answers, null, additional)); } else { //no answer in cache; check for closest delegation if any if (delegation != null) { //return closest name servers in delegation IReadOnlyList <DnsResourceRecord> closestAuthority = delegation.QueryRecords(DnsResourceRecordType.NS, false, true); if ((closestAuthority.Count > 0) && (closestAuthority[0].Type == DnsResourceRecordType.NS)) { IReadOnlyList <DnsResourceRecord> additional = GetAdditionalRecords(closestAuthority, false); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, closestAuthority, additional)); } } //no cached delegation found return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.Refused, request.Question)); } } #endregion } }
protected override void CacheRecords(IReadOnlyList <DnsResourceRecord> resourceRecords) { List <DnsResourceRecord> dnameRecords = null; //read and set glue records from base class; also collect any DNAME records found foreach (DnsResourceRecord resourceRecord in resourceRecords) { IReadOnlyList <DnsResourceRecord> glueRecords = GetGlueRecordsFrom(resourceRecord); IReadOnlyList <DnsResourceRecord> rrsigRecords = GetRRSIGRecordsFrom(resourceRecord); IReadOnlyList <DnsResourceRecord> nsecRecords = GetNSECRecordsFrom(resourceRecord); if ((glueRecords is not null) || (rrsigRecords is not null) || (nsecRecords is not null)) { DnsResourceRecordInfo rrInfo = resourceRecord.GetRecordInfo(); rrInfo.GlueRecords = glueRecords; rrInfo.RRSIGRecords = rrsigRecords; rrInfo.NSECRecords = nsecRecords; if (glueRecords is not null) { foreach (DnsResourceRecord glueRecord in glueRecords) { IReadOnlyList <DnsResourceRecord> glueRRSIGRecords = GetRRSIGRecordsFrom(glueRecord); if (glueRRSIGRecords is not null) { glueRecord.GetRecordInfo().RRSIGRecords = glueRRSIGRecords; } } } if (nsecRecords is not null) { foreach (DnsResourceRecord nsecRecord in nsecRecords) { IReadOnlyList <DnsResourceRecord> nsecRRSIGRecords = GetRRSIGRecordsFrom(nsecRecord); if (nsecRRSIGRecords is not null) { nsecRecord.GetRecordInfo().RRSIGRecords = nsecRRSIGRecords; } } } } if (resourceRecord.Type == DnsResourceRecordType.DNAME) { if (dnameRecords is null) { dnameRecords = new List <DnsResourceRecord>(1); } dnameRecords.Add(resourceRecord); } } if (resourceRecords.Count == 1) { DnsResourceRecord resourceRecord = resourceRecords[0]; CacheZone zone = _root.GetOrAdd(resourceRecord.Name, delegate(string key) { return(new CacheZone(resourceRecord.Name, 1)); }); if (zone.SetRecords(resourceRecord.Type, resourceRecords, _dnsServer.ServeStale)) { Interlocked.Increment(ref _totalEntries); } } else { Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByDomainRecords = DnsResourceRecord.GroupRecords(resourceRecords); bool serveStale = _dnsServer.ServeStale; int addedEntries = 0; //add grouped records foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByTypeRecords in groupedByDomainRecords) { if (dnameRecords is not null) { bool foundSynthesizedCNAME = false; foreach (DnsResourceRecord dnameRecord in dnameRecords) { if (groupedByTypeRecords.Key.EndsWith("." + dnameRecord.Name, StringComparison.OrdinalIgnoreCase)) { foundSynthesizedCNAME = true; break; } } if (foundSynthesizedCNAME) { continue; //do not cache synthesized CNAME } } CacheZone zone = _root.GetOrAdd(groupedByTypeRecords.Key, delegate(string key) { return(new CacheZone(groupedByTypeRecords.Key, groupedByTypeRecords.Value.Count)); }); foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > groupedRecords in groupedByTypeRecords.Value) { if (zone.SetRecords(groupedRecords.Key, groupedRecords.Value, serveStale)) { addedEntries++; } } } if (addedEntries > 0) { Interlocked.Add(ref _totalEntries, addedEntries); } } }
public override DnsDatagram Query(DnsDatagram request, bool serveStaleAndResetExpiry = false, bool findClosestNameServers = false) { DnsQuestionRecord question = request.Question[0]; CacheZone zone; CacheZone closest = null; CacheZone delegation = null; if (findClosestNameServers) { zone = _root.FindZone(question.Name, out closest, out delegation); } else { if (!_root.TryGet(question.Name, out zone)) { _ = _root.FindZone(question.Name, out closest, out _); //zone not found; attempt to find closest } } if (zone is not null) { //zone found IReadOnlyList <DnsResourceRecord> answers = zone.QueryRecords(question.Type, serveStaleAndResetExpiry, false); if (answers.Count > 0) { //answer found in cache DnsResourceRecord firstRR = answers[0]; if (firstRR.RDATA is DnsSpecialCacheRecord dnsSpecialCacheRecord) { IReadOnlyList <EDnsOption> specialOptions = null; if (serveStaleAndResetExpiry) { if (firstRR.IsStale) { firstRR.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } if (dnsSpecialCacheRecord.Authority is not null) { foreach (DnsResourceRecord record in dnsSpecialCacheRecord.Authority) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } } if (dnsSpecialCacheRecord.RCODE == DnsResponseCode.NxDomain) { List <EDnsOption> newOptions = new List <EDnsOption>(dnsSpecialCacheRecord.EDnsOptions.Count + 1); newOptions.AddRange(dnsSpecialCacheRecord.EDnsOptions); newOptions.Add(new EDnsOption(EDnsOptionCode.EXTENDED_DNS_ERROR, new EDnsExtendedDnsErrorOption(EDnsExtendedDnsErrorCode.StaleNxDomainAnswer, null))); specialOptions = newOptions; } } if (specialOptions is null) { specialOptions = dnsSpecialCacheRecord.EDnsOptions; } if (request.DnssecOk) { bool authenticData; switch (dnsSpecialCacheRecord.Type) { case DnsSpecialCacheRecordType.NegativeCache: authenticData = true; break; default: authenticData = false; break; } if (request.CheckingDisabled) { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, authenticData, request.CheckingDisabled, dnsSpecialCacheRecord.OriginalRCODE, request.Question, dnsSpecialCacheRecord.OriginalAnswer, dnsSpecialCacheRecord.OriginalAuthority, dnsSpecialCacheRecord.Additional, _dnsServer.UdpPayloadSize, EDnsHeaderFlags.DNSSEC_OK, specialOptions)); } else { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, authenticData, request.CheckingDisabled, dnsSpecialCacheRecord.RCODE, request.Question, null, dnsSpecialCacheRecord.Authority, null, _dnsServer.UdpPayloadSize, EDnsHeaderFlags.DNSSEC_OK, specialOptions)); } } else { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, dnsSpecialCacheRecord.RCODE, request.Question, null, dnsSpecialCacheRecord.NoDnssecAuthority, null, request.EDNS is null ? ushort.MinValue : _dnsServer.UdpPayloadSize, EDnsHeaderFlags.None, specialOptions)); } } DnsResourceRecord lastRR = answers[answers.Count - 1]; if ((lastRR.Type != question.Type) && (lastRR.Type == DnsResourceRecordType.CNAME) && (question.Type != DnsResourceRecordType.ANY)) { List <DnsResourceRecord> newAnswers = new List <DnsResourceRecord>(answers.Count + 3); newAnswers.AddRange(answers); ResolveCNAME(question, lastRR, serveStaleAndResetExpiry, newAnswers); answers = newAnswers; } IReadOnlyList <DnsResourceRecord> authority = null; EDnsHeaderFlags ednsFlags = EDnsHeaderFlags.None; if (request.DnssecOk) { //DNSSEC enabled; insert RRSIG records List <DnsResourceRecord> newAnswers = new List <DnsResourceRecord>(answers.Count * 2); List <DnsResourceRecord> newAuthority = null; foreach (DnsResourceRecord answer in answers) { newAnswers.Add(answer); DnsResourceRecordInfo rrInfo = answer.GetRecordInfo(); IReadOnlyList <DnsResourceRecord> rrsigRecords = rrInfo.RRSIGRecords; if (rrsigRecords is not null) { newAnswers.AddRange(rrsigRecords); foreach (DnsResourceRecord rrsigRecord in rrsigRecords) { if (!DnsRRSIGRecordData.IsWildcard(rrsigRecord)) { continue; } //add NSEC/NSEC3 for the wildcard proof if (newAuthority is null) { newAuthority = new List <DnsResourceRecord>(2); } IReadOnlyList <DnsResourceRecord> nsecRecords = rrInfo.NSECRecords; if (nsecRecords is not null) { foreach (DnsResourceRecord nsecRecord in nsecRecords) { newAuthority.Add(nsecRecord); IReadOnlyList <DnsResourceRecord> nsecRRSIGRecords = nsecRecord.GetRecordInfo().RRSIGRecords; if (nsecRRSIGRecords is not null) { newAuthority.AddRange(nsecRRSIGRecords); } } } } } } answers = newAnswers; authority = newAuthority; ednsFlags = EDnsHeaderFlags.DNSSEC_OK; } IReadOnlyList <DnsResourceRecord> additional = null; switch (question.Type) { case DnsResourceRecordType.NS: case DnsResourceRecordType.MX: case DnsResourceRecordType.SRV: additional = GetAdditionalRecords(answers, serveStaleAndResetExpiry, request.DnssecOk); break; } EDnsOption[] options = null; if (serveStaleAndResetExpiry) { foreach (DnsResourceRecord record in answers) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } if (additional is not null) { foreach (DnsResourceRecord record in additional) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } } options = new EDnsOption[] { new EDnsOption(EDnsOptionCode.EXTENDED_DNS_ERROR, new EDnsExtendedDnsErrorOption(EDnsExtendedDnsErrorCode.StaleAnswer, null)) }; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, answers[0].DnssecStatus == DnssecStatus.Secure, request.CheckingDisabled, DnsResponseCode.NoError, request.Question, answers, authority, additional, request.EDNS is null ? ushort.MinValue : _dnsServer.UdpPayloadSize, ednsFlags, options)); } } else { //zone not found //check for DNAME in closest zone if (closest is not null) { IReadOnlyList <DnsResourceRecord> answer = closest.QueryRecords(DnsResourceRecordType.DNAME, serveStaleAndResetExpiry, true); if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME)) { DnsResponseCode rCode; if (DoDNAMESubstitution(question, answer, serveStaleAndResetExpiry, out answer)) { rCode = DnsResponseCode.NoError; } else { rCode = DnsResponseCode.YXDomain; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, answer[0].DnssecStatus == DnssecStatus.Secure, request.CheckingDisabled, rCode, request.Question, answer)); } } } //no answer in cache //check for closest delegation if any if (findClosestNameServers && (delegation is not null)) { //return closest name servers in delegation if (question.Type == DnsResourceRecordType.DS) { //find parent delegation string domain = AuthZoneManager.GetParentZone(question.Name); if (domain is null) { return(null); //dont find NS for root } _ = _root.FindZone(domain, out _, out delegation); if (delegation is null) { return(null); //no cached delegation found } } IReadOnlyList <DnsResourceRecord> closestAuthority = delegation.QueryRecords(DnsResourceRecordType.NS, serveStaleAndResetExpiry, true); if ((closestAuthority.Count > 0) && (closestAuthority[0].Type == DnsResourceRecordType.NS) && (closestAuthority[0].Name.Length > 0)) //dont trust root name servers from cache! { if (request.DnssecOk) { closestAuthority = AddDSRecordsTo(delegation, serveStaleAndResetExpiry, closestAuthority); } IReadOnlyList <DnsResourceRecord> additional = GetAdditionalRecords(closestAuthority, serveStaleAndResetExpiry, request.DnssecOk); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, closestAuthority[0].DnssecStatus == DnssecStatus.Secure, request.CheckingDisabled, DnsResponseCode.NoError, request.Question, null, closestAuthority, additional)); } } //no cached delegation found return(null); }