Ejemplo n.º 1
0
        private static void boundStreamSettings(VMess server, ref StreamSettings streamSettings)
        {
            try
            {
                streamSettings.network = server.TransferProtocol;
                var host = server.Host;
                if (server.TLSSecure)
                {
                    streamSettings.security = "tls";

                    var tlsSettings = new TlsSettings
                    {
                        allowInsecure = Global.Settings.V2RayConfig.AllowInsecure
                    };
                    if (!string.IsNullOrWhiteSpace(host))
                    {
                        tlsSettings.serverName = host;
                    }

                    streamSettings.tlsSettings = tlsSettings;
                }
                else
                {
                    streamSettings.security = "";
                }

                switch (server.TransferProtocol)
                {
                case "kcp":
                    var kcpSettings = new KcpSettings
                    {
                        mtu              = Global.Settings.V2RayConfig.KcpConfig.mtu,
                        tti              = Global.Settings.V2RayConfig.KcpConfig.tti,
                        uplinkCapacity   = Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity,
                        downlinkCapacity = Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity,
                        congestion       = Global.Settings.V2RayConfig.KcpConfig.congestion,
                        readBufferSize   = Global.Settings.V2RayConfig.KcpConfig.readBufferSize,
                        writeBufferSize  = Global.Settings.V2RayConfig.KcpConfig.writeBufferSize,
                        header           = new Header
                        {
                            type = server.FakeType
                        },
                        seed = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null
                    };


                    streamSettings.kcpSettings = kcpSettings;
                    break;

                case "ws":
                    var path       = server.Path;
                    var wsSettings = new WsSettings
                    {
                        connectionReuse = true,
                        headers         = !string.IsNullOrWhiteSpace(host)
                                ? new Headers
                        {
                            Host = host
                        }
                                : null,
                        path = !string.IsNullOrWhiteSpace(path) ? path : null
                    };

                    streamSettings.wsSettings = wsSettings;
                    break;

                case "h2":
                    var httpSettings = new HttpSettings
                    {
                        host = new List <string>
                        {
                            string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host
                        },
                        path = server.Path
                    };

                    streamSettings.httpSettings = httpSettings;
                    break;

                case "quic":
                    var quicSettings = new QuicSettings
                    {
                        security = host,
                        key      = server.Path,
                        header   = new Header
                        {
                            type = server.FakeType
                        }
                    };
                    if (server.TLSSecure)
                    {
                        streamSettings.tlsSettings.serverName = server.Hostname;
                    }

                    streamSettings.quicSettings = quicSettings;
                    break;

                case "xtls":
                    streamSettings.security = server.TransferProtocol;

                    var xtlsSettings = new TlsSettings
                    {
                        allowInsecure = Global.Settings.V2RayConfig.AllowInsecure
                    };
                    if (!string.IsNullOrWhiteSpace(host))
                    {
                        xtlsSettings.serverName = host;
                    }

                    streamSettings.xtlsSettings = xtlsSettings;
                    break;

                default:
                    if (server.FakeType == "http")
                    {
                        var tcpSettings = new TcpSettings
                        {
                            connectionReuse = true,
                            header          = new Header
                            {
                                type    = server.FakeType,
                                request = new TCPRequest
                                {
                                    path    = string.IsNullOrWhiteSpace(server.Path) ? "/" : server.Path,
                                    headers = new TCPRequestHeaders
                                    {
                                        Host = string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host
                                    }
                                }
                            }
                        };

                        streamSettings.tcpSettings = tcpSettings;
                    }

                    break;
                }
            }
            catch
            {
                // ignored
            }
        }
        // TODO: this is called from MsQuicListener and when it fails it wreaks havoc in MsQuicListener finalizer.
        //       Consider moving bigger logic like this outside of constructor call chains.
        private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, QUIC_CREDENTIAL_FLAGS flags, X509Certificate?certificate, List <SslApplicationProtocol>?alpnProtocols)
        {
            // TODO: some of these checks should be done by the QuicOptions type.
            if (alpnProtocols == null || alpnProtocols.Count == 0)
            {
                throw new Exception("At least one SslApplicationProtocol value must be present in SslClientAuthenticationOptions or SslServerAuthenticationOptions.");
            }

            if (options.MaxBidirectionalStreams > ushort.MaxValue)
            {
                throw new Exception("MaxBidirectionalStreams overflow.");
            }

            if (options.MaxBidirectionalStreams > ushort.MaxValue)
            {
                throw new Exception("MaxBidirectionalStreams overflow.");
            }

            if ((flags & QUIC_CREDENTIAL_FLAGS.CLIENT) == 0)
            {
                if (certificate == null)
                {
                    throw new Exception("Server must provide certificate");
                }
            }
            else
            {
                flags |= QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED | QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION;
            }

            if (!OperatingSystem.IsWindows())
            {
                // Use certificate handles on Windows, fall-back to ASN1 otherwise.
                flags |= QUIC_CREDENTIAL_FLAGS.USE_PORTABLE_CERTIFICATES;
            }

            Debug.Assert(!MsQuicApi.Api.Registration.IsInvalid);

            var settings = new QuicSettings
            {
                IsSetFlags = QuicSettingsIsSetFlags.PeerBidiStreamCount |
                             QuicSettingsIsSetFlags.PeerUnidiStreamCount,
                PeerBidiStreamCount  = (ushort)options.MaxBidirectionalStreams,
                PeerUnidiStreamCount = (ushort)options.MaxUnidirectionalStreams
            };

            if (options.IdleTimeout != Timeout.InfiniteTimeSpan)
            {
                if (options.IdleTimeout <= TimeSpan.Zero)
                {
                    throw new Exception("IdleTimeout must not be negative.");
                }

                ulong ms = (ulong)options.IdleTimeout.Ticks / TimeSpan.TicksPerMillisecond;
                if (ms > (1ul << 62) - 1)
                {
                    throw new Exception("IdleTimeout is too large (max 2^62-1 milliseconds)");
                }

                settings.IsSetFlags   |= QuicSettingsIsSetFlags.IdleTimeoutMs;
                settings.IdleTimeoutMs = (ulong)options.IdleTimeout.TotalMilliseconds;
            }

            uint status;
            SafeMsQuicConfigurationHandle?configurationHandle;

            MemoryHandle[]? handles = null;
            QuicBuffer[]? buffers   = null;
            try
            {
                MsQuicAlpnHelper.Prepare(alpnProtocols, out handles, out buffers);
                status = MsQuicApi.Api.ConfigurationOpenDelegate(MsQuicApi.Api.Registration, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)alpnProtocols.Count, ref settings, (uint)sizeof(QuicSettings), context: IntPtr.Zero, out configurationHandle);
            }
            finally
            {
                MsQuicAlpnHelper.Return(ref handles, ref buffers);
            }

            QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationOpen failed.");

            try
            {
                CredentialConfig config = default;
                config.Flags = flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback.

                if (certificate != null)
                {
                    if (OperatingSystem.IsWindows())
                    {
                        config.Type        = QUIC_CREDENTIAL_TYPE.CONTEXT;
                        config.Certificate = certificate.Handle;
                        status             = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config);
                    }
                    else
                    {
                        CredentialConfigCertificatePkcs12 pkcs12Config;
                        byte[] asn1 = certificate.Export(X509ContentType.Pkcs12);
                        fixed(void *ptr = asn1)
                        {
                            pkcs12Config.Asn1Blob           = (IntPtr)ptr;
                            pkcs12Config.Asn1BlobLength     = (uint)asn1.Length;
                            pkcs12Config.PrivateKeyPassword = IntPtr.Zero;

                            config.Type        = QUIC_CREDENTIAL_TYPE.PKCS12;
                            config.Certificate = (IntPtr)(&pkcs12Config);
                            status             = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config);
                        }
                    }
                }
                else
                {
                    config.Type = QUIC_CREDENTIAL_TYPE.NONE;
                    status      = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config);
                }

                QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationLoadCredential failed.");
            }
            catch
            {
                configurationHandle.Dispose();
                throw;
            }

            return(configurationHandle);
        }
        // TODO: this is called from MsQuicListener and when it fails it wreaks havoc in MsQuicListener finalizer.
        //       Consider moving bigger logic like this outside of constructor call chains.
        private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, QUIC_CREDENTIAL_FLAGS flags, X509Certificate?certificate, List <SslApplicationProtocol>?alpnProtocols)
        {
            // TODO: some of these checks should be done by the QuicOptions type.
            if (alpnProtocols == null || alpnProtocols.Count == 0)
            {
                throw new Exception("At least one SslApplicationProtocol value must be present in SslClientAuthenticationOptions or SslServerAuthenticationOptions.");
            }

            if (options.MaxBidirectionalStreams > ushort.MaxValue)
            {
                throw new Exception("MaxBidirectionalStreams overflow.");
            }

            if (options.MaxBidirectionalStreams > ushort.MaxValue)
            {
                throw new Exception("MaxBidirectionalStreams overflow.");
            }

            Debug.Assert(!MsQuicApi.Api.Registration.IsInvalid);

            var settings = new QuicSettings
            {
                IsSetFlags = QuicSettingsIsSetFlags.PeerBidiStreamCount |
                             QuicSettingsIsSetFlags.PeerUnidiStreamCount,
                PeerBidiStreamCount  = (ushort)options.MaxBidirectionalStreams,
                PeerUnidiStreamCount = (ushort)options.MaxUnidirectionalStreams
            };

            if (options.IdleTimeout != Timeout.InfiniteTimeSpan)
            {
                if (options.IdleTimeout <= TimeSpan.Zero)
                {
                    throw new Exception("IdleTimeout must not be negative.");
                }

                ulong ms = (ulong)options.IdleTimeout.Ticks / TimeSpan.TicksPerMillisecond;
                if (ms > (1ul << 62) - 1)
                {
                    throw new Exception("IdleTimeout is too large (max 2^62-1 milliseconds)");
                }

                settings.IsSetFlags   |= QuicSettingsIsSetFlags.IdleTimeoutMs;
                settings.IdleTimeoutMs = (ulong)options.IdleTimeout.TotalMilliseconds;
            }

            uint status;
            SafeMsQuicConfigurationHandle?configurationHandle;

            MemoryHandle[]? handles = null;
            QuicBuffer[]? buffers   = null;
            try
            {
                MsQuicAlpnHelper.Prepare(alpnProtocols, out handles, out buffers);
                status = MsQuicApi.Api.ConfigurationOpenDelegate(MsQuicApi.Api.Registration, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)alpnProtocols.Count, ref settings, (uint)sizeof(QuicSettings), context: IntPtr.Zero, out configurationHandle);
            }
            finally
            {
                MsQuicAlpnHelper.Return(ref handles, ref buffers);
            }

            QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationOpen failed.");

            try
            {
                // TODO: find out what to do for OpenSSL here -- passing handle won't work, because
                // MsQuic has a private copy of OpenSSL so the SSL_CTX will be incompatible.

                CredentialConfig config = default;

                config.Flags = flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback.

                if (certificate != null)
                {
#if true
                    // If using stub TLS.
                    config.Type = QUIC_CREDENTIAL_TYPE.STUB_NULL;
#else
                    // TODO: doesn't work on non-Windows
                    config.Type        = QUIC_CREDENTIAL_TYPE.CONTEXT;
                    config.Certificate = certificate.Handle;
#endif
                }
                else
                {
                    // TODO: not allowed for OpenSSL and server
                    config.Type = QUIC_CREDENTIAL_TYPE.NONE;
                }

                status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config);
                QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationLoadCredential failed.");
            }
            catch
            {
                configurationHandle.Dispose();
                throw;
            }

            return(configurationHandle);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// vmess协议远程服务器底层传输配置
        /// </summary>
        /// <param name="config"></param>
        /// <param name="iobound"></param>
        /// <param name="streamSettings"></param>
        /// <returns></returns>
        private static int boundStreamSettings(Config config, string iobound, ref StreamSettings streamSettings)
        {
            try
            {
                //远程服务器底层传输配置
                streamSettings.network = config.network();
                var host = config.requestHost();

                //if tls
                if (config.streamSecurity() == "tls")
                {
                    streamSettings.security = config.streamSecurity();

                    TlsSettings tlsSettings = new TlsSettings();
                    tlsSettings.allowInsecure = config.allowInsecure();
                    if (!string.IsNullOrWhiteSpace(host))
                    {
                        tlsSettings.serverName = host;
                    }
                    streamSettings.tlsSettings = tlsSettings;
                }

                //streamSettings
                switch (config.network())
                {
                //kcp基本配置暂时是默认值,用户能自己设置伪装类型
                case "kcp":
                    KcpSettings kcpSettings = new KcpSettings();
                    kcpSettings.mtu = config.kcpItem.mtu;
                    kcpSettings.tti = config.kcpItem.tti;
                    if (iobound.Equals("out"))
                    {
                        kcpSettings.uplinkCapacity   = config.kcpItem.uplinkCapacity;
                        kcpSettings.downlinkCapacity = config.kcpItem.downlinkCapacity;
                    }
                    else if (iobound.Equals("in"))
                    {
                        kcpSettings.uplinkCapacity   = config.kcpItem.downlinkCapacity;;
                        kcpSettings.downlinkCapacity = config.kcpItem.downlinkCapacity;
                    }
                    else
                    {
                        kcpSettings.uplinkCapacity   = config.kcpItem.uplinkCapacity;
                        kcpSettings.downlinkCapacity = config.kcpItem.downlinkCapacity;
                    }

                    kcpSettings.congestion      = config.kcpItem.congestion;
                    kcpSettings.readBufferSize  = config.kcpItem.readBufferSize;
                    kcpSettings.writeBufferSize = config.kcpItem.writeBufferSize;
                    kcpSettings.header          = new Header();
                    kcpSettings.header.type     = config.headerType();
                    streamSettings.kcpSettings  = kcpSettings;
                    break;

                //ws
                case "ws":
                    WsSettings wsSettings = new WsSettings();
                    wsSettings.connectionReuse = true;

                    string path = config.path();
                    if (!string.IsNullOrWhiteSpace(host))
                    {
                        wsSettings.headers      = new Headers();
                        wsSettings.headers.Host = host;
                    }
                    if (!string.IsNullOrWhiteSpace(path))
                    {
                        wsSettings.path = path;
                    }
                    streamSettings.wsSettings = wsSettings;

                    //TlsSettings tlsSettings = new TlsSettings();
                    //tlsSettings.allowInsecure = config.allowInsecure();
                    //if (!string.IsNullOrWhiteSpace(host))
                    //{
                    //    tlsSettings.serverName = host;
                    //}
                    //streamSettings.tlsSettings = tlsSettings;
                    break;

                //h2
                case "h2":
                    HttpSettings httpSettings = new HttpSettings();

                    if (!string.IsNullOrWhiteSpace(host))
                    {
                        httpSettings.host = Utils.String2List(host);
                    }
                    httpSettings.path = config.path();

                    streamSettings.httpSettings = httpSettings;

                    //TlsSettings tlsSettings2 = new TlsSettings();
                    //tlsSettings2.allowInsecure = config.allowInsecure();
                    //streamSettings.tlsSettings = tlsSettings2;
                    break;

                //quic
                case "quic":
                    QuicSettings quicsettings = new QuicSettings();
                    quicsettings.security    = host;
                    quicsettings.key         = config.path();
                    quicsettings.header      = new Header();
                    quicsettings.header.type = config.headerType();

                    streamSettings.quicSettings = quicsettings;
                    break;

                default:
                    //tcp带http伪装
                    if (config.headerType().Equals(Global.TcpHeaderHttp))
                    {
                        TcpSettings tcpSettings = new TcpSettings();
                        tcpSettings.connectionReuse = true;
                        tcpSettings.header          = new Header();
                        tcpSettings.header.type     = config.headerType();

                        if (iobound.Equals("out"))
                        {
                            //request填入自定义Host
                            string   request = Utils.GetEmbedText(Global.v2raySampleHttprequestFileName);
                            string[] arrHost = host.Split(',');
                            string   host2   = string.Join("\",\"", arrHost);
                            request = request.Replace("$requestHost$", string.Format("\"{0}\"", host2));
                            //request = request.Replace("$requestHost$", string.Format("\"{0}\"", config.requestHost()));

                            //填入自定义Path
                            string pathHttp = @"/";
                            if (!Utils.IsNullOrEmpty(config.path()))
                            {
                                string[] arrPath = config.path().Split(',');
                                pathHttp = string.Join("\",\"", arrPath);
                            }
                            request = request.Replace("$requestPath$", string.Format("\"{0}\"", pathHttp));
                            tcpSettings.header.request = Utils.FromJson <object>(request);
                        }
                        else if (iobound.Equals("in"))
                        {
                            //string response = Utils.GetEmbedText(Global.v2raySampleHttpresponseFileName);
                            //tcpSettings.header.response = Utils.FromJson<object>(response);
                        }

                        streamSettings.tcpSettings = tcpSettings;
                    }
                    break;
                }
            }
            catch
            {
            }
            return(0);
        }
Ejemplo n.º 5
0
        // TODO: this is called from MsQuicListener and when it fails it wreaks havoc in MsQuicListener finalizer.
        //       Consider moving bigger logic like this outside of constructor call chains.
        private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, QUIC_CREDENTIAL_FLAGS flags, X509Certificate?certificate, SslStreamCertificateContext?certificateContext, List <SslApplicationProtocol>?alpnProtocols, CipherSuitesPolicy?cipherSuitesPolicy)
        {
            // TODO: some of these checks should be done by the QuicOptions type.
            if (alpnProtocols == null || alpnProtocols.Count == 0)
            {
                throw new Exception("At least one SslApplicationProtocol value must be present in SslClientAuthenticationOptions or SslServerAuthenticationOptions.");
            }

            if (options.MaxBidirectionalStreams > ushort.MaxValue)
            {
                throw new Exception("MaxBidirectionalStreams overflow.");
            }

            if (options.MaxBidirectionalStreams > ushort.MaxValue)
            {
                throw new Exception("MaxBidirectionalStreams overflow.");
            }

            if ((flags & QUIC_CREDENTIAL_FLAGS.CLIENT) == 0)
            {
                if (certificate == null && certificateContext == null)
                {
                    throw new Exception("Server must provide certificate");
                }
            }
            else
            {
                flags |= QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED | QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION;
            }

            if (!OperatingSystem.IsWindows())
            {
                // Use certificate handles on Windows, fall-back to ASN1 otherwise.
                flags |= QUIC_CREDENTIAL_FLAGS.USE_PORTABLE_CERTIFICATES;
            }

            Debug.Assert(!MsQuicApi.Api.Registration.IsInvalid);

            var settings = new QuicSettings
            {
                IsSetFlags = QuicSettingsIsSetFlags.PeerBidiStreamCount |
                             QuicSettingsIsSetFlags.PeerUnidiStreamCount,
                PeerBidiStreamCount  = (ushort)options.MaxBidirectionalStreams,
                PeerUnidiStreamCount = (ushort)options.MaxUnidirectionalStreams
            };

            if (options.IdleTimeout != Timeout.InfiniteTimeSpan)
            {
                if (options.IdleTimeout <= TimeSpan.Zero)
                {
                    throw new Exception("IdleTimeout must not be negative.");
                }

                ulong ms = (ulong)options.IdleTimeout.Ticks / TimeSpan.TicksPerMillisecond;
                if (ms > (1ul << 62) - 1)
                {
                    throw new Exception("IdleTimeout is too large (max 2^62-1 milliseconds)");
                }

                settings.IdleTimeoutMs = (ulong)options.IdleTimeout.TotalMilliseconds;
            }
            else
            {
                settings.IdleTimeoutMs = 0;
            }
            settings.IsSetFlags |= QuicSettingsIsSetFlags.IdleTimeoutMs;

            uint status;
            SafeMsQuicConfigurationHandle?configurationHandle;

            X509Certificate2[]? intermediates = null;

            MemoryHandle[]? handles = null;
            QuicBuffer[]? buffers   = null;
            try
            {
                MsQuicAlpnHelper.Prepare(alpnProtocols, out handles, out buffers);
                status = MsQuicApi.Api.ConfigurationOpenDelegate(MsQuicApi.Api.Registration, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)alpnProtocols.Count, ref settings, (uint)sizeof(QuicSettings), context: IntPtr.Zero, out configurationHandle);
            }
            finally
            {
                MsQuicAlpnHelper.Return(ref handles, ref buffers);
            }

            QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationOpen failed.");

            try
            {
                CredentialConfig config = default;
                config.Flags = flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback.

                if (cipherSuitesPolicy != null)
                {
                    config.Flags |= QUIC_CREDENTIAL_FLAGS.SET_ALLOWED_CIPHER_SUITES;
                    config.AllowedCipherSuites = CipherSuitePolicyToFlags(cipherSuitesPolicy);
                }

                if (certificateContext != null)
                {
                    certificate   = (X509Certificate2?)_contextCertificate.GetValue(certificateContext);
                    intermediates = (X509Certificate2[]?)_contextChain.GetValue(certificateContext);

                    if (certificate == null || intermediates == null)
                    {
                        throw new ArgumentException(nameof(certificateContext));
                    }
                }

                if (certificate != null)
                {
                    if (OperatingSystem.IsWindows())
                    {
                        config.Type        = QUIC_CREDENTIAL_TYPE.CONTEXT;
                        config.Certificate = certificate.Handle;
                        status             = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config);
                    }
                    else
                    {
                        CredentialConfigCertificatePkcs12 pkcs12Config;
                        byte[] asn1;

                        if (intermediates?.Length > 0)
                        {
                            X509Certificate2Collection collection = new X509Certificate2Collection();
                            collection.Add(certificate);
                            for (int i = 0; i < intermediates?.Length; i++)
                            {
                                collection.Add(intermediates[i]);
                            }

                            asn1 = collection.Export(X509ContentType.Pkcs12) !;
                        }
                        else
                        {
                            asn1 = certificate.Export(X509ContentType.Pkcs12);
                        }

                        fixed(void *ptr = asn1)
                        {
                            pkcs12Config.Asn1Blob           = (IntPtr)ptr;
                            pkcs12Config.Asn1BlobLength     = (uint)asn1.Length;
                            pkcs12Config.PrivateKeyPassword = IntPtr.Zero;

                            config.Type        = QUIC_CREDENTIAL_TYPE.PKCS12;
                            config.Certificate = (IntPtr)(&pkcs12Config);
                            status             = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config);
                        }
                    }
                }
                else
                {
                    config.Type = QUIC_CREDENTIAL_TYPE.NONE;
                    status      = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config);
                }

#if TARGET_WINDOWS
                if ((Interop.SECURITY_STATUS)status == Interop.SECURITY_STATUS.AlgorithmMismatch && MsQuicApi.Tls13MayBeDisabled)
                {
                    throw new QuicException(SR.net_ssl_app_protocols_invalid, null, (int)status);
                }
#endif

                QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationLoadCredential failed.");
            }
            catch
            {
                configurationHandle.Dispose();
                throw;
            }

            return(configurationHandle);
        }