diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index b4a4872d..49ff45ed 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -8,7 +8,6 @@ import ( "strconv" "time" - restlsC "github.com/3andne/restls-client-go" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" @@ -19,6 +18,7 @@ import ( "github.com/Dreamacro/clash/transport/socks5" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" + restlsC "github.com/3andne/restls-client-go" shadowsocks "github.com/metacubex/sing-shadowsocks" "github.com/metacubex/sing-shadowsocks/shadowimpl" "github.com/sagernet/sing/common/bufio" @@ -41,15 +41,16 @@ type ShadowSocks struct { type ShadowSocksOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Password string `proxy:"password"` - Cipher string `proxy:"cipher"` - UDP bool `proxy:"udp,omitempty"` - Plugin string `proxy:"plugin,omitempty"` - PluginOpts map[string]any `proxy:"plugin-opts,omitempty"` - UDPOverTCP bool `proxy:"udp-over-tcp,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Password string `proxy:"password"` + Cipher string `proxy:"cipher"` + UDP bool `proxy:"udp,omitempty"` + Plugin string `proxy:"plugin,omitempty"` + PluginOpts map[string]any `proxy:"plugin-opts,omitempty"` + UDPOverTCP bool `proxy:"udp-over-tcp,omitempty"` + ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } type simpleObfsOption struct { @@ -69,20 +70,18 @@ type v2rayObfsOption struct { } type shadowTLSOption struct { - Password string `obfs:"password"` - Host string `obfs:"host"` - Fingerprint string `obfs:"fingerprint,omitempty"` - ClientFingerprint string `obfs:"client-fingerprint,omitempty"` - SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` - Version int `obfs:"version,omitempty"` + Password string `obfs:"password"` + Host string `obfs:"host"` + Fingerprint string `obfs:"fingerprint,omitempty"` + SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + Version int `obfs:"version,omitempty"` } type restlsOption struct { - Password string `obfs:"password"` - Host string `obfs:"host"` - VersionHint string `obfs:"version-hint"` - RestlsScript string `obfs:"restls-script,omitempty"` - ClientFingerprint string `obfs:"client-fingerprint,omitempty"` + Password string `obfs:"password"` + Host string `obfs:"host"` + VersionHint string `obfs:"version-hint"` + RestlsScript string `obfs:"restls-script,omitempty"` } // StreamConn implements C.ProxyAdapter @@ -269,7 +268,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { Password: opt.Password, Host: opt.Host, Fingerprint: opt.Fingerprint, - ClientFingerprint: opt.ClientFingerprint, + ClientFingerprint: option.ClientFingerprint, SkipCertVerify: opt.SkipCertVerify, Version: opt.Version, } @@ -280,7 +279,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err) } - restlsConfig, err = restlsC.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, restlsOpt.ClientFingerprint) + restlsConfig, err = restlsC.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint) restlsConfig.SessionTicketsDisabled = true if err != nil { return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err) diff --git a/adapter/parser.go b/adapter/parser.go index d9c18694..65db74c4 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -24,6 +24,11 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { switch proxyType { case "ss": ssOption := &outbound.ShadowSocksOption{} + + if GlobalUtlsClient := tlsC.GetGlobalFingerprint(); len(GlobalUtlsClient) != 0 { + ssOption.ClientFingerprint = GlobalUtlsClient + } + err = decoder.Decode(mapping, ssOption) if err != nil { break diff --git a/docs/config.yaml b/docs/config.yaml index f7554e03..3ef09342 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -331,53 +331,54 @@ proxies: # socks5 # headers: # custom: value - - name: "ss4" + - name: "ss4-shadow-tls" type: ss server: server port: 443 cipher: chacha20-ietf-poly1305 password: "password" plugin: shadow-tls + client-fingerprint: chrome plugin-opts: host: "cloud.tencent.com" password: "shadow_tls_password" version: 2 # support 1/2/3 - - name: ss-restls-tls13 + - name: "ss-restls-tls13" type: ss server: [YOUR_SERVER_IP] port: 443 cipher: chacha20-ietf-poly1305 password: [YOUR_SS_PASSWORD] + client-fingerprint: chrome # One of: chrome, ios, firefox or safari + # 可以是chrome, ios, firefox, safari中的一个 plugin: restls plugin-opts: host: "www.microsoft.com" # Must be a TLS 1.3 server # 应当是一个TLS 1.3 服务器 password: [YOUR_RESTLS_PASSWORD] version-hint: "tls13" - client-fingerprint: chrome # One of: chrome, ios, firefox or safari - # 可以是chrome, ios, firefox, safari中的一个 - # Control your post-handshake traffic through restls-script # Hide proxy behaviors like "tls in tls". # see https://github.com/3andne/restls/blob/main/Restls-Script:%20Hide%20Your%20Proxy%20Traffic%20Behavior.md # 用restls剧本来控制握手后的行为,隐藏"tls in tls"等特征 # 详情:https://github.com/3andne/restls/blob/main/Restls-Script:%20%E9%9A%90%E8%97%8F%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%90%86%E8%A1%8C%E4%B8%BA.md restls-script: "300?100<1,400~100,350~100,600~100,300~200,300~100" - - name: ss-restls-tls12 + + - name: "ss-restls-tls12" type: ss server: [YOUR_SERVER_IP] port: 443 cipher: chacha20-ietf-poly1305 password: [YOUR_SS_PASSWORD] + client-fingerprint: chrome # One of: chrome, ios, firefox or safari + # 可以是chrome, ios, firefox, safari中的一个 plugin: restls plugin-opts: host: "vscode.dev" # Must be a TLS 1.2 server # 应当是一个TLS 1.2 服务器 password: [YOUR_RESTLS_PASSWORD] version-hint: "tls12" - client-fingerprint: chrome # One of: chrome, ios, firefox or safari - # 可以是chrome, ios, firefox, safari中的一个 restls-script: "1000?100<1,500~100,350~100,600~100,400~200" # vmess