⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

@laveeshb
Copy link
Contributor

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:

  • invalid and *.invalid - Return SocketError.HostNotFound (NXDOMAIN) per Section 6.4
  • *.localhost subdomains - Resolve to loopback addresses per Section 6.3
  • Plain localhost - Continues to use OS resolver to preserve /etc/hosts customizations

Implementation notes

This addresses feedback from the earlier attempt in #120376 by @gbr-mendes:

  • @rzikm and @filipnavara suggested using a helper method with fewer string comparisons. IsReservedName() uses EndsWith + length check with no allocations.
  • @rzikm pointed out that "." + reservedName allocates on every call. This is avoided with the EndsWith + dot-check pattern.
  • @rzikm noted the need to handle cases where IPv6 is disabled. The implementation checks SocketProtocolSupportPal.OSSupportsIPv6 before returning IPv6 loopback.
  • @wfurt suggested pre-allocating immutable answers. Static readonly arrays s_localhostIPv4, s_localhostIPv6, and s_localhostBoth are used.
  • @ManickaP pointed out that AddressFamily should be respected. GetLoopbackAddresses() filters results based on the requested address family.
  • @ManickaP also noted the code should be inside BeforeResolution/AfterResolution for telemetry. Both sync and async paths emit proper telemetry.
  • @wfurt suggested adding diagnostic logging. NetEventSource.Info logs are emitted for RFC 6761 special cases.

Testing

Added tests covering:

  • Invalid domain rejection (invalid, test.invalid, foo.bar.invalid)
  • Localhost subdomain resolution (foo.localhost, bar.foo.localhost)
  • Case-insensitivity (INVALID, Test.INVALID, FOO.LOCALHOST)
  • AddressFamily filtering for localhost subdomains
  • Edge cases like notlocalhost, localhostfoo to ensure similar names aren't incorrectly matched

Fixes #118569

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.
Copilot AI review requested due to automatic review settings January 11, 2026 18:09
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jan 11, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a 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] == '.');
Copy link

Copilot AI Jan 11, 2026

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] == '.'

Suggested change
hostName[hostName.Length - reservedName.Length - 1] == '.');
(hostName.Length > reservedName.Length + 1 &&
hostName[hostName.Length - reservedName.Length - 1] == '.'));

Copilot uses AI. Check for mistakes.
Copy link
Member

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).
Copy link
Member

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];
Copy link
Member

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.

@rzikm rzikm requested a review from a team January 12, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Net community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Domain name resolution does not handle RFC6761 special names correctly

2 participants