Feature: add basic support for the VLESS protocol (#2891)
This commit is contained in:
parent
651a36964e
commit
8a4c46ae77
10
adapter/outbound/vless.go
Normal file
10
adapter/outbound/vless.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package outbound
|
||||||
|
|
||||||
|
type (
|
||||||
|
Vless = Vmess
|
||||||
|
VlessOption = VmessOption
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewVless(option VlessOption) (*Vless, error) {
|
||||||
|
return newVmess(option, true)
|
||||||
|
}
|
@ -39,8 +39,8 @@ type VmessOption struct {
|
|||||||
Server string `proxy:"server"`
|
Server string `proxy:"server"`
|
||||||
Port int `proxy:"port"`
|
Port int `proxy:"port"`
|
||||||
UUID string `proxy:"uuid"`
|
UUID string `proxy:"uuid"`
|
||||||
AlterID int `proxy:"alterId"`
|
AlterID int `proxy:"alterId,omitempty"`
|
||||||
Cipher string `proxy:"cipher"`
|
Cipher string `proxy:"cipher,omitempty"`
|
||||||
UDP bool `proxy:"udp,omitempty"`
|
UDP bool `proxy:"udp,omitempty"`
|
||||||
Network string `proxy:"network,omitempty"`
|
Network string `proxy:"network,omitempty"`
|
||||||
TLS bool `proxy:"tls,omitempty"`
|
TLS bool `proxy:"tls,omitempty"`
|
||||||
@ -264,7 +264,14 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewVmess(option VmessOption) (*Vmess, error) {
|
func NewVmess(option VmessOption) (*Vmess, error) {
|
||||||
|
return newVmess(option, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVmess(option VmessOption, isVless bool) (*Vmess, error) {
|
||||||
security := strings.ToLower(option.Cipher)
|
security := strings.ToLower(option.Cipher)
|
||||||
|
if security == "" {
|
||||||
|
security = "auto"
|
||||||
|
}
|
||||||
client, err := vmess.NewClient(vmess.Config{
|
client, err := vmess.NewClient(vmess.Config{
|
||||||
UUID: option.UUID,
|
UUID: option.UUID,
|
||||||
AlterID: uint16(option.AlterID),
|
AlterID: uint16(option.AlterID),
|
||||||
@ -272,6 +279,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
|||||||
HostName: option.Server,
|
HostName: option.Server,
|
||||||
Port: strconv.Itoa(option.Port),
|
Port: strconv.Itoa(option.Port),
|
||||||
IsAead: option.AlterID == 0,
|
IsAead: option.AlterID == 0,
|
||||||
|
IsVless: isVless,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -284,11 +292,16 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tp := C.Vmess
|
||||||
|
if isVless {
|
||||||
|
tp = C.Vless
|
||||||
|
}
|
||||||
|
|
||||||
v := &Vmess{
|
v := &Vmess{
|
||||||
Base: &Base{
|
Base: &Base{
|
||||||
name: option.Name,
|
name: option.Name,
|
||||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
tp: C.Vmess,
|
tp: tp,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
|
@ -48,6 +48,8 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
proxy = outbound.NewHttp(*httpOption)
|
proxy = outbound.NewHttp(*httpOption)
|
||||||
|
case "vless":
|
||||||
|
fallthrough
|
||||||
case "vmess":
|
case "vmess":
|
||||||
vmessOption := &outbound.VmessOption{
|
vmessOption := &outbound.VmessOption{
|
||||||
HTTPOpts: outbound.HTTPOptions{
|
HTTPOpts: outbound.HTTPOptions{
|
||||||
@ -59,7 +61,11 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
proxy, err = outbound.NewVmess(*vmessOption)
|
if proxyType == "vless" {
|
||||||
|
proxy, err = outbound.NewVless(*vmessOption)
|
||||||
|
} else {
|
||||||
|
proxy, err = outbound.NewVmess(*vmessOption)
|
||||||
|
}
|
||||||
case "snell":
|
case "snell":
|
||||||
snellOption := &outbound.SnellOption{}
|
snellOption := &outbound.SnellOption{}
|
||||||
err = decoder.Decode(mapping, snellOption)
|
err = decoder.Decode(mapping, snellOption)
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
Snell
|
Snell
|
||||||
Socks5
|
Socks5
|
||||||
Http
|
Http
|
||||||
|
Vless
|
||||||
Vmess
|
Vmess
|
||||||
Trojan
|
Trojan
|
||||||
|
|
||||||
@ -140,6 +141,8 @@ func (at AdapterType) String() string {
|
|||||||
return "Socks5"
|
return "Socks5"
|
||||||
case Http:
|
case Http:
|
||||||
return "Http"
|
return "Http"
|
||||||
|
case Vless:
|
||||||
|
return "Vless"
|
||||||
case Vmess:
|
case Vmess:
|
||||||
return "Vmess"
|
return "Vmess"
|
||||||
case Trojan:
|
case Trojan:
|
||||||
|
40
test/config/vless-grpc.json
Normal file
40
test/config/vless-grpc.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "grpc",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||||
|
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"grpcSettings": {
|
||||||
|
"serviceName": "example!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
}
|
||||||
|
}
|
55
test/config/vless-http.json
Normal file
55
test/config/vless-http.json
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "tcp",
|
||||||
|
"tcpSettings": {
|
||||||
|
"header": {
|
||||||
|
"type": "http",
|
||||||
|
"response": {
|
||||||
|
"version": "1.1",
|
||||||
|
"status": "200",
|
||||||
|
"reason": "OK",
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": [
|
||||||
|
"application/octet-stream",
|
||||||
|
"video/mpeg",
|
||||||
|
"application/x-msdownload",
|
||||||
|
"text/html",
|
||||||
|
"application/x-shockwave-flash"
|
||||||
|
],
|
||||||
|
"Transfer-Encoding": [
|
||||||
|
"chunked"
|
||||||
|
],
|
||||||
|
"Connection": [
|
||||||
|
"keep-alive"
|
||||||
|
],
|
||||||
|
"Pragma": "no-cache"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
}
|
||||||
|
}
|
43
test/config/vless-http2.json
Normal file
43
test/config/vless-http2.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "http",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||||
|
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"httpSettings": {
|
||||||
|
"host": [
|
||||||
|
"example.org"
|
||||||
|
],
|
||||||
|
"path": "/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
}
|
||||||
|
}
|
37
test/config/vless-tls.json
Normal file
37
test/config/vless-tls.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "tcp",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||||
|
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
}
|
||||||
|
}
|
30
test/config/vless-ws-0rtt.json
Normal file
30
test/config/vless-ws-0rtt.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "ws",
|
||||||
|
"security": "none",
|
||||||
|
"wsSettings": {
|
||||||
|
"maxEarlyData": 128,
|
||||||
|
"earlyDataHeaderName": "Sec-WebSocket-Protocol"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
34
test/config/vless-ws-tls-zero.json
Normal file
34
test/config/vless-ws-tls-zero.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "ws",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||||
|
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
34
test/config/vless-ws-tls.json
Normal file
34
test/config/vless-ws-tls.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "ws",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||||
|
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
26
test/config/vless-ws.json
Normal file
26
test/config/vless-ws.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "ws",
|
||||||
|
"security": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
28
test/config/vless.json
Normal file
28
test/config/vless.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
}
|
||||||
|
}
|
452
test/vless_test.go
Normal file
452
test/vless_test.go
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClash_Vless(t *testing.T) {
|
||||||
|
configPath := C.Path.Resolve("vless.json")
|
||||||
|
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{fmt.Sprintf("%s:/etc/v2ray/config.json", configPath)},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
UDP: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessTLS(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-tls.json")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-tls")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
TLS: true,
|
||||||
|
SkipCertVerify: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
UDP: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessHTTP2(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-http2.json")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-http2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
Network: "h2",
|
||||||
|
TLS: true,
|
||||||
|
SkipCertVerify: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
UDP: true,
|
||||||
|
HTTP2Opts: outbound.HTTP2Options{
|
||||||
|
Host: []string{"example.org"},
|
||||||
|
Path: "/test",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessHTTP(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-http.json")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-http")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
Network: "http",
|
||||||
|
UDP: true,
|
||||||
|
HTTPOpts: outbound.HTTPOptions{
|
||||||
|
Method: "GET",
|
||||||
|
Path: []string{"/"},
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"Host": {"www.amazon.com"},
|
||||||
|
"User-Agent": {
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.49",
|
||||||
|
},
|
||||||
|
"Accept-Encoding": {
|
||||||
|
"gzip, deflate",
|
||||||
|
},
|
||||||
|
"Connection": {
|
||||||
|
"keep-alive",
|
||||||
|
},
|
||||||
|
"Pragma": {"no-cache"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessWebsocket(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-ws.json")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-ws")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
Network: "ws",
|
||||||
|
UDP: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessWebsocketTLS(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-ws-tls.json")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-ws-tls")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
Network: "ws",
|
||||||
|
TLS: true,
|
||||||
|
SkipCertVerify: true,
|
||||||
|
UDP: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessWebsocketTLSZero(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-ws-tls-zero.json")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-ws-tls-zero")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "zero",
|
||||||
|
Network: "ws",
|
||||||
|
TLS: true,
|
||||||
|
SkipCertVerify: true,
|
||||||
|
UDP: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessGrpc(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-grpc.json")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||||
|
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-grpc")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
Network: "grpc",
|
||||||
|
TLS: true,
|
||||||
|
SkipCertVerify: true,
|
||||||
|
UDP: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
GrpcOpts: outbound.GrpcOptions{
|
||||||
|
GrpcServiceName: "example!",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessWebsocket0RTT(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-ws-0rtt.json")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-ws-0rtt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
Network: "ws",
|
||||||
|
UDP: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
WSOpts: outbound.WSOptions{
|
||||||
|
MaxEarlyData: 2048,
|
||||||
|
EarlyDataHeaderName: "Sec-WebSocket-Protocol",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClash_VlessWebsocketXray0RTT(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageXray,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/xray/config.json", C.Path.Resolve("vless-ws-0rtt.json")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-xray-ws-0rtt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
Network: "ws",
|
||||||
|
UDP: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
WSOpts: outbound.WSOptions{
|
||||||
|
Path: "/?ed=2048",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Vless(b *testing.B) {
|
||||||
|
configPath := C.Path.Resolve("vless.json")
|
||||||
|
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageVmess,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
Entrypoint: []string{"/usr/bin/v2ray"},
|
||||||
|
Cmd: []string{"run", "-c", "/etc/v2ray/config.json"},
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{fmt.Sprintf("%s:/etc/v2ray/config.json", configPath)},
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := startContainer(cfg, hostCfg, "vless-bench")
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
b.Cleanup(func() {
|
||||||
|
cleanContainer(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
proxy, err := outbound.NewVless(outbound.VlessOption{
|
||||||
|
Name: "vless",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
Cipher: "auto",
|
||||||
|
AlterID: 0,
|
||||||
|
UDP: true,
|
||||||
|
})
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
benchmarkProxy(b, proxy)
|
||||||
|
}
|
@ -34,6 +34,7 @@ type Conn struct {
|
|||||||
security byte
|
security byte
|
||||||
option byte
|
option byte
|
||||||
isAead bool
|
isAead bool
|
||||||
|
isVless bool
|
||||||
|
|
||||||
received bool
|
received bool
|
||||||
}
|
}
|
||||||
@ -55,6 +56,29 @@ func (vc *Conn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) sendRequest() error {
|
func (vc *Conn) sendRequest() error {
|
||||||
|
if vc.isVless {
|
||||||
|
buf := protobytes.BytesWriter{}
|
||||||
|
buf.PutUint8(0) // Protocol Version
|
||||||
|
buf.PutSlice(vc.id.UUID.Bytes()) // UUID
|
||||||
|
buf.PutUint8(0) // Addons Length
|
||||||
|
// buf.PutString("") // Addons Data
|
||||||
|
|
||||||
|
// Command
|
||||||
|
if vc.dst.UDP {
|
||||||
|
buf.PutUint8(CommandUDP)
|
||||||
|
} else {
|
||||||
|
buf.PutUint8(CommandTCP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Port AddrType Addr
|
||||||
|
buf.PutUint16be(uint16(vc.dst.Port))
|
||||||
|
buf.PutUint8(vc.dst.AddrType)
|
||||||
|
buf.PutSlice(vc.dst.Addr)
|
||||||
|
|
||||||
|
_, err := vc.Conn.Write(buf.Bytes())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
timestamp := time.Now()
|
timestamp := time.Now()
|
||||||
|
|
||||||
mbuf := protobytes.BytesWriter{}
|
mbuf := protobytes.BytesWriter{}
|
||||||
@ -119,6 +143,24 @@ func (vc *Conn) sendRequest() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) recvResponse() error {
|
func (vc *Conn) recvResponse() error {
|
||||||
|
if vc.isVless {
|
||||||
|
var buffer [2]byte
|
||||||
|
if _, err := io.ReadFull(vc.Conn, buffer[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if buffer[0] != 0 {
|
||||||
|
return errors.New("unexpected response version")
|
||||||
|
}
|
||||||
|
|
||||||
|
length := int64(buffer[1])
|
||||||
|
if length != 0 { // addon data length > 0
|
||||||
|
io.CopyN(io.Discard, vc.Conn, length) // just discard
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var buf []byte
|
var buf []byte
|
||||||
if !vc.isAead {
|
if !vc.isAead {
|
||||||
block, err := aes.NewCipher(vc.respBodyKey[:])
|
block, err := aes.NewCipher(vc.respBodyKey[:])
|
||||||
@ -194,31 +236,37 @@ func hashTimestamp(t time.Time) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newConn return a Conn instance
|
// newConn return a Conn instance
|
||||||
func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) {
|
func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool, isVless bool) (*Conn, error) {
|
||||||
randBytes := make([]byte, 33)
|
|
||||||
rand.Read(randBytes)
|
|
||||||
reqBodyIV := make([]byte, 16)
|
|
||||||
reqBodyKey := make([]byte, 16)
|
|
||||||
copy(reqBodyIV[:], randBytes[:16])
|
|
||||||
copy(reqBodyKey[:], randBytes[16:32])
|
|
||||||
respV := randBytes[32]
|
|
||||||
option := OptionChunkStream
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
reqBodyKey []byte
|
||||||
|
reqBodyIV []byte
|
||||||
respBodyKey []byte
|
respBodyKey []byte
|
||||||
respBodyIV []byte
|
respBodyIV []byte
|
||||||
|
respV byte
|
||||||
|
option byte
|
||||||
)
|
)
|
||||||
|
|
||||||
if isAead {
|
if !isVless {
|
||||||
bodyKey := sha256.Sum256(reqBodyKey)
|
randBytes := make([]byte, 33)
|
||||||
bodyIV := sha256.Sum256(reqBodyIV)
|
rand.Read(randBytes)
|
||||||
respBodyKey = bodyKey[:16]
|
reqBodyIV = make([]byte, 16)
|
||||||
respBodyIV = bodyIV[:16]
|
reqBodyKey = make([]byte, 16)
|
||||||
} else {
|
copy(reqBodyIV[:], randBytes[:16])
|
||||||
bodyKey := md5.Sum(reqBodyKey)
|
copy(reqBodyKey[:], randBytes[16:32])
|
||||||
bodyIV := md5.Sum(reqBodyIV)
|
respV = randBytes[32]
|
||||||
respBodyKey = bodyKey[:]
|
option = OptionChunkStream
|
||||||
respBodyIV = bodyIV[:]
|
|
||||||
|
if isAead {
|
||||||
|
bodyKey := sha256.Sum256(reqBodyKey)
|
||||||
|
bodyIV := sha256.Sum256(reqBodyIV)
|
||||||
|
respBodyKey = bodyKey[:16]
|
||||||
|
respBodyIV = bodyIV[:16]
|
||||||
|
} else {
|
||||||
|
bodyKey := md5.Sum(reqBodyKey)
|
||||||
|
bodyIV := md5.Sum(reqBodyIV)
|
||||||
|
respBodyKey = bodyKey[:]
|
||||||
|
respBodyIV = bodyIV[:]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var writer io.Writer
|
var writer io.Writer
|
||||||
@ -276,6 +324,7 @@ func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool
|
|||||||
security: security,
|
security: security,
|
||||||
option: option,
|
option: option,
|
||||||
isAead: isAead,
|
isAead: isAead,
|
||||||
|
isVless: isVless,
|
||||||
}
|
}
|
||||||
if err := c.sendRequest(); err != nil {
|
if err := c.sendRequest(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -56,6 +56,7 @@ type Client struct {
|
|||||||
uuid *uuid.UUID
|
uuid *uuid.UUID
|
||||||
security Security
|
security Security
|
||||||
isAead bool
|
isAead bool
|
||||||
|
isVless bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config of vmess
|
// Config of vmess
|
||||||
@ -66,12 +67,13 @@ type Config struct {
|
|||||||
Port string
|
Port string
|
||||||
HostName string
|
HostName string
|
||||||
IsAead bool
|
IsAead bool
|
||||||
|
IsVless bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamConn return a Conn with net.Conn and DstAddr
|
// StreamConn return a Conn with net.Conn and DstAddr
|
||||||
func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {
|
func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {
|
||||||
r := rand.Intn(len(c.user))
|
r := rand.Intn(len(c.user))
|
||||||
return newConn(conn, c.user[r], dst, c.security, c.isAead)
|
return newConn(conn, c.user[r], dst, c.security, c.isAead, c.isVless)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient return Client instance
|
// NewClient return Client instance
|
||||||
@ -81,6 +83,11 @@ func NewClient(config Config) (*Client, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.IsVless {
|
||||||
|
config.AlterID = 0
|
||||||
|
config.Security = "zero"
|
||||||
|
}
|
||||||
|
|
||||||
var security Security
|
var security Security
|
||||||
switch config.Security {
|
switch config.Security {
|
||||||
case "aes-128-gcm":
|
case "aes-128-gcm":
|
||||||
@ -105,5 +112,6 @@ func NewClient(config Config) (*Client, error) {
|
|||||||
uuid: &uid,
|
uuid: &uid,
|
||||||
security: security,
|
security: security,
|
||||||
isAead: config.IsAead,
|
isAead: config.IsAead,
|
||||||
|
isVless: config.IsVless,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user