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); }
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