Skip to content

Commit

Permalink
Merge pull request #66 from rprx/master
Browse files Browse the repository at this point in the history
VLESS PROTOCOL
  • Loading branch information
RPRX committed Jul 29, 2020
2 parents 1fdd9f8 + bad7e2c commit 541fb5c
Show file tree
Hide file tree
Showing 26 changed files with 2,326 additions and 1 deletion.
4 changes: 3 additions & 1 deletion infra/conf/v2ray.go
Expand Up @@ -19,6 +19,7 @@ var (
"http": func() interface{} { return new(HttpServerConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
"socks": func() interface{} { return new(SocksServerConfig) },
"vless": func() interface{} { return new(VLessInboundConfig) },
"vmess": func() interface{} { return new(VMessInboundConfig) },
"mtproto": func() interface{} { return new(MTProtoServerConfig) },
}, "protocol", "settings")
Expand All @@ -28,8 +29,9 @@ var (
"freedom": func() interface{} { return new(FreedomConfig) },
"http": func() interface{} { return new(HttpClientConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
"vmess": func() interface{} { return new(VMessOutboundConfig) },
"socks": func() interface{} { return new(SocksClientConfig) },
"vless": func() interface{} { return new(VLessOutboundConfig) },
"vmess": func() interface{} { return new(VMessOutboundConfig) },
"mtproto": func() interface{} { return new(MTProtoClientConfig) },
"dns": func() interface{} { return new(DnsOutboundConfig) },
}, "protocol", "settings")
Expand Down
140 changes: 140 additions & 0 deletions infra/conf/vless.go
@@ -0,0 +1,140 @@
package conf

import (
"encoding/json"

"github.com/golang/protobuf/proto"

"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy/vless"
"v2ray.com/core/proxy/vless/inbound"
"v2ray.com/core/proxy/vless/outbound"
)

type VLessInboundFallback struct {
Addr *Address `json:"addr"`
Port uint16 `json:"port"`
Unix string `json:"unix"`
}

type VLessInboundConfig struct {
Users []json.RawMessage `json:"clients"`
Decryption string `json:"decryption"`
Fallback *VLessInboundFallback `json:"fallback"`
}

// Build implements Buildable
func (c *VLessInboundConfig) Build() (proto.Message, error) {

config := new(inbound.Config)

if c.Decryption != "none" {
return nil, newError(`please add/set "decryption":"none" directly to every VLESS "settings"`)
}
config.Decryption = c.Decryption

if c.Fallback != nil {
if c.Fallback.Unix != "" {
if c.Fallback.Unix[0] == '@' {
c.Fallback.Unix = "\x00" + c.Fallback.Unix[1:]
}
} else {
if c.Fallback.Port == 0 {
return nil, newError(`please fill in a valid value for "port" in VLESS "fallback"`)
}
}
if c.Fallback.Addr == nil {
c.Fallback.Addr = &Address{
Address: net.ParseAddress("127.0.0.1"),
}
}
config.Fallback = &inbound.Fallback{
Addr: c.Fallback.Addr.Build(),
Port: uint32(c.Fallback.Port),
Unix: c.Fallback.Unix,
}
}

config.User = make([]*protocol.User, len(c.Users))
for idx, rawData := range c.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawData, user); err != nil {
return nil, newError("invalid VLESS user").Base(err)
}
account := new(vless.Account)
if err := json.Unmarshal(rawData, account); err != nil {
return nil, newError("invalid VLESS user").Base(err)
}

if account.Schedulers != "" {
return nil, newError(`VLESS attr "schedulers" is not available in this version`)
}
if account.Encryption != "" {
return nil, newError(`VLESS attr "encryption" should not in inbound settings`)
}

user.Account = serial.ToTypedMessage(account)
config.User[idx] = user
}

return config, nil
}

type VLessOutboundTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Users []json.RawMessage `json:"users"`
}

type VLessOutboundConfig struct {
Receivers []*VLessOutboundTarget `json:"vnext"`
}

// Build implements Buildable
func (c *VLessOutboundConfig) Build() (proto.Message, error) {

config := new(outbound.Config)

if len(c.Receivers) == 0 {
return nil, newError("0 VLESS receiver configured")
}
serverSpecs := make([]*protocol.ServerEndpoint, len(c.Receivers))
for idx, rec := range c.Receivers {
if len(rec.Users) == 0 {
return nil, newError("0 user configured for VLESS outbound")
}
if rec.Address == nil {
return nil, newError("address is not set in VLESS outbound config")
}
spec := &protocol.ServerEndpoint{
Address: rec.Address.Build(),
Port: uint32(rec.Port),
}
for _, rawUser := range rec.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, newError("invalid VLESS user").Base(err)
}
account := new(vless.Account)
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, newError("invalid VLESS user").Base(err)
}

if account.Schedulers != "" {
return nil, newError(`VLESS attr "schedulers" is not available in this version`)
}
if account.Encryption != "none" {
return nil, newError(`please add/set "encryption":"none" for every VLESS user in "users"`)
}

user.Account = serial.ToTypedMessage(account)
spec.User = append(spec.User, user)
}
serverSpecs[idx] = spec
}
config.Receiver = serverSpecs

return config, nil
}
110 changes: 110 additions & 0 deletions infra/conf/vless_test.go
@@ -0,0 +1,110 @@
package conf_test

import (
"testing"

"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
. "v2ray.com/core/infra/conf"
"v2ray.com/core/proxy/vless"
"v2ray.com/core/proxy/vless/inbound"
"v2ray.com/core/proxy/vless/outbound"
)

func TestVLessOutbound(t *testing.T) {
creator := func() Buildable {
return new(VLessOutboundConfig)
}

runMultiTestCase(t, []TestCase{
{
Input: `{
"vnext": [{
"address": "example.com",
"port": 443,
"users": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"schedulers": "",
"encryption": "none",
"level": 0
}
]
}]
}`,
Parser: loadJSON(creator),
Output: &outbound.Config{
Receiver: []*protocol.ServerEndpoint{
{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Domain{
Domain: "example.com",
},
},
Port: 443,
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
Schedulers: "",
Encryption: "none",
}),
Level: 0,
},
},
},
},
},
},
})
}

func TestVLessInbound(t *testing.T) {
creator := func() Buildable {
return new(VLessInboundConfig)
}

runMultiTestCase(t, []TestCase{
{
Input: `{
"clients": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"schedulers": "",
"level": 0,
"email": "love@v2fly.org"
}
],
"decryption": "none",
"fallback": {
"port": 80,
"unix": "@/dev/shm/domain.socket"
}
}`,
Parser: loadJSON(creator),
Output: &inbound.Config{
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
Schedulers: "",
}),
Level: 0,
Email: "love@v2fly.org",
},
},
Decryption: "none",
Fallback: &inbound.Fallback{
Addr: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: 80,
Unix: "\x00/dev/shm/domain.socket",
},
},
},
})
}
2 changes: 2 additions & 0 deletions main/distro/all/all.go
Expand Up @@ -31,6 +31,8 @@ import (
_ "v2ray.com/core/proxy/mtproto"
_ "v2ray.com/core/proxy/shadowsocks"
_ "v2ray.com/core/proxy/socks"
_ "v2ray.com/core/proxy/vless/inbound"
_ "v2ray.com/core/proxy/vless/outbound"
_ "v2ray.com/core/proxy/vmess/inbound"
_ "v2ray.com/core/proxy/vmess/outbound"

Expand Down
40 changes: 40 additions & 0 deletions proxy/vless/account.go
@@ -0,0 +1,40 @@
// +build !confonly

package vless

import (
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/uuid"
)

// AsAccount implements protocol.Account.AsAccount().
func (a *Account) AsAccount() (protocol.Account, error) {
id, err := uuid.ParseString(a.Id)
if err != nil {
return nil, newError("failed to parse ID").Base(err).AtError()
}
return &MemoryAccount{
ID: protocol.NewID(id),
Schedulers: a.Schedulers, // needs parser here?
Encryption: a.Encryption, // needs parser here?
}, nil
}

// MemoryAccount is an in-memory form of VLess account.
type MemoryAccount struct {
// ID of the account.
ID *protocol.ID
// Schedulers of the account.
Schedulers string
// Encryption of the account. Used for client connections, and only accepts "none" for now.
Encryption string
}

// Equals implements protocol.Account.Equals().
func (a *MemoryAccount) Equals(account protocol.Account) bool {
vlessAccount, ok := account.(*MemoryAccount)
if !ok {
return false
}
return a.ID.Equals(vlessAccount.ID)
}

0 comments on commit 541fb5c

Please sign in to comment.