Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ public ValueTask<ServiceResult[]> ResolveServiceAsync(string name, CancellationT
ObjectDisposedException.ThrowIf(_disposed, this);
cancellationToken.ThrowIfCancellationRequested();

if (CheckIsReservedDnsName(name) is ReservedNameType type && type != ReservedNameType.None)
{
// RFC 6761 requires that libraries return negative results for all queries except localhost A/AAAA
return ValueTask.FromResult(Array.Empty<ServiceResult>());
}

// dnsSafeName is Disposed by SendQueryWithTelemetry
EncodedDomainName dnsSafeName = GetNormalizedHostName(name);
return SendQueryWithTelemetry(name, dnsSafeName, QueryType.SRV, ProcessResponse, cancellationToken);
Expand Down Expand Up @@ -111,24 +117,9 @@ public ValueTask<ServiceResult[]> ResolveServiceAsync(string name, CancellationT

public async ValueTask<AddressResult[]> ResolveIPAddressesAsync(string name, CancellationToken cancellationToken = default)
{
if (string.Equals(name, "localhost", StringComparison.OrdinalIgnoreCase))
if (CheckIsReservedDnsName(name) is ReservedNameType type && type != ReservedNameType.None)
{
// name localhost exists outside of DNS and can't be resolved by a DNS server
int len = (Socket.OSSupportsIPv4 ? 1 : 0) + (Socket.OSSupportsIPv6 ? 1 : 0);
AddressResult[] res = new AddressResult[len];

int index = 0;
if (Socket.OSSupportsIPv6) // prefer IPv6
{
res[index] = new AddressResult(DateTime.MaxValue, IPAddress.IPv6Loopback);
index++;
}
if (Socket.OSSupportsIPv4)
{
res[index] = new AddressResult(DateTime.MaxValue, IPAddress.Loopback);
}

return res;
return ResolveDnsReservedNameAddress(type, null);
}

var ipv4AddressesTask = ResolveIPAddressesAsync(name, AddressFamily.InterNetwork, cancellationToken);
Expand All @@ -153,19 +144,9 @@ internal ValueTask<AddressResult[]> ResolveIPAddressesAsync(string name, Address
throw new ArgumentOutOfRangeException(nameof(addressFamily), addressFamily, "Invalid address family");
}

if (string.Equals(name, "localhost", StringComparison.OrdinalIgnoreCase))
if (CheckIsReservedDnsName(name) is ReservedNameType type && type != ReservedNameType.None)
{
// name localhost exists outside of DNS and can't be resolved by a DNS server
if (addressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4)
{
return ValueTask.FromResult<AddressResult[]>([new AddressResult(DateTime.MaxValue, IPAddress.Loopback)]);
}
else if (addressFamily == AddressFamily.InterNetworkV6 && Socket.OSSupportsIPv6)
{
return ValueTask.FromResult<AddressResult[]>([new AddressResult(DateTime.MaxValue, IPAddress.IPv6Loopback)]);
}

return ValueTask.FromResult<AddressResult[]>([]);
return ValueTask.FromResult(ResolveDnsReservedNameAddress(type, addressFamily));
}

// dnsSafeName is Disposed by SendQueryWithTelemetry
Expand Down Expand Up @@ -342,6 +323,41 @@ static bool TryReadAddress(in DnsResourceRecord record, QueryType type, [NotNull
}
}

private static AddressResult[] ResolveDnsReservedNameAddress(ReservedNameType type, AddressFamily? addressFamily)
{
switch (type)
{
case ReservedNameType.Localhost:
// resolve to appropriate loopback address
bool doIpv6 = Socket.OSSupportsIPv6 && (addressFamily == null || addressFamily == AddressFamily.InterNetworkV6);
bool doIpv4 = Socket.OSSupportsIPv4 && (addressFamily == null || addressFamily == AddressFamily.InterNetwork);

int len = (doIpv4 ? 1 : 0) + (doIpv6 ? 1 : 0);
AddressResult[] res = new AddressResult[len];

int count = 0;
if (doIpv6) // put IPv6 first if both are requested
{
res[count] = new AddressResult(DateTime.MaxValue, IPAddress.IPv6Loopback);
count++;
}
if (doIpv4)
{
res[count] = new AddressResult(DateTime.MaxValue, IPAddress.Loopback);
}

return res;

case ReservedNameType.Invalid:
// RFC 6761 requires that libraries return negative results for 'invalid'
return [];

default:
Debug.Fail("Should be unreachable");
throw new ArgumentOutOfRangeException(nameof(type), type, "Invalid reserved name type");
}
}

private async ValueTask<TResult[]> SendQueryWithTelemetry<TResult>(string name, EncodedDomainName dnsSafeName, QueryType queryType, Func<EncodedDomainName, QueryType, DnsResponse, (SendQueryError error, TResult[] result)> processResponseFunc, CancellationToken cancellationToken)
{
NameResolutionActivity activity = Telemetry.StartNameResolution(name, queryType, _timeProvider.GetTimestamp());
Expand Down Expand Up @@ -928,4 +944,44 @@ private static EncodedDomainName GetNormalizedHostName(string name)
}
}
}

//
// See RFC 6761 for reserved DNS names.
//
private static ReservedNameType CheckIsReservedDnsName(string name)
{
ReadOnlySpan<char> nameAsSpan = name;
nameAsSpan = nameAsSpan.TrimEnd('.'); // trim potential explicit root label

if (MatchesReservedName(nameAsSpan, "localhost"))
{
return ReservedNameType.Localhost;
}

if (MatchesReservedName(nameAsSpan, "invalid"))
{
return ReservedNameType.Invalid;
}

return ReservedNameType.None;

static bool MatchesReservedName(ReadOnlySpan<char> name, string reservedName)
{
// check if equal to reserved name or is a subdomain of it
if (name.EndsWith(reservedName, StringComparison.OrdinalIgnoreCase) &&
(name.Length == reservedName.Length || name[name.Length - reservedName.Length - 1] == '.'))
{
return true;
}

return false;
}
}

private enum ReservedNameType
{
None,
Localhost,
Invalid,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
[Theory]
[InlineData("www.resolveipv4.com")]
[InlineData("www.resolveipv4.com.")]
[InlineData("notlocalhost")]
[InlineData("notlocalhost.")]
[InlineData("notinvalid.")]
[InlineData("www.ř.com")]
public async Task ResolveIPv4_Simple_Success(string name)
{
Expand Down Expand Up @@ -219,16 +222,39 @@
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await Resolver.ResolveIPAddressesAsync("invalid-af.test", AddressFamily.Unknown));
}

[Theory]
[InlineData(AddressFamily.InterNetwork, "127.0.0.1")]
[InlineData(AddressFamily.InterNetworkV6, "::1")]
public async Task ResolveIP_Localhost_ReturnsLoopback(AddressFamily family, string addressAsString)
[InlineData("localhost", AddressFamily.InterNetwork, "127.0.0.1")]
[InlineData("localhost", AddressFamily.InterNetworkV6, "::1")]
[InlineData("localhost.", AddressFamily.InterNetwork, "127.0.0.1")]
[InlineData("inner.localhost.", AddressFamily.InterNetwork, "127.0.0.1")]
[InlineData("inner.localhost", AddressFamily.InterNetwork, "127.0.0.1")]
[InlineData("invalid", AddressFamily.InterNetwork, null)]
[InlineData("invalid", AddressFamily.InterNetworkV6, null)]
[InlineData("invalid.", AddressFamily.InterNetwork, null)]
[InlineData("inner.invalid.", AddressFamily.InterNetwork, null)]
[InlineData("inner.invalid", AddressFamily.InterNetwork, null)]
public async Task ResolveIP_SpecialName(string localhost, AddressFamily family, string? addressAsString)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1013: (NETCORE_ENGINEERING_TELEMETRY=Build) Public method 'ResolveIP_SpecialName' on test class 'ResolveAddressesTests' should be marked as a Theory. Reduce the visibility of the method, or add a Theory attribute to the method. (https://xunit.net/xunit.analyzers/rules/xUnit1013)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1008: (NETCORE_ENGINEERING_TELEMETRY=Build) Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method. (https://xunit.net/xunit.analyzers/rules/xUnit1008)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1013: (NETCORE_ENGINEERING_TELEMETRY=Build) Public method 'ResolveIP_SpecialName' on test class 'ResolveAddressesTests' should be marked as a Theory. Reduce the visibility of the method, or add a Theory attribute to the method. (https://xunit.net/xunit.analyzers/rules/xUnit1013)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1008: (NETCORE_ENGINEERING_TELEMETRY=Build) Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method. (https://xunit.net/xunit.analyzers/rules/xUnit1008)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Build Ubuntu)

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1008: (NETCORE_ENGINEERING_TELEMETRY=Build) Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method. (https://xunit.net/xunit.analyzers/rules/xUnit1008)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Build Ubuntu)

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1008: (NETCORE_ENGINEERING_TELEMETRY=Build) Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method. (https://xunit.net/xunit.analyzers/rules/xUnit1008)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1013: (NETCORE_ENGINEERING_TELEMETRY=Build) Public method 'ResolveIP_SpecialName' on test class 'ResolveAddressesTests' should be marked as a Theory. Reduce the visibility of the method, or add a Theory attribute to the method. (https://xunit.net/xunit.analyzers/rules/xUnit1013)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1008: (NETCORE_ENGINEERING_TELEMETRY=Build) Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method. (https://xunit.net/xunit.analyzers/rules/xUnit1008)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1013: (NETCORE_ENGINEERING_TELEMETRY=Build) Public method 'ResolveIP_SpecialName' on test class 'ResolveAddressesTests' should be marked as a Theory. Reduce the visibility of the method, or add a Theory attribute to the method. (https://xunit.net/xunit.analyzers/rules/xUnit1013)

Check failure on line 235 in test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs#L235

test/Libraries/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/Resolver/ResolveAddressesTests.cs(235,23): error xUnit1008: (NETCORE_ENGINEERING_TELEMETRY=Build) Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method. (https://xunit.net/xunit.analyzers/rules/xUnit1008)
{
IPAddress address = IPAddress.Parse(addressAsString);
AddressResult[] results = await Resolver.ResolveIPAddressesAsync("localhost", family);
AddressResult result = Assert.Single(results);
IPAddress? address = addressAsString != null ? IPAddress.Parse(addressAsString) : null;

Assert.Equal(address, result.Address);
bool serverCalled = false;
_ = DnsServer.ProcessUdpRequest(builder =>
{
serverCalled = true;
return Task.CompletedTask;
});

AddressResult[] results = await Resolver.ResolveIPAddressesAsync(localhost, family);
Assert.False(serverCalled, "Special name resolution should not call the DNS server.");

if (address == null)
{
Assert.Empty(results);
}
else
{
AddressResult result = Assert.Single(results);
Assert.Equal(address, result.Address);
}
}

[Fact]
Expand Down
Loading