From 38fe46837aae576b8d1fdaa3177ddb3c24fa6018 Mon Sep 17 00:00:00 2001 From: Radek Zikmund <32671551+rzikm@users.noreply.github.com> Date: Wed, 30 Mar 2022 12:40:32 +0200 Subject: [PATCH] Use shared SocketAddress code in QUIC (#66794) * Use shared SocketAddress code in QUIC * Code review feedback * Disable broken tests * Rename file --- .../src/Resources/Strings.resx | 7 +++ .../src/System.Net.Quic.csproj | 9 ++++ .../Quic/Implementations/Mock/MockListener.cs | 2 +- .../MsQuic/Internal/MsQuicAddressHelpers.cs | 43 ++------------- .../MsQuic/Internal/MsQuicParameterHelpers.cs | 38 ++++++++++--- .../MsQuic/Interop/MsQuicNativeMethods.cs | 53 ++----------------- .../MsQuic/MsQuicConnection.cs | 31 ++++------- .../Implementations/MsQuic/MsQuicListener.cs | 16 +++--- .../tests/FunctionalTests/MsQuicTests.cs | 11 ++++ .../FunctionalTests/QuicListenerTests.cs | 16 ++++++ 10 files changed, 103 insertions(+), 123 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/Resources/Strings.resx b/src/libraries/System.Net.Quic/src/Resources/Strings.resx index af6bfa4d295..4eb23ece75c 100644 --- a/src/libraries/System.Net.Quic/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Quic/src/Resources/Strings.resx @@ -168,5 +168,12 @@ Could not use a TLS version required by Quic. TLS 1.3 may have been disabled in the registry. + + + The AddressFamily {0} is not valid for the {1} end point, use {2} instead. + + + The supplied {0} is an invalid size for the {1} end point. + diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 171290fcbb3..c1f0c1ac644 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -50,6 +50,10 @@ + + + + @@ -70,12 +74,15 @@ + + + @@ -97,6 +104,7 @@ + @@ -129,6 +137,7 @@ + diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs index 48ebf8a0603..1be34005051 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs @@ -22,7 +22,7 @@ internal sealed class MockListener : QuicListenerProvider internal MockListener(QuicListenerOptions options) { - if (options.ListenEndPoint is null || options.ListenEndPoint.Address != IPAddress.Loopback || options.ListenEndPoint.Port != 0) + if (options.ListenEndPoint is null || (options.ListenEndPoint.Address != IPAddress.Loopback && options.ListenEndPoint.Address != IPAddress.IPv6Loopback) || options.ListenEndPoint.Port != 0) { throw new ArgumentException("Must pass loopback address and port 0"); } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs index d280fb8d039..9163794f7a0 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs @@ -9,46 +9,11 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { internal static class MsQuicAddressHelpers { - internal static unsafe IPEndPoint INetToIPEndPoint(ref SOCKADDR_INET inetAddress) + internal static unsafe IPEndPoint INetToIPEndPoint(IntPtr pInetAddress) { - if (inetAddress.si_family == QUIC_ADDRESS_FAMILY.INET) - { - return new IPEndPoint(new IPAddress(MemoryMarshal.CreateReadOnlySpan(ref inetAddress.Ipv4.sin_addr[0], 4)), (ushort)IPAddress.NetworkToHostOrder((short)inetAddress.Ipv4.sin_port)); - } - else - { - return new IPEndPoint(new IPAddress(MemoryMarshal.CreateReadOnlySpan(ref inetAddress.Ipv6.sin6_addr[0], 16)), (ushort)IPAddress.NetworkToHostOrder((short)inetAddress.Ipv6.sin6_port)); - } - } - - internal static unsafe SOCKADDR_INET IPEndPointToINet(IPEndPoint endpoint) - { - SOCKADDR_INET socketAddress = default; - if (!endpoint.Address.Equals(IPAddress.Any) && !endpoint.Address.Equals(IPAddress.IPv6Any)) - { - switch (endpoint.Address.AddressFamily) - { - case AddressFamily.InterNetwork: - endpoint.Address.TryWriteBytes(MemoryMarshal.CreateSpan(ref socketAddress.Ipv4.sin_addr[0], 4), out _); - socketAddress.Ipv4.sin_family = QUIC_ADDRESS_FAMILY.INET; - break; - case AddressFamily.InterNetworkV6: - endpoint.Address.TryWriteBytes(MemoryMarshal.CreateSpan(ref socketAddress.Ipv6.sin6_addr[0], 16), out _); - socketAddress.Ipv6.sin6_family = QUIC_ADDRESS_FAMILY.INET6; - break; - default: - throw new ArgumentException(SR.net_quic_addressfamily_notsupported); - } - } - - SetPort(endpoint.Address.AddressFamily, ref socketAddress, endpoint.Port); - return socketAddress; - } - - private static void SetPort(AddressFamily addressFamily, ref SOCKADDR_INET socketAddrInet, int originalPort) - { - ushort convertedPort = (ushort)IPAddress.HostToNetworkOrder((short)originalPort); - socketAddrInet.Ipv4.sin_port = convertedPort; + // MsQuic always uses storage size as if IPv6 was used + Span addressBytes = new Span((byte*)pInetAddress, Internals.SocketAddress.IPv6AddressSize); + return new Internals.SocketAddress(SocketAddressPal.GetAddressFamily(addressBytes), addressBytes).GetIPEndPoint(); } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs index 63672d2a6e1..33ba18920d9 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs @@ -3,22 +3,46 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using System.Net.Sockets; using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; namespace System.Net.Quic.Implementations.MsQuic.Internal { internal static class MsQuicParameterHelpers { - internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) + internal static unsafe IPEndPoint GetIPEndPointParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) { - SOCKADDR_INET value; - uint valueLen = (uint)sizeof(SOCKADDR_INET); + // MsQuic always uses storage size as if IPv6 was used + uint valueLen = (uint)Internals.SocketAddress.IPv6AddressSize; + Span address = stackalloc byte[Internals.SocketAddress.IPv6AddressSize]; - uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); - QuicExceptionHelpers.ThrowIfFailed(status, "GetINETParam failed."); - Debug.Assert(valueLen == sizeof(SOCKADDR_INET)); + fixed (byte* paddress = &MemoryMarshal.GetReference(address)) + { + uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, paddress); + QuicExceptionHelpers.ThrowIfFailed(status, "GetIPEndPointParam failed."); + } - return value; + address = address.Slice(0, (int)valueLen); + + return new Internals.SocketAddress(SocketAddressPal.GetAddressFamily(address), address) + .GetIPEndPoint(); + } + + internal static unsafe void SetIPEndPointParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, IPEndPoint value) + { + Internals.SocketAddress socketAddress = IPEndPointExtensions.Serialize(value); + + // MsQuic always reads same amount of memory as if IPv6 was used, so we can't pass pointer to socketAddress.Buffer directly + Span address = stackalloc byte[Internals.SocketAddress.IPv6AddressSize]; + socketAddress.Buffer.AsSpan(0, socketAddress.Size).CopyTo(address); + address.Slice(socketAddress.Size).Clear(); + + fixed (byte* paddress = &MemoryMarshal.GetReference(address)) + { + QuicExceptionHelpers.ThrowIfFailed( + api.SetParamDelegate(nativeObject, level, param, (uint)address.Length, paddress), + "Could not set IPEndPoint"); + } } internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index d863398e0d8..681bc94bdb4 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -422,7 +422,7 @@ internal struct NewConnectionInfo SafeMsQuicListenerHandle listener, QuicBuffer* alpnBuffers, uint alpnBufferCount, - ref SOCKADDR_INET localAddress); + byte* localAddress); internal delegate void ListenerStopDelegate( SafeMsQuicListenerHandle listener); @@ -649,48 +649,6 @@ internal struct StreamEvent internal StreamEventDataUnion Data; } - // TODO: rename to C#-like - [StructLayout(LayoutKind.Sequential)] - internal struct SOCKADDR_IN - { -#if SOCKADDR_HAS_LENGTH - internal byte sin_len; -#endif - internal QUIC_ADDRESS_FAMILY sin_family; - internal ushort sin_port; - internal fixed byte sin_addr[4]; - } - - // TODO: rename to C#-like - [StructLayout(LayoutKind.Sequential)] - internal struct SOCKADDR_IN6 - { -#if SOCKADDR_HAS_LENGTH - internal byte sin6_len; -#endif - internal QUIC_ADDRESS_FAMILY sin6_family; - internal ushort sin6_port; - internal uint sin6_flowinfo; - internal fixed byte sin6_addr[16]; - internal uint sin6_scope_id; - } - - // TODO: rename to C#-like - [StructLayout(LayoutKind.Explicit)] - internal struct SOCKADDR_INET - { - [FieldOffset(0)] - internal SOCKADDR_IN Ipv4; - [FieldOffset(0)] - internal SOCKADDR_IN6 Ipv6; -#if SOCKADDR_HAS_LENGTH - [FieldOffset(1)] -#else - [FieldOffset(0)] -#endif - internal QUIC_ADDRESS_FAMILY si_family; - } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamCallbackDelegate( IntPtr stream, @@ -1061,9 +1019,9 @@ internal uint ListenerOpen(SafeMsQuicRegistrationHandle registration, ListenerCa return __retVal; } - internal uint ListenerStart(SafeMsQuicListenerHandle listener, QuicBuffer* alpnBuffers, uint alpnBufferCount, ref SOCKADDR_INET localAddress) + internal uint ListenerStart(SafeMsQuicListenerHandle listener, QuicBuffer* alpnBuffers, uint alpnBufferCount, byte* localAddress) { - IntPtr __listener_gen_native = default; + IntPtr __listener_gen_native; uint __retVal; // // Setup @@ -1076,10 +1034,7 @@ internal uint ListenerStart(SafeMsQuicListenerHandle listener, QuicBuffer* alpnB // listener.DangerousAddRef(ref listener__addRefd); __listener_gen_native = listener.DangerousGetHandle(); - fixed (SOCKADDR_INET* __localAddress_gen_native = &localAddress) - { - __retVal = ((delegate* unmanaged[Cdecl])_functionPointer)(__listener_gen_native, alpnBuffers, alpnBufferCount, __localAddress_gen_native); - } + __retVal = ((delegate* unmanaged[Cdecl])_functionPointer)(__listener_gen_native, alpnBuffers, alpnBufferCount, localAddress); } finally { diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 7b9903a313c..5d3a7e868a7 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -252,10 +252,10 @@ private static uint HandleEventConnected(State state, ref ConnectionEvent connec { // Connected will already be true for connections accepted from a listener. Debug.Assert(!Monitor.IsEntered(state)); - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); + Debug.Assert(state.Connection != null); - state.Connection._localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); + state.Connection._localEndPoint = MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); state.Connection.SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength); state.Connection = null; @@ -640,24 +640,18 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d string targetHost; int port; - if (_remoteEndPoint is IPEndPoint) + if (_remoteEndPoint is IPEndPoint ipEndPoint) { - SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet((IPEndPoint)_remoteEndPoint); - unsafe - { - Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - status = MsQuicApi.Api.SetParamDelegate(_state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, (uint)sizeof(SOCKADDR_INET), (byte*)&address); - QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); - } - + Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); + MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, ipEndPoint); targetHost = _state.TargetHost ?? ((IPEndPoint)_remoteEndPoint).Address.ToString(); port = ((IPEndPoint)_remoteEndPoint).Port; } - else if (_remoteEndPoint is DnsEndPoint) + else if (_remoteEndPoint is DnsEndPoint dnsEndPoint) { - port = ((DnsEndPoint)_remoteEndPoint).Port; - string dnsHost = ((DnsEndPoint)_remoteEndPoint).Host!; + port = dnsEndPoint.Port; + string dnsHost = dnsEndPoint.Host!; // We don't have way how to set separate SNI and name for connection at this moment. // If the name is actually IP address we can use it to make at least some cases work for people @@ -665,13 +659,8 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d if (!string.IsNullOrEmpty(_state.TargetHost) && !dnsHost.Equals(_state.TargetHost, StringComparison.InvariantCultureIgnoreCase) && IPAddress.TryParse(dnsHost, out IPAddress? address)) { // This is form of IPAddress and _state.TargetHost is set to different string - SOCKADDR_INET quicAddress = MsQuicAddressHelpers.IPEndPointToINet(new IPEndPoint(address, port)); - unsafe - { - Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - status = MsQuicApi.Api.SetParamDelegate(_state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, (uint)sizeof(SOCKADDR_INET), (byte*)&quicAddress); - QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); - } + Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); + MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, new IPEndPoint(address, port)); targetHost = _state.TargetHost!; } else diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs index 0e65bf662aa..ab4969a8178 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs @@ -13,6 +13,7 @@ using System.Threading.Channels; using System.Threading.Tasks; using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; +using System.Net.Sockets; namespace System.Net.Quic.Implementations.MsQuic { @@ -176,7 +177,7 @@ private unsafe IPEndPoint Start(QuicListenerOptions options) List applicationProtocols = options.ServerAuthenticationOptions!.ApplicationProtocols!; IPEndPoint listenEndPoint = options.ListenEndPoint!; - SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(listenEndPoint); + Internals.SocketAddress address = IPEndPointExtensions.Serialize(listenEndPoint); uint status; @@ -188,7 +189,10 @@ private unsafe IPEndPoint Start(QuicListenerOptions options) { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicAlpnHelper.Prepare(applicationProtocols, out handles, out buffers); - status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)applicationProtocols.Count, ref address); + fixed (byte* paddress = address.Buffer) + { + status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)applicationProtocols.Count, paddress); + } } catch { @@ -203,8 +207,7 @@ private unsafe IPEndPoint Start(QuicListenerOptions options) QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); - return MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); + return MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); } private void Stop() @@ -244,8 +247,9 @@ private void Stop() { ref NewConnectionInfo connectionInfo = ref *evt->Data.NewConnection.Info; - IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.LocalAddress); - IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.RemoteAddress); + IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(connectionInfo.LocalAddress); + IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(connectionInfo.RemoteAddress); + string targetHost = string.Empty; // compat with SslStream if (connectionInfo.ServerNameLength > 0 && connectionInfo.ServerName != IntPtr.Zero) { diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index be36ad9c39f..7db083a8da2 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -8,12 +8,14 @@ using System.Linq; using System.Net.Security; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Abstractions; @@ -141,6 +143,7 @@ public async Task UntrustedClientCertificateFails() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/67301", TestPlatforms.Linux)] public async Task CertificateCallbackThrowPropagates() { using CancellationTokenSource cts = new CancellationTokenSource(PassingTestTimeout); @@ -182,6 +185,7 @@ public async Task CertificateCallbackThrowPropagates() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/67301", TestPlatforms.Linux)] public async Task ConnectWithCertificateCallback() { X509Certificate2 c1 = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate(); @@ -311,6 +315,13 @@ public async Task ConnectWithCertificateForDifferentName_Throws() public async Task ConnectWithCertificateForLoopbackIP_IndicatesExpectedError(string ipString, bool expectsError) { var ipAddress = IPAddress.Parse(ipString); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/67301")] + throw new SkipTestException("IPv6 on Linux is temporarily broken"); + } + (X509Certificate2 certificate, _) = System.Net.Security.Tests.TestHelper.GenerateCertificates(expectsError ? "badhost" : "localhost"); var listenerOptions = new QuicListenerOptions(); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs index 7e3097fa273..bacc79bc968 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs @@ -26,6 +26,22 @@ public async Task Listener_Backlog_Success() await clientStreamTask; }).WaitAsync(TimeSpan.FromSeconds(6)); } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/67301", TestPlatforms.Linux)] + public async Task Listener_Backlog_Success_IPv6() + { + await Task.Run(async () => + { + using QuicListener listener = CreateQuicListener(new IPEndPoint(IPAddress.IPv6Loopback, 0)); + + using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); + var clientStreamTask = clientConnection.ConnectAsync(); + + using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); + await clientStreamTask; + }).WaitAsync(TimeSpan.FromSeconds(6)); + } } [ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported))] -- GitLab