Ejemplo n.º 1
0
        public void WriteZoneTo(string domain, Stream s)
        {
            List <AuthZone> zones = _root.GetZoneWithSubDomainZones(domain);

            if (zones.Count == 0)
            {
                throw new DnsServerException("Zone was not found: " + domain);
            }

            //serialize zone
            BinaryWriter bW = new BinaryWriter(s);

            bW.Write(Encoding.ASCII.GetBytes("DZ")); //format
            bW.Write((byte)4);                       //version

            //write zone info
            AuthZoneInfo zoneInfo = new AuthZoneInfo(zones[0]);

            if (zoneInfo.Internal)
            {
                throw new InvalidOperationException("Cannot save zones marked as internal.");
            }

            zoneInfo.WriteTo(bW);

            //write all zone records
            List <DnsResourceRecord> records = new List <DnsResourceRecord>();

            foreach (AuthZone zone in zones)
            {
                records.AddRange(zone.ListAllRecords());
            }

            bW.Write(records.Count);

            foreach (DnsResourceRecord record in records)
            {
                record.WriteTo(s);

                DnsResourceRecordInfo rrInfo = record.Tag as DnsResourceRecordInfo;
                if (rrInfo == null)
                {
                    rrInfo = new DnsResourceRecordInfo(); //default info
                }
                rrInfo.WriteTo(bW);
            }
        }
        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);
                }
            }
        }
Ejemplo n.º 3
0
        public void WriteTo(BinaryWriter bW)
        {
            if (_zone == null)
            {
                throw new InvalidOperationException();
            }

            bW.Write((byte)4); //version

            bW.WriteShortString(_name);
            bW.Write((byte)_type);
            bW.Write(_disabled);
            bW.Write((byte)_zoneTransfer);

            if (_zoneTransferNameServers is null)
            {
                bW.Write((byte)0);
            }
            else
            {
                bW.Write(Convert.ToByte(_zoneTransferNameServers.Count));
                foreach (IPAddress nameServer in _zoneTransferNameServers)
                {
                    nameServer.WriteTo(bW);
                }
            }

            bW.Write((byte)_notify);

            if (_notifyNameServers is null)
            {
                bW.Write((byte)0);
            }
            else
            {
                bW.Write(Convert.ToByte(_notifyNameServers.Count));
                foreach (IPAddress nameServer in _notifyNameServers)
                {
                    nameServer.WriteTo(bW);
                }
            }

            switch (_type)
            {
            case AuthZoneType.Primary:
                if (_zoneHistory is null)
                {
                    bW.Write(0);
                }
                else
                {
                    bW.Write(_zoneHistory.Count);

                    foreach (DnsResourceRecord record in _zoneHistory)
                    {
                        record.WriteTo(bW.BaseStream);

                        if (record.Tag is not DnsResourceRecordInfo rrInfo)
                        {
                            rrInfo = new DnsResourceRecordInfo();     //default info
                        }
                        rrInfo.WriteTo(bW);
                    }
                }

                if (_tsigKeyNames is null)
                {
                    bW.Write((byte)0);
                }
                else
                {
                    bW.Write(Convert.ToByte(_tsigKeyNames.Count));

                    foreach (KeyValuePair <string, object> tsigKeyName in _tsigKeyNames)
                    {
                        bW.WriteShortString(tsigKeyName.Key);
                    }
                }

                break;

            case AuthZoneType.Secondary:
                bW.Write(_expiry);

                if (_zoneHistory is null)
                {
                    bW.Write(0);
                }
                else
                {
                    bW.Write(_zoneHistory.Count);

                    foreach (DnsResourceRecord record in _zoneHistory)
                    {
                        record.WriteTo(bW.BaseStream);

                        if (record.Tag is not DnsResourceRecordInfo rrInfo)
                        {
                            rrInfo = new DnsResourceRecordInfo();     //default info
                        }
                        rrInfo.WriteTo(bW);
                    }
                }

                if (_tsigKeyNames is null)
                {
                    bW.Write((byte)0);
                }
                else
                {
                    bW.Write(Convert.ToByte(_tsigKeyNames.Count));

                    foreach (KeyValuePair <string, object> tsigKeyName in _tsigKeyNames)
                    {
                        bW.WriteShortString(tsigKeyName.Key);
                    }
                }

                break;

            case AuthZoneType.Stub:
                bW.Write(_expiry);
                break;
            }
        }
        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);
        }