public AllowedZoneManager(DnsServer dnsServer)
        {
            _dnsServer = dnsServer;

            _zoneManager = new AuthZoneManager(_dnsServer);

            UpdateServerDomain(_dnsServer.ServerDomain);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }