Skip to content

Commit 67b137f

Browse files
committed
Don't unload MsQuic from the process (dotnet#75441)
1 parent 1147c86 commit 67b137f

File tree

1 file changed

+53
-82
lines changed
  • src/libraries/System.Net.Quic/src/System/Net/Quic/Internal

1 file changed

+53
-82
lines changed

src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs

Lines changed: 53 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ internal sealed unsafe partial class MsQuicApi
2020

2121
private static readonly Version MsQuicVersion = new Version(2, 1);
2222

23+
private static readonly delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> MsQuicOpenVersion;
24+
private static readonly delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void> MsQuicClose;
25+
2326
public MsQuicSafeHandle Registration { get; }
2427

2528
public QUIC_API_TABLE* ApiTable { get; }
@@ -58,144 +61,112 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
5861
internal static bool Tls13ServerMayBeDisabled { get; }
5962
internal static bool Tls13ClientMayBeDisabled { get; }
6063

64+
#pragma warning disable CA1810 // Initialize all static fields in 'MsQuicApi' when those fields are declared and remove the explicit static constructor
6165
static MsQuicApi()
6266
{
63-
if (!TryLoadMsQuic(out IntPtr msQuicHandle))
67+
if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle) &&
68+
!NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle))
69+
{
70+
// MsQuic library not loaded
71+
return;
72+
}
73+
74+
MsQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicOpenVersion));
75+
MsQuicClose = (delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicClose));
76+
77+
if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out _))
6478
{
79+
// Too low version of the library (likely pre-2.0)
6580
return;
6681
}
6782

6883
try
6984
{
70-
if (!TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable, out _))
85+
// Check version
86+
int arraySize = 4;
87+
uint* libVersion = stackalloc uint[arraySize];
88+
uint size = (uint)arraySize * sizeof(uint);
89+
if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion)))
7190
{
7291
return;
7392
}
7493

75-
try
94+
var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]);
95+
if (version < MsQuicVersion)
7696
{
77-
// Check version
78-
int arraySize = 4;
79-
uint* libVersion = stackalloc uint[arraySize];
80-
uint size = (uint)arraySize * sizeof(uint);
81-
if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion)))
97+
if (NetEventSource.Log.IsEnabled())
8298
{
83-
return;
84-
}
85-
86-
var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]);
87-
if (version < MsQuicVersion)
88-
{
89-
if (NetEventSource.Log.IsEnabled())
90-
{
91-
NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'");
92-
}
93-
return;
99+
NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'");
94100
}
101+
return;
102+
}
95103

96-
// Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
97-
QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL;
98-
size = sizeof(QUIC_TLS_PROVIDER);
99-
apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider);
100-
UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL;
104+
// Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
105+
QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL;
106+
size = sizeof(QUIC_TLS_PROVIDER);
107+
apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider);
108+
UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL;
101109

102-
if (UsesSChannelBackend)
110+
if (UsesSChannelBackend)
111+
{
112+
// Implies windows platform, check TLS1.3 availability
113+
if (!IsWindowsVersionSupported())
103114
{
104-
// Implies windows platform, check TLS1.3 availability
105-
if (!IsWindowsVersionSupported())
115+
if (NetEventSource.Log.IsEnabled())
106116
{
107-
if (NetEventSource.Log.IsEnabled())
108-
{
109-
NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
110-
}
111-
112-
return;
117+
NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
113118
}
114119

115-
Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true);
116-
Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false);
120+
return;
117121
}
118122

119-
IsQuicSupported = true;
120-
}
121-
finally
122-
{
123-
// Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
124-
bool closed = TryCloseMsQuic(msQuicHandle, apiTable);
125-
Debug.Assert(closed, "Failed to close MsQuic");
123+
Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true);
124+
Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false);
126125
}
126+
127+
IsQuicSupported = true;
127128
}
128129
finally
129130
{
130-
// Unload the library, we will load it again when we actually use QUIC
131-
NativeLibrary.Free(msQuicHandle);
131+
// Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
132+
MsQuicClose(apiTable);
132133
}
133134
}
135+
#pragma warning restore CA1810
134136

135137
private static MsQuicApi AllocateMsQuicApi()
136138
{
137139
Debug.Assert(IsQuicSupported);
138140

139-
int openStatus = MsQuic.QUIC_STATUS_INTERNAL_ERROR;
140-
141-
if (TryLoadMsQuic(out IntPtr msQuicHandle) &&
142-
TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable, out openStatus))
141+
if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus))
143142
{
144-
return new MsQuicApi(apiTable);
143+
throw ThrowHelper.GetExceptionForMsQuicStatus(openStatus);
145144
}
146145

147-
ThrowHelper.ThrowIfMsQuicError(openStatus);
148-
149-
// this should unreachable as TryOpenMsQuic returns non-success status on failure
150-
throw new Exception("Failed to create MsQuicApi instance");
146+
return new MsQuicApi(apiTable);
151147
}
152148

153-
private static bool TryLoadMsQuic(out IntPtr msQuicHandle) =>
154-
NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) ||
155-
NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle);
156-
157-
private static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTable, out int openStatus)
149+
private static bool TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus)
158150
{
159-
apiTable = null;
160-
if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
161-
{
162-
if (NetEventSource.Log.IsEnabled())
163-
{
164-
NetEventSource.Info(null, "Failed to get MsQuicOpenVersion export in msquic library.");
165-
}
166-
167-
openStatus = MsQuic.QUIC_STATUS_NOT_FOUND;
168-
return false;
169-
}
151+
Debug.Assert(MsQuicOpenVersion != null);
170152

171153
QUIC_API_TABLE* table = null;
172-
delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
173-
openStatus = msQuicOpenVersion((uint)MsQuicVersion.Major, &table);
154+
openStatus = MsQuicOpenVersion((uint)MsQuicVersion.Major, &table);
174155
if (StatusFailed(openStatus))
175156
{
176157
if (NetEventSource.Log.IsEnabled())
177158
{
178159
NetEventSource.Info(null, $"MsQuicOpenVersion returned {openStatus} status code.");
179160
}
180161

162+
apiTable = null;
181163
return false;
182164
}
183165

184166
apiTable = table;
185167
return true;
186168
}
187169

188-
private static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable)
189-
{
190-
if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose))
191-
{
192-
((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable);
193-
return true;
194-
}
195-
196-
return false;
197-
}
198-
199170
private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major,
200171
MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision);
201172

0 commit comments

Comments
 (0)