feat: Update utls support.

* client-fingerprint is used to apply Utls for modifying ClientHello, it accepts "chrome","firefox","safari","ios","random" options.
* Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan.
This commit is contained in:
gVisor bot 2023-02-05 17:31:58 +08:00
parent d9d2ea41c0
commit 4d33fb9848
4 changed files with 47 additions and 22 deletions

View file

@ -109,7 +109,7 @@ sniffer:
HTTP: HTTP:
# 需要嗅探的端口 # 需要嗅探的端口
ports: [ 80, 8080-8880 ] ports: [80, 8080-8880]
# 可覆盖 sniffer.override-destination # 可覆盖 sniffer.override-destination
override-destination: true override-destination: true
force-domain: force-domain:
@ -155,7 +155,7 @@ tunnels:
- tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy - tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy
- tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn - tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn
# full yaml config # full yaml config
- network: [ tcp, udp ] - network: [tcp, udp]
address: 127.0.0.1:7777 address: 127.0.0.1:7777
target: target.com target: target.com
proxy: proxy proxy: proxy
@ -317,7 +317,7 @@ proxies:
# udp: true # udp: true
# tls: true # tls: true
# fingerprint: xxxx # fingerprint: xxxx
# client-fingerprint: random # Available: "chrome","firefox","safari","random" # client-fingerprint: chrome # Available: "chrome","firefox","safari","ios","random", currently only support TLS transport in TCP/GRPC/WS/HTTP for VLESS/Vmess and trojan.
# skip-cert-verify: true # skip-cert-verify: true
# servername: example.com # priority over wss host # servername: example.com # priority over wss host
# network: ws # network: ws
@ -423,6 +423,7 @@ proxies:
server: server server: server
port: 443 port: 443
password: yourpsk password: yourpsk
# client-fingerprint: random # Available: "chrome","firefox","safari","random"
# fingerprint: xxxx # fingerprint: xxxx
# udp: true # udp: true
# sni: example.com # aka server name # sni: example.com # aka server name
@ -802,7 +803,7 @@ listeners:
listen: 0.0.0.0 listen: 0.0.0.0
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules # rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错) # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
network: [ tcp, udp ] network: [tcp, udp]
target: target.com target: target.com
- name: tun-in-1 - name: tun-in-1

View file

@ -116,15 +116,26 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
} }
} }
if len(t.option.ClientFingerprint) != 0 {
utlsConn, valid := vmess.GetUtlsConnWithClientFingerprint(conn, t.option.ClientFingerprint, tlsConfig)
if valid {
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
err := utlsConn.(*vmess.UConn).HandshakeContext(ctx)
return utlsConn, err
}
}
tlsConn := tls.Client(conn, tlsConfig) tlsConn := tls.Client(conn, tlsConfig)
// fix tls handshake not timeout // fix tls handshake not timeout
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel() defer cancel()
if err := tlsConn.HandshakeContext(ctx); err != nil {
return nil, err
}
return tlsConn, nil err := tlsConn.HandshakeContext(ctx)
return tlsConn, err
} }
} }
@ -142,12 +153,13 @@ func (t *Trojan) StreamWebsocketConn(conn net.Conn, wsOptions *WebsocketOption)
} }
return vmess.StreamWebsocketConn(conn, &vmess.WebsocketConfig{ return vmess.StreamWebsocketConn(conn, &vmess.WebsocketConfig{
Host: wsOptions.Host, Host: wsOptions.Host,
Port: wsOptions.Port, Port: wsOptions.Port,
Path: wsOptions.Path, Path: wsOptions.Path,
Headers: wsOptions.Headers, Headers: wsOptions.Headers,
TLS: true, TLS: true,
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
ClientFingerprint: t.option.ClientFingerprint,
}) })
} }

View file

@ -36,13 +36,8 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
} }
if len(cfg.ClientFingerprint) != 0 { if len(cfg.ClientFingerprint) != 0 {
if fingerprint, exists := GetFingerprint(cfg.ClientFingerprint); exists { utlsConn, valid := GetUtlsConnWithClientFingerprint(conn, cfg.ClientFingerprint, tlsConfig)
utlsConn := UClient(conn, tlsConfig, &utls.ClientHelloID{ if valid {
Client: fingerprint.Client,
Version: fingerprint.Version,
Seed: nil,
})
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel() defer cancel()
@ -50,7 +45,6 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
return utlsConn, err return utlsConn, err
} }
} }
tlsConn := tls.Client(conn, tlsConfig) tlsConn := tls.Client(conn, tlsConfig)
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
@ -59,3 +53,18 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
err := tlsConn.HandshakeContext(ctx) err := tlsConn.HandshakeContext(ctx)
return tlsConn, err return tlsConn, err
} }
func GetUtlsConnWithClientFingerprint(conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config) (net.Conn, bool) {
if fingerprint, exists := GetFingerprint(ClientFingerprint); exists {
utlsConn := UClient(conn, tlsConfig, &utls.ClientHelloID{
Client: fingerprint.Client,
Version: fingerprint.Version,
Seed: nil,
})
return utlsConn, true
}
return nil, false
}

View file

@ -38,6 +38,7 @@ func RollFingerprint() (*utls.ClientHelloID, bool) {
chooser, _ := weightedrand.NewChooser( chooser, _ := weightedrand.NewChooser(
weightedrand.NewChoice("chrome", 6), weightedrand.NewChoice("chrome", 6),
weightedrand.NewChoice("safari", 3), weightedrand.NewChoice("safari", 3),
weightedrand.NewChoice("ios", 2),
weightedrand.NewChoice("firefox", 1), weightedrand.NewChoice("firefox", 1),
) )
initClient := chooser.Pick() initClient := chooser.Pick()
@ -50,6 +51,7 @@ var Fingerprints = map[string]*utls.ClientHelloID{
"chrome": &utls.HelloChrome_Auto, "chrome": &utls.HelloChrome_Auto,
"firefox": &utls.HelloFirefox_Auto, "firefox": &utls.HelloFirefox_Auto,
"safari": &utls.HelloSafari_Auto, "safari": &utls.HelloSafari_Auto,
"ios": &utls.HelloIOS_Auto,
"randomized": &utls.HelloRandomized, "randomized": &utls.HelloRandomized,
} }
@ -64,6 +66,7 @@ func CopyConfig(c *tls.Config) *utls.Config {
// WebsocketHandshake basically calls UConn.Handshake inside it but it will only send // WebsocketHandshake basically calls UConn.Handshake inside it but it will only send
// http/1.1 in its ALPN. // http/1.1 in its ALPN.
// Copy from https://github.com/XTLS/Xray-core/blob/main/transport/internet/tls/tls.go
func (c *UConn) WebsocketHandshake() error { func (c *UConn) WebsocketHandshake() error {
// Build the handshake state. This will apply every variable of the TLS of the // Build the handshake state. This will apply every variable of the TLS of the
// fingerprint in the UConn // fingerprint in the UConn