-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal for DNS client and custom DNS queries #19443
Comments
@davidsh @terrajobst @stephentoub PTAL |
In general we don't use |
@CIPop yup np, done |
For now, I think this is enough for an initial review but in the final spec we will require the following fully defined |
Thanks @MichaCo! LGTM Maybe not a good place to discuss this but: have you investigated ways to implement the features using OS-level APIs or are you proposing a managed-only (Sockets-based) DNS implementation. (If the latter, would it include DNSSec?) |
@CIPop @davidsh please submit the API for review after it is complete from your point of view (feel free to update the original API proposal post or work with @MichaCo) -- simple LGTM agreement from each of view is sufficient. |
@CIPop I was actually looking at DnsQuery, but I had no time playing with it. I also have no idea about the Linux equivalent so that's why my implementation is managed code only. Regarding DNSSec, the signature would be transferred as another RR with the response, which can easily be supported with the API. Question is if the API also has to validate the signature against a public key. For my use case right now, I don't need it. Another option for security would be DNS Cookies https://tools.ietf.org/html/rfc7873 I guess. |
Would the As-is var questions = new List<DnsQuestion>();
for (int i = 0; i < 10; i++)
{
// Do something real and interesting that conditionally creates the questions
questions.Add(new DnsQuestion());
}
var message = new DnsRequestMessage();
message.Questions = questions.ToArray(); Proposed var message = new DnsRequestMessage();
for (int i = 0; i < 10; i++)
{
// Do something real and interesting that conditionally creates the questions
message.Questions.Add(new DnsQuestion());
} |
@CIPop Regarding a native implementation. I did some tests with both DnsQuery and DnsQueryEx - docs. The implementation is 99% from pinvoke.net (not cleaned up or anything) DnsQuery works great but uses the standard network interfaces only. There is no option to set server/port. Performance is much faster than my managed code but only if I query the same name over and over. There could still be some caching although I did set the bypass cache flag. The DnsQueryEx actually supports setting the IPAddress and Port, unfortunately, setting the port other than 53 always results in an error (connection was closed by the remote host). I don't know why and if there is a way to work around though ~~ Did look so promising... Let me know if someone has some hints ;) |
@MichaCo I've talked to the DNS team owning the API and they were able to use it against a custom port. They recommend ensuring that the DNS server is working with a different client such as dig. E.g: |
@CIPop thanks for the followup! I figured it was actually a bug with the port. NetworkOrder from int gave 0, had to cast to The performance of the native code is not any better than using UDP directly
Regarding security, I don't see anything that the DnsQuery/Ex does that for you, so that part would up to use anyways right? |
Glad you found the bug!
I'd personally prefer to leave the system-level drivers and services to handle what they already do and implement new code only where absolutely necessary. There are several advantages such as security updates, future protocol support and reducing the code/tests that we have to maintain in two different places (.Net and the OSs). Does Linux/iOS have a similar API that could be reused? |
While I'm not very familiar with the API, it looks like since Win7, the OS supports DNSSec: https://technet.microsoft.com/en-us/library/dn593685(v=ws.11).aspx |
@MichaCo I will need to continue my ramp-up to the DNS RFCs and protocols before I review the API proposal. I have been switched to other high-priority items and won't be able to make progress until at least mid-January next year. |
@CIPop no worries, same here ;) I was looking for Linux equivalents to the windows' DnsQuery/Ex. Didn't really made much progress on that either. Have to check res_query. |
Has this stalled? I might pick this up. This isn't very idiotmatic right now - the nomenclature and patterns deviate quite strongly from what you'd expect in .Net. Some suggestions: public class DnsClient
{
public bool UseCache { get; set; }
public bool UseMulticast { get; set; }
public DnsClient();
public DnsClient(params IPEndPoint[] nameServers);
public DnsClient(params IPAddress[] nameServers);
public ValueTask<DnsResponse> QueryAsync(string query, QueryType queryType);
public ValueTask<DnsResponse> QueryAsync(string query, QueryType queryType, QueryClass queryClass);
public ValueTask<DnsResponse> QueryAsync(string query, QueryType queryType, CancellationToken cancellationToken);
public ValueTask<DnsResponse> QueryAsync(string query, QueryType queryType, QueryClass queryClass, CancellationToken cancellationToken);
public ValueTask<DnsResponse> ReverseQueryAsync(IPAddress ipAddress);
public ValueTask<DnsResponse> ReverseQueryAsync(IPAddress ipAddress, CancellationToken cancellationToken);
}
// Removed. We don't see other classes in corelib doing this.
// public class DnsMessageHandler
// Removed. Internal implementation details.
// public class DnsRequestMessage
// public class DnsRequestHeader
// public class DnsQuestion
// the response object returned by the lookup client
// Change name to DnsResponse - DnsQueryResponse is anagolous to DnsRequestResponse, which is strange.
// Should this be changed to a struct?
public readonly struct DnsResponse
{
public DnsResourceList Additionals { get; }
public DnsResourceList AllRecords { get; }
public DnsResourceList Answers { get; }
public DnsResourceList Authorities { get; }
// Removed. Makes no sense to echo this back to the query.
// public IReadOnlyList<DnsQuestion> Questions { get; }
// Removed. Inline the fields from this directly into the struct/class.
// public DnsResponseHeader Header { get; }
// ...
// Removed. However much I agree that Exceptions are awful, they are
// idiomatic in .Net.
// public string ErrorMessage { get; }
// public bool HasError { get; }
}
public readonly struct DnsResourceList : IReadOnlyList<DnsResource>
{
private readonly ReadOnlyMemory<byte> _memory;
private readonly int[] _offsets;
}
public class DnsException : ExternalException
{
public DnsError DnsErrorCode { get; }
}
public enum DnsError
{
Default = 0,
FormatError = 1,
ServerFailure = 2,
NameError = 3,
// NotImplemented = 4, // NotImplementedException?
Refused = 5
}
// see https://tools.ietf.org/html/rfc1035#section-3.2.2 and 3.2.3
public enum QueryType // : short
{
/// <summary>The <c>A</c> record.</summary>
Address = 1,
/// <summary>The <c>NS</c> record.</summary>
NameServer = 2,
/// <summary>...</summary>
CanonicalName = 5,
StartOfZoneAuthority = 6,
WellKnownService = 11,
Pointer = 12,
HostInformation = 13,
MailboxInformation = 14,
MailListInformation = 14,
MailExchange = 15,
Text = 16,
AddressV6 = 28
//...
}
// see https://tools.ietf.org/html/rfc1035#section-3.2.4
public enum QueryClass // : short
{
/// <summary>The <c>IN</c> class.</summary>
Internet = 1,
/// <summary>...</summary>
CSNet = 2,
Chaos = 3,
Hesiod = 4
} |
Looks like you mixed tabs and spaces. |
Any progress? I read through this thread and think the current blocker is
Is that right? Also @jcdickinson why |
I think we should revisit this for .NET 6. SVCB/HTTPSSVC are useful for HttpClient -- they will significantly reduce the time it takes to establish an HTTP/3 connection, as well as supply data needed for ECH, Alt-Svc, and HSTS-like features.
|
I think we should revisit this too. Beyond the SVCB issue, there's also the issue of TTLs -- we can't get these through the current API. HttpClient would very much like to know DNS TTLs so that it can discard connections appropriately when DNS changes. We've heard this ask from customers multiple times, and the best answer we have today is "use PooledConnectionLifetime" which is far from ideal. |
Given latest increased need from higher-level libraries, this is most likely going to happen 9.0 in some form. |
FWIW our use case involves lookups of SRV, TXT and MX records. These are heavily used for various service discovery protocols (Exchange Autodiscover, Kerberos, IMAP/POP3/SMTP/CalDAV server endpoint discovery). Notably, this would also benefit Kerberos.NET which may eventually be used for client authentication scenarios on platforms that don't have native Kerberos support (Android, tvOS). We currently depend on our own library. On Windows it uses |
In
System.Net.NameResolution
theDNS
class gives us a simple API to get the host name and addresses by using the internals of Windows or Linux.What is missing is an interface to query actual DNS servers (custom endpoints/ports) and get more information then just the IP or local host name.
Use Case
One use case could be accessing service discovery (e.g. Consul) to ask for available services by name via DNS:
A local Consul DNS endpoint runs on port 8600 and you'd either query with qtype
SRV
orA
.To query for a service by name in consul, you use
<servicename>.service.consul
.Example query and answers with dig asking for the consul service itself:
The SRV answer gives me the port the service instance is running on, the additional answer gives me the IP address. The result can contain multiple instances.
Why another API?
There are a few other implementations for .NET today, but none of which are really maintained as far as I know nor do they have support for netstandard1.x.
Proposed API
Usage
Creating a lookup client with different overloads:
Using the client to query for standard qtypes:
The result in all cases would contain the response header information, the list of resource records and the question.
API
LookupClient:
There are some more types like return codes / errors etc.
RFC References
Base RFC https://tools.ietf.org/html/rfc1035
Request and response header share the same RFC https://tools.ietf.org/html/rfc6895#section-2. But only some fields / flags are used in the request, others in the response.
The length is always 12 bytes.
The request or response data follows the header data...
I will not list all the details, only the in my opinion most important qtypes/RRs
A: https://tools.ietf.org/html/rfc1035#section-3.4.1
NS: https://tools.ietf.org/html/rfc1035
AAAA: https://tools.ietf.org/html/rfc3596#section-2.2
PTR (for reverse queries): https://tools.ietf.org/html/rfc1035#section-3.3.12
SRV: https://tools.ietf.org/html/rfc2782
MX: https://tools.ietf.org/html/rfc1035#section-3.3.9
TXT: https://tools.ietf.org/html/rfc1035#section-3.3.14
SOA: https://tools.ietf.org/html/rfc1035#section-3.3.13
Implementation
I couldn't help and did a reference/example implementation of the whole thing here: https://github.com/MichaCo/DnsClient.NET.
If we come up with a better API which goes into corefx I'm happy to discontinue that project. But I needed the functionality now ;)
@CIPop I'm creating this proposal as mentioned in #19351
Thanks,
M
The text was updated successfully, but these errors were encountered: