forked from fossabot/clash
/
vmess.go
185 lines (162 loc) · 3.77 KB
/
vmess.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package vmess
import (
"crypto/tls"
"fmt"
"math/rand"
"net"
"net/http"
"runtime"
"sync"
"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,
}
var (
clientSessionCache tls.ClientSessionCache
once sync.Once
)
// 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 {
UDP bool
AddrType byte
Addr []byte
Port uint
}
// Client is vmess connection generator
type Client struct {
user []*ID
uuid *uuid.UUID
security Security
tls bool
host string
wsConfig *WebsocketConfig
tlsConfig *tls.Config
}
// Config of vmess
type Config struct {
UUID string
AlterID uint16
Security string
TLS bool
HostName string
Port string
NetWork string
WebSocketPath string
WebSocketHeaders map[string]string
SkipCertVerify bool
SessionCache tls.ClientSessionCache
}
// New return a Conn with net.Conn and DstAddr
func (c *Client) New(conn net.Conn, dst *DstAddr) (net.Conn, error) {
var err error
r := rand.Intn(len(c.user))
if c.wsConfig != nil {
conn, err = NewWebsocketConn(conn, c.wsConfig)
if err != nil {
return nil, err
}
} else if c.tls {
conn = tls.Client(conn, c.tlsConfig)
}
return newConn(conn, c.user[r], dst, c.security)
}
// 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)
}
if config.NetWork != "" && config.NetWork != "ws" {
return nil, fmt.Errorf("Unknown network type: %s", config.NetWork)
}
header := http.Header{}
for k, v := range config.WebSocketHeaders {
header.Add(k, v)
}
host := net.JoinHostPort(config.HostName, config.Port)
var tlsConfig *tls.Config
if config.TLS {
tlsConfig = &tls.Config{
ServerName: config.HostName,
InsecureSkipVerify: config.SkipCertVerify,
ClientSessionCache: config.SessionCache,
}
if tlsConfig.ClientSessionCache == nil {
tlsConfig.ClientSessionCache = getClientSessionCache()
}
if host := header.Get("Host"); host != "" {
tlsConfig.ServerName = host
}
}
var wsConfig *WebsocketConfig
if config.NetWork == "ws" {
wsConfig = &WebsocketConfig{
Host: host,
Path: config.WebSocketPath,
Headers: header,
TLS: config.TLS,
TLSConfig: tlsConfig,
}
}
return &Client{
user: newAlterIDs(newID(&uid), config.AlterID),
uuid: &uid,
security: security,
tls: config.TLS,
host: host,
wsConfig: wsConfig,
tlsConfig: tlsConfig,
}, nil
}
func getClientSessionCache() tls.ClientSessionCache {
once.Do(func() {
clientSessionCache = tls.NewLRUClientSessionCache(128)
})
return clientSessionCache
}