diff --git a/infra/conf/trojan.go b/infra/conf/trojan.go index baaf337ec..546e9faa7 100644 --- a/infra/conf/trojan.go +++ b/infra/conf/trojan.go @@ -21,6 +21,7 @@ type TrojanServerTarget struct { Password string `json:"password"` Email string `json:"email"` Level byte `json:"level"` + Flow string `json:"flow"` } // TrojanClientConfig is configuration of trojan servers @@ -49,6 +50,7 @@ func (c *TrojanClientConfig) Build() (proto.Message, error) { } account := &trojan.Account{ Password: rec.Password, + Flow: rec.Flow, } trojan := &protocol.ServerEndpoint{ Address: rec.Address.Build(), @@ -84,6 +86,7 @@ type TrojanUserConfig struct { Password string `json:"password"` Level byte `json:"level"` Email string `json:"email"` + Flow string `json:"flow"` } // TrojanServerConfig is Inbound configuration @@ -106,6 +109,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) { user := new(protocol.User) account := &trojan.Account{ Password: rawUser.Password, + Flow: rawUser.Flow, } user.Email = rawUser.Email diff --git a/infra/conf/v2ray.go b/infra/conf/v2ray.go index 92137b3c8..992816490 100644 --- a/infra/conf/v2ray.go +++ b/infra/conf/v2ray.go @@ -188,8 +188,8 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { if err != nil { return nil, err } - if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") { - return nil, newError("XTLS only supports VLESS for now.") + if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") && !strings.EqualFold(c.Protocol, "trojan") { + return nil, newError("XTLS doesn't supports " + c.Protocol + " for now.") } receiverSettings.StreamSettings = ss } @@ -258,8 +258,8 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { if err != nil { return nil, err } - if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") { - return nil, newError("XTLS only supports VLESS for now.") + if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") && !strings.EqualFold(c.Protocol, "trojan") { + return nil, newError("XTLS doesn't supports " + c.Protocol + " for now.") } senderSettings.StreamSettings = ss } diff --git a/proxy/trojan/client.go b/proxy/trojan/client.go index 20392c8d1..74b13f91a 100644 --- a/proxy/trojan/client.go +++ b/proxy/trojan/client.go @@ -18,6 +18,7 @@ import ( "v2ray.com/core/features/policy" "v2ray.com/core/transport" "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/internet/xtls" ) // Client is a inbound handler for trojan protocol @@ -83,6 +84,44 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return newError("user account is not valid") } + iConn := conn + if statConn, ok := iConn.(*internet.StatCouterConnection); ok { + iConn = statConn.Connection + } + + connWriter := &ConnWriter{} + allowUDP443 := false + switch account.Flow { + case XRO + "-udp443", XRD + "-udp443": + allowUDP443 = true + account.Flow = account.Flow[:16] + fallthrough + case XRO, XRD: + if destination.Address.Family().IsDomain() && destination.Address.Domain() == muxCoolAddress { + return newError(account.Flow + " doesn't support Mux").AtWarning() + } + + if destination.Network == net.Network_UDP { + if !allowUDP443 && destination.Port == 443 { + return newError(account.Flow + " stopped UDP/443").AtInfo() + } + } else { // enable XTLS only if making TCP request + if xtlsConn, ok := iConn.(*xtls.Conn); ok { + connWriter.Flow = account.Flow + xtlsConn.RPRX = true + + if account.Flow == XRD { + xtlsConn.DirectMode = true + } + } else { + return newError(`failed to enable XTLS, maybe "security" is not "xtls"`).AtWarning() + } + } + case "": + default: + return newError("unsupported flow type: ", account.Flow).AtWarning() + } + sessionPolicy := c.policyManager.ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) @@ -92,7 +131,9 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter var bodyWriter buf.Writer bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) - connWriter := &ConnWriter{Writer: bufferWriter, Target: destination, Account: account} + connWriter.Writer = bufferWriter + connWriter.Target = destination + connWriter.Account = account if destination.Network == net.Network_UDP { bodyWriter = &PacketWriter{Writer: connWriter, Target: destination} diff --git a/proxy/trojan/config.go b/proxy/trojan/config.go index 817358d4f..d9fb89987 100644 --- a/proxy/trojan/config.go +++ b/proxy/trojan/config.go @@ -13,6 +13,7 @@ import ( type MemoryAccount struct { Password string Key []byte + Flow string } // AsAccount implements protocol.AsAccount. @@ -22,6 +23,7 @@ func (a *Account) AsAccount() (protocol.Account, error) { return &MemoryAccount{ Password: password, Key: key, + Flow: a.Flow, }, nil } diff --git a/proxy/trojan/config.pb.go b/proxy/trojan/config.pb.go index 79e7bb4de..8a58f9d13 100644 --- a/proxy/trojan/config.pb.go +++ b/proxy/trojan/config.pb.go @@ -1,404 +1,263 @@ // 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 ( + fmt "fmt" proto "github.com/golang/protobuf/proto" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" + math "math" 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) -) +// 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 that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 +// 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 Account struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` + Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` + Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -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 (m *Account) Reset() { *m = Account{} } +func (m *Account) String() string { return proto.CompactTextString(m) } +func (*Account) ProtoMessage() {} func (*Account) Descriptor() ([]byte, []int) { - return file_proxy_trojan_config_proto_rawDescGZIP(), []int{0} + return fileDescriptor_c2cd7d26d2c3a1c9, []int{0} } -func (x *Account) GetPassword() string { - if x != nil { - return x.Password +func (m *Account) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Account.Unmarshal(m, b) +} +func (m *Account) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Account.Marshal(b, m, deterministic) +} +func (m *Account) XXX_Merge(src proto.Message) { + xxx_messageInfo_Account.Merge(m, src) +} +func (m *Account) XXX_Size() int { + return xxx_messageInfo_Account.Size(m) +} +func (m *Account) XXX_DiscardUnknown() { + xxx_messageInfo_Account.DiscardUnknown(m) +} + +var xxx_messageInfo_Account proto.InternalMessageInfo + +func (m *Account) GetPassword() string { + if m != nil { + return m.Password + } + return "" +} + +func (m *Account) GetFlow() string { + if m != nil { + return m.Flow } return "" } type Fallback struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Alpn string `protobuf:"bytes,1,opt,name=alpn,proto3" json:"alpn,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` - Dest string `protobuf:"bytes,4,opt,name=dest,proto3" json:"dest,omitempty"` - Xver uint64 `protobuf:"varint,5,opt,name=xver,proto3" json:"xver,omitempty"` + Alpn string `protobuf:"bytes,1,opt,name=alpn,proto3" json:"alpn,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + Dest string `protobuf:"bytes,4,opt,name=dest,proto3" json:"dest,omitempty"` + Xver uint64 `protobuf:"varint,5,opt,name=xver,proto3" json:"xver,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -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 (m *Fallback) Reset() { *m = Fallback{} } +func (m *Fallback) String() string { return proto.CompactTextString(m) } +func (*Fallback) ProtoMessage() {} func (*Fallback) Descriptor() ([]byte, []int) { - return file_proxy_trojan_config_proto_rawDescGZIP(), []int{1} + return fileDescriptor_c2cd7d26d2c3a1c9, []int{1} } -func (x *Fallback) GetAlpn() string { - if x != nil { - return x.Alpn +func (m *Fallback) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Fallback.Unmarshal(m, b) +} +func (m *Fallback) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Fallback.Marshal(b, m, deterministic) +} +func (m *Fallback) XXX_Merge(src proto.Message) { + xxx_messageInfo_Fallback.Merge(m, src) +} +func (m *Fallback) XXX_Size() int { + return xxx_messageInfo_Fallback.Size(m) +} +func (m *Fallback) XXX_DiscardUnknown() { + xxx_messageInfo_Fallback.DiscardUnknown(m) +} + +var xxx_messageInfo_Fallback proto.InternalMessageInfo + +func (m *Fallback) GetAlpn() string { + if m != nil { + return m.Alpn } return "" } -func (x *Fallback) GetPath() string { - if x != nil { - return x.Path +func (m *Fallback) GetPath() string { + if m != nil { + return m.Path } return "" } -func (x *Fallback) GetType() string { - if x != nil { - return x.Type +func (m *Fallback) GetType() string { + if m != nil { + return m.Type } return "" } -func (x *Fallback) GetDest() string { - if x != nil { - return x.Dest +func (m *Fallback) GetDest() string { + if m != nil { + return m.Dest } return "" } -func (x *Fallback) GetXver() uint64 { - if x != nil { - return x.Xver +func (m *Fallback) GetXver() uint64 { + if m != nil { + return m.Xver } return 0 } 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"` + Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -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 (m *ClientConfig) Reset() { *m = ClientConfig{} } +func (m *ClientConfig) String() string { return proto.CompactTextString(m) } +func (*ClientConfig) ProtoMessage() {} func (*ClientConfig) Descriptor() ([]byte, []int) { - return file_proxy_trojan_config_proto_rawDescGZIP(), []int{2} + return fileDescriptor_c2cd7d26d2c3a1c9, []int{2} } -func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { - if x != nil { - return x.Server +func (m *ClientConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ClientConfig.Unmarshal(m, b) +} +func (m *ClientConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ClientConfig.Marshal(b, m, deterministic) +} +func (m *ClientConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientConfig.Merge(m, src) +} +func (m *ClientConfig) XXX_Size() int { + return xxx_messageInfo_ClientConfig.Size(m) +} +func (m *ClientConfig) XXX_DiscardUnknown() { + xxx_messageInfo_ClientConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientConfig proto.InternalMessageInfo + +func (m *ClientConfig) GetServer() []*protocol.ServerEndpoint { + if m != nil { + return m.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"` - Fallbacks []*Fallback `protobuf:"bytes,3,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` + Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` + Fallbacks []*Fallback `protobuf:"bytes,3,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -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 (m *ServerConfig) Reset() { *m = ServerConfig{} } +func (m *ServerConfig) String() string { return proto.CompactTextString(m) } +func (*ServerConfig) ProtoMessage() {} func (*ServerConfig) Descriptor() ([]byte, []int) { - return file_proxy_trojan_config_proto_rawDescGZIP(), []int{3} + return fileDescriptor_c2cd7d26d2c3a1c9, []int{3} } -func (x *ServerConfig) GetUsers() []*protocol.User { - if x != nil { - return x.Users +func (m *ServerConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ServerConfig.Unmarshal(m, b) +} +func (m *ServerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ServerConfig.Marshal(b, m, deterministic) +} +func (m *ServerConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServerConfig.Merge(m, src) +} +func (m *ServerConfig) XXX_Size() int { + return xxx_messageInfo_ServerConfig.Size(m) +} +func (m *ServerConfig) XXX_DiscardUnknown() { + xxx_messageInfo_ServerConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_ServerConfig proto.InternalMessageInfo + +func (m *ServerConfig) GetUsers() []*protocol.User { + if m != nil { + return m.Users } return nil } -func (x *ServerConfig) GetFallbacks() []*Fallback { - if x != nil { - return x.Fallbacks +func (m *ServerConfig) GetFallbacks() []*Fallback { + if m != nil { + return m.Fallbacks } 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, 0x6e, 0x0a, 0x08, 0x46, 0x61, - 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, 0x72, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 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, 0x87, - 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, 0x3f, 0x0a, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 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, 0x09, 0x66, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 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, +func init() { + proto.RegisterType((*Account)(nil), "v2ray.core.proxy.trojan.Account") + proto.RegisterType((*Fallback)(nil), "v2ray.core.proxy.trojan.Fallback") + proto.RegisterType((*ClientConfig)(nil), "v2ray.core.proxy.trojan.ClientConfig") + proto.RegisterType((*ServerConfig)(nil), "v2ray.core.proxy.trojan.ServerConfig") } -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 +func init() { + proto.RegisterFile("proxy/trojan/config.proto", fileDescriptor_c2cd7d26d2c3a1c9) } -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.fallbacks: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 +var fileDescriptor_c2cd7d26d2c3a1c9 = []byte{ + // 348 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xbd, 0x4e, 0xfb, 0x30, + 0x14, 0xc5, 0x95, 0x7e, 0xfd, 0x5b, 0xff, 0x3b, 0x65, 0x69, 0x48, 0x97, 0x34, 0x53, 0xc5, 0xe0, + 0x48, 0x45, 0x42, 0x62, 0x42, 0xb4, 0x82, 0xb9, 0x32, 0xd0, 0x81, 0x05, 0xb9, 0xae, 0x0b, 0x85, + 0xc4, 0xd7, 0xb2, 0xdd, 0x8f, 0x3c, 0x01, 0xef, 0xc2, 0x53, 0x22, 0xdb, 0x09, 0x54, 0x48, 0x65, + 0x3b, 0x39, 0xe7, 0xfc, 0xae, 0x72, 0x6f, 0x82, 0xce, 0xa4, 0x82, 0x43, 0x99, 0x19, 0x05, 0x6f, + 0x54, 0x64, 0x0c, 0xc4, 0x7a, 0xf3, 0x82, 0xa5, 0x02, 0x03, 0xe1, 0x60, 0x37, 0x51, 0xb4, 0xc4, + 0x0c, 0x14, 0xc7, 0xae, 0x85, 0x7d, 0x2b, 0x8e, 0x19, 0x14, 0x05, 0x88, 0xcc, 0xd5, 0x18, 0xe4, + 0xd9, 0x56, 0x73, 0xe5, 0xa1, 0x78, 0xf4, 0x3b, 0xd3, 0x5c, 0xed, 0xb8, 0x7a, 0xd6, 0x92, 0x33, + 0x5f, 0x49, 0xaf, 0xd0, 0xbf, 0x1b, 0xc6, 0x60, 0x2b, 0x4c, 0x18, 0xa3, 0xae, 0xa4, 0x5a, 0xef, + 0x41, 0xad, 0xa2, 0x20, 0x09, 0xc6, 0x3d, 0xf2, 0xfd, 0x1c, 0x86, 0xa8, 0xb5, 0xce, 0x61, 0x1f, + 0x35, 0x9c, 0xef, 0x74, 0x2a, 0x50, 0xf7, 0x8e, 0xe6, 0xf9, 0x92, 0xb2, 0x77, 0x9b, 0xd3, 0x5c, + 0x8a, 0x8a, 0x73, 0xda, 0x7a, 0x92, 0x9a, 0xd7, 0x9a, 0xb1, 0xda, 0x7a, 0xa6, 0x94, 0x3c, 0x6a, + 0x7a, 0xcf, 0x6a, 0xeb, 0xad, 0xb8, 0x36, 0x51, 0xcb, 0x7b, 0x56, 0x5b, 0xef, 0xb0, 0xe3, 0x2a, + 0x6a, 0x27, 0xc1, 0xb8, 0x45, 0x9c, 0x4e, 0x09, 0xea, 0xcf, 0xf2, 0x0d, 0x17, 0x66, 0xe6, 0x0e, + 0x13, 0x4e, 0x51, 0xc7, 0xef, 0x13, 0x05, 0x49, 0x73, 0xfc, 0x7f, 0x72, 0x8e, 0x8f, 0x6e, 0xe4, + 0x37, 0xc7, 0xf5, 0xe6, 0xf8, 0xde, 0x35, 0x6f, 0xc5, 0x4a, 0xc2, 0x46, 0x18, 0x52, 0x91, 0xe9, + 0x47, 0x80, 0xfa, 0x3e, 0xaa, 0x86, 0x5e, 0xa2, 0xb6, 0x3d, 0xa0, 0xae, 0x66, 0x26, 0x7f, 0xcd, + 0x7c, 0xd4, 0x5c, 0x11, 0x5f, 0x0f, 0xaf, 0x51, 0x6f, 0x5d, 0x1d, 0x43, 0x47, 0x4d, 0xc7, 0x8e, + 0xf0, 0x89, 0x6f, 0x86, 0xeb, 0xb3, 0x91, 0x1f, 0x66, 0xba, 0x40, 0x43, 0x06, 0xc5, 0x29, 0x64, + 0x1e, 0x3c, 0x0d, 0xeb, 0xa8, 0xc8, 0x6c, 0x9c, 0x1d, 0xff, 0x2b, 0x9f, 0x8d, 0xc1, 0x62, 0x42, + 0x68, 0x89, 0x67, 0x16, 0x9c, 0x3b, 0xf0, 0xc1, 0x25, 0xcb, 0x8e, 0x7b, 0xdd, 0x8b, 0xaf, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xb0, 0x31, 0x74, 0x85, 0x5c, 0x02, 0x00, 0x00, } diff --git a/proxy/trojan/protocol.go b/proxy/trojan/protocol.go index 4c13964dc..9e7553593 100644 --- a/proxy/trojan/protocol.go +++ b/proxy/trojan/protocol.go @@ -21,9 +21,17 @@ var ( const ( maxLength = 8192 + // XRO is constant for XTLS origin mode + XRO = "xtls-rprx-origin" + // XRD is constant for XTLS direct mode + XRD = "xtls-rprx-direct" commandTCP byte = 1 commandUDP byte = 3 + + // for xtls + commandXRO byte = 0xf1 // XTLS origin mode + commandXRD byte = 0xf2 // XTLS direct mode ) // ConnWriter is TCP Connection Writer Wrapper for trojan protocol @@ -31,6 +39,7 @@ type ConnWriter struct { io.Writer Target net.Destination Account *MemoryAccount + Flow string headerSent bool } @@ -67,6 +76,10 @@ func (c *ConnWriter) writeHeader() error { command := commandTCP if c.Target.Network == net.Network_UDP { command = commandUDP + } else if c.Flow == XRO { + command = commandXRO + } else if c.Flow == XRD { + command = commandXRD } if _, err := buffer.Write(c.Account.Key); err != nil { @@ -160,6 +173,7 @@ func (w *PacketWriter) writePacket(payload []byte, dest net.Destination) (int, e type ConnReader struct { io.Reader Target net.Destination + Flow string headerParsed bool } @@ -183,6 +197,10 @@ func (c *ConnReader) ParseHeader() error { network := net.Network_TCP if command[0] == commandUDP { network = net.Network_UDP + } else if command[0] == commandXRO { + c.Flow = XRO + } else if command[0] == commandXRD { + c.Flow = XRD } addr, port, err := addrParser.ReadAddressPort(nil, c.Reader) diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index ecacdeb68..35fadaabe 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -185,6 +185,34 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet } // handle tcp request + account, ok := user.Account.(*MemoryAccount) + if !ok { + return newError("user account is not valid") + } + + switch clientReader.Flow { + case XRO, XRD: + if account.Flow == clientReader.Flow { + if destination.Address.Family().IsDomain() && destination.Address.Domain() == muxCoolAddress { + return newError("XTLS doesn't support Mux").AtWarning() + } + + if xtlsConn, ok := iConn.(*xtls.Conn); ok { + xtlsConn.RPRX = true + + if clientReader.Flow == XRD { + xtlsConn.DirectMode = true + } + } else { + return newError(`failed to enable XTLS, maybe "security" is not "xtls"`).AtWarning() + } + } else { + return newError("unable to use ", clientReader.Flow).AtWarning() + } + case "": + default: + return newError("unsupported flow type: ", account.Flow).AtWarning() + } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: conn.RemoteAddr(), diff --git a/proxy/trojan/trojan.go b/proxy/trojan/trojan.go index 73b3154fb..4639b7d9b 100644 --- a/proxy/trojan/trojan.go +++ b/proxy/trojan/trojan.go @@ -1 +1,5 @@ package trojan + +const ( + muxCoolAddress = "v1.mux.cool" +)