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); }
/// <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); }
// 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); }