public async Task <List <IPAddress> > ResolveMxIpAddressesAsync(string domainName) { var hostNames = await ResolveMxHostNamesAsync(domainName); var result = new List <IPAddress>(); foreach (var hostName in hostNames) { // TODO: Query for IPV6 as well. var dnsMessage = await _dnsClient.ResolveAsync(DomainName.Parse(hostName), RecordType.A); if (IsFailedQuery(dnsMessage)) { // TODO: Throw specific type throw new Exception($"Dns query for {domainName} failed."); } var aRecords = dnsMessage.AnswerRecords.OfType <ARecord>(); result.AddRange(from record in aRecords select record.Address); } return(result); }
} // End Function ResolveMessage static async Task ForwardMessage(DnsMessage query, QueryReceivedEventArgs e, DnsMessage response) { if ((query.Questions.Count == 1)) { // send query to upstream server DnsQuestion question = query.Questions[0]; DnsMessage upstreamResponse = await s_dnsClient.ResolveAsync(question.Name , question.RecordType, question.RecordClass ); e.Response = upstreamResponse; return; // if got an answer, copy it to the message sent to the client if (upstreamResponse != null) { foreach (DnsRecordBase record in (upstreamResponse.AnswerRecords)) { response.AnswerRecords.Add(record); } // Next record foreach (DnsRecordBase record in (upstreamResponse.AdditionalRecords)) { response.AdditionalRecords.Add(record); } // Next record response.ReturnCode = ReturnCode.NoError; // set the response e.Response = response; } // End if (upstreamResponse != null) } // End if ((message.Questions.Count == 1)) } // End Function OnQueryReceived
/// <summary> /// Queries a the upstream DNS server(s) for specified records as an asynchronous operation. /// </summary> /// <typeparam name="T"> Type of records, that should be returned </typeparam> /// <param name="name"> Domain, that should be queried </param> /// <param name="recordType"> Type the should be queried </param> /// <param name="recordClass"> Class the should be queried </param> /// <param name="token"> The token to monitor cancellation requests </param> /// <returns> A list of matching <see cref="DnsRecordBase">records</see> </returns> public async Task <List <T> > ResolveAsync <T>(DomainName name, RecordType recordType = RecordType.A, RecordClass recordClass = RecordClass.INet, CancellationToken token = default(CancellationToken)) where T : DnsRecordBase { if (name == null) { throw new ArgumentNullException(nameof(name), "Name must be provided"); } List <T> records; if (_cache.TryGetRecords(name, recordType, recordClass, out records)) { return(records); } DnsMessage msg = await _dnsClient.ResolveAsync(name, recordType, recordClass, null, token); if ((msg == null) || ((msg.ReturnCode != ReturnCode.NoError) && (msg.ReturnCode != ReturnCode.NxDomain))) { throw new Exception("DNS request failed"); } CNameRecord cName = msg.AnswerRecords.Where(x => (x.RecordType == RecordType.CName) && (x.RecordClass == recordClass) && x.Name.Equals(name)).OfType <CNameRecord>().FirstOrDefault(); if (cName != null) { records = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(cName.CanonicalName)).OfType <T>().ToList(); if (records.Count > 0) { _cache.Add(name, recordType, recordClass, records, DnsSecValidationResult.Indeterminate, Math.Min(cName.TimeToLive, records.Min(x => x.TimeToLive))); return(records); } records = await ResolveAsync <T>(cName.CanonicalName, recordType, recordClass, token); if (records.Count > 0) { _cache.Add(name, recordType, recordClass, records, DnsSecValidationResult.Indeterminate, Math.Min(cName.TimeToLive, records.Min(x => x.TimeToLive))); } return(records); } records = msg.AnswerRecords.Where(x => x.Name.Equals(name)).OfType <T>().ToList(); if (records.Count > 0) { _cache.Add(name, recordType, recordClass, records, DnsSecValidationResult.Indeterminate, records.Min(x => x.TimeToLive)); } return(records); }
/// <summary> /// Resolves specified records as an asynchronous operation. /// </summary> /// <typeparam name="T"> Type of records, that should be returned </typeparam> /// <param name="name"> Domain, that should be queried </param> /// <param name="recordType"> Type the should be queried </param> /// <param name="recordClass"> Class the should be queried </param> /// <param name="token"> The token to monitor cancellation requests </param> /// <returns> A list of matching <see cref="DnsRecordBase">records</see> </returns> public async Task <DnsSecResult <T> > ResolveSecureAsync <T>(DomainName name, RecordType recordType = RecordType.A, RecordClass recordClass = RecordClass.INet, CancellationToken token = default(CancellationToken)) where T : DnsRecordBase { if (name == null) { throw new ArgumentNullException(nameof(name), "Name must be provided"); } DnsCacheRecordList <T> cacheResult; if (_cache.TryGetRecords(name, recordType, recordClass, out cacheResult)) { return(new DnsSecResult <T>(cacheResult, cacheResult.ValidationResult)); } DnsMessage msg = await _dnsClient.ResolveAsync(name, recordType, recordClass, new DnsQueryOptions() { IsEDnsEnabled = true, IsDnsSecOk = true, IsCheckingDisabled = true, IsRecursionDesired = true }, token).ConfigureAwait(false); if ((msg == null) || ((msg.ReturnCode != ReturnCode.NoError) && (msg.ReturnCode != ReturnCode.NxDomain))) { throw new Exception("DNS request failed"); } DnsSecValidationResult validationResult; CNameRecord cName = msg.AnswerRecords.Where(x => (x.RecordType == RecordType.CName) && (x.RecordClass == recordClass) && x.Name.Equals(name)).OfType <CNameRecord>().FirstOrDefault(); if (cName != null) { DnsSecValidationResult cNameValidationResult = await _validator.ValidateAsync(name, RecordType.CName, recordClass, msg, new List <CNameRecord>() { cName }, null, token).ConfigureAwait(false); if ((cNameValidationResult == DnsSecValidationResult.Bogus) || (cNameValidationResult == DnsSecValidationResult.Indeterminate)) { throw new DnsSecValidationException("CNAME record could not be validated"); } var records = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(cName.CanonicalName)).OfType <T>().ToList(); if (records.Count > 0) { DnsSecValidationResult recordsValidationResult = await _validator.ValidateAsync(cName.CanonicalName, recordType, recordClass, msg, records, null, token).ConfigureAwait(false); if ((recordsValidationResult == DnsSecValidationResult.Bogus) || (recordsValidationResult == DnsSecValidationResult.Indeterminate)) { throw new DnsSecValidationException("CNAME matching records could not be validated"); } validationResult = cNameValidationResult == recordsValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned; _cache.Add(name, recordType, recordClass, records, validationResult, Math.Min(cName.TimeToLive, records.Min(x => x.TimeToLive))); return(new DnsSecResult <T>(records, validationResult)); } var cNameResults = await ResolveSecureAsync <T>(cName.CanonicalName, recordType, recordClass, token).ConfigureAwait(false); validationResult = cNameValidationResult == cNameResults.ValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned; if (cNameResults.Records.Count > 0) { _cache.Add(name, recordType, recordClass, cNameResults.Records, validationResult, Math.Min(cName.TimeToLive, cNameResults.Records.Min(x => x.TimeToLive))); } return(new DnsSecResult <T>(cNameResults.Records, validationResult)); } List <T> res = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(name)).OfType <T>().ToList(); validationResult = await _validator.ValidateAsync(name, recordType, recordClass, msg, res, null, token).ConfigureAwait(false); if ((validationResult == DnsSecValidationResult.Bogus) || (validationResult == DnsSecValidationResult.Indeterminate)) { throw new DnsSecValidationException("Response records could not be validated"); } if (res.Count > 0) { _cache.Add(name, recordType, recordClass, res, validationResult, res.Min(x => x.TimeToLive)); } return(new DnsSecResult <T>(res, validationResult)); }
private async Task Server_QueryReceived(object sender, ARSoft.Tools.Net.Dns.QueryReceivedEventArgs e) { bool foundQuery = false; DnsMessage query = e.Query as DnsMessage; if (query == null) { return; } DnsMessage response = query.CreateResponseInstance(); try { C.WriteLine($"DnsServer: Questions: {query.Questions.Count}", true, "DNS"); for (int idx = 0; idx < query.Questions.Count; idx++) { var question = query.Questions[idx]; using (var connection = db.Connection()) { using (var command = new SqliteCommand( "SELECT * FROM DnsEntries WHERE RecordType = @RecordType AND RecordClass = @RecordClass AND DomainName = @DomainName;", connection)) { command.Parameters.AddWithValue("@RecordType", question.RecordType.ToString()); command.Parameters.AddWithValue("@RecordClass", question.RecordClass.ToString()); command.Parameters.AddWithValue("@DomainName", question.Name.ToString()); using (var reader = command.ExecuteReader()) { C.WriteLine($" Questions [{idx:000}] RES({reader.HasRows}): Type({question.RecordType}), Class({question.RecordClass}), {question.Name}", true, "DNS"); //Every new row will create a new dictionary that holds the columns while (reader.Read()) { try { var dnStr = reader["DomainName"] as string; var dn = DomainName.Parse(dnStr); if (dn == null) { continue; } var ttl = (System.Int64)reader["TTL"]; var rt = (RecordType)Enum.Parse(typeof(RecordType), reader["RecordType"] as string); var rc = (RecordClass)Enum.Parse(typeof(RecordClass), reader["RecordClass"] as string); if (rt == RecordType.A) { if (reader["Address"] == null) { continue; } var addr = reader["Address"] as string; var aRec = new ARecord(dn, Convert.ToInt32(ttl), IPAddress.Parse(addr)); response.AnswerRecords.Add(aRec); foundQuery = true; } } catch (Exception ex) { C.WriteLine($" Questions [{idx:000}]: {C.Red}Exception: {ex.ToString()}", true, "DNS:ERROR"); } } } } } } // Fallback if (dnsFallback != null && !foundQuery) { DnsQuestion question = query.Questions[0]; var resolve = dnsFallback.Resolve(question.Name); DnsMessage upstreamResponse = await dnsFallback.ResolveAsync( question.Name, question.RecordType, question.RecordClass); e.Response = upstreamResponse; return; } response.ReturnCode = ReturnCode.NoError; } catch (Exception ex) { response.ReturnCode = ReturnCode.ServerFailure; C.WriteLine($"{C.Red}{ex.ToString()}", true, "DNS:ERROR"); } // set the response e.Response = response; }