From fbe88dac3fcf0c546db5b76ea43370fd3f4b8e9d Mon Sep 17 00:00:00 2001 From: T3ST3ST3R0N Date: Mon, 29 Jun 2026 21:42:25 +0330 Subject: [PATCH] feat(mtproto): add MTPROTO backend type and Mtproto proxy Sync the service proto with PasarGuard/node: BackendType.MTPROTO, a Mtproto proxy message (secret, user_ad_tag, max_tcp_conns, max_unique_ips), and Proxy.mtproto. Regenerate the gRPC/protobuf code and add a CreateMtproto helper, extending CreateProxies with the mtproto argument. Quota and expiry are intentionally omitted; the panel owns them. Ref: PasarGuard/node#61 --- bridge_test.go | 1 + common/funcs.go | 8 +- common/service.pb.go | 204 +++++++++++++++++++++++++++----------- common/service.proto | 12 +++ common/service_grpc.pb.go | 4 +- 5 files changed, 167 insertions(+), 62 deletions(-) diff --git a/bridge_test.go b/bridge_test.go index 8ddc90a..dcdb91b 100644 --- a/bridge_test.go +++ b/bridge_test.go @@ -66,6 +66,7 @@ var user = common.CreateUser( common.CreateShadowsocks("random", "aes-256-gcm"), nil, nil, + nil, ), []string{}, ) diff --git a/common/funcs.go b/common/funcs.go index f77a3b6..ac358dd 100644 --- a/common/funcs.go +++ b/common/funcs.go @@ -24,8 +24,12 @@ func CreateHysteria(auth string) *Hysteria { return &Hysteria{Auth: auth} } -func CreateProxies(vmess *Vmess, vless *Vless, trojan *Trojan, shadowsocks *Shadowsocks, wireguard *Wireguard, hysteria *Hysteria) *Proxy { - return &Proxy{Vmess: vmess, Vless: vless, Trojan: trojan, Shadowsocks: shadowsocks, Wireguard: wireguard, Hysteria: hysteria} +func CreateMtproto(secret, userAdTag string, maxTcpConns, maxUniqueIps uint32) *Mtproto { + return &Mtproto{Secret: secret, UserAdTag: userAdTag, MaxTcpConns: maxTcpConns, MaxUniqueIps: maxUniqueIps} +} + +func CreateProxies(vmess *Vmess, vless *Vless, trojan *Trojan, shadowsocks *Shadowsocks, wireguard *Wireguard, hysteria *Hysteria, mtproto *Mtproto) *Proxy { + return &Proxy{Vmess: vmess, Vless: vless, Trojan: trojan, Shadowsocks: shadowsocks, Wireguard: wireguard, Hysteria: hysteria, Mtproto: mtproto} } func CreateUser(email string, proxies *Proxy, inbounds []string) *User { diff --git a/common/service.pb.go b/common/service.pb.go index 2025db4..ff12aa4 100644 --- a/common/service.pb.go +++ b/common/service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v7.34.1 +// protoc v7.35.0 // source: common/service.proto package common @@ -26,6 +26,7 @@ type BackendType int32 const ( BackendType_XRAY BackendType = 0 BackendType_WIREGUARD BackendType = 1 + BackendType_MTPROTO BackendType = 2 ) // Enum value maps for BackendType. @@ -33,10 +34,12 @@ var ( BackendType_name = map[int32]string{ 0: "XRAY", 1: "WIREGUARD", + 2: "MTPROTO", } BackendType_value = map[string]int32{ "XRAY": 0, "WIREGUARD": 1, + "MTPROTO": 2, } ) @@ -1117,6 +1120,74 @@ func (x *Hysteria) GetAuth() string { return "" } +type Mtproto struct { + state protoimpl.MessageState `protogen:"open.v1"` + Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` + UserAdTag string `protobuf:"bytes,2,opt,name=user_ad_tag,json=userAdTag,proto3" json:"user_ad_tag,omitempty"` + MaxTcpConns uint32 `protobuf:"varint,3,opt,name=max_tcp_conns,json=maxTcpConns,proto3" json:"max_tcp_conns,omitempty"` + MaxUniqueIps uint32 `protobuf:"varint,4,opt,name=max_unique_ips,json=maxUniqueIps,proto3" json:"max_unique_ips,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Mtproto) Reset() { + *x = Mtproto{} + mi := &file_common_service_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Mtproto) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Mtproto) ProtoMessage() {} + +func (x *Mtproto) ProtoReflect() protoreflect.Message { + mi := &file_common_service_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Mtproto.ProtoReflect.Descriptor instead. +func (*Mtproto) Descriptor() ([]byte, []int) { + return file_common_service_proto_rawDescGZIP(), []int{17} +} + +func (x *Mtproto) GetSecret() string { + if x != nil { + return x.Secret + } + return "" +} + +func (x *Mtproto) GetUserAdTag() string { + if x != nil { + return x.UserAdTag + } + return "" +} + +func (x *Mtproto) GetMaxTcpConns() uint32 { + if x != nil { + return x.MaxTcpConns + } + return 0 +} + +func (x *Mtproto) GetMaxUniqueIps() uint32 { + if x != nil { + return x.MaxUniqueIps + } + return 0 +} + type Proxy struct { state protoimpl.MessageState `protogen:"open.v1"` Vmess *Vmess `protobuf:"bytes,1,opt,name=vmess,proto3" json:"vmess,omitempty"` @@ -1125,13 +1196,14 @@ type Proxy struct { Shadowsocks *Shadowsocks `protobuf:"bytes,4,opt,name=shadowsocks,proto3" json:"shadowsocks,omitempty"` Wireguard *Wireguard `protobuf:"bytes,5,opt,name=wireguard,proto3" json:"wireguard,omitempty"` Hysteria *Hysteria `protobuf:"bytes,6,opt,name=hysteria,proto3" json:"hysteria,omitempty"` + Mtproto *Mtproto `protobuf:"bytes,7,opt,name=mtproto,proto3" json:"mtproto,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Proxy) Reset() { *x = Proxy{} - mi := &file_common_service_proto_msgTypes[17] + mi := &file_common_service_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1143,7 +1215,7 @@ func (x *Proxy) String() string { func (*Proxy) ProtoMessage() {} func (x *Proxy) ProtoReflect() protoreflect.Message { - mi := &file_common_service_proto_msgTypes[17] + mi := &file_common_service_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1156,7 +1228,7 @@ func (x *Proxy) ProtoReflect() protoreflect.Message { // Deprecated: Use Proxy.ProtoReflect.Descriptor instead. func (*Proxy) Descriptor() ([]byte, []int) { - return file_common_service_proto_rawDescGZIP(), []int{17} + return file_common_service_proto_rawDescGZIP(), []int{18} } func (x *Proxy) GetVmess() *Vmess { @@ -1201,6 +1273,13 @@ func (x *Proxy) GetHysteria() *Hysteria { return nil } +func (x *Proxy) GetMtproto() *Mtproto { + if x != nil { + return x.Mtproto + } + return nil +} + type User struct { state protoimpl.MessageState `protogen:"open.v1"` Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` @@ -1212,7 +1291,7 @@ type User struct { func (x *User) Reset() { *x = User{} - mi := &file_common_service_proto_msgTypes[18] + mi := &file_common_service_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1224,7 +1303,7 @@ func (x *User) String() string { func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_common_service_proto_msgTypes[18] + mi := &file_common_service_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1237,7 +1316,7 @@ func (x *User) ProtoReflect() protoreflect.Message { // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { - return file_common_service_proto_rawDescGZIP(), []int{18} + return file_common_service_proto_rawDescGZIP(), []int{19} } func (x *User) GetEmail() string { @@ -1270,7 +1349,7 @@ type Users struct { func (x *Users) Reset() { *x = Users{} - mi := &file_common_service_proto_msgTypes[19] + mi := &file_common_service_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1282,7 +1361,7 @@ func (x *Users) String() string { func (*Users) ProtoMessage() {} func (x *Users) ProtoReflect() protoreflect.Message { - mi := &file_common_service_proto_msgTypes[19] + mi := &file_common_service_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1295,7 +1374,7 @@ func (x *Users) ProtoReflect() protoreflect.Message { // Deprecated: Use Users.ProtoReflect.Descriptor instead. func (*Users) Descriptor() ([]byte, []int) { - return file_common_service_proto_rawDescGZIP(), []int{19} + return file_common_service_proto_rawDescGZIP(), []int{20} } func (x *Users) GetUsers() []*User { @@ -1316,7 +1395,7 @@ type UsersChunk struct { func (x *UsersChunk) Reset() { *x = UsersChunk{} - mi := &file_common_service_proto_msgTypes[20] + mi := &file_common_service_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1328,7 +1407,7 @@ func (x *UsersChunk) String() string { func (*UsersChunk) ProtoMessage() {} func (x *UsersChunk) ProtoReflect() protoreflect.Message { - mi := &file_common_service_proto_msgTypes[20] + mi := &file_common_service_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1341,7 +1420,7 @@ func (x *UsersChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use UsersChunk.ProtoReflect.Descriptor instead. func (*UsersChunk) Descriptor() ([]byte, []int) { - return file_common_service_proto_rawDescGZIP(), []int{20} + return file_common_service_proto_rawDescGZIP(), []int{21} } func (x *UsersChunk) GetUsers() []*User { @@ -1440,14 +1519,20 @@ const file_common_service_proto_rawDesc = "" + "public_key\x18\x01 \x01(\tR\tpublicKey\x12\x19\n" + "\bpeer_ips\x18\x02 \x03(\tR\apeerIps\"\x1e\n" + "\bHysteria\x12\x12\n" + - "\x04auth\x18\x01 \x01(\tR\x04auth\"\x95\x02\n" + + "\x04auth\x18\x01 \x01(\tR\x04auth\"\x8b\x01\n" + + "\aMtproto\x12\x16\n" + + "\x06secret\x18\x01 \x01(\tR\x06secret\x12\x1e\n" + + "\vuser_ad_tag\x18\x02 \x01(\tR\tuserAdTag\x12\"\n" + + "\rmax_tcp_conns\x18\x03 \x01(\rR\vmaxTcpConns\x12$\n" + + "\x0emax_unique_ips\x18\x04 \x01(\rR\fmaxUniqueIps\"\xc1\x02\n" + "\x05Proxy\x12$\n" + "\x05vmess\x18\x01 \x01(\v2\x0e.service.VmessR\x05vmess\x12$\n" + "\x05vless\x18\x02 \x01(\v2\x0e.service.VlessR\x05vless\x12'\n" + "\x06trojan\x18\x03 \x01(\v2\x0f.service.TrojanR\x06trojan\x126\n" + "\vshadowsocks\x18\x04 \x01(\v2\x14.service.ShadowsocksR\vshadowsocks\x120\n" + "\twireguard\x18\x05 \x01(\v2\x12.service.WireguardR\twireguard\x12-\n" + - "\bhysteria\x18\x06 \x01(\v2\x11.service.HysteriaR\bhysteria\"b\n" + + "\bhysteria\x18\x06 \x01(\v2\x11.service.HysteriaR\bhysteria\x12*\n" + + "\amtproto\x18\a \x01(\v2\x10.service.MtprotoR\amtproto\"b\n" + "\x04User\x12\x14\n" + "\x05email\x18\x01 \x01(\tR\x05email\x12(\n" + "\aproxies\x18\x02 \x01(\v2\x0e.service.ProxyR\aproxies\x12\x1a\n" + @@ -1458,10 +1543,11 @@ const file_common_service_proto_rawDesc = "" + "UsersChunk\x12#\n" + "\x05users\x18\x01 \x03(\v2\r.service.UserR\x05users\x12\x14\n" + "\x05index\x18\x02 \x01(\x04R\x05index\x12\x12\n" + - "\x04last\x18\x03 \x01(\bR\x04last*&\n" + + "\x04last\x18\x03 \x01(\bR\x04last*3\n" + "\vBackendType\x12\b\n" + "\x04XRAY\x10\x00\x12\r\n" + - "\tWIREGUARD\x10\x01*_\n" + + "\tWIREGUARD\x10\x01\x12\v\n" + + "\aMTPROTO\x10\x02*_\n" + "\bStatType\x12\r\n" + "\tOutbounds\x10\x00\x12\f\n" + "\bOutbound\x10\x01\x12\f\n" + @@ -1496,7 +1582,7 @@ func file_common_service_proto_rawDescGZIP() []byte { } var file_common_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_common_service_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_common_service_proto_msgTypes = make([]protoimpl.MessageInfo, 23) var file_common_service_proto_goTypes = []any{ (BackendType)(0), // 0: service.BackendType (StatType)(0), // 1: service.StatType @@ -1517,56 +1603,58 @@ var file_common_service_proto_goTypes = []any{ (*Shadowsocks)(nil), // 16: service.Shadowsocks (*Wireguard)(nil), // 17: service.Wireguard (*Hysteria)(nil), // 18: service.Hysteria - (*Proxy)(nil), // 19: service.Proxy - (*User)(nil), // 20: service.User - (*Users)(nil), // 21: service.Users - (*UsersChunk)(nil), // 22: service.UsersChunk - nil, // 23: service.StatsOnlineIpListResponse.IpsEntry + (*Mtproto)(nil), // 19: service.Mtproto + (*Proxy)(nil), // 20: service.Proxy + (*User)(nil), // 21: service.User + (*Users)(nil), // 22: service.Users + (*UsersChunk)(nil), // 23: service.UsersChunk + nil, // 24: service.StatsOnlineIpListResponse.IpsEntry } var file_common_service_proto_depIdxs = []int32{ 0, // 0: service.Backend.type:type_name -> service.BackendType - 20, // 1: service.Backend.users:type_name -> service.User + 21, // 1: service.Backend.users:type_name -> service.User 6, // 2: service.StatResponse.stats:type_name -> service.Stat 1, // 3: service.StatRequest.type:type_name -> service.StatType - 23, // 4: service.StatsOnlineIpListResponse.ips:type_name -> service.StatsOnlineIpListResponse.IpsEntry + 24, // 4: service.StatsOnlineIpListResponse.ips:type_name -> service.StatsOnlineIpListResponse.IpsEntry 13, // 5: service.Proxy.vmess:type_name -> service.Vmess 14, // 6: service.Proxy.vless:type_name -> service.Vless 15, // 7: service.Proxy.trojan:type_name -> service.Trojan 16, // 8: service.Proxy.shadowsocks:type_name -> service.Shadowsocks 17, // 9: service.Proxy.wireguard:type_name -> service.Wireguard 18, // 10: service.Proxy.hysteria:type_name -> service.Hysteria - 19, // 11: service.User.proxies:type_name -> service.Proxy - 20, // 12: service.Users.users:type_name -> service.User - 20, // 13: service.UsersChunk.users:type_name -> service.User - 4, // 14: service.NodeService.Start:input_type -> service.Backend - 2, // 15: service.NodeService.Stop:input_type -> service.Empty - 2, // 16: service.NodeService.GetBaseInfo:input_type -> service.Empty - 2, // 17: service.NodeService.GetLogs:input_type -> service.Empty - 2, // 18: service.NodeService.GetSystemStats:input_type -> service.Empty - 2, // 19: service.NodeService.GetBackendStats:input_type -> service.Empty - 8, // 20: service.NodeService.GetStats:input_type -> service.StatRequest - 8, // 21: service.NodeService.GetUserOnlineStats:input_type -> service.StatRequest - 8, // 22: service.NodeService.GetUserOnlineIpListStats:input_type -> service.StatRequest - 20, // 23: service.NodeService.SyncUser:input_type -> service.User - 21, // 24: service.NodeService.SyncUsers:input_type -> service.Users - 22, // 25: service.NodeService.SyncUsersChunked:input_type -> service.UsersChunk - 3, // 26: service.NodeService.Start:output_type -> service.BaseInfoResponse - 2, // 27: service.NodeService.Stop:output_type -> service.Empty - 3, // 28: service.NodeService.GetBaseInfo:output_type -> service.BaseInfoResponse - 5, // 29: service.NodeService.GetLogs:output_type -> service.Log - 12, // 30: service.NodeService.GetSystemStats:output_type -> service.SystemStatsResponse - 11, // 31: service.NodeService.GetBackendStats:output_type -> service.BackendStatsResponse - 7, // 32: service.NodeService.GetStats:output_type -> service.StatResponse - 9, // 33: service.NodeService.GetUserOnlineStats:output_type -> service.OnlineStatResponse - 10, // 34: service.NodeService.GetUserOnlineIpListStats:output_type -> service.StatsOnlineIpListResponse - 2, // 35: service.NodeService.SyncUser:output_type -> service.Empty - 2, // 36: service.NodeService.SyncUsers:output_type -> service.Empty - 2, // 37: service.NodeService.SyncUsersChunked:output_type -> service.Empty - 26, // [26:38] is the sub-list for method output_type - 14, // [14:26] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 19, // 11: service.Proxy.mtproto:type_name -> service.Mtproto + 20, // 12: service.User.proxies:type_name -> service.Proxy + 21, // 13: service.Users.users:type_name -> service.User + 21, // 14: service.UsersChunk.users:type_name -> service.User + 4, // 15: service.NodeService.Start:input_type -> service.Backend + 2, // 16: service.NodeService.Stop:input_type -> service.Empty + 2, // 17: service.NodeService.GetBaseInfo:input_type -> service.Empty + 2, // 18: service.NodeService.GetLogs:input_type -> service.Empty + 2, // 19: service.NodeService.GetSystemStats:input_type -> service.Empty + 2, // 20: service.NodeService.GetBackendStats:input_type -> service.Empty + 8, // 21: service.NodeService.GetStats:input_type -> service.StatRequest + 8, // 22: service.NodeService.GetUserOnlineStats:input_type -> service.StatRequest + 8, // 23: service.NodeService.GetUserOnlineIpListStats:input_type -> service.StatRequest + 21, // 24: service.NodeService.SyncUser:input_type -> service.User + 22, // 25: service.NodeService.SyncUsers:input_type -> service.Users + 23, // 26: service.NodeService.SyncUsersChunked:input_type -> service.UsersChunk + 3, // 27: service.NodeService.Start:output_type -> service.BaseInfoResponse + 2, // 28: service.NodeService.Stop:output_type -> service.Empty + 3, // 29: service.NodeService.GetBaseInfo:output_type -> service.BaseInfoResponse + 5, // 30: service.NodeService.GetLogs:output_type -> service.Log + 12, // 31: service.NodeService.GetSystemStats:output_type -> service.SystemStatsResponse + 11, // 32: service.NodeService.GetBackendStats:output_type -> service.BackendStatsResponse + 7, // 33: service.NodeService.GetStats:output_type -> service.StatResponse + 9, // 34: service.NodeService.GetUserOnlineStats:output_type -> service.OnlineStatResponse + 10, // 35: service.NodeService.GetUserOnlineIpListStats:output_type -> service.StatsOnlineIpListResponse + 2, // 36: service.NodeService.SyncUser:output_type -> service.Empty + 2, // 37: service.NodeService.SyncUsers:output_type -> service.Empty + 2, // 38: service.NodeService.SyncUsersChunked:output_type -> service.Empty + 27, // [27:39] is the sub-list for method output_type + 15, // [15:27] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_common_service_proto_init() } @@ -1580,7 +1668,7 @@ func file_common_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_service_proto_rawDesc), len(file_common_service_proto_rawDesc)), NumEnums: 2, - NumMessages: 22, + NumMessages: 23, NumExtensions: 0, NumServices: 1, }, diff --git a/common/service.proto b/common/service.proto index 2321ce0..5b74f31 100644 --- a/common/service.proto +++ b/common/service.proto @@ -16,6 +16,7 @@ message BaseInfoResponse { enum BackendType { XRAY = 0; WIREGUARD = 1; + MTPROTO = 2; } message Backend { @@ -119,6 +120,16 @@ message Hysteria { string auth = 1; } +message Mtproto { + string secret = 1; + string user_ad_tag = 2; + uint32 max_tcp_conns = 3; + uint32 max_unique_ips = 4; + // Note: data quota and expiry are intentionally omitted. The panel owns + // quota/expiry enforcement (via usage reporting and user sync), the same as + // for the xray and wireguard backends; telemt must not enforce them. +} + message Proxy { Vmess vmess = 1; Vless vless = 2; @@ -126,6 +137,7 @@ message Proxy { Shadowsocks shadowsocks = 4; Wireguard wireguard = 5; Hysteria hysteria = 6; + Mtproto mtproto = 7; } message User { diff --git a/common/service_grpc.pb.go b/common/service_grpc.pb.go index a917884..5189f07 100644 --- a/common/service_grpc.pb.go +++ b/common/service_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.6.1 -// - protoc v7.34.1 +// - protoc-gen-go-grpc v1.6.2 +// - protoc v7.35.0 // source: common/service.proto package common