Skip to content

Commit 8fbc05e

Browse files
committed
Address review feedback for RFC 6761 handling
- Fix IPv6 address order: use [IPv6Loopback, Loopback] to match Windows resolver behavior - Add validation to reject malformed hostnames (starting with '.' or containing '..') in IsReservedName, letting them fall through to OS resolver - Add tests for malformed hostname edge cases (.localhost, foo..localhost, .invalid, test..invalid)
1 parent b4f7f44 commit 8fbc05e

File tree

2 files changed

+24
-1
lines changed

2 files changed

+24
-1
lines changed

src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,16 +417,24 @@ private static bool ValidateAddressFamily(ref AddressFamily addressFamily, strin
417417
}
418418

419419
// Pre-allocated arrays for RFC 6761 localhost handling to avoid allocations on hot path.
420+
// IPv6 is listed first to match Windows resolver behavior.
420421
private static readonly IPAddress[] s_localhostIPv4 = [IPAddress.Loopback];
421422
private static readonly IPAddress[] s_localhostIPv6 = [IPAddress.IPv6Loopback];
422-
private static readonly IPAddress[] s_localhostBoth = [IPAddress.Loopback, IPAddress.IPv6Loopback];
423+
private static readonly IPAddress[] s_localhostBoth = [IPAddress.IPv6Loopback, IPAddress.Loopback];
423424

424425
/// <summary>
425426
/// Checks if the given host name matches a reserved name or is a subdomain of it.
426427
/// For example, IsReservedName("foo.localhost", "localhost") returns true.
428+
/// Returns false for malformed hostnames (starting with dot or containing consecutive dots).
427429
/// </summary>
428430
private static bool IsReservedName(string hostName, string reservedName)
429431
{
432+
// Reject malformed hostnames - let OS resolver handle them (and reject them)
433+
if (hostName.StartsWith('.') || hostName.Contains(".."))
434+
{
435+
return false;
436+
}
437+
430438
// Matches "reservedName" exactly, or "*.reservedName" (subdomain)
431439
return hostName.EndsWith(reservedName, StringComparison.OrdinalIgnoreCase) &&
432440
(hostName.Length == reservedName.Length ||

src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,21 @@ public async Task DnsGetHostEntry_SimilarButNotReserved_ThrowsSocketException(st
374374
await Assert.ThrowsAnyAsync<SocketException>(() => Dns.GetHostEntryAsync(hostName));
375375
}
376376

377+
// Malformed hostnames should not be treated as RFC 6761 reserved names.
378+
// They should fall through to the OS resolver which will reject them.
379+
[Theory]
380+
[InlineData(".localhost")]
381+
[InlineData("foo..localhost")]
382+
[InlineData(".invalid")]
383+
[InlineData("test..invalid")]
384+
public async Task DnsGetHostEntry_MalformedReservedName_NotTreatedAsReserved(string hostName)
385+
{
386+
// Malformed hostnames should go to OS resolver, not be special-cased.
387+
// OS resolver will typically reject them with ArgumentException or SocketException.
388+
Assert.ThrowsAny<Exception>(() => Dns.GetHostEntry(hostName));
389+
await Assert.ThrowsAnyAsync<Exception>(() => Dns.GetHostEntryAsync(hostName));
390+
}
391+
377392
// RFC 6761: "*.localhost" subdomains should respect AddressFamily parameter.
378393
[Theory]
379394
[InlineData(AddressFamily.InterNetwork)]

0 commit comments

Comments
 (0)