From d39f8b6a02ca5553412606ca88f7eadc5aeb2170 Mon Sep 17 00:00:00 2001 From: wfurt Date: Fri, 16 Jun 2023 02:26:01 -0700 Subject: [PATCH 1/3] surface Quic transport errors --- .../System.Net.Quic/ref/System.Net.Quic.cs | 2 ++ .../System.Net.Quic/src/Resources/Strings.resx | 3 +++ .../src/System/Net/Quic/Internal/ThrowHelper.cs | 13 ++++++++++--- .../src/System/Net/Quic/QuicConnection.cs | 3 ++- .../src/System/Net/Quic/QuicError.cs | 5 +++++ .../src/System/Net/Quic/QuicException.cs | 13 +++++++++++++ .../MsQuicCipherSuitesPolicyTests.cs | 6 ++++-- 7 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs index 932596828d755f..fcb0136a76df65 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs @@ -61,12 +61,14 @@ public enum QuicError ProtocolError = 11, OperationAborted = 12, AlpnInUse = 13, + TransportError = 14, } public sealed partial class QuicException : System.IO.IOException { public QuicException(System.Net.Quic.QuicError error, long? applicationErrorCode, string message) { } public long? ApplicationErrorCode { get { throw null; } } public System.Net.Quic.QuicError QuicError { get { throw null; } } + public long? TransportErrorCode { get { throw null; } } } public sealed partial class QuicListener : System.IAsyncDisposable { diff --git a/src/libraries/System.Net.Quic/src/Resources/Strings.resx b/src/libraries/System.Net.Quic/src/Resources/Strings.resx index 0aff3df0a71f73..2b07df6ac775c1 100644 --- a/src/libraries/System.Net.Quic/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Quic/src/Resources/Strings.resx @@ -207,6 +207,9 @@ A QUIC protocol error was encountered + + QUIC encountered transport error '{0}' + Another QUIC listener is already listening on one of the requested application protocols on the same port. diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs index 65db16ea40640e..834fac3db5b221 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs @@ -49,9 +49,9 @@ internal static bool TryGetStreamExceptionForMsQuicStatus(int status, [NotNullWh return false; } - internal static Exception GetExceptionForMsQuicStatus(int status, string? message = null) + internal static Exception GetExceptionForMsQuicStatus(int status, string? message = null, long? transportErrorCode = null) { - Exception ex = GetExceptionInternal(status, message); + Exception ex = GetExceptionInternal(status, message, transportErrorCode); if (status != 0) { // Include the raw MsQuic status in the HResult property for better diagnostics @@ -60,7 +60,7 @@ internal static Exception GetExceptionForMsQuicStatus(int status, string? messag return ex; - static Exception GetExceptionInternal(int status, string? message) + static Exception GetExceptionInternal(int status, string? message, long? transportErrorCode) { // // Start by checking for statuses mapped to QuicError enum @@ -87,6 +87,8 @@ static Exception GetExceptionInternal(int status, string? message) // Some TLS Alerts are mapped to dedicated QUIC_STATUS codes so we need to handle them individually. // if (status == QUIC_STATUS_ALPN_NEG_FAILURE) return new AuthenticationException(SR.net_quic_alpn_neg_error); + + // should we remove this and surface as TransportError ???? if (status == QUIC_STATUS_USER_CANCELED) return new AuthenticationException(SR.Format(SR.net_auth_tls_alert, TlsAlertMessage.UserCanceled)); // @@ -110,6 +112,11 @@ static Exception GetExceptionInternal(int status, string? message) return new AuthenticationException(SR.Format(SR.net_auth_tls_alert, alert)); } + if (transportErrorCode != null) + { + return new QuicException(QuicError.TransportError, null, transportErrorCode, SR.Format(SR.net_quic_transport_error, transportErrorCode), null); + } + // // for everything else, use general InternalError // diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index 44222777334257..3aab3a0368f6c3 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -481,7 +481,8 @@ private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATE { // TODO: we should propagate transport error code. // https://github.com/dotnet/runtime/issues/72666 - Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(data.Status)); + + Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(data.Status, null, (long)data.ErrorCode)); _connectedTcs.TrySetException(exception); _acceptQueue.Writer.TryComplete(exception); return QUIC_STATUS_SUCCESS; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicError.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicError.cs index e70e86f7d45abe..88563ad1070899 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicError.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicError.cs @@ -77,5 +77,10 @@ public enum QuicError /// Another QUIC listener is already listening on one of the requested application protocols on the same port. /// AlpnInUse, + + /// + /// Quic operation failed because of Quic transport error. + /// + TransportError, } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicException.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicException.cs index 83b2cb588a1e99..18920f2e1c967c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicException.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicException.cs @@ -34,6 +34,14 @@ internal QuicException(QuicError error, long? applicationErrorCode, string messa ApplicationErrorCode = applicationErrorCode; } + internal QuicException(QuicError error, long? applicationErrorCode, long? transportErrorCode, string message, Exception? innerException) + : base(message, innerException) + { + QuicError = error; + ApplicationErrorCode = applicationErrorCode; + TransportErrorCode = transportErrorCode; + } + /// /// Gets the error which is associated with this exception. /// @@ -46,5 +54,10 @@ internal QuicException(QuicError error, long? applicationErrorCode, string messa /// This property contains the error code set by the application layer when closing the connection () or closing a read/write direction of a QUIC stream (). Contains null for all other errors. /// public long? ApplicationErrorCode { get; } + + /// + /// The transport error code as described in Quic RFC. + /// + public long? TransportErrorCode { get; } } } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs index df827e64feadab..ae606127ddeb82 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs @@ -71,10 +71,12 @@ public void NoSupportedCiphers_ThrowsArgumentException(TlsCipherSuite[] ciphers) [Fact] public async Task MismatchedCipherPolicies_ConnectAsync_ThrowsQuicException() { - await Assert.ThrowsAsync(() => TestConnection( + QuicException ex = await Assert.ThrowsAsync(() => TestConnection( new CipherSuitesPolicy(new[] { TlsCipherSuite.TLS_AES_128_GCM_SHA256 }), new CipherSuitesPolicy(new[] { TlsCipherSuite.TLS_AES_256_GCM_SHA384 }) )); + + Assert.Equal(QuicError.TransportError, ex.QuicError); } } -} \ No newline at end of file +} From 3f32d74886a09cefac485bda635816c25db870be Mon Sep 17 00:00:00 2001 From: wfurt Date: Fri, 16 Jun 2023 05:37:09 -0700 Subject: [PATCH 2/3] cleanup --- .../System.Net.Quic/src/System/Net/Quic/QuicConnection.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index 3aab3a0368f6c3..f7c1f4558173d4 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -479,9 +479,6 @@ private unsafe int HandleEventConnected(ref CONNECTED_DATA data) } private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATED_BY_TRANSPORT_DATA data) { - // TODO: we should propagate transport error code. - // https://github.com/dotnet/runtime/issues/72666 - Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(data.Status, null, (long)data.ErrorCode)); _connectedTcs.TrySetException(exception); _acceptQueue.Writer.TryComplete(exception); From d81bf41dacdfefd4950dbe8de088a75aaf347631 Mon Sep 17 00:00:00 2001 From: wfurt Date: Mon, 19 Jun 2023 02:57:32 -0700 Subject: [PATCH 3/3] AssertThrowsQuicExceptionAsync --- .../tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs index ae606127ddeb82..b6c9e36a6b9ab6 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicCipherSuitesPolicyTests.cs @@ -71,12 +71,10 @@ public void NoSupportedCiphers_ThrowsArgumentException(TlsCipherSuite[] ciphers) [Fact] public async Task MismatchedCipherPolicies_ConnectAsync_ThrowsQuicException() { - QuicException ex = await Assert.ThrowsAsync(() => TestConnection( + await AssertThrowsQuicExceptionAsync(QuicError.TransportError, () => TestConnection( new CipherSuitesPolicy(new[] { TlsCipherSuite.TLS_AES_128_GCM_SHA256 }), new CipherSuitesPolicy(new[] { TlsCipherSuite.TLS_AES_256_GCM_SHA384 }) )); - - Assert.Equal(QuicError.TransportError, ex.QuicError); } } }