-
Notifications
You must be signed in to change notification settings - Fork 540
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
[WIP] Managed implementation of DNS resolver #6104
base: main
Are you sure you want to change the base?
Conversation
32d94c5
to
40f8f9b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only reviewed a subset of files. It's a large PR and has a lot of RFC reading attached to it. Will review more later.
src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProvider.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderBase.Log.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProvider.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProvider.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.ServiceDiscovery.Dns/Resolver/DnsDataReader.cs
Outdated
Show resolved
Hide resolved
// padding is used. | ||
// | ||
|
||
if (!Encoding.ASCII.TryGetBytes(name, destination.IsEmpty ? destination : destination.Slice(1), out int length) || destination.Length < length + 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the destination is empty, we should just return.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to add validation that only A-Z
, a-z
, 0-9
, -
, " "
, and .
are allowed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add 255 length check?
names 255 octets or less
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The domain name is already validated at this point, it is done by DnsResolver.GetNormalizedHostName(name)
(which throws if any of the domain name limits is violated)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add Debug.Asserts for some of the properties
src/Microsoft.Extensions.ServiceDiscovery.Dns/Resolver/DnsPrimitives.cs
Outdated
Show resolved
Hide resolved
|
||
} | ||
|
||
internal static bool TryReadQName(ReadOnlySpan<byte> messageBuffer, int offset, [NotNullWhen(true)] out string? name, out int bytesRead) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
messageBuffer
doesn't have any endianness normalization when it's passed in. Is that problematic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only multibyte information is the offset in pointers and we read higher and lower byte separately and combine them correctly. Otherwise we only ever read individual bytes or (ascii) strings, so endianness should not matter.
src/Microsoft.Extensions.ServiceDiscovery.Dns/Resolver/NetworkInfo.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.ServiceDiscovery.Dns/Resolver/ResolvConf.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.ServiceDiscovery.Dns/Resolver/ResolvConf.cs
Outdated
Show resolved
Hide resolved
{ | ||
// the message is not a response for our query. | ||
// don't dispose reader, we will reuse the buffer | ||
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wouldn't use the bytes past DnsMessageHeader.HeaderLength if we read more than that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is UDP path, the DNS message is received in entirety in a single ReceiveAsync call, and here we checked that we are not interested in this particular message.
int bytesRead = 0; | ||
while (responseLength < 0 || bytesRead < length + 2) | ||
{ | ||
int read = await socket.ReceiveAsync(buffer.AsMemory(bytesRead), SocketFlags.None, cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to check if (read == 0)
?
|
||
if (responseLength > buffer.Length) | ||
{ | ||
var largerBuffer = ArrayPool<byte>.Shared.Rent(responseLength); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of dangerous, user controlled pre-allocation. Since we're limited to 65k here you could argue it's not too risky, but we should probably explicitly call it out in a comment and make sure we're okay with the risk.
Technically the array pool (ConfigurableArrayPool) could return a 65 * 2 * 2 (or more) buffer since the implementation is free to search multiple bucket sizes to find a pooled byte[].
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am curious, if we were to decide it's too risky, what would you suggest as a mitigation? We need the entire message in memory for parsing purposes (domain name compression via pointers with offsets), so I don't see us reading the contents of the message from a stream.
ResolverOptions options = ResolvConf.GetOptions(reader); | ||
|
||
IPEndPoint ipAddress = Assert.Single(options.Servers); | ||
Assert.Equal(new IPEndPoint(IPAddress.Parse("10.96.0.10"), 53), ipAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify search domains as well?
@rzikm could you set expectations on this one ? |
in parallel to this, I am also finishing Threat Model Review and will need to setup fuzzing. If there are no major distractions I hope to finish the work this month. |
0fa2406
to
82a8108
Compare
3721d48
to
fa627fd
Compare
@BrennanConroy I pushed a substantial update which should resolve most of the comments, please take another look. |
Description
This PR brings in a C# implementation of a DNS resolver that is able to signal the TTL information together with the query results.
Main features
Checklist
<remarks />
and<code />
elements on your triple slash comments?Microsoft Reviewers: Open in CodeFlow