public AllowedZoneManager(DnsServer dnsServer) { _dnsServer = dnsServer; _zoneManager = new AuthZoneManager(_dnsServer); UpdateServerDomain(_dnsServer.ServerDomain); }
private static bool IsZoneAllowed(Dictionary <string, object> allowedDomains, string domain) { do { if (allowedDomains.TryGetValue(domain, out _)) { return(true); } domain = AuthZoneManager.GetParentZone(domain); }while (domain is not null); return(false); }
private List <Uri> IsZoneBlocked(string domain, out string blockedDomain) { domain = domain.ToLower(); do { if (_blockListZone.TryGetValue(domain, out List <Uri> blockLists)) { //found zone blocked blockedDomain = domain; return(blockLists); } domain = AuthZoneManager.GetParentZone(domain); }while (domain is not null); blockedDomain = null; return(null); }
public DnsDatagram Query(DnsDatagram request) { DnsQuestionRecord question = request.Question[0]; List <Uri> blockLists = IsZoneBlocked(question.Name, out string blockedDomain); if (blockLists is null) { return(null); //zone not blocked } //zone is blocked if (_dnsServer.AllowTxtBlockingReport && (question.Type == DnsResourceRecordType.TXT)) { //return meta data DnsResourceRecord[] answer = new DnsResourceRecord[blockLists.Count]; for (int i = 0; i < answer.Length; i++) { answer[i] = new DnsResourceRecord(question.Name, DnsResourceRecordType.TXT, question.Class, 60, new DnsTXTRecordData("source=block-list-zone; blockListUrl=" + blockLists[i].AbsoluteUri + "; domain=" + blockedDomain)); } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, answer)); } else { IReadOnlyCollection <DnsARecordData> aRecords; IReadOnlyCollection <DnsAAAARecordData> aaaaRecords; switch (_dnsServer.BlockingType) { case DnsServerBlockingType.AnyAddress: aRecords = _aRecords; aaaaRecords = _aaaaRecords; break; case DnsServerBlockingType.CustomAddress: aRecords = _dnsServer.CustomBlockingARecords; aaaaRecords = _dnsServer.CustomBlockingAAAARecords; break; case DnsServerBlockingType.NxDomain: string parentDomain = AuthZoneManager.GetParentZone(blockedDomain); if (parentDomain is null) { parentDomain = string.Empty; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NxDomain, request.Question, null, new DnsResourceRecord[] { new DnsResourceRecord(parentDomain, DnsResourceRecordType.SOA, question.Class, 60, _soaRecord) })); default: throw new InvalidOperationException(); } IReadOnlyList <DnsResourceRecord> answer = null; IReadOnlyList <DnsResourceRecord> authority = null; switch (question.Type) { case DnsResourceRecordType.A: { List <DnsResourceRecord> rrList = new List <DnsResourceRecord>(aRecords.Count); foreach (DnsARecordData record in aRecords) { rrList.Add(new DnsResourceRecord(question.Name, DnsResourceRecordType.A, question.Class, 60, record)); } answer = rrList; } break; case DnsResourceRecordType.AAAA: { List <DnsResourceRecord> rrList = new List <DnsResourceRecord>(aaaaRecords.Count); foreach (DnsAAAARecordData record in aaaaRecords) { rrList.Add(new DnsResourceRecord(question.Name, DnsResourceRecordType.AAAA, question.Class, 60, record)); } answer = rrList; } break; case DnsResourceRecordType.NS: if (question.Name.Equals(blockedDomain, StringComparison.OrdinalIgnoreCase)) { answer = new DnsResourceRecord[] { new DnsResourceRecord(blockedDomain, DnsResourceRecordType.NS, question.Class, 60, _nsRecord) } } ; else { authority = new DnsResourceRecord[] { new DnsResourceRecord(blockedDomain, DnsResourceRecordType.SOA, question.Class, 60, _soaRecord) } }; break; case DnsResourceRecordType.SOA: answer = new DnsResourceRecord[] { new DnsResourceRecord(blockedDomain, DnsResourceRecordType.SOA, question.Class, 60, _soaRecord) }; break; default: authority = new DnsResourceRecord[] { new DnsResourceRecord(blockedDomain, DnsResourceRecordType.SOA, question.Class, 60, _soaRecord) }; break; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, answer, authority)); } }
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); }