diff --git a/docs/config.yaml b/docs/config.yaml index 89b3ce6d..63bda11b 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -109,7 +109,7 @@ sniffer: HTTP: # 需要嗅探的端口 - ports: [ 80, 8080-8880 ] + ports: [80, 8080-8880] # 可覆盖 sniffer.override-destination override-destination: true force-domain: @@ -155,7 +155,7 @@ tunnels: - tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy - tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn # full yaml config - - network: [ tcp, udp ] + - network: [tcp, udp] address: 127.0.0.1:7777 target: target.com proxy: proxy @@ -317,7 +317,7 @@ proxies: # udp: true # tls: true # 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 # servername: example.com # priority over wss host # network: ws @@ -423,6 +423,7 @@ proxies: server: server port: 443 password: yourpsk + # client-fingerprint: random # Available: "chrome","firefox","safari","random" # fingerprint: xxxx # udp: true # sni: example.com # aka server name @@ -802,7 +803,7 @@ listeners: listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - network: [ tcp, udp ] + network: [tcp, udp] target: target.com - name: tun-in-1 diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index ef52712f..235d4d38 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -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) + // fix tls handshake not timeout ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) 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{ - Host: wsOptions.Host, - Port: wsOptions.Port, - Path: wsOptions.Path, - Headers: wsOptions.Headers, - TLS: true, - TLSConfig: tlsConfig, + Host: wsOptions.Host, + Port: wsOptions.Port, + Path: wsOptions.Path, + Headers: wsOptions.Headers, + TLS: true, + TLSConfig: tlsConfig, + ClientFingerprint: t.option.ClientFingerprint, }) } diff --git a/transport/vmess/tls.go b/transport/vmess/tls.go index e60b117e..7dbde61b 100644 --- a/transport/vmess/tls.go +++ b/transport/vmess/tls.go @@ -36,13 +36,8 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) { } if len(cfg.ClientFingerprint) != 0 { - if fingerprint, exists := GetFingerprint(cfg.ClientFingerprint); exists { - utlsConn := UClient(conn, tlsConfig, &utls.ClientHelloID{ - Client: fingerprint.Client, - Version: fingerprint.Version, - Seed: nil, - }) - + utlsConn, valid := GetUtlsConnWithClientFingerprint(conn, cfg.ClientFingerprint, tlsConfig) + if valid { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) defer cancel() @@ -50,7 +45,6 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) { return utlsConn, err } } - tlsConn := tls.Client(conn, tlsConfig) 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) 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 +} diff --git a/transport/vmess/utls.go b/transport/vmess/utls.go index 4e53bed7..93a08111 100644 --- a/transport/vmess/utls.go +++ b/transport/vmess/utls.go @@ -38,6 +38,7 @@ func RollFingerprint() (*utls.ClientHelloID, bool) { chooser, _ := weightedrand.NewChooser( weightedrand.NewChoice("chrome", 6), weightedrand.NewChoice("safari", 3), + weightedrand.NewChoice("ios", 2), weightedrand.NewChoice("firefox", 1), ) initClient := chooser.Pick() @@ -50,6 +51,7 @@ var Fingerprints = map[string]*utls.ClientHelloID{ "chrome": &utls.HelloChrome_Auto, "firefox": &utls.HelloFirefox_Auto, "safari": &utls.HelloSafari_Auto, + "ios": &utls.HelloIOS_Auto, "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 // 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 { // Build the handshake state. This will apply every variable of the TLS of the // fingerprint in the UConn