-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Handle RFC 6761 special-use domain names in Dns resolution #123076
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
base: main
Are you sure you want to change the base?
Conversation
Implement RFC 6761 compliant handling for special-use domain names: - 'invalid' and '*.invalid' domains return SocketError.HostNotFound (NXDOMAIN) - '*.localhost' subdomains resolve to loopback addresses (127.0.0.1 and/or ::1) - Plain 'localhost' continues to use OS resolver to preserve /etc/hosts customizations Implementation details: - Pre-allocated loopback address arrays to avoid allocations on hot path - Respects AddressFamily parameter for filtered results - Includes telemetry for diagnostics consistency - Case-insensitive matching per RFC specification Added tests for invalid domains, localhost subdomains, AddressFamily filtering, and edge cases like 'notlocalhost' to ensure similar names aren't incorrectly matched.
|
Tagging subscribers to this area: @dotnet/ncl |
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.
Pull request overview
This PR implements RFC 6761 compliant handling for special-use domain names in System.Net.Dns. The implementation adds logic to intercept DNS queries for invalid/*.invalid domains (returning NXDOMAIN) and *.localhost subdomains (returning loopback addresses) before they reach the OS resolver. Plain localhost continues to use the OS resolver to preserve /etc/hosts customizations.
Changes:
- Added RFC 6761 special-use domain name handling to DNS resolution with proper telemetry integration
- Implemented helper methods for efficient string matching and loopback address allocation
- Added comprehensive test coverage for invalid domains, localhost subdomains, and address family filtering
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs | Added helper methods (IsReservedName, GetLoopbackAddresses, TryHandleRfc6761SpecialName) and integrated RFC 6761 handling into both sync and async resolution paths |
| src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs | Added tests for invalid domain rejection, localhost subdomain resolution, address family filtering, and edge cases |
| src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs | Added parallel tests for GetHostAddresses API covering invalid domains and localhost subdomains |
| // Matches "reservedName" exactly, or "*.reservedName" (subdomain) | ||
| return hostName.EndsWith(reservedName, StringComparison.OrdinalIgnoreCase) && | ||
| (hostName.Length == reservedName.Length || | ||
| hostName[hostName.Length - reservedName.Length - 1] == '.'); |
Copilot
AI
Jan 11, 2026
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 IsReservedName function has a bug with edge case hostnames starting with a dot. For example, ".localhost" or ".invalid" would incorrectly match because the function checks if the character before the reserved name is a dot, but doesn't verify there's actual content before that dot. This would cause ".localhost" to be treated as a reserved name when it shouldn't be.
To fix this, add a check to ensure the dot is not at position 0. The condition should be:
hostName[hostName.Length - reservedName.Length - 1] == '.' && hostName.Length > reservedName.Length + 1
Or alternatively, check that the position of the dot is greater than 0:
(hostName.Length - reservedName.Length - 1) > 0 && hostName[hostName.Length - reservedName.Length - 1] == '.'
| hostName[hostName.Length - reservedName.Length - 1] == '.'); | |
| (hostName.Length > reservedName.Length + 1 && | |
| hostName[hostName.Length - reservedName.Length - 1] == '.')); |
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.
hmm, this brings up more questions. We probably don't want to return loopback address for cases like:
.localhost
.something.localhost
something..localhost
so.me..thing.localhost
as they are all malformed, but validating that a string is a valid hostname is nontrivial (label lengths, punycode....) and we don't have that implemented anywhere IIRC. @wfurt any thoughts here?
| } | ||
|
|
||
| // RFC 6761 Section 6.3: "*.localhost" subdomains must resolve to loopback. | ||
| // Note: Plain "localhost" is handled by the OS resolver (preserves /etc/hosts customizations). |
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.
It feels weird to allow customizations for "localhost" name itself, but not for subdomains.
It also means that without any customization, names "localhost" and "a.localhost" may return different results (e.g. IPv4 and IPv6 addresses in different order)
| // Pre-allocated arrays for RFC 6761 localhost handling to avoid allocations on hot path. | ||
| private static readonly IPAddress[] s_localhostIPv4 = [IPAddress.Loopback]; | ||
| private static readonly IPAddress[] s_localhostIPv6 = [IPAddress.IPv6Loopback]; | ||
| private static readonly IPAddress[] s_localhostBoth = [IPAddress.Loopback, IPAddress.IPv6Loopback]; |
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.
On Windows, the order is IPv6Loopback first, not sure what the order is on other platforms though, or whether it is somehow configurable.
Handle RFC 6761 special-use domain names in Dns resolution
Description
This PR implements RFC 6761 compliant handling for special-use domain names in
System.Net.Dns.RFC 6761 compliance:
invalidand*.invalid- ReturnSocketError.HostNotFound(NXDOMAIN) per Section 6.4*.localhostsubdomains - Resolve to loopback addresses per Section 6.3localhost- Continues to use OS resolver to preserve/etc/hostscustomizationsImplementation notes
This addresses feedback from the earlier attempt in #120376 by @gbr-mendes:
IsReservedName()usesEndsWith+ length check with no allocations."." + reservedNameallocates on every call. This is avoided with theEndsWith+ dot-check pattern.SocketProtocolSupportPal.OSSupportsIPv6before returning IPv6 loopback.s_localhostIPv4,s_localhostIPv6, ands_localhostBothare used.AddressFamilyshould be respected.GetLoopbackAddresses()filters results based on the requested address family.BeforeResolution/AfterResolutionfor telemetry. Both sync and async paths emit proper telemetry.NetEventSource.Infologs are emitted for RFC 6761 special cases.Testing
Added tests covering:
invalid,test.invalid,foo.bar.invalid)foo.localhost,bar.foo.localhost)INVALID,Test.INVALID,FOO.LOCALHOST)AddressFamilyfiltering for localhost subdomainsnotlocalhost,localhostfooto ensure similar names aren't incorrectly matchedFixes #118569