Browse Source

VLESS PREVIEW 1.1

pull/2667/head
RPRX 4 years ago committed by GitHub
parent
commit
bad7e2cfd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      infra/conf/v2ray.go
  2. 140
      infra/conf/vless.go
  3. 110
      infra/conf/vless_test.go
  4. 2
      main/distro/all/all.go
  5. 40
      proxy/vless/account.go
  6. 175
      proxy/vless/account.pb.go
  7. 16
      proxy/vless/account.proto
  8. 83
      proxy/vless/encoding/addons.go
  9. 386
      proxy/vless/encoding/addons.pb.go
  10. 12
      proxy/vless/encoding/addons.proto
  11. 173
      proxy/vless/encoding/encoding.go
  12. 126
      proxy/vless/encoding/encoding_test.go
  13. 9
      proxy/vless/encoding/errors.generated.go
  14. 9
      proxy/vless/errors.generated.go
  15. 3
      proxy/vless/inbound/config.go
  16. 275
      proxy/vless/inbound/config.pb.go
  17. 23
      proxy/vless/inbound/config.proto
  18. 9
      proxy/vless/inbound/errors.generated.go
  19. 308
      proxy/vless/inbound/inbound.go
  20. 3
      proxy/vless/outbound/config.go
  21. 164
      proxy/vless/outbound/config.pb.go
  22. 13
      proxy/vless/outbound/config.proto
  23. 9
      proxy/vless/outbound/errors.generated.go
  24. 177
      proxy/vless/outbound/outbound.go
  25. 50
      proxy/vless/validator.go
  26. 8
      proxy/vless/vless.go

4
infra/conf/v2ray.go

@ -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")
@ -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")

140
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
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
main/distro/all/all.go

@ -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"

40
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)
}

175
proxy/vless/account.pb.go

@ -0,0 +1,175 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.12.3
// source: v2ray.com/core/proxy/vless/account.proto
package vless
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Account struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// Schedulers settings.
Schedulers string `protobuf:"bytes,2,opt,name=schedulers,proto3" json:"schedulers,omitempty"`
// Encryption settings. Only applies to client side, and only accepts "none" for now.
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
}
func (x *Account) Reset() {
*x = Account{}
if protoimpl.UnsafeEnabled {
mi := &file_v2ray_com_core_proxy_vless_account_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Account) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Account) ProtoMessage() {}
func (x *Account) ProtoReflect() protoreflect.Message {
mi := &file_v2ray_com_core_proxy_vless_account_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Account.ProtoReflect.Descriptor instead.
func (*Account) Descriptor() ([]byte, []int) {
return file_v2ray_com_core_proxy_vless_account_proto_rawDescGZIP(), []int{0}
}
func (x *Account) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *Account) GetSchedulers() string {
if x != nil {
return x.Schedulers
}
return ""
}
func (x *Account) GetEncryption() string {
if x != nil {
return x.Encryption
}
return ""
}
var File_v2ray_com_core_proxy_vless_account_proto protoreflect.FileDescriptor
var file_v2ray_com_core_proxy_vless_account_proto_rawDesc = []byte{
0x0a, 0x28, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x76, 0x32, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65,
0x73, 0x73, 0x22, 0x59, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a,
0x0a, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a,
0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x3e, 0x0a,
0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x05, 0x76,
0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x16, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72,
0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_v2ray_com_core_proxy_vless_account_proto_rawDescOnce sync.Once
file_v2ray_com_core_proxy_vless_account_proto_rawDescData = file_v2ray_com_core_proxy_vless_account_proto_rawDesc
)
func file_v2ray_com_core_proxy_vless_account_proto_rawDescGZIP() []byte {
file_v2ray_com_core_proxy_vless_account_proto_rawDescOnce.Do(func() {
file_v2ray_com_core_proxy_vless_account_proto_rawDescData = protoimpl.X.CompressGZIP(file_v2ray_com_core_proxy_vless_account_proto_rawDescData)
})
return file_v2ray_com_core_proxy_vless_account_proto_rawDescData
}
var file_v2ray_com_core_proxy_vless_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_v2ray_com_core_proxy_vless_account_proto_goTypes = []interface{}{
(*Account)(nil), // 0: v2ray.core.proxy.vless.Account
}
var file_v2ray_com_core_proxy_vless_account_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_v2ray_com_core_proxy_vless_account_proto_init() }
func file_v2ray_com_core_proxy_vless_account_proto_init() {
if File_v2ray_com_core_proxy_vless_account_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_v2ray_com_core_proxy_vless_account_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Account); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_v2ray_com_core_proxy_vless_account_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_v2ray_com_core_proxy_vless_account_proto_goTypes,
DependencyIndexes: file_v2ray_com_core_proxy_vless_account_proto_depIdxs,
MessageInfos: file_v2ray_com_core_proxy_vless_account_proto_msgTypes,
}.Build()
File_v2ray_com_core_proxy_vless_account_proto = out.File
file_v2ray_com_core_proxy_vless_account_proto_rawDesc = nil
file_v2ray_com_core_proxy_vless_account_proto_goTypes = nil
file_v2ray_com_core_proxy_vless_account_proto_depIdxs = nil
}

16
proxy/vless/account.proto

@ -0,0 +1,16 @@
syntax = "proto3";
package v2ray.core.proxy.vless;
option csharp_namespace = "V2Ray.Core.Proxy.Vless";
option go_package = "vless";
option java_package = "com.v2ray.core.proxy.vless";
option java_multiple_files = true;
message Account {
// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
string id = 1;
// Schedulers settings.
string schedulers = 2;
// Encryption settings. Only applies to client side, and only accepts "none" for now.
string encryption = 3;
}

83
proxy/vless/encoding/addons.go

@ -0,0 +1,83 @@
// +build !confonly
package encoding
import (
"io"
"github.com/golang/protobuf/proto"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/protocol"
)
func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error {
switch addons.Scheduler {
default:
if err := buffer.WriteByte(0); err != nil {
return newError("failed to write addons protobuf length").Base(err).AtWarning()
}
}
return nil
}
func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) {
addons := new(Addons)
buffer.Clear()
if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
return nil, newError("failed to read addons protobuf length").Base(err).AtWarning()
}
if length := int32(buffer.Byte(0)); length != 0 {
buffer.Clear()
if _, err := buffer.ReadFullFrom(reader, length); err != nil {
return nil, newError("failed to read addons protobuf value").Base(err).AtWarning()
}
if err := proto.Unmarshal(buffer.Bytes(), addons); err != nil {
return nil, newError("failed to unmarshal addons protobuf value").Base(err).AtWarning()
}
// Verification.
switch addons.Scheduler {
default:
}
}
return addons, nil
}
// EncodeBodyAddons returns a Writer that auto-encrypt content written by caller.
func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, addons *Addons) buf.Writer {
switch addons.Scheduler {
default:
return buf.NewWriter(writer)
}
}
// DecodeBodyAddons returns a Reader from which caller can fetch decrypted body.
func DecodeBodyAddons(reader io.Reader, request *protocol.RequestHeader, addons *Addons) buf.Reader {
switch addons.Scheduler {
default:
return buf.NewReader(reader)
}
}

386
proxy/vless/encoding/addons.pb.go

@ -0,0 +1,386 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: v2ray.com/core/proxy/vless/encoding/addons.proto
package encoding
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Addons struct {
Scheduler string `protobuf:"bytes,1,opt,name=Scheduler,proto3" json:"Scheduler,omitempty"`
SchedulerV []byte `protobuf:"bytes,2,opt,name=SchedulerV,proto3" json:"SchedulerV,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Addons) Reset() { *m = Addons{} }
func (m *Addons) String() string { return proto.CompactTextString(m) }
func (*Addons) ProtoMessage() {}
func (*Addons) Descriptor() ([]byte, []int) {
return fileDescriptor_d597c8244066ecf1, []int{0}
}
func (m *Addons) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Addons) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Addons.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Addons) XXX_Merge(src proto.Message) {
xxx_messageInfo_Addons.Merge(m, src)
}
func (m *Addons) XXX_Size() int {
return m.Size()
}
func (m *Addons) XXX_DiscardUnknown() {
xxx_messageInfo_Addons.DiscardUnknown(m)
}
var xxx_messageInfo_Addons proto.InternalMessageInfo
func (m *Addons) GetScheduler() string {
if m != nil {
return m.Scheduler
}
return ""
}
func (m *Addons) GetSchedulerV() []byte {
if m != nil {
return m.SchedulerV
}
return nil
}
func init() {
proto.RegisterType((*Addons)(nil), "v2ray.core.proxy.vless.encoding.Addons")
}
func init() {
proto.RegisterFile("v2ray.com/core/proxy/vless/encoding/addons.proto", fileDescriptor_d597c8244066ecf1)
}
var fileDescriptor_d597c8244066ecf1 = []byte{
// 193 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x28, 0x33, 0x2a, 0x4a,
0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0x28, 0xca, 0xaf, 0xa8,
0xd4, 0x2f, 0xcb, 0x49, 0x2d, 0x2e, 0xd6, 0x4f, 0xcd, 0x4b, 0xce, 0x4f, 0xc9, 0xcc, 0x4b, 0xd7,
0x4f, 0x4c, 0x49, 0xc9, 0xcf, 0x2b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x87, 0xe9,
0x28, 0x4a, 0xd5, 0x03, 0xab, 0xd6, 0x03, 0xab, 0xd6, 0x83, 0xa9, 0x56, 0x72, 0xe3, 0x62, 0x73,
0x04, 0x6b, 0x10, 0x92, 0xe1, 0xe2, 0x0c, 0x4e, 0xce, 0x48, 0x4d, 0x29, 0xcd, 0x49, 0x2d, 0x92,
0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x42, 0x08, 0x08, 0xc9, 0x71, 0x71, 0xc1, 0x39, 0x61, 0x12,
0x4c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x48, 0x22, 0x4e, 0xc9, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78,
0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x8c, 0xc7, 0x72, 0x0c, 0x5c, 0xca, 0xc9, 0xf9, 0xb9,
0x7a, 0x04, 0xac, 0x0f, 0x60, 0x8c, 0xe2, 0x80, 0xb1, 0x57, 0x31, 0xc9, 0x87, 0x19, 0x05, 0x25,
0x56, 0xea, 0x39, 0x83, 0x54, 0x07, 0x80, 0x55, 0x87, 0x81, 0x55, 0xbb, 0x42, 0x55, 0x24, 0xb1,
0x81, 0x3d, 0x65, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x78, 0xd4, 0xe2, 0x08, 0x01, 0x00,
0x00,
}
func (m *Addons) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Addons) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Addons) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.SchedulerV) > 0 {
i -= len(m.SchedulerV)
copy(dAtA[i:], m.SchedulerV)
i = encodeVarintAddons(dAtA, i, uint64(len(m.SchedulerV)))
i--
dAtA[i] = 0x12
}
if len(m.Scheduler) > 0 {
i -= len(m.Scheduler)
copy(dAtA[i:], m.Scheduler)
i = encodeVarintAddons(dAtA, i, uint64(len(m.Scheduler)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintAddons(dAtA []byte, offset int, v uint64) int {
offset -= sovAddons(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Addons) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Scheduler)
if l > 0 {
n += 1 + l + sovAddons(uint64(l))
}
l = len(m.SchedulerV)
if l > 0 {
n += 1 + l + sovAddons(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovAddons(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozAddons(x uint64) (n int) {
return sovAddons(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Addons) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAddons
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Addons: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Addons: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Scheduler", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAddons
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthAddons
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthAddons
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Scheduler = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SchedulerV", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAddons
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthAddons
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthAddons
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SchedulerV = append(m.SchedulerV[:0], dAtA[iNdEx:postIndex]...)
if m.SchedulerV == nil {
m.SchedulerV = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipAddons(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthAddons
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthAddons
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipAddons(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAddons
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAddons
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAddons
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthAddons
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupAddons
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthAddons
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthAddons = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowAddons = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupAddons = fmt.Errorf("proto: unexpected end of group")
)

12
proxy/vless/encoding/addons.proto

@ -0,0 +1,12 @@
syntax = "proto3";
package v2ray.core.proxy.vless.encoding;
option csharp_namespace = "V2Ray.Core.Proxy.Vless.Encoding";
option go_package = "encoding";
option java_package = "com.v2ray.core.proxy.vless.encoding";
option java_multiple_files = true;
message Addons {
string Scheduler = 1;
bytes SchedulerV = 2;
}

173
proxy/vless/encoding/encoding.go

@ -0,0 +1,173 @@
package encoding
import (
"io"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/proxy/vless"
)
//go:generate errorgen
const (
Version = byte(0)
)
var addrParser = protocol.NewAddressParser(
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4),
protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain),
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6),
protocol.PortThenAddress(),
)
// EncodeRequestHeader writes encoded request header into the given writer.
func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons) error {
buffer := buf.StackNew()
defer buffer.Release()
if err := buffer.WriteByte(request.Version); err != nil {
return newError("failed to write request version").Base(err).AtWarning()
}
if _, err := buffer.Write(request.User.Account.(*vless.MemoryAccount).ID.Bytes()); err != nil {
return newError("failed to write request user id").Base(err).AtWarning()
}
if err := EncodeHeaderAddons(&buffer, requestAddons); err != nil {
return newError("failed to encode request header addons").Base(err).AtWarning()
}
if err := buffer.WriteByte(byte(request.Command)); err != nil {
return newError("failed to write request command").Base(err).AtWarning()
}
if request.Command != protocol.RequestCommandMux {
if err := addrParser.WriteAddressPort(&buffer, request.Address, request.Port); err != nil {
return newError("failed to write request address and port").Base(err).AtWarning()
}
}
if _, err := writer.Write(buffer.Bytes()); err != nil {
return newError("failed to write request header").Base(err).AtWarning()
}
return nil
}
// DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream.
func DecodeRequestHeader(reader io.Reader, validator *vless.Validator) (*protocol.RequestHeader, *Addons, error, *buf.Buffer) {
buffer := buf.StackNew()
defer buffer.Release()
pre := buf.New()
if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
pre.Write(buffer.Bytes())
return nil, nil, newError("failed to read request version").Base(err).AtWarning(), pre
}
request := &protocol.RequestHeader{
Version: buffer.Byte(0),
}
pre.Write(buffer.Bytes())
switch request.Version {
case 0:
buffer.Clear()
if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil {
pre.Write(buffer.Bytes())
return nil, nil, newError("failed to read request user id").Base(err).AtWarning(), pre
}
var id [16]byte
copy(id[:], buffer.Bytes())
if request.User = validator.Get(id); request.User == nil {
pre.Write(buffer.Bytes())
return nil, nil, newError("invalid request user id").AtWarning(), pre
}
requestAddons, err := DecodeHeaderAddons(&buffer, reader)
if err != nil {
return nil, nil, newError("failed to decode request header addons").Base(err).AtWarning(), nil
}
buffer.Clear()
if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
return nil, nil, newError("failed to read request command").Base(err).AtWarning(), nil
}
request.Command = protocol.RequestCommand(buffer.Byte(0))
switch request.Command {
case protocol.RequestCommandMux:
request.Address = net.DomainAddress("v1.mux.cool")
request.Port = 0
case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
if addr, port, err := addrParser.ReadAddressPort(&buffer, reader); err == nil {
request.Address = addr
request.Port = port
}
}
if request.Address == nil {
return nil, nil, newError("invalid request address").AtWarning(), nil
}
return request, requestAddons, nil, nil
default:
return nil, nil, newError("unexpected request version").AtWarning(), pre
}
}
// EncodeResponseHeader writes encoded response header into the given writer.
func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, responseAddons *Addons) error {
buffer := buf.StackNew()
defer buffer.Release()
if err := buffer.WriteByte(request.Version); err != nil {
return newError("failed to write response version").Base(err).AtWarning()
}
if err := EncodeHeaderAddons(&buffer, responseAddons); err != nil {
return newError("failed to encode response header addons").Base(err).AtWarning()
}
if _, err := writer.Write(buffer.Bytes()); err != nil {
return newError("failed to write response header").Base(err).AtWarning()
}
return nil
}
// DecodeResponseHeader decodes and returns (if successful) a ResponseHeader from an input stream.
func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader, responseAddons *Addons) error {
buffer := buf.StackNew()
defer buffer.Release()
if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
return newError("failed to read response version").Base(err).AtWarning()
}
if buffer.Byte(0) != request.Version {
return newError("unexpected response version. Expecting ", int(request.Version), " but actually ", int(buffer.Byte(0))).AtWarning()
}
responseAddons, err := DecodeHeaderAddons(&buffer, reader)
if err != nil {
return newError("failed to decode response header addons").Base(err).AtWarning()
}
return nil
}

126
proxy/vless/encoding/encoding_test.go

@ -0,0 +1,126 @@
package encoding_test
import (
"testing"
"github.com/google/go-cmp/cmp"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/uuid"
"v2ray.com/core/proxy/vless"
. "v2ray.com/core/proxy/vless/encoding"
)
func toAccount(a *vless.Account) protocol.Account {
account, err := a.AsAccount()
common.Must(err)
return account
}
func TestRequestSerialization(t *testing.T) {
user := &protocol.MemoryUser{
Level: 0,
Email: "test@v2fly.org",
}
id := uuid.New()
account := &vless.Account{
Id: id.String(),
}
user.Account = toAccount(account)
expectedRequest := &protocol.RequestHeader{
Version: Version,
User: user,
Command: protocol.RequestCommandTCP,
Address: net.DomainAddress("www.v2fly.org"),
Port: net.Port(443),
}
expectedAddons := &Addons{}
buffer := buf.StackNew()
common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons))
Validator := new(vless.Validator)
Validator.Add(user)
actualRequest, actualAddons, err, _ := DecodeRequestHeader(&buffer, Validator)
common.Must(err)
if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" {
t.Error(r)
}
if r := cmp.Diff(actualAddons, expectedAddons); r != "" {
t.Error(r)
}
}
func TestInvalidRequest(t *testing.T) {
user := &protocol.MemoryUser{
Level: 0,
Email: "test@v2fly.org",
}
id := uuid.New()
account := &vless.Account{
Id: id.String(),
}
user.Account = toAccount(account)
expectedRequest := &protocol.RequestHeader{
Version: Version,
User: user,
Command: protocol.RequestCommand(100),
Address: net.DomainAddress("www.v2fly.org"),
Port: net.Port(443),
}
expectedAddons := &Addons{}
buffer := buf.StackNew()
common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons))
Validator := new(vless.Validator)
Validator.Add(user)
_, _, err, _ := DecodeRequestHeader(&buffer, Validator)
if err == nil {
t.Error("nil error")
}
}
func TestMuxRequest(t *testing.T) {
user := &protocol.MemoryUser{
Level: 0,
Email: "test@v2fly.org",
}
id := uuid.New()
account := &vless.Account{
Id: id.String(),
}
user.Account = toAccount(account)
expectedRequest := &protocol.RequestHeader{
Version: Version,
User: user,
Command: protocol.RequestCommandMux,
Address: net.DomainAddress("v1.mux.cool"),
}
expectedAddons := &Addons{}
buffer := buf.StackNew()
common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons))
Validator := new(vless.Validator)
Validator.Add(user)
actualRequest, actualAddons, err, _ := DecodeRequestHeader(&buffer, Validator)
common.Must(err)
if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" {
t.Error(r)
}
if r := cmp.Diff(actualAddons, expectedAddons); r != "" {
t.Error(r)
}
}

9
proxy/vless/encoding/errors.generated.go

@ -0,0 +1,9 @@
package encoding
import "v2ray.com/core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

9
proxy/vless/errors.generated.go

@ -0,0 +1,9 @@
package vless
import "v2ray.com/core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

3
proxy/vless/inbound/config.go

@ -0,0 +1,3 @@
// +build !confonly
package inbound

275
proxy/vless/inbound/config.pb.go

@ -0,0 +1,275 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.12.3
// source: v2ray.com/core/proxy/vless/inbound/config.proto
package inbound
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
net "v2ray.com/core/common/net"
protocol "v2ray.com/core/common/protocol"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Fallback struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Addr *net.IPOrDomain `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"`
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
Unix string `protobuf:"bytes,3,opt,name=unix,proto3" json:"unix,omitempty"`
}
func (x *Fallback) Reset() {
*x = Fallback{}
if protoimpl.UnsafeEnabled {
mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Fallback) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Fallback) ProtoMessage() {}
func (x *Fallback) ProtoReflect() protoreflect.Message {
mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Fallback.ProtoReflect.Descriptor instead.
func (*Fallback) Descriptor() ([]byte, []int) {
return file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{0}
}
func (x *Fallback) GetAddr() *net.IPOrDomain {
if x != nil {
return x.Addr
}
return nil
}
func (x *Fallback) GetPort() uint32 {
if x != nil {
return x.Port
}
return 0
}
func (x *Fallback) GetUnix() string {
if x != nil {
return x.Unix
}
return ""
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"`
// Decryption settings. Only applies to server side, and only accepts "none" for now.
Decryption string `protobuf:"bytes,2,opt,name=decryption,proto3" json:"decryption,omitempty"`
Fallback *Fallback `protobuf:"bytes,3,opt,name=fallback,proto3" json:"fallback,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{1}
}
func (x *Config) GetUser() []*protocol.User {
if x != nil {
return x.User
}
return nil
}
func (x *Config) GetDecryption() string {
if x != nil {
return x.Decryption
}
return ""
}
func (x *Config) GetFallback() *Fallback {
if x != nil {
return x.Fallback
}
return nil
}
var File_v2ray_com_core_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
var file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc = []byte{
0x0a, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x1e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x1a, 0x27, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72,
0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x29, 0x76, 0x32, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x69, 0x0a, 0x08, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
0x6b, 0x12, 0x35, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04,
0x75, 0x6e, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x78,
0x22, 0xa4, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x75,
0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x32, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65,
0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x44, 0x0a, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x08, 0x66,
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x50, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x76,
0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a,
0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x1e, 0x56, 0x32, 0x52, 0x61, 0x79,
0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73,
0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescOnce sync.Once
file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData = file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc
)
func file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescGZIP() []byte {
file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescOnce.Do(func() {
file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData)
})
return file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData
}
var file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_v2ray_com_core_proxy_vless_inbound_config_proto_goTypes = []interface{}{
(*Fallback)(nil), // 0: v2ray.core.proxy.vless.inbound.Fallback
(*Config)(nil), // 1: v2ray.core.proxy.vless.inbound.Config
(*net.IPOrDomain)(nil), // 2: v2ray.core.common.net.IPOrDomain
(*protocol.User)(nil), // 3: v2ray.core.common.protocol.User
}
var file_v2ray_com_core_proxy_vless_inbound_config_proto_depIdxs = []int32{
2, // 0: v2ray.core.proxy.vless.inbound.Fallback.addr:type_name -> v2ray.core.common.net.IPOrDomain
3, // 1: v2ray.core.proxy.vless.inbound.Config.user:type_name -> v2ray.core.common.protocol.User
0, // 2: v2ray.core.proxy.vless.inbound.Config.fallback:type_name -> v2ray.core.proxy.vless.inbound.Fallback
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_v2ray_com_core_proxy_vless_inbound_config_proto_init() }
func file_v2ray_com_core_proxy_vless_inbound_config_proto_init() {
if File_v2ray_com_core_proxy_vless_inbound_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Fallback); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_v2ray_com_core_proxy_vless_inbound_config_proto_goTypes,
DependencyIndexes: file_v2ray_com_core_proxy_vless_inbound_config_proto_depIdxs,
MessageInfos: file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes,
}.Build()
File_v2ray_com_core_proxy_vless_inbound_config_proto = out.File
file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc = nil
file_v2ray_com_core_proxy_vless_inbound_config_proto_goTypes = nil
file_v2ray_com_core_proxy_vless_inbound_config_proto_depIdxs = nil
}

23
proxy/vless/inbound/config.proto

@ -0,0 +1,23 @@
syntax = "proto3";
package v2ray.core.proxy.vless.inbound;
option csharp_namespace = "V2Ray.Core.Proxy.Vless.Inbound";
option go_package = "inbound";
option java_package = "com.v2ray.core.proxy.vless.inbound";
option java_multiple_files = true;
import "v2ray.com/core/common/net/address.proto";
import "v2ray.com/core/common/protocol/user.proto";
message Fallback {
v2ray.core.common.net.IPOrDomain addr = 1;
uint32 port = 2;
string unix = 3;
}
message Config {
repeated v2ray.core.common.protocol.User user = 1;
// Decryption settings. Only applies to server side, and only accepts "none" for now.
string decryption = 2;
Fallback fallback = 3;
}

9
proxy/vless/inbound/errors.generated.go

@ -0,0 +1,9 @@
package inbound
import "v2ray.com/core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

308
proxy/vless/inbound/inbound.go

@ -0,0 +1,308 @@
// +build !confonly
package inbound
//go:generate errorgen
import (
"context"
"io"
"strconv"
"time"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/log"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/retry"
"v2ray.com/core/common/session"
"v2ray.com/core/common/signal"
"v2ray.com/core/common/task"
"v2ray.com/core/features/dns"
feature_inbound "v2ray.com/core/features/inbound"
"v2ray.com/core/features/policy"
"v2ray.com/core/features/routing"
"v2ray.com/core/proxy/vless"
"v2ray.com/core/proxy/vless/encoding"
"v2ray.com/core/transport/internet"
)
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
var dc dns.Client
if err := core.RequireFeatures(ctx, func(d dns.Client) error {
dc = d
return nil
}); err != nil {
return nil, err
}
return New(ctx, config.(*Config), dc)
}))
}
// Handler is an inbound connection handler that handles messages in VLess protocol.
type Handler struct {
inboundHandlerManager feature_inbound.Manager
policyManager policy.Manager
validator *vless.Validator
dns dns.Client
fallback *Fallback // or nil
addrport string
}
// New creates a new VLess inbound handler.
func New(ctx context.Context, config *Config, dc dns.Client) (*Handler, error) {
v := core.MustFromContext(ctx)
handler := &Handler{
inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager),
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
validator: new(vless.Validator),
dns: dc,
}
for _, user := range config.User {
u, err := user.ToMemoryUser()
if err != nil {
return nil, newError("failed to get VLESS user").Base(err).AtError()
}
if err := handler.AddUser(ctx, u); err != nil {
return nil, newError("failed to initiate user").Base(err).AtError()
}
}
if config.Fallback != nil {
handler.fallback = config.Fallback
handler.addrport = handler.fallback.Addr.AsAddress().String() + ":" + strconv.Itoa(int(handler.fallback.Port))
}
return handler, nil
}
// Close implements common.Closable.Close().
func (h *Handler) Close() error {
return errors.Combine(common.Close(h.validator))
}
// AddUser implements proxy.UserManager.AddUser().
func (h *Handler) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
return h.validator.Add(u)
}
// RemoveUser implements proxy.UserManager.RemoveUser().
func (h *Handler) RemoveUser(ctx context.Context, e string) error {
return h.validator.Del(e)
}
// Network implements proxy.Inbound.Network().
func (*Handler) Network() []net.Network {
return []net.Network{net.Network_TCP}
}
// Process implements proxy.Inbound.Process().
func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher routing.Dispatcher) error {
sessionPolicy := h.policyManager.ForLevel(0)
if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil {
return newError("unable to set read deadline").Base(err).AtWarning()
}
first := buf.New()
first.ReadFrom(connection)
sid := session.ExportIDToError(ctx)
newError("firstLen = ", first.Len()).AtInfo().WriteToLog(sid)
reader := &buf.BufferedReader{
Reader: buf.NewReader(connection),
Buffer: buf.MultiBuffer{first},
}
var request *protocol.RequestHeader
var requestAddons *encoding.Addons
var err error
var pre *buf.Buffer
if h.fallback != nil && first.Len() < 18 {
err = newError("fallback directly")
pre = buf.New()
} else {
request, requestAddons, err, pre = encoding.DecodeRequestHeader(reader, h.validator)
}
if err != nil {
if h.fallback != nil && pre != nil {
newError("fallback starts").AtInfo().WriteToLog(sid)
var conn net.Conn
if err := retry.ExponentialBackoff(5, 100).On(func() error {
var dialer net.Dialer
var err error
if h.fallback.Unix != "" {
conn, err = dialer.DialContext(ctx, "unix", h.fallback.Unix)
} else {
conn, err = dialer.DialContext(ctx, "tcp", h.addrport)
}
if err != nil {
return err
}
return nil
}); err != nil {
return newError("failed to fallback connection").Base(err).AtWarning()
}
defer conn.Close() // nolint: errcheck
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
writer := buf.NewWriter(connection)
serverReader := buf.NewReader(conn)
serverWriter := buf.NewWriter(conn)
postRequest := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
if pre.Len() > 0 {
if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pre}); err != nil {
return newError("failed to fallback request pre").Base(err).AtWarning()
}
}
if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil {
return err // ...
}
return nil
}
getResponse := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil {
return err // ...
}
return nil
}
if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), task.OnSuccess(getResponse, task.Close(writer))); err != nil {
common.Interrupt(serverReader)
common.Interrupt(serverWriter)
return newError("fallback ends").Base(err).AtInfo()
}
return nil
}
if errors.Cause(err) != io.EOF {
log.Record(&log.AccessMessage{
From: connection.RemoteAddr(),
To: "",
Status: log.AccessRejected,
Reason: err,
})
err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtWarning()
}
return err
}
if request.Command != protocol.RequestCommandMux {
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
From: connection.RemoteAddr(),
To: request.Destination(),
Status: log.AccessAccepted,
Reason: "",
Email: request.User.Email,
})
}
newError("received request for ", request.Destination()).AtInfo().WriteToLog(sid)
if err := connection.SetReadDeadline(time.Time{}); err != nil {
newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid)
}
inbound := session.InboundFromContext(ctx)
if inbound == nil {
panic("no inbound metadata")
}
inbound.User = request.User
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
link, err := dispatcher.Dispatch(ctx, request.Destination())
if err != nil {
return newError("failed to dispatch request to ", request.Destination()).Base(err).AtWarning()
}
serverReader := link.Reader
serverWriter := link.Writer
postRequest := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
// default: clientReader := reader
clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons)
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transfer request payload").Base(err).AtInfo()
}
return nil
}
getResponse := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
responseAddons := &encoding.Addons{
Scheduler: requestAddons.Scheduler,
}
bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection))
if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil {
return newError("failed to encode response header").Base(err).AtWarning()
}
// default: clientWriter := bufferWriter
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons)
{
multiBuffer, err := serverReader.ReadMultiBuffer()
if err != nil {
return err // ...
}
if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil {
return err // ...
}
}
// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
if err := bufferWriter.SetBuffered(false); err != nil {
return newError("failed to write A response payload").Base(err).AtWarning()
}
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transfer response payload").Base(err).AtInfo()
}
// Indicates the end of response payload.
switch responseAddons.Scheduler {
default:
}
return nil
}
if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), getResponse); err != nil {
common.Interrupt(serverReader)
common.Interrupt(serverWriter)
return newError("connection ends").Base(err).AtInfo()
}
return nil
}

3
proxy/vless/outbound/config.go

@ -0,0 +1,3 @@
// +build !confonly
package outbound

164
proxy/vless/outbound/config.pb.go

@ -0,0 +1,164 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.12.3
// source: v2ray.com/core/proxy/vless/outbound/config.proto
package outbound
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
protocol "v2ray.com/core/common/protocol"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Receiver []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=receiver,proto3" json:"receiver,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetReceiver() []*protocol.ServerEndpoint {
if x != nil {
return x.Receiver
}
return nil
}
var File_v2ray_com_core_proxy_vless_outbound_config_proto protoreflect.FileDescriptor
var file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc = []byte{
0x0a, 0x30, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x6f, 0x75, 0x74,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x1f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x6f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x1a, 0x30, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x63, 0x6f, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x46, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x72,
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x42, 0x53, 0x0a, 0x23, 0x63, 0x6f, 0x6d, 0x2e, 0x76,
0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01,
0x5a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x1f, 0x56, 0x32, 0x52,
0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c,
0x65, 0x73, 0x73, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescOnce sync.Once
file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData = file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc
)
func file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescGZIP() []byte {
file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescOnce.Do(func() {
file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData)
})
return file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData
}
var file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_v2ray_com_core_proxy_vless_outbound_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: v2ray.core.proxy.vless.outbound.Config
(*protocol.ServerEndpoint)(nil), // 1: v2ray.core.common.protocol.ServerEndpoint
}
var file_v2ray_com_core_proxy_vless_outbound_config_proto_depIdxs = []int32{
1, // 0: v2ray.core.proxy.vless.outbound.Config.receiver:type_name -> v2ray.core.common.protocol.ServerEndpoint
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_v2ray_com_core_proxy_vless_outbound_config_proto_init() }
func file_v2ray_com_core_proxy_vless_outbound_config_proto_init() {
if File_v2ray_com_core_proxy_vless_outbound_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_v2ray_com_core_proxy_vless_outbound_config_proto_goTypes,
DependencyIndexes: file_v2ray_com_core_proxy_vless_outbound_config_proto_depIdxs,
MessageInfos: file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes,
}.Build()
File_v2ray_com_core_proxy_vless_outbound_config_proto = out.File
file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc = nil
file_v2ray_com_core_proxy_vless_outbound_config_proto_goTypes = nil
file_v2ray_com_core_proxy_vless_outbound_config_proto_depIdxs = nil
}

13
proxy/vless/outbound/config.proto

@ -0,0 +1,13 @@
syntax = "proto3";
package v2ray.core.proxy.vless.outbound;
option csharp_namespace = "V2Ray.Core.Proxy.Vless.Outbound";
option go_package = "outbound";
option java_package = "com.v2ray.core.proxy.vless.outbound";
option java_multiple_files = true;
import "v2ray.com/core/common/protocol/server_spec.proto";
message Config {
repeated v2ray.core.common.protocol.ServerEndpoint receiver = 1;
}

9
proxy/vless/outbound/errors.generated.go

@ -0,0 +1,9 @@
package outbound
import "v2ray.com/core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

177
proxy/vless/outbound/outbound.go

@ -0,0 +1,177 @@
// +build !confonly
package outbound
//go:generate errorgen
import (
"context"
"time"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/retry"
"v2ray.com/core/common/session"
"v2ray.com/core/common/signal"
"v2ray.com/core/common/task"
"v2ray.com/core/features/policy"
"v2ray.com/core/proxy/vless"
"v2ray.com/core/proxy/vless/encoding"
"v2ray.com/core/transport"
"v2ray.com/core/transport/internet"
)
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config))
}))
}
// Handler is an outbound connection handler for VLess protocol.
type Handler struct {
serverList *protocol.ServerList
serverPicker protocol.ServerPicker
policyManager policy.Manager
}
// New creates a new VLess outbound handler.
func New(ctx context.Context, config *Config) (*Handler, error) {
serverList := protocol.NewServerList()
for _, rec := range config.Receiver {
s, err := protocol.NewServerSpecFromPB(*rec)
if err != nil {
return nil, newError("failed to parse server spec").Base(err).AtError()
}
serverList.AddServer(s)
}
v := core.MustFromContext(ctx)
handler := &Handler{
serverList: serverList,
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
}
return handler, nil
}
// Process implements proxy.Outbound.Process().
func (v *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
var rec *protocol.ServerSpec
var conn internet.Connection
if err := retry.ExponentialBackoff(5, 200).On(func() error {
rec = v.serverPicker.PickServer()
var err error
conn, err = dialer.Dial(ctx, rec.Destination())
if err != nil {
return err
}
return nil
}); err != nil {
return newError("failed to find an available destination").Base(err).AtWarning()
}
defer conn.Close() // nolint: errcheck
outbound := session.OutboundFromContext(ctx)
if outbound == nil || !outbound.Target.IsValid() {
return newError("target not specified").AtError()
}
target := outbound.Target
newError("tunneling request to ", target, " via ", rec.Destination()).AtInfo().WriteToLog(session.ExportIDToError(ctx))
command := protocol.RequestCommandTCP
if target.Network == net.Network_UDP {
command = protocol.RequestCommandUDP
}
if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" {
command = protocol.RequestCommandMux
}
request := &protocol.RequestHeader{
Version: encoding.Version,
User: rec.PickUser(),
Command: command,
Address: target.Address,
Port: target.Port,
}
account := request.User.Account.(*vless.MemoryAccount)
requestAddons := &encoding.Addons{
Scheduler: account.Schedulers,
}
sessionPolicy := v.policyManager.ForLevel(request.User.Level)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
clientReader := link.Reader
clientWriter := link.Writer
postRequest := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
if err := encoding.EncodeRequestHeader(bufferWriter, request, requestAddons); err != nil {
return newError("failed to encode request header").Base(err).AtWarning()
}
// default: serverWriter := bufferWriter
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons)
if err := buf.CopyOnceTimeout(clientReader, serverWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
return err // ...
}
// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
if err := bufferWriter.SetBuffered(false); err != nil {
return newError("failed to write A request payload").Base(err).AtWarning()
}
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transfer request payload").Base(err).AtInfo()
}
// Indicates the end of request payload.
switch requestAddons.Scheduler {
default:
}
return nil
}
getResponse := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
responseAddons := new(encoding.Addons)
if err := encoding.DecodeResponseHeader(conn, request, responseAddons); err != nil {
return newError("failed to decode response header").Base(err).AtWarning()
}
// default: serverReader := buf.NewReader(conn)
serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transfer response payload").Base(err).AtInfo()
}
return nil
}
if err := task.Run(ctx, postRequest, task.OnSuccess(getResponse, task.Close(clientWriter))); err != nil {
return newError("connection ends").Base(err).AtInfo()
}
return nil
}

50
proxy/vless/validator.go

@ -0,0 +1,50 @@
// +build !confonly
package vless
import (
"strings"
"sync"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/uuid"
)
type Validator struct {
// Considering email's usage here, map + sync.Mutex/RWMutex may have better performance.
email sync.Map
users sync.Map
}
func (v *Validator) Add(u *protocol.MemoryUser) error {
if u.Email != "" {
_, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u)
if loaded {
return newError("User ", u.Email, " already exists.")
}
}
v.users.Store(u.Account.(*MemoryAccount).ID.UUID(), u)
return nil
}
func (v *Validator) Del(e string) error {
if e == "" {
return newError("Email must not be empty.")
}
le := strings.ToLower(e)
u, _ := v.email.Load(le)
if u == nil {
return newError("User ", e, " not found.")
}
v.email.Delete(le)
v.users.Delete(u.(*protocol.MemoryUser).Account.(*MemoryAccount).ID.UUID())
return nil
}
func (v *Validator) Get(id uuid.UUID) *protocol.MemoryUser {
u, _ := v.users.Load(id)
if u != nil {
return u.(*protocol.MemoryUser)
}
return nil
}

8
proxy/vless/vless.go

@ -0,0 +1,8 @@
// Package vless contains the implementation of VLess protocol and transportation.
//
// VLess contains both inbound and outbound connections. VLess inbound is usually used on servers
// together with 'freedom' to talk to final destination, while VLess outbound is usually used on
// clients with 'socks' for proxying.
package vless
//go:generate errorgen
Loading…
Cancel
Save