From afcabad915372c7e75bbb3d4a66d1cbdf78f3c8f Mon Sep 17 00:00:00 2001 From: Page Fault Date: Thu, 25 Jun 2020 07:03:45 +0000 Subject: [PATCH 1/6] simplify grpc api --- api/control/control.go | 18 +-- api/service/api.pb.go | 226 ++++++++++++++----------------------- api/service/api.proto | 15 +-- api/service/server.go | 54 +++++---- api/service/server_test.go | 38 ++++--- 5 files changed, 153 insertions(+), 198 deletions(-) diff --git a/api/control/control.go b/api/control/control.go index b13ae8b53..5a0680b9f 100644 --- a/api/control/control.go +++ b/api/control/control.go @@ -92,14 +92,16 @@ func (o *apiController) setUsers(apiClient service.TrojanServerServiceClient) er defer stream.CloseSend() req := &service.SetUsersRequest{ - User: &service.User{ - Password: *o.password, - Hash: *o.hash, - }, - IpLimit: int32(*o.ipLimit), - SpeedLimit: &service.Speed{ - UploadSpeed: uint64(*o.uploadSpeedLimit), - DownloadSpeed: uint64(*o.downloadSpeedLimit), + Status: &service.UserStatus{ + User: &service.User{ + Password: *o.password, + Hash: *o.hash, + }, + IpLimit: int32(*o.ipLimit), + SpeedLimit: &service.Speed{ + UploadSpeed: uint64(*o.uploadSpeedLimit), + DownloadSpeed: uint64(*o.downloadSpeedLimit), + }, }, } if *o.add { diff --git a/api/service/api.pb.go b/api/service/api.pb.go index 8bc37e1e3..90e916da0 100644 --- a/api/service/api.pb.go +++ b/api/service/api.pb.go @@ -1,21 +1,22 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.23.0 -// protoc v3.11.4 +// protoc v3.12.0 // source: api.proto package service import ( context "context" + reflect "reflect" + sync "sync" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( @@ -194,7 +195,7 @@ type User struct { unknownFields protoimpl.UnknownFields Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` - Hash string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` //optional + Hash string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` } func (x *User) Reset() { @@ -491,8 +492,7 @@ type ListUsersResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` - Status *UserStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + Status *UserStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` } func (x *ListUsersResponse) Reset() { @@ -527,13 +527,6 @@ func (*ListUsersResponse) Descriptor() ([]byte, []int) { return file_api_proto_rawDescGZIP(), []int{7} } -func (x *ListUsersResponse) GetUser() *User { - if x != nil { - return x.User - } - return nil -} - func (x *ListUsersResponse) GetStatus() *UserStatus { if x != nil { return x.Status @@ -595,8 +588,7 @@ type GetUsersResponse struct { Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` Info string `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` - User *User `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"` - Status *UserStatus `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + Status *UserStatus `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` } func (x *GetUsersResponse) Reset() { @@ -645,13 +637,6 @@ func (x *GetUsersResponse) GetInfo() string { return "" } -func (x *GetUsersResponse) GetUser() *User { - if x != nil { - return x.User - } - return nil -} - func (x *GetUsersResponse) GetStatus() *UserStatus { if x != nil { return x.Status @@ -664,11 +649,8 @@ type SetUsersRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` - Operation SetUsersRequest_Operation `protobuf:"varint,2,opt,name=operation,proto3,enum=trojan.api.SetUsersRequest_Operation" json:"operation,omitempty"` - SpeedLimit *Speed `protobuf:"bytes,3,opt,name=speed_limit,json=speedLimit,proto3" json:"speed_limit,omitempty"` - IpLimit int32 `protobuf:"varint,4,opt,name=ip_limit,json=ipLimit,proto3" json:"ip_limit,omitempty"` - TrafficTotal *Traffic `protobuf:"bytes,5,opt,name=traffic_total,json=trafficTotal,proto3" json:"traffic_total,omitempty"` + Status *UserStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Operation SetUsersRequest_Operation `protobuf:"varint,2,opt,name=operation,proto3,enum=trojan.api.SetUsersRequest_Operation" json:"operation,omitempty"` } func (x *SetUsersRequest) Reset() { @@ -703,9 +685,9 @@ func (*SetUsersRequest) Descriptor() ([]byte, []int) { return file_api_proto_rawDescGZIP(), []int{10} } -func (x *SetUsersRequest) GetUser() *User { +func (x *SetUsersRequest) GetStatus() *UserStatus { if x != nil { - return x.User + return x.Status } return nil } @@ -717,27 +699,6 @@ func (x *SetUsersRequest) GetOperation() SetUsersRequest_Operation { return SetUsersRequest_Add } -func (x *SetUsersRequest) GetSpeedLimit() *Speed { - if x != nil { - return x.SpeedLimit - } - return nil -} - -func (x *SetUsersRequest) GetIpLimit() int32 { - if x != nil { - return x.IpLimit - } - return 0 -} - -func (x *SetUsersRequest) GetTrafficTotal() *Traffic { - if x != nil { - return x.TrafficTotal - } - return nil -} - type SetUsersResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -845,74 +806,61 @@ var file_api_proto_rawDesc = []byte{ 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x52, 0x0c, 0x73, 0x70, 0x65, 0x65, 0x64, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x69, 0x0a, 0x11, 0x4c, 0x69, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x37, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x96, - 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, - 0x6f, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb3, 0x02, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x72, 0x6f, 0x6a, - 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, - 0x72, 0x12, 0x43, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, - 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x52, 0x0a, - 0x73, 0x70, 0x65, 0x65, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x70, - 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x69, 0x70, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, - 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, - 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, - 0x63, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x22, - 0x2c, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x07, 0x0a, 0x03, - 0x41, 0x64, 0x64, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x10, - 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x10, 0x02, 0x22, 0x40, 0x0a, - 0x10, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x32, - 0x64, 0x0a, 0x13, 0x54, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, - 0x66, 0x66, 0x69, 0x63, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xfd, 0x01, 0x0a, 0x13, 0x54, 0x72, 0x6f, 0x6a, 0x61, 0x6e, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, - 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x74, 0x72, 0x6f, - 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, - 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x08, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x37, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x70, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x6f, + 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb4, 0x01, 0x0a, 0x0f, 0x53, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x43, + 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x25, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x07, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x10, + 0x02, 0x22, 0x40, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, + 0x6e, 0x66, 0x6f, 0x32, 0x64, 0x0a, 0x13, 0x54, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x47, 0x65, + 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, + 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xfd, 0x01, 0x0a, 0x13, 0x54, 0x72, + 0x6f, 0x6a, 0x61, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1c, + 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74, + 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x4b, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x72, + 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, + 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x08, + 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, + 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x3b, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -952,28 +900,24 @@ var file_api_proto_depIdxs = []int32{ 3, // 4: trojan.api.GetTrafficRequest.user:type_name -> trojan.api.User 1, // 5: trojan.api.GetTrafficResponse.traffic_total:type_name -> trojan.api.Traffic 2, // 6: trojan.api.GetTrafficResponse.speed_current:type_name -> trojan.api.Speed - 3, // 7: trojan.api.ListUsersResponse.user:type_name -> trojan.api.User - 4, // 8: trojan.api.ListUsersResponse.status:type_name -> trojan.api.UserStatus - 3, // 9: trojan.api.GetUsersRequest.user:type_name -> trojan.api.User - 3, // 10: trojan.api.GetUsersResponse.user:type_name -> trojan.api.User - 4, // 11: trojan.api.GetUsersResponse.status:type_name -> trojan.api.UserStatus - 3, // 12: trojan.api.SetUsersRequest.user:type_name -> trojan.api.User - 0, // 13: trojan.api.SetUsersRequest.operation:type_name -> trojan.api.SetUsersRequest.Operation - 2, // 14: trojan.api.SetUsersRequest.speed_limit:type_name -> trojan.api.Speed - 1, // 15: trojan.api.SetUsersRequest.traffic_total:type_name -> trojan.api.Traffic - 5, // 16: trojan.api.TrojanClientService.GetTraffic:input_type -> trojan.api.GetTrafficRequest - 7, // 17: trojan.api.TrojanServerService.ListUsers:input_type -> trojan.api.ListUsersRequest - 9, // 18: trojan.api.TrojanServerService.GetUsers:input_type -> trojan.api.GetUsersRequest - 11, // 19: trojan.api.TrojanServerService.SetUsers:input_type -> trojan.api.SetUsersRequest - 6, // 20: trojan.api.TrojanClientService.GetTraffic:output_type -> trojan.api.GetTrafficResponse - 8, // 21: trojan.api.TrojanServerService.ListUsers:output_type -> trojan.api.ListUsersResponse - 10, // 22: trojan.api.TrojanServerService.GetUsers:output_type -> trojan.api.GetUsersResponse - 12, // 23: trojan.api.TrojanServerService.SetUsers:output_type -> trojan.api.SetUsersResponse - 20, // [20:24] is the sub-list for method output_type - 16, // [16:20] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 4, // 7: trojan.api.ListUsersResponse.status:type_name -> trojan.api.UserStatus + 3, // 8: trojan.api.GetUsersRequest.user:type_name -> trojan.api.User + 4, // 9: trojan.api.GetUsersResponse.status:type_name -> trojan.api.UserStatus + 4, // 10: trojan.api.SetUsersRequest.status:type_name -> trojan.api.UserStatus + 0, // 11: trojan.api.SetUsersRequest.operation:type_name -> trojan.api.SetUsersRequest.Operation + 5, // 12: trojan.api.TrojanClientService.GetTraffic:input_type -> trojan.api.GetTrafficRequest + 7, // 13: trojan.api.TrojanServerService.ListUsers:input_type -> trojan.api.ListUsersRequest + 9, // 14: trojan.api.TrojanServerService.GetUsers:input_type -> trojan.api.GetUsersRequest + 11, // 15: trojan.api.TrojanServerService.SetUsers:input_type -> trojan.api.SetUsersRequest + 6, // 16: trojan.api.TrojanClientService.GetTraffic:output_type -> trojan.api.GetTrafficResponse + 8, // 17: trojan.api.TrojanServerService.ListUsers:output_type -> trojan.api.ListUsersResponse + 10, // 18: trojan.api.TrojanServerService.GetUsers:output_type -> trojan.api.GetUsersResponse + 12, // 19: trojan.api.TrojanServerService.SetUsers:output_type -> trojan.api.SetUsersResponse + 16, // [16:20] is the sub-list for method output_type + 12, // [12:16] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_api_proto_init() } @@ -1236,7 +1180,7 @@ type TrojanServerServiceClient interface { ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (TrojanServerService_ListUsersClient, error) // obtain specified user's info GetUsers(ctx context.Context, opts ...grpc.CallOption) (TrojanServerService_GetUsersClient, error) - // setup exsisting users' config + // setup existing users' config SetUsers(ctx context.Context, opts ...grpc.CallOption) (TrojanServerService_SetUsersClient, error) } @@ -1348,7 +1292,7 @@ type TrojanServerServiceServer interface { ListUsers(*ListUsersRequest, TrojanServerService_ListUsersServer) error // obtain specified user's info GetUsers(TrojanServerService_GetUsersServer) error - // setup exsisting users' config + // setup existing users' config SetUsers(TrojanServerService_SetUsersServer) error } diff --git a/api/service/api.proto b/api/service/api.proto index 4321bc342..bff6dd8b3 100644 --- a/api/service/api.proto +++ b/api/service/api.proto @@ -14,7 +14,7 @@ message Speed { message User { string password = 1; - string hash = 2; //optional + string hash = 2; } message UserStatus { @@ -42,8 +42,7 @@ message ListUsersRequest { } message ListUsersResponse { - User user = 1; - UserStatus status = 2; + UserStatus status = 1; } message GetUsersRequest { @@ -53,21 +52,17 @@ message GetUsersRequest { message GetUsersResponse { bool success = 1; string info = 2; - User user = 3; - UserStatus status = 4; + UserStatus status = 3; } message SetUsersRequest { - User user = 1; enum Operation { Add = 0; Delete = 1; Modify = 2; } + UserStatus status = 1; Operation operation = 2; - Speed speed_limit = 3; - int32 ip_limit = 4; - Traffic traffic_total = 5; } message SetUsersResponse { @@ -84,6 +79,6 @@ service TrojanServerService { rpc ListUsers(ListUsersRequest) returns(stream ListUsersResponse){} // obtain specified user's info rpc GetUsers(stream GetUsersRequest) returns(stream GetUsersResponse){} - // setup exsisting users' config + // setup existing users' config rpc SetUsers(stream SetUsersRequest) returns(stream SetUsersResponse){} } \ No newline at end of file diff --git a/api/service/server.go b/api/service/server.go index d5e6079fb..cc198f34b 100644 --- a/api/service/server.go +++ b/api/service/server.go @@ -40,7 +40,7 @@ func (s *ServerAPI) GetUsers(stream TrojanServerService_GetUsersServer) error { if !valid { stream.Send(&GetUsersResponse{ Success: false, - Info: "Invalid user: " + req.User.Hash, + Info: "invalid user: " + req.User.Hash, }) continue } @@ -85,38 +85,46 @@ func (s *ServerAPI) SetUsers(stream TrojanServerService_SetUsersServer) error { if err != nil { return err } - if req.User == nil { - return common.NewError("User is unspecified") + if req.Status == nil { + return common.NewError("status is unspecified") } - if req.User.Hash == "" { - req.User.Hash = common.SHA224String(req.User.Password) + if req.Status.User.Hash == "" { + req.Status.User.Hash = common.SHA224String(req.Status.User.Password) } switch req.Operation { case SetUsersRequest_Add: - err = s.auth.AddUser(req.User.Hash) - if req.SpeedLimit != nil { - valid, user := s.auth.AuthUser(req.User.Hash) + if err = s.auth.AddUser(req.Status.User.Hash); err != nil { + err = common.NewError("failed to add new user").Base(err) + break + } + if req.Status.SpeedLimit != nil { + valid, user := s.auth.AuthUser(req.Status.User.Hash) if !valid { - return common.NewError("failed to add new user") + err = common.NewError("failed to auth new user").Base(err) + continue + } + if req.Status.SpeedLimit != nil { + user.SetSpeedLimit(int(req.Status.SpeedLimit.DownloadSpeed), int(req.Status.SpeedLimit.UploadSpeed)) } - user.SetSpeedLimit(int(req.SpeedLimit.DownloadSpeed), int(req.SpeedLimit.UploadSpeed)) + if req.Status.TrafficTotal != nil { + user.SetTraffic(req.Status.TrafficTotal.DownloadTraffic, req.Status.TrafficTotal.UploadTraffic) + } + user.SetIPLimit(int(req.Status.IpLimit)) } case SetUsersRequest_Delete: - err = s.auth.DelUser(req.User.Hash) + err = s.auth.DelUser(req.Status.User.Hash) case SetUsersRequest_Modify: - valid, user := s.auth.AuthUser(req.User.Hash) + valid, user := s.auth.AuthUser(req.Status.User.Hash) if !valid { - err = common.NewError("invalid user " + req.User.Hash) + err = common.NewError("invalid user " + req.Status.User.Hash) } else { - if req.SpeedLimit.DownloadSpeed > 0 || req.SpeedLimit.UploadSpeed > 0 { - user.SetSpeedLimit(int(req.SpeedLimit.DownloadSpeed), int(req.SpeedLimit.UploadSpeed)) - } - if req.IpLimit > 0 { - user.SetIPLimit(int(req.IpLimit)) + if req.Status.SpeedLimit != nil { + user.SetSpeedLimit(int(req.Status.SpeedLimit.DownloadSpeed), int(req.Status.SpeedLimit.UploadSpeed)) } - if req.TrafficTotal.DownloadTraffic > 0 || req.TrafficTotal.UploadTraffic > 0 { - user.SetTraffic(req.TrafficTotal.DownloadTraffic, req.TrafficTotal.UploadTraffic) + if req.Status.TrafficTotal != nil { + user.SetTraffic(req.Status.TrafficTotal.DownloadTraffic, req.Status.TrafficTotal.UploadTraffic) } + user.SetIPLimit(int(req.Status.IpLimit)) } } if err != nil { @@ -142,10 +150,10 @@ func (s *ServerAPI) ListUsers(req *ListUsersRequest, stream TrojanServerService_ ipLimit := user.GetIPLimit() ipCurrent := user.GetIP() err := stream.Send(&ListUsersResponse{ - User: &User{ - Hash: user.Hash(), - }, Status: &UserStatus{ + User: &User{ + Hash: user.Hash(), + }, TrafficTotal: &Traffic{ DownloadTraffic: downloadTraffic, UploadTraffic: uploadTraffic, diff --git a/api/service/server_test.go b/api/service/server_test.go index 9273071d5..9463b3e31 100644 --- a/api/service/server_test.go +++ b/api/service/server_test.go @@ -42,8 +42,8 @@ func TestServerAPI(t *testing.T) { if err != nil { break } - fmt.Println(resp.User.Hash) - if resp.User.Hash != "hash1234" { + fmt.Println(resp.Status.User.Hash) + if resp.Status.User.Hash != "hash1234" { t.Fail() } fmt.Println(resp.Status.SpeedCurrent) @@ -70,8 +70,10 @@ func TestServerAPI(t *testing.T) { stream3, err := server.SetUsers(ctx) stream3.Send(&SetUsersRequest{ - User: &User{ - Hash: "hash1234", + Status: &UserStatus{ + User: &User{ + Hash: "hash1234", + }, }, Operation: SetUsersRequest_Delete, }) @@ -84,8 +86,10 @@ func TestServerAPI(t *testing.T) { t.Fail() } stream3.Send(&SetUsersRequest{ - User: &User{ - Hash: "newhash", + Status: &UserStatus{ + User: &User{ + Hash: "newhash", + }, }, Operation: SetUsersRequest_Add, }) @@ -98,18 +102,20 @@ func TestServerAPI(t *testing.T) { t.Fail() } stream3.Send(&SetUsersRequest{ - User: &User{ - Hash: "newhash", + Status: &UserStatus{ + User: &User{ + Hash: "newhash", + }, + SpeedLimit: &Speed{ + DownloadSpeed: 5000, + UploadSpeed: 3000, + }, + TrafficTotal: &Traffic{ + DownloadTraffic: 1, + UploadTraffic: 1, + }, }, Operation: SetUsersRequest_Modify, - SpeedLimit: &Speed{ - DownloadSpeed: 5000, - UploadSpeed: 3000, - }, - TrafficTotal: &Traffic{ - DownloadTraffic: 1, - UploadTraffic: 1, - }, }) go func() { for { From aac9b0ad478eb7305a246c51bbff683758899998 Mon Sep 17 00:00:00 2001 From: Page Fault Date: Thu, 25 Jun 2020 12:32:23 +0000 Subject: [PATCH 2/6] fix socks, tproxy udp --- statistic/memory/memory.go | 8 +- tunnel/dokodemo/conn.go | 16 ++-- tunnel/dokodemo/server.go | 2 +- tunnel/socks/config.go | 15 ---- tunnel/socks/conn.go | 10 --- tunnel/socks/server.go | 15 ++-- tunnel/socks/socks_test.go | 3 - tunnel/tproxy/server.go | 168 ++++++++++++++++++++++--------------- 8 files changed, 121 insertions(+), 116 deletions(-) delete mode 100644 tunnel/socks/config.go diff --git a/statistic/memory/memory.go b/statistic/memory/memory.go index 015f36d84..cf7349bb9 100644 --- a/statistic/memory/memory.go +++ b/statistic/memory/memory.go @@ -192,7 +192,7 @@ func (a *Authenticator) AddUser(hash string) error { a.Lock() defer a.Unlock() if _, found := a.users[hash]; found { - return common.NewError("Hash " + hash + " is already exist") + return common.NewError("hash " + hash + " is already exist") } ctx, cancel := context.WithCancel(a.ctx) meter := &User{ @@ -221,9 +221,11 @@ func (a *Authenticator) DelUser(hash string) error { func (a *Authenticator) ListUsers() []statistic.User { a.RLock() defer a.RUnlock() - result := make([]statistic.User, 0, len(a.users)) + result := make([]statistic.User, len(a.users)) + i := 0 for _, u := range a.users { - result = append(result, u) + result[i] = u + i++ } return result } diff --git a/tunnel/dokodemo/conn.go b/tunnel/dokodemo/conn.go index 3d95f3e66..7832549fd 100644 --- a/tunnel/dokodemo/conn.go +++ b/tunnel/dokodemo/conn.go @@ -25,12 +25,12 @@ func (c *Conn) Metadata() *tunnel.Metadata { // TODO implement net.PacketConn type PacketConn struct { net.PacketConn - M *tunnel.Metadata - Input chan []byte - Output chan []byte - Source net.Addr - Ctx context.Context - Cancel context.CancelFunc + M *tunnel.Metadata + Input chan []byte + Output chan []byte + Source net.Addr + Context context.Context + Cancel context.CancelFunc } func (c *PacketConn) Close() error { @@ -58,7 +58,7 @@ func (c *PacketConn) ReadWithMetadata(p []byte) (int, *tunnel.Metadata, error) { case payload := <-c.Input: n := copy(p, payload) return n, c.M, nil - case <-c.Ctx.Done(): + case <-c.Context.Done(): return 0, nil, common.NewError("dokodemo packet conn closed") } } @@ -67,7 +67,7 @@ func (c *PacketConn) WriteWithMetadata(p []byte, m *tunnel.Metadata) (int, error select { case c.Output <- p: return len(p), nil - case <-c.Ctx.Done(): + case <-c.Context.Done(): return 0, common.NewError("dokodemo packet conn failed to write") } } diff --git a/tunnel/dokodemo/server.go b/tunnel/dokodemo/server.go index 1f8f50cc8..719124640 100644 --- a/tunnel/dokodemo/server.go +++ b/tunnel/dokodemo/server.go @@ -54,7 +54,7 @@ func (s *Server) dispatchLoop() { M: fixedMetadata, Source: addr, PacketConn: s.udpListener, - Ctx: ctx, + Context: ctx, Cancel: cancel, } s.mapping[addr.String()] = conn diff --git a/tunnel/socks/config.go b/tunnel/socks/config.go deleted file mode 100644 index dfd68a56e..000000000 --- a/tunnel/socks/config.go +++ /dev/null @@ -1,15 +0,0 @@ -package socks - -import "github.com/p4gefau1t/trojan-go/config" - -type Config struct { - UDPTimeout int `json:"udp_timeout" yaml:"udp-timeout"` -} - -func init() { - config.RegisterConfigCreator(Name, func() interface{} { - return &Config{ - UDPTimeout: 30, - } - }) -} diff --git a/tunnel/socks/conn.go b/tunnel/socks/conn.go index 3c854d5ef..0e90d9716 100644 --- a/tunnel/socks/conn.go +++ b/tunnel/socks/conn.go @@ -3,7 +3,6 @@ package socks import ( "bytes" "net" - "time" "github.com/p4gefau1t/trojan-go/common" "github.com/p4gefau1t/trojan-go/log" @@ -22,7 +21,6 @@ func (c *Conn) Metadata() *tunnel.Metadata { type PacketConn struct { net.PacketConn srcAddr net.Addr - timeout time.Duration } func (c *PacketConn) Close() error { @@ -69,11 +67,3 @@ func (c *PacketConn) ReadWithMetadata(payload []byte) (int, *tunnel.Metadata, er Address: addr, }, nil } - -func NewPacketConn(packet net.PacketConn, timeout time.Duration) *PacketConn { - conn := &PacketConn{ - PacketConn: packet, - timeout: timeout, - } - return conn -} diff --git a/tunnel/socks/server.go b/tunnel/socks/server.go index 8ac2c0eaa..601b0132c 100644 --- a/tunnel/socks/server.go +++ b/tunnel/socks/server.go @@ -7,10 +7,8 @@ import ( "io" "io/ioutil" "net" - "time" "github.com/p4gefau1t/trojan-go/common" - "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/log" "github.com/p4gefau1t/trojan-go/tunnel" ) @@ -30,7 +28,6 @@ type Server struct { packetChan chan tunnel.PacketConn underlay tunnel.Server localHost string - timeout time.Duration ctx context.Context cancel context.CancelFunc } @@ -141,14 +138,18 @@ func (s *Server) acceptLoop() { log.Error(common.NewError("socks5 failed to bind udp").Base(err)) return } - s.packetChan <- NewPacketConn(l, s.timeout) + packetConn := &PacketConn{ + PacketConn: l, + } + s.packetChan <- packetConn log.Info("socks5 udp session") if err := s.associate(newConn, associateAddr); err != nil { log.Error(common.NewError("socks5 failed to respond to associate request").Base(err)) return } - buf := [1]byte{} + buf := [16]byte{} newConn.Read(buf[:]) + packetConn.Close() log.Debug("socks5 udp session ends") default: log.Error(common.NewError(fmt.Sprintf("unknown socks5 command %d", newConn.metadata.Command))) @@ -160,7 +161,6 @@ func (s *Server) acceptLoop() { // NewServer create a socks server func NewServer(ctx context.Context, underlay tunnel.Server) (tunnel.Server, error) { - cfg := config.FromContext(ctx, Name).(*Config) ctx, cancel := context.WithCancel(ctx) server := &Server{ underlay: underlay, @@ -168,8 +168,9 @@ func NewServer(ctx context.Context, underlay tunnel.Server) (tunnel.Server, erro cancel: cancel, connChan: make(chan tunnel.Conn, 32), packetChan: make(chan tunnel.PacketConn, 32), - timeout: time.Duration(cfg.UDPTimeout) * time.Second, + localHost: "", } + // TODO localhost go server.acceptLoop() log.Debug("socks server created") return server, nil diff --git a/tunnel/socks/socks_test.go b/tunnel/socks/socks_test.go index 6eba0ae39..3c82cfc13 100644 --- a/tunnel/socks/socks_test.go +++ b/tunnel/socks/socks_test.go @@ -22,9 +22,6 @@ func TestSocks(t *testing.T) { LocalHost: "127.0.0.1", LocalPort: port, }) - ctx = config.WithConfig(ctx, Name, &Config{ - UDPTimeout: 30, - }) tcpServer, err := transport.NewServer(ctx, nil) common.Must(err) addr := tunnel.NewAddressFromHostPort("tcp", "127.0.0.1", port) diff --git a/tunnel/tproxy/server.go b/tunnel/tproxy/server.go index 5aa857ec6..29f7c88c9 100644 --- a/tunnel/tproxy/server.go +++ b/tunnel/tproxy/server.go @@ -24,7 +24,7 @@ type Server struct { udpListener *net.UDPConn packetChan chan tunnel.PacketConn timeout time.Duration - mappingLock sync.Mutex + mappingLock sync.RWMutex mapping map[string]*dokodemo.PacketConn ctx context.Context cancel context.CancelFunc @@ -62,82 +62,112 @@ func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) { } func (s *Server) packetDispatchLoop() { - for { - buf := make([]byte, MaxPacketSize) - n, src, dst, err := tproxy.ReadFromUDP(s.udpListener, buf) - if err != nil { - select { - case <-s.ctx.Done(): - default: - log.Fatal(common.NewError("tproxy failed to read from udp socket").Base(err)) + type tproxyPacketInfo struct { + src *net.UDPAddr + dst *net.UDPAddr + payload []byte + } + packetQueue := make(chan *tproxyPacketInfo, 1024) + + go func() { + for { + buf := make([]byte, MaxPacketSize) + n, src, dst, err := tproxy.ReadFromUDP(s.udpListener, buf) + if err != nil { + select { + case <-s.ctx.Done(): + default: + log.Fatal(common.NewError("tproxy failed to read from udp socket").Base(err)) + } + s.Close() + return + } + log.Debug("udp packet from", src, "metadata", dst, "size", n) + packetQueue <- &tproxyPacketInfo{ + src: src, + dst: dst, + payload: buf[:n], } - s.Close() - return - } - log.Debug("udp packet from", src, "metadata", dst, "size", n) - s.mappingLock.Lock() - if conn, found := s.mapping[src.String()]; found { - conn.Input <- buf[:n] - s.mappingLock.Unlock() - continue } - log.Info("tproxy udp session, from", src, "metadata", dst) - - address, err := tunnel.NewAddressFromAddr("udp", dst.String()) - common.Must(err) - - ctx, cancel := context.WithCancel(s.ctx) - conn := &dokodemo.PacketConn{ - Input: make(chan []byte, 16), - Output: make(chan []byte, 16), - Source: src, - PacketConn: s.udpListener, - Ctx: ctx, - Cancel: cancel, - M: &tunnel.Metadata{ - Address: address, - }, + }() + + for { + var info *tproxyPacketInfo + select { + case info = <-packetQueue: + case <-s.ctx.Done(): + log.Debug("exiting") } - s.mapping[src.String()] = conn - s.mappingLock.Unlock() - conn.Input <- buf[:n] - s.packetChan <- conn - - go func(conn *dokodemo.PacketConn) { - defer conn.Close() - back, err := tproxy.DialUDP( - "udp", - &net.UDPAddr{ - IP: conn.M.IP, - Port: conn.M.Port, + + s.mappingLock.RLock() + conn, found := s.mapping[info.src.String()+"|"+info.dst.String()] + s.mappingLock.RUnlock() + + if !found { + ctx, cancel := context.WithCancel(s.ctx) + conn = &dokodemo.PacketConn{ + Input: make(chan []byte, 128), + Output: make(chan []byte, 128), + PacketConn: s.udpListener, + Context: ctx, + Cancel: cancel, + Source: info.src, + M: &tunnel.Metadata{ + Address: tunnel.NewAddressFromHostPort("udp", info.dst.IP.String(), info.dst.Port), }, - conn.Source.(*net.UDPAddr), - ) - if err != nil { - log.Error(common.NewError("failed to dial tproxy udp").Base(err)) - return } - defer back.Close() - for { - select { - case payload := <-conn.Output: - _, err := back.Write(payload) - if err != nil { - log.Error(common.NewError("tproxy udp write error").Base(err)) + + s.mappingLock.Lock() + s.mapping[info.src.String()+"|"+info.dst.String()] = conn + s.mappingLock.Unlock() + + log.Info("new tproxy udp session, from", info.src, "metadata", info.dst) + + go func(conn *dokodemo.PacketConn) { + defer conn.Close() + back, err := tproxy.DialUDP( + "udp", + &net.UDPAddr{ + IP: conn.M.IP, + Port: conn.M.Port, + }, + conn.Source.(*net.UDPAddr), + ) + if err != nil { + log.Error(common.NewError("failed to dial tproxy udp").Base(err)) + return + } + defer back.Close() + for { + select { + case payload := <-conn.Output: + n, err := back.Write(payload) + if err != nil { + log.Error(common.NewError("tproxy udp write error").Base(err)) + return + } + log.Debug("recv packet send back to", conn.Source.String(), "payload", len(payload), "sent", n) + case <-s.ctx.Done(): + log.Debug("exiting") + return + case <-time.After(s.timeout): + s.mappingLock.Lock() + delete(s.mapping, conn.Source.String()+"|"+conn.M.String()) + s.mappingLock.Unlock() + log.Debug("packet session timeout. closed", conn.Source.String()) return } - case <-s.ctx.Done(): - log.Debug("exiting") - return - case <-time.After(s.timeout): - s.mappingLock.Lock() - delete(s.mapping, conn.Source.String()) - s.mappingLock.Unlock() - log.Debug("packet session timeout. closed", conn.Source.String()) - return } - } - }(conn) + }(conn) + } + + select { + case conn.Input <- info.payload: + log.Debug("sending tproxy packet payload to", info.dst.String(), "size", len(info.payload)) + default: + // if we got too many packets, simply drop it + log.Warn("tproxy udp relay queue full!") + } } } From 07d9a08bcdce7dec7902f880f1a9ffa6f0903885 Mon Sep 17 00:00:00 2001 From: Page Fault Date: Thu, 25 Jun 2020 14:11:53 +0000 Subject: [PATCH 3/6] fix ip limit and tproxy udp --- test/scenario/proxy_test.go | 2 +- tunnel/tproxy/server.go | 9 +++++++-- tunnel/trojan/server.go | 11 +++++++++-- tunnel/websocket/client.go | 3 ++- tunnel/websocket/conn.go | 7 +++++++ tunnel/websocket/server.go | 3 ++- 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/test/scenario/proxy_test.go b/test/scenario/proxy_test.go index c1abcbbee..e2d7b5f18 100644 --- a/test/scenario/proxy_test.go +++ b/test/scenario/proxy_test.go @@ -145,7 +145,7 @@ ssl: websocket: enabled: true path: /ws - hostname: 127.0.0.1 + hostname: somedomainname.com shadowsocks: enabled: true method: AEAD_CHACHA20_POLY1305 diff --git a/tunnel/tproxy/server.go b/tunnel/tproxy/server.go index 29f7c88c9..db95f2156 100644 --- a/tunnel/tproxy/server.go +++ b/tunnel/tproxy/server.go @@ -97,6 +97,7 @@ func (s *Server) packetDispatchLoop() { case info = <-packetQueue: case <-s.ctx.Done(): log.Debug("exiting") + return } s.mappingLock.RLock() @@ -104,6 +105,8 @@ func (s *Server) packetDispatchLoop() { s.mappingLock.RUnlock() if !found { + address, err := tunnel.NewAddressFromAddr("udp", info.dst.String()) + common.Must(err) ctx, cancel := context.WithCancel(s.ctx) conn = &dokodemo.PacketConn{ Input: make(chan []byte, 128), @@ -113,7 +116,7 @@ func (s *Server) packetDispatchLoop() { Cancel: cancel, Source: info.src, M: &tunnel.Metadata{ - Address: tunnel.NewAddressFromHostPort("udp", info.dst.IP.String(), info.dst.Port), + Address: address, }, } @@ -121,7 +124,8 @@ func (s *Server) packetDispatchLoop() { s.mapping[info.src.String()+"|"+info.dst.String()] = conn s.mappingLock.Unlock() - log.Info("new tproxy udp session, from", info.src, "metadata", info.dst) + log.Info("new tproxy udp session from", info.src.String(), "metadata", info.dst.String()) + s.packetChan <- conn go func(conn *dokodemo.PacketConn) { defer conn.Close() @@ -138,6 +142,7 @@ func (s *Server) packetDispatchLoop() { return } defer back.Close() + log.Debug("udp packet daemon", conn.Source.String(), conn.M.String()) for { select { case payload := <-conn.Output: diff --git a/tunnel/trojan/server.go b/tunnel/trojan/server.go index 3f71ec92a..45f118632 100644 --- a/tunnel/trojan/server.go +++ b/tunnel/trojan/server.go @@ -28,6 +28,7 @@ type InboundConn struct { user statistic.User hash string metadata *tunnel.Metadata + ip string } func (c *InboundConn) Metadata() *tunnel.Metadata { @@ -50,7 +51,7 @@ func (c *InboundConn) Read(p []byte) (int, error) { func (c *InboundConn) Close() error { log.Info("user", c.hash, "from", c.Conn.RemoteAddr(), "tunneling to", c.metadata.Address, "closed", "sent:", common.HumanFriendlyTraffic(c.sent), "recv:", common.HumanFriendlyTraffic(c.recv)) - c.user.DelIP(c.Conn.RemoteAddr().String()) + c.user.DelIP(c.ip) return c.Conn.Close() } @@ -68,7 +69,13 @@ func (c *InboundConn) Auth() error { c.hash = string(userHash[:]) c.user = user - ok := user.AddIP(c.Conn.RemoteAddr().String()) + ip, _, err := net.SplitHostPort(c.Conn.RemoteAddr().String()) + if err != nil { + return common.NewError("failed to parse host:" + c.Conn.RemoteAddr().String()).Base(err) + } + + c.ip = ip + ok := user.AddIP(ip) if !ok { return common.NewError("ip limit reached") } diff --git a/tunnel/websocket/client.go b/tunnel/websocket/client.go index e003fd8cb..e80f06242 100644 --- a/tunnel/websocket/client.go +++ b/tunnel/websocket/client.go @@ -33,7 +33,8 @@ func (c *Client) DialConn(*tunnel.Address, tunnel.Tunnel) (tunnel.Conn, error) { return nil, common.NewError("websocket failed to handshake with server").Base(err) } return &OutboundConn{ - Conn: wsConn, + Conn: wsConn, + tcpConn: conn, }, nil } diff --git a/tunnel/websocket/conn.go b/tunnel/websocket/conn.go index a067db634..1af90bdd2 100644 --- a/tunnel/websocket/conn.go +++ b/tunnel/websocket/conn.go @@ -2,6 +2,7 @@ package websocket import ( "context" + "net" "github.com/p4gefau1t/trojan-go/tunnel" "golang.org/x/net/websocket" @@ -9,12 +10,18 @@ import ( type OutboundConn struct { *websocket.Conn + tcpConn net.Conn } func (c *OutboundConn) Metadata() *tunnel.Metadata { return nil } +func (c *OutboundConn) RemoteAddr() net.Addr { + // override RemoteAddr of websocket.Conn, or it will return some url from "Origin" + return c.tcpConn.RemoteAddr() +} + type InboundConn struct { OutboundConn ctx context.Context diff --git a/tunnel/websocket/server.go b/tunnel/websocket/server.go index e0c498158..85b6a35f7 100644 --- a/tunnel/websocket/server.go +++ b/tunnel/websocket/server.go @@ -130,7 +130,8 @@ func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) { return &InboundConn{ OutboundConn: OutboundConn{ - Conn: wsConn, + tcpConn: conn, + Conn: wsConn, }, ctx: ctx, cancel: cancel, From 0fa9071f8fbfed740b1e24a2bf1cce14270bab6b Mon Sep 17 00:00:00 2001 From: Page Fault Date: Thu, 25 Jun 2020 16:15:03 +0000 Subject: [PATCH 4/6] add api tls support, remove tproxy upstream --- api/service/client.go | 6 +- api/service/config.go | 2 +- api/service/server.go | 43 ++++++- docs/content/basic/full-config.md | 16 ++- go.mod | 1 - go.sum | 2 - tunnel/tproxy/server.go | 17 ++- tunnel/tproxy/{tproxy.go => tcp.go} | 60 ++++++++- tunnel/tproxy/udp.go | 191 ++++++++++++++++++++++++++++ 9 files changed, 316 insertions(+), 22 deletions(-) rename tunnel/tproxy/{tproxy.go => tcp.go} (55%) create mode 100644 tunnel/tproxy/udp.go diff --git a/api/service/client.go b/api/service/client.go index d28fd4e19..8000aa9f0 100644 --- a/api/service/client.go +++ b/api/service/client.go @@ -11,7 +11,6 @@ import ( "github.com/p4gefau1t/trojan-go/log" "github.com/p4gefau1t/trojan-go/statistic" "github.com/p4gefau1t/trojan-go/tunnel/trojan" - "google.golang.org/grpc" ) type ClientAPI struct { @@ -58,7 +57,10 @@ func RunClientAPI(ctx context.Context, auth statistic.Authenticator) error { if !cfg.API.Enabled { return nil } - server := grpc.NewServer() + server, err := newAPIServer(cfg) + if err != nil { + return err + } service := &ClientAPI{ ctx: ctx, auth: auth, diff --git a/api/service/config.go b/api/service/config.go index 5474416d3..47ae25e6e 100644 --- a/api/service/config.go +++ b/api/service/config.go @@ -8,7 +8,7 @@ type SSLConfig struct { Enabled bool `json,yaml:"enabled"` CertPath string `json:"cert" yaml:"cert"` KeyPath string `json:"key" yaml:"key"` - ClientAuth bool `json:"client_auth" yaml:"client-auth"` + VerifyClient bool `json:"verify_client" yaml:"verify-client"` ClientCertPath []string `json:"client_cert" yaml:"client-cert"` } diff --git a/api/service/server.go b/api/service/server.go index cc198f34b..732c22adc 100644 --- a/api/service/server.go +++ b/api/service/server.go @@ -2,8 +2,11 @@ package service import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "io" + "io/ioutil" "net" "github.com/p4gefau1t/trojan-go/api" @@ -13,6 +16,7 @@ import ( "github.com/p4gefau1t/trojan-go/statistic" "github.com/p4gefau1t/trojan-go/tunnel/trojan" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" ) type ServerAPI struct { @@ -177,15 +181,52 @@ func (s *ServerAPI) ListUsers(req *ListUsersRequest, stream TrojanServerService_ return nil } +func newAPIServer(cfg *Config) (*grpc.Server, error) { + var server *grpc.Server + if cfg.API.SSL.Enabled { + log.Info("api tls enabled") + keyPair, err := tls.LoadX509KeyPair(cfg.API.SSL.CertPath, cfg.API.SSL.KeyPath) + if err != nil { + return nil, common.NewError("failed to load key pair").Base(err) + } + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{keyPair}, + } + if cfg.API.SSL.VerifyClient { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert + tlsConfig.ClientCAs = x509.NewCertPool() + for _, path := range cfg.API.SSL.ClientCertPath { + log.Debug("loading client cert: " + path) + certBytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, common.NewError("failed to load cert file").Base(err) + } + ok := tlsConfig.ClientCAs.AppendCertsFromPEM(certBytes) + if !ok { + return nil, common.NewError("fnvalid client cert") + } + } + } + creds := credentials.NewTLS(tlsConfig) + server = grpc.NewServer(grpc.Creds(creds)) + } else { + server = grpc.NewServer() + } + return server, nil +} + func RunServerAPI(ctx context.Context, auth statistic.Authenticator) error { cfg := config.FromContext(ctx, Name).(*Config) if !cfg.API.Enabled { return nil } - server := grpc.NewServer() service := &ServerAPI{ auth: auth, } + server, err := newAPIServer(cfg) + if err != nil { + return err + } RegisterTrojanServerServiceServer(server, service) listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.API.APIHost, cfg.API.APIPort)) if err != nil { diff --git a/docs/content/basic/full-config.md b/docs/content/basic/full-config.md index 54b413cb3..34a62be0a 100644 --- a/docs/content/basic/full-config.md +++ b/docs/content/basic/full-config.md @@ -121,11 +121,11 @@ weight: 30 "enabled": false, "api_addr": "", "api_port": 0, - "api_tls": false, "ssl": { - "cert": "", + "enabled": false, "key": "", - "key_password": "", + "cert": "", + "verify_client": false, "client_cert": [] } } @@ -373,8 +373,14 @@ trojan-go基于gRPC提供了API,以支持服务端和客户端的管理和统 ```api_port```gRPC监听的端口。 -```api_tls```gRPC是否启用TLS传输(双向认证)。 +```ssl``` TLS相关设置 + +- ```enabled```是否使用开启TLS传输 + +- ```key```,```cert```服务器私钥和证书 + +- ```verify_client```是否认证客户端证书 -```ssl``` TLS相关设置,如果开启TLS传输和双向认证,所有选项为必填。其中```key```, ```cert```为API服务器使用的密钥和证书文件,```client_cert```为客户端使用的证书文件路径,用于客户端认证。 +- ```client_cert```客户端证书列表 警告:**不要将未开启TLS双向认证的API服务直接暴露在互联网上,否则可能导致各类安全问题。** diff --git a/go.mod b/go.mod index 3f4bdd3f5..848dc973e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/p4gefau1t/trojan-go go 1.14 require ( - github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed github.com/go-sql-driver/mysql v1.5.0 github.com/golang/protobuf v1.4.1 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect diff --git a/go.sum b/go.sum index c2ef27db6..2ab9a0e83 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed h1:eqa6queieK8SvoszxCu0WwH7lSVeL4/N/f1JwOMw1G4= -github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA52xkgZwql9LRZXWb2arHEFP6qSR48KY2xOfWzEciQ= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/tunnel/tproxy/server.go b/tunnel/tproxy/server.go index db95f2156..f96358261 100644 --- a/tunnel/tproxy/server.go +++ b/tunnel/tproxy/server.go @@ -1,4 +1,4 @@ -// +build linux,!386 +// +build linux package tproxy @@ -9,7 +9,6 @@ import ( "sync" "time" - "github.com/LiamHaworth/go-tproxy" "github.com/p4gefau1t/trojan-go/common" "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/log" @@ -46,13 +45,13 @@ func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) { } return nil, common.NewError("tproxy failed to accept conn") } - addr, err := getOriginalTCPDest(conn.(*tproxy.Conn).TCPConn) + dst, err := getOriginalTCPDest(conn.(*net.TCPConn)) if err != nil { return nil, common.NewError("tproxy failed to obtain original address of tcp socket").Base(err) } - address, err := tunnel.NewAddressFromAddr("tcp", addr.String()) + address, err := tunnel.NewAddressFromAddr("tcp", dst.String()) common.Must(err) - log.Info("tproxy connection from", conn.RemoteAddr().String(), "metadata", addr.String()) + log.Info("tproxy connection from", conn.RemoteAddr().String(), "metadata", dst.String()) return &Conn{ metadata: &tunnel.Metadata{ Address: address, @@ -72,7 +71,7 @@ func (s *Server) packetDispatchLoop() { go func() { for { buf := make([]byte, MaxPacketSize) - n, src, dst, err := tproxy.ReadFromUDP(s.udpListener, buf) + n, src, dst, err := ReadFromUDP(s.udpListener, buf) if err != nil { select { case <-s.ctx.Done(): @@ -129,7 +128,7 @@ func (s *Server) packetDispatchLoop() { go func(conn *dokodemo.PacketConn) { defer conn.Close() - back, err := tproxy.DialUDP( + back, err := DialUDP( "udp", &net.UDPAddr{ IP: conn.M.IP, @@ -194,7 +193,7 @@ func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) { if err != nil { return nil, common.NewError("invalid tproxy local address").Base(err) } - tcpListener, err := tproxy.ListenTCP("tcp", &net.TCPAddr{ + tcpListener, err := ListenTCP("tcp", &net.TCPAddr{ IP: ip, Port: cfg.LocalPort, }) @@ -202,7 +201,7 @@ func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) { return nil, common.NewError("tproxy failed to listen tcp").Base(err) } - udpListener, err := tproxy.ListenUDP("udp", &net.UDPAddr{ + udpListener, err := ListenUDP("udp", &net.UDPAddr{ IP: ip, Port: cfg.LocalPort, }) diff --git a/tunnel/tproxy/tproxy.go b/tunnel/tproxy/tcp.go similarity index 55% rename from tunnel/tproxy/tproxy.go rename to tunnel/tproxy/tcp.go index aeef6508e..812faa24a 100644 --- a/tunnel/tproxy/tproxy.go +++ b/tunnel/tproxy/tcp.go @@ -1,14 +1,72 @@ -// +build linux,!386 +// +build linux package tproxy import ( + "fmt" "net" "os" "syscall" "unsafe" ) +// Listener describes a TCP Listener +// with the Linux IP_TRANSPARENT option defined +// on the listening socket +type Listener struct { + base net.Listener +} + +// Accept waits for and returns +// the next connection to the listener. +// +// This command wraps the AcceptTProxy +// method of the Listener +func (listener *Listener) Accept() (net.Conn, error) { + tcpConn, err := listener.base.(*net.TCPListener).AcceptTCP() + if err != nil { + return nil, err + } + + return tcpConn, nil +} + +// Addr returns the network address +// the listener is accepting connections +// from +func (listener *Listener) Addr() net.Addr { + return listener.base.Addr() +} + +// Close will close the listener from accepting +// any more connections. Any blocked connections +// will unblock and close +func (listener *Listener) Close() error { + return listener.base.Close() +} + +// ListenTCP will construct a new TCP listener +// socket with the Linux IP_TRANSPARENT option +// set on the underlying socket +func ListenTCP(network string, laddr *net.TCPAddr) (net.Listener, error) { + listener, err := net.ListenTCP(network, laddr) + if err != nil { + return nil, err + } + + fileDescriptorSource, err := listener.File() + if err != nil { + return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("get file descriptor: %s", err)} + } + defer fileDescriptorSource.Close() + + if err = syscall.SetsockoptInt(int(fileDescriptorSource.Fd()), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { + return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)} + } + + return &Listener{listener}, nil +} + const ( IP6T_SO_ORIGINAL_DST = 80 SO_ORIGINAL_DST = 80 diff --git a/tunnel/tproxy/udp.go b/tunnel/tproxy/udp.go new file mode 100644 index 000000000..a0b193cd2 --- /dev/null +++ b/tunnel/tproxy/udp.go @@ -0,0 +1,191 @@ +// +build linux + +package tproxy + +import ( + "bytes" + "encoding/binary" + "fmt" + "net" + "os" + "strconv" + "syscall" + "unsafe" +) + +// ListenUDP will construct a new UDP listener +// socket with the Linux IP_TRANSPARENT option +// set on the underlying socket +func ListenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) { + listener, err := net.ListenUDP(network, laddr) + if err != nil { + return nil, err + } + + fileDescriptorSource, err := listener.File() + if err != nil { + return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("get file descriptor: %s", err)} + } + defer fileDescriptorSource.Close() + + fileDescriptor := int(fileDescriptorSource.Fd()) + if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { + return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)} + } + + if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1); err != nil { + return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("set socket option: IP_RECVORIGDSTADDR: %s", err)} + } + + return listener, nil +} + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// Out-of-band data is also read in so that the original destination +// address can be identified and parsed. +func ReadFromUDP(conn *net.UDPConn, b []byte) (int, *net.UDPAddr, *net.UDPAddr, error) { + oob := make([]byte, 1024) + n, oobn, _, addr, err := conn.ReadMsgUDP(b, oob) + if err != nil { + return 0, nil, nil, err + } + + msgs, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + return 0, nil, nil, fmt.Errorf("parsing socket control message: %s", err) + } + + var originalDst *net.UDPAddr + for _, msg := range msgs { + if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR { + originalDstRaw := &syscall.RawSockaddrInet4{} + if err = binary.Read(bytes.NewReader(msg.Data), binary.LittleEndian, originalDstRaw); err != nil { + return 0, nil, nil, fmt.Errorf("reading original destination address: %s", err) + } + + switch originalDstRaw.Family { + case syscall.AF_INET: + pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(originalDstRaw)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + originalDst = &net.UDPAddr{ + IP: net.IPv4(pp.Addr[0], pp.Addr[1], pp.Addr[2], pp.Addr[3]), + Port: int(p[0])<<8 + int(p[1]), + } + + case syscall.AF_INET6: + pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(originalDstRaw)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + originalDst = &net.UDPAddr{ + IP: net.IP(pp.Addr[:]), + Port: int(p[0])<<8 + int(p[1]), + Zone: strconv.Itoa(int(pp.Scope_id)), + } + + default: + return 0, nil, nil, fmt.Errorf("original destination is an unsupported network family") + } + } + } + + if originalDst == nil { + return 0, nil, nil, fmt.Errorf("unable to obtain original destination: %s", err) + } + + return n, addr, originalDst, nil +} + +// DialUDP connects to the remote address raddr on the network net, +// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is +// used as the local address for the connection. +func DialUDP(network string, laddr *net.UDPAddr, raddr *net.UDPAddr) (*net.UDPConn, error) { + remoteSocketAddress, err := udpAddrToSocketAddr(raddr) + if err != nil { + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build destination socket address: %s", err)} + } + + localSocketAddress, err := udpAddrToSocketAddr(laddr) + if err != nil { + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build local socket address: %s", err)} + } + + fileDescriptor, err := syscall.Socket(udpAddrFamily(network, laddr, raddr), syscall.SOCK_DGRAM, 0) + if err != nil { + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket open: %s", err)} + } + + if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { + syscall.Close(fileDescriptor) + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: SO_REUSEADDR: %s", err)} + } + + if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { + syscall.Close(fileDescriptor) + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)} + } + + if err = syscall.Bind(fileDescriptor, localSocketAddress); err != nil { + syscall.Close(fileDescriptor) + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket bind: %s", err)} + } + + if err = syscall.Connect(fileDescriptor, remoteSocketAddress); err != nil { + syscall.Close(fileDescriptor) + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket connect: %s", err)} + } + + fdFile := os.NewFile(uintptr(fileDescriptor), fmt.Sprintf("net-udp-dial-%s", raddr.String())) + defer fdFile.Close() + + remoteConn, err := net.FileConn(fdFile) + if err != nil { + syscall.Close(fileDescriptor) + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("convert file descriptor to connection: %s", err)} + } + + return remoteConn.(*net.UDPConn), nil +} + +// udpAddToSockerAddr will convert a UDPAddr +// into a Sockaddr that may be used when +// connecting and binding sockets +func udpAddrToSocketAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) { + switch { + case addr.IP.To4() != nil: + ip := [4]byte{} + copy(ip[:], addr.IP.To4()) + + return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil + + default: + ip := [16]byte{} + copy(ip[:], addr.IP.To16()) + + zoneID, err := strconv.ParseUint(addr.Zone, 10, 32) + if err != nil { + return nil, err + } + + return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil + } +} + +// udpAddrFamily will attempt to work +// out the address family based on the +// network and UDP addresses +func udpAddrFamily(net string, laddr, raddr *net.UDPAddr) int { + switch net[len(net)-1] { + case '4': + return syscall.AF_INET + case '6': + return syscall.AF_INET6 + } + + if (laddr == nil || laddr.IP.To4() != nil) && + (raddr == nil || laddr.IP.To4() != nil) { + return syscall.AF_INET + } + return syscall.AF_INET6 +} From ba892860e19ca6395a8fec0883983c3c70eee5a1 Mon Sep 17 00:00:00 2001 From: Page Fault Date: Fri, 26 Jun 2020 08:18:00 +0000 Subject: [PATCH 5/6] fix tproxy for i386, yaml parsing, refactor socks5 udp tunnel --- api/service/config.go | 6 +- constant/constant.go | 4 +- proxy/client/client.go | 10 +- proxy/client/config.go | 18 ++-- proxy/custom/config.go | 14 +-- test/scenario/custom_test.go | 22 +++-- tunnel/adapter/config.go | 14 +++ tunnel/adapter/server.go | 56 +++++++---- tunnel/freedom/config.go | 6 +- tunnel/mux/config.go | 6 +- tunnel/router/config.go | 14 +-- tunnel/shadowsocks/config.go | 8 +- tunnel/shadowsocks/server.go | 2 +- tunnel/socks/config.go | 17 ++++ tunnel/socks/conn.go | 62 ++++++++++-- tunnel/socks/server.go | 158 ++++++++++++++++++++++++------- tunnel/socks/socks_test.go | 78 ++++++++++++++- tunnel/tproxy/config.go | 2 +- tunnel/tproxy/conn.go | 2 +- tunnel/tproxy/getsockopt.go | 18 ++++ tunnel/tproxy/getsockopt_i386.go | 20 ++++ tunnel/tproxy/tcp.go | 10 -- tunnel/tproxy/tunnel.go | 2 +- tunnel/trojan/config.go | 8 +- tunnel/trojan/packet.go | 2 +- tunnel/websocket/config.go | 8 +- 26 files changed, 426 insertions(+), 141 deletions(-) create mode 100644 tunnel/adapter/config.go create mode 100644 tunnel/socks/config.go create mode 100644 tunnel/tproxy/getsockopt.go create mode 100644 tunnel/tproxy/getsockopt_i386.go diff --git a/api/service/config.go b/api/service/config.go index 47ae25e6e..d41b5a8e7 100644 --- a/api/service/config.go +++ b/api/service/config.go @@ -5,7 +5,7 @@ import "github.com/p4gefau1t/trojan-go/config" const Name = "API_SERVICE" type SSLConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` CertPath string `json:"cert" yaml:"cert"` KeyPath string `json:"key" yaml:"key"` VerifyClient bool `json:"verify_client" yaml:"verify-client"` @@ -16,11 +16,11 @@ type APIConfig struct { Enabled bool `json:"enabled" yaml:"enabled"` APIHost string `json:"api_addr" yaml:"api-addr"` APIPort int `json:"api_port" yaml:"api-port"` - SSL SSLConfig `json,yaml:"ssl"` + SSL SSLConfig `json:"ssl" yaml:"ssl"` } type Config struct { - API APIConfig `json,yaml:"api"` + API APIConfig `json:"api" yaml:"api"` } func init() { diff --git a/constant/constant.go b/constant/constant.go index 98759edd4..273fa4574 100644 --- a/constant/constant.go +++ b/constant/constant.go @@ -1,6 +1,6 @@ package constant var ( - Version = "VERSION" - Commit = "COMMIT" + Version = "Custom Version" + Commit = "Unknown Git Commit ID" ) diff --git a/proxy/client/client.go b/proxy/client/client.go index e487c4b6e..bf16d8286 100644 --- a/proxy/client/client.go +++ b/proxy/client/client.go @@ -46,21 +46,21 @@ func init() { proxy.RegisterProxyCreator(Name, func(ctx context.Context) (*proxy.Proxy, error) { cfg := config.FromContext(ctx, Name).(*Config) - transportServer, err := transport.NewServer(ctx, nil) + adapterServer, err := adapter.NewServer(ctx, nil) if err != nil { return nil, err } root := &proxy.Node{ - Name: transport.Name, + Name: adapter.Name, Next: make(map[string]*proxy.Node), IsEndpoint: false, Context: ctx, - Server: transportServer, + Server: adapterServer, } - root.BuildNext(adapter.Name).BuildNext(http.Name).IsEndpoint = true - root.BuildNext(adapter.Name).BuildNext(socks.Name).IsEndpoint = true + root.BuildNext(http.Name).IsEndpoint = true + root.BuildNext(socks.Name).IsEndpoint = true clientStack := GenerateClientTree(cfg.TransportPlugin.Enabled, cfg.Mux.Enabled, cfg.Websocket.Enabled, cfg.Shadowsocks.Enabled, cfg.Router.Enabled) c, err := proxy.CreateClientStack(ctx, clientStack) diff --git a/proxy/client/config.go b/proxy/client/config.go index 7b5bcdf21..17c0a8a22 100644 --- a/proxy/client/config.go +++ b/proxy/client/config.go @@ -3,30 +3,30 @@ package client import "github.com/p4gefau1t/trojan-go/config" type MuxConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` } type WebsocketConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` } type RouterConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` } type ShadowsocksConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` } type TransportPluginConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` } type Config struct { - Mux MuxConfig `json,yaml:"mux"` - Websocket WebsocketConfig `json,yaml:"websocket"` - Router RouterConfig `json,yaml:"router"` - Shadowsocks ShadowsocksConfig `json,yaml:"shadowsocks"` + Mux MuxConfig `json:"mux" yaml:"mux"` + Websocket WebsocketConfig `json:"websocket" yaml:"websocket"` + Router RouterConfig `json:"router" yaml:"router"` + Shadowsocks ShadowsocksConfig `json:"shadowsocks" yaml:"shadowsocks"` TransportPlugin TransportPluginConfig `json:"transport_plugin" yaml:"transport-plugin"` } diff --git a/proxy/custom/config.go b/proxy/custom/config.go index 334daaf53..7a12dc47d 100644 --- a/proxy/custom/config.go +++ b/proxy/custom/config.go @@ -5,19 +5,19 @@ import "github.com/p4gefau1t/trojan-go/config" const Name = "CUSTOM" type NodeConfig struct { - Protocol string `json,yaml:"protocol"` - Tag string `json,yaml:"tag"` - Config interface{} `json,yaml:"config"` + Protocol string `json:"protocol" yaml:"protocol"` + Tag string `json:"tag" yaml:"tag"` + Config interface{} `json:"config" yaml:"config"` } type StackConfig struct { - Path [][]string `json,yaml:"path"` - Node []NodeConfig `json,yaml:"node"` + Path [][]string `json:"path" yaml:"path"` + Node []NodeConfig `json:"node" yaml:"node"` } type Config struct { - Inbound StackConfig `json,yaml:"inbound"` - Outbound StackConfig `json,yaml:"outbound"` + Inbound StackConfig `json:"inbound" yaml:"inbound"` + Outbound StackConfig `json:"outbound" yaml:"outbound"` } func init() { diff --git a/test/scenario/custom_test.go b/test/scenario/custom_test.go index 8dcc8bdf0..4acdec3f3 100644 --- a/test/scenario/custom_test.go +++ b/test/scenario/custom_test.go @@ -17,16 +17,19 @@ run-type: custom inbound: node: - - protocol: transport - tag: transport + - protocol: adapter + tag: adapter config: local-addr: 127.0.0.1 local-port: %d - protocol: socks tag: socks + config: + local-addr: 127.0.0.1 + local-port: %d path: - - - transport + - adapter - socks outbound: @@ -57,7 +60,7 @@ outbound: - tls - trojan -`, socksPort, serverPort) +`, socksPort, socksPort, serverPort) serverData := fmt.Sprintf(` run-type: custom @@ -130,16 +133,19 @@ log-level: 0 inbound: node: - - protocol: transport - tag: transport + - protocol: adapter + tag: adapter config: local-addr: 127.0.0.1 local-port: %d - protocol: socks tag: socks + config: + local-addr: 127.0.0.1 + local-port: %d path: - - - transport + - adapter - socks outbound: @@ -188,7 +194,7 @@ outbound: - shadowsocks - trojan -`, socksPort, serverPort) +`, socksPort, socksPort, serverPort) serverData := fmt.Sprintf(` run-type: custom log-level: 0 diff --git a/tunnel/adapter/config.go b/tunnel/adapter/config.go new file mode 100644 index 000000000..809ad2b16 --- /dev/null +++ b/tunnel/adapter/config.go @@ -0,0 +1,14 @@ +package adapter + +import "github.com/p4gefau1t/trojan-go/config" + +type Config struct { + LocalHost string `json:"local_addr" yaml:"local-addr"` + LocalPort int `json:"local_port" yaml:"local-port"` +} + +func init() { + config.RegisterConfigCreator(Name, func() interface{} { + return new(Config) + }) +} diff --git a/tunnel/adapter/server.go b/tunnel/adapter/server.go index 0abe734f5..cdea4abb1 100644 --- a/tunnel/adapter/server.go +++ b/tunnel/adapter/server.go @@ -2,27 +2,30 @@ package adapter import ( "context" + "net" "github.com/p4gefau1t/trojan-go/common" + "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/log" "github.com/p4gefau1t/trojan-go/tunnel" + "github.com/p4gefau1t/trojan-go/tunnel/freedom" "github.com/p4gefau1t/trojan-go/tunnel/http" "github.com/p4gefau1t/trojan-go/tunnel/socks" - "github.com/p4gefau1t/trojan-go/tunnel/transport" ) type Server struct { - underlay tunnel.Server - socksConn chan tunnel.Conn - httpConn chan tunnel.Conn - nextSocks bool - ctx context.Context - cancel context.CancelFunc + tcpListener net.Listener + udpListener net.PacketConn + socksConn chan tunnel.Conn + httpConn chan tunnel.Conn + nextSocks bool + ctx context.Context + cancel context.CancelFunc } func (s *Server) acceptConnLoop() { for { - conn, err := s.underlay.AcceptConn(&Tunnel{}) + conn, err := s.tcpListener.Accept() if err != nil { select { case <-s.ctx.Done(): @@ -44,12 +47,12 @@ func (s *Server) acceptConnLoop() { } if buf[0] == 5 && s.nextSocks { log.Debug("socks5 connection") - s.socksConn <- &transport.Conn{ + s.socksConn <- &freedom.Conn{ Conn: rewindConn, } } else { log.Debug("http connection") - s.httpConn <- &transport.Conn{ + s.httpConn <- &freedom.Conn{ Conn: rewindConn, } } @@ -78,25 +81,38 @@ func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) { } func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) { - // no packet conn available, but it's ok to stuck here - <-s.ctx.Done() - return nil, common.NewError("adapter server closed") + return &freedom.PacketConn{ + UDPConn: s.udpListener.(*net.UDPConn), + }, nil } func (s *Server) Close() error { s.cancel() - return s.underlay.Close() + s.tcpListener.Close() + return s.udpListener.Close() } -func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, error) { +func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) { + cfg := config.FromContext(ctx, Name).(*Config) ctx, cancel := context.WithCancel(ctx) + addr := tunnel.NewAddressFromHostPort("tcp", cfg.LocalHost, cfg.LocalPort) + tcpListener, err := net.Listen("tcp", addr.String()) + if err != nil { + return nil, common.NewError("adapter failed to create tcp listener").Base(err) + } + udpListener, err := net.ListenPacket("udp", addr.String()) + if err != nil { + return nil, common.NewError("adapter failed to create tcp listener").Base(err) + } server := &Server{ - underlay: underlay, - socksConn: make(chan tunnel.Conn, 32), - httpConn: make(chan tunnel.Conn, 32), - ctx: ctx, - cancel: cancel, + tcpListener: tcpListener, + udpListener: udpListener, + socksConn: make(chan tunnel.Conn, 32), + httpConn: make(chan tunnel.Conn, 32), + ctx: ctx, + cancel: cancel, } + log.Info("adapter listening on tcp/udp:", addr) go server.acceptConnLoop() return server, nil } diff --git a/tunnel/freedom/config.go b/tunnel/freedom/config.go index 3868c715e..c6588d985 100644 --- a/tunnel/freedom/config.go +++ b/tunnel/freedom/config.go @@ -17,11 +17,11 @@ type TCPConfig struct { } type ForwardProxyConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` ProxyHost string `json:"proxy_addr" yaml:"proxy-addr"` ProxyPort int `json:"proxy_port" yaml:"proxy-port"` - Username string `json,yaml:"username"` - Password string `json,yaml:"password"` + Username string `json:"username" yaml:"username"` + Password string `json:"password" yaml:"password"` } func init() { diff --git a/tunnel/mux/config.go b/tunnel/mux/config.go index e58202e71..35570f3ce 100644 --- a/tunnel/mux/config.go +++ b/tunnel/mux/config.go @@ -3,13 +3,13 @@ package mux import "github.com/p4gefau1t/trojan-go/config" type MuxConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` IdleTimeout int `json:"idle_timeout" yaml:"idle-timeout"` - Concurrency int `json,yaml:"concurrency"` + Concurrency int `json:"concurrency" yaml:"concurrency"` } type Config struct { - Mux MuxConfig `json,yaml:"mux"` + Mux MuxConfig `json:"mux" yaml:"mux"` } func init() { diff --git a/tunnel/router/config.go b/tunnel/router/config.go index 4d9092802..1891a7dd9 100644 --- a/tunnel/router/config.go +++ b/tunnel/router/config.go @@ -9,18 +9,18 @@ import ( ) type Config struct { - Router RouterConfig `json,yaml:"router"` + Router RouterConfig `json:"router" yaml:"router"` } type RouterConfig struct { - Enabled bool `json,yaml:"enabled"` - Bypass []string `json,yaml:"bypass"` - Proxy []string `json,yaml:"proxy"` - Block []string `json,yaml:"block"` + Enabled bool `json:"enabled" yaml:"enabled"` + Bypass []string `json:"bypass" yaml:"bypass"` + Proxy []string `json:"proxy" yaml:"proxy"` + Block []string `json:"block" yaml:"block"` DomainStrategy string `json:"domain_strategy" yaml:"domain-strategy"` DefaultPolicy string `json:"default_policy" yaml:"default-policy"` - GeoIPFilename string `json,yaml:"geoip"` - GeoSiteFilename string `json,yaml:"geosite"` + GeoIPFilename string `json:"geoip" yaml:"geoip"` + GeoSiteFilename string `json:"geosite" yaml:"geosite"` } func init() { diff --git a/tunnel/shadowsocks/config.go b/tunnel/shadowsocks/config.go index b775c0be9..78d67b620 100644 --- a/tunnel/shadowsocks/config.go +++ b/tunnel/shadowsocks/config.go @@ -3,15 +3,15 @@ package shadowsocks import "github.com/p4gefau1t/trojan-go/config" type ShadowsocksConfig struct { - Enabled bool `json,yaml:"enabled"` - Method string `json,yaml:"method"` - Password string `json,yaml:"password"` + Enabled bool `json:"enabled" yaml:"enabled"` + Method string `json:"method" yaml:"method"` + Password string `json:"password" yaml:"password"` } type Config struct { RemoteHost string `json:"remote_addr" yaml:"remote-addr"` RemotePort int `json:"remote_port" yaml:"remote-port"` - Shadowsocks ShadowsocksConfig `json,yaml:"shadowsocks"` + Shadowsocks ShadowsocksConfig `json:"shadowsocks" yaml:"shadowsocks"` } func init() { diff --git a/tunnel/shadowsocks/server.go b/tunnel/shadowsocks/server.go index 22eb52444..f34fd3461 100644 --- a/tunnel/shadowsocks/server.go +++ b/tunnel/shadowsocks/server.go @@ -22,7 +22,7 @@ type Server struct { func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) { conn, err := s.underlay.AcceptConn(&Tunnel{}) if err != nil { - return nil, common.NewError("shadowsocks failed to accept connection from underlying tunnel") + return nil, common.NewError("shadowsocks failed to accept connection from underlying tunnel").Base(err) } rewindConn := common.NewRewindConn(conn) rewindConn.SetBufferSize(1024) diff --git a/tunnel/socks/config.go b/tunnel/socks/config.go new file mode 100644 index 000000000..359901c50 --- /dev/null +++ b/tunnel/socks/config.go @@ -0,0 +1,17 @@ +package socks + +import "github.com/p4gefau1t/trojan-go/config" + +type Config struct { + LocalHost string `json:"local_addr" yaml:"local-addr"` + LocalPort int `json:"local_port" yaml:"local-port"` + UDPTimeout int `json:"udp_timeout" yaml:"udp-timeout"` +} + +func init() { + config.RegisterConfigCreator(Name, func() interface{} { + return &Config{ + UDPTimeout: 10, + } + }) +} diff --git a/tunnel/socks/conn.go b/tunnel/socks/conn.go index 0e90d9716..8b19be261 100644 --- a/tunnel/socks/conn.go +++ b/tunnel/socks/conn.go @@ -2,6 +2,7 @@ package socks import ( "bytes" + "context" "net" "github.com/p4gefau1t/trojan-go/common" @@ -18,23 +19,72 @@ func (c *Conn) Metadata() *tunnel.Metadata { return c.metadata } +type packetInfo struct { + metadata *tunnel.Metadata + payload []byte +} + type PacketConn struct { net.PacketConn - srcAddr net.Addr + input chan *packetInfo + output chan *packetInfo + src net.Addr + ctx context.Context + cancel context.CancelFunc +} + +func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + panic("implement me") +} + +func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + panic("implement me") } func (c *PacketConn) Close() error { + c.cancel() + return nil +} + +func (c *PacketConn) WriteWithMetadata(p []byte, m *tunnel.Metadata) (int, error) { + select { + case c.output <- &packetInfo{ + metadata: m, + payload: p, + }: + return len(p), nil + case <-c.ctx.Done(): + return 0, common.NewError("socks packet conn closed") + } +} + +func (c *PacketConn) ReadWithMetadata(p []byte) (int, *tunnel.Metadata, error) { + select { + case info := <-c.input: + n := copy(p, info.payload) + return n, info.metadata, nil + case <-c.ctx.Done(): + return 0, nil, common.NewError("socks packet conn closed") + } +} + +type WrappedPacketConn struct { + net.PacketConn + Destination net.Addr +} + +func (c *WrappedPacketConn) Close() error { return c.PacketConn.Close() } -func (c *PacketConn) WriteWithMetadata(payload []byte, metadata *tunnel.Metadata) (int, error) { +func (c *WrappedPacketConn) WriteWithMetadata(payload []byte, metadata *tunnel.Metadata) (int, error) { buf := bytes.NewBuffer(make([]byte, 0, MaxPacketSize)) buf.Write([]byte{0, 0, 0}) //RSV, FRAG common.Must(metadata.Address.WriteTo(buf)) buf.Write(payload) var clientAddr net.Addr - if c.srcAddr != nil { - clientAddr = c.srcAddr + if c.Destination != nil { + clientAddr = c.Destination } else { return 0, common.NewError("client address not found") } @@ -46,7 +96,7 @@ func (c *PacketConn) WriteWithMetadata(payload []byte, metadata *tunnel.Metadata return len(payload), nil } -func (c *PacketConn) ReadWithMetadata(payload []byte) (int, *tunnel.Metadata, error) { +func (c *WrappedPacketConn) ReadWithMetadata(payload []byte) (int, *tunnel.Metadata, error) { buf := make([]byte, MaxPacketSize) n, from, err := c.PacketConn.ReadFrom(buf) if err != nil { @@ -54,7 +104,7 @@ func (c *PacketConn) ReadWithMetadata(payload []byte) (int, *tunnel.Metadata, er } log.Debug("recv udp packet from " + from.String()) addr := new(tunnel.Address) - c.srcAddr = from + c.Destination = from r := bytes.NewBuffer(buf[3:n]) if err := addr.ReadFrom(r); err != nil { return 0, nil, common.NewError("socks5 failed to parse addr in the packet").Base(err) diff --git a/tunnel/socks/server.go b/tunnel/socks/server.go index 601b0132c..d2867c597 100644 --- a/tunnel/socks/server.go +++ b/tunnel/socks/server.go @@ -7,8 +7,11 @@ import ( "io" "io/ioutil" "net" + "sync" + "time" "github.com/p4gefau1t/trojan-go/common" + "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/log" "github.com/p4gefau1t/trojan-go/tunnel" ) @@ -22,14 +25,18 @@ const ( MaxPacketSize = 1024 * 8 ) -// Server is a socks5 server type Server struct { - connChan chan tunnel.Conn - packetChan chan tunnel.PacketConn - underlay tunnel.Server - localHost string - ctx context.Context - cancel context.CancelFunc + connChan chan tunnel.Conn + packetChan chan tunnel.PacketConn + underlay tunnel.Server + localHost string + localPort int + timeout time.Duration + listenPacketConn tunnel.PacketConn + mapping map[string]*PacketConn + mappingLock sync.RWMutex + ctx context.Context + cancel context.CancelFunc } func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) { @@ -68,7 +75,7 @@ func (s *Server) handshake(conn net.Conn) (*Conn, error) { return nil, common.NewError("failed to read NMETHODS") } if _, err := io.CopyN(ioutil.Discard, conn, int64(nmethods[0])); err != nil { - return nil, common.NewError("socks5 failed to read methods").Base(err) + return nil, common.NewError("socks failed to read methods").Base(err) } if _, err := conn.Write([]byte{0x5, 0x0}); err != nil { return nil, common.NewError("failed to respond auth").Base(err) @@ -105,24 +112,107 @@ func (s *Server) associate(conn net.Conn, addr *tunnel.Address) error { return err } +func (s *Server) packetDispatchLoop() { + for { + buf := make([]byte, MaxPacketSize) + n, src, err := s.listenPacketConn.ReadFrom(buf) + if err != nil { + select { + case <-s.ctx.Done(): + log.Debug("exiting") + return + default: + continue + } + } + log.Debug("socks recv udp packet from", src) + s.mappingLock.RLock() + conn, found := s.mapping[src.String()] + s.mappingLock.RUnlock() + if !found { + ctx, cancel := context.WithCancel(s.ctx) + conn = &PacketConn{ + input: make(chan *packetInfo, 128), + output: make(chan *packetInfo, 128), + ctx: ctx, + cancel: cancel, + PacketConn: s.listenPacketConn, + src: src, + } + go func(conn *PacketConn) { + defer conn.Close() + for { + select { + case info := <-conn.output: + buf := bytes.NewBuffer(make([]byte, 0, MaxPacketSize)) + buf.Write([]byte{0, 0, 0}) //RSV, FRAG + common.Must(info.metadata.Address.WriteTo(buf)) + buf.Write(info.payload) + _, err := s.listenPacketConn.WriteTo(buf.Bytes(), conn.src) + if err != nil { + log.Error("socks failed to respond packet to", src) + return + } + log.Debug("socks respond udp packet to", src, "metadata", info.metadata) + case <-time.After(time.Second * 5): + log.Info("socks udp session timeout, closed") + s.mappingLock.Lock() + delete(s.mapping, src.String()) + s.mappingLock.Unlock() + return + case <-conn.ctx.Done(): + log.Info("socks udp session closed") + return + } + } + }(conn) + + s.mappingLock.Lock() + s.mapping[src.String()] = conn + s.mappingLock.Unlock() + + s.packetChan <- conn + log.Info("socks new udp session from", src) + } + r := bytes.NewBuffer(buf[3:n]) + address := new(tunnel.Address) + if err := address.ReadFrom(r); err != nil { + log.Error(common.NewError("socks failed to parse incoming packet").Base(err)) + continue + } + payload := make([]byte, MaxPacketSize) + length, err := r.Read(payload) + select { + case conn.input <- &packetInfo{ + metadata: &tunnel.Metadata{ + Address: address, + }, + payload: payload[:length], + }: + default: + log.Warn("socks udp WrappedPacketConn full") + } + } +} + func (s *Server) acceptLoop() { for { conn, err := s.underlay.AcceptConn(&Tunnel{}) if err != nil { - log.Error(common.NewError("socks5 accept err").Base(err)) + log.Error(common.NewError("socks accept err").Base(err)) return } go func(conn net.Conn) { newConn, err := s.handshake(conn) if err != nil { - log.Error(common.NewError("socks5 failed to handshake with client").Base(err)) + log.Error(common.NewError("socks failed to handshake with client").Base(err)) return } - log.Info("socks5 connection from", conn.RemoteAddr(), "metadata", newConn.metadata.String()) + log.Info("socks connection from", conn.RemoteAddr(), "metadata", newConn.metadata.String()) switch newConn.metadata.Command { case Connect: if err := s.connect(newConn); err != nil { - log.Error(common.NewError("socks5 failed to respond CONNECT").Base(err)) + log.Error(common.NewError("socks failed to respond CONNECT").Base(err)) newConn.Close() return } @@ -130,29 +220,16 @@ func (s *Server) acceptLoop() { return case Associate: defer newConn.Close() - port := common.PickPort("udp", s.localHost) - // TODO use underlying server - associateAddr := tunnel.NewAddressFromHostPort("udp", s.localHost, port) - l, err := net.ListenPacket("udp", associateAddr.String()) - if err != nil { - log.Error(common.NewError("socks5 failed to bind udp").Base(err)) - return - } - packetConn := &PacketConn{ - PacketConn: l, - } - s.packetChan <- packetConn - log.Info("socks5 udp session") + associateAddr := tunnel.NewAddressFromHostPort("udp", s.localHost, s.localPort) if err := s.associate(newConn, associateAddr); err != nil { - log.Error(common.NewError("socks5 failed to respond to associate request").Base(err)) + log.Error(common.NewError("socks failed to respond to associate request").Base(err)) return } buf := [16]byte{} newConn.Read(buf[:]) - packetConn.Close() - log.Debug("socks5 udp session ends") + log.Debug("socks udp session ends") default: - log.Error(common.NewError(fmt.Sprintf("unknown socks5 command %d", newConn.metadata.Command))) + log.Error(common.NewError(fmt.Sprintf("unknown socks command %d", newConn.metadata.Command))) newConn.Close() } }(conn) @@ -161,17 +238,26 @@ func (s *Server) acceptLoop() { // NewServer create a socks server func NewServer(ctx context.Context, underlay tunnel.Server) (tunnel.Server, error) { + cfg := config.FromContext(ctx, Name).(*Config) + listenPacketConn, err := underlay.AcceptPacket(&Tunnel{}) + if err != nil { + return nil, common.NewError("socks failed to listen packet from underlying server") + } ctx, cancel := context.WithCancel(ctx) server := &Server{ - underlay: underlay, - ctx: ctx, - cancel: cancel, - connChan: make(chan tunnel.Conn, 32), - packetChan: make(chan tunnel.PacketConn, 32), - localHost: "", + underlay: underlay, + ctx: ctx, + cancel: cancel, + connChan: make(chan tunnel.Conn, 32), + packetChan: make(chan tunnel.PacketConn, 32), + localHost: cfg.LocalHost, + localPort: cfg.LocalPort, + timeout: time.Duration(cfg.UDPTimeout) * time.Second, + listenPacketConn: listenPacketConn, + mapping: make(map[string]*PacketConn), } - // TODO localhost go server.acceptLoop() + go server.packetDispatchLoop() log.Debug("socks server created") return server, nil } diff --git a/tunnel/socks/socks_test.go b/tunnel/socks/socks_test.go index 3c82cfc13..cdfdfec85 100644 --- a/tunnel/socks/socks_test.go +++ b/tunnel/socks/socks_test.go @@ -1,8 +1,10 @@ -package socks +package socks_test import ( + "bytes" "context" "fmt" + "io/ioutil" "net" "sync" "testing" @@ -12,20 +14,25 @@ import ( "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/test/util" "github.com/p4gefau1t/trojan-go/tunnel" - "github.com/p4gefau1t/trojan-go/tunnel/transport" + "github.com/p4gefau1t/trojan-go/tunnel/adapter" + "github.com/p4gefau1t/trojan-go/tunnel/socks" "golang.org/x/net/proxy" ) func TestSocks(t *testing.T) { port := common.PickPort("tcp", "127.0.0.1") - ctx := config.WithConfig(context.Background(), transport.Name, &transport.Config{ + ctx := config.WithConfig(context.Background(), adapter.Name, &adapter.Config{ LocalHost: "127.0.0.1", LocalPort: port, }) - tcpServer, err := transport.NewServer(ctx, nil) + ctx = config.WithConfig(ctx, socks.Name, &socks.Config{ + LocalHost: "127.0.0.1", + LocalPort: port, + }) + tcpServer, err := adapter.NewServer(ctx, nil) common.Must(err) addr := tunnel.NewAddressFromHostPort("tcp", "127.0.0.1", port) - s, err := NewServer(ctx, tcpServer) + s, err := socks.NewServer(ctx, tcpServer) common.Must(err) socksClient, err := proxy.SOCKS5("tcp", addr.String(), nil, proxy.Direct) common.Must(err) @@ -52,4 +59,65 @@ func TestSocks(t *testing.T) { t.Fail() } fmt.Println(conn2.(tunnel.Conn).Metadata()) + + udpConn, err := net.ListenPacket("udp", ":0") + common.Must(err) + + addr = &tunnel.Address{ + AddressType: tunnel.DomainName, + DomainName: "google.com", + Port: 12345, + } + + payload := util.GeneratePayload(1024) + buf := bytes.NewBuffer(make([]byte, 0, 4096)) + buf.Write([]byte{0, 0, 0}) //RSV, FRAG + common.Must(addr.WriteTo(buf)) + buf.Write(payload) + + udpConn.WriteTo(buf.Bytes(), &net.UDPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: port, + }) + + packet, err := s.AcceptPacket(nil) + common.Must(err) + recvBuf := make([]byte, 4096) + n, m, err := packet.ReadWithMetadata(recvBuf) + common.Must(err) + if m.DomainName != "google.com" || m.Port != 12345 || n != 1024 || !(bytes.Equal(recvBuf[:n], payload)) { + t.Fail() + } + + payload = util.GeneratePayload(1024) + _, err = packet.WriteWithMetadata(payload, &tunnel.Metadata{ + Address: &tunnel.Address{ + AddressType: tunnel.IPv4, + IP: net.ParseIP("123.123.234.234"), + Port: 12345, + }, + }) + common.Must(err) + + _, _, err = udpConn.ReadFrom(recvBuf) + common.Must(err) + + r := bytes.NewReader(recvBuf) + header := [3]byte{} + r.Read(header[:]) + addr = new(tunnel.Address) + common.Must(addr.ReadFrom(r)) + if addr.IP.String() != "123.123.234.234" || addr.Port != 12345 { + t.Fail() + } + + recvBuf, err = ioutil.ReadAll(r) + common.Must(err) + + if bytes.Equal(recvBuf, payload) { + t.Fail() + } + + packet.Close() + udpConn.Close() } diff --git a/tunnel/tproxy/config.go b/tunnel/tproxy/config.go index 0e621e7ee..11cbf3c33 100644 --- a/tunnel/tproxy/config.go +++ b/tunnel/tproxy/config.go @@ -1,4 +1,4 @@ -// +build linux,!386 +// +build linux package tproxy diff --git a/tunnel/tproxy/conn.go b/tunnel/tproxy/conn.go index 971a3ffb2..b26bd4096 100644 --- a/tunnel/tproxy/conn.go +++ b/tunnel/tproxy/conn.go @@ -1,4 +1,4 @@ -// +build linux,!386 +// +build linux package tproxy diff --git a/tunnel/tproxy/getsockopt.go b/tunnel/tproxy/getsockopt.go new file mode 100644 index 000000000..2a7a639eb --- /dev/null +++ b/tunnel/tproxy/getsockopt.go @@ -0,0 +1,18 @@ +// +build linux,!386 + +package tproxy + +import ( + "syscall" + "unsafe" +) + +func getsockopt(fd int, level int, optname int, optval unsafe.Pointer, optlen *uint32) (err error) { + _, _, e := syscall.Syscall6( + syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(optname), + uintptr(optval), uintptr(unsafe.Pointer(optlen)), 0) + if e != 0 { + return e + } + return +} diff --git a/tunnel/tproxy/getsockopt_i386.go b/tunnel/tproxy/getsockopt_i386.go new file mode 100644 index 000000000..c744eb2fe --- /dev/null +++ b/tunnel/tproxy/getsockopt_i386.go @@ -0,0 +1,20 @@ +// +build linux,386 + +package tproxy + +import ( + "syscall" + "unsafe" +) + +const GETSOCKOPT = 15 + +func getsockopt(fd int, level int, optname int, optval unsafe.Pointer, optlen *uint32) (err error) { + _, _, e := syscall.Syscall6( + GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(optname), + uintptr(optval), uintptr(unsafe.Pointer(optlen)), 0) + if e != 0 { + return e + } + return +} diff --git a/tunnel/tproxy/tcp.go b/tunnel/tproxy/tcp.go index 812faa24a..8d5887ec6 100644 --- a/tunnel/tproxy/tcp.go +++ b/tunnel/tproxy/tcp.go @@ -72,16 +72,6 @@ const ( SO_ORIGINAL_DST = 80 ) -func getsockopt(s int, level int, optname int, optval unsafe.Pointer, optlen *uint32) (err error) { - _, _, e := syscall.Syscall6( - syscall.SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(optname), - uintptr(optval), uintptr(unsafe.Pointer(optlen)), 0) - if e != 0 { - return e - } - return -} - // getOriginalTCPDest retrieves the original destination address from // NATed connection. Currently, only Linux iptables using DNAT/REDIRECT // is supported. For other operating systems, this will just return diff --git a/tunnel/tproxy/tunnel.go b/tunnel/tproxy/tunnel.go index fd0270255..230820115 100644 --- a/tunnel/tproxy/tunnel.go +++ b/tunnel/tproxy/tunnel.go @@ -1,4 +1,4 @@ -// +build linux,!386 +// +build linux package tproxy diff --git a/tunnel/trojan/config.go b/tunnel/trojan/config.go index b9df54330..32ac3a755 100644 --- a/tunnel/trojan/config.go +++ b/tunnel/trojan/config.go @@ -8,16 +8,16 @@ type Config struct { RemoteHost string `json:"remote_addr" yaml:"remote-addr"` RemotePort int `json:"remote_port" yaml:"remote-port"` DisableHTTPCheck bool `json:"disable_http_check" yaml:"disable-http-check"` - MySQL MySQLConfig `json,yaml:"mysql"` - API APIConfig `json,yaml:"api"` + MySQL MySQLConfig `json:"mysql" yaml:"mysql"` + API APIConfig `json:"api" yaml:"api"` } type MySQLConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` } type APIConfig struct { - Enabled bool `json,yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` } func init() { diff --git a/tunnel/trojan/packet.go b/tunnel/trojan/packet.go index 66cc5dc50..7a737bb6a 100644 --- a/tunnel/trojan/packet.go +++ b/tunnel/trojan/packet.go @@ -48,7 +48,7 @@ func (c *PacketConn) WriteWithMetadata(payload []byte, metadata *tunnel.Metadata _, err := c.Conn.Write(w.Bytes()) - log.Debug("udp packet back to", c.RemoteAddr(), "metadata", metadata, "size", length) + log.Debug("udp packet remote", c.RemoteAddr(), "metadata", metadata, "size", length) return len(payload), err } diff --git a/tunnel/websocket/config.go b/tunnel/websocket/config.go index 5d64ce6c5..b9e048313 100644 --- a/tunnel/websocket/config.go +++ b/tunnel/websocket/config.go @@ -3,15 +3,15 @@ package websocket import "github.com/p4gefau1t/trojan-go/config" type WebsocketConfig struct { - Enabled bool `json,yaml:"enabled""` - Hostname string `json,yaml:"hostname"` - Path string `json,yaml:"path"` + Enabled bool `json:"enabled" yaml:"enabled"` + Hostname string `json:"hostname" yaml:"hostname"` + Path string `json:"path" yaml:"path"` } type Config struct { RemoteHost string `json:"remote_addr" yaml:"remote-addr"` RemotePort int `json:"remote_port" yaml:"remote-port"` - Websocket WebsocketConfig `json,yaml:"websocket"` + Websocket WebsocketConfig `json:"websocket" yaml:"websocket"` } func init() { From 81e79d98c8e7be337e0934bb0fba849f5d38bbbe Mon Sep 17 00:00:00 2001 From: Page Fault Date: Fri, 26 Jun 2020 08:41:27 +0000 Subject: [PATCH 6/6] update docs --- README.md | 14 +++++++++ .../advance/customize-protocol-stack.md | 30 ++++++++++++------- docs/content/basic/full-config.md | 19 +++++++----- docs/content/developer/api.md | 9 +++--- 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 6e0e0f4a5..e4aa99961 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,20 @@ client.json } ``` +可以使用更简明易读的YAML语法进行配置,下面是一个客户端的例子,与上面的client.json等价 + +client.yaml + +```yaml +run-type: client +local-addr: 127.0.0.1 +local-port: 1080 +remote-addr: www.your_awesome_domain_name.com +remote-port: 443 +password: + - your_awesome_password +``` + ### WebSocket diff --git a/docs/content/advance/customize-protocol-stack.md b/docs/content/advance/customize-protocol-stack.md index 6fd230c64..f853ea52c 100644 --- a/docs/content/advance/customize-protocol-stack.md +++ b/docs/content/advance/customize-protocol-stack.md @@ -38,10 +38,10 @@ Trojan-Go将所有协议抽象为隧道,每个隧道可能提供客户端, |shadowsocks | y | n | y | n | y | y | |websocket | y | n | y | n | y | y | |freedom | n | n | y | y | n | y | -|socks | y | n | y | y | y | n | +|socks | y | y | y | y | y | n | |http | y | n | y | n | y | n | |router | y | y | y | y | n | y | -|adapter | y | n | y | n | y | n | +|adapter | n | n | y | y | y | n | 自定义协议栈的工作方式是,定义树/链上节点并分别它们起名(tag)并添加配置,然后使用tag组成的有向路径,描述这棵树/链。例如,对于一个典型的Trojan-Go服务器,可以如此描述: @@ -55,7 +55,15 @@ Trojan-Go将所有协议抽象为隧道,每个隧道可能提供客户端, - router->freedom -对于入站,从根开始描述多条路径,组成一棵**多叉树**(也可以退化为一条链),不满足树性质的图将导致未定义的行为;对于出站,必须描述一条链。**注意,每条路径,必须以不需要下层提供流或包的隧道开始(transport/tproxy/dokodemo等),必须以能向上层提供包和流的隧道终止(trojan/simplesocks/freedom),且必须确认对应隧道是否可作为出站/入站** +对于入站,从根开始描述多条路径,组成一棵**多叉树**(也可以退化为一条链),不满足树性质的图将导致未定义的行为;对于出站,必须描述一条**链**。 + +每条路径必须满足这样的条件: + +1. 必须以**不需要下层提供流或包**的隧道开始(transport/adapter/tproxy/dokodemo等) + +2. 必须以**能向上层提供包和流**的隧道终止(trojan/simplesocks/freedom等) + +3. 出站单链上,隧道必须都可作为出站。入站的所有路径上,隧道必须都可作为入站。 要启用自定义协议栈,将```run_type```指定为custom,此时除```inbound```和```outbound```之外的其他选项将被忽略。 @@ -68,16 +76,19 @@ run-type: custom inbound: node: - - protocol: transport - tag: transport + - protocol: adapter + tag: adapter config: local-addr: 127.0.0.1 local-port: 1080 - protocol: socks tag: socks + config: + local-addr: 127.0.0.1 + local-port: 1080 path: - - - transport + - adapter - socks outbound: @@ -103,7 +114,7 @@ outbound: - 12345678 path: - - + - - transport - tls - trojan @@ -154,7 +165,7 @@ inbound: - 87654321 path: - - + - - transport - tls - trojan1 @@ -170,7 +181,6 @@ outbound: tag: freedom path: - - + - - freedom - ``` diff --git a/docs/content/basic/full-config.md b/docs/content/basic/full-config.md index 34a62be0a..5ff2ff6d3 100644 --- a/docs/content/basic/full-config.md +++ b/docs/content/basic/full-config.md @@ -37,6 +37,7 @@ weight: 30 "buffer_size": 32, "dns": [], "disable_http_check": false, + "udp_timeout": 10, "ssl": { "verify": true, "verify_hostname": true, @@ -176,6 +177,8 @@ weight: 30 ```disable_http_check```是否禁用HTTP可用性检查。 +```udp_timeout``` UDP会话超时时间。 + ### ```ssl```选项 ```verify```表示客户端(client/nat/forward)是否校验服务端提供的证书合法性,默认开启。出于安全性考虑,这个选项不应该在实际场景中选择false,否则可能遭受中间人攻击。如果使用自签名或者自签发的证书,开启```verify```会导致校验失败。这种情况下,应当保持```verify```开启,然后在```cert```中填写服务端的证书,即可正常连接。 @@ -222,7 +225,7 @@ weight: 30 ```concurrency```指单个TLS隧道可以承载的最大连接数,默认为8。这个数值越大,多连接并发时TLS由于握手产生的延迟就越低,但网络吞吐量可能会有所降低,填入负数或者0表示所有连接只使用一个TLS隧道承载。 -```idle_timeout```指TLS隧道在空闲多久之后关闭,单位为秒。如果数值为负值或0,则一旦TLS隧道空闲,则立即关闭。 +```idle_timeout```空闲超时时间。指TLS隧道在空闲多长时间之后关闭,单位为秒。如果数值为负值或0,则一旦TLS隧道空闲,则立即关闭。 ### ```router```路由选项 @@ -256,7 +259,7 @@ weight: 30 - "ip_on_demand",域名均解析为IP,在IP列表中匹配。该策略可能导致DNS泄漏或遭到污染。 -```geoip```和```geosite```字段指geoip和geosite数据库文件路径,默认使用当前目录的geoip.dat和geosite.dat。 +```geoip```和```geosite```字段指geoip和geosite数据库文件路径,默认使用程序所在目录的geoip.dat和geosite.dat。也可以通过指定环境变量TROJAN_GO_LOCATION_ASSET指定工作目录。 ### ```websocket```选项 @@ -266,7 +269,7 @@ Websocket传输是trojan-go的特性。在**正常的直接连接代理节点** ```path```指的是Websocket使用的URL路径,必须以斜杠("/")开头,如"/longlongwebsocketpath",并且服务器和客户端必须一致。 -```hostname```Websocket握手时使用的主机名,客户端如果留空则使用```remote_addr```填充。如果使用了CDN,这个选项一般填入域名。 +```hostname```Websocket握手时使用的主机名,客户端如果留空则使用```remote_addr```填充。如果使用了CDN,这个选项一般填入域名。不正确的```hostname```可能导致CDN无法转发请求。 ### ``shadowsocks`` AEAD加密选项 @@ -373,14 +376,14 @@ trojan-go基于gRPC提供了API,以支持服务端和客户端的管理和统 ```api_port```gRPC监听的端口。 -```ssl``` TLS相关设置 +```ssl``` TLS相关设置。 -- ```enabled```是否使用开启TLS传输 +- ```enabled```是否使用TLS传输gRPC流量。 -- ```key```,```cert```服务器私钥和证书 +- ```key```,```cert```服务器私钥和证书。 -- ```verify_client```是否认证客户端证书 +- ```verify_client```是否认证客户端证书。 -- ```client_cert```客户端证书列表 +- ```client_cert```如果开启客户端认证,此处填入认证的客户端证书列表。 警告:**不要将未开启TLS双向认证的API服务直接暴露在互联网上,否则可能导致各类安全问题。** diff --git a/docs/content/developer/api.md b/docs/content/developer/api.md index 309fc01f4..1859e5f64 100644 --- a/docs/content/developer/api.md +++ b/docs/content/developer/api.md @@ -4,18 +4,19 @@ draft: false weight: 100 --- -Trojan-Go基于gRPC实现了API,使用protobuf交换数据。客户端可获取流量和速度信息;服务端可获取各用户流量,速度,在线情况,并动态增删用户和限制速度。可以通过在配置文件中添加```api```选项激活API模块。下面是一个例子 +Trojan-Go基于gRPC实现了API,使用protobuf交换数据。客户端可获取流量和速度信息;服务端可获取各用户流量,速度,在线情况,并动态增删用户和限制速度。可以通过在配置文件中添加```api```选项激活API模块。下面是一个例子,各字段含义参见“完整的配置文件”一节。 ```json +... "api": { "enabled": true, "api_addr": "0.0.0.0", "api_port": 10000, - "api_tls": true, "ssl": { + "enabled": true, "cert": "api_cert.crt", - "key": "api_key.crt", - "key_password": "", + "key": "api_key.key", + "verify_client": true, "client_cert": [ "api_client_cert1.crt", "api_client_cert2.crt"