Browse Source

Add trojan protocol support (#181)

* Add trojan protocol support

Co-authored-by: Eken Chan <ekenchan@msn.com>
Co-authored-by: Loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
maskedeken 5 years ago
parent
commit
e445d21f4b

+ 135 - 0
infra/conf/trojan.go

@@ -0,0 +1,135 @@
+package conf
+
+import (
+	"strconv"
+
+	"github.com/golang/protobuf/proto" // nolint: staticcheck
+	"v2ray.com/core/common/net"
+	"v2ray.com/core/common/protocol"
+	"v2ray.com/core/common/serial"
+	"v2ray.com/core/proxy/trojan"
+)
+
+// TrojanServerTarget is configuration of a single trojan server
+type TrojanServerTarget struct {
+	Address  *Address `json:"address"`
+	Port     uint16   `json:"port"`
+	Password string   `json:"password"`
+	Email    string   `json:"email"`
+	Level    byte     `json:"level"`
+}
+
+// TrojanClientConfig is configuration of trojan servers
+type TrojanClientConfig struct {
+	Servers []*TrojanServerTarget `json:"servers"`
+}
+
+// Build implements Buildable
+func (c *TrojanClientConfig) Build() (proto.Message, error) {
+	config := new(trojan.ClientConfig)
+
+	if len(c.Servers) == 0 {
+		return nil, newError("0 Trojan server configured.")
+	}
+
+	serverSpecs := make([]*protocol.ServerEndpoint, len(c.Servers))
+	for idx, rec := range c.Servers {
+		if rec.Address == nil {
+			return nil, newError("Trojan server address is not set.")
+		}
+		if rec.Port == 0 {
+			return nil, newError("Invalid Trojan port.")
+		}
+		if rec.Password == "" {
+			return nil, newError("Trojan password is not specified.")
+		}
+		account := &trojan.Account{
+			Password: rec.Password,
+		}
+		trojan := &protocol.ServerEndpoint{
+			Address: rec.Address.Build(),
+			Port:    uint32(rec.Port),
+			User: []*protocol.User{
+				{
+					Level:   uint32(rec.Level),
+					Email:   rec.Email,
+					Account: serial.ToTypedMessage(account),
+				},
+			},
+		}
+
+		serverSpecs[idx] = trojan
+	}
+
+	config.Server = serverSpecs
+
+	return config, nil
+}
+
+// TrojanInboundFallback is fallback configuration
+type TrojanInboundFallback struct {
+	Type string `json:"type"`
+	Dest string `json:"dest"`
+}
+
+// TrojanUserConfig is user configuration
+type TrojanUserConfig struct {
+	Password string `json:"password"`
+	Level    byte   `json:"level"`
+	Email    string `json:"email"`
+}
+
+// TrojanServerConfig is Inbound configuration
+type TrojanServerConfig struct {
+	Clients  []*TrojanUserConfig    `json:"clients"`
+	Fallback *TrojanInboundFallback `json:"fallback"`
+}
+
+// Build implements Buildable
+func (c *TrojanServerConfig) Build() (proto.Message, error) {
+	config := new(trojan.ServerConfig)
+
+	if len(c.Clients) == 0 {
+		return nil, newError("No trojan user settings.")
+	}
+
+	config.Users = make([]*protocol.User, len(c.Clients))
+	for idx, rawUser := range c.Clients {
+		user := new(protocol.User)
+		account := &trojan.Account{
+			Password: rawUser.Password,
+		}
+
+		user.Email = rawUser.Email
+		user.Level = uint32(rawUser.Level)
+		user.Account = serial.ToTypedMessage(account)
+		config.Users[idx] = user
+	}
+
+	if c.Fallback != nil {
+		fb := &trojan.Fallback{
+			Dest: c.Fallback.Dest,
+		}
+
+		if fb.Type == "" && fb.Dest != "" {
+			switch fb.Dest[0] {
+			case '@', '/':
+				fb.Type = "unix"
+			default:
+				if _, err := strconv.Atoi(fb.Dest); err == nil {
+					fb.Dest = "127.0.0.1:" + fb.Dest
+				}
+				if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
+					fb.Type = "tcp"
+				}
+			}
+		}
+		if fb.Type == "" {
+			return nil, newError("please fill in a valid value for trojan fallback type")
+		}
+
+		config.Fallback = fb
+	}
+
+	return config, nil
+}

+ 2 - 0
infra/conf/v2ray.go

@@ -22,6 +22,7 @@ var (
 		"socks":         func() interface{} { return new(SocksServerConfig) },
 		"socks":         func() interface{} { return new(SocksServerConfig) },
 		"vless":         func() interface{} { return new(VLessInboundConfig) },
 		"vless":         func() interface{} { return new(VLessInboundConfig) },
 		"vmess":         func() interface{} { return new(VMessInboundConfig) },
 		"vmess":         func() interface{} { return new(VMessInboundConfig) },
+		"trojan":        func() interface{} { return new(TrojanServerConfig) },
 		"mtproto":       func() interface{} { return new(MTProtoServerConfig) },
 		"mtproto":       func() interface{} { return new(MTProtoServerConfig) },
 	}, "protocol", "settings")
 	}, "protocol", "settings")
 
 
@@ -33,6 +34,7 @@ var (
 		"socks":       func() interface{} { return new(SocksClientConfig) },
 		"socks":       func() interface{} { return new(SocksClientConfig) },
 		"vless":       func() interface{} { return new(VLessOutboundConfig) },
 		"vless":       func() interface{} { return new(VLessOutboundConfig) },
 		"vmess":       func() interface{} { return new(VMessOutboundConfig) },
 		"vmess":       func() interface{} { return new(VMessOutboundConfig) },
+		"trojan":      func() interface{} { return new(TrojanClientConfig) },
 		"mtproto":     func() interface{} { return new(MTProtoClientConfig) },
 		"mtproto":     func() interface{} { return new(MTProtoClientConfig) },
 		"dns":         func() interface{} { return new(DnsOutboundConfig) },
 		"dns":         func() interface{} { return new(DnsOutboundConfig) },
 	}, "protocol", "settings")
 	}, "protocol", "settings")

+ 1 - 0
main/distro/all/all.go

@@ -31,6 +31,7 @@ import (
 	_ "v2ray.com/core/proxy/mtproto"
 	_ "v2ray.com/core/proxy/mtproto"
 	_ "v2ray.com/core/proxy/shadowsocks"
 	_ "v2ray.com/core/proxy/shadowsocks"
 	_ "v2ray.com/core/proxy/socks"
 	_ "v2ray.com/core/proxy/socks"
+	_ "v2ray.com/core/proxy/trojan"
 	_ "v2ray.com/core/proxy/vless/inbound"
 	_ "v2ray.com/core/proxy/vless/inbound"
 	_ "v2ray.com/core/proxy/vless/outbound"
 	_ "v2ray.com/core/proxy/vless/outbound"
 	_ "v2ray.com/core/proxy/vmess/inbound"
 	_ "v2ray.com/core/proxy/vmess/inbound"

+ 146 - 0
proxy/trojan/client.go

@@ -0,0 +1,146 @@
+// +build !confonly
+
+package trojan
+
+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/transport"
+	"v2ray.com/core/transport/internet"
+)
+
+// Client is a inbound handler for trojan protocol
+type Client struct {
+	serverPicker  protocol.ServerPicker
+	policyManager policy.Manager
+}
+
+// NewClient create a new trojan client.
+func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
+	serverList := protocol.NewServerList()
+	for _, rec := range config.Server {
+		s, err := protocol.NewServerSpecFromPB(rec)
+		if err != nil {
+			return nil, newError("failed to parse server spec").Base(err)
+		}
+		serverList.AddServer(s)
+	}
+	if serverList.Size() == 0 {
+		return nil, newError("0 server")
+	}
+
+	v := core.MustFromContext(ctx)
+	client := &Client{
+		serverPicker:  protocol.NewRoundRobinServerPicker(serverList),
+		policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
+	}
+	return client, nil
+}
+
+// Process implements OutboundHandler.Process().
+func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { // nolint: funlen
+	outbound := session.OutboundFromContext(ctx)
+	if outbound == nil || !outbound.Target.IsValid() {
+		return newError("target not specified")
+	}
+	destination := outbound.Target
+	network := destination.Network
+
+	var server *protocol.ServerSpec
+	var conn internet.Connection
+
+	err := retry.ExponentialBackoff(5, 100).On(func() error { // nolint: gomnd
+		server = c.serverPicker.PickServer()
+		rawConn, err := dialer.Dial(ctx, server.Destination())
+		if err != nil {
+			return err
+		}
+
+		conn = rawConn
+		return nil
+	})
+	if err != nil {
+		return newError("failed to find an available destination").AtWarning().Base(err)
+	}
+	newError("tunneling request to ", destination, " via ", server.Destination()).WriteToLog(session.ExportIDToError(ctx))
+
+	defer conn.Close()
+
+	user := server.PickUser()
+	account, ok := user.Account.(*MemoryAccount)
+	if !ok {
+		return newError("user account is not valid")
+	}
+
+	sessionPolicy := c.policyManager.ForLevel(user.Level)
+	ctx, cancel := context.WithCancel(ctx)
+	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
+
+	postRequest := func() error {
+		defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
+
+		var bodyWriter buf.Writer
+		bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
+		connWriter := &ConnWriter{Writer: bufferWriter, Target: destination, Account: account}
+
+		if destination.Network == net.Network_UDP {
+			bodyWriter = &PacketWriter{Writer: connWriter, Target: destination}
+		} else {
+			bodyWriter = connWriter
+		}
+
+		// write some request payload to buffer
+		if err = buf.CopyOnceTimeout(link.Reader, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { // nolint: lll,gomnd
+			return newError("failed to write A reqeust payload").Base(err).AtWarning()
+		}
+
+		// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
+		if err = bufferWriter.SetBuffered(false); err != nil {
+			return newError("failed to flush payload").Base(err).AtWarning()
+		}
+
+		if err = buf.Copy(link.Reader, bodyWriter, 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)
+
+		var reader buf.Reader
+		if network == net.Network_UDP {
+			reader = &PacketReader{
+				Reader: conn,
+			}
+		} else {
+			reader = buf.NewReader(conn)
+		}
+		return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
+	}
+
+	var responseDoneAndCloseWriter = task.OnSuccess(getResponse, task.Close(link.Writer))
+	if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
+		return newError("connection ends").Base(err)
+	}
+
+	return nil
+}
+
+func init() {
+	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { // nolint: lll
+		return NewClient(ctx, config.(*ClientConfig))
+	}))
+}

+ 50 - 0
proxy/trojan/config.go

@@ -0,0 +1,50 @@
+package trojan
+
+import (
+	"crypto/sha256"
+	"encoding/hex"
+	fmt "fmt"
+
+	"v2ray.com/core/common"
+	"v2ray.com/core/common/protocol"
+)
+
+// MemoryAccount is an account type converted from Account.
+type MemoryAccount struct {
+	Password string
+	Key      []byte
+}
+
+// AsAccount implements protocol.AsAccount.
+func (a *Account) AsAccount() (protocol.Account, error) {
+	password := a.GetPassword()
+	key := hexSha224(password)
+	return &MemoryAccount{
+		Password: password,
+		Key:      key,
+	}, nil
+}
+
+// Equals implements protocol.Account.Equals().
+func (a *MemoryAccount) Equals(another protocol.Account) bool {
+	if account, ok := another.(*MemoryAccount); ok {
+		return a.Password == account.Password
+	}
+	return false
+}
+
+func hexSha224(password string) []byte {
+	buf := make([]byte, 56)
+	hash := sha256.New224()
+	common.Must2(hash.Write([]byte(password)))
+	hex.Encode(buf, hash.Sum(nil))
+	return buf
+}
+
+func hexString(data []byte) string {
+	str := ""
+	for _, v := range data {
+		str += fmt.Sprintf("%02x", v)
+	}
+	return str
+}

+ 376 - 0
proxy/trojan/config.pb.go

@@ -0,0 +1,376 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        v3.13.0
+// source: proxy/trojan/config.proto
+
+package trojan
+
+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 Account struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
+}
+
+func (x *Account) Reset() {
+	*x = Account{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proxy_trojan_config_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_proxy_trojan_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 Account.ProtoReflect.Descriptor instead.
+func (*Account) Descriptor() ([]byte, []int) {
+	return file_proxy_trojan_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Account) GetPassword() string {
+	if x != nil {
+		return x.Password
+	}
+	return ""
+}
+
+type Fallback struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
+	Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"`
+}
+
+func (x *Fallback) Reset() {
+	*x = Fallback{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proxy_trojan_config_proto_msgTypes[1]
+		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_proxy_trojan_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 Fallback.ProtoReflect.Descriptor instead.
+func (*Fallback) Descriptor() ([]byte, []int) {
+	return file_proxy_trojan_config_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Fallback) GetType() string {
+	if x != nil {
+		return x.Type
+	}
+	return ""
+}
+
+func (x *Fallback) GetDest() string {
+	if x != nil {
+		return x.Dest
+	}
+	return ""
+}
+
+type ClientConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"`
+}
+
+func (x *ClientConfig) Reset() {
+	*x = ClientConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proxy_trojan_config_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ClientConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ClientConfig) ProtoMessage() {}
+
+func (x *ClientConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_proxy_trojan_config_proto_msgTypes[2]
+	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 ClientConfig.ProtoReflect.Descriptor instead.
+func (*ClientConfig) Descriptor() ([]byte, []int) {
+	return file_proxy_trojan_config_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint {
+	if x != nil {
+		return x.Server
+	}
+	return nil
+}
+
+type ServerConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Users    []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
+	Fallback *Fallback        `protobuf:"bytes,2,opt,name=fallback,proto3" json:"fallback,omitempty"`
+}
+
+func (x *ServerConfig) Reset() {
+	*x = ServerConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proxy_trojan_config_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ServerConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ServerConfig) ProtoMessage() {}
+
+func (x *ServerConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_proxy_trojan_config_proto_msgTypes[3]
+	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 ServerConfig.ProtoReflect.Descriptor instead.
+func (*ServerConfig) Descriptor() ([]byte, []int) {
+	return file_proxy_trojan_config_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *ServerConfig) GetUsers() []*protocol.User {
+	if x != nil {
+		return x.Users
+	}
+	return nil
+}
+
+func (x *ServerConfig) GetFallback() *Fallback {
+	if x != nil {
+		return x.Fallback
+	}
+	return nil
+}
+
+var File_proxy_trojan_config_proto protoreflect.FileDescriptor
+
+var file_proxy_trojan_config_proto_rawDesc = []byte{
+	0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2f, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x76, 0x32, 0x72,
+	0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x74, 0x72,
+	0x6f, 0x6a, 0x61, 0x6e, 0x1a, 0x1a, 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,
+	0x1a, 0x21, 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, 0x25, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a,
+	0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x32, 0x0a, 0x08, 0x46, 0x61,
+	0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65,
+	0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x22, 0x52,
+	0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x42,
+	0x0a, 0x06, 0x73, 0x65, 0x72, 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, 0x06, 0x73, 0x65, 0x72, 0x76,
+	0x65, 0x72, 0x22, 0x85, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 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, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x3d, 0x0a, 0x08, 0x66,
+	0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
+	0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
+	0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
+	0x52, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x56, 0x0a, 0x1b, 0x63, 0x6f,
+	0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x78, 0x79, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x1b, 0x76, 0x32, 0x72,
+	0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78,
+	0x79, 0x2f, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0xaa, 0x02, 0x17, 0x56, 0x32, 0x52, 0x61, 0x79,
+	0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x54, 0x72, 0x6f, 0x6a,
+	0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_proxy_trojan_config_proto_rawDescOnce sync.Once
+	file_proxy_trojan_config_proto_rawDescData = file_proxy_trojan_config_proto_rawDesc
+)
+
+func file_proxy_trojan_config_proto_rawDescGZIP() []byte {
+	file_proxy_trojan_config_proto_rawDescOnce.Do(func() {
+		file_proxy_trojan_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_trojan_config_proto_rawDescData)
+	})
+	return file_proxy_trojan_config_proto_rawDescData
+}
+
+var file_proxy_trojan_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_proxy_trojan_config_proto_goTypes = []interface{}{
+	(*Account)(nil),                 // 0: v2ray.core.proxy.trojan.Account
+	(*Fallback)(nil),                // 1: v2ray.core.proxy.trojan.Fallback
+	(*ClientConfig)(nil),            // 2: v2ray.core.proxy.trojan.ClientConfig
+	(*ServerConfig)(nil),            // 3: v2ray.core.proxy.trojan.ServerConfig
+	(*protocol.ServerEndpoint)(nil), // 4: v2ray.core.common.protocol.ServerEndpoint
+	(*protocol.User)(nil),           // 5: v2ray.core.common.protocol.User
+}
+var file_proxy_trojan_config_proto_depIdxs = []int32{
+	4, // 0: v2ray.core.proxy.trojan.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint
+	5, // 1: v2ray.core.proxy.trojan.ServerConfig.users:type_name -> v2ray.core.common.protocol.User
+	1, // 2: v2ray.core.proxy.trojan.ServerConfig.fallback:type_name -> v2ray.core.proxy.trojan.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_proxy_trojan_config_proto_init() }
+func file_proxy_trojan_config_proto_init() {
+	if File_proxy_trojan_config_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_proxy_trojan_config_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
+			}
+		}
+		file_proxy_trojan_config_proto_msgTypes[1].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_proxy_trojan_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ClientConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_proxy_trojan_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ServerConfig); 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_proxy_trojan_config_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   4,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_proxy_trojan_config_proto_goTypes,
+		DependencyIndexes: file_proxy_trojan_config_proto_depIdxs,
+		MessageInfos:      file_proxy_trojan_config_proto_msgTypes,
+	}.Build()
+	File_proxy_trojan_config_proto = out.File
+	file_proxy_trojan_config_proto_rawDesc = nil
+	file_proxy_trojan_config_proto_goTypes = nil
+	file_proxy_trojan_config_proto_depIdxs = nil
+}

+ 28 - 0
proxy/trojan/config.proto

@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+package v2ray.core.proxy.trojan;
+option csharp_namespace = "V2Ray.Core.Proxy.Trojan";
+option go_package = "v2ray.com/core/proxy/trojan";
+option java_package = "com.v2ray.core.proxy.trojan";
+option java_multiple_files = true;
+
+import "common/protocol/user.proto";
+import "common/protocol/server_spec.proto";
+
+message Account {
+  string password = 1;
+}
+
+message Fallback {
+  string type = 1;
+  string dest = 2;
+}
+
+message ClientConfig {
+  repeated v2ray.core.common.protocol.ServerEndpoint server = 1;
+}
+
+message ServerConfig {
+  repeated v2ray.core.common.protocol.User users = 1;
+  Fallback fallback = 2;
+}

+ 9 - 0
proxy/trojan/errors.generated.go

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

+ 282 - 0
proxy/trojan/protocol.go

@@ -0,0 +1,282 @@
+package trojan
+
+import (
+	"encoding/binary"
+	"io"
+
+	"v2ray.com/core/common/buf"
+	"v2ray.com/core/common/net"
+	"v2ray.com/core/common/protocol"
+)
+
+var (
+	crlf = []byte{'\r', '\n'}
+
+	addrParser = protocol.NewAddressParser(
+		protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4),   // nolint: gomnd
+		protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6),   // nolint: gomnd
+		protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain), // nolint: gomnd
+	)
+)
+
+const (
+	maxLength = 8192
+
+	commandTCP byte = 1
+	commandUDP byte = 3
+)
+
+// ConnWriter is TCP Connection Writer Wrapper for trojan protocol
+type ConnWriter struct {
+	io.Writer
+	Target     net.Destination
+	Account    *MemoryAccount
+	headerSent bool
+}
+
+// Write implements io.Writer
+func (c *ConnWriter) Write(p []byte) (n int, err error) {
+	if !c.headerSent {
+		if err := c.writeHeader(); err != nil {
+			return 0, newError("failed to write request header").Base(err)
+		}
+	}
+
+	return c.Writer.Write(p)
+}
+
+// WriteMultiBuffer implements buf.Writer
+func (c *ConnWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
+	defer buf.ReleaseMulti(mb)
+
+	for _, b := range mb {
+		if !b.IsEmpty() {
+			if _, err := c.Write(b.Bytes()); err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func (c *ConnWriter) writeHeader() error {
+	buffer := buf.StackNew()
+	defer buffer.Release()
+
+	command := commandTCP
+	if c.Target.Network == net.Network_UDP {
+		command = commandUDP
+	}
+
+	if _, err := buffer.Write(c.Account.Key); err != nil {
+		return err
+	}
+	if _, err := buffer.Write(crlf); err != nil {
+		return err
+	}
+	if err := buffer.WriteByte(command); err != nil {
+		return err
+	}
+	if err := addrParser.WriteAddressPort(&buffer, c.Target.Address, c.Target.Port); err != nil {
+		return err
+	}
+	if _, err := buffer.Write(crlf); err != nil {
+		return err
+	}
+
+	_, err := c.Writer.Write(buffer.Bytes())
+	if err == nil {
+		c.headerSent = true
+	}
+
+	return err
+}
+
+// PacketWriter UDP Connection Writer Wrapper for trojan protocol
+type PacketWriter struct {
+	io.Writer
+	Target net.Destination
+}
+
+// WriteMultiBuffer implements buf.Writer
+func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
+	b := make([]byte, maxLength)
+	for !mb.IsEmpty() {
+		var length int
+		mb, length = buf.SplitBytes(mb, b)
+		if _, err := w.writePacket(b[:length], w.Target); err != nil {
+			buf.ReleaseMulti(mb)
+			return err
+		}
+	}
+
+	return nil
+}
+
+// WriteMultiBufferWithMetadata writes udp packet with destination specified
+func (w *PacketWriter) WriteMultiBufferWithMetadata(mb buf.MultiBuffer, dest net.Destination) error {
+	b := make([]byte, maxLength)
+	for !mb.IsEmpty() {
+		var length int
+		mb, length = buf.SplitBytes(mb, b)
+		if _, err := w.writePacket(b[:length], dest); err != nil {
+			buf.ReleaseMulti(mb)
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (w *PacketWriter) writePacket(payload []byte, dest net.Destination) (int, error) { // nolint: unparam
+	buffer := buf.StackNew()
+	defer buffer.Release()
+
+	length := len(payload)
+	lengthBuf := [2]byte{}
+	binary.BigEndian.PutUint16(lengthBuf[:], uint16(length))
+	if err := addrParser.WriteAddressPort(&buffer, dest.Address, dest.Port); err != nil {
+		return 0, err
+	}
+	if _, err := buffer.Write(lengthBuf[:]); err != nil {
+		return 0, err
+	}
+	if _, err := buffer.Write(crlf); err != nil {
+		return 0, err
+	}
+	if _, err := buffer.Write(payload); err != nil {
+		return 0, err
+	}
+	_, err := w.Write(buffer.Bytes())
+	if err != nil {
+		return 0, err
+	}
+
+	return length, nil
+}
+
+// ConnReader is TCP Connection Reader Wrapper for trojan protocol
+type ConnReader struct {
+	io.Reader
+	Target       net.Destination
+	headerParsed bool
+}
+
+// ParseHeader parses the trojan protocol header
+func (c *ConnReader) ParseHeader() error {
+	var crlf [2]byte
+	var command [1]byte
+	var hash [56]byte
+	if _, err := io.ReadFull(c.Reader, hash[:]); err != nil {
+		return newError("failed to read user hash").Base(err)
+	}
+
+	if _, err := io.ReadFull(c.Reader, crlf[:]); err != nil {
+		return newError("failed to read crlf").Base(err)
+	}
+
+	if _, err := io.ReadFull(c.Reader, command[:]); err != nil {
+		return newError("failed to read command").Base(err)
+	}
+
+	network := net.Network_TCP
+	if command[0] == commandUDP {
+		network = net.Network_UDP
+	}
+
+	addr, port, err := addrParser.ReadAddressPort(nil, c.Reader)
+	if err != nil {
+		return newError("failed to read address and port").Base(err)
+	}
+	c.Target = net.Destination{Network: network, Address: addr, Port: port}
+
+	if _, err := io.ReadFull(c.Reader, crlf[:]); err != nil {
+		return newError("failed to read crlf").Base(err)
+	}
+
+	c.headerParsed = true
+	return nil
+}
+
+// Read implements io.Reader
+func (c *ConnReader) Read(p []byte) (int, error) {
+	if !c.headerParsed {
+		if err := c.ParseHeader(); err != nil {
+			return 0, err
+		}
+	}
+
+	return c.Reader.Read(p)
+}
+
+// ReadMultiBuffer implements buf.Reader
+func (c *ConnReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
+	b := buf.New()
+	_, err := b.ReadFrom(c)
+	return buf.MultiBuffer{b}, err
+}
+
+// PacketPayload combines udp payload and destination
+type PacketPayload struct {
+	Target net.Destination
+	Buffer buf.MultiBuffer
+}
+
+// PacketReader is UDP Connection Reader Wrapper for trojan protocol
+type PacketReader struct {
+	io.Reader
+}
+
+// ReadMultiBuffer implements buf.Reader
+func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
+	p, err := r.ReadMultiBufferWithMetadata()
+	if p != nil {
+		return p.Buffer, err
+	}
+	return nil, err
+}
+
+// ReadMultiBufferWithMetadata reads udp packet with destination
+func (r *PacketReader) ReadMultiBufferWithMetadata() (*PacketPayload, error) {
+	addr, port, err := addrParser.ReadAddressPort(nil, r)
+	if err != nil {
+		return nil, newError("failed to read address and port").Base(err)
+	}
+
+	var lengthBuf [2]byte
+	if _, err := io.ReadFull(r, lengthBuf[:]); err != nil {
+		return nil, newError("failed to read payload length").Base(err)
+	}
+
+	remain := int(binary.BigEndian.Uint16(lengthBuf[:]))
+	if remain > maxLength {
+		return nil, newError("oversize payload")
+	}
+
+	var crlf [2]byte
+	if _, err := io.ReadFull(r, crlf[:]); err != nil {
+		return nil, newError("failed to read crlf").Base(err)
+	}
+
+	dest := net.UDPDestination(addr, port)
+	var mb buf.MultiBuffer
+	for remain > 0 {
+		length := buf.Size
+		if remain < length {
+			length = remain
+		}
+
+		b := buf.New()
+		mb = append(mb, b)
+		n, err := b.ReadFullFrom(r, int32(length))
+		if err != nil {
+			buf.ReleaseMulti(mb)
+			return nil, newError("failed to read payload").Base(err)
+		}
+
+		remain -= int(n)
+	}
+
+	return &PacketPayload{Target: dest, Buffer: mb}, nil
+}

+ 91 - 0
proxy/trojan/protocol_test.go

@@ -0,0 +1,91 @@
+package trojan_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/proxy/trojan"
+)
+
+func toAccount(a *Account) protocol.Account {
+	account, err := a.AsAccount()
+	common.Must(err)
+	return account
+}
+
+func TestTCPRequest(t *testing.T) {
+	user := &protocol.MemoryUser{
+		Email: "love@v2ray.com",
+		Account: toAccount(&Account{
+			Password: "password",
+		}),
+	}
+	payload := []byte("test string")
+	data := buf.New()
+	common.Must2(data.Write(payload))
+
+	buffer := buf.New()
+	defer buffer.Release()
+
+	destination := net.Destination{Network: net.Network_TCP, Address: net.LocalHostIP, Port: 1234}
+	writer := &ConnWriter{Writer: buffer, Target: destination, Account: user.Account.(*MemoryAccount)}
+	common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data}))
+
+	reader := &ConnReader{Reader: buffer}
+	common.Must(reader.ParseHeader())
+
+	if r := cmp.Diff(reader.Target, destination); r != "" {
+		t.Error("destination: ", r)
+	}
+
+	decodedData, err := reader.ReadMultiBuffer()
+	common.Must(err)
+	if r := cmp.Diff(decodedData[0].Bytes(), payload); r != "" {
+		t.Error("data: ", r)
+	}
+}
+
+func TestUDPRequest(t *testing.T) {
+	user := &protocol.MemoryUser{
+		Email: "love@v2ray.com",
+		Account: toAccount(&Account{
+			Password: "password",
+		}),
+	}
+	payload := []byte("test string")
+	data := buf.New()
+	common.Must2(data.Write(payload))
+
+	buffer := buf.New()
+	defer buffer.Release()
+
+	destination := net.Destination{Network: net.Network_UDP, Address: net.LocalHostIP, Port: 1234}
+	writer := &PacketWriter{Writer: &ConnWriter{Writer: buffer, Target: destination, Account: user.Account.(*MemoryAccount)}, Target: destination}
+	common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data}))
+
+	connReader := &ConnReader{Reader: buffer}
+	common.Must(connReader.ParseHeader())
+
+	packetReader := &PacketReader{Reader: connReader}
+	p, err := packetReader.ReadMultiBufferWithMetadata()
+	common.Must(err)
+
+	if p.Buffer.IsEmpty() {
+		t.Error("no request data")
+	}
+
+	if r := cmp.Diff(p.Target, destination); r != "" {
+		t.Error("destination: ", r)
+	}
+
+	mb, decoded := buf.SplitFirst(p.Buffer)
+	buf.ReleaseMulti(mb)
+
+	if r := cmp.Diff(decoded.Bytes(), payload); r != "" {
+		t.Error("data: ", r)
+	}
+}

+ 290 - 0
proxy/trojan/server.go

@@ -0,0 +1,290 @@
+// +build !confonly
+
+package trojan
+
+import (
+	"context"
+	"io"
+	"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"
+	udp_proto "v2ray.com/core/common/protocol/udp"
+	"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/features/routing"
+	"v2ray.com/core/transport/internet"
+	"v2ray.com/core/transport/internet/udp"
+)
+
+func init() {
+	common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { // nolint: lll
+		return NewServer(ctx, config.(*ServerConfig))
+	}))
+}
+
+// Server is an inbound connection handler that handles messages in trojan protocol.
+type Server struct {
+	validator     *Validator
+	policyManager policy.Manager
+	config        *ServerConfig
+}
+
+// NewServer creates a new trojan inbound handler.
+func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
+	validator := new(Validator)
+	for _, user := range config.Users {
+		u, err := user.ToMemoryUser()
+		if err != nil {
+			return nil, newError("failed to get trojan user").Base(err).AtError()
+		}
+
+		if err := validator.Add(u); err != nil {
+			return nil, newError("failed to add user").Base(err).AtError()
+		}
+	}
+
+	v := core.MustFromContext(ctx)
+	server := &Server{
+		policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
+		validator:     validator,
+		config:        config,
+	}
+
+	return server, nil
+}
+
+// Network implements proxy.Inbound.Network().
+func (s *Server) Network() []net.Network {
+	return []net.Network{net.Network_TCP}
+}
+
+// Process implements proxy.Inbound.Process().
+func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { // nolint: funlen,lll
+	sessionPolicy := s.policyManager.ForLevel(0)
+	if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil {
+		return newError("unable to set read deadline").Base(err).AtWarning()
+	}
+
+	buffer := buf.New()
+	defer buffer.Release()
+
+	n, err := buffer.ReadFrom(conn)
+	if err != nil {
+		return newError("failed to read first request").Base(err)
+	}
+
+	bufferedReader := &buf.BufferedReader{
+		Reader: buf.NewReader(conn),
+		Buffer: buf.MultiBuffer{buffer},
+	}
+
+	var user *protocol.MemoryUser
+	fallbackEnabled := s.config.Fallback != nil
+	shouldFallback := false
+	if n < 56 { // nolint: gomnd
+		// invalid protocol
+		log.Record(&log.AccessMessage{
+			From:   conn.RemoteAddr(),
+			To:     "",
+			Status: log.AccessRejected,
+			Reason: newError("not trojan protocol"),
+		})
+
+		shouldFallback = true
+	} else {
+		user = s.validator.Get(hexString(buffer.BytesTo(56))) // nolint: gomnd
+		if user == nil {
+			// invalid user, let's fallback
+			log.Record(&log.AccessMessage{
+				From:   conn.RemoteAddr(),
+				To:     "",
+				Status: log.AccessRejected,
+				Reason: newError("not a valid user"),
+			})
+
+			shouldFallback = true
+		}
+	}
+
+	if fallbackEnabled && shouldFallback {
+		return s.fallback(ctx, sessionPolicy, bufferedReader, buf.NewWriter(conn))
+	} else if shouldFallback {
+		return newError("invalid protocol or invalid user")
+	}
+
+	clientReader := &ConnReader{Reader: bufferedReader}
+	if err := clientReader.ParseHeader(); err != nil {
+		log.Record(&log.AccessMessage{
+			From:   conn.RemoteAddr(),
+			To:     "",
+			Status: log.AccessRejected,
+			Reason: err,
+		})
+		return newError("failed to create request from: ", conn.RemoteAddr()).Base(err)
+	}
+
+	destination := clientReader.Target
+	if err := conn.SetReadDeadline(time.Time{}); err != nil {
+		return newError("unable to set read deadline").Base(err).AtWarning()
+	}
+
+	inbound := session.InboundFromContext(ctx)
+	if inbound == nil {
+		panic("no inbound metadata")
+	}
+	inbound.User = user
+	sessionPolicy = s.policyManager.ForLevel(user.Level)
+
+	if destination.Network == net.Network_UDP { // handle udp request
+		return s.handleUDPPayload(ctx, &PacketReader{Reader: clientReader}, &PacketWriter{Writer: conn}, dispatcher)
+	}
+
+	// handle tcp request
+
+	log.ContextWithAccessMessage(ctx, &log.AccessMessage{
+		From:   conn.RemoteAddr(),
+		To:     destination,
+		Status: log.AccessAccepted,
+		Reason: "",
+		Email:  user.Email,
+	})
+
+	newError("received request for ", destination).WriteToLog(session.ExportIDToError(ctx))
+	return s.handleConnection(ctx, sessionPolicy, destination, clientReader, buf.NewWriter(conn), dispatcher)
+}
+
+func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error { // nolint: lll
+	udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
+		common.Must(clientWriter.WriteMultiBufferWithMetadata(buf.MultiBuffer{packet.Payload}, packet.Source))
+	})
+
+	inbound := session.InboundFromContext(ctx)
+	user := inbound.User
+
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		default:
+			p, err := clientReader.ReadMultiBufferWithMetadata()
+			if err != nil {
+				if errors.Cause(err) != io.EOF {
+					return newError("unexpected EOF").Base(err)
+				}
+				return nil
+			}
+
+			log.ContextWithAccessMessage(ctx, &log.AccessMessage{
+				From:   inbound.Source,
+				To:     p.Target,
+				Status: log.AccessAccepted,
+				Reason: "",
+				Email:  user.Email,
+			})
+			newError("tunnelling request to ", p.Target).WriteToLog(session.ExportIDToError(ctx))
+
+			for _, b := range p.Buffer {
+				udpServer.Dispatch(ctx, p.Target, b)
+			}
+		}
+	}
+}
+
+func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Session,
+	destination net.Destination,
+	clientReader buf.Reader,
+	clientWriter buf.Writer, dispatcher routing.Dispatcher) error {
+	ctx, cancel := context.WithCancel(ctx)
+	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
+	ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
+
+	link, err := dispatcher.Dispatch(ctx, destination)
+	if err != nil {
+		return newError("failed to dispatch request to ", destination).Base(err)
+	}
+
+	requestDone := func() error {
+		defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
+
+		if err := buf.Copy(clientReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
+			return newError("failed to transfer request").Base(err)
+		}
+		return nil
+	}
+
+	responseDone := func() error {
+		defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
+
+		if err := buf.Copy(link.Reader, clientWriter, buf.UpdateActivity(timer)); err != nil {
+			return newError("failed to write response").Base(err)
+		}
+		return nil
+	}
+
+	var requestDonePost = task.OnSuccess(requestDone, task.Close(link.Writer))
+	if err := task.Run(ctx, requestDonePost, responseDone); err != nil {
+		common.Must(common.Interrupt(link.Reader))
+		common.Must(common.Interrupt(link.Writer))
+		return newError("connection ends").Base(err)
+	}
+
+	return nil
+}
+
+func (s *Server) fallback(ctx context.Context, sessionPolicy policy.Session, requestReader buf.Reader, responseWriter buf.Writer) error { // nolint: lll
+	ctx, cancel := context.WithCancel(ctx)
+	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
+	ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
+
+	var conn net.Conn
+	var err error
+	fb := s.config.Fallback
+	if err := retry.ExponentialBackoff(5, 100).On(func() error { // nolint: gomnd
+		var dialer net.Dialer
+		conn, err = dialer.DialContext(ctx, fb.Type, fb.Dest)
+		if err != nil {
+			return err
+		}
+		return nil
+	}); err != nil {
+		return newError("failed to dial to " + fb.Dest).Base(err).AtWarning()
+	}
+	defer conn.Close()
+
+	serverReader := buf.NewReader(conn)
+	serverWriter := buf.NewWriter(conn)
+
+	requestDone := func() error {
+		defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
+
+		if err := buf.Copy(requestReader, serverWriter, buf.UpdateActivity(timer)); err != nil {
+			return newError("failed to fallback request payload").Base(err).AtInfo()
+		}
+		return nil
+	}
+
+	responseDone := func() error {
+		defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
+		if err := buf.Copy(serverReader, responseWriter, buf.UpdateActivity(timer)); err != nil {
+			return newError("failed to deliver response payload").Base(err).AtInfo()
+		}
+		return nil
+	}
+
+	if err := task.Run(ctx, task.OnSuccess(requestDone, task.Close(serverWriter)), task.OnSuccess(responseDone, task.Close(responseWriter))); err != nil { // nolint: lll
+		common.Must(common.Interrupt(serverReader))
+		common.Must(common.Interrupt(serverWriter))
+		return newError("fallback ends").Base(err).AtInfo()
+	}
+
+	return nil
+}

+ 1 - 0
proxy/trojan/trojan.go

@@ -0,0 +1 @@
+package trojan

+ 28 - 0
proxy/trojan/validator.go

@@ -0,0 +1,28 @@
+package trojan
+
+import (
+	"sync"
+
+	"v2ray.com/core/common/protocol"
+)
+
+// Validator stores valid trojan users
+type Validator struct {
+	users sync.Map
+}
+
+// Add a trojan user
+func (v *Validator) Add(u *protocol.MemoryUser) error {
+	user := u.Account.(*MemoryAccount)
+	v.users.Store(hexString(user.Key), u)
+	return nil
+}
+
+// Get user with hashed key, nil if user doesn't exist.
+func (v *Validator) Get(hash string) *protocol.MemoryUser {
+	u, _ := v.users.Load(hash)
+	if u != nil {
+		return u.(*protocol.MemoryUser)
+	}
+	return nil
+}