2018-09-06 10:53:29 +08:00
|
|
|
package vmess
|
|
|
|
|
|
|
|
import (
|
2018-09-08 19:53:24 +08:00
|
|
|
"crypto/tls"
|
2018-09-06 10:53:29 +08:00
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
2019-07-31 11:13:49 +08:00
|
|
|
"net/http"
|
2018-09-06 10:53:29 +08:00
|
|
|
"runtime"
|
2018-11-01 11:54:45 +08:00
|
|
|
"sync"
|
2018-09-06 10:53:29 +08:00
|
|
|
|
|
|
|
"github.com/gofrs/uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Version of vmess
|
|
|
|
const Version byte = 1
|
|
|
|
|
|
|
|
// Request Options
|
|
|
|
const (
|
|
|
|
OptionChunkStream byte = 1
|
|
|
|
OptionChunkMasking byte = 4
|
|
|
|
)
|
|
|
|
|
|
|
|
// Security type vmess
|
|
|
|
type Security = byte
|
|
|
|
|
|
|
|
// Cipher types
|
|
|
|
const (
|
|
|
|
SecurityAES128GCM Security = 3
|
|
|
|
SecurityCHACHA20POLY1305 Security = 4
|
|
|
|
SecurityNone Security = 5
|
|
|
|
)
|
|
|
|
|
|
|
|
// CipherMapping return
|
|
|
|
var CipherMapping = map[string]byte{
|
|
|
|
"none": SecurityNone,
|
|
|
|
"aes-128-gcm": SecurityAES128GCM,
|
|
|
|
"chacha20-poly1305": SecurityCHACHA20POLY1305,
|
|
|
|
}
|
|
|
|
|
2018-11-01 11:54:45 +08:00
|
|
|
var (
|
|
|
|
clientSessionCache tls.ClientSessionCache
|
|
|
|
once sync.Once
|
|
|
|
)
|
|
|
|
|
2018-09-06 10:53:29 +08:00
|
|
|
// Command types
|
|
|
|
const (
|
|
|
|
CommandTCP byte = 1
|
|
|
|
CommandUDP byte = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
// Addr types
|
|
|
|
const (
|
|
|
|
AtypIPv4 byte = 1
|
|
|
|
AtypDomainName byte = 2
|
|
|
|
AtypIPv6 byte = 3
|
|
|
|
)
|
|
|
|
|
|
|
|
// DstAddr store destination address
|
|
|
|
type DstAddr struct {
|
2019-04-25 16:32:15 +08:00
|
|
|
UDP bool
|
2018-09-06 10:53:29 +08:00
|
|
|
AddrType byte
|
|
|
|
Addr []byte
|
|
|
|
Port uint
|
|
|
|
}
|
|
|
|
|
|
|
|
// Client is vmess connection generator
|
|
|
|
type Client struct {
|
2018-11-04 21:36:20 +08:00
|
|
|
user []*ID
|
|
|
|
uuid *uuid.UUID
|
|
|
|
security Security
|
|
|
|
tls bool
|
|
|
|
host string
|
2019-02-11 15:25:10 +08:00
|
|
|
wsConfig *WebsocketConfig
|
2018-11-04 21:36:20 +08:00
|
|
|
tlsConfig *tls.Config
|
2018-09-06 10:53:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Config of vmess
|
|
|
|
type Config struct {
|
2018-12-11 00:25:05 +08:00
|
|
|
UUID string
|
|
|
|
AlterID uint16
|
|
|
|
Security string
|
|
|
|
TLS bool
|
|
|
|
HostName string
|
|
|
|
Port string
|
|
|
|
NetWork string
|
|
|
|
WebSocketPath string
|
|
|
|
WebSocketHeaders map[string]string
|
|
|
|
SkipCertVerify bool
|
2019-07-29 12:25:29 +08:00
|
|
|
SessionCache tls.ClientSessionCache
|
2018-09-06 10:53:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// New return a Conn with net.Conn and DstAddr
|
2018-10-28 23:46:32 +08:00
|
|
|
func (c *Client) New(conn net.Conn, dst *DstAddr) (net.Conn, error) {
|
2018-11-04 21:36:20 +08:00
|
|
|
var err error
|
2018-09-06 10:53:29 +08:00
|
|
|
r := rand.Intn(len(c.user))
|
2018-11-04 21:36:20 +08:00
|
|
|
if c.wsConfig != nil {
|
2019-02-11 15:25:10 +08:00
|
|
|
conn, err = NewWebsocketConn(conn, c.wsConfig)
|
2018-10-28 23:46:32 +08:00
|
|
|
if err != nil {
|
2018-11-04 21:36:20 +08:00
|
|
|
return nil, err
|
2018-10-28 23:46:32 +08:00
|
|
|
}
|
|
|
|
} else if c.tls {
|
2018-11-01 11:54:45 +08:00
|
|
|
conn = tls.Client(conn, c.tlsConfig)
|
2018-09-08 19:53:24 +08:00
|
|
|
}
|
2019-02-15 21:55:15 +08:00
|
|
|
return newConn(conn, c.user[r], dst, c.security)
|
2018-09-06 10:53:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewClient return Client instance
|
|
|
|
func NewClient(config Config) (*Client, error) {
|
|
|
|
uid, err := uuid.FromString(config.UUID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var security Security
|
|
|
|
switch config.Security {
|
|
|
|
case "aes-128-gcm":
|
|
|
|
security = SecurityAES128GCM
|
|
|
|
case "chacha20-poly1305":
|
|
|
|
security = SecurityCHACHA20POLY1305
|
|
|
|
case "none":
|
|
|
|
security = SecurityNone
|
|
|
|
case "auto":
|
|
|
|
security = SecurityCHACHA20POLY1305
|
|
|
|
if runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" || runtime.GOARCH == "arm64" {
|
|
|
|
security = SecurityAES128GCM
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("Unknown security type: %s", config.Security)
|
|
|
|
}
|
2018-10-28 23:46:32 +08:00
|
|
|
|
|
|
|
if config.NetWork != "" && config.NetWork != "ws" {
|
|
|
|
return nil, fmt.Errorf("Unknown network type: %s", config.NetWork)
|
|
|
|
}
|
|
|
|
|
2019-07-31 11:13:49 +08:00
|
|
|
header := http.Header{}
|
|
|
|
for k, v := range config.WebSocketHeaders {
|
|
|
|
header.Add(k, v)
|
|
|
|
}
|
|
|
|
|
2018-11-28 23:24:57 +08:00
|
|
|
host := net.JoinHostPort(config.HostName, config.Port)
|
|
|
|
|
2018-11-01 11:54:45 +08:00
|
|
|
var tlsConfig *tls.Config
|
|
|
|
if config.TLS {
|
|
|
|
tlsConfig = &tls.Config{
|
2018-11-28 23:24:57 +08:00
|
|
|
ServerName: config.HostName,
|
2018-11-01 11:54:45 +08:00
|
|
|
InsecureSkipVerify: config.SkipCertVerify,
|
2019-07-29 12:25:29 +08:00
|
|
|
ClientSessionCache: config.SessionCache,
|
2018-11-01 11:54:45 +08:00
|
|
|
}
|
|
|
|
if tlsConfig.ClientSessionCache == nil {
|
|
|
|
tlsConfig.ClientSessionCache = getClientSessionCache()
|
|
|
|
}
|
2019-07-31 11:13:49 +08:00
|
|
|
if host := header.Get("Host"); host != "" {
|
|
|
|
tlsConfig.ServerName = host
|
|
|
|
}
|
2018-11-01 11:54:45 +08:00
|
|
|
}
|
|
|
|
|
2019-02-11 15:25:10 +08:00
|
|
|
var wsConfig *WebsocketConfig
|
2018-11-04 21:36:20 +08:00
|
|
|
if config.NetWork == "ws" {
|
2019-02-11 15:25:10 +08:00
|
|
|
wsConfig = &WebsocketConfig{
|
|
|
|
Host: host,
|
|
|
|
Path: config.WebSocketPath,
|
2019-07-31 11:13:49 +08:00
|
|
|
Headers: header,
|
2019-02-11 15:25:10 +08:00
|
|
|
TLS: config.TLS,
|
|
|
|
TLSConfig: tlsConfig,
|
2018-11-04 21:36:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 10:53:29 +08:00
|
|
|
return &Client{
|
2018-11-04 21:36:20 +08:00
|
|
|
user: newAlterIDs(newID(&uid), config.AlterID),
|
|
|
|
uuid: &uid,
|
|
|
|
security: security,
|
|
|
|
tls: config.TLS,
|
2018-11-28 23:24:57 +08:00
|
|
|
host: host,
|
2018-11-04 21:36:20 +08:00
|
|
|
wsConfig: wsConfig,
|
|
|
|
tlsConfig: tlsConfig,
|
2018-09-06 10:53:29 +08:00
|
|
|
}, nil
|
|
|
|
}
|
2018-11-01 11:54:45 +08:00
|
|
|
|
|
|
|
func getClientSessionCache() tls.ClientSessionCache {
|
|
|
|
once.Do(func() {
|
|
|
|
clientSessionCache = tls.NewLRUClientSessionCache(128)
|
|
|
|
})
|
|
|
|
return clientSessionCache
|
|
|
|
}
|