chore: update utls to 1.7.0

This commit is contained in:
wwqgtxx 2025-04-21 12:07:33 +08:00
parent d5243adf89
commit 39d6a0d7ba
14 changed files with 96 additions and 81 deletions

View File

@ -19,7 +19,6 @@ import (
shadowtls "github.com/metacubex/mihomo/transport/sing-shadowtls"
v2rayObfs "github.com/metacubex/mihomo/transport/v2ray-plugin"
restlsC "github.com/3andne/restls-client-go"
shadowsocks "github.com/metacubex/sing-shadowsocks2"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
@ -37,7 +36,7 @@ type ShadowSocks struct {
v2rayOption *v2rayObfs.Option
gostOption *gost.Option
shadowTLSOption *shadowtls.ShadowTLSOption
restlsConfig *restlsC.Config
restlsConfig *restls.Config
}
type ShadowSocksOption struct {
@ -265,7 +264,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
var gostOption *gost.Option
var obfsOption *simpleObfsOption
var shadowTLSOpt *shadowtls.ShadowTLSOption
var restlsConfig *restlsC.Config
var restlsConfig *restls.Config
obfsMode := ""
decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
@ -350,7 +349,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, option.ClientFingerprint)
restlsConfig, err = restls.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint)
if err != nil {
return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err)
}

View File

@ -16,6 +16,17 @@ func Filter[T comparable](tSlice []T, filter func(t T) bool) []T {
return result
}
func Map[T any, N any](arr []T, block func(it T) N) []N {
if arr == nil { // keep nil
return nil
}
retArr := make([]N, 0, len(arr))
for index := range arr {
retArr = append(retArr, block(arr[index]))
}
return retArr
}
func ToStringSlice(value any) ([]string, error) {
strArr := make([]string, 0)
switch reflect.TypeOf(value).Kind() {

View File

@ -111,11 +111,7 @@ func convertFingerprint(fingerprint string) (*[32]byte, error) {
return (*[32]byte)(fpByte), nil
}
// GetTLSConfig specified fingerprint, customCA and customCAString
func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, customCAString string) (*tls.Config, error) {
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error) {
var certificate []byte
var err error
if len(customCA) > 0 {
@ -131,17 +127,35 @@ func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, cu
if !certPool.AppendCertsFromPEM(certificate) {
return nil, fmt.Errorf("failed to parse certificate:\n\n %s", certificate)
}
tlsConfig.RootCAs = certPool
return certPool, nil
} else {
tlsConfig.RootCAs = getCertPool()
return getCertPool(), nil
}
if len(fingerprint) > 0 {
var fingerprintBytes *[32]byte
fingerprintBytes, err = convertFingerprint(fingerprint)
}
func NewFingerprintVerifier(fingerprint string) (func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error, error) {
fingerprintBytes, err := convertFingerprint(fingerprint)
if err != nil {
return nil, err
}
return verifyFingerprint(fingerprintBytes), nil
}
// GetTLSConfig specified fingerprint, customCA and customCAString
func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, customCAString string) (_ *tls.Config, err error) {
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
tlsConfig.RootCAs, err = GetCertPool(customCA, customCAString)
if err != nil {
return nil, err
}
if len(fingerprint) > 0 {
tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(fingerprint)
if err != nil {
return nil, err
}
tlsConfig.VerifyPeerCertificate = verifyFingerprint(fingerprintBytes)
tlsConfig.InsecureSkipVerify = true
}
return tlsConfig, nil

View File

@ -4,15 +4,16 @@ import (
"crypto/tls"
"net"
"github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/log"
utls "github.com/metacubex/utls"
"github.com/mroth/weightedrand/v2"
)
type UConn struct {
*utls.UConn
}
type UConn = utls.UConn
const VersionTLS13 = utls.VersionTLS13
type UClientHelloID struct {
*utls.ClientHelloID
@ -21,13 +22,8 @@ type UClientHelloID struct {
var initRandomFingerprint UClientHelloID
var initUtlsClient string
func UClient(c net.Conn, config *tls.Config, fingerprint UClientHelloID) *UConn {
utlsConn := utls.UClient(c, copyConfig(config), utls.ClientHelloID{
Client: fingerprint.Client,
Version: fingerprint.Version,
Seed: fingerprint.Seed,
})
return &UConn{UConn: utlsConn}
func UClient(c net.Conn, config *utls.Config, fingerprint UClientHelloID) *UConn {
return utls.UClient(c, config, *fingerprint.ClientHelloID)
}
func GetFingerprint(ClientFingerprint string) (UClientHelloID, bool) {
@ -95,18 +91,43 @@ func init() {
Fingerprints["randomized"] = UClientHelloID{&randomized}
}
func copyConfig(c *tls.Config) *utls.Config {
func UCertificates(it tls.Certificate) utls.Certificate {
return utls.Certificate{
Certificate: it.Certificate,
PrivateKey: it.PrivateKey,
SupportedSignatureAlgorithms: utils.Map(it.SupportedSignatureAlgorithms, func(it tls.SignatureScheme) utls.SignatureScheme {
return utls.SignatureScheme(it)
}),
OCSPStaple: it.OCSPStaple,
SignedCertificateTimestamps: it.SignedCertificateTimestamps,
Leaf: it.Leaf,
}
}
func UConfig(config *tls.Config) *utls.Config {
return &utls.Config{
RootCAs: c.RootCAs,
ServerName: c.ServerName,
InsecureSkipVerify: c.InsecureSkipVerify,
VerifyPeerCertificate: c.VerifyPeerCertificate,
Rand: config.Rand,
Time: config.Time,
Certificates: utils.Map(config.Certificates, UCertificates),
VerifyPeerCertificate: config.VerifyPeerCertificate,
RootCAs: config.RootCAs,
NextProtos: config.NextProtos,
ServerName: config.ServerName,
InsecureSkipVerify: config.InsecureSkipVerify,
CipherSuites: config.CipherSuites,
MinVersion: config.MinVersion,
MaxVersion: config.MaxVersion,
CurvePreferences: utils.Map(config.CurvePreferences, func(it tls.CurveID) utls.CurveID {
return utls.CurveID(it)
}),
SessionTicketsDisabled: config.SessionTicketsDisabled,
Renegotiation: utls.RenegotiationSupport(config.Renegotiation),
}
}
// BuildWebsocketHandshakeState 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) BuildWebsocketHandshakeState() error {
func BuildWebsocketHandshakeState(c *UConn) error {
// Build the handshake state. This will apply every variable of the TLS of the
// fingerprint in the UConn
if err := c.BuildHandshakeState(); err != nil {

2
go.mod
View File

@ -32,7 +32,7 @@ require (
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422
github.com/metacubex/utls v1.6.8-alpha.6
github.com/metacubex/utls v1.7.0-alpha.1
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181
github.com/miekg/dns v1.1.63
github.com/mroth/weightedrand/v2 v2.1.0

4
go.sum
View File

@ -129,8 +129,8 @@ github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/utls v1.6.8-alpha.6 h1:5ZdZNiZFkKKgEcuPOOROIc8bA4dX2VJHoY3gajSnSaU=
github.com/metacubex/utls v1.6.8-alpha.6/go.mod h1:MEZ5WO/VLKYs/s/dOzEK/mlXOQxc04ESeLzRgjmLYtk=
github.com/metacubex/utls v1.7.0-alpha.1 h1:oMFsPh2oTlALJ7vKXPJuqgy0YeiZ+q/LLw+ZdxZ80l4=
github.com/metacubex/utls v1.7.0-alpha.1/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=

View File

@ -22,14 +22,13 @@ import (
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
"github.com/metacubex/sing-vmess/vless"
utls "github.com/metacubex/utls"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/metadata"
)
func init() {
vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) {
tlsConn, loaded := common.Cast[*reality.Conn](conn)
tlsConn, loaded := common.Cast[*reality.Conn](conn) // *utls.Conn
if !loaded {
return
}
@ -37,15 +36,7 @@ func init() {
})
vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) {
tlsConn, loaded := common.Cast[*utls.UConn](conn)
if !loaded {
return
}
return true, tlsConn.NetConn(), reflect.TypeOf(tlsConn.Conn).Elem(), unsafe.Pointer(tlsConn.Conn)
})
vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) {
tlsConn, loaded := common.Cast[*tlsC.UConn](conn)
tlsConn, loaded := common.Cast[*tlsC.UConn](conn) // *utls.UConn
if !loaded {
return
}

View File

@ -244,7 +244,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri
if len(clientFingerprint) != 0 {
if realityConfig == nil {
if fingerprint, exists := tlsC.GetFingerprint(clientFingerprint); exists {
utlsConn := tlsC.UClient(pconn, cfg, fingerprint)
utlsConn := tlsC.UClient(pconn, tlsC.UConfig(cfg), fingerprint)
if err := utlsConn.HandshakeContext(ctx); err != nil {
pconn.Close()
return nil, err

View File

@ -19,8 +19,12 @@ func (r *Restls) Upstream() any {
return r.UConn.NetConn()
}
type Config = tls.Config
var NewRestlsConfig = tls.NewRestlsConfig
// NewRestls return a Restls Connection
func NewRestls(ctx context.Context, conn net.Conn, config *tls.Config) (net.Conn, error) {
func NewRestls(ctx context.Context, conn net.Conn, config *Config) (net.Conn, error) {
clientHellowID := tls.HelloChrome_Auto
if config != nil {
clientIDPtr := config.ClientID.Load()

View File

@ -11,7 +11,6 @@ import (
"github.com/metacubex/sing-shadowtls"
utls "github.com/metacubex/utls"
sing_common "github.com/sagernet/sing/common"
)
const (
@ -60,32 +59,15 @@ func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (
func uTLSHandshakeFunc(config *tls.Config, clientFingerprint string) shadowtls.TLSHandshakeFunc {
return func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
tlsConfig := &utls.Config{
Rand: config.Rand,
Time: config.Time,
VerifyPeerCertificate: config.VerifyPeerCertificate,
RootCAs: config.RootCAs,
NextProtos: config.NextProtos,
ServerName: config.ServerName,
InsecureSkipVerify: config.InsecureSkipVerify,
CipherSuites: config.CipherSuites,
MinVersion: config.MinVersion,
MaxVersion: config.MaxVersion,
CurvePreferences: sing_common.Map(config.CurvePreferences, func(it tls.CurveID) utls.CurveID {
return utls.CurveID(it)
}),
SessionTicketsDisabled: config.SessionTicketsDisabled,
Renegotiation: utls.RenegotiationSupport(config.Renegotiation),
SessionIDGenerator: sessionIDGenerator,
}
tlsConfig := tlsC.UConfig(config)
tlsConfig.SessionIDGenerator = sessionIDGenerator
clientFingerprint := clientFingerprint
if tlsC.HaveGlobalFingerprint() && len(clientFingerprint) == 0 {
clientFingerprint = tlsC.GetGlobalFingerprint()
}
if len(clientFingerprint) != 0 {
if fingerprint, exists := tlsC.GetFingerprint(clientFingerprint); exists {
clientHelloID := *fingerprint.ClientHelloID
tlsConn := utls.UClient(conn, tlsConfig, clientHelloID)
tlsConn := tlsC.UClient(conn, tlsConfig, fingerprint)
return tlsConn.HandshakeContext(ctx)
}
}

View File

@ -12,10 +12,10 @@ import (
"github.com/metacubex/mihomo/common/buf"
N "github.com/metacubex/mihomo/common/net"
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/metacubex/mihomo/log"
"github.com/gofrs/uuid/v5"
utls "github.com/metacubex/utls"
)
var (
@ -187,8 +187,8 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) {
buffer.Release()
return ErrNotTLS13
}
case *utls.UConn:
if underlying.ConnectionState().Version != utls.VersionTLS13 {
case *tlsC.UConn:
if underlying.ConnectionState().Version != tlsC.VersionTLS13 {
buffer.Release()
return ErrNotTLS13
}

View File

@ -14,7 +14,6 @@ import (
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/gofrs/uuid/v5"
utls "github.com/metacubex/utls"
"github.com/sagernet/sing/common"
)
@ -46,16 +45,10 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) {
c.tlsConn = underlying
t = reflect.TypeOf(underlying).Elem()
p = unsafe.Pointer(underlying)
case *utls.UConn:
//log.Debugln("type *utls.UConn")
c.Conn = underlying.NetConn()
c.tlsConn = underlying
t = reflect.TypeOf(underlying.Conn).Elem()
p = unsafe.Pointer(underlying.Conn)
case *tlsC.UConn:
//log.Debugln("type *tlsC.UConn")
c.Conn = underlying.NetConn()
c.tlsConn = underlying.UConn
c.tlsConn = underlying
t = reflect.TypeOf(underlying.Conn).Elem()
//log.Debugln("t:%v", t)
p = unsafe.Pointer(underlying.Conn)

View File

@ -39,7 +39,7 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn
if len(clientFingerprint) != 0 {
if cfg.Reality == nil {
if fingerprint, exists := tlsC.GetFingerprint(clientFingerprint); exists {
utlsConn := tlsC.UClient(conn, tlsConfig, fingerprint)
utlsConn := tlsC.UClient(conn, tlsC.UConfig(tlsConfig), fingerprint)
err = utlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err

View File

@ -360,8 +360,8 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig,
}
if len(clientFingerprint) != 0 {
if fingerprint, exists := tlsC.GetFingerprint(clientFingerprint); exists {
utlsConn := tlsC.UClient(conn, config, fingerprint)
if err = utlsConn.BuildWebsocketHandshakeState(); err != nil {
utlsConn := tlsC.UClient(conn, tlsC.UConfig(config), fingerprint)
if err = tlsC.BuildWebsocketHandshakeState(utlsConn); err != nil {
return nil, fmt.Errorf("parse url %s error: %w", c.Path, err)
}
conn = utlsConn