Beispiel #1
0
        private static Zone ParseMasterFile(DomainName name, StreamReader reader)
        {
            List <DnsRecordBase> records = ParseRecords(reader, name, 0, new UnknownRecord(name, RecordType.Invalid, RecordClass.INet, 0, new byte[] { }));

            SoaRecord soa = (SoaRecord)records.SingleOrDefault(x => x.RecordType == RecordType.Soa);

            if (soa != null)
            {
                records.ForEach(x =>
                {
                    if (x.TimeToLive == 0)
                    {
                        x.TimeToLive = soa.NegativeCachingTTL;
                    }
                });
            }

            return(new Zone(name, records));
        }
        private async Task <List <T> > ResolveAsyncInternal <T>(DomainName name, RecordType recordType, RecordClass recordClass, State state, CancellationToken token)
            where T : DnsRecordBase
        {
            List <T> cachedResults;

            if (_cache.TryGetRecords(name, recordType, recordClass, out cachedResults))
            {
                return(cachedResults);
            }

            List <CNameRecord> cachedCNames;

            if (_cache.TryGetRecords(name, RecordType.CName, recordClass, out cachedCNames))
            {
                return(await ResolveAsyncInternal <T>(cachedCNames.First().CanonicalName, recordType, recordClass, state, token));
            }

            DnsMessage msg = await ResolveMessageAsync(name, recordType, recordClass, state, token);

            // check for cname
            List <DnsRecordBase> cNameRecords = msg.AnswerRecords.Where(x => (x.RecordType == RecordType.CName) && (x.RecordClass == recordClass) && x.Name.Equals(name)).ToList();

            if (cNameRecords.Count > 0)
            {
                _cache.Add(name, RecordType.CName, recordClass, cNameRecords, DnsSecValidationResult.Indeterminate, cNameRecords.Min(x => x.TimeToLive));

                DomainName canonicalName = ((CNameRecord)cNameRecords.First()).CanonicalName;

                List <DnsRecordBase> matchingAdditionalRecords = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(canonicalName)).ToList();
                if (matchingAdditionalRecords.Count > 0)
                {
                    _cache.Add(canonicalName, recordType, recordClass, matchingAdditionalRecords, DnsSecValidationResult.Indeterminate, matchingAdditionalRecords.Min(x => x.TimeToLive));
                    return(matchingAdditionalRecords.OfType <T>().ToList());
                }

                return(await ResolveAsyncInternal <T>(canonicalName, recordType, recordClass, state, token));
            }

            // check for "normal" answer
            List <DnsRecordBase> answerRecords = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(name)).ToList();

            if (answerRecords.Count > 0)
            {
                _cache.Add(name, recordType, recordClass, answerRecords, DnsSecValidationResult.Indeterminate, answerRecords.Min(x => x.TimeToLive));
                return(answerRecords.OfType <T>().ToList());
            }

            // check for negative answer
            SoaRecord soaRecord = msg.AuthorityRecords
                                  .Where(x =>
                                         (x.RecordType == RecordType.Soa) &&
                                         (name.Equals(x.Name) || name.IsSubDomainOf(x.Name)))
                                  .OfType <SoaRecord>()
                                  .FirstOrDefault();

            if (soaRecord != null)
            {
                _cache.Add(name, recordType, recordClass, new List <DnsRecordBase>(), DnsSecValidationResult.Indeterminate, soaRecord.NegativeCachingTTL);
                return(new List <T>());
            }

            // authoritive response does not contain answer
            throw new Exception("Could not resolve " + name);
        }
Beispiel #3
0
        private async Task <DnsSecResult <T> > ResolveAsyncInternal <T>(DomainName name, RecordType recordType, RecordClass recordClass, State state, CancellationToken token)
            where T : DnsRecordBase
        {
            DnsCacheRecordList <T> cachedResults;

            if (_cache.TryGetRecords(name, recordType, recordClass, out cachedResults))
            {
                return(new DnsSecResult <T>(cachedResults, cachedResults.ValidationResult));
            }

            DnsCacheRecordList <CNameRecord> cachedCNames;

            if (_cache.TryGetRecords(name, RecordType.CName, recordClass, out cachedCNames))
            {
                var cNameResult = await ResolveAsyncInternal <T>(cachedCNames.First().CanonicalName, recordType, recordClass, state, token);

                return(new DnsSecResult <T>(cNameResult.Records, cachedCNames.ValidationResult == cNameResult.ValidationResult ? cachedCNames.ValidationResult : DnsSecValidationResult.Unsigned));
            }

            DnsMessage msg = await ResolveMessageAsync(name, recordType, recordClass, state, token);

            // check for cname
            List <DnsRecordBase> cNameRecords = msg.AnswerRecords.Where(x => (x.RecordType == RecordType.CName) && (x.RecordClass == recordClass) && x.Name.Equals(name)).ToList();

            if (cNameRecords.Count > 0)
            {
                DnsSecValidationResult cNameValidationResult = await _validator.ValidateAsync(name, RecordType.CName, recordClass, msg, cNameRecords, state, token);

                if ((cNameValidationResult == DnsSecValidationResult.Bogus) || (cNameValidationResult == DnsSecValidationResult.Indeterminate))
                {
                    throw new DnsSecValidationException("CNAME record could not be validated");
                }

                _cache.Add(name, RecordType.CName, recordClass, cNameRecords, cNameValidationResult, cNameRecords.Min(x => x.TimeToLive));

                DomainName canonicalName = ((CNameRecord)cNameRecords.First()).CanonicalName;

                List <DnsRecordBase> matchingAdditionalRecords = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(canonicalName)).ToList();
                if (matchingAdditionalRecords.Count > 0)
                {
                    DnsSecValidationResult matchingValidationResult = await _validator.ValidateAsync(canonicalName, recordType, recordClass, msg, matchingAdditionalRecords, state, token);

                    if ((matchingValidationResult == DnsSecValidationResult.Bogus) || (matchingValidationResult == DnsSecValidationResult.Indeterminate))
                    {
                        throw new DnsSecValidationException("CNAME matching records could not be validated");
                    }

                    DnsSecValidationResult validationResult = cNameValidationResult == matchingValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned;
                    _cache.Add(canonicalName, recordType, recordClass, matchingAdditionalRecords, validationResult, matchingAdditionalRecords.Min(x => x.TimeToLive));

                    return(new DnsSecResult <T>(matchingAdditionalRecords.OfType <T>().ToList(), validationResult));
                }

                var cNameResults = await ResolveAsyncInternal <T>(canonicalName, recordType, recordClass, state, token);

                return(new DnsSecResult <T>(cNameResults.Records, cNameValidationResult == cNameResults.ValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned));
            }

            // check for "normal" answer
            List <DnsRecordBase> answerRecords = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(name)).ToList();

            if (answerRecords.Count > 0)
            {
                DnsSecValidationResult validationResult = await _validator.ValidateAsync(name, recordType, recordClass, msg, answerRecords, state, token);

                if ((validationResult == DnsSecValidationResult.Bogus) || (validationResult == DnsSecValidationResult.Indeterminate))
                {
                    throw new DnsSecValidationException("Response records could not be validated");
                }

                _cache.Add(name, recordType, recordClass, answerRecords, validationResult, answerRecords.Min(x => x.TimeToLive));
                return(new DnsSecResult <T>(answerRecords.OfType <T>().ToList(), validationResult));
            }

            // check for negative answer
            SoaRecord soaRecord = msg.AuthorityRecords
                                  .Where(x =>
                                         (x.RecordType == RecordType.Soa) &&
                                         (name.Equals(x.Name) || name.IsSubDomainOf(x.Name)))
                                  .OfType <SoaRecord>()
                                  .FirstOrDefault();

            if (soaRecord != null)
            {
                DnsSecValidationResult validationResult = await _validator.ValidateAsync(name, recordType, recordClass, msg, answerRecords, state, token);

                if ((validationResult == DnsSecValidationResult.Bogus) || (validationResult == DnsSecValidationResult.Indeterminate))
                {
                    throw new DnsSecValidationException("Negative answer could not be validated");
                }

                _cache.Add(name, recordType, recordClass, new List <DnsRecordBase>(), validationResult, soaRecord.NegativeCachingTTL);
                return(new DnsSecResult <T>(new List <T>(), validationResult));
            }

            // authoritive response does not contain answer
            throw new Exception("Could not resolve " + name);
        }
        } // End Class DbDnsRecord


        static async Task <bool> ResolveMessage(DnsMessage query, QueryReceivedEventArgs e, DnsMessage response)
        {
            DbDnsRecord rec = null;

            using (System.Data.Common.DbCommand cmd = s_sql.CreateCommand(@"
-- DECLARE @in_recordType int 
-- DECLARE @in_recordName varchar(4000) 

-- SET @in_recordType = 1 -- A 
-- SET @in_recordName = 'vortex.data.microsoft.com' 


SELECT 
	 REC_Id
	--,T_Records.REC_DOM_Id
	,T_Records.REC_RT_Id
	,T_Records.REC_Name
	,T_Records.REC_Content
	,T_Records.REC_ResponsibleName
	,COALESCE(T_Records.REC_TTL, 100) AS REC_TTL 
	,T_Records.REC_Prio
	,T_Records.REC_Weight
	,T_Records.REC_Port
	,T_Records.REC_SerialNumber
	,T_Records.REC_RefreshInterval
	,T_Records.REC_RetryInterval
	,T_Records.REC_ExpireInterval
	,T_Records.REC_NegativeCachingTTL
	,T_Records.REC_AfsSubType
	,T_Records.REC_ChangeDate
FROM T_Records 
WHERE REC_RT_Id	= @in_recordType 
AND T_Records.REC_Name = @in_recordName 
;
"))
            {
                try
                {
                    string name = query.Questions[0].Name.ToString();

                    s_sql.AddParameter(cmd, "in_recordType", (int)query.Questions[0].RecordType);
                    s_sql.AddParameter(cmd, "in_recordName", name);

                    // TODO: Can return multiple records...
                    rec = s_sql.GetClass <DbDnsRecord>(cmd);
                }
                catch (System.Exception ex)
                {
                    System.Console.WriteLine(ex.Message);
                    System.Console.WriteLine(ex.StackTrace);
                }
            } // End Using cmd

            if (rec != null)
            {
                int ttl = 3600;

                DnsRecordBase record = null;

                // https://blog.dnsimple.com/2015/04/common-dns-records/
                // https://en.wikipedia.org/wiki/List_of_DNS_record_types
                switch ((RecordType)rec.REC_RT_Id)
                {
                case RecordType.Soa:
                    // SoaRecord(DomainName name, int timeToLive, DomainName masterName
                    //  , DomainName responsibleName, uint serialNumber, int refreshInterval
                    //  , int retryInterval, int expireInterval, int negativeCachingTTL)
                    record = new ARSoft.Tools.Net.Dns.SoaRecord(
                        DomainName.Parse(rec.REC_Name)
                        , rec.REC_TTL.Value
                        , DomainName.Parse(rec.REC_Content)
                        , DomainName.Parse(rec.REC_ResponsibleName)
                        , rec.REC_SerialNumber.Value
                        , rec.REC_RefreshInterval.Value
                        , rec.REC_RetryInterval.Value
                        , rec.REC_ExpireInterval.Value
                        , rec.REC_NegativeCachingTTL.Value
                        );
                    break;

                case RecordType.Ns:
                    record = new ARSoft.Tools.Net.Dns.NsRecord(DomainName.Parse(rec.REC_Name), ttl, DomainName.Parse(rec.REC_Content));
                    break;

                case RecordType.Srv:
                    // SrvRecord(DomainName name, int timeToLive, ushort priority, ushort weight, ushort port, DomainName target)
                    record = new ARSoft.Tools.Net.Dns.SrvRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, (ushort)rec.REC_Prio.Value, (ushort)rec.REC_Weight.Value, (ushort)rec.REC_Port.Value, DomainName.Parse(rec.REC_Content));
                    break;

                // https://www.openafs.org/
                // https://en.wikipedia.org/wiki/OpenAFS
                // http://www.rjsystems.nl/en/2100-dns-discovery-openafs.php
                // OpenAFS is an open source implementation of the Andrew distributed file system(AFS).
                case RecordType.Afsdb:
                    // http://www.rjsystems.nl/en/2100-dns-discovery-openafs.php

                    record = new ARSoft.Tools.Net.Dns.AfsdbRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, (AfsdbRecord.AfsSubType)(uint) rec.REC_AfsSubType.Value, DomainName.Parse(rec.REC_Content));
                    break;

                // A DNS-based Authentication of Named Entities (DANE) method
                // for publishing and locating OpenPGP public keys in DNS
                // for a specific email address using an OPENPGPKEY DNS resource record.
                case RecordType.OpenPGPKey:
                    byte[] publicKey = null;

                    // hexdump(sha256(truncate(utf8(ocalpart), 28)
                    // https://www.huque.com/bin/openpgpkey
                    // The OPENPGPKEY DNS record is specied in RFC 7929.
                    // The localpart of the uid is encoded as a DNS label
                    // containing the hexdump of the SHA-256 hash
                    // of the utf-8 encoded localpart, truncated to 28 octets.
                    // Normally the "Standard" output format should be used.
                    // The "Generic Encoding" output format is provided to help work
                    // with older DNS software that does not yet understand the OPENPGPKEY record type.
                    record = new ARSoft.Tools.Net.Dns.OpenPGPKeyRecord(DomainName.Parse(rec.REC_Name), ttl, publicKey);
                    break;

                // Canonical name records, or CNAME records, are often called alias records because they map an alias to the canonical name. When a name server finds a CNAME record, it replaces the name with the canonical name and looks up the new name.
                case RecordType.CName:
                    record = new ARSoft.Tools.Net.Dns.CNameRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, DomainName.Parse(rec.REC_Content));
                    break;

                case RecordType.Ptr:
                    record = new ARSoft.Tools.Net.Dns.PtrRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, DomainName.Parse(rec.REC_Content));
                    break;

                case RecordType.A:
                    record = new ARSoft.Tools.Net.Dns.ARecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, System.Net.IPAddress.Parse(rec.REC_Content));
                    break;

                case RecordType.Aaaa:
                    record = new ARSoft.Tools.Net.Dns.AaaaRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, System.Net.IPAddress.Parse(rec.REC_Content));
                    break;

                case RecordType.Mx:
                    record = new ARSoft.Tools.Net.Dns.MxRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, 0, DomainName.Parse(rec.REC_Content));
                    break;

                case RecordType.Txt:
                    record = new ARSoft.Tools.Net.Dns.TxtRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, rec.REC_Content);
                    break;

                case RecordType.SshFp:
                    // https://unix.stackexchange.com/questions/121880/how-do-i-generate-sshfp-records

                    // SshFpRecord(DomainName name, int timeToLive, SshFpAlgorithm algorithm
                    //     , SshFpFingerPrintType fingerPrintType, byte[] fingerPrint)
                    ARSoft.Tools.Net.Dns.SshFpRecord.SshFpAlgorithm sfa = ARSoft.Tools.Net.Dns.SshFpRecord
                                                                          .SshFpAlgorithm.Rsa;

                    ARSoft.Tools.Net.Dns.SshFpRecord.SshFpFingerPrintType sfp = ARSoft.Tools.Net.Dns.SshFpRecord
                                                                                .SshFpFingerPrintType.Sha256;

                    byte[] fp = null;

                    record = new ARSoft.Tools.Net.Dns.SshFpRecord(DomainName.Parse(rec.REC_Name), rec.REC_TTL.Value, sfa, sfp, fp);
                    break;

                default:
                    break;
                } // End Switch

                if (record != null)
                {
                    response.AnswerRecords.Add(record);
                }

                response.ReturnCode = ReturnCode.NoError;
                e.Response          = response;
                return(await Task <bool> .FromResult(true));
            } // End if (rec != null)

            return(await Task <bool> .FromResult(false));
        } // End Function ResolveMessage