File: Resolver\ResolveAddressesTests.cs
Web Access
Project: src\tests\Microsoft.Extensions.ServiceDiscovery.Dns.Tests\Microsoft.Extensions.ServiceDiscovery.Dns.Tests.csproj (Microsoft.Extensions.ServiceDiscovery.Dns.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Xunit;
using System.Net;
using System.Net.Sockets;
 
namespace Microsoft.Extensions.ServiceDiscovery.Dns.Resolver.Tests;
 
public class ResolveAddressesTests : LoopbackDnsTestBase
{
    public ResolveAddressesTests(ITestOutputHelper output) : base(output)
    {
    }
 
    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public async Task ResolveIPv4_NoData_Success(bool includeSoa)
    {
        string hostName = "nodata.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            if (includeSoa)
            {
                builder.Authorities.AddStartOfAuthority("ns.com", 240, "ns.com", "admin.ns.com", 1, 900, 180, 6048000, 3600);
            }
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
        Assert.Empty(results);
    }
 
    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public async Task ResolveIPv4_NoSuchName_Success(bool includeSoa)
    {
        string hostName = "nosuchname.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.ResponseCode = QueryResponseCode.NameError;
            if (includeSoa)
            {
                builder.Authorities.AddStartOfAuthority("ns.com", 240, "ns.com", "admin.ns.com", 1, 900, 180, 6048000, 3600);
            }
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
        Assert.Empty(results);
    }
 
    [Theory]
    [InlineData("www.resolveipv4.com")]
    [InlineData("www.resolveipv4.com.")]
    [InlineData("www.ř.com")]
    public async Task ResolveIPv4_Simple_Success(string name)
    {
        IPAddress address = IPAddress.Parse("172.213.245.111");
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddAddress(name, 3600, address);
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(name, AddressFamily.InterNetwork);
 
        AddressResult res = Assert.Single(results);
        Assert.Equal(address, res.Address);
        Assert.Equal(TimeProvider.GetUtcNow().DateTime.AddSeconds(3600), res.ExpiresAt);
    }
 
    [Fact]
    public async Task ResolveIPv4_Aliases_InOrder_Success()
    {
        IPAddress address = IPAddress.Parse("172.213.245.111");
        string hostName = "alias-in-order.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddCname(hostName, 3600, "www.example2.com");
            builder.Answers.AddCname("www.example2.com", 3600, "www.example3.com");
            builder.Answers.AddAddress("www.example3.com", 3600, address);
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
 
        AddressResult res = Assert.Single(results);
        Assert.Equal(address, res.Address);
        Assert.Equal(TimeProvider.GetUtcNow().DateTime.AddSeconds(3600), res.ExpiresAt);
    }
 
    [Fact]
    public async Task ResolveIPv4_Aliases_OutOfOrder_Success()
    {
        IPAddress address = IPAddress.Parse("172.213.245.111");
        string hostName = "alias-out-of-order.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddCname("www.example2.com", 3600, "www.example3.com");
            builder.Answers.AddAddress("www.example3.com", 3600, address);
            builder.Answers.AddCname(hostName, 3600, "www.example2.com");
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
 
        AddressResult res = Assert.Single(results);
        Assert.Equal(address, res.Address);
        Assert.Equal(TimeProvider.GetUtcNow().DateTime.AddSeconds(3600), res.ExpiresAt);
    }
 
    [Fact]
    public async Task ResolveIPv4_Aliases_Loop_ReturnsEmpty()
    {
        string hostName = "alias-loop2.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddCname(hostName, 3600, "www.example2.com");
            builder.Answers.AddCname("www.example2.com", 3600, "www.example3.com");
            builder.Answers.AddCname("www.example3.com", 3600, hostName);
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
 
        Assert.Empty(results);
    }
 
    [Fact]
    public async Task ResolveIPv4_Aliases_Loop_Reverse_ReturnsEmpty()
    {
        string hostName = "alias-loop2.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddCname("www.example3.com", 3600, hostName);
            builder.Answers.AddCname("www.example2.com", 3600, "www.example3.com");
            builder.Answers.AddCname(hostName, 3600, "www.example2.com");
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
 
        Assert.Empty(results);
    }
 
    [Fact]
    public async Task ResolveIPv4_Alias_And_Address()
    {
        IPAddress address = IPAddress.Parse("172.213.245.111");
        string hostName = "alias-address.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddCname(hostName, 3600, "www.example2.com");
            builder.Answers.AddCname("www.example2.com", 3600, "www.example3.com");
            builder.Answers.AddAddress("www.example2.com", 3600, address);
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
 
        Assert.Empty(results);
    }
 
    [Fact]
    public async Task ResolveIPv4_DuplicateAlias()
    {
        IPAddress address = IPAddress.Parse("172.213.245.111");
        string hostName = "duplicate-alias.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddCname(hostName, 3600, "www.example2.com");
            builder.Answers.AddCname("www.example2.com", 3600, "www.example3.com");
            builder.Answers.AddCname("www.example2.com", 3600, "www.example4.com");
            builder.Answers.AddAddress("www.example2.com", 3600, address);
            builder.Answers.AddAddress("www.example4.com", 3600, address);
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
 
        Assert.Empty(results);
    }
 
    [Fact]
    public async Task ResolveIPv4_Aliases_NotFound_Success()
    {
        IPAddress address = IPAddress.Parse("172.213.245.111");
        string hostName = "alias-no-found.test";
 
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Answers.AddCname(hostName, 3600, "www.example2.com");
            builder.Answers.AddCname("www.example2.com", 3600, "www.example3.com");
 
            // extra address in the answer not connected to the above
            builder.Answers.AddAddress("www.example4.com", 3600, address);
            return Task.CompletedTask;
        });
 
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync(hostName, AddressFamily.InterNetwork);
 
        Assert.Empty(results);
    }
 
    [Fact]
    public async Task ResolveIP_InvalidAddressFamily_Throws()
    {
        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)
    {
        IPAddress address = IPAddress.Parse(addressAsString);
        AddressResult[] results = await Resolver.ResolveIPAddressesAsync("localhost", family);
        AddressResult result = Assert.Single(results);
 
        Assert.Equal(address, result.Address);
    }
 
    [Fact]
    public async Task Resolve_Timeout_ReturnsEmpty()
    {
        Options.Timeout = TimeSpan.FromSeconds(1);
        AddressResult[] result = await Resolver.ResolveIPAddressesAsync("timeout-empty.test", AddressFamily.InterNetwork);
        Assert.Empty(result);
    }
 
    [Theory]
    [InlineData("not-example.com", (int)QueryType.A, (int)QueryClass.Internet)]
    [InlineData("example.com", (int)QueryType.AAAA, (int)QueryClass.Internet)]
    [InlineData("example.com", (int)QueryType.A, 0)]
    public async Task Resolve_QuestionMismatch_ReturnsEmpty(string name, int type, int @class)
    {
        Options.Timeout = TimeSpan.FromSeconds(1);
 
        IPAddress address = IPAddress.Parse("172.213.245.111");
        _ = DnsServer.ProcessUdpRequest(builder =>
        {
            builder.Questions[0] = (name, (QueryType)type, (QueryClass)@class);
            builder.Answers.AddAddress("www.example4.com", 3600, address);
            return Task.CompletedTask;
        });
 
        AddressResult[] result = await Resolver.ResolveIPAddressesAsync("example.com", AddressFamily.InterNetwork);
        Assert.Empty(result);
    }
 
    [Fact]
    public async Task Resolve_HeaderMismatch_Ignores()
    {
        string name = "header-mismatch.test";
        Options.Timeout = TimeSpan.FromSeconds(5);
 
        SemaphoreSlim responseSemaphore = new SemaphoreSlim(0, 1);
        SemaphoreSlim requestSemaphore = new SemaphoreSlim(0, 1);
 
        IPEndPoint clientAddress = null!;
 
        IPAddress address = IPAddress.Parse("172.213.245.111");
        ushort transactionId = 0x1234;
        _ = DnsServer.ProcessUdpRequest((builder, clientAddr) =>
        {
            clientAddress = clientAddr;
            transactionId = (ushort)(builder.TransactionId + 1);
 
            builder.Answers.AddAddress(name, 3600, address);
            requestSemaphore.Release();
            return responseSemaphore.WaitAsync();
        });
 
        ValueTask<AddressResult[]> task = Resolver.ResolveIPAddressesAsync(name, AddressFamily.InterNetwork);
 
        await requestSemaphore.WaitAsync().WaitAsync(Options.Timeout);
 
        using Socket socket = new Socket(clientAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
        LoopbackDnsResponseBuilder responseBuilder = new LoopbackDnsResponseBuilder(name, QueryType.A, QueryClass.Internet)
        {
            TransactionId = transactionId,
            ResponseCode = QueryResponseCode.NoError
        };
 
        responseBuilder.Questions.Add((name, QueryType.A, QueryClass.Internet));
        responseBuilder.Answers.AddAddress(name, 3600, IPAddress.Loopback);
        socket.SendTo(responseBuilder.GetMessageBytes(), clientAddress);
 
        responseSemaphore.Release();
 
        AddressResult[] results = await task;
        AddressResult result = Assert.Single(results);
 
        Assert.Equal(address, result.Address);
    }
}