From fd63707399363fee886aa39d9f899bc574fc9744 Mon Sep 17 00:00:00 2001 From: changx <620665+changx@users.noreply.github.com> Date: Thu, 1 Nov 2018 11:54:45 +0800 Subject: [PATCH] Optimization: use client session cache for TLS connection (#26) --- adapters/outbound/socks5.go | 21 ++++++++++++---- adapters/outbound/util.go | 14 +++++++++++ adapters/outbound/vmess.go | 1 + component/vmess/vmess.go | 50 ++++++++++++++++++++++++++----------- 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/adapters/outbound/socks5.go b/adapters/outbound/socks5.go index 42389c34..eae61090 100644 --- a/adapters/outbound/socks5.go +++ b/adapters/outbound/socks5.go @@ -32,6 +32,7 @@ type Socks5 struct { name string tls bool skipCertVerify bool + tlsConfig *tls.Config } type Socks5Option struct { @@ -54,11 +55,9 @@ func (ss *Socks5) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err e c, err := net.DialTimeout("tcp", ss.addr, tcpTimeout) if err == nil && ss.tls { - tlsConfig := tls.Config{ - InsecureSkipVerify: ss.skipCertVerify, - MaxVersion: tls.VersionTLS12, - } - c = tls.Client(c, &tlsConfig) + cc := tls.Client(c, ss.tlsConfig) + err = cc.Handshake() + c = cc } if err != nil { @@ -103,10 +102,22 @@ func (ss *Socks5) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { } func NewSocks5(option Socks5Option) *Socks5 { + var tlsConfig *tls.Config + if option.TLS { + tlsConfig = &tls.Config{ + InsecureSkipVerify: option.SkipCertVerify, + ClientSessionCache: getClientSessionCache(), + MinVersion: tls.VersionTLS11, + MaxVersion: tls.VersionTLS12, + ServerName: option.Server, + } + } + return &Socks5{ addr: fmt.Sprintf("%s:%d", option.Server, option.Port), name: option.Name, tls: option.TLS, skipCertVerify: option.SkipCertVerify, + tlsConfig: tlsConfig, } } diff --git a/adapters/outbound/util.go b/adapters/outbound/util.go index f1292546..f38b55e6 100644 --- a/adapters/outbound/util.go +++ b/adapters/outbound/util.go @@ -1,10 +1,12 @@ package adapters import ( + "crypto/tls" "fmt" "net" "net/http" "net/url" + "sync" "time" C "github.com/Dreamacro/clash/constant" @@ -14,6 +16,11 @@ const ( tcpTimeout = 5 * time.Second ) +var ( + globalClientSessionCache tls.ClientSessionCache + once sync.Once +) + // DelayTest get the delay for the specified URL func DelayTest(proxy C.Proxy, url string) (t int16, err error) { addr, err := urlToMetadata(url) @@ -95,3 +102,10 @@ func tcpKeepAlive(c net.Conn) { tcp.SetKeepAlivePeriod(30 * time.Second) } } + +func getClientSessionCache() tls.ClientSessionCache { + once.Do(func() { + globalClientSessionCache = tls.NewLRUClientSessionCache(128) + }) + return globalClientSessionCache +} diff --git a/adapters/outbound/vmess.go b/adapters/outbound/vmess.go index 9ea20aca..68e65509 100644 --- a/adapters/outbound/vmess.go +++ b/adapters/outbound/vmess.go @@ -72,6 +72,7 @@ func NewVmess(option VmessOption) (*Vmess, error) { NetWork: option.Network, WebSocketPath: option.WSPath, SkipCertVerify: option.SkipCertVerify, + SessionCacahe: getClientSessionCache(), }) if err != nil { return nil, err diff --git a/component/vmess/vmess.go b/component/vmess/vmess.go index be1f02dc..f9cd27ec 100644 --- a/component/vmess/vmess.go +++ b/component/vmess/vmess.go @@ -7,6 +7,7 @@ import ( "net" "net/url" "runtime" + "sync" "time" "github.com/gofrs/uuid" @@ -39,6 +40,11 @@ var CipherMapping = map[string]byte{ "chacha20-poly1305": SecurityCHACHA20POLY1305, } +var ( + clientSessionCache tls.ClientSessionCache + once sync.Once +) + // Command types const ( CommandTCP byte = 1 @@ -61,14 +67,14 @@ type DstAddr struct { // Client is vmess connection generator type Client struct { - user []*ID - uuid *uuid.UUID - security Security - tls bool - host string - websocket bool - websocketPath string - skipCertVerify bool + user []*ID + uuid *uuid.UUID + security Security + tls bool + host string + websocket bool + websocketPath string + tlsConfig *tls.Config } // Config of vmess @@ -81,6 +87,7 @@ type Config struct { NetWork string WebSocketPath string SkipCertVerify bool + SessionCacahe tls.ClientSessionCache } // New return a Conn with net.Conn and DstAddr @@ -98,9 +105,7 @@ func (c *Client) New(conn net.Conn, dst *DstAddr) (net.Conn, error) { scheme := "ws" if c.tls { scheme = "wss" - dialer.TLSClientConfig = &tls.Config{ - InsecureSkipVerify: c.skipCertVerify, - } + dialer.TLSClientConfig = c.tlsConfig } host, port, err := net.SplitHostPort(c.host) @@ -125,9 +130,7 @@ func (c *Client) New(conn net.Conn, dst *DstAddr) (net.Conn, error) { conn = newWebsocketConn(wsConn, conn.RemoteAddr()) } else if c.tls { - conn = tls.Client(conn, &tls.Config{ - InsecureSkipVerify: c.skipCertVerify, - }) + conn = tls.Client(conn, c.tlsConfig) } return newConn(conn, c.user[r], dst, c.security), nil } @@ -160,6 +163,17 @@ func NewClient(config Config) (*Client, error) { return nil, fmt.Errorf("Unknown network type: %s", config.NetWork) } + var tlsConfig *tls.Config + if config.TLS { + tlsConfig = &tls.Config{ + InsecureSkipVerify: config.SkipCertVerify, + ClientSessionCache: config.SessionCacahe, + } + if tlsConfig.ClientSessionCache == nil { + tlsConfig.ClientSessionCache = getClientSessionCache() + } + } + return &Client{ user: newAlterIDs(newID(&uid), config.AlterID), uuid: &uid, @@ -168,5 +182,13 @@ func NewClient(config Config) (*Client, error) { host: config.Host, websocket: config.NetWork == "ws", websocketPath: config.WebSocketPath, + tlsConfig: tlsConfig, }, nil } + +func getClientSessionCache() tls.ClientSessionCache { + once.Do(func() { + clientSessionCache = tls.NewLRUClientSessionCache(128) + }) + return clientSessionCache +}