feat: add uTLS support (#94)

* feat: add uTLS

* feat: change `Utls.Imitate` to `UtlsImitate`

* feat: add desc and example config

* chore: refine code and docs

* docs(example.dae): adjust order

---------

Co-authored-by: mzz2017 <2017@duck.com>
This commit is contained in:
秋のかえで
2023-05-27 10:52:13 +08:00
committed by GitHub
parent eac04e991b
commit 80d3940483
11 changed files with 193 additions and 48 deletions

View File

@ -39,6 +39,8 @@ type GlobalOption struct {
CheckTolerance time.Duration
CheckDnsTcp bool
AllowInsecure bool
TlsImplementation string
UtlsImitate string
}
type InstanceOption struct {

View File

@ -48,11 +48,12 @@ func NewTrojan(option *dialer.GlobalOption, iOption dialer.InstanceOption, link
func (s *Trojan) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOption) (*dialer.Dialer, error) {
d := direct.FullconeDirect // Trojan Proxy supports full-cone.
u := url.URL{
Scheme: "tls",
Scheme: option.TlsImplementation,
Host: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)),
RawQuery: url.Values{
"sni": []string{s.Sni},
"allowInsecure": []string{common.BoolToString(s.AllowInsecure)},
"utlsImitate": []string{option.UtlsImitate},
}.Encode(),
}
var err error

View File

@ -110,11 +110,12 @@ func (s *V2Ray) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOptio
sni = s.Host
}
u := url.URL{
Scheme: "tls",
Scheme: option.TlsImplementation,
Host: net.JoinHostPort(s.Add, s.Port),
RawQuery: url.Values{
"sni": []string{sni},
"allowInsecure": []string{common.BoolToString(s.AllowInsecure)},
"utlsImitate": []string{option.UtlsImitate},
}.Encode(),
}
d, err = tls.NewTls(u.String(), d)

View File

@ -3,17 +3,22 @@ package tls
import (
"crypto/tls"
"fmt"
"github.com/mzz2017/softwind/netproxy"
"net/url"
"github.com/mzz2017/softwind/netproxy"
utls "github.com/refraction-networking/utls"
)
// Tls is a base Tls struct
type Tls struct {
dialer netproxy.Dialer
addr string
serverName string
skipVerify bool
tlsConfig *tls.Config
dialer netproxy.Dialer
addr string
serverName string
skipVerify bool
tlsImplentation string
utlsImitate string
tlsConfig *tls.Config
}
// NewTls returns a Tls infra.
@ -24,12 +29,14 @@ func NewTls(s string, d netproxy.Dialer) (*Tls, error) {
}
t := &Tls{
dialer: d,
addr: u.Host,
dialer: d,
addr: u.Host,
tlsImplentation: u.Scheme,
}
query := u.Query()
t.serverName = query.Get("sni")
t.utlsImitate = query.Get("utlsImitate")
// skipVerify
if query.Get("allowInsecure") == "true" || query.Get("allowInsecure") == "1" ||
@ -72,11 +79,35 @@ func (s *Tls) DialTcp(addr string) (conn netproxy.Conn, err error) {
return nil, fmt.Errorf("[Tls]: dial to %s: %w", s.addr, err)
}
tlsConn := tls.Client(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, s.tlsConfig)
var tlsConn interface {
netproxy.Conn
Handshake() error
}
switch s.tlsImplentation {
case "tls":
tlsConn = tls.Client(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, s.tlsConfig)
case "utls":
clientHelloID, err := nameToUtlsClientHelloID(s.utlsImitate)
if err != nil {
return nil, err
}
tlsConn = utls.UClient(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, uTLSConfigFromTLSConfig(s.tlsConfig), *clientHelloID)
default:
return nil, fmt.Errorf("unknown tls implementation: %v", s.tlsImplentation)
}
if err := tlsConn.Handshake(); err != nil {
return nil, err
}

View File

@ -0,0 +1,63 @@
package tls
import (
"crypto/tls"
"fmt"
utls "github.com/refraction-networking/utls"
)
func uTLSConfigFromTLSConfig(config *tls.Config) *utls.Config {
return &utls.Config{
ServerName: config.ServerName,
InsecureSkipVerify: config.InsecureSkipVerify,
}
}
var clientHelloIDMap = map[string]*utls.ClientHelloID{
"randomized": &utls.HelloRandomized,
"randomizedalpn": &utls.HelloRandomizedALPN,
"randomizednoalpn": &utls.HelloRandomizedNoALPN,
"firefox_auto": &utls.HelloFirefox_Auto,
"firefox_55": &utls.HelloFirefox_55,
"firefox_56": &utls.HelloFirefox_56,
"firefox_63": &utls.HelloFirefox_63,
"firefox_65": &utls.HelloFirefox_65,
"firefox_99": &utls.HelloFirefox_99,
"firefox_102": &utls.HelloFirefox_102,
"firefox_105": &utls.HelloFirefox_105,
"chrome_auto": &utls.HelloChrome_Auto,
"chrome_58": &utls.HelloChrome_58,
"chrome_62": &utls.HelloChrome_62,
"chrome_70": &utls.HelloChrome_70,
"chrome_72": &utls.HelloChrome_72,
"chrome_83": &utls.HelloChrome_83,
"chrome_87": &utls.HelloChrome_87,
"chrome_96": &utls.HelloChrome_96,
"chrome_100": &utls.HelloChrome_100,
"chrome_102": &utls.HelloChrome_102,
"ios_auto": &utls.HelloIOS_Auto,
"ios_11_1": &utls.HelloIOS_11_1,
"ios_12_1": &utls.HelloIOS_12_1,
"ios_13": &utls.HelloIOS_13,
"ios_14": &utls.HelloIOS_14,
"android_11_okhttp": &utls.HelloAndroid_11_OkHttp,
"edge_auto": &utls.HelloEdge_Auto,
"edge_85": &utls.HelloEdge_85,
"edge_106": &utls.HelloEdge_106,
"safari_auto": &utls.HelloSafari_Auto,
"safari_16_0": &utls.HelloSafari_16_0,
"360_auto": &utls.Hello360_Auto,
"360_7_5": &utls.Hello360_7_5,
"360_11_0": &utls.Hello360_11_0,
"qq_auto": &utls.HelloQQ_Auto,
"qq_11_1": &utls.HelloQQ_11_1,
}
func nameToUtlsClientHelloID(name string) (*utls.ClientHelloID, error) {
clientHelloID, ok := clientHelloIDMap[name]
if !ok {
return nil, fmt.Errorf("unknown uTLS Client Hello ID: %s", name)
}
return clientHelloID, nil
}