From 7640482e3ebeeebf7bdf95a5424450a2dc8aaf9b Mon Sep 17 00:00:00 2001 From: tat5522 <81008809+tat5522@users.noreply.github.com> Date: Thu, 25 Mar 2021 21:45:10 +0800 Subject: [PATCH] add reverse api support.(#417) --- app/reverse/bridge.go | 8 + app/reverse/command/command.go | 68 +++ app/reverse/command/command.pb.go | 646 +++++++++++++++++++++++++ app/reverse/command/command.proto | 42 ++ app/reverse/command/command_grpc.pb.go | 196 ++++++++ app/reverse/config.go | 16 + app/reverse/portal.go | 8 + app/reverse/reverse.go | 184 ++++++- features/reverse/reverse.go | 59 +++ infra/conf/api.go | 5 +- testing/scenarios/reverse_test.go | 284 ++++++++++- 11 files changed, 1499 insertions(+), 17 deletions(-) create mode 100644 app/reverse/command/command.go create mode 100644 app/reverse/command/command.pb.go create mode 100644 app/reverse/command/command.proto create mode 100644 app/reverse/command/command_grpc.pb.go create mode 100644 features/reverse/reverse.go diff --git a/app/reverse/bridge.go b/app/reverse/bridge.go index 32226447ff70..093f6194e79d 100644 --- a/app/reverse/bridge.go +++ b/app/reverse/bridge.go @@ -83,6 +83,14 @@ func (b *Bridge) monitor() error { return nil } +func (b *Bridge) GetTag() string { + return b.tag +} + +func (b *Bridge) GetDomain() string { + return b.domain +} + func (b *Bridge) Start() error { return b.monitorTask.Start() } diff --git a/app/reverse/command/command.go b/app/reverse/command/command.go new file mode 100644 index 000000000000..e500920b2210 --- /dev/null +++ b/app/reverse/command/command.go @@ -0,0 +1,68 @@ +package command + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + +import ( + "context" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features/reverse" + "google.golang.org/grpc" +) + +// reverseServer is an implementation of ReverseService. +type reverseServer struct { + s *core.Instance + reverse reverse.Manager +} + +func (s *reverseServer) AddBridge(ctx context.Context, request *AddBridgeRequest) (*AddBridgeResponse, error) { + err := s.reverse.AddBridge(ctx, request.Config) + return &AddBridgeResponse{}, err +} + +func (s *reverseServer) RemoveBridge(ctx context.Context, request *RemoveBridgeRequest) (*RemoveBridgeResponse, error) { + err := s.reverse.RemoveBridge(ctx, request.Tag) + return &RemoveBridgeResponse{}, err +} + +func (s *reverseServer) AddPortal(ctx context.Context, request *AddPortalRequest) (*AddPortalResponse, error) { + err := s.reverse.AddPortal(ctx, request.Config) + return &AddPortalResponse{}, err +} + +func (s *reverseServer) RemovePortal(ctx context.Context, request *RemovePortalRequest) (*RemovePortalResponse, error) { + err := s.reverse.RemovePortal(ctx, request.Tag) + return &RemovePortalResponse{}, err +} + +func (s *reverseServer) mustEmbedUnimplementedReverseServiceServer() {} + +type service struct { + v *core.Instance +} + +func (s *service) Register(server *grpc.Server) { + m := &reverseServer{ + s: s.v, + reverse: nil, + } + + common.Must(s.v.RequireFeatures(func(reverse reverse.Manager) { + m.reverse = reverse + })) + + RegisterReverseServiceServer(server, m) + + // For compatibility purposes + vCoreDesc := ReverseService_ServiceDesc + vCoreDesc.ServiceName = "v2ray.core.app.reverse.command.ReverseService" + server.RegisterService(&vCoreDesc, m) +} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { + s := core.MustFromContext(ctx) + return &service{v: s}, nil + })) +} diff --git a/app/reverse/command/command.pb.go b/app/reverse/command/command.pb.go new file mode 100644 index 000000000000..e001b1d8cb47 --- /dev/null +++ b/app/reverse/command/command.pb.go @@ -0,0 +1,646 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.15.6 +// source: app/reverse/command/command.proto + +package command + +import ( + reverse "github.com/xtls/xray-core/app/reverse" + 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) +) + +type AddBridgeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Config *reverse.BridgeConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` +} + +func (x *AddBridgeRequest) Reset() { + *x = AddBridgeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddBridgeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddBridgeRequest) ProtoMessage() {} + +func (x *AddBridgeRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_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 AddBridgeRequest.ProtoReflect.Descriptor instead. +func (*AddBridgeRequest) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{0} +} + +func (x *AddBridgeRequest) GetConfig() *reverse.BridgeConfig { + if x != nil { + return x.Config + } + return nil +} + +type AddBridgeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *AddBridgeResponse) Reset() { + *x = AddBridgeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddBridgeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddBridgeResponse) ProtoMessage() {} + +func (x *AddBridgeResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_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 AddBridgeResponse.ProtoReflect.Descriptor instead. +func (*AddBridgeResponse) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{1} +} + +type RemoveBridgeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` +} + +func (x *RemoveBridgeRequest) Reset() { + *x = RemoveBridgeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveBridgeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveBridgeRequest) ProtoMessage() {} + +func (x *RemoveBridgeRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_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 RemoveBridgeRequest.ProtoReflect.Descriptor instead. +func (*RemoveBridgeRequest) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{2} +} + +func (x *RemoveBridgeRequest) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +type RemoveBridgeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RemoveBridgeResponse) Reset() { + *x = RemoveBridgeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveBridgeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveBridgeResponse) ProtoMessage() {} + +func (x *RemoveBridgeResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_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 RemoveBridgeResponse.ProtoReflect.Descriptor instead. +func (*RemoveBridgeResponse) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{3} +} + +type AddPortalRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Config *reverse.PortalConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` +} + +func (x *AddPortalRequest) Reset() { + *x = AddPortalRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddPortalRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddPortalRequest) ProtoMessage() {} + +func (x *AddPortalRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_proto_msgTypes[4] + 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 AddPortalRequest.ProtoReflect.Descriptor instead. +func (*AddPortalRequest) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{4} +} + +func (x *AddPortalRequest) GetConfig() *reverse.PortalConfig { + if x != nil { + return x.Config + } + return nil +} + +type AddPortalResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *AddPortalResponse) Reset() { + *x = AddPortalResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddPortalResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddPortalResponse) ProtoMessage() {} + +func (x *AddPortalResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_proto_msgTypes[5] + 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 AddPortalResponse.ProtoReflect.Descriptor instead. +func (*AddPortalResponse) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{5} +} + +type RemovePortalRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` +} + +func (x *RemovePortalRequest) Reset() { + *x = RemovePortalRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemovePortalRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemovePortalRequest) ProtoMessage() {} + +func (x *RemovePortalRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_proto_msgTypes[6] + 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 RemovePortalRequest.ProtoReflect.Descriptor instead. +func (*RemovePortalRequest) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{6} +} + +func (x *RemovePortalRequest) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +type RemovePortalResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RemovePortalResponse) Reset() { + *x = RemovePortalResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemovePortalResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemovePortalResponse) ProtoMessage() {} + +func (x *RemovePortalResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_reverse_command_command_proto_msgTypes[7] + 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 RemovePortalResponse.ProtoReflect.Descriptor instead. +func (*RemovePortalResponse) Descriptor() ([]byte, []int) { + return file_app_reverse_command_command_proto_rawDescGZIP(), []int{7} +} + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_command_command_proto_msgTypes[8] + 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_app_reverse_command_command_proto_msgTypes[8] + 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_app_reverse_command_command_proto_rawDescGZIP(), []int{8} +} + +var File_app_reverse_command_command_proto protoreflect.FileDescriptor + +var file_app_reverse_command_command_proto_rawDesc = []byte{ + 0x0a, 0x21, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2f, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, + 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x18, 0x61, + 0x70, 0x70, 0x2f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4a, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x42, 0x72, + 0x69, 0x64, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, + 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x42, + 0x72, 0x69, 0x64, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0x13, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, + 0x67, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, 0x72, 0x69, 0x64, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x41, 0x64, 0x64, + 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, + 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x13, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x0a, 0x13, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x74, 0x61, 0x67, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, + 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xc2, 0x03, 0x0a, 0x0e, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x42, + 0x72, 0x69, 0x64, 0x67, 0x65, 0x12, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, + 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x2e, 0x41, 0x64, 0x64, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, + 0x65, 0x72, 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, + 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x6f, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, + 0x12, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x66, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x12, 0x2a, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, + 0x74, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x0c, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x12, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, + 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, + 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, + 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_app_reverse_command_command_proto_rawDescOnce sync.Once + file_app_reverse_command_command_proto_rawDescData = file_app_reverse_command_command_proto_rawDesc +) + +func file_app_reverse_command_command_proto_rawDescGZIP() []byte { + file_app_reverse_command_command_proto_rawDescOnce.Do(func() { + file_app_reverse_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_reverse_command_command_proto_rawDescData) + }) + return file_app_reverse_command_command_proto_rawDescData +} + +var file_app_reverse_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_app_reverse_command_command_proto_goTypes = []interface{}{ + (*AddBridgeRequest)(nil), // 0: xray.app.reverse.command.AddBridgeRequest + (*AddBridgeResponse)(nil), // 1: xray.app.reverse.command.AddBridgeResponse + (*RemoveBridgeRequest)(nil), // 2: xray.app.reverse.command.RemoveBridgeRequest + (*RemoveBridgeResponse)(nil), // 3: xray.app.reverse.command.RemoveBridgeResponse + (*AddPortalRequest)(nil), // 4: xray.app.reverse.command.AddPortalRequest + (*AddPortalResponse)(nil), // 5: xray.app.reverse.command.AddPortalResponse + (*RemovePortalRequest)(nil), // 6: xray.app.reverse.command.RemovePortalRequest + (*RemovePortalResponse)(nil), // 7: xray.app.reverse.command.RemovePortalResponse + (*Config)(nil), // 8: xray.app.reverse.command.Config + (*reverse.BridgeConfig)(nil), // 9: xray.app.reverse.BridgeConfig + (*reverse.PortalConfig)(nil), // 10: xray.app.reverse.PortalConfig +} +var file_app_reverse_command_command_proto_depIdxs = []int32{ + 9, // 0: xray.app.reverse.command.AddBridgeRequest.config:type_name -> xray.app.reverse.BridgeConfig + 10, // 1: xray.app.reverse.command.AddPortalRequest.config:type_name -> xray.app.reverse.PortalConfig + 0, // 2: xray.app.reverse.command.ReverseService.AddBridge:input_type -> xray.app.reverse.command.AddBridgeRequest + 2, // 3: xray.app.reverse.command.ReverseService.RemoveBridge:input_type -> xray.app.reverse.command.RemoveBridgeRequest + 4, // 4: xray.app.reverse.command.ReverseService.AddPortal:input_type -> xray.app.reverse.command.AddPortalRequest + 6, // 5: xray.app.reverse.command.ReverseService.RemovePortal:input_type -> xray.app.reverse.command.RemovePortalRequest + 1, // 6: xray.app.reverse.command.ReverseService.AddBridge:output_type -> xray.app.reverse.command.AddBridgeResponse + 3, // 7: xray.app.reverse.command.ReverseService.RemoveBridge:output_type -> xray.app.reverse.command.RemoveBridgeResponse + 5, // 8: xray.app.reverse.command.ReverseService.AddPortal:output_type -> xray.app.reverse.command.AddPortalResponse + 7, // 9: xray.app.reverse.command.ReverseService.RemovePortal:output_type -> xray.app.reverse.command.RemovePortalResponse + 6, // [6:10] is the sub-list for method output_type + 2, // [2:6] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_app_reverse_command_command_proto_init() } +func file_app_reverse_command_command_proto_init() { + if File_app_reverse_command_command_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_app_reverse_command_command_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddBridgeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddBridgeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveBridgeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveBridgeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddPortalRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddPortalResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemovePortalRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemovePortalResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_command_command_proto_msgTypes[8].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_app_reverse_command_command_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_app_reverse_command_command_proto_goTypes, + DependencyIndexes: file_app_reverse_command_command_proto_depIdxs, + MessageInfos: file_app_reverse_command_command_proto_msgTypes, + }.Build() + File_app_reverse_command_command_proto = out.File + file_app_reverse_command_command_proto_rawDesc = nil + file_app_reverse_command_command_proto_goTypes = nil + file_app_reverse_command_command_proto_depIdxs = nil +} \ No newline at end of file diff --git a/app/reverse/command/command.proto b/app/reverse/command/command.proto new file mode 100644 index 000000000000..ab916c1f7bb9 --- /dev/null +++ b/app/reverse/command/command.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package xray.app.reverse.command; +option csharp_namespace = "Xray.App.Reverse.Command"; +option go_package = "github.com/xtls/xray-core/app/reverse/command"; +option java_package = "com.xray.app.reverse.command"; +option java_multiple_files = true; + +import "app/reverse/config.proto"; + +message AddBridgeRequest { + xray.app.reverse.BridgeConfig config = 1; +} +message AddBridgeResponse {} + +message RemoveBridgeRequest { + string tag = 1; +} +message RemoveBridgeResponse {} + +message AddPortalRequest { + xray.app.reverse.PortalConfig config = 1; +} +message AddPortalResponse {} + +message RemovePortalRequest { + string tag = 1; +} +message RemovePortalResponse {} + +service ReverseService { + + rpc AddBridge(AddBridgeRequest) returns (AddBridgeResponse){} + + rpc RemoveBridge(RemoveBridgeRequest) returns (RemoveBridgeResponse){} + + rpc AddPortal(AddPortalRequest) returns (AddPortalResponse){} + + rpc RemovePortal(RemovePortalRequest) returns (RemovePortalResponse){} +} + +message Config {} diff --git a/app/reverse/command/command_grpc.pb.go b/app/reverse/command/command_grpc.pb.go new file mode 100644 index 000000000000..89a56b676fc3 --- /dev/null +++ b/app/reverse/command/command_grpc.pb.go @@ -0,0 +1,196 @@ +package command + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// ReverseServiceClient is the client API for ReverseService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ReverseServiceClient interface { + AddBridge(ctx context.Context, in *AddBridgeRequest, opts ...grpc.CallOption) (*AddBridgeResponse, error) + RemoveBridge(ctx context.Context, in *RemoveBridgeRequest, opts ...grpc.CallOption) (*RemoveBridgeResponse, error) + AddPortal(ctx context.Context, in *AddPortalRequest, opts ...grpc.CallOption) (*AddPortalResponse, error) + RemovePortal(ctx context.Context, in *RemovePortalRequest, opts ...grpc.CallOption) (*RemovePortalResponse, error) +} + +type reverseServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewReverseServiceClient(cc grpc.ClientConnInterface) ReverseServiceClient { + return &reverseServiceClient{cc} +} + +func (c *reverseServiceClient) AddBridge(ctx context.Context, in *AddBridgeRequest, opts ...grpc.CallOption) (*AddBridgeResponse, error) { + out := new(AddBridgeResponse) + err := c.cc.Invoke(ctx, "/xray.app.reverse.command.ReverseService/AddBridge", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reverseServiceClient) RemoveBridge(ctx context.Context, in *RemoveBridgeRequest, opts ...grpc.CallOption) (*RemoveBridgeResponse, error) { + out := new(RemoveBridgeResponse) + err := c.cc.Invoke(ctx, "/xray.app.reverse.command.ReverseService/RemoveBridge", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reverseServiceClient) AddPortal(ctx context.Context, in *AddPortalRequest, opts ...grpc.CallOption) (*AddPortalResponse, error) { + out := new(AddPortalResponse) + err := c.cc.Invoke(ctx, "/xray.app.reverse.command.ReverseService/AddPortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reverseServiceClient) RemovePortal(ctx context.Context, in *RemovePortalRequest, opts ...grpc.CallOption) (*RemovePortalResponse, error) { + out := new(RemovePortalResponse) + err := c.cc.Invoke(ctx, "/xray.app.reverse.command.ReverseService/RemovePortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ReverseServiceServer is the server API for ReverseService service. +type ReverseServiceServer interface { + AddBridge(context.Context, *AddBridgeRequest) (*AddBridgeResponse, error) + RemoveBridge(context.Context, *RemoveBridgeRequest) (*RemoveBridgeResponse, error) + AddPortal(context.Context, *AddPortalRequest) (*AddPortalResponse, error) + RemovePortal(context.Context, *RemovePortalRequest) (*RemovePortalResponse, error) +} + +// UnimplementedReverseServiceServer can be embedded to have forward compatible implementations. +type UnimplementedReverseServiceServer struct { +} + +func (*UnimplementedReverseServiceServer) AddBridge(context.Context, *AddBridgeRequest) (*AddBridgeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddBridge not implemented") +} +func (*UnimplementedReverseServiceServer) RemoveBridge(context.Context, *RemoveBridgeRequest) (*RemoveBridgeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveBridge not implemented") +} +func (*UnimplementedReverseServiceServer) AddPortal(context.Context, *AddPortalRequest) (*AddPortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddPortal not implemented") +} +func (*UnimplementedReverseServiceServer) RemovePortal(context.Context, *RemovePortalRequest) (*RemovePortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemovePortal not implemented") +} + +func RegisterReverseServiceServer(s *grpc.Server, srv ReverseServiceServer) { + s.RegisterService(&ReverseService_ServiceDesc, srv) +} + +func _ReverseService_AddBridge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddBridgeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReverseServiceServer).AddBridge(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/xray.app.reverse.command.ReverseService/AddBridge", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReverseServiceServer).AddBridge(ctx, req.(*AddBridgeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReverseService_RemoveBridge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveBridgeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReverseServiceServer).RemoveBridge(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/xray.app.reverse.command.ReverseService/RemoveBridge", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReverseServiceServer).RemoveBridge(ctx, req.(*RemoveBridgeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReverseService_AddPortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddPortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReverseServiceServer).AddPortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/xray.app.reverse.command.ReverseService/AddPortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReverseServiceServer).AddPortal(ctx, req.(*AddPortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReverseService_RemovePortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemovePortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReverseServiceServer).RemovePortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/xray.app.reverse.command.ReverseService/RemovePortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReverseServiceServer).RemovePortal(ctx, req.(*RemovePortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var ReverseService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "xray.app.reverse.command.ReverseService", + HandlerType: (*ReverseServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddBridge", + Handler: _ReverseService_AddBridge_Handler, + }, + { + MethodName: "RemoveBridge", + Handler: _ReverseService_RemoveBridge_Handler, + }, + { + MethodName: "AddPortal", + Handler: _ReverseService_AddPortal_Handler, + }, + { + MethodName: "RemovePortal", + Handler: _ReverseService_RemovePortal_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/reverse/command/command.proto", +} diff --git a/app/reverse/config.go b/app/reverse/config.go index 517b61702fd9..698d86c74473 100644 --- a/app/reverse/config.go +++ b/app/reverse/config.go @@ -12,3 +12,19 @@ func (c *Control) FillInRandom() { c.Random = make([]byte, randomLength) io.ReadFull(rand.Reader, c.Random) } + +func (x *BridgeConfig) Start() error { + panic("implement me") +} + +func (x *BridgeConfig) Close() error { + panic("implement me") +} + +func (x *PortalConfig) Start() error { + panic("implement me") +} + +func (x *PortalConfig) Close() error { + panic("implement me") +} diff --git a/app/reverse/portal.go b/app/reverse/portal.go index b0860a6ee2e4..09d779a3002a 100644 --- a/app/reverse/portal.go +++ b/app/reverse/portal.go @@ -50,6 +50,14 @@ func NewPortal(config *PortalConfig, ohm outbound.Manager) (*Portal, error) { }, nil } +func (p *Portal) GetTag() string { + return p.tag +} + +func (p *Portal) GetDomain() string { + return p.domain +} + func (p *Portal) Start() error { return p.ohm.AddHandler(context.Background(), &Outbound{ portal: p, diff --git a/app/reverse/reverse.go b/app/reverse/reverse.go index ec3b647526b3..6bd0eec287cf 100644 --- a/app/reverse/reverse.go +++ b/app/reverse/reverse.go @@ -4,12 +4,13 @@ package reverse import ( "context" - "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/outbound" + "github.com/xtls/xray-core/features/reverse" "github.com/xtls/xray-core/features/routing" ) @@ -25,24 +26,17 @@ func isInternalDomain(dest net.Destination) bool { return isDomain(dest, internalDomain) } -func init() { - common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - r := new(Reverse) - if err := core.RequireFeatures(ctx, func(d routing.Dispatcher, om outbound.Manager) error { - return r.Init(config.(*Config), d, om) - }); err != nil { - return nil, err - } - return r, nil - })) -} - type Reverse struct { bridges []*Bridge portals []*Portal + d routing.Dispatcher + ohm outbound.Manager } func (r *Reverse) Init(config *Config, d routing.Dispatcher, ohm outbound.Manager) error { + r.d = d + r.ohm = ohm + for _, bConfig := range config.BridgeConfig { b, err := NewBridge(bConfig, d) if err != nil { @@ -63,7 +57,7 @@ func (r *Reverse) Init(config *Config, d routing.Dispatcher, ohm outbound.Manage } func (r *Reverse) Type() interface{} { - return (*Reverse)(nil) + return reverse.ManagerType() } func (r *Reverse) Start() error { @@ -94,3 +88,165 @@ func (r *Reverse) Close() error { return errors.Combine(errs...) } + +func (r *Reverse) addHandler(ctx context.Context, handler reverse.Handler) error { + tag := handler.GetTag() + domain := handler.GetDomain() + if len(tag) == 0 || len(domain) == 0 { + return newError("tag or domain is empty") + } + + var h interface{} + var err error + + // create object + switch handler.(type) { + case *BridgeConfig: + { + h, err = NewBridge(&BridgeConfig{ + Tag: tag, + Domain: domain, + }, r.d) + } + case *PortalConfig: + { + h, err = NewPortal(&PortalConfig{ + Tag: tag, + Domain: domain, + }, r.ohm) + } + } + + if err != nil { + return err + } + + // start bridge + err = h.(reverse.Handler).Start() + if err != nil { + return err + } + + // append slice + switch handler.(type) { + case *BridgeConfig: + { + r.bridges = append(r.bridges, h.(*Bridge)) + } + case *PortalConfig: + { + r.portals = append(r.portals, h.(*Portal)) + } + } + + return err +} + +// findHandler Find the corresponding handler based on the tag and the specified type +func (r *Reverse) findHandler(tag string, config reverse.Handler) (handler reverse.Handler, index int, err error) { + index = -1 + switch config.(type) { + case *Bridge, *BridgeConfig: + { + for k, v := range r.bridges { + if v.tag == tag { + index = k + handler = v + return + } + } + } + case *Portal, *PortalConfig: + { + for k, v := range r.portals { + if v.tag == tag { + index = k + handler = v + return + } + } + } + } + + err = newError("The specified tag was not found") + return +} + +// AddBridge Implement the Manager interface. +func (r *Reverse) AddBridge(ctx context.Context, bridge reverse.Handler) error { + tag := bridge.GetTag() + _, idx, _ := r.findHandler(tag, bridge) + if idx > -1 { + err := newError("The tag[", tag, "] already exists") + err.WriteToLog(session.ExportIDToError(ctx)) + return err + } + + err := r.addHandler(ctx, bridge) + if err != nil { + return err + } + newError("The bridge has been added successfully through the API.[", tag, "]").WriteToLog(session.ExportIDToError(ctx)) + return nil +} + +// RemoveBridge Implement the Manager interface. +func (r *Reverse) RemoveBridge(ctx context.Context, tag string) error { + b, idx, err := r.findHandler(tag, &Bridge{}) + if err != nil { + return err + } + err = b.Close() + if err != nil { + return err + } + + r.bridges = append(r.bridges[:idx], r.bridges[idx+1:]...) + newError("The bridge has been removed through the API. [", tag, "] ").WriteToLog(session.ExportIDToError(ctx)) + return nil +} + +// AddPortal Implement the Manager interface. +func (r *Reverse) AddPortal(ctx context.Context, portal reverse.Handler) error { + tag := portal.GetTag() + _, idx, _ := r.findHandler(tag, portal) + if idx > -1 { + err := newError("The tag[", tag, "] already exists") + err.WriteToLog(session.ExportIDToError(ctx)) + return err + } + err := r.addHandler(ctx, portal) + if err != nil { + return err + } + newError("The portal has been added successfully through the API.[", tag, "]").WriteToLog(session.ExportIDToError(ctx)) + return nil +} + +// RemovePortal Implement the Manager interface. +func (r *Reverse) RemovePortal(ctx context.Context, tag string) error { + p, idx, err := r.findHandler(tag, &Portal{}) + if err != nil { + return err + } + err = p.Close() + if err != nil { + return err + } + + r.portals = append(r.portals[:idx], r.portals[idx+1:]...) + newError("The bridge has been removed through the API. [", tag, "] ").WriteToLog(session.ExportIDToError(ctx)) + return nil +} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + r := new(Reverse) + if err := core.RequireFeatures(ctx, func(d routing.Dispatcher, om outbound.Manager) error { + return r.Init(config.(*Config), d, om) + }); err != nil { + return nil, err + } + return r, nil + })) +} diff --git a/features/reverse/reverse.go b/features/reverse/reverse.go new file mode 100644 index 000000000000..1fb7fb16fbca --- /dev/null +++ b/features/reverse/reverse.go @@ -0,0 +1,59 @@ +package reverse + +import ( + "context" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/features" +) + +// Handler Reverser feature. +// +// xray:api:alpha +type Handler interface { + common.Runnable + common.Closable + + GetTag() string + GetDomain() string +} + +// Manager Manage Reverse Objects. +// +// xray:api:alpha +type Manager interface { + features.Feature + + AddBridge(ctx context.Context, bridge Handler) error + RemoveBridge(ctx context.Context, tag string) error + AddPortal(ctx context.Context, portal Handler) error + RemovePortal(ctx context.Context, tag string) error +} + +// ManagerType returns the type of Manager interface. Can be used for implementing common.HasType. +// +// xray:api:alpha +func ManagerType() interface{} { + return (*Manager)(nil) +} + +// NoopManager is an implementation of Manager, which doesn't has actual functionalities. +type NoopManager struct{} + +// Type implements common.HasType. +func (NoopManager) Type() interface{} { + return ManagerType() +} + +// Start implements common.Runnable. +func (NoopManager) Start() error { return nil } + +// Close implements common.Closable. +func (NoopManager) Close() error { return nil } + +func (NoopManager) AddBridge(ctx context.Context, bridge Handler) error { return nil } + +func (NoopManager) RemoveBridge(ctx context.Context, tag string) error { return nil } + +func (NoopManager) AddPortal(ctx context.Context, portal Handler) error { return nil } + +func (NoopManager) RemovePortal(ctx context.Context, tag string) error { return nil } diff --git a/infra/conf/api.go b/infra/conf/api.go index 02bc77dc22c2..bdb28792c8e3 100644 --- a/infra/conf/api.go +++ b/infra/conf/api.go @@ -6,6 +6,7 @@ import ( "github.com/xtls/xray-core/app/commander" loggerservice "github.com/xtls/xray-core/app/log/command" handlerservice "github.com/xtls/xray-core/app/proxyman/command" + reverseservice "github.com/xtls/xray-core/app/reverse/command" statsservice "github.com/xtls/xray-core/app/stats/command" "github.com/xtls/xray-core/common/serial" ) @@ -20,13 +21,15 @@ func (c *APIConfig) Build() (*commander.Config, error) { return nil, newError("API tag can't be empty.") } - services := make([]*serial.TypedMessage, 0, 16) + services := make([]*serial.TypedMessage, 0, 20) for _, s := range c.Services { switch strings.ToLower(s) { case "reflectionservice": services = append(services, serial.ToTypedMessage(&commander.ReflectionConfig{})) case "handlerservice": services = append(services, serial.ToTypedMessage(&handlerservice.Config{})) + case "reverseservice": + services = append(services, serial.ToTypedMessage(&reverseservice.Config{})) case "loggerservice": services = append(services, serial.ToTypedMessage(&loggerservice.Config{})) case "statsservice": diff --git a/testing/scenarios/reverse_test.go b/testing/scenarios/reverse_test.go index cbe9abdf6004..8b0d75e742e2 100644 --- a/testing/scenarios/reverse_test.go +++ b/testing/scenarios/reverse_test.go @@ -1,15 +1,19 @@ package scenarios import ( + "context" + "fmt" + "io" + "strings" "testing" "time" - "golang.org/x/sync/errgroup" - + "github.com/xtls/xray-core/app/commander" "github.com/xtls/xray-core/app/log" "github.com/xtls/xray-core/app/policy" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/reverse" + "github.com/xtls/xray-core/app/reverse/command" "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common" clog "github.com/xtls/xray-core/common/log" @@ -25,6 +29,8 @@ import ( "github.com/xtls/xray-core/proxy/vmess/inbound" "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" ) func TestReverseProxy(t *testing.T) { @@ -393,3 +399,277 @@ func TestReverseProxyLongRunning(t *testing.T) { } } } + +func TestReverseProxyAPI(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + externalPort := tcp.PickPort() + reversePort := tcp.PickPort() + sCmdPort := tcp.PickPort() + cCmdPort := tcp.PickPort() + + serverConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&commander.Config{ + Tag: "api", + Service: []*serial.TypedMessage{ + serial.ToTypedMessage(&command.Config{}), + }, + }), + serial.ToTypedMessage(&reverse.Config{}), + serial.ToTypedMessage(&router.Config{ + Rule: []*router.RoutingRule{ + { + InboundTag: []string{"api"}, + TargetTag: &router.RoutingRule_Tag{ + Tag: "api", + }, + }, + { + Domain: []*router.Domain{ + {Type: router.Domain_Full, Value: "test.example.com"}, + }, + TargetTag: &router.RoutingRule_Tag{ + Tag: "portal", + }, + }, + { + InboundTag: []string{"external"}, + TargetTag: &router.RoutingRule_Tag{ + Tag: "portal", + }, + }, + }, + }), + serial.ToTypedMessage(&policy.Config{ + Level: map[uint32]*policy.Policy{ + 0: { + Timeout: &policy.Policy_Timeout{ + UplinkOnly: &policy.Second{Value: 0}, + DownlinkOnly: &policy.Second{Value: 0}, + }, + }, + }, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + Tag: "external", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(externalPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + { + Tag: "api", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(sCmdPort), + Listen: net.NewIPOrDomain(net.AnyIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + Networks: []net.Network{net.Network_TCP}, + }), + }, + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(reversePort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&commander.Config{ + Tag: "api", + Service: []*serial.TypedMessage{ + serial.ToTypedMessage(&command.Config{}), + }, + }), + serial.ToTypedMessage(&reverse.Config{}), + serial.ToTypedMessage(&router.Config{ + Rule: []*router.RoutingRule{ + { + InboundTag: []string{"api"}, + TargetTag: &router.RoutingRule_Tag{ + Tag: "api", + }, + }, + { + Domain: []*router.Domain{ + {Type: router.Domain_Full, Value: "test.example.com"}, + }, + TargetTag: &router.RoutingRule_Tag{ + Tag: "reverse", + }, + }, + { + InboundTag: []string{"bridge"}, + TargetTag: &router.RoutingRule_Tag{ + Tag: "freedom", + }, + }, + }, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(clientPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + { + Tag: "api", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(cCmdPort), + Listen: net.NewIPOrDomain(net.AnyIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + Networks: []net.Network{net.Network_TCP}, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + Tag: "freedom", + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + { + Tag: "reverse", + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(reversePort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + }, + }, + } + + /* + serial.ToTypedMessage(&reverse.Config{ + BridgeConfig: []*reverse.BridgeConfig{ + { + Tag: "bridge", + Domain: "test.example.com", + }, + }, + }), + */ + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + common.Must(err) + defer CloseAllServers(servers) + + if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != io.EOF && + /*We might wish to drain the connection*/ + (err != nil && !strings.HasSuffix(err.Error(), "i/o timeout")) { + t.Fatal("expected error: ", err) + } + + // add client bridge + cCmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cCmdPort), grpc.WithInsecure(), grpc.WithBlock()) + common.Must(err) + defer cCmdConn.Close() + + ctx := context.Background() + clientRSClient := command.NewReverseServiceClient(cCmdConn) + bridgeResp, err := clientRSClient.AddBridge(ctx, &command.AddBridgeRequest{Config: &reverse.BridgeConfig{ + Tag: "bridge", + Domain: "test.example.com", + }}) + common.Must(err) + if bridgeResp == nil { + t.Fatal("nil response") + } + + // add server portal + sCmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", sCmdPort), grpc.WithInsecure(), grpc.WithBlock()) + common.Must(err) + defer sCmdConn.Close() + + serverRSClient := command.NewReverseServiceClient(sCmdConn) + portalResp, err := serverRSClient.AddPortal(ctx, &command.AddPortalRequest{Config: &reverse.PortalConfig{ + Tag: "portal", + Domain: "test.example.com", + }}) + common.Must(err) + if portalResp == nil { + t.Fatal("nil response") + } + time.Sleep(time.Second * 1) + + var errg errgroup.Group + for i := 0; i < 32; i++ { + errg.Go(testTCPConn(externalPort, 10240*1024, time.Second*40)) + } + + if err := errg.Wait(); err != nil { + t.Fatal(err) + } + + _, err = serverRSClient.RemovePortal(ctx, &command.RemovePortalRequest{Tag: "portal"}) + if err != nil { + t.Fatal(err) + } + _, err = clientRSClient.RemoveBridge(ctx, &command.RemoveBridgeRequest{Tag: "bridge"}) + if err != nil { + t.Fatal(err) + } +}