From 2e29d1e129d31909cc4dfcc1add520bd5fe150b2 Mon Sep 17 00:00:00 2001 From: Sascha Lange Date: Mon, 25 Nov 2024 11:36:58 +0100 Subject: [PATCH] ResolveEndpoint has always taken the first entry from the AddressList. If, for example, IPv6 has been completely deactivated on your system, AddressList still always contains the entry "::1" in the first place, which leads to problems in certain constellations. If, for example, virtual network adapters (e.g. Hyper-V Network Switch) are also used on the system, the first entry does not return the correct outgoing IP address. Numerous tests have confirmed that the last address in the list is always the correct and desired address. By setting onlyUseIPv4 = true you can force an IPv4 address to be returned, even if the list contains "::1", for example. As a rule, the AddressList contains IPv6 addresses first and then IPv4 addresses. However, there are situations where additional IPv6 addresses appear in the list after the IPv4 addresses, and these are the correct addresses that you actually want. My changes cover all cases and you have full control over what the end result is. Otherwise, for example, an IP address from a Hyper-V switch is incorrectly used for a hostname. Connection problems arise, especially in conjunction with SSL and an SSL certificate that is then issued for this hostname or IP address. --- extensions/Sisk.SslProxy/DnsUtil.cs | 17 +++++++++++++++-- extensions/Sisk.SslProxy/SslProxyExtensions.cs | 6 ++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/extensions/Sisk.SslProxy/DnsUtil.cs b/extensions/Sisk.SslProxy/DnsUtil.cs index 59be4cc..d776da6 100644 --- a/extensions/Sisk.SslProxy/DnsUtil.cs +++ b/extensions/Sisk.SslProxy/DnsUtil.cs @@ -14,7 +14,7 @@ namespace Sisk.Ssl; static class DnsUtil { - public static IPEndPoint ResolveEndpoint(ListeningPort port) + public static IPEndPoint ResolveEndpoint(ListeningPort port, bool onlyUseIPv4 = false) { var hostEntry = Dns.GetHostEntry(port.Hostname); if (hostEntry.AddressList.Length == 0) @@ -23,7 +23,20 @@ public static IPEndPoint ResolveEndpoint(ListeningPort port) } else { - return new IPEndPoint(hostEntry.AddressList[0], port.Port); + if (onlyUseIPv4) + { + return new IPEndPoint(hostEntry.AddressList.Where(a => a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).Last(), port.Port); + } + else + { + var ipv6AddressList = hostEntry.AddressList.Where(a => a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6); + if (ipv6AddressList.Any()) + { + return new IPEndPoint(ipv6AddressList.Last(), port.Port); + } + else + return new IPEndPoint(hostEntry.AddressList.Last(), port.Port); + } } } } diff --git a/extensions/Sisk.SslProxy/SslProxyExtensions.cs b/extensions/Sisk.SslProxy/SslProxyExtensions.cs index 0ef081b..40c6deb 100644 --- a/extensions/Sisk.SslProxy/SslProxyExtensions.cs +++ b/extensions/Sisk.SslProxy/SslProxyExtensions.cs @@ -29,6 +29,7 @@ public static class SslProxyExtensions /// Optional. Specifies whether a client certificate is required for authentication. Defaults to false. /// Optional. Specifies the Proxy-Authorization header value for creating an trusted gateway between /// the application and the proxy. + /// Optional. Specifies whether DNS Resolve may also use IPv6 addresses or should only use IPv4 addresses /// The configured instance. public static HttpServerHostContextBuilder UseSsl( this HttpServerHostContextBuilder builder, @@ -36,13 +37,14 @@ public static HttpServerHostContextBuilder UseSsl( X509Certificate? certificate = null, SslProtocols allowedProtocols = SslProtocols.Tls12 | SslProtocols.Tls13, bool clientCertificateRequired = false, - object? proxyAuthorization = null) + object? proxyAuthorization = null, + bool onlyUseIPv4 = false) { var primaryHost = builder.ServerConfiguration.ListeningHosts[0]; var primaryPort = primaryHost.Ports[0]; var usableHosts = primaryHost.Ports.Select(p => p.Hostname); - var endpoint = DnsUtil.ResolveEndpoint(primaryPort); + var endpoint = DnsUtil.ResolveEndpoint(primaryPort, onlyUseIPv4); if (certificate is null) { certificate = CertificateUtil.CreateTrustedDevelopmentCertificate(["localhost", .. usableHosts]);