diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go index 6cb27894bd4c..64801e6807a6 100644 --- a/transport/internet/http/dialer.go +++ b/transport/internet/http/dialer.go @@ -120,6 +120,15 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in }, } transport = roundTripper + + if fingerprint := tls.GetQuicFingerprint(tlsConfigs.Fingerprint); fingerprint != nil { + quicSpec, err := quic.QUICID2Spec(*fingerprint) + if err != nil { + errors.LogError(ctx, "unknown fingerprint: ", tlsConfigs.Fingerprint) + } else { + transport = http3.GetURoundTripper(roundTripper, &quicSpec, nil) + } + } } else { transportH2 := &http2.Transport{ DialTLSContext: func(hctx context.Context, string, addr string, tlsConfig *gotls.Config) (net.Conn, error) { diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index f5bf88c351b9..a3f399377c45 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -73,7 +73,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in } muxManager = NewMuxManager(mux, func() interface{} { - return createHTTPClient(dest, streamSettings) + return createHTTPClient(ctx, dest, streamSettings) }) globalDialerMap[key] = muxManager } @@ -82,7 +82,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in return res.Resource.(DialerClient), res } -func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient { +func createHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient { tlsConfig := tls.ConfigFromStreamSettings(streamSettings) realityConfig := reality.ConfigFromStreamSettings(streamSettings) @@ -145,7 +145,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea MaxIncomingStreams: -1, KeepAlivePeriod: h3KeepalivePeriod, } - transport = &http3.RoundTripper{ + roundTripper := &http3.RoundTripper{ QuicConfig: quicConfig, TLSClientConfig: tls.CopyConfig(gotlsConfig), Dial: func(ctx context.Context, addr string, tlsCfg *utls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { @@ -185,6 +185,15 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg) }, } + transport = roundTripper + if fingerprint := tls.GetQuicFingerprint(tlsConfig.Fingerprint); fingerprint != nil { + quicSpec, err := quic.QUICID2Spec(*fingerprint) + if err != nil { + errors.LogError(ctx, "unknown fingerprint: ", tlsConfig.Fingerprint) + } else { + transport = http3.GetURoundTripper(roundTripper, &quicSpec, nil) + } + } } else if isH2 { transport = &http2.Transport{ DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) { diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index 24eb9c6938e7..740bf9d43238 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -7,6 +7,7 @@ import ( "math/big" "time" + "github.com/refraction-networking/uquic" utls "github.com/refraction-networking/utls" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" @@ -154,6 +155,18 @@ func init() { } i++ } + + bigInt, _ = rand.Int(rand.Reader, big.NewInt(int64(len(QuicAllFingerprints)))) + stopAt = int(bigInt.Int64()) + i = 0 + for _, v := range QuicAllFingerprints { + if i == stopAt { + QuicPresetFingerprints["random"] = v + break + } + i++ + } + weights := utls.DefaultWeights weights.TLSVersMax_Set_VersionTLS13 = 1 weights.FirstKeyShare_Set_CurveP256 = 0 @@ -179,6 +192,35 @@ func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) { return } +func GetQuicFingerprint(name string) (fingerprint *quic.QUICID) { + if name == "" { + return + } + if fingerprint = QuicPresetFingerprints[name]; fingerprint != nil { + return + } + if fingerprint = QuicAllFingerprints[name]; fingerprint != nil { + return + } + return +} + +var QuicPresetFingerprints = map[string]*quic.QUICID { + "chrome": &quic.QUICChrome_115, + "firefox": &quic.QUICFirefox_116, + "random": nil, +} + +var QuicAllFingerprints = map[string]*quic.QUICID { + "quicchrome_115": &quic.QUICChrome_115, + "quicchrome_115_ipv4": &quic.QUICChrome_115_IPv4, + "quicchrome_115_ipv6": &quic.QUICChrome_115_IPv6, + "quicfirefox_116": &quic.QUICFirefox_116, + "quicfirefox_116a": &quic.QUICFirefox_116A, + "quicfirefox_116b": &quic.QUICFirefox_116B, + "quicfirefox_116c": &quic.QUICFirefox_116C, +} + var PresetFingerprints = map[string]*utls.ClientHelloID{ // Recommended preset options in GUI clients "chrome": &utls.HelloChrome_Auto,