From 952f286e5fc70a95a48a25329c1426038cc18357 Mon Sep 17 00:00:00 2001 From: anytls Date: Sun, 16 Feb 2025 22:30:09 +0900 Subject: [PATCH] feat: implement clientFingerprint for anytls client --- adapter/outbound/anytls.go | 6 +++++ transport/anytls/client.go | 55 +++++++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/adapter/outbound/anytls.go b/adapter/outbound/anytls.go index 1856e805..a6be0e84 100644 --- a/adapter/outbound/anytls.go +++ b/adapter/outbound/anytls.go @@ -13,6 +13,7 @@ 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/sagernet/sing/common" @@ -101,6 +102,7 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) { Dialer: singDialer, IdleSessionCheckInterval: time.Duration(option.IdleSessionCheckInterval) * time.Second, IdleSessionTimeout: time.Duration(option.IdleSessionTimeout) * time.Second, + ClientFingerprint: option.ClientFingerprint, } tlsConfig := &tls.Config{ ServerName: option.SNI, @@ -112,6 +114,10 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) { } tOption.TLSConfig = tlsConfig + if tlsC.HaveGlobalFingerprint() && len(tOption.ClientFingerprint) == 0 { + tOption.ClientFingerprint = tlsC.GetGlobalFingerprint() + } + outbound := &AnyTLS{ Base: &Base{ name: option.Name, diff --git a/transport/anytls/client.go b/transport/anytls/client.go index e7bf3707..8fbb314e 100644 --- a/transport/anytls/client.go +++ b/transport/anytls/client.go @@ -8,8 +8,11 @@ import ( "net" "time" + tlsC "github.com/metacubex/mihomo/component/tls" + C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/anytls/padding" "github.com/metacubex/mihomo/transport/anytls/session" + "github.com/metacubex/mihomo/transport/vmess" "github.com/sagernet/sing/common/atomic" "github.com/sagernet/sing/common/buf" M "github.com/sagernet/sing/common/metadata" @@ -23,24 +26,27 @@ type ClientConfig struct { Server M.Socksaddr Dialer N.Dialer TLSConfig *tls.Config + ClientFingerprint string } type Client struct { - passwordSha256 []byte - tlsConfig *tls.Config - dialer N.Dialer - server M.Socksaddr - sessionClient *session.Client - padding atomic.TypedValue[*padding.PaddingFactory] + passwordSha256 []byte + tlsConfig *tls.Config + clientFingerprint string + dialer N.Dialer + server M.Socksaddr + sessionClient *session.Client + padding atomic.TypedValue[*padding.PaddingFactory] } func NewClient(ctx context.Context, config ClientConfig) *Client { pw := sha256.Sum256([]byte(config.Password)) c := &Client{ - passwordSha256: pw[:], - tlsConfig: config.TLSConfig, - dialer: config.Dialer, - server: config.Server, + passwordSha256: pw[:], + tlsConfig: config.TLSConfig, + clientFingerprint: config.ClientFingerprint, + dialer: config.Dialer, + server: config.Server, } // Initialize the padding state of this client padding.UpdatePaddingScheme(padding.DefaultPaddingScheme, &c.padding) @@ -78,15 +84,38 @@ func (c *Client) CreateOutboundTLSConnection(ctx context.Context) (net.Conn, err b.WriteZeroN(paddingLen) } - conn = tls.Client(conn, c.tlsConfig) + getTlsConn := func() (net.Conn, error) { + if len(c.clientFingerprint) != 0 { + utlsConn, valid := vmess.GetUTLSConn(conn, c.clientFingerprint, c.tlsConfig) + if valid { + ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout) + defer cancel() - _, err = b.WriteTo(conn) + err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx) + return utlsConn, err + } + } + + tlsConn := tls.Client(conn, c.tlsConfig) + + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + + err = tlsConn.HandshakeContext(ctx) + return tlsConn, err + } + tlsConn, err := getTlsConn() if err != nil { conn.Close() return nil, err } - return conn, nil + _, err = b.WriteTo(tlsConn) + if err != nil { + tlsConn.Close() + return nil, err + } + return tlsConn, nil } func (h *Client) Close() error {