feat: impl restls

This commit is contained in:
3andne 2023-03-08 20:10:27 -08:00
parent 9cc7fdaca9
commit e2d590ca12
5 changed files with 132 additions and 1 deletions

View File

@ -8,10 +8,12 @@ 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"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/restls"
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
shadowtls "github.com/Dreamacro/clash/transport/sing-shadowtls"
"github.com/Dreamacro/clash/transport/socks5"
@ -34,6 +36,7 @@ type ShadowSocks struct {
obfsOption *simpleObfsOption
v2rayOption *v2rayObfs.Option
shadowTLSOption *shadowtls.ShadowTLSOption
restlsConfig *restlsC.Config
}
type ShadowSocksOption struct {
@ -74,6 +77,14 @@ type shadowTLSOption struct {
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"`
ClientID string `obfs:"client-id,omitempty"`
}
// StreamConn implements C.ProxyAdapter
func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
switch ss.obfsMode {
@ -86,6 +97,13 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e
if err != nil {
return nil, err
}
case restls.Mode:
var err error
c, err = restls.NewRestls(c, ss.restlsConfig)
if err != nil {
return nil, fmt.Errorf("%s (restls) connect error: %w", ss.addr, err)
}
}
return ss.streamConn(c, metadata)
}
@ -143,7 +161,7 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale
}
}
c, err = ss.streamConn(c, metadata)
c, err = ss.StreamConn(c, metadata)
return NewConn(c, ss), err
}
@ -202,6 +220,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
var v2rayOption *v2rayObfs.Option
var obfsOption *simpleObfsOption
var shadowTLSOpt *shadowtls.ShadowTLSOption
var restlsConfig *restlsC.Config
obfsMode := ""
decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
@ -254,6 +273,19 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
SkipCertVerify: opt.SkipCertVerify,
Version: opt.Version,
}
} else if option.Plugin == restls.Mode {
obfsMode = restls.Mode
restlsOpt := &restlsOption{}
if err := decoder.Decode(option.PluginOpts, restlsOpt); err != nil {
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.ClientID)
restlsConfig.SessionTicketsDisabled = true
if err != nil {
return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err)
}
}
return &ShadowSocks{
@ -274,6 +306,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
v2rayOption: v2rayOption,
obfsOption: obfsOption,
shadowTLSOption: shadowTLSOpt,
restlsConfig: restlsConfig,
}, nil
}

View File

@ -339,6 +339,43 @@ proxies: # socks5
host: "cloud.tencent.com"
password: "shadow_tls_password"
version: 2 # support 1/2/3
- name: ss-restls-tls13
type: ss
server: [YOUR_SERVER_IP]
port: 443
cipher: chacha20-ietf-poly1305
password: [YOUR_SS_PASSWORD]
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-id: 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
type: ss
server: [YOUR_SERVER_IP]
port: 443
cipher: chacha20-ietf-poly1305
password: [YOUR_SS_PASSWORD]
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-id: 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
# cipher支持 auto/aes-128-gcm/chacha20-poly1305/none

2
go.mod
View File

@ -51,6 +51,7 @@ require (
)
require (
github.com/3andne/restls-client-go v0.1.1
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@ -82,3 +83,4 @@ require (
)
replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370
replace github.com/3andne/restls-client-go v0.1.1 => ../go

2
go.sum
View File

@ -1,3 +1,5 @@
github.com/3andne/restls-client-go v0.1.1 h1:6BbtlaCct9yd2eEjeoELz/I8inXmYgBmLZ1zliXtvUo=
github.com/3andne/restls-client-go v0.1.1/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=

View File

@ -0,0 +1,57 @@
package restls
import (
"net"
tls "github.com/3andne/restls-client-go"
)
const (
Mode string = "restls"
)
// Restls
type Restls struct {
net.Conn
firstPacketCache []byte
firstPacket bool
}
func (r *Restls) Read(b []byte) (int, error) {
if err := r.Conn.(*tls.UConn).Handshake(); err != nil {
return 0, err
}
n, err := r.Conn.(*tls.UConn).Read(b)
return n, err
}
func (r *Restls) Write(b []byte) (int, error) {
if r.firstPacket {
r.firstPacketCache = append([]byte(nil), b...)
r.firstPacket = false
return len(b), nil
}
if len(r.firstPacketCache) != 0 {
b = append(r.firstPacketCache, b...)
r.firstPacketCache = nil
}
n, err := r.Conn.(*tls.UConn).Write(b)
return n, err
}
// NewRestls return a Restls Connection
func NewRestls(conn net.Conn, config *tls.Config) (net.Conn, error) {
if config != nil {
clientIDPtr := config.ClientID.Load()
if clientIDPtr != nil {
return &Restls{
Conn: tls.UClient(conn, config, *clientIDPtr),
firstPacket: true,
}, nil
}
}
return &Restls{
Conn: tls.UClient(conn, config, tls.HelloChrome_Auto),
firstPacket: true,
}, nil
}