using System.Runtime.InteropServices; namespace MoShiro.Measurement; /// Loads the MsQuic v2 ABI used by the QUIC datagram transport. internal static class MsQuicRuntime { private const uint ApiVersion = 2; private static readonly object s_gate = new(); private static nint s_library; private static nint s_api; private static OpenVersionDelegate? s_open; private static CloseDelegate? s_close; private static readonly List s_supportLibraries = []; public static nint Api { get { lock (s_gate) { if (s_api != 0) { return s_api; } string[] candidates = OperatingSystem.IsWindows() ? ["msquic.dll"] : OperatingSystem.IsMacOS() ? ["libmsquic.dylib"] : ["libmsquic.so.2", "libmsquic.so"]; LoadBundledSupportLibraries(); string appDirectory = AppContext.BaseDirectory; foreach (string candidate in candidates) { if (NativeLibrary.TryLoad(Path.Combine(appDirectory, candidate), out s_library) || NativeLibrary.TryLoad(candidate, out s_library)) { break; } } if (s_library == 0) { throw new DllNotFoundException($"MsQuic runtime was not found. Expected one of: {string.Join(", ", candidates)}."); } s_open = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(s_library, "MsQuicOpenVersion")); s_close = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(s_library, "MsQuicClose")); int status = s_open(ApiVersion, out s_api); if (status != 0 || s_api == 0) { NativeLibrary.Free(s_library); s_library = 0; s_api = 0; throw new InvalidOperationException($"MsQuic API v{ApiVersion} could not be opened (status {status})."); } return s_api; } } } public static void Close() { lock (s_gate) { if (s_api != 0) { s_close!(s_api); s_api = 0; } if (s_library != 0) { NativeLibrary.Free(s_library); s_library = 0; } for (int i = s_supportLibraries.Count - 1; i >= 0; i--) { NativeLibrary.Free(s_supportLibraries[i]); } s_supportLibraries.Clear(); } } private static void LoadBundledSupportLibraries() { if (OperatingSystem.IsLinux()) { TryLoadSupport("libnuma.so.1"); } else if (OperatingSystem.IsMacOS()) { TryLoadSupport("libcrypto.3.dylib"); TryLoadSupport("libssl.3.dylib"); } } private static void TryLoadSupport(string library) { if (NativeLibrary.TryLoad(Path.Combine(AppContext.BaseDirectory, library), out nint handle)) { s_supportLibraries.Add(handle); } } public static T Function(int index) where T : Delegate => Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(Api, checked(index * IntPtr.Size))); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int OpenVersionDelegate(uint version, out nint api); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void CloseDelegate(nint api); }