Exemplo n.º 1
0
        public void CacheResponse(DnsDatagram response)
        {
            if (!response.Header.IsResponse)
            {
                return;
            }

            //combine all records in the response
            List <DnsResourceRecord> allRecords = new List <DnsResourceRecord>();

            switch (response.Header.RCODE)
            {
            case DnsResponseCode.NameError:
                if (response.Authority.Length > 0)
                {
                    DnsResourceRecord authority = response.Authority[0];
                    if (authority.Type == DnsResourceRecordType.SOA)
                    {
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsNXRecord(authority));
                            record.SetExpiry();

                            CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record });
                        }
                    }
                }
                break;

            case DnsResponseCode.NoError:
                if ((response.Answer.Length == 0) && (response.Authority.Length > 0))
                {
                    DnsResourceRecord authority = response.Authority[0];
                    if (authority.Type == DnsResourceRecordType.SOA)
                    {
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(authority));
                            record.SetExpiry();

                            CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record });
                        }
                    }
                }
                else
                {
                    allRecords.AddRange(response.Answer);
                }

                break;

            default:
                return;     //nothing to do
            }

            allRecords.AddRange(response.Authority);
            allRecords.AddRange(response.Additional);

            #region group all records by domain and type

            Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntries = new Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > >();

            foreach (DnsResourceRecord record in allRecords)
            {
                Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntries;

                if (cacheEntries.ContainsKey(record.Name))
                {
                    cacheTypeEntries = cacheEntries[record.Name];
                }
                else
                {
                    cacheTypeEntries = new Dictionary <DnsResourceRecordType, List <DnsResourceRecord> >();
                    cacheEntries.Add(record.Name, cacheTypeEntries);
                }

                List <DnsResourceRecord> cacheRREntries;

                if (cacheTypeEntries.ContainsKey(record.Type))
                {
                    cacheRREntries = cacheTypeEntries[record.Type];
                }
                else
                {
                    cacheRREntries = new List <DnsResourceRecord>();
                    cacheTypeEntries.Add(record.Type, cacheRREntries);
                }

                cacheRREntries.Add(record);
            }

            #endregion

            //add grouped entries into cache
            foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntry in cacheEntries)
            {
                string domain = cacheEntry.Key;

                foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntry in cacheEntry.Value)
                {
                    DnsResourceRecordType type    = cacheTypeEntry.Key;
                    DnsResourceRecord[]   records = cacheTypeEntry.Value.ToArray();

                    foreach (DnsResourceRecord record in records)
                    {
                        record.SetExpiry();
                    }

                    CacheEntry(domain, type, records);
                }
            }

            //cache for ANY request
            if (response.Question[0].Type == DnsResourceRecordType.ANY)
            {
                CacheEntry(response.Question[0].Name, DnsResourceRecordType.ANY, response.Answer);
            }
        }
Exemplo n.º 2
0
        public void CacheResponse(DnsDatagram response)
        {
            if (!response.IsResponse || response.Truncation || (response.Question.Count == 0))
            {
                return; //ineligible response
            }
            switch (response.RCODE)
            {
            case DnsResponseCode.NoError:
            case DnsResponseCode.NxDomain:
            case DnsResponseCode.YXDomain:
                //cache response after this switch
                break;

            default:
                //cache as failure record
                foreach (DnsQuestionRecord question in response.Question)
                {
                    DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _failureRecordTtl, new DnsSpecialCacheRecord(response, true));
                    record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);

                    InternalCacheRecords(new DnsResourceRecord[] { record });
                }

                return;
            }

            //combine all records in the response
            List <DnsResourceRecord> cachableRecords = new List <DnsResourceRecord>();

            //get cachable answer records
            foreach (DnsQuestionRecord question in response.Question)
            {
                string qName = question.Name;

                foreach (DnsResourceRecord answer in response.Answer)
                {
                    if (answer.Name.Equals(qName, StringComparison.OrdinalIgnoreCase))
                    {
                        cachableRecords.Add(answer);

                        switch (answer.Type)
                        {
                        case DnsResourceRecordType.CNAME:
                            qName = (answer.RDATA as DnsCNAMERecord).Domain;
                            break;

                        case DnsResourceRecordType.NS:
                            if (response.Authority.Count == 0)
                            {
                                //add glue from additional section
                                string nsDomain = (answer.RDATA as DnsNSRecord).NameServer;

                                foreach (DnsResourceRecord additional in response.Additional)
                                {
                                    if (nsDomain.Equals(additional.Name, StringComparison.OrdinalIgnoreCase))
                                    {
                                        switch (additional.Type)
                                        {
                                        case DnsResourceRecordType.A:
                                            if (IPAddress.IsLoopback((additional.RDATA as DnsARecord).Address))
                                            {
                                                continue;
                                            }

                                            break;

                                        case DnsResourceRecordType.AAAA:
                                            if (IPAddress.IsLoopback((additional.RDATA as DnsAAAARecord).Address))
                                            {
                                                continue;
                                            }

                                            break;
                                        }

                                        AddGlueRecordTo(answer, additional);
                                    }
                                }
                            }
                            break;

                        case DnsResourceRecordType.MX:
                            if (response.Authority.Count == 0)
                            {
                                //add glue from additional section
                                string mxExchange = (answer.RDATA as DnsMXRecord).Exchange;

                                foreach (DnsResourceRecord additional in response.Additional)
                                {
                                    if (mxExchange.Equals(additional.Name, StringComparison.OrdinalIgnoreCase))
                                    {
                                        AddGlueRecordTo(answer, additional);
                                    }
                                }
                            }
                            break;

                        case DnsResourceRecordType.SRV:
                            if (response.Authority.Count == 0)
                            {
                                //add glue from additional section
                                string srvTarget = (answer.RDATA as DnsSRVRecord).Target;

                                foreach (DnsResourceRecord additional in response.Additional)
                                {
                                    if (srvTarget.Equals(additional.Name, StringComparison.OrdinalIgnoreCase))
                                    {
                                        AddGlueRecordTo(answer, additional);
                                    }
                                }
                            }
                            break;
                        }
                    }
                    else if ((answer.Type == DnsResourceRecordType.DNAME) && qName.EndsWith("." + answer.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        cachableRecords.Add(answer);
                    }
                }
            }

            //get cachable authority records
            if (response.Authority.Count > 0)
            {
                foreach (DnsResourceRecord authority in response.Authority)
                {
                    authority.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);
                }

                DnsResourceRecord firstAuthority = response.Authority[0];
                if (firstAuthority.Type == DnsResourceRecordType.SOA)
                {
                    if (response.Answer.Count == 0)
                    {
                        //empty response with authority
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, (firstAuthority.RDATA as DnsSOARecord).Minimum, new DnsSpecialCacheRecord(response));
                            record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);

                            InternalCacheRecords(new DnsResourceRecord[] { record });
                        }
                    }
                    else
                    {
                        //answer response with authority
                        DnsResourceRecord lastAnswer = response.Answer[response.Answer.Count - 1];
                        if (lastAnswer.Type == DnsResourceRecordType.CNAME)
                        {
                            if ((response.RCODE != DnsResponseCode.NxDomain) || (response.Answer.Count == 1))
                            {
                                //negative cache only when RCODE is not NXDOMAIN or when RCODE is NXDOMAIN and there is only 1 CNAME in answer
                                foreach (DnsQuestionRecord question in response.Question)
                                {
                                    DnsResourceRecord record = new DnsResourceRecord((lastAnswer.RDATA as DnsCNAMERecord).Domain, question.Type, question.Class, (firstAuthority.RDATA as DnsSOARecord).Minimum, new DnsSpecialCacheRecord(response));
                                    record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);

                                    InternalCacheRecords(new DnsResourceRecord[] { record });
                                }
                            }
                        }
                    }
                }
                else if (firstAuthority.Type == DnsResourceRecordType.NS)
                {
                    if (response.Answer.Count == 0)
                    {
                        //response is probably referral response
                        bool isReferralResponse = true;

                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            foreach (DnsResourceRecord authority in response.Authority)
                            {
                                if ((authority.Type == DnsResourceRecordType.NS) && (authority.RDATA as DnsNSRecord).NameServer.Equals(response.Metadata.NameServerAddress.Host, StringComparison.OrdinalIgnoreCase))
                                {
                                    //empty response from authority name server that was queried
                                    DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsSpecialCacheRecord(response));
                                    record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);

                                    InternalCacheRecords(new DnsResourceRecord[] { record });
                                    isReferralResponse = false;
                                    break;
                                }
                            }
                        }

                        if (isReferralResponse)
                        {
                            //cache and glue suitable NS records
                            foreach (DnsResourceRecord authority in response.Authority)
                            {
                                if (authority.Type != DnsResourceRecordType.NS)
                                {
                                    continue;
                                }

                                cachableRecords.Add(authority);

                                //add glue from additional section
                                string nsDomain = (authority.RDATA as DnsNSRecord).NameServer;

                                foreach (DnsResourceRecord additional in response.Additional)
                                {
                                    if (nsDomain.Equals(additional.Name, StringComparison.OrdinalIgnoreCase))
                                    {
                                        switch (additional.Type)
                                        {
                                        case DnsResourceRecordType.A:
                                            if (IPAddress.IsLoopback((additional.RDATA as DnsARecord).Address))
                                            {
                                                continue;
                                            }

                                            break;

                                        case DnsResourceRecordType.AAAA:
                                            if (IPAddress.IsLoopback((additional.RDATA as DnsAAAARecord).Address))
                                            {
                                                continue;
                                            }

                                            break;
                                        }

                                        AddGlueRecordTo(authority, additional);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                //no authority records
                if (response.Answer.Count == 0)
                {
                    //empty response with no authority
                    foreach (DnsQuestionRecord question in response.Question)
                    {
                        DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsSpecialCacheRecord(response));
                        record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);

                        InternalCacheRecords(new DnsResourceRecord[] { record });
                    }
                }
            }

            if (cachableRecords.Count < 1)
            {
                return; //nothing to cache
            }
            //set expiry for cached records
            foreach (DnsResourceRecord record in cachableRecords)
            {
                record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);

                foreach (DnsResourceRecord glueRecord in GetGlueRecordsFrom(record))
                {
                    glueRecord.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl);
                }
            }

            InternalCacheRecords(cachableRecords);
        }
Exemplo n.º 3
0
        public void CacheResponse(DnsDatagram response)
        {
            if (!response.Header.IsResponse || response.Header.Truncation || (response.Question.Length == 0))
            {
                return; //ineligible response
            }
            switch (response.Header.RCODE)
            {
            case DnsResponseCode.NoError:
            case DnsResponseCode.NameError:
                //cache response after this switch
                break;

            default:
                //cache as failure record with RCODE
                foreach (DnsQuestionRecord question in response.Question)
                {
                    DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsFailureRecord(response.Header.RCODE));
                    record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                    CacheRecords(new DnsResourceRecord[] { record });
                }

                return;
            }

            //cache only NoError and NameError responses

            //combine all records in the response
            List <DnsResourceRecord> cachableRecords = new List <DnsResourceRecord>();

            //get cachable answer records
            foreach (DnsQuestionRecord question in response.Question)
            {
                string qName = question.Name;

                foreach (DnsResourceRecord answer in response.Answer)
                {
                    if (answer.Name.Equals(qName, StringComparison.OrdinalIgnoreCase))
                    {
                        cachableRecords.Add(answer);

                        switch (answer.Type)
                        {
                        case DnsResourceRecordType.CNAME:
                            qName = (answer.RDATA as DnsCNAMERecord).CNAMEDomainName;
                            break;

                        case DnsResourceRecordType.NS:
                            string nsDomain = (answer.RDATA as DnsNSRecord).NSDomainName;

                            if (!nsDomain.EndsWith(".root-servers.net", StringComparison.OrdinalIgnoreCase))
                            {
                                foreach (DnsResourceRecord record in response.Additional)
                                {
                                    if (nsDomain.Equals(record.Name, StringComparison.OrdinalIgnoreCase))
                                    {
                                        cachableRecords.Add(record);
                                    }
                                }
                            }

                            break;

                        case DnsResourceRecordType.MX:
                            string mxExchange = (answer.RDATA as DnsMXRecord).Exchange;

                            foreach (DnsResourceRecord record in response.Additional)
                            {
                                if (mxExchange.Equals(record.Name, StringComparison.OrdinalIgnoreCase))
                                {
                                    cachableRecords.Add(record);
                                }
                            }

                            break;
                        }
                    }
                }
            }

            //get cachable authority records
            if (response.Authority.Length > 0)
            {
                DnsResourceRecord authority = response.Authority[0];
                if (authority.Type == DnsResourceRecordType.SOA)
                {
                    authority.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                    if (response.Answer.Length == 0)
                    {
                        //empty response with authority
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            if (question.Name.Equals(authority.Name, StringComparison.OrdinalIgnoreCase) || question.Name.EndsWith("." + authority.Name, StringComparison.OrdinalIgnoreCase) || authority.Name.Equals("", StringComparison.OrdinalIgnoreCase))
                            {
                                DnsResourceRecord record = null;

                                switch (response.Header.RCODE)
                                {
                                case DnsResponseCode.NameError:
                                    record = new DnsResourceRecord(question.Name, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsNXRecord(authority));
                                    break;

                                case DnsResponseCode.NoError:
                                    record = new DnsResourceRecord(question.Name, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsEmptyRecord(authority));
                                    break;
                                }

                                if (record != null)
                                {
                                    record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                                    CacheRecords(new DnsResourceRecord[] { record });
                                }
                            }
                        }
                    }
                    else
                    {
                        //answer response with authority
                        DnsResourceRecord lastAnswer = response.Answer[response.Answer.Length - 1];
                        if (lastAnswer.Type == DnsResourceRecordType.CNAME)
                        {
                            foreach (DnsQuestionRecord question in response.Question)
                            {
                                if (question.Name.Equals(authority.Name, StringComparison.OrdinalIgnoreCase) || question.Name.EndsWith("." + authority.Name, StringComparison.OrdinalIgnoreCase))
                                {
                                    DnsResourceRecord record = null;

                                    switch (response.Header.RCODE)
                                    {
                                    case DnsResponseCode.NameError:
                                        record = new DnsResourceRecord((lastAnswer.RDATA as DnsCNAMERecord).CNAMEDomainName, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsNXRecord(authority));
                                        break;

                                    case DnsResponseCode.NoError:
                                        record = new DnsResourceRecord((lastAnswer.RDATA as DnsCNAMERecord).CNAMEDomainName, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsEmptyRecord(authority));
                                        break;
                                    }

                                    if (record != null)
                                    {
                                        record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                                        CacheRecords(new DnsResourceRecord[] { record });
                                    }

                                    break;
                                }
                            }
                        }
                    }
                }
                else if (authority.Type == DnsResourceRecordType.NS)
                {
                    if (response.Answer.Length == 0)
                    {
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            foreach (DnsResourceRecord authorityRecord in response.Authority)
                            {
                                if ((authorityRecord.Type == DnsResourceRecordType.NS) && (authorityRecord.RDATA as DnsNSRecord).NSDomainName.Equals(response.Metadata.NameServerAddress.Host, StringComparison.OrdinalIgnoreCase))
                                {
                                    //empty response from authority name server that was queried
                                    DnsResourceRecord record = null;

                                    switch (response.Header.RCODE)
                                    {
                                    case DnsResponseCode.NameError:
                                        record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsNXRecord(authority));
                                        break;

                                    case DnsResponseCode.NoError:
                                        record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsEmptyRecord(authority));
                                        break;
                                    }

                                    if (record != null)
                                    {
                                        record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                                        CacheRecords(new DnsResourceRecord[] { record });
                                    }

                                    break;
                                }
                            }
                        }
                    }

                    //cache suitable NS records
                    if ((response.Question[0].Type != DnsResourceRecordType.NS) || (response.Answer.Length == 0))
                    {
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            foreach (DnsResourceRecord authorityRecords in response.Authority)
                            {
                                if ((authorityRecords.Type == DnsResourceRecordType.NS) && (question.Name.Equals(authorityRecords.Name, StringComparison.OrdinalIgnoreCase) || question.Name.EndsWith("." + authorityRecords.Name, StringComparison.OrdinalIgnoreCase)))
                                {
                                    cachableRecords.Add(authorityRecords);

                                    string nsDomain = (authorityRecords.RDATA as DnsNSRecord).NSDomainName;
                                    if (!nsDomain.EndsWith(".root-servers.net", StringComparison.OrdinalIgnoreCase))
                                    {
                                        foreach (DnsResourceRecord record in response.Additional)
                                        {
                                            if (nsDomain.Equals(record.Name, StringComparison.OrdinalIgnoreCase))
                                            {
                                                cachableRecords.Add(record);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                //no authority records
                if (response.Answer.Length == 0)
                {
                    //empty response with no authority
                    foreach (DnsQuestionRecord question in response.Question)
                    {
                        DnsResourceRecord record = null;

                        switch (response.Header.RCODE)
                        {
                        case DnsResponseCode.NameError:
                            record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsNXRecord(null));
                            break;

                        case DnsResponseCode.NoError:
                            record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsEmptyRecord(null));
                            break;
                        }

                        if (record != null)
                        {
                            record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                            CacheRecords(new DnsResourceRecord[] { record });
                        }
                    }
                }
            }

            //cache for ANY request
            if (response.Header.RCODE == DnsResponseCode.NoError)
            {
                if ((response.Question.Length == 1) && (response.Question[0].Type == DnsResourceRecordType.ANY))
                {
                    DnsResourceRecord record = new DnsResourceRecord(response.Question[0].Name, DnsResourceRecordType.ANY, response.Question[0].Class, _negativeRecordTtl, new DnsANYRecord(response.Answer));
                    record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                    CacheRecords(new DnsResourceRecord[] { record });
                }
                else
                {
                    foreach (DnsQuestionRecord question in response.Question)
                    {
                        if (question.Type == DnsResourceRecordType.ANY)
                        {
                            List <DnsResourceRecord> answerRecords = new List <DnsResourceRecord>();

                            foreach (DnsResourceRecord answerRecord in response.Answer)
                            {
                                if (answerRecord.Name.Equals(question.Name, StringComparison.OrdinalIgnoreCase))
                                {
                                    answerRecords.Add(answerRecord);
                                }
                            }

                            DnsResourceRecord record = new DnsResourceRecord(question.Name, DnsResourceRecordType.ANY, question.Class, _negativeRecordTtl, new DnsANYRecord(answerRecords.ToArray()));
                            record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);

                            CacheRecords(new DnsResourceRecord[] { record });
                        }
                    }
                }
            }

            if (cachableRecords.Count < 1)
            {
                return; //nothing to cache
            }
            //set expiry for cached records
            foreach (DnsResourceRecord record in cachableRecords)
            {
                record.SetExpiry(_minimumRecordTtl, _serveStaleTtl);
            }

            CacheRecords(cachableRecords);
        }
Exemplo n.º 4
0
        public void CacheResponse(DnsDatagram response)
        {
            if (!response.Header.IsResponse)
            {
                return;
            }

            //combine all records in the response
            List <DnsResourceRecord> allRecords = new List <DnsResourceRecord>();

            switch (response.Header.RCODE)
            {
            case DnsResponseCode.NameError:
                if (response.Authority.Length > 0)
                {
                    DnsResourceRecord authority = response.Authority[0];
                    if (authority.Type == DnsResourceRecordType.SOA)
                    {
                        authority.SetExpiry(MINIMUM_RECORD_TTL, 0u);

                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsNXRecord(authority));
                            record.SetExpiry(MINIMUM_RECORD_TTL, 0u);

                            CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record });
                        }
                    }
                }
                break;

            case DnsResponseCode.NoError:
                if (response.Answer.Length > 0)
                {
                    foreach (DnsQuestionRecord question in response.Question)
                    {
                        string qName = question.Name;

                        foreach (DnsResourceRecord answer in response.Answer)
                        {
                            if (answer.Name.Equals(qName, StringComparison.CurrentCultureIgnoreCase))
                            {
                                allRecords.Add(answer);

                                switch (answer.Type)
                                {
                                case DnsResourceRecordType.CNAME:
                                    qName = (answer.RDATA as DnsCNAMERecord).CNAMEDomainName;
                                    break;

                                case DnsResourceRecordType.NS:
                                    string nsDomain = (answer.RDATA as DnsNSRecord).NSDomainName;

                                    if (!nsDomain.EndsWith(".root-servers.net", StringComparison.CurrentCultureIgnoreCase))
                                    {
                                        foreach (DnsResourceRecord record in response.Additional)
                                        {
                                            if (nsDomain.Equals(record.Name, StringComparison.CurrentCultureIgnoreCase))
                                            {
                                                allRecords.Add(record);
                                            }
                                        }
                                    }

                                    break;

                                case DnsResourceRecordType.MX:
                                    string mxExchange = (answer.RDATA as DnsMXRecord).Exchange;

                                    foreach (DnsResourceRecord record in response.Additional)
                                    {
                                        if (mxExchange.Equals(record.Name, StringComparison.CurrentCultureIgnoreCase))
                                        {
                                            allRecords.Add(record);
                                        }
                                    }

                                    break;
                                }
                            }
                        }
                    }
                }
                else if (response.Authority.Length > 0)
                {
                    DnsResourceRecord authority = response.Authority[0];
                    if (authority.Type == DnsResourceRecordType.SOA)
                    {
                        authority.SetExpiry(MINIMUM_RECORD_TTL, 0u);

                        //empty response with authority
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(authority));
                            record.SetExpiry(MINIMUM_RECORD_TTL, 0u);

                            CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record });
                        }
                    }
                    else
                    {
                        foreach (DnsQuestionRecord question in response.Question)
                        {
                            foreach (DnsResourceRecord authorityRecord in response.Authority)
                            {
                                if ((authorityRecord.Type == DnsResourceRecordType.NS) && question.Name.Equals(authorityRecord.Name, StringComparison.CurrentCultureIgnoreCase) && (authorityRecord.RDATA as DnsNSRecord).NSDomainName.Equals(response.Metadata.NameServerAddress.Host, StringComparison.CurrentCultureIgnoreCase))
                                {
                                    //empty response from authority name server
                                    DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(null));
                                    record.SetExpiry(MINIMUM_RECORD_TTL, 0u);

                                    CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record });
                                    break;
                                }
                            }
                        }
                    }
                }
                else
                {
                    //empty response with no authority
                    foreach (DnsQuestionRecord question in response.Question)
                    {
                        DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(null));
                        record.SetExpiry(MINIMUM_RECORD_TTL, 0u);

                        CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record });
                    }
                }

                break;

            default:
                return;     //nothing to do
            }

            if ((response.Question.Length > 0) && (response.Question[0].Type != DnsResourceRecordType.NS))
            {
                foreach (DnsQuestionRecord question in response.Question)
                {
                    foreach (DnsResourceRecord authority in response.Authority)
                    {
                        if (question.Name.Equals(authority.Name, StringComparison.CurrentCultureIgnoreCase) || question.Name.EndsWith("." + authority.Name, StringComparison.CurrentCultureIgnoreCase))
                        {
                            allRecords.Add(authority);

                            if (authority.Type == DnsResourceRecordType.NS)
                            {
                                string nsDomain = (authority.RDATA as DnsNSRecord).NSDomainName;

                                if (!nsDomain.EndsWith(".root-servers.net", StringComparison.CurrentCultureIgnoreCase))
                                {
                                    foreach (DnsResourceRecord record in response.Additional)
                                    {
                                        if (nsDomain.Equals(record.Name, StringComparison.CurrentCultureIgnoreCase))
                                        {
                                            allRecords.Add(record);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            //set expiry for cached records
            foreach (DnsResourceRecord record in allRecords)
            {
                record.SetExpiry(MINIMUM_RECORD_TTL, 0u);
            }

            #region group all records by domain and type

            Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntries = new Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > >();

            foreach (DnsResourceRecord record in allRecords)
            {
                Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntries;

                if (cacheEntries.ContainsKey(record.Name))
                {
                    cacheTypeEntries = cacheEntries[record.Name];
                }
                else
                {
                    cacheTypeEntries = new Dictionary <DnsResourceRecordType, List <DnsResourceRecord> >();
                    cacheEntries.Add(record.Name, cacheTypeEntries);
                }

                List <DnsResourceRecord> cacheRREntries;

                if (cacheTypeEntries.ContainsKey(record.Type))
                {
                    cacheRREntries = cacheTypeEntries[record.Type];
                }
                else
                {
                    cacheRREntries = new List <DnsResourceRecord>();
                    cacheTypeEntries.Add(record.Type, cacheRREntries);
                }

                cacheRREntries.Add(record);
            }

            #endregion

            //add grouped entries into cache
            foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntry in cacheEntries)
            {
                string domain = cacheEntry.Key;

                foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntry in cacheEntry.Value)
                {
                    DnsResourceRecordType type    = cacheTypeEntry.Key;
                    DnsResourceRecord[]   records = cacheTypeEntry.Value.ToArray();

                    CacheEntry(domain, type, records);
                }
            }

            //cache for ANY request
            if (response.Question[0].Type == DnsResourceRecordType.ANY)
            {
                CacheEntry(response.Question[0].Name, DnsResourceRecordType.ANY, response.Answer);
            }
        }