From a8ce283727d8f8b893881112fe9c4599ac2d71c8 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 11 Jul 2022 13:42:28 +0800 Subject: [PATCH] feat: add fingerprint param --- adapter/outbound/http.go | 23 ++++++++++++----- adapter/outbound/hysteria.go | 16 ++++++++++-- adapter/outbound/shadowsocks.go | 1 + adapter/outbound/socks5.go | 16 +++++++++--- adapter/outbound/trojan.go | 15 +++++++++-- adapter/outbound/vless.go | 16 +++++++++--- adapter/outbound/vmess.go | 17 +++++++++--- adapter/parser.go | 4 +-- component/tls/config.go | 40 ++++++++++++++++++++++++++--- dns/client.go | 2 +- dns/doq.go | 2 +- docs/config.yaml | 13 ++++++++++ transport/trojan/trojan.go | 21 +++++++++++++++ transport/v2ray-plugin/websocket.go | 13 +++++++++- transport/vless/xtls.go | 10 ++++++++ transport/vmess/tls.go | 14 ++++++++-- 16 files changed, 193 insertions(+), 30 deletions(-) diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 6361f7ba..e8ff3e96 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -36,6 +36,7 @@ type HttpOption struct { TLS bool `proxy:"tls,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` Headers map[string]string `proxy:"headers,omitempty"` } @@ -127,16 +128,26 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { return fmt.Errorf("can not connect remote err code: %d", resp.StatusCode) } -func NewHttp(option HttpOption) *Http { +func NewHttp(option HttpOption) (*Http, error) { var tlsConfig *tls.Config if option.TLS { sni := option.Server if option.SNI != "" { sni = option.SNI } - tlsConfig = &tls.Config{ - InsecureSkipVerify: option.SkipCertVerify, - ServerName: sni, + if len(option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(&tls.Config{ + InsecureSkipVerify: option.SkipCertVerify, + ServerName: sni, + }) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(&tls.Config{ + InsecureSkipVerify: option.SkipCertVerify, + ServerName: sni, + }, option.Fingerprint); err != nil { + return nil, err + } } } @@ -150,7 +161,7 @@ func NewHttp(option HttpOption) *Http { }, user: option.UserName, pass: option.Password, - tlsConfig: tlsC.MixinTLSConfig(tlsConfig), + tlsConfig: tlsConfig, option: &option, - } + }, nil } diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 0635ffac..4d45654a 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -87,6 +87,7 @@ type HysteriaOption struct { Obfs string `proxy:"obfs,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` ALPN string `proxy:"alpn,omitempty"` CustomCA string `proxy:"ca,omitempty"` CustomCAString string `proxy:"ca_str,omitempty"` @@ -122,11 +123,22 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if option.SNI != "" { serverName = option.SNI } - tlsConfig := tlsC.MixinTLSConfig(&tls.Config{ + + tlsConfig := &tls.Config{ ServerName: serverName, InsecureSkipVerify: option.SkipCertVerify, MinVersion: tls.VersionTLS13, - }) + } + if len(option.Fingerprint) != 0 { + var err error + tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err + } + } else { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } + if len(option.ALPN) > 0 { tlsConfig.NextProtos = []string{option.ALPN} } else { diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 4d25e9d2..fc872835 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -60,6 +60,7 @@ type v2rayObfsOption struct { Host string `obfs:"host,omitempty"` Path string `obfs:"path,omitempty"` TLS bool `obfs:"tls,omitempty"` + Fingerprint string `obfs:"fingerprint,omitempty"` Headers map[string]string `obfs:"headers,omitempty"` SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` Mux bool `obfs:"mux,omitempty"` diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index ac49dd8c..4e79d6a9 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -34,6 +34,7 @@ type Socks5Option struct { TLS bool `proxy:"tls,omitempty"` UDP bool `proxy:"udp,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` } // StreamConn implements C.ProxyAdapter @@ -139,13 +140,22 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil } -func NewSocks5(option Socks5Option) *Socks5 { +func NewSocks5(option Socks5Option) (*Socks5, error) { var tlsConfig *tls.Config if option.TLS { tlsConfig = &tls.Config{ InsecureSkipVerify: option.SkipCertVerify, ServerName: option.Server, } + + if len(option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { + return nil, err + } + } } return &Socks5{ @@ -161,8 +171,8 @@ func NewSocks5(option Socks5Option) *Socks5 { pass: option.Password, tls: option.TLS, skipCertVerify: option.SkipCertVerify, - tlsConfig: tlsC.MixinTLSConfig(tlsConfig), - } + tlsConfig: tlsConfig, + }, nil } type socksPacketConn struct { diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 43165e2b..88c87e22 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -36,6 +36,7 @@ type TrojanOption struct { ALPN []string `proxy:"alpn,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` UDP bool `proxy:"udp,omitempty"` Network string `proxy:"network,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` @@ -189,6 +190,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { ServerName: option.Server, SkipCertVerify: option.SkipCertVerify, FlowShow: option.FlowShow, + Fingerprint: option.Fingerprint, } if option.Network != "ws" && len(option.Flow) >= 16 { @@ -228,12 +230,21 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { return c, nil } - tlsConfig := tlsC.MixinTLSConfig(&tls.Config{ + tlsConfig := &tls.Config{ NextProtos: option.ALPN, MinVersion: tls.VersionTLS12, InsecureSkipVerify: tOption.SkipCertVerify, ServerName: tOption.ServerName, - }) + } + + if len(option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { + return nil, err + } + } if t.option.Flow != "" { t.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 4ca732d0..7c30c0a9 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -56,6 +56,7 @@ type VlessOption struct { WSPath string `proxy:"ws-path,omitempty"` WSHeaders map[string]string `proxy:"ws-headers,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` ServerName string `proxy:"servername,omitempty"` } @@ -81,12 +82,19 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { } if v.option.TLS { wsOpts.TLS = true - wsOpts.TLSConfig = tlsC.MixinTLSConfig(&tls.Config{ + tlsConfig := &tls.Config{ MinVersion: tls.VersionTLS12, ServerName: host, InsecureSkipVerify: v.option.SkipCertVerify, NextProtos: []string{"http/1.1"}, - }) + } + + if len(v.option.Fingerprint) == 0 { + wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint) + } + if v.option.ServerName != "" { wsOpts.TLSConfig.ServerName = v.option.ServerName } else if host := wsOpts.Headers.Get("Host"); host != "" { @@ -153,6 +161,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) xtlsOpts := vless.XTLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + FingerPrint: v.option.Fingerprint, } if isH2 { @@ -169,6 +178,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) tlsOpts := vmess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + FingerPrint: v.option.Fingerprint, } if isH2 { @@ -437,7 +447,7 @@ func NewVless(option VlessOption) (*Vless, error) { ServiceName: v.option.GrpcOpts.GrpcServiceName, Host: v.option.ServerName, } - tlsConfig := tlsC.MixinTLSConfig(&tls.Config{ + tlsConfig := tlsC.GetGlobalFingerprintTLCConfig(&tls.Config{ InsecureSkipVerify: v.option.SkipCertVerify, ServerName: v.option.ServerName, }) diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index b600825e..cf7814f1 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" tlsC "github.com/Dreamacro/clash/component/tls" + vmess "github.com/sagernet/sing-vmess" "net" "net/http" "strconv" @@ -17,7 +18,6 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/gun" clashVMess "github.com/Dreamacro/clash/transport/vmess" - "github.com/sagernet/sing-vmess" "github.com/sagernet/sing-vmess/packetaddr" M "github.com/sagernet/sing/common/metadata" ) @@ -45,6 +45,7 @@ type VmessOption struct { Network string `proxy:"network,omitempty"` TLS bool `proxy:"tls,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` ServerName string `proxy:"servername,omitempty"` HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` @@ -100,11 +101,21 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { if v.option.TLS { wsOpts.TLS = true - wsOpts.TLSConfig = tlsC.MixinTLSConfig(&tls.Config{ + tlsConfig := &tls.Config{ ServerName: host, InsecureSkipVerify: v.option.SkipCertVerify, NextProtos: []string{"http/1.1"}, - }) + } + + if len(v.option.Fingerprint) == 0 { + wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint); err != nil { + return nil, err + } + } + if v.option.ServerName != "" { wsOpts.TLSConfig.ServerName = v.option.ServerName } else if host := wsOpts.Headers.Get("Host"); host != "" { diff --git a/adapter/parser.go b/adapter/parser.go index 1c7ea963..b68e81ab 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -40,14 +40,14 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { if err != nil { break } - proxy = outbound.NewSocks5(*socksOption) + proxy, err = outbound.NewSocks5(*socksOption) case "http": httpOption := &outbound.HttpOption{} err = decoder.Decode(mapping, httpOption) if err != nil { break } - proxy = outbound.NewHttp(*httpOption) + proxy, err = outbound.NewHttp(*httpOption) case "vmess": vmessOption := &outbound.VmessOption{ HTTPOpts: outbound.HTTPOptions{ diff --git a/component/tls/config.go b/component/tls/config.go index c22ee23e..9309b503 100644 --- a/component/tls/config.go +++ b/component/tls/config.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "encoding/hex" "fmt" + xtls "github.com/xtls/go" "strings" "sync" "time" @@ -75,11 +76,11 @@ func convertFingerprint(fingerprint string) (*[32]byte, error) { } func GetDefaultTLSConfig() *tls.Config { - return MixinTLSConfig(nil) + return GetGlobalFingerprintTLCConfig(nil) } -// GetTLSConfigWithSpecifiedFingerprint specified fingerprint -func GetTLSConfigWithSpecifiedFingerprint(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) { +// GetSpecifiedFingerprintTLSConfig specified fingerprint +func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) { if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil { return nil, err } else { @@ -96,7 +97,7 @@ func GetTLSConfigWithSpecifiedFingerprint(tlsConfig *tls.Config, fingerprint str } } -func MixinTLSConfig(tlsConfig *tls.Config) *tls.Config { +func GetGlobalFingerprintTLCConfig(tlsConfig *tls.Config) *tls.Config { if tlsConfig == nil { return &tls.Config{ InsecureSkipVerify: true, @@ -108,3 +109,34 @@ func MixinTLSConfig(tlsConfig *tls.Config) *tls.Config { tlsConfig.InsecureSkipVerify = true return tlsConfig } + +// GetSpecifiedFingerprintXTLSConfig specified fingerprint +func GetSpecifiedFingerprintXTLSConfig(tlsConfig *xtls.Config, fingerprint string) (*xtls.Config, error) { + if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil { + return nil, err + } else { + if tlsConfig == nil { + return &xtls.Config{ + InsecureSkipVerify: true, + VerifyPeerCertificate: verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, false), + }, nil + } else { + tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, tlsConfig.InsecureSkipVerify) + tlsConfig.InsecureSkipVerify = true + return tlsConfig, nil + } + } +} + +func GetGlobalFingerprintXTLCConfig(tlsConfig *xtls.Config) *xtls.Config { + if tlsConfig == nil { + return &xtls.Config{ + InsecureSkipVerify: true, + VerifyPeerCertificate: verifyPeerCertificateAndFingerprints(globalFingerprints, false), + } + } + + tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints(globalFingerprints, tlsConfig.InsecureSkipVerify) + tlsConfig.InsecureSkipVerify = true + return tlsConfig +} diff --git a/dns/client.go b/dns/client.go index 327839e8..a377ee42 100644 --- a/dns/client.go +++ b/dns/client.go @@ -78,7 +78,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) ch := make(chan result, 1) go func() { if strings.HasSuffix(c.Client.Net, "tls") { - conn = tls.Client(conn, tlsC.MixinTLSConfig(c.Client.TLSConfig)) + conn = tls.Client(conn, tlsC.GetGlobalFingerprintTLCConfig(c.Client.TLSConfig)) } msg, _, err := c.Client.ExchangeWithConn(m, &D.Conn{ diff --git a/dns/doq.go b/dns/doq.go index 7ed4f54e..f0bbbb2e 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -129,7 +129,7 @@ func (dc *quicClient) getSession(ctx context.Context) (quic.Connection, error) { } func (dc *quicClient) openSession(ctx context.Context) (quic.Connection, error) { - tlsConfig := tlsC.MixinTLSConfig( + tlsConfig := tlsC.GetGlobalFingerprintTLCConfig( &tls.Config{ InsecureSkipVerify: false, NextProtos: []string{ diff --git a/docs/config.yaml b/docs/config.yaml index 7b5d1b89..f791537b 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -178,6 +178,7 @@ proxies: plugin-opts: mode: websocket # no QUIC now # tls: true # wss + # fingerprint: xxxx # skip-cert-verify: true # host: bing.com # path: "/" @@ -196,6 +197,7 @@ proxies: cipher: auto # udp: true # tls: true + # fingerprint: xxxx # skip-cert-verify: true # servername: example.com # priority over wss host # network: ws @@ -215,6 +217,7 @@ proxies: cipher: auto network: h2 tls: true + # fingerprint: xxxx h2-opts: host: - http.example.com @@ -248,6 +251,7 @@ proxies: cipher: auto network: grpc tls: true + # fingerprint: xxxx servername: example.com # skip-cert-verify: true grpc-opts: @@ -261,6 +265,7 @@ proxies: # username: username # password: password # tls: true + # fingerprint: xxxx # skip-cert-verify: true # udp: true @@ -274,6 +279,7 @@ proxies: # tls: true # https # skip-cert-verify: true # sni: custom.com + # fingerprint: xxxx # Snell # Beware that there's currently no UDP support yet @@ -293,6 +299,7 @@ proxies: server: server port: 443 password: yourpsk + # fingerprint: xxxx # udp: true # sni: example.com # aka server name # alpn: @@ -308,6 +315,7 @@ proxies: network: grpc sni: example.com # skip-cert-verify: true + # fingerprint: xxxx udp: true grpc-opts: grpc-service-name: "example" @@ -320,6 +328,7 @@ proxies: network: ws sni: example.com # skip-cert-verify: true + # fingerprint: xxxx udp: true # ws-opts: # path: /path @@ -336,6 +345,7 @@ proxies: # udp: true # sni: example.com # aka server name # skip-cert-verify: true + # fingerprint: xxxx # vless - name: "vless-tcp" @@ -347,6 +357,7 @@ proxies: servername: example.com # AKA SNI # flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS # skip-cert-verify: true + # fingerprint: xxxx - name: "vless-ws" type: vless @@ -358,6 +369,7 @@ proxies: network: ws servername: example.com # priority over wss host # skip-cert-verify: true + # fingerprint: xxxx ws-opts: path: "/" headers: @@ -379,6 +391,7 @@ proxies: #ca: "./my.ca" #ca_str: "xyz" #disable_mtu_discovery: false + # fingerprint: xxxx # ShadowsocksR # The supported ciphers (encryption methods): all stream ciphers in ss diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index d6ee7285..4ebb431b 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "errors" "fmt" + tlsC "github.com/Dreamacro/clash/component/tls" "io" "net" "net/http" @@ -50,6 +51,7 @@ type Option struct { ALPN []string ServerName string SkipCertVerify bool + Fingerprint string Flow string FlowShow bool } @@ -80,6 +82,15 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) { ServerName: t.option.ServerName, } + if len(t.option.Fingerprint) == 0 { + xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig) + } else { + var err error + if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, t.option.Fingerprint); err != nil { + return nil, err + } + } + xtlsConn := xtls.Client(conn, xtlsConfig) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) @@ -95,6 +106,16 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) { InsecureSkipVerify: t.option.SkipCertVerify, ServerName: t.option.ServerName, } + + if len(t.option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint); err != nil { + return nil, err + } + } + tlsConn := tls.Client(conn, tlsConfig) if err := tlsConn.Handshake(); err != nil { return nil, err diff --git a/transport/v2ray-plugin/websocket.go b/transport/v2ray-plugin/websocket.go index 7591b4a8..2a052888 100644 --- a/transport/v2ray-plugin/websocket.go +++ b/transport/v2ray-plugin/websocket.go @@ -2,6 +2,7 @@ package obfs import ( "crypto/tls" + tlsC "github.com/Dreamacro/clash/component/tls" "net" "net/http" @@ -16,6 +17,7 @@ type Option struct { Headers map[string]string TLS bool SkipCertVerify bool + Fingerprint string Mux bool } @@ -35,11 +37,20 @@ func NewV2rayObfs(conn net.Conn, option *Option) (net.Conn, error) { if option.TLS { config.TLS = true - config.TLSConfig = &tls.Config{ + tlsConfig := &tls.Config{ ServerName: option.Host, InsecureSkipVerify: option.SkipCertVerify, NextProtos: []string{"http/1.1"}, } + if len(option.Fingerprint) == 0 { + config.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if config.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { + return nil, err + } + } + if host := config.Headers.Get("Host"); host != "" { config.TLSConfig.ServerName = host } diff --git a/transport/vless/xtls.go b/transport/vless/xtls.go index b9a9fd27..8ef4b44e 100644 --- a/transport/vless/xtls.go +++ b/transport/vless/xtls.go @@ -2,6 +2,7 @@ package vless import ( "context" + tlsC "github.com/Dreamacro/clash/component/tls" "net" C "github.com/Dreamacro/clash/constant" @@ -11,6 +12,7 @@ import ( type XTLSConfig struct { Host string SkipCertVerify bool + FingerPrint string NextProtos []string } @@ -20,6 +22,14 @@ func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) { InsecureSkipVerify: cfg.SkipCertVerify, NextProtos: cfg.NextProtos, } + if len(cfg.FingerPrint) == 0 { + xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig) + } else { + var err error + if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.FingerPrint); err != nil { + return nil, err + } + } xtlsConn := xtls.Client(conn, xtlsConfig) diff --git a/transport/vmess/tls.go b/transport/vmess/tls.go index 55fdb777..75434095 100644 --- a/transport/vmess/tls.go +++ b/transport/vmess/tls.go @@ -12,15 +12,25 @@ import ( type TLSConfig struct { Host string SkipCertVerify bool + FingerPrint string NextProtos []string } func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) { - tlsConfig := tlsC.MixinTLSConfig(&tls.Config{ + tlsConfig := &tls.Config{ ServerName: cfg.Host, InsecureSkipVerify: cfg.SkipCertVerify, NextProtos: cfg.NextProtos, - }) + } + + if len(cfg.FingerPrint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, cfg.FingerPrint); err != nil { + return nil, err + } + } tlsConn := tls.Client(conn, tlsConfig)