Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
502aa61c0e | |||
cc6d496143 | |||
10e0231bc1 | |||
fd63707399 | |||
c5757a9b11 |
@ -72,7 +72,7 @@ func (ss *ShadowSocks) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
||||||
server := fmt.Sprintf("%s:%d", option.Server, option.Port)
|
server := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||||
cipher := option.Cipher
|
cipher := option.Cipher
|
||||||
password := option.Password
|
password := option.Password
|
||||||
ciph, err := core.PickCipher(cipher, nil, password)
|
ciph, err := core.PickCipher(cipher, nil, password)
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ type Socks5 struct {
|
|||||||
name string
|
name string
|
||||||
tls bool
|
tls bool
|
||||||
skipCertVerify bool
|
skipCertVerify bool
|
||||||
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type Socks5Option struct {
|
type Socks5Option struct {
|
||||||
@ -54,11 +56,9 @@ func (ss *Socks5) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err e
|
|||||||
c, err := net.DialTimeout("tcp", ss.addr, tcpTimeout)
|
c, err := net.DialTimeout("tcp", ss.addr, tcpTimeout)
|
||||||
|
|
||||||
if err == nil && ss.tls {
|
if err == nil && ss.tls {
|
||||||
tlsConfig := tls.Config{
|
cc := tls.Client(c, ss.tlsConfig)
|
||||||
InsecureSkipVerify: ss.skipCertVerify,
|
err = cc.Handshake()
|
||||||
MaxVersion: tls.VersionTLS12,
|
c = cc
|
||||||
}
|
|
||||||
c = tls.Client(c, &tlsConfig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -103,10 +103,22 @@ func (ss *Socks5) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSocks5(option Socks5Option) *Socks5 {
|
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{
|
return &Socks5{
|
||||||
addr: fmt.Sprintf("%s:%d", option.Server, option.Port),
|
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
name: option.Name,
|
name: option.Name,
|
||||||
tls: option.TLS,
|
tls: option.TLS,
|
||||||
skipCertVerify: option.SkipCertVerify,
|
skipCertVerify: option.SkipCertVerify,
|
||||||
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package adapters
|
package adapters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -14,6 +16,11 @@ const (
|
|||||||
tcpTimeout = 5 * time.Second
|
tcpTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalClientSessionCache tls.ClientSessionCache
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
// DelayTest get the delay for the specified URL
|
// DelayTest get the delay for the specified URL
|
||||||
func DelayTest(proxy C.Proxy, url string) (t int16, err error) {
|
func DelayTest(proxy C.Proxy, url string) (t int16, err error) {
|
||||||
addr, err := urlToMetadata(url)
|
addr, err := urlToMetadata(url)
|
||||||
@ -95,3 +102,10 @@ func tcpKeepAlive(c net.Conn) {
|
|||||||
tcp.SetKeepAlivePeriod(30 * time.Second)
|
tcp.SetKeepAlivePeriod(30 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getClientSessionCache() tls.ClientSessionCache {
|
||||||
|
once.Do(func() {
|
||||||
|
globalClientSessionCache = tls.NewLRUClientSessionCache(128)
|
||||||
|
})
|
||||||
|
return globalClientSessionCache
|
||||||
|
}
|
||||||
|
@ -68,10 +68,11 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
|||||||
AlterID: uint16(option.AlterID),
|
AlterID: uint16(option.AlterID),
|
||||||
Security: security,
|
Security: security,
|
||||||
TLS: option.TLS,
|
TLS: option.TLS,
|
||||||
Host: fmt.Sprintf("%s:%d", option.Server, option.Port),
|
Host: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
NetWork: option.Network,
|
NetWork: option.Network,
|
||||||
WebSocketPath: option.WSPath,
|
WebSocketPath: option.WSPath,
|
||||||
SkipCertVerify: option.SkipCertVerify,
|
SkipCertVerify: option.SkipCertVerify,
|
||||||
|
SessionCacahe: getClientSessionCache(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -79,7 +80,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
|||||||
|
|
||||||
return &Vmess{
|
return &Vmess{
|
||||||
name: option.Name,
|
name: option.Name,
|
||||||
server: fmt.Sprintf("%s:%d", option.Server, option.Port),
|
server: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
client: client,
|
client: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -65,11 +65,10 @@ func (vc *Conn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) sendRequest() error {
|
func (vc *Conn) sendRequest() error {
|
||||||
timestamp := make([]byte, 8)
|
timestamp := time.Now()
|
||||||
binary.BigEndian.PutUint64(timestamp, uint64(time.Now().UTC().Unix()))
|
|
||||||
|
|
||||||
h := hmac.New(md5.New, vc.id.UUID.Bytes())
|
h := hmac.New(md5.New, vc.id.UUID.Bytes())
|
||||||
h.Write(timestamp)
|
binary.Write(h, binary.BigEndian, uint64(timestamp.Unix()))
|
||||||
_, err := vc.Conn.Write(h.Sum(nil))
|
_, err := vc.Conn.Write(h.Sum(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -111,7 +110,7 @@ func (vc *Conn) sendRequest() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stream := cipher.NewCFBEncrypter(block, hashTimestamp(time.Now().UTC()))
|
stream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp))
|
||||||
stream.XORKeyStream(buf.Bytes(), buf.Bytes())
|
stream.XORKeyStream(buf.Bytes(), buf.Bytes())
|
||||||
_, err = vc.Conn.Write(buf.Bytes())
|
_, err = vc.Conn.Write(buf.Bytes())
|
||||||
return err
|
return err
|
||||||
@ -145,7 +144,7 @@ func (vc *Conn) recvResponse() error {
|
|||||||
func hashTimestamp(t time.Time) []byte {
|
func hashTimestamp(t time.Time) []byte {
|
||||||
md5hash := md5.New()
|
md5hash := md5.New()
|
||||||
ts := make([]byte, 8)
|
ts := make([]byte, 8)
|
||||||
binary.BigEndian.PutUint64(ts, uint64(t.UTC().Unix()))
|
binary.BigEndian.PutUint64(ts, uint64(t.Unix()))
|
||||||
md5hash.Write(ts)
|
md5hash.Write(ts)
|
||||||
md5hash.Write(ts)
|
md5hash.Write(ts)
|
||||||
md5hash.Write(ts)
|
md5hash.Write(ts)
|
||||||
|
@ -5,12 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"sync"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version of vmess
|
// Version of vmess
|
||||||
@ -39,6 +37,11 @@ var CipherMapping = map[string]byte{
|
|||||||
"chacha20-poly1305": SecurityCHACHA20POLY1305,
|
"chacha20-poly1305": SecurityCHACHA20POLY1305,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientSessionCache tls.ClientSessionCache
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
// Command types
|
// Command types
|
||||||
const (
|
const (
|
||||||
CommandTCP byte = 1
|
CommandTCP byte = 1
|
||||||
@ -61,14 +64,13 @@ type DstAddr struct {
|
|||||||
|
|
||||||
// Client is vmess connection generator
|
// Client is vmess connection generator
|
||||||
type Client struct {
|
type Client struct {
|
||||||
user []*ID
|
user []*ID
|
||||||
uuid *uuid.UUID
|
uuid *uuid.UUID
|
||||||
security Security
|
security Security
|
||||||
tls bool
|
tls bool
|
||||||
host string
|
host string
|
||||||
websocket bool
|
wsConfig *websocketConfig
|
||||||
websocketPath string
|
tlsConfig *tls.Config
|
||||||
skipCertVerify bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config of vmess
|
// Config of vmess
|
||||||
@ -81,54 +83,20 @@ type Config struct {
|
|||||||
NetWork string
|
NetWork string
|
||||||
WebSocketPath string
|
WebSocketPath string
|
||||||
SkipCertVerify bool
|
SkipCertVerify bool
|
||||||
|
SessionCacahe tls.ClientSessionCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// New return a Conn with net.Conn and DstAddr
|
// New return a Conn with net.Conn and DstAddr
|
||||||
func (c *Client) New(conn net.Conn, dst *DstAddr) (net.Conn, error) {
|
func (c *Client) New(conn net.Conn, dst *DstAddr) (net.Conn, error) {
|
||||||
|
var err error
|
||||||
r := rand.Intn(len(c.user))
|
r := rand.Intn(len(c.user))
|
||||||
if c.websocket {
|
if c.wsConfig != nil {
|
||||||
dialer := &websocket.Dialer{
|
conn, err = newWebsocketConn(conn, c.wsConfig)
|
||||||
NetDial: func(network, addr string) (net.Conn, error) {
|
|
||||||
return conn, nil
|
|
||||||
},
|
|
||||||
ReadBufferSize: 4 * 1024,
|
|
||||||
WriteBufferSize: 4 * 1024,
|
|
||||||
HandshakeTimeout: time.Second * 8,
|
|
||||||
}
|
|
||||||
scheme := "ws"
|
|
||||||
if c.tls {
|
|
||||||
scheme = "wss"
|
|
||||||
dialer.TLSClientConfig = &tls.Config{
|
|
||||||
InsecureSkipVerify: c.skipCertVerify,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
host, port, err := net.SplitHostPort(c.host)
|
|
||||||
if (scheme == "ws" && port != "80") || (scheme == "wss" && port != "443") {
|
|
||||||
host = c.host
|
|
||||||
}
|
|
||||||
|
|
||||||
uri := url.URL{
|
|
||||||
Scheme: scheme,
|
|
||||||
Host: host,
|
|
||||||
Path: c.websocketPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
wsConn, resp, err := dialer.Dial(uri.String(), nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var reason string
|
return nil, err
|
||||||
if resp != nil {
|
|
||||||
reason = resp.Status
|
|
||||||
}
|
|
||||||
println(uri.String(), err.Error())
|
|
||||||
return nil, fmt.Errorf("Dial %s error: %s", host, reason)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = newWebsocketConn(wsConn, conn.RemoteAddr())
|
|
||||||
} else if c.tls {
|
} else if c.tls {
|
||||||
conn = tls.Client(conn, &tls.Config{
|
conn = tls.Client(conn, c.tlsConfig)
|
||||||
InsecureSkipVerify: c.skipCertVerify,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return newConn(conn, c.user[r], dst, c.security), nil
|
return newConn(conn, c.user[r], dst, c.security), nil
|
||||||
}
|
}
|
||||||
@ -161,13 +129,41 @@ func NewClient(config Config) (*Client, error) {
|
|||||||
return nil, fmt.Errorf("Unknown network type: %s", config.NetWork)
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wsConfig *websocketConfig
|
||||||
|
if config.NetWork == "ws" {
|
||||||
|
wsConfig = &websocketConfig{
|
||||||
|
host: config.Host,
|
||||||
|
path: config.WebSocketPath,
|
||||||
|
tls: config.TLS,
|
||||||
|
tlsConfig: tlsConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
user: newAlterIDs(newID(&uid), config.AlterID),
|
user: newAlterIDs(newID(&uid), config.AlterID),
|
||||||
uuid: &uid,
|
uuid: &uid,
|
||||||
security: security,
|
security: security,
|
||||||
tls: config.TLS,
|
tls: config.TLS,
|
||||||
host: config.Host,
|
host: config.Host,
|
||||||
websocket: config.NetWork == "ws",
|
wsConfig: wsConfig,
|
||||||
websocketPath: config.WebSocketPath,
|
tlsConfig: tlsConfig,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getClientSessionCache() tls.ClientSessionCache {
|
||||||
|
once.Do(func() {
|
||||||
|
clientSessionCache = tls.NewLRUClientSessionCache(128)
|
||||||
|
})
|
||||||
|
return clientSessionCache
|
||||||
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package vmess
|
package vmess
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -16,6 +18,13 @@ type websocketConn struct {
|
|||||||
remoteAddr net.Addr
|
remoteAddr net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type websocketConfig struct {
|
||||||
|
host string
|
||||||
|
path string
|
||||||
|
tls bool
|
||||||
|
tlsConfig *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
// Read implements net.Conn.Read()
|
// Read implements net.Conn.Read()
|
||||||
func (wsc *websocketConn) Read(b []byte) (int, error) {
|
func (wsc *websocketConn) Read(b []byte) (int, error) {
|
||||||
for {
|
for {
|
||||||
@ -91,9 +100,44 @@ func (wsc *websocketConn) SetWriteDeadline(t time.Time) error {
|
|||||||
return wsc.conn.SetWriteDeadline(t)
|
return wsc.conn.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWebsocketConn(conn *websocket.Conn, remoteAddr net.Addr) net.Conn {
|
func newWebsocketConn(conn net.Conn, c *websocketConfig) (net.Conn, error) {
|
||||||
return &websocketConn{
|
dialer := &websocket.Dialer{
|
||||||
conn: conn,
|
NetDial: func(network, addr string) (net.Conn, error) {
|
||||||
remoteAddr: remoteAddr,
|
return conn, nil
|
||||||
|
},
|
||||||
|
ReadBufferSize: 4 * 1024,
|
||||||
|
WriteBufferSize: 4 * 1024,
|
||||||
|
HandshakeTimeout: time.Second * 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheme := "ws"
|
||||||
|
if c.tls {
|
||||||
|
scheme = "wss"
|
||||||
|
dialer.TLSClientConfig = c.tlsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
host, port, err := net.SplitHostPort(c.host)
|
||||||
|
if (scheme == "ws" && port != "80") || (scheme == "wss" && port != "443") {
|
||||||
|
host = c.host
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := url.URL{
|
||||||
|
Scheme: scheme,
|
||||||
|
Host: host,
|
||||||
|
Path: c.path,
|
||||||
|
}
|
||||||
|
|
||||||
|
wsConn, resp, err := dialer.Dial(uri.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
var reason string
|
||||||
|
if resp != nil {
|
||||||
|
reason = resp.Status
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Dial %s error: %s", host, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &websocketConn{
|
||||||
|
conn: wsConn,
|
||||||
|
remoteAddr: conn.RemoteAddr(),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user