chore: better global-client-fingerprint handle

This commit is contained in:
wwqgtxx 2025-04-19 02:04:09 +08:00
parent 6236cb1cf0
commit d5243adf89
10 changed files with 47 additions and 67 deletions

View file

@ -11,7 +11,6 @@ import (
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/anytls"
"github.com/metacubex/mihomo/transport/vmess"
@ -115,9 +114,6 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) {
if tlsConfig.Host == "" {
tlsConfig.Host = option.Server
}
if tlsC.HaveGlobalFingerprint() && len(option.ClientFingerprint) == 0 {
tlsConfig.ClientFingerprint = tlsC.GetGlobalFingerprint()
}
tOption.TLSConfig = tlsConfig
outbound := &AnyTLS{

View file

@ -63,13 +63,7 @@ type TrojanSSOption struct {
}
// StreamConnContext implements C.ProxyAdapter
func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
var err error
if tlsC.HaveGlobalFingerprint() && len(t.option.ClientFingerprint) == 0 {
t.option.ClientFingerprint = tlsC.GetGlobalFingerprint()
}
func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {
switch t.option.Network {
case "ws":
host, port, _ := net.SplitHostPort(t.addr)

View file

@ -75,13 +75,7 @@ type VlessOption struct {
ClientFingerprint string `proxy:"client-fingerprint,omitempty"`
}
func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
var err error
if tlsC.HaveGlobalFingerprint() && len(v.option.ClientFingerprint) == 0 {
v.option.ClientFingerprint = tlsC.GetGlobalFingerprint()
}
func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {
switch v.option.Network {
case "ws":
host, port, _ := net.SplitHostPort(v.addr)

View file

@ -96,13 +96,7 @@ type WSOptions struct {
}
// StreamConnContext implements C.ProxyAdapter
func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
var err error
if tlsC.HaveGlobalFingerprint() && (len(v.option.ClientFingerprint) == 0) {
v.option.ClientFingerprint = tlsC.GetGlobalFingerprint()
}
func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {
switch v.option.Network {
case "ws":
host, port, _ := net.SplitHostPort(v.addr)

View file

@ -5,7 +5,6 @@ import (
"github.com/metacubex/mihomo/adapter/outbound"
"github.com/metacubex/mihomo/common/structure"
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
)
@ -22,7 +21,7 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
)
switch proxyType {
case "ss":
ssOption := &outbound.ShadowSocksOption{ClientFingerprint: tlsC.GetGlobalFingerprint()}
ssOption := &outbound.ShadowSocksOption{}
err = decoder.Decode(mapping, ssOption)
if err != nil {
break
@ -55,7 +54,6 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
Method: "GET",
Path: []string{"/"},
},
ClientFingerprint: tlsC.GetGlobalFingerprint(),
}
err = decoder.Decode(mapping, vmessOption)
@ -64,7 +62,7 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
}
proxy, err = outbound.NewVmess(*vmessOption)
case "vless":
vlessOption := &outbound.VlessOption{ClientFingerprint: tlsC.GetGlobalFingerprint()}
vlessOption := &outbound.VlessOption{}
err = decoder.Decode(mapping, vlessOption)
if err != nil {
break
@ -78,7 +76,7 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
}
proxy, err = outbound.NewSnell(*snellOption)
case "trojan":
trojanOption := &outbound.TrojanOption{ClientFingerprint: tlsC.GetGlobalFingerprint()}
trojanOption := &outbound.TrojanOption{}
err = decoder.Decode(mapping, trojanOption)
if err != nil {
break

View file

@ -37,9 +37,9 @@ type RealityConfig struct {
ShortID [RealityMaxShortIDLen]byte
}
func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) {
func GetRealityConn(ctx context.Context, conn net.Conn, clientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) {
retry := 0
for fingerprint, exists := GetFingerprint(ClientFingerprint); exists; retry++ {
for fingerprint, exists := GetFingerprint(clientFingerprint); exists; retry++ {
verifier := &realityVerifier{
serverName: tlsConfig.ServerName,
}

View file

@ -224,7 +224,7 @@ func (g *Conn) SetDeadline(t time.Time) error {
return nil
}
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, realityConfig *tlsC.RealityConfig) *TransportWrap {
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint string, realityConfig *tlsC.RealityConfig) *TransportWrap {
dialFunc := func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)
defer cancel()
@ -237,9 +237,13 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, re
return pconn, nil
}
if len(Fingerprint) != 0 {
clientFingerprint := clientFingerprint
if tlsC.HaveGlobalFingerprint() && len(clientFingerprint) == 0 {
clientFingerprint = tlsC.GetGlobalFingerprint()
}
if len(clientFingerprint) != 0 {
if realityConfig == nil {
if fingerprint, exists := tlsC.GetFingerprint(Fingerprint); exists {
if fingerprint, exists := tlsC.GetFingerprint(clientFingerprint); exists {
utlsConn := tlsC.UClient(pconn, cfg, fingerprint)
if err := utlsConn.HandshakeContext(ctx); err != nil {
pconn.Close()
@ -253,7 +257,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, re
return utlsConn, nil
}
} else {
realityConn, err := tlsC.GetRealityConn(ctx, pconn, Fingerprint, cfg, realityConfig)
realityConn, err := tlsC.GetRealityConn(ctx, pconn, clientFingerprint, cfg, realityConfig)
if err != nil {
pconn.Close()
return nil, err

View file

@ -45,13 +45,7 @@ func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (
return nil, err
}
var clientHelloID utls.ClientHelloID
if len(option.ClientFingerprint) != 0 {
if fingerprint, exists := tlsC.GetFingerprint(option.ClientFingerprint); exists {
clientHelloID = *fingerprint.ClientHelloID
}
}
tlsHandshake := uTLSHandshakeFunc(tlsConfig, clientHelloID)
tlsHandshake := uTLSHandshakeFunc(tlsConfig, option.ClientFingerprint)
client, err := shadowtls.NewClient(shadowtls.ClientConfig{
Version: option.Version,
Password: option.Password,
@ -64,7 +58,7 @@ func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (
return client.DialContextConn(ctx, conn)
}
func uTLSHandshakeFunc(config *tls.Config, clientHelloID utls.ClientHelloID) shadowtls.TLSHandshakeFunc {
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,
@ -84,12 +78,18 @@ func uTLSHandshakeFunc(config *tls.Config, clientHelloID utls.ClientHelloID) sha
Renegotiation: utls.RenegotiationSupport(config.Renegotiation),
SessionIDGenerator: sessionIDGenerator,
}
var empty utls.ClientHelloID
if clientHelloID == empty {
tlsConn := utls.Client(conn, tlsConfig)
return tlsConn.Handshake()
clientFingerprint := clientFingerprint
if tlsC.HaveGlobalFingerprint() && len(clientFingerprint) == 0 {
clientFingerprint = tlsC.GetGlobalFingerprint()
}
tlsConn := utls.UClient(conn, tlsConfig, clientHelloID)
if len(clientFingerprint) != 0 {
if fingerprint, exists := tlsC.GetFingerprint(clientFingerprint); exists {
clientHelloID := *fingerprint.ClientHelloID
tlsConn := utls.UClient(conn, tlsConfig, clientHelloID)
return tlsConn.HandshakeContext(ctx)
}
}
tlsConn := utls.Client(conn, tlsConfig)
return tlsConn.HandshakeContext(ctx)
}
}

View file

@ -32,15 +32,22 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn
return nil, err
}
if len(cfg.ClientFingerprint) != 0 {
clientFingerprint := cfg.ClientFingerprint
if tlsC.HaveGlobalFingerprint() && len(clientFingerprint) == 0 {
clientFingerprint = tlsC.GetGlobalFingerprint()
}
if len(clientFingerprint) != 0 {
if cfg.Reality == nil {
utlsConn, valid := GetUTLSConn(conn, cfg.ClientFingerprint, tlsConfig)
if valid {
if fingerprint, exists := tlsC.GetFingerprint(clientFingerprint); exists {
utlsConn := tlsC.UClient(conn, tlsConfig, fingerprint)
err = utlsConn.HandshakeContext(ctx)
return utlsConn, err
if err != nil {
return nil, err
}
return utlsConn, nil
}
} else {
return tlsC.GetRealityConn(ctx, conn, cfg.ClientFingerprint, tlsConfig, cfg.Reality)
return tlsC.GetRealityConn(ctx, conn, clientFingerprint, tlsConfig, cfg.Reality)
}
}
if cfg.Reality != nil {
@ -52,14 +59,3 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn
err = tlsConn.HandshakeContext(ctx)
return tlsConn, err
}
func GetUTLSConn(conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config) (*tlsC.UConn, bool) {
if fingerprint, exists := tlsC.GetFingerprint(ClientFingerprint); exists {
utlsConn := tlsC.UClient(conn, tlsConfig, fingerprint)
return utlsConn, true
}
return nil, false
}

View file

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