-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[release/7.0] Close MsQuic after checking for QUIC support to free resources (#75163, #75441) #75521
[release/7.0] Close MsQuic after checking for QUIC support to free resources (#75163, #75441) #75521
Changes from 3 commits
1147c86
67b137f
d3b4084
454ce6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Diagnostics; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Runtime.InteropServices; | ||
using Microsoft.Quic; | ||
|
@@ -19,6 +20,9 @@ internal sealed unsafe partial class MsQuicApi | |
|
||
private static readonly Version MsQuicVersion = new Version(2, 1); | ||
|
||
private static readonly delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> MsQuicOpenVersion; | ||
private static readonly delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void> MsQuicClose; | ||
|
||
public MsQuicSafeHandle Registration { get; } | ||
|
||
public QUIC_API_TABLE* ApiTable { get; } | ||
|
@@ -47,7 +51,8 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) | |
} | ||
} | ||
|
||
internal static MsQuicApi Api { get; } = null!; | ||
private static readonly Lazy<MsQuicApi> _api = new Lazy<MsQuicApi>(AllocateMsQuicApi); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: s_api |
||
internal static MsQuicApi Api => _api.Value; | ||
|
||
internal static bool IsQuicSupported { get; } | ||
|
||
|
@@ -56,92 +61,110 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) | |
internal static bool Tls13ServerMayBeDisabled { get; } | ||
internal static bool Tls13ClientMayBeDisabled { get; } | ||
|
||
#pragma warning disable CA1810 // Initialize all static fields in 'MsQuicApi' when those fields are declared and remove the explicit static constructor | ||
static MsQuicApi() | ||
{ | ||
IntPtr msQuicHandle; | ||
if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) && | ||
if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle) && | ||
!NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle)) | ||
{ | ||
// MsQuic library not loaded | ||
return; | ||
} | ||
|
||
MsQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicOpenVersion)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Old version had There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
From #75441 (comment) : The failures to get exports from the MsQuic library should be fatal. If we fail to get the export, it means that there is something very wrong. |
||
MsQuicClose = (delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicClose)); | ||
|
||
if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out _)) | ||
{ | ||
// Too low version of the library (likely pre-2.0) | ||
return; | ||
} | ||
|
||
try | ||
{ | ||
if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) | ||
// Check version | ||
int arraySize = 4; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const int ArraySize = 4; |
||
uint* libVersion = stackalloc uint[arraySize]; | ||
uint size = (uint)arraySize * sizeof(uint); | ||
if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion))) | ||
{ | ||
return; | ||
} | ||
|
||
QUIC_API_TABLE* apiTable = null; | ||
delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress; | ||
if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &apiTable))) | ||
var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]); | ||
if (version < MsQuicVersion) | ||
{ | ||
if (NetEventSource.Log.IsEnabled()) | ||
{ | ||
NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'"); | ||
} | ||
return; | ||
} | ||
|
||
try | ||
{ | ||
int arraySize = 4; | ||
uint* libVersion = stackalloc uint[arraySize]; | ||
uint size = (uint)arraySize * sizeof(uint); | ||
if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion))) | ||
{ | ||
return; | ||
} | ||
// Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported | ||
QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL; | ||
size = sizeof(QUIC_TLS_PROVIDER); | ||
apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider); | ||
UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL; | ||
|
||
var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]); | ||
if (version < MsQuicVersion) | ||
if (UsesSChannelBackend) | ||
{ | ||
// Implies windows platform, check TLS1.3 availability | ||
if (!IsWindowsVersionSupported()) | ||
{ | ||
if (NetEventSource.Log.IsEnabled()) | ||
{ | ||
NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'"); | ||
NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}"); | ||
} | ||
|
||
return; | ||
} | ||
|
||
// Assume SChannel is being used on windows and query for the actual provider from the library | ||
QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL; | ||
size = sizeof(QUIC_TLS_PROVIDER); | ||
apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider); | ||
UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL; | ||
Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true); | ||
Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false); | ||
} | ||
|
||
if (UsesSChannelBackend) | ||
{ | ||
// Implies windows platform, check TLS1.3 availability | ||
if (!IsWindowsVersionSupported()) | ||
{ | ||
if (NetEventSource.Log.IsEnabled()) | ||
{ | ||
NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}"); | ||
} | ||
IsQuicSupported = true; | ||
} | ||
finally | ||
{ | ||
// Gracefully close the API table to free resources. The API table will be allocated lazily again if needed | ||
MsQuicClose(apiTable); | ||
} | ||
} | ||
#pragma warning restore CA1810 | ||
|
||
return; | ||
} | ||
private static MsQuicApi AllocateMsQuicApi() | ||
{ | ||
Debug.Assert(IsQuicSupported); | ||
|
||
Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true); | ||
Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false); | ||
} | ||
if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus)) | ||
{ | ||
throw ThrowHelper.GetExceptionForMsQuicStatus(openStatus); | ||
} | ||
|
||
Api = new MsQuicApi(apiTable); | ||
IsQuicSupported = true; | ||
} | ||
finally | ||
{ | ||
if (!IsQuicSupported && NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose)) | ||
{ | ||
// Gracefully close the API table | ||
((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable); | ||
} | ||
} | ||
return new MsQuicApi(apiTable); | ||
} | ||
|
||
} | ||
finally | ||
private static bool TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus) | ||
{ | ||
Debug.Assert(MsQuicOpenVersion != null); | ||
|
||
QUIC_API_TABLE* table = null; | ||
openStatus = MsQuicOpenVersion((uint)MsQuicVersion.Major, &table); | ||
if (StatusFailed(openStatus)) | ||
{ | ||
if (!IsQuicSupported) | ||
if (NetEventSource.Log.IsEnabled()) | ||
{ | ||
NativeLibrary.Free(msQuicHandle); | ||
NetEventSource.Info(null, $"MsQuicOpenVersion returned {openStatus} status code."); | ||
} | ||
|
||
apiTable = null; | ||
return false; | ||
} | ||
|
||
apiTable = table; | ||
return true; | ||
} | ||
|
||
private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got confused by this naming. This isn't the version of msquic that's being used, but rather than minimum supported version, yes? It should probably be named accordingly, e.g. MinimumSupportedMsQuicVersion.